all crates: make pedantic clippy happy
This commit is contained in:
parent
672cc6a699
commit
e7a776f59f
18
Cargo.toml
18
Cargo.toml
|
@ -26,6 +26,23 @@ members = [
|
|||
"crates/tests",
|
||||
]
|
||||
|
||||
[workspace.lints.clippy]
|
||||
all = { level = "deny", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
|
||||
# used often in tests
|
||||
wildcard_imports = { level = "allow"}
|
||||
|
||||
# annoying
|
||||
must_use_candidate = "allow"
|
||||
return_self_not_must_use = "allow"
|
||||
|
||||
# sometimes i like the logical flow of keeping things in an "else"
|
||||
redundant_else = "allow"
|
||||
|
||||
# i hate using `.unwrap_or_else(|| keyfork_bug::bug!())`
|
||||
expect_fun_call = "allow"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
||||
# Keyfork dependencies
|
||||
|
@ -84,4 +101,3 @@ debug = true
|
|||
[profile.dev.package.keyfork-qrcode]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
doc-valid-idents = ["OpenPGP", ".."]
|
|
@ -4,6 +4,9 @@ version = "0.2.2"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
//!
|
||||
//! ### Request: Derive Key
|
||||
//!
|
||||
//! The client creates a derivation path of at least two indices and requests a derived XPrv
|
||||
//! The client creates a derivation path of at least two indices and requests a derived `XPrv`
|
||||
//! (Extended Private Key) from the server.
|
||||
//!
|
||||
//! ```rust
|
||||
|
@ -68,7 +68,7 @@
|
|||
//! ## Extended Private Keys
|
||||
//!
|
||||
//! Keyfork doesn't need to be continuously called once a key has been derived. Once an Extended
|
||||
//! Private Key (often shortened to "XPrv") has been created, further derivations can be performed.
|
||||
//! Private Key (often shortened to `XPrv`) has been created, further derivations can be performed.
|
||||
//! The tests for this library ensure that all levels of Keyfork derivation beyond the required two
|
||||
//! will be derived similarly between the server and the client.
|
||||
//!
|
||||
|
@ -117,7 +117,7 @@
|
|||
//!
|
||||
//! ## Testing Infrastructure
|
||||
//!
|
||||
//! In tests, the `keyforkd::test_util` module and TestPrivateKeys can be used. These provide
|
||||
//! In tests, the `keyforkd::test_util` module and `TestPrivateKeys` can be used. These provide
|
||||
//! useful utilities for writing tests that interact with the Keyfork Server without needing to
|
||||
//! manually create the server for the purpose of the test. The `run_test` method can be used to
|
||||
//! run a test, which can handle both returning errors and correctly translating panics (though,
|
||||
|
@ -199,6 +199,10 @@ pub enum Error {
|
|||
#[error("Socket was unable to connect to {1}: {0} (make sure keyforkd is running)")]
|
||||
Connect(std::io::Error, PathBuf),
|
||||
|
||||
/// The path of the derived key was of an invalid length.
|
||||
#[error("Derived key path is of invalid length")]
|
||||
InvalidPathLength(#[from] std::num::TryFromIntError),
|
||||
|
||||
/// Data could not be written to, or read from, the socket.
|
||||
#[error("Could not write to or from the socket: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
@ -237,12 +241,9 @@ pub fn get_socket() -> Result<UnixStream, Error> {
|
|||
.filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
let mut socket_path: PathBuf;
|
||||
#[allow(clippy::single_match_else)]
|
||||
match socket_vars.get("KEYFORKD_SOCKET_PATH") {
|
||||
Some(occupied) => {
|
||||
if let Some(occupied) = socket_vars.get("KEYFORKD_SOCKET_PATH") {
|
||||
socket_path = PathBuf::from(occupied);
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
socket_path = PathBuf::from(
|
||||
socket_vars
|
||||
.get("XDG_RUNTIME_DIR")
|
||||
|
@ -250,7 +251,6 @@ pub fn get_socket() -> Result<UnixStream, Error> {
|
|||
);
|
||||
socket_path.extend(["keyforkd", "keyforkd.sock"]);
|
||||
}
|
||||
}
|
||||
UnixStream::connect(&socket_path).map_err(|e| Error::Connect(e, socket_path))
|
||||
}
|
||||
|
||||
|
@ -266,7 +266,7 @@ pub struct Client {
|
|||
|
||||
impl Client {
|
||||
/// Create a new client from a given already-connected [`UnixStream`]. This function is
|
||||
/// provided in case a specific UnixStream has to be used; otherwise,
|
||||
/// provided in case a specific `UnixStream` has to be used; otherwise,
|
||||
/// [`Client::discover_socket`] should be preferred.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -344,7 +344,7 @@ impl Client {
|
|||
return Err(Error::InvalidResponse);
|
||||
}
|
||||
|
||||
let depth = path.len() as u8;
|
||||
let depth = u8::try_from(path.len())?;
|
||||
ExtendedPrivateKey::from_parts(&d.data, depth, d.chain_code)
|
||||
.map_err(|_| Error::InvalidKey)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.0"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.4"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -89,14 +89,10 @@ pub async fn start_and_run_server(mnemonic: Mnemonic) -> Result<(), Box<dyn std:
|
|||
let runtime_vars = std::env::vars()
|
||||
.filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
let mut runtime_path: PathBuf;
|
||||
#[allow(clippy::single_match_else)]
|
||||
match runtime_vars.get("KEYFORKD_SOCKET_PATH") {
|
||||
Some(occupied) => {
|
||||
runtime_path = PathBuf::from(occupied);
|
||||
}
|
||||
None => {
|
||||
runtime_path = PathBuf::from(
|
||||
let runtime_path = if let Some(occupied) = runtime_vars.get("KEYFORKD_SOCKET_PATH") {
|
||||
PathBuf::from(occupied)
|
||||
} else {
|
||||
let mut runtime_path = PathBuf::from(
|
||||
runtime_vars
|
||||
.get("XDG_RUNTIME_DIR")
|
||||
.ok_or(KeyforkdError::NoSocketPath)?,
|
||||
|
@ -108,8 +104,8 @@ pub async fn start_and_run_server(mnemonic: Mnemonic) -> Result<(), Box<dyn std:
|
|||
tokio::fs::create_dir(&runtime_path).await?;
|
||||
}
|
||||
runtime_path.push("keyforkd.sock");
|
||||
}
|
||||
}
|
||||
runtime_path
|
||||
};
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
debug!(
|
||||
|
|
|
@ -49,7 +49,8 @@ impl IsDisconnect for EncodeError {
|
|||
}
|
||||
|
||||
impl UnixServer {
|
||||
/// Bind a socket to the given `address` and create a [`UnixServer`]. This function also creates a ctrl_c handler to automatically clean up the socket file.
|
||||
/// Bind a socket to the given `address` and create a [`UnixServer`]. This function also
|
||||
/// creates a `ctrl_c` handler to automatically clean up the socket file.
|
||||
///
|
||||
/// # Errors
|
||||
/// This function may return an error if the socket can't be bound.
|
||||
|
|
|
@ -174,7 +174,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "InvalidDerivationLength(0)")]
|
||||
#[tokio::test]
|
||||
async fn errors_on_no_path() {
|
||||
let tests = [(
|
||||
|
@ -200,7 +200,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "InvalidDerivationLength(1)")]
|
||||
#[tokio::test]
|
||||
async fn errors_on_short_path() {
|
||||
let tests = [(
|
||||
|
|
|
@ -89,7 +89,7 @@ where
|
|||
.expect(bug!("couldn't send server start signal"));
|
||||
let service = ServiceBuilder::new()
|
||||
.layer(middleware::BincodeLayer::new())
|
||||
.service(Keyforkd::new(seed.to_vec()));
|
||||
.service(Keyforkd::new(seed.clone()));
|
||||
server
|
||||
.run(service)
|
||||
.await
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.2"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.5"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = ["bin"]
|
||||
|
|
|
@ -74,7 +74,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|||
///
|
||||
/// # Errors
|
||||
/// The function may error for any condition mentioned in [`Error`].
|
||||
pub fn derive(xprv: XPrv, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
|
||||
pub fn derive(xprv: &XPrv, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
|
||||
let primary_key_flags = match keys.first() {
|
||||
Some(kf) if kf.for_certification() => kf,
|
||||
_ => return Err(Error::NotCert),
|
||||
|
|
|
@ -120,7 +120,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||
.map(|kt| kt.inner().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let cert = keyfork_derive_openpgp::derive(derived_xprv, subkeys.as_slice(), &default_userid)?;
|
||||
let cert = keyfork_derive_openpgp::derive(&derived_xprv, subkeys.as_slice(), &default_userid)?;
|
||||
|
||||
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.3"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||
|
||||
|
@ -11,7 +11,7 @@ pub mod paths {
|
|||
use super::*;
|
||||
|
||||
/// The default derivation path for OpenPGP.
|
||||
pub static OPENPGP: Lazy<DerivationPath> = Lazy::new(|| {
|
||||
pub static OPENPGP: LazyLock<DerivationPath> = LazyLock::new(|| {
|
||||
DerivationPath::default().chain_push(DerivationIndex::new_unchecked(
|
||||
u32::from_be_bytes(*b"\x00pgp"),
|
||||
true,
|
||||
|
@ -19,7 +19,7 @@ pub mod paths {
|
|||
});
|
||||
|
||||
/// The derivation path for OpenPGP certificates used for sharding.
|
||||
pub static OPENPGP_SHARD: Lazy<DerivationPath> = Lazy::new(|| {
|
||||
pub static OPENPGP_SHARD: LazyLock<DerivationPath> = LazyLock::new(|| {
|
||||
DerivationPath::default()
|
||||
.chain_push(DerivationIndex::new_unchecked(
|
||||
u32::from_be_bytes(*b"\x00pgp"),
|
||||
|
@ -32,7 +32,7 @@ pub mod paths {
|
|||
});
|
||||
|
||||
/// The derivation path for OpenPGP certificates used for disaster recovery.
|
||||
pub static OPENPGP_DISASTER_RECOVERY: Lazy<DerivationPath> = Lazy::new(|| {
|
||||
pub static OPENPGP_DISASTER_RECOVERY: LazyLock<DerivationPath> = LazyLock::new(|| {
|
||||
DerivationPath::default()
|
||||
.chain_push(DerivationIndex::new_unchecked(
|
||||
u32::from_be_bytes(*b"\x00pgp"),
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.2.2"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -52,7 +52,7 @@ pub struct VariableLengthSeed<'a> {
|
|||
}
|
||||
|
||||
impl<'a> VariableLengthSeed<'a> {
|
||||
/// Create a new VariableLengthSeed.
|
||||
/// Create a new `VariableLengthSeed`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
|
@ -167,6 +167,7 @@ where
|
|||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(*seed);
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn new(seed: impl as_private_key::AsPrivateKey) -> Result<Self> {
|
||||
Self::new_internal(seed.as_private_key())
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ mod tests {
|
|||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "IndexTooLarge")]
|
||||
fn fails_on_high_index() {
|
||||
DerivationIndex::new(0x8000_0001, false).unwrap();
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "IndexTooLarge")]
|
||||
fn from_str_fails_on_high_index() {
|
||||
DerivationIndex::from_str(&0x8000_0001u32.to_string()).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::module_name_repetitions, clippy::must_use_candidate)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod extended_key;
|
||||
|
|
|
@ -11,7 +11,7 @@ pub enum Error {
|
|||
|
||||
/// The path could not be parsed due to a bad prefix. Paths must be in the format:
|
||||
///
|
||||
/// m [/ index [']]+
|
||||
/// `m [/ index [']]+`
|
||||
///
|
||||
/// The prefix for the path must be `m`, and all indices must be integers between 0 and
|
||||
/// 2^31.
|
||||
|
@ -35,8 +35,8 @@ impl DerivationPath {
|
|||
self.path.iter()
|
||||
}
|
||||
|
||||
/// The amount of segments in the DerivationPath. For consistency, a [`usize`] is returned, but
|
||||
/// BIP-0032 dictates that the depth should be no larger than `255`, [`u8::MAX`].
|
||||
/// The amount of segments in the [`DerivationPath`]. For consistency, a [`usize`] is returned,
|
||||
/// but BIP-0032 dictates that the depth should be no larger than `255`, [`u8::MAX`].
|
||||
pub fn len(&self) -> usize {
|
||||
self.path.len()
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ mod tests {
|
|||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "UnknownPathPrefix")]
|
||||
fn requires_master_path() {
|
||||
DerivationPath::from_str("1234/5678'").unwrap();
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ pub struct TestPublicKey {
|
|||
}
|
||||
|
||||
impl TestPublicKey {
|
||||
/// Create a new TestPublicKey from the given bytes.
|
||||
/// Create a new [`TestPublicKey`] from the given bytes.
|
||||
#[allow(dead_code)]
|
||||
pub fn from_bytes(b: &[u8]) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -102,7 +102,7 @@ fn ed25519() {
|
|||
|
||||
#[cfg(feature = "ed25519")]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "HardenedDerivationRequired")]
|
||||
fn panics_with_unhardened_derivation() {
|
||||
use ed25519_dalek::SigningKey;
|
||||
|
||||
|
@ -114,7 +114,7 @@ fn panics_with_unhardened_derivation() {
|
|||
|
||||
#[cfg(feature = "ed25519")]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "Depth")]
|
||||
fn panics_at_depth() {
|
||||
use ed25519_dalek::SigningKey;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.3.4"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -13,7 +13,7 @@ fn run() -> Result<()> {
|
|||
match args.as_slice() {
|
||||
[] => (),
|
||||
_ => panic!("Usage: {program_name}"),
|
||||
};
|
||||
}
|
||||
|
||||
let mut bytes = vec![];
|
||||
remote_decrypt(&mut bytes)?;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![allow(clippy::expect_fun_call)]
|
||||
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
|
@ -92,9 +91,10 @@ pub trait KeyDiscovery<F: Format + ?Sized> {
|
|||
/// # Errors
|
||||
/// The method may return an error if private keys could not be loaded from the given
|
||||
/// discovery mechanism. Keys may exist off-system (such as with smartcards), in which case the
|
||||
/// PrivateKeyData type of the asssociated format should be either `()` (if the keys may never
|
||||
/// exist on-system) or an empty container (such as an empty Vec); in either case, this method
|
||||
/// _must not_ return an error if keys are accessible but can't be transferred into memory.
|
||||
/// `PrivateKeyData` type of the asssociated format should be either `()` (if the keys may
|
||||
/// never exist on-system) or an empty container (such as an empty Vec); in either case, this
|
||||
/// method _must not_ return an error if keys are accessible but can't be transferred into
|
||||
/// memory.
|
||||
fn discover_private_keys(&self) -> Result<F::PrivateKeyData, F::Error>;
|
||||
}
|
||||
|
||||
|
@ -121,8 +121,8 @@ pub trait Format {
|
|||
/// Format a header containing necessary metadata. Such metadata contains a version byte, a
|
||||
/// threshold byte, a public version of the [`Format::SigningKey`], and the public keys used to
|
||||
/// encrypt shards. The public keys must be kept _in order_ to the encrypted shards. Keyfork
|
||||
/// will use the same key_data for both, ensuring an iteration of this method will match with
|
||||
/// iterations in methods called later.
|
||||
/// will use the same `key_data` for both, ensuring an iteration of this method will match
|
||||
/// with iterations in methods called later.
|
||||
///
|
||||
/// # Errors
|
||||
/// The method may return an error if encryption to any of the public keys fails.
|
||||
|
@ -263,6 +263,7 @@ pub trait Format {
|
|||
/// The method may return an error if a share can't be decrypted. The method will not return an
|
||||
/// error if the camera is inaccessible or if a hardware error is encountered while scanning a
|
||||
/// QR code; instead, a mnemonic prompt will be used.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn decrypt_one_shard_for_transport(
|
||||
&self,
|
||||
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
||||
|
@ -326,14 +327,14 @@ pub trait Format {
|
|||
if choice == RetryScanMnemonic::Continue {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if QR code scanning failed or was unavailable, read from a set of mnemonics
|
||||
let their_pubkey = match pubkey_data {
|
||||
Some(pubkey) => pubkey,
|
||||
None => {
|
||||
let their_pubkey = if let Some(pubkey) = pubkey_data {
|
||||
pubkey
|
||||
} else {
|
||||
let validator = MnemonicValidator {
|
||||
word_length: Some(WordLength::Count(24)),
|
||||
};
|
||||
|
@ -347,20 +348,6 @@ pub trait Format {
|
|||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| InvalidData)?
|
||||
/*
|
||||
prompt
|
||||
.lock()
|
||||
.expect(bug!(POISONED_MUTEX))
|
||||
.prompt_validated_wordlist::<English, _>(
|
||||
QRCODE_COULDNT_READ,
|
||||
3,
|
||||
validator.to_fn(),
|
||||
)?
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| InvalidData)?
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
// create our shared key
|
||||
|
@ -401,7 +388,6 @@ pub trait Format {
|
|||
// NOTE: Previous versions of Keyfork Shard would modify the padding bytes to avoid
|
||||
// duplicate mnemonic words. This version does not include that, and instead uses a
|
||||
// repeated length byte.
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let mut plaintext_bytes = [u8::try_from(payload.len()).expect(bug!(
|
||||
"previously asserted length must be < {PLAINTEXT_LENGTH}",
|
||||
PLAINTEXT_LENGTH = PLAINTEXT_LENGTH
|
||||
|
@ -481,11 +467,11 @@ pub trait Format {
|
|||
"must have less than u8::MAX public keys"
|
||||
);
|
||||
assert_eq!(
|
||||
max,
|
||||
public_keys.len() as u8,
|
||||
max as usize,
|
||||
public_keys.len(),
|
||||
"max must be equal to amount of public keys"
|
||||
);
|
||||
let max = public_keys.len() as u8;
|
||||
let max = u8::try_from(public_keys.len()).expect(bug!("invalid max: {max}", max = max));
|
||||
assert!(max >= threshold, "threshold must not exceed max keys");
|
||||
|
||||
let header = self.format_encrypted_header(&signing_key, &public_keys, threshold)?;
|
||||
|
@ -547,6 +533,7 @@ static QRCODE_TIMEOUT: LazyLock<u64> = LazyLock::new(|| {
|
|||
/// # Panics
|
||||
/// The function may panic if it is given payloads generated using a version of Keyfork that is
|
||||
/// incompatible with the currently running version.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut pm = keyfork_prompt::default_handler()?;
|
||||
|
||||
|
@ -627,13 +614,13 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
if choice == RetryScanMnemonic::Continue {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (pubkey, payload) = match (pubkey_data, payload_data) {
|
||||
(Some(pubkey), Some(payload)) => (pubkey, payload),
|
||||
_ => {
|
||||
let (pubkey, payload) = if let Some((pubkey, payload)) = pubkey_data.zip(payload_data) {
|
||||
(pubkey, payload)
|
||||
} else {
|
||||
let validator = MnemonicSetValidator {
|
||||
word_lengths: [24, 39],
|
||||
};
|
||||
|
@ -650,7 +637,6 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
.map_err(|_| InvalidData)?;
|
||||
let payload = payload_mnemonic.to_bytes();
|
||||
(pubkey, payload)
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
|
@ -677,17 +663,14 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
let payload = shared_key.decrypt(nonce, payload.as_slice())?;
|
||||
assert_eq!(HUNK_VERSION, payload[0], "Incompatible hunk version");
|
||||
|
||||
match &mut iter_count {
|
||||
Some(n) => {
|
||||
if let Some(n) = &mut iter_count {
|
||||
// Must be > 0 to start loop, can't go lower
|
||||
*n -= 1;
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
// NOTE: Should always be >= 1, < 256 due to Shamir constraints
|
||||
threshold = payload[1];
|
||||
let _ = iter_count.insert(threshold - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let payload_len = payload.last().expect(bug!("payload should not be empty"));
|
||||
shares.push(payload[HUNK_OFFSET..usize::from(*payload_len)].to_vec());
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! OpenPGP Shard functionality.
|
||||
|
||||
#![allow(clippy::expect_fun_call)]
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
|
@ -94,7 +92,7 @@ pub struct EncryptedMessage {
|
|||
}
|
||||
|
||||
impl EncryptedMessage {
|
||||
/// Create a new EncryptedMessage from known parts.
|
||||
/// Create a new [`EncryptedMessage`] from known parts.
|
||||
pub fn new(pkesks: &mut Vec<PKESK>, seip: SEIP) -> Self {
|
||||
Self {
|
||||
pkesks: std::mem::take(pkesks),
|
||||
|
@ -160,7 +158,7 @@ impl EncryptedMessage {
|
|||
/// Decrypt the message with a Sequoia policy and decryptor.
|
||||
///
|
||||
/// This method creates a container containing the packets and passes the serialized container
|
||||
/// to a DecryptorBuilder, which is used to decrypt the message.
|
||||
/// to a `DecryptorBuilder`, which is used to decrypt the message.
|
||||
///
|
||||
/// # Errors
|
||||
/// The method may return an error if it is unable to rebuild the message to decrypt or if it
|
||||
|
@ -265,7 +263,7 @@ impl Format for OpenPGP {
|
|||
.derive_path(&path)
|
||||
.expect(bug!("valid derivation"));
|
||||
keyfork_derive_openpgp::derive(
|
||||
xprv,
|
||||
&xprv,
|
||||
&[KeyFlags::empty().set_certification().set_signing()],
|
||||
&userid,
|
||||
)
|
||||
|
@ -450,8 +448,8 @@ impl Format for OpenPGP {
|
|||
// We don't want to invalidate someone's keys just because the old sig expired.
|
||||
let policy = NullPolicy::new();
|
||||
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone())?;
|
||||
let mut manager = SmartcardManager::new(prompt.clone())?;
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone());
|
||||
let mut manager = SmartcardManager::new(prompt.clone());
|
||||
|
||||
let mut encrypted_messages = encrypted_data.iter();
|
||||
|
||||
|
@ -482,9 +480,9 @@ impl Format for OpenPGP {
|
|||
|
||||
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
||||
if left_from_threshold > 0 {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let new_messages = decrypt_with_manager(
|
||||
left_from_threshold as u8,
|
||||
u8::try_from(left_from_threshold)
|
||||
.expect(bug!("threshold too large: {}", left_from_threshold)),
|
||||
&mut messages,
|
||||
&certs,
|
||||
&policy,
|
||||
|
@ -509,8 +507,8 @@ impl Format for OpenPGP {
|
|||
) -> std::result::Result<(Share, u8), Self::Error> {
|
||||
let policy = NullPolicy::new();
|
||||
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone())?;
|
||||
let mut manager = SmartcardManager::new(prompt.clone())?;
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone());
|
||||
let mut manager = SmartcardManager::new(prompt.clone());
|
||||
|
||||
let mut encrypted_messages = encrypted_data.iter();
|
||||
|
||||
|
@ -557,8 +555,8 @@ impl Format for OpenPGP {
|
|||
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
||||
) -> std::result::Result<(u8, Vec<Self::PublicKey>), Self::Error> {
|
||||
let policy = NullPolicy::new();
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone())?;
|
||||
let mut manager = SmartcardManager::new(prompt.clone())?;
|
||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone());
|
||||
let mut manager = SmartcardManager::new(prompt.clone());
|
||||
let mut encrypted_messages = encrypted_data.iter();
|
||||
|
||||
let metadata = encrypted_messages
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(clippy::expect_fun_call)]
|
||||
|
||||
use std::{rc::Rc, sync::Mutex};
|
||||
|
||||
use keyfork_bug::{bug, POISONED_MUTEX};
|
||||
|
@ -25,8 +23,6 @@ pub enum Error {
|
|||
Prompt(#[from] PromptError),
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
pub struct Keyring {
|
||||
full_certs: Vec<Cert>,
|
||||
root: Option<Cert>,
|
||||
|
@ -34,12 +30,12 @@ pub struct Keyring {
|
|||
}
|
||||
|
||||
impl Keyring {
|
||||
pub fn new(certs: impl AsRef<[Cert]>, p: Rc<Mutex<Box<dyn PromptHandler>>>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
pub fn new(certs: impl AsRef<[Cert]>, p: Rc<Mutex<Box<dyn PromptHandler>>>) -> Self {
|
||||
Self {
|
||||
full_certs: certs.as_ref().to_vec(),
|
||||
root: Default::default(),
|
||||
root: Option::default(),
|
||||
pm: p,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(clippy::expect_fun_call)]
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
rc::Rc,
|
||||
|
@ -81,13 +79,13 @@ pub struct SmartcardManager {
|
|||
}
|
||||
|
||||
impl SmartcardManager {
|
||||
pub fn new(p: Rc<Mutex<Box<dyn PromptHandler>>>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
pub fn new(p: Rc<Mutex<Box<dyn PromptHandler>>>) -> Self {
|
||||
Self {
|
||||
current_card: None,
|
||||
root: None,
|
||||
pm: p,
|
||||
pin_cache: Default::default(),
|
||||
})
|
||||
pin_cache: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the root cert, returning the old cert
|
||||
|
@ -177,10 +175,9 @@ impl SmartcardManager {
|
|||
|
||||
impl VerificationHelper for &mut SmartcardManager {
|
||||
fn get_certs(&mut self, ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
||||
#[allow(clippy::flat_map_option)]
|
||||
Ok(ids
|
||||
.iter()
|
||||
.flat_map(|kh| self.root.as_ref().filter(|cert| cert.key_handle() == *kh))
|
||||
.filter_map(|kh| self.root.as_ref().filter(|cert| cert.key_handle() == *kh))
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
@ -281,8 +278,7 @@ impl DecryptionHelper for &mut SmartcardManager {
|
|||
let temp_pin = prompt_validated_passphrase(&mut **prompt, &message, 3, &pin_validator)?;
|
||||
let verification_status = transaction.verify_user_pin(temp_pin.as_str().trim());
|
||||
match verification_status {
|
||||
#[allow(clippy::ignored_unit_patterns)]
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
self.pin_cache.insert(fp.clone(), temp_pin.clone());
|
||||
pin.replace(temp_pin);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.3.3"
|
|||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"completion",
|
||||
|
|
|
@ -43,7 +43,7 @@ impl FromStr for Options {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.is_empty() {
|
||||
return Ok(Default::default());
|
||||
return Ok(Self::default());
|
||||
}
|
||||
let values = s
|
||||
.split(',')
|
||||
|
|
|
@ -26,9 +26,9 @@ pub trait Deriver {
|
|||
|
||||
fn derivation_path(&self) -> DerivationPath;
|
||||
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()>;
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()>;
|
||||
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()>;
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Clone, Debug)]
|
||||
|
@ -170,9 +170,9 @@ impl DeriveSubcommands {
|
|||
let xprv = Client::discover_socket()?
|
||||
.request_xprv::<<OpenPGP as Deriver>::Prv>(&path.chain_push(account))?;
|
||||
if is_public {
|
||||
opgp.derive_public_with_xprv(writer, xprv)
|
||||
opgp.derive_public_with_xprv(writer, &xprv)
|
||||
} else {
|
||||
opgp.derive_with_xprv(writer, xprv)
|
||||
opgp.derive_with_xprv(writer, &xprv)
|
||||
}
|
||||
}
|
||||
DeriveSubcommands::Key(key) => {
|
||||
|
@ -180,9 +180,9 @@ impl DeriveSubcommands {
|
|||
let xprv = Client::discover_socket()?
|
||||
.request_xprv::<<Key as Deriver>::Prv>(&path.chain_push(account))?;
|
||||
if is_public {
|
||||
key.derive_public_with_xprv(writer, xprv)
|
||||
key.derive_public_with_xprv(writer, &xprv)
|
||||
} else {
|
||||
key.derive_with_xprv(writer, xprv)
|
||||
key.derive_with_xprv(writer, &xprv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ impl DeriveSubcommands {
|
|||
}
|
||||
|
||||
impl OpenPGP {
|
||||
fn cert_from_xprv(&self, xprv: keyfork_derive_openpgp::XPrv) -> Result<Cert> {
|
||||
fn cert_from_xprv(&self, xprv: &keyfork_derive_openpgp::XPrv) -> Result<Cert> {
|
||||
let subkeys = vec![
|
||||
KeyFlags::empty().set_certification(),
|
||||
KeyFlags::empty().set_signing(),
|
||||
|
@ -213,15 +213,12 @@ impl Deriver for OpenPGP {
|
|||
self.derivation_path.derivation_path()
|
||||
}
|
||||
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()> {
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()> {
|
||||
let cert = self.cert_from_xprv(xprv)?;
|
||||
let writer = match writer {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let writer = if let Some(writer) = writer { writer } else {
|
||||
let path = PathBuf::from(cert.fingerprint().to_string()).with_extension("asc");
|
||||
let file = create(&path)?;
|
||||
Box::new(file)
|
||||
}
|
||||
};
|
||||
let mut writer = Writer::new(writer, Kind::SecretKey)?;
|
||||
for packet in cert.as_tsk().into_packets() {
|
||||
|
@ -231,15 +228,12 @@ impl Deriver for OpenPGP {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()> {
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()> {
|
||||
let cert = self.cert_from_xprv(xprv)?;
|
||||
let writer = match writer {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let writer = if let Some(writer) = writer { writer } else {
|
||||
let path = PathBuf::from(cert.fingerprint().to_string()).with_extension("asc");
|
||||
let file = create(&path)?;
|
||||
Box::new(file)
|
||||
}
|
||||
};
|
||||
let mut writer = Writer::new(writer, Kind::PublicKey)?;
|
||||
for packet in cert.into_packets2() {
|
||||
|
@ -259,7 +253,7 @@ impl Deriver for Key {
|
|||
DerivationPath::default().chain_push(self.slug.0.clone())
|
||||
}
|
||||
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()> {
|
||||
fn derive_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()> {
|
||||
let (formatted, ext) = match self.format {
|
||||
KeyFormat::Hex => (smex::encode(xprv.private_key().to_bytes()), "hex"),
|
||||
KeyFormat::Base64 => {
|
||||
|
@ -278,7 +272,7 @@ impl Deriver for Key {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: XPrv<Self::Prv>) -> Result<()> {
|
||||
fn derive_public_with_xprv(&self, writer: OptWrite, xprv: &XPrv<Self::Prv>) -> Result<()> {
|
||||
let (formatted, ext) = match self.format {
|
||||
KeyFormat::Hex => (smex::encode(xprv.public_key().to_bytes()), "hex"),
|
||||
KeyFormat::Base64 => {
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{clap_ext::*, config, openpgp_card::factory_reset_current_card};
|
|||
use card_backend_pcsc::PcscBackend;
|
||||
use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Display,
|
||||
fs::File,
|
||||
io::{IsTerminal, Write},
|
||||
|
@ -164,7 +164,7 @@ pub enum Error {
|
|||
MissingOption(&'static str),
|
||||
}
|
||||
|
||||
fn context_stub<'a>(path: &'a Path) -> impl Fn(std::io::Error) -> Error + 'a {
|
||||
fn context_stub(path: &Path) -> impl Fn(std::io::Error) -> Error + use<'_> {
|
||||
|e| Error::IOContext(e, path.to_path_buf())
|
||||
}
|
||||
|
||||
|
@ -227,13 +227,13 @@ pub enum MnemonicSubcommands {
|
|||
///
|
||||
/// The following additional arguments are available:
|
||||
///
|
||||
/// * threshold, m: the minimum amount of shares required to reconstitute the shard. By
|
||||
/// * `threshold`, m: the minimum amount of shares required to reconstitute the shard. By
|
||||
/// default, this is the amount of certificates provided.
|
||||
///
|
||||
/// * max, n: the maximum amount of shares. When provided, this is used to ensure the
|
||||
/// * `max`, n: the maximum amount of shares. When provided, this is used to ensure the
|
||||
/// certificate count is correct. This is required when using `threshold` or `m`.
|
||||
///
|
||||
/// * output: the file to write the generated Shardfile to. By default, assuming the
|
||||
/// * `output`: the file to write the generated Shardfile to. By default, assuming the
|
||||
/// certificate input is `input.asc`, the generated Shardfile would be written to
|
||||
/// `input.shard.asc`.
|
||||
#[arg(long)]
|
||||
|
@ -256,13 +256,13 @@ pub enum MnemonicSubcommands {
|
|||
///
|
||||
/// The following additional arguments are required:
|
||||
///
|
||||
/// * threshold, m: the minimum amount of shares required to reconstitute the shard.
|
||||
/// * `threshold`, m: the minimum amount of shares required to reconstitute the shard.
|
||||
///
|
||||
/// * max, n: the maximum amount of shares.
|
||||
/// * `max`, n: the maximum amount of shares.
|
||||
///
|
||||
/// * cards_per_shard: the amount of OpenPGP smartcards to provision per shardholder.
|
||||
/// * `cards_per_shard`: the amount of OpenPGP smartcards to provision per shardholder.
|
||||
///
|
||||
/// * cert_output: the file to write all generated OpenPGP certificates to; if not
|
||||
/// * `cert_output`: the file to write all generated OpenPGP certificates to; if not
|
||||
/// provided, files will be automatically generated for each certificate.
|
||||
#[arg(long)]
|
||||
shard_to_self: Option<ValueWithOptions<PathBuf>>,
|
||||
|
@ -302,9 +302,9 @@ fn determine_valid_output_path<T: AsRef<Path>>(
|
|||
mid_ext: &str,
|
||||
optional_path: Option<T>,
|
||||
) -> PathBuf {
|
||||
match optional_path {
|
||||
Some(p) => p.as_ref().to_path_buf(),
|
||||
None => {
|
||||
if let Some(p) = optional_path {
|
||||
p.as_ref().to_path_buf()
|
||||
} else {
|
||||
let extension = match path.extension() {
|
||||
Some(ext) => format!("{mid_ext}.{ext}", ext = ext.to_string_lossy()),
|
||||
None => format!("{mid_ext}.asc"),
|
||||
|
@ -312,14 +312,13 @@ fn determine_valid_output_path<T: AsRef<Path>>(
|
|||
path.with_extension(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_extension_armored(path: &Path) -> bool {
|
||||
match path.extension().and_then(|s| s.to_str()) {
|
||||
Some("pgp") | Some("gpg") => false,
|
||||
Some("pgp" | "gpg") => false,
|
||||
Some("asc") => true,
|
||||
_ => {
|
||||
eprintln!("unable to determine whether to armor file: {path:?}");
|
||||
eprintln!("unable to determine whether to armor file: {path}", path = path.display());
|
||||
eprintln!("use .gpg, .pgp, or .asc extension, or `armor=true`");
|
||||
eprintln!("defaulting to armored");
|
||||
true
|
||||
|
@ -394,8 +393,11 @@ fn do_encrypt_to_self(
|
|||
.clone()
|
||||
.chain_push(account);
|
||||
|
||||
let cert =
|
||||
keyfork_derive_openpgp::derive(xprv.derive_path(&derivation_path)?, &subkeys, &userid)?;
|
||||
let cert = keyfork_derive_openpgp::derive(
|
||||
&xprv.derive_path(&derivation_path)?,
|
||||
&subkeys,
|
||||
&userid,
|
||||
)?;
|
||||
|
||||
certs.push(cert);
|
||||
}
|
||||
|
@ -458,12 +460,9 @@ fn do_shard(
|
|||
return Err(MissingThresholdOrMax.into());
|
||||
}
|
||||
|
||||
let (threshold, max) = match threshold.zip(max) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
let (threshold, max) = if let Some(t) = threshold.zip(max) { t } else {
|
||||
let len = u8::try_from(certs.len())?;
|
||||
(len, len)
|
||||
}
|
||||
};
|
||||
|
||||
let openpgp = keyfork_shard::openpgp::OpenPGP;
|
||||
|
@ -550,7 +549,7 @@ fn derive_key(seed: [u8; 64], index: u8) -> Result<openpgp::Cert, Box<dyn std::e
|
|||
.expect("could not construct master key from seed")
|
||||
.derive_path(&path)?;
|
||||
let userid = UserID::from(format!("Keyfork Shard {index}"));
|
||||
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
|
||||
let cert = keyfork_derive_openpgp::derive(&xprv, &subkeys, &userid)?;
|
||||
Ok(cert)
|
||||
}
|
||||
|
||||
|
@ -626,8 +625,8 @@ fn do_shard_to_self(
|
|||
for i in 0..cards_per_shard.unwrap_or(1) {
|
||||
pm.prompt_message(keyfork_prompt::Message::Text(format!(
|
||||
"Please remove all keys and insert key #{} for user #{}",
|
||||
(i as u16) + 1,
|
||||
(index as u16) + 1,
|
||||
(u16::from(i)) + 1,
|
||||
(u16::from(index)) + 1,
|
||||
)))?;
|
||||
let card_backend = loop {
|
||||
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
||||
|
@ -671,7 +670,7 @@ fn do_shard_to_self(
|
|||
let output = File::create(path)?;
|
||||
opgp.shard_and_encrypt(
|
||||
threshold,
|
||||
certs.len() as u8,
|
||||
u8::try_from(certs.len()).expect("provided more than u8::MAX certs"),
|
||||
mnemonic.as_bytes(),
|
||||
&certs[..],
|
||||
output,
|
||||
|
@ -742,10 +741,10 @@ fn do_provision(
|
|||
metadata @ None => {
|
||||
*metadata = Some(config.clone());
|
||||
}
|
||||
};
|
||||
}
|
||||
provision
|
||||
.provisioner_name
|
||||
.provision_with_mnemonic(mnemonic, provisioner)?;
|
||||
.provision_with_mnemonic(mnemonic, &provisioner)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -772,11 +771,11 @@ fn do_derive(
|
|||
use keyfork_derive_openpgp::XPrv;
|
||||
let root_xprv = XPrv::new(mnemonic.generate_seed(None))?;
|
||||
let account = DerivationIndex::new(*account_id, true)?;
|
||||
let derived = root_xprv.derive_path(&opgp.derivation_path().chain_push(account))?;
|
||||
let derived_key = root_xprv.derive_path(&opgp.derivation_path().chain_push(account))?;
|
||||
if *public {
|
||||
opgp.derive_public_with_xprv(writer, derived)?;
|
||||
opgp.derive_public_with_xprv(writer, &derived_key)?;
|
||||
} else {
|
||||
opgp.derive_with_xprv(writer, derived)?;
|
||||
opgp.derive_with_xprv(writer, &derived_key)?;
|
||||
}
|
||||
}
|
||||
derive::Derive {
|
||||
|
@ -790,11 +789,11 @@ fn do_derive(
|
|||
use keyfork_derive_openpgp::XPrv;
|
||||
let root_xprv = XPrv::new(mnemonic.generate_seed(None))?;
|
||||
let account = DerivationIndex::new(*account_id, true)?;
|
||||
let derived = root_xprv.derive_path(&key.derivation_path().chain_push(account))?;
|
||||
let derived_key = root_xprv.derive_path(&key.derivation_path().chain_push(account))?;
|
||||
if *public {
|
||||
key.derive_public_with_xprv(writer, derived)?;
|
||||
key.derive_public_with_xprv(writer, &derived_key)?;
|
||||
} else {
|
||||
key.derive_with_xprv(writer, derived)?;
|
||||
key.derive_with_xprv(writer, &derived_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -802,6 +801,7 @@ fn do_derive(
|
|||
}
|
||||
|
||||
impl MnemonicSubcommands {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn handle(
|
||||
&self,
|
||||
_m: &Mnemonic,
|
||||
|
@ -831,13 +831,13 @@ impl MnemonicSubcommands {
|
|||
// * Sharding to existing, usable keys
|
||||
// * Sharding to newly provisioned keys
|
||||
let mut will_print_mnemonic =
|
||||
encrypt_to.is_none() || encrypt_to.as_ref().is_some_and(|e| e.is_empty());
|
||||
encrypt_to.is_none() || encrypt_to.as_ref().is_some_and(Vec::is_empty);
|
||||
will_print_mnemonic = will_print_mnemonic
|
||||
&& (encrypt_to_self.as_ref().is_none() || provision.as_ref().is_none());
|
||||
will_print_mnemonic = will_print_mnemonic && shard_to.is_none()
|
||||
|| shard_to.as_ref().is_some_and(|s| s.is_empty());
|
||||
|| shard_to.as_ref().is_some_and(Vec::is_empty);
|
||||
will_print_mnemonic = will_print_mnemonic && shard.is_none()
|
||||
|| shard.as_ref().is_some_and(|s| s.is_empty());
|
||||
|| shard.as_ref().is_some_and(Vec::is_empty);
|
||||
will_print_mnemonic = will_print_mnemonic && shard_to_self.is_none();
|
||||
|
||||
let mnemonic = source.handle(size)?;
|
||||
|
@ -859,7 +859,7 @@ impl MnemonicSubcommands {
|
|||
}
|
||||
|
||||
if let Some(encrypt_to_self) = encrypt_to_self {
|
||||
let mut accounts: std::collections::HashSet<u32> = Default::default();
|
||||
let mut accounts: HashSet<u32> = HashSet::default();
|
||||
if let Some(provision::Provision {
|
||||
provisioner_name: provision::Provisioner::OpenPGPCard(_),
|
||||
account_id,
|
||||
|
@ -938,7 +938,7 @@ impl MnemonicSubcommands {
|
|||
}
|
||||
|
||||
if will_print_mnemonic {
|
||||
println!("{}", mnemonic);
|
||||
println!("{mnemonic}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ impl Provisioner {
|
|||
|
||||
pub fn provision(
|
||||
&self,
|
||||
provisioner: config::Provisioner,
|
||||
provisioner: &config::Provisioner,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
Provisioner::OpenPGPCard(o) => {
|
||||
|
@ -49,7 +49,7 @@ impl Provisioner {
|
|||
.chain_push(account_index);
|
||||
let mut client = keyforkd_client::Client::discover_socket()?;
|
||||
let xprv: XPrv = client.request_xprv(&path)?;
|
||||
o.provision(xprv, provisioner)
|
||||
o.provision(&xprv, provisioner)
|
||||
}
|
||||
Provisioner::Shard(s) => {
|
||||
type Prv = <openpgp::Shard as ProvisionExec>::PrivateKey;
|
||||
|
@ -59,7 +59,7 @@ impl Provisioner {
|
|||
.chain_push(account_index);
|
||||
let mut client = keyforkd_client::Client::discover_socket()?;
|
||||
let xprv: XPrv = client.request_xprv(&path)?;
|
||||
s.provision(xprv, provisioner)
|
||||
s.provision(&xprv, provisioner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ impl Provisioner {
|
|||
pub fn provision_with_mnemonic(
|
||||
&self,
|
||||
mnemonic: &keyfork_mnemonic::Mnemonic,
|
||||
provisioner: config::Provisioner,
|
||||
provisioner: &config::Provisioner,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
Provisioner::OpenPGPCard(o) => {
|
||||
|
@ -77,7 +77,7 @@ impl Provisioner {
|
|||
let path = <openpgp::OpenPGPCard as ProvisionExec>::derivation_prefix()
|
||||
.chain_push(account_index);
|
||||
let xprv = XPrv::new(mnemonic.generate_seed(None))?.derive_path(&path)?;
|
||||
o.provision(xprv, provisioner)
|
||||
o.provision(&xprv, provisioner)
|
||||
}
|
||||
Provisioner::Shard(s) => {
|
||||
type Prv = <openpgp::Shard as ProvisionExec>::PrivateKey;
|
||||
|
@ -86,7 +86,7 @@ impl Provisioner {
|
|||
let path = <openpgp::Shard as ProvisionExec>::derivation_prefix()
|
||||
.chain_push(account_index);
|
||||
let xprv = XPrv::new(mnemonic.generate_seed(None))?.derive_path(&path)?;
|
||||
s.provision(xprv, provisioner)
|
||||
s.provision(&xprv, provisioner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ trait ProvisionExec {
|
|||
/// Derive a key and deploy it to a target.
|
||||
fn provision(
|
||||
&self,
|
||||
xprv: keyfork_derive_util::ExtendedPrivateKey<Self::PrivateKey>,
|
||||
p: config::Provisioner,
|
||||
xprv: &keyfork_derive_util::ExtendedPrivateKey<Self::PrivateKey>,
|
||||
p: &config::Provisioner,
|
||||
) -> Result<(), Box<dyn std::error::Error>>;
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ impl TryFrom<Provision> for config::Provisioner {
|
|||
Ok(Self {
|
||||
account: value.account_id,
|
||||
identifier: value.identifier.ok_or(MissingField("identifier"))?,
|
||||
metadata: Default::default(),
|
||||
metadata: Option::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -210,9 +210,9 @@ impl Provision {
|
|||
}
|
||||
}
|
||||
None => {
|
||||
let provisioner_with_identifier = match self.identifier {
|
||||
Some(_) => self.clone(),
|
||||
None => {
|
||||
let provisioner_with_identifier = if self.identifier.is_some() {
|
||||
self.clone()
|
||||
} else {
|
||||
let identifiers = self.provisioner_name.discover()?;
|
||||
let [id] = &identifiers[..] else {
|
||||
panic!("invalid amount of identifiers; pass --identifier");
|
||||
|
@ -221,10 +221,9 @@ impl Provision {
|
|||
identifier: Some(id.0.clone()),
|
||||
..self.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
let config = config::Provisioner::try_from(provisioner_with_identifier)?;
|
||||
self.provisioner_name.provision(config)?;
|
||||
self.provisioner_name.provision(&config)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -39,8 +39,8 @@ fn discover_cards() -> Result<CardList, Box<dyn std::error::Error>> {
|
|||
}
|
||||
|
||||
fn provision_card(
|
||||
provisioner: config::Provisioner,
|
||||
xprv: XPrv,
|
||||
provisioner: &config::Provisioner,
|
||||
xprv: &XPrv,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut pm = default_handler()?;
|
||||
|
||||
|
@ -59,23 +59,24 @@ fn provision_card(
|
|||
Some(userid) => UserID::from(userid.as_str()),
|
||||
None => UserID::from("Keyfork-Provisioned Key"),
|
||||
};
|
||||
let cert = keyfork_derive_openpgp::derive(xprv.clone(), &subkeys, &userid)?;
|
||||
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
|
||||
|
||||
if !provisioner
|
||||
.metadata
|
||||
.as_ref()
|
||||
.is_some_and(|m| m.contains_key("_skip_cert_output"))
|
||||
{
|
||||
let cert_output = match provisioner.metadata.as_ref().and_then(|m| m.get("output")) {
|
||||
Some(cert_output) => PathBuf::from(cert_output),
|
||||
None => {
|
||||
let cert_output = if let Some(cert_output) =
|
||||
provisioner.metadata.as_ref().and_then(|m| m.get("output"))
|
||||
{
|
||||
PathBuf::from(cert_output)
|
||||
} else {
|
||||
let path = PathBuf::from(cert.fingerprint().to_string()).with_extension("asc");
|
||||
eprintln!(
|
||||
"Writing OpenPGP certificate to: {path}",
|
||||
path = path.display()
|
||||
);
|
||||
path
|
||||
}
|
||||
};
|
||||
|
||||
let cert_output_file = std::fs::File::create(cert_output)?;
|
||||
|
@ -124,8 +125,8 @@ impl ProvisionExec for OpenPGPCard {
|
|||
|
||||
fn provision(
|
||||
&self,
|
||||
xprv: XPrv,
|
||||
provisioner: config::Provisioner,
|
||||
xprv: &XPrv,
|
||||
provisioner: &config::Provisioner,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
provision_card(provisioner, xprv)
|
||||
}
|
||||
|
@ -147,8 +148,8 @@ impl ProvisionExec for Shard {
|
|||
|
||||
fn provision(
|
||||
&self,
|
||||
xprv: XPrv,
|
||||
provisioner: config::Provisioner,
|
||||
xprv: &XPrv,
|
||||
provisioner: &config::Provisioner,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
provision_card(provisioner, xprv)
|
||||
}
|
||||
|
|
|
@ -34,11 +34,10 @@ pub fn get_new_pins(
|
|||
3,
|
||||
&user_pin_validator,
|
||||
)?;
|
||||
if user_pin != validated_user_pin {
|
||||
pm.prompt_message(Message::Text("User PINs did not match. Retrying.".into()))?;
|
||||
} else {
|
||||
if user_pin == validated_user_pin {
|
||||
break user_pin;
|
||||
}
|
||||
pm.prompt_message(Message::Text("User PINs did not match. Retrying.".into()))?;
|
||||
};
|
||||
|
||||
let admin_pin = loop {
|
||||
|
@ -54,11 +53,10 @@ pub fn get_new_pins(
|
|||
3,
|
||||
&admin_pin_validator,
|
||||
)?;
|
||||
if admin_pin != validated_admin_pin {
|
||||
pm.prompt_message(Message::Text("Admin PINs did not match. Retrying.".into()))?;
|
||||
} else {
|
||||
if admin_pin == validated_admin_pin {
|
||||
break admin_pin;
|
||||
}
|
||||
pm.prompt_message(Message::Text("Admin PINs did not match. Retrying.".into()))?;
|
||||
};
|
||||
|
||||
Ok((user_pin, admin_pin))
|
||||
|
|
|
@ -5,6 +5,9 @@ repository = "https://git.distrust.co/public/keyfork"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! Encoding and decoding QR codes.
|
||||
|
||||
#![allow(clippy::expect_fun_call)]
|
||||
|
||||
use keyfork_bug as bug;
|
||||
|
||||
use bug::POISONED_MUTEX;
|
||||
|
@ -39,10 +37,10 @@ pub enum QRCodeScanError {
|
|||
/// The camera could not load the requested format.
|
||||
#[error("Camera could not use {expected} format, instead used {actual}")]
|
||||
CameraGaveBadFormat {
|
||||
/// The expected format, in FourCC format.
|
||||
/// The expected format, in `FourCC` format.
|
||||
expected: String,
|
||||
|
||||
/// The actual format, in FourCC format.
|
||||
/// The actual format, in `FourCC` format.
|
||||
actual: String,
|
||||
},
|
||||
|
||||
|
@ -165,7 +163,7 @@ mod rqrr {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(dead_code, clippy::cast_precision_loss)]
|
||||
fn dbg_elapsed(count: u64, instant: Instant) {
|
||||
let elapsed = instant.elapsed().as_secs();
|
||||
let framerate = count as f64 / elapsed as f64;
|
||||
|
|
|
@ -5,6 +5,9 @@ repository = "https://git.distrust.co/public/keyfork"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(clippy::unreadable_literal, clippy::pub_underscore_fields)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
|
|
@ -5,6 +5,9 @@ repository = "https://git.distrust.co/public/keyfork"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -20,16 +20,13 @@ impl Image {
|
|||
|
||||
/// Link: [`sys::zbar_image_set_format`]
|
||||
///
|
||||
/// A FourCC code can be given in the format:
|
||||
/// A `FourCC` code can be given in the format:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// self.set_format(b"Y800")
|
||||
/// ```
|
||||
pub(crate) fn set_format(&mut self, fourcc: &[u8; 4]) {
|
||||
let fourcc: u64 = fourcc[0] as u64
|
||||
| ((fourcc[1] as u64) << 8)
|
||||
| ((fourcc[2] as u64) << 16)
|
||||
| ((fourcc[3] as u64) << 24);
|
||||
let fourcc = std::os::raw::c_ulong::from(u32::from_le_bytes(*fourcc));
|
||||
unsafe { sys::zbar_image_set_format(self.inner, fourcc) }
|
||||
}
|
||||
|
||||
|
@ -43,7 +40,7 @@ impl Image {
|
|||
/// Accepts raw data in the configured format. See: [`Image::set_format`]
|
||||
fn set_data(&mut self, data: Vec<u8>) {
|
||||
unsafe {
|
||||
sys::zbar_image_set_data(self.inner, data.as_ptr().cast(), data.len() as u64, None)
|
||||
sys::zbar_image_set_data(self.inner, data.as_ptr().cast(), data.len() as u64, None);
|
||||
}
|
||||
// keep data in self to avoid use after free when data goes out of scope
|
||||
let _ = self.inner_data.insert(data);
|
||||
|
|
|
@ -22,7 +22,7 @@ pub struct ImageScanner {
|
|||
}
|
||||
|
||||
impl ImageScanner {
|
||||
/// create a new ImageScanner.
|
||||
/// Create a new `ImageScanner`.
|
||||
///
|
||||
/// Link: [`sys::zbar_image_scanner_create`]
|
||||
pub fn new() -> Self {
|
||||
|
@ -31,7 +31,7 @@ impl ImageScanner {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set a configuration option for the ImageScanner.
|
||||
/// Set a configuration option.
|
||||
///
|
||||
/// Link: [`sys::zbar_image_scanner_set_config`]
|
||||
///
|
||||
|
@ -67,7 +67,7 @@ impl ImageScanner {
|
|||
let symbol_data = unsafe { sys::zbar_symbol_get_data(symbol) };
|
||||
let symbol_data_len = unsafe { sys::zbar_symbol_get_data_length(symbol) };
|
||||
let symbol_slice = unsafe {
|
||||
std::slice::from_raw_parts(symbol_data as *const u8, symbol_data_len as usize)
|
||||
std::slice::from_raw_parts(symbol_data.cast::<u8>(), symbol_data_len as usize)
|
||||
};
|
||||
result.push(Symbol::new(symbol_type, symbol_slice));
|
||||
symbol = unsafe { sys::zbar_symbol_next(symbol) };
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! A Symbol represents some form of encoded data.
|
||||
|
||||
#![allow(clippy::used_underscore_binding)]
|
||||
|
||||
use super::sys;
|
||||
|
||||
/// The type of symbol (i.e. what type of barcode or QR code).
|
||||
|
|
|
@ -5,6 +5,9 @@ edition = "2021"
|
|||
publish = false
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
assert_cmd = "2.0.16"
|
||||
keyforkd = { workspace = true, features = ["default"] }
|
||||
|
|
|
@ -52,7 +52,5 @@ fn test() {
|
|||
key.alive().expect("is live after being generated");
|
||||
key.parts_into_secret().expect("has secret keys");
|
||||
}
|
||||
if !key_formats.is_empty() {
|
||||
panic!("remaining key formats: {key_formats:?}");
|
||||
}
|
||||
assert!(key_formats.is_empty(), "remaining key formats: {key_formats:?}");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -45,6 +45,7 @@ use std::process::ExitCode;
|
|||
/// A result that may contain any error.
|
||||
pub type ProcessResult<T = ()> = Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn report_err(e: Box<dyn std::error::Error>) {
|
||||
eprintln!("Unable to run command: {e}");
|
||||
let mut source = e.source();
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.1"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -14,6 +14,9 @@ edition = "2021"
|
|||
rust-version = "1.58.0"
|
||||
# categories = ["command-line-interface", "command-line-utilities"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# [lib]
|
||||
# name = "crossterm"
|
||||
# path = "src/lib.rs"
|
||||
|
|
|
@ -61,6 +61,7 @@ impl Filter for EventFilter {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct InternalEventFilter;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(missing_docs, clippy::missing_errors_doc, clippy::missing_panics_doc)]
|
||||
#![deny(unused_imports, unused_must_use)]
|
||||
#![allow(clippy::pedantic, clippy::all, unexpected_cfgs)]
|
||||
|
||||
//! # Cross-platform Terminal Manipulation Library
|
||||
//!
|
||||
|
|
|
@ -140,7 +140,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_attributes_const() {
|
||||
const ATTRIBUTES: Attributes = Attributes::none().with(Attribute::Bold).with(Attribute::Italic).without(Attribute::Bold);
|
||||
const ATTRIBUTES: Attributes = Attributes::none()
|
||||
.with(Attribute::Bold)
|
||||
.with(Attribute::Italic)
|
||||
.without(Attribute::Bold);
|
||||
assert!(!ATTRIBUTES.has(Attribute::Bold));
|
||||
assert!(ATTRIBUTES.has(Attribute::Italic));
|
||||
}
|
||||
|
|
|
@ -108,7 +108,10 @@ pub struct FdTerminal {
|
|||
stored_termios: Option<libc::termios>,
|
||||
}
|
||||
|
||||
impl<T> From<T> for FdTerminal where T: os::fd::AsRawFd {
|
||||
impl<T> From<T> for FdTerminal
|
||||
where
|
||||
T: os::fd::AsRawFd,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
fd: value.as_raw_fd(),
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.2"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -6,6 +6,9 @@ repository = "https://git.distrust.co/public/keyfork"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
default = ["bin"]
|
||||
bin = ["smex"]
|
||||
|
|
|
@ -382,12 +382,12 @@ where
|
|||
|
||||
/// A clone of the internal representation of the decoded data.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.data.to_vec()
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
/// A clone of the internal representation of the decoded data.
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.data.to_vec()
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
/// Conver the Mnemonic into the internal representation of the decoded data.
|
||||
|
@ -421,8 +421,8 @@ where
|
|||
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function may panic if the HmacSha512 function returns an error. The only error the
|
||||
/// HmacSha512 function should return is an invalid length, which should not be possible.
|
||||
/// The function may panic if the `HmacSha512` function returns an error. The only error the
|
||||
/// `HmacSha512` function should return is an invalid length, which should not be possible.
|
||||
pub fn generate_seed<'a>(&self, passphrase: impl Into<Option<&'a str>>) -> [u8; 64] {
|
||||
let passphrase = passphrase.into();
|
||||
|
||||
|
@ -605,7 +605,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "bytes.len() <= 1024")]
|
||||
fn fails_over_8192_bits() {
|
||||
let entropy = &mut [0u8; 1024 + 4];
|
||||
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
||||
|
@ -614,7 +614,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic(expected = "bytes.len() % 4 == 0")]
|
||||
fn fails_over_invalid_size() {
|
||||
let entropy = &mut [0u8; 255];
|
||||
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
||||
|
|
|
@ -6,6 +6,9 @@ repository = "https://git.distrust.co/public/keyfork"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
|
|
|
@ -60,11 +60,7 @@ impl PromptHandler for Headless {
|
|||
|
||||
fn prompt_message(&mut self, prompt: Message) -> Result<()> {
|
||||
match prompt {
|
||||
Message::Text(s) => {
|
||||
writeln!(&mut self.stderr, "{s}")?;
|
||||
self.stderr.flush()?;
|
||||
}
|
||||
Message::Data(s) => {
|
||||
Message::Text(s) | Message::Data(s) => {
|
||||
writeln!(&mut self.stderr, "{s}")?;
|
||||
self.stderr.flush()?;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ where
|
|||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.read.consume(amt)
|
||||
self.read.consume(amt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,7 @@ where
|
|||
}
|
||||
|
||||
/// A handler for a terminal.
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct Terminal<R, W> {
|
||||
read: BufReader<R>,
|
||||
write: W,
|
||||
|
@ -259,8 +260,11 @@ where
|
|||
let printable_space = (cols as usize) - prefix_length;
|
||||
input.len() - (printable_space - 1)
|
||||
};
|
||||
|
||||
terminal
|
||||
.queue(cursor::MoveToColumn(prefix_length as u16))?
|
||||
.queue(cursor::MoveToColumn(
|
||||
u16::try_from(prefix_length).unwrap_or(u16::MAX),
|
||||
))?
|
||||
.queue(terminal::Clear(terminal::ClearType::UntilNewLine))?
|
||||
.queue(Print(&input[printable_start..]))?
|
||||
.flush()?;
|
||||
|
@ -388,9 +392,7 @@ where
|
|||
}
|
||||
Err(Error::Validation(
|
||||
retries,
|
||||
last_error
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string()),
|
||||
last_error.map_or_else(|| "Unknown".to_string(), |e| e.to_string()),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -480,10 +482,9 @@ where
|
|||
|
||||
let usable_space = cols as usize - prefix_length - 1;
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
terminal
|
||||
.queue(cursor::MoveToColumn(
|
||||
std::cmp::min(u16::MAX as usize, prefix_length) as u16,
|
||||
u16::try_from(prefix_length).unwrap_or(u16::MAX),
|
||||
))?
|
||||
.queue(terminal::Clear(terminal::ClearType::UntilNewLine))?
|
||||
.flush()?;
|
||||
|
@ -551,9 +552,7 @@ where
|
|||
}
|
||||
Err(Error::Validation(
|
||||
retries,
|
||||
last_error
|
||||
.map(|e| e.to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string()),
|
||||
last_error.map_or_else(|| "Unknown".to_string(), |e| e.to_string()),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -632,8 +631,7 @@ where
|
|||
for line in text.lines() {
|
||||
let mut written_chars = 0;
|
||||
for word in line.split_whitespace() {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let len = std::cmp::min(u16::MAX as usize, word.len()) as u16;
|
||||
let len = u16::try_from(word.len()).unwrap_or(u16::MAX);
|
||||
written_chars += len + 1;
|
||||
if written_chars > cols {
|
||||
terminal.queue(cursor::MoveToNextLine(1))?;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Validator and parser types.
|
||||
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
/// A trait to create validator functions.
|
||||
|
@ -93,7 +94,7 @@ impl Validator for SecurePinValidator {
|
|||
last_char = ch as i32;
|
||||
}
|
||||
let mut chars = s.chars().collect::<Vec<_>>();
|
||||
chars.sort();
|
||||
chars.sort_unstable();
|
||||
chars.dedup();
|
||||
if !ignore_repeated_characters {
|
||||
// SAFETY: the amount of characters can't have _increased_ since deduping
|
||||
|
@ -202,19 +203,16 @@ pub mod mnemonic {
|
|||
|
||||
fn to_fn(&self) -> Box<dyn Fn(String) -> Result<Mnemonic, Box<dyn std::error::Error>>> {
|
||||
let word_length = self.word_length.clone();
|
||||
Box::new(move |s: String| match word_length.as_ref() {
|
||||
Some(wl) => {
|
||||
Box::new(move |s: String| if let Some(wl) = word_length.as_ref() {
|
||||
let count = s.split_whitespace().count();
|
||||
if !wl.matches(count) {
|
||||
return Err(Box::new(Self::Error::InvalidLength(count, wl.clone())));
|
||||
}
|
||||
let m = Mnemonic::from_str(&s)?;
|
||||
Ok(m)
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
let m = Mnemonic::from_str(&s)?;
|
||||
Ok(m)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
smex = { workspace = true }
|
||||
|
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
|
Loading…
Reference in New Issue