Compare commits

...

90 Commits
dev ... main

Author SHA1 Message Date
Nick Zana 1872c78347 ctap2-proto: impl AsRef<[u8]> for authenticator::client_pin::PinUvAuthToken 11 months ago
Nick Zana 4f589f4525 ctap2-proto: fix typo in Command enum variant 11 months ago
Nick Zana 0d00f1b536 chore: Update Cargo.lock 11 months ago
Nick Zana de4e3c39e0 ctap2-proto: Import std::fmt::Display used for authenticator::credential::management::Error 11 months ago
Nick Zana 2bcc0699f1 chore: Derive std::marker::ConstParamTy for types used as const params 11 months ago
Nick Zana 2e6e890c52 ctap2-proto: Remove coset dependency after transition to cosey 11 months ago
Nick Zana ed57873783 ctap2-proto: Add raw module with RawSubcommand to authenticator::credential::management
RawSubcommand is de/serialized as a u8 corresponding to its subCommand
number.

Also implements Display for authenticator::credential::management::Error
so that it can be used as the serde error type for RawSubcommand.
11 months ago
Nick Zana eb06e4c88d chore: Update Cargo.lock 11 months ago
Nick Zana 448c8cb79b ctap2-proto: Add client_pin::raw::RawResponse for de/serialization of client_pin::Response enum as CBOR 11 months ago
Nick Zana 2f53d730c8 ctap2-proto: Add client_pin::raw::RawRequest for de/serialization of client_pin::Request enum as CBOR 11 months ago
Nick Zana 132cd6b03f ctap2-proto: Add ctap2-proto::authenticator::client_pin::raw::RawPermission bitflag set
CTAP 2 uses bitflags to represent the permissions field in client pin
requests. This adds a RawPermission type that can be represented using a
FlagSet<RawPermission> for Serialization and Deserialization.
11 months ago
Nick Zana 10bdfc1547 ctap2-proto: Add authenticator::client_pin::raw::RawSubcommand
This type is used for serialization and deserialization of subcommands
as raw u8s.
11 months ago
Nick Zana df78c9e303 ctap2-proto: Add authenticator::client_pin::raw::PublicKey type for deserialization
The cosey::PublicKey type does not properly implement deserialize for
the general PublicKey type. the client_pin::raw::PublicKey type is used
as an intermediate to allow deserialization of COSE public keys in the
client_pin protocol.
11 months ago
Nick Zana 984e43cd18 ctap2-proto: Derive serde traits as Bytes type for authenticator::client_pin::PinUvAuthToken
Serializes and Deserializes as serde_as::Bytes so that the byte arrays
are serialized as bytestrings, not sequences of bytes.
11 months ago
Nick Zana 63a9be04ad ctap2-proto: Add pin_uv_auth_token param to authenticator::client_pin::Response::GetPinToken 11 months ago
Nick Zana 7827fb82fe ctap2-proto: Remove all fields from authenticator::client_pin::Response::SetPin
This response type has no fields.
11 months ago
Nick Zana 54028012a8 ctap2-proto: Derive Debug, Eq, and Ord traits for authenticator::client_pin::Permission enum 11 months ago
Nick Zana 408c4864da ctap2-proto: Specify relying_party_id type as Cow<'a, str> in authenticator::client_pin::Request variant fields 11 months ago
Nick Zana b1963220d8 ctap2-proto: Implement std::fmt::Display for authenticator::client_pin::Error 11 months ago
Nick Zana 3f04a35447 ctap2-proto: Change &[u8] to fixed size arrays in authenticator::client_pin::{Request, Response} fields 11 months ago
Nick Zana 128ed345d1 ctap2-proto: Change authenticator::client_pin::{Request, Response} to cosey::PublicKey
Replaces coset::CoseKey type, which doesn't sufficiently distinguish
between public and private keys and the parameters required for key
algorithm variants, with the cosey::PublicKey type in the Request and
Response type fields.
11 months ago
Nick Zana 848fcf43b4 ctap2-proto: Add missing version parameter to authenticator::client_pin::Request::SetPin 11 months ago
Nick Zana 795d356ab6 ctap2-proto: Simplify authenticator::client_pin::auth_protocol traits
Rather than use manual lifetime management for PinUvAuthProtocol session
keys, change the auth_protocol::platform::Session trait to represent a
single Session, which maintains its own platform key agreement key, and
can be managed with the lifetime of the value itself.
11 months ago
Nick Zana 941a5f3949 ctap2-proto: Enable adt_const_params feature
This unstable feature is used in the
authenticator::client_pin::auth_protocol module to keep track of which
version of the PinUvAuthProtocol a given Authenticator or Platform
interface implements.
11 months ago
Nick Zana 6b84fd398e ctap2-proto: Put extensions::cred_protect::Policy serde derivation behind "serde" feature flag 11 months ago
Nick Zana feabea834a ctap2-proto: Formatting 11 months ago
Nick Zana f3f2c5128c ctap2-proto: Add authenticator::client_pin::auth_protocol::{Authenticator, Platform} trait defitions
Adds traits to define the Platform and Authenticator abstract
definitions for the PIN/UV Auth Protocol.
11 months ago
Nick Zana a8a9eeb817 ctap2-proto: Move authenticator::client_pin::AuthProtocolVersion to own module
In preparation for adding traits for the PIN/UV Auth Protocol, this
splits the AuthProtocolVersion type into its own module and renames it
to auth_protocol::Version.
11 months ago
Nick Zana 16d11745a3 ctap2-proto: Serialize/Deserialize authenticator::client_pin::AuthProtocolVersion as u8
Use u8::From<AuthProtocolVersion> and AuthProtocolVersion::TryFrom<u8>
implementations instead of manually implementing Serialize/Deserialize.
11 months ago
Nick Zana 4a816e846c ctap2-proto: Add cosey dependency for COSE PublicKey type
Used to manage serialization and deserialization of COSE public keys
instead of coset in order to be able to derive De/Serialize for types
that include COSE public keys as fields in CBOR messages.

coset is inadequate for the purpose because it uses a distinct
AsCborSerialize trait instead of the standard serde::{Serialize,
Deserialize} traits.
11 months ago
Nick Zana 5ea0cfeb9e ctap2-proto: Derive common traits for extensions::cred_protect::Policy
Derive Clone, Copy, and serde traits for usage in CTAP messages
11 months ago
Nick Zana 67a2986167 ctap2-proto: Gate serde attributes for authenticator::assertion::get types behind serde feature 11 months ago
Nick Zana 860f574f0a ctap2-proto: Add hex dev-dependency for CBOR debugging 11 months ago
Nick Zana cea42642dc ctap2-proto: Set associated raw values for extensions::cred_protect::Policy enum 11 months ago
Nick Zana 8c8984edcb fido-common: Derive (Partial)Eq for public_key types 12 months ago
Nick Zana b86d20f0dd fido-common: Add TODO to de/serialize Transport::Unknown as inner String 12 months ago
Nick Zana d09fd56842 fido-common: Add Clone, Eq traits to attestation::Statement 12 months ago
Nick Zana e1b50351a3 Remove all clippy warning bypasses 12 months ago
Nick Zana 04ff08c14f chore: update Cargo.lock 12 months ago
Nick Zana 76089d60cb ctap2_proto: remove extraneous client_pin::Subcommand enum 12 months ago
Nick Zana 413b55f098 ctap2-proto: Add self params to Ctap Authentictaor methods
Self required to access authenticator.
12 months ago
Nick Zana 0804a06f02 ctap2-proto: Uncomment temporarily disabled methods 12 months ago
Nick Zana d5a4a03c0d fido-common: derives comparison traits for several types
Derives PartialOrd and Ord for credential::public_key::Parameters
Derives PartialEq, Eq, PartialOrd and Ord for credential::public_key::UserEntity

Also derives clone and copy for some types.
1 year ago
Nick Zana 03fb2f1eb6 fido-common: Derive ordering for credential::Type 1 year ago
Nick Zana 7db826dc78 ctap2-proto: derive serde traits for authenticator::reset::Error 1 year ago
Nick Zana b5e33f889e ctap2-hid: All ctap commands need self reference 1 year ago
Nick Zana cc837a8641 chore: update Cargo.lock 1 year ago
Nick Zana 7f8f97b0c3 ctap2-proto: Derive builder trait for authenticator::credential::make::Request
Adds typed-builder dependency to derive Builder struct.
1 year ago
Nick Zana 8bf52aa842 ctap2-proto: Derive Deserialize for authenticator::assertion::get::Response 1 year ago
Nick Zana eb4ccf34a3 ctap2-proto: Derive Serialize for authenticator::credential::get::Request 1 year ago
Nick Zana 49dfb44ea7 ctap2-proto: Derive serde traits for authenticator::credential::get::OptionKey 1 year ago
Nick Zana 50bdffbe08 ctap2-proto: Derive Deserialize for authenticator::credential::make::Response 1 year ago
Nick Zana 9d46ecf75b ctap2-proto: Add attestation_statement field to credential::make::Response 1 year ago
Nick Zana 54d8ffc2bf ctap2-proto: Derive Serialize for credential::make::Request
Enables cfg_eval feature because serde_as does not support cfg_attr for
conditional compilation.
1 year ago
Nick Zana ea1df60e74 ctap2-proto: Make make::Request::pin_uv_auth_param optional
the `pin_uv_auth_param` field is an optional field in the CTAP spec.
1 year ago
Nick Zana 573069afad ctap2-proto: Derive serde traits for credential::make::OptionKey 1 year ago
Nick Zana 12d6f7fe51 ctap2-proto: Derive common traits for credential::{make, get}
Derives Debug and other common traits for the credential::{make, get}
types.
1 year ago
Nick Zana 754fc51510 ctap2-proto: Include all of fido-common in prelude 1 year ago
Nick Zana 166fe1bca6 ctap2-proto: Add serde_with
Required for serializing and deserializing Vec<u8>s as byte strings
instead of sequences of bytes in CTAP request/response types.
1 year ago
Nick Zana 7f98c2779a fido-common: Fix serde_with version to support serde fork 1 year ago
Nick Zana cfc7856894 Patch serde to github.com/AndrewScull/serde fork
serde_derive does not currently support using integer values as map
keys. CTAP uses integer values for various CBOR map keys. In order to
properly serialize and deserialize types for the CTAP protocol, a forked
version of serde from a pull request that adds support for renaming
field keys as integer types is used.

