diff --git a/.github/workflows/clippy-check.yml b/.github/workflows/clippy-check.yml index a1135adc..d1b963ca 100644 --- a/.github/workflows/clippy-check.yml +++ b/.github/workflows/clippy-check.yml @@ -13,6 +13,13 @@ jobs: components: clippy, rustfmt toolchain: nightly override: true + - name: Toolchain thumbv8m.main-none-eabi + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + target: thumbv8m.main-none-eabi + override: true - name: Check formatting uses: actions-rs/cargo@v1 with: @@ -35,3 +42,16 @@ jobs: 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 + # 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 + diff --git a/Cargo.toml b/Cargo.toml index 1eca71b6..baaddf7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,35 +11,38 @@ version = "0.17.0" edition = "2018" [dependencies] -tari_utilities = { version = "0.5", features = ["zero", "std"] } -blake2 = { version = "0.10" } -borsh = { version = "0.10" , optional = true } -bulletproofs_plus = { package = "tari_bulletproofs_plus", version = "0.3" } -curve25519-dalek = { package = "tari-curve25519-dalek", version = "4.0.3", default-features = false, features = ["serde", "alloc", "rand_core", "precomputed-tables"] } -digest = { version = "0.10" } -lazy_static = { version = "1.3" } -log = { version = "0.4" } -once_cell = { version = "1.8" } -rand_chacha = { version = "0.3" } -rand_core = { version = "0.6" } -serde = { version = "1.0" } -sha3 = { version = "0.10" } -thiserror = { version = "1.0" } -zeroize = {version = "1" } -rand = { version = "0.8" } +tari_utilities = { version = "0.5", 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 } +curve25519-dalek = { package = "tari-curve25519-dalek", version = "4.0.3", default-features = false, features = [ "alloc", "rand_core", "precomputed-tables", "zeroize"] } +digest = { version = "0.10", default-features = false } +log = { version = "0.4" , default-features = false} +once_cell = { version = "1.8", default-features = false, features = ["critical-section"] } +rand_chacha = { version = "0.3", default-features = false } +rand_core = { version = "0.6" , default-features = false} +serde = { version = "1.0", optional = true } +sha3 = { version = "0.10", default-features = false } +snafu = { version = "0.7", default-features = false} +zeroize = {version = "1" , default-features = false} [dev-dependencies] +tari_utilities = { version = "0.5", features = ["std"] } +serde = { version = "1.0"} bincode = { version = "1.1" } criterion = { version = "0.5", default-features = false } sha2 = { version = "0.10" } +rand = { version = "0.8" } [features] +default = ["bulletproofs_plus", "serde", "precomputed_tables", "borsh"] +precomputed_tables = [] [lib] # Disable benchmarks to allow Criterion to take over bench = false -crate-type = ["lib", "cdylib", "staticlib"] +crate-type = ["lib", "cdylib"] [[bench]] name = "benches" diff --git a/README.md b/README.md index 24f2765a..d01bf378 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,26 @@ Major features of this library include: - Pedersen commitments - Schnorr Signatures - Generic Public and Secret Keys +- no-std support The `tari_crypto` crate makes heavy use of the excellent [Dalek](https://github.com/dalek-cryptography/curve25519-dalek) libraries. The default implementation for Tari ECC is the [Ristretto255 curve](https://ristretto.group). + +# Feature flags +### bulletproofs_plus +This adds in support for rangeproofs using the tari bulletproof plus library +### serde +This adds serialise and deserialize support for all structs using the serde library +### borsh +This adds serialise and deserialize support for all structs using the borsh library +### precomputed_tables +This uses optimised precomputed tables for calculations. While this is faster than straight-up calculations, this requires large memory to store which is not ideal for small no_std devices + +# WASM and FFI support +TariCrypto has external WASM and FFI wrappers available here +WASM: https://github.com/tari-project/tari-crypto-wasm +FFI: https://github.com/tari-project/tari-crypto-ffi + # Benchmarks To run the benchmarks: diff --git a/benches/signatures.rs b/benches/signatures.rs index e8c4755f..224bd554 100644 --- a/benches/signatures.rs +++ b/benches/signatures.rs @@ -5,6 +5,7 @@ use std::time::Duration; use criterion::{criterion_group, BatchSize, Criterion}; use rand::{thread_rng, RngCore}; +use rand_core::OsRng; use tari_crypto::{ keys::{PublicKey, SecretKey}, ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, @@ -45,7 +46,7 @@ fn sign_message(c: &mut Criterion) { b.iter_batched( gen_keypair, |d| { - let _sig = RistrettoSchnorr::sign_message(&d.k, d.m).unwrap(); + let _sig = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap(); }, BatchSize::SmallInput, ); @@ -59,7 +60,7 @@ fn verify_message(c: &mut Criterion) { b.iter_batched( || { let d = gen_keypair(); - let s = RistrettoSchnorr::sign_message(&d.k, d.m).unwrap(); + let s = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap(); (d, s) }, |(d, s)| assert!(s.verify_message(&d.p, d.m)), diff --git a/src/commitment.rs b/src/commitment.rs index faa8c13b..84561da0 100644 --- a/src/commitment.rs +++ b/src/commitment.rs @@ -6,17 +6,17 @@ //! envelope and reveal its contents. Also it's a special envelope that can only be opened by a special opener that //! you keep safe in your drawer. -use std::{ +use core::{ cmp::Ordering, convert::TryFrom, hash::{Hash, Hasher}, ops::{Add, Mul, Sub}, }; -use serde::{Deserialize, Serialize}; use tari_utilities::{ByteArray, ByteArrayError}; use crate::{ + alloc::string::ToString, errors::CommitmentError, keys::{PublicKey, SecretKey}, }; @@ -32,20 +32,21 @@ use crate::{ /// C_2 &= v_2.H + k_2.G \\\\ /// \therefore C_1 + C_2 &= (v_1 + v_2)H + (k_1 + k_2)G /// \end{aligned} $$ -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct HomomorphicCommitment

(pub(crate) P); #[cfg(feature = "borsh")] impl borsh::BorshDeserialize for HomomorphicCommitment

