From c7dc278217062f253958153fec9d545348d92dcf Mon Sep 17 00:00:00 2001 From: Alex Wu Date: Mon, 8 Mar 2021 16:28:15 -0800 Subject: [PATCH 1/4] Add support for encoding Signatures Some functions in tpm2 expect an encoded TPMT_SIGNATURE. Here, we add an Encode method on the Signature type to support these functions. --- tpm2/structures.go | 21 ++++++++++++++ tpm2/test/tpm2_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/tpm2/structures.go b/tpm2/structures.go index 5a7318d5..31913e5a 100644 --- a/tpm2/structures.go +++ b/tpm2/structures.go @@ -573,6 +573,27 @@ type Signature struct { ECC *SignatureECC } +// Encode serializes a Signature structure in TPM wire format. +func (s Signature) Encode() ([]byte, error) { + head, err := tpmutil.Pack(s.Alg) + if err != nil { + return nil, fmt.Errorf("encoding Alg: %v", err) + } + var signature []byte + switch s.Alg { + case AlgRSASSA, AlgRSAPSS: + if signature, err = tpmutil.Pack(s.RSA); err != nil { + return nil, fmt.Errorf("encoding RSA: %v", err) + } + case AlgECDSA: + signature, err = tpmutil.Pack(s.ECC.HashAlg, tpmutil.U16Bytes(s.ECC.R.Bytes()), tpmutil.U16Bytes(s.ECC.S.Bytes())) + if err != nil { + return nil, fmt.Errorf("encoding ECC: %v", err) + } + } + return concat(head, signature) +} + // DecodeSignature decodes a serialized TPMT_SIGNATURE structure. func DecodeSignature(in *bytes.Buffer) (*Signature, error) { var sig Signature diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index 14ab0d6a..4d78ba5b 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -23,10 +23,12 @@ import ( "crypto/rsa" "crypto/sha1" "crypto/sha256" + "encoding/binary" "flag" "fmt" "hash" "io" + "math/big" "reflect" "strings" "testing" @@ -1182,6 +1184,69 @@ func TestEncodeDecodePublicDefaultRSAExponent(t *testing.T) { } } +func TestEncodeDecodeSignature(t *testing.T) { + randRSASig := func() []byte { + // Key size 2048 bits + var size uint16 = 256 + sizeU16 := make([]byte, 2) + binary.BigEndian.PutUint16(sizeU16, size) + key := make([]byte, size) + rand.Read(key) + return append(sizeU16, key...) + } + + run := func(t *testing.T, s Signature) { + e, err := s.Encode() + if err != nil { + t.Fatalf("Signature{%+v}.Encode() returned error: %v", s, err) + } + d, err := DecodeSignature(bytes.NewBuffer(e)) + if err != nil { + t.Fatalf("DecodeSignature{%v} returned error: %v", e, err) + } + if !reflect.DeepEqual(s, *d) { + t.Errorf("got decoded value:\n%v\nwant:\n%v", d, s) + } + } + t.Run("RSASSA", func(t *testing.T) { + run(t, Signature{ + Alg: AlgRSASSA, + RSA: &SignatureRSA{ + HashAlg: AlgSHA256, + Signature: randRSASig(), + }, + }) + }) + t.Run("RSAPSS", func(t *testing.T) { + run(t, Signature{ + Alg: AlgRSAPSS, + RSA: &SignatureRSA{ + HashAlg: AlgSHA256, + Signature: randRSASig(), + }, + }) + }) + t.Run("ECDSA", func(t *testing.T) { + // Key size 256 bits + size := 32 + randBytes := make([]byte, size) + rand.Read(randBytes) + r := big.NewInt(0).SetBytes(randBytes) + + rand.Read(randBytes) + s := big.NewInt(0).SetBytes(randBytes) + + run(t, Signature{ + Alg: AlgECDSA, + ECC: &SignatureECC{ + HashAlg: AlgSHA256, + R: r, + S: s, + }, + }) + }) +} + func TestCreateKeyWithSensitive(t *testing.T) { rw := openTPM(t) defer rw.Close() From 49f8a0e811704491909483f7d3297ae964d80b70 Mon Sep 17 00:00:00 2001 From: Alex Wu Date: Tue, 16 Mar 2021 16:10:56 -0700 Subject: [PATCH 2/4] Add tpm2.PolicySigned Add the ability to call TPM2_PolicySigned, and add PolicySigned test to verify correct behavior given different expiration values. --- tpm2/constants.go | 3 ++ tpm2/test/tpm2_test.go | 102 +++++++++++++++++++++++++++++++++++++++-- tpm2/tpm2.go | 55 +++++++++++++++++----- 3 files changed, 146 insertions(+), 14 deletions(-) diff --git a/tpm2/constants.go b/tpm2/constants.go index 1b4a26b5..e72cd096 100644 --- a/tpm2/constants.go +++ b/tpm2/constants.go @@ -400,7 +400,9 @@ const ( TagAttestCertify tpmutil.Tag = 0x8017 TagAttestQuote tpmutil.Tag = 0x8018 TagAttestCreation tpmutil.Tag = 0x801a + TagAuthSecret tpmutil.Tag = 0x8023 TagHashCheck tpmutil.Tag = 0x8024 + TagAuthSigned tpmutil.Tag = 0x8025 ) // StartupType instructs the TPM on how to handle its state during Shutdown or @@ -470,6 +472,7 @@ const ( CmdSequenceUpdate tpmutil.Command = 0x0000015C CmdSign tpmutil.Command = 0x0000015D CmdUnseal tpmutil.Command = 0x0000015E + CmdPolicySigned tpmutil.Command = 0x00000160 CmdContextLoad tpmutil.Command = 0x00000161 CmdContextSave tpmutil.Command = 0x00000162 CmdECDHKeyGen tpmutil.Command = 0x00000163 diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index 4d78ba5b..216b327c 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -79,8 +79,32 @@ var ( ExponentRaw: 1<<16 + 1, }, } - defaultPassword = "\x01\x02\x03\x04" - emptyPassword = "" + defaultPassword = "\x01\x02\x03\x04" + emptyPassword = "" + defaultRsaSignerParams = Public{ + Type: AlgRSA, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + RSAParameters: &RSAParams{ + Sign: &SigScheme{ + Alg: AlgRSASSA, + Hash: AlgSHA256, + }, + KeyBits: 2048, + }, + } + defaultEccSignerParams = Public{ + Type: AlgECC, + NameAlg: AlgSHA256, + Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, + ECCParameters: &ECCParams{ + Sign: &SigScheme{ + Alg: AlgECDSA, + Hash: AlgSHA256, + }, + CurveID: CurveNISTP256, + }, + } ) func min(a, b int) int { @@ -1494,15 +1518,87 @@ func TestPolicySecret(t *testing.T) { rw := openTPM(t) defer rw.Close() + _, _ = testPolicySecret(t, rw, 0) +} + +func testPolicySecret(t *testing.T, rw io.ReadWriter, expiration int32) ([]byte, *Ticket) { sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) if err != nil { t.Fatalf("StartAuthSession() failed: %v", err) } defer FlushContext(rw, sessHandle) - if _, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nil, nil, nil, 0); err != nil { + timeout, tkt, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nil, nil, nil, expiration) + if err != nil { t.Fatalf("PolicySecret() failed: %v", err) } + return timeout, tkt +} + +func TestPolicySigned(t *testing.T) { + rw := openTPM(t) + defer rw.Close() + + var nullTicket = Ticket{Type: TagAuthSigned, Hierarchy: HandleNull} + signers := map[string]Public{ + "RSA": defaultRsaSignerParams, + "ECC": defaultEccSignerParams, + } + + expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} + for _, expiration := range expirations { + for signerType, params := range signers { + t.Run(t.Name()+signerType+fmt.Sprint(expiration), func(t *testing.T) { + _, tkt := testPolicySigned(t, rw, expiration, params) + if expiration < 0 && len(tkt.Digest) == 0 { + t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") + } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicket) { + t.Fatalf("Got ticket with non-empty digest, expected NULL ticket") + } + }) + } + } +} + +func testPolicySigned(t *testing.T, rw io.ReadWriter, expiration int32, signerParams Public) ([]byte, *Ticket) { + handle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, emptyPassword, signerParams) + if err != nil { + t.Fatalf("CreatePrimary() failed: %s", err) + } + defer FlushContext(rw, handle) + + sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + if err != nil { + t.Fatalf("StartAuthSession() failed: %v", err) + } + defer FlushContext(rw, sessHandle) + + // Sign the hash of the command parameters, as described in the TPM 2.0 spec, Part 3, Section 23.3. + // We only use expiration here. + expBytes := make([]byte, 4) + binary.BigEndian.PutUint32(expBytes, uint32(expiration)) + + // TPM2.0 spec, Revision 1.38, Part 3 nonce must be present if expiration is non-zero. + // aHash ≔ HauthAlg(nonceTPM || expiration || cpHashA || policyRef) + toDigest := append(nonce, expBytes...) + + digest := sha256.Sum256(toDigest) + + sig, err := Sign(rw, handle, emptyPassword, digest[:], nil, nil) + if err != nil { + t.Fatalf("Sign failed: %s", err) + } + + signature, err := sig.Encode() + if err != nil { + t.Fatalf("Encode() failed: %v", err) + } + + timeout, tkt, err := PolicySigned(rw, handle, sessHandle, nonce, nil, nil, expiration, signature) + if err != nil { + t.Fatalf("PolicySigned() failed: %v", err) + } + return timeout, tkt } func TestQuote(t *testing.T) { diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 7a1c0680..65330bd7 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -747,38 +747,71 @@ func encodePolicySecret(entityHandle tpmutil.Handle, entityAuth AuthCommand, pol return concat(handles, auth, params) } -func decodePolicySecret(in []byte) (*Ticket, error) { +func decodePolicySecret(in []byte) ([]byte, *Ticket, error) { buf := bytes.NewBuffer(in) var paramSize uint32 var timeout tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, ¶mSize, &timeout); err != nil { - return nil, fmt.Errorf("decoding timeout: %v", err) + return nil, nil, fmt.Errorf("decoding timeout: %v", err) } var t Ticket if err := tpmutil.UnpackBuf(buf, &t); err != nil { - return nil, fmt.Errorf("decoding ticket: %v", err) + return nil, nil, fmt.Errorf("decoding ticket: %v", err) } - return &t, nil + return timeout, &t, nil } // PolicySecret sets a secret authorization requirement on the provided entity. -// If expiry is non-zero, the authorization is valid for expiry seconds. -func PolicySecret(rw io.ReadWriter, entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32) (*Ticket, error) { +func PolicySecret(rw io.ReadWriter, entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32) ([]byte, *Ticket, error) { Cmd, err := encodePolicySecret(entityHandle, entityAuth, policyHandle, policyNonce, cpHash, policyRef, expiry) if err != nil { - return nil, err + return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdPolicySecret, tpmutil.RawBytes(Cmd)) + if err != nil { + return nil, nil, err + } + return decodePolicySecret(resp) +} + +func encodePolicySigned(validationKeyHandle tpmutil.Handle, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef tpmutil.U16Bytes, expiry int32, auth []byte) ([]byte, error) { + handles, err := tpmutil.Pack(validationKeyHandle, policyHandle) if err != nil { return nil, err } + params, err := tpmutil.Pack(policyNonce, cpHash, policyRef, expiry, auth) + if err != nil { + return nil, err + } + return concat(handles, params) +} + +func decodePolicySigned(in []byte) ([]byte, *Ticket, error) { + buf := bytes.NewBuffer(in) - // Tickets are only provided if expiry is set. - if expiry != 0 { - return decodePolicySecret(resp) + var timeout tpmutil.U16Bytes + if err := tpmutil.UnpackBuf(buf, &timeout); err != nil { + return nil, nil, fmt.Errorf("decoding timeout: %v", err) + } + var t Ticket + if err := tpmutil.UnpackBuf(buf, &t); err != nil { + return nil, nil, fmt.Errorf("decoding ticket: %v", err) + } + return timeout, &t, nil +} + +// PolicySigned sets a signed authorization requirement on the provided policy. +func PolicySigned(rw io.ReadWriter, validationKeyHandle tpmutil.Handle, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32, signedAuth []byte) ([]byte, *Ticket, error) { + Cmd, err := encodePolicySigned(validationKeyHandle, policyHandle, policyNonce, cpHash, policyRef, expiry, signedAuth) + if err != nil { + return nil, nil, err + } + resp, err := runCommand(rw, TagNoSessions, CmdPolicySigned, tpmutil.RawBytes(Cmd)) + if err != nil { + return nil, nil, err } - return nil, nil + return decodePolicySigned(resp) } func encodePolicyPCR(session tpmutil.Handle, expectedDigest tpmutil.U16Bytes, sel PCRSelection) ([]byte, error) { From 9770b632cd4275ceb2a2cbcd214de9fd288447fc Mon Sep 17 00:00:00 2001 From: Alex Wu Date: Tue, 6 Apr 2021 18:56:02 -0700 Subject: [PATCH 3/4] Add test on ComputeAuthTimeout The Microsoft TPM2 and IBM SW TPM simulator both use an absolute value method of `expiration = -expiration` in ComputeAuthTimeout. As abs(Int32Min) cannot be represented an an int32, this expression evaluates to Int32Min. See https://github.com/microsoft/ms-tpm-20-ref/blob/b94f9f92c579b723a16be72a69efbbf9c35ce44e/TPMCmd/tpm/src/command/EA/Policy_spt.c#L189 The function goes on to cast expiration to UINT64. This can either be sign-extended or zero-extended, which is undefined behavior. If it is sign-extended, this carries the negative bit to create a large number (9.22 e+18 ms ~ 292471140.58 years). If it is zero-extended, this results in 2147483648000 ms ~ 68.1 years. Also, enable non-zero expirations on TestPolicySecret The TPM2.0 spec, Revision 1.16, states that TPM2_PolicySecret doesn't return a timeout/ticket for a nonzero expiration without a nonce. --- tpm2/test/tpm2_test.go | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index 216b327c..cfff43aa 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -28,6 +28,7 @@ import ( "fmt" "hash" "io" + "math" "math/big" "reflect" "strings" @@ -1518,17 +1519,29 @@ func TestPolicySecret(t *testing.T) { rw := openTPM(t) defer rw.Close() - _, _ = testPolicySecret(t, rw, 0) + var nullTicket = Ticket{Type: TagAuthSecret, Hierarchy: HandleNull} + + expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} + for _, expiration := range expirations { + t.Run(t.Name()+fmt.Sprint(expiration), func(t *testing.T) { + _, tkt := testPolicySecret(t, rw, expiration) + if expiration < 0 && len(tkt.Digest) == 0 { + t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") + } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicket) { + t.Fatalf("Got ticket with non-empty digest, expected NULL ticket") + } + }) + } } func testPolicySecret(t *testing.T, rw io.ReadWriter, expiration int32) ([]byte, *Ticket) { - sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) if err != nil { t.Fatalf("StartAuthSession() failed: %v", err) } defer FlushContext(rw, sessHandle) - timeout, tkt, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nil, nil, nil, expiration) + timeout, tkt, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nonce, nil, nil, expiration) if err != nil { t.Fatalf("PolicySecret() failed: %v", err) } @@ -1601,6 +1614,62 @@ func testPolicySigned(t *testing.T, rw io.ReadWriter, expiration int32, signerPa return timeout, tkt } +func timeoutToUint64(timeout []byte) uint64 { + // The MSFT TPM simulator uses the MSB to indicate + // whether to expire on reset. Strip this out if set. + timeoutUint64 := binary.BigEndian.Uint64(timeout) + expiryBit := uint64(1) << 63 + if timeoutUint64&expiryBit != 0 { + timeoutUint64 -= expiryBit + } + return timeoutUint64 +} + +func authTimeoutWithinExpectedRange(expiration int32, timeout []byte) bool { + // This function expects the policy to not use nonceTpm. + // https://github.com/microsoft/ms-tpm-20-ref/blob/b94f9f92c579b723a16be72a69efbbf9c35ce44e/TPMCmd/tpm/src/command/EA/Policy_spt.c#L195 + + absExp := uint64(math.Abs(float64(expiration))) + var authTimeout uint64 = timeoutToUint64(timeout) + + absExpInMs := absExp * 1000 + if authTimeout < absExpInMs || authTimeout >= absExpInMs+1000 { + return false + } + return true +} + +func TestComputeAuthTimeoutAbsValue(t *testing.T) { + // This test tests the absolue value function in + // https://github.com/microsoft/ms-tpm-20-ref/blob/b94f9f92c579b723a16be72a69efbbf9c35ce44e/TPMCmd/tpm/src/command/EA/Policy_spt.c#L189. + // ComputeAuthTimeout casts expiration to UINT64. This is undefined + // behavior for a negative number, either sign or zero-extended. + // This only invokes UB when the expiration is int32 min, as the + // abs value function is `expiration = -expiration`. + + rw := openTPM(t) + defer rw.Close() + + expirations := []int32{math.MinInt32, math.MinInt32 + 1} + for _, expiration := range expirations { + timeout, _ := testPolicySecret(t, rw, expiration) + if len(timeout) == 0 { + t.Fatal("Expected a non-empty timeout!") + } + if !authTimeoutWithinExpectedRange(expiration, timeout) { + t.Errorf("The timeout %v is not in the expected range for expiration %v!", timeoutToUint64(timeout), expiration) + } + + timeout, _ = testPolicySigned(t, rw, expiration, defaultRsaSignerParams) + if len(timeout) == 0 { + t.Fatal("Expected a non-empty timeout!") + } + if !authTimeoutWithinExpectedRange(expiration, timeout) { + t.Errorf("The timeout %v is not in the expected range for expiration %v!", timeoutToUint64(timeout), expiration) + } + } +} + func TestQuote(t *testing.T) { rw := openTPM(t) defer rw.Close() From 9660b158cc59605ce99255e198bc7d2655ee9865 Mon Sep 17 00:00:00 2001 From: Alex Wu Date: Thu, 8 Apr 2021 11:43:12 -0700 Subject: [PATCH 4/4] Allow different nonces in TestPolicySigned/Secret Separate revisions of the TPM spec treat expirations and nonces differently. Revision 1.16 requires a nonce with non-zero expiration while 1.59 does not. For the ComputeAuthTimeout test, we need empty nonces and expiration == min int32 to test properly. --- tpm2/test/tpm2_test.go | 57 ++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index cfff43aa..7171f78e 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -1524,7 +1524,13 @@ func TestPolicySecret(t *testing.T) { expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} for _, expiration := range expirations { t.Run(t.Name()+fmt.Sprint(expiration), func(t *testing.T) { - _, tkt := testPolicySecret(t, rw, expiration) + sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + if err != nil { + t.Fatalf("StartAuthSession() failed: %v", err) + } + defer FlushContext(rw, sessHandle) + + _, tkt := testPolicySecret(t, rw, sessHandle, nonce, expiration) if expiration < 0 && len(tkt.Digest) == 0 { t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicket) { @@ -1534,12 +1540,7 @@ func TestPolicySecret(t *testing.T) { } } -func testPolicySecret(t *testing.T, rw io.ReadWriter, expiration int32) ([]byte, *Ticket) { - sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) - if err != nil { - t.Fatalf("StartAuthSession() failed: %v", err) - } - defer FlushContext(rw, sessHandle) +func testPolicySecret(t *testing.T, rw io.ReadWriter, sessHandle tpmutil.Handle, nonce []byte, expiration int32) ([]byte, *Ticket) { timeout, tkt, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nonce, nil, nil, expiration) if err != nil { @@ -1558,11 +1559,17 @@ func TestPolicySigned(t *testing.T) { "ECC": defaultEccSignerParams, } - expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} + expirations := []int32{math.MinInt32, math.MinInt32 + 1, 0, math.MaxInt32} for _, expiration := range expirations { for signerType, params := range signers { t.Run(t.Name()+signerType+fmt.Sprint(expiration), func(t *testing.T) { - _, tkt := testPolicySigned(t, rw, expiration, params) + sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + if err != nil { + t.Fatalf("StartAuthSession() failed: %v", err) + } + defer FlushContext(rw, sessHandle) + + _, tkt := testPolicySigned(t, rw, sessHandle, nonce, expiration, params) if expiration < 0 && len(tkt.Digest) == 0 { t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicket) { @@ -1573,19 +1580,13 @@ func TestPolicySigned(t *testing.T) { } } -func testPolicySigned(t *testing.T, rw io.ReadWriter, expiration int32, signerParams Public) ([]byte, *Ticket) { +func testPolicySigned(t *testing.T, rw io.ReadWriter, sessHandle tpmutil.Handle, nonce []byte, expiration int32, signerParams Public) ([]byte, *Ticket) { handle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, emptyPassword, signerParams) if err != nil { t.Fatalf("CreatePrimary() failed: %s", err) } defer FlushContext(rw, handle) - sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) - if err != nil { - t.Fatalf("StartAuthSession() failed: %v", err) - } - defer FlushContext(rw, sessHandle) - // Sign the hash of the command parameters, as described in the TPM 2.0 spec, Part 3, Section 23.3. // We only use expiration here. expBytes := make([]byte, 4) @@ -1625,8 +1626,8 @@ func timeoutToUint64(timeout []byte) uint64 { return timeoutUint64 } +// authTimeoutWithinExpectedRange expects the policy to not use nonceTpm. func authTimeoutWithinExpectedRange(expiration int32, timeout []byte) bool { - // This function expects the policy to not use nonceTpm. // https://github.com/microsoft/ms-tpm-20-ref/blob/b94f9f92c579b723a16be72a69efbbf9c35ce44e/TPMCmd/tpm/src/command/EA/Policy_spt.c#L195 absExp := uint64(math.Abs(float64(expiration))) @@ -1639,6 +1640,16 @@ func authTimeoutWithinExpectedRange(expiration int32, timeout []byte) bool { return true } +func skipOnUnsupportedRevision(t *testing.T, rw io.ReadWriter, revision uint32) { + props, _, err := GetCapability(rw, CapabilityTPMProperties, 3, uint32(FamilyIndicator)) + if err != nil { + t.Fatalf("GetCapability failed: %v", err) + } + if props[2].(TaggedProperty).Value <= 116 { + t.Skipf("Test %v does not support TPM2, Revision %v", t.Name(), revision) + } +} + func TestComputeAuthTimeoutAbsValue(t *testing.T) { // This test tests the absolue value function in // https://github.com/microsoft/ms-tpm-20-ref/blob/b94f9f92c579b723a16be72a69efbbf9c35ce44e/TPMCmd/tpm/src/command/EA/Policy_spt.c#L189. @@ -1646,13 +1657,21 @@ func TestComputeAuthTimeoutAbsValue(t *testing.T) { // behavior for a negative number, either sign or zero-extended. // This only invokes UB when the expiration is int32 min, as the // abs value function is `expiration = -expiration`. + // This tests against revisions > 1.16, as ComputeAuthTimeout shows up in revisions 1.38 and 1.59. rw := openTPM(t) defer rw.Close() + skipOnUnsupportedRevision(t, rw, 116) expirations := []int32{math.MinInt32, math.MinInt32 + 1} for _, expiration := range expirations { - timeout, _ := testPolicySecret(t, rw, expiration) + sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) + if err != nil { + t.Fatalf("StartAuthSession() failed: %v", err) + } + defer FlushContext(rw, sessHandle) + + timeout, _ := testPolicySecret(t, rw, sessHandle, nil, expiration) if len(timeout) == 0 { t.Fatal("Expected a non-empty timeout!") } @@ -1660,7 +1679,7 @@ func TestComputeAuthTimeoutAbsValue(t *testing.T) { t.Errorf("The timeout %v is not in the expected range for expiration %v!", timeoutToUint64(timeout), expiration) } - timeout, _ = testPolicySigned(t, rw, expiration, defaultRsaSignerParams) + timeout, _ = testPolicySigned(t, rw, sessHandle, nil, expiration, defaultEccSignerParams) if len(timeout) == 0 { t.Fatal("Expected a non-empty timeout!") }