implement Griffin hash function

This commit is contained in:
Balazs Komuves 2025-01-25 12:35:54 +01:00
parent d7cb91e263
commit 0db2f80df0
No known key found for this signature in database
GPG Key ID: F63B7AEF18435562
22 changed files with 661 additions and 76 deletions

View File

@ -1,5 +1,5 @@
[package]
name = "rust-poseidon2-bn254"
name = "rust-bn254-hash"
version = "0.0.0"
edition = "2021"
license = "MIT OR Apache-2.0"
@ -9,3 +9,10 @@ lazy_static = { version = "1.5.0" }
ark-std = { version = "0.5.0" }
ark-ff = { version = "0.5.0" }
ark-bn254 = { version = "0.5.0" }
[dev-dependencies]
criterion = "0.3"
[[bench]]
name = "iterated_perm"
harness = false

View File

@ -1,10 +1,15 @@
`rust-poseidon2-bn254`
--------------------
`rust-bn254-hash`
---------------
Rust implementation of the Poseidon2 hash function over the BN254 curve's scalar field,
Rust implementation of some arithmetic hash functions over the BN254 curve's scalar field,
with permutation state width `t=3`.
Hash functions implemented:
- [x] Poseidon2
- [x] Griffin
We plan to integrate this into Plonky2, to allow an efficient BN254 recursive wrapper.
For this we need some custom features: For example when computing the Fiat-Shamir
challenges, we need a duplex construction which can absorb and squeeze both
@ -13,4 +18,16 @@ BN254 and Goldilocks field elements.
Currently using [arkworks](https://github.com/arkworks-rs) for field arithmetic
(maybe replace it with something more light-weight in the future...)
### Performance
Single core, unoptimized, MacBook Pro M2.
1000 permutations:
- 1000 Poseidon2 permutations: 5.24 msec (approx 12MB/sec linear hashing)
- 1000 Griffin permutations: 76.8 msec (appox 800k/sec linear hashing)
10000 permutations:
- 10000 Poseidon2 permutations: 53 msec
- 10000 Griffin permutations: 762 sec

33
benches/iterated_perm.rs Normal file
View File

@ -0,0 +1,33 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_bn254_hash::state::*;
use rust_bn254_hash::hash::*;
//------------------------------------------------------------------------------
fn iterate_perm(h: Hash, n: usize) -> State {
let mut state = state_012();
for _i in 0..n {
permute_inplace(h, &mut state);
}
state
}
fn bench_iterated_perm(c: &mut Criterion , h: Hash, n: usize) {
let msg = format!("{:?} permutation iterated {} times", h, n);
c.bench_function(&msg, |b| b.iter(|| iterate_perm(h, black_box(n))));
}
//------------------------------------------------------------------------------
fn bench_permutations(c: &mut Criterion) {
bench_iterated_perm(c, Hash::Poseidon2, 1000);
bench_iterated_perm(c, Hash::Griffin , 1000);
}
//------------------------------------------------------------------------------
criterion_group!(benches, bench_permutations);
criterion_main!(benches);

View File

@ -3,3 +3,6 @@ Reference implementation in Haskell
-----------------------------------
This is for generating test vectors.
- [x] Poseidon2
- [x] Griffin

View File

@ -1,10 +1,15 @@
-- | The BN254 scalar field
{-# LANGUAGE BangPatterns #-}
module BN254 where
--------------------------------------------------------------------------------
import Data.Bits
--------------------------------------------------------------------------------
newtype F = MkF Integer deriving (Eq,Show)
fromF :: F -> Integer
@ -28,4 +33,19 @@ instance Num F where
abs x = x
signum _ = toF 1
square :: F -> F
square x = x*x
--------------------------------------------------------------------------------
power :: F -> Integer -> F
power x0 exponent
| exponent < 0 = error "power: expecting positive exponent"
| otherwise = go 1 x0 exponent
where
go !acc _ 0 = acc
go !acc s e = go acc' s' (shiftR e 1) where
s' = s*s
acc' = if e .&. 1 == 0 then acc else acc*s
--------------------------------------------------------------------------------

View File

@ -0,0 +1,71 @@
-- | The Griffin permutation for @t=3@
module Griffin.Permutation
( permute
)
where
--------------------------------------------------------------------------------
import Data.Array
import Data.List
import Data.Word
import BN254
import Griffin.RoundConsts
--------------------------------------------------------------------------------
type State = (F,F,F)
pow5 :: F -> F
pow5 a = a*a4 where
a2 = square a
a4 = square a2
powInv5 :: F -> F
powInv5 a = power a expo_inv
sanityCheckExpoInv =
[ 13 - pow5 (powInv5 13)
, 17 - powInv5 (pow5 17)
]
sbox :: State -> State
sbox (x,y,z) = (x',y',z') where
x' = powInv5 x
y' = pow5 y
z' = z * (square u + alpha * u + beta)
u = x' + y'
addRC :: Int -> State -> State
addRC 0 = id
addRC i = let (a,b,c) = roundConstants!(i-1) in \(x,y,z) -> (x+a, y+b, z+c)
linear :: State -> State
linear (x,y,z) = let s = x+y+z in (x+s, y+s, z+s)
singleRound :: Int -> State -> State
singleRound i = linear . sbox . addRC i
permute :: State -> State
permute input = foldl' (flip singleRound) (linear input) [0..nrounds-1]
-- compress :: F -> F -> F
-- compress x y = case permute (x,y,0) of (z,_,_) -> z
-- compressPair :: (F,F) -> F
-- compressPair (x,y) = compress x y
--------------------------------------------------------------------------------
{-
iter :: Int -> (a -> a) -> a -> a
iter n f = go n where
go 0 !x = x
go !n !x = go (n-1) (f x)
-}
--------------------------------------------------------------------------------

View File

@ -0,0 +1,69 @@
-- | Constants for the Griffin permutation
--
-- based on <https://extgit.isec.tugraz.at/krypto/zkfriendlyhashzoo>
--
module Griffin.RoundConsts where
--------------------------------------------------------------------------------
import Data.Array
import BN254
--------------------------------------------------------------------------------
-- the permutation of (0,1,2)
kat :: (F,F,F)
kat =
( 0x2311cdb3076c3a7ee37fd5a271e0f3a8a3cc38057d0cea37b78951f43b1b6ff6
, 0x1d3aaed9ea361e899e667abd18e5328555b97b5c3890d52b261f940d6ab4df58
, 0x22614a0ac719cb623a636adac3bac1b85b5a7a418fcf8ab3a3ae0787fb4bed9d
)
--------------------------------------------------------------------------------
-- parameters from <https://extgit.isec.tugraz.at/krypto/zkfriendlyhashzoo>
nrounds = 12 :: Int
expo_inv = 0x26b6a528b427b35493736af8679aad17535cb9d394945a0dcfe7f7a98ccccccd :: Integer
alpha = 0x146ecffb34a66316fae66609f78d1310bc14ad7208082ca7943afebb1da4aa4a :: F
beta = 0x2b568115d544c7e941eff6ccc935384619b0fb7d2c5ba6c078c34cf81697ee1c :: F
roundConstants :: Array Int (F,F,F)
roundConstants = listArray (0,10)
[ ( 0x2fb30cafdb1f76156dfabf0cd0af4b895e764ac2a84386c9d0d7aed6a7f4eac9
, 0x282927892ce324572f19abb14871d2b539a80d8a5800cdb87a81e1697a94b6c9
, 0x03d0f3f2711dd59e3d97fc797261300cd3fee33b95cf710a32edf42aa2bc0905 )
, ( 0x036a8b3eb9ef35c74ea5a367ed279ee6d043d4ff69817f192c7251b91dcbb03d
, 0x2a626d396e7fa8ce8d6339bb37bd48491d56db0c7ac0afb5008a7464d5776a26
, 0x0cc9dfabbeaef7982543453ea3ac37ef2bfefd35a7e7070aa39b021035852d5b )
, ( 0x2a1951149e2568ab28e972a2ceddc49eff0cae8e1cddcf4b0684a73a1b4ef61b
, 0x2d0ff8e9158b2fd7ae3afe01cf09d4ce9ff81e6127e441eb6cbc79d21f22be9e
, 0x1cc315b7ea0c1efb538f0c3248a7da062309a9e41af5a555c9ea9e8a10930cb5 )
, ( 0x03cb10093ea62fb3f6e5680a128d07112ee566f1b424558f2ec9d86892e13a80
, 0x12e7bb50ae7e9e90f1765c073eb61c4be4956c424930233ce497d2722a458868
, 0x006b1367547937ae71e2e9b55d2f90c90131f9e6784ce3de0eb314ec748871e7 )
, ( 0x1ffff572c53442c58809aeca02287839b11df1420deb0e99fde2baad8b86fa9c
, 0x13aefd685e7739f9a8b4ccdbfc5ef9e566149af4d54d6b746058ea44cb422840
, 0x1ea6c3ea93fe6f4ed0186941650de76ff94ab0e6e8a583996b67ba026dd2b7a5 )
, ( 0x288f120288f9225643de833c5c15e22aadd358132bbdc12c75109048a158c9f4
, 0x0f638114cd7c781ab299e5233338b00cf2996df962347a00146a22103d9ad91a
, 0x14eeca5fa2c18999ea25ddf44237d6ac3cb8757ea452f67e2590a46f7d5b1e4f )
, ( 0x102d1a099e8cd107dc056e72370e340b0316d237b72d99ef6261761f7eb2d61c
, 0x0ef741fc2fcda50f207c759dbd844a4d630cc0e4062ca80f3ffba2cce2d3f51d
, 0x0989b9f642485692a1f91a4b207db64f38ae545bf3e0622f3862967d27f563db )
, ( 0x1eb4d812c80ce04784a80c89fbcc5aab89db274c62602bdd30f3223655e6cf8a
, 0x0124a9400253731facd46e21f41016aed69a79087f81665bc5d29a34e4e924dd
, 0x2520bfa6b70e6ba7ad380aaf9015b71983868a9c53e66e685ed6e48692c185a8 )
, ( 0x1bd62b5bfa02667ac08d51d9e77bb3ab8dbd19e7a701442a20e23f7d3d6b28b4
, 0x1ae2f0d09fffc6bb869ebc639484a7c2084cfa3c1f88a7440713b1b154e5f952
, 0x0cd06e16a0d570c3799d800d92a25efbd44a795ed5b9114a28f5f869a57d9ba1 )
, ( 0x00691740e313922521fe8c4843355eff8de0f93d4f62df0fe48755b897881c39
, 0x19903aa449fe9c27ee9c8320e6915b50c2822e61ce894be72b47a449c5705762
, 0x126e801aae44016a35deceaa3eba6ccc341fa3c2a65ab3d021fcd39abd170e1b )
, ( 0x1b0a98be27b54ac9d5d72b94187c991c1872cb2c7777c0e880f439c133971e8d
, 0x1e10a35afda2e5a173d4f3edecf29dacf51d8fac33d6bfb4088cc787ec647605
, 0x1793cda85abe2782ea8e911ce92bab59a8c68e0dd561a57b064bb233f109cc57 )
]
--------------------------------------------------------------------------------

35
reference/src/Hash.hs Normal file
View File

@ -0,0 +1,35 @@
module Hash where
--------------------------------------------------------------------------------
import BN254
import qualified Poseidon2.Permutation
import qualified Griffin.Permutation
--------------------------------------------------------------------------------
data Hash
= Poseidon2
| Griffin
deriving (Eq,Ord,Show)
--------------------------------------------------------------------------------
permute :: Hash -> (F,F,F) -> (F,F,F)
permute Poseidon2 = Poseidon2.Permutation.permute
permute Griffin = Griffin.Permutation.permute
--------------------------------------------------------------------------------
compress :: Hash -> (F,F) -> F
compress hash (x,y) = fst3 $ permute hash (x,y,0)
keyedCompress :: Hash -> Word -> (F,F) -> F
keyedCompress hash key (x,y) = fst3 $ permute hash (x,y,fromIntegral key)
--------------------------------------------------------------------------------
fst3 :: (a,b,c) -> a
fst3 (x,_,_) = x

View File

@ -1,16 +1,20 @@
-- | The Poseidon2 permutation
-- | The Poseidon2 permutation for @t=3@
module Permutation where
module Poseidon2.Permutation
( permute
)
where
--------------------------------------------------------------------------------
import Data.Word
import RoundConsts
import BN254
import Poseidon2.RoundConsts
--------------------------------------------------------------------------------
type State a = (a,a,a)
@ -52,11 +56,11 @@ permute
--------------------------------------------------------------------------------
compress :: (F,F) -> F
compress (x,y) = fst3 $ permute (x,y,0)
-- compress :: (F,F) -> F
-- compress (x,y) = fst3 $ permute (x,y,0)
keyedCompress :: Word -> (F,F) -> F
keyedCompress key (x,y) = fst3 $ permute (x,y,fromIntegral key)
-- keyedCompress :: Word -> (F,F) -> F
-- keyedCompress key (x,y) = fst3 $ permute (x,y,fromIntegral key)
--------------------------------------------------------------------------------

View File

@ -1,6 +1,6 @@
module RoundConsts where
module Poseidon2.RoundConsts where
--------------------------------------------------------------------------------

View File

@ -7,7 +7,7 @@ module Sponge where
import Data.Word
import BN254
import Permutation
import Hash
import U64
--------------------------------------------------------------------------------
@ -38,14 +38,14 @@ domainSep (Config{..})
--------------------------------------------------------------------------------
-- | Sponge construction with rate=2 (capacity=1)), and 10* padding
feltSpongeWithPad :: [F] -> F
feltSpongeWithPad input = go (0,0,civ) (felts_pad_10star input) where
feltSpongeWithPad :: Hash -> [F] -> F
feltSpongeWithPad hash input = go (0,0,civ) (felts_pad_10star input) where
-- domain separation capacity IV
civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 254 , padding = 1 , mblen = Nothing }
go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (a:b:rest) = go state' rest where
state' = permute (sx+a, sy+b, sz)
state' = permute hash (sx+a, sy+b, sz)
felts_pad_10star :: [F] -> [F]
felts_pad_10star = go where
@ -56,8 +56,8 @@ feltSpongeWithPad input = go (0,0,civ) (felts_pad_10star input) where
--------------------------------------------------------------------------------
-- | Sponge construction with rate=2 (capacity=1), and no padding
feltSpongeWithNoPad :: [F] -> F
feltSpongeWithNoPad input
feltSpongeWithNoPad :: Hash -> [F] -> F
feltSpongeWithNoPad hash input
| null input = error "the combination of empty input and no padding is not allowed!"
| otherwise = fst3 $ go (0,0,civ) input where
@ -67,21 +67,21 @@ feltSpongeWithNoPad input
add (x,y) (sx,sy,sz) = (sx+x, sy+y, sz)
go state [] = state
go state [a] = permute $ add (a,0) state
go state (a:b:rest) = go (permute $ add (a,b) state) rest
go state [a] = permute hash $ add (a,0) state
go state (a:b:rest) = go (permute hash $ add (a,b) state) rest
--------------------------------------------------------------------------------
-- | Sponge construction for 64 bit words with rate=2 (capacity=1), and 10* padding
u64SpongeWithPad :: [Word64] -> F
u64SpongeWithPad input = go (0,0,civ) (chunk_pad_10star input) where
u64SpongeWithPad :: Hash -> [Word64] -> F
u64SpongeWithPad hash input = go (0,0,civ) (chunk_pad_10star input) where
-- domain separation capacity IV
civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 64 , padding = 1 , mblen = Nothing }
go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (this:rest) = go state' rest where
(a,b) = u64sToFelts this
state' = permute (sx+a, sy+b, sz)
state' = permute hash (sx+a, sy+b, sz)
chunk_pad_10star :: [Word64] -> [[Word64]]
chunk_pad_10star = go where
@ -93,8 +93,8 @@ u64SpongeWithPad input = go (0,0,civ) (chunk_pad_10star input) where
--------------------------------------------------------------------------------
-- | Sponge construction for 64 bit words with rate=2 (capacity=1), no padding
u64SpongeWithNoPad :: [Word64] -> F
u64SpongeWithNoPad input
u64SpongeWithNoPad :: Hash -> [Word64] -> F
u64SpongeWithNoPad hash input
| null input = error "the combination of empty input and no padding is not allowed!"
| otherwise = go (0,0,civ) (chunk_nopad input)
where
@ -104,7 +104,7 @@ u64SpongeWithNoPad input
go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (this:rest) = go state' rest where
(a,b) = u64sToFelts this
state' = permute (sx+a, sy+b, sz)
state' = permute hash (sx+a, sy+b, sz)
chunk_nopad :: [Word64] -> [[Word64]]
chunk_nopad = go where

