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
17 changes: 9 additions & 8 deletions libwebauthn/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Tests and the virt test-utility feature are allowed to use unwrap/expect/panic for convenience.
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::unwrap_used))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::expect_used))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::panic))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::todo))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::unreachable))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::indexing_slicing))]
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::unwrap_in_result))]
// Production code must not panic. Tests keep unwrap/expect/panic latitude
// through `not(test)`, and the virt test-utility code through local allows.
#![cfg_attr(not(test), deny(clippy::unwrap_used))]
#![cfg_attr(not(test), deny(clippy::expect_used))]
#![cfg_attr(not(test), deny(clippy::panic))]
#![cfg_attr(not(test), deny(clippy::todo))]
#![cfg_attr(not(test), deny(clippy::unreachable))]
#![cfg_attr(not(test), deny(clippy::indexing_slicing))]
#![cfg_attr(not(test), deny(clippy::unwrap_in_result))]

#[cfg(all(
feature = "nfc",
Expand Down
1 change: 1 addition & 0 deletions libwebauthn/src/ops/webauthn/client_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl ClientData {
/// Strings are escaped per WebAuthn L3 §5.8.1.2 (CCDToString), via
/// `serde_json`'s RFC 8259 string encoder. Field order matches the spec:
/// `type`, `challenge`, `origin`, `topOrigin?`, `crossOrigin`.
#[allow(clippy::expect_used)] // serialization of this fixed-shape struct cannot fail
pub fn to_json(&self) -> String {
let operation = match self.operation {
Operation::MakeCredential => "webauthn.create",
Expand Down
6 changes: 2 additions & 4 deletions libwebauthn/src/ops/webauthn/get_assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,11 @@ impl PrfInputValue {
/// WebAuthn L3 PRF: salt = SHA-256("WebAuthn PRF" || 0x00 || ev.{first,second}).
pub fn to_hmac_secret_input(&self) -> HMACGetSecretInput {
const PREFIX: &[u8] = b"WebAuthn PRF\x00";
let hash = |slice: &[u8]| {
let hash = |slice: &[u8]| -> [u8; 32] {
let mut hasher = Sha256::default();
hasher.update(PREFIX);
hasher.update(slice);
let mut out = [0u8; 32];
out.copy_from_slice(&hasher.finalize()[..32]);
out
hasher.finalize().into()
};
HMACGetSecretInput {
salt1: hash(&self.first),
Expand Down
2 changes: 1 addition & 1 deletion libwebauthn/src/ops/webauthn/idl/origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ pub(crate) fn is_registrable_domain_suffix_or_equal(
return false;
}
let boundary = effective_domain.len() - rp_id.len() - 1;
if effective_domain.as_bytes()[boundary] != b'.' {
if effective_domain.as_bytes().get(boundary) != Some(&b'.') {
return false;
}
if &effective_domain[boundary + 1..] != rp_id {
Expand Down
3 changes: 0 additions & 3 deletions libwebauthn/src/ops/webauthn/psl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
//! Most callers should use [`SystemPublicSuffixList::auto`], which probes
//! the standard system paths for whichever format is available.

// Module-scoped until the crate-wide indexing_slicing deny lands.
#![cfg_attr(not(any(test, feature = "virt")), deny(clippy::indexing_slicing))]

pub mod dafsa;
pub mod dat;
mod system;
Expand Down
6 changes: 6 additions & 0 deletions libwebauthn/src/transport/hid/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ impl<'d> HidChannel<'d> {
.open_device(&hidapi)
.or(Err(Error::Transport(TransportError::ConnectionFailed)))?),
#[cfg(feature = "virt")]
#[allow(clippy::unreachable)]
// virtual devices never go through hid_open
HidBackendDevice::VirtualDevice(_) => unreachable!(),
}
}
Expand Down Expand Up @@ -317,6 +319,8 @@ impl<'d> HidChannel<'d> {
response
}
#[cfg(feature = "virt")]
#[allow(clippy::panic)]
// virt test-utility: a poisoned lock is unrecoverable
OpenHidDevice::VirtualDevice(backend) => {
let Ok(mut guard) = backend.lock() else {
panic!("Poisoned lock on Virtual HID device");
Expand Down Expand Up @@ -378,6 +382,8 @@ impl<'d> HidChannel<'d> {
})?
}
#[cfg(feature = "virt")]
#[allow(clippy::panic)]
// virt test-utility: a poisoned lock is unrecoverable
OpenHidDevice::VirtualDevice(backend) => {
let Ok(mut guard) = backend.lock() else {
panic!("Poisoned lock on Virtual HID device");
Expand Down
16 changes: 12 additions & 4 deletions libwebauthn/src/transport/hid/framing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ impl HidMessageParser {
if self.packets.is_empty() {
// First packet must be an initialization packet: high bit of
// byte 4 set (CTAP 2.2 §11.2.4).
if packet[4] & PACKET_INITIAL_CMD_MASK == 0 {
let is_initialization = packet
.get(4)
.is_some_and(|&byte| byte & PACKET_INITIAL_CMD_MASK != 0);
if !is_initialization {
error!("First packet is not an initialization packet");
return Err(IOError::new(
IOErrorKind::InvalidData,
Expand All @@ -151,15 +154,20 @@ impl HidMessageParser {
} else {
// Continuation packets: same CID as the initial packet, SEQ has
// high bit cleared, SEQ starts at 0 and increments monotonically.
let initial = &self.packets[0];
if packet[..4] != initial[..4] {
let initial_cid = self.packets.first().and_then(|initial| initial.get(..4));
if packet.get(..4) != initial_cid {
error!("Continuation packet CID does not match initial packet");
return Err(IOError::new(
IOErrorKind::InvalidData,
"Continuation packet CID mismatch",
));
}
let seq = packet[4];
let Some(&seq) = packet.get(4) else {
return Err(IOError::new(
IOErrorKind::InvalidData,
"Packet length is invalid",
));
};
if seq & PACKET_INITIAL_CMD_MASK != 0 {
error!(seq, "Unexpected init packet during continuation");
return Err(IOError::new(
Expand Down
Loading