Skip to content

Instantly share code, notes, and snippets.

@oconnor663
Created April 30, 2025 01:42
Show Gist options
  • Save oconnor663/e3e90d4578f62f439f8146e8ba38eca5 to your computer and use it in GitHub Desktop.
Save oconnor663/e3e90d4578f62f439f8146e8ba38eca5 to your computer and use it in GitHub Desktop.
POC of absorbing, squeezing, and ratcheting with BLAKE3
use blake3::{Hasher, OutputReader};
pub enum RatchetingHasher {
Absorbing(Hasher),
Squeezing(OutputReader),
}
use RatchetingHasher::{Absorbing, Squeezing};
impl RatchetingHasher {
pub fn new(key: &[u8; 32]) -> Self {
Absorbing(Hasher::new_keyed(key))
}
fn get_hasher(&mut self) -> &mut Hasher {
match self {
// already in the absorbing state
Absorbing(hasher) => hasher,
// in the squeezing state, switch to absorbing
Squeezing(reader) => {
let mut key = [0; 32];
reader.fill(&mut key);
*self = Absorbing(Hasher::new_keyed(&key));
match self {
Absorbing(hasher) => hasher,
_ => unreachable!(),
}
}
}
}
fn get_reader(&mut self) -> &mut OutputReader {
match self {
// in the absorbing state, switch to squeezing
Absorbing(hasher) => {
*self = Squeezing(hasher.finalize_xof());
match self {
Squeezing(reader) => reader,
_ => unreachable!(),
}
}
// already in the squeezing state
Squeezing(reader) => reader,
}
}
pub fn absorb(&mut self, input: &[u8]) {
self.get_hasher().update(input);
}
pub fn squeeze(&mut self, output: &mut [u8]) {
self.get_reader().fill(output);
}
pub fn ratchet(&mut self) {
// Force a state switch.
match self {
Absorbing(_) => {
_ = self.get_reader();
}
Squeezing(_) => {
_ = self.get_hasher();
}
}
}
}
@oconnor663
Copy link
Author

Note to self: OutputReader often contains the keyed hash key, so self.get_reader() isn't sufficient to force "forgetting" the key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment