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