all crates: make pedantic clippy happy

This commit is contained in:
Ryan Heywood 2025-05-17 19:36:11 -04:00
parent 672cc6a699
commit e7a776f59f
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
66 changed files with 382 additions and 336 deletions

View File

@ -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

1
clippy.toml Normal file
View File

@ -0,0 +1 @@
doc-valid-idents = ["OpenPGP", ".."]

View File

@ -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]

View File

@ -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)
}

View File

@ -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]

View File

@ -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]

View File

@ -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!(

View File

@ -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.

View File

@ -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 = [(

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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"]

View File

@ -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),

View File

@ -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)?;

View File

@ -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]

View File

@ -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"),

View File

@ -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]

View File

@ -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())
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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 {

View File

@ -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;

View File

@ -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]

View File

@ -13,7 +13,7 @@ fn run() -> Result<()> {
match args.as_slice() {
[] => (),
_ => panic!("Usage: {program_name}"),
};
}
let mut bytes = vec![];
remote_decrypt(&mut bytes)?;

View File

@ -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());

View File

@ -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

View File

@ -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 {

View File

@ -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);
}

View File

@ -4,6 +4,9 @@ version = "0.3.3"
edition = "2021"
license = "AGPL-3.0-only"
[lints]
workspace = true
[features]
default = [
"completion",

View File

@ -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(',')

View File

@ -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 => {

View File

@ -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(())
}

View File

@ -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(())

View File

@ -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)
}

View File

@ -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))

View File

@ -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]

View File

@ -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;

View File

@ -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]

View File

@ -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"));

View File

@ -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]

View File

@ -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);

View File

@ -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) };

View File

@ -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).

View File

@ -5,6 +5,9 @@ edition = "2021"
publish = false
license = "MIT"
[lints]
workspace = true
[dependencies]
assert_cmd = "2.0.16"
keyforkd = { workspace = true, features = ["default"] }

View File

@ -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:?}");
}

View File

@ -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]

View File

@ -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();

View File

@ -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]

View File

@ -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"

View File

@ -61,6 +61,7 @@ impl Filter for EventFilter {
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct InternalEventFilter;

View File

@ -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
//!

View File

@ -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));
}

View File

@ -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(),

View File

@ -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]

View File

@ -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]

View File

@ -6,6 +6,9 @@ repository = "https://git.distrust.co/public/keyfork"
edition = "2021"
license = "MIT"
[lints]
workspace = true
[features]
default = ["bin"]
bin = ["smex"]

View File

@ -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();

View File

@ -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]

View File

@ -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()?;
}

View File

@ -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))?;

View File

@ -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)
}
})
}
}

View File

@ -4,5 +4,8 @@ version = "0.1.0"
edition = "2021"
license = "MIT"
[lints]
workspace = true
[dependencies]
smex = { workspace = true }

View File

@ -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]