Tracked in <https://github.com/serde-rs/serde/pull/2209>
1 year ago
Nick Zana d4a94b83af ctap2-proto: Derive serde for device::Info
Serde renames are integers because CTAP uses integer keys in CBOR maps
1 year ago
Nick Zana 76cec18d0a ctap2-proto: Add missing device::Option CredentialManagementPreview 1 year ago
Nick Zana 38bd34becc ctap2-proto: Rename device::Options to proper serde names
Enum names are full names for clarity and readability. Serde renamed to
compressed CTAP protocol names.
1 year ago
Nick Zana 4670954e0d ctap2-proto: Derive serde for authenticator certification levels 1 year ago
Nick Zana 2434c8602a ctap2-proto: rename authenticator::device::Version to proper serde names
Renamed every value to screaming snake case manually to ensure that the
large number of acronyms and numbers don't cause an accidentally
incorrect value to be derived.
1 year ago
Nick Zana 542a3b9b77 ctap2-proto: Derive serde types for UsizeN
Just a wrapper struct around bounded_integer::BoundedUsize the
bounded_integer/serde1 feature needs to be enabled.
1 year ago
Nick Zana 3f61a17784 ctap2-proto: Derive Debug for helper type UsizeN 1 year ago
Nick Zana 1ca1793a24 ctap2-proto: Add new CTAP 2.2 device::Info fields 1 year ago
Nick Zana 502fe2b176 ctap2-proto: Implement serde for authentciator::client_pin::AuthProtocolVersion 1 year ago
Nick Zana 282a7518ec ctap2-proto: Ctap2_2Authenticator::get_assertion requires self param 1 year ago
Nick Zana e1b8a49f0e ctap2-proto: Replace HashMap with BTreeMap for make::Request 1 year ago
Nick Zana 5bc9e57c11 ctap2-proto: formatting 1 year ago
Nick Zana 999c96d3c5 ctap2-proto: Add CTAP Command enum
Adds ctap2_proto::Command enum to represent the CTAP command codes for
the authenticator commands.
1 year ago
Nick Zana 5803ed02c2 ctap2-proto: Remove bounded_vec dependency
bounded_vec does not correctly handle collections with a lower bound of
0.
1 year ago
Nick Zana 5c0d6e391a webauthn3-proto: remove incomplete serialization code 1 year ago
Nick Zana 6ef8cee4de fido-common: Add attestation::Statement data type
Attestation statements are returned as part of the CBOR maps returned by
authenticators in response to authenticatorMakeCredential and
authenticatorGetAssertion commands.

The attestation statements defined by WebAuthn come in various formats.
However, the format identifier is not part of the attestation statement
field in the CBOR map (0x03 attStmt), but rather as a distinct format
field (fmt 0x01).

Normally, this could be worked around with an externally tagged enum,
but using integer tags is not currently supported by serde. By marking
the enum instead as untagged, this should ideally mean that serde can
differentiate between the enum variants by the fields of the attestation
statement, which is itself a CBOR map.

Otherwise, we could always revert to just raw byte sequences for the
attestation statements during (de)serialization and push validating
these statements onto another part of the code.
1 year ago
Nick Zana a0bd1c9e01 fido-common: De/serialize credential::public_key::UserEntity::id as bytes
Uses serde_with to serialize and deseriailze the UserEntity::id field as
a byte string instead of a sequence (array) of bytes.
1 year ago
Nick Zana 42044f2a46 fido-common: Implement serde for credential::public_key::Descriptor
Adds serde_with as a dependency in order to serialize Descriptor::id as
bytes instead of as a sequence.

Enables cfg_eval feature to enable conditional usage of serde_with
behind the "serde" feature flag for the fido-common crate.
1 year ago
Nick Zana 576addac1f fido-common: Implement Deserialize for authenticator::Data
Requires bitflags to parse CTAP "flags" field of AuthenticatorData.

Implements Deserialize for custom CTAP format for authenticator::Data
byte layout.
1 year ago
Nick Zana 90f75b88d8 fido-common: Implement Deserialize for attestation::CredentialData
Relies on ciborium as a dependency because coset requires one of
ciborium's error types. It should be possible to remove this type's
dependence on ciborium.
1 year ago
Nick Zana ce4150d184 fido-common: Add new backup flags to authenticator::Data
As of the CTAP 2.2 revision, authenticator::Data now contains the
backup_eligibility and backup_state flags.
1 year ago
Nick Zana 6e7d134b6d fido-common: Replace authenticator data bools with enums
Replaces authenticator::Data's user_is_verified and user_is_present
boolean flags with clearer UserVerification and UserPresence enums.
1 year ago
Nick Zana e362a5c237 fido-common: Move attestation::enterprise to its own file 1 year ago
Nick Zana 39a5759d12 fido-common: fix credential::public_key::Parameters signature type
The credential::public_key::Parameters field algorithm designates the
public key algorithm represented by the parameter. This replaces the
incorrect registry::algorithms::Signature type with the correct
coset::iana::Algorithm type and adds Serialization/Deserialization
methods for this type.
1 year ago
Nick Zana 1e8a4f21cf fido_common: derive ordering for UserVerify
Necessary to include UserVerify in the BTreeSet specifying the available
user verification modalities.
1 year ago
Nick Zana 8e1b175136 remove bounded_vec from fido_common crate
bounded_vec crate was used to enforce constraints on size of various
fields as defined by webauthn or ctap specs. However, it does not
properly support zero-sized lower bounds.
1 year ago
Nick Zana c1c95bafc1 fido-common: Make extensions::Identifier de/serializable 1 year ago
Nick Zana 2bb574e62b fido-common: add enums for credential backup
Used in by the authenticator in authenticator::Data to convey
information about the backup state of credentials.
1 year ago
Nick Zana 9351d6ea6c format fido-common/Cargo.toml 1 year ago

1095
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -3,3 +3,6 @@
members = [
"crates/*",
]
[patch.crates-io]
serde = { git = "https://github.com/AndrewScull/serde", branch = "intnames" }

@ -1,5 +1,5 @@
#![feature(associated_const_equality, async_fn_in_trait)]
#![allow(clippy::missing_errors_doc, incomplete_features)]
#![allow(incomplete_features)]
pub mod credential;
pub mod discovery;

@ -8,9 +8,15 @@ edition = "2021"
[dependencies]
fido-common = { path = "../fido-common" }
bounded-integer = { version = "0.5.3", features = ["types", "std"] }
bounded-vec = "0.7.1"
coset = "0.3.3"
serde = { version = "1.0", features = ["derive"], optional = true }
serde = { version = "=1.0.136", features = ["derive"], optional = true }
typed-builder = { version = "0.14.0", default-features = false }
# Version <= to support older serde
serde_with = { version = "<=2.2.0", optional = true }
cosey = "0.3.0"
flagset = { version = "0.4.3", default-features = false, features = ["serde"] }
[dev-dependencies]
hex = "0.4.3"
[features]
serde = ["dep:serde", "bounded-vec/serde", "fido-common/serde"]
serde = ["dep:serde", "dep:serde_with", "fido-common/serde", "bounded-integer/serde1"]

@ -1,9 +1,14 @@
use crate::{authenticator::client_pin::AuthProtocolVersion, extensions};
use crate::Sha256Hash;
use bounded_vec::BoundedVec;
use crate::{authenticator::client_pin::auth_protocol, extensions};
use fido_common::credential::public_key;
use std::{collections::BTreeMap, usize};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_with::{serde_as, skip_serializing_none, Bytes};
#[derive(Debug)]
pub enum Error {
OperationDenied,
PinNotSet,
@ -21,63 +26,96 @@ pub enum Error {
/// > The following option keys are defined for use in
/// > [`assertion::get::Request`]'s `options` parameter.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionKey {
/// > user presence: Instructs the authenticator to require user consent
/// > to complete the operation.
UserPrecense,
#[cfg_attr(feature = "serde", serde(rename = "up"))]
UserPresence,
/// > user verification: If true, instructs the authenticator to require
/// > a user-verifying gesture in order to complete the request.
/// > Examples of such gestures are fingerprint scan or a PIN.
#[cfg_attr(feature = "serde", serde(rename = "uv"))]
UserVerification,
}
/// Request parameters for [`Ctap2Device::get_assertion`] operation.
#[derive(Clone, Copy)]
#[cfg_eval]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", serde_as, skip_serializing_none, derive(Serialize))]
pub struct Request<'a> {
/// > relying party identifier
#[cfg_attr(feature = "serde", serde(rename = 0x01))]
pub relying_party_id: &'a str,
/// > Hash of the serialized client data collected by the host.
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"), serde(rename = 0x02))]
pub client_data_hash: &'a Sha256Hash,
/// > An array of [`public_key::Descriptor`] structures, each denoting a
/// > credential, as specified in `WebAuthn`... If this parameter is present
/// > the authenticator MUST only generate a assertion using one of the
/// > denoted credentials.
pub allow_list: Option<&'a BoundedVec<&'a public_key::Descriptor, 1, { usize::MAX }>>,
// Cannot be empty if present
#[cfg_attr(feature = "serde", serde(rename = 0x03))]
pub allow_list: Option<&'a Vec<&'a public_key::Descriptor>>,
/// > Parameters to influence authenticator operation. These parameters
/// > might be authenticator specific.
#[cfg_attr(feature = "serde", serde(rename = 0x04))]
pub extensions: Option<&'a BTreeMap<extensions::Identifier, &'a [u8]>>,
/// > Parameters to influence authenticator operation.
#[cfg_attr(feature = "serde", serde(rename = 0x05))]
pub options: Option<&'a BTreeMap<OptionKey, bool>>,
#[cfg_attr(feature = "serde", serde(rename = 0x06))]
pub pin_uv_auth_param: Option<&'a [u8]>,
/// > PIN/UV protocol version selected by platform.
pub pin_uv_auth_protocol_version: Option<AuthProtocolVersion>,
#[cfg_attr(feature = "serde", serde(rename = 0x07))]
pub pin_uv_auth_protocol_version: Option<auth_protocol::Version>,
}
/// Response structure for [`Ctap2Device::get_assertion`] operation.
#[cfg_eval]
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "serde",
serde_as,
skip_serializing_none,
derive(Deserialize)
)]
pub struct Response {
/// > PublicKeyCredentialDescriptor structure containing the credential
/// > identifier whose private key was used to generate the assertion.
#[cfg_attr(feature = "serde", serde(rename = 0x01))]
pub credential: public_key::Descriptor,
/// > The signed-over contextual bindings made by the authenticator, as
/// > specified in [WebAuthn].
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"), serde(rename = 0x02))]
pub auth_data: Vec<u8>,
/// > The assertion signature produced by the authenticator, as
/// > specified in [WebAuthn].
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"), serde(rename = 0x03))]
pub signature: Vec<u8>,
/// > [`public_key::UserEntity`] structure containing the user account
/// > information
#[cfg_attr(feature = "serde", serde(rename = 0x04))]
pub user: Option<public_key::UserEntity>,
/// > Total number of account credentials for the RP. Optional; defaults
/// > to one. This member is required when more than one credential is
/// > found for an RP, and the authenticator does not have a display or
/// > the UV & UP flags are false.
#[cfg_attr(feature = "serde", serde(rename = 0x05))]
pub number_of_credentials: Option<usize>,
/// > Indicates that a credential was selected by the user via
/// > interaction directly with the authenticator, and thus the platform
/// > does not need to confirm the credential.
#[cfg_attr(feature = "serde", serde(rename = 0x06))]
pub user_selected: Option<bool>,
/// > The contents of the associated `largeBlobKey` if present for the
/// > asserted credential, and if `largeBlobKey` was true in the
/// > extensions input.
#[cfg_attr(
feature = "serde",
serde_as(as = "Option<Bytes>"),
serde(rename = 0x07)
)]
pub large_blob_key: Option<Vec<u8>>,
}

