diff --git a/zebra-chain/src/addresses/sapling.rs b/zebra-chain/src/addresses/sapling.rs index 6353efa40..28fc77bb1 100644 --- a/zebra-chain/src/addresses/sapling.rs +++ b/zebra-chain/src/addresses/sapling.rs @@ -144,7 +144,7 @@ mod tests { let authorizing_key = sapling::AuthorizingKey::from(spend_authorizing_key); let nullifier_deriving_key = sapling::NullifierDerivingKey::from(proof_authorizing_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 transmission_key = sapling::TransmissionKey::from(incoming_viewing_key, diversifier); diff --git a/zebra-chain/src/keys/sapling.rs b/zebra-chain/src/keys/sapling.rs index 7c1fd9c71..b587041df 100644 --- a/zebra-chain/src/keys/sapling.rs +++ b/zebra-chain/src/keys/sapling.rs @@ -8,6 +8,11 @@ //! [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents //! [3.1]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys +#[cfg(test)] +mod test_vectors; +#[cfg(test)] +mod tests; + use std::{ convert::{From, Into, TryFrom}, fmt, @@ -22,8 +27,8 @@ use jubjub; use rand_core::{CryptoRng, RngCore}; use redjubjub::{self, SpendAuth}; -#[cfg(test)] -use proptest::{array, prelude::*}; +// #[cfg(test)] +// use proptest::{array, prelude::*}; #[cfg(test)] use proptest_derive::Arbitrary; @@ -339,6 +344,12 @@ impl From<[u8; 32]> for OutgoingViewingKey { } } +impl From for [u8; 32] { + fn from(nk: OutgoingViewingKey) -> [u8; 32] { + nk.0 + } +} + impl From for OutgoingViewingKey { /// For this invocation of Blake2b-512 as _PRF^expand_, t=2. /// @@ -407,6 +418,12 @@ impl From<[u8; 32]> for NullifierDerivingKey { } } +impl From for [u8; 32] { + fn from(nk: NullifierDerivingKey) -> [u8; 32] { + nk.0.to_bytes() + } +} + impl Deref for NullifierDerivingKey { type Target = jubjub::AffinePoint; @@ -465,6 +482,8 @@ pub struct IncomingViewingKey { scalar: Scalar, } +// TODO: impl a top-level to_bytes or PartialEq between this and [u8; 32] + // TODO: impl a From that accepts a Network? 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 for [u8; 32] { + fn from(ivk: IncomingViewingKey) -> [u8; 32] { + ivk.scalar.to_bytes() + } +} + impl fmt::Debug for IncomingViewingKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("IncomingViewingKey") @@ -530,26 +575,15 @@ impl IncomingViewingKey { // [spending key]." - [ยง4.2.2][ps] // // [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, nullifier_deriving_key: NullifierDerivingKey, - ) -> IncomingViewingKey { - let mut hash_bytes = crh_ivk(authorizing_key.into(), nullifier_deriving_key.to_bytes()); + ) -> Self { + let hash_bytes = crh_ivk(authorizing_key.into(), nullifier_deriving_key.to_bytes()); - // 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 - hash_bytes[31] &= 0b0000_0111; - - Self { - network: Network::default(), - scalar: Scalar::from_bytes(&hash_bytes).unwrap(), - } + IncomingViewingKey::from(hash_bytes) } } @@ -563,6 +597,8 @@ impl IncomingViewingKey { #[cfg_attr(test, derive(Arbitrary))] pub struct Diversifier(pub [u8; 11]); +// TODO: _DefaultDiversifier_ + impl fmt::Debug for Diversifier { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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::())) - .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; -} - /// Magic human-readable strings used to identify what networks /// Sapling FullViewingKeys are associated with when encoded/decoded /// 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() {} -} diff --git a/zebra-chain/src/keys/sapling/tests.rs b/zebra-chain/src/keys/sapling/tests.rs new file mode 100644 index 000000000..3468b8523 --- /dev/null +++ b/zebra-chain/src/keys/sapling/tests.rs @@ -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::())) + .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; +} + +#[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() {} +}