keyfork-bug: initial commit, refactor use of unwrap() and expect() to use keyfork-bug
This commit is contained in:
parent
354eae5a6a
commit
472d0288f9
|
@ -1700,6 +1700,10 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyfork-bug"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keyfork-crossterm"
|
name = "keyfork-crossterm"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
|
@ -1761,6 +1765,7 @@ dependencies = [
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"hmac",
|
"hmac",
|
||||||
"k256",
|
"k256",
|
||||||
|
"keyfork-bug",
|
||||||
"keyfork-mnemonic-util",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-slip10-test-data",
|
"keyfork-slip10-test-data",
|
||||||
"ripemd",
|
"ripemd",
|
||||||
|
@ -1773,6 +1778,7 @@ dependencies = [
|
||||||
name = "keyfork-entropy"
|
name = "keyfork-entropy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"keyfork-bug",
|
||||||
"smex",
|
"smex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1793,6 +1799,7 @@ dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
"keyfork-bug",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
@ -1803,6 +1810,7 @@ dependencies = [
|
||||||
name = "keyfork-prompt"
|
name = "keyfork-prompt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"keyfork-bug",
|
||||||
"keyfork-crossterm",
|
"keyfork-crossterm",
|
||||||
"keyfork-mnemonic-util",
|
"keyfork-mnemonic-util",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1813,6 +1821,7 @@ name = "keyfork-qrcode"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
|
"keyfork-bug",
|
||||||
"keyfork-zbar",
|
"keyfork-zbar",
|
||||||
"rqrr",
|
"rqrr",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1828,6 +1837,7 @@ dependencies = [
|
||||||
"card-backend",
|
"card-backend",
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
|
"keyfork-bug",
|
||||||
"keyfork-derive-openpgp",
|
"keyfork-derive-openpgp",
|
||||||
"keyfork-mnemonic-util",
|
"keyfork-mnemonic-util",
|
||||||
"keyfork-prompt",
|
"keyfork-prompt",
|
||||||
|
@ -1873,6 +1883,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
"keyfork-bug",
|
||||||
"keyfork-derive-path-data",
|
"keyfork-derive-path-data",
|
||||||
"keyfork-derive-util",
|
"keyfork-derive-util",
|
||||||
"keyfork-frame",
|
"keyfork-frame",
|
||||||
|
|
|
@ -15,6 +15,7 @@ members = [
|
||||||
"crates/qrcode/keyfork-zbar",
|
"crates/qrcode/keyfork-zbar",
|
||||||
"crates/qrcode/keyfork-zbar-sys",
|
"crates/qrcode/keyfork-zbar-sys",
|
||||||
"crates/util/keyfork-bin",
|
"crates/util/keyfork-bin",
|
||||||
|
"crates/util/keyfork-bug",
|
||||||
"crates/util/keyfork-crossterm",
|
"crates/util/keyfork-crossterm",
|
||||||
"crates/util/keyfork-entropy",
|
"crates/util/keyfork-entropy",
|
||||||
"crates/util/keyfork-frame",
|
"crates/util/keyfork-frame",
|
||||||
|
|
|
@ -32,6 +32,7 @@ tower = { version = "0.4.13", features = ["tokio", "util"] }
|
||||||
thiserror = "1.0.47"
|
thiserror = "1.0.47"
|
||||||
serde = { version = "1.0.186", features = ["derive"] }
|
serde = { version = "1.0.186", features = ["derive"] }
|
||||||
tempfile = { version = "3.10.0", default-features = false }
|
tempfile = { version = "3.10.0", default-features = false }
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::{middleware, Keyforkd, ServiceBuilder, UnixServer};
|
||||||
|
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("This error can never be instantiated")]
|
#[error("This error can never be instantiated")]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -54,8 +56,10 @@ where
|
||||||
.worker_threads(2)
|
.worker_threads(2)
|
||||||
.enable_io()
|
.enable_io()
|
||||||
.build()
|
.build()
|
||||||
.expect("tokio threaded IO runtime");
|
.expect(bug!(
|
||||||
let socket_dir = tempfile::tempdir().expect("can't create tempdir");
|
"can't make tokio threaded IO runtime, should be enabled via feature flags"
|
||||||
|
));
|
||||||
|
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");
|
||||||
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);
|
||||||
|
@ -63,25 +67,28 @@ where
|
||||||
let socket_path = socket_path.clone();
|
let socket_path = socket_path.clone();
|
||||||
let seed = seed.to_vec();
|
let seed = seed.to_vec();
|
||||||
async move {
|
async move {
|
||||||
let mut server = UnixServer::bind(&socket_path).expect("can't bind unix socket");
|
let mut server =
|
||||||
tx.send(()).await.expect("couldn't send server start signal");
|
UnixServer::bind(&socket_path).expect(bug!("can't bind unix socket"));
|
||||||
|
tx.send(())
|
||||||
|
.await
|
||||||
|
.expect(bug!("couldn't send server start signal"));
|
||||||
let service = ServiceBuilder::new()
|
let service = ServiceBuilder::new()
|
||||||
.layer(middleware::BincodeLayer::new())
|
.layer(middleware::BincodeLayer::new())
|
||||||
.service(Keyforkd::new(seed.to_vec()));
|
.service(Keyforkd::new(seed.to_vec()));
|
||||||
server.run(service).await.unwrap();
|
server.run(service).await.expect(bug!("Unable to start service"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
rx.recv()
|
rx.recv()
|
||||||
.await
|
.await
|
||||||
.expect("can't receive server start signal from channel");
|
.expect(bug!("can't receive server start signal from channel"));
|
||||||
let test_handle = tokio::task::spawn_blocking(move || closure(&socket_path));
|
let test_handle = tokio::task::spawn_blocking(move || closure(&socket_path));
|
||||||
|
|
||||||
let result = test_handle.await;
|
let result = test_handle.await;
|
||||||
server_handle.abort();
|
server_handle.abort();
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
.expect("runtime could not join all threads")
|
.expect(bug!("runtime could not join all threads"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -29,6 +29,7 @@ thiserror = "1.0.47"
|
||||||
# Optional, not personally audited
|
# Optional, not personally audited
|
||||||
k256 = { version = "0.13.1", default-features = false, features = ["std", "arithmetic"], optional = true }
|
k256 = { version = "0.13.1", default-features = false, features = ["std", "arithmetic"], optional = true }
|
||||||
ed25519-dalek = { version = "2.0.0", optional = true }
|
ed25519-dalek = { version = "2.0.0", optional = true }
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::{DerivationIndex, DerivationPath, ExtendedPublicKey, PrivateKey, PublicKey};
|
use crate::{DerivationIndex, DerivationPath, ExtendedPublicKey, PrivateKey, PublicKey};
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
|
@ -124,7 +126,7 @@ mod serde_with {
|
||||||
let variable_len_bytes = <&[u8]>::deserialize(deserializer)?;
|
let variable_len_bytes = <&[u8]>::deserialize(deserializer)?;
|
||||||
let bytes: [u8; 32] = variable_len_bytes
|
let bytes: [u8; 32] = variable_len_bytes
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("unable to parse serialized private key; no support for static len");
|
.expect(bug!("unable to parse serialized private key; no support for static len"));
|
||||||
Ok(K::from_bytes(&bytes))
|
Ok(K::from_bytes(&bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +173,7 @@ where
|
||||||
|
|
||||||
fn new_internal(seed: &[u8]) -> 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("HmacSha512 InvalidLength should be infallible")
|
.expect(bug!("HmacSha512 InvalidLength should be infallible"))
|
||||||
.chain_update(seed)
|
.chain_update(seed)
|
||||||
.finalize()
|
.finalize()
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
|
@ -180,10 +182,10 @@ where
|
||||||
Self::new_from_parts(
|
Self::new_from_parts(
|
||||||
private_key
|
private_key
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("KEY_SIZE / 8 did not give a 32 byte slice"),
|
.expect(bug!("KEY_SIZE / 8 did not give a 32 byte slice")),
|
||||||
0,
|
0,
|
||||||
// Checked: chain_code is always the same length, hash is static size
|
// Checked: chain_code is always the same length, hash is static size
|
||||||
chain_code.try_into().expect("Invalid chain code length"),
|
chain_code.try_into().expect(bug!("Invalid chain code length")),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +407,7 @@ where
|
||||||
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
||||||
|
|
||||||
let mut hmac = HmacSha512::new_from_slice(&self.chain_code)
|
let mut hmac = HmacSha512::new_from_slice(&self.chain_code)
|
||||||
.expect("HmacSha512 InvalidLength should be infallible");
|
.expect(bug!("HmacSha512 InvalidLength should be infallible"));
|
||||||
if index.is_hardened() {
|
if index.is_hardened() {
|
||||||
hmac.update(&[0]);
|
hmac.update(&[0]);
|
||||||
hmac.update(&self.private_key.to_bytes());
|
hmac.update(&self.private_key.to_bytes());
|
||||||
|
@ -423,7 +425,7 @@ where
|
||||||
.derive_child(
|
.derive_child(
|
||||||
&private_key
|
&private_key
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Invalid length for private key"),
|
.expect(bug!("Invalid length for private key")),
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::Derivation)?;
|
.map_err(|_| Error::Derivation)?;
|
||||||
|
|
||||||
|
@ -432,7 +434,7 @@ where
|
||||||
depth,
|
depth,
|
||||||
chain_code: chain_code
|
chain_code: chain_code
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Invalid length for chain code"),
|
.expect(bug!("Invalid length for chain code")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ use hmac::{Hmac, Mac};
|
||||||
use sha2::Sha512;
|
use sha2::Sha512;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
const KEY_SIZE: usize = 256;
|
const KEY_SIZE: usize = 256;
|
||||||
|
|
||||||
/// Errors associated with creating or deriving Extended Public Keys.
|
/// Errors associated with creating or deriving Extended Public Keys.
|
||||||
|
@ -142,9 +144,11 @@ where
|
||||||
let (child_key, chain_code) = hmac.split_at(KEY_SIZE / 8);
|
let (child_key, chain_code) = hmac.split_at(KEY_SIZE / 8);
|
||||||
let derived_key = self
|
let derived_key = self
|
||||||
.public_key
|
.public_key
|
||||||
.derive_child(child_key.try_into().expect("Invalid key length"))
|
.derive_child(child_key.try_into().expect(bug!("Invalid key length")))
|
||||||
.map_err(|_| Error::Derivation)?;
|
.map_err(|_| Error::Derivation)?;
|
||||||
let chain_code = chain_code.try_into().expect("Invalid chain code length");
|
let chain_code = chain_code
|
||||||
|
.try_into()
|
||||||
|
.expect(bug!("Invalid chain code length"));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
public_key: derived_key,
|
public_key: derived_key,
|
||||||
|
|
|
@ -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`.
|
||||||
|
@ -115,7 +117,7 @@ impl PrivateKey for k256::SecretKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes(b: &PrivateKeyBytes) -> Self {
|
fn from_bytes(b: &PrivateKeyBytes) -> Self {
|
||||||
Self::from_slice(b).expect("Invalid private key bytes")
|
Self::from_slice(b).expect(bug!("Invalid private key bytes"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bytes(&self) -> PrivateKeyBytes {
|
fn to_bytes(&self) -> PrivateKeyBytes {
|
||||||
|
@ -134,13 +136,13 @@ impl PrivateKey for k256::SecretKey {
|
||||||
let other = *other;
|
let other = *other;
|
||||||
// Checked: See above nonzero check
|
// Checked: See above nonzero check
|
||||||
let scalar = Option::<NonZeroScalar>::from(NonZeroScalar::from_repr(other.into()))
|
let scalar = Option::<NonZeroScalar>::from(NonZeroScalar::from_repr(other.into()))
|
||||||
.expect("Should have been able to get a NonZeroScalar");
|
.expect(bug!("Should have been able to get a NonZeroScalar"));
|
||||||
|
|
||||||
let derived_scalar = self.to_nonzero_scalar().as_ref() + scalar.as_ref();
|
let derived_scalar = self.to_nonzero_scalar().as_ref() + scalar.as_ref();
|
||||||
Ok(
|
Ok(
|
||||||
Option::<NonZeroScalar>::from(NonZeroScalar::new(derived_scalar))
|
Option::<NonZeroScalar>::from(NonZeroScalar::new(derived_scalar))
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.expect("Should be able to make Key"),
|
.expect(bug!("Should be able to make Key")),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ use ripemd::Ripemd160;
|
||||||
use sha2::Sha256;
|
use sha2::Sha256;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
pub(crate) type PublicKeyBytes = [u8; 33];
|
pub(crate) type PublicKeyBytes = [u8; 33];
|
||||||
|
|
||||||
/// Functions required to use an `ExtendedPublicKey`.
|
/// Functions required to use an `ExtendedPublicKey`.
|
||||||
|
@ -63,7 +65,7 @@ pub trait PublicKey: Sized {
|
||||||
// Note: Safety assured by type returned from Ripemd160
|
// Note: Safety assured by type returned from Ripemd160
|
||||||
hash[..4]
|
hash[..4]
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Ripemd160 returned too little data")
|
.expect(bug!("Ripemd160 returned too little data"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +110,11 @@ impl PublicKey for k256::PublicKey {
|
||||||
}
|
}
|
||||||
// Checked: See above
|
// Checked: See above
|
||||||
let scalar = Option::<NonZeroScalar>::from(NonZeroScalar::from_repr(other.into()))
|
let scalar = Option::<NonZeroScalar>::from(NonZeroScalar::from_repr(other.into()))
|
||||||
.expect("Should have been able to get a NonZeroScalar");
|
.expect(bug!("Should have been able to get a NonZeroScalar"));
|
||||||
|
|
||||||
let point = self.to_projective() + (AffinePoint::generator() * *scalar);
|
let point = self.to_projective() + (AffinePoint::generator() * *scalar);
|
||||||
Ok(Self::from_affine(point.into()).expect("Could not from_affine after scalar arithmetic"))
|
Ok(Self::from_affine(point.into())
|
||||||
|
.expect(bug!("Could not from_affine after scalar arithmetic")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,10 +153,11 @@ impl TestPublicKey {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn from_bytes(b: &[u8]) -> Self {
|
pub fn from_bytes(b: &[u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
key: b.try_into().unwrap(),
|
key: b
|
||||||
|
.try_into()
|
||||||
|
.expect(bug!("invalid size when constructing TestPublicKey")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKey for TestPublicKey {
|
impl PublicKey for TestPublicKey {
|
||||||
|
|
|
@ -36,3 +36,4 @@ card-backend-pcsc = { version = "0.5.0", optional = true }
|
||||||
openpgp-card-sequoia = { version = "0.2.0", optional = true, default-features = false }
|
openpgp-card-sequoia = { version = "0.2.0", optional = true, default-features = false }
|
||||||
openpgp-card = { version = "0.4.0", optional = true }
|
openpgp-card = { version = "0.4.0", optional = true }
|
||||||
sequoia-openpgp = { version = "1.17.0", optional = true, default-features = false }
|
sequoia-openpgp = { version = "1.17.0", optional = true, default-features = false }
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../util/keyfork-bug" }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![allow(clippy::expect_fun_call)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
|
@ -10,6 +11,7 @@ use aes_gcm::{
|
||||||
Aes256Gcm, KeyInit, Nonce,
|
Aes256Gcm, KeyInit, Nonce,
|
||||||
};
|
};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
|
use keyfork_bug::{bug, POISONED_MUTEX};
|
||||||
use keyfork_mnemonic_util::{English, Mnemonic};
|
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||||
|
@ -200,7 +202,7 @@ pub trait Format {
|
||||||
{
|
{
|
||||||
prompt
|
prompt
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
||||||
if let Ok(Some(hex)) =
|
if let Ok(Some(hex)) =
|
||||||
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0)
|
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0)
|
||||||
|
@ -211,7 +213,7 @@ pub trait Format {
|
||||||
} else {
|
} else {
|
||||||
prompt
|
prompt
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -225,7 +227,7 @@ pub trait Format {
|
||||||
};
|
};
|
||||||
let [nonce_mnemonic, pubkey_mnemonic] = prompt
|
let [nonce_mnemonic, pubkey_mnemonic] = prompt
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_validated_wordlist::<English, _>(
|
.prompt_validated_wordlist::<English, _>(
|
||||||
QRCODE_COULDNT_READ,
|
QRCODE_COULDNT_READ,
|
||||||
3,
|
3,
|
||||||
|
@ -305,24 +307,27 @@ pub trait Format {
|
||||||
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
||||||
qrcode_data.extend(payload_mnemonic.as_bytes());
|
qrcode_data.extend(payload_mnemonic.as_bytes());
|
||||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
||||||
prompt.lock().unwrap().prompt_message(PromptMessage::Text(
|
|
||||||
concat!(
|
|
||||||
"A QR code will be displayed after this prompt. ",
|
|
||||||
"Send the QR code back to the operator combining the shards. ",
|
|
||||||
"Nobody else should scan this QR code."
|
|
||||||
)
|
|
||||||
.to_string(),
|
|
||||||
))?;
|
|
||||||
prompt
|
prompt
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
|
.prompt_message(PromptMessage::Text(
|
||||||
|
concat!(
|
||||||
|
"A QR code will be displayed after this prompt. ",
|
||||||
|
"Send the QR code back to the operator combining the shards. ",
|
||||||
|
"Nobody else should scan this QR code."
|
||||||
|
)
|
||||||
|
.to_string(),
|
||||||
|
))?;
|
||||||
|
prompt
|
||||||
|
.lock()
|
||||||
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(PromptMessage::Data(qrcode))?;
|
.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt
|
prompt
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(PromptMessage::Text(format!(
|
.prompt_message(PromptMessage::Text(format!(
|
||||||
"Upon request, these words should be sent: {our_pubkey_mnemonic} {payload_mnemonic}"
|
"Upon request, these words should be sent: {our_pubkey_mnemonic} {payload_mnemonic}"
|
||||||
)))?;
|
)))?;
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
//! OpenPGP Shard functionality.
|
//! OpenPGP Shard functionality.
|
||||||
|
|
||||||
|
#![allow(clippy::expect_fun_call)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
marker::PhantomData,
|
||||||
path::Path,
|
path::Path,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
marker::PhantomData,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
derive_util::{DerivationPath, VariableLengthSeed},
|
derive_util::{DerivationPath, VariableLengthSeed},
|
||||||
XPrv,
|
XPrv,
|
||||||
|
@ -186,9 +189,7 @@ pub struct OpenPGP<P: PromptHandler> {
|
||||||
impl<P: PromptHandler> OpenPGP<P> {
|
impl<P: PromptHandler> OpenPGP<P> {
|
||||||
#[allow(clippy::new_without_default, missing_docs)]
|
#[allow(clippy::new_without_default, missing_docs)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { p: PhantomData }
|
||||||
p: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +224,8 @@ impl<P: PromptHandler> OpenPGP<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const METADATA_MESSAGE_MISSING: &str = "Metadata message was not found in parsed packets";
|
||||||
|
|
||||||
impl<P: PromptHandler> Format for OpenPGP<P> {
|
impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type PublicKey = Cert;
|
type PublicKey = Cert;
|
||||||
|
@ -235,16 +238,16 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
let seed = VariableLengthSeed::new(seed);
|
let seed = VariableLengthSeed::new(seed);
|
||||||
// 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'").expect("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)
|
||||||
.derive_path(&path)
|
.derive_path(&path)
|
||||||
.expect("valid derivation");
|
.expect(bug!("valid derivation"));
|
||||||
keyfork_derive_openpgp::derive(
|
keyfork_derive_openpgp::derive(
|
||||||
xprv,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
&userid,
|
&userid,
|
||||||
)
|
)
|
||||||
.expect("valid cert creation")
|
.expect(bug!("valid cert creation"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_encrypted_header(
|
fn format_encrypted_header(
|
||||||
|
@ -258,21 +261,26 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
// Note: Sequoia does not export private keys on a Cert, only on a TSK
|
// Note: Sequoia does not export private keys on a Cert, only on a TSK
|
||||||
signing_key
|
signing_key
|
||||||
.serialize(&mut pp)
|
.serialize(&mut pp)
|
||||||
.expect("serialize cert into bytes");
|
.expect(bug!("serialize cert into bytes"));
|
||||||
for cert in key_data {
|
for cert in key_data {
|
||||||
cert.serialize(&mut pp)
|
cert.serialize(&mut pp)
|
||||||
.expect("serialize pubkey into bytes");
|
.expect(bug!("serialize pubkey into bytes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify packet pile
|
// verify packet pile
|
||||||
let mut iter = openpgp::cert::CertParser::from_bytes(&pp[SHARD_METADATA_OFFSET..])
|
let mut iter = openpgp::cert::CertParser::from_bytes(&pp[SHARD_METADATA_OFFSET..])
|
||||||
.expect("should have certs");
|
.expect(bug!("should have certs"));
|
||||||
let first_cert = iter.next().transpose().ok().flatten().expect("first cert");
|
let first_cert = iter
|
||||||
|
.next()
|
||||||
|
.transpose()
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.expect(bug!("first cert"));
|
||||||
assert_eq!(signing_key, &first_cert);
|
assert_eq!(signing_key, &first_cert);
|
||||||
|
|
||||||
for (packet_cert, cert) in iter.zip(key_data) {
|
for (packet_cert, cert) in iter.zip(key_data) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&packet_cert.expect("parsed packet cert"),
|
&packet_cert.expect(bug!("parsed packet cert")),
|
||||||
cert,
|
cert,
|
||||||
"packet pile could not recreate cert: {}",
|
"packet pile could not recreate cert: {}",
|
||||||
cert.fingerprint(),
|
cert.fingerprint(),
|
||||||
|
@ -385,7 +393,7 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
.map_err(Error::Sequoia)?
|
.map_err(Error::Sequoia)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.expect("serialized message should be parseable");
|
.expect(bug!("serialized message should be parseable"));
|
||||||
|
|
||||||
Ok(message)
|
Ok(message)
|
||||||
}
|
}
|
||||||
|
@ -425,7 +433,9 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
|
|
||||||
let mut encrypted_messages = encrypted_data.iter();
|
let mut encrypted_messages = encrypted_data.iter();
|
||||||
|
|
||||||
let metadata = encrypted_messages.next().expect("metdata");
|
let metadata = encrypted_messages
|
||||||
|
.next()
|
||||||
|
.expect(bug!(METADATA_MESSAGE_MISSING));
|
||||||
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
||||||
|
|
||||||
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
||||||
|
@ -482,7 +492,9 @@ impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
|
|
||||||
let mut encrypted_messages = encrypted_data.iter();
|
let mut encrypted_messages = encrypted_data.iter();
|
||||||
|
|
||||||
let metadata = encrypted_messages.next().expect("metadata");
|
let metadata = encrypted_messages
|
||||||
|
.next()
|
||||||
|
.expect(bug!(METADATA_MESSAGE_MISSING));
|
||||||
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
||||||
|
|
||||||
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
let (threshold, root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
||||||
|
@ -625,7 +637,12 @@ fn decrypt_with_manager<P: PromptHandler>(
|
||||||
|
|
||||||
// Iterate over all fingerprints and use key_by_fingerprints to assoc with Enc. Message
|
// Iterate over all fingerprints and use key_by_fingerprints to assoc with Enc. Message
|
||||||
if let Some(fp) = manager.load_any_fingerprint(unused_fingerprints)? {
|
if let Some(fp) = manager.load_any_fingerprint(unused_fingerprints)? {
|
||||||
let cert_keyid = cert_by_fingerprint.get(&fp).unwrap().clone();
|
let cert_keyid = cert_by_fingerprint
|
||||||
|
.get(&fp)
|
||||||
|
.expect(bug!(
|
||||||
|
"manager loaded fingerprint not from unused_fingerprints"
|
||||||
|
))
|
||||||
|
.clone();
|
||||||
if let Some(message) = messages.remove(&cert_keyid) {
|
if let Some(message) = messages.remove(&cert_keyid) {
|
||||||
let message = message.decrypt_with(policy, &mut *manager)?;
|
let message = message.decrypt_with(policy, &mut *manager)?;
|
||||||
decrypted_messages.insert(cert_keyid, message);
|
decrypted_messages.insert(cert_keyid, message);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
#![allow(clippy::expect_fun_call)]
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use keyfork_bug::{bug, POISONED_MUTEX};
|
||||||
use keyfork_prompt::{Error as PromptError, PromptHandler};
|
use keyfork_prompt::{Error as PromptError, PromptHandler};
|
||||||
|
|
||||||
use super::openpgp::{
|
use super::openpgp::{
|
||||||
|
@ -140,7 +143,7 @@ impl<P: PromptHandler> DecryptionHelper for &mut Keyring<P> {
|
||||||
let passphrase = self
|
let passphrase = self
|
||||||
.pm
|
.pm
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_passphrase(&message)
|
.prompt_passphrase(&message)
|
||||||
.context("Decryption passphrase")?;
|
.context("Decryption passphrase")?;
|
||||||
secret_key
|
secret_key
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
#![allow(clippy::expect_fun_call)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use keyfork_bug::{bug, POISONED_MUTEX};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{PinValidator, Validator},
|
validators::{PinValidator, Validator},
|
||||||
Error as PromptError, Message, PromptHandler,
|
Error as PromptError, Message, PromptHandler,
|
||||||
|
@ -100,7 +103,7 @@ impl<P: PromptHandler> SmartcardManager<P> {
|
||||||
}
|
}
|
||||||
self.pm
|
self.pm
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(Message::Text(
|
.prompt_message(Message::Text(
|
||||||
"No smart card was found. Please plug in a smart card and press enter"
|
"No smart card was found. Please plug in a smart card and press enter"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
@ -160,7 +163,7 @@ impl<P: PromptHandler> SmartcardManager<P> {
|
||||||
|
|
||||||
self.pm
|
self.pm
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(Message::Text(
|
.prompt_message(Message::Text(
|
||||||
"Please plug in a smart card and press enter".to_string(),
|
"Please plug in a smart card and press enter".to_string(),
|
||||||
))?;
|
))?;
|
||||||
|
@ -261,11 +264,11 @@ 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()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
||||||
let verification_status = transaction.verify_user_pin(temp_pin.as_str().trim());
|
let verification_status = transaction.verify_user_pin(temp_pin.as_str().trim());
|
||||||
match verification_status {
|
match verification_status {
|
||||||
#[allow(clippy::ignored_unit_patterns)]
|
#[allow(clippy::ignored_unit_patterns)]
|
||||||
|
@ -277,7 +280,7 @@ impl<P: PromptHandler> DecryptionHelper for &mut SmartcardManager<P> {
|
||||||
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => {
|
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => {
|
||||||
self.pm
|
self.pm
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect(bug!(POISONED_MUTEX))
|
||||||
.prompt_message(Message::Text("Invalid PIN length entered.".to_string()))?;
|
.prompt_message(Message::Text("Invalid PIN length entered.".to_string()))?;
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
|
|
|
@ -8,13 +8,14 @@ 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 = ["decode-backend-rqrr"]
|
bin = ["decode-backend-rqrr"]
|
||||||
decode-backend-rqrr = ["dep:rqrr"]
|
decode-backend-rqrr = ["dep:rqrr"]
|
||||||
decode-backend-zbar = ["dep:keyfork-zbar"]
|
decode-backend-zbar = ["dep:keyfork-zbar"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = { version = "0.24.7", default-features = false, features = ["jpeg"] }
|
image = { version = "0.24.7", default-features = false, features = ["jpeg"] }
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../../util/keyfork-bug" }
|
||||||
keyfork-zbar = { version = "0.1.0", path = "../keyfork-zbar", optional = true }
|
keyfork-zbar = { version = "0.1.0", path = "../keyfork-zbar", optional = true }
|
||||||
rqrr = { version = "0.6.0", optional = true }
|
rqrr = { version = "0.6.0", optional = true }
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Encoding and decoding QR codes.
|
//! Encoding and decoding QR codes.
|
||||||
|
|
||||||
|
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},
|
||||||
|
@ -98,11 +100,13 @@ pub fn qrencode(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VIDEO_FORMAT_READ_ERROR: &str = "Failed to read video device format";
|
||||||
|
|
||||||
/// Continuously scan the `index`-th camera for a QR code.
|
/// Continuously scan the `index`-th camera for a QR code.
|
||||||
#[cfg(feature = "decode-backend-rqrr")]
|
#[cfg(feature = "decode-backend-rqrr")]
|
||||||
pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QRCodeScanError> {
|
pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QRCodeScanError> {
|
||||||
let device = Device::new(index)?;
|
let device = Device::new(index)?;
|
||||||
let mut fmt = device.format().expect("Failed to read format");
|
let mut fmt = device.format().unwrap_or_else(bug::panic!(VIDEO_FORMAT_READ_ERROR));
|
||||||
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)?;
|
||||||
|
@ -133,7 +137,7 @@ pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QR
|
||||||
#[cfg(feature = "decode-backend-zbar")]
|
#[cfg(feature = "decode-backend-zbar")]
|
||||||
pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QRCodeScanError> {
|
pub fn scan_camera(timeout: Duration, index: usize) -> Result<Option<String>, QRCodeScanError> {
|
||||||
let device = Device::new(index)?;
|
let device = Device::new(index)?;
|
||||||
let mut fmt = device.format().expect("Failed to read format");
|
let mut fmt = device.format().unwrap_or_else(bug::panic!(VIDEO_FORMAT_READ_ERROR));
|
||||||
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)?;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "keyfork-bug"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,108 @@
|
||||||
|
//! Keyfork Bug Reporting Utilities.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::{fs::File, io::Write};
|
||||||
|
//! use keyfork_bug as bug;
|
||||||
|
//!
|
||||||
|
//! let option = Some("hello world!");
|
||||||
|
//! let value = option.expect(bug::bug!("missing str value!"));
|
||||||
|
//!
|
||||||
|
//! let mut output_file = File::create("/dev/null").expect(bug::bug!("can't open /dev/null"));
|
||||||
|
//! output_file
|
||||||
|
//! .write_all(value.as_bytes())
|
||||||
|
//! .unwrap_or_else(bug::panic!("Can't write to file: {}", value));
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ```rust,should_panic
|
||||||
|
//! use std::fs::File;
|
||||||
|
//! use keyfork_bug as bug;
|
||||||
|
//!
|
||||||
|
//! let mut output_file = File::open("/dev/nukk").expect(bug::bug!("can't open /dev/null"));
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
/// The mutex was poisoned and is unusable.
|
||||||
|
pub const POISONED_MUTEX: &str = "The mutex was poisoned and is unusable";
|
||||||
|
|
||||||
|
/// Automatically generate a bug report message for Keyfork. This macro is intended to use when
|
||||||
|
/// using `Result::expect()` or `Option::expect()` to retrieve information about the callsite where
|
||||||
|
/// the bug was located.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use keyfork_bug::bug;
|
||||||
|
///
|
||||||
|
/// let option = Some(0u32);
|
||||||
|
/// let value = option.expect(bug!("missing u32 value!"));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use keyfork_bug::bug;
|
||||||
|
///
|
||||||
|
/// let error_message = "This is a really long error message that should not be in the macro.";
|
||||||
|
/// let option = Some(0u32);
|
||||||
|
/// let value = option.expect(bug!(error_message));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```rust,should_panic
|
||||||
|
/// use keyfork_bug::bug;
|
||||||
|
///
|
||||||
|
/// let option: Option<u32> = None;
|
||||||
|
/// let value = option.expect(bug!("missing u32 value!"));
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bug {
|
||||||
|
($input:literal) => {
|
||||||
|
concat!(
|
||||||
|
"Keyfork encountered a BUG at: [",
|
||||||
|
file!(),
|
||||||
|
":",
|
||||||
|
line!(),
|
||||||
|
":",
|
||||||
|
column!(),
|
||||||
|
"]: ",
|
||||||
|
$input,
|
||||||
|
"\n\nReport this bug to <team@distrust.co>, this behavior is unexpected!"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
($input:ident) => {
|
||||||
|
format!(
|
||||||
|
concat!("Keyfork encountered a BUG at: [{file}:{line}:{column}]: {input}\n\n",
|
||||||
|
"Report this bug to <team@distrust.co>, this behavior is unexpected!"
|
||||||
|
),
|
||||||
|
file=file!(),
|
||||||
|
line=line!(),
|
||||||
|
column=column!(),
|
||||||
|
input=$input,
|
||||||
|
).as_str()
|
||||||
|
};
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
let message = format!($($arg)*);
|
||||||
|
$crate::bug!(message)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a closure that, when called, panics with a bug report message for Keyfork. Returning a
|
||||||
|
/// closure can help handle the `clippy::expect_fun_call` lint. The closure accepts an error
|
||||||
|
/// argument, so it is suitable for being used with [`Result`] types instead of [`Option`] types.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use std::fs::File;
|
||||||
|
/// use keyfork_bug as bug;
|
||||||
|
///
|
||||||
|
/// let file = File::open("/dev/null").unwrap_or_else(bug::panic!("couldn't open /dev/null"));
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! panic {
|
||||||
|
($input:literal) => { |e| {
|
||||||
|
std::panic!("{}\n{}", $crate::bug!($input), e)
|
||||||
|
}};
|
||||||
|
($input:ident) => { |e| {
|
||||||
|
std::panic!("{}\n{}", $crate::bug!($input), e)
|
||||||
|
}};
|
||||||
|
($($arg:tt)*) => { |e| {
|
||||||
|
std::panic!("{}\n{}", $crate::bug!($($arg)*), e)
|
||||||
|
}};
|
||||||
|
}
|
|
@ -11,4 +11,5 @@ default = ["bin"]
|
||||||
bin = ["smex"]
|
bin = ["smex"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../keyfork-bug" }
|
||||||
smex = { version = "0.1.0", path = "../smex", optional = true }
|
smex = { version = "0.1.0", path = "../smex", optional = true }
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! Utilities for reading entropy from secure sources.
|
//! Utilities for reading entropy from secure sources.
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{read_dir, read_to_string, File},
|
fs::{read_dir, read_to_string, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
|
@ -9,15 +11,16 @@ 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/"];
|
||||||
|
|
||||||
fn ensure_safe_kernel_version() {
|
fn ensure_safe_kernel_version() {
|
||||||
let kernel_version = read_to_string("/proc/version").expect("/proc/version");
|
let kernel_version =
|
||||||
|
read_to_string("/proc/version").expect(bug!("Unable to open file: /proc/version"));
|
||||||
let v = kernel_version
|
let v = kernel_version
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.nth(2)
|
.nth(2)
|
||||||
.expect("Unable to parse kernel version")
|
.expect(bug!("Unable to parse kernel version"))
|
||||||
.split('.')
|
.split('.')
|
||||||
.take(2)
|
.take(2)
|
||||||
.map(str::parse)
|
.map(str::parse)
|
||||||
.map(|x| x.expect("Unable to parse kernel version number"))
|
.map(|x| x.expect(bug!("Unable to parse kernel version number")))
|
||||||
.collect::<Vec<u32>>();
|
.collect::<Vec<u32>>();
|
||||||
let [major, minor, ..] = v.as_slice() else {
|
let [major, minor, ..] = v.as_slice() else {
|
||||||
panic!("Unable to determine major and minor: {kernel_version}");
|
panic!("Unable to determine major and minor: {kernel_version}");
|
||||||
|
@ -30,22 +33,23 @@ fn ensure_safe_kernel_version() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_offline() {
|
fn ensure_offline() {
|
||||||
let paths = read_dir("/sys/class/net").expect("Unable to read network interfaces");
|
let paths = read_dir("/sys/class/net").expect(bug!("Unable to read network interfaces"));
|
||||||
for entry in paths {
|
for entry in paths {
|
||||||
let mut path = entry.expect("Unable to read directory entry").path();
|
let mut path = entry.expect(bug!("Unable to read directory entry")).path();
|
||||||
if path
|
if path
|
||||||
.as_os_str()
|
.as_os_str()
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect("Unable to decode UTF-8 filepath")
|
.expect(bug!("Unable to decode UTF-8 filepath"))
|
||||||
.split('/')
|
.split('/')
|
||||||
.last()
|
.last()
|
||||||
.expect("No data in file path")
|
.expect(bug!("No data in file path"))
|
||||||
== "lo"
|
== "lo"
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
path.push("operstate");
|
path.push("operstate");
|
||||||
let isup = read_to_string(&path).expect("Unable to read operstate of network interfaces");
|
let isup =
|
||||||
|
read_to_string(&path).expect(bug!("Unable to read operstate of network interfaces"));
|
||||||
assert_ne!(isup.trim(), "up", "No network interfaces should be up");
|
assert_ne!(isup.trim(), "up", "No network interfaces should be up");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ sha2 = "0.10.7"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
pbkdf2 = "0.12.2"
|
pbkdf2 = "0.12.2"
|
||||||
smex = { version = "0.1.0", path = "../smex", optional = true }
|
smex = { version = "0.1.0", path = "../smex", optional = true }
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../keyfork-bug" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bip39 = "2.0.0"
|
bip39 = "2.0.0"
|
||||||
|
|
|
@ -48,13 +48,9 @@
|
||||||
//! let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
|
//! let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::{
|
use std::{error::Error, fmt::Display, marker::PhantomData, str::FromStr, sync::OnceLock};
|
||||||
error::Error,
|
|
||||||
fmt::Display,
|
use keyfork_bug::bug;
|
||||||
str::FromStr,
|
|
||||||
sync::OnceLock,
|
|
||||||
marker::PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
use hmac::Hmac;
|
use hmac::Hmac;
|
||||||
use pbkdf2::pbkdf2;
|
use pbkdf2::pbkdf2;
|
||||||
|
@ -115,12 +111,11 @@ impl Wordlist for English {
|
||||||
fn get_singleton<'a>() -> &'a Self {
|
fn get_singleton<'a>() -> &'a Self {
|
||||||
ENGLISH.get_or_init(|| {
|
ENGLISH.get_or_init(|| {
|
||||||
let wordlist_file = include_str!("data/wordlist.txt");
|
let wordlist_file = include_str!("data/wordlist.txt");
|
||||||
let mut words = wordlist_file
|
let mut words = wordlist_file.lines().skip(1).map(|x| x.trim().to_string());
|
||||||
.lines()
|
|
||||||
.skip(1)
|
|
||||||
.map(|x| x.trim().to_string());
|
|
||||||
English {
|
English {
|
||||||
words: std::array::from_fn(|_| words.next().expect("wordlist has 2048 words")),
|
words: std::array::from_fn(|_| {
|
||||||
|
words.next().expect(bug!("wordlist {} should have 2048 words"))
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -247,7 +242,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MnemonicBase { data, marker: PhantomData })
|
Ok(MnemonicBase {
|
||||||
|
data,
|
||||||
|
marker: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +388,7 @@ where
|
||||||
let mnemonic = self.to_string();
|
let mnemonic = self.to_string();
|
||||||
let salt = ["mnemonic", passphrase.unwrap_or("")].join("");
|
let salt = ["mnemonic", passphrase.unwrap_or("")].join("");
|
||||||
pbkdf2::<Hmac<Sha512>>(mnemonic.as_bytes(), salt.as_bytes(), 2048, &mut seed)
|
pbkdf2::<Hmac<Sha512>>(mnemonic.as_bytes(), salt.as_bytes(), 2048, &mut seed)
|
||||||
.expect("HmacSha512 InvalidLength should be infallible");
|
.expect(bug!("HmacSha512 InvalidLength should be infallible"));
|
||||||
seed.to_vec()
|
seed.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,13 +413,16 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find a way to not have to collect to vec
|
// TODO: find a way to not have to collect to vec
|
||||||
bits.chunks_exact(11).peekable().map(|chunk| {
|
bits.chunks_exact(11)
|
||||||
let mut num = 0usize;
|
.peekable()
|
||||||
for i in 0..11 {
|
.map(|chunk| {
|
||||||
num += usize::from(chunk[10 - i]) << i;
|
let mut num = 0usize;
|
||||||
}
|
for i in 0..11 {
|
||||||
num
|
num += usize::from(chunk[10 - i]) << i;
|
||||||
}).collect()
|
}
|
||||||
|
num
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ default = ["mnemonic"]
|
||||||
mnemonic = ["keyfork-mnemonic-util"]
|
mnemonic = ["keyfork-mnemonic-util"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
keyfork-bug = { version = "0.1.0", path = "../keyfork-bug" }
|
||||||
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"] }
|
keyfork-crossterm = { version = "0.27.1", path = "../keyfork-crossterm", default-features = false, features = ["use-dev-tty", "events", "bracketed-paste"] }
|
||||||
keyfork-mnemonic-util = { version = "0.1.0", path = "../keyfork-mnemonic-util", optional = true }
|
keyfork-mnemonic-util = { version = "0.1.0", path = "../keyfork-mnemonic-util", optional = true }
|
||||||
thiserror = "1.0.51"
|
thiserror = "1.0.51"
|
||||||
|
|
|
@ -13,6 +13,8 @@ use keyfork_crossterm::{
|
||||||
ExecutableCommand, QueueableCommand,
|
ExecutableCommand, QueueableCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
use crate::{Error, Message, PromptHandler, Wordlist};
|
use crate::{Error, Message, PromptHandler, Wordlist};
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -120,9 +122,9 @@ where
|
||||||
W: Write + AsRawFd,
|
W: Write + AsRawFd,
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.write.execute(DisableBracketedPaste).unwrap();
|
self.write.execute(DisableBracketedPaste).expect(bug!("can't restore bracketed paste"));
|
||||||
self.write.execute(LeaveAlternateScreen).unwrap();
|
self.write.execute(LeaveAlternateScreen).expect(bug!("can't leave alternate screen"));
|
||||||
self.terminal.disable_raw_mode().unwrap();
|
self.terminal.disable_raw_mode().expect(bug!("can't disable raw mode"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ pub mod mnemonic {
|
||||||
use super::Validator;
|
use super::Validator;
|
||||||
|
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError};
|
||||||
|
use keyfork_bug::bug;
|
||||||
|
|
||||||
/// A mnemonic could not be validated from the given input.
|
/// A mnemonic could not be validated from the given input.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
@ -237,7 +238,7 @@ pub mod mnemonic {
|
||||||
|
|
||||||
Ok(output
|
Ok(output
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("vec with capacity of const N was not filled"))
|
.expect(bug!("vec with capacity of const N was not filled")))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue