mirror of
https://github.com/lightning/bolts.git
synced 2025-01-18 21:32:31 +01:00
BOLT 4: first pass copy edit to line 366
This commit is contained in:
parent
7ce3341254
commit
94f717410f
@ -92,10 +92,10 @@ A number of encryption and verification keys are derived from the shared secret:
|
||||
|
||||
The key generation takes a key-type (_rho_=`0x72686F`, _mu_=`0x6d75` or _um_=`0x756d`) and a 32-byte secret as inputs and returns a 32-byte key.
|
||||
|
||||
Keys are generated by computing an HMAC, with `SHA256` as hashing algorithm, using the key-type, i.e., _rho_, _mu_ or _um_, as HMAC-key and the 32-byte shared secret as the message.
|
||||
Keys are generated by computing an HMAC, with `SHA256` as hashing algorithm, using the key-type, i.e. _rho_, _mu_ or _um_, as HMAC-key and the 32-byte shared secret as the message.
|
||||
The resulting HMAC is then returned as the key.
|
||||
|
||||
Notice that the key-type does not include a C-style `0x00` termination-byte, e.g., the length of the _rho_ key-type is 3 bytes, not 4.
|
||||
Notice that the key-type does not include a C-style `0x00` termination-byte, e.g. the length of the _rho_ key-type is 3 bytes, not 4.
|
||||
|
||||
# Pseudo Random Byte Stream
|
||||
|
||||
@ -134,14 +134,13 @@ The `hops_data` field is a structure that holds obfuscated versions of the next
|
||||
* ...
|
||||
* `filler`
|
||||
|
||||
Where the `realm`, `HMAC` and `per_hop` (whose contents depend on `realm`) are
|
||||
repeated for each hop, and `filler` consists of obfuscated deterministically generated
|
||||
padding. For details about how the `filler` is generated please see
|
||||
below. In addition, `hops_data` is incrementally
|
||||
Where the `realm`, `HMAC`, and `per_hop` (of which contents depend on `realm`) are
|
||||
repeated for each hop; and where `filler` consists of obfuscated, deterministically-generated
|
||||
padding. The generation of `filler` is detailed below. Additionally, `hops_data` is incrementally
|
||||
obfuscated at each hop.
|
||||
|
||||
The `realm` byte determines the format of the `per_hop`; so far
|
||||
only `realm` 0 is defined, and for that, the `per_hop` format is:
|
||||
The `realm` byte determines the format of the `per_hop`; so far,
|
||||
only `realm` 0 is defined, for which the `per_hop` format follows:
|
||||
|
||||
1. type: `per_hop` (for `realm` 0)
|
||||
2. data:
|
||||
@ -152,120 +151,148 @@ only `realm` 0 is defined, and for that, the `per_hop` format is:
|
||||
|
||||
Using the `per_hop`, the sender is able to precisely specify the path and
|
||||
structure of the HTLCs forwarded at each hop. As the `per_hop` is
|
||||
protected under the packet-wide HMAC, the information within
|
||||
protected under the packet-wide HMAC, the information it contains
|
||||
is fully authenticated with each pair-wise relationship between
|
||||
the HTLC sender, and each intermediate node in the path. Using this end-to-end
|
||||
authentication, forwarding nodes are able to ensure that the incoming node
|
||||
didn't forward an ill-crafted HTLC by cross-checking the HTLC parameters with
|
||||
the values as specified within the `per_hop`.
|
||||
the HTLC sender and each intermediate node in the path. Using this end-to-end
|
||||
authentication as well as cross-checking the HTLC parameters with
|
||||
the values specified within the `per_hop`, forwarding nodes are able to ensure that the incoming node
|
||||
hasn't forwarded an ill-crafted HTLC.
|
||||
|
||||
Field Description:
|
||||
Field descriptions:
|
||||
|
||||
* `short_channel_id` - The channel used to route the message, which implies the
|
||||
* `short_channel_id`: [FIXME: Please reword, meaning is unclear] The channel used to route the message, which implies the
|
||||
next hop is the other end of the channel.
|
||||
|
||||
* `amt_to_forward` - The amount in milli-satoshi to forward to the next
|
||||
* `amt_to_forward`: The amount, in milli-satoshis, to forward to the next
|
||||
(outgoing) hop specified within the routing information.
|
||||
|
||||
This value MUST factor in the computed fee for this particular hop. When
|
||||
This value amount MUST factor in the computed fee for this particular hop. When
|
||||
processing an incoming Sphinx packet along with the HTLC message it's
|
||||
encapsulated within, if the following inequality doesn't hold, then the
|
||||
HTLC should be rejected as it indicates a prior node in the path has
|
||||
HTLC should be rejected; as this indicates a prior node in the path has
|
||||
deviated from the specified parameters:
|
||||
|
||||
incoming_htlc_amt - fee >= amt_to_forward
|
||||
|
||||
Where `fee` is calculated according to the receiving node's advertised fee
|
||||
schema as described in [BOLT 7](https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md#htlc-fees), or 0 if this node is the
|
||||
final hop.
|
||||
Where `fee` is either calculated according to the receiving node's advertised fee
|
||||
schema (as described in [BOLT 7](https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md#htlc-fees)) or is 0, if this node is the final hop.
|
||||
|
||||
* `outgoing_cltv_value` - The CLTV value that the _outgoing_ HTLC carrying
|
||||
* `outgoing_cltv_value`: The CLTV value that the _outgoing_ HTLC carrying
|
||||
the packet should have.
|
||||
|
||||
cltv_expiry - cltv_expiry_delta >= outgoing_cltv_value
|
||||
|
||||
Inclusion of this field allows a node to both authenticate the information
|
||||
specified by the original sender and the parameters of the HTLC forwarded,
|
||||
specified by the original sender, and the parameters of the HTLC forwarded,
|
||||
and ensure the original sender is using the current `cltv_expiry_delta` value.
|
||||
If there is no next hop, `cltv_expiry_delta` is 0.
|
||||
If the values don't correspond, then the HTLC should be failed+rejected as
|
||||
this indicates the incoming node has tampered with the intended HTLC
|
||||
If the values don't correspond, then the HTLC should be failed and rejected as
|
||||
this indicates that either the incoming node has tampered with the intended HTLC
|
||||
values, or the origin has an obsolete `cltv_expiry_delta` value.
|
||||
The node MUST be consistent in responding to an unexpected
|
||||
`outgoing_cltv_value` whether it is the final hop or not, to avoid
|
||||
leaking that information.
|
||||
`outgoing_cltv_value`, whether it is the final hop or not, to avoid
|
||||
leaking its position in the route.
|
||||
|
||||
* `padding` - for future use, and also to ensure that future non-0-`realm`
|
||||
`per_hop` won't change the overall `hops_data` size.
|
||||
* `padding`: This field is for future use and also for ensuring that future non-0-`realm`
|
||||
`per_hop`s won't change the overall `hops_data` size.
|
||||
|
||||
Nodes forwarding HTLCs MUST construct the outgoing HTLC as specified within
|
||||
`per_hop`. Otherwise, deviation from the specified HTLC parameters
|
||||
When forwarding HTLCs, nodes MUST construct the outgoing HTLC as specified within
|
||||
`per_hop` above; otherwise, deviation from the specified HTLC parameters
|
||||
may lead to extraneous routing failure.
|
||||
|
||||
## Payload for the Last Node
|
||||
|
||||
The last node in the route could just discard its payload since it will not forward payments. However, when building the route, the original
|
||||
sender must use a payload for the last node with the following values:
|
||||
* `outgoing_cltv_value` is set to the final expiry specified by the recipient
|
||||
* `amt_to_forward` is set to the final amount specified by the recipient
|
||||
When building the route, the original sender MUST use a payload for
|
||||
the last node with the following values:
|
||||
* `outgoing_cltv_value`: set to the final expiry specified by the recipient
|
||||
* `amt_to_forward`: set to the final amount specified by the recipient
|
||||
|
||||
This way, the final node can check these values and return errors if needed, which will defeat probing attacks by the next to last node which could
|
||||
try to find out if the next node is the last one (by re-sending HTLCs with different amounts/expiries):
|
||||
This allows the final node to check these values and return errors if needed,
|
||||
but it also eliminates the possibility of probing attacks by the second-to-last
|
||||
node. Such attacks could, otherwise, attempt to discover if the next node is the
|
||||
last one by re-sending HTLCs with different amounts/expiries.
|
||||
The last node will extract its onion payload from the HTLC it has received and
|
||||
compare its values against those of the HTLC. See the
|
||||
[Returning Errors](#returning-errors) section below for more details.
|
||||
|
||||
The last node will extract its onion payload from the HTLC it has received and compare its values to the HTLC values.
|
||||
See the [Returning Errors](#returning-errors) section below for more details.
|
||||
If not for the above, since it need not forward payments, the last node could
|
||||
simply discard its payload.
|
||||
|
||||
# Packet Construction
|
||||
|
||||
Assuming a _sender node_ `n_0` wants to route a packet to a _final recipient_ `n_r`.
|
||||
The sender computes a route `{n_0, n_1, ..., n_{r-1}, n_r}`, where `n_0` is the sender itself and `n_r` is the final recipient.
|
||||
The nodes `n_i` and `n_{i+1}` MUST be peers in the overlay network.
|
||||
The sender gathers the public keys for `n_1` to `n_r` and generates a random 32-byte `sessionkey`.
|
||||
Optionally the sender may pass in _associated data_, i.e., data that the packet commits to, but is not included in the packet itself.
|
||||
Associated data will be included in the HMACs and has to match the associated data provided during integrity verification at each hop.
|
||||
In the following example, it's assumed that _sender node_, `n_0`, wants to route
|
||||
a packet to a _final recipient_, `n_r`.
|
||||
First, the sender computes a route `{n_0, n_1, ..., n_{r-1}, n_r}`, where `n_0`
|
||||
is the sender itself and `n_r` is the final recipient. The nodes `n_i` and
|
||||
`n_{i+1}` MUST be peers in the overlay network. The sender then gathers the
|
||||
public keys for `n_1` to `n_r` and generates a random 32-byte `sessionkey`.
|
||||
Optionally, the sender may pass in _associated data_, i.e. data that the
|
||||
packet commits to but that is not included in the packet itself. Associated
|
||||
data will be included in the HMACs and must match the associated data provided
|
||||
during integrity verification at each hop.
|
||||
|
||||
For each node the sender computes an _ephemeral public key_, a _shared secret_ and a _blinding factor_.
|
||||
The blinding factor is used at each hop to blind the ephemeral public key for the next hop.
|
||||
The node receiving the header will perform ECDH with the ephemeral public key and its private key to derive the same shared secret.
|
||||
However, when generating the packet we do not have access to the node's private key.
|
||||
Hence, we use the commutative property of multiplication and blind the node's public key with all previous blinding factors and perform ECDH using the node's blinded public key and the `sessionkey`.
|
||||
Next, for each node along the route, the sender computes an
|
||||
_ephemeral public key_, a _shared secret_, and a _blinding factor_. The blinding
|
||||
factor is used at each hop to blind the ephemeral public key for the next hop.
|
||||
The node receiving the header will perform ECDH with the ephemeral public key
|
||||
and its own private key in order to derive the same shared secret.
|
||||
However, when generating the packet, the sender node doesn't have access to the
|
||||
other nodes' private keys. So instead, it uses the commutative property of
|
||||
multiplication to blind each node's public key with all previous blinding
|
||||
factors and performs ECDH using each node's blinded public key and the `sessionkey`.
|
||||
|
||||
The transformations at hop `k` are given by the following:
|
||||
|
||||
- The shared secret `ss_k` is computed by first blinding the node's public key `nodepk_k` with all previous blinding factors `{b_1, ..., b_{k-1}}`, if any, and then executing ECDH with the blinded public key and the `sessionkey`.
|
||||
- The blinding factor is the `SHA256` hash of the concatenation between the node's public key `nodepk_k` and the hop's shared secret `ss_k`.
|
||||
Before concatenation the node's public key is serialized in the compressed format.
|
||||
- The ephemeral public key `epk_k` is computed by blinding the previous hop's ephemeral public key `epk_{k-1}` with the previous hop's blinding factor `b_{k-1}`.
|
||||
- The shared secret `ss_k` is computed by first blinding the node's public key
|
||||
`nodepk_k` with all previous blinding factors `{b_1, ..., b_{k-1}}` (if any),
|
||||
and second executing ECDH with the blinded public key and the `sessionkey`.
|
||||
- The blinding factor is the `SHA256` hash of the concatenation between the
|
||||
node's public key `nodepk_k` and the hop's shared secret `ss_k`. Before
|
||||
concatenation, the node's public key is serialized in the compressed format.
|
||||
- The ephemeral public key `epk_k` is computed by blinding the previous hop's
|
||||
ephemeral public key `epk_{k-1}` with the previous hop's blinding factor `b_{k-1}`.
|
||||
|
||||
This recursive algorithm is initialized by setting the first hop's (`k=1`) ephemeral public key to the public key corresponding with the `sessionkey`, i.e., `secp256k1` is used to derive a public key for the randomly selected `sessionkey`.
|
||||
This recursive algorithm is initialized by setting the first hop's (`k=1`)
|
||||
ephemeral public key to the public key corresponding to the `sessionkey`, i.e.
|
||||
`secp256k1` is used to derive a public key for the randomly selected `sessionkey`.
|
||||
|
||||
The sender then iteratively computes the ephemeral public keys, shared secrets and blinding factors for nodes `{n_2, ..., n_r}`.
|
||||
The sender then iteratively computes the ephemeral public keys, shared secrets,
|
||||
and blinding factors for nodes `{n_2, ..., n_r}`.
|
||||
|
||||
Once the sender has all the required information it can construct the packet.
|
||||
Constructing a packet routed over `r` hops requires `r` 32-byte ephemeral public keys, `r` 32-byte shared secrets, `r` 32-byte blinding factors and `r` 65-byte `per_hop` payloads.
|
||||
The construction returns a single 1366-byte packet and the first hop's address.
|
||||
Once the sender has all the required information above, it can construct the packet.
|
||||
Constructing a packet routed over `r` hops requires `r` 32-byte ephemeral
|
||||
public keys, `r` 32-byte shared secrets, `r` 32-byte blinding factors, and `r`
|
||||
65-byte `per_hop` payloads.
|
||||
The construction returns a single 1366-byte packet along with the first hop's address.
|
||||
|
||||
The packet construction is performed in reverse order of the route, i.e., the last hop's operations are applied first.
|
||||
The packet construction is performed in the reverse order of the route, i.e.
|
||||
the last hop's operations are applied first.
|
||||
|
||||
The packet is initialized with 1366 `0x00`-bytes.
|
||||
|
||||
A 65-byte filler is generated with the shared secret:
|
||||
See below for details on filler generation.
|
||||
A 65-byte filler is generated with the shared secret (generation of filler data
|
||||
is detailed below).
|
||||
|
||||
For each hop in the route in reverse order the sender applies the
|
||||
For each hop in the route, in reverse order, the sender applies the
|
||||
following operations:
|
||||
|
||||
- It generates a _rho_-key and _mu_-key using the hop's shared secret.
|
||||
- The `hops_data` field is right-shifted by 65 bytes, discarding the last 65 bytes that exceed the 1300 bytes.
|
||||
The `version`, `short_channel_id`, `amt_to_forward`, `outgoing_cltv_value`, `padding` and `HMAC` are copied into
|
||||
the following 65 bytes.
|
||||
The _rho_-key is used to generate 1300 bytes of pseudo-random byte stream and applied with `XOR` to the `hops_data` field.
|
||||
Should this be the last hop, i.e., the first iteration, then the tail of the `hops_data` field is overwritten with the routing info `filler`.
|
||||
- The next HMAC is computed over the concatenated `hops_data` and associated data, with the _mu_-key as HMAC-key.
|
||||
- The _rho_-key and _mu_-key are generated using the hop's shared secret.
|
||||
- The `hops_data` field is right-shifted by 65 bytes, discarding the last 65
|
||||
bytes that exceed its 1300 byte size.
|
||||
- The `version`, `short_channel_id`, `amt_to_forward`, `outgoing_cltv_value`,
|
||||
`padding`, and `HMAC` are copied into the following 65 bytes.
|
||||
- The _rho_-key is used to generate 1300 bytes of pseudo-random byte stream
|
||||
and applied, with `XOR`, to the `hops_data` field.
|
||||
- If this is the last hop, i.e. the first iteration, then the tail of the
|
||||
`hops_data` field is overwritten with the routing info `filler`.
|
||||
- The next HMAC is computed (with the _mu_-key as HMAC-key) over the
|
||||
concatenated `hops_data` and associated data.
|
||||
|
||||
The final value for the HMAC is the HMAC as it should be sent to the first hop.
|
||||
[FIXME: Please reword, meaning is unclear] The final value for the HMAC is the HMAC as it should be sent to the first hop.
|
||||
|
||||
The packet generation returns the serialized packet, consisting of the `version` byte, the ephemeral pubkey for the first hop, the HMAC for the first hop, the obfuscated `hops_data`.
|
||||
The packet generation returns a serialized packet that contains the `version`
|
||||
byte, the ephemeral pubkey for the first hop, the HMAC for the first hop, and
|
||||
the obfuscated `hops_data`.
|
||||
|
||||
The following code implements the packet construction in Go:
|
||||
|
||||
@ -299,7 +326,7 @@ func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey
|
||||
var mixHeader [routingInfoSize]byte
|
||||
var nextHmac [hmacSize]byte
|
||||
|
||||
// Now we compute the routing information for each hop, along with a
|
||||
// Compute the routing information for each hop along with a
|
||||
// MAC of the routing info using the shared key for that hop.
|
||||
for i := numHops - 1; i >= 0; i-- {
|
||||
rhoKey := generateKey("rho", hopSharedSecrets[i])
|
||||
@ -316,7 +343,7 @@ func NewOnionPacket(paymentPath []*btcec.PublicKey, sessionKey *btcec.PrivateKey
|
||||
copy(mixHeader[:], buf.Bytes())
|
||||
xor(mixHeader[:], mixHeader[:], streamBytes[:routingInfoSize])
|
||||
|
||||
// We need to overwrite these so every node generates a correct padding
|
||||
// These need to be overwritten, so every node generates a correct padding
|
||||
if i == numHops-1 {
|
||||
copy(mixHeader[len(mixHeader)-len(filler):], filler)
|
||||
}
|
||||
@ -473,12 +500,12 @@ The obfuscation step is repeated by every node on the return path.
|
||||
Upon receiving a packet the node will generate its `ammag`, generate the pseudo-random byte stream and apply it to the packet before forwarding.
|
||||
|
||||
The origin node detects that it is the final hop of the return message since it was the origin of the corresponding forward packet.
|
||||
When an origin node receive an error message matching a transfer it initiated, i.e., it cannot forward the error any further, the node generates the `ammag` and `um` keys for each hop in the route.
|
||||
When an origin node receive an error message matching a transfer it initiated, i.e. it cannot forward the error any further, the node generates the `ammag` and `um` keys for each hop in the route.
|
||||
The node then iteratively decrypts the error message with each of the `ammag` keys and computes the HMAC with the `um` keys.
|
||||
The origin node can detect the sender of the error message by matching the `hmac` field with the computed HMAC.
|
||||
Once the original message has been decrypted it SHOULD be stored in a copy and the node SHOULD continue decrypting, until the loop has been repeated 20 times, using a constant `ammag` and `um` keys to obfuscate the route length.
|
||||
|
||||
The association between forward and return packet is handled outside of the protocol, e.g., by association to an HTLC in a payment channel.
|
||||
The association between forward and return packet is handled outside of the protocol, e.g. by association to an HTLC in a payment channel.
|
||||
|
||||
## Failure Messages
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user