keyfork-derive-util: add arbitrary length seeds, remove length-based errors
This commit is contained in:
parent
8108f5e61a
commit
4354be4304
|
@ -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());
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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()],
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue