Use the ChaCha-Poly1305 cipher for the SPTPS protocol.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 14 Apr 2014 19:43:45 +0000 (21:43 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 14 Apr 2014 19:43:45 +0000 (21:43 +0200)
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.

src/Makefile.am
src/chacha-poly1305/chacha-poly1305.c [new file with mode: 0644]
src/chacha-poly1305/chacha-poly1305.h [new file with mode: 0644]
src/chacha-poly1305/chacha.c [new file with mode: 0644]
src/chacha-poly1305/chacha.h [new file with mode: 0644]
src/chacha-poly1305/poly1305.c [new file with mode: 0644]
src/chacha-poly1305/poly1305.h [new file with mode: 0644]
src/sptps.c
src/sptps.h

index ec1da98..1a84098 100644 (file)
@@ -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 (file)
index 0000000..bd5cb2c
--- /dev/null
@@ -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 (file)
index 0000000..af7eaf5
--- /dev/null
@@ -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 (file)
index 0000000..2d0b918
--- /dev/null
@@ -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 (file)
index 0000000..af1b9a4
--- /dev/null
@@ -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 (file)
index 0000000..f1ddf2d
--- /dev/null
@@ -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 (file)
index 0000000..9a64015
--- /dev/null
@@ -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 */
index 0369d28..0989b14 100644 (file)
@@ -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);
index 3a8e65f..3f8d29c 100644 (file)
@@ -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;