use crate::{digest::sha3::{qrc_cshake_initialize, qrc_keccak_absorb, qrc_keccak_dispose, qrc_shake_squeezeblocks, QrcKeccakRate, QrcKeccakState, QRC_KECCAK_PERMUTATION_ROUNDS, QRC_KECCAK_SHAKE_DOMAIN_ID, QRC_KECCAK_STATE_BYTE_SIZE}, tools::intutils::qrc_intutils_copy8};
use core::default::Default;
#[cfg(feature = "no_std")]
use alloc::{vec, vec::Vec};
pub const QRC_SCB_256_HASH_SIZE: usize = 32;
pub const QRC_SCB_512_HASH_SIZE: usize = 64;
pub const QRC_SCB_256_SEED_SIZE: usize = 32;
pub const QRC_SCB_512_SEED_SIZE: usize = 64;
pub const QRC_SCB_CACHE_MINIMUM: usize = 200;
pub const QRC_SCB_CACHE_MULTIPLIER: usize = 1000000;
#[derive(PartialEq)]
pub struct QrcScbState {
pub kstate: QrcKeccakState,
pub cache: Vec<u8>,
pub clen: usize,
pub cpuc: usize,
pub memc: usize,
pub rate: QrcKeccakRate,
}
impl Default for QrcScbState {
fn default() -> Self {
Self {
kstate: QrcKeccakState::default(),
cache: Default::default(),
clen: Default::default(),
cpuc: Default::default(),
memc: Default::default(),
rate: QrcKeccakRate::QrcKeccakRateNone
}
}
}
pub fn qrc_scb_dispose(ctx: &mut QrcScbState) {
qrc_keccak_dispose(&mut ctx.kstate);
ctx.cache = vec![];
ctx.clen = 0;
ctx.cpuc = 0;
ctx.memc = 0;
ctx.rate = QrcKeccakRate::QrcKeccakRateNone;
}
pub fn qrc_scb_initialize(ctx: &mut QrcScbState, seed: &[u8], seedlen: usize, info: &[u8], infolen: usize, cpucost: usize, memcost: usize) {
ctx.clen = QRC_SCB_CACHE_MINIMUM;
ctx.cache = vec![0u8; QRC_SCB_CACHE_MINIMUM];
ctx.cpuc = cpucost;
ctx.memc = memcost;
if seedlen >= QRC_SCB_512_SEED_SIZE {
ctx.rate = QrcKeccakRate::QrcKeccakRate512;
} else {
ctx.rate = QrcKeccakRate::QrcKeccakRate256;
}
qrc_cshake_initialize(&mut ctx.kstate, ctx.rate as usize, seed, seedlen, SCB_NAME.as_bytes(), QRC_SCB_NAME_SIZE, info, infolen);
}
pub fn qrc_scb_generate(ctx: &mut QrcScbState, output: &mut [u8], outlen: usize){
scb_expand(ctx);
scb_extract(ctx, output, outlen, true);
}
const QRC_SCB_NAME_SIZE: usize = 8;
const SCB_NAME: &str = "SCB v1.a";
fn scb_extract(ctx: &mut QrcScbState, output: &mut [u8], outlen: usize, check: bool) {
if outlen > 0 {
let ctxrate = ctx.rate as usize;
let blkcnt = outlen / ctxrate;
if check {
qrc_shake_squeezeblocks(&mut ctx.kstate, ctxrate, output, blkcnt);
} else {
qrc_shake_squeezeblocks(&mut ctx.kstate, ctxrate, &mut ctx.cache, blkcnt);
}
if ctxrate * blkcnt < outlen {
let tmpb = &mut [0u8; QRC_KECCAK_STATE_BYTE_SIZE];
let fnlblk = outlen - (ctxrate * blkcnt);
qrc_shake_squeezeblocks(&mut ctx.kstate, ctxrate, tmpb, 1);
if check {
qrc_intutils_copy8(&mut output[(ctxrate * blkcnt)..], tmpb, fnlblk);
} else {
qrc_intutils_copy8(&mut ctx.cache[(ctxrate * blkcnt)..], tmpb, fnlblk);
}
}
}
}
fn scb_expand(ctx: &mut QrcScbState) {
for _ in 0..ctx.cpuc {
let clen = ctx.clen;
let temp = &mut vec![];
scb_extract(ctx, temp, clen, false);
qrc_keccak_absorb(&mut ctx.kstate, ctx.rate as usize, &ctx.cache, ctx.clen, QRC_KECCAK_SHAKE_DOMAIN_ID, QRC_KECCAK_PERMUTATION_ROUNDS);
if ctx.clen < ctx.memc * QRC_SCB_CACHE_MULTIPLIER {
let alclen = (ctx.memc * QRC_SCB_CACHE_MULTIPLIER) / ctx.cpuc;
ctx.cache.extend_from_slice(&vec![0u8; alclen]);
ctx.clen += alclen;
}
}
}