use crate::proto::ctap2::cbor;
use crate::{
ops::webauthn::UserVerificationRequirement,
pin::PinUvAuthProtocol,
proto::ctap2::{
Ctap2, Ctap2AuthTokenPermissionRole, Ctap2ClientPinRequest, Ctap2CredentialData,
Ctap2CredentialManagementMetadata, Ctap2CredentialManagementRequest, Ctap2GetInfoResponse,
Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialUserEntity, Ctap2RPData,
Ctap2UserVerifiableRequest,
},
transport::Channel,
unwrap_field,
webauthn::{
error::{CtapError, Error, PlatformError},
handle_errors,
pin_uv_auth_token::{user_verification, UsedPinUvAuthToken},
},
UvUpdate,
};
use async_trait::async_trait;
use serde_bytes::ByteBuf;
use std::time::Duration;
use tracing::info;
#[async_trait]
pub trait CredentialManagement {
async fn get_credential_metadata(
&mut self,
timeout: Duration,
) -> Result<Ctap2CredentialManagementMetadata, Error>;
async fn enumerate_rps_begin(&mut self, timeout: Duration)
-> Result<(Ctap2RPData, u64), Error>;
async fn enumerate_rps_next_rp(&mut self, timeout: Duration) -> Result<Ctap2RPData, Error>;
async fn enumerate_credentials_begin(
&mut self,
rpid_hash: &[u8],
timeout: Duration,
) -> Result<(Ctap2CredentialData, u64), Error>;
async fn enumerate_credentials_next(
&mut self,
timeout: Duration,
) -> Result<Ctap2CredentialData, Error>;
async fn delete_credential(
&mut self,
credential_id: &Ctap2PublicKeyCredentialDescriptor,
timeout: Duration,
) -> Result<(), Error>;
async fn update_user_info(
&mut self,
credential_id: &Ctap2PublicKeyCredentialDescriptor,
user: &Ctap2PublicKeyCredentialUserEntity,
timeout: Duration,
) -> Result<(), Error>;
}
#[async_trait]
impl<C> CredentialManagement for C
where
C: Channel,
{
async fn get_credential_metadata(
&mut self,
timeout: Duration,
) -> Result<Ctap2CredentialManagementMetadata, Error> {
let mut req = Ctap2CredentialManagementRequest::new_get_credential_metadata();
let resp = loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
let metadata = Ctap2CredentialManagementMetadata::new(
unwrap_field!(resp.existing_resident_credentials_count),
unwrap_field!(resp.max_possible_remaining_resident_credentials_count),
);
Ok(metadata)
}
async fn enumerate_rps_begin(
&mut self,
timeout: Duration,
) -> Result<(Ctap2RPData, u64), Error> {
let mut req = Ctap2CredentialManagementRequest::new_enumerate_rps_begin();
let resp = loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
Ok((
Ctap2RPData::new(
unwrap_field!(resp.rp),
cbor::to_vec(&unwrap_field!(&resp.rp_id_hash))?,
),
unwrap_field!(resp.total_rps),
))
}
async fn enumerate_rps_next_rp(&mut self, timeout: Duration) -> Result<Ctap2RPData, Error> {
let mut req = Ctap2CredentialManagementRequest::new_enumerate_rps_next_rp();
let resp = loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
Ok(Ctap2RPData::new(
unwrap_field!(resp.rp),
cbor::to_vec(unwrap_field!(&resp.rp_id_hash))?,
))
}
async fn enumerate_credentials_begin(
&mut self,
rpid_hash: &[u8],
timeout: Duration,
) -> Result<(Ctap2CredentialData, u64), Error> {
let mut req = Ctap2CredentialManagementRequest::new_enumerate_credentials_begin(rpid_hash);
let resp = loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
let cred = Ctap2CredentialData::new(
unwrap_field!(resp.user),
unwrap_field!(resp.credential_id),
unwrap_field!(resp.public_key),
unwrap_field!(resp.cred_protect),
resp.large_blob_key.map(|x| x.into_vec()),
);
let total_creds = unwrap_field!(resp.total_credentials);
Ok((cred, total_creds))
}
async fn enumerate_credentials_next(
&mut self,
timeout: Duration,
) -> Result<Ctap2CredentialData, Error> {
let mut req = Ctap2CredentialManagementRequest::new_enumerate_credentials_next();
let resp = loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
let cred = Ctap2CredentialData::new(
unwrap_field!(resp.user),
unwrap_field!(resp.credential_id),
unwrap_field!(resp.public_key),
unwrap_field!(resp.cred_protect),
resp.large_blob_key.map(|x| x.into_vec()),
);
Ok(cred)
}
async fn delete_credential(
&mut self,
credential_id: &Ctap2PublicKeyCredentialDescriptor,
timeout: Duration,
) -> Result<(), Error> {
let mut req = Ctap2CredentialManagementRequest::new_delete_credential(credential_id);
loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
Ok(())
}
async fn update_user_info(
&mut self,
credential_id: &Ctap2PublicKeyCredentialDescriptor,
user: &Ctap2PublicKeyCredentialUserEntity,
timeout: Duration,
) -> Result<(), Error> {
let mut req =
Ctap2CredentialManagementRequest::new_update_user_information(credential_id, user);
loop {
let uv_auth_used = user_verification(
self,
UserVerificationRequirement::Preferred,
&mut req,
timeout,
)
.await?;
if req.use_legacy_preview {
return Err(Error::Ctap(CtapError::InvalidCommand));
}
handle_errors!(
self,
self.ctap2_credential_management(&req, timeout).await,
uv_auth_used,
timeout
)
}?;
Ok(())
}
}
impl Ctap2UserVerifiableRequest for Ctap2CredentialManagementRequest {
fn ensure_uv_set(&mut self) {
}
fn calculate_and_set_uv_auth(
&mut self,
uv_proto: &dyn PinUvAuthProtocol,
uv_auth_token: &[u8],
) -> Result<(), Error> {
let subcommand = self
.subcommand
.ok_or(Error::Platform(PlatformError::InvalidDeviceResponse))?;
let mut data = vec![subcommand as u8];
if let Some(params) = &self.subcommand_params {
data.extend(cbor::to_vec(¶ms)?);
}
let uv_auth_param = uv_proto.authenticate(uv_auth_token, &data)?;
self.protocol = Some(uv_proto.version());
self.uv_auth_param = Some(ByteBuf::from(uv_auth_param));
Ok(())
}
fn permissions(&self) -> Ctap2AuthTokenPermissionRole {
Ctap2AuthTokenPermissionRole::CREDENTIAL_MANAGEMENT
}
fn permissions_rpid(&self) -> Option<&str> {
None
}
fn can_use_uv(&self, _info: &Ctap2GetInfoResponse) -> bool {
true
}
fn handle_legacy_preview(&mut self, info: &Ctap2GetInfoResponse) {
if let Some(options) = &info.options {
if options.get("credMgmt") != Some(&true)
&& options.get("credentialMgmtPreview") == Some(&true)
{
self.use_legacy_preview = true;
}
}
}
fn needs_shared_secret(&self, _get_info_response: &Ctap2GetInfoResponse) -> bool {
false
}
}