From ce9b5ec25762f21a9f76c391774d140119c4938c Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Sat, 3 Sep 2022 12:35:21 -0700 Subject: [PATCH 1/2] add several commands needed by Google PINT --- tpm2/commands/activate_credential_test.go | 95 ++++++++ tpm2/commands/certify_test.go | 155 ++++++++++++- tpm2/commands/clear_test.go | 64 ++++++ tpm2/commands/ecdh_test.go | 108 ++++++++++ tpm2/commands/nv_test.go | 111 ++++++++++ tpm2/commands/policy_test.go | 56 +++++ tpm2/commands/tpm2.go | 252 +++++++++++++++++++++- tpm2/structures/internal/constants.go | 18 +- tpm2/structures/internal/structures.go | 12 ++ tpm2/structures/tpm/constants.go | 16 ++ tpm2/structures/tpm/tpm.go | 4 + tpm2/structures/tpm2b/tpm2b.go | 8 + 12 files changed, 889 insertions(+), 10 deletions(-) create mode 100644 tpm2/commands/activate_credential_test.go create mode 100644 tpm2/commands/clear_test.go create mode 100644 tpm2/commands/ecdh_test.go diff --git a/tpm2/commands/activate_credential_test.go b/tpm2/commands/activate_credential_test.go new file mode 100644 index 00000000..6acf0279 --- /dev/null +++ b/tpm2/commands/activate_credential_test.go @@ -0,0 +1,95 @@ +package tpm2 + +import ( + "bytes" + "testing" + + "github.com/google/go-tpm/tpm2/structures/tpm" + "github.com/google/go-tpm/tpm2/structures/tpm2b" + "github.com/google/go-tpm/tpm2/templates" + "github.com/google/go-tpm/tpm2/transport/simulator" +) + +func TestActivateCredential(t *testing.T) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + ekCreate := CreatePrimary{ + PrimaryHandle: tpm.RHEndorsement, + InPublic: tpm2b.Public{ + PublicArea: templates.ECCEKTemplate, + }, + } + + ekCreateRsp, err := ekCreate.Execute(thetpm) + if err != nil { + t.Fatalf("could not generate EK: %v", err) + } + defer func() { + flush := FlushContext{ + FlushHandle: ekCreateRsp.ObjectHandle, + } + err := flush.Execute(thetpm) + if err != nil { + t.Fatalf("could not flush EK: %v", err) + } + }() + + srkCreate := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InPublic: tpm2b.Public{ + PublicArea: templates.ECCSRKTemplate, + }, + } + + srkCreateRsp, err := srkCreate.Execute(thetpm) + if err != nil { + t.Fatalf("could not generate SRK: %v", err) + } + defer func() { + flush := FlushContext{ + FlushHandle: srkCreateRsp.ObjectHandle, + } + err := flush.Execute(thetpm) + if err != nil { + t.Fatalf("could not flush SRK: %v", err) + } + }() + + secret := tpm2b.Digest{Buffer: []byte("Secrets!!!")} + + mc := MakeCredential{ + Handle: ekCreateRsp.ObjectHandle, + Credential: secret, + ObjectNamae: srkCreateRsp.Name, + } + mcRsp, err := mc.Execute(thetpm) + if err != nil { + t.Fatalf("could not make credential: %v", err) + } + + ac := ActivateCredential{ + ActivateHandle: NamedHandle{ + Handle: srkCreateRsp.ObjectHandle, + Name: srkCreateRsp.Name, + }, + KeyHandle: AuthHandle{ + Handle: ekCreateRsp.ObjectHandle, + Name: ekCreateRsp.Name, + Auth: Policy(tpm.AlgSHA256, 16, ekPolicy), + }, + CredentialBlob: mcRsp.CredentialBlob, + Secret: mcRsp.Secret, + } + acRsp, err := ac.Execute(thetpm) + if err != nil { + t.Fatalf("could not activate credential: %v", err) + } + + if !bytes.Equal(acRsp.CertInfo.Buffer, secret.Buffer) { + t.Errorf("want %x got %x", secret.Buffer, acRsp.CertInfo.Buffer) + } +} diff --git a/tpm2/commands/certify_test.go b/tpm2/commands/certify_test.go index 0dfc208e..4d392730 100644 --- a/tpm2/commands/certify_test.go +++ b/tpm2/commands/certify_test.go @@ -20,7 +20,6 @@ import ( ) func TestCertify(t *testing.T) { - thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) @@ -275,3 +274,157 @@ func TestCreateAndCertifyCreation(t *testing.T) { t.Errorf("Signature verification failed: %v", err) } } + +func TestNVCertify(t *testing.T) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + Auth := []byte("password") + + public := tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgRSA, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + SignEncrypt: true, + Restricted: true, + FixedTPM: true, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + }, + Parameters: tpmu.PublicParms{ + RSADetail: &tpms.RSAParms{ + Scheme: tpmt.RSAScheme{ + Scheme: tpm.AlgRSASSA, + Details: tpmu.AsymScheme{ + RSASSA: &tpms.SigSchemeRSASSA{ + HashAlg: tpm.AlgSHA256, + }, + }, + }, + KeyBits: 2048, + }, + }, + }, + } + + createPrimarySigner := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ + Buffer: Auth, + }, + }, + }, + InPublic: public, + } + rspSigner, err := createPrimarySigner.Execute(thetpm) + if err != nil { + t.Fatalf("Failed to create primary: %v", err) + } + flushContextSigner := FlushContext{FlushHandle: rspSigner.ObjectHandle} + defer flushContextSigner.Execute(thetpm) + + def := NVDefineSpace{ + AuthHandle: tpm.RHOwner, + PublicInfo: tpm2b.NVPublic{ + NVPublic: tpms.NVPublic{ + NVIndex: tpm.Handle(0x0180000F), + NameAlg: tpm.AlgSHA256, + Attributes: tpma.NV{ + OwnerWrite: true, + OwnerRead: true, + AuthWrite: true, + AuthRead: true, + NT: tpm.NTOrdinary, + NoDA: true, + }, + DataSize: 4, + }, + }, + } + if err := def.Execute(thetpm); err != nil { + t.Fatalf("Calling TPM2_NV_DefineSpace: %v", err) + } + + readPub := NVReadPublic{ + NVIndex: tpm.Handle(0x0180000F), + } + nvPub, err := readPub.Execute(thetpm) + if err != nil { + t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) + } + + prewrite := NVWrite{ + AuthHandle: AuthHandle{ + Handle: def.PublicInfo.NVPublic.NVIndex, + Name: nvPub.NVName, + Auth: PasswordAuth(nil), + }, + NVIndex: NamedHandle{ + Handle: def.PublicInfo.NVPublic.NVIndex, + Name: nvPub.NVName, + }, + Data: tpm2b.MaxNVBuffer{ + Buffer: []byte{0x01, 0x02, 0x03, 0x04}, + }, + Offset: 0, + } + if err := prewrite.Execute(thetpm); err != nil { + t.Errorf("Calling TPM2_NV_Write: %v", err) + } + + nvPub, err = readPub.Execute(thetpm) + if err != nil { + t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) + } + + nvCertify := NVCertify{ + AuthHandle: AuthHandle{ + Handle: tpm.Handle(0x0180000F), + Name: nvPub.NVName, + Auth: PasswordAuth(nil), + }, + SignHandle: AuthHandle{ + Handle: rspSigner.ObjectHandle, + Name: rspSigner.Name, + Auth: PasswordAuth(Auth), + }, + NVIndex: NamedHandle{ + Handle: tpm.Handle(0x0180000F), + Name: nvPub.NVName, + }, + QualifyingData: tpm2b.Data{ + Buffer: []byte("nonce"), + }, + } + rspCert, err := nvCertify.Execute(thetpm) + if err != nil { + t.Fatalf("Failed to certify: %v", err) + } + + info, err := Marshal(rspCert.CertifyInfo.AttestationData) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + + attestHash := sha256.Sum256(info) + pub := rspSigner.OutPublic.PublicArea + rsaPub, err := helpers.RSAPub(pub.Parameters.RSADetail, pub.Unique.RSA) + if err != nil { + t.Fatalf("Failed to retrieve Public Key: %v", err) + } + + if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, attestHash[:], rspCert.Signature.Signature.RSASSA.Sig.Buffer); err != nil { + t.Errorf("Signature verification failed: %v", err) + } + + if !cmp.Equal([]byte("nonce"), rspCert.CertifyInfo.AttestationData.ExtraData.Buffer) { + t.Errorf("Attested buffer is different from original buffer") + } +} diff --git a/tpm2/commands/clear_test.go b/tpm2/commands/clear_test.go new file mode 100644 index 00000000..08e3d7d5 --- /dev/null +++ b/tpm2/commands/clear_test.go @@ -0,0 +1,64 @@ +package tpm2 + +import ( + "bytes" + "testing" + + "github.com/google/go-tpm/tpm2/structures/tpm" + "github.com/google/go-tpm/tpm2/structures/tpm2b" + "github.com/google/go-tpm/tpm2/templates" + "github.com/google/go-tpm/tpm2/transport/simulator" +) + +func TestClear(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: tpm.RHOwner, + InPublic: tpm2b.Public{ + PublicArea: templates.ECCSRKTemplate, + }, + } + + srkCreateRsp, err := srkCreate.Execute(thetpm) + if err != nil { + t.Fatalf("could not generate SRK: %v", err) + } + + srkName1 := srkCreateRsp.Name + + clear := Clear{ + AuthHandle: AuthHandle{ + Handle: tpm.RHLockout, + Auth: PasswordAuth(nil), + }, + } + err = clear.Execute(thetpm) + if err != nil { + t.Fatalf("could not clear TPM: %v", err) + } + + srkCreateRsp, err = srkCreate.Execute(thetpm) + if err != nil { + t.Fatalf("could not generate SRK: %v", err) + } + defer func() { + flush := FlushContext{ + FlushHandle: srkCreateRsp.ObjectHandle, + } + err := flush.Execute(thetpm) + if err != nil { + t.Fatalf("could not flush SRK: %v", err) + } + }() + + srkName2 := srkCreateRsp.Name + + if bytes.Equal(srkName1.Buffer, srkName2.Buffer) { + t.Errorf("SRK Name did not change across clear, was %x both times", srkName1.Buffer) + } +} diff --git a/tpm2/commands/ecdh_test.go b/tpm2/commands/ecdh_test.go new file mode 100644 index 00000000..1f7b18a8 --- /dev/null +++ b/tpm2/commands/ecdh_test.go @@ -0,0 +1,108 @@ +package tpm2 + +import ( + "crypto/elliptic" + "crypto/rand" + "math/big" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-tpm/tpm2/structures/tpm" + "github.com/google/go-tpm/tpm2/structures/tpm2b" + "github.com/google/go-tpm/tpm2/structures/tpma" + "github.com/google/go-tpm/tpm2/structures/tpms" + "github.com/google/go-tpm/tpm2/structures/tpmt" + "github.com/google/go-tpm/tpm2/structures/tpmu" + "github.com/google/go-tpm/tpm2/transport/simulator" +) + +func TestECDH(t *testing.T) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + // Create a TPM ECDH key + tpmCreate := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgECC, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + AdminWithPolicy: false, + NoDA: true, + EncryptedDuplication: false, + Restricted: false, + Decrypt: true, + SignEncrypt: false, + X509Sign: false, + }, + Parameters: tpmu.PublicParms{ + ECCDetail: &tpms.ECCParms{ + CurveID: tpm.ECCNistP256, + Scheme: tpmt.ECCScheme{ + Scheme: tpm.AlgECDH, + Details: tpmu.AsymScheme{ + ECDH: &tpms.KeySchemeECDH{ + HashAlg: tpm.AlgSHA256, + }, + }, + }, + }, + }, + }, + }, + } + + tpmCreateRsp, err := tpmCreate.Execute(thetpm) + if err != nil { + t.Fatalf("could not create the TPM key: %v", err) + } + tpmPub := tpmCreateRsp.OutPublic.PublicArea.Unique.ECC + tpmX := big.NewInt(0).SetBytes(tpmPub.X.Buffer) + tpmY := big.NewInt(0).SetBytes(tpmPub.Y.Buffer) + + // Create a SW ECDH key + priv, x, y, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("could not create the SW key: %v", err) + } + swPub := tpms.ECCPoint{ + X: tpm2b.ECCParameter{Buffer: x.FillBytes(make([]byte, 32))}, + Y: tpm2b.ECCParameter{Buffer: y.FillBytes(make([]byte, 32))}, + } + + // Calculate Z based on the SW priv * TPM pub + zx, zy := elliptic.P256().ScalarMult(tpmX, tpmY, priv) + z := tpms.ECCPoint{ + X: tpm2b.ECCParameter{Buffer: zx.FillBytes(make([]byte, 32))}, + Y: tpm2b.ECCParameter{Buffer: zy.FillBytes(make([]byte, 32))}, + } + + // Calculate Z based on TPM priv * SW pub + ecdh := ECDHZGen{ + KeyHandle: AuthHandle{ + Handle: tpmCreateRsp.ObjectHandle, + Name: tpmCreateRsp.Name, + Auth: PasswordAuth(nil), + }, + InPoint: tpm2b.ECCPoint{ + Point: swPub, + }, + } + ecdhRsp, err := ecdh.Execute(thetpm) + if err != nil { + t.Fatalf("ECDH_ZGen failed: %v", err) + } + + if !cmp.Equal(z, ecdhRsp.OutPoint.Point) { + t.Errorf("want %x got %x", z, ecdhRsp.OutPoint.Point) + } +} diff --git a/tpm2/commands/nv_test.go b/tpm2/commands/nv_test.go index 2ec54df3..133fca84 100644 --- a/tpm2/commands/nv_test.go +++ b/tpm2/commands/nv_test.go @@ -1,6 +1,8 @@ package tpm2 import ( + "bytes" + "encoding/binary" "testing" "github.com/google/go-tpm/tpm2/structures/tpm" @@ -93,3 +95,112 @@ func TestNVAuthWrite(t *testing.T) { t.Errorf("Calling TPM2_NV_Write: %v", err) } } + +func TestNVAuthIncrement(t *testing.T) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + // Define the counter space + def := NVDefineSpace{ + AuthHandle: tpm.RHOwner, + Auth: tpm2b.Auth{ + Buffer: []byte("p@ssw0rd"), + }, + PublicInfo: tpm2b.NVPublic{ + NVPublic: tpms.NVPublic{ + NVIndex: tpm.Handle(0x0180000F), + NameAlg: tpm.AlgSHA256, + Attributes: tpma.NV{ + OwnerWrite: true, + OwnerRead: true, + AuthWrite: true, + AuthRead: true, + NT: tpm.NTCounter, + NoDA: true, + }, + DataSize: 8, + }, + }, + } + if err := def.Execute(thetpm); err != nil { + t.Fatalf("Calling TPM2_NV_DefineSpace: %v", err) + } + + // Calculate the Name of the index as of its creation + // (i.e., without NV_WRITTEN set). + nvName, err := NVName(&def.PublicInfo.NVPublic) + if err != nil { + t.Fatalf("Calculating name of NV index: %v", err) + } + + incr := NVIncrement{ + AuthHandle: AuthHandle{ + Handle: tpm.RHOwner, + Auth: HMAC(tpm.AlgSHA256, 16, Auth([]byte{})), + }, + NVIndex: NamedHandle{ + Handle: def.PublicInfo.NVPublic.NVIndex, + Name: *nvName, + }, + } + if err := incr.Execute(thetpm); err != nil { + t.Errorf("Calling TPM2_NV_Increment: %v", err) + } + + // The NV index's Name has changed. Ask the TPM for it. + readPub := NVReadPublic{ + NVIndex: def.PublicInfo.NVPublic.NVIndex, + } + readPubRsp, err := readPub.Execute(thetpm) + if err != nil { + t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) + } + incr.NVIndex = NamedHandle{ + Handle: def.PublicInfo.NVPublic.NVIndex, + Name: readPubRsp.NVName, + } + + read := NVRead{ + AuthHandle: AuthHandle{ + Handle: tpm.RHOwner, + Auth: HMAC(tpm.AlgSHA256, 16, Auth([]byte{})), + }, + NVIndex: NamedHandle{ + Handle: def.PublicInfo.NVPublic.NVIndex, + Name: readPubRsp.NVName, + }, + Size: 8, + } + readRsp, err := read.Execute(thetpm) + if err != nil { + t.Fatalf("Calling TPM2_NV_Read: %v", err) + } + + if err := incr.Execute(thetpm); err != nil { + t.Errorf("Calling TPM2_NV_Increment: %v", err) + } + + var val1 uint64 + err = binary.Read(bytes.NewReader(readRsp.Data.Buffer), binary.BigEndian, &val1) + if err != nil { + t.Fatalf("Parsing counter: %v", err) + } + + readRsp, err = read.Execute(thetpm) + if err != nil { + t.Fatalf("Calling TPM2_NV_Read: %v", err) + } + + var val2 uint64 + err = binary.Read(bytes.NewReader(readRsp.Data.Buffer), binary.BigEndian, &val2) + if err != nil { + t.Fatalf("Parsing counter: %v", err) + } + + if val2 != (val1 + 1) { + t.Errorf("want %v got %v", val1+1, val2) + } +} diff --git a/tpm2/commands/policy_test.go b/tpm2/commands/policy_test.go index e8457c96..84737eea 100644 --- a/tpm2/commands/policy_test.go +++ b/tpm2/commands/policy_test.go @@ -569,6 +569,62 @@ func TestPolicyNVWrittenUpdate(t *testing.T) { } } +func TestPolicyNVUpdate(t *testing.T) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + nv, cleanup := nvIndex(t, thetpm) + defer cleanup() + + // Use a trial session to calculate this policy + sess, cleanup2, err := PolicySession(thetpm, tpm.AlgSHA256, 16, Trial()) + if err != nil { + t.Fatalf("setting up policy session: %v", err) + } + defer func() { + t.Helper() + if err := cleanup2(); err != nil { + t.Errorf("cleaning up policy session: %v", err) + } + }() + + policyNV := PolicyNV{ + AuthHandle: NamedHandle{Handle: nv.Handle, Name: nv.Name}, + PolicySession: sess.Handle(), + NVIndex: nv, + OperandB: tpm2b.Operand{Buffer: []byte("operandB")}, + Offset: 2, + Operation: tpm.EOSignedLE, + } + + if err := policyNV.Execute(thetpm); err != nil { + t.Fatalf("executing PolicyAuthorizeNV: %v", err) + } + + pgd := PolicyGetDigest{ + PolicySession: sess.Handle(), + } + want, err := pgd.Execute(thetpm) + if err != nil { + t.Fatalf("executing PolicyGetDigest: %v", err) + } + + // Use the policy helper to calculate the same policy + pol, err := NewPolicyCalculator(tpm.AlgSHA256) + if err != nil { + t.Fatalf("creating policy calculator: %v", err) + } + policyNV.Update(pol) + got := pol.Hash() + + if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { + t.Errorf("PolicyAuthorizeNV.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) + } +} + func TestPolicyAuthorizeNVUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { diff --git a/tpm2/commands/tpm2.go b/tpm2/commands/tpm2.go index 516a8448..35199818 100644 --- a/tpm2/commands/tpm2.go +++ b/tpm2/commands/tpm2.go @@ -3,6 +3,7 @@ package tpm2 import ( "bytes" + "encoding/binary" "github.com/google/go-tpm/tpm2/structures/tpm" "github.com/google/go-tpm/tpm2/structures/tpm2b" @@ -293,7 +294,7 @@ func (*LoadExternalResponse) Response() tpm.CC { return tpm.CCLoadExternal } // ReadPublic is the input to TPM2_ReadPublic. // See definition in Part 3, Commands, section 12.4 type ReadPublic struct { - // TPM handle of an object, Auth Index: None + // TPM handle of an object ObjectHandle tpmi.DHObject `gotpm:"handle"` } @@ -322,6 +323,74 @@ type ReadPublicResponse struct { // Response implements the Response interface. func (*ReadPublicResponse) Response() tpm.CC { return tpm.CCReadPublic } +// ActivateCredential is the input to TPM2_ActivateCredential. +// See definition in Part 3, Commands, section 12.5. +type ActivateCredential struct { + // handle of the object associated with certificate in credentialBlob + ActivateHandle handle `gotpm:"handle,auth"` + // loaded key used to decrypt the TPMS_SENSITIVE in credentialBlob + KeyHandle handle `gotpm:"handle,auth"` + // the credential + CredentialBlob tpm2b.IDObject + // keyHandle algorithm-dependent encrypted seed that protects credentialBlob + Secret tpm2b.EncryptedSecret +} + +// Command implements the Command interface. +func (*ActivateCredential) Command() tpm.CC { return tpm.CCActivateCredential } + +// Execute executes the command and returns the response. +func (cmd *ActivateCredential) Execute(t transport.TPM, s ...Session) (*ActivateCredentialResponse, error) { + var rsp ActivateCredentialResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// ActivateCredentialResponse is the response from TPM2_ActivateCredential. +type ActivateCredentialResponse struct { + // the decrypted certificate information + CertInfo tpm2b.Digest +} + +// Response implements the Response interface. +func (*ActivateCredentialResponse) Response() tpm.CC { return tpm.CCActivateCredential } + +// MakeCredential is the input to TPM2_MakeCredential. +// See definition in Part 3, Commands, section 12.6. +type MakeCredential struct { + // loaded public area, used to encrypt the sensitive area containing the credential key + Handle tpmi.DHObject `gotpm:"handle"` + // the credential information + Credential tpm2b.Digest + // Name of the object to which the credential applies + ObjectNamae tpm2b.Name +} + +// Command implements the Command interface. +func (*MakeCredential) Command() tpm.CC { return tpm.CCMakeCredential } + +// Execute executes the command and returns the response. +func (cmd *MakeCredential) Execute(t transport.TPM, s ...Session) (*MakeCredentialResponse, error) { + var rsp MakeCredentialResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// MakeCredentialResponse is the response from TPM2_MakeCredential. +type MakeCredentialResponse struct { + // the credential + CredentialBlob tpm2b.IDObject + // handle algorithm-dependent data that wraps the key that encrypts credentialBlob + Secret tpm2b.EncryptedSecret +} + +// Response implements the Response interface. +func (*MakeCredentialResponse) Response() tpm.CC { return tpm.CCMakeCredential } + // Unseal is the input to TPM2_Unseal. // See definition in Part 3, Commands, section 12.7 type Unseal struct { @@ -387,6 +456,36 @@ type CreateLoadedResponse struct { // Response implements the Response interface. func (*CreateLoadedResponse) Response() tpm.CC { return tpm.CCCreateLoaded } +// ECDHZGen is the input to TPM2_ECDHZGen. +// See definition in Part 3, Commands, section 14.5 +type ECDHZGen struct { + // handle of a loaded ECC key + KeyHandle handle `gotpm:"handle,auth"` + // a public key + InPoint tpm2b.ECCPoint +} + +// Command implements the Command interface. +func (*ECDHZGen) Command() tpm.CC { return tpm.CCECDHZGen } + +// Execute executes the command and returns the response. +func (cmd *ECDHZGen) Execute(t transport.TPM, s ...Session) (*ECDHZGenResponse, error) { + var rsp ECDHZGenResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// ECDHZGenResponse is the response from TPM2_ECDHZGen. +type ECDHZGenResponse struct { + // X and Y coordinates of the product of the multiplication + OutPoint tpm2b.ECCPoint +} + +// Response implements the Response interface. +func (*ECDHZGenResponse) Response() tpm.CC { return tpm.CCECDHZGen } + // Hash is the input to TPM2_Hash. // See definition in Part 3, Commands, section 15.4 type Hash struct { @@ -484,7 +583,7 @@ func (*HashSequenceStartResponse) Response() tpm.CC { return tpm.CCHashSequenceS // SequenceUpdate is the input to TPM2_SequenceUpdate. // See definition in Part 3, Commands, section 17.4 type SequenceUpdate struct { - // handle for the sequence object Auth Index: 1 Auth Role: USER + // handle for the sequence object SequenceHandle handle `gotpm:"handle,auth"` // data to be added to hash Buffer tpm2b.MaxBuffer @@ -511,7 +610,7 @@ func (*SequenceUpdateResponse) Response() tpm.CC { return tpm.CCSequenceUpdate } // SequenceComplete is the input to TPM2_SequenceComplete. // See definition in Part 3, Commands, section 17.5 type SequenceComplete struct { - // authorization for the sequence, Auth Index: 1, Auth Role: USER + // authorization for the sequence SequenceHandle handle `gotpm:"handle,auth"` // data to be added to the hash/HMAC Buffer tpm2b.MaxBuffer @@ -546,9 +645,9 @@ func (*SequenceCompleteResponse) Response() tpm.CC { return tpm.CCSequenceComple // Certify is the input to TPM2_Certify. // See definition in Part 3, Commands, section 18.2. type Certify struct { - // handle of the object to be certified, Auth Index: 1, Auth Role: ADMIN + // handle of the object to be certified ObjectHandle handle `gotpm:"handle,auth"` - // handle of the key used to sign the attestation structure, Auth Index: 2, Auth Role: USER + // handle of the key used to sign the attestation structure SignHandle handle `gotpm:"handle,auth"` // user provided qualifying data QualifyingData tpm2b.Data @@ -769,12 +868,12 @@ func (*VerifySignatureResponse) Response() tpm.CC { return tpm.CCVerifySignature // Sign is the input to TPM2_Sign. // See definition in Part 3, Commands, section 20.2. type Sign struct { - // Handle of key that will perform signing, Auth Index: 1, Auth Role: USER + // Handle of key that will perform signing KeyHandle handle `gotpm:"handle,auth"` // digest to be signed Digest tpm2b.Digest // signing scheme to use if the scheme for keyHandle is TPM_ALG_NULL - InScheme tpmt.SigScheme + InScheme tpmt.SigScheme `gotpm:"nullable"` // proof that digest was created by the TPM // If keyHandle is not a restricted signing key, then this // may be a NULL Ticket with tag = TPM_ST_CHECKHASH. @@ -1076,6 +1175,52 @@ type PolicyPCRResponse struct{} // Response implements the Response interface. func (*PolicyPCRResponse) Response() tpm.CC { return tpm.CCPolicyPCR } +// PolicyNV is the input to TPM2_PolicyNV. +// See definition in Part 3, Commands, section 23.9. +type PolicyNV struct { + // handle indicating the source of the authorization value + AuthHandle handle `gotpm:"handle,auth"` + // the NV Index of the area to read + NVIndex handle `gotpm:"handle"` + // handle for the policy session being extended + PolicySession handle `gotpm:"handle"` + // the second operand + OperandB tpm2b.Operand + // the octet offset in the NV Index for the start of operand A + Offset uint16 + // the comparison to make + Operation tpm.EO +} + +// Command implements the Command interface. +func (*PolicyNV) Command() tpm.CC { return tpm.CCPolicyNV } + +// Execute executes the command and returns the response. +func (cmd *PolicyNV) Execute(t transport.TPM, s ...Session) error { + var rsp PolicyNVResponse + return execute(t, cmd, &rsp, s...) +} + +// Update implements the PolicyCommand interface. +func (cmd *PolicyNV) Update(policy *PolicyCalculator) error { + alg, err := policy.alg.Hash() + if err != nil { + return err + } + h := alg.New() + h.Write(cmd.OperandB.Buffer) + binary.Write(h, binary.BigEndian, cmd.Offset) + binary.Write(h, binary.BigEndian, cmd.Operation) + args := h.Sum(nil) + return policy.Update(tpm.CCPolicyNV, args, cmd.NVIndex.KnownName().Buffer) +} + +// PolicyPCRResponse is the response from TPM2_PolicyPCR. +type PolicyNVResponse struct{} + +// Response implements the Response interface. +func (*PolicyNVResponse) Response() tpm.CC { return tpm.CCPolicyNV } + // PolicyCommandCode is the input to TPM2_PolicyCommandCode. // See definition in Part 3, Commands, section 23.11. type PolicyCommandCode struct { @@ -1313,10 +1458,35 @@ type CreatePrimaryResponse struct { // Response implements the Response interface. func (*CreatePrimaryResponse) Response() tpm.CC { return tpm.CCCreatePrimary } +// Clear is the input to TPM2_Clear. +// See definition in Part 3, Commands, section 24.6 +type Clear struct { + // TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} + AuthHandle handle `gotpm:"handle,auth"` +} + +// Command implements the Command interface. +func (*Clear) Command() tpm.CC { return tpm.CCClear } + +// Execute executes the command and returns the response. +func (cmd *Clear) Execute(t transport.TPM, s ...Session) error { + var rsp ClearResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return err + } + return nil +} + +// ClearResponse is the response from TPM2_Clear. +type ClearResponse struct{} + +// Response implements the Response interface. +func (*ClearResponse) Response() tpm.CC { return tpm.CCClear } + // ContextSave is the input to TPM2_ContextSave. // See definition in Part 3, Commands, section 28.2 type ContextSave struct { - // handle of the resource to save Auth Index: None + // handle of the resource to save SaveHandle tpmi.DHContext } @@ -1554,6 +1724,30 @@ type NVWriteResponse struct{} // Response implements the Response interface. func (*NVWriteResponse) Response() tpm.CC { return tpm.CCNVWrite } +// NVIncrement is the input to TPM2_NV_Increment. +// See definition in Part 3, Commands, section 31.8. +type NVIncrement struct { + // handle indicating the source of the authorization value + AuthHandle handle `gotpm:"handle,auth"` + // the NV index of the area to write + NVIndex handle `gotpm:"handle"` +} + +// Command implements the Command interface. +func (*NVIncrement) Command() tpm.CC { return tpm.CCNVIncrement } + +// Execute executes the command and returns the response. +func (cmd *NVIncrement) Execute(t transport.TPM, s ...Session) error { + var rsp NVIncrementResponse + return execute(t, cmd, &rsp, s...) +} + +// NVIncrementResponse is the response from TPM2_NV_Increment. +type NVIncrementResponse struct{} + +// Response implements the Response interface. +func (*NVIncrementResponse) Response() tpm.CC { return tpm.CCNVIncrement } + // NVWriteLock is the input to TPM2_NV_WriteLock. // See definition in Part 3, Commands, section 31.11. type NVWriteLock struct { @@ -1611,3 +1805,45 @@ type NVReadResponse struct { // Response implements the Response interface. func (*NVReadResponse) Response() tpm.CC { return tpm.CCNVRead } + +// NVCertify is the input to TPM2_NV_Certify. +// See definition in Part 3, Commands, section 31.16. +type NVCertify struct { + // handle of the key used to sign the attestation structure + SignHandle handle `gotpm:"handle,auth"` + // handle indicating the source of the authorization value + AuthHandle handle `gotpm:"handle,auth"` + // Index for the area to be certified + NVIndex handle `gotpm:"handle"` + // user-provided qualifying data + QualifyingData tpm2b.Data + // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL + InScheme tpmt.SigScheme `gotpm:"nullable"` + // number of octets to certify + Size uint16 + // octet offset into the NV area + Offset uint16 +} + +// Command implements the Command interface. +func (*NVCertify) Command() tpm.CC { return tpm.CCNVCertify } + +// Execute executes the command and returns the response. +func (cmd *NVCertify) Execute(t transport.TPM, s ...Session) (*NVCertifyResponse, error) { + var rsp NVCertifyResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// NVCertifyResponse is the response from TPM2_NV_Read. +type NVCertifyResponse struct { + // the structure that was signed + CertifyInfo tpm2b.Attest + // the asymmetric signature over certifyInfo using the key referenced by signHandle + Signature tpmt.Signature +} + +// Response implements the Response interface. +func (*NVCertifyResponse) Response() tpm.CC { return tpm.CCNVCertify } diff --git a/tpm2/structures/internal/constants.go b/tpm2/structures/internal/constants.go index 43a6bfdd..adcca148 100644 --- a/tpm2/structures/internal/constants.go +++ b/tpm2/structures/internal/constants.go @@ -295,7 +295,23 @@ const ( rcS TPMRC = 0x00000800 ) -// TPMST values come from Part 2: Structures, section 6.9. +// TPMEO values come from Part 2: Structures, section 6.8. +const ( + TPMEOEq TPMEO = 0x0000 + TPMEONeq TPMEO = 0x0001 + TPMEOSignedGT TPMEO = 0x0002 + TPMEOUnsignedGT TPMEO = 0x0003 + TPMEOSignedLT TPMEO = 0x0004 + TPMEOUnsignedLT TPMEO = 0x0005 + TPMEOSignedGE TPMEO = 0x0006 + TPMEOUnsignedGE TPMEO = 0x0007 + TPMEOSignedLE TPMEO = 0x0008 + TPMEOUnsignedLE TPMEO = 0x0009 + TPMEOBitSet TPMEO = 0x000A + TPMEOBitClear TPMEO = 0x000B +) + +// TPMST values come from Part 2: Structures, section 6.9. const ( TPMSTRspCommand TPMST = 0x00C4 TPMSTNull TPMST = 0x8000 diff --git a/tpm2/structures/internal/structures.go b/tpm2/structures/internal/structures.go index 16a018d0..e241d7fc 100644 --- a/tpm2/structures/internal/structures.go +++ b/tpm2/structures/internal/structures.go @@ -108,6 +108,10 @@ type TPMCC uint32 // See definition in Part 2: Structures, section 6.6. type TPMRC uint32 +// TPMEO represents a TPM_EO. +// See definition in Part 2: Structures, section 6.8. +type TPMEO uint16 + // TPMST represents a TPM_ST. // See definition in Part 2: Structures, section 6.9. type TPMST uint16 @@ -544,6 +548,10 @@ type TPM2BTimeout TPM2BData // See definition in Part 2: Structures, section 10.4.5. type TPM2BAuth TPM2BDigest +// TPM2BOperand represents a TPM2B_Operand. +// See definition in Part 2: Structures, section 10.4.6. +type TPM2BOperand TPM2BDigest + // TPM2BMaxBuffer represents a TPM2B_MAX_BUFFER. // See definition in Part 2: Structures, section 10.4.8. type TPM2BMaxBuffer TPM2BData @@ -1492,6 +1500,10 @@ type TPMSCreationData struct { OutsideInfo TPM2BData } +// TPM2BIDObject represents a TPM2B_ID_OBJECT. +// See definition in Part 2: Structures, section 12.4.3. +type TPM2BIDObject TPM2BData + // TPMNT represents a TPM_NT. // See definition in Part 2: Structures, section 13.4. type TPMNT uint8 diff --git a/tpm2/structures/tpm/constants.go b/tpm2/structures/tpm/constants.go index 93774313..e08e1a72 100644 --- a/tpm2/structures/tpm/constants.go +++ b/tpm2/structures/tpm/constants.go @@ -286,6 +286,22 @@ const ( RCNVUnavailable = internal.TPMRCNVUnavailable ) +// EO values come from Part 2: Structures, section 6.8. +const ( + EOEq = internal.TPMEOEq + EONeq = internal.TPMEONeq + EOSignedGT = internal.TPMEOSignedGT + EOUnsignedGT = internal.TPMEOUnsignedGT + EOSignedLT = internal.TPMEOSignedLT + EOUnsignedLT = internal.TPMEOUnsignedLT + EOSignedGE = internal.TPMEOSignedGE + EOUnsignedGE = internal.TPMEOUnsignedGE + EOSignedLE = internal.TPMEOSignedLE + EOUnsignedLE = internal.TPMEOUnsignedLE + EOBitSet = internal.TPMEOBitSet + EOBitClear = internal.TPMEOBitClear +) + // ST values come from Part 2: Structures, section 6.9. const ( STRspCommand = internal.TPMSTRspCommand diff --git a/tpm2/structures/tpm/tpm.go b/tpm2/structures/tpm/tpm.go index a3ceb359..f8d3d5b1 100644 --- a/tpm2/structures/tpm/tpm.go +++ b/tpm2/structures/tpm/tpm.go @@ -65,6 +65,10 @@ type RC = internal.TPMRC // Fmt1Error represents a TPM 2.0 format-1 error, with additional information. type Fmt1Error = internal.TPMFmt1Error +// EO represents a TPM_EO. +// See definition in Part 2: Structures, section 6.8. +type EO = internal.TPMEO + // ST represents a TPM_ST. // See definition in Part 2: Structures, section 6.9. type ST = internal.TPMST diff --git a/tpm2/structures/tpm2b/tpm2b.go b/tpm2/structures/tpm2b/tpm2b.go index 01203564..94086f6c 100644 --- a/tpm2/structures/tpm2b/tpm2b.go +++ b/tpm2/structures/tpm2b/tpm2b.go @@ -29,6 +29,10 @@ type Timeout = internal.TPM2BTimeout // See definition in Part 2: Structures, section 10.4.5. type Auth = internal.TPM2BAuth +// Operand represents a TPM2B_Operand. +// See definition in Part 2: Structures, section 10.4.6. +type Operand = internal.TPM2BOperand + // MaxBuffer represents a TPM2B_MAX_BUFFER. // See definition in Part 2: Structures, section 10.4.8. type MaxBuffer = internal.TPM2BMaxBuffer @@ -106,6 +110,10 @@ type Sensitive = internal.TPM2BSensitive // See definition in Part 2: Structures, section 12.3.7. type Private = internal.TPM2BPrivate +// IDObject represents a TPM2B_ID_OBJECT. +// See definition in Part 2: Structures, section 12.4.3. +type IDObject = internal.TPM2BIDObject + // NVPublic represents a TPM2B_NV_PUBLIC. // See definition in Part 2: Structures, section 13.6. type NVPublic = internal.TPM2BNVPublic From 8617920ff477b92085b7f238dad9c06d5c2615b1 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Sat, 3 Sep 2022 12:45:16 -0700 Subject: [PATCH 2/2] fix lint issues --- tpm2/commands/tpm2.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tpm2/commands/tpm2.go b/tpm2/commands/tpm2.go index 35199818..4a75c3fb 100644 --- a/tpm2/commands/tpm2.go +++ b/tpm2/commands/tpm2.go @@ -1215,7 +1215,7 @@ func (cmd *PolicyNV) Update(policy *PolicyCalculator) error { return policy.Update(tpm.CCPolicyNV, args, cmd.NVIndex.KnownName().Buffer) } -// PolicyPCRResponse is the response from TPM2_PolicyPCR. +// PolicyNVResponse is the response from TPM2_PolicyPCR. type PolicyNVResponse struct{} // Response implements the Response interface. @@ -1471,10 +1471,7 @@ func (*Clear) Command() tpm.CC { return tpm.CCClear } // Execute executes the command and returns the response. func (cmd *Clear) Execute(t transport.TPM, s ...Session) error { var rsp ClearResponse - if err := execute(t, cmd, &rsp, s...); err != nil { - return err - } - return nil + return execute(t, cmd, &rsp, s...) } // ClearResponse is the response from TPM2_Clear.