1 /* $OpenBSD: e_chacha20poly1305.c,v 1.36 2024/05/22 14:02:08 tb Exp $ */
2 
3 /*
4  * Copyright (c) 2022 Joel Sing <jsing@openbsd.org>
5  * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org>
6  * Copyright (c) 2014, Google Inc.
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
17  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <limits.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include <openssl/opensslconf.h>
26 
27 #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
28 
29 #include <openssl/err.h>
30 #include <openssl/evp.h>
31 #include <openssl/chacha.h>
32 #include <openssl/poly1305.h>
33 
34 #include "bytestring.h"
35 #include "evp_local.h"
36 
37 #define POLY1305_TAG_LEN 16
38 
39 #define CHACHA20_CONSTANT_LEN 4
40 #define CHACHA20_IV_LEN 8
41 #define CHACHA20_NONCE_LEN (CHACHA20_CONSTANT_LEN + CHACHA20_IV_LEN)
42 #define XCHACHA20_NONCE_LEN 24
43 
44 struct aead_chacha20_poly1305_ctx {
45 	unsigned char key[32];
46 	unsigned char tag_len;
47 };
48 
49 static int
aead_chacha20_poly1305_init(EVP_AEAD_CTX * ctx,const unsigned char * key,size_t key_len,size_t tag_len)50 aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const unsigned char *key,
51     size_t key_len, size_t tag_len)
52 {
53 	struct aead_chacha20_poly1305_ctx *c20_ctx;
54 
55 	if (tag_len == 0)
56 		tag_len = POLY1305_TAG_LEN;
57 
58 	if (tag_len > POLY1305_TAG_LEN) {
59 		EVPerror(EVP_R_TOO_LARGE);
60 		return 0;
61 	}
62 
63 	/* Internal error - EVP_AEAD_CTX_init should catch this. */
64 	if (key_len != sizeof(c20_ctx->key))
65 		return 0;
66 
67 	c20_ctx = malloc(sizeof(struct aead_chacha20_poly1305_ctx));
68 	if (c20_ctx == NULL)
69 		return 0;
70 
71 	memcpy(&c20_ctx->key[0], key, key_len);
72 	c20_ctx->tag_len = tag_len;
73 	ctx->aead_state = c20_ctx;
74 
75 	return 1;
76 }
77 
78 static void
aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX * ctx)79 aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx)
80 {
81 	struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
82 
83 	freezero(c20_ctx, sizeof(*c20_ctx));
84 }
85 
86 static void
poly1305_update_with_length(poly1305_state * poly1305,const unsigned char * data,size_t data_len)87 poly1305_update_with_length(poly1305_state *poly1305,
88     const unsigned char *data, size_t data_len)
89 {
90 	size_t j = data_len;
91 	unsigned char length_bytes[8];
92 	unsigned i;
93 
94 	for (i = 0; i < sizeof(length_bytes); i++) {
95 		length_bytes[i] = j;
96 		j >>= 8;
97 	}
98 
99 	if (data != NULL)
100 		CRYPTO_poly1305_update(poly1305, data, data_len);
101 	CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
102 }
103 
104 static void
poly1305_pad16(poly1305_state * poly1305,size_t data_len)105 poly1305_pad16(poly1305_state *poly1305, size_t data_len)
106 {
107 	static const unsigned char zero_pad16[16];
108 	size_t pad_len;
109 
110 	/* pad16() is defined in RFC 8439 2.8.1. */
111 	if ((pad_len = data_len % 16) == 0)
112 		return;
113 
114 	CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len);
115 }
116 
117 static void
poly1305_update_with_pad16(poly1305_state * poly1305,const unsigned char * data,size_t data_len)118 poly1305_update_with_pad16(poly1305_state *poly1305,
119     const unsigned char *data, size_t data_len)
120 {
121 	CRYPTO_poly1305_update(poly1305, data, data_len);
122 	poly1305_pad16(poly1305, data_len);
123 }
124 
125 static int
aead_chacha20_poly1305_seal(const EVP_AEAD_CTX * ctx,unsigned char * out,size_t * out_len,size_t max_out_len,const unsigned char * nonce,size_t nonce_len,const unsigned char * in,size_t in_len,const unsigned char * ad,size_t ad_len)126 aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, unsigned char *out,
127     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
128     size_t nonce_len, const unsigned char *in, size_t in_len,
129     const unsigned char *ad, size_t ad_len)
130 {
131 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
132 	unsigned char poly1305_key[32];
133 	poly1305_state poly1305;
134 	const unsigned char *iv;
135 	uint64_t ctr;
136 
137 	if (max_out_len < in_len + c20_ctx->tag_len) {
138 		EVPerror(EVP_R_BUFFER_TOO_SMALL);
139 		return 0;
140 	}
141 
142 	if (nonce_len != ctx->aead->nonce_len) {
143 		EVPerror(EVP_R_IV_TOO_LARGE);
144 		return 0;
145 	}
146 
147 	ctr = (uint64_t)((uint32_t)(nonce[0]) | (uint32_t)(nonce[1]) << 8 |
148 	    (uint32_t)(nonce[2]) << 16 | (uint32_t)(nonce[3]) << 24) << 32;
149 	iv = nonce + CHACHA20_CONSTANT_LEN;
150 
151 	memset(poly1305_key, 0, sizeof(poly1305_key));
152 	CRYPTO_chacha_20(poly1305_key, poly1305_key,
153 	    sizeof(poly1305_key), c20_ctx->key, iv, ctr);
154 
155 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
156 	poly1305_update_with_pad16(&poly1305, ad, ad_len);
157 	CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, iv, ctr + 1);
158 	poly1305_update_with_pad16(&poly1305, out, in_len);
159 	poly1305_update_with_length(&poly1305, NULL, ad_len);
160 	poly1305_update_with_length(&poly1305, NULL, in_len);
161 
162 	if (c20_ctx->tag_len != POLY1305_TAG_LEN) {
163 		unsigned char tag[POLY1305_TAG_LEN];
164 		CRYPTO_poly1305_finish(&poly1305, tag);
165 		memcpy(out + in_len, tag, c20_ctx->tag_len);
166 		*out_len = in_len + c20_ctx->tag_len;
167 		return 1;
168 	}
169 
170 	CRYPTO_poly1305_finish(&poly1305, out + in_len);
171 	*out_len = in_len + POLY1305_TAG_LEN;
172 	return 1;
173 }
174 
175 static int
aead_chacha20_poly1305_open(const EVP_AEAD_CTX * ctx,unsigned char * out,size_t * out_len,size_t max_out_len,const unsigned char * nonce,size_t nonce_len,const unsigned char * in,size_t in_len,const unsigned char * ad,size_t ad_len)176 aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, unsigned char *out,
177     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
178     size_t nonce_len, const unsigned char *in, size_t in_len,
179     const unsigned char *ad, size_t ad_len)
180 {
181 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
182 	unsigned char mac[POLY1305_TAG_LEN];
183 	unsigned char poly1305_key[32];
184 	const unsigned char *iv = nonce;
185 	poly1305_state poly1305;
186 	size_t plaintext_len;
187 	uint64_t ctr = 0;
188 
189 	if (in_len < c20_ctx->tag_len) {
190 		EVPerror(EVP_R_BAD_DECRYPT);
191 		return 0;
192 	}
193 
194 	if (nonce_len != ctx->aead->nonce_len) {
195 		EVPerror(EVP_R_IV_TOO_LARGE);
196 		return 0;
197 	}
198 
199 	plaintext_len = in_len - c20_ctx->tag_len;
200 
201 	if (max_out_len < plaintext_len) {
202 		EVPerror(EVP_R_BUFFER_TOO_SMALL);
203 		return 0;
204 	}
205 
206 	ctr = (uint64_t)((uint32_t)(nonce[0]) | (uint32_t)(nonce[1]) << 8 |
207 	    (uint32_t)(nonce[2]) << 16 | (uint32_t)(nonce[3]) << 24) << 32;
208 	iv = nonce + CHACHA20_CONSTANT_LEN;
209 
210 	memset(poly1305_key, 0, sizeof(poly1305_key));
211 	CRYPTO_chacha_20(poly1305_key, poly1305_key,
212 	    sizeof(poly1305_key), c20_ctx->key, iv, ctr);
213 
214 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
215 	poly1305_update_with_pad16(&poly1305, ad, ad_len);
216 	poly1305_update_with_pad16(&poly1305, in, plaintext_len);
217 	poly1305_update_with_length(&poly1305, NULL, ad_len);
218 	poly1305_update_with_length(&poly1305, NULL, plaintext_len);
219 
220 	CRYPTO_poly1305_finish(&poly1305, mac);
221 
222 	if (timingsafe_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) {
223 		EVPerror(EVP_R_BAD_DECRYPT);
224 		return 0;
225 	}
226 
227 	CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, iv, ctr + 1);
228 	*out_len = plaintext_len;
229 	return 1;
230 }
231 
232 static int
aead_xchacha20_poly1305_seal(const EVP_AEAD_CTX * ctx,unsigned char * out,size_t * out_len,size_t max_out_len,const unsigned char * nonce,size_t nonce_len,const unsigned char * in,size_t in_len,const unsigned char * ad,size_t ad_len)233 aead_xchacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, unsigned char *out,
234     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
235     size_t nonce_len, const unsigned char *in, size_t in_len,
236     const unsigned char *ad, size_t ad_len)
237 {
238 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
239 	unsigned char poly1305_key[32];
240 	unsigned char subkey[32];
241 	poly1305_state poly1305;
242 
243 	if (max_out_len < in_len + c20_ctx->tag_len) {
244 		EVPerror(EVP_R_BUFFER_TOO_SMALL);
245 		return 0;
246 	}
247 
248 	if (nonce_len != ctx->aead->nonce_len) {
249 		EVPerror(EVP_R_IV_TOO_LARGE);
250 		return 0;
251 	}
252 
253 	CRYPTO_hchacha_20(subkey, c20_ctx->key, nonce);
254 
255 	CRYPTO_chacha_20(out, in, in_len, subkey, nonce + 16, 1);
256 
257 	memset(poly1305_key, 0, sizeof(poly1305_key));
258 	CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
259 	    subkey, nonce + 16, 0);
260 
261 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
262 	poly1305_update_with_pad16(&poly1305, ad, ad_len);
263 	poly1305_update_with_pad16(&poly1305, out, in_len);
264 	poly1305_update_with_length(&poly1305, NULL, ad_len);
265 	poly1305_update_with_length(&poly1305, NULL, in_len);
266 
267 	if (c20_ctx->tag_len != POLY1305_TAG_LEN) {
268 		unsigned char tag[POLY1305_TAG_LEN];
269 		CRYPTO_poly1305_finish(&poly1305, tag);
270 		memcpy(out + in_len, tag, c20_ctx->tag_len);
271 		*out_len = in_len + c20_ctx->tag_len;
272 		return 1;
273 	}
274 
275 	CRYPTO_poly1305_finish(&poly1305, out + in_len);
276 	*out_len = in_len + POLY1305_TAG_LEN;
277 	return 1;
278 }
279 
280 static int
aead_xchacha20_poly1305_open(const EVP_AEAD_CTX * ctx,unsigned char * out,size_t * out_len,size_t max_out_len,const unsigned char * nonce,size_t nonce_len,const unsigned char * in,size_t in_len,const unsigned char * ad,size_t ad_len)281 aead_xchacha20_poly1305_open(const EVP_AEAD_CTX *ctx, unsigned char *out,
282     size_t *out_len, size_t max_out_len, const unsigned char *nonce,
283     size_t nonce_len, const unsigned char *in, size_t in_len,
284     const unsigned char *ad, size_t ad_len)
285 {
286 	const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state;
287 	unsigned char mac[POLY1305_TAG_LEN];
288 	unsigned char poly1305_key[32];
289 	unsigned char subkey[32];
290 	poly1305_state poly1305;
291 	size_t plaintext_len;
292 
293 	if (in_len < c20_ctx->tag_len) {
294 		EVPerror(EVP_R_BAD_DECRYPT);
295 		return 0;
296 	}
297 
298 	if (nonce_len != ctx->aead->nonce_len) {
299 		EVPerror(EVP_R_IV_TOO_LARGE);
300 		return 0;
301 	}
302 
303 	plaintext_len = in_len - c20_ctx->tag_len;
304 
305 	if (max_out_len < plaintext_len) {
306 		EVPerror(EVP_R_BUFFER_TOO_SMALL);
307 		return 0;
308 	}
309 
310 	CRYPTO_hchacha_20(subkey, c20_ctx->key, nonce);
311 
312 	memset(poly1305_key, 0, sizeof(poly1305_key));
313 	CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key),
314 	    subkey, nonce + 16, 0);
315 
316 	CRYPTO_poly1305_init(&poly1305, poly1305_key);
317 	poly1305_update_with_pad16(&poly1305, ad, ad_len);
318 	poly1305_update_with_pad16(&poly1305, in, plaintext_len);
319 	poly1305_update_with_length(&poly1305, NULL, ad_len);
320 	poly1305_update_with_length(&poly1305, NULL, plaintext_len);
321 
322 	CRYPTO_poly1305_finish(&poly1305, mac);
323 	if (timingsafe_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) {
324 		EVPerror(EVP_R_BAD_DECRYPT);
325 		return 0;
326 	}
327 
328 	CRYPTO_chacha_20(out, in, plaintext_len, subkey, nonce + 16, 1);
329 
330 	*out_len = plaintext_len;
331 	return 1;
332 }
333 
334 /* RFC 8439 */
335 static const EVP_AEAD aead_chacha20_poly1305 = {
336 	.key_len = 32,
337 	.nonce_len = CHACHA20_NONCE_LEN,
338 	.overhead = POLY1305_TAG_LEN,
339 	.max_tag_len = POLY1305_TAG_LEN,
340 
341 	.init = aead_chacha20_poly1305_init,
342 	.cleanup = aead_chacha20_poly1305_cleanup,
343 	.seal = aead_chacha20_poly1305_seal,
344 	.open = aead_chacha20_poly1305_open,
345 };
346 
347 const EVP_AEAD *
EVP_aead_chacha20_poly1305(void)348 EVP_aead_chacha20_poly1305(void)
349 {
350 	return &aead_chacha20_poly1305;
351 }
352 LCRYPTO_ALIAS(EVP_aead_chacha20_poly1305);
353 
354 static const EVP_AEAD aead_xchacha20_poly1305 = {
355 	.key_len = 32,
356 	.nonce_len = XCHACHA20_NONCE_LEN,
357 	.overhead = POLY1305_TAG_LEN,
358 	.max_tag_len = POLY1305_TAG_LEN,
359 
360 	.init = aead_chacha20_poly1305_init,
361 	.cleanup = aead_chacha20_poly1305_cleanup,
362 	.seal = aead_xchacha20_poly1305_seal,
363 	.open = aead_xchacha20_poly1305_open,
364 };
365 
366 const EVP_AEAD *
EVP_aead_xchacha20_poly1305(void)367 EVP_aead_xchacha20_poly1305(void)
368 {
369 	return &aead_xchacha20_poly1305;
370 }
371 LCRYPTO_ALIAS(EVP_aead_xchacha20_poly1305);
372 
373 struct chacha20_poly1305_ctx {
374 	ChaCha_ctx chacha;
375 	poly1305_state poly1305;
376 
377 	unsigned char key[32];
378 	unsigned char nonce[CHACHA20_NONCE_LEN];
379 	size_t nonce_len;
380 	unsigned char tag[POLY1305_TAG_LEN];
381 	size_t tag_len;
382 
383 	size_t ad_len;
384 	size_t in_len;
385 
386 	int in_ad;
387 	int started;
388 };
389 
390 static int
chacha20_poly1305_init(EVP_CIPHER_CTX * ctx,const unsigned char * key,const unsigned char * iv,int encrypt)391 chacha20_poly1305_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
392     const unsigned char *iv, int encrypt)
393 {
394 	struct chacha20_poly1305_ctx *cpx = ctx->cipher_data;
395 	uint8_t *data;
396 	CBB cbb;
397 	int ret = 0;
398 
399 	memset(&cbb, 0, sizeof(cbb));
400 
401 	if (key == NULL && iv == NULL)
402 		goto done;
403 
404 	cpx->started = 0;
405 
406 	if (key != NULL)
407 		memcpy(cpx->key, key, sizeof(cpx->key));
408 
409 	if (iv != NULL) {
410 		/*
411 		 * Left zero pad if configured nonce length is less than ChaCha
412 		 * nonce length.
413 		 */
414 		if (!CBB_init_fixed(&cbb, cpx->nonce, sizeof(cpx->nonce)))
415 			goto err;
416 		if (!CBB_add_space(&cbb, &data, sizeof(cpx->nonce) - cpx->nonce_len))
417 			goto err;
418 		if (!CBB_add_bytes(&cbb, iv, cpx->nonce_len))
419 			goto err;
420 		if (!CBB_finish(&cbb, NULL, NULL))
421 			goto err;
422 	}
423 
424  done:
425 	ret = 1;
426 
427  err:
428 	CBB_cleanup(&cbb);
429 
430 	return ret;
431 }
432 
433 static int
chacha20_poly1305_cipher(EVP_CIPHER_CTX * ctx,unsigned char * out,const unsigned char * in,size_t len)434 chacha20_poly1305_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
435     const unsigned char *in, size_t len)
436 {
437 	struct chacha20_poly1305_ctx *cpx = ctx->cipher_data;
438 
439 	/*
440 	 * Since we're making AEAD work within the constraints of EVP_CIPHER...
441 	 * If in is non-NULL then this is an update, while if in is NULL then
442 	 * this is a final. If in is non-NULL but out is NULL, then the input
443 	 * being provided is associated data. Plus we have to handle encryption
444 	 * (sealing) and decryption (opening) in the same function.
445 	 */
446 
447 	if (!cpx->started) {
448 		unsigned char poly1305_key[32];
449 		const unsigned char *iv;
450 		uint64_t ctr;
451 
452 		ctr = (uint64_t)((uint32_t)(cpx->nonce[0]) |
453 		    (uint32_t)(cpx->nonce[1]) << 8 |
454 		    (uint32_t)(cpx->nonce[2]) << 16 |
455 		    (uint32_t)(cpx->nonce[3]) << 24) << 32;
456 		iv = cpx->nonce + CHACHA20_CONSTANT_LEN;
457 
458 		ChaCha_set_key(&cpx->chacha, cpx->key, 8 * sizeof(cpx->key));
459 		ChaCha_set_iv(&cpx->chacha, iv, NULL);
460 
461 		/* See chacha.c for details re handling of counter. */
462 		cpx->chacha.input[12] = (uint32_t)ctr;
463 		cpx->chacha.input[13] = (uint32_t)(ctr >> 32);
464 
465 		memset(poly1305_key, 0, sizeof(poly1305_key));
466 		ChaCha(&cpx->chacha, poly1305_key, poly1305_key,
467 		    sizeof(poly1305_key));
468 		CRYPTO_poly1305_init(&cpx->poly1305, poly1305_key);
469 
470 		/* Mark remaining key block as used. */
471 		cpx->chacha.unused = 0;
472 
473 		cpx->ad_len = 0;
474 		cpx->in_len = 0;
475 		cpx->in_ad = 0;
476 
477 		cpx->started = 1;
478 	}
479 
480 	if (len > SIZE_MAX - cpx->in_len) {
481 		EVPerror(EVP_R_TOO_LARGE);
482 		return -1;
483 	}
484 
485 	/* Disallow authenticated data after plaintext/ciphertext. */
486 	if (cpx->in_len > 0 && in != NULL && out == NULL)
487 		return -1;
488 
489 	if (cpx->in_ad && (in == NULL || out != NULL)) {
490 		poly1305_pad16(&cpx->poly1305, cpx->ad_len);
491 		cpx->in_ad = 0;
492 	}
493 
494 	/* Update with AD or plaintext/ciphertext. */
495 	if (in != NULL) {
496 		if (!ctx->encrypt || out == NULL)
497 			CRYPTO_poly1305_update(&cpx->poly1305, in, len);
498 		if (out == NULL) {
499 			cpx->ad_len += len;
500 			cpx->in_ad = 1;
501 		} else {
502 			ChaCha(&cpx->chacha, out, in, len);
503 			cpx->in_len += len;
504 		}
505 		if (ctx->encrypt && out != NULL)
506 			CRYPTO_poly1305_update(&cpx->poly1305, out, len);
507 
508 		return len;
509 	}
510 
511 	/* Final. */
512 	poly1305_pad16(&cpx->poly1305, cpx->in_len);
513 	poly1305_update_with_length(&cpx->poly1305, NULL, cpx->ad_len);
514 	poly1305_update_with_length(&cpx->poly1305, NULL, cpx->in_len);
515 
516 	if (ctx->encrypt) {
517 		CRYPTO_poly1305_finish(&cpx->poly1305, cpx->tag);
518 		cpx->tag_len = sizeof(cpx->tag);
519 	} else {
520 		unsigned char tag[POLY1305_TAG_LEN];
521 
522 		/* Ensure that a tag has been provided. */
523 		if (cpx->tag_len <= 0)
524 			return -1;
525 
526 		CRYPTO_poly1305_finish(&cpx->poly1305, tag);
527 		if (timingsafe_memcmp(tag, cpx->tag, cpx->tag_len) != 0)
528 			return -1;
529 	}
530 
531 	cpx->started = 0;
532 
533 	return len;
534 }
535 
536 static int
chacha20_poly1305_cleanup(EVP_CIPHER_CTX * ctx)537 chacha20_poly1305_cleanup(EVP_CIPHER_CTX *ctx)
538 {
539 	struct chacha20_poly1305_ctx *cpx = ctx->cipher_data;
540 
541 	explicit_bzero(cpx, sizeof(*cpx));
542 
543 	return 1;
544 }
545 
546 static int
chacha20_poly1305_ctrl(EVP_CIPHER_CTX * ctx,int type,int arg,void * ptr)547 chacha20_poly1305_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
548 {
549 	struct chacha20_poly1305_ctx *cpx = ctx->cipher_data;
550 
551 	switch (type) {
552 	case EVP_CTRL_INIT:
553 		memset(cpx, 0, sizeof(*cpx));
554 		cpx->nonce_len = sizeof(cpx->nonce);
555 		return 1;
556 
557 	case EVP_CTRL_AEAD_GET_IVLEN:
558 		if (cpx->nonce_len > INT_MAX)
559 			return 0;
560 		*(int *)ptr = (int)cpx->nonce_len;
561 		return 1;
562 
563 	case EVP_CTRL_AEAD_SET_IVLEN:
564 		if (arg <= 0 || arg > sizeof(cpx->nonce))
565 			return 0;
566 		cpx->nonce_len = arg;
567 		return 1;
568 
569 	case EVP_CTRL_AEAD_SET_TAG:
570 		if (ctx->encrypt)
571 			return 0;
572 		if (arg <= 0 || arg > sizeof(cpx->tag))
573 			return 0;
574 		if (ptr != NULL) {
575 			memcpy(cpx->tag, ptr, arg);
576 			cpx->tag_len = arg;
577 		}
578 		return 1;
579 
580 	case EVP_CTRL_AEAD_GET_TAG:
581 		if (!ctx->encrypt)
582 			return 0;
583 		if (arg <= 0 || arg > cpx->tag_len)
584 			return 0;
585 		memcpy(ptr, cpx->tag, arg);
586 		return 1;
587 
588 	case EVP_CTRL_AEAD_SET_IV_FIXED:
589 		if (arg != sizeof(cpx->nonce))
590 			return 0;
591 		memcpy(cpx->nonce, ptr, arg);
592 		return 1;
593 	}
594 
595 	return 0;
596 }
597 
598 static const EVP_CIPHER cipher_chacha20_poly1305 = {
599 	.nid = NID_chacha20_poly1305,
600 	.block_size = 1,
601 	.key_len = 32,
602 	.iv_len = 12,
603 	.flags = EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT |
604 	    EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_IV_LENGTH |
605 	    EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_CUSTOM_CIPHER |
606 	    EVP_CIPH_FLAG_DEFAULT_ASN1,
607 	.init = chacha20_poly1305_init,
608 	.do_cipher = chacha20_poly1305_cipher,
609 	.cleanup = chacha20_poly1305_cleanup,
610 	.ctx_size = sizeof(struct chacha20_poly1305_ctx),
611 	.ctrl = chacha20_poly1305_ctrl,
612 };
613 
614 const EVP_CIPHER *
EVP_chacha20_poly1305(void)615 EVP_chacha20_poly1305(void)
616 {
617 	return &cipher_chacha20_poly1305;
618 }
619 LCRYPTO_ALIAS(EVP_chacha20_poly1305);
620 
621 #endif  /* !OPENSSL_NO_CHACHA && !OPENSSL_NO_POLY1305 */
622