Skip to content
Closed
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
45 changes: 43 additions & 2 deletions elliptic-curve/Cargo.lock

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

3 changes: 2 additions & 1 deletion elliptic-curve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ zeroize = { version = "1", default-features = false }

# optional dependencies
base64ct = { version = "1", optional = true, default-features = false }
digest = { version = "0.9", optional = true, default-features = false }
digest = { version = "0.10", optional = true }
ff = { version = "0.11", optional = true, default-features = false }
group = { version = "0.11", optional = true, default-features = false }
hex-literal = { version = "0.3", optional = true }
Expand All @@ -40,6 +40,7 @@ serde_json = { version = "1", optional = true, default-features = false, feature

[dev-dependencies]
hex-literal = "0.3"
sha2 = "0.10"

[features]
default = ["arithmetic"]
Expand Down
24 changes: 18 additions & 6 deletions elliptic-curve/src/hash2curve/group_digest.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use core::ops::Mul;

use super::MapToCurve;
use crate::hash2field::{hash_to_field, ExpandMsg, FromOkm};
use generic_array::typenum::{Prod, U1, U2};
use generic_array::ArrayLength;
use group::cofactor::CofactorGroup;

/// Adds hashing arbitrary byte sequences to a valid group element
Expand Down Expand Up @@ -34,9 +38,13 @@ pub trait GroupDigest {
/// let pt = ProjectivePoint::hash_from_bytes::<hash2field::ExpandMsgXof<sha3::Shake256>>(b"test data", b"CURVE_XOF:SHAKE-256_SSWU_RO_");
/// ```
///
fn hash_from_bytes<X: ExpandMsg>(msg: &[u8], dst: &'static [u8]) -> Self::Output {
let mut u = [Self::FieldElement::default(), Self::FieldElement::default()];
hash_to_field::<X, _>(msg, dst, &mut u);
fn hash_from_bytes<X>(msg: &[u8], dst: &[u8]) -> Self::Output
where
X: ExpandMsg<Prod<<Self::FieldElement as FromOkm>::Length, U2>>,
<Self::FieldElement as FromOkm>::Length: Mul<U2>,
Prod<<Self::FieldElement as FromOkm>::Length, U2>: ArrayLength<u8>,
{
let u = hash_to_field::<X, _, U2>(msg, dst);
let q0 = Self::Output::map_to_curve(u[0]);
let q1 = Self::Output::map_to_curve(u[1]);
// Ideally we could add and then clear cofactor once
Expand All @@ -60,9 +68,13 @@ pub trait GroupDigest {
/// uniformly random in G: the set of possible outputs of
/// 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.
fn encode_from_bytes<X: ExpandMsg>(msg: &[u8], dst: &'static [u8]) -> Self::Output {
let mut u = [Self::FieldElement::default()];
hash_to_field::<X, _>(msg, dst, &mut u);
fn encode_from_bytes<X>(msg: &[u8], dst: &[u8]) -> Self::Output
where
X: ExpandMsg<Prod<<Self::FieldElement as FromOkm>::Length, U1>>,
<Self::FieldElement as FromOkm>::Length: Mul<U1>,
Prod<<Self::FieldElement as FromOkm>::Length, U1>: ArrayLength<u8>,
{
let u = hash_to_field::<X, _, U1>(msg, dst);
let q0 = Self::Output::map_to_curve(u[0]);
q0.clear_cofactor()
}
Expand Down
25 changes: 15 additions & 10 deletions elliptic-curve/src/hash2field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ mod expand_msg;
mod expand_msg_xmd;
mod expand_msg_xof;

use core::ops::Mul;

pub use expand_msg::*;
pub use expand_msg_xmd::*;
pub use expand_msg_xof::*;
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use generic_array::typenum::{Prod, Unsigned};
use generic_array::{ArrayLength, GenericArray};

/// The trait for helping to convert to a scalar
pub trait FromOkm {
Expand All @@ -18,16 +21,18 @@ pub trait FromOkm {

/// Convert an arbitrary byte sequence according to
/// <https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3>
pub fn hash_to_field<E, T>(data: &[u8], domain: &'static [u8], out: &mut [T])
pub fn hash_to_field<E, T, O: ArrayLength<T>>(data: &[u8], domain: &[u8]) -> GenericArray<T, O>
where
E: ExpandMsg,
E: ExpandMsg<Prod<T::Length, O>>,
T: FromOkm + Default,
T::Length: Mul<O>,
Prod<T::Length, O>: ArrayLength<u8>,
{
let len_in_bytes = T::Length::to_usize() * out.len();
let mut tmp = GenericArray::<u8, <T as FromOkm>::Length>::default();
let mut expander = E::expand_message(data, domain, len_in_bytes);
for o in out.iter_mut() {
expander.fill_bytes(&mut tmp);
*o = T::from_okm(&tmp);
}
let uniform_bytes = E::expand_message(data, domain);

uniform_bytes
.chunks_exact(T::Length::to_usize())
.map(GenericArray::from_slice)
.map(T::from_okm)
.collect()
}
43 changes: 23 additions & 20 deletions elliptic-curve/src/hash2field/expand_msg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use digest::{Digest, ExtendableOutputDirty, Update, XofReader};
use digest::{Digest, ExtendableOutput, Update, XofReader};
use generic_array::typenum::{IsLessOrEqual, U256};
use generic_array::{ArrayLength, GenericArray};

/// Salt when the DST is too long
Expand All @@ -7,49 +8,50 @@ const OVERSIZE_DST_SALT: &[u8] = b"H2C-OVERSIZE-DST-";
const MAX_DST_LEN: usize = 255;

/// Trait for types implementing expand_message interface for hash_to_field
pub trait ExpandMsg {
/// 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(msg: &[u8], dst: &'static [u8], len_in_bytes: usize) -> Self;

/// Fill the array with the expanded bytes
fn fill_bytes(&mut self, okm: &mut [u8]);
pub trait ExpandMsg<L: ArrayLength<u8>> {
/// Expands `msg` to the required number of bytes in `L`
fn expand_message(msg: &[u8], dst: &[u8]) -> GenericArray<u8, L>;
}

/// The domain separation tag
///
/// Implements [section 5.4.3 of `draft-irtf-cfrg-hash-to-curve-13`][dst].
///
/// [dst]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.3
pub(crate) enum Domain<L: ArrayLength<u8>> {
pub(crate) enum Domain<'a, L>
where
L: ArrayLength<u8> + IsLessOrEqual<U256>,
{
/// > 255
Hashed(GenericArray<u8, L>),
/// <= 255
Array(&'static [u8]),
Array(&'a [u8]),
}

impl<L: ArrayLength<u8>> Domain<L> {
pub fn xof<X>(dst: &'static [u8]) -> Self
impl<'a, L> Domain<'a, L>
where
L: ArrayLength<u8> + IsLessOrEqual<U256>,
{
pub fn xof<X>(dst: &'a [u8]) -> Self
where
X: Default + ExtendableOutputDirty + Update,
X: Default + ExtendableOutput + Update,
{
if dst.len() > MAX_DST_LEN {
let mut data = GenericArray::<u8, L>::default();
X::default()
.chain(OVERSIZE_DST_SALT)
.chain(dst)
.finalize_xof_dirty()
.finalize_xof()
.read(&mut data);
Self::Hashed(data)
} else {
Self::Array(dst)
}
}

pub fn xmd<X>(dst: &'static [u8]) -> Self
pub fn xmd<X>(dst: &'a [u8]) -> Self
where
X: Digest<OutputSize = L>,
X: Digest<OutputSize = L> + Update,
{
if dst.len() > MAX_DST_LEN {
Self::Hashed(X::new().chain(OVERSIZE_DST_SALT).chain(dst).finalize())
Expand All @@ -65,10 +67,11 @@ impl<L: ArrayLength<u8>> Domain<L> {
}
}

pub fn len(&self) -> usize {
pub fn len(&self) -> u8 {
match self {
Self::Hashed(_) => L::to_usize(),
Self::Array(d) => d.len(),
Self::Hashed(_) => L::to_u8(),
// Can't overflow because enforced on a type level.
Self::Array(d) => d.len() as u8,
}
}
}
Loading