diff --git a/src/Makefile.am b/src/Makefile.am index ec1da980..1a84098e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,11 @@ 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 + tincd_SOURCES = \ buffer.c buffer.h \ cipher.h \ @@ -83,7 +88,8 @@ tincd_SOURCES = \ tincd.c \ utils.c utils.h \ xalloc.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) tinc_SOURCES = \ dropin.c dropin.h \ @@ -100,14 +106,16 @@ tinc_SOURCES = \ tincctl.c tincctl.h \ top.c top.h \ utils.c utils.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) 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 \ @@ -119,7 +127,8 @@ sptps_speed_SOURCES = \ sptps.c sptps.h \ sptps_speed.c \ utils.c utils.h \ - $(ed25519_SOURCES) + $(ed25519_SOURCES) \ + $(chacha_poly1305_SOURCES) ## Conditionally compile device drivers @@ -174,7 +183,6 @@ tinc_SOURCES += \ openssl/rsa.c \ openssl/rsagen.c sptps_test_SOURCES += \ - openssl/cipher.c \ openssl/crypto.c \ openssl/digest.c openssl/digest.h \ ed25519/ecdh.c \ @@ -184,7 +192,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 0369d283..0989b14d 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -20,7 +20,7 @@ #include "system.h" -#include "cipher.h" +#include "chacha-poly1305/chacha-poly1305.h" #include "crypto.h" #include "ecdh.h" #include "ecdsa.h" @@ -85,26 +85,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); } } @@ -116,27 +109,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); } } @@ -204,14 +189,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) @@ -247,10 +232,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"); } @@ -324,11 +309,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; @@ -422,11 +407,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. @@ -514,22 +497,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. @@ -556,9 +526,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"); } @@ -628,10 +602,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 3a8e65f7..3f8d29c6 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,8 +52,7 @@ 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; @@ -62,8 +60,7 @@ typedef struct sptps { char *late; bool outstate; - cipher_t *outcipher; - digest_t *outdigest; + chacha_poly1305_ctx_t *outcipher; uint32_t outseqno; ecdsa_t *mykey;