Skip to content

Commit

Permalink
Migrate to zip32::hardened_only implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Oct 1, 2024
1 parent 85249b4 commit 26e0f71
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 51 deletions.
11 changes: 5 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ debug = true

[profile.bench]
debug = true

[patch.crates-io]
zcash_spec = { git = "https://github.com/zcash/zcash_spec.git", rev = "569f92d01504deb7b092f4cff1c07a4f60ecfa11" }
zip32 = { git = "https://github.com/zcash/zip32.git", rev = "aebb0214573ccb9d47a8cd84919e8e23c12928a2" }
77 changes: 32 additions & 45 deletions src/zip32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::fmt;

use blake2b_simd::Params as Blake2bParams;
use subtle::{Choice, ConstantTimeEq, CtOption};
use zip32::ChainCode;
use zip32::hardened_only::{self, HardenedOnlyKey};

use crate::{
keys::{FullViewingKey, SpendingKey},
Expand Down Expand Up @@ -116,6 +116,30 @@ impl KeyIndex {
}
}

#[derive(Clone, Copy, Debug)]
struct Orchard;

impl hardened_only::Context for Orchard {
const MKG_DOMAIN: [u8; 16] = *ZIP32_ORCHARD_PERSONALIZATION;
const CKD_DOMAIN: PrfExpand<([u8; 32], [u8; 4])> = PrfExpand::ORCHARD_ZIP32_CHILD;

type Key = SpendingKey;
type KeyError = Error;

fn convert_to_key(sk: [u8; 32]) -> Result<Self::Key, Self::KeyError> {
let sk = SpendingKey::from_bytes(sk);
if sk.is_none().into() {
Err(Error::InvalidSpendingKey)
} else {
Ok(sk.unwrap())
}
}

fn key_bytes(sk: &Self::Key) -> &[u8; 32] {
sk.to_bytes()
}
}

/// An Orchard extended spending key.
///
/// Defined in [ZIP32: Orchard extended keys][orchardextendedkeys].
Expand All @@ -126,17 +150,15 @@ pub(crate) struct ExtendedSpendingKey {
depth: u8,
parent_fvk_tag: FvkTag,
child_index: KeyIndex,
chain_code: ChainCode,
sk: SpendingKey,
inner: HardenedOnlyKey<Orchard>,
}

impl ConstantTimeEq for ExtendedSpendingKey {
fn ct_eq(&self, rhs: &Self) -> Choice {
self.depth.ct_eq(&rhs.depth)
& self.parent_fvk_tag.0.ct_eq(&rhs.parent_fvk_tag.0)
& self.child_index.ct_eq(&rhs.child_index)
& self.chain_code.ct_eq(&rhs.chain_code)
& self.sk.ct_eq(&rhs.sk)
& self.inner.ct_eq(&rhs.inner)
}
}

Expand Down Expand Up @@ -166,33 +188,14 @@ impl ExtendedSpendingKey {
///
/// Panics if the seed is shorter than 32 bytes or longer than 252 bytes.
fn master(seed: &[u8]) -> Result<Self, Error> {
assert!(seed.len() >= 32 && seed.len() <= 252);
// I := BLAKE2b-512("ZcashIP32Orchard", seed)
let I: [u8; 64] = {
let mut I = Blake2bParams::new()
.hash_length(64)
.personal(ZIP32_ORCHARD_PERSONALIZATION)
.to_state();
I.update(seed);
I.finalize().as_bytes().try_into().unwrap()
};
// I_L is used as the master spending key sk_m.
let sk_m = SpendingKey::from_bytes(I[..32].try_into().unwrap());
if sk_m.is_none().into() {
return Err(Error::InvalidSpendingKey);
}
let sk_m = sk_m.unwrap();

// I_R is used as the master chain code c_m.
let c_m = ChainCode::new(I[32..].try_into().unwrap());
let m_orchard = HardenedOnlyKey::master(&[seed])?;

// For the master extended spending key, depth is 0, parent_fvk_tag is 4 zero bytes, and i is 0.
Ok(Self {
depth: 0,
parent_fvk_tag: FvkTag([0; 4]),
child_index: KeyIndex::master(),
chain_code: c_m,
sk: sk_m,
inner: m_orchard,
})
}

Expand All @@ -204,37 +207,21 @@ impl ExtendedSpendingKey {
///
/// Discards index if it results in an invalid sk
fn derive_child(&self, index: ChildIndex) -> Result<Self, Error> {
// I := PRF^Expand(c_par, [0x81] || sk_par || I2LEOSP(i))
let I: [u8; 64] = PrfExpand::ORCHARD_ZIP32_CHILD.with(
self.chain_code.as_bytes(),
self.sk.to_bytes(),
&index.index().to_le_bytes(),
);

// I_L is used as the child spending key sk_i.
let sk_i = SpendingKey::from_bytes(I[..32].try_into().unwrap());
if sk_i.is_none().into() {
return Err(Error::InvalidSpendingKey);
}
let sk_i = sk_i.unwrap();

// I_R is used as the child chain code c_i.
let c_i = ChainCode::new(I[32..].try_into().unwrap());
let child_i = self.inner.derive_child(index)?;

let fvk: FullViewingKey = self.into();

Ok(Self {
depth: self.depth + 1,
parent_fvk_tag: FvkFingerprint::from(&fvk).tag(),
child_index: KeyIndex::child(index),
chain_code: c_i,
sk: sk_i,
inner: child_i,
})
}

/// Returns sk of this ExtendedSpendingKey.
pub fn sk(&self) -> SpendingKey {
self.sk
*self.inner.parts().0
}
}

Expand Down

0 comments on commit 26e0f71

Please sign in to comment.