From e17217b23913d4183fc279176e348b26df48fb20 Mon Sep 17 00:00:00 2001 From: Matthew Tsai <57049863+matt-tsai@users.noreply.github.com> Date: Fri, 3 Jun 2022 14:58:18 -0700 Subject: [PATCH] Implemented and Tested TPM2_Sign (#282) * Implemented and Tested TPM2_Sign * Fixed nits from PR Co-authored-by: Matthew Tsai --- direct/structures/internal/structures.go | 10 +- direct/structures/tpml/tpml.go | 4 +- direct/structures/tpmt/tpmt.go | 4 + direct/tpm2/sign_test.go | 161 +++++++++++++++++++++++ direct/tpm2/tpm2.go | 37 ++++++ 5 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 direct/tpm2/sign_test.go diff --git a/direct/structures/internal/structures.go b/direct/structures/internal/structures.go index 51c0614e..e43120c8 100644 --- a/direct/structures/internal/structures.go +++ b/direct/structures/internal/structures.go @@ -586,11 +586,15 @@ type TPMTTKAuth struct { // ticket structure tag Tag TPMST // the hierarchy of the object used to produce the ticket - Hierarchy TPMIRHHierarchy + Hierarchy TPMIRHHierarchy `gotpm:"nullable"` // This shall be the HMAC produced using a proof value of hierarchy. Digest TPM2BDigest } +// TPMTTKHashCheck represents a TPMT_TK_HASHCHECK. +// See definition in Part 2: Structures, section 10.7.6. +type TPMTTKHashCheck = TPMTTKAuth + // TPMSAlgProperty represents a TPMS_ALG_PROPERTY. // See definition in Part 2: Structures, section 10.8.1. type TPMSAlgProperty struct { @@ -676,13 +680,13 @@ type TPMLDigestValues struct { Digests []TPMTHA `gotpm:"list"` } -// TPMLPCRSelection represents a TPML_PCRzSELECTION. +// TPMLPCRSelection represents a TPML_PCR_SELECTION. // See definition in Part 2: Structures, section 10.9.7. type TPMLPCRSelection struct { PCRSelections []TPMSPCRSelection `gotpm:"list"` } -// TPMLAlgProperty represents a TPML_ALGzPROPERTY. +// TPMLAlgProperty represents a TPML_ALG_PROPERTY. // See definition in Part 2: Structures, section 10.9.8. type TPMLAlgProperty struct { AlgProperties []TPMSAlgProperty `gotpm:"list"` diff --git a/direct/structures/tpml/tpml.go b/direct/structures/tpml/tpml.go index 4a19a01b..3f7ed231 100644 --- a/direct/structures/tpml/tpml.go +++ b/direct/structures/tpml/tpml.go @@ -29,11 +29,11 @@ type Digest = internal.TPMLDigest // See definition in Part 2: Structures, section 10.9.6. type DigestValues = internal.TPMLDigestValues -// PCRSelection represents a TPML_PCRzSELECTION. +// PCRSelection represents a TPML_PCR_SELECTION. // See definition in Part 2: Structures, section 10.9.7. type PCRSelection = internal.TPMLPCRSelection -// AlgProperty represents a TPML_ALGzPROPERTY. +// AlgProperty represents a TPML_ALG_PROPERTY. // See definition in Part 2: Structures, section 10.9.8. type AlgProperty = internal.TPMLAlgProperty diff --git a/direct/structures/tpmt/tpmt.go b/direct/structures/tpmt/tpmt.go index 9ab6022a..6849d540 100644 --- a/direct/structures/tpmt/tpmt.go +++ b/direct/structures/tpmt/tpmt.go @@ -19,6 +19,10 @@ type TKVerified = internal.TPMTTKVerified // See definition in Part 2: Structures, section 10.7.5. type TKAuth = internal.TPMTTKAuth +// TKAuth represents a TPMT_TK_HASHCHECK. +// See definition in Part 2: Structures, section 10.7.6. +type TKHashCheck = internal.TPMTTKHashCheck + // SymDef represents a TPMT_SYM_DEF. // See definition in Part 2: Structures, section 11.1.6. type SymDef = internal.TPMTSymDef diff --git a/direct/tpm2/sign_test.go b/direct/tpm2/sign_test.go new file mode 100644 index 00000000..b93487ab --- /dev/null +++ b/direct/tpm2/sign_test.go @@ -0,0 +1,161 @@ +package tpm2 + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + + "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" + "github.com/google/go-tpm/tpmutil" +) + +func CreatePCRSelection(s []int) ([]byte, error) { + + const sizeOfPCRSelect = 3 + + PCRs := make(tpmutil.RawBytes, sizeOfPCRSelect) + + for _, n := range s { + if n >= 8*sizeOfPCRSelect { + return nil, fmt.Errorf("PCR index %d is out of range (exceeds maximum value %d)", n, 8*sizeOfPCRSelect-1) + } + byteNum := n / 8 + bytePos := byte(1 << (n % 8)) + PCRs[byteNum] |= bytePos + } + + return PCRs, nil +} + +func TestCreatePCRSelection(t *testing.T) { + + emptyTest, err := CreatePCRSelection([]int{}) + if err != nil { + t.Fatalf("Failed to create PCRSelection") + } + + if !cmp.Equal(emptyTest, []byte{0, 0, 0}) { + t.Fatalf("emptyTest does not return valid PCRs") + } + + filledTest, err := CreatePCRSelection([]int{0, 1, 2}) + if err != nil { + t.Fatalf("Failed to create PCRSelection") + } + + if !cmp.Equal(filledTest, []byte{7, 0, 0}) { + t.Fatalf("filledTest does not return valid PCRs") + } +} + +func TestSign(t *testing.T) { + + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + PCR7, err := CreatePCRSelection([]int{7}) + if err != nil { + t.Fatalf("Failed to create PCRSelection") + } + + createPrimary := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgRSA, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + SignEncrypt: 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, + }, + }, + }, + }, + CreationPCR: tpml.PCRSelection{ + PCRSelections: []tpms.PCRSelection{ + { + Hash: tpm.AlgSHA1, + PCRSelect: PCR7, + }, + }, + }, + } + + rspCP, err := createPrimary.Execute(thetpm) + if err != nil { + t.Fatalf("could not create key: %v", err) + } + + flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} + defer flushContext.Execute(thetpm) + + digest := sha256.Sum256([]byte("migrationpains")) + + sign := Sign{ + KeyHandle: NamedHandle{ + Handle: rspCP.ObjectHandle, + Name: rspCP.Name, + }, + Digest: tpm2b.Digest{ + Buffer: digest[:], + }, + InScheme: tpmt.SigScheme{ + Scheme: tpm.AlgRSASSA, + Details: tpmu.SigScheme{ + RSASSA: &tpms.SchemeHash{ + HashAlg: tpm.AlgSHA256, + }, + }, + }, + Validation: tpmt.TKHashCheck{ + Tag: tpm.STHashCheck, + }, + } + + rspSign, err := sign.Execute(thetpm) + if err != nil { + t.Fatalf("Failed to Sign Digest: %v", err) + } + + pub := rspCP.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, digest[:], rspSign.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 2487a88d..f78d08a1 100644 --- a/direct/tpm2/tpm2.go +++ b/direct/tpm2/tpm2.go @@ -521,6 +521,43 @@ type VerifySignatureResponse struct { // Response implements the Response interface. 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 + 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 + // 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. + Validation tpmt.TKHashCheck +} + +// Command implements the Command interface. +func (*Sign) Command() tpm.CC { return tpm.CCSign } + +// Execute executes the command and returns the response. +func (cmd *Sign) Execute(t transport.TPM, s ...Session) (*SignResponse, error) { + var rsp SignResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// SignResponse is the response from TPM2_Sign. +type SignResponse struct { + // the signature + Signature tpmt.Signature +} + +// Response implements the Response interface. +func (*SignResponse) Response() tpm.CC { return tpm.CCSign } + // PCRExtend is the input to TPM2_PCR_Extend. // See definition in Part 3, Commands, section 22.2 type PCRExtend struct {