@ -37,7 +37,7 @@ that requires encrypted communication between two parties.
=== The Channel Graph as Decentralized Public Key Infrastructure
((("channel graph","decentralized public key infrastructure")))((("Lightning encrypted transport protocol","channel graph as decentralized public key infrastructure")))((("PKI (public key infrastructure)")))((("public key infrastructure (PKI)")))As we learned in <<routing>> when discussing multihop forwarding, every node has a long-term
((("channel graph","decentralized public key infrastructure")))((("Lightning encrypted transport protocol","channel graph as decentralized public key infrastructure")))((("PKI (public key infrastructure)")))((("public key infrastructure (PKI)")))As we learned in <<routing>>, every node has a long-term
identity that is used as the identifier for a vertex during pathfinding and
also used in the asymmetric cryptographic operations related to the creation of
onion encrypted routing packets. This public key, which serves as a node's
@ -248,37 +248,38 @@ handshake as:
`ECDH(k, rk)`:: Performs an Elliptic Curve Diffie–Hellman operation using
`k`, which is a valid `secp256k1` private key, and `rk`, which is a valid public key.
+
** The returned value is the SHA-256 of the compressed format of the
The returned value is the SHA-256 of the compressed format of the
generated point.
`HKDF(salt,ikm)`:: A function defined in `RFC 5869`,
evaluated with a zero-length `info` field.
+
** All invocations of `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
`HKDF`.
`encryptWithAD(k, n, ad, plaintext)`:: Outputs `encrypt(k, n, ad, plaintext)`.
+
** Where `encrypt` is an evaluation of `ChaCha20-Poly1305` (Internet Engineering Task Force variant)
Where `encrypt` is an evaluation of `ChaCha20-Poly1305` (Internet Engineering Task Force variant)
with the passed arguments, with nonce `n` encoded as 32 zero bits,
followed by a _little-endian_ 64-bit value. Note: this follows the Noise
Protocol convention, rather than our normal endian.
`decryptWithAD(k, n, ad, ciphertext)`:: Outputs `decrypt(k, n, ad, ciphertext)`.
+
** Where `decrypt` is an evaluation of `ChaCha20-Poly1305` (IETF variant)
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.
`generateKey()`:: Generates and returns a fresh `secp256k1` keypair.
+
** Where 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
Where 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
** Where the object also has a single method:
*** `.serializeCompressed()`
+
Where the object also has a single method:
** `.serializeCompressed()`
`a || b`:: This denotes the concatenation of two byte strings `a` and `b`.
@ -289,13 +290,15 @@ starting state that they'll use to advance the handshake process. To start,
both sides need to construct the initial handshake digest `h`.
1. ++h = SHA-256(__protocolName__)++
* Where ++__protocolName__ = "Noise_XK_secp256k1_ChaChaPoly_SHA256"++ encoded as
+
Where ++__protocolName__ = "Noise_XK_secp256k1_ChaChaPoly_SHA256"++ encoded as
an ASCII string.
2. `ck = h`
3. ++h = SHA-256(h || __prologue__)++
* Where ++__prologue__++ is the ASCII string: `lightning`.
+
Where ++__prologue__++ is the ASCII string: `lightning`.
In addition to the protocol name, we also add in an extra "prologue" that is
used to further bind the protocol context to the Lightning Network.
@ -306,12 +309,10 @@ zero-length ciphertext (only the MAC) is sent, this ensures that the initiator
does indeed know the public key of the responder.
* The initiating node mixes in the responding node's static public key
* Where `s` is the static public key of the initiator.
+
Where `s` is the static public key of the initiator.
2. `h = SHA-256(h || c)`
3. `se = ECDH(s.priv, re)`
* Where `re` is the ephemeral public key of the responder.
+
Where `re` is the ephemeral public key of the responder.
4. `ck, temp_k3 = HKDF(ck, se)`
* The final intermediate shared secret is mixed into the running chaining key.
+
The final intermediate shared secret is mixed into the running chaining key.
5. `t = encryptWithAD(temp_k3, 0, h, zero)`
* Where `zero` is a zero-length plain text.
+
Where `zero` is a zero-length plain text.
6. `sk, rk = HKDF(ck, zero)`
* Where `zero` is a zero-length plain text,
+
Where `zero` is a zero-length plain text,
`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.
* 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.
7. `rn = 0, sn = 0`
* The sending and receiving nonces are initialized to 0.
+
The sending and receiving nonces are initialized to 0.
8. Send `m = 0 || c || t` over the network buffer.
Receiver actions:
1. Read _exactly_ 66 bytes from the network buffer.
2. Parse the read message (`m`) into `v`, `c`, and `t`:
* Where `v` is the _first_ byte of `m`, `c` is the next 49
+
Where `v` is the _first_ byte of `m`, `c` is the next 49
bytes of `m`, and `t` is the last 16 bytes of `m`.
3. If `v` is an unrecognized handshake version, then the responder must
abort the connection attempt.
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.
5. `h = SHA-256(h || c)`
6. `se = ECDH(e.priv, rs)`
* Where `e` is the responder's original ephemeral key.
+
Where `e` is the responder's original ephemeral key.
7. `ck, temp_k3 = HKDF(ck, se)`
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.
9. `rk, sk = HKDF(ck, zero)`
* Where `zero` is a zero-length plain text,
+
Where `zero` is a zero-length plain text,
`rk` is the key to be used by the responder to decrypt the messages sent
by the initiator,
and `sk` is the key to be used by the responder to encrypt messages to
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.
10. `rn = 0, sn = 0`
* The sending and receiving nonces are initialized to 0.(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc6")))(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc5")))(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc4")))
+
The sending and receiving nonces are initialized to 0.(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc6")))(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc5")))(((range="endofrange", startref="ix_14_encrypted_transport-asciidoc4")))
===== Transport message encryption
@ -555,7 +586,8 @@ given a sending key (`sk`) and a nonce (`sn`), the following steps are
completed:
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.
2. Serialize `l` into 2 bytes encoded as a big-endian integer.
3. Encrypt `l` (using `ChaChaPoly-1305`, `sn`, and `sk`), to obtain `lc`
(18 bytes).
@ -566,7 +598,8 @@ completed:
* A zero-length byte slice is to be passed as the AD (associated data).
4. Finally, encrypt the message itself (`m`) using the same procedure used to
encrypt the length prefix. Let this encrypted ciphertext be known as `c`.
* The nonce `sn` must be incremented after this step.
+
The nonce `sn` must be incremented after this step.
5. Send `lc || c` over the network buffer.
====== Receiving and decrypting messages
@ -584,7 +617,8 @@ steps are completed:
known as `c`.
5. Decrypt `c` (using `ChaCha20-Poly1305`, `rn`, and `rk`) to obtain decrypted
plain-text packet `p`.
* The nonce `rn` must be incremented after this step.
+
The nonce `rn` must be incremented after this step.