blob: 6510ff48ad44fbdbe35f1a62ee02bf52f9963264 [file] [log] [blame]
Adam Langleyde0b2022014-06-20 12:00:00 -07001/* 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
David Benjaminb7d63202022-07-26 13:25:02 -070017#include <assert.h>
Adam Langley2b2d66d2015-01-30 17:08:37 -080018#include <string.h>
19
Adam Langleyde0b2022014-06-20 12:00:00 -070020#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
Vlad Krasnovf2adafe2020-12-02 16:05:56 -050026#include "internal.h"
27#include "../chacha/internal.h"
Adam Langley2e2a2262017-05-03 13:23:37 -070028#include "../fipsmodule/cipher/internal.h"
Brian Smith7cae9f52016-01-17 20:12:57 -100029#include "../internal.h"
Adam Langleyde0b2022014-06-20 12:00:00 -070030
31struct aead_chacha20_poly1305_ctx {
David Benjamin8d200742017-08-01 15:02:26 -040032 uint8_t key[32];
33};
34
David Benjaminb7d63202022-07-26 13:25:02 -070035static_assert(sizeof(((EVP_AEAD_CTX *)NULL)->state) >=
36 sizeof(struct aead_chacha20_poly1305_ctx),
37 "AEAD state is too small");
38static_assert(alignof(union evp_aead_ctx_st_state) >=
39 alignof(struct aead_chacha20_poly1305_ctx),
40 "AEAD state has insufficient alignment");
Adam Langley35fb5912018-10-16 12:11:51 -070041
Adam Langleyde0b2022014-06-20 12:00:00 -070042static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
43 size_t key_len, size_t tag_len) {
Adam Langley35fb5912018-10-16 12:11:51 -070044 struct aead_chacha20_poly1305_ctx *c20_ctx =
45 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
Adam Langleyde0b2022014-06-20 12:00:00 -070046
47 if (tag_len == 0) {
48 tag_len = POLY1305_TAG_LEN;
49 }
50
51 if (tag_len > POLY1305_TAG_LEN) {
David Benjamin3570d732015-06-29 00:28:17 -040052 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyde0b2022014-06-20 12:00:00 -070053 return 0;
54 }
55
56 if (key_len != sizeof(c20_ctx->key)) {
David Benjamin808f8322017-08-18 14:06:02 -040057 return 0; // internal error - EVP_AEAD_CTX_init should catch this.
Adam Langleyde0b2022014-06-20 12:00:00 -070058 }
59
David Benjamin17cf2cb2016-12-13 01:07:13 -050060 OPENSSL_memcpy(c20_ctx->key, key, key_len);
Martin Kreichgauer18d9f282017-06-06 12:29:48 -070061 ctx->tag_len = tag_len;
Adam Langleyde0b2022014-06-20 12:00:00 -070062
63 return 1;
64}
65
Adam Langley35fb5912018-10-16 12:11:51 -070066static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) {}
Adam Langleyde0b2022014-06-20 12:00:00 -070067
Brian Smith3e23e4c2015-10-03 11:38:58 -100068static void poly1305_update_length(poly1305_state *poly1305, size_t data_len) {
Adam Langleyde0b2022014-06-20 12:00:00 -070069 uint8_t length_bytes[8];
Adam Langleyde0b2022014-06-20 12:00:00 -070070
Adam Langley2e839242017-01-19 15:12:44 -080071 for (unsigned i = 0; i < sizeof(length_bytes); i++) {
Brian Smith3e23e4c2015-10-03 11:38:58 -100072 length_bytes[i] = data_len;
73 data_len >>= 8;
Adam Langleyde0b2022014-06-20 12:00:00 -070074 }
75
Adam Langleyde0b2022014-06-20 12:00:00 -070076 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
77}
78
David Benjamin808f8322017-08-18 14:06:02 -040079// calc_tag fills |tag| with the authentication tag for the given inputs.
Adam Langley7c1f21a2018-08-07 17:04:54 -070080static void calc_tag(uint8_t tag[POLY1305_TAG_LEN], const uint8_t *key,
Adam Langley2e839242017-01-19 15:12:44 -080081 const uint8_t nonce[12], const uint8_t *ad, size_t ad_len,
Adam Langleyc66e3972017-06-28 13:33:08 -070082 const uint8_t *ciphertext, size_t ciphertext_len,
83 const uint8_t *ciphertext_extra,
84 size_t ciphertext_extra_len) {
Brian Smith7cae9f52016-01-17 20:12:57 -100085 alignas(16) uint8_t poly1305_key[32];
David Benjamin17cf2cb2016-12-13 01:07:13 -050086 OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key));
Adam Langley7c1f21a2018-08-07 17:04:54 -070087 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, nonce,
88 0);
Adam Langley2e839242017-01-19 15:12:44 -080089
David Benjamin808f8322017-08-18 14:06:02 -040090 static const uint8_t padding[16] = { 0 }; // Padding is all zeros.
Brian Smith3e23e4c2015-10-03 11:38:58 -100091 poly1305_state ctx;
92 CRYPTO_poly1305_init(&ctx, poly1305_key);
Adam Langleyc66e3972017-06-28 13:33:08 -070093 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 }
Adam Langley2e839242017-01-19 15:12:44 -0800104 poly1305_update_length(&ctx, ad_len);
Adam Langleyc66e3972017-06-28 13:33:08 -0700105 poly1305_update_length(&ctx, ciphertext_total);
Brian Smith3e23e4c2015-10-03 11:38:58 -1000106 CRYPTO_poly1305_finish(&ctx, tag);
107}
108
Adam Langley7c1f21a2018-08-07 17:04:54 -0700109static int chacha20_poly1305_seal_scatter(
110 const uint8_t *key, uint8_t *out, uint8_t *out_tag,
Martin Kreichgauer18d9f282017-06-06 12:29:48 -0700111 size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
Martin Kreichgauer74bce292017-06-23 14:49:22 -0700112 size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
Adam Langley7c1f21a2018-08-07 17:04:54 -0700113 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) {
Adam Langleyc66e3972017-06-28 13:33:08 -0700115 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
116 return 0;
117 }
Adam Langley7c1f21a2018-08-07 17:04:54 -0700118 if (max_out_tag_len < tag_len + extra_in_len) {
Adam Langleyc66e3972017-06-28 13:33:08 -0700119 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
120 return 0;
121 }
Adam Langley2e839242017-01-19 15:12:44 -0800122 if (nonce_len != 12) {
123 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
124 return 0;
125 }
126
David Benjamin808f8322017-08-18 14:06:02 -0400127 // |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.
Martin Kreichgauer18d9f282017-06-06 12:29:48 -0700133 const uint64_t in_len_64 = in_len;
Piotr Sikora537cfc32016-03-18 15:53:29 -0700134 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
David Benjamin3570d732015-06-29 00:28:17 -0400135 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyde0b2022014-06-20 12:00:00 -0700136 return 0;
137 }
138
Adam Langley7c1f21a2018-08-07 17:04:54 -0700139 if (max_out_tag_len < tag_len) {
David Benjamin3570d732015-06-29 00:28:17 -0400140 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
Adam Langleyde0b2022014-06-20 12:00:00 -0700141 return 0;
142 }
143
David Benjamin808f8322017-08-18 14:06:02 -0400144 // The the extra input is given, it is expected to be very short and so is
145 // encrypted byte-by-byte first.
Adam Langleyc66e3972017-06-28 13:33:08 -0700146 if (extra_in_len) {
147 static const size_t kChaChaBlockSize = 64;
David Benjamin9d64d8d2022-08-31 19:27:56 -0400148 uint32_t block_counter = (uint32_t)(1 + (in_len / kChaChaBlockSize));
Adam Langleyc66e3972017-06-28 13:33:08 -0700149 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));
Adam Langley7c1f21a2018-08-07 17:04:54 -0700154 CRYPTO_chacha_20(block, block, sizeof(block), key, nonce,
Adam Langleyc66e3972017-06-28 13:33:08 -0700155 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
Vlad Krasnovf2adafe2020-12-02 16:05:56 -0500164 union chacha20_poly1305_seal_data data;
165 if (chacha20_poly1305_asm_capable()) {
Adam Langley7c1f21a2018-08-07 17:04:54 -0700166 OPENSSL_memcpy(data.in.key, key, 32);
David Benjamin8d200742017-08-01 15:02:26 -0400167 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);
vkrasnov8d565582017-01-19 15:12:44 -0800172 } else {
Adam Langley7c1f21a2018-08-07 17:04:54 -0700173 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,
David Benjamin8d200742017-08-01 15:02:26 -0400175 extra_in_len);
vkrasnov8d565582017-01-19 15:12:44 -0800176 }
Brian Smith3e23e4c2015-10-03 11:38:58 -1000177
Adam Langley7c1f21a2018-08-07 17:04:54 -0700178 OPENSSL_memcpy(out_tag + extra_in_len, data.out.tag, tag_len);
179 *out_tag_len = extra_in_len + tag_len;
Adam Langleyde0b2022014-06-20 12:00:00 -0700180 return 1;
181}
182
Adam Langley7c1f21a2018-08-07 17:04:54 -0700183static 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) {
Adam Langley35fb5912018-10-16 12:11:51 -0700188 const struct aead_chacha20_poly1305_ctx *c20_ctx =
189 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
Adam Langleyde0b2022014-06-20 12:00:00 -0700190
Adam Langley7c1f21a2018-08-07 17:04:54 -0700191 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
196static 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) {
Adam Langley35fb5912018-10-16 12:11:51 -0700201 const struct aead_chacha20_poly1305_ctx *c20_ctx =
202 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
Adam Langley7c1f21a2018-08-07 17:04:54 -0700203
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
221static 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) {
Adam Langley2e839242017-01-19 15:12:44 -0800225 if (nonce_len != 12) {
226 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
227 return 0;
228 }
229
Adam Langley7c1f21a2018-08-07 17:04:54 -0700230 if (in_tag_len != tag_len) {
David Benjamin3570d732015-06-29 00:28:17 -0400231 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyde0b2022014-06-20 12:00:00 -0700232 return 0;
233 }
234
David Benjamin808f8322017-08-18 14:06:02 -0400235 // |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.
Martin Kreichgauer18d9f282017-06-06 12:29:48 -0700241 const uint64_t in_len_64 = in_len;
Piotr Sikora537cfc32016-03-18 15:53:29 -0700242 if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) {
David Benjamin3570d732015-06-29 00:28:17 -0400243 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
Adam Langleyde0b2022014-06-20 12:00:00 -0700244 return 0;
245 }
246
Vlad Krasnovf2adafe2020-12-02 16:05:56 -0500247 union chacha20_poly1305_open_data data;
248 if (chacha20_poly1305_asm_capable()) {
Adam Langley7c1f21a2018-08-07 17:04:54 -0700249 OPENSSL_memcpy(data.in.key, key, 32);
David Benjamin8d200742017-08-01 15:02:26 -0400250 data.in.counter = 0;
251 OPENSSL_memcpy(data.in.nonce, nonce, 12);
252 chacha20_poly1305_open(out, in, in_len, ad, ad_len, &data);
vkrasnov8d565582017-01-19 15:12:44 -0800253 } else {
Adam Langley7c1f21a2018-08-07 17:04:54 -0700254 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);
vkrasnov8d565582017-01-19 15:12:44 -0800256 }
257
Adam Langley7c1f21a2018-08-07 17:04:54 -0700258 if (CRYPTO_memcmp(data.out.tag, in_tag, tag_len) != 0) {
David Benjamin3570d732015-06-29 00:28:17 -0400259 OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
Adam Langleyde0b2022014-06-20 12:00:00 -0700260 return 0;
261 }
262
Adam Langleyde0b2022014-06-20 12:00:00 -0700263 return 1;
264}
265
Adam Langley7c1f21a2018-08-07 17:04:54 -0700266static 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) {
Adam Langley35fb5912018-10-16 12:11:51 -0700270 const struct aead_chacha20_poly1305_ctx *c20_ctx =
271 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
Adam Langley7c1f21a2018-08-07 17:04:54 -0700272
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
278static 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) {
Adam Langley35fb5912018-10-16 12:11:51 -0700282 const struct aead_chacha20_poly1305_ctx *c20_ctx =
283 (struct aead_chacha20_poly1305_ctx *)&ctx->state;
Adam Langley7c1f21a2018-08-07 17:04:54 -0700284
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
Adam Langleyde0b2022014-06-20 12:00:00 -0700301static const EVP_AEAD aead_chacha20_poly1305 = {
David Benjamin808f8322017-08-18 14:06:02 -0400302 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
Martin Kreichgauer74bce292017-06-23 14:49:22 -0700307
David Benjaminb34f5102015-02-28 03:59:33 -0500308 aead_chacha20_poly1305_init,
David Benjamin808f8322017-08-18 14:06:02 -0400309 NULL, // init_with_direction
David Benjaminb34f5102015-02-28 03:59:33 -0500310 aead_chacha20_poly1305_cleanup,
Martin Kreichgauer18d9f282017-06-06 12:29:48 -0700311 NULL /* open */,
312 aead_chacha20_poly1305_seal_scatter,
313 aead_chacha20_poly1305_open_gather,
David Benjamin808f8322017-08-18 14:06:02 -0400314 NULL, // get_iv
315 NULL, // tag_len
Adam Langleyde0b2022014-06-20 12:00:00 -0700316};
317
Adam Langley7c1f21a2018-08-07 17:04:54 -0700318static 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
David Benjamin8ffab722015-11-30 18:48:18 -0500335const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
David Benjaminc44d2f42014-08-20 16:24:00 -0400336 return &aead_chacha20_poly1305;
337}
Adam Langley7c1f21a2018-08-07 17:04:54 -0700338
339const EVP_AEAD *EVP_aead_xchacha20_poly1305(void) {
340 return &aead_xchacha20_poly1305;
341}