Compare commits

...

2 Commits

9 changed files with 96 additions and 93 deletions

View File

@ -1,26 +0,0 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Index is too large, must be less than 0x80000000: {0}")]
IndexTooLarge(u32),
#[error("Unable to parse integer for index")]
IntParseError(#[from] std::num::ParseIntError),
#[error("Unable to parse path due to bad path prefix")]
UnknownPathPrefix,
#[error("Seed length in bits must be divisible by 32")]
BadSeedLength(usize),
/// 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),
/// There's a 1 in 2^256 chance this will happen. If it does, I'm sorry. Pick a new mnemonic.
#[error("Seed hash generated 32 bytes of zero, pick a new seed")]
HashedSeedIsZero,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

View File

@ -7,11 +7,14 @@ use thiserror::Error;
const KEY_SIZE: usize = 256; const KEY_SIZE: usize = 256;
/// Errors associated with creating or deriving Extended Private Keys.
#[derive(Error, Clone, Debug)] #[derive(Error, Clone, Debug)]
pub enum Error { pub enum Error {
/// The seed has an unsuitable length; supported lengths are 16 bytes, 32 bytes, or 64 bytes.
#[error("Seed had an unsuitable length: {0}")] #[error("Seed had an unsuitable length: {0}")]
BadSeedLength(usize), BadSeedLength(usize),
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
#[error("Reached maximum depth for key derivation")] #[error("Reached maximum depth for key derivation")]
Depth, Depth,
@ -19,16 +22,21 @@ pub enum Error {
#[error("Invalid length for HMAC key while generating master key (report me!)")] #[error("Invalid length for HMAC key while generating master key (report me!)")]
HmacInvalidLength(#[from] hmac::digest::InvalidLength), HmacInvalidLength(#[from] hmac::digest::InvalidLength),
/// An unknown error occurred while deriving a child key.
#[error("Unknown error while deriving child key")] #[error("Unknown error while deriving child key")]
Derivation, Derivation,
} }
pub type Result<T, E = Error> = std::result::Result<T, E>; type Result<T, E = Error> = std::result::Result<T, E>;
pub type ChainCode = [u8; 32]; type ChainCode = [u8; 32];
type HmacSha512 = Hmac<Sha512>; type HmacSha512 = Hmac<Sha512>;
/// Extended private keys derived using BIP-0032.
///
/// Generic over types implementing [`PrivateKey`].
#[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.
pub private_key: K, pub private_key: K,
depth: u8, depth: u8,
pub(crate) chain_code: ChainCode, pub(crate) chain_code: ChainCode,
@ -48,7 +56,7 @@ impl<K> ExtendedPrivateKey<K>
where where
K: PrivateKey + Clone, K: PrivateKey + Clone,
{ {
/// Generate a new [`ExtendedPublicKey`] from a seed, ideally from a 12-word or 24-word /// Generate a new [`ExtendedPrivateKey`] from a seed, ideally from a 12-word or 24-word
/// mnemonic, but may take 16-byte seeds. /// mnemonic, but may take 16-byte seeds.
/// ///
/// # Panics /// # Panics
@ -83,6 +91,7 @@ where
}) })
} }
/// 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()
} }

View File

