-
Notifications
You must be signed in to change notification settings - Fork 520
bolt11: clarify signature normalization requirements #1284
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
bolt11: clarify signature normalization requirements #1284
Conversation
Why don't we just require lower-S form in all cases? That would be simpler, wouldn't it? |
It’s an option, but in practice it would add complexity because of how lnd: // If the destination pubkey was provided as a tagged field, use that
// to verify the signature, if not do public key recovery.
if decodedInvoice.Destination != nil {
signature, err := sig.ToSignature()
if err != nil {
return nil, fmt.Errorf("unable to deserialize "+
"signature: %v", err)
}
if !signature.Verify(hash, decodedInvoice.Destination) {
return nil, fmt.Errorf("invalid invoice signature")
}
} else {
headerByte := recoveryID + 27 + 4
compactSign := append([]byte{headerByte}, sig.RawBytes()...)
pubkey, _, err := ecdsa.RecoverCompact(compactSign, hash)
if err != nil {
return nil, err
}
decodedInvoice.Destination = pubkey
}clightining: if (!have_n) {
struct pubkey k;
if (!secp256k1_ecdsa_recover(secp256k1_ctx,
&k.pubkey,
&sig,
(const u8 *)&hash))
return decode_fail(b11, fail,
"signature recovery failed");
node_id_from_pubkey(&b11->receiver_id, &k);
} else {
struct pubkey k;
/* n parsing checked this! */
if (!pubkey_from_node_id(&k, &b11->receiver_id))
abort();
if (!secp256k1_ecdsa_verify(secp256k1_ctx, &b11->sig,
(const u8 *)&hash,
&k.pubkey))
return decode_fail(b11, fail, "invalid signature");
} |
|
Fair enough! |
11-payment-encoding.md
Outdated
| description. | ||
| - if a valid `n` field is provided: | ||
| - MUST use the `n` field to validate the signature instead of performing signature recovery. | ||
| - the signature MUST be normalized lower-S form. |
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.
Makes sense.
Before merge, we should also go through the test vectors to see if any of them are high-s, regenerating if so.
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.
There are no BOLT-11 test vectors that cover signature validation using the n field. We should probably add some.
We could totally provide a function in libsecp256k1 to make that easy, but I still think that not insisting on low-s is the simplest thing to do here. Some nits:
|
90d8764 to
2e10b7f
Compare
t-bast
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 clarification!
|
I’ll also add a new test vector to cover public key recovery from a high-S signature. I noticed that rust-lightning currently enforces low-S even when recovering a public key from a signature similar to lightning-kmp. |
Explicitly document that signature validation requirements differ based on the presence of the `n` field: - With `n` field: signature MUST be normalized low-S - Without `n` field: public-key recovery accepts both high-S and low-S This clarifies existing implementation behavior where secp256k1_ecdsa_verify (used with `n` field) requires normalized signatures, while secp256k1_ecdsa_recover (used without `n` field) accepts both normalized and non-normalized signatures.
aa8b063 to
aa85c9d
Compare
We must accept both high-S and low-S signatures in Bolt 11 invoices when performing public key recovery (which matches secp256k1's behavior). See lightning/bolts#1284
We must accept both high-S and low-S signatures in Bolt 11 invoices when performing public key recovery (which matches secp256k1's behavior). See lightning/bolts#1284
When the `nodeId` is provided, we must verify that the signature is valid and is in normalized low-S form. When the `nodeId` is not provided though, we perform signature recovery, which accepts both low-S and high-S signatures and verifies its validity. See lightning/bolts#1284
t-bast
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.
ACK aa85c9d
When the `nodeId` is provided, we must verify that the signature is valid and is in normalized low-S form. When the `nodeId` is not provided though, we perform signature recovery, which accepts both low-S and high-S signatures and verifies its validity. See lightning/bolts#1284
When the `nodeId` is provided, we must verify that the signature is valid and is in normalized low-S form. When the `nodeId` is not provided though, we perform signature recovery, which accepts both low-S and high-S signatures and verifies its validity. See lightning/bolts#1284
When the `nodeId` is provided, we must verify that the signature is valid and is in normalized low-S form. When the `nodeId` is not provided though, we perform signature recovery, which accepts both low-S and high-S signatures and verifies its validity. See lightning/bolts#1284
Roasbeef
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.
LGTM 🍦
|
Ack, thanks! |
Explicitly document that signature validation requirements differ based on the presence of the
nfield:nfield: signature MUST be normalized lower-S formnfield: pubkey recovery accepts both high-S and low-SThis clarifies existing implementation behavior where
secp256k1_ecdsa_verify(used withnfield) requires normalized signatures, whilesecp256k1_ecdsa_recover(used withoutnfield) accepts both normalizedand non-normalized signatures.
cc @real-or-random