Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webauthn-authenticator-rs: build bindings from Transport/Token -> AuthenticatorBackend #214

Open
micolous opened this issue Oct 11, 2022 · 4 comments
Assignees
Labels
ctap2 Issues related to the CtapAuthenticator backend enhancement New feature or request

Comments

@micolous
Copy link
Collaborator

micolous commented Oct 11, 2022

Context: https://github.com/kanidm/webauthn-rs/blob/master/designs/authenticator-library.md#implement-an-authenticatorbackend-for-transporttoken

This will:

  • let the library work on platforms without a platform-level WebAuthn API (eg: Linux)
  • test the robustness of the CBOR implementation with a "complete" registration and authentication flow
  • potentially allow us to replace Mozilla's authenticator-rs library (which only supports USB)

PRs:

Pending work ideas:

  • Make the library's API and UI as easy as Windows WebAuthn API:
    • Handle devices being plugged in / appearing after the prompt flow has started
      • make Transport.tokens() async and return a Stream of Token?
    • Handle having multiple authenticators connected (authenticatorSelection only works in CTAP 2.1)
    • Pre-flighting requests
      • For registration/create, ignore authenticators in the exclude list
      • For authentication/get, only use authenticators in the allow list
    • Handle first-time set-up (setting a PIN) where required
    • Browser-like transport selection prompt ("use a security key", "use caBLE", "use a platform authenticator", etc.)
      • ...and interoperate with Windows for extending its functionality (caBLE)
  • CTAP v1 / U2F, including automatic fall-back
  • discoverable credentials (authenticatorCredentialManagement)
  • large blobs (authenticatorLargeBlobs)
  • enterprise attestation
  • Bluetooth Low Energy authenticators (in progress: BTLE authenticator transport #272)
  • MakeCredential / GetAssertion extensions
  • cancellations
  • Handling selection for multiple authenticators. authenticatorSelection only works in CTAP 2.1, so need something else:
    • come up with another strategy for MakeCredential/GetAssertion with multiple tokens
    • Handle user interaction with a selected token
    • handle multiple PC/SC readers
    • handle composite PC/SC devices (eg: ACR 123U)
    • handle connecting or disconnecting an authenticator while a prompt is in progress
    • handle connecting or disconnecting a PC/SC reader while a prompt is in progress
    • handle when there are no tokens connected (ie: wait_for_token)
  • improved session handling (find a nice place to stash pin_uv_auth_token and iface)
  • PIN / UV retry limits

Related:

@micolous micolous self-assigned this Oct 11, 2022
@micolous micolous added enhancement New feature or request ctap2 Issues related to the CtapAuthenticator backend labels Feb 3, 2023
@micolous
Copy link
Collaborator Author

  • Handle devices being plugged in / appearing after the prompt flow has started
    • make Transport.tokens() async and return a Stream of Token?

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:

BTLE

This should be possible with Adapter::events(). The library already uses something similar for cable; and it may need to be merged together.

caBLE

The caBLE UI flow works differently, and already has to listen for BTLE advertisements to start the handshake process.

NFC

The library already tracks reader states, and we can listen for events with Context::get_status_change.

USB HID

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.

@micolous
Copy link
Collaborator Author

micolous commented May 12, 2023

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:

https://github.com/mozilla/authenticator-rs/blob/110b691ff105062352bcc9f00c36c77c42d4e45c/src/windows/monitor.rs#L33-L61

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:

  • communication with tokens over the UWP APIs works
  • the devices aren't actually hidden over this API for non-Administrators in the same way
  • I've got device filtering by usage/usage page working properly
  • discovery doesn't work properly yet - I suspect there's a lifetime issue that I need to sort out; but from the windows-rs example I suspect Windows should be able to work with everything on DeviceInformation::CreateWatcher - even existing devices
  • everything is a bodge, lots of unwrap everywhere, and have hard coded a bunch of Windows stuff for now
  • there's possibly some manifest stuff that'll be needed for Windows Store distribution

Once I've gotten the Windows side of things working, I should be able to then pivot to the others.

@micolous
Copy link
Collaborator Author

micolous commented May 26, 2023

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:

  • make sure error handling is sensible
  • adapt the BTLE, caBLE and NFC transports to the new USB API
  • finish rewriting fido-key-manager to work on this event-based API

@micolous
Copy link
Collaborator Author

I'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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ctap2 Issues related to the CtapAuthenticator backend enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant