]> git.codecow.com Git - Monocypher.git/commitdiff
Added secure channel protocols (experimental)
authorLoup Vaillant <loup@loup-vaillant.fr>
Sun, 3 Mar 2019 21:56:29 +0000 (22:56 +0100)
committerLoup Vaillant <loup@loup-vaillant.fr>
Sun, 3 Mar 2019 21:56:29 +0000 (22:56 +0100)
At long last, the NaCl family of crypto libraries is gaining direct
support for secure channels.

Up until now, the choices were basically invent our own protocol, or
give up and use a TLS library, thus voiding the usability improvements
of NaCl libraries.

Now we have a solution.  It's still a bit experimental, it's not yet
documented, but it's there.  And soon, we will finally be able to shift
the cryptographic right answer for secure channels away from TLS, and
towards the NaCl family.  Or perhaps just Monocypher, if for some reason
Libsodium doesn't follow suit. :-)

src/monocypher.c
src/monocypher.h
tests/gen/makefile
tests/gen/monokex_x.c [new file with mode: 0644]
tests/gen/monokex_xk1.c [new file with mode: 0644]
tests/speed.c
tests/test.c

index b216c26cd86075dc1931f8ab1a2ac61eab9001b7..24c13e2077419cc749f67e94e35646069b6d835e 100644 (file)
@@ -2165,3 +2165,213 @@ int crypto_unlock(u8       *plain_text,
     return crypto_unlock_aead(plain_text, key, nonce, mac, 0, 0,
                               cipher_text, text_size);
 }
