Compare commits

..

No commits in common. "48ccd7c68f008e9264d4ca3ab225248db5755d08" and "1a036a0b5fd4860491ee134f27ea35e34379c4bc" have entirely different histories.

8 changed files with 63 additions and 86 deletions

View File

@ -15,7 +15,7 @@
//! * Derive Key //! * Derive Key
//! //!
//! ## Extended Private Keys //! ## Extended Private Keys
//! //!
//! Keyfork doesn't need to be continuously called once a key has been derived. Once an Extended //! Keyfork doesn't need to be continuously called once a key has been derived. Once an Extended
//! Private Key (often shortened to "XPrv") has been created, further derivations can be performed. //! Private Key (often shortened to "XPrv") has been created, further derivations can be performed.
//! The tests for this library ensure that all levels of Keyfork derivation beyond the required two //! The tests for this library ensure that all levels of Keyfork derivation beyond the required two
@ -108,10 +108,6 @@ pub enum Error {
/// An error encountered in Keyforkd. /// An error encountered in Keyforkd.
#[error("Error in Keyforkd: {0}")] #[error("Error in Keyforkd: {0}")]
Keyforkd(#[from] KeyforkdError), Keyforkd(#[from] KeyforkdError),
/// An invalid key was returned.
#[error("Invalid key returned")]
InvalidKey,
} }
#[allow(missing_docs)] #[allow(missing_docs)]
@ -238,8 +234,11 @@ impl Client {
} }
let depth = path.len() as u8; let depth = path.len() as u8;
ExtendedPrivateKey::from_parts(&d.data, depth, d.chain_code) Ok(ExtendedPrivateKey::from_parts(
.map_err(|_| Error::InvalidKey) &d.data,
depth,
d.chain_code,
))
} }
_ => Err(Error::InvalidResponse), _ => Err(Error::InvalidResponse),
} }

View File

