ironfish_primitives/
zip32.rs

1//! Implementation of [ZIP 32] for hierarchical deterministic key management.
2//!
3//! [ZIP 32]: https://zips.z.cash/zip-0032
4
5use aes::Aes256;
6use blake2b_simd::Params as Blake2bParams;
7use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
8use fpe::ff1::{BinaryNumeralString, FF1};
9use std::convert::TryInto;
10use std::ops::AddAssign;
11use subtle::{Choice, ConditionallySelectable};
12
13use crate::{
14    constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
15    sapling::{Diversifier, PaymentAddress, ViewingKey},
16};
17use std::io::{self, Read, Write};
18
19use crate::{
20    keys::{prf_expand, prf_expand_vec, OutgoingViewingKey},
21    sapling::keys::{ExpandedSpendingKey, FullViewingKey},
22};
23
24pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
25pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
26pub const ZIP32_SAPLING_INT_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingInt";
27
28/// A type-safe wrapper for account identifiers.
29#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct AccountId(u32);
31
32impl From<u32> for AccountId {
33    fn from(id: u32) -> Self {
34        Self(id)
35    }
36}
37
38impl From<AccountId> for u32 {
39    fn from(id: AccountId) -> Self {
40        id.0
41    }
42}
43
44impl ConditionallySelectable for AccountId {
45    fn conditional_select(a0: &Self, a1: &Self, c: Choice) -> Self {
46        AccountId(u32::conditional_select(&a0.0, &a1.0, c))
47    }
48}
49
50// Common helper functions
51
52fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey {
53    let mut ovk = [0u8; 32];
54    ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &parent.0]).as_bytes()[..32]);
55    OutgoingViewingKey(ovk)
56}
57
58// ZIP 32 structures
59
60/// A Sapling full viewing key fingerprint
61struct FvkFingerprint([u8; 32]);
62
63impl From<&FullViewingKey> for FvkFingerprint {
64    fn from(fvk: &FullViewingKey) -> Self {
65        let mut h = Blake2bParams::new()
66            .hash_length(32)
67            .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION)
68            .to_state();
69        h.update(&fvk.to_bytes());
70        let mut fvfp = [0u8; 32];
71        fvfp.copy_from_slice(h.finalize().as_bytes());
72        FvkFingerprint(fvfp)
73    }
74}
75
76/// A Sapling full viewing key tag
77#[derive(Clone, Copy, Debug, PartialEq)]
78struct FvkTag([u8; 4]);
79
80impl FvkFingerprint {
81    fn tag(&self) -> FvkTag {
82        let mut tag = [0u8; 4];
83        tag.copy_from_slice(&self.0[..4]);
84        FvkTag(tag)
85    }
86}
87
88impl FvkTag {
89    fn master() -> Self {
90        FvkTag([0u8; 4])
91    }
92}
93
94/// A child index for a derived key
95#[derive(Clone, Copy, Debug, PartialEq)]
96pub enum ChildIndex {
97    NonHardened(u32),
98    Hardened(u32), // Hardened(n) == n + (1 << 31) == n' in path notation
99}
100
101impl ChildIndex {
102    pub fn from_index(i: u32) -> Self {
103        match i {
104            n if n >= (1 << 31) => ChildIndex::Hardened(n - (1 << 31)),
105            n => ChildIndex::NonHardened(n),
106        }
107    }
108
109    fn master() -> Self {
110        ChildIndex::from_index(0)
111    }
112
113    fn value(&self) -> u32 {
114        match *self {
115            ChildIndex::Hardened(i) => i + (1 << 31),
116            ChildIndex::NonHardened(i) => i,
117        }
118    }
119}
120
121/// A BIP-32 chain code
122#[derive(Clone, Copy, Debug, PartialEq)]
123pub struct ChainCode([u8; 32]);
124
125#[derive(Clone, Copy, Debug, PartialEq)]
126pub struct DiversifierIndex(pub [u8; 11]);
127
128impl Default for DiversifierIndex {
129    fn default() -> Self {
130        DiversifierIndex::new()
131    }
132}
133
134impl DiversifierIndex {
135    pub fn new() -> Self {
136        DiversifierIndex([0; 11])
137    }
138
139    pub fn increment(&mut self) -> Result<(), ()> {
140        for k in 0..11 {
141            self.0[k] = self.0[k].wrapping_add(1);
142            if self.0[k] != 0 {
143                // No overflow
144                return Ok(());
145            }
146        }
147        // Overflow
148        Err(())
149    }
150}
151
152/// A key used to derive diversifiers for a particular child key
153#[derive(Clone, Copy, Debug, PartialEq)]
154pub struct DiversifierKey(pub [u8; 32]);
155
156impl DiversifierKey {
157    pub fn master(sk_m: &[u8]) -> Self {
158        let mut dk_m = [0u8; 32];
159        dk_m.copy_from_slice(&prf_expand(sk_m, &[0x10]).as_bytes()[..32]);
160        DiversifierKey(dk_m)
161    }
162
163    fn derive_child(&self, i_l: &[u8]) -> Self {
164        let mut dk = [0u8; 32];
165        dk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x16], &self.0]).as_bytes()[..32]);
166        DiversifierKey(dk)
167    }
168
169    fn try_diversifier_internal(ff: &FF1<Aes256>, j: DiversifierIndex) -> Option<Diversifier> {
170        // Generate d_j
171        let enc = ff
172            .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.0[..]))
173            .unwrap();
174        let mut d_j = [0; 11];
175        d_j.copy_from_slice(&enc.to_bytes_le());
176        let diversifier = Diversifier(d_j);
177
178        // validate that the generated diversifier maps to a ironfish_jubjub subgroup point.
179        diversifier.g_d().map(|_| diversifier)
180    }
181
182    /// Attempts to produce a diversifier at the given index. Returns None
183    /// if the index does not produce a valid diversifier.
184    pub fn diversifier(&self, j: DiversifierIndex) -> Option<Diversifier> {
185        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
186        Self::try_diversifier_internal(&ff, j)
187    }
188
189    /// Returns the diversifier index to which this key maps the given diversifier.
190    ///
191    /// This method cannot be used to verify whether the diversifier was originally
192    /// generated with this diversifier key, because all valid diversifiers can be
193    /// produced by all diversifier keys.
194    pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
195        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
196        let dec = ff
197            .decrypt(&[], &BinaryNumeralString::from_bytes_le(&d.0[..]))
198            .unwrap();
199        let mut j = DiversifierIndex::new();
200        j.0.copy_from_slice(&dec.to_bytes_le());
201        j
202    }
203
204    /// Returns the first index starting from j that generates a valid
205    /// diversifier, along with the corresponding diversifier. Returns
206    /// `None` if the diversifier space contains no valid diversifiers
207    /// at or above the specified diversifier index.
208    pub fn find_diversifier(
209        &self,
210        mut j: DiversifierIndex,
211    ) -> Option<(DiversifierIndex, Diversifier)> {
212        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
213        loop {
214            match Self::try_diversifier_internal(&ff, j) {
215                Some(d_j) => return Some((j, d_j)),
216                None => {
217                    if j.increment().is_err() {
218                        return None;
219                    }
220                }
221            }
222        }
223    }
224}
225
226/// Attempt to produce a payment address given the specified diversifier
227/// index, and return None if the specified index does not produce a valid
228/// diversifier.
229pub fn sapling_address(
230    fvk: &FullViewingKey,
231    dk: &DiversifierKey,
232    j: DiversifierIndex,
233) -> Option<PaymentAddress> {
234    dk.diversifier(j)
235        .and_then(|d_j| fvk.vk.to_payment_address(d_j))
236}
237
238/// Search the diversifier space starting at diversifier index `j` for
239/// one which will produce a valid diversifier, and return the payment address
240/// constructed using that diversifier along with the index at which the
241/// valid diversifier was found.
242pub fn sapling_find_address(
243    fvk: &FullViewingKey,
244    dk: &DiversifierKey,
245    j: DiversifierIndex,
246) -> Option<(DiversifierIndex, PaymentAddress)> {
247    let (j, d_j) = dk.find_diversifier(j)?;
248    fvk.vk.to_payment_address(d_j).map(|addr| (j, addr))
249}
250
251/// Returns the payment address corresponding to the smallest valid diversifier
252/// index, along with that index.
253pub fn sapling_default_address(
254    fvk: &FullViewingKey,
255    dk: &DiversifierKey,
256) -> (DiversifierIndex, PaymentAddress) {
257    // This unwrap is safe, if you have to search the 2^88 space of
258    // diversifiers it'll never return anyway.
259    sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap()
260}
261
262/// Returns the internal full viewing key and diversifier key
263/// for the provided external FVK = (ak, nk, ovk) and dk encoded
264/// in a [Unified FVK].
265///
266/// [Unified FVK]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
267pub fn sapling_derive_internal_fvk(
268    fvk: &FullViewingKey,
269    dk: &DiversifierKey,
270) -> (FullViewingKey, DiversifierKey) {
271    let i = {
272        let mut h = Blake2bParams::new()
273            .hash_length(32)
274            .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION)
275            .to_state();
276        h.update(&fvk.to_bytes());
277        h.update(&dk.0);
278        h.finalize()
279    };
280    let i_nsk = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array());
281    let r = prf_expand(i.as_bytes(), &[0x18]);
282    let r = r.as_bytes();
283    // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
284    let nk_internal = *PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk;
285    let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
286    let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
287
288    (
289        FullViewingKey {
290            vk: ViewingKey {
291                ak: fvk.vk.ak,
292                nk: nk_internal,
293            },
294            ovk: ovk_internal,
295        },
296        dk_internal,
297    )
298}
299
300/// A Sapling extended spending key
301#[derive(Clone)]
302pub struct ExtendedSpendingKey {
303    depth: u8,
304    parent_fvk_tag: FvkTag,
305    child_index: ChildIndex,
306    chain_code: ChainCode,
307    pub expsk: ExpandedSpendingKey,
308    dk: DiversifierKey,
309}
310
311// A Sapling extended full viewing key
312#[derive(Clone)]
313pub struct ExtendedFullViewingKey {
314    depth: u8,
315    parent_fvk_tag: FvkTag,
316    child_index: ChildIndex,
317    chain_code: ChainCode,
318    pub fvk: FullViewingKey,
319    pub(crate) dk: DiversifierKey,
320}
321
322impl std::cmp::PartialEq for ExtendedSpendingKey {
323    fn eq(&self, rhs: &ExtendedSpendingKey) -> bool {
324        self.depth == rhs.depth
325            && self.parent_fvk_tag == rhs.parent_fvk_tag
326            && self.child_index == rhs.child_index
327            && self.chain_code == rhs.chain_code
328            && self.expsk.ask == rhs.expsk.ask
329            && self.expsk.nsk == rhs.expsk.nsk
330            && self.expsk.ovk == rhs.expsk.ovk
331            && self.dk == rhs.dk
332    }
333}
334
335impl std::fmt::Debug for ExtendedSpendingKey {
336    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
337        write!(
338            f,
339            "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})",
340            self.depth, self.parent_fvk_tag, self.child_index
341        )
342    }
343}
344
345impl std::cmp::PartialEq for ExtendedFullViewingKey {
346    fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool {
347        self.depth == rhs.depth
348            && self.parent_fvk_tag == rhs.parent_fvk_tag
349            && self.child_index == rhs.child_index
350            && self.chain_code == rhs.chain_code
351            && self.fvk.vk.ak == rhs.fvk.vk.ak
352            && self.fvk.vk.nk == rhs.fvk.vk.nk
353            && self.fvk.ovk == rhs.fvk.ovk
354            && self.dk == rhs.dk
355    }
356}
357
358impl std::fmt::Debug for ExtendedFullViewingKey {
359    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
360        write!(
361            f,
362            "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})",
363            self.depth, self.parent_fvk_tag, self.child_index
364        )
365    }
366}
367
368impl ExtendedSpendingKey {
369    pub fn master(seed: &[u8]) -> Self {
370        let i = Blake2bParams::new()
371            .hash_length(64)
372            .personal(ZIP32_SAPLING_MASTER_PERSONALIZATION)
373            .hash(seed);
374
375        let sk_m = &i.as_bytes()[..32];
376        let mut c_m = [0u8; 32];
377        c_m.copy_from_slice(&i.as_bytes()[32..]);
378
379        ExtendedSpendingKey {
380            depth: 0,
381            parent_fvk_tag: FvkTag::master(),
382            child_index: ChildIndex::master(),
383            chain_code: ChainCode(c_m),
384            expsk: ExpandedSpendingKey::from_spending_key(sk_m),
385            dk: DiversifierKey::master(sk_m),
386        }
387    }
388
389    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
390        let depth = reader.read_u8()?;
391        let mut tag = [0; 4];
392        reader.read_exact(&mut tag)?;
393        let i = reader.read_u32::<LittleEndian>()?;
394        let mut c = [0; 32];
395        reader.read_exact(&mut c)?;
396        let expsk = ExpandedSpendingKey::read(&mut reader)?;
397        let mut dk = [0; 32];
398        reader.read_exact(&mut dk)?;
399
400        Ok(ExtendedSpendingKey {
401            depth,
402            parent_fvk_tag: FvkTag(tag),
403            child_index: ChildIndex::from_index(i),
404            chain_code: ChainCode(c),
405            expsk,
406            dk: DiversifierKey(dk),
407        })
408    }
409
410    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
411        writer.write_u8(self.depth)?;
412        writer.write_all(&self.parent_fvk_tag.0)?;
413        writer.write_u32::<LittleEndian>(self.child_index.value())?;
414        writer.write_all(&self.chain_code.0)?;
415        writer.write_all(&self.expsk.to_bytes())?;
416        writer.write_all(&self.dk.0)?;
417
418        Ok(())
419    }
420
421    /// Returns the child key corresponding to the path derived from the master key
422    pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Self {
423        let mut xsk = master.clone();
424        for &i in path.iter() {
425            xsk = xsk.derive_child(i);
426        }
427        xsk
428    }
429
430    #[must_use]
431    pub fn derive_child(&self, i: ChildIndex) -> Self {
432        let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
433        let tmp = match i {
434            ChildIndex::Hardened(i) => {
435                let mut le_i = [0; 4];
436                LittleEndian::write_u32(&mut le_i, i + (1 << 31));
437                prf_expand_vec(
438                    &self.chain_code.0,
439                    &[&[0x11], &self.expsk.to_bytes(), &self.dk.0, &le_i],
440                )
441            }
442            ChildIndex::NonHardened(i) => {
443                let mut le_i = [0; 4];
444                LittleEndian::write_u32(&mut le_i, i);
445                prf_expand_vec(
446                    &self.chain_code.0,
447                    &[&[0x12], &fvk.to_bytes(), &self.dk.0, &le_i],
448                )
449            }
450        };
451        let i_l = &tmp.as_bytes()[..32];
452        let mut c_i = [0u8; 32];
453        c_i.copy_from_slice(&tmp.as_bytes()[32..]);
454
455        ExtendedSpendingKey {
456            depth: self.depth + 1,
457            parent_fvk_tag: FvkFingerprint::from(&fvk).tag(),
458            child_index: i,
459            chain_code: ChainCode(c_i),
460            expsk: {
461                let mut ask = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
462                let mut nsk = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
463                ask.add_assign(&self.expsk.ask);
464                nsk.add_assign(&self.expsk.nsk);
465                let ovk = derive_child_ovk(&self.expsk.ovk, i_l);
466                ExpandedSpendingKey { ask, nsk, ovk }
467            },
468            dk: self.dk.derive_child(i_l),
469        }
470    }
471
472    /// Returns the address with the lowest valid diversifier index, along with
473    /// the diversifier index that generated that address.
474    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
475        ExtendedFullViewingKey::from(self).default_address()
476    }
477
478    /// Derives an internal spending key given an external spending key.
479    ///
480    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key).
481    #[must_use]
482    pub fn derive_internal(&self) -> Self {
483        let i = {
484            let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
485            let mut h = Blake2bParams::new()
486                .hash_length(32)
487                .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION)
488                .to_state();
489            h.update(&fvk.to_bytes());
490            h.update(&self.dk.0);
491            h.finalize()
492        };
493        let i_nsk = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array());
494        let r = prf_expand(i.as_bytes(), &[0x18]);
495        let r = r.as_bytes();
496        let nsk_internal = i_nsk + self.expsk.nsk;
497        let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
498        let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
499
500        ExtendedSpendingKey {
501            depth: self.depth,
502            parent_fvk_tag: self.parent_fvk_tag,
503            child_index: self.child_index,
504            chain_code: self.chain_code,
505            expsk: ExpandedSpendingKey {
506                ask: self.expsk.ask,
507                nsk: nsk_internal,
508                ovk: ovk_internal,
509            },
510            dk: dk_internal,
511        }
512    }
513}
514
515impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey {
516    fn from(xsk: &ExtendedSpendingKey) -> Self {
517        ExtendedFullViewingKey {
518            depth: xsk.depth,
519            parent_fvk_tag: xsk.parent_fvk_tag,
520            child_index: xsk.child_index,
521            chain_code: xsk.chain_code,
522            fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk),
523            dk: xsk.dk,
524        }
525    }
526}
527
528impl ExtendedFullViewingKey {
529    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
530        let depth = reader.read_u8()?;
531        let mut tag = [0; 4];
532        reader.read_exact(&mut tag)?;
533        let i = reader.read_u32::<LittleEndian>()?;
534        let mut c = [0; 32];
535        reader.read_exact(&mut c)?;
536        let fvk = FullViewingKey::read(&mut reader)?;
537        let mut dk = [0; 32];
538        reader.read_exact(&mut dk)?;
539
540        Ok(ExtendedFullViewingKey {
541            depth,
542            parent_fvk_tag: FvkTag(tag),
543            child_index: ChildIndex::from_index(i),
544            chain_code: ChainCode(c),
545            fvk,
546            dk: DiversifierKey(dk),
547        })
548    }
549
550    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
551        writer.write_u8(self.depth)?;
552        writer.write_all(&self.parent_fvk_tag.0)?;
553        writer.write_u32::<LittleEndian>(self.child_index.value())?;
554        writer.write_all(&self.chain_code.0)?;
555        writer.write_all(&self.fvk.to_bytes())?;
556        writer.write_all(&self.dk.0)?;
557
558        Ok(())
559    }
560
561    pub fn derive_child(&self, i: ChildIndex) -> Result<Self, ()> {
562        let tmp = match i {
563            ChildIndex::Hardened(_) => return Err(()),
564            ChildIndex::NonHardened(i) => {
565                let mut le_i = [0; 4];
566                LittleEndian::write_u32(&mut le_i, i);
567                prf_expand_vec(
568                    &self.chain_code.0,
569                    &[&[0x12], &self.fvk.to_bytes(), &self.dk.0, &le_i],
570                )
571            }
572        };
573        let i_l = &tmp.as_bytes()[..32];
574        let mut c_i = [0u8; 32];
575        c_i.copy_from_slice(&tmp.as_bytes()[32..]);
576
577        Ok(ExtendedFullViewingKey {
578            depth: self.depth + 1,
579            parent_fvk_tag: FvkFingerprint::from(&self.fvk).tag(),
580            child_index: i,
581            chain_code: ChainCode(c_i),
582            fvk: {
583                let i_ask = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
584                let i_nsk = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
585                let ak = (*SPENDING_KEY_GENERATOR * i_ask) + self.fvk.vk.ak;
586                let nk = (*PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk;
587
588                FullViewingKey {
589                    vk: ViewingKey { ak, nk },
590                    ovk: derive_child_ovk(&self.fvk.ovk, i_l),
591                }
592            },
593            dk: self.dk.derive_child(i_l),
594        })
595    }
596
597    /// Attempt to produce a payment address given the specified diversifier
598    /// index, and return None if the specified index does not produce a valid
599    /// diversifier.
600    pub fn address(&self, j: DiversifierIndex) -> Option<PaymentAddress> {
601        sapling_address(&self.fvk, &self.dk, j)
602    }
603
604    /// Search the diversifier space starting at diversifier index `j` for
605    /// one which will produce a valid diversifier, and return the payment address
606    /// constructed using that diversifier along with the index at which the
607    /// valid diversifier was found.
608    pub fn find_address(&self, j: DiversifierIndex) -> Option<(DiversifierIndex, PaymentAddress)> {
609        sapling_find_address(&self.fvk, &self.dk, j)
610    }
611
612    /// Returns the payment address corresponding to the smallest valid diversifier
613    /// index, along with that index.
614    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
615        sapling_default_address(&self.fvk, &self.dk)
616    }
617
618    /// Derives an internal full viewing key used for internal operations such
619    /// as change and auto-shielding. The internal FVK has the same spend authority
620    /// (the private key corresponding to ak) as the original, but viewing authority
621    /// only for internal transfers.
622    ///
623    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-full-viewing-key).
624    #[must_use]
625    pub fn derive_internal(&self) -> Self {
626        let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk);
627
628        ExtendedFullViewingKey {
629            depth: self.depth,
630            parent_fvk_tag: self.parent_fvk_tag,
631            child_index: self.child_index,
632            chain_code: self.chain_code,
633            fvk: fvk_internal,
634            dk: dk_internal,
635        }
636    }
637}
638
639#[cfg(test)]
640mod tests {
641    use super::*;
642
643    use ff::PrimeField;
644    use group::GroupEncoding;
645
646    #[test]
647    fn derive_nonhardened_child() {
648        let seed = [0; 32];
649        let xsk_m = ExtendedSpendingKey::master(&seed);
650        let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
651
652        let i_5 = ChildIndex::NonHardened(5);
653        let xsk_5 = xsk_m.derive_child(i_5);
654        let xfvk_5 = xfvk_m.derive_child(i_5);
655
656        assert!(xfvk_5.is_ok());
657        assert_eq!(ExtendedFullViewingKey::from(&xsk_5), xfvk_5.unwrap());
658    }
659
660    #[test]
661    fn derive_hardened_child() {
662        let seed = [0; 32];
663        let xsk_m = ExtendedSpendingKey::master(&seed);
664        let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
665
666        let i_5h = ChildIndex::Hardened(5);
667        let xsk_5h = xsk_m.derive_child(i_5h);
668        let xfvk_5h = xfvk_m.derive_child(i_5h);
669
670        // Cannot derive a hardened child from an ExtendedFullViewingKey
671        assert!(xfvk_5h.is_err());
672        let xfvk_5h = ExtendedFullViewingKey::from(&xsk_5h);
673
674        let i_7 = ChildIndex::NonHardened(7);
675        let xsk_5h_7 = xsk_5h.derive_child(i_7);
676        let xfvk_5h_7 = xfvk_5h.derive_child(i_7);
677
678        // But we *can* derive a non-hardened child from a hardened parent
679        assert!(xfvk_5h_7.is_ok());
680        assert_eq!(ExtendedFullViewingKey::from(&xsk_5h_7), xfvk_5h_7.unwrap());
681    }
682
683    #[test]
684    fn path() {
685        let seed = [0; 32];
686        let xsk_m = ExtendedSpendingKey::master(&seed);
687
688        let xsk_5h = xsk_m.derive_child(ChildIndex::Hardened(5));
689        assert_eq!(
690            ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::Hardened(5)]),
691            xsk_5h
692        );
693
694        let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::NonHardened(7));
695        assert_eq!(
696            ExtendedSpendingKey::from_path(
697                &xsk_m,
698                &[ChildIndex::Hardened(5), ChildIndex::NonHardened(7)]
699            ),
700            xsk_5h_7
701        );
702    }
703
704    #[test]
705    fn diversifier() {
706        let dk = DiversifierKey([0; 32]);
707        let j_0 = DiversifierIndex::new();
708        let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
709        let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
710        let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
711        // Computed using this Rust implementation
712        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
713        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
714
715        // j = 0
716        let d_j = dk.diversifier(j_0).unwrap();
717        assert_eq!(d_j.0, d_0);
718        assert_eq!(dk.diversifier_index(&Diversifier(d_0)), j_0);
719
720        // j = 1
721        assert_eq!(dk.diversifier(j_1), None);
722
723        // j = 2
724        assert_eq!(dk.diversifier(j_2), None);
725
726        // j = 3
727        let d_j = dk.diversifier(j_3).unwrap();
728        assert_eq!(d_j.0, d_3);
729        assert_eq!(dk.diversifier_index(&Diversifier(d_3)), j_3);
730    }
731
732    #[test]
733    fn find_diversifier() {
734        let dk = DiversifierKey([0; 32]);
735        let j_0 = DiversifierIndex::new();
736        let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
737        let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
738        let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
739        // Computed using this Rust implementation
740        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
741        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
742
743        // j = 0
744        let (j, d_j) = dk.find_diversifier(j_0).unwrap();
745        assert_eq!(j, j_0);
746        assert_eq!(d_j.0, d_0);
747
748        // j = 1
749        let (j, d_j) = dk.find_diversifier(j_1).unwrap();
750        assert_eq!(j, j_3);
751        assert_eq!(d_j.0, d_3);
752
753        // j = 2
754        let (j, d_j) = dk.find_diversifier(j_2).unwrap();
755        assert_eq!(j, j_3);
756        assert_eq!(d_j.0, d_3);
757
758        // j = 3
759        let (j, d_j) = dk.find_diversifier(j_3).unwrap();
760        assert_eq!(j, j_3);
761        assert_eq!(d_j.0, d_3);
762    }
763
764    #[test]
765    fn address() {
766        let seed = [0; 32];
767        let xsk_m = ExtendedSpendingKey::master(&seed);
768        let xfvk_m = ExtendedFullViewingKey::from(&xsk_m);
769        let j_0 = DiversifierIndex::new();
770        let addr_m = xfvk_m.address(j_0).unwrap();
771        assert_eq!(
772            addr_m.diversifier().0,
773            // Computed using this Rust implementation
774            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
775        );
776
777        let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
778        assert_eq!(xfvk_m.address(j_1), None);
779    }
780
781    #[test]
782    fn default_address() {
783        let seed = [0; 32];
784        let xsk_m = ExtendedSpendingKey::master(&seed);
785        let (j_m, addr_m) = xsk_m.default_address();
786        assert_eq!(j_m.0, [0; 11]);
787        assert_eq!(
788            addr_m.diversifier().0,
789            // Computed using this Rust implementation
790            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
791        );
792    }
793
794    #[test]
795    fn read_write() {
796        let seed = [0; 32];
797        let xsk = ExtendedSpendingKey::master(&seed);
798        let fvk = ExtendedFullViewingKey::from(&xsk);
799
800        let mut ser = vec![];
801        xsk.write(&mut ser).unwrap();
802        let xsk2 = ExtendedSpendingKey::read(&ser[..]).unwrap();
803        assert_eq!(xsk2, xsk);
804
805        let mut ser = vec![];
806        fvk.write(&mut ser).unwrap();
807        let fvk2 = ExtendedFullViewingKey::read(&ser[..]).unwrap();
808        assert_eq!(fvk2, fvk);
809    }
810
811    #[test]
812    fn test_vectors() {
813        struct TestVector {
814            ask: Option<[u8; 32]>,
815            nsk: Option<[u8; 32]>,
816            ovk: [u8; 32],
817            dk: [u8; 32],
818            c: [u8; 32],
819            ak: [u8; 32],
820            nk: [u8; 32],
821            ivk: [u8; 32],
822            xsk: Option<[u8; 169]>,
823            xfvk: [u8; 169],
824            fp: [u8; 32],
825            d0: Option<[u8; 11]>,
826            d1: Option<[u8; 11]>,
827            d2: Option<[u8; 11]>,
828            dmax: Option<[u8; 11]>,
829            internal_nsk: Option<[u8; 32]>,
830            internal_ovk: [u8; 32],
831            internal_dk: [u8; 32],
832            internal_nk: [u8; 32],
833            internal_ivk: [u8; 32],
834            internal_xsk: Option<[u8; 169]>,
835            internal_xfvk: [u8; 169],
836            internal_fp: [u8; 32],
837        }
838
839        // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py
840        let test_vectors = vec![
841            TestVector {
842                ask: Some([
843                    0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86,
844                    0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6,
845                    0x07, 0xc9, 0x54, 0x74, 0x25, 0x06,
846                ]),
847                nsk: Some([
848                    0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f,
849                    0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f,
850                    0x74, 0x8a, 0x88, 0x21, 0xea, 0x06,
851                ]),
852                ovk: [
853                    0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8,
854                    0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83,
855                    0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21,
856                ],
857                dk: [
858                    0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91,
859                    0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc,
860                    0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
861                ],
862                c: [
863                    0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27,
864                    0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55,
865                    0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e,
866                ],
867                ak: [
868                    0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc,
869                    0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64,
870                    0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71,
871                ],
872                nk: [
873                    0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba,
874                    0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93,
875                    0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06,
876                ],
877                ivk: [
878                    0x48, 0x47, 0xa1, 0x30, 0xe7, 0x99, 0xd3, 0xdb, 0xea, 0x36, 0xa1, 0xc1, 0x64,
879                    0x67, 0xd6, 0x21, 0xfb, 0x2d, 0x80, 0xe3, 0x0b, 0x3b, 0x1d, 0x1a, 0x42, 0x68,
880                    0x93, 0x41, 0x5d, 0xad, 0x66, 0x01,
881                ],
882                xsk: Some([
883                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
884                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
885                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
886                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
887                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
888                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x82, 0x04, 0xed, 0xe8, 0x3b,
889                    0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a,
890                    0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea,
891                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
892                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
893                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
894                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
895                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
896                ]),
897                xfvk: [
898                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
899                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
900                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
901                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
902                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
903                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xdc, 0xe8, 0xe7, 0xed, 0xec,
904                    0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c,
905                    0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01,
906                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
907                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
908                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
909                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
910                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
911                ],
912                fp: [
913                    0x14, 0xc2, 0x71, 0x3a, 0xdc, 0xe9, 0x3a, 0x83, 0x0e, 0xa8, 0x3a, 0x05, 0x19,
914                    0x08, 0xb7, 0x44, 0x77, 0x83, 0xf5, 0xd1, 0x06, 0xc0, 0x98, 0x5e, 0x02, 0x55,
915                    0x0e, 0x42, 0x6f, 0x27, 0x59, 0x7c,
916                ],
917                d0: Some([
918                    0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89,
919                ]),
920                d1: Some([
921                    0x48, 0xea, 0x17, 0xa1, 0x99, 0xc8, 0x4b, 0xd1, 0xba, 0xa5, 0xd4,
922                ]),
923                d2: None,
924                dmax: None,
925                internal_nsk: Some([
926                    0x51, 0x12, 0x33, 0x63, 0x6b, 0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a,
927                    0x7d, 0x8f, 0x49, 0xef, 0xd7, 0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56,
928                    0x68, 0x76, 0x46, 0xea, 0xab, 0x07,
929                ]),
930                internal_ovk: [
931                    0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65, 0x4d,
932                    0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72, 0x3b,
933                    0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a,
934                ],
935                internal_dk: [
936                    0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75, 0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5,
937                    0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16, 0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38,
938                    0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
939                ],
940                internal_nk: [
941                    0xa3, 0x83, 0x1a, 0x5c, 0x69, 0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c,
942                    0x50, 0x8b, 0x79, 0x91, 0xcd, 0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27,
943                    0xa6, 0xae, 0x15, 0xe7, 0x2f, 0xb5,
944                ],
945                internal_ivk: [
946                    0x79, 0x05, 0x77, 0x32, 0x1c, 0x51, 0x18, 0x04, 0x63, 0x6e, 0xe6, 0xba, 0xa4,
947                    0xee, 0xa7, 0x79, 0xb4, 0xa4, 0x6a, 0x5a, 0x12, 0xf8, 0x5d, 0x36, 0x50, 0x74,
948                    0xa0, 0x9d, 0x05, 0x4f, 0x34, 0x01,
949                ],
950                internal_xsk: Some([
951                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
952                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
953                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
954                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
955                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
956                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x51, 0x12, 0x33, 0x63, 0x6b,
957                    0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a, 0x7d, 0x8f, 0x49, 0xef, 0xd7,
958                    0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56, 0x68, 0x76, 0x46, 0xea, 0xab,
959                    0x07, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
960                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
961                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
962                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
963                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
964                ]),
965                internal_xfvk: [
966                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
967                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
968                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
969                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
970                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
971                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xa3, 0x83, 0x1a, 0x5c, 0x69,
972                    0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c, 0x50, 0x8b, 0x79, 0x91, 0xcd,
973                    0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27, 0xa6, 0xae, 0x15, 0xe7, 0x2f,
974                    0xb5, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
975                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
976                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
977                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
978                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
979                ],
980                internal_fp: [
981                    0x82, 0x64, 0xed, 0xec, 0x63, 0xb1, 0x55, 0x00, 0x1d, 0x84, 0x96, 0x68, 0x5c,
982                    0xc7, 0xc2, 0x1e, 0xa9, 0x57, 0xc6, 0xf5, 0x91, 0x09, 0x0a, 0x1c, 0x20, 0xe5,
983                    0x2a, 0x41, 0x89, 0xb8, 0xbb, 0x96,
984                ],
985            },
986            TestVector {
987                ask: Some([
988                    0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6, 0x8c, 0x42,
989                    0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61, 0xd7, 0xb8,
990                    0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c,
991                ]),
992                nsk: Some([
993                    0xe7, 0xa3, 0x29, 0x88, 0xfd, 0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62,
994                    0xe6, 0x29, 0xc2, 0xe9, 0x6b, 0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd,
995                    0x18, 0x10, 0xff, 0x6b, 0xba, 0x01,
996                ]),
997                ovk: [
998                    0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, 0xcf,
999                    0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, 0x1b,
1000                    0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74,
1001                ],
1002                dk: [
1003                    0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b,
1004                    0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51,
1005                    0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc,
1006                ],
1007                c: [
1008                    0x01, 0x47, 0x11, 0x0c, 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05,
1009                    0xc5, 0xe7, 0x90, 0xa5, 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43,
1010                    0x8a, 0x67, 0x05, 0xda, 0xbc, 0xe6,
1011                ],
1012                ak: [
1013                    0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25, 0xaf, 0x2f,
1014                    0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e, 0xa1, 0xbf,
1015                    0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b,
1016                ],
1017                nk: [
1018                    0x61, 0x52, 0xeb, 0x7f, 0xdb, 0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17,
1019                    0xea, 0x4b, 0x6f, 0xd3, 0x40, 0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb,
1020                    0xec, 0xeb, 0x41, 0xba, 0x45, 0x2a,
1021                ],
1022                ivk: [
1023                    0x15, 0x5a, 0x8e, 0xe2, 0x05, 0xd3, 0x87, 0x2d, 0x12, 0xf8, 0xa3, 0xe6, 0x39,
1024                    0x91, 0x46, 0x33, 0xc2, 0x3c, 0xde, 0x1f, 0x30, 0xed, 0x50, 0x51, 0xe5, 0x21,
1025                    0x30, 0xb1, 0xd0, 0x10, 0x4c, 0x06,
1026                ],
1027                xsk: Some([
1028                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c,
1029                    0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5,
1030                    0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda,
1031                    0xbc, 0xe6, 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6,
1032                    0x8c, 0x42, 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61,
1033                    0xd7, 0xb8, 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, 0xe7, 0xa3, 0x29, 0x88, 0xfd,
1034                    0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62, 0xe6, 0x29, 0xc2, 0xe9, 0x6b,
1035                    0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd, 0x18, 0x10, 0xff, 0x6b, 0xba,
1036                    0x01, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef,
1037                    0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8,
1038                    0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7,
1039                    0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7,
1040                    0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc,
1041                ]),
1042                xfvk: [
1043                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c,
1044                    0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5,
1045                    0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda,
1046                    0xbc, 0xe6, 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25,
1047                    0xaf, 0x2f, 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e,
1048                    0xa1, 0xbf, 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, 0x61, 0x52, 0xeb, 0x7f, 0xdb,
1049                    0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17, 0xea, 0x4b, 0x6f, 0xd3, 0x40,
1050                    0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xec, 0xeb, 0x41, 0xba, 0x45,
1051                    0x2a, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef,
1052                    0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8,
1053                    0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7,
1054                    0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7,
1055                    0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc,
1056                ],
1057                fp: [
1058                    0xdb, 0x99, 0x9e, 0x07, 0x1d, 0xcb, 0x58, 0xdd, 0x93, 0x02, 0x9a, 0xe6, 0x97,
1059                    0x05, 0x3e, 0x90, 0xed, 0xb3, 0x59, 0xd1, 0xa1, 0xb7, 0xa1, 0x25, 0x16, 0x7e,
1060                    0xfb, 0xe9, 0x28, 0x06, 0x84, 0x23,
1061                ],
1062                d0: Some([
1063                    0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81,
1064                ]),
1065                d1: None,
1066                d2: Some([
1067                    0x57, 0x49, 0xa1, 0x33, 0x52, 0xbc, 0x22, 0x3e, 0x30, 0x80, 0x78,
1068                ]),
1069                dmax: Some([
1070                    0x63, 0x89, 0x57, 0x4c, 0xde, 0x0f, 0xbb, 0xc6, 0x36, 0x81, 0x31,
1071                ]),
1072                internal_nsk: Some([
1073                    0x74, 0x92, 0x9f, 0x79, 0x0c, 0x11, 0xdc, 0xab, 0x3a, 0x2f, 0x93, 0x12, 0x35,
1074                    0xcd, 0xb2, 0x67, 0xf5, 0xa3, 0x1b, 0x9f, 0x13, 0x9f, 0x2c, 0x9f, 0xd8, 0x16,
1075                    0xb0, 0x44, 0x4f, 0xb8, 0x05, 0x05,
1076                ]),
1077                internal_ovk: [
1078                    0x0c, 0xd4, 0xd7, 0xc5, 0xcc, 0x7f, 0x53, 0x4b, 0x96, 0xd2, 0x41, 0x82, 0xa3,
1079                    0x14, 0x65, 0xb4, 0x78, 0x11, 0x05, 0x48, 0x9c, 0xd1, 0x0d, 0x50, 0x0c, 0xf5,
1080                    0x29, 0x5a, 0x6f, 0xd8, 0x18, 0xcc,
1081                ],
1082                internal_dk: [
1083                    0xd2, 0x78, 0xb7, 0x2c, 0x62, 0x1d, 0x19, 0xcb, 0x00, 0xf9, 0x70, 0x07, 0x9c,
1084                    0x89, 0x22, 0x76, 0x1c, 0xdd, 0x3a, 0xe7, 0xf2, 0x7b, 0x18, 0x47, 0xc5, 0x53,
1085                    0x60, 0xdb, 0xeb, 0xf6, 0x54, 0x92,
1086                ],
1087                internal_nk: [
1088                    0x2b, 0x5c, 0x78, 0xa2, 0xfb, 0xa5, 0x01, 0x9c, 0x15, 0xa7, 0x51, 0x50, 0x2b,
1089                    0xa9, 0x91, 0x6f, 0xae, 0xda, 0xe1, 0xfc, 0x14, 0xdc, 0x81, 0xb0, 0xb8, 0x35,
1090                    0xf2, 0xbf, 0x95, 0xc0, 0x68, 0xe8,
1091                ],
1092                internal_ivk: [
1093                    0xdf, 0x44, 0x54, 0xa6, 0x76, 0xd1, 0xde, 0x32, 0xe2, 0x0a, 0xe6, 0x28, 0x7a,
1094                    0x92, 0xfa, 0xfe, 0xfb, 0xbb, 0x3e, 0x54, 0xb5, 0x88, 0xc8, 0xda, 0x28, 0x07,
1095                    0xec, 0x43, 0x68, 0x2c, 0x85, 0x00,
1096                ],
1097                internal_xsk: Some([
1098                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c,
1099                    0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5,
1100                    0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda,
1101                    0xbc, 0xe6, 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6,
1102                    0x8c, 0x42, 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61,
1103                    0xd7, 0xb8, 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, 0x74, 0x92, 0x9f, 0x79, 0x0c,
1104                    0x11, 0xdc, 0xab, 0x3a, 0x2f, 0x93, 0x12, 0x35, 0xcd, 0xb2, 0x67, 0xf5, 0xa3,
1105                    0x1b, 0x9f, 0x13, 0x9f, 0x2c, 0x9f, 0xd8, 0x16, 0xb0, 0x44, 0x4f, 0xb8, 0x05,
1106                    0x05, 0x0c, 0xd4, 0xd7, 0xc5, 0xcc, 0x7f, 0x53, 0x4b, 0x96, 0xd2, 0x41, 0x82,
1107                    0xa3, 0x14, 0x65, 0xb4, 0x78, 0x11, 0x05, 0x48, 0x9c, 0xd1, 0x0d, 0x50, 0x0c,
1108                    0xf5, 0x29, 0x5a, 0x6f, 0xd8, 0x18, 0xcc, 0xd2, 0x78, 0xb7, 0x2c, 0x62, 0x1d,
1109                    0x19, 0xcb, 0x00, 0xf9, 0x70, 0x07, 0x9c, 0x89, 0x22, 0x76, 0x1c, 0xdd, 0x3a,
1110                    0xe7, 0xf2, 0x7b, 0x18, 0x47, 0xc5, 0x53, 0x60, 0xdb, 0xeb, 0xf6, 0x54, 0x92,
1111                ]),
1112                internal_xfvk: [
1113                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c,
1114                    0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5,
1115                    0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda,
1116                    0xbc, 0xe6, 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25,
1117                    0xaf, 0x2f, 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e,
1118                    0xa1, 0xbf, 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, 0x2b, 0x5c, 0x78, 0xa2, 0xfb,
1119                    0xa5, 0x01, 0x9c, 0x15, 0xa7, 0x51, 0x50, 0x2b, 0xa9, 0x91, 0x6f, 0xae, 0xda,
1120                    0xe1, 0xfc, 0x14, 0xdc, 0x81, 0xb0, 0xb8, 0x35, 0xf2, 0xbf, 0x95, 0xc0, 0x68,
1121                    0xe8, 0x0c, 0xd4, 0xd7, 0xc5, 0xcc, 0x7f, 0x53, 0x4b, 0x96, 0xd2, 0x41, 0x82,
1122                    0xa3, 0x14, 0x65, 0xb4, 0x78, 0x11, 0x05, 0x48, 0x9c, 0xd1, 0x0d, 0x50, 0x0c,
1123                    0xf5, 0x29, 0x5a, 0x6f, 0xd8, 0x18, 0xcc, 0xd2, 0x78, 0xb7, 0x2c, 0x62, 0x1d,
1124                    0x19, 0xcb, 0x00, 0xf9, 0x70, 0x07, 0x9c, 0x89, 0x22, 0x76, 0x1c, 0xdd, 0x3a,
1125                    0xe7, 0xf2, 0x7b, 0x18, 0x47, 0xc5, 0x53, 0x60, 0xdb, 0xeb, 0xf6, 0x54, 0x92,
1126                ],
1127                internal_fp: [
1128                    0x0d, 0xe5, 0x83, 0xca, 0x50, 0x2b, 0x1c, 0x4b, 0x87, 0xca, 0xc8, 0xc9, 0x78,
1129                    0x6c, 0x61, 0x9b, 0x79, 0xe1, 0x69, 0xb4, 0x15, 0x61, 0xf2, 0x44, 0xee, 0xec,
1130                    0x86, 0x86, 0xb8, 0xdb, 0xc4, 0xe1,
1131                ],
1132            },
1133            TestVector {
1134                ask: Some([
1135                    0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4, 0x1f, 0xc8,
1136                    0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c, 0x92, 0xda,
1137                    0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d,
1138                ]),
1139                nsk: Some([
1140                    0x37, 0x78, 0x05, 0x9d, 0xc5, 0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f,
1141                    0x95, 0x1b, 0xbd, 0xe9, 0x2f, 0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c,
1142                    0x5c, 0x27, 0x3a, 0xa6, 0x99, 0x0c,
1143                ]),
1144                ovk: [
1145                    0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47,
1146                    0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93,
1147                    0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc,
1148                ],
1149                dk: [
1150                    0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10,
1151                    0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0,
1152                    0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37,
1153                ],
1154                c: [
1155                    0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b,
1156                    0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42,
1157                    0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35,
1158                ],
1159                ak: [
1160                    0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49,
1161                    0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e,
1162                    0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41,
1163                ],
1164                nk: [
1165                    0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae,
1166                    0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f,
1167                    0x30, 0x77, 0x25, 0xe2, 0xee, 0x11,
1168                ],
1169                ivk: [
1170                    0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30,
1171                    0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a,
1172                    0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04,
1173                ],
1174                xsk: Some([
1175                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1176                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1177                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1178                    0xd4, 0x35, 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4,
1179                    0x1f, 0xc8, 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c,
1180                    0x92, 0xda, 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, 0x37, 0x78, 0x05, 0x9d, 0xc5,
1181                    0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f, 0x95, 0x1b, 0xbd, 0xe9, 0x2f,
1182                    0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c, 0x5c, 0x27, 0x3a, 0xa6, 0x99,
1183                    0x0c, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb,
1184                    0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1,
1185                    0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff,
1186                    0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4,
1187                    0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37,
1188                ]),
1189                xfvk: [
1190                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1191                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1192                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1193                    0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e,
1194                    0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4,
1195                    0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16,
1196                    0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc,
1197                    0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee,
1198                    0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb,
1199                    0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1,
1200                    0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff,
1201                    0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4,
1202                    0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37,
1203                ],
1204                fp: [
1205                    0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40,
1206                    0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e,
1207                    0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf,
1208                ],
1209                d0: Some([
1210                    0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41,
1211                ]),
1212                d1: Some([
1213                    0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68,
1214                ]),
1215                d2: None,
1216                dmax: None,
1217                internal_nsk: Some([
1218                    0x34, 0x78, 0x65, 0xac, 0xf4, 0x7e, 0x50, 0x45, 0x38, 0xf5, 0xef, 0x8b, 0x04,
1219                    0x70, 0x20, 0x80, 0xe6, 0x09, 0x1c, 0xda, 0x57, 0x97, 0xcd, 0x7d, 0x23, 0x5a,
1220                    0x54, 0x6e, 0xb1, 0x0f, 0x55, 0x08,
1221                ]),
1222                internal_ovk: [
1223                    0xdd, 0xba, 0xc2, 0xa4, 0x93, 0xf5, 0x3c, 0x3b, 0x09, 0x33, 0xd9, 0x13, 0xde,
1224                    0xf8, 0x88, 0x48, 0x65, 0x4c, 0x08, 0x7c, 0x12, 0x60, 0x9d, 0xf0, 0x1b, 0xaf,
1225                    0x94, 0x05, 0xce, 0x78, 0x04, 0xfd,
1226                ],
1227                internal_dk: [
1228                    0x60, 0x2c, 0xd3, 0x17, 0xb7, 0xce, 0xa1, 0x1e, 0x8c, 0xc7, 0xae, 0x2e, 0xa4,
1229                    0x05, 0xb4, 0x0d, 0x46, 0xb1, 0x59, 0x2a, 0x30, 0xf0, 0xcb, 0x6e, 0x8c, 0x4f,
1230                    0x17, 0xd7, 0xf7, 0xc4, 0x7f, 0xeb,
1231                ],
1232                internal_nk: [
1233                    0xf9, 0x12, 0x87, 0xc0, 0x6e, 0x30, 0xb6, 0x5e, 0xa1, 0xbd, 0xb7, 0x16, 0xb2,
1234                    0x31, 0xde, 0x67, 0x78, 0xa5, 0xd8, 0x0e, 0xe5, 0xcd, 0x9c, 0x06, 0x0d, 0x1a,
1235                    0xba, 0xca, 0xe0, 0xaa, 0xe2, 0x3b,
1236                ],
1237                internal_ivk: [
1238                    0x1d, 0x59, 0xea, 0x20, 0x17, 0x88, 0x18, 0x64, 0xd2, 0x4e, 0xad, 0xb5, 0xcf,
1239                    0x34, 0x68, 0xa4, 0x1a, 0x1b, 0x2a, 0xaa, 0x0d, 0x1b, 0x3a, 0x72, 0xc6, 0xda,
1240                    0x9c, 0xe6, 0x50, 0x2a, 0x0a, 0x05,
1241                ],
1242                internal_xsk: Some([
1243                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1244                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1245                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1246                    0xd4, 0x35, 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4,
1247                    0x1f, 0xc8, 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c,
1248                    0x92, 0xda, 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, 0x34, 0x78, 0x65, 0xac, 0xf4,
1249                    0x7e, 0x50, 0x45, 0x38, 0xf5, 0xef, 0x8b, 0x04, 0x70, 0x20, 0x80, 0xe6, 0x09,
1250                    0x1c, 0xda, 0x57, 0x97, 0xcd, 0x7d, 0x23, 0x5a, 0x54, 0x6e, 0xb1, 0x0f, 0x55,
1251                    0x08, 0xdd, 0xba, 0xc2, 0xa4, 0x93, 0xf5, 0x3c, 0x3b, 0x09, 0x33, 0xd9, 0x13,
1252                    0xde, 0xf8, 0x88, 0x48, 0x65, 0x4c, 0x08, 0x7c, 0x12, 0x60, 0x9d, 0xf0, 0x1b,
1253                    0xaf, 0x94, 0x05, 0xce, 0x78, 0x04, 0xfd, 0x60, 0x2c, 0xd3, 0x17, 0xb7, 0xce,
1254                    0xa1, 0x1e, 0x8c, 0xc7, 0xae, 0x2e, 0xa4, 0x05, 0xb4, 0x0d, 0x46, 0xb1, 0x59,
1255                    0x2a, 0x30, 0xf0, 0xcb, 0x6e, 0x8c, 0x4f, 0x17, 0xd7, 0xf7, 0xc4, 0x7f, 0xeb,
1256                ]),
1257                internal_xfvk: [
1258                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1259                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1260                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1261                    0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e,
1262                    0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4,
1263                    0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0xf9, 0x12, 0x87, 0xc0, 0x6e,
1264                    0x30, 0xb6, 0x5e, 0xa1, 0xbd, 0xb7, 0x16, 0xb2, 0x31, 0xde, 0x67, 0x78, 0xa5,
1265                    0xd8, 0x0e, 0xe5, 0xcd, 0x9c, 0x06, 0x0d, 0x1a, 0xba, 0xca, 0xe0, 0xaa, 0xe2,
1266                    0x3b, 0xdd, 0xba, 0xc2, 0xa4, 0x93, 0xf5, 0x3c, 0x3b, 0x09, 0x33, 0xd9, 0x13,
1267                    0xde, 0xf8, 0x88, 0x48, 0x65, 0x4c, 0x08, 0x7c, 0x12, 0x60, 0x9d, 0xf0, 0x1b,
1268                    0xaf, 0x94, 0x05, 0xce, 0x78, 0x04, 0xfd, 0x60, 0x2c, 0xd3, 0x17, 0xb7, 0xce,
1269                    0xa1, 0x1e, 0x8c, 0xc7, 0xae, 0x2e, 0xa4, 0x05, 0xb4, 0x0d, 0x46, 0xb1, 0x59,
1270                    0x2a, 0x30, 0xf0, 0xcb, 0x6e, 0x8c, 0x4f, 0x17, 0xd7, 0xf7, 0xc4, 0x7f, 0xeb,
1271                ],
1272                internal_fp: [
1273                    0x30, 0xfe, 0x0d, 0x61, 0x0f, 0x94, 0x7b, 0x2c, 0x26, 0x0e, 0x7b, 0x29, 0xe7,
1274                    0x9e, 0x5c, 0x2e, 0x7d, 0x3e, 0x14, 0xab, 0xf9, 0x79, 0xf6, 0x40, 0x6d, 0x07,
1275                    0xba, 0xf8, 0xfa, 0xdd, 0xf4, 0x95,
1276                ],
1277            },
1278            TestVector {
1279                ask: None,
1280                nsk: None,
1281                ovk: [
1282                    0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47,
1283                    0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93,
1284                    0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc,
1285                ],
1286                dk: [
1287                    0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10,
1288                    0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0,
1289                    0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37,
1290                ],
1291                c: [
1292                    0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b,
1293                    0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42,
1294                    0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35,
1295                ],
1296                ak: [
1297                    0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49,
1298                    0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e,
1299                    0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41,
1300                ],
1301                nk: [
1302                    0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae,
1303                    0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f,
1304                    0x30, 0x77, 0x25, 0xe2, 0xee, 0x11,
1305                ],
1306                ivk: [
1307                    0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30,
1308                    0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a,
1309                    0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04,
1310                ],
1311                xsk: None,
1312                xfvk: [
1313                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1314                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1315                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1316                    0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e,
1317                    0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4,
1318                    0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16,
1319                    0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc,
1320                    0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee,
1321                    0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb,
1322                    0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1,
1323                    0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff,
1324                    0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4,
1325                    0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37,
1326                ],
1327                fp: [
1328                    0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40,
1329                    0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e,
1330                    0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf,
1331                ],
1332                d0: Some([
1333                    0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41,
1334                ]),
1335                d1: Some([
1336                    0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68,
1337                ]),
1338                d2: None,
1339                dmax: None,
1340                internal_nsk: None,
1341                internal_ovk: [
1342                    0xdd, 0xba, 0xc2, 0xa4, 0x93, 0xf5, 0x3c, 0x3b, 0x09, 0x33, 0xd9, 0x13, 0xde,
1343                    0xf8, 0x88, 0x48, 0x65, 0x4c, 0x08, 0x7c, 0x12, 0x60, 0x9d, 0xf0, 0x1b, 0xaf,
1344                    0x94, 0x05, 0xce, 0x78, 0x04, 0xfd,
1345                ],
1346                internal_dk: [
1347                    0x60, 0x2c, 0xd3, 0x17, 0xb7, 0xce, 0xa1, 0x1e, 0x8c, 0xc7, 0xae, 0x2e, 0xa4,
1348                    0x05, 0xb4, 0x0d, 0x46, 0xb1, 0x59, 0x2a, 0x30, 0xf0, 0xcb, 0x6e, 0x8c, 0x4f,
1349                    0x17, 0xd7, 0xf7, 0xc4, 0x7f, 0xeb,
1350                ],
1351                internal_nk: [
1352                    0xf9, 0x12, 0x87, 0xc0, 0x6e, 0x30, 0xb6, 0x5e, 0xa1, 0xbd, 0xb7, 0x16, 0xb2,
1353                    0x31, 0xde, 0x67, 0x78, 0xa5, 0xd8, 0x0e, 0xe5, 0xcd, 0x9c, 0x06, 0x0d, 0x1a,
1354                    0xba, 0xca, 0xe0, 0xaa, 0xe2, 0x3b,
1355                ],
1356                internal_ivk: [
1357                    0x1d, 0x59, 0xea, 0x20, 0x17, 0x88, 0x18, 0x64, 0xd2, 0x4e, 0xad, 0xb5, 0xcf,
1358                    0x34, 0x68, 0xa4, 0x1a, 0x1b, 0x2a, 0xaa, 0x0d, 0x1b, 0x3a, 0x72, 0xc6, 0xda,
1359                    0x9c, 0xe6, 0x50, 0x2a, 0x0a, 0x05,
1360                ],
1361                internal_xsk: None,
1362                internal_xfvk: [
1363                    0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4,
1364                    0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3,
1365                    0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8,
1366                    0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e,
1367                    0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4,
1368                    0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0xf9, 0x12, 0x87, 0xc0, 0x6e,
1369                    0x30, 0xb6, 0x5e, 0xa1, 0xbd, 0xb7, 0x16, 0xb2, 0x31, 0xde, 0x67, 0x78, 0xa5,
1370                    0xd8, 0x0e, 0xe5, 0xcd, 0x9c, 0x06, 0x0d, 0x1a, 0xba, 0xca, 0xe0, 0xaa, 0xe2,
1371                    0x3b, 0xdd, 0xba, 0xc2, 0xa4, 0x93, 0xf5, 0x3c, 0x3b, 0x09, 0x33, 0xd9, 0x13,
1372                    0xde, 0xf8, 0x88, 0x48, 0x65, 0x4c, 0x08, 0x7c, 0x12, 0x60, 0x9d, 0xf0, 0x1b,
1373                    0xaf, 0x94, 0x05, 0xce, 0x78, 0x04, 0xfd, 0x60, 0x2c, 0xd3, 0x17, 0xb7, 0xce,
1374                    0xa1, 0x1e, 0x8c, 0xc7, 0xae, 0x2e, 0xa4, 0x05, 0xb4, 0x0d, 0x46, 0xb1, 0x59,
1375                    0x2a, 0x30, 0xf0, 0xcb, 0x6e, 0x8c, 0x4f, 0x17, 0xd7, 0xf7, 0xc4, 0x7f, 0xeb,
1376                ],
1377                internal_fp: [
1378                    0x30, 0xfe, 0x0d, 0x61, 0x0f, 0x94, 0x7b, 0x2c, 0x26, 0x0e, 0x7b, 0x29, 0xe7,
1379                    0x9e, 0x5c, 0x2e, 0x7d, 0x3e, 0x14, 0xab, 0xf9, 0x79, 0xf6, 0x40, 0x6d, 0x07,
1380                    0xba, 0xf8, 0xfa, 0xdd, 0xf4, 0x95,
1381                ],
1382            },
1383            TestVector {
1384                ask: None,
1385                nsk: None,
1386                ovk: [
1387                    0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee, 0xe8,
1388                    0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44, 0x78,
1389                    0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6,
1390                ],
1391                dk: [
1392                    0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb, 0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce,
1393                    0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8, 0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9,
1394                    0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e,
1395                ],
1396                c: [
1397                    0x8d, 0x93, 0x7b, 0xcf, 0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4,
1398                    0x03, 0x36, 0x7b, 0x1f, 0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c,
1399                    0x5a, 0x4a, 0xa7, 0xd6, 0xe7, 0xe8,
1400                ],
1401                ak: [
1402                    0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3, 0x26, 0xd7,
1403                    0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32, 0x8d, 0x64,
1404                    0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3,
1405                ],
1406                nk: [
1407                    0xdb, 0x88, 0x04, 0x9e, 0x02, 0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d,
1408                    0xb2, 0xab, 0xed, 0x50, 0x0b, 0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99,
1409                    0x76, 0x4b, 0x81, 0xc0, 0x66, 0x4f,
1410                ],
1411                ivk: [
1412                    0xb0, 0xa5, 0xf3, 0x37, 0x23, 0x2f, 0x2c, 0x3d, 0xac, 0x70, 0xc2, 0xa4, 0x10,
1413                    0xfa, 0x56, 0x1f, 0xc4, 0x5d, 0x8c, 0xc5, 0x9c, 0xda, 0x24, 0x6d, 0x31, 0xc8,
1414                    0xb1, 0x71, 0x5a, 0x57, 0xd9, 0x00,
1415                ],
1416                xsk: None,
1417                xfvk: [
1418                    0x03, 0x48, 0xc1, 0x83, 0x75, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x93, 0x7b, 0xcf,
1419                    0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, 0x03, 0x36, 0x7b, 0x1f,
1420                    0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, 0x5a, 0x4a, 0xa7, 0xd6,
1421                    0xe7, 0xe8, 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3,
1422                    0x26, 0xd7, 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32,
1423                    0x8d, 0x64, 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, 0xdb, 0x88, 0x04, 0x9e, 0x02,
1424                    0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d, 0xb2, 0xab, 0xed, 0x50, 0x0b,
1425                    0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99, 0x76, 0x4b, 0x81, 0xc0, 0x66,
1426                    0x4f, 0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee,
1427                    0xe8, 0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44,
1428                    0x78, 0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6, 0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb,
1429                    0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce, 0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8,
1430                    0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9, 0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e,
1431                ],
1432                fp: [
1433                    0x2e, 0x08, 0x15, 0x6d, 0xf8, 0xdf, 0xa2, 0x5b, 0x50, 0x55, 0xfc, 0x06, 0x3c,
1434                    0x67, 0x15, 0x35, 0xa6, 0xa6, 0x5a, 0x60, 0x43, 0x7d, 0x96, 0xe7, 0x93, 0x08,
1435                    0x15, 0xd0, 0x90, 0xf6, 0x2d, 0x67,
1436                ],
1437                d0: None,
1438                d1: Some([
1439                    0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd,
1440                ]),
1441                d2: Some([
1442                    0x7b, 0xbf, 0x63, 0x93, 0x4c, 0x7e, 0x92, 0x67, 0x0c, 0xdb, 0x55,
1443                ]),
1444                dmax: Some([
1445                    0x1a, 0x73, 0x0f, 0xeb, 0x00, 0x59, 0xcf, 0x1f, 0x5b, 0xde, 0xa8,
1446                ]),
1447                internal_nsk: None,
1448                internal_ovk: [
1449                    0xbf, 0x19, 0xe2, 0x57, 0xdd, 0x83, 0x3e, 0x02, 0x94, 0xec, 0x2a, 0xcb, 0xdf,
1450                    0xa4, 0x0e, 0x14, 0x52, 0xf8, 0xe6, 0xa1, 0xf0, 0xc7, 0xf6, 0xf3, 0xab, 0xe5,
1451                    0x6a, 0xfd, 0x5f, 0x6e, 0x26, 0x18,
1452                ],
1453                internal_dk: [
1454                    0x1f, 0xfd, 0x6f, 0x81, 0xfe, 0x85, 0xc4, 0x9f, 0xe3, 0xe7, 0x3e, 0xf7, 0x3e,
1455                    0x50, 0x11, 0x38, 0x22, 0xca, 0x62, 0x67, 0x31, 0x2b, 0x7a, 0xce, 0xd0, 0xc1,
1456                    0x56, 0xa3, 0x2b, 0x3f, 0x24, 0x38,
1457                ],
1458                internal_nk: [
1459                    0x82, 0x2f, 0x2f, 0x70, 0x96, 0x0f, 0x05, 0xd6, 0x96, 0x74, 0x58, 0xe3, 0x92,
1460                    0x10, 0xd5, 0x77, 0x1f, 0x98, 0x47, 0xae, 0xf9, 0xe3, 0x4d, 0x94, 0xb8, 0xaf,
1461                    0xbf, 0x95, 0xbb, 0xc4, 0xd2, 0x27,
1462                ],
1463                internal_ivk: [
1464                    0xf9, 0x8a, 0x76, 0x09, 0x8e, 0x91, 0x05, 0x03, 0xe8, 0x02, 0x77, 0x52, 0x04,
1465                    0x2d, 0xe8, 0x7e, 0x7d, 0x89, 0x3a, 0xb0, 0x14, 0x5e, 0xbc, 0x3b, 0x05, 0x97,
1466                    0xc2, 0x39, 0x7f, 0x69, 0xd2, 0x01,
1467                ],
1468                internal_xsk: None,
1469                internal_xfvk: [
1470                    0x03, 0x48, 0xc1, 0x83, 0x75, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x93, 0x7b, 0xcf,
1471                    0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, 0x03, 0x36, 0x7b, 0x1f,
1472                    0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, 0x5a, 0x4a, 0xa7, 0xd6,
1473                    0xe7, 0xe8, 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3,
1474                    0x26, 0xd7, 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32,
1475                    0x8d, 0x64, 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, 0x82, 0x2f, 0x2f, 0x70, 0x96,
1476                    0x0f, 0x05, 0xd6, 0x96, 0x74, 0x58, 0xe3, 0x92, 0x10, 0xd5, 0x77, 0x1f, 0x98,
1477                    0x47, 0xae, 0xf9, 0xe3, 0x4d, 0x94, 0xb8, 0xaf, 0xbf, 0x95, 0xbb, 0xc4, 0xd2,
1478                    0x27, 0xbf, 0x19, 0xe2, 0x57, 0xdd, 0x83, 0x3e, 0x02, 0x94, 0xec, 0x2a, 0xcb,
1479                    0xdf, 0xa4, 0x0e, 0x14, 0x52, 0xf8, 0xe6, 0xa1, 0xf0, 0xc7, 0xf6, 0xf3, 0xab,
1480                    0xe5, 0x6a, 0xfd, 0x5f, 0x6e, 0x26, 0x18, 0x1f, 0xfd, 0x6f, 0x81, 0xfe, 0x85,
1481                    0xc4, 0x9f, 0xe3, 0xe7, 0x3e, 0xf7, 0x3e, 0x50, 0x11, 0x38, 0x22, 0xca, 0x62,
1482                    0x67, 0x31, 0x2b, 0x7a, 0xce, 0xd0, 0xc1, 0x56, 0xa3, 0x2b, 0x3f, 0x24, 0x38,
1483                ],
1484                internal_fp: [
1485                    0xba, 0x64, 0xe4, 0x0d, 0x08, 0x6d, 0x36, 0x2c, 0xa5, 0xa1, 0x7f, 0x5e, 0x3b,
1486                    0x1b, 0xee, 0x63, 0x24, 0xc8, 0x4f, 0x10, 0x12, 0x44, 0xa4, 0x00, 0x2a, 0x2e,
1487                    0xca, 0xaf, 0x05, 0xbd, 0xd9, 0x81,
1488                ],
1489            },
1490        ];
1491
1492        let seed = [
1493            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1494            24, 25, 26, 27, 28, 29, 30, 31,
1495        ];
1496
1497        let i1 = ChildIndex::NonHardened(1);
1498        let i2h = ChildIndex::Hardened(2);
1499        let i3 = ChildIndex::NonHardened(3);
1500
1501        let m = ExtendedSpendingKey::master(&seed);
1502        let m_1 = m.derive_child(i1);
1503        let m_1_2h = ExtendedSpendingKey::from_path(&m, &[i1, i2h]);
1504        let m_1_2hv = ExtendedFullViewingKey::from(&m_1_2h);
1505        let m_1_2hv_3 = m_1_2hv.derive_child(i3).unwrap();
1506
1507        let xfvks = [
1508            ExtendedFullViewingKey::from(&m),
1509            ExtendedFullViewingKey::from(&m_1),
1510            ExtendedFullViewingKey::from(&m_1_2h),
1511            m_1_2hv, // Appears twice so we can de-duplicate test code below
1512            m_1_2hv_3,
1513        ];
1514        assert_eq!(test_vectors.len(), xfvks.len());
1515
1516        let xsks = [m, m_1, m_1_2h];
1517
1518        for (xsk, tv) in xsks.iter().zip(test_vectors.iter()) {
1519            assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap());
1520            assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap());
1521
1522            assert_eq!(xsk.expsk.ovk.0, tv.ovk);
1523            assert_eq!(xsk.dk.0, tv.dk);
1524            assert_eq!(xsk.chain_code.0, tv.c);
1525
1526            let mut ser = vec![];
1527            xsk.write(&mut ser).unwrap();
1528            assert_eq!(&ser[..], &tv.xsk.unwrap()[..]);
1529
1530            let internal_xsk = xsk.derive_internal();
1531            assert_eq!(internal_xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap());
1532            assert_eq!(
1533                internal_xsk.expsk.nsk.to_repr().as_ref(),
1534                tv.internal_nsk.unwrap()
1535            );
1536
1537            assert_eq!(internal_xsk.expsk.ovk.0, tv.internal_ovk);
1538            assert_eq!(internal_xsk.dk.0, tv.internal_dk);
1539            assert_eq!(internal_xsk.chain_code.0, tv.c);
1540
1541            let mut ser = vec![];
1542            internal_xsk.write(&mut ser).unwrap();
1543            assert_eq!(&ser[..], &tv.internal_xsk.unwrap()[..]);
1544        }
1545
1546        for (xfvk, tv) in xfvks.iter().zip(test_vectors.iter()) {
1547            assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak);
1548            assert_eq!(xfvk.fvk.vk.nk.to_bytes(), tv.nk);
1549
1550            assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
1551            assert_eq!(xfvk.dk.0, tv.dk);
1552            assert_eq!(xfvk.chain_code.0, tv.c);
1553
1554            assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk);
1555
1556            let mut ser = vec![];
1557            xfvk.write(&mut ser).unwrap();
1558            assert_eq!(&ser[..], &tv.xfvk[..]);
1559            assert_eq!(FvkFingerprint::from(&xfvk.fvk).0, tv.fp);
1560
1561            // d0
1562            let mut di = DiversifierIndex::new();
1563            match xfvk.dk.find_diversifier(di).unwrap() {
1564                (l, d) if l == di => assert_eq!(d.0, tv.d0.unwrap()),
1565                (_, _) => assert!(tv.d0.is_none()),
1566            }
1567
1568            // d1
1569            di.increment().unwrap();
1570            match xfvk.dk.find_diversifier(di).unwrap() {
1571                (l, d) if l == di => assert_eq!(d.0, tv.d1.unwrap()),
1572                (_, _) => assert!(tv.d1.is_none()),
1573            }
1574
1575            // d2
1576            di.increment().unwrap();
1577            match xfvk.dk.find_diversifier(di).unwrap() {
1578                (l, d) if l == di => assert_eq!(d.0, tv.d2.unwrap()),
1579                (_, _) => assert!(tv.d2.is_none()),
1580            }
1581
1582            // dmax
1583            let dmax = DiversifierIndex([0xff; 11]);
1584            match xfvk.dk.find_diversifier(dmax) {
1585                Some((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()),
1586                Some((_, _)) => panic!(),
1587                None => assert!(tv.dmax.is_none()),
1588            }
1589
1590            let internal_xfvk = xfvk.derive_internal();
1591            assert_eq!(internal_xfvk.fvk.vk.ak.to_bytes(), tv.ak);
1592            assert_eq!(internal_xfvk.fvk.vk.nk.to_bytes(), tv.internal_nk);
1593
1594            assert_eq!(internal_xfvk.fvk.ovk.0, tv.internal_ovk);
1595            assert_eq!(internal_xfvk.dk.0, tv.internal_dk);
1596            assert_eq!(internal_xfvk.chain_code.0, tv.c);
1597
1598            assert_eq!(
1599                internal_xfvk.fvk.vk.ivk().to_repr().as_ref(),
1600                tv.internal_ivk
1601            );
1602
1603            let mut ser = vec![];
1604            internal_xfvk.write(&mut ser).unwrap();
1605            assert_eq!(&ser[..], &tv.internal_xfvk[..]);
1606            assert_eq!(FvkFingerprint::from(&internal_xfvk.fvk).0, tv.internal_fp);
1607        }
1608    }
1609}
1610
1611#[cfg(any(test, feature = "test-dependencies"))]
1612pub mod testing {
1613    use proptest::prelude::*;
1614
1615    use super::ExtendedSpendingKey;
1616
1617    prop_compose! {
1618        pub fn arb_extended_spending_key()(seed in prop::array::uniform32(prop::num::u8::ANY)) -> ExtendedSpendingKey {
1619            ExtendedSpendingKey::master(&seed)
1620        }
1621    }
1622}