2016-11-30 21:42:22 +01:00
|
|
|
# BOLT #8: Encrypted and Authenticated Transport
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
All communications between Lightning nodes is encrypted in order to
|
2016-11-15 02:23:20 +01:00
|
|
|
provide confidentiality for all transcripts between nodes, and authenticated to
|
|
|
|
avoid malicious interference. Each node has a known long-term identifier which
|
|
|
|
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
|
|
|
|
with peers, and also to authenticate any information advertised on the behalf
|
|
|
|
of a node.
|
|
|
|
|
|
|
|
## Cryptographic Messaging Overview
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
Prior to sending any lightning messages, nodes must first initiate the
|
2016-11-15 02:23:20 +01:00
|
|
|
cryptographic session state which is used to encrypt and authenticate all
|
|
|
|
messages sent between nodes. The initialization of this cryptographic session
|
|
|
|
state is completely distinct from any inner protocol message header or
|
|
|
|
conventions.
|
|
|
|
|
|
|
|
The transcript between two nodes is separated into two distinct segments:
|
|
|
|
|
|
|
|
1. First, before any actual data transfer, both nodes participate in an
|
|
|
|
authenticated key agreement protocol which is based off of the Noise
|
|
|
|
Protocol Framework<sup>[4](#reference-4)</sup>.
|
2016-11-29 01:48:29 +01:00
|
|
|
2. If the initial handshake is successful, then nodes enter the lightning
|
|
|
|
message exchange phase. In the lightning message exchange phase, all
|
2016-11-15 02:23:20 +01:00
|
|
|
messages are `AEAD` ciphertexts.
|
|
|
|
|
|
|
|
### Authenticated Key Agreement Handshake
|
|
|
|
|
|
|
|
The handshake chosen for the authenticated key exchange is `Noise_XK`. As a
|
|
|
|
"pre-message", we assume that the initiator knows the identity public key of
|
|
|
|
the responder. This handshake provides a degree of identity hiding for the
|
|
|
|
responder, its public key is _never_ transmitted during the handshake. Instead,
|
2016-11-24 21:58:57 +01:00
|
|
|
authentication is achieved implicitly via a series of `ECDH` (Elliptic-Curve
|
|
|
|
Diffie-Hellman) operations followed by a `MAC` check.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
The authenticated key agreement (`Noise_XK`) is performed in three distinct
|
|
|
|
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
|
|
|
|
which act is being executed with the result mixed into the current sent of
|
2016-11-24 21:58:57 +01:00
|
|
|
encryption keys (`ck` the chainin gkey and `k` the encryption key), and finally
|
|
|
|
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
|
|
|
|
a hash digest forms an incremental TripleDH handshake.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
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
|
|
|
|
`ECDH` operation between two keys. The handshake is laid out as follows:
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
Noise_XK(s, rs):
|
|
|
|
<- s
|
|
|
|
...
|
|
|
|
-> e, es
|
|
|
|
<- e, ee
|
|
|
|
-> s, se
|
|
|
|
|
|
|
|
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
|
|
|
|
handshake state `h`, is never transmitted during the handshake, instead digest
|
|
|
|
is used as the Authenticated Data within the zero-length AEAD messages.
|
|
|
|
|
|
|
|
By authenticating each message sent, we can ensure that a MiTM hasn't modified
|
|
|
|
or replaced any of the data sent across as part of a handshake, as the MAC
|
|
|
|
check would fail on the other side if so.
|
|
|
|
|
|
|
|
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
|
|
|
|
during the handshake process, then the connection is to be immediately
|
|
|
|
terminated.
|
|
|
|
|
|
|
|
## Handshake Versioning
|
|
|
|
|
|
|
|
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
|
|
|
|
indicates that no change is necessary, while a non-zero version indicate the
|
|
|
|
client has deviated from the protocol originally specified within this
|
|
|
|
document. Clients MUST reject handshake attempts initiated with an unknown
|
|
|
|
version.
|
|
|
|
|
|
|
|
### Noise Protocol Instantiation
|
|
|
|
|
2016-11-23 03:50:27 +01:00
|
|
|
Concrete instantiations of the Noise Protocol require the definition of
|
2016-11-15 02:23:20 +01:00
|
|
|
three abstract cryptographic objects: the hash function, the elliptic curve,
|
|
|
|
and finally the `AEAD` cipher scheme. Within our instantiation `SHA-256` is
|
|
|
|
chosen as the hash function, `secp256k1` as the elliptic curve, and finally
|
2016-11-24 21:58:57 +01:00
|
|
|
`ChaChaPoly-1305` as the `AEAD` construction. The composition of `ChaCha20`
|
2016-11-15 02:23:20 +01:00
|
|
|
and `Poly1305` used MUST conform to `RFC 7539`<sup>[3](#reference-3)</sup>. With this laid out, the
|
|
|
|
official Noise protocol name for our variant is:
|
|
|
|
`Noise_XK_secp256k1_ChaChaPoly_SHA256`. The ascii string representation of
|
|
|
|
this value is hashed into a digest used to initialize the starting handshake
|
|
|
|
state. If the protocol names of two endpoints differs, then the handshake
|
|
|
|
process fails immediately.
|
|
|
|
|
|
|
|
|
|
|
|
## Authenticated Key Exchange Handshake Specification
|
|
|
|
|
|
|
|
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.
|
|
|
|
The exact size of each Act is as follows:
|
|
|
|
|
|
|
|
* **Act One**: `50 bytes`
|
|
|
|
* **Act Two**: `50 bytes`
|
|
|
|
* **Act Three**: `66 bytes`
|
|
|
|
|
|
|
|
### Handshake State
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
Throughout the handshake process, each side maintains these variables:
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
* `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
|
2016-11-29 01:48:29 +01:00
|
|
|
the encryption keys for lightning messages.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
* `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
|
|
|
|
process.
|
|
|
|
|
|
|
|
* `temp_k`: An **intermediate key** key used to encrypt/decrypt the
|
|
|
|
zero-length AEAD payloads at the end of each handshake message.
|
|
|
|
|
|
|
|
* `n`: A **counter-based nonce** which is to be used with `temp_k` to encrypt
|
2016-12-06 04:09:11 +01:00
|
|
|
each message with a new nonce. It is encoded as a big-endian number.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `e`: A party's **ephemeral keypair**. For each session a node MUST generate a
|
2016-11-15 02:23:20 +01:00
|
|
|
new ephemeral key with strong cryptographic randomness.
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `s`: A party's **static public key** (`ls` for local, `rs` for remote)
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
The following functions will also be referenced:
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `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
|
|
|
|
within the finite field as defined by the curve paramters.
|
|
|
|
* The returned value is the raw big-endian byte serialization of
|
|
|
|
`x-coordinate` (using affine coordinates) of the generated point.
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `HKDF`: a function is defined in [5](#reference-5), evaluated with a
|
2016-11-24 21:58:57 +01:00
|
|
|
zero-length `info` field.
|
|
|
|
* All invocations of the `HKDF` implicitly return `64-bytes` of
|
|
|
|
cryptographic randomness using the extract-and-expand component of the
|
2016-12-06 04:09:11 +01:00
|
|
|
`HKDF`.
|
2016-11-24 21:58:57 +01:00
|
|
|
|
|
|
|
* `encryptWithAD(k, n, ad, plaintext)`: outputs `encrypt(k, n++, ad, plaintext)`
|
|
|
|
* where `encrypt` is an evaluation of `ChaCha20-Poly1305` with the passed
|
|
|
|
arguments.
|
|
|
|
|
|
|
|
* `decryptWithAD(k, n, ad, ciphertext)`: outputs `decrypt(k, n++, ad, ciphertext)`
|
|
|
|
* where `decrypt` is an evaluation of `ChaCha20-Poly1305` with the passed
|
|
|
|
arguments.
|
|
|
|
|
|
|
|
* `generateKey()`
|
|
|
|
* where generateKey generates and returns a fresh `secp256k1` keypair
|
|
|
|
* the object returned by `generateKey` has two attributes:
|
|
|
|
* `.pub`: which returns an abstract object representing the public key
|
|
|
|
* `.priv`: which represents the private key used to generate the
|
|
|
|
public key
|
|
|
|
* the object also has a single method:
|
|
|
|
* `.serializeCompressed()`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
* `a || b` denotes the concatenation of two byte strings `a` and `b`
|
|
|
|
|
|
|
|
|
|
|
|
### Handshake State Initialization
|
|
|
|
|
|
|
|
Before the start of the first act, both sides initialize their per-sessions
|
|
|
|
state as follows:
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
1. `h = SHA-256(protocolName)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `protocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256"` encoded as
|
|
|
|
an ascii string.
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
2. `ck = h`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
3. `temp_k = empty`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `empty` is a byte string of length 32 fully zeroed out.
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
4. `n = 0`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
5. `h = SHA-256(h || prologue)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `prologue` is the ascii string: `lightning`.
|
|
|
|
|
|
|
|
|
|
|
|
As a concluding step, both sides mix the responder's public key into the
|
|
|
|
handshake digest:
|
|
|
|
|
|
|
|
|
|
|
|
* The initiating node mixes in the responding node's static public key
|
|
|
|
serialized in Bitcoin's DER compressed format:
|
2016-11-24 21:58:57 +01:00
|
|
|
* `h = SHA-256(h || rs.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
* The responding node mixes in their local static public key serialized in
|
|
|
|
Bitcoin's DER compressed format:
|
2016-11-24 21:58:57 +01:00
|
|
|
* `h = SHA-256(h || ls.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
### Handshake Exchange
|
|
|
|
|
|
|
|
|
|
|
|
#### Act One
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
-> e, es
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
challenge, the initiator _must_ know the static public key of the responder.
|
|
|
|
|
|
|
|
|
|
|
|
The handshake message is _exactly_ `50 bytes`: `1 byte` for the handshake
|
|
|
|
version, `33 bytes` for the compressed ephemeral public key of the initiator,
|
|
|
|
and `16 bytes` for the `poly1305` tag.
|
|
|
|
|
|
|
|
|
|
|
|
**Sender Actions:**
|
|
|
|
|
|
|
|
|
|
|
|
* `e = generateKey()`
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `h = SHA-256(h || e.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
* The newly generated ephemeral key is accumulated into our running
|
|
|
|
handshake digest.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(rs, e.priv)`
|
2016-11-24 21:58:57 +01:00
|
|
|
* The initiator performs a `ECDH` between its newly generated ephemeral
|
|
|
|
key with the remote node's static public key.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* This phase generates a new temporary encryption key (`temp_k`) which is
|
|
|
|
used to generate the authenticating MAC.
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `c = encryptWithAD(temp_k, n, h, zero)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `zero` is a zero-length plaintext
|
|
|
|
|
|
|
|
|
|
|
|
* `h = SHA-256(h || c)`
|
|
|
|
* Finally, the generated ciphertext is accumulated into the authenticating
|
|
|
|
handshake digest.
|
|
|
|
|
|
|
|
|
|
|
|
* Send `m = 0 || e || c` to the responder over the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
**Receiver Actions:**
|
|
|
|
|
|
|
|
|
|
|
|
* Read _exactly_ `50-bytes` from the network buffer.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* Parse out the read message (`m`) into `v = m[0]`, `re = m[1:34]` and `c = m[43:]`
|
2016-11-15 02:23:20 +01:00
|
|
|
* 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`
|
2016-11-24 21:58:57 +01:00
|
|
|
* 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
|
|
|
|
by the key's serialized composed format.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-23 03:50:27 +01:00
|
|
|
* If `v` is an unrecognized handshake version, then the responder MUST
|
2016-11-15 02:23:20 +01:00
|
|
|
abort the connection attempt.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `h = SHA-256(h || re.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
* Accumulate the initiator's ephemeral key into the authenticating
|
|
|
|
handshake digest.
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(re, s.priv)`
|
2016-11-24 21:58:57 +01:00
|
|
|
* The responder performs an `ECDH` between its static public key and the
|
2016-11-15 02:23:20 +01:00
|
|
|
initiator's ephemeral public key.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* This phase generates a new temporary encryption key (`temp_k`) which will
|
|
|
|
be used to shortly check the authenticating MAC.
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `p = decryptWithAD(temp_k, n, h, c)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* 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
|
|
|
|
connection without any further messages.
|
|
|
|
|
|
|
|
|
|
|
|
* `h = SHA-256(h || c)`
|
|
|
|
* Mix the received ciphertext into the handshake digest. This step serves
|
|
|
|
to ensure the payload wasn't modified by a MiTM.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Act Two
|
|
|
|
```
|
|
|
|
<- e, ee
|
|
|
|
```
|
|
|
|
|
|
|
|
`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
|
|
|
|
responder was able to properly decrypt and check the `MAC` of the tag sent at
|
|
|
|
the end of `Act One`.
|
|
|
|
|
|
|
|
The handshake is _exactly_ `50 bytes:` `1 byte` for the handshake version, `33
|
|
|
|
bytes` for the compressed ephemeral public key of the initiator, and `16 bytes`
|
|
|
|
for the `poly1305` tag.
|
|
|
|
|
|
|
|
**Sender Actions:**
|
|
|
|
|
|
|
|
|
|
|
|
* `e = generateKey()`
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `h = SHA-256(h || e.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
* The newly generated ephemeral key is accumulated into our running
|
|
|
|
handshake digest.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(re, e.priv)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `re` is the ephemeral key of the initiator which was received
|
|
|
|
during `ActOne`.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* This phase generates a new temporary encryption key (`temp_k`) which is
|
|
|
|
used to generate the authenticating MAC.
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `c = encryptWithAD(temp_k, n, h, zero)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `zero` is a zero-length plaintext
|
|
|
|
|
|
|
|
|
|
|
|
* `h = SHA-256(h || c)`
|
|
|
|
* Finally, the generated ciphertext is accumulated into the authenticating
|
|
|
|
handshake digest.
|
|
|
|
|
|
|
|
* Send `m = 0 || e || c` to the initiator over the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
**Receiver Actions:**
|
|
|
|
|
|
|
|
|
|
|
|
* Read _exactly_ `50-bytes` from the network buffer.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* Parse out the read message (`m`) into `v = m[0]`, `re = m[1:34]` and `c = m[43:]`
|
2016-11-15 02:23:20 +01:00
|
|
|
* 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`
|
|
|
|
|
|
|
|
|
2016-11-23 03:50:27 +01:00
|
|
|
* If `v` is an unrecognized handshake version, then the responder MUST
|
2016-11-15 02:23:20 +01:00
|
|
|
abort the connection attempt.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `h = SHA-256(h || re.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(re, e.priv)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `re` is the responder's ephemeral public key.
|
2016-12-06 04:09:11 +01:00
|
|
|
* The raw bytes of the remote party's ephemeral public key (`re`) are to be
|
2016-11-24 21:58:57 +01:00
|
|
|
deserialized into a point on the curve using affine coordinates as encoded
|
|
|
|
by the key's serialized composed format.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* This phase generates a new temporary encryption key (`temp_k`) which is
|
|
|
|
used to generate the authenticating MAC.
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `p = decryptWithAD(temp_k, n, h, c)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* If the MAC check in this operation fails, then the initiator MUST
|
|
|
|
terminate the connection without any further messages.
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
* `h = SHA-256(h || c)`
|
|
|
|
* Mix the received ciphertext into the handshake digest. This step serves
|
|
|
|
to ensure the payload wasn't modified by a MiTM.
|
|
|
|
|
|
|
|
|
|
|
|
#### Act Three
|
|
|
|
```
|
|
|
|
-> s, se
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`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 final
|
|
|
|
concluding step. `Act Three` is only executed `iff` `Act Two` was successful.
|
|
|
|
During `Act Three`, the initiator transports its static public key to the
|
|
|
|
responder encrypted with _strong_ forward secrecy using the accumulated `HKDF`
|
|
|
|
derived secret key at this point of the handshake.
|
|
|
|
|
|
|
|
|
|
|
|
The handshake is _exactly_ `66 bytes`: `1 byte` for the handshake version, `33
|
|
|
|
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`
|
|
|
|
construction, and `16 bytes` for a final authenticating tag.
|
|
|
|
|
|
|
|
|
|
|
|
**Sender Actions:**
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `c = encryptWithAD(temp_k, n, h, s.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `s` is the static public key of the initiator.
|
|
|
|
|
|
|
|
|
|
|
|
* `h = SHA-256(h || c)`
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(re, s.priv)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `re` is the ephemeral public key of the responder.
|
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-24 21:58:57 +01:00
|
|
|
* Mix the final intermediate shared secret into the running chaining key.
|
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `t = encryptWithAD(temp_k, n, h, zero)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `zero` is a zero-length plaintext
|
|
|
|
|
|
|
|
|
|
|
|
* `sk, rk = HKDF(ck, zero)`
|
|
|
|
* where `zero` is a zero-length plaintext,
|
|
|
|
|
|
|
|
|
|
|
|
`sk` is the key to be used by the initiator to encrypt messages to the
|
|
|
|
responder,
|
|
|
|
|
|
|
|
|
|
|
|
and `rk` is the key to be used by the initiator to decrypt messages sent by
|
|
|
|
the responder.
|
|
|
|
|
|
|
|
* This step generates the final encryption keys to be used for sending and
|
|
|
|
receiving messages for the duration of the session.
|
|
|
|
|
|
|
|
|
|
|
|
* Send `m = 0 || c || t` over the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
**Receiver Actions:**
|
|
|
|
|
|
|
|
|
|
|
|
* Read _exactly_ `66-bytes` from the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
* Parse out the read message (`m`) into `v = m[0]`, `c = m[1:50]` and `t = m[50:]`
|
|
|
|
|
|
|
|
|
2016-11-23 03:50:27 +01:00
|
|
|
* If `v` is an unrecognized handshake version, then the responder MUST
|
2016-11-15 02:23:20 +01:00
|
|
|
abort the connection attempt.
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `rs = decryptWithAD(temp_k, n, h, c)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* At this point, the responder has recovered the static public key of the
|
|
|
|
initiator.
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `h = SHA-256(h || rs.pub.serializeCompressed())`
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ss = ECDH(rs, e.priv)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* where `e` is the responder's original ephemeral key
|
|
|
|
|
2016-12-06 04:09:11 +01:00
|
|
|
* `ck, temp_k = HKDF(ck, ss)`
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce `n` should be reset to zero: `n = 0`.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* `p = decryptWithAD(temp_k, n, h, t)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* If the MAC check in this operation fails, then the responder MUST
|
|
|
|
terminate the connection without any further messages.
|
|
|
|
|
|
|
|
|
|
|
|
* `rk, sk = HKDF(ck, zero)`
|
|
|
|
* where `zero` is a zero-length plaintext,
|
|
|
|
|
|
|
|
|
|
|
|
`rk` is the key to be used by the responder to decrypt the messages sent
|
|
|
|
by the responder,
|
|
|
|
|
|
|
|
|
|
|
|
and `sk` is the key to be used by the initiator to encrypt messages to
|
|
|
|
the responder,
|
|
|
|
|
|
|
|
* This step generates the final encryption keys to be used for sending and
|
|
|
|
receiving messages for the duration of the session.
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
## Lightning Message Specification
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
At the conclusion of `Act Three` both sides have derived the encryption keys
|
|
|
|
which will be used to encrypt/decrypt messages for the remainder of the
|
|
|
|
session.
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
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
|
|
|
|
message (not counting its MAC).
|
|
|
|
|
|
|
|
The *maximum* size of _any_ lightning message MUST NOT exceed `65535` bytes. A
|
BOLT 1, BOLT 2, BOLT 5: 2-byte lengths everywhere.
Since our cryptopacket limits us to 2 bytes, and since people will
send 1-message-per-crypto-packet and nobody will test the
multiple-messages-in-one-cryptopacket code, let's just restrict to
64k messages.
1. Make cryptopacket length not include the HMAC, so we can actually send
64k messages.
2. Remove len prefix from packet, make type 2 bytes, note alignment properties.
3. Change message internal lengths/counts from 4 to 2 bytes, since more
is nonsensical anyway, and this removes a need to check before allocating:
- init feature bitfield length
- error message length
- shutdown scriptpubkey length
- commit_sig number of HTLC signatures
- revoke_and_ack number of HTLC-timeout signatures
4. Change max-accepted-htlcs to two bytes, and limit it to 511 to ensure
that commit_sig will always be under 64k.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2016-11-24 05:58:30 +01:00
|
|
|
maximum size of `65535` simplifies testing, makes memory management
|
|
|
|
easier and helps mitigate memory exhaustion attacks.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-11-23 03:50:27 +01:00
|
|
|
In order to make make traffic analysis more difficult, the length prefix for
|
2016-11-29 01:48:29 +01:00
|
|
|
all encrypted lightning messages is also encrypted. Additionally we add a
|
2016-11-15 02:23:20 +01:00
|
|
|
`16-byte` `Poly-1305` tag to the encrypted length prefix in order to ensure
|
|
|
|
that the packet length hasn't been modified with in-flight, and also to avoid
|
|
|
|
creating a decryption oracle.
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
The structure of packets on the wire resembles the following:
|
2016-11-15 02:23:20 +01:00
|
|
|
```
|
2016-11-29 01:48:29 +01:00
|
|
|
+-------------------------------
|
|
|
|
|2-byte encrypted message length|
|
|
|
|
+-------------------------------
|
|
|
|
| 16-byte MAC of the encrypted |
|
|
|
|
| message length |
|
|
|
|
+-------------------------------
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
| encrypted lightning |
|
|
|
|
| message |
|
|
|
|
| |
|
|
|
|
+-------------------------------
|
|
|
|
| 16-byte MAC of the |
|
|
|
|
| lightning message |
|
|
|
|
+-------------------------------
|
2016-11-15 02:23:20 +01:00
|
|
|
```
|
2016-11-29 01:48:29 +01:00
|
|
|
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.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
### Encrypting Messages
|
|
|
|
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
In order to encrypt a lightning message (`m`), given a sending key (`sk`), and a nonce
|
2016-11-15 02:23:20 +01:00
|
|
|
(`n`), the following is done:
|
|
|
|
|
|
|
|
|
|
|
|
* let `l = len(m)`,
|
2016-11-29 01:48:29 +01:00
|
|
|
where `len` obtains the length in bytes of the lightning message.
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
* Serialize `l` into `2-bytes` encoded as a big-endian integer.
|
|
|
|
|
|
|
|
|
|
|
|
* Encrypt `l` using `ChaChaPoly-1305`, `n`, and `sk` to obtain `lc`
|
|
|
|
(`18-bytes`)
|
2016-11-24 21:58:57 +01:00
|
|
|
* The nonce for `sk` MUST be incremented after this step.
|
|
|
|
* A zero-length byte slice is to be passed as the AD (associated data).
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
* Finally encrypt the message itself (`m`) using the same procedure used to
|
|
|
|
encrypt the length prefix. Let encrypted ciphertext be known as `c`.
|
|
|
|
* The nonce for `sk` MUST be incremented after this step.
|
|
|
|
|
|
|
|
* Send `lc || c` over the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
### Decrypting Messages
|
|
|
|
|
|
|
|
|
|
|
|
In order to decrypt the _next_ message in the network stream, the following is
|
|
|
|
done:
|
|
|
|
|
|
|
|
|
|
|
|
* Read _exactly_ `18-bytes` from the network buffer.
|
|
|
|
|
|
|
|
|
|
|
|
* Let the encrypted length prefix be known as `lc`
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* Decrypt `lc` using `ChaCha20-Poy1305`, `n`, and `rk` to obtain size of
|
|
|
|
the encrypted packet `l`.
|
|
|
|
* A zero-length byte slice is to be passed as the AD (associated data).
|
2016-11-15 02:23:20 +01:00
|
|
|
* The nonce for `rk` MUST be incremented after this step.
|
|
|
|
|
|
|
|
|
BOLT 1, BOLT 2, BOLT 5: 2-byte lengths everywhere.
Since our cryptopacket limits us to 2 bytes, and since people will
send 1-message-per-crypto-packet and nobody will test the
multiple-messages-in-one-cryptopacket code, let's just restrict to
64k messages.
1. Make cryptopacket length not include the HMAC, so we can actually send
64k messages.
2. Remove len prefix from packet, make type 2 bytes, note alignment properties.
3. Change message internal lengths/counts from 4 to 2 bytes, since more
is nonsensical anyway, and this removes a need to check before allocating:
- init feature bitfield length
- error message length
- shutdown scriptpubkey length
- commit_sig number of HTLC signatures
- revoke_and_ack number of HTLC-timeout signatures
4. Change max-accepted-htlcs to two bytes, and limit it to 511 to ensure
that commit_sig will always be under 64k.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2016-11-24 05:58:30 +01:00
|
|
|
* Read _exactly_ `l+16` bytes from the network buffer, let the bytes be known as
|
2016-11-15 02:23:20 +01:00
|
|
|
`c`.
|
|
|
|
|
|
|
|
|
2016-11-24 21:58:57 +01:00
|
|
|
* Decrypt `c` using `ChaCha20-Poly1305`, `n`, and `rk` to obtain decrypted
|
2016-11-15 02:23:20 +01:00
|
|
|
plaintext packet `p`.
|
|
|
|
|
|
|
|
|
|
|
|
* The nonce for `rk` MUST be incremented after this step.
|
|
|
|
|
|
|
|
|
2016-11-29 01:48:29 +01:00
|
|
|
## Lightning Message Key Rotation
|
2016-11-15 02:23:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
Changing keys regularly and forgetting the previous key is useful for
|
|
|
|
preventing decryption of old messages in the case of later key leakage (ie.
|
|
|
|
backwards secrecy).
|
|
|
|
|
|
|
|
|
|
|
|
Key rotation is performed for _each_ key (`sk` and `rk`) _individually _. A key
|
|
|
|
is to be rotated after a party sends of decrypts `1000` messages with it.
|
|
|
|
This can be properly accounted for by rotating the key once the nonce dedicated
|
|
|
|
to it exceeds `1000`.
|
|
|
|
|
|
|
|
|
|
|
|
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`.
|
2016-11-24 21:58:57 +01:00
|
|
|
* `ck', k' = HKDF(ck, k)`
|
2016-11-15 02:23:20 +01:00
|
|
|
* Reset the nonce for the key to `n = 0`.
|
|
|
|
* `k = k'`
|
2016-11-30 21:42:22 +01:00
|
|
|
* `ck = ck'`
|
2016-11-30 21:05:50 +01:00
|
|
|
|
2016-11-15 02:23:20 +01:00
|
|
|
# Security Considerations #
|
|
|
|
|
|
|
|
|
|
|
|
It is strongly recommended that existing, commonly-used, validated
|
|
|
|
libraries be used for encryption and decryption, to avoid the many
|
|
|
|
implementation pitfalls possible.
|
|
|
|
|
|
|
|
## Acknowledgements
|
|
|
|
|
|
|
|
TODO(roasbeef); fin
|
|
|
|
|
|
|
|
# References
|
|
|
|
3. <a id="reference-3">https://tools.ietf.org/html/rfc7539</a>
|
|
|
|
4. <a id="reference-4">http://noiseprotocol.org/noise.html</a>
|
2016-12-06 04:09:11 +01:00
|
|
|
5. <a id="reference-5">https://tools.ietf.org/html/rfc5869</a>
|
2016-11-15 02:23:20 +01:00
|
|
|
|
2016-11-30 21:05:50 +01:00
|
|
|
|
2016-11-15 02:23:20 +01:00
|
|
|
# Authors
|
|
|
|
|
|
|
|
FIXME
|
2016-11-22 20:52:59 +01:00
|
|
|
|
|
|
|
This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
|