From ce7b7df0cca40d91910545926d4d3d5863dc7a27 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 Dec 2023 00:53:18 +0000 Subject: [PATCH] zcash_primitives: Switch to type-safe `PRF^expand` Part of zcash/librustzcash#1044. --- Cargo.lock | 10 ++++++ Cargo.toml | 1 + zcash_primitives/CHANGELOG.md | 3 ++ zcash_primitives/Cargo.toml | 1 + zcash_primitives/src/keys.rs | 21 ----------- zcash_primitives/src/legacy/keys.rs | 10 +++--- zcash_primitives/src/sapling/keys.rs | 8 ++--- zcash_primitives/src/sapling/note.rs | 6 ++-- .../src/sapling/note_encryption.rs | 2 +- zcash_primitives/src/sapling/zip32.rs | 36 ++++++++++--------- 10 files changed, 47 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64b0d408a..770a4093e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3114,6 +3114,7 @@ dependencies = [ "zcash_address", "zcash_encoding", "zcash_note_encryption", + "zcash_spec", "zip32", ] @@ -3139,6 +3140,15 @@ dependencies = [ "zcash_primitives", ] +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "zerocopy" version = "0.7.25" diff --git a/Cargo.toml b/Cargo.toml index 555e8ca4e..83421e4bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ ff = "0.13" group = "0.13" incrementalmerkletree = "0.5" shardtree = "0.2" +zcash_spec = "0.1" # Payment protocols # - Sapling diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index bc243b660..d0ce59b36 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -207,6 +207,9 @@ and this library adheres to Rust's notion of ### Removed - `zcash_primitives::constants`: - All `const` values (moved to `zcash_primitives::sapling::constants`). +- `zcash_primitives::keys`: + - `PRF_EXPAND_PERSONALIZATION` + - `prf_expand, prf_expand_vec` (use `zcash_spec::PrfExpand` instead). - `zcash_primitives::sapling`: - `bundle`: - `SpendDescription::{read, read_nullifier, read_rk, read_spend_auth_sig}` diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index 0f7d4629a..cffe94683 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -48,6 +48,7 @@ group = { workspace = true, features = ["wnaf-memuse"] } jubjub.workspace = true nonempty.workspace = true orchard.workspace = true +zcash_spec.workspace = true # - Note Commitment Trees incrementalmerkletree = { workspace = true, features = ["legacy-api"] } diff --git a/zcash_primitives/src/keys.rs b/zcash_primitives/src/keys.rs index ea085b2a1..c263127ba 100644 --- a/zcash_primitives/src/keys.rs +++ b/zcash_primitives/src/keys.rs @@ -1,22 +1 @@ -use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; - pub use crate::sapling::keys::OutgoingViewingKey; - -pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed"; - -/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) -pub fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bHash { - prf_expand_vec(sk, &[t]) -} - -pub fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bHash { - let mut h = Blake2bParams::new() - .hash_length(64) - .personal(PRF_EXPAND_PERSONALIZATION) - .to_state(); - h.update(sk); - for t in ts { - h.update(t); - } - h.finalize() -} diff --git a/zcash_primitives/src/legacy/keys.rs b/zcash_primitives/src/legacy/keys.rs index fead35a81..026ba6bf6 100644 --- a/zcash_primitives/src/legacy/keys.rs +++ b/zcash_primitives/src/legacy/keys.rs @@ -4,8 +4,9 @@ use hdwallet::{ }; use secp256k1::PublicKey; use sha2::{Digest, Sha256}; +use zcash_spec::PrfExpand; -use crate::{consensus, keys::prf_expand_vec, zip32::AccountId}; +use crate::{consensus, zip32::AccountId}; use super::TransparentAddress; @@ -111,11 +112,8 @@ impl AccountPubKey { /// /// [transparent-ovk]: https://zips.z.cash/zip-0316#deriving-internal-keys pub fn ovks_for_shielding(&self) -> (InternalOvk, ExternalOvk) { - let i_ovk = prf_expand_vec( - &self.0.chain_code, - &[&[0xd0], &self.0.public_key.serialize()], - ); - let i_ovk = i_ovk.as_bytes(); + let i_ovk = PrfExpand::TRANSPARENT_ZIP316_OVK + .with(&self.0.chain_code, &self.0.public_key.serialize()); let ovk_external = ExternalOvk(i_ovk[..32].try_into().unwrap()); let ovk_internal = InternalOvk(i_ovk[32..].try_into().unwrap()); diff --git a/zcash_primitives/src/sapling/keys.rs b/zcash_primitives/src/sapling/keys.rs index 90585634f..b2d836084 100644 --- a/zcash_primitives/src/sapling/keys.rs +++ b/zcash_primitives/src/sapling/keys.rs @@ -17,7 +17,6 @@ use super::{ PreparedBaseSubgroup, PreparedScalar, }, }; -use crate::keys::prf_expand; use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use ff::{Field, PrimeField}; @@ -25,6 +24,7 @@ use group::{Curve, Group, GroupEncoding}; use redjubjub::SpendAuth; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zcash_note_encryption::EphemeralKeyBytes; +use zcash_spec::PrfExpand; #[cfg(test)] use rand_core::RngCore; @@ -69,7 +69,7 @@ impl From<&SpendValidatingKey> for jubjub::ExtendedPoint { 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()) + jubjub::Scalar::from_bytes_wide(&PrfExpand::SAPLING_ASK.with(sk)) } /// Constructs a `SpendAuthorizingKey` from a raw scalar. @@ -232,10 +232,10 @@ impl ExpandedSpendingKey { pub fn from_spending_key(sk: &[u8]) -> Self { 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 nsk = jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_NSK.with(sk)); let mut ovk = OutgoingViewingKey([0u8; 32]); ovk.0 - .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); + .copy_from_slice(&PrfExpand::SAPLING_OVK.with(sk)[..32]); ExpandedSpendingKey { ask, nsk, ovk } } diff --git a/zcash_primitives/src/sapling/note.rs b/zcash_primitives/src/sapling/note.rs index 46caeb3b9..3d802b4cb 100644 --- a/zcash_primitives/src/sapling/note.rs +++ b/zcash_primitives/src/sapling/note.rs @@ -1,10 +1,10 @@ use group::{ff::Field, GroupEncoding}; use rand_core::{CryptoRng, RngCore}; +use zcash_spec::PrfExpand; use super::{ keys::EphemeralSecretKey, value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress, }; -use crate::keys::prf_expand; mod commitment; pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment}; @@ -30,7 +30,7 @@ impl Rseed { commitment::NoteCommitTrapdoor(match self { Rseed::BeforeZip212(rcm) => *rcm, Rseed::AfterZip212(rseed) => { - jubjub::Fr::from_bytes_wide(prf_expand(rseed, &[0x04]).as_array()) + jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_RCM.with(rseed)) } }) } @@ -144,7 +144,7 @@ impl Note { match self.rseed { Rseed::BeforeZip212(_) => None, Rseed::AfterZip212(rseed) => Some(EphemeralSecretKey(jubjub::Fr::from_bytes_wide( - prf_expand(&rseed, &[0x05]).as_array(), + &PrfExpand::SAPLING_ESK.with(&rseed), ))), } } diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index d4e84454d..92d71664c 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -358,9 +358,9 @@ impl ShieldedOutput for CompactOutputDescripti /// use ff::Field; /// use rand_core::OsRng; /// use zcash_primitives::{ -/// keys::{OutgoingViewingKey, prf_expand}, /// consensus::{TEST_NETWORK, NetworkUpgrade, Parameters}, /// sapling::{ +/// keys::OutgoingViewingKey, /// note_encryption::{sapling_note_encryption, Zip212Enforcement}, /// util::generate_random_rseed, /// value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, diff --git a/zcash_primitives/src/sapling/zip32.rs b/zcash_primitives/src/sapling/zip32.rs index 66365fa57..950e429fc 100644 --- a/zcash_primitives/src/sapling/zip32.rs +++ b/zcash_primitives/src/sapling/zip32.rs @@ -8,13 +8,13 @@ use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; +use zcash_spec::PrfExpand; use std::io::{self, Read, Write}; use std::ops::AddAssign; use super::{Diversifier, NullifierDerivingKey, PaymentAddress, ViewingKey}; use crate::{ - keys::{prf_expand, prf_expand_vec}, sapling::{ constants::PROOF_GENERATION_KEY_GENERATOR, keys::{ @@ -69,7 +69,7 @@ pub fn sapling_default_address( /// Convenience function for child OVK derivation fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey { let mut ovk = [0u8; 32]; - ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &parent.0]).as_bytes()[..32]); + ovk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_OVK.with(i_l, &parent.0)[..32]); OutgoingViewingKey(ovk) } @@ -91,9 +91,9 @@ pub fn sapling_derive_internal_fvk( h.update(&dk.0); h.finalize() }; - let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); - let r = prf_expand(i.as_bytes(), &[0x18]); - let r = r.as_bytes(); + let i_nsk = + jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes())); + let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes()); // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk.0); let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); @@ -156,7 +156,7 @@ pub struct DiversifierKey([u8; 32]); impl DiversifierKey { pub fn master(sk_m: &[u8]) -> Self { let mut dk_m = [0u8; 32]; - dk_m.copy_from_slice(&prf_expand(sk_m, &[0x10]).as_bytes()[..32]); + dk_m.copy_from_slice(&PrfExpand::SAPLING_ZIP32_MASTER_DK.with(sk_m)[..32]); DiversifierKey(dk_m) } @@ -172,7 +172,7 @@ impl DiversifierKey { fn derive_child(&self, i_l: &[u8]) -> Self { let mut dk = [0u8; 32]; - dk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x16], &self.0]).as_bytes()[..32]); + dk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_DK.with(i_l, &self.0)[..32]); DiversifierKey(dk) } @@ -422,14 +422,16 @@ impl ExtendedSpendingKey { let tmp = { let mut le_i = [0; 4]; LittleEndian::write_u32(&mut le_i, i.index()); - prf_expand_vec( + PrfExpand::SAPLING_ZIP32_CHILD_HARDENED.with( self.chain_code.as_bytes(), - &[&[0x11], &self.expsk.to_bytes(), &self.dk.0, &le_i], + &self.expsk.to_bytes(), + &self.dk.0, + &le_i, ) }; - let i_l = &tmp.as_bytes()[..32]; + let i_l = &tmp[..32]; let mut c_i = [0u8; 32]; - c_i.copy_from_slice(&tmp.as_bytes()[32..]); + c_i.copy_from_slice(&tmp[32..]); ExtendedSpendingKey { depth: self.depth + 1, @@ -437,8 +439,10 @@ impl ExtendedSpendingKey { child_index: KeyIndex::Child(i), chain_code: ChainCode::new(c_i), 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()); + let mut ask = + jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_CHILD_I_ASK.with(i_l)); + let mut nsk = + jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_CHILD_I_NSK.with(i_l)); ask.add_assign(self.expsk.ask.to_scalar()); nsk.add_assign(&self.expsk.nsk); let ovk = derive_child_ovk(&self.expsk.ovk, i_l); @@ -474,9 +478,9 @@ impl ExtendedSpendingKey { h.update(&self.dk.0); h.finalize() }; - let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); - let r = prf_expand(i.as_bytes(), &[0x18]); - let r = r.as_bytes(); + let i_nsk = + jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes())); + let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes()); let nsk_internal = i_nsk + self.expsk.nsk; let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());