فهرست منبع

Overhaul the key exchange API

Frank Denis 6 سال پیش
والد
کامیت
cf8b2bcb0c
10فایلهای تغییر یافته به همراه344 افزوده شده و 301 حذف شده
  1. 35 27
      hydrogen.h
  2. 23 23
      impl/common.h
  3. 1 2
      impl/core.h
  4. 2 2
      impl/gimli-core.h
  5. 10 10
      impl/hydrogen_p.h
  6. 239 204
      impl/kx.h
  7. 14 14
      impl/random.h
  8. 4 4
      impl/secretbox.h
  9. 15 14
      impl/x25519.h
  10. 1 1
      tests/tests.c

+ 35 - 27
hydrogen.h

@@ -6,30 +6,30 @@
 #include <stdlib.h>
 
 #ifdef __cplusplus
-# ifdef __GNUC__
-#  pragma GCC diagnostic ignored "-Wlong-long"
-# endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wlong-long"
+#endif
 extern "C" {
 #endif
 
 #if defined(__clang__) || defined(__GNUC__)
-# define _hydro_attr_(X) __attribute__(X)
+#define _hydro_attr_(X) __attribute__(X)
 #else
-# define _hydro_attr_(X)
+#define _hydro_attr_(X)
 #endif
-#define _hydro_attr_deprecated_         _hydro_attr_((deprecated))
-#define _hydro_attr_malloc_             _hydro_attr_((malloc))
-#define _hydro_attr_noinline_           _hydro_attr_((noinline))
-#define _hydro_attr_noreturn_           _hydro_attr_((noreturn))
+#define _hydro_attr_deprecated_ _hydro_attr_((deprecated))
+#define _hydro_attr_malloc_ _hydro_attr_((malloc))
+#define _hydro_attr_noinline_ _hydro_attr_((noinline))
+#define _hydro_attr_noreturn_ _hydro_attr_((noreturn))
 #define _hydro_attr_warn_unused_result_ _hydro_attr_((warn_unused_result))
-#define _hydro_attr_weak_               _hydro_attr_((weak))
+#define _hydro_attr_weak_ _hydro_attr_((weak))
 
 #if defined(__INTEL_COMPILER) || defined(_MSC_VER)
-# define _hydro_attr_aligned_(X)        __declspec(align(X))
+#define _hydro_attr_aligned_(X) __declspec(align(X))
 #elif defined(__clang__) || defined(__GNUC__)
-# define _hydro_attr_aligned_(X)        _hydro_attr_((aligned(X)))
+#define _hydro_attr_aligned_(X) _hydro_attr_((aligned(X)))
 #else
-# define _hydro_attr_aligned_(X)
+#define _hydro_attr_aligned_(X)
 #endif
 
 #define HYDRO_VERSION_MAJOR 1
@@ -180,11 +180,19 @@ typedef struct hydro_kx_session_keypair {
     uint8_t tx[hydro_kx_SESSIONKEYBYTES];
 } hydro_kx_session_keypair;
 
-typedef struct hydro_kx_state {
+typedef struct hydro_kx_cipher_state {
+    uint8_t  k[hydro_secretbox_KEYBYTES];
+    uint64_t msg_id;
+} hydro_kx_cipher_state;
+
+typedef struct hydro_kx_handshake_state {
+    hydro_kx_keypair kp;
     hydro_kx_keypair eph_kp;
-    uint8_t          h[32];
-    uint8_t          ck[32];
-    uint8_t          k[32];
+} hydro_kx_handshake_state;
+
+typedef struct hydro_kx_state {
+    hydro_kx_handshake_state hs;
+    hydro_hash_state         h_st;
 } hydro_kx_state;
 
 void hydro_kx_keygen(hydro_kx_keypair *static_kp);
@@ -194,7 +202,7 @@ void hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp,
 
 /* NOISE_N */
 
-#define hydro_kx_N_PACKET1BYTES 32
+#define hydro_kx_N_PACKET1BYTES (32 + 16)
 
 int hydro_kx_n_1(hydro_kx_session_keypair *kp, uint8_t packet1[hydro_kx_N_PACKET1BYTES],
                  const uint8_t psk[hydro_kx_PSKBYTES],
@@ -205,8 +213,8 @@ int hydro_kx_n_2(hydro_kx_session_keypair *kp, const uint8_t packet1[hydro_kx_N_
 
 /* NOISE_KK */
 
-#define hydro_kx_KK_PACKET1BYTES 32
-#define hydro_kx_KK_PACKET2BYTES 32
+#define hydro_kx_KK_PACKET1BYTES (32 + 16)
+#define hydro_kx_KK_PACKET2BYTES (32 + 16)
 
 int hydro_kx_kk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_KK_PACKET1BYTES],
                   const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
@@ -218,14 +226,14 @@ int hydro_kx_kk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_KK_PACK
                   const hydro_kx_keypair *static_kp);
 
 int hydro_kx_kk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
-                  const uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
+                  const uint8_t           packet2[hydro_kx_KK_PACKET2BYTES],
                   const hydro_kx_keypair *static_kp);
 
 /* NOISE_XX */
 
-#define hydro_kx_XX_PACKET1BYTES 32
-#define hydro_kx_XX_PACKET2BYTES 80
-#define hydro_kx_XX_PACKET3BYTES 48
+#define hydro_kx_XX_PACKET1BYTES (32 + 16)
+#define hydro_kx_XX_PACKET2BYTES (32 + 32 + 16 + 16)
+#define hydro_kx_XX_PACKET3BYTES (32 + 16 + 16)
 
 int hydro_kx_xx_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_XX_PACKET1BYTES],
                   const uint8_t psk[hydro_kx_PSKBYTES]);
@@ -305,9 +313,9 @@ int hydro_unpad(const unsigned char *buf, size_t padded_buflen, size_t blocksize
 #define HYDRO_HWTYPE_ATMEGA328 1
 
 #ifndef HYDRO_HWTYPE
-# ifdef __AVR__
-#  define HYDRO_HWTYPE HYDRO_HWTYPE_ATMEGA328
-# endif
+#ifdef __AVR__
+#define HYDRO_HWTYPE HYDRO_HWTYPE_ATMEGA328
+#endif
 #endif
 
 #ifdef __cplusplus

+ 23 - 23
impl/common.h

@@ -6,42 +6,42 @@
 #include <string.h>
 
 #if !defined(__unix__) && (defined(__APPLE__) || defined(__linux__))
-# define __unix__ 1
+#define __unix__ 1
 #endif
 #ifndef __GNUC__
-# define __restrict__
+#define __restrict__
 #endif
 
 #if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
     __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-# define NATIVE_BIG_ENDIAN
+#define NATIVE_BIG_ENDIAN
 #endif
 #ifndef NATIVE_BIG_ENDIAN
-# ifndef NATIVE_LITTLE_ENDIAN
-#  define NATIVE_LITTLE_ENDIAN
-# endif
+#ifndef NATIVE_LITTLE_ENDIAN
+#define NATIVE_LITTLE_ENDIAN
+#endif
 #endif
 
 #ifndef TLS
-# if defined(_WIN32) && !defined(__GNUC__)
-#  define TLS __declspec(thread)
-# elif (defined(__clang__) || defined(__GNUC__)) && defined(__unix__)
-#  define TLS __thread
-# else
-#  define TLS
-# endif
+#if defined(_WIN32) && !defined(__GNUC__)
+#define TLS __declspec(thread)
+#elif (defined(__clang__) || defined(__GNUC__)) && defined(__unix__)
+#define TLS __thread
+#else
+#define TLS
+#endif
 #endif
 
 #ifndef SIZE_MAX
-# define SIZE_MAX ((size_t) -1)
+#define SIZE_MAX ((size_t) -1)
 #endif
 
 #ifdef __OpenBSD__
-# define HAVE_EXPLICIT_BZERO 1
+#define HAVE_EXPLICIT_BZERO 1
 #elif defined(__GLIBC__) && defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE)
-# if __GLIBC_PREREQ(2, 25)
-#  define HAVE_EXPLICIT_BZERO 1
-# endif
+#if __GLIBC_PREREQ(2, 25)
+#define HAVE_EXPLICIT_BZERO 1
+#endif
 #endif
 
 #define COMPILER_ASSERT(X) (void) sizeof(char[(X) ? 1 : -1])
@@ -93,7 +93,7 @@ store64_le(uint8_t dst[8], uint64_t w)
     w >>= 8;
     dst[6] = (uint8_t) w;
     w >>= 8;
-    dst[7] = (uint8_t) w;
+    dst[7]     = (uint8_t) w;
 #endif
 }
 
