Open micolous opened 1 year ago
- Handle devices being plugged in / appearing after the prompt flow has started
- make
Transport.tokens()
async and return aStream
ofToken
?
I think this is should be the first thing to look at in terms of improving the user experience. It would also unlock being able to handle authenticator selection.
Looking at device monitoring (hot-plug events) this on a transport level:
This should be possible with Adapter::events()
. The library already uses something similar for cable
; and it may need to be merged together.
The caBLE UI flow works differently, and already has to listen for BTLE advertisements to start the handshake process.
The library already tracks reader states, and we can listen for events with Context::get_status_change
.
It looks like hidapi-rs
won't do this. You can only poll for devices with HidApi::refresh_devices()
and HidApi::device_list()
. Monitoring for device connection / disconnection also isn't supported by the underlying hidapi
C library, so would require basically rewriting hidapi-rs
(to eliminate the hidapi
dependency) to make it work, but there is a plan to do this upstream.
rusb
looked promising, but it looks like it's using libusb
underneath, so HID device access will be problematic.
Mozilla's authenticator-rs
has its own HID handling code, which is Rust-native and supports monitoring for devices. This tries to avoid taking dependencies on platform-specific crates, and instead provides its own bindings for everything.
I'm leaning towards forking Mozilla's authenticator-rs
HID handling code; it seems to be the most complete here, but it will mean that we'd need to ship a pile of platform-specific code for it... which is something I had explicitly hoped to avoid.
I started looking into refactoring around USB HID on Windows first in https://github.com/micolous/webauthn-rs/tree/mozilla-hid
It appears that authenticator-rs
on Windows uses the old USB HID APIs which are not async at all, and discover devices based on polling:
There is a way to make this all async, and that's to use the new UWP APIs (Devices::HumanInterfaceDevice::HidDevice
) and to watch for events with DeviceInformation::CreateWatcher
. This will only work on Windows 10 and later, which is in line with the support bar for the rest of this library.
Where I'm at:
DeviceInformation::CreateWatcher
- even existing devicesunwrap
everywhere, and have hard coded a bunch of Windows stuff for nowOnce I've gotten the Windows side of things working, I should be able to then pivot to the others.
My mozilla-hid
branch is now a bit cleaner, implements discovery, and works on macOS and Linux too.
I didn't end up using much of the Mozilla authenticator-rs
implementation at all; but it was helpful for finding the right APIs to use on macOS, but its models (particularly around threads) are completely different, and I ended up rewriting a lot of the FFI bindings to use core_foundation
types. Unfortunately, macOS doesn't have an "enumeration complete" event (and it provides a list of all existing devices when starting discovery), so that's currently faked.
Mozilla's code supports FreeBSD and NetBSD as well, but I'm not targeting those for now. It looks like at least on FreeBSD there is some Linux HID ioctl
compatibility, but I think this would still need to replace all the udev
stuff.
What's left to do:
fido-key-manager
to work on this event-based APII've started working on NFC support, and gotten basic device watching working. There are still some rough edges when tokens and/or NFC transceivers disappear at inopportune times.
I've also got AnyTransport working with both NFC and USB, but need to put the feature flag gates back in.
> .\target\debug\fido-key-manager.exe info --watch
DANGER: make sure you only have one key connected
2023-05-26T07:22:02.135341Z TRACE webauthn_authenticator_rs::usb::platform::os: watch_devices
2023-05-26T07:22:02.136409Z TRACE webauthn_authenticator_rs::nfc: New reader: "ACS ACR122 0"
2023-05-26T07:22:02.136667Z TRACE webauthn_authenticator_rs::nfc: Reader "ACS ACR122 0" current_state: UNAWARE, event_state: CHANGED | EMPTY
2023-05-26T07:22:02.149468Z TRACE webauthn_authenticator_rs::usb::platform::os: Starting WindowsDeviceWatcher
2023-05-26T07:22:02.150626Z TRACE webauthn_authenticator_rs::transport::any: NFC event: EnumerationComplete
2023-05-26T07:22:02.163129Z TRACE webauthn_authenticator_rs::usb: watch_tokens event: EnumerationComplete
2023-05-26T07:22:02.163228Z TRACE webauthn_authenticator_rs::transport::any: Sending enumeration complete from USB
Initial enumeration completed, watching for more devices...
Press Ctrl-C to stop watching.
2023-05-26T07:22:08.855336Z TRACE webauthn_authenticator_rs::usb: watch_tokens event: Added(WindowsUSBDeviceInfo { id: "\\\\?\\HID#VID_1050&PID_0402#9&6bfd493&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", name: "YubiKey FIDO" })
2023-05-26T07:22:08.855528Z TRACE webauthn_authenticator_rs::usb::platform::os: Opening device: WindowsUSBDeviceInfo { id: "\\\\?\\HID#VID_1050&PID_0402#9&6bfd493&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", name: "YubiKey FIDO" }
2023-05-26T07:22:08.860221Z TRACE webauthn_authenticator_rs::usb: >>> 00ffffffff860008a38f776aaf65069a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2023-05-26T07:22:08.864201Z TRACE webauthn_authenticator_rs::usb: <<< ffffffff860011a38f776aaf65069a2aa6b262020504030d00000000000000000000000000000000000000000000000000000000000000000000000000000000
2023-05-26T07:22:08.864305Z TRACE webauthn_authenticator_rs::usb: i=InitResponse { nonce: [163, 143, 119, 106, 175, 101, 6, 154], cid: 715567714, protocol_version: 2, device_version_major: 5, device_version_minor: 4, device_version_build: 3, capabilities: 13 }
2023-05-26T07:22:08.864406Z TRACE webauthn_authenticator_rs::transport: >>> 04
2023-05-26T07:22:08.864476Z TRACE webauthn_authenticator_rs::usb: >>> 002aa6b262900001040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
2023-05-26T07:22:08.868109Z TRACE webauthn_authenticator_rs::usb: <<< 2aa6b2629000c500ac0182684649444f5f325f306c4649444f5f325f315f50524502826b6372656450726f746563746b686d61632d7365637265740350149a20
2023-05-26T07:22:08.870104Z TRACE webauthn_authenticator_rs::usb: <<< 2aa6b26200218ef6413396b881f8d5b7f1f504a562726bf5627570f564706c6174f469636c69656e7450696ef57563726564656e7469616c4d676d7450726576
2023-05-26T07:22:08.872160Z TRACE webauthn_authenticator_rs::usb: <<< 2aa6b26201696577f5051904b00682020107080818800982636e6663637573620a82a263616c672664747970656a7075626c69632d6b6579a263616c67276474
2023-05-26T07:22:08.874182Z TRACE webauthn_authenticator_rs::usb: <<< 2aa6b262027970656a7075626c69632d6b65790d040e1a0005040300000000000000000000000000000000000000000000000000000000000000000000000000
2023-05-26T07:22:08.874366Z TRACE webauthn_authenticator_rs::transport: <<< ac0182684649444f5f325f306c4649444f5f325f315f50524502826b6372656450726f746563746b686d61632d7365637265740350149a20218ef6413396b881f8d5b7f1f504a562726bf5627570f564706c6174f469636c69656e7450696ef57563726564656e7469616c4d676d7450726576696577f5051904b00682020107080818800982636e6663637573620a82a263616c672664747970656a7075626c69632d6b6579a263616c672764747970656a7075626c69632d6b65790d040e1a00050403
2023-05-26T07:22:08.874621Z TRACE webauthn_authenticator_rs::ctap2::commands::get_info: raw = {1: Array([Text("FIDO_2_0"), Text("FIDO_2_1_PRE")]), 2: Array([Text("credProtect"), Text("hmac-secret")]), 3: Bytes([20, 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, 245]), 4: Map({Text("rk"): Bool(true), Text("up"): Bool(true), Text("plat"): Bool(false), Text("clientPin"): Bool(true), Text("credentialMgmtPreview"): Bool(true)}), 5: Integer(1200), 6: Array([Integer(2), Integer(1)]), 7: Integer(8), 8: Integer(128), 9: Array([Text("nfc"), Text("usb")]), 10: Array([Map({Text("alg"): Integer(-7), Text("type"): Text("public-key")}), Map({Text("alg"): Integer(-8), Text("type"): Text("public-key")})]), 13: Integer(4), 14: Integer(328707)}
versions: FIDO_2_0 FIDO_2_1_PRE
extensions: credProtect hmac-secret
aaguid: 149a2021-8ef6-4133-96b8-81f8d5b7f1f5
options: clientPin:true credentialMgmtPreview:true plat:false rk:true up:true
max message size: 1200
PIN protocols: 2 1
max cred count in list: 8
max cred ID length: 128
transports: nfc usb
algorithms: Array([Map({Text("alg"): Integer(-7), Text("type"): Text("public-key")}), Map({Text("alg"): Integer(-8), Text("type"): Text("public-key")})])
force PIN change: false
minimum PIN length: 4
firmware version: 0x50403
2023-05-26T07:22:10.869012Z TRACE webauthn_authenticator_rs::usb: watch_tokens event: Removed(\\?\HID#VID_1050&PID_0402#9&6bfd493&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030})
2023-05-26T07:22:13.203573Z TRACE webauthn_authenticator_rs::nfc: Reader "ACS ACR122 0" current_state: CHANGED | EMPTY, event_state: CHANGED | PRESENT
2023-05-26T07:22:13.203721Z TRACE webauthn_authenticator_rs::nfc: ATR: 3b8d80018073c021c057597562694b6579f9
2023-05-26T07:22:13.203801Z TRACE webauthn_authenticator_rs::nfc: Parsed: Atr { protocols: [0, 1], t1: [128, 115, 192, 33, 192, 87, 89, 117, 98, 105, 75, 101, 121], storage_card: false, card_issuers_data: Some([89, 117, 98, 105, 75, 101, 121]), command_chaining: Some(true), extended_lc: Some(true) }
2023-05-26T07:22:13.209686Z TRACE webauthn_authenticator_rs::nfc: >>> 00a4040008a0000006472f000100
2023-05-26T07:22:13.210497Z TRACE webauthn_authenticator_rs::nfc: Reader "ACS ACR122 0" current_state: CHANGED | PRESENT, event_state: CHANGED | PRESENT | EXCLUSIVE | INUSE
2023-05-26T07:22:13.210589Z TRACE webauthn_authenticator_rs::nfc: ignoring in-use card
2023-05-26T07:22:13.488751Z TRACE webauthn_authenticator_rs::nfc: <<< 5532465f56329000
2023-05-26T07:22:13.488917Z TRACE webauthn_authenticator_rs::transport::any: NFC event: Added(NFCCard { reader_name: "ACS ACR122 0", atr: Atr { protocols: [0, 1], t1: [128, 115, 192, 33, 192, 87, 89, 117, 98, 105, 75, 101, 121], storage_card: false, card_issuers_data: Some([89, 117, 98, 105, 75, 101, 121]), command_chaining: Some(true), extended_lc: Some(true) } })
2023-05-26T07:22:13.489041Z TRACE webauthn_authenticator_rs::nfc: >>> 00a4040008a0000006472f000100
2023-05-26T07:22:13.507438Z TRACE webauthn_authenticator_rs::nfc: <<< 5532465f56329000
2023-05-26T07:22:13.507526Z TRACE webauthn_authenticator_rs::transport: >>> 04
2023-05-26T07:22:13.507610Z TRACE webauthn_authenticator_rs::nfc: >>> 80100000010400
2023-05-26T07:22:13.618258Z TRACE webauthn_authenticator_rs::nfc: <<< 00ac0182684649444f5f325f306c4649444f5f325f315f50524502826b6372656450726f746563746b686d61632d7365637265740350149a20218ef6413396b881f8d5b7f1f504a562726bf5627570f564706c6174f469636c69656e7450696ef57563726564656e7469616c4d676d7450726576696577f5051904b00682020107080818800982636e6663637573620a82a263616c672664747970656a7075626c69632d6b6579a263616c672764747970656a7075626c69632d6b65790d040e1a000504039000
2023-05-26T07:22:13.618421Z TRACE webauthn_authenticator_rs::transport: <<< ac0182684649444f5f325f306c4649444f5f325f315f50524502826b6372656450726f746563746b686d61632d7365637265740350149a20218ef6413396b881f8d5b7f1f504a562726bf5627570f564706c6174f469636c69656e7450696ef57563726564656e7469616c4d676d7450726576696577f5051904b00682020107080818800982636e6663637573620a82a263616c672664747970656a7075626c69632d6b6579a263616c672764747970656a7075626c69632d6b65790d040e1a00050403
2023-05-26T07:22:13.618604Z TRACE webauthn_authenticator_rs::ctap2::commands::get_info: raw = {1: Array([Text("FIDO_2_0"), Text("FIDO_2_1_PRE")]), 2: Array([Text("credProtect"), Text("hmac-secret")]), 3: Bytes([20, 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, 245]), 4: Map({Text("rk"): Bool(true), Text("up"): Bool(true), Text("plat"): Bool(false), Text("clientPin"): Bool(true), Text("credentialMgmtPreview"): Bool(true)}), 5: Integer(1200), 6: Array([Integer(2), Integer(1)]), 7: Integer(8), 8: Integer(128), 9: Array([Text("nfc"), Text("usb")]), 10: Array([Map({Text("alg"): Integer(-7), Text("type"): Text("public-key")}), Map({Text("alg"): Integer(-8), Text("type"): Text("public-key")})]), 13: Integer(4), 14: Integer(328707)}
versions: FIDO_2_0 FIDO_2_1_PRE
extensions: credProtect hmac-secret
aaguid: 149a2021-8ef6-4133-96b8-81f8d5b7f1f5
options: clientPin:true credentialMgmtPreview:true plat:false rk:true up:true
max message size: 1200
PIN protocols: 2 1
max cred count in list: 8
max cred ID length: 128
transports: nfc usb
algorithms: Array([Map({Text("alg"): Integer(-7), Text("type"): Text("public-key")}), Map({Text("alg"): Integer(-8), Text("type"): Text("public-key")})])
force PIN change: false
minimum PIN length: 4
firmware version: 0x50403
2023-05-26T07:22:13.620314Z TRACE webauthn_authenticator_rs::nfc: Reader "ACS ACR122 0" current_state: CHANGED | PRESENT | EXCLUSIVE | INUSE, event_state: CHANGED | PRESENT
2023-05-26T07:22:14.546287Z TRACE webauthn_authenticator_rs::nfc: Reader "ACS ACR122 0" current_state: CHANGED | PRESENT, event_state: CHANGED | EMPTY
Context: https://github.com/kanidm/webauthn-rs/blob/master/designs/authenticator-library.md#implement-an-authenticatorbackend-for-transporttoken
This will:
authenticator-rs
library (which only supports USB)PRs:
215 targets base functionality only
267
261
Pending work ideas:
Transport.tokens()
async
and return aStream
ofToken
?authenticatorSelection
only works in CTAP 2.1)authenticatorCredentialManagement
)authenticatorLargeBlobs
)authenticatorSelection
only works in CTAP 2.1, so need something else:wait_for_token
)pin_uv_auth_token
andiface
)Related:
259