diff --git a/zebra-chain/src/orchard/keys.rs b/zebra-chain/src/orchard/keys.rs index ad0787c2d..4029ffdf8 100644 --- a/zebra-chain/src/orchard/keys.rs +++ b/zebra-chain/src/orchard/keys.rs @@ -17,6 +17,7 @@ use std::{ use aes::Aes256; use bech32::{self, FromBase32, ToBase32, Variant}; +use bitvec::prelude::*; use fpe::ff1::{BinaryNumeralString, FF1}; use group::GroupEncoding; use halo2::{ @@ -65,7 +66,7 @@ fn prp_d(K: [u8; 32], d: [u8; 11]) -> [u8; 11] { // TODO: This is basically a duplicate of the one in our sapling module, its // definition in the draft NU5 spec is incomplete so I'm putting it here in case // it changes. -fn prf_expand(sk: [u8; 32], t: &[&[u8]]) -> [u8; 64] { +fn prf_expand(sk: [u8; 32], t: Vec<&[u8]>) -> [u8; 64] { let mut state = blake2b_simd::Params::new() .hash_length(64) .personal(b"Zcash_ExpandSeed") @@ -244,7 +245,7 @@ impl From for SpendAuthorizingKey { /// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents /// https://zips.z.cash/protocol/protocol.pdf#concreteprfs fn from(spending_key: SpendingKey) -> SpendAuthorizingKey { - let hash_bytes = prf_expand(spending_key.bytes, &[&[6]]); + let hash_bytes = prf_expand(spending_key.bytes, vec![&[6]]); // Handles ToScalar^Orchard Self(pallas::Scalar::from_bytes_wide(&hash_bytes)) @@ -296,7 +297,9 @@ impl From for OutgoingViewingKey { let R = fvk.to_R(); // let ovk be the remaining [32] bytes of R [which is 64 bytes] - Self::from(R[32..64]) + let ovk_bytes: [u8; 32] = R[32..64].try_into().expect("32 byte array"); + + Self::from(ovk_bytes) } } @@ -333,6 +336,7 @@ impl From for [u8; 32] { impl From for SpendValidatingKey { fn from(ask: SpendAuthorizingKey) -> Self { let sk = redpallas::SigningKey::::try_from(<[u8; 32]>::from(ask)).unwrap(); + Self(redpallas::VerificationKey::from(&sk)) } } @@ -393,7 +397,7 @@ impl From for NullifierDerivingKey { fn from(sk: SpendingKey) -> Self { Self(pallas::Base::from_bytes_wide(&prf_expand( sk.into(), - &[&[7]], + vec![&[7]], ))) } } @@ -421,7 +425,7 @@ impl From for IvkCommitRandomness { /// /// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents fn from(sk: SpendingKey) -> Self { - let scalar = pallas::Scalar::from_bytes_wide(&prf_expand(sk.into(), &[&[8]])); + let scalar = pallas::Scalar::from_bytes_wide(&prf_expand(sk.into(), vec![&[8]])); Self(scalar) } @@ -433,6 +437,12 @@ impl From for [u8; 32] { } } +impl From for pallas::Scalar { + fn from(rivk: IvkCommitRandomness) -> Self { + rivk.0 + } +} + impl TryFrom<[u8; 32]> for IvkCommitRandomness { type Error = &'static str; @@ -506,18 +516,22 @@ impl From for IncomingViewingKey { /// /// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents /// https://zips.z.cash/protocol/protocol.pdf#concreteprfs + #[allow(non_snake_case)] fn from(fvk: FullViewingKey) -> Self { - let M = ( - fvk.spend_validating_key.into(), - fvk.nullifier_deriving_key.into(), - ) - .concat(); + let mut M: BitVec = BitVec::new(); + + M.append(&mut BitVec::::from_slice( + &<[u8; 32]>::from(fvk.spend_validating_key)[..], + )); + M.append(&mut BitVec::::from_slice( + &<[u8; 32]>::from(fvk.nullifier_deriving_key)[..], + )); // Commit^ivk_rivk let commit_x = sinsemilla_short_commit( fvk.ivk_commit_randomness.into(), b"z.cash:Orchard-CommitIvk", - M, + &M, ); Self { @@ -649,15 +663,12 @@ impl FullViewingKey { // let K = I2LEBSP_l_sk(rivk) let K: [u8; 32] = self.ivk_commit_randomness.into(); + let t: Vec<&[u8]> = vec![&[0x82u8]]; + t.push(&<[u8; 32]>::from(self.spend_validating_key)); + t.push(&<[u8; 32]>::from(self.nullifier_deriving_key)); + // let R = PRF^expand_K( [0x82] || I2LEOSP256(ak) || I2LEOSP256(nk) ) - prf_expand( - K, - &[ - &[0x82u8], - self.spend_validating_key.into(), - self.nullifier_deriving_key.into(), - ], - ) + prf_expand(K, t) } } diff --git a/zebra-chain/src/orchard/sinsemilla.rs b/zebra-chain/src/orchard/sinsemilla.rs index 5b5c52ffd..29df9ec77 100644 --- a/zebra-chain/src/orchard/sinsemilla.rs +++ b/zebra-chain/src/orchard/sinsemilla.rs @@ -121,8 +121,8 @@ pub fn sinsemilla_hash(D: &[u8], M: &BitVec) -> pallas::Base { /// https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit #[allow(non_snake_case)] pub fn sinsemilla_commit(r: pallas::Scalar, D: &[u8], M: &BitVec) -> pallas::Point { - sinsemilla_hash_to_point((D, b"-M").concat(), M) - + pallas_group_hash((D, b"r").concat(), b"") * r + sinsemilla_hash_to_point(&[D, b"-M"].concat(), M) + + pallas_group_hash(&[D, b"r"].concat(), b"") * r } /// SinsemillaShortCommit_r(D, M) := Extract_P(SinsemillaCommit_r(D, M)) diff --git a/zebra-chain/src/primitives/redpallas.rs b/zebra-chain/src/primitives/redpallas.rs index ef3754e01..3ef64adf5 100644 --- a/zebra-chain/src/primitives/redpallas.rs +++ b/zebra-chain/src/primitives/redpallas.rs @@ -48,12 +48,12 @@ mod private { } impl Sealed for Binding { fn basepoint() -> pallas::Point { - pallas::Point::from_bytes(constants::BINDINGSIG_BASEPOINT_BYTES).unwrap() + pallas::Point::from_bytes(&constants::BINDINGSIG_BASEPOINT_BYTES).unwrap() } } impl Sealed for SpendAuth { fn basepoint() -> pallas::Point { - pallas::Point::from_bytes(constants::SPENDAUTHSIG_BASEPOINT_BYTES).unwrap() + pallas::Point::from_bytes(&constants::SPENDAUTHSIG_BASEPOINT_BYTES).unwrap() } } } diff --git a/zebra-chain/src/primitives/redpallas/error.rs b/zebra-chain/src/primitives/redpallas/error.rs new file mode 100644 index 000000000..063925c61 --- /dev/null +++ b/zebra-chain/src/primitives/redpallas/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)] +pub enum Error { + #[error("Malformed signing key encoding.")] + MalformedSigningKey, + #[error("Malformed verification key encoding.")] + MalformedVerificationKey, + #[error("Invalid signature.")] + InvalidSignature, +} diff --git a/zebra-chain/src/primitives/redpallas/verification_key.rs b/zebra-chain/src/primitives/redpallas/verification_key.rs index ff969eaa5..04d49dfb9 100644 --- a/zebra-chain/src/primitives/redpallas/verification_key.rs +++ b/zebra-chain/src/primitives/redpallas/verification_key.rs @@ -7,7 +7,7 @@ use std::{ use group::{cofactor::CofactorGroup, GroupEncoding}; use halo2::pasta::pallas; -use super::{Error, SigType, SigningKey}; +use super::{Error, SigType}; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedPallas verification key. @@ -79,6 +79,18 @@ impl From> for [u8; 32] { } } +impl From<&pallas::Scalar> for VerificationKey { + fn from(s: &pallas::Scalar) -> VerificationKey { + let point = &T::basepoint() * s; + let bytes = VerificationKeyBytes { + bytes: pallas::Affine::from(&point).to_bytes(), + _marker: PhantomData, + }; + + Self { bytes, point } + } +} + impl TryFrom> for VerificationKey { type Error = Error; @@ -109,14 +121,3 @@ impl TryFrom<[u8; 32]> for VerificationKey { VerificationKeyBytes::from(bytes).try_into() } } - -impl VerificationKey { - pub(crate) fn from(s: &pallas::Scalar) -> VerificationKey { - let point = &T::basepoint() * s; - let bytes = VerificationKeyBytes { - bytes: pallas::Affine::from(&point).to_bytes(), - _marker: PhantomData, - }; - VerificationKey { bytes, point } - } -}