Skip to content

Commit

Permalink
Update scalar parsing and signature API
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronFeickert committed Sep 27, 2023
1 parent 34d8ea5 commit a000006
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 288 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ version = "0.18.0"
edition = "2018"

[dependencies]
tari_utilities = { version = "0.5", default-features = false, features = ["zero"] }
tari_utilities = { version = "0.6", default-features = false, features = ["zero"] }
blake2 = { version = "0.10", default-features = false }
borsh = { version = "0.10" , optional = true , default-features = false}
bulletproofs_plus = { package = "tari_bulletproofs_plus", version = "0.3", optional = true }
Expand All @@ -27,7 +27,7 @@ snafu = { version = "0.7", default-features = false}
zeroize = {version = "1" , default-features = false}

[dev-dependencies]
tari_utilities = { version = "0.5", features = ["std"] }
tari_utilities = { version = "0.6", features = ["std"] }
serde = { version = "1.0"}
bincode = { version = "1.1" }
criterion = { version = "0.5", default-features = false }
Expand Down
6 changes: 3 additions & 3 deletions benches/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn sign_message(c: &mut Criterion) {
b.iter_batched(
gen_keypair,
|d| {
let _sig = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap();
let _sig = RistrettoSchnorr::sign(&d.k, d.m, &mut OsRng).unwrap();
},
BatchSize::SmallInput,
);
Expand All @@ -60,10 +60,10 @@ fn verify_message(c: &mut Criterion) {
b.iter_batched(
|| {
let d = gen_keypair();
let s = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap();
let s = RistrettoSchnorr::sign(&d.k, d.m, &mut OsRng).unwrap();
(d, s)
},
|(d, s)| assert!(s.verify_message(&d.p, d.m)),
|(d, s)| assert!(s.verify(&d.p, d.m)),
BatchSize::SmallInput,
);
});
Expand Down
4 changes: 2 additions & 2 deletions src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ where P: PublicKey
impl<P> ByteArray for HomomorphicCommitment<P>
where P: PublicKey
{
fn from_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
let p = P::from_bytes(bytes)?;
fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
let p = P::from_canonical_bytes(bytes)?;
Ok(Self(p))
}

Expand Down
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub enum HashingError {
reason: String,
},
/// The digest does not produce enough output
#[snafu(display("The digest does produce enough output.`{bytes}' bytes are required."))]
#[snafu(display("The digest does not produce enough output.`{bytes}' bytes are required."))]
DigestTooShort {
/// The number of bytes required
bytes: usize,
Expand Down
46 changes: 31 additions & 15 deletions src/hashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ use alloc::string::String;
use core::{marker::PhantomData, ops::Deref};

use blake2::{Blake2b, Blake2bVar};
use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Output, OutputSizeUser, Update};
use digest::{
consts::{U32, U64},
Digest,
FixedOutput,
FixedOutputReset,
Output,
OutputSizeUser,
Update,
};
use sha3::Sha3_256;
use tari_utilities::ByteArray;

