Skip to content

Commit

Permalink
Common API changes needed to (later) add caBLE support.
Browse files Browse the repository at this point in the history
  • Loading branch information
micolous committed Jan 7, 2023
1 parent c761181 commit f060585
Show file tree
Hide file tree
Showing 31 changed files with 1,694 additions and 515 deletions.
6 changes: 5 additions & 1 deletion webauthn-authenticator-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repository = "https://github.com/kanidm/webauthn-rs"
[features]
nfc_raw_transmit = ["nfc"]
u2fhid = ["authenticator"]

nfc = ["pcsc"]
usb = ["hidapi"]
win10 = ["windows"]
Expand Down Expand Up @@ -50,7 +51,10 @@ num-derive = "0.3"
async-trait = "0.1.58"
futures = "0.3.25"

qrcode = { version = "^0.12.0", optional = true }

[dev-dependencies]
tracing-subscriber = { version = "0.3", features = ["env-filter", "std", "fmt"] }
base64 = "0.13"
clap = { version = "^3.2", features = ["derive", "env"] }
tokio = { version = "1.22.0", features = ["sync", "test-util", "macros", "rt-multi-thread", "time"] }
tempfile = { version = "3.3.0" }
113 changes: 76 additions & 37 deletions webauthn-authenticator-rs/examples/authenticate/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
#[macro_use]
extern crate tracing;

use std::fs::OpenOptions;
use std::io::{stdin, stdout, Write};

use clap::{Args, Parser, Subcommand};
use futures::executor::block_on;
use webauthn_authenticator_rs::ctap2::CtapAuthenticator;
use webauthn_authenticator_rs::prelude::Url;
use webauthn_authenticator_rs::softtoken::SoftToken;
use webauthn_authenticator_rs::softtoken::{SoftToken, SoftTokenFile};
use webauthn_authenticator_rs::transport::*;
use webauthn_authenticator_rs::types::CableRequestType;
use webauthn_authenticator_rs::ui::{Cli, UiCallback};
use webauthn_authenticator_rs::AuthenticatorBackend;
use webauthn_rs_core::proto::RequestAuthenticationExtensions;
use webauthn_rs_core::WebauthnCore as Webauthn;

#[derive(Debug, clap::Parser)]
#[clap(about = "Register and authenticate test")]
pub struct CliParser {
/// Provider to use.
#[clap(subcommand)]
provider: Provider,
}

fn select_transport<'a, U: UiCallback>(ui: &'a U) -> impl AuthenticatorBackend + 'a {
let mut reader = AnyTransport::new().unwrap();
info!("Using reader: {:?}", reader);
Expand All @@ -34,57 +45,74 @@ fn select_transport<'a, U: UiCallback>(ui: &'a U) -> impl AuthenticatorBackend +
panic!("No tokens available!");
}

