55 lines
2.1 KiB
Rust
55 lines
2.1 KiB
Rust
use crate::error::{Error, Result};
|
|
|
|
use hmac::{Hmac, Mac};
|
|
use sha2::Sha512;
|
|
|
|
pub trait MasterKey<'a> {
|
|
/// Return the textual content used to derive the master key, as specified in BIP 0032 and SLIP
|
|
/// 0010. For example, a secp256k1 master key would use the textual content "Bitcoin seed", to
|
|
/// ensure compatibility with BIP 0032, despite the key being used for more functionality than
|
|
/// purely Bitcoin.
|
|
fn key() -> &'static str;
|
|
|
|
/// Some key algorithms, such as Ed25519, allow 0 as a valid private key. Those algorithhms
|
|
/// should override this method to indicate as such.
|
|
fn is_zero_valid_private_key() -> bool {
|
|
false
|
|
}
|
|
|
|
/// Return the seed used to derive the Master Key, with a size between 128 to 512 bits. Most
|
|
/// seeds should be 256 bits, the largest size available from a BIP-0039 mnemonic.
|
|
fn seed(&self) -> &'a [u8];
|
|
}
|
|
|
|
type HmacSha512 = Hmac<Sha512>;
|
|
|
|
/// Generate a Master Secret Key and Chain Code (what is this used for?).
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// An error may be returned if:
|
|
/// * The `HmacSha512` key returned by `T::key()` is invalid. This should never happen.
|
|
/// * The generated master key is all zeroes. This has a cosmically small chance of happening.
|
|
pub fn generate<'a, T: MasterKey<'a>>(generator: &T) -> Result<(Vec<u8>, Vec<u8>)> {
|
|
let seed = generator.seed();
|
|
let len = seed.len();
|
|
if len * 8 % 32 != 0 {
|
|
return Err(Error::BadSeedLength(len));
|
|
}
|
|
|
|
let mut hmac = HmacSha512::new_from_slice(&T::key().bytes().collect::<Vec<_>>())?;
|
|
hmac.update(seed);
|
|
let result = hmac.finalize().into_bytes();
|
|
let left = &result[..32];
|
|
let right = &result[32..];
|
|
if left.iter().all(|n| n == &0) {
|
|
// Wow. Impressive.
|
|
// NOTE: SLIP-0010 says to retry if this happens, but uses some weird terminology to do so.
|
|
// I do not trust it. BIP-0032 says this key is "invalid", with no instructions to retry.
|
|
// This is a low enough chance I am fine with it being freak-of-nature error.
|
|
return Err(Error::HashedSeedIsZero);
|
|
}
|
|
|
|
Ok((left.to_vec(), right.to_vec()))
|
|
}
|