@ -0,0 +1,105 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::marker::ConstParamTy;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, ConstParamTy)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(into = "u8", try_from = "u8")
)]
pub enum Version {
One = 1,
Two = 2,
}
impl From<Version> for u8 {
fn from(value: Version) -> Self {
value as u8
}
}
impl TryFrom<u8> for Version {
type Error = super::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Version::One),
2 => Ok(Version::Two),
_ => Err(super::Error::InvalidParameter),
}
}
}
/// The AES block size, in bytes.
pub const BLOCK_SIZE: usize = 16;
pub mod authenticator {
use super::Version;
pub trait Authenticator {
type Error; // TODO: Can the error cases be enumerated here?
const VERSION: Version;
/// This process is run by the authenticator at power-on.
fn initialize(&mut self) -> Result<(), Self::Error>;
/// Generates a fresh public key.
fn regenerate(&mut self) -> Result<(), Self::Error>;
/// Generates a fresh pinUvAuthToken.
fn reset_pin_uv_auth_token(&mut self) -> Result<(), Self::Error>;
/// Returns the authenticators public key as a COSE_Key structure.
fn get_public_key(&self) -> Result<cosey::PublicKey, Self::Error>;
/// Processes the output of encapsulate from the peer and produces a
/// shared secret, known to both platform and authenticator.
fn decapsulate(&self, peer_cose_key: cosey::PublicKey) -> Result<Vec<u8>, Self::Error>;
/// Decrypts a ciphertext, using sharedSecret as a key, and returns the
/// plaintext.
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, Self::Error>;
/// Verifies that the signature is a valid MAC for the given message. If
/// the key parameter value is the current pinUvAuthToken, it
/// also checks whether the pinUvAuthToken is in use or not.
fn verify(&self, key: &[u8], message: &[u8], signature: &[u8]) -> Result<(), Self::Error>;
}
}
pub mod platform {
use super::{Version, BLOCK_SIZE};
pub trait Session<const VERSION: Version>: Sized {
type Error; // TODO: Can the error cases be enumerated here?
/// Encompasses both the `initialize` and `encapsulate` functions in the
/// platform interface of the spec. This is done to guard against
/// private key reuse by the platform. As a consequence, a new
/// session must be initialized for each transaction with a
/// freshly generated private key.
fn initialize(peer_cose_key: cosey::PublicKey) -> Result<Self, Self::Error>;
fn platform_key_agreement_key(&self) -> &cosey::PublicKey;
/// Encrypts a plaintext to produce a ciphertext, which may be longer
/// than the plaintext. The plaintext is restricted to being a
/// multiple of the AES block size (16 bytes) in length.
fn encrypt<const N: usize>(
&self,
plaintext: &[[u8; BLOCK_SIZE]; N],
) -> Result<[[u8; BLOCK_SIZE]; N], Self::Error>;
/// Decrypts a ciphertext and returns the plaintext.
// TODO: Return a specific type instead of raw bytes?
fn decrypt<const N: usize>(
&self,
ciphertext: &[[u8; BLOCK_SIZE]; N],
) -> [[u8; BLOCK_SIZE]; N];
/// Computes a MAC of the given message.
// TODO: Return a specific type instead of raw bytes?
fn authenticate(&self, message: &[u8]) -> Result<[u8; 16], Self::Error>;
}
}

