///|
/// SHAKE (Secure Hash Algorithm Keccak) extendable-output functions for Dilithium.
///
/// This module implements the SHAKE128 and SHAKE256 extendable-output functions (XOF)
/// based on the Keccak sponge construction. These functions serve as the primary
/// source of cryptographic randomness and hash operations within the Dilithium
/// signature scheme.
///
/// # Cryptographic Role
///
/// SHAKE functions are used throughout Dilithium for:
/// - Seed expansion during key generation
/// - Challenge polynomial generation during signing
/// - Random polynomial sampling
/// - Hash-based message processing
///
/// # Implementation Details
///
/// The implementation follows the NIST FIPS 202 standard specification and uses
/// the Keccak[1600] permutation with appropriate padding and domain separation.
///
/// # References
///
/// - NIST FIPS 202: SHA-3 Standard - Permutation-Based Hash and Extendable-Output Functions
/// - Keccak Team: https://keccak.team/
/// - Original reference: https://github.com/Argyle-Software/dilithium/blob/master/src/fips202.rs
///
/// # Keccak Algorithm Overview (Sponge Construction)
///
/// The Keccak algorithm operates as a sponge construction with three main phases:
///
/// 1. **Absorbing Phase**: Input data is absorbed into a fixed-size state.
/// Data is processed in blocks, with permutation applied after each block.
///
/// This file implements the SHAKE128 and SHAKE256 extendable-output functions
/// based on the Keccak algorithm, which forms the foundation of SHA-3.
/// 本文件基于 Keccak 算法实现 SHAKE128 和 SHAKE256 可扩展输出函数,
/// Keccak 算法是 SHA-3 的基础。
///
/// ## Keccak Algorithm Overview (Sponge Construction)
/// ## Keccak 算法概览(海绵结构)
///
/// The Keccak algorithm operates as a sponge construction with three main phases:
/// Keccak 算法采用海绵结构,包含三个主要阶段:
///
/// 1. **Absorbing Phase**: Input data is absorbed into a fixed-size state.
/// **吸收阶段**:输入数据被吸收到固定大小的状态中。
/// Data is processed in blocks, with permutation applied after each block.
/// 数据以块为单位处理,每个块处理后都会进行一次置换。
///
/// 2. **Finalizing Phase**: Padding is added to mark the end of input.
/// **完成阶段**:添加填充以标记输入的结束。
/// This prepares the state for the squeezing phase.
/// 这为挤压阶段准备状态。
///
/// 3. **Squeezing Phase**: Output data is squeezed from the state.
/// **挤压阶段**:从状态中挤压出输出数据。
/// Permutation is applied as needed to generate more output.
/// 根据需要进行置换以生成更多输出。
///
/// SHAKE functions are XOFs, meaning they can produce output of any desired length.
/// SHAKE 函数是 XOF,意味着它们可以产生任意所需长度的输出。
///| SHAKE128 rate (block size) in bytes | SHAKE128 的比特率(块大小),以字节为单位
// pub const SHAKE128_RATE : UInt = 168
///| SHAKE256 rate (block size) in bytes | SHAKE256 的比特率(块大小),以字节为单位
// pub const SHAKE256_RATE : UInt = 136
///|
/// Defines the number of rounds in the Keccak-f\[1600] permutation function.
///
/// The Keccak-f\[1600] permutation is the core cryptographic primitive used in
/// the SHAKE128 and SHAKE256 extendable-output functions. This constant
/// specifies that exactly 24 rounds of transformation are applied to the
/// 1600-bit state during each permutation operation, which is essential for
/// achieving the required security properties of the Keccak algorithm.
const NROUNDS : Int = 24
///|
/// Internal state structure for Keccak cryptographic hash functions.
///
/// Fields:
///
/// * `s` : 25-element flattened representation of the 5x5 Keccak state matrix,
/// where each element is a 64-bit word.
/// * `pos` : Current byte position within the rate block, used to track
/// progress during incremental absorption and squeezing operations.
pub struct KeccakState {
s : FixedArray[UInt64] // 25-element flattened state array | 25 元素的展平状态数组
mut pos : UInt // Current position in the rate block | 当前在比特率块中的位置
} derive(Show)
///|
/// Creates a default instance of `KeccakState` with all state elements
/// initialized to zero.
///
/// Returns a new `KeccakState` with:
///
/// * `s`: A 25-element array of 64-bit unsigned integers, all initialized to 0
/// * `pos`: Current position set to 0
pub impl Default for KeccakState with default() {
{ s: FixedArray::make(25, 0), pos: 0 }
}
///|
/// Resets the Keccak state to its initial clean state with all fields zeroed.
///
/// Parameters:
///
/// * `self` : The KeccakState instance to be initialized.
pub fn KeccakState::init(self : KeccakState) -> Unit {
self.s.fill(0)
self.pos = 0
}
///|
/// Performs circular left rotation on a 64-bit unsigned integer by the
/// specified number of bit positions.
///
/// Parameters:
///
/// * `value` : The 64-bit unsigned integer to rotate.
/// * `positions` : The number of bit positions to rotate left. Should be
/// between 0 and 63 for meaningful results.
///
/// Returns the rotated 64-bit unsigned integer where bits that shift beyond the
/// left edge wrap around to the right edge.
fn KeccakState::rol(a : UInt64, offset : Int) -> UInt64 {
(a << offset) | (a >> (64 - offset))
}
///|
/// Converts an 8-byte array into a 64-bit unsigned integer using little-endian
/// byte ordering.
///
/// Parameters:
///
/// * `x` : An array of 8 bytes to be converted to a 64-bit integer.
///
/// Returns a `UInt64` value constructed from the input bytes in little-endian
/// format.
pub fn load64(x : Array[Byte]) -> UInt64 {
let mut r = 0UL
for i in 0..<8 {
let v : UInt64 = x[i].to_uint64() << (8 * i)
r = r | v
}
r
}
///|
/// Stores a 64-bit unsigned integer into an 8-byte array using little-endian
/// byte order.
///
/// Parameters:
///
/// * `x` : The 8-byte array where the integer will be stored.
/// * `u` : The 64-bit unsigned integer to be stored.
pub fn store64(x : Array[Byte], u : UInt64) -> Unit {
for i in 0..<8 {
x[i] = (u >> (8 * i)).to_byte()
}
}
///|
/// Precomputed round constants used in the Keccak-f\[1600] permutation
/// function.
///
/// This array contains the 24 round constants that are XORed with the state
/// during the iota step of each round in the Keccak-f\[1600] permutation. Each
/// constant is a 64-bit value that was derived from a linear feedback shift
/// register (LFSR) as specified in the Keccak specification. These constants
/// ensure that each round of the permutation is cryptographically distinct and
/// contribute to the security properties of the SHAKE128 and SHAKE256
/// functions.
///
/// The constants are applied in order during the 24 rounds of the Keccak
/// permutation, with `keccakf_round_constants[i]` being used in round `i`. The
/// values follow the official Keccak specification and are critical for the
/// correctness of the cryptographic hash function implementation.
///
let keccakf_round_constants = [
0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL,
0x000000000000808bUL, 0x0000000080000001UL, 0x8000000080008081UL, 0x8000000000008009UL,
0x000000000000008aUL, 0x0000000000000088UL, 0x0000000080008009UL, 0x000000008000000aUL,
0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL,
]
///| Keccak-f[1600] permutation function | Keccak-f[1600] 置换函数
/// Applies the complete Keccak permutation to the state
/// 对状态应用完整的 Keccak 置换
pub fn keccakf1600_statepermute(state : FixedArray[UInt64]) -> Unit {
// Copy state to local variables for processing | 将状态复制到局部变量进行处理
let mut aba = state[0]
let mut abe = state[1]
let mut abi = state[2]
let mut abo = state[3]
let mut abu = state[4]
let mut aga = state[5]
let mut age = state[6]
let mut agi = state[7]
let mut ago = state[8]
let mut agu = state[9]
let mut aka = state[10]
let mut ake = state[11]
let mut aki = state[12]
let mut ako = state[13]
let mut aku = state[14]
let mut ama = state[15]
let mut ame = state[16]
let mut ami = state[17]
let mut amo = state[18]
let mut amu = state[19]
let mut asa = state[20]
let mut ase = state[21]
let mut asi = state[22]
let mut aso = state[23]
let mut asu = state[24]
let mut round = 0
while round < NROUNDS {
// Theta step: compute column parities | Theta 步:计算列奇偶性
let mut bca = aba ^ aga ^ aka ^ ama ^ asa
let mut bce = abe ^ age ^ ake ^ ame ^ ase
let mut bci = abi ^ agi ^ aki ^ ami ^ asi
let mut bco = abo ^ ago ^ ako ^ amo ^ aso
let mut bcu = abu ^ agu ^ aku ^ amu ^ asu
// Combined Rho, Pi, Chi, Iota steps | 组合的 Rho, Pi, Chi, Iota 步骤
let mut da = bcu ^ KeccakState::rol(bce, 1)
let mut de = bca ^ KeccakState::rol(bci, 1)
let mut di = bce ^ KeccakState::rol(bco, 1)
let mut d_o = bci ^ KeccakState::rol(bcu, 1)
let mut du = bco ^ KeccakState::rol(bca, 1)
aba = aba ^ da
bca = aba
age = age ^ de
bce = KeccakState::rol(age, 44)
aki = aki ^ di
bci = KeccakState::rol(aki, 43)
amo = amo ^ d_o
bco = KeccakState::rol(amo, 21)
asu = asu ^ du
bcu = KeccakState::rol(asu, 14)
let mut eba = bca ^ (bce.lnot() & bci)
eba = eba ^ keccakf_round_constants[round]
let mut ebe = bce ^ (bci.lnot() & bco)
let mut ebi = bci ^ (bco.lnot() & bcu)
let mut ebo = bco ^ (bcu.lnot() & bca)
let mut ebu = bcu ^ (bca.lnot() & bce)
abo = abo ^ d_o
bca = KeccakState::rol(abo, 28)
agu = agu ^ du
bce = KeccakState::rol(agu, 20)
aka = aka ^ da
bci = KeccakState::rol(aka, 3)
ame = ame ^ de
bco = KeccakState::rol(ame, 45)
asi = asi ^ di
bcu = KeccakState::rol(asi, 61)
let mut ega = bca ^ (bce.lnot() & bci)
let mut ege = bce ^ (bci.lnot() & bco)
let mut egi = bci ^ (bco.lnot() & bcu)
let mut ego = bco ^ (bcu.lnot() & bca)
let mut egu = bcu ^ (bca.lnot() & bce)
abe = abe ^ de
bca = KeccakState::rol(abe, 1)
agi = agi ^ di
bce = KeccakState::rol(agi, 6)
ako = ako ^ d_o
bci = KeccakState::rol(ako, 25)
amu = amu ^ du
bco = KeccakState::rol(amu, 8)
asa = asa ^ da
bcu = KeccakState::rol(asa, 18)
let mut eka = bca ^ (bce.lnot() & bci)
let mut eke = bce ^ (bci.lnot() & bco)
let mut eki = bci ^ (bco.lnot() & bcu)
let mut eko = bco ^ (bcu.lnot() & bca)
let mut eku = bcu ^ (bca.lnot() & bce)
abu = abu ^ du
bca = KeccakState::rol(abu, 27)
aga = aga ^ da
bce = KeccakState::rol(aga, 36)
ake = ake ^ de
bci = KeccakState::rol(ake, 10)
ami = ami ^ di
bco = KeccakState::rol(ami, 15)
aso = aso ^ d_o
bcu = KeccakState::rol(aso, 56)
let mut ema = bca ^ (bce.lnot() & bci)
let mut eme = bce ^ (bci.lnot() & bco)
let mut emi = bci ^ (bco.lnot() & bcu)
let mut emo = bco ^ (bcu.lnot() & bca)
let mut emu = bcu ^ (bca.lnot() & bce)
abi = abi ^ di
bca = KeccakState::rol(abi, 62)
ago = ago ^ d_o
bce = KeccakState::rol(ago, 55)
aku = aku ^ du
bci = KeccakState::rol(aku, 39)
ama = ama ^ da
bco = KeccakState::rol(ama, 41)
ase = ase ^ de
bcu = KeccakState::rol(ase, 2)
let mut esa = bca ^ (bce.lnot() & bci)
let mut ese = bce ^ (bci.lnot() & bco)
let mut esi = bci ^ (bco.lnot() & bcu)
let mut eso = bco ^ (bcu.lnot() & bca)
let mut esu = bcu ^ (bca.lnot() & bce)
// Second theta step | 第二个 theta 步骤
bca = eba ^ ega ^ eka ^ ema ^ esa
bce = ebe ^ ege ^ eke ^ eme ^ ese
bci = ebi ^ egi ^ eki ^ emi ^ esi
bco = ebo ^ ego ^ eko ^ emo ^ eso
bcu = ebu ^ egu ^ eku ^ emu ^ esu
// Second combined step | 第二个组合步骤
da = bcu ^ KeccakState::rol(bce, 1)
de = bca ^ KeccakState::rol(bci, 1)
di = bce ^ KeccakState::rol(bco, 1)
d_o = bci ^ KeccakState::rol(bcu, 1)
du = bco ^ KeccakState::rol(bca, 1)
eba = eba ^ da
bca = eba
ege = ege ^ de
bce = KeccakState::rol(ege, 44)
eki = eki ^ di
bci = KeccakState::rol(eki, 43)
emo = emo ^ d_o
bco = KeccakState::rol(emo, 21)
esu = esu ^ du
bcu = KeccakState::rol(esu, 14)
aba = bca ^ (bce.lnot() & bci)
aba = aba ^ keccakf_round_constants[round + 1]
abe = bce ^ (bci.lnot() & bco)
abi = bci ^ (bco.lnot() & bcu)
abo = bco ^ (bcu.lnot() & bca)
abu = bcu ^ (bca.lnot() & bce)
ebo = ebo ^ d_o
bca = KeccakState::rol(ebo, 28)
egu = egu ^ du
bce = KeccakState::rol(egu, 20)
eka = eka ^ da
bci = KeccakState::rol(eka, 3)
eme = eme ^ de
bco = KeccakState::rol(eme, 45)
esi = esi ^ di
bcu = KeccakState::rol(esi, 61)
aga = bca ^ (bce.lnot() & bci)
age = bce ^ (bci.lnot() & bco)
agi = bci ^ (bco.lnot() & bcu)
ago = bco ^ (bcu.lnot() & bca)
agu = bcu ^ (bca.lnot() & bce)
ebe = ebe ^ de
bca = KeccakState::rol(ebe, 1)
egi = egi ^ di
bce = KeccakState::rol(egi, 6)
eko = eko ^ d_o
bci = KeccakState::rol(eko, 25)
emu = emu ^ du
bco = KeccakState::rol(emu, 8)
esa = esa ^ da
bcu = KeccakState::rol(esa, 18)
aka = bca ^ (bce.lnot() & bci)
ake = bce ^ (bci.lnot() & bco)
aki = bci ^ (bco.lnot() & bcu)
ako = bco ^ (bcu.lnot() & bca)
aku = bcu ^ (bca.lnot() & bce)
ebu = ebu ^ du
bca = KeccakState::rol(ebu, 27)
ega = ega ^ da
bce = KeccakState::rol(ega, 36)
eke = eke ^ de
bci = KeccakState::rol(eke, 10)
emi = emi ^ di
bco = KeccakState::rol(emi, 15)
eso = eso ^ d_o
bcu = KeccakState::rol(eso, 56)
ama = bca ^ (bce.lnot() & bci)
ame = bce ^ (bci.lnot() & bco)
ami = bci ^ (bco.lnot() & bcu)
amo = bco ^ (bcu.lnot() & bca)
amu = bcu ^ (bca.lnot() & bce)
ebi = ebi ^ di
bca = KeccakState::rol(ebi, 62)
ego = ego ^ d_o
bce = KeccakState::rol(ego, 55)
eku = eku ^ du
bci = KeccakState::rol(eku, 39)
ema = ema ^ da
bco = KeccakState::rol(ema, 41)
ese = ese ^ de
bcu = KeccakState::rol(ese, 2)
asa = bca ^ (bce.lnot() & bci)
ase = bce ^ (bci.lnot() & bco)
asi = bci ^ (bco.lnot() & bcu)
aso = bco ^ (bcu.lnot() & bca)
asu = bcu ^ (bca.lnot() & bce)
round += 2 // Process two rounds at once | 一次处理两轮
}
// Copy back to state | 复制回状态数组
state[0] = aba
state[1] = abe
state[2] = abi
state[3] = abo
state[4] = abu
state[5] = aga
state[6] = age
state[7] = agi
state[8] = ago
state[9] = agu
state[10] = aka
state[11] = ake
state[12] = aki
state[13] = ako
state[14] = aku
state[15] = ama
state[16] = ame
state[17] = ami
state[18] = amo
state[19] = amu
state[20] = asa
state[21] = ase
state[22] = asi
state[23] = aso
state[24] = asu
}
///| Incremental absorb for Keccak | Keccak 的增量吸收
/// Processes input data in blocks, updating the state incrementally
/// 以块为单位处理输入数据,增量更新状态
fn keccak_absorb(
state : KeccakState,
r : UInt,
input : Array[Byte],
inlen : UInt,
) -> Unit {
let mut idx = 0
let mut pos = state.pos
let mut inlen = inlen
while pos + inlen >= r {
// Fill current block and permute | 填充当前块并置换
for i in pos..> 3).reinterpret_as_int()
let offset = (i & 7).reinterpret_as_int()
state.s[index] = state.s[index] ^ (input[idx].to_uint64() << (8 * offset))
idx += 1
}
inlen -= r - pos
keccakf1600_statepermute(state.s)
pos = 0
}
// Absorb remaining partial block | 吸收剩余的部分块
for i in pos..<(pos + inlen) {
let index = (i >> 3).reinterpret_as_int()
let offset = (i & 7).reinterpret_as_int()
state.s[index] = state.s[index] ^ (input[idx].to_uint64() << (8 * offset))
idx += 1
}
state.pos = pos + inlen
}
///|
/// Finalizes the Keccak absorb phase by adding appropriate padding and
/// termination bits to mark the end of input data.
///
/// Parameters:
///
/// * `state` : The Keccak state array containing 25 64-bit words representing
/// the internal state.
/// * `pos` : Current position in bytes within the rate block where the next
/// input byte would be absorbed.
/// * `rate` : The rate parameter in bytes, determining the size of the
/// absorbing capacity for this Keccak variant.
/// * `padding_byte` : The padding byte value that identifies the specific
/// Keccak variant (e.g., 0x1F for SHAKE, 0x06 for SHA-3).
///
/// Panics if `rate` is less than 8 bytes, as this would cause an underflow when
/// calculating the final state index.
fn keccak_finalize(
s : FixedArray[UInt64],
pos : UInt,
r : UInt,
p : Byte,
) -> Unit {
let index = (pos >> 3).reinterpret_as_int()
let offset = (pos & 7).reinterpret_as_int()
s[index] = s[index] ^ (p.to_uint64() << (8 * offset))
let index = ((r >> 3) - 1).reinterpret_as_int()
s[index] = s[index] ^ (1UL << 63)
}
///|
/// Extracts output bytes from the Keccak state, applying permutation when
/// necessary.
///
/// This function implements the "squeeze" phase of the Keccak sponge
/// construction. It extracts the requested number of output bytes from the
/// internal state, automatically applying the Keccak-f\[1600] permutation when
/// the current block is exhausted and more output is needed.
///
/// Parameters:
///
/// * `out` : The output buffer to write the extracted bytes to.
/// * `outlen` : The number of bytes to extract.
/// * `s` : The Keccak state array (25 64-bit words).
/// * `pos` : The current position within the rate block (in bytes).
/// * `r` : The rate (block size) in bytes for the specific Keccak variant.
///
/// Returns the updated position within the rate block after extraction.
///
fn keccak_squeeze(
out : ArrayView[Byte],
outlen : UInt,
s : FixedArray[UInt64],
pos : UInt,
r : UInt,
) -> UInt {
let mut pos = pos
let mut outlen = outlen
while outlen != 0 {
if pos == r {
// Permute state when current block is exhausted | 当前块用尽时置换状态
keccakf1600_statepermute(s)
pos = 0
}
let mut i = pos
let mut idx = 0
while i < r && i < pos + outlen {
let index = (i >> 3).reinterpret_as_int()
let offset = (i & 7).reinterpret_as_int()
out[idx] = (s[index] >> (8 * offset)).to_byte()
idx += 1
i += 1
}
outlen -= i - pos
pos = i
}
return pos
}
///| Non-incremental absorb for Keccak | Keccak 的非增量吸收
/// Initializes state, absorbs all input, and finalizes in one go
/// 初始化状态,吸收所有输入,并一次性完成
fn keccak_absorb_once(
s : FixedArray[UInt64],
r : UInt,
input : Array[Byte],
inlen : UInt,
p : Byte,
) -> Unit {
let mut inlen = inlen.reinterpret_as_int()
let r = r.reinterpret_as_int()
let mut idx = 0
s.fill(0)
while inlen >= r {
// Process full blocks | 处理完整块
for i in 0..<(r / 8) {
let start = idx + 8 * i
s[i] = s[i] ^ load64(input[start:start + 8].to_array())
}
idx += r
inlen -= r
keccakf1600_statepermute(s)
}
// Absorb partial block and add padding | 吸收部分块并添加填充
for i in 0.. Unit {
let mut nblocks = nblocks
let r = r.reinterpret_as_int()
let mut idx = 0
while nblocks > 0 {
keccakf1600_statepermute(s)
// Extract full block | 提取完整块
for i in 0..<(r >> 3) {
let arr : Array[Byte] = Array::make(8, 0)
store64(arr, s[i])
for j in 0..<8 {
out[idx + 8 * i + j] = arr[j]
}
}
idx += r
nblocks -= 1
}
}
///|
/// Incrementally absorbs input data into a SHAKE128 state for cryptographic
/// hashing.
///
/// This function is part of the SHAKE128 extendable-output function (XOF)
/// implementation. It processes input data in chunks, updating the internal
/// Keccak state without finalizing the hash. Multiple calls to this function
/// can be made to absorb data incrementally before calling the finalize
/// function.
///
/// Parameters:
///
/// * `state` : The SHAKE128 state to absorb data into. The state maintains the
/// current position and internal Keccak state array.
/// * `input` : The byte array containing the data to be absorbed into the hash
/// state.
/// * `inlen` : The number of bytes from the input array to process. This allows
/// processing only a portion of the input array if needed.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// let data : Array[Byte] = "Hello, World!".to_bytes().to_array()
/// shake128_absorb(state, data, data.length().reinterpret_as_uint())
/// // Continue absorbing more data if needed
/// let more_data : Array[Byte] = " More data".to_bytes().to_array()
/// shake128_absorb(state, more_data, more_data.length().reinterpret_as_uint())
/// // Finally call shake128_finalize(state) to complete the absorption phase
/// ```
///
pub fn shake128_absorb(
state : KeccakState,
input : Array[Byte],
inlen : UInt,
) -> Unit {
keccak_absorb(state, SHAKE128_RATE, input, inlen)
}
///|
/// Finalizes the SHAKE128 absorbing phase by applying proper padding and
/// transitions the state to the squeezing phase.
///
/// Parameters:
///
/// * `state` : The SHAKE128 state that has been absorbing input data and needs
/// to be finalized.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// let input : Array[Byte] = "Hello, World!".to_bytes().to_array()
/// shake128_absorb(state, input, input.length().reinterpret_as_uint())
/// shake128_finalize(state)
/// // State is now ready for squeezing output
/// ```
///
pub fn shake128_finalize(state : KeccakState) -> Unit {
keccak_finalize(state.s, state.pos, SHAKE128_RATE, 0x1F)
state.pos = SHAKE128_RATE
}
///|
/// Extracts full blocks of output data from a SHAKE128 state.
///
/// Parameters:
///
/// * `output` : The buffer to store the extracted output bytes.
/// * `nblocks` : The number of complete blocks to extract from the state.
/// * `s` : The SHAKE128 state to extract data from.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// // ... after initializing and finalizing the state ...
/// let output: Array[Byte] = Array::make(336, 0) // 2 blocks * 168 bytes per block
/// shake128_squeezeblocks(output, 2, state)
/// ```
///
pub fn shake128_squeezeblocks(
output : ArrayView[Byte],
nblocks : UInt,
s : KeccakState,
) -> Unit {
keccak_squeezeblocks(output, nblocks, s.s, SHAKE128_RATE)
}
///|
/// Incrementally absorbs input data into the SHAKE256 state during the
/// absorbing phase.
///
/// This function allows you to feed data to SHAKE256 in multiple chunks rather
/// than all at once. The state maintains its position internally, so you can
/// call this function multiple times with different input chunks before
/// finalizing and squeezing output.
///
/// Parameters:
///
/// * `state` (`KeccakState`) : The SHAKE256 state object that maintains the
/// internal hash state.
/// * `input` (`Array[Byte]`) : The byte array containing the data to absorb.
/// * `inlen` (`UInt`) : The number of bytes to absorb from the input array.
///
/// Example:
///
/// ```moonbit
/// let state = @ruifeng/moondsa.KeccakState::default()
/// let chunk1 : Array[Byte] = b"Hello, ".to_array()
/// let chunk2 : Array[Byte] = b"World!".to_array()
///
/// // Absorb data incrementally
/// @ruifeng/moondsa.shake256_absorb(state, chunk1, 7U)
/// @ruifeng/moondsa.shake256_absorb(state, chunk2, 6U)
///
/// // Finalize before squeezing
/// @ruifeng/moondsa.shake256_finalize(state)
///
/// // Now squeeze output
/// let output : Array[Byte] = Array::make(32, 0)
/// @ruifeng/moondsa.shake256_squeeze(output, 32U, state)
/// ```
///
pub fn shake256_absorb(
state : KeccakState,
input : Array[Byte],
inlen : UInt,
) -> Unit {
keccak_absorb(state, SHAKE256_RATE, input, inlen)
}
///|
/// Finalizes the SHAKE256 absorbing phase by applying proper padding and
/// preparing the state for output extraction.
///
/// Parameters:
///
/// * `state` : The SHAKE256 Keccak state to finalize, which has been absorbing
/// input data.
///
/// Example:
///
/// ```moonbit
/// let state = @ruifeng/moondsa.KeccakState::default()
/// let input : Array[Byte] = b"hello world".to_array()
/// @ruifeng/moondsa.shake256_absorb(state, input, 11)
/// @ruifeng/moondsa.shake256_finalize(state)
/// // State is now ready for squeezing output
/// ```
///
pub fn shake256_finalize(state : KeccakState) -> Unit {
keccak_finalize(state.s, state.pos, SHAKE256_RATE, 0x1F)
state.pos = SHAKE256_RATE
}
///|
/// Extracts output bytes from a SHAKE256 state in the squeezing phase.
///
/// Parameters:
///
/// * `out` : The output buffer to write the extracted bytes to.
/// * `outlen` : The number of bytes to extract from the state.
/// * `state` : The SHAKE256 state to squeeze from. The state's internal
/// position will be updated to reflect the new position after squeezing.
///
/// Example:
///
/// ```moonbit
/// // Initialize SHAKE256 state and absorb some data
/// let state = KeccakState::default()
/// let input : Array[Byte] = "Hello, World!".to_bytes().to_array()
/// shake256_absorb(state, input, input.length().reinterpret_as_uint())
/// shake256_finalize(state)
///
/// // Squeeze 32 bytes of output
/// let output : Array[Byte] = Array::make(32, 0)
/// shake256_squeeze(output[:], 32, state)
/// ```
///
pub fn shake256_squeeze(
out : ArrayView[Byte],
outlen : UInt,
state : KeccakState,
) -> Unit {
state.pos = keccak_squeeze(out, outlen, state.s, state.pos, SHAKE256_RATE)
}
///|
/// Performs complete SHAKE256 absorption in a single operation, processing all
/// input data and finalizing the state.
///
/// Parameters:
///
/// * `state` : The Keccak state to be initialized and used for absorption.
/// * `input` : The byte array containing the data to be absorbed.
/// * `inlen` : The number of bytes from the input array to process.
///
/// Example:
///
/// ```moonbit
/// let state = @ruifeng/moondsa.KeccakState::default()
/// let data : Array[Byte] = [0, 1, 2, 3]
/// @ruifeng/moondsa.shake256_absorb_once(state, data, 4)
/// // State is now ready for squeezing output
/// ```
///
pub fn shake256_absorb_once(
state : KeccakState,
input : Array[Byte],
inlen : UInt,
) -> Unit {
keccak_absorb_once(state.s, SHAKE256_RATE, input, inlen, 0x1F)
state.pos = SHAKE256_RATE
}
///|
/// Extracts full blocks of output data from a SHAKE256 state.
///
/// Parameters:
///
/// * `out` : The output array to store the extracted bytes.
/// * `nblocks` : The number of full blocks to extract from the state.
/// * `state` : The SHAKE256 state to extract data from.
///
/// Examples:
///
/// ```moonbit
/// // Initialize SHAKE256 state and absorb some input
/// let state = KeccakState::default()
/// shake256_absorb_once(state, [1, 2, 3, 4], 4)
///
/// // Extract 2 full blocks (272 bytes total for SHAKE256)
/// let output : Array[Byte] = Array::make(272, 0)
/// shake256_squeezeblocks(output, 2, state)
/// ```
///
pub fn shake256_squeezeblocks(
out : Array[Byte],
nblocks : UInt,
state : KeccakState,
) -> Unit {
keccak_squeezeblocks(out, nblocks, state.s, SHAKE256_RATE)
}
///| SHAKE256 complete operation | SHAKE256 完整操作
/// Performs complete SHAKE256 hash: absorb input and squeeze output
/// 执行完整的 SHAKE256 哈希:吸收输入并挤压输出
pub fn shake256(
output~ : Array[Byte],
outlen : UInt,
input~ : Array[Byte],
inlen : UInt,
) -> Unit {
let mut outlen = outlen
let state = KeccakState::default()
shake256_absorb_once(state, input, inlen)
let nblocks = outlen / SHAKE256_RATE
shake256_squeezeblocks(output, nblocks, state)
outlen -= nblocks * SHAKE256_RATE
let idx = nblocks * SHAKE256_RATE
shake256_squeeze(output[idx.reinterpret_as_int():], outlen, state)
}
///|这里主要定义了一些外部api,同时为将来支持aes做准备
///|
/// Initializes a SHAKE128 stream for Dilithium cryptographic operations.
///
/// Parameters:
///
/// * `state` : The Keccak state to be initialized for streaming operations.
/// * `seed` : The seed bytes used for initialization (expected to be SEEDBYTES
/// length).
/// * `nonce` : A 16-bit nonce value to ensure unique stream initialization.
///
/// Example:
///
/// ```moonbit
/// let state = @ruifeng/moondsa.KeccakState::default()
/// let seed: Array[Byte] = Array::make(32, 0) // SEEDBYTES length
/// let nonce = (42: UInt16)
/// @ruifeng/moondsa.stream128_init(state, seed, nonce)
/// ```
///
pub fn stream128_init(
state : KeccakState,
seed : Array[Byte],
nonce : UInt16,
) -> Unit {
dilithium_shake128_stream_init(state, seed, nonce)
}
///|
/// Squeezes multiple full blocks of output data from a SHAKE128 state.
///
/// Parameters:
///
/// * `out` : The output buffer where the squeezed data will be written.
/// * `outblocks` : The number of full blocks to squeeze from the state.
/// * `state` : The SHAKE128 state to squeeze data from.
///
pub fn stream128_squeezeblocks(
out : ArrayView[Byte],
outblocks : UInt64,
state : KeccakState,
) -> Unit {
shake128_squeezeblocks(out, outblocks.to_uint(), state)
}
///|
/// Initializes a SHAKE128 stream for the CRYSTALS-Dilithium post-quantum
/// cryptographic scheme.
///
/// Parameters:
///
/// * `state` : The Keccak state to be initialized for streaming.
/// * `seed` : A byte array containing the seed data, expected to be of length
/// `SEEDBYTES`.
/// * `nonce` : A 16-bit unsigned integer used as a unique identifier for this
/// stream instance.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// let seed: Array[Byte] = Array::make(SEEDBYTES, 0) // Create seed of appropriate size
/// @ruifeng/moondsa.dilithium_shake128_stream_init(state, seed, (42: UInt16))
/// ```
///
pub fn dilithium_shake128_stream_init(
state : KeccakState,
seed : Array[Byte],
nonce : UInt16,
) -> Unit {
let t = [nonce.to_byte(), (nonce >> 8).to_byte()]
state.init()
shake128_absorb(state, seed, SEEDBYTES.reinterpret_as_uint())
shake128_absorb(state, t, 2)
shake128_finalize(state)
}
///|
/// Initializes a SHAKE256 stream state for cryptographic operations with a seed
/// and nonce.
///
/// Parameters:
///
/// * `state` : The KeccakState object to be initialized for SHAKE256 streaming.
/// * `seed` : The cryptographic seed bytes used to initialize the stream state.
/// * `nonce` : A 16-bit nonce value to provide uniqueness for each stream
/// initialization.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// let seed : Array[Byte] = Array::make(CRHBYTES, b'A') // 64-byte seed
/// stream256_init(state, seed, (42: UInt16))
/// ```
///
pub fn stream256_init(
state : KeccakState,
seed : Array[Byte],
nonce : UInt16,
) -> Unit {
dilithium_shake256_stream_init(state, seed, nonce)
}
///|
/// Extracts multiple full blocks of output from a SHAKE256 stream state.
///
/// Parameters:
///
/// * `out` : The output array where the extracted bytes will be stored.
/// * `outblocks` : The number of blocks to extract from the stream.
/// * `state` : The SHAKE256 stream state to extract blocks from.
pub fn stream256_squeezeblocks(
out : Array[Byte],
outblocks : UInt64,
state : KeccakState,
) -> Unit {
shake256_squeezeblocks(out, outblocks.to_uint(), state)
}
///|
/// Initializes a SHAKE256 stream for Dilithium cryptographic operations with a
/// seed and nonce.
///
/// Parameters:
///
/// * `state` : The Keccak state to initialize for SHAKE256 streaming.
/// * `seed` : The cryptographic seed bytes used for initialization.
/// * `nonce` : A 16-bit nonce value to ensure uniqueness of the stream.
///
/// Example:
///
/// ```moonbit
/// let state = KeccakState::default()
/// let seed : Array[Byte] = Array::make(CRHBYTES, 0) // 64-byte seed
/// let nonce = (42: UInt16)
/// dilithium_shake256_stream_init(state, seed, nonce)
/// ```
///
pub fn dilithium_shake256_stream_init(
state : KeccakState,
seed : Array[Byte],
nonce : UInt16,
) -> Unit {
let t = [nonce.to_byte(), (nonce >> 8).to_byte()]
state.init()
shake256_absorb(state, seed, CRHBYTES.reinterpret_as_uint())
shake256_absorb(state, t, 2)
shake256_finalize(state)
}