View File

@ -38,7 +38,7 @@ pub fn domain_separator(rate: usize, state_size: usize, input_bits: usize, paddi
F::from(high + low)
}
};
println!("domsep = {:?}", domsep );
// println!("domsep = {:?}", domsep );
domsep
}

119
src/griffin/constants.rs Normal file
View File

@ -0,0 +1,119 @@
//
// the constants are from: <https://extgit.isec.tugraz.at/krypto/zkfriendlyhashzoo>
//
use lazy_static::lazy_static;
use ark_std::str::{FromStr};
use ark_bn254::Fr as F;
//------------------------------------------------------------------------------
fn unsafe_field_from_str(x: &str) -> F {
F::from_str(x).unwrap()
}
//------------------------------------------------------------------------------
lazy_static! {
pub static ref ROUND_CONST: [F; 33] =
ROUND_CONST_STR.into_iter().map( unsafe_field_from_str ).collect::<Vec<F>>().try_into().unwrap();
}
lazy_static! {
pub static ref ALPHA_BETA: [F; 2] =
ALPHA_BETA_STR.into_iter().map( unsafe_field_from_str ).collect::<Vec<F>>().try_into().unwrap();
}
//------------------------------------------------------------------------------
static ALPHA_BETA_STR: [&str; 2] =
[ "9242045582776035982243706926516204235817048582477991018040169113011339176522"
, "19602292250548824693018549751754462955740127433771860547414036906211039243804"
];
static ROUND_CONST_STR: [&str; 33] =
[ "21575057070032013575607370249422922168572843616054088010296822695840749775561"
, "18165227539333969138148077798698277323402211342398628298607843458095584163529"
, "1726126395805628800811790548952231892211784821081817835072372994523321338117"
, "1545185368456384430532954817331487289151989187672595211469352371091378253885"
, "19171044491551098290395522252304912464559327002560432301678673968926949861926"
, "5784434162687179023209549836142552884059294996247555939093525039376314477915"
, "19041870414424402495330188617219703023579719543579476561296210734219093014043"
, "20382298809249057036005376676670624720446380175272317642464143230260792704670"
, "13009444832954829058985502029500690726501592107627197241390436937333570997429"
, "1715719177078607015724280631114759337373736261537569431451955542082533276288"
, "8551065748201611928251861162781465307280998405242469030926330074404586096744"
, "189186554882481350111665188905053862147574246155198382758216311699101217255"
, "14473938329656650102704190080413881364129591625887496179213615559783397915292"
, "8903124467962093988773613986086570401398179986922534692092313565550524639296"
, "13864034235001923002605948340707601380979809108409165761885129673831018051493"
, "18345297373373059561518255577690656825065380849379965144740035621401877072372"
, "6960501474279948633450408211052204556971979856679403460192799408668697876762"
, "9468163304179776272950081999298986525386580352510795571499529460201406799439"
, "7316693399989096710342810924168860974270210526485412478600810846271724246556"
, "6769246517619366931956311152023029679739269828933775723354435018849544041757"
, "4314157147325441971127677600352431302852929177374643535551749560119361627099"
, "13888909212715802851446585227982106245094197773767741791853418906797723799434"
, "517087463729436326763893682174378235325214458659138005697976070626806932701"
, "16793437231835230049239895698095976948153525201823739063424383701536397690280"
, "12590851438387885785187904453837464772321677997323903043772903379245474719924"
, "12161103543446852100515796717568924513472366588972876623082363094895121791314"
, "5796018174627845229088205498914615661280859445500332062485954530301265681313"
, "185679431318234436664825724785777888085115635820188159876654831323012668473"
, "11562651922421652082384873593424554558742416274272852853323139967159985264482"
, "8336868594467734406490560033578188513619444860218799431782386630832313273883"
, "12231169574412313890341429219149348762535752105916771093104859589828559707789"
, "13598782448295194434910171362195833220188338705311920641119644628875468895749"
, "10664341432766012784920284332819108541459655048225303977605704215204171730007"
];
//------------------------------------------------------------------------------
/*
static ALPHA_BETA_STR: [&str; 2] =
[ "0x146ecffb34a66316fae66609f78d1310bc14ad7208082ca7943afebb1da4aa4a"
, "0x2b568115d544c7e941eff6ccc935384619b0fb7d2c5ba6c078c34cf81697ee1c"
];
static ROUND_CONST_STR: [&str; 33] =
[ "0x2fb30cafdb1f76156dfabf0cd0af4b895e764ac2a84386c9d0d7aed6a7f4eac9"
, "0x282927892ce324572f19abb14871d2b539a80d8a5800cdb87a81e1697a94b6c9"
, "0x03d0f3f2711dd59e3d97fc797261300cd3fee33b95cf710a32edf42aa2bc0905"
, "0x036a8b3eb9ef35c74ea5a367ed279ee6d043d4ff69817f192c7251b91dcbb03d"
, "0x2a626d396e7fa8ce8d6339bb37bd48491d56db0c7ac0afb5008a7464d5776a26"
, "0x0cc9dfabbeaef7982543453ea3ac37ef2bfefd35a7e7070aa39b021035852d5b"
, "0x2a1951149e2568ab28e972a2ceddc49eff0cae8e1cddcf4b0684a73a1b4ef61b"
, "0x2d0ff8e9158b2fd7ae3afe01cf09d4ce9ff81e6127e441eb6cbc79d21f22be9e"
, "0x1cc315b7ea0c1efb538f0c3248a7da062309a9e41af5a555c9ea9e8a10930cb5"
, "0x03cb10093ea62fb3f6e5680a128d07112ee566f1b424558f2ec9d86892e13a80"
, "0x12e7bb50ae7e9e90f1765c073eb61c4be4956c424930233ce497d2722a458868"
, "0x006b1367547937ae71e2e9b55d2f90c90131f9e6784ce3de0eb314ec748871e7"
, "0x1ffff572c53442c58809aeca02287839b11df1420deb0e99fde2baad8b86fa9c"
, "0x13aefd685e7739f9a8b4ccdbfc5ef9e566149af4d54d6b746058ea44cb422840"
, "0x1ea6c3ea93fe6f4ed0186941650de76ff94ab0e6e8a583996b67ba026dd2b7a5"
, "0x288f120288f9225643de833c5c15e22aadd358132bbdc12c75109048a158c9f4"
, "0x0f638114cd7c781ab299e5233338b00cf2996df962347a00146a22103d9ad91a"
, "0x14eeca5fa2c18999ea25ddf44237d6ac3cb8757ea452f67e2590a46f7d5b1e4f"
, "0x102d1a099e8cd107dc056e72370e340b0316d237b72d99ef6261761f7eb2d61c"
, "0x0ef741fc2fcda50f207c759dbd844a4d630cc0e4062ca80f3ffba2cce2d3f51d"
, "0x0989b9f642485692a1f91a4b207db64f38ae545bf3e0622f3862967d27f563db"
, "0x1eb4d812c80ce04784a80c89fbcc5aab89db274c62602bdd30f3223655e6cf8a"
, "0x0124a9400253731facd46e21f41016aed69a79087f81665bc5d29a34e4e924dd"
, "0x2520bfa6b70e6ba7ad380aaf9015b71983868a9c53e66e685ed6e48692c185a8"
, "0x1bd62b5bfa02667ac08d51d9e77bb3ab8dbd19e7a701442a20e23f7d3d6b28b4"
, "0x1ae2f0d09fffc6bb869ebc639484a7c2084cfa3c1f88a7440713b1b154e5f952"
, "0x0cd06e16a0d570c3799d800d92a25efbd44a795ed5b9114a28f5f869a57d9ba1"
, "0x00691740e313922521fe8c4843355eff8de0f93d4f62df0fe48755b897881c39"
, "0x19903aa449fe9c27ee9c8320e6915b50c2822e61ce894be72b47a449c5705762"
, "0x126e801aae44016a35deceaa3eba6ccc341fa3c2a65ab3d021fcd39abd170e1b"
, "0x1b0a98be27b54ac9d5d72b94187c991c1872cb2c7777c0e880f439c133971e8d"
, "0x1e10a35afda2e5a173d4f3edecf29dacf51d8fac33d6bfb4088cc787ec647605"
, "0x1793cda85abe2782ea8e911ce92bab59a8c68e0dd561a57b064bb233f109cc57"
];
*/
//------------------------------------------------------------------------------

