hd_wallet/lib.rs
1//! 
2//! [](https://docs.rs/hd-wallet)
3//! [](https://crates.io/crates/hd-wallet)
4//! [](https://discordapp.com/channels/905194001349627914/1285268686147424388)
5//!
6//! # HD wallets derivation
7//!
8//! This crate supports the following HD derivations:
9//! * [SLIP10][slip10-spec] (compatible with [BIP32][bip32-spec]), see [`Slip10`]
10//! * Non-standard [`Edwards`] derivation for ed25519 curve
11//!
12//! To perform HD derivation, use [`HdWallet`] trait.
13//!
14//! ### Example: SLIP10 derivation
15//!
16//! Derive a master key from the seed, and then derive a child key m/1<sub>H</sub>/10:
17//! ```rust
18//! use hd_wallet::{slip10, curves::Secp256k1};
19//!
20//! let seed = b"16-64 bytes of high entropy".as_slice();
21//! let master_key = slip10::derive_master_key::<Secp256k1>(seed)?;
22//! let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
23//!
24//! let child_key_pair = slip10::derive_child_key_pair_with_path(
25//! &master_key_pair,
26//! [1 + hd_wallet::H, 10],
27//! );
28//! # Ok::<(), Box<dyn std::error::Error>>(())
29//! ```
30//!
31//! ### Example: via [HdWallet] trait
32//!
33//! [`HdWallet`] trait generalizes HD derivation algorithm, you can use it with generics:
34//! ```rust
35//! use hd_wallet::{Slip10, curves::Secp256r1};
36//!
37//! fn derive_using_generic_algo<E: generic_ec::Curve, Hd: hd_wallet::HdWallet<E>>(
38//! master_key: hd_wallet::ExtendedKeyPair<E>,
39//! ) -> hd_wallet::ExtendedKeyPair<E>
40//! {
41//! Hd::derive_child_key_pair_with_path(
42//! &master_key,
43//! [1 + hd_wallet::H, 10],
44//! )
45//! }
46//!
47//! // Use it with any HD derivation:
48//! let seed = b"16-64 bytes of high entropy".as_slice();
49//! let master_key = hd_wallet::slip10::derive_master_key(seed)?;
50//! let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
51//! let child_key = derive_using_generic_algo::<Secp256r1, Slip10>(master_key_pair);
52//!
53//! # Ok::<(), Box<dyn std::error::Error>>(())
54//! ```
55//!
56//! ### Features
57//! * `curve-secp256k1`, `curve-secp256r1`, `curve-ed25519`, `curve-stark` add curve implementation
58//! into the crate [curves] module
59//! * `all-curves` adds all curves listed above
60//! * `slip10`, `edwards`, `stark` add [`slip10`], [`edwards`], and [`stark`] HD derivations respectively
61//! * `serde` adds `serde::{Serialize, Deserialize}` traits implementation to the types in the library
62//!
63//! ## Join us in Discord!
64//! Feel free to reach out to us [in Discord](https://discordapp.com/channels/905194001349627914/1285268686147424388)!
65//!
66//! [slip10-spec]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md
67//! [bip32-spec]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
68
69#![no_std]
70#![forbid(missing_docs, unsafe_code)]
71#![cfg_attr(not(test), forbid(unused_crate_dependencies))]
72
73use core::ops;
74
75use generic_ec::{Curve, Point, Scalar, SecretScalar};
76
77pub use generic_ec::curves;
78
79#[cfg(feature = "edwards")]
80pub mod edwards;
81pub mod errors;
82#[cfg(feature = "slip10")]
83pub mod slip10;
84#[cfg(feature = "stark")]
85pub mod stark;
86
87#[cfg(feature = "edwards")]
88pub use edwards::Edwards;
89#[cfg(feature = "slip10")]
90pub use slip10::Slip10;
91#[cfg(feature = "stark")]
92pub use stark::Stark;
93
94/// Beginning of hardened child indexes
95///
96/// $H = 2^{31}$ defines the range of hardened indexes. All indexes $i$ such that $H \le i$ are hardened.
97///
98/// ## Example
99/// Derive a child key with a path m/1<sub>H</sub>
100/// ```rust
101/// use hd_wallet::HdWallet;
102///
103/// # let seed = b"do not use this seed in prod :)".as_slice();
104/// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
105/// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
106/// #
107/// let hardened_child = hd_wallet::Slip10::derive_child_key_pair(
108/// &master_key_pair,
109/// 1 + hd_wallet::H,
110/// );
111/// #
112/// # Ok::<(), hd_wallet::errors::InvalidLength>(())
113/// ```
114pub const H: u32 = 1 << 31;
115
116/// Child index, whether hardened or not
117#[derive(Clone, Copy, Debug)]
118#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
119#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(from = "u32"))]
120pub enum ChildIndex {
121 /// Hardened index
122 Hardened(HardenedIndex),
123 /// Non-hardened index
124 NonHardened(NonHardenedIndex),
125}
126
127/// Child index in range $2^{31} \le i < 2^{32}$ corresponding to a hardened wallet
128#[derive(Clone, Copy, Debug)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
130#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(try_from = "u32"))]
131pub struct HardenedIndex(u32);
132
133/// Child index in range $0 \le i < 2^{31}$ corresponding to a non-hardened wallet
134#[derive(Clone, Copy, Debug)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
136#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(try_from = "u32"))]
137pub struct NonHardenedIndex(u32);
138
139/// Extended public key
140#[derive(Clone, Copy, Debug)]
141#[cfg_attr(
142 feature = "serde",
143 derive(serde::Serialize, serde::Deserialize),
144 serde(bound = "")
145)]
146pub struct ExtendedPublicKey<E: Curve> {
147 /// The public key that can be used for signature verification
148 pub public_key: Point<E>,
149 /// A chain code that is used to derive child keys
150 pub chain_code: ChainCode,
151}
152
153/// Extended secret key
154#[derive(Clone, Debug)]
155#[cfg_attr(
156 feature = "serde",
157 derive(serde::Serialize, serde::Deserialize),
158 serde(bound = "")
159)]
160pub struct ExtendedSecretKey<E: Curve> {
161 /// The secret key that can be used for signing
162 pub secret_key: SecretScalar<E>,
163 /// A chain code that is used to derive child keys
164 pub chain_code: ChainCode,
165}
166
167/// Pair of extended secret and public keys
168#[derive(Clone, Debug)]
169pub struct ExtendedKeyPair<E: Curve> {
170 public_key: ExtendedPublicKey<E>,
171 secret_key: ExtendedSecretKey<E>,
172}
173
174/// A shift that can be applied to parent key to obtain a child key
175///
176/// It contains an already derived child public key as it needs to be derived
177/// in process of calculating the shift value
178#[derive(Clone, Copy, Debug)]
179#[cfg_attr(
180 feature = "serde",
181 derive(serde::Serialize, serde::Deserialize),
182 serde(bound = "")
183)]
184pub struct DerivedShift<E: Curve> {
185 /// Derived shift
186 pub shift: Scalar<E>,
187 /// Derived child extended public key
188 pub child_public_key: ExtendedPublicKey<E>,
189}
190
191/// Chain code of extended key as defined in SLIP-10
192pub type ChainCode = [u8; 32];
193
194impl HardenedIndex {
195 /// The smallest possible value of hardened index. Equals to $2^{31}$
196 pub const MIN: Self = Self(H);
197 /// The largest possible value of hardened index. Equals to $2^{32} - 1$
198 pub const MAX: Self = Self(u32::MAX);
199}
200impl NonHardenedIndex {
201 /// The smallest possible value of non-hardened index. Equals to $0$
202 pub const MIN: Self = Self(0);
203 /// The largest possible value of non-hardened index. Equals to $2^{31} - 1$
204 pub const MAX: Self = Self(H - 1);
205}
206impl ops::Deref for HardenedIndex {
207 type Target = u32;
208 fn deref(&self) -> &Self::Target {
209 &self.0
210 }
211}
212impl ops::Deref for NonHardenedIndex {
213 type Target = u32;
214 fn deref(&self) -> &Self::Target {
215 &self.0
216 }
217}
218impl ops::Deref for ChildIndex {
219 type Target = u32;
220 fn deref(&self) -> &Self::Target {
221 match self {
222 Self::Hardened(i) => i,
223 Self::NonHardened(i) => i,
224 }
225 }
226}
227impl From<u32> for ChildIndex {
228 fn from(value: u32) -> Self {
229 match value {
230 H.. => Self::Hardened(HardenedIndex(value)),
231 _ => Self::NonHardened(NonHardenedIndex(value)),
232 }
233 }
234}
235impl TryFrom<u32> for HardenedIndex {
236 type Error = errors::OutOfRange;
237 fn try_from(value: u32) -> Result<Self, Self::Error> {
238 match ChildIndex::from(value) {
239 ChildIndex::Hardened(v) => Ok(v),
240 _ => Err(errors::OutOfRange),
241 }
242 }
243}
244impl TryFrom<u32> for NonHardenedIndex {
245 type Error = errors::OutOfRange;
246 fn try_from(value: u32) -> Result<Self, Self::Error> {
247 match ChildIndex::from(value) {
248 ChildIndex::NonHardened(v) => Ok(v),
249 _ => Err(errors::OutOfRange),
250 }
251 }
252}
253impl From<ChildIndex> for u32 {
254 fn from(value: ChildIndex) -> Self {
255 match value {
256 ChildIndex::Hardened(v) => v.0,
257 ChildIndex::NonHardened(v) => v.0,
258 }
259 }
260}
261impl From<HardenedIndex> for u32 {
262 fn from(value: HardenedIndex) -> Self {
263 value.0
264 }
265}
266impl From<NonHardenedIndex> for u32 {
267 fn from(value: NonHardenedIndex) -> Self {
268 value.0
269 }
270}
271impl core::str::FromStr for ChildIndex {
272 type Err = core::num::ParseIntError;
273 fn from_str(s: &str) -> Result<Self, Self::Err> {
274 s.parse::<u32>().map(Into::into)
275 }
276}
277impl core::str::FromStr for HardenedIndex {
278 type Err = errors::ParseChildIndexError;
279 fn from_str(s: &str) -> Result<Self, Self::Err> {
280 let index = s
281 .parse::<u32>()
282 .map_err(errors::ParseChildIndexError::ParseInt)?;
283 HardenedIndex::try_from(index).map_err(errors::ParseChildIndexError::IndexNotInRange)
284 }
285}
286impl core::str::FromStr for NonHardenedIndex {
287 type Err = errors::ParseChildIndexError;
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 let index = s
290 .parse::<u32>()
291 .map_err(errors::ParseChildIndexError::ParseInt)?;
292 NonHardenedIndex::try_from(index).map_err(errors::ParseChildIndexError::IndexNotInRange)
293 }
294}
295
296impl<E: Curve> From<&ExtendedSecretKey<E>> for ExtendedPublicKey<E> {
297 fn from(sk: &ExtendedSecretKey<E>) -> Self {
298 ExtendedPublicKey {
299 public_key: Point::generator() * &sk.secret_key,
300 chain_code: sk.chain_code,
301 }
302 }
303}
304
305impl<E: Curve> From<ExtendedSecretKey<E>> for ExtendedKeyPair<E> {
306 fn from(secret_key: ExtendedSecretKey<E>) -> Self {
307 Self {
308 public_key: (&secret_key).into(),
309 secret_key,
310 }
311 }
312}
313
314impl<E: Curve> ExtendedKeyPair<E> {
315 /// Returns chain code of the key
316 pub fn chain_code(&self) -> &ChainCode {
317 debug_assert_eq!(self.public_key.chain_code, self.secret_key.chain_code);
318 &self.public_key.chain_code
319 }
320
321 /// Returns extended public key
322 pub fn public_key(&self) -> &ExtendedPublicKey<E> {
323 &self.public_key
324 }
325
326 /// Returns extended secret key
327 pub fn secret_key(&self) -> &ExtendedSecretKey<E> {
328 &self.secret_key
329 }
330}
331
332#[cfg(feature = "serde")]
333impl<E: Curve> serde::Serialize for ExtendedKeyPair<E> {
334 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
335 where
336 S: serde::Serializer,
337 {
338 self.secret_key.serialize(serializer)
339 }
340}
341
342#[cfg(feature = "serde")]
343impl<'de, E: Curve> serde::Deserialize<'de> for ExtendedKeyPair<E> {
344 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
345 where
346 D: serde::Deserializer<'de>,
347 {
348 let secret_key = ExtendedSecretKey::<E>::deserialize(deserializer)?;
349 Ok(secret_key.into())
350 }
351}
352
353/// * `$t` - type to monomorphise for, like `Slip10` or `Edwards`
354/// * `$m` - current module, module where these functions will appear. Used in doc
355/// tests only
356/// * `$e` - curve supported by this HD derivation, used in doc tests only
357#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))]
358macro_rules! create_aliases {
359 ($t:ty, $m:expr) => { $crate::create_aliases!($t, $m, hd_wallet::curves::Secp256k1); };
360 ($t:ty, $m:expr, $e:ty) => {
361 /// Derives a shift for non-hardened child
362 ///
363 #[doc = concat!("Alias to [`<", stringify!($t), " as DeriveShift<E>>::derive_public_shift`](crate::DeriveShift::derive_public_shift)")]
364 pub fn derive_public_shift<E>(
365 parent_public_key: &crate::ExtendedPublicKey<E>,
366 child_index: crate::NonHardenedIndex,
367 ) -> crate::DerivedShift<E>
368 where
369 E: generic_ec::Curve,
370 $t: crate::DeriveShift<E>,
371 {
372 <$t as crate::DeriveShift<E>>::derive_public_shift(parent_public_key, child_index)
373 }
374
375 /// Derive a shift for hardened child
376 ///
377 #[doc = concat!("Alias to [`<", stringify!($t), " as DeriveShift<E>>::derive_hardened_shift`](crate::DeriveShift::derive_hardened_shift)")]
378 pub fn derive_hardened_shift<E>(
379 parent_key: &crate::ExtendedKeyPair<E>,
380 child_index: crate::HardenedIndex,
381 ) -> crate::DerivedShift<E>
382 where
383 E: generic_ec::Curve,
384 $t: crate::DeriveShift<E>,
385 {
386 <$t as crate::DeriveShift<E>>::derive_hardened_shift(parent_key, child_index)
387 }
388
389 /// Derives child extended public key from parent extended public key
390 ///
391 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_public_key`](crate::HdWallet::derive_child_public_key)")]
392 ///
393 /// ### Example
394 /// Derive a master public key m/1
395 /// ```rust,no_run
396 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
397 /// # let seed = b"do not use this seed :)".as_slice();
398 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
399 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
400 /// #
401 #[doc = concat!("let derived_key = hd_wallet::", stringify!($m), "::derive_child_public_key(")]
402 /// &master_public_key,
403 /// 1.try_into()?,
404 /// );
405 /// # Ok::<(), Box<dyn std::error::Error>>(())
406 /// ```
407 pub fn derive_child_public_key<E>(
408 parent_public_key: &crate::ExtendedPublicKey<E>,
409 child_index: crate::NonHardenedIndex,
410 ) -> crate::ExtendedPublicKey<E>
411 where
412 E: generic_ec::Curve,
413 $t: crate::HdWallet<E>,
414 {
415 <$t as crate::HdWallet<E>>::derive_child_public_key(parent_public_key, child_index)
416 }
417
418 /// Derives child key pair (extended secret key + public key) from parent key pair
419 ///
420 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_key_pair`](crate::HdWallet::derive_child_key_pair)")]
421 ///
422 /// ### Example
423 /// Derive child key m/1<sub>H</sub> from master key
424 /// ```rust,no_run
425 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
426 /// # let seed = b"do not use this seed :)".as_slice();
427 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
428 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
429 /// #
430 #[doc = concat!("let derived_key = hd_wallet::", stringify!($m), "::derive_child_key_pair(")]
431 /// &master_key_pair,
432 /// 1 + hd_wallet::H,
433 /// );
434 /// # Ok::<(), Box<dyn std::error::Error>>(())
435 /// ```
436 pub fn derive_child_key_pair<E>(
437 parent_key: &crate::ExtendedKeyPair<E>,
438 child_index: impl Into<crate::ChildIndex>,
439 ) -> crate::ExtendedKeyPair<E>
440 where
441 E: generic_ec::Curve,
442 $t: crate::HdWallet<E>,
443 {
444 <$t as crate::HdWallet<E>>::derive_child_key_pair(parent_key, child_index)
445 }
446
447 /// Derives a child key pair with specified derivation path from parent key pair
448 ///
449 /// Derivation path is a fallible iterator that yields child indexes. If iterator
450 /// yields an error, it's propagated to the caller.
451 ///
452 /// Returns:
453 /// * `Ok(child_key_pair)` if derivation was successful
454 /// * `Err(index_err)` if path contained `Err(index_err)`
455 ///
456 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::try_derive_child_key_pair_with_path`](crate::HdWallet::try_derive_child_key_pair_with_path)")]
457 ///
458 /// ### Example
459 /// Parse a path from the string and derive a child without extra allocations:
460 /// ```rust,no_run
461 /// use hd_wallet::HdWallet;
462 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
463 /// # let seed = b"16-64 bytes of high entropy".as_slice();
464 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
465 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
466 ///
467 /// let path = "1/10/2";
468 /// let child_indexes = path.split('/').map(str::parse::<u32>);
469 #[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::try_derive_child_key_pair_with_path(")]
470 /// &master_key_pair,
471 /// child_indexes,
472 /// )?;
473 /// # Ok::<_, Box<dyn std::error::Error>>(())
474 /// ```
475 pub fn try_derive_child_key_pair_with_path<E, Err>(
476 parent_key: &crate::ExtendedKeyPair<E>,
477 path: impl IntoIterator<Item = Result<impl Into<crate::ChildIndex>, Err>>,
478 ) -> Result<crate::ExtendedKeyPair<E>, Err>
479 where
480 E: generic_ec::Curve,
481 $t: crate::HdWallet<E>,
482 {
483 <$t as crate::HdWallet<E>>::try_derive_child_key_pair_with_path(parent_key, path)
484 }
485 /// Derives a child key pair with specified derivation path from parent key pair
486 ///
487 /// Derivation path is an iterator that yields child indexes.
488 ///
489 /// If derivation path is empty, `parent_key` is returned
490 ///
491 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_key_pair_with_path`](crate::HdWallet::derive_child_key_pair_with_path)")]
492 ///
493 /// ### Example
494 /// Derive a child key with path m/1/10/1<sub>H</sub>
495 /// ```rust,no_run
496 /// use hd_wallet::HdWallet;
497 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
498 /// # let seed = b"16-64 bytes of high entropy".as_slice();
499 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
500 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
501 ///
502 #[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::derive_child_key_pair_with_path(")]
503 /// &master_key_pair,
504 /// [1, 10, 1 + hd_wallet::H],
505 /// );
506 /// # Ok::<(), Box<dyn std::error::Error>>(())
507 /// ```
508 pub fn derive_child_key_pair_with_path<E>(
509 parent_key: &crate::ExtendedKeyPair<E>,
510 path: impl IntoIterator<Item = impl Into<crate::ChildIndex>>,
511 ) -> crate::ExtendedKeyPair<E>
512 where
513 E: generic_ec::Curve,
514 $t: crate::HdWallet<E>,
515 {
516 <$t as crate::HdWallet<E>>::derive_child_key_pair_with_path(parent_key, path)
517 }
518
519 /// Derives a child public key with specified derivation path
520 ///
521 /// Derivation path is a fallible iterator that yields child indexes. If iterator
522 /// yields an error, it's propagated to the caller.
523 ///
524 /// Returns:
525 /// * `Ok(child_pk)` if derivation was successful
526 /// * `Err(index_err)` if path contained `Err(index_err)`
527 ///
528 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::try_derive_child_public_key_with_path`](crate::HdWallet::try_derive_child_public_key_with_path)")]
529 ///
530 /// ### Example
531 /// Parse a path from the string and derive a child without extra allocations:
532 /// ```rust,no_run
533 /// use hd_wallet::HdWallet;
534 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
535 /// # let seed = b"16-64 bytes of high entropy".as_slice();
536 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
537 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
538 ///
539 /// let path = "1/10/2";
540 /// let child_indexes = path.split('/').map(str::parse);
541 #[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::try_derive_child_public_key_with_path(")]
542 /// &master_public_key,
543 /// child_indexes,
544 /// )?;
545 /// # Ok::<_, Box<dyn std::error::Error>>(())
546 /// ```
547 pub fn try_derive_child_public_key_with_path<E, Err>(
548 parent_public_key: &crate::ExtendedPublicKey<E>,
549 path: impl IntoIterator<Item = Result<crate::NonHardenedIndex, Err>>,
550 ) -> Result<crate::ExtendedPublicKey<E>, Err>
551 where
552 E: generic_ec::Curve,
553 $t: crate::HdWallet<E>,
554 {
555 <$t as crate::HdWallet<E>>::try_derive_child_public_key_with_path(parent_public_key, path)
556 }
557
558 /// Derives a child public key with specified derivation path
559 ///
560 /// Derivation path is an iterator that yields child indexes.
561 ///
562 /// If derivation path is empty, `parent_public_key` is returned
563 ///
564 #[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_public_key_with_path`](crate::HdWallet::derive_child_public_key_with_path)")]
565 ///
566 /// ### Example
567 /// Derive a child key with path m/1/10
568 /// ```rust,no_run
569 /// use hd_wallet::HdWallet;
570 #[doc = concat!( "# type E = ", stringify!($e), ";" )]
571 /// # let seed = b"16-64 bytes of high entropy".as_slice();
572 /// # let master_key: hd_wallet::ExtendedSecretKey<E> = todo!();
573 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
574 ///
575 #[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::derive_child_public_key_with_path(")]
576 /// &master_public_key,
577 /// [1.try_into()?, 10.try_into()?],
578 /// );
579 /// # Ok::<(), Box<dyn std::error::Error>>(())
580 /// ```
581 pub fn derive_child_public_key_with_path<E>(
582 parent_public_key: &crate::ExtendedPublicKey<E>,
583 path: impl IntoIterator<Item = crate::NonHardenedIndex>,
584 ) -> crate::ExtendedPublicKey<E>
585 where
586 E: generic_ec::Curve,
587 $t: crate::HdWallet<E>,
588 {
589 <$t as crate::HdWallet<E>>::derive_child_public_key_with_path(parent_public_key, path)
590 }
591 };
592}
593#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))]
594pub(crate) use create_aliases;
595
596/// HD derivation
597pub trait HdWallet<E: Curve>: DeriveShift<E> {
598 /// Derives child extended public key from parent extended public key
599 ///
600 /// ### Example
601 /// Derive a master public key m/1
602 /// ```rust
603 /// use hd_wallet::HdWallet;
604 ///
605 /// # let seed = b"do not use this seed :)".as_slice();
606 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
607 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
608 /// #
609 /// let derived_key = hd_wallet::Slip10::derive_child_public_key(
610 /// &master_public_key,
611 /// 1.try_into()?,
612 /// );
613 /// # Ok::<(), Box<dyn std::error::Error>>(())
614 /// ```
615 fn derive_child_public_key(
616 parent_public_key: &ExtendedPublicKey<E>,
617 child_index: NonHardenedIndex,
618 ) -> ExtendedPublicKey<E> {
619 Self::derive_public_shift(parent_public_key, child_index).child_public_key
620 }
621
622 /// Derives child key pair (extended secret key + public key) from parent key pair
623 ///
624 /// ### Example
625 /// Derive child key m/1<sub>H</sub> from master key
626 /// ```rust
627 /// use hd_wallet::HdWallet;
628 ///
629 /// # let seed = b"do not use this seed :)".as_slice();
630 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
631 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
632 /// #
633 /// let derived_key = hd_wallet::Slip10::derive_child_key_pair(
634 /// &master_key_pair,
635 /// 1 + hd_wallet::H,
636 /// );
637 /// # Ok::<(), Box<dyn std::error::Error>>(())
638 /// ```
639 fn derive_child_key_pair(
640 parent_key: &ExtendedKeyPair<E>,
641 child_index: impl Into<ChildIndex>,
642 ) -> ExtendedKeyPair<E> {
643 let child_index = child_index.into();
644 let shift = match child_index {
645 ChildIndex::Hardened(i) => Self::derive_hardened_shift(parent_key, i),
646 ChildIndex::NonHardened(i) => Self::derive_public_shift(&parent_key.public_key, i),
647 };
648 let mut child_sk = &parent_key.secret_key.secret_key + shift.shift;
649 let child_sk = SecretScalar::new(&mut child_sk);
650 ExtendedKeyPair {
651 secret_key: ExtendedSecretKey {
652 secret_key: child_sk,
653 chain_code: shift.child_public_key.chain_code,
654 },
655 public_key: shift.child_public_key,
656 }
657 }
658
659 /// Derives a child key pair with specified derivation path from parent key pair
660 ///
661 /// Derivation path is a fallible iterator that yields child indexes. If iterator
662 /// yields an error, it's propagated to the caller.
663 ///
664 /// Returns:
665 /// * `Ok(child_key_pair)` if derivation was successful
666 /// * `Err(index_err)` if path contained `Err(index_err)`
667 ///
668 /// ### Example
669 /// Parse a path from the string and derive a child without extra allocations:
670 /// ```rust
671 /// use hd_wallet::HdWallet;
672 /// # let seed = b"16-64 bytes of high entropy".as_slice();
673 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
674 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
675 ///
676 /// let path = "1/10/2";
677 /// let child_indexes = path.split('/').map(str::parse::<u32>);
678 /// let child_key = hd_wallet::Slip10::try_derive_child_key_pair_with_path(
679 /// &master_key_pair,
680 /// child_indexes,
681 /// )?;
682 /// # Ok::<_, Box<dyn std::error::Error>>(())
683 /// ```
684 fn try_derive_child_key_pair_with_path<Err>(
685 parent_key: &ExtendedKeyPair<E>,
686 path: impl IntoIterator<Item = Result<impl Into<ChildIndex>, Err>>,
687 ) -> Result<ExtendedKeyPair<E>, Err> {
688 let mut derived_key = parent_key.clone();
689 for child_index in path {
690 derived_key = Self::derive_child_key_pair(&derived_key, child_index?);
691 }
692 Ok(derived_key)
693 }
694
695 /// Derives a child key pair with specified derivation path from parent key pair
696 ///
697 /// Derivation path is an iterator that yields child indexes.
698 ///
699 /// If derivation path is empty, `parent_key` is returned
700 ///
701 /// ### Example
702 /// Derive a child key with path m/1/10/1<sub>H</sub>
703 /// ```rust
704 /// use hd_wallet::HdWallet;
705 /// # let seed = b"16-64 bytes of high entropy".as_slice();
706 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
707 /// # let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key);
708 ///
709 /// let child_key = hd_wallet::Slip10::derive_child_key_pair_with_path(
710 /// &master_key_pair,
711 /// [1, 10, 1 + hd_wallet::H],
712 /// );
713 /// # Ok::<(), Box<dyn std::error::Error>>(())
714 /// ```
715 fn derive_child_key_pair_with_path(
716 parent_key: &ExtendedKeyPair<E>,
717 path: impl IntoIterator<Item = impl Into<ChildIndex>>,
718 ) -> ExtendedKeyPair<E> {
719 let result = Self::try_derive_child_key_pair_with_path(
720 parent_key,
721 path.into_iter().map(Ok::<_, core::convert::Infallible>),
722 );
723 match result {
724 Ok(key) => key,
725 Err(err) => match err {},
726 }
727 }
728
729 /// Derives a child public key with specified derivation path
730 ///
731 /// Derivation path is a fallible iterator that yields child indexes. If iterator
732 /// yields an error, it's propagated to the caller.
733 ///
734 /// Returns:
735 /// * `Ok(child_pk)` if derivation was successful
736 /// * `Err(index_err)` if path contained `Err(index_err)`
737 ///
738 /// ### Example
739 /// Parse a path from the string and derive a child without extra allocations:
740 /// ```rust
741 /// use hd_wallet::HdWallet;
742 /// # let seed = b"16-64 bytes of high entropy".as_slice();
743 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
744 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
745 ///
746 /// let path = "1/10/2";
747 /// let child_indexes = path.split('/').map(str::parse);
748 /// let child_key = hd_wallet::Slip10::try_derive_child_public_key_with_path(
749 /// &master_public_key,
750 /// child_indexes,
751 /// )?;
752 /// # Ok::<_, Box<dyn std::error::Error>>(())
753 /// ```
754 fn try_derive_child_public_key_with_path<Err>(
755 parent_public_key: &ExtendedPublicKey<E>,
756 path: impl IntoIterator<Item = Result<NonHardenedIndex, Err>>,
757 ) -> Result<ExtendedPublicKey<E>, Err> {
758 let mut derived_key = *parent_public_key;
759 for child_index in path {
760 derived_key = Self::derive_child_public_key(&derived_key, child_index?);
761 }
762 Ok(derived_key)
763 }
764
765 /// Derives a child public key with specified derivation path
766 ///
767 /// Derivation path is an iterator that yields child indexes.
768 ///
769 /// If derivation path is empty, `parent_public_key` is returned
770 ///
771 /// ### Example
772 /// Derive a child key with path m/1/10
773 /// ```rust
774 /// use hd_wallet::HdWallet;
775 /// # let seed = b"16-64 bytes of high entropy".as_slice();
776 /// # let master_key = hd_wallet::slip10::derive_master_key::<hd_wallet::curves::Secp256k1>(seed)?;
777 /// # let master_public_key = hd_wallet::ExtendedPublicKey::from(&master_key);
778 ///
779 /// let child_key = hd_wallet::Slip10::derive_child_public_key_with_path(
780 /// &master_public_key,
781 /// [1.try_into()?, 10.try_into()?],
782 /// );
783 /// # Ok::<(), Box<dyn std::error::Error>>(())
784 /// ```
785 fn derive_child_public_key_with_path(
786 parent_public_key: &ExtendedPublicKey<E>,
787 path: impl IntoIterator<Item = NonHardenedIndex>,
788 ) -> ExtendedPublicKey<E> {
789 let result = Self::try_derive_child_public_key_with_path(
790 parent_public_key,
791 path.into_iter().map(Ok::<_, core::convert::Infallible>),
792 );
793 match result {
794 Ok(key) => key,
795 Err(err) => match err {},
796 }
797 }
798}
799
800impl<E: Curve, S: DeriveShift<E>> HdWallet<E> for S {}
801
802/// Core functionality of HD wallet derivation, everything is defined on top of it
803pub trait DeriveShift<E: Curve> {
804 /// Derives a shift for non-hardened child
805 ///
806 /// We support only HD derivations that are always defined. This function may not panic.
807 fn derive_public_shift(
808 parent_public_key: &ExtendedPublicKey<E>,
809 child_index: NonHardenedIndex,
810 ) -> DerivedShift<E>;
811
812 /// Derive a shift for hardened child
813 ///
814 /// We support only HD derivations that are always defined. This function may not panic.
815 fn derive_hardened_shift(
816 parent_key: &ExtendedKeyPair<E>,
817 child_index: HardenedIndex,
818 ) -> DerivedShift<E>;
819}