Skip to content

Commit

Permalink
WIP: implement yubikey config
Browse files Browse the repository at this point in the history
  • Loading branch information
micolous committed Oct 26, 2023
1 parent 98285b8 commit b910768
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 1 deletion.
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
18 changes: 18 additions & 0 deletions fido-key-manager/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use std::time::Duration;
use tokio_stream::StreamExt;
#[cfg(feature = "solokey")]
use webauthn_authenticator_rs::ctap2::SoloKeyAuthenticator;
#[cfg(feature = "yubikey")]
use webauthn_authenticator_rs::ctap2::YubiKeyAuthenticator;
use webauthn_authenticator_rs::prelude::WebauthnCError;
use webauthn_authenticator_rs::{
ctap2::{
Expand Down Expand Up @@ -206,6 +208,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 @@ -770,5 +774,19 @@ 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 r = token
.get_yubikey_config()
.await
.expect("Error getting random data");
todo!()
}
}
}
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
34 changes: 34 additions & 0 deletions webauthn-authenticator-rs/src/ctap2/yubikey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use async_trait::async_trait;

use crate::{
prelude::WebauthnCError,
transport::{yubikey::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 TODO
#[async_trait]
pub trait YubiKeyAuthenticator {
async fn get_yubikey_config(&mut self) -> Result<bool, 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<bool, WebauthnCError> {
self.token.get_yubikey_config().await
}
}
2 changes: 2 additions & 0 deletions webauthn-authenticator-rs/src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub mod iso7816;
pub(crate) mod solokey;
#[cfg(any(doc, feature = "bluetooth", feature = "usb"))]
pub(crate) mod types;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
pub(crate) mod yubikey;

pub use crate::transport::any::{AnyToken, AnyTransport};

Expand Down
45 changes: 45 additions & 0 deletions webauthn-authenticator-rs/src/transport/yubikey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! YubiKey vendor-specific commands.
//!
//! ## USB HID
//!
//! Commands are sent on a `U2FHIDFrame` level, and values are bitwise-OR'd
//! with `transport::TYPE_INIT` (0x80).
//!
//! Command | Description | Request | Response
//! ------- | ----------- | ------- | --------
//!
//! ## NFC
//!
//! ## References
//!
use async_trait::async_trait;

use crate::prelude::WebauthnCError;

use super::AnyToken;

#[cfg(all(feature = "usb", feature = "vendor-yubikey"))]
pub(crate) const CMD_GET_CONFIG: u8 = super::TYPE_INIT | 0x42;

/// See [`YubiKeyAuthenticator`](crate::ctap2::YubiKeyAuthenticator).
#[async_trait]
pub trait YubiKeyToken {
/// See [`SoloKeyAuthenticator::get_solokey_lock()`](crate::ctap2::SoloKeyAuthenticator::get_solokey_lock).
async fn get_yubikey_config(&mut self) -> Result<bool, WebauthnCError>;
}

#[async_trait]
#[allow(clippy::unimplemented)]
impl YubiKeyToken for AnyToken {
async fn get_yubikey_config(&mut self) -> Result<bool, WebauthnCError> {
match self {
AnyToken::Stub => unimplemented!(),
#[cfg(feature = "bluetooth")]
AnyToken::Bluetooth(_) => Err(WebauthnCError::NotSupported),
#[cfg(feature = "nfc")]
AnyToken::Nfc(_) => Err(WebauthnCError::NotSupported),
#[cfg(feature = "usb")]
AnyToken::Usb(u) => u.get_yubikey_config().await,
}
}
}
2 changes: 2 additions & 0 deletions webauthn-authenticator-rs/src/usb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ mod framing;
mod responses;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-solokey"))]
mod solokey;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
mod yubikey;

use fido_hid_rs::{
HidReportBytes, HidSendReportBytes, USBDevice, USBDeviceImpl, USBDeviceInfo, USBDeviceInfoImpl,
Expand Down
44 changes: 44 additions & 0 deletions webauthn-authenticator-rs/src/usb/yubikey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use async_trait::async_trait;
use uuid::Uuid;

#[cfg(all(feature = "usb", feature = "vendor-yubikey"))]
use crate::transport::yubikey::CMD_GET_CONFIG;

use crate::{
prelude::WebauthnCError,
transport::{
types::{U2FError, U2FHID_ERROR},
yubikey::YubiKeyToken,
},
usb::{framing::U2FHIDFrame, USBToken},
};

#[async_trait]
impl YubiKeyToken for USBToken {
async fn get_yubikey_config(&mut self) -> Result<bool, WebauthnCError> {
let cmd = U2FHIDFrame {
cid: self.cid,
cmd: CMD_GET_CONFIG,
len: 0,
data: vec![],
};
self.send_one(&cmd).await?;

let r = self.recv_one().await?;
debug!("config: {}", hex::encode(r.data));
todo!()
// match r.cmd {
// CMD_LOCK => {
// if r.len != 1 || r.data.len() != 1 {
// return Err(WebauthnCError::InvalidMessageLength);
// }

// Ok(r.data[0] != 0)
// }

// U2FHID_ERROR => Err(U2FError::from(r.data.as_slice()).into()),

// _ => Err(WebauthnCError::UnexpectedState),
// }
}
}

0 comments on commit b910768

Please sign in to comment.