3
src/griffin/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod constants;
pub mod permutation;

129
src/griffin/permutation.rs Normal file
View File

@ -0,0 +1,129 @@
// the Griffin permutation for `t=3`
use ark_ff::{Field};
use ark_ff::prelude::{Zero};
use ark_bn254::Fr as F;
use crate::griffin::constants::*;
use crate::state::*;
//------------------------------------------------------------------------------
/*
const EXPO_D_INV_BE: [u64; 4] =
[ 0x26b6a528b427b354u64
, 0x93736af8679aad17u64
, 0x535cb9d394945a0du64
, 0xcfe7f7a98ccccccdu64
];
*/
const EXPO_D_INV_LE: [u64; 4] =
[ 0xcfe7f7a98ccccccdu64
, 0x535cb9d394945a0du64
, 0x93736af8679aad17u64
, 0x26b6a528b427b354u64
];
//------------------------------------------------------------------------------
fn pow_5(x: F) -> F {
let x2: F = x * x;
let x4: F = x2*x2;
x*x4
}
fn pow_inv_5(x: F) -> F {
x.pow( EXPO_D_INV_LE )
}
//------------------------------------------------------------------------------
fn linear_layer(u: &mut State) {
let s = u.x + u.y + u.z;
u.x += s;
u.y += s;
u.z += s;
}
//------------------------------------------------------------------------------
fn round_fun(j: usize, u: &mut State) {
if j > 0 {
let k = 3*(j-1);
u.x += ROUND_CONST[k+0];
u.y += ROUND_CONST[k+1];
u.z += ROUND_CONST[k+2];
}
u.x = pow_inv_5(u.x);
u.y = pow_5 (u.y);
let t = u.x + u.y;
let m = t*t + ALPHA_BETA[0]*t + ALPHA_BETA[1];
u.z *= m;
linear_layer(u);
}
//------------------------------------------------------------------------------
pub fn permute_inplace(u: &mut State) {
linear_layer(u);
for j in 0..12 { round_fun(j, u); }
}
pub fn permute(u: State) -> State {
let mut v = u;
permute_inplace(&mut v);
v
}
//------------------------------------------------------------------------------
pub fn compress(x: F, y: F) -> F {
let mut u = State { x: x, y: y, z: F::zero() };
permute_inplace(&mut u);
u.x
}
pub fn keyed_compress(key: u64, x: F, y: F) -> F {
let mut u = State { x: x, y: y, z: F::from(key) };
permute_inplace(&mut u);
u.x
}
//------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use ark_std::str::FromStr;
#[test]
fn sanity_check_d_inv() {
let x = F::from( 1234567u64 );
let y = pow_5 (x);
let z = pow_inv_5(y);
assert_eq!(x,z);
let x = F::from( 7890123u64 );
let y = pow_inv_5(x);
let z = pow_5 (y);
assert_eq!(x,z);
}
#[test]
fn permute_kat() {
let input = State { x: F::from(0u64), y: F::from(1u64), z: F::from(2u64) };
let output = permute(input);
// println!("x = {}",output.x);
// println!("y = {}",output.y);
// println!("z = {}",output.z);
let expected = State
{ x: F::from_str("15862405785128810275837435502653224425290071258167230490599117376332100235254").unwrap()
, y: F::from_str("13220756517509979517684528785753328587257706928708746278499548208567338458968").unwrap()
, z: F::from_str("15550532036911446928426039913328049280239190234626561457568755196858003615133").unwrap()
};
assert_eq!(output,expected);
}
}

