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
20 changes: 20 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
#
# Pre-commit hook: runs cargo fmt and fails if code is not formatted.
# Install: git config core.hooksPath .githooks

set -eu

# Find the Cargo.toml relative to the repo root
REPO_ROOT="$(git rev-parse --show-toplevel)"
MANIFEST="$REPO_ROOT/libwebauthn/Cargo.toml"

if ! command -v cargo >/dev/null 2>&1; then
echo "warning: cargo not found, skipping format check"
exit 0
fi

if ! cargo fmt --manifest-path "$MANIFEST" -- --check >/dev/null 2>&1; then
echo "error: code is not formatted. Run 'cargo fmt --manifest-path libwebauthn/Cargo.toml' and re-commit."
exit 1
fi
2 changes: 2 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
run: sudo apt-get install libudev-dev libdbus-1-dev libsodium-dev libnfc-dev libpcsclite-dev
- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Check formatting
run: cargo fmt -- --check
- name: Build
run: cargo build --all-targets --all-features
- name: Run tests
Expand Down
9 changes: 1 addition & 8 deletions libwebauthn/examples/prf_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
eval_by_credential,
};

run_success_test(
&mut channel,
&credential,
&challenge,
prf,
"PRF output: ",
)
.await;
run_success_test(&mut channel, &credential, &challenge, prf, "PRF output: ").await;
}
Ok(())
}
Expand Down
3 changes: 1 addition & 2 deletions libwebauthn/examples/u2f_ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
// Signature ceremony
println!("Signature request sent (timeout: {:?} seconds).", TIMEOUT);
let new_key = response.as_registered_key()?;
let sign_request =
SignRequest::new(APP_ID, challenge, &new_key.key_handle, TIMEOUT, true);
let sign_request = SignRequest::new(APP_ID, challenge, &new_key.key_handle, TIMEOUT, true);
let response = channel.u2f_sign(&sign_request).await?;
println!("Response: {:?}", response);
}
Expand Down
3 changes: 1 addition & 2 deletions libwebauthn/examples/u2f_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
// Signature ceremony
println!("Signature request sent (timeout: {:?} seconds).", TIMEOUT);
let new_key = response.as_registered_key()?;
let sign_request =
SignRequest::new(APP_ID, challenge, &new_key.key_handle, TIMEOUT, true);
let sign_request = SignRequest::new(APP_ID, challenge, &new_key.key_handle, TIMEOUT, true);
let response = channel.u2f_sign(&sign_request).await?;
println!("Response: {:?}", response);
}
Expand Down
2 changes: 1 addition & 1 deletion libwebauthn/examples/webauthn_cable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::sync::Arc;
use std::time::Duration;

use libwebauthn::pin::PinRequestReason;
use libwebauthn::transport::cable::is_available;
use libwebauthn::transport::cable::channel::{CableUpdate, CableUxUpdate};
use libwebauthn::transport::cable::is_available;
use libwebauthn::transport::cable::known_devices::{
CableKnownDevice, ClientPayloadHint, EphemeralDeviceInfoStore,
};
Expand Down
4 changes: 2 additions & 2 deletions libwebauthn/examples/webauthn_json_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use tokio::sync::broadcast::Receiver;
use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
GetAssertionRequest, JsonFormat, MakeCredentialRequest, RelyingPartyId,
WebAuthnIDL as _, WebAuthnIDLResponse as _,
GetAssertionRequest, JsonFormat, MakeCredentialRequest, RelyingPartyId, WebAuthnIDL as _,
WebAuthnIDLResponse as _,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::transport::hid::list_devices;
Expand Down
9 changes: 1 addition & 8 deletions libwebauthn/examples/webauthn_prf_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
eval,
eval_by_credential,
};
run_success_test(
&mut channel,
&credential,
&challenge,
prf,
"eval only",
)
.await;
run_success_test(&mut channel, &credential, &challenge, prf, "eval only").await;

