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