Skip to content

Commit

Permalink
Rename strategy to permutation
Browse files Browse the repository at this point in the history
  • Loading branch information
moCello committed Jan 23, 2024
1 parent 6c57fd2 commit f587d5e
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 225 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Restructure crate features [#184]
- Rename trait `hades::Strategy` to `hades::Permutation` [#243]
- Rename struct `hades::ScalarStrategy` to `hades::ScalarState` [#243]
- Rename struct `hades::GadgetStrategy` to `hades::WitnessState` [#243]

### Removed

- Remove `default` and `alloc` features [#184]
- Remove `Strategy`, `ScalarStrategy` and `GadgetStrategy` from public API [#243]
- Remove `hades::Strategy`, `hades::ScalarStrategy` and `hades::GadgetStrategy` from public API [#243]

### Added

- Add `zk` and `cipher` features [#184]
- Add hades permutation here [#240]
- Add internal `permute` and `permute_gadget` functions [#243]
- Add `permute` method to `ScalarStrategy` and `GadgetStrategy` [#243]

## [0.33.0] - 2024-01-03

Expand Down
8 changes: 4 additions & 4 deletions src/hades.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
//! noted in section "Concrete Instantiations Poseidon and Starkad"

mod mds_matrix;
mod permutation;
mod round_constants;
mod strategies;

use mds_matrix::MDS_MATRIX;
use permutation::Permutation;
use round_constants::ROUND_CONSTANTS;
use strategies::Strategy;

const TOTAL_FULL_ROUNDS: usize = 8;

Expand All @@ -37,9 +37,9 @@ const CONSTANTS: usize = 960;
/// The amount of field elements that fit into the hades permutation container
pub const WIDTH: usize = 5;

pub(crate) use strategies::permute;
pub(crate) use permutation::permute;
#[cfg(feature = "zk")]
pub(crate) use strategies::permute_gadget;
pub(crate) use permutation::permute_gadget;

const fn u64_from_buffer<const N: usize>(buf: &[u8; N], i: usize) -> u64 {
u64::from_le_bytes([
Expand Down
192 changes: 192 additions & 0 deletions src/hades/permutation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! This module contains an implementation of the `Hades252` permutation
//! algorithm specifically designed to work outside of Rank 1 Constraint Systems
//! (R1CS) or other custom Constraint Systems such as Add/Mul/Custom plonk
//! gate-circuits.
//!
//! The inputs of the permutation function have to be explicitly over the
//! scalar Field of the bls12_381 curve so over a modulus
//! `p = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`.

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};

/// State for zero-knowledge plonk circuits
#[cfg(feature = "zk")]
mod gadget;
#[cfg(feature = "zk")]
use gadget::WitnessState;

/// State for scalar
mod scalar;
use scalar::ScalarState;

/// Applies one Hades permutation to the state operating on scalar.
///
/// This permutation is a 3-step process that:
///
/// - Applies the half of the `FULL_ROUNDS` (which can be understood as linear
/// ops).
///
/// - In the middle step it applies the `PARTIAL_ROUDS` (which can be understood
/// as non-linear ops).
///
/// - Applies the half of the `FULL_ROUNDS` (which can be understood as linear
/// ops).
///
/// This structure allows to minimize the number of non-linear ops while
/// mantaining the security.
pub fn permute(state: &mut [BlsScalar; WIDTH]) {
let mut hades = ScalarState::new();

hades.perm(state);
}

/// Perform one Hades permutation on the given state in a plonk circuit.
#[cfg(feature = "zk")]
pub fn permute_gadget(composer: &mut Composer, state: &mut [Witness; WIDTH]) {
let mut hades = WitnessState::new(composer);

hades.perm(state);
}

/// Defines the Hades252 permutation algorithm.
pub(crate) trait Permutation<T: Clone + Copy> {
/// 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 out of ARK constants")
}

/// Add round keys to a set of `StrategyInput`.
///
/// This round key addition also known as `ARK` 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])
where
I: Iterator<Item = &'b BlsScalar>;

/// Computes `input ^ 5 (mod p)`
///
/// The modulo depends on the input you use. In our case the modulo is done
/// in respect of the scalar field of the bls12_381 curve
/// `p = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`.
fn quintic_s_box(&mut self, value: &mut T);

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

/// Applies a `Partial Round` also known as a `Partial S-Box layer` to a set
/// of inputs.
///
/// A partial round has 3 steps on every iteration:
///
/// - Add round keys to each word. Also known as `ARK`.
/// - Apply `quintic S-Box` **just to the last element of the state
/// generated from the first step.** This is also known as a `Sub state`
/// operation.
/// - Multiplies the output state from the second step by the `MDS_MATRIX`.
/// This is known as the `Mix Layer`.
fn apply_partial_round<'b, I>(&mut self, constants: &mut I, state: &mut [T])
where
I: Iterator<Item = &'b BlsScalar>,
{
let last = state.len() - 1;

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

// Then apply quintic s-box
self.quintic_s_box(&mut state[last]);

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

/// Applies a `Full Round` also known as a `Full S-Box layer` to a set of
/// inputs.
///
/// A full round has 3 steps on every iteration:
///
/// - Add round keys to each word. Also known as `ARK`.
/// - Apply `quintic S-Box` **to all of the state-elements generated from
/// the first step.** This is also known as a `Sub state` operation.
/// - Multiplies the output state from the second step by the `MDS_MATRIX`.
/// This is known as the `Mix Layer`.
fn apply_full_round<'a, I>(&mut self, constants: &mut I, state: &mut [T])
where
I: Iterator<Item = &'a BlsScalar>,
{
// Add round keys to each word
self.add_round_key(constants, 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);
}

/// Applies a `permutation-round` of the `Hades252` permutation.
///
/// It returns a vec of `WIDTH` outputs as a result which should be a
/// randomly permuted version of the input.
///
/// In general, the same round function is iterated enough times to make
/// sure that any symmetries and structural properties that might exist in
/// the round function vanish.
///
/// This `permutation` is a 3-step process that:
///
/// - Applies twice the half of the `FULL_ROUNDS` (which can be understood
/// as linear ops).
///
/// - In the middle step it applies the `PARTIAL_ROUDS` (which can be
/// understood as non-linear ops).
///
/// This structure allows to minimize the number of non-linear ops while
/// mantaining the security.
fn perm(&mut self, data: &mut [T]) {
let mut constants = ROUND_CONSTANTS.iter();

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

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

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

/// Return the total rounds count
fn rounds() -> usize {
TOTAL_FULL_ROUNDS + PARTIAL_ROUNDS
}
}
48 changes: 24 additions & 24 deletions src/hades/strategies/gadget.rs → src/hades/permutation/gadget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,58 @@
use dusk_bls12_381::BlsScalar;
use dusk_plonk::prelude::*;

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

/// Implements a Hades252 strategy for `Witness` as input values.
/// A state for the ['HadesPermutation`] operating on [`Witness`]es.
/// Requires a reference to a `ConstraintSystem`.
pub(crate) struct GadgetStrategy<'a> {
pub(crate) struct WitnessState<'a> {
/// A reference to the constraint system used by the gadgets
cs: &'a mut Composer,
count: usize,
composer: &'a mut Composer,
round: usize,
}

impl<'a> GadgetStrategy<'a> {
/// Constructs a new `GadgetStrategy` with the constraint system.
pub fn new(cs: &'a mut Composer) -> Self {
GadgetStrategy { cs, count: 0 }
impl<'a> WitnessState<'a> {
/// Constructs a new `WitnessState` with the constraint system.
pub fn new(composer: &'a mut Composer) -> Self {
WitnessState { composer, round: 0 }
}
}

impl AsMut<Composer> for GadgetStrategy<'_> {
impl AsMut<Composer> for WitnessState<'_> {
fn as_mut(&mut self) -> &mut Composer {
self.cs
self.composer
}
}

impl<'a> Strategy<Witness> for GadgetStrategy<'a> {
fn add_round_key<'b, I>(&mut self, constants: &mut I, words: &mut [Witness])
impl<'a> HadesPermutation<Witness> for WitnessState<'a> {
fn add_round_key<'b, I>(&mut self, constants: &mut I, state: &mut [Witness])
where
I: Iterator<Item = &'b BlsScalar>,
{
// Add only for the first round.
//
// The remainder ARC are performed with the constant appended
// to the linear layer
if self.count == 0 {
words.iter_mut().for_each(|w| {
if self.round == 0 {
state.iter_mut().for_each(|w| {
let constant = Self::next_c(constants);
let constraint =
Constraint::new().left(1).a(*w).constant(constant);

*w = self.cs.gate_add(constraint);
*w = self.composer.gate_add(constraint);
});
}
}

fn quintic_s_box(&mut self, value: &mut Witness) {
let constraint = Constraint::new().mult(1).a(*value).b(*value);
let v2 = self.cs.gate_mul(constraint);
let v2 = self.composer.gate_mul(constraint);

let constraint = Constraint::new().mult(1).a(v2).b(v2);
let v4 = self.cs.gate_mul(constraint);
let v4 = self.composer.gate_mul(constraint);

let constraint = Constraint::new().mult(1).a(v4).b(*value);
*value = self.cs.gate_mul(constraint);
*value = self.composer.gate_mul(constraint);
}

/// Adds a constraint for each matrix coefficient multiplication
Expand All @@ -67,7 +67,7 @@ impl<'a> Strategy<Witness> for GadgetStrategy<'a> {
I: Iterator<Item = &'b BlsScalar>,
{
let mut result = [Composer::ZERO; WIDTH];
self.count += 1;
self.round += 1;

// Implementation optimized for WIDTH = 5
//
Expand All @@ -93,7 +93,7 @@ impl<'a> Strategy<Witness> for GadgetStrategy<'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 {
let c = if self.count < Self::rounds() {
let c = if self.round < Self::rounds() {
Self::next_c(constants)
} else {
BlsScalar::zero()
Expand All @@ -107,7 +107,7 @@ impl<'a> Strategy<Witness> for GadgetStrategy<'a> {
.fourth(MDS_MATRIX[j][2])
.d(values[2]);

result[j] = self.cs.gate_add(constraint);
result[j] = self.composer.gate_add(constraint);

let constraint = Constraint::new()
.left(MDS_MATRIX[j][3])
Expand All @@ -118,7 +118,7 @@ impl<'a> Strategy<Witness> for GadgetStrategy<'a> {
.d(result[j])
.constant(c);

result[j] = self.cs.gate_add(constraint);
result[j] = self.composer.gate_add(constraint);
}

values.copy_from_slice(&result);
Expand Down Expand Up @@ -158,7 +158,7 @@ mod tests {
*v = composer.append_witness(*o);
});

// Apply Hades gadget strategy.
// Apply Hades gadget permutation.
permute_gadget(composer, &mut i_var);

// Copy the result of the permutation into the perm.
Expand Down
Loading

0 comments on commit f587d5e

Please sign in to comment.