keyfork-derive-util: fixup request API, publicity of ExtendedPrivateKey

This commit is contained in:
Ryan Heywood 2023-09-11 19:44:22 -05:00
parent 8510e382d2
commit 72666011a4
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 73 additions and 17 deletions

View File

@ -29,6 +29,10 @@ pub enum Error {
/// The algorithm used mandates hardened derivation only. /// The algorithm used mandates hardened derivation only.
#[error("The algorithm used mandates hardened derivation only")] #[error("The algorithm used mandates hardened derivation only")]
HardenedDerivationRequired, 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<T, E = Error> = std::result::Result<T, E>; type Result<T, E = Error> = std::result::Result<T, E>;
@ -41,9 +45,9 @@ type HmacSha512 = Hmac<Sha512>;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> { pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
/// The internal private key data. /// The internal private key data.
pub private_key: K, private_key: K,
depth: u8, depth: u8,
pub(crate) chain_code: ChainCode, chain_code: ChainCode,
} }
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> { impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
@ -88,18 +92,42 @@ where
.into_bytes(); .into_bytes();
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8); 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<Self> {
Ok(Self { Ok(Self {
private_key: K::from_bytes(private_key.try_into().expect("Invalid key length")), private_key: K::from_bytes(seed.try_into()?),
depth: 0, depth,
chain_code: chain_code.try_into().expect("Invalid chain code length"), 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`]. /// Return a public key for the current [`PrivateKey`].
pub fn public_key(&self) -> K::PublicKey { pub fn public_key(&self) -> K::PublicKey {
self.private_key.public_key() 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`]. /// Derive a child using the given [`DerivationPath`].
/// ///
/// # Errors /// # Errors

View File

@ -34,6 +34,14 @@ impl DerivationPath {
pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> { pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> {
self.path.iter() 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 { impl std::str::FromStr for DerivationPath {

View File

@ -28,19 +28,19 @@ impl DerivationAlgorithm {
Self::Ed25519 => { Self::Ed25519 => {
let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed)?; let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed)?;
let derived_key = key.derive_path(path)?; let derived_key = key.derive_path(path)?;
Ok(DerivationResponse { Ok(DerivationResponse::with_algo_and_xprv(
algorithm: self.clone(), self.clone(),
data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(), &derived_key,
}) ))
} }
#[cfg(feature = "secp256k1")] #[cfg(feature = "secp256k1")]
Self::Secp256k1 => { Self::Secp256k1 => {
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?; let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
let derived_key = key.derive_path(path)?; let derived_key = key.derive_path(path)?;
Ok(DerivationResponse { Ok(DerivationResponse::with_algo_and_xprv(
algorithm: self.clone(), self.clone(),
data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(), &derived_key,
}) ))
} }
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
_ => Err(DerivationError::Algorithm), _ => Err(DerivationError::Algorithm),
@ -59,6 +59,10 @@ impl DerivationRequest {
Self { algorithm, path } Self { algorithm, path }
} }
pub fn path(&self) -> &DerivationPath {
&self.path
}
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> { pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
self.derive_with_master_seed(mnemonic.seed()) self.derive_with_master_seed(mnemonic.seed())
} }
@ -72,4 +76,20 @@ impl DerivationRequest {
pub struct DerivationResponse { pub struct DerivationResponse {
pub algorithm: DerivationAlgorithm, pub algorithm: DerivationAlgorithm,
pub data: Vec<u8>, pub data: Vec<u8>,
pub chain_code: [u8; 32],
pub depth: u8,
}
impl DerivationResponse {
pub fn with_algo_and_xprv<T: PrivateKey + Clone>(
algorithm: DerivationAlgorithm,
xprv: &ExtendedPrivateKey<T>,
) -> Self {
Self {
algorithm,
data: PrivateKey::to_bytes(xprv.private_key()).to_vec(),
chain_code: xprv.chain_code(),
depth: xprv.depth(),
}
}
} }

View File

@ -62,8 +62,8 @@ fn secp256k1() {
for (seed, chain, chain_code, private_key, public_key) in tests { for (seed, chain, chain_code, private_key, public_key) in tests {
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap(); let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
let derived_key = xkey.derive_path(&chain).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(derived_key.chain_code, chain_code); assert_eq!(derived_key.chain_code(), chain_code);
assert_eq!(derived_key.private_key.to_bytes().as_slice(), private_key); assert_eq!(derived_key.private_key().to_bytes().as_slice(), private_key);
assert_eq!(derived_key.public_key().to_bytes(), public_key); assert_eq!(derived_key.public_key().to_bytes(), public_key);
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, chain); let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, chain);
let response = request.derive_with_master_seed(seed.to_vec()).unwrap(); 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 { for (seed, chain, chain_code, private_key, public_key) in tests {
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap(); let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let derived_key = xkey.derive_path(&chain).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(derived_key.chain_code, chain_code); assert_eq!(derived_key.chain_code(), chain_code);
assert_eq!(PrivateKey::to_bytes(&derived_key.private_key), private_key); assert_eq!(PrivateKey::to_bytes(derived_key.private_key()), private_key);
assert_eq!(PublicKey::to_bytes(&derived_key.public_key()), public_key); assert_eq!(PublicKey::to_bytes(&derived_key.public_key()), public_key);
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, chain); let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, chain);
let response = request.derive_with_master_seed(seed.to_vec()).unwrap(); let response = request.derive_with_master_seed(seed.to_vec()).unwrap();