Repository navigation
BOLT 2, 3, 9: option_independent_secrets - #17
davelund110 wants to merge 7 commits into
Conversation
Add option_independent_secrets (bits 266/267), a channel_type add-on under which per-commitment secrets need not come from a single seed. Each side may generate its secrets by any method, for example jointly by several signers, and each side keeps every secret it receives. BOLT 9 adds the bits, depending on option_channel_type. BOLT 2 adds the add-on to the allowed variations, stops a receiver failing a channel whose secrets are unrelated to each other, and has a node that cannot check your_last_per_commitment_secret treat a later state as lost data instead of broadcasting. BOLT 3 gives the requirements on generating and keeping the secrets, including the compact form while secrets fit it, splices, a local storage limit, and MuSig2 nonces on taproot channels.
… close Three fixes to max-independent-secrets, from review: - A channel the peer leased from us skipped the backstop as well as the mutual close, so until the lease expired a peer could make us store its secrets without limit. We still don't start a mutual close before the lease expires, but the 10% backstop now applies: our funds stay locked until then however the channel closes. - The mutual close started at the cap had no timeout, so a peer could leave it unfinished for ever. After 48 hours, as `close` waits by default, we now close unilaterally. The timer isn't saved, so at startup it is armed again for a channel that is closing and over the cap. --dev-independent-secrets-close-timeout shortens it for tests. - The 10% now counts from where we first saw the channel at or over the cap, so a cap lowered below what a channel already stores starts a mutual close at its next update instead of failing the channel at the one after. Also point the docs and comments at lightning-blake2b/bolts#17, where the spec now lives, instead of the draft bLIP.
Once a channel with the add-on is stored, the database moves to version 39, which an older release refuses to open. With the add-on on by default, two upgraded nodes would use it on every channel they open, so nearly every node would be marked right after upgrading and couldn't roll back if something else in the release went wrong. So it now ships off, and protocol.independent-secrets turns it on, replacing protocol.no-independent-secrets. A release that can read version 39 can then go out first; turning it on by default in a later release leaves a rollback landing on a release that already reads it. dev.independent-secrets-sender now needs protocol.independent-secrets, since its secrets are only accepted on channels with the add-on. The itest turns the option on for both nodes. The feature bit comment and docs now point to lightning-blake2b/bolts#17 instead of a draft bLIP.
|
Thanks Dave, this is a careful write-up. Some points on the text:
|
|
One more from lab testing: the text says a node MUST NOT set 266 in |
From review: - A node generating independent secrets MUST be able to check your_last_per_commitment_secret for any commitment number it may have reached, including after a restore. It SHOULD generate secrets it can compute again (a KDF or a threshold PRF), and may use random ones only if each is stored where a restore can't roll it back. The channel_reestablish rule for a node that can't check stays only as a last resort, and the rationale no longer claims a peer could do the same by not reconnecting: it can't, since the node can still close on its own. - What generates the secrets should be as recoverable as the funding key. - The storage limit SHOULD NOT be below 1,000,000, and revocations that the receiver's own updates cause shouldn't count against it. - A secret is not reused for two commitment numbers (one secret revokes the same number on every funding output after a splice). - A receiver of bit 266 in init MUST close the connection; a node whose secrets don't come from the seed MUST set 267. - The last secret received is kept for channel_reestablish after a close. - Taproot: a multi-signer node produces each partial signature without one signer learning the nonce or another's share, and never signs two different messages with one nonce. - A sentence on watchtowers that keep secrets in the compact form. - Test vectors for the switch from the compact form to storing secrets individually.
…t in init The spec (lightning-blake2b/bolts#17) now says a receiver of bit 266 in init MUST close the connection: the even bit is only valid in channel_type. A node without --independent-secrets already did, since the bit was unknown to it, but one that offers the option knows the bit and kept the peer. Lightning Fork refuses such a peer either way, so the two now match.
|
Thanks Paul, all fair points. They're in 7acb453:
|
… close Three fixes to max-independent-secrets, from review: - A channel the peer leased from us skipped the backstop as well as the mutual close, so until the lease expired a peer could make us store its secrets without limit. We still don't start a mutual close before the lease expires, but the 10% backstop now applies: our funds stay locked until then however the channel closes. - The mutual close started at the cap had no timeout, so a peer could leave it unfinished for ever. After 48 hours, as `close` waits by default, we now close unilaterally. The timer isn't saved, so at startup it is armed again for a channel that is closing and over the cap. --dev-independent-secrets-close-timeout shortens it for tests. - The 10% now counts from where we first saw the channel at or over the cap, so a cap lowered below what a channel already stores starts a mutual close at its next update instead of failing the channel at the one after. Also point the docs and comments at lightning-blake2b/bolts#17, where the spec now lives, instead of the draft bLIP.
…t in init The spec (lightning-blake2b/bolts#17) now says a receiver of bit 266 in init MUST close the connection: the even bit is only valid in channel_type. A node without --independent-secrets already did, since the bit was unknown to it, but one that offers the option knows the bit and kept the peer. Lightning Fork refuses such a peer either way, so the two now match.
|
Thanks Dave, 7acb453 covers everything I raised, and the rationale on the reestablish case reads right now. I checked the new vectors independently (each point is secret*G, and the n: 1 secret derives the seed-based n: 0 secret rather than the vector's, so insertion fails where stated). A few smaller things from going through the full text again:
For the record on conformance: both implementations meet every MUST in the text as it stands, apart from two CLN details I've put on privkeyio/lightning#48. Both ship 267 off by default, which departs from the SHOULD for a seed-based node; that's deliberate on both sides (the receiver's storage, and in lnd a database version older releases can't open), and a line in the rationale saying an implementation may leave it off at first would make that explicit. |
Once a channel with the add-on is stored, the database moves to version 39, which an older release refuses to open. With the add-on on by default, two upgraded nodes would use it on every channel they open, so nearly every node would be marked right after upgrading and couldn't roll back if something else in the release went wrong. So it now ships off, and protocol.independent-secrets turns it on, replacing protocol.no-independent-secrets. A release that can read version 39 can then go out first; turning it on by default in a later release leaves a rollback landing on a release that already reads it. dev.independent-secrets-sender now needs protocol.independent-secrets, since its secrets are only accepted on channels with the add-on. The itest turns the option on for both nodes. The feature bit comment and docs now point to lightning-blake2b/bolts#17 instead of a draft bLIP.
- A node that stores random secrets treats a commitment number with no record as one it has not reached, so it can check your_last_per_commitment_secret for any commitment number. - The storage limit limits revocations accepted, not storage: every accepted secret is still kept. - For a multi-signer node, the nonce rules replace simple taproot's use of NonceGen. - my_current_per_commitment_point is no longer listed: its receiver ignores it. - A receiver of 266 in node_announcement treats it as an unknown even bit. - channel_reestablish: the mismatch branch applies only when the node can check the secret. - Rationale: the dependency on option_channel_type, and leaving 267 off by default at first. - Vectors: no comment lines, an explanation instead; a table of contents entry; words for the spellchecker.
|
Thanks Paul, and thanks for checking the vectors. All of these are in aa350ae:
The two CLN details are fixed on privkeyio/lightning#48 (the dev sender now refuses channels without the add-on, and a limit below 1,000,000 is logged). |
- Secrets differ between channels as well as commitment numbers, and no one may guess one from secrets revealed on any channel: the examples now include a value unique to the channel. A node reusing secrets across channels could lose the second channel's funds. - A node whose secrets several signers generate checks a claimed secret only against the point, never produces nor releases part of the secret of a commitment it hasn't revoked, and generates each secret so that no group smaller than the one needed to revoke learns or chooses it. - channel_reestablish: the rules are nested so "otherwise" is unambiguous, and a node that cannot check only for now waits and reconnects instead of taking the last-resort branch. - A node storing random secrets records the highest number it stored; "not reached" means above it. - The limit is on secrets stored, and every secret received is stored, including beyond it; the close starts as soon as BOLT 2 allows. - A funding output that can no longer confirm ends the duty to keep secrets for it. - Accepting the add-on is a SHOULD, and a node may refuse such channels by its own policy. - Taproot: such a channel can't be opened until unified sigs is defined for MuSig2, and a multi-signer node keeps what it needs for each verification nonce as recoverable as the funding key. - Rationale corrections (update_fee after shutdown, static backups).
paulscode
left a comment
There was a problem hiding this comment.
Approving: aa350ae covers every point, and I re-checked the whole add-on (the reestablish branches, the limit wording, the NonceGen override, links, spellcheck, and the vectors recomputed again).
Three optional nits, fine as they are or in a follow-up:
- The
node_announcementrule summarises BOLT 7 as "SHOULD NOT connect to the node, and MUST NOT route a payment through it", but BOLT 7 has a third rule for an unknown even bit: unless paying a BOLT 11 invoice that doesn't set the bit, MUST NOT send payments to the node. Either list all three or just point to BOLT 7. - The taproot override replaces
NonceGen, but bolt-simple-taproot.md also says to make the partial signature withSignfrom bip-musig2, which a threshold or nested signer can't follow either. The new MUST already says how such a node signs, so it's only wording. - The first storage vector has no
point:lines and the second does.
A correction to my last comment: Appendix E's vectors do use # lines inside a vector, so that point was overstated. Removing them is still fine.
The even bit of option_independent_secrets is only valid in channel_type. lnd knows the bit, so a node announcing it passed the check for unknown required bits and was paid and routed through, while a node that doesn't know the bit refuses it. lightning-blake2b/bolts#17 now has a receiver of 266 in node_announcement treat it as an unknown even bit. ValidateRoutable adds that to ValidateRequired, and pathfinding uses it for the destination and for every intermediate node. init keeps its own, clearer refusal.
- 266 in node_announcement: list all three of BOLT 7's rules for an unknown even bit, including the one on sending payments to the node. - Taproot: a multi-signer node's rules replace Sign from bip-musig2 as well as NonceGen. - The first storage test vector gets its points, as the second has.
… close Three fixes to max-independent-secrets, from review: - A channel the peer leased from us skipped the backstop as well as the mutual close, so until the lease expired a peer could make us store its secrets without limit. We still don't start a mutual close before the lease expires, but the 10% backstop now applies: our funds stay locked until then however the channel closes. - The mutual close started at the cap had no timeout, so a peer could leave it unfinished for ever. After 48 hours, as `close` waits by default, we now close unilaterally. The timer isn't saved, so at startup it is armed again for a channel that is closing and over the cap. --dev-independent-secrets-close-timeout shortens it for tests. - The 10% now counts from where we first saw the channel at or over the cap, so a cap lowered below what a channel already stores starts a mutual close at its next update instead of failing the channel at the one after. Also point the docs and comments at lightning-blake2b/bolts#17, where the spec now lives, instead of the draft bLIP.
…t in init The spec (lightning-blake2b/bolts#17) now says a receiver of bit 266 in init MUST close the connection: the even bit is only valid in channel_type. A node without --independent-secrets already did, since the bit was unknown to it, but one that offers the option knows the bit and kept the peer. Lightning Fork refuses such a peer either way, so the two now match.
|
Thanks Paul, for the approval and for recomputing the vectors again. Your three nits are in fa15616:
Noted on Appendix E; I've left the vectors without comment lines. There's one more commit before that one, cd4d552, so the add-on needs another look, sorry. After aa350ae I had a security review done of the whole text, and it found two gaps that matter for the multi-signer nodes the option is for, plus some smaller things:
Neither implementation needs a change for cd4d552: neither generates non-seed secrets outside the dev sender, which uses a per-channel seed. |
|
Thanks Dave, and thanks for getting the whole text reviewed. I went through cd4d552 and fa15616 against the rest of the spec. Both attacks hold up as described (one seed across channels lets a peer, or two colluding peers, revoke One thing I'd like changed before I re-approve:
Smaller, optional:
On the implementations: I agree neither needs a change. Lightning Fork refuses taproot channel types on mainnet (unified sigs can't be applied to them), and its dev sender's root is per channel. Only with |
…ect to BOLT 7 says a node that receives a node_announcement with an unknown even feature bit SHOULD NOT connect to that node, and lightning-blake2b/bolts#17 has the even bit of option_independent_secrets count as one there. Pathfinding already refuses such nodes through feature.ValidateRoutable, but the graph bootstrapper and the autopilot agent still picked them as peers to dial and to open channels with. autopilot.Node now carries the node's announced features, and autopilot.Connectable checks them with ValidateRoutable. The graph bootstrapper skips nodes that fail it, and the agent leaves them out of the candidates it hands its heuristic. A node whose features aren't known is picked as before. Explicit connections (lncli connect, addpeer) and reconnections to channel peers are unchanged.
…aths findBlindedPaths picked intermediate nodes by RouteBlindingOptional alone, so an invoice or offer could hand out a blinded path through a node that announces an even feature bit we don't know, or the even bit of option_independent_secrets, which lightning-blake2b/bolts#17 has nodes treat as one. Pathfinding refuses to route through such a node since feature.ValidateRoutable, and a payer that doesn't know the bit refuses the path too. The onion message route search had the same gap, and its first hop is a node we may connect to on our own. Both now skip a node whose announced features fail ValidateRoutable, as pathfinding does. Our own node and channels the caller chains in are not checked, as before.
The comment quoted the old example of option_independent_secrets, a key derivation function over a private seed and the commitment number. lightning-blake2b/bolts#17 now names a value unique to the channel as a third input. The producer meets that already: its root is derived per channel, from a fresh revocation root key and the channel's own funding key, so it stands for both the seed and the channel's value. Say so. No change in behaviour.
From the review of cd4d552 and fa15616, and a second security review of the text, which found the first point too: - The value that makes secrets unique to a channel is one the node chooses, fixes when the channel opens and uses for no other channel, e.g. the funding_pubkey it sent in open_channel(2) or accept_channel(2), not one from a later splice: BOLT 2 says a splice SHOULD change it, so a KDF keyed on the current key couldn't recompute earlier secrets, and after a restore would take a true your_last_per_commitment_secret for an incorrect one. A value the peer chooses could also repeat on another channel. - channel_reestablish: a node that can't check your_last_per_commitment_secret only for now must not broadcast its commitment transaction, or fail the channel, for any reason, including the next_revocation_number requirements above, until it can. One that can't check for good, with a number that isn't greater than expected, fails the channel, since it can't revoke either. - A multi-signer node must not check a claimed secret by producing it: it compares it with the point, or, with random secrets, treats a number above the highest recorded as not reached. - A node that deletes random secrets treats a claimed secret for a number it has deleted as correct, since it has revealed it. - Each signer of a multi-signer node checks the peer's signatures for the next commitment before it releases its part of a secret, and then won't sign the revoked commitment for broadcast. - A funding output stops needing secrets only once a transaction conflicting with the one that creates it has confirmed irrevocably. - Under option_unified_sigs no simple taproot channel can be opened yet, with or without this option. - The rationale's description of insertion into the compact form, the Appendix D introduction, the order of the channel_type variations, and retrieve lines for the first storage test vector.
|
Thanks Paul. All of it is in 86d2b67:
The same commit has the rest of a second security review of the text, which also found the
Neither implementation needs a change for it. And thanks for the connect and blinded-path follow-ups on the Lightning Fork side. |
paulscode
left a comment
There was a problem hiding this comment.
Re-approving: 86d2b67 resolves all six, and I went through the second review's changes too. The deleted-secret rule is safe (only revealed secrets can be deleted, and the latest can't), and failing the channel when a node can't check for good and no later state is claimed adds no exposure beyond an ordinary error from the peer. Vectors recompute, retrieve lines included.
Two optional points:
- The signer rule stops the theft, but a compromised coordinator could still collect the secret for
nand discard the peer's signatures forn+1, leaving the node nothing it can broadcast, and missing HTLC signatures lose an HTLC at its deadline even when the commitment itself can be broadcast. Perhaps: "MUST NOT release its part of the secret of commitment numbernuntil it has itself checked, and stored where the node can use them to broadcast, the peer'ssignatureand everyhtlc_signaturefor the node's commitment transaction with commitment numbern+1, on every funding output while a splice is pending." - "For any reason" in the "cannot check only for now" branch also overrides BOLT 2's rule to fail the channel when an HTLC passes its deadline. That seems the right trade, but saying so would help: "(including an HTLC deadline: it accepts that loss rather than risk broadcasting a revoked commitment)".
From a further review of the multi-signer rules, and Paul's two optional points on 86d2b67: - The per-signer rules didn't stop one compromised signer, acting as the coordinator, from collecting both the secret of a commitment and a signature with which the node can broadcast it. It could have the commitment signed first and revoked afterwards; with k-of-n signing, have it revoked by some signers and signed by others that never heard of it; hide a splice from a signer; or have the peer sign a commitment built on a point whose secret it had handed over. A multi-signer node now MUST ensure that this can't happen, that it can't be made to broadcast a revoked commitment, and that it isn't left without a commitment, with its HTLC transactions, that it can broadcast. The text then lists one way to meet that: each signer works out for itself the commitment number and point of what it checks or signs; a signer that has signed n never helps revoke n or later; every holder of a funding key share records a revocation, which the releasing signer checks for itself, before any part of the secret is released; signers check and keep the peer's signatures for n+1, HTLC signatures included, and track funding outputs themselves; and records survive restores, a signer restored without them helping revoke nothing further. - A node that can no longer revoke SHOULD fail the channel. - Points computed from signers' shares must be ones no group smaller than the one whose parts make up a secret can change, and a node that records the highest point it has sent may treat a higher claim as not reached. - channel_reestablish: the "cannot check only for now" branch names what it overrides, including an HTLC reaching its deadline, and still handles a commitment the peer broadcasts. - Examples: a PRF such as HMAC-SHA256 keyed with a seed used for nothing else, and a threshold PRF whose output stays shared. - Taproot: the threshold rather than a single signer, and each signer records, and never reuses, a nonce share. - Smaller: a secret has to be revealable until a later one is revealed; failing past the margin counts only revocations the receiver's own updates didn't cause; why every later secret is stored individually; and two older lines that said secrets can always be stored compactly.
|
Thanks Paul, and for the re-approval. Both your points are in 60cb7b7, but it also changes the multi-signer rules more than I'd like after an approval, so it needs another look, sorry. A further review of those rules found that the per-signer checks in 86d2b67 don't stop one compromised signer, acting as the coordinator, from ending up with both the secret of a commitment and a signature to broadcast it:
Each fix turned up another way round, so rather than a complete signer protocol, the text now states the outcome as a MUST: no group smaller than both the group whose parts make up a secret and the group needed to sign can get both the secret of a commitment and a signature to broadcast it, make the node broadcast a revoked commitment, or (your first point) leave it without a commitment, with its HTLC transactions, that it can broadcast. It then lists one way to meet that:
So every signer has to be reachable for a k-of-n node to revoke, and a node that can't revoke any more SHOULD fail the channel. The rationale explains why, and that a node where only the signers making up a secret record revocations is protected against fewer than t + s - n compromised signers, which for 2-of-3 is none. Your second point: the "cannot check only for now" branch now names what it overrides: the next_revocation_number requirements, an error it receives, and an HTLC reaching its deadline (it accepts that loss rather than risk broadcasting a revoked commitment). It also says the node still handles a commitment the peer broadcasts, as BOLT 5 requires. Smaller:
Neither implementation needs a change for it: neither generates non-seed secrets outside its dev sender. |
cef72b8 to
60cb7b7
Compare
What
This adds
option_independent_secrets(bits 266/267) to these BOLTs, the same wayoption_blake2bandoption_unified_sigsare specified here. It's achannel_typeadd-on under which per-commitment secrets don't have to come from a single seed: each side may generate its secrets by any method, for example jointly by several signers, and each side keeps every secret it receives.It's implemented in Lightning Fork (paulscode/lightning-fork#7, paulscode/lightning-fork#13 and paulscode/lightning-fork#15, all merged) and in the Core Lightning fork (privkeyio/lightning#48), which were tested against each other.
Why
With the seed-based algorithm in BOLT 3, every signer of a k-of-n node has to hold the seed, because any of them may need to revoke a commitment. Any one of them can give the seed to the channel peer, who then holds the secret of the node's current commitment and can take the channel through the revocation path once that commitment is broadcast. So a "2-of-3" node is really 1-of-1. With independent secrets, the signers can generate and reveal each secret jointly, so no single signer ever holds it.
Changes
BOLT 9
option_independent_secrets, bits 266/267, contextINT, depending onoption_channel_type.BOLT 2
first_per_commitment_pointandsecond_per_commitment_pointare "generated", not "derived", as specified in BOLT 3.revoke_and_ack: on such a channel, a receiver must not fail the channel because a secret is unrelated to earlier ones. The existing checks (a valid key that generates the previous point) are unchanged.channel_reestablish: if a peer reports a later state and the node has lost the means to checkyour_last_per_commitment_secretfor good (such as a signer's share), it must not broadcast its commitment transaction and should ask the peer to fail the channel, as for a node that has lost data. This is a last resort: BOLT 3 requires a node to be able to check it. A node that can't check only for now (signers unreachable) doesn't broadcast, fail the channel or send an error for any reason until it can check, including an HTLC reaching its deadline (it accepts that loss rather than risk broadcasting a revoked commitment), but still handles a commitment its peer broadcasts, and disconnects and retries. One that can't check for good, while the peer reports no later state, fails the channel, since it can't revoke either. The rules are nested so that "otherwise" is unambiguous.BOLT 3, new section "Independent Per-commitment Secrets":
initandnode_announcement, the even bit only inchannel_type. A receiver of 266 ininitcloses the connection, and one that sees it innode_announcementtreats it as an unknown even bit, with BOLT 7's three rules for one (don't connect, don't pay the node unless paying a BOLT 11 invoice without the bit, don't route through it). A node whose secrets don't come from the seed must set 267, include the add-on, and refuse channels without it. Other nodes should accept the add-on, and may refuse such channels by their own policy.funding_pubkeyit sent at open but not one from a later splice); random ones only if each is stored where a restore can't roll it back, with the highest number stored; a deleted secret's number counts as revealed. Each one unguessable from secrets revealed on any channel, a valid key, not reused for two commitment numbers or on two channels, and recoverable for every point already sent. A node must be able to checkyour_last_per_commitment_secretfor any commitment number (one that stores random secrets treats a number above the highest recorded as one it hasn't reached), and should keep what generates its secrets as recoverable as its funding key.channel_reestablishafter a close. It may use the compact 49-entry form for as long as each secret fits it, and stores them individually from the first that doesn't. With a pending splice, each secret must be usable on every funding output.option_unified_sigsno simple taproot channel can be opened, with or without this option, until the unified sighash is defined for MuSig2. For a multi-signer node, these rules replace simple taproot's use ofNonceGenandSign: it makes each partial signature without any group smaller than the one needed to sign learning the nonce or another's share, must not derive its MuSig2 nonces from a seed such a group holds, never signs two different messages with one nonce, has each signer record, and never reuse, a nonce share, and keeps what it needs for each verification nonce as recoverable as its funding key.channel_reestablishrule, the bit numbers and the dependency onoption_channel_type, and why an implementation may ship 267 off by default at first.Notes
channel_reestablishrule above, and the receiver's storage limit, which the Core Lightning fork already implements as--max-independent-secrets.node_announcement, and multi: follow-ups to option_independent_secrets and splice reserves paulscode/lightning-fork#15 for connecting and for blinded and onion-message paths). Neither implementation yet generates non-seed secrets outside a developer-only test mode, so the generation andchannel_reestablishrules don't change them; they're there for the multi-signer nodes the option is meant for. Both close the connection on 266 ininit, and both ship 267 off by default for now.channel_reestablishstructure, the storage limit wording, and the taproot notes. The fifth (fa15616) makes the three optional changes from the approval of aa350ae. The sixth (86d2b67) comes from the review of the fourth and fifth, and from a second security review: how the value unique to a channel is chosen (both found it), thechannel_reestablishrule for a node that can't check only for now, how a multi-signer node checks a claimed secret, deleted random secrets, what each signer checks before it releases its part of a secret, when a funding output stops needing secrets, the taproot note, and some smaller wording. The seventh (60cb7b7) comes from a further review of the multi-signer rules and Paul's two optional points on 86d2b67. The review found that one compromised signer, acting as the coordinator, could still collect both a commitment's secret and a signature to broadcast it (by having it signed before revoking it, through signers that never heard of a revocation, by hiding a splice, or by giving the peer a point whose secret it had handed over), so the multi-signer requirement is now stated as an outcome, with one way to meet it. The same commit has the HTLC signatures each signer keeps, what thechannel_reestablish"cannot check only for now" branch overrides, points a smaller group can't change, the KDF and threshold PRF examples, taproot nonce shares, and some smaller wording.