v2 / vlib / crypto / sha3 / xof.v
135 lines · 112 sloc · 3.01 KB · b615cd08d134956354a72dcc42a6a6ad4e39cb64
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
5// streaming shake-128/256 xof per FIPS 202
6// https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
7
8module sha3
9
10@[noinit]
11pub struct Shake {
12 rate int // bytes per permutation (168 for shake-128, 136 for shake-256)
13mut:
14 s State
15 input_buffer []u8
16 finalized bool
17 squeeze_buf []u8
18}
19
20// new_shake128 returns a new Shake instance for SHAKE-128 extended output function.
21pub fn new_shake128() &Shake {
22 return &Shake{
23 rate: xof_rate_128
24 }
25}
26
27// new_shake256 returns a new Shake instance for SHAKE-256 extended output function.
28pub fn new_shake256() &Shake {
29 return &Shake{
30 rate: xof_rate_256
31 }
32}
33
34// write absorbs more data into the sponge state.
35// Panics if called after `read`.
36@[direct_array_access]
37pub fn (mut s Shake) write(data []u8) {
38 if s.finalized {
39 panic('sha3: write after read on Shake')
40 }
41 if data.len == 0 {
42 return
43 }
44
45 // avoid cloning on each iteration
46 mut remaining := unsafe { data[..] }
47
48 if s.input_buffer.len != 0 {
49 empty_space := s.rate - s.input_buffer.len
50
51 if remaining.len < empty_space {
52 s.input_buffer << remaining
53 return
54 } else {
55 s.input_buffer << remaining[..empty_space]
56 remaining = unsafe { remaining[empty_space..] }
57
58 s.s.xor_bytes(s.input_buffer[..s.rate], s.rate)
59 s.s.kaccak_p_1600_24()
60
61 s.input_buffer = []u8{}
62 }
63 }
64
65 for remaining.len >= s.rate {
66 s.s.xor_bytes(remaining[..s.rate], s.rate)
67 s.s.kaccak_p_1600_24()
68 remaining = unsafe { remaining[s.rate..] }
69 }
70
71 if remaining.len > 0 {
72 s.input_buffer = remaining.clone()
73 }
74}
75
76fn (mut s Shake) finalize() {
77 if s.finalized {
78 return
79 }
80 s.finalized = true
81
82 // pad10*1 with xof domain separator 0x1f (FIPS 202 sec B.2)
83 mut padded := s.input_buffer.clone()
84 if padded.len == s.rate - 1 {
85 padded << u8(0x80 | 0x1f)
86 } else {
87 padded << u8(0x1f)
88 for padded.len < s.rate - 1 {
89 padded << u8(0x00)
90 }
91 padded << u8(0x80)
92 }
93
94 s.s.xor_bytes(padded[..s.rate], s.rate)
95 s.s.kaccak_p_1600_24()
96
97 state_bytes := s.s.to_bytes()
98 s.squeeze_buf = state_bytes[..s.rate].clone()
99 s.input_buffer = []u8{}
100}
101
102// read squeezes `out_len` bytes from the sponge state.
103// Finalizes the sponge on first call; further calls to `write` will panic.
104@[direct_array_access]
105pub fn (mut s Shake) read(out_len int) []u8 {
106 if !s.finalized {
107 s.finalize()
108 }
109
110 mut result := []u8{cap: out_len}
111 mut remaining := out_len
112
113 for remaining > 0 {
114 if s.squeeze_buf.len == 0 {
115 s.s.kaccak_p_1600_24()
116 state_bytes := s.s.to_bytes()
117 s.squeeze_buf = state_bytes[..s.rate].clone()
118 }
119
120 take := if remaining < s.squeeze_buf.len { remaining } else { s.squeeze_buf.len }
121 result << s.squeeze_buf[..take]
122 s.squeeze_buf = s.squeeze_buf[take..].clone()
123 remaining -= take
124 }
125
126 return result
127}
128
129// reset clears the sponge state, allowing the Shake instance to be reused.
130pub fn (mut s Shake) reset() {
131 s.s = State{}
132 s.input_buffer = []u8{}
133 s.finalized = false
134 s.squeeze_buf = []u8{}
135}
136