Move poly1305_get_tag() into poly1305.c, hide poly1305_init().
[tinc] / src / chacha-poly1305 / chachapoly.c
1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2015 Grigori Goronzy <goronzy@kinoho.net>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <assert.h>
29
30 #include "chachapoly.h"
31
32 /**
33  * Constant-time memory compare. This should help to protect against
34  * side-channel attacks.
35  *
36  * \param av input 1
37  * \param bv input 2
38  * \param n bytes to compare
39  * \return 0 if inputs are equal
40  */
41 static int memcmp_eq(const void *av, const void *bv, int n) {
42         const unsigned char *a = (const unsigned char *) av;
43         const unsigned char *b = (const unsigned char *) bv;
44         unsigned char res = 0;
45         int i;
46
47         for(i = 0; i < n; i++) {
48                 res |= *a ^ *b;
49                 a++;
50                 b++;
51         }
52
53         return res;
54 }
55
56 int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len) {
57         assert(key_len == 128 || key_len == 256);
58
59         memset(ctx, 0, sizeof(*ctx));
60         chacha_keysetup(&ctx->cha_ctx, key, key_len);
61         return CHACHAPOLY_OK;
62 }
63
64 int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
65                      void *input, int input_len,
66                      void *output, void *tag, int tag_len, int encrypt) {
67         unsigned char poly_key[CHACHA_BLOCKLEN];
68         unsigned char calc_tag[POLY1305_TAGLEN];
69         const unsigned char one[4] = { 1, 0, 0, 0 };
70
71         /* initialize keystream and generate poly1305 key */
72         memset(poly_key, 0, sizeof(poly_key));
73         chacha_ivsetup(&ctx->cha_ctx, nonce, NULL);
74         chacha_encrypt_bytes(&ctx->cha_ctx, poly_key, poly_key, sizeof(poly_key));
75
76         /* check tag if decrypting */
77         if(encrypt == 0 && tag_len) {
78                 poly1305_get_tag(poly_key, input, input_len, calc_tag);
79
80                 if(memcmp_eq(calc_tag, tag, tag_len) != 0) {
81                         return CHACHAPOLY_INVALID_MAC;
82                 }
83         }
84
85         /* crypt data */
86         chacha_ivsetup(&ctx->cha_ctx, nonce, one);
87         chacha_encrypt_bytes(&ctx->cha_ctx, (unsigned char *)input,
88                              (unsigned char *)output, input_len);
89
90         /* add tag if encrypting */
91         if(encrypt && tag_len) {
92                 poly1305_get_tag(poly_key, output, input_len, calc_tag);
93                 memcpy(tag, calc_tag, tag_len);
94         }
95
96         return CHACHAPOLY_OK;
97 }
98
99 int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce,
100                            void *input, int input_len,
101                            void *output, void *tag, int tag_len, int encrypt) {
102         unsigned char keystream[CHACHA_BLOCKLEN];
103         unsigned char calc_tag[POLY1305_TAGLEN];
104         int i;
105
106         assert(input_len <= 32);
107
108         /* initialize keystream and generate poly1305 key */
109         memset(keystream, 0, sizeof(keystream));
110         chacha_ivsetup(&ctx->cha_ctx, nonce, NULL);
111         chacha_encrypt_bytes(&ctx->cha_ctx, keystream, keystream,
112                              sizeof(keystream));
113
114         /* check tag if decrypting */
115         if(encrypt == 0 && tag_len) {
116                 poly1305_get_tag(keystream, input, input_len, calc_tag);
117
118                 if(memcmp_eq(calc_tag, tag, tag_len) != 0) {
119                         return CHACHAPOLY_INVALID_MAC;
120                 }
121         }
122
123         /* crypt data */
124         for(i = 0; i < input_len; i++) {
125                 ((unsigned char *)output)[i] =
126                         ((unsigned char *)input)[i] ^ keystream[32 + i];
127         }
128
129         /* add tag if encrypting */
130         if(encrypt && tag_len) {
131                 poly1305_get_tag(keystream, output, input_len, calc_tag);
132                 memcpy(tag, calc_tag, tag_len);
133         }
134
135         return CHACHAPOLY_OK;
136 }