1 /* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include <openssl/aead.h>
16
17 #include <string.h>
18
19 #include <openssl/chacha.h>
20 #include <openssl/cipher.h>
21 #include <openssl/err.h>
22 #include <openssl/mem.h>
23 #include <openssl/poly1305.h>
24
25 #include "internal.h"
26
27
28 #define POLY1305_TAG_LEN 16
29
30 struct aead_chacha20_poly1305_ctx {
31 unsigned char key[32];
32 unsigned char tag_len;
33 };
34
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const uint8_t * key,size_t key_len,size_t tag_len)35 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
36 size_t key_len, size_t tag_len) {
37 struct aead_chacha20_poly1305_ctx *c20_ctx;
38
39 if (tag_len == 0) {
40 tag_len = POLY1305_TAG_LEN;
41 }
42
43 if (tag_len > POLY1305_TAG_LEN) {
44 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
45 return 0;
46 }
47
48 if (key_len != sizeof(c20_ctx->key)) {
49 return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */
50 }
51
52 c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx));
53 if (c20_ctx == NULL) {
54 return 0;
55 }
56
57 memcpy(c20_ctx->key, key, key_len);
58 c20_ctx->tag_len = tag_len;
59 ctx->aead_state = c20_ctx;
60
61 return 1;
62 }
63
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)64 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {
65 struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
66 OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key));
67 OPENSSL_free(c20_ctx);
68 }
69
poly1305_update_length(poly1305_state * poly1305,size_t data_len)70 static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
71 uint8_t length_bytes[8];
72 unsigned i;
73
74 for (i = 0; i < sizeof(length_bytes); i++) {
75 length_bytes[i] = data_len;
76 data_len >>= 8;
77 }
78
79 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
80 }
81
82 #if defined(__arm__)
83 #define ALIGNED __attribute__((aligned(16)))
84 #else
85 #define ALIGNED
86 #endif
87
88 typedef void (*aead_poly1305_update)(poly1305_state *ctx, const uint8_t *ad,
89 size_t ad_len, const uint8_t *ciphertext,
90 size_t ciphertext_len);
91
92 /* aead_poly1305 fills |tag| with the authentication tag for the given
93 * inputs, using |update| to control the order and format that the inputs are
94 * signed/authenticated. */
aead_poly1305(aead_poly1305_update update,uint8_t tag[POLY1305_TAG_LEN],const struct aead_chacha20_poly1305_ctx * c20_ctx,const uint8_t nonce[12],const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)95 static void aead_poly1305(aead_poly1305_update update,
96 uint8_t tag[POLY1305_TAG_LEN],
97 const struct aead_chacha20_poly1305_ctx *c20_ctx,
98 const uint8_t nonce[12], const uint8_t *ad,
99 size_t ad_len, const uint8_t *ciphertext,
100 size_t ciphertext_len) {
101 uint8_t poly1305_key[32] ALIGNED;
102 memset(poly1305_key, 0, sizeof(poly1305_key));
103 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
104 c20_ctx->key, nonce, 0);
105 poly1305_state ctx;
106 CRYPTO_poly1305_init(&ctx, poly1305_key);
107 update(&ctx, ad, ad_len, ciphertext, ciphertext_len);
108 CRYPTO_poly1305_finish(&ctx, tag);
109 }
110
seal_impl(aead_poly1305_update poly1305_update,const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t nonce[12],const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)111 static int seal_impl(aead_poly1305_update poly1305_update,
112 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
113 size_t max_out_len, const uint8_t nonce[12],
114 const uint8_t *in, size_t in_len, const uint8_t *ad,
115 size_t ad_len) {
116 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
117 const uint64_t in_len_64 = in_len;
118
119 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
120 * individual operations that work on more than 256GB at a time.
121 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
122 * 32-bits and this produces a warning because it's always false.
123 * Casting to uint64_t inside the conditional is not sufficient to stop
124 * the warning. */
125 if (in_len_64 >= (1ull << 32) * 64 - 64) {
126 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
127 return 0;
128 }
129
130 if (in_len + c20_ctx->tag_len < in_len) {
131 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
132 return 0;
133 }
134
135 if (max_out_len < in_len + c20_ctx->tag_len) {
136 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
137 return 0;
138 }
139
140 CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1);
141
142 uint8_t tag[POLY1305_TAG_LEN] ALIGNED;
143 aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, out, in_len);
144
145 memcpy(out + in_len, tag, c20_ctx->tag_len);
146 *out_len = in_len + c20_ctx->tag_len;
147 return 1;
148 }
149
open_impl(aead_poly1305_update poly1305_update,const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t nonce[12],const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)150 static int open_impl(aead_poly1305_update poly1305_update,
151 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
152 size_t max_out_len, const uint8_t nonce[12],
153 const uint8_t *in, size_t in_len, const uint8_t *ad,
154 size_t ad_len) {
155 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
156 size_t plaintext_len;
157 const uint64_t in_len_64 = in_len;
158
159 if (in_len < c20_ctx->tag_len) {
160 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
161 return 0;
162 }
163
164 /* |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
165 * individual operations that work on more than 256GB at a time.
166 * |in_len_64| is needed because, on 32-bit platforms, size_t is only
167 * 32-bits and this produces a warning because it's always false.
168 * Casting to uint64_t inside the conditional is not sufficient to stop
169 * the warning. */
170 if (in_len_64 >= (1ull << 32) * 64 - 64) {
171 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
172 return 0;
173 }
174
175 plaintext_len = in_len - c20_ctx->tag_len;
176 uint8_t tag[POLY1305_TAG_LEN] ALIGNED;
177 aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, in,
178 plaintext_len);
179 if (CRYPTO_memcmp(tag, in + plaintext_len, c20_ctx->tag_len) != 0) {
180 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
181 return 0;
182 }
183
184 CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1);
185 *out_len = plaintext_len;
186 return 1;
187 }
188
poly1305_update_padded_16(poly1305_state * poly1305,const uint8_t * data,size_t data_len)189 static void poly1305_update_padded_16(poly1305_state *poly1305,
190 const uint8_t *data, size_t data_len) {
191 static const uint8_t padding[16] = { 0 }; /* Padding is all zeros. */
192
193 CRYPTO_poly1305_update(poly1305, data, data_len);
194 if (data_len % 16 != 0) {
195 CRYPTO_poly1305_update(poly1305, padding, sizeof(padding) - (data_len % 16));
196 }
197 }
198
poly1305_update(poly1305_state * ctx,const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)199 static void poly1305_update(poly1305_state *ctx, const uint8_t *ad,
200 size_t ad_len, const uint8_t *ciphertext,
201 size_t ciphertext_len) {
202 poly1305_update_padded_16(ctx, ad, ad_len);
203 poly1305_update_padded_16(ctx, ciphertext, ciphertext_len);
204 poly1305_update_length(ctx, ad_len);
205 poly1305_update_length(ctx, ciphertext_len);
206 }
207
aead_chacha20_poly1305_seal(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)208 static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
209 size_t *out_len, size_t max_out_len,
210 const uint8_t *nonce, size_t nonce_len,
211 const uint8_t *in, size_t in_len,
212 const uint8_t *ad, size_t ad_len) {
213 if (nonce_len != 12) {
214 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
215 return 0;
216 }
217 return seal_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
218 in_len, ad, ad_len);
219 }
220
aead_chacha20_poly1305_open(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)221 static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
222 size_t *out_len, size_t max_out_len,
223 const uint8_t *nonce, size_t nonce_len,
224 const uint8_t *in, size_t in_len,
225 const uint8_t *ad, size_t ad_len) {
226 if (nonce_len != 12) {
227 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
228 return 0;
229 }
230 return open_impl(poly1305_update, ctx, out, out_len, max_out_len, nonce, in,
231 in_len, ad, ad_len);
232 }
233
234 static const EVP_AEAD aead_chacha20_poly1305 = {
235 32, /* key len */
236 12, /* nonce len */
237 POLY1305_TAG_LEN, /* overhead */
238 POLY1305_TAG_LEN, /* max tag length */
239 aead_chacha20_poly1305_init,
240 NULL, /* init_with_direction */
241 aead_chacha20_poly1305_cleanup,
242 aead_chacha20_poly1305_seal,
243 aead_chacha20_poly1305_open,
244 NULL, /* get_rc4_state */
245 NULL, /* get_iv */
246 };
247
EVP_aead_chacha20_poly1305(void)248 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
249 return &aead_chacha20_poly1305;
250 }
251
EVP_aead_chacha20_poly1305_rfc7539(void)252 const EVP_AEAD *EVP_aead_chacha20_poly1305_rfc7539(void) {
253 return EVP_aead_chacha20_poly1305();
254 }
255
poly1305_update_old(poly1305_state * ctx,const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len)256 static void poly1305_update_old(poly1305_state *ctx, const uint8_t *ad,
257 size_t ad_len, const uint8_t *ciphertext,
258 size_t ciphertext_len) {
259 CRYPTO_poly1305_update(ctx, ad, ad_len);
260 poly1305_update_length(ctx, ad_len);
261 CRYPTO_poly1305_update(ctx, ciphertext, ciphertext_len);
262 poly1305_update_length(ctx, ciphertext_len);
263 }
264
aead_chacha20_poly1305_old_seal(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)265 static int aead_chacha20_poly1305_old_seal(
266 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
267 const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
268 const uint8_t *ad, size_t ad_len) {
269 if (nonce_len != 8) {
270 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
271 return 0;
272 }
273 uint8_t nonce_96[12];
274 memset(nonce_96, 0, 4);
275 memcpy(nonce_96 + 4, nonce, 8);
276 return seal_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
277 nonce_96, in, in_len, ad, ad_len);
278 }
279
aead_chacha20_poly1305_old_open(const EVP_AEAD_CTX * ctx,uint8_t * out,size_t * out_len,size_t max_out_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * ad,size_t ad_len)280 static int aead_chacha20_poly1305_old_open(
281 const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
282 const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
283 const uint8_t *ad, size_t ad_len) {
284 if (nonce_len != 8) {
285 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
286 return 0;
287 }
288 uint8_t nonce_96[12];
289 memset(nonce_96, 0, 4);
290 memcpy(nonce_96 + 4, nonce, 8);
291 return open_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
292 nonce_96, in, in_len, ad, ad_len);
293 }
294
295 static const EVP_AEAD aead_chacha20_poly1305_old = {
296 32, /* key len */
297 8, /* nonce len */
298 POLY1305_TAG_LEN, /* overhead */
299 POLY1305_TAG_LEN, /* max tag length */
300 aead_chacha20_poly1305_init,
301 NULL, /* init_with_direction */
302 aead_chacha20_poly1305_cleanup,
303 aead_chacha20_poly1305_old_seal,
304 aead_chacha20_poly1305_old_open,
305 NULL, /* get_rc4_state */
306 NULL, /* get_iv */
307 };
308
EVP_aead_chacha20_poly1305_old(void)309 const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void) {
310 return &aead_chacha20_poly1305_old;
311 }
312