Repository: go-webauthn/webauthn
Pull request #232 (2024-04-26 22:51:24)

feat: webauthn level 3


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
	- Introduced a new function for handling credential type hints during login.
	- Added multiple new functions to enhance WebAuthn registration options, such as handling credential parameters, exclusions, and authenticator selections.

- **Refactor**
	- Updated type handling across various functions for consistency and improved type safety in JWT and certificate processing.
	- Removed outdated login options related to conveyance preferences and attestation formats, replacing them with more relevant functionalities.

- **Bug Fixes**
	- Enhanced the handling of resident key requirements in registration options to improve reliability.

- **Documentation**
	- Updated internal documentation to reflect changes in function signatures and type definitions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

coderabbitai[bot]: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: summarize review in progress by coderabbit.ai -->

> [!NOTE]
> Currently processing new changes in this PR. This may take a few minutes, please wait...
> 
> <details>
> <summary>Commits</summary>
> Files that changed from the base of the PR and between 0c97761a14b4f9d6aa71eb0c3b0f30b365aa7eb9 and 6722d0e6ab4a47d85241e1667d310b92482805c4.
> </details>
> 
> 
> <details>
> <summary>Files selected for processing (29)</summary>
> 
> * metadata/metadata.go (2 hunks)
> * protocol/assertion.go (1 hunks)
> * protocol/assertion_test.go (3 hunks)
> * protocol/attestation.go (1 hunks)
> * protocol/attestation_androidkey.go (3 hunks)
> * protocol/attestation_androidkey_test.go (1 hunks)
> * protocol/attestation_apple.go (1 hunks)
> * protocol/attestation_apple_test.go (2 hunks)
> * protocol/attestation_packed.go (4 hunks)
> * protocol/attestation_packed_test.go (1 hunks)
> * protocol/attestation_safetynet.go (4 hunks)
> * protocol/attestation_safetynet_test.go (2 hunks)
> * protocol/attestation_test.go (1 hunks)
> * protocol/attestation_tpm.go (2 hunks)
> * protocol/attestation_tpm_test.go (8 hunks)
> * protocol/attestation_u2f.go (2 hunks)
> * protocol/attestation_u2f_test.go (2 hunks)
> * protocol/authenticator.go (3 hunks)
> * protocol/challenge_test.go (1 hunks)
> * protocol/client.go (1 hunks)
> * protocol/credential.go (2 hunks)
> * protocol/credential_test.go (1 hunks)
> * protocol/entities.go (1 hunks)
> * protocol/extensions.go (1 hunks)
> * protocol/options.go (5 hunks)
> * protocol/webauthncbor/webauthncbor.go (2 hunks)
> * protocol/webauthncose/webauthncose.go (2 hunks)
> * webauthn/login.go (2 hunks)
> * webauthn/registration.go (5 hunks)
> 
> </details>
> 
> 
> ```ascii
>  _________________________
> < Loose code sinks ships. >
>  -------------------------
>   \
>    \   (\__/)
>        (•ㅅ•)
>        / 　 づ
> ```

<!-- end of auto-generated comment: summarize review in progress by coderabbit.ai --><!-- tips_start -->

---

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

