-
Notifications
You must be signed in to change notification settings - Fork 67
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. |
f618aab to
3452a3f
Compare
|
Rebased to main, added code example. |
|
@callebtc - can you re-review when you get some time please. |
| The blinding scalar for each slot is calculated as: | ||
|
|
||
| ``` | ||
| rᵢ = SHA-256( DOMAIN_SEPARATOR || Zx || keyset_id_bytes || i_byte) |
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.
Do we need to include the keyset here?
I ask because of a problem in the Spilman Channel, where I think I'd like to remove the keyset_id from this. I have the blinding fully working in the channel today, but not (yet) exactly following the system described in this NUT.
TLDR: If a SIG_ALL swap locks output to a blinded pubkey, and the swap then sends the outputs to an unexpected keyset, then the unblinding won't work because the keyset is wrong. To clarify: I'm not talking about the inputs to this swap, I'm talking about the outputs of the swap, where the outputs are blinded P2BK
If I sign a two-party transaction with SIG_ALL then it commits to the amounts in the outputs, but it does not commit to the keyset in those outputs. Imagine the second party adds their signature and performs the swap. The second party decides what keyset those outputs will be in, and this might not be the keyset that I was expecting. They are free to choose any active keyset with the right unit.
If the outputs commit to blinded pubkeys, then things will be strange. I computed the blinded pubkey on one keyset, because I assumed the swap would create outputs in the same keyset as the inputs to the swap. But then the outputs are in a different keyset, and I would need to write special code to unblind and sign those outputs; special code which uses the 'expected keyset' instead of the actual keyset
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.
The keyset ID ensures the blinding factor is unique between mints (and keysets) to avoid privacy leakage in case of ephemeral key reuse.
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.
How about (instead) including the secret's nonce in this derivation?
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.
How about (instead) including the secret's
noncein this derivation?
actually, ignore that particular question from me about the nonce. It would break SIG_ALL, which requires all the proofs to have the same (blinded) public key
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 would still like to remove the keyset_id from the derivation, for the reason described at the start of this thread
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.
@callebtc, @a1denvalu3 - thoughts?
CLOSES #290
REPLACES: #291
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.
This also simplifes Nostr NutZaps. NIP-61 pubkeys no longer need to be advertised and periodically rotated for privacy.
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
Implementations
Live Demo: