-
Notifications
You must be signed in to change notification settings - Fork 70
NUTXX - ECDH-derived Pay-to-Blinded-Key (P2BK) #300
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
base: main
Are you sure you want to change the base?
Conversation
d98d9d6 to
58231b0
Compare
d4rp4t
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've added a package.json file, propably by accident
Good catch - thanks @d4rp4t. Fixed |
f665226 to
518522e
Compare
|
@robwoodgate We should derive a Use case example: I've opened a PR here |
We already DO calculate shared secret (Zx) per locking key. The slot index is just for ADDITIONAL uniqueness, so that if the same key (P) is added to both pubkeys and refund, it will be uniquely blinded by the slot index. Only the sender knows the ephemeral secret, so only they can derive Zx per locking key (eP) The receiver(s) only know the ephemeral pubkey (E) and their own secret key (p), so they can only generate the shared secret for their own key (pE). EDIT: I've added some clarifying note blocks, because it's a crucial point that is easy to overlook. |
|
@robwoodgate I think we can avoid the |
Love this idea! That would be the ultimate privacy move because P2BK proofs would be totally indistinguishable from standard P2PK proofs. And making the The only downside is the privacy benefit lol... there would be no way to know if a proof was blinded or not, so you would have to try signing EVERY P2PK proof that doesn't have your pubkey with both your secret key (p) and both your derived secret keys (p') to be sure it's not yours. Overall, I think that's probably a tradeoff worth making for the privacy. And very in line with Bitcoin silent payments. Anyone disagree? |
Thinking about this some more this afternoon.... a possible reason to not do this: if the Mint knows a P2BK (Though around half of all 32 byte string nonces would naturally be valid x-coordinates in any case...) |
|
@robwoodgate around ~ Though this could be easily fixed if newer wallets always use EC public keys as nonces, even for normal p2pk. The |
That would alleviate the discrimination concern for sure. It would also go some way to alleviating the related concern that using the |
|
Discussed off proposal: Two paths to resolution:
I would personally prefer option 2, especially given the Mint can find out who is using silent payments easily through option 1. |
To summarize the dilemma: Option 1: Carry
|
Discussed again off proposal: The general feeling was to go with
Overall, reason 2 (loss of SIG_ALL compatibility) was seen as the main reason to NOT use the nonce as the carrier. |
|
@robwoodgate We could simplify the parity detection on the receiver side if we compared the x-only of the unblinded public key: But this is more of an implementation choice. We should however mention in the NUT that this is possible. |
I don't think we need to mention implementation detail in the NUT. In cashu-ts, the aim was to achieve algorithmic constant time, so both You are correct the original pubkey It's not much of a simplifcation, as the blinded private key still needs to be derived in any case. |
Overall, the parity detection issue is nothing to do with Pubkeys, it depends on whether the receiver secret key So a wallet/Nostr client etc might allow a negative-Y generating sk to be stored, because it is flipped 'on the fly'. We therefore will always need to check both for Schnorr derived pubkeys. |
To me, this sounds like a semplification. You trade in 2 point-scalar multiplications and 1 point addition for 1 point-scalar multiplication and 1 addition. |
I understand now - yes, you can save a point multiply, and the approach is sound. I will revisit the cashu-ts reference implementation though for optimization. |
@lollerfirst - I've now added this as the primary workflow. As we have one in the spec, it may as well be the optimal one! |
|
@lollerfirst - I've aded a comprehensive test vector page which will allow implementors to double-check a concrete example across all slots. |
|
@callebtc @thesimplekid - We now have implementations in review for Cashu-TS and CDK, so this PR is ready for review too. There is one question I have about whether we update NUT-18 to show p2pk_e as a default, LMK if I should add that. |
|
What does 'abort' mean in practice here:
That the sender must abandon this ephemeral key pair and generate a new Also, why do we have this two-stage process where we try without the (I feel like I might be missing some other justification. So sorry for dumb questions 😀. I'm finally putting blinding into the Spilman channel implementation and am hoping to fully understand this NUT in order to maximize compatibility between this NUT and the channel's blinding system) |
Correct!
The two step process is to ensure the app doesn't have to worry about starting over in the unlikely event the value is out of range. It keeps this "problem" in the crypto aware scope rather than pushing it further up the app stack. It's so astronomically unlikely to ever happen (as N ≈ 2²⁵⁶ × 0.9999999999999999992559580114556583) that a simple one-step retry is plenty to mean an abort is practically impossible. I did originally spec (and would prefer) the We have the same discussion around NUT-13. |
f618aab to
3452a3f
Compare
|
Rebased to main, added code example. |
|
@callebtc - can you re-review when you get some time please. |
a1denvalu3
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we will now need to change the number of the NUT because bech got merged first.
3e11df0 to
b9769ca
Compare
Yeah 🤦♂️ Done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have removed keyset ID from the blinding factor calculation, re-calculated all test vectors and renamed to NUT-28 (as BECH32 got merged first).
cfcc83d to
da009a1
Compare
|
Rebased to main to resolve a merge conflict. |
|
Filenames now set for immediate merge as NUT-28 |
SatsAndSports
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the last small changes. Looks good
CLOSES #290
REPLACES: #291
Implementations:
Summary:
Defines P2BK as an ECDH-derived blinding scheme instead of one using random scalars.
Each proof now includes a per-proof ephemeral pubkey
p2pk_e, from which both parties can derive the same blinding factor(s) deterministically.ECDH shared secret works because:
Importantly, 3rd parties and the mint CANNOT derive the original locking pubkeys. Only the sender and the receiver have the secret keys required to calculate the ECDH shared secret, which can derive both the original pubkeys and the signing secret.
Proofs can be locked to a well known public key, posted in public without compromising privacy, and spent by the recipient without needing any side-channel communication.
Key points:
p2pk_e(33-byte SEC1 pubkey) per proof (stored aspein token v4 format)rᵢ = SHA-256( b"Cashu_P2BK_v1" || Zx || keyset_id_bytes || i_byte)where Zx is a shared ECDH secret,
keyset_id_bytesis the hex_to_bytes of keyset ID, andi_byteis the P2PK locking key "slot" position.Assumptions
Live Demo: