// Copyright (c) 2018, Google LLC All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package tpm2 supports direct communication with a TPM 2.0 device under Linux. package tpm2 import ( "bytes" "crypto" "fmt" "io" "github.com/google/go-tpm/tpmutil" ) // GetRandom gets random bytes from the TPM. func GetRandom(rw io.ReadWriter, size uint16) ([]byte, error) { resp, err := runCommand(rw, TagNoSessions, CmdGetRandom, size) if err != nil { return nil, err } var randBytes tpmutil.U16Bytes if _, err := tpmutil.Unpack(resp, &randBytes); err != nil { return nil, err } return randBytes, nil } // FlushContext removes an object or session under handle to be removed from // the TPM. This must be called for any loaded handle to avoid out-of-memory // errors in TPM. func FlushContext(rw io.ReadWriter, handle tpmutil.Handle) error { _, err := runCommand(rw, TagNoSessions, CmdFlushContext, handle) return err } func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) { if len(sel) == 0 { return tpmutil.Pack(uint32(0)) } // PCR selection is a variable-size bitmask, where position of a set bit is // the selected PCR index. // Size of the bitmask in bytes is pre-pended. It should be at least // sizeOfPCRSelect. // // For example, selecting PCRs 3 and 9 looks like: // size(3) mask mask mask // 00000011 00000000 00000001 00000100 var retBytes []byte for _, s := range sel { if len(s.PCRs) == 0 { return tpmutil.Pack(uint32(0)) } ts := tpmsPCRSelection{ Hash: s.Hash, Size: sizeOfPCRSelect, PCRs: make(tpmutil.RawBytes, sizeOfPCRSelect), } // s[i].PCRs parameter is indexes of PCRs, convert that to set bits. for _, n := range s.PCRs { if n >= 8*sizeOfPCRSelect { return nil, fmt.Errorf("PCR index %d is out of range (exceeds maximum value %d)", n, 8*sizeOfPCRSelect-1) } byteNum := n / 8 bytePos := byte(1 << byte(n%8)) ts.PCRs[byteNum] |= bytePos } tmpBytes, err := tpmutil.Pack(ts) if err != nil { return nil, err } retBytes = append(retBytes, tmpBytes...) } tmpSize, err := tpmutil.Pack(uint32(len(sel))) if err != nil { return nil, err } retBytes = append(tmpSize, retBytes...) return retBytes, nil } func decodeTPMLPCRSelection(buf *bytes.Buffer) ([]PCRSelection, error) { var count uint32 var sel []PCRSelection // This unpacks buffer which is of type TPMLPCRSelection // and returns the count of TPMSPCRSelections. if err := tpmutil.UnpackBuf(buf, &count); err != nil { return sel, err } var ts tpmsPCRSelection for i := 0; i < int(count); i++ { var s PCRSelection if err := tpmutil.UnpackBuf(buf, &ts.Hash, &ts.Size); err != nil { return sel, err } ts.PCRs = make(tpmutil.RawBytes, ts.Size) if _, err := buf.Read(ts.PCRs); err != nil { return sel, err } s.Hash = ts.Hash for j := 0; j < int(ts.Size); j++ { for k := 0; k < 8; k++ { set := ts.PCRs[j] & byte(1< 0, nil case CapabilityAlgs: var numAlgs uint32 if err := tpmutil.UnpackBuf(buf, &numAlgs); err != nil { return nil, false, fmt.Errorf("could not unpack algorithm count: %v", err) } var algs []interface{} for i := 0; i < int(numAlgs); i++ { var alg AlgorithmDescription if err := tpmutil.UnpackBuf(buf, &alg); err != nil { return nil, false, fmt.Errorf("could not unpack algorithm description: %v", err) } algs = append(algs, alg) } return algs, moreData > 0, nil case CapabilityTPMProperties: var numProps uint32 if err := tpmutil.UnpackBuf(buf, &numProps); err != nil { return nil, false, fmt.Errorf("could not unpack fixed properties count: %v", err) } var props []interface{} for i := 0; i < int(numProps); i++ { var prop TaggedProperty if err := tpmutil.UnpackBuf(buf, &prop); err != nil { return nil, false, fmt.Errorf("could not unpack tagged property: %v", err) } props = append(props, prop) } return props, moreData > 0, nil case CapabilityPCRs: var pcrss []interface{} pcrs, err := decodeTPMLPCRSelection(buf) if err != nil { return nil, false, fmt.Errorf("could not unpack pcr selection: %v", err) } for i := 0; i < len(pcrs); i++ { pcrss = append(pcrss, pcrs[i]) } return pcrss, moreData > 0, nil default: return nil, false, fmt.Errorf("unsupported capability %v", capReported) } } // GetCapability returns various information about the TPM state. // // Currently only CapabilityHandles (list active handles) and CapabilityAlgs // (list supported algorithms) are supported. CapabilityHandles will return // a []tpmutil.Handle for vals, CapabilityAlgs will return // []AlgorithmDescription. // // moreData is true if the TPM indicated that more data is available. Follow // the spec for the capability in question on how to query for more data. func GetCapability(rw io.ReadWriter, capa Capability, count, property uint32) (vals []interface{}, moreData bool, err error) { resp, err := runCommand(rw, TagNoSessions, CmdGetCapability, capa, property, count) if err != nil { return nil, false, err } return decodeGetCapability(resp) } // GetManufacturer returns the manufacturer ID func GetManufacturer(rw io.ReadWriter) ([]byte, error) { caps, _, err := GetCapability(rw, CapabilityTPMProperties, 1, uint32(Manufacturer)) if err != nil { return nil, err } prop := caps[0].(TaggedProperty) return tpmutil.Pack(prop.Value) } func encodeAuthArea(sections ...AuthCommand) ([]byte, error) { var res tpmutil.RawBytes for _, s := range sections { buf, err := tpmutil.Pack(s) if err != nil { return nil, err } res = append(res, buf...) } size, err := tpmutil.Pack(uint32(len(res))) if err != nil { return nil, err } return concat(size, res) } func encodePCREvent(pcr tpmutil.Handle, eventData []byte) ([]byte, error) { ha, err := tpmutil.Pack(pcr) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: EmptyAuth}) if err != nil { return nil, err } event, err := tpmutil.Pack(tpmutil.U16Bytes(eventData)) if err != nil { return nil, err } return concat(ha, auth, event) } // PCREvent writes an update to the specified PCR. func PCREvent(rw io.ReadWriter, pcr tpmutil.Handle, eventData []byte) error { Cmd, err := encodePCREvent(pcr, eventData) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdPCREvent, tpmutil.RawBytes(Cmd)) return err } func encodeSensitiveArea(s tpmsSensitiveCreate) ([]byte, error) { // TPMS_SENSITIVE_CREATE buf, err := tpmutil.Pack(s) if err != nil { return nil, err } // TPM2B_SENSITIVE_CREATE return tpmutil.Pack(tpmutil.U16Bytes(buf)) } // encodeCreate works for both TPM2_Create and TPM2_CreatePrimary. func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, sensitiveData []byte, pub Public) ([]byte, error) { parent, err := tpmutil.Pack(owner) if err != nil { return nil, err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } inSensitive, err := encodeSensitiveArea(tpmsSensitiveCreate{ UserAuth: []byte(ownerPassword), Data: sensitiveData, }) if err != nil { return nil, err } inPublic, err := pub.Encode() if err != nil { return nil, err } publicBlob, err := tpmutil.Pack(tpmutil.U16Bytes(inPublic)) if err != nil { return nil, err } outsideInfo, err := tpmutil.Pack(tpmutil.U16Bytes(nil)) if err != nil { return nil, err } creationPCR, err := encodeTPMLPCRSelection(sel) if err != nil { return nil, err } return concat( parent, encodedAuth, inSensitive, publicBlob, outsideInfo, creationPCR, ) } func decodeCreatePrimary(in []byte) (handle tpmutil.Handle, public, creationData, creationHash tpmutil.U16Bytes, ticket Ticket, creationName tpmutil.U16Bytes, err error) { var paramSize uint32 buf := bytes.NewBuffer(in) // Handle and auth data. if err := tpmutil.UnpackBuf(buf, &handle, ¶mSize); err != nil { return 0, nil, nil, nil, Ticket{}, nil, fmt.Errorf("decoding handle, paramSize: %v", err) } if err := tpmutil.UnpackBuf(buf, &public, &creationData, &creationHash, &ticket, &creationName); err != nil { return 0, nil, nil, nil, Ticket{}, nil, fmt.Errorf("decoding public, creationData, creationHash, ticket, creationName: %v", err) } if _, err := DecodeCreationData(creationData); err != nil { return 0, nil, nil, nil, Ticket{}, nil, fmt.Errorf("parsing CreationData: %v", err) } return handle, public, creationData, creationHash, ticket, creationName, err } // CreatePrimary initializes the primary key in a given hierarchy. // The second return value is the public part of the generated key. func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, p Public) (tpmutil.Handle, crypto.PublicKey, error) { hnd, public, _, _, _, _, err := CreatePrimaryEx(rw, owner, sel, parentPassword, ownerPassword, p) if err != nil { return 0, nil, err } pub, err := DecodePublic(public) if err != nil { return 0, nil, fmt.Errorf("parsing public: %v", err) } pubKey, err := pub.Key() if err != nil { return 0, nil, fmt.Errorf("extracting cryto.PublicKey from Public part of primary key: %v", err) } return hnd, pubKey, err } // CreatePrimaryEx initializes the primary key in a given hierarchy. // This function differs from CreatePrimary in that all response elements // are returned, and they are returned in relatively raw form. func CreatePrimaryEx(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (keyHandle tpmutil.Handle, public, creationData, creationHash []byte, ticket Ticket, creationName []byte, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} Cmd, err := encodeCreate(owner, sel, auth, ownerPassword, nil /*inSensitive*/, pub) if err != nil { return 0, nil, nil, nil, Ticket{}, nil, err } resp, err := runCommand(rw, TagSessions, CmdCreatePrimary, tpmutil.RawBytes(Cmd)) if err != nil { return 0, nil, nil, nil, Ticket{}, nil, err } return decodeCreatePrimary(resp) } // CreatePrimaryRawTemplate is CreatePrimary, but with the public template // (TPMT_PUBLIC) provided pre-encoded. This is commonly used with key templates // stored in NV RAM. func CreatePrimaryRawTemplate(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, public []byte) (tpmutil.Handle, crypto.PublicKey, error) { pub, err := DecodePublic(public) if err != nil { return 0, nil, fmt.Errorf("parsing input template: %v", err) } return CreatePrimary(rw, owner, sel, parentPassword, ownerPassword, pub) } func decodeReadPublic(in []byte) (Public, []byte, []byte, error) { var resp struct { Public tpmutil.U16Bytes Name tpmutil.U16Bytes QualifiedName tpmutil.U16Bytes } if _, err := tpmutil.Unpack(in, &resp); err != nil { return Public{}, nil, nil, err } pub, err := DecodePublic(resp.Public) if err != nil { return Public{}, nil, nil, err } return pub, resp.Name, resp.QualifiedName, nil } // ReadPublic reads the public part of the object under handle. // Returns the public data, name and qualified name. func ReadPublic(rw io.ReadWriter, handle tpmutil.Handle) (Public, []byte, []byte, error) { resp, err := runCommand(rw, TagNoSessions, CmdReadPublic, handle) if err != nil { return Public{}, nil, nil, err } return decodeReadPublic(resp) } func decodeCreate(in []byte) (private, public, creationData, creationHash tpmutil.U16Bytes, creationTicket Ticket, err error) { buf := bytes.NewBuffer(in) var paramSize uint32 if err := tpmutil.UnpackBuf(buf, ¶mSize, &private, &public, &creationData, &creationHash, &creationTicket); err != nil { return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding Handle, Private, Public, CreationData, CreationHash, CreationTicket: %v", err) } if err != nil { return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding CreationTicket: %v", err) } if _, err := DecodeCreationData(creationData); err != nil { return nil, nil, nil, nil, Ticket{}, fmt.Errorf("decoding CreationData: %v", err) } return private, public, creationData, creationHash, creationTicket, nil } func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub) if err != nil { return nil, nil, nil, nil, Ticket{}, err } resp, err := runCommand(rw, TagSessions, CmdCreate, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, nil, nil, Ticket{}, err } return decodeCreate(resp) } // CreateKey creates a new key pair under the owner handle. // Returns private key and public key blobs as well as the // creation data, a hash of said data and the creation ticket. func CreateKey(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) } // CreateKeyUsingAuth creates a new key pair under the owner handle using the // provided AuthCommand. Returns private key and public key blobs as well as // the creation data, a hash of said data, and the creation ticket. func CreateKeyUsingAuth(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) } // CreateKeyWithSensitive is very similar to CreateKey, except // that it can take in a piece of sensitive data. func CreateKeyWithSensitive(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public, sensitive []byte) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} return create(rw, owner, auth, ownerPassword, sensitive, pub, sel) } // Seal creates a data blob object that seals the sensitive data under a parent and with a // password and auth policy. Access to the parent must be available with a simple password. // Returns private and public portions of the created object. func Seal(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objectPassword string, objectAuthPolicy []byte, sensitiveData []byte) ([]byte, []byte, error) { inPublic := Public{ Type: AlgKeyedHash, NameAlg: AlgSHA256, Attributes: FlagFixedTPM | FlagFixedParent, AuthPolicy: objectAuthPolicy, } auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} private, public, _, _, _, err := create(rw, parentHandle, auth, objectPassword, sensitiveData, inPublic, PCRSelection{}) if err != nil { return nil, nil, err } return private, public, nil } func encodeImport(parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob, symSeed, encryptionKey tpmutil.U16Bytes, sym *SymScheme) ([]byte, error) { ph, err := tpmutil.Pack(parentHandle) if err != nil { return nil, err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } data, err := tpmutil.Pack(encryptionKey, publicBlob, privateBlob, symSeed) if err != nil { return nil, err } encodedScheme, err := sym.encode() if err != nil { return nil, err } return concat(ph, encodedAuth, data, encodedScheme) } func decodeImport(resp []byte) ([]byte, error) { var paramSize uint32 var outPrivate tpmutil.U16Bytes _, err := tpmutil.Unpack(resp, ¶mSize, &outPrivate) return outPrivate, err } // Import allows a user to import a key created on a different computer // or in a different TPM. The publicBlob and privateBlob must always be // provided. symSeed should be non-nil iff an "outer wrapper" is used. Both of // encryptionKey and sym should be non-nil iff an "inner wrapper" is used. func Import(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob, symSeed, encryptionKey []byte, sym *SymScheme) ([]byte, error) { Cmd, err := encodeImport(parentHandle, auth, publicBlob, privateBlob, symSeed, encryptionKey, sym) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdImport, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeImport(resp) } func encodeLoad(parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob tpmutil.U16Bytes) ([]byte, error) { ah, err := tpmutil.Pack(parentHandle) if err != nil { return nil, err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } params, err := tpmutil.Pack(privateBlob, publicBlob) if err != nil { return nil, err } return concat(ah, encodedAuth, params) } func decodeLoad(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle var paramSize uint32 var name tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, ¶mSize, &name); err != nil { return 0, nil, err } return handle, name, nil } // Load loads public/private blobs into an object in the TPM. // Returns loaded object handle and its name. func Load(rw io.ReadWriter, parentHandle tpmutil.Handle, parentAuth string, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentAuth)} return LoadUsingAuth(rw, parentHandle, auth, publicBlob, privateBlob) } // LoadUsingAuth loads public/private blobs into an object in the TPM using the // provided AuthCommand. Returns loaded object handle and its name. func LoadUsingAuth(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, publicBlob, privateBlob []byte) (tpmutil.Handle, []byte, error) { Cmd, err := encodeLoad(parentHandle, auth, publicBlob, privateBlob) if err != nil { return 0, nil, err } resp, err := runCommand(rw, TagSessions, CmdLoad, tpmutil.RawBytes(Cmd)) if err != nil { return 0, nil, err } return decodeLoad(resp) } func encodeLoadExternal(pub Public, private Private, hierarchy tpmutil.Handle) ([]byte, error) { privateBlob, err := private.Encode() if err != nil { return nil, err } publicBlob, err := pub.Encode() if err != nil { return nil, err } return tpmutil.Pack(tpmutil.U16Bytes(privateBlob), tpmutil.U16Bytes(publicBlob), hierarchy) } func decodeLoadExternal(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle var name tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, &name); err != nil { return 0, nil, err } return handle, name, nil } // LoadExternal loads a public (and optionally a private) key into an object in // the TPM. Returns loaded object handle and its name. func LoadExternal(rw io.ReadWriter, pub Public, private Private, hierarchy tpmutil.Handle) (tpmutil.Handle, []byte, error) { Cmd, err := encodeLoadExternal(pub, private, hierarchy) if err != nil { return 0, nil, err } resp, err := runCommand(rw, TagNoSessions, CmdLoadExternal, tpmutil.RawBytes(Cmd)) if err != nil { return 0, nil, err } handle, name, err := decodeLoadExternal(resp) if err != nil { return 0, nil, err } return handle, name, nil } // PolicyPassword sets password authorization requirement on the object. func PolicyPassword(rw io.ReadWriter, handle tpmutil.Handle) error { _, err := runCommand(rw, TagNoSessions, CmdPolicyPassword, handle) return err } func encodePolicySecret(entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef tpmutil.U16Bytes, expiry int32) ([]byte, error) { auth, err := encodeAuthArea(entityAuth) if err != nil { return nil, err } handles, err := tpmutil.Pack(entityHandle, policyHandle) if err != nil { return nil, err } params, err := tpmutil.Pack(policyNonce, cpHash, policyRef, expiry) if err != nil { return nil, err } return concat(handles, auth, params) } func decodePolicySecret(in []byte) (*Ticket, error) { buf := bytes.NewBuffer(in) var paramSize uint32 var timeout tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, ¶mSize, &timeout); err != nil { return nil, fmt.Errorf("decoding timeout: %v", err) } var t Ticket if err := tpmutil.UnpackBuf(buf, &t); err != nil { return nil, fmt.Errorf("decoding ticket: %v", err) } return &t, nil } // PolicySecret sets a secret authorization requirement on the provided entity. // If expiry is non-zero, the authorization is valid for expiry seconds. func PolicySecret(rw io.ReadWriter, entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32) (*Ticket, error) { Cmd, err := encodePolicySecret(entityHandle, entityAuth, policyHandle, policyNonce, cpHash, policyRef, expiry) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdPolicySecret, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } // Tickets are only provided if expiry is set. if expiry != 0 { return decodePolicySecret(resp) } return nil, nil } func encodePolicyPCR(session tpmutil.Handle, expectedDigest tpmutil.U16Bytes, sel PCRSelection) ([]byte, error) { params, err := tpmutil.Pack(session, expectedDigest) if err != nil { return nil, err } pcrs, err := encodeTPMLPCRSelection(sel) if err != nil { return nil, err } return concat(params, pcrs) } // PolicyPCR sets PCR state binding for authorization on a session. // // expectedDigest is optional. When specified, it's compared against the digest // of PCRs matched by sel. // // Note that expectedDigest must be a *digest* of the expected PCR value. You // must compute the digest manually. ReadPCR returns raw PCR values, not their // digests. // If you wish to select multiple PCRs, concatenate their values before // computing the digest. See "TPM 2.0 Part 1, Selecting Multiple PCR". func PolicyPCR(rw io.ReadWriter, session tpmutil.Handle, expectedDigest []byte, sel PCRSelection) error { Cmd, err := encodePolicyPCR(session, expectedDigest, sel) if err != nil { return err } _, err = runCommand(rw, TagNoSessions, CmdPolicyPCR, tpmutil.RawBytes(Cmd)) return err } // PolicyOr compares PolicySession→Digest against the list of provided values. // If the current Session→Digest does not match any value in the list, // the TPM shall return TPM_RC_VALUE. Otherwise, the TPM will reset policySession→Digest // to a Zero Digest. Then policySession→Digest is extended by the concatenation of // TPM_CC_PolicyOR and the concatenation of all of the digests. func PolicyOr(rw io.ReadWriter, session tpmutil.Handle, digests TPMLDigest) error { d, err := digests.Encode() if err != nil { return err } data, err := tpmutil.Pack(session, d) if err != nil { return err } _, err = runCommand(rw, TagNoSessions, CmdPolicyOr, data) return err } // PolicyGetDigest returns the current policyDigest of the session. func PolicyGetDigest(rw io.ReadWriter, handle tpmutil.Handle) ([]byte, error) { resp, err := runCommand(rw, TagNoSessions, CmdPolicyGetDigest, handle) if err != nil { return nil, err } var digest tpmutil.U16Bytes _, err = tpmutil.Unpack(resp, &digest) return digest, err } func encodeStartAuthSession(tpmKey, bindKey tpmutil.Handle, nonceCaller, secret tpmutil.U16Bytes, se SessionType, sym, hashAlg Algorithm) ([]byte, error) { ha, err := tpmutil.Pack(tpmKey, bindKey) if err != nil { return nil, err } params, err := tpmutil.Pack(nonceCaller, secret, se, sym, hashAlg) if err != nil { return nil, err } return concat(ha, params) } func decodeStartAuthSession(in []byte) (tpmutil.Handle, []byte, error) { var handle tpmutil.Handle var nonce tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &handle, &nonce); err != nil { return 0, nil, err } return handle, nonce, nil } // StartAuthSession initializes a session object. // Returns session handle and the initial nonce from the TPM. func StartAuthSession(rw io.ReadWriter, tpmKey, bindKey tpmutil.Handle, nonceCaller, secret []byte, se SessionType, sym, hashAlg Algorithm) (tpmutil.Handle, []byte, error) { Cmd, err := encodeStartAuthSession(tpmKey, bindKey, nonceCaller, secret, se, sym, hashAlg) if err != nil { return 0, nil, err } resp, err := runCommand(rw, TagNoSessions, CmdStartAuthSession, tpmutil.RawBytes(Cmd)) if err != nil { return 0, nil, err } return decodeStartAuthSession(resp) } func encodeUnseal(sessionHandle, itemHandle tpmutil.Handle, password string) ([]byte, error) { ha, err := tpmutil.Pack(itemHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: sessionHandle, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } return concat(ha, auth) } func decodeUnseal(in []byte) ([]byte, error) { var paramSize uint32 var unsealed tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, ¶mSize, &unsealed); err != nil { return nil, err } return unsealed, nil } // Unseal returns the data for a loaded sealed object. func Unseal(rw io.ReadWriter, itemHandle tpmutil.Handle, password string) ([]byte, error) { return UnsealWithSession(rw, HandlePasswordSession, itemHandle, password) } // UnsealWithSession returns the data for a loaded sealed object. func UnsealWithSession(rw io.ReadWriter, sessionHandle, itemHandle tpmutil.Handle, password string) ([]byte, error) { Cmd, err := encodeUnseal(sessionHandle, itemHandle, password) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdUnseal, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeUnseal(resp) } func encodeQuote(signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote tpmutil.U16Bytes, sel PCRSelection, sigAlg Algorithm) ([]byte, error) { ha, err := tpmutil.Pack(signingHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)}) if err != nil { return nil, err } params, err := tpmutil.Pack(toQuote, sigAlg) if err != nil { return nil, err } pcrs, err := encodeTPMLPCRSelection(sel) if err != nil { return nil, err } return concat(ha, auth, params, pcrs) } func decodeQuote(in []byte) ([]byte, []byte, error) { buf := bytes.NewBuffer(in) var paramSize uint32 if err := tpmutil.UnpackBuf(buf, ¶mSize); err != nil { return nil, nil, err } buf.Truncate(int(paramSize)) var attest tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, &attest); err != nil { return nil, nil, err } return attest, buf.Bytes(), nil } // Quote returns a quote of PCR values. A quote is a signature of the PCR // values, created using a signing TPM key. // // Returns attestation data and the decoded signature. func Quote(rw io.ReadWriter, signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, *Signature, error) { attest, sigRaw, err := QuoteRaw(rw, signingHandle, parentPassword, ownerPassword, toQuote, sel, sigAlg) if err != nil { return nil, nil, err } sig, err := DecodeSignature(bytes.NewBuffer(sigRaw)) if err != nil { return nil, nil, err } return attest, sig, nil } // QuoteRaw is very similar to Quote, except that it will return // the raw signature in a byte array without decoding. func QuoteRaw(rw io.ReadWriter, signingHandle tpmutil.Handle, parentPassword, ownerPassword string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, []byte, error) { Cmd, err := encodeQuote(signingHandle, parentPassword, ownerPassword, toQuote, sel, sigAlg) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdQuote, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodeQuote(resp) } func encodeActivateCredential(auth []AuthCommand, activeHandle tpmutil.Handle, keyHandle tpmutil.Handle, credBlob, secret tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(activeHandle, keyHandle) if err != nil { return nil, err } a, err := encodeAuthArea(auth...) if err != nil { return nil, err } params, err := tpmutil.Pack(credBlob, secret) if err != nil { return nil, err } return concat(ha, a, params) } func decodeActivateCredential(in []byte) ([]byte, error) { var paramSize uint32 var certInfo tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, ¶mSize, &certInfo); err != nil { return nil, err } return certInfo, nil } // ActivateCredential associates an object with a credential. // Returns decrypted certificate information. func ActivateCredential(rw io.ReadWriter, activeHandle, keyHandle tpmutil.Handle, activePassword, protectorPassword string, credBlob, secret []byte) ([]byte, error) { return ActivateCredentialUsingAuth(rw, []AuthCommand{ {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(activePassword)}, {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(protectorPassword)}, }, activeHandle, keyHandle, credBlob, secret) } // ActivateCredentialUsingAuth associates an object with a credential, using the // given set of authorizations. Two authorization must be provided. // Returns decrypted certificate information. func ActivateCredentialUsingAuth(rw io.ReadWriter, auth []AuthCommand, activeHandle, keyHandle tpmutil.Handle, credBlob, secret []byte) ([]byte, error) { if len(auth) != 2 { return nil, fmt.Errorf("len(auth) = %d, want 2", len(auth)) } Cmd, err := encodeActivateCredential(auth, activeHandle, keyHandle, credBlob, secret) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdActivateCredential, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeActivateCredential(resp) } func encodeMakeCredential(protectorHandle tpmutil.Handle, credential, activeName tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(protectorHandle) if err != nil { return nil, err } params, err := tpmutil.Pack(credential, activeName) if err != nil { return nil, err } return concat(ha, params) } func decodeMakeCredential(in []byte) ([]byte, []byte, error) { var credBlob, encryptedSecret tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &credBlob, &encryptedSecret); err != nil { return nil, nil, err } return credBlob, encryptedSecret, nil } // MakeCredential creates an encrypted credential for use in MakeCredential. // Returns encrypted credential and wrapped secret used to encrypt it. func MakeCredential(rw io.ReadWriter, protectorHandle tpmutil.Handle, credential, activeName []byte) ([]byte, []byte, error) { Cmd, err := encodeMakeCredential(protectorHandle, credential, activeName) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagNoSessions, CmdMakeCredential, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodeMakeCredential(resp) } func encodeEvictControl(ownerAuth string, owner, objectHandle, persistentHandle tpmutil.Handle) ([]byte, error) { ha, err := tpmutil.Pack(owner, objectHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)}) if err != nil { return nil, err } params, err := tpmutil.Pack(persistentHandle) if err != nil { return nil, err } return concat(ha, auth, params) } // EvictControl toggles persistence of an object within the TPM. func EvictControl(rw io.ReadWriter, ownerAuth string, owner, objectHandle, persistentHandle tpmutil.Handle) error { Cmd, err := encodeEvictControl(ownerAuth, owner, objectHandle, persistentHandle) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdEvictControl, tpmutil.RawBytes(Cmd)) return err } func encodeClear(handle tpmutil.Handle, auth AuthCommand) ([]byte, error) { ah, err := tpmutil.Pack(handle) if err != nil { return nil, err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } return concat(ah, encodedAuth) } // Clear clears lockout, endorsement and owner hierarchy authorization values func Clear(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand) error { Cmd, err := encodeClear(handle, auth) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdClear, tpmutil.RawBytes(Cmd)) return err } func encodeHierarchyChangeAuth(handle tpmutil.Handle, auth AuthCommand, newAuth string) ([]byte, error) { ah, err := tpmutil.Pack(handle) if err != nil { return nil, err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return nil, err } param, err := tpmutil.Pack(tpmutil.U16Bytes(newAuth)) if err != nil { return nil, err } return concat(ah, encodedAuth, param) } // HierarchyChangeAuth changes the authorization values for a hierarchy or for the lockout authority func HierarchyChangeAuth(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand, newAuth string) error { Cmd, err := encodeHierarchyChangeAuth(handle, auth, newAuth) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdHierarchyChangeAuth, tpmutil.RawBytes(Cmd)) return err } // ContextSave returns an encrypted version of the session, object or sequence // context for storage outside of the TPM. The handle references context to // store. func ContextSave(rw io.ReadWriter, handle tpmutil.Handle) ([]byte, error) { return runCommand(rw, TagNoSessions, CmdContextSave, handle) } // ContextLoad reloads context data created by ContextSave. func ContextLoad(rw io.ReadWriter, saveArea []byte) (tpmutil.Handle, error) { resp, err := runCommand(rw, TagNoSessions, CmdContextLoad, tpmutil.RawBytes(saveArea)) if err != nil { return 0, err } var handle tpmutil.Handle _, err = tpmutil.Unpack(resp, &handle) return handle, err } func encodeIncrementNV(handle tpmutil.Handle, authString string) ([]byte, error) { auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(authString)}) if err != nil { return nil, err } out, err := tpmutil.Pack(handle, handle) if err != nil { return nil, err } return concat(out, auth) } // NVIncrement increments a counter in NVRAM. func NVIncrement(rw io.ReadWriter, handle tpmutil.Handle, authString string) error { Cmd, err := encodeIncrementNV(handle, authString) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdIncrementNVCounter, tpmutil.RawBytes(Cmd)) return err } // NVUndefineSpace removes an index from TPM's NV storage. func NVUndefineSpace(rw io.ReadWriter, ownerAuth string, owner, index tpmutil.Handle) error { authArea := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth)} return NVUndefineSpaceEx(rw, owner, index, authArea) } // NVUndefineSpaceEx removes an index from NVRAM. Unlike, NVUndefineSpace(), custom command // authorization can be provided. func NVUndefineSpaceEx(rw io.ReadWriter, owner, index tpmutil.Handle, authArea AuthCommand) error { out, err := tpmutil.Pack(owner, index) if err != nil { return err } auth, err := encodeAuthArea(authArea) if err != nil { return err } cmd, err := concat(out, auth) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdUndefineSpace, tpmutil.RawBytes(cmd)) return err } // NVUndefineSpaceSpecial This command allows removal of a platform-created NV Index that has TPMA_NV_POLICY_DELETE SET. // The policy to authorize NV index access needs to be created with PolicyCommandCode(rw, sessionHandle, CmdNVUndefineSpaceSpecial) function // nvAuthCmd takes the session handle for the policy and the AuthValue (which can be emptyAuth) for the authorization. // platformAuth takes either a sessionHandle for the platform policy or HandlePasswordSession and the platformAuth value for authorization. func NVUndefineSpaceSpecial(rw io.ReadWriter, nvIndex tpmutil.Handle, nvAuth, platformAuth AuthCommand) error { authBytes, err := encodeAuthArea(nvAuth, platformAuth) if err != nil { return err } auth, err := tpmutil.Pack(authBytes) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdNVUndefineSpaceSpecial, nvIndex, HandlePlatform, tpmutil.RawBytes(auth)) return err } // NVDefineSpace creates an index in TPM's NV storage. func NVDefineSpace(rw io.ReadWriter, owner, handle tpmutil.Handle, ownerAuth, authString string, policy []byte, attributes NVAttr, dataSize uint16) error { nvPub := NVPublic{ NVIndex: handle, NameAlg: AlgSHA1, Attributes: attributes, AuthPolicy: policy, DataSize: dataSize, } authArea := AuthCommand{ Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(ownerAuth), } return NVDefineSpaceEx(rw, owner, authString, nvPub, authArea) } // NVDefineSpaceEx accepts NVPublic structure and AuthCommand, allowing more flexibility. func NVDefineSpaceEx(rw io.ReadWriter, owner tpmutil.Handle, authVal string, pubInfo NVPublic, authArea AuthCommand) error { ha, err := tpmutil.Pack(owner) if err != nil { return err } auth, err := encodeAuthArea(authArea) if err != nil { return err } publicInfo, err := tpmutil.Pack(pubInfo) if err != nil { return err } params, err := tpmutil.Pack(tpmutil.U16Bytes(authVal), tpmutil.U16Bytes(publicInfo)) if err != nil { return err } cmd, err := concat(ha, auth, params) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdDefineSpace, tpmutil.RawBytes(cmd)) return err } // NVWrite writes data into the TPM's NV storage. func NVWrite(rw io.ReadWriter, authHandle, nvIndex tpmutil.Handle, authString string, data tpmutil.U16Bytes, offset uint16) error { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(authString)} return NVWriteEx(rw, authHandle, nvIndex, auth, data, offset) } // NVWriteEx does the same as NVWrite with the exception of letting the user take care of the AuthCommand before calling the function. // This allows more flexibility and does not limit the AuthCommand to PasswordSession. func NVWriteEx(rw io.ReadWriter, authHandle, nvIndex tpmutil.Handle, authArea AuthCommand, data tpmutil.U16Bytes, offset uint16) error { h, err := tpmutil.Pack(authHandle, nvIndex) if err != nil { return err } authEnc, err := encodeAuthArea(authArea) if err != nil { return err } d, err := tpmutil.Pack(data, offset) if err != nil { return err } b, err := concat(h, authEnc, d) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdWriteNV, tpmutil.RawBytes(b)) return err } func encodeLockNV(owner, handle tpmutil.Handle, authString string) ([]byte, error) { auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(authString)}) if err != nil { return nil, err } out, err := tpmutil.Pack(owner, handle) if err != nil { return nil, err } return concat(out, auth) } // NVWriteLock inhibits further writes on the given NV index if at least one of // the AttrWriteSTClear or AttrWriteDefine bits is set. // // AttrWriteSTClear causes the index to be locked until the TPM is restarted // (see the Startup function). // // AttrWriteDefine causes the index to be locked permanently if data has been // written to the index; otherwise the lock is removed on startup. // // NVWriteLock returns an error if neither bit is set. // // It is not an error to call NVWriteLock for an index that is already locked // for writing. func NVWriteLock(rw io.ReadWriter, owner, handle tpmutil.Handle, authString string) error { Cmd, err := encodeLockNV(owner, handle, authString) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdWriteLockNV, tpmutil.RawBytes(Cmd)) return err } func decodeNVReadPublic(in []byte) (NVPublic, error) { var pub NVPublic var buf tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, &buf); err != nil { return pub, err } _, err := tpmutil.Unpack(buf, &pub) return pub, err } // NVReadPublic reads the public data of an NV index. func NVReadPublic(rw io.ReadWriter, index tpmutil.Handle) (NVPublic, error) { // Read public area to determine data size. resp, err := runCommand(rw, TagNoSessions, CmdReadPublicNV, index) if err != nil { return NVPublic{}, err } return decodeNVReadPublic(resp) } func decodeNVRead(in []byte) ([]byte, error) { var paramSize uint32 var data tpmutil.U16Bytes if _, err := tpmutil.Unpack(in, ¶mSize, &data); err != nil { return nil, err } return data, nil } func encodeNVRead(nvIndex, authHandle tpmutil.Handle, password string, offset, dataSize uint16) ([]byte, error) { handles, err := tpmutil.Pack(authHandle, nvIndex) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } params, err := tpmutil.Pack(dataSize, offset) if err != nil { return nil, err } return concat(handles, auth, params) } // NVRead reads a full data blob from an NV index. This function is // deprecated; use NVReadEx instead. func NVRead(rw io.ReadWriter, index tpmutil.Handle) ([]byte, error) { return NVReadEx(rw, index, index, "", 0) } // NVReadEx reads a full data blob from an NV index, using the given // authorization handle. NVRead commands are done in blocks of blockSize. // If blockSize is 0, the TPM is queried for TPM_PT_NV_BUFFER_MAX, and that // value is used. func NVReadEx(rw io.ReadWriter, index, authHandle tpmutil.Handle, password string, blockSize int) ([]byte, error) { if blockSize == 0 { readBuff, _, err := GetCapability(rw, CapabilityTPMProperties, 1, uint32(NVMaxBufferSize)) if err != nil { return nil, fmt.Errorf("GetCapability for TPM_PT_NV_BUFFER_MAX failed: %v", err) } if len(readBuff) != 1 { return nil, fmt.Errorf("could not determine NVRAM read/write buffer size") } rb, ok := readBuff[0].(TaggedProperty) if !ok { return nil, fmt.Errorf("GetCapability returned unexpected type: %T, expected TaggedProperty", readBuff[0]) } blockSize = int(rb.Value) } // Read public area to determine data size. pub, err := NVReadPublic(rw, index) if err != nil { return nil, fmt.Errorf("decoding NV_ReadPublic response: %v", err) } // Read the NVRAM area in blocks. outBuff := make([]byte, 0, int(pub.DataSize)) for len(outBuff) < int(pub.DataSize) { readSize := blockSize if readSize > (int(pub.DataSize) - len(outBuff)) { readSize = int(pub.DataSize) - len(outBuff) } Cmd, err := encodeNVRead(index, authHandle, password, uint16(len(outBuff)), uint16(readSize)) if err != nil { return nil, fmt.Errorf("building NV_Read command: %v", err) } resp, err := runCommand(rw, TagSessions, CmdReadNV, tpmutil.RawBytes(Cmd)) if err != nil { return nil, fmt.Errorf("running NV_Read command (cursor=%d,size=%d): %v", len(outBuff), readSize, err) } data, err := decodeNVRead(resp) if err != nil { return nil, fmt.Errorf("decoding NV_Read command: %v", err) } outBuff = append(outBuff, data...) } return outBuff, nil } // NVReadLock inhibits further reads of the given NV index if AttrReadSTClear // is set. After the TPM is restarted the index can be read again (see the // Startup function). // // NVReadLock returns an error if the AttrReadSTClear bit is not set. // // It is not an error to call NVReadLock for an index that is already locked // for reading. func NVReadLock(rw io.ReadWriter, owner, handle tpmutil.Handle, authString string) error { Cmd, err := encodeLockNV(owner, handle, authString) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdReadLockNV, tpmutil.RawBytes(Cmd)) return err } // decodeHash unpacks a successful response to TPM2_Hash, returning the computed digest and // validation ticket. func decodeHash(resp []byte) ([]byte, *Ticket, error) { var digest tpmutil.U16Bytes var validation Ticket buf := bytes.NewBuffer(resp) if err := tpmutil.UnpackBuf(buf, &digest, &validation); err != nil { return nil, nil, err } return digest, &validation, nil } // Hash computes a hash of data in buf using TPM2_Hash, returning the computed // digest and validation ticket. The validation ticket serves as confirmation // from the TPM that the data in buf did not begin with TPM_GENERATED_VALUE. // NOTE: TPM2_Hash can only accept data up to MAX_DIGEST_BUFFER in size, which // is implementation-dependent, but guaranteed to be at least 1024 octets. func Hash(rw io.ReadWriter, alg Algorithm, buf tpmutil.U16Bytes, hierarchy tpmutil.Handle) (digest []byte, validation *Ticket, err error) { resp, err := runCommand(rw, TagNoSessions, CmdHash, buf, alg, hierarchy) if err != nil { return nil, nil, err } return decodeHash(resp) } // HashSequenceStart starts a hash or an event sequence. If hashAlg is an // implemented hash, then a hash sequence is started. If hashAlg is // TPM_ALG_NULL, then an event sequence is started. func HashSequenceStart(rw io.ReadWriter, sequenceAuth string, hashAlg Algorithm) (seqHandle tpmutil.Handle, err error) { resp, err := runCommand(rw, TagNoSessions, CmdHashSequenceStart, tpmutil.U16Bytes(sequenceAuth), hashAlg) if err != nil { return 0, err } var handle tpmutil.Handle _, err = tpmutil.Unpack(resp, &handle) return handle, err } func encodeSequenceUpdate(sequenceAuth string, seqHandle tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(seqHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)}) if err != nil { return nil, err } params, err := tpmutil.Pack(buf) if err != nil { return nil, err } return concat(ha, auth, params) } // SequenceUpdate is used to add data to a hash or HMAC sequence. func SequenceUpdate(rw io.ReadWriter, sequenceAuth string, seqHandle tpmutil.Handle, buffer []byte) error { cmd, err := encodeSequenceUpdate(sequenceAuth, seqHandle, buffer) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdSequenceUpdate, tpmutil.RawBytes(cmd)) return err } func decodeSequenceComplete(resp []byte) ([]byte, *Ticket, error) { var digest tpmutil.U16Bytes var validation Ticket var paramSize uint32 if _, err := tpmutil.Unpack(resp, ¶mSize, &digest, &validation); err != nil { return nil, nil, err } return digest, &validation, nil } func encodeSequenceComplete(sequenceAuth string, seqHandle, hierarchy tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(seqHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)}) if err != nil { return nil, err } params, err := tpmutil.Pack(buf, hierarchy) if err != nil { return nil, err } return concat(ha, auth, params) } // SequenceComplete adds the last part of data, if any, to a hash/HMAC sequence // and returns the result. func SequenceComplete(rw io.ReadWriter, sequenceAuth string, seqHandle, hierarchy tpmutil.Handle, buffer []byte) (digest []byte, validation *Ticket, err error) { cmd, err := encodeSequenceComplete(sequenceAuth, seqHandle, hierarchy, buffer) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdSequenceComplete, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } return decodeSequenceComplete(resp) } func encodeEventSequenceComplete(auths []AuthCommand, pcrHandle, seqHandle tpmutil.Handle, buf tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(pcrHandle, seqHandle) if err != nil { return nil, err } auth, err := encodeAuthArea(auths...) if err != nil { return nil, err } params, err := tpmutil.Pack(buf) if err != nil { return nil, err } return concat(ha, auth, params) } func decodeEventSequenceComplete(resp []byte) ([]*HashValue, error) { var paramSize uint32 var hashCount uint32 var err error buf := bytes.NewBuffer(resp) if err := tpmutil.UnpackBuf(buf, ¶mSize, &hashCount); err != nil { return nil, err } buf.Truncate(int(paramSize)) digests := make([]*HashValue, hashCount) for i := uint32(0); i < hashCount; i++ { if digests[i], err = decodeHashValue(buf); err != nil { return nil, err } } return digests, nil } // EventSequenceComplete adds the last part of data, if any, to an Event // Sequence and returns the result in a digest list. If pcrHandle references a // PCR and not AlgNull, then the returned digest list is processed in the same // manner as the digest list input parameter to PCRExtend() with the pcrHandle // in each bank extended with the associated digest value. func EventSequenceComplete(rw io.ReadWriter, pcrAuth, sequenceAuth string, pcrHandle, seqHandle tpmutil.Handle, buffer []byte) (digests []*HashValue, err error) { auth := []AuthCommand{ {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(pcrAuth)}, {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(sequenceAuth)}, } cmd, err := encodeEventSequenceComplete(auth, pcrHandle, seqHandle, buffer) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdEventSequenceComplete, tpmutil.RawBytes(cmd)) if err != nil { return nil, err } return decodeEventSequenceComplete(resp) } // Startup initializes a TPM (usually done by the OS). func Startup(rw io.ReadWriter, typ StartupType) error { _, err := runCommand(rw, TagNoSessions, CmdStartup, typ) return err } // Shutdown shuts down a TPM (usually done by the OS). func Shutdown(rw io.ReadWriter, typ StartupType) error { _, err := runCommand(rw, TagNoSessions, CmdShutdown, typ) return err } // nullTicket is a hard-coded null ticket of type TPMT_TK_HASHCHECK. // It is for Sign commands that do not require the TPM to verify that the digest // is not from data that started with TPM_GENERATED_VALUE. var nullTicket = Ticket{ Type: TagHashCheck, Hierarchy: HandleNull, Digest: tpmutil.U16Bytes{}, } func encodeSign(sessionHandle, key tpmutil.Handle, password string, digest tpmutil.U16Bytes, sigScheme *SigScheme, validation *Ticket) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: sessionHandle, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } d, err := tpmutil.Pack(digest) if err != nil { return nil, err } s, err := sigScheme.encode() if err != nil { return nil, err } if validation == nil { validation = &nullTicket } v, err := tpmutil.Pack(validation) if err != nil { return nil, err } return concat(ha, auth, d, s, v) } func decodeSign(buf []byte) (*Signature, error) { in := bytes.NewBuffer(buf) var paramSize uint32 if err := tpmutil.UnpackBuf(in, ¶mSize); err != nil { return nil, err } return DecodeSignature(in) } // SignWithSession computes a signature for digest using a given loaded key. Signature // algorithm depends on the key type. Used for keys with non-password authorization policies. // If 'key' references a Restricted Decryption key, 'validation' must be a valid hash verification // ticket from the TPM, which can be obtained by using Hash() to hash the data with the TPM. // If 'validation' is nil, a NULL ticket is passed to TPM2_Sign. func SignWithSession(rw io.ReadWriter, sessionHandle, key tpmutil.Handle, password string, digest []byte, validation *Ticket, sigScheme *SigScheme) (*Signature, error) { Cmd, err := encodeSign(sessionHandle, key, password, digest, sigScheme, validation) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdSign, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeSign(resp) } // Sign computes a signature for digest using a given loaded key. Signature // algorithm depends on the key type. // If 'key' references a Restricted Decryption key, 'validation' must be a valid hash verification // ticket from the TPM, which can be obtained by using Hash() to hash the data with the TPM. // If 'validation' is nil, a NULL ticket is passed to TPM2_Sign. func Sign(rw io.ReadWriter, key tpmutil.Handle, password string, digest []byte, validation *Ticket, sigScheme *SigScheme) (*Signature, error) { return SignWithSession(rw, HandlePasswordSession, key, password, digest, validation, sigScheme) } func encodeCertify(objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes) ([]byte, error) { ha, err := tpmutil.Pack(object, signer) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(signerAuth)}) if err != nil { return nil, err } scheme := SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256} // Use signing key's scheme. s, err := scheme.encode() if err != nil { return nil, err } data, err := tpmutil.Pack(qualifyingData) if err != nil { return nil, err } return concat(ha, auth, data, s) } // This function differs from encodeCertify in that it takes the scheme to be used as an additional argument. func encodeCertifyEx(objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData tpmutil.U16Bytes, scheme SigScheme) ([]byte, error) { ha, err := tpmutil.Pack(object, signer) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(signerAuth)}) if err != nil { return nil, err } s, err := scheme.encode() if err != nil { return nil, err } data, err := tpmutil.Pack(qualifyingData) if err != nil { return nil, err } return concat(ha, auth, data, s) } func decodeCertify(resp []byte) ([]byte, []byte, error) { var paramSize uint32 var attest, signature tpmutil.U16Bytes var sigAlg, hashAlg Algorithm buf := bytes.NewBuffer(resp) if err := tpmutil.UnpackBuf(buf, ¶mSize); err != nil { return nil, nil, err } buf.Truncate(int(paramSize)) if err := tpmutil.UnpackBuf(buf, &attest, &sigAlg); err != nil { return nil, nil, err } // If sigAlg is AlgNull, there will be no hashAlg or signature. // This will happen if AlgNull was passed in the Certify() as // the signing key (no need to sign the response). // See TPM2 spec part4 pg227 SignAttestInfo() if sigAlg != AlgNull { if sigAlg == AlgECDSA { var r, s tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, &hashAlg, &r, &s); err != nil { return nil, nil, err } signature = append(r, s...) } else { if err := tpmutil.UnpackBuf(buf, &hashAlg, &signature); err != nil { return nil, nil, err } } } return attest, signature, nil } // Certify generates a signature of a loaded TPM object with a signing key // signer. This function calls encodeCertify which makes use of the hardcoded // signing scheme {AlgRSASSA, AlgSHA256}. Returned values are: attestation data (TPMS_ATTEST), // signature and error, if any. func Certify(rw io.ReadWriter, objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData []byte) ([]byte, []byte, error) { cmd, err := encodeCertify(objectAuth, signerAuth, object, signer, qualifyingData) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdCertify, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } return decodeCertify(resp) } // CertifyEx generates a signature of a loaded TPM object with a signing key // signer. This function differs from Certify in that it takes the scheme // to be used as an additional argument and calls encodeCertifyEx instead // of encodeCertify. Returned values are: attestation data (TPMS_ATTEST), // signature and error, if any. func CertifyEx(rw io.ReadWriter, objectAuth, signerAuth string, object, signer tpmutil.Handle, qualifyingData []byte, scheme SigScheme) ([]byte, []byte, error) { cmd, err := encodeCertifyEx(objectAuth, signerAuth, object, signer, qualifyingData, scheme) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdCertify, tpmutil.RawBytes(cmd)) if err != nil { return nil, nil, err } return decodeCertify(resp) } func encodeCertifyCreation(objectAuth string, object, signer tpmutil.Handle, qualifyingData, creationHash tpmutil.U16Bytes, scheme SigScheme, ticket Ticket) ([]byte, error) { handles, err := tpmutil.Pack(signer, object) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(objectAuth)}) if err != nil { return nil, err } s, err := scheme.encode() if err != nil { return nil, err } params, err := tpmutil.Pack(qualifyingData, creationHash, tpmutil.RawBytes(s), ticket) if err != nil { return nil, err } return concat(handles, auth, params) } // CertifyCreation generates a signature of a newly-created & // loaded TPM object, using signer as the signing key. func CertifyCreation(rw io.ReadWriter, objectAuth string, object, signer tpmutil.Handle, qualifyingData, creationHash []byte, sigScheme SigScheme, creationTicket Ticket) (attestation, signature []byte, err error) { Cmd, err := encodeCertifyCreation(objectAuth, object, signer, qualifyingData, creationHash, sigScheme, creationTicket) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdCertifyCreation, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodeCertify(resp) } func runCommand(rw io.ReadWriter, tag tpmutil.Tag, Cmd tpmutil.Command, in ...interface{}) ([]byte, error) { resp, code, err := tpmutil.RunCommand(rw, tag, Cmd, in...) if err != nil { return nil, err } if code != tpmutil.RCSuccess { return nil, decodeResponse(code) } return resp, decodeResponse(code) } // concat is a helper for encoding functions that separately encode handle, // auth and param areas. A nil error is always returned, so that callers can // simply return concat(a, b, c). func concat(chunks ...[]byte) ([]byte, error) { return bytes.Join(chunks, nil), nil } func encodePCRExtend(pcr tpmutil.Handle, hashAlg Algorithm, hash tpmutil.RawBytes, password string) ([]byte, error) { ha, err := tpmutil.Pack(pcr) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } pcrCount := uint32(1) extend, err := tpmutil.Pack(pcrCount, hashAlg, hash) if err != nil { return nil, err } return concat(ha, auth, extend) } // PCRExtend extends a value into the selected PCR func PCRExtend(rw io.ReadWriter, pcr tpmutil.Handle, hashAlg Algorithm, hash []byte, password string) error { Cmd, err := encodePCRExtend(pcr, hashAlg, hash, password) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdPCRExtend, tpmutil.RawBytes(Cmd)) return err } // ReadPCR reads the value of the given PCR. func ReadPCR(rw io.ReadWriter, pcr int, hashAlg Algorithm) ([]byte, error) { pcrSelection := PCRSelection{ Hash: hashAlg, PCRs: []int{pcr}, } pcrVals, err := ReadPCRs(rw, pcrSelection) if err != nil { return nil, fmt.Errorf("unable to read PCRs from TPM: %v", err) } pcrVal, present := pcrVals[pcr] if !present { return nil, fmt.Errorf("PCR %d value missing from response", pcr) } return pcrVal, nil } // EncryptSymmetric encrypts data using a symmetric key. // // WARNING: This command performs low-level cryptographic operations. // Secure use of this command is subtle and requires careful analysis. // Please consult with experts in cryptography for how to use it securely. // // The iv is the initialization vector. The iv must not be empty and its size depends on the // details of the symmetric encryption scheme. // // The data may be longer than block size, EncryptSymmetric will chain // multiple TPM calls to encrypt the entire blob. // // Key handle should point at SymCipher object which is a child of the key (and // not e.g. RSA key itself). func EncryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte) ([]byte, error) { return encryptDecryptSymmetric(rw, keyAuth, key, iv, data, false) } // DecryptSymmetric decrypts data using a symmetric key. // // WARNING: This command performs low-level cryptographic operations. // Secure use of this command is subtle and requires careful analysis. // Please consult with experts in cryptography for how to use it securely. // // The iv is the initialization vector. The iv must not be empty and its size // depends on the details of the symmetric encryption scheme. // // The data may be longer than block size, DecryptSymmetric will chain multiple // TPM calls to decrypt the entire blob. // // Key handle should point at SymCipher object which is a child of the key (and // not e.g. RSA key itself). func DecryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte) ([]byte, error) { return encryptDecryptSymmetric(rw, keyAuth, key, iv, data, true) } func encodeEncryptDecrypt(keyAuth string, key tpmutil.Handle, iv, data tpmutil.U16Bytes, decrypt bool) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(keyAuth)}) if err != nil { return nil, err } // Use encryption key's mode. params, err := tpmutil.Pack(decrypt, AlgNull, iv, data) if err != nil { return nil, err } return concat(ha, auth, params) } func encodeEncryptDecrypt2(keyAuth string, key tpmutil.Handle, iv, data tpmutil.U16Bytes, decrypt bool) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(keyAuth)}) if err != nil { return nil, err } // Use encryption key's mode. params, err := tpmutil.Pack(data, decrypt, AlgNull, iv) if err != nil { return nil, err } return concat(ha, auth, params) } func decodeEncryptDecrypt(resp []byte) ([]byte, []byte, error) { var paramSize uint32 var out, nextIV tpmutil.U16Bytes if _, err := tpmutil.Unpack(resp, ¶mSize, &out, &nextIV); err != nil { return nil, nil, err } return out, nextIV, nil } func encryptDecryptBlockSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte, decrypt bool) ([]byte, []byte, error) { Cmd, err := encodeEncryptDecrypt2(keyAuth, key, iv, data, decrypt) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdEncryptDecrypt2, tpmutil.RawBytes(Cmd)) if err != nil { fmt0Err, ok := err.(Error) if ok && fmt0Err.Code == RCCommandCode { // If TPM2_EncryptDecrypt2 is not supported, fall back to // TPM2_EncryptDecrypt. Cmd, _ := encodeEncryptDecrypt(keyAuth, key, iv, data, decrypt) resp, err = runCommand(rw, TagSessions, CmdEncryptDecrypt, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } } } if err != nil { return nil, nil, err } return decodeEncryptDecrypt(resp) } func encryptDecryptSymmetric(rw io.ReadWriteCloser, keyAuth string, key tpmutil.Handle, iv, data []byte, decrypt bool) ([]byte, error) { var out, block []byte var err error for rest := data; len(rest) > 0; { if len(rest) > maxDigestBuffer { block, rest = rest[:maxDigestBuffer], rest[maxDigestBuffer:] } else { block, rest = rest, nil } block, iv, err = encryptDecryptBlockSymmetric(rw, keyAuth, key, iv, block, decrypt) if err != nil { return nil, err } out = append(out, block...) } return out, nil } func encodeRSAEncrypt(key tpmutil.Handle, message tpmutil.U16Bytes, scheme *AsymScheme, label string) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } m, err := tpmutil.Pack(message) if err != nil { return nil, err } s, err := scheme.encode() if err != nil { return nil, err } if label != "" { label += "\x00" } l, err := tpmutil.Pack(tpmutil.U16Bytes(label)) if err != nil { return nil, err } return concat(ha, m, s, l) } func decodeRSAEncrypt(resp []byte) ([]byte, error) { var out tpmutil.U16Bytes _, err := tpmutil.Unpack(resp, &out) return out, err } // RSAEncrypt performs RSA encryption in the TPM according to RFC 3447. The key must be // a (public) key loaded into the TPM beforehand. Note that when using OAEP with a label, // a null byte is appended to the label and the null byte is included in the padding // scheme. func RSAEncrypt(rw io.ReadWriter, key tpmutil.Handle, message []byte, scheme *AsymScheme, label string) ([]byte, error) { Cmd, err := encodeRSAEncrypt(key, message, scheme, label) if err != nil { return nil, err } resp, err := runCommand(rw, TagNoSessions, CmdRSAEncrypt, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeRSAEncrypt(resp) } func encodeRSADecrypt(key tpmutil.Handle, password string, message tpmutil.U16Bytes, scheme *AsymScheme, label string) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } m, err := tpmutil.Pack(message) if err != nil { return nil, err } s, err := scheme.encode() if err != nil { return nil, err } if label != "" { label += "\x00" } l, err := tpmutil.Pack(tpmutil.U16Bytes(label)) if err != nil { return nil, err } return concat(ha, auth, m, s, l) } func decodeRSADecrypt(resp []byte) ([]byte, error) { var out tpmutil.U16Bytes var paramSize uint32 _, err := tpmutil.Unpack(resp, ¶mSize, &out) return out, err } // RSADecrypt performs RSA decryption in the TPM according to RFC 3447. The key must be // a private RSA key in the TPM with FlagDecrypt set. Note that when using OAEP with a // label, a null byte is appended to the label and the null byte is included in the // padding scheme. func RSADecrypt(rw io.ReadWriter, key tpmutil.Handle, password string, message []byte, scheme *AsymScheme, label string) ([]byte, error) { Cmd, err := encodeRSADecrypt(key, password, message, scheme, label) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdRSADecrypt, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeRSADecrypt(resp) } func encodeECDHKeyGen(key tpmutil.Handle) ([]byte, error) { return tpmutil.Pack(key) } func decodeECDHKeyGen(resp []byte) (*ECPoint, *ECPoint, error) { // Unpack z and pub as TPM2B_ECC_POINT, which is a TPMS_ECC_POINT with a total size prepended. var z2B, pub2B tpmutil.U16Bytes _, err := tpmutil.Unpack(resp, &z2B, &pub2B) if err != nil { return nil, nil, err } var zPoint, pubPoint ECPoint _, err = tpmutil.Unpack(z2B, &zPoint.XRaw, &zPoint.YRaw) if err != nil { return nil, nil, err } _, err = tpmutil.Unpack(pub2B, &pubPoint.XRaw, &pubPoint.YRaw) if err != nil { return nil, nil, err } return &zPoint, &pubPoint, nil } // ECDHKeyGen generates an ephemeral ECC key, calculates the ECDH point multiplcation of the // ephemeral private key and a loaded public key, and returns the public ephemeral point along with // the coordinates of the resulting point. func ECDHKeyGen(rw io.ReadWriter, key tpmutil.Handle) (zPoint, pubPoint *ECPoint, err error) { Cmd, err := encodeECDHKeyGen(key) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagNoSessions, CmdECDHKeyGen, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodeECDHKeyGen(resp) } func encodeECDHZGen(key tpmutil.Handle, password string, inPoint ECPoint) ([]byte, error) { ha, err := tpmutil.Pack(key) if err != nil { return nil, err } auth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(password)}) if err != nil { return nil, err } p, err := tpmutil.Pack(inPoint) if err != nil { return nil, err } // Pack the TPMS_ECC_POINT as a TPM2B_ECC_POINT. p2B, err := tpmutil.Pack(tpmutil.U16Bytes(p)) if err != nil { return nil, err } return concat(ha, auth, p2B) } func decodeECDHZGen(resp []byte) (*ECPoint, error) { var paramSize uint32 // Unpack a TPM2B_ECC_POINT, which is a TPMS_ECC_POINT with a total size prepended. var z2B tpmutil.U16Bytes _, err := tpmutil.Unpack(resp, ¶mSize, &z2B) if err != nil { return nil, err } var zPoint ECPoint _, err = tpmutil.Unpack(z2B, &zPoint.XRaw, &zPoint.YRaw) if err != nil { return nil, err } return &zPoint, nil } // ECDHZGen performs ECDH point multiplication between a private key held in the TPM and a given // public point, returning the coordinates of the resulting point. The key must have FlagDecrypt // set. func ECDHZGen(rw io.ReadWriter, key tpmutil.Handle, password string, inPoint ECPoint) (zPoint *ECPoint, err error) { Cmd, err := encodeECDHZGen(key, password, inPoint) if err != nil { return nil, err } resp, err := runCommand(rw, TagSessions, CmdECDHZGen, tpmutil.RawBytes(Cmd)) if err != nil { return nil, err } return decodeECDHZGen(resp) } // DictionaryAttackLockReset cancels the effect of a TPM lockout due to a number // of successive authorization failures, by setting the lockout counter to zero. // The command requires Lockout Authorization and only one lockoutAuth authorization // failure is allowed for this command during a lockoutRecovery interval. // Lockout Authorization value by default is empty and can be changed via // a call to HierarchyChangeAuth(HandleLockout). func DictionaryAttackLockReset(rw io.ReadWriter, auth AuthCommand) error { ha, err := tpmutil.Pack(HandleLockout) if err != nil { return err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return err } Cmd, err := concat(ha, encodedAuth) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdDictionaryAttackLockReset, tpmutil.RawBytes(Cmd)) return err } // DictionaryAttackParameters changes the lockout parameters. // The command requires Lockout Authorization and has same authorization policy // as in DictionaryAttackLockReset. func DictionaryAttackParameters(rw io.ReadWriter, auth AuthCommand, maxTries, recoveryTime, lockoutRecovery uint32) error { ha, err := tpmutil.Pack(HandleLockout) if err != nil { return err } encodedAuth, err := encodeAuthArea(auth) if err != nil { return err } params, err := tpmutil.Pack(maxTries, recoveryTime, lockoutRecovery) if err != nil { return err } Cmd, err := concat(ha, encodedAuth, params) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdDictionaryAttackParameters, tpmutil.RawBytes(Cmd)) return err } // PolicyCommandCode indicates that the authorization will be limited to a specific command code func PolicyCommandCode(rw io.ReadWriter, session tpmutil.Handle, cc tpmutil.Command) error { data, err := tpmutil.Pack(session, cc) if err != nil { return err } _, err = runCommand(rw, TagNoSessions, CmdPolicyCommandCode, data) return err }