rosetta_crypto/
bip44.rs

1//! BIP44 implementation.
2use anyhow::Result;
3use std::str::FromStr;
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    /// Is a hard derivation.
13    pub fn is_hardened(&self) -> bool {
14        self.0 & HARDENED_BIT == HARDENED_BIT
15    }
16
17    /// Is a normal derivation.
18    pub fn is_normal(&self) -> bool {
19        self.0 & HARDENED_BIT == 0
20    }
21
22    /// Creates a new hard derivation.
23    pub fn hardened_from_u32(index: u32) -> Self {
24        ChildNumber(index | HARDENED_BIT)
25    }
26
27    /// Creates a new soft derivation.
28    pub fn non_hardened_from_u32(index: u32) -> Self {
29        ChildNumber(index)
30    }
31
32    /// Returns the index.
33    pub fn index(&self) -> u32 {
34        self.0 & (i32::MAX as u32)
35    }
36
37    /// Returns BIP32 byte sequence.
38    pub fn to_bytes(&self) -> [u8; 4] {
39        self.0.to_be_bytes()
40    }
41
42    /// Returns the substrate compatible chain code.
43    pub fn to_substrate_chain_code(&self) -> [u8; 32] {
44        let mut chain_code = [0; 32];
45        let bytes = (self.index() as u64).to_le_bytes();
46        chain_code[..bytes.len()].copy_from_slice(&bytes[..]);
47        chain_code
48    }
49}
50
51impl core::ops::Add<u32> for ChildNumber {
52    type Output = Self;
53
54    fn add(self, other: u32) -> Self::Output {
55        Self(self.0 + other)
56    }
57}
58
59impl FromStr for ChildNumber {
60    type Err = anyhow::Error;
61
62    fn from_str(child: &str) -> Result<ChildNumber> {
63        let (child, mask) = if let Some(child) = child.strip_suffix('\'') {
64            (child, HARDENED_BIT)
65        } else {
66            (child, 0)
67        };
68
69        let index: u32 = child.parse()?;
70
71        if index & HARDENED_BIT != 0 {
72            anyhow::bail!("invalid child number");
73        }
74
75        Ok(ChildNumber(index | mask))
76    }
77}
78
79/// BIP44 key derivation path.
80#[derive(Clone, PartialEq, Eq, Debug, Default)]
81pub struct DerivationPath {
82    path: Vec<ChildNumber>,
83}
84
85impl FromStr for DerivationPath {
86    type Err = anyhow::Error;
87
88    fn from_str(path: &str) -> Result<DerivationPath> {
89        let mut path = path.split('/');
90
91        if path.next() != Some("m") {
92            anyhow::bail!("invalid derivation path");
93        }
94
95        Ok(DerivationPath {
96            path: path.map(str::parse).collect::<Result<Vec<ChildNumber>>>()?,
97        })
98    }
99}
100
101impl AsRef<[ChildNumber]> for DerivationPath {
102    fn as_ref(&self) -> &[ChildNumber] {
103        &self.path
104    }
105}
106
107impl DerivationPath {
108    /// Returns an iterator of child numbers.
109    pub fn iter(&self) -> impl Iterator<Item = &ChildNumber> {
110        self.path.iter()
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn derive_path() {
120        let path: DerivationPath = "m/44'/60'/0'/0".parse().unwrap();
121
122        assert_eq!(
123            path,
124            DerivationPath {
125                path: vec![
126                    ChildNumber::hardened_from_u32(44),
127                    ChildNumber::hardened_from_u32(60),
128                    ChildNumber::hardened_from_u32(0),
129                    ChildNumber::non_hardened_from_u32(0),
130                ],
131            }
132        );
133    }
134}