Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat!: enable no_std support for tari_crypto #191

Merged
merged 12 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/clippy-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

37 changes: 20 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 3 additions & 2 deletions benches/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
);
Expand All @@ -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)),
Expand Down
19 changes: 10 additions & 9 deletions src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand All @@ -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<P>(pub(crate) P);

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

#[cfg(feature = "borsh")]
impl<P: borsh::BorshSerialize> borsh::BorshSerialize for HomomorphicCommitment<P> {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
fn serialize<W: borsh::maybestd::io::Write>(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> {
self.0.serialize(writer)
}
}
Expand Down Expand Up @@ -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(),
}),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/deterministic_randomizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
2 changes: 1 addition & 1 deletion src/dhke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
95 changes: 60 additions & 35 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ByteArrayError> 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,
},
}
26 changes: 14 additions & 12 deletions src/extended_range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Extended range proofs

use std::{string::ToString, vec::Vec};

use crate::{
commitment::{ExtensionDegree, HomomorphicCommitment},
errors::RangeProofError,
Expand Down Expand Up @@ -110,9 +112,9 @@ where K: SecretKey
/// Construct a new extended mask
pub fn assign(extension_degree: ExtensionDegree, secrets: Vec<K>) -> Result<ExtendedMask<K>, 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 })
}
Expand Down Expand Up @@ -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<Statement<PK>>) -> Result<Self, RangeProofError> {
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 })
}
Expand All @@ -180,14 +182,14 @@ where PK: PublicKey
/// - mask recovery is not supported with an aggregated statement/proof
pub fn init(statements: Vec<Statement<PK>>, recovery_seed_nonce: Option<PK::K>) -> Result<Self, RangeProofError> {
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,
Expand Down
Loading
Loading