Skip to content

Commit

Permalink
Change Sponge API.
Browse files Browse the repository at this point in the history
Instead of requiring RATE and CAPACITY, we now
require the full state size of the state and the rate.
This allows people to put wrappers around [U; N]
whereas [U; C+R] is still not yet supported by rust.
  • Loading branch information
mmaker committed Feb 2, 2024
1 parent 7edf036 commit b55531e
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 81 deletions.
29 changes: 0 additions & 29 deletions src/hash/index.rs

This file was deleted.

27 changes: 16 additions & 11 deletions src/hash/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ pub struct AlignedKeccakState([u8; 200]);

impl Sponge for AlignedKeccakState {
type U = u8;
const CAPACITY: usize = 64;
const RATE: usize = 136;
const N: usize = 136 + 64;
const R: usize = 136;

fn new(tag: [u8; 32]) -> Self {
let mut state = Self::default();
state[Self::RATE..Self::RATE + 32].copy_from_slice(&tag);
state.0[Self::R..Self::R + 32].copy_from_slice(&tag);
state
}

Expand All @@ -38,14 +38,19 @@ impl Sponge for AlignedKeccakState {

impl Default for AlignedKeccakState {
fn default() -> Self {
Self([0u8; Self::CAPACITY + Self::RATE])
Self([0u8; Self::N])
}
}

super::index::impl_indexing!(
AlignedKeccakState,
0,
Output = u8,
Params = [],
Constants = []
);

impl AsRef<[u8]> for AlignedKeccakState {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl AsMut<[u8]> for AlignedKeccakState {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
4 changes: 1 addition & 3 deletions src/hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
//! the basic type to support cloning, size, read/write procedures, and secure deletion.
//!
//! Additionally, the module exports some utilities:
//! - [`hash::sponge::DuplexSponge`] allows to implement a [`crate::DuplexHash`] using a secure permutation function, specifying `RATE` and `CAPACITY`.
//! - [`hash::sponge::DuplexSponge`] allows to implement a [`crate::DuplexHash`] using a secure permutation function, specifying the rate `R` and the width `N`.
//! This is done using the standard duplex sponge cosntruction in overwrite mode (cf. [Wikipedia](https://en.wikipedia.org/wiki/Sponge_function#Duplex_construction)).
//! - [`hash::legacy::DigestBridge`] takes as input any hash function implementing the NIST API via the standard [`digest::Digest`] trait and makes it suitable for usage in duplex mode for continuous absorb/squeeze.
/// Automate implementation of Index and IndexMut.
pub(crate) mod index;
/// A wrapper around the Keccak-f\[1600\] permutation.
pub mod keccak;
/// Legacy hash functions support (e.g. [`sha2`](https://crates.io/crates/sha2), [`blake2`](https://crates.io/crates/blake2)).
Expand Down
66 changes: 34 additions & 32 deletions src/hash/sponge.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
use super::{DuplexHash, Unit};

use core::ops::{Index, IndexMut, Range};
use std::ops::{RangeFrom, RangeTo};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// The basic state of a cryptographic sponge.
///
/// A cryptographic sponge operates over some domain `SpongeConfig::U` units.
/// It has a capacity `CAPACITY` and a rate `RATE`,
/// and it permutes its internal state using `SpongeConfig::permute()`.
/// A cryptographic sponge operates over some domain [`Sponge::U`] units.
/// It has a width [`Sponge::N`] and can process elements at rate [`Sponge::R`],
/// using the permutation function [`Sponge::permute`].
///
/// For implementors:
///
/// - State is written in *the first* `Self::RATE` bytes of the state.
/// The last [`Self::CAPACITY`] bytes are never touched directly.
/// - State is written in *the first* [`Sponge::R`] (rate) bytes of the state.
/// The last [`Sponge::N`]-[`Sponge::R`] bytes are never touched directly except during initialization.
/// - The duplex sponge is in *overwrite mode*.
/// This mode is not known to affect the security levels and removes assumptions on [`Self::U`]
/// This mode is not known to affect the security levels and removes assumptions on [`Sponge::U`]
/// as well as constraints in the final zero-knowledge proof implementing the hash function.
/// - The `std::default::Default` implementation should initialize the states to zero.
/// - The `new(iv)` method should initialize the sponge using the entropy provided in the `iv` in the last
/// `Self::CAPACITY` elements of the state.
/// - The [`std::default::Default`] implementation should initialize the states to zero.
/// - The [`Sponge::new`] method should initialize the sponge writing the entropy provided in the `iv` in the last
/// [`Sponge::N`]-[`Sponge::R`] elements of the state.
pub trait Sponge:
Zeroize
+ Default
+ Clone
+ Index<usize, Output = Self::U>
+ Index<RangeFrom<usize>, Output = [Self::U]>
+ Index<RangeTo<usize>, Output = [Self::U]>
+ Index<Range<usize>, Output = [Self::U]>
+ IndexMut<RangeFrom<usize>, Output = [Self::U]>
+ IndexMut<RangeTo<usize>, Output = [Self::U]>
+ IndexMut<Range<usize>, Output = [Self::U]>
+ AsRef<[Self::U]>
+ AsMut<[Self::U]>
{
/// The basic unit over which the sponge operates.
type U: Unit;
const CAPACITY: usize;
const RATE: usize;

/// The width of the sponge, equal to rate [`Sponge::R`] plus capacity.
/// Cannot be less than 1. Cannot be less than [`Sponge::R`].
const N: usize;

/// The rate of the sponge.
const R: usize;

/// Initialize the state of the sponge using 32 bytes of seed.
fn new(iv: [u8; 32]) -> Self;

/// Permute the state of the sponge.
fn permute(&mut self);
}

Expand All @@ -53,24 +55,24 @@ impl<U: Unit, C: Sponge<U = U>> DuplexHash<U> for DuplexSponge<C> {
Self {
state: C::new(iv),
absorb_pos: 0,
squeeze_pos: C::RATE,
squeeze_pos: C::R,
}
}

fn absorb_unchecked(&mut self, input: &[U]) -> &mut Self {
if input.is_empty() {
self.squeeze_pos = C::RATE;
self.squeeze_pos = C::R;
self
} else if self.absorb_pos == C::RATE {
} else if self.absorb_pos == C::R {
self.state.permute();
self.absorb_pos = 0;
self.absorb_unchecked(input)
} else {
assert!(!input.is_empty() && self.absorb_pos < C::RATE);
let chunk_len = usize::min(input.len(), C::RATE - self.absorb_pos);
assert!(!input.is_empty() && self.absorb_pos < C::R);
let chunk_len = usize::min(input.len(), C::R - self.absorb_pos);
let (input, rest) = input.split_at(chunk_len);

self.state[self.absorb_pos..self.absorb_pos + chunk_len].clone_from_slice(input);
self.state.as_mut()[self.absorb_pos..self.absorb_pos + chunk_len].clone_from_slice(input);
self.absorb_pos += chunk_len;
self.absorb_unchecked(rest)
}
Expand All @@ -81,16 +83,16 @@ impl<U: Unit, C: Sponge<U = U>> DuplexHash<U> for DuplexSponge<C> {
return self;
}

if self.squeeze_pos == C::RATE {
if self.squeeze_pos == C::R {
self.squeeze_pos = 0;
self.absorb_pos = 0;
self.state.permute();
}

assert!(self.squeeze_pos < C::RATE && !output.is_empty());
let chunk_len = usize::min(output.len(), C::RATE - self.squeeze_pos);
assert!(self.squeeze_pos < C::R && !output.is_empty());
let chunk_len = usize::min(output.len(), C::R - self.squeeze_pos);
let (output, rest) = output.split_at_mut(chunk_len);
output.clone_from_slice(&self.state[self.squeeze_pos..self.squeeze_pos + chunk_len]);
output.clone_from_slice(&self.state.as_ref()[self.squeeze_pos..self.squeeze_pos + chunk_len]);
self.squeeze_pos += chunk_len;
self.squeeze_unchecked(rest)
}
Expand All @@ -103,8 +105,8 @@ impl<U: Unit, C: Sponge<U = U>> DuplexHash<U> for DuplexSponge<C> {
self.state.permute();
// set to zero the state up to rate
// XXX. is the compiler really going to do this?
self.state[0..C::RATE].iter_mut().for_each(|x| x.zeroize());
self.squeeze_pos = C::RATE;
self.state.as_mut()[0..C::R].iter_mut().for_each(|x| x.zeroize());
self.squeeze_pos = C::R;
self
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
//!
//! This work is heavily inspired from:
//! - Libsignal's [shosha256], by Trevor Perrin. It provides an absorb/squeeze interface over legacy hash functions.
//! - the [SAFE] API, by Dmitry Khovratovich, JP Aumasson, Porçu Quine, Bart Mennink. To my knowledge they are the first to introduce this idea of using an IO Pattern to build a transcript.
//! - the [SAFE] API, by Dmitry Khovratovich, JP Aumasson, Porçu Quine, Bart Mennink. To my knowledge they are the first to introduce this idea of using an IO Pattern to build a transcript and the SAFE API.
//! - [Merlin], by Henry de Valence. To my knowledge it introduced this idea of a `Transcript` object carrying over the state of the hash function throughout the protocol.
//!
//!
Expand Down
File renamed without changes.
18 changes: 13 additions & 5 deletions src/plugins/ark/poseidon.rs → src/plugins/ark/poseidon/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use ark_ff::PrimeField;

use crate::hash::index::impl_indexing;
use crate::hash::sponge::Sponge;
use crate::hash::Unit;

Expand All @@ -22,8 +21,17 @@ pub struct PoseidonSponge<F: PrimeField, const R: usize, const N: usize> {
pub state: [F; N],
}

// Indexing over PoseidonSponge is just forwarded to indexing on the state.
impl_indexing!(PoseidonSponge, state, Output = F, Params = [F: PrimeField], Constants = [R, N]);
impl<F: PrimeField, const R: usize, const N: usize> AsRef<[F]> for PoseidonSponge<F, R, N> {
fn as_ref(&self) -> &[F] {
&self.state
}
}

impl<F: PrimeField, const R: usize, const N: usize> AsMut<[F]> for PoseidonSponge<F, R, N> {
fn as_mut(&mut self) -> &mut [F] {
&mut self.state
}
}

impl<F: PrimeField, const R: usize, const N: usize> PoseidonSponge<F, R, N> {
fn apply_s_box(&self, state: &mut [F], is_full_round: bool) {
Expand Down Expand Up @@ -71,8 +79,8 @@ where
F: PrimeField + Unit,
{
type U = F;
const CAPACITY: usize = N - R;
const RATE: usize = R;
const N: usize = N;
const R: usize = R;

fn new(iv: [u8; 32]) -> Self {
assert!(N >= 1);
Expand Down

0 comments on commit b55531e

Please sign in to comment.