diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
index 99b7f830a20..c2c0624dfad 100644
--- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
+++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx
@@ -15,6 +15,14 @@ Verifier for EdDSA signatures
fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool
```
+It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following:
+```rust
+use dep::std::hash::poseidon2::Poseidon2Hasher;
+
+let mut hasher = Poseidon2Hasher::default();
+eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher);
+```
+
## eddsa::eddsa_to_pub
diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr
index 966bc1da2a1..3aff6043ffd 100644
--- a/noir_stdlib/src/eddsa.nr
+++ b/noir_stdlib/src/eddsa.nr
@@ -1,6 +1,8 @@
use crate::hash::poseidon;
use crate::ec::consts::te::baby_jubjub;
use crate::ec::tecurve::affine::Point as TEPoint;
+use crate::hash::{Hash, Hasher, BuildHasher, BuildHasherDefault};
+use crate::hash::poseidon::PoseidonHasher;
// Returns true if signature is valid
pub fn eddsa_poseidon_verify(
@@ -11,6 +13,28 @@ pub fn eddsa_poseidon_verify(
signature_r8_y: Field,
message: Field
) -> bool {
+ let mut hasher = PoseidonHasher::default();
+ eddsa_verify_with_hasher(
+ pub_key_x,
+ pub_key_y,
+ signature_s,
+ signature_r8_x,
+ signature_r8_y,
+ message,
+ &mut hasher
+ )
+}
+
+pub fn eddsa_verify_with_hasher(
+ pub_key_x: Field,
+ pub_key_y: Field,
+ signature_s: Field,
+ signature_r8_x: Field,
+ signature_r8_y: Field,
+ message: Field,
+ hasher: &mut H
+) -> bool
+where H: Hasher {
// Verifies by testing:
// S * B8 = R8 + H(R8, A, m) * A8
let bjj = baby_jubjub();
@@ -23,7 +47,12 @@ pub fn eddsa_poseidon_verify(
// Ensure S < Subgroup Order
assert(signature_s.lt(bjj.suborder));
// Calculate the h = H(R, A, msg)
- let hash: Field = poseidon::bn254::hash_5([signature_r8_x, signature_r8_y, pub_key_x, pub_key_y, message]);
+ signature_r8_x.hash(hasher);
+ signature_r8_y.hash(hasher);
+ pub_key_x.hash(hasher);
+ pub_key_y.hash(hasher);
+ message.hash(hasher);
+ let hash: Field = (*hasher).finish();
// Calculate second part of the right side: right2 = h*8*A
// Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup.
let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key);
diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr
index 10c0a48917c..db8a32d7909 100644
--- a/noir_stdlib/src/hash/mimc.nr
+++ b/noir_stdlib/src/hash/mimc.nr
@@ -1,3 +1,6 @@
+use crate::hash::Hasher;
+use crate::default::Default;
+
// mimc-p/p implementation
// constants are (publicly generated) random numbers, for instance using keccak as a ROM.
// You must use constants generated for the native field
@@ -16,13 +19,8 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp: Field) -> Field {
}
global MIMC_BN254_ROUNDS = 91;
-//mimc implementation with hardcoded parameters for BN254 curve.
-#[field(bn254)]
-pub fn mimc_bn254(array: [Field; N]) -> Field {
- //mimc parameters
- let exponent = 7;
- //generated from seed "mimc" using keccak256
- let constants: [Field; MIMC_BN254_ROUNDS] = [
+//generated from seed "mimc" using keccak256
+global MIMC_BN254_CONSTANTS: [Field; MIMC_BN254_ROUNDS] = [
0,
20888961410941983456478427210666206549300505294776164667214940546594746570981,
15265126113435022738560151911929040668591755459209400716467504685752745317193,
@@ -116,10 +114,46 @@ pub fn mimc_bn254(array: [Field; N]) -> Field {
13602139229813231349386885113156901793661719180900395818909719758150455500533
];
+//mimc implementation with hardcoded parameters for BN254 curve.
+#[field(bn254)]
+pub fn mimc_bn254(array: [Field; N]) -> Field {
+ let exponent = 7;
let mut r = 0;
for elem in array {
- let h = mimc(elem, r, constants, exponent);
+ let h = mimc(elem, r, MIMC_BN254_CONSTANTS, exponent);
r = r + elem + h;
}
r
}
+
+struct MimcHasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for MimcHasher {
+ #[field(bn254)]
+ fn finish(self) -> Field {
+ let exponent = 7;
+ let mut r = 0;
+ for i in 0..self._len {
+ let h = mimc(self._state[i], r, MIMC_BN254_CONSTANTS, exponent);
+ r = r + self._state[i] + h;
+ }
+ r
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for MimcHasher{
+ fn default() -> Self{
+ MimcHasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr
index b1a7c4a2367..7f99ad36316 100644
--- a/noir_stdlib/src/hash/poseidon.nr
+++ b/noir_stdlib/src/hash/poseidon.nr
@@ -1,5 +1,7 @@
mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254
use crate::field::modulus_num_bits;
+use crate::hash::Hasher;
+use crate::default::Default;
struct PoseidonConfig {
t: Field, // Width, i.e. state size
@@ -100,3 +102,77 @@ fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] {
y
}
+
+struct PoseidonHasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for PoseidonHasher {
+ #[field(bn254)]
+ fn finish(self) -> Field {
+ let mut result = 0;
+ assert(self._len < 16);
+ if self._len == 1 {
+ result = bn254::hash_1([self._state[0]]);
+ }
+ if self._len == 2 {
+ result = bn254::hash_2([self._state[0],self._state[1]]);
+ }
+ if self._len == 3 {
+ result = bn254::hash_3([self._state[0],self._state[1],self._state[2]]);
+ }
+ if self._len == 4 {
+ result = bn254::hash_4([self._state[0],self._state[1],self._state[2],self._state[3]]);
+ }
+ if self._len == 5 {
+ result = bn254::hash_5([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4]]);
+ }
+ if self._len == 6 {
+ result = bn254::hash_6([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5]]);
+ }
+ if self._len == 7 {
+ result = bn254::hash_7([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6]]);
+ }
+ if self._len == 8 {
+ result = bn254::hash_8([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7]]);
+ }
+ if self._len == 9 {
+ result = bn254::hash_9([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8]]);
+ }
+ if self._len == 10 {
+ result = bn254::hash_10([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9]]);
+ }
+ if self._len == 11 {
+ result = bn254::hash_11([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10]]);
+ }
+ if self._len == 12 {
+ result = bn254::hash_12([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11]]);
+ }
+ if self._len == 13 {
+ result = bn254::hash_13([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12]]);
+ }
+ if self._len == 14 {
+ result = bn254::hash_14([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13]]);
+ }
+ if self._len == 15 {
+ result = bn254::hash_15([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13], self._state[14]]);
+ }
+
+ result
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for PoseidonHasher{
+ fn default() -> Self{
+ PoseidonHasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr
index 40eea029e82..52229f18dbd 100644
--- a/noir_stdlib/src/hash/poseidon2.nr
+++ b/noir_stdlib/src/hash/poseidon2.nr
@@ -1,3 +1,6 @@
+use crate::hash::Hasher;
+use crate::default::Default;
+
global RATE = 3;
struct Poseidon2 {
@@ -9,7 +12,7 @@ struct Poseidon2 {
impl Poseidon2 {
- pub fn hash(input: [Field; N], message_size: u32) -> Field {
+ pub fn hash(input: [Field; N], message_size: u64) -> Field {
if message_size == N {
Poseidon2::hash_internal(input, N, false)
} else {
@@ -92,12 +95,12 @@ impl Poseidon2 {
result
}
- fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field {
+ fn hash_internal(input: [Field; N], in_len: u64, is_variable_length: bool) -> Field {
let two_pow_64 = 18446744073709551616;
let iv : Field = (in_len as Field) * two_pow_64;
let mut sponge = Poseidon2::new(iv);
for i in 0..input.len() {
- if i as u32 < in_len {
+ if i < in_len {
sponge.absorb(input[i]);
}
}
@@ -111,3 +114,33 @@ impl Poseidon2 {
sponge.squeeze()
}
}
+
+struct Poseidon2Hasher{
+ _state: [Field],
+ _len: u64,
+}
+
+impl Hasher for Poseidon2Hasher {
+ fn finish(self) -> Field {
+ let iv : Field = (self._state.len() as Field)*18446744073709551616; // iv = (self._state.len() << 64)
+ let mut sponge = Poseidon2::new(iv);
+ for i in 0..self._len {
+ sponge.absorb(self._state[i]);
+ }
+ sponge.squeeze()
+ }
+
+ fn write(&mut self, input: [Field]){
+ self._state = self._state.append(input);
+ self._len += input.len();
+ }
+}
+
+impl Default for Poseidon2Hasher{
+ fn default() -> Self{
+ Poseidon2Hasher{
+ _state: [],
+ _len: 0,
+ }
+ }
+}
diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr
index 4404ffe75f7..fd1a95ee5fb 100644
--- a/test_programs/execution_success/eddsa/src/main.nr
+++ b/test_programs/execution_success/eddsa/src/main.nr
@@ -2,7 +2,9 @@ use dep::std::compat;
use dep::std::ec::consts::te::baby_jubjub;
use dep::std::ec::tecurve::affine::Point as TEPoint;
use dep::std::hash;
-use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify};
+use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify_with_hasher};
+use dep::std::hash::poseidon2::Poseidon2Hasher;
+use dep::std::hash::pedersen::PedersenHasher;
fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) {
// Skip this test for non-bn254 backends
@@ -48,5 +50,11 @@ fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) {
assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg));
// User A's signature over the message can't be used with another message
assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1));
+ // Using a different hash should fail
+ let mut hasher = Poseidon2Hasher::default();
+ assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher));
+ // Using a different hash should fail
+ let mut hasher = PedersenHasher::default();
+ assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher));
}
}
diff --git a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
index 939b99595c7..a1607956190 100644
--- a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
+++ b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr
@@ -9,7 +9,7 @@ fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field
let hash2 = poseidon::bn254::hash_4(x2);
assert(hash2 == y2);
- let hash3 = poseidon2::Poseidon2::hash(x3, x3.len() as u32);
+ let hash3 = poseidon2::Poseidon2::hash(x3, x3.len());
assert(hash3 == y3);
}
// docs:end:poseidon