/src/openssl/crypto/sha/sha3.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2017-2024 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 <string.h> |
11 | | #include "internal/sha3.h" |
12 | | #include "internal/common.h" |
13 | | |
14 | | #if defined(__aarch64__) && defined(KECCAK1600_ASM) |
15 | | #include "crypto/arm_arch.h" |
16 | | #endif |
17 | | |
18 | | #if defined(__s390x__) && defined(OPENSSL_CPUID_OBJ) |
19 | | #include "crypto/s390x_arch.h" |
20 | | #if defined(KECCAK1600_ASM) |
21 | | #define S390_SHA3 1 |
22 | | #define S390_SHA3_CAPABLE(name) \ |
23 | | ((OPENSSL_s390xcap_P.kimd[0] & S390X_CAPBIT(name)) && (OPENSSL_s390xcap_P.klmd[0] & S390X_CAPBIT(name))) |
24 | | #endif |
25 | | #endif |
26 | | |
27 | | void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next); |
28 | | |
29 | | void ossl_sha3_reset(KECCAK1600_CTX *ctx) |
30 | 173 | { |
31 | | #if defined(__s390x__) && defined(OPENSSL_CPUID_OBJ) |
32 | | if (!(OPENSSL_s390xcap_P.stfle[1] & S390X_CAPBIT(S390X_MSA12))) |
33 | | #endif |
34 | 173 | memset(ctx->A, 0, sizeof(ctx->A)); |
35 | 173 | ctx->bufsz = 0; |
36 | 173 | ctx->xof_state = XOF_STATE_INIT; |
37 | 173 | } |
38 | | |
39 | | int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen) |
40 | 59 | { |
41 | 59 | size_t bsz = SHA3_BLOCKSIZE(bitlen); |
42 | | |
43 | 59 | if (bsz <= sizeof(ctx->buf)) { |
44 | 59 | ossl_sha3_reset(ctx); |
45 | 59 | ctx->block_size = bsz; |
46 | 59 | ctx->md_size = bitlen / 8; |
47 | 59 | ctx->pad = pad; |
48 | 59 | return 1; |
49 | 59 | } |
50 | | |
51 | 0 | return 0; |
52 | 59 | } |
53 | | |
54 | | int ossl_keccak_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen, size_t mdlen) |
55 | 4 | { |
56 | 4 | int ret = ossl_sha3_init(ctx, pad, bitlen); |
57 | | |
58 | 4 | if (ret) |
59 | 4 | ctx->md_size = mdlen / 8; |
60 | 4 | return ret; |
61 | 4 | } |
62 | | |
63 | | int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len) |
64 | 0 | { |
65 | 0 | const unsigned char *inp = _inp; |
66 | 0 | size_t bsz = ctx->block_size; |
67 | 0 | size_t num, rem; |
68 | |
|
69 | 0 | if (len == 0) |
70 | 0 | return 1; |
71 | | |
72 | 0 | if (ctx->xof_state == XOF_STATE_SQUEEZE |
73 | 0 | || ctx->xof_state == XOF_STATE_FINAL) |
74 | 0 | return 0; |
75 | | |
76 | 0 | if ((num = ctx->bufsz) != 0) { /* process intermediate buffer? */ |
77 | 0 | rem = bsz - num; |
78 | |
|
79 | 0 | if (len < rem) { |
80 | 0 | memcpy(ctx->buf + num, inp, len); |
81 | 0 | ctx->bufsz += len; |
82 | 0 | return 1; |
83 | 0 | } |
84 | | /* |
85 | | * We have enough data to fill or overflow the intermediate |
86 | | * buffer. So we append |rem| bytes and process the block, |
87 | | * leaving the rest for later processing... |
88 | | */ |
89 | 0 | memcpy(ctx->buf + num, inp, rem); |
90 | 0 | inp += rem, len -= rem; |
91 | 0 | (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz); |
92 | 0 | ctx->bufsz = 0; |
93 | | /* ctx->buf is processed, ctx->num is guaranteed to be zero */ |
94 | 0 | } |
95 | | |
96 | 0 | if (len >= bsz) |
97 | 0 | rem = SHA3_absorb(ctx->A, inp, len, bsz); |
98 | 0 | else |
99 | 0 | rem = len; |
100 | |
|
101 | 0 | if (rem) { |
102 | 0 | memcpy(ctx->buf, inp + len - rem, rem); |
103 | 0 | ctx->bufsz = rem; |
104 | 0 | } |
105 | |
|
106 | 0 | return 1; |
107 | 0 | } |
108 | | |
109 | | /* |
110 | | * ossl_sha3_final()is a single shot method |
111 | | * (Use ossl_sha3_squeeze for multiple calls). |
112 | | * outlen is the variable size output. |
113 | | */ |
114 | | int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen) |
115 | 59 | { |
116 | 59 | size_t bsz = ctx->block_size; |
117 | 59 | size_t num = ctx->bufsz; |
118 | | |
119 | 59 | if (outlen == 0) |
120 | 0 | return 1; |
121 | 59 | if (ctx->xof_state == XOF_STATE_SQUEEZE |
122 | 59 | || ctx->xof_state == XOF_STATE_FINAL) |
123 | 0 | return 0; |
124 | | |
125 | | /* |
126 | | * Pad the data with 10*1. Note that |num| can be |bsz - 1| |
127 | | * in which case both byte operations below are performed on |
128 | | * same byte... |
129 | | */ |
130 | 59 | memset(ctx->buf + num, 0, bsz - num); |
131 | 59 | ctx->buf[num] = ctx->pad; |
132 | 59 | ctx->buf[bsz - 1] |= 0x80; |
133 | | |
134 | 59 | (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz); |
135 | | |
136 | 59 | ctx->xof_state = XOF_STATE_FINAL; |
137 | 59 | SHA3_squeeze(ctx->A, out, outlen, bsz, 0); |
138 | 59 | return 1; |
139 | 59 | } |
140 | | |
141 | | /* This is a buffered absorb function. */ |
142 | | int ossl_sha3_absorb(KECCAK1600_CTX *ctx, const unsigned char *inp, size_t len) |
143 | 118 | { |
144 | 118 | const size_t bsz = ctx->block_size; |
145 | 118 | size_t num, rem; |
146 | | |
147 | 118 | if (ossl_unlikely(len == 0)) |
148 | 0 | return 1; |
149 | | |
150 | | /* Is there anything in the buffer already ? */ |
151 | 118 | if (ossl_likely((num = ctx->bufsz) != 0)) { |
152 | | /* Calculate how much space is left in the buffer */ |
153 | 59 | rem = bsz - num; |
154 | | /* If the new input does not fill the buffer then just add it */ |
155 | 59 | if (len < rem) { |
156 | 0 | memcpy(ctx->buf + num, inp, len); |
157 | 0 | ctx->bufsz += len; |
158 | 0 | return 1; |
159 | 0 | } |
160 | | /* otherwise fill up the buffer and absorb the buffer */ |
161 | 59 | memcpy(ctx->buf + num, inp, rem); |
162 | | /* Update the input pointer */ |
163 | 59 | inp += rem; |
164 | 59 | len -= rem; |
165 | 59 | ctx->meth.absorb(ctx, ctx->buf, bsz); |
166 | 59 | ctx->bufsz = 0; |
167 | 59 | } |
168 | | /* Absorb the input - rem = leftover part of the input < blocksize) */ |
169 | 118 | rem = ctx->meth.absorb(ctx, inp, len); |
170 | | /* Copy the leftover bit of the input into the buffer */ |
171 | 118 | if (ossl_likely(rem > 0)) { |
172 | 112 | memcpy(ctx->buf, inp + len - rem, rem); |
173 | 112 | ctx->bufsz = rem; |
174 | 112 | } |
175 | 118 | return 1; |
176 | 118 | } |
177 | | |
178 | | /* |
179 | | * This method can be called multiple times. |
180 | | * Rather than heavily modifying assembler for SHA3_squeeze(), |
181 | | * we instead just use the limitations of the existing function. |
182 | | * i.e. Only request multiples of the ctx->block_size when calling |
183 | | * SHA3_squeeze(). For output length requests smaller than the |
184 | | * ctx->block_size just request a single ctx->block_size bytes and |
185 | | * buffer the results. The next request will use the buffer first |
186 | | * to grab output bytes. |
187 | | */ |
188 | | int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen) |
189 | 0 | { |
190 | 0 | size_t bsz = ctx->block_size; |
191 | 0 | size_t num = ctx->bufsz; |
192 | 0 | size_t len; |
193 | 0 | int next = 1; |
194 | |
|
195 | 0 | if (outlen == 0) |
196 | 0 | return 1; |
197 | | |
198 | 0 | if (ctx->xof_state == XOF_STATE_FINAL) |
199 | 0 | return 0; |
200 | | |
201 | | /* |
202 | | * On the first squeeze call, finish the absorb process, |
203 | | * by adding the trailing padding and then doing |
204 | | * a final absorb. |
205 | | */ |
206 | 0 | if (ctx->xof_state != XOF_STATE_SQUEEZE) { |
207 | | /* |
208 | | * Pad the data with 10*1. Note that |num| can be |bsz - 1| |
209 | | * in which case both byte operations below are performed on |
210 | | * same byte... |
211 | | */ |
212 | 0 | memset(ctx->buf + num, 0, bsz - num); |
213 | 0 | ctx->buf[num] = ctx->pad; |
214 | 0 | ctx->buf[bsz - 1] |= 0x80; |
215 | 0 | (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz); |
216 | 0 | ctx->xof_state = XOF_STATE_SQUEEZE; |
217 | 0 | num = ctx->bufsz = 0; |
218 | 0 | next = 0; |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | * Step 1. Consume any bytes left over from a previous squeeze |
223 | | * (See Step 4 below). |
224 | | */ |
225 | 0 | if (num != 0) { |
226 | 0 | if (outlen > ctx->bufsz) |
227 | 0 | len = ctx->bufsz; |
228 | 0 | else |
229 | 0 | len = outlen; |
230 | 0 | memcpy(out, ctx->buf + bsz - ctx->bufsz, len); |
231 | 0 | out += len; |
232 | 0 | outlen -= len; |
233 | 0 | ctx->bufsz -= len; |
234 | 0 | } |
235 | 0 | if (outlen == 0) |
236 | 0 | return 1; |
237 | | |
238 | | /* Step 2. Copy full sized squeezed blocks to the output buffer directly */ |
239 | 0 | if (outlen >= bsz) { |
240 | 0 | len = bsz * (outlen / bsz); |
241 | 0 | SHA3_squeeze(ctx->A, out, len, bsz, next); |
242 | 0 | next = 1; |
243 | 0 | out += len; |
244 | 0 | outlen -= len; |
245 | 0 | } |
246 | 0 | if (outlen > 0) { |
247 | | /* Step 3. Squeeze one more block into a buffer */ |
248 | 0 | SHA3_squeeze(ctx->A, ctx->buf, bsz, bsz, next); |
249 | 0 | memcpy(out, ctx->buf, outlen); |
250 | | /* Step 4. Remember the leftover part of the squeezed block */ |
251 | 0 | ctx->bufsz = bsz - outlen; |
252 | 0 | } |
253 | |
|
254 | 0 | return 1; |
255 | 0 | } |
256 | | |
257 | | /*- |
258 | | * Generic software version of the absorb() and final(). |
259 | | */ |
260 | | static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len) |
261 | 0 | { |
262 | 0 | KECCAK1600_CTX *ctx = vctx; |
263 | |
|
264 | 0 | if (!(ctx->xof_state == XOF_STATE_INIT || ctx->xof_state == XOF_STATE_ABSORB)) |
265 | 0 | return 0; |
266 | 0 | ctx->xof_state = XOF_STATE_ABSORB; |
267 | 0 | return SHA3_absorb(ctx->A, inp, len, ctx->block_size); |
268 | 0 | } |
269 | | |
270 | | static int generic_sha3_final(void *vctx, unsigned char *out, size_t outlen) |
271 | 0 | { |
272 | 0 | return ossl_sha3_final((KECCAK1600_CTX *)vctx, out, outlen); |
273 | 0 | } |
274 | | |
275 | | static int generic_sha3_squeeze(void *vctx, unsigned char *out, size_t outlen) |
276 | 0 | { |
277 | 0 | return ossl_sha3_squeeze((KECCAK1600_CTX *)vctx, out, outlen); |
278 | 0 | } |
279 | | |
280 | | static PROV_SHA3_METHOD shake_generic_meth = { |
281 | | generic_sha3_absorb, |
282 | | generic_sha3_final, |
283 | | generic_sha3_squeeze |
284 | | }; |
285 | | |
286 | | #if defined(S390_SHA3) |
287 | | |
288 | | /*- |
289 | | * The platform specific parts of the absorb() and final() for S390X. |
290 | | */ |
291 | | static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len) |
292 | | { |
293 | | KECCAK1600_CTX *ctx = vctx; |
294 | | size_t rem = len % ctx->block_size; |
295 | | unsigned int fc; |
296 | | |
297 | | if (!(ctx->xof_state == XOF_STATE_INIT || ctx->xof_state == XOF_STATE_ABSORB)) |
298 | | return 0; |
299 | | if (len - rem > 0) { |
300 | | fc = ctx->pad; |
301 | | fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0; |
302 | | ctx->xof_state = XOF_STATE_ABSORB; |
303 | | s390x_kimd(inp, len - rem, fc, ctx->A); |
304 | | } |
305 | | return rem; |
306 | | } |
307 | | |
308 | | static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen) |
309 | | { |
310 | | KECCAK1600_CTX *ctx = vctx; |
311 | | unsigned int fc; |
312 | | |
313 | | if (!(ctx->xof_state == XOF_STATE_INIT || ctx->xof_state == XOF_STATE_ABSORB)) |
314 | | return 0; |
315 | | fc = ctx->pad | S390X_KLMD_DUFOP; |
316 | | fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0; |
317 | | ctx->xof_state = XOF_STATE_FINAL; |
318 | | s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A); |
319 | | return 1; |
320 | | } |
321 | | |
322 | | static int s390x_shake_squeeze(void *vctx, unsigned char *out, size_t outlen) |
323 | | { |
324 | | KECCAK1600_CTX *ctx = vctx; |
325 | | unsigned int fc; |
326 | | size_t len; |
327 | | |
328 | | if (ctx->xof_state == XOF_STATE_FINAL) |
329 | | return 0; |
330 | | /* |
331 | | * On the first squeeze call, finish the absorb process (incl. padding). |
332 | | */ |
333 | | if (ctx->xof_state != XOF_STATE_SQUEEZE) { |
334 | | fc = ctx->pad; |
335 | | fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0; |
336 | | ctx->xof_state = XOF_STATE_SQUEEZE; |
337 | | s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A); |
338 | | ctx->bufsz = outlen % ctx->block_size; |
339 | | /* reuse ctx->bufsz to count bytes squeezed from current sponge */ |
340 | | return 1; |
341 | | } |
342 | | ctx->xof_state = XOF_STATE_SQUEEZE; |
343 | | if (ctx->bufsz != 0) { |
344 | | len = ctx->block_size - ctx->bufsz; |
345 | | if (outlen < len) |
346 | | len = outlen; |
347 | | memcpy(out, (char *)ctx->A + ctx->bufsz, len); |
348 | | out += len; |
349 | | outlen -= len; |
350 | | ctx->bufsz += len; |
351 | | if (ctx->bufsz == ctx->block_size) |
352 | | ctx->bufsz = 0; |
353 | | } |
354 | | if (outlen == 0) |
355 | | return 1; |
356 | | s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A); |
357 | | ctx->bufsz = outlen % ctx->block_size; |
358 | | |
359 | | return 1; |
360 | | } |
361 | | |
362 | | static PROV_SHA3_METHOD shake_s390x_meth = { |
363 | | s390x_sha3_absorb, |
364 | | s390x_shake_final, |
365 | | s390x_shake_squeeze, |
366 | | }; |
367 | | #elif defined(__aarch64__) && defined(KECCAK1600_ASM) |
368 | | |
369 | | size_t SHA3_absorb_cext(uint64_t A[5][5], const unsigned char *inp, size_t len, |
370 | | size_t r); |
371 | | /*- |
372 | | * Hardware-assisted ARMv8.2 SHA3 extension version of the absorb() |
373 | | */ |
374 | | static size_t armsha3_sha3_absorb(void *vctx, const void *inp, size_t len) |
375 | | { |
376 | | KECCAK1600_CTX *ctx = vctx; |
377 | | |
378 | | return SHA3_absorb_cext(ctx->A, inp, len, ctx->block_size); |
379 | | } |
380 | | |
381 | | static PROV_SHA3_METHOD shake_ARMSHA3_meth = { |
382 | | armsha3_sha3_absorb, |
383 | | generic_sha3_final, |
384 | | generic_sha3_squeeze |
385 | | }; |
386 | | #endif |
387 | | |
388 | | KECCAK1600_CTX *ossl_shake256_new(void) |
389 | 0 | { |
390 | 0 | KECCAK1600_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); |
391 | |
|
392 | 0 | if (ctx == NULL) |
393 | 0 | return NULL; |
394 | 0 | ossl_keccak_init(ctx, '\x1f', 256, 0); |
395 | 0 | ctx->md_size = SIZE_MAX; |
396 | 0 | ctx->meth = shake_generic_meth; |
397 | | #if defined(S390_SHA3) |
398 | | if (S390_SHA3_CAPABLE(S390X_SHAKE_256)) { |
399 | | ctx->pad = S390X_SHAKE_256; |
400 | | ctx->meth = shake_s390x_meth; |
401 | | } |
402 | | #elif defined(__aarch64__) && defined(KECCAK1600_ASM) |
403 | | if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) |
404 | | ctx->meth = shake_ARMSHA3_meth; |
405 | | #endif |
406 | 0 | return ctx; |
407 | 0 | } |