From 903ea3f7ed684f2f9e53d246e77d1acbd781d48a Mon Sep 17 00:00:00 2001 From: trevnoise Date: Wed, 10 May 2017 06:41:35 +0000 Subject: Book1 / Book2 --- Makefile | 10 +- my.bib | 12 ++ noise.md | 78 ++++------- noise_book2.md | 341 +++++++++++++++++++++++++++++++++++++++++++++++++ output/noise.html | 100 ++++++--------- output/noise.pdf | Bin 353192 -> 350650 bytes output/noise_book2.pdf | Bin 0 -> 238243 bytes 7 files changed, 423 insertions(+), 118 deletions(-) create mode 100644 noise_book2.md create mode 100644 output/noise_book2.pdf diff --git a/Makefile b/Makefile index 8f010d0..7a25e1d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -default: output/noise.html output/noise.pdf +default: output/noise.html output/noise.pdf output/noise_book2.pdf # Pandoc 1.17.2, Pandoc-citeproc @@ -18,5 +18,11 @@ output/noise.pdf: noise.md template_pandoc.latex my.bib --filter pandoc-citeproc \ -o output/noise.pdf +output/noise_book2.pdf: noise_book2.md template_pandoc.latex my.bib + pandoc noise_book2.md -s --toc \ + -f markdown\ + --template template_pandoc.latex \ + --filter pandoc-citeproc \ + -o output/noise_book2.pdf clean: - rm output/noise.html output/noise.pdf + rm output/noise.html output/noise.pdf output/noise_book2.pdf diff --git a/my.bib b/my.bib index 7c8db8d..e9b78d0 100644 --- a/my.bib +++ b/my.bib @@ -230,6 +230,18 @@ https://moderncrypto.org/mail-archive/noise/2015/000098.html url = "http://eprint.iacr.org/2017/003", } +@misc{book1, + author = "{Trevor Perrin}", + title = "{The Noise Protocol Framework: Book 1}", + year = {2017}, + url = "https://noiseprotocol.org/noise_book1.pdf", +} +@misc{book2, + author = "{Trevor Perrin}", + title = "{The Noise Protocol Framework: Book 2}", + year = {2017}, + url = "https://noiseprotocol.org/noise_book2.pdf", +} diff --git a/noise.md b/noise.md index 432c249..4c4a2e4 100644 --- a/noise.md +++ b/noise.md @@ -1,8 +1,8 @@ --- -title: 'Noise Protocol Framework: Core Specification' +title: 'The Noise Protocol Framework: Book 1' author: 'Trevor Perrin (noise@trevp.net)' revision: '32draft' -date: '2017-04-07' +date: '2017-05-09' bibliography: 'my.bib' link-citations: 'true' csl: 'ieee-with-url.csl' @@ -13,7 +13,11 @@ csl: 'ieee-with-url.csl' Noise is a framework for crypto protocols based on Diffie-Hellman key agreement. Noise can describe protocols that consist of a single message as -well as interactive protocols. +well as interactive protocols. + +This document is the first of two books which provide the specification for +Noise. This document can be read as a self-contained specification, however +Book 2 adds features that are important in many use cases. 2. Overview ============ @@ -260,9 +264,6 @@ Noise depends on the following **cipher functions**: data `ad`. Returns the plaintext, unless authentication fails, in which case an error is signaled to the caller. - * **`REKEY(k)`**: Returns a new 32-byte cipher key as a pseudorandom function - of `k`. If this function is not specifically defined for some set of cipher functions, then it defaults to returning the first 32 bytes from `ENCRYPT(k, maxnonce, zerolen, zeros)`, where `MAXNONCE` equals 2^64^-1, `zerolen` is a zero-length byte sequence, and `zeros` is a sequence of 32 bytes filled with zeros. - 4.3. Hash functions -------------------- @@ -282,9 +283,9 @@ Noise depends on the following **hash function** (and associated constants): Noise defines additional functions based on the above `HASH()` function: * **`HMAC-HASH(key, data)`**: Applies `HMAC` from [@rfc2104] - using the `HASH()` function. This function is only called as part of `HKDF()`, below. + using the `HASH()` function. This function is only called as part of `HKDF2()`, below. - * **`HKDF(chaining_key, input_key_material)`**: Takes a `chaining_key` byte + * **`HKDF2(chaining_key, input_key_material)`**: Takes a `chaining_key` byte sequence of length `HASHLEN`, and an `input_key_material` byte sequence with length either zero bytes, 32 bytes, or `DHLEN` bytes. Returns a pair of byte sequences each of length `HASHLEN`, as follows: * Sets `temp_key = HMAC-HASH(chaining_key, input_key_material)`. @@ -293,7 +294,7 @@ Noise defines additional functions based on the above `HASH()` function: * Returns (output1, output2). Note that `temp_key`, `output1`, and `output2` are all `HASHLEN` bytes in - length. Also note that the `HKDF()` function is simply `HKDF` from [@rfc5869] + length. Also note that the `HKDF2()` function is simply `HKDF` from [@rfc5869] with the `chaining_key` as HKDF `salt`, and zero-length HKDF `info`. 5. Processing rules @@ -356,17 +357,14 @@ variables: A `CipherState` responds to the following methods. The `++` post-increment operator applied to `n` means "use the current `n` value, then increment it". -The maximum `n` value (2^64^-1) is reserved for rekey (see [Section -4.2](#cipher-functions) and [Section 9.3](#rekey)) and must not be used. If -incrementing `n` results in 2^64^-1, then any further `EncryptWithAd()` or -`DecryptWithAd()` calls will signal an error to the caller. +The maximum `n` value (2^64^-1) is reserved for other use. If incrementing `n` +results in 2^64^-1, then any further `EncryptWithAd()` or `DecryptWithAd()` +calls will signal an error to the caller. * **`InitializeKey(key)`**: Sets `k = key`. Sets `n = 0`. * **`HasKey()`**: Returns true if `k` is non-empty, false otherwise. - * **`Rekey()`**: Sets `k = REKEY(k)`. - * **`EncryptWithAd(ad, plaintext)`**: If `k` is non-empty returns `ENCRYPT(k, n++, ad, plaintext)`. Otherwise returns `plaintext`. @@ -399,7 +397,7 @@ A `SymmetricState` responds to the following methods: * **`MixKey(input_key_material)`**: Executes the following steps: - * Sets `ck, temp_k = HKDF(ck,input_key_material)`. + * Sets `ck, temp_k = HKDF2(ck,input_key_material)`. * If `HASHLEN` is 64, then truncates `temp_k` to 32 bytes. * Calls `InitializeKey(temp_k)`. @@ -416,7 +414,7 @@ A `SymmetricState` responds to the following methods: * **`Split()`**: Returns a pair of `CipherState` objects for encrypting transport messages. Executes the following steps, where `zerolen` is a zero-length byte sequence: - * Sets `temp_k1, temp_k2 = HKDF(ck, zerolen)`. + * Sets `temp_k1, temp_k2 = HKDF2(ck, zerolen)`. * If `HASHLEN` is 64, then truncates `temp_k1` and `temp_k2` to 32 bytes. * Creates two new `CipherState` objects `c1` and `c2`. * Calls `c1.InitializeKey(temp_k1)` and `c2.InitializeKey(temp_k2)`. @@ -481,8 +479,6 @@ A `HandshakeState` responds to the following methods: * Sets `message_patterns` to the message patterns from `handshake_pattern`. -\newpage - * **`WriteMessage(payload, message_buffer)`**: Takes a `payload` byte sequence which may be zero-length, and a `message_buffer` to write the output into. Performs the following steps: @@ -1216,20 +1212,6 @@ To support this, Noise libraries should expose the final value of h to the appli Parties can then sign the handshake hash, or hash it along with their password, to get an authentication token which has a "channel binding" property: the token can't be used by the receiving party with a different sesssion. -9.3. Rekey ------------ -Parties might wish to periodically call the `Rekey()` function on their transport cipherstates, so that a compromise of cipherstate keys will not decrypt older messages. Periodic rekey might also be used to reduce the volume of data encrypted under a single cipher key (this is usually not important with good ciphers, though note the discussion on `AESGCM` data volumes in [Section 13](#security-considerations) ). - -It is up to to the application if and when to perform rekey. For example: - - * Applications might perform continuous rekey, where they rekey the relevant cipherstate after every transport message sent or received. This is simple and gives good protection to older ciphertexts, but might be difficult for implementations where changing keys is expensive. - - * Applications might rekey a cipherstate automatically after it has has been used to send or receive some number of messages. - - * Applications might choose to rekey based on arbitrary criteria, in which case they signal this to the other party by sending a message. - -Note that rekey doesn't reset the cipherstate's `n` value, so applications performing rekey must still perform a new handshake if sending 2^64^ or more transport messages. - 10. DH functions, cipher functions, and hash functions ====================================================== @@ -1314,8 +1296,6 @@ Note that rekey doesn't reset the cipherstate's `n` value, so applications perfo * **`HASHLEN`** = 64 * **`BLOCKLEN`** = 128 -\newpage - 11. Protocol names =================== @@ -1392,9 +1372,10 @@ This section collects various security considerations: with a pre-message public key and assume that a successful handshake implies the other party's knowledge of the public key. Unfortunately, this is not the case, since setting public keys to invalid values might cause - predictable DH output. For example, a `Noise_NK_25519` initiator might - send an invalid ephemeral public key to cause a known DH output of all - zeros, despite not knowing the responder's static public key. + predictable DH output. For example, a `Noise_NK_25519` initiator might send + an invalid ephemeral public key to cause a known DH output of all zeros, + despite not knowing the responder's static public key. If the parties want + to authenticate with a shared secret, it should be used as a "PSK" [@book2]. * **Channel binding**: Depending on the DH functions, it might be possible for a malicious party to engage in multiple sessions that derive the same @@ -1408,9 +1389,8 @@ This section collects various security considerations: * **Incrementing nonces**: Reusing a nonce value for `n` with the same key `k` for encryption would be catastrophic. Implementations must carefully follow the rules for nonces. Nonces are not allowed to wrap back to zero - due to integer overflow, and the maximum nonce value is reserved for - rekeying. This means parties are not allowed to send more than 2^64^-1 - transport messages. + due to integer overflow, and the maximum nonce value is reserved. This + means parties are not allowed to send more than 2^64^-1 transport messages. * **Fresh ephemerals**: Every party in a Noise protocol should send a new ephemeral public key and perform a DH with it prior to sending any encrypted @@ -1430,8 +1410,7 @@ This section collects various security considerations: in security as the volume of data encrypted under a single key increases. Due to this, parties should not send more than 2^56^ bytes (roughly 72 petabytes) encrypted by a single key. If sending such large volumes of data - is a possibility then different cipher functions should be chosen, or the - cipherstate should be rekeyed ([Section 9.3](#rekey)) before this limit is reached. + is a possibility then different cipher functions should be chosen. * **Hash collisions**: If an attacker can find hash collisions on prologue data or the handshake hash, they may be able to perform "transcript @@ -1500,17 +1479,6 @@ Cipher nonces are big-endian for `AESGCM`, and little-endian for `ChaCha20`, bec * It makes sense to use consistent endianness in the cipher code. -Rekey defaults to using encryption with the nonce 2^64^-1 because: - - * With `AESGCM` and `ChaChaPoly` rekey can be computed efficiently (the "encryption" just needs to apply the cipher, and can skip calculation of the authentication tag). - -Rekey doesn't reset the nonce `n` to zero because: - - * Leaving `n` unchanged is simple. - - * If the cipher has a weakness such that repeated rekeying gives rise to a cycle of keys, then letting `n` advance will avoid catastrophic reuse of the same `k` and `n` values. - - * Letting `n` advance puts a bound on the total number of encryptions that can be performed with a set of derived keys. 14.2. Hash functions and hashing -------------- @@ -1620,7 +1588,7 @@ Explicit random nonces (like TLS "Random" fields) are not used because: 15. IPR ======== -The Noise specification (this document) is hereby placed in the public domain. +The Noise specification Book 1 (this document) is hereby placed in the public domain. 16. Acknowledgements ===================== diff --git a/noise_book2.md b/noise_book2.md new file mode 100644 index 0000000..d4a9b66 --- /dev/null +++ b/noise_book2.md @@ -0,0 +1,341 @@ +--- +title: 'The Noise Protocol Framework: Book 2' +author: 'Trevor Perrin (noise@trevp.net)' +revision: '0draft' +date: '2017-05-09' +bibliography: 'my.bib' +link-citations: 'true' +csl: 'ieee-with-url.csl' +--- + +1. Introduction +================ + +Noise is a framework for crypto protocols based on Diffie-Hellman key +agreement. This specification defines additional features that can be used +in combination with the features defined in Book 1 [@book1]. + +2. Modifiers +============= +To specify extensions or modifications to the behavior in Book 1 [@book1], Noise uses **modifiers**. + +A modifier is an ASCII string which is added to some component of the Noise +protocol name. A modifier can be a **pattern modifier**, **DH modifier**, +**cipher modifier**, or **hash modifier**, depending on which component of the +protocol name it is added to. + +The first modifier added onto a base name is simply appended. Thus, `fallback` +is a pattern modifier added to the `XX` base name to produce `XXfallback`. +Additional modifiers are separated with a plus sign. Thus, adding the `psk0` +pattern modifier (below) would result in the pattern name `XXfallback+psk0`. + +The final protocol name, including all modifiers, must be less than or equal to 255 +bytes (e.g. `Noise_XXfallback+psk0_25519_AESGCM_SHA256`). + +3. Pre-shared symmetric keys (PSK) +============ + +Noise provides a **pre-shared symmetric key** or **PSK** mode to +support protocols where both parties have a shared secret key. When +using PSK mode, the following changes are made: + +3.1. Cryptographic functions +---------------------------------- + +An additional function is defined based on the `HASH()` function: + + * **`HKDF3(chaining_key, input_key_material)`**: Takes a `chaining_key` byte + sequence of length `HASHLEN`, and an `input_key_material` byte sequence with + length of 32 bytes or `DHLEN` bytes. Returns a triple of byte sequences each of length `HASHLEN`: + * Sets `temp_key = HMAC-HASH(chaining_key, input_key_material)`. + * Sets `output1 = HMAC-HASH(temp_key, byte(0x01))`. + * Sets `output2 = HMAC-HASH(temp_key, output1 || byte(0x02))`. + * Sets `output3 = HMAC-HASH(temp_key, output2 || byte(0x03))`. + * Returns `(output1, output2, output3)`. + +Note that `temp_key`, `output1`, `output2`, and `output3` are all `HASHLEN` +bytes in length. Also note that the `HKDF3()` function is identical to `HKDF2()` +from [@book1] except that a third output is produced. + +An additional function is also defined in the `SymmetricState` object: + +\newpage + + * **`MixKeyAndHash(input_key_material)`**: Executes the following steps: + + * Sets `ck, temp_h, temp_k = HKDF3(ck, input_key_material)`. + * Calls `MixHash(temp_h)`. + * If `HASHLEN` is 64, then truncates `temp_k` to 32 bytes. + * Calls `InitializeKey(temp_k)`. + +This function combines the functionality of `MixKey()` and `MixHash()` from [@book1]. Unlike those, this function ensures that +its input affects both the encryption keys and the `h` value. + +The third output from `HKDF3()` is used as the `k` value so that +calculation of `k` may be skipped if `k` is not used. + +3.2. Handshake tokens +------------------------------- + +To support PSKs, a new `"psk"` token is allowed to appear once (or multiple +times) in a handshake pattern. This token can only appear in message patterns +(not pre-message patterns). This token is processed by calling +`MixKeyAndHash(psk)`, where `psk` is a 32-byte secret value provided by the +application. + +In [@book1], the `"e"` token in a pre-message pattern or message pattern always +results in a call to `MixHash(e.public_key)`. When PSKs are used, all of these calls +are replaced with `MixKeyAndHash(e.public_key)`. In conjunction with the validity rule in the +next section, this ensures that PSK-based encryption uses encryption keys that are randomized using +ephemeral public keys as nonces. + +3.3. Validity rule +-------------------- + +To prevent catastrophic key reuse, handshake patterns using the `"psk"` token must follow an additional validity rule: + + * A party may not send any encrypted data after it processes a `"psk"` token unless it has previously + sent an ephemeral public key (an `"e"` token), either before or after the `"psk"` token. + +This rule guarantees that a `k` derived from a PSK will never be used for +encryption unless it has also been randomized by `MixKeyAndHash(e.public_key)` +using a self-chosen ephemeral public key. + +3.4. Pattern modifiers +------------ + +To indicate PSK mode and the placement of the `"psk"` token, pattern modifiers +are used (see [Section 2](#modifiers)). The modifier `psk0` places a `"psk"` +token at the beginning of the first handshake message. The modifiers +`psk1`, `psk2`, etc., place a `"psk"` token at the end of the +first, second, etc., handshake message. + +Any pattern using one of these modifiers must process tokens according to the rules in [Section 3.2](#-handshake-tokens]), and must follow the validity rule in [Section 3.3](#validity-rule). + +The table below lists some unmodified one-way patterns on the left, and the recommended +PSK pattern on the right: + + ++--------------------------------+--------------------------------------+ +| Noise_N(rs): | Noise_Npsk0(rs): | +| <- s | <- s | +| ... | ... | +| -> e, es | -> psk, e, es | +| | | ++--------------------------------+--------------------------------------+ +| Noise_K(s, rs): | Noise_Kpsk0(s, rs): | +| <- s | <- s | +| ... | ... | +| -> e, es, ss | -> psk, e, es, ss | +| | | ++--------------------------------+--------------------------------------+ +| Noise_X(s, rs): | Noise_Xpsk1(s, rs): | +| <- s | <- s | +| ... | ... | +| -> e, es, s, ss | -> e, es, s, ss, psk | +| | | ++--------------------------------+--------------------------------------+ + +Note that the `psk1` modifier is recommended for `Noise_X`. This is because +`Noise_X` transmits the initiator's static public key. Because PSKs are +typically pairwise, the responder likely cannot determine the PSK until it has +decrypted the initiator's static public key. Thus, `psk1` is likely to be more +useful here than `psk0`. + +Following similar logic, we can define the most likely interactive PSK patterns: + + ++--------------------------------+--------------------------------------+ +| Noise_NN(): | Noise_NNpsk0(): | +| -> e | -> psk, e | +| <- e, ee | <- e, ee | ++--------------------------------+--------------------------------------+ +| Noise_NN(): | Noise_NNpsk2(): | +| -> e | -> e | +| <- e, ee | <- e, ee, psk | ++--------------------------------+--------------------------------------+ +| Noise_NK(rs): | Noise_NKpsk0(rs): | +| <- s | <- s | +| ... | ... | +| -> e, es | -> psk, e, es | +| <- e, ee | <- e, ee | ++--------------------------------+--------------------------------------+ +| Noise_NK(rs): | Noise_NKpsk2(rs): | +| <- s | <- s | +| ... | ... | +| -> e, es | -> e, es | +| <- e, ee | <- e, ee, psk | ++--------------------------------+--------------------------------------+ +| Noise_NX(rs): | Noise_NXpsk2(rs): | +| -> e | -> e | +| <- e, ee, s, es | <- e, ee, s, es, psk | ++--------------------------------+--------------------------------------+ +| Noise_XN(s): | Noise_XNpsk3(s): | +| -> e | -> e | +| <- e, ee | <- e, ee | +| -> s, se | -> s, se, psk | ++--------------------------------+--------------------------------------+ +| Noise_XK(s, rs): | Noise_XKpsk3(s, rs): | +| <- s | <- s | +| ... | ... | +| -> e, es | -> e, es | +| <- e, ee | <- e, ee | +| -> s, se | -> s, se, psk | ++--------------------------------+--------------------------------------+ +| Noise_XX(s, rs): | Noise_XXpsk3(s, rs): | +| -> e | -> e | +| <- e, ee, s, es | <- e, ee, s, es | +| -> s, se | -> s, se, psk | ++--------------------------------+--------------------------------------+ +| Noise_KN(s): | Noise_KNpsk0(s): | +| -> s | -> s | +| ... | ... | +| -> e | -> psk, e | +| <- e, ee, se | <- e, ee, se | ++--------------------------------+--------------------------------------+ +| Noise_KN(s): | Noise_KNpsk2(s): | +| -> s | -> s | +| ... | ... | +| -> e | -> e | +| <- e, ee, se | <- e, ee, se, psk | ++--------------------------------+--------------------------------------+ +| Noise_KK(s, rs): | Noise_KKpsk0(s, rs): | +| -> s | -> s | +| <- s | <- s | +| ... | ... | +| -> e, es, ss | -> psk, e, es, ss | +| <- e, ee, se | <- e, ee, se | ++--------------------------------+--------------------------------------+ +| Noise_KK(s, rs): | Noise_KKpsk2(s, rs): | +| -> s | -> s | +| <- s | <- s | +| ... | ... | +| -> e, es, ss | -> e, es, ss | +| <- e, ee, se | <- e, ee, se, psk | ++--------------------------------+--------------------------------------+ +| Noise_KX(s, rs): | Noise_KXpsk2(s, rs): | +| -> s | -> s | +| ... | ... | +| -> e | -> e | +| <- e, ee, se, s, es | <- e, ee, se, s, es, psk | ++--------------------------------+--------------------------------------+ +| Noise_IN(s): | Noise_INpsk1(s): | +| -> e, s | -> e, s, psk | +| <- e, ee, se | <- e, ee, se | +| | | ++--------------------------------+--------------------------------------+ +| Noise_IN(s): | Noise_INpsk2(s): | +| -> e, s | -> e, s | +| <- e, ee, se | <- e, ee, se, psk | +| | | ++--------------------------------+--------------------------------------+ +| Noise_IK(s, rs): | Noise_IKpsk1(s, rs): | +| <- s | <- s | +| ... | ... | +| -> e, es, s, ss | -> e, es, s, ss, psk | +| <- e, ee, se | <- e, ee, se | +| | | ++--------------------------------+--------------------------------------+ +| Noise_IK(s, rs): | Noise_IKpsk2(s, rs): | +| <- s | <- s | +| ... | ... | +| -> e, es, s, ss | -> e, es, s, ss | +| <- e, ee, se | <- e, ee, se, psk | +| | | ++--------------------------------+--------------------------------------+ +| Noise_IX(s, rs): | Noise_IXpsk2(s, rs): | +| -> e, s | -> e, s | +| <- e, ee, se, s, es | <- e, ee, se, s, es, psk | +| | | ++--------------------------------+--------------------------------------+ + +Of course, the above list does not exhaust all possible patterns that can be +formed with these modifiers, nor does it exhaust all the ways that `"psk"` +tokens could be used outside of these modifiers (e.g. multiple PSKs per +handshake). Defining additional PSK patterns is outside the scope of this +document. + + +4. Rekey +=========== + +Parties might wish to periodically update their cipherstate keys using a one-way function, so that a compromise of cipherstate keys will not decrypt older messages. Periodic rekey might also be used to reduce the volume of data encrypted under a single cipher key (this is usually not important with good ciphers, though note the discussion on `AESGCM` data volumes in Section 14 of [@book1]). + +To enable this, Noise supports a `Rekey()` function which may be called on a `CipherState`. + +It is up to to the application if and when to perform rekey. For example: + + * Applications might perform continuous rekey, where they rekey the relevant cipherstate after every transport message sent or received. This is simple and gives good protection to older ciphertexts, but might be difficult for implementations where changing keys is expensive. + + * Applications might rekey a cipherstate automatically after it has has been used to send or receive some number of messages. + + * Applications might choose to rekey based on arbitrary criteria, in which case they signal this to the other party by sending a message. + +Applications must make these decisions on their own; there are no modifiers which specify rekey behavior. + +Note that rekey only updates the cipherstate's `k` value, it doesn't reset the cipherstate's `n` value, so applications performing rekey must still perform a new handshake if sending 2^64^ or more transport messages. + +4.1. Cryptographic functions +---------------------------------- + +To support rekey, an additional cipher function is defined: + + * **`REKEY(k)`**: Returns a new 32-byte cipher key as a pseudorandom function + of `k`. If this function is not specifically defined for some set of cipher + functions, then it defaults to returning the first 32 bytes from `ENCRYPT(k, + maxnonce, zerolen, zeros)`, where `MAXNONCE` equals 2^64^-1, `zerolen` is a + zero-length byte sequence, and `zeros` is a sequence of 32 bytes filled with + zeros. + +An additional function is defined in the `CipherState` object: + + * **`Rekey()`**: Sets `k = REKEY(k)`. + + +5. Security Considerations +============================ + + * **Pre-shared symmetric keys**: Pre-shared symmetric keys must be secret + values with 256 bits of entropy. + +6. Rationales +============== + +This section collects various design rationales. + +5.1. Ciphers and encryption +-------------- + +Pre-shared symmetric keys are 256 bits because: + + * Pre-shared key length is fixed to simplify testing and implementation, and + to deter users from mistakenly using low-entropy passwords as pre-shared keys. + +Rekey defaults to using encryption with the nonce 2^64^-1 because: + + * With `AESGCM` and `ChaChaPoly` rekey can be computed efficiently (the "encryption" just needs to apply the cipher, and can skip calculation of the authentication tag). + +Rekey doesn't reset `n` to zero because: + + * Leaving `n` unchanged is simple. + + * If the cipher has a weakness such that repeated rekeying gives rise to a cycle of keys, then letting `n` advance will avoid catastrophic reuse of the same `k` and `n` values. + + * Letting `n` advance puts a bound on the total number of encryptions that can be performed with a set of derived keys. + + +7. IPR +======== + +The Noise specification Book 2 (this document) is hereby placed in the public domain. + +8. Acknowledgements +===================== + +The PSK approach was largely motivated and designed by Jason Donenfeld, based on his experience with PSKs in WireGuard. + +The rekey design benefited from discussions with Rhys Weatherley, Alexey Ermishkin, and Olaoluwa Osuntokun. + + +9. References +================ diff --git a/output/noise.html b/output/noise.html index a8d29b7..7738db3 100644 --- a/output/noise.html +++ b/output/noise.html @@ -5,17 +5,17 @@ - - Noise Protocol Framework: Core Specification + + The Noise Protocol Framework: Book 1

