Add `sapling::keys::DiversifiedTransmissionKey`

This commit is contained in:
Jack Grigg 2023-01-10 11:30:21 +00:00
parent 06ba399d80
commit ded14adbb3
9 changed files with 184 additions and 122 deletions

View File

@ -241,9 +241,6 @@ pub fn decode_extended_full_viewing_key(
///
/// ```
/// use group::Group;
/// use jubjub::SubgroupPoint;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// encoding::encode_payment_address,
/// };
@ -252,15 +249,12 @@ pub fn decode_extended_full_viewing_key(
/// sapling::{Diversifier, PaymentAddress},
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// SubgroupPoint::random(rng),
/// )
/// let pa = PaymentAddress::from_bytes(&[
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11,
/// 0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42,
/// 0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3,
/// 0xea,
/// ])
/// .unwrap();
///
/// assert_eq!(
@ -291,9 +285,6 @@ pub fn encode_payment_address_p<P: consensus::Parameters>(
///
/// ```
/// use group::Group;
/// use jubjub::SubgroupPoint;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// encoding::decode_payment_address,
/// };
@ -302,15 +293,12 @@ pub fn encode_payment_address_p<P: consensus::Parameters>(
/// sapling::{Diversifier, PaymentAddress},
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
/// 0xbc, 0xe5,
/// ]);
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// SubgroupPoint::random(rng),
/// )
/// let pa = PaymentAddress::from_bytes(&[
/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11,
/// 0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42,
/// 0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3,
/// 0xea,
/// ])
/// .unwrap();
///
/// assert_eq!(
@ -461,14 +449,7 @@ pub fn decode_transparent_address(
#[cfg(test)]
mod tests {
use group::Group;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
constants,
sapling::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
};
use zcash_primitives::{constants, sapling::PaymentAddress, zip32::ExtendedSpendingKey};
use super::{
decode_extended_full_viewing_key, decode_extended_spending_key, decode_payment_address,
@ -559,14 +540,13 @@ mod tests {
#[test]
fn payment_address() {
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let addr =
PaymentAddress::from_parts(Diversifier([0u8; 11]), jubjub::SubgroupPoint::random(rng))
.unwrap();
let addr = PaymentAddress::from_bytes(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x8e, 0x11,
0x9d, 0x72, 0x99, 0x2b, 0x56, 0x0d, 0x26, 0x50, 0xff, 0xe0, 0xbe, 0x7f, 0x35, 0x42,
0xfd, 0x97, 0x00, 0x3c, 0xb7, 0xcc, 0x3a, 0xbf, 0xf8, 0x1a, 0x7f, 0x90, 0x37, 0xf3,
0xea,
])
.unwrap();
let encoded_main =
"zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z";

View File

@ -8,6 +8,7 @@ and this library adheres to Rust's notion of
## [Unreleased]
### Added
- `zcash_primitives::sapling`:
- `keys::DiversifiedTransmissionKey`
- `keys::{EphemeralSecretKey, EphemeralPublicKey, SharedSecret}`
- `note`, a module containing types related to Sapling notes. The existing
`Note` and `Rseed` types are re-exported here, and new types are added.
@ -27,6 +28,11 @@ and this library adheres to Rust's notion of
- `zcash_primitives::sapling`:
- `PaymentAddress::from_parts` now rejects invalid diversifiers.
- `PaymentAddress::create_note` is now infallible.
- `DiversifiedTransmissionKey` is now used instead of `jubjub::SubgroupPoint`
in the following places:
- `PaymentAddress::from_parts`
- `PaymentAddress::pk_d`
- `note_encryption::SaplingDomain::DiversifiedTransmissionKey`
- Note commitments now use
`zcash_primitives::sapling::note::ExtractedNoteCommitment` instead of
`bls12_381::Scalar` in the following places:
@ -65,6 +71,7 @@ and this library adheres to Rust's notion of
- `NoteValue` (use `zcash_primitives::sapling::value::NoteValue` instead).
- `ValueCommitment` (use `zcash_primitives::sapling::value::ValueCommitment`
or `zcash_proofs::circuit::sapling::ValueCommitmentPreimage` instead).
- `note_encryption::sapling_ka_agree`
- `testing::{arb_note_value, arb_positive_note_value}` (use the methods in
`zcash_primitives::sapling::value::testing` instead).
- `zcash_primitives::transaction::components`:

View File

@ -14,7 +14,7 @@ use zcash_primitives::{
},
prover::mock::MockTxProver,
value::NoteValue,
Diversifier, PaymentAddress, SaplingIvk,
Diversifier, SaplingIvk,
},
transaction::components::sapling::{
builder::SaplingBuilder, CompactOutputDescription, GrothProofBytes, OutputDescription,
@ -34,8 +34,7 @@ fn bench_note_decryption(c: &mut Criterion) {
// Construct a Sapling output.
let output: OutputDescription<GrothProofBytes> = {
let diversifier = Diversifier([0; 11]);
let pk_d = diversifier.g_d().unwrap() * valid_ivk.0;
let pa = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
let pa = valid_ivk.to_payment_address(diversifier).unwrap();
let mut builder = SaplingBuilder::new(TEST_NETWORK, height);
builder

View File

@ -1,7 +1,5 @@
use group::{Group, GroupEncoding};
use super::{
keys::Diversifier,
keys::{DiversifiedTransmissionKey, Diversifier},
note::{Note, Rseed},
value::NoteValue,
};
@ -15,7 +13,7 @@ use super::{
/// and not the identity).
#[derive(Clone, Copy, Debug)]
pub struct PaymentAddress {
pk_d: jubjub::SubgroupPoint,
pk_d: DiversifiedTransmissionKey,
diversifier: Diversifier,
}
@ -34,30 +32,17 @@ impl PaymentAddress {
/// Note that we cannot verify in this constructor that `pk_d` is derived from
/// `diversifier`, so addresses for which these values have no known relationship
/// (and therefore no-one can receive funds at them) can still be constructed.
pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option<Self> {
pub fn from_parts(diversifier: Diversifier, pk_d: DiversifiedTransmissionKey) -> Option<Self> {
// Check that the diversifier is valid
diversifier.g_d()?;
if pk_d.is_identity().into() {
if pk_d.is_identity() {
None
} else {
Some(PaymentAddress { pk_d, diversifier })
}
}
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Only for test code, as this explicitly bypasses the invariant. [`Self::g_d`] will
/// panic on a `PaymentAddress` that has been constructed by passing an invalid
/// diversifier to this method.
#[cfg(test)]
pub(crate) fn from_parts_unchecked(
diversifier: Diversifier,
pk_d: jubjub::SubgroupPoint,
) -> Self {
PaymentAddress { pk_d, diversifier }
}
/// Parses a PaymentAddress from bytes.
pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
let diversifier = {
@ -66,7 +51,7 @@ impl PaymentAddress {
Diversifier(tmp)
};
let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
let pk_d = DiversifiedTransmissionKey::from_bytes(bytes[11..43].try_into().unwrap());
if pk_d.is_some().into() {
// The remaining invariants are checked here.
PaymentAddress::from_parts(diversifier, pk_d.unwrap())
@ -89,7 +74,7 @@ impl PaymentAddress {
}
/// Returns `pk_d` for this `PaymentAddress`.
pub fn pk_d(&self) -> &jubjub::SubgroupPoint {
pub fn pk_d(&self) -> &DiversifiedTransmissionKey {
&self.pk_d
}

View File

@ -13,7 +13,7 @@ use super::{
},
spec::{
crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared,
ka_sapling_derive_public,
ka_sapling_derive_public, ka_sapling_derive_public_subgroup_prepared, PreparedBaseSubgroup,
},
};
use crate::{
@ -24,7 +24,7 @@ use crate::{
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::PrimeField;
use group::{Curve, Group, GroupEncoding};
use subtle::{ConstantTimeEq, CtOption};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_note_encryption::EphemeralKeyBytes;
/// Errors that can occur in the decoding of Sapling spending keys.
@ -241,11 +241,9 @@ pub struct SaplingIvk(pub jubjub::Fr);
impl SaplingIvk {
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
diversifier.g_d().and_then(|g_d| {
let pk_d = g_d * self.0;
PaymentAddress::from_parts(diversifier, pk_d)
})
let prepared_ivk = PreparedIncomingViewingKey::new(self);
DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
.and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
}
pub fn to_repr(&self) -> [u8; 32] {
@ -262,6 +260,62 @@ impl Diversifier {
}
}
/// The diversified transmission key for a given payment address.
///
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
///
/// Note that this type is allowed to be the identity in the protocol, but we reject this
/// in [`PaymentAddress::from_parts`].
///
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
impl DiversifiedTransmissionKey {
/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
///
/// Returns `None` if `d` is an invalid diversifier.
///
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
d.g_d()
.map(PreparedBaseSubgroup::new)
.map(|g_d| ka_sapling_derive_public_subgroup_prepared(ivk.inner(), &g_d))
.map(DiversifiedTransmissionKey)
}
/// $abst_J(bytes)$
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
}
/// $repr_J(self)$
pub(crate) fn to_bytes(self) -> [u8; 32] {
self.0.to_bytes()
}
/// Returns true if this is the identity.
pub(crate) fn is_identity(&self) -> bool {
self.0.is_identity().into()
}
/// Exposes the inner Jubjub point.
///
/// This API is exposed for `zcash_proof` usage, and will be removed when this type is
/// refactored into the `sapling-crypto` crate.
pub fn inner(&self) -> jubjub::SubgroupPoint {
self.0
}
}
impl ConditionallySelectable for DiversifiedTransmissionKey {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
&a.0, &b.0, choice,
))
}
}
/// An ephemeral secret key used to encrypt an output note on-chain.
///
/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
@ -290,8 +344,8 @@ impl EphemeralSecretKey {
EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
}
pub(crate) fn agree(&self, pk_d: &jubjub::ExtendedPoint) -> SharedSecret {
SharedSecret(ka_sapling_agree(&self.0, pk_d))
pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
}
}

View File

@ -5,7 +5,6 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, WriteBytesExt};
use ff::PrimeField;
use group::GroupEncoding;
use memuse::DynamicUsage;
use rand_core::RngCore;
@ -20,8 +19,11 @@ use crate::{
consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD},
memo::MemoBytes,
sapling::{
keys::{EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, SharedSecret},
spec::{PreparedBase, PreparedBaseSubgroup, PreparedScalar},
keys::{
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
SharedSecret,
},
spec::{PreparedBase, PreparedScalar},
value::ValueCommitment,
Diversifier, Note, PaymentAddress, Rseed, SaplingIvk,
},
@ -73,13 +75,6 @@ impl PreparedEphemeralPublicKey {
}
}
/// Sapling key agreement for note encryption.
///
/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint {
EphemeralSecretKey(*esk).agree(pk_d).into_inner()
}
/// Sapling PRF^ock.
///
/// Implemented per section 5.4.2 of the Zcash Protocol Specification.
@ -111,7 +106,7 @@ fn sapling_parse_note_plaintext_without_memo<F, P: consensus::Parameters>(
get_validated_pk_d: F,
) -> Option<(Note, PaymentAddress)>
where
F: FnOnce(&Diversifier) -> Option<jubjub::SubgroupPoint>,
F: FnOnce(&Diversifier) -> Option<DiversifiedTransmissionKey>,
{
assert!(plaintext.len() >= COMPACT_NOTE_SIZE);
@ -176,7 +171,7 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
type SymmetricKey = Blake2bHash;
type Note = Note;
type Recipient = PaymentAddress;
type DiversifiedTransmissionKey = jubjub::SubgroupPoint;
type DiversifiedTransmissionKey = DiversifiedTransmissionKey;
type IncomingViewingKey = PreparedIncomingViewingKey;
type OutgoingViewingKey = OutgoingViewingKey;
type ValueCommitment = ValueCommitment;
@ -209,7 +204,7 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
esk: &Self::EphemeralSecretKey,
pk_d: &Self::DiversifiedTransmissionKey,
) -> Self::SharedSecret {
EphemeralSecretKey(*esk).agree(pk_d.into()).into_inner()
EphemeralSecretKey(*esk).agree(pk_d).into_inner()
}
fn ka_agree_dec(
@ -296,7 +291,7 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
plaintext: &[u8],
) -> Option<(Self::Note, Self::Recipient)> {
sapling_parse_note_plaintext_without_memo(self, plaintext, |diversifier| {
Some(&PreparedBaseSubgroup::new(diversifier.g_d()?) * &ivk.0)
DiversifiedTransmissionKey::derive(ivk, diversifier)
})
}
@ -326,7 +321,7 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
}
fn extract_pk_d(op: &OutPlaintextBytes) -> Option<Self::DiversifiedTransmissionKey> {
jubjub::SubgroupPoint::from_bytes(
DiversifiedTransmissionKey::from_bytes(
op.0[0..32].try_into().expect("slice is the correct length"),
)
.into()
@ -404,15 +399,15 @@ impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
/// note_encryption::sapling_note_encryption,
/// util::generate_random_rseed,
/// value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
/// Diversifier, PaymentAddress, Rseed,
/// Diversifier, PaymentAddress, Rseed, SaplingIvk,
/// },
/// };
///
/// let mut rng = OsRng;
///
/// let ivk = SaplingIvk(jubjub::Scalar::random(&mut rng));
/// let diversifier = Diversifier([0; 11]);
/// let pk_d = diversifier.g_d().unwrap();
/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
/// let to = ivk.to_payment_address(diversifier).unwrap();
/// let ovk = Some(OutgoingViewingKey([0; 32]));
///
/// let value = NoteValue::from_raw(1000);
@ -549,7 +544,7 @@ mod tests {
};
use ff::{Field, PrimeField};
use group::Group;
use group::{cofactor::CofactorGroup, GroupEncoding};
use group::GroupEncoding;
use rand_core::OsRng;
use rand_core::{CryptoRng, RngCore};
@ -573,7 +568,7 @@ mod tests {
keys::OutgoingViewingKey,
memo::MemoBytes,
sapling::{
keys::{EphemeralPublicKey, EphemeralSecretKey},
keys::{DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey},
note::ExtractedNoteCommitment,
note_encryption::PreparedIncomingViewingKey,
util::generate_random_rseed,
@ -632,8 +627,7 @@ mod tests {
OutputDescription<sapling::GrothProofBytes>,
) {
let diversifier = Diversifier([0; 11]);
let pk_d = diversifier.g_d().unwrap() * ivk.0;
let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d);
let pa = ivk.to_payment_address(diversifier).unwrap();
// Construct the value commitment for the proof instance
let value = NoteValue::from_raw(100);
@ -669,6 +663,40 @@ mod tests {
(ovk, ock, output)
}
fn reencrypt_out_ciphertext(
ovk: &OutgoingViewingKey,
cv: &ValueCommitment,
cmu: &ExtractedNoteCommitment,
ephemeral_key: &EphemeralKeyBytes,
out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE],
modify_plaintext: impl Fn(&mut [u8; OUT_PLAINTEXT_SIZE]),
) -> [u8; OUT_CIPHERTEXT_SIZE] {
let ock = prf_ock(ovk, cv, &cmu.to_bytes(), ephemeral_key);
let mut op = [0; OUT_PLAINTEXT_SIZE];
op.copy_from_slice(&out_ciphertext[..OUT_PLAINTEXT_SIZE]);
ChaCha20Poly1305::new(ock.as_ref().into())
.decrypt_in_place_detached(
[0u8; 12][..].into(),
&[],
&mut op,
out_ciphertext[OUT_PLAINTEXT_SIZE..].into(),
)
.unwrap();
modify_plaintext(&mut op);
let tag = ChaCha20Poly1305::new(ock.as_ref().into())
.encrypt_in_place_detached([0u8; 12][..].into(), &[], &mut op)
.unwrap();
let mut out_ciphertext = [0u8; OUT_CIPHERTEXT_SIZE];
out_ciphertext[..OUT_PLAINTEXT_SIZE].copy_from_slice(&op);
out_ciphertext[OUT_PLAINTEXT_SIZE..].copy_from_slice(&tag);
out_ciphertext
}
fn reencrypt_enc_ciphertext(
ovk: &OutgoingViewingKey,
cv: &ValueCommitment,
@ -692,11 +720,11 @@ mod tests {
)
.unwrap();
let pk_d = jubjub::SubgroupPoint::from_bytes(&op[0..32].try_into().unwrap()).unwrap();
let pk_d = DiversifiedTransmissionKey::from_bytes(&op[0..32].try_into().unwrap()).unwrap();
let esk = jubjub::Fr::from_repr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()).unwrap();
let shared_secret = EphemeralSecretKey(esk).agree(&pk_d.into());
let shared_secret = EphemeralSecretKey(esk).agree(&pk_d);
let key = shared_secret.kdf_sapling(ephemeral_key);
let mut plaintext = [0; NOTE_PLAINTEXT_SIZE];
@ -1362,9 +1390,16 @@ mod tests {
];
for &height in heights.iter() {
let ivk = SaplingIvk(jubjub::Fr::zero());
let (ovk, ock, output) = random_enc_ciphertext_with(height, &ivk, &mut rng);
let (ovk, ock, _, mut output) = random_enc_ciphertext(height, &mut rng);
*output.out_ciphertext_mut() = reencrypt_out_ciphertext(
&ovk,
output.cv(),
output.cmu(),
output.ephemeral_key(),
output.out_ciphertext(),
|pt| pt[0..32].copy_from_slice(&jubjub::ExtendedPoint::random(rng).to_bytes()),
);
assert_eq!(
try_sapling_output_recovery(&TEST_NETWORK, height, &ovk, &output,),
None
@ -1392,9 +1427,9 @@ mod tests {
}};
}
macro_rules! read_point {
macro_rules! read_pk_d {
($field:expr) => {
jubjub::ExtendedPoint::from_bytes(&$field).unwrap()
DiversifiedTransmissionKey::from_bytes(&$field).unwrap()
};
}
@ -1412,7 +1447,7 @@ mod tests {
//
let ivk = PreparedIncomingViewingKey::new(&SaplingIvk(read_jubjub_scalar!(tv.ivk)));
let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap();
let pk_d = read_pk_d!(tv.default_pk_d);
let rcm = read_jubjub_scalar!(tv.rcm);
let cv = read_cv!(tv.cv);
let cmu = read_cmu!(tv.cmu);
@ -1423,7 +1458,7 @@ mod tests {
// Test the individual components
//
let shared_secret = EphemeralSecretKey(esk).agree(&pk_d.into());
let shared_secret = EphemeralSecretKey(esk).agree(&pk_d);
assert_eq!(shared_secret.to_bytes(), tv.shared_secret);
let k_enc = shared_secret.kdf_sapling(&ephemeral_key);

View File

@ -98,6 +98,19 @@ pub(crate) fn ka_sapling_derive_public_prepared(
b * sk
}
/// This is defined implicitly by [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]
/// which uses $KA^\mathsf{Sapling}.\mathsf{DerivePublic}$ to produce a diversified
/// transmission key with type $KA^\mathsf{Sapling}.\mathsf{PublicPrimeSubgroup}$.
///
/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
pub(crate) fn ka_sapling_derive_public_subgroup_prepared(
sk: &PreparedScalar,
b: &PreparedBaseSubgroup,
) -> jubjub::SubgroupPoint {
// [sk] b
b * sk
}
/// Defined in [Zcash Protocol Spec § 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
///
/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement

View File

@ -13,7 +13,7 @@ use crate::{
memo::MemoBytes,
merkle_tree::MerklePath,
sapling::{
keys::EphemeralSecretKey,
keys::{EphemeralSecretKey, SaplingIvk},
note_encryption::sapling_note_encryption,
prover::TxProver,
redjubjub::{PrivateKey, Signature},
@ -454,26 +454,15 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
} else {
// This is a dummy output
let dummy_note = {
let (diversifier, g_d) = {
let mut diversifier;
let g_d;
let payment_address = {
let mut diversifier = Diversifier([0; 11]);
loop {
let mut d = [0; 11];
rng.fill_bytes(&mut d);
diversifier = Diversifier(d);
if let Some(val) = diversifier.g_d() {
g_d = val;
break;
rng.fill_bytes(&mut diversifier.0);
let dummy_ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
if let Some(addr) = dummy_ivk.to_payment_address(diversifier) {
break addr;
}
}
(diversifier, g_d)
};
let payment_address = loop {
let dummy_ivk = jubjub::Fr::random(&mut rng);
let pk_d = g_d * dummy_ivk;
if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d) {
break addr;
}
};
let rseed =

View File

@ -476,7 +476,7 @@ impl Circuit<bls12_381::Scalar> for Output {
let pk_d = self
.payment_address
.as_ref()
.map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine());
.map(|e| jubjub::ExtendedPoint::from(e.pk_d().inner()).to_affine());
// Witness the v-coordinate, encoded as little
// endian bits (to match the representation)