Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Direct TPM2 API #266

Merged
merged 9 commits into from
Feb 19, 2022
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module github.com/google/go-tpm

go 1.12
go 1.17

require (
github.com/google/go-cmp v0.5.0
github.com/google/go-tpm-tools v0.2.0
golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb
)
8 changes: 3 additions & 5 deletions tpm2/credactivation/credential_activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
if err != nil {
return nil, nil, fmt.Errorf("encoding aikName: %v", err)
}
symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
h, err := aik.Alg.Hash()
if err != nil {
return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
}
symmetricKey := tpm2.KDFaHash(h, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
c, err := aes.NewCipher(symmetricKey)
if err != nil {
return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
Expand All @@ -107,10 +108,7 @@ func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secr
// Generate the integrity HMAC, which is used to protect the integrity of the
// encrypted structure.
// See section 24.5 of the TPM specification revision 2 part 1.
macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, crypothash.Size()*8)
if err != nil {
return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
}
macKey := tpm2.KDFaHash(h, seed, labelIntegrity, nil, nil, crypothash.Size()*8)

mac := hmac.New(crypothash.New, macKey)
mac.Write(encIdentity)
Expand Down
77 changes: 77 additions & 0 deletions tpm2/direct/audit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package direct

import (
"bytes"
"fmt"
"reflect"
)

// CommandAudit represents an audit session for attesting the execution of a
// series of commands in the TPM. It is useful for both command and session
// auditing.
type CommandAudit struct {
hash TPMIAlgHash
digest []byte
}

// NewAudit initializes a new CommandAudit with the specified hash algorithm.
func NewAudit(hash TPMIAlgHash) CommandAudit {
return CommandAudit{
hash: hash,
digest: make([]byte, hash.Hash().Size()),
}
}

// Extend extends the audit digest with the given command and response.
func (a *CommandAudit) Extend(cmd Command, rsp Response) error {
cpHash, err := auditCPHash(a.hash, cmd)
if err != nil {
return err
}
rpHash, err := auditRPHash(a.hash, rsp)
if err != nil {
return err
}
h := a.hash.Hash().New()
h.Write(a.digest)
h.Write(cpHash)
h.Write(rpHash)
a.digest = h.Sum(nil)
return nil
}

// Digest returns the current digest of the audit.
func (a *CommandAudit) Digest() []byte {
return a.digest
}

// auditCPHash calculates the command parameter hash for a given command with
// the given hash algorithm. The command is assumed to not have any decrypt
// sessions.
func auditCPHash(h TPMIAlgHash, c Command) ([]byte, error) {
cc := c.Command()
names, err := cmdNames(c)
if err != nil {
return nil, err
}
parms, err := cmdParameters(c, nil)
if err != nil {
return nil, err
}
return cpHash(h, cc, names, parms), nil
}

// auditRPHash calculates the response parameter hash for a given response with
// the given hash algorithm. The command is assumed to be successful and to not
// have any encrypt sessions.
func auditRPHash(h TPMIAlgHash, r Response) ([]byte, error) {
cc := r.Response()
var parms bytes.Buffer
parameters := taggedMembers(reflect.ValueOf(r).Elem(), "handle", true)
for i, parameter := range parameters {
if err := marshal(&parms, parameter); err != nil {
return nil, fmt.Errorf("marshalling parameter %v: %w", i, err)
}
}
return rpHash(h, TPMRCSuccess, cc, parms.Bytes()), nil
}
129 changes: 129 additions & 0 deletions tpm2/direct/audit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package direct

import (
"bytes"
"testing"

"github.com/google/go-tpm-tools/simulator"
)

func TestAuditSession(t *testing.T) {
sim, err := simulator.Get()
if err != nil {
t.Fatalf("could not connect to TPM simulator: %v", err)
}
tpm := NewTPM(sim)
defer tpm.Close()

// Create the audit session
sess, cleanup, err := HMACSession(tpm, TPMAlgSHA256, 16, Audit())
if err != nil {
t.Fatalf("%v", err)
}
defer cleanup()

// Create the AK for audit
createAKCmd := CreatePrimaryCommand{
PrimaryHandle: AuthHandle{
Handle: TPMRHOwner,
},
InPublic: TPM2BPublic{
PublicArea: TPMTPublic{
Type: TPMAlgECC,
NameAlg: TPMAlgSHA256,
ObjectAttributes: TPMAObject{
FixedTPM: true,
STClear: false,
FixedParent: true,
SensitiveDataOrigin: true,
UserWithAuth: true,
AdminWithPolicy: false,
NoDA: true,
EncryptedDuplication: false,
Restricted: true,
Decrypt: false,
SignEncrypt: true,
},
Parameters: TPMUPublicParms{
ECCDetail: &TPMSECCParms{
Scheme: TPMTECCScheme{
Scheme: TPMAlgECDSA,
Details: TPMUAsymScheme{
ECDSA: &TPMSSigSchemeECDSA{
HashAlg: TPMAlgSHA256,
},
},
},
CurveID: TPMECCNistP256,
},
},
},
},
}
var createAKRsp CreatePrimaryResponse
if err := tpm.Execute(&createAKCmd, &createAKRsp); err != nil {
t.Fatalf("%v", err)
}
defer func() {
// Flush the AK
flushCmd := FlushContextCommand{
FlushHandle: createAKRsp.ObjectHandle,
}
var flushRsp FlushContextResponse
if err := tpm.Execute(&flushCmd, &flushRsp); err != nil {
t.Errorf("%v", err)
}
}()

audit := NewAudit(TPMAlgSHA256)
// Call GetCapability a bunch of times with the audit session and make sure it extends like
// we expect it to.
props := []TPMPT{
TPMPTFamilyIndicator,
TPMPTLevel,
TPMPTRevision,
TPMPTDayofYear,
TPMPTYear,
TPMPTManufacturer,
}
for _, prop := range props {
getCmd := GetCapabilityCommand{
Capability: TPMCapTPMProperties,
Property: uint32(prop),
PropertyCount: 1,
}
var getRsp GetCapabilityResponse
if err := tpm.Execute(&getCmd, &getRsp, sess); err != nil {
t.Fatalf("%v", err)
}
if err := audit.Extend(&getCmd, &getRsp); err != nil {
t.Fatalf("%v", err)
}
// Get the audit digest signed by the AK
getAuditCmd := GetSessionAuditDigestCommand{
PrivacyAdminHandle: AuthHandle{
Handle: TPMRHEndorsement,
},
SignHandle: AuthHandle{
Handle: createAKRsp.ObjectHandle,
},
SessionHandle: sess.Handle(),
QualifyingData: TPM2BData{[]byte("foobar")},
}
var getAuditRsp GetSessionAuditDigestResponse
if err := tpm.Execute(&getAuditCmd, &getAuditRsp); err != nil {
t.Errorf("%v", err)
}
// TODO check the signature with the AK pub
aud := getAuditRsp.AuditInfo.AttestationData.Attested.SessionAudit
if aud == nil {
t.Fatalf("got nil session audit attestation")
}
want := audit.Digest()
got := aud.SessionDigest.Buffer
if !bytes.Equal(want, got) {
t.Errorf("unexpected audit value:\ngot %x\nwant %x", got, want)
}
}

}
Loading