Compare commits
5 Commits
1879a250c8
...
5096df993e
Author | SHA1 | Date |
---|---|---|
Ryan Heywood | 5096df993e | |
Ryan Heywood | f2250d00e1 | |
Ryan Heywood | 4e66367376 | |
Ryan Heywood | aa5fde533c | |
Ryan Heywood | 2b8c90fcd5 |
|
@ -68,14 +68,27 @@ where
|
|||
/// mnemonic, but may take 16-byte seeds.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The method performs unchecked `try_into()` operations on a constant-sized slice.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error may be returned if:
|
||||
/// * The given seed had an incorrect length.
|
||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # 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(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
|
||||
Self::new_internal(seed.as_ref())
|
||||
}
|
||||
|
@ -104,6 +117,23 @@ where
|
|||
///
|
||||
/// # Errors
|
||||
/// The function may error if a private key can't be created from the seed.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # 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)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
||||
Ok(Self {
|
||||
private_key: K::from_bytes(seed.try_into()?),
|
||||
|
@ -113,26 +143,123 @@ where
|
|||
}
|
||||
|
||||
/// Returns a reference to the [`PrivateKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::PrivateKey as _,
|
||||
/// # 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)?;
|
||||
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn private_key(&self) -> &K {
|
||||
&self.private_key
|
||||
}
|
||||
|
||||
/// Create an [`ExtendedPublicKey`] for the current [`PrivateKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::PrivateKey as _,
|
||||
/// # public_key::PublicKey as _,
|
||||
/// # 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 known_key: [u8; 33] = [
|
||||
/// # 0, 242, 26, 9, 159, 68, 199, 0, 206, 71, 248,
|
||||
/// # 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 xpub = xprv.extended_public_key();
|
||||
/// assert_eq!(known_key, xpub.public_key().to_bytes());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn extended_public_key(&self) -> ExtendedPublicKey<K::PublicKey> {
|
||||
ExtendedPublicKey::new(self.public_key(), self.chain_code)
|
||||
ExtendedPublicKey::new_from_parts(self.public_key(), self.depth, self.chain_code)
|
||||
}
|
||||
|
||||
/// Return a public key for the current [`PrivateKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::PrivateKey as _,
|
||||
/// # public_key::PublicKey as _,
|
||||
/// # 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)?;
|
||||
/// let pubkey = xprv.public_key();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn public_key(&self) -> K::PublicKey {
|
||||
self.private_key.public_key()
|
||||
}
|
||||
|
||||
/// Returns the current depth.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # 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)?;
|
||||
/// assert_eq!(xprv.depth(), 4);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn depth(&self) -> u8 {
|
||||
self.depth
|
||||
}
|
||||
|
||||
/// Returns a copy of the current chain code.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # 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)?;
|
||||
/// assert_eq!(chain_code, &xprv.chain_code());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn chain_code(&self) -> [u8; 32] {
|
||||
self.chain_code
|
||||
}
|
||||
|
@ -140,9 +267,29 @@ where
|
|||
/// Derive a child using the given [`DerivationPath`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error may be returned under the same circumstances as
|
||||
/// [`ExtendedPrivateKey::derive_child`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # 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 root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||
/// let path = DerivationPath::default()
|
||||
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, false)?);
|
||||
/// let derived_xprv = root_xprv.derive_path(&path)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> {
|
||||
if path.path.is_empty() {
|
||||
Ok(self.clone())
|
||||
|
@ -165,6 +312,33 @@ where
|
|||
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||
/// * Deriving a child key fails. Check the documentation for your [`PrivateKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # private_key::TestPrivateKey as PrivateKey,
|
||||
/// # };
|
||||
/// # fn check_empty(p: &ExtendedPrivateKey<PrivateKey>) -> Result<(), std::io::Error> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let seed: &[u8; 64] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||
/// let bip44_wallet = DerivationPath::default()
|
||||
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, false)?);
|
||||
/// let change_xprv = root_xprv.derive_path(&bip44_wallet)?;
|
||||
/// for account in (0..20).map(|i| DerivationIndex::new(i, false).unwrap()) {
|
||||
/// let account_xprv = change_xprv.derive_child(&account)?;
|
||||
/// check_empty(&account_xprv)?;
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
||||
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
||||
|
||||
|
|
|
@ -44,15 +44,51 @@ where
|
|||
K: PublicKey,
|
||||
{
|
||||
/// Create a new [`ExtendedPublicKey`] from previously known values.
|
||||
pub fn new(public_key: K, chain_code: ChainCode) -> Self {
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # public_key::PublicKey as _,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let key: &[u8; 33] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let chain_code: &[u8; 32] = //
|
||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
/// let pubkey = PublicKey::from_bytes(key);
|
||||
/// let xpub = ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new_from_parts(public_key: K, depth: u8, chain_code: ChainCode) -> Self {
|
||||
Self {
|
||||
public_key,
|
||||
depth: 0,
|
||||
depth,
|
||||
chain_code,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the internal [`PublicKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # public_key::PublicKey as _,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
/// # let pubkey = PublicKey::from_bytes(key);
|
||||
/// let xpub = //
|
||||
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||
/// let pubkey = xpub.public_key();
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn public_key(&self) -> &K {
|
||||
&self.public_key
|
||||
}
|
||||
|
@ -70,6 +106,25 @@ where
|
|||
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||
/// * Deriving a child key fails. Check the documentation for your [`PublicKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # public_key::PublicKey as _,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||
/// # let pubkey = PublicKey::from_bytes(key);
|
||||
/// let xpub = //
|
||||
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||
/// let index = DerivationIndex::new(0, false)?;
|
||||
/// let child = xpub.derive_child(&index)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
||||
if index.is_hardened() {
|
||||
return Err(Error::HardenedIndex);
|
||||
|
|
|
@ -23,8 +23,20 @@ impl DerivationIndex {
|
|||
/// Creates a new [`DerivationIndex`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the index is larger than the hardened flag.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::*;
|
||||
/// let bip44 = DerivationIndex::new(44, true);
|
||||
/// ```
|
||||
///
|
||||
/// Using a derivation index that is higher than 2^31 returns an error:
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// # use keyfork_derive_util::*;
|
||||
/// let too_high = DerivationIndex::new(u32::MAX, true).unwrap();
|
||||
/// ```
|
||||
pub const fn new(index: u32, hardened: bool) -> Result<Self> {
|
||||
if index & (0b1 << 31) > 0 {
|
||||
return Err(Error::IndexTooLarge(index));
|
||||
|
@ -46,6 +58,13 @@ impl DerivationIndex {
|
|||
|
||||
/// Return the internal derivation index. Note that if the derivation index is hardened, the
|
||||
/// highest bit will be set, and the value can't be used to create a new derivation index.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::*;
|
||||
/// assert_eq!(DerivationIndex::new(44, true).unwrap().inner(), 2147483692);
|
||||
/// assert_eq!(DerivationIndex::new(200, false).unwrap().inner(), 200);
|
||||
/// ```
|
||||
pub fn inner(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
@ -54,7 +73,15 @@ impl DerivationIndex {
|
|||
self.0.to_be_bytes()
|
||||
}
|
||||
|
||||
/// Whether or not the index is hardened, allowing deriving the key from a known parent key.
|
||||
/// Whether or not the index is hardened, allowing deriving the key from a known parent public
|
||||
/// key.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::*;
|
||||
/// assert_eq!(DerivationIndex::new(0, true).unwrap().is_hardened(), true);
|
||||
/// assert_eq!(DerivationIndex::new(0, false).unwrap().is_hardened(), false);
|
||||
/// ```
|
||||
pub fn is_hardened(&self) -> bool {
|
||||
self.0 & (0b1 << 31) != 0
|
||||
}
|
||||
|
|
|
@ -52,6 +52,24 @@ impl DerivationPath {
|
|||
}
|
||||
|
||||
/// Append an index to the path, returning self to allow chaining method calls.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::*;
|
||||
/// # fn discover_wallet(_p: DerivationPath) -> Result<bool, std::io::Error> { Ok(true) }
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let account = 0;
|
||||
/// let path = DerivationPath::default()
|
||||
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||
/// .chain_push(DerivationIndex::new(account, true)?);
|
||||
/// let mut has_wallet = false;
|
||||
/// for index in (0..20).map(|i| DerivationIndex::new(i, true).unwrap()) {
|
||||
/// has_wallet = has_wallet || discover_wallet(path.clone().chain_push(index))?;
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn chain_push(mut self, index: DerivationIndex) -> Self {
|
||||
self.path.push(index);
|
||||
self
|
||||
|
|
|
@ -13,9 +13,32 @@ pub trait PrivateKey: Sized {
|
|||
type Err: std::error::Error;
|
||||
|
||||
/// Create a Self from bytes.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// let key_data: &[u8; 32] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||
/// ```
|
||||
fn from_bytes(b: &PrivateKeyBytes) -> Self;
|
||||
|
||||
/// Convert a &Self to bytes.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// let key_data: &[u8; 32] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||
/// assert_eq!(key_data, &private_key.to_bytes());
|
||||
/// ```
|
||||
fn to_bytes(&self) -> PrivateKeyBytes;
|
||||
|
||||
/*
|
||||
|
@ -27,12 +50,35 @@ pub trait PrivateKey: Sized {
|
|||
*/
|
||||
|
||||
/// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed".
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// assert_eq!(OurPrivateKey::key(), "testing seed");
|
||||
/// ```
|
||||
fn key() -> &'static str;
|
||||
|
||||
/// Generate a [`Self::PublicKey`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// let key_data: &[u8; 32] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||
/// let public_key = private_key.public_key();
|
||||
/// ```
|
||||
fn public_key(&self) -> Self::PublicKey;
|
||||
|
||||
/// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`.
|
||||
/// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`. The implementation of
|
||||
/// derivation is algorithm-specific and a specification should be consulted when implementing
|
||||
/// this method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
@ -129,3 +175,48 @@ impl PrivateKey for ed25519_dalek::SigningKey {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
use crate::public_key::TestPublicKey;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct TestPrivateKey {
|
||||
key: [u8; 32],
|
||||
}
|
||||
|
||||
impl TestPrivateKey {
|
||||
pub(crate) fn public_key(&self) -> TestPublicKey {
|
||||
let mut bytes = [0u8; 33];
|
||||
for (i, byte) in self.key.iter().enumerate() {
|
||||
bytes[i + 1] = byte ^ 0xFF;
|
||||
}
|
||||
TestPublicKey { key: bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey for TestPrivateKey {
|
||||
type PublicKey = TestPublicKey;
|
||||
type Err = PrivateKeyError;
|
||||
|
||||
fn from_bytes(b: &PrivateKeyBytes) -> Self {
|
||||
Self {
|
||||
key: *b
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> PrivateKeyBytes {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn key() -> &'static str {
|
||||
"testing seed"
|
||||
}
|
||||
|
||||
fn public_key(&self) -> Self::PublicKey {
|
||||
self.public_key()
|
||||
}
|
||||
|
||||
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err> {
|
||||
Ok(Self { key: *other })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,23 @@ pub trait PublicKey: Sized {
|
|||
*/
|
||||
|
||||
/// Convert a &Self to bytes.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// let key_data: &[u8; 32] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||
/// let public_key_bytes = private_key.public_key().to_bytes();
|
||||
/// ```
|
||||
fn to_bytes(&self) -> PublicKeyBytes;
|
||||
|
||||
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`.
|
||||
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`. The implementation of
|
||||
/// derivation is algorithm-specific and a specification should be consulted when implementing
|
||||
/// this method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
@ -31,6 +45,18 @@ pub trait PublicKey: Sized {
|
|||
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
|
||||
|
||||
/// Create a BIP-0032/SLIP-0010 fingerprint from the public key.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||
/// # };
|
||||
/// let key_data: &[u8; 32] = //
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||
/// let fingerprint = private_key.public_key().fingerprint();
|
||||
/// ```
|
||||
fn fingerprint(&self) -> [u8; 4] {
|
||||
let hash = Sha256::new().chain_update(self.to_bytes()).finalize();
|
||||
let hash = Ripemd160::new().chain_update(hash).finalize();
|
||||
|
@ -112,3 +138,32 @@ impl PublicKey for VerifyingKey {
|
|||
Err(Self::Err::DerivationUnsupported)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone)]
|
||||
pub struct TestPublicKey {
|
||||
pub(crate) key: [u8; 33],
|
||||
}
|
||||
|
||||
impl TestPublicKey {
|
||||
#[doc(hidden)]
|
||||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self {
|
||||
key: b.try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl PublicKey for TestPublicKey {
|
||||
type Err = PublicKeyError;
|
||||
|
||||
fn to_bytes(&self) -> PublicKeyBytes {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn derive_child(&self, _other: PrivateKeyBytes) -> Result<Self, Self::Err> {
|
||||
// whatever it takes for tests to pass...
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#![allow(clippy::match_wildcard_for_single_variants)]
|
||||
|
||||
use crate::{
|
||||
extended_key::private_key::Error as XPrvError, DerivationPath, ExtendedPrivateKey, PrivateKey,
|
||||
extended_key::private_key::Error as XPrvError,
|
||||
private_key::{PrivateKey, TestPrivateKey},
|
||||
DerivationPath, ExtendedPrivateKey,
|
||||
};
|
||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -30,11 +32,14 @@ pub type Result<T, E = DerivationError> = std::result::Result<T, E>;
|
|||
/// The algorithm to derive a key for. The choice of algorithm will result in a different resulting
|
||||
/// derivation.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum DerivationAlgorithm {
|
||||
#[allow(missing_docs)]
|
||||
Ed25519,
|
||||
#[allow(missing_docs)]
|
||||
Secp256k1,
|
||||
#[doc(hidden)]
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl DerivationAlgorithm {
|
||||
|
@ -42,7 +47,7 @@ impl DerivationAlgorithm {
|
|||
///
|
||||
/// # Errors
|
||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||
pub fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
|
||||
fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
|
||||
match self {
|
||||
#[cfg(feature = "ed25519")]
|
||||
Self::Ed25519 => {
|
||||
|
@ -62,6 +67,14 @@ impl DerivationAlgorithm {
|
|||
&derived_key,
|
||||
))
|
||||
}
|
||||
Self::Internal => {
|
||||
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
|
||||
let derived_key = key.derive_path(path)?;
|
||||
Ok(DerivationResponse::with_algo_and_xprv(
|
||||
self.clone(),
|
||||
&derived_key,
|
||||
))
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => Err(DerivationError::Algorithm),
|
||||
}
|
||||
|
@ -89,6 +102,23 @@ pub struct DerivationRequest {
|
|||
|
||||
impl DerivationRequest {
|
||||
/// Create a new derivation request.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # request::*,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # private_key::TestPrivateKey as PrivateKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let algo: DerivationAlgorithm = //
|
||||
/// # DerivationAlgorithm::Internal;
|
||||
/// let path: DerivationPath = //
|
||||
/// # DerivationPath::default();
|
||||
/// let request = DerivationRequest::new(algo, &path);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn new(algorithm: DerivationAlgorithm, path: &DerivationPath) -> Self {
|
||||
Self {
|
||||
algorithm,
|
||||
|
@ -97,6 +127,24 @@ impl DerivationRequest {
|
|||
}
|
||||
|
||||
/// Return the path of the derivation request.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # request::*,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # private_key::TestPrivateKey as PrivateKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let algo: DerivationAlgorithm = //
|
||||
/// # DerivationAlgorithm::Internal;
|
||||
/// let path: DerivationPath = //
|
||||
/// # DerivationPath::default();
|
||||
/// let request = DerivationRequest::new(algo, &path);
|
||||
/// assert_eq!(&path, request.path());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn path(&self) -> &DerivationPath {
|
||||
&self.path
|
||||
}
|
||||
|
@ -105,6 +153,29 @@ impl DerivationRequest {
|
|||
///
|
||||
/// # Errors
|
||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # request::*,
|
||||
/// # public_key::TestPublicKey as PublicKey,
|
||||
/// # private_key::TestPrivateKey as PrivateKey,
|
||||
/// # };
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mnemonic: keyfork_mnemonic_util::Mnemonic = //
|
||||
/// # keyfork_mnemonic_util::Mnemonic::from_entropy(
|
||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
/// # Default::default(),
|
||||
/// # )?;
|
||||
/// let algo: DerivationAlgorithm = //
|
||||
/// # DerivationAlgorithm::Internal;
|
||||
/// let path: DerivationPath = //
|
||||
/// # DerivationPath::default();
|
||||
/// let request = DerivationRequest::new(algo, &path);
|
||||
/// let response = request.derive_with_mnemonic(&mnemonic)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
||||
// TODO: passphrase support and/or store passphrase within mnemonic
|
||||
self.derive_with_master_seed(mnemonic.seed(None)?)
|
||||
|
@ -114,6 +185,26 @@ impl DerivationRequest {
|
|||
///
|
||||
/// # Errors
|
||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use keyfork_derive_util::{
|
||||
/// # *,
|
||||
/// # request::*,
|
||||
/// # 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 algo: DerivationAlgorithm = //
|
||||
/// # DerivationAlgorithm::Internal;
|
||||
/// let path: DerivationPath = //
|
||||
/// # DerivationPath::default();
|
||||
/// let request = DerivationRequest::new(algo, &path);
|
||||
/// let response = request.derive_with_master_seed(seed.to_vec())?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> {
|
||||
self.algorithm.derive(seed, &self.path)
|
||||
}
|
||||
|
@ -137,7 +228,7 @@ pub struct DerivationResponse {
|
|||
|
||||
impl DerivationResponse {
|
||||
/// Create a [`DerivationResponse`] with the given values.
|
||||
pub fn with_algo_and_xprv<T: PrivateKey + Clone>(
|
||||
fn with_algo_and_xprv<T: PrivateKey + Clone>(
|
||||
algorithm: DerivationAlgorithm,
|
||||
xprv: &ExtendedPrivateKey<T>,
|
||||
) -> Self {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# Keyfork: The Kitchen Sink of Entropy
|
||||
|
||||
**Note:** Keyfork operations are meant to be run on an airgapped machine and
|
||||
Keyfork will error if either any network interfaces are detected or if Keyfork
|
||||
is running on a system with a kernel using an insecure random number generator.
|
||||
|
||||
An all-inclusive crate encapsulating end-user functionality of the Keyfork
|
||||
ecosystem, the Keyfork binary includes all mechanisms that should be exposed to
|
||||
the user when running Keyfork. Information about what operations Keyfork
|
||||
performs are available in detail by running `keyfork help` (each subcommand has
|
||||
thorough documentation) or in the [`docs`] mdBook, but here's a quick overview:
|
||||
|
||||
## Getting Started with Keyfork
|
||||
|
||||
Keyfork offers two options for getting started. For multi-user setups, it is
|
||||
best to look at the detailed documentation for Keyfork Shard. For single-user
|
||||
setups, `keyfork mnemonic generate` will (by default) create a 256-bit mnemonic
|
||||
phrase that can be used to start Keyfork. *Store this phrase*, as it's the only
|
||||
way you'll be able to start Keyfork in the future. It is recommended to use a
|
||||
mnemonic recovery sheet or a printed-steel solution such as the [Billfodl] or
|
||||
[Cryptosteel Capsule].
|
||||
|
||||
```sh
|
||||
keyfork mnemonic generate
|
||||
```
|
||||
|
||||
Once a mnemonic has been generated and stored in a secure manner, Keyfork can
|
||||
be started by "recovering" the server from the mnemonic backup mechanism:
|
||||
|
||||
```sh
|
||||
keyfork recover mnemonic
|
||||
```
|
||||
|
||||
## Deriving Keys
|
||||
|
||||
Keyfork's primary goal is to derive keys. These keys can later be used for
|
||||
things such as signing documents and artifacts or decrypting payloads.
|
||||
Keyfork's first derivation target is OpenPGP, a protocol supporting many
|
||||
cryptographic operations. OpenPGP keys require a User ID, which can be used to
|
||||
identify the owner of the key, either by name or by email. To get an OpenPGP
|
||||
public key (more accurately known as a "cert"), the [`sq`][sq] tool is used to
|
||||
convert a key to a certificate:
|
||||
|
||||
```sh
|
||||
keyfork derive openpgp "John Doe <jdoe@example.com>" | sq key extract-cert
|
||||
```
|
||||
|
||||
All Keyfork derivations are intended to be reproducible. Because of this,
|
||||
Keyfork derived keys can be recreated at any time, only requiring the knowledge
|
||||
of how the key was made.
|
||||
|
||||
[`docs`]: /public/keyfork/src/branch/main/docs/src/SUMMARY.md
|
||||
[Billfodl]: https://privacypros.io/products/the-billfodl/
|
||||
[Cryptosteel Capsule]: https://cryptosteel.com/product/cryptosteel-capsule-solo/
|
||||
[sq]: https://gitlab.com/sequoia-pgp/sequoia-sq/
|
|
@ -19,6 +19,9 @@ type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
|||
pub enum DeriveSubcommands {
|
||||
/// Derive an OpenPGP Transferable Secret Key (private key). The key is encoded using OpenPGP
|
||||
/// ASCII Armor, a format usable by most programs using OpenPGP.
|
||||
///
|
||||
/// The key is generated with a 24-hour expiration time. The operation to set the expiration
|
||||
/// time to a higher value is left to the user to ensure the key is usable by the user.
|
||||
#[command(name = "openpgp")]
|
||||
OpenPGP {
|
||||
/// Default User ID for the certificate, using the OpenPGP User ID format.
|
||||
|
@ -72,6 +75,10 @@ pub struct Derive {
|
|||
command: DeriveSubcommands,
|
||||
|
||||
/// Account ID. Required for all derivations.
|
||||
///
|
||||
/// An account ID may not be relevant for the derivation being performed, but the lack of an
|
||||
/// account ID can often come as a hindrance in the future. As such, it is always required. If
|
||||
/// the account ID is not relevant, it is assumed to be `0`.
|
||||
#[arg(long, global = true, default_value = "0")]
|
||||
account_id: u32,
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ mod wizard;
|
|||
|
||||
/// The Kitchen Sink of Entropy.
|
||||
#[derive(Parser, Clone, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(author, version, about, long_about)]
|
||||
pub struct Keyfork {
|
||||
// Global options
|
||||
#[command(subcommand)]
|
||||
|
@ -20,25 +20,51 @@ pub struct Keyfork {
|
|||
pub enum KeyforkCommands {
|
||||
/// Derive keys of various formats. These commands require that the Keyfork server is running,
|
||||
/// which can be started by running a `keyfork recover` command.
|
||||
///
|
||||
/// Derived keys are reproducible: assuming the same arguments are used when deriving a key for
|
||||
/// a second time, the key will be _functionally_ equivalent. This means keys don't need to be
|
||||
/// persisted to cold storage or left hot in a running program. They can be derived when
|
||||
/// they're needed and forgotten when they're not.
|
||||
Derive(derive::Derive),
|
||||
|
||||
/// Mnemonic generation and persistence utilities.
|
||||
Mnemonic(mnemonic::Mnemonic),
|
||||
|
||||
/// Splitting and combining secrets, using Shamir's Secret Sharing.
|
||||
///
|
||||
/// Keys can be split such that a certain amount of users, from a potentially even-larger
|
||||
/// amount of users, can be used to recreate a key. This creates resilience for a key, as in a
|
||||
/// "seven of nine" scenario, nine people in total are capable of recreating a key, but only
|
||||
/// seven may be required.
|
||||
Shard(shard::Shard),
|
||||
|
||||
/// Derive and deploy keys to hardware.
|
||||
///
|
||||
/// Keys existing in hardware creates a situation where it is unlikely (but not impossible) for
|
||||
/// a key to be extracted. While a key in memory could be captured by a rootkit or some other
|
||||
/// privilege escalation mechanism, a key in hardware would require a hardware exploit to
|
||||
/// extract the key.
|
||||
///
|
||||
/// It is recommended to provision keys whenever possible, as opposed to deriving them.
|
||||
#[command(subcommand_negates_reqs(true))]
|
||||
Provision(provision::Provision),
|
||||
|
||||
/// Recover a seed using the requested recovery mechanism and start the Keyfork server.
|
||||
///
|
||||
/// Once the Keyfork server is started, derivation requests can be performed. The Keyfork seed
|
||||
/// is kept solely in the Keyfork server. Derivations with less than two indices are not
|
||||
/// permitted, to ensure a seed often used to derive keys for multiple different paths is not
|
||||
/// leaked by any individual deriver.
|
||||
Recover(recover::Recover),
|
||||
|
||||
/// Utilities to automatically manage the setup of Keyfork.
|
||||
Wizard(wizard::Wizard),
|
||||
|
||||
/// Print an autocompletion file to standard output.
|
||||
///
|
||||
/// Keyfork does not manage the installation of completion files. Consult the documentation for
|
||||
/// the shell for which documentation has been generated on the appropriate location to store
|
||||
/// completion files.
|
||||
#[cfg(feature = "completion")]
|
||||
Completion {
|
||||
#[arg(value_enum)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![doc = include_str!("../../../README.md")]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
/// The top left cell is represented as `(0, 0)`.
|
||||
///
|
||||
/// On unix systems, this function will block and possibly time out while
|
||||
/// [`crossterm::event::read`](crate::event::read) or [`crossterm::event::poll`](crate::event::poll) are being called.
|
||||
/// [`crossterm::event::read`](crate::event::read()) or [`crossterm::event::poll`](crate::event::poll) are being called.
|
||||
pub fn position() -> io::Result<(u16, u16)> {
|
||||
if is_raw_mode_enabled() {
|
||||
read_position_raw()
|
||||
|
|
|
@ -170,7 +170,7 @@ pub fn available_color_count() -> u16 {
|
|||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// crossterm supports NO_COLOR (https://no-color.org/) to disabled colored output.
|
||||
/// crossterm supports NO_COLOR (<https://no-color.org/>) to disabled colored output.
|
||||
///
|
||||
/// This API allows applications to override that behavior and force colorized output
|
||||
/// even if NO_COLOR is set.
|
||||
|
|
|
@ -71,7 +71,7 @@ impl Colored {
|
|||
}
|
||||
|
||||
/// Checks whether ansi color sequences are disabled by setting of NO_COLOR
|
||||
/// in environment as per https://no-color.org/
|
||||
/// in environment as per <https://no-color.org/>
|
||||
pub fn ansi_color_disabled() -> bool {
|
||||
!std::env::var("NO_COLOR")
|
||||
.unwrap_or("".to_string())
|
||||
|
|
|
@ -199,7 +199,7 @@ pub struct WindowSize {
|
|||
/// Returns the terminal size `[WindowSize]`.
|
||||
///
|
||||
/// The width and height in pixels may not be reliably implemented or default to 0.
|
||||
/// For unix, https://man7.org/linux/man-pages/man4/tty_ioctl.4.html documents them as "unused".
|
||||
/// For unix, <https://man7.org/linux/man-pages/man4/tty_ioctl.4.html> documents them as "unused".
|
||||
/// For windows it is not implemented.
|
||||
pub fn window_size() -> io::Result<WindowSize> {
|
||||
sys::window_size()
|
||||
|
|
|
@ -144,7 +144,7 @@ pub(crate) fn disable_raw_mode() -> io::Result<()> {
|
|||
/// Queries the terminal's support for progressive keyboard enhancement.
|
||||
///
|
||||
/// On unix systems, this function will block and possibly time out while
|
||||
/// [`crossterm::event::read`](crate::event::read) or [`crossterm::event::poll`](crate::event::poll) are being called.
|
||||
/// [`crossterm::event::read`](crate::event::read()) or [`crossterm::event::poll`](crate::event::poll) are being called.
|
||||
#[cfg(feature = "events")]
|
||||
pub fn supports_keyboard_enhancement() -> io::Result<bool> {
|
||||
if is_raw_mode_enabled() {
|
||||
|
|
|
@ -48,6 +48,12 @@ fn ensure_offline() {
|
|||
}
|
||||
|
||||
/// Ensure the system is safe.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # std::env::set_var("SHOOT_SELF_IN_FOOT", "1");
|
||||
/// keyfork_entropy::ensure_safe();
|
||||
/// ```
|
||||
pub fn ensure_safe() {
|
||||
if !std::env::vars()
|
||||
.any(|(name, _)| name == "SHOOT_SELF_IN_FOOT" || name == "INSECURE_HARDWARE_ALLOWED")
|
||||
|
@ -61,6 +67,16 @@ pub fn ensure_safe() {
|
|||
///
|
||||
/// # Errors
|
||||
/// An error may be returned if an error occurred while reading from the random source.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # std::env::set_var("SHOOT_SELF_IN_FOOT", "1");
|
||||
/// let entropy = keyfork_entropy::generate_entropy_of_size(64)?;
|
||||
/// assert_eq!(entropy.len(), 64);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn generate_entropy_of_size(byte_count: usize) -> Result<Vec<u8>, std::io::Error> {
|
||||
ensure_safe();
|
||||
let mut vec = vec![0u8; byte_count];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! SLIP-0010 test data for use by derivation tests.
|
||||
//! Source: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vectors
|
||||
//! Source: <https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vectors>
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
|
Loading…
Reference in New Issue