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 <assert.h>
18 #include <string.h>
19
20 #include <openssl/chacha.h>
21 #include <openssl/cipher.h>
22 #include <openssl/err.h>
23 #include <openssl/mem.h>
24 #include <openssl/poly1305.h>
25
26 #include "internal.h"
27 #include "../chacha/internal.h"
28 #include "../fipsmodule/cipher/internal.h"
29 #include "../internal.h"
30
31 struct aead_chacha20_poly1305_ctx {
32 uint8_t key[32];
33 };
34
35 static_assert(sizeof(((EVP_AEAD_CTX *)NULL)->state) >=
36 sizeof(struct aead_chacha20_poly1305_ctx),
37 "AEAD state is too small");
38 static_assert(alignof(union evp_aead_ctx_st_state) >=
39 alignof(struct aead_chacha20_poly1305_ctx),
40 "AEAD state has insufficient alignment");
41
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const uint8_t * key,size_t key_len,size_t tag_len)42 static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
43 size_t key_len, size_t tag_len) {
44 struct aead_chacha20_poly1305_ctx *c20_ctx =
45 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
46
47 if (tag_len == 0) {
48 tag_len = POLY1305_TAG_LEN;
49 }
50
51 if (tag_len > POLY1305_TAG_LEN) {
52 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
53 return 0;
54 }
55
56 if (key_len != sizeof(c20_ctx->key)) {
57 return 0; // internal error - EVP_AEAD_CTX_init should catch this.
58 }
59
60 OPENSSL_memcpy(c20_ctx->key, key, key_len);
61 ctx->tag_len = tag_len;
62
63 return 1;
64 }
65
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)66 static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {}
67
poly1305_update_length(poly1305_state * poly1305,size_t data_len)68 static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
69 uint8_t length_bytes[8];
70
71 for (unsigned i = 0; i < sizeof(length_bytes); i++) {
72 length_bytes[i] = data_len;
73 data_len >>= 8;
74 }
75
76 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
77 }
78
79 // calc_tag fills |tag| with the authentication tag for the given inputs.
calc_tag(uint8_t tag[POLY1305_TAG_LEN],const uint8_t * key,const uint8_t nonce[12],const uint8_t * ad,size_t ad_len,const uint8_t * ciphertext,size_t ciphertext_len,const uint8_t * ciphertext_extra,size_t ciphertext_extra_len)80 static void calc_tag(uint8_t tag[POLY1305_TAG_LEN], const uint8_t *key,
81 const uint8_t nonce[12], const uint8_t *ad, size_t ad_len,
82 const uint8_t *ciphertext, size_t ciphertext_len,
83 const uint8_t *ciphertext_extra,
84 size_t ciphertext_extra_len) {
85 alignas(16) uint8_t poly1305_key[32];
86 OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key));
87 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, nonce,
88 0);
89
90 static const uint8_t padding[16] = { 0 }; // Padding is all zeros.
91 poly1305_state ctx;
92 CRYPTO_poly1305_init(&ctx, poly1305_key);
93 CRYPTO_poly1305_update(&ctx, ad, ad_len);
94 if (ad_len % 16 != 0) {
95 CRYPTO_poly1305_update(&ctx, padding, sizeof(padding) - (ad_len % 16));
96 }
97 CRYPTO_poly1305_update(&ctx, ciphertext, ciphertext_len);
98 CRYPTO_poly1305_update(&ctx, ciphertext_extra, ciphertext_extra_len);
99 const size_t ciphertext_total = ciphertext_len + ciphertext_extra_len;
100 if (ciphertext_total % 16 != 0) {
101 CRYPTO_poly1305_update(&ctx, padding,
102 sizeof(padding) - (ciphertext_total % 16));
103 }
104 poly1305_update_length(&ctx, ad_len);
105 poly1305_update_length(&ctx, ciphertext_total);
106 CRYPTO_poly1305_finish(&ctx, tag);
107 }
108
chacha20_poly1305_seal_scatter(const uint8_t * key,uint8_t * out,uint8_t * out_tag,size_t * out_tag_len,size_t max_out_tag_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * extra_in,size_t extra_in_len,const uint8_t * ad,size_t ad_len,size_t tag_len)109 static int chacha20_poly1305_seal_scatter(
110 const uint8_t *key, uint8_t *out, uint8_t *out_tag,
111 size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
112 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
113 size_t extra_in_len, const uint8_t *ad, size_t ad_len, size_t tag_len) {
114 if (extra_in_len + tag_len < tag_len) {
115 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
116 return 0;
117 }
118 if (max_out_tag_len < tag_len + extra_in_len) {
119 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
120 return 0;
121 }
122 if (nonce_len != 12) {
123 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
124 return 0;
125 }
126
127 // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
128 // individual operations that work on more than 256GB at a time.
129 // |in_len_64| is needed because, on 32-bit platforms, size_t is only
130 // 32-bits and this produces a warning because it's always false.
131 // Casting to uint64_t inside the conditional is not sufficient to stop
132 // the warning.
133 const uint64_t in_len_64 = in_len;
134 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
135 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
136 return 0;
137 }
138
139 if (max_out_tag_len < tag_len) {
140 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
141 return 0;
142 }
143
144 // The the extra input is given, it is expected to be very short and so is
145 // encrypted byte-by-byte first.
146 if (extra_in_len) {
147 static const size_t kChaChaBlockSize = 64;
148 uint32_t block_counter = (uint32_t)(1 + (in_len / kChaChaBlockSize));
149 size_t offset = in_len % kChaChaBlockSize;
150 uint8_t block[64 /* kChaChaBlockSize */];
151
152 for (size_t done = 0; done < extra_in_len; block_counter++) {
153 memset(block, 0, sizeof(block));
154 CRYPTO_chacha_20(block, block, sizeof(block), key, nonce,
155 block_counter);
156 for (size_t i = offset; i < sizeof(block) && done < extra_in_len;
157 i++, done++) {
158 out_tag[done] = extra_in[done] ^ block[i];
159 }
160 offset = 0;
161 }
162 }
163
164 union chacha20_poly1305_seal_data data;
165 if (chacha20_poly1305_asm_capable()) {
166 OPENSSL_memcpy(data.in.key, key, 32);
167 data.in.counter = 0;
168 OPENSSL_memcpy(data.in.nonce, nonce, 12);
169 data.in.extra_ciphertext = out_tag;
170 data.in.extra_ciphertext_len = extra_in_len;
171 chacha20_poly1305_seal(out, in, in_len, ad, ad_len, &data);
172 } else {
173 CRYPTO_chacha_20(out, in, in_len, key, nonce, 1);
174 calc_tag(data.out.tag, key, nonce, ad, ad_len, out, in_len, out_tag,
175 extra_in_len);
176 }
177
178 OPENSSL_memcpy(out_tag + extra_in_len, data.out.tag, tag_len);
179 *out_tag_len = extra_in_len + tag_len;
180 return 1;
181 }
182
aead_chacha20_poly1305_seal_scatter(const EVP_AEAD_CTX * ctx,uint8_t * out,uint8_t * out_tag,size_t * out_tag_len,size_t max_out_tag_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * extra_in,size_t extra_in_len,const uint8_t * ad,size_t ad_len)183 static int aead_chacha20_poly1305_seal_scatter(
184 const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag,
185 size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
186 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
187 size_t extra_in_len, const uint8_t *ad, size_t ad_len) {
188 const struct aead_chacha20_poly1305_ctx *c20_ctx =
189 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
190
191 return chacha20_poly1305_seal_scatter(
192 c20_ctx->key, out, out_tag, out_tag_len, max_out_tag_len, nonce,
193 nonce_len, in, in_len, extra_in, extra_in_len, ad, ad_len, ctx->tag_len);
194 }
195
aead_xchacha20_poly1305_seal_scatter(const EVP_AEAD_CTX * ctx,uint8_t * out,uint8_t * out_tag,size_t * out_tag_len,size_t max_out_tag_len,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * extra_in,size_t extra_in_len,const uint8_t * ad,size_t ad_len)196 static int aead_xchacha20_poly1305_seal_scatter(
197 const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag,
198 size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
199 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
200 size_t extra_in_len, const uint8_t *ad, size_t ad_len) {
201 const struct aead_chacha20_poly1305_ctx *c20_ctx =
202 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
203
204 if (nonce_len != 24) {
205 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
206 return 0;
207 }
208
209 alignas(4) uint8_t derived_key[32];
210 alignas(4) uint8_t derived_nonce[12];
211 CRYPTO_hchacha20(derived_key, c20_ctx->key, nonce);
212 OPENSSL_memset(derived_nonce, 0, 4);
213 OPENSSL_memcpy(&derived_nonce[4], &nonce[16], 8);
214
215 return chacha20_poly1305_seal_scatter(
216 derived_key, out, out_tag, out_tag_len, max_out_tag_len,
217 derived_nonce, sizeof(derived_nonce), in, in_len, extra_in, extra_in_len,
218 ad, ad_len, ctx->tag_len);
219 }
220
chacha20_poly1305_open_gather(const uint8_t * key,uint8_t * out,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * in_tag,size_t in_tag_len,const uint8_t * ad,size_t ad_len,size_t tag_len)221 static int chacha20_poly1305_open_gather(
222 const uint8_t *key, uint8_t *out, const uint8_t *nonce,
223 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag,
224 size_t in_tag_len, const uint8_t *ad, size_t ad_len, size_t tag_len) {
225 if (nonce_len != 12) {
226 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
227 return 0;
228 }
229
230 if (in_tag_len != tag_len) {
231 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
232 return 0;
233 }
234
235 // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow
236 // individual operations that work on more than 256GB at a time.
237 // |in_len_64| is needed because, on 32-bit platforms, size_t is only
238 // 32-bits and this produces a warning because it's always false.
239 // Casting to uint64_t inside the conditional is not sufficient to stop
240 // the warning.
241 const uint64_t in_len_64 = in_len;
242 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
243 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
244 return 0;
245 }
246
247 union chacha20_poly1305_open_data data;
248 if (chacha20_poly1305_asm_capable()) {
249 OPENSSL_memcpy(data.in.key, key, 32);
250 data.in.counter = 0;
251 OPENSSL_memcpy(data.in.nonce, nonce, 12);
252 chacha20_poly1305_open(out, in, in_len, ad, ad_len, &data);
253 } else {
254 calc_tag(data.out.tag, key, nonce, ad, ad_len, in, in_len, NULL, 0);
255 CRYPTO_chacha_20(out, in, in_len, key, nonce, 1);
256 }
257
258 if (CRYPTO_memcmp(data.out.tag, in_tag, tag_len) != 0) {
259 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
260 return 0;
261 }
262
263 return 1;
264 }
265
aead_chacha20_poly1305_open_gather(const EVP_AEAD_CTX * ctx,uint8_t * out,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * in_tag,size_t in_tag_len,const uint8_t * ad,size_t ad_len)266 static int aead_chacha20_poly1305_open_gather(
267 const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce,
268 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag,
269 size_t in_tag_len, const uint8_t *ad, size_t ad_len) {
270 const struct aead_chacha20_poly1305_ctx *c20_ctx =
271 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
272
273 return chacha20_poly1305_open_gather(c20_ctx->key, out, nonce, nonce_len, in,
274 in_len, in_tag, in_tag_len, ad, ad_len,
275 ctx->tag_len);
276 }
277
aead_xchacha20_poly1305_open_gather(const EVP_AEAD_CTX * ctx,uint8_t * out,const uint8_t * nonce,size_t nonce_len,const uint8_t * in,size_t in_len,const uint8_t * in_tag,size_t in_tag_len,const uint8_t * ad,size_t ad_len)278 static int aead_xchacha20_poly1305_open_gather(
279 const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce,
280 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag,
281 size_t in_tag_len, const uint8_t *ad, size_t ad_len) {
282 const struct aead_chacha20_poly1305_ctx *c20_ctx =
283 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
284
285 if (nonce_len != 24) {
286 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
287 return 0;
288 }
289
290 alignas(4) uint8_t derived_key[32];
291 alignas(4) uint8_t derived_nonce[12];
292 CRYPTO_hchacha20(derived_key, c20_ctx->key, nonce);
293 OPENSSL_memset(derived_nonce, 0, 4);
294 OPENSSL_memcpy(&derived_nonce[4], &nonce[16], 8);
295
296 return chacha20_poly1305_open_gather(
297 derived_key, out, derived_nonce, sizeof(derived_nonce), in, in_len,
298 in_tag, in_tag_len, ad, ad_len, ctx->tag_len);
299 }
300
301 static const EVP_AEAD aead_chacha20_poly1305 = {
302 32, // key len
303 12, // nonce len
304 POLY1305_TAG_LEN, // overhead
305 POLY1305_TAG_LEN, // max tag length
306 1, // seal_scatter_supports_extra_in
307
308 aead_chacha20_poly1305_init,
309 NULL, // init_with_direction
310 aead_chacha20_poly1305_cleanup,
311 NULL /* open */,
312 aead_chacha20_poly1305_seal_scatter,
313 aead_chacha20_poly1305_open_gather,
314 NULL, // get_iv
315 NULL, // tag_len
316 };
317
318 static const EVP_AEAD aead_xchacha20_poly1305 = {
319 32, // key len
320 24, // nonce len
321 POLY1305_TAG_LEN, // overhead
322 POLY1305_TAG_LEN, // max tag length
323 1, // seal_scatter_supports_extra_in
324
325 aead_chacha20_poly1305_init,
326 NULL, // init_with_direction
327 aead_chacha20_poly1305_cleanup,
328 NULL /* open */,
329 aead_xchacha20_poly1305_seal_scatter,
330 aead_xchacha20_poly1305_open_gather,
331 NULL, // get_iv
332 NULL, // tag_len
333 };
334
EVP_aead_chacha20_poly1305(void)335 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
336 return &aead_chacha20_poly1305;
337 }
338
EVP_aead_xchacha20_poly1305(void)339 const EVP_AEAD *EVP_aead_xchacha20_poly1305(void) {
340 return &aead_xchacha20_poly1305;
341 }
342