[noise] [PATCH] noise: update to hkdf branch
Jason A. Donenfeld
Jason at zx2c4.com
Mon Oct 12 13:01:18 PDT 2015
This is a diff against the n0 branch.
---
doc/protocol.md | 42 ++++++++++++++++++++++---------
src/crypto/blake2b.c | 2 +-
src/noise/handshake.c | 42 ++++++++++++++++++-------------
src/noise/key.c | 70 +++++++++++++++++++--------------------------------
src/noise/noise.h | 3 ++-
5 files changed, 84 insertions(+), 75 deletions(-)
diff --git a/doc/protocol.md b/doc/protocol.md
index 96e78b1..6ab7b9d 100644
--- a/doc/protocol.md
+++ b/doc/protocol.md
@@ -42,7 +42,6 @@ For the following packet descriptions, refer to these functions:
* `DH_PUBKEY(private key)`: calculate a Curve25519 public key from `private key`, returning 32 bytes of output
* `AEAD(key, counter, plain text, auth text)`: ChaCha20Poly1305 AEAD, as specified in RFC7539, with its `nonce` being composed of 32 bits of zeros followed by the 64-bit little-endian value of `counter`
* `AEAD_LEN(plain len)`: `plain len + 16`
- * `GETKEY(key, counter)`: encrypts 32 bytes of zeros using ChaCha20 with the key as `key` and with `nonce` being composed of 32 bits of zeros followed by the 64-bit little-endian `counter`
* `KDF(key, input)`: `Blake2b(key, input)`, returning 32 bytes of output
* `HASH(input)`: `Blake2b(input)`, returning 32 bytes of output
* `TAI64N()`: TAI64N timestamp of current time which is 12 bytes
@@ -61,17 +60,26 @@ The initiator sends this message:
The fields are populated as follows:
- initiator.key = "Noise WireGuard zx2c4 2015-10-02"
- initiator.hash = HASH("Noise WireGuard zx2c4 2015-10-02" || responder.static_public)
+ initiator.chaining_key = "Noise WireGuard zx2c4 2015-10-12"
+ initiator.hash = HASH("Noise WireGuard zx2c4 2015-10-12" || responder.static_public)
initiator.ephemeral_private = DH_GENERATE()
msg.message_type = 1
msg.sender_index = little_endian(initiator.sender_index)
+
msg.unencrypted_ephemeral = DH_PUBKEY(initiator.ephemeral_private)
initiator.hash = HASH(initiator.hash || msg.unencrypted_ephemeral)
- initiator.key = KDF(initiator.key, DH(initiator.ephemeral_private, responder.static_public))
+
+ temp = KDF(initiator.chaining_key, DH(initiator.ephemeral_private, responder.static_public))
+ initiator.chaining_key = KDF(temp, 0x1)
+ initiator.key = KDF(temp, initiator.chaining_key || 0x2)
+
msg.encrypted_static = AEAD(initiator.key, 0, initiator.static_public, initiator.hash)
initiator.hash = HASH(initiator.hash || msg.encrypted_static)
- initiator.key = KDF(GETKEY(initiator.key, 1), DH(initiator.static_private, responder.static_public))
+
+ temp = KDF(initiator.chaining_key, DH(initiator.static_private, responder.static_public))
+ initiator.chaining_key = KDF(temp, 0x1)
+ initiator.key = KDF(temp, initiator.chaining_key || 0x2)
+
msg.encrypted_timestamp = AEAD(initiator.key, 0, TAI64N(), initiator.hash)
initiator.hash = HASH(initiator.hash || msg.encrypted_timestamp)
@@ -95,10 +103,18 @@ The fields are populated as follows:
msg.message_type = 2
msg.sender_index = little_endian(responder.sender_index)
msg.receiver_index = little_endian(initiator.sender_index)
+
msg.encrypted_ephemeral = AEAD(responder.key, 1, DH_PUBKEY(responder.ephemeral_private), responder.hash)
responder.hash = HASH(responder.hash || msg.encrypted_ephemeral)
- responder.key = KDF(GETKEY(responder.key, 2), DH(responder.ephemeral_private, initiator.ephemeral_public))
- responder.key = KDF(responder.key, DH(responder.ephemeral_private, initiator.static_public))
+
+ temp = KDF(responder.chaining_key, DH(responder.ephemeral_private, initiator.ephemeral_public))
+ responder.chaining_key = KDF(temp, 0x1)
+ responder.key = KDF(temp, responder.chaining_key || 0x2)
+
+ temp = KDF(responder.chaining_key, DH(responder.ephemeral_private, initiator.static_public))
+ responder.chaining_key = KDF(temp, 0x1)
+ responder.key = KDF(temp, responder.chaining_key || 0x2)
+
msg.encrypted_nothing = AEAD(responder.key, 0, [empty], responder.hash)
responder.hash = HASH(responder.hash || msg.encrypted_nothing)
@@ -108,17 +124,19 @@ When the initiator receives this message, he decrypts and does all the above ope
After the above two messages have been exchanged, keys are calculated by the initiator for sending and receiving data:
- initiator.sending_key = GETKEY(initiator.key, 1)
+ temp = KDF(initiator.chaining_key, [32 empty bytes])
+ initiator.sending_key = KDF(temp, 0x1)
+ initiator.receiving_key = KDF(temp, initiator.sending_key || 0x2)
initiator.sending_key_counter = 0
- initiator.receiving_key = GETKEY(initiator.key, 2)
initiator.receiving_key_counter = 0
- responder.receiving_key = GETKEY(responder.key, 1)
+ temp = KDF(responder.chaining_key, [32 empty bytes])
+ responder.receiving_key = KDF(temp, 0x1)
+ responder.sending_key = KDF(temp, responder.receiving_key || 0x2)
responder.receiving_key_counter = 0
- responder.sending_key = GETKEY(responder.key, 2)
responder.sending_key_counter = 0
-And then all previous keys, ephemeral keys, and hashes are zeroed out.
+And then all previous keys, chaining keys, ephemeral keys, and hashes are zeroed out.
### Subsequent Messages: Exchange of Data Packets
diff --git a/src/crypto/blake2b.c b/src/crypto/blake2b.c
index 80d03de..e6fd3dd 100644
--- a/src/crypto/blake2b.c
+++ b/src/crypto/blake2b.c
@@ -275,7 +275,7 @@ void blake2b(uint8_t *out, const uint8_t *in, const uint8_t *key, const uint8_t
{
blake2b_state state;
- BUG_ON(!in || !out);
+ BUG_ON((!in && (!key || !keylen)) || !out);
if (keylen > 0 && key != NULL)
blake2b_init_key(&state, outlen, key, keylen);
diff --git a/src/noise/handshake.c b/src/noise/handshake.c
index 2d53fce..4c2449c 100644
--- a/src/noise/handshake.c
+++ b/src/noise/handshake.c
@@ -16,21 +16,21 @@
static const u8 protocol_specifier[] =
{ 'N', 'o', 'i', 's', 'e', ' ', 'W', 'i',
'r', 'e', 'G', 'u', 'a', 'r', 'd', ' ',
- 'z', 'x', '2', 'c', '4', ' ', '0', '2',
- '-', '1', '0', '-', '2', '0', '1', '5' };
+ 'z', 'x', '2', 'c', '4', ' ', '2', '0',
+ '1', '5', '-', '1', '0', '-', '1', '2' };
-static inline void handshake_init(struct noise_symmetric_key *key, u8 hash[NOISE_HASH_LEN], const u8 remote_static[NOISE_PUBLIC_KEY_LEN])
+static inline void handshake_init(struct noise_symmetric_key *key, u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN], u8 hash[NOISE_HASH_LEN], const u8 remote_static[NOISE_PUBLIC_KEY_LEN])
{
blake2b_state blake;
- memset(key, 0, sizeof(struct noise_symmetric_key));
- memcpy(key->key, protocol_specifier, NOISE_SYMMETRIC_KEY_LEN);
blake2b_init(&blake, NOISE_HASH_LEN);
blake2b_update(&blake, protocol_specifier, NOISE_HASH_LEN);
blake2b_update(&blake, remote_static, NOISE_PUBLIC_KEY_LEN);
blake2b_final(&blake, hash, NOISE_HASH_LEN);
- key->birthdate = jiffies;
+
+ memset(key, 0, sizeof(struct noise_symmetric_key));
spin_lock_init(&key->counter.receive.lock);
- key->is_valid = true;
+
+ memcpy(chaining_key, protocol_specifier, NOISE_SYMMETRIC_KEY_LEN);
}
static inline void tai64n_now_packed(u8 output[NOISE_TIMESTAMP_LEN])
@@ -52,7 +52,7 @@ bool noise_handshake_create_initiation(struct noise_message_handshake_initiation
dst->header.type = NOISE_MESSAGE_HANDSHAKE_INITIATION;
dst->sender_index = cpu_to_le16(self_index);
- handshake_init(&peer->handshake.key, peer->handshake.hash, peer->handshake.remote_static);
+ handshake_init(&peer->handshake.key, peer->handshake.chaining_key, peer->handshake.hash, peer->handshake.remote_static);
/* e */
curve25519_generate_secret(peer->handshake.ephemeral_private);
@@ -60,7 +60,7 @@ bool noise_handshake_create_initiation(struct noise_message_handshake_initiation
noise_handshake_nocrypt(dst->unencrypted_ephemeral, peer->handshake.ephemeral_public, NOISE_PUBLIC_KEY_LEN, peer->handshake.hash);
/* dhes */
- if (!noise_mix_dh(&peer->handshake.key, peer->handshake.ephemeral_private, peer->handshake.remote_static))
+ if (!noise_mix_dh(&peer->handshake.key, peer->handshake.chaining_key, peer->handshake.ephemeral_private, peer->handshake.remote_static))
goto out;
/* s */
@@ -68,7 +68,7 @@ bool noise_handshake_create_initiation(struct noise_message_handshake_initiation
goto out;
/* dhss */
- if (!noise_mix_dh(&peer->handshake.key, peer->handshake.static_private, peer->handshake.remote_static))
+ if (!noise_mix_dh(&peer->handshake.key, peer->handshake.chaining_key, peer->handshake.static_private, peer->handshake.remote_static))
goto out;
/* t */
@@ -96,6 +96,7 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct noise_message_h
struct wireguard_peer *wg_peer = NULL;
struct noise_symmetric_key key;
u8 hash[NOISE_HASH_LEN];
+ u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN];
/* We use the first peer we can find, for determining this device's public key */
for (i = 0; i < max_peers; ++i) {
@@ -112,13 +113,13 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct noise_message_h
peer_put(wg_peer);
wg_peer = NULL;
- handshake_init(&key, hash, static_public);
+ handshake_init(&key, chaining_key, hash, static_public);
/* e */
noise_handshake_nocrypt(e, src->unencrypted_ephemeral, sizeof(src->unencrypted_ephemeral), hash);
/* dhes */
- if (!noise_mix_dh(&key, static_private, e))
+ if (!noise_mix_dh(&key, chaining_key, static_private, e))
goto out;
/* s */
@@ -126,7 +127,7 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct noise_message_h
goto out;
/* dhss */
- if (!noise_mix_dh(&key, static_private, s))
+ if (!noise_mix_dh(&key, chaining_key, static_private, s))
goto out;
/* t */
@@ -154,6 +155,7 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct noise_message_h
memcpy(peer->handshake.remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
memcpy(peer->handshake.latest_timestamp, t, NOISE_TIMESTAMP_LEN);
memcpy(peer->handshake.hash, hash, NOISE_HASH_LEN);
+ memcpy(peer->handshake.chaining_key, chaining_key, NOISE_SYMMETRIC_KEY_LEN);
peer->handshake.key = key;
up_write(&peer->handshake.lock);
@@ -161,6 +163,7 @@ out:
memzero_explicit(&key, sizeof(struct noise_symmetric_key));
memzero_explicit(static_private, NOISE_PUBLIC_KEY_LEN);
memzero_explicit(hash, NOISE_HASH_LEN);
+ memzero_explicit(chaining_key, NOISE_SYMMETRIC_KEY_LEN);
return wg_peer;
}
@@ -180,11 +183,11 @@ bool noise_handshake_create_response(struct noise_message_handshake_response *ds
goto out;
/* dhee */
- if (!noise_mix_dh(&peer->handshake.key, peer->handshake.ephemeral_private, peer->handshake.remote_ephemeral))
+ if (!noise_mix_dh(&peer->handshake.key, peer->handshake.chaining_key, peer->handshake.ephemeral_private, peer->handshake.remote_ephemeral))
goto out;
/* dhes */
- if (!noise_mix_dh(&peer->handshake.key, peer->handshake.ephemeral_private, peer->handshake.remote_static))
+ if (!noise_mix_dh(&peer->handshake.key, peer->handshake.chaining_key, peer->handshake.ephemeral_private, peer->handshake.remote_static))
goto out;
if (!noise_handshake_encrypt(dst->encrypted_nothing, NULL, 0, &peer->handshake.key, peer->handshake.hash))
@@ -205,6 +208,7 @@ struct wireguard_peer *noise_handshake_consume_response(struct noise_message_han
struct wireguard_peer *wg_peer;
struct noise_symmetric_key key;
u8 hash[NOISE_HASH_LEN];
+ u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN];
u8 e[NOISE_PUBLIC_KEY_LEN];
u8 ephemeral_private[NOISE_PUBLIC_KEY_LEN];
u8 static_private[NOISE_PUBLIC_KEY_LEN];
@@ -217,6 +221,7 @@ struct wireguard_peer *noise_handshake_consume_response(struct noise_message_han
down_read(&peer->handshake.lock);
key = peer->handshake.key;
memcpy(hash, peer->handshake.hash, NOISE_HASH_LEN);
+ memcpy(chaining_key, peer->handshake.chaining_key, NOISE_SYMMETRIC_KEY_LEN);
memcpy(ephemeral_private, peer->handshake.ephemeral_private, NOISE_PUBLIC_KEY_LEN);
memcpy(static_private, peer->handshake.static_private, NOISE_PUBLIC_KEY_LEN);
up_read(&peer->handshake.lock);
@@ -226,11 +231,11 @@ struct wireguard_peer *noise_handshake_consume_response(struct noise_message_han
goto fail;
/* dhee */
- if (!noise_mix_dh(&key, ephemeral_private, e))
+ if (!noise_mix_dh(&key, chaining_key, ephemeral_private, e))
goto fail;
/* dhes */
- if (!noise_mix_dh(&key, static_private, e))
+ if (!noise_mix_dh(&key, chaining_key, static_private, e))
goto fail;
/* decrypt nothing */
@@ -242,6 +247,7 @@ struct wireguard_peer *noise_handshake_consume_response(struct noise_message_han
down_write(&peer->handshake.lock);
memcpy(peer->handshake.remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
memcpy(peer->handshake.hash, hash, NOISE_HASH_LEN);
+ memcpy(peer->handshake.chaining_key, chaining_key, NOISE_SYMMETRIC_KEY_LEN);
peer->handshake.key = key;
peer->handshake.key.index = le16_to_cpu(src->sender_index);
peer->handshake.is_valid = true;
@@ -255,6 +261,7 @@ fail:
out:
memzero_explicit(&key, sizeof(struct noise_symmetric_key));
memzero_explicit(hash, NOISE_HASH_LEN);
+ memzero_explicit(chaining_key, NOISE_SYMMETRIC_KEY_LEN);
memzero_explicit(ephemeral_private, NOISE_PUBLIC_KEY_LEN);
memzero_explicit(static_private, NOISE_PUBLIC_KEY_LEN);
return wg_peer;
@@ -267,6 +274,7 @@ void noise_handshake_clear(struct noise_handshake *handshake)
memset(&handshake->ephemeral_private, 0, NOISE_PUBLIC_KEY_LEN);
memset(&handshake->remote_ephemeral, 0, NOISE_PUBLIC_KEY_LEN);
memset(&handshake->hash, 0, NOISE_HASH_LEN);
+ memset(&handshake->chaining_key, 0, NOISE_SYMMETRIC_KEY_LEN);
memset(&handshake->key, 0, sizeof(struct noise_symmetric_key));
handshake->is_valid = false;
up_write(&handshake->lock);
diff --git a/src/noise/key.c b/src/noise/key.c
index d87d92a..a7e71bf 100644
--- a/src/noise/key.c
+++ b/src/noise/key.c
@@ -6,62 +6,47 @@
#include "../crypto/chacha20.h"
static const u8 zeros[NOISE_SYMMETRIC_KEY_LEN] = { 0 };
-
-/* Like the noise_handshake_{en,de}crypt functions, here we also rely
- * the caller taking the handshake lock, because we don't do any
- * atomic operations or locking here. */
-static inline bool getkey(u8 dst_key[NOISE_SYMMETRIC_KEY_LEN], struct noise_symmetric_key *src_key)
+static inline void hkdf(u8 first_dst[NOISE_SYMMETRIC_KEY_LEN], u8 second_dst[NOISE_SYMMETRIC_KEY_LEN],
+ const u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN], const u8 *data, size_t data_len)
{
- u64 nonce = src_key->counter.receive.counter;
- if (!src_key->is_valid || time_is_before_eq_jiffies(src_key->birthdate + REJECT_AFTER_TIME) || nonce >= REJECT_AFTER_MESSAGES) {
- src_key->is_valid = false;
- return false;
- }
- nonce = cpu_to_le64(nonce);
- chacha20(dst_key, zeros, NOISE_SYMMETRIC_KEY_LEN, (uint8_t *)&nonce, 1, src_key->key);
- ++src_key->counter.receive.counter;
- return true;
+ u8 temp_key[NOISE_SYMMETRIC_KEY_LEN];
+ u8 temp_buffer[NOISE_SYMMETRIC_KEY_LEN + 1];
+ blake2b(temp_key, data, chaining_key, NOISE_SYMMETRIC_KEY_LEN, data_len, NOISE_SYMMETRIC_KEY_LEN);
+ temp_buffer[0] = 1;
+ blake2b(first_dst, temp_buffer, temp_key, NOISE_SYMMETRIC_KEY_LEN, 1, NOISE_SYMMETRIC_KEY_LEN);
+ memcpy(temp_buffer, first_dst, NOISE_SYMMETRIC_KEY_LEN);
+ temp_buffer[NOISE_SYMMETRIC_KEY_LEN] = 2;
+ blake2b(second_dst, temp_buffer, temp_key, NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN + 1, NOISE_SYMMETRIC_KEY_LEN);
+ memzero_explicit(temp_key, NOISE_SYMMETRIC_KEY_LEN);
+ memzero_explicit(temp_buffer, NOISE_SYMMETRIC_KEY_LEN + 1);
}
-static inline bool kdf(struct noise_symmetric_key *key, const u8 *src, size_t src_len)
+static inline void key_is_now_fresh(struct noise_symmetric_key *key)
{
- u8 newkey[NOISE_SYMMETRIC_KEY_LEN];
-
- if (!key->counter.receive.counter)
- memcpy(newkey, key->key, NOISE_SYMMETRIC_KEY_LEN);
- else if (!getkey(newkey, key))
- return false;
-
- blake2b(key->key, src, newkey, NOISE_SYMMETRIC_KEY_LEN, src_len, NOISE_SYMMETRIC_KEY_LEN);
- memzero_explicit(newkey, NOISE_SYMMETRIC_KEY_LEN);
atomic64_set(&key->counter.counter, 0);
key->counter.receive.backtrack = 0;
key->birthdate = jiffies;
key->is_valid = true;
- return true;
}
-static inline bool derive_key(struct noise_symmetric_key *dst, struct noise_symmetric_key *src)
+static inline void derive_key(struct noise_symmetric_key *first_dst, struct noise_symmetric_key *second_dst,
+ const struct noise_symmetric_key *key, const u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN])
{
- if (!src->is_valid)
- return false;
- *dst = *src;
- if (!getkey(dst->key, src))
- return false;
- atomic64_set(&dst->counter.counter, 0);
- dst->counter.receive.backtrack = 0;
- dst->birthdate = jiffies;
- dst->is_valid = true;
- return true;
+ *first_dst = *key;
+ *second_dst = *key;
+ hkdf(first_dst->key, second_dst->key, chaining_key, NULL, 0);
+ key_is_now_fresh(first_dst);
+ key_is_now_fresh(second_dst);
}
-bool noise_mix_dh(struct noise_symmetric_key *key, const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN])
+bool noise_mix_dh(struct noise_symmetric_key *key, u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN], const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN])
{
u8 dh_calculation[NOISE_PUBLIC_KEY_LEN];
if (!curve25519(dh_calculation, private, public))
return false;
- kdf(key, dh_calculation, NOISE_PUBLIC_KEY_LEN);
+ hkdf(key->key, chaining_key, chaining_key, dh_calculation, NOISE_PUBLIC_KEY_LEN);
memzero_explicit(dh_calculation, NOISE_PUBLIC_KEY_LEN);
+ key_is_now_fresh(key);
return true;
}
@@ -82,8 +67,7 @@ static void clear_previous_session(struct rcu_head *rcu)
bool noise_handshake_begin_session(struct noise_peer *peer, bool i_am_the_initator)
{
- struct noise_session_keys *session = NULL, *old_session;
- bool success;
+ struct noise_session_keys *session, *old_session;
down_read(&peer->handshake.lock);
if (!peer->handshake.is_valid)
@@ -99,11 +83,9 @@ bool noise_handshake_begin_session(struct noise_peer *peer, bool i_am_the_initat
goto fail;
session->i_am_the_initator = i_am_the_initator;
if (i_am_the_initator)
- success = derive_key(session->receiving, &peer->handshake.key) && derive_key(session->sending, &peer->handshake.key);
+ derive_key(session->receiving, session->sending, &peer->handshake.key, peer->handshake.chaining_key);
else
- success = derive_key(session->sending, &peer->handshake.key) && derive_key(session->receiving, &peer->handshake.key);
- if (!success)
- goto fail;
+ derive_key(session->sending, session->receiving, &peer->handshake.key, peer->handshake.chaining_key);
up_read(&peer->handshake.lock);
noise_handshake_clear(&peer->handshake);
diff --git a/src/noise/noise.h b/src/noise/noise.h
index bc45124..43956e4 100644
--- a/src/noise/noise.h
+++ b/src/noise/noise.h
@@ -64,6 +64,7 @@ struct noise_handshake {
u8 remote_ephemeral[NOISE_PUBLIC_KEY_LEN];
u8 hash[NOISE_HASH_LEN];
+ u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN];
struct noise_symmetric_key key;
struct rw_semaphore lock;
@@ -137,7 +138,7 @@ bool noise_handshake_decrypt(u8 *dst_plaintext, const u8 *src_ciphertext, size_t
void noise_handshake_nocrypt(u8 *dst, const u8 *src, size_t src_len, u8 hash[NOISE_HASH_LEN]);
void noise_symmetric_key_init(struct noise_symmetric_key *key, const u8 responder_static[NOISE_PUBLIC_KEY_LEN]);
void noise_handshake_clear(struct noise_handshake *handshake);
-bool noise_mix_dh(struct noise_symmetric_key *key, const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN]);
+bool noise_mix_dh(struct noise_symmetric_key *key, u8 chaining_key[NOISE_SYMMETRIC_KEY_LEN], const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN]);
/* External API: */
bool noise_handshake_create_initiation(struct noise_message_handshake_initiation *dst, struct noise_peer *peer, u16 self_index);
--
2.6.0
More information about the Noise
mailing list