Add TPM-backed X.509 host certificate support and fix TPM-build client auth #1168
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: TPM SSH Test | |
| on: | |
| push: | |
| branches: [ 'master', 'main', 'release/**' ] | |
| pull_request: | |
| branches: [ '*' ] | |
| workflow_dispatch: | |
| jobs: | |
| test-tpm-ssh: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| keytype: [ rsa, ecc ] | |
| sim: [ ibmswtpm2, fwtpm ] | |
| # raw: plain TPM host key (verified with OpenSSH). | |
| # x509: TPM host key presented as an X.509 certificate, verified by the | |
| # tpmcertserver/tpmcertclient example. | |
| hostkey: [ raw, x509 ] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| path: wolfssh | |
| - name: Clone wolfSSL | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: wolfSSL/wolfssl | |
| path: wolfssl | |
| - name: Clone wolfTPM | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: wolfSSL/wolftpm | |
| path: wolftpm | |
| - name: Install Dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libtool automake autoconf | |
| sudo apt-get install -y build-essential git autoconf-archive \ | |
| libcmocka-dev libssl-dev uthash-dev libglib2.0-dev \ | |
| tpm2-tools openssh-client sshpass | |
| - name: Build wolfSSL | |
| run: | | |
| cd wolfssl | |
| ./autogen.sh | |
| # certgen/certreq/certext/cryptocb: generate the X.509 host certificate | |
| # from the TPM key via the crypto callback. | |
| EXTRA_CFLAGS="-DWC_RSA_NO_PADDING" | |
| # The RSA x509v3-ssh-rsa host cert is SHA-1; modern wolfSSL otherwise | |
| # rejects SHA-1 RSA signatures. Only needed for the RSA cells. | |
| if [ "${{ matrix.keytype }}" = "rsa" ]; then | |
| EXTRA_CFLAGS="$EXTRA_CFLAGS -DWC_SIG_MIN_HASH_TYPE=WC_HASH_TYPE_SHA" | |
| fi | |
| ./configure --enable-wolftpm --enable-wolfssh --enable-keygen \ | |
| --enable-certgen --enable-certreq --enable-certext --enable-cryptocb \ | |
| CFLAGS="$EXTRA_CFLAGS" | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| # The wolfTPM client library uses the SWTPM TCP transport (port 2321) for | |
| # both simulators. The fwTPM build additionally produces fwtpm_server. | |
| - name: Build wolfTPM (fwTPM) | |
| if: matrix.sim == 'fwtpm' | |
| run: | | |
| cd wolftpm | |
| ./autogen.sh | |
| ./configure --enable-fwtpm --enable-swtpm | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| - name: Start fwTPM simulator | |
| if: matrix.sim == 'fwtpm' | |
| run: | | |
| cd wolftpm | |
| ./src/fwtpm/fwtpm_server & | |
| echo "fwtpm_server started with PID: $!" | |
| sleep 2 | |
| - name: Build wolfTPM (SWTPM) | |
| if: matrix.sim == 'ibmswtpm2' | |
| run: | | |
| cd wolftpm | |
| ./autogen.sh | |
| ./configure --enable-swtpm | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| - name: Start ibmswtpm2 simulator | |
| if: matrix.sim == 'ibmswtpm2' | |
| run: | | |
| git clone https://github.com/kgoldman/ibmswtpm2 | |
| cd ibmswtpm2/src | |
| make | |
| ./tpm_server & | |
| echo "tpm_server started with PID: $!" | |
| sleep 2 | |
| - name: Build wolfSSH | |
| run: | | |
| cd wolfssh | |
| ./autogen.sh | |
| ./configure --enable-tpm --enable-certs | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| # Server host key resident in the TPM: the private key never enters RAM. | |
| - name: Test TPM host key (${{ matrix.keytype }}) | |
| if: matrix.hostkey == 'raw' | |
| run: | | |
| cd wolftpm | |
| ./examples/keygen/keygen hostkey.bin -${{ matrix.keytype }} -t -eh | |
| cd ../wolfssh | |
| ./examples/echoserver/echoserver -1 -p 22222 \ | |
| -G ../wolftpm/hostkey.bin & | |
| echo "Echoserver (TPM ${{ matrix.keytype }} host key) PID: $!" | |
| sleep 2 | |
| if [ "${{ matrix.keytype }}" = "ecc" ]; then | |
| HKA=ecdsa-sha2-nistp256 | |
| else | |
| HKA=rsa-sha2-256 | |
| fi | |
| timeout 20 sshpass -p upthehill ssh -v -p 22222 \ | |
| -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ | |
| -o PreferredAuthentications=password -o PubkeyAuthentication=no \ | |
| -o HostKeyAlgorithms=$HKA \ | |
| jill@localhost exit > ssh_out.txt 2>&1 || true | |
| echo "----- ssh output -----" | |
| cat ssh_out.txt | |
| grep -q "Authenticated to localhost" ssh_out.txt | |
| # Server presents an X.509 host certificate whose private key lives in the | |
| # TPM. The client verifies the certificate against it as the trusted CA and | |
| # rejects a mismatched CA; the exchange hash is signed inside the TPM. | |
| - name: Test TPM X.509 host certificate (${{ matrix.keytype }}) | |
| if: matrix.hostkey == 'x509' | |
| run: | | |
| cd wolfssh | |
| ./examples/tpmcertserver/tpmcertserver -k ${{ matrix.keytype }} \ | |
| -p 22223 > tpmcert_server.txt 2>&1 & | |
| echo "tpmcertserver (X.509 ${{ matrix.keytype }}) PID: $!" | |
| # RSA key generation inside the TPM can take longer than ECC. | |
| for i in $(seq 1 40); do | |
| if ss -ltn 2>/dev/null | grep -q ':22223'; then break; fi | |
| sleep 1 | |
| done | |
| ./examples/tpmcertserver/tpmcertclient -A tpm-server-cert.der \ | |
| -p 22223 -h 127.0.0.1 > tpmcert_client.txt 2>&1 | |
| echo "----- server -----"; cat tpmcert_server.txt | |
| echo "----- client -----"; cat tpmcert_client.txt | |
| grep -q "verified server X.509 host certificate" tpmcert_client.txt | |
| 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. | |
| - name: Test TPM X.509 host certificate rejects wrong CA (${{ matrix.keytype }}) | |
| if: matrix.hostkey == 'x509' | |
| run: | | |
| cd wolfssh | |
| ./examples/tpmcertserver/tpmcertserver -k ${{ matrix.keytype }} \ | |
| -p 22224 > tpmcert_server_neg.txt 2>&1 & | |
| echo "tpmcertserver (X.509 neg ${{ matrix.keytype }}) PID: $!" | |
| for i in $(seq 1 40); do | |
| if ss -ltn 2>/dev/null | grep -q ':22224'; then break; fi | |
| sleep 1 | |
| done | |
| # keys/ca-cert-ecc.der is an unrelated CA; the self-signed host cert | |
| # does not chain to it, so the client must refuse to connect. | |
| if ./examples/tpmcertserver/tpmcertclient -A keys/ca-cert-ecc.der \ | |
| -p 22224 -h 127.0.0.1 > tpmcert_client_neg.txt 2>&1; then | |
| echo "ERROR: client accepted a server with an untrusted CA" | |
| cat tpmcert_client_neg.txt | |
| exit 1 | |
| fi | |
| echo "client correctly rejected the untrusted server:" | |
| cat tpmcert_client_neg.txt | |
| # Client public-key authentication with a TPM-resident key (RSA only). | |
| - name: Test TPM client public-key auth | |
| if: matrix.keytype == 'rsa' && matrix.hostkey == 'raw' | |
| run: | | |
| cd wolftpm | |
| ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh | |
| ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh | |
| cd ../wolfssh | |
| ./examples/echoserver/echoserver -1 -s key.ssh & | |
| echo "Echoserver (authorized TPM client key) PID: $!" | |
| sleep 2 | |
| timeout 20 ./examples/client/client -i ../wolftpm/keyblob.bin \ | |
| -u hansel -K ThisIsMyKeyAuth | |
| - name: Archive test artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: test-artifacts-${{ matrix.keytype }}-${{ matrix.sim }}-${{ matrix.hostkey }} | |
| if-no-files-found: ignore | |
| path: | | |
| wolftpm/hostkey.bin | |
| wolfssh/ssh_out.txt | |
| wolfssh/tpmcert_server.txt | |
| wolfssh/tpmcert_client.txt | |
| wolfssh/tpmcert_server_neg.txt | |
| wolfssh/tpmcert_client_neg.txt |