Prop. 159 update KDF sections

This commit is contained in:
zzz
2021-10-15 10:53:35 -04:00
parent b526f75c9b
commit 440d9a4509

View File

@@ -288,12 +288,6 @@ is implemented by I2P routers, may also be implemented by the offline DPI.
It is a goal to reject attempted connections using replay of previous messages.
Future work
```````````
TBD
Address Validation
@@ -2141,7 +2135,7 @@ for inspiration, guidance, and code reuse:
* Packet header obfuscation: Adapted from [NTCP2]_
* Packet header protection: Adapted from QUIC [RFC9001]_ and [NAN]_
* Packet header protection: Adapted from QUIC [RFC9001]_ and [Nonces]_
* Headers used as AEAD associated data as in [ECIES]_.
@@ -2218,6 +2212,88 @@ Processing overhead estimate
TBD
Definitions
===============
We define the following functions corresponding to the cryptographic building blocks used.
ZEROLEN
zero-length byte array
CSRNG(n)
n-byte output from a cryptographically-secure random number generator.
H(p, d)
SHA-256 hash function that takes a personalization string p and data d, and
produces an output of length 32 bytes.
As defined in [NOISE]_.
|| below means append.
Use SHA-256 as follows::
H(p, d) := SHA-256(p || d)
MixHash(d)
SHA-256 hash function that takes a previous hash h and new data d,
and produces an output of length 32 bytes.
|| below means append.
Use SHA-256 as follows::
MixHash(d) := h = SHA-256(h || d)
STREAM
The ChaCha20/Poly1305 AEAD as specified in [RFC-7539]_.
S_KEY_LEN = 32 and S_IV_LEN = 12.
ENCRYPT(k, n, plaintext, ad)
Encrypts plaintext using the cipher key k, and nonce n which MUST be unique for
the key k.
Associated data ad is optional.
Returns a ciphertext that is the size of the plaintext + 16 bytes for the HMAC.
The entire ciphertext must be indistinguishable from random if the key is secret.
DECRYPT(k, n, ciphertext, ad)
Decrypts ciphertext using the cipher key k, and nonce n.
Associated data ad is optional.
Returns the plaintext.
DH
X25519 public key agreement system. Private keys of 32 bytes, public keys of 32
bytes, produces outputs of 32 bytes. It has the following
functions:
GENERATE_PRIVATE()
Generates a new private key.
DERIVE_PUBLIC(privkey)
Returns the public key corresponding to the given private key.
DH(privkey, pubkey)
Generates a shared secret from the given private and public keys.
HKDF(salt, ikm, info, n)
A cryptographic key derivation function which takes some input key material ikm (which
should have good entropy but is not required to be a uniformly random string), a salt
of length 32 bytes, and a context-specific 'info' value, and produces an output
of n bytes suitable for use as key material.
Use HKDF as specified in [RFC-5869]_, using the HMAC hash function SHA-256
as specified in [RFC-2104]_. This means that SALT_LEN is 32 bytes max.
MixKey(d)
Use HKDF() with a previous chainKey and new data d, and
sets the new chainKey and k.
As defined in [NOISE]_.
Use HKDF as follows::
MixKey(d) := output = HKDF(chainKey, d, "", 64)
chainKey = output[0:31]
k = output[32:63]
Messages
@@ -2398,7 +2474,7 @@ the source router hash and IV are used.
Header Protection
```````````````````
In addition to obfuscation, bytes 8-15 of the long header and bytes 8-12 of the short header
are encrypted by XORing with a known key, as in QUIC [RFC9001]_ and [NAN]_.
are encrypted by XORing with a known key, as in QUIC [RFC9001]_ and [Nonces]_.
For SessionCreated, where the destination router hash and IV are not yet known,
the source router hash and IV are used.
@@ -2507,21 +2583,22 @@ AEAD Error Handling
repeated failures.
Key Derivation Function (KDF) (for Session Request)
KDF for Session Request
-------------------------------------------------------
The KDF generates a handshake phase cipher key k from the DH result,
The Key Derivation Function (KDF) generates a handshake phase cipher key k from the DH result,
using HMAC-SHA256(key, data) as defined in [RFC-2104]_.
These are the InitializeSymmetric(), MixHash(), and MixKey() functions,
exactly as defined in the Noise spec.
KDF for Initial ChainKey
````````````````````````
.. raw:: html
{% highlight lang='text' %}
This is the "e" message pattern:
// Define protocol_name.
// Define protocol_name.
Set protocol_name = "Noise_XKaesobfse+hs1+hs2+hs3_25519_ChaChaPoly_SHA256"
(52 bytes, US-ASCII encoded, no NULL termination).
@@ -2538,23 +2615,32 @@ This is the "e" message pattern:
// up until here, can all be precalculated by Alice for all outgoing connections
// Alice must validate that Bob's static key is a valid point on the curve here.
// Bob's X25519 static keys
// bpk is published in routerinfo
bsk = GENERATE_PRIVATE()
bpk = DERIVE_PUBLIC(bsk)
// Bob static key
// MixHash(rs)
// MixHash(bpk)
// || below means append
h = SHA256(h || rs);
h = SHA256(h || bpk);
// up until here, can all be precalculated by Bob for all incoming connections
{% endhighlight %}
KDF for Flags/Static Key Section Encrypted Contents
```````````````````````````````````````````````````
This is the "e" message pattern:
Alice generates her ephemeral DH key pair e.
// Alice's X25519 ephemeral keys
aesk = GENERATE_PRIVATE()
aepk = DERIVE_PUBLIC(aesk)
// Alice ephemeral key X
// MixHash(e.pubkey)
// || below means append
h = SHA256(h || e.pubkey);
// MixHash(aepk)
h = SHA256(h || aepk);
// h is used as the associated data for the AEAD in Session Request
// Retain the Hash h for the Session Created KDF
@@ -2565,34 +2651,21 @@ This is the "e" message pattern:
This is the "es" message pattern:
// DH(e, rs) == DH(s, re)
Define input_key_material = 32 byte DH result of Alice's ephemeral key and Bob's static key
Set input_key_material = X25519 DH result
sharedSecret = DH(aesk, bpk) = DH(bsk, aepk)
// MixKey(DH())
//[chainKey, k] = MixKey(sharedSecret)
// ChaChaPoly parameters to encrypt/decrypt
keydata = HKDF(chainKey, sharedSecret, "", 64)
chainKey = keydata[0:31]
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104]_
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, defined above
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// AEAD parameters
k = keydata[32:64]
n = 0
ad = h
ciphertext = ENCRYPT(k, n, payload, ad)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
// retain the chaining key ck for Session Created KDF
// retain the chainKey for Session Created KDF
End of "es" message pattern.
@@ -2834,19 +2907,19 @@ Key Derivation Function (KDF) (for Session Created and Session Confirmed part 1)
// MixHash(ciphertext)
h = SHA256(h || 32 byte encrypted payload from Session Request)
// MixHash(padding)
// Only if padding length is nonzero
h = SHA256(h || random padding from Session Request)
// MixHash(header)
h = SHA256(h || header)
This is the "e" message pattern:
Bob generates his ephemeral DH key pair e.
// Bob's X25519 ephemeral keys
besk = GENERATE_PRIVATE()
bepk = DERIVE_PUBLIC(besk)
// h is from KDF for Session Request
// Bob ephemeral key Y
// MixHash(e.pubkey)
// || below means append
h = SHA256(h || e.pubkey);
// MixHash(bepk)
h = SHA256(h || bepk);
// h is used as the associated data for the AEAD in Session Created
// Retain the Hash h for the Session Confirmed KDF
@@ -2855,38 +2928,17 @@ Key Derivation Function (KDF) (for Session Created and Session Confirmed part 1)
This is the "ee" message pattern:
// DH(e, re)
Define input_key_material = 32 byte DH result of Alice's ephemeral key and Bob's ephemeral key
Set input_key_material = X25519 DH result
// overwrite Alice's ephemeral key in memory, no longer needed
// Alice:
e(public and private) = (all zeros)
// Bob:
re = (all zeros)
// MixKey(DH())
//[chainKey, k] = MixKey(sharedSecret)
sharedSecret = DH(aesk, bepk) = DH(besk, aepk)
keydata = HKDF(chainKey, sharedSecret, "", 64)
chainKey = keydata[0:31]
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104]_
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, from the KDF for Session Request
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
// AEAD parameters
k = keydata[32:64]
n = 0
ad = h
ciphertext = ENCRYPT(k, n, payload, ad)
// retain the chaining key ck for Session Confirmed KDF
@@ -3066,7 +3118,7 @@ Issues
Encryption for for Session Confirmed part 1, using Session Created KDF)
Encryption for for Session Confirmed part 1, using Session Created KDF
---------------------------------------------------------------------------
.. raw:: html
@@ -3077,23 +3129,23 @@ Encryption for for Session Confirmed part 1, using Session Created KDF)
// MixHash(ciphertext)
h = SHA256(h || 24 byte encrypted payload from Session Created)
// MixHash(padding)
// Only if padding length is nonzero
h = SHA256(h || random padding from Session Created)
// MixHash(header)
h = SHA256(h || header)
// h is used as the associated data for the AEAD in Session Confirmed part 1, below
This is the "s" message pattern:
Define s = Alice's static public key, 32 bytes
// Alice's X25519 static keys
ask = GENERATE_PRIVATE()
apk = DERIVE_PUBLIC(ask)
// EncryptAndHash(s.publickey)
// EncryptWithAd(h, s.publickey)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// AEAD parameters
// k is from Session Request
// n is 1
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, s.publickey)
n = 1
ad = h
ciphertext = ENCRYPT(k, n++, apk, ad)
// MixHash(ciphertext)
// || below means append
h = SHA256(h || ciphertext);
// h is used as the associated data for the AEAD in Session Confirmed part 2
@@ -3112,46 +3164,22 @@ Key Derivation Function (KDF) (for Session Confirmed part 2)
This is the "se" message pattern:
// DH(s, re) == DH(e, rs)
Define input_key_material = 32 byte DH result of Alice's static key and Bob's ephemeral key
Set input_key_material = X25519 DH result
// overwrite Bob's ephemeral key in memory, no longer needed
// Alice:
re = (all zeros)
// Bob:
e(public and private) = (all zeros)
// DH(ask, bepk) == DH(besk, apk)
sharedSecret = DH(ask, bepk) = DH(besk, apk)
// MixKey(DH())
//[chainKey, k] = MixKey(sharedSecret)
keydata = HKDF(chainKey, sharedSecret, "", 64)
chainKey = keydata[0:31]
Define temp_key = 32 bytes
Define HMAC-SHA256(key, data) as in [RFC-2104]_
// Generate a temp key from the chaining key and DH result
// ck is the chaining key, from the KDF for Session Request
temp_key = HMAC-SHA256(ck, input_key_material)
// overwrite the DH result in memory, no longer needed
input_key_material = (all zeros)
// Output 1
// Set a new chaining key from the temp key
// byte() below means a single byte
ck = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// Generate the cipher key k
Define k = 32 bytes
// || below means append
// byte() below means a single byte
k = HMAC-SHA256(temp_key, ck || byte(0x02)).
// AEAD parameters
k = keydata[32:64]
n = 0
ad = h
ciphertext = ENCRYPT(k, n, payload, ad)
// h from Session Confirmed part 1 is used as the associated data for the AEAD in Session Confirmed part 2
// EncryptAndHash(payload)
// EncryptWithAd(h, payload)
// AEAD_ChaCha20_Poly1305(key, nonce, associatedData, data)
// n is 0
ciphertext = AEAD_ChaCha20_Poly1305(k, n++, h, payload)
// MixHash(ciphertext)
// || below means append
h = SHA256(h || ciphertext);
// retain the chaining key ck for the data phase KDF
@@ -3159,9 +3187,6 @@ This is the "se" message pattern:
End of "se" message pattern.
// overwrite the temp_key in memory, no longer needed
temp_key = (all zeros)
{% endhighlight %}
@@ -3347,35 +3372,29 @@ Notes
Key Derivation Function (KDF) (for data phase)
----------------------------------------------
The data phase uses a zero-length associated data input.
The data phase uses the header for associated data.
The KDF generates two cipher keys k_ab and k_ba from the chaining key ck,
using HMAC-SHA256(key, data) as defined in [RFC-2104]_.
This is the Split() function, exactly as defined in the Noise spec.
This is the split() function, exactly as defined in the Noise spec.
.. raw:: html
{% highlight lang='text' %}
// split()
// chainKey = from handshake phase
keydata = HKDF(chainKey, ZEROLEN, "", 64)
k_ab = keydata[0:31]
k_ba = keydata[32:63]
ck = from handshake phase
// k_ab, k_ba = HKDF(ck, zerolen)
// ask_master = HKDF(ck, zerolen, info="ask")
// zerolen is a zero-length byte array
temp_key = HMAC-SHA256(ck, zerolen)
// overwrite the chaining key in memory, no longer needed
ck = (all zeros)
// Output 1
// cipher key, for Alice transmits to Bob (Noise doesn't make clear which is which, but Java code does)
k_ab = HMAC-SHA256(temp_key, byte(0x01)).
// Output 2
// cipher key, for Bob transmits to Alice (Noise doesn't make clear which is which, but Java code does)
k_ba = HMAC-SHA256(temp_key, k_ab || byte(0x02)).
// key is k_ab for Alice to Bob
// key is k_ba for Bob to Alice
// AEAD parameters
k = k_ab or k_ba
n = packet number from header
ad = header
ciphertext = ENCRYPT(k, n, payload, ad)
{% endhighlight %}
@@ -4641,6 +4660,9 @@ References
.. [RFC-3526]
https://tools.ietf.org/html/rfc3526
.. [RFC-5869]
https://tools.ietf.org/html/rfc5869
.. [RFC-6151]
https://tools.ietf.org/html/rfc6151