@ -1,82 +1,144 @@
use std::collections::BTreeSet;
use bounded_integer::BoundedUsize;
use serde_with::{Bytes, DeserializeAs, SerializeAs};
use std::{borrow::Cow, collections::BTreeSet};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub mod auth_protocol;
#[cfg(feature = "serde")]
mod raw;
#[cfg(feature = "serde")]
use raw::{RawRequest, RawResponse};
pub type PinUvAuthParam = [u8; 16];
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PinUvAuthToken {
Short([u8; 16]),
Long([u8; 32]),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum AuthProtocolVersion {
One,
Two,
impl AsRef<[u8]> for PinUvAuthToken {
fn as_ref(&self) -> &[u8] {
match self {
PinUvAuthToken::Short(bytes) => bytes.as_ref(),
PinUvAuthToken::Long(bytes) => bytes.as_ref(),
}
}
}
pub enum Subcommand {
GetPinRetries,
GetKeyAgreement,
SetPin,
ChangePin,
GetPinToken,
GetPinUvAuthTokenUsingUvWithPermissions,
GetUvRetries,
GetPinUvAuthTokenUsingPinWithPermissions,
#[cfg(feature = "serde")]
impl TryFrom<&[u8]> for PinUvAuthToken {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value.len() {
16 => {
let mut short = [0; 16];
short.copy_from_slice(value);
Ok(Self::Short(short))
}
32 => {
let mut long = [0; 32];
long.copy_from_slice(value);
Ok(Self::Long(long))
}
_ => Err(Error::InvalidParameter),
}
}
}
#[cfg(feature = "serde")]
impl<'de> DeserializeAs<'de, PinUvAuthToken> for Bytes {
fn deserialize_as<D>(deserializer: D) -> Result<PinUvAuthToken, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes: Vec<u8> = Bytes::deserialize_as(deserializer)?;
PinUvAuthToken::try_from(bytes.as_ref()).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Copy)]
#[cfg(feature = "serde")]
impl SerializeAs<PinUvAuthToken> for Bytes {
fn serialize_as<S>(source: &PinUvAuthToken, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match source {
PinUvAuthToken::Short(bytes) => Bytes::serialize_as(bytes, serializer),
PinUvAuthToken::Long(bytes) => Bytes::serialize_as(bytes, serializer),
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(into = "RawRequest", try_from = "RawRequest")
)]
pub enum Request<'a> {
GetPinRetries,
GetKeyAgreement {
version: AuthProtocolVersion,
version: auth_protocol::Version,
},
SetPin {
key_agreement: &'a coset::CoseKey,
new_pin_encrypted: &'a [u8],
pin_uv_auth_param: &'a [u8],
version: auth_protocol::Version,
key_agreement: cosey::PublicKey,
new_pin_encrypted: [u8; 64],
pin_uv_auth_param: PinUvAuthParam,
},
ChangePin {
version: AuthProtocolVersion,
pin_hash_encrypted: &'a [u8],
new_pin_encrypted: &'a [u8],
pin_uv_auth_param: &'a [u8],
version: auth_protocol::Version,
key_agreement: cosey::PublicKey,
pin_hash_encrypted: [u8; 16],
new_pin_encrypted: [u8; 64],
pin_uv_auth_param: PinUvAuthParam,
},
GetPinToken {
version: AuthProtocolVersion,
key_agreement: &'a coset::CoseKey,
pin_hash_encrypted: &'a [u8],
version: auth_protocol::Version,
key_agreement: cosey::PublicKey,
pin_hash_encrypted: [u8; 16],
},
GetPinUvAuthTokenUsingUvWithPermissions {
version: AuthProtocolVersion,
key_agreement: &'a coset::CoseKey,
version: auth_protocol::Version,
key_agreement: cosey::PublicKey,
permissions: &'a BTreeSet<Permission>, // TODO: Enforce non-empty set?
relying_party_id: Option<usize>,
relying_party_id: Option<Cow<'a, str>>,
},
GetUvRetries,
GetPinUvAuthTokenUsingPinWithPermissions {
version: AuthProtocolVersion,
key_agreement: &'a coset::CoseKey,
pin_hash_encrypted: usize,
version: auth_protocol::Version,
key_agreement: cosey::PublicKey,
pin_hash_encrypted: [u8; 16],
permissions: &'a BTreeSet<Permission>, // TODO: Enforce non-empty set?
relying_party_id: Option<usize>,
relying_party_id: Option<Cow<'a, str>>,
},
}
pub enum PinUvAuthToken {
Short([u8; 16]),
Long([u8; 32]),
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(into = "RawResponse", try_from = "RawResponse")
)]
pub enum Response {
GetPinRetries {
pin_retries: usize,
power_cycle_state: Option<usize>,
},
GetKeyAgreement {
key_agreement: coset::CoseKey,
},
SetPin {
key_agreement: coset::CoseKey,
new_pin_encrypted: [u8; 64],
pin_uv_auth_param: (),
key_agreement: cosey::PublicKey,
},
SetPin,
ChangePin,
GetPinToken,
GetPinToken {
pin_uv_auth_token: PinUvAuthToken,
},
GetPinUvAuthTokenUsingUvWithPermissions {
/// > The pinUvAuthToken, encrypted by calling encrypt with the shared
/// > secret as the key.
@ -97,6 +159,7 @@ pub enum Response {
},
}
#[derive(Debug, Clone, Copy)]
pub enum Error {
MissingParameter,
InvalidParameter,
@ -116,7 +179,7 @@ pub enum Error {
/// > When obtaining a `pinUvAuthToken`, the platform requests permissions
/// > appropriate for the operations it intends to perform. Consequently, the
/// > `pinUvAuthToken` can only be used for those operations.
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Permission {
/// > This allows the `pinUvAuthToken` to be used for
/// > `authenticatorMakeCredential` operations with the provided `rpId`
@ -142,3 +205,23 @@ pub enum Permission {
/// > `authenticatorConfig` command.
AuthenticatorConfiguration,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::MissingParameter => write!(f, "Missing parameter"),
Error::InvalidParameter => write!(f, "Invalid parameter"),
Error::PinAuthInvalid => write!(f, "PIN auth invalid"),
Error::PinPolicyViolation => write!(f, "PIN policy violation"),
Error::PinBlocked => write!(f, "PIN blocked"),
Error::PinAuthBlocked => write!(f, "PIN auth blocked"),
Error::PinInvalid => write!(f, "PIN invalid"),
Error::OperationDenied => write!(f, "Operation denied"),
Error::UnauthorizedPermission => write!(f, "Unauthorized permission"),
Error::NotAllowed => write!(f, "Not allowed"),
Error::UserVerificationBlocked => write!(f, "User verification blocked"),
Error::UserActionTimeout => write!(f, "User action timeout"),
Error::UserVerificationInvalid => write!(f, "User verification invalid"),
}
}
}

@ -0,0 +1,355 @@
//! Used to make serialization and deseriazation of the request and response
//! possible in CBOR format while maintaining ergonomic enum variants for public
//! API.
use super::auth_protocol;
use super::Error;
use super::Permission;
use super::{PinUvAuthParam, PinUvAuthToken};
use super::{Request, Response};
use flagset::flags;
use flagset::FlagSet;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes};
use std::borrow::Cow;
mod public_key;
#[derive(Clone, Serialize, Deserialize)]
#[serde(into = "u8")]
pub(crate) enum RawSubcommand {
GetPinRetries = 0x01,
GetKeyAgreement = 0x02,
SetPin = 0x03,
ChangePin = 0x04,
GetPinToken = 0x05,
GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
GetUvRetries = 0x07,
GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
}
impl From<RawSubcommand> for u8 {
fn from(value: RawSubcommand) -> Self {
value as u8
}
}
#[serde_as]
#[derive(Clone, Serialize, Deserialize)]
pub(crate) struct RawRequest<'a> {
#[serde(rename = 0x01, skip_serializing_if = "Option::is_none")]
pub pin_uv_auth_protocol: Option<auth_protocol::Version>,
#[serde(rename = 0x02)]
pub sub_command: RawSubcommand,
#[serde(
rename = 0x03,
deserialize_with = "public_key::deserialize",
skip_serializing_if = "Option::is_none"
)]
pub key_agreement: Option<cosey::PublicKey>,
#[serde_as(as = "Option<Bytes>")]
#[serde(rename = 0x04, skip_serializing_if = "Option::is_none")]
pub pin_uv_auth_param: Option<PinUvAuthParam>,
#[serde_as(as = "Option<Bytes>")]
#[serde(rename = 0x05, skip_serializing_if = "Option::is_none")]
pub new_pin_enc: Option<[u8; 64]>,
#[serde_as(as = "Option<Bytes>")]
#[serde(rename = 0x06, skip_serializing_if = "Option::is_none")]
pub pin_hash_enc: Option<[u8; 16]>,
#[serde(rename = 0x09, skip_serializing_if = "Option::is_none")]
pub permissions: Option<FlagSet<RawPermission>>, // TODO: Deserialize from bitfield
#[serde(rename = 0x0A, skip_serializing_if = "Option::is_none")]
pub rp_id: Option<Cow<'a, str>>,
}
impl<'a> From<Request<'a>> for RawRequest<'a> {
fn from(value: Request<'a>) -> Self {
match value {
Request::GetPinRetries => Self {
pin_uv_auth_protocol: None,
sub_command: RawSubcommand::GetPinRetries,
key_agreement: None,
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: None,
rp_id: None,
permissions: None,
},
Request::GetKeyAgreement { version } => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::GetKeyAgreement,
key_agreement: None,
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: None,
rp_id: None,
permissions: None,
},
Request::SetPin {
key_agreement,
new_pin_encrypted,
pin_uv_auth_param,
version,
} => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::SetPin,
key_agreement: Some(key_agreement),
pin_uv_auth_param: Some(pin_uv_auth_param),
new_pin_enc: Some(new_pin_encrypted.clone()),
pin_hash_enc: None,
rp_id: None,
permissions: None,
},
Request::ChangePin {
version,
pin_hash_encrypted,
new_pin_encrypted,
pin_uv_auth_param,
key_agreement,
} => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::ChangePin,
key_agreement: Some(key_agreement),
pin_uv_auth_param: Some(pin_uv_auth_param),
new_pin_enc: Some(new_pin_encrypted.clone()),
pin_hash_enc: Some(pin_hash_encrypted.clone()),
rp_id: None,
permissions: None,
},
Request::GetPinToken {
version,
key_agreement,
pin_hash_encrypted,
} => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::GetPinToken,
key_agreement: Some(key_agreement),
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: Some(pin_hash_encrypted.clone()),
rp_id: None,
permissions: None,
},
Request::GetPinUvAuthTokenUsingUvWithPermissions {
version,
key_agreement,
permissions,
relying_party_id,
} => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::GetPinUvAuthTokenUsingUvWithPermissions,
key_agreement: Some(key_agreement),
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: None,
rp_id: relying_party_id,
permissions: Some(permissions.iter().map(Clone::clone).collect()),
},
Request::GetUvRetries => Self {
pin_uv_auth_protocol: None,
sub_command: RawSubcommand::GetUvRetries,
key_agreement: None,
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: None,
rp_id: None,
permissions: None,
},
Request::GetPinUvAuthTokenUsingPinWithPermissions {
version,
key_agreement,
pin_hash_encrypted,
permissions,
relying_party_id,
} => Self {
pin_uv_auth_protocol: Some(version),
sub_command: RawSubcommand::GetPinUvAuthTokenUsingPinWithPermissions,
key_agreement: Some(key_agreement),
pin_uv_auth_param: None,
new_pin_enc: None,
pin_hash_enc: Some(pin_hash_encrypted),
rp_id: relying_party_id,
permissions: Some(permissions.iter().map(Clone::clone).collect()),
},
}
}
}
impl<'a> TryFrom<RawRequest<'a>> for Request<'a> {
type Error = Error;
fn try_from(value: RawRequest<'a>) -> Result<Self, Self::Error> {
todo!()
}
}
#[serde_as]
#[derive(Serialize, Deserialize)]
pub(crate) struct RawResponse {
#[serde(
rename = 0x01,
default, // Allows for None variant to be deserialized when 0x01 is not present, required
// because of deserialize_with
deserialize_with = "public_key::deserialize",
skip_serializing_if = "Option::is_none",
)]
pub key_agreement: Option<cosey::PublicKey>,
#[serde_as(as = "Option<Bytes>")]
#[serde(rename = 0x02, skip_serializing_if = "Option::is_none")]
pub pin_uv_auth_token: Option<PinUvAuthToken>,
#[serde(rename = 0x03, skip_serializing_if = "Option::is_none")]
pub pin_retries: Option<usize>,
#[serde(rename = 0x04, skip_serializing_if = "Option::is_none")]
pub power_cycle_state: Option<usize>,
#[serde(rename = 0x05, skip_serializing_if = "Option::is_none")]
pub uv_retries: Option<usize>,
}
impl From<Response> for RawResponse {
fn from(value: Response) -> Self {
match value {
Response::GetPinRetries {
pin_retries,
power_cycle_state,
} => Self {
key_agreement: None,
pin_uv_auth_token: None,
pin_retries: Some(pin_retries),
power_cycle_state,
uv_retries: None,
},
Response::GetKeyAgreement { key_agreement } => Self {
key_agreement: Some(key_agreement),
pin_uv_auth_token: None,
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
Response::SetPin => Self {
key_agreement: None,
pin_uv_auth_token: None,
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
Response::ChangePin => Self {
key_agreement: None,
pin_uv_auth_token: None,
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
Response::GetPinToken { pin_uv_auth_token } => Self {
key_agreement: None,
pin_uv_auth_token: Some(pin_uv_auth_token),
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
Response::GetPinUvAuthTokenUsingUvWithPermissions { pin_uv_auth_token } => Self {
key_agreement: None,
pin_uv_auth_token: Some(pin_uv_auth_token),
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
Response::GetUvRetries { uv_retries } => Self {
key_agreement: None,
pin_uv_auth_token: None,
pin_retries: None,
power_cycle_state: None,
uv_retries: Some(uv_retries.get()),
},
Response::GetPinUvAuthTokenUsingPinWithPermissions { pin_uv_auth_token } => Self {
key_agreement: None,
pin_uv_auth_token: Some(pin_uv_auth_token),
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
},
}
}
}
impl TryFrom<RawResponse> for Response {
type Error = Error;
fn try_from(value: RawResponse) -> Result<Self, Self::Error> {
Ok(match value {
RawResponse {
key_agreement: None,
pin_uv_auth_token: None,
pin_retries: Some(pin_retries),
power_cycle_state,
uv_retries: None,
} => Response::GetPinRetries {
pin_retries,
power_cycle_state,
},
RawResponse {
key_agreement: Some(key_agreement),
pin_uv_auth_token: None,
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
} => Response::GetKeyAgreement { key_agreement },
RawResponse {
key_agreement: None,
pin_uv_auth_token: Some(pin_uv_auth_token),
pin_retries: None,
power_cycle_state: None,
uv_retries: None,
} => Response::GetPinToken { pin_uv_auth_token },
_ => todo!(),
})
}
}
flags! {
#[derive(Serialize, Deserialize)]
pub enum RawPermission: u8 {
MakeCredential = 0x01,
GetAssertion = 0x02,
CredentialManagement = 0x04,
BioEnrollment = 0x08,
LargeBlobWrite = 0x10,
AuthenticatorConfiguration = 0x20,
}
}
impl From<Permission> for RawPermission {
fn from(value: Permission) -> Self {
match value {
Permission::MakeCredential => Self::MakeCredential,
Permission::GetAssertion => Self::GetAssertion,
Permission::CredentialManagement => Self::CredentialManagement,
Permission::BiometricEnrollment => Self::BioEnrollment,
Permission::LargeBlobWrite => Self::LargeBlobWrite,
Permission::AuthenticatorConfiguration => Self::AuthenticatorConfiguration,
}
}
}
impl From<RawPermission> for Permission {
fn from(value: RawPermission) -> Self {
match value {
RawPermission::MakeCredential => Self::MakeCredential,
RawPermission::GetAssertion => Self::GetAssertion,
RawPermission::CredentialManagement => Self::CredentialManagement,
RawPermission::BioEnrollment => Self::BiometricEnrollment,
RawPermission::LargeBlobWrite => Self::LargeBlobWrite,
RawPermission::AuthenticatorConfiguration => Self::AuthenticatorConfiguration,
}
}
}
impl FromIterator<Permission> for FlagSet<RawPermission> {
fn from_iter<T: IntoIterator<Item = Permission>>(iter: T) -> Self {
iter.into_iter()
.map(RawPermission::from)
.fold(None.into(), |mut set, flag| {
set |= flag;
set
})
}
}

@ -0,0 +1,32 @@
use cosey::{EcdhEsHkdf256PublicKey, Ed25519PublicKey, P256PublicKey};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum PublicKey {
P256Key(P256PublicKey),
EcdhEsHkdf256Key(EcdhEsHkdf256PublicKey),
Ed25519Key(Ed25519PublicKey),
}
impl Into<cosey::PublicKey> for PublicKey {
fn into(self) -> cosey::PublicKey {
match self {
PublicKey::P256Key(key) => cosey::PublicKey::P256Key(key),
PublicKey::EcdhEsHkdf256Key(key) => cosey::PublicKey::EcdhEsHkdf256Key(key),
PublicKey::Ed25519Key(key) => cosey::PublicKey::Ed25519Key(key),
}
}
}
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<cosey::PublicKey>, D::Error>
where
D: serde::Deserializer<'de>,
{
PublicKey::deserialize(deserializer)
.map(Into::into)
.map(Some)
}

@ -1,19 +1,19 @@
use std::collections::{BTreeMap};
use std::collections::BTreeMap;
use super::client_pin::AuthProtocolVersion;
use super::client_pin::auth_protocol;
#[derive(Clone, Copy)]
pub enum Request<'a> {
/// > This `enableEnterpriseAttestation` subcommand is only implemented if
/// > the enterprise attestation feature is supported.
EnableEnterpriseAttestation {
pin_uv_auth_protocol: AuthProtocolVersion,
pin_uv_auth_protocol: auth_protocol::Version,
pin_uv_auth_param: &'a [u8], // TODO: Is using a more specific type possible?
},
/// > This `toggleAlwaysUv` subcommand is only implemented if the Always
/// > Require User Verification feature is supported.
ToggleAlwaysUserVerification {
pin_uv_auth_protocol: AuthProtocolVersion,
pin_uv_auth_protocol: auth_protocol::Version,
pin_uv_auth_param: &'a [u8], // TODO: Is using a more specific type possible?
},
/// > This `setMinPINLength` subcommand is only implemented if the
@ -22,7 +22,7 @@ pub enum Request<'a> {
/// > This command sets the minimum PIN length in Unicode code points to be
/// > enforced by the authenticator while changing/setting up a ClientPIN.
SetMinPinLength {
pin_uv_auth_protocol: AuthProtocolVersion,
pin_uv_auth_protocol: auth_protocol::Version,
pin_uv_auth_param: &'a [u8], // TODO: Is using a more specific type possible?
},
/// > This subCommand allows vendors to test authenticator configuration
@ -38,8 +38,8 @@ pub enum Request<'a> {
VendorPrototype {
vendor_command_id: usize,
params: &'a BTreeMap<Vec<u8>, Vec<u8>>, /* TODO: Is the character space of keys
* restricted to UTF-8? */
pin_uv_auth_protocol: AuthProtocolVersion,
* restricted to UTF-8? */
pin_uv_auth_protocol: auth_protocol::Version,
pin_uv_auth_param: &'a [u8], // TODO: Is using a more specific type possible?
},
}

@ -3,8 +3,15 @@ use crate::{
extensions, Sha256Hash,
};
use fido_common::{attestation, credential::public_key};
use std::collections::{BTreeMap, HashMap};
use std::collections::BTreeMap;
use typed_builder::TypedBuilder;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_with::{serde_as, skip_serializing_none, Bytes};
#[derive(Debug)]
pub enum Error {
OperationDenied,
PinNotSet,
@ -24,36 +31,46 @@ pub enum Error {
/// > The following option keys are defined for use in
/// > `authenticatorMakeCredential`'s `options` parameter.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionKey {
/// > Specifies whether this credential is to be discoverable or
/// > not.
#[cfg_attr(feature = "serde", serde(rename = "rk"))]
Discoverable,
/// > user presence: Instructs the authenticator to require user
/// > consent
/// > to complete the operation.
#[cfg_attr(feature = "serde", serde(rename = "up"))]
UserPresence,
/// > user verification: If true, instructs the authenticator to require a
/// > user-verifying gesture in order to complete the request. Examples of
/// > such gestures are fingerprint scan or a PIN.
#[cfg_attr(feature = "serde", serde(rename = "uv"))]
UserVerification,
}
#[cfg_eval]
/// Input parameters for [`Ctap2Device::make_credential`] operation.
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", serde_as, skip_serializing_none, derive(Serialize))]
#[derive(Debug, Clone, Copy, TypedBuilder)]
pub struct Request<'a> {
/// > Hash of the ClientData contextual binding specified by host.
#[cfg_attr(feature = "serde", serde(rename = 0x01), serde_as(as = "Bytes"))]
pub client_data_hash: &'a Sha256Hash,
/// > This PublicKeyCredentialRpEntity data structure describes a
/// > Relying Party with which the new public key credential will be
/// > associated.
#[cfg_attr(feature = "serde", serde(rename = 0x02))]
pub relying_party: &'a public_key::RelyingPartyEntity,
/// > ... describes the user account to which the new public key
/// > credential will be associated at the RP.
#[cfg_attr(feature = "serde", serde(rename = 0x03))]
pub user: &'a public_key::UserEntity,
/// > List of supported algorithms for credential generation, as
/// > specified in [WebAuthn]. The array is ordered from most preferred
/// > to least preferred and MUST NOT include duplicate entries.
#[cfg_attr(feature = "serde", serde(rename = 0x04))]
pub public_key_credential_params: &'a [public_key::Parameters], // TODO: BTreeSet? BTreeMap
// with preference as key?
/// > An array of PublicKeyCredentialDescriptor structures, as specified
@ -61,14 +78,28 @@ pub struct Request<'a> {
/// > authenticator already contains one of the credentials enumerated
/// > in this array. This allows RPs to limit the creation of multiple
/// > credentials for the same account on a single authenticator.
#[builder(default, setter(strip_option))]
#[cfg_attr(feature = "serde", serde(rename = 0x05))]
pub exclude_list: Option<&'a [&'a public_key::Descriptor]>,
/// > Parameters to influence authenticator operation, as specified in
/// > [WebAuthn]. These parameters might be authenticator specific.
pub extensions: Option<&'a HashMap<extensions::Identifier, Vec<u8>>>,
#[builder(default, setter(strip_option))]
#[cfg_attr(feature = "serde", serde(rename = 0x06))]
pub extensions: Option<&'a BTreeMap<extensions::Identifier, Vec<u8>>>,
#[builder(default, setter(strip_option))]
#[cfg_attr(feature = "serde", serde(rename = 0x07))]
pub options: Option<&'a BTreeMap<OptionKey, bool>>,
pub pin_uv_auth_param: &'a [u8],
#[builder(default, setter(strip_option))]
#[cfg_attr(
feature = "serde",
serde(rename = 0x08),
serde_as(as = "Option<Bytes>")
)]
pub pin_uv_auth_param: Option<&'a [u8]>,
/// > PIN/UV protocol version selected by platform.
pub pin_uv_auth_protocol_version: Option<client_pin::AuthProtocolVersion>,
#[builder(default, setter(strip_option))]
#[cfg_attr(feature = "serde", serde(rename = 0x09))]
pub pin_uv_auth_protocol_version: Option<client_pin::auth_protocol::Version>,
/// > An authenticator supporting this enterprise attestation feature is
/// > enterprise attestation capable and signals its support via the `ep`
/// > Option ID in the `authenticatorGetInfo` command response.
@ -81,21 +112,32 @@ pub struct Request<'a> {
/// > attestation batching may not apply to the results of this operation
/// > and the platform is requesting an enterprise attestation that includes
/// > uniquely identifying information.
#[builder(default, setter(strip_option))]
#[cfg_attr(feature = "serde", serde(rename = 0x0A))]
pub enterprise_attestation: Option<attestation::enterprise::Kind>,
}
#[cfg_attr(feature = "serde", derive(Deserialize))]
#[derive(Debug)]
pub struct Response {
#[cfg_attr(feature = "serde", serde(rename = 0x01))]
pub format: fido_common::attestation::FormatIdentifier,
#[cfg_attr(feature = "serde", serde(rename = 0x02))]
pub authenticator_data: authenticator::Data,
#[cfg_attr(feature = "serde", serde(rename = 0x03))]
pub attestation_statement: Option<attestation::Statement>,
/// > Indicates whether an enterprise attestation was returned for this
/// > credential. If `epAtt` is absent or present and set to false, then an
/// > enterprise attestation was not returned. If `epAtt` is present and set
/// > to true, then an enterprise attestation was returned.
#[cfg_attr(feature = "serde", serde(rename = 0x04))]
pub enterprise_attestation: Option<bool>,
/// > Contains the `largeBlobKey` for the credential, if requested with the
/// > `largeBlobKey` extension.
#[cfg_attr(feature = "serde", serde(rename = 0x05))]
pub large_blob_key: Option<Vec<u8>>,
/// > A map, keyed by extension identifiers, to unsigned outputs of
/// > extensions, if any.
#[cfg_attr(feature = "serde", serde(rename = 0x06))]
pub unsigned_extension_outputs: Option<BTreeMap<extensions::Identifier, Vec<u8>>>,
}

@ -1,19 +1,24 @@
use crate::{authenticator::client_pin, extensions::cred_protect, Sha256Hash};
use fido_common::credential::public_key;
use std::fmt::Display;
pub type PinUvAuthParam = [u8; 16];
#[cfg(feature = "serde")]
mod raw;
#[derive(Clone, Copy)]
pub enum Request<'a> {
GetCredentialsMetadata {
/// > PIN/UV protocol version chosen by the platform.
pin_uv_auth_protocol: client_pin::AuthProtocolVersion,
pin_uv_auth_protocol: client_pin::auth_protocol::Version,
/// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`.
pin_uv_auth_param: &'a PinUvAuthParam,
},
EnumerateRPsBegin {
/// > PIN/UV protocol version chosen by the platform.
pin_uv_auth_protocol: client_pin::AuthProtocolVersion,
pin_uv_auth_protocol: client_pin::auth_protocol::Version,
/// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`.
pin_uv_auth_param: &'a PinUvAuthParam,
},
@ -22,7 +27,7 @@ pub enum Request<'a> {
/// The ID of the relying party to enumerate credentials for.
relying_party_id_hash: &'a Sha256Hash,
/// > PIN/UV protocol version chosen by the platform.
pin_uv_auth_protocol: client_pin::AuthProtocolVersion,
pin_uv_auth_protocol: client_pin::auth_protocol::Version,
/// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`.
pin_uv_auth_param: &'a PinUvAuthParam,
},
@ -31,7 +36,7 @@ pub enum Request<'a> {
/// The ID of the credential to delete.
credential_id: &'a public_key::Descriptor,
/// > PIN/UV protocol version chosen by the platform.
pin_uv_auth_protocol: client_pin::AuthProtocolVersion,
pin_uv_auth_protocol: client_pin::auth_protocol::Version,
/// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`.
pin_uv_auth_param: &'a PinUvAuthParam,
},
@ -41,7 +46,7 @@ pub enum Request<'a> {
/// The updated user information.
user: &'a public_key::UserEntity,
/// > PIN/UV protocol version chosen by the platform.
pin_uv_auth_protocol: client_pin::AuthProtocolVersion,
pin_uv_auth_protocol: client_pin::auth_protocol::Version,
/// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`.
pin_uv_auth_param: &'a PinUvAuthParam,
},
@ -106,3 +111,17 @@ pub enum Error {
NoCredentials,
KeyStoreFull,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match self {
Error::PinUvAuthTokenRequired => "PIN/UV auth token required",
Error::MissingParameter => "Missing parameter",
Error::InvalidParameter => "Invalid parameter",
Error::PinAuthInvalid => "PIN auth invalid",
Error::NoCredentials => "No credentials",
Error::KeyStoreFull => "Key store full",
};
write!(f, "{}", message)
}
}

