Move sapling key tests to own module, test key derivation against test vectors

Impl a few From<[u8; 32]>'s for a few key types.
This commit is contained in:
Deirdre Connolly 2020-04-15 18:22:30 -04:00 committed by Deirdre Connolly
parent ce1415a8ee
commit e508d09e9b
3 changed files with 166 additions and 83 deletions

View File

@ -144,7 +144,7 @@ mod tests {
let authorizing_key = sapling::AuthorizingKey::from(spend_authorizing_key); let authorizing_key = sapling::AuthorizingKey::from(spend_authorizing_key);
let nullifier_deriving_key = sapling::NullifierDerivingKey::from(proof_authorizing_key); let nullifier_deriving_key = sapling::NullifierDerivingKey::from(proof_authorizing_key);
let incoming_viewing_key = let incoming_viewing_key =
sapling::IncomingViewingKey::from(authorizing_key, nullifier_deriving_key); sapling::IncomingViewingKey::from_keys(authorizing_key, nullifier_deriving_key);
let diversifier = sapling::Diversifier::new(&mut OsRng); let diversifier = sapling::Diversifier::new(&mut OsRng);
let transmission_key = sapling::TransmissionKey::from(incoming_viewing_key, diversifier); let transmission_key = sapling::TransmissionKey::from(incoming_viewing_key, diversifier);

View File

@ -8,6 +8,11 @@
//! [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents //! [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
//! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys //! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys
#[cfg(test)]
mod test_vectors;
#[cfg(test)]
mod tests;
use std::{ use std::{
convert::{From, Into, TryFrom}, convert::{From, Into, TryFrom},
fmt, fmt,
@ -22,8 +27,8 @@ use jubjub;
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
use redjubjub::{self, SpendAuth}; use redjubjub::{self, SpendAuth};
#[cfg(test)] // #[cfg(test)]
use proptest::{array, prelude::*}; // use proptest::{array, prelude::*};
#[cfg(test)] #[cfg(test)]
use proptest_derive::Arbitrary; use proptest_derive::Arbitrary;
@ -339,6 +344,12 @@ impl From<[u8; 32]> for OutgoingViewingKey {
} }
} }
impl From<OutgoingViewingKey> for [u8; 32] {
fn from(nk: OutgoingViewingKey) -> [u8; 32] {
nk.0
}
}
impl From<SpendingKey> for OutgoingViewingKey { impl From<SpendingKey> for OutgoingViewingKey {
/// For this invocation of Blake2b-512 as _PRF^expand_, t=2. /// For this invocation of Blake2b-512 as _PRF^expand_, t=2.
/// ///
@ -407,6 +418,12 @@ impl From<[u8; 32]> for NullifierDerivingKey {
} }
} }
impl From<NullifierDerivingKey> for [u8; 32] {
fn from(nk: NullifierDerivingKey) -> [u8; 32] {
nk.0.to_bytes()
}
}
impl Deref for NullifierDerivingKey { impl Deref for NullifierDerivingKey {
type Target = jubjub::AffinePoint; type Target = jubjub::AffinePoint;
@ -465,6 +482,8 @@ pub struct IncomingViewingKey {
scalar: Scalar, scalar: Scalar,
} }
// TODO: impl a top-level to_bytes or PartialEq between this and [u8; 32]
// TODO: impl a From that accepts a Network? // TODO: impl a From that accepts a Network?
impl Deref for IncomingViewingKey { impl Deref for IncomingViewingKey {
@ -475,6 +494,32 @@ impl Deref for IncomingViewingKey {
} }
} }
impl From<[u8; 32]> for IncomingViewingKey {
/// Generate an _IncomingViewingKey_ from existing bytes.
fn from(mut bytes: [u8; 32]) -> Self {
// Drop the most significant five bits, so it can be interpreted
// as a scalar.
//
// I don't want to put this inside crh_ivk, but does it belong
// inside Scalar/Fr::from_bytes()? That seems the better
// place...
//
// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/primitives.rs#L86
bytes[31] &= 0b0000_0111;
Self {
network: Network::default(),
scalar: Scalar::from_bytes(&bytes).unwrap(),
}
}
}
impl From<IncomingViewingKey> for [u8; 32] {
fn from(ivk: IncomingViewingKey) -> [u8; 32] {
ivk.scalar.to_bytes()
}
}
impl fmt::Debug for IncomingViewingKey { impl fmt::Debug for IncomingViewingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("IncomingViewingKey") f.debug_tuple("IncomingViewingKey")
@ -530,26 +575,15 @@ impl IncomingViewingKey {
// [spending key]." - [§4.2.2][ps] // [spending key]." - [§4.2.2][ps]
// //
// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents // [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
pub fn from( //
// TODO: won't let me name this `from(arg1, arg2)` when I have From impl'd above?
pub fn from_keys(
authorizing_key: AuthorizingKey, authorizing_key: AuthorizingKey,
nullifier_deriving_key: NullifierDerivingKey, nullifier_deriving_key: NullifierDerivingKey,
) -> IncomingViewingKey { ) -> Self {
let mut hash_bytes = crh_ivk(authorizing_key.into(), nullifier_deriving_key.to_bytes()); let hash_bytes = crh_ivk(authorizing_key.into(), nullifier_deriving_key.to_bytes());
// Drop the most significant five bits, so it can be interpreted IncomingViewingKey::from(hash_bytes)
// as a scalar.
//
// I don't want to put this inside crh_ivk, but does it belong
// inside Scalar/Fr::from_bytes()? That seems the better
// place...
//
// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/primitives.rs#L86
hash_bytes[31] &= 0b0000_0111;
Self {
network: Network::default(),
scalar: Scalar::from_bytes(&hash_bytes).unwrap(),
}
} }
} }
@ -563,6 +597,8 @@ impl IncomingViewingKey {
#[cfg_attr(test, derive(Arbitrary))] #[cfg_attr(test, derive(Arbitrary))]
pub struct Diversifier(pub [u8; 11]); pub struct Diversifier(pub [u8; 11]);
// TODO: _DefaultDiversifier_
impl fmt::Debug for Diversifier { impl fmt::Debug for Diversifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Diversifier") f.debug_tuple("Diversifier")
@ -650,25 +686,6 @@ impl TransmissionKey {
} }
} }
#[cfg(test)]
impl Arbitrary for TransmissionKey {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(array::uniform32(any::<u8>()))
.prop_map(|_transmission_key_bytes| {
// TODO: actually generate something better than the identity.
//
// return Self::from_bytes(transmission_key_bytes);
return Self(jubjub::AffinePoint::identity());
})
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
/// Magic human-readable strings used to identify what networks /// Magic human-readable strings used to identify what networks
/// Sapling FullViewingKeys are associated with when encoded/decoded /// Sapling FullViewingKeys are associated with when encoded/decoded
/// with bech32. /// with bech32.
@ -754,47 +771,3 @@ impl std::str::FromStr for FullViewingKey {
} }
} }
} }
#[cfg(test)]
mod tests {
use rand_core::OsRng;
use super::*;
#[test]
fn derive() {
let spending_key = SpendingKey::new(&mut OsRng);
let spend_authorizing_key = SpendAuthorizingKey::from(spending_key);
let proof_authorizing_key = ProofAuthorizingKey::from(spending_key);
let outgoing_viewing_key = OutgoingViewingKey::from(spending_key);
let authorizing_key = AuthorizingKey::from(spend_authorizing_key);
let nullifier_deriving_key = NullifierDerivingKey::from(proof_authorizing_key);
// "If ivk = 0, discard this key and start over with a new
// [spending key]."
// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
let incoming_viewing_key =
IncomingViewingKey::from(authorizing_key, nullifier_deriving_key);
let diversifier = Diversifier::new(&mut OsRng);
let _transmission_key = TransmissionKey::from(incoming_viewing_key, diversifier);
let _full_viewing_key = FullViewingKey {
network: Network::default(),
authorizing_key,
nullifier_deriving_key,
outgoing_viewing_key,
};
}
// TODO: test vectors, not just random data
}
#[cfg(test)]
proptest! {
//#[test]
// fn test() {}
}

