diff --git a/.tempnotes.txt.swp b/.tempnotes.txt.swp new file mode 100644 index 00000000..29e3e815 Binary files /dev/null and b/.tempnotes.txt.swp differ 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/direct/helpers/names.go b/direct/helpers/names.go new file mode 100644 index 00000000..e179ab65 --- /dev/null +++ b/direct/helpers/names.go @@ -0,0 +1,14 @@ +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 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/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<> 8) + r &= 0xFFFFF0FF + return true, TPMFmt1Error{ + 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 { + targetTPMRC, ok := target.(TPMRC) + if !ok { + return false + } + if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { + return fmt1.canonical == targetTPMRC + } + 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.(*TPMFmt1Error) + if !ok { + return false + } + isFmt1, fmt1 := r.isFmt1Error() + if !isFmt1 { + return false + } + *pFmt1 = fmt1 + return true +} diff --git a/direct/structures/internal/structures.go b/direct/structures/internal/structures.go new file mode 100644 index 00000000..2616f62f --- /dev/null +++ b/direct/structures/internal/structures.go @@ -0,0 +1,1289 @@ +// package internal defines all the TPM 2.0 structures together to avoid import cycles +package internal + +import ( + "crypto" + "crypto/elliptic" + "encoding/binary" + + // 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. +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 + +// 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 { + 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 + +// 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 + +// 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 + +// 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 { + bitfield32 + // 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"` + // 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"` +} + +// TPMAObject represents a TPMA_OBJECT. +// See definition in Part 2: Structures, section 8.3.2. +type TPMAObject struct { + bitfield32 + // 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"` + // 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"` + // 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"` + // 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"` +} + +// TPMASession represents a TPMA_SESSION. +// See definition in Part 2: Structures, section 8.4. +type TPMASession struct { + bitfield8 + // 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"` + // 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 { + bitfield8 + 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 { + bitfield32 + // indicates the command being selected + CommandIndex uint16 `gotpm:"bit=15:0"` + // 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"` +} + +// TPMAACT represents a TPMA_ACT. +// See definition in Part 2: Structures, section 8.12. +type TPMAACT struct { + bitfield32 + // 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"` +} + +// 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 + +// 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. + +// 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 + 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. +// 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/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..e67f7a40 --- /dev/null +++ b/direct/structures/tpma/tpma.go @@ -0,0 +1,34 @@ +// package tpma contains the TPM 2.0 structures prefixed by "TPMA_" +package tpma + +import ( + "github.com/google/go-tpm/direct/structures/internal" +) + +type Bitfield = internal.Bitfield +type BitSetter = internal.BitSetter +type BitGetter = internal.BitGetter + +// 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/direct/tpm2/audit.go b/direct/tpm2/audit.go new file mode 100644 index 00000000..e8933577 --- /dev/null +++ b/direct/tpm2/audit.go @@ -0,0 +1,88 @@ +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 tpmi.AlgHash + digest []byte +} + +// NewAudit initializes a new CommandAudit with the specified hash algorithm. +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. +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 + } + ha, err := a.hash.Hash() + if err != nil { + return err + } + h := ha.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 tpmi.AlgHash, 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) +} + +// 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 tpmi.AlgHash, 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, tpm.RCSuccess, cc, parms.Bytes()) +} diff --git a/direct/tpm2/audit_test.go b/direct/tpm2/audit_test.go new file mode 100644 index 00000000..968a68d1 --- /dev/null +++ b/direct/tpm2/audit_test.go @@ -0,0 +1,131 @@ +package tpm2 + +import ( + "bytes" + "testing" + + "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) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + // Create the audit session + sess, cleanup, err := HMACSession(thetpm, tpm.AlgSHA256, 16, Audit()) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup() + + // Create the AK for audit + createAKCmd := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InPublic: 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: false, + SignEncrypt: true, + }, + Parameters: tpmu.PublicParms{ + ECCDetail: &tpms.ECCParms{ + Scheme: tpmt.ECCScheme{ + Scheme: tpm.AlgECDSA, + Details: tpmu.AsymScheme{ + ECDSA: &tpms.SigSchemeECDSA{ + HashAlg: tpm.AlgSHA256, + }, + }, + }, + CurveID: tpm.ECCNistP256, + }, + }, + }, + }, + } + createAKRsp, err := createAKCmd.Execute(thetpm) + if err != nil { + t.Fatalf("%v", err) + } + defer func() { + // Flush the AK + flushCmd := FlushContext{FlushHandle: createAKRsp.ObjectHandle} + if _, err := flushCmd.Execute(thetpm); err != nil { + t.Errorf("%v", err) + } + }() + + 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 := []tpm.PT{ + tpm.PTFamilyIndicator, + tpm.PTLevel, + tpm.PTRevision, + tpm.PTDayofYear, + tpm.PTYear, + tpm.PTManufacturer, + } + for _, prop := range props { + getCmd := GetCapability{ + Capability: tpm.CapTPMProperties, + Property: uint32(prop), + PropertyCount: 1, + } + getRsp, err := getCmd.Execute(thetpm, sess) + if 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 := GetSessionAuditDigest{ + 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.Fatalf("%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/direct/tpm2/ek_test.go b/direct/tpm2/ek_test.go new file mode 100644 index 00000000..763e3a38 --- /dev/null +++ b/direct/tpm2/ek_test.go @@ -0,0 +1,239 @@ +package tpm2 + +import ( + "errors" + "testing" + + "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" + "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. +func TestEKPolicy(t *testing.T) { + templates := map[string]tpm2b.Public{ + "RSA": templates.RSAEKTemplate, + "ECC": templates.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(t transport.TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error { + cmd := PolicySecret{ + AuthHandle: tpm.RHEndorsement, + PolicySession: handle, + NonceTPM: nonceTPM, + } + _, err := cmd.Execute(t) + return err +} + +// This function tests a lot of combinations of authorizing the EK policy. +func ekTest(t *testing.T, ekTemplate tpm2b.Public) { + 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) + } + } + } + } + } + + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + // Create the EK + createEKCmd := CreatePrimary{ + PrimaryHandle: tpm.RHEndorsement, + InPublic: ekTemplate, + } + createEKRsp, err := createEKCmd.Execute(thetpm) + if 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 := FlushContext{createEKRsp.ObjectHandle} + if _, err := flushEKCmd.Execute(thetpm); 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 := Create{ + ParentHandle: NamedHandle{ + Handle: createEKRsp.ObjectHandle, + Name: createEKRsp.Name, + }, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + Data: tpm2b.Data{ + Buffer: data, + }, + }, + }, + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgKeyedHash, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + FixedTPM: true, + FixedParent: true, + UserWithAuth: true, + NoDA: true, + }, + }, + }, + } + + var sessions []Session + if c.decryptAnotherSession { + sessions = append(sessions, HMAC(tpm.AlgSHA1, 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(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(thetpm, tpm.AlgSHA256, 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(thetpm, s.Handle(), s.NonceTPM()); err != nil { + t.Fatalf("executing EK policy: %v", err) + } + } + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createEKRsp.ObjectHandle, + Name: createEKRsp.Name, + Auth: s, + } + + 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(thetpm, s.Handle(), s.NonceTPM()); err != nil { + t.Fatalf("executing EK policy: %v", err) + } + } + + // Try again and make sure it succeeds again. + if _, err = createBlobCmd.Execute(thetpm, 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 = createBlobCmd.Execute(thetpm, sessions...) + if !errors.Is(err, tpm.RCPolicyFail) { + t.Errorf("want TPM_RC_POLICY_FAIL, got %v", err) + } + 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 { + t.Errorf("want TPM_RC_POLICY_FAIL on session 1, got %v", err) + } + } + }) + } + +} diff --git a/direct/tpm2/reflect.go b/direct/tpm2/reflect.go new file mode 100644 index 00000000..0b24e722 --- /dev/null +++ b/direct/tpm2/reflect.go @@ -0,0 +1,1042 @@ +// Package tpm2 provides 1:1 mapping to TPM 2.0 APIs. +package tpm2 + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "reflect" + "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/tpma" + "github.com/google/go-tpm/direct/structures/tpms" + "github.com/google/go-tpm/direct/transport" +) + +const ( + // Chosen based on MAX_DIGEST_BUFFER, the length of the longest + // reasonable list returned by the reference implementation. + maxListLength uint32 = 1024 +) + +// execute sends the provided command and returns the TPM's response. +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()) + } + 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, err := cmdHandles(cmd) + if err != nil { + return err + } + parms, err := cmdParameters(cmd, sess) + 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...) + command = append(command, sessions...) + command = append(command, parms...) + + // Send the command via the transport. + response, err := t.Send(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, tpm.RCSuccess, 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, returning them as a byte slice. +// Returns an error if any of the values are not marshallable. +func Marshal(vs ...interface{}) ([]byte, error) { + var reflects []reflect.Value + for _, v := range vs { + reflects = append(reflects, reflect.ValueOf(v)) + } + var buf bytes.Buffer + for _, reflect := range reflects { + if err := marshal(&buf, reflect); err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} + +// 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 +} + +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. + 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 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) + } + } + 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 + // 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(tpm.AlgNull) + 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(tpm.RHNull)); 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(tpm.AlgNull)); 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 { + bg, ok := v.Interface().(tpma.BitGetter) + if !ok { + return fmt.Errorf("'%v' was not a BitGetter", v.Type().Name()) + } + 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 { + 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 + } + } + // 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 { + 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(tpm.AlgNull) { + 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 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(data []byte, 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 := buf.Write(data); err != nil { + return err + } + for _, reflect := range reflects { + if err := unmarshal(&buf, reflect); err != nil { + return err + } + } + return nil +} + +// 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, 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) + } + } + 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 +} + +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 + // 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 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) + } + } + 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") { + 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 { + bs, ok := v.Addr().Interface().(tpma.BitSetter) + if !ok { + return fmt.Errorf("'%v' was not a BitSetter", v.Addr().Type()) + } + 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-- { + 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) + } + } + // 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) + } else { + 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 +} + +// 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(tpm.AlgNull) { + 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 a slice of length 1, this is an + // 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) + val := "" + if len(assignment) > 1 { + val = assignment[1] + } + if len(assignment) > 0 && 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 { + // 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)) + } + } + + return result, nil +} + +// cmdHandles returns the handles area of the command. +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)) + + 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 + } + + binary.Write(result, binary.BigEndian, h.HandleValue()) + } + + return result.Bytes(), nil +} + +// cmdNames returns the names of the entities referenced by the handles of the command. +func cmdNames(cmd Command) ([]tpm2b.Name, error) { + handles := taggedMembers(reflect.ValueOf(cmd).Elem(), "handle", false) + var result []tpm2b.Name + 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 + } + + 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 +} + +// 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. + 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(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 + } + // 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 tpm.CC) []byte { + tag := tpm.STNoSessions + if hasSessions { + tag = tpm.STSessions + } + hdr := tpm.CmdHeader{ + 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 tpm.RspHeader + if err := unmarshal(rsp, reflect.ValueOf(&hdr).Elem()); err != nil { + return fmt.Errorf("unmarshalling TPM response: %w", err) + } + if hdr.ResponseCode != tpm.RCSuccess { + 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 tpm.RC, cc tpm.CC, names []tpm2b.Name, parms []byte, sess []Session) error { + for i, s := range sess { + var auth tpms.AuthResponse + 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 parameter + // decryption. + // If the command supports parameter encryption, the first parameter 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) + } + } + } + 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 new file mode 100644 index 00000000..bf9a49bb --- /dev/null +++ b/direct/tpm2/reflect_test.go @@ -0,0 +1,245 @@ +package tpm2 + +import ( + "bytes" + "fmt" + "reflect" + "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) { + 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) + } + 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...)) + } +} + +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 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)) + err := Unmarshal(data, 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). + 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) + } +} + +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, + } + 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 := tpma.CC{ + CommandIndex: 6, + NV: true, + } + 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) + }) +} + +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/direct/tpm2/sealing_test.go b/direct/tpm2/sealing_test.go new file mode 100644 index 00000000..208b527b --- /dev/null +++ b/direct/tpm2/sealing_test.go @@ -0,0 +1,434 @@ +package tpm2 + +import ( + "bytes" + "errors" + "testing" + + "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. +func TestUnseal(t *testing.T) { + templates := map[string]tpm2b.Public{ + "RSA": templates.RSASRKTemplate, + "ECC": templates.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 tpm2b.Public) { + thetpm, err := simulator.OpenSimulator() + if err != nil { + t.Fatalf("could not connect to TPM simulator: %v", err) + } + defer thetpm.Close() + + // Create the SRK + // Put a password on the SRK to test more of the flows. + srkAuth := []byte("mySRK") + createSRKCmd := CreatePrimary{ + PrimaryHandle: tpm.RHOwner, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ + Buffer: srkAuth, + }, + }, + }, + InPublic: srkTemplate, + } + 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 := FlushContext{createSRKRsp.ObjectHandle} + if _, err := flushSRKCmd.Execute(thetpm); 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 := Create{ + ParentHandle: AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: PasswordAuth(srkAuth), + }, + InSensitive: tpm2b.SensitiveCreate{ + Sensitive: tpms.SensitiveCreate{ + UserAuth: tpm2b.Auth{ + Buffer: auth, + }, + Data: tpm2b.Data{ + Buffer: data, + }, + }, + }, + InPublic: tpm2b.Public{ + PublicArea: tpmt.Public{ + Type: tpm.AlgKeyedHash, + NameAlg: tpm.AlgSHA256, + ObjectAttributes: tpma.Object{ + 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) { + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if 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 = 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) + } + }) + + // Create the blob, using the auth session also for decryption + t.Run("CreateDecrypt", func(t *testing.T) { + 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) + } + }) + + // Create the blob, using the auth session also for encryption + t.Run("CreateEncrypt", func(t *testing.T) { + 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) + } + }) + + // Create the blob, using the auth session also for decrypt and encrypt + t.Run("CreateDecryptEncrypt", func(t *testing.T) { + 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) + } + }) + + // Create the blob with decrypt and encrypt session + t.Run("CreateDecryptEncryptAudit", func(t *testing.T) { + createBlobCmd.ParentHandle = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptInOut), + Audit()), + } + createBlobRsp, err = createBlobCmd.Execute(thetpm) + if 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 = AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth), + AESEncryption(128, EncryptInOut), + Salted(createSRKRsp.ObjectHandle, createSRKRsp.OutPublic.PublicArea)), + } + 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 = 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, + 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) { + 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) { + 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) { + createBlobRsp, err = createBlobCmd.Execute(thetpm, + // Get weird with the algorithm and nonce choices. Mix lots of things together. + HMAC(tpm.AlgSHA1, 20, AESEncryption(128, EncryptIn)), + HMAC(tpm.AlgSHA384, 23, AESEncryption(128, EncryptOut))) + if 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) { + 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 := Load{ + ParentHandle: AuthHandle{ + Handle: createSRKRsp.ObjectHandle, + Name: createSRKRsp.Name, + Auth: HMAC(tpm.AlgSHA256, 16, Auth(srkAuth)), + }, + InPrivate: createBlobRsp.OutPrivate, + InPublic: createBlobRsp.OutPublic, + } + loadBlobRsp, err := loadBlobCmd.Execute(thetpm) + if err != nil { + t.Fatalf("%v", err) + } + defer func() { + // Flush the blob + flushBlobCmd := FlushContext{loadBlobRsp.ObjectHandle} + if _, err := flushBlobCmd.Execute(thetpm); err != nil { + t.Errorf("%v", err) + } + }() + + unsealCmd := Unseal{ + ItemHandle: NamedHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + }, + } + + // Unseal the blob with a password session + t.Run("WithPassword", func(t *testing.T) { + unsealCmd.ItemHandle = AuthHandle{ + Handle: loadBlobRsp.ObjectHandle, + Name: loadBlobRsp.Name, + Auth: PasswordAuth(auth), + } + unsealRsp, err := unsealCmd.Execute(thetpm) + if 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 = 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) + } + 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 { + 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 = 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) + } + 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 = 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) + } + 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(thetpm, tpm.AlgSHA1, 20, Auth(auth2)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup() + 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++ { + unsealRsp, err := unsealCmd.Execute(thetpm) + if 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(thetpm, tpm.AlgSHA256, 16, Auth(auth2), + AESEncryption(128, EncryptOut), + Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup() + 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++ { + unsealRsp, err := unsealCmd.Execute(thetpm) + if 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(thetpm, tpm.AlgSHA1, 16, Auth(auth2)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup1() + sess2, cleanup2, err := HMACSession(thetpm, tpm.AlgSHA384, 16, + AESEncryption(128, EncryptOut), + Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) + if err != nil { + t.Fatalf("%v", err) + } + defer cleanup2() + 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++ { + unsealRsp, err := unsealCmd.Execute(thetpm, sess2) + if 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/direct/tpm2/sessions.go b/direct/tpm2/sessions.go new file mode 100644 index 00000000..871afeb0 --- /dev/null +++ b/direct/tpm2/sessions.go @@ -0,0 +1,1033 @@ +package tpm2 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/elliptic" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "encoding/binary" + "fmt" + + "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" + "github.com/google/go-tpm/direct/transport" + legacy "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 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 transport.TPM) error + // The last nonceTPM for this session. + NonceTPM() tpm2b.Nonce + // 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 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 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. + 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() tpm.Handle +} + +// 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 transport.TPM) error { return nil } + +// Cleanup is not required and has no effect for a password session. +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 +// in where the HMAC should go, this is not used. +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 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 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 := tpma.Session{ + 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() 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 + password bool + bindHandle tpmi.DHEntity + bindName tpm2b.Name + bindAuth []byte + 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: tpmt.SymDef{ + Algorithm: tpm.AlgNull, + }, + bindHandle: tpm.RHNull, + saltHandle: tpm.RHNull, + } +} + +// 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 + } +} + +// 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 + o.password = true + } +} + +// Bound specifies that this session's session key should depend on the auth +// value of the given object. +func Bound(handle tpmi.DHEntity, name tpm2b.Name, 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 tpmi.DHObject, pub tpmt.Public) 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 specifies a decrypt session. + EncryptIn parameterEncryptionDirection = 1 + iota + // EncryptOut specifies an encrypt session. + EncryptOut + // EncryptInOut specifies a decrypt+encrypt session. + 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 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 = tpmt.SymDef{ + Algorithm: tpm.AlgAES, + KeyBits: tpmu.SymKeyBits{ + AES: helpers.NewKeyBits(keySize), + }, + Mode: tpmu.SymMode{ + AES: helpers.NewAlgID(tpm.AlgCFB), + }, + } + } +} + +// 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 tpmi.AlgHash + nonceSize int + handle tpm.Handle + sessionKey []byte + // last nonceCaller + nonceCaller tpm2b.Nonce + // last nonceTPM + 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 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: tpm.RHNull, + } + for _, opt := range opts { + opt(&sess.sessionOptions) + } + return &sess +} + +// HMACSession sets up a reusable HMAC session that needs to be closed. +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(), + hash: hash, + nonceSize: nonceSize, + handle: tpm.RHNull, + } + 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(t); err != nil { + return nil, nil, err + } + + closer := func() error { + fc := FlushContext{FlushHandle: sess.handle} + _, err := fc.Execute(t) + return err + } + + return &sess, closer, nil +} + +// Part 1, B.10.2 +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 tpmi.AlgHash + switch parms.Scheme.Scheme { + case tpm.AlgRSASSA: + hAlg = parms.Scheme.Details.RSASSA.HashAlg + case tpm.AlgRSAES: + hAlg = nameAlg + case tpm.AlgRSAPSS: + hAlg = parms.Scheme.Details.RSAPSS.HashAlg + case tpm.AlgOAEP: + hAlg = parms.Scheme.Details.OAEP.HashAlg + case tpm.AlgNull: + hAlg = nameAlg + default: + return nil, nil, fmt.Errorf("unsupported RSA salt key scheme: %v", parms.Scheme.Scheme) + } + 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(ha.New(), rand.Reader, rsaPub, salt, []byte("SECRET\x00")) + if err != nil { + return nil, nil, fmt.Errorf("encrypting salt: %w", err) + } + return &tpm2b.EncryptedSecret{ + Buffer: encSalt, + }, salt, nil +} + +// Part 1, 19.6.13 +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 := helpers.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) + 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 &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 tpmt.Public) (*tpm2b.EncryptedSecret, []byte, error) { + switch pub.Type { + case tpm.AlgRSA: + return getEncryptedSaltRSA(pub.NameAlg, pub.Parameters.RSADetail, pub.Unique.RSA) + 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) + } +} + +// Init initializes the session, just in time, if needed. +func (s *hmacSession) Init(t transport.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 = tpm2b.Nonce{ + Buffer: make([]byte, s.nonceSize), + } + if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { + return err + } + + // Start up the actual auth session. + sasCmd := StartAuthSession{ + TPMKey: s.saltHandle, + Bind: s.bindHandle, + NonceCaller: s.nonceCaller, + SessionType: tpm.SEHMAC, + Symmetric: s.symmetric, + AuthHash: s.hash, + } + var salt []byte + if s.saltHandle != tpm.RHNull { + var err error + var encSalt *tpm2b.EncryptedSecret + encSalt, salt, err = getEncryptedSalt(s.saltPub) + if err != nil { + return err + } + sasCmd.EncryptedSalt = *encSalt + } + sasRsp, err := sasCmd.Execute(t) + if err != nil { + return err + } + s.handle = tpm.Handle(sasRsp.SessionHandle.HandleValue()) + s.nonceTPM = sasRsp.NonceTPM + // Part 1, 19.6 + 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 = 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(t transport.TPM) error { + // The user is already responsible to clean up this session. + if s.attrs.ContinueSession { + return nil + } + fc := FlushContext{FlushHandle: s.handle} + if _, err := fc.Execute(t); err != nil { + return err + } + 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() tpm2b.Nonce { return s.nonceTPM } + +// To avoid a circular dependency on gotpm by tpm2, implement a +// tiny serialization by hand for tpma.Session here +func attrsToBytes(attrs tpma.Session) []byte { + var res byte + if attrs.ContinueSession { + res |= (1 << 0) + } + if attrs.AuditExclusive { + res |= (1 << 1) + } + if attrs.AuditReset { + res |= (1 << 2) + } + 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 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) + 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 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") + } + + // 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. + 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 := tpms.AuthCommand{ + Handle: s.handle, + Nonce: s.nonceCaller, + Attributes: s.attrs, + Authorization: tpm2b.Data{ + 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 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 = tpm.RHNull + } + + // 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. + 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 + } + // 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...) + 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 + } + 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...) + 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 + } + 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() 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 transport.TPM, handle tpmi.SHPolicy, nonceTPM tpm2b.Nonce) error + +// policySession generally implements the policy session. +type policySession struct { + sessionOptions + hash tpmi.AlgHash + nonceSize int + handle tpm.Handle + sessionKey []byte + // last nonceCaller + nonceCaller tpm2b.Nonce + // last nonceTPM + nonceTPM tpm2b.Nonce + 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 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: tpm.RHNull, + 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(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(), + hash: hash, + nonceSize: nonceSize, + handle: tpm.RHNull, + } + 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(t); err != nil { + return nil, nil, err + } + + closer := func() error { + 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(t transport.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 = tpm2b.Nonce{ + Buffer: make([]byte, s.nonceSize), + } + if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { + return err + } + + // Start up the actual auth session. + sasCmd := StartAuthSession{ + TPMKey: s.saltHandle, + Bind: s.bindHandle, + NonceCaller: s.nonceCaller, + SessionType: tpm.SEPolicy, + Symmetric: s.symmetric, + AuthHash: s.hash, + } + var salt []byte + if s.saltHandle != tpm.RHNull { + var err error + var encSalt *tpm2b.EncryptedSecret + encSalt, salt, err = getEncryptedSalt(s.saltPub) + if err != nil { + return err + } + sasCmd.EncryptedSalt = *encSalt + } + sasRsp, err := sasCmd.Execute(t) + if err != nil { + return err + } + s.handle = tpm.Handle(sasRsp.SessionHandle.HandleValue()) + s.nonceTPM = sasRsp.NonceTPM + // Part 1, 19.6 + if s.bindHandle != tpm.RHNull || len(salt) != 0 { + var authSalt []byte + authSalt = append(authSalt, s.bindAuth...) + authSalt = append(authSalt, salt...) + 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)(t, 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(t transport.TPM) error { + // The user is already responsible to clean up this session. + if s.attrs.ContinueSession { + return nil + } + fc := FlushContext{FlushHandle: s.handle} + if _, err := fc.Execute(t); err != nil { + return err + } + 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() tpm2b.Nonce { 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 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") + } + + 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. + 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 := tpms.AuthCommand{ + Handle: s.handle, + Nonce: s.nonceCaller, + Attributes: s.attrs, + Authorization: tpm2b.Data{ + 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 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 = tpm.RHNull + } + + 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. + 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 + } + // 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...) + 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 + } + 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...) + 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 + } + 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() tpm.Handle { + return s.handle +} diff --git a/direct/tpm2/tpm2.go b/direct/tpm2/tpm2.go new file mode 100644 index 00000000..0e52ce1a --- /dev/null +++ b/direct/tpm2/tpm2.go @@ -0,0 +1,539 @@ +// package tpm2 contains TPM 2.0 commands +package tpm2 + +import ( + "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" +) + +// 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 +} + +// Name implements the handle interface, shadowing the default +// behavior of the embedded tpm.Handle. +func (h NamedHandle) KnownName() *tpm2b.Name { + return &h.Name +} + +// 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 +// 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() tpm.CC +} + +// 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() tpm.CC +} + +// StartAuthSession is the input to TPM2_StartAuthSession. +// See definition in Part 3, Commands, section 11.1 +type StartAuthSession struct { + // handle of a loaded decrypt key used to encrypt salt + // may be TPM_RH_NULL + TPMKey handle `gotpm:"handle,nullable"` + // entity providing the authValue + // may be TPM_RH_NULL + Bind handle `gotpm:"handle,nullable"` + // initial nonceCaller, sets nonceTPM size for the session + // shall be at least 16 octets + NonceCaller tpm2b.Nonce + // value encrypted according to the type of tpmKey + // If tpmKey is TPM_RH_NULL, this shall be the Empty Buffer. + EncryptedSalt tpm2b.EncryptedSecret + // indicates the type of the session; simple HMAC or policy (including + // a trial policy) + SessionType tpm.SE + // the algorithm and key size for parameter encryption + // 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 tpmi.AlgHash +} + +// Command implements the Command interface. +func (*StartAuthSession) Command() tpm.CC { return tpm.CCStartAuthSession } + +// Execute executes the command and returns the response. +func (cmd *StartAuthSession) Execute(t transport.TPM, s ...Session) (*StartAuthSessionResponse, error) { + var rsp StartAuthSessionResponse + if err := execute(t, 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 tpmi.SHAuthSession `gotpm:"handle"` + // the initial nonce from the TPM, used in the computation of the sessionKey + NonceTPM tpm2b.Nonce +} + +// Response implements the Response interface. +func (*StartAuthSessionResponse) Response() tpm.CC { return tpm.CCStartAuthSession } + +// Create is the input to TPM2_Create. +// See definition in Part 3, Commands, section 12.1 +type Create struct { + // handle of parent for new object + ParentHandle handle `gotpm:"handle,auth"` + // the sensitive data + InSensitive tpm2b.SensitiveCreate + // the public template + 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 tpm2b.Data + // PCR that will be used in creation data + CreationPCR tpml.PCRSelection +} + +// Command implements the Command interface. +func (*Create) Command() tpm.CC { return tpm.CCCreate } + +// Execute executes the command and returns the response. +func (cmd *Create) Execute(t transport.TPM, s ...Session) (*CreateResponse, error) { + var rsp CreateResponse + if err := execute(t, 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 tpm2b.Private + // the public portion of the created object + OutPublic tpm2b.Public + // contains a tpms._CREATION_DATA + CreationData tpm2b.CreationData + // digest of creationData using nameAlg of outPublic + CreationHash tpm2b.Digest + // ticket used by TPM2_CertifyCreation() to validate that the + // creation data was produced by the TPM + CreationTicket tpmt.TKCreation +} + +// Response implements the Response interface. +func (*CreateResponse) Response() tpm.CC { return tpm.CCCreate } + +// Load is the input to TPM2_Load. +// See definition in Part 3, Commands, section 12.2 +type Load struct { + // handle of parent for new object + ParentHandle handle `gotpm:"handle,auth"` + // the private portion of the object + InPrivate tpm2b.Private + // the public portion of the object + InPublic tpm2b.Public +} + +// Command implements the Command interface. +func (*Load) Command() tpm.CC { return tpm.CCLoad } + +// Execute executes the command and returns the response. +func (cmd *Load) Execute(t transport.TPM, s ...Session) (*LoadResponse, error) { + var rsp LoadResponse + if err := execute(t, 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 tpm.Handle `gotpm:"handle"` + // Name of the loaded object + Name tpm2b.Name +} + +// Response implements the Response interface. +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 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 transport.TPM, s ...Session) (*UnsealResponse, error) { + var rsp UnsealResponse + if err := execute(t, cmd, &rsp, s...); err != nil { + return nil, err + } + return &rsp, nil +} + +// UnsealResponse is the response from TPM2_Unseal. +type UnsealResponse struct { + OutData tpm2b.SensitiveData +} + +// Response implements the Response interface. +func (*UnsealResponse) Response() tpm.CC { return tpm.CCUnseal } + +// Quote is the input to TPM2_Quote. +// See definition in Part 3, Commands, section 18.4 +type Quote struct { + // handle of key that will perform signature + 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 + InScheme tpmt.SigScheme + // PCR set to quote + PCRSelect tpml.PCRSelection +} + +// Command implements the Command interface. +func (*Quote) Command() tpm.CC { return tpm.CCQuote } + +// Execute executes the command and returns the response. +func (cmd *Quote) Execute(t transport.TPM, s ...Session) (*QuoteResponse, error) { + var rsp QuoteResponse + if err := execute(t, 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 tpm2b.Attest + // the signature over quoted + Signature tpmt.Signature +} + +// Response implements the Response interface. +func (*QuoteResponse) Response() tpm.CC { return tpm.CCQuote } + +// GetSessionAuditDigest is the input to TPM2_GetSessionAuditDigest. +// See definition in Part 3, Commands, section 18.5 +type GetSessionAuditDigest struct { + // handle of the privacy administrator (TPM_RH_ENDORSEMENT) + PrivacyAdminHandle handle `gotpm:"handle,auth"` + // handle of the signing key + SignHandle handle `gotpm:"handle,auth"` + // handle of the audit session + 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 + InScheme tpmt.SigScheme +} + +// Command implements the Command interface. +func (*GetSessionAuditDigest) Command() tpm.CC { return tpm.CCGetSessionAuditDigest } + +// Execute executes the command and returns the response. +func (cmd *GetSessionAuditDigest) Execute(t transport.TPM, s ...Session) (*GetSessionAuditDigestResponse, error) { + var rsp GetSessionAuditDigestResponse + if err := execute(t, 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 tpm2b.Attest + // the signature over auditInfo + Signature tpmt.Signature +} + +// Response implements the Response interface. +func (*GetSessionAuditDigestResponse) Response() tpm.CC { return tpm.CCGetSessionAuditDigest } + +// PCRExtend is the input to TPM2_PCR_Extend. +// See definition in Part 3, Commands, section 22.2 +type PCRExtend struct { + // handle of the PCR + PCRHandle handle `gotpm:"handle,auth"` + // list of tagged digest values to be extended + Digests tpml.DigestValues +} + +// Command implements the Command interface. +func (*PCRExtend) Command() tpm.CC { return tpm.CCPCRExtend } + +// Execute executes the command and returns the response. +func (cmd *PCRExtend) Execute(t transport.TPM, s ...Session) (*PCRExtendResponse, error) { + var rsp PCRExtendResponse + if err := execute(t, 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() tpm.CC { return tpm.CCPCRExtend } + +// PCREvent is the input to TPM2_PCR_Event. +// See definition in Part 3, Commands, section 22.3 +type PCREvent struct { + // Handle of the PCR + PCRHandle handle `gotpm:"handle,auth"` + // Event data in sized buffer + EventData tpm2b.Event +} + +// Command implements the Command interface. +func (*PCREvent) Command() tpm.CC { return tpm.CCPCREvent } + +// Execute executes the command and returns the response. +func (cmd *PCREvent) Execute(t transport.TPM, s ...Session) (*PCREventResponse, error) { + var rsp PCREventResponse + if err := execute(t, 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() tpm.CC { return tpm.CCPCREvent } + +// PCRRead is the input to TPM2_PCR_Read. +// See definition in Part 3, Commands, section 22.4 +type PCRRead struct { + // The selection of PCR to read + PCRSelectionIn tpml.PCRSelection +} + +// Command implements the Command interface. +func (*PCRRead) Command() tpm.CC { return tpm.CCPCRRead } + +// Execute executes the command and returns the response. +func (cmd *PCRRead) Execute(t transport.TPM, s ...Session) (*PCRReadResponse, error) { + var rsp PCRReadResponse + if err := execute(t, 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 tpml.PCRSelection + // the contents of the PCR indicated in pcrSelectOut-> pcrSelection[] as tagged digests + PCRValues tpml.Digest +} + +// Response implements the Response interface. +func (*PCRReadResponse) Response() tpm.CC { return tpm.CCPCRRead } + +// PolicySecret is the input to TPM2_PolicySecret. +// See definition in Part 3, Commands, section 23.4 +type PolicySecret struct { + // handle for an entity providing the authorization + AuthHandle handle `gotpm:"handle,auth"` + // handle for the policy session being extended + PolicySession handle `gotpm:"handle"` + // the policy nonce for the session + NonceTPM tpm2b.Nonce + // digest of the command parameters to which this authorization is limited + CPHashA tpm2b.Digest + // a reference to a policy relating to the authorization – may be the Empty Buffer + 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 (*PolicySecret) Command() tpm.CC { return tpm.CCPolicySecret } + +// Execute executes the command and returns the response. +func (cmd *PolicySecret) Execute(t transport.TPM, s ...Session) (*PolicySecretResponse, error) { + var rsp PolicySecretResponse + if err := execute(t, 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 tpm2b.Timeout + // produced if the command succeeds and expiration in the command was non-zero + PolicyTicket tpmt.TKAuth +} + +// Response implements the Response interface. +func (*PolicySecretResponse) Response() tpm.CC { return tpm.CCPolicySecret } + +// CreatePrimary is the input to TPM2_CreatePrimary. +// See definition in Part 3, Commands, section 24.1 +type CreatePrimary struct { + // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, + // or TPM_RH_NULL + PrimaryHandle handle `gotpm:"handle,auth"` + // the sensitive data + InSensitive tpm2b.SensitiveCreate + // the public template + 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 tpm2b.Data + // PCR that will be used in creation data + CreationPCR tpml.PCRSelection +} + +// Command implements the Command interface. +func (*CreatePrimary) Command() tpm.CC { return tpm.CCCreatePrimary } + +// Execute executes the command and returns the response. +func (cmd *CreatePrimary) Execute(t transport.TPM, s ...Session) (*CreatePrimaryResponse, error) { + var rsp CreatePrimaryResponse + if err := execute(t, 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 tpm.Handle `gotpm:"handle"` + // the public portion of the created object + OutPublic tpm2b.Public + // contains a tpms._CREATION_DATA + CreationData tpm2b.CreationData + // digest of creationData using nameAlg of outPublic + CreationHash tpm2b.Digest + // ticket used by TPM2_CertifyCreation() to validate that the + // creation data was produced by the TPM + CreationTicket tpmt.TKCreation + // the name of the created object + Name tpm2b.Name +} + +// Response implements the Response interface. +func (*CreatePrimaryResponse) Response() tpm.CC { return tpm.CCCreatePrimary } + +// FlushContext is the input to TPM2_FlushContext. +// See definition in Part 3, Commands, section 28.4 +type FlushContext struct { + // the handle of the item to flush + 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 transport.TPM, s ...Session) (*FlushContextResponse, error) { + var rsp FlushContextResponse + if err := execute(t, 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() tpm.CC { return tpm.CCFlushContext } + +// GetCapability is the input to TPM2_GetCapability. +// See definition in Part 3, Commands, section 30.2 +type GetCapability struct { + // group selection; determines the format of the response + Capability tpm.Cap + // further definition of information + Property uint32 + // number of properties of the indicated type to return + PropertyCount uint32 +} + +// Command implements the Command interface. +func (*GetCapability) Command() tpm.CC { return tpm.CCGetCapability } + +// Execute executes the command and returns the response. +func (cmd *GetCapability) Execute(t transport.TPM, s ...Session) (*GetCapabilityResponse, error) { + var rsp GetCapabilityResponse + if err := execute(t, 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 tpmi.YesNo + // the capability data + CapabilityData tpms.CapabilityData +} + +// Response implements the Response interface. +func (*GetCapabilityResponse) Response() tpm.CC { return tpm.CCGetCapability } 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() +} diff --git a/go.mod b/go.mod index 6a26af26..38c39de2 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,11 @@ 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 ) + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect 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/kdf.go b/tpm2/kdf.go index 846cf346..f2fe507e 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,59 @@ 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 +} + +// 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. +// 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() + 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 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 +// 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 } // 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 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 +// 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 +103,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 }