Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3fba6a7
implementors of `ExpandMsg` define the `Error` type
carloskiki Jul 31, 2025
63e4bd3
fix: clearer comment
carloskiki Jul 31, 2025
2d6a968
nit: rename `EmptyDST` to `DSTError`
carloskiki Jul 31, 2025
a3e6f6b
fix: adjust documentation
carloskiki Jul 31, 2025
05d8f92
chore: fmt
carloskiki Jul 31, 2025
7daf4ea
Update hash2curve/src/hash2field/expand_msg/xof.rs
carloskiki Aug 2, 2025
387cf66
fix: static assert in hash2field
carloskiki Aug 2, 2025
a659f8b
`EmptyDST` => `EmptyDst`
carloskiki Aug 2, 2025
59b7896
fix: redundant variant name
carloskiki Aug 2, 2025
4918b43
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 2, 2025
5304eb1
nit: rename variant
carloskiki Aug 2, 2025
4e52267
add: docs about `len_in_bytes`
carloskiki Aug 2, 2025
0ea02c8
fix: dst errors
carloskiki Aug 2, 2025
1f7d978
chore: fmt
carloskiki Aug 2, 2025
23242be
remove over restrictive bounds
carloskiki Aug 2, 2025
97374b0
chore: fmt
carloskiki Aug 2, 2025
93ba16f
fix: docs
carloskiki Aug 2, 2025
a6af7c1
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 3, 2025
b2899bb
Update hash2curve/src/hash2field.rs
carloskiki Aug 3, 2025
b58c2a2
flatten errors
carloskiki Aug 5, 2025
b0b435d
Update group_digest.rs
carloskiki Aug 5, 2025
4c074ce
Update group_digest.rs
carloskiki Aug 5, 2025
7471ad8
Update expand_msg.rs
carloskiki Aug 5, 2025
490c9c0
standardize comments
carloskiki Aug 5, 2025
0d5fb7c
add: comments about `len_in_bytes` < u16::MAX
carloskiki Aug 5, 2025
e43337a
nit: add doc links where possible
carloskiki Aug 5, 2025
b28e46f
fix imports to minimize diff
carloskiki Aug 5, 2025
9bb1690
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 7, 2025
1cff5b6
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 7, 2025
e84f772
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 7, 2025
54c1faa
Update hash2curve/src/hash2field/expand_msg/xmd.rs
carloskiki Aug 7, 2025
919fbd4
Update hash2curve/src/hash2field/expand_msg/xof.rs
carloskiki Aug 7, 2025
a895b78
Update hash2curve/src/hash2field/expand_msg/xof.rs
carloskiki Aug 7, 2025
0a3b2cc
Update hash2curve/src/group_digest.rs
carloskiki Aug 7, 2025
f917470
replace qualifed path with use
carloskiki Aug 7, 2025
09bc9ff
Add docs for `len_in_bytes` in hash_to_field
carloskiki Aug 7, 2025
2b42323
chore: fmt
carloskiki Aug 7, 2025
e250638
Update hash2curve/src/group_digest.rs
carloskiki Aug 8, 2025
6152101
Update hash2curve/src/group_digest.rs
carloskiki Aug 8, 2025
7a6cc6a
Update hash2curve/src/group_digest.rs
carloskiki Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 19 additions & 29 deletions hash2curve/src/group_digest.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! Traits for handling hash to curve.

use super::{ExpandMsg, MapToCurve, hash_to_field};
use elliptic_curve::array::typenum::Unsigned;
use elliptic_curve::{ProjectivePoint, Result};
use elliptic_curve::{ProjectivePoint, array::typenum::Unsigned};

/// Hash arbitrary byte sequences to a valid group element.
pub trait GroupDigest: MapToCurve {
Expand All @@ -22,17 +21,14 @@ pub trait GroupDigest: MapToCurve {
/// > oracle returning points in G assuming a cryptographically secure
/// > hash function is used.
///
/// # Errors
/// - `len_in_bytes > u16::MAX`
/// - See implementors of [`ExpandMsg`] for additional errors:
/// - [`ExpandMsgXmd`]
/// - [`ExpandMsgXof`]
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length * 2`.
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
///
/// `len_in_bytes = <Self::FieldElement as FromOkm>::Length * 2`
/// # Errors
///
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
fn hash_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`crate::ExpandMsgXmd`]
/// and [`crate::ExpandMsgXof`] for examples.
fn hash_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>, X::Error>
where
X: ExpandMsg<Self::K>,
{
Expand All @@ -52,17 +48,14 @@ pub trait GroupDigest: MapToCurve {
/// > encode_to_curve is only a fraction of the points in G, and some
/// > points in this set are more likely to be output than others.
///
/// # Errors
/// - `len_in_bytes > u16::MAX`
/// - See implementors of [`ExpandMsg`] for additional errors:
/// - [`ExpandMsgXmd`]
/// - [`ExpandMsgXof`]
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length`.
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
///
/// `len_in_bytes = <Self::FieldElement as FromOkm>::Length`
/// # Errors
///
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
fn encode_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`crate::ExpandMsgXmd`]
/// and [`crate::ExpandMsgXof`] for examples.
fn encode_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>, X::Error>
where
X: ExpandMsg<Self::K>,
{
Expand All @@ -74,18 +67,15 @@ pub trait GroupDigest: MapToCurve {
/// Computes the hash to field routine according to
/// <https://www.rfc-editor.org/rfc/rfc9380.html#section-5-4>
/// and returns a scalar.
///
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length`.
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
///
/// # Errors
/// - `len_in_bytes > u16::MAX`
/// - See implementors of [`ExpandMsg`] for additional errors:
/// - [`ExpandMsgXmd`]
/// - [`ExpandMsgXof`]
///
/// `len_in_bytes = <Self::Scalar as FromOkm>::Length`
///
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
fn hash_to_scalar<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar>
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`crate::ExpandMsgXmd`]
/// and [`crate::ExpandMsgXof`] for examples.
fn hash_to_scalar<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, X::Error>
where
X: ExpandMsg<Self::K>,
{
Expand Down
28 changes: 13 additions & 15 deletions hash2curve/src/hash2field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use elliptic_curve::array::{
Array, ArraySize,
typenum::{NonZero, Unsigned},
};
use elliptic_curve::{Error, Result};

/// The trait for helping to convert to a field element.
pub trait FromOkm {
Expand All @@ -28,26 +27,25 @@ pub trait FromOkm {
/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-hash_to_field-implementatio>
///
/// # Errors
/// - `len_in_bytes > u16::MAX`
/// - See implementors of [`ExpandMsg`] for additional errors:
/// - [`ExpandMsgXmd`]
/// - [`ExpandMsgXof`]
///
/// `len_in_bytes = T::Length * out.len()`
///
/// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd
/// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof
/// Returns an error if the [`ExpandMsg`] implementation fails.
#[doc(hidden)]
pub fn hash_to_field<const N: usize, E, K, T>(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]>
pub fn hash_to_field<const N: usize, E, K, T>(
data: &[&[u8]],
domain: &[&[u8]],
) -> Result<[T; N], E::Error>
where
E: ExpandMsg<K>,
T: FromOkm + Default,
{
let len_in_bytes = T::Length::USIZE
.checked_mul(N)
.and_then(|len| len.try_into().ok())
.and_then(NonZeroU16::new)
.ok_or(Error)?;
// Completely degenerate case; `N` and `T::Length` would need to be extremely large.
let len_in_bytes = const {
assert!(
T::Length::USIZE.saturating_mul(N) <= u16::MAX as usize,
"The product of `T::Length` and `N` must not exceed `u16::MAX`."
);
NonZeroU16::new(T::Length::U16 * N as u16).expect("N is greater than 0")
};
let mut tmp = Array::<u8, <T as FromOkm>::Length>::default();
let mut expander = E::expand_message(data, domain, len_in_bytes)?;
Ok(core::array::from_fn(|_| {
Expand Down
19 changes: 9 additions & 10 deletions hash2curve/src/hash2field/expand_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use core::num::NonZero;

use digest::{Digest, ExtendableOutput, Update, XofReader};
use elliptic_curve::array::{Array, ArraySize};
use elliptic_curve::{Error, Result};

/// Salt when the DST is too long
const OVERSIZE_DST_SALT: &[u8] = b"H2C-OVERSIZE-DST-";
Expand All @@ -25,6 +24,8 @@ const MAX_DST_LEN: usize = 255;
pub trait ExpandMsg<K> {
/// Type holding data for the [`Expander`].
type Expander<'dst>: Expander + Sized;
/// Error returned by [`ExpandMsg::expand_message`].
type Error: core::error::Error;

/// Expands `msg` to the required number of bytes.
///
Expand All @@ -34,7 +35,7 @@ pub trait ExpandMsg<K> {
msg: &[&[u8]],
dst: &'dst [&[u8]],
len_in_bytes: NonZero<u16>,
) -> Result<Self::Expander<'dst>>;
) -> Result<Self::Expander<'dst>, Self::Error>;
}

/// Expander that, call `read` until enough bytes have been consumed.
Expand All @@ -57,18 +58,17 @@ pub(crate) enum Domain<'a, L: ArraySize> {
}

impl<'a, L: ArraySize> Domain<'a, L> {
pub fn xof<X>(dst: &'a [&'a [u8]]) -> Result<Self>
pub fn xof<X>(dst: &'a [&'a [u8]]) -> Result<Self, xof::ExpandMsgXofError>
where
X: Default + ExtendableOutput + Update,
{
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2
if dst.iter().map(|slice| slice.len()).sum::<usize>() == 0 {
Err(Error)
Err(xof::ExpandMsgXofError::EmptyDst)
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
if L::USIZE > u8::MAX.into() {
return Err(Error);
return Err(xof::ExpandMsgXofError::DstSecurityLevel);
}

let mut data = Array::<u8, L>::default();
let mut hash = X::default();
hash.update(OVERSIZE_DST_SALT);
Expand All @@ -85,18 +85,17 @@ impl<'a, L: ArraySize> Domain<'a, L> {
}
}

pub fn xmd<X>(dst: &'a [&'a [u8]]) -> Result<Self>
pub fn xmd<X>(dst: &'a [&'a [u8]]) -> Result<Self, xmd::ExpandMsgXmdError>
where
X: Digest<OutputSize = L>,
{
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2
if dst.iter().map(|slice| slice.len()).sum::<usize>() == 0 {
Err(Error)
Err(xmd::ExpandMsgXmdError::EmptyDst)
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
if L::USIZE > u8::MAX.into() {
return Err(Error);
return Err(xmd::ExpandMsgXmdError::DstHash);
}

Ok(Self::Hashed({
let mut hash = X::new();
hash.update(OVERSIZE_DST_SALT);
Expand Down
Loading