From 88a662f0343e7cb153708ee901328776502894a6 Mon Sep 17 00:00:00 2001 From: M Alghazwi Date: Tue, 28 Jan 2025 14:12:59 +0100 Subject: [PATCH] impl skyscraper --- .gitignore | 1 + README.md | 1 + src/hash.rs | 2 +- src/lib.rs | 1 + src/skyscraper/constants.rs | 90 +++++++++++++++++++++ src/skyscraper/mod.rs | 2 + src/skyscraper/permutation.rs | 145 ++++++++++++++++++++++++++++++++++ 7 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/skyscraper/constants.rs create mode 100644 src/skyscraper/mod.rs create mode 100644 src/skyscraper/permutation.rs diff --git a/.gitignore b/.gitignore index ad96b5f..5397afc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.idea Cargo.lock /target /tmp diff --git a/README.md b/README.md index 17c6da3..a152c94 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Hash functions implemented: - [x] Poseidon2 - [x] Griffin +- [x] skyscraper 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 diff --git a/src/hash.rs b/src/hash.rs index b94c57e..80911a3 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -3,7 +3,7 @@ use ark_ff::prelude::{Zero}; use ark_bn254::Fr as F; use crate::state::*; -use crate::poseidon2; +use crate::{poseidon2, skyscraper}; use crate::griffin; //------------------------------------------------------------------------------ diff --git a/src/lib.rs b/src/lib.rs index 6916dfe..b8acc0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ pub mod hash; pub mod poseidon2; pub mod griffin; pub mod sponge; +pub mod skyscraper; diff --git a/src/skyscraper/constants.rs b/src/skyscraper/constants.rs new file mode 100644 index 0000000..07f4017 --- /dev/null +++ b/src/skyscraper/constants.rs @@ -0,0 +1,90 @@ +use std::str::FromStr; +use lazy_static::lazy_static; +use ark_bn254::Fr as F; +use ark_ff::BigInteger256; + + +/// enum for deciding which function (Square or Bar) to apply +#[derive(Clone, Copy, Debug)] +pub enum FunctionBlock { + Square, + Bar, +} + +/// round schedule +pub static RF1: [FunctionBlock; 10] = [ + FunctionBlock::Square, FunctionBlock::Square, + FunctionBlock::Bar, FunctionBlock::Bar, + FunctionBlock::Square, FunctionBlock::Square, + FunctionBlock::Bar, FunctionBlock::Bar, + FunctionBlock::Square, FunctionBlock::Square, +]; + +/// round constants - same as in: +/// https://extgit.isec.tugraz.at/krypto/zkfriendlyhashzoo/-/blob/master/plain_impls/src/skyscraper/skyscraper_instances.rs?ref_type=heads +static RC_STR: [&str; 24] = [ + "17829420340877239108687448009732280677191990375576158938221412342251481978692", + "27740342931201890067831390843279536630457710544396725670188095857896839417202", + "17048088173265532689680903955395019356591870902241717143279822196003888806966", + "109512792282736997633398631034649037613028427788284511060520396554381700616124", + "23518768991468467328187394347260979305359711922005254253047385842741274989784", + "95360373645575887695357714105933674592754581048282220961740831584356266637451", + "57106046715138585370392400429108362862843547132381623658436718362793140581845", + "16971509144034029782226530622087626979814683266929655790026304723118124142299", + "8608910393531852188108777530736778805001620473682472554749734455948859886057", + "54566392379700209585884878067585451869449334062644668287971552334629853792764", + "18708129585851494907644197977764586873688181219062643217509404046560774277231", + "52159802752268413629255578890890486811485406260370834838036769779232029980820", + "98108525134123848500172941527936985409086875223276518679938863940387852105202", + "105831033594660236721345339515389948186304708551041643590872398526195732523291", + "53084450331558915295247017186532447841918727727492403087452333633170905880952", + "78730946611419899835403512890231154575719512053287438310527615801825503526967", + "62089842541186043938517187437087053794210809382724083686360536771123796704819", + "32303085017979849099049635709265581104054174293154472699090841350494692332148", + "19361794324495443451354916303398190341881571975219162871160427826227778850994", + "65021267664773559966759214868166670507376995901124257419858229816098767301789", + "94847021352352647235478120180321422709509900436733319635143815989658015262598", + "51591271359432809566841356156562526830388219805637947403945613063492005256674", + "44534956566050763472510245910556224585100739093572801527559057220740673520964", + "84085239597197409225577945757724209425761279846653606664394225962327262179862", +]; + +/// same as above but in raw representation (just for checking consistency) +static RC_STR_RAW: [&str; 24] = [ + "17829420340877239108687448009732280677191990375576158938221412342251481978692", + "5852100059362614845584985098022261541909346143980691326489891671321030921585", + "17048088173265532689680903955395019356591870902241717143279822196003888806966", + "71577923540621522166602308362662170286605786204339342029375621502658138039", + "1630526119629192105940988602003704216811347521589219909349181656165466494167", + "7807402158218786806372091124904574238561123446618083586948014838053032654983", + "13329560971460034925899588938593812685746818331549554971040309989641523590611", + "16971509144034029782226530622087626979814683266929655790026304723118124142299", + "8608910393531852188108777530736778805001620473682472554749734455948859886057", + "10789906636021659141392066577070901692352605261812599600575143961478236801530", + "18708129585851494907644197977764586873688181219062643217509404046560774277231", + "8383317008589863184762767400375936634388677459538766150640361406080412989586", + "10555553646766747611187318546907885054893417621612381305146047194084618122734", + "18278062107303135832359716534360847832111250949377506216079581779892498540823", + "9307964587880364850754205696017897664821998926660334400055925260019288889718", + "13066217995902074168664295654459329310074418852039335279433003242098078040116", + "18313356797507493494024375946572503617114080581892014998964128397972179713585", + "10414842146140573876803229964008306015505809892738438355392637163918883836531", + "19361794324495443451354916303398190341881571975219162871160427826227778850994", + "21244781921095009522266403377652120330280267100292188732461821442947150310555", + "7294049864995546346492497199292322355316442835069182260350999243354781280130", + "7814785615754259122348544666047976653291491004805878716549204690340388265440", + "758470822372213028017434420041674408004010292740732840162648847589056529730", + "18420510981679583558838728521952384160116186645405503633299613402599836693011", +]; + +lazy_static! { + pub static ref RC: Vec = RC_STR + .iter() + .map(|s| F::new_unchecked(BigInteger256::from_str(s).unwrap())) + .collect(); + + pub static ref RC_RAW: Vec = RC_STR_RAW + .iter() + .map(|s| F::new_unchecked(BigInteger256::from_str(s).unwrap())) + .collect(); +} \ No newline at end of file diff --git a/src/skyscraper/mod.rs b/src/skyscraper/mod.rs new file mode 100644 index 0000000..3fe5cdf --- /dev/null +++ b/src/skyscraper/mod.rs @@ -0,0 +1,2 @@ +pub mod permutation; +mod constants; \ No newline at end of file diff --git a/src/skyscraper/permutation.rs b/src/skyscraper/permutation.rs new file mode 100644 index 0000000..30cb9dd --- /dev/null +++ b/src/skyscraper/permutation.rs @@ -0,0 +1,145 @@ +use ark_bn254::{Fr as F}; +use ark_ff::{BigInteger256, Field, One, PrimeField}; +use core::str::FromStr; +use crate::skyscraper::constants::{FunctionBlock, RF1, RC, RC_RAW}; + +pub fn bars_inplace_mont(x: &mut F) { + // x → two 128‐bit chunks. + let bi = x.0; + let limbs = bi.0; + let mut data = [0u128; 2]; + data[0] = (limbs[0] as u128) | ((limbs[1] as u128) << 64); + data[1] = (limbs[2] as u128) | ((limbs[3] as u128) << 64); + + let t_function = |value: u128| { + let t1 = ((value & 0x80808080808080808080808080808080) >> 7) | ((value & 0x7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F) << 1); //circular left rot by 1 + let t2 = ((value & 0xC0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0) >> 6) | ((value & 0x3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F) << 2); //circular left rot by 2 + let t3 = ((value & 0xE0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0) >> 5) | ((value & 0x1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F) << 3); //circular left rot by 3 + let tmp = (!t1 & t2 & t3) ^ value; + ((tmp & 0x80808080808080808080808080808080) >> 7) | ((tmp & 0x7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F) << 1) // Final left rot by 1 + }; + + // T‐function to each chunk. + let tmp_lo = t_function(data[1]); + let tmp_hi = t_function(data[0]); + data[0] = tmp_lo; + data[1] = tmp_hi; + + // reduce + reduce_small::(&mut data); + + // put back into x + let (lo, hi) = (data[0], data[1]); + let mut out = BigInteger256::new([0, 0, 0, 0]); + out.0[0] = (lo & 0xFFFFFFFFFFFFFFFF) as u64; + out.0[1] = (lo >> 64) as u64; + out.0[2] = (hi & 0xFFFFFFFFFFFFFFFF) as u64; + out.0[3] = (hi >> 64) as u64; + + *x = F::new_unchecked(out) +} + +// same reduction strategy as in the zkfriendlyhashzoo +fn reduce_small(lhs: &mut [u128; 2]) { + let p = F::characteristic(); // same as Modulus + let pa = p.as_ref(); + // prime in two 128‐bit limbs + let mut prime = [0u128; 2]; + prime[0] = (pa[0] as u128) | ((pa[1] as u128) << 64); + prime[1] = (pa[2] as u128) | ((pa[3] as u128) << 64); + + loop { + for idx in (0..2).rev() { + if lhs[idx] < prime[idx] { + return; + } + if lhs[idx] > prime[idx] { + sub_full(lhs, &prime); + break; + } + if idx == 0 && lhs[idx] == prime[idx] { + lhs[0] = 0; + lhs[1] = 0; + return; + } + } + } +} + +pub fn sub_full(lhs: &mut [u128], rhs: &[u128]) { + let mut overflow: bool; + let mut overflow_part: u128; + (lhs[0], overflow) = lhs[0].overflowing_sub(rhs[0]); + overflow_part = if overflow {1} else {0}; + + for index in 1..rhs.len(){ + (lhs[index], overflow) = lhs[index].overflowing_sub(overflow_part); + overflow_part = if overflow {1} else {0}; + (lhs[index], overflow) = lhs[index].overflowing_sub(rhs[index]); + incr(&mut overflow_part, overflow); + } +} +#[inline(always)] +pub fn incr(left: &mut u128, right: bool){ + if right { + *left += 1; + } +} + + +fn square_inplace(x: &mut F) { + *x *= *x; +} + +pub fn permute(input: [F; 2]) -> [F; 2] { + let mut current_state = input; + let mut left = current_state[0]; + let mut right = current_state[1]; + + for (i, &fun) in RF1.iter().enumerate() { + // s‐box on `left` + match fun { + FunctionBlock::Square => square_inplace(&mut left), + FunctionBlock::Bar => bars_inplace_mont(&mut left), + } + + if i > 0 && i < (RF1.len() - 1) { + right += RC_RAW[i - 1]; + } + + // combine + left += right; + + // the feistel rotation + right = current_state[0]; + current_state[0] = left; + } + current_state[1] = right; + current_state +} + +pub fn compress(x: F, y: F) -> F { + let p_out = permute([x, y]); + let out = x + p_out[0]; + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_k_permutation() { + let init = [F::from(1234u64), F::from(5678u64)]; + let init_mont = [F::new_unchecked(init[0].into_bigint()), F::new_unchecked(init[1].into_bigint())]; + let out = permute(init_mont); + println!("Permutation on (1234,5678) => ({},{})", out[0].0, out[1].0); + let expected = [ + BigInteger256::from_str("10398388528337208913702213361515546865573572771332206462397283188708690721181").unwrap(), + BigInteger256::from_str("21827939006013637437091304277086947125806852726479468249428384625000968262245").unwrap() + ]; + + assert_eq!(out[0].0, expected[0]); + assert_eq!(out[1].0, expected[1]); + } +} \ No newline at end of file