Table of Contents

@@ -53,7 +53,6 @@
  • 9. Advanced features
  • 10. DH functions, cipher functions, and hash functions
  • 1. Introduction

    Noise is a framework for crypto protocols based on Diffie-Hellman key agreement. Noise can describe protocols that consist of a single message as well as interactive protocols.

    +

    This document is the first of two books which provide the specification for Noise. This document can be read as a self-contained specification, however Book 2 adds features that are important in many use cases.

    2. Overview

    2.1. Terminology

    A Noise protocol begins with two parties exchanging handshake messages. During this handshake phase the parties exchange DH public keys and perform a sequence of DH operations, hashing the DH results into a shared secret key. After the handshake phase each party can use this shared key to send encrypted transport messages.

    @@ -162,7 +162,6 @@

    4.3. Hash functions

    Noise depends on the following hash function (and associated constants):

    @@ -173,8 +172,8 @@

    Noise defines additional functions based on the above HASH() function:

    -

    Note that temp_key, output1, and output2 are all HASHLEN bytes in length. Also note that the HKDF() function is simply HKDF from [4] with the chaining_key as HKDF salt, and zero-length HKDF info.

    +

    Note that temp_key, output1, and output2 are all HASHLEN bytes in length. Also note that the HKDF2() function is simply HKDF from [4] with the chaining_key as HKDF salt, and zero-length HKDF info.

    5. Processing rules

    To precisely define the processing rules we adopt an object-oriented terminology, and present three "objects" which encapsulate state variables and provide "methods" which implement processing logic. These three objects are presented as a hierarchy: each higher-layer object includes one instance of the object beneath it. From lowest-layer to highest, the objects are:

    -

    A CipherState responds to the following methods. The ++ post-increment operator applied to n means "use the current n value, then increment it". The maximum n value (264-1) is reserved for rekey (see Section 4.2 and Section 9.3) and must not be used. If incrementing n results in 264-1, then any further EncryptWithAd() or DecryptWithAd() calls will signal an error to the caller.

    +

    A CipherState responds to the following methods. The ++ post-increment operator applied to n means "use the current n value, then increment it". The maximum n value (264-1) is reserved for other use. If incrementing n results in 264-1, then any further EncryptWithAd() or DecryptWithAd() calls will signal an error to the caller.

    @@ -224,7 +222,7 @@
  • MixKey(input_key_material): Executes the following steps:

  • @@ -233,7 +231,7 @@
  • DecryptAndHash(ciphertext): Sets plaintext = DecryptWithAd(h, ciphertext), calls MixHash(ciphertext), and returns plaintext. Note that if k is empty, the DecryptWithAd() call will set plaintext equal to ciphertext.

  • Split(): Returns a pair of CipherState objects for encrypting transport messages. Executes the following steps, where zerolen is a zero-length byte sequence:
  • - - -