xref: /freebsd/sys/crypto/openssl/ossl_chacha20.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Netflix, Inc
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer,
11  *    without modification.
12  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14  *    redistribution must be conditioned upon including a substantially
15  *    similar Disclaimer requirement for further binary redistribution.
16  *
17  * NO WARRANTY
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGES.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/endian.h>
33 #include <sys/malloc.h>
34 #include <sys/time.h>
35 
36 #include <opencrypto/cryptodev.h>
37 
38 #include <crypto/openssl/ossl.h>
39 #include <crypto/openssl/ossl_chacha.h>
40 #include <crypto/openssl/ossl_cipher.h>
41 #include <crypto/openssl/ossl_poly1305.h>
42 
43 static ossl_cipher_process_t ossl_chacha20;
44 
45 struct ossl_cipher ossl_cipher_chacha20 = {
46 	.type = CRYPTO_CHACHA20,
47 	.blocksize = CHACHA_BLK_SIZE,
48 	.ivsize = CHACHA_CTR_SIZE,
49 
50 	.set_encrypt_key = NULL,
51 	.set_decrypt_key = NULL,
52 	.process = ossl_chacha20
53 };
54 
55 static int
56 ossl_chacha20(struct ossl_session_cipher *s, struct cryptop *crp,
57     const struct crypto_session_params *csp)
58 {
59 	_Alignas(8) unsigned int key[CHACHA_KEY_SIZE / 4];
60 	unsigned int counter[CHACHA_CTR_SIZE / 4];
61 	unsigned char block[CHACHA_BLK_SIZE];
62 	struct crypto_buffer_cursor cc_in, cc_out;
63 	const unsigned char *in, *inseg, *cipher_key;
64 	unsigned char *out, *outseg;
65 	size_t resid, todo, inlen, outlen;
66 	uint32_t next_counter;
67 	u_int i;
68 
69 	if (crp->crp_cipher_key != NULL)
70 		cipher_key = crp->crp_cipher_key;
71 	else
72 		cipher_key = csp->csp_cipher_key;
73 	for (i = 0; i < nitems(key); i++)
74 		key[i] = CHACHA_U8TOU32(cipher_key + i * 4);
75 	crypto_read_iv(crp, counter);
76 	for (i = 0; i < nitems(counter); i++)
77 		counter[i] = le32toh(counter[i]);
78 
79 	resid = crp->crp_payload_length;
80 	crypto_cursor_init(&cc_in, &crp->crp_buf);
81 	crypto_cursor_advance(&cc_in, crp->crp_payload_start);
82 	inseg = crypto_cursor_segment(&cc_in, &inlen);
83 	if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
84 		crypto_cursor_init(&cc_out, &crp->crp_obuf);
85 		crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
86 	} else
87 		cc_out = cc_in;
88 	outseg = crypto_cursor_segment(&cc_out, &outlen);
89 	while (resid >= CHACHA_BLK_SIZE) {
90 		if (inlen < CHACHA_BLK_SIZE) {
91 			crypto_cursor_copydata(&cc_in, CHACHA_BLK_SIZE, block);
92 			in = block;
93 			inlen = CHACHA_BLK_SIZE;
94 		} else
95 			in = inseg;
96 		if (outlen < CHACHA_BLK_SIZE) {
97 			out = block;
98 			outlen = CHACHA_BLK_SIZE;
99 		} else
100 			out = outseg;
101 
102 		/* Figure out how many blocks we can encrypt/decrypt at once. */
103 		todo = rounddown(MIN(resid, MIN(inlen, outlen)),
104 		    CHACHA_BLK_SIZE);
105 
106 #ifdef __LP64__
107 		/* ChaCha20_ctr32() assumes length is <= 4GB. */
108 		todo = (uint32_t)todo;
109 #endif
110 
111 		/* Truncate if the 32-bit counter would roll over. */
112 		next_counter = counter[0] + todo / CHACHA_BLK_SIZE;
113 		if (next_counter < counter[0]) {
114 			todo -= next_counter * CHACHA_BLK_SIZE;
115 			next_counter = 0;
116 		}
117 
118 		ChaCha20_ctr32(out, in, todo, key, counter);
119 
120 		counter[0] = next_counter;
121 		if (counter[0] == 0)
122 			counter[1]++;
123 
124 		if (out == block) {
125 			crypto_cursor_copyback(&cc_out, CHACHA_BLK_SIZE, block);
126 			outseg = crypto_cursor_segment(&cc_out, &outlen);
127 		} else {
128 			crypto_cursor_advance(&cc_out, todo);
129 			outseg += todo;
130 			outlen -= todo;
131 		}
132 		if (in == block) {
133 			inseg = crypto_cursor_segment(&cc_in, &inlen);
134 		} else {
135 			crypto_cursor_advance(&cc_in, todo);
136 			inseg += todo;
137 			inlen -= todo;
138 		}
139 		resid -= todo;
140 	}
141 
142 	if (resid > 0) {
143 		memset(block, 0, sizeof(block));
144 		crypto_cursor_copydata(&cc_in, resid, block);
145 		ChaCha20_ctr32(block, block, CHACHA_BLK_SIZE, key, counter);
146 		crypto_cursor_copyback(&cc_out, resid, block);
147 	}
148 
149 	explicit_bzero(block, sizeof(block));
150 	explicit_bzero(counter, sizeof(counter));
151 	explicit_bzero(key, sizeof(key));
152 	return (0);
153 }
154 
155 int
156 ossl_chacha20_poly1305_encrypt(struct cryptop *crp,
157     const struct crypto_session_params *csp)
158 {
159 	_Alignas(8) unsigned int key[CHACHA_KEY_SIZE / 4];
160 	unsigned int counter[CHACHA_CTR_SIZE / 4];
161 	_Alignas(8) unsigned char block[CHACHA_BLK_SIZE];
162 	unsigned char tag[POLY1305_HASH_LEN];
163 	POLY1305 auth_ctx;
164 	struct crypto_buffer_cursor cc_in, cc_out;
165 	const unsigned char *in, *inseg, *cipher_key;
166 	unsigned char *out, *outseg;
167 	size_t resid, todo, inlen, outlen;
168 	uint32_t next_counter;
169 	u_int i;
170 
171 	if (crp->crp_cipher_key != NULL)
172 		cipher_key = crp->crp_cipher_key;
173 	else
174 		cipher_key = csp->csp_cipher_key;
175 	for (i = 0; i < nitems(key); i++)
176 		key[i] = CHACHA_U8TOU32(cipher_key + i * 4);
177 
178 	memset(counter, 0, sizeof(counter));
179 	crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4);
180 	for (i = 1; i < nitems(counter); i++)
181 		counter[i] = le32toh(counter[i]);
182 
183 	/* Block 0 is used to generate the poly1305 key. */
184 	counter[0] = 0;
185 
186 	memset(block, 0, sizeof(block));
187 	ChaCha20_ctr32(block, block, sizeof(block), key, counter);
188 	Poly1305_Init(&auth_ctx, block);
189 
190 	/* MAC the AAD. */
191 	if (crp->crp_aad != NULL)
192 		Poly1305_Update(&auth_ctx, crp->crp_aad, crp->crp_aad_length);
193 	else
194 		crypto_apply(crp, crp->crp_aad_start, crp->crp_aad_length,
195 		    ossl_poly1305_update, &auth_ctx);
196 	if (crp->crp_aad_length % 16 != 0) {
197 		/* padding1 */
198 		memset(block, 0, 16);
199 		Poly1305_Update(&auth_ctx, block,
200 		    16 - crp->crp_aad_length % 16);
201 	}
202 
203 	/* Encryption starts with block 1. */
204 	counter[0] = 1;
205 
206 	/* Do encryption with MAC */
207 	resid = crp->crp_payload_length;
208 	crypto_cursor_init(&cc_in, &crp->crp_buf);
209 	crypto_cursor_advance(&cc_in, crp->crp_payload_start);
210 	inseg = crypto_cursor_segment(&cc_in, &inlen);
211 	if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
212 		crypto_cursor_init(&cc_out, &crp->crp_obuf);
213 		crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
214 	} else
215 		cc_out = cc_in;
216 	outseg = crypto_cursor_segment(&cc_out, &outlen);
217 	while (resid >= CHACHA_BLK_SIZE) {
218 		if (inlen < CHACHA_BLK_SIZE) {
219 			crypto_cursor_copydata(&cc_in, CHACHA_BLK_SIZE, block);
220 			in = block;
221 			inlen = CHACHA_BLK_SIZE;
222 		} else
223 			in = inseg;
224 		if (outlen < CHACHA_BLK_SIZE) {
225 			out = block;
226 			outlen = CHACHA_BLK_SIZE;
227 		} else
228 			out = outseg;
229 
230 		/* Figure out how many blocks we can encrypt/decrypt at once. */
231 		todo = rounddown(MIN(resid, MIN(inlen, outlen)),
232 		    CHACHA_BLK_SIZE);
233 
234 #ifdef __LP64__
235 		/* ChaCha20_ctr32() assumes length is <= 4GB. */
236 		todo = (uint32_t)todo;
237 #endif
238 
239 		/* Truncate if the 32-bit counter would roll over. */
240 		next_counter = counter[0] + todo / CHACHA_BLK_SIZE;
241 		if (csp->csp_ivlen == 8 && next_counter < counter[0]) {
242 			todo -= next_counter * CHACHA_BLK_SIZE;
243 			next_counter = 0;
244 		}
245 
246 		ChaCha20_ctr32(out, in, todo, key, counter);
247 		Poly1305_Update(&auth_ctx, out, todo);
248 
249 		counter[0] = next_counter;
250 		if (csp->csp_ivlen == 8 && counter[0] == 0)
251 			counter[1]++;
252 
253 		if (out == block) {
254 			crypto_cursor_copyback(&cc_out, CHACHA_BLK_SIZE, block);
255 			outseg = crypto_cursor_segment(&cc_out, &outlen);
256 		} else {
257 			crypto_cursor_advance(&cc_out, todo);
258 			outseg += todo;
259 			outlen -= todo;
260 		}
261 		if (in == block) {
262 			inseg = crypto_cursor_segment(&cc_in, &inlen);
263 		} else {
264 			crypto_cursor_advance(&cc_in, todo);
265 			inseg += todo;
266 			inlen -= todo;
267 		}
268 		resid -= todo;
269 	}
270 
271 	if (resid > 0) {
272 		memset(block, 0, sizeof(block));
273 		crypto_cursor_copydata(&cc_in, resid, block);
274 		ChaCha20_ctr32(block, block, CHACHA_BLK_SIZE, key, counter);
275 		crypto_cursor_copyback(&cc_out, resid, block);
276 
277 		/* padding2 */
278 		todo = roundup2(resid, 16);
279 		memset(block + resid, 0, todo - resid);
280 		Poly1305_Update(&auth_ctx, block, todo);
281 	}
282 
283 	/* lengths */
284 	le64enc(block, crp->crp_aad_length);
285 	le64enc(block + 8, crp->crp_payload_length);
286 	Poly1305_Update(&auth_ctx, block, sizeof(uint64_t) * 2);
287 
288 	Poly1305_Final(&auth_ctx, tag);
289 	crypto_copyback(crp, crp->crp_digest_start, csp->csp_auth_mlen == 0 ?
290 	    POLY1305_HASH_LEN : csp->csp_auth_mlen, tag);
291 
292 	explicit_bzero(&auth_ctx, sizeof(auth_ctx));
293 	explicit_bzero(tag, sizeof(tag));
294 	explicit_bzero(block, sizeof(block));
295 	explicit_bzero(counter, sizeof(counter));
296 	explicit_bzero(key, sizeof(key));
297 	return (0);
298 }
299 
300 
301 int
302 ossl_chacha20_poly1305_decrypt(struct cryptop *crp,
303     const struct crypto_session_params *csp)
304 {
305 	_Alignas(8) unsigned int key[CHACHA_KEY_SIZE / 4];
306 	unsigned int counter[CHACHA_CTR_SIZE / 4];
307 	_Alignas(8) unsigned char block[CHACHA_BLK_SIZE];
308 	unsigned char tag[POLY1305_HASH_LEN], tag2[POLY1305_HASH_LEN];
309 	struct poly1305_context auth_ctx;
310 	struct crypto_buffer_cursor cc_in, cc_out;
311 	const unsigned char *in, *inseg, *cipher_key;
312 	unsigned char *out, *outseg;
313 	size_t resid, todo, inlen, outlen;
314 	uint32_t next_counter;
315 	int error;
316 	u_int i, mlen;
317 
318 	if (crp->crp_cipher_key != NULL)
319 		cipher_key = crp->crp_cipher_key;
320 	else
321 		cipher_key = csp->csp_cipher_key;
322 	for (i = 0; i < nitems(key); i++)
323 		key[i] = CHACHA_U8TOU32(cipher_key + i * 4);
324 
325 	memset(counter, 0, sizeof(counter));
326 	crypto_read_iv(crp, counter + (CHACHA_CTR_SIZE - csp->csp_ivlen) / 4);
327 	for (i = 1; i < nitems(counter); i++)
328 		counter[i] = le32toh(counter[i]);
329 
330 	/* Block 0 is used to generate the poly1305 key. */
331 	counter[0] = 0;
332 
333 	memset(block, 0, sizeof(block));
334 	ChaCha20_ctr32(block, block, sizeof(block), key, counter);
335 	Poly1305_Init(&auth_ctx, block);
336 
337 	/* MAC the AAD. */
338 	if (crp->crp_aad != NULL)
339 		Poly1305_Update(&auth_ctx, crp->crp_aad, crp->crp_aad_length);
340 	else
341 		crypto_apply(crp, crp->crp_aad_start, crp->crp_aad_length,
342 		    ossl_poly1305_update, &auth_ctx);
343 	if (crp->crp_aad_length % 16 != 0) {
344 		/* padding1 */
345 		memset(block, 0, 16);
346 		Poly1305_Update(&auth_ctx, block,
347 		    16 - crp->crp_aad_length % 16);
348 	}
349 
350 	/* Mac the ciphertext. */
351 	crypto_apply(crp, crp->crp_payload_start, crp->crp_payload_length,
352 	    ossl_poly1305_update, &auth_ctx);
353 	if (crp->crp_payload_length % 16 != 0) {
354 		/* padding2 */
355 		memset(block, 0, 16);
356 		Poly1305_Update(&auth_ctx, block,
357 		    16 - crp->crp_payload_length % 16);
358 	}
359 
360 	/* lengths */
361 	le64enc(block, crp->crp_aad_length);
362 	le64enc(block + 8, crp->crp_payload_length);
363 	Poly1305_Update(&auth_ctx, block, sizeof(uint64_t) * 2);
364 
365 	Poly1305_Final(&auth_ctx, tag);
366 	mlen = csp->csp_auth_mlen == 0 ? POLY1305_HASH_LEN : csp->csp_auth_mlen;
367 	crypto_copydata(crp, crp->crp_digest_start, mlen, tag2);
368 	if (timingsafe_bcmp(tag, tag2, mlen) != 0) {
369 		error = EBADMSG;
370 		goto out;
371 	}
372 
373 	/* Decryption starts with block 1. */
374 	counter[0] = 1;
375 
376 	resid = crp->crp_payload_length;
377 	crypto_cursor_init(&cc_in, &crp->crp_buf);
378 	crypto_cursor_advance(&cc_in, crp->crp_payload_start);
379 	inseg = crypto_cursor_segment(&cc_in, &inlen);
380 	if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
381 		crypto_cursor_init(&cc_out, &crp->crp_obuf);
382 		crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
383 	} else
384 		cc_out = cc_in;
385 	outseg = crypto_cursor_segment(&cc_out, &outlen);
386 	while (resid >= CHACHA_BLK_SIZE) {
387 		if (inlen < CHACHA_BLK_SIZE) {
388 			crypto_cursor_copydata(&cc_in, CHACHA_BLK_SIZE, block);
389 			in = block;
390 			inlen = CHACHA_BLK_SIZE;
391 		} else
392 			in = inseg;
393 		if (outlen < CHACHA_BLK_SIZE) {
394 			out = block;
395 			outlen = CHACHA_BLK_SIZE;
396 		} else
397 			out = outseg;
398 
399 		/* Figure out how many blocks we can encrypt/decrypt at once. */
400 		todo = rounddown(MIN(resid, MIN(inlen, outlen)),
401 		    CHACHA_BLK_SIZE);
402 
403 #ifdef __LP64__
404 		/* ChaCha20_ctr32() assumes length is <= 4GB. */
405 		todo = (uint32_t)todo;
406 #endif
407 
408 		/* Truncate if the 32-bit counter would roll over. */
409 		next_counter = counter[0] + todo / CHACHA_BLK_SIZE;
410 		if (csp->csp_ivlen == 8 && next_counter < counter[0]) {
411 			todo -= next_counter * CHACHA_BLK_SIZE;
412 			next_counter = 0;
413 		}
414 
415 		ChaCha20_ctr32(out, in, todo, key, counter);
416 
417 		counter[0] = next_counter;
418 		if (csp->csp_ivlen == 8 && counter[0] == 0)
419 			counter[1]++;
420 
421 		if (out == block) {
422 			crypto_cursor_copyback(&cc_out, CHACHA_BLK_SIZE, block);
423 			outseg = crypto_cursor_segment(&cc_out, &outlen);
424 		} else {
425 			crypto_cursor_advance(&cc_out, todo);
426 			outseg += todo;
427 			outlen -= todo;
428 		}
429 		if (in == block) {
430 			inseg = crypto_cursor_segment(&cc_in, &inlen);
431 		} else {
432 			crypto_cursor_advance(&cc_in, todo);
433 			inseg += todo;
434 			inlen -= todo;
435 		}
436 		resid -= todo;
437 	}
438 
439 	if (resid > 0) {
440 		memset(block, 0, sizeof(block));
441 		crypto_cursor_copydata(&cc_in, resid, block);
442 		ChaCha20_ctr32(block, block, CHACHA_BLK_SIZE, key, counter);
443 		crypto_cursor_copyback(&cc_out, resid, block);
444 	}
445 
446 	error = 0;
447 out:
448 	explicit_bzero(&auth_ctx, sizeof(auth_ctx));
449 	explicit_bzero(tag, sizeof(tag));
450 	explicit_bzero(block, sizeof(block));
451 	explicit_bzero(counter, sizeof(counter));
452 	explicit_bzero(key, sizeof(key));
453 	return (error);
454 }
455