@@ -127,7 +127,7 @@ store32_le(uint8_t dst[4], uint32_t w)
     w >>= 8;
     dst[2] = (uint8_t) w;
     w >>= 8;
-    dst[3] = (uint8_t) w;
+    dst[3]     = (uint8_t) w;
 #endif
 }
 
@@ -155,7 +155,7 @@ store16_le(uint8_t dst[2], uint16_t w)
 #else
     dst[0] = (uint8_t) w;
     w >>= 8;
-    dst[1] = (uint8_t) w;
+    dst[1]     = (uint8_t) w;
 #endif
 }
 
@@ -203,7 +203,7 @@ store64_be(uint8_t dst[8], uint64_t w)
     w >>= 8;
     dst[1] = (uint8_t) w;
     w >>= 8;
-    dst[0] = (uint8_t) w;
+    dst[0]     = (uint8_t) w;
 #endif
 }
 
@@ -237,7 +237,7 @@ store32_be(uint8_t dst[4], uint32_t w)
     w >>= 8;
     dst[1] = (uint8_t) w;
     w >>= 8;
-    dst[0] = (uint8_t) w;
+    dst[0]     = (uint8_t) w;
 #endif
 }
 

+ 1 - 2
impl/core.h

@@ -186,8 +186,7 @@ hydro_pad(unsigned char *buf, size_t unpadded_buflen, size_t blocksize, size_t m
     tail = &buf[xpadded_len];
     mask = 0U;
     for (i = 0; i < blocksize; i++) {
-        barrier_mask = (unsigned char)
-	 (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
+        barrier_mask = (unsigned char) (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT));
         tail[-i]     = (tail[-i] & mask) | (0x80 & barrier_mask);
         mask |= barrier_mask;
     }

+ 2 - 2
impl/gimli-core.h

@@ -1,7 +1,7 @@
 #ifdef __SSE2__
-# include "gimli-core/sse2.h"
+#include "gimli-core/sse2.h"
 #else
-# include "gimli-core/portable.h"
+#include "gimli-core/portable.h"
 #endif
 
 static void

+ 10 - 10
impl/hydrogen_p.h

@@ -3,18 +3,18 @@ static int hydro_random_init(void);
 /* ---------------- */
 
 #define gimli_BLOCKBYTES 48
-#define gimli_CAPACITY   32
-#define gimli_RATE       16
+#define gimli_CAPACITY 32
+#define gimli_RATE 16
 
-#define gimli_TAG_HEADER  0x01
+#define gimli_TAG_HEADER 0x01
 #define gimli_TAG_PAYLOAD 0x02
-#define gimli_TAG_FINAL   0x08
-#define gimli_TAG_FINAL0  0xf8
-#define gimli_TAG_KEY0    0xfe
-#define gimli_TAG_KEY     0xff
+#define gimli_TAG_FINAL 0x08
+#define gimli_TAG_FINAL0 0xf8
+#define gimli_TAG_KEY0 0xfe
+#define gimli_TAG_KEY 0xff
 
 #define gimli_DOMAIN_AEAD 0x0
-#define gimli_DOMAIN_XOF  0xf
+#define gimli_DOMAIN_XOF 0xf
 
 static void gimli_core_u8(uint8_t state_u8[gimli_BLOCKBYTES], uint8_t tag);
 
@@ -70,8 +70,8 @@ static int hydro_hash_init_with_tweak(hydro_hash_state *state,
 #define hydro_x25519_SECRETKEYBYTES 32
 
 static int hydro_x25519_scalarmult(uint8_t       out[hydro_x25519_BYTES],
-                                   const uint8_t scalar[hydro_x25519_BYTES],
-                                   const uint8_t x1[hydro_x25519_BYTES],
+                                   const uint8_t scalar[hydro_x25519_SECRETKEYBYTES],
+                                   const uint8_t x1[hydro_x25519_PUBLICKEYBYTES],
                                    bool          clamp) _hydro_attr_warn_unused_result_;
 
 static inline int hydro_x25519_scalarmult_base(uint8_t       pk[hydro_x25519_PUBLICKEYBYTES],

+ 239 - 204
impl/kx.h

@@ -1,152 +1,121 @@
 #define hydro_kx_AEAD_KEYBYTES hydro_hash_KEYBYTES
 #define hydro_kx_AEAD_MACBYTES 16
-#define hydro_kx_AEAD_HEADERBYTES hydro_kx_AEAD_MACBYTES
 
 #define hydro_kx_CONTEXT "hydro_kx"
-#define hydro_kx_CONTEXT_CK_K "kdf_ck_k"
-
-void
-hydro_kx_keygen(hydro_kx_keypair *static_kp)
-{
-    hydro_random_buf(static_kp->sk, hydro_kx_SECRETKEYBYTES);
-    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
-        abort();
-    }
-}
-
-void
-hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp, const uint8_t seed[hydro_kx_SEEDBYTES])
-{
-    COMPILER_ASSERT(hydro_kx_SEEDBYTES >= hydro_random_SEEDBYTES);
-    hydro_random_buf_deterministic(static_kp->sk, hydro_kx_SECRETKEYBYTES, seed);
-    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
-        abort();
-    }
-}
 
 static void
-hydro_kx_aead_setup(uint8_t buf[gimli_BLOCKBYTES], const hydro_kx_state *state,
-                    const uint8_t psk[hydro_kx_PSKBYTES])
+hydro_kx_aead_init(uint8_t aead_state[gimli_BLOCKBYTES], uint8_t k[hydro_kx_AEAD_KEYBYTES],
+                   hydro_kx_state *state)
 {
     static const uint8_t prefix[] = { 6, 'k', 'x', 'x', '2', '5', '6', 0 };
 
-    mem_zero(buf + sizeof prefix, gimli_BLOCKBYTES - sizeof prefix);
-    memcpy(buf, prefix, sizeof prefix);
-    gimli_core_u8(buf, gimli_TAG_HEADER);
+    hydro_hash_final(&state->h_st, k, hydro_kx_AEAD_KEYBYTES);
+
+    mem_zero(aead_state + sizeof prefix, gimli_BLOCKBYTES - sizeof prefix);
+    memcpy(aead_state, prefix, sizeof prefix);
+    gimli_core_u8(aead_state, gimli_TAG_HEADER);
 
     COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == 2 * gimli_RATE);