// Test 4: eval and a full list of eval_by_credential
let eval = Some(PRFValue {
Expand Down
19 changes: 9 additions & 10 deletions libwebauthn/src/fido.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for AuthenticatorData<T> {
.read_u8()
.map_err(|e| DesError::custom(format!("failed to read flags: {e}")))?;
let flags = AuthenticatorDataFlags::from_bits_truncate(flags_raw);
let signature_count = cursor
.read_u32::<BigEndian>()
.map_err(|e| DesError::custom(format!("failed to read signature_count: {e}")))?;
let signature_count = cursor.read_u32::<BigEndian>().map_err(|e| {
DesError::custom(format!("failed to read signature_count: {e}"))
})?;

let attested_credential =
if flags.contains(AuthenticatorDataFlags::ATTESTED_CREDENTIALS) {
Expand All @@ -218,17 +218,16 @@ impl<'de, T: DeserializeOwned> Deserialize<'de> for AuthenticatorData<T> {
cursor
.read_exact(&mut aaguid)
.map_err(|e| DesError::custom(format!("failed to read aaguid: {e}")))?;
let credential_id_len = cursor
.read_u16::<BigEndian>()
.map_err(|e| DesError::custom(format!("failed to read credential_id_len: {e}")))?
as usize;
let credential_id_len = cursor.read_u16::<BigEndian>().map_err(|e| {
DesError::custom(format!("failed to read credential_id_len: {e}"))
})? as usize;
if data.len() < 55 + credential_id_len {
return Err(DesError::invalid_length(data.len(), &"55+L"));
}
let mut credential_id = vec![0u8; credential_id_len];
cursor
.read_exact(&mut credential_id)
.map_err(|e| DesError::custom(format!("failed to read credential_id: {e}")))?;
cursor.read_exact(&mut credential_id).map_err(|e| {
DesError::custom(format!("failed to read credential_id: {e}"))
})?;

let credential_public_key: PublicKey =
cbor::from_cursor(&mut cursor).map_err(DesError::custom)?;
Expand Down
1 change: 0 additions & 1 deletion libwebauthn/src/management/authenticator_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ impl Ctap2UserVerifiableRequest for Ctap2AuthenticatorConfigRequest {
Ok(())
}


fn permissions(&self) -> Ctap2AuthTokenPermissionRole {
Ctap2AuthTokenPermissionRole::AUTHENTICATOR_CONFIGURATION
}
Expand Down
11 changes: 6 additions & 5 deletions libwebauthn/src/management/bio_enrollment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ where
let resp = self.ctap2_bio_enrollment(&req, timeout).await?;
let Some(fingerprint_kind) = resp.fingerprint_kind else {
warn!("Channel did not return fingerprint_kind in sensor info.");
return Err(Error::Ctap(CtapError::Other))
return Err(Error::Ctap(CtapError::Other));
};
Ok(Ctap2BioEnrollmentFingerprintSensorInfo {
fingerprint_kind,
max_capture_samples_required_for_enroll: resp
.max_capture_samples_required_for_enroll,
max_capture_samples_required_for_enroll: resp.max_capture_samples_required_for_enroll,
max_template_friendly_name: resp.max_template_friendly_name,
})
}
Expand Down Expand Up @@ -303,7 +302,10 @@ impl Ctap2UserVerifiableRequest for Ctap2BioEnrollmentRequest {
let subcommand = self
.subcommand
.ok_or(Error::Platform(PlatformError::InvalidDeviceResponse))?;
let mut data = vec![Ctap2BioEnrollmentModality::Fingerprint as u8, subcommand as u8];
let mut data = vec![
Ctap2BioEnrollmentModality::Fingerprint as u8,
subcommand as u8,
];
// e.g. "Authenticator calls verify(pinUvAuthToken, fingerprint (0x01) || removeEnrollment (0x06) || subCommandParams, pinUvAuthParam)"
if let Some(params) = &self.subcommand_params {
data.extend(cbor::to_vec(&params)?);
Expand All @@ -314,7 +316,6 @@ impl Ctap2UserVerifiableRequest for Ctap2BioEnrollmentRequest {
Ok(())
}


fn permissions(&self) -> Ctap2AuthTokenPermissionRole {
Ctap2AuthTokenPermissionRole::BIO_ENROLLMENT
}
Expand Down
1 change: 0 additions & 1 deletion libwebauthn/src/management/credential_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,6 @@ impl Ctap2UserVerifiableRequest for Ctap2CredentialManagementRequest {
Ok(())
}


fn permissions(&self) -> Ctap2AuthTokenPermissionRole {
Ctap2AuthTokenPermissionRole::CREDENTIAL_MANAGEMENT
}
Expand Down
3 changes: 1 addition & 2 deletions libwebauthn/src/ops/u2f.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use x509_parser::nom::AsBytes;
use super::webauthn::MakeCredentialRequest;
use crate::fido::{AttestedCredentialData, AuthenticatorData, AuthenticatorDataFlags};
use crate::ops::webauthn::{
GetAssertionRequest, GetAssertionResponse, MakeCredentialResponse,
UserVerificationRequirement,
GetAssertionRequest, GetAssertionResponse, MakeCredentialResponse, UserVerificationRequirement,
};
use crate::proto::ctap1::{Ctap1RegisterRequest, Ctap1SignRequest};
use crate::proto::ctap1::{Ctap1RegisterResponse, Ctap1SignResponse};
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,9 @@ pub fn hkdf_sha256(salt: Option<&[u8]>, ikm: &[u8], info: &[u8]) -> Result<Vec<u
let mut okm = [0u8; 32]; // fixed L = 32
hk.expand(info, &mut okm).map_err(|e| {
error!("HKDF expand error: {e}");
Error::Platform(PlatformError::CryptoError(format!("HKDF expand error: {e}")))
Error::Platform(PlatformError::CryptoError(format!(
"HKDF expand error: {e}"
)))
})?;
Ok(Vec::from(okm))
}
Expand Down Expand Up @@ -483,7 +485,8 @@ where

// In preparation for obtaining pinUvAuthToken, the platform:
// * Obtains a shared secret.
let (public_key, shared_secret) = obtain_shared_secret(self, uv_proto.as_ref(), timeout).await?;
let (public_key, shared_secret) =
obtain_shared_secret(self, uv_proto.as_ref(), timeout).await?;

// paddedPin is newPin padded on the right with 0x00 bytes to make it 64 bytes long. (Since the maximum length of newPin is 63 bytes, there is always at least one byte of padding.)
let mut padded_new_pin = new_pin.as_bytes().to_vec();
Expand Down
6 changes: 1 addition & 5 deletions libwebauthn/src/transport/ble/btleplug/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,7 @@ pub async fn list_fido_devices() -> Result<Vec<FidoDevice>, Error> {
.await
.or(Err(Error::ConnectionFailed))?
.into_iter()
.filter(|p| {
p.services()
.iter()
.any(|s| s.uuid == FIDO_PROFILE_UUID)
})
.filter(|p| p.services().iter().any(|s| s.uuid == FIDO_PROFILE_UUID))
.collect();
let with_properties = discover_properties(peripherals)
.await?
Expand Down
3 changes: 2 additions & 1 deletion libwebauthn/src/transport/cable/connection_stages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ impl ConnectionInput {
qr_device.qr_code.qr_secret.as_ref(),
None,
KeyPurpose::TunnelID,
).map_err(|_| TransportError::InvalidKey)?;
)
.map_err(|_| TransportError::InvalidKey)?;
let tunnel_id = &tunnel_id_full[..16];
let tunnel_id_str = hex::encode(tunnel_id);

Expand Down
4 changes: 3 additions & 1 deletion libwebauthn/src/transport/cable/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ mod tests {
.try_into()
.unwrap();
let salt = hex::decode("ffeeddccbbaa998877665544332211").unwrap();
let output = derive(&input, Some(&salt), KeyPurpose::EIDKey).unwrap().to_vec();
let output = derive(&input, Some(&salt), KeyPurpose::EIDKey)
.unwrap()
.to_vec();
let expected = hex::decode("168cf3dd220a7907f8bac30f559be92a3b6d937fe5594beeaf1e50e35976b7d654dd550e22ae4c801b9d1cdbf0d2b1472daa1328661eb889acae3023b7ffa509").unwrap();
assert_eq!(output, expected);
}
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/src/transport/cable/known_devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,11 @@ impl CableKnownDevice {
let proximity_output = proximity_check_stage(proximity_input, ux_sender).await?;

// Stage 3: Handshake
let handshake_input =
HandshakeInput::new_for_known_device(known_device, connection_output, proximity_output)?;
let handshake_input = HandshakeInput::new_for_known_device(
known_device,
connection_output,
proximity_output,
)?;
let handshake_output = handshake_stage(handshake_input, ux_sender).await?;

Ok(handshake_output)
Expand Down
11 changes: 3 additions & 8 deletions libwebauthn/src/transport/cable/tunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ pub fn decode_tunnel_server_domain(encoded: u16) -> Option<String> {
let digest = hasher.finalize();

let mut v = u64::from_le_bytes([
digest[0], digest[1], digest[2], digest[3],
digest[4], digest[5], digest[6], digest[7],
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
]);
let tld_index = v & 3;
v >>= 2;
Expand Down Expand Up @@ -711,12 +710,8 @@ async fn connection_recv(
let device_id: CableKnownDeviceId = (&linking_info).into();
match known_device_store {
Some(store) => {
match parse_known_device(
private_key,
tunnel_domain,
&linking_info,
noise_state,
) {
match parse_known_device(private_key, tunnel_domain, &linking_info, noise_state)
{
Ok(known_device) => {
debug!(?device_id, "Updating known device");
trace!(?known_device);
Expand Down
3 changes: 1 addition & 2 deletions libwebauthn/src/transport/hid/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,7 @@ impl Channel for HidChannel<'_> {
self.status
}

async fn close(&mut self) {
}
async fn close(&mut self) {}

async fn apdu_send(
&mut self,
Expand Down
4 changes: 1 addition & 3 deletions libwebauthn/src/transport/hid/framing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,7 @@ mod tests {
let mut parser = HidMessageParser::new();
assert_eq!(
parser
.update(&[
0xC0, 0xC1, 0xC2, 0xC3, 0x83, 0x00, 0x04, 0x0A, 0x0B, 0x0C, 0x0D,
])
.update(&[0xC0, 0xC1, 0xC2, 0xC3, 0x83, 0x00, 0x04, 0x0A, 0x0B, 0x0C, 0x0D,])
.unwrap(),
HidMessageParserState::Done
);
Expand Down
4 changes: 2 additions & 2 deletions libwebauthn/src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ pub mod ble;
pub mod cable;
pub mod device;
pub mod hid;
#[cfg(feature = "nfc")]
pub mod nfc;
#[cfg(test)]
/// A mock channel that can be used in tests to
/// queue expected requests and responses in unittests
pub mod mock;
#[cfg(feature = "nfc")]
pub mod nfc;
#[cfg(test)]
/// Fully fledged virtual device based on trussed
/// for end2end tests
Expand Down
3 changes: 1 addition & 2 deletions libwebauthn/src/transport/nfc/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,7 @@ where
self.status
}

async fn close(&mut self) {
}
async fn close(&mut self) {}

#[instrument(level = Level::DEBUG, skip_all)]
async fn apdu_send(&mut self, request: &ApduRequest, _timeout: Duration) -> Result<(), Error> {
Expand Down
4 changes: 1 addition & 3 deletions libwebauthn/src/transport/nfc/pcsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ impl Drop for PcscCard {

impl PcscCard {
pub fn new(card: pcsc::Card) -> Self {
PcscCard {
card: Some(card),
}
PcscCard { card: Some(card) }
}
}

Expand Down
18 changes: 14 additions & 4 deletions libwebauthn/src/webauthn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ where
Ctap2MakeCredentialRequest::from_webauthn_request(op, &get_info_response)?;
if Self::supports_preflight() {
if let Some(exclude_list) = &op.exclude {
let filtered_exclude_list =
ctap2_preflight(self, exclude_list, &op.client_data_hash(), &op.relying_party.id).await;
let filtered_exclude_list = ctap2_preflight(
self,
exclude_list,
&op.client_data_hash(),
&op.relying_party.id,
)
.await;
ctap2_request.exclude = Some(filtered_exclude_list);
}
}
Expand Down Expand Up @@ -171,8 +176,13 @@ where
Ctap2GetAssertionRequest::from_webauthn_request(op, &get_info_response)?;

if Self::supports_preflight() {
let filtered_allow_list =
ctap2_preflight(self, &op.allow, &op.client_data_hash(), &op.relying_party_id).await;
let filtered_allow_list = ctap2_preflight(
self,
&op.allow,
&op.client_data_hash(),
&op.relying_party_id,
)
.await;
if filtered_allow_list.is_empty() && !op.allow.is_empty() {
// We filtered out everything in preflight, meaning none of the allowed
// credentials are present on this device. So we error out here
Expand Down
Loading
Loading