Repository: google/go-tpm
Pull request #330 (2023-06-12 08:22:56)

Replay google/master work since the start of tpmdirect
This PR cherry-picks 4 of the 5 commits that `master` had since the start of `tpmdirect` atop `main` (which has just been created based on `tpmdirect`).

Big picture: we will release this* as "major prerelease" `0.9.0` of go-tpm, to share the new tpmdirect API with external users.

*minor change, `Startup_` will be renamed to `Startup`, a change that has to be coordinated with go-tpm-tools migrating to use the legacy API via its new path

* 9bf5605e4b3a8dcf3de09894777cba55bc6738de Bump golangci-lint to 1.52.2
* e7821111f45313b63d406ae7587ec61df2ce626e Support ECC EKs for credential activation
* 23d018066378cef47300ee438dd1f045c0810d96 Accept *ecdsa.PublicKey and transmute to *ecdh.PublicKey
* 3270509f088425fc9499bc9b7b8ff0811119bedb Check if are available before calling the function

16613e24ca4db99617ac5409b3b8ad603438c00d is not included because we had already updated to an even more recent version of `golang.org/x/sys` (0.5.0)

This PR also includes a minor lint-fixing commit based on issues identified by checks on this PR.

This mainly replays some work that happened in tpm2/credactivation into legacy/tpm2/credactivation.

The only interesting conflicts occurred in e7821111f45313b63d406ae7587ec61df2ce626e. I've undone changes to credactivation 0b55c34ffe2754e7e7f16178b67d0bcd95dc0631 in favor of taking the code exactly as-is after @brandonweeks changes. I don't recall why I touched this file as part of introducing tpmdirect; I suspect it was to make a build or linter happy.