-    mem_xor(buf, state->k, gimli_RATE);
-    gimli_core_u8(buf, gimli_TAG_KEY);
-    mem_xor(buf, state->k + gimli_RATE, gimli_RATE);
-    gimli_core_u8(buf, gimli_TAG_KEY);
-
-    COMPILER_ASSERT(sizeof state->h == 2 * gimli_RATE);
-    mem_xor(buf, state->h, gimli_RATE);
-    gimli_core_u8(buf, gimli_TAG_HEADER);
-    mem_xor(buf, state->h + gimli_RATE, gimli_RATE);
-    gimli_core_u8(buf, gimli_TAG_HEADER);
-
-    if (psk != NULL) {
-        COMPILER_ASSERT(hydro_kx_PSKBYTES == 2 * gimli_RATE);
-        mem_xor(buf, psk, gimli_RATE);
-        gimli_core_u8(buf, gimli_TAG_HEADER);
-        mem_xor(buf, psk + gimli_RATE, gimli_RATE);
-        gimli_core_u8(buf, gimli_TAG_HEADER);
-    }
+    mem_xor(aead_state, k, gimli_RATE);
+    gimli_core_u8(aead_state, gimli_TAG_KEY);
+    mem_xor(aead_state, k + gimli_RATE, gimli_RATE);
+    gimli_core_u8(aead_state, gimli_TAG_KEY);
 }
 
 static void
-hydro_kx_finalize(uint8_t *buf, const uint8_t key[hydro_kx_AEAD_KEYBYTES])
+hydro_kx_aead_final(uint8_t *aead_state, const uint8_t key[hydro_kx_AEAD_KEYBYTES])
 {
     COMPILER_ASSERT(hydro_kx_AEAD_KEYBYTES == gimli_CAPACITY);
-    mem_xor(buf + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
-    gimli_core_u8(buf, gimli_TAG_FINAL);
-    mem_xor(buf + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
-    gimli_core_u8(buf, gimli_TAG_FINAL);
+    mem_xor(aead_state + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
+    gimli_core_u8(aead_state, gimli_TAG_FINAL);
+    mem_xor(aead_state + gimli_RATE, key, hydro_kx_AEAD_KEYBYTES);
+    gimli_core_u8(aead_state, gimli_TAG_FINAL);
 }
 
 static void
-hydro_kx_aead_xor_enc(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in, size_t inlen)
+hydro_kx_aead_xor_enc(uint8_t aead_state[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in,
+                      size_t inlen)
 {
     size_t i;
     size_t leftover;
 
     for (i = 0; i < inlen / gimli_RATE; i++) {
-        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
-        memcpy(buf, &out[i * gimli_RATE], gimli_RATE);
-        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, gimli_RATE);
+        memcpy(aead_state, &out[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(aead_state, gimli_TAG_PAYLOAD);
     }
     leftover = inlen % gimli_RATE;
     if (leftover != 0) {
-        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
-        mem_cpy(buf, &out[i * gimli_RATE], leftover);
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, leftover);
+        mem_cpy(aead_state, &out[i * gimli_RATE], leftover);
     }
-    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
-    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    gimli_pad_u8(aead_state, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(aead_state, gimli_TAG_PAYLOAD);
 }
 
 static void
-hydro_kx_aead_xor_dec(uint8_t buf[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in, size_t inlen)
+hydro_kx_aead_xor_dec(uint8_t aead_state[gimli_BLOCKBYTES], uint8_t *out, const uint8_t *in,
+                      size_t inlen)
 {
     size_t i;
     size_t leftover;
 
     for (i = 0; i < inlen / gimli_RATE; i++) {
-        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, gimli_RATE);
-        memcpy(buf, &in[i * gimli_RATE], gimli_RATE);
-        gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, gimli_RATE);
+        memcpy(aead_state, &in[i * gimli_RATE], gimli_RATE);
+        gimli_core_u8(aead_state, gimli_TAG_PAYLOAD);
     }
     leftover = inlen % gimli_RATE;
     if (leftover != 0) {
-        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], buf, leftover);
-        mem_cpy(buf, &in[i * gimli_RATE], leftover);
+        mem_xor2(&out[i * gimli_RATE], &in[i * gimli_RATE], aead_state, leftover);
+        mem_cpy(aead_state, &in[i * gimli_RATE], leftover);
     }
-    gimli_pad_u8(buf, leftover, gimli_DOMAIN_AEAD);
-    gimli_core_u8(buf, gimli_TAG_PAYLOAD);
+    gimli_pad_u8(aead_state, leftover, gimli_DOMAIN_AEAD);
+    gimli_core_u8(aead_state, gimli_TAG_PAYLOAD);
 }
 
 static void
-hydro_kx_encrypt(const hydro_kx_state *state, uint8_t *c, const uint8_t *m, size_t mlen,
-                 const uint8_t psk[hydro_kx_PSKBYTES])
+hydro_kx_aead_encrypt(hydro_kx_state *state, uint8_t *c, const uint8_t *m, size_t mlen)
 {
-    _hydro_attr_aligned_(16) uint8_t buf[gimli_BLOCKBYTES];
+    _hydro_attr_aligned_(16) uint8_t aead_state[gimli_BLOCKBYTES];
+    uint8_t                          k[hydro_kx_AEAD_KEYBYTES];
     uint8_t *                        mac = &c[0];
     uint8_t *                        ct  = &c[hydro_kx_AEAD_MACBYTES];
 
-    hydro_kx_aead_setup(buf, state, psk);
-    hydro_kx_aead_xor_enc(buf, ct, m, mlen);
-
-    hydro_kx_finalize(buf, state->k);
+    hydro_kx_aead_init(aead_state, k, state);
+    hydro_kx_aead_xor_enc(aead_state, ct, m, mlen);
+    hydro_kx_aead_final(aead_state, k);
     COMPILER_ASSERT(hydro_kx_AEAD_MACBYTES <= gimli_CAPACITY);
-    memcpy(mac, buf + gimli_RATE, hydro_kx_AEAD_MACBYTES);
+    memcpy(mac, aead_state + gimli_RATE, hydro_kx_AEAD_MACBYTES);
+    hydro_hash_update(&state->h_st, c, mlen + hydro_kx_AEAD_MACBYTES);
 }
 
-static int hydro_kx_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen,
-                            const uint8_t psk[hydro_kx_PSKBYTES]) _hydro_attr_warn_unused_result_;
+static int hydro_kx_aead_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c,
+                                 size_t clen) _hydro_attr_warn_unused_result_;
 
 static int
