keyfork/keyfork-derive-util/src/extended_key/public_key.rs

98 lines
2.9 KiB
Rust

use crate::{DerivationIndex, PublicKey};
use hmac::{Hmac, Mac};
use sha2::Sha512;
use thiserror::Error;
const KEY_SIZE: usize = 256;
/// Errors associated with creating or deriving Extended Public Keys.
#[derive(Error, Clone, Debug)]
pub enum Error {
/// BIP-0032 does not support deriving public keys from hardened private keys.
#[error("Public keys may not be derived when hardened")]
HardenedIndex,
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
#[error("Reached maximum depth for key derivation")]
Depth,
/// This should never happen. HMAC keys should be able to take any size input.
#[error("Invalid length for HMAC key while generating master key (report me!)")]
HmacInvalidLength(#[from] hmac::digest::InvalidLength),
/// An unknown error occurred while deriving a child key.
#[error("Unknown error while deriving child key")]
Derivation,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub type ChainCode = [u8; 32];
type HmacSha512 = Hmac<Sha512>;
/// Extended public keys derived using BIP-0032.
///
/// Generic over types implementing [`PublicKey`].
pub struct ExtendedPublicKey<K: PublicKey> {
public_key: K,
depth: u8,
chain_code: ChainCode,
}
impl<K> ExtendedPublicKey<K>
where
K: PublicKey,
{
/*
/// Create a new [`ExtendedPublicKey`] from previously known values.
pub fn new(public_key: K, chain_code: ChainCode) -> Self {
Self {
public_key,
depth: 0,
chain_code,
}
}
*/
/// Derive a child with a given [`DerivationIndex`].
///
/// # Panics
///
/// The method performs unchecked `try_into()` operations on a constant-sized slice.
///
/// # Errors
///
/// An error may be returned if:
///
/// * The depth exceeds the maximum depth [`u8::MAX`].
/// * A `HmacSha512` can't be constructed - this should be impossible.
/// * Deriving a child key fails. Check the documentation for your [`PublicKey`].
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
if index.is_hardened() {
return Err(Error::HardenedIndex);
}
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
let hmac = HmacSha512::new_from_slice(&self.chain_code)
.map_err(Error::from)?
.chain_update(self.public_key.to_bytes())
.chain_update(index.to_bytes())
.finalize()
.into_bytes();
let (child_key, chain_code) = hmac.split_at(KEY_SIZE / 8);
let derived_key = self
.public_key
.derive_child(child_key.try_into().expect("Invalid key length"))
.map_err(|_| Error::Derivation)?;
let chain_code = chain_code.try_into().expect("Invalid chain code length");
Ok(Self {
public_key: derived_key,
depth,
chain_code,
})
}
}