// Package simtest validates tpmkey's TPM ceremony against the go-tpm software // simulator (the MS TPM 2.0 reference). It lives in its own module so the CGO // simulator never enters the shipped fleet-enrol build; see go.mod. package simtest import ( "crypto/ecdsa" "crypto/rand" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "testing" "fleet-enrol/tpmkey" "github.com/google/go-tpm-tools/simulator" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" ) func openSim(t *testing.T) transport.TPM { t.Helper() sim, err := simulator.Get() if err != nil { t.Fatalf("simulator: %v", err) } t.Cleanup(func() { sim.Close() }) return transport.FromReadWriter(sim) } func TestEnsureKeyAndSignCSR(t *testing.T) { tpm := openSim(t) const h = tpm2.TPMHandle(0x81010001) signer, created, err := tpmkey.EnsureKey(tpm, h) if err != nil { t.Fatalf("EnsureKey: %v", err) } if !created { t.Errorf("first EnsureKey should report created=true") } if _, ok := signer.Public().(*ecdsa.PublicKey); !ok { t.Fatalf("signer public key is not ECDSA") } // idempotent: second call reuses the persistent key (same pubkey). signer2, created2, err := tpmkey.EnsureKey(tpm, h) if err != nil || created2 { t.Fatalf("second EnsureKey: created=%v err=%v", created2, err) } if !signer.Public().(*ecdsa.PublicKey).Equal(signer2.Public()) { t.Errorf("persistent key changed between calls") } // the TPM signer must produce a CSR that verifies — this is the exact // openssl-tpm2-provider path the Go binary replaces. tmpl := &x509.CertificateRequest{Subject: pkix.Name{CommonName: "fleet-host"}} der, err := x509.CreateCertificateRequest(rand.Reader, tmpl, signer) if err != nil { t.Fatalf("CreateCertificateRequest with TPM signer: %v", err) } csr, err := x509.ParseCertificateRequest(der) if err != nil { t.Fatalf("parse CSR: %v", err) } if err := csr.CheckSignature(); err != nil { t.Fatalf("CSR signature (TPM-signed) invalid: %v", err) } // raw signer sanity: sign a digest, verify with the public key. dig := sha256.Sum256([]byte("hello")) sig, err := signer.Sign(rand.Reader, dig[:], nil) if err != nil { t.Fatalf("sign: %v", err) } if !ecdsa.VerifyASN1(signer.Public().(*ecdsa.PublicKey), dig[:], sig) { t.Fatalf("TPM ECDSA signature did not verify") } } func TestNVWriteAndPresence(t *testing.T) { tpm := openSim(t) const nv = tpm2.TPMHandle(0x01800001) if tpmkey.NVPresent(tpm, nv) { t.Fatalf("NV should be empty on a fresh TPM") } // realistic cert-sized payload that also crosses the 512B write chunk. want := make([]byte, 700) rand.Read(want) if err := tpmkey.WriteNVCert(tpm, nv, want); err != nil { t.Fatalf("WriteNVCert: %v", err) } if !tpmkey.NVPresent(tpm, nv) { t.Fatalf("NV should be present after write") } // re-write (re-enrolment) must succeed (undefine + redefine). if err := tpmkey.WriteNVCert(tpm, nv, want[:400]); err != nil { t.Fatalf("WriteNVCert rewrite: %v", err) } }