-hydro_kx_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen,
-                 const uint8_t psk[hydro_kx_PSKBYTES])
+hydro_kx_aead_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t clen)
 {
     _hydro_attr_aligned_(16) uint32_t int_state[gimli_BLOCKBYTES / 4];
     uint32_t                          pub_mac[hydro_kx_AEAD_MACBYTES / 4];
-    uint8_t *                         buf = (uint8_t *) (void *) int_state;
+    uint8_t                           k[hydro_kx_AEAD_KEYBYTES];
+    uint8_t *                         aead_state = (uint8_t *) (void *) int_state;
     const uint8_t *                   mac;
     const uint8_t *                   ct;
     size_t                            mlen;
     uint32_t                          cv;
 
-    if (clen < hydro_kx_AEAD_HEADERBYTES) {
+    if (clen < hydro_kx_AEAD_MACBYTES) {
         return -1;
     }
     mac  = &c[0];
     ct   = &c[hydro_kx_AEAD_MACBYTES];
-    mlen = clen - hydro_kx_AEAD_HEADERBYTES;
+    mlen = clen - hydro_kx_AEAD_MACBYTES;
     memcpy(pub_mac, mac, sizeof pub_mac);
-    hydro_kx_aead_setup(buf, state, psk);
-    hydro_kx_aead_xor_dec(buf, m, ct, mlen);
-
-    hydro_kx_finalize(buf, state->k);
+    hydro_kx_aead_init(aead_state, k, state);
+    hydro_hash_update(&state->h_st, c, clen);
+    hydro_kx_aead_xor_dec(aead_state, m, ct, mlen);
+    hydro_kx_aead_final(aead_state, k);
     COMPILER_ASSERT(hydro_kx_AEAD_MACBYTES <= gimli_CAPACITY);
     COMPILER_ASSERT(gimli_RATE % 4 == 0);
     cv = hydro_mem_ct_cmp_u32(int_state + gimli_RATE / 4, pub_mac, hydro_kx_AEAD_MACBYTES / 4);
@@ -158,45 +127,66 @@ hydro_kx_decrypt(hydro_kx_state *state, uint8_t *m, const uint8_t *c, size_t cle
     return 0;
 }
 
+/* -- */
+
+void
+hydro_kx_keygen(hydro_kx_keypair *static_kp)
+{
+    hydro_random_buf(static_kp->sk, hydro_kx_SECRETKEYBYTES);
+    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
+        abort();
+    }
+}
+
+void
+hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp, const uint8_t seed[hydro_kx_SEEDBYTES])
+{
+    COMPILER_ASSERT(hydro_kx_SEEDBYTES >= hydro_random_SEEDBYTES);
+    hydro_random_buf_deterministic(static_kp->sk, hydro_kx_SECRETKEYBYTES, seed);
+    if (hydro_x25519_scalarmult_base(static_kp->pk, static_kp->sk) != 0) {
+        abort();
+    }
+}
+
+static void
+hydro_kx_init_state(hydro_kx_state *state, const char *name)
+{
+    mem_zero(state, sizeof *state);
+    hydro_hash_init(&state->h_st, hydro_kx_CONTEXT, NULL);
+    hydro_hash_update(&state->h_st, name, strlen(name));
+    hydro_hash_final(&state->h_st, NULL, 0);
+}
+
+static void
+hydro_kx_final(hydro_kx_state *state, uint8_t session_k1[hydro_kx_SESSIONKEYBYTES],
+               uint8_t session_k2[hydro_kx_SESSIONKEYBYTES])
+{
+    uint8_t kdf_key[hydro_kdf_KEYBYTES];
+
+    hydro_hash_final(&state->h_st, kdf_key, sizeof kdf_key);
+    hydro_kdf_derive_from_key(session_k1, hydro_kx_SESSIONKEYBYTES, 0, hydro_kx_CONTEXT, kdf_key);
+    hydro_kdf_derive_from_key(session_k2, hydro_kx_SESSIONKEYBYTES, 1, hydro_kx_CONTEXT, kdf_key);
+}
+
 static int
