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:
parent
27cc354c63
commit
2b8b491c74
1 changed files with 154 additions and 286 deletions
436
08-transport.md
436
08-transport.md
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue