Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move hades permutation into poseidon lib #242

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ license = "MPL-2.0"
dusk-bls12_381 = { version = "0.13", default-features = false }
dusk-jubjub = { version = "0.14", default-features = false }
dusk-bytes = "0.1"
dusk-hades = "0.24"
dusk-plonk = { version = "0.19", default-features = false, features = ["alloc"], optional = true }
rkyv = { version = "0.7", optional = true, default-features = false }
bytecheck = { version = "0.6", optional = true, default-features = false }
Expand All @@ -26,7 +25,6 @@ ff = { version = "0.13", default-features = false }
[features]
zk = [
"dusk-plonk",
"dusk-hades/plonk"
]
merkle = []
cipher = []
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ Reference implementation for the Poseidon Hashing algorithm.
This repository has been created so there's a unique library that holds the tools & functions
required to perform Poseidon Hashes.

This hashes heavily rely on the Hades permutation, which is one of the key parts that Poseidon needs in order
to work.
This library uses the reference implementation of [Dusk-Hades](https://github.com/dusk-network/hades252) which has been
designed & build by the [Dusk-Network team](https://dusk.network/).
These hashes heavily rely on the Hades design for its inner permutation.

**The library provides the two hashing techniques of Poseidon:**

Expand Down
115 changes: 115 additions & 0 deletions assets/HOWTO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!-- 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. -->

# How to generate the assets

The `ark.bin` and `mds.bin` files in this folder are generated using the snippets below:

## Filename: ark.bin

```rust
use dusk_bls12_381::BlsScalar;
use sha2::{Digest, Sha512};
use std::fs;
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;

fn constants() -> [BlsScalar; CONSTANTS] {
let mut cnst = [BlsScalar::zero(); CONSTANTS];
let mut p = BlsScalar::one();
let mut bytes = b"poseidon-for-plonk".to_vec();

cnst.iter_mut().for_each(|c| {
let mut hasher = Sha512::new();
hasher.update(bytes.as_slice());
bytes = hasher.finalize().to_vec();

let mut v = [0x00u8; 64];
v.copy_from_slice(&bytes[0..64]);

*c = BlsScalar::from_bytes_wide(&v) + p;
p = *c;
});

cnst
}

fn write_constants() -> std::io::Result<()> {
let filename = "ark.bin";
let mut buf: Vec<u8> = vec![];

constants().iter().for_each(|c| {
c.internal_repr()
.iter()
.for_each(|r| buf.extend_from_slice(&(*r).to_le_bytes()));
});

let mut file = fs::File::create(filename)?;
file.write_all(&buf)?;
Ok(())
}
```

## Filename: mds.bin

```rust
use dusk_bls12_381::BlsScalar;
use std::fs;
use std::io::Write;

// The width of the permutation container, this needs to be the same number as
// in `dusk_poseidon::hades::WIDTH`.
const WIDTH: usize = 5;

fn mds() -> [[BlsScalar; WIDTH]; WIDTH] {
let mut matrix = [[BlsScalar::zero(); WIDTH]; WIDTH];
let mut xs = [BlsScalar::zero(); WIDTH];
let mut ys = [BlsScalar::zero(); WIDTH];

// Generate x and y values deterministically for the cauchy matrix, where
// `x[i] != y[i]` to allow the values to be inverted and there are no
// duplicates in the x vector or y vector, so that the determinant is always
// non-zero.
// [a b]
// [c d]
// det(M) = (ad - bc) ; if a == b and c == d => det(M) = 0
// For an MDS matrix, every possible mxm submatrix, must have det(M) != 0
(0..WIDTH).for_each(|i| {
xs[i] = BlsScalar::from(i as u64);
ys[i] = BlsScalar::from((i + WIDTH) as u64);
});

let mut m = 0;
(0..WIDTH).for_each(|i| {
(0..WIDTH).for_each(|j| {
matrix[m][j] = (xs[i] + ys[j]).invert().unwrap();
});
m += 1;
});

matrix
}

fn write_mds() -> std::io::Result<()> {
let filename = "mds.bin";
let mut buf: Vec<u8> = vec![];

mds().iter().for_each(|row| {
row.iter().for_each(|c| {
c.internal_repr()
.iter()
.for_each(|r| buf.extend_from_slice(&(*r).to_le_bytes()));
});
});

let mut file = fs::File::create(filename)?;
file.write_all(&buf)?;
Ok(())
}
```
Binary file added assets/ark.bin
Binary file not shown.
Binary file added assets/mds.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion benches/sponge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Copyright (c) DUSK NETWORK. All rights reserved.

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dusk_hades::WIDTH;
use dusk_plonk::prelude::*;
use dusk_poseidon::hades::WIDTH;
use ff::Field;
use rand::rngs::StdRng;
use rand::SeedableRng;
Expand Down
9 changes: 5 additions & 4 deletions src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@

use dusk_bls12_381::BlsScalar;
use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
use dusk_hades::{ScalarStrategy, Strategy};
use dusk_jubjub::JubJubAffine;

use crate::hades::{ScalarStrategy, Strategy, WIDTH};

#[cfg(feature = "rkyv-impl")]
use bytecheck::CheckBytes;
#[cfg(feature = "rkyv-impl")]
Expand Down Expand Up @@ -169,7 +170,7 @@ impl PoseidonCipher {
pub fn initial_state(
secret: &JubJubAffine,
nonce: BlsScalar,
) -> [BlsScalar; dusk_hades::WIDTH] {
) -> [BlsScalar; WIDTH] {
[
// Domain - Maximum plaintext length of the elements of Fq, as
// defined in the paper
Expand Down Expand Up @@ -255,7 +256,7 @@ impl PoseidonCipher {
#[cfg(feature = "zk")]
mod zk {
use super::PoseidonCipher;
use dusk_hades::GadgetStrategy;
use crate::hades::{GadgetStrategy, WIDTH};

use dusk_plonk::prelude::*;

Expand All @@ -267,7 +268,7 @@ mod zk {
ks0: Witness,
ks1: Witness,
nonce: Witness,
) -> [Witness; dusk_hades::WIDTH] {
) -> [Witness; WIDTH] {
let domain = BlsScalar::from_raw([0x100000000u64, 0, 0, 0]);
let domain = composer.append_constant(domain);

Expand Down
54 changes: 54 additions & 0 deletions src/hades.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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.

//! Implementation of [Hades252](https://eprint.iacr.org/2019/458.pdf)
//! permutation algorithm over the Bls12-381 Scalar field.
//!
//! ## Parameters
//!
//! - `p = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`
//! - 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)
//! - The MDS matrix is a cauchy matrix, the method used to generate it, is
//! noted in section "Concrete Instantiations Poseidon and Starkad"

mod mds_matrix;
mod round_constants;
mod strategies;

use mds_matrix::MDS_MATRIX;
use round_constants::ROUND_CONSTANTS;

const TOTAL_FULL_ROUNDS: usize = 8;

const PARTIAL_ROUNDS: usize = 59;

const CONSTANTS: usize = 960;

/// The amount of field elements that fit into the hades permutation container
pub const WIDTH: usize = 5;

#[cfg(feature = "zk")]
pub use strategies::GadgetStrategy;
pub use strategies::{ScalarStrategy, Strategy};

const fn u64_from_buffer<const N: usize>(buf: &[u8; N], i: usize) -> u64 {
u64::from_le_bytes([
buf[i],
buf[i + 1],
buf[i + 2],
buf[i + 3],
buf[i + 4],
buf[i + 5],
buf[i + 6],
buf[i + 7],
])
}
39 changes: 39 additions & 0 deletions src/hades/mds_matrix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.

use dusk_bls12_381::BlsScalar;

use crate::hades::WIDTH;

/// Represents a `static reference` to the
/// `Maximum Distance Separable Matrix -> MDS_MATRIX`
/// of `(WIDTH x WIDTH)`.
///
/// This matrix is loaded from the `mds.bin` file where
/// is pre-computed and represented in bytes.
pub const MDS_MATRIX: [[BlsScalar; WIDTH]; WIDTH] = {
let bytes = include_bytes!("../../assets/mds.bin");
let mut mds = [[BlsScalar::zero(); WIDTH]; WIDTH];
let mut k = 0;
let mut i = 0;

while i < WIDTH {
let mut j = 0;
while j < WIDTH {
let a = super::u64_from_buffer(bytes, k);
let b = super::u64_from_buffer(bytes, k + 8);
let c = super::u64_from_buffer(bytes, k + 16);
let d = super::u64_from_buffer(bytes, k + 24);
k += 32;

mds[i][j] = BlsScalar::from_raw([a, b, c, d]);
j += 1;
}
i += 1;
}

mds
};
63 changes: 63 additions & 0 deletions src/hades/round_constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 is designed to load the 960 constants used as `round_constants`
//! from `ark.bin`.
//!
//! The constants were originally computed using:
//! https://extgit.iaik.tugraz.at/krypto/hadesmimc/blob/master/code/calc_round_numbers.py
//! and then mapped onto `BlsScalar` in the Bls12_381 scalar field.

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.
///
/// This 960 `BlsScalar` constants are loaded from `ark.bin`
/// where all of the `BlsScalar`s are represented in buf.
///
/// 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.
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() {
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);
let d = super::u64_from_buffer(bytes, i + 24);

cnst[j] = BlsScalar::from_raw([a, b, c, d]);
j += 1;

i += 32;
}

cnst
};

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_round_constants() {
// Check each element is non-zero
let zero = BlsScalar::zero();
let has_zero = ROUND_CONSTANTS.iter().any(|&x| x == zero);
for ctant in ROUND_CONSTANTS.iter() {
let bytes = ctant.to_bytes();
assert!(&BlsScalar::from_bytes(&bytes).unwrap() == ctant);
}
assert!(!has_zero);
}
}
Loading