diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
index da19e935..3b82a889 100644
--- a/.github/workflows/audit.yml
+++ b/.github/workflows/audit.yml
@@ -7,7 +7,7 @@ jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/check_licence.yml b/.github/workflows/check_licence.yml
index fe8b3844..c923c867 100644
--- a/.github/workflows/check_licence.yml
+++ b/.github/workflows/check_licence.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: install ripgrep
run: |
wget https://github.com/BurntSushi/ripgrep/releases/download/13.0.0/ripgrep_13.0.0_amd64.deb
diff --git a/.github/workflows/clippy-check.yml b/.github/workflows/clippy-check.yml
index d1b963ca..3aa68b22 100644
--- a/.github/workflows/clippy-check.yml
+++ b/.github/workflows/clippy-check.yml
@@ -1,4 +1,4 @@
-name: Clippy and FMT
+name: Formatting, lints, and code checks
on: [push, pull_request]
jobs:
@@ -6,52 +6,29 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
- name: Install components
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
components: clippy, rustfmt
toolchain: nightly
- override: true
- name: Toolchain thumbv8m.main-none-eabi
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
- profile: minimal
toolchain: nightly
- target: thumbv8m.main-none-eabi
- override: true
+ targets: thumbv8m.main-none-eabi
- name: Check formatting
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- toolchain: nightly
- args: --all -- --check
- - name: Install cargo-lints
- uses: actions-rs/cargo@v1
- with:
- command: install
- args: cargo-lints
- - name: Clippy lints
- uses: actions-rs/cargo@v1
- with:
- command: lints
- toolchain: nightly
- args: clippy --all-targets --all-features
- - name: Cargo check
- uses: actions-rs/cargo@v1
- with:
- command: check
- args: --release --all-targets
- - name: Cargo check no default
- uses: actions-rs/cargo@v1
- with:
- command: check
- args: --release --no-default-features
+ run: cargo +nightly fmt --all -- --check
+ - name: Install linter
+ run: cargo install cargo-lints
+ - name: Run linter
+ run: cargo +nightly lints clippy --all-targets --all-features
+ - name: Check code
+ run: cargo +stable check --release --all-targets
+ - name: Check code (no default features)
+ run: cargo +stable check --release --no-default-features
# This check here is to ensure that it builds for no-std rust targets
- - name: Cargo check for no-std
- uses: actions-rs/cargo@v1
- with:
- command: check
- toolchain: nightly
- args: --no-default-features --target=thumbv8m.main-none-eabi -Zavoid-dev-deps
-
+ - name: Check code (no-std)
+ run: cargo +nightly check --no-default-features --target=thumbv8m.main-none-eabi -Zavoid-dev-deps
+ - name: Check benchmarks
+ run: cargo +nightly check --benches
diff --git a/.github/workflows/source-cov.yml b/.github/workflows/source-cov.yml
index d7b64e4a..9352cbf2 100644
--- a/.github/workflows/source-cov.yml
+++ b/.github/workflows/source-cov.yml
@@ -10,16 +10,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y jq lcov
- name: Download Rust
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUSTUP_TOOLCHAIN }}
- override: true
components: llvm-tools-preview
- name: Install requirements for code coverage
run: |
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f61cfc0b..d48a0309 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,40 +11,19 @@ jobs:
rust:
- stable
steps:
- - name: checkout
- uses: actions/checkout@v2
- - name: toolchain
- uses: actions-rs/toolchain@v1
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install components
+ uses: dtolnay/rust-toolchain@master
with:
- profile: minimal
- toolchain: ${{ matrix.rust }}
- override: true
+ toolchain: stable
- name: test/debug
- uses: actions-rs/cargo@v1
- with:
- command: test
+ run: cargo +stable test
- name: test/release
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --release
+ run: cargo +stable test --release
- name: test/debug features
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --all-features
+ run: cargo +stable test --all-features
- name: test/release features
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --release --all-features
- - name: docs build
- uses: actions-rs/cargo@v1
- with:
- command: doc
- args: --all-features
- - name: bench
- uses: actions-rs/cargo@v1
- with:
- command: check
- args: --benches
+ run: cargo +stable test --release --all-features
+ - name: Build documentation
+ run: cargo doc --all-features --no-deps
diff --git a/Cargo.toml b/Cargo.toml
index 739618fb..1fe9a40f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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 }
@@ -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 }
diff --git a/benches/signatures.rs b/benches/signatures.rs
index 224bd554..8ab2d9e9 100644
--- a/benches/signatures.rs
+++ b/benches/signatures.rs
@@ -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,
);
@@ -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,
);
});
diff --git a/lints.toml b/lints.toml
index a8b093f0..14733107 100644
--- a/lints.toml
+++ b/lints.toml
@@ -1,41 +1,33 @@
deny = [
# Prevent spelling mistakes in lints
'unknown_lints',
- # clippy groups:
- 'clippy::correctness',
- # All clippy allows must have a reason
- # TODO: enable lint-reasons feature
- # 'clippy::allow_attributes_without_reason',
- # Docs
+
+ # Use the default groups:
+ # correctness, suspicious, style, complexity, perf
+ 'clippy::all',
+
+ # Require documentation
'missing_docs',
- # 'clippy::missing_errors_doc',
- # 'clippy::missing_safety_doc',
- # 'clippy::missing_panics_doc',
# Common mistakes
- 'clippy::await_holding_lock',
'unused_variables',
'unused_imports',
'dead_code',
'unused_extern_crates',
'unused_must_use',
'unreachable_patterns',
+ 'let_underscore_drop',
+
'clippy::cloned_instead_of_copied',
'clippy::create_dir',
'clippy::dbg_macro',
'clippy::else_if_without_else',
'clippy::enum_glob_use',
'clippy::inline_always',
- 'let_underscore_drop',
- 'clippy::let_unit_value',
'clippy::match_on_vec_items',
'clippy::match_wild_err_arm',
- # In crypto code, it is fairly common to have similar names e.g. `owner_pk` and `owner_k`
- # 'clippy::similar_names',
- 'clippy::needless_borrow',
- # style
- 'clippy::style',
+ # Style preferences
'clippy::explicit_into_iter_loop',
'clippy::explicit_iter_loop',
'clippy::if_not_else',
@@ -46,7 +38,7 @@ deny = [
'clippy::too_many_lines',
'clippy::trivially_copy_pass_by_ref',
- # casting mistakes
+ # Casting mistakes
'clippy::cast_lossless',
'clippy::cast_possible_truncation',
'clippy::cast_possible_wrap',
diff --git a/src/commitment.rs b/src/commitment.rs
index 84561da0..9abfa208 100644
--- a/src/commitment.rs
+++ b/src/commitment.rs
@@ -68,8 +68,8 @@ where P: PublicKey
impl
ByteArray for HomomorphicCommitment
where P: PublicKey
{
- fn from_bytes(bytes: &[u8]) -> Result {
- let p = P::from_bytes(bytes)?;
+ fn from_canonical_bytes(bytes: &[u8]) -> Result {
+ let p = P::from_canonical_bytes(bytes)?;
Ok(Self(p))
}
diff --git a/src/errors.rs b/src/errors.rs
index db0e623c..7c1a0ad6 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -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,
diff --git a/src/hashing.rs b/src/hashing.rs
index be1b29b5..435cf87c 100644
--- a/src/hashing.rs
+++ b/src/hashing.rs
@@ -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,
@@ -209,10 +216,10 @@ impl AsRef<[u8]> for DomainSeparatedHash {
/// 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},
/// };
@@ -418,6 +425,8 @@ impl LengthExtensionAttackResistant for Sha3_256 {}
impl LengthExtensionAttackResistant for Blake2b {}
+impl LengthExtensionAttackResistant for Blake2b {}
+
//------------------------------------------------ HMAC ------------------------------------------------------------
/// A domain separation tag for use in MAC derivation algorithms.
pub struct MacDomain;
@@ -517,6 +526,8 @@ impl Deref for Mac {
/// `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;
@@ -524,14 +535,12 @@ impl Deref for Mac {
/// # 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 {
-/// RistrettoKdf::generate::>(
+/// RistrettoKdf::generate::>(
/// primary_key.as_bytes(),
/// &index.to_le_bytes(),
/// "wallet",
@@ -539,40 +548,47 @@ impl Deref for Mac {
/// }
///
/// 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(primary_key: &[u8], data: &[u8], label: &'static str) -> Result
where
Self: Sized,
D: Digest + Update,
{
- if primary_key.as_ref().len() < ::output_size() {
+ // Ensure the primary key is at least as long as the derived key
+ if primary_key.len() < ::KEY_LEN {
return Err(HashingError::InputTooShort {});
}
+
+ // Ensure the digest length is suitable for wide reduction
+ if ::output_size() != ::WIDE_REDUCTION_LEN {
+ return Err(HashingError::InputTooShort {});
+ }
+
let hash = DomainSeparatedHasher::::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)
}
diff --git a/src/keys.rs b/src/keys.rs
index 14f8e250..b1692f26 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -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
@@ -32,6 +32,9 @@ 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
@@ -39,6 +42,10 @@ pub trait SecretKey:
/// Generates a random secret key
fn random(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;
}
//---------------------------------------- Public Keys ----------------------------------------//
diff --git a/src/ristretto/constants.rs b/src/ristretto/constants.rs
index 8e65f0d1..75261eb7 100644
--- a/src/ristretto/constants.rs
+++ b/src/ristretto/constants.rs
@@ -102,7 +102,7 @@ mod test {
let mut a: [u8; 64] = [0; 64];
for i in 0..n {
let mut data = b"TARI CRYPTO NUMS BASEPOINT LABEL - ".to_vec(); // Domain label
- data.append(&mut i.to_le_bytes().to_vec()); // Append domain separated label counter
+ data.append(&mut (i as u64).to_le_bytes().to_vec()); // Append domain separated label counter
let hashed_v = Sha512::digest(&data);
a.copy_from_slice(&hashed_v);
let next_val = RistrettoPoint::from_uniform_bytes(&a);
diff --git a/src/ristretto/mod.rs b/src/ristretto/mod.rs
index af3102db..de44c132 100644
--- a/src/ristretto/mod.rs
+++ b/src/ristretto/mod.rs
@@ -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,
diff --git a/src/ristretto/pedersen/mod.rs b/src/ristretto/pedersen/mod.rs
index 1230c05c..7fd70f49 100644
--- a/src/ristretto/pedersen/mod.rs
+++ b/src/ristretto/pedersen/mod.rs
@@ -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);
}
diff --git a/src/ristretto/ristretto_com_and_pub_sig.rs b/src/ristretto/ristretto_com_and_pub_sig.rs
index 907b5beb..d3fd04f1 100644
--- a/src/ristretto/ristretto_com_and_pub_sig.rs
+++ b/src/ristretto/ristretto_com_and_pub_sig.rs
@@ -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);
/// ```
///
@@ -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);
@@ -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::::digest(b"Maskerade"); // In real life, this should be strong Fiat-Shamir!
+/// let e = Blake2b::::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);
@@ -72,8 +81,8 @@ pub type RistrettoComAndPubSig = CommitmentAndPublicKeySignature::new()
+ // Challenge; doesn't use domain-separated Fiat-Shamir, so it's for testing only!
+ let challenge = Blake2b::::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;
@@ -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::::digest(b"Guards! Guards!");
+ let evil_challenge = Blake2b::::digest(b"Guards! Guards!");
assert!(!sig.verify_challenge(&commitment, &pubkey, &evil_challenge, &factory, &mut rng));
}
@@ -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::::new()
+ let challenge = Blake2b::::new()
.chain_update(commitment.as_bytes())
.chain_update(pubkey.as_bytes())
.chain_update(ephemeral_commitment.as_bytes())
@@ -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::::digest(b"Test challenge");
+ let challenge = Blake2b::::digest(b"Test challenge");
// Alice's signature
let sig_alice = RistrettoComAndPubSig::sign(
@@ -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();
diff --git a/src/ristretto/ristretto_com_sig.rs b/src/ristretto/ristretto_com_sig.rs
index 6e81fc73..4d790b38 100644
--- a/src/ristretto/ristretto_com_sig.rs
+++ b/src/ristretto/ristretto_com_sig.rs
@@ -13,7 +13,7 @@ use crate::{
///
/// ## Examples
///
-/// You can create a `RistrettoComSig` from it's component parts:
+/// You can create a `RistrettoComSig` from its component parts:
///
/// ```edition2018
/// # use tari_crypto::ristretto::*;
@@ -26,8 +26,14 @@ use crate::{
/// "8063d85e151abee630e643e2b3dc47bfaeb8aa859c9d10d60847985f286aad19",
/// )
/// .unwrap();
-/// let u = RistrettoSecretKey::from_bytes(b"10000000000000000000000010000000").unwrap();
-/// let v = RistrettoSecretKey::from_bytes(b"a00000000000000000000000a0000000").unwrap();
+/// let u = RistrettoSecretKey::from_hex(
+/// "a8fb609c5ab7cc07548b076b6c25cc3237c4526fb7a6dcb83b26f457b172c20a",
+/// )
+/// .unwrap();
+/// let v = RistrettoSecretKey::from_hex(
+/// "0e689df8ad4ad9d2fd5aaf8cb0a66d85cb0d4b7a380405514d453625813b0b0f",
+/// )
+/// .unwrap();
/// let sig = RistrettoComSig::new(r_pub, u, v);
/// ```
///
@@ -40,22 +46,20 @@ use crate::{
/// # use digest::Digest;
/// # use tari_crypto::commitment::HomomorphicCommitmentFactory;
/// # use tari_crypto::ristretto::pedersen::*;
+/// use blake2::Blake2b;
+/// use digest::consts::U64;
/// use tari_crypto::ristretto::pedersen::commitment_factory::PedersenCommitmentFactory;
/// use tari_utilities::hex::Hex;
-/// use blake2::Blake2b;
-/// use digest::consts::U32;
///
/// let mut rng = rand::thread_rng();
/// let a_val = RistrettoSecretKey::random(&mut rng);
/// let x_val = RistrettoSecretKey::random(&mut rng);
/// let a_nonce = RistrettoSecretKey::random(&mut rng);
/// let x_nonce = RistrettoSecretKey::random(&mut rng);
-/// let e = Blake2b::::digest(b"Maskerade");
+/// let e = Blake2b::::digest(b"Maskerade");
/// let factory = PedersenCommitmentFactory::default();
/// let commitment = factory.commit(&x_val, &a_val);
-/// // println!("commitment: {:?}", commitment.to_hex());
/// let sig = RistrettoComSig::sign(&a_val, &x_val, &a_nonce, &x_nonce, &e, &factory).unwrap();
-/// // println!("sig: R {:?} u {:?} v {:?}", sig.public_nonce().to_hex(), sig.u().to_hex(), sig.v().to_hex());
/// assert!(sig.verify_challenge(&commitment, &e, &factory));
/// ```
///
@@ -73,27 +77,27 @@ use crate::{
/// # use tari_utilities::ByteArray;
/// # use digest::Digest;
/// use blake2::Blake2b;
-/// use digest::consts::U32;
+/// use digest::consts::U64;
/// use tari_crypto::ristretto::pedersen::commitment_factory::PedersenCommitmentFactory;
///
/// let commitment = HomomorphicCommitment::from_hex(
-/// "167c6df11bf8106e89328c297e57423dc2a9be53df1ee63f6e50b4610104ab4a",
+/// "869b83416643258f1e03d028b5d0c652dc5b09decdae4a645fc5a43d87bd0a3e",
/// )
/// .unwrap();
/// let r_nonce = HomomorphicCommitment::from_hex(
-/// "4033e00996e61df2ea1abd1494b751b946663e21a20e2729c6592712beb15356",
+/// "665400676bdf8b07679629f703ea86e9cfc7e145f0768d2fdde4bd257009260d",
/// )
/// .unwrap();
/// let u = RistrettoSecretKey::from_hex(
-/// "f44bbc3374b172f77ffa8b904ddf0ad9f879b3e6183f9e440c57e7f01e851300",
+/// "f62fccf7734099d32937f7f767757abcb6eca70f43b3a7fb6500b2cb9ea12b02",
/// )
/// .unwrap();
/// let v = RistrettoSecretKey::from_hex(
-/// "fd54afb2d8008c8a3af10272b24161247b2b7ae11687813fe9fb03e34dd7f009",
+/// "cb9e34a7745cabaec0f9b2c3e217bf18fbe7ee8f4c83c1a523cead32ec9b4700",
/// )
/// .unwrap();
/// let sig = RistrettoComSig::new(r_nonce, u, v);
-/// let e = Blake2b::::digest(b"Maskerade");
+/// let e = Blake2b::::digest(b"Maskerade");
/// let factory = PedersenCommitmentFactory::default();
/// assert!(sig.verify_challenge(&commitment, &e, &factory));
/// ```
@@ -102,8 +106,8 @@ pub type RistrettoComSig = CommitmentSignature::new()
+ let challenge = Blake2b::::new()
.chain_update(commitment.as_bytes())
.chain_update(nonce_commitment.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();
let u_value = &k_1 + e_key.clone() * &x_value;
let v_value = &k_2 + e_key * &a_value;
let sig = RistrettoComSig::sign(&a_value, &x_value, &k_2, &k_1, &challenge, &factory).unwrap();
@@ -159,7 +163,7 @@ mod test {
// Doesn't work for invalid credentials
assert!(!sig.verify_challenge(&nonce_commitment, &challenge, &factory));
// Doesn't work for different challenge
- let wrong_challenge = Blake2b::::digest(b"Guards! Guards!");
+ let wrong_challenge = Blake2b::::digest(b"Guards! Guards!");
assert!(!sig.verify_challenge(&commitment, &wrong_challenge, &factory));
}
@@ -185,7 +189,7 @@ mod test {
let k_2_bob = RistrettoSecretKey::random(&mut rng);
let nonce_commitment_bob = factory.commit(&k_1_bob, &k_2_bob);
// Each of them creates the Challenge committing to both commitments of both parties
- let challenge = Blake2b::::new()
+ let challenge = Blake2b::::new()
.chain_update(commitment_alice.as_bytes())
.chain_update(commitment_bob.as_bytes())
.chain_update(nonce_commitment_alice.as_bytes())
@@ -212,20 +216,6 @@ mod test {
assert!(s_agg.verify_challenge(&combined_commitment, &challenge, &factory));
}
- /// 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 message = from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
- let k_1 = RistrettoSecretKey::random(&mut rng);
- let k_2 = RistrettoSecretKey::random(&mut rng);
- assert!(RistrettoComSig::sign(&a_value, &x_value, &k_2, &k_1, &message, &factory).is_ok());
- }
-
#[test]
fn to_vec() {
let sig = RistrettoComSig::default();
diff --git a/src/ristretto/ristretto_keys.rs b/src/ristretto/ristretto_keys.rs
index 3e97d293..d27b2bc9 100644
--- a/src/ristretto/ristretto_keys.rs
+++ b/src/ristretto/ristretto_keys.rs
@@ -22,7 +22,7 @@ use digest::{consts::U64, Digest};
use once_cell::sync::OnceCell;
use rand_core::{CryptoRng, RngCore};
use tari_utilities::{hex::Hex, ByteArray, ByteArrayError, Hashable};
-use zeroize::{Zeroize, ZeroizeOnDrop};
+use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use crate::{
errors::HashingError,
@@ -44,7 +44,7 @@ use crate::{
/// use tari_utilities::{hex::Hex, ByteArray};
///
/// let mut rng = rand::thread_rng();
-/// let _k1 = RistrettoSecretKey::from_bytes(&[
+/// let _k1 = RistrettoSecretKey::from_canonical_bytes(&[
/// 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/// 0, 0,
/// ]);
@@ -66,7 +66,7 @@ impl borsh::BorshDeserialize for RistrettoSecretKey {
fn deserialize_reader(reader: &mut R) -> Result
where R: borsh::maybestd::io::Read {
let mut bytes: Vec = borsh::BorshDeserialize::deserialize_reader(reader)?;
- match Self::from_bytes(bytes.as_slice())
+ match Self::from_canonical_bytes(bytes.as_slice())
.map_err(|e| borsh::maybestd::io::Error::new(borsh::maybestd::io::ErrorKind::InvalidInput, e.to_string()))
{
Ok(k) => {
@@ -84,29 +84,49 @@ impl borsh::BorshDeserialize for RistrettoSecretKey {
//----------------------------------------- Ristretto Secret Key ------------------------------------------------//
impl SecretKey for RistrettoSecretKey {
const KEY_LEN: usize = 32;
+ const WIDE_REDUCTION_LEN: usize = 64;
/// Return a random secret key on the `ristretto255` curve using the supplied CSPRNG.
fn random(rng: &mut R) -> Self {
RistrettoSecretKey(Scalar::random(rng))
}
+
+ /// Return a secret key computed from a uniform byte slice using wide reduction
+ /// If the byte array is not exactly 64 bytes, returns an error
+ fn from_uniform_bytes(bytes: &[u8]) -> Result {
+ if bytes.len() != Self::WIDE_REDUCTION_LEN {
+ return Err(ByteArrayError::IncorrectLength {});
+ }
+
+ let mut bytes_copied = Zeroizing::new([0u8; Self::WIDE_REDUCTION_LEN]);
+ bytes_copied.copy_from_slice(bytes);
+
+ Ok(RistrettoSecretKey(Scalar::from_bytes_mod_order_wide(&bytes_copied)))
+ }
}
//------------------------------------- Ristretto Secret Key ByteArray ---------------------------------------------//
impl ByteArray for RistrettoSecretKey {
- /// Create a secret key on the Ristretto255 curve using the given little-endian byte array. If the byte array is
- /// not exactly 32 bytes long, `from_bytes` returns an error. This function is guaranteed to return a valid key
- /// in the group since it performs a mod _l_ on the input.
- fn from_bytes(bytes: &[u8]) -> Result
+ /// Return a secret key computed from a canonical byte array
+ /// If the byte array is not exactly 32 bytes, returns an error
+ /// If the byte array does not represent a canonical encoding, returns an error
+ fn from_canonical_bytes(bytes: &[u8]) -> Result
where Self: Sized {
- if bytes.len() != 32 {
+ if bytes.len() != Self::KEY_LEN {
return Err(ByteArrayError::IncorrectLength {});
}
- let mut a = [0u8; 32];
- a.copy_from_slice(bytes);
- let k = Scalar::from_bytes_mod_order(a);
- a.zeroize();
- Ok(RistrettoSecretKey(k))
+
+ let mut bytes_copied = [0u8; 32];
+ bytes_copied.copy_from_slice(bytes);
+ let scalar = Option::::from(Scalar::from_canonical_bytes(bytes_copied)).ok_or(
+ ByteArrayError::ConversionError {
+ reason: ("Invalid canonical scalar byte array".to_string()),
+ },
+ )?;
+ bytes_copied.zeroize();
+
+ Ok(RistrettoSecretKey(scalar))
}
/// Return the byte array for the secret key in little-endian order
@@ -249,7 +269,7 @@ impl<'a> Borrow for &'a RistrettoSecretKey {
/// use tari_utilities::{hex::Hex, ByteArray};
///
/// let mut rng = rand::thread_rng();
-/// let _p1 = RistrettoPublicKey::from_bytes(&[
+/// let _p1 = RistrettoPublicKey::from_canonical_bytes(&[
/// 224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, 144, 33,
/// 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78,
/// ]);
@@ -277,7 +297,7 @@ impl borsh::BorshDeserialize for RistrettoPublicKey {
fn deserialize_reader(reader: &mut R) -> Result
where R: borsh::maybestd::io::Read {
let bytes: Vec = borsh::BorshDeserialize::deserialize_reader(reader)?;
- Self::from_bytes(bytes.as_slice())
+ Self::from_canonical_bytes(bytes.as_slice())
.map_err(|e| borsh::maybestd::io::Error::new(borsh::maybestd::io::ErrorKind::InvalidInput, e.to_string()))
}
}
@@ -488,7 +508,7 @@ impl ByteArray for RistrettoPublicKey {
/// the following circumstances:
/// * The byte array is not exactly 32 bytes
/// * The byte array does not represent a valid (compressed) point on the ristretto255 curve
- fn from_bytes(bytes: &[u8]) -> Result
+ fn from_canonical_bytes(bytes: &[u8]) -> Result
where Self: Sized {
// Check the length here, because The Ristretto constructor panics rather than returning an error
if bytes.len() != 32 {
@@ -602,7 +622,8 @@ impl From for CompressedRistretto {
#[cfg(test)]
mod test {
- use digest::consts::U32;
+ use blake2::Blake2b;
+ use digest::consts::{U32, U64};
use tari_utilities::ByteArray;
use super::*;
@@ -633,7 +654,7 @@ mod test {
#[test]
fn invalid_secret_key_bytes() {
- RistrettoSecretKey::from_bytes(&[1, 2, 3]).expect_err("Secret keys should be 32 bytes");
+ RistrettoSecretKey::from_canonical_bytes(&[1, 2, 3]).expect_err("Secret keys should be 32 bytes");
}
#[test]
@@ -663,7 +684,7 @@ mod test {
for i in 0u8..16 {
let pk = RistrettoPublicKey::from_hex(encodings_of_small_multiples[i as usize]).unwrap();
bytes[0] = i;
- let sk = RistrettoSecretKey::from_bytes(&bytes).unwrap();
+ let sk = RistrettoSecretKey::from_canonical_bytes(&bytes).unwrap();
let pk2 = RistrettoPublicKey::from_secret_key(&sk);
assert_eq!(pk, pk2);
}
@@ -971,35 +992,38 @@ mod test {
}
#[test]
- fn kdf_key_too_short() {
- let err = RistrettoKdf::generate::>(b"this_key_is_too_short", b"data", "test").err();
+ fn kdf_too_short() {
+ let err = RistrettoKdf::generate::>(b"this_hasher_is_too_short", b"data", "test").err();
+ assert!(matches!(err, Some(HashingError::InputTooShort {})));
+
+ let err = RistrettoKdf::generate::>(b"this_key_is_too_short", b"data", "test").err();
assert!(matches!(err, Some(HashingError::InputTooShort {})));
}
#[test]
fn kdf_test() {
let key =
- RistrettoSecretKey::from_hex("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c").unwrap();
- let derived1 = RistrettoKdf::generate::>(key.as_bytes(), b"derived1", "test").unwrap();
- let derived2 = RistrettoKdf::generate::>(key.as_bytes(), b"derived2", "test").unwrap();
+ RistrettoSecretKey::from_hex("45c5b950e04167785ff735bead8d746740db04bce3ee2c1f6523bdc59023e50e").unwrap();
+ let derived1 = RistrettoKdf::generate::>(key.as_bytes(), b"derived1", "test").unwrap();
+ let derived2 = RistrettoKdf::generate::>(key.as_bytes(), b"derived2", "test").unwrap();
assert_eq!(
derived1.to_hex(),
- "e8df6fa40344c1fde721e9a35d46daadb48dc66f7901a9795ebb0374474ea601"
+ "22deb0c38ec2dc9f741912f6e3c2cd3f76a5b33142a289da15eecdcd882bda06"
);
assert_eq!(
derived2.to_hex(),
- "3ae035e2663d9c561300cca67743ccdb56ea07ca7dacd8394356c4354b030e0c"
+ "fcca9ca5c993d817581ab6040d92feef4c78529b5a485b51d0e11af427944207"
);
}
#[test]
fn visibility_test() {
let key =
- RistrettoSecretKey::from_hex("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c").unwrap();
+ RistrettoSecretKey::from_hex("cd4f563f5e53a67ade2400b281e75f4b306253501da6529338e7be4f2b5c6200").unwrap();
let invisible = format!("{key:?}");
- assert!(!invisible.contains("016c"));
+ assert!(invisible.contains("***"));
let visible = format!("{:?}", key.reveal());
- assert!(visible.contains("016c"));
+ assert!(visible.contains("cd4f563f5e53a67ade2400b281e75f4b306253501da6529338e7be4f2b5c6200"));
}
#[test]
diff --git a/src/ristretto/ristretto_sig.rs b/src/ristretto/ristretto_sig.rs
index 9e1c152e..055821b4 100644
--- a/src/ristretto/ristretto_sig.rs
+++ b/src/ristretto/ristretto_sig.rs
@@ -18,7 +18,7 @@ use crate::{
///
/// ## Creating signatures
///
-/// You can create a `RisrettoSchnorr` from it's component parts:
+/// You can create a `RisrettoSchnorr` from its component parts:
///
/// ```edition2018
/// # use tari_crypto::ristretto::*;
@@ -31,7 +31,10 @@ use crate::{
/// "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
/// )
/// .unwrap();
-/// let s = RistrettoSecretKey::from_bytes(b"10000000000000000000000000000000").unwrap();
+/// let s = RistrettoSecretKey::from_hex(
+/// "f62fccf7734099d32937f7f767757abcb6eca70f43b3a7fb6500b2cb9ea12b02",
+/// )
+/// .unwrap();
/// let sig = RistrettoSchnorr::new(public_r, s);
/// ```
///
@@ -55,7 +58,7 @@ use crate::{
/// let (k, P) = get_keypair();
/// let msg = "Small Gods";
/// let mut rng = thread_rng();
-/// let sig = RistrettoSchnorr::sign_message(&k, &msg, &mut rng);
+/// let sig = RistrettoSchnorr::sign(&k, &msg, &mut rng);
/// ```
///
/// # Verifying signatures
@@ -81,8 +84,8 @@ use crate::{
/// let P = RistrettoPublicKey::from_secret_key(&k);
/// let mut rng = thread_rng();
/// let sig: SchnorrSignature =
-/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap();
-/// assert!(sig.verify_message(&P, msg));
+/// SchnorrSignature::sign(&k, msg, &mut rng).unwrap();
+/// assert!(sig.verify(&P, msg));
/// ```
pub type RistrettoSchnorr = SchnorrSignature;
@@ -113,17 +116,17 @@ pub type RistrettoSchnorr = SchnorrSignature =
-/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap();
-/// assert!(sig.verify_message(&P, msg));
+/// SchnorrSignature::sign(&k, msg, &mut rng).unwrap();
+/// assert!(sig.verify(&P, msg));
/// ```
pub type RistrettoSchnorrWithDomain = SchnorrSignature;
#[cfg(test)]
mod test {
use blake2::Blake2b;
- use digest::{consts::U32, Digest};
+ use digest::{consts::U64, Digest};
use tari_utilities::{
- hex::{from_hex, to_hex, Hex},
+ hex::{to_hex, Hex},
ByteArray,
};
@@ -154,23 +157,23 @@ mod test {
let (k, P) = RistrettoPublicKey::random_keypair(&mut rng);
let (r, R) = RistrettoPublicKey::random_keypair(&mut rng);
// Use sign raw, and bind the nonce and public key manually
- let e = Blake2b::::new()
+ let e = Blake2b::::new()
.chain_update(P.as_bytes())
.chain_update(R.as_bytes())
.chain_update(b"Small Gods")
.finalize();
- let e_key = RistrettoSecretKey::from_bytes(&e).unwrap();
+ let e_key = RistrettoSecretKey::from_uniform_bytes(&e).unwrap();
let s = &r + &e_key * &k;
- let sig = RistrettoSchnorr::sign_raw(&k, r, &e).unwrap();
+ let sig = RistrettoSchnorr::sign_raw_uniform(&k, r, &e).unwrap();
let R_calc = sig.get_public_nonce();
assert_eq!(R, *R_calc);
assert_eq!(sig.get_signature(), &s);
- assert!(sig.verify_challenge(&P, &e));
+ assert!(sig.verify_raw_uniform(&P, &e));
// Doesn't work for invalid credentials
- assert!(!sig.verify_challenge(&R, &e));
+ assert!(!sig.verify_raw_uniform(&R, &e));
// Doesn't work for different challenge
- let wrong_challenge = Blake2b::::digest(b"Guards! Guards!");
- assert!(!sig.verify_challenge(&P, &wrong_challenge));
+ let wrong_challenge = Blake2b::::digest(b"Guards! Guards!");
+ assert!(!sig.verify_raw_uniform(&P, &wrong_challenge));
}
/// This test checks that the linearity of Schnorr signatures hold, i.e. that s = s1 + s2 is validated by R1 + R2
@@ -185,7 +188,7 @@ mod test {
let (k2, P2) = RistrettoPublicKey::random_keypair(&mut rng);
let (r2, R2) = RistrettoPublicKey::random_keypair(&mut rng);
// Each of them creates the Challenge = H(R1 || R2 || P1 || P2 || m)
- let e = Blake2b::::new()
+ let e = Blake2b::::new()
.chain_update(R1.as_bytes())
.chain_update(R2.as_bytes())
.chain_update(P1.as_bytes())
@@ -193,25 +196,13 @@ mod test {
.chain_update(b"Moving Pictures")
.finalize();
// Calculate Alice's signature
- let s1 = RistrettoSchnorr::sign_raw(&k1, r1, &e).unwrap();
+ let s1 = RistrettoSchnorr::sign_raw_uniform(&k1, r1, &e).unwrap();
// Calculate Bob's signature
- let s2 = RistrettoSchnorr::sign_raw(&k2, r2, &e).unwrap();
+ let s2 = RistrettoSchnorr::sign_raw_uniform(&k2, r2, &e).unwrap();
// Now add the two signatures together
let s_agg = &s1 + &s2;
// Check that the multi-sig verifies
- assert!(s_agg.verify_challenge(&(P1 + P2), &e));
- }
-
- /// 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]
- #[allow(non_snake_case)]
- fn challenge_from_invalid_scalar() {
- let mut rng = rand::thread_rng();
- let m = from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
- let k = RistrettoSecretKey::random(&mut rng);
- let r = RistrettoSecretKey::random(&mut rng);
- assert!(RistrettoSchnorr::sign_raw(&k, r, &m).is_ok());
+ assert!(s_agg.verify_raw_uniform(&(P1 + P2), &e));
}
#[test]
@@ -222,10 +213,10 @@ mod test {
let R =
RistrettoPublicKey::from_hex("fa14cb581ce5717248444721242e6b195a482d503a853dea4acb513074d8d803").unwrap();
let msg = "Moving Pictures";
- let hash = SchnorrSignature::<_, _, SchnorrSigChallenge>::construct_domain_separated_challenge::<_, Blake2b>(
+ let hash = SchnorrSignature::<_, _, SchnorrSigChallenge>::construct_domain_separated_challenge::<_, Blake2b>(
&R, &P, msg,
);
- let naiive = Blake2b::::new()
+ let naiive = Blake2b::::new()
.chain_update(R.as_bytes())
.chain_update(P.as_bytes())
.chain_update(msg)
@@ -234,7 +225,7 @@ mod test {
assert_ne!(hash.as_ref(), naiive.as_bytes());
assert_eq!(
to_hex(hash.as_ref()),
- "d8f6b29b641113c91175b8d44f265ff1167d58d5aa5ee03e6f1f521505b09d80"
+ "2db0656c9dd1482bf61d32f157726b05a88d567c31107bed9a5c60a02119518af35929f360726bffd846439ab12e7c9f4983cf5fab5ea735422e05e0f560ddfd"
);
}
@@ -256,8 +247,8 @@ mod test {
// assert_ne!(sig1, sig2);
// Prove that the nonces were reused. Again, NEVER do this
assert_eq!(sig1.get_public_nonce(), sig2.get_public_nonce());
- assert!(sig1.verify_message(&P, msg));
- assert!(sig2.verify_message(&P, msg));
+ assert!(sig1.verify(&P, msg));
+ assert!(sig2.verify(&P, msg));
// But the signatures are different, for the same message, secret and nonce.
assert_ne!(sig1.get_signature(), sig2.get_signature());
}
@@ -267,10 +258,9 @@ mod test {
fn sign_and_verify_message() {
let mut rng = rand::thread_rng();
let (k, P) = RistrettoPublicKey::random_keypair(&mut rng);
- let sig =
- RistrettoSchnorr::sign_message(&k, "Queues are things that happen to other people", &mut rng).unwrap();
- assert!(sig.verify_message(&P, "Queues are things that happen to other people"));
- assert!(!sig.verify_message(&P, "Qs are things that happen to other people"));
- assert!(!sig.verify_message(&(&P + &P), "Queues are things that happen to other people"));
+ let sig = RistrettoSchnorr::sign(&k, "Queues are things that happen to other people", &mut rng).unwrap();
+ assert!(sig.verify(&P, "Queues are things that happen to other people"));
+ assert!(!sig.verify(&P, "Qs are things that happen to other people"));
+ assert!(!sig.verify(&(&P + &P), "Queues are things that happen to other people"));
}
}
diff --git a/src/ristretto/serialize.rs b/src/ristretto/serialize.rs
index 6d7aafba..b213322b 100644
--- a/src/ristretto/serialize.rs
+++ b/src/ristretto/serialize.rs
@@ -51,7 +51,7 @@ impl<'de> Deserialize<'de> for RistrettoPublicKey {
fn visit_bytes(self, v: &[u8]) -> Result
where E: de::Error {
- RistrettoPublicKey::from_bytes(v).map_err(E::custom)
+ RistrettoPublicKey::from_canonical_bytes(v).map_err(E::custom)
}
}
@@ -89,7 +89,7 @@ impl<'de> Deserialize<'de> for RistrettoSecretKey {
fn visit_bytes(self, v: &[u8]) -> Result
where E: de::Error {
- RistrettoSecretKey::from_bytes(v).map_err(E::custom)
+ RistrettoSecretKey::from_canonical_bytes(v).map_err(E::custom)
}
}
diff --git a/src/ristretto/utils.rs b/src/ristretto/utils.rs
deleted file mode 100644
index dfc2684a..00000000
--- a/src/ristretto/utils.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019. The Tari Project
-// SPDX-License-Identifier: BSD-3-Clause
-
-//! Handy utility functions for use in tests and demo scripts
-
-use alloc::vec::Vec;
-
-use digest::Digest;
-use rand_core::{CryptoRng, RngCore};
-use tari_utilities::ByteArray;
-
-use crate::{
- keys::PublicKey,
- ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey},
- signatures::SchnorrSignatureError,
-};
-
-/// A set of keys and it's associated signature
-pub struct SignatureSet {
- /// The secret nonce
- pub nonce: RistrettoSecretKey,
- /// The public nonce
- pub public_nonce: RistrettoPublicKey,
- /// The message signed. Note that the [SignatureSet::public_nonce] is prepended to this message before signing
- pub message: Vec,
- /// The signature
- pub signature: RistrettoSchnorr,
-}
-
-/// Generate a random keypair and a signature for the provided message
-///
-/// # Panics
-///
-/// The function panics if it cannot generate a suitable signature
-#[deprecated(
- since = "0.16.0",
- note = "Use SchnorrSignature::sign_message instead. This method will be removed in v1.0.0"
-)]
-pub fn sign(
- private_key: &RistrettoSecretKey,
- message: &[u8],
- rng: &mut R,
-) -> Result {
- let (nonce, public_nonce) = RistrettoPublicKey::random_keypair(rng);
- let message = D::new()
- .chain_update(public_nonce.as_bytes())
- .chain_update(message)
- .finalize()
- .to_vec();
- let e = RistrettoSecretKey::from_bytes(&message).map_err(|_| SchnorrSignatureError::InvalidChallenge)?;
- let s = RistrettoSchnorr::sign_raw(private_key, nonce.clone(), e.as_bytes())?;
- Ok(SignatureSet {
- nonce,
- public_nonce,
- message,
- signature: s,
- })
-}
diff --git a/src/signatures/commitment_and_public_key_signature.rs b/src/signatures/commitment_and_public_key_signature.rs
index a02d6ccc..cf526d46 100644
--- a/src/signatures/commitment_and_public_key_signature.rs
+++ b/src/signatures/commitment_and_public_key_signature.rs
@@ -102,8 +102,8 @@ where
for<'a> &'a K: Mul<&'a K, Output = K>,
C: HomomorphicCommitmentFactory,
{
- // The challenge must be a valid scalar
- let e = match K::from_bytes(challenge) {
+ // The challenge is computed by wide reduction
+ let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return Err(CommitmentAndPublicKeySignatureError::InvalidChallenge),
};
@@ -148,8 +148,8 @@ where
C: HomomorphicCommitmentFactory
,
R: RngCore + CryptoRng,
{
- // The challenge must be a valid scalar
- let e = match K::from_bytes(challenge) {
+ // The challenge is computed by wide reduction
+ let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return false,
};
diff --git a/src/signatures/commitment_signature.rs b/src/signatures/commitment_signature.rs
index a114f425..401d665c 100644
--- a/src/signatures/commitment_signature.rs
+++ b/src/signatures/commitment_signature.rs
@@ -92,7 +92,7 @@ where
for<'a> &'a K: Mul<&'a K, Output = K>,
C: HomomorphicCommitmentFactory
,
{
- let e = match K::from_bytes(challenge) {
+ let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return Err(CommitmentSignatureError::InvalidChallenge),
};
@@ -120,7 +120,7 @@ where
for<'b> &'b HomomorphicCommitment
: Add<&'b HomomorphicCommitment
, Output = HomomorphicCommitment
>,
C: HomomorphicCommitmentFactory
,
{
- let e = match K::from_bytes(challenge) {
+ let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return false,
};
@@ -178,9 +178,9 @@ where
if buf.len() != P::KEY_LEN + 2 * K::key_length() {
return Err(ByteArrayError::IncorrectLength {});
}
- let public_nonce = HomomorphicCommitment::from_public_key(&P::from_bytes(&buf[0..P::KEY_LEN])?);
- let u = K::from_bytes(&buf[P::KEY_LEN..P::KEY_LEN + K::key_length()])?;
- let v = K::from_bytes(&buf[P::KEY_LEN + K::key_length()..P::KEY_LEN + 2 * K::key_length()])?;
+ let public_nonce = HomomorphicCommitment::from_public_key(&P::from_canonical_bytes(&buf[0..P::KEY_LEN])?);
+ let u = K::from_canonical_bytes(&buf[P::KEY_LEN..P::KEY_LEN + K::key_length()])?;
+ let v = K::from_canonical_bytes(&buf[P::KEY_LEN + K::key_length()..P::KEY_LEN + 2 * K::key_length()])?;
Ok(Self { public_nonce, u, v })
}
diff --git a/src/signatures/schnorr.rs b/src/signatures/schnorr.rs
index e9447a14..535872ef 100644
--- a/src/signatures/schnorr.rs
+++ b/src/signatures/schnorr.rs
@@ -13,7 +13,7 @@ use core::{
};
use blake2::Blake2b;
-use digest::{consts::U32, Digest};
+use digest::{consts::U64, Digest};
use rand_core::{CryptoRng, RngCore};
use snafu::prelude::*;
use tari_utilities::ByteArray;
@@ -73,40 +73,43 @@ where
P::from_secret_key(&self.signature)
}
- /// Sign a challenge with the given `secret` and private `nonce`. Returns an SchnorrSignatureError if `::from_bytes(challenge)` returns an error.
+ /// Generate a signature using a given secret key, nonce, and challenge byte slice.
///
- /// WARNING: The public key and nonce are NOT bound to the challenge. This method assumes that the challenge has
- /// been constructed such that all commitments are already included in the challenge.
+ /// WARNING: This is intended for use cases where the challenge byte slice was generated correctly.
+ /// In particlar, it _must_ be the result of securely applying a cryptographic hash function to the correct public
+ /// key, public nonce, and input message; further, it must be of a length suitable for scalar wide reduction.
+ /// This function only checks that the byte slice is of the correct length.
+ /// The nonce _must_ also have been sampled uniformly at random and not reused with the same secret key and a
+ /// different message.
///
- /// Use [`sign_raw`] instead if this is what you want. (This method is a deprecated alias for `sign_raw`).
- ///
- /// If you want a simple API that binds the nonce and public key to the message, use [`sign_message`] instead.
- #[deprecated(
- since = "0.16.0",
- note = "This method probably doesn't do what you think it does. Please use `sign_message` or `sign_raw` \
- instead, depending on your use case. This function will be removed in v1.0.0"
- )]
- #[allow(clippy::needless_pass_by_value)]
- pub fn sign(secret: K, nonce: K, challenge: &[u8]) -> Result
- where
- K: Add