use crate::error::{Error, Result};
use soft_fido2_ctap::CredentialKey;
use soft_fido2_ctap::SecBytes;
use alloc::borrow::ToOwned;
use alloc::string::ToString;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
pub use soft_fido2_ctap::types::{CredentialBackupState, RelyingParty, User};
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Extensions {
pub cred_protect: Option<u8>,
pub hmac_secret: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cred_random: Option<soft_fido2_ctap::SecBytes>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Credential {
pub id: Vec<u8>,
pub rp: RelyingParty,
pub user: User,
pub sign_count: u32,
pub alg: i32,
pub key: CredentialKey,
pub created: i64,
pub discoverable: bool,
#[serde(default)]
pub backup_state: CredentialBackupState,
pub extensions: Extensions,
}
#[derive(Clone, Copy, Debug)]
pub struct CredentialRef<'a> {
pub id: &'a [u8],
pub rp_id: &'a str,
pub rp_name: Option<&'a str>,
pub user_id: &'a [u8],
pub user_name: Option<&'a str>,
pub user_display_name: Option<&'a str>,
pub sign_count: &'a u32,
pub alg: &'a i32,
pub key: &'a CredentialKey,
pub created: &'a i64,
pub discoverable: &'a bool,
pub cred_protect: Option<&'a u8>,
pub backup_state: &'a CredentialBackupState,
pub cred_random: Option<&'a SecBytes>,
}
impl<'a> CredentialRef<'a> {
pub fn to_owned(&self) -> Credential {
Credential {
id: self.id.to_vec(),
rp: RelyingParty {
id: self.rp_id.to_string(),
name: self.rp_name.map(|s| s.to_string()),
},
user: User {
id: self.user_id.to_vec(),
name: self.user_name.map(|s| s.to_string()),
display_name: self.user_display_name.map(|s| s.to_string()),
},
sign_count: self.sign_count.to_owned(),
alg: self.alg.to_owned(),
key: self.key.clone(),
created: self.created.to_owned(),
discoverable: self.discoverable.to_owned(),
backup_state: *self.backup_state,
extensions: Extensions {
cred_protect: self.cred_protect.copied(),
hmac_secret: None,
cred_random: self.cred_random.cloned(),
},
}
}
pub fn to_bytes(&self) -> Result<Vec<u8>> {
let owned = self.to_owned();
let mut buf = Vec::new();
soft_fido2_ctap::cbor::into_writer(&owned, &mut buf).map_err(|_| Error::Other)?;
Ok(buf)
}
}
impl Credential {
pub fn to_bytes(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
soft_fido2_ctap::cbor::into_writer(self, &mut buf).map_err(|_| Error::Other)?;
Ok(buf)
}
pub fn from_bytes(data: &[u8]) -> Result<Self> {
soft_fido2_ctap::cbor::decode(data).map_err(|_| Error::Other)
}
}
impl From<soft_fido2_ctap::types::Credential> for Credential {
fn from(cred: soft_fido2_ctap::types::Credential) -> Self {
Credential {
id: cred.id,
rp: RelyingParty {
id: cred.rp_id,
name: cred.rp_name,
},
user: User {
id: cred.user_id,
name: cred.user_name,
display_name: cred.user_display_name,
},
sign_count: cred.sign_count,
alg: cred.algorithm,
key: cred.key,
created: cred.created,
discoverable: cred.discoverable,
backup_state: cred.backup_state,
extensions: Extensions {
cred_protect: Some(cred.cred_protect),
hmac_secret: cred.cred_random.is_some().then_some(true),
cred_random: cred.cred_random,
},
}
}
}
impl From<Credential> for soft_fido2_ctap::types::Credential {
fn from(cred: Credential) -> Self {
soft_fido2_ctap::types::Credential {
id: cred.id,
rp_id: cred.rp.id,
rp_name: cred.rp.name,
user_id: cred.user.id,
user_name: cred.user.name,
user_display_name: cred.user.display_name,
sign_count: cred.sign_count,
algorithm: cred.alg,
key: cred.key,
created: cred.created,
discoverable: cred.discoverable,
cred_protect: cred.extensions.cred_protect.unwrap_or(1),
backup_state: cred.backup_state,
cred_random: cred.extensions.cred_random,
}
}
}