use anyhow::anyhow;
use bitcoin::bip32::{ChildNumber, DerivationPath};
use bitcoin::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
use std::convert::TryInto;
use std::str::FromStr;
use url::Url;
pub fn get_derivation_path(hashing_key: [u8; 32], url: &Url) -> anyhow::Result<DerivationPath> {
let mut engine = HmacEngine::<sha256::Hash>::new(&hashing_key);
let host = url.host().ok_or(anyhow!("No host"))?;
engine.input(host.to_string().as_bytes());
let derivation_mat = Hmac::<sha256::Hash>::from_engine(engine);
let uints: [u32; 4] = (0..4)
.map(|i| u32::from_be_bytes(derivation_mat[(i * 4)..((i + 1) * 4)].try_into().unwrap()))
.collect::<Vec<u32>>()
.try_into()
.expect("slice with incorrect length");
let children = uints.map(ChildNumber::from);
let path = DerivationPath::from_str(&format!(
"m/138'/{}/{}/{}/{}",
children[0], children[1], children[2], children[3]
))
.map_err(|e| anyhow!("Error deriving path: {e}"))?;
Ok(path)
}
#[cfg(test)]
mod test {
use bitcoin::bip32::{ChildNumber, DerivationPath};
use bitcoin::hashes::hex::FromHex;
use std::str::FromStr;
use url::Url;
#[test]
fn test_lud_05_static_test_vector() {
let hashing_key: [u8; 32] =
FromHex::from_hex("7d417a6a5e9a6a4a879aeaba11a11838764c8fa2b959c242d43dea682b3e409b")
.unwrap();
let url = Url::parse("https://site.com").unwrap();
let path = super::get_derivation_path(hashing_key, &url).unwrap();
let expected = DerivationPath::from_str(&format!(
"m/138'/{}/{}/{}/{}",
ChildNumber::from(1588488367),
ChildNumber::from(2659270754),
ChildNumber::from(38110259),
ChildNumber::from(4136336762),
))
.unwrap();
assert_eq!(path, expected);
}
}