49
src/hash.rs Normal file
View File

@ -0,0 +1,49 @@
use ark_ff::prelude::{Zero};
use ark_bn254::Fr as F;
use crate::state::*;
use crate::poseidon2;
use crate::griffin;
//------------------------------------------------------------------------------
#[derive(Debug, Copy, Clone)]
pub enum Hash {
Poseidon2,
Griffin,
}
//------------------------------------------------------------------------------
pub fn permute(h: Hash, s: State) -> State {
match h {
Hash::Poseidon2 => poseidon2::permutation::permute(s),
Hash::Griffin => griffin::permutation::permute(s),
}
}
pub fn permute_inplace(h: Hash, s: &mut State){
match h {
Hash::Poseidon2 => poseidon2::permutation::permute_inplace(s),
Hash::Griffin => griffin::permutation::permute_inplace(s),
};
}
//------------------------------------------------------------------------------
pub fn compress(h: Hash, x: F, y: F) -> F {
let mut u = State { x: x, y: y, z: F::zero() };
permute_inplace(h, &mut u);
u.x
}
pub fn keyed_compress(h: Hash, key: u64, x: F, y: F) -> F {
let mut u = State { x: x, y: y, z: F::from(key) };
permute_inplace(h, &mut u);
u.x
}
//------------------------------------------------------------------------------

