v2 / vlib / crypto / sha3 / sha3.v
393 lines · 333 sloc · 10.58 KB · ed8024edbf568c7f5713d7d2057b01a2059a02c3
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} must be ${size_224}, ${size_256}, ${size_384}, or ${size_512}'
81}
82
83struct AbsorptionRateError {
84 Error
85 size int
86 rate int
87}
88
89fn (err AbsorptionRateError) msg() string {
90 return 'Absorption rate ${err.rate} is not compatible with a hash size of ${err.size}'
91}
92
93struct XOFRateError {
94 Error
95 rate int
96}
97
98fn (err XOFRateError) msg() string {
99 return 'Extended output rate ${err.rate} must be ${xof_rate_128} or ${xof_rate_256}'
100}
101
102struct XOFSizeError {
103 Error
104 size int
105}
106
107fn (err XOFSizeError) msg() string {
108 return 'Extended output size ${err.size} must be > 0'
109}
110
111struct Digest {
112 rate int // the number of bytes absorbed per permutation
113 suffix u8 // the domain suffix, 0x06 for hash, 0x01 for keccak, 0x1f for extended output
114 output_len int // the number of bytes to output
115mut:
116 input_buffer []u8 // temporary holding buffer for input bytes
117 s State // the state of a kaccak-p[1600, 24] sponge
118}
119
120// the low order pad bits for a hash function
121pub enum Padding as u8 {
122 keccak = 0x01
123 sha3 = 0x06
124 xof = 0x1f
125}
126
127@[params]
128pub struct PaddingConfig {
129pub:
130 padding Padding = .sha3
131}
132
133// new_digest creates an initialized digest structure based on
134// the hash size.
135//
136// absorption_rate is the number of bytes to be absorbed into the
137// sponge per permutation.
138//
139// hash_size - the number if bytes in the generated hash.
140// Legal values are 224, 256, 384, and 512.
141//
142// 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.
143pub fn new_digest(absorption_rate int, hash_size int, config PaddingConfig) !&Digest {
144 match config.padding {
145 .sha3, .keccak { validate_sha3(absorption_rate, hash_size)! }
146 .xof { validate_xof(absorption_rate, hash_size)! }
147 }
148
149 d := Digest{
150 rate: absorption_rate
151 suffix: u8(config.padding)
152 output_len: hash_size
153 s: State{}
154 }
155
156 return &d
157}
158
159// new_xof_digest creates an initialized digest structure based on
160// the absorption rate and how many bytes of output you need
161//
162// absorption_rate is the number of bytes to be absorbed into the
163// sponge per permutation. Legal values are xof_rate_128 and
164// xof_rate_256.
165//
166// hash_size - the number if bytes in the generated hash.
167// Legal values are positive integers.
168pub fn new_xof_digest(absorption_rate int, hash_size int) !&Digest {
169 return new_digest(absorption_rate, hash_size, padding: .xof)
170}
171
172fn validate_sha3(absorption_rate int, hash_size int) ! {
173 match hash_size {
174 size_224 {
175 if absorption_rate != rate_224 {
176 return AbsorptionRateError{
177 rate: absorption_rate
178 size: hash_size
179 }
180 }
181 }
182 size_256 {
183 if absorption_rate != rate_256 {
184 return AbsorptionRateError{
185 rate: absorption_rate
186 size: hash_size
187 }
188 }
189 }
190 size_384 {
191 if absorption_rate != rate_384 {
192 return AbsorptionRateError{
193 rate: absorption_rate
194 size: hash_size
195 }
196 }
197 }
198 size_512 {
199 if absorption_rate != rate_512 {
200 return AbsorptionRateError{
201 rate: absorption_rate
202 size: hash_size
203 }
204 }
205 }
206 else {
207 return HashSizeError{
208 size: hash_size
209 }
210 }
211 }
212}
213
214fn validate_xof(absorption_rate int, hash_size int) ! {
215 match absorption_rate {
216 xof_rate_128, xof_rate_256 {
217 if hash_size < 1 {
218 return XOFSizeError{
219 size: hash_size
220 }
221 }
222 }
223 else {
224 return XOFRateError{
225 rate: absorption_rate
226 }
227 }
228 }
229}
230
231// write adds bytes to the sponge.
232//
233// This is the absorption phase of the computation.
234pub fn (mut d Digest) write(data []u8) ! {
235 // if no data is being added to the hash,
236 // just return
237 if data.len == 0 {
238 return
239 }
240
241 // absorb the input into the sponge
242 mut bytes_remaining := unsafe { data[..] }
243
244 if d.input_buffer.len != 0 {
245 // see if we can accumulate rate bytes to be absorbed
246 empty_space := d.rate - d.input_buffer.len
247
248 if bytes_remaining.len < empty_space {
249 d.input_buffer << bytes_remaining
250
251 // we have not accumulated rate bytes yet.
252 // just return.
253 return
254 } else {
255 // we have enough bytes to add rate bytes to the
256 // sponge.
257 d.input_buffer << bytes_remaining[..empty_space]
258 bytes_remaining = unsafe { bytes_remaining[empty_space..] }
259
260 // absorb them
261 d.s.xor_bytes(d.input_buffer[..d.rate], d.rate)
262 d.s.kaccak_p_1600_24()
263
264 d.input_buffer = ''.bytes()
265 }
266 }
267
268 // absorb the remaining bytes
269 for bytes_remaining.len >= d.rate {
270 d.s.xor_bytes(bytes_remaining[..d.rate], d.rate)
271 d.s.kaccak_p_1600_24()
272 bytes_remaining = unsafe { bytes_remaining[d.rate..] }
273 }
274
275 if bytes_remaining.len > 0 {
276 d.input_buffer = bytes_remaining.clone()
277 }
278}
279
280// checksum finalizes the hash and returns the generated bytes.
281pub fn (mut d Digest) checksum() []u8 {
282 return d.checksum_internal() or { panic(err) }
283}
284
285fn (mut d Digest) checksum_internal() ![]u8 {
286 // pad the last input bytes to have rate bytes
287 if d.input_buffer.len == d.rate - 1 {
288 // a single byte pad needs to be handled specially
289 d.input_buffer << u8(0x80 | d.suffix)
290 } else {
291 zero_pads := (d.rate - d.input_buffer.len) - 2
292
293 // add the first byte of padding
294 d.input_buffer << d.suffix
295
296 // add intermediate zero pad bytes
297 for _ in 0 .. zero_pads {
298 d.input_buffer << u8(0x00)
299 }
300
301 // add the last pad byte
302 d.input_buffer << u8(0x80)
303 }
304
305 d.s.xor_bytes(d.input_buffer[..d.rate], d.rate)
306 d.s.kaccak_p_1600_24()
307
308 // absorption is done. on to squeezing.
309
310 // We know that we can extract rate bytes from the current
311 // state. If the output_len is <= rate, we don't need to
312 // iterate and can just return the bytes from the current
313 // state.
314
315 if d.output_len <= d.rate {
316 return d.s.to_bytes()[..d.output_len]
317 }
318
319 // we need to squeeze the sponge a little harder to get
320 // longer strings of bytes.
321
322 mut output_bytes := []u8{cap: d.output_len}
323 mut remaining_ouput_len := d.output_len
324
325 for remaining_ouput_len > 0 {
326 mut byte_len_this_round := int_min(remaining_ouput_len, d.rate)
327 output_bytes << d.s.to_bytes()[..byte_len_this_round]
328
329 remaining_ouput_len -= byte_len_this_round
330
331 if remaining_ouput_len > 0 {
332 d.s.kaccak_p_1600_24()
333 }
334 }
335
336 return output_bytes
337}
338
339// sum512 returns the sha3 512 bit checksum of the data.
340pub fn sum512(data []u8) []u8 {
341 mut d := new512() or { panic(err) }
342 d.write(data) or { panic(err) }
343 return d.checksum_internal() or { panic(err) }
344}
345
346// sum384 returns the sha3 384 bit checksum of the data.
347pub fn sum384(data []u8) []u8 {
348 mut d := new384() or { panic(err) }
349 d.write(data) or { panic(err) }
350 return d.checksum_internal() or { panic(err) }
351}
352
353// sum256 returns the sha3 256 bit checksum of the data.
354pub fn sum256(data []u8) []u8 {
355 mut d := new256() or { panic(err) }
356 d.write(data) or { panic(err) }
357 return d.checksum_internal() or { panic(err) }
358}
359
360// sum224 returns the sha3 224 bit checksum of the data.
361pub fn sum224(data []u8) []u8 {
362 mut d := new224() or { panic(err) }
363 d.write(data) or { panic(err) }
364 return d.checksum_internal() or { panic(err) }
365}
366
367// keccak256 returns the keccak 256 bit checksum of the data.
368pub fn keccak256(data []u8) []u8 {
369 mut d := new256keccak() or { panic(err) }
370 d.write(data) or { panic(err) }
371 return d.checksum_internal() or { panic(err) }
372}
373
374// keccak512 returns the keccak 512 bit checksum of the data.
375pub fn keccak512(data []u8) []u8 {
376 mut d := new512keccak() or { panic(err) }
377 d.write(data) or { panic(err) }
378 return d.checksum_internal() or { panic(err) }
379}
380
381// shake256 returns the sha3 shake256 bit extended output
382pub fn shake256(data []u8, output_len int) []u8 {
383 mut d := new256xof(output_len) or { panic(err) }
384 d.write(data) or { panic(err) }
385 return d.checksum_internal() or { panic(err) }
386}
387
388// shake128 returns the sha3 shake128 bit extended output
389pub fn shake128(data []u8, output_len int) []u8 {
390 mut d := new128xof(output_len) or { panic(err) }
391 d.write(data) or { panic(err) }
392 return d.checksum_internal() or { panic(err) }
393}
394