use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::Field; use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::bls12_381::Bls12; use crate::{ jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform}, primitives::{Diversifier, PaymentAddress, ViewingKey}, }; use std::io::{self, Read, Write}; use crate::{ keys::{prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey}, JUBJUB, }; pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling"; pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP"; // Common helper functions fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey { let mut ovk = [0u8; 32]; ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &parent.0]).as_bytes()[..32]); OutgoingViewingKey(ovk) } // ZIP 32 structures /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); impl From<&FullViewingKey> for FVKFingerprint { fn from(fvk: &FullViewingKey) -> Self { let mut h = Blake2bParams::new() .hash_length(32) .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION) .to_state(); h.update(&fvk.to_bytes()); let mut fvfp = [0u8; 32]; fvfp.copy_from_slice(h.finalize().as_bytes()); FVKFingerprint(fvfp) } } /// A Sapling full viewing key tag #[derive(Clone, Copy, Debug, PartialEq)] struct FVKTag([u8; 4]); impl FVKFingerprint { fn tag(&self) -> FVKTag { let mut tag = [0u8; 4]; tag.copy_from_slice(&self.0[..4]); FVKTag(tag) } } impl FVKTag { fn master() -> Self { FVKTag([0u8; 4]) } } /// A child index for a derived key #[derive(Clone, Copy, Debug, PartialEq)] pub enum ChildIndex { NonHardened(u32), Hardened(u32), // Hardened(n) == n + (1 << 31) == n' in path notation } impl ChildIndex { pub fn from_index(i: u32) -> Self { match i { n if n >= (1 << 31) => ChildIndex::Hardened(n - (1 << 31)), n => ChildIndex::NonHardened(n), } } fn master() -> Self { ChildIndex::from_index(0) } fn to_index(&self) -> u32 { match *self { ChildIndex::Hardened(i) => i + (1 << 31), ChildIndex::NonHardened(i) => i, } } } /// A chain code #[derive(Clone, Copy, Debug, PartialEq)] struct ChainCode([u8; 32]); #[derive(Clone, Copy, Debug, PartialEq)] pub struct DiversifierIndex(pub [u8; 11]); impl DiversifierIndex { pub fn new() -> Self { DiversifierIndex([0; 11]) } pub fn increment(&mut self) -> Result<(), ()> { for k in 0..11 { self.0[k] = self.0[k].wrapping_add(1); if self.0[k] != 0 { // No overflow return Ok(()); } } // Overflow Err(()) } } /// A key used to derive diversifiers for a particular child key #[derive(Clone, Copy, Debug, PartialEq)] pub struct DiversifierKey(pub [u8; 32]); impl DiversifierKey { pub fn master(sk_m: &[u8]) -> Self { let mut dk_m = [0u8; 32]; dk_m.copy_from_slice(&prf_expand(sk_m, &[0x10]).as_bytes()[..32]); DiversifierKey(dk_m) } fn derive_child(&self, i_l: &[u8]) -> Self { let mut dk = [0u8; 32]; dk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x16], &self.0]).as_bytes()[..32]); DiversifierKey(dk) } /// Returns the first index starting from j that generates a valid /// diversifier, along with the corresponding diversifier. Returns /// an error if the diversifier space is exhausted. pub fn diversifier( &self, mut j: DiversifierIndex, ) -> Result<(DiversifierIndex, Diversifier), ()> { let ff = FF1::::new(&self.0, 2).unwrap(); loop { // Generate d_j let enc = ff .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.0[..])) .unwrap(); let mut d_j = [0; 11]; d_j.copy_from_slice(&enc.to_bytes_le()); let d_j = Diversifier(d_j); // Return (j, d_j) if valid, else increment j and try again match d_j.g_d::(&JUBJUB) { Some(_) => return Ok((j, d_j)), None => { if j.increment().is_err() { return Err(()); } } } } } } /// A Sapling extended spending key #[derive(Clone)] pub struct ExtendedSpendingKey { depth: u8, parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, pub expsk: ExpandedSpendingKey, dk: DiversifierKey, } // A Sapling extended full viewing key #[derive(Clone)] pub struct ExtendedFullViewingKey { depth: u8, parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, pub fvk: FullViewingKey, dk: DiversifierKey, } impl std::cmp::PartialEq for ExtendedSpendingKey { fn eq(&self, rhs: &ExtendedSpendingKey) -> bool { self.depth == rhs.depth && self.parent_fvk_tag == rhs.parent_fvk_tag && self.child_index == rhs.child_index && self.chain_code == rhs.chain_code && self.expsk.ask == rhs.expsk.ask && self.expsk.nsk == rhs.expsk.nsk && self.expsk.ovk == rhs.expsk.ovk && self.dk == rhs.dk } } impl std::fmt::Debug for ExtendedSpendingKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})", self.depth, self.parent_fvk_tag, self.child_index ) } } impl std::cmp::PartialEq for ExtendedFullViewingKey { fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool { self.depth == rhs.depth && self.parent_fvk_tag == rhs.parent_fvk_tag && self.child_index == rhs.child_index && self.chain_code == rhs.chain_code && self.fvk.vk.ak == rhs.fvk.vk.ak && self.fvk.vk.nk == rhs.fvk.vk.nk && self.fvk.ovk == rhs.fvk.ovk && self.dk == rhs.dk } } impl std::fmt::Debug for ExtendedFullViewingKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( f, "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})", self.depth, self.parent_fvk_tag, self.child_index ) } } impl ExtendedSpendingKey { pub fn master(seed: &[u8]) -> Self { let i = Blake2bParams::new() .hash_length(64) .personal(ZIP32_SAPLING_MASTER_PERSONALIZATION) .hash(seed); let sk_m = &i.as_bytes()[..32]; let mut c_m = [0u8; 32]; c_m.copy_from_slice(&i.as_bytes()[32..]); ExtendedSpendingKey { depth: 0, parent_fvk_tag: FVKTag::master(), child_index: ChildIndex::master(), chain_code: ChainCode(c_m), expsk: ExpandedSpendingKey::from_spending_key(sk_m), dk: DiversifierKey::master(sk_m), } } pub fn read(mut reader: R) -> io::Result { let depth = reader.read_u8()?; let mut tag = [0; 4]; reader.read_exact(&mut tag)?; let i = reader.read_u32::()?; let mut c = [0; 32]; reader.read_exact(&mut c)?; let expsk = ExpandedSpendingKey::read(&mut reader)?; let mut dk = [0; 32]; reader.read_exact(&mut dk)?; Ok(ExtendedSpendingKey { depth, parent_fvk_tag: FVKTag(tag), child_index: ChildIndex::from_index(i), chain_code: ChainCode(c), expsk, dk: DiversifierKey(dk), }) } pub fn write(&self, mut writer: W) -> io::Result<()> { writer.write_u8(self.depth)?; writer.write_all(&self.parent_fvk_tag.0)?; writer.write_u32::(self.child_index.to_index())?; writer.write_all(&self.chain_code.0)?; writer.write_all(&self.expsk.to_bytes())?; writer.write_all(&self.dk.0)?; Ok(()) } /// Returns the child key corresponding to the path derived from the master key pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Self { let mut xsk = master.clone(); for &i in path.iter() { xsk = xsk.derive_child(i); } xsk } pub fn derive_child(&self, i: ChildIndex) -> Self { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk, &JUBJUB); let tmp = match i { ChildIndex::Hardened(i) => { let mut le_i = [0; 4]; LittleEndian::write_u32(&mut le_i, i + (1 << 31)); prf_expand_vec( &self.chain_code.0, &[&[0x11], &self.expsk.to_bytes(), &self.dk.0, &le_i], ) } ChildIndex::NonHardened(i) => { let mut le_i = [0; 4]; LittleEndian::write_u32(&mut le_i, i); prf_expand_vec( &self.chain_code.0, &[&[0x12], &fvk.to_bytes(), &self.dk.0, &le_i], ) } }; let i_l = &tmp.as_bytes()[..32]; let mut c_i = [0u8; 32]; c_i.copy_from_slice(&tmp.as_bytes()[32..]); ExtendedSpendingKey { depth: self.depth + 1, parent_fvk_tag: FVKFingerprint::from(&fvk).tag(), child_index: i, chain_code: ChainCode(c_i), expsk: { let mut ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); let mut nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); ask.add_assign(&self.expsk.ask); nsk.add_assign(&self.expsk.nsk); let ovk = derive_child_ovk(&self.expsk.ovk, i_l); ExpandedSpendingKey { ask, nsk, ovk } }, dk: self.dk.derive_child(i_l), } } pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { ExtendedFullViewingKey::from(self).default_address() } } impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { fn from(xsk: &ExtendedSpendingKey) -> Self { ExtendedFullViewingKey { depth: xsk.depth, parent_fvk_tag: xsk.parent_fvk_tag, child_index: xsk.child_index, chain_code: xsk.chain_code, fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB), dk: xsk.dk, } } } impl ExtendedFullViewingKey { pub fn read(mut reader: R) -> io::Result { let depth = reader.read_u8()?; let mut tag = [0; 4]; reader.read_exact(&mut tag)?; let i = reader.read_u32::()?; let mut c = [0; 32]; reader.read_exact(&mut c)?; let fvk = FullViewingKey::read(&mut reader, &*JUBJUB)?; let mut dk = [0; 32]; reader.read_exact(&mut dk)?; Ok(ExtendedFullViewingKey { depth, parent_fvk_tag: FVKTag(tag), child_index: ChildIndex::from_index(i), chain_code: ChainCode(c), fvk, dk: DiversifierKey(dk), }) } pub fn write(&self, mut writer: W) -> io::Result<()> { writer.write_u8(self.depth)?; writer.write_all(&self.parent_fvk_tag.0)?; writer.write_u32::(self.child_index.to_index())?; writer.write_all(&self.chain_code.0)?; writer.write_all(&self.fvk.to_bytes())?; writer.write_all(&self.dk.0)?; Ok(()) } pub fn derive_child(&self, i: ChildIndex) -> Result { let tmp = match i { ChildIndex::Hardened(_) => return Err(()), ChildIndex::NonHardened(i) => { let mut le_i = [0; 4]; LittleEndian::write_u32(&mut le_i, i); prf_expand_vec( &self.chain_code.0, &[&[0x12], &self.fvk.to_bytes(), &self.dk.0, &le_i], ) } }; let i_l = &tmp.as_bytes()[..32]; let mut c_i = [0u8; 32]; c_i.copy_from_slice(&tmp.as_bytes()[32..]); Ok(ExtendedFullViewingKey { depth: self.depth + 1, parent_fvk_tag: FVKFingerprint::from(&self.fvk).tag(), child_index: i, chain_code: ChainCode(c_i), fvk: { let i_ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); let i_nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); let ak = JUBJUB .generator(FixedGenerators::SpendingKeyGenerator) .mul(i_ask, &JUBJUB) .add(&self.fvk.vk.ak, &JUBJUB); let nk = JUBJUB .generator(FixedGenerators::ProofGenerationKey) .mul(i_nsk, &JUBJUB) .add(&self.fvk.vk.nk, &JUBJUB); FullViewingKey { vk: ViewingKey { ak, nk }, ovk: derive_child_ovk(&self.fvk.ovk, i_l), } }, dk: self.dk.derive_child(i_l), }) } pub fn address( &self, j: DiversifierIndex, ) -> Result<(DiversifierIndex, PaymentAddress), ()> { let (j, d_j) = match self.dk.diversifier(j) { Ok(ret) => ret, Err(()) => return Err(()), }; match self.fvk.vk.to_payment_address(d_j, &JUBJUB) { Some(addr) => Ok((j, addr)), None => Err(()), } } pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { self.address(DiversifierIndex::new()) } } #[cfg(test)] mod tests { use super::*; use ff::{PrimeField, PrimeFieldRepr}; #[test] fn derive_nonhardened_child() { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed); let xfvk_m = ExtendedFullViewingKey::from(&xsk_m); let i_5 = ChildIndex::NonHardened(5); let xsk_5 = xsk_m.derive_child(i_5); let xfvk_5 = xfvk_m.derive_child(i_5); assert!(xfvk_5.is_ok()); assert_eq!(ExtendedFullViewingKey::from(&xsk_5), xfvk_5.unwrap()); } #[test] fn derive_hardened_child() { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed); let xfvk_m = ExtendedFullViewingKey::from(&xsk_m); let i_5h = ChildIndex::Hardened(5); let xsk_5h = xsk_m.derive_child(i_5h); let xfvk_5h = xfvk_m.derive_child(i_5h); // Cannot derive a hardened child from an ExtendedFullViewingKey assert!(xfvk_5h.is_err()); let xfvk_5h = ExtendedFullViewingKey::from(&xsk_5h); let i_7 = ChildIndex::NonHardened(7); let xsk_5h_7 = xsk_5h.derive_child(i_7); let xfvk_5h_7 = xfvk_5h.derive_child(i_7); // But we *can* derive a non-hardened child from a hardened parent assert!(xfvk_5h_7.is_ok()); assert_eq!(ExtendedFullViewingKey::from(&xsk_5h_7), xfvk_5h_7.unwrap()); } #[test] fn path() { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed); let xsk_5h = xsk_m.derive_child(ChildIndex::Hardened(5)); assert_eq!( ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::Hardened(5)]), xsk_5h ); let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::NonHardened(7)); assert_eq!( ExtendedSpendingKey::from_path( &xsk_m, &[ChildIndex::Hardened(5), ChildIndex::NonHardened(7)] ), xsk_5h_7 ); } #[test] fn diversifier() { let dk = DiversifierKey([0; 32]); let j_0 = DiversifierIndex::new(); let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // Computed using this Rust implementation let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140]; let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34]; // j = 0 let (j, d_j) = dk.diversifier(j_0).unwrap(); assert_eq!(j, j_0); assert_eq!(d_j.0, d_0); // j = 1 let (j, d_j) = dk.diversifier(j_1).unwrap(); assert_eq!(j, j_3); assert_eq!(d_j.0, d_3); // j = 2 let (j, d_j) = dk.diversifier(j_2).unwrap(); assert_eq!(j, j_3); assert_eq!(d_j.0, d_3); // j = 3 let (j, d_j) = dk.diversifier(j_3).unwrap(); assert_eq!(j, j_3); assert_eq!(d_j.0, d_3); } #[test] fn default_address() { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed); let (j_m, addr_m) = xsk_m.default_address().unwrap(); assert_eq!(j_m.0, [0; 11]); assert_eq!( addr_m.diversifier.0, // Computed using this Rust implementation [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19] ); } #[test] fn read_write() { let seed = [0; 32]; let xsk = ExtendedSpendingKey::master(&seed); let fvk = ExtendedFullViewingKey::from(&xsk); let mut ser = vec![]; xsk.write(&mut ser).unwrap(); let xsk2 = ExtendedSpendingKey::read(&ser[..]).unwrap(); assert_eq!(xsk2, xsk); let mut ser = vec![]; fvk.write(&mut ser).unwrap(); let fvk2 = ExtendedFullViewingKey::read(&ser[..]).unwrap(); assert_eq!(fvk2, fvk); } #[test] fn test_vectors() { struct TestVector { ask: Option<[u8; 32]>, nsk: Option<[u8; 32]>, ovk: [u8; 32], dk: [u8; 32], c: [u8; 32], ak: [u8; 32], nk: [u8; 32], ivk: [u8; 32], xsk: Option<[u8; 169]>, xfvk: [u8; 169], fp: [u8; 32], d0: Option<[u8; 11]>, d1: Option<[u8; 11]>, d2: Option<[u8; 11]>, dmax: Option<[u8; 11]>, }; // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py let test_vectors = vec![ TestVector { ask: Some([ 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, ]), nsk: Some([ 0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea, 0x06, ]), ovk: [ 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, ], dk: [ 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, ], c: [ 0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e, ], ak: [ 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, ], nk: [ 0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06, ], ivk: [ 0x48, 0x47, 0xa1, 0x30, 0xe7, 0x99, 0xd3, 0xdb, 0xea, 0x36, 0xa1, 0xc1, 0x64, 0x67, 0xd6, 0x21, 0xfb, 0x2d, 0x80, 0xe3, 0x0b, 0x3b, 0x1d, 0x1a, 0x42, 0x68, 0x93, 0x41, 0x5d, 0xad, 0x66, 0x01, ], xsk: Some([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea, 0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, ]), xfvk: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, ], fp: [ 0x14, 0xc2, 0x71, 0x3a, 0xdc, 0xe9, 0x3a, 0x83, 0x0e, 0xa8, 0x3a, 0x05, 0x19, 0x08, 0xb7, 0x44, 0x77, 0x83, 0xf5, 0xd1, 0x06, 0xc0, 0x98, 0x5e, 0x02, 0x55, 0x0e, 0x42, 0x6f, 0x27, 0x59, 0x7c, ], d0: Some([ 0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89, ]), d1: Some([ 0x48, 0xea, 0x17, 0xa1, 0x99, 0xc8, 0x4b, 0xd1, 0xba, 0xa5, 0xd4, ]), d2: None, dmax: None, }, TestVector { ask: Some([ 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6, 0x8c, 0x42, 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61, 0xd7, 0xb8, 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, ]), nsk: Some([ 0xe7, 0xa3, 0x29, 0x88, 0xfd, 0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62, 0xe6, 0x29, 0xc2, 0xe9, 0x6b, 0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd, 0x18, 0x10, 0xff, 0x6b, 0xba, 0x01, ]), ovk: [ 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, 0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, 0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, ], dk: [ 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, ], c: [ 0x01, 0x47, 0x11, 0x0c, 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5, 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda, 0xbc, 0xe6, ], ak: [ 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25, 0xaf, 0x2f, 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e, 0xa1, 0xbf, 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, ], nk: [ 0x61, 0x52, 0xeb, 0x7f, 0xdb, 0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17, 0xea, 0x4b, 0x6f, 0xd3, 0x40, 0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xec, 0xeb, 0x41, 0xba, 0x45, 0x2a, ], ivk: [ 0x15, 0x5a, 0x8e, 0xe2, 0x05, 0xd3, 0x87, 0x2d, 0x12, 0xf8, 0xa3, 0xe6, 0x39, 0x91, 0x46, 0x33, 0xc2, 0x3c, 0xde, 0x1f, 0x30, 0xed, 0x50, 0x51, 0xe5, 0x21, 0x30, 0xb1, 0xd0, 0x10, 0x4c, 0x06, ], xsk: Some([ 0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c, 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5, 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda, 0xbc, 0xe6, 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6, 0x8c, 0x42, 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61, 0xd7, 0xb8, 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, 0xe7, 0xa3, 0x29, 0x88, 0xfd, 0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62, 0xe6, 0x29, 0xc2, 0xe9, 0x6b, 0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd, 0x18, 0x10, 0xff, 0x6b, 0xba, 0x01, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, 0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, 0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, ]), xfvk: [ 0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c, 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5, 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda, 0xbc, 0xe6, 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25, 0xaf, 0x2f, 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e, 0xa1, 0xbf, 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, 0x61, 0x52, 0xeb, 0x7f, 0xdb, 0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17, 0xea, 0x4b, 0x6f, 0xd3, 0x40, 0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xec, 0xeb, 0x41, 0xba, 0x45, 0x2a, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, 0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, 0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, ], fp: [ 0xdb, 0x99, 0x9e, 0x07, 0x1d, 0xcb, 0x58, 0xdd, 0x93, 0x02, 0x9a, 0xe6, 0x97, 0x05, 0x3e, 0x90, 0xed, 0xb3, 0x59, 0xd1, 0xa1, 0xb7, 0xa1, 0x25, 0x16, 0x7e, 0xfb, 0xe9, 0x28, 0x06, 0x84, 0x23, ], d0: Some([ 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81, ]), d1: None, d2: Some([ 0x57, 0x49, 0xa1, 0x33, 0x52, 0xbc, 0x22, 0x3e, 0x30, 0x80, 0x78, ]), dmax: Some([ 0x63, 0x89, 0x57, 0x4c, 0xde, 0x0f, 0xbb, 0xc6, 0x36, 0x81, 0x31, ]), }, TestVector { ask: Some([ 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4, 0x1f, 0xc8, 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c, 0x92, 0xda, 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, ]), nsk: Some([ 0x37, 0x78, 0x05, 0x9d, 0xc5, 0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f, 0x95, 0x1b, 0xbd, 0xe9, 0x2f, 0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c, 0x5c, 0x27, 0x3a, 0xa6, 0x99, 0x0c, ]), ovk: [ 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, ], dk: [ 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, ], c: [ 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, ], ak: [ 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, ], nk: [ 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, ], ivk: [ 0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30, 0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a, 0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04, ], xsk: Some([ 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4, 0x1f, 0xc8, 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c, 0x92, 0xda, 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, 0x37, 0x78, 0x05, 0x9d, 0xc5, 0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f, 0x95, 0x1b, 0xbd, 0xe9, 0x2f, 0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c, 0x5c, 0x27, 0x3a, 0xa6, 0x99, 0x0c, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, ]), xfvk: [ 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, ], fp: [ 0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40, 0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e, 0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf, ], d0: Some([ 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41, ]), d1: Some([ 0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68, ]), d2: None, dmax: None, }, TestVector { ask: None, nsk: None, ovk: [ 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, ], dk: [ 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, ], c: [ 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, ], ak: [ 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, ], nk: [ 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, ], ivk: [ 0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30, 0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a, 0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04, ], xsk: None, xfvk: [ 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, ], fp: [ 0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40, 0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e, 0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf, ], d0: Some([ 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41, ]), d1: Some([ 0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68, ]), d2: None, dmax: None, }, TestVector { ask: None, nsk: None, ovk: [ 0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee, 0xe8, 0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44, 0x78, 0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6, ], dk: [ 0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb, 0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce, 0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8, 0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9, 0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e, ], c: [ 0x8d, 0x93, 0x7b, 0xcf, 0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, 0x03, 0x36, 0x7b, 0x1f, 0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, 0x5a, 0x4a, 0xa7, 0xd6, 0xe7, 0xe8, ], ak: [ 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3, 0x26, 0xd7, 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32, 0x8d, 0x64, 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, ], nk: [ 0xdb, 0x88, 0x04, 0x9e, 0x02, 0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d, 0xb2, 0xab, 0xed, 0x50, 0x0b, 0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99, 0x76, 0x4b, 0x81, 0xc0, 0x66, 0x4f, ], ivk: [ 0xb0, 0xa5, 0xf3, 0x37, 0x23, 0x2f, 0x2c, 0x3d, 0xac, 0x70, 0xc2, 0xa4, 0x10, 0xfa, 0x56, 0x1f, 0xc4, 0x5d, 0x8c, 0xc5, 0x9c, 0xda, 0x24, 0x6d, 0x31, 0xc8, 0xb1, 0x71, 0x5a, 0x57, 0xd9, 0x00, ], xsk: None, xfvk: [ 0x03, 0x48, 0xc1, 0x83, 0x75, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x93, 0x7b, 0xcf, 0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, 0x03, 0x36, 0x7b, 0x1f, 0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, 0x5a, 0x4a, 0xa7, 0xd6, 0xe7, 0xe8, 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3, 0x26, 0xd7, 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32, 0x8d, 0x64, 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, 0xdb, 0x88, 0x04, 0x9e, 0x02, 0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d, 0xb2, 0xab, 0xed, 0x50, 0x0b, 0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99, 0x76, 0x4b, 0x81, 0xc0, 0x66, 0x4f, 0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee, 0xe8, 0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44, 0x78, 0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6, 0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb, 0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce, 0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8, 0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9, 0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e, ], fp: [ 0x2e, 0x08, 0x15, 0x6d, 0xf8, 0xdf, 0xa2, 0x5b, 0x50, 0x55, 0xfc, 0x06, 0x3c, 0x67, 0x15, 0x35, 0xa6, 0xa6, 0x5a, 0x60, 0x43, 0x7d, 0x96, 0xe7, 0x93, 0x08, 0x15, 0xd0, 0x90, 0xf6, 0x2d, 0x67, ], d0: None, d1: Some([ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd, ]), d2: Some([ 0x7b, 0xbf, 0x63, 0x93, 0x4c, 0x7e, 0x92, 0x67, 0x0c, 0xdb, 0x55, ]), dmax: Some([ 0x1a, 0x73, 0x0f, 0xeb, 0x00, 0x59, 0xcf, 0x1f, 0x5b, 0xde, 0xa8, ]), }, ]; let seed = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ]; let i1 = ChildIndex::NonHardened(1); let i2h = ChildIndex::Hardened(2); let i3 = ChildIndex::NonHardened(3); let m = ExtendedSpendingKey::master(&seed); let m_1 = m.derive_child(i1); let m_1_2h = ExtendedSpendingKey::from_path(&m, &[i1, i2h]); let m_1_2hv = ExtendedFullViewingKey::from(&m_1_2h); let m_1_2hv_3 = m_1_2hv.derive_child(i3).unwrap(); let xfvks = [ ExtendedFullViewingKey::from(&m), ExtendedFullViewingKey::from(&m_1), ExtendedFullViewingKey::from(&m_1_2h), m_1_2hv, // Appears twice so we can de-duplicate test code below m_1_2hv_3, ]; assert_eq!(test_vectors.len(), xfvks.len()); let xsks = [m, m_1, m_1_2h]; for j in 0..xsks.len() { let xsk = &xsks[j]; let tv = &test_vectors[j]; let mut buf = [0; 32]; xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); assert_eq!(buf, tv.ask.unwrap()); xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); assert_eq!(buf, tv.nsk.unwrap()); assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); assert_eq!(xsk.chain_code.0, tv.c); let mut ser = vec![]; xsk.write(&mut ser).unwrap(); assert_eq!(&ser[..], &tv.xsk.unwrap()[..]); } for j in 0..xfvks.len() { let xfvk = &xfvks[j]; let tv = &test_vectors[j]; let mut buf = [0; 32]; xfvk.fvk.vk.ak.write(&mut buf[..]).unwrap(); assert_eq!(buf, tv.ak); xfvk.fvk.vk.nk.write(&mut buf[..]).unwrap(); assert_eq!(buf, tv.nk); assert_eq!(xfvk.fvk.ovk.0, tv.ovk); assert_eq!(xfvk.dk.0, tv.dk); assert_eq!(xfvk.chain_code.0, tv.c); xfvk.fvk .vk .ivk() .into_repr() .write_le(&mut buf[..]) .unwrap(); assert_eq!(buf, tv.ivk); let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); assert_eq!(&ser[..], &tv.xfvk[..]); assert_eq!(FVKFingerprint::from(&xfvk.fvk).0, tv.fp); // d0 let mut di = DiversifierIndex::new(); match xfvk.dk.diversifier(di) { Ok((l, d)) if l == di => assert_eq!(d.0, tv.d0.unwrap()), Ok((_, _)) => assert!(tv.d0.is_none()), Err(_) => panic!(), } // d1 di.increment().unwrap(); match xfvk.dk.diversifier(di) { Ok((l, d)) if l == di => assert_eq!(d.0, tv.d1.unwrap()), Ok((_, _)) => assert!(tv.d1.is_none()), Err(_) => panic!(), } // d2 di.increment().unwrap(); match xfvk.dk.diversifier(di) { Ok((l, d)) if l == di => assert_eq!(d.0, tv.d2.unwrap()), Ok((_, _)) => assert!(tv.d2.is_none()), Err(_) => panic!(), } // dmax let dmax = DiversifierIndex([0xff; 11]); match xfvk.dk.diversifier(dmax) { Ok((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()), Ok((_, _)) => panic!(), Err(_) => assert!(tv.dmax.is_none()), } } } }