Skip to content

Commit

Permalink
feat: add hash to field
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Lodder <redmike7@gmail.com>
  • Loading branch information
mikelodder7 committed Dec 22, 2021
1 parent dcbce6a commit 72e11e3
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 16 deletions.
42 changes: 26 additions & 16 deletions elliptic-curve/Cargo.lock

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

2 changes: 2 additions & 0 deletions elliptic-curve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +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 }
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 @@ -47,6 +48,7 @@ arithmetic = ["ff", "group"]
bits = ["arithmetic", "ff/bits"]
dev = ["arithmetic", "hex-literal", "pem", "pkcs8"]
ecdh = ["arithmetic"]
hashing = ["digest"]
hazmat = []
jwk = ["alloc", "base64ct/alloc", "serde", "serde_json", "zeroize/alloc"]
osswu = ["ff"]
Expand Down
33 changes: 33 additions & 0 deletions elliptic-curve/src/hash2field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
mod expand_msg;
mod expand_msg_xmd;
mod expand_msg_xof;

use core::convert::TryFrom;
pub use expand_msg::*;
pub use expand_msg_xmd::*;
pub use expand_msg_xof::*;

/// The trait for helping to convert to a scalar
pub trait FromOkm<const L: usize>: Sized {
/// Convert a byte sequence into a scalar
fn from_okm(data: &[u8; L]) -> Self;
}

/// 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, const L: usize, const COUNT: usize, const OUT: usize>(
data: &[u8],
domain: &[u8],
) -> [T; COUNT]
where
E: ExpandMsg<OUT>,
T: FromOkm<L> + Default + Copy,
{
let random_bytes = E::expand_message(data, domain);
let mut out = [T::default(); COUNT];
for i in 0..COUNT {
let u = <[u8; L]>::try_from(&random_bytes[(L * i)..L * (i + 1)]).expect("not enough bytes");
out[i] = T::from_okm(&u);
}
out
}
5 changes: 5 additions & 0 deletions elliptic-curve/src/hash2field/expand_msg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// Trait for types implementing expand_message interface for hash_to_field
pub trait ExpandMsg<const OUT: usize> {
/// Expands `msg` to the required number of bytes in `buf`
fn expand_message(msg: &[u8], dst: &[u8]) -> [u8; OUT];
}
72 changes: 72 additions & 0 deletions elliptic-curve/src/hash2field/expand_msg_xmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use super::ExpandMsg;
use core::marker::PhantomData;
use digest::{
generic_array::{typenum::Unsigned, GenericArray},
BlockInput, Digest,
};
use subtle::{Choice, ConditionallySelectable};

/// Placeholder type for implementing expand_message_xmd based on a hash function
#[derive(Debug)]
pub struct ExpandMsgXmd<HashT> {
phantom: PhantomData<HashT>,
}

/// ExpandMsgXmd implements expand_message_xmd for the ExpandMsg trait
impl<HashT, const LEN_IN_BYTES: usize> ExpandMsg<LEN_IN_BYTES> for ExpandMsgXmd<HashT>
where
HashT: Digest + BlockInput,
{
fn expand_message(msg: &[u8], dst: &[u8]) -> [u8; LEN_IN_BYTES] {
let b_in_bytes = HashT::OutputSize::to_usize();
let ell = (LEN_IN_BYTES + b_in_bytes - 1) / b_in_bytes;
if ell > 255 {
panic!("ell was too big in expand_message_xmd");
}
let b_0 = HashT::new()
.chain(GenericArray::<u8, HashT::BlockSize>::default())
.chain(msg)
.chain([(LEN_IN_BYTES >> 8) as u8, LEN_IN_BYTES as u8, 0u8])
.chain(dst)
.chain([dst.len() as u8])
.finalize();

let mut b_vals = HashT::new()
.chain(&b_0[..])
.chain([1u8])
.chain(dst)
.chain([dst.len() as u8])
.finalize();

let mut buf = [0u8; LEN_IN_BYTES];
let mut offset = 0;

for i in 1..ell {
// b_0 XOR b_(idx - 1)
let mut tmp = GenericArray::<u8, HashT::OutputSize>::default();
b_0.iter()
.zip(&b_vals[..])
.enumerate()
.for_each(|(j, (b0val, bi1val))| tmp[j] = b0val ^ bi1val);
for b in b_vals {
buf[offset % LEN_IN_BYTES].conditional_assign(
&b,
Choice::from(if offset < LEN_IN_BYTES { 1 } else { 0 }),
);
offset += 1;
}
b_vals = HashT::new()
.chain(tmp)
.chain([(i + 1) as u8])
.chain(dst)
.chain([dst.len() as u8])
.finalize();
}
for b in b_vals {
buf[offset % LEN_IN_BYTES]
.conditional_assign(&b, Choice::from(if offset < LEN_IN_BYTES { 1 } else { 0 }));
offset += 1;
}
buf
}
}
27 changes: 27 additions & 0 deletions elliptic-curve/src/hash2field/expand_msg_xof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use super::ExpandMsg;
use core::marker::PhantomData;
use digest::{ExtendableOutput, Update, XofReader};

/// Placeholder type for implementing expand_message_xof based on an extendable output function
#[derive(Debug)]
pub struct ExpandMsgXof<HashT> {
phantom: PhantomData<HashT>,
}

/// ExpandMsgXof implements expand_message_xof for the ExpandMsg trait
impl<HashT, const LEN_IN_BYTES: usize> ExpandMsg<LEN_IN_BYTES> for ExpandMsgXof<HashT>
where
HashT: Default + ExtendableOutput + Update,
{
fn expand_message(msg: &[u8], dst: &[u8]) -> [u8; LEN_IN_BYTES] {
let mut buf = [0u8; LEN_IN_BYTES];
let mut r = HashT::default()
.chain(msg)
.chain([(LEN_IN_BYTES >> 8) as u8, LEN_IN_BYTES as u8])
.chain(dst)
.chain([dst.len() as u8])
.finalize_xof();
r.read(&mut buf);
buf
}
}
6 changes: 6 additions & 0 deletions elliptic-curve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ mod jwk;
#[cfg_attr(docsrs, doc(cfg(feature = "osswu")))]
pub mod osswu;

/// Traits for computing hash to field as described in
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve>
#[cfg(feature = "hashing")]
#[cfg_attr(docsrs, doc(cfg(feature = "hashing")))]
pub mod hash2field;

pub use crate::{
error::{Error, Result},
point::{
Expand Down

0 comments on commit 72e11e3

Please sign in to comment.