From 03aafb2c9ea38c9baf9bc0672001ffe38c91c47d Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 14 Apr 2014 21:43:45 +0200 Subject: [PATCH] Use the ChaCha-Poly1305 cipher for the SPTPS protocol. The main reason to switch from AES-256-GCM to ChaCha-Poly1305 is to remove a dependency on OpenSSL, whose behaviour of the AES-256-GCM decryption function changes between versions. The source code for ChaCha-Pol1305 is small and in the public domain, and can therefore be easily included in tinc itself. Moreover, it is very fast even without using any optimized assembler, easily outperforming AES-256-GCM on platforms that don't have special AES instructions in hardware. Conflicts: src/Makefile.am --- src/Makefile.am | 17 +- src/chacha-poly1305/chacha-poly1305.c | 103 ++++++++++++ src/chacha-poly1305/chacha-poly1305.h | 15 ++ src/chacha-poly1305/chacha.c | 215 ++++++++++++++++++++++++++ src/chacha-poly1305/chacha.h | 24 +++ src/chacha-poly1305/poly1305.c | 197 +++++++++++++++++++++++ src/chacha-poly1305/poly1305.h | 16 ++ src/sptps.c | 84 ++++------ src/sptps.h | 9 +- 9 files changed, 612 insertions(+), 68 deletions(-) create mode 100644 src/chacha-poly1305/chacha-poly1305.c create mode 100644 src/chacha-poly1305/chacha-poly1305.h create mode 100644 src/chacha-poly1305/chacha.c create mode 100644 src/chacha-poly1305/chacha.h create mode 100644 src/chacha-poly1305/poly1305.c create mode 100644 src/chacha-poly1305/poly1305.h diff --git a/src/Makefile.am b/src/Makefile.am index 083b2728..bf2122d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,12 +23,18 @@ ed25519_SOURCES = \ ed25519/sign.c \ ed25519/verify.c +chacha_poly1305_SOURCES = \ + chacha-poly1305/chacha.c chacha-poly1305/chacha.h \ + chacha-poly1305/chacha-poly1305.c chacha-poly1305/chacha-poly1305.h \ + chacha-poly1305/poly1305.c chacha-poly1305/poly1305.h + sptps_test_SOURCES = \ logger.c logger.h \ sptps.c sptps.h \ sptps_test.c \ utils.c utils.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) sptps_keypair_SOURCES = \ sptps_keypair.c \ @@ -40,7 +46,8 @@ sptps_speed_SOURCES = \ sptps.c sptps.h \ sptps_speed.c \ utils.c utils.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) lib_LTLIBRARIES = libmeshlink.la @@ -86,14 +93,14 @@ libmeshlink_la_SOURCES = \ tincd.c \ utils.c utils.h \ xalloc.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) libmeshlink_la_CFLAGS = -fPIC libmeshlink_la_LIBADD = -lpthread libmeshlink_la_SOURCES += \ - openssl/cipher.c \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ openssl/ecdh.c \ @@ -101,7 +108,6 @@ libmeshlink_la_SOURCES += \ openssl/ecdsagen.c \ openssl/prf.c sptps_test_SOURCES += \ - openssl/cipher.c \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ ed25519/ecdh.c \ @@ -111,7 +117,6 @@ sptps_keypair_SOURCES += \ openssl/crypto.c \ ed25519/ecdsagen.c sptps_speed_SOURCES += \ - openssl/cipher.c \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ ed25519/ecdh.c \ diff --git a/src/chacha-poly1305/chacha-poly1305.c b/src/chacha-poly1305/chacha-poly1305.c new file mode 100644 index 00000000..bd5cb2c4 --- /dev/null +++ b/src/chacha-poly1305/chacha-poly1305.c @@ -0,0 +1,103 @@ +#include "../system.h" + +#include "../cipher.h" +#include "../xalloc.h" + +#include "chacha.h" +#include "chacha-poly1305.h" +#include "poly1305.h" + +struct chacha_poly1305_ctx { + struct chacha_ctx main_ctx, header_ctx; +}; + +chacha_poly1305_ctx_t *chacha_poly1305_init(void) +{ + chacha_poly1305_ctx_t *ctx = xzalloc(sizeof *ctx); + return ctx; +} + +void chacha_poly1305_exit(chacha_poly1305_ctx_t *ctx) +{ + free(ctx); +} + +bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key) +{ + chacha_keysetup(&ctx->main_ctx, key, 256); + chacha_keysetup(&ctx->header_ctx, key + 32, 256); + return true; +} + +static void put_u64(void *vp, uint64_t v) +{ + uint8_t *p = (uint8_t *) vp; + + p[0] = (uint8_t) (v >> 56) & 0xff; + p[1] = (uint8_t) (v >> 48) & 0xff; + p[2] = (uint8_t) (v >> 40) & 0xff; + p[3] = (uint8_t) (v >> 32) & 0xff; + p[4] = (uint8_t) (v >> 24) & 0xff; + p[5] = (uint8_t) (v >> 16) & 0xff; + p[6] = (uint8_t) (v >> 8) & 0xff; + p[7] = (uint8_t) v & 0xff; +} + +bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + uint8_t seqbuf[8]; + const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ + uint8_t poly_key[POLY1305_KEYLEN]; + + /* + * Run ChaCha20 once to generate the Poly1305 key. The IV is the + * packet sequence number. + */ + memset(poly_key, 0, sizeof(poly_key)); + put_u64(seqbuf, seqnr); + chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); + chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key)); + + /* Set Chacha's block counter to 1 */ + chacha_ivsetup(&ctx->main_ctx, seqbuf, one); + + chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen); + poly1305_auth(outdata + inlen, outdata, inlen, poly_key); + + if (outlen) + *outlen = inlen + POLY1305_TAGLEN; + + return true; +} + +bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + uint8_t seqbuf[8]; + const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ + uint8_t expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; + + /* + * Run ChaCha20 once to generate the Poly1305 key. The IV is the + * packet sequence number. + */ + memset(poly_key, 0, sizeof(poly_key)); + put_u64(seqbuf, seqnr); + chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); + chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key)); + + /* Set Chacha's block counter to 1 */ + chacha_ivsetup(&ctx->main_ctx, seqbuf, one); + + /* Check tag before anything else */ + inlen -= POLY1305_TAGLEN; + const uint8_t *tag = indata + inlen; + + poly1305_auth(expected_tag, indata, inlen, poly_key); + if (memcmp(expected_tag, tag, POLY1305_TAGLEN)) + return false; + + chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen); + + if (outlen) + *outlen = inlen; + + return true; +} diff --git a/src/chacha-poly1305/chacha-poly1305.h b/src/chacha-poly1305/chacha-poly1305.h new file mode 100644 index 00000000..af7eaf5e --- /dev/null +++ b/src/chacha-poly1305/chacha-poly1305.h @@ -0,0 +1,15 @@ +#ifndef CHACHA_POLY1305_H +#define CHACHA_POLY1305_H + +#define CHACHA_POLY1305_KEYLEN 64 + +typedef struct chacha_poly1305_ctx chacha_poly1305_ctx_t; + +extern chacha_poly1305_ctx_t *chacha_poly1305_init(void); +extern void chacha_poly1305_exit(chacha_poly1305_ctx_t *); +extern bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key); + +extern bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen); +extern bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen); + +#endif //CHACHA_POLY1305_H diff --git a/src/chacha-poly1305/chacha.c b/src/chacha-poly1305/chacha.c new file mode 100644 index 00000000..2d0b9183 --- /dev/null +++ b/src/chacha-poly1305/chacha.c @@ -0,0 +1,215 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include "../system.h" + +#include "chacha.h" + +typedef struct chacha_ctx chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((uint8_t)(v) & U8C(0xFF)) +#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((uint32_t)((p)[0]) ) | \ + ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[2]) << 16) | \ + ((uint32_t)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +void chacha_keysetup(chacha_ctx *x, const uint8_t *k, uint32_t kbits) +{ + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +void chacha_ivsetup(chacha_ctx *x, const uint8_t *iv, const uint8_t *counter) +{ + x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0); + x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4); + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +void +chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes) +{ + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + uint8_t *ctarget = NULL; + uint8_t tmp[64]; + uint32_t i; + + if (!bytes) + return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + x0 = XOR(x0, U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x4); + U32TO8_LITTLE(c + 20, x5); + U32TO8_LITTLE(c + 24, x6); + U32TO8_LITTLE(c + 28, x7); + U32TO8_LITTLE(c + 32, x8); + U32TO8_LITTLE(c + 36, x9); + U32TO8_LITTLE(c + 40, x10); + U32TO8_LITTLE(c + 44, x11); + U32TO8_LITTLE(c + 48, x12); + U32TO8_LITTLE(c + 52, x13); + U32TO8_LITTLE(c + 56, x14); + U32TO8_LITTLE(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} diff --git a/src/chacha-poly1305/chacha.h b/src/chacha-poly1305/chacha.h new file mode 100644 index 00000000..af1b9a40 --- /dev/null +++ b/src/chacha-poly1305/chacha.h @@ -0,0 +1,24 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#ifndef CHACHA_H +#define CHACHA_H + +struct chacha_ctx { + uint32_t input[16]; +}; + +#define CHACHA_MINKEYLEN 16 +#define CHACHA_NONCELEN 8 +#define CHACHA_CTRLEN 8 +#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) +#define CHACHA_BLOCKLEN 64 + +void chacha_keysetup(struct chacha_ctx *x, const uint8_t *k, uint32_t kbits); +void chacha_ivsetup(struct chacha_ctx *x, const uint8_t *iv, const uint8_t *ctr); +void chacha_encrypt_bytes(struct chacha_ctx *x, const uint8_t *m, uint8_t * c, uint32_t bytes); + +#endif /* CHACHA_H */ diff --git a/src/chacha-poly1305/poly1305.c b/src/chacha-poly1305/poly1305.c new file mode 100644 index 00000000..f1ddf2de --- /dev/null +++ b/src/chacha-poly1305/poly1305.c @@ -0,0 +1,197 @@ +/* + * Public Domain poly1305 from Andrew Moon + * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna + */ + +#include "../system.h" + +#include "poly1305.h" + +#define mul32x32_64(a,b) ((uint64_t)(a) * (b)) + +#define U8TO32_LE(p) \ + (((uint32_t)((p)[0])) | \ + ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[2]) << 16) | \ + ((uint32_t)((p)[3]) << 24)) + +#define U32TO8_LE(p, v) \ + do { \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); \ + } while (0) + +void +poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) +{ + uint32_t t0, t1, t2, t3; + uint32_t h0, h1, h2, h3, h4; + uint32_t r0, r1, r2, r3, r4; + uint32_t s1, s2, s3, s4; + uint32_t b, nb; + size_t j; + uint64_t t[5]; + uint64_t f0, f1, f2, f3; + uint32_t g0, g1, g2, g3, g4; + uint64_t c; + unsigned char mp[16]; + + /* clamp key */ + t0 = U8TO32_LE(key + 0); + t1 = U8TO32_LE(key + 4); + t2 = U8TO32_LE(key + 8); + t3 = U8TO32_LE(key + 12); + + /* precompute multipliers */ + r0 = t0 & 0x3ffffff; + t0 >>= 26; + t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; + t1 >>= 20; + t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; + t2 >>= 14; + t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; + t3 >>= 8; + r4 = t3 & 0x00fffff; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + /* init state */ + h0 = 0; + h1 = 0; + h2 = 0; + h3 = 0; + h4 = 0; + + /* full blocks */ + if (inlen < 16) + goto poly1305_donna_atmost15bytes; + + poly1305_donna_16bytes: + m += 16; + inlen -= 16; + + t0 = U8TO32_LE(m - 16); + t1 = U8TO32_LE(m - 12); + t2 = U8TO32_LE(m - 8); + t3 = U8TO32_LE(m - 4); + + h0 += t0 & 0x3ffffff; + h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff; + h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff; + h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff; + h4 += (t3 >> 8) | (1 << 24); + + poly1305_donna_mul: + t[0] = mul32x32_64(h0, r0) + mul32x32_64(h1, s4) + mul32x32_64(h2, s3) + mul32x32_64(h3, s2) + mul32x32_64(h4, s1); + t[1] = mul32x32_64(h0, r1) + mul32x32_64(h1, r0) + mul32x32_64(h2, s4) + mul32x32_64(h3, s3) + mul32x32_64(h4, s2); + t[2] = mul32x32_64(h0, r2) + mul32x32_64(h1, r1) + mul32x32_64(h2, r0) + mul32x32_64(h3, s4) + mul32x32_64(h4, s3); + t[3] = mul32x32_64(h0, r3) + mul32x32_64(h1, r2) + mul32x32_64(h2, r1) + mul32x32_64(h3, r0) + mul32x32_64(h4, s4); + t[4] = mul32x32_64(h0, r4) + mul32x32_64(h1, r3) + mul32x32_64(h2, r2) + mul32x32_64(h3, r1) + mul32x32_64(h4, r0); + + h0 = (uint32_t) t[0] & 0x3ffffff; + c = (t[0] >> 26); + t[1] += c; + h1 = (uint32_t) t[1] & 0x3ffffff; + b = (uint32_t) (t[1] >> 26); + t[2] += b; + h2 = (uint32_t) t[2] & 0x3ffffff; + b = (uint32_t) (t[2] >> 26); + t[3] += b; + h3 = (uint32_t) t[3] & 0x3ffffff; + b = (uint32_t) (t[3] >> 26); + t[4] += b; + h4 = (uint32_t) t[4] & 0x3ffffff; + b = (uint32_t) (t[4] >> 26); + h0 += b * 5; + + if (inlen >= 16) + goto poly1305_donna_16bytes; + + /* final bytes */ + poly1305_donna_atmost15bytes: + if (!inlen) + goto poly1305_donna_finish; + + for (j = 0; j < inlen; j++) + mp[j] = m[j]; + mp[j++] = 1; + for (; j < 16; j++) + mp[j] = 0; + inlen = 0; + + t0 = U8TO32_LE(mp + 0); + t1 = U8TO32_LE(mp + 4); + t2 = U8TO32_LE(mp + 8); + t3 = U8TO32_LE(mp + 12); + + h0 += t0 & 0x3ffffff; + h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff; + h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff; + h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff; + h4 += (t3 >> 8); + + goto poly1305_donna_mul; + + poly1305_donna_finish: + b = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += b; + b = h1 >> 26; + h1 = h1 & 0x3ffffff; + h2 += b; + b = h2 >> 26; + h2 = h2 & 0x3ffffff; + h3 += b; + b = h3 >> 26; + h3 = h3 & 0x3ffffff; + h4 += b; + b = h4 >> 26; + h4 = h4 & 0x3ffffff; + h0 += b * 5; + b = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += b; + + g0 = h0 + 5; + b = g0 >> 26; + g0 &= 0x3ffffff; + g1 = h1 + b; + b = g1 >> 26; + g1 &= 0x3ffffff; + g2 = h2 + b; + b = g2 >> 26; + g2 &= 0x3ffffff; + g3 = h3 + b; + b = g3 >> 26; + g3 &= 0x3ffffff; + g4 = h4 + b - (1 << 26); + + b = (g4 >> 31) - 1; + nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0) | (h1 << 26)) + (uint64_t) U8TO32_LE(&key[16]); + f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t) U8TO32_LE(&key[20]); + f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t) U8TO32_LE(&key[24]); + f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t) U8TO32_LE(&key[28]); + + U32TO8_LE(&out[0], f0); + f1 += (f0 >> 32); + U32TO8_LE(&out[4], f1); + f2 += (f1 >> 32); + U32TO8_LE(&out[8], f2); + f3 += (f2 >> 32); + U32TO8_LE(&out[12], f3); +} diff --git a/src/chacha-poly1305/poly1305.h b/src/chacha-poly1305/poly1305.h new file mode 100644 index 00000000..9a640158 --- /dev/null +++ b/src/chacha-poly1305/poly1305.h @@ -0,0 +1,16 @@ +/* $OpenBSD: poly1305.h,v 1.2 2013/12/19 22:57:13 djm Exp $ */ + +/* + * Public Domain poly1305 from Andrew Moon + * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna + */ + +#ifndef POLY1305_H +#define POLY1305_H + +#define POLY1305_KEYLEN 32 +#define POLY1305_TAGLEN 16 + +void poly1305_auth(uint8_t out[POLY1305_TAGLEN], const uint8_t *m, size_t inlen, const uint8_t key[POLY1305_KEYLEN]); + +#endif /* POLY1305_H */ diff --git a/src/sptps.c b/src/sptps.c index 54c5a55e..133f2b7e 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -19,7 +19,7 @@ #include "system.h" -#include "cipher.h" +#include "chacha-poly1305/chacha-poly1305.h" #include "crypto.h" #include "ecdh.h" #include "ecdsa.h" @@ -84,26 +84,19 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data char buffer[len + 21UL]; // Create header with sequence number, length and record type - uint32_t seqno = htonl(s->outseqno++); + uint32_t seqno = s->outseqno++; + uint32_t netseqno = ntohl(seqno); - memcpy(buffer, &seqno, 4); + memcpy(buffer, &netseqno, 4); buffer[4] = type; + memcpy(buffer + 5, data, len); if(s->outstate) { // If first handshake has finished, encrypt and HMAC - if(!cipher_set_counter(s->outcipher, &seqno, sizeof seqno)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_encrypt_start(s->outcipher, buffer + 4, 1, buffer + 4, NULL)) - return error(s, EINVAL, "Error encrypting record"); - - if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 5, NULL)) - return error(s, EINVAL, "Error encrypting record"); - + chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL); return s->send_data(s->handle, type, buffer, len + 21UL); } else { // Otherwise send as plaintext - memcpy(buffer + 5, data, len); return s->send_data(s->handle, type, buffer, len + 5UL); } } @@ -115,27 +108,19 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_ char buffer[len + 19UL]; // Create header with sequence number, length and record type - uint32_t seqno = htonl(s->outseqno++); + uint32_t seqno = s->outseqno++; uint16_t netlen = htons(len); memcpy(buffer, &netlen, 2); buffer[2] = type; + memcpy(buffer + 3, data, len); if(s->outstate) { // If first handshake has finished, encrypt and HMAC - if(!cipher_set_counter(s->outcipher, &seqno, 4)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_encrypt_start(s->outcipher, buffer, 3, buffer, NULL)) - return error(s, EINVAL, "Error encrypting record"); - - if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 3, NULL)) - return error(s, EINVAL, "Error encrypting record"); - + chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL); return s->send_data(s->handle, type, buffer, len + 19UL); } else { // Otherwise send as plaintext - memcpy(buffer + 3, data, len); return s->send_data(s->handle, type, buffer, len + 3UL); } } @@ -203,14 +188,14 @@ static bool send_sig(sptps_t *s) { static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { // Initialise cipher and digest structures if necessary if(!s->outstate) { - s->incipher = cipher_open_by_name("aes-256-gcm"); - s->outcipher = cipher_open_by_name("aes-256-gcm"); + s->incipher = chacha_poly1305_init(); + s->outcipher = chacha_poly1305_init(); if(!s->incipher || !s->outcipher) return error(s, EINVAL, "Failed to open cipher"); } // Allocate memory for key material - size_t keylen = cipher_keylength(s->incipher) + cipher_keylength(s->outcipher); + size_t keylen = 2 * CHACHA_POLY1305_KEYLEN; s->key = realloc(s->key, keylen); if(!s->key) @@ -246,10 +231,10 @@ static bool receive_ack(sptps_t *s, const char *data, uint16_t len) { return error(s, EIO, "Invalid ACK record length"); if(s->initiator) { - if(!cipher_set_counter_key(s->incipher, s->key)) + if(!chacha_poly1305_set_key(s->incipher, s->key)) return error(s, EINVAL, "Failed to set counter"); } else { - if(!cipher_set_counter_key(s->incipher, s->key + cipher_keylength(s->outcipher))) + if(!chacha_poly1305_set_key(s->incipher, s->key + CHACHA_POLY1305_KEYLEN)) return error(s, EINVAL, "Failed to set counter"); } @@ -323,11 +308,11 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) { // TODO: only set new keys after ACK has been set/received if(s->initiator) { - if(!cipher_set_counter_key(s->outcipher, s->key + cipher_keylength(s->incipher))) - return error(s, EINVAL, "Failed to set counter"); + if(!chacha_poly1305_set_key(s->outcipher, s->key + CHACHA_POLY1305_KEYLEN)) + return error(s, EINVAL, "Failed to set key"); } else { - if(!cipher_set_counter_key(s->outcipher, s->key)) - return error(s, EINVAL, "Failed to set counter"); + if(!chacha_poly1305_set_key(s->outcipher, s->key)) + return error(s, EINVAL, "Failed to set key"); } return true; @@ -421,11 +406,9 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len char buffer[len]; - if(!cipher_set_counter(s->incipher, data, sizeof seqno)) - return error(s, EINVAL, "Failed to set counter"); size_t outlen; - if(!cipher_gcm_decrypt(s->incipher, data + 4, len - 4, buffer, &outlen)) + if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen)) return error(s, EIO, "Failed to decrypt and verify packet"); // Replay protection using a sliding window of configurable size. @@ -508,22 +491,9 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { if(s->buflen < 2) return true; - // Update sequence number. - - uint32_t seqno = htonl(s->inseqno++); - - // Decrypt the length bytes - - if(s->instate) { - if(!cipher_set_counter(s->incipher, &seqno, 4)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_decrypt_start(s->incipher, s->inbuf, 2, &s->reclen, NULL)) - return error(s, EINVAL, "Failed to decrypt record"); - } else { - memcpy(&s->reclen, s->inbuf, 2); - } + // Get the length bytes + memcpy(&s->reclen, s->inbuf, 2); s->reclen = ntohs(s->reclen); // If we have the length bytes, ensure our buffer can hold the whole request. @@ -550,9 +520,13 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) return true; + // Update sequence number. + + uint32_t seqno = s->inseqno++; + // Check HMAC and decrypt. if(s->instate) { - if(!cipher_gcm_decrypt_finish(s->incipher, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) + if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) return error(s, EINVAL, "Failed to decrypt and verify record"); } @@ -622,10 +596,8 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_ // Stop a SPTPS session. bool sptps_stop(sptps_t *s) { // Clean up any resources. - cipher_close(s->incipher); - cipher_close(s->outcipher); - digest_close(s->indigest); - digest_close(s->outdigest); + chacha_poly1305_exit(s->incipher); + chacha_poly1305_exit(s->outcipher); ecdh_free(s->ecdh); free(s->inbuf); free(s->mykex); diff --git a/src/sptps.h b/src/sptps.h index 98e70f14..9754811c 100644 --- a/src/sptps.h +++ b/src/sptps.h @@ -22,8 +22,7 @@ #include "system.h" -#include "cipher.h" -#include "digest.h" +#include "chacha-poly1305/chacha-poly1305.h" #include "ecdh.h" #include "ecdsa.h" @@ -53,16 +52,14 @@ typedef struct sptps { uint16_t reclen; bool instate; - cipher_t *incipher; - digest_t *indigest; + chacha_poly1305_ctx_t *incipher; uint32_t inseqno; uint32_t received; unsigned int replaywin; char *late; bool outstate; - cipher_t *outcipher; - digest_t *outdigest; + chacha_poly1305_ctx_t *outcipher; uint32_t outseqno; ecdsa_t *mykey; -- 2.39.5