v / vlib / crypto / sha3 / sha3.v
404 lines · 342 sloc · 10.79 KB · 4b7955afe528e233a43f8586b923529e6d28c391
Raw
1// Copyright (c) 2023 Kim Shrier. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4// Package sha3 implements the 512, 384, 256, and 224
5// bit hash algorithms and the 128 and 256 bit
6// extended output functions as defined in
7// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf.
8// Last updated: August 2015
9module sha3
10
11// size_224 is the size, in bytes, of a sha3 sum224 checksum.
12pub const size_224 = 28
13// size_256 is the size, in bytes, of a sha3 sum256 checksum.
14pub const size_256 = 32
15// size_384 is the size, in bytes, of a sha3 sum384 checksum.
16pub const size_384 = 48
17// size_512 is the size, in bytes, of a sha3 sum512 checksum.
18pub const size_512 = 64
19
20// rate_224 is the rate, in bytes, absorbed into the sponge on every permutation
21pub const rate_224 = 144
22// rate_256 is the rate, in bytes, absorbed into the sponge on every permutation
23pub const rate_256 = 136
24// rate_384 is the rate, in bytes, absorbed into the sponge on every permutation
25pub const rate_384 = 104
26// rate_512 is the rate, in bytes, absorbed into the sponge on every permutation
27pub const rate_512 = 72
28
29// xof_rate_128 is the capacity, in bytes, of a 128 bit extended output function sponge
30pub const xof_rate_128 = 168
31// xof_rate_256 is the capacity, in bytes, of a 256 bit extended output function sponge
32pub const xof_rate_256 = 136
33
34// new512 initializes the digest structure for a sha3 512 bit hash
35pub fn new512() !&Digest {
36 return new_digest(rate_512, size_512)!
37}
38
39// new384 initializes the digest structure for a sha3 384 bit hash
40pub fn new384() !&Digest {
41 return new_digest(rate_384, size_384)!
42}
43
44// new256 initializes the digest structure for a sha3 256 bit hash
45pub fn new256() !&Digest {
46 return new_digest(rate_256, size_256)!
47}
48
49// new224 initializes the digest structure for a sha3 224 bit hash
50pub fn new224() !&Digest {
51 return new_digest(rate_224, size_224)!
52}
53
54// new256keccak initializes the digest structure for a keccak 256 bit hash
55pub fn new256keccak() !&Digest {
56 return new_digest(rate_256, size_256, padding: .keccak)!
57}
58
59// new512keccak initializes the digest structure for a keccak 512 bit hash
60pub fn new512keccak() !&Digest {
61 return new_digest(rate_512, size_512, padding: .keccak)!
62}
63
64// new256_xof initializes the digest structure for a sha3 256 bit extended output function
65pub fn new256xof(output_len int) !&Digest {
66 return new_xof_digest(xof_rate_256, output_len)!
67}
68
69// new128_xof initializes the digest structure for a sha3 128 bit extended output function
70pub fn new128xof(output_len int) !&Digest {
71 return new_xof_digest(xof_rate_128, output_len)!
72}
73
74struct HashSizeError {
75 Error
76 size int
77}
78
79fn (err HashSizeError) msg() string {
80 return 'Hash size ' + err.size.str() + ' must be ' + size_224.str() + ', ' + size_256.str() +
81 ', ' + size_384.str() + ', or ' + size_512.str()
82}
83
84struct AbsorptionRateError {
85 Error
86 size int
87 rate int
88}
89
90fn (err AbsorptionRateError) msg() string {
91 return 'Absorption rate ' + err.rate.str() + ' is not compatible with a hash size of ' +
92 err.size.str()
93}
94
95struct XOFRateError {
96 Error
97 rate int
98}
99
100fn (err XOFRateError) msg() string {
101 return 'Extended output rate ' + err.rate.str() + ' must be ' + xof_rate_128.str() + ' or ' +
102 xof_rate_256.str()
103}
104
105struct XOFSizeError {
106 Error
107 size int
108}
109
110fn (err XOFSizeError) msg() string {
111 return 'Extended output size ' + err.size.str() + ' must be > 0'
112}
113
114struct Digest {
115 rate int // the number of bytes absorbed per permutation
116 suffix u8 // the domain suffix, 0x06 for hash, 0x01 for keccak, 0x1f for extended output
117 output_len int // the number of bytes to output
118mut:
119 input_buffer []u8 // temporary holding buffer for input bytes
120 s State // the state of a kaccak-p[1600, 24] sponge
121}
122
123// the low order pad bits for a hash function
124pub enum Padding as u8 {
125 keccak = 0x01
126 sha3 = 0x06
127 xof = 0x1f
128}
129
130@[params]
131pub struct PaddingConfig {
132pub:
133 padding Padding = .sha3
134}
135
136// new_digest creates an initialized digest structure based on
137// the hash size.
138//
139// absorption_rate is the number of bytes to be absorbed into the
140// sponge per permutation.
141//
142// hash_size - the number if bytes in the generated hash.
143// Legal values are 224, 256, 384, and 512.
144//
145// config - the padding setting for hash generation. .sha3 should be used for FIPS PUB 202 compliant SHA3-224, SHA3-256, SHA3-384 and SHA3-512. Use .keccak if you want a legacy Keccak-224, Keccak-256, Keccak-384 or Keccak-512 algorithm. .xof is for extended output functions.
146pub fn new_digest(absorption_rate int, hash_size int, config PaddingConfig) !&Digest {
147 match config.padding {
148 .sha3, .keccak { validate_sha3(absorption_rate, hash_size)! }
149 .xof { validate_xof(absorption_rate, hash_size)! }
150 }
151
152 return new_digest_unchecked(absorption_rate, hash_size, config.padding)
153}
154
155fn new_digest_unchecked(absorption_rate int, hash_size int, padding Padding) &Digest {
156 d := Digest{
157 rate: absorption_rate
158 suffix: u8(padding)
159 output_len: hash_size
160 s: State{}
161 }
162
163 return &d
164}
165
166// new_xof_digest creates an initialized digest structure based on
167// the absorption rate and how many bytes of output you need
168//
169// absorption_rate is the number of bytes to be absorbed into the
170// sponge per permutation. Legal values are xof_rate_128 and
171// xof_rate_256.
172//
173// hash_size - the number if bytes in the generated hash.
174// Legal values are positive integers.
175pub fn new_xof_digest(absorption_rate int, hash_size int) !&Digest {
176 return new_digest(absorption_rate, hash_size, padding: .xof)
177}
178
179fn validate_sha3(absorption_rate int, hash_size int) ! {
180 match hash_size {
181 size_224 {
182 if absorption_rate != rate_224 {
183 return AbsorptionRateError{
184 rate: absorption_rate
185 size: hash_size
186 }
187 }
188 }
189 size_256 {
190 if absorption_rate != rate_256 {
191 return AbsorptionRateError{
192 rate: absorption_rate
193 size: hash_size
194 }
195 }
196 }
197 size_384 {
198 if absorption_rate != rate_384 {
199 return AbsorptionRateError{
200 rate: absorption_rate
201 size: hash_size
202 }
203 }
204 }
205 size_512 {
206 if absorption_rate != rate_512 {
207 return AbsorptionRateError{
208 rate: absorption_rate
209 size: hash_size
210 }
211 }
212 }
213 else {
214 return HashSizeError{
215 size: hash_size
216 }
217 }
218 }
219}
220
221fn validate_xof(absorption_rate int, hash_size int) ! {
222 match absorption_rate {
223 xof_rate_128, xof_rate_256 {
224 if hash_size < 1 {
225 return XOFSizeError{
226 size: hash_size
227 }
228 }
229 }
230 else {
231 return XOFRateError{
232 rate: absorption_rate
233 }
234 }
235 }
236}
237
238// write adds bytes to the sponge.
239//
240// This is the absorption phase of the computation.
241pub fn (mut d Digest) write(data []u8) ! {
242 d.write_bytes(data)
243}
244
245fn (mut d Digest) write_bytes(data []u8) {
246 // if no data is being added to the hash,
247 // just return
248 if data.len == 0 {
249 return
250 }
251
252 // absorb the input into the sponge
253 mut bytes_remaining := unsafe { data[..] }
254
255 if d.input_buffer.len != 0 {
256 // see if we can accumulate rate bytes to be absorbed
257 empty_space := d.rate - d.input_buffer.len
258
259 if bytes_remaining.len < empty_space {
260 d.input_buffer << bytes_remaining
261
262 // we have not accumulated rate bytes yet.
263 // just return.
264 return
265 } else {
266 // we have enough bytes to add rate bytes to the
267 // sponge.
268 d.input_buffer << bytes_remaining[..empty_space]
269 bytes_remaining = unsafe { bytes_remaining[empty_space..] }
270
271 // absorb them
272 d.s.xor_bytes(d.input_buffer[..d.rate], d.rate)
273 d.s.kaccak_p_1600_24()
274
275 d.input_buffer = ''.bytes()
276 }
277 }
278
279 // absorb the remaining bytes
280 for bytes_remaining.len >= d.rate {
281 d.s.xor_bytes(bytes_remaining[..d.rate], d.rate)
282 d.s.kaccak_p_1600_24()
283 bytes_remaining = unsafe { bytes_remaining[d.rate..] }
284 }
285
286 if bytes_remaining.len > 0 {
287 d.input_buffer = bytes_remaining.clone()
288 }
289}
290
291// checksum finalizes the hash and returns the generated bytes.
292pub fn (mut d Digest) checksum() []u8 {
293 return d.checksum_internal()
294}
295
296fn (mut d Digest) checksum_internal() []u8 {
297 // pad the last input bytes to have rate bytes
298 if d.input_buffer.len == d.rate - 1 {
299 // a single byte pad needs to be handled specially
300 d.input_buffer << u8(0x80 | d.suffix)
301 } else {
302 zero_pads := (d.rate - d.input_buffer.len) - 2
303
304 // add the first byte of padding
305 d.input_buffer << d.suffix
306
307 // add intermediate zero pad bytes
308 for _ in 0 .. zero_pads {
309 d.input_buffer << u8(0x00)
310 }
311
312 // add the last pad byte
313 d.input_buffer << u8(0x80)
314 }
315
316 d.s.xor_bytes(d.input_buffer[..d.rate], d.rate)
317 d.s.kaccak_p_1600_24()
318
319 // absorption is done. on to squeezing.
320
321 // We know that we can extract rate bytes from the current
322 // state. If the output_len is <= rate, we don't need to
323 // iterate and can just return the bytes from the current
324 // state.
325
326 if d.output_len <= d.rate {
327 return d.s.to_bytes()[..d.output_len]
328 }
329
330 // we need to squeeze the sponge a little harder to get
331 // longer strings of bytes.
332
333 mut output_bytes := []u8{cap: d.output_len}
334 mut remaining_ouput_len := d.output_len
335
336 for remaining_ouput_len > 0 {
337 mut byte_len_this_round := int_min(remaining_ouput_len, d.rate)
338 output_bytes << d.s.to_bytes()[..byte_len_this_round]
339
340 remaining_ouput_len -= byte_len_this_round
341
342 if remaining_ouput_len > 0 {
343 d.s.kaccak_p_1600_24()
344 }
345 }
346
347 return output_bytes
348}
349
350// sum512 returns the sha3 512 bit checksum of the data.
351pub fn sum512(data []u8) []u8 {
352 mut d := new_digest_unchecked(rate_512, size_512, .sha3)
353 d.write_bytes(data)
354 return d.checksum_internal()
355}
356
357// sum384 returns the sha3 384 bit checksum of the data.
358pub fn sum384(data []u8) []u8 {
359 mut d := new_digest_unchecked(rate_384, size_384, .sha3)
360 d.write_bytes(data)
361 return d.checksum_internal()
362}
363
364// sum256 returns the sha3 256 bit checksum of the data.
365pub fn sum256(data []u8) []u8 {
366 mut d := new_digest_unchecked(rate_256, size_256, .sha3)
367 d.write_bytes(data)
368 return d.checksum_internal()
369}
370
371// sum224 returns the sha3 224 bit checksum of the data.
372pub fn sum224(data []u8) []u8 {
373 mut d := new_digest_unchecked(rate_224, size_224, .sha3)
374 d.write_bytes(data)
375 return d.checksum_internal()
376}
377
378// keccak256 returns the keccak 256 bit checksum of the data.
379pub fn keccak256(data []u8) []u8 {
380 mut d := new_digest_unchecked(rate_256, size_256, .keccak)
381 d.write_bytes(data)
382 return d.checksum_internal()
383}
384
385// keccak512 returns the keccak 512 bit checksum of the data.
386pub fn keccak512(data []u8) []u8 {
387 mut d := new_digest_unchecked(rate_512, size_512, .keccak)
388 d.write_bytes(data)
389 return d.checksum_internal()
390}
391
392// shake256 returns the sha3 shake256 bit extended output
393pub fn shake256(data []u8, output_len int) []u8 {
394 mut d := new256xof(output_len) or { panic(err) }
395 d.write(data) or { panic(err) }
396 return d.checksum_internal()
397}
398
399// shake128 returns the sha3 shake128 bit extended output
400pub fn shake128(data []u8, output_len int) []u8 {
401 mut d := new128xof(output_len) or { panic(err) }
402 d.write(data) or { panic(err) }
403 return d.checksum_internal()
404}
405