quasor 6.1.6

A high-security AEAD based on a Duplex Sponge construction with SHAKE256, Argon2id, and BLAKE3.
Documentation
// src/sponge.rs

use crate::keccak::keccak_f1600;
use zeroize::Zeroize;

const RATE: usize = 136;

#[derive(Clone)]
pub struct Sponge {
    state: [u64; 25],
    offset: usize,
    squeezing: bool,
}

impl Sponge {
    pub fn new() -> Self {
        Self {
            state: [0; 25],
            offset: 0,
            squeezing: false,
        }
    }

    pub fn absorb(&mut self, input: &[u8]) {
        // If we were squeezing, we must reset to absorbing mode.
        if self.squeezing {
            self.squeezing = false;
        }

        for &byte in input {
            if self.offset >= RATE {
                keccak_f1600(&mut self.state);
                self.offset = 0;
            }
            let state_idx = self.offset / 8;
            let byte_shift = (self.offset % 8) * 8;
            self.state[state_idx] ^= (byte as u64) << byte_shift;
            self.offset += 1;
        }
    }

    pub fn squeeze(&mut self, out_len: usize) -> Vec<u8> {
        if !self.squeezing {
            // *** THE FIX: Apply the correct padding before the first squeeze. ***
            // This is the "finalizing" step of the absorption phase.
            let state_idx = self.offset / 8;
            let byte_shift = (self.offset % 8) * 8;
            // Use the original Keccak 0x01 padding delimiter.
            self.state[state_idx] ^= (0x01 as u64) << byte_shift;

            // Set the high bit of the last byte of the rate.
            let last_byte_idx = (RATE - 1) / 8;
            let last_byte_shift = ((RATE - 1) % 8) * 8;
            self.state[last_byte_idx] ^= (0x80 as u64) << last_byte_shift;

            keccak_f1600(&mut self.state);
            self.offset = 0;
            self.squeezing = true;
        }

        let mut out = vec![0u8; out_len];
        for i in 0..out_len {
            if self.offset >= RATE {
                keccak_f1600(&mut self.state);
                self.offset = 0;
            }
            let state_idx = self.offset / 8;
            let byte_shift = (self.offset % 8) * 8;
            out[i] = (self.state[state_idx] >> byte_shift) as u8;
            self.offset += 1;
        }
        out
    }

    pub fn fork(&self) -> Self {
        self.clone()
    }
}

impl Zeroize for Sponge {
    fn zeroize(&mut self) {
        self.state.zeroize();
        self.offset = 0;
        self.squeezing = false;
    }
}