/src/wolfssl-normal-math/wolfcrypt/src/chacha20_poly1305.c
Line | Count | Source |
1 | | /* chacha.c |
2 | | * |
3 | | * Copyright (C) 2006-2026 wolfSSL Inc. |
4 | | * |
5 | | * This file is part of wolfSSL. |
6 | | * |
7 | | * wolfSSL is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 3 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * wolfSSL is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA |
20 | | */ |
21 | | /* |
22 | | |
23 | | DESCRIPTION |
24 | | This library contains implementation for the ChaCha20 stream cipher and |
25 | | the Poly1305 authenticator, both as as combined-mode, |
26 | | or Authenticated Encryption with Additional Data (AEAD) algorithm. |
27 | | |
28 | | */ |
29 | | |
30 | | #include <wolfssl/wolfcrypt/libwolfssl_sources.h> |
31 | | |
32 | | #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) |
33 | | |
34 | | #include <wolfssl/wolfcrypt/chacha20_poly1305.h> |
35 | | #include <wolfssl/wolfcrypt/cpuid.h> |
36 | | |
37 | | #ifdef WOLF_CRYPTO_CB |
38 | | #include <wolfssl/wolfcrypt/cryptocb.h> |
39 | | #endif |
40 | | |
41 | | #ifdef NO_INLINE |
42 | | #include <wolfssl/wolfcrypt/misc.h> |
43 | | #else |
44 | | #define WOLFSSL_MISC_INCLUDED |
45 | | #include <wolfcrypt/src/misc.c> |
46 | | #endif |
47 | | |
48 | 240 | #define CHACHA20_POLY1305_AEAD_INITIAL_COUNTER 0 |
49 | | |
50 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED |
51 | | /* Fused single-pass encrypt kernel (in chacha_asm.S) and the 4-way power |
52 | | * precompute it depends on. */ |
53 | | #ifdef __cplusplus |
54 | | extern "C" { |
55 | | #endif |
56 | | WOLFSSL_LOCAL void chacha20_poly1305_avx512(ChaCha* chacha, Poly1305* poly, |
57 | | const byte* m, byte* c, word32 bytes); |
58 | | WOLFSSL_LOCAL void poly1305_calc_powers_avx2(Poly1305* ctx); |
59 | | #ifdef __cplusplus |
60 | | } |
61 | | #endif |
62 | | |
63 | | /* The fused kernel uses 4-block ChaCha (256-bit) + 4-way Poly1305, which beats |
64 | | * the wide two-pass only where 512-bit code is throttled - Intel Ice Lake and |
65 | | * later, under the AVX-512 frequency license. On AMD (no throttle, very fast |
66 | | * wide primitives) the two-pass wins, so gate on an Intel vendor. Override: |
67 | | * WOLFSSL_CHACHA20_POLY1305_FUSED_ALWAYS / _NEVER. */ |
68 | | static WC_INLINE int chacha20_poly1305_use_fused(void) |
69 | | { |
70 | | #if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_NEVER) |
71 | | return 0; |
72 | | #elif defined(WOLFSSL_CHACHA20_POLY1305_FUSED_ALWAYS) |
73 | | return 1; |
74 | | #else |
75 | | cpuid_flags_t f = cpuid_get_flags(); |
76 | | return (IS_CPU_INTEL(f) != 0) && (IS_INTEL_AVX512(f) != 0); |
77 | | #endif |
78 | | } |
79 | | |
80 | | /* Encrypt with the fused kernel: no AAD, so Poly1305 starts clean and 256-byte |
81 | | * aligned. Drive Poly1305 4-way (forceAvx2) so the kernel and the tail/final |
82 | | * share the layout; the kernel does the aligned bulk, the tail and length |
83 | | * framing go through the normal 4-way path. */ |
84 | | static int chacha20_poly1305_encrypt_fused(ChaChaPoly_Aead* aead, |
85 | | const byte* pt, word32 ptLen, byte* ct, byte* tag) |
86 | | { |
87 | | word32 bulk = ptLen & ~(word32)0xff; |
88 | | int ret; |
89 | | |
90 | | aead->poly.forceAvx2 = 1; |
91 | | /* The cpuid setkey may have zeroed a different accumulator; ready the 4-way |
92 | | * hash and let the kernel initialise the lanes. */ |
93 | | XMEMSET(aead->poly.hh, 0, sizeof(aead->poly.hh)); |
94 | | aead->poly.started = 0; |
95 | | aead->poly.leftover = 0; |
96 | | aead->state = CHACHA20_POLY1305_STATE_DATA; |
97 | | |
98 | | SAVE_VECTOR_REGISTERS(return _svr_ret;); |
99 | | poly1305_calc_powers_avx2(&aead->poly); |
100 | | aead->poly.started = 1; |
101 | | /* bulk is a non-zero multiple of 256 here (caller gates on >= 256), but the |
102 | | * kernel has no short-length entry guard - it would run a full 256-byte unit |
103 | | * off the end on a zero length, so never call it with nothing to do. */ |
104 | | if (bulk > 0) |
105 | | chacha20_poly1305_avx512(&aead->chacha, &aead->poly, pt, ct, bulk); |
106 | | RESTORE_VECTOR_REGISTERS(); |
107 | | |
108 | | aead->dataLen = bulk; |
109 | | ret = 0; |
110 | | if (ptLen > bulk) |
111 | | ret = wc_ChaCha20Poly1305_UpdateData(aead, pt + bulk, ct + bulk, |
112 | | ptLen - bulk); |
113 | | if (ret == 0) |
114 | | ret = wc_ChaCha20Poly1305_Final(aead, tag); |
115 | | |
116 | | return ret; |
117 | | } |
118 | | #endif /* WOLFSSL_CHACHA20_POLY1305_FUSED */ |
119 | | |
120 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
121 | | /* IFMA stitched single-pass encrypt kernel (in chacha_asm.S): full 512-bit |
122 | | * 16-block ChaCha interleaved with an 8-way IFMA (vpmadd52) Poly1305 that |
123 | | * collapses to the scalar hash. Processes 1024-byte units. Depends on the |
124 | | * radix-2^44 powers. */ |
125 | | #ifdef __cplusplus |
126 | | extern "C" { |
127 | | #endif |
128 | | WOLFSSL_LOCAL void chacha20_poly1305_ifma(ChaCha* chacha, Poly1305* poly, |
129 | | const byte* m, byte* c, word32 bytes); |
130 | | /* Decrypt counterpart: hashes the ciphertext INPUT (m) as it decrypts to c |
131 | | * (in-place safe - m is hashed before it is overwritten). Same 1024-byte |
132 | | * units and radix-2^44 powers. */ |
133 | | WOLFSSL_LOCAL void chacha20_poly1305_ifma_decrypt(ChaCha* chacha, |
134 | | Poly1305* poly, const byte* m, byte* c, word32 bytes); |
135 | | WOLFSSL_LOCAL void poly1305_calc_powers_avx512ifma(Poly1305* ctx); |
136 | | /* ctx->h = ctx->hh * r^nBlocks + ctx->h - advances the running hash (saved by |
137 | | * the kernel to ctx->hh) past a chunk the kernel hashed from zero into ctx->h. |
138 | | * Radix-2^64 scalar, so no 26<->64 conversions. */ |
139 | | WOLFSSL_LOCAL void poly1305_fold_avx512ifma(Poly1305* ctx, word32 nBlocks); |
140 | | #ifdef __cplusplus |
141 | | } |
142 | | #endif |
143 | | |
144 | | /* Minimum length to stitch. The kernel processes 1024-byte units and a |
145 | | * once-per-op power precompute, and any sub-1024 remainder is authenticated by |
146 | | * the slower scalar Poly1305; below this the two-pass wins (measured crossover |
147 | | * on Zen5). Above it the stitch wins 1.1-1.4x, growing with size. |
148 | | * |
149 | | * HARD LOWER BOUND 1024: callers gate on this then pass bulk = sz & ~0x3ff to |
150 | | * the kernel, and the kernel has NO short-length entry guard - its first length |
151 | | * test runs only AFTER a full 1024-byte chunk. A value below 1024 lets a |
152 | | * sub-1024 message compute bulk == 0, and the kernel then reads/writes a whole |
153 | | * 1024-byte unit off the end of the buffer and underflows its counter into a |
154 | | * multi-million-iteration loop. Enforced at compile time below; stitch_chunk() |
155 | | * also guards bulk == 0 at runtime as defence in depth. */ |
156 | | #ifndef CHACHA20_POLY1305_STITCH_MIN |
157 | | #define CHACHA20_POLY1305_STITCH_MIN 4096 |
158 | | #endif |
159 | | #if CHACHA20_POLY1305_STITCH_MIN < 1024 |
160 | | #error "CHACHA20_POLY1305_STITCH_MIN must be >= 1024 (the kernel unit size)" |
161 | | #endif |
162 | | |
163 | | /* Short-message fused path: for messages this small the Poly1305 key block |
164 | | * (ChaCha counter 0) and the whole ciphertext (counter 1+) fit in a single |
165 | | * ChaCha keystream generation - one pass instead of two - and a scalar |
166 | | * Poly1305 avoids the vector power-precompute cost. Needs the forceScalar |
167 | | * flag (same builds as the fused kernels). Measured 1.1-1.5x on Zen5 for |
168 | | * 64-192 byte records; above SHORT_MAX the poly key spills to a second ChaCha |
169 | | * chunk and the saving is gone. */ |
170 | | #if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA) && \ |
171 | | !defined(WOLFSSL_NO_CHACHA20_POLY1305_SHORT) |
172 | | #define WOLFSSL_CHACHA20_POLY1305_SHORT |
173 | | #ifndef CHACHA20_POLY1305_SHORT_MAX |
174 | | /* 64 (poly-key block) + 192 = 256 = one AVX-512VL 4-block chunk */ |
175 | | #define CHACHA20_POLY1305_SHORT_MAX 192 |
176 | | #endif |
177 | | #endif |
178 | | |
179 | | /* The IFMA stitch runs a full-width 512-bit 16-block ChaCha interleaved with an |
180 | | * 8-way IFMA Poly1305: ChaCha is the bottleneck and Poly hides under it, so it |
181 | | * beats the two-pass (which runs the two passes back to back) by ~1.3-1.4x at |
182 | | * >=16KB - measured on AMD Zen5, and expected wherever AVX-512 + IFMA exist |
183 | | * (both use 512-bit ChaCha, so any frequency throttle hits both equally). Gate |
184 | | * on AVX-512 + IFMA, any vendor. Override: ..._FUSED_IFMA_ALWAYS / _NEVER. */ |
185 | | static WC_INLINE int chacha20_poly1305_use_fused_ifma(void) |
186 | | { |
187 | | #if defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_NEVER) |
188 | | return 0; |
189 | | #elif defined(WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA_ALWAYS) |
190 | | return 1; |
191 | | #else |
192 | | cpuid_flags_t f = cpuid_get_flags(); |
193 | | return (IS_INTEL_AVX512(f) != 0) && (IS_INTEL_AVX512_IFMA(f) != 0); |
194 | | #endif |
195 | | } |
196 | | |
197 | | /* Stitch one 1024-byte-aligned bulk. (IFMA path) The kernel hashes this |
198 | | * chunk's ciphertext from zero (leaving ctx->h = H_chunk) and advances the |
199 | | * ChaCha counter, saving the running hash (the AAD, or previous chunks) to |
200 | | * ctx->hh; poly1305_fold_avx512ifma then advances that hash past this chunk |
201 | | * (ctx->h = ctx->hh * r^nBlocks + H_chunk). Powers are computed once (started |
202 | | * flag). Caller must have ctx->h = running hash, leftover == 0, forceScalar |
203 | | * and finished set, and the ChaCha counter placed for this chunk. decrypt: in |
204 | | * is ciphertext, out is plaintext (in-place safe - the kernel hashes in before |
205 | | * overwriting it); the hash math is identical. */ |
206 | | static int chacha20_poly1305_stitch_chunk(ChaCha* chacha, Poly1305* poly, |
207 | | const byte* in, byte* out, word32 bulk, int decrypt) |
208 | | { |
209 | | int fold; |
210 | | |
211 | | /* The kernel has no short-length entry guard and would run a full 1024-byte |
212 | | * unit off the end of the buffer on a zero length. bulk is always a |
213 | | * non-zero multiple of 1024 here (STITCH_MIN >= 1024, enforced at compile |
214 | | * time), but never invoke the kernel with nothing to do. */ |
215 | | if (bulk == 0) |
216 | | return 0; |
217 | | |
218 | | /* A running hash (AAD or previous chunks) must be folded past this chunk; |
219 | | * detect it before the kernel overwrites poly->h with this chunk's hash. */ |
220 | | fold = (poly->h[0] | poly->h[1] | poly->h[2]) != 0; |
221 | | |
222 | | SAVE_VECTOR_REGISTERS(return _svr_ret;); |
223 | | if (!poly->started) { |
224 | | poly1305_calc_powers_avx512ifma(poly); |
225 | | poly->started = 1; |
226 | | } |
227 | | if (decrypt) |
228 | | chacha20_poly1305_ifma_decrypt(chacha, poly, in, out, bulk); |
229 | | else |
230 | | chacha20_poly1305_ifma(chacha, poly, in, out, bulk); |
231 | | RESTORE_VECTOR_REGISTERS(); |
232 | | |
233 | | /* poly->h = H_chunk, poly->hh = running hash (scalar fold, no vectors). */ |
234 | | if (fold) |
235 | | poly1305_fold_avx512ifma(poly, bulk / 16); |
236 | | return 0; |
237 | | } |
238 | | |
239 | | /* Encrypt the whole message with the IFMA stitch (one-shot path). AAD is |
240 | | * hashed scalar into ctx->h, the 1024-aligned bulk is stitched (folding AAD |
241 | | * through it), the tail + length framing go through the scalar path. |
242 | | * forceScalar/finished: see the AVX2 fused note (setkey_avx2 leaves finished |
243 | | * clear). Both this and the streaming UpdateData path share stitch_chunk(). */ |
244 | | static int chacha20_poly1305_encrypt_fused_ifma(ChaChaPoly_Aead* aead, |
245 | | const byte* aad, word32 aadLen, const byte* pt, word32 ptLen, byte* ct, |
246 | | byte* tag) |
247 | | { |
248 | | word32 bulk = ptLen & ~(word32)0x3ff; |
249 | | int ret = 0; |
250 | | |
251 | | aead->poly.forceScalar = 1; |
252 | | XMEMSET(aead->poly.h, 0, sizeof(aead->poly.h)); |
253 | | aead->poly.finished = 1; |
254 | | aead->poly.leftover = 0; |
255 | | aead->poly.started = 0; |
256 | | |
257 | | /* Hash AAD + pad1 (scalar) -> H_aad in ctx->h. */ |
258 | | if (aadLen > 0) { |
259 | | ret = wc_Poly1305Update(&aead->poly, aad, aadLen); |
260 | | if (ret == 0) |
261 | | ret = wc_Poly1305_Pad(&aead->poly, aadLen); |
262 | | } |
263 | | aead->aadLen = aadLen; |
264 | | aead->state = CHACHA20_POLY1305_STATE_DATA; |
265 | | |
266 | | if (ret == 0) |
267 | | ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly, pt, |
268 | | ct, bulk, 0); |
269 | | if (ret == 0) { |
270 | | aead->dataLen = bulk; |
271 | | if (ptLen > bulk) |
272 | | ret = wc_ChaCha20Poly1305_UpdateData(aead, pt + bulk, ct + bulk, |
273 | | ptLen - bulk); |
274 | | if (ret == 0) |
275 | | ret = wc_ChaCha20Poly1305_Final(aead, tag); |
276 | | } |
277 | | return ret; |
278 | | } |
279 | | #endif /* WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA */ |
280 | | |
281 | | #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT |
282 | | |
283 | | /* small_enc/small_dec are HAVE_INTEL_AVX2 kernels in chacha_asm.S; gate on |
284 | | * NO_AVX2_SUPPORT so a -DNO_AVX2_SUPPORT build does not reference them. */ |
285 | | #if defined(USE_INTEL_SPEEDUP) && defined(WOLFSSL_X86_64_BUILD) && \ |
286 | | !defined(WOLFSSL_NO_CHACHA20_POLY1305_SMALL_ASM) && \ |
287 | | !defined(NO_AVX2_SUPPORT) |
288 | | #define WOLFSSL_CP_SMALL_ASM |
289 | | |
290 | | /* Fused single-call ChaCha20-Poly1305 encrypt for a one-block (<=64 byte) |
291 | | * record (in chacha_asm.S): SSSE3 crypt2 produces the Poly1305 key block and |
292 | | * the single data block together; the scalar poly1305_*_avx do the MAC. */ |
293 | | #ifdef __cplusplus |
294 | | extern "C" { |
295 | | #endif |
296 | | WOLFSSL_LOCAL void chacha20_poly1305_small_enc(ChaCha* chacha, Poly1305* poly, |
297 | | const byte* m, byte* c, word32 mLen, const byte* aad, word32 aadLen, |
298 | | byte* tag); |
299 | | /* Decrypt twin: decrypts in->out AND verifies the tag in one pass (decrypt- |
300 | | * then-verify). Constant-time-compares the computed tag against the received |
301 | | * tag internally and returns 0 on match, 1 on mismatch; the caller ForceZeros |
302 | | * the output on mismatch, so no plaintext is released on a bad tag. */ |
303 | | WOLFSSL_LOCAL int chacha20_poly1305_small_dec(ChaCha* chacha, Poly1305* poly, |
304 | | const byte* in, byte* out, word32 ctLen, const byte* aad, word32 aadLen, |
305 | | const byte* tag); |
306 | | #ifdef __cplusplus |
307 | | } |
308 | | #endif |
309 | | |
310 | | static WC_INLINE int chacha20_poly1305_use_small(void) |
311 | | { |
312 | | return IS_INTEL_AVX2(cpuid_get_flags()) != 0; |
313 | | } |
314 | | #endif |
315 | | |
316 | | /* Fused short-message (sz <= CHACHA20_POLY1305_SHORT_MAX) encrypt for the |
317 | | * pre-keyed contexts: derive the Poly1305 key (counter 0) and the encryption |
318 | | * keystream (counter 1+) in a SINGLE ChaCha pass, then scalar-hash. Saves the |
319 | | * second ChaCha invocation the two-pass path would make. */ |
320 | | static int chacha20_poly1305_encrypt_short(ChaCha* chacha, Poly1305* poly, |
321 | | byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag, |
322 | | const byte* aad, word32 aadSz) |
323 | | { |
324 | | byte ks[64 + CHACHA20_POLY1305_SHORT_MAX]; |
325 | | int ret; |
326 | | |
327 | | #ifdef WOLFSSL_CP_SMALL_ASM |
328 | | /* One block or less of data: the fused single-call kernel. */ |
329 | | if (sz <= 64 && chacha20_poly1305_use_small()) { |
330 | | ret = wc_Chacha_SetIV(chacha, nonce, |
331 | | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
332 | | if (ret == 0) { |
333 | | SAVE_VECTOR_REGISTERS(return _svr_ret;); |
334 | | chacha20_poly1305_small_enc(chacha, poly, in, out, sz, aad, aadSz, |
335 | | tag); |
336 | | RESTORE_VECTOR_REGISTERS(); |
337 | | } |
338 | | return ret; |
339 | | } |
340 | | #endif |
341 | | |
342 | | XMEMSET(ks, 0, 64 + sz); |
343 | | ret = wc_Chacha_SetIV(chacha, nonce, |
344 | | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
345 | | if (ret == 0) /* ctr0 (poly key) .. ctrN, one pass */ |
346 | | ret = wc_Chacha_Process(chacha, ks, ks, 64 + sz); |
347 | | if (ret == 0) |
348 | | ret = wc_Poly1305SetKey(poly, ks, CHACHA20_POLY1305_AEAD_KEYSIZE); |
349 | | if (ret == 0) { |
350 | | xorbufout(out, in, ks + 64, sz); /* ct = pt ^ keystream (ctr1+) */ |
351 | | poly->forceScalar = 1; |
352 | | poly->finished = 1; |
353 | | ret = wc_Poly1305_MAC(poly, aad, aadSz, out, sz, tag, |
354 | | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
355 | | } |
356 | | ForceZero(ks, 64 + sz); /* ks[0:32] was the poly key */ |
357 | | return ret; |
358 | | } |
359 | | |
360 | | /* Fused short-message decrypt twin: derive key + keystream in one ChaCha pass, |
361 | | * MAC the ciphertext INPUT and verify the tag BEFORE decrypting, so no |
362 | | * plaintext is produced on a bad tag (stronger than the stitch, cheap here |
363 | | * because the message is small). In-place safe. */ |
364 | | static int chacha20_poly1305_decrypt_short(ChaCha* chacha, Poly1305* poly, |
365 | | byte* out, const byte* in, word32 sz, const byte* nonce, const byte* tag, |
366 | | const byte* aad, word32 aadSz) |
367 | | { |
368 | | byte ks[64 + CHACHA20_POLY1305_SHORT_MAX]; |
369 | | byte calcTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; |
370 | | int ret; |
371 | | |
372 | | #ifdef WOLFSSL_CP_SMALL_ASM |
373 | | /* One block or less of data: the fused single-call kernel decrypts in->out |
374 | | * and computes calcTag in one pass (decrypt-then-verify). Zero the output |
375 | | * if the tag is bad, so no plaintext is released. */ |
376 | | if (sz <= 64 && chacha20_poly1305_use_small()) { |
377 | | ret = wc_Chacha_SetIV(chacha, nonce, |
378 | | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
379 | | if (ret == 0) { |
380 | | int bad; |
381 | | SAVE_VECTOR_REGISTERS(return _svr_ret;); |
382 | | bad = chacha20_poly1305_small_dec(chacha, poly, in, out, sz, aad, |
383 | | aadSz, tag); |
384 | | RESTORE_VECTOR_REGISTERS(); |
385 | | if (bad) { /* bad tag: no plaintext */ |
386 | | if (sz > 0) |
387 | | ForceZero(out, sz); |
388 | | ret = MAC_CMP_FAILED_E; |
389 | | } |
390 | | } |
391 | | (void)calcTag; |
392 | | return ret; |
393 | | } |
394 | | #endif |
395 | | |
396 | | XMEMSET(ks, 0, 64 + sz); |
397 | | ret = wc_Chacha_SetIV(chacha, nonce, |
398 | | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
399 | | if (ret == 0) |
400 | | ret = wc_Chacha_Process(chacha, ks, ks, 64 + sz); |
401 | | if (ret == 0) |
402 | | ret = wc_Poly1305SetKey(poly, ks, CHACHA20_POLY1305_AEAD_KEYSIZE); |
403 | | if (ret == 0) { |
404 | | poly->forceScalar = 1; |
405 | | poly->finished = 1; |
406 | | ret = wc_Poly1305_MAC(poly, aad, aadSz, in, sz, calcTag, |
407 | | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
408 | | } |
409 | | if (ret == 0) |
410 | | ret = wc_ChaCha20Poly1305_CheckTag(tag, calcTag); |
411 | | if (ret == 0) /* tag good: decrypt pt = ct ^ ks */ |
412 | | xorbufout(out, in, ks + 64, sz); |
413 | | else if (sz > 0) /* bad tag/error: no stale output */ |
414 | | ForceZero(out, sz); |
415 | | ForceZero(ks, 64 + sz); |
416 | | return ret; |
417 | | } |
418 | | #endif /* WOLFSSL_CHACHA20_POLY1305_SHORT */ |
419 | | |
420 | | /* Encrypt + authenticate one message with PRE-KEYED ChaCha20 and Poly1305 |
421 | | * contexts - the ChaCha20-Poly1305 analogue of wc_AesGcmEncrypt on a keyed Aes. |
422 | | * Intended for the TLS record layer, which keeps the ChaCha context keyed once |
423 | | * (per traffic key) and only varies the nonce per record. The per-record |
424 | | * Poly1305 key is derived here from the ChaCha keystream. Uses the single-pass |
425 | | * IFMA stitch when beneficial, else the two-pass; identical output either way. |
426 | | * |
427 | | * chacha ChaCha20 context with the key already set (wc_Chacha_SetKey) |
428 | | * poly Poly1305 scratch context (re-keyed here every call) |
429 | | * out ciphertext out (may alias in) |
430 | | * in/sz plaintext / length |
431 | | * nonce CHACHA20_POLY1305_AEAD_IV_SIZE (12) byte record nonce |
432 | | * tag CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE (16) byte tag out |
433 | | * aad/aadSz additional authenticated data |
434 | | * returns 0 on success, negative on error. |
435 | | */ |
436 | | WOLFSSL_API int wc_ChaCha20Poly1305_Encrypt_ex(ChaCha* chacha, Poly1305* poly, |
437 | | byte* out, const byte* in, word32 sz, const byte* nonce, byte* tag, |
438 | | const byte* aad, word32 aadSz) |
439 | 95 | { |
440 | 95 | byte polyKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; |
441 | 95 | int ret; |
442 | | |
443 | 95 | if (chacha == NULL || poly == NULL || nonce == NULL || tag == NULL || |
444 | 95 | (sz > 0 && (in == NULL || out == NULL)) || |
445 | 95 | (aadSz > 0 && aad == NULL)) { |
446 | 0 | return BAD_FUNC_ARG; |
447 | 0 | } |
448 | | |
449 | 95 | #ifdef WOLF_CRYPTO_CB |
450 | | /* devId comes from wc_Chacha_SetKey_ex(). This is the path the TLS |
451 | | * record layer uses. Software runs if the device declines. |
452 | | * |
453 | | * wc_Chacha_SetKey_ex() also accepts a 16 byte key, which this AEAD is |
454 | | * not defined for and which the callback carries no length for, so a |
455 | | * device would key itself with 32 bytes and diverge from software. Keep |
456 | | * those contexts on the software path. */ |
457 | 95 | if (chacha->devId != INVALID_DEVID && |
458 | 0 | chacha->devKeySz == CHACHA20_POLY1305_AEAD_KEYSIZE) { |
459 | 0 | ret = wc_CryptoCb_Chacha20Poly1305Encrypt(chacha->devId, chacha->devKey, |
460 | 0 | nonce, aad, aadSz, in, sz, out, tag); |
461 | 0 | if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) |
462 | 0 | return ret; |
463 | 0 | } |
464 | 95 | #endif |
465 | | |
466 | | #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT |
467 | | if (sz <= CHACHA20_POLY1305_SHORT_MAX) |
468 | | return chacha20_poly1305_encrypt_short(chacha, poly, out, in, sz, |
469 | | nonce, tag, aad, aadSz); |
470 | | #endif |
471 | | |
472 | | /* Per-record Poly1305 key = first 32 bytes of ChaCha20(nonce, ctr 0). */ |
473 | 95 | XMEMSET(polyKey, 0, sizeof(polyKey)); |
474 | 95 | ret = wc_Chacha_SetIV(chacha, nonce, |
475 | 95 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
476 | 95 | if (ret == 0) |
477 | 95 | ret = wc_Chacha_Process(chacha, polyKey, polyKey, sizeof(polyKey)); |
478 | 95 | if (ret == 0) /* message data starts at counter 1 */ |
479 | 95 | ret = wc_Chacha_SetIV(chacha, nonce, |
480 | 95 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1); |
481 | 95 | if (ret == 0) |
482 | 95 | ret = wc_Poly1305SetKey(poly, polyKey, sizeof(polyKey)); |
483 | 95 | ForceZero(polyKey, sizeof(polyKey)); |
484 | 95 | if (ret != 0) |
485 | 0 | return ret; |
486 | | |
487 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
488 | | if (sz >= CHACHA20_POLY1305_STITCH_MIN && |
489 | | chacha20_poly1305_use_fused_ifma()) { |
490 | | word32 bulk = sz & ~(word32)0x3ff; |
491 | | /* Scalar running hash (in poly->h) so stitch and tail chain; poly->h, |
492 | | * leftover and started are all zeroed by wc_Poly1305SetKey. */ |
493 | | poly->forceScalar = 1; |
494 | | poly->finished = 1; |
495 | | if (aadSz > 0) { /* H_aad + pad1 (scalar) */ |
496 | | ret = wc_Poly1305Update(poly, aad, aadSz); |
497 | | if (ret == 0) |
498 | | ret = wc_Poly1305_Pad(poly, aadSz); |
499 | | } |
500 | | if (ret == 0) /* stitch bulk + fold AAD */ |
501 | | ret = chacha20_poly1305_stitch_chunk(chacha, poly, in, out, bulk, |
502 | | 0); |
503 | | if (ret == 0 && sz > bulk) { /* scalar tail */ |
504 | | ret = wc_Chacha_Process(chacha, out + bulk, in + bulk, sz - bulk); |
505 | | if (ret == 0) |
506 | | ret = wc_Poly1305Update(poly, out + bulk, sz - bulk); |
507 | | } |
508 | | if (ret == 0) /* pad2 + lengths + tag */ |
509 | | ret = wc_Poly1305_Pad(poly, sz); |
510 | | if (ret == 0) |
511 | | ret = wc_Poly1305_EncodeSizes(poly, aadSz, sz); |
512 | | if (ret == 0) |
513 | | ret = wc_Poly1305Final(poly, tag); |
514 | | return ret; |
515 | | } |
516 | | #endif |
517 | | |
518 | | /* Two-pass: fast vector Poly1305 (small msgs, or stitch not beneficial). */ |
519 | 95 | ret = wc_Chacha_Process(chacha, out, in, sz); |
520 | 95 | if (ret == 0) |
521 | 95 | ret = wc_Poly1305_MAC(poly, aad, aadSz, out, sz, tag, |
522 | 95 | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
523 | 95 | return ret; |
524 | 95 | } |
525 | | |
526 | | /* Verify+decrypt one message with pre-keyed ChaCha20 and Poly1305 contexts - |
527 | | * the decrypt counterpart of wc_ChaCha20Poly1305_Encrypt_ex, for the TLS record |
528 | | * layer. Verifies the Poly1305 tag over AAD+ciphertext and decrypts to out |
529 | | * (in-place safe). Uses the single-pass IFMA decrypt stitch when beneficial. |
530 | | * The plaintext is produced while the tag is computed, so on tag mismatch out |
531 | | * is zeroed and MAC_CMP_FAILED_E returned - callers must check the result. |
532 | | * |
533 | | * chacha ChaCha20 context with the key already set (wc_Chacha_SetKey) |
534 | | * poly Poly1305 scratch context (re-keyed here every call) |
535 | | * out plaintext out (may alias in) |
536 | | * in/sz ciphertext / length |
537 | | * nonce CHACHA20_POLY1305_AEAD_IV_SIZE (12) byte record nonce |
538 | | * tag CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE (16) byte tag to verify |
539 | | * aad/aadSz additional authenticated data |
540 | | * returns 0 on success, MAC_CMP_FAILED_E on tag mismatch, else negative. |
541 | | */ |
542 | | WOLFSSL_API int wc_ChaCha20Poly1305_Decrypt_ex(ChaCha* chacha, Poly1305* poly, |
543 | | byte* out, const byte* in, word32 sz, const byte* nonce, const byte* tag, |
544 | | const byte* aad, word32 aadSz) |
545 | 25 | { |
546 | 25 | byte polyKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; |
547 | 25 | byte calcTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; |
548 | 25 | int ret; |
549 | | |
550 | 25 | if (chacha == NULL || poly == NULL || nonce == NULL || tag == NULL || |
551 | 25 | (sz > 0 && (in == NULL || out == NULL)) || |
552 | 25 | (aadSz > 0 && aad == NULL)) { |
553 | 0 | return BAD_FUNC_ARG; |
554 | 0 | } |
555 | | |
556 | 25 | #ifdef WOLF_CRYPTO_CB |
557 | | /* See the encrypt counterpart, including the 16 byte key exclusion. */ |
558 | 25 | if (chacha->devId != INVALID_DEVID && |
559 | 0 | chacha->devKeySz == CHACHA20_POLY1305_AEAD_KEYSIZE) { |
560 | 0 | ret = wc_CryptoCb_Chacha20Poly1305Decrypt(chacha->devId, chacha->devKey, |
561 | 0 | nonce, aad, aadSz, in, sz, tag, out); |
562 | 0 | if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) |
563 | 0 | return ret; |
564 | 0 | } |
565 | 25 | #endif |
566 | | |
567 | | #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT |
568 | | if (sz <= CHACHA20_POLY1305_SHORT_MAX) |
569 | | return chacha20_poly1305_decrypt_short(chacha, poly, out, in, sz, |
570 | | nonce, tag, aad, aadSz); |
571 | | #endif |
572 | | |
573 | | /* Per-record Poly1305 key = first 32 bytes of ChaCha20(nonce, ctr 0). */ |
574 | 25 | XMEMSET(polyKey, 0, sizeof(polyKey)); |
575 | 25 | ret = wc_Chacha_SetIV(chacha, nonce, |
576 | 25 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
577 | 25 | if (ret == 0) |
578 | 25 | ret = wc_Chacha_Process(chacha, polyKey, polyKey, sizeof(polyKey)); |
579 | 25 | if (ret == 0) /* message data starts at counter 1 */ |
580 | 25 | ret = wc_Chacha_SetIV(chacha, nonce, |
581 | 25 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1); |
582 | 25 | if (ret == 0) |
583 | 25 | ret = wc_Poly1305SetKey(poly, polyKey, sizeof(polyKey)); |
584 | 25 | ForceZero(polyKey, sizeof(polyKey)); |
585 | 25 | if (ret != 0) |
586 | 0 | return ret; |
587 | | |
588 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
589 | | if (sz >= CHACHA20_POLY1305_STITCH_MIN && |
590 | | chacha20_poly1305_use_fused_ifma()) { |
591 | | word32 bulk = sz & ~(word32)0x3ff; |
592 | | /* Scalar running hash (in poly->h) so stitch and tail chain; poly->h, |
593 | | * leftover and started are all zeroed by wc_Poly1305SetKey. */ |
594 | | poly->forceScalar = 1; |
595 | | poly->finished = 1; |
596 | | if (aadSz > 0) { /* H_aad + pad1 (scalar) */ |
597 | | ret = wc_Poly1305Update(poly, aad, aadSz); |
598 | | if (ret == 0) |
599 | | ret = wc_Poly1305_Pad(poly, aadSz); |
600 | | } |
601 | | if (ret == 0) /* stitch: hash CT + decrypt */ |
602 | | ret = chacha20_poly1305_stitch_chunk(chacha, poly, in, out, bulk, |
603 | | 1); |
604 | | if (ret == 0 && sz > bulk) { /* scalar tail */ |
605 | | /* hash the ciphertext tail before decrypt overwrites it */ |
606 | | ret = wc_Poly1305Update(poly, in + bulk, sz - bulk); |
607 | | if (ret == 0) |
608 | | ret = wc_Chacha_Process(chacha, out + bulk, in + bulk, |
609 | | sz - bulk); |
610 | | } |
611 | | if (ret == 0) /* pad2 + lengths + tag */ |
612 | | ret = wc_Poly1305_Pad(poly, sz); |
613 | | if (ret == 0) |
614 | | ret = wc_Poly1305_EncodeSizes(poly, aadSz, sz); |
615 | | if (ret == 0) |
616 | | ret = wc_Poly1305Final(poly, calcTag); |
617 | | } |
618 | | else |
619 | | #endif |
620 | 25 | { |
621 | | /* Two-pass: MAC the ciphertext (in), then decrypt in -> out. */ |
622 | 25 | ret = wc_Poly1305_MAC(poly, aad, aadSz, in, sz, calcTag, |
623 | 25 | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
624 | 25 | if (ret == 0) |
625 | 25 | ret = wc_Chacha_Process(chacha, out, in, sz); |
626 | 25 | } |
627 | | |
628 | 25 | if (ret == 0) |
629 | 25 | ret = wc_ChaCha20Poly1305_CheckTag(tag, calcTag); |
630 | 25 | if (ret != 0 && sz > 0) |
631 | 24 | ForceZero(out, sz); |
632 | 25 | return ret; |
633 | 25 | } |
634 | | |
635 | | /* Clear a temporary ChaChaPoly_Aead. On an AVX-512/IFMA build the Poly1305 |
636 | | * state carries ~320 extra bytes (r5..r8 for the 16-way poly, ifma_h for the |
637 | | * IFMA stitch) that are only ever written on AVX-512-capable CPUs; on a CPU |
638 | | * without AVX-512 they are never touched, so zeroing them on every call is pure |
639 | | * overhead - a large fraction of a small AEAD op. Skip them there (they hold |
640 | | * no key material from this call), and clear the full struct otherwise. */ |
641 | | static WC_INLINE void chacha20_poly1305_aead_zero(ChaChaPoly_Aead* aead) |
642 | 0 | { |
643 | | #ifdef WOLFSSL_POLY1305_AVX512 |
644 | | if (IS_INTEL_AVX512(cpuid_get_flags()) != 0) |
645 | | ForceZero(aead, sizeof(ChaChaPoly_Aead)); |
646 | | else |
647 | | ForceZero(aead, (word32)((const byte*)&aead->poly.r5 |
648 | | - (const byte*)aead)); |
649 | | #else |
650 | 0 | ForceZero(aead, sizeof(ChaChaPoly_Aead)); |
651 | 0 | #endif |
652 | 0 | } |
653 | | |
654 | | WOLFSSL_ABI |
655 | | int wc_ChaCha20Poly1305_Encrypt( |
656 | | const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], |
657 | | const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], |
658 | | const byte* inAAD, const word32 inAADLen, |
659 | | const byte* inPlaintext, const word32 inPlaintextLen, |
660 | | byte* outCiphertext, |
661 | | byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]) |
662 | 0 | { |
663 | 0 | int ret; |
664 | 0 | WC_DECLARE_VAR(aead, ChaChaPoly_Aead, 1, 0); |
665 | | |
666 | | /* Validate function arguments. A NULL data pointer is rejected even at |
667 | | * zero length: the direct/short paths below bypass UpdateData, whose own |
668 | | * check rejects a NULL data pointer unconditionally - this preserves that |
669 | | * contract. A valid pointer with zero length still succeeds (empty AEAD |
670 | | * message). */ |
671 | 0 | if (!inKey || !inIV || |
672 | 0 | inPlaintext == NULL || |
673 | 0 | (inAADLen > 0 && inAAD == NULL) || |
674 | 0 | !outCiphertext || |
675 | 0 | !outAuthTag) |
676 | 0 | { |
677 | 0 | return BAD_FUNC_ARG; |
678 | 0 | } |
679 | | |
680 | 0 | WC_ALLOC_VAR_EX(aead, ChaChaPoly_Aead, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, |
681 | 0 | return MEMORY_E); |
682 | | |
683 | | #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT |
684 | | /* Small record: derive the Poly1305 key and the keystream in a SINGLE |
685 | | * ChaCha pass (SSSE3), then scalar-hash - the same short path Encrypt_ex |
686 | | * uses. Avoids wc_ChaCha20Poly1305_Init's separate scalar poly-key block |
687 | | * and the second scalar data block the two-pass fallback would run. */ |
688 | | if (inPlaintextLen <= CHACHA20_POLY1305_SHORT_MAX) { |
689 | | ret = wc_Chacha_SetKey(&aead->chacha, inKey, |
690 | | CHACHA20_POLY1305_AEAD_KEYSIZE); |
691 | | if (ret == 0) |
692 | | ret = chacha20_poly1305_encrypt_short(&aead->chacha, &aead->poly, |
693 | | outCiphertext, inPlaintext, inPlaintextLen, inIV, outAuthTag, |
694 | | inAAD, inAADLen); |
695 | | } |
696 | | else |
697 | | #endif |
698 | 0 | { |
699 | 0 | ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV, |
700 | 0 | CHACHA20_POLY1305_AEAD_ENCRYPT); |
701 | | /* Prefer the IFMA stitch - full 512-bit ChaCha, beats the others where |
702 | | * AVX-512 + IFMA exist. */ |
703 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
704 | | if (ret == 0 && inPlaintextLen >= CHACHA20_POLY1305_STITCH_MIN && |
705 | | chacha20_poly1305_use_fused_ifma()) { |
706 | | ret = chacha20_poly1305_encrypt_fused_ifma(aead, inAAD, inAADLen, |
707 | | inPlaintext, inPlaintextLen, outCiphertext, outAuthTag); |
708 | | } |
709 | | else |
710 | | #endif |
711 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED |
712 | | if (ret == 0 && inAADLen == 0 && inPlaintextLen >= 256 && |
713 | | chacha20_poly1305_use_fused()) { |
714 | | ret = chacha20_poly1305_encrypt_fused(aead, inPlaintext, |
715 | | inPlaintextLen, outCiphertext, outAuthTag); |
716 | | } |
717 | | else |
718 | | #endif |
719 | 0 | { |
720 | | /* Direct two-pass on the contexts Init already keyed (ChaCha counter is |
721 | | * at 1, Poly1305 keyed). Faster than the UpdateAad/UpdateData/Final |
722 | | * state machine for the common non-stitched case - in particular |
723 | | * wc_Poly1305_MAC hashes the AAD inline instead of buffering it through |
724 | | * UpdateAad, which is where the small-message-with-AAD cost was. */ |
725 | 0 | if (ret == 0) |
726 | 0 | ret = wc_Chacha_Process(&aead->chacha, outCiphertext, inPlaintext, |
727 | 0 | inPlaintextLen); |
728 | 0 | if (ret == 0) |
729 | 0 | ret = wc_Poly1305_MAC(&aead->poly, inAAD, inAADLen, outCiphertext, |
730 | 0 | inPlaintextLen, outAuthTag, |
731 | 0 | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
732 | 0 | } |
733 | 0 | } |
734 | 0 | #ifdef WOLFSSL_SMALL_STACK |
735 | 0 | if (aead != NULL) |
736 | 0 | #endif |
737 | 0 | chacha20_poly1305_aead_zero(aead); |
738 | 0 | WC_FREE_VAR_EX(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
739 | |
|
740 | 0 | return ret; |
741 | 0 | } |
742 | | |
743 | | WOLFSSL_ABI |
744 | | int wc_ChaCha20Poly1305_Decrypt( |
745 | | const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], |
746 | | const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], |
747 | | const byte* inAAD, const word32 inAADLen, |
748 | | const byte* inCiphertext, const word32 inCiphertextLen, |
749 | | const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE], |
750 | | byte* outPlaintext) |
751 | 0 | { |
752 | 0 | int ret; |
753 | 0 | WC_DECLARE_VAR(aead, ChaChaPoly_Aead, 1, 0); |
754 | 0 | byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; |
755 | | |
756 | | /* Validate function arguments. A NULL data pointer is rejected even at |
757 | | * zero length: the direct/short paths below bypass UpdateData, whose own |
758 | | * check rejects a NULL data pointer unconditionally - this preserves that |
759 | | * contract. A valid pointer with zero length still succeeds (empty AEAD |
760 | | * message). */ |
761 | 0 | if (!inKey || !inIV || |
762 | 0 | inCiphertext == NULL || |
763 | 0 | (inAADLen > 0 && inAAD == NULL) || |
764 | 0 | !inAuthTag || |
765 | 0 | !outPlaintext) |
766 | 0 | { |
767 | 0 | return BAD_FUNC_ARG; |
768 | 0 | } |
769 | | |
770 | 0 | WC_ALLOC_VAR_EX(aead, ChaChaPoly_Aead, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, |
771 | 0 | return MEMORY_E); |
772 | | |
773 | 0 | XMEMSET(calculatedAuthTag, 0, sizeof(calculatedAuthTag)); |
774 | |
|
775 | | #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT |
776 | | /* Small record: single ChaCha pass for poly key + keystream, MAC the |
777 | | * ciphertext and verify BEFORE decrypting (no plaintext on bad tag). Same |
778 | | * short path Decrypt_ex uses; avoids Init's extra scalar poly-key block. */ |
779 | | if (inCiphertextLen <= CHACHA20_POLY1305_SHORT_MAX) { |
780 | | ret = wc_Chacha_SetKey(&aead->chacha, inKey, |
781 | | CHACHA20_POLY1305_AEAD_KEYSIZE); |
782 | | if (ret == 0) |
783 | | ret = chacha20_poly1305_decrypt_short(&aead->chacha, &aead->poly, |
784 | | outPlaintext, inCiphertext, inCiphertextLen, inIV, inAuthTag, |
785 | | inAAD, inAADLen); |
786 | | } |
787 | | else |
788 | | #endif |
789 | 0 | { |
790 | 0 | ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV, |
791 | 0 | CHACHA20_POLY1305_AEAD_DECRYPT); |
792 | | /* Direct two-pass on the contexts Init already keyed: MAC the ciphertext, |
793 | | * verify the tag, then decrypt - verify-then-decrypt, so no plaintext is |
794 | | * produced on a bad tag. Faster than the UpdateAad/UpdateData/Final state |
795 | | * machine (wc_Poly1305_MAC hashes the AAD inline). In-place safe: the MAC |
796 | | * reads inCiphertext before the decrypt overwrites it. */ |
797 | 0 | if (ret == 0) |
798 | 0 | ret = wc_Poly1305_MAC(&aead->poly, inAAD, inAADLen, inCiphertext, |
799 | 0 | inCiphertextLen, calculatedAuthTag, |
800 | 0 | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE); |
801 | 0 | if (ret == 0) |
802 | 0 | ret = wc_ChaCha20Poly1305_CheckTag(inAuthTag, calculatedAuthTag); |
803 | 0 | if (ret == 0) |
804 | 0 | ret = wc_Chacha_Process(&aead->chacha, outPlaintext, inCiphertext, |
805 | 0 | inCiphertextLen); |
806 | 0 | } |
807 | |
|
808 | 0 | if (ret != 0) { |
809 | | /* zero plaintext on error */ |
810 | 0 | ForceZero(outPlaintext, inCiphertextLen); |
811 | 0 | } |
812 | 0 | #ifdef WOLFSSL_SMALL_STACK |
813 | 0 | if (aead != NULL) |
814 | 0 | #endif |
815 | 0 | chacha20_poly1305_aead_zero(aead); |
816 | 0 | WC_FREE_VAR_EX(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
817 | |
|
818 | 0 | return ret; |
819 | 0 | } |
820 | | |
821 | | int wc_ChaCha20Poly1305_CheckTag( |
822 | | const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE], |
823 | | const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]) |
824 | 25 | { |
825 | 25 | int ret = 0; |
826 | 25 | if (authTag == NULL || authTagChk == NULL) { |
827 | 0 | return BAD_FUNC_ARG; |
828 | 0 | } |
829 | 25 | if (ConstantCompare(authTag, authTagChk, |
830 | 25 | CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0) { |
831 | 25 | ret = MAC_CMP_FAILED_E; |
832 | 25 | } |
833 | 25 | return ret; |
834 | 25 | } |
835 | | |
836 | | int wc_ChaCha20Poly1305_Init_ex(ChaChaPoly_Aead* aead, |
837 | | const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], |
838 | | const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], |
839 | | int isEncrypt, void* heap, int devId) |
840 | 0 | { |
841 | 0 | int ret; |
842 | 0 | byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; |
843 | | |
844 | | /* authKey will hold the derived Poly1305 key. Register from the top with a |
845 | | * zero baseline so every exit (including the arg-check return, where it is |
846 | | * still zero) is covered. */ |
847 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
848 | | XMEMSET(authKey, 0, sizeof(authKey)); |
849 | | wc_MemZero_Add("wc_ChaCha20Poly1305_Init authKey", authKey, |
850 | | sizeof(authKey)); |
851 | | #endif |
852 | | |
853 | | /* check arguments */ |
854 | 0 | if (aead == NULL || inKey == NULL || inIV == NULL) { |
855 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
856 | | wc_MemZero_Check(authKey, sizeof(authKey)); |
857 | | #endif |
858 | 0 | return BAD_FUNC_ARG; |
859 | 0 | } |
860 | | |
861 | | /* setup aead context (full clear: the aadLen/dataLen/state wrapper fields |
862 | | * live after the Poly1305 member and must be initialized) */ |
863 | 0 | XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead)); |
864 | 0 | XMEMSET(authKey, 0, sizeof(authKey)); |
865 | 0 | aead->isEncrypt = isEncrypt ? 1 : 0; |
866 | | |
867 | | /* Initialize the ChaCha20 context (key and iv) */ |
868 | 0 | ret = wc_Chacha_SetKey_ex(&aead->chacha, inKey, |
869 | 0 | CHACHA20_POLY1305_AEAD_KEYSIZE, heap, devId); |
870 | 0 | (void)heap; |
871 | 0 | (void)devId; |
872 | 0 | if (ret == 0) { |
873 | 0 | ret = wc_Chacha_SetIV(&aead->chacha, inIV, |
874 | 0 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); |
875 | 0 | } |
876 | | |
877 | | /* Create the Poly1305 key */ |
878 | 0 | if (ret == 0) { |
879 | 0 | ret = wc_Chacha_Process(&aead->chacha, authKey, authKey, |
880 | 0 | CHACHA20_POLY1305_AEAD_KEYSIZE); |
881 | 0 | } |
882 | | |
883 | | /* Initialize Poly1305 context */ |
884 | 0 | if (ret == 0) { |
885 | 0 | ret = wc_Poly1305SetKey(&aead->poly, authKey, |
886 | 0 | CHACHA20_POLY1305_AEAD_KEYSIZE); |
887 | 0 | } |
888 | | |
889 | | /* advance counter by 1 after creating Poly1305 key */ |
890 | 0 | if (ret == 0) { |
891 | 0 | ret = wc_Chacha_SetIV(&aead->chacha, inIV, |
892 | 0 | CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1); |
893 | 0 | } |
894 | |
|
895 | 0 | if (ret == 0) { |
896 | 0 | aead->state = CHACHA20_POLY1305_STATE_READY; |
897 | 0 | } |
898 | |
|
899 | 0 | ForceZero(authKey, sizeof(authKey)); |
900 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
901 | | wc_MemZero_Check(authKey, sizeof(authKey)); |
902 | | #endif |
903 | |
|
904 | 0 | return ret; |
905 | 0 | } |
906 | | |
907 | | int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, |
908 | | const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], |
909 | | const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], |
910 | | int isEncrypt) |
911 | 0 | { |
912 | 0 | return wc_ChaCha20Poly1305_Init_ex(aead, inKey, inIV, isEncrypt, NULL, |
913 | 0 | INVALID_DEVID); |
914 | 0 | } |
915 | | |
916 | | /* optional additional authentication data */ |
917 | | int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead, |
918 | | const byte* inAAD, word32 inAADLen) |
919 | 0 | { |
920 | 0 | int ret = 0; |
921 | |
|
922 | 0 | if (aead == NULL || (inAAD == NULL && inAADLen > 0)) { |
923 | 0 | return BAD_FUNC_ARG; |
924 | 0 | } |
925 | 0 | if (aead->state != CHACHA20_POLY1305_STATE_READY && |
926 | 0 | aead->state != CHACHA20_POLY1305_STATE_AAD) { |
927 | 0 | return BAD_STATE_E; |
928 | 0 | } |
929 | 0 | if (inAADLen > CHACHA20_POLY1305_MAX - aead->aadLen) |
930 | 0 | return CHACHA_POLY_OVERFLOW; |
931 | | |
932 | 0 | if (inAAD && inAADLen > 0) { |
933 | 0 | ret = wc_Poly1305Update(&aead->poly, inAAD, inAADLen); |
934 | 0 | if (ret == 0) { |
935 | 0 | aead->aadLen += inAADLen; |
936 | 0 | aead->state = CHACHA20_POLY1305_STATE_AAD; |
937 | 0 | } |
938 | 0 | } |
939 | |
|
940 | 0 | return ret; |
941 | 0 | } |
942 | | |
943 | | /* inData and outData can be same pointer (inline) */ |
944 | | int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead, |
945 | | const byte* inData, byte* outData, word32 dataLen) |
946 | 0 | { |
947 | 0 | int ret = 0; |
948 | |
|
949 | 0 | if (aead == NULL || inData == NULL || outData == NULL) { |
950 | 0 | return BAD_FUNC_ARG; |
951 | 0 | } |
952 | 0 | if (aead->state != CHACHA20_POLY1305_STATE_READY && |
953 | 0 | aead->state != CHACHA20_POLY1305_STATE_AAD && |
954 | 0 | aead->state != CHACHA20_POLY1305_STATE_DATA) { |
955 | 0 | return BAD_STATE_E; |
956 | 0 | } |
957 | 0 | if (dataLen > CHACHA20_POLY1305_MAX - aead->dataLen) |
958 | 0 | return CHACHA_POLY_OVERFLOW; |
959 | | |
960 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
961 | | /* Enter scalar-stitch mode at the first data chunk when it is large enough |
962 | | * to benefit and no vector Poly1305 state exists yet (started==0): any AAD |
963 | | * so far is then fully buffered by the vector path. The vector buffer can |
964 | | * hold >16 bytes, which the scalar path cannot resume, so re-hash the |
965 | | * buffered AAD cleanly through the scalar path; the pad below finishes it. |
966 | | * If the AAD was large enough to be processed by the vector path |
967 | | * (started==1) we cannot switch, so it stays two-pass - no regression. |
968 | | * The IFMA stitch handles both directions (decrypt hashes the ciphertext |
969 | | * input). */ |
970 | | if (!aead->poly.forceScalar && aead->poly.started == 0 && |
971 | | aead->dataLen == 0 && dataLen >= CHACHA20_POLY1305_STITCH_MIN && |
972 | | chacha20_poly1305_use_fused_ifma()) { |
973 | | word32 aadN = (word32)aead->poly.leftover; |
974 | | byte aadBuf[8 * POLY1305_BLOCK_SIZE]; |
975 | | if (aadN > 0) |
976 | | XMEMCPY(aadBuf, aead->poly.buffer, aadN); |
977 | | aead->poly.forceScalar = 1; |
978 | | aead->poly.finished = 1; |
979 | | aead->poly.leftover = 0; |
980 | | if (aadN > 0) |
981 | | ret = wc_Poly1305Update(&aead->poly, aadBuf, aadN); |
982 | | } |
983 | | #endif |
984 | | |
985 | | /* Pad the AAD */ |
986 | 0 | if (ret == 0 && aead->state == CHACHA20_POLY1305_STATE_AAD) { |
987 | 0 | ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen); |
988 | 0 | } |
989 | | |
990 | | /* advance state */ |
991 | 0 | aead->state = CHACHA20_POLY1305_STATE_DATA; |
992 | | |
993 | | /* Perform ChaCha20 encrypt/decrypt and Poly1305 auth calc */ |
994 | 0 | if (ret == 0) { |
995 | 0 | if (aead->isEncrypt) { |
996 | 0 | const byte* in = inData; |
997 | 0 | byte* out = outData; |
998 | 0 | word32 len = dataLen; |
999 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
1000 | | /* Stitch the 1024-aligned bulk (encrypt + auth in one pass) when |
1001 | | * the data so far is 64-byte aligned - so BOTH ChaCha (no buffered |
1002 | | * partial keystream) and Poly1305 (leftover==0, running hash in |
1003 | | * ctx->h) are at a block boundary - and we are in scalar-hash mode |
1004 | | * (set at Init for AVX-512+IFMA encrypt). The kernel processes |
1005 | | * whole blocks from the ChaCha counter, so a mid-block position |
1006 | | * would make it skip the buffered keystream; the 64-alignment check |
1007 | | * prevents that. The remainder falls through to the path below. */ |
1008 | | if (aead->poly.forceScalar && (aead->dataLen & 63) == 0 && |
1009 | | len >= CHACHA20_POLY1305_STITCH_MIN) { |
1010 | | word32 bulk = len & ~(word32)0x3ff; |
1011 | | ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly, |
1012 | | in, out, bulk, 0); |
1013 | | in += bulk; |
1014 | | out += bulk; |
1015 | | len -= bulk; |
1016 | | } |
1017 | | #endif |
1018 | 0 | if (ret == 0 && len > 0) { |
1019 | 0 | ret = wc_Chacha_Process(&aead->chacha, out, in, len); |
1020 | 0 | if (ret == 0) |
1021 | 0 | ret = wc_Poly1305Update(&aead->poly, out, len); |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | else { |
1025 | 0 | const byte* in = inData; |
1026 | 0 | byte* out = outData; |
1027 | 0 | word32 len = dataLen; |
1028 | | #ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA |
1029 | | /* Stitch the 1024-aligned bulk (auth + decrypt in one pass) under |
1030 | | * the same conditions as encrypt. The kernel hashes the ciphertext |
1031 | | * (in) before overwriting it, so in-place decrypt is safe. */ |
1032 | | if (aead->poly.forceScalar && (aead->dataLen & 63) == 0 && |
1033 | | len >= CHACHA20_POLY1305_STITCH_MIN) { |
1034 | | word32 bulk = len & ~(word32)0x3ff; |
1035 | | ret = chacha20_poly1305_stitch_chunk(&aead->chacha, &aead->poly, |
1036 | | in, out, bulk, 1); |
1037 | | in += bulk; |
1038 | | out += bulk; |
1039 | | len -= bulk; |
1040 | | } |
1041 | | #endif |
1042 | 0 | if (ret == 0 && len > 0) { |
1043 | | /* hash the ciphertext before decrypt overwrites it */ |
1044 | 0 | ret = wc_Poly1305Update(&aead->poly, in, len); |
1045 | 0 | if (ret == 0) |
1046 | 0 | ret = wc_Chacha_Process(&aead->chacha, out, in, len); |
1047 | 0 | } |
1048 | 0 | } |
1049 | 0 | } |
1050 | 0 | if (ret == 0) { |
1051 | 0 | aead->dataLen += dataLen; |
1052 | 0 | } |
1053 | 0 | return ret; |
1054 | 0 | } |
1055 | | |
1056 | | int wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead* aead, |
1057 | | byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]) |
1058 | 0 | { |
1059 | 0 | int ret = 0; |
1060 | |
|
1061 | 0 | if (aead == NULL || outAuthTag == NULL) { |
1062 | 0 | return BAD_FUNC_ARG; |
1063 | 0 | } |
1064 | 0 | if (aead->state != CHACHA20_POLY1305_STATE_AAD && |
1065 | 0 | aead->state != CHACHA20_POLY1305_STATE_DATA) { |
1066 | 0 | return BAD_STATE_E; |
1067 | 0 | } |
1068 | | |
1069 | | /* Pad the AAD - Make sure it is done */ |
1070 | 0 | if (aead->state == CHACHA20_POLY1305_STATE_AAD) { |
1071 | 0 | ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen); |
1072 | 0 | } |
1073 | | |
1074 | | /* Pad the plaintext/ciphertext to 16 bytes */ |
1075 | 0 | if (ret == 0) { |
1076 | 0 | ret = wc_Poly1305_Pad(&aead->poly, aead->dataLen); |
1077 | 0 | } |
1078 | | |
1079 | | /* Add the aad length and plaintext/ciphertext length */ |
1080 | 0 | if (ret == 0) { |
1081 | 0 | ret = wc_Poly1305_EncodeSizes(&aead->poly, aead->aadLen, |
1082 | 0 | aead->dataLen); |
1083 | 0 | } |
1084 | | |
1085 | | /* Finalize the auth tag */ |
1086 | 0 | if (ret == 0) { |
1087 | 0 | ret = wc_Poly1305Final(&aead->poly, outAuthTag); |
1088 | 0 | } |
1089 | | |
1090 | | /* reset and cleanup sensitive context */ |
1091 | 0 | ForceZero(aead, sizeof(ChaChaPoly_Aead)); |
1092 | |
|
1093 | 0 | return ret; |
1094 | 0 | } |
1095 | | |
1096 | | #ifdef HAVE_XCHACHA |
1097 | | |
1098 | | int wc_XChaCha20Poly1305_Init( |
1099 | | ChaChaPoly_Aead *aead, |
1100 | | const byte *ad, word32 ad_len, |
1101 | | const byte *nonce, word32 nonce_len, |
1102 | | const byte *key, word32 key_len, |
1103 | | int isEncrypt) |
1104 | 0 | { |
1105 | 0 | byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; |
1106 | 0 | int ret; |
1107 | | |
1108 | | /* authKey will hold the derived Poly1305 key. Register from the top with a |
1109 | | * zero baseline so every exit (including the arg/setup error returns below, |
1110 | | * where it is still zero) is covered. */ |
1111 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1112 | | XMEMSET(authKey, 0, sizeof authKey); |
1113 | | wc_MemZero_Add("xchacha20poly1305 authKey", authKey, sizeof authKey); |
1114 | | #endif |
1115 | |
|
1116 | 0 | if ((aead == NULL) || (ad == NULL && ad_len > 0) || (nonce == NULL) || |
1117 | 0 | (key == NULL)) { |
1118 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1119 | | wc_MemZero_Check(authKey, sizeof authKey); |
1120 | | #endif |
1121 | 0 | return BAD_FUNC_ARG; |
1122 | 0 | } |
1123 | | |
1124 | 0 | if ((key_len != CHACHA20_POLY1305_AEAD_KEYSIZE) || |
1125 | 0 | (nonce_len != XCHACHA20_POLY1305_AEAD_NONCE_SIZE)) { |
1126 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1127 | | wc_MemZero_Check(authKey, sizeof authKey); |
1128 | | #endif |
1129 | 0 | return BAD_FUNC_ARG; |
1130 | 0 | } |
1131 | | |
1132 | | /* setup aead context (full clear: the aadLen/dataLen/state wrapper fields |
1133 | | * live after the Poly1305 member and must be initialized) */ |
1134 | 0 | XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead)); |
1135 | |
|
1136 | 0 | if ((ret = wc_XChacha_SetKey(&aead->chacha, |
1137 | 0 | key, key_len, |
1138 | 0 | nonce, nonce_len, |
1139 | 0 | 0 /* counter */)) < 0) { |
1140 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1141 | | wc_MemZero_Check(authKey, sizeof authKey); |
1142 | | #endif |
1143 | 0 | return ret; |
1144 | 0 | } |
1145 | | |
1146 | 0 | XMEMSET(authKey, 0, sizeof authKey); |
1147 | | |
1148 | | /* Create the Poly1305 key */ |
1149 | 0 | if ((ret = wc_Chacha_Process(&aead->chacha, authKey, authKey, |
1150 | 0 | (word32)sizeof authKey)) < 0) |
1151 | 0 | goto out; |
1152 | | /* advance to start of the next ChaCha block. */ |
1153 | 0 | wc_Chacha_purge_current_block(&aead->chacha); |
1154 | | |
1155 | | /* Initialize Poly1305 context */ |
1156 | 0 | if ((ret = wc_Poly1305SetKey(&aead->poly, authKey, |
1157 | 0 | (word32)sizeof authKey)) < 0) |
1158 | 0 | goto out; |
1159 | | |
1160 | | /* Absorb the AAD exactly as wc_ChaCha20Poly1305_UpdateAad does: hash it |
1161 | | * and record its length. The 16-byte pad is applied by the first |
1162 | | * wc_ChaCha20Poly1305_UpdateData or wc_ChaCha20Poly1305_Final call (or by |
1163 | | * the one-shot helper below), so the AAD length that |
1164 | | * wc_ChaCha20Poly1305_Final encodes into the tag is the length hashed. */ |
1165 | 0 | if (ad_len > 0) { |
1166 | 0 | if ((ret = wc_Poly1305Update(&aead->poly, ad, ad_len)) < 0) |
1167 | 0 | goto out; |
1168 | 0 | aead->aadLen = ad_len; |
1169 | 0 | } |
1170 | | |
1171 | 0 | aead->isEncrypt = isEncrypt ? 1 : 0; |
1172 | 0 | aead->state = CHACHA20_POLY1305_STATE_AAD; |
1173 | |
|
1174 | 0 | ret = 0; |
1175 | |
|
1176 | 0 | out: |
1177 | 0 | ForceZero(authKey, sizeof(authKey)); |
1178 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1179 | | wc_MemZero_Check(authKey, sizeof(authKey)); |
1180 | | #endif |
1181 | |
|
1182 | 0 | return ret; |
1183 | 0 | } |
1184 | | |
1185 | | static WC_INLINE int wc_XChaCha20Poly1305_crypt_oneshot( |
1186 | | byte *dst, const size_t dst_space, |
1187 | | const byte *src, const size_t src_len, |
1188 | | const byte *ad, const size_t ad_len, |
1189 | | const byte *nonce, const size_t nonce_len, |
1190 | | const byte *key, const size_t key_len, |
1191 | | int isEncrypt) |
1192 | 0 | { |
1193 | 0 | int ret; |
1194 | 0 | size_t dst_len; |
1195 | 0 | const byte *src_i; |
1196 | 0 | byte *dst_i; |
1197 | 0 | size_t src_len_rem; |
1198 | 0 | #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) |
1199 | 0 | ChaChaPoly_Aead *aead = (ChaChaPoly_Aead *)XMALLOC(sizeof *aead, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
1200 | |
|
1201 | 0 | if (aead == NULL) |
1202 | 0 | return MEMORY_E; |
1203 | | #else |
1204 | | ChaChaPoly_Aead aead_buf, *aead = &aead_buf; |
1205 | | #endif |
1206 | | |
1207 | 0 | if (isEncrypt) { |
1208 | 0 | if (src_len > (size_t)(CHACHA20_POLY1305_MAX - POLY1305_DIGEST_SIZE)) { |
1209 | 0 | ret = BAD_FUNC_ARG; |
1210 | 0 | goto out; |
1211 | 0 | } |
1212 | 0 | dst_len = src_len + (size_t)POLY1305_DIGEST_SIZE; |
1213 | 0 | } |
1214 | 0 | else { |
1215 | 0 | if (src_len < POLY1305_DIGEST_SIZE) { |
1216 | 0 | ret = BAD_FUNC_ARG; |
1217 | 0 | goto out; |
1218 | 0 | } |
1219 | 0 | dst_len = src_len - (size_t)POLY1305_DIGEST_SIZE; |
1220 | 0 | } |
1221 | | |
1222 | 0 | if ((dst == NULL) || (src == NULL)) { |
1223 | 0 | ret = BAD_FUNC_ARG; |
1224 | 0 | goto out; |
1225 | 0 | } |
1226 | | |
1227 | 0 | if (dst_space < dst_len) { |
1228 | 0 | ret = BUFFER_E; |
1229 | 0 | goto out; |
1230 | 0 | } |
1231 | | |
1232 | | /* Sanity check lengths to prevent truncation when cast to word32. */ |
1233 | 0 | if ((ad_len > WOLFSSL_MAX_32BIT) || |
1234 | 0 | (nonce_len > WOLFSSL_MAX_32BIT) || |
1235 | 0 | (key_len > WOLFSSL_MAX_32BIT)) { |
1236 | 0 | ret = BAD_FUNC_ARG; |
1237 | 0 | goto out; |
1238 | 0 | } |
1239 | | |
1240 | 0 | if ((ret = wc_XChaCha20Poly1305_Init(aead, ad, (word32)ad_len, |
1241 | 0 | nonce, (word32)nonce_len, |
1242 | 0 | key, (word32)key_len, 1)) < 0) |
1243 | 0 | goto out; |
1244 | | |
1245 | | /* wc_XChaCha20Poly1305_Init hashes the AAD and leaves the pad to the |
1246 | | * streaming data call; this helper bypasses that call, so pad here. */ |
1247 | 0 | if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)ad_len)) < 0) |
1248 | 0 | goto out; |
1249 | | |
1250 | | #ifdef WOLFSSL_CHECK_MEM_ZERO |
1251 | | wc_MemZero_Add("wc_XChaCha20Poly1305_crypt_oneshot aead", aead, |
1252 | | sizeof(ChaChaPoly_Aead)); |
1253 | | #endif |
1254 | | |
1255 | | /* process the input in 16k pieces to accommodate src_lens that don't fit in a word32, |
1256 | | * and to exploit hot cache for the input data. |
1257 | | */ |
1258 | 0 | src_i = src; |
1259 | 0 | src_len_rem = isEncrypt ? src_len : dst_len; |
1260 | 0 | dst_i = dst; |
1261 | 0 | while (src_len_rem > 0) { |
1262 | 0 | word32 this_src_len = |
1263 | 0 | (src_len_rem > 16384) ? |
1264 | 0 | 16384 : |
1265 | 0 | (word32)src_len_rem; |
1266 | |
|
1267 | 0 | if ((ret = wc_Chacha_Process(&aead->chacha, dst_i, src_i, this_src_len)) < 0) |
1268 | 0 | goto out; |
1269 | | |
1270 | 0 | if ((ret = wc_Poly1305Update(&aead->poly, isEncrypt ? dst_i : src_i, this_src_len)) < 0) |
1271 | 0 | goto out; |
1272 | | |
1273 | 0 | src_len_rem -= (size_t)this_src_len; |
1274 | 0 | src_i += this_src_len; |
1275 | 0 | dst_i += this_src_len; |
1276 | 0 | } |
1277 | | |
1278 | 0 | if (aead->poly.leftover) { |
1279 | 0 | if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)aead->poly.leftover)) < 0) |
1280 | 0 | goto out; |
1281 | 0 | } |
1282 | | |
1283 | 0 | #ifdef WORD64_AVAILABLE |
1284 | 0 | ret = wc_Poly1305_EncodeSizes64(&aead->poly, ad_len, isEncrypt ? src_len : dst_len); |
1285 | | #else |
1286 | | ret = wc_Poly1305_EncodeSizes(&aead->poly, ad_len, isEncrypt ? src_len : dst_len); |
1287 | | #endif |
1288 | 0 | if (ret < 0) |
1289 | 0 | goto out; |
1290 | | |
1291 | 0 | if (isEncrypt) |
1292 | 0 | ret = wc_Poly1305Final(&aead->poly, dst + src_len); |
1293 | 0 | else { |
1294 | 0 | byte outAuthTag[POLY1305_DIGEST_SIZE]; |
1295 | |
|
1296 | 0 | if ((ret = wc_Poly1305Final(&aead->poly, outAuthTag)) < 0) |
1297 | 0 | goto out; |
1298 | | |
1299 | 0 | if (ConstantCompare(outAuthTag, src + dst_len, POLY1305_DIGEST_SIZE) |
1300 | 0 | != 0) { |
1301 | 0 | ForceZero(dst, dst_space); |
1302 | 0 | ret = MAC_CMP_FAILED_E; |
1303 | 0 | goto out; |
1304 | 0 | } |
1305 | 0 | } |
1306 | | |
1307 | 0 | out: |
1308 | |
|
1309 | 0 | ForceZero(aead, sizeof *aead); |
1310 | |
|
1311 | 0 | #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) |
1312 | 0 | XFREE(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
1313 | | #elif defined(WOLFSSL_CHECK_MEM_ZERO) |
1314 | | wc_MemZero_Check(aead, sizeof(ChaChaPoly_Aead)); |
1315 | | #endif |
1316 | |
|
1317 | 0 | return ret; |
1318 | 0 | } |
1319 | | |
1320 | | int wc_XChaCha20Poly1305_Encrypt( |
1321 | | byte *dst, const size_t dst_space, |
1322 | | const byte *src, const size_t src_len, |
1323 | | const byte *ad, const size_t ad_len, |
1324 | | const byte *nonce, const size_t nonce_len, |
1325 | | const byte *key, const size_t key_len) |
1326 | 0 | { |
1327 | 0 | return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 1); |
1328 | 0 | } |
1329 | | |
1330 | | int wc_XChaCha20Poly1305_Decrypt( |
1331 | | byte *dst, const size_t dst_space, |
1332 | | const byte *src, const size_t src_len, |
1333 | | const byte *ad, const size_t ad_len, |
1334 | | const byte *nonce, const size_t nonce_len, |
1335 | | const byte *key, const size_t key_len) |
1336 | 0 | { |
1337 | 0 | return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 0); |
1338 | 0 | } |
1339 | | |
1340 | | #endif /* HAVE_XCHACHA */ |
1341 | | |
1342 | | #endif /* HAVE_CHACHA && HAVE_POLY1305 */ |