fn select_provider<'a>(ui: &'a Cli) -> Box<dyn AuthenticatorBackend + 'a> {
let mut providers: Vec<(&str, fn(&'a Cli) -> Box<dyn AuthenticatorBackend>)> = Vec::new();
#[derive(Debug, Args, Clone)]
pub struct SoftTokenOpt {
/// Path to serialised key data, created by the softtoken example.
///
/// If not supplied, creates a temporary key in memory.
#[clap()]
pub path: Option<String>,
}

#[derive(Debug, Clone, Subcommand)]
enum Provider {
/// Software token provider
SoftToken(SoftTokenOpt),

providers.push(("SoftToken", |_| Box::new(SoftToken::new().unwrap().0)));
providers.push(("CTAP", |ui| Box::new(select_transport(ui))));
/// CtapAuthenticator using Transport/Token backends (NFC, USB HID)
///
/// Requires administrative permissions on Windows.
CTAP,

#[cfg(feature = "u2fhid")]
providers.push(("Mozilla", |_| {
Box::new(webauthn_authenticator_rs::u2fhid::U2FHid::default())
}));
/// Mozilla webauthn-authenticator-rs provider, supporting USB HID only.
Mozilla,

#[cfg(feature = "win10")]
providers.push(("Windows 10", |_| {
Box::new(webauthn_authenticator_rs::win10::Win10::default())
}));

if providers.is_empty() {
panic!("oops, no providers available in this build!");
}

loop {
println!("Select a provider:");
for (i, (name, _)) in providers.iter().enumerate() {
println!("({}): {}", i + 1, name);
}
/// Windows 10 WebAuthn API, supporting BTLE, NFC and USB HID.
Win10,
}

let mut buf = String::new();
print!("? ");
stdout().flush().ok();
stdin().read_line(&mut buf).expect("Cannot read stdin");
let selected: Result<u64, _> = buf.trim().parse();
match selected {
Ok(v) => {
if v < 1 || (v as usize) > providers.len() {
println!("Input out of range: {}", v);
impl Provider {
#[allow(unused_variables)]
async fn connect_provider<'a, U: UiCallback>(
&self,
request_type: CableRequestType,
ui: &'a U,
) -> Box<dyn AuthenticatorBackend + 'a> {
match self {
Provider::SoftToken(o) => {
if let Some(path) = &o.path {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(false)
.open(path)
.unwrap();
Box::new(SoftTokenFile::open(file).unwrap())
} else {
let p = providers.remove((v as usize) - 1);
println!("Using {}...", p.0);
return p.1(ui);
Box::new(SoftToken::new().unwrap().0)
}
}
Err(_) => println!("Input was not a number"),
Provider::CTAP => Box::new(select_transport(ui)),
#[cfg(feature = "u2fhid")]
Provider::Mozilla => Box::new(webauthn_authenticator_rs::u2fhid::U2FHid::default()),
#[cfg(feature = "win10")]
Provider::Win10 => Box::new(webauthn_authenticator_rs::win10::Win10::default()),
}
println!();
}
}

fn main() {
#[tokio::main]
async fn main() {
let opt = CliParser::parse();

tracing_subscriber::fmt::init();
let ui = Cli {};
let mut u = select_provider(&ui);
let provider = opt.provider;
let mut u = provider
.connect_provider(CableRequestType::MakeCredential, &ui)
.await;

// WARNING: don't use this as an example of how to use the library!
let wan = Webauthn::new_unsafe_experts_only(
Expand Down Expand Up @@ -118,8 +146,17 @@ fn main() {
let cred = wan.register_credential(&r, &reg_state, None).unwrap();

trace!(?cred);
drop(u);
let mut buf = String::new();
println!("WARNING: Some NFC keys need to be power-cycled before you can authenticate.");
println!("Press ENTER to authenticate, or Ctrl-C to abort");
stdout().flush().ok();
stdin().read_line(&mut buf).expect("Cannot read stdin");

loop {
u = provider
.connect_provider(CableRequestType::GetAssertion, &ui)
.await;
let (chal, auth_state) = wan
.generate_challenge_authenticate(
vec![cred.clone()],
Expand Down Expand Up @@ -150,6 +187,8 @@ fn main() {

info!("auth_res -> {:x?}", auth_res);
}

drop(u);
let mut buf = String::new();
println!("Press ENTER to try again, or Ctrl-C to abort");
stdout().flush().ok();
Expand Down
4 changes: 2 additions & 2 deletions webauthn-authenticator-rs/examples/conformance/core.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use pcsc::Scope;
use webauthn_authenticator_rs::ctap2::{commands::*, *};
use webauthn_authenticator_rs::ctap2::commands::*;
use webauthn_authenticator_rs::nfc::*;
use webauthn_authenticator_rs::transport::iso7816::*;

Expand Down Expand Up @@ -62,7 +62,7 @@ fn test_extended_lc_info(card: &NFCCard) -> TestResult {
return TestResult::Fail("Unsupported CTAP applet");
}

let mut get_info = (GetInfoRequest {}).to_extended_apdu().unwrap();
let mut get_info = to_extended_apdu((GetInfoRequest {}).cbor().unwrap());
get_info.ne = 65536;
resp = card
.transmit(&get_info, &ISO7816LengthForm::Extended)
Expand Down
36 changes: 18 additions & 18 deletions webauthn-authenticator-rs/examples/key_manager/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,23 +140,25 @@ fn main() {
println!("No tokens available!");
return;
}

let token_count = tokens.len();
// let authenticator = select_transport(&ui);
let authenticator = &tokens[0];
let authenticator = &mut tokens[0];

match opt.commands {
Opt::Selection => {
let token = block_on(select_one_token(tokens.iter()));
let token = block_on(select_one_token(tokens.iter_mut()));
println!("selected token: {:?}", token);
}

Opt::Info => {
for token in &tokens {
println!("{}", token.get_info());
println!("{:?}", token.get_info());
}
}

Opt::FactoryReset => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
println!("Resetting token to factory settings. Type 'yes' to continue.");
let mut buf = String::new();
stdout().flush().ok();
Expand All @@ -171,18 +173,18 @@ fn main() {
}

Opt::ToggleAlwaysUv => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
block_on(authenticator.toggle_always_uv()).expect("Error toggling UV");
}

Opt::EnableEnterpriseAttestation => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
block_on(authenticator.enable_enterprise_attestation())
.expect("Error enabling enterprise attestation");
}

Opt::BioInfo => {
for token in &tokens {
for token in &mut tokens {
if let CtapAuthenticator::Fido21(t) = token {
let i = block_on(t.get_fingerprint_sensor_info());
println!("Fingerprint sensor info: {:?}", i);
Expand All @@ -193,7 +195,7 @@ fn main() {
}

Opt::EnrollFingerprint(o) => {
let tokens: Vec<_> = tokens
let mut tokens: Vec<_> = tokens
.drain(..)
.filter_map(|t| {
if let CtapAuthenticator::Fido21(t) = t {
Expand All @@ -205,8 +207,7 @@ fn main() {
})
.collect();
assert_eq!(
tokens.len(),
1,
token_count, 1,
"Expected exactly 1 CTAP2.1 authenticator supporting biometrics"
);
let id =
Expand All @@ -216,7 +217,7 @@ fn main() {
}

Opt::ListFingerprints => {
let tokens: Vec<_> = tokens
let mut tokens: Vec<_> = tokens
.drain(..)
.filter_map(|t| {
if let CtapAuthenticator::Fido21(t) = t {
Expand Down Expand Up @@ -246,7 +247,7 @@ fn main() {
}

Opt::RenameFingerprint(o) => {
let tokens: Vec<_> = tokens
let mut tokens: Vec<_> = tokens
.drain(..)
.filter_map(|t| {
if let CtapAuthenticator::Fido21(t) = t {
Expand All @@ -273,7 +274,7 @@ fn main() {
}

Opt::RemoveFingerprint(o) => {
let tokens: Vec<_> = tokens
let mut tokens: Vec<_> = tokens
.drain(..)
.filter_map(|t| {
if let CtapAuthenticator::Fido21(t) = t {
Expand All @@ -285,8 +286,7 @@ fn main() {
})
.collect();
assert_eq!(
tokens.len(),
1,
token_count, 1,
"Expected exactly 1 CTAP2.1 authenticator supporting biometrics"
);
let ids: Vec<Vec<u8>> =
Expand All @@ -297,7 +297,7 @@ fn main() {
}

Opt::SetPinPolicy(o) => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
block_on(authenticator.set_min_pin_length(
o.length,
o.rpids.unwrap_or_default(),
Expand All @@ -307,12 +307,12 @@ fn main() {
}

Opt::SetPin(o) => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
block_on(authenticator.set_new_pin(&o.new_pin)).expect("Error setting PIN");
}

Opt::ChangePin(o) => {
assert_eq!(tokens.len(), 1);
assert_eq!(token_count, 1);
block_on(authenticator.change_pin(&o.old_pin, &o.new_pin)).expect("Error changing PIN");
}
}
Expand Down
43 changes: 43 additions & 0 deletions webauthn-authenticator-rs/examples/softtoken/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
extern crate tracing;

use std::fs::OpenOptions;

use clap::{Args, Parser, Subcommand};
use webauthn_authenticator_rs::softtoken::{SoftToken, SoftTokenFile};

#[derive(Debug, clap::Parser)]
#[clap(about = "SoftToken management tool")]
pub struct CliParser {
#[clap(subcommand)]
pub commands: Opt,
}

#[derive(Debug, Subcommand)]
pub enum Opt {
Create(CreateArgs),
}

#[derive(Debug, Args)]
pub struct CreateArgs {
#[clap()]
pub filename: String,
}

fn main() {
use Opt::*;

let opt = CliParser::parse();
tracing_subscriber::fmt::init();
match opt.commands {
Create(args) => {
let (token, _) = SoftToken::new().unwrap();
let f = OpenOptions::new()
.write(true)
.create_new(true)
.open(args.filename)
.unwrap();
let authenticator = SoftTokenFile::new(token, f);
drop(authenticator);
}
}
}
Loading

0 comments on commit f060585

Please sign in to comment.