-hydro_kx_scalarmult(hydro_kx_state *state, uint8_t dh_res[hydro_x25519_BYTES],
-                    const uint8_t scalar[hydro_x25519_BYTES], const uint8_t x1[hydro_x25519_BYTES])
+hydro_kx_dh(hydro_kx_state *state, const uint8_t sk[hydro_x25519_SECRETKEYBYTES],
+            const uint8_t pk[hydro_x25519_PUBLICKEYBYTES])
 {
-    uint8_t ck_k[hydro_hash_BYTES + hydro_hash_KEYBYTES];
+    uint8_t dh_result[hydro_x25519_BYTES];
 
-    if (hydro_x25519_scalarmult(dh_res, scalar, x1, 1) != 0) {
+    if (hydro_x25519_scalarmult(dh_result, sk, pk, 1) != 0) {
         return -1;
     }
-    COMPILER_ASSERT(sizeof state->ck >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(ck_k, sizeof ck_k, dh_res, hydro_x25519_BYTES, hydro_kx_CONTEXT_CK_K,
-                    state->ck);
-    memcpy(state->ck, ck_k, sizeof state->ck);
-    memcpy(state->k, ck_k + sizeof state->ck, sizeof state->k);
+    hydro_hash_update(&state->h_st, dh_result, hydro_x25519_BYTES);
 
     return 0;
 }
 
 static void
-hydro_kx_final(hydro_kx_state *state, uint8_t rx[hydro_kx_SESSIONKEYBYTES],
-               uint8_t tx[hydro_kx_SESSIONKEYBYTES])
+hydro_kx_eph_keygen(hydro_kx_state *state, hydro_kx_keypair *kp)
 {
-    COMPILER_ASSERT(hydro_kx_SESSIONKEYBYTES == hydro_kx_AEAD_KEYBYTES);
-    COMPILER_ASSERT(hydro_kx_PUBLICKEYBYTES == hydro_x25519_BYTES);
-    COMPILER_ASSERT(hydro_kx_SECRETKEYBYTES == hydro_x25519_BYTES);
-    COMPILER_ASSERT(hydro_kx_PSKBYTES == hydro_hash_KEYBYTES);
-    COMPILER_ASSERT(sizeof(state->h) == hydro_hash_BYTES);
-    COMPILER_ASSERT(sizeof(state->ck) == hydro_hash_KEYBYTES);
-    COMPILER_ASSERT(sizeof(state->k) == hydro_kx_AEAD_KEYBYTES);
-    COMPILER_ASSERT(hydro_kx_XX_PACKET1BYTES == hydro_kx_PUBLICKEYBYTES);
-    COMPILER_ASSERT(hydro_kx_XX_PACKET2BYTES ==
-                    hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES);
-    COMPILER_ASSERT(hydro_kx_XX_PACKET3BYTES ==
-                    hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES);
-
-    COMPILER_ASSERT(hydro_kdf_KEYBYTES == hydro_hash_BYTES);
-    hydro_kdf_derive_from_key(rx, hydro_kx_SESSIONKEYBYTES, 0, hydro_kx_CONTEXT, state->ck);
-    hydro_kdf_derive_from_key(tx, hydro_kx_SESSIONKEYBYTES, 1, hydro_kx_CONTEXT, state->ck);
-    hydro_memzero(state, sizeof *state);
+    hydro_kx_keygen(kp);
+    hydro_hash_update(&state->h_st, kp->pk, sizeof kp->pk);
 }
 
 /* NOISE_N */
@@ -207,23 +197,23 @@ hydro_kx_n_1(hydro_kx_session_keypair *kp, uint8_t packet1[hydro_kx_N_PACKET1BYT
              const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES])
 {
     hydro_kx_state state;
-    uint8_t        dh_res[hydro_x25519_BYTES];
-
-    mem_zero(&state, sizeof state);
+    uint8_t *      packet1_eph_pk = &packet1[0];
+    uint8_t *      packet1_mac    = &packet1[hydro_kx_PUBLICKEYBYTES];
 
-    hydro_kx_keygen(&state.eph_kp);
-    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state.h, sizeof state.h, peer_static_pk, hydro_kx_PUBLICKEYBYTES,
-                    hydro_kx_CONTEXT, psk);
-    memcpy(packet1, state.eph_kp.pk, hydro_kx_PUBLICKEYBYTES);
-
-    COMPILER_ASSERT(sizeof state.h >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state.h, sizeof state.h, state.eph_kp.pk, sizeof state.eph_kp.pk,
-                    hydro_kx_CONTEXT, state.h);
+    if (psk == NULL) {
+        psk = zero;
+    }
+    hydro_kx_init_state(&state, "Noise_Npsk0_hydro1");
+    hydro_hash_update(&state.h_st, peer_static_pk, hydro_x25519_PUBLICKEYBYTES);
 
-    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_static_pk) != 0) {
+    hydro_hash_update(&state.h_st, psk, hydro_kx_PSKBYTES);
+    hydro_kx_eph_keygen(&state, &state.hs.eph_kp);
+    if (hydro_kx_dh(&state, state.hs.eph_kp.sk, peer_static_pk) != 0) {
         return -1;
     }
+    hydro_kx_aead_encrypt(&state, packet1_mac, NULL, 0);
+    memcpy(packet1_eph_pk, state.hs.eph_kp.pk, sizeof state.hs.eph_kp.pk);
+
     hydro_kx_final(&state, kp->rx, kp->tx);
 
     return 0;
@@ -234,20 +224,21 @@ hydro_kx_n_2(hydro_kx_session_keypair *kp, const uint8_t packet1[hydro_kx_N_PACK
              const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp)
 {
     hydro_kx_state state;
-    uint8_t        dh_res[hydro_x25519_BYTES];
-    const uint8_t *peer_eph_pk = packet1;
+    const uint8_t *peer_eph_pk = &packet1[0];
+    const uint8_t *packet1_mac = &packet1[hydro_kx_PUBLICKEYBYTES];
 
-    mem_zero(&state, sizeof state);
-
-    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state.h, sizeof state.h, static_kp->pk, sizeof static_kp->pk, hydro_kx_CONTEXT,
-                    psk);
-
-    COMPILER_ASSERT(sizeof state.h >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state.h, sizeof state.h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES, hydro_kx_CONTEXT,
-                    state.h);
+    if (psk == NULL) {
+        psk = zero;
+    }
+    hydro_kx_init_state(&state, "Noise_Npsk0_hydro1");
+    hydro_hash_update(&state.h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES);
 
-    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+    hydro_hash_update(&state.h_st, psk, hydro_kx_PSKBYTES);
+    hydro_hash_update(&state.h_st, peer_eph_pk, hydro_x25519_PUBLICKEYBYTES);
+    if (hydro_kx_dh(&state, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_aead_decrypt(&state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) {
         return -1;
     }
     hydro_kx_final(&state, kp->tx, kp->rx);
@@ -262,20 +253,23 @@ hydro_kx_kk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_KK_PACKET1BYTES],
               const uint8_t           peer_static_pk[hydro_kx_PUBLICKEYBYTES],
               const hydro_kx_keypair *static_kp)
 {
-    uint8_t dh_res[hydro_x25519_BYTES];
-    mem_zero(state, sizeof *state);
+    uint8_t *packet1_eph_pk = &packet1[0];
+    uint8_t *packet1_mac    = &packet1[hydro_kx_PUBLICKEYBYTES];
 
-    hydro_kx_keygen(&state->eph_kp);
-    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
-                    hydro_kx_CONTEXT, NULL);
-    memcpy(packet1, state->eph_kp.pk, sizeof state->eph_kp.pk);
+    hydro_kx_init_state(state, "Noise_KK_hydro1");
+    hydro_hash_update(&state->h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES);
+    hydro_hash_update(&state->h_st, peer_static_pk, hydro_kx_PUBLICKEYBYTES);
 
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+    hydro_kx_eph_keygen(state, &state->hs.eph_kp);
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_static_pk) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_static_pk) != 0) {
+    if (hydro_kx_dh(state, static_kp->sk, peer_static_pk) != 0) {
         return -1;
     }
+    hydro_kx_aead_encrypt(state, packet1_mac, NULL, 0);
+    memcpy(packet1_eph_pk, state->hs.eph_kp.pk, sizeof state->hs.eph_kp.pk);
+
     return 0;
 }
 
@@ -286,49 +280,59 @@ hydro_kx_kk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_KK_PACKET2B
               const hydro_kx_keypair *static_kp)
 {
     hydro_kx_state state;
-    uint8_t        dh_res[hydro_x25519_BYTES];
-    const uint8_t *peer_eph_pk = packet1;
-
-    mem_zero(&state, sizeof state);
+    const uint8_t *peer_eph_pk    = &packet1[0];
+    const uint8_t *packet1_mac    = &packet1[hydro_kx_PUBLICKEYBYTES];
+    uint8_t *      packet2_eph_pk = &packet2[0];
+    uint8_t *      packet2_mac    = &packet2[hydro_kx_PUBLICKEYBYTES];
 
-    hydro_kx_keygen(&state.eph_kp);
-    hydro_hash_hash(state.h, sizeof state.h, state.eph_kp.pk, sizeof state.eph_kp.pk,
-                    hydro_kx_CONTEXT, NULL);
-    memcpy(packet2, state.eph_kp.pk, sizeof state.eph_kp.pk);
+    hydro_kx_init_state(&state, "Noise_KK_hydro1");
+    hydro_hash_update(&state.h_st, peer_static_pk, hydro_kx_PUBLICKEYBYTES);
+    hydro_hash_update(&state.h_st, static_kp->pk, hydro_kx_PUBLICKEYBYTES);
 
-    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+    hydro_hash_update(&state.h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES);
+    if (hydro_kx_dh(&state, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_dh(&state, static_kp->sk, peer_static_pk) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(&state, dh_res, static_kp->sk, peer_static_pk) != 0) {
+    if (hydro_kx_aead_decrypt(&state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) {
         return -1;
     }
 
-    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_eph_pk) != 0) {
+    hydro_kx_eph_keygen(&state, &state.hs.eph_kp);
+    if (hydro_kx_dh(&state, state.hs.eph_kp.sk, peer_eph_pk) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(&state, dh_res, state.eph_kp.sk, peer_static_pk) != 0) {
+    if (hydro_kx_dh(&state, state.hs.eph_kp.sk, peer_static_pk) != 0) {
         return -1;
     }
-    hydro_kx_final(&state, kp->rx, kp->tx);
+    hydro_kx_aead_encrypt(&state, packet2_mac, NULL, 0);
+    hydro_kx_final(&state, kp->tx, kp->rx);
+    memcpy(packet2_eph_pk, state.hs.eph_kp.pk, sizeof state.hs.eph_kp.pk);
 
     return 0;
 }
 
 int
 hydro_kx_kk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
-              const uint8_t packet2[hydro_kx_KK_PACKET2BYTES],
-              const hydro_kx_keypair *static_kp)
+              const uint8_t packet2[hydro_kx_KK_PACKET2BYTES], const hydro_kx_keypair *static_kp)
 {
-    uint8_t        dh_res[hydro_x25519_BYTES];
     const uint8_t *peer_eph_pk = packet2;
+    const uint8_t *packet2_mac = &packet2[hydro_kx_PUBLICKEYBYTES];
 
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
+    hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES);
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_eph_pk) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+    if (hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) {
         return -1;
     }
