NUT28 - ECDH-derived Pay-to-Blinded-Key (P2BK)#300
Conversation
d98d9d6 to
58231b0
Compare
d4rp4t
left a comment
There was a problem hiding this comment.
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. |
28.md
Outdated
|
|
||
| ECDH allows two parties to create an x-coordinate shared secret (`Zx`) by combining their private key with the public key of the other party: `Zx = x(epG) = x(eP) = x(pE)`. | ||
|
|
||
| For P2BK, the sender creates an ephemeral keypair (private key: `e`, public key: `E`). This protects the privacy of their own long-lived public key. They then calculate the shared secret by combining the ephemeral private key (`e`) and the receiver's long-lived public key (`P`). |
There was a problem hiding this comment.
This protects the privacy of their own long-lived public key.
we didn't define what the long-lived public key is yet (next sentence). we only speak about the receiver's pubkey but the sentence sounds like it protects the sender's pubkey. suggest to just remove the sentence.
There was a problem hiding this comment.
That's exactly right - using an ephemeral keypair does protect the senders pubkey :)
c972bc1 to
b66dfc8
Compare
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: