diff --git a/Cargo.lock b/Cargo.lock index 8d83675a0..2ecae6446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,6 +899,7 @@ name = "primeorder" version = "0.14.0-pre.9" dependencies = [ "elliptic-curve", + "hash2curve", "serdect", ] diff --git a/ed448-goldilocks/src/decaf/points.rs b/ed448-goldilocks/src/decaf/points.rs index a090cb30a..ec8f536e2 100644 --- a/ed448-goldilocks/src/decaf/points.rs +++ b/ed448-goldilocks/src/decaf/points.rs @@ -599,8 +599,6 @@ impl From> for DecafPoint { mod test { use super::*; use crate::TWISTED_EDWARDS_BASE_POINT; - use hash2curve::ExpandMsgXof; - use sha3::Shake256; #[test] fn test_edwards_decaf_operations() { @@ -751,11 +749,7 @@ mod test { #[test] fn test_hash_to_curve() { let msg = b"Hello, world!"; - let point = hash2curve::hash_from_bytes::>( - &[msg], - &[b"test_hash_to_curve"], - ) - .unwrap(); + let point = Decaf448::hash_from_bytes(msg, b"test_hash_to_curve").unwrap(); assert_eq!(point.0.is_on_curve().unwrap_u8(), 1u8); assert_ne!(point, DecafPoint::IDENTITY); assert_ne!(point, DecafPoint::GENERATOR); diff --git a/ed448-goldilocks/src/edwards/extended.rs b/ed448-goldilocks/src/edwards/extended.rs index c376cbe47..04041c19a 100644 --- a/ed448-goldilocks/src/edwards/extended.rs +++ b/ed448-goldilocks/src/edwards/extended.rs @@ -946,9 +946,7 @@ mod tests { ]; for (msg, x, y) in MSGS { - let p = - hash2curve::hash_from_bytes::>(&[msg], &[DST]) - .unwrap(); + let p = Ed448::hash_from_bytes(msg, DST).unwrap(); assert_eq!(p.is_on_curve().unwrap_u8(), 1u8); let p = p.to_affine(); let mut xx = [0u8; 56]; @@ -985,11 +983,7 @@ mod tests { ]; for (msg, x, y) in MSGS { - let p = hash2curve::encode_from_bytes::>( - &[msg], - &[DST], - ) - .unwrap(); + let p = Ed448::encode_from_bytes(msg, DST).unwrap(); assert_eq!(p.is_on_curve().unwrap_u8(), 1u8); let p = p.to_affine(); let mut xx = [0u8; 56]; diff --git a/ed448-goldilocks/src/field.rs b/ed448-goldilocks/src/field.rs index 0f0d5e339..bb2fa36a2 100644 --- a/ed448-goldilocks/src/field.rs +++ b/ed448-goldilocks/src/field.rs @@ -2,6 +2,7 @@ mod element; mod scalar; pub(crate) use element::*; +pub use element::{Decaf448Map, Elligator2}; pub(crate) use scalar::CurveWithScalar; pub use scalar::{MODULUS_LIMBS, ORDER, Scalar, ScalarBytes, WIDE_ORDER, WideScalarBytes}; diff --git a/ed448-goldilocks/src/field/element.rs b/ed448-goldilocks/src/field/element.rs index 329694437..477a835f6 100644 --- a/ed448-goldilocks/src/field/element.rs +++ b/ed448-goldilocks/src/field/element.rs @@ -13,7 +13,7 @@ use elliptic_curve::{ array::Array, bigint::{ Integer, NonZero, U448, U704, Zero, - consts::{U28, U56, U84, U88}, + consts::{U56, U84, U88}, modular::ConstMontyParams, }, zeroize::DefaultIsZeroes, @@ -190,21 +190,21 @@ impl Neg for FieldElement { } } -impl MapToCurve for Ed448 { - type SecurityLevel = U28; - type FieldElement = FieldElement; - type Length = U84; +/// Elligator2 mapping for [`Ed448`] hash-to-curve operations. +#[derive(Clone, Copy, Debug)] +pub struct Elligator2; +impl MapToCurve for Elligator2 { fn map_to_curve(element: FieldElement) -> EdwardsPoint { element.map_to_curve_elligator2().isogeny().to_edwards() } } -impl MapToCurve for Decaf448 { - type SecurityLevel = U28; - type FieldElement = FieldElement; - type Length = U56; +/// [`Decaf448`] mapping for hash-to-curve operations. +#[derive(Clone, Copy, Debug)] +pub struct Decaf448Map; +impl MapToCurve for Decaf448Map { fn map_to_curve(element: FieldElement) -> DecafPoint { DecafPoint(element.map_to_curve_decaf448()) } diff --git a/ed448-goldilocks/src/lib.rs b/ed448-goldilocks/src/lib.rs index 6013b30b4..2ae730e54 100644 --- a/ed448-goldilocks/src/lib.rs +++ b/ed448-goldilocks/src/lib.rs @@ -47,7 +47,7 @@ pub(crate) mod montgomery; #[cfg(feature = "signing")] pub(crate) mod sign; -pub(crate) use field::{GOLDILOCKS_BASE_POINT, TWISTED_EDWARDS_BASE_POINT}; +pub(crate) use field::{FieldElement, GOLDILOCKS_BASE_POINT, TWISTED_EDWARDS_BASE_POINT}; pub use decaf::{ AffinePoint as DecafAffinePoint, CompressedDecaf, DecafPoint, DecafScalar, DecafScalarBytes, @@ -57,18 +57,18 @@ pub use edwards::{ AffinePoint, CompressedEdwardsY, EdwardsPoint, EdwardsScalar, EdwardsScalarBytes, WideEdwardsScalarBytes, }; -pub use field::{MODULUS_LIMBS, ORDER, Scalar, WIDE_ORDER}; +pub use field::{Decaf448Map, Elligator2, MODULUS_LIMBS, ORDER, Scalar, WIDE_ORDER}; pub use montgomery::{MontgomeryPoint, ProjectiveMontgomeryPoint}; #[cfg(feature = "signing")] pub use sign::*; use elliptic_curve::{ Curve, FieldBytesEncoding, PrimeCurve, - array::typenum::{U56, U57}, + array::typenum::{U28, U56, U57, U84}, bigint::{ArrayEncoding, Odd, U448}, point::PointCompression, }; -use hash2curve::{ExpandMsgXof, GroupDigest}; +use hash2curve::{ExpandMsgXof, GroupDigest, HashToCurve}; use sha3::Shake256; /// Edwards448 curve. @@ -116,11 +116,18 @@ impl elliptic_curve::CurveArithmetic for Ed448 { type Scalar = EdwardsScalar; } +impl HashToCurve for Ed448 { + type SecurityLevel = U28; + type FieldElement = FieldElement; + type Length = U84; +} + impl GroupDigest for Ed448 { const HASH_TO_CURVE_ID: &[u8] = b"edwards448_XOF:SHAKE256_ELL2_RO_"; const ENCODE_TO_CURVE_ID: &[u8] = b"edwards448_XOF:SHAKE256_ELL2_NU_"; type ExpandMsg = ExpandMsgXof; + type MapToCurve = Elligator2; } /// Decaf448 curve. @@ -168,9 +175,16 @@ impl elliptic_curve::CurveArithmetic for Decaf448 { type Scalar = DecafScalar; } +impl HashToCurve for Decaf448 { + type SecurityLevel = U28; + type FieldElement = FieldElement; + type Length = U56; +} + impl GroupDigest for Decaf448 { const HASH_TO_CURVE_ID: &[u8] = b"decaf448_XOF:SHAKE256_D448MAP_RO_"; const ENCODE_TO_CURVE_ID: &[u8] = b"decaf448_XOF:SHAKE256_D448MAP_NU_"; type ExpandMsg = ExpandMsgXof; + type MapToCurve = Decaf448Map; } diff --git a/hash2curve/src/group_digest.rs b/hash2curve/src/group_digest.rs index fbe4a595f..a6c5b1da3 100644 --- a/hash2curve/src/group_digest.rs +++ b/hash2curve/src/group_digest.rs @@ -1,14 +1,10 @@ //! Traits for handling hash to curve. -use super::{ExpandMsg, MapToCurve, hash_to_field}; +use crate::{ExpandMsg, HashToCurve, MapToCurve}; use elliptic_curve::ProjectivePoint; -use elliptic_curve::array::typenum::NonZero; -use elliptic_curve::array::{Array, ArraySize}; -use elliptic_curve::group::cofactor::CofactorGroup; -use elliptic_curve::ops::Reduce; /// Hash arbitrary byte sequences to a valid group element. -pub trait GroupDigest: MapToCurve { +pub trait GroupDigest: HashToCurve { /// Suite ID for the [hash to curve routine](Self::hash_from_bytes). const HASH_TO_CURVE_ID: &[u8]; /// Suite ID for the [encode to curve routine](Self::encode_from_bytes). @@ -16,6 +12,8 @@ pub trait GroupDigest: MapToCurve { /// The `expand_message` function to use. type ExpandMsg: ExpandMsg; + /// The mapping function. + type MapToCurve: MapToCurve; /// Computes the hash to curve routine. /// @@ -40,7 +38,7 @@ pub trait GroupDigest: MapToCurve { dst: &[u8], ) -> Result, >::Error> { - hash_from_bytes::(&[msg], &[dst]) + crate::hash_from_bytes::(&[msg], &[dst]) } /// Computes the encode to curve routine. @@ -65,77 +63,6 @@ pub trait GroupDigest: MapToCurve { dst: &[u8], ) -> Result, >::Error> { - encode_from_bytes::(&[msg], &[dst]) + crate::encode_from_bytes::(&[msg], &[dst]) } } - -/// Computes the hash to curve routine. -/// See [`GroupDigest::hash_from_bytes()`] for more details. -/// -/// For the `expand_message` call, `len_in_bytes = ::Length * 2`. -/// This value must be less than `u16::MAX` or otherwise a compiler error will occur. -/// -/// # Errors -/// -/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] -/// and [`ExpandMsgXofError`] for examples. -/// -/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError -/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError -pub fn hash_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result, X::Error> -where - C: MapToCurve, - X: ExpandMsg, -{ - let [u0, u1] = hash_to_field::<2, X, _, C::FieldElement, C::Length>(msg, dst)?; - let q0 = C::map_to_curve(u0); - let q1 = C::map_to_curve(u1); - Ok((q0 + q1).clear_cofactor()) -} - -/// Computes the encode to curve routine. -/// See [`GroupDigest::encode_from_bytes()`] for more details. -/// -/// For the `expand_message` call, `len_in_bytes = ::Length`. -/// -/// # Errors -/// -/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] -/// and [`ExpandMsgXofError`] for examples. -/// -/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError -/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError -pub fn encode_from_bytes(msg: &[&[u8]], dst: &[&[u8]]) -> Result, X::Error> -where - C: MapToCurve, - X: ExpandMsg, -{ - let [u] = hash_to_field::<1, X, _, C::FieldElement, C::Length>(msg, dst)?; - let q0 = C::map_to_curve(u); - Ok(q0.clear_cofactor()) -} - -/// Computes the hash to field routine according to -/// -/// and returns a scalar. -/// -/// For the `expand_message` call, `len_in_bytes = ::Length`. -/// This value must be less than `u16::MAX` or otherwise a compiler error will occur. -/// -/// # Errors -/// -/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] -/// and [`ExpandMsgXofError`] for examples. -/// -/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError -/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError -pub fn hash_to_scalar(msg: &[&[u8]], dst: &[&[u8]]) -> Result -where - C: MapToCurve, - X: ExpandMsg, - L: ArraySize + NonZero, - C::Scalar: Reduce>, -{ - let [u] = hash_to_field::<1, X, _, C::Scalar, L>(msg, dst)?; - Ok(u) -} diff --git a/hash2curve/src/lib.rs b/hash2curve/src/lib.rs index b11369db0..d071a2fae 100644 --- a/hash2curve/src/lib.rs +++ b/hash2curve/src/lib.rs @@ -26,8 +26,95 @@ mod group_digest; mod hash2field; mod map2curve; mod oprf; +mod parameters; pub use group_digest::*; pub use hash2field::*; pub use map2curve::*; pub use oprf::*; +pub use parameters::*; + +use elliptic_curve::ProjectivePoint; +use elliptic_curve::array::typenum::NonZero; +use elliptic_curve::array::{Array, ArraySize}; +use elliptic_curve::group::cofactor::CofactorGroup; +use elliptic_curve::ops::Reduce; + +/// Computes the hash to curve routine. +/// See [`GroupDigest::hash_from_bytes()`] for more details. +/// +/// For the `expand_message` call, `len_in_bytes = ::Length * 2`. +/// This value must be less than `u16::MAX` or otherwise a compiler error will occur. +/// +/// # Errors +/// +/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] +/// and [`ExpandMsgXofError`] for examples. +/// +/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError +/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError +pub fn hash_from_bytes( + msg: &[&[u8]], + dst: &[&[u8]], +) -> Result, X::Error> +where + C: HashToCurve, + X: ExpandMsg, + M: MapToCurve, +{ + let [u0, u1] = hash_to_field::<2, X, _, C::FieldElement, C::Length>(msg, dst)?; + let q0 = M::map_to_curve(u0); + let q1 = M::map_to_curve(u1); + Ok((q0 + q1).clear_cofactor()) +} + +/// Computes the encode to curve routine. +/// See [`GroupDigest::encode_from_bytes()`] for more details. +/// +/// For the `expand_message` call, `len_in_bytes = ::Length`. +/// +/// # Errors +/// +/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] +/// and [`ExpandMsgXofError`] for examples. +/// +/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError +/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError +pub fn encode_from_bytes( + msg: &[&[u8]], + dst: &[&[u8]], +) -> Result, X::Error> +where + C: HashToCurve, + X: ExpandMsg, + M: MapToCurve, +{ + let [u] = hash_to_field::<1, X, _, C::FieldElement, C::Length>(msg, dst)?; + let q0 = M::map_to_curve(u); + Ok(q0.clear_cofactor()) +} + +/// Computes the hash to field routine according to +/// +/// and returns a scalar. +/// +/// For the `expand_message` call, `len_in_bytes = ::Length`. +/// This value must be less than `u16::MAX` or otherwise a compiler error will occur. +/// +/// # Errors +/// +/// When the chosen [`ExpandMsg`] implementation returns an error. See [`ExpandMsgXmdError`] +/// and [`ExpandMsgXofError`] for examples. +/// +/// [`ExpandMsgXmdError`]: crate::ExpandMsgXmdError +/// [`ExpandMsgXofError`]: crate::ExpandMsgXofError +pub fn hash_to_scalar(msg: &[&[u8]], dst: &[&[u8]]) -> Result +where + C: HashToCurve, + X: ExpandMsg, + L: ArraySize + NonZero, + C::Scalar: Reduce>, +{ + let [u] = hash_to_field::<1, X, _, C::Scalar, L>(msg, dst)?; + Ok(u) +} diff --git a/hash2curve/src/map2curve.rs b/hash2curve/src/map2curve.rs index fd94856f5..0da6e8ee9 100644 --- a/hash2curve/src/map2curve.rs +++ b/hash2curve/src/map2curve.rs @@ -1,25 +1,12 @@ //! Traits for mapping field elements to points on the curve. -use elliptic_curve::array::typenum::{NonZero, Unsigned}; -use elliptic_curve::array::{Array, ArraySize}; -use elliptic_curve::group::cofactor::CofactorGroup; -use elliptic_curve::ops::Reduce; -use elliptic_curve::{CurveArithmetic, ProjectivePoint}; +use elliptic_curve::ProjectivePoint; + +use crate::HashToCurve; /// 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> -{ - /// The target security level in bytes: - /// - /// - type SecurityLevel: Unsigned; - /// The field element representation for a group value with multiple elements. - type FieldElement: Reduce> + Default + Copy; - /// The `L` parameter as specified in the [RFC](https://www.rfc-editor.org/rfc/rfc9380.html#section-5-6). - type Length: ArraySize + NonZero; - +pub trait MapToCurve { /// Map a field element into a curve point. - fn map_to_curve(element: Self::FieldElement) -> ProjectivePoint; + fn map_to_curve(element: C::FieldElement) -> ProjectivePoint; } diff --git a/hash2curve/src/parameters.rs b/hash2curve/src/parameters.rs new file mode 100644 index 000000000..fc967af42 --- /dev/null +++ b/hash2curve/src/parameters.rs @@ -0,0 +1,19 @@ +use elliptic_curve::CurveArithmetic; +use elliptic_curve::array::typenum::{NonZero, Unsigned}; +use elliptic_curve::array::{Array, ArraySize}; +use elliptic_curve::group::cofactor::CofactorGroup; +use elliptic_curve::ops::Reduce; + +/// Trait defining curve parameters for hash-to-curve operations. +pub trait HashToCurve: + CurveArithmetic> +{ + /// The target security level in bytes: + /// + /// + type SecurityLevel: Unsigned; + /// The field element representation for a group value with multiple elements. + type FieldElement: Reduce> + Default + Copy; + /// The `L` parameter as specified in the [RFC](https://www.rfc-editor.org/rfc/rfc9380.html#section-5-6). + type Length: ArraySize + NonZero; +} diff --git a/k256/src/arithmetic/hash2curve.rs b/k256/src/arithmetic/hash2curve.rs index fcb77b729..9794026fd 100644 --- a/k256/src/arithmetic/hash2curve.rs +++ b/k256/src/arithmetic/hash2curve.rs @@ -4,19 +4,27 @@ use elliptic_curve::bigint::{ArrayEncoding, U256}; use elliptic_curve::consts::{U4, U16, U48}; use elliptic_curve::ops::Reduce; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use hash2curve::MapToCurve; +use hash2curve::{HashToCurve, MapToCurve}; +use primeorder::osswu::ShallueVanDeWoestijne; use primeorder::osswu::{OsswuMap, OsswuMapParams, Sgn0}; use crate::{AffinePoint, ProjectivePoint, Scalar, Secp256k1}; use super::FieldElement; +impl HashToCurve for Secp256k1 { + type SecurityLevel = U16; + type FieldElement = FieldElement; + type Length = U48; +} + #[cfg(feature = "group-digest")] impl hash2curve::GroupDigest for Secp256k1 { const HASH_TO_CURVE_ID: &[u8] = b"secp256k1_XMD:SHA-256_SSWU_RO_"; const ENCODE_TO_CURVE_ID: &[u8] = b"secp256k1_XMD:SHA-256_SSWU_NU_"; type ExpandMsg = hash2curve::ExpandMsgXmd; + type MapToCurve = ShallueVanDeWoestijne; } impl Reduce> for FieldElement { @@ -130,11 +138,7 @@ impl OsswuMap for FieldElement { } } -impl MapToCurve for Secp256k1 { - type SecurityLevel = U16; - type FieldElement = FieldElement; - type Length = U48; - +impl MapToCurve for ShallueVanDeWoestijne { fn map_to_curve(element: FieldElement) -> ProjectivePoint { let (rx, ry) = element.osswu(); let (qx, qy) = isogeny(rx, ry); @@ -287,8 +291,9 @@ mod tests { group::cofactor::CofactorGroup, ops::Reduce, }; - use hash2curve::MapToCurve; + use hash2curve::{HashToCurve, MapToCurve}; use hex_literal::hex; + use primeorder::osswu::ShallueVanDeWoestijne; use proptest::{num::u64::ANY, prelude::ProptestConfig, proptest}; #[test] @@ -374,20 +379,20 @@ mod tests { let u = hash2curve::hash_to_field::< 2, ExpandMsgXmd, - ::SecurityLevel, + ::SecurityLevel, FieldElement, - ::Length, + ::Length, >(&[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); - let q0 = Secp256k1::map_to_curve(u[0]); + let q0 = >::map_to_curve(u[0]); let aq0 = q0.to_affine(); assert_eq!(aq0.x.to_bytes().as_slice(), test_vector.q0_x); assert_eq!(aq0.y.to_bytes().as_slice(), test_vector.q0_y); - let q1 = Secp256k1::map_to_curve(u[1]); + let q1 = >::map_to_curve(u[1]); let aq1 = q1.to_affine(); assert_eq!(aq1.x.to_bytes().as_slice(), test_vector.q1_x); assert_eq!(aq1.y.to_bytes().as_slice(), test_vector.q1_y); @@ -398,10 +403,11 @@ mod tests { assert_eq!(ap.y.to_bytes().as_slice(), test_vector.p_y); // complete run - let pt = hash2curve::hash_from_bytes::>( - &[test_vector.msg], - &[DST], - ) + let pt = hash2curve::hash_from_bytes::< + Secp256k1, + ExpandMsgXmd, + ShallueVanDeWoestijne, + >(&[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/k256/src/lib.rs b/k256/src/lib.rs index 7d7efac09..51d2f7923 100644 --- a/k256/src/lib.rs +++ b/k256/src/lib.rs @@ -64,6 +64,9 @@ pub use elliptic_curve::pkcs8; #[cfg(feature = "sha2")] pub use sha2; +#[cfg(feature = "hash2curve")] +pub use primeorder::osswu::ShallueVanDeWoestijne; + use elliptic_curve::{ FieldBytesEncoding, array::Array, diff --git a/p256/src/arithmetic/hash2curve.rs b/p256/src/arithmetic/hash2curve.rs index 9edd8a9df..a43c28a20 100644 --- a/p256/src/arithmetic/hash2curve.rs +++ b/p256/src/arithmetic/hash2curve.rs @@ -1,5 +1,5 @@ use super::FieldElement; -use crate::{AffinePoint, FieldBytes, NistP256, ProjectivePoint, Scalar}; +use crate::{FieldBytes, NistP256, Scalar}; use elliptic_curve::{ array::Array, bigint::{ArrayEncoding, U256}, @@ -7,8 +7,14 @@ use elliptic_curve::{ ops::Reduce, subtle::Choice, }; -use hash2curve::MapToCurve; -use primeorder::osswu::{AffineOsswuMap, OsswuMap, OsswuMapParams, Sgn0}; +use hash2curve::HashToCurve; +use primeorder::osswu::{OsswuMap, OsswuMapParams, Sgn0}; + +impl HashToCurve for NistP256 { + type SecurityLevel = U16; + type FieldElement = FieldElement; + type Length = U48; +} #[cfg(feature = "group-digest")] impl hash2curve::GroupDigest for NistP256 { @@ -16,6 +22,7 @@ impl hash2curve::GroupDigest for NistP256 { const ENCODE_TO_CURVE_ID: &[u8] = b"P256_XMD:SHA-256_SSWU_NU_"; type ExpandMsg = hash2curve::ExpandMsgXmd; + type MapToCurve = primeorder::osswu::ShallueVanDeWoestijne; } impl Reduce> for FieldElement { @@ -61,16 +68,6 @@ impl OsswuMap for FieldElement { }; } -impl MapToCurve for NistP256 { - type SecurityLevel = U16; - type FieldElement = FieldElement; - type Length = U48; - - fn map_to_curve(element: Self::FieldElement) -> ProjectivePoint { - AffinePoint::osswu(&element).into() - } -} - impl Reduce> for Scalar { fn reduce(value: &Array) -> Self { const F_2_192: Scalar = Scalar(U256::from_be_hex( @@ -100,10 +97,10 @@ mod tests { consts::U48, sec1::{self, ToEncodedPoint}, }; - use hash2curve::{self, ExpandMsgXmd, MapToCurve}; + use hash2curve::{self, ExpandMsgXmd, HashToCurve, MapToCurve}; use hex_literal::hex; use primefield::bigint::Reduce; - use primeorder::osswu::OsswuMap; + use primeorder::osswu::{OsswuMap, ShallueVanDeWoestijne}; use proptest::{num::u64::ANY, prelude::ProptestConfig, proptest}; use sha2::Sha256; @@ -206,9 +203,9 @@ mod tests { let u = hash2curve::hash_to_field::< 2, ExpandMsgXmd, - ::SecurityLevel, + ::SecurityLevel, FieldElement, - ::Length, + ::Length, >(&[test_vector.msg], &[DST]) .unwrap(); @@ -230,20 +227,21 @@ mod tests { assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0); assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1); - let q0 = NistP256::map_to_curve(u[0]); + let q0 = >::map_to_curve(u[0]); assert_point_eq!(q0, test_vector.q0_x, test_vector.q0_y); - let q1 = NistP256::map_to_curve(u[1]); + let q1 = >::map_to_curve(u[1]); assert_point_eq!(q1, test_vector.q1_x, test_vector.q1_y); let p = q0 + q1; assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = hash2curve::hash_from_bytes::>( - &[test_vector.msg], - &[DST], - ) + let pt = hash2curve::hash_from_bytes::< + NistP256, + ExpandMsgXmd, + ShallueVanDeWoestijne, + >(&[test_vector.msg], &[DST]) .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } diff --git a/p256/src/lib.rs b/p256/src/lib.rs index a3f921be4..7b046cfde 100644 --- a/p256/src/lib.rs +++ b/p256/src/lib.rs @@ -50,6 +50,9 @@ pub use arithmetic::field::FieldElement; #[cfg(feature = "pkcs8")] pub use elliptic_curve::pkcs8; +#[cfg(feature = "hash2curve")] +pub use primeorder::osswu::ShallueVanDeWoestijne; + use elliptic_curve::{ FieldBytesEncoding, array::Array, diff --git a/p384/src/arithmetic/hash2curve.rs b/p384/src/arithmetic/hash2curve.rs index e46c28c2c..584a7f546 100644 --- a/p384/src/arithmetic/hash2curve.rs +++ b/p384/src/arithmetic/hash2curve.rs @@ -1,5 +1,5 @@ use super::FieldElement; -use crate::{AffinePoint, FieldBytes, NistP384, ProjectivePoint, Scalar}; +use crate::{FieldBytes, NistP384, Scalar}; use elliptic_curve::{ array::Array, bigint::{ArrayEncoding, U384}, @@ -7,8 +7,14 @@ use elliptic_curve::{ ops::Reduce, subtle::Choice, }; -use hash2curve::MapToCurve; -use primeorder::osswu::{AffineOsswuMap, OsswuMap, OsswuMapParams, Sgn0}; +use hash2curve::HashToCurve; +use primeorder::osswu::{OsswuMap, OsswuMapParams, Sgn0}; + +impl HashToCurve for NistP384 { + type SecurityLevel = U24; + type FieldElement = FieldElement; + type Length = U72; +} #[cfg(feature = "group-digest")] impl hash2curve::GroupDigest for NistP384 { @@ -16,6 +22,7 @@ impl hash2curve::GroupDigest for NistP384 { const ENCODE_TO_CURVE_ID: &[u8] = b"P384_XMD:SHA-384_SSWU_NU_"; type ExpandMsg = hash2curve::ExpandMsgXmd; + type MapToCurve = primeorder::osswu::ShallueVanDeWoestijne; } impl Reduce> for FieldElement { @@ -63,16 +70,6 @@ impl OsswuMap for FieldElement { }; } -impl MapToCurve for NistP384 { - type SecurityLevel = U24; - type FieldElement = FieldElement; - type Length = U72; - - fn map_to_curve(element: FieldElement) -> ProjectivePoint { - AffinePoint::osswu(&element).into() - } -} - impl Reduce> for Scalar { fn reduce(value: &Array) -> Self { const F_2_288: Scalar = Scalar::from_hex_vartime( @@ -104,9 +101,9 @@ mod tests { ops::Reduce, sec1::{self, ToEncodedPoint}, }; - use hash2curve::{self, ExpandMsgXmd, MapToCurve}; + use hash2curve::{self, ExpandMsgXmd, HashToCurve, MapToCurve}; use hex_literal::hex; - use primeorder::osswu::OsswuMap; + use primeorder::osswu::{OsswuMap, ShallueVanDeWoestijne}; use proptest::{num::u64::ANY, prelude::ProptestConfig, proptest}; use sha2::Sha384; @@ -207,9 +204,9 @@ mod tests { let u = hash2curve::hash_to_field::< 2, ExpandMsgXmd, - ::SecurityLevel, + ::SecurityLevel, FieldElement, - ::Length, + ::Length, >(&[test_vector.msg], &[DST]) .unwrap(); @@ -231,20 +228,21 @@ mod tests { assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0); assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1); - let q0 = NistP384::map_to_curve(u[0]); + let q0 = >::map_to_curve(u[0]); assert_point_eq!(q0, test_vector.q0_x, test_vector.q0_y); - let q1 = NistP384::map_to_curve(u[1]); + let q1 = >::map_to_curve(u[1]); assert_point_eq!(q1, test_vector.q1_x, test_vector.q1_y); let p = (q0 + q1).clear_cofactor(); assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = hash2curve::hash_from_bytes::>( - &[test_vector.msg], - &[DST], - ) + let pt = hash2curve::hash_from_bytes::< + NistP384, + ExpandMsgXmd, + ShallueVanDeWoestijne, + >(&[test_vector.msg], &[DST]) .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } diff --git a/p384/src/lib.rs b/p384/src/lib.rs index 8d3cb2369..1280ed097 100644 --- a/p384/src/lib.rs +++ b/p384/src/lib.rs @@ -47,6 +47,9 @@ pub use arithmetic::field::FieldElement; #[cfg(feature = "pkcs8")] pub use elliptic_curve::pkcs8; +#[cfg(feature = "hash2curve")] +pub use primeorder::osswu::ShallueVanDeWoestijne; + use elliptic_curve::{FieldBytesEncoding, array::Array, bigint::ArrayEncoding, consts::U49}; /// Order of NIST P-384's elliptic curve group (i.e. scalar modulus) in hexadecimal. diff --git a/p521/src/arithmetic/hash2curve.rs b/p521/src/arithmetic/hash2curve.rs index 463ef551b..293f7d65e 100644 --- a/p521/src/arithmetic/hash2curve.rs +++ b/p521/src/arithmetic/hash2curve.rs @@ -1,5 +1,5 @@ use super::FieldElement; -use crate::{AffinePoint, NistP521, ProjectivePoint, Scalar, Uint}; +use crate::{NistP521, Scalar, Uint}; use elliptic_curve::{ FieldBytesEncoding, array::Array, @@ -7,8 +7,14 @@ use elliptic_curve::{ ops::Reduce, subtle::Choice, }; -use hash2curve::MapToCurve; -use primeorder::osswu::{AffineOsswuMap, OsswuMap, OsswuMapParams, Sgn0}; +use hash2curve::HashToCurve; +use primeorder::osswu::{OsswuMap, OsswuMapParams, Sgn0}; + +impl HashToCurve for NistP521 { + type SecurityLevel = U32; + type FieldElement = FieldElement; + type Length = U98; +} #[cfg(feature = "group-digest")] impl hash2curve::GroupDigest for NistP521 { @@ -16,6 +22,7 @@ impl hash2curve::GroupDigest for NistP521 { const ENCODE_TO_CURVE_ID: &[u8] = b"P521_XMD:SHA-512_SSWU_NU_"; type ExpandMsg = hash2curve::ExpandMsgXmd; + type MapToCurve = primeorder::osswu::ShallueVanDeWoestijne; } impl Reduce> for FieldElement { @@ -66,16 +73,6 @@ impl OsswuMap for FieldElement { }; } -impl MapToCurve for NistP521 { - type SecurityLevel = U32; - type FieldElement = FieldElement; - type Length = U98; - - fn map_to_curve(element: FieldElement) -> ProjectivePoint { - AffinePoint::osswu(&element).into() - } -} - impl Reduce> for Scalar { fn reduce(value: &Array) -> Self { // TODO(tarcieri): make `Scalar::from_hex*` accept a `FieldBytes`-length hex input @@ -117,9 +114,9 @@ mod tests { ops::Reduce, sec1::{self, ToEncodedPoint}, }; - use hash2curve::{self, ExpandMsgXmd, MapToCurve}; + use hash2curve::{self, ExpandMsgXmd, HashToCurve, MapToCurve}; use hex_literal::hex; - use primeorder::osswu::OsswuMap; + use primeorder::osswu::{OsswuMap, ShallueVanDeWoestijne}; use proptest::{num, prelude::ProptestConfig, proptest}; use sha2::Sha512; @@ -223,9 +220,9 @@ mod tests { let u = hash2curve::hash_to_field::< 2, ExpandMsgXmd, - ::SecurityLevel, + ::SecurityLevel, FieldElement, - ::Length, + ::Length, >(&[test_vector.msg], &[DST]) .unwrap(); @@ -247,20 +244,21 @@ mod tests { assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0); assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1); - let q0 = NistP521::map_to_curve(u[0]); + let q0 = >::map_to_curve(u[0]); assert_point_eq!(q0, test_vector.q0_x, test_vector.q0_y); - let q1 = NistP521::map_to_curve(u[1]); + let q1 = >::map_to_curve(u[1]); assert_point_eq!(q1, test_vector.q1_x, test_vector.q1_y); let p = (q0 + q1).clear_cofactor(); assert_point_eq!(p, test_vector.p_x, test_vector.p_y); // complete run - let pt = hash2curve::hash_from_bytes::>( - &[test_vector.msg], - &[DST], - ) + let pt = hash2curve::hash_from_bytes::< + NistP521, + ExpandMsgXmd, + ShallueVanDeWoestijne, + >(&[test_vector.msg], &[DST]) .unwrap(); assert_point_eq!(pt, test_vector.p_x, test_vector.p_y); } diff --git a/p521/src/lib.rs b/p521/src/lib.rs index ecc962116..07fd3cb44 100644 --- a/p521/src/lib.rs +++ b/p521/src/lib.rs @@ -49,6 +49,9 @@ pub use elliptic_curve; #[cfg(feature = "pkcs8")] pub use elliptic_curve::pkcs8; +#[cfg(feature = "hash2curve")] +pub use primeorder::osswu::ShallueVanDeWoestijne; + use elliptic_curve::{FieldBytesEncoding, array::Array, bigint::Odd, consts::U66}; #[cfg(target_pointer_width = "32")] diff --git a/primeorder/Cargo.toml b/primeorder/Cargo.toml index 827fb006e..4d308848b 100644 --- a/primeorder/Cargo.toml +++ b/primeorder/Cargo.toml @@ -19,13 +19,14 @@ rust-version = "1.85" [dependencies] elliptic-curve = { version = "0.14.0-rc.14", default-features = false, features = ["arithmetic", "sec1"] } +hash2curve = { version = "0.14.0-rc.2", optional = true } # optional dependencies serdect = { version = "0.4", optional = true, default-features = false } [features] alloc = ["elliptic-curve/alloc"] -hash2curve = [] +hash2curve = ["dep:hash2curve"] std = ["alloc", "elliptic-curve/std"] dev = [] diff --git a/primeorder/src/osswu.rs b/primeorder/src/osswu.rs index 3c7bd4bc0..310bd7db7 100644 --- a/primeorder/src/osswu.rs +++ b/primeorder/src/osswu.rs @@ -3,21 +3,25 @@ //! use elliptic_curve::Field; +use elliptic_curve::group::cofactor::CofactorGroup; use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use hash2curve::{HashToCurve, MapToCurve}; -use crate::{AffinePoint, PrimeCurveParams}; +use crate::{AffinePoint, PrimeCurveParams, ProjectivePoint}; -/// [`OsswuMap`] for [`AffinePoint`]. -pub trait AffineOsswuMap> { - /// [`OsswuMap::osswu()`] to [`AffinePoint`]. - fn osswu(u: &C::FieldElement) -> Self; -} +/// Shallue-van de Woestijne-Ulas mapping for Weierstrass curves. +pub struct ShallueVanDeWoestijne; -impl> AffineOsswuMap for AffinePoint { - fn osswu(u: &::FieldElement) -> Self { - let (x, y) = u.osswu(); +impl MapToCurve for ShallueVanDeWoestijne +where + C: HashToCurve + + PrimeCurveParams::FieldElement>, + ProjectivePoint: CofactorGroup>, +{ + fn map_to_curve(element: ::FieldElement) -> ProjectivePoint { + let (x, y) = element.osswu(); - Self { x, y, infinity: 0 } + AffinePoint { x, y, infinity: 0 }.into() } }