Blinding factor handling #321
Labels
No labels
breaking change
bug
documentation
enhancement
needs discussion
needs implementation
new nut
ready
wallet-only
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo-admin/nuts#321
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Our core protocol in NUT 00 says:
Alice sends to Bob: B_ = Y + rG with r being a random blinding factorHowever, it doesn’t explicitly state that
ris a secp256k1 scalar inZ_n*( i.e: "a non zero element in the range 1..n−1, wherenis the curve order"), or how to handle values outside this canonical range.This seems to be assumed elsewhere. In NUT 13, for example, the blinded message section derives
rvia:and BIP32 derived private keys are commonly treated as scalars modulon(with0treated as invalid and retried).EDIT: Apparently not!
Why this matters
In practice this matters for interoperability because different implementations may:
n,1..n−1,r = 0edge case.It also matters for privacy. If
r ≡ 0 (mod n), thenB_ = Y + rGcollapses toB_ = Y, so blinding is lost for that output.Questions:
rbe defined as a scalar inZ_n*, meaning implementations MUST interpret it modulonand canonicalise it to1..n−1, rather than rejecting “out of range” inputs?r ≡ 0 (mod n)be explicitly invalid, and if so, should NUT 13 specify a deterministic retry mechanism (for example, rejection sampling / re-derivation with an internal counter) to avoid restore failures and ensure cross implementation determinism?I think being explicit does not hurt, although in EC cryptography values are implicitly assumed to be curve-save, no?
For general blinded messages, we can assume curve order for
ris what we EXPECT, but handling of the edge cases is open to interpretatation.The
r = 0ncase is problematic. Inhash_to_curve, we explicitly retry until a valid point it found. No problem.In NUT-13, however, we don't have a explicit canonical method to resolve, without knowing the internals of
self.bip32.get_privkey_from_path.IMO when NUTs references other specs, we should simply assume that implementations of those specifications implement correctly. In this case this is stated in BIP32:
I tend to agree, however, the NUT references a specific Python implementation of the BIP32 spec.
And in the case of BIP32, it is not immediately obvious whether the stated issue is resolved within the spec, so that a dervation path always resolves to something canonically, or whether it returns nonsense expecting the caller to then do the counter increment and try again.
IMO, a little clarity hurts much less than misinterpretation.
Have added a small PR with some suggestions for clarifications:
https://github.com/cashubtc/nuts/pull/322
Tl;DR: we assume scalars are
modulo nunless othewise noted (eg NUT-13, NUT-26)