-
Notifications
You must be signed in to change notification settings - Fork 225
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stdlib): Implement Poseidon hash (#768)
* Implement Poseidon hash * Minor changes. Addition of BN254-specific permutation and sponge functions. * Stylistic changes * Minor changes. Exclude sponge test. * Replace stdlib functions with methods * Fix typo * Fix typo
- Loading branch information
Showing
12 changed files
with
589 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[package] | ||
name = "Poseidon 254-bit permutation test on 3 elements with alpha = 5" | ||
authors = [""] | ||
compiler_version = "0.1" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
x1 = [0,1,2] | ||
y1 = "0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a" | ||
x2 = [0,1,2,3,4] | ||
y2 = "0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465" |
10 changes: 10 additions & 0 deletions
10
crates/nargo/tests/test_data/poseidonperm_x5_254/src/main.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
use dep::std::hash::poseidon; | ||
|
||
fn main(x1: [Field; 3], y1: pub Field, x2: [Field; 5], y2: pub Field) | ||
{ | ||
let perm1 = poseidon::bn254::perm::x5_3(x1); | ||
constrain perm1[0] == y1; | ||
|
||
let perm2 = poseidon::bn254::perm::x5_5(x2); | ||
constrain perm2[0] == y2; | ||
} |
6 changes: 6 additions & 0 deletions
6
crates/nargo/tests/test_data/poseidonsponge_x5_254/Nargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[package] | ||
name = "Variable-length Poseidon-128 sponge test on 7 elements with alpha = 5" | ||
authors = [""] | ||
compiler_version = "0.1" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
x = [1,2,3,4,5,6,7] |
14 changes: 14 additions & 0 deletions
14
crates/nargo/tests/test_data/poseidonsponge_x5_254/src/main.nr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use dep::std::hash::poseidon; | ||
|
||
fn main(x: [Field; 7]) | ||
{ | ||
// Test optimised sponge | ||
let result = poseidon::bn254::sponge(x); | ||
|
||
constrain result == 0x080ae1669d62f0197190573d4a325bfb8d8fc201ce3127cbac0c47a7ac81ac48; | ||
|
||
// Test unoptimised sponge | ||
let result2 = poseidon::absorb(poseidon::bn254::consts::x5_5_config(), [0;5], 4, 1, x)[1]; | ||
|
||
constrain result2 == result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
mod poseidon; | ||
|
||
#[foreign(sha256)] | ||
fn sha256<N>(_input : [u8; N]) -> [u8; 32] {} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254 | ||
|
||
use crate::field::modulus_num_bits; | ||
|
||
struct PoseidonConfig<M,N> { | ||
t: comptime Field, // Width, i.e. state size | ||
rf: comptime u8, // Number of full rounds; should be even | ||
rp: comptime u8, // Number of partial rounds | ||
alpha: comptime Field, // S-box power; depends on the underlying field | ||
ark: [Field; M], // Additive round keys | ||
mds: [Field; N] // MDS Matrix in row-major order | ||
} | ||
|
||
fn config<M,N>( | ||
t: comptime Field, | ||
rf: comptime u8, | ||
rp: comptime u8, | ||
alpha: comptime Field, | ||
ark: [Field; M], | ||
mds: [Field; N]) | ||
-> PoseidonConfig<M,N> { | ||
// Input checks | ||
constrain t as u8 * (rf + rp) == ark.len() as u8; | ||
constrain t * t == mds.len(); | ||
constrain alpha != 0; | ||
|
||
PoseidonConfig {t, rf, rp, alpha, ark, mds} | ||
} | ||
|
||
// General Poseidon permutation on elements of type Field | ||
fn permute<M,N,O>( | ||
pos_conf: PoseidonConfig<M, N>, | ||
mut state: [Field; O]) | ||
-> [Field; O] { | ||
let PoseidonConfig {t, rf, rp, alpha, ark, mds} = pos_conf; | ||
|
||
constrain t == state.len(); | ||
|
||
let mut count = 0; | ||
|
||
// for r in 0..rf + rp | ||
for r in 0..(ark.len()/state.len()) { | ||
for i in 0..state.len() { | ||
state[i] = state[i] + ark[count + i]; | ||
} // Shift by round constants | ||
|
||
state[0] = state[0].pow_32(alpha); | ||
|
||
// Check whether we are in a full round | ||
if (r as u8 < rf/2) | (r as u8 >= rf/2 + rp) { | ||
for i in 1..state.len() { | ||
state[i] = state[i].pow_32(alpha); | ||
} | ||
} | ||
|
||
state = apply_matrix(mds, state); // Apply MDS matrix | ||
count = count + t; | ||
} | ||
|
||
state | ||
} | ||
|
||
// Absorption. Fully absorbs input message. | ||
fn absorb<M,N,O,P>( | ||
pos_conf: PoseidonConfig<M, N>, | ||
mut state: [Field; O], // Initial state; usually [0; O] | ||
rate: comptime Field, // Rate | ||
capacity: comptime Field, // Capacity; usually 1 | ||
msg: [Field; P]) // Arbitrary length message | ||
-> [Field; O] { | ||
constrain pos_conf.t == rate + capacity; | ||
|
||
let mut i = 0; | ||
|
||
for k in 0..msg.len() { | ||
// Add current block to state | ||
state[capacity + i] += msg[k]; | ||
i = i+1; | ||
|
||
// Enough to absorb | ||
if i == rate { | ||
state = permute(pos_conf, state); | ||
i = 0; | ||
} | ||
} | ||
|
||
// If we have one more block to permute | ||
if i != 0 { | ||
state = permute(pos_conf, state); | ||
} | ||
|
||
state | ||
} | ||
|
||
|
||
// Check security of sponge instantiation | ||
fn check_security(rate: Field, width: Field, security: Field) -> bool { | ||
let n = modulus_num_bits(); | ||
|
||
((n-1)*(width-rate)/2) as u8 > security as u8 | ||
} | ||
|
||
// A*x where A is an n x n matrix in row-major order and x an n-vector | ||
fn apply_matrix<N>(a: [Field], x: [Field; N]) -> [Field; N] { | ||
let mut y = x; | ||
|
||
for i in 0..x.len() { | ||
y[i] = 0; | ||
for j in 0..x.len() { | ||
y[i] = y[i] + a[x.len()*i + j]* x[j]; | ||
} | ||
} | ||
|
||
y | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Instantiations of Poseidon constants, permutations and sponge for prime field of the same order as BN254 | ||
mod perm; | ||
mod consts; | ||
|
||
use crate::hash::poseidon::PoseidonConfig; | ||
use crate::hash::poseidon::apply_matrix; | ||
|
||
// Optimised permutation for this particular field; uses hardcoded rf and rp values, | ||
// which should agree with those in pos_conf. | ||
fn permute<M,N,O>( | ||
pos_conf: PoseidonConfig<M, N>, | ||
mut state: [Field; O]) | ||
-> [Field; O] { | ||
let PoseidonConfig {t, rf: config_rf, rp: config_rp, alpha, ark, mds} = pos_conf; | ||
let rf = 8; | ||
let rp = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68][state.len() - 2]; | ||
|
||
constrain t == state.len(); | ||
constrain rf == config_rf as Field; | ||
constrain rp == config_rp as Field; | ||
|
||
let mut count = 0; | ||
|
||
// First half of full rounds | ||
for _r in 0..rf/2 { | ||
for i in 0..state.len() { | ||
state[i] = state[i] + ark[count + i]; | ||
} // Shift by round constants | ||
|
||
for i in 0..state.len() { | ||
state[i] = state[i].pow_32(alpha); | ||
} | ||
|
||
state = apply_matrix(mds, state); // Apply MDS matrix | ||
count = count + t; | ||
} | ||
|
||
// Partial rounds | ||
for _r in 0..rp { | ||
for i in 0..state.len() { | ||
state[i] = state[i] + ark[count + i]; | ||
} // Shift by round constants | ||
|
||
state[0] = state[0].pow_32(alpha); | ||
|
||
state = apply_matrix(mds, state); // Apply MDS matrix | ||
count = count + t; | ||
} | ||
|
||
// Second half of full rounds | ||
for _r in 0..rf/2 { | ||
for i in 0..state.len() { | ||
state[i] = state[i] + ark[count + i]; | ||
} // Shift by round constants | ||
|
||
for i in 0..state.len() { | ||
state[i] = state[i].pow_32(alpha); | ||
} | ||
|
||
state = apply_matrix(mds, state); // Apply MDS matrix | ||
count = count + t; | ||
} | ||
|
||
state | ||
} | ||
|
||
// Corresponding absorption. | ||
fn absorb<M,N,O,P>( | ||
pos_conf: PoseidonConfig<M, N>, | ||
mut state: [Field; O], // Initial state; usually [0; O] | ||
rate: comptime Field, // Rate | ||
capacity: comptime Field, // Capacity; usually 1 | ||
msg: [Field; P] // Arbitrary length message | ||
) -> [Field; O] { | ||
|
||
constrain pos_conf.t == rate + capacity; | ||
|
||
let mut i = 0; | ||
|
||
for k in 0..msg.len() { | ||
// Add current block to state | ||
state[capacity + i] += msg[k]; | ||
i = i+1; | ||
|
||
// Enough to absorb | ||
if i == rate { | ||
state = permute(pos_conf, state); | ||
i = 0; | ||
} | ||
} | ||
|
||
// If we have one more block to permute | ||
if i != 0 { | ||
state = permute(pos_conf, state); | ||
} | ||
|
||
state | ||
} | ||
|
||
// Variable-length Poseidon-128 sponge as suggested in second bullet point of §3 of https://eprint.iacr.org/2019/458.pdf | ||
fn sponge<N>(msg: [Field; N]) -> Field { | ||
absorb(consts::x5_5_config(), [0;5], 4, 1, msg)[1] | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.