From 0db2f80df0526db158cd6203e5473e58f544291b Mon Sep 17 00:00:00 2001 From: Balazs Komuves Date: Sat, 25 Jan 2025 12:35:54 +0100 Subject: [PATCH] implement Griffin hash function --- Cargo.toml | 9 +- README.md | 23 +++- benches/iterated_perm.rs | 33 +++++ reference/README.md | 3 + reference/src/BN254.hs | 22 +++- reference/src/Griffin/Permutation.hs | 71 ++++++++++ reference/src/Griffin/RoundConsts.hs | 69 ++++++++++ reference/src/Hash.hs | 35 +++++ reference/src/{ => Poseidon2}/Permutation.hs | 18 ++- reference/src/{ => Poseidon2}/RoundConsts.hs | 2 +- reference/src/Sponge.hs | 30 ++--- src/dom_sep.rs | 2 +- src/griffin/constants.rs | 119 +++++++++++++++++ src/griffin/mod.rs | 3 + src/griffin/permutation.rs | 129 +++++++++++++++++++ src/hash.rs | 49 +++++++ src/lib.rs | 6 +- src/{ => poseidon2}/constants.rs | 0 src/poseidon2/mod.rs | 3 + src/{ => poseidon2}/permutation.rs | 31 ++--- src/sponge.rs | 47 +++---- src/state.rs | 33 +++++ 22 files changed, 661 insertions(+), 76 deletions(-) create mode 100644 benches/iterated_perm.rs create mode 100644 reference/src/Griffin/Permutation.hs create mode 100644 reference/src/Griffin/RoundConsts.hs create mode 100644 reference/src/Hash.hs rename reference/src/{ => Poseidon2}/Permutation.hs (81%) rename reference/src/{ => Poseidon2}/RoundConsts.hs (99%) create mode 100644 src/griffin/constants.rs create mode 100644 src/griffin/mod.rs create mode 100644 src/griffin/permutation.rs create mode 100644 src/hash.rs rename src/{ => poseidon2}/constants.rs (100%) create mode 100644 src/poseidon2/mod.rs rename src/{ => poseidon2}/permutation.rs (90%) create mode 100644 src/state.rs diff --git a/Cargo.toml b/Cargo.toml index 587c053..958217a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index 4ae2937..17c6da3 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/benches/iterated_perm.rs b/benches/iterated_perm.rs new file mode 100644 index 0000000..9da216f --- /dev/null +++ b/benches/iterated_perm.rs @@ -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); + diff --git a/reference/README.md b/reference/README.md index eb47047..37beac1 100644 --- a/reference/README.md +++ b/reference/README.md @@ -3,3 +3,6 @@ Reference implementation in Haskell ----------------------------------- This is for generating test vectors. + +- [x] Poseidon2 +- [x] Griffin diff --git a/reference/src/BN254.hs b/reference/src/BN254.hs index 69abcdc..431aef3 100644 --- a/reference/src/BN254.hs +++ b/reference/src/BN254.hs @@ -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 --------------------------------------------------------------------------------- \ No newline at end of file +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 + +-------------------------------------------------------------------------------- diff --git a/reference/src/Griffin/Permutation.hs b/reference/src/Griffin/Permutation.hs new file mode 100644 index 0000000..637705f --- /dev/null +++ b/reference/src/Griffin/Permutation.hs @@ -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) +-} + +-------------------------------------------------------------------------------- diff --git a/reference/src/Griffin/RoundConsts.hs b/reference/src/Griffin/RoundConsts.hs new file mode 100644 index 0000000..068f7ab --- /dev/null +++ b/reference/src/Griffin/RoundConsts.hs @@ -0,0 +1,69 @@ + +-- | Constants for the Griffin permutation +-- +-- based on +-- + +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 + +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 ) + ] + +-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/reference/src/Hash.hs b/reference/src/Hash.hs new file mode 100644 index 0000000..8d0a459 --- /dev/null +++ b/reference/src/Hash.hs @@ -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 \ No newline at end of file diff --git a/reference/src/Permutation.hs b/reference/src/Poseidon2/Permutation.hs similarity index 81% rename from reference/src/Permutation.hs rename to reference/src/Poseidon2/Permutation.hs index 5234b37..3991c59 100644 --- a/reference/src/Permutation.hs +++ b/reference/src/Poseidon2/Permutation.hs @@ -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) -------------------------------------------------------------------------------- diff --git a/reference/src/RoundConsts.hs b/reference/src/Poseidon2/RoundConsts.hs similarity index 99% rename from reference/src/RoundConsts.hs rename to reference/src/Poseidon2/RoundConsts.hs index 5e5009e..405af6f 100644 --- a/reference/src/RoundConsts.hs +++ b/reference/src/Poseidon2/RoundConsts.hs @@ -1,6 +1,6 @@ -module RoundConsts where +module Poseidon2.RoundConsts where -------------------------------------------------------------------------------- diff --git a/reference/src/Sponge.hs b/reference/src/Sponge.hs index b50cc29..296b3fd 100644 --- a/reference/src/Sponge.hs +++ b/reference/src/Sponge.hs @@ -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 @@ -66,22 +66,22 @@ 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 [] = state + 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 diff --git a/src/dom_sep.rs b/src/dom_sep.rs index d4bb601..1776896 100644 --- a/src/dom_sep.rs +++ b/src/dom_sep.rs @@ -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 } diff --git a/src/griffin/constants.rs b/src/griffin/constants.rs new file mode 100644 index 0000000..aade070 --- /dev/null +++ b/src/griffin/constants.rs @@ -0,0 +1,119 @@ + +// +// the constants are from: +// + +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::>().try_into().unwrap(); +} + +lazy_static! { + pub static ref ALPHA_BETA: [F; 2] = + ALPHA_BETA_STR.into_iter().map( unsafe_field_from_str ).collect::>().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" + ]; + +*/ + +//------------------------------------------------------------------------------ diff --git a/src/griffin/mod.rs b/src/griffin/mod.rs new file mode 100644 index 0000000..ffe20f5 --- /dev/null +++ b/src/griffin/mod.rs @@ -0,0 +1,3 @@ + +mod constants; +pub mod permutation; diff --git a/src/griffin/permutation.rs b/src/griffin/permutation.rs new file mode 100644 index 0000000..a0d4319 --- /dev/null +++ b/src/griffin/permutation.rs @@ -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); + } + +} diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000..b94c57e --- /dev/null +++ b/src/hash.rs @@ -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 +} + +//------------------------------------------------------------------------------ + + diff --git a/src/lib.rs b/src/lib.rs index 94450a5..6916dfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/constants.rs b/src/poseidon2/constants.rs similarity index 100% rename from src/constants.rs rename to src/poseidon2/constants.rs diff --git a/src/poseidon2/mod.rs b/src/poseidon2/mod.rs new file mode 100644 index 0000000..ffe20f5 --- /dev/null +++ b/src/poseidon2/mod.rs @@ -0,0 +1,3 @@ + +mod constants; +pub mod permutation; diff --git a/src/permutation.rs b/src/poseidon2/permutation.rs similarity index 90% rename from src/permutation.rs rename to src/poseidon2/permutation.rs index 042d43a..dc1319d 100644 --- a/src/permutation.rs +++ b/src/poseidon2/permutation.rs @@ -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() diff --git a/src/sponge.rs b/src/sponge.rs index 556b446..ffb37f7 100644 --- a/src/sponge.rs +++ b/src/sponge.rs @@ -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 { +pub fn sponge_felts_pad(hash: Hash, xs: Vec) -> 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 { 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 { //------------------------------------------------------------------------------ // hash of field elements without padding (rate=2, capacity=1) -pub fn sponge_felts_no_pad(xs: Vec) -> F { +pub fn sponge_felts_no_pad(hash: Hash, xs: Vec) -> 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 { 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 { 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 { //------------------------------------------------------------------------------ // hash of u64 elements with the `10*` padding stategy (rate=2, capacity=1) -pub fn sponge_u64_pad(input: Vec) -> F { +pub fn sponge_u64_pad(hash: Hash, input: Vec) -> 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) -> 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) -> 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) -> F { //------------------------------------------------------------------------------ // hash of u64 elements with no padding (rate=2, capacity=1) -pub fn sponge_u64_no_pad(input: Vec) -> F { +pub fn sponge_u64_no_pad(hash: Hash, input: Vec) -> 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) -> 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) -> 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] ); } } diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..ca1b15a --- /dev/null +++ b/src/state.rs @@ -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) + } +} + +//------------------------------------------------------------------------------