From 72666011a45e1b870762eb39ba5ceff0c1b94871 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 11 Sep 2023 19:44:22 -0500 Subject: [PATCH] keyfork-derive-util: fixup request API, publicity of ExtendedPrivateKey --- .../src/extended_key/private_key.rs | 38 ++++++++++++++++--- keyfork-derive-util/src/path.rs | 8 ++++ keyfork-derive-util/src/request.rs | 36 ++++++++++++++---- keyfork-derive-util/src/tests.rs | 8 ++-- 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/keyfork-derive-util/src/extended_key/private_key.rs b/keyfork-derive-util/src/extended_key/private_key.rs index c0a94be..564d6bb 100644 --- a/keyfork-derive-util/src/extended_key/private_key.rs +++ b/keyfork-derive-util/src/extended_key/private_key.rs @@ -29,6 +29,10 @@ pub enum Error { /// The algorithm used mandates hardened derivation only. #[error("The algorithm used mandates hardened derivation only")] HardenedDerivationRequired, + + /// The given slice was of an inappropriate size to create a Private Key. + #[error("The given slice was of an inappropriate size to create a Private Key")] + InvalidSliceError(#[from] std::array::TryFromSliceError), } type Result = std::result::Result; @@ -41,9 +45,9 @@ type HmacSha512 = Hmac; #[derive(Clone, Serialize, Deserialize)] pub struct ExtendedPrivateKey { /// The internal private key data. - pub private_key: K, + private_key: K, depth: u8, - pub(crate) chain_code: ChainCode, + chain_code: ChainCode, } impl std::fmt::Debug for ExtendedPrivateKey { @@ -88,18 +92,42 @@ where .into_bytes(); let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8); + Self::new_from_parts( + private_key, + 0, + // Checked: chain_code is always the same length, hash is static size + chain_code.try_into().expect("Invalid chain code length"), + ) + } + + pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result { Ok(Self { - private_key: K::from_bytes(private_key.try_into().expect("Invalid key length")), - depth: 0, - chain_code: chain_code.try_into().expect("Invalid chain code length"), + private_key: K::from_bytes(seed.try_into()?), + depth, + chain_code, }) } + /// Returns a reference to the [`PrivateKey`]. + pub fn private_key(&self) -> &K { + &self.private_key + } + /// Return a public key for the current [`PrivateKey`]. pub fn public_key(&self) -> K::PublicKey { self.private_key.public_key() } + /// Returns the current depth. + pub fn depth(&self) -> u8 { + self.depth + } + + /// Returns a copy of the current chain code. + pub fn chain_code(&self) -> [u8; 32] { + self.chain_code + } + /// Derive a child using the given [`DerivationPath`]. /// /// # Errors diff --git a/keyfork-derive-util/src/path.rs b/keyfork-derive-util/src/path.rs index 03522e5..431513c 100644 --- a/keyfork-derive-util/src/path.rs +++ b/keyfork-derive-util/src/path.rs @@ -34,6 +34,14 @@ impl DerivationPath { pub fn iter(&self) -> impl Iterator { self.path.iter() } + + pub fn len(&self) -> usize { + self.path.len() + } + + pub fn is_empty(&self) -> bool { + self.path.is_empty() + } } impl std::str::FromStr for DerivationPath { diff --git a/keyfork-derive-util/src/request.rs b/keyfork-derive-util/src/request.rs index 7ec84e4..5245037 100644 --- a/keyfork-derive-util/src/request.rs +++ b/keyfork-derive-util/src/request.rs @@ -28,19 +28,19 @@ impl DerivationAlgorithm { Self::Ed25519 => { let key = ExtendedPrivateKey::::new(seed)?; let derived_key = key.derive_path(path)?; - Ok(DerivationResponse { - algorithm: self.clone(), - data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(), - }) + Ok(DerivationResponse::with_algo_and_xprv( + self.clone(), + &derived_key, + )) } #[cfg(feature = "secp256k1")] Self::Secp256k1 => { let key = ExtendedPrivateKey::::new(seed)?; let derived_key = key.derive_path(path)?; - Ok(DerivationResponse { - algorithm: self.clone(), - data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(), - }) + Ok(DerivationResponse::with_algo_and_xprv( + self.clone(), + &derived_key, + )) } #[allow(unreachable_patterns)] _ => Err(DerivationError::Algorithm), @@ -59,6 +59,10 @@ impl DerivationRequest { Self { algorithm, path } } + pub fn path(&self) -> &DerivationPath { + &self.path + } + pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result { self.derive_with_master_seed(mnemonic.seed()) } @@ -72,4 +76,20 @@ impl DerivationRequest { pub struct DerivationResponse { pub algorithm: DerivationAlgorithm, pub data: Vec, + pub chain_code: [u8; 32], + pub depth: u8, +} + +impl DerivationResponse { + pub fn with_algo_and_xprv( + algorithm: DerivationAlgorithm, + xprv: &ExtendedPrivateKey, + ) -> Self { + Self { + algorithm, + data: PrivateKey::to_bytes(xprv.private_key()).to_vec(), + chain_code: xprv.chain_code(), + depth: xprv.depth(), + } + } } diff --git a/keyfork-derive-util/src/tests.rs b/keyfork-derive-util/src/tests.rs index b4a74a3..eb529fd 100644 --- a/keyfork-derive-util/src/tests.rs +++ b/keyfork-derive-util/src/tests.rs @@ -62,8 +62,8 @@ fn secp256k1() { for (seed, chain, chain_code, private_key, public_key) in tests { let xkey = ExtendedPrivateKey::::new(seed).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap(); - assert_eq!(derived_key.chain_code, chain_code); - assert_eq!(derived_key.private_key.to_bytes().as_slice(), private_key); + assert_eq!(derived_key.chain_code(), chain_code); + assert_eq!(derived_key.private_key().to_bytes().as_slice(), private_key); assert_eq!(derived_key.public_key().to_bytes(), public_key); let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, chain); let response = request.derive_with_master_seed(seed.to_vec()).unwrap(); @@ -103,8 +103,8 @@ fn ed25519() { for (seed, chain, chain_code, private_key, public_key) in tests { let xkey = ExtendedPrivateKey::::new(seed).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap(); - assert_eq!(derived_key.chain_code, chain_code); - assert_eq!(PrivateKey::to_bytes(&derived_key.private_key), private_key); + assert_eq!(derived_key.chain_code(), chain_code); + assert_eq!(PrivateKey::to_bytes(derived_key.private_key()), private_key); assert_eq!(PublicKey::to_bytes(&derived_key.public_key()), public_key); let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, chain); let response = request.derive_with_master_seed(seed.to_vec()).unwrap();