/src/openssl/crypto/ascon/ascon_aead128.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | #include "crypto/ascon.h" |
11 | | #include <string.h> |
12 | | #include "internal/cryptlib.h" |
13 | | #include <openssl/byteorder.h> |
14 | | #include <openssl/crypto.h> |
15 | | |
16 | 0 | #define ROR64(x, i) ((x << (64 - i)) | (x >> i)) |
17 | | |
18 | | /** |
19 | | * constant addition layer, NIST SP 800-232 Table 5 |
20 | | * 3c 2d 1e 0f f0 e1 d2 c3 b4 a5 96 87 78 69 5a 4b |
21 | | */ |
22 | | #define ASCONPC(x0, x1, x2, x3, x4, rcon) \ |
23 | 0 | do { \ |
24 | 0 | x2 ^= rcon; \ |
25 | 0 | } while (0) |
26 | | |
27 | | /** |
28 | | * nonlinear layer, lifted from p43 of |
29 | | * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/ |
30 | | * documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf |
31 | | */ |
32 | | #define ASCONPS(x0, x1, x2, x3, x4) \ |
33 | 0 | do { \ |
34 | 0 | uint64_t q0, q1; \ |
35 | 0 | \ |
36 | 0 | x0 ^= x4; \ |
37 | 0 | x4 ^= x3; \ |
38 | 0 | x2 ^= x1; \ |
39 | 0 | q0 = x0 & (~x4); \ |
40 | 0 | q1 = x2 & (~x1); \ |
41 | 0 | x0 ^= q1; \ |
42 | 0 | q1 = x4 & (~x3); \ |
43 | 0 | x2 ^= q1; \ |
44 | 0 | q1 = x1 & (~x0); \ |
45 | 0 | x4 ^= q1; \ |
46 | 0 | q1 = x3 & (~x2); \ |
47 | 0 | x1 ^= q1; \ |
48 | 0 | x3 ^= q0; \ |
49 | 0 | x1 ^= x0; \ |
50 | 0 | x3 ^= x2; \ |
51 | 0 | x0 ^= x4; \ |
52 | 0 | x2 = ~x2; \ |
53 | 0 | } while (0) |
54 | | |
55 | | /* linear layer, NIST SP 800-232 Figure 3 */ |
56 | | #define ASCONPL(x0, x1, x2, x3, x4) \ |
57 | 0 | do { \ |
58 | 0 | x0 ^= ROR64(x0, 19) ^ ROR64(x0, 28); \ |
59 | 0 | x1 ^= ROR64(x1, 61) ^ ROR64(x1, 39); \ |
60 | 0 | x2 ^= ROR64(x2, 1) ^ ROR64(x2, 6); \ |
61 | 0 | x3 ^= ROR64(x3, 10) ^ ROR64(x3, 17); \ |
62 | 0 | x4 ^= ROR64(x4, 7) ^ ROR64(x4, 41); \ |
63 | 0 | } while (0) |
64 | | |
65 | | /* one round */ |
66 | | #define ASCONP1(x0, x1, x2, x3, x4, rcon) \ |
67 | 0 | do { \ |
68 | 0 | ASCONPC(x0, x1, x2, x3, x4, rcon); \ |
69 | 0 | ASCONPS(x0, x1, x2, x3, x4); \ |
70 | 0 | ASCONPL(x0, x1, x2, x3, x4); \ |
71 | 0 | } while (0) |
72 | | |
73 | | /* 8 rounds */ |
74 | | #define ASCONP8(x0, x1, x2, x3, x4) \ |
75 | 0 | do { \ |
76 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xB4ULL); \ |
77 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xA5ULL); \ |
78 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x96ULL); \ |
79 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x87ULL); \ |
80 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x78ULL); \ |
81 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x69ULL); \ |
82 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x5AULL); \ |
83 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0x4BULL); \ |
84 | 0 | } while (0) |
85 | | |
86 | | /* 12 rounds */ |
87 | | #define ASCONP12(x0, x1, x2, x3, x4) \ |
88 | 0 | do { \ |
89 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xF0ULL); \ |
90 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xE1ULL); \ |
91 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xD2ULL); \ |
92 | 0 | ASCONP1(x0, x1, x2, x3, x4, 0xC3ULL); \ |
93 | 0 | ASCONP8(x0, x1, x2, x3, x4); \ |
94 | 0 | } while (0) |
95 | | |
96 | | /* misc ascon flags for the context */ |
97 | 0 | #define ASCONFLG_AAD 0x0000000000000001ULL /* has AAD inputs? */ |
98 | 0 | #define ASCONFLG_DEC 0x0000000000000002ULL /* in decrypt mode? */ |
99 | 0 | #define ASCONFLG_DOMAINSEP 0x8000000000000000ULL /* ready to absorb non-AAD? */ |
100 | | |
101 | | static ossl_inline void ascon_aead128_update(ascon_aead128_ctx *ctx, |
102 | | unsigned char *out, |
103 | | const unsigned char *in, |
104 | | size_t len) |
105 | 0 | { |
106 | 0 | uint64_t s0, s1, s2, s3, s4, flags; |
107 | 0 | unsigned char pad = 0x01; |
108 | |
|
109 | 0 | if (ctx->flags & ASCONFLG_DOMAINSEP) { |
110 | 0 | flags = ctx->flags; |
111 | 0 | if (flags & ASCONFLG_AAD) { |
112 | 0 | ctx->flags = 0; |
113 | 0 | ascon_aead128_update(ctx, NULL, &pad, 1); |
114 | 0 | ASCONP8(ctx->state[0], ctx->state[1], ctx->state[2], ctx->state[3], ctx->state[4]); |
115 | 0 | flags ^= ASCONFLG_AAD; |
116 | 0 | ctx->offset = 0; |
117 | 0 | } |
118 | 0 | ctx->state[4] ^= ASCONFLG_DOMAINSEP; |
119 | 0 | flags ^= ASCONFLG_DOMAINSEP; |
120 | 0 | ctx->flags = flags; |
121 | 0 | } |
122 | |
|
123 | 0 | s0 = ctx->state[0]; |
124 | 0 | s1 = ctx->state[1]; |
125 | 0 | s2 = ctx->state[2]; |
126 | 0 | s3 = ctx->state[3]; |
127 | 0 | s4 = ctx->state[4]; |
128 | |
|
129 | 0 | while (len--) { |
130 | 0 | unsigned char ob, ib = *in++; |
131 | |
|
132 | 0 | if (ctx->offset >= 16) { |
133 | 0 | ASCONP8(s0, s1, s2, s3, s4); |
134 | 0 | ctx->offset = 0; |
135 | 0 | } |
136 | |
|
137 | 0 | if (ctx->flags & ASCONFLG_DEC) { |
138 | 0 | if (ctx->offset >= 8) { |
139 | 0 | ob = (unsigned char)(s1 >> 8 * (ctx->offset & 0x7)) ^ ib; |
140 | 0 | s1 ^= (uint64_t)(ob) << 8 * (ctx->offset & 0x7); |
141 | 0 | } else { |
142 | 0 | ob = (unsigned char)(s0 >> 8 * ctx->offset) ^ ib; |
143 | 0 | s0 ^= (uint64_t)(ob) << 8 * ctx->offset; |
144 | 0 | } |
145 | 0 | } else { |
146 | 0 | if (ctx->offset >= 8) { |
147 | 0 | s1 ^= (uint64_t)(ib) << 8 * (ctx->offset & 0x7); |
148 | 0 | ob = (unsigned char)(s1 >> 8 * (ctx->offset & 0x7)); |
149 | 0 | } else { |
150 | 0 | s0 ^= (uint64_t)(ib) << 8 * ctx->offset; |
151 | 0 | ob = (unsigned char)(s0 >> 8 * ctx->offset); |
152 | 0 | } |
153 | 0 | } |
154 | |
|
155 | 0 | if (out != NULL) |
156 | 0 | *out++ = ob; |
157 | |
|
158 | 0 | ctx->offset++; |
159 | 0 | } |
160 | |
|
161 | 0 | ctx->state[0] = s0; |
162 | 0 | ctx->state[1] = s1; |
163 | 0 | ctx->state[2] = s2; |
164 | 0 | ctx->state[3] = s3; |
165 | 0 | ctx->state[4] = s4; |
166 | 0 | } |
167 | | |
168 | | static void ascon_aead128_encrypt_update(ascon_aead128_ctx *ctx, unsigned char *ct, |
169 | | const unsigned char *pt, size_t len) |
170 | 0 | { |
171 | 0 | ascon_aead128_update(ctx, ct, pt, len); |
172 | 0 | } |
173 | | |
174 | | static void ascon_aead128_decrypt_update(ascon_aead128_ctx *ctx, unsigned char *pt, |
175 | | const unsigned char *ct, size_t len) |
176 | 0 | { |
177 | 0 | ctx->flags |= ASCONFLG_DEC; |
178 | 0 | ascon_aead128_update(ctx, pt, ct, len); |
179 | 0 | } |
180 | | |
181 | | static void ascon_aead128_init(ascon_aead128_ctx *ctx, const unsigned char *k, |
182 | | const unsigned char *n) |
183 | 0 | { |
184 | 0 | uint64_t s0, s1, s2, s3, s4, k0, k1; |
185 | |
|
186 | 0 | OPENSSL_load_u64_le(&s1, k); |
187 | 0 | OPENSSL_load_u64_le(&s2, k + 8); |
188 | 0 | OPENSSL_load_u64_le(&s3, n); |
189 | 0 | OPENSSL_load_u64_le(&s4, n + 8); |
190 | 0 | ctx->key[0] = k0 = s1; |
191 | 0 | ctx->key[1] = k1 = s2; |
192 | 0 | s0 = 0x00001000808C0001ULL; |
193 | 0 | ASCONP12(s0, s1, s2, s3, s4); |
194 | 0 | s3 ^= k0; |
195 | 0 | s4 ^= k1; |
196 | 0 | ctx->state[0] = s0; |
197 | 0 | ctx->state[1] = s1; |
198 | 0 | ctx->state[2] = s2; |
199 | 0 | ctx->state[3] = s3; |
200 | 0 | ctx->state[4] = s4; |
201 | 0 | ctx->offset = 0; |
202 | 0 | ctx->flags = ASCONFLG_DOMAINSEP; |
203 | 0 | } |
204 | | |
205 | | static void ascon_aead128_aad_update(ascon_aead128_ctx *ctx, const unsigned char *in, |
206 | | size_t len) |
207 | 0 | { |
208 | 0 | uint64_t flags; |
209 | |
|
210 | 0 | flags = ctx->flags; |
211 | 0 | ctx->flags = 0; |
212 | 0 | ascon_aead128_update(ctx, NULL, in, len); |
213 | 0 | ctx->flags = (len > 0) ? flags |= ASCONFLG_AAD : flags; |
214 | 0 | } |
215 | | |
216 | | static void ascon_aead128_final(ascon_aead128_ctx *ctx, unsigned char *tag) |
217 | 0 | { |
218 | 0 | uint64_t s0, s1, s2, s3, s4, k0, k1; |
219 | 0 | unsigned char pad = 0x01; |
220 | |
|
221 | 0 | ascon_aead128_update(ctx, NULL, NULL, 0); |
222 | 0 | ctx->flags = 0; |
223 | 0 | ascon_aead128_update(ctx, NULL, &pad, 1); |
224 | |
|
225 | 0 | k0 = ctx->key[0]; |
226 | 0 | k1 = ctx->key[1]; |
227 | 0 | s0 = ctx->state[0]; |
228 | 0 | s1 = ctx->state[1]; |
229 | 0 | s2 = ctx->state[2] ^ k0; |
230 | 0 | s3 = ctx->state[3] ^ k1; |
231 | 0 | s4 = ctx->state[4]; |
232 | 0 | ASCONP12(s0, s1, s2, s3, s4); |
233 | 0 | s3 ^= k0; |
234 | 0 | s4 ^= k1; |
235 | 0 | OPENSSL_store_u64_le(tag, s3); |
236 | 0 | OPENSSL_store_u64_le(tag + 8, s4); |
237 | 0 | } |
238 | | |
239 | | /* Provider compatibility wrapper functions */ |
240 | | void ossl_ascon_aead128_init(ASCON_AEAD_CTX *ctx, const unsigned char *k, |
241 | | const unsigned char *n) |
242 | 0 | { |
243 | 0 | ascon_aead128_init(ctx, k, n); |
244 | 0 | } |
245 | | |
246 | | void ossl_ascon_aead128_assoc_data_update(ASCON_AEAD_CTX *ctx, |
247 | | const unsigned char *in, size_t inl) |
248 | 0 | { |
249 | 0 | ascon_aead128_aad_update(ctx, in, inl); |
250 | 0 | } |
251 | | |
252 | | size_t ossl_ascon_aead128_encrypt_update(ASCON_AEAD_CTX *ctx, |
253 | | unsigned char *out, |
254 | | const unsigned char *in, size_t inl) |
255 | 0 | { |
256 | 0 | ascon_aead128_encrypt_update(ctx, out, in, inl); |
257 | 0 | return inl; |
258 | 0 | } |
259 | | |
260 | | size_t ossl_ascon_aead128_decrypt_update(ASCON_AEAD_CTX *ctx, |
261 | | unsigned char *out, |
262 | | const unsigned char *in, size_t inl) |
263 | 0 | { |
264 | 0 | ascon_aead128_decrypt_update(ctx, out, in, inl); |
265 | 0 | return inl; |
266 | 0 | } |
267 | | |
268 | | size_t ossl_ascon_aead128_encrypt_final(ASCON_AEAD_CTX *ctx, |
269 | | unsigned char *out, |
270 | | unsigned char *tag, size_t tag_len) |
271 | 0 | { |
272 | 0 | unsigned char computed_tag[16]; |
273 | |
|
274 | 0 | ascon_aead128_final(ctx, computed_tag); |
275 | 0 | if (tag != NULL && tag_len >= 16) |
276 | 0 | memcpy(tag, computed_tag, 16); |
277 | 0 | if (out != NULL) { |
278 | | /* No additional output for final */ |
279 | 0 | } |
280 | 0 | return 0; |
281 | 0 | } |
282 | | |
283 | | size_t ossl_ascon_aead128_decrypt_final(ASCON_AEAD_CTX *ctx, |
284 | | unsigned char *out, |
285 | | int *is_tag_valid, |
286 | | const unsigned char *tag, |
287 | | size_t tag_len) |
288 | 0 | { |
289 | 0 | unsigned char computed_tag[16]; |
290 | |
|
291 | 0 | ascon_aead128_final(ctx, computed_tag); |
292 | 0 | if (is_tag_valid != NULL && tag != NULL && tag_len >= 16) { |
293 | 0 | *is_tag_valid = (CRYPTO_memcmp(computed_tag, tag, 16) == 0); |
294 | 0 | } else if (is_tag_valid != NULL) { |
295 | 0 | *is_tag_valid = 0; |
296 | 0 | } |
297 | 0 | return 0; |
298 | 0 | } |
299 | | |
300 | | void ossl_ascon_aead_cleanup(ASCON_AEAD_CTX *ctx) |
301 | 0 | { |
302 | 0 | if (ctx != NULL) |
303 | 0 | OPENSSL_cleanse(ctx, sizeof(ASCON_AEAD_CTX)); |
304 | 0 | } |