You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.3 KiB
Rust
188 lines
5.3 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::ops::Deref;
|
|
|
|
use ctap2_proto::extensions;
|
|
use ctap2_proto::prelude::*;
|
|
use ctap2_proto::Ctap2_2Authenticator;
|
|
|
|
extern crate ctap2_proto;
|
|
|
|
struct FidoKey(ctap_hid_fido2::FidoKeyHid);
|
|
|
|
impl Deref for FidoKey {
|
|
type Target = ctap_hid_fido2::FidoKeyHid;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl Ctap2_2Authenticator for FidoKey {
|
|
fn make_credential(&mut self, request: make::Request) -> Result<make::Response, make::Error> {
|
|
let args = MakeCredentialArgsBuilder::new(
|
|
&request.relying_party.id,
|
|
request.client_data_hash.as_ref(),
|
|
)
|
|
.build();
|
|
|
|
let attestation = match self.make_credential_with_args(&args) {
|
|
Ok(attestation) => attestation,
|
|
Err(e) => {
|
|
todo!("unhandled error: {e}") // anyhow::Error requires manually
|
|
// mapping
|
|
}
|
|
};
|
|
|
|
let format = if !attestation.flags_attested_credential_data_included {
|
|
FormatIdentifier::None
|
|
} else {
|
|
unimplemented!("do not support attestation yet")
|
|
};
|
|
|
|
let authenticator_data = attestation.auth_data.as_slice().try_into().unwrap();
|
|
|
|
let unsigned_extension_outputs: BTreeMap<_, _> = attestation
|
|
.extensions
|
|
.into_iter()
|
|
.filter_map(|extension| -> Option<(extensions::Identifier, Vec<u8>)> {
|
|
match extension {
|
|
CredentialExtension::CredBlob(_) => {
|
|
todo!()
|
|
}
|
|
CredentialExtension::CredProtect(_) => {
|
|
todo!()
|
|
}
|
|
CredentialExtension::HmacSecret(_) => {
|
|
todo!()
|
|
}
|
|
CredentialExtension::LargeBlobKey(_) => {
|
|
todo!()
|
|
}
|
|
CredentialExtension::MinPinLength(_) => {
|
|
todo!()
|
|
}
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let unsigned_extension_outputs = if !unsigned_extension_outputs.is_empty() {
|
|
Some(unsigned_extension_outputs)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(make::Response {
|
|
format,
|
|
authenticator_data,
|
|
enterprise_attestation: None,
|
|
large_blob_key: None,
|
|
unsigned_extension_outputs,
|
|
})
|
|
}
|
|
|
|
fn get_assertion(request: get::Request) -> Result<get::Response, get::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn get_info(&self) -> device::Info {
|
|
todo!()
|
|
}
|
|
|
|
fn client_pin(request: client_pin::Request) -> Result<client_pin::Response, client_pin::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn reset() -> Result<(), reset::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn selection() -> Result<(), ctap2_proto::authenticator::selection::Error> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
use ctap_hid_fido2::fidokey::CredentialExtension;
|
|
use ctap_hid_fido2::{
|
|
fidokey::{GetAssertionArgsBuilder, MakeCredentialArgsBuilder},
|
|
verifier, Cfg, FidoKeyHidFactory,
|
|
};
|
|
use fido_common::attestation::FormatIdentifier;
|
|
|
|
fn main() {
|
|
let rpid = "reg-auth-example-app";
|
|
let pin = get_input_with_message("input PIN:");
|
|
|
|
println!("Register");
|
|
// create `challenge`
|
|
let challenge = verifier::create_challenge();
|
|
|
|
// create `MakeCredentialArgs`
|
|
let make_credential_args = MakeCredentialArgsBuilder::new(rpid, &challenge)
|
|
.pin(&pin)
|
|
.build();
|
|
|
|
let mut cfg = Cfg::init();
|
|
cfg.enable_log = false;
|
|
|
|
// create `FidoKeyHid`
|
|
let device = FidoKeyHidFactory::create(&cfg).unwrap();
|
|
|
|
if device.get_info().unwrap().force_pin_change {
|
|
device.set_new_pin("1234").unwrap();
|
|
}
|
|
|
|
// get `Attestation` Object
|
|
let attestation = device
|
|
.make_credential_with_args(&make_credential_args)
|
|
.unwrap();
|
|
println!("- Register Success");
|
|
|
|
// verify `Attestation` Object
|
|
let verify_result = verifier::verify_attestation(rpid, &challenge, &attestation);
|
|
if !verify_result.is_success {
|
|
println!("- ! Verify Failed");
|
|
return;
|
|
}
|
|
|
|
// store Credential Id and Publickey
|
|
let userdata_credential_id = verify_result.credential_id;
|
|
let userdata_credential_public_key = verify_result.credential_public_key;
|
|
|
|
println!("Authenticate");
|
|
// create `challenge`
|
|
let challenge = verifier::create_challenge();
|
|
|
|
// create `GetAssertionArgs`
|
|
let get_assertion_args = GetAssertionArgsBuilder::new(rpid, &challenge)
|
|
.pin(&pin)
|
|
.credential_id(&userdata_credential_id)
|
|
.build();
|
|
|
|
// get `Assertion` Object
|
|
let assertions = device.get_assertion_with_args(&get_assertion_args).unwrap();
|
|
println!("- Authenticate Success");
|
|
|
|
// verify `Assertion` Object
|
|
if !verifier::verify_assertion(
|
|
rpid,
|
|
&userdata_credential_public_key,
|
|
&challenge,
|
|
&assertions[0],
|
|
) {
|
|
println!("- ! Verify Assertion Failed");
|
|
}
|
|
}
|
|
|
|
pub fn get_input() -> String {
|
|
let mut word = String::new();
|
|
std::io::stdin().read_line(&mut word).ok();
|
|
return word.trim().to_string();
|
|
}
|
|
|
|
pub fn get_input_with_message(message: &str) -> String {
|
|
println!("{}", message);
|
|
let input = get_input();
|
|
println!();
|
|
input
|
|
}
|