keyfork-derive-util: fixup request API, publicity of ExtendedPrivateKey
This commit is contained in:
parent
8510e382d2
commit
72666011a4
|
@ -29,6 +29,10 @@ pub enum Error {
|
||||||
/// The algorithm used mandates hardened derivation only.
|
/// The algorithm used mandates hardened derivation only.
|
||||||
#[error("The algorithm used mandates hardened derivation only")]
|
#[error("The algorithm used mandates hardened derivation only")]
|
||||||
HardenedDerivationRequired,
|
HardenedDerivationRequired,
|
||||||
|
|
||||||
|
/// The given slice was of an inappropriate size to create a Private Key.
|
||||||
|
#[error("The given slice was of an inappropriate size to create a Private Key")]
|
||||||
|
InvalidSliceError(#[from] std::array::TryFromSliceError),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
@ -41,9 +45,9 @@ type HmacSha512 = Hmac<Sha512>;
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
||||||
/// The internal private key data.
|
/// The internal private key data.
|
||||||
pub private_key: K,
|
private_key: K,
|
||||||
depth: u8,
|
depth: u8,
|
||||||
pub(crate) chain_code: ChainCode,
|
chain_code: ChainCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
|
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
|
||||||
|
@ -88,18 +92,42 @@ where
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
||||||
|
|
||||||
|
Self::new_from_parts(
|
||||||
|
private_key,
|
||||||
|
0,
|
||||||
|
// Checked: chain_code is always the same length, hash is static size
|
||||||
|
chain_code.try_into().expect("Invalid chain code length"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
private_key: K::from_bytes(private_key.try_into().expect("Invalid key length")),
|
private_key: K::from_bytes(seed.try_into()?),
|
||||||
depth: 0,
|
depth,
|
||||||
chain_code: chain_code.try_into().expect("Invalid chain code length"),
|
chain_code,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the [`PrivateKey`].
|
||||||
|
pub fn private_key(&self) -> &K {
|
||||||
|
&self.private_key
|
||||||
|
}
|
||||||
|
|
||||||
/// Return a public key for the current [`PrivateKey`].
|
/// Return a public key for the current [`PrivateKey`].
|
||||||
pub fn public_key(&self) -> K::PublicKey {
|
pub fn public_key(&self) -> K::PublicKey {
|
||||||
self.private_key.public_key()
|
self.private_key.public_key()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current depth.
|
||||||
|
pub fn depth(&self) -> u8 {
|
||||||
|
self.depth
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the current chain code.
|
||||||
|
pub fn chain_code(&self) -> [u8; 32] {
|
||||||
|
self.chain_code
|
||||||
|
}
|
||||||
|
|
||||||
/// Derive a child using the given [`DerivationPath`].
|
/// Derive a child using the given [`DerivationPath`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -34,6 +34,14 @@ impl DerivationPath {
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> {
|
pub fn iter(&self) -> impl Iterator<Item = &DerivationIndex> {
|
||||||
self.path.iter()
|
self.path.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.path.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.path.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for DerivationPath {
|
impl std::str::FromStr for DerivationPath {
|
||||||
|
|
|
@ -28,19 +28,19 @@ impl DerivationAlgorithm {
|
||||||
Self::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)?;
|
let derived_key = key.derive_path(path)?;
|
||||||
Ok(DerivationResponse {
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
algorithm: self.clone(),
|
self.clone(),
|
||||||
data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(),
|
&derived_key,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Self::Secp256k1 => {
|
Self::Secp256k1 => {
|
||||||
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
|
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
|
||||||
let derived_key = key.derive_path(path)?;
|
let derived_key = key.derive_path(path)?;
|
||||||
Ok(DerivationResponse {
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
algorithm: self.clone(),
|
self.clone(),
|
||||||
data: PrivateKey::to_bytes(&derived_key.private_key).to_vec(),
|
&derived_key,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => Err(DerivationError::Algorithm),
|
_ => Err(DerivationError::Algorithm),
|
||||||
|
@ -59,6 +59,10 @@ impl DerivationRequest {
|
||||||
Self { algorithm, path }
|
Self { algorithm, path }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> &DerivationPath {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
||||||
self.derive_with_master_seed(mnemonic.seed())
|
self.derive_with_master_seed(mnemonic.seed())
|
||||||
}
|
}
|
||||||
|
@ -72,4 +76,20 @@ impl DerivationRequest {
|
||||||
pub struct DerivationResponse {
|
pub struct DerivationResponse {
|
||||||
pub algorithm: DerivationAlgorithm,
|
pub algorithm: DerivationAlgorithm,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
pub chain_code: [u8; 32],
|
||||||
|
pub depth: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerivationResponse {
|
||||||
|
pub fn with_algo_and_xprv<T: PrivateKey + Clone>(
|
||||||
|
algorithm: DerivationAlgorithm,
|
||||||
|
xprv: &ExtendedPrivateKey<T>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
algorithm,
|
||||||
|
data: PrivateKey::to_bytes(xprv.private_key()).to_vec(),
|
||||||
|
chain_code: xprv.chain_code(),
|
||||||
|
depth: xprv.depth(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ fn secp256k1() {
|
||||||
for (seed, chain, chain_code, private_key, public_key) in tests {
|
for (seed, chain, chain_code, private_key, public_key) in tests {
|
||||||
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
|
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
|
||||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||||
assert_eq!(derived_key.chain_code, chain_code);
|
assert_eq!(derived_key.chain_code(), chain_code);
|
||||||
assert_eq!(derived_key.private_key.to_bytes().as_slice(), private_key);
|
assert_eq!(derived_key.private_key().to_bytes().as_slice(), private_key);
|
||||||
assert_eq!(derived_key.public_key().to_bytes(), public_key);
|
assert_eq!(derived_key.public_key().to_bytes(), public_key);
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, chain);
|
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, chain);
|
||||||
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
|
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
|
||||||
|
@ -103,8 +103,8 @@ fn ed25519() {
|
||||||
for (seed, chain, chain_code, private_key, public_key) in tests {
|
for (seed, chain, chain_code, private_key, public_key) in tests {
|
||||||
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
|
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
|
||||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||||
assert_eq!(derived_key.chain_code, chain_code);
|
assert_eq!(derived_key.chain_code(), chain_code);
|
||||||
assert_eq!(PrivateKey::to_bytes(&derived_key.private_key), private_key);
|
assert_eq!(PrivateKey::to_bytes(derived_key.private_key()), private_key);
|
||||||
assert_eq!(PublicKey::to_bytes(&derived_key.public_key()), public_key);
|
assert_eq!(PublicKey::to_bytes(&derived_key.public_key()), public_key);
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, chain);
|
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, chain);
|
||||||
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
|
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
|
||||||
|
|
Loading…
Reference in New Issue