Skip to content

Commit e73bccf

Browse files
authored
hash2curve: error rework (#1338)
Discussed in #1295
1 parent 4e9e34a commit e73bccf

File tree

5 files changed

+141
-115
lines changed

5 files changed

+141
-115
lines changed

hash2curve/src/group_digest.rs

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//! Traits for handling hash to curve.
22
33
use super::{ExpandMsg, MapToCurve, hash_to_field};
4-
use elliptic_curve::array::typenum::Unsigned;
5-
use elliptic_curve::{ProjectivePoint, Result};
4+
use elliptic_curve::{ProjectivePoint, array::typenum::Unsigned};
65

76
/// Hash arbitrary byte sequences to a valid group element.
87
pub trait GroupDigest: MapToCurve {
@@ -22,17 +21,17 @@ pub trait GroupDigest: MapToCurve {
2221
/// > oracle returning points in G assuming a cryptographically secure
2322
/// > hash function is used.
2423
///
24+
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length * 2`.
25+
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
26+
///
2527
/// # Errors
26-
/// - `len_in_bytes > u16::MAX`
27-
/// - See implementors of [`ExpandMsg`] for additional errors:
28-
/// - [`ExpandMsgXmd`]
29-
/// - [`ExpandMsgXof`]
3028
///
31-
/// `len_in_bytes = <Self::FieldElement as FromOkm>::Length * 2`
29+
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`]
30+
/// and [`ExpandMsgXofError`] for examples.
3231
///
33-
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
34-
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
35-
fn hash_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
32+
/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError
33+
/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError
34+
fn hash_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>, X::Error>
3635
where
3736
X: ExpandMsg<Self::K>,
3837
{
@@ -52,17 +51,17 @@ pub trait GroupDigest: MapToCurve {
5251
/// > encode_to_curve is only a fraction of the points in G, and some
5352
/// > points in this set are more likely to be output than others.
5453
///
54+
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length`.
55+
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
56+
///
5557
/// # Errors
56-
/// - `len_in_bytes > u16::MAX`
57-
/// - See implementors of [`ExpandMsg`] for additional errors:
58-
/// - [`ExpandMsgXmd`]
59-
/// - [`ExpandMsgXof`]
6058
///
61-
/// `len_in_bytes = <Self::FieldElement as FromOkm>::Length`
59+
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`]
60+
/// and [`ExpandMsgXofError`] for examples.
6261
///
63-
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
64-
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
65-
fn encode_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
62+
/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError
63+
/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError
64+
fn encode_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>, X::Error>
6665
where
6766
X: ExpandMsg<Self::K>,
6867
{
@@ -74,18 +73,18 @@ pub trait GroupDigest: MapToCurve {
7473
/// Computes the hash to field routine according to
7574
/// <https://www.rfc-editor.org/rfc/rfc9380.html#section-5-4>
7675
/// and returns a scalar.
76+
///
77+
/// For the `expand_message` call, `len_in_bytes = <Self::FieldElement as FromOkm>::Length`.
78+
/// This value must be less than `u16::MAX` or otherwise a compiler error will occur.
7779
///
7880
/// # Errors
79-
/// - `len_in_bytes > u16::MAX`
80-
/// - See implementors of [`ExpandMsg`] for additional errors:
81-
/// - [`ExpandMsgXmd`]
82-
/// - [`ExpandMsgXof`]
8381
///
84-
/// `len_in_bytes = <Self::Scalar as FromOkm>::Length`
82+
/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`]
83+
/// and [`ExpandMsgXofError`] for examples.
8584
///
86-
/// [`ExpandMsgXmd`]: crate::ExpandMsgXmd
87-
/// [`ExpandMsgXof`]: crate::ExpandMsgXof
88-
fn hash_to_scalar<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar>
85+
/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError
86+
/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError
87+
fn hash_to_scalar<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, X::Error>
8988
where
9089
X: ExpandMsg<Self::K>,
9190
{

hash2curve/src/hash2field.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use elliptic_curve::array::{
1212
Array, ArraySize,
1313
typenum::{NonZero, Unsigned},
1414
};
15-
use elliptic_curve::{Error, Result};
1615

1716
/// The trait for helping to convert to a field element.
1817
pub trait FromOkm {
@@ -27,27 +26,28 @@ pub trait FromOkm {
2726
///
2827
/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-hash_to_field-implementatio>
2928
///
30-
/// # Errors
31-
/// - `len_in_bytes > u16::MAX`
32-
/// - See implementors of [`ExpandMsg`] for additional errors:
33-
/// - [`ExpandMsgXmd`]
34-
/// - [`ExpandMsgXof`]
29+
/// For the `expand_message` call, `len_in_bytes = T::Length * N`.
3530
///
36-
/// `len_in_bytes = T::Length * out.len()`
31+
/// # Errors
3732
///
38-
/// [`ExpandMsgXmd`]: crate::hash2field::ExpandMsgXmd
39-
/// [`ExpandMsgXof`]: crate::hash2field::ExpandMsgXof
33+
/// Returns an error if the [`ExpandMsg`] implementation fails.
4034
#[doc(hidden)]
41-
pub fn hash_to_field<const N: usize, E, K, T>(data: &[&[u8]], domain: &[&[u8]]) -> Result<[T; N]>
35+
pub fn hash_to_field<const N: usize, E, K, T>(
36+
data: &[&[u8]],
37+
domain: &[&[u8]],
38+
) -> Result<[T; N], E::Error>
4239
where
4340
E: ExpandMsg<K>,
4441
T: FromOkm + Default,
4542
{
46-
let len_in_bytes = T::Length::USIZE
47-
.checked_mul(N)
48-
.and_then(|len| len.try_into().ok())
49-
.and_then(NonZeroU16::new)
50-
.ok_or(Error)?;
43+
// Completely degenerate case; `N` and `T::Length` would need to be extremely large.
44+
let len_in_bytes = const {
45+
assert!(
46+
T::Length::USIZE.saturating_mul(N) <= u16::MAX as usize,
47+
"The product of `T::Length` and `N` must not exceed `u16::MAX`."
48+
);
49+
NonZeroU16::new(T::Length::U16 * N as u16).expect("N is greater than 0")
50+
};
5151
let mut tmp = Array::<u8, <T as FromOkm>::Length>::default();
5252
let mut expander = E::expand_message(data, domain, len_in_bytes)?;
5353
Ok(core::array::from_fn(|_| {

hash2curve/src/hash2field/expand_msg.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use core::num::NonZero;
77

88
use digest::{Digest, ExtendableOutput, Update, XofReader};
99
use elliptic_curve::array::{Array, ArraySize};
10-
use elliptic_curve::{Error, Result};
10+
use xmd::ExpandMsgXmdError;
11+
use xof::ExpandMsgXofError;
1112

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

2932
/// Expands `msg` to the required number of bytes.
3033
///
@@ -34,7 +37,7 @@ pub trait ExpandMsg<K> {
3437
msg: &[&[u8]],
3538
dst: &'dst [&[u8]],
3639
len_in_bytes: NonZero<u16>,
37-
) -> Result<Self::Expander<'dst>>;
40+
) -> Result<Self::Expander<'dst>, Self::Error>;
3841
}
3942

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

5962
impl<'a, L: ArraySize> Domain<'a, L> {
60-
pub fn xof<X>(dst: &'a [&'a [u8]]) -> Result<Self>
63+
pub fn xof<X>(dst: &'a [&'a [u8]]) -> Result<Self, ExpandMsgXofError>
6164
where
6265
X: Default + ExtendableOutput + Update,
6366
{
6467
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2
6568
if dst.iter().map(|slice| slice.len()).sum::<usize>() == 0 {
66-
Err(Error)
69+
Err(ExpandMsgXofError::EmptyDst)
6770
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
6871
if L::USIZE > u8::MAX.into() {
69-
return Err(Error);
72+
return Err(ExpandMsgXofError::DstSecurityLevel);
7073
}
71-
7274
let mut data = Array::<u8, L>::default();
7375
let mut hash = X::default();
7476
hash.update(OVERSIZE_DST_SALT);
@@ -85,18 +87,17 @@ impl<'a, L: ArraySize> Domain<'a, L> {
8587
}
8688
}
8789

88-
pub fn xmd<X>(dst: &'a [&'a [u8]]) -> Result<Self>
90+
pub fn xmd<X>(dst: &'a [&'a [u8]]) -> Result<Self, ExpandMsgXmdError>
8991
where
9092
X: Digest<OutputSize = L>,
9193
{
9294
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3.1-4.2
9395
if dst.iter().map(|slice| slice.len()).sum::<usize>() == 0 {
94-
Err(Error)
96+
Err(ExpandMsgXmdError::EmptyDst)
9597
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
9698
if L::USIZE > u8::MAX.into() {
97-
return Err(Error);
99+
return Err(ExpandMsgXmdError::DstHash);
98100
}
99-
100101
Ok(Self::Hashed({
101102
let mut hash = X::new();
102103
hash.update(OVERSIZE_DST_SALT);

0 commit comments

Comments
 (0)