Skip to main content

orchard/
keys.rs

1//! Key structures for Orchard.
2
3use alloc::vec::Vec;
4use corez::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 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    pub 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 struct NullifierDerivingKey(pallas::Base);
230
231impl NullifierDerivingKey {
232    /// Returns the inner base field element.
233    pub fn inner(&self) -> pallas::Base {
234        self.0
235    }
236}
237
238impl From<&SpendingKey> for NullifierDerivingKey {
239    fn from(sk: &SpendingKey) -> Self {
240        NullifierDerivingKey(to_base(PrfExpand::ORCHARD_NK.with(&sk.0)))
241    }
242}
243
244impl NullifierDerivingKey {
245    /// Computes PRF^nf on the given rho value.
246    pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base {
247        prf_nf(self.0, rho)
248    }
249
250    /// Converts this nullifier deriving key to its serialized form.
251    pub(crate) fn to_bytes(self) -> [u8; 32] {
252        <[u8; 32]>::from(self.0)
253    }
254
255    /// Parses a nullifier deriving key from a byte slice.
256    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
257        let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
258        let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey);
259        if nk.is_some().into() {
260            Some(nk.unwrap())
261        } else {
262            None
263        }
264    }
265}
266
267/// The randomness for $\mathsf{Commit}^\mathsf{ivk}$.
268///
269/// $\mashsf{rivk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
270///
271/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
272#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
273pub struct CommitIvkRandomness(pallas::Scalar);
274
275impl From<&SpendingKey> for CommitIvkRandomness {
276    fn from(sk: &SpendingKey) -> Self {
277        CommitIvkRandomness(to_scalar(PrfExpand::ORCHARD_RIVK.with(&sk.0)))
278    }
279}
280
281impl CommitIvkRandomness {
282    /// Returns the inner scalar value.
283    pub fn inner(&self) -> pallas::Scalar {
284        self.0
285    }
286
287    /// Converts this commit-ivk randomness to its serialized form.
288    pub(crate) fn to_bytes(self) -> [u8; 32] {
289        <[u8; 32]>::from(self.0)
290    }
291
292    /// Parses commit-ivk randomness from a byte slice.
293    pub(crate) fn from_bytes(bytes: &[u8]) -> Option<Self> {
294        let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?;
295        let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness);
296        if rivk.is_some().into() {
297            Some(rivk.unwrap())
298        } else {
299            None
300        }
301    }
302}
303
304/// A key that provides the capability to view incoming and outgoing transactions.
305///
306/// This key is useful anywhere you need to maintain accurate balance, but do not want the
307/// ability to spend funds (such as a view-only wallet).
308///
309/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
310///
311/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
312#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
313pub struct FullViewingKey {
314    ak: SpendValidatingKey,
315    nk: NullifierDerivingKey,
316    rivk: CommitIvkRandomness,
317}
318
319impl From<&SpendingKey> for FullViewingKey {
320    fn from(sk: &SpendingKey) -> Self {
321        FullViewingKey {
322            ak: (&SpendAuthorizingKey::from(sk)).into(),
323            nk: sk.into(),
324            rivk: sk.into(),
325        }
326    }
327}
328
329impl From<&ExtendedSpendingKey> for FullViewingKey {
330    fn from(extsk: &ExtendedSpendingKey) -> Self {
331        (&extsk.sk()).into()
332    }
333}
334
335impl From<FullViewingKey> for SpendValidatingKey {
336    fn from(fvk: FullViewingKey) -> Self {
337        fvk.ak
338    }
339}
340
341impl FullViewingKey {
342    /// Returns the nullifier deriving key for this full viewing key.
343    pub fn nk(&self) -> &NullifierDerivingKey {
344        &self.nk
345    }
346
347    /// Returns either `rivk` or `rivk_internal` based on `scope`.
348    pub fn rivk(&self, scope: Scope) -> CommitIvkRandomness {
349        match scope {
350            Scope::External => self.rivk,
351            Scope::Internal => {
352                let k = self.rivk.0.to_repr();
353                let ak = self.ak.to_bytes();
354                let nk = self.nk.to_bytes();
355                CommitIvkRandomness(to_scalar(
356                    PrfExpand::ORCHARD_RIVK_INTERNAL.with(&k, &ak, &nk),
357                ))
358            }
359        }
360    }
361
362    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
363    ///
364    /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
365    fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) {
366        let k = self.rivk.0.to_repr();
367        let b = [(&self.ak.0).into(), self.nk.0.to_repr()];
368        let r = PrfExpand::ORCHARD_DK_OVK.with(&k, &b[0], &b[1]);
369        (
370            DiversifierKey(r[..32].try_into().unwrap()),
371            OutgoingViewingKey(r[32..].try_into().unwrap()),
372        )
373    }
374
375    /// Returns the payment address for this key at the given index.
376    pub fn address_at(&self, j: impl Into<DiversifierIndex>, scope: Scope) -> Address {
377        self.to_ivk(scope).address_at(j)
378    }
379
380    /// Returns the payment address for this key corresponding to the given diversifier.
381    pub fn address(&self, d: Diversifier, scope: Scope) -> Address {
382        // Shortcut: we don't need to derive DiversifierKey.
383        match scope {
384            Scope::External => KeyAgreementPrivateKey::from_fvk(self),
385            Scope::Internal => KeyAgreementPrivateKey::from_fvk(&self.derive_internal()),
386        }
387        .address(d)
388    }
389
390    /// Returns the scope of the given address, or `None` if the address is not derived
391    /// from this full viewing key.
392    pub fn scope_for_address(&self, address: &Address) -> Option<Scope> {
393        [Scope::External, Scope::Internal]
394            .into_iter()
395            .find(|scope| self.to_ivk(*scope).diversifier_index(address).is_some())
396    }
397
398    /// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
399    ///
400    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
401    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
402        writer.write_all(&self.to_bytes())
403    }
404
405    /// 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]
406    ///
407    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
408    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
409        let mut data = [0u8; 96];
410        reader.read_exact(&mut data)?;
411
412        Self::from_bytes(&data).ok_or_else(|| {
413            io::Error::new(
414                io::ErrorKind::InvalidInput,
415                "Unable to deserialize a valid Orchard FullViewingKey from bytes",
416            )
417        })
418    }
419
420    /// Serializes the full viewing key as specified in [Zcash Protocol Spec § 5.6.4.4: Orchard Raw Full Viewing Keys][orchardrawfullviewingkeys]
421    ///
422    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
423    pub fn to_bytes(&self) -> [u8; 96] {
424        let mut result = [0u8; 96];
425        result[0..32].copy_from_slice(&<[u8; 32]>::from(self.ak.0.clone()));
426        result[32..64].copy_from_slice(&self.nk.0.to_repr());
427        result[64..96].copy_from_slice(&self.rivk.0.to_repr());
428        result
429    }
430
431    /// 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]
432    ///
433    /// [orchardrawfullviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
434    pub fn from_bytes(bytes: &[u8; 96]) -> Option<Self> {
435        let ak = SpendValidatingKey::from_bytes(&bytes[..32])?;
436        let nk = NullifierDerivingKey::from_bytes(&bytes[32..64])?;
437        let rivk = CommitIvkRandomness::from_bytes(&bytes[64..])?;
438
439        let fvk = FullViewingKey { ak, nk, rivk };
440
441        // If either ivk is 0 or ⊥, this FVK is invalid.
442        let _: NonZeroPallasBase = Option::from(KeyAgreementPrivateKey::derive_inner(&fvk))?;
443        let _: NonZeroPallasBase =
444            Option::from(KeyAgreementPrivateKey::derive_inner(&fvk.derive_internal()))?;
445
446        Some(fvk)
447    }
448
449    /// Derives an internal full viewing key from a full viewing key, as specified in
450    /// [ZIP32][orchardinternalfullviewingkey]. Internal use only.
451    ///
452    /// [orchardinternalfullviewingkey]: https://zips.z.cash/zip-0032#orchard-internal-key-derivation
453    fn derive_internal(&self) -> Self {
454        FullViewingKey {
455            ak: self.ak.clone(),
456            nk: self.nk,
457            rivk: self.rivk(Scope::Internal),
458        }
459    }
460
461    /// Derives an `IncomingViewingKey` for this full viewing key.
462    pub fn to_ivk(&self, scope: Scope) -> IncomingViewingKey {
463        match scope {
464            Scope::External => IncomingViewingKey::from_fvk(self),
465            Scope::Internal => IncomingViewingKey::from_fvk(&self.derive_internal()),
466        }
467    }
468
469    /// Derives an `OutgoingViewingKey` for this full viewing key.
470    pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
471        match scope {
472            Scope::External => OutgoingViewingKey::from_fvk(self),
473            Scope::Internal => OutgoingViewingKey::from_fvk(&self.derive_internal()),
474        }
475    }
476}
477
478/// A key that provides the capability to derive a sequence of diversifiers.
479///
480/// $\mathsf{dk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
481///
482/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
483#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
484pub(crate) struct DiversifierKey([u8; 32]);
485
486impl DiversifierKey {
487    /// Returns the diversifier at the given index.
488    pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
489        let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
490        let enc = ff
491            .encrypt(
492                &[],
493                &BinaryNumeralString::from_bytes_le(j.into().as_bytes()),
494            )
495            .unwrap();
496        Diversifier(enc.to_bytes_le().try_into().unwrap())
497    }
498
499    /// Returns the diversifier index obtained by decrypting the diversifier.
500    pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
501        let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
502        let dec = ff
503            .decrypt(&[], &BinaryNumeralString::from_bytes_le(d.as_array()))
504            .unwrap();
505        DiversifierIndex::from(<[u8; 11]>::try_from(dec.to_bytes_le()).unwrap())
506    }
507
508    /// Return the raw bytes of the diversifier key
509    pub fn to_bytes(&self) -> &[u8; 32] {
510        &self.0
511    }
512
513    /// Construct a diversifier key from bytes
514    pub fn from_bytes(bytes: [u8; 32]) -> Self {
515        DiversifierKey(bytes)
516    }
517}
518
519/// A diversifier that can be used to derive a specific [`Address`] from a
520/// [`FullViewingKey`] or [`IncomingViewingKey`].
521///
522/// $\mathsf{d}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
523///
524/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
525#[derive(Clone, Copy, Debug, PartialEq, Eq)]
526pub struct Diversifier([u8; 11]);
527
528impl Diversifier {
529    /// Reads a diversifier from a byte array.
530    pub fn from_bytes(d: [u8; 11]) -> Self {
531        Diversifier(d)
532    }
533
534    /// Returns the byte array corresponding to this diversifier.
535    pub fn as_array(&self) -> &[u8; 11] {
536        &self.0
537    }
538}
539
540/// The private key $\mathsf{ivk}$ used in $KA^{Orchard}$, for decrypting incoming notes.
541///
542/// In Sapling this is what was encoded as an incoming viewing key. For Orchard, we store
543/// both this and [`DiversifierKey`] inside [`IncomingViewingKey`] for usability (to
544/// enable deriving the default address for an incoming viewing key), while this separate
545/// type represents $\mathsf{ivk}$.
546///
547/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
548///
549/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
550///
551/// # Implementation notes
552///
553/// We store $\mathsf{ivk}$ in memory as a scalar instead of a base, so that we aren't
554/// incurring an expensive serialize-and-parse step every time we use it (e.g. for trial
555/// decryption of notes). When we actually want to serialize ivk, we're guaranteed to get
556/// a valid base field element encoding, because we always construct ivk from an integer
557/// in the correct range.
558#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
559struct KeyAgreementPrivateKey(NonZeroPallasScalar);
560
561impl KeyAgreementPrivateKey {
562    /// Derives `KeyAgreementPrivateKey` from fvk.
563    ///
564    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
565    ///
566    /// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
567    fn from_fvk(fvk: &FullViewingKey) -> Self {
568        // FullViewingKey cannot be constructed such that this unwrap would fail.
569        let ivk = KeyAgreementPrivateKey::derive_inner(fvk).unwrap();
570        KeyAgreementPrivateKey(ivk.into())
571    }
572}
573
574impl KeyAgreementPrivateKey {
575    /// Derives ivk from fvk. Internal use only, does not enforce all constraints.
576    ///
577    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
578    ///
579    /// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
580    fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
581        let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
582        commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
583            // sinsemilla::CommitDomain::short_commit returns a value in range
584            // [0..q_P] ∪ {⊥}:
585            // - sinsemilla::HashDomain::hash_to_point uses incomplete addition and
586            //   returns a point in P* ∪ {⊥}.
587            // - sinsemilla::CommitDomain::commit applies a final complete addition step
588            //   and returns a point in P ∪ {⊥}.
589            // - 0 is not a valid x-coordinate for any Pallas point.
590            // - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which
591            //   replaces the identity (which has no affine coordinates) with 0.
592            //
593            // Commit^ivk.Output is specified as [1..q_P] ∪ {⊥}, so we explicitly check
594            // for 0 and map it to None. Note that we are collapsing this case (which is
595            // rejected by the circuit) with ⊥ (which the circuit explicitly allows for
596            // efficiency); this is fine because we don't want users of the `orchard`
597            // crate to encounter either case (and it matches the behaviour described in
598            // Section 4.2.3 of the protocol spec when generating spending keys).
599            .and_then(NonZeroPallasBase::from_base)
600    }
601
602    /// Returns the payment address for this key corresponding to the given diversifier.
603    fn address(&self, d: Diversifier) -> Address {
604        let prepared_ivk = PreparedIncomingViewingKey::new_inner(self);
605        let pk_d = DiversifiedTransmissionKey::derive(&prepared_ivk, &d);
606        Address::from_parts(d, pk_d)
607    }
608}
609
610/// A key that provides the capability to detect and decrypt incoming notes from the block
611/// chain, without being able to spend the notes or detect when they are spent.
612///
613/// This key is useful in situations where you only need the capability to detect inbound
614/// payments, such as merchant terminals.
615///
616/// This key is not suitable for use on its own in a wallet, as it cannot maintain
617/// accurate balance. You should use a [`FullViewingKey`] instead.
618///
619/// Defined in [Zcash Protocol Spec § 5.6.4.3: Orchard Raw Incoming Viewing Keys][orchardinviewingkeyencoding].
620///
621/// [orchardinviewingkeyencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardinviewingkeyencoding
622#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
623pub struct IncomingViewingKey {
624    dk: DiversifierKey,
625    ivk: KeyAgreementPrivateKey,
626}
627
628impl IncomingViewingKey {
629    /// Helper method.
630    fn from_fvk(fvk: &FullViewingKey) -> Self {
631        IncomingViewingKey {
632            dk: fvk.derive_dk_ovk().0,
633            ivk: KeyAgreementPrivateKey::from_fvk(fvk),
634        }
635    }
636}
637
638impl IncomingViewingKey {
639    /// 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]
640    ///
641    /// [orchardrawinviewingkeys]: https://zips.z.cash/protocol/protocol.pdf#orchardinviewingkeyencoding
642    pub fn to_bytes(&self) -> [u8; 64] {
643        let mut result = [0u8; 64];
644        result[..32].copy_from_slice(self.dk.to_bytes());
645        result[32..].copy_from_slice(&self.ivk.0.to_repr());
646        result
647    }
648
649    /// Parses an Orchard incoming viewing key from its raw encoding.
650    pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
651        NonZeroPallasBase::from_bytes(bytes[32..].try_into().unwrap()).map(|ivk| {
652            IncomingViewingKey {
653                dk: DiversifierKey(bytes[..32].try_into().unwrap()),
654                ivk: KeyAgreementPrivateKey(ivk.into()),
655            }
656        })
657    }
658
659    /// Checks whether the given address was derived from this incoming viewing
660    /// key, and returns the diversifier index used to derive the address if
661    /// so. Returns `None` if the address was not derived from this key.
662    pub fn diversifier_index(&self, addr: &Address) -> Option<DiversifierIndex> {
663        let j = self.dk.diversifier_index(&addr.diversifier());
664        if &self.address_at(j) == addr {
665            Some(j)
666        } else {
667            None
668        }
669    }
670
671    /// Returns the payment address for this key at the given index.
672    pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Address {
673        self.address(self.dk.get(j))
674    }
675
676    /// Returns the payment address for this key corresponding to the given diversifier.
677    pub fn address(&self, d: Diversifier) -> Address {
678        self.ivk.address(d)
679    }
680
681    /// Returns the [`PreparedIncomingViewingKey`] for this [`IncomingViewingKey`].
682    pub fn prepare(&self) -> PreparedIncomingViewingKey {
683        PreparedIncomingViewingKey::new(self)
684    }
685}
686
687/// An Orchard incoming viewing key that has been precomputed for trial decryption.
688#[derive(Clone, Debug)]
689pub struct PreparedIncomingViewingKey(PreparedNonZeroScalar);
690
691#[cfg(feature = "std")]
692impl memuse::DynamicUsage for PreparedIncomingViewingKey {
693    fn dynamic_usage(&self) -> usize {
694        self.0.dynamic_usage()
695    }
696
697    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
698        self.0.dynamic_usage_bounds()
699    }
700}
701
702impl PreparedIncomingViewingKey {
703    /// Performs the necessary precomputations to use an `IncomingViewingKey` for note
704    /// decryption.
705    pub fn new(ivk: &IncomingViewingKey) -> Self {
706        Self::new_inner(&ivk.ivk)
707    }
708
709    fn new_inner(ivk: &KeyAgreementPrivateKey) -> Self {
710        Self(PreparedNonZeroScalar::new(&ivk.0))
711    }
712}
713
714/// A key that provides the capability to recover outgoing transaction information from
715/// the block chain.
716///
717/// This key is not suitable for use on its own in a wallet, as it cannot maintain
718/// accurate balance. You should use a [`FullViewingKey`] instead.
719///
720/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
721///
722/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
723#[derive(Debug, Clone)]
724pub struct OutgoingViewingKey([u8; 32]);
725
726impl OutgoingViewingKey {
727    /// Helper method.
728    fn from_fvk(fvk: &FullViewingKey) -> Self {
729        fvk.derive_dk_ovk().1
730    }
731}
732
733impl From<[u8; 32]> for OutgoingViewingKey {
734    fn from(ovk: [u8; 32]) -> Self {
735        OutgoingViewingKey(ovk)
736    }
737}
738
739impl AsRef<[u8; 32]> for OutgoingViewingKey {
740    fn as_ref(&self) -> &[u8; 32] {
741        &self.0
742    }
743}
744
745/// The diversified transmission key for a given payment address.
746///
747/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
748///
749/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
750#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
751pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint);
752
753impl DiversifiedTransmissionKey {
754    /// Returns the inner `NonIdentityPallasPoint`.
755    pub fn inner(&self) -> NonIdentityPallasPoint {
756        self.0
757    }
758}
759
760impl DiversifiedTransmissionKey {
761    /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
762    ///
763    /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
764    pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self {
765        let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array()));
766        DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d))
767    }
768
769    /// $abst_P(bytes)$
770    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
771        NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey)
772    }
773
774    /// $repr_P(self)$
775    pub fn to_bytes(self) -> [u8; 32] {
776        self.0.to_bytes()
777    }
778}
779
780impl ConditionallySelectable for DiversifiedTransmissionKey {
781    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
782        DiversifiedTransmissionKey(NonIdentityPallasPoint::conditional_select(
783            &a.0, &b.0, choice,
784        ))
785    }
786}
787
788/// An ephemeral secret key used to encrypt an output note on-chain.
789///
790/// `esk` is "ephemeral" in the sense that each secret key is only used once. In
791/// practice, `esk` is derived deterministically from the note that it is encrypting.
792///
793/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{Private} := \mathbb{F}^{\ast}_{r_P}$
794///
795/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
796///
797/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
798#[derive(Debug)]
799pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar);
800
801impl ConstantTimeEq for EphemeralSecretKey {
802    fn ct_eq(&self, other: &Self) -> subtle::Choice {
803        self.0.ct_eq(&other.0)
804    }
805}
806
807impl EphemeralSecretKey {
808    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
809        NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey)
810    }
811
812    pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey {
813        EphemeralPublicKey(ka_orchard(&self.0, &g_d))
814    }
815
816    pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret {
817        SharedSecret(ka_orchard(&self.0, &pk_d.0))
818    }
819}
820
821/// An ephemeral public key used to encrypt an output note on-chain.
822///
823/// `epk` is "ephemeral" in the sense that each public key is only used once. In practice,
824/// `epk` is derived deterministically from the note that it is encrypting.
825///
826/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{Public} := \mathbb{P}^{\ast}$
827///
828/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
829///
830/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
831#[derive(Debug)]
832pub struct EphemeralPublicKey(NonIdentityPallasPoint);
833
834impl EphemeralPublicKey {
835    pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
836        NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey)
837    }
838
839    pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes {
840        EphemeralKeyBytes(self.0.to_bytes())
841    }
842
843    pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret {
844        SharedSecret(ka_orchard(&ivk.ivk.0, &self.0))
845    }
846}
847
848/// An Orchard ephemeral public key that has been precomputed for trial decryption.
849#[derive(Clone, Debug)]
850pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase);
851
852impl PreparedEphemeralPublicKey {
853    pub(crate) fn new(epk: EphemeralPublicKey) -> Self {
854        PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0))
855    }
856
857    pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret {
858        SharedSecret(ka_orchard_prepared(&ivk.0, &self.0))
859    }
860}
861
862/// $\mathsf{KA}^\mathsf{Orchard}.\mathsf{SharedSecret} := \mathbb{P}^{\ast}$
863///
864/// Defined in [section 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
865///
866/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
867#[derive(Debug)]
868pub struct SharedSecret(NonIdentityPallasPoint);
869
870impl SharedSecret {
871    /// For checking test vectors only.
872    #[cfg(test)]
873    pub(crate) fn to_bytes(&self) -> [u8; 32] {
874        self.0.to_bytes()
875    }
876
877    /// Only for use in batched note encryption.
878    pub(crate) fn batch_to_affine(
879        shared_secrets: Vec<Option<Self>>,
880    ) -> impl Iterator<Item = Option<pallas::Affine>> {
881        // Filter out the positions for which ephemeral_key was not a valid encoding.
882        let secrets: Vec<_> = shared_secrets
883            .iter()
884            .filter_map(|s| s.as_ref().map(|s| *(s.0)))
885            .collect();
886
887        // Batch-normalize the shared secrets.
888        let mut secrets_affine = vec![pallas::Affine::identity(); secrets.len()];
889        group::Curve::batch_normalize(&secrets, &mut secrets_affine);
890
891        // Re-insert the invalid ephemeral_key positions.
892        let mut secrets_affine = secrets_affine.into_iter();
893        shared_secrets
894            .into_iter()
895            .map(move |s| s.and_then(|_| secrets_affine.next()))
896    }
897
898    /// Defined in [Zcash Protocol Spec § 5.4.5.6: Orchard Key Agreement][concreteorchardkdf].
899    ///
900    /// [concreteorchardkdf]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkdf
901    pub(crate) fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash {
902        Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key)
903    }
904
905    /// Only for direct use in batched note encryption.
906    pub(crate) fn kdf_orchard_inner(
907        secret: pallas::Affine,
908        ephemeral_key: &EphemeralKeyBytes,
909    ) -> Blake2bHash {
910        Params::new()
911            .hash_length(32)
912            .personal(KDF_ORCHARD_PERSONALIZATION)
913            .to_state()
914            .update(&secret.to_bytes())
915            .update(&ephemeral_key.0)
916            .finalize()
917    }
918}
919
920/// Generators for property testing.
921#[cfg(any(test, feature = "test-dependencies"))]
922#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
923pub mod testing {
924    use proptest::prelude::*;
925
926    use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey};
927
928    prop_compose! {
929        /// Generate a uniformly distributed Orchard spending key.
930        pub fn arb_spending_key()(
931            key in prop::array::uniform32(prop::num::u8::ANY)
932                .prop_map(SpendingKey::from_bytes)
933                .prop_filter(
934                    "Values must correspond to valid Orchard spending keys.",
935                    |opt| bool::from(opt.is_some())
936                )
937        ) -> SpendingKey {
938            key.unwrap()
939        }
940    }
941
942    prop_compose! {
943        /// Generate a uniformly distributed Orchard ephemeral secret key.
944        pub fn arb_esk()(
945            esk in prop::array::uniform32(prop::num::u8::ANY)
946                .prop_map(|b| EphemeralSecretKey::from_bytes(&b))
947                .prop_filter(
948                    "Values must correspond to valid Orchard ephemeral secret keys.",
949                    |opt| bool::from(opt.is_some())
950                )
951        ) -> EphemeralSecretKey {
952            esk.unwrap()
953        }
954    }
955
956    prop_compose! {
957        /// Generate a uniformly distributed Orchard diversifier key.
958        pub(crate) fn arb_diversifier_key()(
959            dk_bytes in prop::array::uniform32(prop::num::u8::ANY)
960        ) -> DiversifierKey {
961            DiversifierKey::from_bytes(dk_bytes)
962        }
963    }
964
965    prop_compose! {
966        /// Generate a uniformly distributed diversifier index.
967        pub fn arb_diversifier_index()(
968            d_bytes in prop::array::uniform11(prop::num::u8::ANY)
969        ) -> DiversifierIndex {
970            DiversifierIndex::from(d_bytes)
971        }
972    }
973}
974
975#[cfg(test)]
976mod tests {
977    use ff::PrimeField;
978    use proptest::prelude::*;
979
980    use super::{
981        testing::{arb_diversifier_index, arb_diversifier_key, arb_esk, arb_spending_key},
982        *,
983    };
984    use crate::{
985        note::{ExtractedNoteCommitment, RandomSeed, Rho},
986        value::NoteValue,
987        Note,
988    };
989
990    #[test]
991    fn spend_validating_key_from_bytes() {
992        // ak_P must not be the identity.
993        assert!(SpendValidatingKey::from_bytes(&[0; 32]).is_none());
994    }
995
996    #[test]
997    fn parsers_reject_invalid() {
998        assert!(bool::from(
999            EphemeralSecretKey::from_bytes(&[0xff; 32]).is_none()
1000        ));
1001        assert!(bool::from(
1002            EphemeralPublicKey::from_bytes(&[0xff; 32]).is_none()
1003        ));
1004    }
1005
1006    proptest! {
1007        #[test]
1008        fn key_agreement(
1009            sk in arb_spending_key(),
1010            esk in arb_esk(),
1011            j in arb_diversifier_index(),
1012        ) {
1013            let ivk = IncomingViewingKey::from_fvk(&(&sk).into());
1014            let addr = ivk.address_at(j);
1015
1016            let epk = esk.derive_public(addr.g_d());
1017
1018            assert!(bool::from(
1019                esk.agree(addr.pk_d()).0.ct_eq(&epk.agree(&ivk).0)
1020            ));
1021        }
1022    }
1023
1024    proptest! {
1025        #[test]
1026        fn diversifier_index(
1027            dk in arb_diversifier_key(),
1028            j in arb_diversifier_index(),
1029        ) {
1030            let d = dk.get(j);
1031            assert_eq!(j, dk.diversifier_index(&d));
1032        }
1033    }
1034
1035    #[test]
1036    fn test_vectors() {
1037        for tv in crate::test_vectors::keys::test_vectors() {
1038            let sk = SpendingKey::from_bytes(tv.sk).unwrap();
1039
1040            let ask: SpendAuthorizingKey = (&sk).into();
1041            assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask);
1042
1043            let ak: SpendValidatingKey = (&ask).into();
1044            assert_eq!(<[u8; 32]>::from(ak.0), tv.ak);
1045
1046            let nk: NullifierDerivingKey = (&sk).into();
1047            assert_eq!(nk.0.to_repr(), tv.nk);
1048
1049            let rivk: CommitIvkRandomness = (&sk).into();
1050            assert_eq!(rivk.0.to_repr(), tv.rivk);
1051
1052            let fvk: FullViewingKey = (&sk).into();
1053            assert_eq!(<[u8; 32]>::from(&fvk.ak.0), tv.ak);
1054            assert_eq!(fvk.nk().0.to_repr(), tv.nk);
1055            assert_eq!(fvk.rivk.0.to_repr(), tv.rivk);
1056
1057            let external_ivk = fvk.to_ivk(Scope::External);
1058            assert_eq!(external_ivk.ivk.0.to_repr(), tv.ivk);
1059
1060            let diversifier = Diversifier(tv.default_d);
1061
1062            let addr = fvk.address(diversifier, Scope::External);
1063            assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d);
1064
1065            let rho = Rho::from_bytes(&tv.note_rho).unwrap();
1066            let note = Note::from_parts(
1067                addr,
1068                NoteValue::from_raw(tv.note_v),
1069                rho,
1070                RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
1071            )
1072            .unwrap();
1073
1074            let cmx: ExtractedNoteCommitment = note.commitment().into();
1075            assert_eq!(cmx.to_bytes(), tv.note_cmx);
1076
1077            assert_eq!(note.nullifier(&fvk).to_bytes(), tv.note_nf);
1078
1079            let internal_rivk = fvk.rivk(Scope::Internal);
1080            assert_eq!(internal_rivk.0.to_repr(), tv.internal_rivk);
1081
1082            let internal_ivk = fvk.to_ivk(Scope::Internal);
1083            assert_eq!(internal_ivk.ivk.0.to_repr(), tv.internal_ivk);
1084            assert_eq!(internal_ivk.dk.0, tv.internal_dk);
1085
1086            let internal_ovk = fvk.to_ovk(Scope::Internal);
1087            assert_eq!(internal_ovk.0, tv.internal_ovk);
1088        }
1089    }
1090}