Add `sapling::keys::DiversifiedTransmissionKey`
This commit is contained in:
parent
06ba399d80
commit
ded14adbb3
|
@ -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";
|
||||
|
|
|
@ -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`:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue