Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for importing raw key bytes #13

Open
BHawleyWall opened this issue Nov 4, 2024 · 1 comment
Open

Support for importing raw key bytes #13

BHawleyWall opened this issue Nov 4, 2024 · 1 comment

Comments

@BHawleyWall
Copy link

Hello - your implementation of slip-0010 is one of the few "in the wild" for Rust. I recently had need for implementing HD-keys in Rust and after exploring the options available, I liked your crate the most out of the options available. I was able to put together a decent implementation that was clean and readable thanks to your abstractions and was successful in generating and extending keys.

Unfortunately, I've reached a point where I now want to support extending keys (deriving some new step or steps, not from a seed) generated from other systems. Your ExtendedSecretKey and ExtendedKeyPair types do not seem to expose any constructors from a slice of bytes for the secret material and chain code.

Was this intentional, or would it be possible to request a way to "import" an ExtendedSecretKey from just the raw bytes for the key + chain code?

@BHawleyWall
Copy link
Author

For anyone else who stumbles upon this as part of a search, here's how I solved it for my own use-case. Note that I've cobbled this example together from various files, so it may or may not compile without tweaking imports, etc. Primarily it is meant to showcase the steps necessary.

use aes_gcm_siv::{
    aead::{Aead, KeyInit, OsRng},
    AeadCore,
    Aes256GcmSiv,
};
use anyhow::{anyhow, Result};
use generic_ec::Curve;
use hd_wallet::{
    curves::{Ed25519, Secp256k1, Secp256r1},
    ExtendedSecretKey,
};
use zeroize::{Zeroize, Zeroizing, ZeroizeOnDrop};

#[derive(Clone, PartialEq, Default, Zeroize, ZeroizeOnDrop)]
pub struct SecretMaterial(pub(crate) Vec<u8>);

// snip various impl's for the newtype

impl<T: Curve> TryFrom<&SecretMaterial> for ExtendedSecretKey<T> {
    type Error = anyhow::Error;

    fn try_from(value: &SecretMaterial) -> Result<Self> {
        let scalar = SecretScalar::from_be_bytes(value.as_bytes())?;

        Ok(Self {
            secret_key: scalar,
            chain_code: [0u8; 32],
        })
    }
}

pub fn generate_256_key() -> SecretMaterial {
    let material = Zeroizing::new(Aes256GcmSiv::generate_key(&mut OsRng));

    SecretMaterial(material.as_slice().to_vec())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // dummy setup for example
    let first_half = generate_256_key().into_vec().into_iter();
    let second_half = generate_256_key().into_vec().into_iter();
    let seed = Zeroizing::new(first_half.chain(second_half).collect::<Vec<u8>>());
    let parent = SecretMaterial::from(seed.to_vec());
    let chain_code = [1u8; 32];

    // ...because we need both the secret and the chain code, 
    // and the target type provides no help to convert from 
    // Big Endian bytes alone, we do this in two steps
    // to avoid the compilation blocker for implementing on
    // tuples for the conversion trait
    let mut parent_key = ExtendedSecretKey::<Secp256k1>::try_from(parent)?;
    parent_key.chain_code = chain_code;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant