1use anyhow::anyhow;
2use bitcoin::bip32::{ChildNumber, DerivationPath};
3use bitcoin::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
4use std::convert::TryInto;
5use std::str::FromStr;
6use url::Url;
7
8pub fn get_derivation_path(hashing_key: [u8; 32], url: &Url) -> anyhow::Result<DerivationPath> {
11 let mut engine = HmacEngine::<sha256::Hash>::new(&hashing_key);
13
14 let host = url.host().ok_or(anyhow!("No host"))?;
16
17 engine.input(host.to_string().as_bytes());
19 let derivation_mat = Hmac::<sha256::Hash>::from_engine(engine);
20
21 let uints: [u32; 4] = (0..4)
23 .map(|i| u32::from_be_bytes(derivation_mat[(i * 4)..((i + 1) * 4)].try_into().unwrap()))
24 .collect::<Vec<u32>>()
25 .try_into()
26 .expect("slice with incorrect length");
27 let children = uints.map(ChildNumber::from);
29
30 let path = DerivationPath::from_str(&format!(
32 "m/138'/{}/{}/{}/{}",
33 children[0], children[1], children[2], children[3]
34 ))
35 .map_err(|e| anyhow!("Error deriving path: {e}"))?;
36
37 Ok(path)
38}
39
40#[cfg(test)]
41mod test {
42 use bitcoin::bip32::{ChildNumber, DerivationPath};
43 use bitcoin::hashes::hex::FromHex;
44 use std::str::FromStr;
45 use url::Url;
46
47 #[test]
48 fn test_lud_05_static_test_vector() {
49 let hashing_key: [u8; 32] =
50 FromHex::from_hex("7d417a6a5e9a6a4a879aeaba11a11838764c8fa2b959c242d43dea682b3e409b")
51 .unwrap();
52 let url = Url::parse("https://site.com").unwrap();
53
54 let path = super::get_derivation_path(hashing_key, &url).unwrap();
55 let expected = DerivationPath::from_str(&format!(
56 "m/138'/{}/{}/{}/{}",
57 ChildNumber::from(1588488367),
58 ChildNumber::from(2659270754),
59 ChildNumber::from(38110259),
60 ChildNumber::from(4136336762),
61 ))
62 .unwrap();
63
64 assert_eq!(path, expected);
65 }
66}