Skip to content

Add TPM-backed X.509 host certificate support and fix TPM-build client auth#1081

Open
aidangarske wants to merge 5 commits into
wolfSSL:masterfrom
aidangarske:tpm-x509-hostkey
Open

Add TPM-backed X.509 host certificate support and fix TPM-build client auth#1081
aidangarske wants to merge 5 commits into
wolfSSL:masterfrom
aidangarske:tpm-x509-hostkey

Conversation

@aidangarske

@aidangarske aidangarske commented Jul 2, 2026

Copy link
Copy Markdown
Member

Description

  • Add tpm backed x509 host cert support for tpm builds
  • minor change and fixes a TPM-build regression where clients could not perform password/keyboard-interactive authentication.
  • CI test for matrix x509 added

support for ZD 22094

Copilot AI review requested due to automatic review settings July 2, 2026 19:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds TPM-backed X.509 host-certificate support for wolfSSH servers (non-exportable TPM private key, certificate-based server identity verification) and fixes a TPM-build regression where clients could not perform password/keyboard-interactive authentication.

Changes:

  • Fixes client auth-method selection so --enable-tpm builds still handle password/keyboard-interactive, with TPM publickey auth preferred when a TPM key is configured.
  • Adds examples/tpmcertserver/ demonstrating a TPM-resident host key presented via an X.509 certificate, plus a minimal verifying client.
  • Extends CI (tpm-ssh.yml) and documentation (README.md) to cover the new X.509 host-certificate flow.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/internal.c Fixes TPM-related auth-method handling and adds TPM publickey preference logic.
README.md Documents how to run a TPM-backed X.509 host-certificate server/client flow.
examples/tpmcertserver/tpmcertserver.c New example server: creates TPM key, generates self-signed cert, serves with TPM host key + cert.
examples/tpmcertserver/tpmcertclient.c New example client: loads CA, restricts hostkey algos to x509, connects and performs echo.
examples/tpmcertserver/include.am Builds the new example programs under the appropriate feature gates.
examples/tpmcertserver/.gitignore Ignores built example binaries and generated cert output.
examples/include.am Includes the new example directory in the examples build.
.github/workflows/tpm-ssh.yml Adds matrix coverage and runs positive/negative X.509 TPM-host-certificate tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md
Comment thread examples/tpmcertserver/tpmcertserver.c Outdated
Comment thread examples/tpmcertserver/tpmcertclient.c Outdated
Comment thread .github/workflows/tpm-ssh.yml Outdated
@aidangarske aidangarske requested a review from dgarske July 2, 2026 21:16
@aidangarske aidangarske self-assigned this Jul 2, 2026

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: reviewOverall recommendation: COMMENT
Findings: 5 total — 5 posted, 0 skipped
5 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] TPM publickey preference has no fallback to password/keyboard on failuresrc/internal.c:16420-16431
  • [Medium] TpmCcLoadFile silently truncates CA files larger than the bufferexamples/tpmcertserver/tpmcertclient.c:70-89
  • [Low] sigType/subject may trigger -Wmaybe-uninitializedexamples/tpmcertserver/tpmcertserver.c:87-91
  • [Low] Example server compares password with non-constant-time XMEMCMPexamples/tpmcertserver/tpmcertserver.c:69-72
  • [Low] Negative CI test can false-pass on server startup failure.github/workflows/tpm-ssh.yml:169-190

Review generated by Skoll

Comment thread src/internal.c
keySig_ptr->sigId = ID_NONE;
keySig_ptr->heap = ssh->ctx->heap;

#ifdef WOLFSSH_TPM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] TPM publickey preference has no fallback to password/keyboard on failure

When ssh->ctx->tpmKey != NULL and the server offers publickey, the new block strips the WOLFSSH_USERAUTH_PASSWORD (and WOLFSSH_USERAUTH_KEYBOARD) bits from authType. Because DoUserAuthFailure() re-invokes SendUserAuthRequest() on every server FAILURE, a client with a TPM key configured can never fall back to password/keyboard auth if the TPM publickey attempt is rejected by the server -- it will keep re-sending publickey until the server disconnects. The comment says 'prefer publickey', but the implementation makes it exclusive with no fallback. This matches the pre-PR TPM behavior (which was publickey-only), so it is not a regression, but confirm exclusivity (rather than true 'prefer with fallback') is the intended design. If a client legitimately has both a TPM key and a valid password and the key is not authorized server-side, it will fail to authenticate.

Fix: Confirm the intended behavior. If password/keyboard fallback should occur after publickey fails, gate the stripping so it only applies to the first attempt (e.g. track a publickey-attempted flag) rather than on every DoUserAuthFailure retry.

}


static int TpmCcLoadFile(const char* file, byte* buf, word32* bufSz)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] TpmCcLoadFile silently truncates CA files larger than the buffer

fread(buf, 1, *bufSz, f) reads at most *bufSz (2048) bytes. If the CA file is larger than the buffer, n == *bufSz and the function reports success (*bufSz = n) while silently returning a truncated certificate. The truncated DER then fails to parse in wolfSSH_CTX_AddRootCert_buffer() with a confusing error, or (worse for a test harness) masks the real cause. There is no check that EOF was actually reached.

Fix: Detect the file-larger-than-buffer case and return an error instead of loading a truncated certificate.

{
int rc;
int devId = INVALID_DEVID;
int sigType;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] sigType/subject may trigger -Wmaybe-uninitialized

int sigType; and const char* subject; are only assigned inside the first if (rc == 0) block and only read inside the later if (rc == 0) block that calls wolfTPM2_CSR_Generate_ex(). The uses are logically guarded (they are reachable only when the assigning block ran), but GCC/Clang flow analysis often cannot correlate the separate rc == 0 blocks across the intervening wolfTPM2_CreateAndLoadKey() call and may emit -Wmaybe-uninitialized. If the example is ever built with -Werror this breaks the build.

Fix: Initialize sigType = 0; and subject = NULL; at declaration to silence potential maybe-uninitialized warnings.

const char* pw = (const char*)authData->sf.password.password;
word32 pwSz = authData->sf.password.passwordSz;

if (pwSz == (word32)XSTRLEN(TPMCS_PASSWORD)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Example server compares password with non-constant-time XMEMCMP

The password check uses XMEMCMP, which is not constant-time and violates the wolfSSL convention of comparing secrets with ConstantCompare(). This is example/demo code with a hardcoded password, so impact is negligible, but the convention exists and other examples may be copied from this one. Note this is a convention nit, not a security-audit finding.

Fix: Use ConstantCompare() for the password comparison to match repo convention, or leave as-is given it is demo code with a fixed credential.

grep -q "Client connected and verified the TPM-backed host certificate" \
tpmcert_server.txt

# Negative test: a client that trusts an unrelated CA must reject the server.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Negative CI test can false-pass on server startup failure

The negative test asserts rejection purely via the client's non-zero exit code (if tpmcertclient ...; then ERROR). If the server never comes up (TPM provisioning failure, port bind failure, etc.), the client also exits non-zero -- from a plain connection failure, not certificate rejection -- and the negative test passes anyway, hiding the real problem. This is largely mitigated because the positive test step (lines 149-167) runs first in the same matrix cell and would fail on a broken server, but the negative step is not self-guarding.

Fix: After the client fails, additionally assert the failure was a certificate/verification error (e.g. grep -q "wolfSSH_connect failed" tpmcert_client_neg.txt and confirm the server logged that it started listening) so a startup failure cannot masquerade as a correct rejection.

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants