diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 863e206f8..4138ed25d 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -14,6 +14,7 @@ harness = false blake3 = "0.3.6" sha3 = "0.9.1" serde = { version = "1.0.114", features = ["derive"] } +math = { path = "../math" } [dev-dependencies] criterion = "0.3.3" diff --git a/crypto/benches/hash.rs b/crypto/benches/hash.rs index f27214f74..419f8ea1e 100644 --- a/crypto/benches/hash.rs +++ b/crypto/benches/hash.rs @@ -8,7 +8,7 @@ pub fn blake3(c: &mut Criterion) { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ]; let mut r = [0u8; 32]; - c.bench_function("Blake3", |bench| { + c.bench_function("hash_blake3", |bench| { bench.iter(|| hash::blake3(black_box(&v), black_box(&mut r))) }); } @@ -20,10 +20,33 @@ pub fn sha3(c: &mut Criterion) { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ]; let mut r = [0u8; 32]; - c.bench_function("Sha3", |bench| { + c.bench_function("hash_sha3", |bench| { bench.iter(|| hash::sha3(black_box(&v), black_box(&mut r))) }); } -criterion_group!(hash_group, blake3, sha3); +pub fn rescue_s(c: &mut Criterion) { + let v: [u8; 32] = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, + ]; + let mut r = [0u8; 32]; + c.bench_function("hash_rescue_s", |bench| { + bench.iter(|| hash::rescue_s(black_box(&v), black_box(&mut r))) + }); +} + +pub fn rescue_d(c: &mut Criterion) { + let v: [u8; 64] = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]; + let mut r = [0u8; 32]; + c.bench_function("hash_rescue_d", |bench| { + bench.iter(|| hash::rescue_d(black_box(&v), black_box(&mut r))) + }); +} + +criterion_group!(hash_group, blake3, sha3, rescue_s, rescue_d); criterion_main!(hash_group); diff --git a/crypto/src/hash/mod.rs b/crypto/src/hash/mod.rs index d60bed80b..146031ac8 100644 --- a/crypto/src/hash/mod.rs +++ b/crypto/src/hash/mod.rs @@ -1,5 +1,8 @@ use sha3::Digest; +mod rescue; +pub use rescue::{rescue_d, rescue_s}; + /// Wrapper around blake3 hash function pub fn blake3(values: &[u8], result: &mut [u8]) { debug_assert!( diff --git a/crypto/src/hash/rescue/double.rs b/crypto/src/hash/rescue/double.rs new file mode 100644 index 000000000..3e2fd729e --- /dev/null +++ b/crypto/src/hash/rescue/double.rs @@ -0,0 +1,251 @@ +use crate::utils::as_bytes; +use math::field::{self, add, exp, mul}; + +/// Function state is set to 6 field elements or 96 bytes; 4 elements are reserved for rate +/// and 2 elements are reserved for capacity. +const STATE_WIDTH: usize = 6; +const STATE_BYTES: usize = STATE_WIDTH * 16; + +/// Two elements (32-bytes) are returned as digest. +const DIGEST_SIZE: usize = 2; + +/// The number of rounds is set to 8 to provide 128-bit security level. +/// computed using algorithm 7 from https://eprint.iacr.org/2020/1143.pdf +const NUM_ROUNDS: usize = 8; + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Rescue hash function for double input; This implementation accepts a 64-byte input +/// and returns a 32-byte digest. +pub fn rescue_d(values: &[u8], result: &mut [u8]) { + debug_assert!( + values.len() <= 64, + "expected 64 or fewer input bytes but received {}", + values.len() + ); + debug_assert!( + result.len() == 32, + "expected result to be exactly 32 bytes but received {}", + result.len() + ); + + // copy values into state and set the remaining state elements to 0 + let mut state = [field::ZERO; STATE_WIDTH]; + #[allow(clippy::cast_ref_to_mut)] + let state_bytes: &mut [u8; STATE_BYTES] = + unsafe { &mut *(&state as *const _ as *mut [u8; STATE_BYTES]) }; + state_bytes[..values.len()].copy_from_slice(values); + + // apply round function 10 times; the round function implementation is based on + // algorithm 3 from https://eprint.iacr.org/2020/1143.pdf + for i in 0..NUM_ROUNDS { + // step 1 + apply_sbox(&mut state); + apply_mds(&mut state); + add_constants(&mut state, i * 2 * STATE_WIDTH); + + // step 2 + apply_inv_sbox(&mut state); + apply_mds(&mut state); + add_constants(&mut state, (i * 2 + 1) * STATE_WIDTH); + } + + // return the result + result.copy_from_slice(as_bytes(&state[..DIGEST_SIZE])); +} + +// HELPER FUNCTIONS +// ================================================================================================ + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn add_constants(state: &mut [u128; STATE_WIDTH], offset: usize) { + for i in 0..STATE_WIDTH { + state[i] = add(state[i], ARK[offset + i]); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_sbox(state: &mut [u128; STATE_WIDTH]) { + for i in 0..STATE_WIDTH { + state[i] = exp(state[i], ALPHA); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_inv_sbox(state: &mut [u128; STATE_WIDTH]) { + // TODO: optimize + for i in 0..STATE_WIDTH { + state[i] = exp(state[i], INV_ALPHA); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_mds(state: &mut [u128; STATE_WIDTH]) { + let mut result = [0u128; STATE_WIDTH]; + let mut temp = [0u128; STATE_WIDTH]; + for i in 0..STATE_WIDTH { + for j in 0..STATE_WIDTH { + temp[j] = mul(MDS[i * STATE_WIDTH + j], state[j]); + } + + for j in 0..STATE_WIDTH { + result[i] = add(result[i], temp[j]); + } + } + state.copy_from_slice(&result); +} + +// CONSTANTS +// ================================================================================================ + +/// S-Box and Inverse S-Box powers; +/// computed using algorithm 6 from https://eprint.iacr.org/2020/1143.pdf +const ALPHA: u128 = 5; +const INV_ALPHA: u128 = 272225893536750770770699646362995969229; + +/// Rescue MDS matrix +/// Computed using algorithm 4 from https://eprint.iacr.org/2020/1143.pdf +const MDS: [u128; STATE_WIDTH * STATE_WIDTH] = [ + 340282366920938463463374557953730612630, + 21493836, + 340282366920938463463374557953736934518, + 914760, + 340282366920938463463374557953744928504, + 364, + 340282366920938463463374557948521959389, + 7809407397, + 340282366920938463463374557950844620457, + 324945621, + 340282366920938463463374557953733852285, + 99463, + 340282366920938463463374556526559624596, + 2132618407920, + 340282366920938463463374557163162978137, + 88084432800, + 340282366920938463463374557950784345879, + 25095280, + 340282366920938463463374197863906102577, + 537966647357139, + 340282366920938463463374358646073999137, + 22165576349400, + 340282366920938463463374557212857010097, + 6174066262, + 340282366920938463463285966851139685903, + 132344277849702072, + 340282366920938463463325536573199985698, + 5448481182864720, + 340282366920938463463374376171390478291, + 1506472167928, + 340282366920938463441758328918057706841, + 32291274613403616174, + 340282366920938463451414421516665416977, + 1329039099788841441, + 340282366920938463463330243139804660633, + 366573514642546, +]; + +/// Rescue round constants; +/// computed using algorithm 5 from https://eprint.iacr.org/2020/1143.pdf +const ARK: [u128; STATE_WIDTH * NUM_ROUNDS * 2] = [ + 232350694689151131917165570858777669544, + 297138716840883070166239111380460167036, + 262280230220923724082396709497064092149, + 172158049344191113832187131208632037738, + 49064466045797039562408393043269857959, + 310779117230843293557874990285120450495, + 256706820970445617734149759518940865107, + 79123538858040670180278455836284339197, + 78750303544367952484014721485273250812, + 288861383492149579433903883762711410179, + 59801749333456280387477464033868461625, + 21443300235508431203706748477819269958, + 58568963110264836729315799795504150465, + 330748576252425315826992430477036516321, + 186265990460580587588657915966473647991, + 33474186560709631768594728335471560699, + 158848462530608412921046130349797355353, + 103951280788776493556470655637893338265, + 143328281743837680325887693977200434046, + 84141533915622931968833899936597847300, + 8289043147167319381038668861607412243, + 182690551456641207603161012621368395791, + 189966993584382842241685332212477020587, + 32137923394454105763485467845755642950, + 37831789571282423629213813309051107559, + 128553631204082467137622394929811125529, + 267986778741944677472811189878493395927, + 16604948458564067211433039503683613987, + 336102510949899388907937615764984494068, + 269515689098362827313089599343791905108, + 299424679105391259942771484229152481303, + 204910193356347483970850685012209050540, + 297547986861132400067173315704469727918, + 90994669428470088728996184833134573519, + 194832530917116381832912394976136685925, + 3544879195102182108390682435201981399, + 339480205126523778084089852053600037139, + 7584482258985997923597941079175892345, + 293411952222390873312400094181647328549, + 199529004542042321671242096609546451065, + 67129123347758775813781826519244753478, + 262358775581253675478636059962684988488, + 214578730175648891816936630380713062555, + 298888476681892954783673663609236117055, + 28713802418311531156758766332916445632, + 1440134829402109711440873134882900954, + 136568912729847804743104940208565395935, + 282333114631262903665175684297593586626, + 179980515973143677823617972256218090691, + 262324617228293661450608983002445445851, + 101457408539557988072857167265007764003, + 135015365700146217343913438445165565670, + 160037359781136723784361845515476884821, + 182530253870899012049936279038476084254, + 135879876810809726132885131537021449499, + 232021530889024386996643355214152586646, + 145764181560102807472161589832442506602, + 30096323905520593555387863391076216460, + 26964230850883304384940372063347292502, + 248723932438838238159920468579438468564, + 294269904099379916907622037481357861347, + 68547751515194812125080398554316505804, + 206967528806115588933607920597265054243, + 218563991130423186053843420486943196637, + 271753381570791699387473121354016967661, + 280821616954361601859332610476339898658, + 10004341245328361103806488533574675264, + 102737972201824925757345477497905200949, + 181579715086871199454198713448655357907, + 334443686013848360201749831728546200670, + 43930702221243327593116820380585481596, + 16744004758332429127464852702179311517, + 310201738135125726809998762242791360596, + 155126893730515639579436939964032992002, + 61238650483248463229462616021804212788, + 6693212157784826508674787451860949238, + 197651057967963974372308220503477603713, + 174221476673212934077040088950046690415, + 287511813733819668564695051918836002922, + 304531189544765525159398110881793396421, + 276777415462914862553995344360435589651, + 241036817921529641113885285343669990717, + 320958231309550951576801366383624382828, + 242260690344880997681123448650535822378, + 201589105262974747061391276271612166799, + 21009766855942890883171267876432289297, + 303226336248222109074995022589884483065, + 105515432862530091605210357969101266504, + 235097661610089805414814372959229370626, + 210361497167001742816223425802317150493, + 218546747003262668455051521918398855294, + 280724473534270362895764829061545243190, + 179926408118748249708833901850481685351, + 168859451670725335987760025077648496937, + 127174659756870191527945451601624140498, + 290826558340641225374953827677533570165, +]; diff --git a/crypto/src/hash/rescue/mod.rs b/crypto/src/hash/rescue/mod.rs new file mode 100644 index 000000000..c38ec80d6 --- /dev/null +++ b/crypto/src/hash/rescue/mod.rs @@ -0,0 +1,8 @@ +mod single; +pub use single::rescue_s; + +mod double; +pub use double::rescue_d; + +#[cfg(test)] +mod tests; diff --git a/crypto/src/hash/rescue/single.rs b/crypto/src/hash/rescue/single.rs new file mode 100644 index 000000000..4a62cff25 --- /dev/null +++ b/crypto/src/hash/rescue/single.rs @@ -0,0 +1,247 @@ +use crate::utils::as_bytes; +use math::field::{self, add, exp, mul}; + +/// Function state is set to 4 field elements or 64 bytes. 2 elements are reserved for rate +/// and 2 elements are reserved for capacity. +const STATE_WIDTH: usize = 4; +const STATE_BYTES: usize = STATE_WIDTH * 16; + +/// Two elements (32-bytes) are returned as digest. +const DIGEST_SIZE: usize = 2; + +/// The number of rounds is set to 14 to provide 128-bit security level. +/// computed using algorithm 7 from https://eprint.iacr.org/2020/1143.pdf +const NUM_ROUNDS: usize = 14; + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Rescue hash function for small inputs; This implementation accepts a 32-byte input +/// and returns a 32-byte digest. +pub fn rescue_s(values: &[u8], result: &mut [u8]) { + debug_assert!( + values.len() <= 32, + "expected 32 or fewer input bytes but received {}", + values.len() + ); + debug_assert!( + result.len() == 32, + "expected result to be exactly 32 bytes but received {}", + result.len() + ); + + // copy values into state and set the remaining state elements to 0 + let mut state = [field::ZERO; STATE_WIDTH]; + #[allow(clippy::cast_ref_to_mut)] + let state_bytes: &mut [u8; STATE_BYTES] = + unsafe { &mut *(&state as *const _ as *mut [u8; STATE_BYTES]) }; + state_bytes[..values.len()].copy_from_slice(values); + + // apply round function 14 times; the round function implementation is based on + // algorithm 3 from https://eprint.iacr.org/2020/1143.pdf + for i in 0..NUM_ROUNDS { + // step 1 + apply_sbox(&mut state); + apply_mds(&mut state); + add_constants(&mut state, i * 2 * STATE_WIDTH); + + // step 2 + apply_inv_sbox(&mut state); + apply_mds(&mut state); + add_constants(&mut state, (i * 2 + 1) * STATE_WIDTH); + } + + // return the result + result.copy_from_slice(as_bytes(&state[..DIGEST_SIZE])); +} + +// HELPER FUNCTIONS +// ================================================================================================ + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn add_constants(state: &mut [u128; STATE_WIDTH], offset: usize) { + for i in 0..STATE_WIDTH { + state[i] = add(state[i], ARK[offset + i]); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_sbox(state: &mut [u128; STATE_WIDTH]) { + for i in 0..STATE_WIDTH { + state[i] = exp(state[i], ALPHA); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_inv_sbox(state: &mut [u128; STATE_WIDTH]) { + // TODO: optimize + for i in 0..STATE_WIDTH { + state[i] = exp(state[i], INV_ALPHA); + } +} + +#[inline(always)] +#[allow(clippy::needless_range_loop)] +fn apply_mds(state: &mut [u128; STATE_WIDTH]) { + let mut result = [0u128; STATE_WIDTH]; + let mut temp = [0u128; STATE_WIDTH]; + for i in 0..STATE_WIDTH { + for j in 0..STATE_WIDTH { + temp[j] = mul(MDS[i * STATE_WIDTH + j], state[j]); + } + + for j in 0..STATE_WIDTH { + result[i] = add(result[i], temp[j]); + } + } + state.copy_from_slice(&result); +} + +// CONSTANTS +// ================================================================================================ + +/// S-Box and Inverse S-Box powers; +/// computed using algorithm 6 from https://eprint.iacr.org/2020/1143.pdf +const ALPHA: u128 = 3; +const INV_ALPHA: u128 = 226854911280625642308916371969163307691; + +/// Rescue MDS matrix +/// Computed using algorithm 4 from https://eprint.iacr.org/2020/1143.pdf +const MDS: [u128; STATE_WIDTH * STATE_WIDTH] = [ + 340282366920938463463374557953744960808, + 1080, + 340282366920938463463374557953744961147, + 40, + 340282366920938463463374557953744932377, + 42471, + 340282366920938463463374557953744947017, + 1210, + 340282366920938463463374557953744079447, + 1277640, + 340282366920938463463374557953744532108, + 33880, + 340282366920938463463374557953720263017, + 35708310, + 340282366920938463463374557953733025977, + 925771, +]; + +/// Rescue round constants; +/// computed using algorithm 5 from https://eprint.iacr.org/2020/1143.pdf +const ARK: [u128; STATE_WIDTH * NUM_ROUNDS * 2] = [ + 252629594110556276281235816992330349983, + 121163867507455621442731872354015891839, + 244623479936175870778515556108748234900, + 181999122442017949289616572388308120964, + 130035663054758320517176088024859935575, + 274932696133623013607933255959111946013, + 130096286077538976127585373664362805864, + 209506446014122131232133742654202790201, + 51912929769931267810162308005565017268, + 202610584823002946089528994694473145326, + 295992101426532309592836871256175669136, + 313404555247438968545340310449654540090, + 137671644572045862038757754124537020379, + 29113322527929260506148183779738829778, + 98634637270536166954048957710629281939, + 90484051915535813802492401077197602516, + 193753019093186599897082621380539177732, + 88328997664086495053801384396180288832, + 134379598544046716907663161480793367313, + 50911186425769400405474055284903795891, + 12945394282446072785093894845750344239, + 110650301505380365788620562912149942995, + 154214463184362737046953674082326221874, + 306646039504788072647764955304698381135, + 279745705918489041552127329708931301079, + 111293612078035530300709391234153848359, + 18110020378502034462498434861690576309, + 41797883582559360517115865611622162330, + 333888808893608021579859508112201825908, + 291192643991850989562610634125476905625, + 115042354025120848770557866862388897952, + 281483497320099569269754505499721335457, + 172898111753678285350206449646444309824, + 202661860135906394577472615378659980424, + 141885268042225970011312316000526746741, + 270195331267041521741794476882482499817, + 196457080224171120865903216527675657315, + 56730777565482395039564396246195716949, + 4886253806084919544862202000090732791, + 147384194551383352824518757380733021990, + 119476237236248181092343711369608370324, + 182869361251406039022577235058473348729, + 45308522364899994411952744852450066909, + 15438528253368638146901598290564135576, + 130060283207960095436997328133261743365, + 83953475955438079154228277940680487556, + 328659226769709797512044291035930357326, + 228749522131871685132212950281473676382, + 46194972462682851176957413491161426658, + 296333983305826854863835978241833143471, + 138957733159616849361016139528307260698, + 67842086763518777676559492559456199109, + 45580040156133202522383315452912604930, + 67567837934606680937620346425373752595, + 202860989528104560171546683198384659325, + 22630500510153322451285114937258973361, + 324160761097464842200838878419866223614, + 338466547889555546143667391979278153877, + 189171173535649401433078628567098769571, + 162173266902020502126600904559755837464, + 136209703129442038834374731074825683052, + 61998071517031804812562190829480056772, + 307309080039351604461536918194634835054, + 26708622949278137915061761772299784349, + 129516553661717764361826568456881002617, + 224023580754958002183324313900177991825, + 17590440203644538688189654586240082513, + 135610063062379124269847491297867667710, + 146865534517067293442442506551295645352, + 238139104484181583196227119098779158429, + 39300761479713744892853256947725570060, + 54114440355764484955231402374312070440, + 222758070305343916663075833184045878425, + 323840793618712078836672915700599856701, + 103586087979277053032666296091805459741, + 160263698024385270625527195046420579470, + 76620453913654705501329735586535761337, + 117793948142462197480091377165008040465, + 86998218841589258723143213495722487114, + 203188618662906890442620821687773659689, + 313098786815741054633864043424353402357, + 133085673687338880872979866135939079867, + 219888424885634764555580944265544343421, + 5893221169005427793512575133564978746, + 123830602624063632344313821515642988189, + 99030942908036387138287682010525589136, + 181549003357535890945363082242256699137, + 152424978799328476472358562493335008209, + 274481943862544603168725464029979191673, + 4975004592976331754728718693838357226, + 101850445399221640701542169338886750079, + 230325699922192981509673754024218912397, + 50419227750575087142720761582056939006, + 112444234528764731925178653200320603078, + 312169855609816651638877239277948636598, + 204255114617024487729019111502542629940, + 95797476952346525817251811755749179939, + 306977388944722094681694167558392710189, + 300754874465668732709232449646112602172, + 25567836410351071106804347269705784680, + 129659188855548935155840545784705385753, + 228441586459539470069565041053012869566, + 178382533299631576605259357906020320778, + 274458637266680353971597477639962034316, + 280059913840028448065185235205261648486, + 246537412674731137211182698562269717969, + 259930078572522349821084822750913159564, + 186061633995391650657311511040160727356, + 179777566992900315528995607912777709520, + 209753365793154515863736129686836743468, + 270445008049478596978645420017585428243, + 70998387591825316724846035292940615733, +]; diff --git a/crypto/src/hash/rescue/tests.rs b/crypto/src/hash/rescue/tests.rs new file mode 100644 index 000000000..b70f86f27 --- /dev/null +++ b/crypto/src/hash/rescue/tests.rs @@ -0,0 +1 @@ +// TODO: add tests diff --git a/crypto/src/utils.rs b/crypto/src/utils.rs index 4958f9af1..c7783c905 100644 --- a/crypto/src/utils.rs +++ b/crypto/src/utils.rs @@ -1,3 +1,5 @@ +use std::{mem, slice}; + pub fn uninit_vector(length: usize) -> Vec { let mut vector = Vec::with_capacity(length); unsafe { @@ -5,3 +7,10 @@ pub fn uninit_vector(length: usize) -> Vec { } vector } + +pub fn as_bytes(values: &[T]) -> &[u8] { + let value_size = mem::size_of::(); + let result = + unsafe { slice::from_raw_parts(values.as_ptr() as *const u8, values.len() * value_size) }; + result +}