From c268944f51e7aa3b6263e36f78f4e3b9f74a5ec9 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 23 May 2025 23:10:33 +0200 Subject: [PATCH 1/8] Rename VOPRF to OPRF --- .github/workflows/elliptic-curve.yml | 4 ++-- elliptic-curve/Cargo.toml | 4 ++-- elliptic-curve/src/lib.rs | 8 ++++---- elliptic-curve/src/oprf.rs | 30 ++++++++++++++++++++++++++++ elliptic-curve/src/voprf.rs | 27 ------------------------- 5 files changed, 38 insertions(+), 35 deletions(-) create mode 100644 elliptic-curve/src/oprf.rs delete mode 100644 elliptic-curve/src/voprf.rs diff --git a/.github/workflows/elliptic-curve.yml b/.github/workflows/elliptic-curve.yml index ac4bdccdc..57d4d4c9a 100644 --- a/.github/workflows/elliptic-curve.yml +++ b/.github/workflows/elliptic-curve.yml @@ -45,16 +45,16 @@ jobs: - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features ecdh - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features hash2curve - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features jwk + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features oprf - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pem - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pkcs8 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features sec1 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features serde - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features voprf - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,arithmetic - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,arithmetic,pkcs8 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,serde - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features arithmetic,serde - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,digest,ecdh,hash2curve,jwk,pem,pkcs8,sec1,serde,voprf + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc,digest,ecdh,hash2curve,jwk,oprf,pem,pkcs8,sec1,serde minimal-versions: # Temporarily disabled until elliptic-curve 0.13.0-pre.0 is published diff --git a/elliptic-curve/Cargo.toml b/elliptic-curve/Cargo.toml index f60db0eca..f9b545a4d 100644 --- a/elliptic-curve/Cargo.toml +++ b/elliptic-curve/Cargo.toml @@ -66,10 +66,10 @@ hash2curve = ["arithmetic", "digest"] ecdh = ["arithmetic", "digest", "dep:hkdf"] group = ["dep:group", "ff"] jwk = ["dep:base64ct", "dep:serde_json", "alloc", "serde", "zeroize/alloc"] +oprf = ["digest", "hash2curve"] pkcs8 = ["dep:pkcs8", "sec1"] pem = ["dep:pem-rfc7468", "alloc", "arithmetic", "pkcs8", "sec1/pem"] serde = ["dep:serdect", "alloc", "pkcs8", "sec1/serde"] -voprf = ["digest", "hash2curve"] [package.metadata.docs.rs] -features = ["bits", "ecdh", "hash2curve", "jwk", "pem", "std", "voprf"] +features = ["bits", "ecdh", "hash2curve", "jwk", "oprf", "pem", "std"] diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index ff5acd88f..8e5ccacaf 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -112,8 +112,8 @@ mod public_key; #[cfg(feature = "jwk")] mod jwk; -#[cfg(feature = "voprf")] -mod voprf; +#[cfg(feature = "oprf")] +mod oprf; pub use crate::{ error::{Error, Result}, @@ -146,8 +146,8 @@ pub use crate::jwk::{JwkEcKey, JwkParameters}; #[cfg(feature = "pkcs8")] pub use pkcs8; -#[cfg(feature = "voprf")] -pub use crate::voprf::VoprfParameters; +#[cfg(feature = "oprf")] +pub use crate::oprf::OprfParameters; use core::{ fmt::Debug, diff --git a/elliptic-curve/src/oprf.rs b/elliptic-curve/src/oprf.rs new file mode 100644 index 000000000..e2d7cb871 --- /dev/null +++ b/elliptic-curve/src/oprf.rs @@ -0,0 +1,30 @@ +//! Oblivious Pseudorandom Functions (OPRF) using prime order groups +//! +//! + +use digest::{FixedOutput, Update}; +use hybrid_array::typenum::{IsLess, True, U65536}; + +use crate::PrimeCurve; +use crate::hash2curve::ExpandMsg; + +/// Elliptic curve parameters used by OPRF. +pub trait OprfParameters: PrimeCurve { + /// The `ID` parameter which identifies a particular elliptic curve + /// as defined in [section 4 of RFC9497][oprf]. + /// + /// [oprf]: https://www.rfc-editor.org/rfc/rfc9497.html#name-ciphersuites + const ID: &'static [u8]; + + /// The `Hash` parameter which assigns a particular hash function to this + /// ciphersuite as defined in [section 4 of RFC9497][oprf]. + /// + /// [oprf]: https://www.rfc-editor.org/rfc/rfc9497.html#name-ciphersuites + type Hash: Default + FixedOutput> + Update; + + /// The `expand_message` parameter which assigns a particular algorithm for `HashToGroup` + /// and `HashToScalar` as defined in [section 4 of RFC9497][oprf]. + /// + /// [oprf]: https://www.rfc-editor.org/rfc/rfc9497.html#name-ciphersuites + type ExpandMsg: for<'a> ExpandMsg<'a>; +} diff --git a/elliptic-curve/src/voprf.rs b/elliptic-curve/src/voprf.rs deleted file mode 100644 index e882fe1b3..000000000 --- a/elliptic-curve/src/voprf.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Verifiable Oblivious Pseudorandom Function (VOPRF) using prime order groups -//! -//! - -use crate::PrimeCurve; -use crate::hash2curve::ExpandMsg; - -/// Elliptic curve parameters used by VOPRF. -pub trait VoprfParameters: PrimeCurve { - /// The `ID` parameter which identifies a particular elliptic curve - /// as defined in [section 4 of `draft-irtf-cfrg-voprf-19`][voprf]. - /// - /// [voprf]: https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#name-ciphersuites-2 - const ID: &'static str; - - /// The `Hash` parameter which assigns a particular hash function to this - /// ciphersuite as defined in [section 4 of `draft-irtf-cfrg-voprf-19`][voprf]. - /// - /// [voprf]: https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#name-ciphersuites-2 - type Hash: digest::Digest; - - /// The `expand_message` parameter which assigns a particular algorithm for `HashToGroup` - /// and `HashToScalar` as defined in [section 4 of `draft-irtf-cfrg-voprf-19`][voprf]. - /// - /// [voprf]: https://www.rfc-editor.org/rfc/rfc9497#name-ciphersuites - type ExpandMsg: for<'a> ExpandMsg<'a>; -} From 70074d35da95d17e3a78af8bac8e95fc4045cb2d Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 23 May 2025 22:38:09 +0200 Subject: [PATCH 2/8] Improve various bounds --- elliptic-curve/src/hash2curve/group_digest.rs | 7 ++----- .../src/hash2curve/hash2field/expand_msg/xmd.rs | 8 ++++---- .../src/hash2curve/hash2field/expand_msg/xof.rs | 15 ++++++--------- elliptic-curve/src/hash2curve/map2curve.rs | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/elliptic-curve/src/hash2curve/group_digest.rs b/elliptic-curve/src/hash2curve/group_digest.rs index 5320c5a69..84aa9db46 100644 --- a/elliptic-curve/src/hash2curve/group_digest.rs +++ b/elliptic-curve/src/hash2curve/group_digest.rs @@ -1,6 +1,6 @@ //! Traits for handling hash to curve. -use super::{ExpandMsg, FromOkm, MapToCurve, hash_to_field}; +use super::{ExpandMsg, MapToCurve, hash_to_field}; use crate::{ProjectivePoint, Result}; use hybrid_array::typenum::Unsigned; @@ -101,10 +101,7 @@ pub trait GroupDigest: MapToCurve { fn hash_to_scalar<'a, X: ExpandMsg<'a>>( msgs: &[&[u8]], dsts: &'a [&'a [u8]], - ) -> Result - where - Self::Scalar: FromOkm, - { + ) -> Result { let mut u = [Self::Scalar::default()]; hash_to_field::(msgs, dsts, &mut u)?; Ok(u[0]) diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs index fcd63dca7..f91099453 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs @@ -8,7 +8,7 @@ use digest::{ FixedOutput, HashMarker, array::{ Array, - typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, True, U2, U256, Unsigned}, + typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U256, Unsigned}, }, block_api::BlockSizeUser, }; @@ -31,7 +31,7 @@ where HashT::OutputSize: IsLess, HashT::OutputSize: IsLessOrEqual, K: Mul, - HashT::OutputSize: IsGreaterOrEqual<>::Output, Output = True>; + HashT::OutputSize: IsGreaterOrEqual, Output = True>; impl<'a, HashT, K> ExpandMsg<'a> for ExpandMsgXmd where @@ -46,7 +46,7 @@ where // The number of bits output by `HashT` MUST be larger or equal to `K * 2`: // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 K: Mul, - HashT::OutputSize: IsGreaterOrEqual<>::Output, Output = True>, + HashT::OutputSize: IsGreaterOrEqual, Output = True>, { type Expander = ExpanderXmd<'a, HashT>; @@ -222,7 +222,7 @@ mod test { HashT::OutputSize: IsLess + IsLessOrEqual + Mul, - HashT::OutputSize: IsGreaterOrEqual<>::Output, Output = True>, + HashT::OutputSize: IsGreaterOrEqual, { assert_message::(self.msg, domain, L::to_u16(), self.msg_prime); diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs index 201aee156..38af409c8 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs @@ -6,7 +6,7 @@ use core::{fmt, marker::PhantomData, num::NonZero, ops::Mul}; use digest::{ExtendableOutput, HashMarker, Update, XofReader}; use hybrid_array::{ ArraySize, - typenum::{IsLess, True, U2, U256}, + typenum::{IsLess, Prod, True, U2, U256}, }; /// Implements `expand_message_xof` via the [`ExpandMsg`] trait: @@ -22,8 +22,7 @@ use hybrid_array::{ pub struct ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul, - >::Output: ArraySize + IsLess, + K: Mul>, { reader: ::Reader, _k: PhantomData, @@ -32,8 +31,7 @@ where impl fmt::Debug for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul, - >::Output: ArraySize + IsLess, + K: Mul>, ::Reader: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -49,7 +47,7 @@ where // If DST is larger than 255 bytes, the length of the computed DST is calculated by `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 K: Mul, - >::Output: ArraySize + IsLess, + K: Mul>, { type Expander = Self; @@ -60,7 +58,7 @@ where ) -> Result { let len_in_bytes = u16::try_from(len_in_bytes.get()).map_err(|_| Error)?; - let domain = Domain::<>::Output>::xof::(dsts)?; + let domain = Domain::>::xof::(dsts)?; let mut reader = HashT::default(); for msg in msgs { @@ -81,8 +79,7 @@ where impl Expander for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul, - >::Output: ArraySize + IsLess, + K: Mul>, { fn fill_bytes(&mut self, okm: &mut [u8]) { self.reader.read(okm); diff --git a/elliptic-curve/src/hash2curve/map2curve.rs b/elliptic-curve/src/hash2curve/map2curve.rs index aa72e11b5..7ab69a2af 100644 --- a/elliptic-curve/src/hash2curve/map2curve.rs +++ b/elliptic-curve/src/hash2curve/map2curve.rs @@ -6,7 +6,7 @@ use super::FromOkm; /// Trait for converting field elements into a point via a mapping method like /// Simplified Shallue-van de Woestijne-Ulas or Elligator. -pub trait MapToCurve: CurveArithmetic { +pub trait MapToCurve: CurveArithmetic { /// The intermediate representation, an element of the curve which may or may not /// be in the curve subgroup. type CurvePoint; From 3afc169e097e81640d86201cdda929c3194a2cac Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 23 May 2025 23:11:18 +0200 Subject: [PATCH 3/8] Move `K` to `ExpandMsg` --- elliptic-curve/src/hash2curve/group_digest.rs | 29 ++++++++++--------- elliptic-curve/src/hash2curve/hash2field.rs | 8 +++-- .../src/hash2curve/hash2field/expand_msg.rs | 6 +++- .../hash2curve/hash2field/expand_msg/xmd.rs | 14 +++------ .../hash2curve/hash2field/expand_msg/xof.rs | 26 +++++------------ elliptic-curve/src/oprf.rs | 6 ++-- 6 files changed, 41 insertions(+), 48 deletions(-) diff --git a/elliptic-curve/src/hash2curve/group_digest.rs b/elliptic-curve/src/hash2curve/group_digest.rs index 84aa9db46..059a2e601 100644 --- a/elliptic-curve/src/hash2curve/group_digest.rs +++ b/elliptic-curve/src/hash2curve/group_digest.rs @@ -45,12 +45,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_from_bytes<'a, X: ExpandMsg<'a>>( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], - ) -> Result> { + fn hash_from_bytes<'a, X>(msgs: &[&[u8]], dsts: &'a [&'a [u8]]) -> Result> + where + X: ExpandMsg<'a, Self::K>, + { let mut u = [Self::FieldElement::default(), Self::FieldElement::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msgs, dsts, &mut u)?; let q0 = Self::map_to_curve(u[0]); let q1 = Self::map_to_curve(u[1]); Ok(Self::add_and_map_to_subgroup(q0, q1)) @@ -75,12 +75,15 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn encode_from_bytes<'a, X: ExpandMsg<'a>>( + fn encode_from_bytes<'a, X>( msgs: &[&[u8]], dsts: &'a [&'a [u8]], - ) -> Result> { + ) -> Result> + where + X: ExpandMsg<'a, Self::K>, + { let mut u = [Self::FieldElement::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msgs, dsts, &mut u)?; let q0 = Self::map_to_curve(u[0]); Ok(Self::map_to_subgroup(q0)) } @@ -98,12 +101,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_to_scalar<'a, X: ExpandMsg<'a>>( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], - ) -> Result { + fn hash_to_scalar<'a, X>(msgs: &[&[u8]], dsts: &'a [&'a [u8]]) -> Result + where + X: ExpandMsg<'a, Self::K>, + { let mut u = [Self::Scalar::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msgs, dsts, &mut u)?; Ok(u[0]) } } diff --git a/elliptic-curve/src/hash2curve/hash2field.rs b/elliptic-curve/src/hash2curve/hash2field.rs index 4ffcf8062..95546ee88 100644 --- a/elliptic-curve/src/hash2curve/hash2field.rs +++ b/elliptic-curve/src/hash2curve/hash2field.rs @@ -37,9 +37,13 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field<'a, E, T>(data: &[&[u8]], domain: &'a [&'a [u8]], out: &mut [T]) -> Result<()> +pub fn hash_to_field<'a, E, K, T>( + data: &[&[u8]], + domain: &'a [&'a [u8]], + out: &mut [T], +) -> Result<()> where - E: ExpandMsg<'a>, + E: ExpandMsg<'a, K>, T: FromOkm + Default, { let len_in_bytes = T::Length::to_usize() diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs index e8fb802a6..8a6ab3bff 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs @@ -17,9 +17,13 @@ const MAX_DST_LEN: usize = 255; /// Trait for types implementing expand_message interface for `hash_to_field`. /// +/// `K` is the target security level in bytes: +/// +/// +/// /// # Errors /// See implementors of [`ExpandMsg`] for errors. -pub trait ExpandMsg<'a> { +pub trait ExpandMsg<'a, K> { /// Type holding data for the [`Expander`]. type Expander: Expander + Sized; diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs index f91099453..21869ad3d 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs @@ -16,24 +16,18 @@ use digest::{ /// Implements `expand_message_xof` via the [`ExpandMsg`] trait: /// /// -/// `K` is the target security level in bytes: -/// -/// -/// /// # Errors /// - `dst.is_empty()` /// - `len_in_bytes > u16::MAX` /// - `len_in_bytes > 255 * HashT::OutputSize` #[derive(Debug)] -pub struct ExpandMsgXmd(PhantomData<(HashT, K)>) +pub struct ExpandMsgXmd(PhantomData) where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, HashT::OutputSize: IsLess, - HashT::OutputSize: IsLessOrEqual, - K: Mul, - HashT::OutputSize: IsGreaterOrEqual, Output = True>; + HashT::OutputSize: IsLessOrEqual; -impl<'a, HashT, K> ExpandMsg<'a> for ExpandMsgXmd +impl<'a, HashT, K> ExpandMsg<'a, K> for ExpandMsgXmd where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST will depend on the output @@ -227,7 +221,7 @@ mod test { assert_message::(self.msg, domain, L::to_u16(), self.msg_prime); let dst = [dst]; - let mut expander = ExpandMsgXmd::::expand_message( + let mut expander = as ExpandMsg<'_, U4>>::expand_message( &[self.msg], &dst, NonZero::new(L::to_usize()).ok_or(Error)?, diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs index 38af409c8..f346d7ac8 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs @@ -2,7 +2,7 @@ use super::{Domain, ExpandMsg, Expander}; use crate::{Error, Result}; -use core::{fmt, marker::PhantomData, num::NonZero, ops::Mul}; +use core::{fmt, num::NonZero, ops::Mul}; use digest::{ExtendableOutput, HashMarker, Update, XofReader}; use hybrid_array::{ ArraySize, @@ -12,26 +12,19 @@ use hybrid_array::{ /// Implements `expand_message_xof` via the [`ExpandMsg`] trait: /// /// -/// `K` is the target security level in bytes: -/// -/// -/// /// # Errors /// - `dst.is_empty()` /// - `len_in_bytes > u16::MAX` -pub struct ExpandMsgXof +pub struct ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul>, { reader: ::Reader, - _k: PhantomData, } -impl fmt::Debug for ExpandMsgXof +impl fmt::Debug for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul>, ::Reader: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -41,12 +34,11 @@ where } } -impl<'a, HashT, K> ExpandMsg<'a> for ExpandMsgXof +impl<'a, HashT, K> ExpandMsg<'a, K> for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST is calculated by `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 - K: Mul, K: Mul>, { type Expander = Self; @@ -69,17 +61,13 @@ where domain.update_hash(&mut reader); reader.update(&[domain.len()]); let reader = reader.finalize_xof(); - Ok(Self { - reader, - _k: PhantomData, - }) + Ok(Self { reader }) } } -impl Expander for ExpandMsgXof +impl Expander for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, - K: Mul>, { fn fill_bytes(&mut self, okm: &mut [u8]) { self.reader.read(okm); @@ -131,7 +119,7 @@ mod test { { assert_message(self.msg, domain, L::to_u16(), self.msg_prime); - let mut expander = ExpandMsgXof::::expand_message( + let mut expander = as ExpandMsg<'_, U16>>::expand_message( &[self.msg], &[dst], NonZero::new(L::to_usize()).ok_or(Error)?, diff --git a/elliptic-curve/src/oprf.rs b/elliptic-curve/src/oprf.rs index e2d7cb871..be24d1015 100644 --- a/elliptic-curve/src/oprf.rs +++ b/elliptic-curve/src/oprf.rs @@ -6,10 +6,10 @@ use digest::{FixedOutput, Update}; use hybrid_array::typenum::{IsLess, True, U65536}; use crate::PrimeCurve; -use crate::hash2curve::ExpandMsg; +use crate::hash2curve::{ExpandMsg, GroupDigest}; /// Elliptic curve parameters used by OPRF. -pub trait OprfParameters: PrimeCurve { +pub trait OprfParameters: GroupDigest + PrimeCurve { /// The `ID` parameter which identifies a particular elliptic curve /// as defined in [section 4 of RFC9497][oprf]. /// @@ -26,5 +26,5 @@ pub trait OprfParameters: PrimeCurve { /// and `HashToScalar` as defined in [section 4 of RFC9497][oprf]. /// /// [oprf]: https://www.rfc-editor.org/rfc/rfc9497.html#name-ciphersuites - type ExpandMsg: for<'a> ExpandMsg<'a>; + type ExpandMsg: for<'a> ExpandMsg<'a, ::K>; } From 749300c45c0445c6e3cc81bdd890cbf9e9d59fd4 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 24 May 2025 13:21:18 +0200 Subject: [PATCH 4/8] Fix some documentation nits --- elliptic-curve/src/hash2curve/group_digest.rs | 29 ++++--------------- .../src/hash2curve/hash2field/expand_msg.rs | 24 +++++++-------- .../hash2curve/hash2field/expand_msg/xmd.rs | 16 +++++----- .../hash2curve/hash2field/expand_msg/xof.rs | 10 +++---- 4 files changed, 31 insertions(+), 48 deletions(-) diff --git a/elliptic-curve/src/hash2curve/group_digest.rs b/elliptic-curve/src/hash2curve/group_digest.rs index 059a2e601..6f9cb9fe1 100644 --- a/elliptic-curve/src/hash2curve/group_digest.rs +++ b/elliptic-curve/src/hash2curve/group_digest.rs @@ -22,20 +22,6 @@ pub trait GroupDigest: MapToCurve { /// > oracle returning points in G assuming a cryptographically secure /// > hash function is used. /// - /// # Examples - /// - /// ## Using a fixed size hash function - /// - /// ```ignore - /// let pt = ProjectivePoint::hash_from_bytes::>(b"test data", b"CURVE_XMD:SHA-256_SSWU_RO_"); - /// ``` - /// - /// ## Using an extendable output function - /// - /// ```ignore - /// let pt = ProjectivePoint::hash_from_bytes::>(b"test data", b"CURVE_XOF:SHAKE-256_SSWU_RO_"); - /// ``` - /// /// # Errors /// See implementors of [`ExpandMsg`] for errors: /// - [`ExpandMsgXmd`] @@ -45,12 +31,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_from_bytes<'a, X>(msgs: &[&[u8]], dsts: &'a [&'a [u8]]) -> Result> + fn hash_from_bytes<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result> where X: ExpandMsg<'a, Self::K>, { let mut u = [Self::FieldElement::default(), Self::FieldElement::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msg, dst, &mut u)?; let q0 = Self::map_to_curve(u[0]); let q1 = Self::map_to_curve(u[1]); Ok(Self::add_and_map_to_subgroup(q0, q1)) @@ -75,15 +61,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn encode_from_bytes<'a, X>( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], - ) -> Result> + fn encode_from_bytes<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result> where X: ExpandMsg<'a, Self::K>, { let mut u = [Self::FieldElement::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msg, dst, &mut u)?; let q0 = Self::map_to_curve(u[0]); Ok(Self::map_to_subgroup(q0)) } @@ -101,12 +84,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_to_scalar<'a, X>(msgs: &[&[u8]], dsts: &'a [&'a [u8]]) -> Result + fn hash_to_scalar<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result where X: ExpandMsg<'a, Self::K>, { let mut u = [Self::Scalar::default()]; - hash_to_field::(msgs, dsts, &mut u)?; + hash_to_field::(msg, dst, &mut u)?; Ok(u[0]) } } diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs index 8a6ab3bff..58a2788e6 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs @@ -32,8 +32,8 @@ pub trait ExpandMsg<'a, K> { /// Returns an expander that can be used to call `read` until enough /// bytes have been consumed fn expand_message( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], + msg: &[&[u8]], + dst: &'a [&'a [u8]], len_in_bytes: NonZero, ) -> Result; } @@ -64,18 +64,18 @@ impl<'a, L> Domain<'a, L> where L: ArraySize + IsLess, { - pub fn xof(dsts: &'a [&'a [u8]]) -> Result + pub fn xof(dst: &'a [&'a [u8]]) -> Result where X: Default + ExtendableOutput + Update, { - if dsts.is_empty() { + if dst.is_empty() { Err(Error) - } else if dsts.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { + } else if dst.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { let mut data = Array::::default(); let mut hash = X::default(); hash.update(OVERSIZE_DST_SALT); - for dst in dsts { + for dst in dst { hash.update(dst); } @@ -83,29 +83,29 @@ where Ok(Self::Hashed(data)) } else { - Ok(Self::Array(dsts)) + Ok(Self::Array(dst)) } } - pub fn xmd(dsts: &'a [&'a [u8]]) -> Result + pub fn xmd(dst: &'a [&'a [u8]]) -> Result where X: Digest, { - if dsts.is_empty() { + if dst.is_empty() { Err(Error) - } else if dsts.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { + } else if dst.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { Ok(Self::Hashed({ let mut hash = X::new(); hash.update(OVERSIZE_DST_SALT); - for dst in dsts { + for dst in dst { hash.update(dst); } hash.finalize() })) } else { - Ok(Self::Array(dsts)) + Ok(Self::Array(dst)) } } diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs index 21869ad3d..0e551e8e8 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs @@ -17,7 +17,7 @@ use digest::{ /// /// /// # Errors -/// - `dst.is_empty()` +/// - `dst` contains no bytes /// - `len_in_bytes > u16::MAX` /// - `len_in_bytes > 255 * HashT::OutputSize` #[derive(Debug)] @@ -31,13 +31,13 @@ impl<'a, HashT, K> ExpandMsg<'a, K> for ExpandMsgXmd where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST will depend on the output - // size of the hash, which is still not allowed to be larger than 256: + // size of the hash, which is still not allowed to be larger than 255. // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-6 HashT::OutputSize: IsLess, - // Constraint set by `expand_message_xmd`: + // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`. // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-4 HashT::OutputSize: IsLessOrEqual, - // The number of bits output by `HashT` MUST be larger or equal to `K * 2`: + // The number of bits output by `HashT` MUST be at least `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 K: Mul, HashT::OutputSize: IsGreaterOrEqual, Output = True>, @@ -45,8 +45,8 @@ where type Expander = ExpanderXmd<'a, HashT>; fn expand_message( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], + msg: &[&[u8]], + dst: &'a [&'a [u8]], len_in_bytes: NonZero, ) -> Result { let len_in_bytes_u16 = u16::try_from(len_in_bytes.get()).map_err(|_| Error)?; @@ -59,11 +59,11 @@ where let b_in_bytes = HashT::OutputSize::to_usize(); let ell = u8::try_from(len_in_bytes.get().div_ceil(b_in_bytes)).map_err(|_| Error)?; - let domain = Domain::xmd::(dsts)?; + let domain = Domain::xmd::(dst)?; let mut b_0 = HashT::default(); b_0.update(&Array::::default()); - for msg in msgs { + for msg in msg { b_0.update(msg); } diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs index f346d7ac8..ac6001f45 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs @@ -13,7 +13,7 @@ use hybrid_array::{ /// /// /// # Errors -/// - `dst.is_empty()` +/// - `dst` contains no bytes /// - `len_in_bytes > u16::MAX` pub struct ExpandMsgXof where @@ -44,16 +44,16 @@ where type Expander = Self; fn expand_message( - msgs: &[&[u8]], - dsts: &'a [&'a [u8]], + msg: &[&[u8]], + dst: &'a [&'a [u8]], len_in_bytes: NonZero, ) -> Result { let len_in_bytes = u16::try_from(len_in_bytes.get()).map_err(|_| Error)?; - let domain = Domain::>::xof::(dsts)?; + let domain = Domain::>::xof::(dst)?; let mut reader = HashT::default(); - for msg in msgs { + for msg in msg { reader = reader.chain(msg); } From ffa122796bfd318a9f48b35f0f5ecdb2b0d794c2 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 25 May 2025 08:46:54 +0200 Subject: [PATCH 5/8] Move `ExpandMsg` lifetime to expander --- elliptic-curve/src/hash2curve/group_digest.rs | 12 ++++++------ elliptic-curve/src/hash2curve/hash2field.rs | 8 ++------ .../src/hash2curve/hash2field/expand_msg.rs | 10 +++++----- .../src/hash2curve/hash2field/expand_msg/xmd.rs | 12 ++++++------ .../src/hash2curve/hash2field/expand_msg/xof.rs | 12 ++++++------ elliptic-curve/src/oprf.rs | 2 +- 6 files changed, 26 insertions(+), 30 deletions(-) diff --git a/elliptic-curve/src/hash2curve/group_digest.rs b/elliptic-curve/src/hash2curve/group_digest.rs index 6f9cb9fe1..222918dad 100644 --- a/elliptic-curve/src/hash2curve/group_digest.rs +++ b/elliptic-curve/src/hash2curve/group_digest.rs @@ -31,9 +31,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_from_bytes<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result> + fn hash_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> where - X: ExpandMsg<'a, Self::K>, + X: ExpandMsg, { let mut u = [Self::FieldElement::default(), Self::FieldElement::default()]; hash_to_field::(msg, dst, &mut u)?; @@ -61,9 +61,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn encode_from_bytes<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result> + fn encode_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> where - X: ExpandMsg<'a, Self::K>, + X: ExpandMsg, { let mut u = [Self::FieldElement::default()]; hash_to_field::(msg, dst, &mut u)?; @@ -84,9 +84,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof - fn hash_to_scalar<'a, X>(msg: &[&[u8]], dst: &'a [&'a [u8]]) -> Result + fn hash_to_scalar(msg: &[&[u8]], dst: &[&[u8]]) -> Result where - X: ExpandMsg<'a, Self::K>, + X: ExpandMsg, { let mut u = [Self::Scalar::default()]; hash_to_field::(msg, dst, &mut u)?; diff --git a/elliptic-curve/src/hash2curve/hash2field.rs b/elliptic-curve/src/hash2curve/hash2field.rs index 95546ee88..b3a4ff3f2 100644 --- a/elliptic-curve/src/hash2curve/hash2field.rs +++ b/elliptic-curve/src/hash2curve/hash2field.rs @@ -37,13 +37,9 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field<'a, E, K, T>( - data: &[&[u8]], - domain: &'a [&'a [u8]], - out: &mut [T], -) -> Result<()> +pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]], out: &mut [T]) -> Result<()> where - E: ExpandMsg<'a, K>, + E: ExpandMsg, T: FromOkm + Default, { let len_in_bytes = T::Length::to_usize() diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs index 58a2788e6..e7f6411a7 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs @@ -23,19 +23,19 @@ const MAX_DST_LEN: usize = 255; /// /// # Errors /// See implementors of [`ExpandMsg`] for errors. -pub trait ExpandMsg<'a, K> { +pub trait ExpandMsg { /// Type holding data for the [`Expander`]. - type Expander: Expander + Sized; + type Expander<'dst>: Expander + Sized; /// Expands `msg` to the required number of bytes. /// /// Returns an expander that can be used to call `read` until enough /// bytes have been consumed - fn expand_message( + fn expand_message<'dst>( msg: &[&[u8]], - dst: &'a [&'a [u8]], + dst: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result; + ) -> Result>; } /// Expander that, call `read` until enough bytes have been consumed. diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs index 0e551e8e8..7c7104945 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs @@ -27,7 +27,7 @@ where HashT::OutputSize: IsLess, HashT::OutputSize: IsLessOrEqual; -impl<'a, HashT, K> ExpandMsg<'a, K> for ExpandMsgXmd +impl ExpandMsg for ExpandMsgXmd where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST will depend on the output @@ -42,13 +42,13 @@ where K: Mul, HashT::OutputSize: IsGreaterOrEqual, Output = True>, { - type Expander = ExpanderXmd<'a, HashT>; + type Expander<'dst> = ExpanderXmd<'dst, HashT>; - fn expand_message( + fn expand_message<'dst>( msg: &[&[u8]], - dst: &'a [&'a [u8]], + dst: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result { + ) -> Result> { let len_in_bytes_u16 = u16::try_from(len_in_bytes.get()).map_err(|_| Error)?; // `255 * ` can not exceed `u16::MAX` @@ -221,7 +221,7 @@ mod test { assert_message::(self.msg, domain, L::to_u16(), self.msg_prime); let dst = [dst]; - let mut expander = as ExpandMsg<'_, U4>>::expand_message( + let mut expander = as ExpandMsg>::expand_message( &[self.msg], &dst, NonZero::new(L::to_usize()).ok_or(Error)?, diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs index ac6001f45..6133af460 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs @@ -34,20 +34,20 @@ where } } -impl<'a, HashT, K> ExpandMsg<'a, K> for ExpandMsgXof +impl ExpandMsg for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST is calculated by `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 K: Mul>, { - type Expander = Self; + type Expander<'dst> = Self; - fn expand_message( + fn expand_message<'dst>( msg: &[&[u8]], - dst: &'a [&'a [u8]], + dst: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result { + ) -> Result> { let len_in_bytes = u16::try_from(len_in_bytes.get()).map_err(|_| Error)?; let domain = Domain::>::xof::(dst)?; @@ -119,7 +119,7 @@ mod test { { assert_message(self.msg, domain, L::to_u16(), self.msg_prime); - let mut expander = as ExpandMsg<'_, U16>>::expand_message( + let mut expander = as ExpandMsg>::expand_message( &[self.msg], &[dst], NonZero::new(L::to_usize()).ok_or(Error)?, diff --git a/elliptic-curve/src/oprf.rs b/elliptic-curve/src/oprf.rs index be24d1015..b6806f74f 100644 --- a/elliptic-curve/src/oprf.rs +++ b/elliptic-curve/src/oprf.rs @@ -26,5 +26,5 @@ pub trait OprfParameters: GroupDigest + PrimeCurve { /// and `HashToScalar` as defined in [section 4 of RFC9497][oprf]. /// /// [oprf]: https://www.rfc-editor.org/rfc/rfc9497.html#name-ciphersuites - type ExpandMsg: for<'a> ExpandMsg<'a, ::K>; + type ExpandMsg: ExpandMsg<::K>; } From 7a736a894b60a65f075a83886485ec620b340abf Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 26 May 2025 12:14:36 +0200 Subject: [PATCH 6/8] Properly check for empty DSTs --- .../src/hash2curve/hash2field/expand_msg.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs index e7f6411a7..9a282fff7 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs @@ -68,15 +68,16 @@ where where X: Default + ExtendableOutput + Update, { - if dst.is_empty() { + // https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2 + if dst.iter().map(|slice| slice.len()).sum::() == 0 { Err(Error) - } else if dst.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { + } else if dst.iter().map(|slice| slice.len()).sum::() > MAX_DST_LEN { let mut data = Array::::default(); let mut hash = X::default(); hash.update(OVERSIZE_DST_SALT); - for dst in dst { - hash.update(dst); + for slice in dst { + hash.update(slice); } hash.finalize_xof().read(&mut data); @@ -91,15 +92,16 @@ where where X: Digest, { - if dst.is_empty() { + // https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2 + if dst.iter().map(|slice| slice.len()).sum::() == 0 { Err(Error) - } else if dst.iter().map(|dst| dst.len()).sum::() > MAX_DST_LEN { + } else if dst.iter().map(|slice| slice.len()).sum::() > MAX_DST_LEN { Ok(Self::Hashed({ let mut hash = X::new(); hash.update(OVERSIZE_DST_SALT); - for dst in dst { - hash.update(dst); + for slice in dst { + hash.update(slice); } hash.finalize() From eec19e31e93a041e74dd130169d94ed306fe457c Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 27 May 2025 22:52:09 +0200 Subject: [PATCH 7/8] Require `CollisionResistance` for `ExpandMsgXof` --- Cargo.lock | 4 ++-- .../src/hash2curve/hash2field/expand_msg/xof.rs | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f11b4d85b..9aadabffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,7 +540,7 @@ dependencies = [ [[package]] name = "sha2" version = "0.11.0-pre.5" -source = "git+https://github.com/RustCrypto/hashes.git#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" +source = "git+https://github.com/RustCrypto/hashes.git#d9ad085ed12dba58d2a6d75d76a17a1b2706c4c7" dependencies = [ "cfg-if", "cpufeatures", @@ -550,7 +550,7 @@ dependencies = [ [[package]] name = "sha3" version = "0.11.0-pre.5" -source = "git+https://github.com/RustCrypto/hashes.git#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" +source = "git+https://github.com/RustCrypto/hashes.git#d9ad085ed12dba58d2a6d75d76a17a1b2706c4c7" dependencies = [ "digest", "keccak", diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs index 6133af460..25f6858be 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xof.rs @@ -3,7 +3,9 @@ use super::{Domain, ExpandMsg, Expander}; use crate::{Error, Result}; use core::{fmt, num::NonZero, ops::Mul}; -use digest::{ExtendableOutput, HashMarker, Update, XofReader}; +use digest::{ + CollisionResistance, ExtendableOutput, HashMarker, Update, XofReader, typenum::IsGreaterOrEqual, +}; use hybrid_array::{ ArraySize, typenum::{IsLess, Prod, True, U2, U256}, @@ -40,6 +42,9 @@ where // If DST is larger than 255 bytes, the length of the computed DST is calculated by `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 K: Mul>, + // The collision resistance of `HashT` MUST be at least `K` bits. + // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.2-2.1 + HashT: CollisionResistance>, { type Expander<'dst> = Self; @@ -114,7 +119,11 @@ mod test { #[allow(clippy::panic_in_result_fn)] fn assert(&self, dst: &'static [u8], domain: &Domain<'_, U32>) -> Result<()> where - HashT: Default + ExtendableOutput + Update + HashMarker, + HashT: Default + + ExtendableOutput + + Update + + HashMarker + + CollisionResistance>, L: ArraySize, { assert_message(self.msg, domain, L::to_u16(), self.msg_prime); From d11b3be367f0b0ff4bb31386dd17f1fa87f1c87f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 27 May 2025 23:18:25 +0200 Subject: [PATCH 8/8] Update all mentions of hash2curve draft to RFC9380 --- elliptic-curve/src/hash2curve.rs | 2 +- elliptic-curve/src/hash2curve/group_digest.rs | 6 +++--- elliptic-curve/src/hash2curve/hash2field.rs | 4 ++-- elliptic-curve/src/hash2curve/hash2field/expand_msg.rs | 4 ++-- elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs | 4 ++-- elliptic-curve/src/hash2curve/isogeny.rs | 2 +- elliptic-curve/src/hash2curve/osswu.rs | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/elliptic-curve/src/hash2curve.rs b/elliptic-curve/src/hash2curve.rs index 3df394f79..da923e4d2 100644 --- a/elliptic-curve/src/hash2curve.rs +++ b/elliptic-curve/src/hash2curve.rs @@ -1,6 +1,6 @@ //! Traits for hashing byte sequences to curve points. //! -//! +//! mod group_digest; mod hash2field; diff --git a/elliptic-curve/src/hash2curve/group_digest.rs b/elliptic-curve/src/hash2curve/group_digest.rs index 222918dad..571f43e81 100644 --- a/elliptic-curve/src/hash2curve/group_digest.rs +++ b/elliptic-curve/src/hash2curve/group_digest.rs @@ -13,7 +13,7 @@ pub trait GroupDigest: MapToCurve { /// Computes the hash to curve routine. /// - /// From : + /// From : /// /// > Uniform encoding from byte strings to points in G. /// > That is, the distribution of its output is statistically close @@ -44,7 +44,7 @@ pub trait GroupDigest: MapToCurve { /// Computes the encode to curve routine. /// - /// From : + /// From : /// /// > Nonuniform encoding from byte strings to /// > points in G. That is, the distribution of its output is not @@ -72,7 +72,7 @@ pub trait GroupDigest: MapToCurve { } /// Computes the hash to field routine according to - /// + /// /// and returns a scalar. /// /// # Errors diff --git a/elliptic-curve/src/hash2curve/hash2field.rs b/elliptic-curve/src/hash2curve/hash2field.rs index b3a4ff3f2..990230105 100644 --- a/elliptic-curve/src/hash2curve/hash2field.rs +++ b/elliptic-curve/src/hash2curve/hash2field.rs @@ -1,6 +1,6 @@ //! Traits for hashing to field elements. //! -//! +//! mod expand_msg; @@ -25,7 +25,7 @@ pub trait FromOkm { /// Convert an arbitrary byte sequence into a field element. /// -/// +/// /// /// # Errors /// See implementors of [`ExpandMsg`] for errors: diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs index 9a282fff7..233b6b9f0 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg.rs @@ -46,9 +46,9 @@ pub trait Expander { /// The domain separation tag /// -/// Implements [section 5.4.3 of `draft-irtf-cfrg-hash-to-curve-13`][dst]. +/// Implements [section 5.3.3 of RFC9380][dst]. /// -/// [dst]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.3 +/// [dst]: https://www.rfc-editor.org/rfc/rfc9380.html#name-using-dsts-longer-than-255- #[derive(Debug)] pub(crate) enum Domain<'a, L> where diff --git a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs index 7c7104945..8ae1ec022 100644 --- a/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs +++ b/elliptic-curve/src/hash2curve/hash2field/expand_msg/xmd.rs @@ -32,10 +32,10 @@ where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // If DST is larger than 255 bytes, the length of the computed DST will depend on the output // size of the hash, which is still not allowed to be larger than 255. - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-6 + // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-6 HashT::OutputSize: IsLess, // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`. - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-4 + // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-4 HashT::OutputSize: IsLessOrEqual, // The number of bits output by `HashT` MUST be at least `K * 2`. // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.1-2.1 diff --git a/elliptic-curve/src/hash2curve/isogeny.rs b/elliptic-curve/src/hash2curve/isogeny.rs index 4644d786a..5b11900d9 100644 --- a/elliptic-curve/src/hash2curve/isogeny.rs +++ b/elliptic-curve/src/hash2curve/isogeny.rs @@ -1,6 +1,6 @@ //! Traits for mapping an isogeny to another curve //! -//! +//! use core::ops::{AddAssign, Mul}; use ff::Field; diff --git a/elliptic-curve/src/hash2curve/osswu.rs b/elliptic-curve/src/hash2curve/osswu.rs index dc5d16d57..ea8acdc5b 100644 --- a/elliptic-curve/src/hash2curve/osswu.rs +++ b/elliptic-curve/src/hash2curve/osswu.rs @@ -1,6 +1,6 @@ //! Optimized simplified Shallue-van de Woestijne-Ulas methods. //! -//! +//! use ff::Field; use subtle::Choice;