orchard/
keys.rs

1//! Key structures for Orchard.
2
3use alloc::vec::Vec;
4use core2::io::{self, Read, Write};
5
6use ::zip32::{AccountId, ChildIndex};
7use aes::Aes256;
8use blake2b_simd::{Hash as Blake2bHash, Params};
9use fpe::ff1::{BinaryNumeralString, FF1};
10use group::{
11    ff::{Field, PrimeField},
12    prime::PrimeCurveAffine,
13    Curve, GroupEncoding,
14};
15use pasta_curves::pallas;
16use rand::RngCore;
17use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
18use zcash_note_encryption::EphemeralKeyBytes;
19
20use crate::{
21    address::Address,
22    primitives::redpallas::{self, SpendAuth},
23    spec::{
24        commit_ivk, diversify_hash, extract_p, ka_orchard, ka_orchard_prepared, prf_nf, to_base,
25        to_scalar, NonIdentityPallasPoint, NonZeroPallasBase, NonZeroPallasScalar,
26        PreparedNonIdentityBase, PreparedNonZeroScalar, PrfExpand,
27    },
28    zip32::{self, ExtendedSpendingKey},
29};
30
31pub use ::zip32::{DiversifierIndex, Scope};
32
33const KDF_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_OrchardKDF";
34const ZIP32_PURPOSE: u32 = 32;
35
36/// A spending key, from which all key material is derived.
37///
38/// $\mathsf{sk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
39///
40/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
41#[derive(Debug, Copy, Clone)]
42pub struct SpendingKey([u8; 32]);
43
44impl ConstantTimeEq for SpendingKey {
45    fn ct_eq(&self, other: &Self) -> Choice {
46        self.to_bytes().ct_eq(other.to_bytes())
47    }
48}
49
50impl SpendingKey {
51    /// Generates a random spending key.
52    ///
53    /// This is only used when generating dummy notes. Real spending keys should be
54    /// derived according to [ZIP 32].
55    ///
56    /// [ZIP 32]: https://zips.z.cash/zip-0032
57    pub(crate) fn random(rng: &mut impl RngCore) -> Self {
58        loop {
59            let mut bytes = [0; 32];
60            rng.fill_bytes(&mut bytes);
61            let sk = SpendingKey::from_bytes(bytes);
62            if sk.is_some().into() {
63                break sk.unwrap();
64            }
65        }
66    }
67
68    /// Constructs an Orchard spending key from uniformly-random bytes.
69    ///
70    /// Returns `None` if the bytes do not correspond to a valid Orchard spending key.
71    pub fn from_bytes(sk: [u8; 32]) -> CtOption<Self> {
72        let sk = SpendingKey(sk);
73        // If ask = 0, discard this key. We call `derive_inner` rather than
74        // `SpendAuthorizingKey::from` here because we only need to know
75        // whether ask = 0; the adjustment to potentially negate ask is not
76        // needed. Also, `from` would panic on ask = 0.
77        let ask = SpendAuthorizingKey::derive_inner(&sk);
78        // If ivk is 0 or ⊥, discard this key.
79        let fvk = (&sk).into();
80        let external_ivk = KeyAgreementPrivateKey::derive_inner(&fvk);
81        let internal_ivk = KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal());
82        CtOption::new(
83            sk,
84            !(ask.is_zero() | external_ivk.is_none() | internal_ivk.is_none()),
85        )
86    }
87
88    /// Returns the raw bytes of the spending key.
89    pub fn to_bytes(&self) -> &[u8; 32] {
90        &self.0
91    }
92
93    /// Derives the Orchard spending key for the given seed, coin type, and account.
94    pub fn from_zip32_seed(
95        seed: &[u8],
96        coin_type: u32,
97        account: AccountId,
98    ) -> Result<Self, zip32::Error> {
99        if coin_type >= (1 << 31) {
100            return Err(zip32::Error::InvalidChildIndex(coin_type));
101        }
102
103        // Call zip32 logic
104        let path = &[
105            ChildIndex::hardened(ZIP32_PURPOSE),
106            ChildIndex::hardened(coin_type),
107            ChildIndex::hardened(account.into()),
108        ];
109        ExtendedSpendingKey::from_path(seed, path).map(|esk| esk.sk())
110    }
111}
112
113/// A spend authorizing key, used to create spend authorization signatures.
114/// This type enforces that the corresponding public point (ak^ℙ) has ỹ = 0.
115///
116/// $\mathsf{ask}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
117///
118/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
119#[derive(Clone, Debug)]
120pub struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
121
122impl SpendAuthorizingKey {
123    /// Derives ask from sk. Internal use only, does not enforce all constraints.
124    fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
125        to_scalar(PrfExpand::ORCHARD_ASK.with(&sk.0))
126    }
127
128    /// Randomizes this spend authorizing key with the given `randomizer`.
129    ///
130    /// The resulting key can be used to actually sign a spend.
131    pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::SigningKey<SpendAuth> {
132        self.0.randomize(randomizer)
133    }
134}
135
136impl From<&SpendingKey> for SpendAuthorizingKey {
137    fn from(sk: &SpendingKey) -> Self {
138        let ask = Self::derive_inner(sk);
139        // SpendingKey cannot be constructed such that this assertion would fail.
140        assert!(!bool::from(ask.is_zero()));
141        // TODO: Add TryFrom<S::Scalar> for SpendAuthorizingKey.
142        let ret = SpendAuthorizingKey(ask.to_repr().try_into().unwrap());
143        // If the last bit of repr_P(ak) is 1, negate ask.
144        if (<[u8; 32]>::from(SpendValidatingKey::from(&ret).0)[31] >> 7) == 1 {
145            SpendAuthorizingKey((-ask).to_repr().try_into().unwrap())
146        } else {
147            ret
148        }
149    }
150}
151
152/// A key used to validate spend authorization signatures.
153///
154/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
155/// Note that this is $\mathsf{ak}^\mathbb{P}$, which by construction is equivalent to
156/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
157///
158/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
159#[derive(Debug, Clone, PartialOrd, Ord)]
160pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
161
162impl From<&SpendAuthorizingKey> for SpendValidatingKey {
163    fn from(ask: &SpendAuthorizingKey) -> Self {
164        SpendValidatingKey((&ask.0).into())
165    }
166}
167
168impl From<&SpendValidatingKey> for pallas::Point {
169    fn from(spend_validating_key: &SpendValidatingKey) -> pallas::Point {
170        pallas::Point::from_bytes(&(&spend_validating_key.0).into()).unwrap()
171    }
172}
173
174impl PartialEq for SpendValidatingKey {
175    fn eq(&self, other: &Self) -> bool {
176        <[u8; 32]>::from(&self.0).eq(&<[u8; 32]>::from(&other.0))
177    }
178}
179
180impl Eq for SpendValidatingKey {}
181
182impl SpendValidatingKey {
183    /// Randomizes this spend validating key with the given `randomizer`.
184    pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::VerificationKey<SpendAuth> {
185        self.0.randomize(randomizer)
186    }
187
188    /// Converts this spend validating key to its serialized form,
189    /// I2LEOSP_256(ak).
190    #[cfg_attr(feature = "unstable-frost", visibility::make(pub))]
191    pub(crate) fn to_bytes(&self) -> [u8; 32] {
192        // This is correct because the wrapped point must have ỹ = 0, and
193        // so the point repr is the same as I2LEOSP of its x-coordinate.
194        let b = <[u8; 32]>::from(&self.0);
195        assert!(b[31] & 0x80 == 0);
196        b
197    }
198
199    /// Attempts to parse a byte slice as a spend validating key, `I2LEOSP_256(ak)`.
200    ///
201    /// Returns `None` if the given slice does not contain a valid spend validating key.
202    #[cfg_attr(feature = "unstable-frost", visibility::make(pub))]
203    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
204        <[u8; 32]>::try_from(bytes)
205            .ok()
206            .and_then(|b| {
207                // Structural validity checks for ak_P:
208                // - The point must not be the identity
209                //   (which for Pallas is canonically encoded as all-zeroes).
210                // - The sign of the y-coordinate must be positive.
211                if b != [0; 32] && b[31] & 0x80 == 0 {
212                    <redpallas::VerificationKey<SpendAuth>>::try_from(b).ok()
213                } else {
214                    None
215                }
216            })
217            .map(SpendValidatingKey)
218    }
219}
220
221/// A key used to derive [`Nullifier`]s from [`Note`]s.
222///
223/// $\mathsf{nk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
224///
225/// [`Nullifier`]: crate::note::Nullifier
226/// [`Note`]: crate::note::Note
227/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
228#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
229pub(crate) struct NullifierDerivingKey(pallas::Base);
230
231impl NullifierDerivingKey {
232    pub(crate) fn inner(&self) -> pallas::Base {
233        self.0
234    }
235}
236
237impl From<&SpendingKey> for NullifierDerivingKey {
238    fn from(sk: &SpendingKey) -> Self {
239        NullifierDerivingKey(to_base(PrfExpand::ORCHARD_NK.with(&sk.0)))
240    }
241}
242
243impl NullifierDerivingKey {
244    pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
245        prf_nf(self.0, rho)
246    }
247
248    /// Converts this nullifier deriving key to its serialized form.
249    pub(crate) fn to_bytes(self) -> [u8; 32] {
250        <[u8; 32]>::from(self.0)
251    }
252
253    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
254        let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
255        let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey);
256        if nk.is_some().into() {
257            Some(nk.unwrap())
258        } else {
259            None
260        }
261    }
262}
263
264/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
265///
266/// $\mashsf{rivk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
267///
268/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
269#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
270pub(crate) struct CommitIvkRandomness(pallas::Scalar);
271
272impl From<&SpendingKey> for CommitIvkRandomness {
273    fn from(sk: &SpendingKey) -> Self {
274        CommitIvkRandomness(to_scalar(PrfExpand::ORCHARD_RIVK.with(&sk.0)))
275    }
276}
277
278impl CommitIvkRandomness {
279    pub(crate) fn inner(&self) -> pallas::Scalar {
280        self.0
281    }
282
283    /// Converts this nullifier deriving key to its serialized form.
284    pub(crate) fn to_bytes(self) -> [u8; 32] {
285        <[u8; 32]>::from(self.0)
286    }
287
288    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
289        let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
290        let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness);
291        if rivk.is_some().into() {
292            Some(rivk.unwrap())
293        } else {
294            None
295        }
296    }
297}
298
299/// A key that provides the capability to view incoming and outgoing transactions.
300///
301/// This key is useful anywhere you need to maintain accurate balance, but do not want the
302/// ability to spend funds (such as a view-only wallet).
303///
304/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
305///
306/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
307#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
308pub struct FullViewingKey {
309    ak: SpendValidatingKey,
310    nk: NullifierDerivingKey,
311    rivk: CommitIvkRandomness,
312}
313
314impl From<&SpendingKey> for FullViewingKey {
315    fn from(sk: &SpendingKey) -> Self {
316        FullViewingKey {
317            ak: (&SpendAuthorizingKey::from(sk)).into(),
318            nk: sk.into(),
319            rivk: sk.into(),
320        }
321    }
322}
323
324impl From<&ExtendedSpendingKey> for FullViewingKey {
325    fn from(extsk: &ExtendedSpendingKey) -> Self {
326        (&extsk.sk()).into()
327    }
328}
329
330impl From<FullViewingKey> for SpendValidatingKey {
331    fn from(fvk: FullViewingKey) -> Self {
332        fvk.ak
333    }
334}
335
336impl FullViewingKey {
337    pub(crate) fn nk(&self) -> &NullifierDerivingKey {
338        &self.nk
339    }
340
341    /// Returns either `rivk` or `rivk_internal` based on `scope`.
342    pub(crate) fn rivk(&self, scope: Scope) -> CommitIvkRandomness {
343        match scope {
344            Scope::External => self.rivk,
345            Scope::Internal => {
346                let k = self.rivk.0.to_repr();
347                let ak = self.ak.to_bytes();
348                let nk = self.nk.to_bytes();
349                CommitIvkRandomness(to_scalar(
350                    PrfExpand::ORCHARD_RIVK_INTERNAL.with(&k, &ak, &nk),
351                ))
352            }
353        }
354    }
355
356    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
357    ///
358    /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
359    fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
360        let k = self.rivk.0.to_repr();
361        let b = [(&self.ak.0).into(), self.nk.0.to_repr()];
362        let r = PrfExpand::ORCHARD_DK_OVK.with(&k, &b[0], &b[1]);
363        (
364            DiversifierKey(r[..32].try_into().unwrap()),
365            OutgoingViewingKey(r[32..].try_into().unwrap()),
366        )
367    }
368
369    /// Returns the payment address for this key at the given index.
370    pub fn address_at(&self, j: impl Into<DiversifierIndex>, scope: Scope) -> Address {
371        self.to_ivk(scope).address_at(j)
372    }
373
374    /// Returns the payment address for this key corresponding to the given diversifier.
375    pub fn address(&self, d: Diversifier, scope: Scope) -> Address {
376        // Shortcut: we don't need to derive DiversifierKey.
377        match scope {
378            Scope::External => KeyAgreementPrivateKey::from_fvk(self),
379            Scope::Internal => KeyAgreementPrivateKey::from_fvk(&self.derive_internal()),
380        }
381        .address(d)
382    }
383
384    /// Returns the scope of the given address, or `None` if the address is not derived
385    /// from this full viewing key.
386    pub fn scope_for_address(&self, address: &Address) -> Option<Scope> {
387        [Scope::External, Scope::Internal]
388            .into_iter()
389            .find(|scope| self.to_ivk(*scope).diversifier_index(address).is_some())
390    }
391
392    /// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
393    ///
394    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
395    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
396        writer.write_all(&self.to_bytes())
397    }
398
399    /// Parses a full viewing key from its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
400    ///
401    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
402    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
403        let mut data = [0u8; 96];
404        reader.read_exact(&mut data)?;
405
406        Self::from_bytes(&data).ok_or_else(|| {
407            io::Error::new(
408                io::ErrorKind::InvalidInput,
409                "Unable to deserialize a valid Orchard FullViewingKey from bytes",
410            )
411        })
412    }
413
414    /// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
415    ///
416    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
417    pub fn to_bytes(&self) -> [u8; 96] {
418        let mut result = [0u8; 96];
419        result[0..32].copy_from_slice(&<[u8; 32]>::from(self.ak.0.clone()));
420        result[32..64].copy_from_slice(&self.nk.0.to_repr());
421        result[64..96].copy_from_slice(&self.rivk.0.to_repr());
422        result
423    }
424
425    /// Parses a full viewing key from its "raw" encoding as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
426    ///
427    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
428    pub fn from_bytes(bytes: &[u8; 96]) -> Option<Self> {
429        let ak = SpendValidatingKey::from_bytes(&bytes[..32])?;
430        let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
431        let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
432
433        let fvk = FullViewingKey { ak, nk, rivk };
434
435        // If either ivk is 0 or ⊥, this FVK is invalid.
436        let _: NonZeroPallasBase = Option::from(KeyAgreementPrivateKey::derive_inner(&fvk))?;
437        let _: NonZeroPallasBase =
438            Option::from(KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal()))?;
439
440        Some(fvk)
441    }
442
443    /// Derives an internal full viewing key from a full viewing key, as specified in
444    /// [ZIP32][orchardinternalfullviewingkey]. Internal use only.
445    ///
446    /// [orchardinternalfullviewingkey]: https://zips.z.cash/zip-0032#orchard-internal-key-derivation
447    fn derive_internal(&self) -> Self {
448        FullViewingKey {
449            ak: self.ak.clone(),
450            nk: self.nk,
451            rivk: self.rivk(Scope::Internal),
452        }
453    }
454
455    /// Derives an `IncomingViewingKey` for this full viewing key.
456    pub fn to_ivk(&self, scope: Scope) -> IncomingViewingKey {
457        match scope {
458            Scope::External => IncomingViewingKey::from_fvk(self),
459            Scope::Internal => IncomingViewingKey::from_fvk(&self.derive_internal()),
460        }
461    }
462
463    /// Derives an `OutgoingViewingKey` for this full viewing key.
464    pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
465        match scope {
466            Scope::External => OutgoingViewingKey::from_fvk(self),
467            Scope::Internal => OutgoingViewingKey::from_fvk(&self.derive_internal()),
468        }
469    }
470}
471
472/// A key that provides the capability to derive a sequence of diversifiers.
473///
474/// $\mathsf{dk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
475///
476/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
477#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
478pub(crate) struct DiversifierKey([u8; 32]);
479
480impl DiversifierKey {
481    /// Returns the diversifier at the given index.
482    pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
483        let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
484        let enc = ff
485            .encrypt(
486                &[],
487                &BinaryNumeralString::from_bytes_le(j.into().as_bytes()),
488            )
489            .unwrap();
490        Diversifier(enc.to_bytes_le().try_into().unwrap())
491    }
492
493    /// Returns the diversifier index obtained by decrypting the diversifier.
494    pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
495        let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
496        let dec = ff
497            .decrypt(&[], &BinaryNumeralString::from_bytes_le(d.as_array()))
498            .unwrap();
499        DiversifierIndex::from(<[u8; 11]>::try_from(dec.to_bytes_le()).unwrap())
500    }
501
502    /// Return the raw bytes of the diversifier key
503    pub fn to_bytes(&self) -> &[u8; 32] {
504        &self.0
505    }
506
507    /// Construct a diversifier key from bytes
508    pub fn from_bytes(bytes: [u8; 32]) -> Self {
509        DiversifierKey(bytes)
510    }
511}
512
513/// A diversifier that can be used to derive a specific [`Address`] from a
514/// [`FullViewingKey`] or [`IncomingViewingKey`].
515///
516/// $\mathsf{d}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
517///
518/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
519#[derive(Clone, Copy, Debug, PartialEq, Eq)]
520pub struct Diversifier([u8; 11]);
521
522impl Diversifier {
523    /// Reads a diversifier from a byte array.
524    pub fn from_bytes(d: [u8; 11]) -> Self {
525        Diversifier(d)
526    }
527
528    /// Returns the byte array corresponding to this diversifier.
529    pub fn as_array(&self) -> &[u8; 11] {
530        &self.0
531    }
532}
533
534/// The private key $\mathsf{ivk}$ used in $KA^{Orchard}$, for decrypting incoming notes.
535///
536/// In Sapling this is what was encoded as an incoming viewing key. For Orchard, we store
537/// both this and [`DiversifierKey`] inside [`IncomingViewingKey`] for usability (to
538/// enable deriving the default address for an incoming viewing key), while this separate
539/// type represents $\mathsf{ivk}$.
540///
541/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
542///
543/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
544///
545/// # Implementation notes
546///
547/// We store $\mathsf{ivk}$ in memory as a scalar instead of a base, so that we aren't
548/// incurring an expensive serialize-and-parse step every time we use it (e.g. for trial
549/// decryption of notes). When we actually want to serialize ivk, we're guaranteed to get
550/// a valid base field element encoding, because we always construct ivk from an integer
551/// in the correct range.
552#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
553struct KeyAgreementPrivateKey(NonZeroPallasScalar);
554
555impl KeyAgreementPrivateKey {
556    /// Derives `KeyAgreementPrivateKey` from fvk.
557    ///
558    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
559    ///
560    /// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
561    fn from_fvk(fvk: &FullViewingKey) -> Self {
562        // FullViewingKey cannot be constructed such that this unwrap would fail.
563        let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
564        KeyAgreementPrivateKey(ivk.into())
565    }
566}
567
568impl KeyAgreementPrivateKey {
569    /// Derives ivk from fvk. Internal use only, does not enforce all constraints.
570    ///
571    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
572    ///
573    /// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
574    fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
575        let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
576        commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
577            // sinsemilla::CommitDomain::short_commit returns a value in range
578            // [0..q_P] ∪ {⊥}:
579            // - sinsemilla::HashDomain::hash_to_point uses incomplete addition and
580            //   returns a point in P* ∪ {⊥}.
581            // - sinsemilla::CommitDomain::commit applies a final complete addition step
582            //   and returns a point in P ∪ {⊥}.
583            // - 0 is not a valid x-coordinate for any Pallas point.
584            // - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which
585            //   replaces the identity (which has no affine coordinates) with 0.
586            //
587            // Commit^ivk.Output is specified as [1..q_P] ∪ {⊥}, so we explicitly check
588            // for 0 and map it to None. Note that we are collapsing this case (which is
589            // rejected by the circuit) with ⊥ (which the circuit explicitly allows for
590            // efficiency); this is fine because we don't want users of the `orchard`
591            // crate to encounter either case (and it matches the behaviour described in
592            // Section 4.2.3 of the protocol spec when generating spending keys).
593            .and_then(NonZeroPallasBase::from_base)
594    }
595
596    /// Returns the payment address for this key corresponding to the given diversifier.
597    fn address(&self, d: Diversifier) -> Address {
598        let prepared_ivk = PreparedIncomingViewingKey::new_inner(self);
599        let pk_d = DiversifiedTransmissionKey::derive(&prepared_ivk, &d);
600        Address::from_parts(d, pk_d)
601    }
602}
603
604/// A key that provides the capability to detect and decrypt incoming notes from the block
605/// chain, without being able to spend the notes or detect when they are spent.
606///
607/// This key is useful in situations where you only need the capability to detect inbound
608/// payments, such as merchant terminals.
609///
610/// This key is not suitable for use on its own in a wallet, as it cannot maintain
611/// accurate balance. You should use a [`FullViewingKey`] instead.
612///
613/// Defined in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardinviewingkeyencoding].
614///
615/// [orchardinviewingkeyencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardinviewingkeyencoding
616#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
617pub struct IncomingViewingKey {
618    dk: DiversifierKey,
619    ivk: KeyAgreementPrivateKey,
620}
621
622impl IncomingViewingKey {
623    /// Helper method.
624    fn from_fvk(fvk: &FullViewingKey) -> Self {
625        IncomingViewingKey {
626            dk: fvk.derive_dk_ovk().0,
627            ivk: KeyAgreementPrivateKey::from_fvk(fvk),
628        }
629    }
630}
631
632impl IncomingViewingKey {
633    /// Serializes an Orchard incoming viewing key to its raw encoding as specified in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardrawinviewingkeys]
634    ///
635    /// [orchardrawinviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardinviewingkeyencoding
636    pub fn to_bytes(&self) -> [u8; 64] {
637        let mut result = [0u8; 64];
638        result[..32].copy_from_slice(self.dk.to_bytes());
639        result[32..].copy_from_slice(&self.ivk.0.to_repr());
640        result
641    }
642
643    /// Parses an Orchard incoming viewing key from its raw encoding.
644    pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
645        NonZeroPallasBase::from_bytes(bytes[32..].try_into().unwrap()).map(|ivk| {
646            IncomingViewingKey {
647                dk: DiversifierKey(bytes[..32].try_into().unwrap()),
648                ivk: KeyAgreementPrivateKey(ivk.into()),
649            }
650        })
651    }
652
653    /// Checks whether the given address was derived from this incoming viewing
654    /// key, and returns the diversifier index used to derive the address if
655    /// so. Returns `None` if the address was not derived from this key.
656    pub fn diversifier_index(&self, addr: &Address) -> Option<DiversifierIndex> {
657        let j = self.dk.diversifier_index(&addr.diversifier());
658        if &self.address_at(j) == addr {
659            Some(j)
660        } else {
661            None
662        }
663    }
664
665    /// Returns the payment address for this key at the given index.
666    pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
667        self.address(self.dk.get(j))
668    }
669
670    /// Returns the payment address for this key corresponding to the given diversifier.
671    pub fn address(&self, d: Diversifier) -> Address {
672        self.ivk.address(d)
673    }
674
675    /// Returns the [`PreparedIncomingViewingKey`] for this [`IncomingViewingKey`].
676    pub fn prepare(&self) -> PreparedIncomingViewingKey {
677        PreparedIncomingViewingKey::new(self)
678    }
679}
680
681/// An Orchard incoming viewing key that has been precomputed for trial decryption.
682#[derive(Clone, Debug)]
683pub struct PreparedIncomingViewingKey(PreparedNonZeroScalar);
684
685#[cfg(feature = "std")]
686impl memuse::DynamicUsage for PreparedIncomingViewingKey {
687    fn dynamic_usage(&self) -> usize {
688        self.0.dynamic_usage()
689    }
690
691    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
692        self.0.dynamic_usage_bounds()
693    }
694}
695
696impl PreparedIncomingViewingKey {
697    /// Performs the necessary precomputations to use an `IncomingViewingKey` for note
698    /// decryption.
699    pub fn new(ivk: &IncomingViewingKey) -> Self {
700        Self::new_inner(&ivk.ivk)
701    }
702
703    fn new_inner(ivk: &KeyAgreementPrivateKey) -> Self {
704        Self(PreparedNonZeroScalar::new(&ivk.0))
705    }
706}
707
708/// A key that provides the capability to recover outgoing transaction information from
709/// the block chain.
710///
711/// This key is not suitable for use on its own in a wallet, as it cannot maintain
712/// accurate balance. You should use a [`FullViewingKey`] instead.
713///
714/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
715///
716/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
717#[derive(Debug, Clone)]
718pub struct OutgoingViewingKey([u8; 32]);
719
720impl OutgoingViewingKey {
721    /// Helper method.
722    fn from_fvk(fvk: &FullViewingKey) -> Self {
723        fvk.derive_dk_ovk().1
724    }
725}
726
727impl From<[u8; 32]> for OutgoingViewingKey {
728    fn from(ovk: [u8; 32]) -> Self {
729        OutgoingViewingKey(ovk)
730    }
731}
732
733impl AsRef<[u8; 32]> for OutgoingViewingKey {
734    fn as_ref(&self) -> &[u8; 32] {
735        &self.0
736    }
737}
738
739/// The diversified transmission key for a given payment address.
740///
741/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
742///
743/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
744#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
745pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
746
747impl DiversifiedTransmissionKey {
748    pub(crate) fn inner(&self) -> NonIdentityPallasPoint {
749        self.0
750    }
751}
752
753impl DiversifiedTransmissionKey {
754    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
755    ///
756    /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
757    pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self {
758        let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array()));
759        DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d))
760    }
761
762    /// $abst_P(bytes)$
763    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
764        NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
765    }
766
767    /// $repr_P(self)$
768    pub(crate) fn to_bytes(self) -> [u8; 32] {
769        self.0.to_bytes()
770    }
771}
772
773impl ConditionallySelectable for DiversifiedTransmissionKey {
774    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
775        DiversifiedTransmissionKey(NonIdentityPallasPoint::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{Orchard}.\mathsf{Private} := \mathbb{F}^{\ast}_{r_P}$
787///
788/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
789///
790/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
791#[derive(Debug)]
792pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar);
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        NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey)
803    }
804
805    pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey {
806        EphemeralPublicKey(ka_orchard(&self.0, &g_d))
807    }
808
809    pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
810        SharedSecret(ka_orchard(&self.0, &pk_d.0))
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{Orchard}.\mathsf{Public} := \mathbb{P}^{\ast}$
820///
821/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
822///
823/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
824#[derive(Debug)]
825pub struct EphemeralPublicKey(NonIdentityPallasPoint);
826
827impl EphemeralPublicKey {
828    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
829        NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey)
830    }
831
832    pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
833        EphemeralKeyBytes(self.0.to_bytes())
834    }
835
836    pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret {
837        SharedSecret(ka_orchard(&ivk.ivk.0, &self.0))
838    }
839}
840
841/// An Orchard ephemeral public key that has been precomputed for trial decryption.
842#[derive(Clone, Debug)]
843pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase);
844
845impl PreparedEphemeralPublicKey {
846    pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
847        PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0))
848    }
849
850    pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
851        SharedSecret(ka_orchard_prepared(&ivk.0, &self.0))
852    }
853}
854
855/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{SharedSecret} := \mathbb{P}^{\ast}$
856///
857/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
858///
859/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
860#[derive(Debug)]
861pub struct SharedSecret(NonIdentityPallasPoint);
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<pallas::Affine>> {
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| *(s.0)))
878            .collect();
879
880        // Batch-normalize the shared secrets.
881        let mut secrets_affine = vec![pallas::Affine::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.6: Orchard Key Agreement][concreteorchardkdf].
892    ///
893    /// [concreteorchardkdf]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkdf
894    pub(crate) fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
895        Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key)
896    }
897
898    /// Only for direct use in batched note encryption.
899    pub(crate) fn kdf_orchard_inner(
900        secret: pallas::Affine,
901        ephemeral_key: &EphemeralKeyBytes,
902    ) -> Blake2bHash {
903        Params::new()
904            .hash_length(32)
905            .personal(KDF_ORCHARD_PERSONALIZATION)
906            .to_state()
907            .update(&secret.to_bytes())
908            .update(&ephemeral_key.0)
909            .finalize()
910    }
911}
912
913/// Generators for property testing.
914#[cfg(any(test, feature = "test-dependencies"))]
915#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
916pub mod testing {
917    use proptest::prelude::*;
918
919    use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey};
920
921    prop_compose! {
922        /// Generate a uniformly distributed Orchard spending key.
923        pub fn arb_spending_key()(
924            key in prop::array::uniform32(prop::num::u8::ANY)
925                .prop_map(SpendingKey::from_bytes)
926                .prop_filter(
927                    "Values must correspond to valid Orchard spending keys.",
928                    |opt| bool::from(opt.is_some())
929                )
930        ) -> SpendingKey {
931            key.unwrap()
932        }
933    }
934
935    prop_compose! {
936        /// Generate a uniformly distributed Orchard ephemeral secret key.
937        pub fn arb_esk()(
938            esk in prop::array::uniform32(prop::num::u8::ANY)
939                .prop_map(|b| EphemeralSecretKey::from_bytes(&b))
940                .prop_filter(
941                    "Values must correspond to valid Orchard ephemeral secret keys.",
942                    |opt| bool::from(opt.is_some())
943                )
944        ) -> EphemeralSecretKey {
945            esk.unwrap()
946        }
947    }
948
949    prop_compose! {
950        /// Generate a uniformly distributed Orchard diversifier key.
951        pub(crate) fn arb_diversifier_key()(
952            dk_bytes in prop::array::uniform32(prop::num::u8::ANY)
953        ) -> DiversifierKey {
954            DiversifierKey::from_bytes(dk_bytes)
955        }
956    }
957
958    prop_compose! {
959        /// Generate a uniformly distributed diversifier index.
960        pub fn arb_diversifier_index()(
961            d_bytes in prop::array::uniform11(prop::num::u8::ANY)
962        ) -> DiversifierIndex {
963            DiversifierIndex::from(d_bytes)
964        }
965    }
966}
967
968#[cfg(test)]
969mod tests {
970    use ff::PrimeField;
971    use proptest::prelude::*;
972
973    use super::{
974        testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key},
975        *,
976    };
977    use crate::{
978        note::{ExtractedNoteCommitment, RandomSeed, Rho},
979        value::NoteValue,
980        Note,
981    };
982
983    #[test]
984    fn spend_validating_key_from_bytes() {
985        // ak_P must not be the identity.
986        assert!(SpendValidatingKey::from_bytes(&[0; 32]).is_none());
987    }
988
989    #[test]
990    fn parsers_reject_invalid() {
991        assert!(bool::from(
992            EphemeralSecretKey::from_bytes(&[0xff; 32]).is_none()
993        ));
994        assert!(bool::from(
995            EphemeralPublicKey::from_bytes(&[0xff; 32]).is_none()
996        ));
997    }
998
999    proptest! {
1000        #[test]
1001        fn key_agreement(
1002            sk in arb_spending_key(),
1003            esk in arb_esk(),
1004            j in arb_diversifier_index(),
1005        ) {
1006            let ivk = IncomingViewingKey::from_fvk(&(&sk).into());
1007            let addr = ivk.address_at(j);
1008
1009            let epk = esk.derive_public(addr.g_d());
1010
1011            assert!(bool::from(
1012                esk.agree(addr.pk_d()).0.ct_eq(&epk.agree(&ivk).0)
1013            ));
1014        }
1015    }
1016
1017    proptest! {
1018        #[test]
1019        fn diversifier_index(
1020            dk in arb_diversifier_key(),
1021            j in arb_diversifier_index(),
1022        ) {
1023            let d = dk.get(j);
1024            assert_eq!(j, dk.diversifier_index(&d));
1025        }
1026    }
1027
1028    #[test]
1029    fn test_vectors() {
1030        for tv in crate::test_vectors::keys::test_vectors() {
1031            let sk = SpendingKey::from_bytes(tv.sk).unwrap();
1032
1033            let ask: SpendAuthorizingKey = (&sk).into();
1034            assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask);
1035
1036            let ak: SpendValidatingKey = (&ask).into();
1037            assert_eq!(<[u8; 32]>::from(ak.0), tv.ak);
1038
1039            let nk: NullifierDerivingKey = (&sk).into();
1040            assert_eq!(nk.0.to_repr(), tv.nk);
1041
1042            let rivk: CommitIvkRandomness = (&sk).into();
1043            assert_eq!(rivk.0.to_repr(), tv.rivk);
1044
1045            let fvk: FullViewingKey = (&sk).into();
1046            assert_eq!(<[u8; 32]>::from(&fvk.ak.0), tv.ak);
1047            assert_eq!(fvk.nk().0.to_repr(), tv.nk);
1048            assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
1049
1050            let external_ivk = fvk.to_ivk(Scope::External);
1051            assert_eq!(external_ivk.ivk.0.to_repr(), tv.ivk);
1052
1053            let diversifier = Diversifier(tv.default_d);
1054
1055            let addr = fvk.address(diversifier, Scope::External);
1056            assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
1057
1058            let rho = Rho::from_bytes(&tv.note_rho).unwrap();
1059            let note = Note::from_parts(
1060                addr,
1061                NoteValue::from_raw(tv.note_v),
1062                rho,
1063                RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
1064            )
1065            .unwrap();
1066
1067            let cmx: ExtractedNoteCommitment = note.commitment().into();
1068            assert_eq!(cmx.to_bytes(), tv.note_cmx);
1069
1070            assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
1071
1072            let internal_rivk = fvk.rivk(Scope::Internal);
1073            assert_eq!(internal_rivk.0.to_repr(), tv.internal_rivk);
1074
1075            let internal_ivk = fvk.to_ivk(Scope::Internal);
1076            assert_eq!(internal_ivk.ivk.0.to_repr(), tv.internal_ivk);
1077            assert_eq!(internal_ivk.dk.0, tv.internal_dk);
1078
1079            let internal_ovk = fvk.to_ovk(Scope::Internal);
1080            assert_eq!(internal_ovk.0, tv.internal_ovk);
1081        }
1082    }
1083}