From 6f022b9526d4841dc3e6ea0411604fc5f28bf702 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 13 Jul 2025 15:01:56 -0400 Subject: [PATCH 01/21] make hash_to_field output an array --- hash2curve/src/group_digest.rs | 10 ++++------ hash2curve/src/hash2field.rs | 8 +++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index 74ecaf8b0..328525f81 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -1,6 +1,7 @@ //! Traits for handling hash to curve. use super::{ExpandMsg, MapToCurve, hash_to_field}; +use digest::consts::{U1, U2}; use elliptic_curve::array::typenum::Unsigned; use elliptic_curve::{ProjectivePoint, Result}; @@ -36,8 +37,7 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::FieldElement::default(), Self::FieldElement::default()]; - hash_to_field::(msg, dst, &mut u)?; + let u = hash_to_field::(msg, dst)?; 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)) @@ -67,8 +67,7 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::FieldElement::default()]; - hash_to_field::(msg, dst, &mut u)?; + let u = hash_to_field::(msg, dst)?; let q0 = Self::map_to_curve(u[0]); Ok(Self::map_to_subgroup(q0)) } @@ -91,8 +90,7 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::Scalar::default()]; - hash_to_field::(msg, dst, &mut u)?; + let u = hash_to_field::(msg, dst)?; Ok(u[0]) } } diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index ad7fa0d86..548ce9ffe 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -38,21 +38,23 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]], out: &mut [T]) -> Result<()> +pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result> where E: ExpandMsg, T: FromOkm + Default, + C: ArraySize, { let len_in_bytes = T::Length::USIZE - .checked_mul(out.len()) + .checked_mul(C::USIZE) .and_then(|len| len.try_into().ok()) .and_then(NonZeroU16::new) .ok_or(Error)?; let mut tmp = Array::::Length>::default(); let mut expander = E::expand_message(data, domain, len_in_bytes)?; + let mut out = Array::::default(); for o in out.iter_mut() { expander.fill_bytes(&mut tmp); *o = T::from_okm(&tmp); } - Ok(()) + Ok(out) } From 4a0553a854dec7719cd57e5c5bdd455f9a4766da Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Fri, 18 Jul 2025 15:45:49 -0400 Subject: [PATCH 02/21] use primitive array instead of `hybrid_array::Array` --- hash2curve/src/group_digest.rs | 15 +++++++-------- hash2curve/src/hash2field.rs | 13 +++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index 328525f81..da493503c 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -1,7 +1,6 @@ //! Traits for handling hash to curve. use super::{ExpandMsg, MapToCurve, hash_to_field}; -use digest::consts::{U1, U2}; use elliptic_curve::array::typenum::Unsigned; use elliptic_curve::{ProjectivePoint, Result}; @@ -37,9 +36,9 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let u = hash_to_field::(msg, dst)?; - let q0 = Self::map_to_curve(u[0]); - let q1 = Self::map_to_curve(u[1]); + let [u0, u1] = hash_to_field::<2, X, _, Self::FieldElement>(msg, dst)?; + let q0 = Self::map_to_curve(u0); + let q1 = Self::map_to_curve(u1); Ok(Self::add_and_map_to_subgroup(q0, q1)) } @@ -67,8 +66,8 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let u = hash_to_field::(msg, dst)?; - let q0 = Self::map_to_curve(u[0]); + let [u] = hash_to_field::<1, X, _, Self::FieldElement>(msg, dst)?; + let q0 = Self::map_to_curve(u); Ok(Self::map_to_subgroup(q0)) } @@ -90,7 +89,7 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let u = hash_to_field::(msg, dst)?; - Ok(u[0]) + let [u] = hash_to_field::<1, X, _, Self::Scalar>(msg, dst)?; + Ok(u) } } diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index 548ce9ffe..267cdefc6 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -38,23 +38,20 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result> +pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]> where E: ExpandMsg, T: FromOkm + Default, - C: ArraySize, { let len_in_bytes = T::Length::USIZE - .checked_mul(C::USIZE) + .checked_mul(N) .and_then(|len| len.try_into().ok()) .and_then(NonZeroU16::new) .ok_or(Error)?; let mut tmp = Array::::Length>::default(); let mut expander = E::expand_message(data, domain, len_in_bytes)?; - let mut out = Array::::default(); - for o in out.iter_mut() { + Ok(core::array::from_fn(|_| { expander.fill_bytes(&mut tmp); - *o = T::from_okm(&tmp); - } - Ok(out) + T::from_okm(&tmp) + })) } From c4b4c0487acd5fa7c408ccc86a559a8a4075595a Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Fri, 18 Jul 2025 15:55:06 -0400 Subject: [PATCH 03/21] fix: all uses of hash_to_field in other crates --- k256/src/arithmetic/hash2curve.rs | 6 +++--- p256/src/arithmetic/hash2curve.rs | 6 +++--- p384/src/arithmetic/hash2curve.rs | 6 +++--- p521/src/arithmetic/hash2curve.rs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 87d210eb6..1e8c7a6ba 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -353,12 +353,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0); assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1); diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index 33419618e..e60441035 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -204,12 +204,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. diff --git a/p384/src/arithmetic/hash2curve.rs b/p384/src/arithmetic/hash2curve.rs index 087c87240..c364a53eb 100644 --- a/p384/src/arithmetic/hash2curve.rs +++ b/p384/src/arithmetic/hash2curve.rs @@ -209,12 +209,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. diff --git a/p521/src/arithmetic/hash2curve.rs b/p521/src/arithmetic/hash2curve.rs index 071104d15..8795ff8c9 100644 --- a/p521/src/arithmetic/hash2curve.rs +++ b/p521/src/arithmetic/hash2curve.rs @@ -212,12 +212,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. From e42fdf541595d9fde8a42523be0ae9e70b4d5704 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 11 Jul 2025 18:39:37 -0500 Subject: [PATCH 04/21] ed448-goldilocks v0.14.0-pre.2 (#1293) make hash_to_field output an array use primitive array instead of `hybrid_array::Array` fix: all uses of hash_to_field in other crates --- Cargo.lock | 2 +- ed448-goldilocks/Cargo.toml | 2 +- hash2curve/src/group_digest.rs | 17 +++++++---------- hash2curve/src/hash2field.rs | 11 +++++------ k256/src/arithmetic/hash2curve.rs | 6 +++--- p256/src/arithmetic/hash2curve.rs | 6 +++--- p384/src/arithmetic/hash2curve.rs | 6 +++--- p521/src/arithmetic/hash2curve.rs | 6 +++--- 8 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23e58dd51..295bcaa86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,7 @@ dependencies = [ [[package]] name = "ed448-goldilocks" -version = "0.14.0-pre.1" +version = "0.14.0-pre.2" dependencies = [ "ed448", "elliptic-curve", diff --git a/ed448-goldilocks/Cargo.toml b/ed448-goldilocks/Cargo.toml index 367714a6d..8096392ee 100644 --- a/ed448-goldilocks/Cargo.toml +++ b/ed448-goldilocks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ed448-goldilocks" -version = "0.14.0-pre.1" +version = "0.14.0-pre.2" authors = ["RustCrypto Developers"] categories = ["cryptography"] keywords = ["cryptography", "decaf", "ed448", "ed448-goldilocks"] diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index 74ecaf8b0..da493503c 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -36,10 +36,9 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::FieldElement::default(), Self::FieldElement::default()]; - hash_to_field::(msg, dst, &mut u)?; - let q0 = Self::map_to_curve(u[0]); - let q1 = Self::map_to_curve(u[1]); + let [u0, u1] = hash_to_field::<2, X, _, Self::FieldElement>(msg, dst)?; + let q0 = Self::map_to_curve(u0); + let q1 = Self::map_to_curve(u1); Ok(Self::add_and_map_to_subgroup(q0, q1)) } @@ -67,9 +66,8 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::FieldElement::default()]; - hash_to_field::(msg, dst, &mut u)?; - let q0 = Self::map_to_curve(u[0]); + let [u] = hash_to_field::<1, X, _, Self::FieldElement>(msg, dst)?; + let q0 = Self::map_to_curve(u); Ok(Self::map_to_subgroup(q0)) } @@ -91,8 +89,7 @@ pub trait GroupDigest: MapToCurve { where X: ExpandMsg, { - let mut u = [Self::Scalar::default()]; - hash_to_field::(msg, dst, &mut u)?; - Ok(u[0]) + let [u] = hash_to_field::<1, X, _, Self::Scalar>(msg, dst)?; + Ok(u) } } diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index ad7fa0d86..267cdefc6 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -38,21 +38,20 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]], out: &mut [T]) -> Result<()> +pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]> where E: ExpandMsg, T: FromOkm + Default, { let len_in_bytes = T::Length::USIZE - .checked_mul(out.len()) + .checked_mul(N) .and_then(|len| len.try_into().ok()) .and_then(NonZeroU16::new) .ok_or(Error)?; let mut tmp = Array::::Length>::default(); let mut expander = E::expand_message(data, domain, len_in_bytes)?; - for o in out.iter_mut() { + Ok(core::array::from_fn(|_| { expander.fill_bytes(&mut tmp); - *o = T::from_okm(&tmp); - } - Ok(()) + T::from_okm(&tmp) + })) } diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 87d210eb6..1e8c7a6ba 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -353,12 +353,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0); assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1); diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index 33419618e..e60441035 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -204,12 +204,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. diff --git a/p384/src/arithmetic/hash2curve.rs b/p384/src/arithmetic/hash2curve.rs index 087c87240..c364a53eb 100644 --- a/p384/src/arithmetic/hash2curve.rs +++ b/p384/src/arithmetic/hash2curve.rs @@ -209,12 +209,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. diff --git a/p521/src/arithmetic/hash2curve.rs b/p521/src/arithmetic/hash2curve.rs index 071104d15..8795ff8c9 100644 --- a/p521/src/arithmetic/hash2curve.rs +++ b/p521/src/arithmetic/hash2curve.rs @@ -212,12 +212,12 @@ mod tests { for test_vector in TEST_VECTORS { // in parts - let mut u = [FieldElement::default(), FieldElement::default()]; - hash2curve::hash_to_field::< + let u = hash2curve::hash_to_field::< + 2, ExpandMsgXmd, ::K, FieldElement, - >(&[test_vector.msg], &[DST], &mut u) + >(&[test_vector.msg], &[DST]) .unwrap(); /// Assert that the provided projective point matches the given test vector. From 8735cbafec16293bbf75e3a76655679269630f46 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sat, 19 Jul 2025 12:52:36 -0400 Subject: [PATCH 05/21] Use `Iterator` instead of `Expander` --- hash2curve/src/group_digest.rs | 15 +-- hash2curve/src/hash2field.rs | 12 ++- hash2curve/src/hash2field/expand_msg.rs | 15 +-- hash2curve/src/hash2field/expand_msg/xmd.rs | 114 ++++++++------------ hash2curve/src/hash2field/expand_msg/xof.rs | 43 ++++---- hash2curve/src/oprf.rs | 2 +- 6 files changed, 91 insertions(+), 110 deletions(-) diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index da493503c..2c3d0ddc3 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -32,9 +32,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn hash_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> + fn hash_from_bytes<'dst, X>(msg: &[&[u8]], dst: &'dst [&[u8]]) -> Result> where - X: ExpandMsg, + X: ExpandMsg<'dst, Self::K>, { let [u0, u1] = hash_to_field::<2, X, _, Self::FieldElement>(msg, dst)?; let q0 = Self::map_to_curve(u0); @@ -62,9 +62,12 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn encode_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> + fn encode_from_bytes<'dst, X>( + msg: &[&[u8]], + dst: &'dst [&[u8]], + ) -> Result> where - X: ExpandMsg, + X: ExpandMsg<'dst, Self::K>, { let [u] = hash_to_field::<1, X, _, Self::FieldElement>(msg, dst)?; let q0 = Self::map_to_curve(u); @@ -85,9 +88,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn hash_to_scalar(msg: &[&[u8]], dst: &[&[u8]]) -> Result + fn hash_to_scalar<'dst, X>(msg: &[&[u8]], dst: &'dst [&[u8]]) -> Result where - X: ExpandMsg, + X: ExpandMsg<'dst, Self::K>, { let [u] = hash_to_field::<1, X, _, Self::Scalar>(msg, dst)?; Ok(u) diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index 267cdefc6..41418478d 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -38,9 +38,12 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]> +pub fn hash_to_field<'dst, const N: usize, E, K, T>( + data: &[&[u8]], + domain: &'dst [&[u8]], +) -> Result<[T; N]> where - E: ExpandMsg, + E: ExpandMsg<'dst, K>, T: FromOkm + Default, { let len_in_bytes = T::Length::USIZE @@ -48,10 +51,11 @@ where .and_then(|len| len.try_into().ok()) .and_then(NonZeroU16::new) .ok_or(Error)?; - let mut tmp = Array::::Length>::default(); let mut expander = E::expand_message(data, domain, len_in_bytes)?; Ok(core::array::from_fn(|_| { - expander.fill_bytes(&mut tmp); + let tmp = Array::::Length>::from_iter( + expander.by_ref().take(T::Length::USIZE), + ); T::from_okm(&tmp) })) } diff --git a/hash2curve/src/hash2field/expand_msg.rs b/hash2curve/src/hash2field/expand_msg.rs index 5db42b73a..ccf89797c 100644 --- a/hash2curve/src/hash2field/expand_msg.rs +++ b/hash2curve/src/hash2field/expand_msg.rs @@ -22,25 +22,16 @@ const MAX_DST_LEN: usize = 255; /// /// # Errors /// See implementors of [`ExpandMsg`] for errors. -pub trait ExpandMsg { - /// Type holding data for the [`Expander`]. - type Expander<'dst>: Expander + Sized; - +pub trait ExpandMsg<'dst, K>: Iterator + 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<'dst>( + fn expand_message( msg: &[&[u8]], dst: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result>; -} - -/// Expander that, call `read` until enough bytes have been consumed. -pub trait Expander { - /// Fill the array with the expanded bytes - fn fill_bytes(&mut self, okm: &mut [u8]); + ) -> Result; } /// The domain separation tag diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index c464700d2..57786c80b 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -1,8 +1,8 @@ //! `expand_message_xmd` based on a hash function. -use core::{marker::PhantomData, num::NonZero, ops::Mul}; +use core::{num::NonZero, ops::Mul}; -use super::{Domain, ExpandMsg, Expander}; +use super::{Domain, ExpandMsg}; use digest::{ FixedOutput, HashMarker, array::{ @@ -21,12 +21,20 @@ use elliptic_curve::{Error, Result}; /// - `dst > 255 && HashT::OutputSize > 255` /// - `len_in_bytes > 255 * HashT::OutputSize` #[derive(Debug)] -pub struct ExpandMsgXmd(PhantomData) +pub struct ExpandMsgXmd<'a, HashT> where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, - HashT::OutputSize: IsLessOrEqual; + HashT::OutputSize: IsLessOrEqual, +{ + b_0: Array, + b_vals: Array, + domain: Domain<'a, HashT::OutputSize>, + index: u8, + offset: usize, + length: u16, +} -impl ExpandMsg for ExpandMsgXmd +impl<'dst, HashT, K> ExpandMsg<'dst, K> for ExpandMsgXmd<'dst, HashT> where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`. @@ -37,13 +45,11 @@ where K: Mul, HashT::OutputSize: IsGreaterOrEqual, Output = True>, { - type Expander<'dst> = ExpanderXmd<'dst, HashT>; - - fn expand_message<'dst>( + fn expand_message( msg: &[&[u8]], dst: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result> { + ) -> Result { let b_in_bytes = HashT::OutputSize::USIZE; // `255 * ` can not exceed `u16::MAX` @@ -51,9 +57,6 @@ where return Err(Error); } - let ell = u8::try_from(usize::from(len_in_bytes.get()).div_ceil(b_in_bytes)) - .expect("should never pass the previous check"); - let domain = Domain::xmd::(dst)?; let mut b_0 = HashT::default(); b_0.update(&Array::::default()); @@ -75,74 +78,51 @@ where b_vals.update(&[domain.len()]); let b_vals = b_vals.finalize_fixed(); - Ok(ExpanderXmd { + Ok(Self { b_0, b_vals, domain, index: 1, offset: 0, - ell, + length: len_in_bytes.get(), }) } } -/// [`Expander`] type for [`ExpandMsgXmd`]. -#[derive(Debug)] -pub struct ExpanderXmd<'a, HashT> +impl Iterator for ExpandMsgXmd<'_, HashT> where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, HashT::OutputSize: IsLessOrEqual, { - b_0: Array, - b_vals: Array, - domain: Domain<'a, HashT::OutputSize>, - index: u8, - offset: usize, - ell: u8, -} + type Item = u8; -impl ExpanderXmd<'_, HashT> -where - HashT: BlockSizeUser + Default + FixedOutput + HashMarker, - HashT::OutputSize: IsLessOrEqual, -{ - fn next(&mut self) -> bool { - if self.index < self.ell { - self.index += 1; - self.offset = 0; - // b_0 XOR b_(idx - 1) - let mut tmp = Array::::default(); - self.b_0 - .iter() - .zip(&self.b_vals[..]) - .enumerate() - .for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val); - let mut b_vals = HashT::default(); - b_vals.update(&tmp); - b_vals.update(&[self.index]); - self.domain.update_hash(&mut b_vals); - b_vals.update(&[self.domain.len()]); - self.b_vals = b_vals.finalize_fixed(); - true - } else { - false - } - } -} - -impl Expander for ExpanderXmd<'_, HashT> -where - HashT: BlockSizeUser + Default + FixedOutput + HashMarker, - HashT::OutputSize: IsLessOrEqual, -{ - fn fill_bytes(&mut self, okm: &mut [u8]) { - for b in okm { - if self.offset == self.b_vals.len() && !self.next() { - return; - } - *b = self.b_vals[self.offset]; + fn next(&mut self) -> Option { + if (self.index as u16 - 1) * HashT::OutputSize::U16 + self.offset as u16 + == self.length + { + return None; + } else if self.offset != self.b_vals.len() { + let byte = self.b_vals[self.offset]; self.offset += 1; + return Some(byte); } + + self.index += 1; + self.offset = 1; + // b_0 XOR b_(idx - 1) + let mut tmp = Array::::default(); + self.b_0 + .iter() + .zip(&self.b_vals[..]) + .enumerate() + .for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val); + let mut b_vals = HashT::default(); + b_vals.update(&tmp); + b_vals.update(&[self.index]); + self.domain.update_hash(&mut b_vals); + b_vals.update(&[self.domain.len()]); + self.b_vals = b_vals.finalize_fixed(); + Some(self.b_vals[0]) } } @@ -210,15 +190,13 @@ mod test { assert_message::(self.msg, domain, L::U16, self.msg_prime); let dst = [dst]; - let mut expander = as ExpandMsg>::expand_message( + let expander = as ExpandMsg>::expand_message( &[self.msg], &dst, NonZero::new(L::U16).ok_or(Error)?, )?; - let mut uniform_bytes = Array::::default(); - expander.fill_bytes(&mut uniform_bytes); - + let uniform_bytes = Array::::from_iter(expander); assert_eq!(uniform_bytes.as_slice(), self.uniform_bytes); Ok(()) } diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index 32d00a7c1..ed1129be0 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -1,9 +1,10 @@ //! `expand_message_xof` for the `ExpandMsg` trait -use super::{Domain, ExpandMsg, Expander}; +use super::{Domain, ExpandMsg}; use core::{fmt, num::NonZero, ops::Mul}; +use digest::XofReader; use digest::{ - CollisionResistance, ExtendableOutput, HashMarker, Update, XofReader, typenum::IsGreaterOrEqual, + CollisionResistance, ExtendableOutput, HashMarker, Update, typenum::IsGreaterOrEqual, }; use elliptic_curve::Result; use elliptic_curve::array::{ @@ -21,7 +22,8 @@ pub struct ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, { - reader: ::Reader, + reader: HashT::Reader, + length: u16, } impl fmt::Debug for ExpandMsgXof @@ -36,7 +38,7 @@ where } } -impl ExpandMsg for ExpandMsgXof +impl ExpandMsg<'_, 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`. @@ -46,13 +48,7 @@ where // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.2-2.1 HashT: CollisionResistance>, { - type Expander<'dst> = Self; - - fn expand_message<'dst>( - msg: &[&[u8]], - dst: &'dst [&[u8]], - len_in_bytes: NonZero, - ) -> Result> { + fn expand_message(msg: &[&[u8]], dst: &[&[u8]], len_in_bytes: NonZero) -> Result { let len_in_bytes = len_in_bytes.get(); let domain = Domain::>::xof::(dst)?; @@ -66,16 +62,27 @@ where domain.update_hash(&mut reader); reader.update(&[domain.len()]); let reader = reader.finalize_xof(); - Ok(Self { reader }) + Ok(Self { + reader, + length: len_in_bytes, + }) } } -impl Expander for ExpandMsgXof +impl Iterator for ExpandMsgXof where HashT: Default + ExtendableOutput + Update + HashMarker, { - fn fill_bytes(&mut self, okm: &mut [u8]) { - self.reader.read(okm); + type Item = u8; + + fn next(&mut self) -> Option { + if self.length == 0 { + return None; + } + self.length -= 1; + let mut buf = [0u8; 1]; + self.reader.read(&mut buf); + Some(buf[0]) } } @@ -130,15 +137,13 @@ mod test { { assert_message(self.msg, domain, L::to_u16(), self.msg_prime); - let mut expander = as ExpandMsg>::expand_message( + let expander = as ExpandMsg>::expand_message( &[self.msg], &[dst], NonZero::new(L::U16).ok_or(Error)?, )?; - let mut uniform_bytes = Array::::default(); - expander.fill_bytes(&mut uniform_bytes); - + let uniform_bytes = Array::::from_iter(expander); assert_eq!(uniform_bytes.as_slice(), self.uniform_bytes); Ok(()) } diff --git a/hash2curve/src/oprf.rs b/hash2curve/src/oprf.rs index 36a8caccf..a86e02d0f 100644 --- a/hash2curve/src/oprf.rs +++ b/hash2curve/src/oprf.rs @@ -25,5 +25,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: ExpandMsg<::K>; + type ExpandMsg<'a>: ExpandMsg<'a, ::K>; } From 1798532c582f2bd617886f931b317256261446e6 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 14:04:07 -0400 Subject: [PATCH 06/21] chore:fmt --- hash2curve/src/hash2field/expand_msg/xmd.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 57786c80b..79e9b3541 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -97,9 +97,7 @@ where type Item = u8; fn next(&mut self) -> Option { - if (self.index as u16 - 1) * HashT::OutputSize::U16 + self.offset as u16 - == self.length - { + if (self.index as u16 - 1) * HashT::OutputSize::U16 + self.offset as u16 == self.length { return None; } else if self.offset != self.b_vals.len() { let byte = self.b_vals[self.offset]; From 504a1dd65565f3506e25c6517cc65a292ad121a7 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 14:10:44 -0400 Subject: [PATCH 07/21] fix: elliptic-curves tests --- ed448-goldilocks/src/field/element.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ed448-goldilocks/src/field/element.rs b/ed448-goldilocks/src/field/element.rs index 2a1d9d59b..2f846eb66 100644 --- a/ed448-goldilocks/src/field/element.rs +++ b/ed448-goldilocks/src/field/element.rs @@ -441,7 +441,7 @@ impl FieldElement { mod tests { use super::*; use elliptic_curve::consts::U32; - use hash2curve::{ExpandMsg, ExpandMsgXof, Expander}; + use hash2curve::{ExpandMsg, ExpandMsgXof}; use hex_literal::hex; use sha3::Shake256; @@ -463,8 +463,7 @@ mod tests { (84 * 2).try_into().unwrap(), ) .unwrap(); - let mut data = Array::::default(); - expander.fill_bytes(&mut data); + let mut data = Array::::from_iter(expander.by_ref().take(84)); // TODO: This should be `Curve448FieldElement`. let u0 = Ed448FieldElement::from_okm(&data).0; let mut e_u0 = *expected_u0; @@ -472,7 +471,8 @@ mod tests { let mut e_u1 = *expected_u1; e_u1.reverse(); assert_eq!(u0.to_bytes(), e_u0); - expander.fill_bytes(&mut data); + data = Array::::from_iter(expander); + // TODO: This should be `Curve448FieldElement`. let u1 = Ed448FieldElement::from_okm(&data).0; assert_eq!(u1.to_bytes(), e_u1); @@ -497,15 +497,14 @@ mod tests { (84 * 2).try_into().unwrap(), ) .unwrap(); - let mut data = Array::::default(); - expander.fill_bytes(&mut data); + let mut data = Array::::from_iter(expander.by_ref().take(84)); let u0 = Ed448FieldElement::from_okm(&data).0; let mut e_u0 = *expected_u0; e_u0.reverse(); let mut e_u1 = *expected_u1; e_u1.reverse(); assert_eq!(u0.to_bytes(), e_u0); - expander.fill_bytes(&mut data); + data = Array::::from_iter(expander.by_ref()); let u1 = Ed448FieldElement::from_okm(&data).0; assert_eq!(u1.to_bytes(), e_u1); } From 14b902fdf44dc732dc05c5c332abb05b40b25d7c Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 14:13:41 -0400 Subject: [PATCH 08/21] fix: k256 tests --- k256/src/arithmetic/hash2curve.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 1e8c7a6ba..4ab0ac838 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -355,7 +355,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd, + ExpandMsgXmd<'_, Sha256>, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -379,8 +379,9 @@ mod tests { assert_eq!(ap.y.to_bytes().as_slice(), test_vector.p_y); // complete run - let pt = Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = + Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); let apt = pt.to_affine(); assert_eq!(apt.x.to_bytes().as_slice(), test_vector.p_x); assert_eq!(apt.y.to_bytes().as_slice(), test_vector.p_y); From a7fef2714e06aac3cf6150425b4a06c1e49c14e7 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 14:19:57 -0400 Subject: [PATCH 09/21] fix: voprf lifetime errors --- p256/src/arithmetic/hash2curve.rs | 9 +++++---- p256/src/lib.rs | 2 +- p384/src/arithmetic/hash2curve.rs | 9 +++++---- p384/src/lib.rs | 2 +- p521/src/arithmetic/hash2curve.rs | 9 +++++---- p521/src/lib.rs | 2 +- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index e60441035..def449ef2 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -206,7 +206,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd, + ExpandMsgXmd<'_, Sha256>, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -240,8 +240,9 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = + NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } } @@ -283,7 +284,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP256::hash_to_scalar::>( + let scalar = NistP256::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p256/src/lib.rs b/p256/src/lib.rs index 4ab8b6052..b98e0b8ce 100644 --- a/p256/src/lib.rs +++ b/p256/src/lib.rs @@ -182,5 +182,5 @@ impl hash2curve::OprfParameters for NistP256 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha256>; } diff --git a/p384/src/arithmetic/hash2curve.rs b/p384/src/arithmetic/hash2curve.rs index c364a53eb..8cb276745 100644 --- a/p384/src/arithmetic/hash2curve.rs +++ b/p384/src/arithmetic/hash2curve.rs @@ -211,7 +211,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd, + ExpandMsgXmd<'_, Sha384>, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -245,8 +245,9 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = NistP384::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = + NistP384::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } } @@ -294,7 +295,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP384::hash_to_scalar::>( + let scalar = NistP384::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p384/src/lib.rs b/p384/src/lib.rs index 0196be5ff..d2318ef2d 100644 --- a/p384/src/lib.rs +++ b/p384/src/lib.rs @@ -135,5 +135,5 @@ impl hash2curve::OprfParameters for NistP384 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha384>; } diff --git a/p521/src/arithmetic/hash2curve.rs b/p521/src/arithmetic/hash2curve.rs index 8795ff8c9..b9afd18d2 100644 --- a/p521/src/arithmetic/hash2curve.rs +++ b/p521/src/arithmetic/hash2curve.rs @@ -214,7 +214,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd, + ExpandMsgXmd<'_, Sha512>, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -248,8 +248,9 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = NistP521::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = + NistP521::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } } @@ -297,7 +298,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP521::hash_to_scalar::>( + let scalar = NistP521::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p521/src/lib.rs b/p521/src/lib.rs index 5d7e6296d..9b9f8925e 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -125,5 +125,5 @@ impl hash2curve::OprfParameters for NistP521 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha512>; } From 1f7abce6c07648fb76b76ef546c47c07c8579e7c Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon <76854355+carloskiki@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:38:34 -0400 Subject: [PATCH 10/21] nit: `collect` instead of `from_iter` Co-authored-by: daxpedda --- hash2curve/src/hash2field.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index 41418478d..97a5402a5 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -53,9 +53,6 @@ where .ok_or(Error)?; let mut expander = E::expand_message(data, domain, len_in_bytes)?; Ok(core::array::from_fn(|_| { - let tmp = Array::::Length>::from_iter( - expander.by_ref().take(T::Length::USIZE), - ); - T::from_okm(&tmp) + T::from_okm(&expander.by_ref().take(T::Length::USIZE).collect()) })) } From f791d15d6f8b57abbf5ffea12fd403135f11b08a Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon <76854355+carloskiki@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:43:06 -0400 Subject: [PATCH 11/21] nit: more idiomatic code Co-authored-by: daxpedda --- hash2curve/src/hash2field/expand_msg/xof.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index ed1129be0..10e617e65 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -80,9 +80,9 @@ where return None; } self.length -= 1; - let mut buf = [0u8; 1]; - self.reader.read(&mut buf); - Some(buf[0]) + let mut byte = 0; + self.reader.read(array::from_mut(&mut byte)); + Some(byte) } } From 3bb770e72e068d33324c79c63e30fb5cc7c4159b Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon <76854355+carloskiki@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:43:45 -0400 Subject: [PATCH 12/21] nit: more idiomatic code Co-authored-by: daxpedda --- ed448-goldilocks/src/field/element.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ed448-goldilocks/src/field/element.rs b/ed448-goldilocks/src/field/element.rs index 2f846eb66..18a583be8 100644 --- a/ed448-goldilocks/src/field/element.rs +++ b/ed448-goldilocks/src/field/element.rs @@ -463,9 +463,8 @@ mod tests { (84 * 2).try_into().unwrap(), ) .unwrap(); - let mut data = Array::::from_iter(expander.by_ref().take(84)); // TODO: This should be `Curve448FieldElement`. - let u0 = Ed448FieldElement::from_okm(&data).0; + let u0 = Ed448FieldElement::from_okm(&expander.by_ref().take(84).collect()).0; let mut e_u0 = *expected_u0; e_u0.reverse(); let mut e_u1 = *expected_u1; From 2302105f0947136d7af371a8e6536bb7a817e79e Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon <76854355+carloskiki@users.noreply.github.com> Date: Sun, 20 Jul 2025 17:46:49 -0400 Subject: [PATCH 13/21] nit: clearer procedure Co-authored-by: daxpedda --- hash2curve/src/hash2field/expand_msg/xmd.rs | 44 +++++++++++---------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 79e9b3541..15e568180 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -97,30 +97,32 @@ where type Item = u8; fn next(&mut self) -> Option { - if (self.index as u16 - 1) * HashT::OutputSize::U16 + self.offset as u16 == self.length { + if self.remaining == 0 { return None; - } else if self.offset != self.b_vals.len() { - let byte = self.b_vals[self.offset]; - self.offset += 1; - return Some(byte); } - self.index += 1; - self.offset = 1; - // b_0 XOR b_(idx - 1) - let mut tmp = Array::::default(); - self.b_0 - .iter() - .zip(&self.b_vals[..]) - .enumerate() - .for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val); - let mut b_vals = HashT::default(); - b_vals.update(&tmp); - b_vals.update(&[self.index]); - self.domain.update_hash(&mut b_vals); - b_vals.update(&[self.domain.len()]); - self.b_vals = b_vals.finalize_fixed(); - Some(self.b_vals[0]) + if self.offset == self.b_vals.len() { + self.index += 1; + self.offset = 0; + // b_0 XOR b_(idx - 1) + let mut tmp = Array::::default(); + self.b_0 + .iter() + .zip(&self.b_vals[..]) + .enumerate() + .for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val); + let mut b_vals = HashT::default(); + b_vals.update(&tmp); + b_vals.update(&[self.index]); + self.domain.update_hash(&mut b_vals); + b_vals.update(&[self.domain.len()]); + self.b_vals = b_vals.finalize_fixed(); + } + + let byte = self.b_vals[self.offset]; + self.offset += 1; + self.remaining -= 1; + Some(byte) } } From 94126757c7c01fdb1785d519eb4b94d8e4a612e9 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 17:51:30 -0400 Subject: [PATCH 14/21] fix: make it compile --- hash2curve/src/hash2field/expand_msg/xmd.rs | 4 ++-- hash2curve/src/hash2field/expand_msg/xof.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 15e568180..2f5da7169 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -31,7 +31,7 @@ where domain: Domain<'a, HashT::OutputSize>, index: u8, offset: usize, - length: u16, + remaining: u16, } impl<'dst, HashT, K> ExpandMsg<'dst, K> for ExpandMsgXmd<'dst, HashT> @@ -84,7 +84,7 @@ where domain, index: 1, offset: 0, - length: len_in_bytes.get(), + remaining: len_in_bytes.get(), }) } } diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index 10e617e65..290d24690 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -1,7 +1,7 @@ //! `expand_message_xof` for the `ExpandMsg` trait use super::{Domain, ExpandMsg}; -use core::{fmt, num::NonZero, ops::Mul}; +use core::{fmt, num::NonZero, ops::Mul, array}; use digest::XofReader; use digest::{ CollisionResistance, ExtendableOutput, HashMarker, Update, typenum::IsGreaterOrEqual, From 0e5ec569a3107551b7cff9b80a89afc5d1c6b131 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 17:51:41 -0400 Subject: [PATCH 15/21] chore: fmt --- hash2curve/src/hash2field/expand_msg/xof.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index 290d24690..eede3729a 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -1,7 +1,7 @@ //! `expand_message_xof` for the `ExpandMsg` trait use super::{Domain, ExpandMsg}; -use core::{fmt, num::NonZero, ops::Mul, array}; +use core::{array, fmt, num::NonZero, ops::Mul}; use digest::XofReader; use digest::{ CollisionResistance, ExtendableOutput, HashMarker, Update, typenum::IsGreaterOrEqual, From bf56ba85fceb028516396020d3d7f4bac09d8dab Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 20 Jul 2025 19:55:33 -0400 Subject: [PATCH 16/21] fix: compilation errors --- ed448-goldilocks/src/field/element.rs | 9 +++------ hash2curve/src/hash2field/expand_msg/xmd.rs | 2 +- hash2curve/src/hash2field/expand_msg/xof.rs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ed448-goldilocks/src/field/element.rs b/ed448-goldilocks/src/field/element.rs index 18a583be8..fcbf6c59d 100644 --- a/ed448-goldilocks/src/field/element.rs +++ b/ed448-goldilocks/src/field/element.rs @@ -470,10 +470,9 @@ mod tests { let mut e_u1 = *expected_u1; e_u1.reverse(); assert_eq!(u0.to_bytes(), e_u0); - data = Array::::from_iter(expander); // TODO: This should be `Curve448FieldElement`. - let u1 = Ed448FieldElement::from_okm(&data).0; + let u1 = Ed448FieldElement::from_okm(&expander.collect()).0; assert_eq!(u1.to_bytes(), e_u1); } } @@ -496,15 +495,13 @@ mod tests { (84 * 2).try_into().unwrap(), ) .unwrap(); - let mut data = Array::::from_iter(expander.by_ref().take(84)); - let u0 = Ed448FieldElement::from_okm(&data).0; + let u0 = Ed448FieldElement::from_okm(&expander.by_ref().take(84).collect()).0; let mut e_u0 = *expected_u0; e_u0.reverse(); let mut e_u1 = *expected_u1; e_u1.reverse(); assert_eq!(u0.to_bytes(), e_u0); - data = Array::::from_iter(expander.by_ref()); - let u1 = Ed448FieldElement::from_okm(&data).0; + let u1 = Ed448FieldElement::from_okm(&expander.collect()).0; assert_eq!(u1.to_bytes(), e_u1); } } diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 2f5da7169..8970abea3 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -196,7 +196,7 @@ mod test { NonZero::new(L::U16).ok_or(Error)?, )?; - let uniform_bytes = Array::::from_iter(expander); + let uniform_bytes: Array = expander.collect(); assert_eq!(uniform_bytes.as_slice(), self.uniform_bytes); Ok(()) } diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index eede3729a..d50875ce5 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -143,7 +143,7 @@ mod test { NonZero::new(L::U16).ok_or(Error)?, )?; - let uniform_bytes = Array::::from_iter(expander); + let uniform_bytes: Array = expander.collect(); assert_eq!(uniform_bytes.as_slice(), self.uniform_bytes); Ok(()) } From 6e0339d2271380af70de2f83fa0aa474655103fb Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 27 Jul 2025 12:21:13 -0400 Subject: [PATCH 17/21] `ExpandMsg` add associated type to remove trait lifetime --- hash2curve/src/group_digest.rs | 15 ++++++--------- hash2curve/src/hash2field.rs | 7 ++----- hash2curve/src/hash2field/expand_msg.rs | 9 ++++++--- hash2curve/src/hash2field/expand_msg/xmd.rs | 12 +++++++----- hash2curve/src/hash2field/expand_msg/xof.rs | 4 +++- hash2curve/src/oprf.rs | 2 +- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index 2c3d0ddc3..da493503c 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -32,9 +32,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn hash_from_bytes<'dst, X>(msg: &[&[u8]], dst: &'dst [&[u8]]) -> Result> + fn hash_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> where - X: ExpandMsg<'dst, Self::K>, + X: ExpandMsg, { let [u0, u1] = hash_to_field::<2, X, _, Self::FieldElement>(msg, dst)?; let q0 = Self::map_to_curve(u0); @@ -62,12 +62,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn encode_from_bytes<'dst, X>( - msg: &[&[u8]], - dst: &'dst [&[u8]], - ) -> Result> + fn encode_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result> where - X: ExpandMsg<'dst, Self::K>, + X: ExpandMsg, { let [u] = hash_to_field::<1, X, _, Self::FieldElement>(msg, dst)?; let q0 = Self::map_to_curve(u); @@ -88,9 +85,9 @@ pub trait GroupDigest: MapToCurve { /// /// [`ExpandMsgXmd`]: crate::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::ExpandMsgXof - fn hash_to_scalar<'dst, X>(msg: &[&[u8]], dst: &'dst [&[u8]]) -> Result + fn hash_to_scalar(msg: &[&[u8]], dst: &[&[u8]]) -> Result where - X: ExpandMsg<'dst, Self::K>, + X: ExpandMsg, { let [u] = hash_to_field::<1, X, _, Self::Scalar>(msg, dst)?; Ok(u) diff --git a/hash2curve/src/hash2field.rs b/hash2curve/src/hash2field.rs index 97a5402a5..12094c37c 100644 --- a/hash2curve/src/hash2field.rs +++ b/hash2curve/src/hash2field.rs @@ -38,12 +38,9 @@ pub trait FromOkm { /// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd /// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof #[doc(hidden)] -pub fn hash_to_field<'dst, const N: usize, E, K, T>( - data: &[&[u8]], - domain: &'dst [&[u8]], -) -> Result<[T; N]> +pub fn hash_to_field(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]> where - E: ExpandMsg<'dst, K>, + E: ExpandMsg, T: FromOkm + Default, { let len_in_bytes = T::Length::USIZE diff --git a/hash2curve/src/hash2field/expand_msg.rs b/hash2curve/src/hash2field/expand_msg.rs index ccf89797c..f070b3a82 100644 --- a/hash2curve/src/hash2field/expand_msg.rs +++ b/hash2curve/src/hash2field/expand_msg.rs @@ -22,16 +22,19 @@ const MAX_DST_LEN: usize = 255; /// /// # Errors /// See implementors of [`ExpandMsg`] for errors. -pub trait ExpandMsg<'dst, K>: Iterator + Sized { +pub trait ExpandMsg { + /// The expanded message. + type Expanded<'a>: Iterator; + /// 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: &'dst [&[u8]], len_in_bytes: NonZero, - ) -> Result; + ) -> Result>; } /// The domain separation tag diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 8970abea3..9a42582a2 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -34,7 +34,7 @@ where remaining: u16, } -impl<'dst, HashT, K> ExpandMsg<'dst, K> for ExpandMsgXmd<'dst, HashT> +impl ExpandMsg for ExpandMsgXmd<'_, HashT> where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`. @@ -45,11 +45,13 @@ where K: Mul, HashT::OutputSize: IsGreaterOrEqual, Output = True>, { - fn expand_message( + type Expanded<'a> = ExpandMsgXmd<'a, HashT>; + + fn expand_message<'a>( msg: &[&[u8]], - dst: &'dst [&[u8]], + dst: &'a [&[u8]], len_in_bytes: NonZero, - ) -> Result { + ) -> Result> { let b_in_bytes = HashT::OutputSize::USIZE; // `255 * ` can not exceed `u16::MAX` @@ -78,7 +80,7 @@ where b_vals.update(&[domain.len()]); let b_vals = b_vals.finalize_fixed(); - Ok(Self { + Ok(ExpandMsgXmd { b_0, b_vals, domain, diff --git a/hash2curve/src/hash2field/expand_msg/xof.rs b/hash2curve/src/hash2field/expand_msg/xof.rs index d50875ce5..5745a2b4a 100644 --- a/hash2curve/src/hash2field/expand_msg/xof.rs +++ b/hash2curve/src/hash2field/expand_msg/xof.rs @@ -38,7 +38,7 @@ where } } -impl ExpandMsg<'_, 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`. @@ -48,6 +48,8 @@ where // https://www.rfc-editor.org/rfc/rfc9380.html#section-5.3.2-2.1 HashT: CollisionResistance>, { + type Expanded<'a> = Self; + fn expand_message(msg: &[&[u8]], dst: &[&[u8]], len_in_bytes: NonZero) -> Result { let len_in_bytes = len_in_bytes.get(); diff --git a/hash2curve/src/oprf.rs b/hash2curve/src/oprf.rs index a86e02d0f..36a8caccf 100644 --- a/hash2curve/src/oprf.rs +++ b/hash2curve/src/oprf.rs @@ -25,5 +25,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<'a>: ExpandMsg<'a, ::K>; + type ExpandMsg: ExpandMsg<::K>; } From 7a67db10063abadb8ec28f09bc89caad1d025432 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 27 Jul 2025 12:25:45 -0400 Subject: [PATCH 18/21] fix: downstream users --- k256/src/arithmetic/hash2curve.rs | 2 +- p256/src/arithmetic/hash2curve.rs | 2 +- p256/src/lib.rs | 2 +- p384/src/arithmetic/hash2curve.rs | 9 ++++----- p384/src/lib.rs | 2 +- p521/src/arithmetic/hash2curve.rs | 9 ++++----- p521/src/lib.rs | 2 +- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 4ab0ac838..4172e6f99 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -355,7 +355,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd<'_, Sha256>, + ExpandMsgXmd, ::K, FieldElement, >(&[test_vector.msg], &[DST]) diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index def449ef2..567788a7a 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -206,7 +206,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd<'_, Sha256>, + ExpandMsgXmd, ::K, FieldElement, >(&[test_vector.msg], &[DST]) diff --git a/p256/src/lib.rs b/p256/src/lib.rs index b98e0b8ce..4ab8b6052 100644 --- a/p256/src/lib.rs +++ b/p256/src/lib.rs @@ -182,5 +182,5 @@ impl hash2curve::OprfParameters for NistP256 { /// See /// and . - type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha256>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } diff --git a/p384/src/arithmetic/hash2curve.rs b/p384/src/arithmetic/hash2curve.rs index 8cb276745..c364a53eb 100644 --- a/p384/src/arithmetic/hash2curve.rs +++ b/p384/src/arithmetic/hash2curve.rs @@ -211,7 +211,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd<'_, Sha384>, + ExpandMsgXmd, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -245,9 +245,8 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = - NistP384::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = NistP384::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } } @@ -295,7 +294,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP384::hash_to_scalar::>( + let scalar = NistP384::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p384/src/lib.rs b/p384/src/lib.rs index d2318ef2d..0196be5ff 100644 --- a/p384/src/lib.rs +++ b/p384/src/lib.rs @@ -135,5 +135,5 @@ impl hash2curve::OprfParameters for NistP384 { /// See /// and . - type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha384>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } diff --git a/p521/src/arithmetic/hash2curve.rs b/p521/src/arithmetic/hash2curve.rs index b9afd18d2..8795ff8c9 100644 --- a/p521/src/arithmetic/hash2curve.rs +++ b/p521/src/arithmetic/hash2curve.rs @@ -214,7 +214,7 @@ mod tests { // in parts let u = hash2curve::hash_to_field::< 2, - ExpandMsgXmd<'_, Sha512>, + ExpandMsgXmd, ::K, FieldElement, >(&[test_vector.msg], &[DST]) @@ -248,9 +248,8 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = - NistP521::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = NistP521::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } } @@ -298,7 +297,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP521::hash_to_scalar::>( + let scalar = NistP521::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p521/src/lib.rs b/p521/src/lib.rs index 9b9f8925e..5d7e6296d 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -125,5 +125,5 @@ impl hash2curve::OprfParameters for NistP521 { /// See /// and . - type ExpandMsg<'a> = hash2curve::ExpandMsgXmd<'a, sha2::Sha512>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } From b6f7b53ff4d232a5d94891c766c8485b0298bbb0 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 27 Jul 2025 13:17:27 -0400 Subject: [PATCH 19/21] fix: use static as placeholder lifetime --- p256/src/lib.rs | 2 +- p384/src/lib.rs | 2 +- p521/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/p256/src/lib.rs b/p256/src/lib.rs index 4ab8b6052..b2b293152 100644 --- a/p256/src/lib.rs +++ b/p256/src/lib.rs @@ -182,5 +182,5 @@ impl hash2curve::OprfParameters for NistP256 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha256>; } diff --git a/p384/src/lib.rs b/p384/src/lib.rs index 0196be5ff..afa92304d 100644 --- a/p384/src/lib.rs +++ b/p384/src/lib.rs @@ -135,5 +135,5 @@ impl hash2curve::OprfParameters for NistP384 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha384>; } diff --git a/p521/src/lib.rs b/p521/src/lib.rs index 5d7e6296d..cc9de71e9 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -125,5 +125,5 @@ impl hash2curve::OprfParameters for NistP521 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd; + type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha512>; } From 4752c70c559ca01e62ea690cf0380848e9dcd2c6 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 27 Jul 2025 14:13:35 -0400 Subject: [PATCH 20/21] separate `ExpandMsgXmd` and `ExpandedXmd` --- hash2curve/src/hash2field/expand_msg/xmd.rs | 38 ++++++++++++--------- k256/src/arithmetic/hash2curve.rs | 2 +- p256/src/arithmetic/hash2curve.rs | 4 +-- p256/src/lib.rs | 2 +- p384/src/lib.rs | 2 +- p521/src/lib.rs | 2 +- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/hash2curve/src/hash2field/expand_msg/xmd.rs b/hash2curve/src/hash2field/expand_msg/xmd.rs index 9a42582a2..4491fd444 100644 --- a/hash2curve/src/hash2field/expand_msg/xmd.rs +++ b/hash2curve/src/hash2field/expand_msg/xmd.rs @@ -20,21 +20,10 @@ use elliptic_curve::{Error, Result}; /// - `dst` contains no bytes /// - `dst > 255 && HashT::OutputSize > 255` /// - `len_in_bytes > 255 * HashT::OutputSize` -#[derive(Debug)] -pub struct ExpandMsgXmd<'a, HashT> -where - HashT: BlockSizeUser + Default + FixedOutput + HashMarker, - HashT::OutputSize: IsLessOrEqual, -{ - b_0: Array, - b_vals: Array, - domain: Domain<'a, HashT::OutputSize>, - index: u8, - offset: usize, - remaining: u16, -} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ExpandMsgXmd(core::marker::PhantomData); -impl ExpandMsg for ExpandMsgXmd<'_, HashT> +impl ExpandMsg for ExpandMsgXmd where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, // The number of bits output by `HashT` MUST be at most `HashT::BlockSize`. @@ -45,7 +34,7 @@ where K: Mul, HashT::OutputSize: IsGreaterOrEqual, Output = True>, { - type Expanded<'a> = ExpandMsgXmd<'a, HashT>; + type Expanded<'a> = ExpandedXmd<'a, HashT>; fn expand_message<'a>( msg: &[&[u8]], @@ -80,7 +69,7 @@ where b_vals.update(&[domain.len()]); let b_vals = b_vals.finalize_fixed(); - Ok(ExpandMsgXmd { + Ok(ExpandedXmd { b_0, b_vals, domain, @@ -91,7 +80,22 @@ where } } -impl Iterator for ExpandMsgXmd<'_, HashT> +/// The expanded bytes of `expand_message_xmd`. +#[derive(Debug)] +pub struct ExpandedXmd<'a, HashT> +where + HashT: BlockSizeUser + Default + FixedOutput + HashMarker, + HashT::OutputSize: IsLessOrEqual, +{ + b_0: Array, + b_vals: Array, + domain: Domain<'a, HashT::OutputSize>, + index: u8, + offset: usize, + remaining: u16, +} + +impl Iterator for ExpandedXmd<'_, HashT> where HashT: BlockSizeUser + Default + FixedOutput + HashMarker, HashT::OutputSize: IsLessOrEqual, diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 4172e6f99..922ec26e7 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -380,7 +380,7 @@ mod tests { // complete run let pt = - Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) + Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) .unwrap(); let apt = pt.to_affine(); assert_eq!(apt.x.to_bytes().as_slice(), test_vector.p_x); diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index 567788a7a..276629519 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -241,7 +241,7 @@ mod tests { // complete run let pt = - NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) + NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } @@ -284,7 +284,7 @@ mod tests { .to_be_bytes(); for counter in 0_u8..=u8::MAX { - let scalar = NistP256::hash_to_scalar::>( + let scalar = NistP256::hash_to_scalar::>( &[ test_vector.seed, &key_info_len, diff --git a/p256/src/lib.rs b/p256/src/lib.rs index b2b293152..4ab8b6052 100644 --- a/p256/src/lib.rs +++ b/p256/src/lib.rs @@ -182,5 +182,5 @@ impl hash2curve::OprfParameters for NistP256 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha256>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } diff --git a/p384/src/lib.rs b/p384/src/lib.rs index afa92304d..0196be5ff 100644 --- a/p384/src/lib.rs +++ b/p384/src/lib.rs @@ -135,5 +135,5 @@ impl hash2curve::OprfParameters for NistP384 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha384>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } diff --git a/p521/src/lib.rs b/p521/src/lib.rs index cc9de71e9..5d7e6296d 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -125,5 +125,5 @@ impl hash2curve::OprfParameters for NistP521 { /// See /// and . - type ExpandMsg = hash2curve::ExpandMsgXmd<'static, sha2::Sha512>; + type ExpandMsg = hash2curve::ExpandMsgXmd; } From c0ec7da5683cd8dddfdf8762705a4e5ee81734ba Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sun, 27 Jul 2025 14:13:43 -0400 Subject: [PATCH 21/21] chore: fmt --- k256/src/arithmetic/hash2curve.rs | 5 ++--- p256/src/arithmetic/hash2curve.rs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index 922ec26e7..1e8c7a6ba 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -379,9 +379,8 @@ mod tests { assert_eq!(ap.y.to_bytes().as_slice(), test_vector.p_y); // complete run - let pt = - Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = Secp256k1::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); let apt = pt.to_affine(); assert_eq!(apt.x.to_bytes().as_slice(), test_vector.p_x); assert_eq!(apt.y.to_bytes().as_slice(), test_vector.p_y); diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index 276629519..e60441035 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -240,9 +240,8 @@ mod tests { assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = - NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) - .unwrap(); + let pt = NistP256::hash_from_bytes::>(&[test_vector.msg], &[DST]) + .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } }