use zcash_spec::PrfExpand;
use crate::{
hardened_only::{Context, HardenedOnlyKey},
ChainCode, ChildIndex,
};
struct Arbitrary;
impl Context for Arbitrary {
const MKG_DOMAIN: [u8; 16] = *b"ZcashArbitraryKD";
const CKD_DOMAIN: PrfExpand<([u8; 32], [u8; 4])> = PrfExpand::ARBITRARY_ZIP32_CHILD;
}
pub struct SecretKey {
inner: HardenedOnlyKey<Arbitrary>,
}
impl SecretKey {
pub fn from_path(context_string: &[u8], seed: &[u8], path: &[ChildIndex]) -> Self {
let mut xsk = Self::master(context_string, seed);
for i in path {
xsk = xsk.derive_child(*i);
}
xsk
}
fn master(context_string: &[u8], seed: &[u8]) -> Self {
let context_len =
u8::try_from(context_string.len()).expect("context string should be at most 252 bytes");
assert!((1..=252).contains(&context_len));
let seed_len = u8::try_from(seed.len()).expect("seed should be at most 252 bytes");
assert!((32..=252).contains(&seed_len));
let ikm = &[&[context_len], context_string, &[seed_len], seed];
Self {
inner: HardenedOnlyKey::master(ikm),
}
}
fn derive_child(&self, index: ChildIndex) -> Self {
Self {
inner: self.inner.derive_child(index),
}
}
pub fn data(&self) -> &[u8; 32] {
self.inner.parts().0
}
pub fn chain_code(&self) -> &ChainCode {
self.inner.parts().1
}
pub fn into_full_width_key(self) -> [u8; 64] {
let (sk, c) = self.inner.into_parts();
let mut key = [0; 64];
key[..32].copy_from_slice(&sk);
key[32..].copy_from_slice(&c.0);
key
}
}