#! /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
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef LOCK_H
+#define LOCK_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+// 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
#include "poly1305.h"
#include "argon2i.h"
#include "ae.h"
+#include "x25519.h"
/////////////////////////
/// General utilities ///
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);
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,
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;
--- /dev/null
+scalar: a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4
+point: e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
+out: c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552
+
+scalar: 4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d
+point: e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493
+out: 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957
+
+scalar: 77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
+point: de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f
+out: 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742
+
+scalar: 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
+point: 8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a
+out: 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef X25519_H
+#define X25519_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+// 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