Skip to main content

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 corez::io::{self, Read, Write};
10
11use super::{
12    address::PaymentAddress,
13    constants,
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#[cfg(feature = "zeroize")]
30use zeroize::{Zeroize, ZeroizeOnDrop};
31
32use crate::zeroize_secret;
33
34#[cfg(all(feature = "circuit", test))]
35use rand_core::Rng;
36
37/// Errors that can occur in the decoding of Sapling spending keys.
38#[derive(Debug)]
39#[non_exhaustive]
40pub enum DecodingError {
41    /// The length of the byte slice provided for decoding was incorrect.
42    LengthInvalid { expected: usize, actual: usize },
43    /// Could not decode the `ask` bytes to a jubjub field element.
44    InvalidAsk,
45    /// Could not decode the `nsk` bytes to a jubjub field element.
46    InvalidNsk,
47    /// The incoming viewing key derived from the decoded key is zero.
48    InvalidIvk,
49    /// An extended spending key had an unsupported child index: either a non-hardened
50    /// index, or a non-zero index at depth 0.
51    UnsupportedChildIndex,
52}
53
54impl fmt::Display for DecodingError {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        match self {
57            DecodingError::LengthInvalid { expected, actual } => {
58                write!(f, "invalid slice length (expected {expected}, got {actual}")
59            }
60            DecodingError::InvalidAsk => write!(f, "invalid `ask`"),
61            DecodingError::InvalidNsk => write!(f, "invalid `nsk`"),
62            DecodingError::InvalidIvk => write!(f, "derived `ivk` is zero"),
63            DecodingError::UnsupportedChildIndex => write!(
64                f,
65                "unsupported child index (either non-hardened, or non-zero at depth 0)"
66            ),
67        }
68    }
69}
70
71#[cfg(feature = "std")]
72impl std::error::Error for DecodingError {}
73
74/// A spend authorizing key, used to create spend authorization signatures.
75///
76/// $\mathsf{ask}$ as defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
77///
78/// If the `zeroize` feature is enabled, the key material is zeroized on drop.
79///
80/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
81#[derive(Clone, Debug)]
82pub struct SpendAuthorizingKey(redjubjub::SigningKey<SpendAuth>);
83
84impl PartialEq for SpendAuthorizingKey {
85    fn eq(&self, other: &Self) -> bool {
86        self.0.to_bytes().ct_eq(&other.0.to_bytes()).into()
87    }
88}
89
90#[cfg(feature = "zeroize")]
91impl Zeroize for SpendAuthorizingKey {
92    fn zeroize(&mut self) {
93        self.0.zeroize();
94    }
95}
96
97// The inner `redjubjub::SigningKey` zeroizes itself on drop.
98#[cfg(feature = "zeroize")]
99impl ZeroizeOnDrop for SpendAuthorizingKey {}
100
101impl Eq for SpendAuthorizingKey {}
102
103impl From<&SpendValidatingKey> for jubjub::ExtendedPoint {
104    fn from(spend_validating_key: &SpendValidatingKey) -> jubjub::ExtendedPoint {
105        jubjub::ExtendedPoint::from_bytes(&spend_validating_key.to_bytes()).unwrap()
106    }
107}
108
109impl SpendAuthorizingKey {
110    /// Derives ask from sk. Internal use only, does not enforce all constraints.
111    fn derive_inner(sk: &[u8]) -> jubjub::Scalar {
112        let mut prf = PrfExpand::SAPLING_ASK.with(sk);
113        let ask = jubjub::Scalar::from_bytes_wide(&prf);
114        zeroize_secret(&mut prf);
115        ask
116    }
117
118    /// Constructs a `SpendAuthorizingKey` from a raw scalar.
119    ///
120    /// The scalar is copied into the returned key; the caller is responsible for
121    /// zeroizing its own copy once it is no longer needed.
122    pub(crate) fn from_scalar(ask: jubjub::Scalar) -> Option<Self> {
123        if ask.is_zero().into() {
124            None
125        } else {
126            let mut repr = ask.to_bytes();
127            let key = SpendAuthorizingKey(
128                redjubjub::SigningKey::from_bytes(&repr)
129                    .expect("canonical scalar encodings are valid RedJubjub signing keys"),
130            );
131            zeroize_secret(&mut repr);
132            Some(key)
133        }
134    }
135
136    /// Derives a `SpendAuthorizingKey` from a spending key.
137    fn from_spending_key(sk: &[u8]) -> Option<Self> {
138        Self::from_scalar(Self::derive_inner(sk))
139    }
140
141    /// Parses a `SpendAuthorizingKey` from its encoded form.
142    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
143        <[u8; 32]>::try_from(bytes)
144            .ok()
145            .and_then(|b| {
146                // RedJubjub.Private permits the full set of Jubjub scalars including
147                // zero. However, a SpendAuthorizingKey is further restricted within the
148                // Sapling key tree to be a non-zero scalar.
149                jubjub::Scalar::from_repr(b)
150                    .and_then(|s| {
151                        CtOption::new(
152                            redjubjub::SigningKey::from_bytes(&b)
153                                .expect("RedJubjub permits the set of valid SpendAuthorizingKeys"),
154                            !s.is_zero(),
155                        )
156                    })
157                    .into()
158            })
159            .map(SpendAuthorizingKey)
160    }
161
162    /// Converts this spend authorizing key to its serialized form.
163    ///
164    /// The returned array is secret key material; the caller is responsible for
165    /// zeroizing it once it is no longer needed.
166    pub fn to_bytes(&self) -> [u8; 32] {
167        self.0.to_bytes()
168    }
169
170    /// Converts this spend authorizing key to a raw scalar.
171    ///
172    /// Only used for ZIP 32 child derivation.
173    pub(crate) fn to_scalar(&self) -> jubjub::Scalar {
174        let mut repr = self.0.to_bytes();
175        let ask = jubjub::Scalar::from_repr(repr).unwrap();
176        zeroize_secret(&mut repr);
177        ask
178    }
179
180    /// Randomizes this spend authorizing key with the given `randomizer`.
181    ///
182    /// The resulting key can be used to actually sign a spend.
183    pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::SigningKey<SpendAuth> {
184        self.0.randomize(randomizer)
185    }
186}
187
188/// A key used to validate spend authorization signatures.
189///
190/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
191///
192/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
193#[derive(Clone, Debug)]
194pub struct SpendValidatingKey(redjubjub::VerificationKey<SpendAuth>);
195
196impl From<&SpendAuthorizingKey> for SpendValidatingKey {
197    fn from(ask: &SpendAuthorizingKey) -> Self {
198        SpendValidatingKey((&ask.0).into())
199    }
200}
201
202impl PartialEq for SpendValidatingKey {
203    fn eq(&self, other: &Self) -> bool {
204        <[u8; 32]>::from(self.0)
205            .ct_eq(&<[u8; 32]>::from(other.0))
206            .into()
207    }
208}
209
210impl Eq for SpendValidatingKey {}
211
212impl SpendValidatingKey {
213    /// For circuit tests only.
214    #[cfg(all(feature = "circuit", test))]
215    pub(crate) fn fake_random<R: Rng>(mut rng: R) -> Self {
216        loop {
217            if let Some(k) = Self::from_bytes(&jubjub::SubgroupPoint::random(&mut rng).to_bytes()) {
218                break k;
219            }
220        }
221    }
222
223    /// Only exposed for `zcashd` unit tests.
224    #[cfg(feature = "temporary-zcashd")]
225    pub fn temporary_zcash_from_bytes(bytes: &[u8]) -> Option<Self> {
226        Self::from_bytes(bytes)
227    }
228
229    /// Parses a `SpendValidatingKey` from its encoded form.
230    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
231        <[u8; 32]>::try_from(bytes)
232            .ok()
233            .and_then(|b| {
234                // RedJubjub.Public permits the full set of Jubjub points including the
235                // identity and cofactors; this is the type used for `rk` in Spend
236                // descriptions. However, a SpendValidatingKey is further restricted
237                // within the Sapling key tree to be a non-identity element of the
238                // prime-order subgroup.
239                jubjub::SubgroupPoint::from_bytes(&b)
240                    .and_then(|p| {
241                        CtOption::new(
242                            redjubjub::VerificationKey::try_from(b)
243                                .expect("RedJubjub permits the set of valid SpendValidatingKeys"),
244                            !p.is_identity(),
245                        )
246                    })
247                    .into()
248            })
249            .map(SpendValidatingKey)
250    }
251
252    /// Converts this spend validating key to its serialized form,
253    /// `LEBS2OSP_256(repr_J(ak))`.
254    pub fn to_bytes(&self) -> [u8; 32] {
255        <[u8; 32]>::from(self.0)
256    }
257
258    /// Randomizes this spend validating key with the given `randomizer`.
259    pub fn randomize(&self, randomizer: &jubjub::Scalar) -> redjubjub::VerificationKey<SpendAuth> {
260        self.0.randomize(randomizer)
261    }
262}
263
264/// An outgoing viewing key
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub struct OutgoingViewingKey(pub [u8; 32]);
267
268/// `OutgoingViewingKey` is `Copy`, so it cannot implement `ZeroizeOnDrop`; types that
269/// own one alongside secret key material are responsible for zeroizing it.
270#[cfg(feature = "zeroize")]
271impl Zeroize for OutgoingViewingKey {
272    fn zeroize(&mut self) {
273        self.0.zeroize();
274    }
275}
276
277/// A Sapling expanded spending key
278///
279/// The incoming viewing key derived from an `ExpandedSpendingKey` is never zero.
280///
281/// If the `zeroize` feature is enabled, the key material is zeroized on drop.
282#[derive(Clone)]
283pub struct ExpandedSpendingKey {
284    ask: SpendAuthorizingKey,
285    nsk: jubjub::Fr,
286    ovk: OutgoingViewingKey,
287}
288
289#[cfg(feature = "zeroize")]
290impl Zeroize for ExpandedSpendingKey {
291    fn zeroize(&mut self) {
292        self.ask.zeroize();
293        self.nsk.zeroize();
294        self.ovk.zeroize();
295    }
296}
297
298#[cfg(feature = "zeroize")]
299impl ZeroizeOnDrop for ExpandedSpendingKey {}
300
301#[cfg(feature = "zeroize")]
302impl Drop for ExpandedSpendingKey {
303    fn drop(&mut self) {
304        self.zeroize();
305    }
306}
307
308impl fmt::Debug for ExpandedSpendingKey {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        f.debug_struct("ExpandedSpendingKey")
311            .finish_non_exhaustive()
312    }
313}
314
315impl ExpandedSpendingKey {
316    /// Expands a spending key into its components.
317    ///
318    /// Returns `None` if the spending key expands to `ask = 0`, or to a key whose
319    /// incoming viewing key is zero. The protocol requires such a spending key to be
320    /// discarded. This has a negligible probability of occurring.
321    pub fn from_spending_key(sk: &[u8]) -> Option<Self> {
322        let ask = SpendAuthorizingKey::from_spending_key(sk)?;
323        let mut nsk_prf = PrfExpand::SAPLING_NSK.with(sk);
324        let mut nsk = jubjub::Fr::from_bytes_wide(&nsk_prf);
325        zeroize_secret(&mut nsk_prf);
326        let mut ovk_prf = PrfExpand::SAPLING_OVK.with(sk);
327        let mut ovk = OutgoingViewingKey([0u8; 32]);
328        ovk.0.copy_from_slice(&ovk_prf[..32]);
329        zeroize_secret(&mut ovk_prf);
330        let expsk = Self::from_parts(ask, nsk, ovk);
331        zeroize_secret(&mut nsk);
332        zeroize_secret(&mut ovk.0);
333        expsk
334    }
335
336    /// Constructs an expanded spending key from its components.
337    ///
338    /// Returns `None` if the incoming viewing key derived from the components is zero.
339    pub fn from_parts(
340        ask: SpendAuthorizingKey,
341        nsk: jubjub::Fr,
342        ovk: OutgoingViewingKey,
343    ) -> Option<Self> {
344        ProofGenerationKey::from_parts((&ask).into(), nsk).map(|_| ExpandedSpendingKey {
345            ask,
346            nsk,
347            ovk,
348        })
349    }
350
351    /// Returns the spend authorizing key.
352    pub fn ask(&self) -> &SpendAuthorizingKey {
353        &self.ask
354    }
355
356    /// Returns the proof authorizing key.
357    pub fn nsk(&self) -> &jubjub::Fr {
358        &self.nsk
359    }
360
361    /// Returns the outgoing viewing key.
362    pub fn ovk(&self) -> &OutgoingViewingKey {
363        &self.ovk
364    }
365
366    pub fn proof_generation_key(&self) -> ProofGenerationKey {
367        // `ExpandedSpendingKey::from_parts` has checked this key.
368        ProofGenerationKey {
369            ak: (&self.ask).into(),
370            nsk: self.nsk,
371        }
372    }
373
374    /// Decodes the expanded spending key from its serialized representation
375    /// as part of the encoding of the extended spending key as defined in
376    /// [ZIP 32](https://zips.z.cash/zip-0032)
377    ///
378    /// Returns an error if the incoming viewing key derived from the key is zero.
379    pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
380        if b.len() != 96 {
381            return Err(DecodingError::LengthInvalid {
382                expected: 96,
383                actual: b.len(),
384            });
385        }
386
387        let ask = SpendAuthorizingKey::from_bytes(&b[0..32]).ok_or(DecodingError::InvalidAsk)?;
388        let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
389            .ok_or(DecodingError::InvalidNsk)?;
390        let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
391
392        Self::from_parts(ask, nsk, ovk).ok_or(DecodingError::InvalidIvk)
393    }
394
395    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
396        let mut repr = [0u8; 96];
397        reader.read_exact(repr.as_mut())?;
398        let result = Self::from_bytes(&repr);
399        zeroize_secret(&mut repr);
400        result.map_err(|e| match e {
401            DecodingError::InvalidAsk => {
402                io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
403            }
404            DecodingError::InvalidNsk => {
405                io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
406            }
407            DecodingError::InvalidIvk => io::Error::new(io::ErrorKind::InvalidData, "ivk is zero"),
408            DecodingError::LengthInvalid { .. } | DecodingError::UnsupportedChildIndex => {
409                unreachable!()
410            }
411        })
412    }
413
414    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
415        writer.write_all(&self.to_bytes())
416    }
417
418    /// Encodes the expanded spending key to its serialized representation
419    /// as part of the encoding of the extended spending key as defined in
420    /// [ZIP 32](https://zips.z.cash/zip-0032)
421    ///
422    /// The returned array is secret key material; the caller is responsible for
423    /// zeroizing it once it is no longer needed.
424    pub fn to_bytes(&self) -> [u8; 96] {
425        let mut result = [0u8; 96];
426        result[0..32].copy_from_slice(&self.ask.to_bytes());
427        result[32..64].copy_from_slice(&self.nsk.to_repr());
428        result[64..96].copy_from_slice(&self.ovk.0);
429        result
430    }
431}
432
433/// A Sapling proof generation key.
434///
435/// The incoming viewing key derived from a `ProofGenerationKey` is never zero.
436///
437/// If the `zeroize` feature is enabled, `nsk` is zeroized on drop.
438#[derive(Clone)]
439pub struct ProofGenerationKey {
440    ak: SpendValidatingKey,
441    nsk: jubjub::Fr,
442}
443
444#[cfg(feature = "zeroize")]
445impl Zeroize for ProofGenerationKey {
446    fn zeroize(&mut self) {
447        // `ak` is public and is left intact.
448        self.nsk.zeroize();
449    }
450}
451
452#[cfg(feature = "zeroize")]
453impl ZeroizeOnDrop for ProofGenerationKey {}
454
455#[cfg(feature = "zeroize")]
456impl Drop for ProofGenerationKey {
457    fn drop(&mut self) {
458        self.zeroize();
459    }
460}
461
462impl fmt::Debug for ProofGenerationKey {
463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464        f.debug_struct("ProofGenerationKey")
465            .field("ak", &self.ak)
466            .finish_non_exhaustive()
467    }
468}
469
470impl ProofGenerationKey {
471    /// Constructs a proof generation key from its components.
472    ///
473    /// Returns `None` if the incoming viewing key derived from `ak` and `nsk` is zero.
474    /// Such a key is invalid and has no payment addresses.
475    pub fn from_parts(ak: SpendValidatingKey, nsk: jubjub::Fr) -> Option<Self> {
476        let nk = NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * nsk);
477        ViewingKey::from_parts(ak.clone(), nk).map(|_| ProofGenerationKey { ak, nsk })
478    }
479
480    /// Returns the spend validating key.
481    pub fn ak(&self) -> &SpendValidatingKey {
482        &self.ak
483    }
484
485    /// Returns the proof authorizing key.
486    pub fn nsk(&self) -> &jubjub::Fr {
487        &self.nsk
488    }
489
490    /// Derives the viewing key corresponding to this proof generation key.
491    pub fn to_viewing_key(&self) -> ViewingKey {
492        // `ProofGenerationKey::from_parts` has checked this key.
493        ViewingKey {
494            ak: self.ak.clone(),
495            nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
496        }
497    }
498}
499
500/// A key used to derive the nullifier for a Sapling note.
501#[derive(Debug, Copy, Clone, PartialEq, Eq)]
502pub struct NullifierDerivingKey(pub jubjub::SubgroupPoint);
503
504/// A Sapling viewing key: the spend validating key `ak` and the nullifier deriving key
505/// `nk`.
506///
507/// The incoming viewing key derived from a `ViewingKey` is never zero.
508#[derive(Debug, Clone, PartialEq, Eq)]
509pub struct ViewingKey {
510    ak: SpendValidatingKey,
511    nk: NullifierDerivingKey,
512}
513
514impl ViewingKey {
515    /// Constructs a viewing key from its components.
516    ///
517    /// Returns `None` if the incoming viewing key derived from `ak` and `nk` is zero.
518    /// Such a key is invalid and has no payment addresses.
519    pub fn from_parts(ak: SpendValidatingKey, nk: NullifierDerivingKey) -> Option<Self> {
520        let ivk = crh_ivk(ak.to_bytes(), nk.0.to_bytes());
521        SaplingIvk::from_scalar(ivk)
522            .into_option()
523            .map(|_| ViewingKey { ak, nk })
524    }
525
526    /// Returns the spend validating key.
527    pub fn ak(&self) -> &SpendValidatingKey {
528        &self.ak
529    }
530
531    /// Returns the nullifier deriving key.
532    pub fn nk(&self) -> &NullifierDerivingKey {
533        &self.nk
534    }
535
536    pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
537        self.ak.randomize(&ar)
538    }
539
540    /// Derives the incoming viewing key.
541    pub fn ivk(&self) -> SaplingIvk {
542        SaplingIvk::from_scalar(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
543            .expect("ViewingKey constructors reject a zero ivk")
544    }
545
546    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
547        self.ivk().to_payment_address(diversifier)
548    }
549}
550
551/// A Sapling key that provides the capability to view incoming and outgoing transactions.
552#[derive(Debug, PartialEq, Eq)]
553pub struct FullViewingKey {
554    pub vk: ViewingKey,
555    pub ovk: OutgoingViewingKey,
556}
557
558impl Clone for FullViewingKey {
559    fn clone(&self) -> Self {
560        FullViewingKey {
561            vk: self.vk.clone(),
562            ovk: self.ovk,
563        }
564    }
565}
566
567impl FullViewingKey {
568    /// Derives the full viewing key corresponding to an expanded spending key.
569    pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
570        FullViewingKey {
571            vk: expsk.proof_generation_key().to_viewing_key(),
572            ovk: expsk.ovk,
573        }
574    }
575
576    /// Reads a full viewing key from its raw encoding.
577    ///
578    /// Returns an error if `ak` is not a prime-order point, if `nk` is not in the
579    /// prime-order subgroup, or if the derived incoming viewing key is zero.
580    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
581        let ak = {
582            let mut buf = [0u8; 32];
583            reader.read_exact(&mut buf)?;
584            SpendValidatingKey::from_bytes(&buf)
585        };
586        let nk = {
587            let mut buf = [0u8; 32];
588            reader.read_exact(&mut buf)?;
589            jubjub::SubgroupPoint::from_bytes(&buf)
590        };
591        if ak.is_none() {
592            return Err(io::Error::new(
593                io::ErrorKind::InvalidInput,
594                "ak not of prime order",
595            ));
596        }
597        if nk.is_none().into() {
598            return Err(io::Error::new(
599                io::ErrorKind::InvalidInput,
600                "nk not in prime-order subgroup",
601            ));
602        }
603        let ak = ak.unwrap();
604        let nk = NullifierDerivingKey(nk.unwrap());
605        let vk = ViewingKey::from_parts(ak, nk)
606            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "ivk is zero"))?;
607
608        let mut ovk = [0u8; 32];
609        reader.read_exact(&mut ovk)?;
610
611        Ok(FullViewingKey {
612            vk,
613            ovk: OutgoingViewingKey(ovk),
614        })
615    }
616
617    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
618        writer.write_all(&self.vk.ak().to_bytes())?;
619        writer.write_all(&self.vk.nk().0.to_bytes())?;
620        writer.write_all(&self.ovk.0)?;
621
622        Ok(())
623    }
624
625    pub fn to_bytes(&self) -> [u8; 96] {
626        let mut result = [0u8; 96];
627        self.write(&mut result[..])
628            .expect("should be able to serialize a FullViewingKey");
629        result
630    }
631}
632
633/// A Sapling incoming viewing key.
634///
635/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents]
636/// as an integer in the range $\{1 .. 2^{\ell_{\mathsf{ivk}}} - 1\}$, where
637/// $\ell_{\mathsf{ivk}} = 251$.
638///
639/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
640#[derive(Debug, Clone, PartialEq, Eq)]
641pub struct SaplingIvk(jubjub::Fr);
642
643impl SaplingIvk {
644    /// Parses an incoming viewing key from its little-endian encoding.
645    ///
646    /// Returns `None` if `bytes` does not encode an integer in the range
647    /// $\{1 .. 2^{251} - 1\}$.
648    pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
649        Self::parse_with(bytes, |ivk| ivk)
650    }
651
652    /// Parses an incoming viewing key from its little-endian encoding, and passes it to
653    /// `f`.
654    ///
655    /// Returns `None` if `bytes` does not encode an integer in the range
656    /// $\{1 .. 2^{251} - 1\}$. Validity is determined in constant time.
657    pub(crate) fn parse_with<T>(bytes: &[u8; 32], f: impl FnOnce(Self) -> T) -> CtOption<T> {
658        // An encoding with any of the five most significant bits set is at least 2^251.
659        let in_range = (bytes[31] & 0b1111_1000).ct_eq(&0);
660        jubjub::Fr::from_repr(*bytes)
661            .and_then(|ivk| CtOption::new(f(SaplingIvk(ivk)), in_range & !ivk.is_zero()))
662    }
663
664    /// Samples a uniformly random incoming viewing key.
665    pub(crate) fn random<R: rand_core::Rng + ?Sized>(rng: &mut R) -> Self {
666        loop {
667            let mut bytes = [0u8; 32];
668            rng.fill_bytes(&mut bytes);
669            bytes[31] &= 0b0000_0111;
670            if let Some(ivk) = Self::from_bytes(&bytes).into_option() {
671                break ivk;
672            }
673        }
674    }
675
676    /// Constructs an incoming viewing key from a scalar.
677    ///
678    /// Returns `None` if `ivk` is not in the range $\{1 .. 2^{251} - 1\}$.
679    pub(crate) fn from_scalar(ivk: jubjub::Fr) -> CtOption<Self> {
680        Self::from_bytes(&ivk.to_repr())
681    }
682
683    pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
684        let prepared_ivk = PreparedIncomingViewingKey::new(self);
685        DiversifiedTransmissionKey::derive(&prepared_ivk, &diversifier)
686            .and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
687    }
688
689    pub fn to_repr(&self) -> [u8; 32] {
690        self.0.to_repr()
691    }
692}
693
694/// A Sapling incoming viewing key that has been precomputed for trial decryption.
695#[derive(Clone, Debug)]
696pub struct PreparedIncomingViewingKey(PreparedScalar);
697
698#[cfg(feature = "std")]
699impl memuse::DynamicUsage for PreparedIncomingViewingKey {
700    fn dynamic_usage(&self) -> usize {
701        self.0.dynamic_usage()
702    }
703
704    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
705        self.0.dynamic_usage_bounds()
706    }
707}
708
709impl PreparedIncomingViewingKey {
710    /// Performs the necessary precomputations to use a `SaplingIvk` for note decryption.
711    pub fn new(ivk: &SaplingIvk) -> Self {
712        Self(PreparedScalar::new(&ivk.0))
713    }
714}
715
716#[derive(Copy, Clone, Debug, PartialEq, Eq)]
717pub struct Diversifier(pub [u8; 11]);
718
719impl Diversifier {
720    pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
721        diversify_hash(&self.0)
722    }
723}
724
725/// The diversified transmission key for a given payment address.
726///
727/// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
728///
729/// The protocol requires this key to not be the identity. [`PaymentAddress::from_parts`]
730/// enforces this.
731///
732/// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
733#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
734pub struct DiversifiedTransmissionKey(jubjub::SubgroupPoint);
735
736impl DiversifiedTransmissionKey {
737    /// Defined in [Zcash Protocol Spec § 4.2.2: Sapling Key Components][saplingkeycomponents].
738    ///
739    /// Returns `None` if `d` is an invalid diversifier.
740    ///
741    /// [saplingkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
742    pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Option<Self> {
743        d.g_d()
744            .map(PreparedBaseSubgroup::new)
745            .map(|g_d| ka_sapling_derive_public_subgroup_prepared(&ivk.0, &g_d))
746            .map(DiversifiedTransmissionKey)
747    }
748
749    /// $abst_J(bytes)$
750    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
751        jubjub::SubgroupPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
752    }
753
754    /// $repr_J(self)$
755    pub(crate) fn to_bytes(self) -> [u8; 32] {
756        self.0.to_bytes()
757    }
758
759    /// Returns true if this is the identity.
760    pub(crate) fn is_identity(&self) -> bool {
761        self.0.is_identity().into()
762    }
763
764    /// Exposes the inner Jubjub point.
765    ///
766    /// This API is exposed for `zcash_proof` usage, and will be removed when this type is
767    /// refactored into the `sapling-crypto` crate.
768    pub fn inner(&self) -> jubjub::SubgroupPoint {
769        self.0
770    }
771}
772
773impl ConditionallySelectable for DiversifiedTransmissionKey {
774    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
775        DiversifiedTransmissionKey(jubjub::SubgroupPoint::conditional_select(
776            &a.0, &b.0, choice,
777        ))
778    }
779}
780
781/// An ephemeral secret key used to encrypt an output note on-chain.
782///
783/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
784/// practice, `esk` is derived deterministically from the note that it is encrypting.
785///
786/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Private} := \mathbb{F}_{r_J}$
787///
788/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
789///
790/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
791#[derive(Debug)]
792pub struct EphemeralSecretKey(pub(crate) jubjub::Scalar);
793
794impl ConstantTimeEq for EphemeralSecretKey {
795    fn ct_eq(&self, other: &Self) -> subtle::Choice {
796        self.0.ct_eq(&other.0)
797    }
798}
799
800impl EphemeralSecretKey {
801    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
802        jubjub::Scalar::from_bytes(bytes).map(EphemeralSecretKey)
803    }
804
805    pub(crate) fn derive_public(&self, g_d: jubjub::ExtendedPoint) -> EphemeralPublicKey {
806        EphemeralPublicKey(ka_sapling_derive_public(&self.0, &g_d))
807    }
808
809    pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
810        SharedSecret(ka_sapling_agree(&self.0, &pk_d.0.into()))
811    }
812}
813
814/// An ephemeral public key used to encrypt an output note on-chain.
815///
816/// `epk` is "ephemeral" in the sense that each public key is only used once. In practice,
817/// `epk` is derived deterministically from the note that it is encrypting.
818///
819/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{Public} := \mathbb{J}$
820///
821/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
822///
823/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
824#[derive(Debug)]
825pub struct EphemeralPublicKey(jubjub::ExtendedPoint);
826
827impl EphemeralPublicKey {
828    pub(crate) fn from_affine(epk: jubjub::AffinePoint) -> Self {
829        EphemeralPublicKey(epk.into())
830    }
831
832    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
833        jubjub::ExtendedPoint::from_bytes(bytes).map(EphemeralPublicKey)
834    }
835
836    pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
837        EphemeralKeyBytes(self.0.to_bytes())
838    }
839}
840
841/// A Sapling ephemeral public key that has been precomputed for trial decryption.
842#[derive(Clone, Debug)]
843pub struct PreparedEphemeralPublicKey(PreparedBase);
844
845impl PreparedEphemeralPublicKey {
846    pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
847        PreparedEphemeralPublicKey(PreparedBase::new(epk.0))
848    }
849
850    pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
851        SharedSecret(ka_sapling_agree_prepared(&ivk.0, &self.0))
852    }
853}
854
855/// $\mathsf{KA}^\mathsf{Sapling}.\mathsf{SharedSecret} := \mathbb{J}^{(r)}$
856///
857/// Defined in [section 5.4.5.3: Sapling Key Agreement][concretesaplingkeyagreement].
858///
859/// [concretesaplingkeyagreement]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
860#[derive(Debug)]
861pub struct SharedSecret(jubjub::SubgroupPoint);
862
863impl SharedSecret {
864    /// For checking test vectors only.
865    #[cfg(test)]
866    pub(crate) fn to_bytes(&self) -> [u8; 32] {
867        self.0.to_bytes()
868    }
869
870    /// Only for use in batched note encryption.
871    pub(crate) fn batch_to_affine(
872        shared_secrets: Vec<Option<Self>>,
873    ) -> impl Iterator<Item = Option<jubjub::AffinePoint>> {
874        // Filter out the positions for which ephemeral_key was not a valid encoding.
875        let secrets: Vec<_> = shared_secrets
876            .iter()
877            .filter_map(|s| s.as_ref().map(|s| jubjub::ExtendedPoint::from(s.0)))
878            .collect();
879
880        // Batch-normalize the shared secrets.
881        let mut secrets_affine = vec![jubjub::AffinePoint::identity(); secrets.len()];
882        group::Curve::batch_normalize(&secrets, &mut secrets_affine);
883
884        // Re-insert the invalid ephemeral_key positions.
885        let mut secrets_affine = secrets_affine.into_iter();
886        shared_secrets
887            .into_iter()
888            .map(move |s| s.and_then(|_| secrets_affine.next()))
889    }
890
891    /// Defined in [Zcash Protocol Spec § 5.4.5.4: Sapling Key Agreement][concretesaplingkdf].
892    ///
893    /// [concretesaplingkdf]: https://zips.z.cash/protocol/protocol.pdf#concretesaplingkdf
894    pub(crate) fn kdf_sapling(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
895        Self::kdf_sapling_inner(
896            jubjub::ExtendedPoint::from(self.0).to_affine(),
897            ephemeral_key,
898        )
899    }
900
901    /// Only for direct use in batched note encryption.
902    pub(crate) fn kdf_sapling_inner(
903        secret: jubjub::AffinePoint,
904        ephemeral_key: &EphemeralKeyBytes,
905    ) -> Blake2bHash {
906        Blake2bParams::new()
907            .hash_length(32)
908            .personal(KDF_SAPLING_PERSONALIZATION)
909            .to_state()
910            .update(&secret.to_bytes())
911            .update(ephemeral_key.as_ref())
912            .finalize()
913    }
914}
915
916#[cfg(any(test, feature = "test-dependencies"))]
917#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
918pub mod testing {
919    use proptest::collection::vec;
920    use proptest::prelude::*;
921
922    use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
923
924    prop_compose! {
925        pub fn arb_expanded_spending_key()(
926            expsk in vec(any::<u8>(), 32..252).prop_filter_map(
927                "spending key must be valid",
928                |sk| ExpandedSpendingKey::from_spending_key(&sk),
929            )
930        ) -> ExpandedSpendingKey {
931            expsk
932        }
933    }
934
935    prop_compose! {
936        pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
937            FullViewingKey::from_expanded_spending_key(&sk)
938        }
939    }
940
941    prop_compose! {
942        pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
943            fvk.vk.ivk()
944        }
945    }
946}
947
948#[cfg(test)]
949mod tests {
950    use alloc::string::ToString;
951    use group::{Group, GroupEncoding};
952
953    use ff::PrimeField;
954    use proptest::prelude::*;
955
956    use super::{
957        testing::arb_incoming_viewing_key, FullViewingKey, SaplingIvk, SpendAuthorizingKey,
958        SpendValidatingKey,
959    };
960    use crate::{constants::SPENDING_KEY_GENERATOR, test_vectors};
961
962    #[test]
963    fn ak_must_be_prime_order() {
964        let mut buf = [0; 96];
965        let identity = jubjub::SubgroupPoint::identity();
966
967        // Set both ak and nk to the identity.
968        buf[0..32].copy_from_slice(&identity.to_bytes());
969        buf[32..64].copy_from_slice(&identity.to_bytes());
970
971        // ak is not allowed to be the identity.
972        assert_eq!(
973            FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
974            "ak not of prime order"
975        );
976
977        // Set ak to a basepoint.
978        let basepoint = SPENDING_KEY_GENERATOR;
979        buf[0..32].copy_from_slice(&basepoint.to_bytes());
980
981        // nk is allowed to be the identity.
982        assert!(FullViewingKey::read(&buf[..]).is_ok());
983    }
984
985    #[test]
986    fn spend_auth_sig_test_vectors() {
987        for tv in test_vectors::signatures::make_test_vectors() {
988            let sk = SpendAuthorizingKey::from_bytes(&tv.sk).unwrap();
989            let vk = SpendValidatingKey::from_bytes(&tv.vk).unwrap();
990            let rvk = redjubjub::VerificationKey::try_from(tv.rvk).unwrap();
991            let sig = redjubjub::Signature::from(tv.sig);
992            let rsig = redjubjub::Signature::from(tv.rsig);
993
994            let alpha = jubjub::Scalar::from_bytes(&tv.alpha).unwrap();
995
996            assert_eq!(sk.randomize(&alpha).to_bytes(), tv.rsk);
997            assert_eq!(vk.randomize(&alpha), rvk);
998
999            // assert_eq!(vk.0.verify(&tv.m, &sig), Ok(()));
1000            // assert_eq!(rvk.verify(&tv.m, &rsig), Ok(()));
1001            assert_eq!(
1002                vk.0.verify(&tv.m, &rsig),
1003                Err(redjubjub::Error::InvalidSignature),
1004            );
1005            assert_eq!(
1006                rvk.verify(&tv.m, &sig),
1007                Err(redjubjub::Error::InvalidSignature),
1008            );
1009        }
1010    }
1011
1012    #[test]
1013    fn ivk_encoding_must_be_in_range() {
1014        let parses = |bytes: [u8; 32]| bool::from(SaplingIvk::from_bytes(&bytes).is_some());
1015
1016        // The range is {1 .. 2^251 - 1}.
1017        assert!(!parses([0; 32]));
1018
1019        let mut one = [0; 32];
1020        one[0] = 1;
1021        assert!(parses(one));
1022
1023        let mut max = [0xff; 32];
1024        max[31] = 0b0000_0111;
1025        assert!(parses(max));
1026
1027        // 2^251 and r - 1 are canonical scalars, but are not in range.
1028        let mut two_pow_251 = [0; 32];
1029        two_pow_251[31] = 0b0000_1000;
1030        assert!(!parses(two_pow_251));
1031        assert!(!parses((-jubjub::Fr::one()).to_repr()));
1032    }
1033
1034    proptest! {
1035        #[test]
1036        fn ivk_encoding_round_trip(ivk in arb_incoming_viewing_key()) {
1037            prop_assert_eq!(SaplingIvk::from_bytes(&ivk.to_repr()).into_option(), Some(ivk));
1038        }
1039    }
1040}
1041
1042#[cfg(all(test, feature = "zeroize"))]
1043mod zeroize_tests {
1044    use zeroize::Zeroize;
1045
1046    use super::ExpandedSpendingKey;
1047
1048    #[test]
1049    fn expanded_spending_key_zeroizes() {
1050        let mut expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1051        assert_ne!(expsk.to_bytes(), [0; 96]);
1052
1053        expsk.zeroize();
1054        assert_eq!(expsk.to_bytes(), [0; 96]);
1055    }
1056
1057    #[test]
1058    fn proof_generation_key_zeroizes() {
1059        let expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1060        let mut pgk = expsk.proof_generation_key();
1061        assert_ne!(pgk.nsk, <jubjub::Fr as ff::Field>::ZERO);
1062
1063        pgk.zeroize();
1064        assert_eq!(pgk.nsk, <jubjub::Fr as ff::Field>::ZERO);
1065    }
1066
1067    #[test]
1068    fn spend_authorizing_key_zeroizes() {
1069        let expsk = ExpandedSpendingKey::from_spending_key(&[7; 32]).unwrap();
1070        let mut ask = expsk.ask.clone();
1071        assert_ne!(ask.to_bytes(), [0; 32]);
1072
1073        ask.zeroize();
1074        assert_eq!(ask.to_bytes(), [0; 32]);
1075    }
1076}