{ - fn deserialize_reader(reader: &mut R) -> Result - where R: std::io::Read { + fn deserialize_reader(reader: &mut R) -> Result + where R: borsh::maybestd::io::Read { Ok(Self(P::deserialize_reader(reader)?)) } } #[cfg(feature = "borsh")] impl borsh::BorshSerialize for HomomorphicCommitment

{ - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { self.0.serialize(writer) } } @@ -251,9 +252,9 @@ impl ExtensionDegree { 4 => Ok(ExtensionDegree::AddThreeBasePoints), 5 => Ok(ExtensionDegree::AddFourBasePoints), 6 => Ok(ExtensionDegree::AddFiveBasePoints), - _ => Err(CommitmentError::ExtensionDegree( - "Extension degree not valid".to_string(), - )), + _ => Err(CommitmentError::CommitmentExtensionDegree { + reason: "Extension degree not valid".to_string(), + }), } } } diff --git a/src/deterministic_randomizer.rs b/src/deterministic_randomizer.rs index 94f6ab7e..be56e46b 100644 --- a/src/deterministic_randomizer.rs +++ b/src/deterministic_randomizer.rs @@ -4,7 +4,8 @@ //! A deterministic randomizer with utility functions for operating on numbers and arrays in a reproducible and //! platform-indepdent way. -use std::convert::TryFrom; +use alloc::vec::Vec; +use core::convert::TryFrom; use rand_core::{CryptoRng, RngCore, SeedableRng}; diff --git a/src/dhke.rs b/src/dhke.rs index 06450f15..ea409ba0 100644 --- a/src/dhke.rs +++ b/src/dhke.rs @@ -9,7 +9,7 @@ //! clone the byte array without a very good reason. If you need the underlying public key itself, you probably should //! be using something else. -use std::ops::Mul; +use core::ops::Mul; use zeroize::Zeroize; diff --git a/src/errors.rs b/src/errors.rs index 6a0b440a..db0e623c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,65 +3,90 @@ //! Errors used in the Tari Crypto crate -use serde::{Deserialize, Serialize}; -use tari_utilities::ByteArrayError; -use thiserror::Error; +use alloc::string::String; +use snafu::prelude::*; /// Errors encountered when creating of verifying range proofs -#[derive(Debug, Clone, Error, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Debug, Clone, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum RangeProofError { /// Cold not construct a range proof - #[error("Could not construct range proof: `{0}`")] - ProofConstructionError(String), + #[snafu(display("Could not construct range proof: `{reason}'"))] + ProofConstructionError { + /// The reason for the error + reason: String, + }, /// The deserialization of the range proof failed - #[error("The deserialization of the range proof failed")] - InvalidProof, + #[snafu(display("The deserialization of the range proof failed"))] + InvalidProof {}, /// Invalid input was provided to the RangeProofService constructor - #[error("Invalid input was provided to the RangeProofService constructor: `{0}`")] - InitializationError(String), + #[snafu(display("Invalid input was provided to the RangeProofService constructor: `{reason}'"))] + InitializationError { + /// The reason for the error + reason: String, + }, /// Invalid range proof provided - #[error("Invalid range proof provided: `{0}`")] - InvalidRangeProof(String), + #[snafu(display("Invalid range proof provided: `{reason}"))] + InvalidRangeProof { + /// The reason for the error + reason: String, + }, /// Invalid range proof rewind, the rewind keys provided must be invalid - #[error("Invalid range proof rewind, the rewind keys provided must be invalid")] - InvalidRewind(String), + #[snafu(display("Invalid range proof rewind, the rewind keys provided must be invalid: `{reason}'"))] + InvalidRewind { + /// The reason for the error + reason: String, + }, /// Inconsistent extension degree - #[error("Inconsistent extension degree: `{0}`")] - ExtensionDegree(String), + #[snafu(display("Inconsistent extension degree: `{reason}'"))] + RPExtensionDegree { + /// The reason for the error + reason: String, + }, } /// Errors encountered when committing values -#[derive(Debug, Clone, Error, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Debug, Clone, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum CommitmentError { /// Inconsistent extension degree - #[error("Inconsistent extension degree: `{0}`")] - ExtensionDegree(String), + #[snafu(display("Inconsistent extension degree: `{reason}'"))] + CommitmentExtensionDegree { + /// The reason for the error + reason: String, + }, } /// Errors encountered when hashing -#[derive(Debug, Error, PartialEq, Eq)] +#[derive(Debug, Snafu, PartialEq, Eq)] pub enum HashingError { /// The input to the hashing function is too short - #[error("The input to the hashing function is too short.")] - InputTooShort, + #[snafu(display("The input to the hashing function is too short."))] + InputTooShort {}, /// Converting a byte string into a secret key failed - #[error("Converting a byte string into a secret key failed: {0}")] - ConversionFromBytes(String), + #[snafu(display("Converting a byte string into a secret key failed. `{reason}'"))] + ConversionFromBytes { + /// The reason for the error + reason: String, + }, /// The digest does not produce enough output - #[error("The digest does produce enough output. {0} bytes are required.")] - DigestTooShort(usize), -} - -impl From for HashingError { - fn from(byte_error: ByteArrayError) -> Self { - HashingError::ConversionFromBytes(byte_error.to_string()) - } + #[snafu(display("The digest does produce enough output.`{bytes}' bytes are required."))] + DigestTooShort { + /// The number of bytes required + bytes: usize, + }, } /// Errors encountered when copying to a buffer -#[derive(Debug, Clone, Error, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SliceError { /// The requested fixed slice length exceeds the available slice length - #[error("Cannot create fixed slice of length {0} from a slice of length {1}.")] - CopyFromSlice(usize, usize), + #[snafu(display("Cannot create fixed slice of length '{target}' from a slice of length '{provided}'"))] + CopyFromSlice { + /// The requested fixed slice length + target: usize, + /// The available slice length + provided: usize, + }, } diff --git a/src/extended_range_proof.rs b/src/extended_range_proof.rs index f8c17887..7850dc25 100644 --- a/src/extended_range_proof.rs +++ b/src/extended_range_proof.rs @@ -3,6 +3,8 @@ //! Extended range proofs +use std::{string::ToString, vec::Vec}; + use crate::{ commitment::{ExtensionDegree, HomomorphicCommitment}, errors::RangeProofError, @@ -110,9 +112,9 @@ where K: SecretKey /// Construct a new extended mask pub fn assign(extension_degree: ExtensionDegree, secrets: Vec) -> Result, RangeProofError> { if secrets.is_empty() || secrets.len() != extension_degree as usize { - Err(RangeProofError::InitializationError( - "Extended mask length must correspond to the extension degree".to_string(), - )) + Err(RangeProofError::InitializationError { + reason: "Extended mask length must correspond to the extension degree".to_string(), + }) } else { Ok(Self { secrets }) } @@ -152,9 +154,9 @@ where PK: PublicKey /// - `statements` must be a power of 2 as mandated by the `bulletproofs_plus` implementation pub fn init(statements: Vec>) -> Result { if !statements.len().is_power_of_two() { - return Err(RangeProofError::InitializationError( - "Number of commitments must be a power of two".to_string(), - )); + return Err(RangeProofError::InitializationError { + reason: "Number of commitments must be a power of two".to_string(), + }); } Ok(Self { statements }) } @@ -180,14 +182,14 @@ where PK: PublicKey /// - mask recovery is not supported with an aggregated statement/proof pub fn init(statements: Vec>, recovery_seed_nonce: Option) -> Result { if recovery_seed_nonce.is_some() && statements.len() > 1 { - return Err(RangeProofError::InitializationError( - "Mask recovery is not supported with an aggregated statement".to_string(), - )); + return Err(RangeProofError::InitializationError { + reason: "Mask recovery is not supported with an aggregated statement".to_string(), + }); } if !statements.len().is_power_of_two() { - return Err(RangeProofError::InitializationError( - "Number of commitments must be a power of two".to_string(), - )); + return Err(RangeProofError::InitializationError { + reason: "Number of commitments must be a power of two".to_string(), + }); } Ok(Self { statements, diff --git a/src/hashing.rs b/src/hashing.rs index 903f9b0a..be1b29b5 100644 --- a/src/hashing.rs +++ b/src/hashing.rs @@ -28,7 +28,8 @@ //! //! [hmac]: https://en.wikipedia.org/wiki/HMAC#Design_principles "HMAC: Design principles" -use std::{marker::PhantomData, ops::Deref}; +use alloc::string::String; +use core::{marker::PhantomData, ops::Deref}; use blake2::{Blake2b, Blake2bVar}; use digest::{consts::U32, Digest, FixedOutput, FixedOutputReset, Output, OutputSizeUser, Update}; @@ -36,6 +37,7 @@ use sha3::Sha3_256; use tari_utilities::ByteArray; use crate::{ + alloc::string::ToString, errors::{HashingError, SliceError}, keys::SecretKey, }; @@ -305,7 +307,10 @@ pub trait AsFixedBytes: AsRef<[u8]> { let hash_vec = self.as_ref(); if hash_vec.is_empty() || hash_vec.len() < I { let hash_vec_length = if hash_vec.is_empty() { 0 } else { hash_vec.len() }; - return Err(SliceError::CopyFromSlice(I, hash_vec_length)); + return Err(SliceError::CopyFromSlice { + target: I, + provided: hash_vec_length, + }); } let mut buffer: [u8; I] = [0; I]; buffer.copy_from_slice(&hash_vec[..I]); @@ -567,7 +572,8 @@ pub trait DerivedKeyDomain: DomainSeparation { .chain(primary_key) .chain(data) .finalize(); - let derived_key = Self::DerivedKeyType::from_bytes(hash.as_ref())?; + let derived_key = Self::DerivedKeyType::from_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 6c4ef25d..14f8e250 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -6,10 +6,9 @@ //! implementation of ECC curve). The idea being that we can swap out the underlying //! implementation without worrying too much about the impact on upstream code. -use std::ops::Add; +use core::ops::Add; -use rand::{CryptoRng, Rng}; -use serde::{de::DeserializeOwned, ser::Serialize}; +use rand_core::{CryptoRng, RngCore}; use tari_utilities::ByteArray; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -39,7 +38,7 @@ pub trait SecretKey: } /// Generates a random secret key - fn random(rng: &mut R) -> Self; + fn random(rng: &mut R) -> Self; } //---------------------------------------- Public Keys ----------------------------------------// @@ -48,9 +47,7 @@ pub trait SecretKey: /// implementations need to implement this trait for them to be used in Tari. /// /// See [SecretKey](trait.SecretKey.html) for an example. -pub trait PublicKey: - ByteArray + Add + Clone + PartialOrd + Ord + Default + Serialize + DeserializeOwned + Zeroize -{ +pub trait PublicKey: ByteArray + Add + Clone + PartialOrd + Ord + Default + Zeroize { /// The length of the byte encoding of a key, in bytes const KEY_LEN: usize; @@ -71,7 +68,7 @@ pub trait PublicKey: fn batch_mul(scalars: &[Self::K], points: &[Self]) -> Self; /// Generate a random public and secret key - fn random_keypair(rng: &mut R) -> (Self::K, Self) { + fn random_keypair(rng: &mut R) -> (Self::K, Self) { let k = Self::K::random(rng); let pk = Self::from_secret_key(&k); (k, pk) diff --git a/src/lib.rs b/src/lib.rs index 7cc5862e..7f524aa7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,15 @@ // SPDX-License-Identifier: BSD-3-Clause //! Tari-Crypto +#![no_std] +#[allow(unused_imports)] #[macro_use] -extern crate lazy_static; +extern crate alloc; + +#[cfg(any(feature = "bulletproofs_plus", test))] +#[macro_use] +extern crate std; #[macro_use] mod macros; @@ -13,7 +19,9 @@ pub mod deterministic_randomizer; pub mod dhke; pub mod hashing; pub mod keys; +#[cfg(feature = "bulletproofs_plus")] pub mod range_proof; +#[cfg(feature = "bulletproofs_plus")] pub mod rewindable_range_proof; pub mod signatures; @@ -22,6 +30,7 @@ pub mod signatures; pub mod ristretto; pub mod errors; +#[cfg(feature = "bulletproofs_plus")] pub mod extended_range_proof; // Re-export tari_utils diff --git a/src/ristretto/bulletproofs_plus.rs b/src/ristretto/bulletproofs_plus.rs index 7c5be256..48e6f96b 100644 --- a/src/ristretto/bulletproofs_plus.rs +++ b/src/ristretto/bulletproofs_plus.rs @@ -3,6 +3,7 @@ //! Bulletproofs+ implementation +use alloc::vec::Vec; use std::convert::TryFrom; pub use bulletproofs_plus::ristretto::RistrettoRangeProof; @@ -20,6 +21,7 @@ use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar}; use log::*; use crate::{ + alloc::string::ToString, commitment::{ExtensionDegree as CommitmentExtensionDegree, HomomorphicCommitment}, errors::RangeProofError, extended_range_proof, @@ -73,10 +75,10 @@ impl TryFrom<&BulletproofsExtendedMask> for RistrettoExtendedMask { fn try_from(extended_mask: &BulletproofsExtendedMask) -> Result { let secrets = extended_mask .blindings() - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string()))?; + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() })?; RistrettoExtendedMask::assign( CommitmentExtensionDegree::try_from_size(secrets.len()) - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string()))?, + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() })?, secrets.iter().map(|k| RistrettoSecretKey(*k)).collect(), ) } @@ -87,9 +89,9 @@ impl TryFrom<&RistrettoExtendedMask> for BulletproofsExtendedMask { fn try_from(extended_mask: &RistrettoExtendedMask) -> Result { let extension_degree = BulletproofsExtensionDegree::try_from_size(extended_mask.secrets().len()) - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string()))?; + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() })?; BulletproofsExtendedMask::assign(extension_degree, Vec::try_from(extended_mask)?) - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string())) + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() }) } } @@ -108,9 +110,9 @@ impl BulletproofsPlusService { g_base_vec: factory.g_base_vec, g_base_compressed_vec: factory.g_base_compressed_vec, extension_degree: BulletproofsExtensionDegree::try_from_size(factory.extension_degree as usize) - .map_err(|e| RangeProofError::InitializationError(e.to_string()))?, + .map_err(|e| RangeProofError::InitializationError { reason: e.to_string() })?, }) - .map_err(|e| RangeProofError::InitializationError(e.to_string()))?, + .map_err(|e| RangeProofError::InitializationError { reason: e.to_string() })?, transcript_label: "Tari Bulletproofs+", }) } @@ -123,9 +125,9 @@ impl BulletproofsPlusService { /// Helper function to return the serialized proof's extension degree pub fn extension_degree(serialized_proof: &[u8]) -> Result { let extension_degree = RistrettoRangeProof::extension_degree_from_proof_bytes(serialized_proof) - .map_err(|e| RangeProofError::InvalidRangeProof(e.to_string()))?; + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() })?; CommitmentExtensionDegree::try_from_size(extension_degree as usize) - .map_err(|e| RangeProofError::InvalidRangeProof(e.to_string())) + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() }) } /// Helper function to prepare a batch of public range statements @@ -187,15 +189,16 @@ impl BulletproofsPlusService { ) -> Result>, RangeProofError> { let mut range_proofs = Vec::with_capacity(proofs.len()); for (i, proof) in proofs.iter().enumerate() { - match RistrettoRangeProof::from_bytes(proof).map_err(|e| RangeProofError::InvalidRangeProof(e.to_string())) + match RistrettoRangeProof::from_bytes(proof) + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() }) { Ok(rp) => { range_proofs.push(rp); }, Err(e) => { - return Err(RangeProofError::InvalidRangeProof(format!( - "Range proof at index '{i}' could not be deserialized ({e})" - ))); + return Err(RangeProofError::InvalidRangeProof { + reason: format!("Range proof at index '{i}' could not be deserialized ({e})"), + }); }, } } @@ -213,21 +216,23 @@ impl RangeProofService for BulletproofsPlusService { .generators .pc_gens() .commit(&Scalar::from(value), &[key.0]) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let opening = CommitmentOpening::new(value, vec![key.0]); - let witness = - RangeWitness::init(vec![opening]).map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + let witness = RangeWitness::init(vec![opening]) + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let statement = RangeStatement::init(self.generators.clone(), vec![commitment], vec![None], None) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let proof = RistrettoRangeProof::prove(self.transcript_label, &statement, &witness) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; Ok(proof.to_bytes()) } fn verify(&self, proof: &Self::Proof, commitment: &HomomorphicCommitment) -> bool { - match RistrettoRangeProof::from_bytes(proof).map_err(|e| RangeProofError::InvalidRangeProof(e.to_string())) { + match RistrettoRangeProof::from_bytes(proof) + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() }) + { Ok(rp) => { let statement = RangeStatement { generators: self.generators.clone(), @@ -289,20 +294,20 @@ impl ExtendedRangeProofService for BulletproofsPlusService { .generators .pc_gens() .commit(&Scalar::from(value), &[mask.0]) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let opening = CommitmentOpening::new(value, vec![mask.0]); - let witness = - RangeWitness::init(vec![opening]).map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + let witness = RangeWitness::init(vec![opening]) + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let statement = RangeStatement::init( self.generators.clone(), vec![commitment], vec![None], Some(seed_nonce.0), ) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let proof = RistrettoRangeProof::prove(self.transcript_label, &statement, &witness) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; Ok(proof.to_bytes()) } @@ -313,9 +318,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { seed_nonce: Option, ) -> Result { if extended_witnesses.is_empty() { - return Err(RangeProofError::ProofConstructionError( - "Extended witness vector cannot be empty".to_string(), - )); + return Err(RangeProofError::ProofConstructionError { + reason: "Extended witness vector cannot be empty".to_string(), + }); } let mut commitments = Vec::with_capacity(extended_witnesses.len()); let mut openings = Vec::with_capacity(extended_witnesses.len()); @@ -325,23 +330,23 @@ impl ExtendedRangeProofService for BulletproofsPlusService { self.generators .pc_gens() .commit(&Scalar::from(witness.value), &Vec::try_from(&witness.mask)?) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?, + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?, ); openings.push(CommitmentOpening::new(witness.value, Vec::try_from(&witness.mask)?)); min_value_promises.push(witness.minimum_value_promise); } - let witness = - RangeWitness::init(openings).map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + let witness = RangeWitness::init(openings) + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let statement = RangeStatement::init( self.generators.clone(), commitments, min_value_promises.iter().map(|v| Some(*v)).collect(), seed_nonce.map(|s| s.0), ) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; let proof = RistrettoRangeProof::prove(self.transcript_label, &statement, &witness) - .map_err(|e| RangeProofError::ProofConstructionError(e.to_string()))?; + .map_err(|e| RangeProofError::ProofConstructionError { reason: e.to_string() })?; Ok(proof.to_bytes()) } @@ -368,9 +373,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { Ok(recovered_masks) => { if recovered_masks.is_empty() { // A mask vector should always be returned so this is a valid error condition - return Err(RangeProofError::InvalidRewind( - "Range proof(s) verified Ok, but no mask vector returned".to_string(), - )); + return Err(RangeProofError::InvalidRewind { + reason: "Range proof(s) verified Ok, but no mask vector returned".to_string(), + }); } else { for recovered_mask in recovered_masks { if let Some(mask) = &recovered_mask { @@ -382,9 +387,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { } }, Err(e) => { - return Err(RangeProofError::InvalidRangeProof(format!( - "Internal range proof(s) error ({e})" - ))) + return Err(RangeProofError::InvalidRangeProof { + reason: format!("Internal range proof(s) error ({e})"), + }) }, }; Ok(recovered_extended_masks) @@ -409,9 +414,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { VerifyAction::VerifyOnly, ) { Ok(_) => Ok(()), - Err(e) => Err(RangeProofError::InvalidRangeProof(format!( - "Internal range proof(s) error ({e})" - ))), + Err(e) => Err(RangeProofError::InvalidRangeProof { + reason: format!("Internal range proof(s) error ({e})"), + }), } } @@ -421,7 +426,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { commitment: &HomomorphicCommitment, seed_nonce: &Self::K, ) -> Result { - match RistrettoRangeProof::from_bytes(proof).map_err(|e| RangeProofError::InvalidRangeProof(e.to_string())) { + match RistrettoRangeProof::from_bytes(proof) + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() }) + { Ok(rp) => { let statement = RangeStatement { generators: self.generators.clone(), @@ -440,28 +447,28 @@ impl ExtendedRangeProofService for BulletproofsPlusService { ) { Ok(recovered_mask) => { if recovered_mask.is_empty() { - Err(RangeProofError::InvalidRewind( - "Mask could not be recovered".to_string(), - )) + Err(RangeProofError::InvalidRewind { + reason: "Mask could not be recovered".to_string(), + }) } else if let Some(mask) = &recovered_mask[0] { Ok(RistrettoSecretKey( mask.blindings() - .map_err(|e| RangeProofError::InvalidRewind(e.to_string()))?[0], + .map_err(|e| RangeProofError::InvalidRewind { reason: e.to_string() })?[0], )) } else { - Err(RangeProofError::InvalidRewind( - "Mask could not be recovered".to_string(), - )) + Err(RangeProofError::InvalidRewind { + reason: "Mask could not be recovered".to_string(), + }) } }, - Err(e) => Err(RangeProofError::InvalidRangeProof(format!( - "Internal range proof error ({e})" - ))), + Err(e) => Err(RangeProofError::InvalidRangeProof { + reason: format!("Internal range proof error ({e})"), + }), } }, - Err(e) => Err(RangeProofError::InvalidRangeProof(format!( - "Range proof could not be deserialized ({e})" - ))), + Err(e) => Err(RangeProofError::InvalidRangeProof { + reason: format!("Range proof could not be deserialized ({e})"), + }), } } @@ -470,7 +477,9 @@ impl ExtendedRangeProofService for BulletproofsPlusService { proof: &Self::Proof, statement: &RistrettoAggregatedPrivateStatement, ) -> Result, RangeProofError> { - match RistrettoRangeProof::from_bytes(proof).map_err(|e| RangeProofError::InvalidRangeProof(e.to_string())) { + match RistrettoRangeProof::from_bytes(proof) + .map_err(|e| RangeProofError::InvalidRangeProof { reason: e.to_string() }) + { Ok(rp) => { // Prepare the range statement let range_statements = self.prepare_private_range_statements(vec![statement]); @@ -490,14 +499,14 @@ impl ExtendedRangeProofService for BulletproofsPlusService { Ok(None) } }, - Err(e) => Err(RangeProofError::InvalidRangeProof(format!( - "Internal range proof error ({e})" - ))), + Err(e) => Err(RangeProofError::InvalidRangeProof { + reason: format!("Internal range proof error ({e})"), + }), } }, - Err(e) => Err(RangeProofError::InvalidRangeProof(format!( - "Range proof could not be deserialized ({e})" - ))), + Err(e) => Err(RangeProofError::InvalidRangeProof { + reason: format!("Range proof could not be deserialized ({e})"), + }), } } @@ -511,7 +520,7 @@ impl ExtendedRangeProofService for BulletproofsPlusService { .generators .pc_gens() .commit(&Scalar::from(value), &[mask.0]) - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string())) + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() }) { Ok(val) => Ok(val == commitment.0.point()), Err(e) => Err(e), @@ -528,7 +537,7 @@ impl ExtendedRangeProofService for BulletproofsPlusService { .generators .pc_gens() .commit(&Scalar::from(value), &Vec::try_from(extended_mask)?) - .map_err(|e| RangeProofError::ExtensionDegree(e.to_string())) + .map_err(|e| RangeProofError::RPExtensionDegree { reason: e.to_string() }) { Ok(val) => Ok(val == commitment.0.point()), Err(e) => Err(e), @@ -538,7 +547,7 @@ impl ExtendedRangeProofService for BulletproofsPlusService { #[cfg(test)] mod test { - use std::collections::HashMap; + use std::{collections::HashMap, vec::Vec}; use bulletproofs_plus::protocols::scalar_protocol::ScalarProtocol; use curve25519_dalek::scalar::Scalar; diff --git a/src/ristretto/constants.rs b/src/ristretto/constants.rs index 1588cc53..8e65f0d1 100644 --- a/src/ristretto/constants.rs +++ b/src/ristretto/constants.rs @@ -6,6 +6,7 @@ //! Tests the correctness of the NUMS construction. use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoBasepointTable, RistrettoPoint}; +use once_cell::sync::OnceCell; const NUMBER_NUMS_POINTS: usize = 10; @@ -56,22 +57,28 @@ pub const RISTRETTO_NUMS_POINTS_COMPRESSED: [CompressedRistretto; NUMBER_NUMS_PO ]), ]; -lazy_static! { - /// A static array of pre-generated NUMS points - pub static ref RISTRETTO_NUMS_POINTS: [RistrettoPoint; NUMBER_NUMS_POINTS] = { +/// A static array of pre-generated NUMS points +pub fn ristretto_nums_points() -> &'static [RistrettoPoint; NUMBER_NUMS_POINTS] { + static INSTANCE: OnceCell<[RistrettoPoint; NUMBER_NUMS_POINTS]> = OnceCell::new(); + INSTANCE.get_or_init(|| { let mut arr = [RistrettoPoint::default(); NUMBER_NUMS_POINTS]; for i in 0..NUMBER_NUMS_POINTS { arr[i] = RISTRETTO_NUMS_POINTS_COMPRESSED[i].decompress().unwrap(); } arr - }; + }) +} - /// Precomputation table for the first point, which is used as the default commitment generator - pub static ref RISTRETTO_NUMS_TABLE_0: RistrettoBasepointTable = RistrettoBasepointTable::create(&RISTRETTO_NUMS_POINTS[0]); +/// Precomputation table for the first point, which is used as the default commitment generator +pub fn ristretto_nums_table_0() -> &'static RistrettoBasepointTable { + static INSTANCE: OnceCell = OnceCell::new(); + INSTANCE.get_or_init(|| RistrettoBasepointTable::create(&ristretto_nums_points()[0])) } #[cfg(test)] mod test { + use alloc::vec::Vec; + use curve25519_dalek::{ constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT}, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -81,9 +88,9 @@ mod test { use sha2::{Digest, Sha512}; use crate::ristretto::constants::{ - RISTRETTO_NUMS_POINTS, + ristretto_nums_points, + ristretto_nums_table_0, RISTRETTO_NUMS_POINTS_COMPRESSED, - RISTRETTO_NUMS_TABLE_0, }; /// Generate a set of NUMS points by hashing domain separation labels and converting the hash output to a Ristretto @@ -105,25 +112,27 @@ mod test { (points, compressed_points) } - /// Confirm that the [RISTRETTO_NUM_POINTS array](Const.RISTRETTO_NUMS_POINTS.html) is generated with Nothing Up + /// Confirm that the [RISTRETTO_NUM_POINTS array](Const.ristretto_nums_points().html) is generated with Nothing Up /// My Sleeve (NUMS), unique, not equal to the identity value and not equal to the Ristretto base point. #[test] pub fn check_nums_points() { let n = RISTRETTO_NUMS_POINTS_COMPRESSED.len(); let calculated_nums_points = nums_ristretto(n); + #[allow(clippy::needless_range_loop)] for i in 0..n { // Should be equal to the NUMS constants - assert_eq!(calculated_nums_points.0[i], RISTRETTO_NUMS_POINTS[i]); + assert_eq!(calculated_nums_points.0[i], ristretto_nums_points()[i]); assert_eq!(calculated_nums_points.1[i], RISTRETTO_NUMS_POINTS_COMPRESSED[i]); // Should not be equal to the identity values - assert_ne!(RistrettoPoint::default(), RISTRETTO_NUMS_POINTS[i]); + assert_ne!(RistrettoPoint::default(), ristretto_nums_points()[i]); assert_ne!(CompressedRistretto::default(), RISTRETTO_NUMS_POINTS_COMPRESSED[i]); // Should not be equal to the Ristretto base point - assert_ne!(RISTRETTO_BASEPOINT_POINT, RISTRETTO_NUMS_POINTS[i]); + assert_ne!(RISTRETTO_BASEPOINT_POINT, ristretto_nums_points()[i]); assert_ne!(RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_NUMS_POINTS_COMPRESSED[i]); // Should all be unique + #[allow(clippy::needless_range_loop)] for j in i + 1..n { - assert_ne!(RISTRETTO_NUMS_POINTS[i], RISTRETTO_NUMS_POINTS[j]); + assert_ne!(ristretto_nums_points()[i], ristretto_nums_points()[j]); assert_ne!(RISTRETTO_NUMS_POINTS_COMPRESSED[i], RISTRETTO_NUMS_POINTS_COMPRESSED[j]); } } @@ -133,12 +142,12 @@ mod test { #[test] pub fn check_tables() { // Perform test multiplications - assert_eq!(&*RISTRETTO_NUMS_TABLE_0 * &Scalar::ZERO, RistrettoPoint::identity()); + assert_eq!(ristretto_nums_table_0() * &Scalar::ZERO, RistrettoPoint::identity()); for j in 0..15u8 { assert_eq!( - &*RISTRETTO_NUMS_TABLE_0 * &Scalar::from(j), - RISTRETTO_NUMS_POINTS[0] * Scalar::from(j) + ristretto_nums_table_0() * &Scalar::from(j), + ristretto_nums_points()[0] * Scalar::from(j) ); } } diff --git a/src/ristretto/mod.rs b/src/ristretto/mod.rs index 169a61b5..af3102db 100644 --- a/src/ristretto/mod.rs +++ b/src/ristretto/mod.rs @@ -3,6 +3,7 @@ //! This module contains implementations using the Ristretto curve. +#[cfg(feature = "bulletproofs_plus")] pub mod bulletproofs_plus; pub mod constants; pub mod pedersen; @@ -10,6 +11,7 @@ mod ristretto_com_and_pub_sig; mod ristretto_com_sig; pub mod ristretto_keys; mod ristretto_sig; +#[cfg(feature = "serde")] pub mod serialize; pub mod utils; diff --git a/src/ristretto/pedersen/commitment_factory.rs b/src/ristretto/pedersen/commitment_factory.rs index edae45a1..0cc112ed 100644 --- a/src/ristretto/pedersen/commitment_factory.rs +++ b/src/ristretto/pedersen/commitment_factory.rs @@ -8,15 +8,12 @@ use curve25519_dalek::{ traits::{Identity, MultiscalarMul}, }; +#[cfg(feature = "precomputed_tables")] +use crate::ristretto::pedersen::scalar_mul_with_pre_computation_tables; use crate::{ commitment::{HomomorphicCommitment, HomomorphicCommitmentFactory}, ristretto::{ - pedersen::{ - scalar_mul_with_pre_computation_tables, - PedersenCommitment, - RISTRETTO_PEDERSEN_G, - RISTRETTO_PEDERSEN_H, - }, + pedersen::{ristretto_pedersen_h, PedersenCommitment, RISTRETTO_PEDERSEN_G}, RistrettoPublicKey, RistrettoSecretKey, }, @@ -43,7 +40,7 @@ impl PedersenCommitmentFactory { impl Default for PedersenCommitmentFactory { /// The default Ristretto Commitment factory uses the Base point for x25519 and its first Blake256 hash. fn default() -> Self { - PedersenCommitmentFactory::new(RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H) + PedersenCommitmentFactory::new(RISTRETTO_PEDERSEN_G, *ristretto_pedersen_h()) } } @@ -53,8 +50,15 @@ impl HomomorphicCommitmentFactory for PedersenCommitmentFactory { #[allow(non_snake_case)] fn commit(&self, k: &RistrettoSecretKey, v: &RistrettoSecretKey) -> PedersenCommitment { // If we're using the default generators, speed it up using pre-computation tables - let c = if (self.G, self.H) == (RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H) { - scalar_mul_with_pre_computation_tables(&k.0, &v.0) + let c = if (self.G, self.H) == (RISTRETTO_PEDERSEN_G, *ristretto_pedersen_h()) { + #[cfg(feature = "precomputed_tables")] + { + scalar_mul_with_pre_computation_tables(&k.0, &v.0) + } + #[cfg(not(feature = "precomputed_tables"))] + { + RistrettoPoint::multiscalar_mul(&[v.0, k.0], &[self.H, self.G]) + } } else { RistrettoPoint::multiscalar_mul(&[v.0, k.0], &[self.H, self.G]) }; @@ -83,6 +87,7 @@ impl HomomorphicCommitmentFactory for PedersenCommitmentFactory { #[cfg(test)] mod test { + use alloc::vec::Vec; use std::{ collections::hash_map::DefaultHasher, convert::From, @@ -90,7 +95,6 @@ mod test { }; use curve25519_dalek::scalar::Scalar; - use tari_utilities::message_format::MessageFormat; use super::*; use crate::{ @@ -103,7 +107,7 @@ mod test { fn check_default_base() { let base = PedersenCommitmentFactory::default(); assert_eq!(base.G, RISTRETTO_PEDERSEN_G); - assert_eq!(base.H, *RISTRETTO_PEDERSEN_H) + assert_eq!(base.H, *ristretto_pedersen_h()) } #[test] @@ -111,7 +115,7 @@ mod test { fn check_zero() { let c = RistrettoPoint::multiscalar_mul(&[Scalar::ZERO, Scalar::ZERO], &[ RISTRETTO_PEDERSEN_G, - *RISTRETTO_PEDERSEN_H, + *ristretto_pedersen_h(), ]); let factory = PedersenCommitmentFactory::default(); assert_eq!( @@ -126,7 +130,7 @@ mod test { #[allow(non_snake_case)] fn check_open() { let factory = PedersenCommitmentFactory::default(); - let H = *RISTRETTO_PEDERSEN_H; + let H = *ristretto_pedersen_h(); let mut rng = rand::thread_rng(); for _ in 0..100 { let v = RistrettoSecretKey::random(&mut rng); @@ -220,9 +224,10 @@ mod test { assert!(commitment_factory.open(&k_sum, &v_sum, &c_sum)); assert_eq!(c_sum, commitments.iter().sum()); } - + #[cfg(feature = "serde")] #[test] fn serialize_deserialize() { + use tari_utilities::message_format::MessageFormat; let mut rng = rand::thread_rng(); let factory = PedersenCommitmentFactory::default(); let k = RistrettoSecretKey::random(&mut rng); diff --git a/src/ristretto/pedersen/extended_commitment_factory.rs b/src/ristretto/pedersen/extended_commitment_factory.rs index 5c55beaf..232b24ce 100644 --- a/src/ristretto/pedersen/extended_commitment_factory.rs +++ b/src/ristretto/pedersen/extended_commitment_factory.rs @@ -3,7 +3,8 @@ //! Extended commitments are commitments that have more than one blinding factor. -use std::{borrow::Borrow, iter::once}; +use alloc::vec::Vec; +use core::{borrow::Borrow, iter::once}; use curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, @@ -11,7 +12,10 @@ use curve25519_dalek::{ traits::{Identity, MultiscalarMul}, }; +#[cfg(feature = "precomputed_tables")] +use crate::ristretto::pedersen::scalar_mul_with_pre_computation_tables; use crate::{ + alloc::string::ToString, commitment::{ ExtendedHomomorphicCommitmentFactory, ExtensionDegree, @@ -20,14 +24,13 @@ use crate::{ }, errors::CommitmentError, ristretto::{ - constants::{RISTRETTO_NUMS_POINTS, RISTRETTO_NUMS_POINTS_COMPRESSED}, + constants::{ristretto_nums_points, RISTRETTO_NUMS_POINTS_COMPRESSED}, pedersen::{ - scalar_mul_with_pre_computation_tables, + ristretto_pedersen_h, + ristretto_pedersen_h_compressed, PedersenCommitment, RISTRETTO_PEDERSEN_G, RISTRETTO_PEDERSEN_G_COMPRESSED, - RISTRETTO_PEDERSEN_H, - RISTRETTO_PEDERSEN_H_COMPRESSED, }, RistrettoPublicKey, RistrettoSecretKey, @@ -56,24 +59,24 @@ impl ExtendedPedersenCommitmentFactory { /// Create a new Extended Pedersen Ristretto Commitment factory for the required extension degree using /// pre-calculated compressed constants - we only hold references to the static generator points. pub fn new_with_extension_degree(extension_degree: ExtensionDegree) -> Result { - if extension_degree as usize > RISTRETTO_NUMS_POINTS.len() || + if extension_degree as usize > ristretto_nums_points().len() || extension_degree as usize > RISTRETTO_NUMS_POINTS_COMPRESSED.len() { - return Err(CommitmentError::ExtensionDegree( - "Not enough Ristretto NUMS points to construct the extended commitment factory".to_string(), - )); + return Err(CommitmentError::CommitmentExtensionDegree { + reason: "Not enough Ristretto NUMS points to construct the extended commitment factory".to_string(), + }); } - let g_base_vec = std::iter::once(&RISTRETTO_PEDERSEN_G) - .chain(RISTRETTO_NUMS_POINTS[1..extension_degree as usize].iter()) + let g_base_vec = once(&RISTRETTO_PEDERSEN_G) + .chain(ristretto_nums_points()[1..extension_degree as usize].iter()) .copied() .collect(); - let g_base_compressed_vec = std::iter::once(&RISTRETTO_PEDERSEN_G_COMPRESSED) + let g_base_compressed_vec = once(&RISTRETTO_PEDERSEN_G_COMPRESSED) .chain(RISTRETTO_NUMS_POINTS_COMPRESSED[1..extension_degree as usize].iter()) .copied() .collect(); Ok(Self { - h_base: *RISTRETTO_PEDERSEN_H, - h_base_compressed: *RISTRETTO_PEDERSEN_H_COMPRESSED, + h_base: *ristretto_pedersen_h(), + h_base_compressed: *ristretto_pedersen_h_compressed(), g_base_vec, g_base_compressed_vec, extension_degree, @@ -90,11 +93,23 @@ impl ExtendedPedersenCommitmentFactory { for<'a> &'a Scalar: Borrow, { if blinding_factors.is_empty() || blinding_factors.len() > self.extension_degree as usize { - Err(CommitmentError::ExtensionDegree("blinding vector".to_string())) + Err(CommitmentError::CommitmentExtensionDegree { + reason: "blinding vector".to_string(), + }) } else if blinding_factors.len() == 1 && - (self.g_base_vec[0], self.h_base) == (RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H) + (self.g_base_vec[0], self.h_base) == (RISTRETTO_PEDERSEN_G, *ristretto_pedersen_h()) { - Ok(scalar_mul_with_pre_computation_tables(&blinding_factors[0], value)) + #[cfg(feature = "precomputed_tables")] + { + Ok(scalar_mul_with_pre_computation_tables(&blinding_factors[0], value)) + } + #[cfg(not(feature = "precomputed_tables"))] + { + let scalars = once(value).chain(blinding_factors); + let g_base_head = self.g_base_vec.iter().take(blinding_factors.len()); + let points = once(&self.h_base).chain(g_base_head); + Ok(RistrettoPoint::multiscalar_mul(scalars, points)) + } } else { let scalars = once(value).chain(blinding_factors); let g_base_head = self.g_base_vec.iter().take(blinding_factors.len()); @@ -168,7 +183,7 @@ impl ExtendedHomomorphicCommitmentFactory for ExtendedPedersenCommitmentFactory ) -> Result { let c_test = self .commit_extended(k_vec, v) - .map_err(|e| CommitmentError::ExtensionDegree(e.to_string()))?; + .map_err(|e| CommitmentError::CommitmentExtensionDegree { reason: e.to_string() })?; Ok(commitment == &c_test) } @@ -194,6 +209,7 @@ impl ExtendedHomomorphicCommitmentFactory for ExtendedPedersenCommitmentFactory #[cfg(test)] mod test { + use alloc::vec::Vec; use std::{ collections::hash_map::DefaultHasher, hash::{Hash, Hasher}, @@ -201,7 +217,6 @@ mod test { use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar, traits::MultiscalarMul}; use rand::rngs::ThreadRng; - use tari_utilities::message_format::MessageFormat; use crate::{ commitment::{ @@ -212,13 +227,12 @@ mod test { }, keys::{PublicKey, SecretKey}, ristretto::{ - constants::RISTRETTO_NUMS_POINTS, + constants::ristretto_nums_points, pedersen::{ commitment_factory::PedersenCommitmentFactory, extended_commitment_factory::ExtendedPedersenCommitmentFactory, - PedersenCommitment, + ristretto_pedersen_h, RISTRETTO_PEDERSEN_G, - RISTRETTO_PEDERSEN_H, }, RistrettoPublicKey, RistrettoSecretKey, @@ -238,7 +252,7 @@ mod test { fn check_default_base() { let factory = ExtendedPedersenCommitmentFactory::default(); assert_eq!(factory.g_base_vec[0], RISTRETTO_PEDERSEN_G); - assert_eq!(factory.h_base, *RISTRETTO_PEDERSEN_H); + assert_eq!(factory.h_base, *ristretto_pedersen_h()); assert_eq!( factory, ExtendedPedersenCommitmentFactory::new_with_extension_degree(ExtensionDegree::DefaultPedersen).unwrap() @@ -291,7 +305,7 @@ mod test { #[test] #[allow(non_snake_case)] fn check_open_both_traits() { - let H = *RISTRETTO_PEDERSEN_H; + let H = *ristretto_pedersen_h(); let mut rng = rand::thread_rng(); for extension_degree in EXTENSION_DEGREE { let factory = ExtendedPedersenCommitmentFactory::new_with_extension_degree(extension_degree).unwrap(); @@ -300,8 +314,9 @@ mod test { let k_vec = vec![RistrettoSecretKey::random(&mut rng); extension_degree as usize]; let c_extended = factory.commit_extended(&k_vec, &v).unwrap(); let mut c_calc: RistrettoPoint = v.0 * H + k_vec[0].0 * RISTRETTO_PEDERSEN_G; + #[allow(clippy::needless_range_loop)] for i in 1..(extension_degree as usize) { - c_calc += k_vec[i].0 * RISTRETTO_NUMS_POINTS[i]; + c_calc += k_vec[i].0 * ristretto_nums_points()[i]; } assert_eq!(RistrettoPoint::from(c_extended.as_public_key()), c_calc); @@ -539,42 +554,49 @@ mod test { } } - #[test] - fn serialize_deserialize_singular() { - let mut rng = rand::thread_rng(); - let factory = ExtendedPedersenCommitmentFactory::default(); - let k = RistrettoSecretKey::random(&mut rng); - let c = factory.commit_value(&k, 420); - // Base64 - let ser_c = c.to_base64().unwrap(); - let c2 = PedersenCommitment::from_base64(&ser_c).unwrap(); - assert!(factory.open_value(&k, 420, &c2)); - // MessagePack - let ser_c = c.to_binary().unwrap(); - let c2 = PedersenCommitment::from_binary(&ser_c).unwrap(); - assert!(factory.open_value(&k, 420, &c2)); - // Invalid Base64 - assert!(PedersenCommitment::from_base64("bad@ser$").is_err()); - } + #[cfg(feature = "serde")] + mod test_serialize { + use tari_utilities::message_format::MessageFormat; - #[test] - fn serialize_deserialize_extended() { - let mut rng = rand::thread_rng(); - for extension_degree in EXTENSION_DEGREE { - let factory = ExtendedPedersenCommitmentFactory::new_with_extension_degree(extension_degree).unwrap(); - let k_vec = vec![RistrettoSecretKey::random(&mut rng); extension_degree as usize]; - let c = factory.commit_value_extended(&k_vec, 420).unwrap(); + use super::*; + use crate::ristretto::pedersen::PedersenCommitment; + #[test] + fn serialize_deserialize_singular() { + let mut rng = rand::thread_rng(); + let factory = ExtendedPedersenCommitmentFactory::default(); + let k = RistrettoSecretKey::random(&mut rng); + let c = factory.commit_value(&k, 420); // Base64 let ser_c = c.to_base64().unwrap(); let c2 = PedersenCommitment::from_base64(&ser_c).unwrap(); - assert!(factory.open_value_extended(&k_vec, 420, &c2).unwrap()); + assert!(factory.open_value(&k, 420, &c2)); // MessagePack let ser_c = c.to_binary().unwrap(); let c2 = PedersenCommitment::from_binary(&ser_c).unwrap(); - assert!(factory.open_value_extended(&k_vec, 420, &c2).unwrap()); + assert!(factory.open_value(&k, 420, &c2)); // Invalid Base64 assert!(PedersenCommitment::from_base64("bad@ser$").is_err()); } + + #[test] + fn serialize_deserialize_extended() { + let mut rng = rand::thread_rng(); + for extension_degree in EXTENSION_DEGREE { + let factory = ExtendedPedersenCommitmentFactory::new_with_extension_degree(extension_degree).unwrap(); + let k_vec = vec![RistrettoSecretKey::random(&mut rng); extension_degree as usize]; + let c = factory.commit_value_extended(&k_vec, 420).unwrap(); + // Base64 + let ser_c = c.to_base64().unwrap(); + let c2 = PedersenCommitment::from_base64(&ser_c).unwrap(); + assert!(factory.open_value_extended(&k_vec, 420, &c2).unwrap()); + // MessagePack + let ser_c = c.to_binary().unwrap(); + let c2 = PedersenCommitment::from_binary(&ser_c).unwrap(); + assert!(factory.open_value_extended(&k_vec, 420, &c2).unwrap()); + // Invalid Base64 + assert!(PedersenCommitment::from_base64("bad@ser$").is_err()); + } + } } #[test] diff --git a/src/ristretto/pedersen/mod.rs b/src/ristretto/pedersen/mod.rs index 8e7c2af0..1230c05c 100644 --- a/src/ristretto/pedersen/mod.rs +++ b/src/ristretto/pedersen/mod.rs @@ -3,18 +3,22 @@ //! Pederson commitments utilities -use std::{borrow::Borrow, iter::Sum}; +use core::{borrow::Borrow, iter::Sum}; +#[cfg(feature = "precomputed_tables")] +use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_TABLE, scalar::Scalar}; use curve25519_dalek::{ - constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE}, + constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT}, ristretto::{CompressedRistretto, RistrettoPoint}, - scalar::Scalar, }; +use once_cell::sync::OnceCell; +#[cfg(feature = "precomputed_tables")] +use crate::ristretto::constants::ristretto_nums_table_0; use crate::{ commitment::HomomorphicCommitment, ristretto::{ - constants::{RISTRETTO_NUMS_POINTS, RISTRETTO_NUMS_POINTS_COMPRESSED, RISTRETTO_NUMS_TABLE_0}, + constants::{ristretto_nums_points, RISTRETTO_NUMS_POINTS_COMPRESSED}, RistrettoPublicKey, }, }; @@ -26,11 +30,15 @@ pub mod extended_commitment_factory; pub const RISTRETTO_PEDERSEN_G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; /// The default generator on a Pedersen commitment used for the blinding factor in a compressed form pub const RISTRETTO_PEDERSEN_G_COMPRESSED: CompressedRistretto = RISTRETTO_BASEPOINT_COMPRESSED; -lazy_static! { - /// The default generator on a Pedersen commitment used for the value - pub static ref RISTRETTO_PEDERSEN_H: RistrettoPoint = RISTRETTO_NUMS_POINTS[0]; - /// The default generator on a Pedersen commitment used for the value in a compressed form - pub static ref RISTRETTO_PEDERSEN_H_COMPRESSED: CompressedRistretto = RISTRETTO_NUMS_POINTS_COMPRESSED[0]; +/// The default generator on a Pedersen commitment used for the value +pub fn ristretto_pedersen_h() -> &'static RistrettoPoint { + static INSTANCE: OnceCell = OnceCell::new(); + INSTANCE.get_or_init(|| ristretto_nums_points()[0]) +} +/// The default generator on a Pedersen commitment used for the value in a compressed form +pub fn ristretto_pedersen_h_compressed() -> &'static CompressedRistretto { + static INSTANCE: OnceCell = OnceCell::new(); + INSTANCE.get_or_init(|| RISTRETTO_NUMS_POINTS_COMPRESSED[0]) } /// The Pedersen commitment @@ -51,8 +59,9 @@ where T: Borrow } } +#[cfg(feature = "precomputed_tables")] pub(crate) fn scalar_mul_with_pre_computation_tables(k: &Scalar, v: &Scalar) -> RistrettoPoint { - RISTRETTO_BASEPOINT_TABLE * k + &*RISTRETTO_NUMS_TABLE_0 * v + RISTRETTO_BASEPOINT_TABLE * k + ristretto_nums_table_0() * v } #[cfg(test)] @@ -66,9 +75,9 @@ mod test { pedersen::{ commitment_factory::PedersenCommitmentFactory, extended_commitment_factory::ExtendedPedersenCommitmentFactory, + ristretto_pedersen_h, PedersenCommitment, RISTRETTO_PEDERSEN_G, - RISTRETTO_PEDERSEN_H, }, RistrettoPublicKey, RistrettoSecretKey, @@ -99,7 +108,7 @@ mod test { #[test] fn check_g_ne_h() { - assert_ne!(RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H); + assert_ne!(RISTRETTO_PEDERSEN_G, *ristretto_pedersen_h()); } #[test] diff --git a/src/ristretto/ristretto_keys.rs b/src/ristretto/ristretto_keys.rs index 2eade9cc..6a53b287 100644 --- a/src/ristretto/ristretto_keys.rs +++ b/src/ristretto/ristretto_keys.rs @@ -2,15 +2,14 @@ // SPDX-License-Identifier: BSD-3-Clause //! The Tari-compatible implementation of Ristretto based on the curve25519-dalek implementation -use std::{ +use alloc::{string::ToString, vec::Vec}; +use core::{ borrow::Borrow, cmp::Ordering, fmt, hash::{Hash, Hasher}, ops::{Add, Mul, Sub}, }; -#[cfg(feature = "borsh")] -use std::{io, io::Write}; use blake2::Blake2b; use curve25519_dalek::{ @@ -21,7 +20,7 @@ use curve25519_dalek::{ }; use digest::{consts::U64, Digest}; use once_cell::sync::OnceCell; -use rand::{CryptoRng, Rng}; +use rand_core::{CryptoRng, RngCore}; use tari_utilities::{hex::Hex, ByteArray, ByteArrayError, Hashable}; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -57,17 +56,18 @@ pub struct RistrettoSecretKey(pub(crate) Scalar); #[cfg(feature = "borsh")] impl borsh::BorshSerialize for RistrettoSecretKey { - fn serialize(&self, writer: &mut W) -> io::Result<()> { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { borsh::BorshSerialize::serialize(&self.as_bytes(), writer) } } #[cfg(feature = "borsh")] impl borsh::BorshDeserialize for RistrettoSecretKey { - fn deserialize_reader(reader: &mut R) -> Result - where R: io::Read { + 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()).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string())) + Self::from_bytes(bytes.as_slice()) + .map_err(|e| borsh::maybestd::io::Error::new(borsh::maybestd::io::ErrorKind::InvalidInput, e.to_string())) } } @@ -76,7 +76,7 @@ impl SecretKey for RistrettoSecretKey { const KEY_LEN: usize = 32; /// Return a random secret key on the `ristretto255` curve using the supplied CSPRNG. - fn random(rng: &mut R) -> Self { + fn random(rng: &mut R) -> Self { RistrettoSecretKey(Scalar::random(rng)) } } @@ -263,17 +263,18 @@ pub struct RistrettoPublicKey { #[cfg(feature = "borsh")] impl borsh::BorshSerialize for RistrettoPublicKey { - fn serialize(&self, writer: &mut W) -> io::Result<()> { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { borsh::BorshSerialize::serialize(&self.as_bytes(), writer) } } #[cfg(feature = "borsh")] impl borsh::BorshDeserialize for RistrettoPublicKey { - fn deserialize_reader(reader: &mut R) -> Result - where R: io::Read { + 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()).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string())) + Self::from_bytes(bytes.as_slice()) + .map_err(|e| borsh::maybestd::io::Error::new(borsh::maybestd::io::ErrorKind::InvalidInput, e.to_string())) } } @@ -298,7 +299,7 @@ impl RistrettoPublicKey { // This function requires 512 bytes of data, so let's be opinionated here and use blake2b let hash = DomainSeparatedHasher::, RistrettoGeneratorPoint>::new_with_label(label).finalize(); if hash.as_ref().len() < 64 { - return Err(HashingError::DigestTooShort(64)); + return Err(HashingError::DigestTooShort { bytes: 64 }); } let mut bytes = [0u8; 64]; bytes.copy_from_slice(hash.as_ref()); @@ -429,7 +430,7 @@ impl RistrettoPublicKey { let right = hex.len() - (w - left - 3); f.write_str(format!("{}...{}", &hex[..left], &hex[right..]).as_str()) }, - _ => std::fmt::Display::fmt(&hex, f), + _ => core::fmt::Display::fmt(&hex, f), } } } @@ -598,7 +599,7 @@ impl From for CompressedRistretto { #[cfg(test)] mod test { use digest::consts::U32; - use tari_utilities::{message_format::MessageFormat, ByteArray}; + use tari_utilities::ByteArray; use super::*; use crate::{keys::PublicKey, ristretto::test_common::get_keypair}; @@ -807,7 +808,7 @@ mod test { // can fail in release mode, even though the values were effectively scrubbed. if cfg!(debug_assertions) { unsafe { - use std::slice; + use core::slice; assert_eq!(slice::from_raw_parts(ptr, 32), zero); } } @@ -831,44 +832,48 @@ mod test { "00e1f50500000000000000000000000000000000000000000000000000000000" ); } + #[cfg(feature = "serde")] + mod test_serialize { + use tari_utilities::message_format::MessageFormat; - #[test] - fn serialize_deserialize_base64() { - let mut rng = rand::thread_rng(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_base64().unwrap(); - let ser_pk = pk.to_base64().unwrap(); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_base64(&ser_k).unwrap(); - assert_eq!(k, k2, "Deserialised secret key"); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_base64(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); - } + use super::*; + #[test] + fn serialize_deserialize_base64() { + let mut rng = rand::thread_rng(); + let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); + let ser_k = k.to_base64().unwrap(); + let ser_pk = pk.to_base64().unwrap(); + let k2: RistrettoSecretKey = RistrettoSecretKey::from_base64(&ser_k).unwrap(); + assert_eq!(k, k2, "Deserialised secret key"); + let pk2: RistrettoPublicKey = RistrettoPublicKey::from_base64(&ser_pk).unwrap(); + assert_completely_equal(&pk, &pk2); + } - #[test] - fn serialize_deserialize_json() { - let mut rng = rand::thread_rng(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_json().unwrap(); - let ser_pk = pk.to_json().unwrap(); - println!("JSON pubkey: {ser_pk} privkey: {ser_k}"); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_json(&ser_k).unwrap(); - assert_eq!(k, k2, "Deserialised secret key"); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_json(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); - } + #[test] + fn serialize_deserialize_json() { + let mut rng = rand::thread_rng(); + let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); + let ser_k = k.to_json().unwrap(); + let ser_pk = pk.to_json().unwrap(); + println!("JSON pubkey: {ser_pk} privkey: {ser_k}"); + let k2: RistrettoSecretKey = RistrettoSecretKey::from_json(&ser_k).unwrap(); + assert_eq!(k, k2, "Deserialised secret key"); + let pk2: RistrettoPublicKey = RistrettoPublicKey::from_json(&ser_pk).unwrap(); + assert_completely_equal(&pk, &pk2); + } - #[test] - fn serialize_deserialize_binary() { - let mut rng = rand::thread_rng(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_binary().unwrap(); - let ser_pk = pk.to_binary().unwrap(); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_binary(&ser_k).unwrap(); - assert_eq!(k, k2); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_binary(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); + #[test] + fn serialize_deserialize_binary() { + let mut rng = rand::thread_rng(); + let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); + let ser_k = k.to_binary().unwrap(); + let ser_pk = pk.to_binary().unwrap(); + let k2: RistrettoSecretKey = RistrettoSecretKey::from_binary(&ser_k).unwrap(); + assert_eq!(k, k2); + let pk2: RistrettoPublicKey = RistrettoPublicKey::from_binary(&ser_pk).unwrap(); + assert_completely_equal(&pk, &pk2); + } } - #[test] fn display_and_debug() { let hex = "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"; @@ -964,7 +969,7 @@ mod test { #[test] fn kdf_key_too_short() { let err = RistrettoKdf::generate::>(b"this_key_is_too_short", b"data", "test").err(); - assert!(matches!(err, Some(HashingError::InputTooShort))); + assert!(matches!(err, Some(HashingError::InputTooShort {}))); } #[test] @@ -1036,6 +1041,8 @@ mod test { #[cfg(feature = "borsh")] mod borsh { + use alloc::vec::Vec; + use borsh::{BorshDeserialize, BorshSerialize}; use crate::ristretto::{test_common::get_keypair, RistrettoPublicKey, RistrettoSecretKey}; diff --git a/src/ristretto/ristretto_sig.rs b/src/ristretto/ristretto_sig.rs index 245c949c..9e1c152e 100644 --- a/src/ristretto/ristretto_sig.rs +++ b/src/ristretto/ristretto_sig.rs @@ -42,6 +42,7 @@ use crate::{ /// # use tari_crypto::keys::*; /// # use tari_crypto::signatures::SchnorrSignature; /// # use digest::Digest; +/// # use rand::{Rng, thread_rng}; /// /// fn get_keypair() -> (RistrettoSecretKey, RistrettoPublicKey) { /// let mut rng = rand::thread_rng(); @@ -53,7 +54,8 @@ use crate::{ /// #[allow(non_snake_case)] /// let (k, P) = get_keypair(); /// let msg = "Small Gods"; -/// let sig = RistrettoSchnorr::sign_message(&k, &msg); +/// let mut rng = thread_rng(); +/// let sig = RistrettoSchnorr::sign_message(&k, &msg, &mut rng); /// ``` /// /// # Verifying signatures @@ -68,6 +70,7 @@ use crate::{ /// # use tari_utilities::hex::*; /// # use tari_utilities::ByteArray; /// # use digest::Digest; +/// # use rand::{Rng, thread_rng}; /// /// let msg = "Maskerade"; /// let k = RistrettoSecretKey::from_hex( @@ -76,8 +79,9 @@ use crate::{ /// .unwrap(); /// # #[allow(non_snake_case)] /// let P = RistrettoPublicKey::from_secret_key(&k); +/// let mut rng = thread_rng(); /// let sig: SchnorrSignature = -/// SchnorrSignature::sign_message(&k, msg).unwrap(); +/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap(); /// assert!(sig.verify_message(&P, msg)); /// ``` pub type RistrettoSchnorr = SchnorrSignature; @@ -94,6 +98,7 @@ pub type RistrettoSchnorr = SchnorrSignature = -/// SchnorrSignature::sign_message(&k, msg).unwrap(); +/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap(); /// assert!(sig.verify_message(&P, msg)); /// ``` pub type RistrettoSchnorrWithDomain = SchnorrSignature; @@ -261,7 +267,8 @@ 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").unwrap(); + 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")); diff --git a/src/ristretto/serialize.rs b/src/ristretto/serialize.rs index 8054102f..6d7aafba 100644 --- a/src/ristretto/serialize.rs +++ b/src/ristretto/serialize.rs @@ -22,7 +22,8 @@ //! } //! ``` -use std::fmt; +use alloc::string::String; +use core::fmt; use serde::{ de::{self, Visitor}, diff --git a/src/ristretto/utils.rs b/src/ristretto/utils.rs index 7ac8cf58..dfc2684a 100644 --- a/src/ristretto/utils.rs +++ b/src/ristretto/utils.rs @@ -3,7 +3,10 @@ //! 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::{ @@ -33,12 +36,12 @@ pub struct SignatureSet { since = "0.16.0", note = "Use SchnorrSignature::sign_message instead. This method will be removed in v1.0.0" )] -pub fn sign( +pub fn sign( private_key: &RistrettoSecretKey, message: &[u8], + rng: &mut R, ) -> Result { - let mut rng = rand::thread_rng(); - let (nonce, public_nonce) = RistrettoPublicKey::random_keypair(&mut rng); + let (nonce, public_nonce) = RistrettoPublicKey::random_keypair(rng); let message = D::new() .chain_update(public_nonce.as_bytes()) .chain_update(message) diff --git a/src/signatures/commitment_and_public_key_signature.rs b/src/signatures/commitment_and_public_key_signature.rs index 942d7290..a02d6ccc 100644 --- a/src/signatures/commitment_and_public_key_signature.rs +++ b/src/signatures/commitment_and_public_key_signature.rs @@ -1,27 +1,29 @@ // Copyright 2021. The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::{ +use alloc::vec::Vec; +use core::{ cmp::Ordering, hash::{Hash, Hasher}, ops::{Add, Mul}, }; -use rand::{CryptoRng, RngCore}; -use serde::{Deserialize, Serialize}; +use rand_core::{CryptoRng, RngCore}; +use snafu::prelude::*; use tari_utilities::ByteArray; -use thiserror::Error; use crate::{ + alloc::borrow::ToOwned, commitment::{HomomorphicCommitment, HomomorphicCommitmentFactory}, keys::{PublicKey, SecretKey}, }; /// An error when creating a commitment signature -#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(missing_docs)] pub enum CommitmentAndPublicKeySignatureError { - #[error("An invalid challenge was provided")] + #[snafu(display("An invalid challenge was provided"))] InvalidChallenge, } @@ -53,8 +55,9 @@ pub enum CommitmentAndPublicKeySignatureError { /// The use of efficient multiscalar multiplication algorithms may also be useful for efficiency. /// The use of precomputation tables for `G` and `H` may also be useful for efficiency. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone)] #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CommitmentAndPublicKeySignature { ephemeral_commitment: HomomorphicCommitment

