diff --git a/Cargo.toml b/Cargo.toml index 39f87e8..e4b0850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ ark-serialize = { version = "0.4.2", default-features = false } rand_core = { version = "0.6.4", default-features = false, optional = true } rand_chacha = { version = "0.3.1", default-features = false } zeroize = { version = "1.7.0", default-features = false } +hmac = {version = "0.12.1", default-features = false } +digest = { version = "0.10.7", default-features = false } # Curves ark-secp256r1 = { version = "0.4.0", default-features = false, optional = true } ark-ed25519 = { version = "0.4.0", default-features = false, optional = true } @@ -26,11 +28,9 @@ fflonk = { git = "https://github.com/w3f/fflonk", default-features = false, opti ring-proof = { package = "ring", git = "https://github.com/w3f/ring-proof", default-features = false, optional = true } merlin = { version = "3.0.0", default-features = false, optional = true } -hmac = "0.12.1" -hex = "0.4.3" - [dev-dependencies] ark-ed25519 = "0.4.0" +hex = "0.4.3" [features] default = [ "std" ] diff --git a/src/ietf.rs b/src/ietf.rs index c2ebfb3..dc551a8 100644 --- a/src/ietf.rs +++ b/src/ietf.rs @@ -84,7 +84,7 @@ impl ark_serialize::Valid for Signature { impl Signature { /// Proof to hash as defined by RFC9381 section 5.2 - pub fn hash(&self) -> S::Hash { + pub fn hash(&self) -> HashOutput { self.gamma.hash() } diff --git a/src/lib.rs b/src/lib.rs index a5af618..87e117d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ use ark_ff::PrimeField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{vec, vec::Vec}; -use core::ops::{Index, RangeFrom, RangeFull, RangeTo}; +use digest::Digest; pub mod ietf; pub mod pedersen; @@ -40,6 +40,8 @@ pub type ScalarField = <::Affine as AffineRepr>::ScalarField; pub type BaseField = <::Affine as AffineRepr>::BaseField; pub type AffinePoint = ::Affine; +pub type HashOutput = digest::Output<::Hasher>; + /// Verification error(s) pub enum Error { VerificationFailure, @@ -64,14 +66,8 @@ pub trait Suite: Copy + Clone { /// by the `AffineRepr` bound. type Affine: AffineRepr; - /// Hash output. - type Hash: Index - + Index, Output = [u8]> - + Index, Output = [u8]> - + Index; - - /// Hasher. - fn hash(data: &[u8]) -> Self::Hash; + /// Hasher output. + type Hasher: Digest; /// Nonce generation as described by RFC-9381 section 5.4.2. /// @@ -107,7 +103,7 @@ pub trait Suite: Copy + Clone { }); buf.extend_from_slice(ad); buf.push(DOM_SEP_END); - let hash = &Self::hash(&buf)[..Self::CHALLENGE_LEN]; + let hash = &utils::hash::(&buf)[..Self::CHALLENGE_LEN]; ScalarField::::from_be_bytes_mod_order(hash) } @@ -118,13 +114,13 @@ pub trait Suite: Copy + Clone { utils::hash_to_curve_tai::(data, false) } - fn point_to_hash(pt: &AffinePoint) -> Self::Hash { + fn point_to_hash(pt: &AffinePoint) -> HashOutput { const DOM_SEP_START: u8 = 0x03; const DOM_SEP_END: u8 = 0x00; let mut buf = vec![Self::SUITE_ID, DOM_SEP_START]; Self::point_encode(pt, &mut buf); buf.push(DOM_SEP_END); - Self::hash(&buf) + utils::hash::(&buf) } #[inline(always)] @@ -209,7 +205,7 @@ impl Secret { /// /// The `seed` is hashed using the `Suite::hash` to construct the secret scalar. pub fn from_seed(seed: &[u8]) -> Self { - let bytes = S::hash(seed); + let bytes = utils::hash::(seed); let scalar = ScalarField::::from_le_bytes_mod_order(&bytes[..]); Self::from_scalar(scalar) } @@ -268,7 +264,7 @@ impl Output { } /// Hash using `[Suite::point_to_hash]`. - pub fn hash(&self) -> S::Hash { + pub fn hash(&self) -> HashOutput { S::point_to_hash(&self.0) } } @@ -300,8 +296,7 @@ mod tests { let input = Input::from(random_val(None)); let output = secret.output(input); - let hash = output.hash(); - let expected = "f30ceafdbd80a9280547a9d44a88c188"; - assert_eq!(expected, hex::encode(&hash[..16])); + let expected = "08ffdc9d48f6553c0352b92a233a8101a69ac9f4dcb7f9e2c9c43d46a441c331"; + assert_eq!(expected, hex::encode(output.hash())); } } diff --git a/src/suites/bandersnatch.rs b/src/suites/bandersnatch.rs index b14e344..379006c 100644 --- a/src/suites/bandersnatch.rs +++ b/src/suites/bandersnatch.rs @@ -66,11 +66,7 @@ impl Suite for BandersnatchSha512 { const CHALLENGE_LEN: usize = 32; type Affine = ark_ed_on_bls12_381_bandersnatch::SWAffine; - type Hash = [u8; 64]; - - fn hash(data: &[u8]) -> Self::Hash { - utils::sha512(data) - } + type Hasher = sha2::Sha512; } impl PedersenSuite for BandersnatchSha512 { diff --git a/src/suites/ed25519.rs b/src/suites/ed25519.rs index c60424c..0a53832 100644 --- a/src/suites/ed25519.rs +++ b/src/suites/ed25519.rs @@ -58,9 +58,5 @@ impl Suite for Ed25519Sha512 { const CHALLENGE_LEN: usize = 16; type Affine = ark_ed25519::EdwardsAffine; - type Hash = [u8; 64]; - - fn hash(data: &[u8]) -> Self::Hash { - utils::sha512(data) - } + type Hasher = sha2::Sha512; } diff --git a/src/suites/secp256.rs b/src/suites/secp256.rs index 7a6a20b..35b2992 100644 --- a/src/suites/secp256.rs +++ b/src/suites/secp256.rs @@ -60,11 +60,7 @@ impl Suite for P256Sha256Tai { const CHALLENGE_LEN: usize = 16; type Affine = ark_secp256r1::Affine; - type Hash = [u8; 32]; - - fn hash(data: &[u8]) -> Self::Hash { - utils::sha256(data) - } + type Hasher = sha2::Sha256; fn nonce(sk: &ScalarField, pt: Input) -> ScalarField { utils::nonce_rfc_6979::(sk, &pt.0) diff --git a/src/utils.rs b/src/utils.rs index 663a001..52be525 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,6 @@ use crate::{AffinePoint, ScalarField, Suite}; use ark_ff::PrimeField; +use digest::{core_api::BlockSizeUser, Digest}; #[cfg(not(feature = "std"))] use ark_std::vec::Vec; @@ -26,40 +27,22 @@ macro_rules! suite_types { }; } -/// SHA-256 hasher +// Generic hash wrapper. #[inline(always)] -pub fn sha256(input: &[u8]) -> [u8; 32] { - use sha2::{Digest, Sha256}; - let mut hasher = Sha256::new(); - hasher.update(input); - let result = hasher.finalize(); - let mut h = [0u8; 32]; - h.copy_from_slice(&result); - h +pub(crate) fn hash(data: &[u8]) -> digest::Output { + H::new().chain_update(data).finalize() } -/// SHA-512 hasher +/// Generic HMAC wrapper. #[inline(always)] -pub fn sha512(input: &[u8]) -> [u8; 64] { - use sha2::{Digest, Sha512}; - let mut hasher = Sha512::new(); - hasher.update(input); - let result = hasher.finalize(); - let mut h = [0u8; 64]; - h.copy_from_slice(&result); - h -} - -/// HMAC -pub fn hmac(sk: &[u8], data: &[u8]) -> Vec { - use hmac::{Hmac, Mac}; - use sha2::Sha256; - // Create alias for HMAC-SHA256 - type HmacSha256 = Hmac; - let mut mac = HmacSha256::new_from_slice(sk).expect("HMAC can take key of any size"); - mac.update(data); - let result = mac.finalize(); - result.into_bytes().to_vec() +pub(crate) fn hmac(sk: &[u8], data: &[u8]) -> Vec { + use hmac::{Mac, SimpleHmac}; + SimpleHmac::::new_from_slice(sk) + .expect("HMAC can take key of any size") + .chain_update(data) + .finalize() + .into_bytes() + .to_vec() } /// Try-And-Increment (TAI) method as defined by RFC9381 section 5.4.1.1. @@ -83,6 +66,9 @@ pub fn hash_to_curve_tai(data: &[u8], point_be_encoding: bool) -> Opti const DOM_SEP_BACK: u8 = 0x00; let mod_size = <<::BaseField as Field>::BasePrimeField as PrimeField>::MODULUS_BIT_SIZE as usize / 8; + if S::Hasher::output_size() < mod_size { + return None; + } let mut buf = [&[S::SUITE_ID, DOM_SEP_FRONT], data, &[0x00, DOM_SEP_BACK]].concat(); let ctr_pos = buf.len() - 2; @@ -90,7 +76,7 @@ pub fn hash_to_curve_tai(data: &[u8], point_be_encoding: bool) -> Opti for ctr in 0..256 { // Modify ctr value buf[ctr_pos] = ctr as u8; - let hash = &S::hash(&buf)[..]; + let hash = &hash::(&buf)[..]; if hash.len() < mod_size { return None; } @@ -125,11 +111,11 @@ pub fn hash_to_curve_tai(data: &[u8], point_be_encoding: bool) -> Opti /// This function panics if `Hash` is less than 32 bytes. pub fn nonce_rfc_8032(sk: &ScalarField, input: &AffinePoint) -> ScalarField { let raw = encode_scalar::(sk); - let sk_hash = &S::hash(&raw)[32..]; + let sk_hash = &hash::(&raw)[32..]; let raw = encode_point::(input); let v = [sk_hash, &raw[..]].concat(); - let h = &S::hash(&v)[..]; + let h = &hash::(&v)[..]; S::scalar_decode(h) } @@ -142,9 +128,12 @@ pub fn nonce_rfc_8032(sk: &ScalarField, input: &AffinePoint) -> /// /// The algorithm generate the nonce value in a deterministic /// pseudorandom fashion. -pub fn nonce_rfc_6979(sk: &ScalarField, input: &AffinePoint) -> ScalarField { +pub fn nonce_rfc_6979(sk: &ScalarField, input: &AffinePoint) -> ScalarField +where + S::Hasher: BlockSizeUser, +{ let raw = encode_point::(input); - let h1 = S::hash(&raw); + let h1 = hash::(&raw); let v = [1; 32]; let k = [0; 32]; @@ -152,20 +141,20 @@ pub fn nonce_rfc_6979(sk: &ScalarField, input: &AffinePoint) -> // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)) let x = encode_scalar::(sk); let raw = [&v[..], &[0x00], &x[..], &h1[..]].concat(); - let k = hmac(&k, &raw); + let k = hmac::(&k, &raw); // V = HMAC_K(V) - let v = hmac(&k, &v); + let v = hmac::(&k, &v); // K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1)) let raw = [&v[..], &[0x01], &x[..], &h1[..]].concat(); - let k = hmac(&k, &raw); + let k = hmac::(&k, &raw); // V = HMAC_K(V) - let v = hmac(&k, &v); + let v = hmac::(&k, &v); // TODO: loop until 1 < k < q - let v = hmac(&k, &v); + let v = hmac::(&k, &v); S::scalar_decode(&v) } @@ -198,11 +187,7 @@ pub(crate) mod testing { const CHALLENGE_LEN: usize = 16; type Affine = ark_ed25519::EdwardsAffine; - type Hash = [u8; 64]; - - fn hash(data: &[u8]) -> Self::Hash { - utils::sha512(data) - } + type Hasher = sha2::Sha256; } suite_types!(TestSuite);