Skip to main content

sapling_crypto/
zip32.rs

1//! Sapling key derivation according to ZIP 32 and ZIP 316
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 aes::Aes256;
8use blake2b_simd::Params as Blake2bParams;
9use fpe::ff1::{BinaryNumeralString, FF1};
10use subtle::CtOption;
11use zcash_spec::PrfExpand;
12use zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope};
13
14use core::ops::AddAssign;
15use corez::io::{self, Read, Write};
16#[cfg(feature = "zeroize")]
17use zeroize::{Zeroize, ZeroizeOnDrop};
18
19use crate::zeroize_secret;
20
21use super::{Diversifier, NullifierDerivingKey, PaymentAddress, ViewingKey};
22use crate::note_encryption::PreparedIncomingViewingKey;
23use crate::{
24    constants::PROOF_GENERATION_KEY_GENERATOR,
25    keys::{
26        DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey,
27    },
28    SaplingIvk,
29};
30
31pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
32pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
33pub const ZIP32_SAPLING_INT_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingInt";
34
35/// Attempt to produce a payment address given the specified diversifier
36/// index, and return None if the specified index does not produce a valid
37/// diversifier.
38pub fn sapling_address(
39    fvk: &FullViewingKey,
40    dk: &DiversifierKey,
41    j: DiversifierIndex,
42) -> Option<PaymentAddress> {
43    dk.diversifier(j)
44        .and_then(|d_j| fvk.vk.to_payment_address(d_j))
45}
46
47/// Search the diversifier space starting at diversifier index `j` for
48/// one which will produce a valid diversifier, and return the payment address
49/// constructed using that diversifier along with the index at which the
50/// valid diversifier was found.
51pub fn sapling_find_address(
52    fvk: &FullViewingKey,
53    dk: &DiversifierKey,
54    j: DiversifierIndex,
55) -> Option<(DiversifierIndex, PaymentAddress)> {
56    let (j, d_j) = dk.find_diversifier(j)?;
57    fvk.vk.to_payment_address(d_j).map(|addr| (j, addr))
58}
59
60/// Returns the payment address corresponding to the smallest valid diversifier
61/// index, along with that index.
62pub fn sapling_default_address(
63    fvk: &FullViewingKey,
64    dk: &DiversifierKey,
65) -> (DiversifierIndex, PaymentAddress) {
66    // This unwrap is safe, if you have to search the 2^88 space of
67    // diversifiers it'll never return anyway.
68    sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap()
69}
70
71/// Convenience function for child OVK derivation
72fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey {
73    let mut ovk = [0u8; 32];
74    ovk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_OVK.with(i_l, &parent.0)[..32]);
75    OutgoingViewingKey(ovk)
76}
77
78/// Returns the internal full viewing key and diversifier key
79/// for the provided external FVK = (ak, nk, ovk) and dk encoded
80/// in a [Unified FVK].
81///
82/// Returns `None` if the incoming viewing key of the internal full viewing key is zero.
83/// ZIP 32 treats the internal key as invalid in that case.
84///
85/// [Unified FVK]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
86pub fn sapling_derive_internal_fvk(
87    fvk: &FullViewingKey,
88    dk: &DiversifierKey,
89) -> Option<(FullViewingKey, DiversifierKey)> {
90    let i = {
91        let mut h = Blake2bParams::new()
92            .hash_length(32)
93            .personal(ZIP32_SAPLING_INT_PERSONALIZATION)
94            .to_state();
95        h.update(&fvk.to_bytes());
96        h.update(&dk.0);
97        h.finalize()
98    };
99    let i_nsk =
100        jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes()));
101    let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes());
102    // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
103    let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk().0);
104    let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
105    let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
106
107    ViewingKey::from_parts(fvk.vk.ak().clone(), nk_internal).map(|vk| {
108        (
109            FullViewingKey {
110                vk,
111                ovk: ovk_internal,
112            },
113            dk_internal,
114        )
115    })
116}
117
118/// A Sapling full viewing key fingerprint
119struct FvkFingerprint([u8; 32]);
120
121impl From<&FullViewingKey> for FvkFingerprint {
122    fn from(fvk: &FullViewingKey) -> Self {
123        let mut h = Blake2bParams::new()
124            .hash_length(32)
125            .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION)
126            .to_state();
127        h.update(&fvk.to_bytes());
128        let mut fvfp = [0u8; 32];
129        fvfp.copy_from_slice(h.finalize().as_bytes());
130        FvkFingerprint(fvfp)
131    }
132}
133
134impl FvkFingerprint {
135    fn tag(&self) -> FvkTag {
136        let mut tag = [0u8; 4];
137        tag.copy_from_slice(&self.0[..4]);
138        FvkTag(tag)
139    }
140}
141
142/// A Sapling full viewing key tag
143#[derive(Clone, Copy, Debug, PartialEq)]
144struct FvkTag([u8; 4]);
145
146impl FvkTag {
147    fn master() -> Self {
148        FvkTag([0u8; 4])
149    }
150
151    fn as_bytes(&self) -> &[u8; 4] {
152        &self.0
153    }
154}
155
156/// A key used to derive diversifiers for a particular child key
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub struct DiversifierKey([u8; 32]);
159
160/// `DiversifierKey` is `Copy`, so it cannot implement `ZeroizeOnDrop`; types that own
161/// one alongside secret key material are responsible for zeroizing it.
162#[cfg(feature = "zeroize")]
163impl Zeroize for DiversifierKey {
164    fn zeroize(&mut self) {
165        self.0.zeroize();
166    }
167}
168
169impl DiversifierKey {
170    pub fn master(sk_m: &[u8]) -> Self {
171        let mut dk_m = [0u8; 32];
172        dk_m.copy_from_slice(&PrfExpand::SAPLING_ZIP32_MASTER_DK.with(sk_m)[..32]);
173        DiversifierKey(dk_m)
174    }
175
176    /// Constructs the diversifier key from its constituent bytes.
177    pub fn from_bytes(key: [u8; 32]) -> Self {
178        DiversifierKey(key)
179    }
180
181    /// Returns the byte representation of the diversifier key.
182    pub fn as_bytes(&self) -> &[u8; 32] {
183        &self.0
184    }
185
186    fn derive_child(&self, i_l: &[u8]) -> Self {
187        let mut dk = [0u8; 32];
188        dk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_DK.with(i_l, &self.0)[..32]);
189        DiversifierKey(dk)
190    }
191
192    fn try_diversifier_internal(ff: &FF1<Aes256>, j: DiversifierIndex) -> Option<Diversifier> {
193        // Generate d_j
194        let enc = ff
195            .encrypt(&[], &BinaryNumeralString::from_bytes_le(j.as_bytes()))
196            .unwrap();
197        let mut d_j = [0; 11];
198        d_j.copy_from_slice(&enc.to_bytes_le());
199        let diversifier = Diversifier(d_j);
200
201        // validate that the generated diversifier maps to a jubjub subgroup point.
202        diversifier.g_d().map(|_| diversifier)
203    }
204
205    /// Attempts to produce a diversifier at the given index. Returns None
206    /// if the index does not produce a valid diversifier.
207    pub fn diversifier(&self, j: DiversifierIndex) -> Option<Diversifier> {
208        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
209        Self::try_diversifier_internal(&ff, j)
210    }
211
212    /// Returns the diversifier index to which this key maps the given diversifier.
213    ///
214    /// This method cannot be used to verify whether the diversifier was originally
215    /// generated with this diversifier key, because all valid diversifiers can be
216    /// produced by all diversifier keys.
217    pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
218        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
219        let dec = ff
220            .decrypt(&[], &BinaryNumeralString::from_bytes_le(&d.0[..]))
221            .unwrap();
222        DiversifierIndex::from(<[u8; 11]>::try_from(&dec.to_bytes_le()[..]).unwrap())
223    }
224
225    /// Returns the first index starting from j that generates a valid
226    /// diversifier, along with the corresponding diversifier. Returns
227    /// `None` if the diversifier space contains no valid diversifiers
228    /// at or above the specified diversifier index.
229    pub fn find_diversifier(
230        &self,
231        mut j: DiversifierIndex,
232    ) -> Option<(DiversifierIndex, Diversifier)> {
233        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
234        loop {
235            match Self::try_diversifier_internal(&ff, j) {
236                Some(d_j) => return Some((j, d_j)),
237                None => {
238                    if j.increment().is_err() {
239                        return None;
240                    }
241                }
242            }
243        }
244    }
245}
246
247/// The derivation index associated with a key.
248///
249/// Master keys are never derived via the ZIP 32 child derivation process, but they have
250/// an index in their encoding. This type allows the encoding to be represented, while
251/// also enabling the derivation methods to only accept [`ChildIndex`].
252#[derive(Clone, Copy, Debug, PartialEq, Eq)]
253enum KeyIndex {
254    Master,
255    Child(ChildIndex),
256}
257
258impl KeyIndex {
259    fn new(depth: u8, i: u32) -> Option<Self> {
260        match (depth == 0, i) {
261            (true, 0) => Some(KeyIndex::Master),
262            (false, _) => ChildIndex::from_index(i).map(KeyIndex::Child),
263            _ => None,
264        }
265    }
266
267    fn index(&self) -> u32 {
268        match self {
269            KeyIndex::Master => 0,
270            KeyIndex::Child(i) => i.index(),
271        }
272    }
273}
274
275/// A Sapling extended spending key
276///
277/// The incoming viewing keys of both the external and the internal full viewing key
278/// derived from an `ExtendedSpendingKey` are never zero.
279///
280/// If the `zeroize` feature is enabled, the key material is zeroized on drop.
281#[derive(Clone)]
282pub struct ExtendedSpendingKey {
283    depth: u8,
284    parent_fvk_tag: FvkTag,
285    child_index: KeyIndex,
286    chain_code: ChainCode,
287    expsk: ExpandedSpendingKey,
288    dk: DiversifierKey,
289}
290
291#[cfg(feature = "zeroize")]
292impl Zeroize for ExtendedSpendingKey {
293    fn zeroize(&mut self) {
294        self.chain_code.zeroize();
295        self.expsk.zeroize();
296        self.dk.zeroize();
297    }
298}
299
300#[cfg(feature = "zeroize")]
301impl ZeroizeOnDrop for ExtendedSpendingKey {}
302
303#[cfg(feature = "zeroize")]
304impl Drop for ExtendedSpendingKey {
305    fn drop(&mut self) {
306        self.zeroize();
307    }
308}
309
310impl core::cmp::PartialEq for ExtendedSpendingKey {
311    fn eq(&self, rhs: &ExtendedSpendingKey) -> bool {
312        self.depth == rhs.depth
313            && self.parent_fvk_tag == rhs.parent_fvk_tag
314            && self.child_index == rhs.child_index
315            && self.chain_code == rhs.chain_code
316            && self.expsk.ask() == rhs.expsk.ask()
317            && self.expsk.nsk() == rhs.expsk.nsk()
318            && self.expsk.ovk() == rhs.expsk.ovk()
319            && self.dk == rhs.dk
320    }
321}
322
323impl core::fmt::Debug for ExtendedSpendingKey {
324    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
325        write!(
326            f,
327            "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})",
328            self.depth, self.parent_fvk_tag, self.child_index
329        )
330    }
331}
332
333impl ExtendedSpendingKey {
334    /// Constructs an extended spending key from its components.
335    ///
336    /// Returns `None` if the incoming viewing key of the internal full viewing key is
337    /// zero.
338    fn from_parts(
339        depth: u8,
340        parent_fvk_tag: FvkTag,
341        child_index: KeyIndex,
342        chain_code: ChainCode,
343        expsk: ExpandedSpendingKey,
344        dk: DiversifierKey,
345    ) -> Option<Self> {
346        let xsk = ExtendedSpendingKey {
347            depth,
348            parent_fvk_tag,
349            child_index,
350            chain_code,
351            expsk,
352            dk,
353        };
354        sapling_derive_internal_fvk(&xsk.fvk(), &xsk.dk).map(|_| xsk)
355    }
356
357    /// Derives the master extended spending key from a seed.
358    ///
359    /// Returns `None` if the master key is invalid: if it has `ask = 0`, or if the
360    /// incoming viewing key of its external or internal full viewing key is zero. This
361    /// has a negligible probability of occurring.
362    pub fn master(seed: &[u8]) -> Option<Self> {
363        // The hasher's own output value cannot be zeroized; this copy of it can.
364        let mut i: [u8; 64] = *Blake2bParams::new()
365            .hash_length(64)
366            .personal(ZIP32_SAPLING_MASTER_PERSONALIZATION)
367            .hash(seed)
368            .as_array();
369
370        let sk_m = &i[..32];
371        let mut c_m = [0u8; 32];
372        c_m.copy_from_slice(&i[32..]);
373
374        let xsk = ExpandedSpendingKey::from_spending_key(sk_m).and_then(|expsk| {
375            Self::from_parts(
376                0,
377                FvkTag::master(),
378                KeyIndex::Master,
379                ChainCode::new(c_m),
380                expsk,
381                DiversifierKey::master(sk_m),
382            )
383        });
384        zeroize_secret(&mut c_m);
385        zeroize_secret(&mut i);
386        xsk
387    }
388
389    /// Decodes the extended spending key from its serialized representation as defined in
390    /// [ZIP 32](https://zips.z.cash/zip-0032)
391    ///
392    /// Returns an error if the incoming viewing key of the external or internal full
393    /// viewing key is zero.
394    pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
395        if b.len() != 169 {
396            return Err(DecodingError::LengthInvalid {
397                expected: 169,
398                actual: b.len(),
399            });
400        }
401
402        let depth = b[0];
403
404        let mut parent_fvk_tag = FvkTag([0; 4]);
405        parent_fvk_tag.0[..].copy_from_slice(&b[1..5]);
406
407        let mut ci_bytes = [0u8; 4];
408        ci_bytes[..].copy_from_slice(&b[5..9]);
409        let child_index = KeyIndex::new(depth, u32::from_le_bytes(ci_bytes))
410            .ok_or(DecodingError::UnsupportedChildIndex)?;
411
412        let mut c = [0u8; 32];
413        c[..].copy_from_slice(&b[9..41]);
414
415        let expsk = ExpandedSpendingKey::from_bytes(&b[41..137])?;
416
417        let mut dk = DiversifierKey([0u8; 32]);
418        dk.0[..].copy_from_slice(&b[137..169]);
419
420        Self::from_parts(
421            depth,
422            parent_fvk_tag,
423            child_index,
424            ChainCode::new(c),
425            expsk,
426            dk,
427        )
428        .ok_or(DecodingError::InvalidIvk)
429    }
430
431    /// Reads and decodes the encoded form of the extended spending key as defined in
432    /// [ZIP 32](https://zips.z.cash/zip-0032) from the provided reader.
433    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
434        let mut depth = [0; 1];
435        reader.read_exact(&mut depth)?;
436        let depth = depth[0];
437        let mut tag = [0; 4];
438        reader.read_exact(&mut tag)?;
439        let mut child_index_bytes = [0; 4];
440        reader.read_exact(&mut child_index_bytes)?;
441        let child_index =
442            KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| {
443                corez::io::Error::new(
444                    corez::io::ErrorKind::InvalidData,
445                    "Unsupported child index in encoding",
446                )
447            })?;
448        let mut c = [0; 32];
449        reader.read_exact(&mut c)?;
450        let expsk = ExpandedSpendingKey::read(&mut reader)?;
451        let mut dk = [0; 32];
452        reader.read_exact(&mut dk)?;
453
454        let xsk = Self::from_parts(
455            depth,
456            FvkTag(tag),
457            child_index,
458            ChainCode::new(c),
459            expsk,
460            DiversifierKey(dk),
461        );
462        zeroize_secret(&mut c);
463        zeroize_secret(&mut dk);
464        xsk.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ivk is zero"))
465    }
466
467    /// Encodes the extended spending key to its serialized representation as defined in
468    /// [ZIP 32](https://zips.z.cash/zip-0032)
469    ///
470    /// The returned array is secret key material; the caller is responsible for
471    /// zeroizing it once it is no longer needed.
472    pub fn to_bytes(&self) -> [u8; 169] {
473        let mut result = [0u8; 169];
474        result[0] = self.depth;
475        result[1..5].copy_from_slice(&self.parent_fvk_tag.as_bytes()[..]);
476        result[5..9].copy_from_slice(&self.child_index.index().to_le_bytes()[..]);
477        result[9..41].copy_from_slice(&self.chain_code.as_bytes()[..]);
478        result[41..137].copy_from_slice(&self.expsk.to_bytes()[..]);
479        result[137..169].copy_from_slice(&self.dk.as_bytes()[..]);
480        result
481    }
482
483    /// Writes the encoded form of the extended spending key as defined in
484    /// [ZIP 32](https://zips.z.cash/zip-0032) to the provided writer.
485    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
486        writer.write_all(&self.to_bytes())
487    }
488
489    /// Returns the child key corresponding to the path derived from the master key
490    ///
491    /// Returns `None` if any key along the path is invalid. See [`Self::derive_child`].
492    pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Option<Self> {
493        path.iter()
494            .try_fold(master.clone(), |xsk, &i| xsk.derive_child(i))
495    }
496
497    /// Derives the child key at the given (hardened) index.
498    ///
499    /// Returns `None` if the child key is invalid: if it has `ask = 0`, or if the
500    /// incoming viewing key of its external or internal full viewing key is zero. This
501    /// has a negligible probability of occurring. ZIP 32 directs the caller to proceed
502    /// with the next index in that case.
503    #[must_use]
504    pub fn derive_child(&self, i: ChildIndex) -> Option<Self> {
505        let fvk = self.fvk();
506        let mut tmp = {
507            let le_i = i.index().to_le_bytes();
508            let mut expsk_bytes = self.expsk.to_bytes();
509            let tmp = PrfExpand::SAPLING_ZIP32_CHILD_HARDENED.with(
510                self.chain_code.as_bytes(),
511                &expsk_bytes,
512                &self.dk.0,
513                &le_i,
514            );
515            zeroize_secret(&mut expsk_bytes);
516            tmp
517        };
518        let i_l = &tmp[..32];
519        let mut c_i = [0u8; 32];
520        c_i.copy_from_slice(&tmp[32..]);
521
522        let expsk = {
523            let mut ask_prf = PrfExpand::SAPLING_ZIP32_CHILD_I_ASK.with(i_l);
524            let mut ask = jubjub::Fr::from_bytes_wide(&ask_prf);
525            zeroize_secret(&mut ask_prf);
526            let mut nsk_prf = PrfExpand::SAPLING_ZIP32_CHILD_I_NSK.with(i_l);
527            let mut nsk = jubjub::Fr::from_bytes_wide(&nsk_prf);
528            zeroize_secret(&mut nsk_prf);
529            let mut parent_ask = self.expsk.ask().to_scalar();
530            ask.add_assign(&parent_ask);
531            zeroize_secret(&mut parent_ask);
532            nsk.add_assign(self.expsk.nsk());
533            let ovk = derive_child_ovk(self.expsk.ovk(), i_l);
534            let expsk = SpendAuthorizingKey::from_scalar(ask)
535                .and_then(|ask| ExpandedSpendingKey::from_parts(ask, nsk, ovk));
536            zeroize_secret(&mut ask);
537            zeroize_secret(&mut nsk);
538            expsk
539        };
540
541        let xsk = expsk.and_then(|expsk| {
542            Self::from_parts(
543                self.depth + 1,
544                FvkFingerprint::from(&fvk).tag(),
545                KeyIndex::Child(i),
546                ChainCode::new(c_i),
547                expsk,
548                self.dk.derive_child(i_l),
549            )
550        });
551        zeroize_secret(&mut c_i);
552        zeroize_secret(&mut tmp);
553        xsk
554    }
555
556    /// Returns the address with the lowest valid diversifier index, along with
557    /// the diversifier index that generated that address.
558    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
559        self.to_diversifiable_full_viewing_key().default_address()
560    }
561
562    /// Derives an internal spending key given an external spending key.
563    ///
564    /// Returns `None` if the internal spending key does not satisfy the invariant of
565    /// `ExtendedSpendingKey`: that is, if the incoming viewing key of the internal full
566    /// viewing key derived from the internal spending key is zero. This has a negligible
567    /// probability of occurring.
568    ///
569    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key).
570    #[must_use]
571    pub fn derive_internal(&self) -> Option<Self> {
572        let i = {
573            let fvk = self.fvk();
574            let mut h = Blake2bParams::new()
575                .hash_length(32)
576                .personal(ZIP32_SAPLING_INT_PERSONALIZATION)
577                .to_state();
578            h.update(&fvk.to_bytes());
579            h.update(&self.dk.0);
580            h.finalize()
581        };
582        let mut i_nsk_prf = PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes());
583        let mut i_nsk = jubjub::Fr::from_bytes_wide(&i_nsk_prf);
584        zeroize_secret(&mut i_nsk_prf);
585        let mut r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes());
586        let mut nsk_internal = i_nsk + self.expsk.nsk();
587        zeroize_secret(&mut i_nsk);
588        let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
589        let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
590        zeroize_secret(&mut r);
591
592        // The internal full viewing key of this key is the full viewing key of the
593        // internal spending key, and `ExtendedSpendingKey::from_parts` has checked it.
594        let expsk =
595            ExpandedSpendingKey::from_parts(self.expsk.ask().clone(), nsk_internal, ovk_internal)
596                .expect("ExtendedSpendingKey::from_parts rejects a zero internal ivk");
597        let xsk = Self::from_parts(
598            self.depth,
599            self.parent_fvk_tag,
600            self.child_index,
601            self.chain_code,
602            expsk,
603            dk_internal,
604        );
605        zeroize_secret(&mut nsk_internal);
606        xsk
607    }
608
609    /// Returns the expanded spending key component of this extended spending key.
610    pub fn expsk(&self) -> &ExpandedSpendingKey {
611        &self.expsk
612    }
613
614    /// Derives the full viewing key corresponding to this spending key.
615    fn fvk(&self) -> FullViewingKey {
616        FullViewingKey::from_expanded_spending_key(&self.expsk)
617    }
618
619    #[deprecated(note = "Use `to_diversifiable_full_viewing_key` instead.")]
620    pub fn to_extended_full_viewing_key(&self) -> ExtendedFullViewingKey {
621        ExtendedFullViewingKey {
622            depth: self.depth,
623            parent_fvk_tag: self.parent_fvk_tag,
624            child_index: self.child_index,
625            chain_code: self.chain_code,
626            fvk: self.fvk(),
627            dk: self.dk,
628        }
629    }
630
631    pub fn to_diversifiable_full_viewing_key(&self) -> DiversifiableFullViewingKey {
632        DiversifiableFullViewingKey {
633            fvk: self.fvk(),
634            dk: self.dk,
635        }
636    }
637}
638
639/// A Sapling extended full viewing key
640///
641/// The incoming viewing keys of both the external and the internal full viewing key of an
642/// `ExtendedFullViewingKey` are never zero.
643#[derive(Clone)]
644pub struct ExtendedFullViewingKey {
645    depth: u8,
646    parent_fvk_tag: FvkTag,
647    child_index: KeyIndex,
648    chain_code: ChainCode,
649    fvk: FullViewingKey,
650    pub(crate) dk: DiversifierKey,
651}
652
653impl core::cmp::PartialEq for ExtendedFullViewingKey {
654    fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool {
655        self.depth == rhs.depth
656            && self.parent_fvk_tag == rhs.parent_fvk_tag
657            && self.child_index == rhs.child_index
658            && self.chain_code == rhs.chain_code
659            && self.fvk.vk.ak() == rhs.fvk.vk.ak()
660            && self.fvk.vk.nk() == rhs.fvk.vk.nk()
661            && self.fvk.ovk == rhs.fvk.ovk
662            && self.dk == rhs.dk
663    }
664}
665
666impl core::fmt::Debug for ExtendedFullViewingKey {
667    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
668        write!(
669            f,
670            "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})",
671            self.depth, self.parent_fvk_tag, self.child_index
672        )
673    }
674}
675
676impl ExtendedFullViewingKey {
677    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
678        let mut depth = [0; 1];
679        reader.read_exact(&mut depth)?;
680        let depth = depth[0];
681        let mut tag = [0; 4];
682        reader.read_exact(&mut tag)?;
683        let mut child_index_bytes = [0; 4];
684        reader.read_exact(&mut child_index_bytes)?;
685        let child_index =
686            KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| {
687                corez::io::Error::new(
688                    corez::io::ErrorKind::InvalidData,
689                    "Unsupported child index in encoding",
690                )
691            })?;
692        let mut c = [0; 32];
693        reader.read_exact(&mut c)?;
694        let fvk = FullViewingKey::read(&mut reader)?;
695        let mut dk = [0; 32];
696        reader.read_exact(&mut dk)?;
697
698        let dk = DiversifierKey(dk);
699        sapling_derive_internal_fvk(&fvk, &dk)
700            .map(|_| ExtendedFullViewingKey {
701                depth,
702                parent_fvk_tag: FvkTag(tag),
703                child_index,
704                chain_code: ChainCode::new(c),
705                fvk,
706                dk,
707            })
708            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "internal ivk is zero"))
709    }
710
711    /// Returns the full viewing key component of this extended full viewing key.
712    pub fn fvk(&self) -> &FullViewingKey {
713        &self.fvk
714    }
715
716    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
717        writer.write_all(&[self.depth])?;
718        writer.write_all(&self.parent_fvk_tag.0)?;
719        writer.write_all(&self.child_index.index().to_le_bytes())?;
720        writer.write_all(self.chain_code.as_bytes())?;
721        writer.write_all(&self.fvk.to_bytes())?;
722        writer.write_all(&self.dk.0)?;
723
724        Ok(())
725    }
726
727    /// Attempt to produce a payment address given the specified diversifier
728    /// index, and return None if the specified index does not produce a valid
729    /// diversifier.
730    pub fn address(&self, j: DiversifierIndex) -> Option<PaymentAddress> {
731        sapling_address(&self.fvk, &self.dk, j)
732    }
733
734    /// Search the diversifier space starting at diversifier index `j` for
735    /// one which will produce a valid diversifier, and return the payment address
736    /// constructed using that diversifier along with the index at which the
737    /// valid diversifier was found.
738    pub fn find_address(&self, j: DiversifierIndex) -> Option<(DiversifierIndex, PaymentAddress)> {
739        sapling_find_address(&self.fvk, &self.dk, j)
740    }
741
742    /// Returns the payment address corresponding to the smallest valid diversifier
743    /// index, along with that index.
744    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
745        sapling_default_address(&self.fvk, &self.dk)
746    }
747
748    /// Derives an internal full viewing key used for internal operations such
749    /// as change and auto-shielding. The internal FVK has the same spend authority
750    /// (the private key corresponding to ak) as the original, but viewing authority
751    /// only for internal transfers.
752    ///
753    /// Returns `None` if the internal full viewing key does not satisfy the invariant of
754    /// `ExtendedFullViewingKey`: that is, if the incoming viewing key of its own internal
755    /// full viewing key is zero. This has a negligible probability of occurring.
756    ///
757    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-full-viewing-key).
758    #[must_use]
759    pub fn derive_internal(&self) -> Option<Self> {
760        let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk)
761            .expect("ExtendedFullViewingKey constructors reject a zero internal ivk");
762
763        sapling_derive_internal_fvk(&fvk_internal, &dk_internal).map(|_| ExtendedFullViewingKey {
764            depth: self.depth,
765            parent_fvk_tag: self.parent_fvk_tag,
766            child_index: self.child_index,
767            chain_code: self.chain_code,
768            fvk: fvk_internal,
769            dk: dk_internal,
770        })
771    }
772
773    pub fn to_diversifiable_full_viewing_key(&self) -> DiversifiableFullViewingKey {
774        DiversifiableFullViewingKey {
775            fvk: self.fvk.clone(),
776            dk: self.dk,
777        }
778    }
779}
780
781/// A Sapling key that provides the capability to view incoming and outgoing transactions.
782///
783/// This key is useful anywhere you need to maintain accurate balance, but do not want the
784/// ability to spend funds (such as a view-only wallet).
785///
786/// It comprises the subset of the ZIP 32 extended full viewing key that is used for the
787/// Sapling item in a [ZIP 316 Unified Full Viewing Key][zip-0316-ufvk].
788///
789/// [zip-0316-ufvk]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
790///
791/// The incoming viewing keys of both the external and the internal full viewing key of an
792/// `DiversifiableFullViewingKey` are never zero.
793#[derive(Clone, Debug, PartialEq, Eq)]
794pub struct DiversifiableFullViewingKey {
795    fvk: FullViewingKey,
796    dk: DiversifierKey,
797}
798
799impl From<ExtendedFullViewingKey> for DiversifiableFullViewingKey {
800    fn from(extfvk: ExtendedFullViewingKey) -> Self {
801        DiversifiableFullViewingKey {
802            fvk: extfvk.fvk,
803            dk: extfvk.dk,
804        }
805    }
806}
807
808impl From<&ExtendedFullViewingKey> for DiversifiableFullViewingKey {
809    fn from(extfvk: &ExtendedFullViewingKey) -> Self {
810        extfvk.to_diversifiable_full_viewing_key()
811    }
812}
813
814impl DiversifiableFullViewingKey {
815    /// Parses a `DiversifiableFullViewingKey` from its raw byte encoding.
816    ///
817    /// Returns `None` if the bytes do not contain a valid encoding of a diversifiable
818    /// Sapling full viewing key, or if the incoming viewing key of its external or
819    /// internal full viewing key is zero.
820    pub fn from_bytes(bytes: &[u8; 128]) -> Option<Self> {
821        let fvk = FullViewingKey::read(&bytes[..96]).ok()?;
822        let dk = DiversifierKey::from_bytes(bytes[96..].try_into().unwrap());
823        sapling_derive_internal_fvk(&fvk, &dk).map(|_| Self { fvk, dk })
824    }
825
826    /// Returns the raw encoding of this `DiversifiableFullViewingKey`.
827    pub fn to_bytes(&self) -> [u8; 128] {
828        let mut bytes = [0; 128];
829        self.fvk
830            .write(&mut bytes[..96])
831            .expect("slice should be the correct length");
832        bytes[96..].copy_from_slice(&self.dk.as_bytes()[..]);
833        bytes
834    }
835
836    /// Derives the internal full viewing key and diversifier key corresponding to `self`
837    /// (which is assumed here to be an external DFVK).
838    fn internal_parts(&self) -> (FullViewingKey, DiversifierKey) {
839        sapling_derive_internal_fvk(&self.fvk, &self.dk)
840            .expect("DiversifiableFullViewingKey constructors reject a zero internal ivk")
841    }
842
843    /// Exposes the external [`FullViewingKey`] component of this diversifiable full viewing key.
844    pub fn fvk(&self) -> &FullViewingKey {
845        &self.fvk
846    }
847
848    /// Returns the internal [`FullViewingKey`] component of this diversifiable full viewing key.
849    pub fn to_internal_fvk(&self) -> FullViewingKey {
850        self.internal_parts().0
851    }
852
853    /// Derives a nullifier-deriving key for the provided scope.
854    ///
855    /// This API is provided so that nullifiers for change notes can be correctly computed.
856    pub fn to_nk(&self, scope: Scope) -> NullifierDerivingKey {
857        match scope {
858            Scope::External => *self.fvk.vk.nk(),
859            Scope::Internal => *self.internal_parts().0.vk.nk(),
860        }
861    }
862
863    /// Derives an incoming viewing key corresponding to this full viewing key.
864    // TODO: This should be renamed "prepare" and return a PreparedIncomingViewingKey
865    pub fn to_ivk(&self, scope: Scope) -> SaplingIvk {
866        match scope {
867            Scope::External => self.fvk.vk.ivk(),
868            Scope::Internal => self.internal_parts().0.vk.ivk(),
869        }
870    }
871
872    /// Derives the external diversifiable incoming viewing key corresponding to this full viewing key.
873    pub fn to_external_ivk(&self) -> IncomingViewingKey {
874        IncomingViewingKey {
875            dk: self.dk,
876            ivk: self.to_ivk(Scope::External),
877        }
878    }
879
880    /// Derives an outgoing viewing key corresponding to this full viewing key.
881    pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
882        match scope {
883            Scope::External => self.fvk.ovk,
884            Scope::Internal => self.internal_parts().0.ovk,
885        }
886    }
887
888    /// Attempts to produce a valid payment address for the given diversifier index.
889    ///
890    /// Returns `None` if the diversifier index does not produce a valid diversifier for
891    /// this `DiversifiableFullViewingKey`.
892    pub fn address(&self, j: DiversifierIndex) -> Option<PaymentAddress> {
893        self.to_external_ivk().address_at(j)
894    }
895
896    /// Finds the next valid payment address starting from the given diversifier index.
897    ///
898    /// This searches the diversifier space starting at `j` and incrementing, to find an
899    /// index which will produce a valid diversifier (a 50% probability for each index).
900    ///
901    /// Returns the index at which the valid diversifier was found along with the payment
902    /// address constructed using that diversifier, or `None` if the maximum index was
903    /// reached and no valid diversifier was found.
904    pub fn find_address(&self, j: DiversifierIndex) -> Option<(DiversifierIndex, PaymentAddress)> {
905        self.to_external_ivk().find_address(j)
906    }
907
908    /// Returns the payment address corresponding to the smallest valid diversifier index,
909    /// along with that index.
910    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
911        self.to_external_ivk().find_address(0u32).unwrap()
912    }
913
914    /// Returns the payment address corresponding to the specified diversifier, if any.
915    ///
916    /// In general, it is preferable to use `find_address` instead, but this method is
917    /// useful in some cases for matching keys to existing payment addresses.
918    pub fn diversified_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
919        self.to_external_ivk().address(diversifier)
920    }
921
922    /// Returns the internal address corresponding to the smallest valid diversifier index,
923    /// along with that index.
924    ///
925    /// This address **MUST NOT** be encoded and exposed to end users. User interfaces
926    /// should instead mark these notes as "change notes" or "internal wallet operations".
927    pub fn change_address(&self) -> (DiversifierIndex, PaymentAddress) {
928        let (fvk, dk) = self.internal_parts();
929        sapling_default_address(&fvk, &dk)
930    }
931
932    /// Returns the change address corresponding to the specified diversifier, if any.
933    ///
934    /// In general, it is preferable to use `change_address` instead, but this method is
935    /// useful in some cases for matching keys to existing payment addresses.
936    pub fn diversified_change_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
937        self.internal_parts().0.vk.to_payment_address(diversifier)
938    }
939
940    /// Attempts to decrypt the given address's diversifier with this full viewing key.
941    ///
942    /// This method extracts the diversifier from the given address and decrypts it as a
943    /// diversifier index, then verifies that this diversifier index produces the same
944    /// address. Decryption is attempted using both the internal and external parts of the
945    /// full viewing key.
946    ///
947    /// Returns the decrypted diversifier index and its scope, or `None` if the address
948    /// was not generated from this key.
949    pub fn decrypt_diversifier(&self, addr: &PaymentAddress) -> Option<(DiversifierIndex, Scope)> {
950        let j_external = self.dk.diversifier_index(addr.diversifier());
951        if self.address(j_external).as_ref() == Some(addr) {
952            return Some((j_external, Scope::External));
953        }
954
955        let j_internal = self
956            .internal_parts()
957            .1
958            .diversifier_index(addr.diversifier());
959        if self.address(j_internal).as_ref() == Some(addr) {
960            return Some((j_internal, Scope::Internal));
961        }
962
963        None
964    }
965}
966
967/// A Sapling key that provides the capability to decrypt incoming notes and generate diversified
968/// Sapling payment addresses.
969///
970/// This key is useful for detecting received funds and memos, but is not sufficient
971/// for determining balance.
972///
973/// It comprises the subset of the ZIP 32 extended full viewing key that is used for the
974/// Sapling item in a [ZIP 316 Unified Incoming Viewing Key][zip-0316-ufvk].
975///
976/// [zip-0316-ufvk]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
977#[derive(Clone, Debug, PartialEq, Eq)]
978pub struct IncomingViewingKey {
979    dk: DiversifierKey,
980    ivk: SaplingIvk,
981}
982
983impl IncomingViewingKey {
984    /// Parses a `IncomingViewingKey` from its raw byte encoding.
985    ///
986    /// Returns `None` if the bytes do not contain a valid encoding of a diversifiable
987    /// Sapling incoming viewing key. In particular, the `ivk` component must encode an
988    /// integer in the range $\{1 .. 2^{251} - 1\}$.
989    pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
990        SaplingIvk::parse_with(&bytes[32..].try_into().unwrap(), |ivk| IncomingViewingKey {
991            dk: DiversifierKey::from_bytes(bytes[..32].try_into().unwrap()),
992            ivk,
993        })
994    }
995
996    /// Returns the raw encoding of this `IncomingViewingKey`.
997    pub fn to_bytes(&self) -> [u8; 64] {
998        let mut bytes = [0; 64];
999        bytes[..32].copy_from_slice(&self.dk.as_bytes()[..]);
1000        bytes[32..].copy_from_slice(&self.ivk.to_repr()[..]);
1001        bytes
1002    }
1003
1004    /// Returns the prepared incoming viewing key for this IVK.
1005    pub fn prepare(&self) -> PreparedIncomingViewingKey {
1006        PreparedIncomingViewingKey::new(&self.ivk)
1007    }
1008
1009    /// Attempts to produce a valid payment address for the given diversifier index.
1010    ///
1011    /// Returns `None` if the diversifier index does not produce a valid diversifier for
1012    /// this `IncomingViewingKey`.
1013    pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Option<PaymentAddress> {
1014        self.dk
1015            .diversifier(j.into())
1016            .and_then(|d_j| self.ivk.to_payment_address(d_j))
1017    }
1018
1019    /// Returns the payment address corresponding to the specified diversifier, if any.
1020    ///
1021    /// In general, it is preferable to use `find_address` instead, but this method is
1022    /// useful in some cases for matching keys to existing payment addresses.
1023    pub fn address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
1024        self.ivk.to_payment_address(diversifier)
1025    }
1026
1027    /// Finds the next valid payment address starting from the given diversifier index.
1028    ///
1029    /// This searches the diversifier space starting at `j` and incrementing, to find an
1030    /// index which will produce a valid diversifier (a 50% probability for each index).
1031    ///
1032    /// Returns the index at which the valid diversifier was found along with the payment
1033    /// address constructed using that diversifier, or `None` if the maximum index was
1034    /// reached and no valid diversifier was found.
1035    pub fn find_address(
1036        &self,
1037        j: impl Into<DiversifierIndex>,
1038    ) -> Option<(DiversifierIndex, PaymentAddress)> {
1039        let (j, d_j) = self.dk.find_diversifier(j.into())?;
1040        self.ivk.to_payment_address(d_j).map(|addr| (j, addr))
1041    }
1042
1043    /// Attempts to decrypt the given address's diversifier with this incoming viewing key.
1044    ///
1045    /// This method extracts the diversifier from the given address and decrypts it as a
1046    /// diversifier index, then verifies that this diversifier index produces the same
1047    /// address. Decryption is attempted using both the internal and external parts of the
1048    /// full viewing key.
1049    ///
1050    /// Returns the decrypted diversifier index and its scope, or `None` if the address
1051    /// was not generated from this key.
1052    pub fn decrypt_diversifier(&self, addr: &PaymentAddress) -> Option<DiversifierIndex> {
1053        let j = self.dk.diversifier_index(addr.diversifier());
1054        if self.address_at(j).as_ref() == Some(addr) {
1055            Some(j)
1056        } else {
1057            None
1058        }
1059    }
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064    use super::*;
1065
1066    use super::{DiversifiableFullViewingKey, ExtendedSpendingKey};
1067    use ff::PrimeField;
1068    use group::GroupEncoding;
1069
1070    #[test]
1071    #[allow(deprecated)]
1072    fn derive_hardened_child() {
1073        let seed = [0; 32];
1074        let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
1075
1076        let i_5h = ChildIndex::hardened(5);
1077        let _ = xsk_m.derive_child(i_5h).unwrap();
1078    }
1079
1080    #[test]
1081    fn path() {
1082        let seed = [0; 32];
1083        let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
1084
1085        let xsk_5h = xsk_m.derive_child(ChildIndex::hardened(5)).unwrap();
1086        assert_eq!(
1087            ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::hardened(5)]).unwrap(),
1088            xsk_5h
1089        );
1090
1091        let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::hardened(7)).unwrap();
1092        assert_eq!(
1093            ExtendedSpendingKey::from_path(
1094                &xsk_m,
1095                &[ChildIndex::hardened(5), ChildIndex::hardened(7)]
1096            )
1097            .unwrap(),
1098            xsk_5h_7
1099        );
1100    }
1101
1102    #[test]
1103    fn diversifier() {
1104        let dk = DiversifierKey([0; 32]);
1105        let j_0 = DiversifierIndex::new();
1106        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1107        let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1108        let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1109        // Computed using this Rust implementation
1110        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
1111        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
1112
1113        // j = 0
1114        let d_j = dk.diversifier(j_0).unwrap();
1115        assert_eq!(d_j.0, d_0);
1116        assert_eq!(dk.diversifier_index(&Diversifier(d_0)), j_0);
1117
1118        // j = 1
1119        assert_eq!(dk.diversifier(j_1), None);
1120
1121        // j = 2
1122        assert_eq!(dk.diversifier(j_2), None);
1123
1124        // j = 3
1125        let d_j = dk.diversifier(j_3).unwrap();
1126        assert_eq!(d_j.0, d_3);
1127        assert_eq!(dk.diversifier_index(&Diversifier(d_3)), j_3);
1128    }
1129
1130    #[test]
1131    fn diversifier_index_from() {
1132        let di32: u32 = 0xa0b0c0d0;
1133        assert_eq!(
1134            DiversifierIndex::from(di32),
1135            DiversifierIndex::from([0xd0, 0xc0, 0xb0, 0xa0, 0, 0, 0, 0, 0, 0, 0])
1136        );
1137        let di64: u64 = 0x0102030405060708;
1138        assert_eq!(
1139            DiversifierIndex::from(di64),
1140            DiversifierIndex::from([8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0])
1141        );
1142    }
1143
1144    #[test]
1145    fn find_diversifier() {
1146        let dk = DiversifierKey([0; 32]);
1147        let j_0 = DiversifierIndex::new();
1148        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1149        let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1150        let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1151        // Computed using this Rust implementation
1152        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
1153        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
1154
1155        // j = 0
1156        let (j, d_j) = dk.find_diversifier(j_0).unwrap();
1157        assert_eq!(j, j_0);
1158        assert_eq!(d_j.0, d_0);
1159
1160        // j = 1
1161        let (j, d_j) = dk.find_diversifier(j_1).unwrap();
1162        assert_eq!(j, j_3);
1163        assert_eq!(d_j.0, d_3);
1164
1165        // j = 2
1166        let (j, d_j) = dk.find_diversifier(j_2).unwrap();
1167        assert_eq!(j, j_3);
1168        assert_eq!(d_j.0, d_3);
1169
1170        // j = 3
1171        let (j, d_j) = dk.find_diversifier(j_3).unwrap();
1172        assert_eq!(j, j_3);
1173        assert_eq!(d_j.0, d_3);
1174    }
1175
1176    #[test]
1177    fn dfvk_round_trip() {
1178        let dfvk = {
1179            let extsk = ExtendedSpendingKey::master(&[]).unwrap();
1180            #[allow(deprecated)]
1181            let extfvk = extsk.to_extended_full_viewing_key();
1182            DiversifiableFullViewingKey::from(extfvk)
1183        };
1184
1185        // Check value -> bytes -> parsed round trip.
1186        let dfvk_bytes = dfvk.to_bytes();
1187        let dfvk_parsed = DiversifiableFullViewingKey::from_bytes(&dfvk_bytes).unwrap();
1188        assert_eq!(dfvk_parsed.fvk.vk.ak(), dfvk.fvk.vk.ak());
1189        assert_eq!(dfvk_parsed.fvk.vk.nk(), dfvk.fvk.vk.nk());
1190        assert_eq!(dfvk_parsed.fvk.ovk, dfvk.fvk.ovk);
1191        assert_eq!(dfvk_parsed.dk, dfvk.dk);
1192
1193        // Check bytes -> parsed -> bytes round trip.
1194        assert_eq!(dfvk_parsed.to_bytes(), dfvk_bytes);
1195    }
1196
1197    #[test]
1198    fn ivk_round_trip() {
1199        let ivk = {
1200            let extsk = ExtendedSpendingKey::master(&[]).unwrap();
1201            #[allow(deprecated)]
1202            let extfvk = extsk.to_extended_full_viewing_key();
1203            DiversifiableFullViewingKey::from(extfvk).to_external_ivk()
1204        };
1205
1206        // Check value -> bytes -> parsed round trip.
1207        let ivk_bytes = ivk.to_bytes();
1208        let ivk_parsed = IncomingViewingKey::from_bytes(&ivk_bytes).unwrap();
1209        assert_eq!(ivk_parsed.dk, ivk.dk);
1210        assert_eq!(ivk_parsed.ivk, ivk.ivk);
1211
1212        // Check bytes -> parsed -> bytes round trip.
1213        assert_eq!(ivk_parsed.to_bytes(), ivk_bytes);
1214    }
1215
1216    #[test]
1217    fn ivk_must_be_nonzero() {
1218        let ivk = {
1219            let extsk = ExtendedSpendingKey::master(&[]).unwrap();
1220            extsk.to_diversifiable_full_viewing_key().to_external_ivk()
1221        };
1222
1223        let mut ivk_bytes = ivk.to_bytes();
1224        ivk_bytes[32..].fill(0);
1225        assert!(bool::from(
1226            IncomingViewingKey::from_bytes(&ivk_bytes).is_none()
1227        ));
1228    }
1229
1230    #[test]
1231    fn address() {
1232        let seed = [0; 32];
1233        let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
1234        let xfvk_m = xsk_m.to_diversifiable_full_viewing_key();
1235        let j_0 = DiversifierIndex::new();
1236        let addr_m = xfvk_m.address(j_0).unwrap();
1237        assert_eq!(
1238            addr_m.diversifier().0,
1239            // Computed using this Rust implementation
1240            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
1241        );
1242
1243        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1244        assert_eq!(xfvk_m.address(j_1), None);
1245    }
1246
1247    #[test]
1248    fn default_address() {
1249        let seed = [0; 32];
1250        let xsk_m = ExtendedSpendingKey::master(&seed).unwrap();
1251        let (j_m, addr_m) = xsk_m.default_address();
1252        assert_eq!(j_m.as_bytes(), &[0; 11]);
1253        assert_eq!(
1254            addr_m.diversifier().0,
1255            // Computed using this Rust implementation
1256            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
1257        );
1258    }
1259
1260    #[test]
1261    #[allow(deprecated)]
1262    fn read_write() {
1263        let seed = [0; 32];
1264        let xsk = ExtendedSpendingKey::master(&seed).unwrap();
1265        let fvk = xsk.to_extended_full_viewing_key();
1266
1267        let mut ser = vec![];
1268        xsk.write(&mut ser).unwrap();
1269        let xsk2 = ExtendedSpendingKey::read(&ser[..]).unwrap();
1270        assert_eq!(xsk2, xsk);
1271
1272        let mut ser = vec![];
1273        fvk.write(&mut ser).unwrap();
1274        let fvk2 = ExtendedFullViewingKey::read(&ser[..]).unwrap();
1275        assert_eq!(fvk2, fvk);
1276    }
1277
1278    #[test]
1279    #[allow(deprecated)]
1280    fn test_vectors() {
1281        struct TestVector {
1282            ask: Option<[u8; 32]>,
1283            nsk: Option<[u8; 32]>,
1284            ovk: [u8; 32],
1285            dk: [u8; 32],
1286            c: [u8; 32],
1287            ak: [u8; 32],
1288            nk: [u8; 32],
1289            ivk: [u8; 32],
1290            xsk: Option<[u8; 169]>,
1291            xfvk: [u8; 169],
1292            fp: [u8; 32],
1293            d0: Option<[u8; 11]>,
1294            d1: Option<[u8; 11]>,
1295            d2: Option<[u8; 11]>,
1296            dmax: Option<[u8; 11]>,
1297            internal_nsk: Option<[u8; 32]>,
1298            internal_ovk: [u8; 32],
1299            internal_dk: [u8; 32],
1300            internal_nk: [u8; 32],
1301            internal_ivk: [u8; 32],
1302            internal_xsk: Option<[u8; 169]>,
1303            internal_xfvk: [u8; 169],
1304            internal_fp: [u8; 32],
1305        }
1306
1307        // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py
1308        let test_vectors = [
1309            TestVector {
1310                ask: Some([
1311                    0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86,
1312                    0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6,
1313                    0x07, 0xc9, 0x54, 0x74, 0x25, 0x06,
1314                ]),
1315                nsk: Some([
1316                    0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f,
1317                    0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f,
1318                    0x74, 0x8a, 0x88, 0x21, 0xea, 0x06,
1319                ]),
1320                ovk: [
1321                    0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8,
1322                    0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83,
1323                    0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21,
1324                ],
1325                dk: [
1326                    0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91,
1327                    0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc,
1328                    0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1329                ],
1330                c: [
1331                    0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27,
1332                    0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55,
1333                    0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e,
1334                ],
1335                ak: [
1336                    0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc,
1337                    0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64,
1338                    0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71,
1339                ],
1340                nk: [
1341                    0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba,
1342                    0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93,
1343                    0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06,
1344                ],
1345                ivk: [
1346                    0x48, 0x47, 0xa1, 0x30, 0xe7, 0x99, 0xd3, 0xdb, 0xea, 0x36, 0xa1, 0xc1, 0x64,
1347                    0x67, 0xd6, 0x21, 0xfb, 0x2d, 0x80, 0xe3, 0x0b, 0x3b, 0x1d, 0x1a, 0x42, 0x68,
1348                    0x93, 0x41, 0x5d, 0xad, 0x66, 0x01,
1349                ],
1350                xsk: Some([
1351                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1352                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1353                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1354                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
1355                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
1356                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x82, 0x04, 0xed, 0xe8, 0x3b,
1357                    0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a,
1358                    0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea,
1359                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
1360                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
1361                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
1362                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
1363                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1364                ]),
1365                xfvk: [
1366                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1367                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1368                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1369                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
1370                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
1371                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xdc, 0xe8, 0xe7, 0xed, 0xec,
1372                    0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c,
1373                    0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01,
1374                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
1375                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
1376                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
1377                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
1378                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1379                ],
1380                fp: [
1381                    0x14, 0xc2, 0x71, 0x3a, 0xdc, 0xe9, 0x3a, 0x83, 0x0e, 0xa8, 0x3a, 0x05, 0x19,
1382                    0x08, 0xb7, 0x44, 0x77, 0x83, 0xf5, 0xd1, 0x06, 0xc0, 0x98, 0x5e, 0x02, 0x55,
1383                    0x0e, 0x42, 0x6f, 0x27, 0x59, 0x7c,
1384                ],
1385                d0: Some([
1386                    0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89,
1387                ]),
1388                d1: Some([
1389                    0x48, 0xea, 0x17, 0xa1, 0x99, 0xc8, 0x4b, 0xd1, 0xba, 0xa5, 0xd4,
1390                ]),
1391                d2: None,
1392                dmax: None,
1393                internal_nsk: Some([
1394                    0x51, 0x12, 0x33, 0x63, 0x6b, 0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a,
1395                    0x7d, 0x8f, 0x49, 0xef, 0xd7, 0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56,
1396                    0x68, 0x76, 0x46, 0xea, 0xab, 0x07,
1397                ]),
1398                internal_ovk: [
1399                    0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65, 0x4d,
1400                    0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72, 0x3b,
1401                    0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a,
1402                ],
1403                internal_dk: [
1404                    0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75, 0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5,
1405                    0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16, 0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38,
1406                    0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1407                ],
1408                internal_nk: [
1409                    0xa3, 0x83, 0x1a, 0x5c, 0x69, 0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c,
1410                    0x50, 0x8b, 0x79, 0x91, 0xcd, 0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27,
1411                    0xa6, 0xae, 0x15, 0xe7, 0x2f, 0xb5,
1412                ],
1413                internal_ivk: [
1414                    0x79, 0x05, 0x77, 0x32, 0x1c, 0x51, 0x18, 0x04, 0x63, 0x6e, 0xe6, 0xba, 0xa4,
1415                    0xee, 0xa7, 0x79, 0xb4, 0xa4, 0x6a, 0x5a, 0x12, 0xf8, 0x5d, 0x36, 0x50, 0x74,
1416                    0xa0, 0x9d, 0x05, 0x4f, 0x34, 0x01,
1417                ],
1418                internal_xsk: Some([
1419                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1420                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1421                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1422                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
1423                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
1424                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x51, 0x12, 0x33, 0x63, 0x6b,
1425                    0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a, 0x7d, 0x8f, 0x49, 0xef, 0xd7,
1426                    0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56, 0x68, 0x76, 0x46, 0xea, 0xab,
1427                    0x07, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
1428                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
1429                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
1430                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
1431                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1432                ]),
1433                internal_xfvk: [
1434                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1435                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1436                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1437                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
1438                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
1439                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xa3, 0x83, 0x1a, 0x5c, 0x69,
1440                    0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c, 0x50, 0x8b, 0x79, 0x91, 0xcd,
1441                    0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27, 0xa6, 0xae, 0x15, 0xe7, 0x2f,
1442                    0xb5, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
1443                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
1444                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
1445                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
1446                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1447                ],
1448                internal_fp: [
1449                    0x82, 0x64, 0xed, 0xec, 0x63, 0xb1, 0x55, 0x00, 0x1d, 0x84, 0x96, 0x68, 0x5c,
1450                    0xc7, 0xc2, 0x1e, 0xa9, 0x57, 0xc6, 0xf5, 0x91, 0x09, 0x0a, 0x1c, 0x20, 0xe5,
1451                    0x2a, 0x41, 0x89, 0xb8, 0xbb, 0x96,
1452                ],
1453            },
1454            TestVector {
1455                ask: Some([
1456                    0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14, 0x8b, 0x0b,
1457                    0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93, 0xb6, 0x8d,
1458                    0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04,
1459                ]),
1460                nsk: Some([
1461                    0x37, 0x2a, 0x7c, 0x68, 0x22, 0xcb, 0xe6, 0x03, 0xf3, 0x46, 0x5c, 0x4b, 0x9b,
1462                    0x65, 0x58, 0xf3, 0xa3, 0x51, 0x2d, 0xec, 0xd4, 0x34, 0x01, 0x2e, 0x67, 0xbf,
1463                    0xfc, 0xf6, 0x57, 0xe5, 0x75, 0x0a,
1464                ]),
1465                ovk: [
1466                    0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54, 0x33,
1467                    0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97, 0x16,
1468                    0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69,
1469                ],
1470                dk: [
1471                    0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f, 0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a,
1472                    0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d, 0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9,
1473                    0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1474                ],
1475                c: [
1476                    0x6f, 0xcc, 0xaa, 0x45, 0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10,
1477                    0xe0, 0x59, 0x27, 0xaa, 0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8,
1478                    0x2e, 0xfd, 0x68, 0xca, 0xae, 0xdb,
1479                ],
1480                ak: [
1481                    0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09, 0xa5, 0x4e,
1482                    0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c, 0x9b, 0xec,
1483                    0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53,
1484                ],
1485                nk: [
1486                    0x51, 0x3d, 0xe6, 0x40, 0x85, 0xd3, 0x5a, 0x3a, 0xdf, 0x23, 0xd8, 0x9d, 0x5a,
1487                    0x21, 0xcd, 0xee, 0x4d, 0xb4, 0xc6, 0x25, 0xbd, 0x6a, 0x3c, 0x3c, 0x62, 0x4b,
1488                    0xef, 0x43, 0x44, 0x14, 0x1d, 0xeb,
1489                ],
1490                ivk: [
1491                    0xf6, 0xe7, 0x5c, 0xd9, 0x80, 0xc3, 0x0e, 0xab, 0xc6, 0x1f, 0x49, 0xac, 0x68,
1492                    0xf4, 0x88, 0x57, 0x3a, 0xb3, 0xe6, 0xaf, 0xe1, 0x53, 0x76, 0x37, 0x5d, 0x34,
1493                    0xe4, 0x06, 0x70, 0x2f, 0xfd, 0x02,
1494                ],
1495                xsk: Some([
1496                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1497                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1498                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1499                    0xae, 0xdb, 0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14,
1500                    0x8b, 0x0b, 0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93,
1501                    0xb6, 0x8d, 0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04, 0x37, 0x2a, 0x7c, 0x68, 0x22,
1502                    0xcb, 0xe6, 0x03, 0xf3, 0x46, 0x5c, 0x4b, 0x9b, 0x65, 0x58, 0xf3, 0xa3, 0x51,
1503                    0x2d, 0xec, 0xd4, 0x34, 0x01, 0x2e, 0x67, 0xbf, 0xfc, 0xf6, 0x57, 0xe5, 0x75,
1504                    0x0a, 0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54,
1505                    0x33, 0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97,
1506                    0x16, 0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69, 0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f,
1507                    0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a, 0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d,
1508                    0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9, 0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1509                ]),
1510                xfvk: [
1511                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1512                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1513                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1514                    0xae, 0xdb, 0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09,
1515                    0xa5, 0x4e, 0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c,
1516                    0x9b, 0xec, 0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53, 0x51, 0x3d, 0xe6, 0x40, 0x85,
1517                    0xd3, 0x5a, 0x3a, 0xdf, 0x23, 0xd8, 0x9d, 0x5a, 0x21, 0xcd, 0xee, 0x4d, 0xb4,
1518                    0xc6, 0x25, 0xbd, 0x6a, 0x3c, 0x3c, 0x62, 0x4b, 0xef, 0x43, 0x44, 0x14, 0x1d,
1519                    0xeb, 0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54,
1520                    0x33, 0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97,
1521                    0x16, 0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69, 0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f,
1522                    0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a, 0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d,
1523                    0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9, 0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1524                ],
1525                fp: [
1526                    0x76, 0x84, 0x23, 0xcb, 0x88, 0xd2, 0x2d, 0xee, 0x91, 0xb5, 0xb7, 0x66, 0x1e,
1527                    0x72, 0xed, 0x00, 0x95, 0x57, 0xeb, 0xa1, 0x44, 0xc7, 0x8d, 0x1a, 0xa7, 0x1a,
1528                    0x3e, 0x88, 0xb6, 0x91, 0x06, 0x96,
1529                ],
1530                d0: None,
1531                d1: Some([
1532                    0xbc, 0xc3, 0x23, 0xe8, 0xda, 0x39, 0xb4, 0x96, 0xc0, 0x50, 0x51,
1533                ]),
1534                d2: None,
1535                dmax: Some([
1536                    0x25, 0x14, 0x32, 0x0d, 0x33, 0x9c, 0x66, 0x6a, 0x25, 0x4c, 0x06,
1537                ]),
1538                internal_nsk: Some([
1539                    0x5d, 0x47, 0x0f, 0x97, 0x79, 0xcc, 0x25, 0x7f, 0x21, 0x28, 0x8f, 0x50, 0x5a,
1540                    0x4e, 0x65, 0xb3, 0x8e, 0xb8, 0x53, 0xf1, 0xa2, 0x45, 0x63, 0xb9, 0xf6, 0x74,
1541                    0x17, 0x26, 0xf4, 0xd3, 0x01, 0x03,
1542                ]),
1543                internal_ovk: [
1544                    0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca, 0x10,
1545                    0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18, 0x0d,
1546                    0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70,
1547                ],
1548                internal_dk: [
1549                    0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d, 0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d,
1550                    0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7, 0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf,
1551                    0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1552                ],
1553                internal_nk: [
1554                    0x31, 0x42, 0x48, 0x75, 0xd6, 0xa5, 0xed, 0x75, 0xde, 0x20, 0x0b, 0xb5, 0xc1,
1555                    0xd8, 0x1a, 0xec, 0x4d, 0xff, 0x16, 0x50, 0xb7, 0x8b, 0xb0, 0xca, 0xde, 0x3c,
1556                    0x8c, 0x7a, 0xb0, 0x3d, 0xf1, 0x11,
1557                ],
1558                internal_ivk: [
1559                    0xe5, 0x42, 0x6b, 0x5b, 0x80, 0xb1, 0x18, 0x67, 0x97, 0x01, 0x65, 0x80, 0xc1,
1560                    0xf4, 0x1c, 0x34, 0x19, 0x68, 0x3a, 0xac, 0x77, 0xcf, 0x8d, 0xe0, 0x2f, 0x2f,
1561                    0x98, 0x07, 0xd1, 0x50, 0xb4, 0x02,
1562                ],
1563                internal_xsk: Some([
1564                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1565                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1566                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1567                    0xae, 0xdb, 0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14,
1568                    0x8b, 0x0b, 0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93,
1569                    0xb6, 0x8d, 0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04, 0x5d, 0x47, 0x0f, 0x97, 0x79,
1570                    0xcc, 0x25, 0x7f, 0x21, 0x28, 0x8f, 0x50, 0x5a, 0x4e, 0x65, 0xb3, 0x8e, 0xb8,
1571                    0x53, 0xf1, 0xa2, 0x45, 0x63, 0xb9, 0xf6, 0x74, 0x17, 0x26, 0xf4, 0xd3, 0x01,
1572                    0x03, 0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca,
1573                    0x10, 0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18,
1574                    0x0d, 0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70, 0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d,
1575                    0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d, 0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7,
1576                    0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf, 0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1577                ]),
1578                internal_xfvk: [
1579                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1580                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1581                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1582                    0xae, 0xdb, 0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09,
1583                    0xa5, 0x4e, 0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c,
1584                    0x9b, 0xec, 0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53, 0x31, 0x42, 0x48, 0x75, 0xd6,
1585                    0xa5, 0xed, 0x75, 0xde, 0x20, 0x0b, 0xb5, 0xc1, 0xd8, 0x1a, 0xec, 0x4d, 0xff,
1586                    0x16, 0x50, 0xb7, 0x8b, 0xb0, 0xca, 0xde, 0x3c, 0x8c, 0x7a, 0xb0, 0x3d, 0xf1,
1587                    0x11, 0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca,
1588                    0x10, 0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18,
1589                    0x0d, 0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70, 0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d,
1590                    0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d, 0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7,
1591                    0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf, 0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1592                ],
1593                internal_fp: [
1594                    0x12, 0x87, 0xc3, 0x79, 0x02, 0x35, 0x69, 0x16, 0x4e, 0xb5, 0x23, 0xff, 0xdd,
1595                    0x72, 0x03, 0x1c, 0x35, 0xe1, 0x85, 0x9f, 0x3e, 0xf8, 0xd4, 0x83, 0x0a, 0x29,
1596                    0x91, 0xba, 0x7e, 0x15, 0xde, 0x2d,
1597                ],
1598            },
1599            TestVector {
1600                ask: Some([
1601                    0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c, 0x08, 0xd3,
1602                    0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42, 0x4b, 0xae,
1603                    0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06,
1604                ]),
1605                nsk: Some([
1606                    0x77, 0x9c, 0x6e, 0xe4, 0xa0, 0x39, 0x44, 0xeb, 0xa2, 0x8b, 0xc9, 0xbd, 0xc1,
1607                    0x32, 0x9a, 0x39, 0x14, 0x07, 0xf4, 0x8c, 0x41, 0x0d, 0x5a, 0xe0, 0xa3, 0x64,
1608                    0xf5, 0x99, 0x59, 0xbf, 0xde, 0x00,
1609                ]),
1610                ovk: [
1611                    0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30, 0xa5,
1612                    0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2, 0x3d,
1613                    0x76, 0x05, 0x93, 0x61, 0x85, 0xac,
1614                ],
1615                dk: [
1616                    0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0, 0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96,
1617                    0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec, 0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6,
1618                    0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1619                ],
1620                c: [
1621                    0x44, 0x79, 0x08, 0x6c, 0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1,
1622                    0xe3, 0x0a, 0x54, 0xcf, 0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a,
1623                    0x50, 0x80, 0x6f, 0xbe, 0xf7, 0xda,
1624                ],
1625                ak: [
1626                    0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17, 0x64, 0xda,
1627                    0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42, 0xee, 0x76,
1628                    0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e,
1629                ],
1630                nk: [
1631                    0xf1, 0x66, 0xa2, 0x8a, 0x4f, 0x88, 0xce, 0xc1, 0x21, 0x41, 0xa8, 0x2d, 0x21,
1632                    0x20, 0xbd, 0x6d, 0x8c, 0xaf, 0x87, 0x9c, 0x9a, 0x1b, 0x3a, 0xd2, 0x11, 0x85,
1633                    0x01, 0x36, 0x4f, 0x5d, 0x4f, 0xbe,
1634                ],
1635                ivk: [
1636                    0x33, 0xbd, 0x46, 0x01, 0x5a, 0x2c, 0xad, 0x17, 0xd6, 0xe0, 0x15, 0xeb, 0x88,
1637                    0x86, 0x1b, 0x0c, 0x91, 0x77, 0x96, 0x24, 0x65, 0x70, 0x52, 0x1c, 0x9e, 0x1a,
1638                    0xe4, 0xb1, 0xc8, 0x31, 0x1d, 0x06,
1639                ],
1640                xsk: Some([
1641                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1642                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1643                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1644                    0xf7, 0xda, 0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c,
1645                    0x08, 0xd3, 0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42,
1646                    0x4b, 0xae, 0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06, 0x77, 0x9c, 0x6e, 0xe4, 0xa0,
1647                    0x39, 0x44, 0xeb, 0xa2, 0x8b, 0xc9, 0xbd, 0xc1, 0x32, 0x9a, 0x39, 0x14, 0x07,
1648                    0xf4, 0x8c, 0x41, 0x0d, 0x5a, 0xe0, 0xa3, 0x64, 0xf5, 0x99, 0x59, 0xbf, 0xde,
1649                    0x00, 0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30,
1650                    0xa5, 0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2,
1651                    0x3d, 0x76, 0x05, 0x93, 0x61, 0x85, 0xac, 0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0,
1652                    0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96, 0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec,
1653                    0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6, 0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1654                ]),
1655                xfvk: [
1656                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1657                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1658                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1659                    0xf7, 0xda, 0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17,
1660                    0x64, 0xda, 0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42,
1661                    0xee, 0x76, 0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e, 0xf1, 0x66, 0xa2, 0x8a, 0x4f,
1662                    0x88, 0xce, 0xc1, 0x21, 0x41, 0xa8, 0x2d, 0x21, 0x20, 0xbd, 0x6d, 0x8c, 0xaf,
1663                    0x87, 0x9c, 0x9a, 0x1b, 0x3a, 0xd2, 0x11, 0x85, 0x01, 0x36, 0x4f, 0x5d, 0x4f,
1664                    0xbe, 0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30,
1665                    0xa5, 0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2,
1666                    0x3d, 0x76, 0x05, 0x93, 0x61, 0x85, 0xac, 0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0,
1667                    0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96, 0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec,
1668                    0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6, 0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1669                ],
1670                fp: [
1671                    0x0b, 0xdc, 0x2d, 0x2b, 0x6e, 0xb1, 0xf9, 0x27, 0xcb, 0xab, 0xdb, 0xb9, 0xd4,
1672                    0x3d, 0xb8, 0xde, 0x85, 0x7b, 0xb7, 0x16, 0xdf, 0x86, 0xce, 0xcf, 0x08, 0x1e,
1673                    0x1a, 0x2b, 0x74, 0xfc, 0xad, 0x55,
1674                ],
1675                d0: None,
1676                d1: None,
1677                d2: None,
1678                dmax: None,
1679                internal_nsk: Some([
1680                    0x7b, 0x17, 0x17, 0x65, 0x27, 0xf9, 0x17, 0x99, 0x0f, 0x9f, 0x51, 0x79, 0xcb,
1681                    0x23, 0xc1, 0x6e, 0xc0, 0xa9, 0x26, 0xed, 0xc4, 0x1a, 0xb2, 0xba, 0x42, 0x13,
1682                    0x7b, 0xef, 0x5c, 0x20, 0x9f, 0x09,
1683                ]),
1684                internal_ovk: [
1685                    0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b, 0xd7,
1686                    0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a, 0xa3,
1687                    0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a,
1688                ],
1689                internal_dk: [
1690                    0x35, 0xef, 0x4d, 0x26, 0x59, 0x51, 0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd,
1691                    0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04, 0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43,
1692                    0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1693                ],
1694                internal_nk: [
1695                    0x8d, 0x05, 0x55, 0xe8, 0xe0, 0x20, 0xc9, 0xd3, 0x60, 0x68, 0x5d, 0x24, 0x2f,
1696                    0x2b, 0xa9, 0xf7, 0x74, 0x61, 0x3f, 0xa0, 0x94, 0x01, 0xf1, 0x25, 0xbc, 0xa9,
1697                    0x29, 0xec, 0xa4, 0x86, 0xa3, 0xd1,
1698                ],
1699                internal_ivk: [
1700                    0x7f, 0x7c, 0xee, 0xfa, 0x65, 0x42, 0x8e, 0x8b, 0x70, 0x76, 0x19, 0x1a, 0x23,
1701                    0x93, 0x95, 0x7b, 0x9c, 0x09, 0x50, 0x61, 0xd8, 0xcc, 0xe1, 0x28, 0x3d, 0xd1,
1702                    0x5c, 0x2b, 0x5e, 0x8f, 0xc3, 0x05,
1703                ],
1704                internal_xsk: Some([
1705                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1706                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1707                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1708                    0xf7, 0xda, 0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c,
1709                    0x08, 0xd3, 0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42,
1710                    0x4b, 0xae, 0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06, 0x7b, 0x17, 0x17, 0x65, 0x27,
1711                    0xf9, 0x17, 0x99, 0x0f, 0x9f, 0x51, 0x79, 0xcb, 0x23, 0xc1, 0x6e, 0xc0, 0xa9,
1712                    0x26, 0xed, 0xc4, 0x1a, 0xb2, 0xba, 0x42, 0x13, 0x7b, 0xef, 0x5c, 0x20, 0x9f,
1713                    0x09, 0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b,
1714                    0xd7, 0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a,
1715                    0xa3, 0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a, 0x35, 0xef, 0x4d, 0x26, 0x59, 0x51,
1716                    0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd, 0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04,
1717                    0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43, 0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1718                ]),
1719                internal_xfvk: [
1720                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1721                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1722                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1723                    0xf7, 0xda, 0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17,
1724                    0x64, 0xda, 0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42,
1725                    0xee, 0x76, 0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e, 0x8d, 0x05, 0x55, 0xe8, 0xe0,
1726                    0x20, 0xc9, 0xd3, 0x60, 0x68, 0x5d, 0x24, 0x2f, 0x2b, 0xa9, 0xf7, 0x74, 0x61,
1727                    0x3f, 0xa0, 0x94, 0x01, 0xf1, 0x25, 0xbc, 0xa9, 0x29, 0xec, 0xa4, 0x86, 0xa3,
1728                    0xd1, 0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b,
1729                    0xd7, 0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a,
1730                    0xa3, 0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a, 0x35, 0xef, 0x4d, 0x26, 0x59, 0x51,
1731                    0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd, 0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04,
1732                    0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43, 0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1733                ],
1734                internal_fp: [
1735                    0xe0, 0xba, 0xa5, 0xdb, 0xb8, 0x06, 0xc7, 0x21, 0x33, 0x3c, 0x63, 0x08, 0x34,
1736                    0x5f, 0xc5, 0x1c, 0x2d, 0xc1, 0xe0, 0x09, 0xda, 0x04, 0x47, 0x78, 0xa3, 0xc3,
1737                    0x29, 0x4d, 0x68, 0x17, 0xa3, 0xc4,
1738                ],
1739            },
1740            TestVector {
1741                ask: Some([
1742                    0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf, 0x90, 0x46,
1743                    0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83, 0xe0, 0xac,
1744                    0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07,
1745                ]),
1746                nsk: Some([
1747                    0x1a, 0xc8, 0x7e, 0xc2, 0x12, 0x3f, 0x50, 0x57, 0xe3, 0xc0, 0xf8, 0x58, 0xe8,
1748                    0x0d, 0xfa, 0x0e, 0xe4, 0x55, 0x3d, 0xed, 0x27, 0xb7, 0xb5, 0xab, 0xfb, 0xb6,
1749                    0xfa, 0x6e, 0xff, 0xa7, 0xbb, 0x0b,
1750                ]),
1751                ovk: [
1752                    0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8, 0xaf,
1753                    0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31, 0x1b,
1754                    0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3,
1755                ],
1756                dk: [
1757                    0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3, 0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71,
1758                    0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d, 0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70,
1759                    0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1760                ],
1761                c: [
1762                    0x33, 0xdc, 0x01, 0x2d, 0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3,
1763                    0xe4, 0x63, 0xe2, 0x8d, 0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd,
1764                    0xfc, 0x38, 0x5b, 0xbd, 0xc7, 0x4b,
1765                ],
1766                ak: [
1767                    0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde, 0x95, 0xf2,
1768                    0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b, 0xc6, 0x01,
1769                    0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12,
1770                ],
1771                nk: [
1772                    0xd3, 0xee, 0x41, 0xf8, 0x4b, 0x5a, 0x95, 0x08, 0xb6, 0x1d, 0x29, 0xb2, 0xfb,
1773                    0x45, 0x63, 0x6d, 0x19, 0xaa, 0x10, 0xd7, 0x82, 0xcd, 0x97, 0x8c, 0xfe, 0x67,
1774                    0x15, 0x49, 0x2f, 0xcd, 0x22, 0x4e,
1775                ],
1776                ivk: [
1777                    0xd1, 0x38, 0xe1, 0x37, 0xc6, 0x67, 0x1d, 0xe7, 0x82, 0xfb, 0x01, 0xba, 0x91,
1778                    0x1d, 0x98, 0x64, 0xbe, 0xbc, 0x44, 0x36, 0xcc, 0xb3, 0x88, 0xb4, 0xc1, 0xce,
1779                    0x02, 0x56, 0xa8, 0xdb, 0x74, 0x01,
1780                ],
1781                xsk: Some([
1782                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1783                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1784                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1785                    0xc7, 0x4b, 0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf,
1786                    0x90, 0x46, 0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83,
1787                    0xe0, 0xac, 0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07, 0x1a, 0xc8, 0x7e, 0xc2, 0x12,
1788                    0x3f, 0x50, 0x57, 0xe3, 0xc0, 0xf8, 0x58, 0xe8, 0x0d, 0xfa, 0x0e, 0xe4, 0x55,
1789                    0x3d, 0xed, 0x27, 0xb7, 0xb5, 0xab, 0xfb, 0xb6, 0xfa, 0x6e, 0xff, 0xa7, 0xbb,
1790                    0x0b, 0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8,
1791                    0xaf, 0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31,
1792                    0x1b, 0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3, 0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3,
1793                    0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71, 0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d,
1794                    0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70, 0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1795                ]),
1796                xfvk: [
1797                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1798                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1799                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1800                    0xc7, 0x4b, 0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde,
1801                    0x95, 0xf2, 0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b,
1802                    0xc6, 0x01, 0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12, 0xd3, 0xee, 0x41, 0xf8, 0x4b,
1803                    0x5a, 0x95, 0x08, 0xb6, 0x1d, 0x29, 0xb2, 0xfb, 0x45, 0x63, 0x6d, 0x19, 0xaa,
1804                    0x10, 0xd7, 0x82, 0xcd, 0x97, 0x8c, 0xfe, 0x67, 0x15, 0x49, 0x2f, 0xcd, 0x22,
1805                    0x4e, 0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8,
1806                    0xaf, 0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31,
1807                    0x1b, 0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3, 0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3,
1808                    0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71, 0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d,
1809                    0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70, 0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1810                ],
1811                fp: [
1812                    0xdf, 0x0a, 0x89, 0xbd, 0x88, 0x35, 0x39, 0xc0, 0x7b, 0x89, 0xe0, 0x4c, 0x92,
1813                    0x76, 0x4e, 0xc2, 0xd1, 0x59, 0x69, 0x0f, 0x5a, 0xd5, 0xdd, 0x3d, 0x0a, 0xd8,
1814                    0xac, 0x29, 0x69, 0xde, 0x22, 0xc8,
1815                ],
1816                d0: None,
1817                d1: None,
1818                d2: None,
1819                dmax: Some([
1820                    0xb8, 0x31, 0xc2, 0x96, 0x5a, 0x86, 0x0a, 0xd7, 0x60, 0xec, 0x2a,
1821                ]),
1822                internal_nsk: Some([
1823                    0x9c, 0x39, 0x3c, 0x5b, 0xd7, 0x66, 0x4d, 0x63, 0xef, 0xa1, 0xba, 0xea, 0x99,
1824                    0xfc, 0x6d, 0xc4, 0x74, 0xfe, 0xa7, 0x53, 0xce, 0x84, 0xc8, 0x81, 0xd9, 0xef,
1825                    0x28, 0x77, 0x86, 0x75, 0xb1, 0x05,
1826                ]),
1827                internal_ovk: [
1828                    0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8, 0xb4,
1829                    0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41, 0x15,
1830                    0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72,
1831                ],
1832                internal_dk: [
1833                    0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6, 0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72,
1834                    0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26, 0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14,
1835                    0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1836                ],
1837                internal_nk: [
1838                    0x59, 0xba, 0xa9, 0x0f, 0x83, 0x4a, 0x66, 0x1b, 0xf2, 0xbe, 0x42, 0x46, 0xa4,
1839                    0x3d, 0x18, 0x9c, 0x7d, 0x0e, 0x17, 0xa8, 0x24, 0x7b, 0x4f, 0xd9, 0xd2, 0xe1,
1840                    0x53, 0xa5, 0x97, 0x3d, 0xc8, 0xec,
1841                ],
1842                internal_ivk: [
1843                    0x8a, 0x86, 0xfb, 0x27, 0x81, 0xfe, 0x6f, 0x24, 0xd9, 0x60, 0xdd, 0xdb, 0x2f,
1844                    0x78, 0x13, 0xc0, 0x31, 0xfe, 0xc5, 0x5d, 0x26, 0xcc, 0xde, 0xe1, 0xf7, 0x18,
1845                    0x2a, 0x3e, 0xc6, 0x83, 0xcf, 0x04,
1846                ],
1847                internal_xsk: Some([
1848                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1849                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1850                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1851                    0xc7, 0x4b, 0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf,
1852                    0x90, 0x46, 0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83,
1853                    0xe0, 0xac, 0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07, 0x9c, 0x39, 0x3c, 0x5b, 0xd7,
1854                    0x66, 0x4d, 0x63, 0xef, 0xa1, 0xba, 0xea, 0x99, 0xfc, 0x6d, 0xc4, 0x74, 0xfe,
1855                    0xa7, 0x53, 0xce, 0x84, 0xc8, 0x81, 0xd9, 0xef, 0x28, 0x77, 0x86, 0x75, 0xb1,
1856                    0x05, 0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8,
1857                    0xb4, 0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41,
1858                    0x15, 0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72, 0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6,
1859                    0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72, 0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26,
1860                    0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14, 0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1861                ]),
1862                internal_xfvk: [
1863                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1864                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1865                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1866                    0xc7, 0x4b, 0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde,
1867                    0x95, 0xf2, 0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b,
1868                    0xc6, 0x01, 0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12, 0x59, 0xba, 0xa9, 0x0f, 0x83,
1869                    0x4a, 0x66, 0x1b, 0xf2, 0xbe, 0x42, 0x46, 0xa4, 0x3d, 0x18, 0x9c, 0x7d, 0x0e,
1870                    0x17, 0xa8, 0x24, 0x7b, 0x4f, 0xd9, 0xd2, 0xe1, 0x53, 0xa5, 0x97, 0x3d, 0xc8,
1871                    0xec, 0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8,
1872                    0xb4, 0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41,
1873                    0x15, 0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72, 0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6,
1874                    0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72, 0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26,
1875                    0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14, 0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1876                ],
1877                internal_fp: [
1878                    0x3f, 0x63, 0x16, 0x1d, 0x5b, 0x43, 0x72, 0x04, 0xf7, 0x01, 0x2a, 0x3a, 0x1d,
1879                    0x36, 0x58, 0x1d, 0xab, 0x39, 0x7a, 0x84, 0x3b, 0x2c, 0x58, 0x98, 0x11, 0xed,
1880                    0xcc, 0x5b, 0x50, 0x1c, 0xd4, 0xeb,
1881                ],
1882            },
1883        ];
1884
1885        let seed = [
1886            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1887            24, 25, 26, 27, 28, 29, 30, 31,
1888        ];
1889
1890        let i1h = ChildIndex::hardened(1);
1891        let i2h = ChildIndex::hardened(2);
1892        let i3h = ChildIndex::hardened(3);
1893
1894        let m = ExtendedSpendingKey::master(&seed).unwrap();
1895        let m_1h = m.derive_child(i1h).unwrap();
1896        let m_1h_2h = ExtendedSpendingKey::from_path(&m, &[i1h, i2h]).unwrap();
1897        let m_1h_2h_3h = m_1h_2h.derive_child(i3h).unwrap();
1898
1899        let xfvks = [
1900            m.to_extended_full_viewing_key(),
1901            m_1h.to_extended_full_viewing_key(),
1902            m_1h_2h.to_extended_full_viewing_key(),
1903            m_1h_2h_3h.to_extended_full_viewing_key(),
1904        ];
1905        assert_eq!(test_vectors.len(), xfvks.len());
1906
1907        let xsks = [m, m_1h, m_1h_2h, m_1h_2h_3h];
1908
1909        for (xsk, tv) in xsks.iter().zip(test_vectors.iter()) {
1910            assert_eq!(xsk.expsk.ask().to_bytes(), tv.ask.unwrap());
1911            assert_eq!(xsk.expsk.nsk().to_repr().as_ref(), tv.nsk.unwrap());
1912
1913            assert_eq!(xsk.expsk.ovk().0, tv.ovk);
1914            assert_eq!(xsk.dk.0, tv.dk);
1915            assert_eq!(xsk.chain_code.as_bytes(), &tv.c);
1916
1917            let mut ser = vec![];
1918            xsk.write(&mut ser).unwrap();
1919            assert_eq!(&ser[..], &tv.xsk.unwrap()[..]);
1920
1921            let internal_xsk = xsk.derive_internal().unwrap();
1922            assert_eq!(internal_xsk.expsk.ask().to_bytes(), tv.ask.unwrap());
1923            assert_eq!(
1924                internal_xsk.expsk.nsk().to_repr().as_ref(),
1925                tv.internal_nsk.unwrap()
1926            );
1927
1928            assert_eq!(internal_xsk.expsk.ovk().0, tv.internal_ovk);
1929            assert_eq!(internal_xsk.dk.0, tv.internal_dk);
1930            assert_eq!(internal_xsk.chain_code.as_bytes(), &tv.c);
1931
1932            let mut ser = vec![];
1933            internal_xsk.write(&mut ser).unwrap();
1934            assert_eq!(&ser[..], &tv.internal_xsk.unwrap()[..]);
1935        }
1936
1937        for (xfvk, tv) in xfvks.iter().zip(test_vectors.iter()) {
1938            assert_eq!(xfvk.fvk.vk.ak().to_bytes(), tv.ak);
1939            assert_eq!(xfvk.fvk.vk.nk().0.to_bytes(), tv.nk);
1940
1941            assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
1942            assert_eq!(xfvk.dk.0, tv.dk);
1943            assert_eq!(xfvk.chain_code.as_bytes(), &tv.c);
1944
1945            assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk);
1946
1947            let mut ser = vec![];
1948            xfvk.write(&mut ser).unwrap();
1949            assert_eq!(&ser[..], &tv.xfvk[..]);
1950            assert_eq!(FvkFingerprint::from(&xfvk.fvk).0, tv.fp);
1951
1952            // d0
1953            let mut di = DiversifierIndex::new();
1954            match xfvk.dk.find_diversifier(di).unwrap() {
1955                (l, d) if l == di => assert_eq!(d.0, tv.d0.unwrap()),
1956                (_, _) => assert!(tv.d0.is_none()),
1957            }
1958
1959            // d1
1960            di.increment().unwrap();
1961            match xfvk.dk.find_diversifier(di).unwrap() {
1962                (l, d) if l == di => assert_eq!(d.0, tv.d1.unwrap()),
1963                (_, _) => assert!(tv.d1.is_none()),
1964            }
1965
1966            // d2
1967            di.increment().unwrap();
1968            match xfvk.dk.find_diversifier(di).unwrap() {
1969                (l, d) if l == di => assert_eq!(d.0, tv.d2.unwrap()),
1970                (_, _) => assert!(tv.d2.is_none()),
1971            }
1972
1973            // dmax
1974            let dmax = DiversifierIndex::from([0xff; 11]);
1975            match xfvk.dk.find_diversifier(dmax) {
1976                Some((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()),
1977                Some((_, _)) => panic!(),
1978                None => assert!(tv.dmax.is_none()),
1979            }
1980
1981            let internal_xfvk = xfvk.derive_internal().unwrap();
1982            assert_eq!(internal_xfvk.fvk.vk.ak().to_bytes(), tv.ak);
1983            assert_eq!(internal_xfvk.fvk.vk.nk().0.to_bytes(), tv.internal_nk);
1984
1985            assert_eq!(internal_xfvk.fvk.ovk.0, tv.internal_ovk);
1986            assert_eq!(internal_xfvk.dk.0, tv.internal_dk);
1987            assert_eq!(internal_xfvk.chain_code.as_bytes(), &tv.c);
1988
1989            assert_eq!(
1990                internal_xfvk.fvk.vk.ivk().to_repr().as_ref(),
1991                tv.internal_ivk
1992            );
1993
1994            let mut ser = vec![];
1995            internal_xfvk.write(&mut ser).unwrap();
1996            assert_eq!(&ser[..], &tv.internal_xfvk[..]);
1997            assert_eq!(FvkFingerprint::from(&internal_xfvk.fvk).0, tv.internal_fp);
1998
1999            let dfvk = xfvk.to_diversifiable_full_viewing_key();
2000            let ivk = dfvk.to_external_ivk();
2001            let ivk_bytes = ivk.to_bytes();
2002            assert_eq!(&ivk_bytes[..32], tv.dk);
2003            assert_eq!(&ivk_bytes[32..], tv.ivk);
2004
2005            let ivk_rt = IncomingViewingKey::from_bytes(&ivk_bytes).unwrap();
2006            assert_eq!(ivk.dk, ivk_rt.dk);
2007            assert_eq!(ivk.ivk, ivk_rt.ivk);
2008        }
2009    }
2010}
2011
2012#[cfg(any(test, feature = "test-dependencies"))]
2013#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
2014pub mod testing {
2015    use proptest::collection::vec;
2016    use proptest::prelude::{any, prop_compose, Strategy};
2017
2018    use super::ExtendedSpendingKey;
2019
2020    prop_compose! {
2021        pub fn arb_extended_spending_key()(
2022            xsk in vec(any::<u8>(), 32..252).prop_filter_map(
2023                "master key must be valid",
2024                |seed| ExtendedSpendingKey::master(&seed),
2025            )
2026        ) -> ExtendedSpendingKey {
2027            xsk
2028        }
2029    }
2030}
2031
2032#[cfg(all(test, feature = "zeroize"))]
2033mod zeroize_tests {
2034    use zeroize::Zeroize;
2035
2036    use super::ExtendedSpendingKey;
2037
2038    #[test]
2039    fn extended_spending_key_zeroizes() {
2040        let mut xsk = ExtendedSpendingKey::master(&[7; 32]).unwrap();
2041        let before = xsk.to_bytes();
2042        assert_ne!(&before[9..], &[0u8; 160][..]);
2043
2044        xsk.zeroize();
2045        let after = xsk.to_bytes();
2046        // The depth, parent tag and child index are not key material and are kept.
2047        assert_eq!(&after[..9], &before[..9]);
2048        assert_eq!(&after[9..], &[0u8; 160][..]);
2049    }
2050}