Skip to content

Commit

Permalink
Remove the hades constants iterator
Browse files Browse the repository at this point in the history
This change also decreases the amount of constants that are statically
loaded from 960 to 335. As it turns out, we never needed so many
constants.
Also rename `add_round_key` to `add_round_constants` to match the paper.
  • Loading branch information
moCello committed Jan 25, 2024
1 parent 537eab9 commit c0aa604
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 119 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Rename trait `hades::Strategy` to `hades::Permutation` [#243]
- Rename struct `hades::ScalarStrategy` to `hades::ScalarPermutation` [#243]
- Rename struct `hades::GadgetStrategy` to `hades::GadgetPermutaiton` [#243]
- Reduce the number of `ROUND_CONSTANTS` from 960 to 335 [#246]
- Remove the constants iterator in favor of indexing the constants array directly [#246]

### Removed

Expand Down
2 changes: 1 addition & 1 deletion assets/HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::io::Write;

// The amount of constants generated, this needs to be the same number as in
// `dusk_poseidon::hades::CONSTANTS`.
const CONSTANTS: usize = 960;
const CONSTANTS: usize = 335;

fn constants() -> [BlsScalar; CONSTANTS] {
let mut cnst = [BlsScalar::zero(); CONSTANTS];
Expand Down
Binary file modified assets/ark.bin
Binary file not shown.
7 changes: 4 additions & 3 deletions src/hades.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
//! end, and each full round has `WIDTH` quintic S-Boxes.
//! - 59 partial rounds: each partial round has `WIDTH - 1` identity function
//! and one quintic S-Box.
//! - 960 round constants
//! - Round constants are generated using [this algorithm](https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py)
//! - 335 round constants
//! - The round constants are generated using [this algorithm](https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py)
//! - The MDS matrix is a cauchy matrix, the method used to generate it, is
//! noted in section "Concrete Instantiations Poseidon and Starkad"
Expand All @@ -32,7 +32,8 @@ const TOTAL_FULL_ROUNDS: usize = 8;

const PARTIAL_ROUNDS: usize = 59;

const CONSTANTS: usize = 960;
// (PARTIAL_ROUNDS + TOTAL_FULL_ROUNDS) * WIDTH
const CONSTANTS: usize = 335;

/// The amount of field elements that fit into the hades permutation container
pub const WIDTH: usize = 5;
Expand Down
101 changes: 40 additions & 61 deletions src/hades/permutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use dusk_bls12_381::BlsScalar;
#[cfg(feature = "zk")]
use dusk_plonk::prelude::{Composer, Witness};

use crate::hades::{PARTIAL_ROUNDS, ROUND_CONSTANTS, TOTAL_FULL_ROUNDS, WIDTH};
use crate::hades::{PARTIAL_ROUNDS, TOTAL_FULL_ROUNDS, WIDTH};

/// State for zero-knowledge plonk circuits
#[cfg(feature = "zk")]
Expand All @@ -39,7 +39,7 @@ use scalar::ScalarPermutation;
/// - Applies the other half of the `FULL_ROUNDS`.
///
/// This structure allows to minimize the number of non-linear ops while
/// mantaining the security.
/// maintaining the security.
pub(crate) fn permute(state: &mut [BlsScalar; WIDTH]) {
let mut hades = ScalarPermutation::new();

Expand All @@ -50,11 +50,11 @@ pub(crate) fn permute(state: &mut [BlsScalar; WIDTH]) {
///
/// This permutation is a 3-step process that:
/// - Applies half of the `FULL_ROUNDS` (which can be understood as linear ops).
/// - Applies the `PARTIAL_ROUDS` (which can be understood as non-linear ops).
/// - Applies the `PARTIAL_ROUNDS` (which can be understood as non-linear ops).
/// - Applies the other half of the `FULL_ROUNDS`.
///
/// This structure allows to minimize the number of non-linear ops while
/// mantaining the security.
/// maintaining the security.
#[cfg(feature = "zk")]
pub(crate) fn permute_gadget(
composer: &mut Composer,
Expand All @@ -67,30 +67,19 @@ pub(crate) fn permute_gadget(

/// Defines the Hades252 permutation algorithm.
pub(crate) trait Permutation<T> {
/// Fetch the next round constant from an iterator
fn next_c<'b, I>(constants: &mut I) -> BlsScalar
where
I: Iterator<Item = &'b BlsScalar>,
{
constants
.next()
.copied()
.expect("Hades252 shouldn't be out of ARK constants")
}
/// Increment the inner rounds counter.
///
/// This counter is needed to index the `ROUND_CONSTANTS`.
fn increment_round(&mut self);

/// Add round keys to the state.
/// Add round constants to the state.
///
/// This round key addition, also known as `ARK`, is used to reach
/// This constants addition, also known as `ARC`, is used to reach
/// `Confusion and Diffusion` properties for the algorithm.
///
/// Basically it allows to destroy any connection between the inputs and the
/// outputs of the function.
fn add_round_key<'b, I>(
&mut self,
constants: &mut I,
state: &mut [T; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>;
fn add_round_constants(&mut self, state: &mut [T; WIDTH]);

/// Computes `input ^ 5 (mod p)`
///
Expand All @@ -100,89 +89,79 @@ pub(crate) trait Permutation<T> {
fn quintic_s_box(&mut self, value: &mut T);

/// Multiply the MDS matrix with the state.
fn mul_matrix<'b, I>(&mut self, constants: &mut I, state: &mut [T; WIDTH])
where
I: Iterator<Item = &'b BlsScalar>;
fn mul_matrix(&mut self, state: &mut [T; WIDTH]);

/// Applies a `Partial Round` also known as a `Partial S-Box layer` to a set
/// of inputs.
///
/// One partial round consists of 3 steps:
/// - ARK: Add round keys constants to each state element.
/// - Sub State: Apply `quintic S-Box` just to **the last element of the
/// - ARC: Add round constants to the elements of the state.
/// - Sub Words: Apply `quintic S-Box` just to **the last element of the
/// state** generated from the first step.
/// - Mix Layer: Multiplies the output state from the second step by the
/// `MDS_MATRIX`.
fn apply_partial_round<'b, I>(
&mut self,
constants: &mut I,
state: &mut [T; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>,
{
// Add round keys to each state element
self.add_round_key(constants, state);

// Then apply quintic s-box
fn apply_partial_round(&mut self, state: &mut [T; WIDTH]) {
// Increment the inner rounds counter
self.increment_round();

// Add round constants to each state element
self.add_round_constants(state);

// Then apply quintic s-box to the last element of the state
self.quintic_s_box(&mut state[WIDTH - 1]);

// Multiply this result by the MDS matrix
self.mul_matrix(constants, state);
self.mul_matrix(state);
}

/// Applies a `Full Round` also known as a `Full S-Box layer` to a set of
/// inputs.
///
/// One full round constists of 3 steps:
/// - ARK: Add round keys to each state element.
/// - Sub State: Apply `quintic S-Box` to **all of the state-elements**
/// One full round consists of 3 steps:
/// - ARC: Add round constants to the elements of the state.
/// - Sub Words: Apply `quintic S-Box` to **all of the state-elements**
/// generated from the first step.
/// - Mix Layer: Multiplies the output state from the second step by the
/// `MDS_MATRIX`.
fn apply_full_round<'a, I>(
&mut self,
constants: &mut I,
state: &mut [T; WIDTH],
) where
I: Iterator<Item = &'a BlsScalar>,
{
// Add round keys to each state element
self.add_round_key(constants, state);

// Then apply quintic s-box
fn apply_full_round(&mut self, state: &mut [T; WIDTH]) {
// Increment the inner rounds counter
self.increment_round();

// Add round constants to each state element
self.add_round_constants(state);

// Then apply quintic s-box to each element of the state
state.iter_mut().for_each(|w| self.quintic_s_box(w));

// Multiply this result by the MDS matrix
self.mul_matrix(constants, state);
self.mul_matrix(state);
}

/// Applies one Hades permutation.
///
/// This permutation is a 3-step process that:
/// - Applies half of the `FULL_ROUNDS` (which can be understood as linear
/// ops).
/// - Applies the `PARTIAL_ROUDS` (which can be understood as non-linear
/// - Applies the `PARTIAL_ROUNDS` (which can be understood as non-linear
/// ops).
/// - Applies the other half of the `FULL_ROUNDS`.
///
/// This structure allows to minimize the number of non-linear ops while
/// mantaining the security.
/// maintaining the security.
fn perm(&mut self, state: &mut [T; WIDTH]) {
let mut constants = ROUND_CONSTANTS.iter();

// Apply R_f full rounds
for _ in 0..TOTAL_FULL_ROUNDS / 2 {
self.apply_full_round(&mut constants, state);
self.apply_full_round(state);
}

// Apply R_P partial rounds
for _ in 0..PARTIAL_ROUNDS {
self.apply_partial_round(&mut constants, state);
self.apply_partial_round(state);
}

// Apply R_f full rounds
for _ in 0..TOTAL_FULL_ROUNDS / 2 {
self.apply_full_round(&mut constants, state);
self.apply_full_round(state);
}
}

Expand Down
39 changes: 16 additions & 23 deletions src/hades/permutation/gadget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use dusk_bls12_381::BlsScalar;
use dusk_plonk::prelude::*;

use crate::hades::{Permutation as HadesPermutation, MDS_MATRIX, WIDTH};
use crate::hades::{
Permutation as HadesPermutation, MDS_MATRIX, ROUND_CONSTANTS, WIDTH,
};

/// A state for the ['HadesPermutation`] operating on [`Witness`]es.
/// An implementation for the ['HadesPermutation`] operating on [`Witness`]es.
/// Requires a reference to a `ConstraintSystem`.
pub(crate) struct GadgetPermutaiton<'a> {
/// A reference to the constraint system used by the gadgets
Expand All @@ -31,19 +33,17 @@ impl AsMut<Composer> for GadgetPermutaiton<'_> {
}

impl<'a> HadesPermutation<Witness> for GadgetPermutaiton<'a> {
fn add_round_key<'b, I>(
&mut self,
constants: &mut I,
state: &mut [Witness; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>,
{
fn increment_round(&mut self) {
self.round += 1;
}

fn add_round_constants(&mut self, state: &mut [Witness; WIDTH]) {
// To safe constraints we only add the constants here in the first
// round. The remaining constants will be added in the matrix
// multiplication.
if self.round == 0 {
state.iter_mut().for_each(|w| {
let constant = Self::next_c(constants);
if self.round == 1 {
state.iter_mut().enumerate().for_each(|(i, w)| {
let constant = ROUND_CONSTANTS[i];
let constraint =
Constraint::new().left(1).a(*w).constant(constant);

Expand All @@ -64,22 +64,14 @@ impl<'a> HadesPermutation<Witness> for GadgetPermutaiton<'a> {
}

/// Adds a constraint for each matrix coefficient multiplication
fn mul_matrix<'b, I>(
&mut self,
constants: &mut I,
state: &mut [Witness; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>,
{
fn mul_matrix(&mut self, state: &mut [Witness; WIDTH]) {
let mut result = [Composer::ZERO; WIDTH];
self.round += 1;

// Implementation optimized for WIDTH = 5
//
// c is the next round's constant and hence zero for the last round.
//
// The resulting array `r` will be defined as
// r[x] = sum_{j=0..WIDTH} ( MDS[x][j] * state[j] ) + c
// with c being the constant for the next round.
//
// q_l = MDS[x][0]
// q_r = MDS[x][1]
Expand All @@ -97,8 +89,9 @@ impl<'a> HadesPermutation<Witness> for GadgetPermutaiton<'a> {
// w_4 = r[x]
// r[x] = q_l · w_l + q_r · w_r + q_4 · w_4 + c;
for j in 0..WIDTH {
// c is the next round's constant and hence zero for the last round.
let c = match self.round < Self::rounds() {
true => Self::next_c(constants),
true => ROUND_CONSTANTS[self.round * WIDTH + j],
false => BlsScalar::zero(),
};

Expand Down
43 changes: 22 additions & 21 deletions src/hades/permutation/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,45 @@

use dusk_bls12_381::BlsScalar;

use crate::hades::{Permutation as HadesPermutation, MDS_MATRIX, WIDTH};
use crate::hades::{
Permutation as HadesPermutation, MDS_MATRIX, ROUND_CONSTANTS, WIDTH,
};

/// State that implements the [`HadesPermutation`] for `BlsScalar` as input
/// An implementation of the [`HadesPermutation`] for `BlsScalar` as input
/// values.
#[derive(Default)]
pub(crate) struct ScalarPermutation {}
pub(crate) struct ScalarPermutation {
round: usize,
}

impl ScalarPermutation {
/// Constructs a new `ScalarPermutation`.
pub fn new() -> Self {
Default::default()
Self { round: 0 }
}
}

impl HadesPermutation<BlsScalar> for ScalarPermutation {
fn add_round_key<'b, I>(
&mut self,
constants: &mut I,
state: &mut [BlsScalar; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>,
{
state.iter_mut().for_each(|w| {
*w += Self::next_c(constants);
});
fn increment_round(&mut self) {
self.round += 1;
}

fn add_round_constants(&mut self, state: &mut [BlsScalar; WIDTH]) {
state
.iter_mut()
.enumerate()
// We start counting the rounds at 1 so we need to substract 1 when
// indexing
.for_each(|(i, s)| {
*s += ROUND_CONSTANTS[(self.round - 1) * WIDTH + i]
});
}

fn quintic_s_box(&mut self, value: &mut BlsScalar) {
*value = value.square().square() * *value;
}

fn mul_matrix<'b, I>(
&mut self,
_constants: &mut I,
state: &mut [BlsScalar; WIDTH],
) where
I: Iterator<Item = &'b BlsScalar>,
{
fn mul_matrix(&mut self, state: &mut [BlsScalar; WIDTH]) {
let mut result = [BlsScalar::zero(); WIDTH];

for (j, value) in state.iter().enumerate() {
Expand Down
Loading

0 comments on commit c0aa604

Please sign in to comment.