Merge pull request #1061 from zcash/1044-sapling-prf-expand

zcash_primitives: Introduce type-safe `PRF^expand`
This commit is contained in:
str4d 2023-12-07 18:03:37 +00:00 committed by GitHub
commit ecd5402266
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 47 additions and 51 deletions

10
Cargo.lock generated
View File

@ -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"

View File

@ -39,6 +39,7 @@ ff = "0.13"
group = "0.13"
incrementalmerkletree = "0.5"
shardtree = "0.2"
zcash_spec = "0.1"
# Payment protocols
# - Sapling

View File

@ -215,6 +215,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}`

View File

@ -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"] }

View File

@ -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()
}

View File

@ -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());

View File

@ -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 }
}

View File

@ -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),
))),
}
}

View File

@ -358,9 +358,9 @@ impl ShieldedOutput<SaplingDomain, COMPACT_NOTE_SIZE> 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},

View File

@ -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());