You've already forked fido-authenticator
mirror of
https://github.com/trussed-dev/fido-authenticator.git
synced 2026-06-20 04:16:16 -07:00
ctap2.3: advertise smart-card transport when CCID is enabled
This commit is contained in:
committed by
Robin Krahl
parent
bcec723f1b
commit
e91fb4779f
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Implement the `config` command with these subcommands:
|
||||
- `toggleAlwaysUv`
|
||||
- `setMinPINLength`
|
||||
- Add `ccid_transport` to `Config` and set `transports` in `get_info` accordingly.
|
||||
- Load full credential from filesstem for getAssertion if an allow list is used with a discoverable credential.
|
||||
- Use UTF-8 code points instead of bytes when checking the minimum length for PINs.
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ fuzz_target!(|requests: Vec<Request<'_>>| {
|
||||
max_resident_credential_count: None,
|
||||
large_blobs: None,
|
||||
nfc_transport: false,
|
||||
ccid_transport: false,
|
||||
firmware_version: Some(0),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -110,6 +110,9 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
|
||||
if self.config.nfc_transport {
|
||||
transports.push(Transport::Nfc).unwrap();
|
||||
}
|
||||
if self.config.ccid_transport {
|
||||
transports.push(Transport::SmartCard).unwrap();
|
||||
}
|
||||
transports.push(Transport::Usb).unwrap();
|
||||
|
||||
let mut attestation_formats = Vec::new();
|
||||
|
||||
@@ -126,6 +126,10 @@ pub struct Config {
|
||||
pub large_blobs: Option<ctap2::large_blobs::Config>,
|
||||
/// Whether the authenticator supports the NFC transport.
|
||||
pub nfc_transport: bool,
|
||||
/// Whether the authenticator exposes FIDO over a CCID smart-card interface
|
||||
/// (CTAP 2.3 §3 FIDO Interfaces). When `true`, GetInfo advertises the
|
||||
/// `"smart-card"` transport alongside `"usb"` / `"nfc"`.
|
||||
pub ccid_transport: bool,
|
||||
/// Firmware version reported by `authenticatorGetInfo` (CTAP 2.1 §6.4 0x0E).
|
||||
///
|
||||
/// The runner is expected to plumb its own version constant in here.
|
||||
|
||||
@@ -2669,3 +2669,91 @@ fn test_make_credential_hmac_secret_mc_invalid_salt_length_rejected() {
|
||||
assert_eq!(result.err(), Some(Ctap2Error(0x03)));
|
||||
})
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Transports (CTAP 2.1 §6.4 0x09 / CTAP 2.3 §3 smart-card)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Default config (USB only) advertises only `"usb"`.
|
||||
#[test]
|
||||
fn test_transports_usb_only_by_default() {
|
||||
virt::run_ctap2(|device| {
|
||||
let reply = device.exec(GetInfo).unwrap();
|
||||
let transports = reply.transports.expect("transports list missing");
|
||||
assert_eq!(transports, vec!["usb".to_owned()]);
|
||||
})
|
||||
}
|
||||
|
||||
/// With `nfc_transport=true`, `"nfc"` and `"usb"` are advertised.
|
||||
#[test]
|
||||
fn test_transports_nfc_added() {
|
||||
let options = Options {
|
||||
nfc_transport: true,
|
||||
..Default::default()
|
||||
};
|
||||
virt::run_ctap2_with_options(options, |device| {
|
||||
let reply = device.exec(GetInfo).unwrap();
|
||||
let transports = reply.transports.expect("transports list missing");
|
||||
assert!(transports.contains(&"nfc".to_owned()));
|
||||
assert!(transports.contains(&"usb".to_owned()));
|
||||
})
|
||||
}
|
||||
|
||||
/// CTAP 2.3 §3: with `ccid_transport=true`, `"smart-card"` is advertised
|
||||
/// alongside the other transports.
|
||||
#[test]
|
||||
fn test_transports_smart_card_advertised_when_ccid_enabled() {
|
||||
let options = Options {
|
||||
ccid_transport: true,
|
||||
..Default::default()
|
||||
};
|
||||
virt::run_ctap2_with_options(options, |device| {
|
||||
let reply = device.exec(GetInfo).unwrap();
|
||||
let transports = reply.transports.expect("transports list missing");
|
||||
assert!(
|
||||
transports.contains(&"smart-card".to_owned()),
|
||||
"smart-card missing from transports: {:?}",
|
||||
transports
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// `"smart-card"` is NOT advertised by default (no CCID).
|
||||
#[test]
|
||||
fn test_transports_smart_card_absent_when_ccid_disabled() {
|
||||
virt::run_ctap2(|device| {
|
||||
let reply = device.exec(GetInfo).unwrap();
|
||||
let transports = reply.transports.expect("transports list missing");
|
||||
assert!(!transports.contains(&"smart-card".to_owned()));
|
||||
})
|
||||
}
|
||||
|
||||
/// NFC + CCID together: all three transports advertised. Verifies the
|
||||
/// flags are independent.
|
||||
#[test]
|
||||
fn test_transports_nfc_and_smart_card_combined() {
|
||||
let options = Options {
|
||||
nfc_transport: true,
|
||||
ccid_transport: true,
|
||||
..Default::default()
|
||||
};
|
||||
virt::run_ctap2_with_options(options, |device| {
|
||||
let reply = device.exec(GetInfo).unwrap();
|
||||
let transports = reply.transports.expect("transports list missing");
|
||||
assert!(
|
||||
transports.contains(&"nfc".to_owned()),
|
||||
"nfc missing: {:?}",
|
||||
transports
|
||||
);
|
||||
assert!(
|
||||
transports.contains(&"smart-card".to_owned()),
|
||||
"smart-card missing: {:?}",
|
||||
transports
|
||||
);
|
||||
assert!(
|
||||
transports.contains(&"usb".to_owned()),
|
||||
"usb missing: {:?}",
|
||||
transports
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
+4
-1
@@ -73,7 +73,8 @@ where
|
||||
skip_up_timeout: None,
|
||||
max_resident_credential_count: options.max_resident_credential_count,
|
||||
large_blobs: None,
|
||||
nfc_transport: false,
|
||||
nfc_transport: options.nfc_transport,
|
||||
ccid_transport: options.ccid_transport,
|
||||
firmware_version: Some(0),
|
||||
},
|
||||
);
|
||||
@@ -134,6 +135,8 @@ pub type InspectFsFn = Box<dyn Fn(&dyn DynFilesystem)>;
|
||||
pub struct Options {
|
||||
pub files: Vec<(PathBuf, Vec<u8>)>,
|
||||
pub max_resident_credential_count: Option<u32>,
|
||||
pub nfc_transport: bool,
|
||||
pub ccid_transport: bool,
|
||||
pub inspect_ifs: Option<InspectFsFn>,
|
||||
}
|
||||
|
||||
|
||||
@@ -977,6 +977,7 @@ pub struct GetInfoReply {
|
||||
pub aaguid: Value,
|
||||
pub options: Option<BTreeMap<String, Value>>,
|
||||
pub pin_protocols: Option<Vec<u8>>,
|
||||
pub transports: Option<Vec<String>>,
|
||||
pub force_pin_change: Option<bool>,
|
||||
pub min_pin_length: Option<u32>,
|
||||
pub attestation_formats: Option<Vec<String>>,
|
||||
@@ -992,6 +993,8 @@ impl From<Value> for GetInfoReply {
|
||||
aaguid: map.remove(&3).unwrap().deserialized().unwrap(),
|
||||
options: map.remove(&4).map(|value| value.deserialized().unwrap()),
|
||||
pin_protocols: map.remove(&6).map(|value| value.deserialized().unwrap()),
|
||||
// 0x09: transports (CTAP 2.1)
|
||||
transports: map.remove(&9).map(|value| value.deserialized().unwrap()),
|
||||
// 0x0C: forcePINChange (CTAP 2.1)
|
||||
force_pin_change: map.remove(&0x0C).map(|value| value.deserialized().unwrap()),
|
||||
// 0x0D: minPINLength (CTAP 2.1)
|
||||
|
||||
Reference in New Issue
Block a user