, ephemeral_pubkey: P, diff --git a/src/signatures/commitment_signature.rs b/src/signatures/commitment_signature.rs index ed8d2e93..a114f425 100644 --- a/src/signatures/commitment_signature.rs +++ b/src/signatures/commitment_signature.rs @@ -1,15 +1,15 @@ // Copyright 2021. The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use std::{ +use alloc::vec::Vec; +use core::{ cmp::Ordering, hash::{Hash, Hasher}, ops::{Add, Mul}, }; -use serde::{Deserialize, Serialize}; +use snafu::prelude::*; use tari_utilities::{ByteArray, ByteArrayError}; -use thiserror::Error; use crate::{ commitment::{HomomorphicCommitment, HomomorphicCommitmentFactory}, @@ -17,10 +17,11 @@ use crate::{ }; /// An error when creating a commitment signature -#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(missing_docs)] pub enum CommitmentSignatureError { - #[error("An invalid challenge was provided")] + #[snafu(display("An invalid challenge was provided"))] InvalidChallenge, } @@ -46,8 +47,9 @@ pub enum CommitmentSignatureError { /// S =? R + e.C ... (final verification) #[allow(non_snake_case)] -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone)] #[cfg_attr(feature = "borsh", derive(borsh::BorshDeserialize, borsh::BorshSerialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CommitmentSignature { public_nonce: HomomorphicCommitment

