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/cpu.h> |
22 | #include <openssl/err.h> |
23 | #include <openssl/mem.h> |
24 | #include <openssl/poly1305.h> |
25 | #include <openssl/type_check.h> |
26 | |
27 | #include "../fipsmodule/cipher/internal.h" |
28 | #include "../internal.h" |
29 | #include "../chacha/internal.h" |
30 | |
31 | |
32 | #define POLY1305_TAG_LEN 16 |
33 | |
34 | struct aead_chacha20_poly1305_ctx { |
35 | uint8_t key[32]; |
36 | }; |
37 | |
38 | OPENSSL_STATIC_ASSERT(sizeof(((EVP_AEAD_CTX *)NULL)->state) >= |
39 | sizeof(struct aead_chacha20_poly1305_ctx), |
40 | "AEAD state is too small" ); |
41 | #if defined(__GNUC__) || defined(__clang__) |
42 | OPENSSL_STATIC_ASSERT(alignof(union evp_aead_ctx_st_state) >= |
43 | alignof(struct aead_chacha20_poly1305_ctx), |
44 | "AEAD state has insufficient alignment" ); |
45 | #endif |
46 | |
47 | // For convenience (the x86_64 calling convention allows only six parameters in |
48 | // registers), the final parameter for the assembly functions is both an input |
49 | // and output parameter. |
50 | union open_data { |
51 | struct { |
52 | alignas(16) uint8_t key[32]; |
53 | uint32_t counter; |
54 | uint8_t nonce[12]; |
55 | } in; |
56 | struct { |
57 | uint8_t tag[POLY1305_TAG_LEN]; |
58 | } out; |
59 | }; |
60 | |
61 | union seal_data { |
62 | struct { |
63 | alignas(16) uint8_t key[32]; |
64 | uint32_t counter; |
65 | uint8_t nonce[12]; |
66 | const uint8_t *; |
67 | size_t ; |
68 | } in; |
69 | struct { |
70 | uint8_t tag[POLY1305_TAG_LEN]; |
71 | } out; |
72 | }; |
73 | |
74 | #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \ |
75 | !defined(OPENSSL_WINDOWS) |
76 | static int asm_capable(void) { |
77 | const int sse41_capable = (OPENSSL_ia32cap_P[1] & (1 << 19)) != 0; |
78 | return sse41_capable; |
79 | } |
80 | |
81 | OPENSSL_STATIC_ASSERT(sizeof(union open_data) == 48, "wrong open_data size" ); |
82 | OPENSSL_STATIC_ASSERT(sizeof(union seal_data) == 48 + 8 + 8, |
83 | "wrong seal_data size" ); |
84 | |
85 | // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It decrypts |
86 | // |plaintext_len| bytes from |ciphertext| and writes them to |out_plaintext|. |
87 | // Additional input parameters are passed in |aead_data->in|. On exit, it will |
88 | // write calculated tag value to |aead_data->out.tag|, which the caller must |
89 | // check. |
90 | extern void chacha20_poly1305_open(uint8_t *out_plaintext, |
91 | const uint8_t *ciphertext, |
92 | size_t plaintext_len, const uint8_t *ad, |
93 | size_t ad_len, union open_data *aead_data); |
94 | |
95 | // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It encrypts |
96 | // |plaintext_len| bytes from |plaintext| and writes them to |out_ciphertext|. |
97 | // Additional input parameters are passed in |aead_data->in|. The calculated tag |
98 | // value is over the computed ciphertext concatenated with |extra_ciphertext| |
99 | // and written to |aead_data->out.tag|. |
100 | extern void chacha20_poly1305_seal(uint8_t *out_ciphertext, |
101 | const uint8_t *plaintext, |
102 | size_t plaintext_len, const uint8_t *ad, |
103 | size_t ad_len, union seal_data *aead_data); |
104 | #else |
105 | static int asm_capable(void) { return 0; } |
106 | |
107 | |
108 | static void chacha20_poly1305_open(uint8_t *out_plaintext, |
109 | const uint8_t *ciphertext, |
110 | size_t plaintext_len, const uint8_t *ad, |
111 | size_t ad_len, union open_data *aead_data) {} |
112 | |
113 | static void chacha20_poly1305_seal(uint8_t *out_ciphertext, |
114 | const uint8_t *plaintext, |
115 | size_t plaintext_len, const uint8_t *ad, |
116 | size_t ad_len, union seal_data *aead_data) {} |
117 | #endif |
118 | |
119 | static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key, |
120 | size_t key_len, size_t tag_len) { |
121 | struct aead_chacha20_poly1305_ctx *c20_ctx = |
122 | (struct aead_chacha20_poly1305_ctx *)&ctx->state; |
123 | |
124 | if (tag_len == 0) { |
125 | tag_len = POLY1305_TAG_LEN; |
126 | } |
127 | |
128 | if (tag_len > POLY1305_TAG_LEN) { |
129 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
130 | return 0; |
131 | } |
132 | |
133 | if (key_len != sizeof(c20_ctx->key)) { |
134 | return 0; // internal error - EVP_AEAD_CTX_init should catch this. |
135 | } |
136 | |
137 | OPENSSL_memcpy(c20_ctx->key, key, key_len); |
138 | ctx->tag_len = tag_len; |
139 | |
140 | return 1; |
141 | } |
142 | |
143 | static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {} |
144 | |
145 | static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) { |
146 | uint8_t length_bytes[8]; |
147 | |
148 | for (unsigned i = 0; i < sizeof(length_bytes); i++) { |
149 | length_bytes[i] = data_len; |
150 | data_len >>= 8; |
151 | } |
152 | |
153 | CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); |
154 | } |
155 | |
156 | // calc_tag fills |tag| with the authentication tag for the given inputs. |
157 | static void calc_tag(uint8_t tag[POLY1305_TAG_LEN], const uint8_t *key, |
158 | const uint8_t nonce[12], const uint8_t *ad, size_t ad_len, |
159 | const uint8_t *ciphertext, size_t ciphertext_len, |
160 | const uint8_t *, |
161 | size_t ) { |
162 | alignas(16) uint8_t poly1305_key[32]; |
163 | OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key)); |
164 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, nonce, |
165 | 0); |
166 | |
167 | static const uint8_t padding[16] = { 0 }; // Padding is all zeros. |
168 | poly1305_state ctx; |
169 | CRYPTO_poly1305_init(&ctx, poly1305_key); |
170 | CRYPTO_poly1305_update(&ctx, ad, ad_len); |
171 | if (ad_len % 16 != 0) { |
172 | CRYPTO_poly1305_update(&ctx, padding, sizeof(padding) - (ad_len % 16)); |
173 | } |
174 | CRYPTO_poly1305_update(&ctx, ciphertext, ciphertext_len); |
175 | CRYPTO_poly1305_update(&ctx, ciphertext_extra, ciphertext_extra_len); |
176 | const size_t ciphertext_total = ciphertext_len + ciphertext_extra_len; |
177 | if (ciphertext_total % 16 != 0) { |
178 | CRYPTO_poly1305_update(&ctx, padding, |
179 | sizeof(padding) - (ciphertext_total % 16)); |
180 | } |
181 | poly1305_update_length(&ctx, ad_len); |
182 | poly1305_update_length(&ctx, ciphertext_total); |
183 | CRYPTO_poly1305_finish(&ctx, tag); |
184 | } |
185 | |
186 | static int chacha20_poly1305_seal_scatter( |
187 | const uint8_t *key, uint8_t *out, uint8_t *out_tag, |
188 | size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce, |
189 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *, |
190 | size_t , const uint8_t *ad, size_t ad_len, size_t tag_len) { |
191 | if (extra_in_len + tag_len < tag_len) { |
192 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
193 | return 0; |
194 | } |
195 | if (max_out_tag_len < tag_len + extra_in_len) { |
196 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
197 | return 0; |
198 | } |
199 | if (nonce_len != 12) { |
200 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); |
201 | return 0; |
202 | } |
203 | |
204 | // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow |
205 | // individual operations that work on more than 256GB at a time. |
206 | // |in_len_64| is needed because, on 32-bit platforms, size_t is only |
207 | // 32-bits and this produces a warning because it's always false. |
208 | // Casting to uint64_t inside the conditional is not sufficient to stop |
209 | // the warning. |
210 | const uint64_t in_len_64 = in_len; |
211 | if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) { |
212 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
213 | return 0; |
214 | } |
215 | |
216 | if (max_out_tag_len < tag_len) { |
217 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); |
218 | return 0; |
219 | } |
220 | |
221 | // The the extra input is given, it is expected to be very short and so is |
222 | // encrypted byte-by-byte first. |
223 | if (extra_in_len) { |
224 | static const size_t kChaChaBlockSize = 64; |
225 | uint32_t block_counter = 1 + (in_len / kChaChaBlockSize); |
226 | size_t offset = in_len % kChaChaBlockSize; |
227 | uint8_t block[64 /* kChaChaBlockSize */]; |
228 | |
229 | for (size_t done = 0; done < extra_in_len; block_counter++) { |
230 | memset(block, 0, sizeof(block)); |
231 | CRYPTO_chacha_20(block, block, sizeof(block), key, nonce, |
232 | block_counter); |
233 | for (size_t i = offset; i < sizeof(block) && done < extra_in_len; |
234 | i++, done++) { |
235 | out_tag[done] = extra_in[done] ^ block[i]; |
236 | } |
237 | offset = 0; |
238 | } |
239 | } |
240 | |
241 | union seal_data data; |
242 | if (asm_capable()) { |
243 | OPENSSL_memcpy(data.in.key, key, 32); |
244 | data.in.counter = 0; |
245 | OPENSSL_memcpy(data.in.nonce, nonce, 12); |
246 | data.in.extra_ciphertext = out_tag; |
247 | data.in.extra_ciphertext_len = extra_in_len; |
248 | chacha20_poly1305_seal(out, in, in_len, ad, ad_len, &data); |
249 | } else { |
250 | CRYPTO_chacha_20(out, in, in_len, key, nonce, 1); |
251 | calc_tag(data.out.tag, key, nonce, ad, ad_len, out, in_len, out_tag, |
252 | extra_in_len); |
253 | } |
254 | |
255 | OPENSSL_memcpy(out_tag + extra_in_len, data.out.tag, tag_len); |
256 | *out_tag_len = extra_in_len + tag_len; |
257 | return 1; |
258 | } |
259 | |
260 | static int aead_chacha20_poly1305_seal_scatter( |
261 | const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag, |
262 | size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce, |
263 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *, |
264 | size_t , const uint8_t *ad, size_t ad_len) { |
265 | const struct aead_chacha20_poly1305_ctx *c20_ctx = |
266 | (struct aead_chacha20_poly1305_ctx *)&ctx->state; |
267 | |
268 | return chacha20_poly1305_seal_scatter( |
269 | c20_ctx->key, out, out_tag, out_tag_len, max_out_tag_len, nonce, |
270 | nonce_len, in, in_len, extra_in, extra_in_len, ad, ad_len, ctx->tag_len); |
271 | } |
272 | |
273 | static int aead_xchacha20_poly1305_seal_scatter( |
274 | const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag, |
275 | size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce, |
276 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *, |
277 | size_t , const uint8_t *ad, size_t ad_len) { |
278 | const struct aead_chacha20_poly1305_ctx *c20_ctx = |
279 | (struct aead_chacha20_poly1305_ctx *)&ctx->state; |
280 | |
281 | if (nonce_len != 24) { |
282 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); |
283 | return 0; |
284 | } |
285 | |
286 | alignas(4) uint8_t derived_key[32]; |
287 | alignas(4) uint8_t derived_nonce[12]; |
288 | CRYPTO_hchacha20(derived_key, c20_ctx->key, nonce); |
289 | OPENSSL_memset(derived_nonce, 0, 4); |
290 | OPENSSL_memcpy(&derived_nonce[4], &nonce[16], 8); |
291 | |
292 | return chacha20_poly1305_seal_scatter( |
293 | derived_key, out, out_tag, out_tag_len, max_out_tag_len, |
294 | derived_nonce, sizeof(derived_nonce), in, in_len, extra_in, extra_in_len, |
295 | ad, ad_len, ctx->tag_len); |
296 | } |
297 | |
298 | static int chacha20_poly1305_open_gather( |
299 | const uint8_t *key, uint8_t *out, const uint8_t *nonce, |
300 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag, |
301 | size_t in_tag_len, const uint8_t *ad, size_t ad_len, size_t tag_len) { |
302 | if (nonce_len != 12) { |
303 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); |
304 | return 0; |
305 | } |
306 | |
307 | if (in_tag_len != tag_len) { |
308 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
309 | return 0; |
310 | } |
311 | |
312 | // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow |
313 | // individual operations that work on more than 256GB at a time. |
314 | // |in_len_64| is needed because, on 32-bit platforms, size_t is only |
315 | // 32-bits and this produces a warning because it's always false. |
316 | // Casting to uint64_t inside the conditional is not sufficient to stop |
317 | // the warning. |
318 | const uint64_t in_len_64 = in_len; |
319 | if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) { |
320 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); |
321 | return 0; |
322 | } |
323 | |
324 | union open_data data; |
325 | if (asm_capable()) { |
326 | OPENSSL_memcpy(data.in.key, key, 32); |
327 | data.in.counter = 0; |
328 | OPENSSL_memcpy(data.in.nonce, nonce, 12); |
329 | chacha20_poly1305_open(out, in, in_len, ad, ad_len, &data); |
330 | } else { |
331 | calc_tag(data.out.tag, key, nonce, ad, ad_len, in, in_len, NULL, 0); |
332 | CRYPTO_chacha_20(out, in, in_len, key, nonce, 1); |
333 | } |
334 | |
335 | if (CRYPTO_memcmp(data.out.tag, in_tag, tag_len) != 0) { |
336 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); |
337 | return 0; |
338 | } |
339 | |
340 | return 1; |
341 | } |
342 | |
343 | static int aead_chacha20_poly1305_open_gather( |
344 | const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce, |
345 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag, |
346 | size_t in_tag_len, const uint8_t *ad, size_t ad_len) { |
347 | const struct aead_chacha20_poly1305_ctx *c20_ctx = |
348 | (struct aead_chacha20_poly1305_ctx *)&ctx->state; |
349 | |
350 | return chacha20_poly1305_open_gather(c20_ctx->key, out, nonce, nonce_len, in, |
351 | in_len, in_tag, in_tag_len, ad, ad_len, |
352 | ctx->tag_len); |
353 | } |
354 | |
355 | static int aead_xchacha20_poly1305_open_gather( |
356 | const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce, |
357 | size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag, |
358 | size_t in_tag_len, const uint8_t *ad, size_t ad_len) { |
359 | const struct aead_chacha20_poly1305_ctx *c20_ctx = |
360 | (struct aead_chacha20_poly1305_ctx *)&ctx->state; |
361 | |
362 | if (nonce_len != 24) { |
363 | OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); |
364 | return 0; |
365 | } |
366 | |
367 | alignas(4) uint8_t derived_key[32]; |
368 | alignas(4) uint8_t derived_nonce[12]; |
369 | CRYPTO_hchacha20(derived_key, c20_ctx->key, nonce); |
370 | OPENSSL_memset(derived_nonce, 0, 4); |
371 | OPENSSL_memcpy(&derived_nonce[4], &nonce[16], 8); |
372 | |
373 | return chacha20_poly1305_open_gather( |
374 | derived_key, out, derived_nonce, sizeof(derived_nonce), in, in_len, |
375 | in_tag, in_tag_len, ad, ad_len, ctx->tag_len); |
376 | } |
377 | |
378 | static const EVP_AEAD aead_chacha20_poly1305 = { |
379 | 32, // key len |
380 | 12, // nonce len |
381 | POLY1305_TAG_LEN, // overhead |
382 | POLY1305_TAG_LEN, // max tag length |
383 | 1, // seal_scatter_supports_extra_in |
384 | |
385 | aead_chacha20_poly1305_init, |
386 | NULL, // init_with_direction |
387 | aead_chacha20_poly1305_cleanup, |
388 | NULL /* open */, |
389 | aead_chacha20_poly1305_seal_scatter, |
390 | aead_chacha20_poly1305_open_gather, |
391 | NULL, // get_iv |
392 | NULL, // tag_len |
393 | }; |
394 | |
395 | static const EVP_AEAD aead_xchacha20_poly1305 = { |
396 | 32, // key len |
397 | 24, // nonce len |
398 | POLY1305_TAG_LEN, // overhead |
399 | POLY1305_TAG_LEN, // max tag length |
400 | 1, // seal_scatter_supports_extra_in |
401 | |
402 | aead_chacha20_poly1305_init, |
403 | NULL, // init_with_direction |
404 | aead_chacha20_poly1305_cleanup, |
405 | NULL /* open */, |
406 | aead_xchacha20_poly1305_seal_scatter, |
407 | aead_xchacha20_poly1305_open_gather, |
408 | NULL, // get_iv |
409 | NULL, // tag_len |
410 | }; |
411 | |
412 | const EVP_AEAD *EVP_aead_chacha20_poly1305(void) { |
413 | return &aead_chacha20_poly1305; |
414 | } |
415 | |
416 | const EVP_AEAD *EVP_aead_xchacha20_poly1305(void) { |
417 | return &aead_xchacha20_poly1305; |
418 | } |
419 | |