View File

@ -1,7 +1,9 @@
mod constants;
mod dom_sep;
mod words;
pub mod permutation;
pub mod state;
pub mod hash;
pub mod poseidon2;
pub mod griffin;
pub mod sponge;

3
src/poseidon2/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod constants;
pub mod permutation;

View File

@ -1,26 +1,11 @@
// the Poseidon2 permutation for `t=3`
use ark_ff::prelude::{Zero};
use ark_bn254::Fr as F;
use crate::constants::*;
//------------------------------------------------------------------------------
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct State
{ pub x: F
, pub y: F
, pub z: F
}
// cannot be a constant, because stupid rust people...
pub fn zero_state() -> State {
State
{ x: F::zero()
, y: F::zero()
, z: F::zero()
}
}
use crate::state::*;
use crate::poseidon2::constants::*;
//------------------------------------------------------------------------------
@ -30,6 +15,8 @@ fn sbox(x: F) -> F {
x*x4
}
//------------------------------------------------------------------------------
fn linear_layer(u: &mut State) {
let s = u.x + u.y + u.z;
u.x += s;
@ -110,9 +97,9 @@ mod tests {
fn permute_kat() {
let input = State { x: F::from(0u64), y: F::from(1u64), z: F::from(2u64) };
let output = permute(input);
println!("x = {}",output.x);
println!("y = {}",output.y);
println!("z = {}",output.z);
// println!("x = {}",output.x);
// println!("y = {}",output.y);
// println!("z = {}",output.z);
let expected = State
{ x: F::from_str("21882471761025344482456282050943515707267606647948403374880378562101343146243").unwrap()
, y: F::from_str("9030699330013392132529464674294378792132780497765201297316864012141442630280" ).unwrap()

View File

@ -2,14 +2,15 @@
use ark_ff::{One, Zero};
use ark_bn254::Fr as F;
use crate::permutation::*;
use crate::state::*;
use crate::hash::*;
use crate::dom_sep::*;
use crate::words::*;
//------------------------------------------------------------------------------
// hash of field elements with the `10*` padding stategy (rate=2, capacity=1)
pub fn sponge_felts_pad(xs: Vec<F>) -> F {
pub fn sponge_felts_pad(hash: Hash, xs: Vec<F>) -> F {
let l: usize = xs.len();
let m: usize = l / 2;
let mut state: State = zero_state();
@ -17,19 +18,19 @@ pub fn sponge_felts_pad(xs: Vec<F>) -> F {
for i in 0..m {
state.x += xs[2*i ];
state.y += xs[2*i+1];
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
if (l & 1) == 0 {
// even
state.x += F::one();
state.y += F::zero();
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
else {
// odd
state.x += xs[2*m];
state.y += F::one();
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
state.x
}
@ -37,7 +38,7 @@ pub fn sponge_felts_pad(xs: Vec<F>) -> F {
//------------------------------------------------------------------------------
// hash of field elements without padding (rate=2, capacity=1)
pub fn sponge_felts_no_pad(xs: Vec<F>) -> F {
pub fn sponge_felts_no_pad(hash: Hash, xs: Vec<F>) -> F {
let l: usize = xs.len();
assert!( l > 0 , "calling a sponge without padding for an empty input is not allowed");
let m: usize = l / 2;
@ -46,7 +47,7 @@ pub fn sponge_felts_no_pad(xs: Vec<F>) -> F {
for i in 0..m {
state.x += xs[2*i ];
state.y += xs[2*i+1];
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
if (l & 1) == 0 {
// even
@ -54,7 +55,7 @@ pub fn sponge_felts_no_pad(xs: Vec<F>) -> F {
else {
// odd
state.x += xs[2*m];
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
state.x
}
@ -62,7 +63,7 @@ pub fn sponge_felts_no_pad(xs: Vec<F>) -> F {
//------------------------------------------------------------------------------
// hash of u64 elements with the `10*` padding stategy (rate=2, capacity=1)
pub fn sponge_u64_pad(input: Vec<u64>) -> F {
pub fn sponge_u64_pad(hash: Hash, input: Vec<u64>) -> F {
let l: usize = input.len();
let m: usize = l / 7;
let mut state: State = zero_state();
@ -72,7 +73,7 @@ pub fn sponge_u64_pad(input: Vec<u64>) -> F {
let (a,b) = u64s_to_felts( input[7*i..7*(i+1)].try_into().unwrap() );
state.x += a;
state.y += b;
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
let r = l - 7*m;
@ -85,7 +86,7 @@ pub fn sponge_u64_pad(input: Vec<u64>) -> F {
let (a,b) = u64s_to_felts( ws );
state.x += a;
state.y += b;
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
state.x
}
@ -93,7 +94,7 @@ pub fn sponge_u64_pad(input: Vec<u64>) -> F {
//------------------------------------------------------------------------------
// hash of u64 elements with no padding (rate=2, capacity=1)
pub fn sponge_u64_no_pad(input: Vec<u64>) -> F {
pub fn sponge_u64_no_pad(hash: Hash, input: Vec<u64>) -> F {
let l: usize = input.len();
let m: usize = l / 7;
let mut state: State = zero_state();
@ -103,7 +104,7 @@ pub fn sponge_u64_no_pad(input: Vec<u64>) -> F {
let (a,b) = u64s_to_felts( input[7*i..7*(i+1)].try_into().unwrap() );
state.x += a;
state.y += b;
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
let r = l - 7*m;
@ -115,7 +116,7 @@ pub fn sponge_u64_no_pad(input: Vec<u64>) -> F {
let (a,b) = u64s_to_felts( ws );
state.x += a;
state.y += b;
permute_inplace(&mut state);
permute_inplace(hash, &mut state);
}
state.x
@ -129,7 +130,7 @@ mod tests {
use ark_std::str::FromStr;
#[test]
fn sponge_felt_pad_kats() {
fn poseidon2_sponge_felt_pad_kats() {
let expected_results_rate2: [F; 9] =
[ F::from_str("19499082269592267598445447545057958665614609297846978854367973542689225942769").unwrap()
, F::from_str("18575012789231469229884764324941297325518518125159947154666143736134627612926").unwrap()
@ -147,13 +148,13 @@ mod tests {
input.push( F::from( (i+1) as u64 ) );
}
// println!("{} -> {:?}",n,input);
let hash = sponge_felts_pad(input);
let hash = sponge_felts_pad(Hash::Poseidon2, input);
assert_eq!( hash, expected_results_rate2[n] );
}
}
#[test]
fn sponge_felt_no_pad_kats() {
fn poseidon2_sponge_felt_no_pad_kats() {
let expected_results_rate2: [F; 8] =
[ F::from_str("1115918818644972208256556451100635122398669465847258748217648737442977805626" ).unwrap()
, F::from_str("14606415634430706215884818096498881510473181807310833038459556078598593236816").unwrap()
@ -170,13 +171,13 @@ mod tests {
input.push( F::from( (i+1) as u64 ) );
}
// println!("{} -> {:?}",n,input);
let hash = sponge_felts_no_pad(input);
let hash = sponge_felts_no_pad(Hash::Poseidon2, input);
assert_eq!( hash, expected_results_rate2[n-1] );
}
}
#[test]
fn sponge_u64_pad_kats() {
fn poseidon2_sponge_u64_pad_kats() {
let expected_results: [F; 26] =
[ F::from_str("17514399988141690447675586952929944437374386600212775525745235123193699555032").unwrap()
, F::from_str("15124300392866129625907254960122795910205844155666063049200988935627203491787").unwrap()
@ -211,13 +212,13 @@ mod tests {
input.push( (i+1) as u64 );
}
// println!("{} -> {:?}",n,input);
let hash = sponge_u64_pad(input);
let hash = sponge_u64_pad(Hash::Poseidon2, input);
assert_eq!( hash, expected_results[n] );
}
}
#[test]
fn sponge_u64_no_pad_kats() {
fn poseidon2_sponge_u64_no_pad_kats() {
let expected_results: [F; 25] =
[ F::from_str("21622597924725733979495520095566570609749554956201434757717414014785749203159").unwrap()
, F::from_str("15070262037465553878910423448919356460240312147417573379822098585639266186611").unwrap()
@ -250,8 +251,8 @@ mod tests {
for i in 0..n {
input.push( (i+1) as u64 );
}
println!("{} -> {:?}",n,input);
let hash = sponge_u64_no_pad(input);
// println!("{} -> {:?}",n,input);
let hash = sponge_u64_no_pad(Hash::Poseidon2, input);
assert_eq!( hash, expected_results[n-1] );
}
}

33
src/state.rs Normal file
View File

@ -0,0 +1,33 @@
use ark_ff::prelude::{Zero};
use ark_bn254::Fr as F;
//------------------------------------------------------------------------------
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct State
{ pub x: F
, pub y: F
, pub z: F
}
// cannot be a constant, because stupid rust people...
pub fn zero_state() -> State {
State
{ x: F::zero()
, y: F::zero()
, z: F::zero()
}
}
// used as a "default" input state for testing purposes
pub fn state_012() -> State {
State
{ x: F::from(0u64)
, y: F::from(1u64)
, z: F::from(2u64)
}
}
//------------------------------------------------------------------------------