Merge pull request #1056 from zcash/581-use-redjubjub-crate

zcash_primitives: Replace `sapling::redjubjub` with `redjubjub` crate
This commit is contained in:
Daira Emma Hopwood 2023-12-04 18:27:32 +00:00 committed by GitHub
commit acf146248e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 887 additions and 664 deletions

View File

@ -45,6 +45,8 @@ and this library adheres to Rust's notion of
- `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}`
- `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}`
- `constants` module.
- `keys::SpendAuthorizingKey`
- `keys::SpendValidatingKey`
- `note_encryption::CompactOutputDescription` (moved from
`zcash_primitives::transaction::components::sapling`).
- `note_encryption::SaplingDomain::new`
@ -100,6 +102,10 @@ and this library adheres to Rust's notion of
- `zcash_primitives::sapling`:
- `BatchValidator::validate` now takes the `SpendVerifyingKey` and
`OutputVerifyingKey` newtypes.
- `SaplingVerificationContext::new` now always creates a context with ZIP 216
rules enforced, and no longer has a boolean for configuring this.
- `SaplingVerificationContext::{check_spend, final_check}` now use the
`redjubjub` crate types for `rk`, `spend_auth_sig`, and `binding_sig`.
- `SaplingVerificationContext::{check_spend, check_output}` now take
the `PreparedSpendVerifyingKey` and `PreparedOutputVerifyingKey`
newtypes.
@ -127,11 +133,23 @@ and this library adheres to Rust's notion of
- `Error::MissingSignatures`
- `bundle::Bundle` now has a second generic parameter `V`.
- `bundle::Bundle::value_balance` now returns `&V` instead of `&Amount`.
- `bundle::Authorized::binding_sig` now has type `redjubjub::Signature<Binding>`.
- `bundle::Authorized::AuthSig` now has type `redjubjub::Signature<SpendAuth>`.
- `bundle::SpendDescription::temporary_zcashd_from_parts` now takes `rk` as
`redjubjub::VerificationKey<SpendAuth>` instead of
`zcash_primitives::sapling::redjubjub::PublicKey`.
- `bundle::SpendDescription::rk` now returns `&redjubjub::VerificationKey<SpendAuth>`.
- `bundle::SpendDescriptionV5::into_spend_description` now takes
`spend_auth_sig` as `redjubjub::Signature<SpendAuth>` instead of
`zcash_primitives::sapling::redjubjub::Signature`.
- `bundle::testing::arb_bundle` now takes a `value_balance: V` argument.
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
instead of as a bare `u64`.
- `keys::DecodingError` has a new variant `UnsupportedChildIndex`.
- `keys::ExpandedSpendingKey.ask` now has type `SpendAuthorizingKey`.
- `keys::ProofGenerationKey.ak` now has type `SpendValidatingKey`.
- `keys::ViewingKey.ak` now has type `SpendValidatingKey`.
- `note_encryption`:
- `SaplingDomain` no longer has a `P: consensus::Parameters` type parameter.
- The following methods now take a `Zip212Enforcement` argument instead of a
@ -150,6 +168,11 @@ and this library adheres to Rust's notion of
- `try_sapling_output_recovery`
- `util::generate_random_rseed` now takes a `Zip212Enforcement` argument
instead of a `P: consensus::Parameters` argument and a height.
- `value::TrapdoorSum::into_bsk` now returns `redjubjub::SigningKey<Binding>`
instead of `zcash_primitives::sapling::redjubjub::PrivateKey`.
- `value::CommitmentSum::into_bvk` now returns
`redjubjub::VerificationKey<Binding>` instead of
`zcash_primitives::sapling::redjubjub::PublicKey`.
- `zcash_primitives::transaction`:
- `builder::Builder::{build, build_zfuture}` now take
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
@ -192,6 +215,8 @@ and this library adheres to Rust's notion of
- `OutputDescriptionV5::read`
- `note_encryption::SaplingDomain::for_height` (use `SaplingDomain::new`
instead).
- `redjubjub` module (use the `redjubjub` crate instead).
- `spend_sig` (use `redjubjub::SigningKey::{randomize, sign}` instead).
- `zcash_primitives::transaction::components::sapling`:
- The following types were removed from this module (moved into
`zcash_primitives::sapling::bundle`):
@ -217,6 +242,12 @@ and this library adheres to Rust's notion of
- `ChildIndex::NonHardened`
- `sapling::ExtendedFullViewingKey::derive_child`
### Fixed
- `zcash_primitives::keys::ExpandedSpendingKey::from_spending_key` now panics if the
spending key expands to `ask = 0`. This has a negligible probability of occurring.
- `zcash_primitives::zip32::ExtendedSpendingKey::derive_child` now panics if the
child key has `ask = 0`. This has a negligible probability of occurring.
## [0.13.0] - 2023-09-25
### Added
- `zcash_primitives::consensus::BlockHeight::saturating_sub`

View File