@ -27,10 +27,6 @@ pub enum Error {
/// The given slice was of an inappropriate size to create a Private Key. /// 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")] #[error("The given slice was of an inappropriate size to create a Private Key")]
InvalidSliceError(#[from] std::array::TryFromSliceError), InvalidSliceError(#[from] std::array::TryFromSliceError),
/// The given data was not a valid key for the chosen key type.
#[error("The given data was not a valid key for the chosen key type")]
InvalidKey,
} }
type Result<T, E = Error> = std::result::Result<T, E>; type Result<T, E = Error> = std::result::Result<T, E>;
@ -131,7 +127,7 @@ mod serde_with {
let bytes: [u8; 32] = variable_len_bytes.try_into().expect(bug!( let bytes: [u8; 32] = variable_len_bytes.try_into().expect(bug!(
"unable to parse serialized private key; no support for static len" "unable to parse serialized private key; no support for static len"
)); ));
Ok(K::from_bytes(&bytes).expect(bug!("could not deserialize key with invalid scalar"))) Ok(K::from_bytes(&bytes))
} }
} }
@ -152,9 +148,13 @@ where
/// Generate a new [`ExtendedPrivateKey`] from a seed, ideally from a 12-word or 24-word /// Generate a new [`ExtendedPrivateKey`] from a seed, ideally from a 12-word or 24-word
/// mnemonic, but may take 16-byte seeds. /// mnemonic, but may take 16-byte seeds.
/// ///
/// # Panics
/// The method performs unchecked `try_into()` operations on a constant-sized slice.
///
/// # Errors /// # Errors
/// The function may return an error if the derived master key could not be parsed as a key for /// An error may be returned if:
/// the given algorithm (such as exceeding values on the secp256k1 curve). /// * The given seed had an incorrect length.
/// * A `HmacSha512` can't be constructed.
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
@ -167,11 +167,11 @@ where
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed); /// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
/// ``` /// ```
pub fn new(seed: impl as_private_key::AsPrivateKey) -> Result<Self> { pub fn new(seed: impl as_private_key::AsPrivateKey) -> Self {
Self::new_internal(seed.as_private_key()) Self::new_internal(seed.as_private_key())
} }
fn new_internal(seed: &[u8]) -> Result<Self> { fn new_internal(seed: &[u8]) -> Self {
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>()) let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())
.expect(bug!("HmacSha512 InvalidLength should be infallible")) .expect(bug!("HmacSha512 InvalidLength should be infallible"))
.chain_update(seed) .chain_update(seed)
@ -179,10 +179,6 @@ 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);
// NOTE: Could potentially cause side-channel attacks, but Rust will likely optimize any
// possible comparison I could make anyways. This is kept as-is for clarity's sake, but can
// potentially leak information about the first few bytes of a key, such as if they all
// happen to be zero.
assert!( assert!(
!private_key.iter().all(|byte| *byte == 0), !private_key.iter().all(|byte| *byte == 0),
bug!("hmac function returned all-zero master key") bug!("hmac function returned all-zero master key")
@ -218,16 +214,11 @@ where
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code); /// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code);
/// ``` /// ```
pub fn from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Result<Self> { pub fn from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Self {
match K::from_bytes(key) { Self {
Ok(key) => { private_key: K::from_bytes(key),
Ok(Self { depth,
private_key: key, chain_code,
depth,
chain_code,
})
}
Err(_) => Err(Error::InvalidKey),
} }
} }
@ -241,15 +232,12 @@ where
/// # public_key::TestPublicKey as PublicKey, /// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey, /// # private_key::TestPrivateKey as PrivateKey,
/// # }; /// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = // /// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = // /// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code)?; /// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code);
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key)?); /// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
/// # Ok(())
/// # }
/// ``` /// ```
pub fn private_key(&self) -> &K { pub fn private_key(&self) -> &K {
&self.private_key &self.private_key
@ -274,7 +262,7 @@ where
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27, /// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251 /// # 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(); /// let xpub = xprv.extended_public_key();
/// assert_eq!(known_key, xpub.public_key().to_bytes()); /// assert_eq!(known_key, xpub.public_key().to_bytes());
/// # Ok(()) /// # Ok(())
@ -298,7 +286,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = // /// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed)?; /// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
/// let pubkey = xprv.public_key(); /// let pubkey = xprv.public_key();
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -316,15 +304,12 @@ where
/// # public_key::TestPublicKey as PublicKey, /// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey, /// # private_key::TestPrivateKey as PrivateKey,
/// # }; /// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = // /// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = // /// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code)?; /// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code);
/// assert_eq!(xprv.depth(), 4); /// assert_eq!(xprv.depth(), 4);
/// # Ok(())
/// # }
/// ``` /// ```
pub fn depth(&self) -> u8 { pub fn depth(&self) -> u8 {
self.depth self.depth
@ -339,15 +324,12 @@ where
/// # public_key::TestPublicKey as PublicKey, /// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey, /// # private_key::TestPrivateKey as PrivateKey,
/// # }; /// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = // /// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = // /// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; /// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code)?; /// let xprv = ExtendedPrivateKey::<PrivateKey>::from_parts(key, 4, *chain_code);
/// assert_eq!(chain_code, &xprv.chain_code()); /// assert_eq!(chain_code, &xprv.chain_code());
/// # Ok(())
/// # }
/// ``` /// ```
pub fn chain_code(&self) -> [u8; 32] { pub fn chain_code(&self) -> [u8; 32] {
self.chain_code self.chain_code
@ -369,7 +351,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = // /// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed)?; /// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
/// let path = DerivationPath::default() /// let path = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?) /// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?) /// .chain_push(DerivationIndex::new(0, true)?)
@ -415,7 +397,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = // /// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed)?; /// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
/// let bip44_wallet = DerivationPath::default() /// let bip44_wallet = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?) /// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?) /// .chain_push(DerivationIndex::new(0, true)?)

View File

@ -2,6 +2,8 @@ use crate::PublicKey;
use thiserror::Error; use thiserror::Error;
use keyfork_bug::bug;
pub(crate) type PrivateKeyBytes = [u8; 32]; pub(crate) type PrivateKeyBytes = [u8; 32];
/// Functions required to use an `ExtendedPrivateKey`. /// Functions required to use an `ExtendedPrivateKey`.
@ -24,7 +26,7 @@ pub trait PrivateKey: Sized {
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data); /// let private_key = OurPrivateKey::from_bytes(key_data);
/// ``` /// ```
fn from_bytes(b: &PrivateKeyBytes) -> Result<Self, Self::Err>; fn from_bytes(b: &PrivateKeyBytes) -> Self;
/// Convert a &Self to bytes. /// Convert a &Self to bytes.
/// ///
@ -36,7 +38,7 @@ pub trait PrivateKey: Sized {
/// # }; /// # };
/// let key_data: &[u8; 32] = // /// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data).unwrap(); /// let private_key = OurPrivateKey::from_bytes(key_data);
/// assert_eq!(key_data, &private_key.to_bytes()); /// assert_eq!(key_data, &private_key.to_bytes());
/// ``` /// ```
fn to_bytes(&self) -> PrivateKeyBytes; fn to_bytes(&self) -> PrivateKeyBytes;
@ -71,7 +73,7 @@ pub trait PrivateKey: Sized {
/// # }; /// # };
/// let key_data: &[u8; 32] = // /// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data).unwrap(); /// let private_key = OurPrivateKey::from_bytes(key_data);
/// let public_key = private_key.public_key(); /// let public_key = private_key.public_key();
/// ``` /// ```
fn public_key(&self) -> Self::PublicKey; fn public_key(&self) -> Self::PublicKey;
@ -118,8 +120,8 @@ impl PrivateKey for k256::SecretKey {
"Bitcoin seed" "Bitcoin seed"
} }
fn from_bytes(b: &PrivateKeyBytes) -> Result<Self, Self::Err> { fn from_bytes(b: &PrivateKeyBytes) -> Self {
Self::from_slice(b).map_err(|_| PrivateKeyError::InvalidScalar) Self::from_slice(b).expect(bug!("Invalid private key bytes"))
} }
fn to_bytes(&self) -> PrivateKeyBytes { fn to_bytes(&self) -> PrivateKeyBytes {
@ -157,8 +159,8 @@ impl PrivateKey for ed25519_dalek::SigningKey {
"ed25519 seed" "ed25519 seed"
} }
fn from_bytes(b: &PrivateKeyBytes) -> Result<Self, Self::Err> { fn from_bytes(b: &PrivateKeyBytes) -> Self {
Ok(Self::from_bytes(b)) Self::from_bytes(b)
} }
fn to_bytes(&self) -> PrivateKeyBytes { fn to_bytes(&self) -> PrivateKeyBytes {
@ -202,8 +204,8 @@ impl PrivateKey for TestPrivateKey {
type PublicKey = TestPublicKey; type PublicKey = TestPublicKey;
type Err = PrivateKeyError; type Err = PrivateKeyError;
fn from_bytes(b: &PrivateKeyBytes) -> Result<Self, Self::Err> { fn from_bytes(b: &PrivateKeyBytes) -> Self {
Ok(Self { key: *b }) Self { key: *b }
} }
fn to_bytes(&self) -> PrivateKeyBytes { fn to_bytes(&self) -> PrivateKeyBytes {

View File

@ -30,7 +30,7 @@ pub trait PublicKey: Sized {
/// # }; /// # };
/// let key_data: &[u8; 32] = // /// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data).unwrap(); /// let private_key = OurPrivateKey::from_bytes(key_data);
/// let public_key_bytes = private_key.public_key().to_bytes(); /// let public_key_bytes = private_key.public_key().to_bytes();
/// ``` /// ```
fn to_bytes(&self) -> PublicKeyBytes; fn to_bytes(&self) -> PublicKeyBytes;
@ -56,7 +56,7 @@ pub trait PublicKey: Sized {
/// # }; /// # };
/// let key_data: &[u8; 32] = // /// let key_data: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; /// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let private_key = OurPrivateKey::from_bytes(key_data).unwrap(); /// let private_key = OurPrivateKey::from_bytes(key_data);
/// let fingerprint = private_key.public_key().fingerprint(); /// let fingerprint = private_key.public_key().fingerprint();
/// ``` /// ```
fn fingerprint(&self) -> [u8; 4] { fn fingerprint(&self) -> [u8; 4] {

View File

@ -70,7 +70,7 @@ impl DerivationAlgorithm {
match self { match self {
#[cfg(feature = "ed25519")] #[cfg(feature = "ed25519")]
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::with_algo_and_xprv( Ok(DerivationResponse::with_algo_and_xprv(
self.clone(), self.clone(),
@ -79,7 +79,7 @@ impl DerivationAlgorithm {
} }
#[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::with_algo_and_xprv( Ok(DerivationResponse::with_algo_and_xprv(
self.clone(), self.clone(),
@ -87,7 +87,7 @@ impl DerivationAlgorithm {
)) ))
} }
Self::TestAlgorithm => { Self::TestAlgorithm => {
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?; let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed);
let derived_key = key.derive_path(path)?; let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv( Ok(DerivationResponse::with_algo_and_xprv(
self.clone(), self.clone(),
@ -300,9 +300,11 @@ mod secp256k1 {
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> { fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
match value.algorithm { match value.algorithm {
DerivationAlgorithm::Secp256k1 => { DerivationAlgorithm::Secp256k1 => Ok(Self::from_parts(
Self::from_parts(&value.data, value.depth, value.chain_code).map_err(Into::into) &value.data,
} value.depth,
value.chain_code,
)),
_ => Err(Self::Error::Algorithm), _ => Err(Self::Error::Algorithm),
} }
} }
@ -333,9 +335,11 @@ mod ed25519 {
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> { fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
match value.algorithm { match value.algorithm {
DerivationAlgorithm::Ed25519 => { DerivationAlgorithm::Ed25519 => Ok(Self::from_parts(
Self::from_parts(&value.data, value.depth, value.chain_code).map_err(Into::into) &value.data,
} value.depth,
value.chain_code,
)),
_ => Err(Self::Error::Algorithm), _ => Err(Self::Error::Algorithm),
} }
} }

View File

@ -31,7 +31,7 @@ fn secp256k1() {
// Tests for ExtendedPrivateKey // Tests for ExtendedPrivateKey
let varlen_seed = VariableLengthSeed::new(seed); let varlen_seed = VariableLengthSeed::new(seed);
let xkey = ExtendedPrivateKey::<SecretKey>::new(varlen_seed).unwrap(); let xkey = ExtendedPrivateKey::<SecretKey>::new(varlen_seed);
let derived_key = xkey.derive_path(&chain).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!( assert_eq!(
derived_key.chain_code().as_slice(), derived_key.chain_code().as_slice(),
@ -77,7 +77,7 @@ fn ed25519() {
// Tests for ExtendedPrivateKey // Tests for ExtendedPrivateKey
let varlen_seed = VariableLengthSeed::new(seed); let varlen_seed = VariableLengthSeed::new(seed);
let xkey = ExtendedPrivateKey::<SigningKey>::new(varlen_seed).unwrap(); let xkey = ExtendedPrivateKey::<SigningKey>::new(varlen_seed);
let derived_key = xkey.derive_path(&chain).unwrap(); let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!( assert_eq!(
derived_key.chain_code().as_slice(), derived_key.chain_code().as_slice(),
@ -110,7 +110,7 @@ fn panics_with_unhardened_derivation() {
use ed25519_dalek::SigningKey; use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f"); 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()) xkey.derive_path(&DerivationPath::from_str("m/0").unwrap())
.unwrap(); .unwrap();
} }
@ -122,7 +122,7 @@ fn panics_at_depth() {
use ed25519_dalek::SigningKey; use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f"); 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) { for i in 0..=u32::from(u8::MAX) {
xkey = xkey xkey = xkey
.derive_child(&DerivationIndex::new(i, true).unwrap()) .derive_child(&DerivationIndex::new(i, true).unwrap())

View File

@ -259,7 +259,6 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
let userid = UserID::from("keyfork-sss"); let userid = UserID::from("keyfork-sss");
let path = DerivationPath::from_str("m/7366512'/0'").expect(bug!("valid derivation path")); let path = DerivationPath::from_str("m/7366512'/0'").expect(bug!("valid derivation path"));
let xprv = XPrv::new(seed) let xprv = XPrv::new(seed)
.expect(bug!("could not create XPrv from key"))
.derive_path(&path) .derive_path(&path)
.expect(bug!("valid derivation")); .expect(bug!("valid derivation"));
keyfork_derive_openpgp::derive( keyfork_derive_openpgp::derive(

View File

@ -11,12 +11,11 @@ use keyfork_derive_openpgp::{
}; };
use keyfork_derive_util::{DerivationIndex, DerivationPath}; use keyfork_derive_util::{DerivationIndex, DerivationPath};
use keyfork_prompt::{ use keyfork_prompt::{
default_terminal,
validators::{SecurePinValidator, Validator}, validators::{SecurePinValidator, Validator},
DefaultTerminal, Message, PromptHandler, Message, PromptHandler, DefaultTerminal, default_terminal
}; };
use keyfork_shard::{openpgp::OpenPGP, Format}; use keyfork_shard::{Format, openpgp::OpenPGP};
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
#[error("Invalid PIN length: {0}")] #[error("Invalid PIN length: {0}")]
@ -45,9 +44,7 @@ fn derive_key(seed: [u8; 32], index: u8) -> Result<Cert> {
.chain_push(chain) .chain_push(chain)
.chain_push(account) .chain_push(account)
.chain_push(subkey); .chain_push(subkey);
let xprv = XPrv::new(seed) let xprv = XPrv::new(seed).derive_path(&path)?;
.expect("could not construct master key from seed")
.derive_path(&path)?;
let userid = UserID::from(format!("Keyfork Shard {index}")); let userid = UserID::from(format!("Keyfork Shard {index}"));
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?; let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
Ok(cert) Ok(cert)
@ -107,7 +104,7 @@ fn generate_shard_secret(
keys_per_shard: u8, keys_per_shard: u8,
output_file: &Option<PathBuf>, output_file: &Option<PathBuf>,
) -> Result<()> { ) -> Result<()> {
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?; let seed = keyfork_entropy::generate_entropy_of_const_size::<{256 / 8}>()?;
let mut pm = default_terminal()?; let mut pm = default_terminal()?;
let mut certs = vec![]; let mut certs = vec![];
let mut seen_cards: HashSet<String> = HashSet::new(); let mut seen_cards: HashSet<String> = HashSet::new();
@ -174,13 +171,7 @@ fn generate_shard_secret(
let output = File::create(output_file)?; let output = File::create(output_file)?;
opgp.shard_and_encrypt(threshold, certs.len() as u8, &seed, &certs[..], output)?; opgp.shard_and_encrypt(threshold, certs.len() as u8, &seed, &certs[..], output)?;
} else { } else {
opgp.shard_and_encrypt( opgp.shard_and_encrypt(threshold, certs.len() as u8, &seed, &certs[..], std::io::stdout())?;
threshold,
certs.len() as u8,
&seed,
&certs[..],
std::io::stdout(),
)?;
} }
Ok(()) Ok(())
} }