Compare commits
4 Commits
08a66e2365
...
4da3eadcca
Author | SHA1 | Date |
---|---|---|
Ryan Heywood | 4da3eadcca | |
Ryan Heywood | ffaf087fd9 | |
Ryan Heywood | f957b489b9 | |
Ryan Heywood | 249c743791 |
|
@ -1764,7 +1764,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-derive-util"
|
name = "keyfork-derive-util"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
@ -1782,7 +1782,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-entropy"
|
name = "keyfork-entropy"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"keyfork-bug",
|
"keyfork-bug",
|
||||||
"smex",
|
"smex",
|
||||||
|
@ -1824,7 +1824,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-qrcode"
|
name = "keyfork-qrcode"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
"keyfork-bug",
|
"keyfork-bug",
|
||||||
|
@ -1886,7 +1886,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyforkd"
|
name = "keyforkd"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
|
|
@ -25,9 +25,6 @@ fn secp256k1_test_suite() {
|
||||||
if chain_len < 2 {
|
if chain_len < 2 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if chain.iter().take(2).any(|index| !index.is_hardened()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Consistency check: ensure the server and the client can each derive the same
|
// Consistency check: ensure the server and the client can each derive the same
|
||||||
// key using an XPrv, for all but the last XPrv, which is verified after this
|
// key using an XPrv, for all but the last XPrv, which is verified after this
|
||||||
for i in 2..chain_len {
|
for i in 2..chain_len {
|
||||||
|
|
|
@ -43,10 +43,6 @@ pub enum DerivationError {
|
||||||
#[error("Invalid derivation length: Expected at least 2, actual: {0}")]
|
#[error("Invalid derivation length: Expected at least 2, actual: {0}")]
|
||||||
InvalidDerivationLength(usize),
|
InvalidDerivationLength(usize),
|
||||||
|
|
||||||
/// The derivation request did not use hardened derivation on the 2 highest indexes.
|
|
||||||
#[error("Invalid derivation paths: expected index #{0} (1) to be hardened")]
|
|
||||||
InvalidDerivationPath(usize, u32),
|
|
||||||
|
|
||||||
/// An error occurred while deriving data.
|
/// An error occurred while deriving data.
|
||||||
#[error("Derivation error: {0}")]
|
#[error("Derivation error: {0}")]
|
||||||
Derivation(String),
|
Derivation(String),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyforkd"
|
name = "keyforkd"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|
||||||
|
|
|
@ -69,18 +69,6 @@ impl Service<Request> for Keyforkd {
|
||||||
return Err(DerivationError::InvalidDerivationLength(len).into());
|
return Err(DerivationError::InvalidDerivationLength(len).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((i, unhardened_index)) = req
|
|
||||||
.path()
|
|
||||||
.iter()
|
|
||||||
.take(2)
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, index)| {
|
|
||||||
!index.is_hardened()
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return Err(DerivationError::InvalidDerivationPath(i, unhardened_index.inner()).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
if let Some(target) = guess_target(req.path()) {
|
if let Some(target) = guess_target(req.path()) {
|
||||||
info!("Deriving path: {target}");
|
info!("Deriving path: {target}");
|
||||||
|
@ -122,9 +110,6 @@ mod tests {
|
||||||
if chain.len() < 2 {
|
if chain.len() < 2 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if chain.iter().take(2).any(|index| !index.is_hardened()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let req = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
let req = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
||||||
let response: DerivationResponse = keyforkd
|
let response: DerivationResponse = keyforkd
|
||||||
.ready()
|
.ready()
|
||||||
|
|
|
@ -61,7 +61,7 @@ where
|
||||||
));
|
));
|
||||||
let socket_dir = tempfile::tempdir().expect(bug!("can't create tempdir"));
|
let socket_dir = tempfile::tempdir().expect(bug!("can't create tempdir"));
|
||||||
let socket_path = socket_dir.path().join("keyforkd.sock");
|
let socket_path = socket_dir.path().join("keyforkd.sock");
|
||||||
let result = rt.block_on(async move {
|
rt.block_on(async move {
|
||||||
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
||||||
let server_handle = tokio::spawn({
|
let server_handle = tokio::spawn({
|
||||||
let socket_path = socket_path.clone();
|
let socket_path = socket_path.clone();
|
||||||
|
@ -87,13 +87,8 @@ where
|
||||||
let result = test_handle.await;
|
let result = test_handle.await;
|
||||||
server_handle.abort();
|
server_handle.abort();
|
||||||
result
|
result
|
||||||
});
|
})
|
||||||
if let Err(e) = result {
|
.expect(bug!("runtime could not join all threads"))
|
||||||
if e.is_panic() {
|
|
||||||
std::panic::resume_unwind(e.into_panic());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork-derive-util"
|
name = "keyfork-derive-util"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ const KEY_SIZE: usize = 256;
|
||||||
/// Errors associated with creating or deriving Extended Public Keys.
|
/// Errors associated with creating or deriving Extended Public Keys.
|
||||||
#[derive(Error, Clone, Debug)]
|
#[derive(Error, Clone, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// BIP-0032 does not support hardened public key derivation from parent public keys.
|
/// BIP-0032 does not support deriving public keys from hardened private keys.
|
||||||
#[error("Hardened child public keys may not be derived from parent public keys")]
|
#[error("Public keys may not be derived when hardened")]
|
||||||
HardenedIndex,
|
HardenedIndex,
|
||||||
|
|
||||||
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
|
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub trait PrivateKey: Sized {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// An error may be returned if:
|
/// An error may be returned if:
|
||||||
/// * An all-zero `other` is provided.
|
/// * A nonzero `other` is provided.
|
||||||
/// * An error specific to the given algorithm was encountered.
|
/// * An error specific to the given algorithm was encountered.
|
||||||
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err>;
|
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err>;
|
||||||
|
|
||||||
|
@ -180,6 +180,7 @@ impl PrivateKey for ed25519_dalek::SigningKey {
|
||||||
|
|
||||||
use crate::public_key::TestPublicKey;
|
use crate::public_key::TestPublicKey;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct TestPrivateKey {
|
pub struct TestPrivateKey {
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub trait PublicKey: Sized {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// An error may be returned if:
|
/// An error may be returned if:
|
||||||
/// * An all-zero `other` is provided.
|
/// * A nonzero `other` is provided.
|
||||||
/// * An error specific to the given algorithm was encountered.
|
/// * An error specific to the given algorithm was encountered.
|
||||||
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
|
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
|
||||||
|
|
||||||
|
@ -142,6 +142,7 @@ impl PublicKey for VerifyingKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TestPublicKey {
|
pub struct TestPublicKey {
|
||||||
pub(crate) key: [u8; 33],
|
pub(crate) key: [u8; 33],
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub enum DerivationAlgorithm {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
Secp256k1,
|
Secp256k1,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
TestAlgorithm,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerivationAlgorithm {
|
impl DerivationAlgorithm {
|
||||||
|
@ -86,7 +86,7 @@ impl DerivationAlgorithm {
|
||||||
&derived_key,
|
&derived_key,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Self::TestAlgorithm => {
|
Self::Internal => {
|
||||||
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(
|
||||||
|
@ -120,7 +120,7 @@ pub trait AsAlgorithm: PrivateKey {
|
||||||
|
|
||||||
impl AsAlgorithm for TestPrivateKey {
|
impl AsAlgorithm for TestPrivateKey {
|
||||||
fn as_algorithm() -> DerivationAlgorithm {
|
fn as_algorithm() -> DerivationAlgorithm {
|
||||||
DerivationAlgorithm::TestAlgorithm
|
DerivationAlgorithm::Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ impl DerivationRequest {
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let algo: DerivationAlgorithm = //
|
/// let algo: DerivationAlgorithm = //
|
||||||
/// # DerivationAlgorithm::TestAlgorithm;
|
/// # DerivationAlgorithm::Internal;
|
||||||
/// let path: DerivationPath = //
|
/// let path: DerivationPath = //
|
||||||
/// # DerivationPath::default();
|
/// # DerivationPath::default();
|
||||||
/// let request = DerivationRequest::new(algo, &path);
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
@ -169,7 +169,7 @@ impl DerivationRequest {
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let algo: DerivationAlgorithm = //
|
/// let algo: DerivationAlgorithm = //
|
||||||
/// # DerivationAlgorithm::TestAlgorithm;
|
/// # DerivationAlgorithm::Internal;
|
||||||
/// let path: DerivationPath = //
|
/// let path: DerivationPath = //
|
||||||
/// # DerivationPath::default();
|
/// # DerivationPath::default();
|
||||||
/// let request = DerivationRequest::new(algo, &path);
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
@ -199,7 +199,7 @@ impl DerivationRequest {
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
/// # )?;
|
/// # )?;
|
||||||
/// let algo: DerivationAlgorithm = //
|
/// let algo: DerivationAlgorithm = //
|
||||||
/// # DerivationAlgorithm::TestAlgorithm;
|
/// # DerivationAlgorithm::Internal;
|
||||||
/// let path: DerivationPath = //
|
/// let path: DerivationPath = //
|
||||||
/// # DerivationPath::default();
|
/// # DerivationPath::default();
|
||||||
/// let request = DerivationRequest::new(algo, &path);
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
@ -228,7 +228,7 @@ impl DerivationRequest {
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let algo: DerivationAlgorithm = //
|
/// let algo: DerivationAlgorithm = //
|
||||||
/// # DerivationAlgorithm::TestAlgorithm;
|
/// # DerivationAlgorithm::Internal;
|
||||||
/// let path: DerivationPath = //
|
/// let path: DerivationPath = //
|
||||||
/// # DerivationPath::default();
|
/// # DerivationPath::default();
|
||||||
/// let request = DerivationRequest::new(algo, &path);
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
|
|
@ -84,23 +84,13 @@ impl<P: PromptHandler> VerificationHelper for &mut Keyring<P> {
|
||||||
aead_algo,
|
aead_algo,
|
||||||
} => {}
|
} => {}
|
||||||
MessageLayer::SignatureGroup { results } => {
|
MessageLayer::SignatureGroup { results } => {
|
||||||
match &results[..] {
|
|
||||||
[Ok(_)] => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// FIXME: anyhow leak: VerificationError impl std::error::Error
|
|
||||||
// return Err(e.context("Invalid signature"));
|
|
||||||
return Err(anyhow::anyhow!("Error validating signature; either multiple signatures were passed or the single signature was not valid"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
for result in results {
|
for result in results {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
|
// FIXME: anyhow leak: VerificationError impl std::error::Error
|
||||||
|
// return Err(e.context("Invalid signature"));
|
||||||
return Err(anyhow::anyhow!("Invalid signature: {e}"));
|
return Err(anyhow::anyhow!("Invalid signature: {e}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,23 +193,12 @@ impl<P: PromptHandler> VerificationHelper for &mut SmartcardManager<P> {
|
||||||
aead_algo,
|
aead_algo,
|
||||||
} => {}
|
} => {}
|
||||||
MessageLayer::SignatureGroup { results } => {
|
MessageLayer::SignatureGroup { results } => {
|
||||||
match &results[..] {
|
|
||||||
[Ok(_)] => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// FIXME: anyhow leak: VerificationError impl std::error::Error
|
|
||||||
// return Err(e.context("Invalid signature"));
|
|
||||||
return Err(anyhow::anyhow!("Error validating signature; either multiple signatures were passed or the single signature was not valid"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
for result in results {
|
for result in results {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
return Err(anyhow::anyhow!("Invalid signature: {e}"));
|
// FIXME: anyhow leak
|
||||||
|
return Err(anyhow::anyhow!("Verification error: {}", e.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,8 +264,8 @@ impl<P: PromptHandler> DecryptionHelper for &mut SmartcardManager<P> {
|
||||||
} else {
|
} else {
|
||||||
format!("Unlock card {card_id} ({cardholder_name})\n{rpea}: {attempts}\n\nPIN: ")
|
format!("Unlock card {card_id} ({cardholder_name})\n{rpea}: {attempts}\n\nPIN: ")
|
||||||
};
|
};
|
||||||
let temp_pin = self
|
let temp_pin =
|
||||||
.pm
|
self.pm
|
||||||
.lock()
|
.lock()
|
||||||
.expect(bug!(POISONED_MUTEX))
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork-qrcode"
|
name = "keyfork-qrcode"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
repository = "https://git.distrust.co/public/keyfork"
|
repository = "https://git.distrust.co/public/keyfork"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -5,7 +5,7 @@ use keyfork_bug as bug;
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use std::{
|
use std::{
|
||||||
io::{Cursor, Write},
|
io::{Cursor, Write},
|
||||||
time::{Duration, Instant},
|
time::{Duration, SystemTime},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
use v4l::{
|
use v4l::{
|
||||||
|
@ -110,10 +110,11 @@ pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QR
|
||||||
fmt.fourcc = FourCC::new(b"MPG1");
|
fmt.fourcc = FourCC::new(b"MPG1");
|
||||||
device.set_format(&fmt)?;
|
device.set_format(&fmt)?;
|
||||||
let mut stream = Stream::with_buffers(&device, Type::VideoCapture, 4)?;
|
let mut stream = Stream::with_buffers(&device, Type::VideoCapture, 4)?;
|
||||||
let start = Instant::now();
|
let start = SystemTime::now();
|
||||||
|
|
||||||
while Instant::now()
|
while SystemTime::now()
|
||||||
.duration_since(start)
|
.duration_since(start)
|
||||||
|
.unwrap_or(Duration::from_secs(0))
|
||||||
< timeout
|
< timeout
|
||||||
{
|
{
|
||||||
let (buffer, _) = stream.next()?;
|
let (buffer, _) = stream.next()?;
|
||||||
|
@ -140,11 +141,12 @@ pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QR
|
||||||
fmt.fourcc = FourCC::new(b"MPG1");
|
fmt.fourcc = FourCC::new(b"MPG1");
|
||||||
device.set_format(&fmt)?;
|
device.set_format(&fmt)?;
|
||||||
let mut stream = Stream::with_buffers(&device, Type::VideoCapture, 4)?;
|
let mut stream = Stream::with_buffers(&device, Type::VideoCapture, 4)?;
|
||||||
let start = Instant::now();
|
let start = SystemTime::now();
|
||||||
let mut scanner = keyfork_zbar::image_scanner::ImageScanner::new();
|
let mut scanner = keyfork_zbar::image_scanner::ImageScanner::new();
|
||||||
|
|
||||||
while Instant::now()
|
while SystemTime::now()
|
||||||
.duration_since(start)
|
.duration_since(start)
|
||||||
|
.unwrap_or(Duration::from_secs(0))
|
||||||
< timeout
|
< timeout
|
||||||
{
|
{
|
||||||
let (buffer, _) = stream.next()?;
|
let (buffer, _) = stream.next()?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keyfork-entropy"
|
name = "keyfork-entropy"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
bit_size % 8 == 0,
|
bit_size % 8 == 0,
|
||||||
"Bit size must be divisible by 8, got: {bit_size}"
|
"Bit size must be divisible by 8, got: {bit_size}"
|
||||||
);
|
);
|
||||||
match bit_size {
|
assert!(
|
||||||
128 | 256 | 512 => {}
|
bit_size <= 256,
|
||||||
_ => {
|
"Maximum supported bit size is 256, got: {bit_size}"
|
||||||
eprintln!("reading entropy of uncommon size: {bit_size}");
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let entropy = keyfork_entropy::generate_entropy_of_size(bit_size / 8)?;
|
let entropy = keyfork_entropy::generate_entropy_of_size(bit_size / 8)?;
|
||||||
println!("{}", smex::encode(entropy));
|
println!("{}", smex::encode(entropy));
|
||||||
|
|
Loading…
Reference in New Issue