use serde::{Deserialize, Serialize};
use webauthn_rs_core::error::WebauthnError;
use webauthn_rs_core::proto::{
AttestationCa, AttestationCaList, AuthenticationResult, AuthenticationState, COSEAlgorithm,
COSEKey, Credential, CredentialID, ParsedAttestation, RegistrationState,
};
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
pub struct PasskeyRegistration {
pub(crate) rs: RegistrationState,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
pub struct PasskeyAuthentication {
pub(crate) ast: AuthenticationState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Passkey {
pub(crate) cred: Credential,
}
impl Passkey {
pub fn cred_id(&self) -> &CredentialID {
&self.cred.cred_id
}
pub fn cred_algorithm(&self) -> &COSEAlgorithm {
&self.cred.cred.type_
}
pub fn get_public_key(&self) -> &COSEKey {
&self.cred.cred
}
pub fn update_credential(&mut self, res: &AuthenticationResult) -> Option<bool> {
if res.cred_id() == self.cred_id() {
let mut changed = false;
if res.counter() > self.cred.counter {
self.cred.counter = res.counter();
changed = true;
}
if res.backup_state() != self.cred.backup_state {
self.cred.backup_state = res.backup_state();
changed = true;
}
if res.backup_eligible() && !self.cred.backup_eligible {
self.cred.backup_eligible = true;
changed = true;
}
Some(changed)
} else {
None
}
}
}
#[cfg(feature = "danger-credential-internals")]
impl From<Passkey> for Credential {
fn from(pk: Passkey) -> Self {
pk.cred
}
}
#[cfg(feature = "danger-credential-internals")]
impl From<Credential> for Passkey {
fn from(cred: Credential) -> Self {
Passkey { cred }
}
}
impl PartialEq for Passkey {
fn eq(&self, other: &Self) -> bool {
self.cred.cred_id == other.cred.cred_id
}
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
pub struct AttestedPasskeyRegistration {
pub(crate) rs: RegistrationState,
pub(crate) ca_list: AttestationCaList,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
pub struct AttestedPasskeyAuthentication {
pub(crate) ast: AuthenticationState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
pub struct AttestedPasskey {
pub(crate) cred: Credential,
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl AttestedPasskey {
pub fn cred_id(&self) -> &CredentialID {
&self.cred.cred_id
}
pub fn cred_algorithm(&self) -> &COSEAlgorithm {
&self.cred.cred.type_
}
pub fn attestation(&self) -> &ParsedAttestation {
&self.cred.attestation
}
pub fn update_credential(&mut self, res: &AuthenticationResult) -> Option<bool> {
if res.cred_id() == self.cred_id() {
let mut changed = false;
if res.counter() > self.cred.counter {
self.cred.counter = res.counter();
changed = true;
}
if res.backup_state() != self.cred.backup_state {
self.cred.backup_state = res.backup_state();
changed = true;
}
Some(changed)
} else {
None
}
}
pub fn verify_attestation<'a>(
&'_ self,
ca_list: &'a AttestationCaList,
) -> Result<&'a AttestationCa, WebauthnError> {
self.cred
.verify_attestation(ca_list)
.and_then(|maybe_att_ca| {
if let Some(att_ca) = maybe_att_ca {
Ok(att_ca)
} else {
Err(WebauthnError::AttestationNotVerifiable)
}
})
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl std::borrow::Borrow<CredentialID> for AttestedPasskey {
fn borrow(&self) -> &CredentialID {
&self.cred.cred_id
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl PartialEq for AttestedPasskey {
fn eq(&self, other: &Self) -> bool {
self.cred.cred_id == other.cred.cred_id
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl Eq for AttestedPasskey {}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl PartialOrd for AttestedPasskey {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl Ord for AttestedPasskey {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.cred.cred_id.cmp(&other.cred.cred_id)
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl From<&AttestedPasskey> for Passkey {
fn from(k: &AttestedPasskey) -> Self {
Passkey {
cred: k.cred.clone(),
}
}
}
#[cfg(any(all(doc, not(doctest)), feature = "attestation"))]
impl From<AttestedPasskey> for Passkey {
fn from(k: AttestedPasskey) -> Self {
Passkey { cred: k.cred }
}
}
#[cfg(all(feature = "danger-credential-internals", feature = "attestation"))]
impl From<AttestedPasskey> for Credential {
fn from(pk: AttestedPasskey) -> Self {
pk.cred
}
}
#[cfg(all(feature = "danger-credential-internals", feature = "attestation"))]
impl From<Credential> for AttestedPasskey {
fn from(cred: Credential) -> Self {
AttestedPasskey { cred }
}
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
pub struct SecurityKeyRegistration {
pub(crate) rs: RegistrationState,
pub(crate) ca_list: Option<AttestationCaList>,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
pub struct SecurityKeyAuthentication {
pub(crate) ast: AuthenticationState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityKey {
pub(crate) cred: Credential,
}
impl SecurityKey {
pub fn cred_id(&self) -> &CredentialID {
&self.cred.cred_id
}
pub fn cred_algorithm(&self) -> &COSEAlgorithm {
&self.cred.cred.type_
}
pub fn attestation(&self) -> &ParsedAttestation {
&self.cred.attestation
}
pub fn update_credential(&mut self, res: &AuthenticationResult) -> Option<bool> {
if res.cred_id() == self.cred_id() {
let mut changed = false;
if res.counter() > self.cred.counter {
self.cred.counter = res.counter();
changed = true;
}
if res.backup_state() != self.cred.backup_state {
self.cred.backup_state = res.backup_state();
changed = true;
}
Some(changed)
} else {
None
}
}
}
impl PartialEq for SecurityKey {
fn eq(&self, other: &Self) -> bool {
self.cred.cred_id == other.cred.cred_id
}
}
#[cfg(feature = "danger-credential-internals")]
impl From<SecurityKey> for Credential {
fn from(sk: SecurityKey) -> Self {
sk.cred
}
}
#[cfg(feature = "danger-credential-internals")]
impl From<Credential> for SecurityKey {
fn from(cred: Credential) -> Self {
SecurityKey { cred }
}
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
pub struct AttestedResidentKeyRegistration {
pub(crate) rs: RegistrationState,
pub(crate) ca_list: AttestationCaList,
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
pub struct AttestedResidentKeyAuthentication {
pub(crate) ast: AuthenticationState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
pub struct AttestedResidentKey {
pub(crate) cred: Credential,
}
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
impl AttestedResidentKey {
pub fn cred_id(&self) -> &CredentialID {
&self.cred.cred_id
}
pub fn cred_algorithm(&self) -> &COSEAlgorithm {
&self.cred.cred.type_
}
pub fn attestation(&self) -> &ParsedAttestation {
&self.cred.attestation
}
pub fn update_credential(&mut self, res: &AuthenticationResult) -> Option<bool> {
if res.cred_id() == self.cred_id() {
let mut changed = false;
if res.counter() > self.cred.counter {
self.cred.counter = res.counter();
changed = true;
}
if res.backup_state() != self.cred.backup_state {
self.cred.backup_state = res.backup_state();
changed = true;
}
Some(changed)
} else {
None
}
}
pub fn verify_attestation<'a>(
&'_ self,
ca_list: &'a AttestationCaList,
) -> Result<&'a AttestationCa, WebauthnError> {
self.cred
.verify_attestation(ca_list)
.and_then(|maybe_att_ca| {
if let Some(att_ca) = maybe_att_ca {
Ok(att_ca)
} else {
Err(WebauthnError::AttestationNotVerifiable)
}
})
}
}
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
impl PartialEq for AttestedResidentKey {
fn eq(&self, other: &Self) -> bool {
self.cred.cred_id == other.cred.cred_id
}
}
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
impl From<&AttestedResidentKey> for Passkey {
fn from(k: &AttestedResidentKey) -> Self {
Passkey {
cred: k.cred.clone(),
}
}
}
#[cfg(any(all(doc, not(doctest)), feature = "resident-key-support"))]
impl From<AttestedResidentKey> for Passkey {
fn from(k: AttestedResidentKey) -> Self {
Passkey { cred: k.cred }
}
}
#[cfg(all(
feature = "danger-credential-internals",
feature = "resident-key-support"
))]
impl From<AttestedResidentKey> for Credential {
fn from(dk: AttestedResidentKey) -> Self {
dk.cred
}
}
#[cfg(all(
feature = "danger-credential-internals",
feature = "resident-key-support"
))]
impl From<Credential> for AttestedResidentKey {
fn from(cred: Credential) -> Self {
AttestedResidentKey { cred }
}
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "danger-allow-state-serialisation",
derive(Serialize, Deserialize)
)]
#[cfg(any(all(doc, not(doctest)), feature = "conditional-ui"))]
pub struct DiscoverableAuthentication {
pub(crate) ast: AuthenticationState,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg(any(all(doc, not(doctest)), feature = "conditional-ui"))]
pub struct DiscoverableKey {
pub(crate) cred: Credential,
}
#[cfg(any(
all(doc, not(doctest)),
all(feature = "conditional-ui", feature = "resident-key-support")
))]
impl From<&AttestedResidentKey> for DiscoverableKey {
fn from(k: &AttestedResidentKey) -> Self {
DiscoverableKey {
cred: k.cred.clone(),
}
}
}
#[cfg(any(
all(doc, not(doctest)),
all(feature = "conditional-ui", feature = "resident-key-support")
))]
impl From<AttestedResidentKey> for DiscoverableKey {
fn from(k: AttestedResidentKey) -> Self {
DiscoverableKey { cred: k.cred }
}
}
#[cfg(any(
all(doc, not(doctest)),
all(feature = "conditional-ui", feature = "attestation")
))]
impl From<&AttestedPasskey> for DiscoverableKey {
fn from(k: &AttestedPasskey) -> Self {
DiscoverableKey {
cred: k.cred.clone(),
}
}
}
#[cfg(any(
all(doc, not(doctest)),
all(feature = "conditional-ui", feature = "attestation")
))]
impl From<AttestedPasskey> for DiscoverableKey {
fn from(k: AttestedPasskey) -> Self {
DiscoverableKey { cred: k.cred }
}
}
#[cfg(any(all(doc, not(doctest)), feature = "conditional-ui"))]
impl From<&Passkey> for DiscoverableKey {
fn from(k: &Passkey) -> Self {
DiscoverableKey {
cred: k.cred.clone(),
}
}
}
#[cfg(any(all(doc, not(doctest)), feature = "conditional-ui"))]
impl From<Passkey> for DiscoverableKey {
fn from(k: Passkey) -> Self {
DiscoverableKey { cred: k.cred }
}
}