use std::str::FromStr;
use emerald_hwkey::ledger::app::BitcoinApp;
use emerald_hwkey::ledger::app::EthereumApp;
use emerald_hwkey::ledger::connect::LedgerKeyShared;
use emerald_hwkey::ledger::app::PubkeyAddressApp;
use hdpath::{HDPath, StandardHDPath};
use hmac::Hmac;
use hmac::Mac;
use sha2::Sha256;
use emerald_hwkey::ledger::connect::LedgerKey;
use crate::error::VaultError;
use crate::structs::seed::{Bytes256, FingerprintType, HDPathFingerprint, LedgerSource};
pub trait Fingerprints {
fn find_fingerprints(&self) -> Result<Vec<HDPathFingerprint>, VaultError>;
}
fn read(app: &dyn PubkeyAddressApp, hd_path: StandardHDPath) -> Option<(StandardHDPath, [u8; 33])> {
if let Ok(key) = app.get_extkey_at(&hd_path) {
let pub_key = key.as_pubkey();
let pub_key = pub_key.serialize();
Some((hd_path, pub_key))
} else {
None
}
}
impl TryFrom<(StandardHDPath, [u8; 33])> for HDPathFingerprint {
type Error = ();
fn try_from(value: (StandardHDPath, [u8; 33])) -> Result<Self, Self::Error> {
let mut hmac = Hmac::<Sha256>::new_from_slice(b"emerald-seed-fingerprint/pubkey-hmac-sha256").unwrap();
hmac.update(value.0.to_bytes().as_slice());
hmac.update(value.1.as_slice());
let result = hmac.finalize();
let hash = result.into_bytes();
let hash = Bytes256::try_from(hash.as_slice()).map_err(|_| ())?;
Ok(HDPathFingerprint {
value: FingerprintType::PubkeySha256(hash),
})
}
}
impl Fingerprints for LedgerSource {
fn find_fingerprints(&self) -> Result<Vec<HDPathFingerprint>, VaultError> {
let manager = LedgerKeyShared::instance().map_err(|_| VaultError::PrivateKeyUnavailable)?;
manager.find_fingerprints()
}
}
impl<LK: LedgerKey + 'static> Fingerprints for LedgerKeyShared<LK> {
fn find_fingerprints(&self) -> Result<Vec<HDPathFingerprint>, VaultError> {
let mut source = vec![];
let app = self.get_app_details()?.name;
if app.starts_with("Ethereum") {
let app = self.access::<EthereumApp>()?;
if let Some(fp) = read(&app, StandardHDPath::from_str("m/44'/60'/0'/0/0").unwrap()) {
source.push(fp)
}
if let Some(fp) = read(&app, StandardHDPath::from_str("m/44'/61'/0'/0/0").unwrap()) {
source.push(fp)
}
} else if app == "Bitcoin" {
let app = self.access::<BitcoinApp>()?;
if let Some(fp) = read(&app, StandardHDPath::from_str("m/44'/0'/0'/0/0").unwrap()) {
source.push(fp)
}
} else if app == "Bitcoin Test" {
let app = self.access::<BitcoinApp>()?;
if let Some(fp) = read(&app, StandardHDPath::from_str("m/44'/1'/0'/0/0").unwrap()) {
source.push(fp)
}
} else {
return Ok(vec![])
}
let mut result = vec![];
for s in source {
if let Ok(fp) = HDPathFingerprint::try_from(s) {
result.push(fp)
}
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use hdpath::StandardHDPath;
use crate::structs::seed::HDPathFingerprint;
#[test]
fn create_zero() {
let pk = (StandardHDPath::from_str("m/44'/60'/0'/0/0").unwrap(), [0u8; 33]);
let fingerprint = HDPathFingerprint::try_from(pk);
assert!(fingerprint.is_ok());
let fingerprint = fingerprint.unwrap();
assert_eq!(hex::encode(fingerprint.value.to_vec()), "8641f4987cdf650bc82a8f97596de5c9457c327662452cba2a1afe552e2ff170")
}
}