You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
2.5 KiB
88 lines
2.5 KiB
use anyhow::{anyhow, Result};
|
|
use bdk::bitcoin::util::bip32::ExtendedPrivKey;
|
|
use bdk::bitcoin::Network;
|
|
use hkdf::Hkdf;
|
|
use rand::Rng;
|
|
use sha2::Sha256;
|
|
use std::convert::TryInto;
|
|
use std::path::Path;
|
|
|
|
pub struct Seed([u8; 256]);
|
|
|
|
impl Seed {
|
|
/// Initialize a [`Seed`] from a path.
|
|
/// Generates new seed if there was no seed found in the given path
|
|
pub async fn initialize(seed_file: &Path) -> Result<Seed> {
|
|
let seed = if !seed_file.exists() {
|
|
tracing::info!("No seed found. Generating new seed");
|
|
let seed = Seed::default();
|
|
seed.write_to(seed_file).await?;
|
|
seed
|
|
} else {
|
|
Seed::read_from(seed_file).await?
|
|
};
|
|
Ok(seed)
|
|
}
|
|
|
|
pub async fn read_from(path: &Path) -> Result<Self> {
|
|
let bytes = tokio::fs::read(path).await?;
|
|
|
|
let bytes = bytes
|
|
.try_into()
|
|
.map_err(|_| anyhow!("Bytes from seed file don't fit into array"))?;
|
|
|
|
Ok(Seed(bytes))
|
|
}
|
|
|
|
pub async fn write_to(&self, path: &Path) -> Result<()> {
|
|
if path.exists() {
|
|
anyhow::bail!("Refusing to overwrite file at {}", path.display())
|
|
}
|
|
|
|
tokio::fs::write(path, &self.0).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn derive_extended_priv_key(&self, network: Network) -> Result<ExtendedPrivKey> {
|
|
let mut ext_priv_key_seed = [0u8; 64];
|
|
|
|
Hkdf::<Sha256>::new(None, &self.0)
|
|
.expand(b"BITCOIN_WALLET_SEED", &mut ext_priv_key_seed)
|
|
.expect("okm array is of correct length");
|
|
|
|
let ext_priv_key = ExtendedPrivKey::new_master(network, &ext_priv_key_seed)?;
|
|
|
|
Ok(ext_priv_key)
|
|
}
|
|
|
|
pub fn derive_auth_password<P: From<[u8; 32]>>(&self) -> P {
|
|
let mut password = [0u8; 32];
|
|
|
|
Hkdf::<Sha256>::new(None, &self.0)
|
|
.expand(b"HTTP_AUTH_PASSWORD", &mut password)
|
|
.expect("okm array is of correct length");
|
|
|
|
P::from(password)
|
|
}
|
|
|
|
pub fn derive_identity(&self) -> (x25519_dalek::PublicKey, x25519_dalek::StaticSecret) {
|
|
let mut secret = [0u8; 32];
|
|
|
|
Hkdf::<Sha256>::new(None, &self.0)
|
|
.expand(b"NOISE_STATIC_SECRET", &mut secret)
|
|
.expect("okm array is of correct length");
|
|
|
|
let identity_sk = x25519_dalek::StaticSecret::from(secret);
|
|
(x25519_dalek::PublicKey::from(&identity_sk), identity_sk)
|
|
}
|
|
}
|
|
|
|
impl Default for Seed {
|
|
fn default() -> Self {
|
|
let mut seed = [0u8; 256];
|
|
rand::thread_rng().fill(&mut seed);
|
|
|
|
Self(seed)
|
|
}
|
|
}
|
|
|