From 1b8da6b12d6f7813817aeb498031e861758fe463 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 10 Jul 2018 23:58:06 +0100 Subject: [PATCH 01/19] ZIP 32 structures --- Cargo.toml | 5 +++++ src/lib.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 16375d0..f97aee5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,8 @@ homepage = "https://github.com/zcash-hackworks/zip32" repository = "https://github.com/zcash-hackworks/zip32" [dependencies] +pairing = "0.14.2" + +[dependencies.sapling-crypto] +git = "https://github.com/zcash-hackworks/sapling-crypto" +rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..e696dd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,67 @@ +extern crate pairing; +extern crate sapling_crypto; + +use pairing::bls12_381::Bls12; +use sapling_crypto::{jubjub::JubjubEngine, primitives::ViewingKey}; + +// Sapling key components + +/// An outgoing viewing key +struct OutgoingViewingKey([u8; 32]); + +/// A Sapling expanded spending key +struct ExpandedSpendingKey { + ask: E::Fs, + nsk: E::Fs, + ovk: OutgoingViewingKey, +} + +/// A Sapling full viewing key +struct FullViewingKey { + vk: ViewingKey, + ovk: OutgoingViewingKey, +} + +// ZIP 32 structures + +/// A Sapling full viewing key fingerprint +struct FVKFingerprint([u8; 32]); + +/// A Sapling full viewing key tag +struct FVKTag([u8; 4]); + +/// A child index for a derived key +pub enum ChildIndex { + NonHardened(u32), + Hardened(u32), // Hardened(n) == n + (1 << 31) == n' in path notation +} + +/// A chain code +struct ChainCode([u8; 32]); + +/// A key used to derive diversifiers for a particular child key +struct DiversifierKey([u8; 32]); + +/// A Sapling extended spending key +pub struct ExtendedSpendingKey { + depth: u8, + parent_fvk_tag: FVKTag, + child_index: ChildIndex, + chain_code: ChainCode, + xsk: ExpandedSpendingKey, + dk: DiversifierKey, +} + +// A Sapling extended full viewing key +pub struct ExtendedFullViewingKey { + depth: u8, + parent_fvk_tag: FVKTag, + child_index: ChildIndex, + chain_code: ChainCode, + fvk: FullViewingKey, + dk: DiversifierKey, +} + #[cfg(test)] mod tests { #[test] From 8db848139b5fdfb8f216cae71bb38511b7f5afbb Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Jul 2018 01:26:07 +0100 Subject: [PATCH 02/19] Basic conversions --- Cargo.toml | 1 + src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f97aee5..0872467 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://github.com/zcash-hackworks/zip32" repository = "https://github.com/zcash-hackworks/zip32" [dependencies] +lazy_static = "1.0" pairing = "0.14.2" [dependencies.sapling-crypto] diff --git a/src/lib.rs b/src/lib.rs index e696dd3..60429eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,21 @@ +#[macro_use] +extern crate lazy_static; extern crate pairing; extern crate sapling_crypto; use pairing::bls12_381::Bls12; -use sapling_crypto::{jubjub::JubjubEngine, primitives::ViewingKey}; +use sapling_crypto::{ + jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams}, primitives::ViewingKey, +}; + +lazy_static! { + static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; +} // Sapling key components /// An outgoing viewing key +#[derive(Clone, Copy)] struct OutgoingViewingKey([u8; 32]); /// A Sapling expanded spending key @@ -22,24 +31,67 @@ struct FullViewingKey { ovk: OutgoingViewingKey, } +impl FullViewingKey { + fn from_expanded_spending_key(xsk: &ExpandedSpendingKey, params: &E::Params) -> Self { + FullViewingKey { + vk: ViewingKey { + ak: params + .generator(FixedGenerators::SpendingKeyGenerator) + .mul(xsk.ask, params), + nk: params + .generator(FixedGenerators::ProofGenerationKey) + .mul(xsk.nsk, params), + }, + ovk: xsk.ovk, + } + } +} + // ZIP 32 structures /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); /// A Sapling full viewing key tag +#[derive(Clone, Copy)] struct FVKTag([u8; 4]); +impl<'a> From<&'a FVKFingerprint> for FVKTag { + fn from(fingerprint: &FVKFingerprint) -> Self { + let mut tag = [0u8; 4]; + tag.copy_from_slice(&fingerprint.0[..4]); + FVKTag(tag) + } +} + +impl From for FVKTag { + fn from(fingerprint: FVKFingerprint) -> Self { + (&fingerprint).into() + } +} + /// A child index for a derived key +#[derive(Clone, Copy)] 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), + } + } +} + /// A chain code +#[derive(Clone, Copy)] struct ChainCode([u8; 32]); /// A key used to derive diversifiers for a particular child key +#[derive(Clone, Copy)] struct DiversifierKey([u8; 32]); /// A Sapling extended spending key @@ -62,6 +114,19 @@ pub struct ExtendedFullViewingKey { dk: DiversifierKey, } +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.xsk, &JUBJUB), + dk: xsk.dk, + } + } +} + #[cfg(test)] mod tests { #[test] From 2fdfa4b6710d020bdd4a768225d9ed2a4f208e7c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Jul 2018 01:28:10 +0100 Subject: [PATCH 03/19] ExtendedSpendingKey::master() --- Cargo.toml | 4 ++++ src/lib.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0872467..764a2a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ repository = "https://github.com/zcash-hackworks/zip32" lazy_static = "1.0" pairing = "0.14.2" +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" + [dependencies.sapling-crypto] git = "https://github.com/zcash-hackworks/sapling-crypto" rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" diff --git a/src/lib.rs b/src/lib.rs index 60429eb..88b8438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,33 @@ +extern crate blake2_rfc; #[macro_use] extern crate lazy_static; extern crate pairing; extern crate sapling_crypto; +use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; use pairing::bls12_381::Bls12; use sapling_crypto::{ - jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams}, primitives::ViewingKey, + jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, + primitives::ViewingKey, }; lazy_static! { static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; } +pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; +pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling"; + // Sapling key components +/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) +fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult { + let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION); + h.update(sk); + h.update(t); + h.finalize() +} + /// An outgoing viewing key #[derive(Clone, Copy)] struct OutgoingViewingKey([u8; 32]); @@ -31,6 +45,17 @@ struct FullViewingKey { ovk: OutgoingViewingKey, } +impl ExpandedSpendingKey { + fn from_spending_key(sk: &[u8]) -> Self { + let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes()); + let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes()); + let mut ovk = OutgoingViewingKey([0u8; 32]); + ovk.0 + .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); + ExpandedSpendingKey { ask, nsk, ovk } + } +} + impl FullViewingKey { fn from_expanded_spending_key(xsk: &ExpandedSpendingKey, params: &E::Params) -> Self { FullViewingKey { @@ -70,6 +95,12 @@ impl From for FVKTag { } } +impl FVKTag { + fn master() -> Self { + FVKTag([0u8; 4]) + } +} + /// A child index for a derived key #[derive(Clone, Copy)] pub enum ChildIndex { @@ -84,6 +115,10 @@ impl ChildIndex { n => ChildIndex::NonHardened(n), } } + + fn master() -> Self { + ChildIndex::from_index(0) + } } /// A chain code @@ -94,6 +129,14 @@ struct ChainCode([u8; 32]); #[derive(Clone, Copy)] struct DiversifierKey([u8; 32]); +impl DiversifierKey { + 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) + } +} + /// A Sapling extended spending key pub struct ExtendedSpendingKey { depth: u8, @@ -114,6 +157,27 @@ pub struct ExtendedFullViewingKey { dk: DiversifierKey, } +impl ExtendedSpendingKey { + pub fn master(seed: &[u8]) -> Self { + let mut h = Blake2b::with_params(64, &[], &[], ZIP32_SAPLING_MASTER_PERSONALIZATION); + h.update(seed); + let i = h.finalize(); + + 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), + xsk: ExpandedSpendingKey::from_spending_key(sk_m), + dk: DiversifierKey::master(sk_m), + } + } +} + impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { fn from(xsk: &ExtendedSpendingKey) -> Self { ExtendedFullViewingKey { From 0e1d4402872a00618e72c8c6fb242215acdf2f53 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Jul 2018 12:29:39 +0100 Subject: [PATCH 04/19] Serialization and fingerprint for ExpandedSpendingKey and FullViewingKey --- src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 88b8438..29599e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ extern crate pairing; extern crate sapling_crypto; use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; -use pairing::bls12_381::Bls12; +use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, primitives::ViewingKey, @@ -17,6 +17,7 @@ lazy_static! { pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling"; +pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP"; // Sapling key components @@ -54,6 +55,20 @@ impl ExpandedSpendingKey { .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); ExpandedSpendingKey { ask, nsk, ovk } } + + fn to_bytes(&self) -> [u8; 96] { + let mut result = [0u8; 96]; + self.ask + .into_repr() + .write_le(&mut result[..32]) + .expect("length is 32 bytes"); + self.nsk + .into_repr() + .write_le(&mut result[32..64]) + .expect("length is 32 bytes"); + (&mut result[64..]).copy_from_slice(&self.ovk.0); + result + } } impl FullViewingKey { @@ -70,6 +85,20 @@ impl FullViewingKey { ovk: xsk.ovk, } } + + fn to_bytes(&self) -> [u8; 96] { + let mut result = [0u8; 96]; + self.vk + .ak + .write(&mut result[..32]) + .expect("length is 32 bytes"); + self.vk + .nk + .write(&mut result[32..64]) + .expect("length is 32 bytes"); + (&mut result[64..]).copy_from_slice(&self.ovk.0); + result + } } // ZIP 32 structures @@ -77,6 +106,16 @@ impl FullViewingKey { /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); +impl<'a, E: JubjubEngine> From<&'a FullViewingKey> for FVKFingerprint { + fn from(fvk: &FullViewingKey) -> Self { + let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION); + 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)] struct FVKTag([u8; 4]); From e87fb329f974fa135e3e99c050aebf76e3eeea62 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Jul 2018 12:43:15 +0100 Subject: [PATCH 05/19] Child key derivation --- Cargo.toml | 1 + src/lib.rs | 192 +++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 181 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 764a2a0..93978c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://github.com/zcash-hackworks/zip32" repository = "https://github.com/zcash-hackworks/zip32" [dependencies] +byteorder = "1" lazy_static = "1.0" pairing = "0.14.2" diff --git a/src/lib.rs b/src/lib.rs index 29599e3..f9b68d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,13 @@ extern crate blake2_rfc; +extern crate byteorder; #[macro_use] extern crate lazy_static; extern crate pairing; extern crate sapling_crypto; use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; -use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr}; +use byteorder::{ByteOrder, LittleEndian}; +use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, primitives::ViewingKey, @@ -23,16 +25,30 @@ pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSapling /// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult { + prf_expand_vec(sk, &vec![t]) +} + +fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bResult { let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION); h.update(sk); - h.update(t); + for t in ts { + h.update(t); + } h.finalize() } /// An outgoing viewing key -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] struct OutgoingViewingKey([u8; 32]); +impl OutgoingViewingKey { + fn derive_child(&self, i_l: &[u8]) -> Self { + let mut ovk = [0u8; 32]; + ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &self.0]).as_bytes()[..32]); + OutgoingViewingKey(ovk) + } +} + /// A Sapling expanded spending key struct ExpandedSpendingKey { ask: E::Fs, @@ -56,6 +72,15 @@ impl ExpandedSpendingKey { ExpandedSpendingKey { ask, nsk, ovk } } + fn derive_child(&self, i_l: &[u8]) -> Self { + let mut ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); + let mut nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + ask.add_assign(&self.ask); + nsk.add_assign(&self.nsk); + let ovk = self.ovk.derive_child(i_l); + ExpandedSpendingKey { ask, nsk, ovk } + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; self.ask @@ -86,6 +111,24 @@ impl FullViewingKey { } } + fn derive_child(&self, i_l: &[u8], params: &E::Params) -> Self { + let i_ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); + let i_nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + let ak = params + .generator(FixedGenerators::SpendingKeyGenerator) + .mul(i_ask, params) + .add(&self.vk.ak, params); + let nk = params + .generator(FixedGenerators::ProofGenerationKey) + .mul(i_nsk, params) + .add(&self.vk.nk, params); + + FullViewingKey { + vk: ViewingKey { ak, nk }, + ovk: self.ovk.derive_child(i_l), + } + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; self.vk @@ -117,7 +160,7 @@ impl<'a, E: JubjubEngine> From<&'a FullViewingKey> for FVKFingerprint { } /// A Sapling full viewing key tag -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] struct FVKTag([u8; 4]); impl<'a> From<&'a FVKFingerprint> for FVKTag { @@ -141,7 +184,7 @@ impl FVKTag { } /// A child index for a derived key -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum ChildIndex { NonHardened(u32), Hardened(u32), // Hardened(n) == n + (1 << 31) == n' in path notation @@ -161,11 +204,11 @@ impl ChildIndex { } /// A chain code -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] struct ChainCode([u8; 32]); /// A key used to derive diversifiers for a particular child key -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq)] struct DiversifierKey([u8; 32]); impl DiversifierKey { @@ -174,6 +217,12 @@ impl DiversifierKey { 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) + } } /// A Sapling extended spending key @@ -196,6 +245,29 @@ pub struct ExtendedFullViewingKey { dk: DiversifierKey, } +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 mut h = Blake2b::with_params(64, &[], &[], ZIP32_SAPLING_MASTER_PERSONALIZATION); @@ -215,6 +287,40 @@ impl ExtendedSpendingKey { dk: DiversifierKey::master(sk_m), } } + + pub fn derive_child(&self, i: ChildIndex) -> Self { + let fvk = FullViewingKey::from_expanded_spending_key(&self.xsk, &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.xsk.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).into(), + child_index: i, + chain_code: ChainCode(c_i), + xsk: self.xsk.derive_child(i_l), + dk: self.dk.derive_child(i_l), + } + } } impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { @@ -230,10 +336,72 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { } } -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +impl ExtendedFullViewingKey { + 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).into(), + child_index: i, + chain_code: ChainCode(c_i), + fvk: self.fvk.derive_child(i_l, &JUBJUB), + dk: self.dk.derive_child(i_l), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[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()); } } From f5d6091c93d62ec9b7654e0919647d50fb467f45 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 11 Jul 2018 13:15:31 +0100 Subject: [PATCH 06/19] Path derivation --- src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f9b68d8..9c364bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ impl OutgoingViewingKey { } /// A Sapling expanded spending key +#[derive(Clone)] struct ExpandedSpendingKey { ask: E::Fs, nsk: E::Fs, @@ -226,6 +227,7 @@ impl DiversifierKey { } /// A Sapling extended spending key +#[derive(Clone)] pub struct ExtendedSpendingKey { depth: u8, parent_fvk_tag: FVKTag, @@ -245,6 +247,29 @@ pub struct ExtendedFullViewingKey { 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.xsk.ask == rhs.xsk.ask + && self.xsk.nsk == rhs.xsk.nsk + && self.xsk.ovk == rhs.xsk.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 @@ -288,6 +313,15 @@ impl ExtendedSpendingKey { } } + /// 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.xsk, &JUBJUB); let tmp = match i { @@ -404,4 +438,25 @@ mod tests { 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 + ); + } } From fea07c52decc09d218eb15e23e7f768af42a96d2 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 19 Jul 2018 11:57:54 -0400 Subject: [PATCH 07/19] Diversifier derivation --- Cargo.toml | 2 ++ src/lib.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 93978c5..48cc9ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ homepage = "https://github.com/zcash-hackworks/zip32" repository = "https://github.com/zcash-hackworks/zip32" [dependencies] +aes = "0.2" byteorder = "1" +fpe = "0.1" lazy_static = "1.0" pairing = "0.14.2" diff --git a/src/lib.rs b/src/lib.rs index 9c364bb..6ad3c21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,20 @@ +extern crate aes; extern crate blake2_rfc; extern crate byteorder; +extern crate fpe; #[macro_use] extern crate lazy_static; extern crate pairing; extern crate sapling_crypto; +use aes::Aes256; use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; use byteorder::{ByteOrder, LittleEndian}; +use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, - primitives::ViewingKey, + primitives::{Diversifier, ViewingKey}, }; lazy_static! { @@ -208,6 +212,31 @@ impl ChildIndex { #[derive(Clone, Copy, Debug, PartialEq)] struct ChainCode([u8; 32]); +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct DiversifierIndex([u8; 11]); + +impl DiversifierIndex { + fn new() -> Self { + DiversifierIndex([0; 11]) + } + + pub fn increment(&mut self) -> Result<(), ()> { + let mut k = 0; + loop { + self.0[k] += 1; + if self.0[k] != 0 { + // No overflow + return Ok(()); + } + // Overflow + k += 1; + if k == 11 { + return Err(()); + } + } + } +} + /// A key used to derive diversifiers for a particular child key #[derive(Clone, Copy, Debug, PartialEq)] struct DiversifierKey([u8; 32]); @@ -224,6 +253,29 @@ impl DiversifierKey { 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. + 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 @@ -459,4 +511,36 @@ mod tests { 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); + } } From 3b31854de774bfd706855be131337c9e52c88f82 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 19 Jul 2018 12:19:29 -0400 Subject: [PATCH 08/19] Payment address derivation --- src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6ad3c21..f3f27dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, - primitives::{Diversifier, ViewingKey}, + primitives::{Diversifier, PaymentAddress, ViewingKey}, }; lazy_static! { @@ -407,6 +407,10 @@ impl ExtendedSpendingKey { 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 { @@ -448,6 +452,24 @@ impl ExtendedFullViewingKey { 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.into_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)] @@ -543,4 +565,17 @@ mod tests { 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] + ); + } } From e47cafd6915839ec5cd018bdef9f6c5eaef25215 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 02:51:18 +0100 Subject: [PATCH 09/19] ExtendedSpendingKey::write() and ExtendedFullViewingKey::write() --- src/lib.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f3f27dc..af60702 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,13 +9,14 @@ extern crate sapling_crypto; use aes::Aes256; use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; -use byteorder::{ByteOrder, LittleEndian}; +use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, primitives::{Diversifier, PaymentAddress, ViewingKey}, }; +use std::io::{self, Write}; lazy_static! { static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; @@ -206,6 +207,13 @@ impl ChildIndex { 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 @@ -365,6 +373,17 @@ impl ExtendedSpendingKey { } } + 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.xsk.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(); @@ -427,6 +446,17 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { } impl ExtendedFullViewingKey { + 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(()), From 342d97ac15ec210f45bf9d6e14f4c5dbfd2e7cdd Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 02:52:41 +0100 Subject: [PATCH 10/19] Test vectors from Python implementation --- src/lib.rs | 507 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 507 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index af60702..6405b7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -608,4 +608,511 @@ mod tests { [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19] ); } + + #[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.xsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.ask.unwrap()); + xsk.xsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.nsk.unwrap()); + + assert_eq!(xsk.xsk.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); + + 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()), + } + } + } } From 9c9607e47ad2b4d39169325d7c8bf9630ff8cd14 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 03:00:19 +0100 Subject: [PATCH 11/19] Add missing wrapping_add() caught by test vectors --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6405b7b..a233436 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,7 +231,7 @@ impl DiversifierIndex { pub fn increment(&mut self) -> Result<(), ()> { let mut k = 0; loop { - self.0[k] += 1; + self.0[k] = self.0[k].wrapping_add(1); if self.0[k] != 0 { // No overflow return Ok(()); From 39f978bb4f21d995d8f68d4d5ac45a302eb88dbc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 09:56:20 +0100 Subject: [PATCH 12/19] ExtendedSpendingKey::read() and ExtendedFullViewingKey::read() --- src/lib.rs | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a233436..2bc48d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,14 +9,16 @@ extern crate sapling_crypto; use aes::Aes256; use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; -use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; use sapling_crypto::{ - jubjub::{FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform}, + jubjub::{ + edwards, FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform, Unknown, + }, primitives::{Diversifier, PaymentAddress, ViewingKey}, }; -use std::io::{self, Write}; +use std::io::{self, Read, Write}; lazy_static! { static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; @@ -87,6 +89,27 @@ impl ExpandedSpendingKey { ExpandedSpendingKey { ask, nsk, ovk } } + pub fn read(mut reader: R) -> io::Result { + let mut ask_repr = ::Repr::default(); + ask_repr.read_le(&mut reader)?; + let ask = + E::Fs::from_repr(ask_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let mut nsk_repr = ::Repr::default(); + nsk_repr.read_le(&mut reader)?; + let nsk = + E::Fs::from_repr(nsk_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let mut ovk = [0; 32]; + reader.read_exact(&mut ovk)?; + + Ok(ExpandedSpendingKey { + ask, + nsk, + ovk: OutgoingViewingKey(ovk), + }) + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; self.ask @@ -135,6 +158,38 @@ impl FullViewingKey { } } + pub fn read(mut reader: R, params: &E::Params) -> io::Result { + let ak = edwards::Point::::read(&mut reader, params)?; + let ak = match ak.as_prime_order(params) { + Some(p) => p, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "ak not of prime order", + )) + } + }; + + let nk = edwards::Point::::read(&mut reader, params)?; + let nk = match nk.as_prime_order(params) { + Some(p) => p, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "nk not of prime order", + )) + } + }; + + let mut ovk = [0; 32]; + reader.read_exact(&mut ovk)?; + + Ok(FullViewingKey { + vk: ViewingKey { ak, nk }, + ovk: OutgoingViewingKey(ovk), + }) + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; self.vk @@ -373,6 +428,27 @@ impl ExtendedSpendingKey { } } + 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 xsk = 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), + xsk, + 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)?; @@ -446,6 +522,27 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { } 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)?; @@ -609,6 +706,23 @@ mod tests { ); } + #[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 { From 599079fafe95b1bbc5c04777c467b9277d742b7b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 10:38:07 +0100 Subject: [PATCH 13/19] Rename xsk: ExpandedSpendingKey -> expsk to disambiguate --- src/lib.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2bc48d2..08bd0bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,17 +126,17 @@ impl ExpandedSpendingKey { } impl FullViewingKey { - fn from_expanded_spending_key(xsk: &ExpandedSpendingKey, params: &E::Params) -> Self { + fn from_expanded_spending_key(expsk: &ExpandedSpendingKey, params: &E::Params) -> Self { FullViewingKey { vk: ViewingKey { ak: params .generator(FixedGenerators::SpendingKeyGenerator) - .mul(xsk.ask, params), + .mul(expsk.ask, params), nk: params .generator(FixedGenerators::ProofGenerationKey) - .mul(xsk.nsk, params), + .mul(expsk.nsk, params), }, - ovk: xsk.ovk, + ovk: expsk.ovk, } } @@ -348,7 +348,7 @@ pub struct ExtendedSpendingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - xsk: ExpandedSpendingKey, + expsk: ExpandedSpendingKey, dk: DiversifierKey, } @@ -368,9 +368,9 @@ impl std::cmp::PartialEq for ExtendedSpendingKey { && self.parent_fvk_tag == rhs.parent_fvk_tag && self.child_index == rhs.child_index && self.chain_code == rhs.chain_code - && self.xsk.ask == rhs.xsk.ask - && self.xsk.nsk == rhs.xsk.nsk - && self.xsk.ovk == rhs.xsk.ovk + && self.expsk.ask == rhs.expsk.ask + && self.expsk.nsk == rhs.expsk.nsk + && self.expsk.ovk == rhs.expsk.ovk && self.dk == rhs.dk } } @@ -423,7 +423,7 @@ impl ExtendedSpendingKey { parent_fvk_tag: FVKTag::master(), child_index: ChildIndex::master(), chain_code: ChainCode(c_m), - xsk: ExpandedSpendingKey::from_spending_key(sk_m), + expsk: ExpandedSpendingKey::from_spending_key(sk_m), dk: DiversifierKey::master(sk_m), } } @@ -435,7 +435,7 @@ impl ExtendedSpendingKey { let i = reader.read_u32::()?; let mut c = [0; 32]; reader.read_exact(&mut c)?; - let xsk = ExpandedSpendingKey::read(&mut reader)?; + let expsk = ExpandedSpendingKey::read(&mut reader)?; let mut dk = [0; 32]; reader.read_exact(&mut dk)?; @@ -444,7 +444,7 @@ impl ExtendedSpendingKey { parent_fvk_tag: FVKTag(tag), child_index: ChildIndex::from_index(i), chain_code: ChainCode(c), - xsk, + expsk, dk: DiversifierKey(dk), }) } @@ -454,7 +454,7 @@ impl ExtendedSpendingKey { 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.xsk.to_bytes())?; + writer.write_all(&self.expsk.to_bytes())?; writer.write_all(&self.dk.0)?; Ok(()) @@ -470,14 +470,14 @@ impl ExtendedSpendingKey { } pub fn derive_child(&self, i: ChildIndex) -> Self { - let fvk = FullViewingKey::from_expanded_spending_key(&self.xsk, &JUBJUB); + 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.xsk.to_bytes(), &self.dk.0, &le_i], + &[&[0x11], &self.expsk.to_bytes(), &self.dk.0, &le_i], ) } ChildIndex::NonHardened(i) => { @@ -498,7 +498,7 @@ impl ExtendedSpendingKey { parent_fvk_tag: FVKFingerprint::from(&fvk).into(), child_index: i, chain_code: ChainCode(c_i), - xsk: self.xsk.derive_child(i_l), + expsk: self.expsk.derive_child(i_l), dk: self.dk.derive_child(i_l), } } @@ -515,7 +515,7 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { parent_fvk_tag: xsk.parent_fvk_tag, child_index: xsk.child_index, chain_code: xsk.chain_code, - fvk: FullViewingKey::from_expanded_spending_key(&xsk.xsk, &JUBJUB), + fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB), dk: xsk.dk, } } @@ -1163,12 +1163,12 @@ mod tests { let tv = &test_vectors[j]; let mut buf = [0; 32]; - xsk.xsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); + xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); assert_eq!(buf, tv.ask.unwrap()); - xsk.xsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); + xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); assert_eq!(buf, tv.nsk.unwrap()); - assert_eq!(xsk.xsk.ovk.0, tv.ovk); + assert_eq!(xsk.expsk.ovk.0, tv.ovk); assert_eq!(xsk.dk.0, tv.dk); assert_eq!(xsk.chain_code.0, tv.c); From 80130f565651172d0c55d19c0ca2d47c097d4634 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 1 Aug 2018 10:41:50 +0100 Subject: [PATCH 14/19] Expose underlying ExpandedSpendingKey and FullViewingKey for writing --- src/lib.rs | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 08bd0bb..4a93fdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,14 +58,14 @@ impl OutgoingViewingKey { /// A Sapling expanded spending key #[derive(Clone)] -struct ExpandedSpendingKey { +pub struct ExpandedSpendingKey { ask: E::Fs, nsk: E::Fs, ovk: OutgoingViewingKey, } /// A Sapling full viewing key -struct FullViewingKey { +pub struct FullViewingKey { vk: ViewingKey, ovk: OutgoingViewingKey, } @@ -110,17 +110,18 @@ impl ExpandedSpendingKey { }) } + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.ask.into_repr().write_le(&mut writer)?; + self.nsk.into_repr().write_le(&mut writer)?; + writer.write_all(&self.ovk.0)?; + + Ok(()) + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; - self.ask - .into_repr() - .write_le(&mut result[..32]) - .expect("length is 32 bytes"); - self.nsk - .into_repr() - .write_le(&mut result[32..64]) - .expect("length is 32 bytes"); - (&mut result[64..]).copy_from_slice(&self.ovk.0); + self.write(&mut result[..]) + .expect("should be able to serialize an ExpandedSpendingKey"); result } } @@ -190,17 +191,18 @@ impl FullViewingKey { }) } + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.vk.ak.write(&mut writer)?; + self.vk.nk.write(&mut writer)?; + writer.write_all(&self.ovk.0)?; + + Ok(()) + } + fn to_bytes(&self) -> [u8; 96] { let mut result = [0u8; 96]; - self.vk - .ak - .write(&mut result[..32]) - .expect("length is 32 bytes"); - self.vk - .nk - .write(&mut result[32..64]) - .expect("length is 32 bytes"); - (&mut result[64..]).copy_from_slice(&self.ovk.0); + self.write(&mut result[..]) + .expect("should be able to serialize a FullViewingKey"); result } } @@ -348,7 +350,7 @@ pub struct ExtendedSpendingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - expsk: ExpandedSpendingKey, + pub expsk: ExpandedSpendingKey, dk: DiversifierKey, } @@ -358,7 +360,7 @@ pub struct ExtendedFullViewingKey { parent_fvk_tag: FVKTag, child_index: ChildIndex, chain_code: ChainCode, - fvk: FullViewingKey, + pub fvk: FullViewingKey, dk: DiversifierKey, } From a4e86be66581a67d06d7371bb9c50c53946ee15d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 3 Aug 2018 13:22:35 +0100 Subject: [PATCH 15/19] Check ivk in test vectors --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4a93fdf..4e0b25e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1193,6 +1193,14 @@ mod tests { 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[..]); From ac8b27a5ba3f5ee52a48f761422d2a758d423271 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 3 Aug 2018 13:22:50 +0100 Subject: [PATCH 16/19] Expose contents of DiversifierIndex --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4e0b25e..b89576f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,7 +278,7 @@ impl ChildIndex { struct ChainCode([u8; 32]); #[derive(Clone, Copy, Debug, PartialEq)] -pub struct DiversifierIndex([u8; 11]); +pub struct DiversifierIndex(pub [u8; 11]); impl DiversifierIndex { fn new() -> Self { From e27fc674f52d900d28037095ef4dc3c80670fd75 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Aug 2018 23:45:56 +0100 Subject: [PATCH 17/19] Tidy up DiversifierIndex::increment() --- src/lib.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b89576f..088a8a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,19 +286,15 @@ impl DiversifierIndex { } pub fn increment(&mut self) -> Result<(), ()> { - let mut k = 0; - loop { + for k in 0..11 { self.0[k] = self.0[k].wrapping_add(1); if self.0[k] != 0 { // No overflow return Ok(()); } - // Overflow - k += 1; - if k == 11 { - return Err(()); - } } + // Overflow + Err(()) } } From 770c4bef252c047fdcb79e1393578e65ed30834e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Aug 2018 23:59:20 +0100 Subject: [PATCH 18/19] Simplify creation of FVKFingerprint and FVKTag --- src/lib.rs | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 088a8a1..557fbbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,6 +205,14 @@ impl FullViewingKey { .expect("should be able to serialize a FullViewingKey"); result } + + fn fingerprint(&self) -> FVKFingerprint { + let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION); + h.update(&self.to_bytes()); + let mut fvfp = [0u8; 32]; + fvfp.copy_from_slice(h.finalize().as_bytes()); + FVKFingerprint(fvfp) + } } // ZIP 32 structures @@ -212,34 +220,18 @@ impl FullViewingKey { /// A Sapling full viewing key fingerprint struct FVKFingerprint([u8; 32]); -impl<'a, E: JubjubEngine> From<&'a FullViewingKey> for FVKFingerprint { - fn from(fvk: &FullViewingKey) -> Self { - let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION); - 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<'a> From<&'a FVKFingerprint> for FVKTag { - fn from(fingerprint: &FVKFingerprint) -> Self { +impl FVKFingerprint { + fn tag(&self) -> FVKTag { let mut tag = [0u8; 4]; - tag.copy_from_slice(&fingerprint.0[..4]); + tag.copy_from_slice(&self.0[..4]); FVKTag(tag) } } -impl From for FVKTag { - fn from(fingerprint: FVKFingerprint) -> Self { - (&fingerprint).into() - } -} - impl FVKTag { fn master() -> Self { FVKTag([0u8; 4]) @@ -493,7 +485,7 @@ impl ExtendedSpendingKey { ExtendedSpendingKey { depth: self.depth + 1, - parent_fvk_tag: FVKFingerprint::from(&fvk).into(), + parent_fvk_tag: fvk.fingerprint().tag(), child_index: i, chain_code: ChainCode(c_i), expsk: self.expsk.derive_child(i_l), @@ -570,7 +562,7 @@ impl ExtendedFullViewingKey { Ok(ExtendedFullViewingKey { depth: self.depth + 1, - parent_fvk_tag: FVKFingerprint::from(&self.fvk).into(), + parent_fvk_tag: self.fvk.fingerprint().tag(), child_index: i, chain_code: ChainCode(c_i), fvk: self.fvk.derive_child(i_l, &JUBJUB), @@ -1200,7 +1192,7 @@ mod tests { let mut ser = vec![]; xfvk.write(&mut ser).unwrap(); assert_eq!(&ser[..], &tv.xfvk[..]); - assert_eq!(FVKFingerprint::from(&xfvk.fvk).0, tv.fp); + assert_eq!(xfvk.fvk.fingerprint().0, tv.fp); // d0 let mut di = DiversifierIndex::new(); From 6671234ad12051bb2ae15f43aea72a9e7bddb65e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 22 Aug 2018 23:59:40 +0100 Subject: [PATCH 19/19] cargo fmt --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 557fbbb..70173ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -314,7 +314,8 @@ impl DiversifierKey { let ff = FF1::::new(&self.0, 2).unwrap(); loop { // Generate d_j - let enc = ff.encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.0[..])) + 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());