diff --git a/zebra-chain/src/error.rs b/zebra-chain/src/error.rs new file mode 100644 index 00000000000..a3182a21feb --- /dev/null +++ b/zebra-chain/src/error.rs @@ -0,0 +1,53 @@ +//! Errors that can occur inside any `zebra-chain` submodule. +use thiserror::Error; + +/// Errors related to random bytes generation. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum RandError { + /// Error of the `try_fill_bytes` function. + #[error("failed to generate a secure stream of random bytes")] + FillBytes, +} + +/// An error type pertaining to shielded notes. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum NoteError { + /// Errors of type `RandError`. + #[error("Randomness generation failure")] + InsufficientRandomness(#[from] RandError), + /// Error of `pallas::Point::from_bytes()` for new rho randomness. + #[error("failed to generate an Orchard note's rho.")] + InvalidRho, +} + +/// An error type pertaining to note commitments. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum NoteCommitmentError { + /// Errors of type `RandError`. + #[error("Randomness generation failure")] + InsufficientRandomness(#[from] RandError), + /// Error of `jubjub::AffinePoint::try_from`. + #[error("failed to generate a sapling::NoteCommitment from a diversifier")] + InvalidDiversifier, +} + +/// An error type pertaining to key generation, parsing, modification, +/// randomization. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum KeyError { + /// Errors of type `RandError`. + #[error("Randomness generation failure")] + InsufficientRandomness(#[from] RandError), +} + +/// An error type pertaining to payment address generation, parsing, +/// modification, diversification. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +pub enum AddressError { + /// Errors of type `RandError`. + #[error("Randomness generation failure")] + InsufficientRandomness(#[from] RandError), + /// Errors pertaining to diversifier generation. + #[error("Randomness did not hash into the Jubjub group for producing a new diversifier")] + DiversifierGenerationFailure, +} diff --git a/zebra-chain/src/lib.rs b/zebra-chain/src/lib.rs index d96e681e0be..eee58e7e9e1 100644 --- a/zebra-chain/src/lib.rs +++ b/zebra-chain/src/lib.rs @@ -23,6 +23,7 @@ pub mod block; pub mod chain_sync_status; pub mod chain_tip; pub mod diagnostic; +pub mod error; pub mod fmt; pub mod history_tree; pub mod orchard; diff --git a/zebra-chain/src/orchard/commitment.rs b/zebra-chain/src/orchard/commitment.rs index ccd23cad469..4e69258c4e5 100644 --- a/zebra-chain/src/orchard/commitment.rs +++ b/zebra-chain/src/orchard/commitment.rs @@ -16,6 +16,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ amount::Amount, + error::RandError, serialization::{ serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, }, @@ -26,14 +27,16 @@ use super::sinsemilla::*; /// Generates a random scalar from the scalar field 𝔽_{q_P}. /// /// -pub fn generate_trapdoor(csprng: &mut T) -> pallas::Scalar +pub fn generate_trapdoor(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { let mut bytes = [0u8; 64]; - csprng.fill_bytes(&mut bytes); - // pallas::Scalar::from_bytes_wide() reduces the input modulo q_P under the hood. - pallas::Scalar::from_uniform_bytes(&bytes) + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| RandError::FillBytes)?; + // pallas::Scalar::from_uniform_bytes() reduces the input modulo q_P under the hood. + Ok(pallas::Scalar::from_uniform_bytes(&bytes)) } /// The randomness used in the Simsemilla hash for note commitment. @@ -227,13 +230,13 @@ impl ValueCommitment { /// Generate a new _ValueCommitment_. /// /// - pub fn randomized(csprng: &mut T, value: Amount) -> Self + pub fn randomized(csprng: &mut T, value: Amount) -> Result where T: RngCore + CryptoRng, { - let rcv = generate_trapdoor(csprng); + let rcv = generate_trapdoor(csprng)?; - Self::new(rcv, value) + Ok(Self::new(rcv, value)) } /// Generate a new `ValueCommitment` from an existing `rcv on a `value`. diff --git a/zebra-chain/src/orchard/keys.rs b/zebra-chain/src/orchard/keys.rs index 7fdbfb81045..3fd9e0ce67a 100644 --- a/zebra-chain/src/orchard/keys.rs +++ b/zebra-chain/src/orchard/keys.rs @@ -13,8 +13,11 @@ use halo2::{ }; use rand_core::{CryptoRng, RngCore}; -use crate::serialization::{ - serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, +use crate::{ + error::RandError, + serialization::{ + serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, + }, }; use super::sinsemilla::*; @@ -102,14 +105,16 @@ impl Diversifier { /// Generate a new `Diversifier`. /// /// - pub fn new(csprng: &mut T) -> Self + pub fn new(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { let mut bytes = [0u8; 11]; - csprng.fill_bytes(&mut bytes); + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| RandError::FillBytes)?; - Self::from(bytes) + Ok(Self::from(bytes)) } } diff --git a/zebra-chain/src/orchard/note.rs b/zebra-chain/src/orchard/note.rs index 5eb78236b09..8d46220ff86 100644 --- a/zebra-chain/src/orchard/note.rs +++ b/zebra-chain/src/orchard/note.rs @@ -4,7 +4,10 @@ use group::{ff::PrimeField, GroupEncoding}; use halo2::pasta::pallas; use rand_core::{CryptoRng, RngCore}; -use crate::amount::{Amount, NonNegative}; +use crate::{ + amount::{Amount, NonNegative}, + error::{NoteError, RandError}, +}; use super::{address::Address, sinsemilla::extract_p}; @@ -22,13 +25,15 @@ mod arbitrary; pub struct SeedRandomness(pub(crate) [u8; 32]); impl SeedRandomness { - pub fn new(csprng: &mut T) -> Self + pub fn new(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - Self(bytes) + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| RandError::FillBytes)?; + Ok(Self(bytes)) } } @@ -57,14 +62,22 @@ impl From for Rho { } impl Rho { - pub fn new(csprng: &mut T) -> Self + pub fn new(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| NoteError::from(RandError::FillBytes))?; - Self(extract_p(pallas::Point::from_bytes(&bytes).unwrap())) + let possible_point = pallas::Point::from_bytes(&bytes); + + if possible_point.is_some().into() { + Ok(Self(extract_p(possible_point.unwrap()))) + } else { + Err(NoteError::InvalidRho) + } } } @@ -108,15 +121,15 @@ impl Note { address: Address, value: Amount, nf_old: Nullifier, - ) -> Self + ) -> Result where T: RngCore + CryptoRng, { - Self { + Ok(Self { address, value, rho: nf_old.into(), - rseed: SeedRandomness::new(csprng), - } + rseed: SeedRandomness::new(csprng)?, + }) } } diff --git a/zebra-chain/src/sapling/commitment.rs b/zebra-chain/src/sapling/commitment.rs index 9e0c5983b2f..65c789dc6ed 100644 --- a/zebra-chain/src/sapling/commitment.rs +++ b/zebra-chain/src/sapling/commitment.rs @@ -12,6 +12,7 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ amount::{Amount, NonNegative}, + error::{NoteCommitmentError, RandError}, serialization::{ serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, }, @@ -34,14 +35,16 @@ use pedersen_hashes::*; /// trapdoor generators. /// /// -pub fn generate_trapdoor(csprng: &mut T) -> jubjub::Fr +pub fn generate_trapdoor(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { let mut bytes = [0u8; 64]; - csprng.fill_bytes(&mut bytes); + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| RandError::FillBytes)?; // Fr::from_bytes_wide() reduces the input modulo r via Fr::from_u512() - jubjub::Fr::from_bytes_wide(&bytes) + Ok(jubjub::Fr::from_bytes_wide(&bytes)) } /// The randomness used in the Pedersen Hash for note commitment. @@ -104,7 +107,7 @@ impl NoteCommitment { diversifier: Diversifier, transmission_key: TransmissionKey, value: Amount, - ) -> Option<(CommitmentRandomness, Self)> + ) -> Result<(CommitmentRandomness, Self), NoteCommitmentError> where T: RngCore + CryptoRng, { @@ -120,11 +123,9 @@ impl NoteCommitment { // The `TryFrom` impls for the `jubjub::*Point`s handles // calling `DiversifyHash` implicitly. - let g_d_bytes: [u8; 32] = if let Ok(g_d) = jubjub::AffinePoint::try_from(diversifier) { - g_d.to_bytes() - } else { - return None; - }; + let g_d_bytes = jubjub::AffinePoint::try_from(diversifier) + .map_err(|_| NoteCommitmentError::InvalidDiversifier)? + .to_bytes(); let pk_d_bytes = <[u8; 32]>::from(transmission_key); let v_bytes = value.to_bytes(); @@ -133,9 +134,9 @@ impl NoteCommitment { s.extend(pk_d_bytes); s.extend(v_bytes); - let rcm = CommitmentRandomness(generate_trapdoor(csprng)); + let rcm = CommitmentRandomness(generate_trapdoor(csprng)?); - Some(( + Ok(( rcm, NoteCommitment::from(windowed_pedersen_commitment(rcm.0, &s)), )) @@ -265,13 +266,13 @@ impl ValueCommitment { /// Generate a new _ValueCommitment_. /// /// - pub fn randomized(csprng: &mut T, value: Amount) -> Self + pub fn randomized(csprng: &mut T, value: Amount) -> Result where T: RngCore + CryptoRng, { - let rcv = generate_trapdoor(csprng); + let rcv = generate_trapdoor(csprng)?; - Self::new(rcv, value) + Ok(Self::new(rcv, value)) } /// Generate a new _ValueCommitment_ from an existing _rcv_ on a _value_. diff --git a/zebra-chain/src/sapling/keys.rs b/zebra-chain/src/sapling/keys.rs index fabe2e2970c..2037e368a12 100644 --- a/zebra-chain/src/sapling/keys.rs +++ b/zebra-chain/src/sapling/keys.rs @@ -15,6 +15,7 @@ use std::{fmt, io}; use rand_core::{CryptoRng, RngCore}; use crate::{ + error::{AddressError, RandError}, primitives::redjubjub::{self, SpendAuth}, serialization::{ serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize, @@ -180,18 +181,24 @@ impl Diversifier { /// /// /// - pub fn new(csprng: &mut T) -> Self + pub fn new(csprng: &mut T) -> Result where T: RngCore + CryptoRng, { - loop { + /// Number of times a `diversify_hash` will try to obtain a diversified base point. + const DIVERSIFY_HASH_TRIES: u8 = 2; + + for _ in 0..DIVERSIFY_HASH_TRIES { let mut bytes = [0u8; 11]; - csprng.fill_bytes(&mut bytes); + csprng + .try_fill_bytes(&mut bytes) + .map_err(|_| AddressError::from(RandError::FillBytes))?; if diversify_hash(bytes).is_some() { - break Self(bytes); + return Ok(Self(bytes)); } } + Err(AddressError::DiversifierGenerationFailure) } }