use bip32::{Mnemonic, Seed, XPrv};
use rand_core::OsRng;
use x25519_dalek::{self, PublicKey, StaticSecret};
pub struct DHKey {
pub password: Option<String>,
pub seed: Seed,
pub key_path: String,
}
impl DHKey {
pub fn from(seed: Seed) -> Self {
let child_path = "m/44'/0'/0'/0/0'".to_string();
Self {
password: None,
seed: seed,
key_path: child_path,
}
}
pub fn get_seed(&self) -> Seed {
let phrase = Self::get_mnemonic();
let password = self.password.clone().unwrap_or_default();
let seed = phrase.to_seed(password.as_str());
seed
}
pub fn get_key(&self, child_path: &str) -> (StaticSecret, PublicKey) {
let child_xprv = XPrv::derive_from_path(&self.seed, &child_path.parse().unwrap()).unwrap();
let xprv = child_xprv.to_bytes();
let child_priv_key: [u8; 32] = xprv[0..32].try_into().unwrap();
let secret_key = StaticSecret::from(child_priv_key);
let public_key = PublicKey::from(&secret_key);
(secret_key, public_key)
}
pub fn get_mnemonic() -> Mnemonic {
let language = Default::default();
let mnemonic = Mnemonic::random(&mut OsRng, language);
let phrase = Mnemonic::new(mnemonic.phrase(), language);
phrase.unwrap()
}
pub fn sedd_to_dh_key(seed: [u8; 32]) -> (StaticSecret, PublicKey) {
let secret_key = StaticSecret::from(seed);
let public_key = PublicKey::from(&secret_key);
(secret_key, public_key)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::seed::get_seed;
#[test]
fn dh_flow() {
let seed1 = get_seed(None);
let seed2 = get_seed(None);
assert_ne!(seed1.as_bytes(), seed2.as_bytes());
let child_path = "m/44'/0'/0'/0/0'";
let dhk1 = DHKey::from(seed1);
let (sec1, pub1) = dhk1.get_key(child_path);
let any_path = "m/11'/0'/0'/0/0'";
let dhk2 = DHKey::from(seed2);
let (sec2, pub2) = dhk2.get_key(any_path);
assert_ne!(sec1.to_bytes(), sec2.to_bytes());
assert_ne!(pub1.to_bytes(), pub2.to_bytes());
let shard1 = x25519_dalek::x25519(sec1.to_bytes(), pub2.to_bytes());
let shard2 = x25519_dalek::x25519(sec2.to_bytes(), pub1.to_bytes());
assert_eq!(shard1, shard2);
}
}