Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ jobs:
- name: Check formatting
run: cargo fmt --all --check

# Runs on `stable` rather than a pinned version, so a toolchain bump that introduces
# new lints fails here rather than accumulating unnoticed — which is how this became a
# 74-diagnostic cleanup instead of a one-line fix. Anything deliberate is `#[allow]`ed
# at the site with the reason, so a failure here means something new.
clippy:
name: clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install Rust (stable + clippy)
uses: dtolnay/rust-toolchain@67ef31d5b988238dd797d409d6f9574278e20537 # master
with:
toolchain: stable
components: clippy
- name: Cache cargo
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
- name: Lint
run: cargo clippy --workspace --all-targets -- -D warnings

build-test:
name: build & test
runs-on: ubuntu-latest
Expand Down
6 changes: 4 additions & 2 deletions crates/aadhaar-offline-kyc/src/qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ pub fn decode_qr_from_luma8(
expected
)));
}
let mut hints = DecodeHints::default();
hints.TryHarder = Some(true);
let mut hints = DecodeHints {
TryHarder: Some(true),
..Default::default()
};
let result = rxing::helpers::detect_in_luma_with_hints(
luma,
width,
Expand Down
6 changes: 1 addition & 5 deletions crates/aamva/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,7 @@ mod tests {
let subfile_len = subfile.len();

let designator = format!("DL{:04}{:04}", subfile_offset, subfile_len);
let mut payload = Vec::new();
payload.push(COMPLIANCE);
payload.push(DATA_ELEMENT_SEP);
payload.push(RECORD_SEP);
payload.push(SEGMENT_TERM);
let mut payload = vec![COMPLIANCE, DATA_ELEMENT_SEP, RECORD_SEP, SEGMENT_TERM];
payload.extend_from_slice(ANSI_TAG);
payload.extend_from_slice(iin);
payload.extend_from_slice(version);
Expand Down
8 changes: 5 additions & 3 deletions crates/aamva/src/pdf417.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ pub fn decode_pdf417_from_luma8(
expected
)));
}
let mut hints = DecodeHints::default();
hints.TryHarder = Some(true);
let mut hints = DecodeHints {
TryHarder: Some(true),
..Default::default()
};
let result = rxing::helpers::detect_in_luma_with_hints(
luma,
width,
Expand Down Expand Up @@ -70,7 +72,7 @@ mod tests {

/// Renders a PDF417 of `text` into a luma8 buffer.
fn encode_pdf417_to_luma(text: &str, width: i32, height: i32) -> (u32, u32, Vec<u8>) {
let writer = PDF417Writer::default();
let writer = PDF417Writer;
let matrix = writer
.encode_with_hints(
text,
Expand Down
10 changes: 5 additions & 5 deletions crates/dmrtd/src/crypto/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ impl AesCipher {
/// - `data` – Plaintext bytes.
/// - `key` – Must be exactly [`key_size()`] bytes.
/// - `iv` – 16-byte IV. Required for CBC mode (`None` returns
/// [`AesCipherError::MissingIv`]). Ignored for ECB mode.
/// [`AesCipherError::MissingIv`]). Ignored for ECB mode.
/// - `mode` – [`BlockCipherMode::Cbc`] (default) or [`BlockCipherMode::Ecb`].
/// - `padding` – If `true`, `data` is zero-padded to the next 16-byte boundary
/// before encryption. If `false`, `data` must already be a multiple
/// of 16 bytes.
/// before encryption. If `false`, `data` must already be a multiple of 16
/// bytes.
///
/// # Errors
/// - [`AesCipherError::InvalidKeyLength`] if `key.len() != key_size`.
Expand Down Expand Up @@ -208,7 +208,7 @@ impl AesCipher {
/// - `data` – Ciphertext bytes; must be a multiple of 16 bytes.
/// - `key` – Must be exactly [`key_size()`] bytes.
/// - `iv` – 16-byte IV. Required for CBC mode (`None` returns
/// [`AesCipherError::MissingIv`]). Ignored for ECB mode.
/// [`AesCipherError::MissingIv`]). Ignored for ECB mode.
/// - `mode` – [`BlockCipherMode::Cbc`] or [`BlockCipherMode::Ecb`].
///
/// # Errors
Expand All @@ -222,7 +222,7 @@ impl AesCipher {
) -> Result<Vec<u8>, AesCipherError> {
self.validate_key(key)?;

if data.len() % AES_BLOCK_SIZE != 0 {
if !data.len().is_multiple_of(AES_BLOCK_SIZE) {
return Err(AesCipherError::InvalidDataLength(data.len()));
}

Expand Down
4 changes: 2 additions & 2 deletions crates/dmrtd/src/crypto/des.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl DesedeCipher {
} else {
data
};
if input.len() % DES_BLOCK_SIZE != 0 {
if !input.len().is_multiple_of(DES_BLOCK_SIZE) {
return Err(DesError::InvalidDataLength(input.len()));
}
Ok(cbc_encrypt_3des(&self.triple_key.0, &self.iv, input))
Expand All @@ -154,7 +154,7 @@ impl DesedeCipher {
/// When `padded_data` is `true`, also returns [`DesError::Iso9797`] if the
/// decrypted plaintext is not valid ISO/IEC 9797-1 Method 2 padding.
pub fn decrypt(&self, edata: &[u8], padded_data: bool) -> Result<Vec<u8>, DesError> {
if edata.len() % DES_BLOCK_SIZE != 0 {
if !edata.len().is_multiple_of(DES_BLOCK_SIZE) {
return Err(DesError::InvalidDataLength(edata.len()));
}
let plain = cbc_decrypt_3des(&self.triple_key.0, &self.iv, edata);
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/crypto/diffie_hellman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ impl DHpkcs3Engine {
}

let length = spec.length;
if length == 0 || length % 8 != 0 {
if length == 0 || !length.is_multiple_of(8) {
return Err(DhPkcs3EngineError(format!(
"Invalid bitLength value - {length}"
)));
Expand Down
6 changes: 3 additions & 3 deletions crates/dmrtd/src/crypto/iso9797.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub fn pad(data: &[u8], block_size: usize) -> Result<Vec<u8>, Iso9797Error> {
let mut padded = Vec::with_capacity(data.len() + pad_len);
padded.extend_from_slice(data);
padded.push(0x80);
padded.extend(std::iter::repeat(0x00).take(pad_len - 1));
padded.extend(std::iter::repeat_n(0x00, pad_len - 1));
Ok(padded)
}

Expand Down Expand Up @@ -114,7 +114,7 @@ pub fn pad(data: &[u8], block_size: usize) -> Result<Vec<u8>, Iso9797Error> {
pub fn unpad(data: &[u8], block_size: usize) -> Result<&[u8], Iso9797Error> {
// Method-2 padded ciphertext output is always a whole number of blocks;
// a non-block-aligned (or empty) input cannot be validly padded.
if block_size != 0 && (data.is_empty() || data.len() % block_size != 0) {
if block_size != 0 && (data.is_empty() || !data.len().is_multiple_of(block_size)) {
return Err(Iso9797Error::UnpadFailed);
}
let mut i = data.len();
Expand Down Expand Up @@ -150,7 +150,7 @@ pub fn unpad(data: &[u8], block_size: usize) -> Result<&[u8], Iso9797Error> {
/// - `key` – 16 or 24-byte key.
/// - `msg` – Message to authenticate.
/// - `pad_msg`– When `true` the message is padded with Method 2 before MAC
/// computation; when `false` the caller is responsible for padding.
/// computation; when `false` the caller is responsible for padding.
///
/// # Errors
/// Returns [`Iso9797Error::InvalidKeyLength`] if the key length is not 16 or 24.
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/lds/df1/efdg2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl EfDG2 {
if bict.value.is_empty() {
return Err(EfParseError::new("DG2 BICT value is empty"));
}
let bit_count = bict.value[0] & 0xFF;
let bit_count = bict.value[0];

let mut out = Self {
encoded,
Expand Down
5 changes: 5 additions & 0 deletions crates/dmrtd/src/lds/df1/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
//! DF1 (Dedicated File 1) – Elementary Files for ICAO 9303 Machine Readable Travel Documents.

// `df1::df1` holds the application's own constants (its AID and name) while the
// siblings hold the elementary files inside it, so the nesting says something real.
// Renaming it would change a public path in a published crate to satisfy a naming
// convention.
#[allow(clippy::module_inception)]
pub mod df1;
pub mod dg;
pub mod efcom;
Expand Down
13 changes: 6 additions & 7 deletions crates/dmrtd/src/lds/substruct/pace_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ fn check_domain_parameter_supported(id: i64, algo: TokenAgreementAlgo) -> bool {
if !entry.is_supported {
return false;
}
match (algo, entry.kind) {
(TokenAgreementAlgo::Ecdh, DomainParameterType::Ecp) => true,
(TokenAgreementAlgo::Dh, DomainParameterType::Gfp) => true,
_ => false,
}
matches!(
(algo, entry.kind),
(TokenAgreementAlgo::Ecdh, DomainParameterType::Ecp)
| (TokenAgreementAlgo::Dh, DomainParameterType::Gfp)
)
}

// ---------------------------------------------------------------------------
Expand All @@ -145,8 +145,7 @@ mod tests {
/// OID suffix.
/// - `version` : the INTEGER value to emit for the version field.
/// - `parameter_id` : optional INTEGER value for the parameter field;
/// `None` omits the element (to exercise the 3-elements
/// check).
/// `None` omits the element (to exercise the 3-elements check).
fn build_pace_info(oid_der: &[u8], version: u32, parameter_id: Option<u32>) -> Vec<u8> {
asn1::write_single(&PaceInfoBuilder {
oid_der,
Expand Down
8 changes: 4 additions & 4 deletions crates/dmrtd/src/lds/tlv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ impl Tlv {
let bc = byte_count(n);
let count = if bc == 0 { 1 } else { bc };
let mut out = vec![0u8; count];
for i in 0..bc {
for (i, byte) in out.iter_mut().enumerate().take(bc) {
let pos = 8 * (bc - i - 1);
out[i] = ((n >> pos) & 0xFF) as u8;
*byte = ((n >> pos) & 0xFF) as u8;
}
out
}
Expand Down Expand Up @@ -380,8 +380,8 @@ impl Tlv {
}

let mut length = 0usize;
for i in 1..total_bytes {
length = length * 0x100 + (encoded_length[i] as usize);
for byte in &encoded_length[1..total_bytes] {
length = length * 0x100 + (*byte as usize);
}

Ok(DecodedLen {
Expand Down
10 changes: 7 additions & 3 deletions crates/dmrtd/src/proto/bac_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ impl BacSession {
}

/// Advances the state machine and returns the next action.
// Not `Iterator::next`, and deliberately so: this is fallible, it drives an APDU
// exchange with the chip rather than yielding from a sequence, and it ends by
// returning `Done` rather than `None`. Renaming it would also break a published
// API to avoid a resemblance that the return type already rules out.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<BacAction, BacError> {
match &self.state {
State::Start => {
Expand Down Expand Up @@ -160,12 +165,11 @@ impl BacSession {
State::Completed(sm_slot) => {
// We placed the SM here when we parsed the EXTERNAL
// AUTHENTICATE response; hand it over now.
let sm = sm_slot
sm_slot
.as_ref()
.is_some()
.then(|| ())
.then_some(())
.ok_or_else(|| BacError("BAC session already consumed".into()))?;
let _ = sm;
let taken = match &mut self.state {
State::Completed(slot) => slot.take(),
_ => unreachable!(),
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/proto/dba_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ fn pad_right(s: &str, min_len: usize, fill: char) -> String {
s.to_string()
} else {
let mut out = s.to_string();
out.extend(std::iter::repeat(fill).take(min_len - s.len()));
out.extend(std::iter::repeat_n(fill, min_len - s.len()));
out
}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/dmrtd/src/proto/ecdh_pace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,12 @@ impl_curve_ops!(
b"26959946667150639794667015087019625940457807714424391721682722368061"
);

// The P-521 engine is much larger than the P-224 one, so every variant carries the
// padding of the largest. Boxing is not worth it here: exactly one of these exists per
// PACE session, so the padding is never multiplied, while the indirection would land on
// the key-agreement path — and `Box<NistP521Engine>` is a breaking change to a public
// enum for callers who match on it.
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum ECDHPace {
NistP256(NistP256Engine),
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/proto/iso7816/command_apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl CommandApdu {
pub fn to_bytes(&self) -> Vec<u8> {
let data_len = self.data.as_ref().map_or(0, |d| d.len());
// Non-empty data present in the Lc/data fields?
let has_data = self.data.as_ref().map_or(false, |d| !d.is_empty());
let has_data = self.data.as_ref().is_some_and(|d| !d.is_empty());

// Extended form is required when data > 255 bytes OR ne > 256.
let extended = data_len > 255 || self.ne > 256;
Expand Down
7 changes: 6 additions & 1 deletion crates/dmrtd/src/proto/iso7816/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
//!

pub mod command_apdu; // CommandAPDU (CLA/INS/P1/P2/data/Ne)
// pub mod icc; // icc.dart – ICC (Integrated Circuit Card) interface

// Not ported from the Dart original: `icc.dart` (the ICC interface).

// `iso7816::iso7816` holds the basic inter-industry command constants, distinct from
// the APDU types beside it. Renaming would change a public path in a published crate.
#[allow(clippy::module_inception)]
pub mod iso7816;
pub mod response_apdu; // ResponseAPDU + StatusWord
pub mod sm;
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/proto/iso7816/response_apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ impl ResponseApdu {

/// Serialises this APDU as `data_bytes || [SW1, SW2]`.
pub fn to_bytes(&self) -> Vec<u8> {
let mut out: Vec<u8> = self.data.as_ref().map(|d| d.clone()).unwrap_or_default();
let mut out: Vec<u8> = self.data.clone().unwrap_or_default();
out.extend_from_slice(&self.status.to_bytes());
out
}
Expand Down
5 changes: 5 additions & 0 deletions crates/dmrtd/src/proto/pace_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ pub enum PaceSessionError {
/// MODP groups). Both expose the same set of operations the session needs,
/// exchanging [`PublicKeyPace`] values and raw KDF seed bytes so the state
/// machine stays agreement-agnostic.
// One per session, as with [`ECDHPace`] — the size difference is paid once and never
// multiplied, so boxing would buy nothing and cost an allocation.
#[allow(clippy::large_enum_variant)]
enum PaceEngine {
Ecdh(ECDHPace),
Dh(DHPace),
Expand Down Expand Up @@ -364,6 +367,8 @@ impl<K: AccessKey> PaceSession<K> {
}

/// Advances the state machine and returns the next action.
// See `BacSession::next`: a fallible protocol step, not an iterator.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<PaceAction, PaceSessionError> {
match std::mem::replace(&mut self.state, State::Start) {
State::Start => {
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/proto/public_key_pace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl PublicKeyPace {
/// most card implementations. The input must have even length; each half is
/// the fixed coordinate width.
pub fn ecdh_from_hex(xy: &[u8]) -> Option<Self> {
if xy.is_empty() || xy.len() % 2 != 0 {
if xy.is_empty() || !xy.len().is_multiple_of(2) {
return None;
}
let half = xy.len() / 2;
Expand Down
8 changes: 4 additions & 4 deletions crates/dmrtd/src/proto/ssc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ impl Ssc {
///
/// # Arguments
/// - `ssc` – Initial value as a big-endian byte slice. Leading zero
/// bytes are permitted (they are stripped when interpreting
/// the value, but the result must still fit in `bit_size` bits).
/// bytes are permitted (they are stripped when interpreting the value, but
/// the result must still fit in `bit_size` bits).
/// - `bit_size` – Counter width in bits. Must be a multiple of 8.
///
/// # Errors
Expand All @@ -109,7 +109,7 @@ impl Ssc {
/// assert!(Ssc::new(&[0x01, 0x00], 8).is_err()); // 0x100 > 8-bit max
/// ```
pub fn new(ssc: &[u8], bit_size: usize) -> Result<Self, SscError> {
if bit_size % 8 != 0 {
if !bit_size.is_multiple_of(8) {
return Err(SscError::BitSizeNotMultipleOf8(bit_size));
}
if bit_size > 128 {
Expand Down Expand Up @@ -150,7 +150,7 @@ impl Ssc {
// absurd `bit_size` would trigger a huge `vec![0u8; bit_size / 8]`
// allocation before [`Ssc::new`] ever gets a chance to reject it.
assert!(
bit_size % 8 == 0,
bit_size.is_multiple_of(8),
"zeroed SSC: bit_size must be a multiple of 8"
);
assert!(
Expand Down
2 changes: 1 addition & 1 deletion crates/dmrtd/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn bit_count(n: u64) -> usize {
/// assert_eq!(byte_count(0x10000),3);
/// ```
pub fn byte_count(n: u64) -> usize {
(bit_count(n) + 7) / 8
bit_count(n).div_ceil(8)
}

// ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion crates/incometax-pan-qr/src/unpacker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl BitUnpacker {
/// check, drains whole bytes via the `>> 8` loop, then accumulates the final
/// partial byte.
pub fn bit_unpack(&mut self, v: u32, v1: u32) -> Result<(), PanQrError> {
if v1 > 0x20 || v1 < 1 {
if !(1..=0x20).contains(&v1) {
return Err(PanQrError::InvalidBitCount(v1 as i64));
}

Expand Down
Loading