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.
 
 

85 lines
2.4 KiB

use anyhow::{anyhow, bail, 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.
///
/// Fails if the file does not exist or it exists but would be overwritten.
pub async fn initialize(seed_file: &Path, generate: bool) -> Result<Seed> {
let exists = seed_file.exists();
let seed = match (exists, generate) {
(true, false) => Seed::read_from(seed_file).await?,
(false, true) => {
let seed = Seed::default();
seed.write_to(seed_file).await?;
seed
}
(true, true) => bail!("Refusing to overwrite seed at {}", seed_file.display()),
(false, false) => bail!("Seed file at {} does not exist", seed_file.display()),
};
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)
}
#[allow(dead_code)] // Not used by all binaries.
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)
}
}
impl Default for Seed {
fn default() -> Self {
let mut seed = [0u8; 256];
rand::thread_rng().fill(&mut seed);
Self(seed)
}
}