use crate::governance::error::{GovernanceError, GovernanceResult};
use hmac::{Hmac, Mac};
use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey};
use sha2::Sha512;
type HmacSha512 = Hmac<Sha512>;
#[derive(Debug, Clone)]
pub struct ExtendedPrivateKey {
pub depth: u8,
pub parent_fingerprint: [u8; 4],
pub child_number: u32,
pub chain_code: [u8; 32],
pub private_key: SecretKey,
}
#[derive(Debug, Clone)]
pub struct ExtendedPublicKey {
pub depth: u8,
pub parent_fingerprint: [u8; 4],
pub child_number: u32,
pub chain_code: [u8; 32],
pub public_key: PublicKey,
}
pub fn derive_master_key(seed: &[u8]) -> GovernanceResult<(ExtendedPrivateKey, ExtendedPublicKey)> {
if seed.len() < 16 || seed.len() > 64 {
return Err(GovernanceError::InvalidInput(
"Seed must be 16-64 bytes".to_string(),
));
}
let mut hmac = HmacSha512::new_from_slice(b"Bitcoin seed")
.map_err(|e| GovernanceError::InvalidInput(format!("HMAC key error: {e}")))?;
hmac.update(seed);
let result = hmac.finalize();
let bytes = result.into_bytes();
let mut private_key_bytes = [0u8; 32];
private_key_bytes.copy_from_slice(&bytes[..32]);
let mut chain_code = [0u8; 32];
chain_code.copy_from_slice(&bytes[32..]);
let secp = Secp256k1::new();
let private_key = SecretKey::from_slice(&private_key_bytes)
.map_err(|e| GovernanceError::InvalidKey(format!("Invalid master private key: {e}")))?;
let public_key = private_key.public_key(&secp);
let xprv = ExtendedPrivateKey {
depth: 0,
parent_fingerprint: [0u8; 4],
child_number: 0,
chain_code,
private_key,
};
let xpub = ExtendedPublicKey {
depth: 0,
parent_fingerprint: [0u8; 4],
child_number: 0,
chain_code,
public_key,
};
Ok((xprv, xpub))
}
pub fn derive_child_private(
parent: &ExtendedPrivateKey,
child_number: u32,
) -> GovernanceResult<(ExtendedPrivateKey, ExtendedPublicKey)> {
let secp = Secp256k1::new();
let is_hardened = child_number >= 0x80000000;
let mut data = Vec::with_capacity(37);
if is_hardened {
data.push(0x00);
data.extend_from_slice(&parent.private_key.secret_bytes());
} else {
let parent_pubkey = parent.private_key.public_key(&secp);
data.extend_from_slice(&parent_pubkey.serialize());
}
data.extend_from_slice(&child_number.to_be_bytes());
let parent_pubkey = parent.private_key.public_key(&secp);
let parent_fingerprint = calculate_fingerprint(&parent_pubkey.serialize());
let mut hmac = HmacSha512::new_from_slice(&parent.chain_code)
.map_err(|e| GovernanceError::InvalidInput(format!("HMAC error: {e}")))?;
hmac.update(&data);
let result = hmac.finalize();
let bytes = result.into_bytes();
let mut il = [0u8; 32];
il.copy_from_slice(&bytes[..32]);
let mut child_chain_code = [0u8; 32];
child_chain_code.copy_from_slice(&bytes[32..]);
let il_scalar = Scalar::from_be_bytes(il)
.map_err(|_| GovernanceError::InvalidKey("IL cannot be converted to scalar".to_string()))?;
let child_private = parent.private_key.add_tweak(&il_scalar).map_err(|_| {
GovernanceError::InvalidKey("Key addition resulted in zero or invalid key".to_string())
})?;
let child_public = child_private.public_key(&secp);
let child_xprv = ExtendedPrivateKey {
depth: parent.depth + 1,
parent_fingerprint,
child_number,
chain_code: child_chain_code,
private_key: child_private,
};
let child_xpub = ExtendedPublicKey {
depth: parent.depth + 1,
parent_fingerprint,
child_number,
chain_code: child_chain_code,
public_key: child_public,
};
Ok((child_xprv, child_xpub))
}
pub fn derive_child_public(
parent: &ExtendedPublicKey,
child_number: u32,
) -> GovernanceResult<ExtendedPublicKey> {
if child_number >= 0x80000000 {
return Err(GovernanceError::InvalidInput(
"Hardened derivation requires private key".to_string(),
));
}
let mut data = Vec::with_capacity(37);
data.extend_from_slice(&parent.public_key.serialize());
data.extend_from_slice(&child_number.to_be_bytes());
let parent_fingerprint = calculate_fingerprint(&parent.public_key.serialize());
let mut hmac = HmacSha512::new_from_slice(&parent.chain_code)
.map_err(|e| GovernanceError::InvalidInput(format!("HMAC error: {e}")))?;
hmac.update(&data);
let result = hmac.finalize();
let bytes = result.into_bytes();
let mut il = [0u8; 32];
il.copy_from_slice(&bytes[..32]);
let mut child_chain_code = [0u8; 32];
child_chain_code.copy_from_slice(&bytes[32..]);
let il_scalar = Scalar::from_be_bytes(il)
.map_err(|_| GovernanceError::InvalidKey("Invalid scalar".to_string()))?;
let secp = Secp256k1::new();
let child_public = parent
.public_key
.add_exp_tweak(&secp, &il_scalar)
.map_err(|_| GovernanceError::InvalidKey("Point addition failed".to_string()))?;
Ok(ExtendedPublicKey {
depth: parent.depth + 1,
parent_fingerprint,
child_number,
chain_code: child_chain_code,
public_key: child_public,
})
}
fn calculate_fingerprint(pubkey: &[u8]) -> [u8; 4] {
use ripemd::{Digest as RipemdDigest, Ripemd160};
use sha2::Sha256;
let mut sha256 = Sha256::new();
sha256.update(pubkey);
let sha256_hash = sha256.finalize();
let mut ripemd = Ripemd160::new();
ripemd.update(sha256_hash);
let ripemd_hash = ripemd.finalize();
let mut fingerprint = [0u8; 4];
fingerprint.copy_from_slice(&ripemd_hash[..4]);
fingerprint
}
impl ExtendedPrivateKey {
pub fn to_extended_public(&self) -> ExtendedPublicKey {
let secp = Secp256k1::new();
ExtendedPublicKey {
depth: self.depth,
parent_fingerprint: self.parent_fingerprint,
child_number: self.child_number,
chain_code: self.chain_code,
public_key: self.private_key.public_key(&secp),
}
}
pub fn derive_child(
&self,
child_number: u32,
) -> GovernanceResult<(ExtendedPrivateKey, ExtendedPublicKey)> {
derive_child_private(self, child_number)
}
pub fn private_key_bytes(&self) -> [u8; 32] {
self.private_key.secret_bytes()
}
}
impl ExtendedPublicKey {
pub fn derive_child(&self, child_number: u32) -> GovernanceResult<ExtendedPublicKey> {
derive_child_public(self, child_number)
}
pub fn public_key_bytes(&self) -> [u8; 33] {
self.public_key.serialize()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_master_key_derivation() {
let seed = b"Hello, Bitcoin Commons!";
let (xprv, xpub) = derive_master_key(seed).unwrap();
assert_eq!(xprv.depth, 0);
assert_eq!(xpub.depth, 0);
assert_eq!(xprv.child_number, 0);
assert_eq!(xpub.child_number, 0);
}
#[test]
fn test_child_derivation() {
let seed = b"test seed for BIP32";
let (master_xprv, _master_xpub) = derive_master_key(seed).unwrap();
let (child_xprv, child_xpub) = master_xprv.derive_child(0).unwrap();
assert_eq!(child_xprv.depth, 1);
assert_eq!(child_xpub.depth, 1);
assert_eq!(child_xprv.child_number, 0);
let derived_xpub = child_xprv.to_extended_public();
assert_eq!(
derived_xpub.public_key_bytes(),
child_xpub.public_key_bytes()
);
}
#[test]
fn test_hardened_derivation() {
let seed = b"test seed for hardened derivation";
let (master_xprv, _) = derive_master_key(seed).unwrap();
let hardened_index = 0x80000000;
let (hardened_xprv, _) = master_xprv.derive_child(hardened_index).unwrap();
assert_eq!(hardened_xprv.child_number, hardened_index);
assert!(hardened_xprv.child_number >= 0x80000000);
}
}