use crate::{
collection::{ArrayVectorU8, Vector},
http::HttpError,
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ClientDataJsonTy {
#[cfg_attr(feature = "serde", serde(rename = "webauthn.get"))]
Get,
#[cfg_attr(feature = "serde", serde(rename = "webauthn.create"))]
Create,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Alg {
ES256,
ES384,
ES512,
EdDSA,
RS256,
RS384,
RS512,
PS256,
PS384,
PS512,
}
impl From<Alg> for i16 {
#[inline]
fn from(value: Alg) -> Self {
match value {
Alg::ES256 => -7,
Alg::ES384 => -35,
Alg::ES512 => -36,
Alg::EdDSA => -8,
Alg::RS256 => -257,
Alg::RS384 => -258,
Alg::RS512 => -259,
Alg::PS256 => -37,
Alg::PS384 => -38,
Alg::PS512 => -39,
}
}
}
impl TryFrom<i16> for Alg {
type Error = crate::Error;
#[inline]
fn try_from(value: i16) -> Result<Self, Self::Error> {
Ok(match value {
-7 => Self::ES256,
-35 => Self::ES384,
-36 => Self::ES512,
-8 => Self::EdDSA,
-257 => Self::RS256,
-258 => Self::RS384,
-259 => Self::RS512,
-37 => Self::PS256,
-38 => Self::PS384,
-39 => Self::PS512,
_ => return Err(HttpError::UnknownWebAuthnAlg.into()),
})
}
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AttestationConveyancePreference {
Direct,
Enterprise,
Indirect,
None,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AttestationFormat {
Packed,
Tpm,
AndroidKey,
AndroidSafetyNet,
FidoU2f,
None,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AuthenticatorAttachment {
CrossPlatform,
Platform,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AuthenticatorTransport {
Ble,
Hybrid,
Internal,
Nfc,
Usb,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PublicKeyCredentialHints {
ClientDevice,
Hybrid,
SecurityKey,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PublicKeyCredentialType {
#[cfg_attr(feature = "serde", serde(rename = "public-key"))]
PublicKey,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ResidentKeyRequirement {
Discouraged,
Preferred,
Required,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum UserVerificationRequirement {
Discouraged,
Preferred,
Required,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AttestationObject<B> {
#[cfg_attr(feature = "serde", serde(rename = "fmt"))]
pub f: AttestationFormat,
pub att_stmt: AttestationStatement<B>,
pub auth_data: B,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct AttestationStatement<B> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub sig: Option<B>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub x5c: Option<ArrayVectorU8<B, 2>>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AuthenticatorSelectionCriteria {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub authenticator_attachment: Option<AuthenticatorAttachment>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub resident_key: Option<ResidentKeyRequirement>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub require_resident_key: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub user_verification: Option<UserVerificationRequirement>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AuthenticationExtensionsClientInputs<S> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub appid: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub appid_exclude: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub cred_props: Option<bool>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AuthenticationExtensionsClientOutputs {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub appid: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub appid_exclude: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub cred_props: Option<CredentialPropertiesOutput>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AuthenticatorAssertionResponse<S> {
#[cfg_attr(feature = "serde", serde(rename = "clientDataJSON"))]
pub client_data_json: S,
pub authenticator_data: S,
pub signature: S,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub user_handle: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attestation_object: Option<S>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct AuthenticatorAttestationResponse<S> {
#[cfg_attr(feature = "serde", serde(rename = "clientDataJSON"))]
pub client_data_json: S,
pub attestation_object: S,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub authenticator_data: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub transports: Option<ArrayVectorU8<AuthenticatorTransport, 5>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub public_key: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub public_key_algorithm: Option<i64>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct ClientDataJson<S> {
pub challenge: S,
pub origin: S,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: ClientDataJsonTy,
}
#[derive(Debug, PartialEq)]
pub struct CoseKey<B> {
pub kty: i32,
pub alg: Alg,
pub crv: i32,
pub x: B,
pub y: Option<B>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct CredentialPropertiesOutput {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub rk: Option<bool>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredential<R, S> {
pub id: S,
pub raw_id: S,
pub response: R,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub authenticator_attachment: Option<AuthenticatorAttachment>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub client_extension_results: Option<AuthenticationExtensionsClientOutputs>,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: PublicKeyCredentialType,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialCreationOptions<B, S> {
pub rp: PublicKeyCredentialRpEntity<S>,
pub user: PublicKeyCredentialUserEntity<B, S>,
pub challenge: B,
pub pub_key_cred_params: ArrayVectorU8<PublicKeyCredentialParameters, 4>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub timeout: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub exclude_credentials: Option<Vector<PublicKeyCredentialDescriptor<B>>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub authenticator_selection: Option<AuthenticatorSelectionCriteria>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub hints: Option<ArrayVectorU8<PublicKeyCredentialHints, 3>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attestation: Option<AttestationConveyancePreference>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attestation_formats: Option<ArrayVectorU8<AttestationFormat, 7>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub extensions: Option<AuthenticationExtensionsClientInputs<S>>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialDescriptor<B> {
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: PublicKeyCredentialType,
pub id: B,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub transports: Option<ArrayVectorU8<AuthenticatorTransport, 5>>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialParameters {
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: PublicKeyCredentialType,
pub alg: Alg,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialRequestOptions<B, S> {
pub challenge: B,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub timeout: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub rp_id: Option<S>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub allow_credentials: Option<Vector<PublicKeyCredentialDescriptor<B>>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub user_verification: Option<UserVerificationRequirement>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub hints: Option<ArrayVectorU8<PublicKeyCredentialHints, 3>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attestation: Option<AttestationConveyancePreference>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub attestation_formats: Option<ArrayVectorU8<AttestationFormat, 7>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub extensions: Option<AuthenticationExtensionsClientInputs<S>>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialRpEntity<S> {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub id: Option<S>,
pub name: S,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Debug, PartialEq)]
pub struct PublicKeyCredentialUserEntity<B, S> {
pub id: B,
pub name: S,
pub display_name: S,
}
#[cfg(feature = "serde")]
mod serde_local {
use crate::http::web_authn::{Alg, CoseKey};
use core::{fmt::Formatter, marker::PhantomData};
use serde::{
Deserialize, Deserializer,
de::{Error as _, IgnoredAny, MapAccess, Visitor},
};
impl serde::Serialize for Alg {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i16((*self).into())
}
}
impl<'de> Deserialize<'de> for Alg {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::try_from(i16::deserialize(deserializer)?).map_err(D::Error::custom)
}
}
impl<'de, B> Deserialize<'de> for CoseKey<B>
where
B: Deserialize<'de>,
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct LocalVisitor<B>(PhantomData<B>);
impl<'de, B> Visitor<'de> for LocalVisitor<B>
where
B: Deserialize<'de>,
{
type Value = CoseKey<B>;
fn expecting(&self, formatter: &mut Formatter<'_>) -> core::fmt::Result {
formatter.write_str("a COSE key map with integer keys")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut kty = None;
let mut alg = None;
let mut crv = None;
let mut x = None;
let mut y = None;
while let Some(key) = map.next_key::<i64>()? {
match key {
-3 => y = Some(map.next_value::<B>()?),
-2 => x = Some(map.next_value::<B>()?),
-1 => crv = Some(map.next_value()?),
1 => kty = Some(map.next_value()?),
3 => alg = Some(map.next_value()?),
_ => {
let _ = map.next_value::<IgnoredAny>()?;
}
}
}
Ok(CoseKey {
kty: kty.ok_or_else(|| A::Error::missing_field("kty"))?,
alg: alg.ok_or_else(|| A::Error::missing_field("alg"))?,
crv: crv.ok_or_else(|| A::Error::missing_field("crv"))?,
x: x.ok_or_else(|| A::Error::missing_field("x"))?,
y,
})
}
}
deserializer.deserialize_map(LocalVisitor(PhantomData))
}
}
}