-    hydro_kx_final(state, kp->tx, kp->rx);
+
+    if (hydro_kx_aead_decrypt(state, NULL, packet2_mac, hydro_kx_AEAD_MACBYTES) != 0) {
+        return -1;
+    }
+    hydro_kx_final(state, kp->rx, kp->tx);
 
     return 0;
 }
@@ -339,13 +343,18 @@ int
 hydro_kx_xx_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_XX_PACKET1BYTES],
               const uint8_t psk[hydro_kx_PSKBYTES])
 {
-    mem_zero(state, sizeof *state);
+    uint8_t *packet1_eph_pk = &packet1[0];
+    uint8_t *packet1_mac    = &packet1[hydro_kx_PUBLICKEYBYTES];
+
+    if (psk == NULL) {
+        psk = zero;
+    }
+    hydro_kx_init_state(state, "Noise_XXpsk0+psk3_hydro1");
 
-    hydro_kx_keygen(&state->eph_kp);
-    COMPILER_ASSERT(hydro_kx_PSKBYTES >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
-                    hydro_kx_CONTEXT, psk);
-    memcpy(packet1, state->eph_kp.pk, hydro_kx_PUBLICKEYBYTES);
+    hydro_kx_eph_keygen(state, &state->hs.eph_kp);
+    hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES);
+    memcpy(packet1_eph_pk, state->hs.eph_kp.pk, sizeof state->hs.eph_kp.pk);
+    hydro_kx_aead_encrypt(state, packet1_mac, NULL, 0);
 
     return 0;
 }
@@ -355,27 +364,36 @@ hydro_kx_xx_2(hydro_kx_state *state, uint8_t packet2[hydro_kx_XX_PACKET2BYTES],
               const uint8_t packet1[hydro_kx_XX_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES],
               const hydro_kx_keypair *static_kp)
 {
-    uint8_t        dh_res[hydro_x25519_BYTES];
-    const uint8_t *peer_eph_pk = packet1;
-
-    mem_zero(state, sizeof *state);
+    const uint8_t *peer_eph_pk           = &packet1[0];
+    const uint8_t *packet1_mac           = &packet1[hydro_kx_PUBLICKEYBYTES];
+    uint8_t *      packet2_eph_pk        = &packet2[0];
+    uint8_t *      packet2_enc_static_pk = &packet2[hydro_kx_PUBLICKEYBYTES];
+    uint8_t *      packet2_mac =
+        &packet2[hydro_kx_PUBLICKEYBYTES + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES];
+
+    if (psk == NULL) {
+        psk = zero;
+    }
+    hydro_kx_init_state(state, "Noise_XXpsk0+psk3_hydro1");
 
-    hydro_hash_hash(state->h, sizeof state->h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES,
-                    hydro_kx_CONTEXT, psk);
-    hydro_kx_keygen(&state->eph_kp);
-    memcpy(packet2, state->eph_kp.pk, sizeof state->eph_kp.pk);
-    COMPILER_ASSERT(sizeof state->h >= hydro_hash_KEYBYTES);
-    hydro_hash_hash(state->h, sizeof state->h, state->eph_kp.pk, sizeof state->eph_kp.pk,
-                    hydro_kx_CONTEXT, state->h);
+    hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES);
+    hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES);
+    if (hydro_kx_aead_decrypt(state, NULL, packet1_mac, hydro_kx_AEAD_MACBYTES) != 0) {
+        return -1;
+    }
 
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
+    hydro_kx_eph_keygen(state, &state->hs.eph_kp);
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_eph_pk) != 0) {
         return -1;
     }
-    hydro_kx_encrypt(state, packet2 + sizeof state->eph_kp.pk, static_kp->pk, sizeof static_kp->pk,
-                     psk);
-    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+    hydro_kx_aead_encrypt(state, packet2_enc_static_pk, static_kp->pk, sizeof static_kp->pk);
+    if (hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) {
         return -1;
     }
+    hydro_kx_aead_encrypt(state, packet2_mac, NULL, 0);
+
+    memcpy(packet2_eph_pk, state->hs.eph_kp.pk, sizeof state->hs.eph_kp.pk);
+
     return 0;
 }
 