@ -11,7 +11,6 @@ pub mod note;
pub mod note_encryption;
pub mod pedersen_hash;
pub mod prover;
pub mod redjubjub;
mod spec;
mod tree;
pub mod util;
@ -19,13 +18,6 @@ pub mod value;
mod verifier;
pub mod zip32;
use group::GroupEncoding;
use rand_core::{CryptoRng, RngCore};
use constants::SPENDING_KEY_GENERATOR;
use self::redjubjub::{PrivateKey, PublicKey, Signature};
pub use address::PaymentAddress;
pub use bundle::Bundle;
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
@ -35,60 +27,6 @@ pub use tree::{
};
pub use verifier::{BatchValidator, SaplingVerificationContext};
/// Create the spendAuthSig for a Sapling SpendDescription.
pub fn spend_sig<R: RngCore + CryptoRng>(
ask: PrivateKey,
ar: jubjub::Fr,
sighash: &[u8; 32],
rng: &mut R,
) -> Signature {
spend_sig_internal(&ask, ar, sighash, rng)
}
pub(crate) fn spend_sig_internal<R: RngCore>(
ask: &PrivateKey,
ar: jubjub::Fr,
sighash: &[u8; 32],
rng: &mut R,
) -> Signature {
// We compute `rsk`...
let rsk = ask.randomize(ar);
// We compute `rk` from there (needed for key prefixing)
let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR);
// Compute the signature's message for rk/spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
data_to_be_signed[32..64].copy_from_slice(&sighash[..]);
// Do the signing
rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR)
}
/// Verifies a spendAuthSig.
///
/// This only exists because the RedJubjub implementation inside `zcash_primitives` does
/// not implement key prefixing (which was added in response to a Sapling audit). This
/// will be fixed by migrating to the redjubjub crate.
pub(crate) fn verify_spend_sig(
ak: &PublicKey,
alpha: jubjub::Fr,
sighash: &[u8; 32],
sig: &Signature,
) -> bool {
// We compute `rk` (needed for key prefixing)
let rk = ak.randomize(alpha, SPENDING_KEY_GENERATOR);
// Compute the signature's message for rk/spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
data_to_be_signed[32..64].copy_from_slice(&sighash[..]);
// Do the verifying
rk.verify(&data_to_be_signed, sig, SPENDING_KEY_GENERATOR)
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
pub use super::{
@ -96,3 +34,6 @@ pub mod testing {
note::testing::arb_note, tree::testing::arb_node,
};
}
#[cfg(test)]
mod test_vectors;

View File

@ -3,9 +3,10 @@
use core::fmt;
use std::{marker::PhantomData, sync::mpsc::Sender};
use ff::Field;
use group::ff::Field;
use rand::{seq::SliceRandom, RngCore};
use rand_core::CryptoRng;
use redjubjub::{Binding, SpendAuth};
use crate::{
sapling::{
@ -14,17 +15,13 @@ use crate::{
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
SpendDescription,
},
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
keys::OutgoingViewingKey,
keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey},
note_encryption::{sapling_note_encryption, Zip212Enforcement},
prover::{OutputProver, SpendProver},
redjubjub::{PrivateKey, PublicKey, Signature},
spend_sig_internal,
util::generate_random_rseed_internal,
value::{
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
},
verify_spend_sig,
zip32::ExtendedSpendingKey,
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
},
@ -117,10 +114,10 @@ impl SpendDescriptionInfo {
// Construct the value commitment.
let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone());
let ak = PublicKey(self.proof_generation_key.ak.into());
let ak = self.proof_generation_key.ak.clone();
// This is the result of the re-randomization, we compute it for the caller
let rk = ak.randomize(self.alpha, SPENDING_KEY_GENERATOR);
let rk = ak.randomize(&self.alpha);
let nullifier = self.note.nf(
&self.proof_generation_key.to_viewing_key().nk,
@ -507,10 +504,7 @@ impl SaplingBuilder {
(spends - outputs)
.into_bvk(i64::try_from(self.value_balance).map_err(|_| Error::InvalidAmount)?)
};
assert_eq!(
PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0,
bvk.0,
);
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
let bundle = if shielded_spends.is_empty() && shielded_outputs.is_empty() {
None
@ -676,16 +670,9 @@ impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
}
/// Marker for an unauthorized bundle with no signatures.
#[derive(Clone)]
pub struct Unsigned {
bsk: PrivateKey,
}
impl Clone for Unsigned {
fn clone(&self) -> Self {
Self {
bsk: PrivateKey(self.bsk.0),
}
}
bsk: redjubjub::SigningKey<Binding>,
}
impl fmt::Debug for Unsigned {
@ -703,7 +690,7 @@ impl InProgressSignatures for Unsigned {
pub struct SigningParts {
/// The spend validating key for this spend description. Used to match spend
/// authorizing keys to spend descriptions they can create signatures for.
ak: PublicKey,
ak: SpendValidatingKey,
/// The randomization needed to derive the actual signing key for this note.
alpha: jubjub::Scalar,
}
@ -711,7 +698,7 @@ pub struct SigningParts {
/// Marker for a partially-authorized bundle, in the process of being signed.
#[derive(Clone, Debug)]
pub struct PartiallyAuthorized {
binding_signature: Signature,
binding_signature: redjubjub::Signature<Binding>,
sighash: [u8; 32],
}
@ -720,16 +707,18 @@ impl InProgressSignatures for PartiallyAuthorized {
}
/// A heisen[`Signature`] for a particular [`SpendDescription`].
///
/// [`Signature`]: redjubjub::Signature
#[derive(Clone, Debug)]
pub enum MaybeSigned {
/// The information needed to sign this [`SpendDescription`].
SigningMetadata(SigningParts),
/// The signature for this [`SpendDescription`].
Signature(Signature),
Signature(redjubjub::Signature<SpendAuth>),
}
impl MaybeSigned {
fn finalize(self) -> Result<Signature, Error> {
fn finalize(self) -> Result<redjubjub::Signature<SpendAuth>, Error> {
match self {
Self::Signature(sig) => Ok(sig),
_ => Err(Error::MissingSignatures),
@ -752,11 +741,7 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, Unsigned>, V> {
MaybeSigned::SigningMetadata,
|auth: InProgress<P, Unsigned>| InProgress {
sigs: PartiallyAuthorized {
binding_signature: auth.sigs.bsk.sign(
&sighash,
&mut rng,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
),
binding_signature: auth.sigs.bsk.sign(&mut rng, &sighash),
sighash,
},
_proof_state: PhantomData::default(),
@ -774,7 +759,7 @@ impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
self,
mut rng: R,
sighash: [u8; 32],
signing_keys: &[PrivateKey],
signing_keys: &[SpendAuthorizingKey],
) -> Result<Bundle<Authorized, V>, Error> {
signing_keys
.iter()
@ -786,18 +771,18 @@ impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
}
impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
/// Signs this bundle with the given [`PrivateKey`].
/// Signs this bundle with the given [`redjubjub::SigningKey`].
///
/// This will apply signatures for all notes controlled by this spending key.
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &PrivateKey) -> Self {
let expected_ak = PublicKey::from_private(ask, SPENDING_KEY_GENERATOR);
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &SpendAuthorizingKey) -> Self {
let expected_ak = ask.into();
let sighash = self.authorization().sigs.sighash;
self.map_authorization((
|proof| proof,
|proof| proof,
|maybe| match maybe {
MaybeSigned::SigningMetadata(parts) if parts.ak.0 == expected_ak.0 => {
MaybeSigned::Signature(spend_sig_internal(ask, parts.alpha, &sighash, &mut rng))
MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => {
MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(&mut rng, &sighash))
}
s => s,
},
@ -805,16 +790,19 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
))
}
/// Appends externally computed [`Signature`]s.
/// Appends externally computed [`redjubjub::Signature`]s.
///
/// Each signature will be applied to the one input for which it is valid. An error
/// will be returned if the signature is not valid for any inputs, or if it is valid
/// for more than one input.
pub fn append_signatures(self, signatures: &[Signature]) -> Result<Self, Error> {
pub fn append_signatures(
self,
signatures: &[redjubjub::Signature<SpendAuth>],
) -> Result<Self, Error> {
signatures.iter().try_fold(self, Self::append_signature)
}
fn append_signature(self, signature: &Signature) -> Result<Self, Error> {
fn append_signature(self, signature: &redjubjub::Signature<SpendAuth>) -> Result<Self, Error> {
let sighash = self.authorization().sigs.sighash;
let mut signature_valid_for = 0usize;
let bundle = self.map_authorization((
@ -822,7 +810,8 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|proof| proof,
|maybe| match maybe {
MaybeSigned::SigningMetadata(parts) => {
if verify_spend_sig(&parts.ak, parts.alpha, &sighash, signature) {
let rk = parts.ak.randomize(&parts.alpha);
if rk.verify(&sighash, signature).is_ok() {
signature_valid_for += 1;
MaybeSigned::Signature(*signature)
} else {
@ -872,7 +861,6 @@ pub mod testing {
bundle::{Authorized, Bundle},
note_encryption::Zip212Enforcement,
prover::mock::{MockOutputProver, MockSpendProver},
redjubjub::PrivateKey,
testing::{arb_node, arb_note},
value::testing::arb_positive_note_value,
zip32::testing::arb_extended_spending_key,
@ -926,11 +914,7 @@ pub mod testing {
bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, None);
bundle
.apply_signatures(
&mut rng,
fake_sighash_bytes,
&[PrivateKey(extsk.expsk.ask)],
)
.apply_signatures(&mut rng, fake_sighash_bytes, &[extsk.expsk.ask])
.unwrap()
},
)

View File

@ -1,6 +1,7 @@
use core::fmt::Debug;
use memuse::DynamicUsage;
use redjubjub::{Binding, SpendAuth};
use zcash_note_encryption::{
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
@ -10,7 +11,6 @@ use crate::sapling::{
circuit::GROTH_PROOF_SIZE,
note::ExtractedNoteCommitment,
note_encryption::{CompactOutputDescription, SaplingDomain},
redjubjub::{self, PublicKey, Signature},
value::ValueCommitment,
Nullifier,
};
@ -29,13 +29,13 @@ pub trait Authorization: Debug {
#[derive(Debug, Copy, Clone)]
pub struct Authorized {
// TODO: Make this private.
pub binding_sig: redjubjub::Signature,
pub binding_sig: redjubjub::Signature<Binding>,
}
impl Authorization for Authorized {
type SpendProof = GrothProofBytes;
type OutputProof = GrothProofBytes;
type AuthSig = redjubjub::Signature;
type AuthSig = redjubjub::Signature<SpendAuth>;
}
/// A map from one bundle authorization to another.
@ -315,7 +315,7 @@ pub struct SpendDescription<A: Authorization> {
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
rk: redjubjub::VerificationKey<SpendAuth>,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
}
@ -336,7 +336,7 @@ impl<A: Authorization> SpendDescription<A> {
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
rk: redjubjub::VerificationKey<SpendAuth>,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
@ -347,7 +347,7 @@ impl<A: Authorization> SpendDescription<A> {
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
rk: redjubjub::VerificationKey<SpendAuth>,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
@ -377,7 +377,7 @@ impl<A: Authorization> SpendDescription<A> {
}
/// Returns the randomized verification key for the note being spent.
pub fn rk(&self) -> &PublicKey {
pub fn rk(&self) -> &redjubjub::VerificationKey<SpendAuth> {
&self.rk
}
@ -406,11 +406,15 @@ impl DynamicUsage for SpendDescription<Authorized> {
pub struct SpendDescriptionV5 {
cv: ValueCommitment,
nullifier: Nullifier,
rk: PublicKey,
rk: redjubjub::VerificationKey<SpendAuth>,
}
impl SpendDescriptionV5 {
pub(crate) fn from_parts(cv: ValueCommitment, nullifier: Nullifier, rk: PublicKey) -> Self {
pub(crate) fn from_parts(
cv: ValueCommitment,
nullifier: Nullifier,
rk: redjubjub::VerificationKey<SpendAuth>,
) -> Self {
Self { cv, nullifier, rk }
}
@ -418,7 +422,7 @@ impl SpendDescriptionV5 {
self,
anchor: bls12_381::Scalar,
zkproof: GrothProofBytes,
spend_auth_sig: Signature,
spend_auth_sig: redjubjub::Signature<SpendAuth>,
) -> SpendDescription<Authorized> {
SpendDescription {
cv: self.cv,
@ -627,9 +631,7 @@ pub mod testing {
use crate::{
sapling::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
note::ExtractedNoteCommitment,
redjubjub::{PrivateKey, PublicKey},
value::{
testing::{arb_note_value_bounded, arb_trapdoor},
ValueCommitment, MAX_NOTE_VALUE,
@ -669,8 +671,8 @@ pub mod testing {
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
) -> SpendDescription<Authorized> {
let mut rng = StdRng::from_seed(rng_seed);
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
let rk = PublicKey::from_private(&sk1, SPENDING_KEY_GENERATOR);
let sk1 = redjubjub::SigningKey::new(&mut rng);
let rk = redjubjub::VerificationKey::from(&sk1);
let cv = ValueCommitment::derive(value, rcv);
SpendDescription {
cv,
@ -678,7 +680,7 @@ pub mod testing {
nullifier,
rk,
zkproof,
spend_auth_sig: sk1.sign(&fake_sighash_bytes, &mut rng, SPENDING_KEY_GENERATOR),
spend_auth_sig: sk1.sign(&mut rng, &fake_sighash_bytes),
}
}
}
@ -731,18 +733,14 @@ pub mod testing {
None
} else {
let mut rng = StdRng::from_seed(rng_seed);
let bsk = PrivateKey(jubjub::Fr::random(&mut rng));
let bsk = redjubjub::SigningKey::new(&mut rng);
Some(Bundle {
shielded_spends,
shielded_outputs,
value_balance,
authorization: Authorized {
binding_sig: bsk.sign(
&fake_bvk_bytes,
&mut rng,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
),
binding_sig: bsk.sign(&mut rng, &fake_bvk_bytes),
},
})
}

View File

@ -160,7 +160,7 @@ impl Circuit<bls12_381::Scalar> for Spend {
// Prover witnesses ak (ensures that it's on the curve)
let ak = ecc::EdwardsPoint::witness(
cs.namespace(|| "ak"),
self.proof_generation_key.as_ref().map(|k| k.ak.into()),
self.proof_generation_key.as_ref().map(|k| (&k.ak).into()),
)?;
// There are no sensible attacks on small order points
@ -634,9 +634,12 @@ pub struct PreparedOutputVerifyingKey(pub(crate) groth16::PreparedVerifyingKey<B
#[test]
fn test_input_circuit_with_bls12_381() {
use crate::sapling::{pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed};
use crate::sapling::{
keys::SpendValidatingKey, pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed,
};
use bellman::gadgets::test::*;
use group::{ff::Field, Group};
use group::ff::Field;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
@ -654,7 +657,7 @@ fn test_input_circuit_with_bls12_381() {
};
let proof_generation_key = ProofGenerationKey {
ak: jubjub::SubgroupPoint::random(&mut rng),
ak: SpendValidatingKey::fake_random(&mut rng),
nsk: jubjub::Fr::random(&mut rng),
};
@ -681,7 +684,7 @@ fn test_input_circuit_with_bls12_381() {
let ar = jubjub::Fr::random(&mut rng);
{
let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine();
let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
let expected_value_commitment = value_commitment.commitment().to_affine();
let note = Note::from_parts(
payment_address,
@ -780,9 +783,12 @@ fn test_input_circuit_with_bls12_381() {
#[test]
fn test_input_circuit_with_bls12_381_external_test_vectors() {
use crate::sapling::{pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed};
use crate::sapling::{
keys::SpendValidatingKey, pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed,
};
use bellman::gadgets::test::*;
use group::{ff::Field, Group};
use group::ff::Field;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
@ -826,7 +832,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
};
let proof_generation_key = ProofGenerationKey {
ak: jubjub::SubgroupPoint::random(&mut rng),
ak: SpendValidatingKey::fake_random(&mut rng),
nsk: jubjub::Fr::random(&mut rng),
};
@ -853,7 +859,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
let ar = jubjub::Fr::random(&mut rng);
{
let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine();
let rk = jubjub::AffinePoint::from_bytes(viewing_key.rk(ar).into()).unwrap();
let expected_value_commitment = value_commitment.commitment().to_affine();
assert_eq!(
expected_value_commitment.get_u(),
@ -960,9 +966,10 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
#[test]
fn test_output_circuit_with_bls12_381() {
use crate::sapling::{Diversifier, ProofGenerationKey, Rseed};
use crate::sapling::{keys::SpendValidatingKey, Diversifier, ProofGenerationKey, Rseed};
use bellman::gadgets::test::*;
use group::{ff::Field, Group};
use group::ff::Field;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
@ -978,7 +985,7 @@ fn test_output_circuit_with_bls12_381() {
};
let nsk = jubjub::Fr::random(&mut rng);
let ak = jubjub::SubgroupPoint::random(&mut rng);
let ak = SpendValidatingKey::fake_random(&mut rng);
let proof_generation_key = ProofGenerationKey { ak, nsk };

View File

@ -9,7 +9,7 @@ use std::io::{self, Read, Write};
use super::{
address::PaymentAddress,
constants::{self, PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
constants::{self, PROOF_GENERATION_KEY_GENERATOR},
note_encryption::KDF_SAPLING_PERSONALIZATION,
spec::{
crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared,
@ -20,11 +20,15 @@ use super::{
use crate::keys::prf_expand;
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::PrimeField;
use ff::{Field, PrimeField};
use group::{Curve, Group, GroupEncoding};
use redjubjub::SpendAuth;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_note_encryption::EphemeralKeyBytes;
#[cfg(test)]
use rand_core::RngCore;
/// Errors that can occur in the decoding of Sapling spending keys.
pub enum DecodingError {
/// The length of the byte slice provided for decoding was incorrect.
@ -38,6 +42,167 @@ pub enum DecodingError {
UnsupportedChildIndex,
}
/// A spend authorizing key, used to create spend authorization signatures.
///
/// $\mathsf{ask}$ as defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
///
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
#[derive(Clone, Debug)]
pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
impl PartialEq for SpendAuthorizingKey {
fn eq(&self, other: &Self) -> bool {
<[u8; 32]>::from(self.0)
.ct_eq(&<[u8; 32]>::from(other.0))
.into()
}
}
impl Eq for SpendAuthorizingKey {}
impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
}
}
impl SpendAuthorizingKey {
/// Derives ask from sk. Internal use only, does not enforce all constraints.
fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
jubjub::Scalar::from_bytes_wide(prf_expand(sk, &[0x00]).as_array())
}
/// Constructs a `SpendAuthorizingKey` from a raw scalar.
pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
if ask.is_zero().into() {
None
} else {
Some(SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()))
}
}
/// Derives a `SpendAuthorizingKey` from a spending key.
fn from_spending_key(sk: &[u8]) -> Option<Self> {
Self::from_scalar(Self::derive_inner(sk))
}
/// Parses a `SpendAuthorizingKey` from its encoded form.
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
<[u8; 32]>::try_from(bytes)
.ok()
.and_then(|b| {
// RedJubjub.Private permits the full set of Jubjub scalars including
// zero. However, a SpendAuthorizingKey is further restricted within the
// Sapling key tree to be a non-zero scalar.
jubjub::Scalar::from_repr(b)
.and_then(|s| {
CtOption::new(
redjubjub::SigningKey::try_from(b)
.expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
!s.is_zero(),
)
})
.into()
})
.map(SpendAuthorizingKey)
}
/// Converts this spend authorizing key to its serialized form.
pub(crate) fn to_bytes(&self) -> [u8; 32] {
<[u8; 32]>::from(self.0)
}
/// Converts this spend authorizing key to a raw scalar.
///
/// Only used for ZIP 32 child derivation.
pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
jubjub::Scalar::from_repr(self.0.into()).unwrap()
}
/// Randomizes this spend authorizing key with the given `randomizer`.
///
/// The resulting key can be used to actually sign a spend.
pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
self.0.randomize(randomizer)
}
}
/// A key used to validate spend authorization signatures.
///
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
///
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
#[derive(Clone, Debug)]
pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
fn from(ask: &SpendAuthorizingKey) -> Self {
SpendValidatingKey((&ask.0).into())
}
}
impl PartialEq for SpendValidatingKey {
fn eq(&self, other: &Self) -> bool {
<[u8; 32]>::from(self.0)
.ct_eq(&<[u8; 32]>::from(other.0))
.into()
}
}
impl Eq for SpendValidatingKey {}
impl SpendValidatingKey {
/// For circuit tests only.
#[cfg(test)]
pub(crate) fn fake_random<R: RngCore>(mut rng: R) -> Self {
loop {
if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
break k;
}
}
}
/// Only exposed for `zcashd` unit tests.
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
Self::from_bytes(bytes)
}
/// Parses a `SpendValidatingKey` from its encoded form.
pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
<[u8; 32]>::try_from(bytes)
.ok()
.and_then(|b| {
// RedJubjub.Public permits the full set of Jubjub points including the
// identity and cofactors; this is the type used for `rk` in Spend
// descriptions. However, a SpendValidatingKey is further restricted
// within the Sapling key tree to be a non-identity element of the
// prime-order subgroup.
jubjub::SubgroupPoint::from_bytes(&b)
.and_then(|p| {
CtOption::new(
redjubjub::VerificationKey::try_from(b)
.expect("RedJubjub permits the set of valid SpendValidatingKeys"),
!p.is_identity(),
)
})
.into()
})
.map(SpendValidatingKey)
}
/// Converts this spend validating key to its serialized form,
/// `LEBS2OSP_256(repr_J(ak))`.
pub(crate) fn to_bytes(&self) -> [u8; 32] {
<[u8; 32]>::from(self.0)
}
/// Randomizes this spend validating key with the given `randomizer`.
pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
self.0.randomize(randomizer)
}
}
/// An outgoing viewing key
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OutgoingViewingKey(pub [u8; 32]);
@ -45,7 +210,7 @@ pub struct OutgoingViewingKey(pub [u8; 32]);
/// A Sapling expanded spending key
#[derive(Clone)]
pub struct ExpandedSpendingKey {
pub ask: jubjub::Fr,
pub ask: SpendAuthorizingKey,
pub nsk: jubjub::Fr,
pub ovk: OutgoingViewingKey,
}
@ -58,8 +223,15 @@ impl fmt::Debug for ExpandedSpendingKey {
}
impl ExpandedSpendingKey {
/// Expands a spending key into its components.
///
/// # Panics
///
/// Panics if this spending key expands to `ask = 0`. This has a negligible
/// probability of occurring.
pub fn from_spending_key(sk: &[u8]) -> Self {
let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array());
let ask =
SpendAuthorizingKey::from_spending_key(sk).expect("negligible chance of ask == 0");
let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array());
let mut ovk = OutgoingViewingKey([0u8; 32]);
ovk.0
@ -69,7 +241,7 @@ impl ExpandedSpendingKey {
pub fn proof_generation_key(&self) -> ProofGenerationKey {
ProofGenerationKey {
ak: SPENDING_KEY_GENERATOR * self.ask,
ak: (&self.ask).into(),
nsk: self.nsk,
}
}
@ -85,8 +257,7 @@ impl ExpandedSpendingKey {
});
}
let ask = Option::from(jubjub::Fr::from_repr(b[0..32].try_into().unwrap()))
.ok_or(DecodingError::InvalidAsk)?;
let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
.ok_or(DecodingError::InvalidNsk)?;
let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
@ -119,7 +290,7 @@ impl ExpandedSpendingKey {
/// [ZIP 32](https://zips.z.cash/zip-0032)
pub fn to_bytes(&self) -> [u8; 96] {
let mut result = [0u8; 96];
result[0..32].copy_from_slice(&self.ask.to_repr());
result[0..32].copy_from_slice(&self.ask.to_bytes());
result[32..64].copy_from_slice(&self.nsk.to_repr());
result[64..96].copy_from_slice(&self.ovk.0);
result
@ -128,7 +299,7 @@ impl ExpandedSpendingKey {
#[derive(Clone)]
pub struct ProofGenerationKey {
pub ak: jubjub::SubgroupPoint,
pub ak: SpendValidatingKey,
pub nsk: jubjub::Fr,
}
@ -143,7 +314,7 @@ impl fmt::Debug for ProofGenerationKey {
impl ProofGenerationKey {
pub fn to_viewing_key(&self) -> ViewingKey {
ViewingKey {
ak: self.ak,
ak: self.ak.clone(),
nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
}
}
@ -155,13 +326,13 @@ pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
#[derive(Debug, Clone)]
pub struct ViewingKey {
pub ak: jubjub::SubgroupPoint,
pub ak: SpendValidatingKey,
pub nk: NullifierDerivingKey,
}
impl ViewingKey {
pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint {
self.ak + constants::SPENDING_KEY_GENERATOR * ar
pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
self.ak.randomize(&ar)
}
pub fn ivk(&self) -> SaplingIvk {
@ -184,7 +355,7 @@ impl Clone for FullViewingKey {
fn clone(&self) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: self.vk.ak,
ak: self.vk.ak.clone(),
nk: self.vk.nk,
},
ovk: self.ovk,
@ -196,7 +367,7 @@ impl FullViewingKey {
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: SPENDING_KEY_GENERATOR * expsk.ask,
ak: (&expsk.ask).into(),
nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
},
ovk: expsk.ovk,
@ -207,14 +378,14 @@ impl FullViewingKey {
let ak = {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity()))
SpendValidatingKey::from_bytes(&buf)
};
let nk = {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf)
};
if ak.is_none().into() {
if ak.is_none() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"ak not of prime order",
@ -520,8 +691,8 @@ pub mod testing {
mod tests {
use group::{Group, GroupEncoding};
use super::FullViewingKey;
use crate::sapling::constants::SPENDING_KEY_GENERATOR;
use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey};
use crate::sapling::{constants::SPENDING_KEY_GENERATOR, test_vectors};
#[test]
fn ak_must_be_prime_order() {
@ -545,4 +716,31 @@ mod tests {
// nk is allowed to be the identity.
assert!(FullViewingKey::read(&buf[..]).is_ok());
}
#[test]
fn spend_auth_sig_test_vectors() {
for tv in test_vectors::signatures::make_test_vectors() {
let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
let sig = redjubjub::Signature::from(tv.sig);
let rsig = redjubjub::Signature::from(tv.rsig);
let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk);
assert_eq!(vk.randomize(&alpha), rvk);
// assert_eq!(vk.0.verify(&tv.m, &sig), Ok(()));
// assert_eq!(rvk.verify(&tv.m, &rsig), Ok(()));
assert_eq!(
vk.0.verify(&tv.m, &rsig),
Err(redjubjub::Error::InvalidSignature),
);
assert_eq!(
rvk.verify(&tv.m, &sig),
Err(redjubjub::Error::InvalidSignature),
);
}
}
}

View File

@ -1,386 +0,0 @@
//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub
//! curve.
//!
//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
use ff::{Field, PrimeField};
use group::GroupEncoding;
use jubjub::{AffinePoint, ExtendedPoint, SubgroupPoint};
use rand_core::RngCore;
use std::fmt;
use std::io::{self, Read, Write};
use std::ops::{AddAssign, MulAssign, Neg};
use super::util::hash_to_scalar;
fn read_scalar<R: Read>(mut reader: R) -> io::Result<jubjub::Fr> {
let mut s_repr = [0u8; 32];
reader.read_exact(s_repr.as_mut())?;
Option::from(jubjub::Fr::from_repr(s_repr))
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field"))
}
fn write_scalar<W: Write>(s: &jubjub::Fr, mut writer: W) -> io::Result<()> {
writer.write_all(s.to_repr().as_ref())
}
fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr {
hash_to_scalar(b"Zcash_RedJubjubH", a, b)
}
#[derive(Copy, Clone)]
pub struct Signature {
rbar: [u8; 32],
sbar: [u8; 32],
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Signature")
.field("rbar", &hex::encode(self.rbar))
.field("sbar", &hex::encode(self.sbar))
.finish()
}
}
pub struct PrivateKey(pub jubjub::Fr);
#[derive(Debug, Clone)]
pub struct PublicKey(pub ExtendedPoint);
impl Signature {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut rbar = [0u8; 32];
let mut sbar = [0u8; 32];
reader.read_exact(&mut rbar)?;
reader.read_exact(&mut sbar)?;
Ok(Signature { rbar, sbar })
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.rbar)?;
writer.write_all(&self.sbar)
}
}
impl PrivateKey {
#[must_use]
pub fn randomize(&self, alpha: jubjub::Fr) -> Self {
let mut tmp = self.0;
tmp.add_assign(&alpha);
PrivateKey(tmp)
}
pub fn read<R: Read>(reader: R) -> io::Result<Self> {
let pk = read_scalar::<R>(reader)?;
Ok(PrivateKey(pk))
}
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
write_scalar::<W>(&self.0, writer)
}
pub fn sign<R: RngCore>(&self, msg: &[u8], rng: &mut R, p_g: SubgroupPoint) -> Signature {
// T = (l_H + 128) bits of randomness
// For H*, l_H = 512 bits
let mut t = [0u8; 80];
rng.fill_bytes(&mut t[..]);
// r = H*(T || M)
let r = h_star(&t[..], msg);
// R = r . P_G
let r_g = p_g * r;
let rbar = r_g.to_bytes();
// S = r + H*(Rbar || M) . sk
let mut s = h_star(&rbar[..], msg);
s.mul_assign(&self.0);
s.add_assign(&r);
let mut sbar = [0u8; 32];
write_scalar::<&mut [u8]>(&s, &mut sbar[..])
.expect("Jubjub scalars should serialize to 32 bytes");
Signature { rbar, sbar }
}
}
impl PublicKey {
pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self {
PublicKey((p_g * privkey.0).into())
}
#[must_use]
pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self {
PublicKey(ExtendedPoint::from(p_g * alpha) + self.0)
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut bytes = [0u8; 32];
reader.read_exact(&mut bytes)?;
let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey);
if p.is_some().into() {
Ok(p.unwrap())
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid RedJubjub public key",
))
}
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.0.to_bytes())
}
pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool {
self.verify_with_zip216(msg, sig, p_g, true)
}
pub fn verify_with_zip216(
&self,
msg: &[u8],
sig: &Signature,
p_g: SubgroupPoint,
zip216_enabled: bool,
) -> bool {
// c = H*(Rbar || M)
let c = h_star(&sig.rbar[..], msg);
// Signature checks:
// R != invalid
let r = {
let r = if zip216_enabled {
ExtendedPoint::from_bytes(&sig.rbar)
} else {
AffinePoint::from_bytes_pre_zip216_compatibility(sig.rbar).map(|p| p.to_extended())
};
if r.is_none().into() {
return false;
}
r.unwrap()
};
// S < order(G)
// (jubjub::Scalar guarantees its representation is in the field)
let s = match read_scalar::<&[u8]>(&sig.sbar[..]) {
Ok(s) => s,
Err(_) => return false,
};
// 0 = h_G(-S . P_G + R + c . vk)
((self.0 * c) + r - (p_g * s))
.mul_by_cofactor()
.is_identity()
.into()
}
}
pub struct BatchEntry<'a> {
vk: PublicKey,
msg: &'a [u8],
sig: Signature,
}
// TODO: #82: This is a naive implementation currently,
// and doesn't use multiexp.
pub fn batch_verify<'a, R: RngCore>(
mut rng: &mut R,
batch: &[BatchEntry<'a>],
p_g: SubgroupPoint,
) -> bool {
let mut acc = ExtendedPoint::identity();
for entry in batch {
let mut r = {
let r = ExtendedPoint::from_bytes(&entry.sig.rbar);
if r.is_none().into() {
return false;
}
r.unwrap()
};
let mut s = match read_scalar::<&[u8]>(&entry.sig.sbar[..]) {
Ok(s) => s,
Err(_) => return false,
};
let mut c = h_star(&entry.sig.rbar[..], entry.msg);
let z = jubjub::Fr::random(&mut rng);
s.mul_assign(&z);
s = s.neg();
r *= z;
c.mul_assign(&z);
acc = acc + r + (entry.vk.0 * c) + (p_g * s);
}
acc.mul_by_cofactor().is_identity().into()
}
#[cfg(test)]
mod tests {
use group::Group;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use super::*;
use crate::sapling::constants::SPENDING_KEY_GENERATOR;
#[test]
fn test_batch_verify() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = SPENDING_KEY_GENERATOR;
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
let vk1 = PublicKey::from_private(&sk1, p_g);
let msg1 = b"Foo bar";
let sig1 = sk1.sign(msg1, &mut rng, p_g);
assert!(vk1.verify(msg1, &sig1, p_g));
let sk2 = PrivateKey(jubjub::Fr::random(&mut rng));
let vk2 = PublicKey::from_private(&sk2, p_g);
let msg2 = b"Foo bar";
let sig2 = sk2.sign(msg2, &mut rng, p_g);
assert!(vk2.verify(msg2, &sig2, p_g));
let mut batch = vec![
BatchEntry {
vk: vk1,
msg: msg1,
sig: sig1,
},
BatchEntry {
vk: vk2,
msg: msg2,
sig: sig2,
},
];
assert!(batch_verify(&mut rng, &batch, p_g));
batch[0].sig = sig2;
assert!(!batch_verify(&mut rng, &batch, p_g));
}
#[test]
fn cofactor_check() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let zero = jubjub::ExtendedPoint::identity();
let p_g = SPENDING_KEY_GENERATOR;
let jubjub_modulus_bytes = [
0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20,
0x68, 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65,
0xea, 0xb4, 0x7d, 0x0e,
];
// Get a point of order 8
let p8 = loop {
let r = jubjub::ExtendedPoint::random(&mut rng)
.to_niels()
.multiply_bits(&jubjub_modulus_bytes);
let r2 = r.double();
let r4 = r2.double();
let r8 = r4.double();
if r2 != zero && r4 != zero && r8 == zero {
break r;
}
};
let sk = PrivateKey(jubjub::Fr::random(&mut rng));
let vk = PublicKey::from_private(&sk, p_g);
// TODO: This test will need to change when #77 is fixed
let msg = b"Foo bar";
let sig = sk.sign(msg, &mut rng, p_g);
assert!(vk.verify(msg, &sig, p_g));
let vktorsion = PublicKey(vk.0 + p8);
assert!(vktorsion.verify(msg, &sig, p_g));
}
#[test]
fn round_trip_serialization() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = SPENDING_KEY_GENERATOR;
for _ in 0..1000 {
let sk = PrivateKey(jubjub::Fr::random(&mut rng));
let vk = PublicKey::from_private(&sk, p_g);
let msg = b"Foo bar";
let sig = sk.sign(msg, &mut rng, p_g);
let mut sk_bytes = [0u8; 32];
let mut vk_bytes = [0u8; 32];
let mut sig_bytes = [0u8; 64];
sk.write(&mut sk_bytes[..]).unwrap();
vk.write(&mut vk_bytes[..]).unwrap();
sig.write(&mut sig_bytes[..]).unwrap();
let sk_2 = PrivateKey::read(&sk_bytes[..]).unwrap();
let vk_2 = PublicKey::from_private(&sk_2, p_g);
let mut vk_2_bytes = [0u8; 32];
vk_2.write(&mut vk_2_bytes[..]).unwrap();
assert!(vk_bytes == vk_2_bytes);
let vk_2 = PublicKey::read(&vk_bytes[..]).unwrap();
let sig_2 = Signature::read(&sig_bytes[..]).unwrap();
assert!(vk.verify(msg, &sig_2, p_g));
assert!(vk_2.verify(msg, &sig, p_g));
assert!(vk_2.verify(msg, &sig_2, p_g));
}
}
#[test]
fn random_signatures() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = SPENDING_KEY_GENERATOR;
for _ in 0..1000 {
let sk = PrivateKey(jubjub::Fr::random(&mut rng));
let vk = PublicKey::from_private(&sk, p_g);
let msg1 = b"Foo bar";
let msg2 = b"Spam eggs";
let sig1 = sk.sign(msg1, &mut rng, p_g);
let sig2 = sk.sign(msg2, &mut rng, p_g);
assert!(vk.verify(msg1, &sig1, p_g));
assert!(vk.verify(msg2, &sig2, p_g));
assert!(!vk.verify(msg1, &sig2, p_g));
assert!(!vk.verify(msg2, &sig1, p_g));
let alpha = jubjub::Fr::random(&mut rng);
let rsk = sk.randomize(alpha);
let rvk = vk.randomize(alpha, p_g);
let sig1 = rsk.sign(msg1, &mut rng, p_g);
let sig2 = rsk.sign(msg2, &mut rng, p_g);
assert!(rvk.verify(msg1, &sig1, p_g));
assert!(rvk.verify(msg2, &sig2, p_g));
assert!(!rvk.verify(msg1, &sig2, p_g));
assert!(!rvk.verify(msg2, &sig1, p_g));
}
}
}

View File

@ -0,0 +1 @@
pub(crate) mod signatures;

View File

@ -0,0 +1,476 @@
pub(crate) struct TestVector {
pub(crate) sk: [u8; 32],
pub(crate) vk: [u8; 32],
pub(crate) alpha: [u8; 32],
pub(crate) rsk: [u8; 32],
pub(crate) rvk: [u8; 32],
pub(crate) m: [u8; 32],
pub(crate) sig: [u8; 64],
pub(crate) rsig: [u8; 64],
}
pub(crate) fn make_test_vectors() -> Vec<TestVector> {
// From https://github.com/zcash/zcash-test-vectors/blob/master/zcash_test_vectors/sapling/redjubjub.py
vec![
TestVector {
sk: [
0x18, 0xe2, 0x8d, 0xea, 0x5c, 0x11, 0x81, 0x7a, 0xee, 0xb2, 0x1a, 0x19, 0x98, 0x1d,
0x28, 0x36, 0x8e, 0xc4, 0x38, 0xaf, 0xc2, 0x5a, 0x8d, 0xb9, 0x4e, 0xbe, 0x08, 0xd7,
0xa0, 0x28, 0x8e, 0x09,
],
vk: [
0x9b, 0x01, 0x53, 0xb0, 0x3d, 0x32, 0x0f, 0xe2, 0x3e, 0x28, 0x34, 0xd5, 0xd6, 0x1d,
0xbb, 0x1f, 0x51, 0x9b, 0x3f, 0x41, 0xf8, 0xf9, 0x46, 0x15, 0x2b, 0xf0, 0xc3, 0xf2,
0x47, 0xd1, 0x18, 0x07,
],
alpha: [
0xff, 0xd1, 0xa1, 0x27, 0x32, 0x52, 0xb1, 0x87, 0xf4, 0xed, 0x32, 0x6d, 0xfc, 0x98,
0x85, 0x3e, 0x29, 0x17, 0xc2, 0xb3, 0x63, 0x79, 0xb1, 0x75, 0xda, 0x63, 0xb9, 0xef,
0x6d, 0xda, 0x6c, 0x08,
],
rsk: [
0x60, 0x87, 0x38, 0x3b, 0x30, 0x55, 0x9b, 0x31, 0x60, 0x90, 0x85, 0xb9, 0x00, 0x96,
0x45, 0xce, 0xb6, 0xa0, 0xc6, 0x61, 0x25, 0x99, 0xd7, 0x28, 0x80, 0x72, 0x8e, 0x61,
0x24, 0x4e, 0x7d, 0x03,
],
rvk: [
0xc1, 0xba, 0xbc, 0xb6, 0xea, 0xe2, 0xb9, 0x94, 0xee, 0x6d, 0x65, 0xc1, 0x0b, 0x9d,
0xad, 0x59, 0x40, 0xdc, 0x73, 0x5b, 0x07, 0x50, 0x4d, 0xae, 0xd1, 0xe4, 0x6b, 0x07,
0x09, 0xb4, 0x51, 0x36,
],
m: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
sig: [
0xdc, 0xa3, 0xbb, 0x2c, 0xb8, 0xf0, 0x48, 0xcc, 0xab, 0x10, 0xae, 0xd7, 0x75, 0x46,
0xc1, 0xdb, 0xb1, 0x0c, 0xc4, 0xfb, 0x15, 0xab, 0x02, 0xac, 0xae, 0xf9, 0x44, 0xdd,
0xab, 0x8b, 0x67, 0x22, 0x54, 0x5f, 0xda, 0x4c, 0x62, 0x04, 0x6d, 0x69, 0xd9, 0x8f,
0x92, 0x2f, 0x4e, 0x8c, 0x21, 0x0b, 0xc4, 0x7b, 0x4f, 0xdd, 0xe0, 0xa1, 0x94, 0x71,
0x79, 0x80, 0x4c, 0x1a, 0xce, 0x56, 0x90, 0x05,
],
rsig: [
0x70, 0xc2, 0x84, 0x50, 0x4e, 0x90, 0xf0, 0x00, 0x8e, 0x8e, 0xd2, 0x20, 0x8f, 0x49,
0x69, 0x72, 0x7a, 0x41, 0x5e, 0xc3, 0x10, 0x2c, 0x29, 0x9e, 0x39, 0x8b, 0x6c, 0x16,
0x57, 0x2b, 0xd9, 0x64, 0x3e, 0xe1, 0x01, 0x17, 0x66, 0x68, 0x1e, 0x40, 0x6e, 0xe6,
0xbe, 0xe3, 0xd0, 0x3e, 0xe8, 0xf2, 0x71, 0x76, 0xe3, 0x2f, 0xba, 0xbd, 0xde, 0xd2,
0x0b, 0x0d, 0x17, 0x86, 0xa4, 0xee, 0x18, 0x01,
],
},
TestVector {
sk: [
0x05, 0x96, 0x54, 0xf9, 0x61, 0x27, 0x3d, 0xaf, 0xda, 0x3b, 0x26, 0x77, 0xb3, 0x5c,
0x18, 0xaf, 0x6b, 0x11, 0xad, 0xfb, 0x9e, 0xe9, 0x0b, 0x48, 0x93, 0x5e, 0x55, 0x7c,
0x8d, 0x5d, 0x9c, 0x04,
],
vk: [
0xfa, 0xf6, 0xc3, 0xb7, 0x37, 0xe8, 0xe6, 0x11, 0xaa, 0xfe, 0xa5, 0x2f, 0x03, 0xbb,
0x27, 0x86, 0xe1, 0x83, 0x53, 0xeb, 0xe0, 0xd3, 0x13, 0x9e, 0x3c, 0x54, 0x49, 0x87,
0x80, 0xc8, 0xc1, 0x99,
],
alpha: [
0xc3, 0x0b, 0x96, 0x20, 0x8d, 0xa8, 0x00, 0xe1, 0x0a, 0xf0, 0x25, 0x42, 0xce, 0x69,
0x4b, 0x7e, 0xd7, 0x6a, 0x28, 0x29, 0x9f, 0x85, 0x99, 0x8e, 0x5d, 0x61, 0x08, 0x12,
0x68, 0x1b, 0xf0, 0x03,
],
rsk: [
0xc8, 0xa1, 0xea, 0x19, 0xef, 0xcf, 0x3d, 0x90, 0xe5, 0x2b, 0x4c, 0xb9, 0x81, 0xc6,
0x63, 0x2d, 0x43, 0x7c, 0xd5, 0x24, 0x3e, 0x6f, 0xa5, 0xd6, 0xf0, 0xbf, 0x5d, 0x8e,
0xf5, 0x78, 0x8c, 0x08,
],
rvk: [
0xd5, 0x24, 0xdc, 0xe7, 0x73, 0x40, 0x69, 0x75, 0x8a, 0x91, 0xf0, 0x07, 0xa8, 0x69,
0x50, 0x5d, 0xfc, 0x4a, 0xba, 0x17, 0x20, 0x59, 0x4d, 0x4d, 0x74, 0xf0, 0x07, 0x70,
0x0e, 0x62, 0xee, 0x00,
],
m: [
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
],
sig: [
0xb5, 0xa1, 0xf3, 0x2d, 0x3d, 0x50, 0xfc, 0x73, 0x8b, 0x5c, 0x3b, 0x4e, 0x99, 0x60,
0x72, 0x9c, 0xe4, 0x31, 0x6b, 0xa7, 0x72, 0x1a, 0x12, 0x68, 0x66, 0x04, 0xfe, 0xba,
0x6b, 0xd7, 0x48, 0x45, 0x00, 0x70, 0xcb, 0x92, 0x24, 0x06, 0xfd, 0xfc, 0x5d, 0x60,
0xde, 0xa9, 0xbe, 0x3a, 0x52, 0x6a, 0x16, 0xcf, 0xeb, 0x87, 0x77, 0x79, 0xfb, 0x78,
0x2d, 0x5d, 0x41, 0x39, 0x5b, 0x45, 0x5f, 0x04,
],
rsig: [
0x5a, 0x5a, 0x20, 0xd2, 0x00, 0xef, 0xdd, 0xd4, 0x98, 0xdf, 0xae, 0x2a, 0x9e, 0xf8,
0xcf, 0x01, 0x28, 0x1a, 0x89, 0x19, 0x01, 0x8a, 0x82, 0x4c, 0xc7, 0xa4, 0x98, 0x3b,
0x9a, 0x0d, 0x4a, 0x06, 0xff, 0x17, 0x20, 0x79, 0xe0, 0x13, 0xd4, 0x2a, 0x2a, 0x3a,
0x88, 0xa6, 0x52, 0x0c, 0x86, 0xfc, 0xe3, 0xb9, 0x8e, 0x1e, 0xfa, 0xa3, 0x25, 0x83,
0x2a, 0x6a, 0x56, 0x58, 0xd8, 0xdd, 0x7c, 0x0a,
],
},
TestVector {
sk: [
0xad, 0xe7, 0xab, 0xb5, 0x51, 0xc7, 0x9d, 0x0f, 0x0e, 0x42, 0xef, 0x7f, 0x12, 0x06,
0xb8, 0x77, 0x12, 0xa8, 0x4a, 0x61, 0xde, 0xa3, 0xf3, 0x7b, 0x42, 0x49, 0x6d, 0x7e,
0xfd, 0x12, 0x52, 0x0c,
],
vk: [
0x36, 0x9e, 0xa7, 0x51, 0x76, 0x2f, 0x83, 0x9d, 0x25, 0x70, 0x1a, 0x5e, 0xeb, 0x55,
0x1e, 0xc4, 0xf0, 0x6c, 0x12, 0x90, 0xb3, 0xb9, 0xc3, 0xa7, 0x24, 0x40, 0x2d, 0xec,
0x02, 0x73, 0x92, 0x21,
],
alpha: [
0x81, 0x92, 0x25, 0x29, 0xa6, 0x3e, 0xe7, 0x43, 0xfc, 0x4f, 0xbb, 0xac, 0x45, 0xc4,
0x98, 0x83, 0x16, 0xbc, 0x9b, 0x6e, 0x42, 0x8b, 0x01, 0xa8, 0xd3, 0x1f, 0xc1, 0xc2,
0xa6, 0xca, 0x62, 0x05,
],
rsk: [
0x77, 0x4d, 0xda, 0x07, 0x99, 0xf7, 0xed, 0x82, 0x87, 0x81, 0xe2, 0x5f, 0xc4, 0xa9,
0xe8, 0x54, 0x28, 0x29, 0xb2, 0xce, 0x1f, 0xf4, 0x8d, 0x1d, 0x6d, 0xb9, 0xfa, 0xdb,
0xb9, 0x28, 0x37, 0x03,
],
rvk: [
0x0d, 0x92, 0xad, 0x6d, 0x46, 0xed, 0xac, 0xd0, 0x23, 0xd4, 0xd2, 0xef, 0x70, 0x3a,
0x6c, 0xa0, 0xa7, 0x92, 0xcf, 0xc4, 0xb7, 0xda, 0x11, 0xc2, 0x35, 0x3b, 0xc8, 0x45,
0xa2, 0x7a, 0x97, 0x4d,
],
m: [
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
],
sig: [
0x1f, 0x3e, 0x8a, 0x94, 0x31, 0x0c, 0x20, 0x71, 0xa7, 0x0f, 0x9d, 0xf5, 0xe7, 0x9a,
0xa9, 0xe8, 0x48, 0x5d, 0xec, 0xcb, 0x17, 0x8b, 0xdf, 0xf9, 0x80, 0x5f, 0xcb, 0xe6,
0xf7, 0xd5, 0x51, 0xee, 0xe3, 0xc3, 0x54, 0x2c, 0xa7, 0x5c, 0x9d, 0x8d, 0x4a, 0xdc,
0x54, 0xd7, 0x2c, 0x3d, 0xbe, 0x28, 0x62, 0x6d, 0x20, 0x78, 0x5b, 0xb7, 0xf5, 0x88,
0xc1, 0xa5, 0x82, 0xb8, 0x93, 0xdb, 0xb6, 0x01,
],
rsig: [
0xd1, 0x36, 0x21, 0x4c, 0x5d, 0x52, 0x8e, 0xa3, 0xd4, 0xcb, 0x7b, 0x63, 0x1a, 0x6b,
0xb0, 0x36, 0x06, 0x49, 0x73, 0xa1, 0x08, 0xb7, 0x33, 0xa5, 0xe3, 0xa4, 0x52, 0xab,
0x52, 0xa6, 0x59, 0xe5, 0x67, 0xcb, 0x55, 0xd2, 0x64, 0x4e, 0x74, 0xb6, 0xe8, 0x42,
0x6f, 0x2a, 0x7d, 0xd2, 0xa0, 0x4d, 0x2d, 0xda, 0x49, 0x35, 0xcc, 0x38, 0x20, 0xb7,
0x7a, 0x9c, 0x1a, 0xb6, 0x19, 0x86, 0x3c, 0x05,
],
},
TestVector {
sk: [
0xc9, 0xd2, 0xae, 0x1f, 0x6d, 0x32, 0xa6, 0x75, 0xd0, 0x9e, 0xb0, 0x82, 0x3f, 0x46,
0x7f, 0xa9, 0x21, 0xb3, 0x28, 0x4a, 0xcb, 0x35, 0xfa, 0xbd, 0xfc, 0x99, 0x4d, 0xe5,
0x49, 0xb8, 0x59, 0x0d,
],
vk: [
0x2d, 0x2f, 0x31, 0x6e, 0x5c, 0x36, 0x9a, 0xe4, 0xdd, 0x2c, 0x82, 0x5f, 0x3d, 0x86,
0x46, 0x00, 0x58, 0x40, 0x71, 0x84, 0x60, 0x3b, 0x21, 0x2c, 0xf3, 0x45, 0x9f, 0x36,
0xc8, 0x69, 0x7f, 0xd8,
],
alpha: [
0xeb, 0xbc, 0x89, 0x03, 0x11, 0x07, 0xc4, 0x4f, 0x47, 0x88, 0x9e, 0xd4, 0xd4, 0x37,
0x5a, 0x41, 0x14, 0xcf, 0x8a, 0x75, 0xdd, 0x33, 0xb9, 0x62, 0xf2, 0xd7, 0x59, 0xd3,
0xf4, 0xc6, 0xdf, 0x06,
],
rsk: [
0xfd, 0x62, 0x41, 0x4c, 0x1f, 0x2b, 0xd3, 0xf4, 0x94, 0x16, 0x87, 0x8a, 0x80, 0x5d,
0x71, 0x44, 0x35, 0x47, 0x7f, 0xbe, 0xa7, 0x2e, 0x4c, 0x1a, 0x46, 0xc2, 0x73, 0x53,
0x54, 0xca, 0xbb, 0x05,
],
rvk: [
0xf0, 0x43, 0x0e, 0x95, 0x3b, 0xe6, 0x0b, 0xf4, 0x38, 0xdb, 0xdc, 0xc2, 0x30, 0x3f,
0x0e, 0x32, 0xa6, 0xf7, 0xce, 0x2f, 0xbe, 0xdf, 0xb1, 0x3a, 0xc5, 0x18, 0xf7, 0x5a,
0x3f, 0xd1, 0x0e, 0xb5,
],
m: [
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03,
],
sig: [
0x12, 0xc7, 0x8d, 0xdd, 0x20, 0xd3, 0x0a, 0x61, 0xf8, 0x93, 0x0c, 0x6f, 0xe0, 0x85,
0x0f, 0xd1, 0x12, 0xbb, 0x7b, 0xe8, 0x8b, 0x12, 0x38, 0xea, 0x33, 0xd6, 0xbe, 0xf8,
0x81, 0xc1, 0x02, 0xd1, 0x04, 0xaa, 0x36, 0x54, 0x4a, 0x78, 0x47, 0x1c, 0x9e, 0x28,
0x42, 0xe6, 0xfd, 0x42, 0x55, 0x83, 0x46, 0xcf, 0xf4, 0x31, 0x27, 0x03, 0x26, 0x66,
0xeb, 0x11, 0x6f, 0x44, 0x2a, 0x28, 0x48, 0x0c,
],
rsig: [
0x01, 0xba, 0xaa, 0x26, 0x27, 0x4c, 0x14, 0x9a, 0xcf, 0x12, 0xe1, 0xcc, 0xf5, 0x50,
0x7d, 0x56, 0x79, 0x04, 0x82, 0xf0, 0x67, 0xe5, 0xc9, 0x2b, 0x32, 0x19, 0xad, 0x6b,
0xf9, 0x11, 0x18, 0xcc, 0x3f, 0xce, 0x8d, 0x2a, 0x23, 0x19, 0x8a, 0x3b, 0x29, 0x0a,
0x7b, 0xf6, 0x8c, 0x2a, 0xc0, 0x7b, 0x5d, 0x90, 0x62, 0xb9, 0xf8, 0x68, 0x66, 0x2b,
0xb2, 0x52, 0x49, 0x12, 0xd4, 0x85, 0x6e, 0x0c,
],
},
TestVector {
sk: [
0x33, 0xbc, 0xd2, 0x86, 0x45, 0x41, 0xb8, 0xbb, 0x7f, 0xdc, 0x77, 0xa1, 0x9d, 0x97,
0x0f, 0x92, 0x4e, 0xae, 0xec, 0xf4, 0x10, 0x3c, 0x38, 0xc8, 0xd2, 0xb0, 0x66, 0x81,
0x42, 0xf2, 0x7d, 0x09,
],
vk: [
0x74, 0x17, 0x94, 0xe6, 0x2c, 0xf9, 0x32, 0x0c, 0x58, 0xba, 0xc5, 0x94, 0xa2, 0xb9,
0x0e, 0x34, 0x0a, 0x6d, 0x8a, 0x68, 0x05, 0x6f, 0x6e, 0xd5, 0xc7, 0x86, 0x8c, 0x5f,
0xf3, 0xe4, 0xd6, 0x16,
],
alpha: [
0x7c, 0xe7, 0x25, 0xa5, 0xfe, 0xf6, 0x1b, 0xd4, 0xa1, 0xe9, 0xc7, 0x73, 0x28, 0xe8,
0x21, 0x0e, 0xb7, 0x29, 0x2d, 0x95, 0x4c, 0x64, 0xe9, 0x9e, 0x8b, 0xed, 0xd0, 0x7a,
0xb3, 0xab, 0x0e, 0x0d,
],
rsk: [
0xf8, 0x76, 0x01, 0x55, 0xe5, 0x29, 0x3d, 0xbf, 0x9e, 0xb5, 0x77, 0x48, 0x32, 0x5f,
0xc9, 0xf9, 0x04, 0x9d, 0xe5, 0x88, 0x5c, 0x65, 0xba, 0x60, 0xb5, 0xee, 0x03, 0x97,
0x0b, 0xe9, 0x0e, 0x08,
],
rvk: [
0x66, 0x62, 0xba, 0x09, 0x95, 0x0a, 0xcc, 0xd2, 0xce, 0xa3, 0xc7, 0xa8, 0x12, 0x90,
0xcd, 0x59, 0x78, 0xa6, 0x2b, 0x5a, 0xc5, 0xbb, 0xc4, 0x8d, 0x9f, 0x58, 0x19, 0xcd,
0xc9, 0x64, 0x6f, 0x0a,
],
m: [
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04,
],
sig: [
0x77, 0x4a, 0xc4, 0x67, 0x3f, 0x09, 0xf3, 0xac, 0x57, 0x89, 0xb2, 0x86, 0xb5, 0xee,
0xcb, 0xed, 0xb2, 0x57, 0x23, 0x4e, 0x8c, 0xdf, 0xd9, 0x3f, 0x02, 0x89, 0x09, 0x78,
0xa6, 0xbb, 0xa6, 0x11, 0x69, 0xed, 0x48, 0xf9, 0xe1, 0xc9, 0xfd, 0x13, 0x19, 0xbd,
0x33, 0x0d, 0x2c, 0xf5, 0xb4, 0x91, 0x01, 0x0d, 0x69, 0xb0, 0x43, 0xf4, 0x64, 0x8b,
0xff, 0x55, 0x41, 0x62, 0xc6, 0xa6, 0xdc, 0x09,
],
rsig: [
0x7c, 0x6c, 0x49, 0x8d, 0xe0, 0x01, 0x78, 0x61, 0x09, 0xb3, 0x03, 0xa4, 0xc5, 0xdc,
0xb7, 0xfd, 0x07, 0x57, 0x50, 0xa0, 0xb9, 0xdf, 0x5e, 0x1e, 0x2a, 0x8e, 0x75, 0x47,
0xb7, 0xed, 0x70, 0xcc, 0x0b, 0x56, 0xa5, 0xbf, 0xa9, 0x65, 0x78, 0x43, 0xef, 0xd8,
0x9c, 0x66, 0xa8, 0x4f, 0x41, 0xd2, 0xb1, 0xb5, 0x07, 0x51, 0x19, 0x6b, 0x1e, 0x8c,
0x0c, 0x44, 0x98, 0x60, 0x06, 0x96, 0xa4, 0x04,
],
},
TestVector {
sk: [
0xca, 0x35, 0x06, 0xd6, 0xaf, 0x77, 0x67, 0xb5, 0x79, 0x0e, 0xf0, 0xc5, 0x19, 0x0f,
0xb3, 0xf3, 0x87, 0x7c, 0x4a, 0xab, 0x40, 0xe0, 0xdd, 0x65, 0x1a, 0xbb, 0xda, 0xcb,
0x54, 0x4e, 0xd0, 0x05,
],
vk: [
0xba, 0xb6, 0xcf, 0xb5, 0xc8, 0xea, 0x34, 0x91, 0x25, 0x1b, 0x46, 0xd5, 0x2a, 0xca,
0x25, 0xd9, 0xe9, 0xaf, 0x69, 0xfa, 0xa9, 0xb4, 0xe4, 0x0b, 0x03, 0xad, 0x00, 0x86,
0xde, 0x59, 0xb5, 0x1f,
],
alpha: [
0xbe, 0xa3, 0x87, 0x20, 0x3f, 0x43, 0x76, 0x0a, 0xd3, 0x7d, 0x61, 0xde, 0x0e, 0xb5,
0x9f, 0xca, 0x6c, 0xab, 0x75, 0x60, 0xdf, 0x64, 0xfa, 0xbb, 0x95, 0x11, 0x57, 0x9f,
0x6f, 0x68, 0x26, 0x06,
],
rsk: [
0x88, 0xd9, 0x8d, 0xf6, 0xee, 0xba, 0xdd, 0xbf, 0x4c, 0x8c, 0x51, 0xa4, 0x28, 0xc4,
0x52, 0xbe, 0xf4, 0x27, 0xc0, 0x0b, 0x20, 0x45, 0xd8, 0x21, 0xb0, 0xcc, 0x31, 0x6b,
0xc4, 0xb6, 0xf6, 0x0b,
],
rvk: [
0x11, 0x26, 0x7d, 0x14, 0xd5, 0xe0, 0xb2, 0xbb, 0x3c, 0xe0, 0x99, 0xe8, 0xef, 0x84,
0x49, 0x47, 0x1c, 0xbc, 0xfc, 0x69, 0x39, 0xa4, 0xb3, 0x48, 0xde, 0xa2, 0xc1, 0x73,
0x56, 0xa1, 0xe8, 0xdd,
],
m: [
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05,
],
sig: [
0x9a, 0x25, 0x42, 0x9f, 0x3e, 0xfd, 0x9b, 0x2f, 0x7d, 0xe2, 0x9e, 0x45, 0x12, 0x8d,
0xd7, 0xb7, 0x60, 0xf0, 0x50, 0x8c, 0xd9, 0x58, 0x21, 0x82, 0xab, 0xaf, 0x53, 0xdd,
0x76, 0xc0, 0x34, 0x2c, 0xe4, 0x1b, 0x4a, 0xcf, 0x8e, 0x0a, 0x48, 0x24, 0xe4, 0x11,
0x08, 0xc2, 0x02, 0x65, 0x73, 0x11, 0x4b, 0x60, 0xbe, 0xec, 0xb1, 0x74, 0x01, 0x2a,
0x2b, 0xdb, 0xee, 0xcb, 0xaa, 0x00, 0xb5, 0x06,
],
rsig: [
0xcf, 0xf5, 0x83, 0x57, 0x13, 0xbe, 0x07, 0xfb, 0xe1, 0x25, 0xbb, 0xf2, 0x7a, 0x63,
0x6a, 0xdd, 0x13, 0x1c, 0x90, 0x81, 0x71, 0x6c, 0x52, 0xfd, 0xa8, 0x75, 0x42, 0x6d,
0x03, 0x98, 0x2c, 0xd2, 0x7e, 0xbd, 0x14, 0xb4, 0x22, 0x7b, 0x83, 0x96, 0x15, 0xfd,
0x03, 0x71, 0xbf, 0xdb, 0x8a, 0x30, 0xab, 0xdd, 0xff, 0x74, 0xd7, 0x95, 0xf3, 0xe2,
0x7d, 0x1d, 0x47, 0xc6, 0x29, 0x46, 0x9b, 0x08,
],
},
TestVector {
sk: [
0xbc, 0x27, 0x83, 0x8d, 0xe2, 0xa6, 0x14, 0xcf, 0xba, 0x6c, 0x3e, 0x92, 0x2a, 0x8f,
0x84, 0x24, 0xd9, 0x85, 0x6f, 0x68, 0x16, 0xf3, 0xbc, 0x61, 0x02, 0x31, 0x3b, 0x7f,
0xaf, 0x5c, 0x3a, 0x0c,
],
vk: [
0xd7, 0x9b, 0xe9, 0xff, 0x22, 0x9a, 0x2e, 0x35, 0xf5, 0xbc, 0xa4, 0x48, 0xe5, 0xeb,
0x4a, 0x8a, 0xa9, 0x7f, 0xb4, 0x18, 0x02, 0x91, 0x25, 0xcf, 0xba, 0xa7, 0x8a, 0x91,
0xa3, 0x82, 0xb0, 0x94,
],
alpha: [
0x21, 0xa7, 0x15, 0x0e, 0x19, 0x4f, 0xed, 0xfe, 0xf9, 0x0c, 0x5d, 0x10, 0xe4, 0x20,
0x85, 0x8b, 0xca, 0x40, 0x04, 0x04, 0x0e, 0xb6, 0x81, 0xd1, 0x4e, 0x75, 0xc4, 0x47,
0x13, 0x51, 0xcb, 0x02,
],
rsk: [
0x26, 0xa2, 0xa1, 0xc4, 0x9c, 0xe7, 0x6a, 0xfd, 0x31, 0x69, 0xd3, 0xd5, 0x7a, 0x8f,
0xa1, 0x09, 0xa3, 0x8b, 0x3f, 0x6b, 0x23, 0x6e, 0xd7, 0x2c, 0xa8, 0xf6, 0xcb, 0x61,
0xd8, 0xf8, 0x87, 0x00,
],
rvk: [
0x54, 0xbf, 0x1b, 0xe7, 0x2e, 0x6d, 0x41, 0x20, 0x8b, 0x8a, 0xec, 0x11, 0x61, 0xd3,
0xba, 0x59, 0x51, 0x9f, 0xb9, 0x3d, 0xa0, 0x1a, 0x55, 0xe6, 0x78, 0xe2, 0x75, 0x20,
0x06, 0x60, 0x36, 0xc9,
],
m: [
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06,
],
sig: [
0xbb, 0xe0, 0x23, 0x59, 0x87, 0xc6, 0xe0, 0xec, 0x68, 0x6d, 0xdb, 0x8a, 0x65, 0x72,
0x66, 0xad, 0x60, 0x5f, 0x7b, 0x75, 0x95, 0x5b, 0xb0, 0xe8, 0x02, 0xf8, 0x81, 0x64,
0xa0, 0xff, 0xe1, 0x0c, 0x3b, 0x73, 0x85, 0x04, 0xab, 0xb3, 0xd1, 0x05, 0x62, 0xb9,
0x27, 0xb3, 0xd2, 0x9f, 0xe9, 0xb0, 0xd3, 0x56, 0x28, 0x6a, 0xea, 0xe5, 0xa2, 0xac,
0x9e, 0x43, 0x5f, 0x20, 0x79, 0x1a, 0xf8, 0x00,
],
rsig: [
0x6d, 0xe3, 0x2b, 0x54, 0x15, 0xd7, 0x7a, 0x90, 0x5f, 0x09, 0x03, 0x90, 0x2a, 0x11,
0x7e, 0xda, 0x79, 0x3c, 0x70, 0x8e, 0x23, 0xa5, 0x42, 0x45, 0xba, 0x8a, 0x8d, 0x1f,
0xe0, 0x26, 0x75, 0x23, 0x23, 0x15, 0x65, 0xe0, 0x57, 0x09, 0xae, 0xd9, 0x6c, 0x22,
0x1f, 0xb1, 0xf3, 0xd0, 0x42, 0x04, 0x35, 0x03, 0xff, 0x33, 0x85, 0x85, 0xa9, 0xbb,
0x98, 0x9c, 0x9d, 0xd4, 0x30, 0xd6, 0xd6, 0x0b,
],
},
TestVector {
sk: [
0xb2, 0x08, 0x59, 0xb8, 0x8e, 0xe3, 0x33, 0x8a, 0x64, 0x95, 0x4f, 0x8a, 0x9e, 0x8e,
0x9b, 0xf3, 0xe7, 0x11, 0x5a, 0xcf, 0x7c, 0x6e, 0x7f, 0x01, 0x43, 0x2c, 0x5f, 0x76,
0x96, 0xd2, 0xd0, 0x05,
],
vk: [
0xa8, 0x1f, 0xe6, 0x84, 0x6d, 0xbe, 0x0a, 0x75, 0xc0, 0xf4, 0x9b, 0x21, 0x32, 0x32,
0xbe, 0xad, 0xd1, 0xf9, 0xa5, 0x64, 0x67, 0x3d, 0x25, 0xb9, 0x1e, 0xe0, 0xf1, 0x7c,
0xe9, 0xca, 0xa3, 0x63,
],
alpha: [
0x44, 0xd9, 0x08, 0xe1, 0xc1, 0x5e, 0x6b, 0xd9, 0x38, 0x0a, 0x8b, 0x23, 0x5a, 0xce,
0x02, 0xfa, 0xc1, 0xc0, 0x87, 0x94, 0x45, 0x4b, 0xcd, 0xb4, 0xa6, 0xf4, 0x8c, 0xea,
0x78, 0xa7, 0x4a, 0x04,
],
rsk: [
0xf6, 0xe1, 0x61, 0x99, 0x50, 0x42, 0x9f, 0x63, 0x9d, 0x9f, 0xda, 0xad, 0xf8, 0x5c,
0x9e, 0xed, 0xa9, 0xd2, 0xe1, 0x63, 0xc2, 0xb9, 0x4c, 0xb6, 0xe9, 0x20, 0xec, 0x60,
0x0f, 0x7a, 0x1b, 0x0a,
],
rvk: [
0x0b, 0x68, 0xd5, 0x0f, 0x91, 0x3c, 0xd1, 0xb7, 0x8b, 0x59, 0x92, 0x1e, 0x16, 0x56,
0xd5, 0x76, 0xb0, 0xeb, 0x17, 0x1e, 0xd3, 0x87, 0x0d, 0x39, 0xfe, 0xc6, 0x94, 0x41,
0xb3, 0x4b, 0x25, 0x38,
],
m: [
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07,
],
sig: [
0x44, 0x6d, 0x67, 0x7c, 0x4c, 0xfe, 0xfd, 0x02, 0x4b, 0x0a, 0xeb, 0x37, 0xa5, 0x98,
0xcc, 0x2e, 0xb3, 0xd2, 0x9b, 0x02, 0x94, 0xfe, 0x5b, 0xb6, 0x97, 0x8e, 0x8b, 0x43,
0xd3, 0x2b, 0x2e, 0x4f, 0x09, 0x56, 0xac, 0xd1, 0x3e, 0x7e, 0x3a, 0x63, 0xa1, 0x8f,
0xca, 0x32, 0xd6, 0xab, 0x94, 0xb9, 0x4e, 0xd0, 0x33, 0xe9, 0xa1, 0x0f, 0xc5, 0x69,
0x28, 0xbc, 0x8a, 0x0f, 0x4f, 0x8e, 0x95, 0x00,
],
rsig: [
0x8d, 0xe0, 0x41, 0xe7, 0x09, 0xdb, 0x62, 0x4a, 0xe2, 0xbe, 0x16, 0x48, 0xb6, 0x62,
0x23, 0x9c, 0xde, 0xdf, 0x85, 0xec, 0xd3, 0x82, 0x26, 0x8b, 0x0e, 0x35, 0x54, 0xbf,
0xa0, 0xf2, 0x08, 0x1c, 0xd6, 0x41, 0xbc, 0xa0, 0x40, 0x78, 0xaa, 0x89, 0xf7, 0xdd,
0x25, 0x40, 0x58, 0x7c, 0xed, 0x6b, 0x45, 0x89, 0x16, 0xb1, 0x3e, 0x4b, 0x6a, 0x36,
0x30, 0xda, 0x69, 0x76, 0x46, 0xdb, 0xbf, 0x09,
],
},
TestVector {
sk: [
0x32, 0x16, 0xae, 0x47, 0xe9, 0xf5, 0x3e, 0x8a, 0x52, 0x79, 0x6f, 0x24, 0xb6, 0x24,
0x60, 0x77, 0x6b, 0xd5, 0xf2, 0x05, 0xa7, 0x8e, 0x15, 0x95, 0xbc, 0x8e, 0xfe, 0xdc,
0x51, 0x9d, 0x36, 0x0b,
],
vk: [
0xdf, 0x74, 0xbf, 0x04, 0x79, 0x61, 0xcc, 0x5c, 0xda, 0xc8, 0x28, 0x90, 0xc7, 0x6e,
0xc6, 0x75, 0xbd, 0x4e, 0x89, 0xea, 0xd2, 0x80, 0xc9, 0x52, 0xd7, 0xc3, 0x3e, 0xea,
0xf2, 0xb5, 0xa6, 0x6b,
],
alpha: [
0xc9, 0x61, 0xf2, 0xdd, 0x93, 0x68, 0x2a, 0xdb, 0x93, 0xf5, 0xc0, 0x5a, 0x73, 0xfd,
0xbc, 0x6d, 0x43, 0xc7, 0x0e, 0x1b, 0x15, 0xe8, 0xd5, 0x3e, 0x3f, 0x17, 0xa8, 0x24,
0x94, 0xe3, 0xf2, 0x09,
],
rsk: [
0x44, 0x4b, 0xa9, 0x4e, 0x1e, 0x50, 0xd2, 0x94, 0x63, 0x5e, 0x68, 0xb2, 0x95, 0x01,
0xb5, 0x3e, 0xae, 0x61, 0xcd, 0x1f, 0xbb, 0x3b, 0x84, 0xcd, 0x52, 0xf6, 0x72, 0x9c,
0xfb, 0xcb, 0xab, 0x06,
],
rvk: [
0x0a, 0xfb, 0xe4, 0x06, 0xa8, 0x91, 0xc3, 0xb8, 0xc3, 0x10, 0xc2, 0x15, 0xbc, 0x68,
0xa9, 0x13, 0xde, 0x7c, 0xda, 0x06, 0xaf, 0x29, 0x42, 0x00, 0x56, 0x46, 0x8d, 0x0c,
0x08, 0x85, 0x5b, 0x28,
],
m: [
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08,
],
sig: [
0x99, 0x35, 0x80, 0xef, 0x93, 0x34, 0x9a, 0x1c, 0x9e, 0xe9, 0x60, 0xca, 0x3e, 0x7c,
0xd0, 0x4c, 0x13, 0xb4, 0xa0, 0xec, 0x4f, 0xd1, 0x80, 0x53, 0xa1, 0x9c, 0xff, 0x77,
0x63, 0x62, 0x09, 0x65, 0xfb, 0xee, 0x96, 0xc1, 0x64, 0x72, 0x30, 0xe3, 0x73, 0xcb,
0x82, 0xb8, 0x1d, 0x00, 0x03, 0x92, 0x23, 0xd3, 0x0b, 0x39, 0x3e, 0xd1, 0x72, 0xc9,
0xb3, 0xc5, 0x63, 0xc6, 0x11, 0x79, 0x22, 0x05,
],
rsig: [
0xcc, 0x7a, 0xae, 0x1c, 0xed, 0xad, 0x2d, 0x7f, 0x6c, 0xe0, 0x4c, 0x19, 0xc5, 0xa5,
0xb6, 0xb7, 0xa6, 0xa0, 0x82, 0x78, 0x5c, 0x54, 0x0c, 0x14, 0xf6, 0x30, 0x9b, 0x06,
0x4d, 0x1f, 0xfa, 0x68, 0x17, 0x29, 0x53, 0xfb, 0xa0, 0xc2, 0xfc, 0xfb, 0x87, 0x5c,
0xa7, 0xf7, 0xea, 0x98, 0xef, 0x55, 0xa0, 0x40, 0x2f, 0xd5, 0x29, 0xcf, 0xcd, 0xdf,
0x99, 0x6c, 0xa2, 0xb8, 0xca, 0x89, 0x90, 0x0a,
],
},
TestVector {
sk: [
0x85, 0x83, 0x6f, 0x98, 0x32, 0xb2, 0x8d, 0xe7, 0xc6, 0x36, 0x13, 0xe2, 0xa6, 0xed,
0x36, 0xfb, 0x1a, 0xb4, 0x4f, 0xb0, 0xc1, 0x3f, 0xa8, 0x79, 0x8c, 0xd9, 0xcd, 0x30,
0x30, 0xd4, 0x55, 0x03,
],
vk: [
0xbf, 0xd5, 0xbc, 0x00, 0xc7, 0xc0, 0x22, 0xaa, 0x89, 0x01, 0xae, 0x08, 0x3c, 0x12,
0xd5, 0x4b, 0x82, 0xf0, 0xdd, 0xff, 0x8e, 0xd6, 0xdb, 0x9a, 0x12, 0xd5, 0x9a, 0x5e,
0xf6, 0xa5, 0xa2, 0xe0,
],
alpha: [
0xa2, 0xe8, 0xb9, 0xe1, 0x6d, 0x6f, 0xf3, 0xca, 0x6c, 0x53, 0xd4, 0xe8, 0x8a, 0xbb,
0xb9, 0x9b, 0xe7, 0xaf, 0x7e, 0x36, 0x59, 0x63, 0x1f, 0x1e, 0xae, 0x1e, 0xff, 0x23,
0x87, 0x4d, 0x8e, 0x0c,
],
rsk: [
0x70, 0x3f, 0x32, 0xa3, 0x41, 0x13, 0xea, 0xe1, 0xb0, 0x79, 0x1f, 0xfe, 0x9d, 0x88,
0x88, 0xf0, 0x01, 0x29, 0x9a, 0xe5, 0x19, 0x68, 0x60, 0x91, 0x91, 0x48, 0x99, 0xef,
0xcc, 0x6c, 0x66, 0x01,
],
rvk: [
0xeb, 0x92, 0x97, 0x03, 0x6c, 0xf5, 0x17, 0xe1, 0x5e, 0x9e, 0xfe, 0x39, 0x75, 0x32,
0x8d, 0xb4, 0x8e, 0xe7, 0xc2, 0x69, 0x4e, 0x94, 0x6d, 0xb2, 0x5f, 0x52, 0x87, 0x88,
0xf6, 0xa1, 0xdb, 0x14,
],
m: [
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09,
],
sig: [
0xce, 0x90, 0xdd, 0xf4, 0xaf, 0x21, 0xaa, 0xc4, 0xd9, 0x41, 0x93, 0xea, 0x16, 0xff,
0x35, 0xcd, 0x93, 0x79, 0x20, 0x4e, 0x7d, 0x8f, 0xf4, 0xc0, 0xf5, 0x41, 0x17, 0xab,
0xb1, 0x6b, 0x7c, 0x85, 0xa0, 0xb1, 0x97, 0xcf, 0x13, 0xab, 0x14, 0xd7, 0xc3, 0xba,
0x68, 0x01, 0x0a, 0xb8, 0x05, 0x12, 0x25, 0x91, 0x3b, 0xdb, 0xc3, 0x9a, 0x51, 0xf6,
0x03, 0x7a, 0xfc, 0x6c, 0xee, 0xcb, 0x0b, 0x06,
],
rsig: [
0xa8, 0x47, 0x74, 0x2e, 0x94, 0x01, 0xcf, 0x22, 0x39, 0x21, 0x3d, 0xc8, 0x81, 0x3e,
0x97, 0x72, 0xe9, 0x7a, 0xf8, 0xd6, 0x7a, 0xdf, 0xfe, 0xab, 0xc8, 0xe6, 0x7f, 0x5d,
0x2d, 0x90, 0xd0, 0xb4, 0x1b, 0xc2, 0x5b, 0x05, 0xf9, 0x4a, 0xce, 0x16, 0x8a, 0xec,
0xc6, 0x58, 0x3e, 0x18, 0xf7, 0x63, 0x74, 0x92, 0xf3, 0x7a, 0x9c, 0xa3, 0x00, 0x20,
0x2b, 0xc0, 0x65, 0xab, 0xd3, 0x80, 0xec, 0x00,
],
},
]
}

View File

@ -228,9 +228,7 @@ mod tests {
use super::{
testing::{arb_note_value_bounded, arb_trapdoor},
CommitmentSum, OverflowError, TrapdoorSum, ValueCommitment, ValueSum,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
};
use crate::sapling::redjubjub;
proptest! {
#[test]
@ -260,8 +258,7 @@ mod tests {
.sum::<CommitmentSum>()
.into_bvk(value_balance);
assert_eq!(redjubjub::PublicKey::from_private(
&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0, bvk.0);
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
}
}
}

View File

@ -2,8 +2,11 @@ use core::fmt::{self, Debug};
use core::iter::Sum;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use group::GroupEncoding;
use redjubjub::Binding;
use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment};
use crate::sapling::{constants::VALUE_COMMITMENT_VALUE_GENERATOR, redjubjub};
use crate::sapling::constants::VALUE_COMMITMENT_VALUE_GENERATOR;
/// A value operation overflowed.
#[derive(Debug)]
@ -88,8 +91,9 @@ impl TrapdoorSum {
/// Transform this trapdoor sum into the corresponding RedJubjub private key.
///
/// This is public for access by `zcash_proofs`.
pub fn into_bsk(self) -> redjubjub::PrivateKey {
redjubjub::PrivateKey(self.0)
pub fn into_bsk(self) -> redjubjub::SigningKey<Binding> {
redjubjub::SigningKey::try_from(self.0.to_bytes())
.expect("valid scalars are valid signing keys")
}
}
@ -156,7 +160,7 @@ impl CommitmentSum {
/// Transform this value commitment sum into the corresponding RedJubjub public key.
///
/// This is public for access by `zcash_proofs`.
pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::PublicKey {
pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
let value: i64 = value_balance.into();
// Compute the absolute value.
@ -175,7 +179,8 @@ impl CommitmentSum {
// Subtract `value_balance` from the sum to get the final bvk.
let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
redjubjub::PublicKey(bvk)
redjubjub::VerificationKey::try_from(bvk.to_bytes())
.expect("valid points are valid verification keys")
}
}

View File

@ -1,10 +1,10 @@
use bellman::{gadgets::multipack, groth16::Proof};
use bls12_381::Bls12;
use group::{ff::PrimeField, Curve, GroupEncoding};
use group::{ff::PrimeField, Curve};
use redjubjub::{Binding, SpendAuth};
use crate::sapling::{
note::ExtractedNoteCommitment,
redjubjub::{PublicKey, Signature},
value::{CommitmentSum, ValueCommitment},
};
@ -36,18 +36,17 @@ impl SaplingVerificationContextInner {
cv: &ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: &[u8; 32],
rk: &PublicKey,
sighash_value: &[u8; 32],
spend_auth_sig: &Signature,
rk: &redjubjub::VerificationKey<SpendAuth>,
zkproof: Proof<Bls12>,
verifier_ctx: &mut C,
spend_auth_sig_verifier: impl FnOnce(&mut C, &PublicKey, [u8; 64], &Signature) -> bool,
spend_auth_sig_verifier: impl FnOnce(&mut C, &redjubjub::VerificationKey<SpendAuth>) -> bool,
proof_verifier: impl FnOnce(&mut C, Proof<Bls12>, [bls12_381::Scalar; 7]) -> bool,
) -> bool {
// The "cv is not small order" happens when a SpendDescription is deserialized.
// This happens when transactions or blocks are received over the network, or when
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
if rk.0.is_small_order().into() {
let rk_affine = jubjub::AffinePoint::from_bytes((*rk).into()).unwrap();
if rk_affine.is_small_order().into() {
return false;
}
@ -57,14 +56,8 @@ impl SaplingVerificationContextInner {
// Grab the nullifier as a sequence of bytes
let nullifier = &nullifier[..];
// Compute the signature's message for rk/spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]);
// Verify the spend_auth_sig
let rk_affine = rk.0.to_affine();
if !spend_auth_sig_verifier(verifier_ctx, rk, data_to_be_signed, spend_auth_sig) {
if !spend_auth_sig_verifier(verifier_ctx, rk) {
return false;
}
@ -145,19 +138,12 @@ impl SaplingVerificationContextInner {
fn final_check<V: Into<i64>>(
&self,
value_balance: V,
sighash_value: &[u8; 32],
binding_sig: Signature,
binding_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool,
binding_sig_verifier: impl FnOnce(redjubjub::VerificationKey<Binding>) -> bool,
) -> bool {
// Compute the final bvk.
let bvk = self.cv_sum.into_bvk(value_balance);
// Compute the signature's message for bvk/binding_sig
let mut data_to_be_signed = [0u8; 64];
data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes());
data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]);
// Verify the binding_sig
binding_sig_verifier(bvk, data_to_be_signed, binding_sig)
binding_sig_verifier(bvk)
}
}

View File

@ -68,21 +68,11 @@ impl BatchValidator {
*spend.anchor(),
&spend.nullifier().0,
spend.rk(),
&sighash,
spend.spend_auth_sig(),
zkproof,
self,
|this, rk, _, spend_auth_sig| {
let rk = redjubjub::VerificationKeyBytes::<redjubjub::SpendAuth>::from(
rk.0.to_bytes(),
);
let spend_auth_sig = {
let mut buf = [0; 64];
spend_auth_sig.write(&mut buf[..]).unwrap();
redjubjub::Signature::<redjubjub::SpendAuth>::from(buf)
};
this.signatures.queue((rk, spend_auth_sig, &sighash));
|this, rk| {
this.signatures
.queue(((*rk).into(), *spend.spend_auth_sig(), &sighash));
true
},
|this, proof, public_inputs| {
@ -125,23 +115,11 @@ impl BatchValidator {
}
// Check the whole-bundle consensus rules, and batch the binding signature.
ctx.final_check(
*bundle.value_balance(),
&sighash,
bundle.authorization().binding_sig,
|bvk, _, binding_sig| {
let bvk =
redjubjub::VerificationKeyBytes::<redjubjub::Binding>::from(bvk.0.to_bytes());
let binding_sig = {
let mut buf = [0; 64];
binding_sig.write(&mut buf[..]).unwrap();
redjubjub::Signature::<redjubjub::Binding>::from(buf)
};
self.signatures.queue((bvk, binding_sig, &sighash));
true
},
)
ctx.final_check(*bundle.value_balance(), |bvk| {
self.signatures
.queue((bvk.into(), bundle.authorization().binding_sig, &sighash));
true
})
}
/// Batch-validates the accumulated bundles.

View File

@ -1,27 +1,25 @@
use bellman::groth16::{verify_proof, Proof};
use bls12_381::Bls12;
use redjubjub::{Binding, SpendAuth};
use super::SaplingVerificationContextInner;
use crate::sapling::{
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
note::ExtractedNoteCommitment,
redjubjub::{PublicKey, Signature},
value::ValueCommitment,
};
/// A context object for verifying the Sapling components of a single Zcash transaction.
pub struct SaplingVerificationContext {
inner: SaplingVerificationContextInner,
zip216_enabled: bool,
}
impl SaplingVerificationContext {
/// Construct a new context to be used with a single transaction.
pub fn new(zip216_enabled: bool) -> Self {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
SaplingVerificationContext {
inner: SaplingVerificationContextInner::new(),
zip216_enabled,
}
}
@ -33,25 +31,20 @@ impl SaplingVerificationContext {
cv: &ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: &[u8; 32],
rk: PublicKey,
rk: redjubjub::VerificationKey<SpendAuth>,
sighash_value: &[u8; 32],
spend_auth_sig: Signature,
spend_auth_sig: redjubjub::Signature<SpendAuth>,
zkproof: Proof<Bls12>,
verifying_key: &PreparedSpendVerifyingKey,
) -> bool {
let zip216_enabled = self.zip216_enabled;
self.inner.check_spend(
cv,
anchor,
nullifier,
&rk,
sighash_value,
&spend_auth_sig,
zkproof,
&mut (),
|_, rk, msg, spend_auth_sig| {
rk.verify_with_zip216(&msg, spend_auth_sig, SPENDING_KEY_GENERATOR, zip216_enabled)
},
|_, rk| rk.verify(sighash_value, &spend_auth_sig).is_ok(),
|_, proof, public_inputs| {
verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok()
},
@ -81,20 +74,10 @@ impl SaplingVerificationContext {
&self,
value_balance: V,
sighash_value: &[u8; 32],
binding_sig: Signature,
binding_sig: redjubjub::Signature<Binding>,
) -> bool {
self.inner.final_check(
value_balance,
sighash_value,
binding_sig,
|bvk, msg, binding_sig| {
bvk.verify_with_zip216(
&msg,
&binding_sig,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
self.zip216_enabled,
)
},
)
self.inner.final_check(value_balance, |bvk| {
bvk.verify(sighash_value, &binding_sig).is_ok()
})
}
}

View File

@ -16,7 +16,10 @@ use crate::{
keys::{prf_expand, prf_expand_vec},
sapling::{
constants::PROOF_GENERATION_KEY_GENERATOR,
keys::{DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey},
keys::{
DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey,
SpendAuthorizingKey,
},
SaplingIvk,
},
zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope},
@ -98,7 +101,7 @@ pub fn sapling_derive_internal_fvk(
(
FullViewingKey {
vk: ViewingKey {
ak: fvk.vk.ak,
ak: fvk.vk.ak.clone(),
nk: nk_internal,
},
ovk: ovk_internal,
@ -408,6 +411,12 @@ impl ExtendedSpendingKey {
xsk
}
/// Derives the child key at the given (hardened) index.
///
/// # Panics
///
/// Panics if the child key has `ask = 0`. This has a negligible probability of
/// occurring.
#[must_use]
pub fn derive_child(&self, i: ChildIndex) -> Self {
let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
@ -431,10 +440,15 @@ impl ExtendedSpendingKey {
expsk: {
let mut ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
let mut nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
ask.add_assign(&self.expsk.ask);
ask.add_assign(self.expsk.ask.to_scalar());
nsk.add_assign(&self.expsk.nsk);
let ovk = derive_child_ovk(&self.expsk.ovk, i_l);
ExpandedSpendingKey { ask, nsk, ovk }
ExpandedSpendingKey {
ask: SpendAuthorizingKey::from_scalar(ask)
.expect("negligible chance of ask == 0"),
nsk,
ovk,
}
},
dk: self.dk.derive_child(i_l),
}
@ -474,7 +488,7 @@ impl ExtendedSpendingKey {
child_index: self.child_index,
chain_code: self.chain_code,
expsk: ExpandedSpendingKey {
ask: self.expsk.ask,
ask: self.expsk.ask.clone(),
nsk: nsk_internal,
ovk: ovk_internal,
},
@ -1611,7 +1625,7 @@ mod tests {
let xsks = [m, m_1h, m_1h_2h, m_1h_2h_3h];
for (xsk, tv) in xsks.iter().zip(test_vectors.iter()) {
assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap());
assert_eq!(xsk.expsk.ask.to_bytes(), tv.ask.unwrap());
assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap());
assert_eq!(xsk.expsk.ovk.0, tv.ovk);
@ -1623,7 +1637,7 @@ mod tests {
assert_eq!(&ser[..], &tv.xsk.unwrap()[..]);
let internal_xsk = xsk.derive_internal();
assert_eq!(internal_xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap());
assert_eq!(internal_xsk.expsk.ask.to_bytes(), tv.ask.unwrap());
assert_eq!(
internal_xsk.expsk.nsk.to_repr().as_ref(),
tv.internal_nsk.unwrap()

View File

@ -15,7 +15,7 @@ use crate::{
self,
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
prover::{OutputProver, SpendProver},
redjubjub, Note, PaymentAddress,
Note, PaymentAddress,
},
transaction::{
components::{
@ -162,7 +162,7 @@ pub struct Builder<'a, P, R> {
// `add_sapling_spend` or `add_orchard_spend`, we will build an unauthorized, unproven
// transaction, and then the caller will be responsible for using the spending keys or their
// derivatives for proving and signing to complete transaction creation.
sapling_asks: Vec<redjubjub::PrivateKey>,
sapling_asks: Vec<sapling::keys::SpendAuthorizingKey>,
orchard_saks: Vec<orchard::keys::SpendAuthorizingKey>,
#[cfg(feature = "zfuture")]
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
@ -334,8 +334,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
self.sapling_builder
.add_spend(&mut self.rng, &extsk, note, merkle_path)?;
self.sapling_asks
.push(redjubjub::PrivateKey(extsk.expsk.ask));
self.sapling_asks.push(extsk.expsk.ask);
Ok(())
}

View File

@ -1,4 +1,5 @@
use ff::PrimeField;
use redjubjub::SpendAuth;
use std::io::{self, Read, Write};
@ -12,7 +13,6 @@ use crate::{
SpendDescription, SpendDescriptionV5,
},
note::ExtractedNoteCommitment,
redjubjub::{self, PublicKey, Signature},
value::ValueCommitment,
Nullifier,
},
@ -81,15 +81,20 @@ fn read_nullifier<R: Read>(mut reader: R) -> io::Result<Nullifier> {
/// Consensus rules (§4.4):
/// - Canonical encoding is enforced here.
/// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
fn read_rk<R: Read>(mut reader: R) -> io::Result<PublicKey> {
PublicKey::read(&mut reader)
fn read_rk<R: Read>(mut reader: R) -> io::Result<redjubjub::VerificationKey<SpendAuth>> {
let mut bytes = [0; 32];
reader.read_exact(&mut bytes)?;
redjubjub::VerificationKey::try_from(bytes)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
/// Consensus rules (§4.4):
/// - Canonical encoding is enforced here.
/// - Signature validity is enforced in SaplingVerificationContext::check_spend()
fn read_spend_auth_sig<R: Read>(mut reader: R) -> io::Result<Signature> {
Signature::read(&mut reader)
fn read_spend_auth_sig<R: Read>(mut reader: R) -> io::Result<redjubjub::Signature<SpendAuth>> {
let mut sig = [0; 64];
reader.read_exact(&mut sig)?;
Ok(redjubjub::Signature::from(sig))
}
#[cfg(feature = "temporary-zcashd")]
@ -127,9 +132,9 @@ fn write_spend_v4<W: Write>(mut writer: W, spend: &SpendDescription<Authorized>)
writer.write_all(&spend.cv().to_bytes())?;
writer.write_all(spend.anchor().to_repr().as_ref())?;
writer.write_all(&spend.nullifier().0)?;
spend.rk().write(&mut writer)?;
writer.write_all(&<[u8; 32]>::from(*spend.rk()))?;
writer.write_all(spend.zkproof())?;
spend.spend_auth_sig().write(&mut writer)
writer.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig()))
}
fn write_spend_v5_without_witness_data<W: Write>(
@ -138,7 +143,7 @@ fn write_spend_v5_without_witness_data<W: Write>(
) -> io::Result<()> {
writer.write_all(&spend.cv().to_bytes())?;
writer.write_all(&spend.nullifier().0)?;
spend.rk().write(&mut writer)
writer.write_all(&<[u8; 32]>::from(*spend.rk()))
}
fn read_spend_v5<R: Read>(mut reader: &mut R) -> io::Result<SpendDescriptionV5> {
@ -350,7 +355,9 @@ pub(crate) fn read_v5_bundle<R: Read>(
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| read_zkproof(r))?;
let binding_sig = if n_spends > 0 || n_outputs > 0 {
Some(redjubjub::Signature::read(&mut reader)?)
let mut sig = [0; 64];
reader.read_exact(&mut sig)?;
Some(redjubjub::Signature::from(sig))
} else {
None
};
@ -413,7 +420,7 @@ pub(crate) fn write_v5_bundle<W: Write>(
Array::write(
&mut writer,
bundle.shielded_spends().iter().map(|s| s.spend_auth_sig()),
|w, e| e.write(w),
|w, e| w.write_all(&<[u8; 64]>::from(**e)),
)?;
Array::write(
@ -423,7 +430,7 @@ pub(crate) fn write_v5_bundle<W: Write>(
)?;
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
bundle.authorization().binding_sig.write(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?;
}
} else {
CompactSize::write(&mut writer, 0)?;

View File

@ -23,7 +23,7 @@ use zcash_encoding::{CompactSize, Vector};
use crate::{
consensus::{BlockHeight, BranchId},
sapling::{self, builder as sapling_builder, redjubjub},
sapling::{self, builder as sapling_builder},
};
use self::{
@ -626,7 +626,9 @@ impl Transaction {
let binding_sig = if version.has_sapling()
&& !(shielded_spends.is_empty() && shielded_outputs.is_empty())
{
Some(redjubjub::Signature::read(&mut reader)?)
let mut sig = [0; 64];
reader.read_exact(&mut sig)?;
Some(redjubjub::Signature::from(sig))
} else {
None
};
@ -786,7 +788,7 @@ impl Transaction {
if self.version.has_sapling() {
if let Some(bundle) = self.sapling_bundle.as_ref() {
bundle.authorization().binding_sig.write(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?;
}
}

View File

@ -112,7 +112,7 @@ fn shielded_spends_hash<
data.extend_from_slice(&s_spend.cv().to_bytes());
data.extend_from_slice(s_spend.anchor().to_repr().as_ref());
data.extend_from_slice(s_spend.nullifier().as_ref());
s_spend.rk().write(&mut data).unwrap();
data.extend_from_slice(&<[u8; 32]>::from(*s_spend.rk()));
data.extend_from_slice(s_spend.zkproof());
}
Blake2bParams::new()

View File

@ -154,7 +154,7 @@ pub(crate) fn hash_sapling_spends<A: sapling::bundle::Authorization>(
nh.write_all(&s_spend.cv().to_bytes()).unwrap();
nh.write_all(&s_spend.anchor().to_repr()).unwrap();
s_spend.rk().write(&mut nh).unwrap();
nh.write_all(&<[u8; 32]>::from(*s_spend.rk())).unwrap();
}
let compact_digest = ch.finalize();
@ -476,14 +476,16 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
}
for spend in bundle.shielded_spends() {
spend.spend_auth_sig().write(&mut h).unwrap();
h.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig()))
.unwrap();
}
for output in bundle.shielded_outputs() {
h.write_all(output.zkproof()).unwrap();
}
bundle.authorization().binding_sig.write(&mut h).unwrap();
h.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))
.unwrap();
}
h.finalize()
}