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