coins_bip32/
xkeys.rs

1use coins_core::hashes::{Hash160, Hash160Digest, MarkedDigest, MarkedDigestOutput};
2use hmac::{Hmac, Mac};
3use k256::{ecdsa, elliptic_curve::sec1::FromEncodedPoint};
4use sha2::Sha512;
5use std::{
6    convert::{TryFrom, TryInto},
7    ops::{AddAssign, Mul},
8};
9
10use crate::{
11    path::DerivationPath,
12    primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo},
13    Bip32Error, BIP32_HARDEN,
14};
15
16/// The BIP32-defined seed used for derivation of the root node.
17pub const SEED: &[u8; 12] = b"Bitcoin seed";
18
19fn hmac_and_split(
20    seed: &[u8],
21    data: &[u8],
22) -> Result<(k256::NonZeroScalar, ChainCode), Bip32Error> {
23    let mut mac = Hmac::<Sha512>::new_from_slice(seed).expect("key length is ok");
24    mac.update(data);
25    let result = mac.finalize().into_bytes();
26
27    let left = k256::NonZeroScalar::try_from(&result[..32])?;
28
29    let mut right = [0u8; 32];
30    right.copy_from_slice(&result[32..]);
31
32    Ok((left, ChainCode(right)))
33}
34
35/// A Parent key can be used to derive children.
36pub trait Parent: Sized + Clone {
37    /// Derive the child at `index`. Note that this may produce the child at
38    /// `index+1` in rare circumstances. For public keys this will derive public
39    /// children. For private keys it will derive private children.
40    fn derive_child(&self, index: u32) -> Result<Self, Bip32Error>;
41
42    /// Derive a series of child indices. Allows traversing several levels of the tree at once.
43    /// Accepts an iterator producing u32, or a string.
44    fn derive_path<E, P>(&self, p: P) -> Result<Self, Bip32Error>
45    where
46        E: Into<Bip32Error>,
47        P: TryInto<DerivationPath, Error = E>,
48    {
49        let path: DerivationPath = p.try_into().map_err(Into::into)?;
50
51        if path.is_empty() {
52            return Ok(self.clone());
53        }
54
55        let mut current = self.to_owned();
56        for index in path.iter() {
57            current = current.derive_child(*index)?;
58        }
59        Ok(current)
60    }
61}
62
63/// A BIP32 eXtended Privkey
64pub struct XPriv {
65    pub(crate) key: ecdsa::SigningKey,
66    pub(crate) xkey_info: XKeyInfo,
67}
68
69impl PartialEq for XPriv {
70    fn eq(&self, other: &XPriv) -> bool {
71        self.fingerprint() == other.fingerprint() && self.xkey_info == other.xkey_info
72    }
73}
74
75impl Clone for XPriv {
76    fn clone(&self) -> Self {
77        Self {
78            key: ecdsa::SigningKey::from_bytes(&self.key.to_bytes()).unwrap(),
79            xkey_info: self.xkey_info,
80        }
81    }
82}
83
84inherit_signer!(XPriv.key);
85
86impl std::fmt::Debug for XPriv {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        f.debug_struct("XPriv")
89            .field("key fingerprint", &self.fingerprint())
90            .field("key info", &self.xkey_info)
91            .finish()
92    }
93}
94
95impl AsRef<XPriv> for XPriv {
96    fn as_ref(&self) -> &XPriv {
97        self
98    }
99}
100
101impl AsRef<XKeyInfo> for XPriv {
102    fn as_ref(&self) -> &XKeyInfo {
103        &self.xkey_info
104    }
105}
106
107impl AsRef<ecdsa::SigningKey> for XPriv {
108    fn as_ref(&self) -> &ecdsa::SigningKey {
109        &self.key
110    }
111}
112
113impl XPriv {
114    /// Instantiate a new XPriv.
115    pub const fn new(key: ecdsa::SigningKey, xkey_info: XKeyInfo) -> Self {
116        Self { key, xkey_info }
117    }
118
119    /// Derive the associated XPub
120    pub fn verify_key(&self) -> XPub {
121        XPub {
122            key: self.key.verifying_key().to_owned(),
123            xkey_info: self.xkey_info,
124        }
125    }
126
127    /// The fingerprint is the first 4 bytes of the HASH160 of the public key
128    pub fn fingerprint(&self) -> KeyFingerprint {
129        self.verify_key().fingerprint()
130    }
131
132    /// Generate a customized root node
133    pub fn root_node(
134        hmac_key: &[u8],
135        data: &[u8],
136        hint: Option<Hint>,
137    ) -> Result<XPriv, Bip32Error> {
138        Self::custom_root_node(hmac_key, data, hint)
139    }
140
141    /// Generate a root node from some seed data. Uses the BIP32-standard hmac
142    /// key.
143    ///
144    /// # Important:
145    ///
146    /// Use a seed of AT LEAST 128 bits.
147    pub fn root_from_seed(data: &[u8], hint: Option<Hint>) -> Result<XPriv, Bip32Error> {
148        Self::custom_root_from_seed(data, hint)
149    }
150
151    /// Instantiate a root node using a custom HMAC key.
152    pub fn custom_root_node(
153        hmac_key: &[u8],
154        data: &[u8],
155        hint: Option<Hint>,
156    ) -> Result<XPriv, Bip32Error> {
157        if data.len() < 16 {
158            return Err(Bip32Error::SeedTooShort);
159        }
160        let parent = KeyFingerprint([0u8; 4]);
161        let (key, chain_code) = hmac_and_split(hmac_key, data)?;
162        if bool::from(key.is_zero()) {
163            // This can only be tested by mocking hmac_and_split
164            return Err(Bip32Error::InvalidKey);
165        }
166
167        let key = ecdsa::SigningKey::from(key);
168
169        Ok(XPriv {
170            key,
171            xkey_info: XKeyInfo {
172                depth: 0,
173                parent,
174                index: 0,
175                chain_code,
176                hint: hint.unwrap_or(Hint::SegWit),
177            },
178        })
179    }
180
181    /// Generate a root node from some seed data. Uses the BIP32-standard hmac key.
182    ///
183    ///
184    /// # Important:
185    ///
186    /// Use a seed of AT LEAST 128 bits.
187    pub fn custom_root_from_seed(data: &[u8], hint: Option<Hint>) -> Result<XPriv, Bip32Error> {
188        Self::custom_root_node(SEED, data, hint)
189    }
190
191    /// Derive a series of child indices. Allows traversing several levels of the tree at once.
192    /// Accepts an iterator producing u32, or a string.
193    pub fn derive_path<E, P>(&self, p: P) -> Result<Self, Bip32Error>
194    where
195        E: Into<Bip32Error>,
196        P: TryInto<DerivationPath, Error = E>,
197    {
198        let path: DerivationPath = p.try_into().map_err(Into::into)?;
199
200        if path.is_empty() {
201            return Ok(self.clone());
202        }
203
204        let mut current = self.to_owned();
205        for index in path.iter() {
206            current = current.derive_child(*index)?;
207        }
208        Ok(current)
209    }
210}
211
212impl Parent for XPriv {
213    fn derive_child(&self, index: u32) -> Result<Self, Bip32Error> {
214        let hardened = index >= BIP32_HARDEN;
215
216        let key: &ecdsa::SigningKey = self.as_ref();
217
218        let mut data: Vec<u8> = vec![];
219        if hardened {
220            data.push(0);
221            data.extend(key.to_bytes());
222            data.extend(index.to_be_bytes());
223        } else {
224            data.extend(key.verifying_key().to_sec1_bytes().iter());
225            data.extend(index.to_be_bytes());
226        };
227
228        let res = hmac_and_split(&self.xkey_info.chain_code.0, &data);
229        let (tweak, chain_code) = match res {
230            Ok((tweak, chain_code)) => (tweak, chain_code),
231            _ => return self.derive_child(index + 1),
232        };
233
234        let parent_key = k256::NonZeroScalar::from_repr(key.to_bytes()).unwrap();
235        let tweaked = tweak.clone().add(&parent_key);
236
237        let tweaked: k256::NonZeroScalar =
238            Option::from(k256::NonZeroScalar::new(tweaked)).ok_or(Bip32Error::BadTweak)?;
239
240        Ok(Self {
241            key: ecdsa::SigningKey::from(tweaked),
242            xkey_info: XKeyInfo {
243                depth: self.xkey_info.depth + 1,
244                parent: self.fingerprint(),
245                index,
246                chain_code,
247                hint: self.xkey_info.hint,
248            },
249        })
250    }
251}
252
253#[derive(Copy)]
254/// A BIP32 eXtended Public key
255pub struct XPub {
256    pub(crate) key: ecdsa::VerifyingKey,
257    pub(crate) xkey_info: XKeyInfo,
258}
259
260inherit_verifier!(XPub.key);
261
262impl Clone for XPub {
263    fn clone(&self) -> Self {
264        *self
265    }
266}
267
268impl std::fmt::Debug for XPub {
269    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270        f.debug_struct("XPub")
271            .field("public key", &self.key.to_sec1_bytes())
272            .field("key fingerprint", &self.fingerprint())
273            .field("key info", &self.xkey_info)
274            .finish()
275    }
276}
277
278impl AsRef<XPub> for XPub {
279    fn as_ref(&self) -> &XPub {
280        self
281    }
282}
283
284impl AsRef<XKeyInfo> for XPub {
285    fn as_ref(&self) -> &XKeyInfo {
286        &self.xkey_info
287    }
288}
289
290impl AsRef<ecdsa::VerifyingKey> for XPub {
291    fn as_ref(&self) -> &ecdsa::VerifyingKey {
292        &self.key
293    }
294}
295
296impl XPub {
297    /// Instantiate a new XPub
298    pub const fn new(key: ecdsa::VerifyingKey, xkey_info: XKeyInfo) -> Self {
299        Self { key, xkey_info }
300    }
301
302    /// The fingerprint is the first 4 bytes of the HASH160 of the serialized
303    /// public key.
304    pub fn fingerprint(&self) -> KeyFingerprint {
305        let digest = self.pubkey_hash160();
306        let mut buf = [0u8; 4];
307        buf.copy_from_slice(&digest.as_slice()[..4]);
308        buf.into()
309    }
310
311    /// Return the bitcoin HASH160 of the serialized public key
312    pub fn pubkey_hash160(&self) -> Hash160Digest {
313        Hash160::digest_marked(self.key.to_sec1_bytes().as_ref())
314    }
315}
316
317impl PartialEq for XPub {
318    fn eq(&self, other: &XPub) -> bool {
319        self.key == other.key
320    }
321}
322
323impl Parent for XPub {
324    fn derive_child(&self, index: u32) -> Result<XPub, Bip32Error> {
325        if index >= BIP32_HARDEN {
326            return Err(Bip32Error::HardenedDerivationFailed);
327        }
328        let mut data = vec![];
329        // secp256k1 points are converted to compressed form
330        // https://github.com/RustCrypto/elliptic-curves/blob/3ee0ba1aa5bb74777928c21bb198d2a696f0dd9d/k256/src/lib.rs#L98
331        data.extend(self.key.to_sec1_bytes().iter());
332        data.extend(index.to_be_bytes());
333
334        let res = hmac_and_split(&self.xkey_info.chain_code.0, &data);
335
336        let (tweak, chain_code) = match res {
337            Ok((tweak, chain_code)) => (tweak, chain_code),
338            _ => return self.derive_child(index + 1),
339        };
340
341        if bool::from(tweak.is_zero()) {
342            return self.derive_child(index + 1);
343        }
344
345        let parent_key =
346            k256::ProjectivePoint::from_encoded_point(&self.key.to_encoded_point(true)).unwrap();
347        let mut tweak_point = k256::ProjectivePoint::GENERATOR.mul(*tweak);
348        tweak_point.add_assign(parent_key);
349
350        let key = ecdsa::VerifyingKey::from_affine(tweak_point.to_affine())?;
351        Ok(Self {
352            key,
353            xkey_info: XKeyInfo {
354                depth: self.xkey_info.depth + 1,
355                parent: self.fingerprint(),
356                index,
357                chain_code,
358                hint: self.xkey_info.hint,
359            },
360        })
361    }
362}
363
364#[cfg(test)]
365mod test {
366    use super::*;
367    use crate::{
368        enc::{MainnetEncoder, XKeyEncoder},
369        primitives::*,
370    };
371    use coins_core::hashes::Hash256;
372    use k256::ecdsa::signature::{DigestSigner, DigestVerifier};
373
374    use hex;
375
376    struct KeyDeriv<'a> {
377        pub(crate) path: &'a [u32],
378        pub(crate) xpub: String,
379        pub(crate) xpriv: String,
380    }
381
382    fn validate_descendant(d: &KeyDeriv, m: &XPriv) {
383        let xpriv = m.derive_path(d.path).unwrap();
384        let xpub = xpriv.verify_key();
385
386        // let m_pub = m.verify_key();
387        // let xpub_2 = m_pub.derive_path(d.path).unwrap();
388        // assert_eq!(&xpub, &xpub_2);
389
390        let deser_xpriv = MainnetEncoder::xpriv_from_base58(&d.xpriv).unwrap();
391        let deser_xpub = MainnetEncoder::xpub_from_base58(&d.xpub).unwrap();
392
393        assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes());
394        assert_eq!(MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(), d.xpriv);
395        assert_eq!(&xpub.key.to_sec1_bytes(), &deser_xpub.key.to_sec1_bytes());
396        assert_eq!(MainnetEncoder::xpub_to_base58(&xpub).unwrap(), d.xpub);
397    }
398
399    #[test]
400    fn bip32_vector_1() {
401        let seed: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
402
403        let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap();
404        let xpub = xpriv.verify_key();
405
406        let expected_xpub = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8";
407        let expected_xpriv = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
408
409        let deser_xpub = MainnetEncoder::xpub_from_base58(expected_xpub).unwrap();
410        let deser_xpriv = MainnetEncoder::xpriv_from_base58(expected_xpriv).unwrap();
411
412        assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes());
413        assert_eq!(
414            MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(),
415            expected_xpriv
416        );
417        assert_eq!(&xpub.key.to_sec1_bytes(), &deser_xpub.key.to_sec1_bytes());
418        assert_eq!(
419            MainnetEncoder::xpub_to_base58(&xpub).unwrap(),
420            expected_xpub
421        );
422
423        let descendants = [
424            KeyDeriv {
425                path: &[BIP32_HARDEN],
426                xpub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw".to_owned(),
427                xpriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7".to_owned(),
428            },
429            KeyDeriv {
430                path: &[BIP32_HARDEN, 1],
431                xpub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ".to_owned(),
432                xpriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs".to_owned(),
433            },
434            KeyDeriv {
435                path: &[BIP32_HARDEN, 1, 2 + BIP32_HARDEN],
436                xpub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5".to_owned(),
437                xpriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM".to_owned(),
438            },
439            KeyDeriv {
440                path: &[BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2],
441                xpub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV".to_owned(),
442                xpriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334".to_owned(),
443            },
444            KeyDeriv {
445                path: &[BIP32_HARDEN, 1, 2 + BIP32_HARDEN, 2, 1000000000],
446                xpub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy".to_owned(),
447                xpriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76".to_owned(),
448            },
449        ];
450
451        for case in descendants.iter() {
452            validate_descendant(case, &xpriv);
453        }
454    }
455
456    #[test]
457    fn bip32_vector_2() {
458        let seed = hex::decode("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap();
459
460        let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap();
461        let xpub = xpriv.verify_key();
462
463        let expected_xpub = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB";
464        let expected_xpriv = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U";
465
466        let deser_xpub = MainnetEncoder::xpub_from_base58(expected_xpub).unwrap();
467        let deser_xpriv = MainnetEncoder::xpriv_from_base58(expected_xpriv).unwrap();
468
469        assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes());
470        assert_eq!(
471            MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(),
472            expected_xpriv
473        );
474        assert_eq!(&xpub.key.to_sec1_bytes(), &deser_xpub.key.to_sec1_bytes());
475        assert_eq!(
476            MainnetEncoder::xpub_to_base58(&xpub).unwrap(),
477            expected_xpub
478        );
479
480        let descendants = [
481            KeyDeriv {
482                path: &[0],
483                xpub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH".to_owned(),
484                xpriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt".to_owned(),
485            },
486            KeyDeriv {
487                path: &[0, 2147483647 + BIP32_HARDEN],
488                xpub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a".to_owned(),
489                xpriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9".to_owned(),
490            },
491            KeyDeriv {
492                path: &[0, 2147483647 + BIP32_HARDEN, 1],
493                xpub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon".to_owned(),
494                xpriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef".to_owned(),
495            },
496            KeyDeriv {
497                path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN],
498                xpub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL".to_owned(),
499                xpriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc".to_owned(),
500            },
501            KeyDeriv {
502                path: &[0, 2147483647 + BIP32_HARDEN, 1, 2147483646 + BIP32_HARDEN, 2],
503                xpub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt".to_owned(),
504                xpriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j".to_owned(),
505            },
506        ];
507
508        for case in descendants.iter() {
509            validate_descendant(case, &xpriv);
510        }
511    }
512
513    #[test]
514    fn bip32_vector_3() {
515        let seed = hex::decode("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap();
516
517        let xpriv = XPriv::root_from_seed(&seed, Some(Hint::Legacy)).unwrap();
518        let xpub = xpriv.verify_key();
519
520        let expected_xpub = "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13";
521        let expected_xpriv = "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6";
522
523        let deser_xpub = MainnetEncoder::xpub_from_base58(expected_xpub).unwrap();
524        let deser_xpriv = MainnetEncoder::xpriv_from_base58(expected_xpriv).unwrap();
525
526        assert_eq!(&xpriv.key.to_bytes(), &deser_xpriv.key.to_bytes());
527        assert_eq!(
528            MainnetEncoder::xpriv_to_base58(&xpriv).unwrap(),
529            expected_xpriv
530        );
531        assert_eq!(&xpub.key.to_sec1_bytes(), &deser_xpub.key.to_sec1_bytes());
532        assert_eq!(
533            MainnetEncoder::xpub_to_base58(&xpub).unwrap(),
534            expected_xpub
535        );
536
537        let descendants = [
538            KeyDeriv {
539                path: &[BIP32_HARDEN],
540                xpub: "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y".to_owned(),
541                xpriv: "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L".to_owned(),
542            },
543        ];
544
545        for case in descendants.iter() {
546            validate_descendant(case, &xpriv);
547        }
548    }
549
550    #[test]
551    fn it_can_sign_and_verify() {
552        let digest = Hash256::default();
553        let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned();
554        let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap();
555
556        let child = xpriv.derive_child(33).unwrap();
557        let sig: ecdsa::Signature = child.sign_digest(digest.clone());
558
559        let child_xpub = child.verify_key();
560        child_xpub.verify_digest(digest, &sig).unwrap();
561    }
562
563    #[test]
564    fn it_can_verify_and_recover_from_signatures() {
565        let digest = Hash256::default();
566
567        let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned();
568        let xpriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap();
569
570        let child = xpriv.derive_child(33).unwrap();
571
572        let (sig, recovery_id): (ecdsa::Signature, ecdsa::RecoveryId) =
573            child.sign_digest(digest.clone());
574
575        let child_xpub = child.verify_key();
576        child_xpub.verify_digest(digest.clone(), &sig).unwrap();
577
578        let recovered =
579            ecdsa::VerifyingKey::recover_from_digest(digest, &sig, recovery_id).unwrap();
580
581        assert_eq!(&recovered.to_sec1_bytes(), &child_xpub.key.to_sec1_bytes());
582    }
583
584    #[test]
585    fn it_can_read_keys() {
586        let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned();
587        let _xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap();
588    }
589
590    #[test]
591    fn print_key() {
592        let xpriv_str = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".to_owned();
593        let xpriv: XPriv = MainnetEncoder::xpriv_from_base58(&xpriv_str).unwrap();
594        println!("{xpriv:?}");
595    }
596}