From 4082fbb437839e134cffd4c9fb073c839f62b951 Mon Sep 17 00:00:00 2001 From: Nick Zana Date: Wed, 10 May 2023 13:49:07 -0400 Subject: [PATCH] Add ctap2-proto crate The ctap2-proto crate defines the Ctap2_2Authenticator trait, which implements the latest CTAP spec. --- crates/ctap2-proto/.gitignore | 2 + crates/ctap2-proto/Cargo.toml | 17 ++ crates/ctap2-proto/README.md | 4 + crates/ctap2-proto/src/attestation/mod.rs | 38 +++ .../src/authenticator/assertion/get.rs | 84 ++++++ .../src/authenticator/assertion/mod.rs | 1 + .../src/authenticator/bio_enrollment/mod.rs | 3 + .../src/authenticator/client_pin/mod.rs | 157 ++++++++++ .../ctap2-proto/src/authenticator/config.rs | 52 ++++ .../src/authenticator/credential/make.rs | 97 ++++++ .../authenticator/credential/management.rs | 111 +++++++ .../src/authenticator/credential/mod.rs | 2 + .../ctap2-proto/src/authenticator/device.rs | 277 ++++++++++++++++++ crates/ctap2-proto/src/authenticator/mod.rs | 38 +++ .../src/authenticator/reset/mod.rs | 17 ++ .../src/authenticator/selection.rs | 4 + .../src/extensions/cred_protect.rs | 5 + crates/ctap2-proto/src/extensions/mod.rs | 29 ++ crates/ctap2-proto/src/lib.rs | 65 ++++ 19 files changed, 1003 insertions(+) create mode 100644 crates/ctap2-proto/.gitignore create mode 100644 crates/ctap2-proto/Cargo.toml create mode 100644 crates/ctap2-proto/README.md create mode 100644 crates/ctap2-proto/src/attestation/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/assertion/get.rs create mode 100644 crates/ctap2-proto/src/authenticator/assertion/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/bio_enrollment/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/client_pin/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/config.rs create mode 100644 crates/ctap2-proto/src/authenticator/credential/make.rs create mode 100644 crates/ctap2-proto/src/authenticator/credential/management.rs create mode 100644 crates/ctap2-proto/src/authenticator/credential/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/device.rs create mode 100644 crates/ctap2-proto/src/authenticator/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/reset/mod.rs create mode 100644 crates/ctap2-proto/src/authenticator/selection.rs create mode 100644 crates/ctap2-proto/src/extensions/cred_protect.rs create mode 100644 crates/ctap2-proto/src/extensions/mod.rs create mode 100644 crates/ctap2-proto/src/lib.rs diff --git a/crates/ctap2-proto/.gitignore b/crates/ctap2-proto/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/crates/ctap2-proto/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/crates/ctap2-proto/Cargo.toml b/crates/ctap2-proto/Cargo.toml new file mode 100644 index 0000000..7b045f7 --- /dev/null +++ b/crates/ctap2-proto/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ctap2-proto" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fido-common = { path = "../fido-common" } +bounded-integer = { version = "0.5.3", features = ["types", "std"] } +bounded-vec = "0.7.1" +coset = "0.3.3" +indexmap = "1.9.2" +serde = { version = "1.0", features = ["derive"], optional = true } + +[features] +serde = ["dep:serde", "bounded-vec/serde", "fido-common/serde"] diff --git a/crates/ctap2-proto/README.md b/crates/ctap2-proto/README.md new file mode 100644 index 0000000..f686672 --- /dev/null +++ b/crates/ctap2-proto/README.md @@ -0,0 +1,4 @@ +In *Client to Authenticator Protocol* terminology, this library is intended for +use between the [*client +platform*](https://www.w3.org/TR/webauthn-2/#client-platform) and +[*authenticator*](https://www.w3.org/TR/webauthn-2/#authenticator). diff --git a/crates/ctap2-proto/src/attestation/mod.rs b/crates/ctap2-proto/src/attestation/mod.rs new file mode 100644 index 0000000..acccfe7 --- /dev/null +++ b/crates/ctap2-proto/src/attestation/mod.rs @@ -0,0 +1,38 @@ +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, + } +} + +/// > Attested credential data is a variable-length byte array added to the +/// > authenticator data when generating an attestation object for a given +/// > credential. +pub struct CredentialData { + /// > The AAGUID of the authenticator. + pub aaguid: [u8; 16], + /// The ID of the credential. + pub id: Vec, + /// The public key of the credential. + pub public_key: coset::CoseKey, +} diff --git a/crates/ctap2-proto/src/authenticator/assertion/get.rs b/crates/ctap2-proto/src/authenticator/assertion/get.rs new file mode 100644 index 0000000..32763df --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/assertion/get.rs @@ -0,0 +1,84 @@ +use crate::authenticator::{client_pin::AuthProtocolVersion, Sha256Hash}; +use bounded_vec::BoundedVec; +use fido_common::credential::public_key; +use std::{collections::HashMap, usize}; + +pub enum Error { + OperationDenied, + PinNotSet, + PinInvalid, + InvalidParameter, + MissingParameter, + InvalidOption, + UnsupportedOption, + PinUvAuthTokenRequired, + PinAuthInvalid, + UserActionTimeout, + PinBlocked, + NoCredentials, +} + +/// > The following option keys are defined for use in +/// > [`assertion::get::Request`]'s `options` parameter. +pub enum OptionKey { + /// > user presence: Instructs the authenticator to require user consent + /// > to complete the operation. + UserPrecense, + /// > 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. + UserVerification, +} + +/// Request parameters for [`Ctap2Device::get_assertion`] operation. +#[derive(Clone, Copy)] +pub struct Request<'a> { + /// > relying party identifier + pub relying_party_id: &'a str, + /// > Hash of the serialized client data collected by the host. + pub client_data_hash: &'a Sha256Hash, + /// > An array of [`public_key::Descriptor`] structures, each denoting a + /// > credential, as specified in `WebAuthn`. A platform MUST NOT send + /// > an empty `allowList`—if it would be empty it MUST be omitted. If + /// > this parameter is present the authenticator MUST only generate an + /// > assertion using one of the denoted credentials. + // TODO: Is there an equivalent of Vec1 but for a slice? + pub allow_list: Option<&'a BoundedVec>, + /// > Parameters to influence authenticator operation. These parameters + /// > might be authenticator specific. + pub extensions: Option<&'a HashMap>>, + /// > Parameters to influence authenticator operation. + pub options: Option<&'a HashMap>, + pub pin_uv_auth_param: Option<&'a [u8]>, + /// > PIN/UV protocol version selected by platform. + pub pin_uv_auth_protocol_version: Option, +} + +/// Response structure for [`Ctap2Device::get_assertion`] operation. +pub struct Response { + /// > PublicKeyCredentialDescriptor structure containing the credential + /// > identifier whose private key was used to generate the assertion. + pub credential: public_key::Descriptor, + /// > The signed-over contextual bindings made by the authenticator, as + /// > specified in [WebAuthn]. + pub auth_data: Vec, + /// > The assertion signature produced by the authenticator, as + /// > specified in [WebAuthn]. + pub signature: Vec, + /// > [`public_key::UserEntity`] structure containing the user account + /// > information + pub user: Option, + /// > 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. + pub number_of_credentials: Option, + /// > 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. + pub user_selected: Option, + /// > The contents of the associated `largeBlobKey` if present for the + /// > asserted credential, and if `largeBlobKey` was true in the + /// > extensions input. + pub large_blob_key: Option>, +} diff --git a/crates/ctap2-proto/src/authenticator/assertion/mod.rs b/crates/ctap2-proto/src/authenticator/assertion/mod.rs new file mode 100644 index 0000000..125ca70 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/assertion/mod.rs @@ -0,0 +1 @@ +pub mod get; diff --git a/crates/ctap2-proto/src/authenticator/bio_enrollment/mod.rs b/crates/ctap2-proto/src/authenticator/bio_enrollment/mod.rs new file mode 100644 index 0000000..9e31eb1 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/bio_enrollment/mod.rs @@ -0,0 +1,3 @@ +pub enum Request {} + +pub enum Response {} diff --git a/crates/ctap2-proto/src/authenticator/client_pin/mod.rs b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs new file mode 100644 index 0000000..6500de5 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/client_pin/mod.rs @@ -0,0 +1,157 @@ +use std::collections::HashSet; + +use bounded_integer::BoundedUsize; + +#[derive(Clone, Copy)] +pub enum AuthProtocolVersion { + One, + Two, +} + +pub enum Subcommand { + GetPinRetries, + GetKeyAgreement, + SetPin, + ChangePin, + GetPinToken, + GetPinUvAuthTokenUsingUvWithPermissions, + GetUvRetries, + GetPinUvAuthTokenUsingPinWithPermissions, +} + +#[derive(Clone, Copy)] +pub enum Request<'a> { + GetPinRetries, + GetKeyAgreement { + version: AuthProtocolVersion, + }, + SetPin { + key_agreement: &'a KeyAgreement, + new_pin_encrypted: &'a [u8], + pin_uv_auth_param: &'a [u8], + }, + ChangePin { + version: AuthProtocolVersion, + pin_hash_encrypted: &'a [u8], + new_pin_encrypted: &'a [u8], + pin_uv_auth_param: &'a [u8], + }, + GetPinToken { + version: AuthProtocolVersion, + key_agreement: &'a KeyAgreement, + pin_hash_encrypted: &'a [u8], + }, + GetPinUvAuthTokenUsingUvWithPermissions { + version: AuthProtocolVersion, + key_agreement: &'a KeyAgreement, + permissions: &'a HashSet, // TODO: Enforce non-empty hashset? HashSet1? + relying_party_id: Option, + }, + GetUvRetries, + GetPinUvAuthTokenUsingPinWithPermissions { + version: AuthProtocolVersion, + key_agreement: &'a KeyAgreement, + pin_hash_encrypted: usize, + permissions: &'a HashSet, // TODO: Enforce non-empty hashset? HashSet1? + relying_party_id: Option, + }, +} + +/// The [`Ctap2Device::client_pin`] command enforces several restrictions on the +/// COSE key used in a request and response. The restrictions are as follows: +/// +/// > This COSE_Key-encoded public key MUST contain the optional "`alg`" +/// > parameter and MUST NOT contain any other optional parameters. The "`alg`" +/// > parameter MUST contain a `COSEAlgorithmIdentifier` value. +// This seems like it should be an enum where each `KeyType` variant has its own +// parameters? `coset` uses a CBOR map directly +pub struct KeyAgreement { + pub kty: coset::KeyType, + pub alg: Option, +} + +pub enum PinUvAuthToken { + Short([u8; 16]), + Long([u8; 32]), +} + +pub enum Response { + GetPinRetries { + pin_retries: usize, + power_cycle_state: Option, + }, + GetKeyAgreement { + key_agreement: KeyAgreement, + }, + SetPin { + key_agreement: KeyAgreement, + new_pin_encrypted: [u8; 64], + pin_uv_auth_param: (), + }, + ChangePin, + GetPinToken, + GetPinUvAuthTokenUsingUvWithPermissions { + /// > The pinUvAuthToken, encrypted by calling encrypt with the shared + /// > secret as the key. + pin_uv_auth_token: PinUvAuthToken, + }, + GetUvRetries { + /// > Number of uv attempts remaining before lockout. + /// + /// > The `uv_retries` counter represents the number of user + /// > verification attempts left before built-in user verification is + /// > disabled. + uv_retries: BoundedUsize<1, 25>, + }, + GetPinUvAuthTokenUsingPinWithPermissions { + /// > The pinUvAuthToken, encrypted by calling encrypt with the shared + /// > secret as the key. + pin_uv_auth_token: PinUvAuthToken, + }, +} + +pub enum Error { + MissingParameter, + InvalidParameter, + PinAuthInvalid, + PinPolicyViolation, + PinBlocked, + PinAuthBlocked, + PinInvalid, + OperationDenied, + UnauthorizedPermission, + NotAllowed, + UserVerificationBlocked, + UserActionTimeout, + UserVerificationInvalid, +} + +/// > 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)] +pub enum Permission { + /// > This allows the `pinUvAuthToken` to be used for + /// > `authenticatorMakeCredential` operations with the provided `rpId` + /// > parameter. + MakeCredential, + /// > This allows the `pinUvAuthToken` to be used for + /// > `authenticatorGetAssertion` operations with the provided `rpId` + /// > parameter. + GetAssertion, + /// > This allows the `pinUvAuthToken` to be used with the + /// > `authenticatorCredentialManagement` command. The `rpId` parameter is + /// > optional, if it is present, the `pinUvAuthToken` can only be used for + /// > Credential Management operations on Credentials associated with that + /// > RP ID. + CredentialManagement, + /// > This allows the `pinUvAuthToken` to be used with the + /// > `authenticatorBioEnrollment` command. + BiometricEnrollment, + /// > This allows the `pinUvAuthToken` to be used with the + /// > `authenticatorLargeBlobs` command. + LargeBlobWrite, + /// > This allows the `pinUvAuthToken` to be used with the + /// > `authenticatorConfig` command. + AuthenticatorConfiguration, +} diff --git a/crates/ctap2-proto/src/authenticator/config.rs b/crates/ctap2-proto/src/authenticator/config.rs new file mode 100644 index 0000000..2e94522 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/config.rs @@ -0,0 +1,52 @@ +use std::collections::HashMap; + +use super::client_pin::AuthProtocolVersion; + +#[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_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_param: &'a [u8], // TODO: Is using a more specific type possible? + }, + /// > This `setMinPINLength` subcommand is only implemented if the + /// > `setMinPINLength` option ID is present. + /// > + /// > 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_param: &'a [u8], // TODO: Is using a more specific type possible? + }, + /// > This subCommand allows vendors to test authenticator configuration + /// > features. + /// > + /// > This `vendorPrototype` subcommand is only implemented if the + /// > `vendorPrototypeConfigCommands` member in the `authenticatorGetInfo` + /// > response is present. + /// > + /// > Note: The `vendorPrototype` subCommand is reserved for vendor-specific + /// > authenticator configuration and experimentation. Platforms are not + /// > expected to generally utilize this subCommand. + VendorPrototype { + vendor_command_id: usize, + params: &'a HashMap, Vec>, /* TODO: Is the character space of keys + * restricted to UTF-8? */ + pin_uv_auth_protocol: AuthProtocolVersion, + pin_uv_auth_param: &'a [u8], // TODO: Is using a more specific type possible? + }, +} + +pub enum Error { + MissingParameter, + InvalidParameter, + PinUvAuthTokenRequired, + PinAuthInvalid, +} diff --git a/crates/ctap2-proto/src/authenticator/credential/make.rs b/crates/ctap2-proto/src/authenticator/credential/make.rs new file mode 100644 index 0000000..0a9eff7 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/credential/make.rs @@ -0,0 +1,97 @@ +use crate::{ + attestation, + authenticator::{self, client_pin, Sha256Hash}, +}; +use fido_common::credential::public_key; +use indexmap::IndexSet; +use std::collections::HashMap; + +pub enum Error { + OperationDenied, + PinNotSet, + PinInvalid, + InvalidParameter, + MissingParameter, + UnsupportedAlgorithm, + InvalidOption, + UnsupportedOption, + PinUvAuthTokenRequired, + PinAuthInvalid, + UserActionTimeout, + PinBlocked, + CredentialExcluded, + KeyStoreFull, +} + +/// > The following option keys are defined for use in +/// > `authenticatorMakeCredential`'s `options` parameter. +pub enum OptionKey { + /// > Specifies whether this credential is to be discoverable or + /// > not. + Discoverable, + /// > user presence: Instructs the authenticator to require user + /// > consent + /// > to complete the operation. + 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. + UserVerification, +} + +/// Input parameters for [`Ctap2Device::make_credential`] operation. +#[derive(Clone, Copy)] +pub struct Request<'a> { + /// > Hash of the ClientData contextual binding specified by host. + pub client_data_hash: &'a Sha256Hash, + /// > This PublicKeyCredentialRpEntity data structure describes a + /// > Relying Party with which the new public key credential will be + /// > associated. + pub relying_party: &'a public_key::RelyingPartyEntity, + /// > ... describes the user account to which the new public key + /// > credential will be associated at the RP. + pub user: &'a public_key::UserEntity, + /// > [Set] of supported algorithms for credential generation, as + /// > specified in [WebAuthn]. The array is ordered from most preferred + /// > to least preferred... + pub public_key_credential_params: &'a IndexSet, + /// > An array of PublicKeyCredentialDescriptor structures, as specified + /// > in [WebAuthn]. The authenticator returns an error if the + /// > 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. + 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>>, + pub options: Option<&'a HashMap>, + pub pin_uv_auth_param: &'a [u8], + /// > PIN/UV protocol version selected by platform. + pub pin_uv_auth_protocol_version: Option, + /// > 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. + /// > + /// > If the `enterpriseAttestation` parameter is absent, attestation’s + /// > privacy characteristics are unaffected, regardless of whether the + /// > enterprise attestation feature is presently enabled. + /// > + /// > If present with a valid value, the usual privacy concerns around + /// > attestation batching may not apply to the results of this operation + /// > and the platform is requesting an enterprise attestation that includes + /// > uniquely identifying information. + pub enterprise_attestation: Option, +} + +pub struct Response { + pub format: fido_common::attestation::FormatIdentifier, + pub authenticator_data: authenticator::Data, + /// > 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. + pub enterprise_attestation: Option, + /// > Contains the `largeBlobKey` for the credential, if requested with the + /// > `largeBlobKey` extension. + pub large_blob_key: Option>, +} diff --git a/crates/ctap2-proto/src/authenticator/credential/management.rs b/crates/ctap2-proto/src/authenticator/credential/management.rs new file mode 100644 index 0000000..bd70fc3 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/credential/management.rs @@ -0,0 +1,111 @@ +use crate::{ + authenticator::{client_pin, Sha256Hash}, + extensions::cred_protect, +}; +use fido_common::credential::public_key; + +pub type PinUvAuthParam = [u8; 16]; + +#[derive(Clone, Copy)] +pub enum Request<'a> { + GetCredentialsMetadata { + /// > PIN/UV protocol version chosen by the platform. + pin_uv_auth_protocol: client_pin::AuthProtocolVersion, + /// > 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, + /// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`. + pin_uv_auth_param: &'a PinUvAuthParam, + }, + EnumerateRPsGetNextRP, + EnumerateCredentialsBegin { + /// 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, + /// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`. + pin_uv_auth_param: &'a PinUvAuthParam, + }, + EnumerateCredentialsGetNextCredential, + DeleteCredential { + /// 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, + /// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`. + pin_uv_auth_param: &'a PinUvAuthParam, + }, + UpdateUserInformation { + /// The ID of the credential to update. + credential_id: &'a public_key::Descriptor, + /// The updated user information. + user: &'a public_key::UserEntity, + /// > PIN/UV protocol version chosen by the platform. + pin_uv_auth_protocol: client_pin::AuthProtocolVersion, + /// > First 16 bytes of HMAC-SHA-256 of contents using `pinUvAuthToken`. + pin_uv_auth_param: &'a PinUvAuthParam, + }, +} + +pub enum Response { + GetCredentialsMetadata { + /// > Number of existing discoverable credentials present on the + /// > authenticator. + existing_resident_credentials_count: usize, + /// > Number of maximum possible remaining discoverable credentials + /// > which can be created on the authenticator. + max_possible_remaining_resident_credentials_count: usize, + }, + EnumerateRPsBegin { + relying_party: RelyingParty, + /// > total number of RPs present on the authenticator + total_relying_parties: usize, + }, + EnumerateRPsGetNextRP { + relying_party: RelyingParty, + }, + EnumerateCredentialsBegin { + credential: Credential, + /// > Total number of credentials present on the authenticator for the + /// > RP in question + total_credentials: usize, + }, + EnumerateCredentialsGetNextCredential { + credential: Credential, + }, + DeleteCredential, + UpdateUserInformation, +} + +pub struct RelyingParty { + /// The description of the relying party. + pub relying_party: public_key::RelyingPartyEntity, + /// The hash of the relying party ID. + pub relying_party_id_hash: Sha256Hash, +} + +pub struct Credential { + /// The description of the user account associated with the credential. + pub user: public_key::UserEntity, + /// A description of the public key associated with the credential. + pub credential_id: public_key::Descriptor, + /// The public key associated with the credential. + pub public_key: coset::CoseKey, // TODO: Is this the right set of parameters for cosekey? + /// Indicates the level of user verification the authenticator requires for + /// this credential. + pub credential_protection_policy: cred_protect::Policy, + /// > Large blob encryption key. + pub large_blob_key: Vec, +} + +pub enum Error { + PinUvAuthTokenRequired, + MissingParameter, + InvalidParameter, + PinAuthInvalid, + NoCredentials, + KeyStoreFull, +} diff --git a/crates/ctap2-proto/src/authenticator/credential/mod.rs b/crates/ctap2-proto/src/authenticator/credential/mod.rs new file mode 100644 index 0000000..68e3b34 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/credential/mod.rs @@ -0,0 +1,2 @@ +pub mod make; +pub mod management; diff --git a/crates/ctap2-proto/src/authenticator/device.rs b/crates/ctap2-proto/src/authenticator/device.rs new file mode 100644 index 0000000..b01c091 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/device.rs @@ -0,0 +1,277 @@ +use crate::authenticator::client_pin::AuthProtocolVersion; +use bounded_vec::BoundedVec; +use fido_common::credential::public_key; +use fido_common::{registry, Transport}; +use std::usize; +use std::{ + collections::{HashMap, HashSet}, + num::NonZeroUsize, +}; + +/// A usize with a minimum value of N +#[derive(PartialEq, Eq)] +pub struct UsizeN(bounded_integer::BoundedUsize); + +/// > data type byte string and identifying the authenticator model, i.e. +/// > identical values mean that they refer to the same authenticator model and +/// > different values mean they refer to different authenticator models. +pub struct Aaguid([u8; 16]); + +#[derive(Hash)] +pub enum Version { + Fido2_1, + Fido2_0, + Fido2_1Preview, + U2fV2, +} + +/// > The certifications member provides a hint to the platform with additional +/// > information about certifications that the authenticator has received. +/// > Certification programs may revoke certification of specific devices at any +/// > time. Relying partys are responsible for validating attestations and +/// > `AAGUID` via appropriate methods. Platforms may alter their behaviour +/// > based on these hints such as selecting a PIN protocol or `credProtect` +/// > level. +pub enum Certification { + /// > The [FIPS140-2] Cryptographic-Module-Validation-Program overall + /// > certification level. + FipsCryptoValidation2(FipsCryptoValidation2Level), + FipsCryptoValidation3(FipsCryptoValidation3Level), + FipsPhysicalCryptoValidation2(FipsPhysicalCryptoValidation2Level), + FipsPhysicalCryptoValidation3(FipsPhysicalCryptoValidation3Level), + CommonCriteria(CommonCriterialLevel), + Fido(FidoLevel), +} + +#[repr(usize)] +pub enum FipsCryptoValidation2Level { + Level1 = 1, + Level2 = 2, + Level3 = 3, + Level4 = 4, +} + +#[repr(usize)] +pub enum FipsCryptoValidation3Level { + Level1 = 1, + Level2 = 2, + Level3 = 3, + Level4 = 4, +} + +#[repr(usize)] +pub enum FipsPhysicalCryptoValidation2Level { + Level1 = 1, + Level2 = 2, + Level3 = 3, + Level4 = 4, +} + +#[repr(usize)] +pub enum FipsPhysicalCryptoValidation3Level { + Level1 = 1, + Level2 = 2, + Level3 = 3, + Level4 = 4, +} + +/// > Common Criteria Evaluation Assurance Level [CC1V3-1R5]. This is a integer +/// > from 1 to 7. The intermediate-plus levels are not represented. +#[repr(usize)] +pub enum CommonCriterialLevel { + EAL1 = 1, + EAL2 = 2, + EAL3 = 3, + EAL4 = 4, + EAL5 = 5, + EAL6 = 6, + EAL7 = 7, +} + +/// > FIDO Alliance certification level. This is an integer from 1 to 6. The +/// > 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)] +pub enum FidoLevel { + L1 = 1, + L1Plus = 2, + L2 = 3, + L2Plus = 4, + L3 = 5, + L3Plus = 6, +} + +/// These options describe properties of a CTAP device. +pub enum OptionId { + /// > Indicates that the device is attached to the client and therefore + /// > can’t be removed and used on another client. + PlatformDevice, + /// > Specifies whether this authenticator can create discoverable + /// > credentials, and therefore can satisfy `authenticatorGetAssertion` + /// > requests with the `allowList` parameter omitted. + DiscoverableCredentials, + /// > ClientPIN feature support: + /// > If present and set to true, it indicates that the device is capable of + /// > accepting a PIN from the client and PIN has been set. + /// > + /// > If present and set to false, it indicates that the device is capable + /// > of accepting a PIN from the client and PIN has not been set yet. + /// > + /// > If absent, it indicates that the device is not capable of accepting a + /// > PIN from the client. + ClientPin, + /// > Indicates that the device is capable of testing user presence. + UserPresence, + /// > Indicates that the authenticator supports a built-in user verification + /// > method. For example, devices with UI, biometrics fall into this + /// > category. + /// > + /// > If present and set to true, it indicates that the device is capable of + /// > built-in user verification and its user verification feature is + /// > presently configured. + /// > + /// > If present and set to false, it indicates that the authenticator is + /// > capable of built-in user verification and its user verification + /// > feature is not presently configured. For example, an authenticator + /// > featuring a built-in biometric user verification feature that is not + /// > presently configured will return this "uv" option id set to false. + /// > + /// > If absent, it indicates that the authenticator does not have a + /// > built-in user verification capability. + /// > + /// > A device that can only do Client PIN will not return the "uv" option + /// > id. + /// > + /// > 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. + UserVerification, + PinUvAuthToken, + /// > If this noMcGaPermissionsWithClientPin is: + /// > - present and set to true: A `pinUvAuthToken` obtained via + /// > `getPinUvAuthTokenUsingPinWithPermissions` (or `getPinToken`) cannot + /// > be used for `authenticatorMakeCredential` or + /// > `authenticatorGetAssertion` commands, because it will lack the + /// > necessary `mc` and `ga` permissions. In this situation, platforms + /// > SHOULD NOT attempt to use `getPinUvAuthTokenUsingPinWithPermissions` + /// > if using `getPinUvAuthTokenUsingUvWithPermissions` fails. + /// > + /// > - present and set to false, or absent: A `pinUvAuthToken` obtained via + /// > `getPinUvAuthTokenUsingPinWithPermissions` (or `getPinToken`) can be + /// > used for `authenticatorMakeCredential` or `authenticatorGetAssertion` + /// > commands. + /// > + /// > Note: `noMcGaPermissionsWithClientPin` MUST only be present if the + /// > `clientPin` option ID is present. + NoMcGaPermissionsWithClientPin, + LargeBlobs, + EnterpriseAttestation, + BiometricEnroll, + UvManagementPreview, + UvBiometricEnroll, + AuthenticatorConfig, + UvAuthenticatorConfig, + CredentialManagement, + SetMinPinLength, + MakeCredentialUvNotRequired, + AlwaysRequireUv, +} + +/// > Using this method, platforms can request that the authenticator report a +/// > 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. +pub struct Info { + /// > List of supported CTAP versions. + pub versions: HashSet, + /// > List of supported extensions. + pub extensions: Option>, + /// > The claimed AAGUID. + pub aaguid: Aaguid, + /// > List of supported options. + pub options: Option>, + /// > Maximum message size supported by the authenticator. + pub max_message_size: Option, + /// > List of supported PIN/UV auth protocols in order of decreasing + /// > authenticator preference. MUST NOT contain duplicate values nor be + /// > empty if present. + pub pin_uv_auth_protocols: Option>, + /// > Maximum number of credentials supported in credentialID list at a time + /// > by the authenticator. + pub max_credential_count_in_list: Option, + /// > Maximum Credential ID Length supported by the authenticator. + pub max_credential_id_length: Option, + /// > List of supported transports. + pub transports: Option>, + /// > 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>, + /// > 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. + pub max_serialized_large_blob_array_size: Option>, + /// > 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. + pub force_pin_change: Option, + /// > 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. + pub min_pin_length: Option, + /// > Indicates the firmware version of the authenticator model identified + /// > by AAGUID. + pub firmware_version: Option, + /// > Maximum credBlob length in bytes supported by the authenticator. Must + /// > be present if, and only if, credBlob is included in the supported + /// > extensions list. + pub max_cred_blob_length: Option>, + /// > This specifies the max number of RP IDs that authenticator can set via + /// > `setMinPINLength` subcommand. This is in addition to pre-configured + /// > list authenticator may have. If the authenticator does not support + /// > adding additional RP IDs, its value is 0. This MUST ONLY be present + /// > if, and only if, the authenticator supports the `setMinPINLength` + /// > subcommand. + pub max_rpids_for_set_min_pin_length: Option, + /// > 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. + pub preferred_platform_uv_attempts: Option, + /// > This specifies the user verification modality supported by the + /// > authenticator via `authenticatorClientPIN`'s + /// > `getPinUvAuthTokenUsingUvWithPermissions` subcommand. This is a hint + /// > 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. + pub uv_modality: Option>, + /// > This specifies a list of authenticator certifications. + pub certifications: Option>, + /// > If this member is present it indicates the estimated number of + /// > additional discoverable credentials that can be stored. If this value + /// > is zero then platforms SHOULD create non-discoverable credentials if + /// > possible. + /// > + /// > This estimate SHOULD be based on the assumption that all future + /// > discoverable credentials will have maximally-sized fields and SHOULD + /// > be zero whenever an attempt to create a discoverable credential may + /// > fail due to lack of space, even if it’s possible that some specific + /// > request might succeed. For example, a specific request might include + /// > fields that are smaller than the maximum possible size and thus + /// > succeed, but this value should be zero if a request with maximum-sized + /// > fields would fail. Also, a specific request might have an rp.id and + /// > user.id that match an existing discoverable credential and thus + /// > overwrite it, but this value should be set assuming that will not + /// > happen. + pub remaining_discoverable_credentials: Option, + /// > If present the authenticator supports the `authenticatorConfig` + /// > `vendorPrototype` subcommand, and its value is a list of + /// > `authenticatorConfig` `vendorCommandId` values supported, which MAY be + /// > empty. + pub vendor_prototype_config_commands: Option>, +} diff --git a/crates/ctap2-proto/src/authenticator/mod.rs b/crates/ctap2-proto/src/authenticator/mod.rs new file mode 100644 index 0000000..ed04b75 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/mod.rs @@ -0,0 +1,38 @@ +use crate::attestation; + +pub mod assertion; +pub mod bio_enrollment; +pub mod client_pin; +pub mod config; +pub mod credential; +pub mod device; +pub mod reset; +pub mod selection; + +/// SHA 256 hash values are 32 bytes long. +pub type Sha256Hash = [u8; 32]; + +/// > The authenticator data structure encodes contextual bindings made by the +/// > authenticator. These bindings are controlled by the authenticator itself, +/// > and derive their trust from the `WebAuthn` Relying Party's assessment of +/// > the security properties of the authenticator. In one extreme case, the +/// > authenticator may be embedded in the client, and its bindings may be no +/// > more trustworthy than the client data. At the other extreme, the +/// > authenticator may be a discrete entity with high-security hardware and +/// > 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. +/// > +/// > The authenticator data has a compact but extensible encoding. This is +/// > desired since authenticators can be devices with limited capabilities and +/// > low power requirements, with much simpler software stacks than the client +/// > platform. +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 signature_counter: u32, + pub attested_credential_data: attestation::CredentialData, + // TODO: extensions +} diff --git a/crates/ctap2-proto/src/authenticator/reset/mod.rs b/crates/ctap2-proto/src/authenticator/reset/mod.rs new file mode 100644 index 0000000..4799666 --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/reset/mod.rs @@ -0,0 +1,17 @@ +/// Possible errors for the [`Ctap2Device::reset`] command. +pub enum Error { + /// Returned if the `reset` operation is disabled for the transport used or + /// if user precense is explicitly denied. + OperationDenied, + /// Returned when a user action timeout occurs. + /// + /// > This refers to a timeout that occurs when the authenticator is waiting + /// > for direct action from the user, like a touch. (I.e. not a command + /// > from the platform.) The duration of this timeout is chosen by the + /// > authenticator but MUST be at least 10 seconds. Thirty seconds is a + /// > reasonable value. + UserActionTimeout, + /// Returned when the `reset` request is received by the authenticator more + /// than ten seconds after powering up. + NotAllowed, +} diff --git a/crates/ctap2-proto/src/authenticator/selection.rs b/crates/ctap2-proto/src/authenticator/selection.rs new file mode 100644 index 0000000..16acb9c --- /dev/null +++ b/crates/ctap2-proto/src/authenticator/selection.rs @@ -0,0 +1,4 @@ +pub enum Error { + OperationDenied, + UserActionTimeout, +} diff --git a/crates/ctap2-proto/src/extensions/cred_protect.rs b/crates/ctap2-proto/src/extensions/cred_protect.rs new file mode 100644 index 0000000..ceeb669 --- /dev/null +++ b/crates/ctap2-proto/src/extensions/cred_protect.rs @@ -0,0 +1,5 @@ +pub enum Policy { + UserVerificationOptional, + UserVerificationOptionalWithCredentialIdList, + UserVerificationRequired, +} diff --git a/crates/ctap2-proto/src/extensions/mod.rs b/crates/ctap2-proto/src/extensions/mod.rs new file mode 100644 index 0000000..c9288d9 --- /dev/null +++ b/crates/ctap2-proto/src/extensions/mod.rs @@ -0,0 +1,29 @@ +pub mod cred_protect; + +/// The extension input parameters passed to the authenticator during a call to +/// `make_credential` call. Defined by the extension author. +/// +/// > An extension defines one or two request arguments. The client extension +/// > input, which is a value that can be encoded in JSON, is passed from the +/// > WebAuthn Relying Party to the client in the get() or create() call, while +/// > the CBOR authenticator extension input is passed from the client to the +/// > authenticator for authenticator extensions during the processing of these +/// > calls. +pub enum AuthenticatorExtensionInput { + AppId, + TransactionAuthSimple, + TransactionAuthGeneric, + AuthenticationSelection, + Extensions, + UserVerificationIndex, + Location, + UserVerificationMethod, + CredentialProtection, + CredentialBlob, + LargeBlobKey, + MinPinLength, + HmacSecret, + AppIdExclude, + CredentialProperties, + LargeBlob, +} diff --git a/crates/ctap2-proto/src/lib.rs b/crates/ctap2-proto/src/lib.rs new file mode 100644 index 0000000..6a43442 --- /dev/null +++ b/crates/ctap2-proto/src/lib.rs @@ -0,0 +1,65 @@ +use authenticator::{ + assertion::get, + bio_enrollment, client_pin, config, + credential::{make, management}, + reset, +}; + +pub mod attestation; +pub mod authenticator; +pub mod extensions; + +/// 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(request: make::Request) -> Result; + + #[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; + + /// > Using this method, platforms can request that the authenticator report + /// > a 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. + fn get_info() -> authenticator::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; + + #[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 bio_enrollment(request: bio_enrollment::Request, response: bio_enrollment::Response); + + #[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; + + #[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 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>; +}