keyfork-derive-util: add arbitrary length seeds, remove length-based errors

This commit is contained in:
Ryan Heywood 2024-02-11 20:35:26 -05:00
parent 8108f5e61a
commit 4354be4304
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
5 changed files with 29 additions and 50 deletions

View File

@ -10,18 +10,10 @@ const KEY_SIZE: usize = 256;
/// Errors associated with creating or deriving Extended Private Keys.
#[derive(Error, Clone, Debug)]
pub enum Error {
/// The seed has an unsuitable length; supported lengths are 16 bytes, 32 bytes, or 64 bytes.
#[error("Seed had an unsuitable length: {0}")]
BadSeedLength(usize),
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
#[error("Reached maximum depth for key derivation")]
Depth,
/// This should never happen. HMAC keys should be able to take any size input.
#[error("Invalid length for HMAC key while generating master key (report me!)")]
HmacInvalidLength(#[from] hmac::digest::InvalidLength),
/// An unknown error occurred while deriving a child key.
#[error("Unknown error while deriving child key")]
Derivation,
@ -98,7 +90,7 @@ where
/// # Errors
/// An error may be returned if:
/// * The given seed had an incorrect length.
/// * A `HmacSha512` can't be constructed - this should be impossible.
/// * A `HmacSha512` can't be constructed.
///
/// # Examples
/// ```rust
@ -107,37 +99,30 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// # Ok(())
/// # }
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// ```
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
pub fn new(seed: impl AsRef<[u8]>) -> Self {
Self::new_internal(seed.as_ref())
}
fn new_internal(seed: &[u8]) -> Result<Self> {
let len = seed.len();
if ![16, 32, 64].contains(&len) {
return Err(Error::BadSeedLength(len));
}
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())?
fn new_internal(seed: &[u8]) -> Self {
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())
.expect("HmacSha512 InvalidLength should be infallible")
.chain_update(seed)
.finalize()
.into_bytes();
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
Ok(Self::new_from_parts(
Self::new_from_parts(
private_key
.try_into()
.expect("KEY_SIZE / 8 did not give a 32 byte slice"),
0,
// Checked: chain_code is always the same length, hash is static size
chain_code.try_into().expect("Invalid chain code length"),
))
)
}
/// Create an [`ExtendedPrivateKey`] from a given `seed`, `depth`, and `chain_code`.
@ -160,7 +145,7 @@ where
/// ```
pub fn new_from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Self {
Self {
private_key: K::from_bytes(&key),
private_key: K::from_bytes(key),
depth,
chain_code,
}
@ -206,7 +191,7 @@ where
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251
/// # ];
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let xpub = xprv.extended_public_key();
/// assert_eq!(known_key, xpub.public_key().to_bytes());
/// # Ok(())
@ -230,7 +215,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let pubkey = xprv.public_key();
/// # Ok(())
/// # }
@ -248,15 +233,12 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// assert_eq!(xprv.depth(), 4);
/// # Ok(())
/// # }
/// ```
pub fn depth(&self) -> u8 {
self.depth
@ -271,15 +253,12 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// assert_eq!(chain_code, &xprv.chain_code());
/// # Ok(())
/// # }
/// ```
pub fn chain_code(&self) -> [u8; 32] {
self.chain_code
@ -301,7 +280,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let path = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
@ -347,7 +326,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let bip44_wallet = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
@ -363,8 +342,8 @@ where
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
let mut hmac =
HmacSha512::new_from_slice(&self.chain_code).map_err(Error::HmacInvalidLength)?;
let mut hmac = HmacSha512::new_from_slice(&self.chain_code)
.expect("HmacSha512 InvalidLength should be infallible");
if index.is_hardened() {
hmac.update(&[0]);
hmac.update(&self.private_key.to_bytes());

View File

@ -68,7 +68,7 @@ impl DerivationAlgorithm {
match self {
#[cfg(feature = "ed25519")]
Self::Ed25519 => {
let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed)?;
let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
@ -77,7 +77,7 @@ impl DerivationAlgorithm {
}
#[cfg(feature = "secp256k1")]
Self::Secp256k1 => {
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
@ -85,7 +85,7 @@ impl DerivationAlgorithm {
))
}
Self::Internal => {
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),

View File

@ -30,7 +30,7 @@ fn secp256k1() {
} = test;
// Tests for ExtendedPrivateKey
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed);
let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(
derived_key.chain_code().as_slice(),
@ -51,7 +51,7 @@ fn secp256k1() {
// Tests for DerivationRequest
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
let response = request.derive_with_master_seed(seed.clone()).unwrap();
assert_eq!(&response.data, private_key, "test: {chain}");
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
}
}
}
@ -75,7 +75,7 @@ fn ed25519() {
} = test;
// Tests for ExtendedPrivateKey
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(
derived_key.chain_code().as_slice(),
@ -96,7 +96,7 @@ fn ed25519() {
// Tests for DerivationRequest
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
assert_eq!(&response.data, private_key, "test: {chain}");
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
}
}
}
@ -108,7 +108,7 @@ fn panics_with_unhardened_derivation() {
use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f");
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
xkey.derive_path(&DerivationPath::from_str("m/0").unwrap())
.unwrap();
}
@ -120,7 +120,7 @@ fn panics_at_depth() {
use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f");
let mut xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let mut xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
for i in 0..=u32::from(u8::MAX) {
xkey = xkey
.derive_child(&DerivationIndex::new(i, true).unwrap())

View File

@ -648,7 +648,7 @@ pub fn combine(
// TODO: extract as function
let userid = UserID::from("keyfork-sss");
let path = DerivationPath::from_str("m/7366512'/0'")?;
let xprv = XPrv::new(&secret)?.derive_path(&path)?;
let xprv = XPrv::new(&secret).derive_path(&path)?;
let derived_cert = keyfork_derive_openpgp::derive(
xprv,
&[KeyFlags::empty().set_certification().set_signing()],
@ -682,7 +682,7 @@ pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write)
// build cert to sign encrypted shares
let userid = UserID::from("keyfork-sss");
let path = DerivationPath::from_str("m/7366512'/0'")?;
let xprv = XPrv::new(&secret)?.derive_path(&path)?;
let xprv = XPrv::new(&secret).derive_path(&path)?;
let derived_cert = keyfork_derive_openpgp::derive(
xprv,
&[KeyFlags::empty().set_certification().set_signing()],

View File

@ -42,7 +42,7 @@ fn derive_key(seed: &[u8], index: u8) -> Result<Cert> {
.chain_push(chain)
.chain_push(account)
.chain_push(subkey);
let xprv = XPrv::new(seed)?.derive_path(&path)?;
let xprv = XPrv::new(seed).derive_path(&path)?;
let userid = UserID::from(format!("Keyfork Shard {index}"));
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
Ok(cert)