sapling_crypto/
keys.rs

1//! Sapling key components.
2//!
3//! Implements [section 4.2.2] of the Zcash Protocol Specification.
4//!
5//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
6
7use alloc::vec::Vec;
8use core::fmt;
9use core2::io::{self, Read, Write};
10
11use super::{
12    address::PaymentAddress,
13    constants::{self, PROOF_GENERATION_KEY_GENERATOR},
14    note_encryption::KDF_SAPLING_PERSONALIZATION,
15    spec::{
16        crh_ivk, diversify_hash, ka_sapling_agree, ka_sapling_agree_prepared,
17        ka_sapling_derive_public, ka_sapling_derive_public_subgroup_prepared, PreparedBase,
18        PreparedBaseSubgroup, PreparedScalar,
19    },
20};
21
22use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
23use ff::{Field, PrimeField};
24use group::{Curve, Group, GroupEncoding};
25use redjubjub::SpendAuth;
26use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
27use zcash_note_encryption::EphemeralKeyBytes;
28use zcash_spec::PrfExpand;
29
30#[cfg(all(feature = "circuit", test))]
31use rand_core::RngCore;
32
33/// Errors that can occur in the decoding of Sapling spending keys.
34#[derive(Debug)]
35#[non_exhaustive]
36pub enum DecodingError {
37    /// The length of the byte slice provided for decoding was incorrect.
38    LengthInvalid { expected: usize, actual: usize },
39    /// Could not decode the `ask` bytes to a jubjub field element.
40    InvalidAsk,
41    /// Could not decode the `nsk` bytes to a jubjub field element.
42    InvalidNsk,
43    /// An extended spending key had an unsupported child index: either a non-hardened
44    /// index, or a non-zero index at depth 0.
45    UnsupportedChildIndex,
46}
47
48impl fmt::Display for DecodingError {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        match self {
51            DecodingError::LengthInvalid { expected, actual } => {
52                write!(f, "invalid slice length (expected {expected}, got {actual}")
53            }
54            DecodingError::InvalidAsk => write!(f, "invalid `ask`"),
55            DecodingError::InvalidNsk => write!(f, "invalid `nsk`"),
56            DecodingError::UnsupportedChildIndex => write!(
57                f,
58                "unsupported child index (either non-hardened, or non-zero at depth 0)"
59            ),
60        }
61    }
62}
63
64#[cfg(feature = "std")]
65impl std::error::Error for DecodingError {}
66
67/// A spend authorizing key, used to create spend authorization signatures.
68///
69/// $\mathsf{ask}$ as defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
70///
71/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
72#[derive(Clone, Debug)]
73pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
74
75impl PartialEq for SpendAuthorizingKey {
76    fn eq(&self, other: &Self) -> bool {
77        <[u8; 32]>::from(self.0)
78            .ct_eq(&<[u8; 32]>::from(other.0))
79            .into()
80    }
81}
82
83impl Eq for SpendAuthorizingKey {}
84
85impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
86    fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
87        jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
88    }
89}
90
91impl SpendAuthorizingKey {
92    /// Derives ask from sk. Internal use only, does not enforce all constraints.
93    fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
94        jubjub::Scalar::from_bytes_wide(&PrfExpand::SAPLING_ASK.with(sk))
95    }
96
97    /// Constructs a `SpendAuthorizingKey` from a raw scalar.
98    pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
99        if ask.is_zero().into() {
100            None
101        } else {
102            Some(SpendAuthorizingKey(ask.to_bytes().try_into().unwrap()))
103        }
104    }
105
106    /// Derives a `SpendAuthorizingKey` from a spending key.
107    fn from_spending_key(sk: &[u8]) -> Option<Self> {
108        Self::from_scalar(Self::derive_inner(sk))
109    }
110
111    /// Parses a `SpendAuthorizingKey` from its encoded form.
112    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
113        <[u8; 32]>::try_from(bytes)
114            .ok()
115            .and_then(|b| {
116                // RedJubjub.Private permits the full set of Jubjub scalars including
117                // zero. However, a SpendAuthorizingKey is further restricted within the
118                // Sapling key tree to be a non-zero scalar.
119                jubjub::Scalar::from_repr(b)
120                    .and_then(|s| {
121                        CtOption::new(
122                            redjubjub::SigningKey::try_from(b)
123                                .expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
124                            !s.is_zero(),
125                        )
126                    })
127                    .into()
128            })
129            .map(SpendAuthorizingKey)
130    }
131
132    /// Converts this spend authorizing key to its serialized form.
133    pub fn to_bytes(&self) -> [u8; 32] {
134        <[u8; 32]>::from(self.0)
135    }
136
137    /// Converts this spend authorizing key to a raw scalar.
138    ///
139    /// Only used for ZIP 32 child derivation.
140    pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
141        jubjub::Scalar::from_repr(self.0.into()).unwrap()
142    }
143
144    /// Randomizes this spend authorizing key with the given `randomizer`.
145    ///
146    /// The resulting key can be used to actually sign a spend.
147    pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
148        self.0.randomize(randomizer)
149    }
150}
151
152/// A key used to validate spend authorization signatures.
153///
154/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
155///
156/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
157#[derive(Clone, Debug)]
158pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
159
160impl From<&SpendAuthorizingKey> for SpendValidatingKey {
161    fn from(ask: &SpendAuthorizingKey) -> Self {
162        SpendValidatingKey((&ask.0).into())
163    }
164}
165
166impl PartialEq for SpendValidatingKey {
167    fn eq(&self, other: &Self) -> bool {
168        <[u8; 32]>::from(self.0)
169            .ct_eq(&<[u8; 32]>::from(other.0))
170            .into()
171    }
172}
173
174impl Eq for SpendValidatingKey {}
175
176impl SpendValidatingKey {
177    /// For circuit tests only.
178    #[cfg(all(feature = "circuit", test))]
179    pub(crate) fn fake_random<R: RngCore>(mut rng: R) -> Self {
180        loop {
181            if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
182                break k;
183            }
184        }
185    }
186
187    /// Only exposed for `zcashd` unit tests.
188    #[cfg(feature = "temporary-zcashd")]
189    pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
190        Self::from_bytes(bytes)
191    }
192
193    /// Parses a `SpendValidatingKey` from its encoded form.
194    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
195        <[u8; 32]>::try_from(bytes)
196            .ok()
197            .and_then(|b| {
198                // RedJubjub.Public permits the full set of Jubjub points including the
199                // identity and cofactors; this is the type used for `rk` in Spend
200                // descriptions. However, a SpendValidatingKey is further restricted
201                // within the Sapling key tree to be a non-identity element of the
202                // prime-order subgroup.
203                jubjub::SubgroupPoint::from_bytes(&b)
204                    .and_then(|p| {
205                        CtOption::new(
206                            redjubjub::VerificationKey::try_from(b)
207                                .expect("RedJubjub permits the set of valid SpendValidatingKeys"),
208                            !p.is_identity(),
209                        )
210                    })
211                    .into()
212            })
213            .map(SpendValidatingKey)
214    }
215
216    /// Converts this spend validating key to its serialized form,
217    /// `LEBS2OSP_256(repr_J(ak))`.
218    pub fn to_bytes(&self) -> [u8; 32] {
219        <[u8; 32]>::from(self.0)
220    }
221
222    /// Randomizes this spend validating key with the given `randomizer`.
223    pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
224        self.0.randomize(randomizer)
225    }
226}
227
228/// An outgoing viewing key
229#[derive(Clone, Copy, Debug, PartialEq, Eq)]
230pub struct OutgoingViewingKey(pub [u8; 32]);
231
232/// A Sapling expanded spending key
233#[derive(Clone)]
234pub struct ExpandedSpendingKey {
235    pub ask: SpendAuthorizingKey,
236    pub nsk: jubjub::Fr,
237    pub ovk: OutgoingViewingKey,
238}
239
240impl fmt::Debug for ExpandedSpendingKey {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        f.debug_struct("ExpandedSpendingKey")
243            .finish_non_exhaustive()
244    }
245}
246
247impl ExpandedSpendingKey {
248    /// Expands a spending key into its components.
249    ///
250    /// # Panics
251    ///
252    /// Panics if this spending key expands to `ask = 0`. This has a negligible
253    /// probability of occurring.
254    pub fn from_spending_key(sk: &[u8]) -> Self {
255        let ask =
256            SpendAuthorizingKey::from_spending_key(sk).expect("negligible chance of ask == 0");
257        let nsk = jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_NSK.with(sk));
258        let mut ovk = OutgoingViewingKey([0u8; 32]);
259        ovk.0
260            .copy_from_slice(&PrfExpand::SAPLING_OVK.with(sk)[..32]);
261        ExpandedSpendingKey { ask, nsk, ovk }
262    }
263
264    pub fn proof_generation_key(&self) -> ProofGenerationKey {
265        ProofGenerationKey {
266            ak: (&self.ask).into(),
267            nsk: self.nsk,
268        }
269    }
270
271    /// Decodes the expanded spending key from its serialized representation
272    /// as part of the encoding of the extended spending key as defined in
273    /// [ZIP 32](https://zips.z.cash/zip-0032)
274    pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
275        if b.len() != 96 {
276            return Err(DecodingError::LengthInvalid {
277                expected: 96,
278                actual: b.len(),
279            });
280        }
281
282        let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
283        let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
284            .ok_or(DecodingError::InvalidNsk)?;
285        let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
286
287        Ok(ExpandedSpendingKey { ask, nsk, ovk })
288    }
289
290    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
291        let mut repr = [0u8; 96];
292        reader.read_exact(repr.as_mut())?;
293        Self::from_bytes(&repr).map_err(|e| match e {
294            DecodingError::InvalidAsk => {
295                io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
296            }
297            DecodingError::InvalidNsk => {
298                io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
299            }
300            DecodingError::LengthInvalid { .. } | DecodingError::UnsupportedChildIndex => {
301                unreachable!()
302            }
303        })
304    }
305
306    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
307        writer.write_all(&self.to_bytes())
308    }
309
310    /// Encodes the expanded spending key to its serialized representation
311    /// as part of the encoding of the extended spending key as defined in
312    /// [ZIP 32](https://zips.z.cash/zip-0032)
313    pub fn to_bytes(&self) -> [u8; 96] {
314        let mut result = [0u8; 96];
315        result[0..32].copy_from_slice(&self.ask.to_bytes());
316        result[32..64].copy_from_slice(&self.nsk.to_repr());
317        result[64..96].copy_from_slice(&self.ovk.0);
318        result
319    }
320}
321
322#[derive(Clone)]
323pub struct ProofGenerationKey {
324    pub ak: SpendValidatingKey,
325    pub nsk: jubjub::Fr,
326}
327
328impl fmt::Debug for ProofGenerationKey {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        f.debug_struct("ProofGenerationKey")
331            .field("ak", &self.ak)
332            .finish_non_exhaustive()
333    }
334}
335
336impl ProofGenerationKey {
337    pub fn to_viewing_key(&self) -> ViewingKey {
338        ViewingKey {
339            ak: self.ak.clone(),
340            nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
341        }
342    }
343}
344
345/// A key used to derive the nullifier for a Sapling note.
346#[derive(Debug, Copy, Clone, PartialEq, Eq)]
347pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
348
349#[derive(Debug, Clone)]
350pub struct ViewingKey {
351    pub ak: SpendValidatingKey,
352    pub nk: NullifierDerivingKey,
353}
354
355impl ViewingKey {
356    pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
357        self.ak.randomize(&ar)
358    }
359
360    pub fn ivk(&self) -> SaplingIvk {
361        SaplingIvk(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
362    }
363
364    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
365        self.ivk().to_payment_address(diversifier)
366    }
367}
368
369/// A Sapling key that provides the capability to view incoming and outgoing transactions.
370#[derive(Debug)]
371pub struct FullViewingKey {
372    pub vk: ViewingKey,
373    pub ovk: OutgoingViewingKey,
374}
375
376impl Clone for FullViewingKey {
377    fn clone(&self) -> Self {
378        FullViewingKey {
379            vk: ViewingKey {
380                ak: self.vk.ak.clone(),
381                nk: self.vk.nk,
382            },
383            ovk: self.ovk,
384        }
385    }
386}
387
388impl FullViewingKey {
389    pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
390        FullViewingKey {
391            vk: ViewingKey {
392                ak: (&expsk.ask).into(),
393                nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
394            },
395            ovk: expsk.ovk,
396        }
397    }
398
399    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
400        let ak = {
401            let mut buf = [0u8; 32];
402            reader.read_exact(&mut buf)?;
403            SpendValidatingKey::from_bytes(&buf)
404        };
405        let nk = {
406            let mut buf = [0u8; 32];
407            reader.read_exact(&mut buf)?;
408            jubjub::SubgroupPoint::from_bytes(&buf)
409        };
410        if ak.is_none() {
411            return Err(io::Error::new(
412                io::ErrorKind::InvalidInput,
413                "ak not of prime order",
414            ));
415        }
416        if nk.is_none().into() {
417            return Err(io::Error::new(
418                io::ErrorKind::InvalidInput,
419                "nk not in prime-order subgroup",
420            ));
421        }
422        let ak = ak.unwrap();
423        let nk = NullifierDerivingKey(nk.unwrap());
424
425        let mut ovk = [0u8; 32];
426        reader.read_exact(&mut ovk)?;
427
428        Ok(FullViewingKey {
429            vk: ViewingKey { ak, nk },
430            ovk: OutgoingViewingKey(ovk),
431        })
432    }
433
434    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
435        writer.write_all(&self.vk.ak.to_bytes())?;
436        writer.write_all(&self.vk.nk.0.to_bytes())?;
437        writer.write_all(&self.ovk.0)?;
438
439        Ok(())
440    }
441
442    pub fn to_bytes(&self) -> [u8; 96] {
443        let mut result = [0u8; 96];
444        self.write(&mut result[..])
445            .expect("should be able to serialize a FullViewingKey");
446        result
447    }
448}
449
450#[derive(Debug, Clone)]
451pub struct SaplingIvk(pub jubjub::Fr);
452
453impl SaplingIvk {
454    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
455        let prepared_ivk = PreparedIncomingViewingKey::new(self);
456        DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
457            .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
458    }
459
460    pub fn to_repr(&self) -> [u8; 32] {
461        self.0.to_repr()
462    }
463}
464
465/// A Sapling incoming viewing key that has been precomputed for trial decryption.
466#[derive(Clone, Debug)]
467pub struct PreparedIncomingViewingKey(PreparedScalar);
468
469#[cfg(feature = "std")]
470impl memuse::DynamicUsage for PreparedIncomingViewingKey {
471    fn dynamic_usage(&self) -> usize {
472        self.0.dynamic_usage()
473    }
474
475    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
476        self.0.dynamic_usage_bounds()
477    }
478}
479
480impl PreparedIncomingViewingKey {
481    /// Performs the necessary precomputations to use a `SaplingIvk` for note decryption.
482    pub fn new(ivk: &SaplingIvk) -> Self {
483        Self(PreparedScalar::new(&ivk.0))
484    }
485}
486
487#[derive(Copy, Clone, Debug, PartialEq, Eq)]
488pub struct Diversifier(pub [u8; 11]);
489
490impl Diversifier {
491    pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
492        diversify_hash(&self.0)
493    }
494}
495
496/// The diversified transmission key for a given payment address.
497///
498/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
499///
500/// Note that this type is allowed to be the identity in the protocol, but we reject this
501/// in [`PaymentAddress::from_parts`].
502///
503/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
504#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
505pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
506
507impl DiversifiedTransmissionKey {
508    /// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
509    ///
510    /// Returns `None` if `d` is an invalid diversifier.
511    ///
512    /// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
513    pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
514        d.g_d()
515            .map(PreparedBaseSubgroup::new)
516            .map(|g_d| ka_sapling_derive_public_subgroup_prepared(&ivk.0, &g_d))
517            .map(DiversifiedTransmissionKey)
518    }
519
520    /// $abst_J(bytes)$
521    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
522        jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
523    }
524
525    /// $repr_J(self)$
526    pub(crate) fn to_bytes(self) -> [u8; 32] {
527        self.0.to_bytes()
528    }
529
530    /// Returns true if this is the identity.
531    pub(crate) fn is_identity(&self) -> bool {
532        self.0.is_identity().into()
533    }
534
535    /// Exposes the inner Jubjub point.
536    ///
537    /// This API is exposed for `zcash_proof` usage, and will be removed when this type is
538    /// refactored into the `sapling-crypto` crate.
539    pub fn inner(&self) -> jubjub::SubgroupPoint {
540        self.0
541    }
542}
543
544impl ConditionallySelectable for DiversifiedTransmissionKey {
545    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
546        DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
547            &a.0, &b.0, choice,
548        ))
549    }
550}
551
552/// An ephemeral secret key used to encrypt an output note on-chain.
553///
554/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
555/// practice, `esk` is derived deterministically from the note that it is encrypting.
556///
557/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Private} := \mathbb{F}_{r_J}$
558///
559/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
560///
561/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
562#[derive(Debug)]
563pub struct EphemeralSecretKey(pub(crate) jubjub::Scalar);
564
565impl ConstantTimeEq for EphemeralSecretKey {
566    fn ct_eq(&self, other: &Self) -> subtle::Choice {
567        self.0.ct_eq(&other.0)
568    }
569}
570
571impl EphemeralSecretKey {
572    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
573        jubjub::Scalar::from_bytes(bytes).map(EphemeralSecretKey)
574    }
575
576    pub(crate) fn derive_public(&self, g_d: jubjub::ExtendedPoint) -> EphemeralPublicKey {
577        EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
578    }
579
580    pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
581        SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
582    }
583}
584
585/// An ephemeral public key used to encrypt an output note on-chain.
586///
587/// `epk` is "ephemeral" in the sense that each public key is only used once. In practice,
588/// `epk` is derived deterministically from the note that it is encrypting.
589///
590/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Public} := \mathbb{J}$
591///
592/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
593///
594/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
595#[derive(Debug)]
596pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
597
598impl EphemeralPublicKey {
599    pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
600        EphemeralPublicKey(epk.into())
601    }
602
603    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
604        jubjub::ExtendedPoint::from_bytes(bytes).map(EphemeralPublicKey)
605    }
606
607    pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
608        EphemeralKeyBytes(self.0.to_bytes())
609    }
610}
611
612/// A Sapling ephemeral public key that has been precomputed for trial decryption.
613#[derive(Clone, Debug)]
614pub struct PreparedEphemeralPublicKey(PreparedBase);
615
616impl PreparedEphemeralPublicKey {
617    pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
618        PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
619    }
620
621    pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
622        SharedSecret(ka_sapling_agree_prepared(&ivk.0, &self.0))
623    }
624}
625
626/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{SharedSecret} := \mathbb{J}^{(r)}$
627///
628/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
629///
630/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
631#[derive(Debug)]
632pub struct SharedSecret(jubjub::SubgroupPoint);
633
634impl SharedSecret {
635    /// For checking test vectors only.
636    #[cfg(test)]
637    pub(crate) fn to_bytes(&self) -> [u8; 32] {
638        self.0.to_bytes()
639    }
640
641    /// Only for use in batched note encryption.
642    pub(crate) fn batch_to_affine(
643        shared_secrets: Vec<Option<Self>>,
644    ) -> impl Iterator<Item = Option<jubjub::AffinePoint>> {
645        // Filter out the positions for which ephemeral_key was not a valid encoding.
646        let secrets: Vec<_> = shared_secrets
647            .iter()
648            .filter_map(|s| s.as_ref().map(|s| jubjub::ExtendedPoint::from(s.0)))
649            .collect();
650
651        // Batch-normalize the shared secrets.
652        let mut secrets_affine = vec![jubjub::AffinePoint::identity(); secrets.len()];
653        group::Curve::batch_normalize(&secrets, &mut secrets_affine);
654
655        // Re-insert the invalid ephemeral_key positions.
656        let mut secrets_affine = secrets_affine.into_iter();
657        shared_secrets
658            .into_iter()
659            .map(move |s| s.and_then(|_| secrets_affine.next()))
660    }
661
662    /// Defined in [Zcash Protocol Spec § 5.4.5.4: Sapling Key Agreement][concretesaplingkdf].
663    ///
664    /// [concretesaplingkdf]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkdf
665    pub(crate) fn kdf_sapling(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
666        Self::kdf_sapling_inner(
667            jubjub::ExtendedPoint::from(self.0).to_affine(),
668            ephemeral_key,
669        )
670    }
671
672    /// Only for direct use in batched note encryption.
673    pub(crate) fn kdf_sapling_inner(
674        secret: jubjub::AffinePoint,
675        ephemeral_key: &EphemeralKeyBytes,
676    ) -> Blake2bHash {
677        Blake2bParams::new()
678            .hash_length(32)
679            .personal(KDF_SAPLING_PERSONALIZATION)
680            .to_state()
681            .update(&secret.to_bytes())
682            .update(ephemeral_key.as_ref())
683            .finalize()
684    }
685}
686
687#[cfg(any(test, feature = "test-dependencies"))]
688#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
689pub mod testing {
690    use proptest::collection::vec;
691    use proptest::prelude::*;
692
693    use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
694
695    prop_compose! {
696        pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey {
697            ExpandedSpendingKey::from_spending_key(&v)
698        }
699    }
700
701    prop_compose! {
702        pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
703            FullViewingKey::from_expanded_spending_key(&sk)
704        }
705    }
706
707    prop_compose! {
708        pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
709            fvk.vk.ivk()
710        }
711    }
712}
713
714#[cfg(test)]
715mod tests {
716    use alloc::string::ToString;
717    use group::{Group, GroupEncoding};
718
719    use super::{FullViewingKey, SpendAuthorizingKey, SpendValidatingKey};
720    use crate::{constants::SPENDING_KEY_GENERATOR, test_vectors};
721
722    #[test]
723    fn ak_must_be_prime_order() {
724        let mut buf = [0; 96];
725        let identity = jubjub::SubgroupPoint::identity();
726
727        // Set both ak and nk to the identity.
728        buf[0..32].copy_from_slice(&identity.to_bytes());
729        buf[32..64].copy_from_slice(&identity.to_bytes());
730
731        // ak is not allowed to be the identity.
732        assert_eq!(
733            FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
734            "ak not of prime order"
735        );
736
737        // Set ak to a basepoint.
738        let basepoint = SPENDING_KEY_GENERATOR;
739        buf[0..32].copy_from_slice(&basepoint.to_bytes());
740
741        // nk is allowed to be the identity.
742        assert!(FullViewingKey::read(&buf[..]).is_ok());
743    }
744
745    #[test]
746    fn spend_auth_sig_test_vectors() {
747        for tv in test_vectors::signatures::make_test_vectors() {
748            let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
749            let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
750            let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
751            let sig = redjubjub::Signature::from(tv.sig);
752            let rsig = redjubjub::Signature::from(tv.rsig);
753
754            let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
755
756            assert_eq!(<[u8; 32]>::from(sk.randomize(&alpha)), tv.rsk);
757            assert_eq!(vk.randomize(&alpha), rvk);
758
759            // assert_eq!(vk.0.verify(&tv.m, &sig), Ok(()));
760            // assert_eq!(rvk.verify(&tv.m, &rsig), Ok(()));
761            assert_eq!(
762                vk.0.verify(&tv.m, &rsig),
763                Err(redjubjub::Error::InvalidSignature),
764            );
765            assert_eq!(
766                rvk.verify(&tv.m, &sig),
767                Err(redjubjub::Error::InvalidSignature),
768            );
769        }
770    }
771}