Patch:
diff --git a/Dockerfile b/Dockerfile
index 98cfdb6..347204c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@ RUN apt-get update && apt-get install -y \
     libssl-dev \
  && rm -rf /var/lib/apt/lists/*
 # We need golangci-lint for linting
-ARG VERSION=1.49.0
+ARG VERSION=1.52.2
 RUN curl -SL \
     https://github.com/golangci/golangci-lint/releases/download/v${VERSION}/golangci-lint-${VERSION}-linux-amd64.tar.gz \
     --output golangci.tar.gz \
diff --git a/go.mod b/go.mod
index 45dbbfa..35140eb 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/google/go-tpm
 
-go 1.18
+go 1.20
 
 require (
 	github.com/google/go-cmp v0.5.7
diff --git a/legacy/tpm2/credactivation/credential_activation.go b/legacy/tpm2/credactivation/credential_activation.go
index 9a90e85..90e9123 100644
--- a/legacy/tpm2/credactivation/credential_activation.go
+++ b/legacy/tpm2/credactivation/credential_activation.go
@@ -20,6 +20,8 @@ import (
 	"crypto"
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/ecdh"
+	"crypto/ecdsa"
 	"crypto/hmac"
 	"crypto/rand"
 	"crypto/rsa"
@@ -47,37 +49,34 @@ const (
 // by the TPM. A 32 byte secret is a safe, recommended default.
 //
 // This function implements Credential Protection as defined in section 24 of the TPM
-// specification revision 2 part 1, with the additional caveat of not supporting ECC EKs.
+// specification revision 2 part 1.
 // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/
 func Generate(aik *tpm2.HashValue, pub crypto.PublicKey, symBlockSize int, secret []byte) ([]byte, []byte, error) {
-	rsaPub, ok := pub.(*rsa.PublicKey)
-	if !ok {
-		return nil, nil, errors.New("only RSA public keys are supported for credential activation")
-	}
-
-	return generateRSA(aik, rsaPub, symBlockSize, secret, rand.Reader)
+	return generate(aik, pub, symBlockSize, secret, rand.Reader)
 }
 
-func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
-	crypothash, err := aik.Alg.Hash()
-	if err != nil {
-		return nil, nil, err
-	}
-
-	// The seed length should match the keysize used by the EKs symmetric cipher.
-	// For typical RSA EKs, this will be 128 bits (16 bytes).
-	// Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
-	seed := make([]byte, symBlockSize)
-	if _, err := io.ReadFull(rnd, seed); err != nil {
-		return nil, nil, fmt.Errorf("generating seed: %v", err)
-	}
-
-	// Encrypt the seed value using the provided public key.
-	// See annex B, section 10.4 of the TPM specification revision 2 part 1.
-	label := append([]byte(labelIdentity), 0)
-	encSecret, err := rsa.EncryptOAEP(crypothash.New(), rnd, pub, seed, label)
-	if err != nil {
-		return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
+func generate(aik *tpm2.HashValue, pub crypto.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
+	var seed, encSecret []byte
+	var err error
+	switch ekKey := pub.(type) {
+	case *ecdh.PublicKey:
+		seed, encSecret, err = createECSeed(aik, ekKey, rnd)
+		if err != nil {
+			return nil, nil, fmt.Errorf("creating seed: %v", err)
+		}
+	case *ecdsa.PublicKey:
+		ecdhKey, err := ekKey.ECDH()
+		if err != nil {
+			return nil, nil, fmt.Errorf("transmuting ecdsa key to ecdh key: %v", err)
+		}
+		return generate(aik, ecdhKey, symBlockSize, secret, rnd)
+	case *rsa.PublicKey:
+		seed, encSecret, err = createRSASeed(aik, ekKey, symBlockSize, rnd)
+		if err != nil {
+			return nil, nil, fmt.Errorf("creating seed: %v", err)
+		}
+	default:
+		return nil, nil, errors.New("only RSA and EC public keys are supported for credential activation")
 	}
 
 	// Generate the encrypted credential by convolving the seed with the digest of
@@ -87,11 +86,10 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
 	if err != nil {
 		return nil, nil, fmt.Errorf("encoding aikName: %v", err)
 	}
-	h, err := aik.Alg.Hash()
+	symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, symBlockSize*8)
 	if err != nil {
 		return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
 	}
-	symmetricKey := tpm2.KDFaHash(h, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
 	c, err := aes.NewCipher(symmetricKey)
 	if err != nil {
 		return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
@@ -107,10 +105,17 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
 
 	// Generate the integrity HMAC, which is used to protect the integrity of the
 	// encrypted structure.
-	// See section 24.5 of the TPM specification revision 2 part 1.
-	macKey := tpm2.KDFaHash(h, seed, labelIntegrity, nil, nil, crypothash.Size()*8)
+	// See section 24.5 of the TPM 2.0 specification.
+	cryptohash, err := aik.Alg.Hash()
+	if err != nil {
+		return nil, nil, err
+	}
+	macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, cryptohash.Size()*8)
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
+	}
 
-	mac := hmac.New(crypothash.New, macKey)
+	mac := hmac.New(cryptohash.New, macKey)
 	mac.Write(encIdentity)
 	mac.Write(aikNameEncoded)
 	integrityHMAC := mac.Sum(nil)
@@ -135,3 +140,67 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
 
 	return packedID, packedEncSecret, nil
 }
+
+func createRSASeed(aik *tpm2.HashValue, ek *rsa.PublicKey, symBlockSize int, rnd io.Reader) ([]byte, []byte, error) {
+	crypothash, err := aik.Alg.Hash()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// The seed length should match the keysize used by the EKs symmetric cipher.
+	// For typical RSA EKs, this will be 128 bits (16 bytes).
+	// Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
+	seed := make([]byte, symBlockSize)
+	if _, err := io.ReadFull(rnd, seed); err != nil {
+		return nil, nil, fmt.Errorf("generating seed: %v", err)
+	}
+
+	// Encrypt the seed value using the provided public key.
+	// See annex B, section 10.4 of the TPM specification revision 2 part 1.
+	label := append([]byte(labelIdentity), 0)
+	encryptedSeed, err := rsa.EncryptOAEP(crypothash.New(), rnd, ek, seed, label)
+	if err != nil {
+		return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
+	}
+
+	encryptedSeed, err = tpmutil.Pack(encryptedSeed)
+	return seed, encryptedSeed, err
+}
+
+func createECSeed(ak *tpm2.HashValue, ek *ecdh.PublicKey, rnd io.Reader) (seed, encryptedSeed []byte, err error) {
+	ephemeralPriv, err := ek.Curve().GenerateKey(rnd)
+	if err != nil {
+		return nil, nil, err
+	}
+	ephemeralX, ephemeralY := deconstructECDHPublicKey(ephemeralPriv.PublicKey())
+
+	z, err := ephemeralPriv.ECDH(ek)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ekX, _ := deconstructECDHPublicKey(ek)
+
+	crypothash, err := ak.Alg.Hash()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	seed, err = tpm2.KDFe(
+		ak.Alg,
+		z,
+		labelIdentity,
+		ephemeralX,
+		ekX,
+		crypothash.Size()*8)
+	if err != nil {
+		return nil, nil, err
+	}
+	encryptedSeed, err = tpmutil.Pack(tpmutil.U16Bytes(ephemeralX), tpmutil.U16Bytes(ephemeralY))
+	return seed, encryptedSeed, err
+}
+
+func deconstructECDHPublicKey(key *ecdh.PublicKey) (x []byte, y []byte) {
+	b := key.Bytes()[1:]
+	return b[:len(b)/2], b[len(b)/2:]
+}
diff --git a/legacy/tpm2/tpm2.go b/legacy/tpm2/tpm2.go
index 0f2d32c..8de0c40 100644
--- a/legacy/tpm2/tpm2.go
+++ b/legacy/tpm2/tpm2.go
@@ -1003,7 +1003,7 @@ func Quote(rw io.ReadWriter, signingHandle tpmutil.Handle, signerAuth, unused st
 
 // QuoteRaw is very similar to Quote, except that it will return
 // the raw signature in a byte array without decoding.
-func QuoteRaw(rw io.ReadWriter, signingHandle tpmutil.Handle, signerAuth, unused string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, []byte, error) {
+func QuoteRaw(rw io.ReadWriter, signingHandle tpmutil.Handle, signerAuth, _ string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, []byte, error) {
 	// TODO: Remove "unused" parameter on next breaking change.
 	Cmd, err := encodeQuote(signingHandle, signerAuth, toQuote, sel, sigAlg)
 	if err != nil {
diff --git a/tpm2/sessions.go b/tpm2/sessions.go
index f0fbda6..171adf8 100644
--- a/tpm2/sessions.go
+++ b/tpm2/sessions.go
@@ -87,10 +87,10 @@ func PasswordAuth(auth []byte) Session {
 }
 
 // Init is not required and has no effect for a password session.
-func (s *pwSession) Init(tpm transport.TPM) error { return nil }
+func (s *pwSession) Init(_ transport.TPM) error { return nil }
 
 // Cleanup is not required and has no effect for a password session.
-func (s *pwSession) CleanupFailure(tpm transport.TPM) error { return nil }
+func (s *pwSession) CleanupFailure(_ transport.TPM) error { return nil }
 
 // NonceTPM normally returns the last nonceTPM value from the session.
 // Since a password session is a pseudo-session with the auth value stuffed
@@ -102,7 +102,7 @@ func (s *pwSession) NonceTPM() TPM2BNonce { return TPM2BNonce{} }
 func (s *pwSession) NewNonceCaller() error { return nil }
 
 // Computes the authorization structure for the session.
-func (s *pwSession) Authorize(cc TPMCC, parms, addNonces []byte, _ []TPM2BName, _ int) (*TPMSAuthCommand, error) {
+func (s *pwSession) Authorize(_ TPMCC, _, _ []byte, _ []TPM2BName, _ int) (*TPMSAuthCommand, error) {
 	return &TPMSAuthCommand{
 		Handle:     TPMRSPW,
 		Nonce:      TPM2BNonce{},
@@ -114,7 +114,7 @@ func (s *pwSession) Authorize(cc TPMCC, parms, addNonces []byte, _ []TPM2BName,
 }
 
 // Validates the response session structure for the session.
-func (s *pwSession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error {
+func (s *pwSession) Validate(_ TPMRC, _ TPMCC, _ []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error {
 	if len(auth.Nonce.Buffer) != 0 {
 		return fmt.Errorf("expected empty nonce in response auth to PW session, got %x", auth.Nonce)
 	}
@@ -141,12 +141,12 @@ func (s *pwSession) IsDecryption() bool { return false }
 // If this session is used for parameter decryption, encrypts the
 // parameter. Otherwise, does not modify the parameter.
 // Password sessions can't be used for decryption.
-func (s *pwSession) Encrypt(parameter []byte) error { return nil }
+func (s *pwSession) Encrypt(_ []byte) error { return nil }
 
 // If this session is used for parameter encryption, encrypts the
 // parameter. Otherwise, does not modify the parameter.
 // Password sessions can't be used for encryption.
-func (s *pwSession) Decrypt(parameter []byte) error { return nil }
+func (s *pwSession) Decrypt(_ []byte) error { return nil }
 
 // Handle returns the handle value associated with this session.
 // In the case of a password session, this is always TPM_RS_PW.
diff --git a/tpmutil/poll_other.go b/tpmutil/poll_other.go
index 52d9b69..ba7e062 100644
--- a/tpmutil/poll_other.go
+++ b/tpmutil/poll_other.go
@@ -7,4 +7,4 @@ import (
 )
 
 // Not implemented on Windows.
-func poll(f *os.File) error { return nil }
+func poll(_ *os.File) error { return nil }
diff --git a/tpmutil/structures.go b/tpmutil/structures.go
index 6ddacd5..893b6b6 100644
--- a/tpmutil/structures.go
+++ b/tpmutil/structures.go
@@ -167,7 +167,7 @@ func (h Handle) HandleValue() uint32 {
 
 type handleList []Handle
 
-func (l *handleList) TPMMarshal(out io.Writer) error {
+func (l *handleList) TPMMarshal(_ io.Writer) error {
 	return fmt.Errorf("TPMMarhsal on []Handle is not supported yet")
 }
 
diff --git a/tpmutil/tbs/tbs_windows.go b/tpmutil/tbs/tbs_windows.go
index 1150c25..b23bf96 100644
--- a/tpmutil/tbs/tbs_windows.go
+++ b/tpmutil/tbs/tbs_windows.go
@@ -161,6 +161,9 @@ func GetDeviceInfo() (*DeviceInfo, error) {
 	//   UINT32 Size,
 	//   PVOID  Info
 	// );
+	if err := tbsGetDeviceInfo.Find(); err != nil {
+		return nil, err
+	}
 	result, _, _ := tbsGetDeviceInfo.Call(
 		unsafe.Sizeof(info),
 		uintptr(unsafe.Pointer(&info)),
@@ -180,6 +183,9 @@ func CreateContext(version Version, flag Flag) (Context, error) {
 	//   _In_  PCTBS_CONTEXT_PARAMS pContextParams,
 	//   _Out_ PTBS_HCONTEXT        *phContext
 	// );
+	if err := tbsCreateContext.Find(); err != nil {
+		return context, err
+	}
 	result, _, _ := tbsCreateContext.Call(
 		uintptr(unsafe.Pointer(&params)),
 		uintptr(unsafe.Pointer(&context)),
@@ -193,6 +199,9 @@ func (context Context) Close() error {
 	// TBS_RESULT Tbsip_Context_Close(
 	//   _In_ TBS_HCONTEXT hContext
 	// );
+	if err := tbsContextClose.Find(); err != nil {
+		return err
+	}
 	result, _, _ := tbsContextClose.Call(uintptr(context))
 	return getError(result)
 }
@@ -218,6 +227,9 @@ func (context Context) SubmitCommand(
 	//   _Out_         PBYTE                *pabResult,
 	//   _Inout_       UINT32               *pcbOutput
 	// );
+	if err := tbsSubmitCommand.Find(); err != nil {
+		return 0, err
+	}
 	result, _, _ := tbsSubmitCommand.Call(
 		uintptr(context),
 		uintptr(commandLocalityZero),
@@ -243,6 +255,9 @@ func (context Context) GetTCGLog(logBuffer []byte) (uint32, error) {
 	//   PBYTE        pOutputBuf,
 	//   PUINT32      pOutputBufLen
 	// );
+	if err := tbsGetTCGLog.Find(); err != nil {
+		return 0, err
+	}
 	result, _, _ := tbsGetTCGLog.Call(
 		uintptr(context),
 		sliceAddress(logBuffer),


Test patch:
diff --git a/legacy/tpm2/credactivation/credential_activation_test.go b/legacy/tpm2/credactivation/credential_activation_test.go
index a27deb4..8b069c2 100644
--- a/legacy/tpm2/credactivation/credential_activation_test.go
+++ b/legacy/tpm2/credactivation/credential_activation_test.go
@@ -16,15 +16,39 @@ package credactivation
 
 import (
 	"bytes"
-	"crypto/rsa"
+	"crypto/x509"
 	"encoding/base64"
-	"math/big"
-	insecureRand "math/rand"
+	"encoding/pem"
 	"testing"
 
 	"github.com/google/go-tpm/legacy/tpm2"
 )
 
+var (
+	eccEKPub = []byte(`-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsIfixqsUp8cJBSeYDhJKCZ32eAHF
+3rS7HdTMOnoFj1MrX+PutPTxa6SFdhWGLnhEQyfcwRni8veQX/dSP2on2w==
+-----END PUBLIC KEY-----`)
+	rsaEKPub = []byte(`-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArIqmuAvuIcakIEPd2hZl
+avob21ehQ7zaHduJQNbNuKVSc1HTlvw9DkWN03b0SktcRIfsjw/omqPl60RhCx0j
+qxsYnf5Gk4jhfCnUeQVicAqHnUGrKjMkLIGTZVOpyqBEXHsdhugw6M5HVIKyfwNO
+KhvLZKRH8JkvtElVhLQ6E2+H83XoSpkt9oCnGPyN2Z5qRP+fhQiRylMCD8Rz8ABn
+YVqGBBrG+2cBt/0uFLjxHx2mm/4sI/1scG5xrcrDLva9WZB40MehW5VlS6Fwqq05
+dKtLGpk7ludjH38m2zhM5/UdKZ34skJaS/Aiyj+P5AT1BpJL2ZtjCbBdnMDUSbRF
+1QIDAQAB
+-----END PUBLIC KEY-----`)
+)
+
+type zeroReader struct{}
+
+func (zeroReader) Read(b []byte) (int, error) {
+	for i := range b {
+		b[i] = 0
+	}
+	return len(b), nil
+}
+
 func mustDecodeBase64(in string, t *testing.T) []byte {
 	d, err := base64.StdEncoding.DecodeString(in)
 	if err != nil {
@@ -34,34 +58,46 @@ func mustDecodeBase64(in string, t *testing.T) []byte {
 }
 
 func TestCredentialActivation(t *testing.T) {
-	// These values were independently tested/derived-from from TCG 2.0.38-compliant hardware.
-	n, ok := new(big.Int).SetString("21781359931719875035142348126986833104406251147281912291128410183893060751686286557235105177011038982931176491091366273712008774268043339103634631078508025847736699362996617038459342869130285665581223736549299195932345592253444537445668838861984376176364138265105552997914795970576284975601851753797509031880704132484924873723738272046545068767315124876824011679223652746414206246649323781826144832659865886735865286033208505363212876011411861316385696414905053502571926429826843117374014575605550176234010475825493066764152314323863950174296024693364113127191375694561947145403061250952175062770094723660429657392597", 10)
-	if !ok {
-		t.Fatalf("Failed to parse publicN string.")
-	}
-	public := rsa.PublicKey{
-		N: n,
-		E: 65537,
+	var activateTests = []struct {
+		ekPub    []byte
+		expected string
+	}{
+		{
+			eccEKPub,
+			"AEQAIE4SquOcMAzLi7f3ru6P8nuIpSmzr8OTACXpzLo3PTOe/oBazv+fZF2JiKDZqNPNeHISCsZMdEtEfvyYjqmzZ/CB7ABEACAeGDfvDRlRiDV1cbXlVFsSLo8JZ/2nJCA+slYczpcoXgAg+CstT57xB59sS1uDVuIyQulYttdJprVoGkEDVmvcWok=",
+		},
+		{
+			rsaEKPub,
+			"AEQAIFjKZAUo3Wmgxu+CqHFzsQZr7BqawtprBmpXpZa77nb5S+iN6IcSPQLCKPZMNuunv7BIb4/VJA/xjMrj8RnQbjspCwEAJcQogjACOfStYTVjmR4p61ZbTTRt7ZNG5nc6iifq+TfnyfoU+E3T6Kount4M8fSUdMWlKx5A24Ms4ndi1VYOA+s4inPusyn1X1ZCHe5tNwT1E9jpVxc0jaUAVad6Q5cOgUyAp4qvc8wmaYXcIa/PzVfa6teF4iXxNqVDAYqpdmbP68v0Hk5gRqCa/tHAdg5avE3C20DP1SSvPitumWROL6mHMooVxjsyjPnHEBLo7y/BKwezEO/15xnBvPOvWs7ARIu1KdER+zrCJX9SMCPbn4cVMfLdrX70xko7XjdhV7pXtAeUeKmmKSYE45m5ZN0h83YgHXGDjf+ynWse10okyA==",
+		},
 	}
 
-	aikDigest := mustDecodeBase64("5snpf9qRfKD2Tb72eLAZqC/a/MyUhg+IvdwDZkTJK9w=", t)
-	expected := mustDecodeBase64("AEQAIIQNQu1RkQagbyN+7JlCKUfwBJxIsONZ2/4BD7Q4A15+BcDylTlcvTDgl1CdTuiZk3JcechnrpbfdDXynZ9Sp0uOAwEApDH7zhzLAqsNMSiEdv0xoGrGf/sOCYzSccZ1pDIv7uHON3yMMrX8beOLtCZ9vEQ3vW4i6NdWUJEd/UeMYuc1+Ucu4IB5teUtExhNyvtOXEM7FNXnKooS2ltLA0L7jlkyqwGM7CE0MK4jeFvy13RFNek6S5Rd5MH3RpBuqpL5NjX/yr4g7xCyE2RmXrCSD2DiTm6wU/PtOxYXUVdXeuLaLD69g5pnEAWhARuYa9SomBI8Ewvcxm+slfJpTK/Unrg+FN/d/n0k0IajklNli/jRhuQh5nhrTZXg80kPsEGraSP8eJof49vR643EtoO88jzpTC+/9Tu3yiGCCxEMqR2szA==", t)
-	secret := mustDecodeBase64("AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=", t)
+	for _, test := range activateTests {
+		p, _ := pem.Decode(test.ekPub)
+		public, err := x509.ParsePKIXPublicKey(p.Bytes)
+		if err != nil {
+			t.Fatal(err)
+		}
 
-	aikName := &tpm2.HashValue{
-		Alg:   tpm2.AlgSHA256,
-		Value: aikDigest,
-	}
+		aikDigest := mustDecodeBase64("5snpf9qRfKD2Tb72eLAZqC/a/MyUhg+IvdwDZkTJK9w=", t)
+		expected := mustDecodeBase64(test.expected, t)
+		secret := mustDecodeBase64("AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=", t)
 
-	idObject, wrappedCredential, err := generateRSA(aikName, &public, 16, secret, insecureRand.New(insecureRand.NewSource(99)))
-	if err != nil {
-		t.Fatal(err)
-	}
-	activationBlob := append(idObject, wrappedCredential...)
+		aikName := &tpm2.HashValue{
+			Alg:   tpm2.AlgSHA256,
+			Value: aikDigest,
+		}
+
+		idObject, wrappedCredential, err := generate(aikName, public, 16, secret, zeroReader{})
+		if err != nil {
+			t.Fatal(err)
+		}
+		activationBlob := append(idObject, wrappedCredential...)
 
-	if !bytes.Equal(expected, activationBlob) {
-		t.Errorf("generate(%v, %v, %v) returned incorrect result", aikName, public, secret)
-		t.Logf("  Got:  %v", activationBlob)
-		t.Logf("  Want: %v", expected)
+		if !bytes.Equal(expected, activationBlob) {
+			t.Errorf("generate(%v, %v, %v) returned incorrect result", aikName, public, secret)
+			t.Logf("  Got:  %v", base64.StdEncoding.EncodeToString(activationBlob))
+			t.Logf("  Want: %v", base64.StdEncoding.EncodeToString(expected))
+		}
 	}
 }
diff --git a/tpm2/test/hash_sequence_hash_test.go b/tpm2/test/hash_sequence_hash_test.go
index a4b12d2..7378cef 100644
--- a/tpm2/test/hash_sequence_hash_test.go
+++ b/tpm2/test/hash_sequence_hash_test.go
@@ -2,9 +2,9 @@ package tpm2test
 
 import (
 	"bytes"
+	"crypto/rand"
 	"crypto/sha256"
 	"fmt"
-	"math/rand"
 	"testing"
 
 	. "github.com/google/go-tpm/tpm2"
diff --git a/tpmutil/emulator_read_write_closer_test.go b/tpmutil/emulator_read_write_closer_test.go
index 0a1885c..8480748 100644
--- a/tpmutil/emulator_read_write_closer_test.go
+++ b/tpmutil/emulator_read_write_closer_test.go
@@ -74,17 +74,17 @@ func (mc *mockConn) RemoteAddr() net.Addr {
 }
 
 // SetDeadline returns nil.
-func (mc *mockConn) SetDeadline(t time.Time) error {
+func (mc *mockConn) SetDeadline(_ time.Time) error {
 	return nil
 }
 
 // SetReadDeadline returns nil.
-func (mc *mockConn) SetReadDeadline(t time.Time) error {
+func (mc *mockConn) SetReadDeadline(_ time.Time) error {
 	return nil
 }
 
 // SetWriteDeadline returns nil.
-func (mc *mockConn) SetWriteDeadline(t time.Time) error {
+func (mc *mockConn) SetWriteDeadline(_ time.Time) error {
 	return nil
 }