From 805ef4b5c01547ebed1c89b97bbf8a2aae81d6f4 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Mon, 30 Jan 2023 18:16:53 -0300 Subject: [PATCH] port improvements from Zebra (#40) * simplify fmt::Debug impls with new hex_if_possible() * Update src/signature.rs authors Co-authored-by: Deirdre Connolly --- Cargo.toml | 3 ++- src/batch.rs | 16 ++++++++++++++-- src/lib.rs | 12 ++++++++++++ src/orchard.rs | 8 ++++++-- src/scalar_mul.rs | 16 +++++++++++++++- src/signature.rs | 16 +++++++++++++--- src/verification_key.rs | 13 +++++++++++-- 7 files changed, 73 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 823083f..e13af68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ features = ["nightly"] blake2b_simd = { version = "1", default-features = false } byteorder = { version = "1.4", default-features = false } group = { version = "0.12", default-features = false } +hex = { version = "0.4", optional = true, default-features = false, features = ["alloc"] } jubjub = { version = "0.9", default-features = false } pasta_curves = { version = "0.4", default-features = false } rand_core = { version = "0.6", default-features = false } @@ -53,7 +54,7 @@ features = ["alloc"] [features] std = ["blake2b_simd/std", "thiserror", "zeroize", "alloc", "serde"] # conditional compilation for serde not complete (issue #9) -alloc = [] +alloc = ["hex"] nightly = [] default = ["std"] diff --git a/src/batch.rs b/src/batch.rs index af3a1bc..c4bc566 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -30,8 +30,10 @@ use rand_core::{CryptoRng, RngCore}; use crate::{private::SealedScalar, scalar_mul::VartimeMultiscalarMul, *}; -// Shim to generate a random 128bit value in a [u64; 4], without -// importing `rand`. +/// Shim to generate a random 128 bit value in a `[u64; 4]`, without +/// importing `rand`. +/// +/// The final 128 bits are zero. fn gen_128_bits(mut rng: R) -> [u64; 4] { let mut bytes = [0u64; 4]; bytes[0] = rng.next_u64(); @@ -39,13 +41,23 @@ fn gen_128_bits(mut rng: R) -> [u64; 4] { bytes } +/// Inner type of a batch verification item. +/// +/// This struct exists to allow batch processing to be decoupled from the +/// lifetime of the message. This is useful when using the batch verification +/// API in an async context +/// +/// The different enum variants are for the different signature types which use +/// different basepoints for computation: SpendAuth and Binding signatures. #[derive(Clone, Debug)] enum Inner> { + /// A RedDSA signature using the SpendAuth generator group element. SpendAuth { vk_bytes: VerificationKeyBytes, sig: Signature, c: S::Scalar, }, + /// A RedDSA signature using the Binding generator group element. Binding { vk_bytes: VerificationKeyBytes, sig: Signature, diff --git a/src/lib.rs b/src/lib.rs index fc6a1b6..ee689f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,3 +119,15 @@ pub(crate) mod private { } } } + +/// Return the given byte array as a hex-encoded string. +#[cfg(feature = "alloc")] +pub(crate) fn hex_if_possible(bytes: &[u8]) -> alloc::string::String { + hex::encode(bytes) +} + +/// Return the given byte array. +#[cfg(not(feature = "alloc"))] +pub(crate) fn hex_if_possible(bytes: &[u8]) -> &[u8] { + bytes +} diff --git a/src/orchard.rs b/src/orchard.rs index 9573cc7..0bc0126 100644 --- a/src/orchard.rs +++ b/src/orchard.rs @@ -15,13 +15,17 @@ use crate::{private, SigType}; #[cfg(feature = "alloc")] use crate::scalar_mul::{LookupTable5, NonAdjacentForm, VartimeMultiscalarMul}; -/// The byte-encoding of the basepoint for `OrchardSpendAuthSig`. +/// The byte-encoding of the basepoint for the Orchard `SpendAuthSig` on the [Pallas curve][pallasandvesta]. +/// +/// [pallasandvesta]: https://zips.z.cash/protocol/nu5.pdf#pallasandvesta +// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard")(b"G").to_bytes() const ORCHARD_SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [ 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, 124, 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183, ]; -/// The byte-encoding of the basepoint for `OrchardBindingSig`. +/// The byte-encoding of the basepoint for the Orchard `BindingSig` on the Pallas curve. +// Reproducible by pallas::Point::hash_to_curve("z.cash:Orchard-cv")(b"r").to_bytes() const ORCHARD_BINDINGSIG_BASEPOINT_BYTES: [u8; 32] = [ 145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, 91, 35, 102, 79, 187, 9, 164, 15, 85, 68, 244, 7, diff --git a/src/scalar_mul.rs b/src/scalar_mul.rs index 0635e9d..2d619ea 100644 --- a/src/scalar_mul.rs +++ b/src/scalar_mul.rs @@ -10,6 +10,8 @@ // - Henry de Valence // - Deirdre Connolly +//! Traits and types that support variable-time multiscalar multiplication. + use alloc::vec::Vec; use core::{borrow::Borrow, fmt::Debug}; @@ -65,7 +67,9 @@ pub trait VartimeMultiscalarMul { impl NonAdjacentForm for jubjub::Scalar { /// Compute a width-\\(w\\) "Non-Adjacent Form" of this scalar. /// - /// Thanks to curve25519-dalek + /// Thanks to [`curve25519-dalek`]. + /// + /// [`curve25519-dalek`]: https://github.com/dalek-cryptography/curve25519-dalek/blob/3e189820da03cc034f5fa143fc7b2ccb21fffa5e/src/scalar.rs#L907 fn non_adjacent_form(&self, w: usize) -> [i8; 256] { // required by the NAF definition debug_assert!(w >= 2); @@ -160,6 +164,16 @@ impl VartimeMultiscalarMul for ExtendedPoint { type Scalar = jubjub::Scalar; type Point = ExtendedPoint; + /// Variable-time multiscalar multiplication using a non-adjacent form of + /// width (5). + /// + /// The non-adjacent form has signed, odd digits. Using only odd digits + /// halves the table size (since we only need odd multiples), or gives fewer + /// additions for the same table size. + /// + /// As the name implies, the runtime varies according to the values of the + /// inputs, thus is not safe for computing over secret data, but is great + /// for computing over public data, such as validating signatures. #[allow(non_snake_case)] fn optional_multiscalar_mul(scalars: I, points: J) -> Option where diff --git a/src/signature.rs b/src/signature.rs index 9236ef1..1ab3b42 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -6,14 +6,15 @@ // // Authors: // - Henry de Valence +// - Conrado Gouvea //! RedDSA Signatures -use core::marker::PhantomData; +use core::{fmt, marker::PhantomData}; -use crate::SigType; +use crate::{hex_if_possible, SigType}; /// A RedDSA signature. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Signature { pub(crate) r_bytes: [u8; 32], @@ -21,6 +22,15 @@ pub struct Signature { pub(crate) _marker: PhantomData, } +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Signature") + .field("r_bytes", &hex_if_possible(&self.r_bytes)) + .field("s_bytes", &hex_if_possible(&self.s_bytes)) + .finish() + } +} + impl From<[u8; 64]> for Signature { fn from(bytes: [u8; 64]) -> Signature { let mut r_bytes = [0; 32]; diff --git a/src/verification_key.rs b/src/verification_key.rs index 8374e0d..eac342c 100644 --- a/src/verification_key.rs +++ b/src/verification_key.rs @@ -10,13 +10,14 @@ use core::{ convert::{TryFrom, TryInto}, + fmt, hash::Hash, marker::PhantomData, }; use group::{cofactor::CofactorGroup, ff::PrimeField, GroupEncoding}; -use crate::{Error, Randomizer, SigType, Signature, SpendAuth}; +use crate::{hex_if_possible, Error, Randomizer, SigType, Signature, SpendAuth}; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedDSA verification key. @@ -24,13 +25,21 @@ use crate::{Error, Randomizer, SigType, Signature, SpendAuth}; /// This is useful for representing a compressed verification key; the /// [`VerificationKey`] type in this library holds other decompressed state /// used in signature verification. -#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct VerificationKeyBytes { pub(crate) bytes: [u8; 32], pub(crate) _marker: PhantomData, } +impl fmt::Debug for VerificationKeyBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("VerificationKeyBytes") + .field("bytes", &hex_if_possible(&self.bytes)) + .finish() + } +} + impl From<[u8; 32]> for VerificationKeyBytes { fn from(bytes: [u8; 32]) -> VerificationKeyBytes { VerificationKeyBytes {