From 6aef8a04269b180eafc49bef87a8ca7393a28be9 Mon Sep 17 00:00:00 2001 From: El Mostafa Idrassi Date: Tue, 13 Oct 2020 18:34:08 +0000 Subject: [PATCH] TPM2: Added CertifyEx and encodeCertifyEx (#211) * TPM2: Added CertifyEx and encodeCertifyEx which differ from Certify and encodeCertify in that they take the scheme to be used as an additional argument. Signed-off-by: El Mostafa IDRASSI * Fixed typo * Rename parentAuth and ownerAuth to objectAuth and signerAuth respectively. Signed-off-by: El Mostafa IDRASSI * Update Certify documentation to explain it makes use of the hardcoded signing scheme {AlgRSASSA, AlgSHA256}. * Pack AlgNull with no following hash alg in case of AlgNull scheme, and add TODO comment. * Comment formatting and other requested changes. Signed-off-by: El Mostafa IDRASSI * Replace all occurrences of tpmtSigScheme with SigScheme. Add CertifyEx tests for both RSASSA/SHA256 and ALG_NULL cases. * Better implementation of TestCertifyEx with all cases. Signed-off-by: El Mostafa IDRASSI * Requested changes implemented. --- tpm2/structures.go | 5 ---- tpm2/test/tpm2_test.go | 55 +++++++++++++++++++++++++++++++++++ tpm2/tpm2.go | 65 +++++++++++++++++++++++++++++++++++------- 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/tpm2/structures.go b/tpm2/structures.go index 02bc6e11..c90b6837 100644 --- a/tpm2/structures.go +++ b/tpm2/structures.go @@ -619,11 +619,6 @@ func (p Private) Encode() ([]byte, error) { return tpmutil.Pack(p) } -type tpmtSigScheme struct { - Scheme Algorithm - Hash Algorithm -} - // AttestationData contains data attested by TPM commands (like Certify). type AttestationData struct { Magic uint32 diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index 55567851..e19ae3bc 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -575,6 +575,61 @@ func TestCertify(t *testing.T) { }) } +func TestCertifyEx(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + restrictedKeySignerFlags := FlagSignerDefault + unrestrictedKeySignerFlags := FlagSign | FlagFixedTPM | FlagFixedParent | FlagSensitiveDataOrigin | FlagUserWithAuth + testCases := []struct { + description string + attributes KeyProp + keyScheme SigScheme + passedScheme SigScheme + shouldPass bool + }{ + {"Null-SHA1", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA1}, true}, + {"Null-SHA256", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, true}, + {"Null-Null", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgNull}, false}, + {"SHA256-Null", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgNull}, true}, + {"SHA256-SHA256", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, true}, + {"SHA256-SHA1", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA1}, false}, + } + + for _, testCase := range testCases { + params := Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: testCase.attributes, + RSAParameters: &RSAParams{ + Sign: &testCase.keyScheme, + KeyBits: 2048, + }, + } + + t.Run(testCase.description, func(t *testing.T) { + signerHandle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, defaultPassword, params) + if err != nil { + t.Fatalf("CreatePrimary(signer) failed: %s", err) + } + defer FlushContext(rw, signerHandle) + + subjectHandle, _, err := CreatePrimary(rw, HandlePlatform, PCRSelection{}, emptyPassword, defaultPassword, params) + if err != nil { + t.Fatalf("CreatePrimary(subject) failed: %s", err) + } + defer FlushContext(rw, subjectHandle) + + _, _, err = CertifyEx(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil, testCase.passedScheme) + if err != nil && testCase.shouldPass { + t.Errorf("CertifyEx expected to succeed but failed: %s", err) + } else if err == nil && !testCase.shouldPass { + t.Errorf("CertifyEx expected to fail but succeeded") + } + }) + } +} + func TestCertifyExternalKey(t *testing.T) { rw := openTPM(t) defer rw.Close() diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 702ee91a..9aa0cc18 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -1550,24 +1550,51 @@ func Sign(rw io.ReadWriter, key tpmutil.Handle, password string, digest []byte, return SignWithSession(rw, HandlePasswordSession, key, password, digest, validation, sigScheme) } -func encodeCertify(parentAuth, ownerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes) ([]byte, error) { +func encodeCertify(objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(object, signer) if err != nil { return nil, err } - auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(signerAuth)}) if err != nil { return nil, err } - scheme := tpmtSigScheme{AlgRSASSA, AlgSHA256} + scheme := SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256} // Use signing key's scheme. - params, err := tpmutil.Pack(qualifyingData, scheme) + s, err := scheme.encode() if err != nil { return nil, err } - return concat(ha, auth, params) + data, err := tpmutil.Pack(qualifyingData) + if err != nil { + return nil, err + } + return concat(ha, auth, data, s) +} + +// This function differs from encodeCertify in that it takes the scheme to be used as an additional argument. +func encodeCertifyEx(objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes, scheme SigScheme) ([]byte, error) { + ha, err := tpmutil.Pack(object, signer) + if err != nil { + return nil, err + } + + auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(signerAuth)}) + if err != nil { + return nil, err + } + + s, err := scheme.encode() + if err != nil { + return nil, err + } + data, err := tpmutil.Pack(qualifyingData) + if err != nil { + return nil, err + } + return concat(ha, auth, data, s) } func decodeCertify(resp []byte) ([]byte, []byte, error) { @@ -1604,14 +1631,32 @@ func decodeCertify(resp []byte) ([]byte, []byte, error) { } // Certify generates a signature of a loaded TPM object with a signing key -// signer. Returned values are: attestation data (TPMS_ATTEST), signature and -// error, if any. -func Certify(rw io.ReadWriter, parentAuth, ownerAuth string, object, signer tpmutil.Handle, qualifyingData []byte) ([]byte, []byte, error) { - Cmd, err := encodeCertify(parentAuth, ownerAuth, object, signer, qualifyingData) +// signer. This function calls encodeCertify which makes use of the hardcoded +// signing scheme {AlgRSASSA, AlgSHA256}. Returned values are: attestation data (TPMS_ATTEST), +// signature and error, if any. +func Certify(rw io.ReadWriter, objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData []byte) ([]byte, []byte, error) { + cmd, err := encodeCertify(objectAuth, signerAuth, object, signer, qualifyingData) + if err != nil { + return nil, nil, err + } + resp, err := runCommand(rw, TagSessions, CmdCertify, tpmutil.RawBytes(cmd)) + if err != nil { + return nil, nil, err + } + return decodeCertify(resp) +} + +// CertifyEx generates a signature of a loaded TPM object with a signing key +// signer. This function differs from Certify in that it takes the scheme +// to be used as an additional argument and calls encodeCertifyEx instead +// of encodeCertify. Returned values are: attestation data (TPMS_ATTEST), +// signature and error, if any. +func CertifyEx(rw io.ReadWriter, objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData []byte, scheme SigScheme) ([]byte, []byte, error) { + cmd, err := encodeCertifyEx(objectAuth, signerAuth, object, signer, qualifyingData, scheme) if err != nil { return nil, nil, err } - resp, err := runCommand(rw, TagSessions, CmdCertify, tpmutil.RawBytes(Cmd)) + resp, err := runCommand(rw, TagSessions, CmdCertify, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err }