+
+///////////////////////
+/// Secure channels ///
+///////////////////////
+static void copy32(u8 out[32], const u8 in[32]){FOR (i, 0, 32){out[i] = in[i];}}
+static void xor32 (u8 out[32], const u8 in[32]){FOR (i, 0, 32){out[i]^= in[i];}}
+
+static void kex_update_key(crypto_kex_ctx  *ctx,
+                           const u8 secret_key[32],
+                           const u8 public_key[32])
+{
+    static const u8 one[16] = {1};
+
+    // Extract
+    u8 shared_secret[32];
+    crypto_x25519(shared_secret, secret_key, public_key);
+    crypto_chacha20_H(shared_secret    , shared_secret    , zero);
+    crypto_chacha20_H(ctx->chaining_key, ctx->chaining_key, one );
+    xor32(ctx->chaining_key, shared_secret);
+
+    // Expand (directly from chaining key)
+    crypto_chacha_ctx chacha_ctx;
+    crypto_chacha20_init  (&chacha_ctx, ctx->chaining_key, one);
+    crypto_chacha20_stream(&chacha_ctx, ctx->derived_keys, 64);
+
+    // Clean up
+    WIPE_BUFFER(shared_secret);
+    WIPE_CTX(&chacha_ctx);
+}
+
+static void kex_auth(crypto_kex_ctx *ctx, u8 mac[16])
+{
+    crypto_poly1305(mac, ctx->transcript, ctx->transcript_size,
+                    ctx->derived_keys);
+}
+
+static int kex_verify(crypto_kex_ctx *ctx, const u8 mac[16])
+{
+    u8 real_mac[16];
+    kex_auth(ctx, real_mac);
+    int mismatch = crypto_verify16(real_mac, mac);
+    if (mismatch) {  WIPE_CTX(ctx); }
+    WIPE_BUFFER(real_mac);
+    return mismatch;
+}
+
+static void kex_send(crypto_kex_ctx *ctx, u8 msg[32], const u8 src[32])
+{
+    // Send message, encrypted if we have a key
+    copy32(msg, src);
+    xor32(msg, ctx->derived_keys + 32);
+    // Record sent message
+    copy32(ctx->transcript + ctx->transcript_size, msg);
+    ctx->transcript_size += 32;
+}
+
+static void kex_receive(crypto_kex_ctx *ctx, u8 dest[32], const u8 msg[32])
+{
+    // Record incoming message
+    copy32(ctx->transcript + ctx->transcript_size, msg);
+    ctx->transcript_size += 32;
+    // Receive message, decrypted it if we have a key
+    copy32(dest, msg);
+    xor32(dest, ctx->derived_keys + 32);
+}
+
+static void kex_init(crypto_kex_ctx *ctx,
+                     const u8        local_sk[32],
+                     const u8        local_pk[32])
+{
+    if (local_pk == 0) crypto_x25519_public_key(ctx->local_pk, local_sk);
+    else               copy32                  (ctx->local_pk, local_pk);
+    copy32(ctx->chaining_key     , zero    );
+    copy32(ctx->derived_keys + 32, zero    ); // first encryption key is zero
+    copy32(ctx->local_sk         , local_sk);
+    ctx->transcript_size = 0;
+}
+
+static void kex_seed(crypto_kex_ctx *ctx, u8 random_seed[32])
+{
+    copy32(ctx->local_ske        , random_seed);
+    crypto_wipe(random_seed, 32); // auto wipe seed to avoid reuse
+    crypto_x25519_public_key(ctx->local_pke, ctx->local_ske);
+}
+
+
+///////////
+/// XK1 ///
+///////////
+static const u8 xk1_ck0[32] = "Monokex XK1";
+void crypto_kex_xk1_init_client(crypto_kex_ctx *ctx,
+                                u8              random_seed[32],
+                                const u8        local_sk   [32],
+                                const u8        local_pk   [32],
+                                const u8        remote_pk  [32])
+{
+    kex_init   (ctx, local_sk, local_pk);
+    kex_seed   (ctx, random_seed);
+    kex_receive(ctx, ctx->remote_pk, remote_pk);
+    copy32(ctx->chaining_key, xk1_ck0);
+}
+
+void crypto_kex_xk1_init_server(crypto_kex_ctx *ctx,
+                                u8              random_seed[32],
+                                const u8        local_sk   [32],
+                                const u8        local_pk   [32])
+{
+    kex_init   (ctx, local_sk, local_pk);
+    kex_seed   (ctx, random_seed);
+    kex_receive(ctx, ctx->local_pk, ctx->local_pk);
+    copy32(ctx->chaining_key, xk1_ck0);
+}
+
+void crypto_kex_xk1_1(crypto_kex_ctx *ctx, u8 msg1[32])
+{
+    kex_send      (ctx, msg1           , ctx->local_pke );  // -> IE
+}
+
+void crypto_kex_xk1_2(crypto_kex_ctx *ctx, u8 msg2[48], const u8 msg1[32])
+{
+    kex_receive   (ctx, ctx->remote_pke, msg1           );  // -> IE
+    kex_send      (ctx, msg2           , ctx->local_pke );  // <- RE
+    kex_update_key(ctx, ctx->local_ske , ctx->remote_pke);  //    ee
+    kex_update_key(ctx, ctx->local_sk  , ctx->remote_pke);  //    es
+    kex_auth      (ctx, msg2 + 32);                         // auth
+}
+
+int crypto_kex_xk1_3(crypto_kex_ctx *ctx,
+                     u8              session_key[32],
+                     u8              msg3[48],
+                     const u8        msg2[48])
+{
+    kex_receive   (ctx, ctx->remote_pke, msg2           );  // <- RE
+    kex_update_key(ctx, ctx->local_ske , ctx->remote_pke);  //    ee
+    kex_update_key(ctx, ctx->local_ske , ctx->remote_pk );  //    es
+    if (kex_verify(ctx, msg2 + 32)) { return -1; }          // verify
+    kex_send      (ctx, msg3           , ctx->local_pk  );  // -> IS
+    kex_update_key(ctx, ctx->local_sk  , ctx->remote_pke);  //    se
+    kex_auth      (ctx, msg3 + 32);                         // auth
+    copy32(session_key, ctx->derived_keys + 32);
+    WIPE_CTX(ctx);
+    return 0;
+}
+
+int crypto_kex_xk1_4(crypto_kex_ctx *ctx,
+                     u8              session_key[32],
+                     u8              remote_pk[32],
+                     const u8        msg3[48])
+{
+    kex_receive   (ctx, ctx->remote_pk , msg3           );  // -> IS
+    kex_update_key(ctx, ctx->local_ske , ctx->remote_pk );  //    se
+    if (kex_verify(ctx, msg3 + 32)) { return -1; }          // verify
+    copy32(remote_pk  , ctx->remote_pk);
+    copy32(session_key, ctx->derived_keys + 32);
+    WIPE_CTX(ctx);
+    return 0;
+}
+
+/////////
+/// X ///
+/////////
+static const u8 x_ck0[32] = "Monokex X";
+void crypto_kex_x_init_client(crypto_kex_ctx *ctx,
+                              u8              random_seed[32],
+                              const u8        local_sk   [32],
+                              const u8        local_pk   [32],
+                              const u8        remote_pk  [32])
+{
+    kex_init   (ctx, local_sk, local_pk);
+    kex_seed   (ctx, random_seed);
+    kex_receive(ctx, ctx->remote_pk, remote_pk);
+    copy32(ctx->chaining_key, x_ck0);
+}
+
+void crypto_kex_x_init_server(crypto_kex_ctx *ctx,
+                              const u8        local_sk[32],
+                              const u8        local_pk[32])
+{
+    kex_init   (ctx, local_sk, local_pk);
+    kex_receive(ctx, ctx->local_pk, ctx->local_pk);
+    copy32(ctx->chaining_key, x_ck0);
+}
+
+void crypto_kex_x_1(crypto_kex_ctx *ctx, u8 session_key[32], u8 msg1[80])
+{
+    kex_send      (ctx, msg1           , ctx->local_pke );  // -> IE
+    kex_update_key(ctx, ctx->local_ske , ctx->remote_pk );  //    es
+    kex_send      (ctx, msg1 + 32      , ctx->local_pk  );  // -> IS
+    kex_update_key(ctx, ctx->local_sk  , ctx->remote_pk );  //    ss
+    kex_auth      (ctx, msg1 + 64);                         // auth
+    copy32(session_key, ctx->derived_keys + 32);
+    WIPE_CTX(ctx);
+}
+
+int crypto_kex_x_2(crypto_kex_ctx *ctx,
+                   u8              session_key[32],
+                   u8              remote_pk  [32],
+                   const u8        msg1       [80])
+{
+    kex_receive   (ctx, ctx->remote_pke, msg1           );  // -> IE
+    kex_update_key(ctx, ctx->local_sk  , ctx->remote_pke);  //    es
+    kex_receive   (ctx, ctx->remote_pk , msg1 + 32      );  // -> IS
+    kex_update_key(ctx, ctx->local_sk  , ctx->remote_pk );  //    ss
+    if (kex_verify(ctx, msg1 + 64)) { return -1; }          // verify
+    copy32(remote_pk  , ctx->remote_pk);
+    copy32(session_key, ctx->derived_keys + 32);
+    WIPE_CTX(ctx);
+    return 0;
+}
+
index 019f70ffa9e300dc8b2a4a209e8e3f7e5cfb1984..1c6716a78796cf5292f6329445543d7a6b28a54b 100644 (file)
@@ -64,6 +64,19 @@ typedef struct {
     uint8_t pk [32];
 } crypto_check_ctx;
 
+// Secure channels (Monokex)
+typedef struct {
+    uint8_t transcript [128];
+    uint8_t chaining_key[32];
+    uint8_t derived_keys[64];
+    uint8_t local_sk    [32];
+    uint8_t local_pk    [32];
+    uint8_t local_ske   [32];
+    uint8_t local_pke   [32];
+    uint8_t remote_pk   [32];
+    uint8_t remote_pke  [32];
+    size_t  transcript_size;
+} crypto_kex_ctx;
 
 ////////////////////////////
 /// High level interface ///
