Repository: google/go-tpm (go)

Issue:
EvictControl missing from new tpm2
legacy tpm2 had a evictcontrol https://github.com/google/go-tpm/blob/f17b0354fbfcce8fe6db7cea78dd029c334529ad/legacy/tpm2/tpm2.go#L1125 to persist , seems to be missing in new tpm2. Or is that not needed anymore?

Pull request description:
feat: tpm2.EvictControl
Fix #335 

Sample Code:
```go
package main

import (
	"flag"
	"fmt"
	"github.com/google/go-tpm/tpm2"
	"github.com/google/go-tpm/tpm2/transport"
	"github.com/google/go-tpm/tpmutil/mssim"
	"log"
	"os"
)

var (
	defaultKeyTemplate = tpm2.TPMTPublic{
		Type:    tpm2.TPMAlgECC,
		NameAlg: tpm2.TPMAlgSHA256,
		ObjectAttributes: tpm2.TPMAObject{
			FixedTPM:            true,
			STClear:             false,
			FixedParent:         true,
			SensitiveDataOrigin: true,
			UserWithAuth:        true,
			NoDA:                true,
			Decrypt:             false,
			SignEncrypt:         true,
			X509Sign:            false,
		},
		Parameters: tpm2.NewTPMUPublicParms(
			tpm2.TPMAlgECC,
			&tpm2.TPMSECCParms{
				Scheme: tpm2.TPMTECCScheme{
					Scheme: tpm2.TPMAlgECDSA,
					Details: tpm2.NewTPMUAsymScheme(
						tpm2.TPMAlgECDSA,
						&tpm2.TPMSSigSchemeECDSA{
							HashAlg: tpm2.TPMAlgSHA256,
						},
					),
				},
				CurveID: tpm2.TPMECCNistP256,
			},
		),
	}
)

func main() {
	flag.Parse()

	conn, err := mssim.Open(mssim.Config{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Couldn't open mssim %s\n", err)
		return
	}
	defer conn.Close()

	tpmTransport := transport.FromReadWriter(conn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could't open the TPM: %s\n", err)
		return
	}

	if true {
		tpm2.Startup{}.Execute(tpmTransport)
	}

	session, closer, err := tpm2.HMACSession(tpmTransport, tpm2.TPMAlgSHA256, 32)
	if err != nil {
		log.Fatalln("hmac session failed: ", err)
	}
	defer closer()

	var nextPersistentHandle tpm2.TPMIDHObject
	if true {
		resp, err := tpm2.GetCapability{
			Capability:    tpm2.TPMCapHandles,
			Property:      0x81000000,
			PropertyCount: 64,
		}.Execute(tpmTransport)
		if err != nil {
			log.Fatalln("read public failed: ", err)
		}

		handles, err := resp.CapabilityData.Data.Handles()
		if err != nil {
			log.Fatalln("read public failed: ", err)
		}
		for _, handle := range handles.Handle {
			if nextPersistentHandle < handle {
				nextPersistentHandle = handle
			}
		}
		if nextPersistentHandle == 0 {
			nextPersistentHandle = 0x81000000
		} else {
			nextPersistentHandle += 1
		}
	}

	createResp, err := tpm2.CreatePrimary{
		PrimaryHandle: tpm2.AuthHandle{
			Handle: tpm2.TPMRHOwner,
			Auth:   session,
		},
		InPublic: tpm2.New2B(defaultKeyTemplate),
	}.Execute(tpmTransport)
	if err != nil {
		log.Fatalln("CreatePrimary failed: ", err)
	}

	resp, err := tpm2.EvictControl{
		Auth: tpm2.AuthHandle{
			Handle: tpm2.TPMRHOwner,
			Auth:   session,
		},
		ObjectHandle: &tpm2.NamedHandle{
			Handle: createResp.ObjectHandle,
			Name:   createResp.Name,
		},
		PersistentHandle: nextPersistentHandle,
	}.Execute(tpmTransport)
	if err != nil {
		log.Printf("EVICT FAILED: %v", err)
	}
	_ = resp

	log.Printf("persistent handle: 0x%08x", nextPersistentHandle)
}
```

