Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hash functions cleanup #36

Merged
merged 3 commits into from
Mar 6, 2021
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
2 changes: 0 additions & 2 deletions umbral-pre/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ categories = ["cryptography", "no-std"]

[dependencies]
k256 = { version = "0.7", default-features = false, features = ["ecdsa", "arithmetic"] }
blake2 = "0.9"
sha3 = "0.9"
sha2 = "0.9"
fjarri marked this conversation as resolved.
Show resolved Hide resolved
chacha20poly1305 = "0.7"
hkdf = "0.10"
Expand Down
1 change: 0 additions & 1 deletion umbral-pre/src/capsule_frag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ impl CapsuleFrag {
let v2 = self.proof.point_v2;
let u2 = self.proof.kfrag_pok;

// TODO (#2): original uses ExtendedKeccak here
let h = ScalarDigest::new()
.chain_points(&[e, e1, e2, v, v1, v2, u, u1, u2])
.chain_scalar(&self.proof.metadata)
Expand Down
6 changes: 3 additions & 3 deletions umbral-pre/src/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ impl SerializableToArray for PublicKey {
#[cfg(test)]
mod tests {

use sha3::Sha3_256;
use sha2::Sha256;
fjarri marked this conversation as resolved.
Show resolved Hide resolved
use signature::digest::Digest;

use super::{PublicKey, SecretKey};
Expand All @@ -295,11 +295,11 @@ mod tests {
fn test_sign_and_verify() {
let sk = SecretKey::random();
let message = b"asdafdahsfdasdfasd";
let digest = Sha3_256::new().chain(message);
let digest = Sha256::new().chain(message);
let signature = sk.sign_digest(digest);

let pk = PublicKey::from_secret_key(&sk);
let digest = Sha3_256::new().chain(message);
let digest = Sha256::new().chain(message);
assert!(pk.verify_digest(digest, &signature));
}
}
4 changes: 2 additions & 2 deletions umbral-pre/src/dem.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use alloc::boxed::Box;

use aead::{Aead, AeadInPlace, Payload};
use blake2::Blake2b;
use chacha20poly1305::aead::NewAead;
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use generic_array::{typenum::Unsigned, GenericArray};
use hkdf::Hkdf;
use rand_core::OsRng;
use rand_core::RngCore;
use sha2::Sha256;

type KdfSize = <ChaCha20Poly1305 as NewAead>::KeySize;

fn kdf(seed: &[u8], salt: Option<&[u8]>, info: Option<&[u8]>) -> GenericArray<u8, KdfSize> {
let hk = Hkdf::<Blake2b>::new(salt, &seed);
let hk = Hkdf::<Sha256>::new(salt, &seed);

let mut okm = GenericArray::<u8, KdfSize>::default();

Expand Down
72 changes: 35 additions & 37 deletions umbral-pre/src/hashing.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,49 @@
use blake2::VarBlake2b;
use digest::{Digest, Update, VariableOutput};
use generic_array::typenum::Unsigned;
use digest::Digest;
use generic_array::sequence::Concat;
use generic_array::GenericArray;
use sha3::Sha3_256;
use sha2::Sha256;
use typenum::U1;

use crate::curve::{CurvePoint, CurveScalar, PublicKey, SecretKey, Signature};
use crate::traits::SerializableToArray;

/// Hashes arbitrary data into a valid EC point of the specified curve,
/// Hashes arbitrary data with the given domain separation tag
/// into a valid EC point of the specified curve,
/// using the try-and-increment method.
/// It admits an optional label as an additional input to the hash function.
/// It uses BLAKE2b (with a digest size of 64 bytes) as the internal hash function.
///
/// WARNING: Do not use when the input data is secret, as this implementation is not
/// in constant time, and hence, it is not safe with respect to timing attacks.
pub fn unsafe_hash_to_point(data: &[u8], label: &[u8]) -> Option<CurvePoint> {
pub fn unsafe_hash_to_point(dst: &[u8], data: &[u8]) -> Option<CurvePoint> {
// NOTE: Yes, this function is hacky, but it is the only way
// to hash to a point with an *unknown* discrete log.
// Don't replace with hashing to scalar and multiplying by a generator!

let len_data = (data.len() as u32).to_be_bytes();
let len_label = (label.len() as u32).to_be_bytes();
// TODO (#35): use the standard method when it is available in RustCrypto.

type PointSize = <CurvePoint as SerializableToArray>::Size;
let point_size = PointSize::to_usize();
let mut arr = GenericArray::<u8, PointSize>::default();
// Fixed sign prefix. Halves the range of the generated points, but we only need one,
// and it is always the same.
let sign_prefix = GenericArray::<u8, U1>::from_slice(&[2u8]);
fjarri marked this conversation as resolved.
Show resolved Hide resolved

let dst_len = (dst.len() as u32).to_be_bytes();
let data_len = (data.len() as u32).to_be_bytes();

// We use an internal 32-bit counter as additional input
let mut i = 0u32;
while i < <u32>::MAX {
let ibytes = (i as u32).to_be_bytes();

// May fail if `point_size` is too large for the hashing algorithm.
let digest = VarBlake2b::new(point_size).ok()?;
digest
.chain(&len_label)
.chain(label)
.chain(&len_data)
.chain(data)
.chain(&ibytes)
.finalize_variable(|buf| arr = *GenericArray::<u8, PointSize>::from_slice(buf));
let mut digest = Sha256::new();
digest.update(&dst_len);
digest.update(dst);
digest.update(&data_len);
digest.update(data);
digest.update(&ibytes);
let result = digest.finalize();

// Set the sign byte
let arr_data = arr.as_mut_slice();
arr_data[0] = if arr_data[0] & 1 == 0 { 2 } else { 3 };
let maybe_point_bytes = sign_prefix.concat(result);

let maybe_point = CurvePoint::from_bytes(&arr);
let maybe_point = CurvePoint::from_bytes(&maybe_point_bytes);
if maybe_point.is_some() {
return maybe_point;
}
Expand All @@ -58,12 +56,11 @@ pub fn unsafe_hash_to_point(data: &[u8], label: &[u8]) -> Option<CurvePoint> {
None
}

pub(crate) struct ScalarDigest(Sha3_256);
pub(crate) struct ScalarDigest(Sha256);

// TODO (#2): original uses ExtendedKeccak here
impl ScalarDigest {
pub fn new() -> Self {
Self(Sha3_256::new())
Self(Sha256::new())
}

pub fn new_with_dst(bytes: &[u8]) -> Self {
Expand Down Expand Up @@ -95,15 +92,16 @@ impl ScalarDigest {
}

pub fn finalize(self) -> CurveScalar {
// TODO (#35): use the standard method when it is available in RustCrypto.
CurveScalar::from_digest(self.0)
}
}

pub(crate) struct SignatureDigest(Sha3_256);
pub(crate) struct SignatureDigest(Sha256);

impl SignatureDigest {
pub fn new() -> Self {
Self(Sha3_256::new())
Self(Sha256::new())
}

fn chain_impl(self, bytes: &[u8]) -> Self {
Expand Down Expand Up @@ -144,18 +142,18 @@ mod tests {
#[test]
fn test_unsafe_hash_to_point() {
let data = b"abcdefg";
let label = b"sdasdasd";
let p = unsafe_hash_to_point(&data[..], &label[..]);
let p_same = unsafe_hash_to_point(&data[..], &label[..]);
let dst = b"sdasdasd";
let p = unsafe_hash_to_point(&dst[..], &data[..]);
let p_same = unsafe_hash_to_point(&dst[..], &data[..]);
assert_eq!(p, p_same);

let data2 = b"abcdefgh";
let p_data2 = unsafe_hash_to_point(&data2[..], &label[..]);
let p_data2 = unsafe_hash_to_point(&dst[..], &data2[..]);
assert_ne!(p, p_data2);

let label2 = b"sdasdasds";
let p_label2 = unsafe_hash_to_point(&data[..], &label2[..]);
assert_ne!(p, p_label2);
let dst2 = b"sdasdasds";
let p_dst2 = unsafe_hash_to_point(&dst2[..], &data[..]);
assert_ne!(p, p_dst2);
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions umbral-pre/src/hashing_ds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use crate::curve::{CurvePoint, CurveScalar};
use crate::hashing::ScalarDigest;

// TODO (#39): Ideally this should return a non-zero scalar.
pub(crate) fn hash_to_polynomial_arg(
precursor: &CurvePoint,
pubkey: &CurvePoint,
Expand All @@ -18,6 +19,8 @@ pub(crate) fn hash_to_polynomial_arg(
.finalize()
}

// TODO (#39): Ideally this should return a non-zero scalar.
// (when it does, the loop in `KeyFragFactory::new()` can be removed)
pub(crate) fn hash_to_shared_secret(
precursor: &CurvePoint,
pubkey: &CurvePoint,
Expand Down
6 changes: 3 additions & 3 deletions umbral-pre/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ pub struct Parameters {
impl Parameters {
/// Creates a new parameter object.
pub fn new() -> Self {
// Some future-proofing for a scenario where someone uses a different generator.
// We wouldn't want `u` to be the same in that case.
let g = CurvePoint::generator();
let g_bytes = g.to_array();

let parameters_seed = b"NuCypher/UmbralParameters/u";

// Only fails with a minuscule probability,
// or if the size of a point is too large for the hasher.
// In any case, we will notice it in tests.
let u = unsafe_hash_to_point(&g_bytes, parameters_seed).unwrap();
let u = unsafe_hash_to_point(b"POINT_U", &g_bytes).unwrap();

Self { u }
}
Expand Down