diff --git a/direct/tpm2/certify_test.go b/direct/tpm2/certify_test.go new file mode 100644 index 00000000..9613ff27 --- /dev/null +++ b/direct/tpm2/certify_test.go @@ -0,0 +1,147 @@ +package tpm2 + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "testing" + + "github.com/google/go-tpm/direct/helpers" + "github.com/google/go-tpm/direct/structures/tpm" + "github.com/google/go-tpm/direct/structures/tpm2b" + "github.com/google/go-tpm/direct/structures/tpma" + "github.com/google/go-tpm/direct/structures/tpml" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/structures/tpmu" + "github.com/google/go-tpm/direct/transport/simulator" +) + +func TestCertify(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") + + PCR7, err := CreatePCRSelection([]int{7}) + if err != nil { + t.Fatalf("Failed to create PCRSelection") + } + 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, + }, + }, + }, + } + + pcrSelection := tpml.PCRSelection{ + PCRSelections: []tpms.PCRSelection{ + { + Hash: tpm.AlgSHA256, + PCRSelect: PCR7, + }, + }, + } + + createPrimarySigner := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ + Buffer: Auth, + }, + }, + }, + InPublic: public, + CreationPCR: pcrSelection, + } + 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) + + createPrimarySubject := CreatePrimary{ + PrimaryHandle: tpm.RHPlatform, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ + Buffer: Auth, + }, + }, + }, + InPublic: public, + CreationPCR: pcrSelection, + } + rspSubject, err := createPrimarySubject.Execute(thetpm) + if err != nil { + t.Fatalf("Failed to create primary: %v", err) + } + flushContextSubject := FlushContext{FlushHandle: rspSubject.ObjectHandle} + defer flushContextSubject.Execute(thetpm) + + certify := Certify{ + ObjectHandle: AuthHandle{ + Handle: rspSubject.ObjectHandle, + Name: rspSubject.Name, + Auth: PasswordAuth(Auth), + }, + SignHandle: AuthHandle{ + Handle: rspSigner.ObjectHandle, + Name: rspSigner.Name, + Auth: PasswordAuth(Auth), + }, + QualifyingData: tpm2b.Data{ + Buffer: nil, + }, + InScheme: tpmt.SigScheme{ + Scheme: tpm.AlgNull, + }, + } + + rspCert, err := certify.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 retrive 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) + } +} diff --git a/direct/tpm2/tpm2.go b/direct/tpm2/tpm2.go index 5f72a3e2..43de4b6c 100644 --- a/direct/tpm2/tpm2.go +++ b/direct/tpm2/tpm2.go @@ -543,6 +543,42 @@ type SequenceCompleteResponse struct { // Response implements the Response interface. func (*SequenceCompleteResponse) Response() tpm.CC { return tpm.CCSequenceComplete } +// 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 + ObjectHandle handle `gotpm:"handle,auth"` + // handle of the key used to sign the attestation structure, Auth Index: 2, Auth Role: USER + SignHandle handle `gotpm:"handle,auth"` + // user provided qualifying data + QualifyingData tpm2b.Data + // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL + InScheme tpmt.SigScheme + } + + // Command implements the Command interface. + func (*Certify) Command() tpm.CC { return tpm.CCCertify } + + // Execute executes the command and returns the response. + func (cmd *Certify) Execute(t transport.TPM, s ...Session) (*CertifyResponse, error) { + var rsp CertifyResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil + } + + // CertifyResponse is the response from TPM2_Certify. + type CertifyResponse 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 (*CertifyResponse) Response() tpm.CC { return tpm.CCCertify } + // Quote is the input to TPM2_Quote. // See definition in Part 3, Commands, section 18.4 type Quote struct {