Skip to content

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).

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) 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(x: <Array<[PublicKey]>) -> bytes

The hash(x) function generates a deterministic Sha256 hash for a given input list of PublicKey. The uncompressed hexadecimal representations of each PublicKey is concatenated before taking the Sha256 hash.

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

Mint to user: DLEQ in BlindSignature

The mint produces these DLEQ proofs when returning BlindSignature's in the responses for minting (NUT-04) and swapping (NUT-03) tokens. The BlindSignature object is extended in the following way to include the DLEQ proof:

{
  "id": <str>,
  "amount": <int>,
  "C_": <str>,
  "dleq": { <-- New: DLEQ proof
    "e": <str>,
    "s": <str>
  }
}

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) 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.

{
  "id": <str>,
  "amount": <int>,
  "secret": <str>,
  "C": <str>,
  "dleq": { <-- New: DLEQ proof
    "e": <str>,
    "s": <str>,
    "r": <str>
  }
}

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) to send it to another user Carol.

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
  • rAlice'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 MintMethodSetting indicates support for this feature:

{
  "12": {
    "supported": true
  }
}