1
0
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:
Landon Mutch 2017-11-28 17:49:17 -08:00 committed by Rusty Russell
parent 7ce3341254
commit 94f717410f

View File

@ -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