use super::{
Ctap2PinUvAuthProtocol, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
Ctap2PublicKeyCredentialUserEntity,
};
use crate::proto::ctap2::CoseEncodedKey;
use serde_bytes::ByteBuf;
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Debug, Clone, SerializeIndexed)]
pub struct Ctap2CredentialManagementRequest {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x01)]
pub subcommand: Option<Ctap2CredentialManagementSubcommand>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x02)]
pub subcommand_params: Option<Ctap2CredentialManagementParams>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x03)]
pub protocol: Option<Ctap2PinUvAuthProtocol>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x04)]
pub uv_auth_param: Option<ByteBuf>,
#[serde(skip)]
pub use_legacy_preview: bool,
#[serde(skip)]
pub use_persistent_token: bool,
#[serde(skip)]
pub persistent_token_rejected: bool,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, FromPrimitive, PartialEq, Serialize_repr, Deserialize_repr)]
pub enum Ctap2CredentialManagementSubcommand {
GetCredsMetadata = 0x01,
EnumerateRPsBegin = 0x02,
EnumerateRPsGetNextRP = 0x03,
EnumerateCredentialsBegin = 0x04,
EnumerateCredentialsGetNextCredential = 0x05,
DeleteCredential = 0x06,
UpdateUserInformation = 0x07,
}
impl Ctap2CredentialManagementSubcommand {
pub fn is_read_only(self) -> bool {
matches!(
self,
Self::GetCredsMetadata
| Self::EnumerateRPsBegin
| Self::EnumerateRPsGetNextRP
| Self::EnumerateCredentialsBegin
| Self::EnumerateCredentialsGetNextCredential
)
}
}
#[derive(Debug, Clone, SerializeIndexed)]
pub struct Ctap2CredentialManagementParams {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x01)]
rpid_hash: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x02)]
credential_id: Option<Ctap2PublicKeyCredentialDescriptor>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x03)]
user: Option<Ctap2PublicKeyCredentialUserEntity>,
}
#[derive(Debug, Default, Clone, DeserializeIndexed)]
pub struct Ctap2CredentialManagementResponse {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x01)]
pub existing_resident_credentials_count: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x02)]
pub max_possible_remaining_resident_credentials_count: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x03)]
pub rp: Option<Ctap2PublicKeyCredentialRpEntity>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x04)]
pub rp_id_hash: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x05)]
pub total_rps: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x06)]
pub user: Option<Ctap2PublicKeyCredentialUserEntity>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x07)]
pub credential_id: Option<Ctap2PublicKeyCredentialDescriptor>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x08)]
pub public_key: Option<CoseEncodedKey>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x09)]
pub total_credentials: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x0A)]
pub cred_protect: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x0B)]
pub large_blob_key: Option<ByteBuf>,
}
impl Ctap2CredentialManagementRequest {
pub fn new_get_credential_metadata() -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::GetCredsMetadata),
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_enumerate_rps_begin() -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::EnumerateRPsBegin),
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_enumerate_rps_next_rp() -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::EnumerateRPsGetNextRP),
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_enumerate_credentials_begin(rpid_hash: &[u8]) -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::EnumerateCredentialsBegin),
subcommand_params: Some(Ctap2CredentialManagementParams {
rpid_hash: Some(ByteBuf::from(rpid_hash)),
credential_id: None,
user: None,
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_enumerate_credentials_next() -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(
Ctap2CredentialManagementSubcommand::EnumerateCredentialsGetNextCredential,
),
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_delete_credential(credential_id: &Ctap2PublicKeyCredentialDescriptor) -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::DeleteCredential),
subcommand_params: Some(Ctap2CredentialManagementParams {
rpid_hash: None,
credential_id: Some(credential_id.clone()),
user: None,
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
pub fn new_update_user_information(
credential_id: &Ctap2PublicKeyCredentialDescriptor,
user: &Ctap2PublicKeyCredentialUserEntity,
) -> Self {
Ctap2CredentialManagementRequest {
subcommand: Some(Ctap2CredentialManagementSubcommand::UpdateUserInformation),
subcommand_params: Some(Ctap2CredentialManagementParams {
rpid_hash: None,
credential_id: Some(credential_id.clone()),
user: Some(user.clone()),
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
use_persistent_token: false,
persistent_token_rejected: false,
}
}
}
#[derive(Clone, Debug)]
pub struct Ctap2CredentialManagementMetadata {
pub existing_resident_credentials_count: u64,
pub max_possible_remaining_resident_credentials_count: u64,
}
impl Ctap2CredentialManagementMetadata {
pub fn new(
existing_resident_credentials_count: u64,
max_possible_remaining_resident_credentials_count: u64,
) -> Self {
Self {
existing_resident_credentials_count,
max_possible_remaining_resident_credentials_count,
}
}
}
#[derive(Debug, Clone)]
pub struct Ctap2CredentialData {
pub user: Ctap2PublicKeyCredentialUserEntity,
pub credential_id: Ctap2PublicKeyCredentialDescriptor,
pub public_key: Vec<u8>,
pub cred_protect: u64,
pub large_blob_key: Option<Vec<u8>>,
}
impl Ctap2CredentialData {
pub fn new(
user: Ctap2PublicKeyCredentialUserEntity,
credential_id: Ctap2PublicKeyCredentialDescriptor,
public_key: Vec<u8>,
cred_protect: u64,
large_blob_key: Option<Vec<u8>>,
) -> Self {
Self {
user,
credential_id,
public_key,
cred_protect,
large_blob_key,
}
}
}
#[derive(Debug, Clone)]
pub struct Ctap2RPData {
pub rp: Ctap2PublicKeyCredentialRpEntity,
pub rp_id_hash: Vec<u8>,
}
impl Ctap2RPData {
pub fn new(rp: Ctap2PublicKeyCredentialRpEntity, rp_id_hash: Vec<u8>) -> Self {
Self { rp, rp_id_hash }
}
}
#[cfg(test)]
mod test {
use super::Ctap2CredentialManagementSubcommand as Sub;
#[test]
fn read_only_classification() {
for subcommand in [
Sub::GetCredsMetadata,
Sub::EnumerateRPsBegin,
Sub::EnumerateRPsGetNextRP,
Sub::EnumerateCredentialsBegin,
Sub::EnumerateCredentialsGetNextCredential,
] {
assert!(
subcommand.is_read_only(),
"{subcommand:?} should be read-only"
);
}
for subcommand in [Sub::DeleteCredential, Sub::UpdateUserInformation] {
assert!(
!subcommand.is_read_only(),
"{subcommand:?} should be a write"
);
}
}
}