# NUT-12: Offline ecash signature validation `optional` --- In this document, we present an extension of Cashu's crypto system to allow a user `Alice` to verify the mint `Bob`'s signature using only `Bob`'s public keys. We explain how another user `Carol` who receives ecash from `Alice` can execute the DLEQ proof as well. This is achieved using a Discrete Log Equality (DLEQ) proof. Previously, `Bob`'s signature could only be checked by himself using his own private keys ([NUT-00][00]). # The DLEQ proof The purpose of this DLEQ is to prove that the mint has used the same private key `a` for creating its public key `A` ([NUT-01][01]) and for signing the BlindedMessage `B'`. `Bob` returns the DLEQ proof additional to the blind signature `C'` for a mint or swap operation. The complete DLEQ proof reads ``` # DLEQ Proof (These steps occur when Bob returns C') Bob: r = random nonce R1 = r*G R2 = r*B' e = hash(R1,R2,A,C') s = r + e*a return e, s Alice: R1 = s*G - e*A R2 = s*B' - e*C' e == hash(R1,R2,A,C') If true, a in A = a*G must be equal to a in C' = a*B' ``` ### Hash function The hash function `hash(x: ) -> bytes` generates a deterministic SHA256 hash for a given input list of `PublicKey`. The uncompressed (32+32+1)-byte hexadecimal representations (130 characters) of each `PublicKey` is concatenated before taking the SHA256 hash. ```python def hash_e(*publickeys: PublicKey) -> bytes: e_ = "" for p in publickeys: _p = p.serialize(compressed=False).hex() e_ += str(_p) e = hashlib.sha256(e_.encode("utf-8")).digest() return e ``` > [!NOTE] > For examples of valid DLEQ proofs, see the [test vectors][tests]. ### Mint to user: DLEQ in `BlindSignature` The mint produces these DLEQ proofs when returning `BlindSignature`'s in the responses for minting ([NUT-04][04]) and swapping ([NUT-03][03]) tokens. The `BlindSignature` object is extended in the following way to include the DLEQ proof: ```json { "id": , "amount": , "C_": , "dleq": { <-- New: DLEQ proof "e": , "s": } } ``` `e` and `s` are the DLEQ proof. ### User to user: DLEQ in `Proof` In order for `Alice` to communicate the DLEQ to another user `Carol`, we extend the `Proof` (see [NUT-00][00]) object and include the DLEQ proof. As explained below, we also need to include the blinding factor `r` for the proof to be convincing to another user `Carol`. ```json { "id": , "amount": , "secret": , "C": , "dleq": { <-- New: DLEQ proof "e": , "s": , "r": } } ``` `e` and `s` are the challenge and response of the DLEQ proof returned by `Bob`, `r` is the blinding factor of `Alice` that was used to generate the `Proof`. `Alice` serializes these proofs like any other in a token (see [NUT-00][00]) to send it to another user `Carol`. > [!IMPORTANT] > > **Privacy:** The blinding factor `r` should not be shared with the mint or otherwise, the mint will be able to associate the `BlindSignature` with the `Proof`. ## Alice (minting user) verifies DLEQ proof When minting or swapping tokens, `Alice` receives DLEQ proofs in the `BlindSignature` response from the mint `Bob`. `Alice` checks the validity of the DLEQ proofs for each ecash token she receives via the equations: ``` R1 = s*G - e*A R2 = s*B' - e*C' e == hash(R1,R2,A,C') # must be True ``` Here, the variables are - `A` – the public key `Bob` used to sign this Proof - `(e, s)` – the DLEQ proof returned by `Bob` - `B'` – `Alice`'s `BlindedMessage` - `C'` – `Bob`'s `BlindSignature` on `B'` In order to execute the proof, `Alice` needs `e, s` that are returned in the `BlindSignature` by `Bob`. `Alice` further needs `B'` (the `BlindedMessage` `Alice` created and `Bob` signed) and `C'` (the blind signature in the `BlindSignature` response) from `Bob`, and `A` (the public key of `Bob` with which he signed the BlindedMessage). All these values are available to `Alice` during or after calling the mint and swap operations. If a DLEQ proof is included in the mint's `BlindSignature` response, wallets **MUST** verify the DLEQ proof. ## Carol (another user) verifies DLEQ proof `Carol` is a user that receives `Proofs` in a token from another user Alice. When `Alice` sends `Proofs` with DLEQ proofs to `Carol` or when `Alice` posts the `Proofs` publicly, `Carol` can validate the DLEQ proof herself and verify `Bob`'s signature without having to talk to `Bob`. `Alice` includes the following information in the `Proof` (see above): - `(x, C)` – the ecash `Proof` - `(e, s)` – the DLEQ proof revealed by `Alice` - `r` – `Alice`'s blinding factor Here, `x` is the Proof's secret, and `C` is the mint's signature on it. To execute the DLEQ proof like `Alice` did above, `Carol` needs `(B', C')` which she can compute herself using the blinding factor `r` that she receives from `Alice`. To verify the DLEQ proof of a received token, `Carol` needs to reconstruct `B'` and `C'` using the blinding factor `r` that `Alice` has included in the `Proof` she sent to `Carol`. Since `Carol` now has all the necessary information, she can execute the same equations to verify the DLEQ proof as `Alice` did: ``` Y = hash_to_curve(x) C' = C + r*A B' = Y + r*G R1 = ... (same as Alice) ``` If a DLEQ proof is included in a received token, wallets **MUST** verify the proof. ## Mint info setting The [NUT-06][06] `MintMethodSetting` indicates support for this feature: ```json { "12": { "supported": true } } ``` [00]: 00.md [01]: 01.md [02]: 02.md [03]: 03.md [04]: 04.md [05]: 05.md [06]: 06.md [07]: 07.md [08]: 08.md [09]: 09.md [10]: 10.md [11]: 11.md [12]: 12.md [tests]: tests/12-tests.md