use serde_bytes::ByteBuf;
use std::time::Duration;
use tracing::{debug, info};
use super::{Ctap2GetAssertionRequest, Ctap2PublicKeyCredentialDescriptor};
use crate::{
proto::ctap2::{model::Ctap2GetAssertionOptions, Ctap2},
transport::Channel,
};
pub async fn ctap2_preflight<C: Channel>(
channel: &mut C,
credentials: &[Ctap2PublicKeyCredentialDescriptor],
client_data_hash: &[u8],
rp: &str,
) -> Vec<Ctap2PublicKeyCredentialDescriptor> {
ctap2_preflight_with_appid(channel, credentials, client_data_hash, rp, None).await
}
pub async fn ctap2_preflight_with_appid<C: Channel>(
channel: &mut C,
credentials: &[Ctap2PublicKeyCredentialDescriptor],
client_data_hash: &[u8],
rp: &str,
appid_exclude: Option<&str>,
) -> Vec<Ctap2PublicKeyCredentialDescriptor> {
info!("Credential list BEFORE preflight: {credentials:?}");
let mut filtered_list = Vec::new();
for credential in credentials {
if let Some(matched) = preflight_one(channel, credential, client_data_hash, rp).await {
debug!("Pre-flight: Found already known credential under rpId {credential:?}");
filtered_list.push(matched);
continue;
}
if let Some(appid) = appid_exclude {
if let Some(matched) = preflight_one(channel, credential, client_data_hash, appid).await
{
debug!(
"Pre-flight: Found already known credential under appidExclude {credential:?}"
);
filtered_list.push(matched);
continue;
}
}
debug!("Pre-flight: Filtering out {credential:?}");
}
info!("Credential list AFTER preflight: {filtered_list:?}");
filtered_list
}
async fn preflight_one<C: Channel>(
channel: &mut C,
credential: &Ctap2PublicKeyCredentialDescriptor,
client_data_hash: &[u8],
rp: &str,
) -> Option<Ctap2PublicKeyCredentialDescriptor> {
let preflight_request = Ctap2GetAssertionRequest {
relying_party_id: rp.to_string(),
client_data_hash: ByteBuf::from(client_data_hash),
allow: vec![credential.clone()],
extensions: None,
options: Some(Ctap2GetAssertionOptions {
require_user_presence: false,
require_user_verification: false,
}),
pin_auth_param: None,
pin_auth_proto: None,
};
match channel
.ctap2_get_assertion(&preflight_request, Duration::from_secs(2))
.await
{
Ok(resp) => {
let id = resp
.credential_id
.or(resp
.authenticator_data
.attested_credential
.map(|x| Ctap2PublicKeyCredentialDescriptor::from(&x)))
.unwrap_or(credential.clone());
Some(id)
}
Err(e) => {
debug!("Pre-flight: Not found under {rp:?}: {e:?}");
None
}
}
}