View File

@ -0,0 +1,110 @@
#[cfg(test)]
use proptest::{array, prelude::*};
#[cfg(test)]
use proptest_derive::Arbitrary;
use super::*;
#[cfg(test)]
impl Arbitrary for TransmissionKey {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(array::uniform32(any::<u8>()))
.prop_map(|_transmission_key_bytes| {
// TODO: actually generate something better than the identity.
//
// return Self::from_bytes(transmission_key_bytes);
return Self(jubjub::AffinePoint::identity());
})
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
#[cfg(test)]
mod tests {
use rand_core::OsRng;
use super::*;
#[test]
fn derive() {
let spending_key = SpendingKey::new(&mut OsRng);
let spend_authorizing_key = SpendAuthorizingKey::from(spending_key);
let proof_authorizing_key = ProofAuthorizingKey::from(spending_key);
let outgoing_viewing_key = OutgoingViewingKey::from(spending_key);
let authorizing_key = AuthorizingKey::from(spend_authorizing_key);
let nullifier_deriving_key = NullifierDerivingKey::from(proof_authorizing_key);
// "If ivk = 0, discard this key and start over with a new
// [spending key]."
// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
let incoming_viewing_key =
IncomingViewingKey::from_keys(authorizing_key, nullifier_deriving_key);
let diversifier = Diversifier::new(&mut OsRng);
let _transmission_key = TransmissionKey::from(incoming_viewing_key, diversifier);
let _full_viewing_key = FullViewingKey {
network: Network::default(),
authorizing_key,
nullifier_deriving_key,
outgoing_viewing_key,
};
}
#[test]
fn derive_for_each_test_vector() {
for test_vector in test_vectors::TEST_VECTORS.iter() {
let spending_key = SpendingKey::from(test_vector.sk);
let spend_authorizing_key = SpendAuthorizingKey::from(spending_key);
assert_eq!(spend_authorizing_key.to_bytes(), test_vector.ask);
let proof_authorizing_key = ProofAuthorizingKey::from(spending_key);
assert_eq!(proof_authorizing_key.to_bytes(), test_vector.nsk);
let outgoing_viewing_key = OutgoingViewingKey::from(spending_key);
assert_eq!(
Into::<[u8; 32]>::into(outgoing_viewing_key),
test_vector.ovk
);
let authorizing_key = AuthorizingKey::from(spend_authorizing_key);
assert_eq!(Into::<[u8; 32]>::into(authorizing_key), test_vector.ak);
let nullifier_deriving_key = NullifierDerivingKey::from(proof_authorizing_key);
assert_eq!(
Into::<[u8; 32]>::into(nullifier_deriving_key),
test_vector.nk
);
let incoming_viewing_key =
IncomingViewingKey::from_keys(authorizing_key, nullifier_deriving_key);
assert_eq!(incoming_viewing_key.scalar.to_bytes(), test_vector.ivk);
// TODO: replace with _DefaultDiversifier_ with spending
// key bytes as input.
let diversifier = Diversifier(test_vector.default_d);
assert_eq!(diversifier.0, test_vector.default_d);
let transmission_key = TransmissionKey::from(incoming_viewing_key, diversifier);
assert_eq!(transmission_key.to_bytes(), test_vector.default_pk_d);
let _full_viewing_key = FullViewingKey {
network: Network::default(),
authorizing_key,
nullifier_deriving_key,
outgoing_viewing_key,
};
}
}
}
#[cfg(test)]
proptest! {
//#[test]
// fn test() {}
}