use cosey::PublicKey;
use serde_bytes::ByteBuf;
use serde_indexed::{DeserializeIndexed, SerializeIndexed};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::pin::{PinUvAuthProtocol, PinUvAuthProtocolOne, PinUvAuthProtocolTwo};
#[derive(Debug, Clone, SerializeIndexed)]
pub struct Ctap2ClientPinRequest {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x01)]
pub protocol: Option<Ctap2PinUvAuthProtocol>,
#[serde(index = 0x02)]
pub command: Ctap2PinUvAuthProtocolCommand,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x03)]
pub key_agreement: Option<PublicKey>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x04)]
pub uv_auth_param: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x05)]
pub new_pin_encrypted: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x06)]
pub pin_hash_encrypted: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x09)]
pub permissions: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x0A)]
pub permissions_rpid: Option<String>,
}
impl Ctap2ClientPinRequest {
pub fn new_get_key_agreement(protocol: Ctap2PinUvAuthProtocol) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::GetKeyAgreement,
key_agreement: None,
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: None,
permissions: None,
permissions_rpid: None,
}
}
pub fn new_get_pin_token(
protocol: Ctap2PinUvAuthProtocol,
public_key: PublicKey,
pin_hash_enc: &[u8],
) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::GetPinToken,
key_agreement: Some(public_key),
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: Some(ByteBuf::from(pin_hash_enc)),
permissions: None,
permissions_rpid: None,
}
}
pub fn new_get_pin_retries(pin_proto: Option<Ctap2PinUvAuthProtocol>) -> Self {
Self {
protocol: pin_proto,
command: Ctap2PinUvAuthProtocolCommand::GetPinRetries,
key_agreement: None,
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: None,
permissions: None,
permissions_rpid: None,
}
}
pub fn new_get_pin_token_with_perm(
protocol: Ctap2PinUvAuthProtocol,
public_key: PublicKey,
pin_hash_enc: &[u8],
permissions: Ctap2AuthTokenPermissionRole,
permissions_rpid: Option<&str>,
) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::GetPinUvAuthTokenUsingPinWithPermissions,
key_agreement: Some(public_key),
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: Some(ByteBuf::from(pin_hash_enc)),
permissions: Some(permissions.bits()),
permissions_rpid: permissions_rpid.map(str::to_owned),
}
}
pub fn new_get_uv_token_with_perm(
protocol: Ctap2PinUvAuthProtocol,
public_key: PublicKey,
permissions: Ctap2AuthTokenPermissionRole,
permissions_rpid: Option<&str>,
) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::GetPinUvAuthTokenUsingUvWithPermissions,
key_agreement: Some(public_key),
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: None,
permissions: Some(permissions.bits()),
permissions_rpid: permissions_rpid.map(str::to_owned),
}
}
pub fn new_get_uv_retries() -> Self {
Self {
protocol: None,
command: Ctap2PinUvAuthProtocolCommand::GetUvRetries,
key_agreement: None,
uv_auth_param: None,
new_pin_encrypted: None,
pin_hash_encrypted: None,
permissions: None,
permissions_rpid: None,
}
}
pub fn new_change_pin(
protocol: Ctap2PinUvAuthProtocol,
new_pin_enc: &[u8],
curr_pin_enc: &[u8],
public_key: PublicKey,
uv_auth_param: &[u8],
) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::ChangePin,
key_agreement: Some(public_key),
uv_auth_param: Some(ByteBuf::from(uv_auth_param)),
new_pin_encrypted: Some(ByteBuf::from(new_pin_enc)),
pin_hash_encrypted: Some(ByteBuf::from(curr_pin_enc)),
permissions: None,
permissions_rpid: None,
}
}
pub fn new_set_pin(
protocol: Ctap2PinUvAuthProtocol,
new_pin_enc: &[u8],
public_key: PublicKey,
uv_auth_param: &[u8],
) -> Self {
Self {
protocol: Some(protocol),
command: Ctap2PinUvAuthProtocolCommand::SetPin,
key_agreement: Some(public_key),
uv_auth_param: Some(ByteBuf::from(uv_auth_param)),
new_pin_encrypted: Some(ByteBuf::from(new_pin_enc)),
pin_hash_encrypted: None,
permissions: None,
permissions_rpid: None,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ctap2AuthTokenPermissionRole: u32 {
const MAKE_CREDENTIAL = 0x01;
const GET_ASSERTION = 0x02;
const CREDENTIAL_MANAGEMENT = 0x04;
const BIO_ENROLLMENT = 0x08;
const LARGE_BLOB_WRITE = 0x10;
const AUTHENTICATOR_CONFIGURATION = 0x20;
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, FromPrimitive, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
pub enum Ctap2PinUvAuthProtocol {
One = 1,
Two = 2,
}
impl Ctap2PinUvAuthProtocol {
pub(crate) fn create_protocol_object(&self) -> Box<dyn PinUvAuthProtocol> {
match self {
Ctap2PinUvAuthProtocol::One => Box::new(PinUvAuthProtocolOne::new()),
Ctap2PinUvAuthProtocol::Two => Box::new(PinUvAuthProtocolTwo::new()),
}
}
}
#[repr(u32)]
#[derive(Debug, Clone, FromPrimitive, PartialEq, Serialize_repr, Deserialize_repr)]
pub enum Ctap2PinUvAuthProtocolCommand {
GetPinRetries = 0x01,
GetKeyAgreement = 0x02,
SetPin = 0x03,
ChangePin = 0x04,
GetPinToken = 0x05,
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
GetUvRetries = 0x07,
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
}
#[cfg_attr(test, derive(SerializeIndexed))]
#[derive(Debug, Clone, Default, DeserializeIndexed)]
pub struct Ctap2ClientPinResponse {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x01)]
pub key_agreement: Option<PublicKey>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x02)]
pub pin_uv_auth_token: Option<ByteBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x03)]
pub pin_retries: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x04)]
pub power_cycle_state: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(index = 0x05)]
pub uv_retries: Option<u32>,
}