keyfork-derive-util: add doctests/examples

This commit is contained in:
Ryan Heywood 2024-02-10 03:50:55 -05:00
parent f2250d00e1
commit 5096df993e
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
7 changed files with 524 additions and 13 deletions

View File

@ -68,14 +68,27 @@ where
/// mnemonic, but may take 16-byte seeds. /// mnemonic, but may take 16-byte seeds.
/// ///
/// # Panics /// # Panics
///
/// The method performs unchecked `try_into()` operations on a constant-sized slice. /// The method performs unchecked `try_into()` operations on a constant-sized slice.
/// ///
/// # Errors /// # Errors
///
/// An error may be returned if: /// An error may be returned if:
/// * The given seed had an incorrect length. /// * The given seed had an incorrect length.
/// * A `HmacSha512` can't be constructed - this should be impossible. /// * A `HmacSha512` can't be constructed - this should be impossible.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// # Ok(())
/// # }
/// ```
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> { pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
Self::new_internal(seed.as_ref()) Self::new_internal(seed.as_ref())
} }
@ -104,6 +117,23 @@ where
/// ///
/// # Errors /// # Errors
/// The function may error if a private key can't be created from the seed. /// The function may error if a private key can't be created from the seed.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// # Ok(())
/// # }
/// ```
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> { pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
Ok(Self { Ok(Self {
private_key: K::from_bytes(seed.try_into()?), private_key: K::from_bytes(seed.try_into()?),
@ -113,26 +143,123 @@ where
} }
/// Returns a reference to the [`PrivateKey`]. /// Returns a reference to the [`PrivateKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::PrivateKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
/// # Ok(())
/// # }
/// ```
pub fn private_key(&self) -> &K { pub fn private_key(&self) -> &K {
&self.private_key &self.private_key
} }
/// Create an [`ExtendedPublicKey`] for the current [`PrivateKey`]. /// Create an [`ExtendedPublicKey`] for the current [`PrivateKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::PrivateKey as _,
/// # public_key::PublicKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// # let known_key: [u8; 33] = [
/// # 0, 242, 26, 9, 159, 68, 199, 0, 206, 71, 248,
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251
/// # ];
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let xpub = xprv.extended_public_key();
/// assert_eq!(known_key, xpub.public_key().to_bytes());
/// # Ok(())
/// # }
/// ```
pub fn extended_public_key(&self) -> ExtendedPublicKey<K::PublicKey> { pub fn extended_public_key(&self) -> ExtendedPublicKey<K::PublicKey> {
ExtendedPublicKey::new(self.public_key(), self.chain_code) ExtendedPublicKey::new_from_parts(self.public_key(), self.depth, self.chain_code)
} }
/// Return a public key for the current [`PrivateKey`]. /// Return a public key for the current [`PrivateKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::PrivateKey as _,
/// # public_key::PublicKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let pubkey = xprv.public_key();
/// # Ok(())
/// # }
/// ```
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. /// Returns the current depth.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// assert_eq!(xprv.depth(), 4);
/// # Ok(())
/// # }
/// ```
pub fn depth(&self) -> u8 { pub fn depth(&self) -> u8 {
self.depth self.depth
} }
/// Returns a copy of the current chain code. /// Returns a copy of the current chain code.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// assert_eq!(chain_code, &xprv.chain_code());
/// # Ok(())
/// # }
/// ```
pub fn chain_code(&self) -> [u8; 32] { pub fn chain_code(&self) -> [u8; 32] {
self.chain_code self.chain_code
} }
@ -140,9 +267,29 @@ where
/// Derive a child using the given [`DerivationPath`]. /// Derive a child using the given [`DerivationPath`].
/// ///
/// # Errors /// # Errors
///
/// An error may be returned under the same circumstances as /// An error may be returned under the same circumstances as
/// [`ExtendedPrivateKey::derive_child`]. /// [`ExtendedPrivateKey::derive_child`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let path = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
/// .chain_push(DerivationIndex::new(0, false)?);
/// let derived_xprv = root_xprv.derive_path(&path)?;
/// # Ok(())
/// # }
/// ```
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> { pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> {
if path.path.is_empty() { if path.path.is_empty() {
Ok(self.clone()) Ok(self.clone())
@ -165,6 +312,33 @@ where
/// * The depth exceeds the maximum depth [`u8::MAX`]. /// * The depth exceeds the maximum depth [`u8::MAX`].
/// * A `HmacSha512` can't be constructed - this should be impossible. /// * A `HmacSha512` can't be constructed - this should be impossible.
/// * Deriving a child key fails. Check the documentation for your [`PrivateKey`]. /// * Deriving a child key fails. Check the documentation for your [`PrivateKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn check_empty(p: &ExtendedPrivateKey<PrivateKey>) -> Result<(), std::io::Error> {
/// # Ok(())
/// # }
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let bip44_wallet = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
/// .chain_push(DerivationIndex::new(0, false)?);
/// let change_xprv = root_xprv.derive_path(&bip44_wallet)?;
/// for account in (0..20).map(|i| DerivationIndex::new(i, false).unwrap()) {
/// let account_xprv = change_xprv.derive_child(&account)?;
/// check_empty(&account_xprv)?;
/// }
/// # Ok(())
/// # }
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> { pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?; let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;

View File

@ -44,15 +44,51 @@ where
K: PublicKey, K: PublicKey,
{ {
/// Create a new [`ExtendedPublicKey`] from previously known values. /// Create a new [`ExtendedPublicKey`] from previously known values.
pub fn new(public_key: K, chain_code: ChainCode) -> Self { ///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::PublicKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 33] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let pubkey = PublicKey::from_bytes(key);
/// let xpub = ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
/// # Ok(())
/// # }
/// ```
pub fn new_from_parts(public_key: K, depth: u8, chain_code: ChainCode) -> Self {
Self { Self {
public_key, public_key,
depth: 0, depth,
chain_code, chain_code,
} }
} }
/// Return the internal [`PublicKey`]. /// Return the internal [`PublicKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::PublicKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// # let pubkey = PublicKey::from_bytes(key);
/// let xpub = //
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
/// let pubkey = xpub.public_key();
/// # Ok(())
/// # }
/// ```
pub fn public_key(&self) -> &K { pub fn public_key(&self) -> &K {
&self.public_key &self.public_key
} }
@ -70,6 +106,25 @@ where
/// * The depth exceeds the maximum depth [`u8::MAX`]. /// * The depth exceeds the maximum depth [`u8::MAX`].
/// * A `HmacSha512` can't be constructed - this should be impossible. /// * A `HmacSha512` can't be constructed - this should be impossible.
/// * Deriving a child key fails. Check the documentation for your [`PublicKey`]. /// * Deriving a child key fails. Check the documentation for your [`PublicKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # public_key::PublicKey as _,
/// # public_key::TestPublicKey as PublicKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// # let pubkey = PublicKey::from_bytes(key);
/// let xpub = //
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
/// let index = DerivationIndex::new(0, false)?;
/// let child = xpub.derive_child(&index)?;
/// # Ok(())
/// # }
/// ```
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> { pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
if index.is_hardened() { if index.is_hardened() {
return Err(Error::HardenedIndex); return Err(Error::HardenedIndex);

View File

@ -23,8 +23,20 @@ impl DerivationIndex {
/// Creates a new [`DerivationIndex`]. /// Creates a new [`DerivationIndex`].
/// ///
/// # Errors /// # Errors
///
/// Returns an error if the index is larger than the hardened flag. /// Returns an error if the index is larger than the hardened flag.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::*;
/// let bip44 = DerivationIndex::new(44, true);
/// ```
///
/// Using a derivation index that is higher than 2^31 returns an error:
///
/// ```rust,should_panic
/// # use keyfork_derive_util::*;
/// let too_high = DerivationIndex::new(u32::MAX, true).unwrap();
/// ```
pub const fn new(index: u32, hardened: bool) -> Result<Self> { pub const fn new(index: u32, hardened: bool) -> Result<Self> {
if index & (0b1 << 31) > 0 { if index & (0b1 << 31) > 0 {
return Err(Error::IndexTooLarge(index)); return Err(Error::IndexTooLarge(index));
@ -46,6 +58,13 @@ impl DerivationIndex {
/// Return the internal derivation index. Note that if the derivation index is hardened, the /// Return the internal derivation index. Note that if the derivation index is hardened, the
/// highest bit will be set, and the value can't be used to create a new derivation index. /// highest bit will be set, and the value can't be used to create a new derivation index.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::*;
/// assert_eq!(DerivationIndex::new(44, true).unwrap().inner(), 2147483692);
/// assert_eq!(DerivationIndex::new(200, false).unwrap().inner(), 200);
/// ```
pub fn inner(&self) -> u32 { pub fn inner(&self) -> u32 {
self.0 self.0
} }
@ -54,7 +73,15 @@ 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. /// Whether or not the index is hardened, allowing deriving the key from a known parent public
/// key.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::*;
/// assert_eq!(DerivationIndex::new(0, true).unwrap().is_hardened(), true);
/// assert_eq!(DerivationIndex::new(0, false).unwrap().is_hardened(), false);
/// ```
pub fn is_hardened(&self) -> bool { pub fn is_hardened(&self) -> bool {
self.0 & (0b1 << 31) != 0 self.0 & (0b1 << 31) != 0
} }

View File

@ -52,6 +52,24 @@ impl DerivationPath {
} }
/// Append an index to the path, returning self to allow chaining method calls. /// Append an index to the path, returning self to allow chaining method calls.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::*;
/// # fn discover_wallet(_p: DerivationPath) -> Result<bool, std::io::Error> { Ok(true) }
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let account = 0;
/// let path = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
/// .chain_push(DerivationIndex::new(account, true)?);
/// let mut has_wallet = false;
/// for index in (0..20).map(|i| DerivationIndex::new(i, true).unwrap()) {
/// has_wallet = has_wallet || discover_wallet(path.clone().chain_push(index))?;
/// }
/// # Ok(())
/// # }
/// ```
pub fn chain_push(mut self, index: DerivationIndex) -> Self { pub fn chain_push(mut self, index: DerivationIndex) -> Self {
self.path.push(index); self.path.push(index);
self self

View File

@ -13,9 +13,32 @@ pub trait PrivateKey: Sized {
type Err: std::error::Error; type Err: std::error::Error;
/// Create a Self from bytes. /// Create a Self from bytes.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data);
/// ```
fn from_bytes(b: &PrivateKeyBytes) -> Self; fn from_bytes(b: &PrivateKeyBytes) -> Self;
/// Convert a &Self to bytes. /// Convert a &Self to bytes.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data);
/// assert_eq!(key_data, &private_key.to_bytes());
/// ```
fn to_bytes(&self) -> PrivateKeyBytes; fn to_bytes(&self) -> PrivateKeyBytes;
/* /*
@ -27,12 +50,35 @@ pub trait PrivateKey: Sized {
*/ */
/// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed". /// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed".
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// assert_eq!(OurPrivateKey::key(), "testing seed");
/// ```
fn key() -> &'static str; fn key() -> &'static str;
/// Generate a [`Self::PublicKey`]. /// Generate a [`Self::PublicKey`].
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data);
/// let public_key = private_key.public_key();
/// ```
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`. The implementation of
/// derivation is algorithm-specific and a specification should be consulted when implementing
/// this method.
/// ///
/// # Errors /// # Errors
/// ///
@ -129,3 +175,48 @@ impl PrivateKey for ed25519_dalek::SigningKey {
true true
} }
} }
use crate::public_key::TestPublicKey;
#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TestPrivateKey {
key: [u8; 32],
}
impl TestPrivateKey {
pub(crate) fn public_key(&self) -> TestPublicKey {
let mut bytes = [0u8; 33];
for (i, byte) in self.key.iter().enumerate() {
bytes[i + 1] = byte ^ 0xFF;
}
TestPublicKey { key: bytes }
}
}
impl PrivateKey for TestPrivateKey {
type PublicKey = TestPublicKey;
type Err = PrivateKeyError;
fn from_bytes(b: &PrivateKeyBytes) -> Self {
Self {
key: *b
}
}
fn to_bytes(&self) -> PrivateKeyBytes {
self.key
}
fn key() -> &'static str {
"testing seed"
}
fn public_key(&self) -> Self::PublicKey {
self.public_key()
}
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err> {
Ok(Self { key: *other })
}
}

View File

@ -19,9 +19,23 @@ pub trait PublicKey: Sized {
*/ */
/// Convert a &Self to bytes. /// Convert a &Self to bytes.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data);
/// let public_key_bytes = private_key.public_key().to_bytes();
/// ```
fn to_bytes(&self) -> PublicKeyBytes; fn to_bytes(&self) -> PublicKeyBytes;
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`. /// Derive a child [`PublicKey`] with given `PrivateKeyBytes`. The implementation of
/// derivation is algorithm-specific and a specification should be consulted when implementing
/// this method.
/// ///
/// # Errors /// # Errors
/// ///
@ -31,6 +45,18 @@ pub trait PublicKey: Sized {
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. /// Create a BIP-0032/SLIP-0010 fingerprint from the public key.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # private_key::TestPrivateKey as OurPrivateKey,
/// # };
/// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data);
/// let fingerprint = private_key.public_key().fingerprint();
/// ```
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();
@ -112,3 +138,32 @@ impl PublicKey for VerifyingKey {
Err(Self::Err::DerivationUnsupported) Err(Self::Err::DerivationUnsupported)
} }
} }
#[doc(hidden)]
#[derive(Clone)]
pub struct TestPublicKey {
pub(crate) key: [u8; 33],
}
impl TestPublicKey {
#[doc(hidden)]
pub fn from_bytes(b: &[u8]) -> Self {
Self {
key: b.try_into().unwrap(),
}
}
}
impl PublicKey for TestPublicKey {
type Err = PublicKeyError;
fn to_bytes(&self) -> PublicKeyBytes {
self.key
}
fn derive_child(&self, _other: PrivateKeyBytes) -> Result<Self, Self::Err> {
// whatever it takes for tests to pass...
Ok(self.clone())
}
}

View File

@ -2,7 +2,9 @@
#![allow(clippy::match_wildcard_for_single_variants)] #![allow(clippy::match_wildcard_for_single_variants)]
use crate::{ use crate::{
extended_key::private_key::Error as XPrvError, DerivationPath, ExtendedPrivateKey, PrivateKey, extended_key::private_key::Error as XPrvError,
private_key::{PrivateKey, TestPrivateKey},
DerivationPath, ExtendedPrivateKey,
}; };
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError}; use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -30,11 +32,14 @@ pub type Result<T, E = DerivationError> = std::result::Result<T, E>;
/// The algorithm to derive a key for. The choice of algorithm will result in a different resulting /// The algorithm to derive a key for. The choice of algorithm will result in a different resulting
/// derivation. /// derivation.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[non_exhaustive]
pub enum DerivationAlgorithm { pub enum DerivationAlgorithm {
#[allow(missing_docs)] #[allow(missing_docs)]
Ed25519, Ed25519,
#[allow(missing_docs)] #[allow(missing_docs)]
Secp256k1, Secp256k1,
#[doc(hidden)]
Internal,
} }
impl DerivationAlgorithm { impl DerivationAlgorithm {
@ -42,7 +47,7 @@ impl DerivationAlgorithm {
/// ///
/// # Errors /// # Errors
/// The method may error if the derivation fails or if the algorithm is not supported. /// The method may error if the derivation fails or if the algorithm is not supported.
pub fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> { fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
match self { match self {
#[cfg(feature = "ed25519")] #[cfg(feature = "ed25519")]
Self::Ed25519 => { Self::Ed25519 => {
@ -62,6 +67,14 @@ impl DerivationAlgorithm {
&derived_key, &derived_key,
)) ))
} }
Self::Internal => {
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
&derived_key,
))
}
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
_ => Err(DerivationError::Algorithm), _ => Err(DerivationError::Algorithm),
} }
@ -89,6 +102,23 @@ pub struct DerivationRequest {
impl DerivationRequest { impl DerivationRequest {
/// Create a new derivation request. /// Create a new derivation request.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # request::*,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let algo: DerivationAlgorithm = //
/// # DerivationAlgorithm::Internal;
/// let path: DerivationPath = //
/// # DerivationPath::default();
/// let request = DerivationRequest::new(algo, &path);
/// # Ok(())
/// # }
pub fn new(algorithm: DerivationAlgorithm, path: &DerivationPath) -> Self { pub fn new(algorithm: DerivationAlgorithm, path: &DerivationPath) -> Self {
Self { Self {
algorithm, algorithm,
@ -97,6 +127,24 @@ impl DerivationRequest {
} }
/// Return the path of the derivation request. /// Return the path of the derivation request.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # request::*,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let algo: DerivationAlgorithm = //
/// # DerivationAlgorithm::Internal;
/// let path: DerivationPath = //
/// # DerivationPath::default();
/// let request = DerivationRequest::new(algo, &path);
/// assert_eq!(&path, request.path());
/// # Ok(())
/// # }
pub fn path(&self) -> &DerivationPath { pub fn path(&self) -> &DerivationPath {
&self.path &self.path
} }
@ -105,6 +153,29 @@ impl DerivationRequest {
/// ///
/// # Errors /// # Errors
/// The method may error if the derivation fails or if the algorithm is not supported. /// The method may error if the derivation fails or if the algorithm is not supported.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # request::*,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mnemonic: keyfork_mnemonic_util::Mnemonic = //
/// # keyfork_mnemonic_util::Mnemonic::from_entropy(
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
/// # Default::default(),
/// # )?;
/// let algo: DerivationAlgorithm = //
/// # DerivationAlgorithm::Internal;
/// let path: DerivationPath = //
/// # DerivationPath::default();
/// let request = DerivationRequest::new(algo, &path);
/// let response = request.derive_with_mnemonic(&mnemonic)?;
/// # Ok(())
/// # }
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> { pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
// TODO: passphrase support and/or store passphrase within mnemonic // TODO: passphrase support and/or store passphrase within mnemonic
self.derive_with_master_seed(mnemonic.seed(None)?) self.derive_with_master_seed(mnemonic.seed(None)?)
@ -114,6 +185,26 @@ impl DerivationRequest {
/// ///
/// # Errors /// # Errors
/// The method may error if the derivation fails or if the algorithm is not supported. /// The method may error if the derivation fails or if the algorithm is not supported.
///
/// # Examples
/// ```rust
/// # use keyfork_derive_util::{
/// # *,
/// # request::*,
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let algo: DerivationAlgorithm = //
/// # DerivationAlgorithm::Internal;
/// let path: DerivationPath = //
/// # DerivationPath::default();
/// let request = DerivationRequest::new(algo, &path);
/// let response = request.derive_with_master_seed(seed.to_vec())?;
/// # Ok(())
/// # }
pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> { pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> {
self.algorithm.derive(seed, &self.path) self.algorithm.derive(seed, &self.path)
} }
@ -137,7 +228,7 @@ pub struct DerivationResponse {
impl DerivationResponse { impl DerivationResponse {
/// Create a [`DerivationResponse`] with the given values. /// Create a [`DerivationResponse`] with the given values.
pub fn with_algo_and_xprv<T: PrivateKey + Clone>( fn with_algo_and_xprv<T: PrivateKey + Clone>(
algorithm: DerivationAlgorithm, algorithm: DerivationAlgorithm,
xprv: &ExtendedPrivateKey<T>, xprv: &ExtendedPrivateKey<T>,
) -> Self { ) -> Self {