mirror of
https://github.com/lightning/bolts.git
synced 2025-03-10 17:18:44 +01:00
BOLT 8: use incremented numbers for numbering.
Markdown doesn't care, but we have humans reading the text. Reported-by: @roasbeef Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
0f50cc220d
commit
58f6a70889
1 changed files with 54 additions and 54 deletions
108
08-transport.md
108
08-transport.md
|
@ -226,47 +226,47 @@ and 16 bytes for the `poly1305` tag.
|
||||||
**Sender Actions:**
|
**Sender Actions:**
|
||||||
|
|
||||||
1. `e = generateKey()`
|
1. `e = generateKey()`
|
||||||
1. `h = SHA-256(h || e.pub.serializeCompressed())`
|
2. `h = SHA-256(h || e.pub.serializeCompressed())`
|
||||||
* The newly generated ephemeral key is accumulated into the running
|
* The newly generated ephemeral key is accumulated into the running
|
||||||
handshake digest.
|
handshake digest.
|
||||||
1. `ss = ECDH(rs, e.priv)`
|
3. `ss = ECDH(rs, e.priv)`
|
||||||
* The initiator performs an ECDH between its newly generated ephemeral
|
* The initiator performs an ECDH between its newly generated ephemeral
|
||||||
key and the remote node's static public key.
|
key and the remote node's static public key.
|
||||||
1. `ck, temp_k1 = HKDF(ck, ss)`
|
4. `ck, temp_k1 = HKDF(ck, ss)`
|
||||||
* A new temporary encryption key is generated, which is
|
* A new temporary encryption key is generated, which is
|
||||||
used to generate the authenticating MAC.
|
used to generate the authenticating MAC.
|
||||||
1. `c = encryptWithAD(temp_k1, 0, h, zero)`
|
5. `c = encryptWithAD(temp_k1, 0, h, zero)`
|
||||||
* where `zero` is a zero-length plaintext
|
* where `zero` is a zero-length plaintext
|
||||||
1. `h = SHA-256(h || c)`
|
6. `h = SHA-256(h || c)`
|
||||||
* Finally, the generated ciphertext is accumulated into the authenticating
|
* Finally, the generated ciphertext is accumulated into the authenticating
|
||||||
handshake digest.
|
handshake digest.
|
||||||
1. Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer.
|
7. Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer.
|
||||||
|
|
||||||
**Receiver Actions:**
|
**Receiver Actions:**
|
||||||
|
|
||||||
1. Read _exactly_ 50 bytes from the network buffer.
|
1. Read _exactly_ 50 bytes from the network buffer.
|
||||||
1. Parse out the read message (`m`) into `v = m[0]`, `re = m[1:33]`, and `c = m[34:]`.
|
2. Parse out the read message (`m`) into `v = m[0]`, `re = m[1:33]`, and `c = m[34:]`.
|
||||||
* where `m[0]` is the _first_ byte of `m`, `m[1:33]` is the next 33
|
* where `m[0]` is the _first_ byte of `m`, `m[1:33]` is the next 33
|
||||||
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
|
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
|
||||||
* The raw bytes of the remote party's ephemeral public key (`e`) are to be
|
* The raw bytes of the remote party's ephemeral public key (`e`) are to be
|
||||||
deserialized into a point on the curve using affine coordinates as encoded
|
deserialized into a point on the curve using affine coordinates as encoded
|
||||||
by the key's serialized composed format.
|
by the key's serialized composed format.
|
||||||
1. If `v` is an unrecognized handshake version, then the responder MUST
|
3. If `v` is an unrecognized handshake version, then the responder MUST
|
||||||
abort the connection attempt.
|
abort the connection attempt.
|
||||||
1. `h = SHA-256(h || re.serializeCompressed())`
|
4. `h = SHA-256(h || re.serializeCompressed())`
|
||||||
* The responder accumulates the initiator's ephemeral key into the authenticating
|
* The responder accumulates the initiator's ephemeral key into the authenticating
|
||||||
handshake digest.
|
handshake digest.
|
||||||
1. `ss = ECDH(re, s.priv)`
|
5. `ss = ECDH(re, s.priv)`
|
||||||
* The responder performs an ECDH between its static public key and the
|
* The responder performs an ECDH between its static public key and the
|
||||||
initiator's ephemeral public key.
|
initiator's ephemeral public key.
|
||||||
1. `ck, temp_k1 = HKDF(ck, ss)`
|
6. `ck, temp_k1 = HKDF(ck, ss)`
|
||||||
* A new temporary encryption key is generated, which will
|
* A new temporary encryption key is generated, which will
|
||||||
shortly be used to check the authenticating MAC.
|
shortly be used to check the authenticating MAC.
|
||||||
1. `p = decryptWithAD(temp_k1, 0, h, c)`
|
7. `p = decryptWithAD(temp_k1, 0, h, c)`
|
||||||
* If the MAC check in this operation fails, then the initiator does _not_
|
* If the MAC check in this operation fails, then the initiator does _not_
|
||||||
know the responder's static public key. If so, then the responder MUST terminate the
|
know the responder's static public key. If so, then the responder MUST terminate the
|
||||||
connection without any further messages.
|
connection without any further messages.
|
||||||
1. `h = SHA-256(h || c)`
|
8. `h = SHA-256(h || c)`
|
||||||
* The received ciphertext is mixed into the handshake digest. This step serves
|
* The received ciphertext is mixed into the handshake digest. This step serves
|
||||||
to ensure the payload wasn't modified by a MiTM.
|
to ensure the payload wasn't modified by a MiTM.
|
||||||
|
|
||||||
|
@ -288,44 +288,44 @@ for the `poly1305` tag.
|
||||||
**Sender Actions:**
|
**Sender Actions:**
|
||||||
|
|
||||||
1. `e = generateKey()`
|
1. `e = generateKey()`
|
||||||
1. `h = SHA-256(h || e.pub.serializeCompressed())`
|
2. `h = SHA-256(h || e.pub.serializeCompressed())`
|
||||||
* The newly generated ephemeral key is accumulated into the running
|
* The newly generated ephemeral key is accumulated into the running
|
||||||
handshake digest.
|
handshake digest.
|
||||||
1. `ss = ECDH(re, e.priv)`
|
3. `ss = ECDH(re, e.priv)`
|
||||||
* where `re` is the ephemeral key of the initiator, which was received
|
* where `re` is the ephemeral key of the initiator, which was received
|
||||||
during Act One
|
during Act One
|
||||||
1. `ck, temp_k2 = HKDF(ck, ss)`
|
4. `ck, temp_k2 = HKDF(ck, ss)`
|
||||||
* A new temporary encryption key is generated, which is
|
* A new temporary encryption key is generated, which is
|
||||||
used to generate the authenticating MAC.
|
used to generate the authenticating MAC.
|
||||||
1. `c = encryptWithAD(temp_k2, 0, h, zero)`
|
5. `c = encryptWithAD(temp_k2, 0, h, zero)`
|
||||||
* where `zero` is a zero-length plaintext
|
* where `zero` is a zero-length plaintext
|
||||||
1. `h = SHA-256(h || c)`
|
6. `h = SHA-256(h || c)`
|
||||||
* Finally, the generated ciphertext is accumulated into the authenticating
|
* Finally, the generated ciphertext is accumulated into the authenticating
|
||||||
handshake digest.
|
handshake digest.
|
||||||
1. Send `m = 0 || e.pub.serializeCompressed() || c` to the initiator over the network buffer.
|
7. Send `m = 0 || e.pub.serializeCompressed() || c` to the initiator over the network buffer.
|
||||||
|
|
||||||
|
|
||||||
**Receiver Actions:**
|
**Receiver Actions:**
|
||||||
|
|
||||||
1. Read _exactly_ 50 bytes from the network buffer.
|
1. Read _exactly_ 50 bytes from the network buffer.
|
||||||
1. Parse out the read message (`m`) into `v = m[0]`, `re = m[1:33]`, and `c = m[34:]`.
|
2. Parse out the read message (`m`) into `v = m[0]`, `re = m[1:33]`, and `c = m[34:]`.
|
||||||
* where `m[0]` is the _first_ byte of `m`, `m[1:33]` is the next 33
|
* where `m[0]` is the _first_ byte of `m`, `m[1:33]` is the next 33
|
||||||
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
|
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
|
||||||
1. If `v` is an unrecognized handshake version, then the responder MUST
|
3. If `v` is an unrecognized handshake version, then the responder MUST
|
||||||
abort the connection attempt.
|
abort the connection attempt.
|
||||||
1. `h = SHA-256(h || re.serializeCompressed())`
|
4. `h = SHA-256(h || re.serializeCompressed())`
|
||||||
1. `ss = ECDH(re, e.priv)`
|
5. `ss = ECDH(re, e.priv)`
|
||||||
* where `re` is the responder's ephemeral public key
|
* where `re` is the responder's ephemeral public key
|
||||||
* The raw bytes of the remote party's ephemeral public key (`re`) are to be
|
* The raw bytes of the remote party's ephemeral public key (`re`) are to be
|
||||||
deserialized into a point on the curve using affine coordinates as encoded
|
deserialized into a point on the curve using affine coordinates as encoded
|
||||||
by the key's serialized composed format.
|
by the key's serialized composed format.
|
||||||
1. `ck, temp_k2 = HKDF(ck, ss)`
|
6. `ck, temp_k2 = HKDF(ck, ss)`
|
||||||
* A new temporary encryption key is generated, which is
|
* A new temporary encryption key is generated, which is
|
||||||
used to generate the authenticating MAC.
|
used to generate the authenticating MAC.
|
||||||
1. `p = decryptWithAD(temp_k2, 0, h, c)`
|
7. `p = decryptWithAD(temp_k2, 0, h, c)`
|
||||||
* If the MAC check in this operation fails, then the initiator MUST
|
* If the MAC check in this operation fails, then the initiator MUST
|
||||||
terminate the connection without any further messages.
|
terminate the connection without any further messages.
|
||||||
1. `h = SHA-256(h || c)`
|
8. `h = SHA-256(h || c)`
|
||||||
* The received ciphertext is mixed into the handshake digest. This step serves
|
* The received ciphertext is mixed into the handshake digest. This step serves
|
||||||
to ensure the payload wasn't modified by a MiTM.
|
to ensure the payload wasn't modified by a MiTM.
|
||||||
|
|
||||||
|
@ -351,14 +351,14 @@ construction, and 16 bytes for a final authenticating tag.
|
||||||
|
|
||||||
1. `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())`
|
1. `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())`
|
||||||
* where `s` is the static public key of the initiator
|
* where `s` is the static public key of the initiator
|
||||||
1. `h = SHA-256(h || c)`
|
2. `h = SHA-256(h || c)`
|
||||||
1. `ss = ECDH(re, s.priv)`
|
3. `ss = ECDH(re, s.priv)`
|
||||||
* where `re` is the ephemeral public key of the responder.
|
* where `re` is the ephemeral public key of the responder.
|
||||||
1. `ck, temp_k3 = HKDF(ck, ss)`
|
4. `ck, temp_k3 = HKDF(ck, ss)`
|
||||||
* The final intermediate shared secret is mixed into the running chaining key.
|
* The final intermediate shared secret is mixed into the running chaining key.
|
||||||
1. `t = encryptWithAD(temp_k3, 0, h, zero)`
|
5. `t = encryptWithAD(temp_k3, 0, h, zero)`
|
||||||
* where `zero` is a zero-length plaintext
|
* where `zero` is a zero-length plaintext
|
||||||
1. `sk, rk = HKDF(ck, zero)`
|
6. `sk, rk = HKDF(ck, zero)`
|
||||||
* where `zero` is a zero-length plaintext,
|
* where `zero` is a zero-length plaintext,
|
||||||
`sk` is the key to be used by the initiator to encrypt messages to the
|
`sk` is the key to be used by the initiator to encrypt messages to the
|
||||||
responder,
|
responder,
|
||||||
|
@ -366,28 +366,28 @@ construction, and 16 bytes for a final authenticating tag.
|
||||||
the responder
|
the responder
|
||||||
* The final encryption keys to be used for sending and
|
* The final encryption keys to be used for sending and
|
||||||
receiving messages for the duration of the session are generated.
|
receiving messages for the duration of the session are generated.
|
||||||
1. `rn = 0, sn = 0`
|
7. `rn = 0, sn = 0`
|
||||||
* The sending and receiving nonces are initialized to zero.
|
* The sending and receiving nonces are initialized to zero.
|
||||||
1. Send `m = 0 || c || t` over the network buffer.
|
8. Send `m = 0 || c || t` over the network buffer.
|
||||||
|
|
||||||
|
|
||||||
**Receiver Actions:**
|
**Receiver Actions:**
|
||||||
|
|
||||||
1. Read _exactly_ 66 bytes from the network buffer.
|
1. Read _exactly_ 66 bytes from the network buffer.
|
||||||
1. Parse out the read message (`m`) into `v = m[0]`, `c = m[1:49]` and `t = m[50:]`
|
2. Parse out the read message (`m`) into `v = m[0]`, `c = m[1:49]` and `t = m[50:]`
|
||||||
1. If `v` is an unrecognized handshake version, then the responder MUST
|
3. If `v` is an unrecognized handshake version, then the responder MUST
|
||||||
abort the connection attempt.
|
abort the connection attempt.
|
||||||
1. `rs = decryptWithAD(temp_k2, 1, h, c)`
|
4. `rs = decryptWithAD(temp_k2, 1, h, c)`
|
||||||
* At this point, the responder has recovered the static public key of the
|
* At this point, the responder has recovered the static public key of the
|
||||||
initiator.
|
initiator.
|
||||||
1. `h = SHA-256(h || c)`
|
5. `h = SHA-256(h || c)`
|
||||||
1. `ss = ECDH(rs, e.priv)`
|
6. `ss = ECDH(rs, e.priv)`
|
||||||
* where `e` is the responder's original ephemeral key
|
* where `e` is the responder's original ephemeral key
|
||||||
1. `ck, temp_k3 = HKDF(ck, ss)`
|
7. `ck, temp_k3 = HKDF(ck, ss)`
|
||||||
1. `p = decryptWithAD(temp_k3, 0, h, t)`
|
8. `p = decryptWithAD(temp_k3, 0, h, t)`
|
||||||
* If the MAC check in this operation fails, then the responder MUST
|
* If the MAC check in this operation fails, then the responder MUST
|
||||||
terminate the connection without any further messages.
|
terminate the connection without any further messages.
|
||||||
1. `rk, sk = HKDF(ck, zero)`
|
9. `rk, sk = HKDF(ck, zero)`
|
||||||
* where `zero` is a zero-length plaintext,
|
* where `zero` is a zero-length plaintext,
|
||||||
`rk` is the key to be used by the responder to decrypt the messages sent
|
`rk` is the key to be used by the responder to decrypt the messages sent
|
||||||
by the initiator,
|
by the initiator,
|
||||||
|
@ -395,7 +395,7 @@ construction, and 16 bytes for a final authenticating tag.
|
||||||
the initiator
|
the initiator
|
||||||
* The final encryption keys to be used for sending and
|
* The final encryption keys to be used for sending and
|
||||||
receiving messages for the duration of the session are generated.
|
receiving messages for the duration of the session are generated.
|
||||||
1. `rn = 0, sn = 0`
|
10. `rn = 0, sn = 0`
|
||||||
* The sending and receiving nonces are initialized to zero.
|
* The sending and receiving nonces are initialized to zero.
|
||||||
|
|
||||||
## Lightning Message Specification
|
## Lightning Message Specification
|
||||||
|
@ -445,18 +445,18 @@ In order to encrypt a Lightning message (`m`), given a sending key (`sk`) and a
|
||||||
|
|
||||||
1. let `l = len(m)`
|
1. let `l = len(m)`
|
||||||
* where `len` obtains the length in bytes of the Lightning message
|
* where `len` obtains the length in bytes of the Lightning message
|
||||||
1. Serialize `l` into 2 bytes encoded as a big-endian integer.
|
2. Serialize `l` into 2 bytes encoded as a big-endian integer.
|
||||||
1. Encrypt `l` (using `ChaChaPoly-1305`, `sn`, and `sk`), to obtain `lc`
|
3. Encrypt `l` (using `ChaChaPoly-1305`, `sn`, and `sk`), to obtain `lc`
|
||||||
(18 bytes)
|
(18 bytes)
|
||||||
* The nonce `sn` is encoded as a 96-bit little-endian number. As the
|
* The nonce `sn` is encoded as a 96-bit little-endian number. As the
|
||||||
decoded nonce is 64 bits, the 96-bit nonce is encoded as: 32 bits
|
decoded nonce is 64 bits, the 96-bit nonce is encoded as: 32 bits
|
||||||
of leading zeroes followed by a 64-bit value.
|
of leading zeroes followed by a 64-bit value.
|
||||||
* The nonce `sn` MUST be incremented after this step.
|
* The nonce `sn` MUST be incremented after this step.
|
||||||
* A zero-length byte slice is to be passed as the AD (associated data).
|
* A zero-length byte slice is to be passed as the AD (associated data).
|
||||||
1. Finally, encrypt the message itself (`m`) using the same procedure used to
|
4. Finally, encrypt the message itself (`m`) using the same procedure used to
|
||||||
encrypt the length prefix. Let encrypted ciphertext be known as `c`.
|
encrypt the length prefix. Let encrypted ciphertext be known as `c`.
|
||||||
* The nonce `sn` MUST be incremented after this step.
|
* The nonce `sn` MUST be incremented after this step.
|
||||||
1. Send `lc || c` over the network buffer.
|
5. Send `lc || c` over the network buffer.
|
||||||
|
|
||||||
|
|
||||||
### Decrypting Messages
|
### Decrypting Messages
|
||||||
|
@ -465,14 +465,14 @@ In order to decrypt the _next_ message in the network stream, the following is
|
||||||
done:
|
done:
|
||||||
|
|
||||||
1. Read _exactly_ 18 bytes from the network buffer.
|
1. Read _exactly_ 18 bytes from the network buffer.
|
||||||
1. Let the encrypted length prefix be known as `lc`
|
2. Let the encrypted length prefix be known as `lc`
|
||||||
1. Decrypt `lc` (using `ChaCha20-Poly1305`, `rn`, and `rk`), to obtain the size of
|
3. Decrypt `lc` (using `ChaCha20-Poly1305`, `rn`, and `rk`), to obtain the size of
|
||||||
the encrypted packet `l`.
|
the encrypted packet `l`.
|
||||||
* A zero-length byte slice is to be passed as the AD (associated data).
|
* A zero-length byte slice is to be passed as the AD (associated data).
|
||||||
* The nonce `rn` MUST be incremented after this step.
|
* The nonce `rn` MUST be incremented after this step.
|
||||||
1. Read _exactly_ `l+16` bytes from the network buffer, let the bytes be known as
|
4. Read _exactly_ `l+16` bytes from the network buffer, let the bytes be known as
|
||||||
`c`.
|
`c`.
|
||||||
1. Decrypt `c` (using `ChaCha20-Poly1305`, `rn`, and `rk`), to obtain decrypted
|
5. Decrypt `c` (using `ChaCha20-Poly1305`, `rn`, and `rk`), to obtain decrypted
|
||||||
plaintext packet `p`.
|
plaintext packet `p`.
|
||||||
* The nonce `rn` MUST be incremented after this step.
|
* The nonce `rn` MUST be incremented after this step.
|
||||||
|
|
||||||
|
@ -491,10 +491,10 @@ to it exceeds 1000.
|
||||||
Key rotation for a key `k` is performed according to the following:
|
Key rotation for a key `k` is performed according to the following:
|
||||||
|
|
||||||
1. Let `ck` be the chaining key obtained at the end of Act Three.
|
1. Let `ck` be the chaining key obtained at the end of Act Three.
|
||||||
1. `ck', k' = HKDF(ck, k)`
|
2. `ck', k' = HKDF(ck, k)`
|
||||||
1. Reset the nonce for the key to `n = 0`.
|
3. Reset the nonce for the key to `n = 0`.
|
||||||
1. `k = k'`
|
4. `k = k'`
|
||||||
1. `ck = ck'`
|
5. `ck = ck'`
|
||||||
|
|
||||||
# Security Considerations
|
# Security Considerations
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue