ex3_crypto/bip/
bip44.rs

1use std::str::FromStr;
2
3use crate::error::CryptoError;
4
5const HARDENED_BIT: u32 = 1 << 31;
6
7/// A child number for a derived key
8#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9pub struct ChildNumber(u32);
10
11impl ChildNumber {
12    pub fn is_hardened(&self) -> bool {
13        self.0 & HARDENED_BIT == HARDENED_BIT
14    }
15
16    pub fn is_normal(&self) -> bool {
17        self.0 & HARDENED_BIT == 0
18    }
19
20    pub fn to_bytes(&self) -> [u8; 4] {
21        self.0.to_be_bytes()
22    }
23
24    pub fn hardened_from_u32(index: u32) -> Self {
25        ChildNumber(index | HARDENED_BIT)
26    }
27
28    pub fn non_hardened_from_u32(index: u32) -> Self {
29        ChildNumber(index)
30    }
31}
32
33impl FromStr for ChildNumber {
34    type Err = CryptoError;
35
36    fn from_str(child: &str) -> Result<ChildNumber, CryptoError> {
37        let (child, mask) = if child.ends_with('\'') {
38            (&child[..child.len() - 1], HARDENED_BIT)
39        } else {
40            (child, 0)
41        };
42
43        let index: u32 = child
44            .parse()
45            .map_err(|_| CryptoError::InvalidChildNumber.into())?;
46
47        if index & HARDENED_BIT == 0 {
48            Ok(ChildNumber(index | mask))
49        } else {
50            Err(CryptoError::InvalidChildNumber)
51        }
52    }
53}
54
55#[derive(Clone, PartialEq, Eq, Debug, Default)]
56pub struct DerivationPath {
57    path: Vec<ChildNumber>,
58}
59
60impl FromStr for DerivationPath {
61    type Err = CryptoError;
62
63    fn from_str(path: &str) -> Result<DerivationPath, CryptoError> {
64        let mut path = path.split('/');
65
66        if path.next() != Some("m") {
67            return Err(CryptoError::InvalidDerivationPath);
68        }
69
70        Ok(DerivationPath {
71            path: path
72                .map(str::parse)
73                .collect::<Result<Vec<ChildNumber>, CryptoError>>()?,
74        })
75    }
76}
77
78impl DerivationPath {
79    pub fn as_ref(&self) -> &[ChildNumber] {
80        &self.path
81    }
82
83    pub fn iter(&self) -> impl Iterator<Item = &ChildNumber> {
84        self.path.iter()
85    }
86}
87
88pub trait IntoDerivationPath {
89    fn into(self) -> Result<DerivationPath, CryptoError>;
90}
91
92impl IntoDerivationPath for DerivationPath {
93    fn into(self) -> Result<DerivationPath, CryptoError> {
94        Ok(self)
95    }
96}
97
98impl IntoDerivationPath for &str {
99    fn into(self) -> Result<DerivationPath, CryptoError> {
100        self.parse()
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn derive_path() {
110        let path: DerivationPath = "m/44'/60'/0'/0/0".parse().unwrap();
111
112        assert_eq!(
113            path,
114            DerivationPath {
115                path: vec![
116                    ChildNumber(44 | HARDENED_BIT),
117                    ChildNumber(60 | HARDENED_BIT),
118                    ChildNumber(0 | HARDENED_BIT),
119                    ChildNumber(0),
120                    ChildNumber(0),
121                ],
122            }
123        );
124    }
125}