use crate::{
alloc::string::ToString,
Expand Down Expand Up @@ -209,10 +216,10 @@ impl<D: Digest> AsRef<[u8]> for DomainSeparatedHash<D> {
/// Calculating a signature challenge
///
/// ```
/// # use blake2::Blake2b;
/// # use digest::{consts::U32, Digest};
/// # use tari_utilities::hex::{to_hex, Hex};
/// use blake2::{Blake2b, Digest};
/// use digest::consts::U32;
/// use tari_crypto::{
/// # use tari_crypto::{
/// hash_domain,
/// hashing::{DomainSeparatedHash, DomainSeparatedHasher, DomainSeparation},
/// };
Expand Down Expand Up @@ -418,6 +425,8 @@ impl LengthExtensionAttackResistant for Sha3_256 {}

impl LengthExtensionAttackResistant for Blake2b<U32> {}

impl LengthExtensionAttackResistant for Blake2b<U64> {}

//------------------------------------------------ HMAC ------------------------------------------------------------
/// A domain separation tag for use in MAC derivation algorithms.
pub struct MacDomain;
Expand Down Expand Up @@ -517,62 +526,69 @@ impl<D: Digest> Deref for Mac<D> {
/// `RistrettoKdf` is an implementation of [`DerivedKeyDomain`] that generates Ristretto keys.
///
/// ```
/// # use blake2::Blake2b;
/// # use digest::{consts::U64, Digest};
/// # use tari_utilities::ByteArray;
/// # use tari_utilities::hex::Hex;
/// # use tari_crypto::errors::HashingError;
/// # use tari_crypto::hashing::{DerivedKeyDomain, MacDomain};
/// # use tari_crypto::keys::SecretKey;
/// # use tari_crypto::ristretto::ristretto_keys::RistrettoKdf;
/// # use tari_crypto::ristretto::RistrettoSecretKey;
/// # use digest::consts::U32;
/// # use blake2::Blake2b;
///
/// fn wallet_keys(
/// primary_key: &RistrettoSecretKey,
/// index: usize,
/// ) -> Result<RistrettoSecretKey, HashingError> {
/// RistrettoKdf::generate::<Blake2b<U32>>(
/// RistrettoKdf::generate::<Blake2b<U64>>(
/// primary_key.as_bytes(),
/// &index.to_le_bytes(),
/// "wallet",
/// )
/// }
///
/// let key = RistrettoSecretKey::from_hex(
/// "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c",
/// "a8fb609c5ab7cc07548b076b6c25cc3237c4526fb7a6dcb83b26f457b172c20a",
/// )
/// .unwrap();
/// let key_1 = wallet_keys(&key, 1).unwrap();
/// assert_eq!(
/// key_1.to_hex(),
/// "b778b8b5041fbde6c78be5bafd6d62633824bf303c97736d7337b3f6f70c4e0b"
/// "08106b88a2ff4c52d1d8b458cf34802df8655ba989a7d91351e3504e087a2e0c"
/// );
/// let key_64 = wallet_keys(&key, 64).unwrap();
/// assert_eq!(
/// key_64.to_hex(),
/// "09e5204c93406ef3334ff5f7a4d5d84199ceb9119fafcb98928fa95e95f0ae05"
/// "2c2206dadd2a21e71b6c52dd321572cde0f2b00e7116e1123fb580b09ed1b70e"
/// );
/// ```
pub trait DerivedKeyDomain: DomainSeparation {
/// The associated derived secret key type
type DerivedKeyType: SecretKey;

/// Derive a key from the input key using a suitable domain separation tag and the given application label.
/// An error is returned if the supplied primary key isn't at least as long as the digest algorithm's output size.
/// Derive a key from the input key using a suitable domain separation tag and the given application label by wide
/// reduction. An error is returned if the supplied primary key isn't at least as long as the derived key.
/// If the digest's output size is not sufficient to generate the derived key type, then an error will be thrown.
fn generate<D>(primary_key: &[u8], data: &[u8], label: &'static str) -> Result<Self::DerivedKeyType, HashingError>
where
Self: Sized,
D: Digest + Update,
{
if primary_key.as_ref().len() < <D as Digest>::output_size() {
// Ensure the primary key is at least as long as the derived key
if primary_key.len() < <Self::DerivedKeyType as SecretKey>::KEY_LEN {
return Err(HashingError::InputTooShort {});
}

// Ensure the digest length is suitable for wide reduction
if <D as Digest>::output_size() != <Self::DerivedKeyType as SecretKey>::WIDE_REDUCTION_LEN {
return Err(HashingError::InputTooShort {});
}

let hash = DomainSeparatedHasher::<D, Self>::new_with_label(label)
.chain(primary_key)
.chain(data)
.finalize();
let derived_key = Self::DerivedKeyType::from_bytes(hash.as_ref())
let derived_key = Self::DerivedKeyType::from_uniform_bytes(hash.as_ref())
.map_err(|e| HashingError::ConversionFromBytes { reason: e.to_string() })?;
Ok(derived_key)
}
Expand Down
9 changes: 8 additions & 1 deletion src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use core::ops::Add;

use rand_core::{CryptoRng, RngCore};
use tari_utilities::ByteArray;
use tari_utilities::{ByteArray, ByteArrayError};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// A trait specifying common behaviour for representing `SecretKey`s. Specific elliptic curve
Expand All @@ -32,13 +32,20 @@ pub trait SecretKey:
/// The length of the byte encoding of a key, in bytes
const KEY_LEN: usize;

/// The number of bytes used for construction by wide reduction
const WIDE_REDUCTION_LEN: usize;

/// The length of the byte encoding of a key, in bytes
fn key_length() -> usize {
Self::KEY_LEN
}

/// Generates a random secret key
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self;

/// Generates a secret key from a slice of uniformly-distributed bytes using wide reduction
/// If the number of bytes is incorrect, this will fail
fn from_uniform_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError>;
}

//---------------------------------------- Public Keys ----------------------------------------//
Expand Down
1 change: 0 additions & 1 deletion src/ristretto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub mod ristretto_keys;
mod ristretto_sig;
#[cfg(feature = "serde")]
pub mod serialize;
pub mod utils;

pub use self::{
ristretto_com_and_pub_sig::RistrettoComAndPubSig,
Expand Down
2 changes: 1 addition & 1 deletion src/ristretto/pedersen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ mod test {
let (_, p) = RistrettoPublicKey::random_keypair(&mut rng);
let c = PedersenCommitment::from_public_key(&p);
assert_eq!(c.as_public_key(), &p);
let c2 = PedersenCommitment::from_bytes(c.as_bytes()).unwrap();
let c2 = PedersenCommitment::from_canonical_bytes(c.as_bytes()).unwrap();
assert_eq!(c, c2);
}

Expand Down
57 changes: 22 additions & 35 deletions src/ristretto/ristretto_com_and_pub_sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,18 @@ use crate::{
/// "8063d85e151abee630e643e2b3dc47bfaeb8aa859c9d10d60847985f286aad19",
/// )
/// .unwrap();
/// let u_a = RistrettoSecretKey::from_bytes(b"10000000000000000000000010000000").unwrap();
/// let u_x = RistrettoSecretKey::from_bytes(b"a00000000000000000000000a0000000").unwrap();
/// let u_y = RistrettoSecretKey::from_bytes(b"a00000000000000000000000a0000000").unwrap();
/// let u_a = RistrettoSecretKey::from_hex(
/// "a8fb609c5ab7cc07548b076b6c25cc3237c4526fb7a6dcb83b26f457b172c20a",
/// )
/// .unwrap();
/// let u_x = RistrettoSecretKey::from_hex(
/// "0e689df8ad4ad9d2fd5aaf8cb0a66d85cb0d4b7a380405514d453625813b0b0f",
/// )
/// .unwrap();
/// let u_y = RistrettoSecretKey::from_hex(
/// "f494050bd0d4ed0ec514cdce9430d0564df6b35d2a12b7daa0e99c7d94a06509",
/// )
/// .unwrap();
/// let sig = RistrettoComAndPubSig::new(ephemeral_commitment, ephemeral_pubkey, u_a, u_x, u_y);
/// ```
///
Expand All @@ -48,7 +57,7 @@ use crate::{
/// # use tari_crypto::ristretto::pedersen::*;
/// use tari_crypto::ristretto::pedersen::commitment_factory::PedersenCommitmentFactory;
/// use tari_utilities::hex::Hex;
/// use digest::consts::U32;
/// use digest::consts::U64;
///
/// let mut rng = rand::thread_rng();
/// let a_val = RistrettoSecretKey::random(&mut rng);
Expand All @@ -57,7 +66,7 @@ use crate::{
/// let a_nonce = RistrettoSecretKey::random(&mut rng);
/// let x_nonce = RistrettoSecretKey::random(&mut rng);
/// let y_nonce = RistrettoSecretKey::random(&mut rng);
/// let e = Blake2b::<U32>::digest(b"Maskerade"); // In real life, this should be strong Fiat-Shamir!
/// let e = Blake2b::<U64>::digest(b"Maskerade"); // In real life, this should be strong Fiat-Shamir!
/// let factory = PedersenCommitmentFactory::default();
/// let commitment = factory.commit(&x_val, &a_val);
/// let pubkey = RistrettoPublicKey::from_secret_key(&y_val);
Expand All @@ -72,8 +81,8 @@ pub type RistrettoComAndPubSig = CommitmentAndPublicKeySignature<RistrettoPublic
#[cfg(test)]
mod test {
use blake2::Blake2b;
use digest::{consts::U32, Digest};
use tari_utilities::{hex::from_hex, ByteArray};
use digest::{consts::U64, Digest};
use tari_utilities::ByteArray;

use crate::{
commitment::HomomorphicCommitmentFactory,
Expand Down Expand Up @@ -132,15 +141,15 @@ mod test {
let ephemeral_commitment = factory.commit(&r_x, &r_a);
let ephemeral_pubkey = RistrettoPublicKey::from_secret_key(&r_y);

// Challenge; doesn't use proper Fiat-Shamir, so it's for testing only!
let challenge = Blake2b::<U32>::new()
// Challenge; doesn't use domain-separated Fiat-Shamir, so it's for testing only!
let challenge = Blake2b::<U64>::new()
.chain_update(commitment.as_bytes())
.chain_update(pubkey.as_bytes())
.chain_update(ephemeral_commitment.as_bytes())
.chain_update(ephemeral_pubkey.as_bytes())
.chain_update(b"Small Gods")
.finalize();
let e_key = RistrettoSecretKey::from_bytes(&challenge).unwrap();
let e_key = RistrettoSecretKey::from_uniform_bytes(&challenge).unwrap();

// Responses
let u_a = &r_a + e_key.clone() * &a_value;
Expand Down Expand Up @@ -178,7 +187,7 @@ mod test {
assert!(!sig.verify_challenge(&commitment, &evil_pubkey, &challenge, &factory, &mut rng));

// A different challenge should fail
let evil_challenge = Blake2b::<U32>::digest(b"Guards! Guards!");
let evil_challenge = Blake2b::<U64>::digest(b"Guards! Guards!");
assert!(!sig.verify_challenge(&commitment, &pubkey, &evil_challenge, &factory, &mut rng));
}

Expand All @@ -205,7 +214,7 @@ mod test {
let ephemeral_pubkey = RistrettoPublicKey::from_secret_key(&r_y);

// Challenge; doesn't use proper Fiat-Shamir, so it's for testing only!
let challenge = Blake2b::<U32>::new()
let challenge = Blake2b::<U64>::new()
.chain_update(commitment.as_bytes())
.chain_update(pubkey.as_bytes())
.chain_update(ephemeral_commitment.as_bytes())
Expand Down Expand Up @@ -292,7 +301,7 @@ mod test {
let ephemeral_pubkey_bob = RistrettoPublicKey::from_secret_key(&r_y_bob);

// The challenge is common to Alice and Bob; here we use an arbitrary hash
let challenge = Blake2b::<U32>::digest(b"Test challenge");
let challenge = Blake2b::<U64>::digest(b"Test challenge");

// Alice's signature
let sig_alice = RistrettoComAndPubSig::sign(
Expand Down Expand Up @@ -338,28 +347,6 @@ mod test {
assert!(sig_sum.verify_challenge(&commitment_sum, &pubkey_sum, &challenge, &factory, &mut rng))
}

/// Ristretto scalars have a max value 2^255. This test checks that hashed messages above this value can still be
/// signed as a result of applying modulo arithmetic on the challenge value
#[test]
fn challenge_from_invalid_scalar() {
let mut rng = rand::thread_rng();
let factory = PedersenCommitmentFactory::default();

let a_value = RistrettoSecretKey::random(&mut rng);
let x_value = RistrettoSecretKey::random(&mut rng);
let y_value = RistrettoSecretKey::random(&mut rng);

let message = from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();

let r_a = RistrettoSecretKey::random(&mut rng);
let r_x = RistrettoSecretKey::random(&mut rng);
let r_y = RistrettoSecretKey::random(&mut rng);

assert!(
RistrettoComAndPubSig::sign(&a_value, &x_value, &y_value, &r_a, &r_x, &r_y, &message, &factory).is_ok()
);
}

#[test]
fn to_vec() {
let sig = RistrettoComAndPubSig::default();
Expand Down
Loading

0 comments on commit a000006

Please sign in to comment.