@@ -386,31 +404,41 @@ hydro_kx_xx_3(hydro_kx_state *state, hydro_kx_session_keypair *kp,
               const uint8_t packet2[hydro_kx_XX_PACKET2BYTES], const uint8_t psk[hydro_kx_PSKBYTES],
               const hydro_kx_keypair *static_kp)
 {
-    uint8_t        dh_res[hydro_x25519_BYTES];
     uint8_t        peer_static_pk_[hydro_kx_PUBLICKEYBYTES];
-    const uint8_t *peer_eph_pk              = packet2;
-    const uint8_t *peer_encrypted_static_pk = packet2 + hydro_kx_PUBLICKEYBYTES;
-
-    hydro_hash_hash(state->h, sizeof state->h, peer_eph_pk, hydro_kx_PUBLICKEYBYTES,
-                    hydro_kx_CONTEXT, state->h);
-
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_eph_pk) != 0) {
-        return -1;
+    const uint8_t *peer_eph_pk        = &packet2[0];
+    const uint8_t *peer_enc_static_pk = &packet2[hydro_kx_PUBLICKEYBYTES];
+    const uint8_t *packet2_mac =
+        &packet2[hydro_kx_PUBLICKEYBYTES + hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES];
+    uint8_t *packet3_enc_static_pk = &packet3[0];
+    uint8_t *packet3_mac           = &packet3[hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES];
+
+    if (psk == NULL) {
+        psk = zero;
     }
     if (peer_static_pk == NULL) {
         peer_static_pk = peer_static_pk_;
     }
-    if (hydro_kx_decrypt(state, peer_static_pk, peer_encrypted_static_pk,
-                         hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES, psk) != 0) {
+    hydro_hash_update(&state->h_st, peer_eph_pk, hydro_kx_PUBLICKEYBYTES);
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    if (hydro_kx_aead_decrypt(state, peer_static_pk, peer_enc_static_pk,
+                              hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_static_pk) != 0) {
         return -1;
     }
-    hydro_kx_encrypt(state, packet3, static_kp->pk, sizeof static_kp->pk, psk);
-    if (hydro_kx_scalarmult(state, dh_res, static_kp->sk, peer_eph_pk) != 0) {
+    if (hydro_kx_aead_decrypt(state, NULL, packet2_mac, hydro_kx_AEAD_MACBYTES) != 0) {
         return -1;
     }
+
+    hydro_kx_aead_encrypt(state, packet3_enc_static_pk, static_kp->pk, sizeof static_kp->pk);
+    if (hydro_kx_dh(state, static_kp->sk, peer_eph_pk) != 0) {
+        return -1;
+    }
+    hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES);
+    hydro_kx_aead_encrypt(state, packet3_mac, NULL, 0);
     hydro_kx_final(state, kp->rx, kp->tx);
 
     return 0;
@@ -421,18 +449,25 @@ hydro_kx_xx_4(hydro_kx_state *state, hydro_kx_session_keypair *kp,
               uint8_t       peer_static_pk[hydro_kx_PUBLICKEYBYTES],
               const uint8_t packet3[hydro_kx_XX_PACKET3BYTES], const uint8_t psk[hydro_kx_PSKBYTES])
 {
-    uint8_t        dh_res[hydro_x25519_BYTES];
     uint8_t        peer_static_pk_[hydro_kx_PUBLICKEYBYTES];
-    const uint8_t *peer_encrypted_static_pk = packet3;
+    const uint8_t *peer_enc_static_pk = &packet3[0];
+    const uint8_t *packet3_mac        = &packet3[hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES];
 
+    if (psk == NULL) {
+        psk = zero;
+    }
     if (peer_static_pk == NULL) {
         peer_static_pk = peer_static_pk_;
     }
-    if (hydro_kx_decrypt(state, peer_static_pk, peer_encrypted_static_pk,
-                         hydro_kx_AEAD_HEADERBYTES + hydro_kx_PUBLICKEYBYTES, psk) != 0) {
+    if (hydro_kx_aead_decrypt(state, peer_static_pk, peer_enc_static_pk,
+                              hydro_kx_PUBLICKEYBYTES + hydro_kx_AEAD_MACBYTES) != 0) {
+        return -1;
+    }
+    if (hydro_kx_dh(state, state->hs.eph_kp.sk, peer_static_pk) != 0) {
         return -1;
     }
-    if (hydro_kx_scalarmult(state, dh_res, state->eph_kp.sk, peer_static_pk) != 0) {
+    hydro_hash_update(&state->h_st, psk, hydro_kx_PSKBYTES);
+    if (hydro_kx_aead_decrypt(state, NULL, packet3_mac, hydro_kx_AEAD_MACBYTES) != 0) {
         return -1;
     }
     hydro_kx_final(state, kp->tx, kp->rx);

+ 14 - 14
impl/random.h

@@ -13,7 +13,7 @@ hydro_random_rbit(uint16_t x)
 {
     uint8_t x8;
 
-    x8 = ((uint8_t) (x >> 8)) ^ (uint8_t) x;
+    x8 = ((uint8_t)(x >> 8)) ^ (uint8_t) x;
     x8 = (x8 >> 4) ^ (x8 & 0xf);
     x8 = (x8 >> 2) ^ (x8 & 0x3);
     x8 = (x8 >> 1) ^ x8;
@@ -100,8 +100,9 @@ hydro_random_init(void)
 
 #elif defined(PARTICLE) && defined(PLATFORM_ID) && PLATFORM_ID > 2 && !defined(__unix__)
 
-// Note: All particle platforms except for the Spark Core have a HW RNG.  Only allow building on supported platforms for now.
-// PLATFORM_ID definitions: https://github.com/particle-iot/device-os/blob/mesh-develop/hal/shared/platforms.h
+// Note: All particle platforms except for the Spark Core have a HW RNG.  Only allow building on
+// supported platforms for now. PLATFORM_ID definitions:
+// https://github.com/particle-iot/device-os/blob/mesh-develop/hal/shared/platforms.h
 
 #include "Particle.h"
 
@@ -138,7 +139,7 @@ hydro_random_init(void)
 {
     const char       ctx[hydro_hash_CONTEXTBYTES] = { 'h', 'y', 'd', 'r', 'o', 'P', 'R', 'G' };
     hydro_hash_state st;
-    const uint8_t    total_bytes = 32;
+    const uint8_t    total_bytes     = 32;
     uint8_t          remaining_bytes = total_bytes;
     uint8_t          available_bytes;
     uint8_t          rand_buffer[32];
@@ -178,7 +179,7 @@ hydro_random_init(void)
 extern "C"
 #endif
     BOOLEAN NTAPI
-            RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
+    RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
 #pragma comment(lib, "advapi32.lib")
 
 static int
@@ -200,8 +201,7 @@ hydro_random_init(void)
 static int
 hydro_random_init(void)
 {
-    if (getentropy(hydro_random_context.state,
-                   sizeof hydro_random_context.state) != 0) {
+    if (getentropy(hydro_random_context.state, sizeof hydro_random_context.state) != 0) {
         return -1;
     }
     hydro_random_context.counter = ~LOAD64_LE(hydro_random_context.state);
@@ -214,7 +214,7 @@ hydro_random_init(void)
 #include <errno.h>
 #include <fcntl.h>
 #ifdef __linux__
-# include <poll.h>
+#include <poll.h>
 #endif
 #include <sys/types.h>
 #include <unistd.h>
@@ -253,8 +253,8 @@ hydro_random_safe_read(const int fd, void *const buf_, size_t len)
     ssize_t        readnb;
 
     do {
-        while ((readnb = read(fd, buf, len)) < (ssize_t) 0 &&
-               (errno == EINTR || errno == EAGAIN)) { }
+        while ((readnb = read(fd, buf, len)) < (ssize_t) 0 && (errno == EINTR || errno == EAGAIN)) {
+        }
         if (readnb < (ssize_t) 0) {
             return readnb;
         }
@@ -329,8 +329,8 @@ hydro_random_init(void)
 
         // Forces mbedTLS to fetch fresh entropy, then get some to feed libhydrogen.
         if (mbedtls_entropy_gather(&entropy) != 0 ||
-            mbedtls_entropy_func(&entropy,
-                                 &hydro_random_context.state[pos], currentChunkSize) != 0) {
+            mbedtls_entropy_func(&entropy, &hydro_random_context.state[pos], currentChunkSize) !=
+                0) {
             return -1;
         }
         pos += MBEDTLS_ENTROPY_BLOCK_SIZE;
@@ -342,11 +342,11 @@ hydro_random_init(void)
 }
 
 #else
-# error Need an entropy source
+#error Need an entropy source
 #endif
 
 #else
-# error Unsupported platform
+#error Unsupported platform
 #endif
 
 static void

+ 4 - 4
impl/secretbox.h

@@ -85,7 +85,7 @@ hydro_secretbox_setup(uint8_t buf[gimli_BLOCKBYTES], uint64_t msg_id,
 }
 
 static void
-hydro_secretbox_finalize(uint8_t *buf, const uint8_t key[hydro_secretbox_KEYBYTES], uint8_t tag)
+hydro_secretbox_final(uint8_t *buf, const uint8_t key[hydro_secretbox_KEYBYTES], uint8_t tag)
 {
     COMPILER_ASSERT(hydro_secretbox_KEYBYTES == gimli_CAPACITY);
     mem_xor(buf + gimli_RATE, key, hydro_secretbox_KEYBYTES);
@@ -128,7 +128,7 @@ hydro_secretbox_encrypt_iv(uint8_t *c, const void *m_, size_t mlen, uint64_t msg
     gimli_pad_u8(buf, leftover, gimli_DOMAIN_XOF);
     gimli_core_u8(buf, gimli_TAG_PAYLOAD);
 
-    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL0);
+    hydro_secretbox_final(buf, key, gimli_TAG_FINAL0);
     COMPILER_ASSERT(hydro_secretbox_SIVBYTES <= gimli_CAPACITY);
     memcpy(siv, buf + gimli_RATE, hydro_secretbox_SIVBYTES);
 
@@ -139,7 +139,7 @@ hydro_secretbox_encrypt_iv(uint8_t *c, const void *m_, size_t mlen, uint64_t msg
     hydro_secretbox_setup(buf, msg_id, ctx, key, siv, gimli_TAG_KEY);
     hydro_secretbox_xor_enc(buf, ct, m, mlen);
 
-    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL);
+    hydro_secretbox_final(buf, key, gimli_TAG_FINAL);
     COMPILER_ASSERT(hydro_secretbox_MACBYTES <= gimli_CAPACITY);
     memcpy(mac, buf + gimli_RATE, hydro_secretbox_MACBYTES);
 
@@ -223,7 +223,7 @@ hydro_secretbox_decrypt(void *m_, const uint8_t *c, size_t clen, uint64_t msg_id
     hydro_secretbox_setup(buf, msg_id, ctx, key, siv, gimli_TAG_KEY);
     hydro_secretbox_xor_dec(buf, m, ct, mlen);
 
-    hydro_secretbox_finalize(buf, key, gimli_TAG_FINAL);
+    hydro_secretbox_final(buf, key, gimli_TAG_FINAL);
     COMPILER_ASSERT(hydro_secretbox_MACBYTES <= gimli_CAPACITY);
     COMPILER_ASSERT(gimli_RATE % 4 == 0);
     cv = hydro_mem_ct_cmp_u32(state + gimli_RATE / 4, pub_mac, hydro_secretbox_MACBYTES / 4);

+ 15 - 14
impl/x25519.h

@@ -5,25 +5,25 @@
  */
 
 #if defined(__GNUC__) && defined(__SIZEOF_INT128__)
-# define hydro_x25519_WBITS 64
+#define hydro_x25519_WBITS 64
 #else
-# define hydro_x25519_WBITS 32
+#define hydro_x25519_WBITS 32
 #endif
 
 #if hydro_x25519_WBITS == 64
 typedef uint64_t    hydro_x25519_limb_t;
 typedef __uint128_t hydro_x25519_dlimb_t;
 typedef __int128_t  hydro_x25519_sdlimb_t;
-# define hydro_x25519_eswap_limb(X) LOAD64_LE((const uint8_t *) &(X))
-# define hydro_x25519_LIMB(x) x##ull
+#define hydro_x25519_eswap_limb(X) LOAD64_LE((const uint8_t *) &(X))
+#define hydro_x25519_LIMB(x) x##ull
 #elif hydro_x25519_WBITS == 32
 typedef uint32_t hydro_x25519_limb_t;
 typedef uint64_t hydro_x25519_dlimb_t;
 typedef int64_t  hydro_x25519_sdlimb_t;
-# define hydro_x25519_eswap_limb(X) LOAD32_LE((const uint8_t *) &(X))
-# define hydro_x25519_LIMB(x) (uint32_t)(x##ull), (uint32_t)((x##ull) >> 32)
+#define hydro_x25519_eswap_limb(X) LOAD32_LE((const uint8_t *) &(X))
+#define hydro_x25519_LIMB(x) (uint32_t)(x##ull), (uint32_t)((x##ull) >> 32)
 #else
-# error "Need to know hydro_x25519_WBITS"
+#error "Need to know hydro_x25519_WBITS"
 #endif
 
 #define hydro_x25519_NLIMBS (256 / hydro_x25519_WBITS)
@@ -109,10 +109,10 @@ hydro_x25519_sub(hydro_x25519_fe out, const hydro_x25519_fe a, const hydro_x2551
     int                   i;
 
     for (i = 0; i < hydro_x25519_NLIMBS; i++) {
-        out[i] = (hydro_x25519_limb_t) (carry = carry + a[i] - b[i]);
+        out[i] = (hydro_x25519_limb_t)(carry = carry + a[i] - b[i]);
         carry >>= hydro_x25519_WBITS;
     }
-    hydro_x25519_propagate(out, (hydro_x25519_limb_t) (1 + carry));
+    hydro_x25519_propagate(out, (hydro_x25519_limb_t)(1 + carry));
 }
 
 static void
@@ -207,7 +207,7 @@ hydro_x25519_canon(hydro_x25519_fe x)
     carry = -19;
     res   = 0;
     for (i = 0; i < hydro_x25519_NLIMBS; i++) {
-        res |= x[i] = (hydro_x25519_limb_t) (carry += x[i]);
+        res |= x[i] = (hydro_x25519_limb_t)(carry += x[i]);
         carry >>= hydro_x25519_WBITS;
     }
     return ((hydro_x25519_dlimb_t) res - 1) >> hydro_x25519_WBITS;
@@ -286,8 +286,9 @@ hydro_x25519_core(hydro_x25519_fe xs[5], const uint8_t scalar[hydro_x25519_BYTES
 }
 
 static int
-hydro_x25519_scalarmult(uint8_t out[hydro_x25519_BYTES], const uint8_t scalar[hydro_x25519_BYTES],
-                        const uint8_t x1[hydro_x25519_BYTES], bool clamp)
+hydro_x25519_scalarmult(uint8_t       out[hydro_x25519_BYTES],
+                        const uint8_t scalar[hydro_x25519_SECRETKEYBYTES],
+                        const uint8_t x1[hydro_x25519_PUBLICKEYBYTES], bool clamp)
 {
     hydro_x25519_fe      xs[5];
     hydro_x25519_limb_t *x2, *z2, *z3;
@@ -371,10 +372,10 @@ hydro_x25519_sc_montmul(hydro_x25519_scalar_t out, const hydro_x25519_scalar_t a
     /* Reduce */
     hydro_x25519_sdlimb_t scarry = 0;
     for (i = 0; i < hydro_x25519_NLIMBS; i++) {
-        out[i] = (hydro_x25519_limb_t) (scarry = scarry + out[i] - hydro_x25519_sc_p[i]);
+        out[i] = (hydro_x25519_limb_t)(scarry = scarry + out[i] - hydro_x25519_sc_p[i]);
         scarry >>= hydro_x25519_WBITS;
     }
-    hydro_x25519_limb_t need_add = (hydro_x25519_limb_t) -(scarry + hic);
+    hydro_x25519_limb_t need_add = (hydro_x25519_limb_t) - (scarry + hic);
 
     hydro_x25519_limb_t carry = 0;
     for (i = 0; i < hydro_x25519_NLIMBS; i++) {

+ 1 - 1
tests/tests.c

@@ -1,5 +1,5 @@
 #ifdef NDEBUG
-# undef NDEBUG
+#undef NDEBUG
 #endif
 #include <assert.h>
 #include <stdio.h>