@ -6,11 +6,14 @@ use thiserror::Error;
const KEY_SIZE: usize = 256; const KEY_SIZE: usize = 256;
/// Errors associated with creating or deriving Extended Public Keys.
#[derive(Error, Clone, Debug)] #[derive(Error, Clone, Debug)]
pub enum Error { pub enum Error {
/// BIP-0032 does not support deriving public keys from hardened private keys.
#[error("Public keys may not be derived when hardened")] #[error("Public keys may not be derived when hardened")]
HardenedIndex, HardenedIndex,
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
#[error("Reached maximum depth for key derivation")] #[error("Reached maximum depth for key derivation")]
Depth, Depth,
@ -18,14 +21,18 @@ pub enum Error {
#[error("Invalid length for HMAC key while generating master key (report me!)")] #[error("Invalid length for HMAC key while generating master key (report me!)")]
HmacInvalidLength(#[from] hmac::digest::InvalidLength), HmacInvalidLength(#[from] hmac::digest::InvalidLength),
/// An unknown error occurred while deriving a child key.
#[error("Unknown error while deriving child key")] #[error("Unknown error while deriving child key")]
Derivation, Derivation,
} }
pub type Result<T, E = Error> = std::result::Result<T, E>; type Result<T, E = Error> = std::result::Result<T, E>;
pub type ChainCode = [u8; 32]; type ChainCode = [u8; 32];
type HmacSha512 = Hmac<Sha512>; type HmacSha512 = Hmac<Sha512>;
/// Extended public keys derived using BIP-0032.
///
/// Generic over types implementing [`PublicKey`].
pub struct ExtendedPublicKey<K: PublicKey> { pub struct ExtendedPublicKey<K: PublicKey> {
public_key: K, public_key: K,
depth: u8, depth: u8,
@ -36,6 +43,8 @@ impl<K> ExtendedPublicKey<K>
where where
K: PublicKey, K: PublicKey,
{ {
/*
/// Create a new [`ExtendedPublicKey`] from previously known values.
pub fn new(public_key: K, chain_code: ChainCode) -> Self { pub fn new(public_key: K, chain_code: ChainCode) -> Self {
Self { Self {
public_key, public_key,
@ -43,6 +52,7 @@ where
chain_code, chain_code,
} }
} }
*/
/// Derive a child with a given [`DerivationIndex`]. /// Derive a child with a given [`DerivationIndex`].
/// ///

View File

@ -1,6 +1,21 @@
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error;
/// Errors associated with creating a [`DerivationIndex`].
#[derive(Error, Debug)]
pub enum Error {
/// The index was too large and should be less than 2^31.
#[error("Index is too large, must be less than 0x80000000: {0}")]
IndexTooLarge(u32),
/// An integer could not be parsed from the string.
#[error("Unable to parse integer for index")]
IntParseError(#[from] std::num::ParseIntError),
}
type Result<T, E = Error> = std::result::Result<T, E>;
/// Index for a given extended private key.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DerivationIndex(pub(crate) u32); pub struct DerivationIndex(pub(crate) u32);
@ -28,6 +43,7 @@ impl DerivationIndex {
self.0.to_be_bytes() self.0.to_be_bytes()
} }
/// Whether or not the index is hardened, allowing deriving the key from a known parent key.
pub fn is_hardened(&self) -> bool { pub fn is_hardened(&self) -> bool {
self.0 & (0b1 << 31) != 0 self.0 & (0b1 << 31) != 0
} }

View File

@ -1,9 +1,9 @@
#![allow(clippy::module_name_repetitions, clippy::must_use_candidate)] #![allow(clippy::module_name_repetitions, clippy::must_use_candidate)]
pub mod error; //! BIP-0032 derivation utilities.
pub mod extended_key; pub mod extended_key;
pub mod index; pub mod index;
pub mod master_key;
pub mod path; pub mod path;
pub mod private_key; pub mod private_key;
pub mod public_key; pub mod public_key;
@ -12,7 +12,6 @@ pub mod public_key;
mod tests; mod tests;
pub use crate::{ pub use crate::{
error::{Error, Result},
extended_key::{private_key::ExtendedPrivateKey, public_key::ExtendedPublicKey}, extended_key::{private_key::ExtendedPrivateKey, public_key::ExtendedPublicKey},
index::DerivationIndex, index::DerivationIndex,
path::DerivationPath, path::DerivationPath,

View File

@ -1,54 +0,0 @@
use crate::error::{Error, Result};
use hmac::{Hmac, Mac};
use sha2::Sha512;
pub trait MasterKey<'a> {
/// Return the textual content used to derive the master key, as specified in BIP 0032 and SLIP
/// 0010. For example, a secp256k1 master key would use the textual content "Bitcoin seed", to
/// ensure compatibility with BIP 0032, despite the key being used for more functionality than
/// purely Bitcoin.
fn key() -> &'static str;
/// Some key algorithms, such as Ed25519, allow 0 as a valid private key. Those algorithhms
/// should override this method to indicate as such.
fn is_zero_valid_private_key() -> bool {
false
}
/// Return the seed used to derive the Master Key, with a size between 128 to 512 bits. Most
/// seeds should be 256 bits, the largest size available from a BIP-0039 mnemonic.
fn seed(&self) -> &'a [u8];
}
type HmacSha512 = Hmac<Sha512>;
/// Generate a Master Secret Key and Chain Code (what is this used for?).
///
/// # Errors
///
/// An error may be returned if:
/// * The `HmacSha512` key returned by `T::key()` is invalid. This should never happen.
/// * The generated master key is all zeroes. This has a cosmically small chance of happening.
pub fn generate<'a, T: MasterKey<'a>>(generator: &T) -> Result<(Vec<u8>, Vec<u8>)> {
let seed = generator.seed();
let len = seed.len();
if len * 8 % 32 != 0 {
return Err(Error::BadSeedLength(len));
}
let mut hmac = HmacSha512::new_from_slice(&T::key().bytes().collect::<Vec<_>>())?;
hmac.update(seed);
let result = hmac.finalize().into_bytes();
let left = &result[..32];
let right = &result[32..];
if left.iter().all(|n| n == &0) {
// Wow. Impressive.
// NOTE: SLIP-0010 says to retry if this happens, but uses some weird terminology to do so.
// I do not trust it. BIP-0032 says this key is "invalid", with no instructions to retry.
// This is a low enough chance I am fine with it being freak-of-nature error.
return Err(Error::HashedSeedIsZero);
}
Ok((left.to_vec(), right.to_vec()))
}

View File

@ -1,15 +1,36 @@
use crate::error::{Error, Result};
use crate::index::DerivationIndex; use crate::index::DerivationIndex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error;
/// Errors associated with creating a [`DerivationPath`].
#[derive(Error, Debug)]
pub enum Error {
/// A [`DerivationIndex`] was not able to be created.
#[error("Unable to create index: {0}")]
UnableToCreateIndex(#[from] super::index::Error),
/// The path could not be parsed due to a bad prefix. Paths must be in the format:
///
/// m [/ index [']]+
///
/// The prefix for the path must be `m`, and all indices must be integers between 0 and
/// 2^31.
#[error("Unable to parse path due to bad path prefix")]
UnknownPathPrefix,
}
type Result<T, E = Error> = std::result::Result<T, E>;
const PREFIX: &str = "m"; const PREFIX: &str = "m";
/// A fully qualified path to derive a key.
#[derive(Serialize, Deserialize, Clone, Debug, Default)] #[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct DerivationPath { pub struct DerivationPath {
pub(crate) path: Vec<DerivationIndex>, pub(crate) path: Vec<DerivationIndex>,
} }
impl DerivationPath { impl DerivationPath {
/// Returns an iterator over the [`DerivationPath`].
pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> { pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> {
self.path.iter() self.path.iter()
} }
@ -24,7 +45,10 @@ impl std::str::FromStr for DerivationPath {
return Err(Error::UnknownPathPrefix); return Err(Error::UnknownPathPrefix);
} }
Ok(Self { Ok(Self {
path: iter.map(DerivationIndex::from_str).collect::<Result<_>>()?, path: iter
.map(DerivationIndex::from_str)
.map(|maybe_err| maybe_err.map_err(From::from))
.collect::<Result<Vec<DerivationIndex>>>()?,
}) })
} }
} }

View File

@ -2,20 +2,31 @@ use crate::PublicKey;
use thiserror::Error; use thiserror::Error;
pub type PrivateKeyBytes = [u8; 32]; pub(crate) type PrivateKeyBytes = [u8; 32];
/// Functions required to use an `ExtendedPrivateKey`.
pub trait PrivateKey: Sized { pub trait PrivateKey: Sized {
/// A type implementing [`PublicKey`] associated with Self.
type PublicKey: PublicKey; type PublicKey: PublicKey;
/// The error returned by [`PrivateKey::derive_child()`].
type Err: std::error::Error; type Err: std::error::Error;
/// Create a Self from bytes.
fn from_bytes(b: &PrivateKeyBytes) -> Self; fn from_bytes(b: &PrivateKeyBytes) -> Self;
/// Convert a &Self to bytes.
fn to_bytes(&self) -> PrivateKeyBytes; fn to_bytes(&self) -> PrivateKeyBytes;
/// Whether or not zero is a valid public key (such as with ed25519 keys).
fn is_zero_valid_public_key() -> bool { fn is_zero_valid_public_key() -> bool {
false false
} }
/// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed".
fn key() -> &'static str; fn key() -> &'static str;
/// Generate a [`Self::PublicKey`].
fn public_key(&self) -> Self::PublicKey; fn public_key(&self) -> Self::PublicKey;
/// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`. /// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`.
@ -28,11 +39,15 @@ pub trait PrivateKey: Sized {
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err>; fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err>;
} }
/// Errors associated with creating and arithmetic on private keys. This specific error is only
/// intended to be used by the implementations in this crate.
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub enum PrivateKeyError { pub enum PrivateKeyError {
/// For the given algorithm, the private key must be nonzero.
#[error("The provided private key must be nonzero, but is not")] #[error("The provided private key must be nonzero, but is not")]
NonZero, NonZero,
/// Unable to convert a point to a key.
#[error("Unable to convert point to key")] #[error("Unable to convert point to key")]
PointToKey(#[from] k256::elliptic_curve::Error), PointToKey(#[from] k256::elliptic_curve::Error),
} }

View File

@ -5,12 +5,17 @@ use ripemd::Ripemd160;
use sha2::Sha256; use sha2::Sha256;
use thiserror::Error; use thiserror::Error;
pub type PublicKeyBytes = [u8; 33]; pub(crate) type PublicKeyBytes = [u8; 33];
/// Functions required to use an `ExtendedPublicKey`.
pub trait PublicKey: Sized { pub trait PublicKey: Sized {
/// The error returned by [`PublicKey::derive_child()`].
type Err: std::error::Error; type Err: std::error::Error;
/// Create a Self from bytes.
fn from_bytes(b: &PublicKeyBytes) -> Self; fn from_bytes(b: &PublicKeyBytes) -> Self;
/// Convert a &Self to bytse.
fn to_bytes(&self) -> PublicKeyBytes; fn to_bytes(&self) -> PublicKeyBytes;
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`. /// Derive a child [`PublicKey`] with given `PrivateKeyBytes`.
@ -22,6 +27,7 @@ pub trait PublicKey: Sized {
/// * An error specific to the given algorithm was encountered. /// * An error specific to the given algorithm was encountered.
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>; fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
/// Create a BIP-0032/SLIP-0010 fingerprint from the public key.
fn fingerprint(&self) -> [u8; 4] { fn fingerprint(&self) -> [u8; 4] {
let hash = Sha256::new().chain_update(self.to_bytes()).finalize(); let hash = Sha256::new().chain_update(self.to_bytes()).finalize();
let hash = Ripemd160::new().chain_update(hash).finalize(); let hash = Ripemd160::new().chain_update(hash).finalize();
@ -30,11 +36,15 @@ pub trait PublicKey: Sized {
} }
} }
/// Errors associated with creating and arithmetic on public keys. This specific error is only
/// intended to be used by the implementations in this crate.
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub enum PublicKeyError { pub enum PublicKeyError {
/// For the given algorithm, the private key must be nonzero.
#[error("The provided public key must be nonzero, but is not")] #[error("The provided public key must be nonzero, but is not")]
NonZero, NonZero,
/// Unable to convert a point to a key.
#[error("Unable to convert point to key")] #[error("Unable to convert point to key")]
PointToKey(#[from] k256::elliptic_curve::Error), PointToKey(#[from] k256::elliptic_curve::Error),
} }