From: Loup Vaillant Date: Thu, 19 Jan 2017 15:33:12 +0000 (+0100) Subject: added x25519 and crypto_lock X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=6941aebc68c49048b42d25a6228723c722f2de7f;p=Monocypher.git added x25519 and crypto_lock --- diff --git a/build.sh b/build.sh index 6d4096a..c2db67d 100755 --- a/build.sh +++ b/build.sh @@ -1,14 +1,16 @@ #! /bin/bash CC="gcc" -CFLAGS="-O2 -Wall -Wextra -std=c11 -g" +CFLAGS="-O2 -Wall -Wextra -std=c11" $CC $CFLAGS -c chacha20.c $CC $CFLAGS -c blake2b.c $CC $CFLAGS -c poly1305.c $CC $CFLAGS -c argon2i.c $CC $CFLAGS -c ae.c +$CC $CFLAGS -c x25519.c +$CC $CFLAGS -c lock.c $CC $CFLAGS -c test.c -$CC $CFLAGS -o test test.o chacha20.o argon2i.o blake2b.o poly1305.o ae.o +$CC $CFLAGS -o test test.o chacha20.o argon2i.o blake2b.o poly1305.o x25519.o ae.o lock.o $CC $CFLAGS -o speed_blake2b speed_blake2b.c blake2b.o diff --git a/lock.c b/lock.c new file mode 100644 index 0000000..64220ae --- /dev/null +++ b/lock.c @@ -0,0 +1,64 @@ +#include "lock.h" +#include "x25519.h" +#include "chacha20.h" +#include "ae.h" + +void crypto_lock_key(uint8_t shared_key[32], + const uint8_t your_secret_key [32], + const uint8_t their_public_key[32]) +{ + static const uint8_t _0[16]; + uint8_t shared_secret[32]; + crypto_x25519(shared_secret, your_secret_key, their_public_key); + crypto_chacha20_H(shared_key, shared_secret, _0); +} + +void crypto_lock_detached(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t text_size, + uint8_t mac[16]) +{ + uint8_t shared_key[32]; + crypto_lock_key(shared_key, your_secret_key, their_public_key); + crypto_ae_lock_detached(shared_key, nonce, plaintext, ciphertext, + text_size, mac); +} + +int crypto_unlock_detached(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t text_size, + const uint8_t mac[16]) +{ + uint8_t shared_key[32]; + crypto_lock_key(shared_key, your_secret_key, their_public_key); + return crypto_ae_unlock_detached(shared_key, nonce, ciphertext, plaintext, + text_size, mac); +} + +void crypto_lock(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + size_t text_size, + uint8_t *box) +{ + crypto_lock_detached(your_secret_key, their_public_key, nonce, + plaintext, box + 16, text_size, box); +} + +int crypto_unlock(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *box, + size_t text_size, + uint8_t *plaintext) +{ + return crypto_unlock_detached(your_secret_key, their_public_key, nonce, + box + 16, plaintext, text_size, box); +} diff --git a/lock.h b/lock.h new file mode 100644 index 0000000..ae47eca --- /dev/null +++ b/lock.h @@ -0,0 +1,50 @@ +#ifndef LOCK_H +#define LOCK_H + +#include +#include + +// Computes a shared key with your secret key and their public key, +// suitable for crypto_ae* functions. +void crypto_lock_key(uint8_t shared_key [32], + const uint8_t your_secret_key [32], + const uint8_t their_public_key[32]); + +// Authenticated encryption with the sender's secret key and the recipient's +// publick key. The message leaks if one of the secret key gets compromised. +void crypto_lock_detached(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t text_size, + uint8_t mac[16]); + +// Authenticated decryption with the recipient's secret key, and the sender's +// public key. Has no effect if the message is forged. +int crypto_unlock_detached(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t text_size, + const uint8_t mac[16]); + +// Like the above, only puts the mac and the ciphertext together +// in a "box", mac first +void crypto_lock(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + size_t text_size, + uint8_t *box); + +// Unlocks a box locked by crypto_lock() +int crypto_unlock(const uint8_t your_secret_key [32], + const uint8_t their_public_key[32], + const uint8_t nonce[24], + const uint8_t *box, + size_t text_size, + uint8_t *plaintext); + +#endif // LOCK_H diff --git a/test.c b/test.c index ff961c1..8dcea70 100644 --- a/test.c +++ b/test.c @@ -8,6 +8,7 @@ #include "poly1305.h" #include "argon2i.h" #include "ae.h" +#include "x25519.h" ///////////////////////// /// General utilities /// @@ -68,6 +69,14 @@ static vector vec_uninitialized(size_t size) return v; } +/* static vector vec_copy(const vector *v) */ +/* { */ +/* vector w = *v; */ +/* w.buffer = alloc(w.buf_size); */ +/* memcpy(w.buffer, v->buffer, w.buf_size); */ +/* return w; */ +/* } */ + static void vec_del(vector *v) { free(v->buffer); @@ -209,6 +218,54 @@ static void argon2i(const vector in[], vector *out) free(work_area); } +static void x25519(const vector in[], vector *out) +{ + const vector *scalar = in; + const vector *point = in + 1; + crypto_x25519(out->buffer, scalar->buffer, point->buffer); +} + +// Disabling the following test, because it takes too damn long +// I suggest you run it once, though. +/* +static void iterate_x25519(uint8_t k[32], uint8_t u[32]) +{ + uint8_t tmp[32]; + crypto_x25519(tmp , k, u); + memcpy(u, k , 32); + memcpy(k, tmp, 32); +} + +static int test_x25519() +{ + uint8_t _1 [32] = {0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, + 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, + 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, + 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79}; + uint8_t _1k [32] = {0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, + 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, + 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, + 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51}; + uint8_t _100k[32] = {0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, + 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, + 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, + 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24}; + uint8_t k[32] = {9}; + uint8_t u[32] = {9}; + + iterate_x25519(k, u); + int status = memcmp(k, _1, 32); + for (int i = 1; i < 1000; i++) + iterate_x25519(k, u); + status |= memcmp(k, _1k, 32); + for (int i = 1000; i < 1000000; i++) + iterate_x25519(k, u); + status |= memcmp(k, _100k, 32); + + printf("%s: x25519\n", status != 0 ? "FAILED" : "OK"); + return status; +} +*/ static int test_ae() { uint8_t key[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, @@ -237,6 +294,8 @@ int main(void) status |= test(blake2b , "vectors_blake2b.txt" , 2); status |= test(poly1305, "vectors_poly1305.txt", 2); status |= test(argon2i , "vectors_argon2i.txt" , 6); + status |= test(x25519 , "vectors_x25519.txt" , 2); +// status |= test_x25519(); // Too Long; Didn't Run status |= test_ae(); printf(status ? "TESTS FAILED\n" : "ALL TESTS OK\n"); return status; diff --git a/vectors_x25519.txt b/vectors_x25519.txt new file mode 100644 index 0000000..eb5b79d --- /dev/null +++ b/vectors_x25519.txt @@ -0,0 +1,15 @@ +scalar: a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4 +point: e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c +out: c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552 + +scalar: 4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d +point: e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493 +out: 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957 + +scalar: 77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a +point: de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f +out: 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742 + +scalar: 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb +point: 8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a +out: 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742 diff --git a/x25519.c b/x25519.c new file mode 100644 index 0000000..d4421a7 --- /dev/null +++ b/x25519.c @@ -0,0 +1,195 @@ +#include "x25519.h" + +#define FOR(i, start, end) for (size_t i = start; i < end; i++) +#define sv static void +typedef int64_t gf[16]; + +static const uint8_t _0[16]; +static const uint8_t _9[32] = { 9 }; +static const gf _121665 = { 0xDB41, 1 }; + +/* static int vn(const uint8_t *x, const uint8_t *y, size_t n) */ +/* { */ +/* uint32_t d = 0; */ +/* FOR(i, 0, n) d |= x[i] ^ y[i]; */ +/* return (1 & ((d - 1) >> 8)) - 1; */ +/* } */ + + +// needed for signatures (not here) +/* sv set_25519(gf r, const gf a) */ +/* { */ +/* FOR(i, 0, 16) r[i] = a[i]; */ +/* } */ + +sv car_25519(gf o) +{ + FOR(i, 0, 16) { + o[i] += 1LL << 16; + int64_t c = o[i] >> 16; + o[(i+1) * (i<15)] += c - 1 + (37 * (c-1) * (i==15)); + o[i] -= c << 16; + } +} + +sv sel_25519(gf p, gf q, int b) +{ + int64_t c = ~(b-1); + FOR(i, 0, 16) { + int64_t t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} + +sv pack_25519(uint8_t *o, const gf n) +{ + gf t; + FOR(i, 0, 16) t[i] = n[i]; + car_25519(t); + car_25519(t); + car_25519(t); + FOR(j, 0, 2) { + gf m; + m[0] = t[0] - 0xffed; + FOR(i, 1, 15) { + m[i ] = t[i] - 0xffff - ((m[i-1] >> 16) & 1); + m[i-1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + int b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + sel_25519(t, m, 1-b); + } + FOR(i, 0, 16) { + o[2*i ] = t[i] & 0xff; + o[2*i + 1] = t[i] >> 8; + } +} + +// needed for signatures (not here) +/* static int neq_25519(const gf a, const gf b) */ +/* { */ +/* uint8_t c[32],d[32]; */ +/* pack_25519(c, a); */ +/* pack_25519(d, b); */ +/* return vn(c, d, 32); */ +/* } */ +// needed for signatures (not here) +/* static uint8_t par_25519(const gf a) */ +/* { */ +/* uint8_t d[32]; */ +/* pack_25519(d, a); */ +/* return d[0] & 1; */ +/* } */ + +sv unpack_25519(gf o, const uint8_t *n) +{ + FOR(i, 0, 16) o[i] = n[2*i] + ((int64_t)n[2*i + 1] << 8); + o[15] &= 0x7fff; +} + +sv A(gf o, const gf a, const gf b) +{ + FOR(i, 0, 16) o[i] = a[i] + b[i]; +} + +sv Z(gf o, const gf a, const gf b) +{ + FOR(i, 0, 16) o[i] = a[i] - b[i]; +} + +sv M(gf o, const gf a, const gf b) +{ + int64_t t[31]; + FOR(i, 0, 31) t[i] = 0; + FOR(i, 0, 16) FOR(j, 0, 16) t[i+j] += a[i] * b[j]; + FOR(i, 0, 15) t[i] += 38 * t[i+16]; + FOR(i, 0, 16) o[i] = t[i]; + car_25519(o); + car_25519(o); +} + +sv S(gf o,const gf a) +{ + M(o, a, a); +} + +sv inv_25519(gf o,const gf i) +{ + gf c; + FOR(a, 0, 16) c[a] = i[a]; + for(int a = 253; a >= 0; a--) { + S(c, c); + if(a != 2 && a != 4) + M(c, c, i); + } + FOR(a, 0, 16) o[a] = c[a]; +} +// needed for signatures (not here) +/* sv pow2523(gf o,const gf i) */ +/* { */ +/* gf c; */ +/* FOR(a, 0, 16) c[a] = i[a]; */ +/* for(int a = 250; a >= 0; a--) { */ +/* S(c, c); */ +/* if(a != 1) M(c, c, i); */ +/* } */ +/* FOR(a, 0, 16) o[a] = c[a]; */ +/* } */ + +void crypto_x25519(uint8_t q[32], const uint8_t n[32], const uint8_t p[32]) +{ + uint8_t z[32]; + int64_t x[80]; + int64_t r; + gf a, b, c, d, e, f; + FOR(i, 0, 31) z[i] = n[i]; + z[31] = (n[31] & 127) | 64; + z[0 ] &= 248; + unpack_25519(x, p); + FOR(i, 0, 16) { + b[i] = x[i]; + d[i] = a[i] = c[i] = 0; + } + a[0] = d[0] = 1; + for(int i = 254; i>=0; i--) { + r = (z[i>>3] >> (i & 7)) & 1; + sel_25519(a, b, r); + sel_25519(c, d, r); + A(e, a, c); + Z(a, a, c); + A(c, b, d); + Z(b, b, d); + S(d, e); + S(f, a); + M(a, c, a); + M(c, b, e); + A(e, a, c); + Z(a, a, c); + S(b, a); + Z(c, d, f); + M(a, c, _121665); + A(a, a, d); + M(c, c, a); + M(a, d, f); + M(d, b, x); + S(b, e); + sel_25519(a, b, r); + sel_25519(c, d, r); + } + FOR(i, 0, 16) { + x[i+16] = a[i]; + x[i+32] = c[i]; + x[i+48] = b[i]; + x[i+64] = d[i]; + } + inv_25519(x+32, x+32); + M(x+16, x+16, x+32); + pack_25519(q, x+16); +} + +void crypto_x25519_base(uint8_t q[32], const uint8_t n[32]) +{ + crypto_x25519(q, n, _9); +} diff --git a/x25519.h b/x25519.h new file mode 100644 index 0000000..1c5d2ac --- /dev/null +++ b/x25519.h @@ -0,0 +1,34 @@ +#ifndef X25519_H +#define X25519_H + +#include +#include + +// Computes a shared secret from your private key and their public key. +// WARNING: DO NOT USE THE SHARED SECRET DIRECTLY. +// The shared secret is not pseudo-random. You need to hash it to derive +// an acceptable secret key. Any cryptographic hash can work, as well as +// HChacha20. +// +// Implementation details: this is an elliptic curve. The public key is +// a point on this curve, and your private key is a scalar. The shared +// secret is another point on this curve, obtained by scalar multiplication. +// Basically: +// shared_secret == your_sk * their_pk == your_sk * (their_sk * base_point) +// == their_sk * your_pk == their_sk * (your_sk * base_point) +void crypto_x25519(uint8_t shared_secret [32], + const uint8_t your_secret_key [32], + const uint8_t their_public_key[32]); + +// Generates a public key from the specified secret key. +// Make sure the secret key is randomly selected. +// +// Implementation detail: your secret key is a scalar, and we multiply +// the base point (a constant) by it to obtain a public key. That is: +// public_key == secret_key * base_point +// Reversing the operation is conjectured to be infeasible +// without quantum computers (128 bits of security). +void crypto_x25519_base(uint8_t public_key[32], const uint8_t secret_key[32]); + + +#endif // X25519_H