xref: /openssl/crypto/sha/sha3.c (revision 7ed6de99)
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 #if defined(__s390x__) && defined(OPENSSL_CPUID_OBJ)
12 # include "crypto/s390x_arch.h"
13 #endif
14 #include "internal/sha3.h"
15 
16 void SHA3_squeeze(uint64_t A[5][5], unsigned char *out, size_t len, size_t r, int next);
17 
ossl_sha3_reset(KECCAK1600_CTX * ctx)18 void ossl_sha3_reset(KECCAK1600_CTX *ctx)
19 {
20 #if defined(__s390x__) && defined(OPENSSL_CPUID_OBJ)
21     if (!(OPENSSL_s390xcap_P.stfle[1] & S390X_CAPBIT(S390X_MSA12)))
22 #endif
23         memset(ctx->A, 0, sizeof(ctx->A));
24     ctx->bufsz = 0;
25     ctx->xof_state = XOF_STATE_INIT;
26 }
27 
ossl_sha3_init(KECCAK1600_CTX * ctx,unsigned char pad,size_t bitlen)28 int ossl_sha3_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen)
29 {
30     size_t bsz = SHA3_BLOCKSIZE(bitlen);
31 
32     if (bsz <= sizeof(ctx->buf)) {
33         ossl_sha3_reset(ctx);
34         ctx->block_size = bsz;
35         ctx->md_size = bitlen / 8;
36         ctx->pad = pad;
37         return 1;
38     }
39 
40     return 0;
41 }
42 
ossl_keccak_init(KECCAK1600_CTX * ctx,unsigned char pad,size_t bitlen,size_t mdlen)43 int ossl_keccak_init(KECCAK1600_CTX *ctx, unsigned char pad, size_t bitlen, size_t mdlen)
44 {
45     int ret = ossl_sha3_init(ctx, pad, bitlen);
46 
47     if (ret)
48         ctx->md_size = mdlen / 8;
49     return ret;
50 }
51 
ossl_sha3_update(KECCAK1600_CTX * ctx,const void * _inp,size_t len)52 int ossl_sha3_update(KECCAK1600_CTX *ctx, const void *_inp, size_t len)
53 {
54     const unsigned char *inp = _inp;
55     size_t bsz = ctx->block_size;
56     size_t num, rem;
57 
58     if (len == 0)
59         return 1;
60 
61     if (ctx->xof_state == XOF_STATE_SQUEEZE
62         || ctx->xof_state == XOF_STATE_FINAL)
63         return 0;
64 
65     if ((num = ctx->bufsz) != 0) {      /* process intermediate buffer? */
66         rem = bsz - num;
67 
68         if (len < rem) {
69             memcpy(ctx->buf + num, inp, len);
70             ctx->bufsz += len;
71             return 1;
72         }
73         /*
74          * We have enough data to fill or overflow the intermediate
75          * buffer. So we append |rem| bytes and process the block,
76          * leaving the rest for later processing...
77          */
78         memcpy(ctx->buf + num, inp, rem);
79         inp += rem, len -= rem;
80         (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
81         ctx->bufsz = 0;
82         /* ctx->buf is processed, ctx->num is guaranteed to be zero */
83     }
84 
85     if (len >= bsz)
86         rem = SHA3_absorb(ctx->A, inp, len, bsz);
87     else
88         rem = len;
89 
90     if (rem) {
91         memcpy(ctx->buf, inp + len - rem, rem);
92         ctx->bufsz = rem;
93     }
94 
95     return 1;
96 }
97 
98 /*
99  * ossl_sha3_final()is a single shot method
100  * (Use ossl_sha3_squeeze for multiple calls).
101  * outlen is the variable size output.
102  */
ossl_sha3_final(KECCAK1600_CTX * ctx,unsigned char * out,size_t outlen)103 int ossl_sha3_final(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
104 {
105     size_t bsz = ctx->block_size;
106     size_t num = ctx->bufsz;
107 
108     if (outlen == 0)
109         return 1;
110     if (ctx->xof_state == XOF_STATE_SQUEEZE
111         || ctx->xof_state == XOF_STATE_FINAL)
112         return 0;
113 
114     /*
115      * Pad the data with 10*1. Note that |num| can be |bsz - 1|
116      * in which case both byte operations below are performed on
117      * same byte...
118      */
119     memset(ctx->buf + num, 0, bsz - num);
120     ctx->buf[num] = ctx->pad;
121     ctx->buf[bsz - 1] |= 0x80;
122 
123     (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
124 
125     ctx->xof_state = XOF_STATE_FINAL;
126     SHA3_squeeze(ctx->A, out, outlen, bsz, 0);
127     return 1;
128 }
129 
130 /*
131  * This method can be called multiple times.
132  * Rather than heavily modifying assembler for SHA3_squeeze(),
133  * we instead just use the limitations of the existing function.
134  * i.e. Only request multiples of the ctx->block_size when calling
135  * SHA3_squeeze(). For output length requests smaller than the
136  * ctx->block_size just request a single ctx->block_size bytes and
137  * buffer the results. The next request will use the buffer first
138  * to grab output bytes.
139  */
ossl_sha3_squeeze(KECCAK1600_CTX * ctx,unsigned char * out,size_t outlen)140 int ossl_sha3_squeeze(KECCAK1600_CTX *ctx, unsigned char *out, size_t outlen)
141 {
142     size_t bsz = ctx->block_size;
143     size_t num = ctx->bufsz;
144     size_t len;
145     int next = 1;
146 
147     if (outlen == 0)
148         return 1;
149 
150     if (ctx->xof_state == XOF_STATE_FINAL)
151         return 0;
152 
153     /*
154      * On the first squeeze call, finish the absorb process,
155      * by adding the trailing padding and then doing
156      * a final absorb.
157      */
158     if (ctx->xof_state != XOF_STATE_SQUEEZE) {
159         /*
160          * Pad the data with 10*1. Note that |num| can be |bsz - 1|
161          * in which case both byte operations below are performed on
162          * same byte...
163          */
164         memset(ctx->buf + num, 0, bsz - num);
165         ctx->buf[num] = ctx->pad;
166         ctx->buf[bsz - 1] |= 0x80;
167         (void)SHA3_absorb(ctx->A, ctx->buf, bsz, bsz);
168         ctx->xof_state = XOF_STATE_SQUEEZE;
169         num = ctx->bufsz = 0;
170         next = 0;
171     }
172 
173     /*
174      * Step 1. Consume any bytes left over from a previous squeeze
175      * (See Step 4 below).
176      */
177     if (num != 0) {
178         if (outlen > ctx->bufsz)
179             len = ctx->bufsz;
180         else
181             len = outlen;
182         memcpy(out, ctx->buf + bsz - ctx->bufsz, len);
183         out += len;
184         outlen -= len;
185         ctx->bufsz -= len;
186     }
187     if (outlen == 0)
188         return 1;
189 
190     /* Step 2. Copy full sized squeezed blocks to the output buffer directly */
191     if (outlen >= bsz) {
192         len = bsz * (outlen / bsz);
193         SHA3_squeeze(ctx->A, out, len, bsz, next);
194         next = 1;
195         out += len;
196         outlen -= len;
197     }
198     if (outlen > 0) {
199         /* Step 3. Squeeze one more block into a buffer */
200         SHA3_squeeze(ctx->A, ctx->buf, bsz, bsz, next);
201         memcpy(out, ctx->buf, outlen);
202         /* Step 4. Remember the leftover part of the squeezed block */
203         ctx->bufsz = bsz - outlen;
204     }
205 
206     return 1;
207 }
208