@ -0,0 +1,37 @@
use super::Error;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(into = "u8", try_from = "u8")]
enum RawSubcommand {
GetCredsMetadata = 0x01,
EnumerateRpsBegin = 0x02,
EnumerateRpsGetNextRp = 0x03,
EnumerateCredentialsBegin = 0x04,
EnumerateCredentialsGetNextCredential = 0x05,
DeleteCredential = 0x06,
UpdateUserInformation = 0x07,
}
impl From<RawSubcommand> for u8 {
fn from(val: RawSubcommand) -> Self {
val as u8
}
}
impl TryFrom<u8> for RawSubcommand {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0x01 => RawSubcommand::GetCredsMetadata,
0x02 => RawSubcommand::EnumerateRpsBegin,
0x03 => RawSubcommand::EnumerateRpsGetNextRp,
0x04 => RawSubcommand::EnumerateCredentialsBegin,
0x05 => RawSubcommand::EnumerateCredentialsGetNextCredential,
0x06 => RawSubcommand::DeleteCredential,
0x07 => RawSubcommand::UpdateUserInformation,
_ => return Err(Error::InvalidParameter),
})
}
}

@ -1,15 +1,18 @@
use crate::authenticator::client_pin::AuthProtocolVersion;
use crate::authenticator::client_pin::auth_protocol;
use crate::authenticator::Transport;
use crate::extensions;
use bounded_vec::BoundedVec;
use fido_common::credential::public_key;
use fido_common::registry;
use fido_common::{attestation, registry};
use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZeroUsize;
use std::usize;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A usize with a minimum value of N
#[derive(PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UsizeN<const N: usize>(bounded_integer::BoundedUsize<N, { usize::MAX }>);
/// > data type byte string and identifying the authenticator model, i.e.
@ -18,10 +21,15 @@ pub struct UsizeN<const N: usize>(bounded_integer::BoundedUsize<N, { usize::MAX
pub type Aaguid = [u8; 16];
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Version {
#[cfg_attr(feature = "serde", serde(rename = "FIDO_2_1"))]
Fido2_1,
#[cfg_attr(feature = "serde", serde(rename = "FIDO_2_0"))]
Fido2_0,
#[cfg_attr(feature = "serde", serde(rename = "FIDO_2_1_PRE"))]
Fido2_1Preview,
#[cfg_attr(feature = "serde", serde(rename = "U2F_V2"))]
U2fV2,
}
@ -32,6 +40,8 @@ pub enum Version {
/// > `AAGUID` via appropriate methods. Platforms may alter their behaviour
/// > based on these hints such as selecting a PIN protocol or `credProtect`
/// > level.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Certification {
/// > The [FIPS140-2] Cryptographic-Module-Validation-Program overall
/// > certification level.
@ -44,6 +54,8 @@ pub enum Certification {
}
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FipsCryptoValidation2Level {
Level1 = 1,
Level2 = 2,
@ -52,6 +64,8 @@ pub enum FipsCryptoValidation2Level {
}
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FipsCryptoValidation3Level {
Level1 = 1,
Level2 = 2,
@ -60,6 +74,8 @@ pub enum FipsCryptoValidation3Level {
}
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FipsPhysicalCryptoValidation2Level {
Level1 = 1,
Level2 = 2,
@ -68,6 +84,8 @@ pub enum FipsPhysicalCryptoValidation2Level {
}
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FipsPhysicalCryptoValidation3Level {
Level1 = 1,
Level2 = 2,
@ -78,6 +96,8 @@ pub enum FipsPhysicalCryptoValidation3Level {
/// > Common Criteria Evaluation Assurance Level [CC1V3-1R5]. This is a integer
/// > from 1 to 7. The intermediate-plus levels are not represented.
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum CommonCriterialLevel {
EAL1 = 1,
EAL2 = 2,
@ -92,6 +112,8 @@ pub enum CommonCriterialLevel {
/// > numbered levels are mapped to the odd numbers, with the plus levels mapped
/// > to the even numbers e.g., level 3+ is mapped to 6.
#[repr(usize)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FidoLevel {
L1 = 1,
L1Plus = 2,
@ -103,13 +125,16 @@ pub enum FidoLevel {
/// These options describe properties of a CTAP device.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OptionId {
/// > Indicates that the device is attached to the client and therefore
/// > cant be removed and used on another client.
#[cfg_attr(feature = "serde", serde(rename = "plat"))]
PlatformDevice,
/// > Specifies whether this authenticator can create discoverable
/// > credentials, and therefore can satisfy `authenticatorGetAssertion`
/// > requests with the `allowList` parameter omitted.
#[cfg_attr(feature = "serde", serde(rename = "rk"))]
DiscoverableCredentials,
/// > ClientPIN feature support:
/// > If present and set to true, it indicates that the device is capable of
@ -120,8 +145,10 @@ pub enum OptionId {
/// >
/// > If absent, it indicates that the device is not capable of accepting a
/// > PIN from the client.
#[cfg_attr(feature = "serde", serde(rename = "clientPin"))]
ClientPin,
/// > Indicates that the device is capable of testing user presence.
#[cfg_attr(feature = "serde", serde(rename = "up"))]
UserPresence,
/// > Indicates that the authenticator supports a built-in user verification
/// > method. For example, devices with UI, biometrics fall into this
@ -146,7 +173,9 @@ pub enum OptionId {
/// > If a device is capable of both built-in user verification and Client
/// > PIN, the authenticator will return both the "uv" and the "clientPin"
/// > option ids.
#[cfg_attr(feature = "serde", serde(rename = "uv"))]
UserVerification,
#[cfg_attr(feature = "serde", serde(rename = "pinUvAuthToken"))]
PinUvAuthToken,
/// > If this noMcGaPermissionsWithClientPin is:
/// > - present and set to true: A `pinUvAuthToken` obtained via
@ -164,17 +193,31 @@ pub enum OptionId {
/// >
/// > Note: `noMcGaPermissionsWithClientPin` MUST only be present if the
/// > `clientPin` option ID is present.
#[cfg_attr(feature = "serde", serde(rename = "noMcGaPermissionsWithClientPin"))]
NoMcGaPermissionsWithClientPin,
#[cfg_attr(feature = "serde", serde(rename = "largeBlobs"))]
LargeBlobs,
#[cfg_attr(feature = "serde", serde(rename = "ep"))]
EnterpriseAttestation,
#[cfg_attr(feature = "serde", serde(rename = "bioEnroll"))]
BiometricEnroll,
#[cfg_attr(feature = "serde", serde(rename = "userVerificationMgmtPreview"))]
UvManagementPreview,
#[cfg_attr(feature = "serde", serde(rename = "uvBioEnroll"))]
UvBiometricEnroll,
#[cfg_attr(feature = "serde", serde(rename = "authnrCfg"))]
AuthenticatorConfig,
#[cfg_attr(feature = "serde", serde(rename = "uvAcfg"))]
UvAuthenticatorConfig,
#[cfg_attr(feature = "serde", serde(rename = "credMgmt"))]
CredentialManagement,
#[cfg_attr(feature = "serde", serde(rename = "credentialMgmtPreview"))]
CredentialManagementPreview,
#[cfg_attr(feature = "serde", serde(rename = "setMinPINLength"))]
SetMinPinLength,
#[cfg_attr(feature = "serde", serde(rename = "makeCredUvNotRqd"))]
MakeCredentialUvNotRequired,
#[cfg_attr(feature = "serde", serde(rename = "alwaysUv"))]
AlwaysRequireUv,
}
@ -182,53 +225,72 @@ pub enum OptionId {
/// > list of its supported protocol versions and extensions, its AAGUID, and
/// > other aspects of its overall capabilities. Platforms should use this
/// > information to tailor their command parameters choices.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct Info {
/// > List of supported CTAP versions.
#[cfg_attr(feature = "serde", serde(rename = 0x01))]
pub versions: BTreeSet<Version>,
/// > List of supported extensions.
#[cfg_attr(feature = "serde", serde(rename = 0x02))]
pub extensions: Option<BTreeSet<extensions::Identifier>>,
/// > The claimed AAGUID.
#[cfg_attr(feature = "serde", serde(rename = 0x03))]
pub aaguid: Aaguid,
/// > List of supported options.
#[cfg_attr(feature = "serde", serde(rename = 0x04))]
pub options: Option<BTreeMap<OptionId, bool>>,
/// > Maximum message size supported by the authenticator.
#[cfg_attr(feature = "serde", serde(rename = 0x05))]
pub max_message_size: Option<usize>,
/// > List of supported PIN/UV auth protocols in order of decreasing
/// > authenticator preference. MUST NOT contain duplicate values...
pub pin_uv_auth_protocols: Option<BoundedVec<AuthProtocolVersion, 1, { usize::MAX }>>,
// Cannot be empty if present
#[cfg_attr(feature = "serde", serde(rename = 0x06))]
pub pin_uv_auth_protocols: Option<Vec<auth_protocol::Version>>,
/// > Maximum number of credentials supported in credentialID list at a time
/// > by the authenticator.
#[cfg_attr(feature = "serde", serde(rename = 0x07))]
pub max_credential_count_in_list: Option<NonZeroUsize>,
/// > Maximum Credential ID Length supported by the authenticator.
#[cfg_attr(feature = "serde", serde(rename = 0x08))]
pub max_credential_id_length: Option<NonZeroUsize>,
/// > List of supported transports.
#[cfg_attr(feature = "serde", serde(rename = 0x09))]
pub transports: Option<BTreeSet<Transport>>,
/// > List of supported algorithms for credential generation... The array is
/// > ordered from most preferred to least preferred and MUST NOT include
/// > duplicate entries...
pub algorithms: Option<BoundedVec<public_key::Parameters, 1, { usize::MAX }>>,
#[cfg_attr(feature = "serde", serde(rename = 0x0A))]
// Cannot be empty if present
pub algorithms: Option<Vec<public_key::Parameters>>,
/// > The maximum size, in bytes, of the serialized large-blob array that
/// > this authenticator can store. If the `authenticatorLargeBlobs` command
/// > is supported, this MUST be specified. Otherwise it MUST NOT be.
#[cfg_attr(feature = "serde", serde(rename = 0x0B))]
pub max_serialized_large_blob_array_size: Option<UsizeN<1024>>,
/// > If this member is:
/// > - present and set to true: `getPinToken` and
/// > `getPinUvAuthTokenUsingPinWithPermissions` will return errors until
/// > after a successful PIN Change.
/// > - present and set to false, or absent: no PIN Change is required.
#[cfg_attr(feature = "serde", serde(rename = 0x0C))]
pub force_pin_change: Option<bool>,
/// > This specifies the current minimum PIN length, in Unicode code points,
/// > the authenticator enforces for ClientPIN. This is applicable for
/// > ClientPIN only: the minPINLength member MUST be absent if the
/// > clientPin option ID is absent; it MUST be present if the authenticator
/// > supports authenticatorClientPIN.
#[cfg_attr(feature = "serde", serde(rename = 0x0D))]
pub min_pin_length: Option<usize>,
/// > Indicates the firmware version of the authenticator model identified
/// > by AAGUID.
#[cfg_attr(feature = "serde", serde(rename = 0x0E))]
pub firmware_version: Option<usize>,
/// > Maximum credBlob length in bytes supported by the authenticator. Must
/// > be present if, and only if, credBlob is included in the supported
/// > extensions list.
#[cfg_attr(feature = "serde", serde(rename = 0x0F))]
pub max_cred_blob_length: Option<UsizeN<32>>,
/// > This specifies the max number of RP IDs that authenticator can set via
/// > `setMinPINLength` subcommand. This is in addition to pre-configured
@ -236,12 +298,14 @@ pub struct Info {
/// > adding additional RP IDs, its value is 0. This MUST ONLY be present
/// > if, and only if, the authenticator supports the `setMinPINLength`
/// > subcommand.
#[cfg_attr(feature = "serde", serde(rename = 0x10))]
pub max_rpids_for_set_min_pin_length: Option<usize>,
/// > This specifies the preferred number of invocations of the
/// > `getPinUvAuthTokenUsingUvWithPermissions` subCommand the platform may
/// > attempt before falling back to the
/// > `getPinUvAuthTokenUsingPinWithPermissions` subCommand or displaying an
/// > error.
#[cfg_attr(feature = "serde", serde(rename = 0x11))]
pub preferred_platform_uv_attempts: Option<NonZeroUsize>,
/// > This specifies the user verification modality supported by the
/// > authenticator via `authenticatorClientPIN`'s
@ -249,8 +313,10 @@ pub struct Info {
/// > to help the platform construct user dialogs. If `clientPin`
/// > is supported it MUST NOT be included in the bit-flags, as `clientPIN`
/// > is not a built-in user verification method.
#[cfg_attr(feature = "serde", serde(rename = 0x12))]
pub uv_modality: Option<BTreeSet<registry::UserVerify>>,
/// > This specifies a list of authenticator certifications.
#[cfg_attr(feature = "serde", serde(rename = 0x13))]
pub certifications: Option<BTreeSet<Certification>>,
/// > If this member is present it indicates the estimated number of
/// > additional discoverable credentials that can be stored. If this value
@ -268,10 +334,22 @@ pub struct Info {
/// > user.id that match an existing discoverable credential and thus
/// > overwrite it, but this value should be set assuming that will not
/// > happen.
#[cfg_attr(feature = "serde", serde(rename = 0x14))]
pub remaining_discoverable_credentials: Option<usize>,
/// > If present the authenticator supports the `authenticatorConfig`
/// > `vendorPrototype` subcommand, and its value is a list of
/// > `authenticatorConfig` `vendorCommandId` values supported, which MAY be
/// > empty.
#[cfg_attr(feature = "serde", serde(rename = 0x15))]
pub vendor_prototype_config_commands: Option<BTreeSet<usize>>,
/// > List of supported attestation formats.
#[cfg_attr(feature = "serde", serde(rename = 0x16))]
pub attestation_formats: Option<BTreeSet<attestation::FormatIdentifier>>,
/// > If present the number of internal User Verification operations since
/// > the last pin entry including all failed attempts.
#[cfg_attr(feature = "serde", serde(rename = 0x17))]
pub uv_count_since_last_pin_entry: Option<usize>,
/// > If present the authenticator requires a 10 second touch for reset.
#[cfg_attr(feature = "serde", serde(rename = 0x18))]
pub long_touch_for_reset: Option<bool>,
}

@ -8,4 +8,3 @@ pub mod credential;
pub mod device;
pub mod reset;
pub mod selection;

@ -1,4 +1,9 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Possible errors for the [`Ctap2Device::reset`] command.
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Error {
/// Returned if the `reset` operation is disabled for the transport used or
/// if user precense is explicitly denied.

@ -1,5 +1,10 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Policy {
UserVerificationOptional,
UserVerificationOptionalWithCredentialIdList,
UserVerificationRequired,
UserVerificationOptional = 0x01,
UserVerificationOptionalWithCredentialIdList = 0x02,
UserVerificationRequired = 0x03,
}

@ -1,3 +1,5 @@
#![feature(cfg_eval, adt_const_params)]
pub mod prelude {
pub use crate::{
authenticator::{
@ -6,29 +8,26 @@ pub mod prelude {
credential::{make, management},
device, reset, selection,
},
Ctap2_2Authenticator,
Command, Ctap2_2Authenticator,
};
pub use fido_common::Sha256Hash;
pub use fido_common::*;
}
use prelude::*;
pub mod authenticator;
pub mod extensions;
use prelude::*;
/// Defines the raw CTAP operations
pub trait Ctap2_2Authenticator {
#[allow(clippy::missing_errors_doc)]
/// > This method is invoked by the host to request generation of a new
/// > credential in the authenticator.
fn make_credential(&mut self, request: make::Request) -> Result<make::Response, make::Error>;
#[allow(clippy::missing_errors_doc)]
/// > This method is used by a host to request cryptographic proof of user
/// > authentication as well as user consent to a given transaction, using a
/// > previously generated credential that is bound to the authenticator and
/// > relying party identifier.
fn get_assertion(request: get::Request) -> Result<get::Response, get::Error>;
fn get_assertion(&mut self, request: get::Request) -> Result<get::Response, get::Error>;
/// > Using this method, platforms can request that the authenticator report
/// > a list of its supported protocol versions and extensions, its AAGUID,
@ -36,40 +35,56 @@ pub trait Ctap2_2Authenticator {
/// > this information to tailor their command parameters choices.
fn get_info(&self) -> device::Info;
#[allow(clippy::missing_errors_doc)]
/// > This command exists so that plaintext PINs are not sent to the
/// > authenticator. Instead, a PIN/UV auth protocol (aka
/// > `pinUvAuthProtocol`) ensures that PINs are encrypted when sent to an
/// > authenticator and are exchanged for a `pinUvAuthToken` that serves to
/// > authenticate subsequent commands.
fn client_pin(request: client_pin::Request) -> Result<client_pin::Response, client_pin::Error>;
fn client_pin(
&mut self,
request: client_pin::Request,
) -> Result<client_pin::Response, client_pin::Error>;
#[allow(clippy::missing_errors_doc)]
/// > This method is used by the client to reset an authenticator back to a
/// > factory default state.
fn reset() -> Result<(), reset::Error>;
fn reset(&mut self) -> Result<(), reset::Error>;
// fn bio_enrollment(
// request: bio_enrollment::Request,
// ) -> Result<bio_enrollment::Response, bio_enrollment::Error>;
fn bio_enrollment(
&mut self,
request: bio_enrollment::Request,
) -> Result<bio_enrollment::Response, bio_enrollment::Error>;
// #[allow(clippy::missing_errors_doc)]
// > This command is used by the platform to manage discoverable
// > credentials on the authenticator.
// fn credential_management(
// request: management::Request,
// ) -> Result<management::Response, management::Error>;
fn credential_management(
&mut self,
request: management::Request,
) -> Result<management::Response, management::Error>;
#[allow(clippy::missing_errors_doc)]
/// > This command allows the platform to let a user select a certain
/// > authenticator by asking for user presence.
fn selection() -> Result<(), authenticator::selection::Error>;
fn selection(&mut self) -> Result<(), authenticator::selection::Error>;
// fn large_blobs() -> Result<(), ()>;
// #[allow(clippy::missing_errors_doc)]
// > This command is used to configure various authenticator features
// > through the use of its subcommands.
// fn authenticator_config(request: config::Request) -> Result<(),
// config::Error>;
fn authenticator_config(&mut self, request: config::Request) -> Result<(), config::Error>;
}
#[repr(u8)]
pub enum Command {
AuthenticatorMakeCredential = 0x01,
AuthenticatorGetAssertion = 0x02,
AuthenticatorGetNextAssertion = 0x08,
AuthenticatorGetInfo = 0x04,
AuthenticatorClientPin = 0x06,
AuthenticatorReset = 0x07,
AuthenticatorBioEnrollment = 0x09,
AuthenticatorCredentialManagement = 0x0A,
AuthenticatorSelection = 0x0B,
AuthenticatorLargeBlobs = 0x0C,
AuthenticatorConfig = 0x0D,
PrototypeAuthenticatorBioEnrollment = 0x40,
PrototypeAuthenticatorCredentialManagement = 0x41,
}

@ -6,9 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bounded-vec = { version = "0.7.1", features = ["serde"] }
ciborium = { version = "0.2.1", default-features = false, optional = true }
coset = { version = "0.3.4", default-features = false }
serde = { version = "1", features = ["derive"], optional = true }
serde = { version = "=1.0.136", features = ["derive"], optional = true }
# Version <= to support older serde
serde_with = { version = "<=2.2.0", optional = true }
bitflags = { version = "2.2.1", default-features = false, optional = true }
[features]
serde = ["dep:serde", "bounded-vec/serde"]
serde = ["dep:serde", "dep:serde_with", "dep:bitflags", "dep:ciborium"]

@ -1,5 +1,11 @@
#[cfg(feature = "serde")]
use crate::credential::public_key::algorithm;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_with::{serde_as, Bytes};
pub mod enterprise;
/// > Attestation statement formats are identified by a string, called an
/// > attestation statement format identifier, chosen by the author of the
@ -35,7 +41,7 @@ use serde::{Deserialize, Serialize};
/// > of registered `WebAuthn` Extensions is maintained in the IANA "WebAuthn
/// > Attestation Statement Format Identifiers" registry
/// > [IANA-WebAuthn-Registries] established by [RFC8809].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FormatIdentifier {
/// > The "packed" attestation statement format is a WebAuthn-optimized
@ -70,36 +76,35 @@ pub enum FormatIdentifier {
None,
}
pub mod enterprise {
#[repr(usize)]
#[derive(Clone, Copy)]
pub enum Kind {
/// > In this case, an enterprise attestation capable authenticator, on
/// > which enterprise attestation is enabled, upon receiving the
/// > enterpriseAttestation parameter with a value of 1 (or 2, see Note
/// > below) on a authenticatorMakeCredential command, will provide
/// > enterprise attestation to a non-updateable pre-configured RP ID
/// > list, as identified by the enterprise and provided to the
/// > authenticator vendor, which is "burned into" the authenticator by
/// > the vendor.
/// > If enterprise attestation is requested for any RP ID other than
/// > the pre-configured RP ID(s), the attestation returned along with
/// > the new credential is a regular privacy-preserving attestation,
/// > i.e., NOT an enterprise attestation.
VendorFacilitated = 1,
/// > In this case, an enterprise attestation capable authenticator on
/// > which enterprise attestation is enabled, upon receiving the
/// > enterpriseAttestation parameter with a value of 2 on a
/// > authenticatorMakeCredential command, will return an enterprise
/// > attestation. The platform is enterprise-managed and has already
/// > performed the necessary vetting of the RP ID.
PlatformManaged = 2,
}
#[cfg_eval]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
serde_as,
derive(Serialize, Deserialize),
// TODO: Workaround until serde can use integer keys as tag, since "fmt" is CBOR key 0x01.
serde(untagged)
)]
pub enum Statement {
#[cfg_attr(feature = "serde", serde(rename = "packed"))]
Packed {
#[cfg_attr(feature = "serde", serde(rename = "alg", with = "algorithm"))]
algorithm: coset::iana::Algorithm,
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"), serde(rename = "sig"))]
signature: Vec<u8>,
#[cfg_attr(feature = "serde", serde_as(as = "Vec<Bytes>"), serde(rename = "x5c"))]
attestation_certificate_chain: Vec<Vec<u8>>, // TODO: Parse X.509 certs
},
Unregistered {
identifier: String,
data: Vec<u8>,
},
}
/// > Attested credential data is a variable-length byte array added to the
/// > authenticator data when generating an attestation object for a given
/// > credential.
#[derive(Debug)]
pub struct CredentialData {
/// > The AAGUID of the authenticator.
pub aaguid: [u8; 16],
@ -108,3 +113,57 @@ pub struct CredentialData {
/// The public key of the credential.
pub public_key: coset::CoseKey,
}
#[cfg(feature = "serde")]
impl TryFrom<&[u8]> for CredentialData {
// TODO: Custom error type?
type Error = coset::CoseError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
// aaguid: 16 Bytes
// SAFETY: Validate that data.len >= 16 for aaguid bytes
if data.len() < 16 {
return Err(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)));
}
let (&aaguid, data) = data.split_array_ref::<16>();
// credentialIdLengh: 2 Bytes
// > Byte length L of credentialId, 16-bit unsigned big-endian integer. Value
// > MUST be ≤ 1023.
// SAFETY: Validate that there are 2 bytes for u16
if data.len() < 2 {
return Err(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)));
}
let (&credential_id_length, mut data) = data.split_array_ref::<2>();
let credential_id_length = u16::from_be_bytes(credential_id_length);
if credential_id_length > 1023 {
return Err(coset::CoseError::UnexpectedItem(
"a credentialIdLength (L) of greater than 1023",
"a 16-bit unsigned big-endian integer less than or equal to 1023",
));
}
// credentialId: L (credential_id_length) Bytes
let credential_id: &[u8] = data.take(..credential_id_length as usize)
.ok_or(coset::CoseError::DecodeFailed(ciborium::de::Error::Io(
coset::EndOfFile,
)))?;
Ok(Self { aaguid, id: credential_id.to_vec(), public_key: Default::default() })
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CredentialData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de> {
let data = Vec::<u8>::deserialize(deserializer)?;
// TODO: Improve error handling
CredentialData::try_from(data.as_slice()).map_err(serde::de::Error::custom)
}
}

@ -0,0 +1,28 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[repr(usize)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Kind {
/// > In this case, an enterprise attestation capable authenticator, on
/// > which enterprise attestation is enabled, upon receiving the
/// > enterpriseAttestation parameter with a value of 1 (or 2, see Note
/// > below) on a authenticatorMakeCredential command, will provide
/// > enterprise attestation to a non-updateable pre-configured RP ID
/// > list, as identified by the enterprise and provided to the
/// > authenticator vendor, which is "burned into" the authenticator by
/// > the vendor.
/// > If enterprise attestation is requested for any RP ID other than
/// > the pre-configured RP ID(s), the attestation returned along with
/// > the new credential is a regular privacy-preserving attestation,
/// > i.e., NOT an enterprise attestation.
VendorFacilitated = 1,
/// > In this case, an enterprise attestation capable authenticator on
/// > which enterprise attestation is enabled, upon receiving the
/// > enterpriseAttestation parameter with a value of 2 on a
/// > authenticatorMakeCredential command, will return an enterprise
/// > attestation. The platform is enterprise-managed and has already
/// > performed the necessary vetting of the RP ID.
PlatformManaged = 2,
}

@ -1,7 +1,27 @@
use crate::{attestation, extensions, Sha256Hash};
use crate::{
attestation,
credential::{BackupEligibility, BackupState},
extensions, Sha256Hash,
};
use std::collections::BTreeMap;
pub enum Flags {}
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserPresence {
Present,
NotPresent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserVerification {
Verified,
NotVerified,
}
/// > The authenticator data structure encodes contextual bindings made by the
/// > authenticator. These bindings are controlled by the authenticator itself,
@ -13,37 +33,134 @@ pub enum Flags {}
/// > software, connected to the client over a secure channel. In both cases,
/// > the Relying Party receives the authenticator data in the same format, and
/// > uses its knowledge of the authenticator to make trust decisions.
#[derive(Debug)]
pub struct Data {
/// > SHA-256 hash of the RP ID the credential is scoped to.
pub relying_party_id_hash: Sha256Hash,
pub user_is_present: bool,
pub user_is_verified: bool,
pub user_presence: UserPresence,
pub user_verification: UserVerification,
pub backup_eligibility: BackupEligibility,
pub backup_state: BackupState,
pub signature_counter: u32,
pub attested_credential_data: Option<attestation::CredentialData>,
pub extensions: Option<BTreeMap<extensions::Identifier, Vec<u8>>>,
}
impl Data {
fn try_from(value: &[u8]) -> Option<Self> {
// 32 bytes: RP id hash
let rp_id = value.get(0..32)?.as_ref();
//
let flags = value.get(32)?;
None
#[cfg(feature = "serde")]
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
// > Flags (bit 0 is the least significant bit):
struct DataFlags: u8 {
// > Bit 0: User Present (UP) result.
// > 1 means the user is present.
const USER_PRESENCE = 0b1 << 0;
// > Bit 2: User Verified (UV) result.
// > 1 means the user is verified.
const USER_VERIFIED = 0b1 << 2;
// > Bit 3: Backup Eligibility (BE).
// > 1 means the public key credential source is backup eligible.
const BACKUP_ELIGIBLE = 0b1 << 3;
// > Bit 4: Backup State (BS).
// > 1 means the public key credential source is currently backed up.
const BACKUP_STATE = 0b1 << 4;
// > Bit 6: Attested credential data included (AT).
// > Indicates whether the authenticator added attested credential data.
const ATTESTED_CREDENTIAL_DATA = 0b1 << 6;
// > Bit 7: Extension data included (ED).
// > Indicates if the authenticator data has extensions.
const EXTENSION_DATA_INCLUDED = 0b1 << 7;
}
}
impl TryFrom<&[u8]> for Data {
type Error = ();
#[cfg(feature = "serde")]
impl DataFlags {
fn user_presence(&self) -> UserPresence {
if self.contains(DataFlags::USER_PRESENCE) {
UserPresence::Present
} else {
UserPresence::NotPresent
}
}
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::try_from(value).ok_or(())
fn user_verification(&self) -> UserVerification {
if self.contains(DataFlags::USER_VERIFIED) {
UserVerification::Verified
} else {
UserVerification::NotVerified
}
}
fn backup_eligibility(&self) -> BackupEligibility {
if self.contains(DataFlags::BACKUP_ELIGIBLE) {
BackupEligibility::Eligible
} else {
BackupEligibility::Ineligible
}
}
fn backup_state(&self) -> BackupState {
if self.contains(DataFlags::BACKUP_STATE) {
BackupState::BackedUp
} else {
BackupState::NotBackedUp
}
}
fn has_attested_credential_data(&self) -> bool {
self.contains(DataFlags::ATTESTED_CREDENTIAL_DATA)
}
}
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
impl<'de> Deserialize<'de> for Data {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de;
let data = Vec::<u8>::deserialize(deserializer)?;
// The authenticator data structure is a byte array of 37 bytes or more
if data.len() < 37 {
return Err(de::Error::invalid_length(data.len(), &"at least 37 bytes"));
}
// SAFETY: split_array_ref panics if const param is out of bounds for slice.
// data.len() guard protects against out of bounds indicies.
// rpIdHash: 32 Bytes
// > SHA-256 hash of the RP ID the credential is scoped to.
let (&relying_party_id_hash, data): (&Sha256Hash, _) = data.split_array_ref::<32>();
// flags: 1 Byte
let (&[flags], data): (&[u8; 1], _) = data.split_array_ref::<1>();
let flags = DataFlags::from_bits_truncate(flags);
// signCount: 4 Bytes
// > Signature counter, 32-bit unsigned big-endian integer.
let (&counter_be_bytes, data) = data.split_array_ref::<4>();
let signature_counter = u32::from_be_bytes(counter_be_bytes);
let attested_credential_data: Option<attestation::CredentialData> =
if flags.has_attested_credential_data() {
Some(attestation::CredentialData::try_from(data).map_err(de::Error::custom)?)
} else {
None
};
Ok(Self {
relying_party_id_hash,
user_presence: flags.user_presence(),
user_verification: flags.user_verification(),
backup_eligibility: flags.backup_eligibility(),
backup_state: flags.backup_state(),
signature_counter,
attested_credential_data,
extensions: None,
})
}
}
/// > Authenticators may implement various transports for communicating with
/// > clients. This enumeration defines hints as to how clients might
@ -78,5 +195,6 @@ pub enum Transport {
/// > authenticators are not removable from the client device.
#[cfg_attr(feature = "serde", serde(rename = "internal"))]
Internal,
Unknown(String),
// TODO: Serialize as contents of string
Unknown(String),
}

@ -9,10 +9,22 @@ use serde::{Deserialize, Serialize};
/// > enumeration are used for versioning the Authentication
/// > Assertion and attestation structures according to the type of
/// > the authenticator.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum Type {
#[cfg_attr(feature = "serde", serde(rename = "public-key"))]
PublicKey,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackupState {
BackedUp = 0b0,
NotBackedUp = 0b1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackupEligibility {
Eligible,
Ineligible,
}

@ -1,14 +1,40 @@
use crate::registry::algorithms;
use crate::{authenticator::Transport, credential};
use bounded_vec::BoundedVec;
use std::collections::BTreeSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use serde_with::{serde_as, skip_serializing_none, Bytes};
#[cfg(feature = "serde")]
pub(crate) mod algorithm {
use coset::iana::{Algorithm, EnumI64};
use serde::{Deserialize, Serialize};
pub(crate) fn serialize<S>(algorithm: &Algorithm, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let i = algorithm.to_i64();
i64::serialize(&i, serializer)
}
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Algorithm, D::Error>
where
D: serde::Deserializer<'de>,
{
let i = i64::deserialize(deserializer)?;
coset::iana::Algorithm::from_i64(i).ok_or(serde::de::Error::invalid_value(
serde::de::Unexpected::Signed(i),
&"an IANA-registered COSE algorithm value",
))
}
}
/// > This dictionary is used to supply additional parameters when
/// > creating a new credential.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_eval]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Parameters {
/// > This member specifies the type of credential to be
@ -20,13 +46,19 @@ pub struct Parameters {
/// > algorithm with which the newly generated credential will
/// > be used, and thus also the type of asymmetric key pair to
/// > be generated, e.g., RSA or Elliptic Curve.
#[cfg_attr(feature = "serde", serde(rename = "alg"))]
pub algorithm: algorithms::Signature,
#[cfg_attr(feature = "serde", serde(rename = "alg", with = "algorithm"))]
pub algorithm: coset::iana::Algorithm,
}
/// > This dictionary identifies a specific public key credential.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
#[cfg_eval]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(
feature = "serde",
serde_as,
skip_serializing_none,
derive(Serialize, Deserialize)
)]
pub struct Descriptor {
/// > This member contains the type of the public key credential
/// > the caller is referring to.
@ -35,7 +67,9 @@ pub struct Descriptor {
/// > A probabilistically-unique byte sequence identifying a
/// > public key credential source and its authentication
/// > assertions.
pub id: BoundedVec<u8, 16, 1023>,
// Bounds: [16, 1023] bytes
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"))]
pub id: Vec<u8>,
/// > This... member contains a hint as to how the client might
/// > communicate with the managing authenticator of the public
/// > key credential the caller is referring to.
@ -46,9 +80,12 @@ pub struct Descriptor {
/// > account to which the new public key credential will be associated at
/// > the RP.
/// Due to deprecation, the `icon` URL is omitted. See <https://github.com/w3c/webauthn/pull/1337/>.
#[derive(Debug)]
#[cfg_eval]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
feature = "serde",
serde_as,
skip_serializing_none,
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
@ -64,7 +101,9 @@ pub struct UserEntity {
//
// WebAuthn says that "The user handle MUST NOT be empty." To maximimize compatibility, the
// definition from the CTAP specs is used.
pub id: BoundedVec<u8, 0, 64>,
// Bounds: [0, 64] bytes
#[cfg_attr(feature = "serde", serde_as(as = "Bytes"))]
pub id: Vec<u8>,
/// > a human-palatable identifier for a user account. It is intended
/// > only for display, i.e., aiding the user in determining the
/// > difference between user accounts with similar displayNames. For
@ -79,7 +118,7 @@ pub struct UserEntity {
/// > This `PublicKeyCredentialRpEntity` data structure describes a Relying
/// > Party with which the new public key credential will be associated.
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RelyingPartyEntity {
/// > A unique identifier for the Relying Party entity.

@ -1,3 +1,6 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// > Extensions are identified by a string, called an extension identifier,
/// > chosen by the extension author.
/// >
@ -18,22 +21,39 @@
/// > Extensions that may exist in multiple versions should take care to include
/// > a version in their identifier. In effect, different versions are thus
/// > treated as different extensions, e.g., `myCompany_extension_01`
#[derive(PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Identifier {
#[cfg_attr(feature = "serde", serde(rename = "appid"))]
AppId,
#[cfg_attr(feature = "serde", serde(rename = "txAuthSimple"))]
TransactionAuthSimple,
#[cfg_attr(feature = "serde", serde(rename = "txAuthGeneric"))]
TransactionAuthGeneric,
#[cfg_attr(feature = "serde", serde(rename = "authnSel"))]
AuthenticationSelection,
#[cfg_attr(feature = "serde", serde(rename = "exts"))]
Extensions,
#[cfg_attr(feature = "serde", serde(rename = "uvi"))]
UserVerificationIndex,
#[cfg_attr(feature = "serde", serde(rename = "loc"))]
Location,
#[cfg_attr(feature = "serde", serde(rename = "uvm"))]
UserVerificationMethod,
#[cfg_attr(feature = "serde", serde(rename = "credProtect"))]
CredentialProtection,
#[cfg_attr(feature = "serde", serde(rename = "credBlob"))]
CredentialBlob,
#[cfg_attr(feature = "serde", serde(rename = "largeBlobKey"))]
LargeBlobKey,
#[cfg_attr(feature = "serde", serde(rename = "minPinLength"))]
MinPinLength,
#[cfg_attr(feature = "serde", serde(rename = "hmac-secret"))]
HmacSecret,
#[cfg_attr(feature = "serde", serde(rename = "appidExclude"))]
AppIdExclude,
#[cfg_attr(feature = "serde", serde(rename = "credProps"))]
CredentialProperties,
#[cfg_attr(feature = "serde", serde(rename = "largeBlob"))]
LargeBlob,
}

@ -1,5 +1,7 @@
pub mod authenticator;
#![feature(cfg_eval, split_array, slice_take)]
pub mod attestation;
pub mod authenticator;
pub mod credential;
pub mod extensions;
pub mod registry;

@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
/// > representation (in quotes), which is used in the authoritative metadata
/// > for FIDO authenticators.
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum UserVerify {
/// > This flag MUST be set if the authenticator is able to confirm user

@ -1,10 +1,11 @@
use crate::token;
use std::marker::ConstParamTy;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(PartialEq, Eq, Clone, Copy, ConstParamTy)]
pub enum DataType {
#[cfg_attr(feature = "serde", serde(rename = "webauthn.create"))]
Create,
@ -32,18 +33,3 @@ pub struct Data<const TYPE: DataType> {
/// > absence indicates that the client doesnt support token binding.
pub token_binding: Option<token::Binding>,
}
#[cfg(feature = "serde")]
impl<const TYPE: DataType> Serialize for Data<TYPE> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
// Keys are: "type", "challenge", "origin", "topOrigin", "crossOrigin"
const LEN: usize = 5;
let mut map = serializer.serialize_map(Some(LEN))?;
// map.serialize_entry("type", value)
todo!()
}
}

@ -1,5 +1,5 @@
#![feature(async_fn_in_trait, adt_const_params, associated_const_equality)]
#![allow(incomplete_features, clippy::unused_async, clippy::doc_markdown)]
#![allow(incomplete_features)]
pub mod attestation;
pub mod authenticator;

Loading…
Cancel
Save