diff --git a/crates/derive/keyfork-derive-util/src/extended_key/private_key.rs b/crates/derive/keyfork-derive-util/src/extended_key/private_key.rs index 6e74a94..1bbf3ca 100644 --- a/crates/derive/keyfork-derive-util/src/extended_key/private_key.rs +++ b/crates/derive/keyfork-derive-util/src/extended_key/private_key.rs @@ -68,14 +68,27 @@ where /// mnemonic, but may take 16-byte seeds. /// /// # Panics - /// /// The method performs unchecked `try_into()` operations on a constant-sized slice. /// /// # Errors - /// /// An error may be returned if: /// * The given seed had an incorrect length. /// * 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> { + /// let seed: &[u8; 64] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let xprv = ExtendedPrivateKey::::new(seed)?; + /// # Ok(()) + /// # } + /// ``` pub fn new(seed: impl AsRef<[u8]>) -> Result { Self::new_internal(seed.as_ref()) } @@ -104,6 +117,23 @@ where /// /// # Errors /// 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> { + /// let key: &[u8; 32] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let chain_code: &[u8; 32] = // + /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// let xprv = ExtendedPrivateKey::::new_from_parts(key, 4, *chain_code)?; + /// # Ok(()) + /// # } + /// ``` pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result { Ok(Self { private_key: K::from_bytes(seed.try_into()?), @@ -113,26 +143,123 @@ where } /// 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> { + /// let key: &[u8; 32] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let chain_code: &[u8; 32] = // + /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// let xprv = ExtendedPrivateKey::::new_from_parts(key, 4, *chain_code)?; + /// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key)); + /// # Ok(()) + /// # } + /// ``` pub fn private_key(&self) -> &K { &self.private_key } /// 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> { + /// 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::::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 { - 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`]. + /// + /// # 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> { + /// let seed: &[u8; 64] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let xprv = ExtendedPrivateKey::::new(seed)?; + /// let pubkey = xprv.public_key(); + /// # Ok(()) + /// # } + /// ``` pub fn public_key(&self) -> K::PublicKey { self.private_key.public_key() } /// Returns the current depth. + /// + /// # Examples + /// ```rust + /// # use keyfork_derive_util::{ + /// # *, + /// # public_key::TestPublicKey as PublicKey, + /// # private_key::TestPrivateKey as PrivateKey, + /// # }; + /// # fn main() -> Result<(), Box> { + /// let key: &[u8; 32] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let chain_code: &[u8; 32] = // + /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// let xprv = ExtendedPrivateKey::::new_from_parts(key, 4, *chain_code)?; + /// assert_eq!(xprv.depth(), 4); + /// # Ok(()) + /// # } + /// ``` pub fn depth(&self) -> u8 { self.depth } /// 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> { + /// let key: &[u8; 32] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let chain_code: &[u8; 32] = // + /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// let xprv = ExtendedPrivateKey::::new_from_parts(key, 4, *chain_code)?; + /// assert_eq!(chain_code, &xprv.chain_code()); + /// # Ok(()) + /// # } + /// ``` pub fn chain_code(&self) -> [u8; 32] { self.chain_code } @@ -140,9 +267,29 @@ where /// Derive a child using the given [`DerivationPath`]. /// /// # Errors - /// /// An error may be returned under the same circumstances as /// [`ExtendedPrivateKey::derive_child`]. + /// + /// # Examples + /// ```rust + /// # use keyfork_derive_util::{ + /// # *, + /// # public_key::TestPublicKey as PublicKey, + /// # private_key::TestPrivateKey as PrivateKey, + /// # }; + /// # fn main() -> Result<(), Box> { + /// let seed: &[u8; 64] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let root_xprv = ExtendedPrivateKey::::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 { if path.path.is_empty() { Ok(self.clone()) @@ -165,6 +312,33 @@ where /// * 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 [`PrivateKey`]. + /// + /// # Examples + /// ```rust + /// # use keyfork_derive_util::{ + /// # *, + /// # public_key::TestPublicKey as PublicKey, + /// # private_key::TestPrivateKey as PrivateKey, + /// # }; + /// # fn check_empty(p: &ExtendedPrivateKey) -> Result<(), std::io::Error> { + /// # Ok(()) + /// # } + /// # fn main() -> Result<(), Box> { + /// let seed: &[u8; 64] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let root_xprv = ExtendedPrivateKey::::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 { let depth = self.depth.checked_add(1).ok_or(Error::Depth)?; diff --git a/crates/derive/keyfork-derive-util/src/extended_key/public_key.rs b/crates/derive/keyfork-derive-util/src/extended_key/public_key.rs index 8bb7287..e280820 100644 --- a/crates/derive/keyfork-derive-util/src/extended_key/public_key.rs +++ b/crates/derive/keyfork-derive-util/src/extended_key/public_key.rs @@ -44,15 +44,51 @@ where K: PublicKey, { /// 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> { + /// let key: &[u8; 33] = // + /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// let chain_code: &[u8; 32] = // + /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// let pubkey = PublicKey::from_bytes(key); + /// let xpub = ExtendedPublicKey::::new_from_parts(pubkey, 0, *chain_code); + /// # Ok(()) + /// # } + /// ``` + pub fn new_from_parts(public_key: K, depth: u8, chain_code: ChainCode) -> Self { Self { public_key, - depth: 0, + depth, chain_code, } } /// Return the internal [`PublicKey`]. + /// + /// # Examples + /// ```rust + /// # use keyfork_derive_util::{ + /// # *, + /// # public_key::PublicKey as _, + /// # public_key::TestPublicKey as PublicKey, + /// # }; + /// # fn main() -> Result<(), Box> { + /// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// # let pubkey = PublicKey::from_bytes(key); + /// let xpub = // + /// # ExtendedPublicKey::::new_from_parts(pubkey, 0, *chain_code); + /// let pubkey = xpub.public_key(); + /// # Ok(()) + /// # } + /// ``` pub fn public_key(&self) -> &K { &self.public_key } @@ -70,6 +106,25 @@ where /// * 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`]. + /// + /// # Examples + /// ```rust + /// # use keyfork_derive_util::{ + /// # *, + /// # public_key::PublicKey as _, + /// # public_key::TestPublicKey as PublicKey, + /// # }; + /// # fn main() -> Result<(), Box> { + /// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + /// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; + /// # let pubkey = PublicKey::from_bytes(key); + /// let xpub = // + /// # ExtendedPublicKey::::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 { if index.is_hardened() { return Err(Error::HardenedIndex); diff --git a/crates/derive/keyfork-derive-util/src/index.rs b/crates/derive/keyfork-derive-util/src/index.rs index 3c0da37..0d5296b 100644 --- a/crates/derive/keyfork-derive-util/src/index.rs +++ b/crates/derive/keyfork-derive-util/src/index.rs @@ -23,8 +23,20 @@ impl DerivationIndex { /// Creates a new [`DerivationIndex`]. /// /// # Errors - /// /// 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 { if index & (0b1 << 31) > 0 { 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 /// 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 { self.0 } @@ -54,7 +73,15 @@ impl DerivationIndex { 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 { self.0 & (0b1 << 31) != 0 } diff --git a/crates/derive/keyfork-derive-util/src/path.rs b/crates/derive/keyfork-derive-util/src/path.rs index c32bfdb..ce5df57 100644 --- a/crates/derive/keyfork-derive-util/src/path.rs +++ b/crates/derive/keyfork-derive-util/src/path.rs @@ -52,6 +52,24 @@ impl DerivationPath { } /// 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 { Ok(true) } + /// # fn main() -> Result<(), Box> { + /// 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 { self.path.push(index); self diff --git a/crates/derive/keyfork-derive-util/src/private_key.rs b/crates/derive/keyfork-derive-util/src/private_key.rs index d0dc3f9..8919400 100644 --- a/crates/derive/keyfork-derive-util/src/private_key.rs +++ b/crates/derive/keyfork-derive-util/src/private_key.rs @@ -13,9 +13,32 @@ pub trait PrivateKey: Sized { type Err: std::error::Error; /// 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; /// 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; /* @@ -27,12 +50,35 @@ pub trait PrivateKey: Sized { */ /// 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; /// 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; - /// 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 /// @@ -129,3 +175,48 @@ impl PrivateKey for ed25519_dalek::SigningKey { 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 { + Ok(Self { key: *other }) + } +} diff --git a/crates/derive/keyfork-derive-util/src/public_key.rs b/crates/derive/keyfork-derive-util/src/public_key.rs index b91d3e1..eb84999 100644 --- a/crates/derive/keyfork-derive-util/src/public_key.rs +++ b/crates/derive/keyfork-derive-util/src/public_key.rs @@ -19,9 +19,23 @@ pub trait PublicKey: Sized { */ /// 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; - /// 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 /// @@ -31,6 +45,18 @@ pub trait PublicKey: Sized { fn derive_child(&self, other: PrivateKeyBytes) -> Result; /// 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] { let hash = Sha256::new().chain_update(self.to_bytes()).finalize(); let hash = Ripemd160::new().chain_update(hash).finalize(); @@ -112,3 +138,32 @@ impl PublicKey for VerifyingKey { 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 { + // whatever it takes for tests to pass... + Ok(self.clone()) + } +} diff --git a/crates/derive/keyfork-derive-util/src/request.rs b/crates/derive/keyfork-derive-util/src/request.rs index 7336c7a..231fba5 100644 --- a/crates/derive/keyfork-derive-util/src/request.rs +++ b/crates/derive/keyfork-derive-util/src/request.rs @@ -2,7 +2,9 @@ #![allow(clippy::match_wildcard_for_single_variants)] 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 serde::{Deserialize, Serialize}; @@ -30,11 +32,14 @@ pub type Result = std::result::Result; /// The algorithm to derive a key for. The choice of algorithm will result in a different resulting /// derivation. #[derive(Serialize, Deserialize, Clone, Debug)] +#[non_exhaustive] pub enum DerivationAlgorithm { #[allow(missing_docs)] Ed25519, #[allow(missing_docs)] Secp256k1, + #[doc(hidden)] + Internal, } impl DerivationAlgorithm { @@ -42,7 +47,7 @@ impl DerivationAlgorithm { /// /// # Errors /// The method may error if the derivation fails or if the algorithm is not supported. - pub fn derive(&self, seed: Vec, path: &DerivationPath) -> Result { + fn derive(&self, seed: Vec, path: &DerivationPath) -> Result { match self { #[cfg(feature = "ed25519")] Self::Ed25519 => { @@ -62,6 +67,14 @@ impl DerivationAlgorithm { &derived_key, )) } + Self::Internal => { + let key = ExtendedPrivateKey::::new(seed)?; + let derived_key = key.derive_path(path)?; + Ok(DerivationResponse::with_algo_and_xprv( + self.clone(), + &derived_key, + )) + } #[allow(unreachable_patterns)] _ => Err(DerivationError::Algorithm), } @@ -89,6 +102,23 @@ pub struct DerivationRequest { impl DerivationRequest { /// 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> { + /// 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 { Self { algorithm, @@ -97,6 +127,24 @@ impl DerivationRequest { } /// 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> { + /// 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 { &self.path } @@ -105,6 +153,29 @@ impl DerivationRequest { /// /// # Errors /// 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> { + /// 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 { // TODO: passphrase support and/or store passphrase within mnemonic self.derive_with_master_seed(mnemonic.seed(None)?) @@ -114,6 +185,26 @@ impl DerivationRequest { /// /// # Errors /// 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> { + /// 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) -> Result { self.algorithm.derive(seed, &self.path) } @@ -137,7 +228,7 @@ pub struct DerivationResponse { impl DerivationResponse { /// Create a [`DerivationResponse`] with the given values. - pub fn with_algo_and_xprv( + fn with_algo_and_xprv( algorithm: DerivationAlgorithm, xprv: &ExtendedPrivateKey, ) -> Self {