Repository: go-webauthn/webauthn
Pull request #357 (2024-12-20 21:13:53)

build(deps): bump github/codeql-action from 3.27.9 to 3.28.0
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.9 to 3.28.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/github/codeql-action/releases">github/codeql-action's releases</a>.</em></p>
<blockquote>
<h2>v3.28.0</h2>
<h1>CodeQL Action Changelog</h1>
<p>See the <a href="https://github.com/github/codeql-action/releases">releases page</a> for the relevant changes to the CodeQL CLI and language packs.</p>
<p>Note that the only difference between <code>v2</code> and <code>v3</code> of the CodeQL Action is the node version they support, with <code>v3</code> running on node 20 while we continue to release <code>v2</code> to support running on node 16. For example <code>3.22.11</code> was the first <code>v3</code> release and is functionally identical to <code>2.22.11</code>. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.</p>
<h2>3.28.0 - 20 Dec 2024</h2>
<ul>
<li>Bump the minimum CodeQL bundle version to 2.15.5. <a href="https://redirect.github.com/github/codeql-action/pull/2655">#2655</a></li>
<li>Don't fail in the unusual case that a file is on the search path. <a href="https://redirect.github.com/github/codeql-action/pull/2660">#2660</a>.</li>
</ul>
<p>See the full <a href="https://github.com/github/codeql-action/blob/v3.28.0/CHANGELOG.md">CHANGELOG.md</a> for more information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/github/codeql-action/blob/main/CHANGELOG.md">github/codeql-action's changelog</a>.</em></p>
<blockquote>
<h1>CodeQL Action Changelog</h1>
<p>See the <a href="https://github.com/github/codeql-action/releases">releases page</a> for the relevant changes to the CodeQL CLI and language packs.</p>
<p>Note that the only difference between <code>v2</code> and <code>v3</code> of the CodeQL Action is the node version they support, with <code>v3</code> running on node 20 while we continue to release <code>v2</code> to support running on node 16. For example <code>3.22.11</code> was the first <code>v3</code> release and is functionally identical to <code>2.22.11</code>. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.</p>
<h2>[UNRELEASED]</h2>
<p>No user facing changes.</p>
<h2>3.28.0 - 20 Dec 2024</h2>
<ul>
<li>Bump the minimum CodeQL bundle version to 2.15.5. <a href="https://redirect.github.com/github/codeql-action/pull/2655">#2655</a></li>
<li>Don't fail in the unusual case that a file is on the search path. <a href="https://redirect.github.com/github/codeql-action/pull/2660">#2660</a>.</li>
</ul>
<h2>3.27.9 - 12 Dec 2024</h2>
<p>No user facing changes.</p>
<h2>3.27.8 - 12 Dec 2024</h2>
<ul>
<li>Fixed an issue where streaming the download and extraction of the CodeQL bundle did not respect proxy settings. <a href="https://redirect.github.com/github/codeql-action/pull/2624">#2624</a></li>
</ul>
<h2>3.27.7 - 10 Dec 2024</h2>
<ul>
<li>We are rolling out a change in December 2024 that will extract the CodeQL bundle directly to the toolcache to improve performance. <a href="https://redirect.github.com/github/codeql-action/pull/2631">#2631</a></li>
<li>Update default CodeQL bundle version to 2.20.0. <a href="https://redirect.github.com/github/codeql-action/pull/2636">#2636</a></li>
</ul>
<h2>3.27.6 - 03 Dec 2024</h2>
<ul>
<li>Update default CodeQL bundle version to 2.19.4. <a href="https://redirect.github.com/github/codeql-action/pull/2626">#2626</a></li>
</ul>
<h2>3.27.5 - 19 Nov 2024</h2>
<p>No user facing changes.</p>
<h2>3.27.4 - 14 Nov 2024</h2>
<p>No user facing changes.</p>
<h2>3.27.3 - 12 Nov 2024</h2>
<p>No user facing changes.</p>
<h2>3.27.2 - 12 Nov 2024</h2>
<ul>
<li>Fixed an issue where setting up the CodeQL tools would sometimes fail with the message &quot;Invalid value 'undefined' for header 'authorization'&quot;. <a href="https://redirect.github.com/github/codeql-action/pull/2590">#2590</a></li>
</ul>
<h2>3.27.1 - 08 Nov 2024</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/github/codeql-action/commit/48ab28a6f5dbc2a99bf1e0131198dd8f1df78169"><code>48ab28a</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2662">#2662</a> from github/update-v3.28.0-d01b25e64</li>
<li><a href="https://github.com/github/codeql-action/commit/4946b765dee77596c258fee1bc1f8c5a86cb45a2"><code>4946b76</code></a> Update changelog for v3.28.0</li>
<li><a href="https://github.com/github/codeql-action/commit/d01b25e645295d11d0fbb084a8c5e7546b1e508b"><code>d01b25e</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2660">#2660</a> from github/henrymercer/fix-error-file-on-path</li>
<li><a href="https://github.com/github/codeql-action/commit/7d6d36ce5f3654bed1de7eb8600c22e3a12910a4"><code>7d6d36c</code></a> Add changelog note</li>
<li><a href="https://github.com/github/codeql-action/commit/b58f4471c8015d1581e4112c33316a6b04ace55f"><code>b58f447</code></a> Use <code>@actions/io</code> to locate binaries</li>
<li><a href="https://github.com/github/codeql-action/commit/64cc90bcd4b0a6919309f7882f920e60de2aef1c"><code>64cc90b</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2653">#2653</a> from github/dependabot/npm_and_yarn/npm-61c837125e</li>
<li><a href="https://github.com/github/codeql-action/commit/d8f8eca6c5448bea9fbfb5a16bcdc9ecc5c4305d"><code>d8f8eca</code></a> Merge branch 'main' into dependabot/npm_and_yarn/npm-61c837125e</li>
<li><a href="https://github.com/github/codeql-action/commit/562042d742d834fa44c4fd29c197a62d76568c60"><code>562042d</code></a> Merge pull request <a href="https://redirect.github.com/github/codeql-action/issues/2655">#2655</a> from github/aeisenberg/deprecate-2.14</li>
<li><a href="https://github.com/github/codeql-action/commit/beed6ff2e94aa8b13c7e8c50b7cb642f82855e37"><code>beed6ff</code></a> Change codeql version used in test</li>
<li><a href="https://github.com/github/codeql-action/commit/5f0a4d3e67a0f3cb983e0e664a6408ccf8866999"><code>5f0a4d3</code></a> Bump the minimum supported version of CodeQL to 2.15.5</li>
<li>Additional commits viewable in <a href="https://github.com/github/codeql-action/compare/df409f7d9260372bd5f19e5b04e83cb3c43714ae...48ab28a6f5dbc2a99bf1e0131198dd8f1df78169">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.27.9&new-version=3.28.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

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

