Skip to content

Commit

Permalink
Remove the 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.
  • Loading branch information
moCello committed Jan 24, 2024
1 parent 686b2b1 commit 873c7b3
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 102 deletions.
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.
11 changes: 6 additions & 5 deletions src/hades.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
//! - Permutation `WIDTH` is 5 field elements
//! - 8 full rounds: 4 full rounds at the beginning and 4 full rounds at the
//! end, and each full round has `WIDTH` quintic S-Boxes.
//! - 59 partial rounds: each partial round has a quintic S-Box and `WIDTH - 1`
//! identity functions.
//! - 960 round constants
//! - Round constants for the full rounds are generated using [this algorithm](https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py)
//! - 59 partial rounds: each partial round has `WIDTH - 1` identity function
//! and one quintic S-Box.
//! - 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
65 changes: 21 additions & 44 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 Down Expand Up @@ -66,16 +66,8 @@ 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
fn increment_round(&mut self);

/// Add round keys to the state.
///
Expand All @@ -84,12 +76,7 @@ pub(crate) trait Permutation<T> {
///
/// 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_key(&mut self, state: &mut [T; WIDTH]);

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

/// Multiply the values for 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.
Expand All @@ -112,21 +97,18 @@ pub(crate) trait Permutation<T> {
/// 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>,
{
fn apply_partial_round(&mut self, state: &mut [T; WIDTH]) {
// Increment the inner rounds counter
self.increment_round();

// Add round keys to each state element
self.add_round_key(constants, state);
self.add_round_key(state);

// Then apply quintic s-box
// 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
Expand All @@ -138,21 +120,18 @@ pub(crate) trait Permutation<T> {
/// 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>,
{
fn apply_full_round(&mut self, state: &mut [T; WIDTH]) {
// Increment the inner rounds counter
self.increment_round();

// Add round keys to each state element
self.add_round_key(constants, state);
self.add_round_key(state);

// Then apply quintic s-box
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.
Expand All @@ -167,21 +146,19 @@ pub(crate) trait Permutation<T> {
/// This structure allows to minimize the number of non-linear ops while
/// mantaining 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
37 changes: 15 additions & 22 deletions src/hades/permutation/gadget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
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.
/// Requires a reference to a `ConstraintSystem`.
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_key(&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
41 changes: 21 additions & 20 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
/// 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_key(&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
17 changes: 7 additions & 10 deletions src/hades/round_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! This module is designed to load the 960 constants used as `round_constants`
//! from `ark.bin`.
//! This module is designed to load the constants used as `round_constants`
//! from `assets/ark.bin`.
//!
//! The constants were originally computed using:
//! https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py
Expand All @@ -15,22 +15,19 @@ use dusk_bls12_381::BlsScalar;

use crate::hades::CONSTANTS;

/// `ROUND_CONSTANTS` constists on a static reference
/// that points to the pre-loaded 960 Fq constants.
/// `ROUND_CONSTANTS` constists on a static reference that points to the
/// pre-loaded 335 constant scalar of the bls12_381 curve.
///
/// This 960 `BlsScalar` constants are loaded from `ark.bin`
/// where all of the `BlsScalar`s are represented in buf.
/// These 335 `BlsScalar` constants are loaded from `assets/ark.bin`.
///
/// This round constants have been taken from:
/// https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py
/// and then mapped onto `Fq` in the Ristretto scalar field.
/// Check `assets/HOWTO.md` to see how we generated the constants.
pub const ROUND_CONSTANTS: [BlsScalar; CONSTANTS] = {
let bytes = include_bytes!("../../assets/ark.bin");
let mut cnst = [BlsScalar::zero(); CONSTANTS];

let mut i = 0;
let mut j = 0;
while i < bytes.len() {
while i < CONSTANTS * 32 {
let a = super::u64_from_buffer(bytes, i);
let b = super::u64_from_buffer(bytes, i + 8);
let c = super::u64_from_buffer(bytes, i + 16);
Expand Down

0 comments on commit 873c7b3

Please sign in to comment.