Fearless Conversions #27
|
@ -47,7 +47,7 @@ fn secp256k1() {
|
||||||
);
|
);
|
||||||
let response =
|
let response =
|
||||||
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
||||||
assert_eq!(response.data, test.private_key);
|
assert_eq!(&response.data, test.private_key.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.abort();
|
handle.abort();
|
||||||
|
@ -92,7 +92,7 @@ fn ed25519() {
|
||||||
);
|
);
|
||||||
let response =
|
let response =
|
||||||
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
|
||||||
assert_eq!(response.data, test.private_key);
|
assert_eq!(&response.data, test.private_key.as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.abort();
|
handle.abort();
|
||||||
|
|
|
@ -76,7 +76,7 @@ impl Service<Request> for Keyforkd {
|
||||||
info!("Deriving path: {}", req.path());
|
info!("Deriving path: {}", req.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
req.derive_with_master_seed((*seed).clone())
|
req.derive_with_master_seed(seed.as_ref())
|
||||||
.map(Response::Derivation)
|
.map(Response::Derivation)
|
||||||
.map_err(|e| DerivationError::Derivation(e.to_string()).into())
|
.map_err(|e| DerivationError::Derivation(e.to_string()).into())
|
||||||
}),
|
}),
|
||||||
|
@ -120,7 +120,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response.data, test.private_key);
|
assert_eq!(&response.data, test.private_key.as_slice());
|
||||||
assert_eq!(response.chain_code.as_slice(), test.chain_code);
|
assert_eq!(response.chain_code.as_slice(), test.chain_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response.data, test.private_key);
|
assert_eq!(&response.data, test.private_key.as_slice());
|
||||||
assert_eq!(response.chain_code.as_slice(), test.chain_code);
|
assert_eq!(response.chain_code.as_slice(), test.chain_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let key1 = request.derive_with_mnemonic(&mnemonic)?;
|
let key1 = request.derive_with_mnemonic(&mnemonic)?;
|
||||||
|
|
||||||
let seed = mnemonic.seed(None)?;
|
let seed = mnemonic.seed(None)?;
|
||||||
let key2 = request.derive_with_master_seed(seed)?;
|
let key2 = request.derive_with_master_seed(&seed)?;
|
||||||
|
|
||||||
assert_eq!(key1, key2);
|
assert_eq!(key1, key2);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,68 @@ type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
type ChainCode = [u8; 32];
|
type ChainCode = [u8; 32];
|
||||||
type HmacSha512 = Hmac<Sha512>;
|
type HmacSha512 = Hmac<Sha512>;
|
||||||
|
|
||||||
|
/// A reference to a variable-length seed. Keyfork automatically supports a seed of 128 bits,
|
||||||
|
/// 256 bits, or 512 bits, but because the master key is derived from a hashed seed, in theory
|
||||||
|
/// any amount of bytes could be used. It is not advised to use a variable-length seed longer
|
||||||
|
/// than 256 bits, as a brute-force attack on the master key could be performed in 2^256
|
||||||
|
/// attempts.
|
||||||
|
///
|
||||||
|
/// Mnemonics use a 512 bit seed, as knowledge of the mnemonics' words (such as through a side
|
||||||
|
/// channel attack) could leak which individual word is used, but not the order the words are
|
||||||
|
/// used in. Using a 512 bit hash to generate the seed results in a more computationally
|
||||||
|
/// expensive brute-force requirement.
|
||||||
|
pub struct VariableLengthSeed<'a> {
|
||||||
|
seed: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VariableLengthSeed<'a> {
|
||||||
|
/// Create a new VariableLengthSeed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use sha2::{Sha256, Digest};
|
||||||
|
/// use keyfork_derive_util::VariableLengthSeed;
|
||||||
|
///
|
||||||
|
/// let data = b"the missile is very eepy and wants to take a small sleeb";
|
||||||
|
/// let seed = VariableLengthSeed::new(data);
|
||||||
|
/// ```
|
||||||
|
pub fn new(seed: &'a [u8]) -> Self {
|
||||||
|
Self { seed }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod as_private_key {
|
||||||
|
use super::VariableLengthSeed;
|
||||||
|
|
||||||
|
pub trait AsPrivateKey {
|
||||||
|
fn as_private_key(&self) -> &[u8];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPrivateKey for [u8; 16] {
|
||||||
|
fn as_private_key(&self) -> &[u8] {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPrivateKey for [u8; 32] {
|
||||||
|
fn as_private_key(&self) -> &[u8] {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPrivateKey for [u8; 64] {
|
||||||
|
fn as_private_key(&self) -> &[u8] {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsPrivateKey for VariableLengthSeed<'_> {
|
||||||
|
fn as_private_key(&self) -> &[u8] {
|
||||||
|
self.seed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extended private keys derived using BIP-0032.
|
/// Extended private keys derived using BIP-0032.
|
||||||
///
|
///
|
||||||
/// Generic over types implementing [`PrivateKey`].
|
/// Generic over types implementing [`PrivateKey`].
|
||||||
|
@ -101,10 +163,10 @@ where
|
||||||
/// # };
|
/// # };
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(seed: impl AsRef<[u8]>) -> Self {
|
pub fn new(seed: impl as_private_key::AsPrivateKey) -> Self {
|
||||||
Self::new_internal(seed.as_ref())
|
Self::new_internal(seed.as_private_key())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_internal(seed: &[u8]) -> Self {
|
fn new_internal(seed: &[u8]) -> Self {
|
||||||
|
@ -191,7 +253,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(())
|
||||||
|
@ -215,7 +277,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(())
|
||||||
/// # }
|
/// # }
|
||||||
|
@ -280,7 +342,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)?)
|
||||||
|
@ -326,7 +388,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)?)
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub mod public_key;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use crate::extended_key::{private_key::ExtendedPrivateKey, public_key::ExtendedPublicKey};
|
pub use crate::extended_key::{private_key::{ExtendedPrivateKey, Error as XPrvError, VariableLengthSeed}, public_key::{ExtendedPublicKey, Error as XPubError}};
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
index::{DerivationIndex, Error as IndexError},
|
index::{DerivationIndex, Error as IndexError},
|
||||||
|
|
|
@ -19,10 +19,11 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extended_key::private_key::Error as XPrvError,
|
extended_key::private_key::{Error as XPrvError, VariableLengthSeed},
|
||||||
private_key::{PrivateKey, TestPrivateKey},
|
private_key::{PrivateKey, TestPrivateKey},
|
||||||
DerivationPath, ExtendedPrivateKey,
|
DerivationPath, ExtendedPrivateKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -64,7 +65,8 @@ impl DerivationAlgorithm {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||||
fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
|
fn derive(&self, seed: &[u8], path: &DerivationPath) -> Result<DerivationResponse> {
|
||||||
|
let seed = VariableLengthSeed::new(seed);
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
Self::Ed25519 => {
|
Self::Ed25519 => {
|
||||||
|
@ -207,7 +209,7 @@ impl DerivationRequest {
|
||||||
/// # }
|
/// # }
|
||||||
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
||||||
// TODO: passphrase support and/or store passphrase within mnemonic
|
// TODO: passphrase support and/or store passphrase within mnemonic
|
||||||
self.derive_with_master_seed(mnemonic.seed(None)?)
|
self.derive_with_master_seed(&mnemonic.seed(None)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive an [`ExtendedPrivateKey`] using the given seed.
|
/// Derive an [`ExtendedPrivateKey`] using the given seed.
|
||||||
|
@ -231,10 +233,10 @@ impl DerivationRequest {
|
||||||
/// let path: DerivationPath = //
|
/// let path: DerivationPath = //
|
||||||
/// # DerivationPath::default();
|
/// # DerivationPath::default();
|
||||||
/// let request = DerivationRequest::new(algo, &path);
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
/// let response = request.derive_with_master_seed(seed.to_vec())?;
|
/// let response = request.derive_with_master_seed(seed)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> {
|
pub fn derive_with_master_seed(&self, seed: &[u8]) -> Result<DerivationResponse> {
|
||||||
self.algorithm.derive(seed, &self.path)
|
self.algorithm.derive(seed, &self.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ fn secp256k1() {
|
||||||
} = test;
|
} = test;
|
||||||
|
|
||||||
// Tests for ExtendedPrivateKey
|
// Tests for ExtendedPrivateKey
|
||||||
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed);
|
let varlen_seed = VariableLengthSeed::new(&seed);
|
||||||
|
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(),
|
||||||
|
@ -50,7 +51,7 @@ fn secp256k1() {
|
||||||
|
|
||||||
// Tests for DerivationRequest
|
// Tests for DerivationRequest
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
||||||
let response = request.derive_with_master_seed(seed.clone()).unwrap();
|
let response = request.derive_with_master_seed(&seed).unwrap();
|
||||||
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,8 @@ fn ed25519() {
|
||||||
} = test;
|
} = test;
|
||||||
|
|
||||||
// Tests for ExtendedPrivateKey
|
// Tests for ExtendedPrivateKey
|
||||||
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
|
let varlen_seed = VariableLengthSeed::new(&seed);
|
||||||
|
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(),
|
||||||
|
@ -95,7 +97,7 @@ fn ed25519() {
|
||||||
|
|
||||||
// Tests for DerivationRequest
|
// Tests for DerivationRequest
|
||||||
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).unwrap();
|
||||||
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,10 @@ license = "AGPL-3.0-only"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["openpgp", "openpgp-card", "qrcode"]
|
default = ["openpgp", "openpgp-card", "qrcode", "sequoia-openpgp/crypto-nettle", "keyfork-qrcode/decode-backend-rqrr"]
|
||||||
openpgp = ["sequoia-openpgp", "anyhow"]
|
openpgp = ["sequoia-openpgp", "anyhow"]
|
||||||
openpgp-card = ["openpgp-card-sequoia", "card-backend-pcsc", "card-backend", "dep:openpgp-card"]
|
openpgp-card = ["openpgp-card-sequoia", "card-backend-pcsc", "card-backend", "dep:openpgp-card"]
|
||||||
qrcode = ["keyfork-qrcode"]
|
qrcode = ["keyfork-qrcode"]
|
||||||
bin = ["sequoia-openpgp/crypto-nettle", "keyfork-qrcode/decode-backend-rqrr"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyfork-prompt = { version = "0.1.0", path = "../util/keyfork-prompt", default-features = false, features = ["mnemonic"] }
|
keyfork-prompt = { version = "0.1.0", path = "../util/keyfork-prompt", default-features = false, features = ["mnemonic"] }
|
||||||
|
|
|
@ -14,7 +14,7 @@ use aes_gcm::{
|
||||||
};
|
};
|
||||||
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
derive_util::{DerivationPath, PathError},
|
derive_util::{DerivationPath, PathError, VariableLengthSeed},
|
||||||
XPrv,
|
XPrv,
|
||||||
};
|
};
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
||||||
|
@ -648,7 +648,8 @@ pub fn combine(
|
||||||
// TODO: extract as function
|
// TODO: extract as function
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
let xprv = XPrv::new(&secret).derive_path(&path)?;
|
let seed = VariableLengthSeed::new(&secret);
|
||||||
|
let xprv = XPrv::new(seed).derive_path(&path)?;
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
xprv,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
|
@ -679,10 +680,11 @@ pub fn combine(
|
||||||
/// The function may panic if the metadata can't properly store the certificates used to generate
|
/// The function may panic if the metadata can't properly store the certificates used to generate
|
||||||
/// the encrypted shares.
|
/// the encrypted shares.
|
||||||
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
||||||
|
let seed = VariableLengthSeed::new(secret);
|
||||||
// build cert to sign encrypted shares
|
// build cert to sign encrypted shares
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
let xprv = XPrv::new(&secret).derive_path(&path)?;
|
let xprv = XPrv::new(seed).derive_path(&path)?;
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
xprv,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct PinLength(usize);
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
|
||||||
fn derive_key(seed: &[u8], index: u8) -> Result<Cert> {
|
fn derive_key(seed: [u8; 32], index: u8) -> Result<Cert> {
|
||||||
let subkeys = vec![
|
let subkeys = vec![
|
||||||
KeyFlags::empty().set_certification(),
|
KeyFlags::empty().set_certification(),
|
||||||
KeyFlags::empty().set_signing(),
|
KeyFlags::empty().set_signing(),
|
||||||
|
@ -102,7 +102,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_size(256 / 8)?;
|
let seed = keyfork_entropy::generate_entropy_of_const_size::<{256 / 8}>()?;
|
||||||
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
||||||
let mut certs = vec![];
|
let mut certs = vec![];
|
||||||
let mut seen_cards: HashSet<String> = HashSet::new();
|
let mut seen_cards: HashSet<String> = HashSet::new();
|
||||||
|
@ -126,7 +126,7 @@ fn generate_shard_secret(
|
||||||
.to_fn();
|
.to_fn();
|
||||||
|
|
||||||
for index in 0..max {
|
for index in 0..max {
|
||||||
let cert = derive_key(&seed, index)?;
|
let cert = derive_key(seed, index)?;
|
||||||
for i in 0..keys_per_shard {
|
for i in 0..keys_per_shard {
|
||||||
pm.prompt_message(Message::Text(format!(
|
pm.prompt_message(Message::Text(format!(
|
||||||
"Please remove all keys and insert key #{} for user #{}",
|
"Please remove all keys and insert key #{} for user #{}",
|
||||||
|
|
|
@ -7,7 +7,7 @@ license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["bin"]
|
||||||
bin = ["smex"]
|
bin = ["smex"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
//! Utilities for reading entropy from secure sources.
|
//! Utilities for reading entropy from secure sources.
|
||||||
|
|
||||||
use std::{fs::{read_dir, read_to_string, File}, io::Read};
|
use std::{
|
||||||
|
fs::{read_dir, read_to_string, File},
|
||||||
|
io::Read,
|
||||||
|
};
|
||||||
|
|
||||||
static WARNING_LINKS: [&str; 1] =
|
static WARNING_LINKS: [&str; 1] =
|
||||||
["https://lore.kernel.org/lkml/20211223141113.1240679-2-Jason@zx2c4.com/"];
|
["https://lore.kernel.org/lkml/20211223141113.1240679-2-Jason@zx2c4.com/"];
|
||||||
|
@ -84,3 +87,24 @@ pub fn generate_entropy_of_size(byte_count: usize) -> Result<Vec<u8>, std::io::E
|
||||||
entropy_file.read_exact(&mut vec[..])?;
|
entropy_file.read_exact(&mut vec[..])?;
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read system entropy of a constant size.
|
||||||
|
///
|
||||||
|
/// # 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_const_size::<64>()?;
|
||||||
|
/// assert_eq!(entropy.len(), 64);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn generate_entropy_of_const_size<const N: usize>() -> Result<[u8; N], std::io::Error> {
|
||||||
|
let mut output = [0u8; N];
|
||||||
|
let mut entropy_file = File::open("/dev/urandom")?;
|
||||||
|
entropy_file.read_exact(&mut output[..])?;
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["bin"]
|
||||||
bin = ["smex"]
|
bin = ["smex"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -276,7 +276,7 @@ impl Mnemonic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone the existing entropy.
|
/// Clone the existing entropy.
|
||||||
#[deprecated]
|
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
||||||
pub fn entropy(&self) -> Vec<u8> {
|
pub fn entropy(&self) -> Vec<u8> {
|
||||||
self.entropy.clone()
|
self.entropy.clone()
|
||||||
}
|
}
|
||||||
|
@ -353,8 +353,8 @@ mod tests {
|
||||||
random_handle.read_exact(&mut entropy[..]).unwrap();
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
||||||
let wordlist = Wordlist::default().arc();
|
let wordlist = Wordlist::default().arc();
|
||||||
let mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8], wordlist).unwrap();
|
let mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8], wordlist).unwrap();
|
||||||
let new_entropy = mnemonic.entropy();
|
let new_entropy = mnemonic.as_bytes();
|
||||||
assert_eq!(&new_entropy, entropy);
|
assert_eq!(new_entropy, entropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -22,8 +22,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
3,
|
3,
|
||||||
transport_validator.to_fn(),
|
transport_validator.to_fn(),
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(mnemonics[0].entropy().len(), 12);
|
assert_eq!(mnemonics[0].as_bytes().len(), 12);
|
||||||
assert_eq!(mnemonics[1].entropy().len(), 32);
|
assert_eq!(mnemonics[1].as_bytes().len(), 32);
|
||||||
|
|
||||||
let mnemonics = mgr.prompt_validated_wordlist(
|
let mnemonics = mgr.prompt_validated_wordlist(
|
||||||
"Enter a 24 and 48-word mnemonic: ",
|
"Enter a 24 and 48-word mnemonic: ",
|
||||||
|
@ -31,8 +31,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
3,
|
3,
|
||||||
combine_validator.to_fn(),
|
combine_validator.to_fn(),
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(mnemonics[0].entropy().len(), 32);
|
assert_eq!(mnemonics[0].as_bytes().len(), 32);
|
||||||
assert_eq!(mnemonics[1].entropy().len(), 64);
|
assert_eq!(mnemonics[1].as_bytes().len(), 64);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue