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