Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stamps #295

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft

Stamps #295

wants to merge 11 commits into from

Conversation

callebtc
Copy link
Collaborator

@callebtc callebtc commented Jul 30, 2023

Problem

The BDHKE scheme used in Cashu does not allow users you to verify the mint's blind signature C without the knowledge of the mint's private key a. This is bad since users don't know whether (1) the signature C is valid without asking the mint (it would be great to be able to do this because we can implement final offline payments in combination with P2PK) and (2) whether the mint is tagging tokens by using unique private keys a_{nonce} for every individual token and can thus track all tokens.

Solution 1

Chaum-Pedersen DLEQ proofs (implemented in #175) can be used by the minting user Alice of a token to ensure that A = a*G is indeed the key that signed C_. To work, it requires the knowledge of the blinding factor r which the minting wallet of Alice has access to. Alice can also send this proof together with r to Carol who can verify the signature.

Problem of solution 1

The problem with this approach is Carol could actually be the mint Bob and receive r. However, if Bob knows r, it can be used to break the unlinkability of the token to its previous output, which is bad. For situations where the Alice knows Carol and that they don't collude with the mint, this approach is still ok.

Solution 2

Another type of DLEQ proof called a stamp (implemented in this PR) can be used to make the mint commit that it has indeed signed C with A = a*G. This requires an additional round trip after unblinding (since the mint can only ever receive C after Alice has unblinded it). It does not require the knowledge of r and can therefore be posted publicly and does not have to be kept secret from the mint.

Problem of solution 2

The problem with this solution is timing and correlations: if Alice resubmits C immediately after she receives C_ from the mint, the mint can correlate them (which breaks unlinkability). One approach to mitigate this is to delay the minting of C_ to the stamping of C in time (and/or in token distribution, when sets of tokens are minted/stamped).

My first approach in this PR is to allow stamping tokens in Alice's wallet upon execution of a command cashu stamp that requests stamps on all (or a subset) of tokens in the wallet. This can happen at a later time, say, days after the token has been minted. That way, a mint should not be able to link the minting operation to the stamping operation.

Implementation

We introduce a new endpoint POST /stamp which takes the following input json data.

Note: ProofY is the same as a Proof but instead of the secret it contains Y = hash_to_curve(secret).

{
  "proofys": [
    {
      "id": "string",
      "amount": 0,
      "C": "string",
      "Y": "string"
    }
  ]
}

The mint responds with DLEQ proofs (e,s) for each provided Proof

{
  "stamps": [
    {
      "e": "string",
      "s": "string"
    }
  ]
}

Todos

  • stamp endpoint
  • write tests
  • cli command
  • nut

Notes:

  • Previously implemented a Schnorr signature on (x, C), but now proving mint's signature on C by DLEQ proof of equality of a in C = aY and A = aG via a second DLEQ proof by @moonsettler

@codecov
Copy link

codecov bot commented Jul 30, 2023

Codecov Report

Patch coverage: 75.78% and project coverage change: +0.45% 🎉

Comparison is base (ca2b8e7) 57.04% compared to head (74b1532) 57.49%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #295      +/-   ##
==========================================
+ Coverage   57.04%   57.49%   +0.45%     
==========================================
  Files          43       43              
  Lines        3997     4110     +113     
==========================================
+ Hits         2280     2363      +83     
- Misses       1717     1747      +30     
Files Changed Coverage Δ
cashu/mint/router.py 0.00% <0.00%> (ø)
cashu/mint/ledger.py 27.96% <12.50%> (-0.27%) ⬇️
cashu/wallet/cli/cli.py 47.63% <23.80%> (-1.13%) ⬇️
cashu/core/base.py 91.82% <92.85%> (+0.18%) ⬆️
cashu/core/crypto/b_dhke.py 94.44% <96.29%> (+1.58%) ⬆️
cashu/wallet/crud.py 74.80% <100.00%> (+1.03%) ⬆️
cashu/wallet/migrations.py 100.00% <100.00%> (ø)
cashu/wallet/wallet.py 81.37% <100.00%> (+0.63%) ⬆️

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Collaborator

@moonsettler moonsettler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bob does not need to know secret_msg to verify kY = C and provide a DLEQ proof verifiable by anyone. This is a conditional proof if secret_msg is not revealed to the third party they can not be sure that the signature is valid even if the DLEQ proof checks out.

While it is trivial to forge (Y, C) type 'signatures', the values are explicitly committed to in the DLEQ here, and thus can be considered valid, if Y = hash_to_curve(x) is satisfied.

def stamp_step1_bob(
    Y: PublicKey, C: PublicKey, a: PrivateKey, p_bytes: bytes = b""
) -> Tuple[PrivateKey, PrivateKey]:
    if p_bytes:
        # deterministic p for testing
        p = PrivateKey(privkey=p_bytes, raw=True)
    else:
        # normally, we generate a random p
        p = PrivateKey()
    assert p.pubkey
    R1: PublicKey = p.pubkey  # R1 = pG
    R2: PublicKey = Y.mult(p)  # type: ignore # R2 = pY
    print(R1.serialize().hex(), R2.serialize().hex())
    e = hash_e(R1, R2, Y, C)
    s = p.tweak_add(a.tweak_mul(e))  # s = p + ea
    spk = PrivateKey(s, raw=True)
    epk = PrivateKey(e, raw=True)
    return epk, spk

@callebtc
Copy link
Collaborator Author

callebtc commented Aug 3, 2023

Bob does not need to know secret_msg

Correct! Adjusted appropriately and introduced a new object ProofY for that. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants