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

YubiKey 5 vendor commands #415

Merged
merged 11 commits into from
Apr 10, 2024
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
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,15 @@ jobs:
# --help should be enough to find an issue.
- run: cargo run --bin cable-tunnel-server-backend -- --help
- run: cargo run --bin cable-tunnel-server-frontend -- --help
- run: cargo run --bin fido-mds-tool -- --help
# fido-key-manager requires elevation on Windows, which cargo can't
# handle.
- if: runner.os != 'windows'
run: cargo run --bin fido-key-manager -- --help
- if: runner.os != 'windows'
run: cargo run --bin fido-key-manager --features solokey -- --help
- run: cargo run --bin fido-mds-tool -- --help
- if: runner.os != 'windows'
run: cargo run --bin fido-key-manager --features yubikey -- --help

authenticator:
name: webauthn-authenticator-rs test
Expand All @@ -90,7 +92,7 @@ jobs:
- softtoken
- usb
- bluetooth,nfc,usb,ctap2-management
- bluetooth,cable,cable-override-tunnel,ctap2-management,nfc,softpasskey,softtoken,usb,vendor-solokey
- bluetooth,cable,cable-override-tunnel,ctap2-management,nfc,softpasskey,softtoken,usb,vendor-solokey,vendor-yubikey
os:
- ubuntu-latest
- windows-latest
Expand Down
1 change: 1 addition & 0 deletions fido-key-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ bluetooth = ["webauthn-authenticator-rs/bluetooth"]
nfc = ["webauthn-authenticator-rs/nfc"]
usb = ["webauthn-authenticator-rs/usb"]
solokey = ["webauthn-authenticator-rs/vendor-solokey"]
yubikey = ["webauthn-authenticator-rs/vendor-yubikey"]

default = ["nfc", "usb"]

Expand Down
20 changes: 20 additions & 0 deletions fido-key-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ Command | Description
`solo-key-info` | get all connected SoloKeys' unique ID, firmware version and secure boot status
`solo-key-random` | get some random bytes from a SoloKey

### YubiKey

> **Tip:** this functionality is only available when `fido-key-manager` is built
> with `--features yubikey`.

This only supports [YubiKey 5 series][yk5] and [Security Key by Yubico][sky]
devices via USB HID with the CTAP 2.0 interface (FIDO2) enabled. NFC support may
be added in future.

YubiKey 4 and earlier support is not planned - they do not support CTAP 2.0,
they use a different config format and protocol, and some firmware versions
report bogus data.

Command | Description
------- | -----------
`yubikey-get-config` | gets a connected YubiKey's device info, firmware version and interface configuration

[yk5]: https://www.yubico.com/products/yubikey-5-overview/
[sky]: https://www.yubico.com/products/security-key/

## Platform-specific notes

Bluetooth is currently disabled by default, as it's not particularly reliable on
Expand Down
20 changes: 20 additions & 0 deletions fido-key-manager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use hex::{FromHex, FromHexError};
use std::io::{stdin, stdout, Write};
use std::time::Duration;
use tokio_stream::StreamExt;
#[cfg(feature = "yubikey")]
use webauthn_authenticator_rs::ctap2::YubiKeyAuthenticator;
#[cfg(feature = "solokey")]
use webauthn_authenticator_rs::{ctap2::SoloKeyAuthenticator, prelude::WebauthnCError};
use webauthn_authenticator_rs::{
Expand Down Expand Up @@ -205,6 +207,8 @@ pub enum Opt {
#[cfg(feature = "solokey")]
/// Gets some random bytes from a connected SoloKey 2 or Trussed device.
SoloKeyRandom,
#[cfg(feature = "yubikey")]
YubikeyGetConfig,
}

#[derive(Debug, clap::Parser)]
Expand Down Expand Up @@ -754,5 +758,21 @@ async fn main() {
.expect("Error getting random data");
println!("Random bytes: {}", hex::encode(r));
}

#[cfg(feature = "yubikey")]
Opt::YubikeyGetConfig => {
// TODO: filter this to just YubiKey devices in a safe way
println!("Insert a YubiKey device...");
let mut token: CtapAuthenticator<AnyToken, Cli> =
select_one_device(stream, &ui).await.unwrap();

let cfg = token
.get_yubikey_config()
.await
.expect("Error getting YubiKey config");

println!("YubiKey config:");
println!("{cfg}")
}
}
}
2 changes: 2 additions & 0 deletions webauthn-authenticator-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ ctap2 = [
ctap2-management = ["ctap2"]
# Support for SoloKey's vendor commands
vendor-solokey = []
# Support for YubiKey's vendor commands
vendor-yubikey = []
nfc = ["ctap2", "dep:pcsc"]
# TODO: allow running softpasskey without softtoken
softpasskey = ["crypto", "softtoken"]
Expand Down
5 changes: 4 additions & 1 deletion webauthn-authenticator-rs/src/ctap2/ctap20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,10 @@ impl<'a, T: Token, U: UiCallback> Ctap20Authenticator<'a, T, U> {
let ret = self.token.transmit(mc, self.ui_callback).await;

if let Err(WebauthnCError::Ctap(e)) = ret {
if e == CtapError::Ctap2PinAuthInvalid || e == CtapError::Ctap2PinNotSet {
if e == CtapError::Ctap2PinAuthInvalid
|| e == CtapError::Ctap2PinNotSet
|| e == CtapError::Ctap2PinInvalid
{
// User pressed the button
return Ok(());
}
Expand Down
7 changes: 7 additions & 0 deletions webauthn-authenticator-rs/src/ctap2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ mod pin_uv;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-solokey"))]
#[doc(hidden)]
mod solokey;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
#[doc(hidden)]
mod yubikey;

use std::ops::{Deref, DerefMut};
use std::pin::Pin;
Expand Down Expand Up @@ -166,6 +169,10 @@ pub use self::{
#[doc(inline)]
pub use self::solokey::SoloKeyAuthenticator;

#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
#[doc(inline)]
pub use self::yubikey::YubiKeyAuthenticator;

/// Abstraction for different versions of the CTAP2 protocol.
///
/// All tokens can [Deref] into [Ctap20Authenticator].
Expand Down
37 changes: 37 additions & 0 deletions webauthn-authenticator-rs/src/ctap2/yubikey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use async_trait::async_trait;

use crate::{
prelude::WebauthnCError,
transport::{
yubikey::{YubiKeyConfig, YubiKeyToken},
Token,
},
ui::UiCallback,
};

use super::Ctap20Authenticator;

/// YubiKey vendor-specific commands.
///
/// ## Warning
///
/// These commands currently operate on *any* [`Ctap20Authenticator`][], and do
/// not filter to just YubiKey devices. Due to the nature of CTAP
/// vendor-specific commands, this may cause unexpected or undesirable behaviour
/// on other vendors' keys.
///
/// Protocol notes are in [`crate::transport::yubikey`].
#[async_trait]
pub trait YubiKeyAuthenticator {
async fn get_yubikey_config(&mut self) -> Result<YubiKeyConfig, WebauthnCError>;
}

#[async_trait]
impl<'a, T: Token + YubiKeyToken, U: UiCallback> YubiKeyAuthenticator
for Ctap20Authenticator<'a, T, U>
{
#[inline]
async fn get_yubikey_config(&mut self) -> Result<YubiKeyConfig, WebauthnCError> {
self.token.get_yubikey_config().await
}
}
2 changes: 2 additions & 0 deletions webauthn-authenticator-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ mod crypto;
#[cfg(any(all(doc, not(doctest)), feature = "ctap2"))]
pub mod ctap2;
pub mod error;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
mod tlv;
#[cfg(any(all(doc, not(doctest)), feature = "ctap2"))]
pub mod transport;
pub mod types;
Expand Down
Loading
Loading