1
0
Fork 0
mirror of https://github.com/lightning/bolts.git synced 2025-02-23 14:40:41 +01:00

BOLT-8 Edits

More clarity and copyediting. I also removed quite a few `s that didn't seem to match general usage for "code".
This commit is contained in:
Shannon Appelcline 2017-12-07 16:16:24 -08:00 committed by Rusty Russell
parent 27cc354c63
commit 2b8b491c74

View file

@ -1,17 +1,17 @@
# BOLT #8: Encrypted and Authenticated Transport # BOLT #8: Encrypted and Authenticated Transport
All communications between Lightning nodes is encrypted in order to All communications between Lightning nodes is encrypted in order to
provide confidentiality for all transcripts between nodes, and authenticated to provide confidentiality for all transcripts between nodes and is authenticated in order to
avoid malicious interference. Each node has a known long-term identifier which avoid malicious interference. Each node has a known long-term identifier that
is a public key on Bitcoin's `secp256k1` curve. This long-term public key is is a public key on Bitcoin's `secp256k1` curve. This long-term public key is
used within the protocol to establish an encrypted+authenticated connection used within the protocol to establish an encrypted and authenticated connection
with peers, and also to authenticate any information advertised on behalf with peers, and also to authenticate any information advertised on behalf
of a node. of a node.
## Cryptographic Messaging Overview ## Cryptographic Messaging Overview
Prior to sending any lightning messages, nodes must first initiate the Prior to sending any Lightning messages, nodes must first initiate the
cryptographic session state which is used to encrypt and authenticate all cryptographic session state that is used to encrypt and authenticate all
messages sent between nodes. The initialization of this cryptographic session messages sent between nodes. The initialization of this cryptographic session
state is completely distinct from any inner protocol message header or state is completely distinct from any inner protocol message header or
conventions. conventions.
@ -19,60 +19,60 @@ conventions.
The transcript between two nodes is separated into two distinct segments: The transcript between two nodes is separated into two distinct segments:
1. First, before any actual data transfer, both nodes participate in an 1. First, before any actual data transfer, both nodes participate in an
authenticated key agreement protocol which is based off of the Noise authenticated key agreement handshake, which is based off of the Noise
Protocol Framework<sup>[2](#reference-2)</sup>. Protocol Framework<sup>[2](#reference-2)</sup>.
2. If the initial handshake is successful, then nodes enter the lightning 2. If the initial handshake is successful, then nodes enter the Lightning
message exchange phase. In the lightning message exchange phase, all message exchange phase. In the Lightning message exchange phase, all
messages are `AEAD` ciphertexts. messages are Authenticated Encryption with Associated Data (AEAD) ciphertexts.
### Authenticated Key Agreement Handshake ### Authenticated Key Agreement Handshake
The handshake chosen for the authenticated key exchange is `Noise_XK`. As a The handshake chosen for the authenticated key exchange is `Noise_XK`. Prior
"pre-message", we assume that the initiator knows the identity public key of to the handshake, the initiator must know the identity public key of
the responder. This handshake provides a degree of identity hiding for the the responder. This provides a degree of identity hiding for the
responder, its public key is _never_ transmitted during the handshake. Instead, responder, as its public key is _never_ transmitted during the handshake. Instead,
authentication is achieved implicitly via a series of `ECDH` (Elliptic-Curve authentication is achieved implicitly via a series of Elliptic-Curve
Diffie-Hellman) operations followed by a `MAC` check. Diffie-Hellman (ECDH) operations followed by a MAC check.
The authenticated key agreement (`Noise_XK`) is performed in three distinct The authenticated key agreement (`Noise_XK`) is performed in three distinct
steps. During each "act" of the handshake, some (possibly encrypted) keying steps. During each "act" of the handshake: some (possibly encrypted) keying
material is sent to the other party, an `ECDH` is performed based on exactly material is sent to the other party; an ECDH is performed based on exactly
which act is being executed with the result mixed into the current set of which act is being executed, with the result mixed into the current set of
encryption keys (`ck` the chaining key and `k` the encryption key), and finally encryption keys (`ck` the chaining key and `k` the encryption key); and
an `AEAD` payload with a zero length cipher text is sent. As this payload is an AEAD payload with a zero-length cipher text is sent. As this payload is
of length zero, only a `MAC` is sent across. The mixing of `ECDH` outputs into length zero, only a MAC is sent across. The mixing of ECDH outputs into
a hash digest forms an incremental TripleDH handshake. a hash digest forms an incremental TripleDH handshake.
Using the language of the Noise Protocol, `e` and `s` (both public keys) Using the language of the Noise Protocol, `e` and `s` (both public keys)
indicate possibly encrypted keying material, and `es, ee, se` each indicate an indicate possibly encrypted keying material, and `es`, `ee`, and `se` each indicate an
`ECDH` operation between two keys. The handshake is laid out as follows: ECDH operation between two keys. The handshake is laid out as follows:
```
Noise_XK(s, rs): Noise_XK(s, rs):
<- s <- s
... ...
-> e, es -> e, es
<- e, ee <- e, ee
-> s, se -> s, se
```
All of the handshake data sent across the wire, including the keying material, is All of the handshake data sent across the wire, including the keying material, is
incrementally hashed into a session-wide "handshake digest", `h`. Note that the incrementally hashed into a session-wide "handshake digest", `h`. Note that the
handshake state `h`, is never transmitted during the handshake, instead digest handshake state `h`, is never transmitted during the handshake; instead, digest
is used as the Associated Data within the zero-length AEAD messages. is used as the Associated Data within the zero-length AEAD messages.
By authenticating each message sent, we can ensure that a MiTM hasn't modified Authenticating each message sent ensures that a man-in-the-middle (MITM) hasn't modified
or replaced any of the data sent across as part of a handshake, as the MAC or replaced any of the data sent as part of a handshake, as the MAC
check would fail on the other side if so. check would fail on the other side if so.
A successful check of the `MAC` by the receiver indicates implicitly that all A successful check of the MAC by the receiver indicates implicitly that all
authentication has been successful up to that point. If `MAC` check ever fails authentication has been successful up to that point. If a MAC check ever fails
during the handshake process, then the connection is to be immediately during the handshake process, then the connection is to be immediately
terminated. terminated.
## Handshake Versioning ### Handshake Versioning
Each message sent during the initial handshake starts with a single leading Each message sent during the initial handshake starts with a single leading
byte which indicates the version used for the current handshake. A version of 0 byte, which indicates the version used for the current handshake. A version of 0
indicates that no change is necessary, while a non-zero version indicate the indicates that no change is necessary, while a non-zero version indicate that the
client has deviated from the protocol originally specified within this client has deviated from the protocol originally specified within this
document. Clients MUST reject handshake attempts initiated with an unknown document. Clients MUST reject handshake attempts initiated with an unknown
version. version.
@ -81,26 +81,26 @@ version.
Concrete instantiations of the Noise Protocol require the definition of Concrete instantiations of the Noise Protocol require the definition of
three abstract cryptographic objects: the hash function, the elliptic curve, three abstract cryptographic objects: the hash function, the elliptic curve,
and finally the `AEAD` cipher scheme. Within our instantiation `SHA-256` is and the AEAD cipher scheme. For Lightning, `SHA-256` is
chosen as the hash function, `secp256k1` as the elliptic curve, and finally chosen as the hash function, `secp256k1` as the elliptic curve, and
`ChaChaPoly-1305` as the `AEAD` construction. The composition of `ChaCha20` `ChaChaPoly-1305` as the AEAD construction. The composition of `ChaCha20`
and `Poly1305` used MUST conform to `RFC 7539`<sup>[1](#reference-1)</sup>. With this laid out, the and `Poly1305` that are used MUST conform to `RFC 7539`<sup>[1](#reference-1)</sup>.
official Noise protocol name for our variant is:
The official protocol name for the Lightning variant of Noise is
`Noise_XK_secp256k1_ChaChaPoly_SHA256`. The ASCII string representation of `Noise_XK_secp256k1_ChaChaPoly_SHA256`. The ASCII string representation of
this value is hashed into a digest used to initialize the starting handshake this value is hashed into a digest used to initialize the starting handshake
state. If the protocol names of two endpoints differ, then the handshake state. If the protocol names of two endpoints differ, then the handshake
process fails immediately. process fails immediately.
## Authenticated Key Exchange Handshake Specification ## Authenticated Key Exchange Handshake Specification
The handshake proceeds in three acts, taking 1.5 round trips. Each handshake is The handshake proceeds in three acts, taking 1.5 round trips. Each handshake is
a _fixed_ sized payload without any header or additional meta-data attached. a _fixed_ sized payload without any header or additional meta-data attached.
The exact size of each Act is as follows: The exact size of each Act is as follows:
* **Act One**: `50 bytes` * **Act One**: 50 bytes
* **Act Two**: `50 bytes` * **Act Two**: 50 bytes
* **Act Three**: `66 bytes` * **Act Three**: 66 bytes
### Handshake State ### Handshake State
@ -108,52 +108,49 @@ Throughout the handshake process, each side maintains these variables:
* `ck`: The **chaining key**. This value is the accumulated hash of all * `ck`: The **chaining key**. This value is the accumulated hash of all
previous ECDH outputs. At the end of the handshake, `ck` is used to derive previous ECDH outputs. At the end of the handshake, `ck` is used to derive
the encryption keys for lightning messages. the encryption keys for Lightning messages.
* `h`: The **handshake hash**. This value is the accumulated hash of _all_ * `h`: The **handshake hash**. This value is the accumulated hash of _all_
handshake data that has been sent and received so far during the handshake handshake data that has been sent and received so far during the handshake
process. process.
* `temp_k1`, `temp_k2`, `temp_k3`: **intermediate keys** used to encrypt/decrypt the * `temp_k1`, `temp_k2`, `temp_k3`: **intermediate keys**. These are used to encrypt and decrypt the
zero-length AEAD payloads at the end of each handshake message. zero-length AEAD payloads at the end of each handshake message.
* `e`: A party's **ephemeral keypair**. For each session a node MUST generate a * `e`: A party's **ephemeral keypair**. For each session a node MUST generate a
new ephemeral key with strong cryptographic randomness. new ephemeral key with strong cryptographic randomness.
* `s`: A party's **static public key** (`ls` for local, `rs` for remote) * `s`: A party's **static public key** (`ls` for local, `rs` for remote).
The following functions will also be referenced: The following functions will also be referenced:
* `ECDH(rk, k)`: Performs an Elliptic-Curve Diffie-Hellman operation using * `ECDH(rk, k)`: performs an Elliptic-Curve Diffie-Hellman operation using
`rk` which is a `secp256k1` public key and `k` which is a valid private key `rk`, which is a `secp256k1` public key, and `k`, which is a valid private key
within the finite field as defined by the curve parameters. within the finite field as defined by the curve parameters
* The returned value is the SHA256 of the DER compressed format of the * The returned value is the SHA256 of the DER-compressed format of the
generated point. generated point.
* `HKDF(salt,ikm)`: a function is defined in [3](#reference-3), evaluated with a * `HKDF(salt,ikm)`: a function defined in [3](#reference-3), evaluated with a
zero-length `info` field. zero-length `info` field
* All invocations of the `HKDF` implicitly return `64-bytes` of * All invocations of `HKDF` implicitly return 64 bytes of
cryptographic randomness using the extract-and-expand component of the cryptographic randomness using the extract-and-expand component of the
`HKDF`. `HKDF`.
* `encryptWithAD(k, n, ad, plaintext)`: outputs `encrypt(k, n, ad, plaintext)` * `encryptWithAD(k, n, ad, plaintext)`: outputs `encrypt(k, n, ad, plaintext)`
* where `encrypt` is an evaluation of `ChaCha20-Poly1305` (IETF variant) with the passed * where `encrypt` is an evaluation of `ChaCha20-Poly1305` (IETF variant) with the passed
arguments, with nonce `n` encoded as 32 zero bits followed by a *little-endian* 64-bit value (this arguments, with nonce `n` encoded as 32 zero bits, followed by a *little-endian* 64-bit value (this
follows the Noise Protocol convention, rather than our normal endian). follows the Noise Protocol convention, rather than our normal endian).
* `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n, ad, ciphertext)` * `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n, ad, ciphertext)`
* where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF variant) with the passed * where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF variant) with the passed
arguments, with nonce `n` encoded as 32 zero bits followed by a *little-endian* 64-bit value. arguments, with nonce `n` encoded as 32 zero bits, followed by a *little-endian* 64-bit value.
* `generateKey()` * `generateKey()`: generates and returns a fresh `secp256k1` keypair
* where generateKey generates and returns a fresh `secp256k1` keypair * where the object returned by `generateKey` has two attributes:
* the object returned by `generateKey` has two attributes: * `.pub`, which returns an abstract object representing the public key
* `.pub`: which returns an abstract object representing the public key * `.priv`, which represents the private key used to generate the
* `.priv`: which represents the private key used to generate the
public key public key
* the object also has a single method: * where the object also has a single method:
* `.serializeCompressed()` * `.serializeCompressed()`
* `a || b` denotes the concatenation of two byte strings `a` and `b` * `a || b` denotes the concatenation of two byte strings `a` and `b`
@ -178,20 +175,17 @@ state as follows:
As a concluding step, both sides mix the responder's public key into the As a concluding step, both sides mix the responder's public key into the
handshake digest: handshake digest:
* The initiating node mixes in the responding node's static public key * The initiating node mixes in the responding node's static public key
serialized in Bitcoin's DER compressed format: serialized in Bitcoin's DER-compressed format:
* `h = SHA-256(h || rs.pub.serializeCompressed())` * `h = SHA-256(h || rs.pub.serializeCompressed())`
* The responding node mixes in their local static public key serialized in * The responding node mixes in their local static public key serialized in
Bitcoin's DER compressed format: Bitcoin's DER-compressed format:
* `h = SHA-256(h || ls.pub.serializeCompressed())` * `h = SHA-256(h || ls.pub.serializeCompressed())`
### Handshake Exchange ### Handshake Exchange
#### Act One #### Act One
@ -199,177 +193,118 @@ handshake digest:
-> e, es -> e, es
``` ```
Act One is sent from initiator to responder. During Act One, the initiator
Act One is sent from initiator to responder. During `Act One`, the initiator
attempts to satisfy an implicit challenge by the responder. To complete this attempts to satisfy an implicit challenge by the responder. To complete this
challenge, the initiator _must_ know the static public key of the responder. challenge, the initiator _must_ know the static public key of the responder.
The handshake message is _exactly_ 50 bytes: 1 byte for the handshake
The handshake message is _exactly_ `50 bytes`: `1 byte` for the handshake version, 33 bytes for the compressed ephemeral public key of the initiator,
version, `33 bytes` for the compressed ephemeral public key of the initiator, and 16 bytes for the `poly1305` tag.
and `16 bytes` for the `poly1305` tag.
**Sender Actions:** **Sender Actions:**
* `e = generateKey()` * `e = generateKey()`
* `h = SHA-256(h || e.pub.serializeCompressed())` * `h = SHA-256(h || e.pub.serializeCompressed())`
* The newly generated ephemeral key is accumulated into our running * The newly generated ephemeral key is accumulated into the running
handshake digest. handshake digest.
* `ss = ECDH(rs, e.priv)` * `ss = ECDH(rs, e.priv)`
* The initiator performs a `ECDH` between its newly generated ephemeral * The initiator performs an ECDH between its newly generated ephemeral
key with the remote node's static public key. key and the remote node's static public key.
* `ck, temp_k1 = HKDF(ck, ss)` * `ck, temp_k1 = HKDF(ck, ss)`
* This phase generates a new temporary encryption key which is * A new temporary encryption key is generated, which is
used to generate the authenticating MAC. used to generate the authenticating MAC.
* `c = encryptWithAD(temp_k1, 0, h, zero)` * `c = encryptWithAD(temp_k1, 0, h, zero)`
* where `zero` is a zero-length plaintext * where `zero` is a zero-length plaintext
* `h = SHA-256(h || c)` * `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.
* Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer. * Send `m = 0 || e.pub.serializeCompressed() || c` to the responder over the network buffer.
**Receiver Actions:** **Receiver Actions:**
* Read _exactly_ 50 bytes from the network buffer.
* Read _exactly_ `50-bytes` from the network buffer. * 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
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
* 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]` are the next `33`
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.
* If `v` is an unrecognized handshake version, then the responder MUST * If `v` is an unrecognized handshake version, then the responder MUST
abort the connection attempt. abort the connection attempt.
* `h = SHA-256(h || re.serializeCompressed())` * `h = SHA-256(h || re.serializeCompressed())`
* Accumulate the initiator's ephemeral key into the authenticating * The responder accumulates the initiator's ephemeral key into the authenticating
handshake digest. handshake digest.
* `ss = ECDH(re, s.priv)` * `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.
* `ck, temp_k1 = HKDF(ck, ss)` * `ck, temp_k1 = HKDF(ck, ss)`
* This phase generates a new temporary encryption key which will * A new temporary encryption key is generated, which will
be used to shortly check the authenticating MAC. shortly be used to check the authenticating MAC.
* `p = decryptWithAD(temp_k1, 0, h, c)` * `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 our 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.
* `h = SHA-256(h || c)` * `h = SHA-256(h || c)`
* Mix the received ciphertext 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.
#### Act Two #### Act Two
``` ```
<- e, ee <- e, ee
``` ```
`Act Two` is sent from the responder to the initiator. `Act Two` will _only_ Act Two is sent from the responder to the initiator. Act Two will _only_
take place if `Act One` was successful. `Act One` was successful if the take place if Act One was successful. Act One was successful if the
responder was able to properly decrypt and check the `MAC` of the tag sent at responder was able to properly decrypt and check the MAC of the tag sent at
the end of `Act One`. the end of Act One.
The handshake is _exactly_ `50 bytes:` `1 byte` for the handshake version, `33 The handshake is _exactly_ 50 bytes: 1 byte for the handshake version, 33
bytes` for the compressed ephemeral public key of the responder, and `16 bytes` bytes for the compressed ephemeral public key of the responder, and 16 bytes
for the `poly1305` tag. for the `poly1305` tag.
**Sender Actions:** **Sender Actions:**
* `e = generateKey()` * `e = generateKey()`
* `h = SHA-256(h || e.pub.serializeCompressed())` * `h = SHA-256(h || e.pub.serializeCompressed())`
* The newly generated ephemeral key is accumulated into our running * The newly generated ephemeral key is accumulated into the running
handshake digest. handshake digest.
* `ss = ECDH(re, e.priv)` * `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 `ActOne`. during Act One
* `ck, temp_k2 = HKDF(ck, ss)` * `ck, temp_k2 = HKDF(ck, ss)`
* This phase generates a new temporary encryption key which is * A new temporary encryption key is generated, which is
used to generate the authenticating MAC. used to generate the authenticating MAC.
* `c = encryptWithAD(temp_k2, 0, h, zero)` * `c = encryptWithAD(temp_k2, 0, h, zero)`
* where `zero` is a zero-length plaintext * where `zero` is a zero-length plaintext
* `h = SHA-256(h || c)` * `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.
* Send `m = 0 || e.pub.serializeCompressed() || c` to the initiator over the network buffer. * Send `m = 0 || e.pub.serializeCompressed() || c` to the initiator over the network buffer.
**Receiver Actions:** **Receiver Actions:**
* Read _exactly_ 50-bytes from the network buffer.
* Read _exactly_ `50-bytes` from the network buffer. * 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
bytes of `m`, and `m[34:]` is the last 16 bytes of `m`
* 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]` are the next `33`
bytes of `m` and `m[34:]` is the last 16 bytes of `m`
* If `v` is an unrecognized handshake version, then the responder MUST * If `v` is an unrecognized handshake version, then the responder MUST
abort the connection attempt. abort the connection attempt.
* `h = SHA-256(h || re.serializeCompressed())` * `h = SHA-256(h || re.serializeCompressed())`
* `ss = ECDH(re, e.priv)` * `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.
* `ck, temp_k2 = HKDF(ck, ss)` * `ck, temp_k2 = HKDF(ck, ss)`
* This phase generates a new temporary encryption key which is * A new temporary encryption key is generated, which is
used to generate the authenticating MAC. used to generate the authenticating MAC.
* `p = decryptWithAD(temp_k2, 0, h, c)` * `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.
* `h = SHA-256(h || c)` * `h = SHA-256(h || c)`
* Mix the received ciphertext 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.
@ -378,129 +313,86 @@ for the `poly1305` tag.
-> s, se -> s, se
``` ```
Act Three is the final phase in the authenticated key agreement described in
`Act Three` is the final phase in the authenticated key agreement described in this section. This act is sent from the initiator to the responder as a
this section. This act is sent from the initiator to the responder as a final concluding step. Act Three is executed _if and only if_ Act Two was successful.
concluding step. `Act Three` is only executed `iff` `Act Two` was successful. During Act Three, the initiator transports its static public key to the
During `Act Three`, the initiator transports its static public key to the responder encrypted with _strong_ forward secrecy, using the accumulated `HKDF`
responder encrypted with _strong_ forward secrecy using the accumulated `HKDF`
derived secret key at this point of the handshake. derived secret key at this point of the handshake.
The handshake is _exactly_ 66 bytes: 1 byte for the handshake version, 33
The handshake is _exactly_ `66 bytes`: `1 byte` for the handshake version, `33 bytes for the ephemeral public key encrypted with the `ChaCha20` stream
bytes` for the ephemeral public key encrypted with the `ChaCha20` stream cipher, 16 bytes for the encrypted public key's tag generated via the AEAD
cipher, `16 bytes` for the encrypted public key's tag generated via the `AEAD` construction, and 16 bytes for a final authenticating tag.
construction, and `16 bytes` for a final authenticating tag.
**Sender Actions:** **Sender Actions:**
* `c = encryptWithAD(temp_k2, 1, h, s.pub.serializeCompressed())` * `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
* `h = SHA-256(h || c)` * `h = SHA-256(h || c)`
* `ss = ECDH(re, s.priv)` * `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.
* `ck, temp_k3 = HKDF(ck, ss)` * `ck, temp_k3 = HKDF(ck, ss)`
* Mix the final intermediate shared secret into the running chaining key. * The final intermediate shared secret is mixed into the running chaining key.
* `t = encryptWithAD(temp_k3, 0, h, zero)` * `t = encryptWithAD(temp_k3, 0, h, zero)`
* where `zero` is a zero-length plaintext * where `zero` is a zero-length plaintext
* `sk, rk = HKDF(ck, zero)` * `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,
and `rk` is the key to be used by the initiator to decrypt messages sent by and `rk` is the key to be used by the initiator to decrypt messages sent by
the responder. the responder
* The final encryption keys to be used for sending and
* This step generates 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.
* `rn = 0, sn = 0` * `rn = 0, sn = 0`
* The sending and receiving nonces are initialized to zero. * The sending and receiving nonces are initialized to zero.
* Send `m = 0 || c || t` over the network buffer. * Send `m = 0 || c || t` over the network buffer.
**Receiver Actions:** **Receiver Actions:**
* Read _exactly_ 66-bytes from the network buffer.
* Read _exactly_ `66-bytes` from the network buffer.
* Parse out the read message (`m`) into `v = m[0]`, `c = m[1:49]` and `t = m[50:]` * Parse out the read message (`m`) into `v = m[0]`, `c = m[1:49]` and `t = m[50:]`
* If `v` is an unrecognized handshake version, then the responder MUST * If `v` is an unrecognized handshake version, then the responder MUST
abort the connection attempt. abort the connection attempt.
* `rs = decryptWithAD(temp_k2, 1, h, c)` * `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.
* `h = SHA-256(h || c)` * `h = SHA-256(h || c)`
* `ss = ECDH(rs, e.priv)` * `ss = ECDH(rs, e.priv)`
* where `e` is the responder's original ephemeral key * where `e` is the responder's original ephemeral key
* `ck, temp_k3 = HKDF(ck, ss)` * `ck, temp_k3 = HKDF(ck, ss)`
* `p = decryptWithAD(temp_k3, 0, h, t)` * `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.
* `rk, sk = HKDF(ck, zero)` * `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,
and `sk` is the key to be used by the responder to encrypt messages to and `sk` is the key to be used by the responder to encrypt messages to
the initiator, the initiator
* The final encryption keys to be used for sending and
* This step generates 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.
* `rn = 0, sn = 0` * `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
At the conclusion of `Act Three` both sides have derived the encryption keys At the conclusion of Act Three, both sides have derived the encryption keys, which will be used to encrypt and decrypt messages for the remainder of the
which will be used to encrypt/decrypt messages for the remainder of the
session. session.
The actual lightning protocol messages are encapsulated within `AEAD` ciphertexts. Each message is prefixed with The actual Lightning protocol messages are encapsulated within AEAD ciphertexts. Each message is prefixed with
another `AEAD` ciphertext which encodes the total length of the following lightning another AEAD ciphertext, which encodes the total length of the following Lightning
message (not counting its MAC). message (not counting its MAC).
The *maximum* size of _any_ lightning message MUST NOT exceed `65535` bytes. A The *maximum* size of _any_ Lightning message MUST NOT exceed `65535` bytes. A
maximum size of `65535` simplifies testing, makes memory management maximum size of `65535` simplifies testing, makes memory management
easier and helps mitigate memory exhaustion attacks. easier, and helps mitigate memory-exhaustion attacks.
In order to make make traffic analysis more difficult, the length prefix for In order to make make traffic analysis more difficult, the length prefix for
all encrypted lightning messages is also encrypted. Additionally we add a all encrypted Lightning messages is also encrypted. Additionally a
`16-byte` `Poly-1305` tag to the encrypted length prefix in order to ensure 16-byte `Poly-1305` tag is added to the encrypted length prefix in order to ensure
that the packet length hasn't been modified with in-flight, and also to avoid that the packet length hasn't been modified when in-flight and also to avoid
creating a decryption oracle. creating a decryption oracle.
The structure of packets on the wire resembles the following: The structure of packets on the wire resembles the following:
@ -513,100 +405,76 @@ The structure of packets on the wire resembles the following:
+------------------------------- +-------------------------------
| | | |
| | | |
| encrypted lightning | | encrypted Lightning |
| message | | message |
| | | |
+------------------------------- +-------------------------------
| 16-byte MAC of the | | 16-byte MAC of the |
| lightning message | | Lightning message |
+------------------------------- +-------------------------------
``` ```
The prefixed message length is encoded as a `2-byte` big-endian integer, The prefixed message length is encoded as a 2-byte big-endian integer,
for a total maximum packet length of `2 + 16 + 65535 + 16` = `65569` bytes. for a total maximum packet length of `2 + 16 + 65535 + 16` = `65569` bytes.
### Encrypting Messages ### Encrypting Messages
In order to encrypt a Lightning message (`m`), given a sending key (`sk`) and a nonce
In order to encrypt a lightning message (`m`), given a sending key (`sk`), and a nonce
(`sn`), the following is done: (`sn`), the following is done:
* let `l = len(m)`
* 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. * Serialize `l` into 2 bytes encoded as a big-endian integer.
* Encrypt `l` using `ChaChaPoly-1305`, `sn`, and `sk`, to obtain `lc`
(18 bytes)
* Serialize `l` into `2-bytes` encoded as a big-endian integer. * 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
* Encrypt `l` using `ChaChaPoly-1305`, `sn`, and `sk` to obtain `lc`
(`18-bytes`)
* The nonce `sn` is encoded as a 96-bit little-endian number. As our
decoded nonces a 64-bit, we encode the 96-bit nonce as follows: 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).
* Finally, encrypt the message itself (`m`) using the same procedure used to
* 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.
* Send `lc || c` over the network buffer. * Send `lc || c` over the network buffer.
### Decrypting Messages ### Decrypting Messages
In order to decrypt the _next_ message in the network stream, the following is In order to decrypt the _next_ message in the network stream, the following is
done: done:
* Read _exactly_ 18 bytes from the network buffer.
* Read _exactly_ `18-bytes` from the network buffer.
* Let the encrypted length prefix be known as `lc` * Let the encrypted length prefix be known as `lc`
* Decrypt `lc` using `ChaCha20-Poly1305`, `rn`, and `rk`, to obtain the size of
* Decrypt `lc` using `ChaCha20-Poly1305`, `rn`, and `rk` to obtain 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.
* Read _exactly_ `l+16` bytes from the network buffer, let the bytes be known as * Read _exactly_ `l+16` bytes from the network buffer, let the bytes be known as
`c`. `c`.
* Decrypt `c` using `ChaCha20-Poly1305`, `rn`, and `rk`, to obtain decrypted
* 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.
## Lightning Message Key Rotation ## Lightning Message Key Rotation
Changing keys regularly and forgetting previous keys is useful to
Changing keys regularly and forgetting the previous key is useful for prevent the decryption of old messages in the case of later key leakage (i.e.
preventing decryption of old messages in the case of later key leakage (ie.
backwards secrecy). backwards secrecy).
Key rotation is performed for _each_ key (`sk` and `rk`) _individually_. A key Key rotation is performed for _each_ key (`sk` and `rk`) _individually_. A key
is to be rotated after a party sends or decrypts `1000` messages with it. is to be rotated after a party sends or decrypts 1000 messages with it.
This can be properly accounted for by rotating the key once the nonce dedicated This can be properly accounted for by rotating the key once the nonce dedicated
to it exceeds `1000`. 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:
* Let `ck` be the chaining key obtained at the end of Act Three.
* Let `ck` be the chaining key obtained at the end of `Act Three`.
* `ck', k' = HKDF(ck, k)` * `ck', k' = HKDF(ck, k)`
* Reset the nonce for the key to `n = 0`. * Reset the nonce for the key to `n = 0`.
* `k = k'` * `k = k'`
* `ck = ck'` * `ck = ck'`
# Security Considerations # # Security Considerations
It is strongly recommended that existing, commonly-used, validated It is strongly recommended that existing, commonly-used, validated
libraries be used for encryption and decryption, to avoid the many libraries be used for encryption and decryption, to avoid the many
@ -614,15 +482,15 @@ implementation pitfalls possible.
# Appendix A: Transport Test Vectors # Appendix A: Transport Test Vectors
To make a repeatable handshake, we specify what `generateKey()` will To make a repeatable test handshake, the following specifies what `generateKey()` will
return (ie. the value for `e.priv`) for each side. Note that this return (i.e. the value for `e.priv`) for each side. Note that this
is a violation of the spec, which requires randomness here. is a violation of the spec, which requires randomness.
## Initiator Tests ## Initiator Tests
The initiator should produce the given output when fed this input. The initiator should produce the given output when fed this input.
The comments reflect internal state for debugging. The comments reflect internal state for debugging.
```
name: transport-initiator successful handshake name: transport-initiator successful handshake
rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7 rs.pub: 0x028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111 ls.priv: 0x1111111111111111111111111111111111111111111111111111111111111111
@ -703,11 +571,11 @@ The comments reflect internal state for debugging.
# Act Two # Act Two
input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730af input: 0x0002466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f276e2470b93aac583c9ef6eafca3f730af
output: ERROR (ACT2_BAD_TAG) output: ERROR (ACT2_BAD_TAG)
```
## Responder Tests ## Responder Tests
The responder should produce the given output when fed this input. The responder should produce the given output when fed this input.
```
name: transport-responder successful handshake name: transport-responder successful handshake
ls.priv=2121212121212121212121212121212121212121212121212121212121212121 ls.priv=2121212121212121212121212121212121212121212121212121212121212121
ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7 ls.pub=028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7
@ -846,12 +714,12 @@ The responder should produce the given output when fed this input.
# Act Three # Act Three
input: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139bb input: 0x00b9e3a702e93e3a9948c2ed6e5fd7590a6e1c3a0344cfc9d5b57357049aa22355361aa02e55a8fc28fef5bd6d71ad0c38228dc68b1c466263b47fdf31e560e139bb
output: ERROR (ACT3_BAD_TAG) output: ERROR (ACT3_BAD_TAG)
```
## Message Encryption Tests ## Message Encryption Tests
In this test, the initiator sends length 5 messages containing "hello" In this test, the initiator sends length 5 messages containing "hello"
1001 times (we only show 6 example outputs for brevity, and to test 1001 times. Only six example outputs are shown, for brevity and to test
two key rotations): two key rotations:
name: transport-message test name: transport-message test
ck=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01 ck=0x919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01