136 lines
3.0 KiB
V
136 lines
3.0 KiB
V
// Copyright (c) 2023 Kim Shrier. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
|
|
// streaming shake-128/256 xof per FIPS 202
|
|
// https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
|
|
|
|
module sha3
|
|
|
|
@[noinit]
|
|
pub struct Shake {
|
|
rate int // bytes per permutation (168 for shake-128, 136 for shake-256)
|
|
mut:
|
|
s State
|
|
input_buffer []u8
|
|
finalized bool
|
|
squeeze_buf []u8
|
|
}
|
|
|
|
// new_shake128 returns a new Shake instance for SHAKE-128 extended output function.
|
|
pub fn new_shake128() &Shake {
|
|
return &Shake{
|
|
rate: xof_rate_128
|
|
}
|
|
}
|
|
|
|
// new_shake256 returns a new Shake instance for SHAKE-256 extended output function.
|
|
pub fn new_shake256() &Shake {
|
|
return &Shake{
|
|
rate: xof_rate_256
|
|
}
|
|
}
|
|
|
|
// write absorbs more data into the sponge state.
|
|
// Panics if called after `read`.
|
|
@[direct_array_access]
|
|
pub fn (mut s Shake) write(data []u8) {
|
|
if s.finalized {
|
|
panic('sha3: write after read on Shake')
|
|
}
|
|
if data.len == 0 {
|
|
return
|
|
}
|
|
|
|
// avoid cloning on each iteration
|
|
mut remaining := unsafe { data[..] }
|
|
|
|
if s.input_buffer.len != 0 {
|
|
empty_space := s.rate - s.input_buffer.len
|
|
|
|
if remaining.len < empty_space {
|
|
s.input_buffer << remaining
|
|
return
|
|
} else {
|
|
s.input_buffer << remaining[..empty_space]
|
|
remaining = unsafe { remaining[empty_space..] }
|
|
|
|
s.s.xor_bytes(s.input_buffer[..s.rate], s.rate)
|
|
s.s.kaccak_p_1600_24()
|
|
|
|
s.input_buffer = []u8{}
|
|
}
|
|
}
|
|
|
|
for remaining.len >= s.rate {
|
|
s.s.xor_bytes(remaining[..s.rate], s.rate)
|
|
s.s.kaccak_p_1600_24()
|
|
remaining = unsafe { remaining[s.rate..] }
|
|
}
|
|
|
|
if remaining.len > 0 {
|
|
s.input_buffer = remaining.clone()
|
|
}
|
|
}
|
|
|
|
fn (mut s Shake) finalize() {
|
|
if s.finalized {
|
|
return
|
|
}
|
|
s.finalized = true
|
|
|
|
// pad10*1 with xof domain separator 0x1f (FIPS 202 sec B.2)
|
|
mut padded := s.input_buffer.clone()
|
|
if padded.len == s.rate - 1 {
|
|
padded << u8(0x80 | 0x1f)
|
|
} else {
|
|
padded << u8(0x1f)
|
|
for padded.len < s.rate - 1 {
|
|
padded << u8(0x00)
|
|
}
|
|
padded << u8(0x80)
|
|
}
|
|
|
|
s.s.xor_bytes(padded[..s.rate], s.rate)
|
|
s.s.kaccak_p_1600_24()
|
|
|
|
state_bytes := s.s.to_bytes()
|
|
s.squeeze_buf = state_bytes[..s.rate].clone()
|
|
s.input_buffer = []u8{}
|
|
}
|
|
|
|
// read squeezes `out_len` bytes from the sponge state.
|
|
// Finalizes the sponge on first call; further calls to `write` will panic.
|
|
@[direct_array_access]
|
|
pub fn (mut s Shake) read(out_len int) []u8 {
|
|
if !s.finalized {
|
|
s.finalize()
|
|
}
|
|
|
|
mut result := []u8{cap: out_len}
|
|
mut remaining := out_len
|
|
|
|
for remaining > 0 {
|
|
if s.squeeze_buf.len == 0 {
|
|
s.s.kaccak_p_1600_24()
|
|
state_bytes := s.s.to_bytes()
|
|
s.squeeze_buf = state_bytes[..s.rate].clone()
|
|
}
|
|
|
|
take := if remaining < s.squeeze_buf.len { remaining } else { s.squeeze_buf.len }
|
|
result << s.squeeze_buf[..take]
|
|
s.squeeze_buf = s.squeeze_buf[take..].clone()
|
|
remaining -= take
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// reset clears the sponge state, allowing the Shake instance to be reused.
|
|
pub fn (mut s Shake) reset() {
|
|
s.s = State{}
|
|
s.input_buffer = []u8{}
|
|
s.finalized = false
|
|
s.squeeze_buf = []u8{}
|
|
}
|