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] [package]
name = "rust-poseidon2-bn254" name = "rust-bn254-hash"
version = "0.0.0" version = "0.0.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -9,3 +9,10 @@ lazy_static = { version = "1.5.0" }
ark-std = { version = "0.5.0" } ark-std = { version = "0.5.0" }
ark-ff = { version = "0.5.0" } ark-ff = { version = "0.5.0" }
ark-bn254 = { 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`. 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. 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 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 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 Currently using [arkworks](https://github.com/arkworks-rs) for field arithmetic
(maybe replace it with something more light-weight in the future...) (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. This is for generating test vectors.
- [x] Poseidon2
- [x] Griffin

View File

@ -1,10 +1,15 @@
-- | The BN254 scalar field -- | The BN254 scalar field
{-# LANGUAGE BangPatterns #-}
module BN254 where module BN254 where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Data.Bits
--------------------------------------------------------------------------------
newtype F = MkF Integer deriving (Eq,Show) newtype F = MkF Integer deriving (Eq,Show)
fromF :: F -> Integer fromF :: F -> Integer
@ -28,4 +33,19 @@ instance Num F where
abs x = x abs x = x
signum _ = toF 1 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 Data.Word
import RoundConsts
import BN254 import BN254
import Poseidon2.RoundConsts
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type State a = (a,a,a) type State a = (a,a,a)
@ -52,11 +56,11 @@ permute
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
compress :: (F,F) -> F -- compress :: (F,F) -> F
compress (x,y) = fst3 $ permute (x,y,0) -- compress (x,y) = fst3 $ permute (x,y,0)
keyedCompress :: Word -> (F,F) -> F -- keyedCompress :: Word -> (F,F) -> F
keyedCompress key (x,y) = fst3 $ permute (x,y,fromIntegral key) -- 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 Data.Word
import BN254 import BN254
import Permutation import Hash
import U64 import U64
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -38,14 +38,14 @@ domainSep (Config{..})
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Sponge construction with rate=2 (capacity=1)), and 10* padding -- | Sponge construction with rate=2 (capacity=1)), and 10* padding
feltSpongeWithPad :: [F] -> F feltSpongeWithPad :: Hash -> [F] -> F
feltSpongeWithPad input = go (0,0,civ) (felts_pad_10star input) where feltSpongeWithPad hash input = go (0,0,civ) (felts_pad_10star input) where
-- domain separation capacity IV -- domain separation capacity IV
civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 254 , padding = 1 , mblen = Nothing } civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 254 , padding = 1 , mblen = Nothing }
go (sx,_ ,_ ) [] = sx go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (a:b:rest) = go state' rest where 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 :: [F] -> [F]
felts_pad_10star = go where 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 -- | Sponge construction with rate=2 (capacity=1), and no padding
feltSpongeWithNoPad :: [F] -> F feltSpongeWithNoPad :: Hash -> [F] -> F
feltSpongeWithNoPad input feltSpongeWithNoPad hash input
| null input = error "the combination of empty input and no padding is not allowed!" | null input = error "the combination of empty input and no padding is not allowed!"
| otherwise = fst3 $ go (0,0,civ) input where | otherwise = fst3 $ go (0,0,civ) input where
@ -66,22 +66,22 @@ feltSpongeWithNoPad input
add (x,y) (sx,sy,sz) = (sx+x, sy+y, sz) add (x,y) (sx,sy,sz) = (sx+x, sy+y, sz)
go state [] = state go state [] = state
go state [a] = permute $ add (a,0) state go state [a] = permute hash $ add (a,0) state
go state (a:b:rest) = go (permute $ add (a,b) state) rest 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 -- | Sponge construction for 64 bit words with rate=2 (capacity=1), and 10* padding
u64SpongeWithPad :: [Word64] -> F u64SpongeWithPad :: Hash -> [Word64] -> F
u64SpongeWithPad input = go (0,0,civ) (chunk_pad_10star input) where u64SpongeWithPad hash input = go (0,0,civ) (chunk_pad_10star input) where
-- domain separation capacity IV -- domain separation capacity IV
civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 64 , padding = 1 , mblen = Nothing } civ = domainSep $ Config { rate = 2 , width = 3 , nbits = 64 , padding = 1 , mblen = Nothing }
go (sx,_ ,_ ) [] = sx go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (this:rest) = go state' rest where go (sx,sy,sz) (this:rest) = go state' rest where
(a,b) = u64sToFelts this (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 :: [Word64] -> [[Word64]]
chunk_pad_10star = go where 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 -- | Sponge construction for 64 bit words with rate=2 (capacity=1), no padding
u64SpongeWithNoPad :: [Word64] -> F u64SpongeWithNoPad :: Hash -> [Word64] -> F
u64SpongeWithNoPad input u64SpongeWithNoPad hash input
| null input = error "the combination of empty input and no padding is not allowed!" | null input = error "the combination of empty input and no padding is not allowed!"
| otherwise = go (0,0,civ) (chunk_nopad input) | otherwise = go (0,0,civ) (chunk_nopad input)
where where
@ -104,7 +104,7 @@ u64SpongeWithNoPad input
go (sx,_ ,_ ) [] = sx go (sx,_ ,_ ) [] = sx
go (sx,sy,sz) (this:rest) = go state' rest where go (sx,sy,sz) (this:rest) = go state' rest where
(a,b) = u64sToFelts this (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 :: [Word64] -> [[Word64]]
chunk_nopad = go where 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) F::from(high + low)
} }
}; };
println!("domsep = {:?}", domsep ); // println!("domsep = {:?}", 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 dom_sep;
mod words; mod words;
pub mod permutation; pub mod state;
pub mod hash;
pub mod poseidon2;
pub mod griffin;
pub mod sponge; 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_ff::prelude::{Zero};
use ark_bn254::Fr as F; use ark_bn254::Fr as F;
use crate::constants::*; use crate::state::*;
use crate::poseidon2::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()
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -30,6 +15,8 @@ fn sbox(x: F) -> F {
x*x4 x*x4
} }
//------------------------------------------------------------------------------
fn linear_layer(u: &mut State) { fn linear_layer(u: &mut State) {
let s = u.x + u.y + u.z; let s = u.x + u.y + u.z;
u.x += s; u.x += s;
@ -110,9 +97,9 @@ mod tests {
fn permute_kat() { fn permute_kat() {
let input = State { x: F::from(0u64), y: F::from(1u64), z: F::from(2u64) }; let input = State { x: F::from(0u64), y: F::from(1u64), z: F::from(2u64) };
let output = permute(input); let output = permute(input);
println!("x = {}",output.x); // println!("x = {}",output.x);
println!("y = {}",output.y); // println!("y = {}",output.y);
println!("z = {}",output.z); // println!("z = {}",output.z);
let expected = State let expected = State
{ x: F::from_str("21882471761025344482456282050943515707267606647948403374880378562101343146243").unwrap() { x: F::from_str("21882471761025344482456282050943515707267606647948403374880378562101343146243").unwrap()
, y: F::from_str("9030699330013392132529464674294378792132780497765201297316864012141442630280" ).unwrap() , y: F::from_str("9030699330013392132529464674294378792132780497765201297316864012141442630280" ).unwrap()

View File

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