Skip to content

Commit

Permalink
Merge pull request #187 from zcash/poseidon-fq
Browse files Browse the repository at this point in the history
primitives::poseidon: Add constants for Fq field modulus.
  • Loading branch information
ebfull authored Oct 15, 2021
2 parents 2c8241f + c61524e commit 4f9c0be
Show file tree
Hide file tree
Showing 9 changed files with 3,006 additions and 824 deletions.
4 changes: 2 additions & 2 deletions benches/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::array;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use ff::Field;
use orchard::primitives::{
poseidon::{self, ConstantLength, OrchardNullifier},
poseidon::{self, ConstantLength, P128Pow5T3},
sinsemilla,
};

Expand All @@ -21,7 +21,7 @@ fn bench_primitives(c: &mut Criterion) {
let message = [pallas::Base::random(rng), pallas::Base::random(rng)];

group.bench_function("2-to-1", |b| {
b.iter(|| poseidon::Hash::init(OrchardNullifier, ConstantLength).hash(message))
b.iter(|| poseidon::Hash::init(P128Pow5T3, ConstantLength).hash(message))
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// Configuration for the Poseidon hash.
let poseidon_config = PoseidonChip::configure(
meta,
poseidon::OrchardNullifier,
poseidon::P128Pow5T3,
// We place the state columns after the partial_sbox column so that the
// pad-and-add region can be layed out more efficiently.
advices[6..9].try_into().unwrap(),
Expand Down Expand Up @@ -499,7 +499,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|| value.ok_or(plonk::Error::SynthesisError),
)?;
region.constrain_equal(var, message[i].cell())?;
Ok(Word::<_, _, poseidon::OrchardNullifier, 3, 2>::from_inner(
Ok(Word::<_, _, poseidon::P128Pow5T3, 3, 2>::from_inner(
StateWord::new(var, value),
))
};
Expand Down
4 changes: 2 additions & 2 deletions src/circuit/gadget/poseidon/pow5t3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ mod tests {
use super::{PoseidonInstructions, Pow5T3Chip, Pow5T3Config, StateWord, WIDTH};
use crate::{
circuit::gadget::poseidon::{Hash, Word},
primitives::poseidon::{self, ConstantLength, OrchardNullifier, Spec},
primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec},
};

struct PermuteCircuit {}
Expand Down Expand Up @@ -833,7 +833,7 @@ mod tests {

#[test]
fn hash_test_vectors() {
for tv in crate::primitives::poseidon::test_vectors::hash() {
for tv in crate::primitives::poseidon::test_vectors::fp::hash() {
let message = [
pallas::Base::from_repr(tv.input[0]).unwrap(),
pallas::Base::from_repr(tv.input[1]).unwrap(),
Expand Down
8 changes: 5 additions & 3 deletions src/primitives/poseidon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ use std::marker::PhantomData;

use halo2::arithmetic::FieldExt;

pub(crate) mod fp;
pub(crate) mod fq;
pub(crate) mod grain;
pub(crate) mod mds;

#[cfg(test)]
pub(crate) mod test_vectors;

mod nullifier;
pub use nullifier::OrchardNullifier;
mod p128pow5t3;
pub use p128pow5t3::P128Pow5T3;

use grain::SboxType;

Expand Down Expand Up @@ -363,7 +365,7 @@ mod tests {
use halo2::arithmetic::FieldExt;
use pasta_curves::pallas;

use super::{permute, ConstantLength, Hash, OrchardNullifier, Spec};
use super::{permute, ConstantLength, Hash, P128Pow5T3 as OrchardNullifier, Spec};

#[test]
fn orchard_spec_equivalence() {
Expand Down
205 changes: 6 additions & 199 deletions src/primitives/poseidon/nullifier.rs → src/primitives/poseidon/fp.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,11 @@
use halo2::arithmetic::Field;
//! https://github.com/daira/pasta-hadeshash
//!
//! $ sage generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
use pasta_curves::pallas;

use super::{Mds, Spec};

/// Poseidon-128 using the $x^5$ S-box, with a width of 3 field elements, and the
/// standard number of rounds for 128-bit security "with margin".
///
/// The standard specification for this set of parameters uses $R_F = 8, R_P = 56$.
/// This is conveniently an even number of partial rounds, making it easier to
/// construct a Halo 2 circuit.
#[derive(Debug)]
pub struct OrchardNullifier;

impl Spec<pallas::Base, 3, 2> for OrchardNullifier {
fn full_rounds() -> usize {
8
}

fn partial_rounds() -> usize {
56
}

fn sbox(val: pallas::Base) -> pallas::Base {
val.pow_vartime(&[5])
}

fn secure_mds(&self) -> usize {
unimplemented!()
}

fn constants(
&self,
) -> (
Vec<[pallas::Base; 3]>,
Mds<pallas::Base, 3>,
Mds<pallas::Base, 3>,
) {
(ROUND_CONSTANTS[..].to_vec(), MDS, MDS_INV)
}
}

// $ sage generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
// Number of round constants: 192
// Round constants for GF(p):
const ROUND_CONSTANTS: [[pallas::Base; 3]; 64] = [
pub(crate) const ROUND_CONSTANTS: [[pallas::Base; 3]; 64] = [
[
pallas::Base::from_raw([
0x5753_8c25_9642_6303,
Expand Down Expand Up @@ -1337,7 +1299,7 @@ const ROUND_CONSTANTS: [[pallas::Base; 3]; 64] = [
// [True, None]
// Prime number: 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
// MDS matrix:
const MDS: [[pallas::Base; 3]; 3] = [
pub(crate) const MDS: [[pallas::Base; 3]; 3] = [
[
pallas::Base::from_raw([
0x323f_2486_d7e1_1b63,
Expand Down Expand Up @@ -1400,7 +1362,7 @@ const MDS: [[pallas::Base; 3]; 3] = [
],
];

const MDS_INV: [[pallas::Base; 3]; 3] = [
pub(crate) const MDS_INV: [[pallas::Base; 3]; 3] = [
[
pallas::Base::from_raw([
0xc6de_463c_d140_4e6b,
Expand Down Expand Up @@ -1462,158 +1424,3 @@ const MDS_INV: [[pallas::Base; 3]; 3] = [
]),
],
];

#[cfg(test)]
mod tests {
use std::marker::PhantomData;

use ff::PrimeField;
use halo2::arithmetic::FieldExt;
use pasta_curves::pallas;

use crate::primitives::poseidon::{permute, ConstantLength, Hash, Spec};

use super::{OrchardNullifier, MDS, MDS_INV, ROUND_CONSTANTS};

/// The same Poseidon specification as poseidon::OrchardNullifier, but constructed
/// such that its constants will be generated at runtime.
#[derive(Debug)]
pub struct P128Pow5T3<F: FieldExt> {
secure_mds: usize,
_field: PhantomData<F>,
}

impl<F: FieldExt> P128Pow5T3<F> {
pub fn new(secure_mds: usize) -> Self {
P128Pow5T3 {
secure_mds,
_field: PhantomData::default(),
}
}
}

impl<F: FieldExt> Spec<F, 3, 2> for P128Pow5T3<F> {
fn full_rounds() -> usize {
8
}

fn partial_rounds() -> usize {
56
}

fn sbox(val: F) -> F {
val.pow_vartime(&[5])
}

fn secure_mds(&self) -> usize {
self.secure_mds
}
}

#[test]
fn verify_constants() {
let poseidon = P128Pow5T3::<pallas::Base>::new(0);
let (round_constants, mds, mds_inv) = poseidon.constants();

for (actual, expected) in round_constants
.iter()
.flatten()
.zip(ROUND_CONSTANTS.iter().flatten())
{
assert_eq!(actual, expected);
}

for (actual, expected) in mds.iter().flatten().zip(MDS.iter().flatten()) {
assert_eq!(actual, expected);
}

for (actual, expected) in mds_inv.iter().flatten().zip(MDS_INV.iter().flatten()) {
assert_eq!(actual, expected);
}
}

#[test]
fn test_against_reference() {
// This is the test vector output by the reference code at
// <https://extgit.iaik.tugraz.at/krypto/hadeshash>, using parameters from
// `generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001`.

let mut input = [
pallas::Base::from_raw([
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
]),
pallas::Base::from_raw([
0x0000_0000_0000_0001,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
]),
pallas::Base::from_raw([
0x0000_0000_0000_0002,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
]),
];

let expected_output = [
pallas::Base::from_raw([
0xaeb1_bc02_4aec_a456,
0xf7e6_9a71_d0b6_42a0,
0x94ef_b364_f966_240f,
0x2a52_6acd_0b64_b453,
]),
pallas::Base::from_raw([
0x012a_3e96_28e5_b82a,
0xdcd4_2e7f_bed9_dafe,
0x76ff_7dae_343d_5512,
0x13c5_d156_8b4a_a430,
]),
pallas::Base::from_raw([
0x3590_29a1_d34e_9ddd,
0xf7cf_dfe1_bda4_2c7b,
0x256f_cd59_7984_561a,
0x0a49_c868_c697_6544,
]),
];

permute::<pallas::Base, P128Pow5T3<pallas::Base>, 3, 2>(&mut input, &MDS, &ROUND_CONSTANTS);
assert_eq!(input, expected_output);
}

#[test]
fn permute_test_vectors() {
let (round_constants, mds, _) = OrchardNullifier.constants();

for tv in crate::primitives::poseidon::test_vectors::permute() {
let mut state = [
pallas::Base::from_repr(tv.initial_state[0]).unwrap(),
pallas::Base::from_repr(tv.initial_state[1]).unwrap(),
pallas::Base::from_repr(tv.initial_state[2]).unwrap(),
];

permute::<pallas::Base, OrchardNullifier, 3, 2>(&mut state, &mds, &round_constants);

for (expected, actual) in tv.final_state.iter().zip(state.iter()) {
assert_eq!(&actual.to_repr(), expected);
}
}
}

#[test]
fn hash_test_vectors() {
for tv in crate::primitives::poseidon::test_vectors::hash() {
let message = [
pallas::Base::from_repr(tv.input[0]).unwrap(),
pallas::Base::from_repr(tv.input[1]).unwrap(),
];

let result = Hash::init(OrchardNullifier, ConstantLength).hash(message);

assert_eq!(result.to_repr(), tv.output);
}
}
}
Loading

0 comments on commit 4f9c0be

Please sign in to comment.