Patch:
diff --git a/tpm2/structures.go b/tpm2/structures.go
index 7fe0d3c..ac531a3 100644
--- a/tpm2/structures.go
+++ b/tpm2/structures.go
@@ -343,6 +343,10 @@ type TPMIYesNo = bool
 // See definition in Part 2: Structures, section 9.3.
 type TPMIDHObject = TPMHandle
 
+// TPMIDHPersistent represents a TPMI_DH_PERSISTENT.
+// See definition in Part 2: Structures, section 9.5.
+type TPMIDHPersistent = TPMHandle
+
 // TPMIDHEntity represents a TPMI_DH_ENTITY.
 // See definition in Part 2: Structures, section 9.6.
 type TPMIDHEntity = TPMHandle
diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go
index ad354bb..c4e2baa 100644
--- a/tpm2/tpm2.go
+++ b/tpm2/tpm2.go
@@ -1469,6 +1469,30 @@ func (cmd FlushContext) Execute(t transport.TPM, s ...Session) (*FlushContextRes
 // FlushContextResponse is the response from TPM2_FlushContext.
 type FlushContextResponse struct{}
 
+// EvictControl is the input to TPM2_EvictControl.
+// See definition in Part 3, Commands, section 28.5
+type EvictControl struct {
+	// TPM_RH_OWNER or TPM_RH_PLATFORM+{PP}
+	Auth             handle `gotpm:"handle,auth"`
+	ObjectHandle     handle `gotpm:"handle"`
+	PersistentHandle TPMIDHPersistent
+}
+
+// EvictControlResponse is the response from TPM2_EvictControl.
+type EvictControlResponse struct{}
+
+// Command implements the Command interface.
+func (EvictControl) Command() TPMCC { return TPMCCEvictControl }
+
+// Execute executes the command and returns the response.
+func (cmd EvictControl) Execute(t transport.TPM, s ...Session) (*EvictControlResponse, error) {
+	var rsp EvictControlResponse
+	if err := execute[EvictControlResponse](t, cmd, &rsp, s...); err != nil {
+		return nil, err
+	}
+	return &rsp, nil
+}
+
 // GetCapability is the input to TPM2_GetCapability.
 // See definition in Part 3, Commands, section 30.2
 type GetCapability struct {


Test patch:
diff --git a/tpm2/test/evict_control_test.go b/tpm2/test/evict_control_test.go
new file mode 100644
index 0000000..fc84bb9
--- /dev/null
+++ b/tpm2/test/evict_control_test.go
@@ -0,0 +1,38 @@
+package tpm2test
+
+import (
+	"testing"
+
+	. "github.com/google/go-tpm/tpm2"
+	"github.com/google/go-tpm/tpm2/transport/simulator"
+)
+
+func TestEvictControl(t *testing.T) {
+	thetpm, err := simulator.OpenSimulator()
+	if err != nil {
+		t.Fatalf("could not connect to TPM simulator: %v", err)
+	}
+	defer thetpm.Close()
+
+	srkCreate := CreatePrimary{
+		PrimaryHandle: TPMRHOwner,
+		InPublic:      New2B(ECCSRKTemplate),
+	}
+
+	srkCreateRsp, err := srkCreate.Execute(thetpm)
+	if err != nil {
+		t.Fatalf("could not generate SRK: %v", err)
+	}
+
+	_, err = EvictControl{
+		Auth: TPMRHOwner,
+		ObjectHandle: &NamedHandle{
+			Handle: srkCreateRsp.ObjectHandle,
+			Name:   srkCreateRsp.Name,
+		},
+		PersistentHandle: 0x81000000,
+	}.Execute(thetpm)
+	if err != nil {
+		t.Fatalf("could not persist: %v", err)
+	}
+}