Simplify poseidon::Spec and remove poseidon::Generic

Poseidon specifications are now all concrete, and only generation of
constants at runtime requires an instance of the specification.
This commit is contained in:
Jack Grigg 2021-03-12 05:33:47 +13:00
parent 266705166f
commit 5c8e9beea7
2 changed files with 56 additions and 72 deletions

View File

@ -14,98 +14,87 @@ use grain::SboxType;
/// A specification for a Poseidon permutation.
pub trait Spec<F: FieldExt> {
/// The arity of this specification.
fn arity(&self) -> usize;
fn arity() -> usize;
/// The number of full rounds for this specification.
fn full_rounds(&self) -> usize;
fn full_rounds() -> usize;
/// The number of partial rounds for this specification.
fn partial_rounds(&self) -> usize;
fn partial_rounds() -> usize;
fn sbox(&self, val: F) -> F;
/// The S-box for this specification.
fn sbox(val: F) -> F;
/// Side-loaded index of the first correct and secure MDS that will be generated by
/// the reference implementation.
///
/// This is used by the default implementation of [`Spec::constants`]. If you are
/// hard-coding the constants, you may leave this unimplemented.
fn secure_mds(&self) -> usize;
/// Generates `(round_constants, mds, mds^-1)` corresponding to this specification.
fn constants(&self) -> (Vec<Vec<F>>, Vec<Vec<F>>, Vec<Vec<F>>);
fn constants(&self) -> (Vec<Vec<F>>, Vec<Vec<F>>, Vec<Vec<F>>) {
let t = Self::arity();
let r_f = Self::full_rounds();
let r_p = Self::partial_rounds();
let mut grain = grain::Grain::new(SboxType::Pow, t as u16, r_f as u16, r_p as u16);
let round_constants = (0..(r_f + r_p))
.map(|_| (0..t).map(|_| grain.next_field_element()).collect())
.collect();
let (mds, mds_inv) = mds::generate_mds(&mut grain, t, self.secure_mds());
(round_constants, mds, mds_inv)
}
}
/// A generic Poseidon specification.
/// Poseidon-256 with arity 3, using the `x^5` S-box.
#[derive(Debug)]
pub struct Generic<F: FieldExt> {
pow_sbox: u64,
/// The arity of the Poseidon permutation.
t: u16,
/// The number of full rounds.
r_f: u16,
/// The number of partial rounds.
r_p: u16,
/// The index of the first secure MDS matrix that will be generated for the given
/// parameters.
pub struct P256Pow5T3<F: FieldExt> {
secure_mds: usize,
_field: PhantomData<F>,
}
impl<F: FieldExt> Generic<F> {
/// Creates a new Poseidon specification for a field, using the `x^\alpha` S-box.
pub fn with_pow_sbox(
pow_sbox: u64,
arity: usize,
full_rounds: usize,
partial_rounds: usize,
secure_mds: usize,
) -> Self {
Generic {
pow_sbox,
t: arity as u16,
r_f: full_rounds as u16,
r_p: partial_rounds as u16,
impl<F: FieldExt> P256Pow5T3<F> {
pub fn new(secure_mds: usize) -> Self {
P256Pow5T3 {
secure_mds,
_field: PhantomData::default(),
}
}
}
impl<F: FieldExt> Spec<F> for Generic<F> {
fn arity(&self) -> usize {
self.t as usize
impl<F: FieldExt> Spec<F> for P256Pow5T3<F> {
fn arity() -> usize {
3
}
fn full_rounds(&self) -> usize {
self.r_f as usize
fn full_rounds() -> usize {
8
}
fn partial_rounds(&self) -> usize {
self.r_p as usize
fn partial_rounds() -> usize {
120
}
fn sbox(&self, val: F) -> F {
val.pow_vartime(&[self.pow_sbox])
fn sbox(val: F) -> F {
val.pow_vartime(&[5])
}
fn constants(&self) -> (Vec<Vec<F>>, Vec<Vec<F>>, Vec<Vec<F>>) {
let mut grain = grain::Grain::new(SboxType::Pow, self.t, self.r_f, self.r_p);
let round_constants = (0..(self.r_f + self.r_p))
.map(|_| (0..self.t).map(|_| grain.next_field_element()).collect())
.collect();
let (mds, mds_inv) = mds::generate_mds(&mut grain, self.t as usize, self.secure_mds);
(round_constants, mds, mds_inv)
fn secure_mds(&self) -> usize {
self.secure_mds
}
}
/// Runs the Poseidon permutation on the given state.
fn permute<F: FieldExt, S: Spec<F>>(
state: &mut [F],
spec: &S,
mds: &[Vec<F>],
round_constants: &[Vec<F>],
) {
fn permute<F: FieldExt, S: Spec<F>>(state: &mut [F], mds: &[Vec<F>], round_constants: &[Vec<F>]) {
// TODO: Remove this when we can use const generics.
assert!(state.len() == spec.arity());
assert!(state.len() == S::arity());
let r_f = spec.full_rounds() / 2;
let r_p = spec.partial_rounds();
let r_f = S::full_rounds() / 2;
let r_p = S::partial_rounds();
let apply_mds = |state: &mut [F]| {
let new_state: Vec<_> = mds
@ -124,7 +113,7 @@ fn permute<F: FieldExt, S: Spec<F>>(
let full_round = |state: &mut [F], rcs: &[F]| {
for (word, rc) in state.iter_mut().zip(rcs.iter()) {
*word = spec.sbox(*word + rc);
*word = S::sbox(*word + rc);
}
apply_mds(state);
};
@ -133,7 +122,7 @@ fn permute<F: FieldExt, S: Spec<F>>(
for (word, rc) in state.iter_mut().zip(rcs.iter()) {
*word += rc;
}
state[0] = spec.sbox(state[0]);
state[0] = S::sbox(state[0]);
apply_mds(state);
};
@ -166,41 +155,36 @@ enum SpongeState<F: FieldExt> {
/// A Poseidon duplex sponge.
pub struct Duplex<F: FieldExt, S: Spec<F>> {
spec: S,
sponge: Option<SpongeState<F>>,
state: Vec<F>,
rate: usize,
mds_matrix: Vec<Vec<F>>,
round_constants: Vec<Vec<F>>,
_marker: PhantomData<S>,
}
impl<F: FieldExt, S: Spec<F>> Duplex<F, S> {
/// Constructs a new duplex sponge with the given rate.
pub fn new(spec: S, rate: usize) -> Self {
assert!(rate < spec.arity());
assert!(rate < S::arity());
let state = vec![F::zero(); spec.arity()];
let state = vec![F::zero(); S::arity()];
let (round_constants, mds_matrix, _) = spec.constants();
Duplex {
spec,
sponge: Some(SpongeState::Absorbing(vec![])),
state,
rate,
mds_matrix,
round_constants,
_marker: PhantomData::default(),
}
}
fn process(&mut self, input: &[F]) -> Vec<F> {
pad_and_add(&mut self.state[..self.rate], input);
permute(
&mut self.state,
&self.spec,
&self.mds_matrix,
&self.round_constants,
);
permute::<F, S>(&mut self.state, &self.mds_matrix, &self.round_constants);
self.state[..self.rate].to_vec()
}

View File

@ -1,7 +1,7 @@
use halo2::arithmetic::FieldExt;
use pasta_curves::pallas;
use super::{Generic, Spec};
use super::{P256Pow5T3, Spec};
// $ sage generate_parameters_grain.sage 1 0 255 3 8 120 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
// Number of round constants: 384
@ -424,7 +424,7 @@ const MDS: [[&str; 3]; 3] = [
#[test]
fn test_vectors() {
let poseidon = Generic::<pallas::Base>::with_pow_sbox(5, 3, 8, 120, 0);
let poseidon = P256Pow5T3::<pallas::Base>::new(0);
let (round_constants, mds, _) = poseidon.constants();
for (actual, expected) in round_constants