Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/elliptic-curve.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions elliptic-curve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
2 changes: 1 addition & 1 deletion elliptic-curve/src/hash2curve.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Traits for hashing byte sequences to curve points.
//!
//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve>
//! <https://www.rfc-editor.org/rfc/rfc9380.html>

mod group_digest;
mod hash2field;
Expand Down
51 changes: 17 additions & 34 deletions elliptic-curve/src/hash2curve/group_digest.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,7 +13,7 @@ pub trait GroupDigest: MapToCurve {

/// Computes the hash to curve routine.
///
/// From <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html>:
/// From <https://www.rfc-editor.org/rfc/rfc9380.html>:
///
/// > Uniform encoding from byte strings to points in G.
/// > That is, the distribution of its output is statistically close
Expand All @@ -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::<ExpandMsgXmd<sha2::Sha256>>(b"test data", b"CURVE_XMD:SHA-256_SSWU_RO_");
/// ```
///
/// ## Using an extendable output function
///
/// ```ignore
/// let pt = ProjectivePoint::hash_from_bytes::<ExpandMsgXof<sha3::Shake256>>(b"test data", b"CURVE_XOF:SHAKE-256_SSWU_RO_");
/// ```
///
/// # Errors
/// See implementors of [`ExpandMsg`] for errors:
/// - [`ExpandMsgXmd`]
Expand All @@ -45,20 +31,20 @@ 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<ProjectivePoint<Self>> {
fn hash_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
where
X: ExpandMsg<Self::K>,
{
let mut u = [Self::FieldElement::default(), Self::FieldElement::default()];
hash_to_field::<X, _>(msgs, dsts, &mut u)?;
hash_to_field::<X, _, _>(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))
}

/// Computes the encode to curve routine.
///
/// From <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html>:
/// From <https://www.rfc-editor.org/rfc/rfc9380.html>:
///
/// > Nonuniform encoding from byte strings to
/// > points in G. That is, the distribution of its output is not
Expand All @@ -75,18 +61,18 @@ pub trait GroupDigest: MapToCurve {
///
/// [`ExpandMsgXmd`]: crate::hash2curve::ExpandMsgXmd
/// [`ExpandMsgXof`]: crate::hash2curve::ExpandMsgXof
fn encode_from_bytes<'a, X: ExpandMsg<'a>>(
msgs: &[&[u8]],
dsts: &'a [&'a [u8]],
) -> Result<ProjectivePoint<Self>> {
fn encode_from_bytes<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<ProjectivePoint<Self>>
where
X: ExpandMsg<Self::K>,
{
let mut u = [Self::FieldElement::default()];
hash_to_field::<X, _>(msgs, dsts, &mut u)?;
hash_to_field::<X, _, _>(msg, dst, &mut u)?;
let q0 = Self::map_to_curve(u[0]);
Ok(Self::map_to_subgroup(q0))
}

/// Computes the hash to field routine according to
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5>
/// <https://www.rfc-editor.org/rfc/rfc9380.html#section-5-4>
/// and returns a scalar.
///
/// # Errors
Expand All @@ -98,15 +84,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<Self::Scalar>
fn hash_to_scalar<X>(msg: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar>
where
Self::Scalar: FromOkm,
X: ExpandMsg<Self::K>,
{
let mut u = [Self::Scalar::default()];
hash_to_field::<X, _>(msgs, dsts, &mut u)?;
hash_to_field::<X, _, _>(msg, dst, &mut u)?;
Ok(u[0])
}
}
8 changes: 4 additions & 4 deletions elliptic-curve/src/hash2curve/hash2field.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Traits for hashing to field elements.
//!
//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve>
//! <https://www.rfc-editor.org/rfc/rfc9380.html>

mod expand_msg;

Expand All @@ -25,7 +25,7 @@ pub trait FromOkm {

/// Convert an arbitrary byte sequence into a field element.
///
/// <https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3>
/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-hash_to_field-implementatio>
///
/// # Errors
/// See implementors of [`ExpandMsg`] for errors:
Expand All @@ -37,9 +37,9 @@ 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<E, K, T>(data: &[&[u8]], domain: &[&[u8]], out: &mut [T]) -> Result<()>
where
E: ExpandMsg<'a>,
E: ExpandMsg<K>,
T: FromOkm + Default,
{
let len_in_bytes = T::Length::to_usize()
Expand Down
46 changes: 26 additions & 20 deletions elliptic-curve/src/hash2curve/hash2field/expand_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,25 @@ 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:
/// <https://www.rfc-editor.org/rfc/rfc9380.html#section-8.9-2.2>
/// <https://www.rfc-editor.org/rfc/rfc9380.html#name-target-security-levels>
///
/// # Errors
/// See implementors of [`ExpandMsg`] for errors.
pub trait ExpandMsg<'a> {
pub trait ExpandMsg<K> {
/// 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(
msgs: &[&[u8]],
dsts: &'a [&'a [u8]],
fn expand_message<'dst>(
msg: &[&[u8]],
dst: &'dst [&[u8]],
len_in_bytes: NonZero<usize>,
) -> Result<Self::Expander>;
) -> Result<Self::Expander<'dst>>;
}

/// Expander that, call `read` until enough bytes have been consumed.
Expand All @@ -42,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
Expand All @@ -60,48 +64,50 @@ impl<'a, L> Domain<'a, L>
where
L: ArraySize + IsLess<U256, Output = True>,
{
pub fn xof<X>(dsts: &'a [&'a [u8]]) -> Result<Self>
pub fn xof<X>(dst: &'a [&'a [u8]]) -> Result<Self>
where
X: Default + ExtendableOutput + Update,
{
if dsts.is_empty() {
// 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)
} else if dsts.iter().map(|dst| dst.len()).sum::<usize>() > MAX_DST_LEN {
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
let mut data = Array::<u8, L>::default();
let mut hash = X::default();
hash.update(OVERSIZE_DST_SALT);

for dst in dsts {
hash.update(dst);
for slice in dst {
hash.update(slice);
}

hash.finalize_xof().read(&mut data);

Ok(Self::Hashed(data))
} else {
Ok(Self::Array(dsts))
Ok(Self::Array(dst))
}
}

pub fn xmd<X>(dsts: &'a [&'a [u8]]) -> Result<Self>
pub fn xmd<X>(dst: &'a [&'a [u8]]) -> Result<Self>
where
X: Digest<OutputSize = L>,
{
if dsts.is_empty() {
// 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)
} else if dsts.iter().map(|dst| dst.len()).sum::<usize>() > MAX_DST_LEN {
} else if dst.iter().map(|slice| slice.len()).sum::<usize>() > MAX_DST_LEN {
Ok(Self::Hashed({
let mut hash = X::new();
hash.update(OVERSIZE_DST_SALT);

for dst in dsts {
hash.update(dst);
for slice in dst {
hash.update(slice);
}

hash.finalize()
}))
} else {
Ok(Self::Array(dsts))
Ok(Self::Array(dst))
}
}

Expand Down
Loading