> [!IMPORTANT]
> ## Review skipped
> 
> Bot user detected.
> 
> To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->

<!-- tips_start -->

---



<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>, please review it.`
  - `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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@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.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

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 using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR. ([Beta](https://docs.coderabbit.ai/early-access#beta))
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration 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://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) 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/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index bdb5c9a..50497a9 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -50,7 +50,7 @@ jobs:
 
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
+        uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
         with:
           languages: ${{ matrix.language }}
           # If you wish to specify custom queries, you can do so here or in a config file.
@@ -60,7 +60,7 @@ jobs:
       # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
       # If this step fails, then you should remove it and run the build manually (see below)
       - name: Autobuild
-        uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
+        uses: github/codeql-action/autobuild@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
 
       # ℹ️ Command-line programs to run using the OS shell.
       # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -73,6 +73,6 @@ jobs:
       #   ./location_of_script_within_repo/buildscript.sh
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
+        uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
         with:
           category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 9df98fa..abdd338 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -71,6 +71,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
+        uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
         with:
           sarif_file: results.sarif
diff --git a/go.mod b/go.mod
index 9537e90..6f0b007 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/fxamacker/cbor/v2 v2.7.0
 	github.com/go-webauthn/x v0.1.15
 	github.com/golang-jwt/jwt/v5 v5.2.1
-	github.com/google/go-tpm v0.9.2
+	github.com/google/go-tpm v0.9.3
 	github.com/google/uuid v1.6.0
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/stretchr/testify v1.10.0
diff --git a/go.sum b/go.sum
index 396cbdc..b63583c 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU=
 github.com/go-webauthn/x v0.1.15/go.mod h1:pf7VI23raFLHPO9VVIs9/u1etqwAOP0S2KoHGL6WbZ8=
 github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
 github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/google/go-tpm v0.9.2 h1:Gh8CMnMm06b09DmcsuY9fI3oF69188lGXCpiT/a05T4=
-github.com/google/go-tpm v0.9.2/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
+github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
+github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 0a43e68..d6e9183 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -319,15 +319,26 @@ type Statement struct {
 	AuthenticatorGetInfo AuthenticatorGetInfo
 }
 
-func (s *Statement) Verifier() (opts x509.VerifyOptions) {
+func (s *Statement) Verifier(x5cis []*x509.Certificate) (opts x509.VerifyOptions) {
 	roots := x509.NewCertPool()
 
 	for _, root := range s.AttestationRootCertificates {
 		roots.AddCert(root)
 	}
 
+	var intermediates *x509.CertPool
+
+	if len(x5cis) > 0 {
+		intermediates = x509.NewCertPool()
+
+		for _, x5c := range x5cis {
+			intermediates.AddCert(x5c)
+		}
+	}
+
 	return x509.VerifyOptions{
-		Roots: roots,
+		Roots:         roots,
+		Intermediates: intermediates,
 	}
 }
 
diff --git a/webauthn/login.go b/webauthn/login.go
index 2132c8d..7122e97 100644
--- a/webauthn/login.go
+++ b/webauthn/login.go
@@ -56,14 +56,8 @@ func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protoco
 		return nil, nil, fmt.Errorf(errFmtConfigValidate, err)
 	}
 
-	challenge, err := protocol.CreateChallenge()
-	if err != nil {
-		return nil, nil, err
-	}
-
 	assertion = &protocol.CredentialAssertion{
 		Response: protocol.PublicKeyCredentialRequestOptions{
-			Challenge:          challenge,
 			RelyingPartyID:     webauthn.Config.RPID,
 			UserVerification:   webauthn.Config.AuthenticatorSelection.UserVerification,
 			AllowedCredentials: allowedCredentials,
@@ -74,6 +68,18 @@ func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protoco
 		opt(&assertion.Response)
 	}
 
+	if len(assertion.Response.Challenge) == 0 {
+		challenge, err := protocol.CreateChallenge()
+		if err != nil {
+			return nil, nil, err
+		}
+		assertion.Response.Challenge = challenge
+	}
+
+	if len(assertion.Response.Challenge) < 16 {
+		return nil, nil, fmt.Errorf("error generating assertion: the challenge must be at least 16 bytes")
+	}
+
 	if len(assertion.Response.RelyingPartyID) == 0 {
 		return nil, nil, fmt.Errorf("error generating assertion: the relying party id must be provided via the configuration or a functional option for a login")
 	} else if _, err = url.Parse(assertion.Response.RelyingPartyID); err != nil {
@@ -90,7 +96,7 @@ func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protoco
 	}
 
 	session = &SessionData{
-		Challenge:            challenge.String(),
+		Challenge:            assertion.Response.Challenge.String(),
 		RelyingPartyID:       assertion.Response.RelyingPartyID,
 		UserID:               userID,
 		AllowedCredentialIDs: assertion.Response.GetAllowedCredentialIDs(),
@@ -165,6 +171,18 @@ func WithLoginRelyingPartyID(id string) LoginOption {
 	}
 }
 
+// WithChallenge overrides the default random challenge with a user supplied value.
+// In order to prevent replay attacks, the challenges MUST contain enough entropy to make guessing them infeasible.
+// Challenges SHOULD therefore be at least 16 bytes long.
+// This function is EXPERIMENTAL and can be removed without warning. 
+//
+// Specification: §13.4.3. Cryptographic Challenges (https://www.w3.org/TR/webauthn/#sctn-cryptographic-challenges)
+func WithChallenge(challenge []byte) LoginOption {
+	return func(cco *protocol.PublicKeyCredentialRequestOptions) {
+		cco.Challenge = challenge
+	}
+}
+
 // FinishLogin takes the response from the client and validate it against the user credentials and stored session data.
 func (webauthn *WebAuthn) FinishLogin(user User, session SessionData, response *http.Request) (*Credential, error) {
 	parsedResponse, err := protocol.ParseCredentialRequestResponse(response)


Test patch:
diff --git a/protocol/attestation.go b/protocol/attestation.go
index a0701ed..be30e92 100644
--- a/protocol/attestation.go
+++ b/protocol/attestation.go
@@ -232,35 +232,56 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat
 		}
 
 		var (
-			x5c *x509.Certificate
-			raw []byte
-			ok  bool
+			x5c, parsed *x509.Certificate
+			x5cis       []*x509.Certificate
+			raw         []byte
+			ok          bool
 		)
 
 		if len(x5cs) == 0 {
 			return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo("The attestation had no certificates")
 		}
 
-		if raw, ok = x5cs[0].([]byte); !ok {
-			return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("The first certificate in the attestation was type '%T' but '[]byte' was expected", x5cs[0]))
-		}
+		for _, x5cAny := range x5cs {
+			if raw, ok = x5cAny.([]byte); !ok {
+				return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("The first certificate in the attestation was type '%T' but '[]byte' was expected", x5cs[0]))
+			}
+
+			if parsed, err = x509.ParseCertificate(raw); err != nil {
+				return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
+			}
 
-		if x5c, err = x509.ParseCertificate(raw); err != nil {
-			return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
+			if x5c == nil {
+				x5c = parsed
+			} else {
+				x5cis = append(x5cis, parsed)
+			}
 		}
 
 		if attestationType == string(metadata.AttCA) {
 			if err = tpmParseSANExtension(x5c); err != nil {
 				return err
 			}
+
+			if err = tpmRemoveEKU(x5c); err != nil {
+				return err
+			}
+
+			for _, parent := range x5cis {
+				if err = tpmRemoveEKU(parent); err != nil {
+					return err
+				}
+			}
 		}
 
-		if x5c.Subject.CommonName != x5c.Issuer.CommonName {
+		if x5c != nil && x5c.Subject.CommonName != x5c.Issuer.CommonName {
 			if !entry.MetadataStatement.AttestationTypes.HasBasicFull() {
 				return ErrInvalidAttestation.WithDetails("Unable to validate attestation statement signature during attestation validation: attestation with full attestation from authenticator that does not support full attestation")
 			}
 
-			if _, err = x5c.Verify(entry.MetadataStatement.Verifier()); err != nil {
+			verifier := entry.MetadataStatement.Verifier(x5cis)
+
+			if _, err = x5c.Verify(verifier); err != nil {
 				return ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: %v", err))
 			}
 		}
diff --git a/protocol/attestation_tpm.go b/protocol/attestation_tpm.go
index 347d70a..873e864 100644
--- a/protocol/attestation_tpm.go
+++ b/protocol/attestation_tpm.go
@@ -395,9 +395,40 @@ var (
 	oidExtensionSubjectAltName   = []int{2, 5, 29, 17}
 	oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
 	oidExtensionBasicConstraints = []int{2, 5, 29, 19}
+	oidKpPrivacyCA               = []int{1, 3, 6, 1, 4, 1, 311, 21, 36}
 )
 
 type tpmBasicConstraints struct {
 	IsCA       bool `asn1:"optional"`
 	MaxPathLen int  `asn1:"optional,default:-1"`
 }
+
+// Remove extension key usage to avoid ExtKeyUsage check failure.
+func tpmRemoveEKU(x5c *x509.Certificate) error {
+	var (
+		unknown []asn1.ObjectIdentifier
+		hasAiK  bool
+	)
+
+	for _, eku := range x5c.UnknownExtKeyUsage {
+		if eku.Equal(tcgKpAIKCertificate) {
+			hasAiK = true
+
+			continue
+		}
+
+		if eku.Equal(oidKpPrivacyCA) {
+			continue
+		}
+
+		unknown = append(unknown, eku)
+	}
+
+	if !hasAiK {
+		return ErrAttestationFormat.WithDetails("AIK certificate missing EKU")
+	}
+
+	x5c.UnknownExtKeyUsage = unknown
+
+	return nil
+}
diff --git a/webauthn/login_test.go b/webauthn/login_test.go
index a869cf9..098c913 100644
--- a/webauthn/login_test.go
+++ b/webauthn/login_test.go
@@ -32,11 +32,12 @@ func TestLogin_FinishLoginFailure(t *testing.T) {
 
 func TestWithLoginRelyingPartyID(t *testing.T) {
 	testCases := []struct {
-		name       string
-		have       *Config
-		opts       []LoginOption
-		expectedID string
-		err        string
+		name              string
+		have              *Config
+		opts              []LoginOption
+		expectedID        string
+		expectedChallenge []byte
+		err               string
 	}{
 		{
 			name: "OptionDefinedInConfig",
@@ -84,6 +85,27 @@ func TestWithLoginRelyingPartyID(t *testing.T) {
 			opts: nil,
 			err:  "error generating assertion: the relying party id must be provided via the configuration or a functional option for a login",
 		},
+		{
+			name: "TooShortWithChallengeOption",
+			have: &Config{
+				RPID:          "https://example.com",
+				RPOrigins:     []string{"https://example.com"},
+				RPDisplayName: "Test Display Name",
+			},
+			opts: []LoginOption{WithChallenge([]byte("1234567890"))},
+			err:  "error generating assertion: the challenge must be at least 16 bytes",
+		},
+		{
+			name: "WithChallengeOption",
+			have: &Config{
+				RPID:          "https://example.com",
+				RPOrigins:     []string{"https://example.com"},
+				RPDisplayName: "Test Display Name",
+			},
+			opts:              []LoginOption{WithChallenge([]byte("00000000000000000000000000000000"))},
+			expectedID:        "https://example.com",
+			expectedChallenge: []byte("00000000000000000000000000000000"),
+		},
 	}
 
 	for _, tc := range testCases {
@@ -104,6 +126,9 @@ func TestWithLoginRelyingPartyID(t *testing.T) {
 				assert.NoError(t, err)
 				require.NotNil(t, creation)
 				assert.Equal(t, tc.expectedID, creation.Response.RelyingPartyID)
+				if len(tc.expectedChallenge) > 0 {
+					assert.Equal(t, protocol.URLEncodedBase64(tc.expectedChallenge).String(), creation.Response.Challenge.String())
+				}
 			}
 		})
 	}