<details>
<summary>Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
	- `I pushed a fix in commit <commit_id>.`
	- `Generate unit testing code for this file.`
	- `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
	- `@coderabbitai generate unit testing code for this file.`
	-	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
	- `@coderabbitai generate interesting stats about this repository and render them as a table.`
	- `@coderabbitai show all the console.log statements in this repository.`
	- `@coderabbitai read src/utils.ts and generate unit testing code.`
	- `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (invoked as PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger a review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai help` to get help.


Additionally, you can add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.

### CodeRabbit Configration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Patch:
diff --git a/metadata/metadata.go b/metadata/metadata.go
index af0a495..d97d809 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -548,16 +548,16 @@ type MDSGetEndpointsResponse struct {
 func unmarshalMDSBLOB(body []byte, c http.Client) (MetadataBLOBPayload, error) {
 	var payload MetadataBLOBPayload
 
-	token, err := jwt.Parse(string(body), func(token *jwt.Token) (interface{}, error) {
+	token, err := jwt.Parse(string(body), func(token *jwt.Token) (any, error) {
 		// 2. If the x5u attribute is present in the JWT Header, then
-		if _, ok := token.Header["x5u"].([]interface{}); ok {
+		if _, ok := token.Header["x5u"].(any); ok {
 			// never seen an x5u here, although it is in the spec
 			return nil, errors.New("x5u encountered in header of metadata TOC payload")
 		}
-		var chain []interface{}
+		var chain []any
 		// 3. If the x5u attribute is missing, the chain should be retrieved from the x5c attribute.
 
-		if x5c, ok := token.Header["x5c"].([]interface{}); !ok {
+		if x5c, ok := token.Header["x5c"].([]any); !ok {
 			// If that attribute is missing as well, Metadata TOC signing trust anchor is considered the TOC signing certificate chain.
 			chain[0] = MDSRoot
 		} else {
@@ -600,7 +600,7 @@ func unmarshalMDSBLOB(body []byte, c http.Client) (MetadataBLOBPayload, error) {
 	return payload, err
 }
 
-func validateChain(chain []interface{}, c http.Client) (bool, error) {
+func validateChain(chain []any, c http.Client) (bool, error) {
 	oRoot := make([]byte, base64.StdEncoding.DecodedLen(len(MDSRoot)))
 
 	nRoot, err := base64.StdEncoding.Decode(oRoot, []byte(MDSRoot))
diff --git a/protocol/assertion.go b/protocol/assertion.go
index 48dfb62..184eb42 100644
--- a/protocol/assertion.go
+++ b/protocol/assertion.go
@@ -161,7 +161,7 @@ func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPa
 	sigData := append(p.Raw.AssertionResponse.AuthenticatorData, clientDataHash[:]...)
 
 	var (
-		key interface{}
+		key any
 		err error
 	)
 
diff --git a/protocol/authenticator.go b/protocol/authenticator.go
index 8424fe5..b10b72d 100644
--- a/protocol/authenticator.go
+++ b/protocol/authenticator.go
@@ -135,6 +135,11 @@ const (
 	// BLE indicates the respective authenticator can be contacted over Bluetooth Smart (Bluetooth Low Energy / BLE).
 	BLE AuthenticatorTransport = "ble"
 
+	// SmartCard indicates the respective authenticator can be contacted over ISO/IEC 7816 smart card with contacts.
+	//
+	// WebAuthn Level 3.
+	SmartCard AuthenticatorTransport = "smart-card"
+
 	// Hybrid indicates the respective authenticator can be contacted using a combination of (often separate)
 	// data-transport and proximity mechanisms. This supports, for example, authentication on a desktop computer using
 	// a smartphone.
@@ -322,7 +327,7 @@ func (a *AuthenticatorData) unmarshalAttestedData(rawAuthData []byte) (err error
 
 // Unmarshall the credential's Public Key into CBOR encoding.
 func unmarshalCredentialPublicKey(keyBytes []byte) (rawBytes []byte, err error) {
-	var m interface{}
+	var m any
 
 	if err = webauthncbor.Unmarshal(keyBytes, &m); err != nil {
 		return nil, err
@@ -348,11 +353,6 @@ func ResidentKeyNotRequired() *bool {
 	return &required
 }
 
-// Deprecated: ResidentKeyUnrequired is an alias for ResidentKeyNotRequired and will be completely removed in the future.
-func ResidentKeyUnrequired() *bool {
-	return ResidentKeyNotRequired()
-}
-
 // Verify on AuthenticatorData handles Steps 9 through 12 for Registration
 // and Steps 11 through 14 for Assertion.
 func (a *AuthenticatorData) Verify(rpIdHash []byte, appIDHash []byte, userVerificationRequired bool) error {
diff --git a/protocol/client.go b/protocol/client.go
index 02402f7..ab9b6ab 100644
--- a/protocol/client.go
+++ b/protocol/client.go
@@ -20,7 +20,7 @@ type CollectedClientData struct {
 	Type         CeremonyType  `json:"type"`
 	Challenge    string        `json:"challenge"`
 	Origin       string        `json:"origin"`
-	TopOrigin    string        `json:"topOrigin"`
+	TopOrigin    string        `json:"topOrigin,omitempty"`
 	CrossOrigin  bool          `json:"crossOrigin,omitempty"`
 	TokenBinding *TokenBinding `json:"tokenBinding,omitempty"`
 
diff --git a/protocol/credential.go b/protocol/credential.go
index 7067402..e316ebb 100644
--- a/protocol/credential.go
+++ b/protocol/credential.go
@@ -31,6 +31,7 @@ type ParsedCredential struct {
 
 type PublicKeyCredential struct {
 	Credential
+
 	RawID                   URLEncodedBase64                      `json:"rawId"`
 	ClientExtensionResults  AuthenticationExtensionsClientOutputs `json:"clientExtensionResults,omitempty"`
 	AuthenticatorAttachment string                                `json:"authenticatorAttachment,omitempty"`
@@ -38,6 +39,7 @@ type PublicKeyCredential struct {
 
 type ParsedPublicKeyCredential struct {
 	ParsedCredential
+
 	RawID                   []byte                                `json:"rawId"`
 	ClientExtensionResults  AuthenticationExtensionsClientOutputs `json:"clientExtensionResults,omitempty"`
 	AuthenticatorAttachment AuthenticatorAttachment               `json:"authenticatorAttachment,omitempty"`
@@ -45,6 +47,7 @@ type ParsedPublicKeyCredential struct {
 
 type CredentialCreationResponse struct {
 	PublicKeyCredential
+
 	AttestationResponse AuthenticatorAttestationResponse `json:"response"`
 
 	// Deprecated: Transports is deprecated due to upstream changes to the API.
@@ -56,6 +59,7 @@ type CredentialCreationResponse struct {
 
 type ParsedCredentialCreationData struct {
 	ParsedPublicKeyCredential
+
 	Response ParsedAttestationResponse
 	Raw      CredentialCreationResponse
 }
diff --git a/protocol/entities.go b/protocol/entities.go
index 1d2f6e8..40f3c30 100644
--- a/protocol/entities.go
+++ b/protocol/entities.go
@@ -51,5 +51,5 @@ type UserEntity struct {
 	// authentication and authorization decisions MUST be made on the basis of this id
 	// member, not the displayName nor name members. See Section 6.1 of
 	// [RFC8266](https://www.w3.org/TR/webauthn/#biblio-rfc8266).
-	ID interface{} `json:"id"`
+	ID any `json:"id"`
 }
diff --git a/protocol/extensions.go b/protocol/extensions.go
index f925eb2..e226f7c 100644
--- a/protocol/extensions.go
+++ b/protocol/extensions.go
@@ -5,7 +5,7 @@ package protocol
 // For a list of commonly supported extensions, see §10. Defined Extensions
 // (https://www.w3.org/TR/webauthn/#sctn-defined-extensions).
 
-type AuthenticationExtensionsClientOutputs map[string]interface{}
+type AuthenticationExtensionsClientOutputs map[string]any
 
 const (
 	ExtensionAppID        = "appid"
diff --git a/protocol/options.go b/protocol/options.go
index f48577c..9085fcd 100644
--- a/protocol/options.go
+++ b/protocol/options.go
@@ -17,24 +17,28 @@ type CredentialAssertion struct {
 // In order to create a Credential via create(), the caller specifies a few parameters in a
 // PublicKeyCredentialCreationOptions object.
 //
+// WebAuthn Level 3: hints,attestationFormats.
+//
 // Specification: §5.4. Options for Credential Creation (https://www.w3.org/TR/webauthn/#dictionary-makecredentialoptions)
 type PublicKeyCredentialCreationOptions struct {
-	RelyingParty           RelyingPartyEntity        `json:"rp"`
-	User                   UserEntity                `json:"user"`
-	Challenge              URLEncodedBase64          `json:"challenge"`
-	Parameters             []CredentialParameter     `json:"pubKeyCredParams,omitempty"`
-	Timeout                int                       `json:"timeout,omitempty"`
-	CredentialExcludeList  []CredentialDescriptor    `json:"excludeCredentials,omitempty"`
-	AuthenticatorSelection AuthenticatorSelection    `json:"authenticatorSelection,omitempty"`
-	Hints                  []PublicKeyCredentialHint `json:"hints,omitempty"`
-	Attestation            ConveyancePreference      `json:"attestation,omitempty"`
-	AttestationFormats     []AttestationFormat       `json:"attestationFormats,omitempty"`
-	Extensions             AuthenticationExtensions  `json:"extensions,omitempty"`
+	RelyingParty           RelyingPartyEntity         `json:"rp"`
+	User                   UserEntity                 `json:"user"`
+	Challenge              URLEncodedBase64           `json:"challenge"`
+	Parameters             []CredentialParameter      `json:"pubKeyCredParams,omitempty"`
+	Timeout                int                        `json:"timeout,omitempty"`
+	CredentialExcludeList  []CredentialDescriptor     `json:"excludeCredentials,omitempty"`
+	AuthenticatorSelection AuthenticatorSelection     `json:"authenticatorSelection,omitempty"`
+	Hints                  []PublicKeyCredentialHints `json:"hints,omitempty"`
+	Attestation            ConveyancePreference       `json:"attestation,omitempty"`
+	AttestationFormats     []AttestationFormat        `json:"attestationFormats,omitempty"`
+	Extensions             AuthenticationExtensions   `json:"extensions,omitempty"`
 }
 
 // The PublicKeyCredentialRequestOptions dictionary supplies get() with the data it needs to generate an assertion.
 // Its challenge member MUST be present, while its other members are OPTIONAL.
 //
+// WebAuthn Level 3: hints.
+//
 // Specification: §5.5. Options for Assertion Generation (https://www.w3.org/TR/webauthn/#dictionary-assertion-options)
 type PublicKeyCredentialRequestOptions struct {
 	Challenge          URLEncodedBase64            `json:"challenge"`
@@ -42,8 +46,7 @@ type PublicKeyCredentialRequestOptions struct {
 	RelyingPartyID     string                      `json:"rpId,omitempty"`
 	AllowedCredentials []CredentialDescriptor      `json:"allowCredentials,omitempty"`
 	UserVerification   UserVerificationRequirement `json:"userVerification,omitempty"`
-	Attestation        ConveyancePreference        `json:"attestation,omitempty"`
-	AttestationFormats []AttestationFormat         `json:"attestationFormats,omitempty"`
+	Hints              []PublicKeyCredentialHints  `json:"hints,omitempty"`
 	Extensions         AuthenticationExtensions    `json:"extensions,omitempty"`
 }
 
@@ -97,7 +100,7 @@ const (
 // parameters requesting additional processing by the client and authenticator.
 //
 // Specification: §5.7.1. Authentication Extensions Client Inputs (https://www.w3.org/TR/webauthn/#iface-authentication-extensions-client-inputs)
-type AuthenticationExtensions map[string]interface{}
+type AuthenticationExtensions map[string]any
 
 // AuthenticatorSelection represents the AuthenticatorSelectionCriteria IDL.
 //
@@ -125,69 +128,6 @@ type AuthenticatorSelection struct {
 	UserVerification UserVerificationRequirement `json:"userVerification,omitempty"`
 }
 
-// PublicKeyCredentialHint is a type representing the enum PublicKeyCredentialHints from
-// https://www.w3.org/TR/webauthn-3/#enum-hints.
-type PublicKeyCredentialHint string
-
-const (
-	// PublicKeyCredentialHintSecurityKey is a PublicKeyCredentialHint that indicates that the Relying Party believes
-	// that users will satisfy this request with a physical security key. For example, an enterprise Relying Party may
-	// set this hint if they have issued security keys to their employees and will only accept those authenticators for
-	// registration and authentication.
-	//
-	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
-	// authenticatorAttachment SHOULD be set to cross-platform.
-	PublicKeyCredentialHintSecurityKey PublicKeyCredentialHint = "security-key"
-
-	// PublicKeyCredentialHintClientDevice is a PublicKeyCredentialHint that indicates that the Relying Party believes
-	// that users will satisfy this request with a platform authenticator attached to the client device.
-	//
-	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
-	// authenticatorAttachment SHOULD be set to platform.
-	PublicKeyCredentialHintClientDevice PublicKeyCredentialHint = "client-device"
-
-	// PublicKeyCredentialHintHybrid is a PublicKeyCredentialHint that indicates that the Relying Party believes that
-	// users will satisfy this request with general-purpose authenticators such as smartphones. For example, a consumer
-	// Relying Party may believe that only a small fraction of their customers possesses dedicated security keys. This
-	// option also implies that the local platform authenticator should not be promoted in the UI.
-	//
-	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
-	// authenticatorAttachment SHOULD be set to cross-platform.
-	PublicKeyCredentialHintHybrid PublicKeyCredentialHint = "hybrid"
-)
-
-type AttestationFormat string
-
-const (
-	// AttestationFormatPacked is the "packed" attestation statement format is a WebAuthn-optimized format for
-	// attestation. It uses a very compact but still extensible encoding method. This format is implementable by
-	//authenticators with limited resources (e.g., secure elements).
-	AttestationFormatPacked AttestationFormat = "packed"
-
-	// AttestationFormatTPM is the TPM attestation statement format returns an attestation statement in the same format
-	// as the packed attestation statement format, although the rawData and signature fields are computed differently.
-	AttestationFormatTPM AttestationFormat = "tpm"
-
-	// AttestationFormatAndroidKey is the attestation statement format for platform authenticators on versions "N", and
-	// later, which may provide this proprietary "hardware attestation" statement.
-	AttestationFormatAndroidKey AttestationFormat = "android-key"
-
-	// AttestationFormatAndroidSafetyNet is the attestation statement format that Android-based platform authenticators
-	// MAY produce an attestation statement based on the Android SafetyNet API.
-	AttestationFormatAndroidSafetyNet AttestationFormat = "android-safetynet"
-
-	// AttestationFormatFIDOU2F is the attestation statement format that is used with FIDO U2F authenticators.
-	AttestationFormatFIDOU2F AttestationFormat = "fido-u2f"
-
-	// AttestationFormatApple is the attestation statement format that is used with Apple devices' platform
-	// authenticators.
-	AttestationFormatApple AttestationFormat = "apple"
-
-	// AttestationFormatNone is the attestation statement format that is used to replace any authenticator-provided
-	// attestation statement when a WebAuthn Relying Party indicates it does not wish to receive attestation information.
-	AttestationFormatNone AttestationFormat = "none"
-)
-
 // ConveyancePreference is the type representing the AttestationConveyancePreference IDL.
 //
 // WebAuthn Relying Parties may use AttestationConveyancePreference to specify their preference regarding attestation
@@ -245,6 +185,72 @@ const (
 	PreferEnterpriseAttestation ConveyancePreference = "enterprise"
 )
 
+// AttestationFormat is an internal representation of the relevant inputs for registration.
+//
+// Specification: §5.4 Options for Credential Creation (https://w3c.github.io/webauthn/#dom-publickeycredentialcreationoptions-attestationformats)
+// Registry: https://www.iana.org/assignments/webauthn/webauthn.xhtml
+type AttestationFormat string
+
+const (
+	// AttestationFormatPacked is the "packed" attestation statement format is a WebAuthn-optimized format for
+	// attestation. It uses a very compact but still extensible encoding method. This format is implementable by
+	//authenticators with limited resources (e.g., secure elements).
+	AttestationFormatPacked AttestationFormat = "packed"
+
+	// AttestationFormatTPM is the TPM attestation statement format returns an attestation statement in the same format
+	// as the packed attestation statement format, although the rawData and signature fields are computed differently.
+	AttestationFormatTPM AttestationFormat = "tpm"
+
+	// AttestationFormatAndroidKey is the attestation statement format for platform authenticators on versions "N", and
+	// later, which may provide this proprietary "hardware attestation" statement.
+	AttestationFormatAndroidKey AttestationFormat = "android-key"
+
+	// AttestationFormatAndroidSafetyNet is the attestation statement format that Android-based platform authenticators
+	// MAY produce an attestation statement based on the Android SafetyNet API.
+	AttestationFormatAndroidSafetyNet AttestationFormat = "android-safetynet"
+
+	// AttestationFormatFIDOUniversalSecondFactor is the attestation statement format that is used with FIDO U2F
+	// authenticators.
+	AttestationFormatFIDOUniversalSecondFactor AttestationFormat = "fido-u2f"
+
+	// AttestationFormatApple is the attestation statement format that is used with Apple devices' platform
+	// authenticators.
+	AttestationFormatApple AttestationFormat = "apple"
+
+	// AttestationFormatNone is the attestation statement format that is used to replace any authenticator-provided
+	// attestation statement when a WebAuthn Relying Party indicates it does not wish to receive attestation information.
+	AttestationFormatNone AttestationFormat = "none"
+)
+
+type PublicKeyCredentialHints string
+
+const (
+	// PublicKeyCredentialHintSecurityKey is a PublicKeyCredentialHint that indicates that the Relying Party believes
+	// that users will satisfy this request with a physical security key. For example, an enterprise Relying Party may
+	// set this hint if they have issued security keys to their employees and will only accept those authenticators for
+	// registration and authentication.
+	//
+	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
+	// authenticatorAttachment SHOULD be set to cross-platform.
+	PublicKeyCredentialHintSecurityKey PublicKeyCredentialHints = "security-key"
+
+	// PublicKeyCredentialHintClientDevice is a PublicKeyCredentialHint that indicates that the Relying Party believes
+	// that users will satisfy this request with a platform authenticator attached to the client device.
+	//
+	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
+	// authenticatorAttachment SHOULD be set to platform.
+	PublicKeyCredentialHintClientDevice PublicKeyCredentialHints = "client-device"
+
+	// PublicKeyCredentialHintHybrid is a PublicKeyCredentialHint that indicates that the Relying Party believes that
+	// users will satisfy this request with general-purpose authenticators such as smartphones. For example, a consumer
+	// Relying Party may believe that only a small fraction of their customers possesses dedicated security keys. This
+	// option also implies that the local platform authenticator should not be promoted in the UI.
+	//
+	// For compatibility with older user agents, when this hint is used in PublicKeyCredentialCreationOptions, the
+	// authenticatorAttachment SHOULD be set to cross-platform.
+	PublicKeyCredentialHintHybrid PublicKeyCredentialHints = "hybrid"
+)
+
 func (a *PublicKeyCredentialRequestOptions) GetAllowedCredentialIDs() [][]byte {
 	var allowedCredentialIDs = make([][]byte, len(a.AllowedCredentials))
 
@@ -255,7 +261,7 @@ func (a *PublicKeyCredentialRequestOptions) GetAllowedCredentialIDs() [][]byte {
 	return allowedCredentialIDs
 }
 
-type Extensions interface{}
+type Extensions any
 
 type ServerResponse struct {
 	Status  ServerResponseStatus `json:"status"`
diff --git a/protocol/webauthncbor/webauthncbor.go b/protocol/webauthncbor/webauthncbor.go
index 2886d0f..aff1ac1 100644
--- a/protocol/webauthncbor/webauthncbor.go
+++ b/protocol/webauthncbor/webauthncbor.go
@@ -18,7 +18,7 @@ var ctap2CBOREncMode, _ = cbor.CTAP2EncOptions().EncMode()
 // Unmarshal parses the CBOR-encoded data into the value pointed to by v
 // following the CTAP2 canonical CBOR encoding form.
 // (https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding)
-func Unmarshal(data []byte, v interface{}) error {
+func Unmarshal(data []byte, v any) error {
 	// TODO (james-d-elliott): investigate the specific use case for Unmarshal vs UnmarshalFirst to determine the edge cases where this may be useful.
 	_, err := ctap2CBORDecMode.UnmarshalFirst(data, v)
 
@@ -28,6 +28,6 @@ func Unmarshal(data []byte, v interface{}) error {
 // Marshal encodes the value pointed to by v
 // following the CTAP2 canonical CBOR encoding form.
 // (https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#message-encoding)
-func Marshal(v interface{}) ([]byte, error) {
+func Marshal(v any) ([]byte, error) {
 	return ctap2CBOREncMode.Marshal(v)
 }
diff --git a/protocol/webauthncose/webauthncose.go b/protocol/webauthncose/webauthncose.go
index 308adef..76a0aad 100644
--- a/protocol/webauthncose/webauthncose.go
+++ b/protocol/webauthncose/webauthncose.go
@@ -178,7 +178,7 @@ func HasherFromCOSEAlg(coseAlg COSEAlgorithmIdentifier) func() hash.Hash {
 }
 
 // ParsePublicKey figures out what kind of COSE material was provided and create the data for the new key.
-func ParsePublicKey(keyBytes []byte) (interface{}, error) {
+func ParsePublicKey(keyBytes []byte) (any, error) {
 	pk := PublicKeyData{}
 	// TODO (james-d-elliott): investigate the ignored errors.
 	webauthncbor.Unmarshal(keyBytes, &pk)
@@ -342,7 +342,7 @@ func (k *EC2PublicKeyData) TPMCurveID() tpm2.EllipticCurve {
 	}
 }
 
-func VerifySignature(key interface{}, data []byte, sig []byte) (bool, error) {
+func VerifySignature(key any, data []byte, sig []byte) (bool, error) {
 	switch k := key.(type) {
 	case OKPPublicKeyData:
 		return k.Verify(data, sig)
diff --git a/webauthn/login.go b/webauthn/login.go
index ff1c5c2..69ecb97 100644
--- a/webauthn/login.go
+++ b/webauthn/login.go
@@ -115,19 +115,12 @@ func WithUserVerification(userVerification protocol.UserVerificationRequirement)
 	}
 }
 
-// WithLoginConveyancePreference adjusts the non-default parameters regarding whether the authenticator should attest to the
-// credential.
-func WithLoginConveyancePreference(preference protocol.ConveyancePreference) LoginOption {
-	return func(cco *protocol.PublicKeyCredentialRequestOptions) {
-		cco.Attestation = preference
-	}
-}
-
-// WithLoginAttestationFormats adjusts the preferred attestation formats for this credential request in most to least
-// preferable. Advisory only.
-func WithLoginAttestationFormats(formats ...protocol.AttestationFormat) LoginOption {
+// WithAssertionPublicKeyCredentialHints adjusts the non-default hints for credential types to select during login.
+//
+// WebAuthn Level 3.
+func WithAssertionPublicKeyCredentialHints(hints []protocol.PublicKeyCredentialHints) LoginOption {
 	return func(cco *protocol.PublicKeyCredentialRequestOptions) {
-		cco.AttestationFormats = formats
+		cco.Hints = hints
 	}
 }
 
@@ -145,7 +138,7 @@ func WithAppIdExtension(appid string) LoginOption {
 		for _, credential := range cco.AllowedCredentials {
 			if credential.AttestationType == protocol.CredentialTypeFIDOU2F {
 				if cco.Extensions == nil {
-					cco.Extensions = map[string]interface{}{}
+					cco.Extensions = map[string]any{}
 				}
 
 				cco.Extensions[protocol.ExtensionAppID] = appid
diff --git a/webauthn/registration.go b/webauthn/registration.go
index e24e2b1..c6009ea 100644
--- a/webauthn/registration.go
+++ b/webauthn/registration.go
@@ -29,7 +29,7 @@ func (webauthn *WebAuthn) BeginRegistration(user User, opts ...RegistrationOptio
 		return nil, nil, err
 	}
 
-	var entityUserID interface{}
+	var entityUserID any
 
 	if webauthn.Config.EncodeUserIDAsString {
 		entityUserID = string(user.WebAuthnID())
@@ -93,6 +93,20 @@ func (webauthn *WebAuthn) BeginRegistration(user User, opts ...RegistrationOptio
 	return creation, session, nil
 }
 
+// WithCredentialParameters adjusts the credential parameters in the registration options.
+func WithCredentialParameters(credentialParams []protocol.CredentialParameter) RegistrationOption {
+	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
+		cco.Parameters = credentialParams
+	}
+}
+
+// WithExclusions adjusts the non-default parameters regarding credentials to exclude from registration.
+func WithExclusions(excludeList []protocol.CredentialDescriptor) RegistrationOption {
+	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
+		cco.CredentialExcludeList = excludeList
+	}
+}
+
 // WithAuthenticatorSelection adjusts the non-default parameters regarding the authenticator to select during
 // registration.
 func WithAuthenticatorSelection(authenticatorSelection protocol.AuthenticatorSelection) RegistrationOption {
@@ -101,38 +115,41 @@ func WithAuthenticatorSelection(authenticatorSelection protocol.AuthenticatorSel
 	}
 }
 
-func WithHints(hints ...protocol.PublicKeyCredentialHint) RegistrationOption {
+// WithResidentKeyRequirement sets both the resident key and require resident key protocol options.
+func WithResidentKeyRequirement(requirement protocol.ResidentKeyRequirement) RegistrationOption {
 	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
-		cco.Hints = hints
-	}
-}
+		cco.AuthenticatorSelection.ResidentKey = requirement
 
-// WithExclusions adjusts the non-default parameters regarding credentials to exclude from registration.
-func WithExclusions(excludeList []protocol.CredentialDescriptor) RegistrationOption {
-	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
-		cco.CredentialExcludeList = excludeList
+		switch requirement {
+		case protocol.ResidentKeyRequirementRequired:
+			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyRequired()
+		default:
+			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyNotRequired()
+		}
 	}
 }
 
-// WithConveyancePreference is a direct alias for WithRegistrationConveyancePreference.
+// WithPublicKeyCredentialHints adjusts the non-default hints for credential types to select during registration.
 //
-// Deprecated: Use WithRegistrationConveyancePreference in favor of WithConveyancePreference as this function will be
-// likely be removed in a future release.
-func WithConveyancePreference(preference protocol.ConveyancePreference) RegistrationOption {
-	return WithRegistrationConveyancePreference(preference)
+// WebAuthn Level 3.
+func WithPublicKeyCredentialHints(hints []protocol.PublicKeyCredentialHints) RegistrationOption {
+	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
+		cco.Hints = hints
+	}
 }
 
-// WithRegistrationConveyancePreference adjusts the non-default parameters regarding whether the authenticator should attest to the
+// WithConveyancePreference adjusts the non-default parameters regarding whether the authenticator should attest to the
 // credential.
-func WithRegistrationConveyancePreference(preference protocol.ConveyancePreference) RegistrationOption {
+func WithConveyancePreference(preference protocol.ConveyancePreference) RegistrationOption {
 	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
 		cco.Attestation = preference
 	}
 }
 
-// WithRegistrationAttestationFormats adjusts the preferred attestation formats for this credential creation in most to
-// least preferable. Advisory only.
-func WithRegistrationAttestationFormats(formats ...protocol.AttestationFormat) RegistrationOption {
+// WithAttestationFormats adjusts the non-default formats for credential types to select during registration.
+//
+// WebAuthn Level 3.
+func WithAttestationFormats(formats []protocol.AttestationFormat) RegistrationOption {
 	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
 		cco.AttestationFormats = formats
 	}
@@ -145,13 +162,6 @@ func WithExtensions(extension protocol.AuthenticationExtensions) RegistrationOpt
 	}
 }
 
-// WithCredentialParameters adjusts the credential parameters in the registration options.
-func WithCredentialParameters(credentialParams []protocol.CredentialParameter) RegistrationOption {
-	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
-		cco.Parameters = credentialParams
-	}
-}
-
 // WithAppIdExcludeExtension automatically includes the specified appid if the CredentialExcludeList contains a credential
 // with the type `fido-u2f`.
 func WithAppIdExcludeExtension(appid string) RegistrationOption {
@@ -159,7 +169,7 @@ func WithAppIdExcludeExtension(appid string) RegistrationOption {
 		for _, credential := range cco.CredentialExcludeList {
 			if credential.AttestationType == protocol.CredentialTypeFIDOU2F {
 				if cco.Extensions == nil {
-					cco.Extensions = map[string]interface{}{}
+					cco.Extensions = map[string]any{}
 				}
 
 				cco.Extensions[protocol.ExtensionAppIDExclude] = appid
@@ -168,20 +178,6 @@ func WithAppIdExcludeExtension(appid string) RegistrationOption {
 	}
 }
 
-// WithResidentKeyRequirement sets both the resident key and require resident key protocol options.
-func WithResidentKeyRequirement(requirement protocol.ResidentKeyRequirement) RegistrationOption {
-	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
-		cco.AuthenticatorSelection.ResidentKey = requirement
-
-		switch requirement {
-		case protocol.ResidentKeyRequirementRequired:
-			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyRequired()
-		default:
-			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyNotRequired()
-		}
-	}
-}
-
 // FinishRegistration takes the response from the authenticator and client and verify the credential against the user's
 // credentials and session data.
 func (webauthn *WebAuthn) FinishRegistration(user User, session SessionData, response *http.Request) (*Credential, error) {


Test patch:
diff --git a/protocol/assertion_test.go b/protocol/assertion_test.go
index ab62239..2731850 100644
--- a/protocol/assertion_test.go
+++ b/protocol/assertion_test.go
@@ -47,7 +47,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
 						Type: "public-key",
 					},
 					RawID: byteID,
-					ClientExtensionResults: map[string]interface{}{
+					ClientExtensionResults: map[string]any{
 						"appID": "example.com",
 					},
 				},
@@ -78,7 +78,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
 							ID:   "AI7D5q2P0LS-Fal9ZT7CHM2N5BLbUunF92T8b6iYC199bO2kagSuU05-5dZGqb1SP0A0lyTWng",
 						},
 						RawID: byteID,
-						ClientExtensionResults: map[string]interface{}{
+						ClientExtensionResults: map[string]any{
 							"appID": "example.com",
 						},
 					},
@@ -135,7 +135,7 @@ func TestParseCredentialRequestResponse(t *testing.T) {
 			assert.Equal(t, tc.expected.Response.CollectedClientData, actual.Response.CollectedClientData)
 
 			var (
-				pkExpected, pkActual interface{}
+				pkExpected, pkActual any
 			)
 
 			assert.NoError(t, webauthncbor.Unmarshal(tc.expected.Response.AuthenticatorData.AttData.CredentialPublicKey, &pkExpected))
diff --git a/protocol/attestation.go b/protocol/attestation.go
index 8376141..cdad1bf 100644
--- a/protocol/attestation.go
+++ b/protocol/attestation.go
@@ -65,10 +65,10 @@ type AttestationObject struct {
 	// The format of the Attestation data.
 	Format string `json:"fmt"`
 	// The attestation statement data sent back if attestation is requested.
-	AttStatement map[string]interface{} `json:"attStmt,omitempty"`
+	AttStatement map[string]any `json:"attStmt,omitempty"`
 }
 
-type attestationFormatValidationHandler func(AttestationObject, []byte) (string, []interface{}, error)
+type attestationFormatValidationHandler func(AttestationObject, []byte) (string, []any, error)
 
 var attestationRegistry = make(map[AttestationFormat]attestationFormatValidationHandler)
 
diff --git a/protocol/attestation_androidkey.go b/protocol/attestation_androidkey.go
index 0b733d3..b8551ca 100644
--- a/protocol/attestation_androidkey.go
+++ b/protocol/attestation_androidkey.go
@@ -29,7 +29,7 @@ func init() {
 //	  }
 //
 // Specification: §8.4. Android Key Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-android-key-attestation)
-func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	// Given the verification procedure inputs attStmt, authenticatorData and clientDataHash, the verification procedure is as follows:
 	// §8.4.1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract
 	// the contained fields.
@@ -48,7 +48,7 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte) (strin
 	}
 
 	// If x5c is not present, return an error
-	x5c, x509present := att.AttStatement["x5c"].([]interface{})
+	x5c, x509present := att.AttStatement["x5c"].([]any)
 	if !x509present {
 		// Handle Basic Attestation steps for the x509 Certificate
 		return "", nil, ErrAttestationFormat.WithDetails("Error retrieving x5c value")
@@ -163,19 +163,19 @@ type authorizationList struct {
 	Padding                     []int       `asn1:"tag:6,explicit,set,optional"`
 	EcCurve                     int         `asn1:"tag:10,explicit,optional"`
 	RsaPublicExponent           int         `asn1:"tag:200,explicit,optional"`
-	RollbackResistance          interface{} `asn1:"tag:303,explicit,optional"`
+	RollbackResistance          any         `asn1:"tag:303,explicit,optional"`
 	ActiveDateTime              int         `asn1:"tag:400,explicit,optional"`
 	OriginationExpireDateTime   int         `asn1:"tag:401,explicit,optional"`
 	UsageExpireDateTime         int         `asn1:"tag:402,explicit,optional"`
-	NoAuthRequired              interface{} `asn1:"tag:503,explicit,optional"`
+	NoAuthRequired              any         `asn1:"tag:503,explicit,optional"`
 	UserAuthType                int         `asn1:"tag:504,explicit,optional"`
 	AuthTimeout                 int         `asn1:"tag:505,explicit,optional"`
-	AllowWhileOnBody            interface{} `asn1:"tag:506,explicit,optional"`
-	TrustedUserPresenceRequired interface{} `asn1:"tag:507,explicit,optional"`
-	TrustedConfirmationRequired interface{} `asn1:"tag:508,explicit,optional"`
-	UnlockedDeviceRequired      interface{} `asn1:"tag:509,explicit,optional"`
-	AllApplications             interface{} `asn1:"tag:600,explicit,optional"`
-	ApplicationID               interface{} `asn1:"tag:601,explicit,optional"`
+	AllowWhileOnBody            any         `asn1:"tag:506,explicit,optional"`
+	TrustedUserPresenceRequired any         `asn1:"tag:507,explicit,optional"`
+	TrustedConfirmationRequired any         `asn1:"tag:508,explicit,optional"`
+	UnlockedDeviceRequired      any         `asn1:"tag:509,explicit,optional"`
+	AllApplications             any         `asn1:"tag:600,explicit,optional"`
+	ApplicationID               any         `asn1:"tag:601,explicit,optional"`
 	CreationDateTime            int         `asn1:"tag:701,explicit,optional"`
 	Origin                      int         `asn1:"tag:702,explicit,optional"`
 	RootOfTrust                 rootOfTrust `asn1:"tag:704,explicit,optional"`
diff --git a/protocol/attestation_androidkey_test.go b/protocol/attestation_androidkey_test.go
index cb35205..73266c8 100644
--- a/protocol/attestation_androidkey_test.go
+++ b/protocol/attestation_androidkey_test.go
@@ -22,7 +22,7 @@ func TestVerifyAndroidKeyFormat(t *testing.T) {
 		name    string
 		args    args
 		want    string
-		want1   []interface{}
+		want1   []any
 		wantErr bool
 	}{
 		{
diff --git a/protocol/attestation_apple.go b/protocol/attestation_apple.go
index 589e5d0..0e7be7c 100644
--- a/protocol/attestation_apple.go
+++ b/protocol/attestation_apple.go
@@ -31,12 +31,12 @@ func init() {
 //	  }
 //
 // Specification: §8.8. Apple Anonymous Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-apple-anonymous-attestation)
-func verifyAppleFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifyAppleFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	// Step 1. Verify that attStmt is valid CBOR conforming to the syntax defined
 	// above and perform CBOR decoding on it to extract the contained fields.
 
 	// If x5c is not present, return an error
-	x5c, x509present := att.AttStatement["x5c"].([]interface{})
+	x5c, x509present := att.AttStatement["x5c"].([]any)
 	if !x509present {
 		// Handle Basic Attestation steps for the x509 Certificate
 		return "", nil, ErrAttestationFormat.WithDetails("Error retrieving x5c value")
diff --git a/protocol/attestation_apple_test.go b/protocol/attestation_apple_test.go
index bfb6ee6..fc42ff6 100644
--- a/protocol/attestation_apple_test.go
+++ b/protocol/attestation_apple_test.go
@@ -20,7 +20,7 @@ func Test_verifyAppleFormat(t *testing.T) {
 		name    string
 		args    args
 		want    string
-		want1   []interface{}
+		want1   []any
 		wantErr bool
 	}{
 		{
@@ -42,12 +42,10 @@ func Test_verifyAppleFormat(t *testing.T) {
 				t.Errorf("verifyAppleFormat() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
+
 			if got != tt.want {
 				t.Errorf("verifyAppleFormat() got = %v, want %v", got, tt.want)
 			}
-			//if !reflect.DeepEqual(got1, tt.want1) {
-			//	t.Errorf("verifyPackedFormat() got1 = %v, want %v", got1, tt.want1)
-			//}
 		})
 	}
 }
diff --git a/protocol/attestation_packed.go b/protocol/attestation_packed.go
index 8874d80..c33a904 100644
--- a/protocol/attestation_packed.go
+++ b/protocol/attestation_packed.go
@@ -34,7 +34,7 @@ func init() {
 //	 }
 //
 // Specification: §8.2. Packed Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-packed-attestation)
-func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	// Step 1. Verify that attStmt is valid CBOR conforming to the syntax defined
 	// above and perform CBOR decoding on it to extract the contained fields.
 
@@ -53,7 +53,7 @@ func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, [
 	}
 
 	// Step 2. If x5c is present, this indicates that the attestation type is not ECDAA.
-	x5c, x509present := att.AttStatement["x5c"].([]interface{})
+	x5c, x509present := att.AttStatement["x5c"].([]any)
 	if x509present {
 		// Handle Basic Attestation steps for the x509 Certificate
 		return handleBasicAttestation(sig, clientDataHash, att.RawAuthData, att.AuthData.AttData.AAGUID, alg, x5c)
@@ -72,7 +72,7 @@ func verifyPackedFormat(att AttestationObject, clientDataHash []byte) (string, [
 }
 
 // Handle the attestation steps laid out in
-func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte, alg int64, x5c []interface{}) (string, []interface{}, error) {
+func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte, alg int64, x5c []any) (string, []any, error) {
 	// Step 2.1. Verify that sig is a valid signature over the concatenation of authenticatorData
 	// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
 	for _, c := range x5c {
@@ -199,11 +199,11 @@ func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte,
 	return string(metadata.BasicFull), x5c, nil
 }
 
-func handleECDAAAttestation(signature, clientDataHash, ecdaaKeyID []byte) (string, []interface{}, error) {
+func handleECDAAAttestation(signature, clientDataHash, ecdaaKeyID []byte) (string, []any, error) {
 	return "Packed (ECDAA)", nil, ErrNotSpecImplemented
 }
 
-func handleSelfAttestation(alg int64, pubKey, authData, clientDataHash, signature []byte) (string, []interface{}, error) {
+func handleSelfAttestation(alg int64, pubKey, authData, clientDataHash, signature []byte) (string, []any, error) {
 	// §4.1 Validate that alg matches the algorithm of the credentialPublicKey in authenticatorData.
 
 	// §4.2 Verify that sig is a valid signature over the concatenation of authenticatorData and
diff --git a/protocol/attestation_packed_test.go b/protocol/attestation_packed_test.go
index ea0552f..66e8b6b 100644
--- a/protocol/attestation_packed_test.go
+++ b/protocol/attestation_packed_test.go
@@ -24,7 +24,7 @@ func Test_verifyPackedFormat(t *testing.T) {
 		name    string
 		args    args
 		want    string
-		want1   []interface{}
+		want1   []any
 		wantErr bool
 	}{
 		{
diff --git a/protocol/attestation_safetynet.go b/protocol/attestation_safetynet.go
index f88e804..954541d 100644
--- a/protocol/attestation_safetynet.go
+++ b/protocol/attestation_safetynet.go
@@ -19,13 +19,13 @@ func init() {
 }
 
 type SafetyNetResponse struct {
-	Nonce                      string        `json:"nonce"`
-	TimestampMs                int64         `json:"timestampMs"`
-	ApkPackageName             string        `json:"apkPackageName"`
-	ApkDigestSha256            string        `json:"apkDigestSha256"`
-	CtsProfileMatch            bool          `json:"ctsProfileMatch"`
-	ApkCertificateDigestSha256 []interface{} `json:"apkCertificateDigestSha256"`
-	BasicIntegrity             bool          `json:"basicIntegrity"`
+	Nonce                      string `json:"nonce"`
+	TimestampMs                int64  `json:"timestampMs"`
+	ApkPackageName             string `json:"apkPackageName"`
+	ApkDigestSha256            string `json:"apkDigestSha256"`
+	CtsProfileMatch            bool   `json:"ctsProfileMatch"`
+	ApkCertificateDigestSha256 []any  `json:"apkCertificateDigestSha256"`
+	BasicIntegrity             bool   `json:"basicIntegrity"`
 }
 
 // Thanks to @koesie10 and @herrjemand for outlining how to support this type really well
@@ -40,7 +40,7 @@ type SafetyNetResponse struct {
 // authenticators SHOULD make use of the Android Key Attestation when available, even if the SafetyNet API is also present.
 //
 // Specification: §8.5. Android SafetyNet Attestation Statement Format (https://www.w3.org/TR/webauthn/#sctn-android-safetynet-attestation)
-func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	// The syntax of an Android Attestation statement is defined as follows:
 	//     $$attStmtType //= (
 	//                           fmt: "android-safetynet",
@@ -73,8 +73,8 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string
 		return "", nil, ErrAttestationFormat.WithDetails("Unable to find the SafetyNet response")
 	}
 
-	token, err := jwt.Parse(string(response), func(token *jwt.Token) (interface{}, error) {
-		chain := token.Header["x5c"].([]interface{})
+	token, err := jwt.Parse(string(response), func(token *jwt.Token) (any, error) {
+		chain := token.Header["x5c"].([]any)
 
 		o := make([]byte, base64.StdEncoding.DecodedLen(len(chain[0].(string))))
 
@@ -108,7 +108,7 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte) (string
 	}
 
 	// §8.5.4 Let attestationCert be the attestation certificate (https://www.w3.org/TR/webauthn/#attestation-certificate)
-	certChain := token.Header["x5c"].([]interface{})
+	certChain := token.Header["x5c"].([]any)
 	l := make([]byte, base64.StdEncoding.DecodedLen(len(certChain[0].(string))))
 
 	n, err := base64.StdEncoding.Decode(l, []byte(certChain[0].(string)))
diff --git a/protocol/attestation_safetynet_test.go b/protocol/attestation_safetynet_test.go
index 379133a..d40ba30 100644
--- a/protocol/attestation_safetynet_test.go
+++ b/protocol/attestation_safetynet_test.go
@@ -21,7 +21,7 @@ func Test_verifySafetyNetFormat(t *testing.T) {
 		name    string
 		args    args
 		want    string
-		want1   []interface{}
+		want1   []any
 		wantErr bool
 	}{
 		{
@@ -43,9 +43,11 @@ func Test_verifySafetyNetFormat(t *testing.T) {
 				t.Errorf("verifySafetyNetFormat() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
+
 			if got != tt.want {
 				t.Errorf("verifySafetyNetFormat() got = %v, want %v", got, tt.want)
 			}
+
 			if !reflect.DeepEqual(got1, tt.want1) {
 				t.Errorf("verifySafetyNetFormat() got1 = %v, want %v", got1, tt.want1)
 			}
diff --git a/protocol/attestation_test.go b/protocol/attestation_test.go
index 60a6bf6..15831c4 100644
--- a/protocol/attestation_test.go
+++ b/protocol/attestation_test.go
@@ -20,10 +20,13 @@ func TestAttestationVerify(t *testing.T) {
 			if err := json.Unmarshal([]byte(testAttestationOptions[i]), &options); err != nil {
 				t.Fatal(err)
 			}
+
 			ccr := CredentialCreationResponse{}
+
 			if err := json.Unmarshal([]byte(testAttestationResponses[i]), &ccr); err != nil {
 				t.Fatal(err)
 			}
+
 			var pcc ParsedCredentialCreationData
 			pcc.ID, pcc.RawID, pcc.Type, pcc.ClientExtensionResults = ccr.ID, ccr.RawID, ccr.Type, ccr.ClientExtensionResults
 			pcc.Raw = ccr
diff --git a/protocol/attestation_tpm.go b/protocol/attestation_tpm.go
index c1fbe39..e86881c 100644
--- a/protocol/attestation_tpm.go
+++ b/protocol/attestation_tpm.go
@@ -19,7 +19,7 @@ func init() {
 	RegisterAttestationFormat(AttestationFormatTPM, verifyTPMFormat)
 }
 
-func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	// Given the verification procedure inputs attStmt, authenticatorData
 	// and clientDataHash, the verification procedure is as follows
 
@@ -42,7 +42,7 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte) (string, []in
 
 	coseAlg := webauthncose.COSEAlgorithmIdentifier(alg)
 
-	x5c, x509present := att.AttStatement["x5c"].([]interface{})
+	x5c, x509present := att.AttStatement["x5c"].([]any)
 	if !x509present {
 		// Handle Basic Attestation steps for the x509 Certificate
 		return "", nil, ErrNotImplemented
diff --git a/protocol/attestation_tpm_test.go b/protocol/attestation_tpm_test.go
index f467f37..412a74f 100644
--- a/protocol/attestation_tpm_test.go
+++ b/protocol/attestation_tpm_test.go
@@ -31,6 +31,7 @@ func TestTPMAttestationVerificationSuccess(t *testing.T) {
 			if err != nil {
 				t.Fatalf("Not valid: %+v", err)
 			}
+
 			assert.Equal(t, "attca", attestationType)
 		})
 	}
@@ -82,37 +83,37 @@ func TestTPMAttestationVerificationFailAttStatement(t *testing.T) {
 		},
 		{
 			"TPM Negative Test AttStatement Ver not 2.0",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "foo.bar"}},
+			AttestationObject{AttStatement: map[string]any{"ver": "foo.bar"}},
 			"WebAuthn only supports TPM 2.0 currently",
 		},
 		{
 			"TPM Negative Test AttStatement Alg not present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0"}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0"}},
 			"Error retrieving alg value",
 		},
 		{
 			"TPM Negative Test AttStatement x5c not present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0)}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0)}},
 			ErrNotImplemented.Details,
 		},
 		{
 			"TPM Negative Test AttStatement ecdaaKeyId present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "ecdaaKeyId": []byte{}}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0), "x5c": []any{}, "ecdaaKeyId": []byte{}}},
 			ErrNotImplemented.Details,
 		},
 		{
 			"TPM Negative Test AttStatement sig not present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0), "x5c": []any{}}},
 			"Error retrieving sig value",
 		},
 		{
 			"TPM Negative Test AttStatement certInfo not present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0), "x5c": []any{}, "sig": []byte{}}},
 			"Error retrieving certInfo value",
 		},
 		{
 			"TPM Negative Test AttStatement pubArea not present",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}, "certInfo": []byte{}}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0), "x5c": []any{}, "sig": []byte{}, "certInfo": []byte{}}},
 			"Error retrieving pubArea value",
 		},
 		{
@@ -122,7 +123,7 @@ func TestTPMAttestationVerificationFailAttStatement(t *testing.T) {
 		},
 		{
 			"TPM Negative Test Unsupported Public Key Type",
-			AttestationObject{AttStatement: map[string]interface{}{"ver": "2.0", "alg": int64(0), "x5c": []interface{}{}, "sig": []byte{}, "certInfo": []byte{}, "pubArea": makeDefaultRSAPublicBytes()}, AuthData: AuthenticatorData{AttData: AttestedCredentialData{CredentialPublicKey: []byte{}}}},
+			AttestationObject{AttStatement: map[string]any{"ver": "2.0", "alg": int64(0), "x5c": []any{}, "sig": []byte{}, "certInfo": []byte{}, "pubArea": makeDefaultRSAPublicBytes()}, AuthData: AuthenticatorData{AttData: AttestedCredentialData{CredentialPublicKey: []byte{}}}},
 			"Unsupported Public Key Type",
 		},
 	}
@@ -169,7 +170,7 @@ var (
 	}
 )
 
-var defaultAttStatement = map[string]interface{}{"ver": "2.0", "alg": int64(-257), "x5c": []interface{}{}, "sig": []byte{}, "certInfo": []byte{}, "pubArea": []byte{}}
+var defaultAttStatement = map[string]any{"ver": "2.0", "alg": int64(-257), "x5c": []any{}, "sig": []byte{}, "certInfo": []byte{}, "pubArea": []byte{}}
 
 type CredentialPublicKey struct {
 	KeyType   int64  `cbor:"1,keyasint" json:"kty"`
@@ -332,7 +333,7 @@ func TestTPMAttestationVerificationFailPubArea(t *testing.T) {
 		},
 	}
 	for _, tt := range tests {
-		attStmt := make(map[string]interface{}, len(defaultAttStatement))
+		attStmt := make(map[string]any, len(defaultAttStatement))
 		for id, v := range defaultAttStatement {
 			attStmt[id] = v
 		}
@@ -373,7 +374,7 @@ func TestTPMAttestationVerificationFailPubArea(t *testing.T) {
 }
 
 func TestTPMAttestationVerificationFailCertInfo(t *testing.T) {
-	attStmt := make(map[string]interface{}, len(defaultAttStatement))
+	attStmt := make(map[string]any, len(defaultAttStatement))
 
 	for id, v := range defaultAttStatement {
 		attStmt[id] = v
@@ -487,7 +488,7 @@ var (
 )
 
 func TestTPMAttestationVerificationFailX5c(t *testing.T) {
-	attStmt := make(map[string]interface{}, len(defaultAttStatement))
+	attStmt := make(map[string]any, len(defaultAttStatement))
 
 	for id, v := range defaultAttStatement {
 		attStmt[id] = v
@@ -543,8 +544,8 @@ func TestTPMAttestationVerificationFailX5c(t *testing.T) {
 	}
 	attStmt["certInfo"], _ = certInfo.Encode()
 
-	makeX5c := func(b []byte) []interface{} {
-		q := make([]interface{}, 1)
+	makeX5c := func(b []byte) []any {
+		q := make([]any, 1)
 		q[0] = b
 
 		return q
@@ -552,12 +553,12 @@ func TestTPMAttestationVerificationFailX5c(t *testing.T) {
 
 	tests := []struct {
 		name    string
-		x5c     []interface{}
+		x5c     []any
 		wantErr string
 	}{
 		{
 			"TPM Negative Test x5c empty",
-			make([]interface{}, 1),
+			make([]any, 1),
 			"Error getting certificate from x5c cert chain",
 		},
 		{
diff --git a/protocol/attestation_u2f.go b/protocol/attestation_u2f.go
index 4b84962..0689d42 100644
--- a/protocol/attestation_u2f.go
+++ b/protocol/attestation_u2f.go
@@ -12,11 +12,11 @@ import (
 )
 
 func init() {
-	RegisterAttestationFormat(AttestationFormatFIDOU2F, verifyU2FFormat)
+	RegisterAttestationFormat(AttestationFormatFIDOUniversalSecondFactor, verifyU2FFormat)
 }
 
 // verifyU2FFormat - Follows verification steps set out by https://www.w3.org/TR/webauthn/#fido-u2f-attestation
-func verifyU2FFormat(att AttestationObject, clientDataHash []byte) (string, []interface{}, error) {
+func verifyU2FFormat(att AttestationObject, clientDataHash []byte) (string, []any, error) {
 	if !bytes.Equal(att.AuthData.AttData.AAGUID, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) {
 		return "", nil, ErrUnsupportedAlgorithm.WithDetails("U2F attestation format AAGUID not set to 0x00")
 	}
@@ -40,7 +40,7 @@ func verifyU2FFormat(att AttestationObject, clientDataHash []byte) (string, []in
 	// }
 
 	// Check for "x5c" which is a single element array containing the attestation certificate in X.509 format.
-	x5c, present := att.AttStatement["x5c"].([]interface{})
+	x5c, present := att.AttStatement["x5c"].([]any)
 	if !present {
 		return "", nil, ErrAttestationFormat.WithDetails("Missing properly formatted x5c data")
 	}
diff --git a/protocol/attestation_u2f_test.go b/protocol/attestation_u2f_test.go
index 813ec9e..04dc618 100644
--- a/protocol/attestation_u2f_test.go
+++ b/protocol/attestation_u2f_test.go
@@ -20,7 +20,7 @@ func TestVerifyU2FFormat(t *testing.T) {
 		name    string
 		args    args
 		want    string
-		want1   []interface{}
+		want1   []any
 		wantErr bool
 	}{
 		{
@@ -42,12 +42,10 @@ func TestVerifyU2FFormat(t *testing.T) {
 				t.Errorf("verifyU2FFormat() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
+
 			if got != tt.want {
 				t.Errorf("verifyU2FFormat() got = %v, want %v", got, tt.want)
 			}
-			//if !reflect.DeepEqual(got1, tt.want1) {
-			//	t.Errorf("verifyU2FFormat() got1 = %v, want %v", got1, tt.want1)
-			//}
 		})
 	}
 }
diff --git a/protocol/challenge_test.go b/protocol/challenge_test.go
index f2e5d8d..6118d4f 100644
--- a/protocol/challenge_test.go
+++ b/protocol/challenge_test.go
@@ -26,7 +26,9 @@ func TestCreateChallenge(t *testing.T) {
 				t.Errorf("CreateChallenge() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
+
 			tt.want = got
+
 			if !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("CreateChallenge() = %v, want %v", got, tt.want)
 			}
diff --git a/protocol/credential_test.go b/protocol/credential_test.go
index 201c45f..70f1ba0 100644
--- a/protocol/credential_test.go
+++ b/protocol/credential_test.go
@@ -259,7 +259,7 @@ func TestParseCredentialCreationResponse(t *testing.T) {
 			assert.Equal(t, tc.expected.Response.AttestationObject.Format, actual.Response.AttestationObject.Format)
 
 			// Unmarshall CredentialPublicKey
-			var pkExpected, pkActual interface{}
+			var pkExpected, pkActual any
 
 			pkBytesExpected := tc.expected.Response.AttestationObject.AuthData.AttData.CredentialPublicKey
 			assert.NoError(t, webauthncbor.Unmarshal(pkBytesExpected, &pkExpected))