, u: K, diff --git a/src/signatures/schnorr.rs b/src/signatures/schnorr.rs index 5047e413..e9447a14 100644 --- a/src/signatures/schnorr.rs +++ b/src/signatures/schnorr.rs @@ -5,7 +5,7 @@ //! This module defines generic traits for handling the digital signature operations, agnostic //! of the underlying elliptic curve implementation -use std::{ +use core::{ cmp::Ordering, hash::{Hash, Hasher}, marker::PhantomData, @@ -14,9 +14,9 @@ use std::{ use blake2::Blake2b; use digest::{consts::U32, Digest}; -use serde::{Deserialize, Serialize}; +use rand_core::{CryptoRng, RngCore}; +use snafu::prelude::*; use tari_utilities::ByteArray; -use thiserror::Error; use crate::{ hash_domain, @@ -28,10 +28,11 @@ use crate::{ hash_domain!(SchnorrSigChallenge, "com.tari.schnorr_signature", 1); /// An error occurred during construction of a SchnorrSignature -#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, Snafu, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(missing_docs)] pub enum SchnorrSignatureError { - #[error("An invalid challenge was provided")] + #[snafu(display("An invalid challenge was provided"))] InvalidChallenge, } @@ -42,12 +43,13 @@ pub enum SchnorrSignatureError { /// /// More details on Schnorr signatures can be found at [TLU](https://tlu.tarilabs.com/cryptography/introduction-schnorr-signatures). #[allow(non_snake_case)] -#[derive(Copy, Debug, Clone, Serialize, Deserialize)] +#[derive(Copy, Debug, Clone)] #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SchnorrSignature { public_nonce: P, signature: K, - #[serde(skip)] + #[cfg_attr(feature = "serde", serde(skip))] _phantom: PhantomData, } @@ -121,12 +123,16 @@ where /// /// it is possible to customise the challenge by using [`construct_domain_separated_challenge`] and [`sign_raw`] /// yourself, or even use [`sign_raw`] using a completely custom challenge. - pub fn sign_message<'a, B>(secret: &'a K, message: B) -> Result + pub fn sign_message<'a, B, R: RngCore + CryptoRng>( + secret: &'a K, + message: B, + rng: &mut R, + ) -> Result where K: Add + Mul<&'a K, Output = K>, B: AsRef<[u8]>, { - let nonce = K::random(&mut rand::thread_rng()); + let nonce = K::random(rng); Self::sign_with_nonce_and_message(secret, nonce, message) }