From 440d9a450971e7b5471dde44c1df94d48e207ff9 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Fri, 15 Oct 2021 10:53:35 -0400
Subject: [PATCH] Prop. 159 update KDF sections

---
 i2p2www/spec/proposals/159-ssu2.rst | 320 +++++++++++++++-------------
 1 file changed, 171 insertions(+), 149 deletions(-)

diff --git a/i2p2www/spec/proposals/159-ssu2.rst b/i2p2www/spec/proposals/159-ssu2.rst
index 0731ca888..dc2bc4565 100644
--- a/i2p2www/spec/proposals/159-ssu2.rst
+++ b/i2p2www/spec/proposals/159-ssu2.rst
@@ -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)
-
-  // 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 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
 
-- 
GitLab