From 7c2f71c36a36cd49c6848b32fc97d81e781b1d02 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 20:37:02 +0000 Subject: [PATCH 1/9] Introduce Direct TPM2 API This commit introduces a new method of interacting with a TPM 2.0. Instead of plumbing each TPM API into one or more Go functions, this code defines structures for every TPM 2.0 command request and response. These map 1:1 with the actual command parameters comprehended by the TPM, so any invocation of any command is possible (once all the command structures are written). This commit introduces enough of the TPM 2.0 API surface to put together some interesting end-to-end tests, mostly around sealing. Another objective of the Direct API is to facilitate use of the TPM's session-based command transport features (e.g., audit and encryption sessions). See the test code for examples of how to easily use these APIs to, e.g., set up an EK-salted session for session-encrypted unseal. Change-Id: I1549dd596869d79ddd41ff3c5f9ffdadc9628ed4 --- go.mod | 3 +- tpm2/direct/audit.go | 77 +++ tpm2/direct/audit_test.go | 129 ++++ tpm2/direct/commands.go | 399 +++++++++++ tpm2/direct/constants.go | 603 +++++++++++++++++ tpm2/direct/crypto.go | 43 ++ tpm2/direct/ek_test.go | 237 +++++++ tpm2/direct/errors.go | 557 ++++++++++++++++ tpm2/direct/names.go | 11 + tpm2/direct/reflect.go | 1010 ++++++++++++++++++++++++++++ tpm2/direct/reflect_test.go | 208 ++++++ tpm2/direct/sealing_test.go | 375 +++++++++++ tpm2/direct/sessions.go | 982 +++++++++++++++++++++++++++ tpm2/direct/structures.go | 1237 +++++++++++++++++++++++++++++++++++ tpm2/direct/templates.go | 190 ++++++ tpm2/direct/wrappers.go | 10 + tpm2/kdf.go | 58 +- tpmutil/run.go | 65 +- 18 files changed, 6147 insertions(+), 47 deletions(-) create mode 100644 tpm2/direct/audit.go create mode 100644 tpm2/direct/audit_test.go create mode 100644 tpm2/direct/commands.go create mode 100644 tpm2/direct/constants.go create mode 100644 tpm2/direct/crypto.go create mode 100644 tpm2/direct/ek_test.go create mode 100644 tpm2/direct/errors.go create mode 100644 tpm2/direct/names.go create mode 100644 tpm2/direct/reflect.go create mode 100644 tpm2/direct/reflect_test.go create mode 100644 tpm2/direct/sealing_test.go create mode 100644 tpm2/direct/sessions.go create mode 100644 tpm2/direct/structures.go create mode 100644 tpm2/direct/templates.go create mode 100644 tpm2/direct/wrappers.go diff --git a/go.mod b/go.mod index 6a26af26..6a31b534 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/tpm2/direct/audit.go b/tpm2/direct/audit.go new file mode 100644 index 00000000..2fc0d260 --- /dev/null +++ b/tpm2/direct/audit.go @@ -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 +} diff --git a/tpm2/direct/audit_test.go b/tpm2/direct/audit_test.go new file mode 100644 index 00000000..2e4522e9 --- /dev/null +++ b/tpm2/direct/audit_test.go @@ -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) + } + } + +} diff --git a/tpm2/direct/commands.go b/tpm2/direct/commands.go new file mode 100644 index 00000000..5bcc2c82 --- /dev/null +++ b/tpm2/direct/commands.go @@ -0,0 +1,399 @@ +package direct + +import "encoding/binary" + +// AuthHandle is a convenience type to wrap an authorized handle. +type AuthHandle struct { + // The handle that is authorized. + // If zero, treated as TPM_RH_NULL. + Handle TPMIDHObject `gotpm:"nullable"` + // The Name of the object expected at the given handle value. + // If Name contains a nil buffer, the effective Name will be + // the big-endian UINT32 representation of Handle, as in + // Part 1, section 16 "Names" for PCRs, sessions, and + // permanent values. + Name TPM2BName `gotpm:"skip"` + // The session used to authorize the object. + // If the 'UserWithAuth' attribute is not set on the object, + // must be a Policy session. + // For ADMIN-role commands, if 'AdminWithPolicy' is set on + // the object, must be a Policy session. + // For DUP-role commands, must be a Policy session that + // sets the policy command code to TPM_CC_DUPLICATE. + // If nil, the effective Session will be a password session + // with NULL authorization. + Auth Session `gotpm:"skip"` +} + +// effectiveHandle returns the effective handle value. +// Returns TPM_RH_NULL if unset. +func (a *AuthHandle) effectiveHandle() TPMIDHObject { + if a.Handle != 0 { + return a.Handle + } + return TPMRHNull +} + +// effectiveName returns the effective Name. +// Returns the handle value as a name if unset. +func (a *AuthHandle) effectiveName() TPM2BName { + if len(a.Name.Buffer) > 0 { + return a.Name + } + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(a.effectiveHandle())) + return TPM2BName{buf} +} + +// effectiveAuth returns the effective auth session. +// Returns a NULL password session if unset. +func (a *AuthHandle) effectiveAuth() Session { + if a.Auth == nil { + return PasswordAuth(nil) + } + return a.Auth +} + +// Command is a placeholder interface for TPM command structures so that they +// can be easily distinguished from other types of structures. +type Command interface { + // The TPM command code associated with this command. + Command() TPMCC +} + +// Response is a placeholder interface for TPM response structures so that they +// can be easily distinguished from other types of structures. +// All implementations of this interface are pointers to structures, for +// settability. +// See https://go.dev/blog/laws-of-reflection +type Response interface { + // The TPM command code associated with this response. + Response() TPMCC +} + +// StartAuthSessionCommand is the input to TPM2_StartAuthSession. +// See definition in Part 3, Commands, section 11.1 +type StartAuthSessionCommand struct { + // handle of a loaded decrypt key used to encrypt salt + // may be TPM_RH_NULL + TPMKey TPMIDHObject `gotpm:"handle,nullable"` + // entity providing the authValue + // may be TPM_RH_NULL + Bind TPMIDHEntity `gotpm:"handle,nullable"` + // initial nonceCaller, sets nonceTPM size for the session + // shall be at least 16 octets + NonceCaller TPM2BNonce + // value encrypted according to the type of tpmKey + // If tpmKey is TPM_RH_NULL, this shall be the Empty Buffer. + EncryptedSalt TPM2BEncryptedSecret + // indicates the type of the session; simple HMAC or policy (including + // a trial policy) + SessionType TPMSE + // the algorithm and key size for parameter encryption + // may select TPM_ALG_NULL + Symmetric TPMTSymDef + // hash algorithm to use for the session + // Shall be a hash algorithm supported by the TPM and not TPM_ALG_NULL + AuthHash TPMIAlgHash +} + +func (_ *StartAuthSessionCommand) Command() TPMCC { return TPMCCStartAuthSession } + +// StartAuthSessionResponse is the response from TPM2_StartAuthSession. +type StartAuthSessionResponse struct { + // handle for the newly created session + SessionHandle TPMISHAuthSession `gotpm:"handle"` + // the initial nonce from the TPM, used in the computation of the sessionKey + NonceTPM TPM2BNonce +} + +func (_ *StartAuthSessionResponse) Response() TPMCC { return TPMCCStartAuthSession } + +// CreateCommand is the input to TPM2_Create. +// See definition in Part 3, Commands, section 12.1 +type CreateCommand struct { + // handle of parent for new object + ParentHandle AuthHandle `gotpm:"handle,auth"` + // the sensitive data + InSensitive TPM2BSensitiveCreate + // the public template + InPublic TPM2BPublic + // data that will be included in the creation data for this + // object to provide permanent, verifiable linkage between this + // object and some object owner data + OutsideInfo TPM2BData + // PCR that will be used in creation data + CreationPCR TPMLPCRSelection +} + +func (_ *CreateCommand) Command() TPMCC { return TPMCCCreate } + +// CreateResponse is the response from TPM2_Create. +type CreateResponse struct { + // the private portion of the object + OutPrivate TPM2BPrivate + // the public portion of the created object + OutPublic TPM2BPublic + // contains a TPMS_CREATION_DATA + CreationData TPM2BCreationData + // digest of creationData using nameAlg of outPublic + CreationHash TPM2BDigest + // ticket used by TPM2_CertifyCreation() to validate that the + // creation data was produced by the TPM + CreationTicket TPMTTKCreation +} + +func (_ *CreateResponse) Response() TPMCC { return TPMCCCreate } + +// LoadCommand is the input to TPM2_Load. +// See definition in Part 3, Commands, section 12.2 +type LoadCommand struct { + // handle of parent for new object + ParentHandle AuthHandle `gotpm:"handle,auth"` + // the private portion of the object + InPrivate TPM2BPrivate + // the public portion of the object + InPublic TPM2BPublic +} + +func (_ *LoadCommand) Command() TPMCC { return TPMCCLoad } + +// LoadResponse is the response from TPM2_Load. +type LoadResponse struct { + // handle of type TPM_HT_TRANSIENT for loaded object + ObjectHandle TPMHandle `gotpm:"handle"` + // Name of the loaded object + Name TPM2BName +} + +func (_ *LoadResponse) Response() TPMCC { return TPMCCLoad } + +// UnsealCommand is the input to TPM2_Unseal. +// See definition in Part 3, Commands, section 12.7 +type UnsealCommand struct { + ItemHandle AuthHandle `gotpm:"handle,auth"` +} + +func (_ *UnsealCommand) Command() TPMCC { return TPMCCUnseal } + +// UnsealResponse is the response from TPM2_Unseal. +type UnsealResponse struct { + OutData TPM2BSensitiveData +} + +func (_ *UnsealResponse) Response() TPMCC { return TPMCCUnseal } + +// QuoteCommand is the input to TPM2_Quote. +// See definition in Part 3, Commands, section 18.4 +type QuoteCommand struct { + // handle of key that will perform signature + SignHandle AuthHandle `gotpm:"handle,auth"` + // data supplied by the caller + QualifyingData TPM2BData + // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL + InScheme TPMTSigScheme + // PCR set to quote + PCRSelect TPMLPCRSelection +} + +func (_ *QuoteCommand) Command() TPMCC { return TPMCCQuote } + +// QuoteResponse is the response from TPM2_Quote. +type QuoteResponse struct { + // the quoted information + Quoted TPM2BAttest + // the signature over quoted + Signature TPMTSignature +} + +func (_ *QuoteResponse) Response() TPMCC { return TPMCCQuote } + +// GetSessionAuditDigestCommand is the input to TPM2_GetSessionAuditDigest. +// See definition in Part 3, Commands, section 18.5 +type GetSessionAuditDigestCommand struct { + // handle of the privacy administrator (TPM_RH_ENDORSEMENT) + PrivacyAdminHandle AuthHandle `gotpm:"handle,auth"` + // handle of the signing key + SignHandle AuthHandle `gotpm:"handle,auth"` + // handle of the audit session + SessionHandle TPMISHHMAC `gotpm:"handle"` + // user-provided qualifying data – may be zero-length + QualifyingData TPM2BData + // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL + InScheme TPMTSigScheme +} + +func (_ *GetSessionAuditDigestCommand) Command() TPMCC { return TPMCCGetSessionAuditDigest } + +// GetSessionAuditDigestResponse is the response from +// TPM2_GetSessionAuditDigest. +type GetSessionAuditDigestResponse struct { + // the audit information that was signed + AuditInfo TPM2BAttest + // the signature over auditInfo + Signature TPMTSignature +} + +func (_ *GetSessionAuditDigestResponse) Response() TPMCC { return TPMCCGetSessionAuditDigest } + +// PCRExtendCommand is the input to TPM2_PCR_Extend. +// See definition in Part 3, Commands, section 22.2 +type PCRExtendCommand struct { + // handle of the PCR + PCRHandle AuthHandle `gotpm:"handle,auth"` + // list of tagged digest values to be extended + Digests TPMLDigestValues +} + +func (_ *PCRExtendCommand) Command() TPMCC { return TPMCCPCRExtend } + +// PCRExtendResponse is the response from TPM2_PCR_Extend. +type PCRExtendResponse struct { +} + +func (_ *PCRExtendResponse) Response() TPMCC { return TPMCCPCRExtend } + +// PCREventCommand is the input to TPM2_PCR_Event. +// See definition in Part 3, Commands, section 22.3 +type PCREventCommand struct { + // Handle of the PCR + PCRHandle AuthHandle `gotpm:"handle,auth"` + // Event data in sized buffer + EventData TPM2BEvent +} + +func (_ *PCREventCommand) Command() TPMCC { return TPMCCPCREvent } + +// PCREventResponse is the response from TPM2_PCR_Event. +type PCREventResponse struct { +} + +func (_ *PCREventResponse) Response() TPMCC { return TPMCCPCREvent } + +// PCRReadCommand is the input to TPM2_PCR_Read. +// See definition in Part 3, Commands, section 22.4 +type PCRReadCommand struct { + // The selection of PCR to read + PCRSelectionIn TPMLPCRSelection +} + +func (_ *PCRReadCommand) Command() TPMCC { return TPMCCPCRRead } + +// PCRReadResponse is the response from TPM2_PCR_Read. +type PCRReadResponse struct { + // the current value of the PCR update counter + PCRUpdateCounter uint32 + // the PCR in the returned list + PCRSelectionOut TPMLPCRSelection + // the contents of the PCR indicated in pcrSelectOut-> pcrSelection[] as tagged digests + PCRValues TPMLDigest +} + +func (_ *PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } + +// PolicySecret is the input to TPM2_PolicySecret. +// See definition in Part 3, Commands, section 23.4 +type PolicySecretCommand struct { + // handle for an entity providing the authorization + AuthHandle AuthHandle `gotpm:"handle,auth"` + // handle for the policy session being extended + PolicySession TPMISHPolicy `gotpm:"handle"` + // the policy nonce for the session + NonceTPM TPM2BNonce + // digest of the command parameters to which this authorization is limited + CPHashA TPM2BDigest + // a reference to a policy relating to the authorization – may be the Empty Buffer + PolicyRef TPM2BNonce + // time when authorization will expire, measured in seconds from the time + // that nonceTPM was generated + Expiration int32 +} + +func (_ *PolicySecretCommand) Command() TPMCC { return TPMCCPolicySecret } + +// PolicySecretResponse is the response from TPM2_PolicySecret. +type PolicySecretResponse struct { + // implementation-specific time value used to indicate to the TPM when the ticket expires + Timeout TPM2BTimeout + // produced if the command succeeds and expiration in the command was non-zero + PolicyTicket TPMTTKAuth +} + +func (_ *PolicySecretResponse) Response() TPMCC { return TPMCCPolicySecret } + +// CreatePrimaryCommand is the input to TPM2_CreatePrimary. +// See definition in Part 3, Commands, section 24.1 +type CreatePrimaryCommand struct { + // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, + // or TPM_RH_NULL + PrimaryHandle AuthHandle `gotpm:"handle,auth"` + // the sensitive data + InSensitive TPM2BSensitiveCreate + // the public template + InPublic TPM2BPublic + // data that will be included in the creation data for this + // object to provide permanent, verifiable linkage between this + // object and some object owner data + OutsideInfo TPM2BData + // PCR that will be used in creation data + CreationPCR TPMLPCRSelection +} + +func (_ *CreatePrimaryCommand) Command() TPMCC { return TPMCCCreatePrimary } + +// CreatePrimaryResponse is the response from TPM2_CreatePrimary. +type CreatePrimaryResponse struct { + // handle of type TPM_HT_TRANSIENT for created Primary Object + ObjectHandle TPMHandle `gotpm:"handle"` + // the public portion of the created object + OutPublic TPM2BPublic + // contains a TPMS_CREATION_DATA + CreationData TPM2BCreationData + // digest of creationData using nameAlg of outPublic + CreationHash TPM2BDigest + // ticket used by TPM2_CertifyCreation() to validate that the + // creation data was produced by the TPM + CreationTicket TPMTTKCreation + // the name of the created object + Name TPM2BName +} + +func (_ *CreatePrimaryResponse) Response() TPMCC { return TPMCCCreatePrimary } + +// FlushContextCommand is the input to TPM2_FlushContext. +// See definition in Part 3, Commands, section 28.4 +type FlushContextCommand struct { + // the handle of the item to flush + FlushHandle TPMIDHContext +} + +func (_ *FlushContextCommand) Command() TPMCC { return TPMCCFlushContext } + +// FlushContextResponse is the response from TPM2_FlushContext. +type FlushContextResponse struct { +} + +func (_ *FlushContextResponse) Response() TPMCC { return TPMCCFlushContext } + +// GetCapabilityCommand is the input to TPM2_GetCapability. +// See definition in Part 3, Commands, section 30.2 +type GetCapabilityCommand struct { + // group selection; determines the format of the response + Capability TPMCap + // further definition of information + Property uint32 + // number of properties of the indicated type to return + PropertyCount uint32 +} + +func (_ *GetCapabilityCommand) Command() TPMCC { return TPMCCGetCapability } + +// GetCapabilityResponse is the response from TPM2_GetCapability. +type GetCapabilityResponse struct { + // flag to indicate if there are more values of this type + MoreData TPMIYesNo + // the capability data + CapabilityData TPMSCapabilityData +} + +func (_ *GetCapabilityResponse) Response() TPMCC { return TPMCCGetCapability } diff --git a/tpm2/direct/constants.go b/tpm2/direct/constants.go new file mode 100644 index 00000000..42132529 --- /dev/null +++ b/tpm2/direct/constants.go @@ -0,0 +1,603 @@ +package direct + +import ( + "crypto" + "crypto/elliptic" + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "fmt" +) + +// TPMGenerated values come from Part 2: Structures, section 6.2. +const ( + TPMGeneratedValue TPMGenerated = 0xff544347 +) + +// TPMAlgID values come from Part 2: Structures, section 6.3. +const ( + TPMAlgRSA TPMAlgID = 0x0001 + TPMAlgTDES TPMAlgID = 0x0003 + TPMAlgSHA TPMAlgID = 0x0004 + TPMAlgSHA1 = TPMAlgSHA + TPMAlgHMAC TPMAlgID = 0x0005 + TPMAlgAES TPMAlgID = 0x0006 + TPMAlgMGF1 TPMAlgID = 0x0007 + TPMAlgKeyedHash TPMAlgID = 0x0008 + TPMAlgXOR TPMAlgID = 0x000A + TPMAlgSHA256 TPMAlgID = 0x000B + TPMAlgSHA384 TPMAlgID = 0x000C + TPMAlgSHA512 TPMAlgID = 0x000D + TPMAlgNull TPMAlgID = 0x0010 + TPMAlgSM3256 TPMAlgID = 0x0012 + TPMAlgSM4 TPMAlgID = 0x0013 + TPMAlgRSASSA TPMAlgID = 0x0014 + TPMAlgRSAES TPMAlgID = 0x0015 + TPMAlgRSAPSS TPMAlgID = 0x0016 + TPMAlgOAEP TPMAlgID = 0x0017 + TPMAlgECDSA TPMAlgID = 0x0018 + TPMAlgECDH TPMAlgID = 0x0019 + TPMAlgECDAA TPMAlgID = 0x001A + TPMAlgSM2 TPMAlgID = 0x001B + TPMAlgECSchnorr TPMAlgID = 0x001C + TPMAlgECMQV TPMAlgID = 0x001D + TPMAlgKDF1SP80056A TPMAlgID = 0x0020 + TPMAlgKDF2 TPMAlgID = 0x0021 + TPMAlgKDF1SP800108 TPMAlgID = 0x0022 + TPMAlgECC TPMAlgID = 0x0023 + TPMAlgSymCipher TPMAlgID = 0x0025 + TPMAlgCamellia TPMAlgID = 0x0026 + TPMAlgSHA3256 TPMAlgID = 0x0027 + TPMAlgSHA3384 TPMAlgID = 0x0028 + TPMAlgSHA3512 TPMAlgID = 0x0029 + TPMAlgCTR TPMAlgID = 0x0040 + TPMAlgOFB TPMAlgID = 0x0041 + TPMAlgCBC TPMAlgID = 0x0042 + TPMAlgCFB TPMAlgID = 0x0043 + TPMAlgECB TPMAlgID = 0x0044 +) + +// Hash returns the crypto.Hash associated with a TPMIAlgHash. +func (a TPMIAlgHash) Hash() crypto.Hash { + switch TPMAlgID(a) { + case TPMAlgSHA1: + return crypto.SHA1 + case TPMAlgSHA256: + return crypto.SHA256 + case TPMAlgSHA384: + return crypto.SHA384 + case TPMAlgSHA512: + return crypto.SHA512 + } + panic(fmt.Sprintf("unsupported hash algorithm: %v", a)) +} + +// TPMECCCurve values come from Part 2: Structures, section 6.4. +const ( + TPMECCNone TPMECCCurve = 0x0000 + TPMECCNistP192 TPMECCCurve = 0x0001 + TPMECCNistP224 TPMECCCurve = 0x0002 + TPMECCNistP256 TPMECCCurve = 0x0003 + TPMECCNistP384 TPMECCCurve = 0x0004 + TPMECCNistP521 TPMECCCurve = 0x0005 + TPMECCBNP256 TPMECCCurve = 0x0010 + TPMECCBNP638 TPMECCCurve = 0x0011 + TPMECCSM2P256 TPMECCCurve = 0x0020 +) + +// Curve returns the elliptic.Curve associated with a TPMECCCurve. +func (c TPMECCCurve) Curve() (elliptic.Curve, error) { + switch c { + case TPMECCNistP224: + return elliptic.P224(), nil + case TPMECCNistP256: + return elliptic.P256(), nil + case TPMECCNistP384: + return elliptic.P384(), nil + case TPMECCNistP521: + return elliptic.P521(), nil + default: + return nil, fmt.Errorf("unsupported ECC curve: %v", c) + } +} + +// TPMCC values come from Part 2: Structures, section 6.5.2. +const ( + TPMCCNVUndefineSpaceSpecial TPMCC = 0x0000011F + TPMCCEvictControl TPMCC = 0x00000120 + TPMCCHierarchyControl TPMCC = 0x00000121 + TPMCCNVUndefineSpace TPMCC = 0x00000122 + TPMCCChangeEPS TPMCC = 0x00000124 + TPMCCChangePPS TPMCC = 0x00000125 + TPMCCClear TPMCC = 0x00000126 + TPMCCClearControl TPMCC = 0x00000127 + TPMCCClockSet TPMCC = 0x00000128 + TPMCCHierarchyChanegAuth TPMCC = 0x00000129 + TPMCCNVDefineSpace TPMCC = 0x0000012A + TPMCCPCRAllocate TPMCC = 0x0000012B + TPMCCPCRSetAuthPolicy TPMCC = 0x0000012C + TPMCCPPCommands TPMCC = 0x0000012D + TPMCCSetPrimaryPolicy TPMCC = 0x0000012E + TPMCCFieldUpgradeStart TPMCC = 0x0000012F + TPMCCClockRateAdjust TPMCC = 0x00000130 + TPMCCCreatePrimary TPMCC = 0x00000131 + TPMCCNVGlobalWriteLock TPMCC = 0x00000132 + TPMCCGetCommandAuditDigest TPMCC = 0x00000133 + TPMCCNVIncrement TPMCC = 0x00000134 + TPMCCNVSetBits TPMCC = 0x00000135 + TPMCCNVExtend TPMCC = 0x00000136 + TPMCCNVWrite TPMCC = 0x00000137 + TPMCCNVWriteLock TPMCC = 0x00000138 + TPMCCDictionaryAttackLockReset TPMCC = 0x00000139 + TPMCCDictionaryAttackParameters TPMCC = 0x0000013A + TPMCCNVChangeAuth TPMCC = 0x0000013B + TPMCCPCREvent TPMCC = 0x0000013C + TPMCCPCRReset TPMCC = 0x0000013D + TPMCCSequenceComplete TPMCC = 0x0000013E + TPMCCSetAlgorithmSet TPMCC = 0x0000013F + TPMCCSetCommandCodeAuditStatus TPMCC = 0x00000140 + TPMCCFieldUpgradeData TPMCC = 0x00000141 + TPMCCIncrementalSelfTest TPMCC = 0x00000142 + TPMCCSelfTest TPMCC = 0x00000143 + TPMCCStartup TPMCC = 0x00000144 + TPMCCShutdown TPMCC = 0x00000145 + TPMCCStirRandom TPMCC = 0x00000146 + TPMCCActivateCredential TPMCC = 0x00000147 + TPMCCCertify TPMCC = 0x00000148 + TPMCCPolicyNV TPMCC = 0x00000149 + TPMCCCertifyCreation TPMCC = 0x0000014A + TPMCCDuplicate TPMCC = 0x0000014B + TPMCCGetTime TPMCC = 0x0000014C + TPMCCGetSessionAuditDigest TPMCC = 0x0000014D + TPMCCNVRead TPMCC = 0x0000014E + TPMCCNVReadLock TPMCC = 0x0000014F + TPMCCObjectChangeAuth TPMCC = 0x00000150 + TPMCCPolicySecret TPMCC = 0x00000151 + TPMCCRewrap TPMCC = 0x00000152 + TPMCCCreate TPMCC = 0x00000153 + TPMCCECDHZGen TPMCC = 0x00000154 + TPMCCHMAC TPMCC = 0x00000155 + TPMCCMAC TPMCC = TPMCCHMAC + TPMCCImport TPMCC = 0x00000156 + TPMCCLoad TPMCC = 0x00000157 + TPMCCQuote TPMCC = 0x00000158 + TPMCCRSADecrypt TPMCC = 0x00000159 + TPMCCHMACStart TPMCC = 0x0000015B + TPMCCMACStart TPMCC = TPMCCHMACStart + TPMCCSequenceUpdate TPMCC = 0x0000015C + TPMCCSign TPMCC = 0x0000015D + TPMCCUnseal TPMCC = 0x0000015E + TPMCCPolicySigned TPMCC = 0x00000160 + TPMCCContextLoad TPMCC = 0x00000161 + TPMCCContextSave TPMCC = 0x00000162 + TPMCCECDHKeyGen TPMCC = 0x00000163 + TPMCCEncryptDecrypt TPMCC = 0x00000164 + TPMCCFlushContext TPMCC = 0x00000165 + TPMCCLoadExternal TPMCC = 0x00000167 + TPMCCMakeCredential TPMCC = 0x00000168 + TPMCCNVReadPublic TPMCC = 0x00000169 + TPMCCPolicyAuthorize TPMCC = 0x0000016A + TPMCCPolicyAuthValue TPMCC = 0x0000016B + TPMCCPolicyCommandCode TPMCC = 0x0000016C + TPMCCPolicyCounterTimer TPMCC = 0x0000016D + TPMCCPolicyCpHash TPMCC = 0x0000016E + TPMCCPolicyLocality TPMCC = 0x0000016F + TPMCCPolicyNameHash TPMCC = 0x00000170 + TPMCCPolicyOR TPMCC = 0x00000171 + TPMCCPolicyTicket TPMCC = 0x00000172 + TPMCCReadPublic TPMCC = 0x00000173 + TPMCCRSAEncrypt TPMCC = 0x00000174 + TPMCCStartAuthSession TPMCC = 0x00000176 + TPMCCVerifySignature TPMCC = 0x00000177 + TPMCCECCParameters TPMCC = 0x00000178 + TPMCCFirmwareRead TPMCC = 0x00000179 + TPMCCGetCapability TPMCC = 0x0000017A + TPMCCGetRandom TPMCC = 0x0000017B + TPMCCGetTestResult TPMCC = 0x0000017C + TPMCCHash TPMCC = 0x0000017D + TPMCCPCRRead TPMCC = 0x0000017E + TPMCCPolicyPCR TPMCC = 0x0000017F + TPMCCPolicyRestart TPMCC = 0x00000180 + TPMCCReadClock TPMCC = 0x00000181 + TPMCCPCRExtend TPMCC = 0x00000182 + TPMCCPCRSetAuthValue TPMCC = 0x00000183 + TPMCCNVCertify TPMCC = 0x00000184 + TPMCCEventSequenceComplete TPMCC = 0x00000185 + TPMCCHashSequenceStart TPMCC = 0x00000186 + TPMCCPolicyPhysicalPresence TPMCC = 0x00000187 + TPMCCPolicyDuplicationSelect TPMCC = 0x00000188 + TPMCCPolicyGetDigest TPMCC = 0x00000189 + TPMCCTestParams TPMCC = 0x0000018A + TPMCCCommit TPMCC = 0x0000018B + TPMCCPolicyPassword TPMCC = 0x0000018C + TPMCCZGen2Phase TPMCC = 0x0000018D + TPMCCECEphemeral TPMCC = 0x0000018E + TPMCCPolicyNvWritten TPMCC = 0x0000018F + TPMCCPolicyTemplate TPMCC = 0x00000190 + TPMCCCreateLoaded TPMCC = 0x00000191 + TPMCCPolicyAuthorizeNV TPMCC = 0x00000192 + TPMCCEncryptDecrypt2 TPMCC = 0x00000193 + TPMCCACGetCapability TPMCC = 0x00000194 + TPMCCACSend TPMCC = 0x00000195 + TPMCCPolicyACSendSelect TPMCC = 0x00000196 + TPMCCCertifyX509 TPMCC = 0x00000197 + TPMCCACTSetTimeout TPMCC = 0x00000198 +) + +// TPMRC values come from Part 2: Structures, section 6.6.3. +const ( + TPMRCSuccess TPMRC = 0x00000000 + rcVer1 TPMRC = 0x00000100 + // FMT0 error codes + TPMRCInitialize TPMRC = rcVer1 + 0x000 + TPMRCFailure TPMRC = rcVer1 + 0x001 + TPMRCSequence TPMRC = rcVer1 + 0x003 + TPMRCPrivate TPMRC = rcVer1 + 0x00B + TPMRCHMAC TPMRC = rcVer1 + 0x019 + TPMRCDisabled TPMRC = rcVer1 + 0x020 + TPMRCExclusive TPMRC = rcVer1 + 0x021 + TPMRCAuthType TPMRC = rcVer1 + 0x024 + TPMRCAuthMissing TPMRC = rcVer1 + 0x025 + TPMRCPolicy TPMRC = rcVer1 + 0x026 + TPMRCPCR TPMRC = rcVer1 + 0x027 + TPMRCPCRChanged TPMRC = rcVer1 + 0x028 + TPMRCUpgrade TPMRC = rcVer1 + 0x02D + TPMRCTooManyContexts TPMRC = rcVer1 + 0x02E + TPMRCAuthUnavailable TPMRC = rcVer1 + 0x02F + TPMRCReboot TPMRC = rcVer1 + 0x030 + TPMRCUnbalanced TPMRC = rcVer1 + 0x031 + TPMRCCommandSize TPMRC = rcVer1 + 0x042 + TPMRCCommandCode TPMRC = rcVer1 + 0x043 + TPMRCAuthSize TPMRC = rcVer1 + 0x044 + TPMRCAuthContext TPMRC = rcVer1 + 0x045 + TPMRCNVRange TPMRC = rcVer1 + 0x046 + TPMRCNVSize TPMRC = rcVer1 + 0x047 + TPMRCNVLocked TPMRC = rcVer1 + 0x048 + TPMRCNVAuthorization TPMRC = rcVer1 + 0x049 + TPMRCNVUninitialized TPMRC = rcVer1 + 0x04A + TPMRCNVSpace TPMRC = rcVer1 + 0x04B + TPMRCNVDefined TPMRC = rcVer1 + 0x04C + TPMRCBadContext TPMRC = rcVer1 + 0x050 + TPMRCCPHash TPMRC = rcVer1 + 0x051 + TPMRCParent TPMRC = rcVer1 + 0x052 + TPMRCNeedsTest TPMRC = rcVer1 + 0x053 + TPMRCNoResult TPMRC = rcVer1 + 0x054 + TPMRCSensitive TPMRC = rcVer1 + 0x055 + rcFmt1 TPMRC = 0x00000080 + // FMT1 error codes + TPMRCAsymmetric TPMRC = rcFmt1 + 0x001 + TPMRCAttributes TPMRC = rcFmt1 + 0x002 + TPMRCHash TPMRC = rcFmt1 + 0x003 + TPMRCValue TPMRC = rcFmt1 + 0x004 + TPMRCHierarchy TPMRC = rcFmt1 + 0x005 + TPMRCKeySize TPMRC = rcFmt1 + 0x007 + TPMRCMGF TPMRC = rcFmt1 + 0x008 + TPMRCMode TPMRC = rcFmt1 + 0x009 + TPMRCType TPMRC = rcFmt1 + 0x00A + TPMRCHandle TPMRC = rcFmt1 + 0x00B + TPMRCKDF TPMRC = rcFmt1 + 0x00C + TPMRCRange TPMRC = rcFmt1 + 0x00D + TPMRCAuthFail TPMRC = rcFmt1 + 0x00E + TPMRCNonce TPMRC = rcFmt1 + 0x00F + TPMRCPP TPMRC = rcFmt1 + 0x010 + TPMRCScheme TPMRC = rcFmt1 + 0x012 + TPMRCSize TPMRC = rcFmt1 + 0x015 + TPMRCSymmetric TPMRC = rcFmt1 + 0x016 + TPMRCTag TPMRC = rcFmt1 + 0x017 + TPMRCSelector TPMRC = rcFmt1 + 0x018 + TPMRCInsufficient TPMRC = rcFmt1 + 0x01A + TPMRCSignature TPMRC = rcFmt1 + 0x01B + TPMRCKey TPMRC = rcFmt1 + 0x01C + TPMRCPolicyFail TPMRC = rcFmt1 + 0x01D + TPMRCIntegrity TPMRC = rcFmt1 + 0x01F + TPMRCTicket TPMRC = rcFmt1 + 0x020 + TPMRCReservedBits TPMRC = rcFmt1 + 0x021 + TPMRCBadAuth TPMRC = rcFmt1 + 0x022 + TPMRCExpired TPMRC = rcFmt1 + 0x023 + TPMRCPolicyCC TPMRC = rcFmt1 + 0x024 + TPMRCBinding TPMRC = rcFmt1 + 0x025 + TPMRCCurve TPMRC = rcFmt1 + 0x026 + TPMRCECCPoint TPMRC = rcFmt1 + 0x027 + // Warnings + rcWarn TPMRC = 0x00000900 + TPMRCContextGap TPMRC = rcWarn + 0x001 + TPMRCObjectMemory TPMRC = rcWarn + 0x002 + TPMRCSessionMemory TPMRC = rcWarn + 0x003 + TPMRCMemory TPMRC = rcWarn + 0x004 + TPMRCSessionHandles TPMRC = rcWarn + 0x005 + TPMRCObjectHandles TPMRC = rcWarn + 0x006 + TPMRCLocality TPMRC = rcWarn + 0x007 + TPMRCYielded TPMRC = rcWarn + 0x008 + TPMRCCanceled TPMRC = rcWarn + 0x009 + TPMRCTesting TPMRC = rcWarn + 0x00A + TPMRCReferenceH0 TPMRC = rcWarn + 0x010 + TPMRCReferenceH1 TPMRC = rcWarn + 0x011 + TPMRCReferenceH2 TPMRC = rcWarn + 0x012 + TPMRCReferenceH3 TPMRC = rcWarn + 0x013 + TPMRCReferenceH4 TPMRC = rcWarn + 0x014 + TPMRCReferenceH5 TPMRC = rcWarn + 0x015 + TPMRCReferenceH6 TPMRC = rcWarn + 0x016 + TPMRCReferenceS0 TPMRC = rcWarn + 0x018 + TPMRCReferenceS1 TPMRC = rcWarn + 0x019 + TPMRCReferenceS2 TPMRC = rcWarn + 0x01A + TPMRCReferenceS3 TPMRC = rcWarn + 0x01B + TPMRCReferenceS4 TPMRC = rcWarn + 0x01C + TPMRCReferenceS5 TPMRC = rcWarn + 0x01D + TPMRCReferenceS6 TPMRC = rcWarn + 0x01E + TPMRCNVRate TPMRC = rcWarn + 0x020 + TPMRCLockout TPMRC = rcWarn + 0x021 + TPMRCRetry TPMRC = rcWarn + 0x022 + TPMRCNVUnavailable TPMRC = rcWarn + 0x023 + rcP TPMRC = 0x00000040 + rcS TPMRC = 0x00000800 +) + +// TPMST values come from Part 2: Structures, section 6.9. +const ( + TPMSTRspCommand TPMST = 0x00C4 + TPMSTNull TPMST = 0x8000 + TPMSTNoSessions TPMST = 0x8001 + TPMSTSessions TPMST = 0x8002 + TPMSTAttestNV TPMST = 0x8014 + TPMSTAttestCommandAudit TPMST = 0x8015 + TPMSTAttestSessionAudit TPMST = 0x8016 + TPMSTAttestCertify TPMST = 0x8017 + TPMSTAttestQuote TPMST = 0x8018 + TPMSTAttestTime TPMST = 0x8019 + TPMSTAttestCreation TPMST = 0x801A + TPMSTAttestNVDigest TPMST = 0x801C + TPMSTCreation TPMST = 0x8021 + TPMSTVerified TPMST = 0x8022 + TPMSTAuthSecret TPMST = 0x8023 + TPMSTHashCheck TPMST = 0x8024 + TPMSTAuthSigned TPMST = 0x8025 + TPMSTFuManifest TPMST = 0x8029 +) + +// TPMSE values come from Part 2: Structures, section 6.11. +const ( + TPMSEHMAC TPMSE = 0x00 + TPMSEPolicy TPMSE = 0x01 + TPMXETrial TPMSE = 0x03 +) + +// TPMCap values come from Part 2: Structures, section 6.12. +const ( + TPMCapAlgs TPMCap = 0x00000000 + TPMCapHandles TPMCap = 0x00000001 + TPMCapCommands TPMCap = 0x00000002 + TPMCapPPCommands TPMCap = 0x00000003 + TPMCapAuditCommands TPMCap = 0x00000004 + TPMCapPCRs TPMCap = 0x00000005 + TPMCapTPMProperties TPMCap = 0x00000006 + TPMCapPCRProperties TPMCap = 0x00000007 + TPMCapECCCurves TPMCap = 0x00000008 + TPMCapAuthPolicies TPMCap = 0x00000009 + TPMCapACT TPMCap = 0x0000000A +) + +// TPMPTFamilyIndicator values come from Part 2: Structures, section 6.13. +const ( + // a 4-octet character string containing the TPM Family value + // (TPM_SPEC_FAMILY) + TPMPTFamilyIndicator TPMPT = 0x00000100 + // the level of the specification + TPMPTLevel TPMPT = 0x00000101 + // the specification Revision times 100 + TPMPTRevision TPMPT = 0x00000102 + // the specification day of year using TCG calendar + TPMPTDayofYear TPMPT = 0x00000103 + // the specification year using the CE + TPMPTYear TPMPT = 0x00000104 + // the vendor ID unique to each TPM manufacturer + TPMPTManufacturer TPMPT = 0x00000105 + // the first four characters of the vendor ID string + TPMPTVendorString1 TPMPT = 0x00000106 + // the second four characters of the vendor ID string + TPMPTVendorString2 TPMPT = 0x00000107 + // the third four characters of the vendor ID string + TPMPTVendorString3 TPMPT = 0x00000108 + // the fourth four characters of the vendor ID sting + TPMPTVendorString4 TPMPT = 0x00000109 + // vendor-defined value indicating the TPM model + TPMPTVendorTPMType TPMPT = 0x0000010A + // the most-significant 32 bits of a TPM vendor-specific value + // indicating the version number of the firmware. + TPMPTFirmwareVersion1 TPMPT = 0x0000010B + // the least-significant 32 bits of a TPM vendor-specific value + // indicating the version number of the firmware. + TPMPTFirmwareVersion2 TPMPT = 0x0000010C + // the maximum size of a parameter TPM2B_MAX_BUFFER) + TPMPTInputBuffer TPMPT = 0x0000010D + // the minimum number of transient objects that can be held in TPM RAM + TPMPTHRTransientMin TPMPT = 0x0000010E + // the minimum number of persistent objects that can be held in TPM NV + // memory + TPMPTHRPersistentMin TPMPT = 0x0000010F + // the minimum number of authorization sessions that can be held in TPM + // RAM + TPMPTHRLoadedMin TPMPT = 0x00000110 + // the number of authorization sessions that may be active at a time + TPMPTActiveSessionsMax TPMPT = 0x00000111 + // the number of PCR implemented + TPMPTPCRCount TPMPT = 0x00000112 + // the minimum number of octets in a TPMS_PCR_SELECT.sizeOfSelect + TPMPTPCRSelectMin TPMPT = 0x00000113 + // the maximum allowed difference (unsigned) between the contextID + // values of two saved session contexts + TPMPTContextGapMax TPMPT = 0x00000114 + // skipped + skippedTPMPT115 TPMPT = 0x00000115 + // the maximum number of NV Indexes that are allowed to have the + // TPM_NT_COUNTER attribute + TPMPTNVCountersMax TPMPT = 0x00000116 + // the maximum size of an NV Index data area + TPMPTNVIndexMax TPMPT = 0x00000117 + // a TPMA_MEMORY indicating the memory management method for the TPM + TPMPTMemory TPMPT = 0x00000118 + // interval, in milliseconds, between updates to the copy of + // TPMS_CLOCK_INFO.clock in NV + TPMPTClockUpdate TPMPT = 0x00000119 + // the algorithm used for the integrity HMAC on saved contexts and for + // hashing the fuData of TPM2_FirmwareRead() + TPMPTContextHash TPMPT = 0x0000011A + // TPM_ALG_ID, the algorithm used for encryption of saved contexts + TPMPTContextSym TPMPT = 0x0000011B + // TPM_KEY_BITS, the size of the key used for encryption of saved + // contexts + TPMPTContextSymSize TPMPT = 0x0000011C + // the modulus - 1 of the count for NV update of an orderly counter + TPMPTOrderlyCount TPMPT = 0x0000011D + // the maximum value for commandSize in a command + TPMPTMaxCommandSize TPMPT = 0x0000011E + // the maximum value for responseSize in a response + TPMPTMaxResponseSize TPMPT = 0x0000011F + // the maximum size of a digest that can be produced by the TPM + TPMPTMaxDigest TPMPT = 0x00000120 + // the maximum size of an object context that will be returned by + // TPM2_ContextSave + TPMPTMaxObjectContext TPMPT = 0x00000121 + // the maximum size of a session context that will be returned by + // TPM2_ContextSave + TPMPTMaxSessionContext TPMPT = 0x00000122 + // platform-specific family (a TPM_PS value)(see Table 25) + TPMPTPSFamilyIndicator TPMPT = 0x00000123 + // the level of the platform-specific specification + TPMPTPSLevel TPMPT = 0x00000124 + // a platform specific value + TPMPTPSRevision TPMPT = 0x00000125 + // the platform-specific TPM specification day of year using TCG + // calendar + TPMPTPSDayOfYear TPMPT = 0x00000126 + // the platform-specific TPM specification year using the CE + TPMPTPSYear TPMPT = 0x00000127 + // the number of split signing operations supported by the TPM + TPMPTSplitMax TPMPT = 0x00000128 + // total number of commands implemented in the TPM + TPMPTTotalCommands TPMPT = 0x00000129 + // number of commands from the TPM library that are implemented + TPMPTLibraryCommands TPMPT = 0x0000012A + // number of vendor commands that are implemented + TPMPTVendorCommands TPMPT = 0x0000012B + // the maximum data size in one NV write, NV read, NV extend, or NV + // certify command + TPMPTNVBufferMax TPMPT = 0x0000012C + // a TPMA_MODES value, indicating that the TPM is designed for these + // modes. + TPMPTModes TPMPT = 0x0000012D + // the maximum size of a TPMS_CAPABILITY_DATA structure returned in + // TPM2_GetCapability(). + TPMPTMaxCapBuffer TPMPT = 0x0000012E + // TPMA_PERMANENT + TPMPTPermanent TPMPT = 0x00000200 + // TPMA_STARTUP_CLEAR + TPMPTStartupClear TPMPT = 0x00000201 + // the number of NV Indexes currently defined + TPMPTHRNVIndex TPMPT = 0x00000202 + // the number of authorization sessions currently loaded into TPM RAM + TPMPTHRLoaded TPMPT = 0x00000203 + // the number of additional authorization sessions, of any type, that + // could be loaded into TPM RAM + TPMPTHRLoadedAvail TPMPT = 0x00000204 + // the number of active authorization sessions currently being tracked + // by the TPM + TPMPTHRActive TPMPT = 0x00000205 + // the number of additional authorization sessions, of any type, that + // could be created + TPMPTHRActiveAvail TPMPT = 0x00000206 + // estimate of the number of additional transient objects that could be + // loaded into TPM RAM + TPMPTHRTransientAvail TPMPT = 0x00000207 + // the number of persistent objects currently loaded into TPM NV memory + TPMPTHRPersistent TPMPT = 0x00000208 + // the number of additional persistent objects that could be loaded into + // NV memory + TPMPTHRPersistentAvail TPMPT = 0x00000209 + // the number of defined NV Indexes that have NV the TPM_NT_COUNTER + // attribute + TPMPTNVCounters TPMPT = 0x0000020A + // the number of additional NV Indexes that can be defined with their + // TPM_NT of TPM_NV_COUNTER and the TPMA_NV_ORDERLY attribute SET + TPMPTNVCountersAvail TPMPT = 0x0000020B + // code that limits the algorithms that may be used with the TPM + TPMPTAlgorithmSet TPMPT = 0x0000020C + // the number of loaded ECC curves + TPMPTLoadedCurves TPMPT = 0x0000020D + // the current value of the lockout counter (failedTries) + TPMPTLockoutCounter TPMPT = 0x0000020E + // the number of authorization failures before DA lockout is invoked + TPMPTMaxAuthFail TPMPT = 0x0000020F + // the number of seconds before the value reported by + // TPM_PT_LOCKOUT_COUNTER is decremented + TPMPTLockoutInterval TPMPT = 0x00000210 + // the number of seconds after a lockoutAuth failure before use of + // lockoutAuth may be attempted again + TPMPTLockoutRecovery TPMPT = 0x00000211 + // number of milliseconds before the TPM will accept another command + // that will modify NV + TPMPTNVWriteRecovery TPMPT = 0x00000212 + // the high-order 32 bits of the command audit counter + TPMPTAuditCounter0 TPMPT = 0x00000213 + // the low-order 32 bits of the command audit counter + TPMPTAuditCounter1 TPMPT = 0x00000214 +) + +// TPMPTPCR values come from Part 2: Structures, section 6.14. +const ( + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is saved and + // restored by TPM_SU_STATE + TPMPTPCRSave TPMPTPCR = 0x00000000 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 0 + TPMPTPCRExtendL0 TPMPTPCR = 0x00000001 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 0 + TPMPTPCRResetL0 TPMPTPCR = 0x00000002 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 1 + TPMPTPCRExtendL1 TPMPTPCR = 0x00000003 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 1 + TPMPTPCRResetL1 TPMPTPCR = 0x00000004 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 2 + TPMPTPCRExtendL2 TPMPTPCR = 0x00000005 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 2 + TPMPTPCRResetL2 TPMPTPCR = 0x00000006 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 3 + TPMPTPCRExtendL3 TPMPTPCR = 0x00000007 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 3 + TPMPTPCRResetL3 TPMPTPCR = 0x00000008 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 4 + TPMPTPCRExtendL4 TPMPTPCR = 0x00000009 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 4 + TPMPTPCRResetL4 TPMPTPCR = 0x0000000A + // a SET bit in the TPMS_PCR_SELECT indicates that modifications to this + // PCR (reset or Extend) will not increment the pcrUpdateCounter + TPMPTPCRNoIncrement TPMPTPCR = 0x00000011 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is reset by a + // D-RTM event + TPMPTPCRDRTMRest TPMPTPCR = 0x00000012 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is controlled + // by policy + TPMPTPCRPolicy TPMPTPCR = 0x00000013 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is controlled + // by an authorization value + TPMPTPCRAuth TPMPTPCR = 0x00000014 +) + +// TPMHandle values come from Part 2: Structures, section 7.4. +const ( + TPMRHOwner TPMHandle = 0x40000001 + TPMRHNull TPMHandle = 0x40000007 + TPMRSPW TPMHandle = 0x40000009 + TPMRHLockout TPMHandle = 0x4000000A + TPMRHEndorsement TPMHandle = 0x4000000B + TPMRHPlatform TPMHandle = 0x4000000C + TPMRHPlatformNV TPMHandle = 0x4000000D +) diff --git a/tpm2/direct/crypto.go b/tpm2/direct/crypto.go new file mode 100644 index 00000000..179fbb27 --- /dev/null +++ b/tpm2/direct/crypto.go @@ -0,0 +1,43 @@ +package direct + +import ( + "crypto/elliptic" + "crypto/rsa" + "math/big" +) + +// rsaPub converts a TPM RSA public key into one recognized by the rsa package. +func rsaPub(parms *TPMSRSAParms, pub *TPM2BPublicKeyRSA) (*rsa.PublicKey, error) { + result := rsa.PublicKey{ + N: big.NewInt(0).SetBytes(pub.Buffer), + E: int(parms.Exponent), + } + // TPM considers 65537 to be the default RSA public exponent, and 0 in + // the parms + // indicates so. + if result.E == 0 { + result.E = 65537 + } + return &result, nil +} + +// ecdh is a convenience wrapper around the necessary info to perform point +// multiplication with the elliptic package. +type ecdhPub struct { + curve elliptic.Curve + x, y *big.Int +} + +// eccPub converts a TPM ECC public key into one recognized by the elliptic +// package's point-multiplication functions, for use in ECDH. +func eccPub(parms *TPMSECCParms, pub *TPMSECCPoint) (*ecdhPub, error) { + curve, err := parms.CurveID.Curve() + if err != nil { + return nil, err + } + return &ecdhPub{ + curve: curve, + x: big.NewInt(0).SetBytes(pub.X.Buffer), + y: big.NewInt(0).SetBytes(pub.Y.Buffer), + }, nil +} diff --git a/tpm2/direct/ek_test.go b/tpm2/direct/ek_test.go new file mode 100644 index 00000000..6ab89a65 --- /dev/null +++ b/tpm2/direct/ek_test.go @@ -0,0 +1,237 @@ +package direct + +import ( + "errors" + "testing" + + "github.com/google/go-tpm-tools/simulator" +) + +// Test creating a sealed data blob on the standard-template EK using its policy. +func TestEKPolicy(t *testing.T) { + templates := map[string]TPM2BPublic{ + "RSA": RSAEKTemplate, + "ECC": ECCEKTemplate, + } + + // Run the whole test for each of RSA and ECC EKs. + for name, ekTemplate := range templates { + t.Run(name, func(t *testing.T) { + ekTest(t, ekTemplate) + }) + } +} + +func ekPolicy(tpm *TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error { + cmd := PolicySecretCommand{ + AuthHandle: AuthHandle{Handle: TPMRHEndorsement}, + PolicySession: handle, + NonceTPM: nonceTPM, + } + rsp := PolicySecretResponse{} + return tpm.Execute(&cmd, &rsp) +} + +// This function tests a lot of combinations of authorizing the EK policy. +func ekTest(t *testing.T, ekTemplate TPM2BPublic) { + type ekTestCase struct { + name string + // Use Policy instead of PolicySession, passing the callback instead of + // managing it ourselves? + jitPolicySession bool + // Use the policy session for decrypt? (Incompatible with decryptAnotherSession) + decryptPolicySession bool + // Use another session for decrypt? (Incompatible with decryptPolicySession) + decryptAnotherSession bool + // Use a bound session? + bound bool + // Use a salted session? + salted bool + } + var cases []ekTestCase + for jit := 0; jit < 2; jit++ { + for decryptPol := 0; decryptPol < 2; decryptPol++ { + for decryptAnother := 0; decryptAnother < 2; decryptAnother++ { + if decryptPol != 0 && decryptAnother != 0 { + continue + } + for bound := 0; bound < 2; bound++ { + for salted := 0; salted < 2; salted++ { + nextCase := ekTestCase{ + name: "test", + jitPolicySession: jit != 0, + decryptPolicySession: decryptPol != 0, + decryptAnotherSession: decryptAnother != 0, + bound: bound != 0, + salted: salted != 0, + } + if nextCase.jitPolicySession { + nextCase.name += "-jit" + } else { + nextCase.name += "-standalone" + } + if nextCase.decryptPolicySession { + nextCase.name += "-decrypt-same" + } + if nextCase.decryptAnotherSession { + nextCase.name += "-decrypt-another" + } + if nextCase.bound { + nextCase.name += "-bound" + } + if nextCase.salted { + nextCase.name += "-salted" + } + cases = append(cases, nextCase) + } + } + } + } + } + + sim, err := simulator.Get() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + tpm := NewTPM(sim) + defer tpm.Close() + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + // Create the EK + createEKCmd := CreatePrimaryCommand{ + PrimaryHandle: AuthHandle{ + Handle: TPMRHEndorsement, + }, + InPublic: ekTemplate, + } + var createEKRsp CreatePrimaryResponse + if err := tpm.Execute(&createEKCmd, &createEKRsp); err != nil { + t.Fatalf("%v", err) + } + if createEKRsp.OutPublic.PublicArea.Unique.ECC != nil { + t.Logf("EK pub:\n%x\n%x\n", createEKRsp.OutPublic.PublicArea.Unique.ECC.X, createEKRsp.OutPublic.PublicArea.Unique.ECC.Y) + t.Logf("EK name: %x", createEKRsp.Name) + } + defer func() { + // Flush the EK + flushEKCmd := FlushContextCommand{ + FlushHandle: createEKRsp.ObjectHandle, + } + var flushEKRsp FlushContextResponse + if err := tpm.Execute(&flushEKCmd, &flushEKRsp); err != nil { + t.Errorf("%v", err) + } + }() + + // Exercise the EK's auth policy (PolicySecret[RH_ENDORSEMENT]) + // by creating an object under it + data := []byte("secrets") + createBlobCmd := CreateCommand{ + ParentHandle: AuthHandle{ + Handle: createEKRsp.ObjectHandle, + Name: createEKRsp.Name, + }, + InSensitive: TPM2BSensitiveCreate{ + Sensitive: TPMSSensitiveCreate{ + Data: TPM2BData{ + Buffer: data, + }, + }, + }, + InPublic: TPM2BPublic{ + PublicArea: TPMTPublic{ + Type: TPMAlgKeyedHash, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + FixedParent: true, + UserWithAuth: true, + NoDA: true, + }, + }, + }, + } + var createBlobRsp CreateResponse + + var sessions []Session + if c.decryptAnotherSession { + sessions = append(sessions, HMAC(TPMAlgSHA1, 16, AESEncryption(128, EncryptIn))) + } + + var options []AuthOption + if c.decryptPolicySession { + options = append(options, AESEncryption(128, EncryptIn)) + } + if c.bound { + options = append(options, Bound(createEKRsp.ObjectHandle, createEKRsp.Name, nil)) + } + if c.salted { + options = append(options, Salted(createEKRsp.ObjectHandle, createEKRsp.OutPublic.PublicArea)) + } + + var s Session + if c.jitPolicySession { + // Use the convenience function to pass a policy callback. + s = Policy(TPMAlgSHA256, 16, ekPolicy, options...) + } else { + // Set up a session we have to execute and clean up ourselves. + var cleanup func() error + var err error + s, cleanup, err = PolicySession(tpm, TPMAlgSHA256, 16, options...) + if err != nil { + t.Fatalf("creating session: %v", err) + } + // Clean up the session at the end of the test. + defer func() { + if err := cleanup(); err != nil { + t.Fatalf("cleaning up policy session: %v", err) + } + }() + // Execute the same callback ourselves. + if err = ekPolicy(tpm, s.Handle(), s.NonceTPM()); err != nil { + t.Fatalf("executing EK policy: %v", err) + } + } + createBlobCmd.ParentHandle.Auth = s + + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...); err != nil { + t.Fatalf("%v", err) + } + + if !c.jitPolicySession { + // If we're not using a "just-in-time" session with a callback, + // we have to re-initialize the session. + if err = ekPolicy(tpm, s.Handle(), s.NonceTPM()); err != nil { + t.Fatalf("executing EK policy: %v", err) + } + } + + // Try again and make sure it succeeds again. + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...); err != nil { + t.Fatalf("%v", err) + } + + if !c.jitPolicySession { + // Finally, for non-JIT policy sessions, make sure we fail if + // we don't re-initialize the session. + // This is because after using a policy session, it's as if + // PolicyRestart was called. + err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...) + if err == nil { + t.Fatalf("wanted an error, got nil") + } + if !errors.Is(err, TPMRCPolicyFail) { + t.Errorf("want TPM_RC_POLICY_FAIL, got %v", err) + } + var fmt1 Fmt1Error + if !errors.As(err, &fmt1) { + t.Errorf("want a Fmt1Error, got %v", err) + } else if isSession, session := fmt1.Session(); !isSession || session != 1 { + t.Errorf("want TPM_RC_POLICY_FAIL on session 1, got %v", err) + } + } + }) + } + +} diff --git a/tpm2/direct/errors.go b/tpm2/direct/errors.go new file mode 100644 index 00000000..6e5d74a7 --- /dev/null +++ b/tpm2/direct/errors.go @@ -0,0 +1,557 @@ +package direct + +import ( + "fmt" +) + +type errorDesc struct { + name string + description string +} + +var fmt0Descs = map[TPMRC]errorDesc{ + TPMRCFailure: errorDesc{ + name: "TPM_RC_FAILURE", + description: "commands not being accepted because of a TPM failure", + }, + TPMRCSequence: errorDesc{ + name: "TPM_RC_SEQUENCE", + description: "improper use of a sequence handle", + }, + TPMRCPrivate: errorDesc{ + name: "TPM_RC_PRIVATE", + description: "not currently used", + }, + TPMRCHMAC: errorDesc{ + name: "TPM_RC_HMAC", + description: "not currently used", + }, + TPMRCDisabled: errorDesc{ + name: "TPM_RC_DISABLED", + description: "the command is disabled", + }, + TPMRCExclusive: errorDesc{ + name: "TPM_RC_EXCLUSIVE", + description: "command failed because audit sequence required exclusivity", + }, + TPMRCAuthType: errorDesc{ + name: "TPM_RC_AUTH_TYPE", + description: "authorization handle is not correct for command", + }, + TPMRCAuthMissing: errorDesc{ + name: "TPM_RC_AUTH_MISSING", + description: "command requires an authorization session for handle and it is not present.", + }, + TPMRCPolicy: errorDesc{ + name: "TPM_RC_POLICY", + description: "policy failure in math operation or an invalid authPolicy value", + }, + TPMRCPCR: errorDesc{ + name: "TPM_RC_PCR", + description: "PCR check fail", + }, + TPMRCPCRChanged: errorDesc{ + name: "TPM_RC_PCR_CHANGED", + description: "PCR have changed since checked.", + }, + TPMRCUpgrade: errorDesc{ + name: "TPM_RC_UPGRADE", + description: "for all commands other than TPM2_FieldUpgradeData(), this code indicates that the TPM is in field upgrade mode; for TPM2_FieldUpgradeData(), this code indicates that the TPM is not in field upgrade mode", + }, + TPMRCTooManyContexts: errorDesc{ + name: "TPM_RC_TOO_MANY_CONTEXTS", + description: "context ID counter is at maximum.", + }, + TPMRCAuthUnavailable: errorDesc{ + name: "TPM_RC_AUTH_UNAVAILABLE", + description: "authValue or authPolicy is not available for selected entity.", + }, + TPMRCReboot: errorDesc{ + name: "TPM_RC_REBOOT", + description: "a _TPM_Init and Startup(CLEAR) is required before the TPM can resume operation.", + }, + TPMRCUnbalanced: errorDesc{ + name: "TPM_RC_UNBALANCED", + description: "the protection algorithms (hash and symmetric) are not reasonably balanced. The digest size of the hash must be larger than the key size of the symmetric algorithm.", + }, + TPMRCCommandSize: errorDesc{ + name: "TPM_RC_COMMAND_SIZE", + description: "command commandSize value is inconsistent with contents of the command buffer; either the size is not the same as the octets loaded by the hardware interface layer or the value is not large enough to hold a command header", + }, + TPMRCCommandCode: errorDesc{ + name: "TPM_RC_COMMAND_CODE", + description: "command code not supported", + }, + TPMRCAuthSize: errorDesc{ + name: "TPM_RC_AUTHSIZE", + description: "the value of authorizationSize is out of range or the number of octets in the Authorization Area is greater than required", + }, + TPMRCAuthContext: errorDesc{ + name: "TPM_RC_AUTH_CONTEXT", + description: "use of an authorization session with a context command or another command that cannot have an authorization session.", + }, + TPMRCNVRange: errorDesc{ + name: "TPM_RC_NV_RANGE", + description: "NV offset+size is out of range.", + }, + TPMRCNVSize: errorDesc{ + name: "TPM_RC_NV_SIZE", + description: "Requested allocation size is larger than allowed.", + }, + TPMRCNVLocked: errorDesc{ + name: "TPM_RC_NV_LOCKED", + description: "NV access locked.", + }, + TPMRCNVAuthorization: errorDesc{ + name: "TPM_RC_NV_AUTHORIZATION", + description: "NV access authorization fails in command actions (this failure does not affect lockout.action)", + }, + TPMRCNVUninitialized: errorDesc{ + name: "TPM_RC_NV_UNINITIALIZED", + description: "an NV Index is used before being initialized or the state saved by TPM2_Shutdown(STATE) could not be restored", + }, + TPMRCNVSpace: errorDesc{ + name: "TPM_RC_NV_SPACE", + description: "insufficient space for NV allocation", + }, + TPMRCNVDefined: errorDesc{ + name: "TPM_RC_NV_DEFINED", + description: "NV Index or persistent object already defined", + }, + TPMRCBadContext: errorDesc{ + name: "TPM_RC_BAD_CONTEXT", + description: "context in TPM2_ContextLoad() is not valid", + }, + TPMRCCPHash: errorDesc{ + name: "TPM_RC_CPHASH", + description: "cpHash value already set or not correct for use", + }, + TPMRCParent: errorDesc{ + name: "TPM_RC_PARENT", + description: "handle for parent is not a valid parent", + }, + TPMRCNeedsTest: errorDesc{ + name: "TPM_RC_NEEDS_TEST", + description: "some function needs testing.", + }, + TPMRCNoResult: errorDesc{ + name: "TPM_RC_NO_RESULT", + description: "an internal function cannot process a request due to an unspecified problem. This code is usually related to invalid parameters that are not properly filtered by the input unmarshaling code.", + }, + TPMRCSensitive: errorDesc{ + name: "TPM_RC_SENSITIVE", + description: "the sensitive area did not unmarshal correctly after decryption – this code is used in lieu of the other unmarshaling errors so that an attacker cannot determine where the unmarshaling error occurred", + }, +} + +var fmt1Descs = map[TPMRC]errorDesc{ + TPMRCAsymmetric: errorDesc{ + name: "TPM_RC_ASYMMETRIC RC_FMT1", + description: "asymmetric algorithm not supported or not correct", + }, + TPMRCAttributes: errorDesc{ + name: "TPM_RC_ATTRIBUTES", + description: "inconsistent attributes", + }, + TPMRCHash: errorDesc{ + name: "TPM_RC_HASH", + description: "hash algorithm not supported or not appropriate", + }, + TPMRCValue: errorDesc{ + name: "TPM_RC_VALUE", + description: "value is out of range or is not correct for the context", + }, + TPMRCHierarchy: errorDesc{ + name: "TPM_RC_HIERARCHY", + description: "hierarchy is not enabled or is not correct for the use", + }, + TPMRCKeySize: errorDesc{ + name: "TPM_RC_KEY_SIZE", + description: "key size is not supported", + }, + TPMRCMGF: errorDesc{ + name: "TPM_RC_MGF", + description: "mask generation function not supported", + }, + TPMRCMode: errorDesc{ + name: "TPM_RC_MODE", + description: "mode of operation not supported", + }, + TPMRCType: errorDesc{ + name: "TPM_RC_TYPE", + description: "the type of the value is not appropriate for the use", + }, + TPMRCHandle: errorDesc{ + name: "TPM_RC_HANDLE", + description: "the handle is not correct for the use", + }, + TPMRCKDF: errorDesc{ + name: "TPM_RC_KDF", + description: "unsupported key derivation function or function not appropriate for use", + }, + TPMRCRange: errorDesc{ + name: "TPM_RC_RANGE", + description: "value was out of allowed range.", + }, + TPMRCAuthFail: errorDesc{ + name: "TPM_RC_AUTH_FAIL", + description: "the authorization HMAC check failed and DA counter incremented", + }, + TPMRCNonce: errorDesc{ + name: "TPM_RC_NONCE", + description: "invalid nonce size or nonce value mismatch", + }, + TPMRCPP: errorDesc{ + name: "TPM_RC_PP", + description: "authorization requires assertion of PP", + }, + TPMRCScheme: errorDesc{ + name: "TPM_RC_SCHEME", + description: "unsupported or incompatible scheme", + }, + TPMRCSize: errorDesc{ + name: "TPM_RC_SIZE", + description: "structure is the wrong size", + }, + TPMRCSymmetric: errorDesc{ + name: "TPM_RC_SYMMETRIC", + description: "unsupported symmetric algorithm or key size, or not appropriate for instance", + }, + TPMRCTag: errorDesc{ + name: "TPM_RC_TAG", + description: "incorrect structure tag", + }, + TPMRCSelector: errorDesc{ + name: "TPM_RC_SELECTOR", + description: "union selector is incorrect", + }, + TPMRCInsufficient: errorDesc{ + name: "TPM_RC_INSUFFICIENT", + description: "the TPM was unable to unmarshal a value because there were not enough octets in the input buffer", + }, + TPMRCSignature: errorDesc{ + name: "TPM_RC_SIGNATURE", + description: "the signature is not valid", + }, + TPMRCKey: errorDesc{ + name: "TPM_RC_KEY", + description: "key fields are not compatible with the selected use", + }, + TPMRCPolicyFail: errorDesc{ + name: "TPM_RC_POLICY_FAIL", + description: "a policy check failed", + }, + TPMRCIntegrity: errorDesc{ + name: "TPM_RC_INTEGRITY", + description: "integrity check failed", + }, + TPMRCTicket: errorDesc{ + name: "TPM_RC_TICKET", + description: "invalid ticket", + }, + TPMRCReservedBits: errorDesc{ + name: "TPM_RC_RESERVED_BITS", + description: "reserved bits not set to zero as required", + }, + TPMRCBadAuth: errorDesc{ + name: "TPM_RC_BAD_AUTH", + description: "authorization failure without DA implications", + }, + TPMRCExpired: errorDesc{ + name: "TPM_RC_EXPIRED", + description: "the policy has expired", + }, + TPMRCPolicyCC: errorDesc{ + name: "TPM_RC_POLICY_CC", + description: "the commandCode in the policy is not the commandCode of the command or the command code in a policy command references a command that is not implemented", + }, + TPMRCBinding: errorDesc{ + name: "TPM_RC_BINDING", + description: "public and sensitive portions of an object are not cryptographically bound", + }, + TPMRCCurve: errorDesc{ + name: "TPM_RC_CURVE", + description: "curve not supported", + }, + TPMRCECCPoint: errorDesc{ + name: "TPM_RC_ECC_POINT", + description: "point is not on the required curve.", + }, +} + +var warnDescs = map[TPMRC]errorDesc{ + TPMRCContextGap: errorDesc{ + name: "TPM_RC_CONTEXT_GAP", + description: "gap for context ID is too large", + }, + TPMRCObjectMemory: errorDesc{ + name: "TPM_RC_OBJECT_MEMORY", + description: "out of memory for object contexts", + }, + TPMRCSessionMemory: errorDesc{ + name: "TPM_RC_SESSION_MEMORY", + description: "out of memory for session contexts", + }, + TPMRCMemory: errorDesc{ + name: "TPM_RC_MEMORY", + description: "out of shared object/session memory or need space for internal operations", + }, + TPMRCSessionHandles: errorDesc{ + name: "TPM_RC_SESSION_HANDLES", + description: "out of session handles – a session must be flushed before a new session may be created", + }, + TPMRCObjectHandles: errorDesc{ + name: "TPM_RC_OBJECT_HANDLES", + description: "out of object handles – the handle space for objects is depleted and a reboot is required", + }, + TPMRCLocality: errorDesc{ + name: "TPM_RC_LOCALITY", + description: "bad locality", + }, + TPMRCYielded: errorDesc{ + name: "TPM_RC_YIELDED", + description: "the TPM has suspended operation on the command; forward progress was made and the command may be retried", + }, + TPMRCCanceled: errorDesc{ + name: "TPM_RC_CANCELED", + description: "the command was canceled", + }, + TPMRCTesting: errorDesc{ + name: "TPM_RC_TESTING", + description: "TPM is performing self-tests", + }, + TPMRCReferenceH0: errorDesc{ + name: "TPM_RC_REFERENCE_H0", + description: "the 1st handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH1: errorDesc{ + name: "TPM_RC_REFERENCE_H1", + description: "the 2nd handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH2: errorDesc{ + name: "TPM_RC_REFERENCE_H2", + description: "the 3rd handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH3: errorDesc{ + name: "TPM_RC_REFERENCE_H3", + description: "the 4th handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH4: errorDesc{ + name: "TPM_RC_REFERENCE_H4", + description: "the 5th handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH5: errorDesc{ + name: "TPM_RC_REFERENCE_H5", + description: "the 6th handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceH6: errorDesc{ + name: "TPM_RC_REFERENCE_H6", + description: "the 7th handle in the handle area references a transient object or session that is not loaded", + }, + TPMRCReferenceS0: errorDesc{ + name: "TPM_RC_REFERENCE_S0", + description: "the 1st authorization session handle references a session that is not loaded", + }, + TPMRCReferenceS1: errorDesc{ + name: "TPM_RC_REFERENCE_S1", + description: "the 2nd authorization session handle references a session that is not loaded", + }, + TPMRCReferenceS2: errorDesc{ + name: "TPM_RC_REFERENCE_S2", + description: "the 3rd authorization session handle references a session that is not loaded", + }, + TPMRCReferenceS3: errorDesc{ + name: "TPM_RC_REFERENCE_S3", + description: "the 4th authorization session handle references a session that is not loaded", + }, + TPMRCReferenceS4: errorDesc{ + name: "TPM_RC_REFERENCE_S4", + description: "the 5th session handle references a session that is not loaded", + }, + TPMRCReferenceS5: errorDesc{ + name: "TPM_RC_REFERENCE_S5", + description: "the 6th session handle references a session that is not loaded", + }, + TPMRCReferenceS6: errorDesc{ + name: "TPM_RC_REFERENCE_S6", + description: "the 7th authorization session handle references a session that is not loaded", + }, + TPMRCNVRate: errorDesc{ + name: "TPM_RC_NV_RATE", + description: "the TPM is rate-limiting accesses to prevent wearout of NV", + }, + TPMRCLockout: errorDesc{ + name: "TPM_RC_LOCKOUT", + description: "authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode", + }, + TPMRCRetry: errorDesc{ + name: "TPM_RC_RETRY", + description: "the TPM was not able to start the command", + }, + TPMRCNVUnavailable: errorDesc{ + name: "TPM_RC_NV_UNAVAILABLE", + description: "the command may require writing of NV and NV is not current accessible", + }, +} + +// subject represents a subject of a TPM error code with additional details +// (i.e., FMT1 codes) +type subject int + +const ( + handle subject = iota + 1 + parameter + session +) + +// String returns the string representation of the ErrorSubject. +func (s subject) String() string { + switch s { + case handle: + return "handle" + case parameter: + return "parameter" + case session: + return "session" + default: + return "unknown subject" + } +} + +// Fmt1Error represents a TPM 2.0 format-1 error, with additional information. +type Fmt1Error struct { + // The canonical TPM error code, with handle/parameter/session info + // stripped out. + canonical TPMRC + // Whether this was a handle, parameter, or session error. + subject subject + // Which handle, parameter, or session was in error + index int +} + +// Error returns the string representation of the error. +func (e Fmt1Error) Error() string { + desc, ok := fmt1Descs[e.canonical] + if !ok { + return fmt.Sprintf("unknown format-1 error: %s %d (%x)", e.subject, e.index, uint32(e.canonical)) + } + return fmt.Sprintf("%s (%v %d): %s", desc.name, e.subject, e.index, desc.description) +} + +// Handle returns whether the error is handle-related and if so, which handle is +// in error. +func (e Fmt1Error) Handle() (bool, int) { + if e.subject != handle { + return false, 0 + } + return true, e.index +} + +// Parameter returns whether the error is handle-related and if so, which handle +// is in error. +func (e Fmt1Error) Parameter() (bool, int) { + if e.subject != parameter { + return false, 0 + } + return true, e.index +} + +// Session returns whether the error is handle-related and if so, which handle +// is in error. +func (e Fmt1Error) Session() (bool, int) { + if e.subject != session { + return false, 0 + } + return true, e.index +} + +// isFmt0Error returns true if the result is a format-0 error. +func (r TPMRC) isFmt0Error() bool { + return (r&rcVer1) == rcVer1 && (r&rcWarn) != rcWarn +} + +// isFmt1Error returns true and a format-1 error structure if the error is a +// format-1 error. +func (r TPMRC) isFmt1Error() (bool, Fmt1Error) { + if (r & rcFmt1) != rcFmt1 { + return false, Fmt1Error{} + } + subj := handle + if (r & rcP) == rcP { + subj = parameter + r ^= rcP + } else if (r & rcS) == rcS { + subj = session + r ^= rcS + } + idx := int((r & 0xF00) >> 8) + r &= 0xFFFFF0FF + return true, Fmt1Error{ + canonical: r, + subject: subj, + index: idx, + } +} + +// IsWarning returns true if the error is a warning code. +// This usually indicates a problem with the TPM state, and not the command. +// Retrying the command later may succeed. +func (r TPMRC) IsWarning() bool { + if isFmt1, _ := r.isFmt1Error(); isFmt1 { + // There aren't any format-1 warnings. + return false + } + return (r&rcVer1) == rcVer1 && (r&rcWarn) == rcWarn +} + +// Error produces a nice human-readable representation of the error, parsing TPM +// FMT1 errors as needed. +func (r TPMRC) Error() string { + if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { + return fmt1.Error() + } + if r.isFmt0Error() { + desc, ok := fmt0Descs[r] + if !ok { + return fmt.Sprintf("unknown format-0 error code (0x%x)", uint32(r)) + } + return fmt.Sprintf("%s: %s", desc.name, desc.description) + } + if r.IsWarning() { + desc, ok := warnDescs[r] + if !ok { + return fmt.Sprintf("unknown warning (0x%x)", uint32(r)) + } + return fmt.Sprintf("%s: %s", desc.name, desc.description) + } + return fmt.Sprintf("unrecognized error code (0x%x)", uint32(r)) +} + +// Is returns whether the TPMRC (which may be a FMT1 error) is equal to the +// given canonical error. +func (r TPMRC) Is(target error) bool { + targetRC, ok := target.(TPMRC) + if !ok { + return false + } + if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { + return fmt1.canonical == targetRC + } + return r == targetRC +} + +// As returns whether the error can be assigned to the given interface type. +// If supported, it updates the value pointed at by target. +// Supports the Fmt1Error type. +func (r TPMRC) As(target interface{}) bool { + pFmt1, ok := target.(*Fmt1Error) + if !ok { + return false + } + isFmt1, fmt1 := r.isFmt1Error() + if !isFmt1 { + return false + } + *pFmt1 = fmt1 + return true +} diff --git a/tpm2/direct/names.go b/tpm2/direct/names.go new file mode 100644 index 00000000..76a471d8 --- /dev/null +++ b/tpm2/direct/names.go @@ -0,0 +1,11 @@ +package direct + +import ( + "encoding/binary" +) + +func PrimaryHandleName(h TPMHandle) []byte { + result := make([]byte, 4) + binary.BigEndian.PutUint32(result, uint32(h)) + return result +} diff --git a/tpm2/direct/reflect.go b/tpm2/direct/reflect.go new file mode 100644 index 00000000..f547e966 --- /dev/null +++ b/tpm2/direct/reflect.go @@ -0,0 +1,1010 @@ +package direct + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "reflect" + "strconv" + "strings" + + "github.com/google/go-tpm/tpmutil" +) + +const ( + // Chosen based on MAX_DIGEST_BUFFER, the length of the longest + // reasonable list returned by the reference implementation. + maxListLength uint32 = 1024 +) + +// TPM represents a logical connection to a TPM. Support for +// various commands is provided at runtime using reflection. +type TPM struct { + transport io.ReadWriteCloser +} + +// NewTPM opens a TPM connection using the provided ReadWriteCloser. +// When this TPM connection is closed, the transport is closed. +func NewTPM(t io.ReadWriteCloser) *TPM { + return &TPM{ + transport: t, + } +} + +// Close closes the connection to the TPM and its underlying +// transport. +func (t *TPM) Close() error { + return t.transport.Close() +} + +// Execute sends the provided command and returns the TPM's response. +func (t *TPM) Execute(cmd Command, rsp Response, extraSess ...Session) error { + cc := cmd.Command() + if rsp.Response() != cc { + return fmt.Errorf("cmd and rsp must be for same command: %v != %v", cc, rsp.Response()) + } + sess, err := cmdAuths(cmd) + if err != nil { + return err + } + sess = append(sess, extraSess...) + if len(sess) > 3 { + return fmt.Errorf("too many sessions: %v", len(sess)) + } + hasSessions := len(sess) > 0 + // Initialize the sessions, if needed + for i, s := range sess { + if err := s.Init(t); err != nil { + return fmt.Errorf("initializing session %d: %w", i, err) + } + if err := s.NewNonceCaller(); err != nil { + return err + } + } + handles := cmdHandles(cmd) + names, err := cmdNames(cmd) + if err != nil { + return err + } + parms, err := cmdParameters(cmd, sess) + if err != nil { + return err + } + sessions, err := cmdSessions(t, sess, cc, names, parms) + if err != nil { + return err + } + hdr := cmdHeader(hasSessions, 10 /* size of command header */ +len(handles)+len(sessions)+len(parms), cc) + command := append(hdr, handles...) + command = append(command, sessions...) + command = append(command, parms...) + + // Send the command via the transport. + response, err := tpmutil.RunCommandRaw(t.transport, command) + if err != nil { + return err + } + + // Parse the command directly into the response structure. + rspBuf := bytes.NewBuffer(response) + err = rspHeader(rspBuf) + if err != nil { + var bonusErrs []string + // Emergency cleanup, then return. + for _, s := range sess { + if err := s.CleanupFailure(t); err != nil { + bonusErrs = append(bonusErrs, err.Error()) + } + } + if len(bonusErrs) != 0 { + return fmt.Errorf("%w - additional errors encountered during cleanup: %v", err, strings.Join(bonusErrs, ", ")) + } + return err + } + err = rspHandles(rspBuf, rsp) + if err != nil { + return err + } + rspParms, err := rspParametersArea(hasSessions, rspBuf) + if err != nil { + return err + } + if hasSessions { + // We don't need the TPM RC here because we would have errored + // out from rspHeader + // TODO: Authenticate the error code with sessions, if desired. + err = rspSessions(rspBuf, TPMRCSuccess, cc, names, rspParms, sess) + if err != nil { + return err + } + } + err = rspParameters(rspParms, sess, rsp) + if err != nil { + return err + } + + return nil +} + +// Marshal will serialize the given values, appending them onto the given writer. +// Returns an error if any of the values are not marshallable. +func Marshal(w io.Writer, vs ...interface{}) error { + var reflects []reflect.Value + for _, v := range vs { + reflects = append(reflects, reflect.ValueOf(v)) + } + var buf bytes.Buffer + if err := marshal(&buf, reflects...); err != nil { + return err + } + _, err := io.Copy(w, &buf) + return err +} + +// marshal will serialize the given values, appending them onto the given +// buffer. +// Returns an error if any of the values are not marshallable. +func marshal(buf *bytes.Buffer, vs ...reflect.Value) error { + for _, v := range vs { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := marshalNumeric(buf, v); err != nil { + return err + } + case reflect.Array, reflect.Slice: + if err := marshalArray(buf, v); err != nil { + return err + } + case reflect.Struct: + if err := marshalStruct(buf, v); err != nil { + return err + } + case reflect.Ptr: + if err := marshalStruct(buf, v.Elem()); err != nil { + return err + } + default: + return fmt.Errorf("not marshallable: %#v", v) + } + } + return nil +} + +func marshalNumeric(buf *bytes.Buffer, v reflect.Value) error { + return binary.Write(buf, binary.BigEndian, v.Interface()) +} + +func marshalArray(buf *bytes.Buffer, v reflect.Value) error { + for i := 0; i < v.Len(); i++ { + if err := marshal(buf, v.Index(i)); err != nil { + return fmt.Errorf("marshalling element %d of %v: %v", i, v.Type(), err) + } + } + return nil +} + +// Marshals the members of the struct, handling sized and bitwise fields. +func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { + // Check if this is a bitwise-defined structure. This requires all the + // members to be bitwise-defined. + if v.NumField() > 0 { + bitwise := hasTag(v.Type().Field(0), "bit") + for i := 0; i < v.NumField(); i++ { + thisBitwise := hasTag(v.Type().Field(i), "bit") + if thisBitwise { + if hasTag(v.Type().Field(i), "sized") || hasTag(v.Type().Field(i), "sized8") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", + v.Type().Name(), v.Type().Field(i).Name) + } + if hasTag(v.Type().Field(i), "tag") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", + v.Type().Name(), v.Type().Field(i).Name) + } + } + if bitwise != thisBitwise { + return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + } + } + if bitwise { + return marshalBitwise(buf, v) + } + } + // Make a pass to create a map of tag values + // UInt64-valued fields with values greater than MaxInt64 cannot be + // selectors. + possibleSelectors := make(map[string]int64) + for i := 0; i < v.NumField(); i++ { + // Special case: Treat a zero-valued nullable field as + // TPMAlgNull for union selection. + // This allows callers to omit uninteresting scheme structures. + if v.Field(i).IsZero() && hasTag(v.Type().Field(i), "nullable") { + possibleSelectors[v.Type().Field(i).Name] = int64(TPMAlgNull) + continue + } + switch v.Field(i).Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + possibleSelectors[v.Type().Field(i).Name] = v.Field(i).Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + val := v.Field(i).Uint() + if val <= math.MaxInt64 { + possibleSelectors[v.Type().Field(i).Name] = int64(val) + } + } + } + for i := 0; i < v.NumField(); i++ { + if hasTag(v.Type().Field(i), "skip") { + continue + } + list := hasTag(v.Type().Field(i), "list") + sized := hasTag(v.Type().Field(i), "sized") + sized8 := hasTag(v.Type().Field(i), "sized8") + tag := tags(v.Type().Field(i))["tag"] + // Serialize to a temporary buffer, in case we need to size it + // (Better to simplify this complex reflection-based marshalling + // code than to save some unnecessary copying before talking to + // a low-speed device like a TPM) + var res bytes.Buffer + if list { + binary.Write(&res, binary.BigEndian, uint32(v.Field(i).Len())) + } + if tag != "" { + // Check that the tagged value was present (and numeric + // and smaller than MaxInt64) + tagValue, ok := possibleSelectors[tag] + if !ok { + return fmt.Errorf("union tag '%v' for member '%v' of struct '%v' did not reference "+ + "a numeric field of int64-compatible value", + tag, v.Type().Field(i).Name, v.Type().Name()) + } + if err := marshalUnion(&res, v.Field(i), tagValue); err != nil { + return err + } + } else if v.Field(i).IsZero() && v.Field(i).Kind() == reflect.Uint32 && hasTag(v.Type().Field(i), "nullable") { + // Special case: Anything with the same underlying type + // as TPMHandle's zero value is TPM_RH_NULL. + // This allows callers to omit uninteresting handles + // instead of specifying them as TPM_RH_NULL. + if err := binary.Write(&res, binary.BigEndian, uint32(TPMRHNull)); err != nil { + return err + } + } else if v.Field(i).IsZero() && v.Field(i).Kind() == reflect.Uint16 && hasTag(v.Type().Field(i), "nullable") { + // Special case: Anything with the same underlying type + // as TPMAlg's zero value is TPM_ALG_NULL. + // This allows callers to omit uninteresting + // algorithms/schemes instead of specifying them as + // TPM_ALG_NULL. + if err := binary.Write(&res, binary.BigEndian, uint16(TPMAlgNull)); err != nil { + return err + } + } else { + if err := marshal(&res, v.Field(i)); err != nil { + return err + } + } + if sized { + if err := binary.Write(buf, binary.BigEndian, uint16(res.Len())); err != nil { + return err + } + } + if sized8 { + if err := binary.Write(buf, binary.BigEndian, uint8(res.Len())); err != nil { + return err + } + } + buf.Write(res.Bytes()) + } + return nil +} + +// Marshals a bitwise-defined struct. +func marshalBitwise(buf *bytes.Buffer, v reflect.Value) error { + maxBit := 0 + for i := 0; i < v.NumField(); i++ { + high, _, ok := rangeTag(v.Type().Field(i), "bit") + if !ok { + return fmt.Errorf("'%v' struct member '%v' did not specify a bit index or range", v.Type().Name(), v.Type().Field(i).Name) + } + if high > maxBit { + maxBit = high + } + } + if (maxBit+1)%8 != 0 { + return fmt.Errorf("'%v' bitwise members did not total up to a multiple of 8 bits", v.Type().Name()) + } + bitArray := make([]bool, maxBit+1) + for i := 0; i < v.NumField(); i++ { + high, low, _ := rangeTag(v.Type().Field(i), "bit") + var buf bytes.Buffer + if err := marshal(&buf, v.Field(i)); err != nil { + return err + } + b := buf.Bytes() + for i := 0; i <= (high - low); i++ { + bitArray[low+i] = ((b[len(b)-i/8-1] >> (i % 8)) & 1) == 1 + } + } + result := make([]byte, len(bitArray)/8) + for i, bit := range bitArray { + if bit { + result[len(result)-(i/8)-1] |= (1 << (i % 8)) + } + } + buf.Write(result) + return nil +} + +// Marshals the member of the given union struct corresponding to the given +// selector. Marshals nothing if the selector is equal to TPM_ALG_NULL (0x0010). +func marshalUnion(buf *bytes.Buffer, v reflect.Value, selector int64) error { + // Special case: TPM_ALG_NULL as a selector means marshal nothing + if selector == int64(TPMAlgNull) { + return nil + } + for i := 0; i < v.NumField(); i++ { + sel, ok := numericTag(v.Type().Field(i), "selector") + if !ok { + return fmt.Errorf("'%v' union member '%v' did not have a selector tag", v.Type().Name(), v.Type().Field(i).Name) + } + if sel == selector { + if v.Field(i).IsNil() { + // Special case: if the selected value is found + // but nil, marshal the zero-value instead + return marshal(buf, reflect.New(v.Field(i).Type().Elem()).Elem()) + } + return marshal(buf, v.Field(i).Elem()) + } + } + return fmt.Errorf("selector value '%v' not handled for type '%v'", selector, v.Type().Name()) +} + +// Unmarshal deserializes the given values from the reader. +// Returns an error if the buffer does not contain enough data to satisfy the +// types, or if the types are not unmarshallable. +func Unmarshal(r io.Reader, vs ...interface{}) error { + var reflects []reflect.Value + for _, v := range vs { + if reflect.ValueOf(v).Kind() != reflect.Ptr { + return fmt.Errorf("all parameters to Unmarshal must be pointers") + } + reflects = append(reflects, reflect.ValueOf(v).Elem()) + } + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { + return err + } + return unmarshal(&buf, reflects...) +} + +// unmarshal will deserialize the given values from the given buffer. +// Returns an error if the buffer does not contain enough data to satisfy the +// type. +func unmarshal(buf *bytes.Buffer, vs ...reflect.Value) error { + for _, v := range vs { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := unmarshalNumeric(buf, v); err != nil { + return err + } + continue + case reflect.Slice: + var length uint32 + // special case for byte slices: just read the entire + // rest of the buffer + if v.Type().Elem().Kind() == reflect.Uint8 { + length = uint32(buf.Len()) + } else { + err := unmarshalNumeric(buf, reflect.ValueOf(&length).Elem()) + if err != nil { + return fmt.Errorf("deserializing size for field of type '%v': %w", v.Type(), err) + } + } + if length > uint32(math.MaxInt32) || length > maxListLength { + return fmt.Errorf("could not deserialize slice of length %v", length) + } + // Go's reflect library doesn't allow increasing the + // capacity of an existing slice. + // Since we can't be sure that the capacity of the + // passed-in value was enough, allocate + // a new temporary one of the correct length, unmarshal + // to it, and swap it in. + tmp := reflect.MakeSlice(v.Type(), int(length), int(length)) + if err := unmarshalArray(buf, tmp); err != nil { + return err + } + v.Set(tmp) + continue + case reflect.Array: + if err := unmarshalArray(buf, v); err != nil { + return err + } + continue + case reflect.Struct: + if err := unmarshalStruct(buf, v); err != nil { + return err + } + continue + default: + return fmt.Errorf("not unmarshallable: %v", v.Type()) + } + } + return nil +} + +func unmarshalNumeric(buf *bytes.Buffer, v reflect.Value) error { + return binary.Read(buf, binary.BigEndian, v.Addr().Interface()) +} + +// For slices, the slice's length must already be set to the expected amount of +// data. +func unmarshalArray(buf *bytes.Buffer, v reflect.Value) error { + for i := 0; i < v.Len(); i++ { + if err := unmarshal(buf, v.Index(i)); err != nil { + return fmt.Errorf("deserializing slice/array index %v: %w", i, err) + } + } + return nil +} + +func unmarshalStruct(buf *bytes.Buffer, v reflect.Value) error { + // Check if this is a bitwise-defined structure. This requires all the + // members to be bitwise-defined. + if v.NumField() > 0 { + bitwise := hasTag(v.Type().Field(0), "bit") + for i := 0; i < v.NumField(); i++ { + thisBitwise := hasTag(v.Type().Field(i), "bit") + if thisBitwise { + if hasTag(v.Type().Field(i), "sized") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", + v.Type().Name(), v.Type().Field(i).Name) + } + if hasTag(v.Type().Field(i), "tag") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", + v.Type().Name(), v.Type().Field(i).Name) + } + } + if bitwise != thisBitwise { + return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + } + } + if bitwise { + return unmarshalBitwise(buf, v) + } + } + for i := 0; i < v.NumField(); i++ { + if hasTag(v.Type().Field(i), "skip") { + continue + } + list := hasTag(v.Type().Field(i), "list") + if list && (v.Field(i).Kind() != reflect.Slice) { + return fmt.Errorf("field '%v' of struct '%v' had the 'list' tag but was not a slice", + v.Type().Field(i).Name, v.Type().Name()) + } + // Slices of anything but byte/uint8 must have the 'list' tag. + if !list && (v.Field(i).Kind() == reflect.Slice) && (v.Type().Field(i).Type.Elem().Kind() != reflect.Uint8) { + return fmt.Errorf("field '%v' of struct '%v' was a slice of non-byte but did not have the 'list' tag", + v.Type().Field(i).Name, v.Type().Name()) + } + sized := hasTag(v.Type().Field(i), "sized") + sized8 := hasTag(v.Type().Field(i), "sized8") + // If sized, unmarshal a size field first, then restrict + // unmarshalling to the given size + bufToReadFrom := buf + if sized { + var expectedSize uint16 + binary.Read(buf, binary.BigEndian, &expectedSize) + sizedBufArray := make([]byte, int(expectedSize)) + n, err := buf.Read(sizedBufArray) + if n != int(expectedSize) { + return fmt.Errorf("ran out of data reading sized parameter '%v' inside struct of type '%v'", + v.Type().Field(i).Name, v.Type().Name()) + } + if err != nil { + return fmt.Errorf("error reading data for parameter '%v' inside struct of type '%v'", + v.Type().Field(i).Name, v.Type().Name()) + } + bufToReadFrom = bytes.NewBuffer(sizedBufArray) + } + if sized8 { + var expectedSize uint8 + binary.Read(buf, binary.BigEndian, &expectedSize) + sizedBufArray := make([]byte, int(expectedSize)) + n, err := buf.Read(sizedBufArray) + if n != int(expectedSize) { + return fmt.Errorf("ran out of data reading sized parameter '%v' inside struct of type '%v'", + v.Type().Field(i).Name, v.Type().Name()) + } + if err != nil { + return fmt.Errorf("error reading data for parameter '%v' inside struct of type '%v'", + v.Type().Field(i).Name, v.Type().Name()) + } + bufToReadFrom = bytes.NewBuffer(sizedBufArray) + } + tag := tags(v.Type().Field(i))["tag"] + if tag != "" { + // Make a pass to create a map of tag values + // UInt64-valued fields with values greater than + // MaxInt64 cannot be selectors. + possibleSelectors := make(map[string]int64) + for j := 0; j < v.NumField(); j++ { + switch v.Field(j).Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + possibleSelectors[v.Type().Field(j).Name] = v.Field(j).Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + val := v.Field(j).Uint() + if val <= math.MaxInt64 { + possibleSelectors[v.Type().Field(j).Name] = int64(val) + } + } + } + // Check that the tagged value was present (and numeric + // and smaller than MaxInt64) + tagValue, ok := possibleSelectors[tag] + if !ok { + return fmt.Errorf("union tag '%v' for member '%v' of struct '%v' did not reference "+ + "a numeric field of in64-compatible value", + tag, v.Type().Field(i).Name, v.Type().Name()) + } + if err := unmarshalUnion(bufToReadFrom, v.Field(i), tagValue); err != nil { + return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) + } + } else { + if err := unmarshal(bufToReadFrom, v.Field(i)); err != nil { + return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) + } + } + if sized || sized8 { + if bufToReadFrom.Len() != 0 { + return fmt.Errorf("extra data at the end of sized parameter '%v' inside struct of type '%v'", + v.Type().Field(i).Name, v.Type().Name()) + } + } + } + return nil +} + +// Unmarshals a bitwise-defined struct. +func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { + maxBit := 0 + for i := 0; i < v.NumField(); i++ { + high, _, ok := rangeTag(v.Type().Field(i), "bit") + if !ok { + return fmt.Errorf("'%v' struct member '%v' did not specify a bit index or range", v.Type().Name(), v.Type().Field(i).Name) + } + if high > maxBit { + maxBit = high + } + } + if (maxBit+1)%8 != 0 { + return fmt.Errorf("'%v' bitwise members did not total up to a multiple of 8 bits", v.Type().Name()) + } + bitArray := make([]bool, maxBit+1) + // We will read big-endian, starting from the last byte and working our + // way down. + for i := len(bitArray)/8 - 1; i >= 0; i-- { + b, err := buf.ReadByte() + if err != nil { + return fmt.Errorf("error %d bits into field '%v' of struct '%v': %w", + i, v.Type().Field(i).Name, v.Type().Name(), err) + } + for j := 0; j < 8; j++ { + bitArray[8*i+j] = (((b >> j) & 1) == 1) + } + } + for i := 0; i < v.NumField(); i++ { + high, low, _ := rangeTag(v.Type().Field(i), "bit") + var val uint64 + for j := 0; j <= high-low; j++ { + if bitArray[low+j] { + val |= (1 << j) + } + } + if v.Field(i).Kind() == reflect.Bool { + v.Field(i).SetBool((val & 1) == 1) + } else { + v.Field(i).SetUint(val) + } + } + return nil +} + +// Unmarshals the member of the given union struct corresponding to the given +// selector. Unmarshals nothing if the selector is TPM_ALG_NULL (0x0010). +func unmarshalUnion(buf *bytes.Buffer, v reflect.Value, selector int64) error { + // Special case: TPM_ALG_NULL as a selector means unmarshal nothing + if selector == int64(TPMAlgNull) { + return nil + } + for i := 0; i < v.NumField(); i++ { + sel, ok := numericTag(v.Type().Field(i), "selector") + if !ok { + return fmt.Errorf("'%v' union member '%v' did not have a selector tag", v.Type().Name(), v.Type().Field(i).Name) + } + if sel == selector { + val := reflect.New(v.Type().Field(i).Type.Elem()) + if err := unmarshal(buf, val.Elem()); err != nil { + return fmt.Errorf("unmarshalling '%v' union member '%v': %w", v.Type().Name(), v.Type().Field(i).Name, err) + } + v.Field(i).Set(val) + return nil + } + } + return fmt.Errorf("selector value '%v' not handled for type '%v'", selector, v.Type().Name()) +} + +// Returns all the gotpm tags on a field as a map. +// Some tags are settable (with "="). For these, the value is the RHS. +// For all others, the value is the empty string. +func tags(t reflect.StructField) map[string]string { + allTags, ok := t.Tag.Lookup("gotpm") + if !ok { + return nil + } + result := make(map[string]string) + tags := strings.Split(allTags, ",") + for _, tag := range tags { + // Split on the equals sign for settable tags. + // If the split returns an empty slice, this is an empty tag. + // If the split returns a slice of length 1, this is an + // un-settable tag. + // If the split returns a slice of length 2, this is a settable + // tag. + assignment := strings.SplitN(tag, "=", 2) + val := "" + if len(assignment) > 1 { + val = assignment[1] + } + if len(assignment) > 0 { + key := assignment[0] + result[key] = val + } + } + return result +} + +// hasTag looks up to see if the type's gotpm-namespaced tag contains the +// given value. +// Returns false if there is no gotpm-namespaced tag on the type. +func hasTag(t reflect.StructField, tag string) bool { + ts := tags(t) + _, ok := ts[tag] + return ok +} + +// Returns the numeric tag value, or false if the tag is not present. +func numericTag(t reflect.StructField, tag string) (int64, bool) { + val, ok := tags(t)[tag] + if !ok { + return 0, false + } + v, err := strconv.ParseInt(val, 0, 64) + if err != nil { + return 0, false + } + return v, true +} + +// Returns the range on a tag like 4:3 or 4. +// If there is no colon, the low and high part of the range are equal. +func rangeTag(t reflect.StructField, tag string) (int, int, bool) { + val, ok := tags(t)[tag] + if !ok { + return 0, 0, false + } + vals := strings.Split(val, ":") + high, err := strconv.Atoi(vals[0]) + if err != nil { + return 0, 0, false + } + low := high + if len(vals) > 1 { + low, err = strconv.Atoi(vals[1]) + if err != nil { + return 0, 0, false + } + } + if low > high { + low, high = high, low + } + return high, low, true +} + +// taggedMembers will return a slice of all the members of the given +// structure that contain (or don't contain) the given tag in the "gotpm" +// namespace. +// Panics if v's Kind is not Struct. +func taggedMembers(v reflect.Value, tag string, invert bool) []reflect.Value { + var result []reflect.Value + t := v.Type() + + for i := 0; i < t.NumField(); i++ { + // Add this one to the list if it has the tag and we're not + // inverting, or if it doesn't have the tag and we are + // inverting. + if hasTag(t.Field(i), tag) != invert { + result = append(result, v.Field(i)) + } + } + + return result +} + +// cmdAuths returns the authorization sessions of the command. +func cmdAuths(cmd Command) ([]Session, error) { + authHandles := taggedMembers(reflect.ValueOf(cmd).Elem(), "auth", false) + var result []Session + for _, authHandle := range authHandles { + handle, ok := authHandle.Interface().(AuthHandle) + if !ok { + return nil, fmt.Errorf("'auth'-tagged member of %v was of type %v instead of AuthHandle", + reflect.TypeOf(cmd), authHandle.Type()) + } + result = append(result, handle.effectiveAuth()) + } + + return result, nil +} + +// numAuthHandles returns the number of authorization sessions needed for the +// command. +func numAuthHandles(cmd Command) int { + result := 0 + cmdType := reflect.TypeOf(cmd).Elem() + for i := 0; i < cmdType.NumField(); i++ { + if hasTag(cmdType.Field(i), "auth") { + result++ + } + } + return result +} + +// cmdHandles returns the handles area of the command. +func cmdHandles(cmd Command) []byte { + handles := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", false) + + // Initial capacity is enough to hold 3 handles + result := bytes.NewBuffer(make([]byte, 0, 12)) + + marshal(result, handles...) + + return result.Bytes() +} + +// cmdNames returns the authorized names of the command. +func cmdNames(cmd Command) ([]TPM2BName, error) { + authHandles := taggedMembers(reflect.ValueOf(cmd).Elem(), "auth", false) + var result []TPM2BName + for _, authHandle := range authHandles { + handle, ok := authHandle.Interface().(AuthHandle) + if !ok { + return nil, fmt.Errorf("'auth'-tagged member of %v was of type %v instead of AuthHandle", + reflect.TypeOf(cmd), authHandle.Type()) + } + result = append(result, handle.effectiveName()) + } + + return result, nil +} + +// cmdParameters returns the parameters area of the command. +// The first parameter may be encrypted by one of the sessions. +func cmdParameters(cmd Command, sess []Session) ([]byte, error) { + parms := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", true) + if len(parms) == 0 { + return nil, nil + } + + // Marshal the first parameter for in-place session encryption. + var firstParm bytes.Buffer + marshal(&firstParm, parms[0]) + firstParmBytes := firstParm.Bytes() + + // Encrypt the first parameter if there are any decryption sessions. + encrypted := false + for i, s := range sess { + if s.IsDecryption() { + if encrypted { + // Only one session may be used for decryption. + return nil, fmt.Errorf("too many decrypt sessions") + } + if len(firstParmBytes) < 2 { + return nil, fmt.Errorf("this command's first parameter is not a TPM2B") + } + err := s.Encrypt(firstParmBytes[2:]) + if err != nil { + return nil, fmt.Errorf("encrypting with session %d: %w", i, err) + } + encrypted = true + } + } + + var result bytes.Buffer + result.Write(firstParmBytes) + // Write the rest of the parameters normally. + marshal(&result, parms[1:]...) + return result.Bytes(), nil +} + +// cmdSessions returns the authorization area of the command. +func cmdSessions(tpm *TPM, sess []Session, cc TPMCC, names []TPM2BName, parms []byte) ([]byte, error) { + // There is no authorization area if there are no sessions. + if len(sess) == 0 { + return nil, nil + } + // Find the non-first-session encryption and decryption session + // nonceTPMs, if any. + var encNonceTPM, decNonceTPM []byte + if len(sess) > 0 { + for i := 1; i < len(sess); i++ { + s := sess[i] + if s.IsEncryption() { + if encNonceTPM != nil { + // Only one encrypt session is permitted. + return nil, fmt.Errorf("too many encrypt sessions") + } + encNonceTPM = s.NonceTPM().Buffer + // A session used for both encryption and + // decryption only needs its nonce counted once. + continue + } + if s.IsDecryption() { + if decNonceTPM != nil { + // Only one decrypt session is permitted. + return nil, fmt.Errorf("too many decrypt sessions") + } + decNonceTPM = s.NonceTPM().Buffer + } + } + } + + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + // Skip space to write the size later + buf.Write(make([]byte, 4)) + // Calculate the authorization HMAC for each session + for i, s := range sess { + var addNonces []byte + // Special case: the HMAC on the first authorization session of + // a command also includes any decryption and encryption + // nonceTPMs, too. + if i == 0 { + addNonces = append(addNonces, decNonceTPM...) + addNonces = append(addNonces, encNonceTPM...) + } + auth, err := s.Authorize(cc, parms, addNonces, names, i) + if err != nil { + return nil, fmt.Errorf("session %d: %w", i, err) + } + marshal(buf, reflect.ValueOf(auth).Elem()) + } + + result := buf.Bytes() + // Write the size + binary.BigEndian.PutUint32(result[0:], uint32(buf.Len()-4)) + + return result, nil +} + +// cmdHeader returns the structured TPM command header. +func cmdHeader(hasSessions bool, length int, cc TPMCC) []byte { + tag := TPMSTNoSessions + if hasSessions { + tag = TPMSTSessions + } + hdr := TPMCmdHeader{ + Tag: tag, + Length: uint32(length), + CommandCode: cc, + } + buf := bytes.NewBuffer(make([]byte, 0, 8)) + marshal(buf, reflect.ValueOf(hdr)) + return buf.Bytes() +} + +// rspHeader parses the response header. If the TPM returned an error, +// returns an error here. +// rsp is updated to point to the rest of the response after the header. +func rspHeader(rsp *bytes.Buffer) error { + var hdr TPMRspHeader + if err := unmarshal(rsp, reflect.ValueOf(&hdr).Elem()); err != nil { + return fmt.Errorf("unmarshalling TPM response: %w", err) + } + if hdr.ResponseCode != TPMRCSuccess { + return hdr.ResponseCode + } + return nil +} + +// rspHandles parses the response handles area into the response structure. +// If there is a mismatch between the expected and actual amount of handles, +// returns an error here. +// rsp is updated to point to the rest of the response after the handles. +func rspHandles(rsp *bytes.Buffer, rspStruct Response) error { + handles := taggedMembers(reflect.ValueOf(rspStruct).Elem(), "handle", false) + for i, handle := range handles { + if err := unmarshal(rsp, handle); err != nil { + return fmt.Errorf("unmarshalling handle %v: %w", i, err) + } + } + return nil +} + +// rspParametersArea fetches, but does not manipulate, the parameters area +// from the response. If there is a mismatch between the response's +// indicated parameters area size and the actual size, returns an error here. +// rsp is updated to point to the rest of the response after the handles. +func rspParametersArea(hasSessions bool, rsp *bytes.Buffer) ([]byte, error) { + var length uint32 + if hasSessions { + if err := binary.Read(rsp, binary.BigEndian, &length); err != nil { + return nil, fmt.Errorf("reading length of parameter area: %w", err) + } + } else { + // If there are no sessions, there is no length-of-parameters + // field, because the whole rest of the response is the + // parameters area. + length = uint32(rsp.Len()) + } + if length > uint32(rsp.Len()) { + return nil, fmt.Errorf("response indicated %d bytes of parameters but there "+ + "were only %d more bytes of response", length, rsp.Len()) + } + if length > math.MaxInt32 { + return nil, fmt.Errorf("invalid length of parameter area: %d", length) + } + parms := make([]byte, int(length)) + if n, err := rsp.Read(parms); err != nil { + return nil, fmt.Errorf("reading parameter area: %w", err) + } else if n != len(parms) { + return nil, fmt.Errorf("only read %d bytes of parameters, expected %d", n, len(parms)) + } + return parms, nil +} + +// rspSessions fetches the sessions area of the response and updates all +// the sessions with it. If there is a response validation error, returns +// an error here. +// rsp is updated to point to the rest of the response after the sessions. +func rspSessions(rsp *bytes.Buffer, rc TPMRC, cc TPMCC, names []TPM2BName, parms []byte, sess []Session) error { + for i, s := range sess { + var auth TPMSAuthResponse + if err := unmarshal(rsp, reflect.ValueOf(&auth).Elem()); err != nil { + return fmt.Errorf("reading auth session %d: %w", i, err) + } + if err := s.Validate(rc, cc, parms, names, i, &auth); err != nil { + return fmt.Errorf("validating auth session %d: %w", i, err) + } + } + if rsp.Len() != 0 { + return fmt.Errorf("%d unaccounted-for bytes at the end of the TPM response", rsp.Len()) + } + return nil +} + +// rspParameters decrypts (if needed) the parameters area of the response +// into the response structure. If there is a mismatch between the expected +// and actual response structure, returns an error here. +func rspParameters(parms []byte, sess []Session, rspStruct Response) error { + parmsFields := taggedMembers(reflect.ValueOf(rspStruct).Elem(), "handle", true) + + // Use the heuristic of "does interpreting the first 2 bytes of response + // as a length make any sense" to attempt encrypted paramter decryption. + // If the command supports parameter encryption, the first paramter is + // a 2B. + if len(parms) < 2 { + return nil + } + length := binary.BigEndian.Uint16(parms[0:]) + // TODO: Make this nice using structure tagging. + if int(length)+2 <= len(parms) { + for i, s := range sess { + if !s.IsEncryption() { + continue + } + if err := s.Decrypt(parms[2 : 2+length]); err != nil { + return fmt.Errorf("decrypting first parameter with session %d: %w", i, err) + } + } + } + return unmarshal(bytes.NewBuffer(parms), parmsFields...) +} diff --git a/tpm2/direct/reflect_test.go b/tpm2/direct/reflect_test.go new file mode 100644 index 00000000..5712a05c --- /dev/null +++ b/tpm2/direct/reflect_test.go @@ -0,0 +1,208 @@ +package direct + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func marshalUnmarshal(t *testing.T, v interface{}, want []byte) { + t.Helper() + var buf bytes.Buffer + marshal(&buf, reflect.ValueOf(v)) + if !bytes.Equal(buf.Bytes(), want) { + t.Errorf("want %x got %x", want, buf.Bytes()) + } + got := reflect.New(reflect.TypeOf(v)) + err := unmarshal(&buf, got.Elem()) + if err != nil { + t.Fatalf("want nil, got %v", err) + } + if !cmp.Equal(v, got.Elem().Interface()) { + t.Errorf("want %#v, got %#v\n%v", v, got.Elem().Interface(), cmp.Diff(v, got.Elem().Interface())) + } +} + +func TestMarshalNumeric(t *testing.T) { + vals := map[interface{}][]byte{ + false: []byte{0}, + byte(1): []byte{1}, + int8(2): []byte{2}, + uint8(3): []byte{3}, + int16(260): []byte{1, 4}, + uint16(261): []byte{1, 5}, + int32(65542): []byte{0, 1, 0, 6}, + uint32(65543): []byte{0, 1, 0, 7}, + int64(4294967304): []byte{0, 0, 0, 1, 0, 0, 0, 8}, + uint64(4294967305): []byte{0, 0, 0, 1, 0, 0, 0, 9}, + } + for v, want := range vals { + t.Run(fmt.Sprintf("%v-%v", reflect.TypeOf(v), v), func(t *testing.T) { + marshalUnmarshal(t, v, want) + }) + } +} + +func TestMarshalArray(t *testing.T) { + vals := []struct { + Data interface{} + Serialization []byte + }{ + {[4]int8{1, 2, 3, 4}, []byte{1, 2, 3, 4}}, + {[3]uint16{5, 6, 7}, []byte{0, 5, 0, 6, 0, 7}}, + } + for _, val := range vals { + v, want := val.Data, val.Serialization + t.Run(fmt.Sprintf("%v-%v", reflect.TypeOf(v), v), func(t *testing.T) { + marshalUnmarshal(t, v, want) + }) + } +} + +func TestMarshalSlice(t *testing.T) { + // Slices in reflect/gotpm must be tagged marshalled/unmarshalled as + // part of a struct with the 'list' tag + type sliceWrapper struct { + Elems []uint32 `gotpm:"list"` + } + vals := []struct { + Name string + Data sliceWrapper + Serialization []byte + }{ + {"3", sliceWrapper{[]uint32{1, 2, 3}}, []byte{0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}}, + {"1", sliceWrapper{[]uint32{4}}, []byte{0, 0, 0, 1, 0, 0, 0, 4}}, + {"empty", sliceWrapper{[]uint32{}}, []byte{0, 0, 0, 0}}, + } + for _, val := range vals { + v, want := val.Data, val.Serialization + t.Run(val.Name, func(t *testing.T) { + marshalUnmarshal(t, v, want) + }) + } +} + +func TestMarshalBitfield(t *testing.T) { + type bitfield8 struct { + Bit0 uint8 `gotpm:"bit=0"` + Bit1 uint8 `gotpm:"bit=1"` + Bit2 uint8 `gotpm:"bit=2"` + Bit3 uint8 `gotpm:"bit=3"` + Bit4 uint8 `gotpm:"bit=4"` + Bit5 uint8 `gotpm:"bit=5"` + Bit6 uint8 `gotpm:"bit=6"` + Bit7 uint8 `gotpm:"bit=7"` + } + type bitfield32 struct { + Reserved1 uint16 `gotpm:"bit=5:0"` + Bit6 uint8 `gotpm:"bit=6"` + Reserved2 uint8 `gotpm:"bit=12:7"` + Bit13 bool `gotpm:"bit=13"` + Bits14Through18 uint8 `gotpm:"bit=18:14"` + Bit19 byte `gotpm:"bit=19"` + Reserved3 uint16 `gotpm:"bit=30:20"` + Bit31 uint32 `gotpm:"bit=31"` + } + t.Run("8bit", func(t *testing.T) { + v := bitfield8{ + Bit0: 0, + Bit1: 1, + Bit2: 0, + Bit3: 1, + Bit4: 1, + Bit5: 0, + Bit6: 0, + Bit7: 1, + } + want := []byte{0x9a} + marshalUnmarshal(t, v, want) + }) + t.Run("32bit", func(t *testing.T) { + v := bitfield32{ + Bit6: 1, + Bit13: false, + Bits14Through18: 29, + Bit19: 1, + Bit31: 1, + } + want := []byte{0x80, 0x0f, 0x40, 0x40} + marshalUnmarshal(t, v, want) + }) +} + +func TestMarshalUnion(t *testing.T) { + type valStruct struct { + First bool + Second int32 + } + type unionValue struct { + Val8 *uint8 `gotpm:"selector=8"` + Val64 *uint64 `gotpm:"selector=0x00000040"` + ValStruct *valStruct `gotpm:"selector=5"` // 5 for '5truct' + } + type unionEnvelope struct { + Type uint8 + OtherThing uint32 + Value unionValue `gotpm:"tag=Type"` + } + eight := uint8(8) + sixtyFour := uint64(64) + cases := []struct { + Name string + Data unionEnvelope + Serialization []byte + }{ + { + Name: "8", + Data: unionEnvelope{ + Type: 8, + OtherThing: 0xabcd1234, + Value: unionValue{ + Val8: &eight, + }, + }, + Serialization: []byte{ + 0x08, 0xab, 0xcd, 0x12, 0x34, 0x08, + }, + }, + { + Name: "64", + Data: unionEnvelope{ + Type: 64, + OtherThing: 0xffffffff, + Value: unionValue{ + Val64: &sixtyFour, + }, + }, + Serialization: []byte{ + 0x40, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + }, + }, + { + Name: "Struct", + Data: unionEnvelope{ + Type: 5, + OtherThing: 0x11111111, + Value: unionValue{ + ValStruct: &valStruct{ + First: true, + Second: 65537, + }, + }, + }, + Serialization: []byte{ + 0x05, 0x11, 0x11, 0x11, 0x11, 0x01, 0x00, 0x01, 0x00, 0x01, + }, + }, + } + + for _, c := range cases { + v, want := c.Data, c.Serialization + t.Run(c.Name, func(t *testing.T) { + marshalUnmarshal(t, v, want) + }) + } +} diff --git a/tpm2/direct/sealing_test.go b/tpm2/direct/sealing_test.go new file mode 100644 index 00000000..8276c9fc --- /dev/null +++ b/tpm2/direct/sealing_test.go @@ -0,0 +1,375 @@ +package direct + +import ( + "bytes" + "errors" + "testing" + + "github.com/google/go-tpm-tools/simulator" +) + +// Test creating and unsealing a sealed data blob with a password and HMAC. +func TestUnseal(t *testing.T) { + templates := map[string]TPM2BPublic{ + "RSA": RSASRKTemplate, + "ECC": ECCSRKTemplate, + } + + // Run the whole test for each of RSA and ECC SRKs. + for name, srkTemplate := range templates { + t.Run(name, func(t *testing.T) { + unsealingTest(t, srkTemplate) + }) + } +} + +func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { + 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 SRK + // Put a password on the SRK to test more of the flows. + srkAuth := []byte("mySRK") + createSRKCmd := CreatePrimaryCommand{ + PrimaryHandle: AuthHandle{ + Handle: TPMRHOwner, + }, + InSensitive: TPM2BSensitiveCreate{ + Sensitive: TPMSSensitiveCreate{ + UserAuth: TPM2BAuth{ + Buffer: srkAuth, + }, + }, + }, + InPublic: srkTemplate, + } + var createSRKRsp CreatePrimaryResponse + if err := tpm.Execute(&createSRKCmd, &createSRKRsp); err != nil { + t.Fatalf("%v", err) + } + t.Logf("SRK name: %x", createSRKRsp.Name) + defer func() { + // Flush the SRK + flushSRKCmd := FlushContextCommand{ + FlushHandle: createSRKRsp.ObjectHandle, + } + var flushSRKRsp FlushContextResponse + if err := tpm.Execute(&flushSRKCmd, &flushSRKRsp); err != nil { + t.Errorf("%v", err) + } + }() + + // Create a sealed blob under the SRK + data := []byte("secrets") + // Include some trailing zeros to exercise the TPM's trimming of them from auth values. + auth := []byte("p@ssw0rd\x00\x00") + auth2 := []byte("p@ssw0rd") + createBlobCmd := CreateCommand{ + ParentHandle: AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: PasswordAuth(srkAuth), + }, + InSensitive: TPM2BSensitiveCreate{ + Sensitive: TPMSSensitiveCreate{ + UserAuth: TPM2BAuth{ + Buffer: auth, + }, + Data: TPM2BData{ + Buffer: data, + }, + }, + }, + InPublic: TPM2BPublic{ + PublicArea: TPMTPublic{ + Type: TPMAlgKeyedHash, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + FixedParent: true, + UserWithAuth: true, + NoDA: true, + }, + }, + }, + } + var createBlobRsp CreateResponse + + // Create the blob with password auth, without any session encryption + t.Run("Create", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob using an hmac auth session also for audit + t.Run("CreateAudit", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AuditExclusive()) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob, using the auth session also for decryption + t.Run("CreateDecrypt", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptIn)) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob, using the auth session also for encryption + t.Run("CreateEncrypt", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptOut)) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob, using the auth session also for decrypt and encrypt + t.Run("CreateDecryptEncrypt", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptInOut)) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with decrypt and encrypt session + t.Run("CreateDecryptEncryptAudit", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptInOut), + Audit()) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with decrypt and encrypt session bound to SRK + t.Run("CreateDecryptEncryptSalted", func(t *testing.T) { + createBlobCmd.ParentHandle.Auth = + HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptInOut), + Salted(createSRKRsp.ObjectHandle, createSRKRsp.OutPublic.PublicArea)) + if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + t.Fatalf("%v", err) + } + }) + + // Use HMAC auth to authorize the rest of the Create commands + // Exercise re-using a use-once HMAC structure (which will spin up the session each time) + createBlobCmd.ParentHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(srkAuth)) + // Create the blob with a separate decrypt and encrypt session + t.Run("CreateDecryptEncryptSeparate", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut))); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with a separate decrypt and encrypt session, and another for audit + t.Run("CreateDecryptEncryptAuditSeparate", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), + HMAC(TPMAlgSHA256, 16, Audit())); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with a separate decrypt and encrypt session, and another for exclusive audit + t.Run("CreateDecryptEncryptAuditExclusiveSeparate", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), + HMAC(TPMAlgSHA256, 16, AuditExclusive())); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with separate decrypt and encrypt sessions. + t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + // Get weird with the algorithm and nonce choices. Mix lots of things together. + HMAC(TPMAlgSHA1, 20, AESEncryption(128, EncryptIn)), + HMAC(TPMAlgSHA384, 23, AESEncryption(128, EncryptOut))); err != nil { + t.Fatalf("%v", err) + } + }) + + // Create the blob with separate encrypt and decrypt sessions. + // (The TPM spec orders some extra nonces included in the first session in the order + // nonceTPM_decrypt, nonceTPM_encrypt, so this exercises that) + t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { + if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + HMAC(TPMAlgSHA1, 17, AESEncryption(128, EncryptOut)), + HMAC(TPMAlgSHA256, 32, AESEncryption(128, EncryptIn))); err != nil { + t.Fatalf("%v", err) + } + }) + + // Load the sealed blob + loadBlobCmd := LoadCommand{ + ParentHandle: AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth)), + }, + InPrivate: createBlobRsp.OutPrivate, + InPublic: createBlobRsp.OutPublic, + } + var loadBlobRsp LoadResponse + if err := tpm.Execute(&loadBlobCmd, &loadBlobRsp); err != nil { + t.Fatalf("%v", err) + } + defer func() { + // Flush the blob + flushBlobCmd := FlushContextCommand{ + FlushHandle: loadBlobRsp.ObjectHandle, + } + var flushBlobRsp FlushContextResponse + if err := tpm.Execute(&flushBlobCmd, &flushBlobRsp); err != nil { + t.Errorf("%v", err) + } + }() + + unsealCmd := UnsealCommand{ + ItemHandle: AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + }, + } + var unsealRsp UnsealResponse + // Unseal the blob with a password session + t.Run("WithPassword", func(t *testing.T) { + unsealCmd.ItemHandle.Auth = PasswordAuth(auth) + if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + }) + + // Unseal the blob with an incorrect password session + t.Run("WithWrongPassword", func(t *testing.T) { + unsealCmd.ItemHandle.Auth = PasswordAuth([]byte("NotThePassword")) + err := tpm.Execute(&unsealCmd, &unsealRsp) + if err == nil { + t.Errorf("want TPM_RC_BAD_AUTH, got nil") + } + if !errors.Is(err, TPMRCBadAuth) { + t.Errorf("want TPM_RC_BAD_AUTH, got %v", err) + } + var fmt1 Fmt1Error + if !errors.As(err, &fmt1) { + t.Errorf("want a Fmt1Error, got %v", err) + } else if isSession, session := fmt1.Session(); !isSession || session != 1 { + t.Errorf("want TPM_RC_BAD_AUTH on session 1, got %v", err) + } + }) + + // Unseal the blob with a use-once HMAC session + t.Run("WithHMAC", func(t *testing.T) { + unsealCmd.ItemHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(auth2)) + if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + }) + + // Unseal the blob with a use-once HMAC session with encryption + t.Run("WithHMACEncrypt", func(t *testing.T) { + unsealCmd.ItemHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(auth2), + AESEncryption(128, EncryptOut)) + if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + }) + + // Unseal the blob with a standalone HMAC session, re-using the session. + t.Run("WithHMACSession", func(t *testing.T) { + sess, cleanup, err := HMACSession(tpm, TPMAlgSHA1, 20, Auth(auth2)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup() + unsealCmd.ItemHandle.Auth = sess + + // It should be possible to use the session multiple times. + for i := 0; i < 3; i++ { + if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + } + }) + + // Unseal the blob with a standalone bound HMAC session, re-using the session. + // Also, use session encryption. + t.Run("WithHMACSessionEncrypt", func(t *testing.T) { + sess, cleanup, err := HMACSession(tpm, TPMAlgSHA256, 16, Auth(auth2), + AESEncryption(128, EncryptOut), + Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup() + unsealCmd.ItemHandle.Auth = sess + + // It should be possible to use the session multiple times. + for i := 0; i < 3; i++ { + if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + } + }) + + // Unseal the blob with a standalone HMAC session, re-using the session. + // Spin up another bound session for encryption. + t.Run("WithHMACSessionEncryptSeparate", func(t *testing.T) { + sess1, cleanup1, err := HMACSession(tpm, TPMAlgSHA1, 16, Auth(auth2)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup1() + sess2, cleanup2, err := HMACSession(tpm, TPMAlgSHA384, 16, + AESEncryption(128, EncryptOut), + Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup2() + unsealCmd.ItemHandle.Auth = sess1 + + // It should be possible to use the sessions multiple times. + for i := 0; i < 3; i++ { + if err := tpm.Execute(&unsealCmd, &unsealRsp, sess2); err != nil { + t.Errorf("%v", err) + } + if !bytes.Equal(unsealRsp.OutData.Buffer, data) { + t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) + } + } + }) +} diff --git a/tpm2/direct/sessions.go b/tpm2/direct/sessions.go new file mode 100644 index 00000000..c5504e80 --- /dev/null +++ b/tpm2/direct/sessions.go @@ -0,0 +1,982 @@ +package direct + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/elliptic" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "encoding/binary" + "fmt" + + "github.com/google/go-tpm/tpm2" +) + +// Session represents a session in the TPM. +type Session interface { + // Initializes the session, if needed. Has no effect if not needed or + // already done. Some types of sessions may need to be initialized + // just-in-time, e.g., to support calling patterns that help the user + // securely authorize their actions without writing a lot of code. + Init(tpm *TPM) error + // Cleans up the session, if needed. + // Some types of session need to be cleaned up if the command failed, + // again to support calling patterns that help the user securely + // authorize their actions without writing a lot of code. + CleanupFailure(tpm *TPM) error + // The last nonceTPM for this session. + NonceTPM() TPM2BNonce + // Updates nonceCaller to a new random value. + NewNonceCaller() error + // Computes the authorization HMAC for the session. + // If this is the first authorization session for a command, and + // there is another session (or sessions) for parameter + // decryption and/or encryption, then addNonces contains the + // nonceTPMs from each of them, respectively (see Part 1, 19.6.5) + Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) + // Validates the response for the session. + // Updates NonceTPM for the session. + Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error + // Returns true if this is an encryption session. + IsEncryption() bool + // Returns true if this is a decryption session. + IsDecryption() bool + // If this session is used for parameter decryption, encrypts the + // parameter. Otherwise, does not modify the parameter. + Encrypt(parameter []byte) error + // If this session is used for parameter encryption, encrypts the + // parameter. Otherwise, does not modify the parameter. + Decrypt(parameter []byte) error + // Returns the handle value of this session. + Handle() TPMHandle +} + +// cpHash calculates the TPM command parameter hash. +// cpHash = hash(CC || names || parms) +func cpHash(alg TPMIAlgHash, cc TPMCC, names []TPM2BName, parms []byte) []byte { + h := alg.Hash().New() + binary.Write(h, binary.BigEndian, cc) + for _, name := range names { + h.Write(name.Buffer) + } + h.Write(parms) + return h.Sum(nil) +} + +// rpHash calculates the TPM response parameter hash. +// rpHash = hash(RC || CC || parms) +func rpHash(alg TPMIAlgHash, rc TPMRC, cc TPMCC, parms []byte) []byte { + h := alg.Hash().New() + binary.Write(h, binary.BigEndian, rc) + binary.Write(h, binary.BigEndian, cc) + h.Write(parms) + return h.Sum(nil) +} + +// pwSession represents a password-pseudo-session. +type pwSession struct { + auth []byte +} + +// PasswordAuth assembles a password pseudo-session with the given auth value. +func PasswordAuth(auth []byte) Session { + return &pwSession{ + auth: auth, + } +} + +// Init is not required and has no effect for a password session. +func (s *pwSession) Init(tpm *TPM) error { return nil } + +// Cleanup is not required and has no effect for a password session. +func (s *pwSession) CleanupFailure(tpm *TPM) error { return nil } + +// NonceTPM normally returns the last nonceTPM value from the session. +// Since a password session is a pseudo-session with the auth value stuffed +// in where the HMAC should go, this is not used. +func (s *pwSession) NonceTPM() TPM2BNonce { return TPM2BNonce{} } + +// NewNonceCaller updates the nonceCaller for this session. +// Password sessions don't have nonces. +func (s *pwSession) NewNonceCaller() error { return nil } + +// Computes the authorization structure for the session. +func (s *pwSession) Authorize(cc TPMCC, parms, addNonces []byte, _ []TPM2BName, _ int) (*TPMSAuthCommand, error) { + return &TPMSAuthCommand{ + Handle: TPMRSPW, + Nonce: TPM2BNonce{}, + Attributes: TPMASession{}, + Authorization: TPM2BData{ + Buffer: s.auth, + }, + }, nil +} + +// Validates the response session structure for the session. +func (s *pwSession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { + if len(auth.Nonce.Buffer) != 0 { + return fmt.Errorf("expected empty nonce in response auth to PW session, got %x", auth.Nonce) + } + expectedAttrs := TPMASession{ + ContinueSession: true, + } + if auth.Attributes != expectedAttrs { + return fmt.Errorf("expected only ContinueSession in response auth to PW session, got %v", auth.Attributes) + } + if len(auth.Authorization.Buffer) != 0 { + return fmt.Errorf("expected empty HMAC in response auth to PW session, got %x", auth.Authorization) + } + return nil +} + +// IsEncryption returns true if this is an encryption session. +// Password sessions can't be used for encryption. +func (s *pwSession) IsEncryption() bool { return false } + +// IsDecryption returns true if this is a decryption session. +// Password sessions can't be used for decryption. +func (s *pwSession) IsDecryption() bool { return false } + +// If this session is used for parameter decryption, encrypts the +// parameter. Otherwise, does not modify the parameter. +// Password sessions can't be used for decryption. +func (s *pwSession) Encrypt(parameter []byte) error { return nil } + +// If this session is used for parameter encryption, encrypts the +// parameter. Otherwise, does not modify the parameter. +// Password sessions can't be used for encryption. +func (s *pwSession) Decrypt(parameter []byte) error { return nil } + +// Handle returns the handle value associated with this session. +// In the case of a password session, this is always TPM_RS_PW. +func (s *pwSession) Handle() TPMHandle { return TPMRSPW } + +// sessionOptions represents extra options used when setting up an HMAC or policy session. +type sessionOptions struct { + auth []byte + password bool + bindHandle TPMIDHEntity + bindName TPM2BName + bindAuth []byte + saltHandle TPMIDHObject + saltPub TPMTPublic + attrs TPMASession + symmetric TPMTSymDef +} + +// defaultOptions represents the default options used when none are provided. +func defaultOptions() sessionOptions { + return sessionOptions{ + symmetric: TPMTSymDef{ + Algorithm: TPMAlgNull, + }, + bindHandle: TPMRHNull, + saltHandle: TPMRHNull, + } +} + +// AuthOption is an option for setting up an auth session variadically. +type AuthOption func(*sessionOptions) + +// Auth uses the session to prove knowledge of the object's auth value. +func Auth(auth []byte) AuthOption { + return func(o *sessionOptions) { + o.auth = auth + } +} + +// Deprecated: This is not recommended and is only provided for completeness; +// use Auth instead. +// Password is a policy-session-only option that specifies to provide the +// object's auth value in place of the authorization HMAC when authorizing. +// For HMAC sessions, has the same effect as using Auth. +func Password(auth []byte) AuthOption { + return func(o *sessionOptions) { + o.auth = auth + o.password = true + } +} + +// Bound specifies that this session's session key should depend on the auth +// value of the given object. +func Bound(handle TPMIDHEntity, name TPM2BName, auth []byte) AuthOption { + return func(o *sessionOptions) { + o.bindHandle = handle + o.bindName = name + o.bindAuth = auth + } +} + +// Salted specifies that this session's session key should depend on an +// encrypted seed value using the given public key. +// 'handle' must refer to a loaded RSA or ECC key. +func Salted(handle TPMIDHObject, pub TPMTPublic) AuthOption { + return func(o *sessionOptions) { + o.saltHandle = handle + o.saltPub = pub + } +} + +// parameterEncryptionDirection specifies whether the session-encrypted +// parameters are encrypted on the way into the TPM, out of the TPM, or both. +type parameterEncryptionDirection int + +const ( + EncryptIn parameterEncryptionDirection = 1 + iota + EncryptOut + EncryptInOut +) + +// AESEncryption uses the session to encrypt the first parameter sent to/from +// the TPM. +// Note that only commands whose first command/response parameter is a 2B can +// support session encryption. +func AESEncryption(keySize TPMKeyBits, dir parameterEncryptionDirection) AuthOption { + return func(o *sessionOptions) { + o.attrs.Decrypt = (dir == EncryptIn || dir == EncryptInOut) + o.attrs.Encrypt = (dir == EncryptOut || dir == EncryptInOut) + o.symmetric = TPMTSymDef{ + Algorithm: TPMAlgAES, + KeyBits: TPMUSymKeyBits{ + AES: NewTPMKeyBits(keySize), + }, + Mode: TPMUSymMode{ + AES: NewTPMAlgID(TPMAlgCFB), + }, + } + } +} + +// Audit uses the session to compute extra HMACs. +// An Audit session can be used with GetSessionAuditDigest to obtain attestation +// over a sequence of commands. +func Audit() AuthOption { + return func(o *sessionOptions) { + o.attrs.Audit = true + } +} + +// AuditExclusive is like an audit session, but even more powerful. +// This allows an audit session to additionally indicate that no other auditable +// commands were executed other than the ones described by the audit hash. +func AuditExclusive() AuthOption { + return func(o *sessionOptions) { + o.attrs.Audit = true + o.attrs.AuditExclusive = true + } +} + +// hmacSession generally implements the HMAC session. +type hmacSession struct { + sessionOptions + hash TPMIAlgHash + nonceSize int + handle TPMHandle + sessionKey []byte + // last nonceCaller + nonceCaller TPM2BNonce + // last nonceTPM + nonceTPM TPM2BNonce +} + +// HMAC sets up a just-in-time HMAC session that is used only once. +// A real session is created, but just in time and it is flushed when used. +func HMAC(hash TPMIAlgHash, nonceSize int, opts ...AuthOption) Session { + // Set up a one-off session that knows the auth value. + sess := hmacSession{ + sessionOptions: defaultOptions(), + hash: hash, + nonceSize: nonceSize, + handle: TPMRHNull, + } + for _, opt := range opts { + opt(&sess.sessionOptions) + } + return &sess +} + +// HMACSession sets up a reusable HMAC session that needs to be closed. +func HMACSession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { + // Set up a not-one-off session that knows the auth value. + sess := hmacSession{ + sessionOptions: defaultOptions(), + hash: hash, + nonceSize: nonceSize, + handle: TPMRHNull, + } + for _, opt := range opts { + opt(&sess.sessionOptions) + } + // This session is reusable and is closed with the function we'll + // return. + sess.sessionOptions.attrs.ContinueSession = true + + // Initialize the session. + if err := sess.Init(tpm); err != nil { + return nil, nil, err + } + + closer := func() error { + flushCmd := FlushContextCommand{ + FlushHandle: sess.handle, + } + var flushRsp FlushContextResponse + return tpm.Execute(&flushCmd, &flushRsp) + } + + return &sess, closer, nil +} + +// Part 1, B.10.2 +func getEncryptedSaltRSA(nameAlg TPMIAlgHash, parms *TPMSRSAParms, pub *TPM2BPublicKeyRSA) (*TPM2BEncryptedSecret, []byte, error) { + rsaPub, err := rsaPub(parms, pub) + if err != nil { + return nil, nil, fmt.Errorf("could not encrypt salt to RSA key: %w", err) + } + // Odd special case: the size of the salt depends on the RSA scheme's + // hash alg. + var hAlg TPMIAlgHash + switch parms.Scheme.Scheme { + case TPMAlgRSASSA: + hAlg = parms.Scheme.Details.RSASSA.HashAlg + case TPMAlgRSAES: + hAlg = nameAlg + case TPMAlgRSAPSS: + hAlg = parms.Scheme.Details.RSAPSS.HashAlg + case TPMAlgOAEP: + hAlg = parms.Scheme.Details.OAEP.HashAlg + case TPMAlgNull: + hAlg = nameAlg + default: + return nil, nil, fmt.Errorf("unsupported RSA salt key scheme: %v", parms.Scheme.Scheme) + } + salt := make([]byte, hAlg.Hash().Size()) + if _, err := rand.Read(salt); err != nil { + return nil, nil, fmt.Errorf("generating random salt: %w", err) + } + // Part 1, section 4.6 specifies the trailing NULL byte for the label. + encSalt, err := rsa.EncryptOAEP(hAlg.Hash().New(), rand.Reader, rsaPub, salt, []byte("SECRET\x00")) + if err != nil { + return nil, nil, fmt.Errorf("encrypting salt: %w", err) + } + return &TPM2BEncryptedSecret{ + Buffer: encSalt, + }, salt, nil +} + +// Part 1, 19.6.13 +func getEncryptedSaltECC(nameAlg TPMIAlgHash, parms *TPMSECCParms, pub *TPMSECCPoint) (*TPM2BEncryptedSecret, []byte, error) { + curve, err := parms.CurveID.Curve() + if err != nil { + return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) + } + eccPub, err := eccPub(parms, pub) + if err != nil { + return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) + } + ephPriv, ephPubX, ephPubY, err := elliptic.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) + } + zx, _ := curve.Params().ScalarMult(eccPub.x, eccPub.y, ephPriv) + // ScalarMult returns a big.Int, whose Bytes() function may return the + // compacted form. In our case, we want to left-pad zx to the size of + // the curve. + z := make([]byte, (curve.Params().BitSize+7)/8) + zx.FillBytes(z) + salt := tpm2.KDFeHash(nameAlg.Hash(), z, "SECRET", ephPubX.Bytes(), pub.X.Buffer, nameAlg.Hash().Size()*8) + + var encSalt bytes.Buffer + binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubX.Bytes()))) + encSalt.Write(ephPubX.Bytes()) + binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubY.Bytes()))) + encSalt.Write(ephPubY.Bytes()) + return &TPM2BEncryptedSecret{ + Buffer: encSalt.Bytes(), + }, salt, nil +} + +// getEncryptedSalt creates a salt value for salted sessions. +// Returns the encrypted salt and plaintext salt, or an error value. +func getEncryptedSalt(pub TPMTPublic) (*TPM2BEncryptedSecret, []byte, error) { + switch pub.Type { + case TPMAlgRSA: + return getEncryptedSaltRSA(pub.NameAlg, pub.Parameters.RSADetail, pub.Unique.RSA) + case TPMAlgECC: + return getEncryptedSaltECC(pub.NameAlg, pub.Parameters.ECCDetail, pub.Unique.ECC) + default: + return nil, nil, fmt.Errorf("salt encryption alg '%v' not supported", pub.Type) + } +} + +// Init initializes the session, just in time, if needed. +func (s *hmacSession) Init(tpm *TPM) error { + if s.handle != TPMRHNull { + // Session is already initialized. + return nil + } + + // Get a high-quality nonceCaller for our use. + // Store it with the session object for later reference. + s.nonceCaller = TPM2BNonce{ + Buffer: make([]byte, s.nonceSize), + } + if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { + return err + } + + // Start up the actual auth session. + sasCmd := StartAuthSessionCommand{ + TPMKey: s.saltHandle, + Bind: s.bindHandle, + NonceCaller: s.nonceCaller, + SessionType: TPMSEHMAC, + Symmetric: s.symmetric, + AuthHash: s.hash, + } + var salt []byte + if s.saltHandle != TPMRHNull { + var err error + var encSalt *TPM2BEncryptedSecret + encSalt, salt, err = getEncryptedSalt(s.saltPub) + if err != nil { + return err + } + sasCmd.EncryptedSalt = *encSalt + } + var sasRsp StartAuthSessionResponse + if err := tpm.Execute(&sasCmd, &sasRsp); err != nil { + return err + } + s.handle = sasRsp.SessionHandle + s.nonceTPM = sasRsp.NonceTPM + // Part 1, 19.6 + if s.bindHandle != TPMRHNull || len(salt) != 0 { + var authSalt []byte + authSalt = append(authSalt, s.bindAuth...) + authSalt = append(authSalt, salt...) + s.sessionKey = tpm2.KDFaHash(s.hash.Hash(), authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, s.hash.Hash().Size()*8) + } + return nil +} + +// Cleanup cleans up the session, if needed. +func (s *hmacSession) CleanupFailure(tpm *TPM) error { + // The user is already responsible to clean up this session. + if s.attrs.ContinueSession { + return nil + } + flushCmd := FlushContextCommand{ + FlushHandle: s.handle, + } + var flushRsp FlushContextResponse + if err := tpm.Execute(&flushCmd, &flushRsp); err != nil { + return err + } + s.handle = TPMRHNull + return nil +} + +// NonceTPM returns the last nonceTPM value from the session. +// May be nil, if the session hasn't been initialized yet. +func (s *hmacSession) NonceTPM() TPM2BNonce { return s.nonceTPM } + +// To avoid a circular dependency on gotpm by tpm2, implement a +// tiny serialization by hand for TPMASession here +func attrsToBytes(attrs TPMASession) []byte { + var res byte + if attrs.ContinueSession { + res |= (1 << 0) + } + if attrs.AuditExclusive { + res |= (1 << 1) + } + if attrs.AuditReset { + res |= (1 << 2) + } + if attrs.Reserved1 { + res |= (1 << 3) + } + if attrs.Reserved2 { + res |= (1 << 4) + } + if attrs.Decrypt { + res |= (1 << 5) + } + if attrs.Encrypt { + res |= (1 << 6) + } + if attrs.Audit { + res |= (1 << 7) + } + return []byte{res} +} + +// computeHMAC computes an authorization HMAC according to various equations in +// Part 1. +// This applies to both commands and responses. +// The value of key depends on whether the session is bound and/or salted. +// pHash cpHash for a command, or an rpHash for a response. +// nonceNewer in a command is the new nonceCaller sent in the command session +// packet. +// nonceNewer in a response is the new nonceTPM sent in the response session +// packet. +// nonceOlder in a command is the last nonceTPM sent by the TPM for this +// session. This may be when the session was created, or the last time it was +// used. +// nonceOlder in a response is the corresponding nonceCaller sent in the +// command. +func computeHMAC(alg TPMIAlgHash, key, pHash, nonceNewer, nonceOlder, addNonces []byte, attrs TPMASession) ([]byte, error) { + mac := hmac.New(alg.Hash().New, key) + mac.Write(pHash) + mac.Write(nonceNewer) + mac.Write(nonceOlder) + mac.Write(addNonces) + mac.Write(attrsToBytes(attrs)) + return mac.Sum(nil), nil +} + +// Trim trailing zeros from the auth value. Part 1, 19.6.5, Note 2 +// Does not allocate a new underlying byte array. +func hmacKeyFromAuthValue(auth []byte) []byte { + key := auth + for i := len(key) - 1; i >= 0; i-- { + if key[i] == 0 { + key = key[:i] + } + } + return key +} + +// NewNonceCaller updates the nonceCaller for this session. +func (s *hmacSession) NewNonceCaller() error { + _, err := rand.Read(s.nonceCaller.Buffer) + return err +} + +// Authorize computes the authorization structure for the session. +// Unlike the TPM spec, authIndex is zero-based. +func (s *hmacSession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) { + if s.handle == TPMRHNull { + // Session is not initialized. + return nil, fmt.Errorf("session not initialized") + } + + // Part 1, 19.6 + // HMAC key is (sessionKey || auth) unless this session is authorizing + // its bind target + var hmacKey []byte + hmacKey = append(hmacKey, s.sessionKey...) + if len(s.bindName.Buffer) == 0 || authIndex >= len(names) || !bytes.Equal(names[authIndex].Buffer, s.bindName.Buffer) { + hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) + } + + // Compute the authorization HMAC. + hmac, err := computeHMAC(s.hash, hmacKey, cpHash(s.hash, cc, names, parms), + s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) + if err != nil { + return nil, err + } + result := TPMSAuthCommand{ + Handle: s.handle, + Nonce: s.nonceCaller, + Attributes: s.attrs, + Authorization: TPM2BData{ + Buffer: hmac, + }, + } + return &result, nil +} + +// Validate validates the response session structure for the session. +// It updates nonceTPM from the TPM's response. +func (s *hmacSession) Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error { + // Track the new nonceTPM for the session. + s.nonceTPM = auth.Nonce + // Track the session being automatically flushed. + if !auth.Attributes.ContinueSession { + s.handle = TPMRHNull + } + + // Part 1, 19.6 + // HMAC key is (sessionKey || auth) unless this session is authorizing + // its bind target + var hmacKey []byte + hmacKey = append(hmacKey, s.sessionKey...) + if len(s.bindName.Buffer) == 0 || authIndex >= len(names) || !bytes.Equal(names[authIndex].Buffer, s.bindName.Buffer) { + hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) + } + + // Compute the authorization HMAC. + mac, err := computeHMAC(s.hash, hmacKey, rpHash(s.hash, rc, cc, parms), + s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) + if err != nil { + return err + } + // Compare the HMAC (constant time) + if !hmac.Equal(mac, auth.Authorization.Buffer) { + return fmt.Errorf("incorrect authorization HMAC") + } + return nil +} + +// IsEncryption returns true if this is an encryption session. +func (s *hmacSession) IsEncryption() bool { + return s.attrs.Encrypt +} + +// IsDecryption returns true if this is a decryption session. +func (s *hmacSession) IsDecryption() bool { + return s.attrs.Decrypt +} + +// Encrypt decrypts the parameter in place, if this session is used for +// parameter decryption. Otherwise, it does not modify the parameter. +func (s *hmacSession) Encrypt(parameter []byte) error { + if !s.IsDecryption() { + return nil + } + // Only AES-CFB is supported. + keyBytes := *s.symmetric.KeyBits.AES / 8 + keyIVBytes := int(keyBytes) + 16 + var sessionValue []byte + sessionValue = append(sessionValue, s.sessionKey...) + sessionValue = append(sessionValue, s.auth...) + keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) + key, err := aes.NewCipher(keyIV[:keyBytes]) + if err != nil { + return err + } + stream := cipher.NewCFBEncrypter(key, keyIV[keyBytes:]) + stream.XORKeyStream(parameter, parameter) + return nil +} + +// Decrypt encrypts the parameter in place, if this session is used for +// parameter encryption. Otherwise, it does not modify the parameter. +func (s *hmacSession) Decrypt(parameter []byte) error { + if !s.IsEncryption() { + return nil + } + // Only AES-CFB is supported. + keyBytes := *s.symmetric.KeyBits.AES / 8 + keyIVBytes := int(keyBytes) + 16 + // Part 1, 21.1 + var sessionValue []byte + sessionValue = append(sessionValue, s.sessionKey...) + sessionValue = append(sessionValue, s.auth...) + keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) + key, err := aes.NewCipher(keyIV[:keyBytes]) + if err != nil { + return err + } + stream := cipher.NewCFBDecrypter(key, keyIV[keyBytes:]) + stream.XORKeyStream(parameter, parameter) + return nil +} + +// Handle returns the handle value of the session. +// If the session is created with HMAC (instead of HMACSession) this will be +// TPM_RH_NULL. +func (s *hmacSession) Handle() TPMHandle { + return s.handle +} + +// PolicyCallback represents an object's policy in the form of a function. +// This function makes zero or more TPM policy commands and returns error. +type PolicyCallback = func(tpm *TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error + +// policySession generally implements the policy session. +type policySession struct { + sessionOptions + hash TPMIAlgHash + nonceSize int + handle TPMHandle + sessionKey []byte + // last nonceCaller + nonceCaller TPM2BNonce + // last nonceTPM + nonceTPM TPM2BNonce + callback *PolicyCallback +} + +// Policy sets up a just-in-time policy session that created each time it's +// needed. +// Each time the policy is created, the callback is invoked to authorize the +// session. +// A real session is created, but just in time, and it is flushed when used. +func Policy(hash TPMIAlgHash, nonceSize int, callback PolicyCallback, opts ...AuthOption) Session { + // Set up a one-off session that knows the auth value. + sess := policySession{ + sessionOptions: defaultOptions(), + hash: hash, + nonceSize: nonceSize, + handle: TPMRHNull, + callback: &callback, + } + for _, opt := range opts { + opt(&sess.sessionOptions) + } + return &sess +} + +// PolicySession opens a policy session that needs to be closed. +// The caller is responsible to call whichever policy commands they want in the +// session. +// Note that the TPM resets a policy session after it is successfully used. +func PolicySession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { + // Set up a not-one-off session that knows the auth value. + sess := policySession{ + sessionOptions: defaultOptions(), + hash: hash, + nonceSize: nonceSize, + handle: TPMRHNull, + } + for _, opt := range opts { + opt(&sess.sessionOptions) + } + + // This session is reusable and is closed with the function we'll + // return. + sess.sessionOptions.attrs.ContinueSession = true + + // Initialize the session. + if err := sess.Init(tpm); err != nil { + return nil, nil, err + } + + closer := func() error { + flushCmd := FlushContextCommand{ + FlushHandle: sess.handle, + } + var flushRsp FlushContextResponse + return tpm.Execute(&flushCmd, &flushRsp) + } + + return &sess, closer, nil +} + +// Init initializes the session, just in time, if needed. +func (s *policySession) Init(tpm *TPM) error { + if s.handle != TPMRHNull { + // Session is already initialized. + return nil + } + + // Get a high-quality nonceCaller for our use. + // Store it with the session object for later reference. + s.nonceCaller = TPM2BNonce{ + Buffer: make([]byte, s.nonceSize), + } + if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { + return err + } + + // Start up the actual auth session. + sasCmd := StartAuthSessionCommand{ + TPMKey: s.saltHandle, + Bind: s.bindHandle, + NonceCaller: s.nonceCaller, + SessionType: TPMSEPolicy, + Symmetric: s.symmetric, + AuthHash: s.hash, + } + var salt []byte + if s.saltHandle != TPMRHNull { + var err error + var encSalt *TPM2BEncryptedSecret + encSalt, salt, err = getEncryptedSalt(s.saltPub) + if err != nil { + return err + } + sasCmd.EncryptedSalt = *encSalt + } + var sasRsp StartAuthSessionResponse + if err := tpm.Execute(&sasCmd, &sasRsp); err != nil { + return err + } + s.handle = sasRsp.SessionHandle + s.nonceTPM = sasRsp.NonceTPM + // Part 1, 19.6 + if s.bindHandle != TPMRHNull || len(salt) != 0 { + var authSalt []byte + authSalt = append(authSalt, s.bindAuth...) + authSalt = append(authSalt, salt...) + s.sessionKey = tpm2.KDFaHash(s.hash.Hash(), authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, s.hash.Hash().Size()*8) + } + + // Call the callback to execute the policy, if needed + if s.callback != nil { + if err := (*s.callback)(tpm, s.handle, s.nonceTPM); err != nil { + return fmt.Errorf("executing policy: %w", err) + } + } + + return nil +} + +// CleanupFailure cleans up the session, if needed. +func (s *policySession) CleanupFailure(tpm *TPM) error { + // The user is already responsible to clean up this session. + if s.attrs.ContinueSession { + return nil + } + flushCmd := FlushContextCommand{ + FlushHandle: s.handle, + } + var flushRsp FlushContextResponse + if err := tpm.Execute(&flushCmd, &flushRsp); err != nil { + return err + } + s.handle = TPMRHNull + return nil +} + +// NonceTPM returns the last nonceTPM value from the session. +// May be nil, if the session hasn't been initialized yet. +func (s *policySession) NonceTPM() TPM2BNonce { return s.nonceTPM } + +// NewNonceCaller updates the nonceCaller for this session. +func (s *policySession) NewNonceCaller() error { + _, err := rand.Read(s.nonceCaller.Buffer) + return err +} + +// Authorize computes the authorization structure for the session. +func (s *policySession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, _ int) (*TPMSAuthCommand, error) { + if s.handle == TPMRHNull { + // Session is not initialized. + return nil, fmt.Errorf("session not initialized") + } + + var hmac []byte + if s.password { + hmac = s.auth + } else { + // Part 1, 19.6 + // HMAC key is (sessionKey || auth). + var hmacKey []byte + hmacKey = append(hmacKey, s.sessionKey...) + hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) + + // Compute the authorization HMAC. + var err error + hmac, err = computeHMAC(s.hash, hmacKey, cpHash(s.hash, cc, names, parms), + s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) + if err != nil { + return nil, err + } + } + + result := TPMSAuthCommand{ + Handle: s.handle, + Nonce: s.nonceCaller, + Attributes: s.attrs, + Authorization: TPM2BData{ + Buffer: hmac, + }, + } + return &result, nil +} + +// Validate valitades the response session structure for the session. +// Updates nonceTPM from the TPM's response. +func (s *policySession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { + // Track the new nonceTPM for the session. + s.nonceTPM = auth.Nonce + // Track the session being automatically flushed. + if !auth.Attributes.ContinueSession { + s.handle = TPMRHNull + } + + if s.password { + // If we used a password, expect no nonce and no response HMAC. + if len(auth.Nonce.Buffer) != 0 { + return fmt.Errorf("expected empty nonce in response auth to PW policy, got %x", auth.Nonce) + } + if len(auth.Authorization.Buffer) != 0 { + return fmt.Errorf("expected empty HMAC in response auth to PW policy, got %x", auth.Authorization) + } + } else { + // Part 1, 19.6 + // HMAC key is (sessionKey || auth). + var hmacKey []byte + hmacKey = append(hmacKey, s.sessionKey...) + hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) + // Compute the authorization HMAC. + mac, err := computeHMAC(s.hash, hmacKey, rpHash(s.hash, rc, cc, parms), + s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) + if err != nil { + return err + } + // Compare the HMAC (constant time) + if !hmac.Equal(mac, auth.Authorization.Buffer) { + return fmt.Errorf("incorrect authorization HMAC") + } + } + return nil +} + +// IsEncryption returns true if this is an encryption session. +func (s *policySession) IsEncryption() bool { + return s.attrs.Encrypt +} + +// IsDecryption returns true if this is a decryption session. +func (s *policySession) IsDecryption() bool { + return s.attrs.Decrypt +} + +// Encrypt encrypts the parameter in place, if this session is used for +// parameter decryption. Otherwise, it does not modify the parameter. +func (s *policySession) Encrypt(parameter []byte) error { + if !s.IsDecryption() { + return nil + } + // Only AES-CFB is supported. + keyBytes := *s.symmetric.KeyBits.AES / 8 + keyIVBytes := int(keyBytes) + 16 + var sessionValue []byte + sessionValue = append(sessionValue, s.sessionKey...) + sessionValue = append(sessionValue, s.auth...) + keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) + key, err := aes.NewCipher(keyIV[:keyBytes]) + if err != nil { + return err + } + stream := cipher.NewCFBEncrypter(key, keyIV[keyBytes:]) + stream.XORKeyStream(parameter, parameter) + return nil +} + +// Decrypt decrypts the parameter in place, if this session is used for +// parameter encryption. Otherwise, it does not modify the parameter. +func (s *policySession) Decrypt(parameter []byte) error { + if !s.IsEncryption() { + return nil + } + // Only AES-CFB is supported. + keyBytes := *s.symmetric.KeyBits.AES / 8 + keyIVBytes := int(keyBytes) + 16 + // Part 1, 21.1 + var sessionValue []byte + sessionValue = append(sessionValue, s.sessionKey...) + sessionValue = append(sessionValue, s.auth...) + keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) + key, err := aes.NewCipher(keyIV[:keyBytes]) + if err != nil { + return err + } + stream := cipher.NewCFBDecrypter(key, keyIV[keyBytes:]) + stream.XORKeyStream(parameter, parameter) + return nil +} + +// Handle returns the handle value of the session. +// If the session is created with Policy (instead of PolicySession) this will be +// TPM_RH_NULL. +func (s *policySession) Handle() TPMHandle { + return s.handle +} diff --git a/tpm2/direct/structures.go b/tpm2/direct/structures.go new file mode 100644 index 00000000..0817cdc5 --- /dev/null +++ b/tpm2/direct/structures.go @@ -0,0 +1,1237 @@ +package direct + +import "fmt" + +// TPMCmdHeader is the header structure in front of any TPM command. +// It is described in Part 1, Architecture. +type TPMCmdHeader struct { + Tag TPMISTCommandTag + Length uint32 + CommandCode TPMCC +} + +// TPMRspHeader is the header structure in front of any TPM response. +// It is described in Part 1, Architecture. +type TPMRspHeader struct { + Tag TPMISTCommandTag + Length uint32 + ResponseCode TPMRC +} + +// TPMAlgorithmID represents a TPM_ALGORITHM_ID +// this is the 1.2 compatible form of the TPM_ALG_ID +// See definition in Part 2, Structures, section 5.3. +type TPMAlgorithmID uint32 + +// TPMModifierIndicator represents a TPM_MODIFIER_INDICATOR. +// See definition in Part 2, Structures, section 5.3. +type TPMModifierIndicator uint32 + +// TPMAuthorizationSize represents a TPM_AUTHORIZATION_SIZE. +// the authorizationSize parameter in a command +// See definition in Part 2, Structures, section 5.3. +type TPMAuthorizationSize uint32 + +// TPMParameterSize represents a TPM_PARAMETER_SIZE. +// the parameterSize parameter in a command +// See definition in Part 2, Structures, section 5.3. +type TPMParameterSize uint32 + +// TPMKeySize represents a TPM_KEY_SIZE. +// a key size in octets +// See definition in Part 2, Structures, section 5.3. +type TPMKeySize uint16 + +// TPMKeyBits represents a TPM_KEY_BITS. +// a key size in bits +// See definition in Part 2, Structures, section 5.3. +type TPMKeyBits uint16 + +// TPMGenerated represents a TPM_GENERATED. +// See definition in Part 2: Structures, section 6.2. +type TPMGenerated uint32 + +// Check verifies that a TPMGenerated value is correct, and returns an error +// otherwise. +func (g TPMGenerated) Check() error { + if g != TPMGeneratedValue { + return fmt.Errorf("TPM_GENERATED value should be 0x%x, was 0x%x", TPMGeneratedValue, g) + } + return nil +} + +// TPMAlgID represents a TPM_ALG_ID. +// See definition in Part 2: Structures, section 6.3. +type TPMAlgID uint16 + +// TPMECCCurve represents a TPM_ECC_Curve. +// See definition in Part 2: Structures, section 6.4. +type TPMECCCurve uint16 + +// TPMCC represents a TPM_CC. +// See definition in Part 2: Structures, section 6.5.2. +type TPMCC uint32 + +// TPMRC represents a TPM_RC. +// See definition in Part 2: Structures, section 6.6. +type TPMRC uint32 + +// TPMST represents a TPM_ST. +// See definition in Part 2: Structures, section 6.9. +type TPMST uint16 + +// TPMSE represents a TPM_SE. +// See definition in Part 2: Structures, section 6.11. +type TPMSE uint8 + +// TPMCap represents a TPM_CAP. +// See definition in Part 2: Structures, section 6.12. +type TPMCap uint32 + +// TPMPT represents a TPM_PT. +// See definition in Part 2: Structures, section 6.13. +type TPMPT uint32 + +// TPMPTPCR represents a TPM_PT_PCR. +// See definition in Part 2: Structures, section 6.14. +type TPMPTPCR uint32 + +// TPMHandle represents a TPM_HANDLE. +// See definition in Part 2: Structures, section 7.1. +type TPMHandle uint32 + +// TPMAAlgorithm represents a TPMA_ALGORITHM. +// See definition in Part 2: Structures, section 8.2. +type TPMAAlgorithm struct { + // SET (1): an asymmetric algorithm with public and private portions + // CLEAR (0): not an asymmetric algorithm + Asymmetric bool `gotpm:"bit=0"` + // SET (1): a symmetric block cipher + // CLEAR (0): not a symmetric block cipher + Symmetric bool `gotpm:"bit=1"` + // SET (1): a hash algorithm + // CLEAR (0): not a hash algorithm + Hash bool `gotpm:"bit=2"` + // SET (1): an algorithm that may be used as an object type + // CLEAR (0): an algorithm that is not used as an object type + Object bool `gotpm:"bit=3"` + Reserved1 uint8 `gotpm:"bit=7:4"` + // SET (1): a signing algorithm. The setting of asymmetric, + // symmetric, and hash will indicate the type of signing algorithm. + // CLEAR (0): not a signing algorithm + Signing bool `gotpm:"bit=8"` + // SET (1): an encryption/decryption algorithm. The setting of + // asymmetric, symmetric, and hash will indicate the type of + // encryption/decryption algorithm. + // CLEAR (0): not an encryption/decryption algorithm + Encrypting bool `gotpm:"bit=9"` + // SET (1): a method such as a key derivative function (KDF) + // CLEAR (0): not a method + Method bool `gotpm:"bit=10"` + Reserved2 uint32 `gotpm:"bit=31:11"` +} + +// TPMAObject represents a TPMA_OBJECT. +// See definition in Part 2: Structures, section 8.3.2. +type TPMAObject struct { + // shall be zero + Reserved1 uint8 `gotpm:"bit=0"` + // SET (1): The hierarchy of the object, as indicated by its + // Qualified Name, may not change. + // CLEAR (0): The hierarchy of the object may change as a result + // of this object or an ancestor key being duplicated for use in + // another hierarchy. + FixedTPM bool `gotpm:"bit=1"` + // SET (1): Previously saved contexts of this object may not be + // loaded after Startup(CLEAR). + // CLEAR (0): Saved contexts of this object may be used after a + // Shutdown(STATE) and subsequent Startup(). + STClear bool `gotpm:"bit=2"` + // shall be zero + Reserved2 uint8 `gotpm:"bit=3"` + // SET (1): The parent of the object may not change. + // CLEAR (0): The parent of the object may change as the result of + // a TPM2_Duplicate() of the object. + FixedParent bool `gotpm:"bit=4"` + // SET (1): Indicates that, when the object was created with + // TPM2_Create() or TPM2_CreatePrimary(), the TPM generated all of + // the sensitive data other than the authValue. + // CLEAR (0): A portion of the sensitive data, other than the + // authValue, was provided by the caller. + SensitiveDataOrigin bool `gotpm:"bit=5"` + // SET (1): Approval of USER role actions with this object may be + // with an HMAC session or with a password using the authValue of + // the object or a policy session. + // CLEAR (0): Approval of USER role actions with this object may + // only be done with a policy session. + UserWithAuth bool `gotpm:"bit=6"` + // SET (1): Approval of ADMIN role actions with this object may + // only be done with a policy session. + // CLEAR (0): Approval of ADMIN role actions with this object may + // be with an HMAC session or with a password using the authValue + // of the object or a policy session. + AdminWithPolicy bool `gotpm:"bit=7"` + // shall be zero + Reserved3 uint8 `gotpm:"bit=9:8"` + // SET (1): The object is not subject to dictionary attack + // protections. + // CLEAR (0): The object is subject to dictionary attack + // protections. + NoDA bool `gotpm:"bit=10"` + // SET (1): If the object is duplicated, then symmetricAlg shall + // not be TPM_ALG_NULL and newParentHandle shall not be + // TPM_RH_NULL. + // CLEAR (0): The object may be duplicated without an inner + // wrapper on the private portion of the object and the new parent + // may be TPM_RH_NULL. + EncryptedDuplication bool `gotpm:"bit=11"` + // shall be zero + Reserved4 uint8 `gotpm:"bit=15:12"` + // SET (1): Key usage is restricted to manipulate structures of + // known format; the parent of this key shall have restricted SET. + // CLEAR (0): Key usage is not restricted to use on special + // formats. + Restricted bool `gotpm:"bit=16"` + // SET (1): The private portion of the key may be used to decrypt. + // CLEAR (0): The private portion of the key may not be used to + // decrypt. + Decrypt bool `gotpm:"bit=17"` + // SET (1): For a symmetric cipher object, the private portion of + // the key may be used to encrypt. For other objects, the private + // portion of the key may be used to sign. + // CLEAR (0): The private portion of the key may not be used to + // sign or encrypt. + SignEncrypt bool `gotpm:"bit=18"` + // SET (1): An asymmetric key that may not be used to sign with + // TPM2_Sign() CLEAR (0): A key that may be used with TPM2_Sign() + // if sign is SET + // NOTE: This attribute only has significance if sign is SET. + X509Sign bool `gotpm:"bit=19"` + // shall be zero + Reserved5 uint16 `gotpm:"bit=31:20"` +} + +// TPMASession represents a TPMA_SESSION. +// See definition in Part 2: Structures, section 8.4. +type TPMASession struct { + // SET (1): In a command, this setting indicates that the session + // is to remain active after successful completion of the command. + // In a response, it indicates that the session is still active. + // If SET in the command, this attribute shall be SET in the response. + // CLEAR (0): In a command, this setting indicates that the TPM should + // close the session and flush any related context when the command + // completes successfully. In a response, it indicates that the + // session is closed and the context is no longer active. + // This attribute has no meaning for a password authorization and the + // TPM will allow any setting of the attribute in the command and SET + // the attribute in the response. + ContinueSession bool `gotpm:"bit=0"` + // SET (1): In a command, this setting indicates that the command + // should only be executed if the session is exclusive at the start of + // the command. In a response, it indicates that the session is + // exclusive. This setting is only allowed if the audit attribute is + // SET (TPM_RC_ATTRIBUTES). + // CLEAR (0): In a command, indicates that the session need not be + // exclusive at the start of the command. In a response, indicates that + // the session is not exclusive. + AuditExclusive bool `gotpm:"bit=1"` + // SET (1): In a command, this setting indicates that the audit digest + // of the session should be initialized and the exclusive status of the + // session SET. This setting is only allowed if the audit attribute is + // SET (TPM_RC_ATTRIBUTES). + // CLEAR (0): In a command, indicates that the audit digest should not + // be initialized. This bit is always CLEAR in a response. + AuditReset bool `gotpm:"bit=2"` + // shall be CLEAR + Reserved1 bool `gotpm:"bit=3"` + // shall be CLEAR + Reserved2 bool `gotpm:"bit=4"` + // SET (1): In a command, this setting indicates that the first + // parameter in the command is symmetrically encrypted using the + // parameter encryption scheme described in TPM 2.0 Part 1. The TPM will + // decrypt the parameter after performing any HMAC computations and + // before unmarshaling the parameter. In a response, the attribute is + // copied from the request but has no effect on the response. + // CLEAR (0): Session not used for encryption. + // For a password authorization, this attribute will be CLEAR in both the + // command and response. + Decrypt bool `gotpm:"bit=5"` + // SET (1): In a command, this setting indicates that the TPM should use + // this session to encrypt the first parameter in the response. In a + // response, it indicates that the attribute was set in the command and + // that the TPM used the session to encrypt the first parameter in the + // response using the parameter encryption scheme described in TPM 2.0 + // Part 1. + // CLEAR (0): Session not used for encryption. + // For a password authorization, this attribute will be CLEAR in both the + // command and response. + Encrypt bool `gotpm:"bit=6"` + // SET (1): In a command or response, this setting indicates that the + // session is for audit and that auditExclusive and auditReset have + // meaning. This session may also be used for authorization, encryption, + // or decryption. The encrypted and encrypt fields may be SET or CLEAR. + // CLEAR (0): Session is not used for audit. + // If SET in the command, then this attribute will be SET in the response. + Audit bool `gotpm:"bit=7"` +} + +// TPMALocality represents a TPMA_LOCALITY. +// See definition in Part 2: Structures, section 8.5. +type TPMALocality struct { + TPMLocZero bool `gotpm:"bit=0"` + TPMLocOne bool `gotpm:"bit=1"` + TPMLocTwo bool `gotpm:"bit=2"` + TPMLocThree bool `gotpm:"bit=3"` + TPMLocFour bool `gotpm:"bit=4"` + // If any of these bits is set, an extended locality is indicated + Extended uint8 `gotpm:"bit=7:5"` +} + +// TPMACC represents a TPMA_CC. +// See definition in Part 2: Structures, section 8.9. +type TPMACC struct { + // indicates the command being selected + CommandIndex uint16 `gotpm:"bit=15:0"` + // shall be zero + Reserved1 uint16 `gotpm:"bit=21:16"` + // SET (1): indicates that the command may write to NV + // CLEAR (0): indicates that the command does not write to NV + NV bool `gotpm:"bit=22"` + // SET (1): This command could flush any number of loaded contexts. + // CLEAR (0): no additional changes other than indicated by the flushed attribute + Extensive bool `gotpm:"bit=23"` + // SET (1): The context associated with any transient handle in the command will be flushed when this command completes. + // CLEAR (0): No context is flushed as a side effect of this command. + Flushed bool `gotpm="bit=24"` + // indicates the number of the handles in the handle area for this command + CHandles uint8 `gotpm="bit=27:25"` + // SET (1): indicates the presence of the handle area in the response + RHandle bool `gotpm="bit=28"` + // SET (1): indicates that the command is vendor-specific + // CLEAR (0): indicates that the command is defined in a version of this specification + V bool `gotpm="bit=29"` + // allocated for software; shall be zero + Reserved2 uint8 `gotpm:"bit=31:30"` +} + +// TPMAACT represents a TPMA_ACT. +// See definition in Part 2: Structures, section 8.12. +type TPMAACT struct { + // SET (1): The ACT has signaled + // CLEAR (0): The ACT has not signaled + Signaled bool `gotpm:"bit=0"` + // SET (1): The ACT signaled bit is preserved over a power cycle + // CLEAR (0): The ACT signaled bit is not preserved over a power cycle + PreserveSignaled bool `gotpm:"bit=1"` + // shall be zero + Reserved uint32 `gotpm:"bit=31:2"` +} + +// TPMIYesNo represents a TPMI_YES_NO. +// See definition in Part 2: Structures, section 9.2. +// Use native bool for TPMI_YES_NO; encoding/binary already treats this as 8 bits wide. +type TPMIYesNo = bool + +// TPMIDHObject represents a TPMI_DH_OBJECT. +// See definition in Part 2: Structures, section 9.3. +type TPMIDHObject = TPMHandle + +// TPMIDHEntity represents a TPMI_DH_ENTITY. +// See definition in Part 2: Structures, section 9.6. +type TPMIDHEntity = TPMHandle + +// TPMISHAuthSession represents a TPMI_SH_AUTH_SESSION. +// See definition in Part 2: Structures, section 9.8. +type TPMISHAuthSession = TPMHandle + +// TPMISHHMAC represents a TPMI_SH_HMAC. +// See definition in Part 2: Structures, section 9.9. +type TPMISHHMAC = TPMHandle + +// TPMISHPolicy represents a TPMI_SH_POLICY. +// See definition in Part 2: Structures, section 9.10. +type TPMISHPolicy = TPMHandle + +// TPMIDHContext represents a TPMI_DH_CONTEXT. +// See definition in Part 2: Structures, section 9.11. +type TPMIDHContext = TPMHandle + +// TPMIRHHierarchy represents a TPMI_RH_HIERARCHY. +// See definition in Part 2: Structures, section 9.13. +type TPMIRHHierarchy = TPMHandle + +// TPMIAlgHash represents a TPMI_ALG_HASH. +// See definition in Part 2: Structures, section 9.27. +type TPMIAlgHash = TPMAlgID + +// TODO: Provide a placeholder interface here so we can explicitly enumerate +// these for compile-time protection. + +// TPMIAlgSym represents a TPMI_ALG_SYM. +// See definition in Part 2: Structures, section 9.29. +type TPMIAlgSym = TPMAlgID + +// TPMIAlgSymObject represents a TPMI_ALG_SYM_OBJECT. +// See definition in Part 2: Structures, section 9.30. +type TPMIAlgSymObject = TPMAlgID + +// TPMIAlgSymMode represents a TPMI_ALG_SYM_MODE. +// See definition in Part 2: Structures, section 9.31. +type TPMIAlgSymMode = TPMAlgID + +// TPMIAlgKDF represents a TPMI_ALG_KDF. +// See definition in Part 2: Structures, section 9.32. +type TPMIAlgKDF = TPMAlgID + +// TPMIAlgSigScheme represents a TPMI_ALG_SIG_SCHEME. +// See definition in Part 2: Structures, section 9.33. +type TPMIAlgSigScheme = TPMAlgID + +// TPMISTCommandTag represents a TPMI_ST_COMMAND_TAG. +// See definition in Part 2: Structures, section 9.35. +type TPMISTCommandTag = TPMST + +// TPMSEmpty represents a TPMS_EMPTY. +// See definition in Part 2: Structures, section 10.1. +type TPMSEmpty = struct{} + +// TPMUHA represents a TPMU_HA. +// See definition in Part 2: Structures, section 10.3.1. +type TPMUHA struct { + SHA1 *[20]byte `gotpm:"selector=0x0004"` // TPM_ALG_SHA1 + SHA256 *[32]byte `gotpm:"selector=0x000B"` // TPM_ALG_SHA256 + SHA384 *[48]byte `gotpm:"selector=0x000C"` // TPM_ALG_SHA384 + SHA512 *[64]byte `gotpm:"selector=0x000D"` // TPM_ALG_SHA512 + SHA3_256 *[32]byte `gotpm:"selector=0x0027"` // TPM_ALG_SHA3_256 + SHA3_384 *[48]byte `gotpm:"selector=0x0028"` // TPM_ALG_SHA3_384 + SHA3_512 *[64]byte `gotpm:"selector=0x0029"` // TPM_ALG_SHA3_512 +} + +// TPMTHA represents a TPMT_HA. +// See definition in Part 2: Structures, section 10.3.2. +type TPMTHA struct { + // selector of the hash contained in the digest that implies the size of the digest + HashAlg TPMIAlgHash `gotpm:"nullable"` + // the digest data + Digest TPMUHA `gotpm:"tag=HashAlg"` +} + +// TPM2BDigest represents a TPM2B_DIGEST. +// See definition in Part 2: Structures, section 10.4.2. +type TPM2BDigest TPM2BData + +// TPM2BData represents a TPM2B_DATA. +// See definition in Part 2: Structures, section 10.4.3. +type TPM2BData struct { + // size in octets of the buffer field; may be 0 + Buffer []byte `gotpm:"sized"` +} + +// TPM2BNonce represents a TPM2B_NONCE. +// See definition in Part 2: Structures, section 10.4.4. +type TPM2BNonce TPM2BDigest + +// TPM2BEvent represents a TPM2B_EVENT. +// See definition in Part 2: Structures, section 10.4.7. +type TPM2BEvent TPM2BData + +// TPM2BTimeout represents a TPM2B_TIMEOUT. +// See definition in Part 2: Structures, section 10.4.10. +type TPM2BTimeout TPM2BData + +// TPM2BAuth represents a TPM2B_AUTH. +// See definition in Part 2: Structures, section 10.4.5. +type TPM2BAuth TPM2BDigest + +// TPM2BName represents a TPM2B_NAME. +// See definition in Part 2: Structures, section 10.5.3. +// NOTE: This structure does not contain a TPMUName, because that union +// is not tagged with a selector. Instead, TPM2B_Name is flattened and +// all TPMDirect helpers that deal with names will deal with them as so. +type TPM2BName TPM2BData + +// TPMSPCRSelection represents a TPMS_PCR_SELECTION. +// See definition in Part 2: Structures, section 10.6.2. +type TPMSPCRSelection struct { + Hash TPMIAlgHash + PCRSelect []byte `gotpm:"sized8"` +} + +// TPMTTKCreation represents a TPMT_TK_CREATION. +// See definition in Part 2: Structures, section 10.7.3. +type TPMTTKCreation struct { + // ticket structure tag + Tag TPMST + // the hierarchy containing name + Hierarchy TPMIRHHierarchy + // This shall be the HMAC produced using a proof value of hierarchy. + Digest TPM2BDigest +} + +// TPMTTKAuth represents a TPMT_TK_AUTH. +// See definition in Part 2: Structures, section 10.7.5. +type TPMTTKAuth struct { + // ticket structure tag + Tag TPMST + // the hierarchy of the object used to produce the ticket + Hierarchy TPMIRHHierarchy + // This shall be the HMAC produced using a proof value of hierarchy. + Digest TPM2BDigest +} + +// TPMSAlgProperty represents a TPMS_ALG_PROPERTY. +// See definition in Part 2: Structures, section 10.8.1. +type TPMSAlgProperty struct { + // an algorithm identifier + Alg TPMAlgID + // the attributes of the algorithm + AlgProperties TPMAAlgorithm +} + +// TPMSTaggedProperty represents a TPMS_TAGGED_PROPERTY. +// See definition in Part 2: Structures, section 10.8.2. +type TPMSTaggedProperty struct { + // a property identifier + Property TPMPT + // the value of the property + Value uint32 +} + +// TPMSTaggedPCRSelect represents a TPMS_TAGGED_PCR_SELECT. +// See definition in Part 2: Structures, section 10.8.3. +type TPMSTaggedPCRSelect struct { + // the property identifier + Tag TPMPTPCR + // the bit map of PCR with the identified property + PCRSelect []byte `gotpm:"sized8"` +} + +// TPMSTaggedPolicy represents a TPMS_TAGGED_POLICY. +// See definition in Part 2: Structures, section 10.8.4. +type TPMSTaggedPolicy struct { + // a permanent handle + Handle TPMHandle + // the policy algorithm and hash + PolicyHash TPMTHA +} + +// TPMSACTData represents a TPMS_ACT_DATA. +// See definition in Part 2: Structures, section 10.8.5. +type TPMSACTData struct { + // a permanent handle + Handle TPMHandle + // the current timeout of the ACT + Timeout uint32 + // the state of the ACT + Attributes TPMAACT +} + +// TPMLCC represents a TPML_CC. +// See definition in Part 2: Structures, section 10.9.1. +type TPMLCC struct { + CommandCodes []TPMCC `gotpm:"list"` +} + +// TPMLCCA represents a TPML_CCA. +// See definition in Part 2: Structures, section 10.9.2. +type TPMLCCA struct { + CommandAttributes []TPMACC `gotpm:"list"` +} + +// TPMLAlg represents a TPMLALG. +// See definition in Part 2: Structures, section 10.9.3. +type TPMLAlg struct { + Algorithms []TPMAlgID `gotpm:"list"` +} + +// TPMLHandle represents a TPML_HANDLE. +// See definition in Part 2: Structures, section 10.9.4. +type TPMLHandle struct { + Handle []TPMHandle `gotpm:"list"` +} + +// TPMLDigest represents a TPML_DIGEST. +// See definition in Part 2: Structures, section 10.9.5. +type TPMLDigest struct { + // a list of digests + Digests []TPM2BDigest `gotpm:"list"` +} + +// TPMLDigestValues represents a TPML_DIGEST_VALUES. +// See definition in Part 2: Structures, section 10.9.6. +type TPMLDigestValues struct { + // a list of tagged digests + Digests []TPMTHA `gotpm:"list"` +} + +// TPMLPCRSelection represents a TPML_PCRzSELECTION. +// See definition in Part 2: Structures, section 10.9.7. +type TPMLPCRSelection struct { + PCRSelections []TPMSPCRSelection `gotpm:"list"` +} + +// TPMLAlgProperty represents a TPML_ALGzPROPERTY. +// See definition in Part 2: Structures, section 10.9.8. +type TPMLAlgProperty struct { + AlgProperties []TPMSAlgProperty `gotpm:"list"` +} + +// TPMLTaggedTPMProperty represents a TPML_TAGGED_TPM_PROPERTY. +// See definition in Part 2: Structures, section 10.9.9. +type TPMLTaggedTPMProperty struct { + TPMProperty []TPMSTaggedProperty `gotpm:"list"` +} + +// TPMLTaggedPCRProperty represents a TPML_TAGGED_PCR_PROPERTY. +// See definition in Part 2: Structures, section 10.9.10. +type TPMLTaggedPCRProperty struct { + PCRProperty []TPMSTaggedPCRSelect `gotpm:"list"` +} + +// TPMLECCCurve represents a TPML_ECC_CURVE. +// See definition in Part 2: Structures, section 10.9.11. +type TPMLECCCurve struct { + ECCCurves []TPMECCCurve `gotpm:"list"` +} + +// TPMLTaggedPolicy represents a TPML_TAGGED_POLICY. +// See definition in Part 2: Structures, section 10.9.12. +type TPMLTaggedPolicy struct { + Policies []TPMSTaggedPolicy `gotpm:"list"` +} + +// TPMLACTData represents a TPML_ACT_DATA. +// See definition in Part 2: Structures, section 10.9.13. +type TPMLACTData struct { + ACTData []TPMSACTData `gotpm:"list"` +} + +// TPMUCapabilities represents a TPMU_CAPABILITIES. +// See definition in Part 2: Structures, section 10.10.1. +type TPMUCapabilities struct { + Algorithms *TPMLAlgProperty `gotpm:"selector=0x00000000"` // TPM_CAP_ALGS + Handles *TPMLHandle `gotpm:"selector=0x00000001"` // TPM_CAP_HANDLES + Command *TPMLCCA `gotpm:"selector=0x00000002"` // TPM_CAP_COMMANDS + PPCommands *TPMLCC `gotpm:"selector=0x00000003"` // TPM_CAP_PP_COMMANDS + AuditCommands *TPMLCC `gotpm:"selector=0x00000004"` // TPM_CAP_AUDIT_COMMANDS + AssignedPCR *TPMLPCRSelection `gotpm:"selector=0x00000005"` // TPM_CAP_PCRS + TPMProperties *TPMLTaggedTPMProperty `gotpm:"selector=0x00000006"` // TPM_CAP_TPM_PROPERTIES + PCRProperties *TPMLTaggedPCRProperty `gotpm:"selector=0x00000007"` // TPM_CAP_PCR_PROPERTIES + ECCCurves *TPMLECCCurve `gotpm:"selector=0x00000008"` // TPM_CAP_ECC_CURVES + AuthPolicies *TPMLTaggedPolicy `gotpm:"selector=0x00000009"` // TPM_CAP_AUTH_POLICIES + ACTData *TPMLACTData `gotpm:"selector=0x0000000A"` // TPM_CAP_ACT +} + +// TPMSCapabilityData represents a TPMS_CAPABILITY_DATA. +// See definition in Part 2: Structures, section 10.10.2. +type TPMSCapabilityData struct { + // the capability + Capability TPMCap + // the capability data + Data TPMUCapabilities `gotpm:"tag=Capability"` +} + +// TPMSClockInfo represents a TPMS_CLOCK_INFO. +// See definition in Part 2: Structures, section 10.11.1. +type TPMSClockInfo struct { + // time value in milliseconds that advances while the TPM is powered + Clock uint64 + // number of occurrences of TPM Reset since the last TPM2_Clear() + ResetCount uint32 + // number of times that TPM2_Shutdown() or _TPM_Hash_Start have + // occurred since the last TPM Reset or TPM2_Clear(). + RestartCount uint32 + // no value of Clock greater than the current value of Clock has been + // previously reported by the TPM. Set to YES on TPM2_Clear(). + Safe TPMIYesNo +} + +// TPMSTimeInfo represents a TPMS_TIMEzINFO. +// See definition in Part 2: Structures, section 10.11.6. +type TPMSTimeInfo struct { + // time in milliseconds since the TIme circuit was last reset + Time uint64 + // a structure containing the clock information + ClockInfo TPMSClockInfo +} + +// TPMSTimeAttestInfo represents a TPMS_TIME_ATTEST_INFO. +// See definition in Part 2: Structures, section 10.12.2. +type TPMSTimeAttestInfo struct { + // the Time, Clock, resetCount, restartCount, and Safe indicator + Time TPMSTimeInfo + // a TPM vendor-specific value indicating the version number of the firmware + FirmwareVersion uint64 +} + +// TPMSCertifyInfo represents a TPMS_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.3. +type TPMSCertifyInfo struct { + // Name of the certified object + Name TPM2BName + // Qualified Name of the certified object + QualifiedName TPM2BName +} + +// TPMSQuoteInfo represents a TPMS_QUOTE_INFO. +// See definition in Part 2: Structures, section 10.12.4. +type TPMSQuoteInfo struct { + // information on algID, PCR selected and digest + PCRSelect TPMLPCRSelection + // digest of the selected PCR using the hash of the signing key + PCRDigest TPM2BDigest +} + +// TPMSCommandAuditInfo represents a TPMS_COMMAND_AUDIT_INFO. +// See definition in Part 2: Structures, section 10.12.5. +type TPMSCommandAuditInfo struct { + // the monotonic audit counter + AuditCounter uint64 + // hash algorithm used for the command audit + DigestAlg TPMAlgID + // the current value of the audit digest + AuditDigest TPM2BDigest + // digest of the command codes being audited using digestAlg + CommandDigest TPM2BDigest +} + +// TPMSSessionAuditInfo represents a TPMS_SESSION_AUDIT_INFO. +// See definition in Part 2: Structures, section 10.12.6. +type TPMSSessionAuditInfo struct { + // current exclusive status of the session + ExclusiveSession TPMIYesNo + // the current value of the session audit digest + SessionDigest TPM2BDigest +} + +// TPMSCreationInfo represents a TPMS_CREATION_INFO. +// See definition in Part 2: Structures, section 10.12.7. +type TPMSCreationInfo struct { + // Name of the object + ObjectName TPM2BName + // creationHash + CreationHash TPM2BDigest +} + +// TPMSNVCertifyInfo represents a TPMS_NV_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.8. +type TPMSNVCertifyInfo struct { + // Name of the NV Index + IndexName TPM2BName + // the offset parameter of TPM2_NV_Certify() + Offset uint16 + // contents of the NV Index + NVContents TPM2BData +} + +// TPMSNVDigestCertifyInfo represents a TPMS_NV_DIGEST_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.9. +type TPMSNVDigestCertifyInfo struct { + // Name of the NV Index + IndexName TPM2BName + // hash of the contents of the index + NVDigest TPM2BDigest +} + +// TPMISTAttest represents a TPMI_ST_ATTEST. +// See definition in Part 2: Structures, section 10.12.10. +type TPMISTAttest = TPMST + +// TPMUAttest represents a TPMU_ATTEST. +// See definition in Part 2: Structures, section 10.12.11. +type TPMUAttest struct { + NV *TPMSNVCertifyInfo `gotpm:"selector=0x8014"` // TPM_ST_ATTEST_NV + CommandAudit *TPMSCommandAuditInfo `gotpm:"selector=0x8015"` // TPM_ST_ATTEST_COMMAND_AUDIT + SessionAudit *TPMSSessionAuditInfo `gotpm:"selector=0x8016"` // TPM_ST_ATTEST_SESSION_AUDIT + Certify *TPMSCertifyInfo `gotpm:"selector=0x8017"` // TPM_ST_ATTEST_CERTIFY + Quote *TPMSQuoteInfo `gotpm:"selector=0x8018"` // TPM_ST_ATTEST_QUOTE + Time *TPMSTimeAttestInfo `gotpm:"selector=0x8019"` // TPM_ST_ATTEST_TIME + Creation *TPMSCreationInfo `gotpm:"selector=0x801A"` // TPM_ST_ATTEST_CREATION + NVDigest *TPMSNVDigestCertifyInfo `gotpm:"selector=0x801C"` // TPM_ST_ATTEST_NV_DIGEST +} + +// TPMSAttest represents a TPMS_ATTEST. +// See definition in Part 2: Structures, section 10.12.12. +type TPMSAttest struct { + // the indication that this structure was created by a TPM (always TPM_GENERATED_VALUE) + Magic TPMGenerated `gotpm:"check"` + // type of the attestation structure + Type TPMISTAttest + // Qualified Name of the signing key + QualifiedSigner TPM2BName + // external information supplied by caller + ExtraData TPM2BData + // Clock, resetCount, restartCount, and Safe + ClockInfo TPMSClockInfo + // TPM-vendor-specific value identifying the version number of the firmware + FirmwareVersion uint64 + // the type-specific attestation information + Attested TPMUAttest `gotpm:"tag=Type"` +} + +// TPM2BAttest represents a TPM2B_ATTEST. +// See definition in Part 2: Structures, section 10.12.13. +// Note that in the spec, this is just a 2B_DATA with enough room for an S_ATTEST. +// For ergonomics, pretend that TPM2B_Attest wraps a TPMS_Attest just like other 2Bs. +type TPM2BAttest struct { + // the signed structure + AttestationData TPMSAttest `gotpm:"sized"` +} + +// TPMSAuthCommand represents a TPMS_AUTH_COMMAND. +// See definition in Part 2: Structures, section 10.13.2. +type TPMSAuthCommand struct { + Handle TPMISHAuthSession + Nonce TPM2BNonce + Attributes TPMASession + Authorization TPM2BData +} + +// TPMSAuthResponse represents a TPMS_AUTH_RESPONSE. +// See definition in Part 2: Structures, section 10.13.3. +type TPMSAuthResponse struct { + Nonce TPM2BNonce + Attributes TPMASession + Authorization TPM2BData +} + +// TPMUSymKeyBits represents a TPMU_SYM_KEY_BITS. +// See definition in Part 2: Structures, section 11.1.3. +type TPMUSymKeyBits struct { + // TODO: The rest of the symmetric algorithms get their own entry + // in this union. + AES *TPMKeyBits `gotpm:"selector=0x0006"` // TPM_ALG_AES + XOR *TPMIAlgHash `gotpm:"selector=0x000A"` // TPM_ALG_XOR +} + +// TPMUSymMode represents a TPMU_SYM_MODE. +// See definition in Part 2: Structures, section 11.1.4. +type TPMUSymMode struct { + // TODO: The rest of the symmetric algorithms get their own entry + // in this union. + AES *TPMIAlgSymMode `gotpm:"selector=0x0006"` // TPM_ALG_AES + XOR *struct{} `gotpm:"selector=0x000A"` // TPM_ALG_XOR +} + +// TPMUSymDetails represents a TPMU_SYM_DETAILS. +// See definition in Part 2: Structures, section 11.1.5. +type TPMUSymDetails struct { + // TODO: The rest of the symmetric algorithms get their own entry + // in this union. + AES *struct{} `gotpm:"selector=0x0006"` // TPM_ALG_AES + XOR *struct{} `gotpm:"selector=0x000A"` // TPM_ALG_XOR +} + +// TPMTSymDef represents a TPMT_SYM_DEF. +// See definition in Part 2: Structures, section 11.1.6. +type TPMTSymDef struct { + // indicates a symmetric algorithm + Algorithm TPMIAlgSym `gotpm:"nullable"` + // the key size + KeyBits TPMUSymKeyBits `gotpm:"tag=Algorithm"` + // the mode for the key + Mode TPMUSymMode `gotpm:"tag=Algorithm"` + // contains the additional algorithm details + Details TPMUSymDetails `gotpm:"tag=Algorithm"` +} + +// TPMTSymDefObject represents a TPMT_SYM_DEF_OBJECT. +// See definition in Part 2: Structures, section 11.1.7. +type TPMTSymDefObject struct { + // selects a symmetric block cipher + // When used in the parameter area of a parent object, this shall + // be a supported block cipher and not TPM_ALG_NULL + Algorithm TPMIAlgSymObject `gotpm:"nullable"` + // the key size + KeyBits TPMUSymKeyBits `gotpm:"tag=Algorithm"` + // default mode + // When used in the parameter area of a parent object, this shall + // be TPM_ALG_CFB. + Mode TPMUSymMode `gotpm:"tag=Algorithm"` + // contains the additional algorithm details, if any + Details TPMUSymDetails `gotpm:"tag=Algorithm"` +} + +// TPMSSymCipherParms represents a TPMS_SYMCIPHER_PARMS. +// See definition in Part 2: Structures, section 11.1.9. +type TPMSSymCipherParms struct { + // a symmetric block cipher + Sym TPMTSymDefObject +} + +// TPM2BSensitiveData represents a TPM2B_SENSITIVE_DATA. +// See definition in Part 2: Structures, section 11.1.14. +type TPM2BSensitiveData TPM2BData + +// TPMSSensitiveCreate represents a TPMS_SENSITIVE_CREATE. +// See definition in Part 2: Structures, section 11.1.15. +type TPMSSensitiveCreate struct { + // the USER auth secret value. + UserAuth TPM2BAuth + // data to be sealed, a key, or derivation values. + Data TPM2BData +} + +// TPM2BSensitiveCreate represents a TPM2B_SENSITIVE_CREATE. +// See definition in Part 2: Structures, section 11.1.16. +type TPM2BSensitiveCreate struct { + // data to be sealed or a symmetric key value. + Sensitive TPMSSensitiveCreate `gotpm:"sized"` +} + +// TPMSSchemeHash represents a TPMS_SCHEME_HASH. +// See definition in Part 2: Structures, section 11.1.17. +type TPMSSchemeHash struct { + // the hash algorithm used to digest the message + HashAlg TPMIAlgHash +} + +// TPMIAlgKeyedHashScheme represents a TPMI_ALG_KEYEDHASH_SCHEME. +// See definition in Part 2: Structures, section 11.1.10. +type TPMIAlgKeyedHashScheme = TPMAlgID + +// TPMSSchemeHMAC represents a TPMS_SCHEME_HMAC. +// See definition in Part 2: Structures, section 11.1.20. +type TPMSSchemeHMAC TPMSSchemeHash + +// TPMSSchemeXOR represents a TPMS_SCHEME_XOR. +// See definition in Part 2: Structures, section 11.1.21. +type TPMSSchemeXOR struct { + // the hash algorithm used to digest the message + HashAlg TPMIAlgHash + // the key derivation function + KDF TPMIAlgKDF +} + +// TPMUSchemeKeyedHash represents a TPMU_SCHEME_KEYEDHASH. +// See definition in Part 2: Structures, section 11.1.22. +type TPMUSchemeKeyedHash struct { + HMAC *TPMSSchemeHMAC `gotpm:"selector=0x0005"` // TPM_ALG_HMAC + XOR *TPMSSchemeXOR `gotpm:"selector=0x000A"` // TPM_ALG_XOR +} + +// TPMTKeyedHashScheme represents a TPMT_KEYEDHASH_SCHEME. +// See definition in Part 2: Structures, section 11.1.23. +type TPMTKeyedHashScheme struct { + Scheme TPMIAlgKeyedHashScheme `gotpm:"nullable"` + Details TPMUSchemeKeyedHash `gotpm:"tag=Scheme"` +} + +// TPMSSigSchemeRSASSA represents a TPMS_SIG_SCHEME_RSASSA. +// See definition in Part 2: Structures, section 11.2.1.2. +type TPMSSigSchemeRSASSA TPMSSchemeHash + +// TPMSSigSchemeRSAPSS represents a TPMS_SIG_SCHEME_RSAPSS. +// See definition in Part 2: Structures, section 11.2.1.2. +type TPMSSigSchemeRSAPSS TPMSSchemeHash + +// TPMSSigSchemeECDSA represents a TPMS_SIG_SCHEME_ECDSA. +// See definition in Part 2: Structures, section 11.2.1.3. +type TPMSSigSchemeECDSA TPMSSchemeHash + +// TPMUSigScheme represents a TPMU_SIG_SCHEME. +// See definition in Part 2: Structures, section 11.2.1.4. +type TPMUSigScheme struct { + HMAC *TPMSSchemeHMAC `gotpm:"selector=0x0005"` // TPM_ALG_HMAC + RSASSA *TPMSSchemeHash `gotpm:"selector=0x0014"` // TPM_ALG_RSASSA + RSAPSS *TPMSSchemeHash `gotpm:"selector=0x0016"` // TPM_ALG_RSAPSS + ECDSA *TPMSSchemeHash `gotpm:"selector=0x0018"` // TPM_ALG_ECDSA +} + +// TPMTSigScheme represents a TPMT_SIG_SCHEME. +// See definition in Part 2: Structures, section 11.2.1.5. +type TPMTSigScheme struct { + Scheme TPMIAlgSigScheme `gotpm:"nullable"` + Details TPMUSigScheme `gotpm:"tag=Scheme"` +} + +// TPMSEncSchemeRSAES represents a TPMS_ENC_SCHEME_RSAES. +// See definition in Part 2: Structures, section 11.2.2.2. +type TPMSEncSchemeRSAES TPMSEmpty + +// TPMSEncSchemeOAEP represents a TPMS_ENC_SCHEME_OAEP. +// See definition in Part 2: Structures, section 11.2.2.2. +type TPMSEncSchemeOAEP TPMSSchemeHash + +// TPMSKeySchemeECDH represents a TPMS_KEY_SCHEME_ECDH. +// See definition in Part 2: Structures, section 11.2.2.3. +type TPMSKeySchemeECDH TPMSSchemeHash + +// TPMSKDFSchemeMGF1 represents a TPMS_KDF_SCHEME_MGF1. +// See definition in Part 2: Structures, section 11.2.3.1. +type TPMSKDFSchemeMGF1 TPMSSchemeHash + +// TPMSKDFSchemeECDH represents a TPMS_KDF_SCHEME_ECDH. +// See definition in Part 2: Structures, section 11.2.3.1. +type TPMSKDFSchemeECDH TPMSSchemeHash + +// TPMSKDFSchemeKDF1SP80056A represents a TPMS_KDF_SCHEME_KDF1SP80056A. +// See definition in Part 2: Structures, section 11.2.3.1. +type TPMSKDFSchemeKDF1SP80056A TPMSSchemeHash + +// TPMSKDFSchemeKDF2 represents a TPMS_KDF_SCHEME_KDF2. +// See definition in Part 2: Structures, section 11.2.3.1. +type TPMSKDFSchemeKDF2 TPMSSchemeHash + +// TPMSKDFSchemeKDF1SP800108 represents a TPMS_KDF_SCHEME_KDF1SP800108. +// See definition in Part 2: Structures, section 11.2.3.1. +type TPMSKDFSchemeKDF1SP800108 TPMSSchemeHash + +// TPMUKDFScheme represents a TPMU_KDF_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.2. +type TPMUKDFScheme struct { + MGF1 *TPMSKDFSchemeMGF1 `gotpm:"selector=0x0007"` // TPM_ALG_MGF1 + ECDH *TPMSKDFSchemeECDH `gotpm:"selector=0x0019"` // TPM_ALG_ECDH + KDF1SP80056A *TPMSKDFSchemeKDF1SP80056A `gotpm:"selector=0x0020"` // TPM_ALG_KDF1_SP800_56A + KDF2 *TPMSKDFSchemeKDF2 `gotpm:"selector=0x0021"` // TPM_ALG_KDF2 + KDF1SP800108 *TPMSKDFSchemeKDF1SP800108 `gotpm:"selector=0x0022"` // TPM_ALG_KDF1_SP800_108 +} + +// TPMTKDFScheme represents a TPMT_KDF_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.3. +type TPMTKDFScheme struct { + // scheme selector + Scheme TPMIAlgKDF `gotpm:"nullable"` + // scheme parameters + Details TPMUKDFScheme `gotpm:"tag=Scheme"` +} + +// TPMUAsymScheme represents a TPMU_ASYM_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.5. +type TPMUAsymScheme struct { + // TODO every asym scheme gets an entry in this union. + RSASSA *TPMSSigSchemeRSASSA `gotpm:"selector=0x0014"` // TPM_ALG_RSASSA + RSAES *TPMSEncSchemeRSAES `gotpm:"selector=0x0015"` // TPM_ALG_RSAES + RSAPSS *TPMSSigSchemeRSAPSS `gotpm:"selector=0x0016"` // TPM_ALG_RSAPSS + OAEP *TPMSEncSchemeOAEP `gotpm:"selector=0x0017"` // TPM_ALG_OAEP + ECDSA *TPMSSigSchemeECDSA `gotpm:"selector=0x0018"` // TPM_ALG_ECDSA + ECDH *TPMSKeySchemeECDH `gotpm:"selector=0x0019"` // TPM_ALG_ECDH +} + +// TPMIAlgRSAScheme represents a TPMI_ALG_RSA_SCHEME. +// See definition in Part 2: Structures, section 11.2.4.1. +type TPMIAlgRSAScheme = TPMAlgID + +// TPMTRSAScheme represents a TPMT_RSA_SCHEME. +// See definition in Part 2: Structures, section 11.2.4.2. +type TPMTRSAScheme struct { + // scheme selector + Scheme TPMIAlgRSAScheme `gotpm:"nullable"` + // scheme parameters + Details TPMUAsymScheme `gotpm:"tag=Scheme"` +} + +// TPM2BPublicKeyRSA represents a TPM2B_PUBLIC_KEY_RSA. +// See definition in Part 2: Structures, section 11.2.4.5. +type TPM2BPublicKeyRSA TPM2BData + +// TPMIRSAKeyBits represents a TPMI_RSA_KEY_BITS. +// See definition in Part 2: Structures, section 11.2.4.6. +type TPMIRSAKeyBits = TPMKeyBits + +// TPM2BECCParameter represents a TPM2B_ECC_PARAMETER. +// See definition in Part 2: Structures, section 11.2.5.1. +type TPM2BECCParameter TPM2BData + +// TPMSECCPoint represents a TPMS_ECC_POINT. +// See definition in Part 2: Structures, section 11.2.5.2. +type TPMSECCPoint struct { + // X coordinate + X TPM2BECCParameter + // Y coordinate + Y TPM2BECCParameter +} + +// TPMIAlgECCScheme represents a TPMI_ALG_ECC_SCHEME. +// See definition in Part 2: Structures, section 11.2.5.4. +type TPMIAlgECCScheme = TPMAlgID + +// TPMIECCCurve represents a TPMI_ECC_CURVE. +// See definition in Part 2: Structures, section 11.2.5.5. +type TPMIECCCurve = TPMECCCurve + +// TPMTECCScheme represents a TPMT_ECC_SCHEME. +// See definition in Part 2: Structures, section 11.2.5.6. +type TPMTECCScheme struct { + // scheme selector + Scheme TPMIAlgECCScheme `gotpm:"nullable"` + // scheme parameters + Details TPMUAsymScheme `gotpm:"tag=Scheme"` +} + +// TPMSSignatureRSA represents a TPMS_SIGNATURE_RSA. +// See definition in Part 2: Structures, section 11.3.1. +type TPMSSignatureRSA struct { + // the hash algorithm used to digest the message + Hash TPMIAlgHash + // The signature is the size of a public key. + Sig TPM2BPublicKeyRSA +} + +// TPMSSignatureECC represents a TPMS_SIGNATURE_ECC. +// See definition in Part 2: Structures, section 11.3.2. +type TPMSSignatureECC struct { + // the hash algorithm used in the signature process + Hash TPMIAlgHash + SignatureR TPM2BECCParameter + SignatureS TPM2BECCParameter +} + +// TPMUSignature represents a TPMU_SIGNATURE. +// See definition in Part 2: Structures, section 11.3.3. +type TPMUSignature struct { + HMAC *TPMTHA `gotpm:"selector=0x0005"` // TPM_ALG_HMAC + RSASSA *TPMSSignatureRSA `gotpm:"selector=0x0014"` // TPM_ALG_RSASSA + RSAPSS *TPMSSignatureRSA `gotpm:"selector=0x0016"` // TPM_ALG_RSAPSS + ECDSA *TPMSSignatureECC `gotpm:"selector=0x0018"` // TPM_ALG_ECDSA +} + +// TPMTSignature represents a TPMT_SIGNATURE. +// See definition in Part 2: Structures, section 11.3.4. +type TPMTSignature struct { + // selector of the algorithm used to construct the signature + SigAlg TPMIAlgSigScheme `gotpm:"nullable"` + // This shall be the actual signature information. + Signature TPMUSignature `gotpm:"tag=SigAlg"` +} + +// TPM2BEncryptedSecret represents a TPM2B_ENCRYPTED_SECRET. +// See definition in Part 2: Structures, section 11.4.33. +type TPM2BEncryptedSecret TPM2BData + +// TPMIAlgPublic represents a TPMI_ALG_PUBLIC. +// See definition in Part 2: Structures, section 12.2.2. +type TPMIAlgPublic = TPMAlgID + +// TPMUPublicID represents a TPMU_PUBLIC_ID. +// See definition in Part 2: Structures, section 12.2.3.2. +type TPMUPublicID struct { + KeyedHash *TPM2BDigest `gotpm:"selector=0x0008"` // TPM_ALG_KEYEDHASH + Sym *TPM2BDigest `gotpm:"selector=0x0025"` // TPM_ALG_SYMCIPHER + RSA *TPM2BPublicKeyRSA `gotpm:"selector=0x0001"` // TPM_ALG_RSA + ECC *TPMSECCPoint `gotpm:"selector=0x0023"` // TPM_ALG_ECC +} + +// TPMSKeyedHashParms represents a TPMS_KEYED_HASH_PARMS. +// See definition in Part 2: Structures, section 12.2.3.3. +type TPMSKeyedHashParms struct { + // Indicates the signing method used for a keyedHash signing + // object. This field also determines the size of the data field + // for a data object created with TPM2_Create() or + // TPM2_CreatePrimary(). + Scheme TPMTKeyedHashScheme +} + +// TPMSRSAParms represents a TPMS_RSA_PARMS. +// See definition in Part 2: Structures, section 12.2.3.5. +type TPMSRSAParms struct { + // for a restricted decryption key, shall be set to a supported + // symmetric algorithm, key size, and mode. + // if the key is not a restricted decryption key, this field shall + // be set to TPM_ALG_NULL. + Symmetric TPMTSymDefObject + // scheme.scheme shall be: + // for an unrestricted signing key, either TPM_ALG_RSAPSS + // TPM_ALG_RSASSA or TPM_ALG_NULL + // for a restricted signing key, either TPM_ALG_RSAPSS or + // TPM_ALG_RSASSA + // for an unrestricted decryption key, TPM_ALG_RSAES, TPM_ALG_OAEP, + // or TPM_ALG_NULL unless the object also has the sign attribute + // for a restricted decryption key, TPM_ALG_NULL + Scheme TPMTRSAScheme + // number of bits in the public modulus + KeyBits TPMIRSAKeyBits + // the public exponent + // A prime number greater than 2. + Exponent uint32 +} + +// TPMSECCParms represents a TPMS_ECC_PARMS. +// See definition in Part 2: Structures, section 12.2.3.6. +type TPMSECCParms struct { + // for a restricted decryption key, shall be set to a supported + // symmetric algorithm, key size. and mode. + // if the key is not a restricted decryption key, this field shall + // be set to TPM_ALG_NULL. + Symmetric TPMTSymDefObject + // If the sign attribute of the key is SET, then this shall be a + // valid signing scheme. + Scheme TPMTECCScheme + // ECC curve ID + CurveID TPMIECCCurve + // an optional key derivation scheme for generating a symmetric key + // from a Z value + // If the kdf parameter associated with curveID is not TPM_ALG_NULL + // then this is required to be NULL. + KDF TPMTKDFScheme +} + +// TPMUPublicParms represents a TPMU_PUBLIC_PARMS. +// See definition in Part 2: Structures, section 12.2.3.7. +type TPMUPublicParms struct { + // sign | decrypt | neither + KeyedHashDetail *TPMSKeyedHashParms `gotpm:"selector=0x0008"` // TPM_ALG_KEYEDHASH + // sign | decrypt | neither + SymCipherDetail *TPMSSymCipherParms `gotpm:"selector=0x0025"` // TPM_ALG_SYMCIPHER + // decrypt + sign + RSADetail *TPMSRSAParms `gotpm:"selector=0x0001"` // TPM_ALG_RSA + // decrypt + sign + ECCDetail *TPMSECCParms `gotpm:"selector=0x0023"` // TPM_ALG_ECC +} + +// TPMTPublic represents a TPMT_PUBLIC. +// See definition in Part 2: Structures, section 12.2.4. +type TPMTPublic struct { + // “algorithm” associated with this object + Type TPMIAlgPublic + // algorithm used for computing the Name of the object + NameAlg TPMIAlgHash + // attributes that, along with type, determine the manipulations + // of this object + ObjectAttributes TPMAObject + // optional policy for using this key + // The policy is computed using the nameAlg of the object. + AuthPolicy TPM2BDigest + // the algorithm or structure details + Parameters TPMUPublicParms `gotpm:"tag=Type"` + // the unique identifier of the structure + // For an asymmetric key, this would be the public key. + Unique TPMUPublicID `gotpm:"tag=Type"` +} + +// TPM2BPublic represents a TPM2B_PUBLIC. +// See definition in Part 2: Structures, section 12.2.5. +type TPM2BPublic struct { + // the public area + PublicArea TPMTPublic `gotpm:"sized"` +} + +// TPM2BPrivate represents a TPM2B_PRIVATE. +// See definition in Part 2: Structures, section 12.3.7. +type TPM2BPrivate TPM2BData + +// TPMSCreationData represents a TPMS_CREATION_DATA. +// See definition in Part 2: Structures, section 15.1. +type TPMSCreationData struct { + // list indicating the PCR included in pcrDigest + PCRSelect TPMLPCRSelection + // digest of the selected PCR using nameAlg of the object for which + // this structure is being created + PCRDigest TPM2BDigest + // the locality at which the object was created + Locality TPMALocality + // nameAlg of the parent + ParentNameAlg TPMAlgID + // Name of the parent at time of creation + ParentName TPM2BName + // Qualified Name of the parent at the time of creation + ParentQualifiedName TPM2BName + // association with additional information added by the key + OutsideInfo TPM2BData +} + +// TPM2BCreationData represents a TPM2B_CREATION_DATA. +// See definition in Part 2: Structures, section 15.2. +type TPM2BCreationData struct { + CreationData TPMSCreationData `gotpm:"sized"` +} diff --git a/tpm2/direct/templates.go b/tpm2/direct/templates.go new file mode 100644 index 00000000..53f82ae5 --- /dev/null +++ b/tpm2/direct/templates.go @@ -0,0 +1,190 @@ +package direct + +var ( + // RSASRKTemplate contains the TCG reference RSA-2048 SRK template. + // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf + RSASRKTemplate = TPM2BPublic{ + PublicArea: TPMTPublic{ + Type: TPMAlgRSA, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + AdminWithPolicy: false, + NoDA: true, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + Parameters: TPMUPublicParms{ + RSADetail: &TPMSRSAParms{ + Symmetric: TPMTSymDefObject{ + Algorithm: TPMAlgAES, + KeyBits: TPMUSymKeyBits{ + AES: NewTPMKeyBits(128), + }, + Mode: TPMUSymMode{ + AES: NewTPMAlgID(TPMAlgCFB), + }, + }, + KeyBits: 2048, + }, + }, + Unique: TPMUPublicID{ + RSA: &TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + }, + }, + } + // RSAEKTemplate contains the TCG reference RSA-2048 EK template. + RSAEKTemplate = TPM2BPublic{ + PublicArea: TPMTPublic{ + Type: TPMAlgRSA, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: false, + AdminWithPolicy: true, + NoDA: false, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + AuthPolicy: TPM2BDigest{ + Buffer: []byte{ + // TPM2_PolicySecret(RH_ENDORSEMENT) + 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, + 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, + 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, + 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, + }, + }, + Parameters: TPMUPublicParms{ + RSADetail: &TPMSRSAParms{ + Symmetric: TPMTSymDefObject{ + Algorithm: TPMAlgAES, + KeyBits: TPMUSymKeyBits{ + AES: NewTPMKeyBits(128), + }, + Mode: TPMUSymMode{ + AES: NewTPMAlgID(TPMAlgCFB), + }, + }, + KeyBits: 2048, + }, + }, + Unique: TPMUPublicID{ + RSA: &TPM2BPublicKeyRSA{ + Buffer: make([]byte, 256), + }, + }, + }, + } + // ECCSRKTemplate contains the TCG reference ECC-P256 SRK template. + // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf + ECCSRKTemplate = 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: true, + SignEncrypt: false, + }, + Parameters: TPMUPublicParms{ + ECCDetail: &TPMSECCParms{ + Symmetric: TPMTSymDefObject{ + Algorithm: TPMAlgAES, + KeyBits: TPMUSymKeyBits{ + AES: NewTPMKeyBits(128), + }, + Mode: TPMUSymMode{ + AES: NewTPMAlgID(TPMAlgCFB), + }, + }, + CurveID: TPMECCNistP256, + }, + }, + Unique: TPMUPublicID{ + ECC: &TPMSECCPoint{ + X: TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + Y: TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + }, + }, + }, + } + // ECCEKTemplate contains the TCG reference ECC-P256 EK template. + ECCEKTemplate = TPM2BPublic{ + PublicArea: TPMTPublic{ + Type: TPMAlgECC, + NameAlg: TPMAlgSHA256, + ObjectAttributes: TPMAObject{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: false, + AdminWithPolicy: true, + NoDA: false, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + AuthPolicy: TPM2BDigest{ + Buffer: []byte{ + // TPM2_PolicySecret(RH_ENDORSEMENT) + 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, + 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, + 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, + 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, + }, + }, + Parameters: TPMUPublicParms{ + ECCDetail: &TPMSECCParms{ + Symmetric: TPMTSymDefObject{ + Algorithm: TPMAlgAES, + KeyBits: TPMUSymKeyBits{ + AES: NewTPMKeyBits(128), + }, + Mode: TPMUSymMode{ + AES: NewTPMAlgID(TPMAlgCFB), + }, + }, + CurveID: TPMECCNistP256, + }, + }, + Unique: TPMUPublicID{ + ECC: &TPMSECCPoint{ + X: TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + Y: TPM2BECCParameter{ + Buffer: make([]byte, 32), + }, + }, + }, + }, + } +) diff --git a/tpm2/direct/wrappers.go b/tpm2/direct/wrappers.go new file mode 100644 index 00000000..5a304032 --- /dev/null +++ b/tpm2/direct/wrappers.go @@ -0,0 +1,10 @@ +package direct + +// This file provides wrapper functions for concrete types used by tpm2, for +// setting union member pointers. + +// NewTPMKeyBits allocates and returns the address of a new TPMKeyBits. +func NewTPMKeyBits(v TPMKeyBits) *TPMKeyBits { return &v } + +// NewTPMAlgID allocates and returns the address of a new TPMAlgID. +func NewTPMAlgID(v TPMAlgID) *TPMAlgID { return &v } diff --git a/tpm2/kdf.go b/tpm2/kdf.go index 846cf346..fd575768 100644 --- a/tpm2/kdf.go +++ b/tpm2/kdf.go @@ -15,22 +15,19 @@ package tpm2 import ( + "crypto" "crypto/hmac" "encoding/binary" "hash" ) -// KDFa implements TPM 2.0's default key derivation function, as defined in +// KDFaHash implements TPM 2.0's default key derivation function, as defined in // section 11.4.9.2 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The key & label parameters must not be zero length. // The label parameter is a non-null-terminated string. // The contextU & contextV parameters are optional. -func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte, bits int) ([]byte, error) { - h, err := hashAlg.Hash() - if err != nil { - return nil, err - } +func KDFaHash(h crypto.Hash, key []byte, label string, contextU, contextV []byte, bits int) []byte { mac := hmac.New(h.New, key) out := kdf(mac, bits, func() { @@ -40,30 +37,46 @@ func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte mac.Write(contextV) binary.Write(mac, binary.BigEndian, uint32(bits)) }) - return out, nil + return out } -// KDFe implements TPM 2.0's ECDH key derivation function, as defined in +// Deprecated: Use KDFaHash. +func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte, bits int) ([]byte, error) { + h, err := hashAlg.Hash() + if err != nil { + return nil, err + } + return KDFaHash(h, key, label, contextU, contextV, bits), nil +} + +// KDFeHash implements TPM 2.0's ECDH key derivation function, as defined in // section 11.4.9.3 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ -// The z parameter is the x coordinate of one parties private ECC key and the other parties public ECC key. +// The z parameter is the x coordinate of one party's private ECC key multiplied +// by the other party's public ECC point. // The use parameter is a non-null-terminated string. -// The partyUInfo and partyVInfo are the x coordinates of the initiators and the responders ECC points respectively. +// The partyUInfo and partyVInfo are the x coordinates of the initiator's and +// the responder's ECC points, respectively. +func KDFeHash(h crypto.Hash, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) []byte { + hash := h.New() + + out := kdf(hash, bits, func() { + hash.Write(z) + hash.Write([]byte(use)) + hash.Write([]byte{0}) // Terminating null character for C-string. + hash.Write(partyUInfo) + hash.Write(partyVInfo) + }) + return out +} + +// Deprecated: Use KDFeHash. func KDFe(hashAlg Algorithm, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) ([]byte, error) { - createHash, err := hashAlg.Hash() + h, err := hashAlg.Hash() if err != nil { return nil, err } - h := createHash.New() - - out := kdf(h, bits, func() { - h.Write(z) - h.Write([]byte(use)) - h.Write([]byte{0}) // Terminating null character for C-string. - h.Write(partyUInfo) - h.Write(partyVInfo) - }) - return out, nil + return KDFeHash(h, z, use, partyUInfo, partyVInfo, bits), nil } func kdf(h hash.Hash, bits int, update func()) []byte { @@ -77,7 +90,8 @@ func kdf(h hash.Hash, bits int, update func()) []byte { out = h.Sum(out) } - // out's length is a multiple of hash size, so there will be excess bytes if bytes isn't a multiple of hash size. + // out's length is a multiple of hash size, so there will be excess + // bytes if bytes isn't a multiple of hash size. out = out[:bytes] // As mentioned in the KDFa and KDFe specs mentioned above, diff --git a/tpmutil/run.go b/tpmutil/run.go index 984b1238..c07e3aba 100644 --- a/tpmutil/run.go +++ b/tpmutil/run.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package tpmutil provides common utility functions for both TPM 1.2 and TPM 2.0 devices. +// Package tpmutil provides common utility functions for both TPM 1.2 and TPM +// 2.0 devices. package tpmutil import ( @@ -28,18 +29,11 @@ import ( // returning a header and a body in separate responses. const maxTPMResponse = 4096 -// RunCommand executes cmd with given tag and arguments. Returns TPM response -// body (without response header) and response code from the header. Returned -// error may be nil if response code is not RCSuccess; caller should check -// both. -func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) { +// RunCommandRaw executes the given raw command and returns the raw response. +// Does not check the response code except to execute retry logic. +func RunCommandRaw(rw io.ReadWriter, inb []byte) ([]byte, error) { if rw == nil { - return nil, 0, errors.New("nil TPM handle") - } - ch := commandHeader{tag, 0, cmd} - inb, err := packWithHeader(ch, in...) - if err != nil { - return nil, 0, err + return nil, errors.New("nil TPM handle") } // f(t) = (2^t)ms, up to 2s @@ -49,48 +43,71 @@ func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]by for { if _, err := rw.Write(inb); err != nil { - return nil, 0, err + return nil, err } - // If the TPM is a real device, it may not be ready for reading immediately after writing - // the command. Wait until the file descriptor is ready to be read from. + // If the TPM is a real device, it may not be ready for reading + // immediately after writing the command. Wait until the file + // descriptor is ready to be read from. if f, ok := rw.(*os.File); ok { - if err = poll(f); err != nil { - return nil, 0, err + if err := poll(f); err != nil { + return nil, err } } outb = make([]byte, maxTPMResponse) outlen, err := rw.Read(outb) if err != nil { - return nil, 0, err + return nil, err } // Resize the buffer to match the amount read from the TPM. outb = outb[:outlen] - read, err := Unpack(outb, &rh) + _, err = Unpack(outb, &rh) if err != nil { - return nil, 0, err + return nil, err } - outb = outb[read:] - // In case TPM is busy retry the command after waiting a few miliseconds + // If TPM is busy, retry the command after waiting a few ms. if rh.Res == RCRetry { if backoffFac < 11 { dur := (1 << backoffFac) * time.Millisecond time.Sleep(dur) backoffFac++ } else { - return nil, 0, err + return nil, err } } else { break } } + return outb, nil +} + +// RunCommand executes cmd with given tag and arguments. Returns TPM response +// body (without response header) and response code from the header. Returned +// error may be nil if response code is not RCSuccess; caller should check +// both. +func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) { + inb, err := packWithHeader(commandHeader{tag, 0, cmd}, in...) + if err != nil { + return nil, 0, err + } + + outb, err := RunCommandRaw(rw, inb) + if err != nil { + return nil, 0, err + } + + var rh responseHeader + read, err := Unpack(outb, &rh) + if err != nil { + return nil, 0, err + } if rh.Res != RCSuccess { return nil, rh.Res, nil } - return outb, rh.Res, nil + return outb[read:], rh.Res, nil } From 571036637b132599664283777597dd1848e8b771 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 21:08:55 +0000 Subject: [PATCH 2/9] fix problems identified by go vet --- tpm2/credactivation/credential_activation.go | 8 +++----- tpm2/direct/reflect.go | 1 + tpm2/direct/sessions.go | 3 +++ tpm2/direct/structures.go | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tpm2/credactivation/credential_activation.go b/tpm2/credactivation/credential_activation.go index 01014d63..7045b35c 100644 --- a/tpm2/credactivation/credential_activation.go +++ b/tpm2/credactivation/credential_activation.go @@ -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) @@ -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) diff --git a/tpm2/direct/reflect.go b/tpm2/direct/reflect.go index f547e966..15af3b10 100644 --- a/tpm2/direct/reflect.go +++ b/tpm2/direct/reflect.go @@ -1,3 +1,4 @@ +// Package direct provides 1:1 mapping to TPM 2.0 APIs. package direct import ( diff --git a/tpm2/direct/sessions.go b/tpm2/direct/sessions.go index c5504e80..bee4120f 100644 --- a/tpm2/direct/sessions.go +++ b/tpm2/direct/sessions.go @@ -224,8 +224,11 @@ func Salted(handle TPMIDHObject, pub TPMTPublic) AuthOption { type parameterEncryptionDirection int const ( + // EncryptIn specifies a decrypt session. EncryptIn parameterEncryptionDirection = 1 + iota + // EncryptOut specifies an encrypt session. EncryptOut + // EncryptInOut specifies a decrypt+encrypt session. EncryptInOut ) diff --git a/tpm2/direct/structures.go b/tpm2/direct/structures.go index 0817cdc5..ac7a8a78 100644 --- a/tpm2/direct/structures.go +++ b/tpm2/direct/structures.go @@ -302,14 +302,14 @@ type TPMACC struct { Extensive bool `gotpm:"bit=23"` // SET (1): The context associated with any transient handle in the command will be flushed when this command completes. // CLEAR (0): No context is flushed as a side effect of this command. - Flushed bool `gotpm="bit=24"` + Flushed bool `gotpm:"bit=24"` // indicates the number of the handles in the handle area for this command - CHandles uint8 `gotpm="bit=27:25"` + CHandles uint8 `gotpm:"bit=27:25"` // SET (1): indicates the presence of the handle area in the response - RHandle bool `gotpm="bit=28"` + RHandle bool `gotpm:"bit=28"` // SET (1): indicates that the command is vendor-specific // CLEAR (0): indicates that the command is defined in a version of this specification - V bool `gotpm="bit=29"` + V bool `gotpm:"bit=29"` // allocated for software; shall be zero Reserved2 uint8 `gotpm:"bit=31:30"` } From a0ecb01913b5b939251a7a5caacf7b55b9cfcf60 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 21:11:45 +0000 Subject: [PATCH 3/9] fix some more issues identified by go vet --- tpm2/direct/commands.go | 52 ++++++++++++++++++++--------------------- tpm2/direct/sessions.go | 4 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tpm2/direct/commands.go b/tpm2/direct/commands.go index 5bcc2c82..3acd09da 100644 --- a/tpm2/direct/commands.go +++ b/tpm2/direct/commands.go @@ -97,7 +97,7 @@ type StartAuthSessionCommand struct { AuthHash TPMIAlgHash } -func (_ *StartAuthSessionCommand) Command() TPMCC { return TPMCCStartAuthSession } +func (*StartAuthSessionCommand) Command() TPMCC { return TPMCCStartAuthSession } // StartAuthSessionResponse is the response from TPM2_StartAuthSession. type StartAuthSessionResponse struct { @@ -107,7 +107,7 @@ type StartAuthSessionResponse struct { NonceTPM TPM2BNonce } -func (_ *StartAuthSessionResponse) Response() TPMCC { return TPMCCStartAuthSession } +func (*StartAuthSessionResponse) Response() TPMCC { return TPMCCStartAuthSession } // CreateCommand is the input to TPM2_Create. // See definition in Part 3, Commands, section 12.1 @@ -126,7 +126,7 @@ type CreateCommand struct { CreationPCR TPMLPCRSelection } -func (_ *CreateCommand) Command() TPMCC { return TPMCCCreate } +func (*CreateCommand) Command() TPMCC { return TPMCCCreate } // CreateResponse is the response from TPM2_Create. type CreateResponse struct { @@ -143,7 +143,7 @@ type CreateResponse struct { CreationTicket TPMTTKCreation } -func (_ *CreateResponse) Response() TPMCC { return TPMCCCreate } +func (*CreateResponse) Response() TPMCC { return TPMCCCreate } // LoadCommand is the input to TPM2_Load. // See definition in Part 3, Commands, section 12.2 @@ -156,7 +156,7 @@ type LoadCommand struct { InPublic TPM2BPublic } -func (_ *LoadCommand) Command() TPMCC { return TPMCCLoad } +func (*LoadCommand) Command() TPMCC { return TPMCCLoad } // LoadResponse is the response from TPM2_Load. type LoadResponse struct { @@ -166,7 +166,7 @@ type LoadResponse struct { Name TPM2BName } -func (_ *LoadResponse) Response() TPMCC { return TPMCCLoad } +func (*LoadResponse) Response() TPMCC { return TPMCCLoad } // UnsealCommand is the input to TPM2_Unseal. // See definition in Part 3, Commands, section 12.7 @@ -174,14 +174,14 @@ type UnsealCommand struct { ItemHandle AuthHandle `gotpm:"handle,auth"` } -func (_ *UnsealCommand) Command() TPMCC { return TPMCCUnseal } +func (*UnsealCommand) Command() TPMCC { return TPMCCUnseal } // UnsealResponse is the response from TPM2_Unseal. type UnsealResponse struct { OutData TPM2BSensitiveData } -func (_ *UnsealResponse) Response() TPMCC { return TPMCCUnseal } +func (*UnsealResponse) Response() TPMCC { return TPMCCUnseal } // QuoteCommand is the input to TPM2_Quote. // See definition in Part 3, Commands, section 18.4 @@ -196,7 +196,7 @@ type QuoteCommand struct { PCRSelect TPMLPCRSelection } -func (_ *QuoteCommand) Command() TPMCC { return TPMCCQuote } +func (*QuoteCommand) Command() TPMCC { return TPMCCQuote } // QuoteResponse is the response from TPM2_Quote. type QuoteResponse struct { @@ -206,7 +206,7 @@ type QuoteResponse struct { Signature TPMTSignature } -func (_ *QuoteResponse) Response() TPMCC { return TPMCCQuote } +func (*QuoteResponse) Response() TPMCC { return TPMCCQuote } // GetSessionAuditDigestCommand is the input to TPM2_GetSessionAuditDigest. // See definition in Part 3, Commands, section 18.5 @@ -223,7 +223,7 @@ type GetSessionAuditDigestCommand struct { InScheme TPMTSigScheme } -func (_ *GetSessionAuditDigestCommand) Command() TPMCC { return TPMCCGetSessionAuditDigest } +func (*GetSessionAuditDigestCommand) Command() TPMCC { return TPMCCGetSessionAuditDigest } // GetSessionAuditDigestResponse is the response from // TPM2_GetSessionAuditDigest. @@ -234,7 +234,7 @@ type GetSessionAuditDigestResponse struct { Signature TPMTSignature } -func (_ *GetSessionAuditDigestResponse) Response() TPMCC { return TPMCCGetSessionAuditDigest } +func (*GetSessionAuditDigestResponse) Response() TPMCC { return TPMCCGetSessionAuditDigest } // PCRExtendCommand is the input to TPM2_PCR_Extend. // See definition in Part 3, Commands, section 22.2 @@ -245,13 +245,13 @@ type PCRExtendCommand struct { Digests TPMLDigestValues } -func (_ *PCRExtendCommand) Command() TPMCC { return TPMCCPCRExtend } +func (*PCRExtendCommand) Command() TPMCC { return TPMCCPCRExtend } // PCRExtendResponse is the response from TPM2_PCR_Extend. type PCRExtendResponse struct { } -func (_ *PCRExtendResponse) Response() TPMCC { return TPMCCPCRExtend } +func (*PCRExtendResponse) Response() TPMCC { return TPMCCPCRExtend } // PCREventCommand is the input to TPM2_PCR_Event. // See definition in Part 3, Commands, section 22.3 @@ -262,13 +262,13 @@ type PCREventCommand struct { EventData TPM2BEvent } -func (_ *PCREventCommand) Command() TPMCC { return TPMCCPCREvent } +func (*PCREventCommand) Command() TPMCC { return TPMCCPCREvent } // PCREventResponse is the response from TPM2_PCR_Event. type PCREventResponse struct { } -func (_ *PCREventResponse) Response() TPMCC { return TPMCCPCREvent } +func (*PCREventResponse) Response() TPMCC { return TPMCCPCREvent } // PCRReadCommand is the input to TPM2_PCR_Read. // See definition in Part 3, Commands, section 22.4 @@ -277,7 +277,7 @@ type PCRReadCommand struct { PCRSelectionIn TPMLPCRSelection } -func (_ *PCRReadCommand) Command() TPMCC { return TPMCCPCRRead } +func (*PCRReadCommand) Command() TPMCC { return TPMCCPCRRead } // PCRReadResponse is the response from TPM2_PCR_Read. type PCRReadResponse struct { @@ -289,7 +289,7 @@ type PCRReadResponse struct { PCRValues TPMLDigest } -func (_ *PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } +func (*PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } // PolicySecret is the input to TPM2_PolicySecret. // See definition in Part 3, Commands, section 23.4 @@ -309,7 +309,7 @@ type PolicySecretCommand struct { Expiration int32 } -func (_ *PolicySecretCommand) Command() TPMCC { return TPMCCPolicySecret } +func (*PolicySecretCommand) Command() TPMCC { return TPMCCPolicySecret } // PolicySecretResponse is the response from TPM2_PolicySecret. type PolicySecretResponse struct { @@ -319,7 +319,7 @@ type PolicySecretResponse struct { PolicyTicket TPMTTKAuth } -func (_ *PolicySecretResponse) Response() TPMCC { return TPMCCPolicySecret } +func (*PolicySecretResponse) Response() TPMCC { return TPMCCPolicySecret } // CreatePrimaryCommand is the input to TPM2_CreatePrimary. // See definition in Part 3, Commands, section 24.1 @@ -339,7 +339,7 @@ type CreatePrimaryCommand struct { CreationPCR TPMLPCRSelection } -func (_ *CreatePrimaryCommand) Command() TPMCC { return TPMCCCreatePrimary } +func (*CreatePrimaryCommand) Command() TPMCC { return TPMCCCreatePrimary } // CreatePrimaryResponse is the response from TPM2_CreatePrimary. type CreatePrimaryResponse struct { @@ -358,7 +358,7 @@ type CreatePrimaryResponse struct { Name TPM2BName } -func (_ *CreatePrimaryResponse) Response() TPMCC { return TPMCCCreatePrimary } +func (*CreatePrimaryResponse) Response() TPMCC { return TPMCCCreatePrimary } // FlushContextCommand is the input to TPM2_FlushContext. // See definition in Part 3, Commands, section 28.4 @@ -367,13 +367,13 @@ type FlushContextCommand struct { FlushHandle TPMIDHContext } -func (_ *FlushContextCommand) Command() TPMCC { return TPMCCFlushContext } +func (*FlushContextCommand) Command() TPMCC { return TPMCCFlushContext } // FlushContextResponse is the response from TPM2_FlushContext. type FlushContextResponse struct { } -func (_ *FlushContextResponse) Response() TPMCC { return TPMCCFlushContext } +func (*FlushContextResponse) Response() TPMCC { return TPMCCFlushContext } // GetCapabilityCommand is the input to TPM2_GetCapability. // See definition in Part 3, Commands, section 30.2 @@ -386,7 +386,7 @@ type GetCapabilityCommand struct { PropertyCount uint32 } -func (_ *GetCapabilityCommand) Command() TPMCC { return TPMCCGetCapability } +func (*GetCapabilityCommand) Command() TPMCC { return TPMCCGetCapability } // GetCapabilityResponse is the response from TPM2_GetCapability. type GetCapabilityResponse struct { @@ -396,4 +396,4 @@ type GetCapabilityResponse struct { CapabilityData TPMSCapabilityData } -func (_ *GetCapabilityResponse) Response() TPMCC { return TPMCCGetCapability } +func (*GetCapabilityResponse) Response() TPMCC { return TPMCCGetCapability } diff --git a/tpm2/direct/sessions.go b/tpm2/direct/sessions.go index bee4120f..a1a75e10 100644 --- a/tpm2/direct/sessions.go +++ b/tpm2/direct/sessions.go @@ -187,11 +187,11 @@ func Auth(auth []byte) AuthOption { } } -// Deprecated: This is not recommended and is only provided for completeness; -// use Auth instead. // Password is a policy-session-only option that specifies to provide the // object's auth value in place of the authorization HMAC when authorizing. // For HMAC sessions, has the same effect as using Auth. +// Deprecated: This is not recommended and is only provided for completeness; +// use Auth instead. func Password(auth []byte) AuthOption { return func(o *sessionOptions) { o.auth = auth From 16932234c116187db27fb7aba63f746087c0cbf2 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 21:18:52 +0000 Subject: [PATCH 4/9] fix some more issues surfaced by vet --- tpm2/direct/commands.go | 26 ++++++++++++++++++++++++++ tpm2/direct/constants.go | 2 ++ tpm2/direct/structures.go | 6 +++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/tpm2/direct/commands.go b/tpm2/direct/commands.go index 3acd09da..e05274d2 100644 --- a/tpm2/direct/commands.go +++ b/tpm2/direct/commands.go @@ -97,6 +97,7 @@ type StartAuthSessionCommand struct { AuthHash TPMIAlgHash } +// Command implements the Command interface. func (*StartAuthSessionCommand) Command() TPMCC { return TPMCCStartAuthSession } // StartAuthSessionResponse is the response from TPM2_StartAuthSession. @@ -107,6 +108,7 @@ type StartAuthSessionResponse struct { NonceTPM TPM2BNonce } +// Response implements the Response interface. func (*StartAuthSessionResponse) Response() TPMCC { return TPMCCStartAuthSession } // CreateCommand is the input to TPM2_Create. @@ -126,6 +128,7 @@ type CreateCommand struct { CreationPCR TPMLPCRSelection } +// Command implements the Command interface. func (*CreateCommand) Command() TPMCC { return TPMCCCreate } // CreateResponse is the response from TPM2_Create. @@ -143,6 +146,7 @@ type CreateResponse struct { CreationTicket TPMTTKCreation } +// Response implements the Response interface. func (*CreateResponse) Response() TPMCC { return TPMCCCreate } // LoadCommand is the input to TPM2_Load. @@ -156,6 +160,7 @@ type LoadCommand struct { InPublic TPM2BPublic } +// Command implements the Command interface. func (*LoadCommand) Command() TPMCC { return TPMCCLoad } // LoadResponse is the response from TPM2_Load. @@ -166,6 +171,7 @@ type LoadResponse struct { Name TPM2BName } +// Response implements the Response interface. func (*LoadResponse) Response() TPMCC { return TPMCCLoad } // UnsealCommand is the input to TPM2_Unseal. @@ -174,6 +180,7 @@ type UnsealCommand struct { ItemHandle AuthHandle `gotpm:"handle,auth"` } +// Command implements the Command interface. func (*UnsealCommand) Command() TPMCC { return TPMCCUnseal } // UnsealResponse is the response from TPM2_Unseal. @@ -181,6 +188,7 @@ type UnsealResponse struct { OutData TPM2BSensitiveData } +// Response implements the Response interface. func (*UnsealResponse) Response() TPMCC { return TPMCCUnseal } // QuoteCommand is the input to TPM2_Quote. @@ -196,6 +204,7 @@ type QuoteCommand struct { PCRSelect TPMLPCRSelection } +// Command implements the Command interface. func (*QuoteCommand) Command() TPMCC { return TPMCCQuote } // QuoteResponse is the response from TPM2_Quote. @@ -206,6 +215,7 @@ type QuoteResponse struct { Signature TPMTSignature } +// Response implements the Response interface. func (*QuoteResponse) Response() TPMCC { return TPMCCQuote } // GetSessionAuditDigestCommand is the input to TPM2_GetSessionAuditDigest. @@ -223,6 +233,7 @@ type GetSessionAuditDigestCommand struct { InScheme TPMTSigScheme } +// Command implements the Command interface. func (*GetSessionAuditDigestCommand) Command() TPMCC { return TPMCCGetSessionAuditDigest } // GetSessionAuditDigestResponse is the response from @@ -234,6 +245,7 @@ type GetSessionAuditDigestResponse struct { Signature TPMTSignature } +// Response implements the Response interface. func (*GetSessionAuditDigestResponse) Response() TPMCC { return TPMCCGetSessionAuditDigest } // PCRExtendCommand is the input to TPM2_PCR_Extend. @@ -245,12 +257,14 @@ type PCRExtendCommand struct { Digests TPMLDigestValues } +// Command implements the Command interface. func (*PCRExtendCommand) Command() TPMCC { return TPMCCPCRExtend } // PCRExtendResponse is the response from TPM2_PCR_Extend. type PCRExtendResponse struct { } +// Response implements the Response interface. func (*PCRExtendResponse) Response() TPMCC { return TPMCCPCRExtend } // PCREventCommand is the input to TPM2_PCR_Event. @@ -262,12 +276,14 @@ type PCREventCommand struct { EventData TPM2BEvent } +// Command implements the Command interface. func (*PCREventCommand) Command() TPMCC { return TPMCCPCREvent } // PCREventResponse is the response from TPM2_PCR_Event. type PCREventResponse struct { } +// Response implements the Response interface. func (*PCREventResponse) Response() TPMCC { return TPMCCPCREvent } // PCRReadCommand is the input to TPM2_PCR_Read. @@ -277,6 +293,7 @@ type PCRReadCommand struct { PCRSelectionIn TPMLPCRSelection } +// Command implements the Command interface. func (*PCRReadCommand) Command() TPMCC { return TPMCCPCRRead } // PCRReadResponse is the response from TPM2_PCR_Read. @@ -289,6 +306,7 @@ type PCRReadResponse struct { PCRValues TPMLDigest } +// Response implements the Response interface. func (*PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } // PolicySecret is the input to TPM2_PolicySecret. @@ -309,6 +327,7 @@ type PolicySecretCommand struct { Expiration int32 } +// Command implements the Command interface. func (*PolicySecretCommand) Command() TPMCC { return TPMCCPolicySecret } // PolicySecretResponse is the response from TPM2_PolicySecret. @@ -319,6 +338,7 @@ type PolicySecretResponse struct { PolicyTicket TPMTTKAuth } +// Response implements the Response interface. func (*PolicySecretResponse) Response() TPMCC { return TPMCCPolicySecret } // CreatePrimaryCommand is the input to TPM2_CreatePrimary. @@ -339,6 +359,7 @@ type CreatePrimaryCommand struct { CreationPCR TPMLPCRSelection } +// Command implements the Command interface. func (*CreatePrimaryCommand) Command() TPMCC { return TPMCCCreatePrimary } // CreatePrimaryResponse is the response from TPM2_CreatePrimary. @@ -358,6 +379,7 @@ type CreatePrimaryResponse struct { Name TPM2BName } +// Response implements the Response interface. func (*CreatePrimaryResponse) Response() TPMCC { return TPMCCCreatePrimary } // FlushContextCommand is the input to TPM2_FlushContext. @@ -367,12 +389,14 @@ type FlushContextCommand struct { FlushHandle TPMIDHContext } +// Command implements the Command interface. func (*FlushContextCommand) Command() TPMCC { return TPMCCFlushContext } // FlushContextResponse is the response from TPM2_FlushContext. type FlushContextResponse struct { } +// Response implements the Response interface. func (*FlushContextResponse) Response() TPMCC { return TPMCCFlushContext } // GetCapabilityCommand is the input to TPM2_GetCapability. @@ -386,6 +410,7 @@ type GetCapabilityCommand struct { PropertyCount uint32 } +// Command implements the Command interface. func (*GetCapabilityCommand) Command() TPMCC { return TPMCCGetCapability } // GetCapabilityResponse is the response from TPM2_GetCapability. @@ -396,4 +421,5 @@ type GetCapabilityResponse struct { CapabilityData TPMSCapabilityData } +// Response implements the Response interface. func (*GetCapabilityResponse) Response() TPMCC { return TPMCCGetCapability } diff --git a/tpm2/direct/constants.go b/tpm2/direct/constants.go index 42132529..f81bf34e 100644 --- a/tpm2/direct/constants.go +++ b/tpm2/direct/constants.go @@ -3,6 +3,8 @@ package direct import ( "crypto" "crypto/elliptic" + + // Register the relevant hash implementations. _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" diff --git a/tpm2/direct/structures.go b/tpm2/direct/structures.go index ac7a8a78..1d9454e3 100644 --- a/tpm2/direct/structures.go +++ b/tpm2/direct/structures.go @@ -402,9 +402,9 @@ type TPMUHA struct { SHA256 *[32]byte `gotpm:"selector=0x000B"` // TPM_ALG_SHA256 SHA384 *[48]byte `gotpm:"selector=0x000C"` // TPM_ALG_SHA384 SHA512 *[64]byte `gotpm:"selector=0x000D"` // TPM_ALG_SHA512 - SHA3_256 *[32]byte `gotpm:"selector=0x0027"` // TPM_ALG_SHA3_256 - SHA3_384 *[48]byte `gotpm:"selector=0x0028"` // TPM_ALG_SHA3_384 - SHA3_512 *[64]byte `gotpm:"selector=0x0029"` // TPM_ALG_SHA3_512 + SHA3x256 *[32]byte `gotpm:"selector=0x0027"` // TPM_ALG_SHA3_256 + SHA3x384 *[48]byte `gotpm:"selector=0x0028"` // TPM_ALG_SHA3_384 + SHA3x512 *[64]byte `gotpm:"selector=0x0029"` // TPM_ALG_SHA3_512 } // TPMTHA represents a TPMT_HA. From 2aa4c49b5f8e8177ac4a5ff94a20ee0801d8ba5c Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 21:22:40 +0000 Subject: [PATCH 5/9] fix more go vet issues --- tpm2/direct/commands.go | 2 +- tpm2/direct/constants.go | 2 -- tpm2/direct/names.go | 1 + tpm2/direct/reflect.go | 18 +++--------------- tpm2/kdf.go | 13 +++++++++++++ 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tpm2/direct/commands.go b/tpm2/direct/commands.go index e05274d2..cc4107ad 100644 --- a/tpm2/direct/commands.go +++ b/tpm2/direct/commands.go @@ -309,7 +309,7 @@ type PCRReadResponse struct { // Response implements the Response interface. func (*PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } -// PolicySecret is the input to TPM2_PolicySecret. +// PolicySecretCommand is the input to TPM2_PolicySecret. // See definition in Part 3, Commands, section 23.4 type PolicySecretCommand struct { // handle for an entity providing the authorization diff --git a/tpm2/direct/constants.go b/tpm2/direct/constants.go index f81bf34e..0ea763b2 100644 --- a/tpm2/direct/constants.go +++ b/tpm2/direct/constants.go @@ -428,8 +428,6 @@ const ( // the maximum allowed difference (unsigned) between the contextID // values of two saved session contexts TPMPTContextGapMax TPMPT = 0x00000114 - // skipped - skippedTPMPT115 TPMPT = 0x00000115 // the maximum number of NV Indexes that are allowed to have the // TPM_NT_COUNTER attribute TPMPTNVCountersMax TPMPT = 0x00000116 diff --git a/tpm2/direct/names.go b/tpm2/direct/names.go index 76a471d8..7e6f3ad8 100644 --- a/tpm2/direct/names.go +++ b/tpm2/direct/names.go @@ -4,6 +4,7 @@ import ( "encoding/binary" ) +// PrimaryHandleName returns the TPM Name of a primary handle. func PrimaryHandleName(h TPMHandle) []byte { result := make([]byte, 4) binary.BigEndian.PutUint32(result, uint32(h)) diff --git a/tpm2/direct/reflect.go b/tpm2/direct/reflect.go index 15af3b10..5dc08725 100644 --- a/tpm2/direct/reflect.go +++ b/tpm2/direct/reflect.go @@ -747,19 +747,6 @@ func cmdAuths(cmd Command) ([]Session, error) { return result, nil } -// numAuthHandles returns the number of authorization sessions needed for the -// command. -func numAuthHandles(cmd Command) int { - result := 0 - cmdType := reflect.TypeOf(cmd).Elem() - for i := 0; i < cmdType.NumField(); i++ { - if hasTag(cmdType.Field(i), "auth") { - result++ - } - } - return result -} - // cmdHandles returns the handles area of the command. func cmdHandles(cmd Command) []byte { handles := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", false) @@ -989,8 +976,9 @@ func rspParameters(parms []byte, sess []Session, rspStruct Response) error { parmsFields := taggedMembers(reflect.ValueOf(rspStruct).Elem(), "handle", true) // Use the heuristic of "does interpreting the first 2 bytes of response - // as a length make any sense" to attempt encrypted paramter decryption. - // If the command supports parameter encryption, the first paramter is + // as a length make any sense" to attempt encrypted parameter + // decryption. + // If the command supports parameter encryption, the first parameter is // a 2B. if len(parms) < 2 { return nil diff --git a/tpm2/kdf.go b/tpm2/kdf.go index fd575768..b2108484 100644 --- a/tpm2/kdf.go +++ b/tpm2/kdf.go @@ -40,6 +40,12 @@ func KDFaHash(h crypto.Hash, key []byte, label string, contextU, contextV []byte return out } +// KDFaHash implements TPM 2.0's default key derivation function, as defined in +// section 11.4.9.2 of the TPM revision 2 specification part 1. +// See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ +// The key & label parameters must not be zero length. +// The label parameter is a non-null-terminated string. +// The contextU & contextV parameters are optional. // Deprecated: Use KDFaHash. func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte, bits int) ([]byte, error) { h, err := hashAlg.Hash() @@ -70,6 +76,13 @@ func KDFeHash(h crypto.Hash, z []byte, use string, partyUInfo, partyVInfo []byte return out } +// KDFeHash implements TPM 2.0's ECDH key derivation function, as defined in +// section 11.4.9.3 of the TPM revision 2 specification part 1. +// See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ +// The z parameter is the x coordinate of one party's private ECC key multiplied +// by the other party's public ECC point. +// The use parameter is a non-null-terminated string. +// The partyUInfo and partyVInfo are the x coordinates of the initiator's and // Deprecated: Use KDFeHash. func KDFe(hashAlg Algorithm, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) ([]byte, error) { h, err := hashAlg.Hash() From c7f46f00eb873d8a7ee38caa83242e55d0fd69e2 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Wed, 15 Dec 2021 21:24:47 +0000 Subject: [PATCH 6/9] one last round of go vet fixes --- tpm2/kdf.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tpm2/kdf.go b/tpm2/kdf.go index b2108484..f2fe507e 100644 --- a/tpm2/kdf.go +++ b/tpm2/kdf.go @@ -40,7 +40,7 @@ func KDFaHash(h crypto.Hash, key []byte, label string, contextU, contextV []byte return out } -// KDFaHash implements TPM 2.0's default key derivation function, as defined in +// KDFa implements TPM 2.0's default key derivation function, as defined in // section 11.4.9.2 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The key & label parameters must not be zero length. @@ -76,7 +76,7 @@ func KDFeHash(h crypto.Hash, z []byte, use string, partyUInfo, partyVInfo []byte return out } -// KDFeHash implements TPM 2.0's ECDH key derivation function, as defined in +// KDFe implements TPM 2.0's ECDH key derivation function, as defined in // section 11.4.9.3 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The z parameter is the x coordinate of one party's private ECC key multiplied From 90f003e6f0459e5e147eb739987ca37bcec491d7 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Sat, 29 Jan 2022 12:50:24 -0800 Subject: [PATCH 7/9] Use subpackages and put the Execute function on the command types (#1) * WIP: put stuff into subpackages to make the names nice * use internal monolithic package to avoid cycles * complete the tpm2 package * fix test references to renamed types * switch to command.Execute pattern * fix some unkeyed field values detected by go vet * stop panicking from Hash --- .tempnotes.txt.swp | Bin 0 -> 12288 bytes direct/helpers/crypto.go | 46 ++ {tpm2/direct => direct/helpers}/names.go | 6 +- direct/helpers/wrappers.go | 12 + .../structures/internal}/constants.go | 43 +- .../structures/internal}/errors.go | 30 +- .../structures/internal}/structures.go | 50 +- direct/structures/tpm/constants.go | 506 ++++++++++++++++++ direct/structures/tpm/tpm.go | 90 ++++ direct/structures/tpm2b/tpm2b.go | 75 +++ direct/structures/tpma/tpma.go | 30 ++ direct/structures/tpmi/tpmi.go | 95 ++++ direct/structures/tpml/tpml.go | 58 ++ direct/structures/tpms/tpms.go | 180 +++++++ direct/structures/tpmt/tpmt.go | 52 ++ direct/structures/tpmu/tpmu.go | 56 ++ direct/templates/templates.go | 200 +++++++ {tpm2/direct => direct/tpm2}/audit.go | 33 +- {tpm2/direct => direct/tpm2}/audit_test.go | 94 ++-- {tpm2/direct => direct/tpm2}/ek_test.go | 88 +-- {tpm2/direct => direct/tpm2}/reflect.go | 47 +- {tpm2/direct => direct/tpm2}/reflect_test.go | 2 +- {tpm2/direct => direct/tpm2}/sealing_test.go | 189 ++++--- {tpm2/direct => direct/tpm2}/sessions.go | 371 +++++++------ .../direct/commands.go => direct/tpm2/tpm2.go | 377 ++++++++----- tpm2/direct/crypto.go | 43 -- tpm2/direct/templates.go | 190 ------- tpm2/direct/wrappers.go | 10 - 28 files changed, 2178 insertions(+), 795 deletions(-) create mode 100644 .tempnotes.txt.swp create mode 100644 direct/helpers/crypto.go rename {tpm2/direct => direct/helpers}/names.go (62%) create mode 100644 direct/helpers/wrappers.go rename {tpm2/direct => direct/structures/internal}/constants.go (95%) rename {tpm2/direct => direct/structures/internal}/errors.go (96%) rename {tpm2/direct => direct/structures/internal}/structures.go (97%) create mode 100644 direct/structures/tpm/constants.go create mode 100644 direct/structures/tpm/tpm.go create mode 100644 direct/structures/tpm2b/tpm2b.go create mode 100644 direct/structures/tpma/tpma.go create mode 100644 direct/structures/tpmi/tpmi.go create mode 100644 direct/structures/tpml/tpml.go create mode 100644 direct/structures/tpms/tpms.go create mode 100644 direct/structures/tpmt/tpmt.go create mode 100644 direct/structures/tpmu/tpmu.go create mode 100644 direct/templates/templates.go rename {tpm2/direct => direct/tpm2}/audit.go (73%) rename {tpm2/direct => direct/tpm2}/audit_test.go (50%) rename {tpm2/direct => direct/tpm2}/ek_test.go (72%) rename {tpm2/direct => direct/tpm2}/reflect.go (96%) rename {tpm2/direct => direct/tpm2}/reflect_test.go (99%) rename {tpm2/direct => direct/tpm2}/sealing_test.go (65%) rename {tpm2/direct => direct/tpm2}/sessions.go (73%) rename tpm2/direct/commands.go => direct/tpm2/tpm2.go (52%) delete mode 100644 tpm2/direct/crypto.go delete mode 100644 tpm2/direct/templates.go delete mode 100644 tpm2/direct/wrappers.go diff --git a/.tempnotes.txt.swp b/.tempnotes.txt.swp new file mode 100644 index 0000000000000000000000000000000000000000..29e3e81539e394487a8680815ac7c5d2121af5e6 GIT binary patch literal 12288 zcmeI&u};EJ6vpv`qY2UA3xJNsTc``Kz~G2G)Y_2H-iBVI8*jkFvA8(7xVX5u=-@e6 z7DJpEX!39JeK&1xZ*G5E(@y)hqnqx~#iGZl)$cKSyp-fVEsE`hf5y!`m$=OPY@RME zm-u|w^r%`SQRc5gnMWbb%fg4Wa^WfLn#?r>5ZJRodwd-a`nKrBd9Qoc8Si<0mO}sm z1Q0*~0R#|00D(gm2xUu_y1}D*lgIVg`ZLz23O^F%vG4!e_y2D_|I&PFJ~Z!|H_fZ&Mf0rL+|$5kX3Gd5fB*srAbx0fMQd&qPpyO1gH>fUvzmyt`~bT$H9!CW literal 0 HcmV?d00001 diff --git a/direct/helpers/crypto.go b/direct/helpers/crypto.go new file mode 100644 index 00000000..49bab755 --- /dev/null +++ b/direct/helpers/crypto.go @@ -0,0 +1,46 @@ +package helpers + +import ( + "crypto/elliptic" + "crypto/rsa" + "math/big" + + "github.com/google/go-tpm/direct/structures/tpm2b" + "github.com/google/go-tpm/direct/structures/tpms" +) + +// RSAPub converts a TPM RSA public key into one recognized by the rsa package. +func RSAPub(parms *tpms.RSAParms, pub *tpm2b.PublicKeyRSA) (*rsa.PublicKey, error) { + result := rsa.PublicKey{ + N: big.NewInt(0).SetBytes(pub.Buffer), + E: int(parms.Exponent), + } + // TPM considers 65537 to be the default RSA public exponent, and 0 in + // the parms + // indicates so. + if result.E == 0 { + result.E = 65537 + } + return &result, nil +} + +// ECDHPub is a convenience wrapper around the necessary info to perform point +// multiplication with the elliptic package. +type ECDHPub struct { + Curve elliptic.Curve + X, Y *big.Int +} + +// ECCPub converts a TPM ECC public key into one recognized by the elliptic +// package's point-multiplication functions, for use in ECDH. +func ECCPub(parms *tpms.ECCParms, pub *tpms.ECCPoint) (*ECDHPub, error) { + curve, err := parms.CurveID.Curve() + if err != nil { + return nil, err + } + return &ECDHPub{ + Curve: curve, + X: big.NewInt(0).SetBytes(pub.X.Buffer), + Y: big.NewInt(0).SetBytes(pub.Y.Buffer), + }, nil +} diff --git a/tpm2/direct/names.go b/direct/helpers/names.go similarity index 62% rename from tpm2/direct/names.go rename to direct/helpers/names.go index 7e6f3ad8..e179ab65 100644 --- a/tpm2/direct/names.go +++ b/direct/helpers/names.go @@ -1,11 +1,13 @@ -package direct +package helpers import ( "encoding/binary" + + "github.com/google/go-tpm/direct/structures/tpm" ) // PrimaryHandleName returns the TPM Name of a primary handle. -func PrimaryHandleName(h TPMHandle) []byte { +func PrimaryHandleName(h tpm.Handle) []byte { result := make([]byte, 4) binary.BigEndian.PutUint32(result, uint32(h)) return result diff --git a/direct/helpers/wrappers.go b/direct/helpers/wrappers.go new file mode 100644 index 00000000..404d350a --- /dev/null +++ b/direct/helpers/wrappers.go @@ -0,0 +1,12 @@ +package helpers + +import "github.com/google/go-tpm/direct/structures/tpm" + +// This file provides wrapper functions for concrete types used by tpm2, for +// setting union member pointers. + +// NewKeyBits allocates and returns the address of a new tpm.KeyBits. +func NewKeyBits(v tpm.KeyBits) *tpm.KeyBits { return &v } + +// NewAlgID allocates and returns the address of a new tpm.AlgID. +func NewAlgID(v tpm.AlgID) *tpm.AlgID { return &v } diff --git a/tpm2/direct/constants.go b/direct/structures/internal/constants.go similarity index 95% rename from tpm2/direct/constants.go rename to direct/structures/internal/constants.go index 0ea763b2..b777a71c 100644 --- a/tpm2/direct/constants.go +++ b/direct/structures/internal/constants.go @@ -1,19 +1,11 @@ -package direct +package internal import ( - "crypto" - "crypto/elliptic" // Register the relevant hash implementations. _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" - "fmt" -) - -// TPMGenerated values come from Part 2: Structures, section 6.2. -const ( - TPMGeneratedValue TPMGenerated = 0xff544347 ) // TPMAlgID values come from Part 2: Structures, section 6.3. @@ -59,21 +51,6 @@ const ( TPMAlgECB TPMAlgID = 0x0044 ) -// Hash returns the crypto.Hash associated with a TPMIAlgHash. -func (a TPMIAlgHash) Hash() crypto.Hash { - switch TPMAlgID(a) { - case TPMAlgSHA1: - return crypto.SHA1 - case TPMAlgSHA256: - return crypto.SHA256 - case TPMAlgSHA384: - return crypto.SHA384 - case TPMAlgSHA512: - return crypto.SHA512 - } - panic(fmt.Sprintf("unsupported hash algorithm: %v", a)) -} - // TPMECCCurve values come from Part 2: Structures, section 6.4. const ( TPMECCNone TPMECCCurve = 0x0000 @@ -87,22 +64,6 @@ const ( TPMECCSM2P256 TPMECCCurve = 0x0020 ) -// Curve returns the elliptic.Curve associated with a TPMECCCurve. -func (c TPMECCCurve) Curve() (elliptic.Curve, error) { - switch c { - case TPMECCNistP224: - return elliptic.P224(), nil - case TPMECCNistP256: - return elliptic.P256(), nil - case TPMECCNistP384: - return elliptic.P384(), nil - case TPMECCNistP521: - return elliptic.P521(), nil - default: - return nil, fmt.Errorf("unsupported ECC curve: %v", c) - } -} - // TPMCC values come from Part 2: Structures, section 6.5.2. const ( TPMCCNVUndefineSpaceSpecial TPMCC = 0x0000011F @@ -378,7 +339,7 @@ const ( TPMCapACT TPMCap = 0x0000000A ) -// TPMPTFamilyIndicator values come from Part 2: Structures, section 6.13. +// PTFamilyIndicator values come from Part 2: Structures, section 6.13. const ( // a 4-octet character string containing the TPM Family value // (TPM_SPEC_FAMILY) diff --git a/tpm2/direct/errors.go b/direct/structures/internal/errors.go similarity index 96% rename from tpm2/direct/errors.go rename to direct/structures/internal/errors.go index 6e5d74a7..4a6f153c 100644 --- a/tpm2/direct/errors.go +++ b/direct/structures/internal/errors.go @@ -1,4 +1,4 @@ -package direct +package internal import ( "fmt" @@ -162,7 +162,7 @@ var fmt1Descs = map[TPMRC]errorDesc{ description: "value is out of range or is not correct for the context", }, TPMRCHierarchy: errorDesc{ - name: "TPM_RC_HIERARCHY", + name: "TPM_RC_HIERATPMRCHY", description: "hierarchy is not enabled or is not correct for the use", }, TPMRCKeySize: errorDesc{ @@ -418,8 +418,8 @@ func (s subject) String() string { } } -// Fmt1Error represents a TPM 2.0 format-1 error, with additional information. -type Fmt1Error struct { +// TPMFmt1Error represents a TPM 2.0 format-1 error, with additional information. +type TPMFmt1Error struct { // The canonical TPM error code, with handle/parameter/session info // stripped out. canonical TPMRC @@ -430,7 +430,7 @@ type Fmt1Error struct { } // Error returns the string representation of the error. -func (e Fmt1Error) Error() string { +func (e TPMFmt1Error) Error() string { desc, ok := fmt1Descs[e.canonical] if !ok { return fmt.Sprintf("unknown format-1 error: %s %d (%x)", e.subject, e.index, uint32(e.canonical)) @@ -440,7 +440,7 @@ func (e Fmt1Error) Error() string { // Handle returns whether the error is handle-related and if so, which handle is // in error. -func (e Fmt1Error) Handle() (bool, int) { +func (e TPMFmt1Error) Handle() (bool, int) { if e.subject != handle { return false, 0 } @@ -449,7 +449,7 @@ func (e Fmt1Error) Handle() (bool, int) { // Parameter returns whether the error is handle-related and if so, which handle // is in error. -func (e Fmt1Error) Parameter() (bool, int) { +func (e TPMFmt1Error) Parameter() (bool, int) { if e.subject != parameter { return false, 0 } @@ -458,7 +458,7 @@ func (e Fmt1Error) Parameter() (bool, int) { // Session returns whether the error is handle-related and if so, which handle // is in error. -func (e Fmt1Error) Session() (bool, int) { +func (e TPMFmt1Error) Session() (bool, int) { if e.subject != session { return false, 0 } @@ -472,9 +472,9 @@ func (r TPMRC) isFmt0Error() bool { // isFmt1Error returns true and a format-1 error structure if the error is a // format-1 error. -func (r TPMRC) isFmt1Error() (bool, Fmt1Error) { +func (r TPMRC) isFmt1Error() (bool, TPMFmt1Error) { if (r & rcFmt1) != rcFmt1 { - return false, Fmt1Error{} + return false, TPMFmt1Error{} } subj := handle if (r & rcP) == rcP { @@ -486,7 +486,7 @@ func (r TPMRC) isFmt1Error() (bool, Fmt1Error) { } idx := int((r & 0xF00) >> 8) r &= 0xFFFFF0FF - return true, Fmt1Error{ + return true, TPMFmt1Error{ canonical: r, subject: subj, index: idx, @@ -530,21 +530,21 @@ func (r TPMRC) Error() string { // Is returns whether the TPMRC (which may be a FMT1 error) is equal to the // given canonical error. func (r TPMRC) Is(target error) bool { - targetRC, ok := target.(TPMRC) + targetTPMRC, ok := target.(TPMRC) if !ok { return false } if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { - return fmt1.canonical == targetRC + return fmt1.canonical == targetTPMRC } - return r == targetRC + return r == targetTPMRC } // As returns whether the error can be assigned to the given interface type. // If supported, it updates the value pointed at by target. // Supports the Fmt1Error type. func (r TPMRC) As(target interface{}) bool { - pFmt1, ok := target.(*Fmt1Error) + pFmt1, ok := target.(*TPMFmt1Error) if !ok { return false } diff --git a/tpm2/direct/structures.go b/direct/structures/internal/structures.go similarity index 97% rename from tpm2/direct/structures.go rename to direct/structures/internal/structures.go index 1d9454e3..32618ad5 100644 --- a/tpm2/direct/structures.go +++ b/direct/structures/internal/structures.go @@ -1,6 +1,16 @@ -package direct +// package internal defines all the TPM 2.0 structures together to avoid import cycles +package internal -import "fmt" +import ( + "crypto" + "crypto/elliptic" + + // Register the relevant hash implementations. + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "fmt" +) // TPMCmdHeader is the header structure in front of any TPM command. // It is described in Part 1, Architecture. @@ -51,6 +61,11 @@ type TPMKeyBits uint16 // See definition in Part 2: Structures, section 6.2. type TPMGenerated uint32 +// Generated values come from Part 2: Structures, section 6.2. +const ( + TPMGeneratedValue TPMGenerated = 0xff544347 +) + // Check verifies that a TPMGenerated value is correct, and returns an error // otherwise. func (g TPMGenerated) Check() error { @@ -68,6 +83,22 @@ type TPMAlgID uint16 // See definition in Part 2: Structures, section 6.4. type TPMECCCurve uint16 +// Curve returns the elliptic.Curve associated with a TPMECCCurve. +func (c TPMECCCurve) Curve() (elliptic.Curve, error) { + switch c { + case TPMECCNistP224: + return elliptic.P224(), nil + case TPMECCNistP256: + return elliptic.P256(), nil + case TPMECCNistP384: + return elliptic.P384(), nil + case TPMECCNistP521: + return elliptic.P521(), nil + default: + return nil, fmt.Errorf("unsupported ECC curve: %v", c) + } +} + // TPMCC represents a TPM_CC. // See definition in Part 2: Structures, section 6.5.2. type TPMCC uint32 @@ -364,6 +395,21 @@ type TPMIRHHierarchy = TPMHandle // See definition in Part 2: Structures, section 9.27. type TPMIAlgHash = TPMAlgID +// Hash returns the crypto.Hash associated with a TPMIAlgHash. +func (a TPMIAlgHash) Hash() (crypto.Hash, error) { + switch TPMAlgID(a) { + case TPMAlgSHA1: + return crypto.SHA1, nil + case TPMAlgSHA256: + return crypto.SHA256, nil + case TPMAlgSHA384: + return crypto.SHA384, nil + case TPMAlgSHA512: + return crypto.SHA512, nil + } + return crypto.SHA256, fmt.Errorf("unsupported hash algorithm: %v", a) +} + // TODO: Provide a placeholder interface here so we can explicitly enumerate // these for compile-time protection. diff --git a/direct/structures/tpm/constants.go b/direct/structures/tpm/constants.go new file mode 100644 index 00000000..d481a291 --- /dev/null +++ b/direct/structures/tpm/constants.go @@ -0,0 +1,506 @@ +package tpm + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// AlgID values come from Part 2: Structures, section 6.3. +const ( + AlgRSA = internal.TPMAlgRSA + AlgTDES = internal.TPMAlgTDES + AlgSHA = internal.TPMAlgSHA + AlgSHA1 = internal.TPMAlgSHA1 + AlgHMAC = internal.TPMAlgHMAC + AlgAES = internal.TPMAlgAES + AlgMGF1 = internal.TPMAlgMGF1 + AlgKeyedHash = internal.TPMAlgKeyedHash + AlgXOR = internal.TPMAlgXOR + AlgSHA256 = internal.TPMAlgSHA256 + AlgSHA384 = internal.TPMAlgSHA384 + AlgSHA512 = internal.TPMAlgSHA512 + AlgNull = internal.TPMAlgNull + AlgSM3256 = internal.TPMAlgSM3256 + AlgSM4 = internal.TPMAlgSM4 + AlgRSASSA = internal.TPMAlgRSASSA + AlgRSAES = internal.TPMAlgRSAES + AlgRSAPSS = internal.TPMAlgRSAPSS + AlgOAEP = internal.TPMAlgOAEP + AlgECDSA = internal.TPMAlgECDSA + AlgECDH = internal.TPMAlgECDH + AlgECDAA = internal.TPMAlgECDAA + AlgSM2 = internal.TPMAlgSM2 + AlgECSchnorr = internal.TPMAlgECSchnorr + AlgECMQV = internal.TPMAlgECMQV + AlgKDF1SP80056A = internal.TPMAlgKDF1SP80056A + AlgKDF2 = internal.TPMAlgKDF2 + AlgKDF1SP800108 = internal.TPMAlgKDF1SP800108 + AlgECC = internal.TPMAlgECC + AlgSymCipher = internal.TPMAlgSymCipher + AlgCamellia = internal.TPMAlgCamellia + AlgSHA3256 = internal.TPMAlgSHA3256 + AlgSHA3384 = internal.TPMAlgSHA3384 + AlgSHA3512 = internal.TPMAlgSHA3512 + AlgCTR = internal.TPMAlgCTR + AlgOFB = internal.TPMAlgOFB + AlgCBC = internal.TPMAlgCBC + AlgCFB = internal.TPMAlgCFB + AlgECB = internal.TPMAlgECB +) + +// ECCCurve = internal.TPMECCCurve +const ( + ECCNone = internal.TPMECCNone + ECCNistP192 = internal.TPMECCNistP192 + ECCNistP224 = internal.TPMECCNistP224 + ECCNistP256 = internal.TPMECCNistP256 + ECCNistP384 = internal.TPMECCNistP384 + ECCNistP521 = internal.TPMECCNistP521 + ECCBNP256 = internal.TPMECCBNP256 + ECCBNP638 = internal.TPMECCBNP638 + ECCSM2P256 = internal.TPMECCSM2P256 +) + +// CC = internal.TPMCC +const ( + CCNVUndefineSpaceSpecial = internal.TPMCCNVUndefineSpaceSpecial + CCEvictControl = internal.TPMCCEvictControl + CCHierarchyControl = internal.TPMCCHierarchyControl + CCNVUndefineSpace = internal.TPMCCNVUndefineSpace + CCChangeEPS = internal.TPMCCChangeEPS + CCChangePPS = internal.TPMCCChangePPS + CCClear = internal.TPMCCClear + CCClearControl = internal.TPMCCClearControl + CCClockSet = internal.TPMCCClockSet + CCHierarchyChanegAuth = internal.TPMCCHierarchyChanegAuth + CCNVDefineSpace = internal.TPMCCNVDefineSpace + CCPCRAllocate = internal.TPMCCPCRAllocate + CCPCRSetAuthPolicy = internal.TPMCCPCRSetAuthPolicy + CCPPCommands = internal.TPMCCPPCommands + CCSetPrimaryPolicy = internal.TPMCCSetPrimaryPolicy + CCFieldUpgradeStart = internal.TPMCCFieldUpgradeStart + CCClockRateAdjust = internal.TPMCCClockRateAdjust + CCCreatePrimary = internal.TPMCCCreatePrimary + CCNVGlobalWriteLock = internal.TPMCCNVGlobalWriteLock + CCGetCommandAuditDigest = internal.TPMCCGetCommandAuditDigest + CCNVIncrement = internal.TPMCCNVIncrement + CCNVSetBits = internal.TPMCCNVSetBits + CCNVExtend = internal.TPMCCNVExtend + CCNVWrite = internal.TPMCCNVWrite + CCNVWriteLock = internal.TPMCCNVWriteLock + CCDictionaryAttackLockReset = internal.TPMCCDictionaryAttackLockReset + CCDictionaryAttackParameters = internal.TPMCCDictionaryAttackParameters + CCNVChangeAuth = internal.TPMCCNVChangeAuth + CCPCREvent = internal.TPMCCPCREvent + CCPCRReset = internal.TPMCCPCRReset + CCSequenceComplete = internal.TPMCCSequenceComplete + CCSetAlgorithmSet = internal.TPMCCSetAlgorithmSet + CCSetCommandCodeAuditStatus = internal.TPMCCSetCommandCodeAuditStatus + CCFieldUpgradeData = internal.TPMCCFieldUpgradeData + CCIncrementalSelfTest = internal.TPMCCIncrementalSelfTest + CCSelfTest = internal.TPMCCSelfTest + CCStartup = internal.TPMCCStartup + CCShutdown = internal.TPMCCShutdown + CCStirRandom = internal.TPMCCStirRandom + CCActivateCredential = internal.TPMCCActivateCredential + CCCertify = internal.TPMCCCertify + CCPolicyNV = internal.TPMCCPolicyNV + CCCertifyCreation = internal.TPMCCCertifyCreation + CCDuplicate = internal.TPMCCDuplicate + CCGetTime = internal.TPMCCGetTime + CCGetSessionAuditDigest = internal.TPMCCGetSessionAuditDigest + CCNVRead = internal.TPMCCNVRead + CCNVReadLock = internal.TPMCCNVReadLock + CCObjectChangeAuth = internal.TPMCCObjectChangeAuth + CCPolicySecret = internal.TPMCCPolicySecret + CCRewrap = internal.TPMCCRewrap + CCCreate = internal.TPMCCCreate + CCECDHZGen = internal.TPMCCECDHZGen + CCHMAC = internal.TPMCCHMAC + CCMAC = internal.TPMCCMAC + CCImport = internal.TPMCCImport + CCLoad = internal.TPMCCLoad + CCQuote = internal.TPMCCQuote + CCRSADecrypt = internal.TPMCCRSADecrypt + CCHMACStart = internal.TPMCCHMACStart + CCMACStart = internal.TPMCCMACStart + CCSequenceUpdate = internal.TPMCCSequenceUpdate + CCSign = internal.TPMCCSign + CCUnseal = internal.TPMCCUnseal + CCPolicySigned = internal.TPMCCPolicySigned + CCContextLoad = internal.TPMCCContextLoad + CCContextSave = internal.TPMCCContextSave + CCECDHKeyGen = internal.TPMCCECDHKeyGen + CCEncryptDecrypt = internal.TPMCCEncryptDecrypt + CCFlushContext = internal.TPMCCFlushContext + CCLoadExternal = internal.TPMCCLoadExternal + CCMakeCredential = internal.TPMCCMakeCredential + CCNVReadPublic = internal.TPMCCNVReadPublic + CCPolicyAuthorize = internal.TPMCCPolicyAuthorize + CCPolicyAuthValue = internal.TPMCCPolicyAuthValue + CCPolicyCommandCode = internal.TPMCCPolicyCommandCode + CCPolicyCounterTimer = internal.TPMCCPolicyCounterTimer + CCPolicyCpHash = internal.TPMCCPolicyCpHash + CCPolicyLocality = internal.TPMCCPolicyLocality + CCPolicyNameHash = internal.TPMCCPolicyNameHash + CCPolicyOR = internal.TPMCCPolicyOR + CCPolicyTicket = internal.TPMCCPolicyTicket + CCReadPublic = internal.TPMCCReadPublic + CCRSAEncrypt = internal.TPMCCRSAEncrypt + CCStartAuthSession = internal.TPMCCStartAuthSession + CCVerifySignature = internal.TPMCCVerifySignature + CCECCParameters = internal.TPMCCECCParameters + CCFirmwareRead = internal.TPMCCFirmwareRead + CCGetCapability = internal.TPMCCGetCapability + CCGetRandom = internal.TPMCCGetRandom + CCGetTestResult = internal.TPMCCGetTestResult + CCHash = internal.TPMCCHash + CCPCRRead = internal.TPMCCPCRRead + CCPolicyPCR = internal.TPMCCPolicyPCR + CCPolicyRestart = internal.TPMCCPolicyRestart + CCReadClock = internal.TPMCCReadClock + CCPCRExtend = internal.TPMCCPCRExtend + CCPCRSetAuthValue = internal.TPMCCPCRSetAuthValue + CCNVCertify = internal.TPMCCNVCertify + CCEventSequenceComplete = internal.TPMCCEventSequenceComplete + CCHashSequenceStart = internal.TPMCCHashSequenceStart + CCPolicyPhysicalPresence = internal.TPMCCPolicyPhysicalPresence + CCPolicyDuplicationSelect = internal.TPMCCPolicyDuplicationSelect + CCPolicyGetDigest = internal.TPMCCPolicyGetDigest + CCTestParams = internal.TPMCCTestParams + CCCommit = internal.TPMCCCommit + CCPolicyPassword = internal.TPMCCPolicyPassword + CCZGen2Phase = internal.TPMCCZGen2Phase + CCECEphemeral = internal.TPMCCECEphemeral + CCPolicyNvWritten = internal.TPMCCPolicyNvWritten + CCPolicyTemplate = internal.TPMCCPolicyTemplate + CCCreateLoaded = internal.TPMCCCreateLoaded + CCPolicyAuthorizeNV = internal.TPMCCPolicyAuthorizeNV + CCEncryptDecrypt2 = internal.TPMCCEncryptDecrypt2 + CCACGetCapability = internal.TPMCCACGetCapability + CCACSend = internal.TPMCCACSend + CCPolicyACSendSelect = internal.TPMCCPolicyACSendSelect + CCCertifyX509 = internal.TPMCCCertifyX509 + CCACTSetTimeout = internal.TPMCCACTSetTimeout +) + +// RC = internal.TPMRC +const ( + RCSuccess = internal.TPMRCSuccess + // FMT0 error codes + RCInitialize = internal.TPMRCInitialize + RCFailure = internal.TPMRCFailure + RCSequence = internal.TPMRCSequence + RCPrivate = internal.TPMRCPrivate + RCHMAC = internal.TPMRCHMAC + RCDisabled = internal.TPMRCDisabled + RCExclusive = internal.TPMRCExclusive + RCAuthType = internal.TPMRCAuthType + RCAuthMissing = internal.TPMRCAuthMissing + RCPolicy = internal.TPMRCPolicy + RCPCR = internal.TPMRCPCR + RCPCRChanged = internal.TPMRCPCRChanged + RCUpgrade = internal.TPMRCUpgrade + RCTooManyContexts = internal.TPMRCTooManyContexts + RCAuthUnavailable = internal.TPMRCAuthUnavailable + RCReboot = internal.TPMRCReboot + RCUnbalanced = internal.TPMRCUnbalanced + RCCommandSize = internal.TPMRCCommandSize + RCCommandCode = internal.TPMRCCommandCode + RCAuthSize = internal.TPMRCAuthSize + RCAuthContext = internal.TPMRCAuthContext + RCNVRange = internal.TPMRCNVRange + RCNVSize = internal.TPMRCNVSize + RCNVLocked = internal.TPMRCNVLocked + RCNVAuthorization = internal.TPMRCNVAuthorization + RCNVUninitialized = internal.TPMRCNVUninitialized + RCNVSpace = internal.TPMRCNVSpace + RCNVDefined = internal.TPMRCNVDefined + RCBadContext = internal.TPMRCBadContext + RCCPHash = internal.TPMRCCPHash + RCParent = internal.TPMRCParent + RCNeedsTest = internal.TPMRCNeedsTest + RCNoResult = internal.TPMRCNoResult + RCSensitive = internal.TPMRCSensitive + // FMT1 error codes + RCAsymmetric = internal.TPMRCAsymmetric + RCAttributes = internal.TPMRCAttributes + RCHash = internal.TPMRCHash + RCValue = internal.TPMRCValue + RCHierarchy = internal.TPMRCHierarchy + RCKeySize = internal.TPMRCKeySize + RCMGF = internal.TPMRCMGF + RCMode = internal.TPMRCMode + RCType = internal.TPMRCType + RCHandle = internal.TPMRCHandle + RCKDF = internal.TPMRCKDF + RCRange = internal.TPMRCRange + RCAuthFail = internal.TPMRCAuthFail + RCNonce = internal.TPMRCNonce + RCPP = internal.TPMRCPP + RCScheme = internal.TPMRCScheme + RCSize = internal.TPMRCSize + RCSymmetric = internal.TPMRCSymmetric + RCTag = internal.TPMRCTag + RCSelector = internal.TPMRCSelector + RCInsufficient = internal.TPMRCInsufficient + RCSignature = internal.TPMRCSignature + RCKey = internal.TPMRCKey + RCPolicyFail = internal.TPMRCPolicyFail + RCIntegrity = internal.TPMRCIntegrity + RCTicket = internal.TPMRCTicket + RCReservedBits = internal.TPMRCReservedBits + RCBadAuth = internal.TPMRCBadAuth + RCExpired = internal.TPMRCExpired + RCPolicyCC = internal.TPMRCPolicyCC + RCBinding = internal.TPMRCBinding + RCCurve = internal.TPMRCCurve + RCECCPoint = internal.TPMRCECCPoint + // Warnings + RCContextGap = internal.TPMRCContextGap + RCObjectMemory = internal.TPMRCObjectMemory + RCSessionMemory = internal.TPMRCSessionMemory + RCMemory = internal.TPMRCMemory + RCSessionHandles = internal.TPMRCSessionHandles + RCObjectHandles = internal.TPMRCObjectHandles + RCLocality = internal.TPMRCLocality + RCYielded = internal.TPMRCYielded + RCCanceled = internal.TPMRCCanceled + RCTesting = internal.TPMRCTesting + RCReferenceH0 = internal.TPMRCReferenceH0 + RCReferenceH1 = internal.TPMRCReferenceH1 + RCReferenceH2 = internal.TPMRCReferenceH2 + RCReferenceH3 = internal.TPMRCReferenceH3 + RCReferenceH4 = internal.TPMRCReferenceH4 + RCReferenceH5 = internal.TPMRCReferenceH5 + RCReferenceH6 = internal.TPMRCReferenceH6 + RCReferenceS0 = internal.TPMRCReferenceS0 + RCReferenceS1 = internal.TPMRCReferenceS1 + RCReferenceS2 = internal.TPMRCReferenceS2 + RCReferenceS3 = internal.TPMRCReferenceS3 + RCReferenceS4 = internal.TPMRCReferenceS4 + RCReferenceS5 = internal.TPMRCReferenceS5 + RCReferenceS6 = internal.TPMRCReferenceS6 + RCNVRate = internal.TPMRCNVRate + RCLockout = internal.TPMRCLockout + RCRetry = internal.TPMRCRetry + RCNVUnavailable = internal.TPMRCNVUnavailable +) + +// ST = internal.TPMST +const ( + STRspCommand = internal.TPMSTRspCommand + STNull = internal.TPMSTNull + STNoSessions = internal.TPMSTNoSessions + STSessions = internal.TPMSTSessions + STAttestNV = internal.TPMSTAttestNV + STAttestCommandAudit = internal.TPMSTAttestCommandAudit + STAttestSessionAudit = internal.TPMSTAttestSessionAudit + STAttestCertify = internal.TPMSTAttestCertify + STAttestQuote = internal.TPMSTAttestQuote + STAttestTime = internal.TPMSTAttestTime + STAttestCreation = internal.TPMSTAttestCreation + STAttestNVDigest = internal.TPMSTAttestNVDigest + STCreation = internal.TPMSTCreation + STVerified = internal.TPMSTVerified + STAuthSecret = internal.TPMSTAuthSecret + STHashCheck = internal.TPMSTHashCheck + STAuthSigned = internal.TPMSTAuthSigned + STFuManifest = internal.TPMSTFuManifest +) + +// SE = internal.TPMSE +const ( + SEHMAC = internal.TPMSEHMAC + SEPolicy = internal.TPMSEPolicy + XETrial = internal.TPMXETrial +) + +// Cap = internal.TPMCap +const ( + CapAlgs = internal.TPMCapAlgs + CapHandles = internal.TPMCapHandles + CapCommands = internal.TPMCapCommands + CapPPCommands = internal.TPMCapPPCommands + CapAuditCommands = internal.TPMCapAuditCommands + CapPCRs = internal.TPMCapPCRs + CapTPMProperties = internal.TPMCapTPMProperties + CapPCRProperties = internal.TPMCapPCRProperties + CapECCCurves = internal.TPMCapECCCurves + CapAuthPolicies = internal.TPMCapAuthPolicies + CapACT = internal.TPMCapACT +) + +// PTFamilyIndicator values come from Part 2: Structures, section 6.13. +const ( + // a 4-octet character string containing the = internal.TPM + // (_SPEC_FAMILY= internal.TPM_SPEC_FAMILY + PTFamilyIndicator = internal.TPMPTFamilyIndicator + // the level of the specification + PTLevel = internal.TPMPTLevel + // the specification Revision times 100 + PTRevision = internal.TPMPTRevision + // the specification day of year using TCG calendar + PTDayofYear = internal.TPMPTDayofYear + // the specification year using the CE + PTYear = internal.TPMPTYear + // the vendor ID unique to each = internal.TPM + PTManufacturer = internal.TPMPTManufacturer + // the first four characters of the vendor ID string + PTVendorString1 = internal.TPMPTVendorString1 + // the second four characters of the vendor ID string + PTVendorString2 = internal.TPMPTVendorString2 + // the third four characters of the vendor ID string + PTVendorString3 = internal.TPMPTVendorString3 + // the fourth four characters of the vendor ID sting + PTVendorString4 = internal.TPMPTVendorString4 + // vendor-defined value indicating the = internal.TPM + PTVendorTPMType = internal.TPMPTVendorTPMType + // the most-significant 32 bits of a = internal.TPM + // indicating the version number of the firmware. + PTFirmwareVersion1 = internal.TPMPTFirmwareVersion1 + // the least-significant 32 bits of a = internal.TPM + // indicating the version number of the firmware. + // the maximum value for commandSize in a command + PTMaxCommandSize = internal.TPMPTMaxCommandSize + // the maximum value for responseSize in a response + PTMaxResponseSize = internal.TPMPTMaxResponseSize + // the maximum size of a digest that can be produced by the TPM + PTMaxDigest = internal.TPMPTMaxDigest + // the maximum size of an object context that will be returned by + // TPM2_ContextSave + PTMaxObjectContext = internal.TPMPTMaxObjectContext + // the maximum size of a session context that will be returned by + // TPM2_ContextSave + PTMaxSessionContext = internal.TPMPTMaxSessionContext + // platform-specific family (a TPM_PS value)(see Table 25) + PTPSFamilyIndicator = internal.TPMPTPSFamilyIndicator + // the number of split signing operations supported by the TPM + PTSplitMax = internal.TPMPTSplitMax + // total number of commands implemented in the TPM + PTTotalCommands = internal.TPMPTTotalCommands + // number of commands from the TPM library that are implemented + PTLibraryCommands = internal.TPMPTLibraryCommands + // number of vendor commands that are implemented + PTVendorCommands = internal.TPMPTVendorCommands + // the maximum data size in one NV write, NV read, NV extend, or NV + // certify command + PTNVBufferMax = internal.TPMPTNVBufferMax + // a TPMA_MODES value, indicating that the TPM is designed for these + // modes. + PTModes = internal.TPMPTModes + // the maximum size of a TPMS_CAPABILITY_DATA structure returned in + // TPM2_GetCapability(). + PTMaxCapBuffer = internal.TPMPTMaxCapBuffer + // TPMA_PERMANENT + PTPermanent = internal.TPMPTPermanent + // TPMA_STARTUP_CLEAR + PTStartupClear = internal.TPMPTStartupClear + // the number of NV Indexes currently defined + PTHRNVIndex = internal.TPMPTHRNVIndex + // the number of authorization sessions currently loaded into TPM RAM + PTHRLoaded = internal.TPMPTHRLoaded + // the number of additional authorization sessions, of any type, that + // could be loaded into TPM RAM + PTHRLoadedAvail = internal.TPMPTHRLoadedAvail + // the number of active authorization sessions currently being tracked + // by the TPM + PTHRActive = internal.TPMPTHRActive + // the number of additional authorization sessions, of any type, that + // could be created + PTHRActiveAvail = internal.TPMPTHRActiveAvail + // estimate of the number of additional transient objects that could be + // loaded into TPM RAM + PTHRTransientAvail = internal.TPMPTHRTransientAvail + // the number of persistent objects currently loaded into TPM NV memory + PTHRPersistent = internal.TPMPTHRPersistent + // the number of additional persistent objects that could be loaded into + // NV memory + PTHRPersistentAvail = internal.TPMPTHRPersistentAvail + // the number of defined NV Indexes that have NV the TPM_NT_COUNTER + // attribute + PTNVCounters = internal.TPMPTNVCounters + // the number of additional NV Indexes that can be defined with their + // TPM_NT of TPM_NV_COUNTER and the TPMA_NV_ORDERLY attribute SET + PTNVCountersAvail = internal.TPMPTNVCountersAvail + // code that limits the algorithms that may be used with the TPM + PTAlgorithmSet = internal.TPMPTAlgorithmSet + // the number of loaded ECC curves + PTLoadedCurves = internal.TPMPTLoadedCurves + // the current value of the lockout counter (failedTries) + PTLockoutCounter = internal.TPMPTLockoutCounter + // the number of authorization failures before DA lockout is invoked + PTMaxAuthFail = internal.TPMPTMaxAuthFail + // the number of seconds before the value reported by + // TPM_PT_LOCKOUT_COUNTER is decremented + PTLockoutInterval = internal.TPMPTLockoutInterval + // the number of seconds after a lockoutAuth failure before use of + // lockoutAuth may be attempted again + PTLockoutRecovery = internal.TPMPTLockoutRecovery + // number of milliseconds before the TPM will accept another command + // that will modify NV + PTNVWriteRecovery = internal.TPMPTNVWriteRecovery + // the high-order 32 bits of the command audit counter + PTAuditCounter0 = internal.TPMPTAuditCounter0 + // the low-order 32 bits of the command audit counter + PTAuditCounter1 = internal.TPMPTAuditCounter1 +) + +// TPMPTPCR values come from Part 2: Structures, section 6.14. +const ( + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is saved and + // restored by TPM_SU_STATE + PTPCRSave = internal.TPMPTPCRSave + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 0 + PTPCRExtendL0 = internal.TPMPTPCRExtendL0 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 0 + PTPCRResetL0 = internal.TPMPTPCRResetL0 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 1 + PTPCRExtendL1 = internal.TPMPTPCRExtendL1 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 1 + PTPCRResetL1 = internal.TPMPTPCRResetL1 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 2 + PTPCRExtendL2 = internal.TPMPTPCRExtendL2 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 2 + PTPCRResetL2 = internal.TPMPTPCRResetL2 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 3 + PTPCRExtendL3 = internal.TPMPTPCRExtendL3 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 3 + PTPCRResetL3 = internal.TPMPTPCRResetL3 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be + // extended from locality 4 + PTPCRExtendL4 = internal.TPMPTPCRExtendL4 + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR may be reset + // by TPM2_PCR_Reset() from locality 4 + PTPCRResetL4 = internal.TPMPTPCRResetL4 + // a SET bit in the TPMS_PCR_SELECT indicates that modifications to this + // PCR (reset or Extend) will not increment the pcrUpdateCounter + PTPCRNoIncrement = internal.TPMPTPCRNoIncrement + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is reset by a + // D-RTM event + PTPCRDRTMRest = internal.TPMPTPCRDRTMRest + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is controlled + // by policy + PTPCRPolicy = internal.TPMPTPCRPolicy + // a SET bit in the TPMS_PCR_SELECT indicates that the PCR is controlled + // by an authorization value + PTPCRAuth = internal.TPMPTPCRAuth +) + +// TPMHandle values come from Part 2: Structures, section 7.4. +const ( + RHOwner = internal.TPMRHOwner + RHNull = internal.TPMRHNull + RSPW = internal.TPMRSPW + RHLockout = internal.TPMRHLockout + RHEndorsement = internal.TPMRHEndorsement + RHPlatform = internal.TPMRHPlatform + RHPlatformNV = internal.TPMRHPlatformNV +) diff --git a/direct/structures/tpm/tpm.go b/direct/structures/tpm/tpm.go new file mode 100644 index 00000000..b54c6419 --- /dev/null +++ b/direct/structures/tpm/tpm.go @@ -0,0 +1,90 @@ +// package tpm contains the TPM 2.0 structures prefixed with "TPM_" +package tpm + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// CmdHeader is the header structure in front of any TPM command. +// It is described in Part 1, Architecture. +type CmdHeader = internal.TPMCmdHeader + +// RspHeader is the header structure in front of any TPM response. +// It is described in Part 1, Architecture. +type RspHeader = internal.TPMRspHeader + +// AlgorithmID represents a TPM_ALGORITHM_ID +// this is the 1.2 compatible form of the TPM_ALG_ID +// See definition in Part 2, Structures, section 5.3. +type AlgorithmID = internal.TPMAlgorithmID + +// ModifierIndicator represents a TPM_MODIFIER_INDICATOR. +// See definition in Part 2, Structures, section 5.3. +type ModifierIndicator = internal.TPMModifierIndicator + +// AuthorizationSize represents a TPM_AUTHORIZATION_SIZE. +// the authorizationSize parameter in a command +// See definition in Part 2, Structures, section 5.3. +type AuthorizationSize = internal.TPMAuthorizationSize + +// ParameterSize represents a TPM_PARAMETER_SIZE. +// the parameterSize parameter in a command +// See definition in Part 2, Structures, section 5.3. +type ParameterSize = internal.TPMParameterSize + +// KeySize represents a TPM_KEY_SIZE. +// a key size in octets +// See definition in Part 2, Structures, section 5.3. +type KeySize = internal.TPMKeySize + +// KeyBits represents a TPM_KEY_BITS. +// a key size in bits +// See definition in Part 2, Structures, section 5.3. +type KeyBits = internal.TPMKeyBits + +// Generated represents a TPM_GENERATED. +// See definition in Part 2: Structures, section 6.2. +type Generated = internal.TPMGenerated + +// AlgID represents a TPM_ALG_ID. +// See definition in Part 2: Structures, section 6.3. +type AlgID = internal.TPMAlgID + +// ECCCurve represents a TPM_ECC_Curve. +// See definition in Part 2: Structures, section 6.4. +type ECCCurve = internal.TPMECCCurve + +// CC represents a TPM_CC. +// See definition in Part 2: Structures, section 6.5.2. +type CC = internal.TPMCC + +// RC represents a TPM_RC. +// See definition in Part 2: Structures, section 6.6. +type RC = internal.TPMRC + +// Fmt1Error represents a TPM 2.0 format-1 error, with additional information. +type Fmt1Error = internal.TPMFmt1Error + +// ST represents a TPM_ST. +// See definition in Part 2: Structures, section 6.9. +type ST = internal.TPMST + +// SE represents a TPM_SE. +// See definition in Part 2: Structures, section 6.11. +type SE = internal.TPMSE + +// Cap represents a TPM_CAP. +// See definition in Part 2: Structures, section 6.12. +type Cap = internal.TPMCap + +// PT represents a TPM_PT. +// See definition in Part 2: Structures, section 6.13. +type PT = internal.TPMPT + +// PTPCR represents a TPM_PT_PCR. +// See definition in Part 2: Structures, section 6.14. +type PTPCR = internal.TPMPTPCR + +// Handle represents a TPM_HANDLE. +// See definition in Part 2: Structures, section 7.1. +type Handle = internal.TPMHandle diff --git a/direct/structures/tpm2b/tpm2b.go b/direct/structures/tpm2b/tpm2b.go new file mode 100644 index 00000000..97057315 --- /dev/null +++ b/direct/structures/tpm2b/tpm2b.go @@ -0,0 +1,75 @@ +// package tpm2b contains the TPM 2.0 structures prefixed with "TPM2B_" +package tpm2b + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// Digest represents a TPM2B_DIGEST. +// See definition in Part 2: Structures, section 10.4.2. +type Digest = internal.TPM2BDigest + +// Data represents a TPM2B_DATA. +// See definition in Part 2: Structures, section 10.4.3. +type Data = internal.TPM2BData + +// Nonce represents a TPM2B_NONCE. +// See definition in Part 2: Structures, section 10.4.4. +type Nonce = internal.TPM2BNonce + +// Event represents a TPM2B_EVENT. +// See definition in Part 2: Structures, section 10.4.7. +type Event = internal.TPM2BEvent + +// Timeout represents a TPM2B_TIMEOUT. +// See definition in Part 2: Structures, section 10.4.10. +type Timeout = internal.TPM2BTimeout + +// Auth represents a TPM2B_AUTH. +// See definition in Part 2: Structures, section 10.4.5. +type Auth = internal.TPM2BAuth + +// Name represents a TPM2B_NAME. +// See definition in Part 2: Structures, section 10.5.3. +// NOTE: This structure does not contain a TPMUName, because that union +// is not tagged with a selector. Instead, TPM2B_Name is flattened and +// all TPMDirect helpers that deal with names will deal with them as so. +type Name = internal.TPM2BName + +// Attest represents a TPM2B_ATTEST. +// See definition in Part 2: Structures, section 10.12.13. +// Note that in the spec, this is just a 2B_DATA with enough room for an S_ATTEST. +// For ergonomics, pretend that TPM2B_Attest wraps a TPMS_Attest just like other 2Bs. +type Attest = internal.TPM2BAttest + +// SensitiveData represents a TPM2B_SENSITIVE_DATA. +// See definition in Part 2: Structures, section 11.1.14. +type SensitiveData = internal.TPM2BSensitiveData + +// SensitiveCreate represents a TPM2B_SENSITIVE_CREATE. +// See definition in Part 2: Structures, section 11.1.16. +type SensitiveCreate = internal.TPM2BSensitiveCreate + +// PublicKeyRSA represents a TPM2B_PUBLIC_KEY_RSA. +// See definition in Part 2: Structures, section 11.2.4.5. +type PublicKeyRSA = internal.TPM2BPublicKeyRSA + +// ECCParameter represents a TPM2B_ECC_PARAMETER. +// See definition in Part 2: Structures, section 11.2.5.1. +type ECCParameter = internal.TPM2BECCParameter + +// EncryptedSecret represents a TPM2B_ENCRYPTED_SECRET. +// See definition in Part 2: Structures, section 11.4.33. +type EncryptedSecret = internal.TPM2BEncryptedSecret + +// Public represents a TPM2B_PUBLIC. +// See definition in Part 2: Structures, section 12.2.5. +type Public = internal.TPM2BPublic + +// Private represents a TPM2B_PRIVATE. +// See definition in Part 2: Structures, section 12.3.7. +type Private = internal.TPM2BPrivate + +// CreationData represents a TPM2B_CREATION_DATA. +// See definition in Part 2: Structures, section 15.2. +type CreationData = internal.TPM2BCreationData diff --git a/direct/structures/tpma/tpma.go b/direct/structures/tpma/tpma.go new file mode 100644 index 00000000..4f0cea0c --- /dev/null +++ b/direct/structures/tpma/tpma.go @@ -0,0 +1,30 @@ +// package tpma contains the TPM 2.0 structures prefixed by "TPMA_" +package tpma + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// Algorithm represents a TPMA_ALGORITHM. +// See definition in Part 2: Structures, section 8.2. +type Algorithm = internal.TPMAAlgorithm + +// Object represents a TPMA_OBJECT. +// See definition in Part 2: Structures, section 8.3.2. +type Object = internal.TPMAObject + +// Session represents a TPMA_SESSION. +// See definition in Part 2: Structures, section 8.4. +type Session = internal.TPMASession + +// Locality represents a TPMA_LOCALITY. +// See definition in Part 2: Structures, section 8.5. +type Locality = internal.TPMALocality + +// CC represents a TPMA_CC. +// See definition in Part 2: Structures, section 8.9. +type CC = internal.TPMACC + +// ACT represents a TPMA_ACT. +// See definition in Part 2: Structures, section 8.12. +type ACT = internal.TPMAACT diff --git a/direct/structures/tpmi/tpmi.go b/direct/structures/tpmi/tpmi.go new file mode 100644 index 00000000..5b763e5e --- /dev/null +++ b/direct/structures/tpmi/tpmi.go @@ -0,0 +1,95 @@ +// package tpmi contains the TPM 2.0 structures prefixed by "TPMI_" +package tpmi + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// YesNo represents a TPMI_YES_NO. +// See definition in Part 2: Structures, section 9.2. +// Use native bool for TPMI_YES_NO; encoding/binary already treats this as 8 bits wide. +type YesNo = internal.TPMIYesNo + +// DHObject represents a TPMI_DH_OBJECT. +// See definition in Part 2: Structures, section 9.3. +type DHObject = internal.TPMIDHObject + +// DHEntity represents a TPMI_DH_ENTITY. +// See definition in Part 2: Structures, section 9.6. +type DHEntity = internal.TPMIDHEntity + +// SHAuthSession represents a TPMI_SH_AUTH_SESSION. +// See definition in Part 2: Structures, section 9.8. +type SHAuthSession = internal.TPMISHAuthSession + +// SHHMAC represents a TPMI_SH_HMAC. +// See definition in Part 2: Structures, section 9.9. +type SHHMAC = internal.TPMISHHMAC + +// SHPolicy represents a TPMI_SH_POLICY. +// See definition in Part 2: Structures, section 9.10. +type SHPolicy = internal.TPMISHPolicy + +// DHContext represents a TPMI_DH_CONTEXT. +// See definition in Part 2: Structures, section 9.11. +type DHContext = internal.TPMIDHContext + +// RHHierarchy represents a TPMI_RH_HIERARCHY. +// See definition in Part 2: Structures, section 9.13. +type RHHierarchy = internal.TPMIRHHierarchy + +// AlgHash represents a TPMI_ALG_HASH. +// See definition in Part 2: Structures, section 9.27. +type AlgHash = internal.TPMIAlgHash + +// AlgSym represents a TPMI_ALG_SYM. +// See definition in Part 2: Structures, section 9.29. +type AlgSym = internal.TPMIAlgSym + +// AlgSymObject represents a TPMI_ALG_SYM_OBJECT. +// See definition in Part 2: Structures, section 9.30. +type AlgSymObject = internal.TPMIAlgSymObject + +// AlgSymMode represents a TPMI_ALG_SYM_MODE. +// See definition in Part 2: Structures, section 9.31. +type AlgSymMode = internal.TPMIAlgSymMode + +// AlgKDF represents a TPMI_ALG_KDF. +// See definition in Part 2: Structures, section 9.32. +type AlgKDF = internal.TPMIAlgKDF + +// AlgSigScheme represents a TPMI_ALG_SIG_SCHEME. +// See definition in Part 2: Structures, section 9.33. +type AlgSigScheme = internal.TPMIAlgSigScheme + +// STCommandTag represents a TPMI_ST_COMMAND_TAG. +// See definition in Part 2: Structures, section 9.35. +type STCommandTag = internal.TPMISTCommandTag + +// STAttest represents a TPMI_ST_ATTEST. +// See definition in Part 2: Structures, section 10.12.10. +type STAttest = internal.TPMISTAttest + +// AlgKeyedHashScheme represents a TPMI_ALG_KEYEDHASH_SCHEME. +// See definition in Part 2: Structures, section 11.1.10. +type AlgKeyedHashScheme = internal.TPMIAlgKeyedHashScheme + +// AlgRSAScheme represents a TPMI_ALG_RSA_SCHEME. +// See definition in Part 2: Structures, section 11.2.4.1. +type AlgRSAScheme = internal.TPMIAlgRSAScheme + +// RSAKeyBits represents a TPMI_RSA_KEY_BITS. +// See definition in Part 2: Structures, section 11.2.4.6. +type RSAKeyBits = internal.TPMIRSAKeyBits + +// AlgECCScheme represents a TPMI_ALG_ECC_SCHEME. +// See definition in Part 2: Structures, section 11.2.5.4. +type AlgECCScheme = internal.TPMIAlgECCScheme + +// ECCCurve represents a TPMI_ECC_CURVE. +// See definition in Part 2: Structures, section 11.2.5.5. +type ECCCurve = internal.TPMIECCCurve + +// AlgPublic represents a TPMI_ALG_PUBLIC. +// See definition in Part 2: Structures, section 12.2.2. +type AlgPublic = internal.TPMIAlgPublic diff --git a/direct/structures/tpml/tpml.go b/direct/structures/tpml/tpml.go new file mode 100644 index 00000000..4a19a01b --- /dev/null +++ b/direct/structures/tpml/tpml.go @@ -0,0 +1,58 @@ +// package tpml contains TPM 2.0 structures prefixed with "TPML_" +package tpml + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +// CC represents a TPML_CC. +// See definition in Part 2: Structures, section 10.9.1. +type CC = internal.TPMLCC + +// CCA represents a TPML_CCA. +// See definition in Part 2: Structures, section 10.9.2. +type CCA = internal.TPMLCCA + +// Alg represents a TPMLALG. +// See definition in Part 2: Structures, section 10.9.3. +type Alg = internal.TPMLAlg + +// Handle represents a TPML_HANDLE. +// See definition in Part 2: Structures, section 10.9.4. +type Handle = internal.TPMLHandle + +// Digest represents a TPML_DIGEST. +// See definition in Part 2: Structures, section 10.9.5. +type Digest = internal.TPMLDigest + +// DigestValues represents a TPML_DIGEST_VALUES. +// See definition in Part 2: Structures, section 10.9.6. +type DigestValues = internal.TPMLDigestValues + +// PCRSelection represents a TPML_PCRzSELECTION. +// See definition in Part 2: Structures, section 10.9.7. +type PCRSelection = internal.TPMLPCRSelection + +// AlgProperty represents a TPML_ALGzPROPERTY. +// See definition in Part 2: Structures, section 10.9.8. +type AlgProperty = internal.TPMLAlgProperty + +// TaggedTPMProperty represents a TPML_TAGGED_TPM_PROPERTY. +// See definition in Part 2: Structures, section 10.9.9. +type TaggedTPMProperty = internal.TPMLTaggedTPMProperty + +// TaggedPCRProperty represents a TPML_TAGGED_PCR_PROPERTY. +// See definition in Part 2: Structures, section 10.9.10. +type TaggedPCRProperty = internal.TPMLTaggedPCRProperty + +// ECCCurve represents a TPML_ECC_CURVE. +// See definition in Part 2: Structures, section 10.9.11. +type ECCCurve = internal.TPMLECCCurve + +// TaggedPolicy represents a TPML_TAGGED_POLICY. +// See definition in Part 2: Structures, section 10.9.12. +type TaggedPolicy = internal.TPMLTaggedPolicy + +// ACTData represents a TPML_ACT_DATA. +// See definition in Part 2: Structures, section 10.9.13. +type ACTData = internal.TPMLACTData diff --git a/direct/structures/tpms/tpms.go b/direct/structures/tpms/tpms.go new file mode 100644 index 00000000..1049b1ec --- /dev/null +++ b/direct/structures/tpms/tpms.go @@ -0,0 +1,180 @@ +// package tpms contains the TPM 2.0 structures prefixed by "TPMS_" +package tpms + +import "github.com/google/go-tpm/direct/structures/internal" + +// Empty represents a TPMS_EMPTY. +// See definition in Part 2: Structures, section 10.1. +type Empty = internal.TPMSEmpty + +// PCRSelection represents a TPMS_PCR_SELECTION. +// See definition in Part 2: Structures, section 10.6.2. +type PCRSelection = internal.TPMSPCRSelection + +// AlgProperty represents a TPMS_ALG_PROPERTY. +// See definition in Part 2: Structures, section 10.8.1. +type AlgProperty = internal.TPMSAlgProperty + +// TaggedProperty represents a TPMS_TAGGED_PROPERTY. +// See definition in Part 2: Structures, section 10.8.2. +type TaggedProperty = internal.TPMSTaggedProperty + +// TaggedPCRSelect represents a TPMS_TAGGED_PCR_SELECT. +// See definition in Part 2: Structures, section 10.8.3. +type TaggedPCRSelect = internal.TPMSTaggedPCRSelect + +// TaggedPolicy represents a TPMS_TAGGED_POLICY. +// See definition in Part 2: Structures, section 10.8.4. +type TaggedPolicy = internal.TPMSTaggedPolicy + +// ACTData represents a TPMS_ACT_DATA. +// See definition in Part 2: Structures, section 10.8.5. +type ACTData = internal.TPMSACTData + +// CapabilityData represents a TPMS_CAPABILITY_DATA. +// See definition in Part 2: Structures, section 10.10.2. +type CapabilityData = internal.TPMSCapabilityData + +// ClockInfo represents a TPMS_CLOCK_INFO. +// See definition in Part 2: Structures, section 10.11.1. +type ClockInfo = internal.TPMSClockInfo + +// TimeInfo represents a TPMS_TIMEzINFO. +// See definition in Part 2: Structures, section 10.11.6. +type TimeInfo = internal.TPMSTimeInfo + +// TimeAttestInfo represents a TPMS_TIME_ATTEST_INFO. +// See definition in Part 2: Structures, section 10.12.2. +type TimeAttestInfo = internal.TPMSTimeAttestInfo + +// CertifyInfo represents a TPMS_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.3. +type CertifyInfo = internal.TPMSCertifyInfo + +// QuoteInfo represents a TPMS_QUOTE_INFO. +// See definition in Part 2: Structures, section 10.12.4. +type QuoteInfo = internal.TPMSQuoteInfo + +// CommandAuditInfo represents a TPMS_COMMAND_AUDIT_INFO. +// See definition in Part 2: Structures, section 10.12.5. +type CommandAuditInfo = internal.TPMSCommandAuditInfo + +// SessionAuditInfo represents a TPMS_SESSION_AUDIT_INFO. +// See definition in Part 2: Structures, section 10.12.6. +type SessionAuditInfo = internal.TPMSSessionAuditInfo + +// CreationInfo represents a TPMS_CREATION_INFO. +// See definition in Part 2: Structures, section 10.12.7. +type CreationInfo = internal.TPMSCreationInfo + +// NVCertifyInfo represents a TPMS_NV_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.8. +type NVCertifyInfo = internal.TPMSNVCertifyInfo + +// NVDigestCertifyInfo represents a TPMS_NV_DIGEST_CERTIFY_INFO. +// See definition in Part 2: Structures, section 10.12.9. +type NVDigestCertifyInfo = internal.TPMSNVDigestCertifyInfo + +// Attest represents a TPMS_ATTEST. +// See definition in Part 2: Structures, section 10.12.12. +type Attest = internal.TPMSAttest + +// AuthCommand represents a TPMS_AUTH_COMMAND. +// See definition in Part 2: Structures, section 10.13.2. +type AuthCommand = internal.TPMSAuthCommand + +// AuthResponse represents a TPMS_AUTH_RESPONSE. +// See definition in Part 2: Structures, section 10.13.3. +type AuthResponse = internal.TPMSAuthResponse + +// SymCipherParms represents a TPMS_SYMCIPHER_PARMS. +// See definition in Part 2: Structures, section 11.1.9. +type SymCipherParms = internal.TPMSSymCipherParms + +// SensitiveCreate represents a TPMS_SENSITIVE_CREATE. +// See definition in Part 2: Structures, section 11.1.15. +type SensitiveCreate = internal.TPMSSensitiveCreate + +// SchemeHash represents a TPMS_SCHEME_HASH. +// See definition in Part 2: Structures, section 11.1.17. +type SchemeHash = internal.TPMSSchemeHash + +// SchemeHMAC represents a TPMS_SCHEME_HMAC. +// See definition in Part 2: Structures, section 11.1.20. +type SchemeHMAC = internal.TPMSSchemeHMAC + +// SchemeXOR represents a TPMS_SCHEME_XOR. +// See definition in Part 2: Structures, section 11.1.21. +type SchemeXOR = internal.TPMSSchemeXOR + +// SigSchemeRSASSA represents a TPMS_SIG_SCHEME_RSASSA. +// See definition in Part 2: Structures, section 11.2.1.2. +type SigSchemeRSASSA = internal.TPMSSigSchemeRSASSA + +// SigSchemeRSAPSS represents a TPMS_SIG_SCHEME_RSAPSS. +// See definition in Part 2: Structures, section 11.2.1.2. +type SigSchemeRSAPSS = internal.TPMSSigSchemeRSAPSS + +// SigSchemeECDSA represents a TPMS_SIG_SCHEME_ECDSA. +// See definition in Part 2: Structures, section 11.2.1.3. +type SigSchemeECDSA = internal.TPMSSigSchemeECDSA + +// EncSchemeRSAES represents a TPMS_ENC_SCHEME_RSAES. +// See definition in Part 2: Structures, section 11.2.2.2. +type EncSchemeRSAES = internal.TPMSEncSchemeRSAES + +// EncSchemeOAEP represents a TPMS_ENC_SCHEME_OAEP. +// See definition in Part 2: Structures, section 11.2.2.2. +type EncSchemeOAEP = internal.TPMSEncSchemeOAEP + +// KeySchemeECDH represents a TPMS_KEY_SCHEME_ECDH. +// See definition in Part 2: Structures, section 11.2.2.3. +type KeySchemeECDH = internal.TPMSKeySchemeECDH + +// KDFSchemeMGF1 represents a TPMS_KDF_SCHEME_MGF1. +// See definition in Part 2: Structures, section 11.2.3.1. +type KDFSchemeMGF1 = internal.TPMSKDFSchemeMGF1 + +// KDFSchemeECDH represents a TPMS_KDF_SCHEME_ECDH. +// See definition in Part 2: Structures, section 11.2.3.1. +type KDFSchemeECDH = internal.TPMSKDFSchemeECDH + +// KDFSchemeKDF1SP80056A represents a TPMS_KDF_SCHEME_KDF1SP80056A. +// See definition in Part 2: Structures, section 11.2.3.1. +type KDFSchemeKDF1SP80056A = internal.TPMSKDFSchemeKDF1SP80056A + +// KDFSchemeKDF2 represents a TPMS_KDF_SCHEME_KDF2. +// See definition in Part 2: Structures, section 11.2.3.1. +type KDFSchemeKDF2 = internal.TPMSKDFSchemeKDF2 + +// KDFSchemeKDF1SP800108 represents a TPMS_KDF_SCHEME_KDF1SP800108. +// See definition in Part 2: Structures, section 11.2.3.1. +type KDFSchemeKDF1SP800108 = internal.TPMSKDFSchemeKDF1SP800108 + +// ECCPoint represents a TPMS_ECC_POINT. +// See definition in Part 2: Structures, section 11.2.5.2. +type ECCPoint = internal.TPMSECCPoint + +// SignatureRSA represents a TPMS_SIGNATURE_RSA. +// See definition in Part 2: Structures, section 11.3.1. +type SignatureRSA = internal.TPMSSignatureRSA + +// SignatureECC represents a TPMS_SIGNATURE_ECC. +// See definition in Part 2: Structures, section 11.3.2. +type SignatureECC = internal.TPMSSignatureECC + +// KeyedHashParms represents a TPMS_KEYED_HASH_PARMS. +// See definition in Part 2: Structures, section 12.2.3.3. +type KeyedHashParms = internal.TPMSKeyedHashParms + +// RSAParms represents a TPMS_RSA_PARMS. +// See definition in Part 2: Structures, section 12.2.3.5. +type RSAParms = internal.TPMSRSAParms + +// ECCParms represents a TPMS_ECC_PARMS. +// See definition in Part 2: Structures, section 12.2.3.6. +type ECCParms = internal.TPMSECCParms + +// CreationData represents a TPMS_CREATION_DATA. +// See definition in Part 2: Structures, section 15.1. +type CreationData = internal.TPMSCreationData diff --git a/direct/structures/tpmt/tpmt.go b/direct/structures/tpmt/tpmt.go new file mode 100644 index 00000000..90c92136 --- /dev/null +++ b/direct/structures/tpmt/tpmt.go @@ -0,0 +1,52 @@ +// package tpmt contains TPM 2.0 structures prefixed with "TPMT_" +package tpmt + +import "github.com/google/go-tpm/direct/structures/internal" + +// HA represents a TPMT_HA. +// See definition in Part 2: Structures, section 10.3.2. +type HA = internal.TPMTHA + +// TKCreation represents a TPMT_TK_CREATION. +// See definition in Part 2: Structures, section 10.7.3. +type TKCreation = internal.TPMTTKCreation + +// TKAuth represents a TPMT_TK_AUTH. +// See definition in Part 2: Structures, section 10.7.5. +type TKAuth = internal.TPMTTKAuth + +// SymDef represents a TPMT_SYM_DEF. +// See definition in Part 2: Structures, section 11.1.6. +type SymDef = internal.TPMTSymDef + +// SymDefObject represents a TPMT_SYM_DEF_OBJECT. +// See definition in Part 2: Structures, section 11.1.7. +type SymDefObject = internal.TPMTSymDefObject + +// KeyedHashScheme represents a TPMT_KEYEDHASH_SCHEME. +// See definition in Part 2: Structures, section 11.1.23. +type KeyedHashScheme = internal.TPMTKeyedHashScheme + +// SigScheme represents a TPMT_SIG_SCHEME. +// See definition in Part 2: Structures, section 11.2.1.5. +type SigScheme = internal.TPMTSigScheme + +// KDFScheme represents a TPMT_KDF_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.3. +type KDFScheme = internal.TPMTKDFScheme + +// RSAScheme represents a TPMT_RSA_SCHEME. +// See definition in Part 2: Structures, section 11.2.4.2. +type RSAScheme = internal.TPMTRSAScheme + +// ECCScheme represents a TPMT_ECC_SCHEME. +// See definition in Part 2: Structures, section 11.2.5.6. +type ECCScheme = internal.TPMTECCScheme + +// Signature represents a TPMT_SIGNATURE. +// See definition in Part 2: Structures, section 11.3.4. +type Signature = internal.TPMTSignature + +// Public represents a TPMT_PUBLIC. +// See definition in Part 2: Structures, section 12.2.4. +type Public = internal.TPMTPublic diff --git a/direct/structures/tpmu/tpmu.go b/direct/structures/tpmu/tpmu.go new file mode 100644 index 00000000..21633993 --- /dev/null +++ b/direct/structures/tpmu/tpmu.go @@ -0,0 +1,56 @@ +// package tpmu contains TPM 2.0 structures prefixed with "TPMU_" +package tpmu + +import "github.com/google/go-tpm/direct/structures/internal" + +// HA represents a TPMU_HA. +// See definition in Part 2: Structures, section 10.3.1. +type HA = internal.TPMUHA + +// Capabilities represents a TPMU_CAPABILITIES. +// See definition in Part 2: Structures, section 10.10.1. +type Capabilities = internal.TPMUCapabilities + +// Attest represents a TPMU_ATTEST. +// See definition in Part 2: Structures, section 10.12.11. +type Attest = internal.TPMUAttest + +// SymKeyBits represents a TPMU_SYM_KEY_BITS. +// See definition in Part 2: Structures, section 11.1.3. +type SymKeyBits = internal.TPMUSymKeyBits + +// SymMode represents a TPMU_SYM_MODE. +// See definition in Part 2: Structures, section 11.1.4. +type SymMode = internal.TPMUSymMode + +// SymDetails represents a TPMU_SYM_DETAILS. +// See definition in Part 2: Structures, section 11.1.5. +type SymDetails = internal.TPMUSymDetails + +// SchemeKeyedHash represents a TPMU_SCHEME_KEYEDHASH. +// See definition in Part 2: Structures, section 11.1.22. +type SchemeKeyedHash = internal.TPMUSchemeKeyedHash + +// SigScheme represents a TPMU_SIG_SCHEME. +// See definition in Part 2: Structures, section 11.2.1.4. +type SigScheme = internal.TPMUSigScheme + +// KDFScheme represents a TPMU_KDF_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.2. +type KDFScheme = internal.TPMUKDFScheme + +// AsymScheme represents a TPMU_ASYM_SCHEME. +// See definition in Part 2: Structures, section 11.2.3.5. +type AsymScheme = internal.TPMUAsymScheme + +// Signature represents a TPMU_SIGNATURE. +// See definition in Part 2: Structures, section 11.3.3. +type Signature = internal.TPMUSignature + +// PublicID represents a TPMU_PUBLIC_ID. +// See definition in Part 2: Structures, section 12.2.3.2. +type PublicID = internal.TPMUPublicID + +// PublicParms represents a TPMU_PUBLIC_PARMS. +// See definition in Part 2: Structures, section 12.2.3.7. +type PublicParms = internal.TPMUPublicParms diff --git a/direct/templates/templates.go b/direct/templates/templates.go new file mode 100644 index 00000000..b4b439f2 --- /dev/null +++ b/direct/templates/templates.go @@ -0,0 +1,200 @@ +package templates + +import ( + "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/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/structures/tpmu" +) + +var ( + // RSASRKTemplate contains the TCG reference RSA-2048 SRK template. + // https://trustedcomputinggroup.org/wp-content/uploads/TCG-tpm.-v2.0-Provisioning-Guidance-Published-v1r1.pdf + RSASRKTemplate = tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgRSA, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + AdminWithPolicy: false, + NoDA: true, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + Parameters: tpmu.PublicParms{ + RSADetail: &tpms.RSAParms{ + Symmetric: tpmt.SymDefObject{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(128), + }, + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), + }, + }, + KeyBits: 2048, + }, + }, + Unique: tpmu.PublicID{ + RSA: &tpm2b.PublicKeyRSA{ + Buffer: make([]byte, 256), + }, + }, + }, + } + // RSAEKTemplate contains the TCG reference RSA-2048 EK template. + RSAEKTemplate = tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgRSA, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: false, + AdminWithPolicy: true, + NoDA: false, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + AuthPolicy: tpm2b.Digest{ + Buffer: []byte{ + // tpm.2_PolicySecret(RH_ENDORSEMENT) + 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, + 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, + 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, + 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, + }, + }, + Parameters: tpmu.PublicParms{ + RSADetail: &tpms.RSAParms{ + Symmetric: tpmt.SymDefObject{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(128), + }, + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), + }, + }, + KeyBits: 2048, + }, + }, + Unique: tpmu.PublicID{ + RSA: &tpm2b.PublicKeyRSA{ + Buffer: make([]byte, 256), + }, + }, + }, + } + // ECCSRKTemplate contains the TCG reference ECC-P256 SRK template. + // https://trustedcomputinggroup.org/wp-content/uploads/TCG-tpm.-v2.0-Provisioning-Guidance-Published-v1r1.pdf + ECCSRKTemplate = tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgECC, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: true, + AdminWithPolicy: false, + NoDA: true, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + Parameters: tpmu.PublicParms{ + ECCDetail: &tpms.ECCParms{ + Symmetric: tpmt.SymDefObject{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(128), + }, + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), + }, + }, + CurveID: tpm.ECCNistP256, + }, + }, + Unique: tpmu.PublicID{ + ECC: &tpms.ECCPoint{ + X: tpm2b.ECCParameter{ + Buffer: make([]byte, 32), + }, + Y: tpm2b.ECCParameter{ + Buffer: make([]byte, 32), + }, + }, + }, + }, + } + // ECCEKTemplate contains the TCG reference ECC-P256 EK template. + ECCEKTemplate = tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgECC, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + STClear: false, + FixedParent: true, + SensitiveDataOrigin: true, + UserWithAuth: false, + AdminWithPolicy: true, + NoDA: false, + EncryptedDuplication: false, + Restricted: true, + Decrypt: true, + SignEncrypt: false, + }, + AuthPolicy: tpm2b.Digest{ + Buffer: []byte{ + // tpm.2_PolicySecret(RH_ENDORSEMENT) + 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, + 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, + 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, + 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, + }, + }, + Parameters: tpmu.PublicParms{ + ECCDetail: &tpms.ECCParms{ + Symmetric: tpmt.SymDefObject{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(128), + }, + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), + }, + }, + CurveID: tpm.ECCNistP256, + }, + }, + Unique: tpmu.PublicID{ + ECC: &tpms.ECCPoint{ + X: tpm2b.ECCParameter{ + Buffer: make([]byte, 32), + }, + Y: tpm2b.ECCParameter{ + Buffer: make([]byte, 32), + }, + }, + }, + }, + } +) diff --git a/tpm2/direct/audit.go b/direct/tpm2/audit.go similarity index 73% rename from tpm2/direct/audit.go rename to direct/tpm2/audit.go index 2fc0d260..e8933577 100644 --- a/tpm2/direct/audit.go +++ b/direct/tpm2/audit.go @@ -1,25 +1,32 @@ -package direct +package tpm2 import ( "bytes" "fmt" "reflect" + + "github.com/google/go-tpm/direct/structures/tpm" + "github.com/google/go-tpm/direct/structures/tpmi" ) // 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 + hash tpmi.AlgHash 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()), +func NewAudit(hash tpmi.AlgHash) (*CommandAudit, error) { + h, err := hash.Hash() + if err != nil { + return nil, err } + return &CommandAudit{ + hash: hash, + digest: make([]byte, h.Size()), + }, nil } // Extend extends the audit digest with the given command and response. @@ -32,7 +39,11 @@ func (a *CommandAudit) Extend(cmd Command, rsp Response) error { if err != nil { return err } - h := a.hash.Hash().New() + ha, err := a.hash.Hash() + if err != nil { + return err + } + h := ha.New() h.Write(a.digest) h.Write(cpHash) h.Write(rpHash) @@ -48,7 +59,7 @@ func (a *CommandAudit) Digest() []byte { // 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) { +func auditCPHash(h tpmi.AlgHash, c Command) ([]byte, error) { cc := c.Command() names, err := cmdNames(c) if err != nil { @@ -58,13 +69,13 @@ func auditCPHash(h TPMIAlgHash, c Command) ([]byte, error) { if err != nil { return nil, err } - return cpHash(h, cc, names, parms), nil + return cpHash(h, cc, names, parms) } // 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) { +func auditRPHash(h tpmi.AlgHash, r Response) ([]byte, error) { cc := r.Response() var parms bytes.Buffer parameters := taggedMembers(reflect.ValueOf(r).Elem(), "handle", true) @@ -73,5 +84,5 @@ func auditRPHash(h TPMIAlgHash, r Response) ([]byte, error) { return nil, fmt.Errorf("marshalling parameter %v: %w", i, err) } } - return rpHash(h, TPMRCSuccess, cc, parms.Bytes()), nil + return rpHash(h, tpm.RCSuccess, cc, parms.Bytes()) } diff --git a/tpm2/direct/audit_test.go b/direct/tpm2/audit_test.go similarity index 50% rename from tpm2/direct/audit_test.go rename to direct/tpm2/audit_test.go index 2e4522e9..051005b8 100644 --- a/tpm2/direct/audit_test.go +++ b/direct/tpm2/audit_test.go @@ -1,10 +1,16 @@ -package direct +package tpm2 import ( "bytes" "testing" "github.com/google/go-tpm-tools/simulator" + "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/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/structures/tpmu" ) func TestAuditSession(t *testing.T) { @@ -12,26 +18,26 @@ func TestAuditSession(t *testing.T) { if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - tpm := NewTPM(sim) - defer tpm.Close() + thetpm := NewTPM(sim) + defer thetpm.Close() // Create the audit session - sess, cleanup, err := HMACSession(tpm, TPMAlgSHA256, 16, Audit()) + sess, cleanup, err := HMACSession(thetpm, tpm.AlgSHA256, 16, Audit()) if err != nil { t.Fatalf("%v", err) } defer cleanup() // Create the AK for audit - createAKCmd := CreatePrimaryCommand{ + createAKCmd := CreatePrimary{ PrimaryHandle: AuthHandle{ - Handle: TPMRHOwner, + Handle: tpm.RHOwner, }, - InPublic: TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgECC, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgECC, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ FixedTPM: true, STClear: false, FixedParent: true, @@ -44,74 +50,74 @@ func TestAuditSession(t *testing.T) { Decrypt: false, SignEncrypt: true, }, - Parameters: TPMUPublicParms{ - ECCDetail: &TPMSECCParms{ - Scheme: TPMTECCScheme{ - Scheme: TPMAlgECDSA, - Details: TPMUAsymScheme{ - ECDSA: &TPMSSigSchemeECDSA{ - HashAlg: TPMAlgSHA256, + Parameters: tpmu.PublicParms{ + ECCDetail: &tpms.ECCParms{ + Scheme: tpmt.ECCScheme{ + Scheme: tpm.AlgECDSA, + Details: tpmu.AsymScheme{ + ECDSA: &tpms.SigSchemeECDSA{ + HashAlg: tpm.AlgSHA256, }, }, }, - CurveID: TPMECCNistP256, + CurveID: tpm.ECCNistP256, }, }, }, }, } - var createAKRsp CreatePrimaryResponse - if err := tpm.Execute(&createAKCmd, &createAKRsp); err != nil { + createAKRsp, err := createAKCmd.Execute(thetpm) + if 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 { + flushCmd := FlushContext{FlushHandle: createAKRsp.ObjectHandle} + if _, err := flushCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() - audit := NewAudit(TPMAlgSHA256) + audit, err := NewAudit(tpm.AlgSHA256) + if err != nil { + t.Fatalf("%v", err) + } // 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, + props := []tpm.PT{ + tpm.PTFamilyIndicator, + tpm.PTLevel, + tpm.PTRevision, + tpm.PTDayofYear, + tpm.PTYear, + tpm.PTManufacturer, } for _, prop := range props { - getCmd := GetCapabilityCommand{ - Capability: TPMCapTPMProperties, + getCmd := GetCapability{ + Capability: tpm.CapTPMProperties, Property: uint32(prop), PropertyCount: 1, } - var getRsp GetCapabilityResponse - if err := tpm.Execute(&getCmd, &getRsp, sess); err != nil { + getRsp, err := getCmd.Execute(thetpm, sess) + if err != nil { t.Fatalf("%v", err) } - if err := audit.Extend(&getCmd, &getRsp); err != nil { + if err := audit.Extend(&getCmd, getRsp); err != nil { t.Fatalf("%v", err) } // Get the audit digest signed by the AK - getAuditCmd := GetSessionAuditDigestCommand{ + getAuditCmd := GetSessionAuditDigest{ PrivacyAdminHandle: AuthHandle{ - Handle: TPMRHEndorsement, + Handle: tpm.RHEndorsement, }, SignHandle: AuthHandle{ Handle: createAKRsp.ObjectHandle, }, SessionHandle: sess.Handle(), - QualifyingData: TPM2BData{[]byte("foobar")}, + QualifyingData: tpm2b.Data{Buffer: []byte("foobar")}, } - var getAuditRsp GetSessionAuditDigestResponse - if err := tpm.Execute(&getAuditCmd, &getAuditRsp); err != nil { + getAuditRsp, err := getAuditCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } // TODO check the signature with the AK pub diff --git a/tpm2/direct/ek_test.go b/direct/tpm2/ek_test.go similarity index 72% rename from tpm2/direct/ek_test.go rename to direct/tpm2/ek_test.go index 6ab89a65..595f76bd 100644 --- a/tpm2/direct/ek_test.go +++ b/direct/tpm2/ek_test.go @@ -1,17 +1,24 @@ -package direct +package tpm2 import ( "errors" "testing" "github.com/google/go-tpm-tools/simulator" + "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/tpmi" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/templates" ) // Test creating a sealed data blob on the standard-template EK using its policy. func TestEKPolicy(t *testing.T) { - templates := map[string]TPM2BPublic{ - "RSA": RSAEKTemplate, - "ECC": ECCEKTemplate, + templates := map[string]tpm2b.Public{ + "RSA": templates.RSAEKTemplate, + "ECC": templates.ECCEKTemplate, } // Run the whole test for each of RSA and ECC EKs. @@ -22,18 +29,18 @@ func TestEKPolicy(t *testing.T) { } } -func ekPolicy(tpm *TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error { - cmd := PolicySecretCommand{ - AuthHandle: AuthHandle{Handle: TPMRHEndorsement}, +func ekPolicy(t *TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error { + cmd := PolicySecret{ + AuthHandle: AuthHandle{Handle: tpm.RHEndorsement}, PolicySession: handle, NonceTPM: nonceTPM, } - rsp := PolicySecretResponse{} - return tpm.Execute(&cmd, &rsp) + _, err := cmd.Execute(t) + return err } // This function tests a lot of combinations of authorizing the EK policy. -func ekTest(t *testing.T, ekTemplate TPM2BPublic) { +func ekTest(t *testing.T, ekTemplate tpm2b.Public) { type ekTestCase struct { name string // Use Policy instead of PolicySession, passing the callback instead of @@ -93,20 +100,20 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - tpm := NewTPM(sim) - defer tpm.Close() + thetpm := NewTPM(sim) + defer thetpm.Close() for _, c := range cases { t.Run(c.name, func(t *testing.T) { // Create the EK - createEKCmd := CreatePrimaryCommand{ + createEKCmd := CreatePrimary{ PrimaryHandle: AuthHandle{ - Handle: TPMRHEndorsement, + Handle: tpm.RHEndorsement, }, InPublic: ekTemplate, } - var createEKRsp CreatePrimaryResponse - if err := tpm.Execute(&createEKCmd, &createEKRsp); err != nil { + createEKRsp, err := createEKCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } if createEKRsp.OutPublic.PublicArea.Unique.ECC != nil { @@ -115,11 +122,8 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { } defer func() { // Flush the EK - flushEKCmd := FlushContextCommand{ - FlushHandle: createEKRsp.ObjectHandle, - } - var flushEKRsp FlushContextResponse - if err := tpm.Execute(&flushEKCmd, &flushEKRsp); err != nil { + flushEKCmd := FlushContext{createEKRsp.ObjectHandle} + if _, err := flushEKCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() @@ -127,23 +131,23 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { // Exercise the EK's auth policy (PolicySecret[RH_ENDORSEMENT]) // by creating an object under it data := []byte("secrets") - createBlobCmd := CreateCommand{ + createBlobCmd := Create{ ParentHandle: AuthHandle{ Handle: createEKRsp.ObjectHandle, Name: createEKRsp.Name, }, - InSensitive: TPM2BSensitiveCreate{ - Sensitive: TPMSSensitiveCreate{ - Data: TPM2BData{ + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + Data: tpm2b.Data{ Buffer: data, }, }, }, - InPublic: TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgKeyedHash, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgKeyedHash, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ FixedTPM: true, FixedParent: true, UserWithAuth: true, @@ -152,11 +156,10 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { }, }, } - var createBlobRsp CreateResponse var sessions []Session if c.decryptAnotherSession { - sessions = append(sessions, HMAC(TPMAlgSHA1, 16, AESEncryption(128, EncryptIn))) + sessions = append(sessions, HMAC(tpm.AlgSHA1, 16, AESEncryption(128, EncryptIn))) } var options []AuthOption @@ -173,12 +176,12 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { var s Session if c.jitPolicySession { // Use the convenience function to pass a policy callback. - s = Policy(TPMAlgSHA256, 16, ekPolicy, options...) + s = Policy(tpm.AlgSHA256, 16, ekPolicy, options...) } else { // Set up a session we have to execute and clean up ourselves. var cleanup func() error var err error - s, cleanup, err = PolicySession(tpm, TPMAlgSHA256, 16, options...) + s, cleanup, err = PolicySession(thetpm, tpm.AlgSHA256, 16, options...) if err != nil { t.Fatalf("creating session: %v", err) } @@ -189,26 +192,26 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { } }() // Execute the same callback ourselves. - if err = ekPolicy(tpm, s.Handle(), s.NonceTPM()); err != nil { + if err = ekPolicy(thetpm, s.Handle(), s.NonceTPM()); err != nil { t.Fatalf("executing EK policy: %v", err) } } createBlobCmd.ParentHandle.Auth = s - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...); err != nil { + if _, err := createBlobCmd.Execute(thetpm, sessions...); err != nil { t.Fatalf("%v", err) } if !c.jitPolicySession { // If we're not using a "just-in-time" session with a callback, // we have to re-initialize the session. - if err = ekPolicy(tpm, s.Handle(), s.NonceTPM()); err != nil { + if err = ekPolicy(thetpm, s.Handle(), s.NonceTPM()); err != nil { t.Fatalf("executing EK policy: %v", err) } } // Try again and make sure it succeeds again. - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...); err != nil { + if _, err = createBlobCmd.Execute(thetpm, sessions...); err != nil { t.Fatalf("%v", err) } @@ -217,14 +220,11 @@ func ekTest(t *testing.T, ekTemplate TPM2BPublic) { // we don't re-initialize the session. // This is because after using a policy session, it's as if // PolicyRestart was called. - err := tpm.Execute(&createBlobCmd, &createBlobRsp, sessions...) - if err == nil { - t.Fatalf("wanted an error, got nil") - } - if !errors.Is(err, TPMRCPolicyFail) { + _, err = createBlobCmd.Execute(thetpm, sessions...) + if !errors.Is(err, tpm.RCPolicyFail) { t.Errorf("want TPM_RC_POLICY_FAIL, got %v", err) } - var fmt1 Fmt1Error + var fmt1 tpm.Fmt1Error if !errors.As(err, &fmt1) { t.Errorf("want a Fmt1Error, got %v", err) } else if isSession, session := fmt1.Session(); !isSession || session != 1 { diff --git a/tpm2/direct/reflect.go b/direct/tpm2/reflect.go similarity index 96% rename from tpm2/direct/reflect.go rename to direct/tpm2/reflect.go index 5dc08725..e79e5da1 100644 --- a/tpm2/direct/reflect.go +++ b/direct/tpm2/reflect.go @@ -1,5 +1,5 @@ -// Package direct provides 1:1 mapping to TPM 2.0 APIs. -package direct +// Package tpm2 provides 1:1 mapping to TPM 2.0 APIs. +package tpm2 import ( "bytes" @@ -11,6 +11,9 @@ import ( "strconv" "strings" + "github.com/google/go-tpm/direct/structures/tpm" + "github.com/google/go-tpm/direct/structures/tpm2b" + "github.com/google/go-tpm/direct/structures/tpms" "github.com/google/go-tpm/tpmutil" ) @@ -40,8 +43,8 @@ func (t *TPM) Close() error { return t.transport.Close() } -// Execute sends the provided command and returns the TPM's response. -func (t *TPM) Execute(cmd Command, rsp Response, extraSess ...Session) error { +// execute sends the provided command and returns the TPM's response. +func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { cc := cmd.Command() if rsp.Response() != cc { return fmt.Errorf("cmd and rsp must be for same command: %v != %v", cc, rsp.Response()) @@ -116,7 +119,7 @@ func (t *TPM) Execute(cmd Command, rsp Response, extraSess ...Session) error { // We don't need the TPM RC here because we would have errored // out from rspHeader // TODO: Authenticate the error code with sessions, if desired. - err = rspSessions(rspBuf, TPMRCSuccess, cc, names, rspParms, sess) + err = rspSessions(rspBuf, tpm.RCSuccess, cc, names, rspParms, sess) if err != nil { return err } @@ -221,7 +224,7 @@ func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { // TPMAlgNull for union selection. // This allows callers to omit uninteresting scheme structures. if v.Field(i).IsZero() && hasTag(v.Type().Field(i), "nullable") { - possibleSelectors[v.Type().Field(i).Name] = int64(TPMAlgNull) + possibleSelectors[v.Type().Field(i).Name] = int64(tpm.AlgNull) continue } switch v.Field(i).Kind() { @@ -267,7 +270,7 @@ func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { // as TPMHandle's zero value is TPM_RH_NULL. // This allows callers to omit uninteresting handles // instead of specifying them as TPM_RH_NULL. - if err := binary.Write(&res, binary.BigEndian, uint32(TPMRHNull)); err != nil { + if err := binary.Write(&res, binary.BigEndian, uint32(tpm.RHNull)); err != nil { return err } } else if v.Field(i).IsZero() && v.Field(i).Kind() == reflect.Uint16 && hasTag(v.Type().Field(i), "nullable") { @@ -276,7 +279,7 @@ func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { // This allows callers to omit uninteresting // algorithms/schemes instead of specifying them as // TPM_ALG_NULL. - if err := binary.Write(&res, binary.BigEndian, uint16(TPMAlgNull)); err != nil { + if err := binary.Write(&res, binary.BigEndian, uint16(tpm.AlgNull)); err != nil { return err } } else { @@ -340,7 +343,7 @@ func marshalBitwise(buf *bytes.Buffer, v reflect.Value) error { // selector. Marshals nothing if the selector is equal to TPM_ALG_NULL (0x0010). func marshalUnion(buf *bytes.Buffer, v reflect.Value, selector int64) error { // Special case: TPM_ALG_NULL as a selector means marshal nothing - if selector == int64(TPMAlgNull) { + if selector == int64(tpm.AlgNull) { return nil } for i := 0; i < v.NumField(); i++ { @@ -614,7 +617,7 @@ func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { // selector. Unmarshals nothing if the selector is TPM_ALG_NULL (0x0010). func unmarshalUnion(buf *bytes.Buffer, v reflect.Value, selector int64) error { // Special case: TPM_ALG_NULL as a selector means unmarshal nothing - if selector == int64(TPMAlgNull) { + if selector == int64(tpm.AlgNull) { return nil } for i := 0; i < v.NumField(); i++ { @@ -760,9 +763,9 @@ func cmdHandles(cmd Command) []byte { } // cmdNames returns the authorized names of the command. -func cmdNames(cmd Command) ([]TPM2BName, error) { +func cmdNames(cmd Command) ([]tpm2b.Name, error) { authHandles := taggedMembers(reflect.ValueOf(cmd).Elem(), "auth", false) - var result []TPM2BName + var result []tpm2b.Name for _, authHandle := range authHandles { handle, ok := authHandle.Interface().(AuthHandle) if !ok { @@ -797,7 +800,7 @@ func cmdParameters(cmd Command, sess []Session) ([]byte, error) { return nil, fmt.Errorf("too many decrypt sessions") } if len(firstParmBytes) < 2 { - return nil, fmt.Errorf("this command's first parameter is not a TPM2B") + return nil, fmt.Errorf("this command's first parameter is not a tpm2b.") } err := s.Encrypt(firstParmBytes[2:]) if err != nil { @@ -815,7 +818,7 @@ func cmdParameters(cmd Command, sess []Session) ([]byte, error) { } // cmdSessions returns the authorization area of the command. -func cmdSessions(tpm *TPM, sess []Session, cc TPMCC, names []TPM2BName, parms []byte) ([]byte, error) { +func cmdSessions(tpm *TPM, sess []Session, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { // There is no authorization area if there are no sessions. if len(sess) == 0 { return nil, nil @@ -874,12 +877,12 @@ func cmdSessions(tpm *TPM, sess []Session, cc TPMCC, names []TPM2BName, parms [] } // cmdHeader returns the structured TPM command header. -func cmdHeader(hasSessions bool, length int, cc TPMCC) []byte { - tag := TPMSTNoSessions +func cmdHeader(hasSessions bool, length int, cc tpm.CC) []byte { + tag := tpm.STNoSessions if hasSessions { - tag = TPMSTSessions + tag = tpm.STSessions } - hdr := TPMCmdHeader{ + hdr := tpm.CmdHeader{ Tag: tag, Length: uint32(length), CommandCode: cc, @@ -893,11 +896,11 @@ func cmdHeader(hasSessions bool, length int, cc TPMCC) []byte { // returns an error here. // rsp is updated to point to the rest of the response after the header. func rspHeader(rsp *bytes.Buffer) error { - var hdr TPMRspHeader + var hdr tpm.RspHeader if err := unmarshal(rsp, reflect.ValueOf(&hdr).Elem()); err != nil { return fmt.Errorf("unmarshalling TPM response: %w", err) } - if hdr.ResponseCode != TPMRCSuccess { + if hdr.ResponseCode != tpm.RCSuccess { return hdr.ResponseCode } return nil @@ -953,9 +956,9 @@ func rspParametersArea(hasSessions bool, rsp *bytes.Buffer) ([]byte, error) { // the sessions with it. If there is a response validation error, returns // an error here. // rsp is updated to point to the rest of the response after the sessions. -func rspSessions(rsp *bytes.Buffer, rc TPMRC, cc TPMCC, names []TPM2BName, parms []byte, sess []Session) error { +func rspSessions(rsp *bytes.Buffer, rc tpm.RC, cc tpm.CC, names []tpm2b.Name, parms []byte, sess []Session) error { for i, s := range sess { - var auth TPMSAuthResponse + var auth tpms.AuthResponse if err := unmarshal(rsp, reflect.ValueOf(&auth).Elem()); err != nil { return fmt.Errorf("reading auth session %d: %w", i, err) } diff --git a/tpm2/direct/reflect_test.go b/direct/tpm2/reflect_test.go similarity index 99% rename from tpm2/direct/reflect_test.go rename to direct/tpm2/reflect_test.go index 5712a05c..68ca6e8b 100644 --- a/tpm2/direct/reflect_test.go +++ b/direct/tpm2/reflect_test.go @@ -1,4 +1,4 @@ -package direct +package tpm2 import ( "bytes" diff --git a/tpm2/direct/sealing_test.go b/direct/tpm2/sealing_test.go similarity index 65% rename from tpm2/direct/sealing_test.go rename to direct/tpm2/sealing_test.go index 8276c9fc..c5abffe9 100644 --- a/tpm2/direct/sealing_test.go +++ b/direct/tpm2/sealing_test.go @@ -1,4 +1,4 @@ -package direct +package tpm2 import ( "bytes" @@ -6,13 +6,19 @@ import ( "testing" "github.com/google/go-tpm-tools/simulator" + "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/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/templates" ) // Test creating and unsealing a sealed data blob with a password and HMAC. func TestUnseal(t *testing.T) { - templates := map[string]TPM2BPublic{ - "RSA": RSASRKTemplate, - "ECC": ECCSRKTemplate, + templates := map[string]tpm2b.Public{ + "RSA": templates.RSASRKTemplate, + "ECC": templates.ECCSRKTemplate, } // Run the whole test for each of RSA and ECC SRKs. @@ -23,42 +29,39 @@ func TestUnseal(t *testing.T) { } } -func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { +func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { sim, err := simulator.Get() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - tpm := NewTPM(sim) - defer tpm.Close() + thetpm := NewTPM(sim) + defer thetpm.Close() // Create the SRK // Put a password on the SRK to test more of the flows. srkAuth := []byte("mySRK") - createSRKCmd := CreatePrimaryCommand{ + createSRKCmd := CreatePrimary{ PrimaryHandle: AuthHandle{ - Handle: TPMRHOwner, + Handle: tpm.RHOwner, }, - InSensitive: TPM2BSensitiveCreate{ - Sensitive: TPMSSensitiveCreate{ - UserAuth: TPM2BAuth{ + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ Buffer: srkAuth, }, }, }, InPublic: srkTemplate, } - var createSRKRsp CreatePrimaryResponse - if err := tpm.Execute(&createSRKCmd, &createSRKRsp); err != nil { + createSRKRsp, err := createSRKCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } t.Logf("SRK name: %x", createSRKRsp.Name) defer func() { // Flush the SRK - flushSRKCmd := FlushContextCommand{ - FlushHandle: createSRKRsp.ObjectHandle, - } - var flushSRKRsp FlushContextResponse - if err := tpm.Execute(&flushSRKCmd, &flushSRKRsp); err != nil { + flushSRKCmd := FlushContext{createSRKRsp.ObjectHandle} + if _, err := flushSRKCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() @@ -68,27 +71,27 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Include some trailing zeros to exercise the TPM's trimming of them from auth values. auth := []byte("p@ssw0rd\x00\x00") auth2 := []byte("p@ssw0rd") - createBlobCmd := CreateCommand{ + createBlobCmd := Create{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: PasswordAuth(srkAuth), }, - InSensitive: TPM2BSensitiveCreate{ - Sensitive: TPMSSensitiveCreate{ - UserAuth: TPM2BAuth{ + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ Buffer: auth, }, - Data: TPM2BData{ + Data: tpm2b.Data{ Buffer: data, }, }, }, - InPublic: TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgKeyedHash, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgKeyedHash, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ FixedTPM: true, FixedParent: true, UserWithAuth: true, @@ -97,11 +100,12 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { }, }, } - var createBlobRsp CreateResponse + var createBlobRsp *CreateResponse // Create the blob with password auth, without any session encryption t.Run("Create", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -109,9 +113,10 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob using an hmac auth session also for audit t.Run("CreateAudit", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AuditExclusive()) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -119,9 +124,10 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob, using the auth session also for decryption t.Run("CreateDecrypt", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptIn)) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -129,9 +135,10 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob, using the auth session also for encryption t.Run("CreateEncrypt", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptOut)) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -139,9 +146,10 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob, using the auth session also for decrypt and encrypt t.Run("CreateDecryptEncrypt", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut)) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -149,10 +157,11 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob with decrypt and encrypt session t.Run("CreateDecryptEncryptAudit", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), Audit()) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) @@ -160,49 +169,54 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Create the blob with decrypt and encrypt session bound to SRK t.Run("CreateDecryptEncryptSalted", func(t *testing.T) { createBlobCmd.ParentHandle.Auth = - HMAC(TPMAlgSHA256, 16, Auth(srkAuth), + HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), Salted(createSRKRsp.ObjectHandle, createSRKRsp.OutPublic.PublicArea)) - if err := tpm.Execute(&createBlobCmd, &createBlobRsp); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } }) // Use HMAC auth to authorize the rest of the Create commands // Exercise re-using a use-once HMAC structure (which will spin up the session each time) - createBlobCmd.ParentHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(srkAuth)) + createBlobCmd.ParentHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(srkAuth)) // Create the blob with a separate decrypt and encrypt session t.Run("CreateDecryptEncryptSeparate", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, - HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut))); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm, + HMAC(tpm.AlgSHA256, 16, AESEncryption(128, EncryptInOut))) + if err != nil { t.Fatalf("%v", err) } }) // Create the blob with a separate decrypt and encrypt session, and another for audit t.Run("CreateDecryptEncryptAuditSeparate", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, - HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), - HMAC(TPMAlgSHA256, 16, Audit())); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm, + HMAC(tpm.AlgSHA256, 16, AESEncryption(128, EncryptInOut)), + HMAC(tpm.AlgSHA256, 16, Audit())) + if err != nil { t.Fatalf("%v", err) } }) // Create the blob with a separate decrypt and encrypt session, and another for exclusive audit t.Run("CreateDecryptEncryptAuditExclusiveSeparate", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, - HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), - HMAC(TPMAlgSHA256, 16, AuditExclusive())); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm, + HMAC(tpm.AlgSHA256, 16, AESEncryption(128, EncryptInOut)), + HMAC(tpm.AlgSHA256, 16, AuditExclusive())) + if err != nil { t.Fatalf("%v", err) } }) // Create the blob with separate decrypt and encrypt sessions. t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, + createBlobRsp, err = createBlobCmd.Execute(thetpm, // Get weird with the algorithm and nonce choices. Mix lots of things together. - HMAC(TPMAlgSHA1, 20, AESEncryption(128, EncryptIn)), - HMAC(TPMAlgSHA384, 23, AESEncryption(128, EncryptOut))); err != nil { + HMAC(tpm.AlgSHA1, 20, AESEncryption(128, EncryptIn)), + HMAC(tpm.AlgSHA384, 23, AESEncryption(128, EncryptOut))) + if err != nil { t.Fatalf("%v", err) } }) @@ -211,49 +225,48 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // (The TPM spec orders some extra nonces included in the first session in the order // nonceTPM_decrypt, nonceTPM_encrypt, so this exercises that) t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { - if err := tpm.Execute(&createBlobCmd, &createBlobRsp, - HMAC(TPMAlgSHA1, 17, AESEncryption(128, EncryptOut)), - HMAC(TPMAlgSHA256, 32, AESEncryption(128, EncryptIn))); err != nil { + createBlobRsp, err = createBlobCmd.Execute(thetpm, + HMAC(tpm.AlgSHA1, 17, AESEncryption(128, EncryptOut)), + HMAC(tpm.AlgSHA256, 32, AESEncryption(128, EncryptIn))) + if err != nil { t.Fatalf("%v", err) } }) // Load the sealed blob - loadBlobCmd := LoadCommand{ + loadBlobCmd := Load{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, - Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth)), + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth)), }, InPrivate: createBlobRsp.OutPrivate, InPublic: createBlobRsp.OutPublic, } - var loadBlobRsp LoadResponse - if err := tpm.Execute(&loadBlobCmd, &loadBlobRsp); err != nil { + loadBlobRsp, err := loadBlobCmd.Execute(thetpm) + if err != nil { t.Fatalf("%v", err) } defer func() { // Flush the blob - flushBlobCmd := FlushContextCommand{ - FlushHandle: loadBlobRsp.ObjectHandle, - } - var flushBlobRsp FlushContextResponse - if err := tpm.Execute(&flushBlobCmd, &flushBlobRsp); err != nil { + flushBlobCmd := FlushContext{loadBlobRsp.ObjectHandle} + if _, err := flushBlobCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() - unsealCmd := UnsealCommand{ + unsealCmd := Unseal{ ItemHandle: AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, }, } - var unsealRsp UnsealResponse + // Unseal the blob with a password session t.Run("WithPassword", func(t *testing.T) { unsealCmd.ItemHandle.Auth = PasswordAuth(auth) - if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + unsealRsp, err := unsealCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { @@ -264,14 +277,11 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with an incorrect password session t.Run("WithWrongPassword", func(t *testing.T) { unsealCmd.ItemHandle.Auth = PasswordAuth([]byte("NotThePassword")) - err := tpm.Execute(&unsealCmd, &unsealRsp) - if err == nil { - t.Errorf("want TPM_RC_BAD_AUTH, got nil") - } - if !errors.Is(err, TPMRCBadAuth) { + _, err := unsealCmd.Execute(thetpm) + if !errors.Is(err, tpm.RCBadAuth) { t.Errorf("want TPM_RC_BAD_AUTH, got %v", err) } - var fmt1 Fmt1Error + var fmt1 tpm.Fmt1Error if !errors.As(err, &fmt1) { t.Errorf("want a Fmt1Error, got %v", err) } else if isSession, session := fmt1.Session(); !isSession || session != 1 { @@ -281,8 +291,9 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with a use-once HMAC session t.Run("WithHMAC", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(auth2)) - if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + unsealCmd.ItemHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(auth2)) + unsealRsp, err := unsealCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { @@ -292,9 +303,10 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with a use-once HMAC session with encryption t.Run("WithHMACEncrypt", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = HMAC(TPMAlgSHA256, 16, Auth(auth2), + unsealCmd.ItemHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(auth2), AESEncryption(128, EncryptOut)) - if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + unsealRsp, err := unsealCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { @@ -304,7 +316,7 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with a standalone HMAC session, re-using the session. t.Run("WithHMACSession", func(t *testing.T) { - sess, cleanup, err := HMACSession(tpm, TPMAlgSHA1, 20, Auth(auth2)) + sess, cleanup, err := HMACSession(thetpm, tpm.AlgSHA1, 20, Auth(auth2)) if err != nil { t.Fatalf("%v", err) } @@ -313,7 +325,8 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { - if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + unsealRsp, err := unsealCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { @@ -325,7 +338,7 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with a standalone bound HMAC session, re-using the session. // Also, use session encryption. t.Run("WithHMACSessionEncrypt", func(t *testing.T) { - sess, cleanup, err := HMACSession(tpm, TPMAlgSHA256, 16, Auth(auth2), + sess, cleanup, err := HMACSession(thetpm, tpm.AlgSHA256, 16, Auth(auth2), AESEncryption(128, EncryptOut), Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) if err != nil { @@ -336,7 +349,8 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { - if err := tpm.Execute(&unsealCmd, &unsealRsp); err != nil { + unsealRsp, err := unsealCmd.Execute(thetpm) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { @@ -348,12 +362,12 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // Unseal the blob with a standalone HMAC session, re-using the session. // Spin up another bound session for encryption. t.Run("WithHMACSessionEncryptSeparate", func(t *testing.T) { - sess1, cleanup1, err := HMACSession(tpm, TPMAlgSHA1, 16, Auth(auth2)) + sess1, cleanup1, err := HMACSession(thetpm, tpm.AlgSHA1, 16, Auth(auth2)) if err != nil { t.Fatalf("%v", err) } defer cleanup1() - sess2, cleanup2, err := HMACSession(tpm, TPMAlgSHA384, 16, + sess2, cleanup2, err := HMACSession(thetpm, tpm.AlgSHA384, 16, AESEncryption(128, EncryptOut), Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) if err != nil { @@ -364,7 +378,8 @@ func unsealingTest(t *testing.T, srkTemplate TPM2BPublic) { // It should be possible to use the sessions multiple times. for i := 0; i < 3; i++ { - if err := tpm.Execute(&unsealCmd, &unsealRsp, sess2); err != nil { + unsealRsp, err := unsealCmd.Execute(thetpm, sess2) + if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { diff --git a/tpm2/direct/sessions.go b/direct/tpm2/sessions.go similarity index 73% rename from tpm2/direct/sessions.go rename to direct/tpm2/sessions.go index a1a75e10..27907420 100644 --- a/tpm2/direct/sessions.go +++ b/direct/tpm2/sessions.go @@ -1,4 +1,4 @@ -package direct +package tpm2 import ( "bytes" @@ -11,7 +11,15 @@ import ( "encoding/binary" "fmt" - "github.com/google/go-tpm/tpm2" + "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/tpmi" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" + "github.com/google/go-tpm/direct/structures/tpmu" + legacy "github.com/google/go-tpm/tpm2" ) // Session represents a session in the TPM. @@ -27,7 +35,7 @@ type Session interface { // authorize their actions without writing a lot of code. CleanupFailure(tpm *TPM) error // The last nonceTPM for this session. - NonceTPM() TPM2BNonce + NonceTPM() tpm2b.Nonce // Updates nonceCaller to a new random value. NewNonceCaller() error // Computes the authorization HMAC for the session. @@ -35,10 +43,10 @@ type Session interface { // there is another session (or sessions) for parameter // decryption and/or encryption, then addNonces contains the // nonceTPMs from each of them, respectively (see Part 1, 19.6.5) - Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) + Authorize(cc tpm.CC, parms, addNonces []byte, names []tpm2b.Name, authIndex int) (*tpms.AuthCommand, error) // Validates the response for the session. // Updates NonceTPM for the session. - Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error + Validate(rc tpm.RC, cc tpm.CC, parms []byte, names []tpm2b.Name, authIndex int, auth *tpms.AuthResponse) error // Returns true if this is an encryption session. IsEncryption() bool // Returns true if this is a decryption session. @@ -50,29 +58,37 @@ type Session interface { // parameter. Otherwise, does not modify the parameter. Decrypt(parameter []byte) error // Returns the handle value of this session. - Handle() TPMHandle + Handle() tpm.Handle } // cpHash calculates the TPM command parameter hash. // cpHash = hash(CC || names || parms) -func cpHash(alg TPMIAlgHash, cc TPMCC, names []TPM2BName, parms []byte) []byte { - h := alg.Hash().New() +func cpHash(alg tpmi.AlgHash, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { + ha, err := alg.Hash() + if err != nil { + return nil, err + } + h := ha.New() binary.Write(h, binary.BigEndian, cc) for _, name := range names { h.Write(name.Buffer) } h.Write(parms) - return h.Sum(nil) + return h.Sum(nil), nil } // rpHash calculates the TPM response parameter hash. // rpHash = hash(RC || CC || parms) -func rpHash(alg TPMIAlgHash, rc TPMRC, cc TPMCC, parms []byte) []byte { - h := alg.Hash().New() +func rpHash(alg tpmi.AlgHash, rc tpm.RC, cc tpm.CC, parms []byte) ([]byte, error) { + ha, err := alg.Hash() + if err != nil { + return nil, err + } + h := ha.New() binary.Write(h, binary.BigEndian, rc) binary.Write(h, binary.BigEndian, cc) h.Write(parms) - return h.Sum(nil) + return h.Sum(nil), nil } // pwSession represents a password-pseudo-session. @@ -96,30 +112,30 @@ func (s *pwSession) CleanupFailure(tpm *TPM) error { return nil } // NonceTPM normally returns the last nonceTPM value from the session. // Since a password session is a pseudo-session with the auth value stuffed // in where the HMAC should go, this is not used. -func (s *pwSession) NonceTPM() TPM2BNonce { return TPM2BNonce{} } +func (s *pwSession) NonceTPM() tpm2b.Nonce { return tpm2b.Nonce{} } // NewNonceCaller updates the nonceCaller for this session. // Password sessions don't have nonces. func (s *pwSession) NewNonceCaller() error { return nil } // Computes the authorization structure for the session. -func (s *pwSession) Authorize(cc TPMCC, parms, addNonces []byte, _ []TPM2BName, _ int) (*TPMSAuthCommand, error) { - return &TPMSAuthCommand{ - Handle: TPMRSPW, - Nonce: TPM2BNonce{}, - Attributes: TPMASession{}, - Authorization: TPM2BData{ +func (s *pwSession) Authorize(cc tpm.CC, parms, addNonces []byte, _ []tpm2b.Name, _ int) (*tpms.AuthCommand, error) { + return &tpms.AuthCommand{ + Handle: tpm.RSPW, + Nonce: tpm2b.Nonce{}, + Attributes: tpma.Session{}, + Authorization: tpm2b.Data{ Buffer: s.auth, }, }, nil } // Validates the response session structure for the session. -func (s *pwSession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { +func (s *pwSession) Validate(rc tpm.RC, cc tpm.CC, parms []byte, _ []tpm2b.Name, _ int, auth *tpms.AuthResponse) error { if len(auth.Nonce.Buffer) != 0 { return fmt.Errorf("expected empty nonce in response auth to PW session, got %x", auth.Nonce) } - expectedAttrs := TPMASession{ + expectedAttrs := tpma.Session{ ContinueSession: true, } if auth.Attributes != expectedAttrs { @@ -151,29 +167,29 @@ func (s *pwSession) Decrypt(parameter []byte) error { return nil } // Handle returns the handle value associated with this session. // In the case of a password session, this is always TPM_RS_PW. -func (s *pwSession) Handle() TPMHandle { return TPMRSPW } +func (s *pwSession) Handle() tpm.Handle { return tpm.RSPW } // sessionOptions represents extra options used when setting up an HMAC or policy session. type sessionOptions struct { auth []byte password bool - bindHandle TPMIDHEntity - bindName TPM2BName + bindHandle tpmi.DHEntity + bindName tpm2b.Name bindAuth []byte - saltHandle TPMIDHObject - saltPub TPMTPublic - attrs TPMASession - symmetric TPMTSymDef + saltHandle tpmi.DHObject + saltPub tpmt.Public + attrs tpma.Session + symmetric tpmt.SymDef } // defaultOptions represents the default options used when none are provided. func defaultOptions() sessionOptions { return sessionOptions{ - symmetric: TPMTSymDef{ - Algorithm: TPMAlgNull, + symmetric: tpmt.SymDef{ + Algorithm: tpm.AlgNull, }, - bindHandle: TPMRHNull, - saltHandle: TPMRHNull, + bindHandle: tpm.RHNull, + saltHandle: tpm.RHNull, } } @@ -201,7 +217,7 @@ func Password(auth []byte) AuthOption { // Bound specifies that this session's session key should depend on the auth // value of the given object. -func Bound(handle TPMIDHEntity, name TPM2BName, auth []byte) AuthOption { +func Bound(handle tpmi.DHEntity, name tpm2b.Name, auth []byte) AuthOption { return func(o *sessionOptions) { o.bindHandle = handle o.bindName = name @@ -212,7 +228,7 @@ func Bound(handle TPMIDHEntity, name TPM2BName, auth []byte) AuthOption { // Salted specifies that this session's session key should depend on an // encrypted seed value using the given public key. // 'handle' must refer to a loaded RSA or ECC key. -func Salted(handle TPMIDHObject, pub TPMTPublic) AuthOption { +func Salted(handle tpmi.DHObject, pub tpmt.Public) AuthOption { return func(o *sessionOptions) { o.saltHandle = handle o.saltPub = pub @@ -236,17 +252,17 @@ const ( // the TPM. // Note that only commands whose first command/response parameter is a 2B can // support session encryption. -func AESEncryption(keySize TPMKeyBits, dir parameterEncryptionDirection) AuthOption { +func AESEncryption(keySize tpm.KeyBits, dir parameterEncryptionDirection) AuthOption { return func(o *sessionOptions) { o.attrs.Decrypt = (dir == EncryptIn || dir == EncryptInOut) o.attrs.Encrypt = (dir == EncryptOut || dir == EncryptInOut) - o.symmetric = TPMTSymDef{ - Algorithm: TPMAlgAES, - KeyBits: TPMUSymKeyBits{ - AES: NewTPMKeyBits(keySize), + o.symmetric = tpmt.SymDef{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(keySize), }, - Mode: TPMUSymMode{ - AES: NewTPMAlgID(TPMAlgCFB), + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), }, } } @@ -274,25 +290,25 @@ func AuditExclusive() AuthOption { // hmacSession generally implements the HMAC session. type hmacSession struct { sessionOptions - hash TPMIAlgHash + hash tpmi.AlgHash nonceSize int - handle TPMHandle + handle tpm.Handle sessionKey []byte // last nonceCaller - nonceCaller TPM2BNonce + nonceCaller tpm2b.Nonce // last nonceTPM - nonceTPM TPM2BNonce + nonceTPM tpm2b.Nonce } // HMAC sets up a just-in-time HMAC session that is used only once. // A real session is created, but just in time and it is flushed when used. -func HMAC(hash TPMIAlgHash, nonceSize int, opts ...AuthOption) Session { +func HMAC(hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) Session { // Set up a one-off session that knows the auth value. sess := hmacSession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, - handle: TPMRHNull, + handle: tpm.RHNull, } for _, opt := range opts { opt(&sess.sessionOptions) @@ -301,13 +317,13 @@ func HMAC(hash TPMIAlgHash, nonceSize int, opts ...AuthOption) Session { } // HMACSession sets up a reusable HMAC session that needs to be closed. -func HMACSession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { +func HMACSession(t *TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := hmacSession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, - handle: TPMRHNull, + handle: tpm.RHNull, } for _, opt := range opts { opt(&sess.sessionOptions) @@ -317,65 +333,67 @@ func HMACSession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) sess.sessionOptions.attrs.ContinueSession = true // Initialize the session. - if err := sess.Init(tpm); err != nil { + if err := sess.Init(t); err != nil { return nil, nil, err } closer := func() error { - flushCmd := FlushContextCommand{ - FlushHandle: sess.handle, - } - var flushRsp FlushContextResponse - return tpm.Execute(&flushCmd, &flushRsp) + fc := FlushContext{FlushHandle: sess.handle} + _, err := fc.Execute(t) + return err } return &sess, closer, nil } // Part 1, B.10.2 -func getEncryptedSaltRSA(nameAlg TPMIAlgHash, parms *TPMSRSAParms, pub *TPM2BPublicKeyRSA) (*TPM2BEncryptedSecret, []byte, error) { - rsaPub, err := rsaPub(parms, pub) +func getEncryptedSaltRSA(nameAlg tpmi.AlgHash, parms *tpms.RSAParms, pub *tpm2b.PublicKeyRSA) (*tpm2b.EncryptedSecret, []byte, error) { + rsaPub, err := helpers.RSAPub(parms, pub) if err != nil { return nil, nil, fmt.Errorf("could not encrypt salt to RSA key: %w", err) } // Odd special case: the size of the salt depends on the RSA scheme's // hash alg. - var hAlg TPMIAlgHash + var hAlg tpmi.AlgHash switch parms.Scheme.Scheme { - case TPMAlgRSASSA: + case tpm.AlgRSASSA: hAlg = parms.Scheme.Details.RSASSA.HashAlg - case TPMAlgRSAES: + case tpm.AlgRSAES: hAlg = nameAlg - case TPMAlgRSAPSS: + case tpm.AlgRSAPSS: hAlg = parms.Scheme.Details.RSAPSS.HashAlg - case TPMAlgOAEP: + case tpm.AlgOAEP: hAlg = parms.Scheme.Details.OAEP.HashAlg - case TPMAlgNull: + case tpm.AlgNull: hAlg = nameAlg default: return nil, nil, fmt.Errorf("unsupported RSA salt key scheme: %v", parms.Scheme.Scheme) } - salt := make([]byte, hAlg.Hash().Size()) + ha, err := hAlg.Hash() + if err != nil { + return nil, nil, err + } + salt := make([]byte, ha.Size()) if _, err := rand.Read(salt); err != nil { return nil, nil, fmt.Errorf("generating random salt: %w", err) } // Part 1, section 4.6 specifies the trailing NULL byte for the label. - encSalt, err := rsa.EncryptOAEP(hAlg.Hash().New(), rand.Reader, rsaPub, salt, []byte("SECRET\x00")) + encSalt, err := rsa.EncryptOAEP(ha.New(), rand.Reader, rsaPub, salt, []byte("SECRET\x00")) if err != nil { return nil, nil, fmt.Errorf("encrypting salt: %w", err) } - return &TPM2BEncryptedSecret{ + return &tpm2b.EncryptedSecret{ Buffer: encSalt, }, salt, nil } // Part 1, 19.6.13 -func getEncryptedSaltECC(nameAlg TPMIAlgHash, parms *TPMSECCParms, pub *TPMSECCPoint) (*TPM2BEncryptedSecret, []byte, error) { +func getEncryptedSaltECC(nameAlg tpmi.AlgHash, parms *tpms.ECCParms, pub *tpms.ECCPoint) (*tpm2b.EncryptedSecret, []byte, error) { curve, err := parms.CurveID.Curve() if err != nil { return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) } - eccPub, err := eccPub(parms, pub) + eccPub, err := helpers.ECCPub(parms, pub) if err != nil { return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) } @@ -383,31 +401,35 @@ func getEncryptedSaltECC(nameAlg TPMIAlgHash, parms *TPMSECCParms, pub *TPMSECCP if err != nil { return nil, nil, fmt.Errorf("could not encrypt salt to ECC key: %w", err) } - zx, _ := curve.Params().ScalarMult(eccPub.x, eccPub.y, ephPriv) + zx, _ := curve.Params().ScalarMult(eccPub.X, eccPub.Y, ephPriv) // ScalarMult returns a big.Int, whose Bytes() function may return the // compacted form. In our case, we want to left-pad zx to the size of // the curve. z := make([]byte, (curve.Params().BitSize+7)/8) zx.FillBytes(z) - salt := tpm2.KDFeHash(nameAlg.Hash(), z, "SECRET", ephPubX.Bytes(), pub.X.Buffer, nameAlg.Hash().Size()*8) + ha, err := nameAlg.Hash() + if err != nil { + return nil, nil, err + } + salt := legacy.KDFeHash(ha, z, "SECRET", ephPubX.Bytes(), pub.X.Buffer, ha.Size()*8) var encSalt bytes.Buffer binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubX.Bytes()))) encSalt.Write(ephPubX.Bytes()) binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubY.Bytes()))) encSalt.Write(ephPubY.Bytes()) - return &TPM2BEncryptedSecret{ + return &tpm2b.EncryptedSecret{ Buffer: encSalt.Bytes(), }, salt, nil } // getEncryptedSalt creates a salt value for salted sessions. // Returns the encrypted salt and plaintext salt, or an error value. -func getEncryptedSalt(pub TPMTPublic) (*TPM2BEncryptedSecret, []byte, error) { +func getEncryptedSalt(pub tpmt.Public) (*tpm2b.EncryptedSecret, []byte, error) { switch pub.Type { - case TPMAlgRSA: + case tpm.AlgRSA: return getEncryptedSaltRSA(pub.NameAlg, pub.Parameters.RSADetail, pub.Unique.RSA) - case TPMAlgECC: + case tpm.AlgECC: return getEncryptedSaltECC(pub.NameAlg, pub.Parameters.ECCDetail, pub.Unique.ECC) default: return nil, nil, fmt.Errorf("salt encryption alg '%v' not supported", pub.Type) @@ -415,15 +437,15 @@ func getEncryptedSalt(pub TPMTPublic) (*TPM2BEncryptedSecret, []byte, error) { } // Init initializes the session, just in time, if needed. -func (s *hmacSession) Init(tpm *TPM) error { - if s.handle != TPMRHNull { +func (s *hmacSession) Init(t *TPM) error { + if s.handle != tpm.RHNull { // Session is already initialized. return nil } // Get a high-quality nonceCaller for our use. // Store it with the session object for later reference. - s.nonceCaller = TPM2BNonce{ + s.nonceCaller = tpm2b.Nonce{ Buffer: make([]byte, s.nonceSize), } if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { @@ -431,64 +453,65 @@ func (s *hmacSession) Init(tpm *TPM) error { } // Start up the actual auth session. - sasCmd := StartAuthSessionCommand{ + sasCmd := StartAuthSession{ TPMKey: s.saltHandle, Bind: s.bindHandle, NonceCaller: s.nonceCaller, - SessionType: TPMSEHMAC, + SessionType: tpm.SEHMAC, Symmetric: s.symmetric, AuthHash: s.hash, } var salt []byte - if s.saltHandle != TPMRHNull { + if s.saltHandle != tpm.RHNull { var err error - var encSalt *TPM2BEncryptedSecret + var encSalt *tpm2b.EncryptedSecret encSalt, salt, err = getEncryptedSalt(s.saltPub) if err != nil { return err } sasCmd.EncryptedSalt = *encSalt } - var sasRsp StartAuthSessionResponse - if err := tpm.Execute(&sasCmd, &sasRsp); err != nil { + sasRsp, err := sasCmd.Execute(t) + if err != nil { return err } s.handle = sasRsp.SessionHandle s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 - if s.bindHandle != TPMRHNull || len(salt) != 0 { + ha, err := s.hash.Hash() + if err != nil { + return err + } + if s.bindHandle != tpm.RHNull || len(salt) != 0 { var authSalt []byte authSalt = append(authSalt, s.bindAuth...) authSalt = append(authSalt, salt...) - s.sessionKey = tpm2.KDFaHash(s.hash.Hash(), authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, s.hash.Hash().Size()*8) + s.sessionKey = legacy.KDFaHash(ha, authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, ha.Size()*8) } return nil } // Cleanup cleans up the session, if needed. -func (s *hmacSession) CleanupFailure(tpm *TPM) error { +func (s *hmacSession) CleanupFailure(t *TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil } - flushCmd := FlushContextCommand{ - FlushHandle: s.handle, - } - var flushRsp FlushContextResponse - if err := tpm.Execute(&flushCmd, &flushRsp); err != nil { + fc := FlushContext{FlushHandle: s.handle} + if _, err := fc.Execute(t); err != nil { return err } - s.handle = TPMRHNull + s.handle = tpm.RHNull return nil } // NonceTPM returns the last nonceTPM value from the session. // May be nil, if the session hasn't been initialized yet. -func (s *hmacSession) NonceTPM() TPM2BNonce { return s.nonceTPM } +func (s *hmacSession) NonceTPM() tpm2b.Nonce { return s.nonceTPM } // To avoid a circular dependency on gotpm by tpm2, implement a -// tiny serialization by hand for TPMASession here -func attrsToBytes(attrs TPMASession) []byte { +// tiny serialization by hand for tpma.Session here +func attrsToBytes(attrs tpma.Session) []byte { var res byte if attrs.ContinueSession { res |= (1 << 0) @@ -531,8 +554,12 @@ func attrsToBytes(attrs TPMASession) []byte { // used. // nonceOlder in a response is the corresponding nonceCaller sent in the // command. -func computeHMAC(alg TPMIAlgHash, key, pHash, nonceNewer, nonceOlder, addNonces []byte, attrs TPMASession) ([]byte, error) { - mac := hmac.New(alg.Hash().New, key) +func computeHMAC(alg tpmi.AlgHash, key, pHash, nonceNewer, nonceOlder, addNonces []byte, attrs tpma.Session) ([]byte, error) { + ha, err := alg.Hash() + if err != nil { + return nil, err + } + mac := hmac.New(ha.New, key) mac.Write(pHash) mac.Write(nonceNewer) mac.Write(nonceOlder) @@ -561,8 +588,8 @@ func (s *hmacSession) NewNonceCaller() error { // Authorize computes the authorization structure for the session. // Unlike the TPM spec, authIndex is zero-based. -func (s *hmacSession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) { - if s.handle == TPMRHNull { +func (s *hmacSession) Authorize(cc tpm.CC, parms, addNonces []byte, names []tpm2b.Name, authIndex int) (*tpms.AuthCommand, error) { + if s.handle == tpm.RHNull { // Session is not initialized. return nil, fmt.Errorf("session not initialized") } @@ -577,16 +604,19 @@ func (s *hmacSession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2B } // Compute the authorization HMAC. - hmac, err := computeHMAC(s.hash, hmacKey, cpHash(s.hash, cc, names, parms), - s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) + cph, err := cpHash(s.hash, cc, names, parms) + if err != nil { + return nil, err + } + hmac, err := computeHMAC(s.hash, hmacKey, cph, s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) if err != nil { return nil, err } - result := TPMSAuthCommand{ + result := tpms.AuthCommand{ Handle: s.handle, Nonce: s.nonceCaller, Attributes: s.attrs, - Authorization: TPM2BData{ + Authorization: tpm2b.Data{ Buffer: hmac, }, } @@ -595,12 +625,12 @@ func (s *hmacSession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2B // Validate validates the response session structure for the session. // It updates nonceTPM from the TPM's response. -func (s *hmacSession) Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error { +func (s *hmacSession) Validate(rc tpm.RC, cc tpm.CC, parms []byte, names []tpm2b.Name, authIndex int, auth *tpms.AuthResponse) error { // Track the new nonceTPM for the session. s.nonceTPM = auth.Nonce // Track the session being automatically flushed. if !auth.Attributes.ContinueSession { - s.handle = TPMRHNull + s.handle = tpm.RHNull } // Part 1, 19.6 @@ -613,8 +643,11 @@ func (s *hmacSession) Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BNa } // Compute the authorization HMAC. - mac, err := computeHMAC(s.hash, hmacKey, rpHash(s.hash, rc, cc, parms), - s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) + rph, err := rpHash(s.hash, rc, cc, parms) + if err != nil { + return err + } + mac, err := computeHMAC(s.hash, hmacKey, rph, s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) if err != nil { return err } @@ -647,7 +680,11 @@ func (s *hmacSession) Encrypt(parameter []byte) error { var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) - keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) + ha, err := s.hash.Hash() + if err != nil { + return err + } + keyIV := legacy.KDFaHash(ha, sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err @@ -670,7 +707,11 @@ func (s *hmacSession) Decrypt(parameter []byte) error { var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) - keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) + ha, err := s.hash.Hash() + if err != nil { + return err + } + keyIV := legacy.KDFaHash(ha, sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err @@ -683,25 +724,25 @@ func (s *hmacSession) Decrypt(parameter []byte) error { // Handle returns the handle value of the session. // If the session is created with HMAC (instead of HMACSession) this will be // TPM_RH_NULL. -func (s *hmacSession) Handle() TPMHandle { +func (s *hmacSession) Handle() tpm.Handle { return s.handle } // PolicyCallback represents an object's policy in the form of a function. // This function makes zero or more TPM policy commands and returns error. -type PolicyCallback = func(tpm *TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error +type PolicyCallback = func(tpm *TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error // policySession generally implements the policy session. type policySession struct { sessionOptions - hash TPMIAlgHash + hash tpmi.AlgHash nonceSize int - handle TPMHandle + handle tpm.Handle sessionKey []byte // last nonceCaller - nonceCaller TPM2BNonce + nonceCaller tpm2b.Nonce // last nonceTPM - nonceTPM TPM2BNonce + nonceTPM tpm2b.Nonce callback *PolicyCallback } @@ -710,13 +751,13 @@ type policySession struct { // Each time the policy is created, the callback is invoked to authorize the // session. // A real session is created, but just in time, and it is flushed when used. -func Policy(hash TPMIAlgHash, nonceSize int, callback PolicyCallback, opts ...AuthOption) Session { +func Policy(hash tpmi.AlgHash, nonceSize int, callback PolicyCallback, opts ...AuthOption) Session { // Set up a one-off session that knows the auth value. sess := policySession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, - handle: TPMRHNull, + handle: tpm.RHNull, callback: &callback, } for _, opt := range opts { @@ -729,13 +770,13 @@ func Policy(hash TPMIAlgHash, nonceSize int, callback PolicyCallback, opts ...Au // The caller is responsible to call whichever policy commands they want in the // session. // Note that the TPM resets a policy session after it is successfully used. -func PolicySession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { +func PolicySession(t *TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := policySession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, - handle: TPMRHNull, + handle: tpm.RHNull, } for _, opt := range opts { opt(&sess.sessionOptions) @@ -746,31 +787,29 @@ func PolicySession(tpm *TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption sess.sessionOptions.attrs.ContinueSession = true // Initialize the session. - if err := sess.Init(tpm); err != nil { + if err := sess.Init(t); err != nil { return nil, nil, err } closer := func() error { - flushCmd := FlushContextCommand{ - FlushHandle: sess.handle, - } - var flushRsp FlushContextResponse - return tpm.Execute(&flushCmd, &flushRsp) + fc := FlushContext{sess.handle} + _, err := fc.Execute(t) + return err } return &sess, closer, nil } // Init initializes the session, just in time, if needed. -func (s *policySession) Init(tpm *TPM) error { - if s.handle != TPMRHNull { +func (s *policySession) Init(t *TPM) error { + if s.handle != tpm.RHNull { // Session is already initialized. return nil } // Get a high-quality nonceCaller for our use. // Store it with the session object for later reference. - s.nonceCaller = TPM2BNonce{ + s.nonceCaller = tpm2b.Nonce{ Buffer: make([]byte, s.nonceSize), } if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { @@ -778,41 +817,45 @@ func (s *policySession) Init(tpm *TPM) error { } // Start up the actual auth session. - sasCmd := StartAuthSessionCommand{ + sasCmd := StartAuthSession{ TPMKey: s.saltHandle, Bind: s.bindHandle, NonceCaller: s.nonceCaller, - SessionType: TPMSEPolicy, + SessionType: tpm.SEPolicy, Symmetric: s.symmetric, AuthHash: s.hash, } var salt []byte - if s.saltHandle != TPMRHNull { + if s.saltHandle != tpm.RHNull { var err error - var encSalt *TPM2BEncryptedSecret + var encSalt *tpm2b.EncryptedSecret encSalt, salt, err = getEncryptedSalt(s.saltPub) if err != nil { return err } sasCmd.EncryptedSalt = *encSalt } - var sasRsp StartAuthSessionResponse - if err := tpm.Execute(&sasCmd, &sasRsp); err != nil { + sasRsp, err := sasCmd.Execute(t) + if err != nil { return err } s.handle = sasRsp.SessionHandle s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 - if s.bindHandle != TPMRHNull || len(salt) != 0 { + if s.bindHandle != tpm.RHNull || len(salt) != 0 { var authSalt []byte authSalt = append(authSalt, s.bindAuth...) authSalt = append(authSalt, salt...) - s.sessionKey = tpm2.KDFaHash(s.hash.Hash(), authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, s.hash.Hash().Size()*8) + ha, err := s.hash.Hash() + if err != nil { + return err + } + s.sessionKey = legacy.KDFaHash(ha, authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, ha.Size()*8) } // Call the callback to execute the policy, if needed if s.callback != nil { - if err := (*s.callback)(tpm, s.handle, s.nonceTPM); err != nil { + if err := (*s.callback)(t, s.handle, s.nonceTPM); err != nil { return fmt.Errorf("executing policy: %w", err) } } @@ -821,25 +864,22 @@ func (s *policySession) Init(tpm *TPM) error { } // CleanupFailure cleans up the session, if needed. -func (s *policySession) CleanupFailure(tpm *TPM) error { +func (s *policySession) CleanupFailure(t *TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil } - flushCmd := FlushContextCommand{ - FlushHandle: s.handle, - } - var flushRsp FlushContextResponse - if err := tpm.Execute(&flushCmd, &flushRsp); err != nil { + fc := FlushContext{FlushHandle: s.handle} + if _, err := fc.Execute(t); err != nil { return err } - s.handle = TPMRHNull + s.handle = tpm.RHNull return nil } // NonceTPM returns the last nonceTPM value from the session. // May be nil, if the session hasn't been initialized yet. -func (s *policySession) NonceTPM() TPM2BNonce { return s.nonceTPM } +func (s *policySession) NonceTPM() tpm2b.Nonce { return s.nonceTPM } // NewNonceCaller updates the nonceCaller for this session. func (s *policySession) NewNonceCaller() error { @@ -848,8 +888,8 @@ func (s *policySession) NewNonceCaller() error { } // Authorize computes the authorization structure for the session. -func (s *policySession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, _ int) (*TPMSAuthCommand, error) { - if s.handle == TPMRHNull { +func (s *policySession) Authorize(cc tpm.CC, parms, addNonces []byte, names []tpm2b.Name, _ int) (*tpms.AuthCommand, error) { + if s.handle == tpm.RHNull { // Session is not initialized. return nil, fmt.Errorf("session not initialized") } @@ -865,19 +905,21 @@ func (s *policySession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) // Compute the authorization HMAC. - var err error - hmac, err = computeHMAC(s.hash, hmacKey, cpHash(s.hash, cc, names, parms), - s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) + cph, err := cpHash(s.hash, cc, names, parms) + if err != nil { + return nil, err + } + hmac, err = computeHMAC(s.hash, hmacKey, cph, s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) if err != nil { return nil, err } } - result := TPMSAuthCommand{ + result := tpms.AuthCommand{ Handle: s.handle, Nonce: s.nonceCaller, Attributes: s.attrs, - Authorization: TPM2BData{ + Authorization: tpm2b.Data{ Buffer: hmac, }, } @@ -886,12 +928,12 @@ func (s *policySession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM // Validate valitades the response session structure for the session. // Updates nonceTPM from the TPM's response. -func (s *policySession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { +func (s *policySession) Validate(rc tpm.RC, cc tpm.CC, parms []byte, _ []tpm2b.Name, _ int, auth *tpms.AuthResponse) error { // Track the new nonceTPM for the session. s.nonceTPM = auth.Nonce // Track the session being automatically flushed. if !auth.Attributes.ContinueSession { - s.handle = TPMRHNull + s.handle = tpm.RHNull } if s.password { @@ -909,8 +951,11 @@ func (s *policySession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName hmacKey = append(hmacKey, s.sessionKey...) hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) // Compute the authorization HMAC. - mac, err := computeHMAC(s.hash, hmacKey, rpHash(s.hash, rc, cc, parms), - s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) + rph, err := rpHash(s.hash, rc, cc, parms) + if err != nil { + return err + } + mac, err := computeHMAC(s.hash, hmacKey, rph, s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) if err != nil { return err } @@ -944,7 +989,11 @@ func (s *policySession) Encrypt(parameter []byte) error { var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) - keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) + ha, err := s.hash.Hash() + if err != nil { + return err + } + keyIV := legacy.KDFaHash(ha, sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err @@ -967,7 +1016,11 @@ func (s *policySession) Decrypt(parameter []byte) error { var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) - keyIV := tpm2.KDFaHash(s.hash.Hash(), sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) + ha, err := s.hash.Hash() + if err != nil { + return err + } + keyIV := legacy.KDFaHash(ha, sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err @@ -980,6 +1033,6 @@ func (s *policySession) Decrypt(parameter []byte) error { // Handle returns the handle value of the session. // If the session is created with Policy (instead of PolicySession) this will be // TPM_RH_NULL. -func (s *policySession) Handle() TPMHandle { +func (s *policySession) Handle() tpm.Handle { return s.handle } diff --git a/tpm2/direct/commands.go b/direct/tpm2/tpm2.go similarity index 52% rename from tpm2/direct/commands.go rename to direct/tpm2/tpm2.go index cc4107ad..fdaa1ea1 100644 --- a/tpm2/direct/commands.go +++ b/direct/tpm2/tpm2.go @@ -1,18 +1,28 @@ -package direct +// package tpm2 contains TPM 2.0 commands +package tpm2 -import "encoding/binary" +import ( + "encoding/binary" + + "github.com/google/go-tpm/direct/structures/tpm" + "github.com/google/go-tpm/direct/structures/tpm2b" + "github.com/google/go-tpm/direct/structures/tpmi" + "github.com/google/go-tpm/direct/structures/tpml" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/structures/tpmt" +) // AuthHandle is a convenience type to wrap an authorized handle. type AuthHandle struct { // The handle that is authorized. // If zero, treated as TPM_RH_NULL. - Handle TPMIDHObject `gotpm:"nullable"` + Handle tpmi.DHObject `gotpm:"nullable"` // The Name of the object expected at the given handle value. // If Name contains a nil buffer, the effective Name will be // the big-endian UINT32 representation of Handle, as in // Part 1, section 16 "Names" for PCRs, sessions, and // permanent values. - Name TPM2BName `gotpm:"skip"` + Name tpm2b.Name `gotpm:"skip"` // The session used to authorize the object. // If the 'UserWithAuth' attribute is not set on the object, // must be a Policy session. @@ -27,22 +37,22 @@ type AuthHandle struct { // effectiveHandle returns the effective handle value. // Returns TPM_RH_NULL if unset. -func (a *AuthHandle) effectiveHandle() TPMIDHObject { +func (a *AuthHandle) effectiveHandle() tpmi.DHObject { if a.Handle != 0 { return a.Handle } - return TPMRHNull + return tpm.RHNull } // effectiveName returns the effective Name. // Returns the handle value as a name if unset. -func (a *AuthHandle) effectiveName() TPM2BName { +func (a *AuthHandle) effectiveName() tpm2b.Name { if len(a.Name.Buffer) > 0 { return a.Name } buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, uint32(a.effectiveHandle())) - return TPM2BName{buf} + return tpm2b.Name{Buffer: buf} } // effectiveAuth returns the effective auth session. @@ -56,9 +66,11 @@ func (a *AuthHandle) effectiveAuth() Session { // Command is a placeholder interface for TPM command structures so that they // can be easily distinguished from other types of structures. +// TODO: once go-tpm requires Go 1.18, parameterize this type for compile-time +// command/response matching. type Command interface { // The TPM command code associated with this command. - Command() TPMCC + Command() tpm.CC } // Response is a placeholder interface for TPM response structures so that they @@ -68,342 +80,450 @@ type Command interface { // See https://go.dev/blog/laws-of-reflection type Response interface { // The TPM command code associated with this response. - Response() TPMCC + Response() tpm.CC } -// StartAuthSessionCommand is the input to TPM2_StartAuthSession. +// StartAuthSession is the input to TPM2_StartAuthSession. // See definition in Part 3, Commands, section 11.1 -type StartAuthSessionCommand struct { +type StartAuthSession struct { // handle of a loaded decrypt key used to encrypt salt // may be TPM_RH_NULL - TPMKey TPMIDHObject `gotpm:"handle,nullable"` + TPMKey tpmi.DHObject `gotpm:"handle,nullable"` // entity providing the authValue // may be TPM_RH_NULL - Bind TPMIDHEntity `gotpm:"handle,nullable"` + Bind tpmi.DHEntity `gotpm:"handle,nullable"` // initial nonceCaller, sets nonceTPM size for the session // shall be at least 16 octets - NonceCaller TPM2BNonce + NonceCaller tpm2b.Nonce // value encrypted according to the type of tpmKey // If tpmKey is TPM_RH_NULL, this shall be the Empty Buffer. - EncryptedSalt TPM2BEncryptedSecret + EncryptedSalt tpm2b.EncryptedSecret // indicates the type of the session; simple HMAC or policy (including // a trial policy) - SessionType TPMSE + SessionType tpm.SE // the algorithm and key size for parameter encryption - // may select TPM_ALG_NULL - Symmetric TPMTSymDef + // may select *TPM_ALG_NULL + Symmetric tpmt.SymDef // hash algorithm to use for the session - // Shall be a hash algorithm supported by the TPM and not TPM_ALG_NULL - AuthHash TPMIAlgHash + // Shall be a hash algorithm supported by the TPM and not *TPM_ALG_NULL + AuthHash tpmi.AlgHash } // Command implements the Command interface. -func (*StartAuthSessionCommand) Command() TPMCC { return TPMCCStartAuthSession } +func (*StartAuthSession) Command() tpm.CC { return tpm.CCStartAuthSession } + +// Execute executes the command and returns the response. +func (cmd *StartAuthSession) Execute(t *TPM, s ...Session) (*StartAuthSessionResponse, error) { + var rsp StartAuthSessionResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // StartAuthSessionResponse is the response from TPM2_StartAuthSession. type StartAuthSessionResponse struct { // handle for the newly created session - SessionHandle TPMISHAuthSession `gotpm:"handle"` + SessionHandle tpmi.SHAuthSession `gotpm:"handle"` // the initial nonce from the TPM, used in the computation of the sessionKey - NonceTPM TPM2BNonce + NonceTPM tpm2b.Nonce } // Response implements the Response interface. -func (*StartAuthSessionResponse) Response() TPMCC { return TPMCCStartAuthSession } +func (*StartAuthSessionResponse) Response() tpm.CC { return tpm.CCStartAuthSession } -// CreateCommand is the input to TPM2_Create. +// Create is the input to TPM2_Create. // See definition in Part 3, Commands, section 12.1 -type CreateCommand struct { +type Create struct { // handle of parent for new object ParentHandle AuthHandle `gotpm:"handle,auth"` // the sensitive data - InSensitive TPM2BSensitiveCreate + InSensitive tpm2b.SensitiveCreate // the public template - InPublic TPM2BPublic + InPublic tpm2b.Public // data that will be included in the creation data for this // object to provide permanent, verifiable linkage between this // object and some object owner data - OutsideInfo TPM2BData + OutsideInfo tpm2b.Data // PCR that will be used in creation data - CreationPCR TPMLPCRSelection + CreationPCR tpml.PCRSelection } // Command implements the Command interface. -func (*CreateCommand) Command() TPMCC { return TPMCCCreate } +func (*Create) Command() tpm.CC { return tpm.CCCreate } + +// Execute executes the command and returns the response. +func (cmd *Create) Execute(t *TPM, s ...Session) (*CreateResponse, error) { + var rsp CreateResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // CreateResponse is the response from TPM2_Create. type CreateResponse struct { // the private portion of the object - OutPrivate TPM2BPrivate + OutPrivate tpm2b.Private // the public portion of the created object - OutPublic TPM2BPublic - // contains a TPMS_CREATION_DATA - CreationData TPM2BCreationData + OutPublic tpm2b.Public + // contains a tpms._CREATION_DATA + CreationData tpm2b.CreationData // digest of creationData using nameAlg of outPublic - CreationHash TPM2BDigest + CreationHash tpm2b.Digest // ticket used by TPM2_CertifyCreation() to validate that the // creation data was produced by the TPM - CreationTicket TPMTTKCreation + CreationTicket tpmt.TKCreation } // Response implements the Response interface. -func (*CreateResponse) Response() TPMCC { return TPMCCCreate } +func (*CreateResponse) Response() tpm.CC { return tpm.CCCreate } -// LoadCommand is the input to TPM2_Load. +// Load is the input to TPM2_Load. // See definition in Part 3, Commands, section 12.2 -type LoadCommand struct { +type Load struct { // handle of parent for new object ParentHandle AuthHandle `gotpm:"handle,auth"` // the private portion of the object - InPrivate TPM2BPrivate + InPrivate tpm2b.Private // the public portion of the object - InPublic TPM2BPublic + InPublic tpm2b.Public } // Command implements the Command interface. -func (*LoadCommand) Command() TPMCC { return TPMCCLoad } +func (*Load) Command() tpm.CC { return tpm.CCLoad } + +// Execute executes the command and returns the response. +func (cmd *Load) Execute(t *TPM, s ...Session) (*LoadResponse, error) { + var rsp LoadResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // LoadResponse is the response from TPM2_Load. type LoadResponse struct { // handle of type TPM_HT_TRANSIENT for loaded object - ObjectHandle TPMHandle `gotpm:"handle"` + ObjectHandle tpm.Handle `gotpm:"handle"` // Name of the loaded object - Name TPM2BName + Name tpm2b.Name } // Response implements the Response interface. -func (*LoadResponse) Response() TPMCC { return TPMCCLoad } +func (*LoadResponse) Response() tpm.CC { return tpm.CCLoad } -// UnsealCommand is the input to TPM2_Unseal. +// Unseal is the input to TPM2_Unseal. // See definition in Part 3, Commands, section 12.7 -type UnsealCommand struct { +type Unseal struct { ItemHandle AuthHandle `gotpm:"handle,auth"` } // Command implements the Command interface. -func (*UnsealCommand) Command() TPMCC { return TPMCCUnseal } +func (*Unseal) Command() tpm.CC { return tpm.CCUnseal } + +// Execute executes the command and returns the response. +func (cmd *Unseal) Execute(t *TPM, s ...Session) (*UnsealResponse, error) { + var rsp UnsealResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // UnsealResponse is the response from TPM2_Unseal. type UnsealResponse struct { - OutData TPM2BSensitiveData + OutData tpm2b.SensitiveData } // Response implements the Response interface. -func (*UnsealResponse) Response() TPMCC { return TPMCCUnseal } +func (*UnsealResponse) Response() tpm.CC { return tpm.CCUnseal } -// QuoteCommand is the input to TPM2_Quote. +// Quote is the input to TPM2_Quote. // See definition in Part 3, Commands, section 18.4 -type QuoteCommand struct { +type Quote struct { // handle of key that will perform signature SignHandle AuthHandle `gotpm:"handle,auth"` // data supplied by the caller - QualifyingData TPM2BData + QualifyingData tpm2b.Data // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL - InScheme TPMTSigScheme + InScheme tpmt.SigScheme // PCR set to quote - PCRSelect TPMLPCRSelection + PCRSelect tpml.PCRSelection } // Command implements the Command interface. -func (*QuoteCommand) Command() TPMCC { return TPMCCQuote } +func (*Quote) Command() tpm.CC { return tpm.CCQuote } + +// Execute executes the command and returns the response. +func (cmd *Quote) Execute(t *TPM, s ...Session) (*QuoteResponse, error) { + var rsp QuoteResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // QuoteResponse is the response from TPM2_Quote. type QuoteResponse struct { // the quoted information - Quoted TPM2BAttest + Quoted tpm2b.Attest // the signature over quoted - Signature TPMTSignature + Signature tpmt.Signature } // Response implements the Response interface. -func (*QuoteResponse) Response() TPMCC { return TPMCCQuote } +func (*QuoteResponse) Response() tpm.CC { return tpm.CCQuote } -// GetSessionAuditDigestCommand is the input to TPM2_GetSessionAuditDigest. +// GetSessionAuditDigest is the input to TPM2_GetSessionAuditDigest. // See definition in Part 3, Commands, section 18.5 -type GetSessionAuditDigestCommand struct { +type GetSessionAuditDigest struct { // handle of the privacy administrator (TPM_RH_ENDORSEMENT) PrivacyAdminHandle AuthHandle `gotpm:"handle,auth"` // handle of the signing key SignHandle AuthHandle `gotpm:"handle,auth"` // handle of the audit session - SessionHandle TPMISHHMAC `gotpm:"handle"` + SessionHandle tpmi.SHHMAC `gotpm:"handle"` // user-provided qualifying data – may be zero-length - QualifyingData TPM2BData + QualifyingData tpm2b.Data // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL - InScheme TPMTSigScheme + InScheme tpmt.SigScheme } // Command implements the Command interface. -func (*GetSessionAuditDigestCommand) Command() TPMCC { return TPMCCGetSessionAuditDigest } +func (*GetSessionAuditDigest) Command() tpm.CC { return tpm.CCGetSessionAuditDigest } + +// Execute executes the command and returns the response. +func (cmd *GetSessionAuditDigest) Execute(t *TPM, s ...Session) (*GetSessionAuditDigestResponse, error) { + var rsp GetSessionAuditDigestResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // GetSessionAuditDigestResponse is the response from // TPM2_GetSessionAuditDigest. type GetSessionAuditDigestResponse struct { // the audit information that was signed - AuditInfo TPM2BAttest + AuditInfo tpm2b.Attest // the signature over auditInfo - Signature TPMTSignature + Signature tpmt.Signature } // Response implements the Response interface. -func (*GetSessionAuditDigestResponse) Response() TPMCC { return TPMCCGetSessionAuditDigest } +func (*GetSessionAuditDigestResponse) Response() tpm.CC { return tpm.CCGetSessionAuditDigest } -// PCRExtendCommand is the input to TPM2_PCR_Extend. +// PCRExtend is the input to TPM2_PCR_Extend. // See definition in Part 3, Commands, section 22.2 -type PCRExtendCommand struct { +type PCRExtend struct { // handle of the PCR PCRHandle AuthHandle `gotpm:"handle,auth"` // list of tagged digest values to be extended - Digests TPMLDigestValues + Digests tpml.DigestValues } // Command implements the Command interface. -func (*PCRExtendCommand) Command() TPMCC { return TPMCCPCRExtend } +func (*PCRExtend) Command() tpm.CC { return tpm.CCPCRExtend } + +// Execute executes the command and returns the response. +func (cmd *PCRExtend) Execute(t *TPM, s ...Session) (*PCRExtendResponse, error) { + var rsp PCRExtendResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // PCRExtendResponse is the response from TPM2_PCR_Extend. type PCRExtendResponse struct { } // Response implements the Response interface. -func (*PCRExtendResponse) Response() TPMCC { return TPMCCPCRExtend } +func (*PCRExtendResponse) Response() tpm.CC { return tpm.CCPCRExtend } -// PCREventCommand is the input to TPM2_PCR_Event. +// PCREvent is the input to TPM2_PCR_Event. // See definition in Part 3, Commands, section 22.3 -type PCREventCommand struct { +type PCREvent struct { // Handle of the PCR PCRHandle AuthHandle `gotpm:"handle,auth"` // Event data in sized buffer - EventData TPM2BEvent + EventData tpm2b.Event } // Command implements the Command interface. -func (*PCREventCommand) Command() TPMCC { return TPMCCPCREvent } +func (*PCREvent) Command() tpm.CC { return tpm.CCPCREvent } + +// Execute executes the command and returns the response. +func (cmd *PCREvent) Execute(t *TPM, s ...Session) (*PCREventResponse, error) { + var rsp PCREventResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // PCREventResponse is the response from TPM2_PCR_Event. type PCREventResponse struct { } // Response implements the Response interface. -func (*PCREventResponse) Response() TPMCC { return TPMCCPCREvent } +func (*PCREventResponse) Response() tpm.CC { return tpm.CCPCREvent } -// PCRReadCommand is the input to TPM2_PCR_Read. +// PCRRead is the input to TPM2_PCR_Read. // See definition in Part 3, Commands, section 22.4 -type PCRReadCommand struct { +type PCRRead struct { // The selection of PCR to read - PCRSelectionIn TPMLPCRSelection + PCRSelectionIn tpml.PCRSelection } // Command implements the Command interface. -func (*PCRReadCommand) Command() TPMCC { return TPMCCPCRRead } +func (*PCRRead) Command() tpm.CC { return tpm.CCPCRRead } + +// Execute executes the command and returns the response. +func (cmd *PCRRead) Execute(t *TPM, s ...Session) (*PCRReadResponse, error) { + var rsp PCRReadResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // PCRReadResponse is the response from TPM2_PCR_Read. type PCRReadResponse struct { // the current value of the PCR update counter PCRUpdateCounter uint32 // the PCR in the returned list - PCRSelectionOut TPMLPCRSelection + PCRSelectionOut tpml.PCRSelection // the contents of the PCR indicated in pcrSelectOut-> pcrSelection[] as tagged digests - PCRValues TPMLDigest + PCRValues tpml.Digest } // Response implements the Response interface. -func (*PCRReadResponse) Response() TPMCC { return TPMCCPCRRead } +func (*PCRReadResponse) Response() tpm.CC { return tpm.CCPCRRead } -// PolicySecretCommand is the input to TPM2_PolicySecret. +// PolicySecret is the input to TPM2_PolicySecret. // See definition in Part 3, Commands, section 23.4 -type PolicySecretCommand struct { +type PolicySecret struct { // handle for an entity providing the authorization AuthHandle AuthHandle `gotpm:"handle,auth"` // handle for the policy session being extended - PolicySession TPMISHPolicy `gotpm:"handle"` + PolicySession tpmi.SHPolicy `gotpm:"handle"` // the policy nonce for the session - NonceTPM TPM2BNonce + NonceTPM tpm2b.Nonce // digest of the command parameters to which this authorization is limited - CPHashA TPM2BDigest + CPHashA tpm2b.Digest // a reference to a policy relating to the authorization – may be the Empty Buffer - PolicyRef TPM2BNonce + PolicyRef tpm2b.Nonce // time when authorization will expire, measured in seconds from the time // that nonceTPM was generated Expiration int32 } // Command implements the Command interface. -func (*PolicySecretCommand) Command() TPMCC { return TPMCCPolicySecret } +func (*PolicySecret) Command() tpm.CC { return tpm.CCPolicySecret } + +// Execute executes the command and returns the response. +func (cmd *PolicySecret) Execute(t *TPM, s ...Session) (*PolicySecretResponse, error) { + var rsp PolicySecretResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // PolicySecretResponse is the response from TPM2_PolicySecret. type PolicySecretResponse struct { // implementation-specific time value used to indicate to the TPM when the ticket expires - Timeout TPM2BTimeout + Timeout tpm2b.Timeout // produced if the command succeeds and expiration in the command was non-zero - PolicyTicket TPMTTKAuth + PolicyTicket tpmt.TKAuth } // Response implements the Response interface. -func (*PolicySecretResponse) Response() TPMCC { return TPMCCPolicySecret } +func (*PolicySecretResponse) Response() tpm.CC { return tpm.CCPolicySecret } -// CreatePrimaryCommand is the input to TPM2_CreatePrimary. +// CreatePrimary is the input to TPM2_CreatePrimary. // See definition in Part 3, Commands, section 24.1 -type CreatePrimaryCommand struct { +type CreatePrimary struct { // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, // or TPM_RH_NULL PrimaryHandle AuthHandle `gotpm:"handle,auth"` // the sensitive data - InSensitive TPM2BSensitiveCreate + InSensitive tpm2b.SensitiveCreate // the public template - InPublic TPM2BPublic + InPublic tpm2b.Public // data that will be included in the creation data for this // object to provide permanent, verifiable linkage between this // object and some object owner data - OutsideInfo TPM2BData + OutsideInfo tpm2b.Data // PCR that will be used in creation data - CreationPCR TPMLPCRSelection + CreationPCR tpml.PCRSelection } // Command implements the Command interface. -func (*CreatePrimaryCommand) Command() TPMCC { return TPMCCCreatePrimary } +func (*CreatePrimary) Command() tpm.CC { return tpm.CCCreatePrimary } + +// Execute executes the command and returns the response. +func (cmd *CreatePrimary) Execute(t *TPM, s ...Session) (*CreatePrimaryResponse, error) { + var rsp CreatePrimaryResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // CreatePrimaryResponse is the response from TPM2_CreatePrimary. type CreatePrimaryResponse struct { // handle of type TPM_HT_TRANSIENT for created Primary Object - ObjectHandle TPMHandle `gotpm:"handle"` + ObjectHandle tpm.Handle `gotpm:"handle"` // the public portion of the created object - OutPublic TPM2BPublic - // contains a TPMS_CREATION_DATA - CreationData TPM2BCreationData + OutPublic tpm2b.Public + // contains a tpms._CREATION_DATA + CreationData tpm2b.CreationData // digest of creationData using nameAlg of outPublic - CreationHash TPM2BDigest + CreationHash tpm2b.Digest // ticket used by TPM2_CertifyCreation() to validate that the // creation data was produced by the TPM - CreationTicket TPMTTKCreation + CreationTicket tpmt.TKCreation // the name of the created object - Name TPM2BName + Name tpm2b.Name } // Response implements the Response interface. -func (*CreatePrimaryResponse) Response() TPMCC { return TPMCCCreatePrimary } +func (*CreatePrimaryResponse) Response() tpm.CC { return tpm.CCCreatePrimary } -// FlushContextCommand is the input to TPM2_FlushContext. +// FlushContext is the input to TPM2_FlushContext. // See definition in Part 3, Commands, section 28.4 -type FlushContextCommand struct { +type FlushContext struct { // the handle of the item to flush - FlushHandle TPMIDHContext + FlushHandle tpmi.DHContext } // Command implements the Command interface. -func (*FlushContextCommand) Command() TPMCC { return TPMCCFlushContext } +func (*FlushContext) Command() tpm.CC { return tpm.CCFlushContext } + +// Execute executes the command and returns the response. +func (cmd *FlushContext) Execute(t *TPM, s ...Session) (*FlushContextResponse, error) { + var rsp FlushContextResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // FlushContextResponse is the response from TPM2_FlushContext. type FlushContextResponse struct { } // Response implements the Response interface. -func (*FlushContextResponse) Response() TPMCC { return TPMCCFlushContext } +func (*FlushContextResponse) Response() tpm.CC { return tpm.CCFlushContext } -// GetCapabilityCommand is the input to TPM2_GetCapability. +// GetCapability is the input to TPM2_GetCapability. // See definition in Part 3, Commands, section 30.2 -type GetCapabilityCommand struct { +type GetCapability struct { // group selection; determines the format of the response - Capability TPMCap + Capability tpm.Cap // further definition of information Property uint32 // number of properties of the indicated type to return @@ -411,15 +531,24 @@ type GetCapabilityCommand struct { } // Command implements the Command interface. -func (*GetCapabilityCommand) Command() TPMCC { return TPMCCGetCapability } +func (*GetCapability) Command() tpm.CC { return tpm.CCGetCapability } + +// Execute executes the command and returns the response. +func (cmd *GetCapability) Execute(t *TPM, s ...Session) (*GetCapabilityResponse, error) { + var rsp GetCapabilityResponse + if err := t.execute(cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} // GetCapabilityResponse is the response from TPM2_GetCapability. type GetCapabilityResponse struct { // flag to indicate if there are more values of this type - MoreData TPMIYesNo + MoreData tpmi.YesNo // the capability data - CapabilityData TPMSCapabilityData + CapabilityData tpms.CapabilityData } // Response implements the Response interface. -func (*GetCapabilityResponse) Response() TPMCC { return TPMCCGetCapability } +func (*GetCapabilityResponse) Response() tpm.CC { return tpm.CCGetCapability } diff --git a/tpm2/direct/crypto.go b/tpm2/direct/crypto.go deleted file mode 100644 index 179fbb27..00000000 --- a/tpm2/direct/crypto.go +++ /dev/null @@ -1,43 +0,0 @@ -package direct - -import ( - "crypto/elliptic" - "crypto/rsa" - "math/big" -) - -// rsaPub converts a TPM RSA public key into one recognized by the rsa package. -func rsaPub(parms *TPMSRSAParms, pub *TPM2BPublicKeyRSA) (*rsa.PublicKey, error) { - result := rsa.PublicKey{ - N: big.NewInt(0).SetBytes(pub.Buffer), - E: int(parms.Exponent), - } - // TPM considers 65537 to be the default RSA public exponent, and 0 in - // the parms - // indicates so. - if result.E == 0 { - result.E = 65537 - } - return &result, nil -} - -// ecdh is a convenience wrapper around the necessary info to perform point -// multiplication with the elliptic package. -type ecdhPub struct { - curve elliptic.Curve - x, y *big.Int -} - -// eccPub converts a TPM ECC public key into one recognized by the elliptic -// package's point-multiplication functions, for use in ECDH. -func eccPub(parms *TPMSECCParms, pub *TPMSECCPoint) (*ecdhPub, error) { - curve, err := parms.CurveID.Curve() - if err != nil { - return nil, err - } - return &ecdhPub{ - curve: curve, - x: big.NewInt(0).SetBytes(pub.X.Buffer), - y: big.NewInt(0).SetBytes(pub.Y.Buffer), - }, nil -} diff --git a/tpm2/direct/templates.go b/tpm2/direct/templates.go deleted file mode 100644 index 53f82ae5..00000000 --- a/tpm2/direct/templates.go +++ /dev/null @@ -1,190 +0,0 @@ -package direct - -var ( - // RSASRKTemplate contains the TCG reference RSA-2048 SRK template. - // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf - RSASRKTemplate = TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgRSA, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ - FixedTPM: true, - STClear: false, - FixedParent: true, - SensitiveDataOrigin: true, - UserWithAuth: true, - AdminWithPolicy: false, - NoDA: true, - EncryptedDuplication: false, - Restricted: true, - Decrypt: true, - SignEncrypt: false, - }, - Parameters: TPMUPublicParms{ - RSADetail: &TPMSRSAParms{ - Symmetric: TPMTSymDefObject{ - Algorithm: TPMAlgAES, - KeyBits: TPMUSymKeyBits{ - AES: NewTPMKeyBits(128), - }, - Mode: TPMUSymMode{ - AES: NewTPMAlgID(TPMAlgCFB), - }, - }, - KeyBits: 2048, - }, - }, - Unique: TPMUPublicID{ - RSA: &TPM2BPublicKeyRSA{ - Buffer: make([]byte, 256), - }, - }, - }, - } - // RSAEKTemplate contains the TCG reference RSA-2048 EK template. - RSAEKTemplate = TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgRSA, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ - FixedTPM: true, - STClear: false, - FixedParent: true, - SensitiveDataOrigin: true, - UserWithAuth: false, - AdminWithPolicy: true, - NoDA: false, - EncryptedDuplication: false, - Restricted: true, - Decrypt: true, - SignEncrypt: false, - }, - AuthPolicy: TPM2BDigest{ - Buffer: []byte{ - // TPM2_PolicySecret(RH_ENDORSEMENT) - 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, - 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, - 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, - 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, - }, - }, - Parameters: TPMUPublicParms{ - RSADetail: &TPMSRSAParms{ - Symmetric: TPMTSymDefObject{ - Algorithm: TPMAlgAES, - KeyBits: TPMUSymKeyBits{ - AES: NewTPMKeyBits(128), - }, - Mode: TPMUSymMode{ - AES: NewTPMAlgID(TPMAlgCFB), - }, - }, - KeyBits: 2048, - }, - }, - Unique: TPMUPublicID{ - RSA: &TPM2BPublicKeyRSA{ - Buffer: make([]byte, 256), - }, - }, - }, - } - // ECCSRKTemplate contains the TCG reference ECC-P256 SRK template. - // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf - ECCSRKTemplate = 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: true, - SignEncrypt: false, - }, - Parameters: TPMUPublicParms{ - ECCDetail: &TPMSECCParms{ - Symmetric: TPMTSymDefObject{ - Algorithm: TPMAlgAES, - KeyBits: TPMUSymKeyBits{ - AES: NewTPMKeyBits(128), - }, - Mode: TPMUSymMode{ - AES: NewTPMAlgID(TPMAlgCFB), - }, - }, - CurveID: TPMECCNistP256, - }, - }, - Unique: TPMUPublicID{ - ECC: &TPMSECCPoint{ - X: TPM2BECCParameter{ - Buffer: make([]byte, 32), - }, - Y: TPM2BECCParameter{ - Buffer: make([]byte, 32), - }, - }, - }, - }, - } - // ECCEKTemplate contains the TCG reference ECC-P256 EK template. - ECCEKTemplate = TPM2BPublic{ - PublicArea: TPMTPublic{ - Type: TPMAlgECC, - NameAlg: TPMAlgSHA256, - ObjectAttributes: TPMAObject{ - FixedTPM: true, - STClear: false, - FixedParent: true, - SensitiveDataOrigin: true, - UserWithAuth: false, - AdminWithPolicy: true, - NoDA: false, - EncryptedDuplication: false, - Restricted: true, - Decrypt: true, - SignEncrypt: false, - }, - AuthPolicy: TPM2BDigest{ - Buffer: []byte{ - // TPM2_PolicySecret(RH_ENDORSEMENT) - 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, - 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, - 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, - 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, - }, - }, - Parameters: TPMUPublicParms{ - ECCDetail: &TPMSECCParms{ - Symmetric: TPMTSymDefObject{ - Algorithm: TPMAlgAES, - KeyBits: TPMUSymKeyBits{ - AES: NewTPMKeyBits(128), - }, - Mode: TPMUSymMode{ - AES: NewTPMAlgID(TPMAlgCFB), - }, - }, - CurveID: TPMECCNistP256, - }, - }, - Unique: TPMUPublicID{ - ECC: &TPMSECCPoint{ - X: TPM2BECCParameter{ - Buffer: make([]byte, 32), - }, - Y: TPM2BECCParameter{ - Buffer: make([]byte, 32), - }, - }, - }, - }, - } -) diff --git a/tpm2/direct/wrappers.go b/tpm2/direct/wrappers.go deleted file mode 100644 index 5a304032..00000000 --- a/tpm2/direct/wrappers.go +++ /dev/null @@ -1,10 +0,0 @@ -package direct - -// This file provides wrapper functions for concrete types used by tpm2, for -// setting union member pointers. - -// NewTPMKeyBits allocates and returns the address of a new TPMKeyBits. -func NewTPMKeyBits(v TPMKeyBits) *TPMKeyBits { return &v } - -// NewTPMAlgID allocates and returns the address of a new TPMAlgID. -func NewTPMAlgID(v TPMAlgID) *TPMAlgID { return &v } From 61d3178e82ba247805118dffa8d87405b6421b87 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Sat, 19 Feb 2022 00:22:30 +0000 Subject: [PATCH 8/9] Draft implementation of Joe's embedded-reserved-field bitwise solution --- direct/structures/internal/bitfield.go | 83 +++++++++++++ direct/structures/internal/structures.go | 28 +---- direct/structures/tpma/tpma.go | 4 + direct/tpm2/reflect.go | 142 +++++++++++++---------- direct/tpm2/reflect_test.go | 115 +++++++++++------- direct/tpm2/sessions.go | 6 - go.mod | 2 + 7 files changed, 250 insertions(+), 130 deletions(-) create mode 100644 direct/structures/internal/bitfield.go diff --git a/direct/structures/internal/bitfield.go b/direct/structures/internal/bitfield.go new file mode 100644 index 00000000..41fd4ec4 --- /dev/null +++ b/direct/structures/internal/bitfield.go @@ -0,0 +1,83 @@ +package internal + +import ( + "fmt" +) + +// Bitfield represents a TPM bitfield (i.e., TPMA_*) type. +type Bitfield interface { + // Length returns the length of the bitfield. + Length() int +} + +// BitGetter represents a TPM bitfield (i.e., TPMA_*) type that can be read. +type BitGetter interface { + Bitfield + // GetReservedBit returns the value of the given reserved bit. + // If the bit is not reserved, returns false. + GetReservedBit(pos int) bool +} + +// BitSetter represents a TPM bitfield (i.e., TPMA_*) type that can be written. +type BitSetter interface { + Bitfield + // GetReservedBit sets the value of the given reserved bit. + SetReservedBit(pos int, val bool) +} + +func checkPos(pos int, len int) { + if pos >= len || pos < 0 { + panic(fmt.Errorf("bit %d out of range for %d-bit field", pos, len)) + } +} + +// bitfield8 represents an 8-bit bitfield which may have reserved bits. +// 8-bit TPMA_* types embed this one, and the reserved bits are stored in it. +type bitfield8 uint8 + +// Length implements the Bitfield interface. +func (bitfield8) Length() int { + return 8 +} + +// GetReservedBit implements the BitGetter interface. +func (r bitfield8) GetReservedBit(pos int) bool { + checkPos(pos, 8) + return r&(1< 0 { - bitwise := hasTag(v.Type().Field(0), "bit") - for i := 0; i < v.NumField(); i++ { - thisBitwise := hasTag(v.Type().Field(i), "bit") - if thisBitwise { - if hasTag(v.Type().Field(i), "sized") || hasTag(v.Type().Field(i), "sized8") { - return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", - v.Type().Name(), v.Type().Field(i).Name) - } - if hasTag(v.Type().Field(i), "tag") { - return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", - v.Type().Name(), v.Type().Field(i).Name) - } + numBitwise := 0 + numChecked := 0 + for i := 0; i < v.NumField(); i++ { + // Ignore embedded Bitfield hints. + if !v.Type().Field(i).IsExported() { + //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { + continue + } + thisBitwise := hasTag(v.Type().Field(i), "bit") + if thisBitwise { + numBitwise++ + if hasTag(v.Type().Field(i), "sized") || hasTag(v.Type().Field(i), "sized8") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", + v.Type().Name(), v.Type().Field(i).Name) } - if bitwise != thisBitwise { - return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + if hasTag(v.Type().Field(i), "tag") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", + v.Type().Name(), v.Type().Field(i).Name) } } - if bitwise { - return marshalBitwise(buf, v) - } + numChecked++ + } + if numBitwise != numChecked && numBitwise != 0 { + return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + } + if numBitwise > 0 { + return marshalBitwise(buf, v) } // Make a pass to create a map of tag values // UInt64-valued fields with values greater than MaxInt64 cannot be @@ -304,21 +311,16 @@ func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Marshals a bitwise-defined struct. func marshalBitwise(buf *bytes.Buffer, v reflect.Value) error { - maxBit := 0 - for i := 0; i < v.NumField(); i++ { - high, _, ok := rangeTag(v.Type().Field(i), "bit") - if !ok { - return fmt.Errorf("'%v' struct member '%v' did not specify a bit index or range", v.Type().Name(), v.Type().Field(i).Name) - } - if high > maxBit { - maxBit = high - } - } - if (maxBit+1)%8 != 0 { - return fmt.Errorf("'%v' bitwise members did not total up to a multiple of 8 bits", v.Type().Name()) + bg, ok := v.Interface().(tpma.BitGetter) + if !ok { + return fmt.Errorf("'%v' was not a BitGetter", v.Type().Name()) } - bitArray := make([]bool, maxBit+1) + bitArray := make([]bool, bg.Length()) + // Marshal the defined fields for i := 0; i < v.NumField(); i++ { + if !v.Type().Field(i).IsExported() { + continue + } high, low, _ := rangeTag(v.Type().Field(i), "bit") var buf bytes.Buffer if err := marshal(&buf, v.Field(i)); err != nil { @@ -329,6 +331,12 @@ func marshalBitwise(buf *bytes.Buffer, v reflect.Value) error { bitArray[low+i] = ((b[len(b)-i/8-1] >> (i % 8)) & 1) == 1 } } + // Also marshal the reserved values + for i := 0; i < len(bitArray); i++ { + if bg.GetReservedBit(i) { + bitArray[i] = true + } + } result := make([]byte, len(bitArray)/8) for i, bit := range bitArray { if bit { @@ -453,28 +461,35 @@ func unmarshalArray(buf *bytes.Buffer, v reflect.Value) error { func unmarshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Check if this is a bitwise-defined structure. This requires all the - // members to be bitwise-defined. - if v.NumField() > 0 { - bitwise := hasTag(v.Type().Field(0), "bit") - for i := 0; i < v.NumField(); i++ { - thisBitwise := hasTag(v.Type().Field(i), "bit") - if thisBitwise { - if hasTag(v.Type().Field(i), "sized") { - return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", - v.Type().Name(), v.Type().Field(i).Name) - } - if hasTag(v.Type().Field(i), "tag") { - return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", - v.Type().Name(), v.Type().Field(i).Name) - } + // exported members to be bitwise-defined. + numBitwise := 0 + numChecked := 0 + for i := 0; i < v.NumField(); i++ { + // Ignore embedded Bitfield hints. + // Ignore embedded Bitfield hints. + if !v.Type().Field(i).IsExported() { + //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { + continue + } + thisBitwise := hasTag(v.Type().Field(i), "bit") + if thisBitwise { + numBitwise++ + if hasTag(v.Type().Field(i), "sized") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", + v.Type().Name(), v.Type().Field(i).Name) } - if bitwise != thisBitwise { - return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + if hasTag(v.Type().Field(i), "tag") { + return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", + v.Type().Name(), v.Type().Field(i).Name) } } - if bitwise { - return unmarshalBitwise(buf, v) - } + numChecked++ + } + if numBitwise != numChecked && numBitwise != 0 { + return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) + } + if numBitwise > 0 { + return unmarshalBitwise(buf, v) } for i := 0; i < v.NumField(); i++ { if hasTag(v.Type().Field(i), "skip") { @@ -570,20 +585,11 @@ func unmarshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Unmarshals a bitwise-defined struct. func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { - maxBit := 0 - for i := 0; i < v.NumField(); i++ { - high, _, ok := rangeTag(v.Type().Field(i), "bit") - if !ok { - return fmt.Errorf("'%v' struct member '%v' did not specify a bit index or range", v.Type().Name(), v.Type().Field(i).Name) - } - if high > maxBit { - maxBit = high - } - } - if (maxBit+1)%8 != 0 { - return fmt.Errorf("'%v' bitwise members did not total up to a multiple of 8 bits", v.Type().Name()) + bs, ok := v.Addr().Interface().(tpma.BitSetter) + if !ok { + return fmt.Errorf("'%v' was not a BitSetter", v.Addr().Type()) } - bitArray := make([]bool, maxBit+1) + bitArray := make([]bool, bs.Length()) // We will read big-endian, starting from the last byte and working our // way down. for i := len(bitArray)/8 - 1; i >= 0; i-- { @@ -596,13 +602,19 @@ func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { bitArray[8*i+j] = (((b >> j) & 1) == 1) } } + // Unmarshal the defined fields and clear the bits from the array as we + // read them. for i := 0; i < v.NumField(); i++ { + if !v.Type().Field(i).IsExported() { + continue + } high, low, _ := rangeTag(v.Type().Field(i), "bit") var val uint64 for j := 0; j <= high-low; j++ { if bitArray[low+j] { val |= (1 << j) } + bitArray[low+j] = false } if v.Field(i).Kind() == reflect.Bool { v.Field(i).SetBool((val & 1) == 1) @@ -610,6 +622,10 @@ func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { v.Field(i).SetUint(val) } } + // Unmarshal the remaining uncleared bits as reserved bits. + for i := 0; i < len(bitArray); i++ { + bs.SetReservedBit(i, bitArray[i]) + } return nil } diff --git a/direct/tpm2/reflect_test.go b/direct/tpm2/reflect_test.go index 68ca6e8b..676975f3 100644 --- a/direct/tpm2/reflect_test.go +++ b/direct/tpm2/reflect_test.go @@ -7,6 +7,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/google/go-tpm/direct/structures/tpma" ) func marshalUnmarshal(t *testing.T, v interface{}, want []byte) { @@ -21,8 +24,12 @@ func marshalUnmarshal(t *testing.T, v interface{}, want []byte) { if err != nil { t.Fatalf("want nil, got %v", err) } - if !cmp.Equal(v, got.Elem().Interface()) { - t.Errorf("want %#v, got %#v\n%v", v, got.Elem().Interface(), cmp.Diff(v, got.Elem().Interface())) + var opts []cmp.Option + if reflect.TypeOf(v).Kind() == reflect.Struct { + opts = append(opts, cmpopts.IgnoreUnexported(v)) + } + if !cmp.Equal(v, got.Elem().Interface(), opts...) { + t.Errorf("want %#v, got %#v\n%v", v, got.Elem().Interface(), cmp.Diff(v, got.Elem().Interface(), opts...)) } } @@ -85,51 +92,80 @@ func TestMarshalSlice(t *testing.T) { } } -func TestMarshalBitfield(t *testing.T) { - type bitfield8 struct { - Bit0 uint8 `gotpm:"bit=0"` - Bit1 uint8 `gotpm:"bit=1"` - Bit2 uint8 `gotpm:"bit=2"` - Bit3 uint8 `gotpm:"bit=3"` - Bit4 uint8 `gotpm:"bit=4"` - Bit5 uint8 `gotpm:"bit=5"` - Bit6 uint8 `gotpm:"bit=6"` - Bit7 uint8 `gotpm:"bit=7"` - } - type bitfield32 struct { - Reserved1 uint16 `gotpm:"bit=5:0"` - Bit6 uint8 `gotpm:"bit=6"` - Reserved2 uint8 `gotpm:"bit=12:7"` - Bit13 bool `gotpm:"bit=13"` - Bits14Through18 uint8 `gotpm:"bit=18:14"` - Bit19 byte `gotpm:"bit=19"` - Reserved3 uint16 `gotpm:"bit=30:20"` - Bit31 uint32 `gotpm:"bit=31"` +func unmarshalReserved(t *testing.T, data []byte, want interface{}) { + t.Helper() + + // Attempt to unmarshal data to the type of want, and compare + // Want is assumed to be a bitfield that may have reserved bits. + // Reserved bits are not going to be present in the input structure, + // or the accessible fields of what we marshalled. + got := reflect.New(reflect.TypeOf(want)) + rdr := bytes.NewReader(data) + err := Unmarshal(rdr, got.Interface()) + if err != nil { + t.Fatalf("want nil, got %v", err) + } + var opts []cmp.Option + if reflect.TypeOf(want).Kind() == reflect.Struct { + opts = append(opts, cmpopts.IgnoreUnexported(want)) + } + if !cmp.Equal(want, got.Elem().Interface(), opts...) { + t.Errorf("want %#v, got %#v\n%v", want, got.Elem().Interface(), cmp.Diff(want, got.Elem().Interface(), opts...)) + } + + // Re-marshal what we unmarshalled and ensure that it contains the + // original serialization (i.e., any reserved bits are still there). + var buf bytes.Buffer + Marshal(&buf, got.Interface()) + if !bytes.Equal(buf.Bytes(), data) { + t.Errorf("want %x got %x", data, buf.Bytes()) } +} + +func TestMarshalBitfield(t *testing.T) { t.Run("8bit", func(t *testing.T) { - v := bitfield8{ - Bit0: 0, - Bit1: 1, - Bit2: 0, - Bit3: 1, - Bit4: 1, - Bit5: 0, - Bit6: 0, - Bit7: 1, + v := tpma.Session{ + ContinueSession: true, + AuditExclusive: true, + AuditReset: false, + Decrypt: true, + Encrypt: true, + Audit: false, } - want := []byte{0x9a} + want := []byte{0x63} + marshalUnmarshal(t, v, want) + unmarshalReserved(t, []byte{0x7b}, v) + }) + t.Run("full8bit", func(t *testing.T) { + v := tpma.Locality{ + TPMLocZero: true, + TPMLocOne: true, + TPMLocTwo: false, + TPMLocThree: true, + TPMLocFour: false, + Extended: 1, + } + want := []byte{0x2b} marshalUnmarshal(t, v, want) }) t.Run("32bit", func(t *testing.T) { - v := bitfield32{ - Bit6: 1, - Bit13: false, - Bits14Through18: 29, - Bit19: 1, - Bit31: 1, + v := tpma.CC{ + CommandIndex: 6, + NV: true, } - want := []byte{0x80, 0x0f, 0x40, 0x40} + want := []byte{0x00, 0x40, 0x00, 0x06} marshalUnmarshal(t, v, want) + unmarshalReserved(t, []byte{0x80, 0x41, 0x00, 0x06}, v) + }) + t.Run("TPMAObject", func(t *testing.T) { + v := tpma.Object{ + FixedTPM: true, + STClear: true, + FixedParent: true, + } + want := []byte{0x00, 0x00, 0x00, 0x16} + marshalUnmarshal(t, v, want) + unmarshalReserved(t, []byte{0xff, 0x00, 0x00, 0x16}, v) }) } @@ -206,3 +242,4 @@ func TestMarshalUnion(t *testing.T) { }) } } + diff --git a/direct/tpm2/sessions.go b/direct/tpm2/sessions.go index 27907420..718e6522 100644 --- a/direct/tpm2/sessions.go +++ b/direct/tpm2/sessions.go @@ -522,12 +522,6 @@ func attrsToBytes(attrs tpma.Session) []byte { if attrs.AuditReset { res |= (1 << 2) } - if attrs.Reserved1 { - res |= (1 << 3) - } - if attrs.Reserved2 { - res |= (1 << 4) - } if attrs.Decrypt { res |= (1 << 5) } diff --git a/go.mod b/go.mod index 6a31b534..38c39de2 100644 --- a/go.mod +++ b/go.mod @@ -7,3 +7,5 @@ require ( github.com/google/go-tpm-tools v0.2.0 golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb ) + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect From d277081e778d920c248a5fbead7be614fa65b9a5 Mon Sep 17 00:00:00 2001 From: Chris Fenner Date: Sat, 19 Feb 2022 09:56:24 -0800 Subject: [PATCH 9/9] Turn command handles into an interface to avoid caller stuttering --- direct/structures/internal/structures.go | 26 +- direct/tpm2/audit_test.go | 18 +- direct/tpm2/ek_test.go | 24 +- direct/tpm2/reflect.go | 294 ++++++++++++----------- direct/tpm2/reflect_test.go | 38 +-- direct/tpm2/sealing_test.go | 112 ++++++--- direct/tpm2/sessions.go | 87 +++---- direct/tpm2/tpm2.go | 163 ++++++------- direct/transport/open_other.go | 28 +++ direct/transport/open_windows.go | 30 +++ direct/transport/simulator/simulator.go | 36 +++ direct/transport/tpm.go | 29 +++ 12 files changed, 541 insertions(+), 344 deletions(-) create mode 100644 direct/transport/open_other.go create mode 100644 direct/transport/open_windows.go create mode 100644 direct/transport/simulator/simulator.go create mode 100644 direct/transport/tpm.go diff --git a/direct/structures/internal/structures.go b/direct/structures/internal/structures.go index c60c9884..2616f62f 100644 --- a/direct/structures/internal/structures.go +++ b/direct/structures/internal/structures.go @@ -4,6 +4,7 @@ package internal import ( "crypto" "crypto/elliptic" + "encoding/binary" // Register the relevant hash implementations. _ "crypto/sha1" @@ -131,6 +132,27 @@ type TPMPTPCR uint32 // See definition in Part 2: Structures, section 7.1. type TPMHandle uint32 +// HandleValue returns the handle value. This behavior is intended to satisfy +// an interface that can be implemented by other, more complex types as well. +func (h TPMHandle) HandleValue() uint32 { + return uint32(h) +} + +// KnownName returns the TPM Name associated with the handle, if it can be known +// based only on the handle. This depends upon the value of the handle: +// only PCR, session, and permanent values have known constant Names. +// See definition in part 1: Architecture, section 16. +func (h TPMHandle) KnownName() *TPM2BName { + switch (byte)(h >> 24) { + case 0x00, 0x02, 0x03, 0x40: + result := make([]byte, 4) + binary.BigEndian.PutUint32(result, h.HandleValue()) + return &TPM2BName{Buffer: result} + default: + return nil + } +} + // TPMAAlgorithm represents a TPMA_ALGORITHM. // See definition in Part 2: Structures, section 8.2. type TPMAAlgorithm struct { @@ -146,7 +168,7 @@ type TPMAAlgorithm struct { Hash bool `gotpm:"bit=2"` // SET (1): an algorithm that may be used as an object type // CLEAR (0): an algorithm that is not used as an object type - Object bool `gotpm:"bit=3"` + Object bool `gotpm:"bit=3"` // SET (1): a signing algorithm. The setting of asymmetric, // symmetric, and hash will indicate the type of signing algorithm. // CLEAR (0): not a signing algorithm @@ -158,7 +180,7 @@ type TPMAAlgorithm struct { Encrypting bool `gotpm:"bit=9"` // SET (1): a method such as a key derivative function (KDF) // CLEAR (0): not a method - Method bool `gotpm:"bit=10"` + Method bool `gotpm:"bit=10"` } // TPMAObject represents a TPMA_OBJECT. diff --git a/direct/tpm2/audit_test.go b/direct/tpm2/audit_test.go index 051005b8..968a68d1 100644 --- a/direct/tpm2/audit_test.go +++ b/direct/tpm2/audit_test.go @@ -4,21 +4,20 @@ import ( "bytes" "testing" - "github.com/google/go-tpm-tools/simulator" "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/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 TestAuditSession(t *testing.T) { - sim, err := simulator.Get() + thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - thetpm := NewTPM(sim) defer thetpm.Close() // Create the audit session @@ -30,9 +29,7 @@ func TestAuditSession(t *testing.T) { // Create the AK for audit createAKCmd := CreatePrimary{ - PrimaryHandle: AuthHandle{ - Handle: tpm.RHOwner, - }, + PrimaryHandle: tpm.RHOwner, InPublic: tpm2b.Public{ PublicArea: tpmt.Public{ Type: tpm.AlgECC, @@ -107,18 +104,17 @@ func TestAuditSession(t *testing.T) { } // Get the audit digest signed by the AK getAuditCmd := GetSessionAuditDigest{ - PrivacyAdminHandle: AuthHandle{ - Handle: tpm.RHEndorsement, - }, - SignHandle: AuthHandle{ + PrivacyAdminHandle: tpm.RHEndorsement, + SignHandle: NamedHandle{ Handle: createAKRsp.ObjectHandle, + Name: createAKRsp.Name, }, SessionHandle: sess.Handle(), QualifyingData: tpm2b.Data{Buffer: []byte("foobar")}, } getAuditRsp, err := getAuditCmd.Execute(thetpm) if err != nil { - t.Errorf("%v", err) + t.Fatalf("%v", err) } // TODO check the signature with the AK pub aud := getAuditRsp.AuditInfo.AttestationData.Attested.SessionAudit diff --git a/direct/tpm2/ek_test.go b/direct/tpm2/ek_test.go index 595f76bd..763e3a38 100644 --- a/direct/tpm2/ek_test.go +++ b/direct/tpm2/ek_test.go @@ -4,7 +4,6 @@ import ( "errors" "testing" - "github.com/google/go-tpm-tools/simulator" "github.com/google/go-tpm/direct/structures/tpm" "github.com/google/go-tpm/direct/structures/tpm2b" "github.com/google/go-tpm/direct/structures/tpma" @@ -12,6 +11,8 @@ import ( "github.com/google/go-tpm/direct/structures/tpms" "github.com/google/go-tpm/direct/structures/tpmt" "github.com/google/go-tpm/direct/templates" + "github.com/google/go-tpm/direct/transport" + "github.com/google/go-tpm/direct/transport/simulator" ) // Test creating a sealed data blob on the standard-template EK using its policy. @@ -29,9 +30,9 @@ func TestEKPolicy(t *testing.T) { } } -func ekPolicy(t *TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error { +func ekPolicy(t transport.TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error { cmd := PolicySecret{ - AuthHandle: AuthHandle{Handle: tpm.RHEndorsement}, + AuthHandle: tpm.RHEndorsement, PolicySession: handle, NonceTPM: nonceTPM, } @@ -96,21 +97,18 @@ func ekTest(t *testing.T, ekTemplate tpm2b.Public) { } } - sim, err := simulator.Get() + thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - thetpm := NewTPM(sim) defer thetpm.Close() for _, c := range cases { t.Run(c.name, func(t *testing.T) { // Create the EK createEKCmd := CreatePrimary{ - PrimaryHandle: AuthHandle{ - Handle: tpm.RHEndorsement, - }, - InPublic: ekTemplate, + PrimaryHandle: tpm.RHEndorsement, + InPublic: ekTemplate, } createEKRsp, err := createEKCmd.Execute(thetpm) if err != nil { @@ -132,7 +130,7 @@ func ekTest(t *testing.T, ekTemplate tpm2b.Public) { // by creating an object under it data := []byte("secrets") createBlobCmd := Create{ - ParentHandle: AuthHandle{ + ParentHandle: NamedHandle{ Handle: createEKRsp.ObjectHandle, Name: createEKRsp.Name, }, @@ -196,7 +194,11 @@ func ekTest(t *testing.T, ekTemplate tpm2b.Public) { t.Fatalf("executing EK policy: %v", err) } } - createBlobCmd.ParentHandle.Auth = s + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createEKRsp.ObjectHandle, + Name: createEKRsp.Name, + Auth: s, + } if _, err := createBlobCmd.Execute(thetpm, sessions...); err != nil { t.Fatalf("%v", err) diff --git a/direct/tpm2/reflect.go b/direct/tpm2/reflect.go index f4ed90c5..0b24e722 100644 --- a/direct/tpm2/reflect.go +++ b/direct/tpm2/reflect.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/binary" "fmt" - "io" "math" "reflect" "strconv" @@ -13,9 +12,9 @@ import ( "github.com/google/go-tpm/direct/structures/tpm" "github.com/google/go-tpm/direct/structures/tpm2b" - "github.com/google/go-tpm/direct/structures/tpms" "github.com/google/go-tpm/direct/structures/tpma" - "github.com/google/go-tpm/tpmutil" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/transport" ) const ( @@ -24,28 +23,8 @@ const ( maxListLength uint32 = 1024 ) -// TPM represents a logical connection to a TPM. Support for -// various commands is provided at runtime using reflection. -type TPM struct { - transport io.ReadWriteCloser -} - -// NewTPM opens a TPM connection using the provided ReadWriteCloser. -// When this TPM connection is closed, the transport is closed. -func NewTPM(t io.ReadWriteCloser) *TPM { - return &TPM{ - transport: t, - } -} - -// Close closes the connection to the TPM and its underlying -// transport. -func (t *TPM) Close() error { - return t.transport.Close() -} - // execute sends the provided command and returns the TPM's response. -func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { +func execute(t transport.TPM, cmd Command, rsp Response, extraSess ...Session) error { cc := cmd.Command() if rsp.Response() != cc { return fmt.Errorf("cmd and rsp must be for same command: %v != %v", cc, rsp.Response()) @@ -68,8 +47,7 @@ func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { return err } } - handles := cmdHandles(cmd) - names, err := cmdNames(cmd) + handles, err := cmdHandles(cmd) if err != nil { return err } @@ -77,9 +55,18 @@ func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { if err != nil { return err } - sessions, err := cmdSessions(t, sess, cc, names, parms) - if err != nil { - return err + var names []tpm2b.Name + var sessions []byte + if hasSessions { + var err error + names, err = cmdNames(cmd) + if err != nil { + return err + } + sessions, err = cmdSessions(sess, cc, names, parms) + if err != nil { + return err + } } hdr := cmdHeader(hasSessions, 10 /* size of command header */ +len(handles)+len(sessions)+len(parms), cc) command := append(hdr, handles...) @@ -87,7 +74,7 @@ func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { command = append(command, parms...) // Send the command via the transport. - response, err := tpmutil.RunCommandRaw(t.transport, command) + response, err := t.Send(command) if err != nil { return err } @@ -133,46 +120,44 @@ func (t *TPM) execute(cmd Command, rsp Response, extraSess ...Session) error { return nil } -// Marshal will serialize the given values, appending them onto the given writer. +// Marshal will serialize the given values, returning them as a byte slice. // Returns an error if any of the values are not marshallable. -func Marshal(w io.Writer, vs ...interface{}) error { +func Marshal(vs ...interface{}) ([]byte, error) { var reflects []reflect.Value for _, v := range vs { reflects = append(reflects, reflect.ValueOf(v)) } var buf bytes.Buffer - if err := marshal(&buf, reflects...); err != nil { - return err + for _, reflect := range reflects { + if err := marshal(&buf, reflect); err != nil { + return nil, err + } } - _, err := io.Copy(w, &buf) - return err + return buf.Bytes(), nil } -// marshal will serialize the given values, appending them onto the given -// buffer. -// Returns an error if any of the values are not marshallable. -func marshal(buf *bytes.Buffer, vs ...reflect.Value) error { - for _, v := range vs { - switch v.Kind() { - case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if err := marshalNumeric(buf, v); err != nil { - return err - } - case reflect.Array, reflect.Slice: - if err := marshalArray(buf, v); err != nil { - return err - } - case reflect.Struct: - if err := marshalStruct(buf, v); err != nil { - return err - } - case reflect.Ptr: - if err := marshalStruct(buf, v.Elem()); err != nil { - return err - } - default: - return fmt.Errorf("not marshallable: %#v", v) +// marshal will serialize the given value, appending onto the given buffer. +// Returns an error if the value is not marshallable. +func marshal(buf *bytes.Buffer, v reflect.Value) error { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := marshalNumeric(buf, v); err != nil { + return err + } + case reflect.Array, reflect.Slice: + if err := marshalArray(buf, v); err != nil { + return err } + case reflect.Struct: + if err := marshalStruct(buf, v); err != nil { + return err + } + case reflect.Ptr: + if err := marshalStruct(buf, v.Elem()); err != nil { + return err + } + default: + return fmt.Errorf("not marshallable: %#v", v) } return nil } @@ -199,7 +184,7 @@ func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { for i := 0; i < v.NumField(); i++ { // Ignore embedded Bitfield hints. if !v.Type().Field(i).IsExported() { - //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { + //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { continue } thisBitwise := hasTag(v.Type().Field(i), "bit") @@ -371,10 +356,10 @@ func marshalUnion(buf *bytes.Buffer, v reflect.Value, selector int64) error { return fmt.Errorf("selector value '%v' not handled for type '%v'", selector, v.Type().Name()) } -// Unmarshal deserializes the given values from the reader. +// Unmarshal deserializes the given values from the byte slice. // Returns an error if the buffer does not contain enough data to satisfy the // types, or if the types are not unmarshallable. -func Unmarshal(r io.Reader, vs ...interface{}) error { +func Unmarshal(data []byte, vs ...interface{}) error { var reflects []reflect.Value for _, v := range vs { if reflect.ValueOf(v).Kind() != reflect.Ptr { @@ -383,63 +368,62 @@ func Unmarshal(r io.Reader, vs ...interface{}) error { reflects = append(reflects, reflect.ValueOf(v).Elem()) } var buf bytes.Buffer - if _, err := io.Copy(&buf, r); err != nil { + if _, err := buf.Write(data); err != nil { return err } - return unmarshal(&buf, reflects...) + for _, reflect := range reflects { + if err := unmarshal(&buf, reflect); err != nil { + return err + } + } + return nil } -// unmarshal will deserialize the given values from the given buffer. +// unmarshal will deserialize the given value from the given buffer. // Returns an error if the buffer does not contain enough data to satisfy the // type. -func unmarshal(buf *bytes.Buffer, vs ...reflect.Value) error { - for _, v := range vs { - switch v.Kind() { - case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if err := unmarshalNumeric(buf, v); err != nil { - return err - } - continue - case reflect.Slice: - var length uint32 - // special case for byte slices: just read the entire - // rest of the buffer - if v.Type().Elem().Kind() == reflect.Uint8 { - length = uint32(buf.Len()) - } else { - err := unmarshalNumeric(buf, reflect.ValueOf(&length).Elem()) - if err != nil { - return fmt.Errorf("deserializing size for field of type '%v': %w", v.Type(), err) - } - } - if length > uint32(math.MaxInt32) || length > maxListLength { - return fmt.Errorf("could not deserialize slice of length %v", length) - } - // Go's reflect library doesn't allow increasing the - // capacity of an existing slice. - // Since we can't be sure that the capacity of the - // passed-in value was enough, allocate - // a new temporary one of the correct length, unmarshal - // to it, and swap it in. - tmp := reflect.MakeSlice(v.Type(), int(length), int(length)) - if err := unmarshalArray(buf, tmp); err != nil { - return err - } - v.Set(tmp) - continue - case reflect.Array: - if err := unmarshalArray(buf, v); err != nil { - return err - } - continue - case reflect.Struct: - if err := unmarshalStruct(buf, v); err != nil { - return err +func unmarshal(buf *bytes.Buffer, v reflect.Value) error { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if err := unmarshalNumeric(buf, v); err != nil { + return err + } + case reflect.Slice: + var length uint32 + // special case for byte slices: just read the entire + // rest of the buffer + if v.Type().Elem().Kind() == reflect.Uint8 { + length = uint32(buf.Len()) + } else { + err := unmarshalNumeric(buf, reflect.ValueOf(&length).Elem()) + if err != nil { + return fmt.Errorf("deserializing size for field of type '%v': %w", v.Type(), err) } - continue - default: - return fmt.Errorf("not unmarshallable: %v", v.Type()) } + if length > uint32(math.MaxInt32) || length > maxListLength { + return fmt.Errorf("could not deserialize slice of length %v", length) + } + // Go's reflect library doesn't allow increasing the + // capacity of an existing slice. + // Since we can't be sure that the capacity of the + // passed-in value was enough, allocate + // a new temporary one of the correct length, unmarshal + // to it, and swap it in. + tmp := reflect.MakeSlice(v.Type(), int(length), int(length)) + if err := unmarshalArray(buf, tmp); err != nil { + return err + } + v.Set(tmp) + case reflect.Array: + if err := unmarshalArray(buf, v); err != nil { + return err + } + case reflect.Struct: + if err := unmarshalStruct(buf, v); err != nil { + return err + } + default: + return fmt.Errorf("not unmarshallable: %v", v.Type()) } return nil } @@ -468,7 +452,7 @@ func unmarshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Ignore embedded Bitfield hints. // Ignore embedded Bitfield hints. if !v.Type().Field(i).IsExported() { - //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { + //if _, isBitfield := v.Field(i).Interface().(tpma.Bitfield); isBitfield { continue } thisBitwise := hasTag(v.Type().Field(i), "bit") @@ -665,9 +649,8 @@ func tags(t reflect.StructField) map[string]string { tags := strings.Split(allTags, ",") for _, tag := range tags { // Split on the equals sign for settable tags. - // If the split returns an empty slice, this is an empty tag. // If the split returns a slice of length 1, this is an - // un-settable tag. + // un-settable tag or an empty tag (which we'll ignore). // If the split returns a slice of length 2, this is a settable // tag. assignment := strings.SplitN(tag, "=", 2) @@ -675,7 +658,7 @@ func tags(t reflect.StructField) map[string]string { if len(assignment) > 1 { val = assignment[1] } - if len(assignment) > 0 { + if len(assignment) > 0 && assignment[0] != "" { key := assignment[0] result[key] = val } @@ -755,40 +738,71 @@ func cmdAuths(cmd Command) ([]Session, error) { authHandles := taggedMembers(reflect.ValueOf(cmd).Elem(), "auth", false) var result []Session for _, authHandle := range authHandles { - handle, ok := authHandle.Interface().(AuthHandle) - if !ok { - return nil, fmt.Errorf("'auth'-tagged member of %v was of type %v instead of AuthHandle", - reflect.TypeOf(cmd), authHandle.Type()) + // Dynamically check if the caller provided auth in an AuthHandle. + // If not, use an empty password auth. + // A cleaner way to do this would be to have an interface method that + // returns a Session, but that would require Session to live inside + // the internal package, so that tpm.Handle can return it. + // So instead, we live with a little more magic reflection behavior. + if h, ok := authHandle.Interface().(AuthHandle); ok { + result = append(result, h.Auth) + } else { + result = append(result, PasswordAuth(nil)) } - result = append(result, handle.effectiveAuth()) } return result, nil } // cmdHandles returns the handles area of the command. -func cmdHandles(cmd Command) []byte { +func cmdHandles(cmd Command) ([]byte, error) { handles := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", false) // Initial capacity is enough to hold 3 handles result := bytes.NewBuffer(make([]byte, 0, 12)) - marshal(result, handles...) + for i, maybeHandle := range handles { + h, ok := maybeHandle.Interface().(handle) + if !ok { + return nil, fmt.Errorf("'handle'-tagged member of '%v' was of type '%v', which does not satisfy handle", + reflect.TypeOf(cmd), maybeHandle.Type()) + } + + // Special behavior: nullable handles have an effective zero-value of + // TPM_RH_NULL. + if h.HandleValue() == 0 && hasTag(reflect.TypeOf(cmd).Elem().Field(i), "nullable") { + h = tpm.RHNull + } - return result.Bytes() + binary.Write(result, binary.BigEndian, h.HandleValue()) + } + + return result.Bytes(), nil } -// cmdNames returns the authorized names of the command. +// cmdNames returns the names of the entities referenced by the handles of the command. func cmdNames(cmd Command) ([]tpm2b.Name, error) { - authHandles := taggedMembers(reflect.ValueOf(cmd).Elem(), "auth", false) + handles := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", false) var result []tpm2b.Name - for _, authHandle := range authHandles { - handle, ok := authHandle.Interface().(AuthHandle) + for i, maybeHandle := range handles { + h, ok := maybeHandle.Interface().(handle) if !ok { - return nil, fmt.Errorf("'auth'-tagged member of %v was of type %v instead of AuthHandle", - reflect.TypeOf(cmd), authHandle.Type()) + return nil, fmt.Errorf("'handle'-tagged member of '%v' was of type '%v', which does not satisfy handle", + reflect.TypeOf(cmd), maybeHandle.Type()) + } + + // Special behavior: nullable handles have an effective zero-value of + // TPM_RH_NULL. + if h.HandleValue() == 0 && hasTag(reflect.TypeOf(cmd).Elem().Field(i), "nullable") { + h = tpm.RHNull } - result = append(result, handle.effectiveName()) + + name := h.KnownName() + if name == nil { + return nil, fmt.Errorf("missing Name for '%v' parameter", + reflect.ValueOf(cmd).Elem().Type().Field(i).Name) + } + result = append(result, *name) } return result, nil @@ -816,7 +830,7 @@ func cmdParameters(cmd Command, sess []Session) ([]byte, error) { return nil, fmt.Errorf("too many decrypt sessions") } if len(firstParmBytes) < 2 { - return nil, fmt.Errorf("this command's first parameter is not a tpm2b.") + return nil, fmt.Errorf("this command's first parameter is not a tpm2b") } err := s.Encrypt(firstParmBytes[2:]) if err != nil { @@ -829,12 +843,16 @@ func cmdParameters(cmd Command, sess []Session) ([]byte, error) { var result bytes.Buffer result.Write(firstParmBytes) // Write the rest of the parameters normally. - marshal(&result, parms[1:]...) + for _, parm := range parms[1:] { + if err := marshal(&result, parm); err != nil { + return nil, err + } + } return result.Bytes(), nil } // cmdSessions returns the authorization area of the command. -func cmdSessions(tpm *TPM, sess []Session, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { +func cmdSessions(sess []Session, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { // There is no authorization area if there are no sessions. if len(sess) == 0 { return nil, nil @@ -1014,5 +1032,11 @@ func rspParameters(parms []byte, sess []Session, rspStruct Response) error { } } } - return unmarshal(bytes.NewBuffer(parms), parmsFields...) + buf := bytes.NewBuffer(parms) + for _, parmsField := range parmsFields { + if err := unmarshal(buf, parmsField); err != nil { + return err + } + } + return nil } diff --git a/direct/tpm2/reflect_test.go b/direct/tpm2/reflect_test.go index 676975f3..bf9a49bb 100644 --- a/direct/tpm2/reflect_test.go +++ b/direct/tpm2/reflect_test.go @@ -100,8 +100,7 @@ func unmarshalReserved(t *testing.T, data []byte, want interface{}) { // Reserved bits are not going to be present in the input structure, // or the accessible fields of what we marshalled. got := reflect.New(reflect.TypeOf(want)) - rdr := bytes.NewReader(data) - err := Unmarshal(rdr, got.Interface()) + err := Unmarshal(data, got.Interface()) if err != nil { t.Fatalf("want nil, got %v", err) } @@ -115,10 +114,12 @@ func unmarshalReserved(t *testing.T, data []byte, want interface{}) { // Re-marshal what we unmarshalled and ensure that it contains the // original serialization (i.e., any reserved bits are still there). - var buf bytes.Buffer - Marshal(&buf, got.Interface()) - if !bytes.Equal(buf.Bytes(), data) { - t.Errorf("want %x got %x", data, buf.Bytes()) + result, err := Marshal(got.Interface()) + if err != nil { + t.Fatalf("error marshalling %v: %v", got, err) + } + if !bytes.Equal(result, data) { + t.Errorf("want %x got %x", data, result) } } @@ -126,11 +127,11 @@ func TestMarshalBitfield(t *testing.T) { t.Run("8bit", func(t *testing.T) { v := tpma.Session{ ContinueSession: true, - AuditExclusive: true, - AuditReset: false, - Decrypt: true, - Encrypt: true, - Audit: false, + AuditExclusive: true, + AuditReset: false, + Decrypt: true, + Encrypt: true, + Audit: false, } want := []byte{0x63} marshalUnmarshal(t, v, want) @@ -138,12 +139,12 @@ func TestMarshalBitfield(t *testing.T) { }) t.Run("full8bit", func(t *testing.T) { v := tpma.Locality{ - TPMLocZero: true, - TPMLocOne: true, - TPMLocTwo: false, + TPMLocZero: true, + TPMLocOne: true, + TPMLocTwo: false, TPMLocThree: true, - TPMLocFour: false, - Extended: 1, + TPMLocFour: false, + Extended: 1, } want := []byte{0x2b} marshalUnmarshal(t, v, want) @@ -159,8 +160,8 @@ func TestMarshalBitfield(t *testing.T) { }) t.Run("TPMAObject", func(t *testing.T) { v := tpma.Object{ - FixedTPM: true, - STClear: true, + FixedTPM: true, + STClear: true, FixedParent: true, } want := []byte{0x00, 0x00, 0x00, 0x16} @@ -242,4 +243,3 @@ func TestMarshalUnion(t *testing.T) { }) } } - diff --git a/direct/tpm2/sealing_test.go b/direct/tpm2/sealing_test.go index c5abffe9..208b527b 100644 --- a/direct/tpm2/sealing_test.go +++ b/direct/tpm2/sealing_test.go @@ -5,13 +5,13 @@ import ( "errors" "testing" - "github.com/google/go-tpm-tools/simulator" "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/tpms" "github.com/google/go-tpm/direct/structures/tpmt" "github.com/google/go-tpm/direct/templates" + "github.com/google/go-tpm/direct/transport/simulator" ) // Test creating and unsealing a sealed data blob with a password and HMAC. @@ -30,20 +30,17 @@ func TestUnseal(t *testing.T) { } func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { - sim, err := simulator.Get() + thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } - thetpm := NewTPM(sim) defer thetpm.Close() // Create the SRK // Put a password on the SRK to test more of the flows. srkAuth := []byte("mySRK") createSRKCmd := CreatePrimary{ - PrimaryHandle: AuthHandle{ - Handle: tpm.RHOwner, - }, + PrimaryHandle: tpm.RHOwner, InSensitive: tpm2b.SensitiveCreate{ Sensitive: tpms.SensitiveCreate{ UserAuth: tpm2b.Auth{ @@ -112,9 +109,12 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob using an hmac auth session also for audit t.Run("CreateAudit", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), - AuditExclusive()) + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AuditExclusive()), + } + createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -123,9 +123,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob, using the auth session also for decryption t.Run("CreateDecrypt", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), - AESEncryption(128, EncryptIn)) + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptIn)), + } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -134,9 +136,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob, using the auth session also for encryption t.Run("CreateEncrypt", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), - AESEncryption(128, EncryptOut)) + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptOut)), + } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -145,9 +149,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob, using the auth session also for decrypt and encrypt t.Run("CreateDecryptEncrypt", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), - AESEncryption(128, EncryptInOut)) + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut)), + } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -156,10 +162,13 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob with decrypt and encrypt session t.Run("CreateDecryptEncryptAudit", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), - Audit()) + Audit()), + } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -168,10 +177,13 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Create the blob with decrypt and encrypt session bound to SRK t.Run("CreateDecryptEncryptSalted", func(t *testing.T) { - createBlobCmd.ParentHandle.Auth = - HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), - Salted(createSRKRsp.ObjectHandle, createSRKRsp.OutPublic.PublicArea)) + Salted(createSRKRsp.ObjectHandle, createSRKRsp.OutPublic.PublicArea)), + } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) @@ -180,7 +192,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Use HMAC auth to authorize the rest of the Create commands // Exercise re-using a use-once HMAC structure (which will spin up the session each time) - createBlobCmd.ParentHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(srkAuth)) + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth)), + } // Create the blob with a separate decrypt and encrypt session t.Run("CreateDecryptEncryptSeparate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, @@ -256,7 +272,7 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { }() unsealCmd := Unseal{ - ItemHandle: AuthHandle{ + ItemHandle: NamedHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, }, @@ -264,7 +280,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Unseal the blob with a password session t.Run("WithPassword", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = PasswordAuth(auth) + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: PasswordAuth(auth), + } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) @@ -276,7 +296,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Unseal the blob with an incorrect password session t.Run("WithWrongPassword", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = PasswordAuth([]byte("NotThePassword")) + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: PasswordAuth([]byte("NotThePassword")), + } _, err := unsealCmd.Execute(thetpm) if !errors.Is(err, tpm.RCBadAuth) { t.Errorf("want TPM_RC_BAD_AUTH, got %v", err) @@ -291,7 +315,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Unseal the blob with a use-once HMAC session t.Run("WithHMAC", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(auth2)) + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(auth2)), + } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) @@ -303,8 +331,12 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { // Unseal the blob with a use-once HMAC session with encryption t.Run("WithHMACEncrypt", func(t *testing.T) { - unsealCmd.ItemHandle.Auth = HMAC(tpm.AlgSHA256, 16, Auth(auth2), - AESEncryption(128, EncryptOut)) + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(auth2), + AESEncryption(128, EncryptOut)), + } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) @@ -321,7 +353,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { t.Fatalf("%v", err) } defer cleanup() - unsealCmd.ItemHandle.Auth = sess + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: sess, + } // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { @@ -345,7 +381,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { t.Fatalf("%v", err) } defer cleanup() - unsealCmd.ItemHandle.Auth = sess + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: sess, + } // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { @@ -374,7 +414,11 @@ func unsealingTest(t *testing.T, srkTemplate tpm2b.Public) { t.Fatalf("%v", err) } defer cleanup2() - unsealCmd.ItemHandle.Auth = sess1 + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: sess1, + } // It should be possible to use the sessions multiple times. for i := 0; i < 3; i++ { diff --git a/direct/tpm2/sessions.go b/direct/tpm2/sessions.go index 718e6522..871afeb0 100644 --- a/direct/tpm2/sessions.go +++ b/direct/tpm2/sessions.go @@ -19,6 +19,7 @@ import ( "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" legacy "github.com/google/go-tpm/tpm2" ) @@ -28,12 +29,12 @@ type Session interface { // already done. Some types of sessions may need to be initialized // just-in-time, e.g., to support calling patterns that help the user // securely authorize their actions without writing a lot of code. - Init(tpm *TPM) error + Init(tpm transport.TPM) error // Cleans up the session, if needed. // Some types of session need to be cleaned up if the command failed, // again to support calling patterns that help the user securely // authorize their actions without writing a lot of code. - CleanupFailure(tpm *TPM) error + CleanupFailure(tpm transport.TPM) error // The last nonceTPM for this session. NonceTPM() tpm2b.Nonce // Updates nonceCaller to a new random value. @@ -61,36 +62,6 @@ type Session interface { Handle() tpm.Handle } -// cpHash calculates the TPM command parameter hash. -// cpHash = hash(CC || names || parms) -func cpHash(alg tpmi.AlgHash, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { - ha, err := alg.Hash() - if err != nil { - return nil, err - } - h := ha.New() - binary.Write(h, binary.BigEndian, cc) - for _, name := range names { - h.Write(name.Buffer) - } - h.Write(parms) - return h.Sum(nil), nil -} - -// rpHash calculates the TPM response parameter hash. -// rpHash = hash(RC || CC || parms) -func rpHash(alg tpmi.AlgHash, rc tpm.RC, cc tpm.CC, parms []byte) ([]byte, error) { - ha, err := alg.Hash() - if err != nil { - return nil, err - } - h := ha.New() - binary.Write(h, binary.BigEndian, rc) - binary.Write(h, binary.BigEndian, cc) - h.Write(parms) - return h.Sum(nil), nil -} - // pwSession represents a password-pseudo-session. type pwSession struct { auth []byte @@ -104,10 +75,10 @@ func PasswordAuth(auth []byte) Session { } // Init is not required and has no effect for a password session. -func (s *pwSession) Init(tpm *TPM) error { return nil } +func (s *pwSession) Init(tpm transport.TPM) error { return nil } // Cleanup is not required and has no effect for a password session. -func (s *pwSession) CleanupFailure(tpm *TPM) error { return nil } +func (s *pwSession) CleanupFailure(tpm transport.TPM) error { return nil } // NonceTPM normally returns the last nonceTPM value from the session. // Since a password session is a pseudo-session with the auth value stuffed @@ -169,6 +140,36 @@ func (s *pwSession) Decrypt(parameter []byte) error { return nil } // In the case of a password session, this is always TPM_RS_PW. func (s *pwSession) Handle() tpm.Handle { return tpm.RSPW } +// cpHash calculates the TPM command parameter hash. +// cpHash = hash(CC || names || parms) +func cpHash(alg tpmi.AlgHash, cc tpm.CC, names []tpm2b.Name, parms []byte) ([]byte, error) { + ha, err := alg.Hash() + if err != nil { + return nil, err + } + h := ha.New() + binary.Write(h, binary.BigEndian, cc) + for _, name := range names { + h.Write(name.Buffer) + } + h.Write(parms) + return h.Sum(nil), nil +} + +// rpHash calculates the TPM response parameter hash. +// rpHash = hash(RC || CC || parms) +func rpHash(alg tpmi.AlgHash, rc tpm.RC, cc tpm.CC, parms []byte) ([]byte, error) { + ha, err := alg.Hash() + if err != nil { + return nil, err + } + h := ha.New() + binary.Write(h, binary.BigEndian, rc) + binary.Write(h, binary.BigEndian, cc) + h.Write(parms) + return h.Sum(nil), nil +} + // sessionOptions represents extra options used when setting up an HMAC or policy session. type sessionOptions struct { auth []byte @@ -317,7 +318,7 @@ func HMAC(hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) Session { } // HMACSession sets up a reusable HMAC session that needs to be closed. -func HMACSession(t *TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { +func HMACSession(t transport.TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := hmacSession{ sessionOptions: defaultOptions(), @@ -437,7 +438,7 @@ func getEncryptedSalt(pub tpmt.Public) (*tpm2b.EncryptedSecret, []byte, error) { } // Init initializes the session, just in time, if needed. -func (s *hmacSession) Init(t *TPM) error { +func (s *hmacSession) Init(t transport.TPM) error { if s.handle != tpm.RHNull { // Session is already initialized. return nil @@ -475,7 +476,7 @@ func (s *hmacSession) Init(t *TPM) error { if err != nil { return err } - s.handle = sasRsp.SessionHandle + s.handle = tpm.Handle(sasRsp.SessionHandle.HandleValue()) s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 ha, err := s.hash.Hash() @@ -492,7 +493,7 @@ func (s *hmacSession) Init(t *TPM) error { } // Cleanup cleans up the session, if needed. -func (s *hmacSession) CleanupFailure(t *TPM) error { +func (s *hmacSession) CleanupFailure(t transport.TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil @@ -724,7 +725,7 @@ func (s *hmacSession) Handle() tpm.Handle { // PolicyCallback represents an object's policy in the form of a function. // This function makes zero or more TPM policy commands and returns error. -type PolicyCallback = func(tpm *TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error +type PolicyCallback = func(tpm transport.TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error // policySession generally implements the policy session. type policySession struct { @@ -764,7 +765,7 @@ func Policy(hash tpmi.AlgHash, nonceSize int, callback PolicyCallback, opts ...A // The caller is responsible to call whichever policy commands they want in the // session. // Note that the TPM resets a policy session after it is successfully used. -func PolicySession(t *TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { +func PolicySession(t transport.TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := policySession{ sessionOptions: defaultOptions(), @@ -795,7 +796,7 @@ func PolicySession(t *TPM, hash tpmi.AlgHash, nonceSize int, opts ...AuthOption) } // Init initializes the session, just in time, if needed. -func (s *policySession) Init(t *TPM) error { +func (s *policySession) Init(t transport.TPM) error { if s.handle != tpm.RHNull { // Session is already initialized. return nil @@ -833,7 +834,7 @@ func (s *policySession) Init(t *TPM) error { if err != nil { return err } - s.handle = sasRsp.SessionHandle + s.handle = tpm.Handle(sasRsp.SessionHandle.HandleValue()) s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 if s.bindHandle != tpm.RHNull || len(salt) != 0 { @@ -858,7 +859,7 @@ func (s *policySession) Init(t *TPM) error { } // CleanupFailure cleans up the session, if needed. -func (s *policySession) CleanupFailure(t *TPM) error { +func (s *policySession) CleanupFailure(t transport.TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil diff --git a/direct/tpm2/tpm2.go b/direct/tpm2/tpm2.go index fdaa1ea1..0e52ce1a 100644 --- a/direct/tpm2/tpm2.go +++ b/direct/tpm2/tpm2.go @@ -2,66 +2,51 @@ package tpm2 import ( - "encoding/binary" - "github.com/google/go-tpm/direct/structures/tpm" "github.com/google/go-tpm/direct/structures/tpm2b" "github.com/google/go-tpm/direct/structures/tpmi" "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/transport" ) -// AuthHandle is a convenience type to wrap an authorized handle. -type AuthHandle struct { - // The handle that is authorized. - // If zero, treated as TPM_RH_NULL. - Handle tpmi.DHObject `gotpm:"nullable"` - // The Name of the object expected at the given handle value. - // If Name contains a nil buffer, the effective Name will be - // the big-endian UINT32 representation of Handle, as in - // Part 1, section 16 "Names" for PCRs, sessions, and - // permanent values. - Name tpm2b.Name `gotpm:"skip"` - // The session used to authorize the object. - // If the 'UserWithAuth' attribute is not set on the object, - // must be a Policy session. - // For ADMIN-role commands, if 'AdminWithPolicy' is set on - // the object, must be a Policy session. - // For DUP-role commands, must be a Policy session that - // sets the policy command code to TPM_CC_DUPLICATE. - // If nil, the effective Session will be a password session - // with NULL authorization. - Auth Session `gotpm:"skip"` -} - -// effectiveHandle returns the effective handle value. -// Returns TPM_RH_NULL if unset. -func (a *AuthHandle) effectiveHandle() tpmi.DHObject { - if a.Handle != 0 { - return a.Handle - } - return tpm.RHNull +// handle represents a TPM handle as comprehended in Part 3: Commands. +// In the context of TPM commands, handles are special parameters for which +// there is a known associated name. +// This is not an exported interface, because the reflection logic has special +// behavior for AuthHandle, due to the fact that referencing Session from this +// interface would break the ability to make tpm.Handle implement it. +type handle interface { + // HandleValue is the numeric concrete handle value in the TPM. + HandleValue() uint32 + // KnownName is the TPM Name of the associated entity. See Part 1, section 16. + KnownName() *tpm2b.Name +} + +// NamedHandle represents an associated pairing of TPM handle and known Name. +type NamedHandle struct { + tpm.Handle + Name tpm2b.Name } -// effectiveName returns the effective Name. -// Returns the handle value as a name if unset. -func (a *AuthHandle) effectiveName() tpm2b.Name { - if len(a.Name.Buffer) > 0 { - return a.Name - } - buf := make([]byte, 4) - binary.BigEndian.PutUint32(buf, uint32(a.effectiveHandle())) - return tpm2b.Name{Buffer: buf} +// Name implements the handle interface, shadowing the default +// behavior of the embedded tpm.Handle. +func (h NamedHandle) KnownName() *tpm2b.Name { + return &h.Name } -// effectiveAuth returns the effective auth session. -// Returns a NULL password session if unset. -func (a *AuthHandle) effectiveAuth() Session { - if a.Auth == nil { - return PasswordAuth(nil) - } - return a.Auth +// AuthHandle allows the caller to add an authorization session onto a handle. +type AuthHandle struct { + tpm.Handle + Name tpm2b.Name + Auth Session +} + +// Name implements the handle interface, shadowing the default +// behavior of the embedded tpm.Handle. +func (h AuthHandle) KnownName() *tpm2b.Name { + return &h.Name } // Command is a placeholder interface for TPM command structures so that they @@ -88,10 +73,10 @@ type Response interface { type StartAuthSession struct { // handle of a loaded decrypt key used to encrypt salt // may be TPM_RH_NULL - TPMKey tpmi.DHObject `gotpm:"handle,nullable"` + TPMKey handle `gotpm:"handle,nullable"` // entity providing the authValue // may be TPM_RH_NULL - Bind tpmi.DHEntity `gotpm:"handle,nullable"` + Bind handle `gotpm:"handle,nullable"` // initial nonceCaller, sets nonceTPM size for the session // shall be at least 16 octets NonceCaller tpm2b.Nonce @@ -113,9 +98,9 @@ type StartAuthSession struct { func (*StartAuthSession) Command() tpm.CC { return tpm.CCStartAuthSession } // Execute executes the command and returns the response. -func (cmd *StartAuthSession) Execute(t *TPM, s ...Session) (*StartAuthSessionResponse, error) { +func (cmd *StartAuthSession) Execute(t transport.TPM, s ...Session) (*StartAuthSessionResponse, error) { var rsp StartAuthSessionResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -136,7 +121,7 @@ func (*StartAuthSessionResponse) Response() tpm.CC { return tpm.CCStartAuthSessi // See definition in Part 3, Commands, section 12.1 type Create struct { // handle of parent for new object - ParentHandle AuthHandle `gotpm:"handle,auth"` + ParentHandle handle `gotpm:"handle,auth"` // the sensitive data InSensitive tpm2b.SensitiveCreate // the public template @@ -153,9 +138,9 @@ type Create struct { func (*Create) Command() tpm.CC { return tpm.CCCreate } // Execute executes the command and returns the response. -func (cmd *Create) Execute(t *TPM, s ...Session) (*CreateResponse, error) { +func (cmd *Create) Execute(t transport.TPM, s ...Session) (*CreateResponse, error) { var rsp CreateResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -183,7 +168,7 @@ func (*CreateResponse) Response() tpm.CC { return tpm.CCCreate } // See definition in Part 3, Commands, section 12.2 type Load struct { // handle of parent for new object - ParentHandle AuthHandle `gotpm:"handle,auth"` + ParentHandle handle `gotpm:"handle,auth"` // the private portion of the object InPrivate tpm2b.Private // the public portion of the object @@ -194,9 +179,9 @@ type Load struct { func (*Load) Command() tpm.CC { return tpm.CCLoad } // Execute executes the command and returns the response. -func (cmd *Load) Execute(t *TPM, s ...Session) (*LoadResponse, error) { +func (cmd *Load) Execute(t transport.TPM, s ...Session) (*LoadResponse, error) { var rsp LoadResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -216,16 +201,16 @@ func (*LoadResponse) Response() tpm.CC { return tpm.CCLoad } // Unseal is the input to TPM2_Unseal. // See definition in Part 3, Commands, section 12.7 type Unseal struct { - ItemHandle AuthHandle `gotpm:"handle,auth"` + ItemHandle handle `gotpm:"handle,auth"` } // Command implements the Command interface. func (*Unseal) Command() tpm.CC { return tpm.CCUnseal } // Execute executes the command and returns the response. -func (cmd *Unseal) Execute(t *TPM, s ...Session) (*UnsealResponse, error) { +func (cmd *Unseal) Execute(t transport.TPM, s ...Session) (*UnsealResponse, error) { var rsp UnsealResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -243,7 +228,7 @@ func (*UnsealResponse) Response() tpm.CC { return tpm.CCUnseal } // See definition in Part 3, Commands, section 18.4 type Quote struct { // handle of key that will perform signature - SignHandle AuthHandle `gotpm:"handle,auth"` + SignHandle handle `gotpm:"handle,auth"` // data supplied by the caller QualifyingData tpm2b.Data // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL @@ -256,9 +241,9 @@ type Quote struct { func (*Quote) Command() tpm.CC { return tpm.CCQuote } // Execute executes the command and returns the response. -func (cmd *Quote) Execute(t *TPM, s ...Session) (*QuoteResponse, error) { +func (cmd *Quote) Execute(t transport.TPM, s ...Session) (*QuoteResponse, error) { var rsp QuoteResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -279,11 +264,11 @@ func (*QuoteResponse) Response() tpm.CC { return tpm.CCQuote } // See definition in Part 3, Commands, section 18.5 type GetSessionAuditDigest struct { // handle of the privacy administrator (TPM_RH_ENDORSEMENT) - PrivacyAdminHandle AuthHandle `gotpm:"handle,auth"` + PrivacyAdminHandle handle `gotpm:"handle,auth"` // handle of the signing key - SignHandle AuthHandle `gotpm:"handle,auth"` + SignHandle handle `gotpm:"handle,auth"` // handle of the audit session - SessionHandle tpmi.SHHMAC `gotpm:"handle"` + SessionHandle handle `gotpm:"handle"` // user-provided qualifying data – may be zero-length QualifyingData tpm2b.Data // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL @@ -294,9 +279,9 @@ type GetSessionAuditDigest struct { func (*GetSessionAuditDigest) Command() tpm.CC { return tpm.CCGetSessionAuditDigest } // Execute executes the command and returns the response. -func (cmd *GetSessionAuditDigest) Execute(t *TPM, s ...Session) (*GetSessionAuditDigestResponse, error) { +func (cmd *GetSessionAuditDigest) Execute(t transport.TPM, s ...Session) (*GetSessionAuditDigestResponse, error) { var rsp GetSessionAuditDigestResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -318,7 +303,7 @@ func (*GetSessionAuditDigestResponse) Response() tpm.CC { return tpm.CCGetSessio // See definition in Part 3, Commands, section 22.2 type PCRExtend struct { // handle of the PCR - PCRHandle AuthHandle `gotpm:"handle,auth"` + PCRHandle handle `gotpm:"handle,auth"` // list of tagged digest values to be extended Digests tpml.DigestValues } @@ -327,9 +312,9 @@ type PCRExtend struct { func (*PCRExtend) Command() tpm.CC { return tpm.CCPCRExtend } // Execute executes the command and returns the response. -func (cmd *PCRExtend) Execute(t *TPM, s ...Session) (*PCRExtendResponse, error) { +func (cmd *PCRExtend) Execute(t transport.TPM, s ...Session) (*PCRExtendResponse, error) { var rsp PCRExtendResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -346,7 +331,7 @@ func (*PCRExtendResponse) Response() tpm.CC { return tpm.CCPCRExtend } // See definition in Part 3, Commands, section 22.3 type PCREvent struct { // Handle of the PCR - PCRHandle AuthHandle `gotpm:"handle,auth"` + PCRHandle handle `gotpm:"handle,auth"` // Event data in sized buffer EventData tpm2b.Event } @@ -355,9 +340,9 @@ type PCREvent struct { func (*PCREvent) Command() tpm.CC { return tpm.CCPCREvent } // Execute executes the command and returns the response. -func (cmd *PCREvent) Execute(t *TPM, s ...Session) (*PCREventResponse, error) { +func (cmd *PCREvent) Execute(t transport.TPM, s ...Session) (*PCREventResponse, error) { var rsp PCREventResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -381,9 +366,9 @@ type PCRRead struct { func (*PCRRead) Command() tpm.CC { return tpm.CCPCRRead } // Execute executes the command and returns the response. -func (cmd *PCRRead) Execute(t *TPM, s ...Session) (*PCRReadResponse, error) { +func (cmd *PCRRead) Execute(t transport.TPM, s ...Session) (*PCRReadResponse, error) { var rsp PCRReadResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -406,9 +391,9 @@ func (*PCRReadResponse) Response() tpm.CC { return tpm.CCPCRRead } // See definition in Part 3, Commands, section 23.4 type PolicySecret struct { // handle for an entity providing the authorization - AuthHandle AuthHandle `gotpm:"handle,auth"` + AuthHandle handle `gotpm:"handle,auth"` // handle for the policy session being extended - PolicySession tpmi.SHPolicy `gotpm:"handle"` + PolicySession handle `gotpm:"handle"` // the policy nonce for the session NonceTPM tpm2b.Nonce // digest of the command parameters to which this authorization is limited @@ -424,9 +409,9 @@ type PolicySecret struct { func (*PolicySecret) Command() tpm.CC { return tpm.CCPolicySecret } // Execute executes the command and returns the response. -func (cmd *PolicySecret) Execute(t *TPM, s ...Session) (*PolicySecretResponse, error) { +func (cmd *PolicySecret) Execute(t transport.TPM, s ...Session) (*PolicySecretResponse, error) { var rsp PolicySecretResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -448,7 +433,7 @@ func (*PolicySecretResponse) Response() tpm.CC { return tpm.CCPolicySecret } type CreatePrimary struct { // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, // or TPM_RH_NULL - PrimaryHandle AuthHandle `gotpm:"handle,auth"` + PrimaryHandle handle `gotpm:"handle,auth"` // the sensitive data InSensitive tpm2b.SensitiveCreate // the public template @@ -465,9 +450,9 @@ type CreatePrimary struct { func (*CreatePrimary) Command() tpm.CC { return tpm.CCCreatePrimary } // Execute executes the command and returns the response. -func (cmd *CreatePrimary) Execute(t *TPM, s ...Session) (*CreatePrimaryResponse, error) { +func (cmd *CreatePrimary) Execute(t transport.TPM, s ...Session) (*CreatePrimaryResponse, error) { var rsp CreatePrimaryResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -497,16 +482,16 @@ func (*CreatePrimaryResponse) Response() tpm.CC { return tpm.CCCreatePrimary } // See definition in Part 3, Commands, section 28.4 type FlushContext struct { // the handle of the item to flush - FlushHandle tpmi.DHContext + FlushHandle handle `gotpm:"handle"` } // Command implements the Command interface. func (*FlushContext) Command() tpm.CC { return tpm.CCFlushContext } // Execute executes the command and returns the response. -func (cmd *FlushContext) Execute(t *TPM, s ...Session) (*FlushContextResponse, error) { +func (cmd *FlushContext) Execute(t transport.TPM, s ...Session) (*FlushContextResponse, error) { var rsp FlushContextResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil @@ -534,9 +519,9 @@ type GetCapability struct { func (*GetCapability) Command() tpm.CC { return tpm.CCGetCapability } // Execute executes the command and returns the response. -func (cmd *GetCapability) Execute(t *TPM, s ...Session) (*GetCapabilityResponse, error) { +func (cmd *GetCapability) Execute(t transport.TPM, s ...Session) (*GetCapabilityResponse, error) { var rsp GetCapabilityResponse - if err := t.execute(cmd, &rsp, s...); err != nil { + if err := execute(t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil diff --git a/direct/transport/open_other.go b/direct/transport/open_other.go new file mode 100644 index 00000000..ea4ce8e6 --- /dev/null +++ b/direct/transport/open_other.go @@ -0,0 +1,28 @@ +//go:build !windows + +package transport + +import ( + legacy "github.com/google/go-tpm/tpm2" +) + +// Wrap the legacy OpenTPM function so callers don't have to import both the +// legacy and the new TPM 2.0 API. +// TODO: When we delete the legacy API, we can make this the only copy of +// OpenTPM. + +// OpenTPM opens a channel to the TPM at the given path. If the file is a +// device, then it treats it like a normal TPM device, and if the file is a +// Unix domain socket, then it opens a connection to the socket. +// +// This function may also be invoked with no paths, as tpm2.OpenTPM(). In this +// case, the default paths on Linux (/dev/tpmrm0 then /dev/tpm0), will be used. +func OpenTPM(path ...string) (TPM, error) { + rwc, err := legacy.OpenTPM(path...) + if err != nil { + return nil, err + } + return &LocalTPM{ + transport: rwc, + }, nil +} diff --git a/direct/transport/open_windows.go b/direct/transport/open_windows.go new file mode 100644 index 00000000..8295798f --- /dev/null +++ b/direct/transport/open_windows.go @@ -0,0 +1,30 @@ +//go:build windows + +package transport + +import ( + "io" + + legacy "github.com/google/go-tpm/tpm2" +) + +// Wrap the legacy OpenTPM function so callers don't have to import both the +// legacy and the new TPM 2.0 API. +// TODO: When we delete the legacy API, we can make this the only copy of +// OpenTPM. + +// OpenTPM opens a channel to the TPM at the given path. If the file is a +// device, then it treats it like a normal TPM device, and if the file is a +// Unix domain socket, then it opens a connection to the socket. +// +// This function may also be invoked with no paths, as tpm2.OpenTPM(). In this +// case, the default paths on Linux (/dev/tpmrm0 then /dev/tpm0), will be used. +func OpenTPM() (TPM, error) { + rwc, err := legacy.OpenTPM() + if err != nil { + return nil, err + } + return &LocalTPM{ + transport: rwc, + }, nil +} diff --git a/direct/transport/simulator/simulator.go b/direct/transport/simulator/simulator.go new file mode 100644 index 00000000..f8afd036 --- /dev/null +++ b/direct/transport/simulator/simulator.go @@ -0,0 +1,36 @@ +// package simulator provides access to a local simulator for TPM testing. +package simulator + +import ( + "io" + + "github.com/google/go-tpm-tools/simulator" + "github.com/google/go-tpm/direct/transport" + "github.com/google/go-tpm/tpmutil" +) + +// TPM represents a connection to a TPM simulator. +type TPM struct { + transport io.ReadWriteCloser +} + +// Send implements the TPM interface. +func (t *TPM) Send(input []byte) ([]byte, error) { + return tpmutil.RunCommandRaw(t.transport, input) +} + +// OpenSimulator starts and opens a TPM simulator. +func OpenSimulator() (transport.TPM, error) { + sim, err := simulator.Get() + if err != nil { + return nil, err + } + return &TPM{ + transport: sim, + }, nil +} + +// Close implements the TPM interface. +func (t *TPM) Close() error { + return t.transport.Close() +} diff --git a/direct/transport/tpm.go b/direct/transport/tpm.go new file mode 100644 index 00000000..1142b696 --- /dev/null +++ b/direct/transport/tpm.go @@ -0,0 +1,29 @@ +// package transport implements types for physically talking to TPMs. +package transport + +import ( + "io" + + "github.com/google/go-tpm/tpmutil" +) + +// TPM represents a logical connection to a TPM. +type TPM interface { + Send(input []byte) ([]byte, error) + Close() error +} + +// LocalTPM represents a connection to the local TPM. +type LocalTPM struct { + transport io.ReadWriteCloser +} + +// Send implements the TPM interface. +func (t *LocalTPM) Send(input []byte) ([]byte, error) { + return tpmutil.RunCommandRaw(t.transport, input) +} + +// Close implements the TPM interface. +func (t *LocalTPM) Close() error { + return t.transport.Close() +}