@@ -220,6 +233,51 @@ void crypto_check_update(crypto_check_ctx *ctx,
 int crypto_check_final  (crypto_check_ctx *ctx);
 
 
+// Secure channel (interactive)
+// ----------------------------
+void crypto_kex_xk1_init_client(crypto_kex_ctx  *ctx,
+                                uint8_t       random_seed[32],
+                                const uint8_t local_sk   [32],
+                                const uint8_t local_pk   [32],
+                                const uint8_t remote_pk  [32]);
+void crypto_kex_xk1_init_server(crypto_kex_ctx  *ctx,
+                                uint8_t       random_seed[32],
+                                const uint8_t local_sk   [32],
+                                const uint8_t local_pk   [32]);
+
+void crypto_kex_xk1_1(crypto_kex_ctx *ctx,
+                      uint8_t         msg1[32]);
+void crypto_kex_xk1_2(crypto_kex_ctx *ctx,
+                      uint8_t         msg2[48],
+                      const uint8_t   msg1[32]);
+int crypto_kex_xk1_3(crypto_kex_ctx *ctx,
+                     uint8_t         session_key[32],
+                     uint8_t         msg3       [48],
+                     const uint8_t   msg2       [48]);
+int crypto_kex_xk1_4(crypto_kex_ctx *ctx,
+                     uint8_t         session_key[32],
+                     uint8_t         remote_pk  [32],
+                     const uint8_t   msg3       [48]);
+
+// Secure channel (one way)
+// ------------------------
+void crypto_kex_x_init_client(crypto_kex_ctx *ctx,
+                              uint8_t         random_seed[32],
+                              const uint8_t   local_sk   [32],
+                              const uint8_t   local_pk   [32],
+                              const uint8_t   remote_pk  [32]);
+void crypto_kex_x_init_server(crypto_kex_ctx *ctx,
+                              const uint8_t   local_sk   [32],
+                              const uint8_t   local_pk   [32]);
+void crypto_kex_x_1(crypto_kex_ctx *ctx,
+                    uint8_t         session_key[32],
+                    uint8_t         msg1       [80]);
+int crypto_kex_x_2(crypto_kex_ctx *ctx,
+                   uint8_t         session_key[32],
+                   uint8_t         remote_pk  [32],
+                   const uint8_t   msg1       [80]);
+
+
 ////////////////////////////
 /// Low level primitives ///
 ////////////////////////////
index 484da48df64e1b5e4ac76618f4d2d5807f5f0a6a..0883b26c36fb621f911df82317896ba25322d0f4 100644 (file)
@@ -3,12 +3,13 @@ CFLAGS = -pedantic -Wall -Wextra
 
 .PHONY: all clean
 
-VEC     = chacha20.vec hchacha20.vec xchacha20.vec aead_ietf.vec poly1305.vec \
-          blake2b.vec  sha512.vec    argon2i.vec                              \
-          edDSA.vec    edDSA_pk.vec  ed_25519.vec  ed_25519_check.vec         \
-          x25519.vec   x25519_pk.vec
-VEC2    = $(patsubst %.vec, %.all.vec, $(VEC)) key_exchange.all.vec
-HEADERS = $(patsubst %.all.vec, %.h.vec, $(VEC2))
+VEC     = chacha20    hchacha20  xchacha20     aead_ietf poly1305 \
+          blake2b     sha512     argon2i                          \
+          edDSA       edDSA_pk   ed_25519      ed_25519_check     \
+          x25519      x25519_pk  key_exchange                     \
+          monokex_xk1 monokex_x
+VEC2    = $(patsubst %, %.all.vec, $(VEC))
+HEADERS = $(patsubst %, %.h.vec  , $(VEC))
 VECTORS = ../vectors.h
 
 all: $(VECTORS)
@@ -45,21 +46,23 @@ ed25519.o: ../ed25519-donna/ed25519.c  $(wildcard ../ed25519-donna/*.h)
 vector_to_header.out: ../vector_to_header.c
        $(CC) $(CFLAGS) $< -o $@
 
-chacha20.all.vec      : chacha20.vec  ../vectors/chacha20
-poly1305.all.vec      : poly1305.vec  ../vectors/poly1305
-x25519.all.vec        : x25519.vec    ../vectors/x25519
+chacha20.all.vec      : chacha20.vec    ../vectors/chacha20
+poly1305.all.vec      : poly1305.vec    ../vectors/poly1305
+x25519.all.vec        : x25519.vec      ../vectors/x25519
 x25519_pk.all.vec     : x25519_pk.vec
 hchacha20.all.vec     : hchacha20.vec
 xchacha20.all.vec     : xchacha20.vec
 aead_ietf.all.vec     : aead_ietf.vec
 blake2b.all.vec       : blake2b.vec
 sha512.all.vec        : sha512.vec
-argon2i.all.vec       : argon2i.vec   ../vectors/argon2i
+argon2i.all.vec       : argon2i.vec     ../vectors/argon2i
 edDSA.all.vec         : edDSA.vec
 edDSA_pk.all.vec      : edDSA_pk.vec
 ed_25519.all.vec      : ed_25519.vec
-ed_25519_check.all.vec:               ../vectors/ed_25519_check
-key_exchange.all.vec  :               ../vectors/key_exchange
+ed_25519_check.all.vec:                 ../vectors/ed_25519_check
+key_exchange.all.vec  :                 ../vectors/key_exchange
+monokex_xk1.all.vec   : monokex_xk1.vec
+monokex_x.all.vec     : monokex_x.vec
 $(VEC2):
        mkdir -p $(@D)
        cat $^ > $@
diff --git a/tests/gen/monokex_x.c b/tests/gen/monokex_x.c
new file mode 100644 (file)
index 0000000..95f62c7
--- /dev/null
@@ -0,0 +1,136 @@
+#include <sodium.h>
+#include "utils.h"
+
+static const uint8_t zero[32] = {0};
+static const uint8_t one [16] = {1};
+
+static void crypto_chacha20_H(uint8_t       out[32],
+                              const uint8_t key[32],
+                              const uint8_t in [16])
+{
+    crypto_core_hchacha20(out, in, key, 0);
+}
+int crypto_x25519(uint8_t       shared[32],
+                  const uint8_t sk    [32],
+                  const uint8_t pk    [32])
+{
+    return crypto_scalarmult(shared, sk, pk);
+}
+#define crypto_x25519_public_key crypto_scalarmult_base
+#define crypto_poly1305_ctx      crypto_onetimeauth_state
+#define crypto_poly1305_init     crypto_onetimeauth_init
+#define crypto_poly1305_update   crypto_onetimeauth_update
+#define crypto_poly1305_final    crypto_onetimeauth_final
+
+static void xor(uint8_t out[32], const uint8_t a[32], const uint8_t b[32])
+{
+    for (unsigned i = 0; i < 32; i++) {
+        out[i] = a[i] ^ b[i];
+    }
+}
+
+static void copy(uint8_t out[32], const uint8_t in[32])
+{
+    for (unsigned i = 0; i < 32; i++) {
+        out[i] = in[i];
+    }
+}
+
+static void chacha_block(uint8_t       out[64],
+                         const uint8_t key[32],
+                         const uint8_t nonce[16])
+{
+    static const uint8_t in[64] = {0};
+    crypto_stream_chacha20_xor_ic(out, in, 64, nonce, 0, key);
+}
+
+typedef struct {
+    // Key pairs
+    uint8_t is[32];  uint8_t IS[32];
+    uint8_t ie[32];  uint8_t IE[32];
+    uint8_t rs[32];  uint8_t RS[32];
+
+    // Shared secrets
+    uint8_t es[32];
+    uint8_t ss[32];
+
+    // Symmetric Keys
+    uint8_t CK1[32];
+    uint8_t CK2[32];
+    uint8_t AK2[32];
+    uint8_t EK1[32];
+    uint8_t EK2[32];
+
+    // Messages
+    uint8_t msg1[80];
+} test_vectors_x;
+
+static void vectors_x_fill(test_vectors_x *v,
+                           const uint8_t client_sk  [32],
+                           const uint8_t server_sk  [32],
+                           const uint8_t client_seed[32])
+{
+    // Private keys
+    copy(v->is, client_sk  );
+    copy(v->ie, client_seed);
+    copy(v->rs, server_sk  );
+
+    // Public keys
+    crypto_x25519_public_key(v->IS, v->is);
+    crypto_x25519_public_key(v->IE, v->ie);
+    crypto_x25519_public_key(v->RS, v->rs);
+
+    // Exchanges
+    crypto_x25519(v->es, v->ie, v->RS);
+    crypto_x25519(v->ss, v->is, v->RS);
+
+    // Keys
+    uint8_t tmp1[32];
+    uint8_t tmp2[32];
+    uint8_t CK0 [32] = "Monokex X";
+    crypto_chacha20_H(tmp1, v->es , zero);
+    crypto_chacha20_H(tmp2, CK0   , one );
+    xor(v->CK1, tmp1, tmp2);
+    crypto_chacha20_H(tmp1, v->ss , zero);
+    crypto_chacha20_H(tmp2, v->CK1, one );
+    xor(v->CK2, tmp1, tmp2);
+    uint8_t tmp[64];
+    chacha_block(tmp, v->CK1, one);
+    copy(v->EK1, tmp + 32);
+    chacha_block(tmp, v->CK2, one);
+    copy(v->AK2, tmp     );
+    copy(v->EK2, tmp + 32);
+
+    // Messages
+    crypto_poly1305_ctx ctx;
+    uint8_t XIS[32];
+    xor(XIS, v->IS, v->EK1);
+    copy(v->msg1     , v->IE);
+    copy(v->msg1 + 32, XIS  );
+    crypto_poly1305_init  (&ctx, v->AK2);
+    crypto_poly1305_update(&ctx, v->RS, 32);
+    crypto_poly1305_update(&ctx, v->IE, 32);
+    crypto_poly1305_update(&ctx, XIS  , 32);
+    crypto_poly1305_final (&ctx, v->msg1 + 64);
+}
+
+int main(void)
+{
+    FOR(i, 0, 5) {
+        RANDOM_INPUT(client_sk  , 32);
+        RANDOM_INPUT(server_sk  , 32);
+        RANDOM_INPUT(client_seed, 32);
+        RANDOM_INPUT(server_seed, 32);
+        test_vectors_x v;
+        vectors_x_fill(&v, client_sk, server_sk, client_seed);
+
+        print_vector(v.is  , 32);
+        print_vector(v.ie  , 32);
+        print_vector(v.rs  , 32);
+        print_vector(v.IS  , 32);
+        print_vector(v.RS  , 32);
+        print_vector(v.msg1, 80);
+        print_vector(v.EK2 , 32);
+    }
+    return 0;
+}
diff --git a/tests/gen/monokex_xk1.c b/tests/gen/monokex_xk1.c
new file mode 100644 (file)
index 0000000..b89de03
--- /dev/null
@@ -0,0 +1,160 @@
+#include <sodium.h>
+#include "utils.h"
+
+static const uint8_t zero[32] = {0};
+static const uint8_t one [16] = {1};
+
+static void crypto_chacha20_H(uint8_t       out[32],
+                              const uint8_t key[32],
+                              const uint8_t in [16])
+{
+    crypto_core_hchacha20(out, in, key, 0);
+}
+int crypto_x25519(uint8_t       shared[32],
+                  const uint8_t sk    [32],
+                  const uint8_t pk    [32])
+{
+    return crypto_scalarmult(shared, sk, pk);
+}
+#define crypto_x25519_public_key crypto_scalarmult_base
+#define crypto_poly1305_ctx      crypto_onetimeauth_state
+#define crypto_poly1305_init     crypto_onetimeauth_init
+#define crypto_poly1305_update   crypto_onetimeauth_update
+#define crypto_poly1305_final    crypto_onetimeauth_final
+
+static void xor(uint8_t out[32], const uint8_t a[32], const uint8_t b[32])
+{
+    for (unsigned i = 0; i < 32; i++) {
+        out[i] = a[i] ^ b[i];
+    }
+}
+
+static void copy(uint8_t out[32], const uint8_t in[32])
+{
+    for (unsigned i = 0; i < 32; i++) {
+        out[i] = in[i];
+    }
+}
+
+static void chacha_block(uint8_t       out[64],
+                         const uint8_t key[32],
+                         const uint8_t nonce[16])
+{
+    static const uint8_t in[64] = {0};
+    crypto_stream_chacha20_xor_ic(out, in, 64, nonce, 0, key);
+}
+
+typedef struct {
+    // Key pairs
+    uint8_t is[32];  uint8_t IS[32];
+    uint8_t ie[32];  uint8_t IE[32];
+    uint8_t rs[32];  uint8_t RS[32];
+    uint8_t re[32];  uint8_t RE[32];
+
+    // Shared secrets
+    uint8_t ee[32];
+    uint8_t es[32];
+    uint8_t se[32];
+
+    // Symmetric keys
+    uint8_t CK1[32];
+    uint8_t CK2[32];
+    uint8_t CK3[32];
+    uint8_t AK2[32];
+    uint8_t AK3[32];
+    uint8_t EK2[32];
+    uint8_t EK3[32];
+
+    // Messages
+    uint8_t msg1[32];
+    uint8_t msg2[48];
+    uint8_t msg3[48];
+} test_vectors_xk1;
+
+static void vectors_xk1_fill(test_vectors_xk1 *v,
+                             const uint8_t client_sk  [32],
+                             const uint8_t server_sk  [32],
+                             const uint8_t client_seed[32],
+                             const uint8_t server_seed[32])
+{
+    // Private keys
+    copy(v->is, client_sk  );
+    copy(v->ie, client_seed);
+    copy(v->rs, server_sk  );
+    copy(v->re, server_seed);
+
+    // Public keys
+    crypto_x25519_public_key(v->IS, v->is);
+    crypto_x25519_public_key(v->IE, v->ie);
+    crypto_x25519_public_key(v->RS, v->rs);
+    crypto_x25519_public_key(v->RE, v->re);
+
+    // Shared secrets
+    crypto_x25519(v->ee, v->ie, v->RE);
+    crypto_x25519(v->es, v->ie, v->RS);
+    crypto_x25519(v->se, v->is, v->RE);
+
+    // Keys
+    uint8_t tmp1[32];
+    uint8_t tmp2[32];
+    uint8_t CK0 [32] = "Monokex XK1";
+    crypto_chacha20_H(tmp1, v->ee , zero);
+    crypto_chacha20_H(tmp2, CK0   , one );
+    xor(v->CK1, tmp1, tmp2);
+    crypto_chacha20_H(tmp1, v->es , zero);
+    crypto_chacha20_H(tmp2, v->CK1, one );
+    xor(v->CK2, tmp1, tmp2);
+    crypto_chacha20_H(tmp1, v->se , zero);
+    crypto_chacha20_H(tmp2, v->CK2, one );
+    xor(v->CK3, tmp1, tmp2);
+    uint8_t tmp[64];
+    chacha_block(tmp, v->CK2, one);
+    copy(v->AK2, tmp     );
+    copy(v->EK2, tmp + 32);
+    chacha_block(tmp, v->CK3, one);
+    copy(v->AK3, tmp     );
+    copy(v->EK3, tmp + 32);
+
+    // Messages
+    crypto_poly1305_ctx ctx;
+    uint8_t XIS[32];
+    xor(XIS, v->IS, v->EK2);
+    copy(v->msg1, v->IE);
+    copy(v->msg2, v->RE);
+    crypto_poly1305_init  (&ctx, v->AK2);
+    crypto_poly1305_update(&ctx, v->RS, 32);
+    crypto_poly1305_update(&ctx, v->IE, 32);
+    crypto_poly1305_update(&ctx, v->RE, 32);
+    crypto_poly1305_final (&ctx, v->msg2 + 32);
+    copy(v->msg3, XIS);
+    crypto_poly1305_init  (&ctx, v->AK3);
+    crypto_poly1305_update(&ctx, v->RS, 32);
+    crypto_poly1305_update(&ctx, v->IE, 32);
+    crypto_poly1305_update(&ctx, v->RE, 32);
+    crypto_poly1305_update(&ctx, XIS  , 32);
+    crypto_poly1305_final (&ctx, v->msg3 + 32);
+}
+
+int main(void)
+{
+    FOR(i, 0, 5) {
+        RANDOM_INPUT(client_sk  , 32);
+        RANDOM_INPUT(server_sk  , 32);
+        RANDOM_INPUT(client_seed, 32);
+        RANDOM_INPUT(server_seed, 32);
+        test_vectors_xk1 v;
+        vectors_xk1_fill(&v, client_sk, server_sk, client_seed, server_seed);
+
+        print_vector(v.is  , 32);
+        print_vector(v.ie  , 32);
+        print_vector(v.rs  , 32);
+        print_vector(v.re  , 32);
+        print_vector(v.IS  , 32);
+        print_vector(v.RS  , 32);
+        print_vector(v.msg1, 32);
+        print_vector(v.msg2, 48);
+        print_vector(v.msg3, 48);
+        print_vector(v.EK3 , 32);
+    }
+    return 0;
+}
index fbdf7d0d1f56e50e161c71559c6630a0eb2377c5..7f804d216fe09e39a676e8ee40b0aad36bb01159 100644 (file)
@@ -126,17 +126,220 @@ static u64 edDSA_check(void)
     TIMING_END;
 }
 
+static void get_interactive_session(u8 msg1[32], u8 msg2[48], u8 msg3[48],
+                                    u8 client_pk[32], u8 server_pk[32],
+                                    const u8 client_sk  [32],
+                                    const u8 server_sk  [32],
+                                    const u8 client_seed[32],
+                                    const u8 server_seed[32])
+{
+    crypto_key_exchange_public_key(client_pk, client_sk);
+    crypto_key_exchange_public_key(server_pk, server_sk);
+
+    u8 c_seed[32];
+    u8 s_seed[32];
+    FOR (i, 0, 32) {
+        c_seed[i] = client_seed[i];
+        s_seed[i] = server_seed[i];
+    }
+    crypto_kex_ctx client_ctx;
+    crypto_kex_xk1_init_client(&client_ctx, c_seed, client_sk, client_pk,
+                               server_pk);
+    crypto_kex_ctx server_ctx;
+    crypto_kex_xk1_init_server(&server_ctx, s_seed, server_sk, server_pk);
+
+    crypto_kex_xk1_1(&client_ctx, msg1);
+    crypto_kex_xk1_2(&server_ctx, msg2, msg1);
+
+    u8 client_session_key[32];
+    if (crypto_kex_xk1_3(&client_ctx, client_session_key,
+                         msg3, msg2)) {
+        fprintf(stderr, "Cannot confirm\n");
+        return;
+    }
+
+    u8 server_session_key[32];
+    u8 remote_pk         [32]; // same as client_pk
+    if (crypto_kex_xk1_4(&server_ctx, server_session_key, remote_pk,
+                         msg3)) {
+        fprintf(stderr, "Cannot accept\n");
+        return;
+    }
+
+    if (crypto_verify32(client_session_key, server_session_key)) {
+        fprintf(stderr, "Different session keys\n");
+        return;
+    }
+    if (crypto_verify32(remote_pk, client_pk)) {
+        fprintf(stderr, "Server got the wrong client public key\n");
+        return;
+    }
+}
+
+
+static u64 interactive_client(void)
+{
+    RANDOM_INPUT(client_sk, 32);
+    RANDOM_INPUT(server_sk, 32);
+    RANDOM_INPUT(client_seed, 32);
+    RANDOM_INPUT(server_seed, 32);
+    u8 msg1[32]; u8 msg2[48]; u8 msg3[48];
+    u8 client_pk[32]; u8 server_pk[32];
+    get_interactive_session(msg1, msg2, msg3,
+                            client_pk  , server_pk,
+                            client_sk  , server_sk,
+                            client_seed, server_seed);
+    TIMING_START {
+        u8 session_key[32];
+        crypto_kex_ctx client_ctx;
+        u8 seed[32];
+        FOR (i, 0, 32) {
+            seed[i] = client_seed[i];
+        }
+        crypto_kex_xk1_init_client(&client_ctx, seed, client_sk, client_pk,
+                                   server_pk);
+        crypto_kex_xk1_1(&client_ctx, msg1);
+        if (crypto_kex_xk1_3(&client_ctx, session_key,
+                             msg3, msg2)) {
+            fprintf(stderr, "Cannot confirm\n");
+            return 1;
+        }
+    }
+    TIMING_END;
+}
+
+static u64 interactive_server(void)
+{
+    RANDOM_INPUT(client_sk, 32);
+    RANDOM_INPUT(server_sk, 32);
+    RANDOM_INPUT(client_seed, 32);
+    RANDOM_INPUT(server_seed, 32);
+    u8 msg1[32]; u8 msg2[48]; u8 msg3[48];
+    u8 client_pk[32]; u8 server_pk[32];
+    get_interactive_session(msg1, msg2, msg3,
+                            client_pk  , server_pk,
+                            client_sk  , server_sk,
+                            client_seed, server_seed);
+    TIMING_START {
+        u8 session_key[32];
+        u8 remote_pk         [32]; // same as client_pk
+        crypto_kex_ctx server_ctx;
+        u8 seed[32];
+        FOR (i, 0, 32) {
+            seed[i] = server_seed[i];
+        }
+        crypto_kex_xk1_init_server(&server_ctx, seed, server_sk, server_pk);
+        crypto_kex_xk1_2(&server_ctx, msg2, msg1);
+        if (crypto_kex_xk1_4(&server_ctx, session_key, remote_pk,
+                             msg3)) {
+            fprintf(stderr, "Cannot accept\n");
+            return 1;
+        }
+    }
+    TIMING_END;
+}
+
+static void get_one_way_session(u8 msg[80], u8 client_pk[32], u8 server_pk[32],
+                                const u8 client_sk  [32],
+                                const u8 server_sk  [32],
+                                const u8 client_seed[32])
+{
+    crypto_key_exchange_public_key(client_pk, client_sk);
+    crypto_key_exchange_public_key(server_pk, server_sk);
+
+    u8 c_seed[32];
+    FOR (i, 0, 32) {
+        c_seed[i] = client_seed[i];
+    }
+
+    crypto_kex_ctx client_ctx;
+    crypto_kex_x_init_client(&client_ctx, c_seed, client_sk, client_pk,
+                             server_pk);
+    crypto_kex_ctx server_ctx;
+    crypto_kex_x_init_server(&server_ctx, server_sk, server_pk);
+
+    u8 client_session_key[32];
+    crypto_kex_x_1(&client_ctx, client_session_key, msg);
+
+    u8 server_session_key[32];
+    u8 remote_pk         [32]; // same as client_pk
+    if (crypto_kex_x_2(&server_ctx, server_session_key, remote_pk, msg)) {
+        fprintf(stderr, "Cannot receive\n");
+        return;
+    }
+
+    if (crypto_verify32(client_session_key, server_session_key)) {
+        fprintf(stderr, "Different session keys\n");
+        return;
+    }
+    if (crypto_verify32(remote_pk, client_pk)) {
+        fprintf(stderr, "Server got the wrong client public key\n");
+        return;
+    }
+}
+
+static u64 one_way_client(void)
+{
+    RANDOM_INPUT(client_sk, 32);
+    RANDOM_INPUT(server_sk, 32);
+    RANDOM_INPUT(client_seed, 32);
+    u8 msg[80]; u8 client_pk[32]; u8 server_pk[32];
+    get_one_way_session(msg,
+                        client_pk, server_pk,
+                        client_sk, server_sk,
+                        client_seed);
+    TIMING_START {
+        u8 session_key[32];
+        u8 seed[32];
+        FOR (i, 0, 32) {
+            seed[i] = client_seed[i];
+        }
+        crypto_kex_ctx client_ctx;
+        crypto_kex_x_init_client(&client_ctx, seed, client_sk, client_pk,
+                                 server_pk);
+        crypto_kex_x_1(&client_ctx, session_key, msg);
+    }
+    TIMING_END;
+}
+
+static u64 one_way_server(void)
+{
+    RANDOM_INPUT(client_sk, 32);
+    RANDOM_INPUT(server_sk, 32);
+    RANDOM_INPUT(client_seed, 32);
+    u8 msg[80]; u8 client_pk[32]; u8 server_pk[32];
+    get_one_way_session(msg,
+                        client_pk, server_pk,
+                        client_sk, server_sk,
+                        client_seed);
+    TIMING_START {
+        u8 session_key[32];
+        u8 remote_pk         [32]; // same as client_pk
+        crypto_kex_ctx server_ctx;
+        crypto_kex_x_init_server(&server_ctx, server_sk, server_pk);
+        if (crypto_kex_x_2(&server_ctx, session_key, remote_pk, msg)) {
+            fprintf(stderr, "Cannot receive\n");
+            return 1;
+        }
+    }
+    TIMING_END;
+}
+
 int main()
 {
-    print("Chacha20         ",chacha20()     *MUL,"megabytes  per second");
-    print("Poly1305         ",poly1305()     *MUL,"megabytes  per second");
-    print("Auth'd encryption",authenticated()*MUL,"megabytes  per second");
-    print("Blake2b          ",blake2b()      *MUL,"megabytes  per second");
-    print("Sha512           ",sha512()       *MUL,"megabytes  per second");
-    print("Argon2i, 3 passes",argon2i()      *MUL,"megabytes  per second");
-    print("x25519           ",x25519()           ,"exchanges  per second");
-    print("EdDSA(sign)      ",edDSA_sign()       ,"signatures per second");
-    print("EdDSA(check)     ",edDSA_check()      ,"checks     per second");
+    print("Chacha20            ",chacha20()     *MUL ,"megabytes  per second");
+    print("Poly1305            ",poly1305()     *MUL ,"megabytes  per second");
+    print("Auth'd encryption   ",authenticated()*MUL ,"megabytes  per second");
+    print("Blake2b             ",blake2b()      *MUL ,"megabytes  per second");
+    print("Sha512              ",sha512()       *MUL ,"megabytes  per second");
+    print("Argon2i, 3 passes   ",argon2i()      *MUL ,"megabytes  per second");
+    print("x25519              ",x25519()            ,"exchanges  per second");
+    print("EdDSA(sign)         ",edDSA_sign()        ,"signatures per second");
+    print("EdDSA(check)        ",edDSA_check()       ,"checks     per second");
+    print("Monokex XK1 (client)",interactive_client(),"handshakes per second");
+    print("Monokex XK1 (server)",interactive_server(),"handshakes per second");
+    print("Monokex X   (client)",one_way_client()    ,"handshakes per second");
+    print("Monokex X   (server)",one_way_server()    ,"handshakes per second");
     printf("\n");
     return 0;
 }
index ac3014b006d48da6dc562a6c92feed9ea59e69da..d312e0442cd6b9a09f941b402d4493abff1deff2 100644 (file)
@@ -229,6 +229,75 @@ static void ed_25519_check(const vector in[], vector *out)
 }
 #endif
 
+static void monokex_xk1(const vector in[], vector *out)
+{
+    const vector *is   = in;
+    const vector *ie   = in + 1;
+    const vector *rs   = in + 2;
+    const vector *re   = in + 3;
+    const vector *IS   = in + 4;
+    const vector *RS   = in + 5;
+    const vector *msg1 = in + 6;
+    const vector *msg2 = in + 7;
+    const vector *msg3 = in + 8;
+    crypto_kex_ctx client;
+    crypto_kex_ctx server;
+    u8 client_seed[32];  memcpy(client_seed, ie->buf, ie->size);
+    u8 server_seed[32];  memcpy(server_seed, re->buf, re->size);
+    crypto_kex_xk1_init_client(&client, client_seed, is->buf, IS->buf, RS->buf);
+    crypto_kex_xk1_init_server(&server, server_seed, rs->buf, RS->buf);
+    u8 m1        [32];
+    u8 m2        [48];
+    u8 m3        [48];
+    u8 client_key[32];
+    u8 remote_pk [32];
+    crypto_kex_xk1_1(&client, m1);
+    crypto_kex_xk1_2(&server, m2, m1);
+    if (crypto_kex_xk1_3(&client, client_key, m3, m2)) {
+        fprintf(stderr, "FAILURE: crypto_kex_xk1_3\n");
+        return;
+    }
+    if (crypto_kex_xk1_4(&server, out->buf, remote_pk, m3)) {
+        fprintf(stderr, "FAILURE: crypto_kex_xk1_4\n");
+        return;
+    }
+#define COMPARE(local, vector)                      \
+    if (memcmp(local, vector->buf, vector->size)) { \
+        fprintf(stderr, "FAILURE: "#vector"\n");    \
+        return;                                     \
+    }
+    COMPARE(m1        , msg1);
+    COMPARE(m2        , msg2);
+    COMPARE(m3        , msg3);
+    COMPARE(remote_pk , IS  );
+    COMPARE(client_key, out );
+}
+
+static void monokex_x(const vector in[], vector *out)
+{
+    const vector *is   = in;
+    const vector *ie   = in + 1;
+    const vector *rs   = in + 2;
+    const vector *IS   = in + 3;
+    const vector *RS   = in + 4;
+    const vector *msg1 = in + 5;
+    crypto_kex_ctx client;
+    crypto_kex_ctx server;
+    u8 seed[32];  memcpy(seed, ie->buf, ie->size);
+    crypto_kex_x_init_client(&client, seed, is->buf, IS->buf, RS->buf);
+    crypto_kex_x_init_server(&server,       rs->buf, RS->buf);
+    u8 m1        [80];
+    u8 client_key[32];
+    u8 remote_pk [32];
+    crypto_kex_x_1(&client, client_key, m1);
+    if (crypto_kex_x_2(&server, out->buf, remote_pk, m1)) {
+        fprintf(stderr, "FAILURE: crypto_kex_x_2\n");
+        return;
+    }
+    COMPARE(m1        , msg1);
+    COMPARE(remote_pk , IS  );
+    COMPARE(client_key, out );
+}
 static void iterate_x25519(u8 k[32], u8 u[32])
 {
     u8 tmp[32];
@@ -855,6 +924,98 @@ static int p_auth()
     return status;
 }
 
+static int p_monokex_xk1()
+{
+    int status = 0;
+    RANDOM_INPUT(is, 32);
+    RANDOM_INPUT(ie, 32);
+    RANDOM_INPUT(rs, 32);
+    RANDOM_INPUT(re, 32);
+    u8 IS[32];  crypto_x25519_public_key(IS, is);
+    u8 RS[32];  crypto_x25519_public_key(RS, rs);
+
+    crypto_kex_ctx client1, client2;
+    crypto_kex_ctx server1, server2;
+    u8 client_seed1[32];  memcpy(client_seed1, ie, 32);
+    u8 client_seed2[32];  memcpy(client_seed2, ie, 32);
+    u8 server_seed1[32];  memcpy(server_seed1, re, 32);
+    u8 server_seed2[32];  memcpy(server_seed2, re, 32);
+
+    // Test the same thing, with and without the local pk
+    // (the API is supposed to reconstruct it)
+    crypto_kex_xk1_init_client(&client1, client_seed1, is, IS, RS);
+    crypto_kex_xk1_init_client(&client2, client_seed2, is,  0, RS);
+    crypto_kex_xk1_init_server(&server1, server_seed1, rs, RS);
+    crypto_kex_xk1_init_server(&server2, server_seed2, rs,  0);
+    u8 msg11      [32];    u8 msg12      [32];
+    u8 msg21      [48];    u8 msg22      [48];
+    u8 msg31      [48];    u8 msg32      [48];
+    u8 client_key1[32];    u8 client_key2[32];
+    u8 server_key1[32];    u8 server_key2[32];
+    u8 remote_pk1 [32];    u8 remote_pk2 [32];
+    crypto_kex_xk1_1(&client1, msg11);
+    crypto_kex_xk1_1(&client2, msg12);
+    crypto_kex_xk1_2(&server1, msg21, msg11);
+    crypto_kex_xk1_2(&server2, msg22, msg12);
+    // make sure everything is accepted as it should be
+    status |= crypto_kex_xk1_3(&client1, client_key1, msg31, msg21);
+    status |= crypto_kex_xk1_3(&client2, client_key2, msg32, msg22);
+    status |= crypto_kex_xk1_4(&server1, server_key1, remote_pk1, msg31);
+    status |= crypto_kex_xk1_4(&server2, server_key2, remote_pk2, msg32);
+    // Make sure we get the same result whether we gave the local pk or not
+    status |= memcmp(msg11      , msg12      , 32);
+    status |= memcmp(msg21      , msg22      , 48);
+    status |= memcmp(msg31      , msg32      , 48);
+    status |= memcmp(client_key1, client_key2, 32);
+    status |= memcmp(remote_pk1 , remote_pk2 , 32);
+    // make sure wrong messages are rejected as they should be.
+    msg21[1]++;
+    status |= !crypto_kex_xk1_3(&client1, client_key1, msg31, msg21);
+    msg32[1]++;
+    status |= !crypto_kex_xk1_4(&server2, server_key2, remote_pk2, msg32);
+
+    printf("%s: monokex_xk1\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
+static int p_monokex_x()
+{
+    int status = 0;
+    RANDOM_INPUT(is, 32);
+    RANDOM_INPUT(ie, 32);
+    RANDOM_INPUT(rs, 32);
+    u8 IS[32];  crypto_x25519_public_key(IS, is);
+    u8 RS[32];  crypto_x25519_public_key(RS, rs);
+
+    crypto_kex_ctx client1, client2;
+    crypto_kex_ctx server1, server2;
+    u8 seed1[32];  memcpy(seed1, ie, 32);
+    u8 seed2[32];  memcpy(seed2, ie, 32);
+    crypto_kex_x_init_client(&client1, seed1, is, IS, RS);
+    crypto_kex_x_init_client(&client2, seed2, is,  0, RS);
+    crypto_kex_x_init_server(&server1,        rs, RS);
+    crypto_kex_x_init_server(&server2,        rs,  0);
+    u8 msg11      [80];    u8 msg12      [80];
+    u8 client_key1[32];    u8 client_key2[32];
+    u8 server_key1[32];    u8 server_key2[32];
+    u8 remote_pk1 [32];    u8 remote_pk2 [32];
+    crypto_kex_x_1(&client1, client_key1, msg11);
+    crypto_kex_x_1(&client2, client_key2, msg12);
+    // make sure everything is accepted as it should be
+    status |= crypto_kex_x_2(&server1, server_key1, remote_pk1, msg11);
+    status |= crypto_kex_x_2(&server2, server_key2, remote_pk2, msg12);
+    // Make sure we get the same result whether we gave the local pk or not
+    status |= memcmp(msg11      , msg12      , 80);
+    status |= memcmp(client_key1, client_key2, 32);
+    status |= memcmp(remote_pk1 , remote_pk2 , 32);
+    // make sure wrong messages are rejected as they should be.
+    msg11[1]++;
+    status |= !crypto_kex_x_2(&server1, server_key1, remote_pk1, msg11);
+
+    printf("%s: monokex_x\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
 int main(int argc, char *argv[])
 {
     if (argc > 1) {
@@ -883,6 +1044,8 @@ int main(int argc, char *argv[])
     status |= TEST(edDSA         , 3);
     status |= TEST(edDSA_pk      , 1);
 #endif
+    status |= TEST(monokex_xk1   , 9);
+    status |= TEST(monokex_x     , 6);
     status |= test_x25519();
 
     printf("\nProperty based tests");
@@ -909,7 +1072,8 @@ int main(int argc, char *argv[])
     status |= p_aead();
     status |= p_lock_incremental();
     status |= p_auth();
-
+    status |= p_monokex_xk1();
+    status |= p_monokex_x();
     printf("\n%s\n\n", status != 0 ? "SOME TESTS FAILED" : "All tests OK!");
     return status;
 }