d4f37237983764ff51c6714b050cce6700c2cd25
[tinc] / src / chacha-poly1305 / chacha-poly1305.c
1 #include "../system.h"
2
3 #include "../cipher.h"
4 #include "../xalloc.h"
5
6 #include "chacha.h"
7 #include "chacha-poly1305.h"
8 #include "poly1305.h"
9
10 struct chacha_poly1305_ctx {
11         struct chacha_ctx main_ctx, header_ctx;
12 };
13
14 chacha_poly1305_ctx_t *chacha_poly1305_init(void)
15 {
16         chacha_poly1305_ctx_t *ctx = xzalloc(sizeof(*ctx));
17         return ctx;
18 }
19
20 void chacha_poly1305_exit(chacha_poly1305_ctx_t *ctx)
21 {
22         free(ctx);
23 }
24
25 bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key)
26 {
27         chacha_keysetup(&ctx->main_ctx, key, 256);
28         chacha_keysetup(&ctx->header_ctx, key + 32, 256);
29         return true;
30 }
31
32 static void put_u64(void *vp, uint64_t v)
33 {
34         uint8_t *p = (uint8_t *) vp;
35
36         p[0] = (uint8_t) (v >> 56) & 0xff;
37         p[1] = (uint8_t) (v >> 48) & 0xff;
38         p[2] = (uint8_t) (v >> 40) & 0xff;
39         p[3] = (uint8_t) (v >> 32) & 0xff;
40         p[4] = (uint8_t) (v >> 24) & 0xff;
41         p[5] = (uint8_t) (v >> 16) & 0xff;
42         p[6] = (uint8_t) (v >> 8) & 0xff;
43         p[7] = (uint8_t) v & 0xff;
44 }
45
46 bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
47         uint8_t seqbuf[8];
48         const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 };      /* NB little-endian */
49         uint8_t poly_key[POLY1305_KEYLEN];
50
51         /*
52          * Run ChaCha20 once to generate the Poly1305 key. The IV is the
53          * packet sequence number.
54          */
55         memset(poly_key, 0, sizeof(poly_key));
56         put_u64(seqbuf, seqnr);
57         chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
58         chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
59
60         /* Set Chacha's block counter to 1 */
61         chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
62
63         chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
64         poly1305_auth(outdata + inlen, outdata, inlen, poly_key);
65
66         if (outlen)
67                 *outlen = inlen + POLY1305_TAGLEN;
68
69         return true;
70 }
71
72 bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
73         uint8_t seqbuf[8];
74         const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 };      /* NB little-endian */
75         uint8_t expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
76
77         /*
78          * Run ChaCha20 once to generate the Poly1305 key. The IV is the
79          * packet sequence number.
80          */
81         memset(poly_key, 0, sizeof(poly_key));
82         put_u64(seqbuf, seqnr);
83         chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
84         chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key));
85
86         /* Set Chacha's block counter to 1 */
87         chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
88
89         /* Check tag before anything else */
90         inlen -= POLY1305_TAGLEN;
91         const uint8_t *tag = indata + inlen;
92
93         poly1305_auth(expected_tag, indata, inlen, poly_key);
94         if (memcmp(expected_tag, tag, POLY1305_TAGLEN))
95                 return false;
96
97         chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen);
98
99         if (outlen)
100                 *outlen = inlen;
101
102         return true;
103 }