diff --git a/vendor/github.com/google/go-tpm-tools/LICENSE b/vendor/github.com/google/go-tpm-tools/LICENSE new file mode 100644 index 000000000000..228ecc6d6b1e --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/LICENSE @@ -0,0 +1,297 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +-------------------------------------------------------------------- +IBM simulator code (in tpm2-simulator/) uses the following license: +-------------------------------------------------------------------- + +(c) Copyright IBM Corporation 2016. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the names of the IBM Corporation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +A portion of the source code is derived from the TPM specification, +which has a TCG copyright. It is reproduced here for reference. + +-------------------------------------------------------------------- + +Licenses and Notices +Copyright Licenses: + +* Trusted Computing Group (TCG) grants to the user of the source code +in this specification (the "Source Code") a worldwide, irrevocable, +nonexclusive, royalty free, copyright license to reproduce, create +derivative works, distribute, display and perform the Source Code and +derivative works thereof, and to grant others the rights granted +herein. + +* The TCG grants to the user of the other parts of the specification +(other than the Source Code) the rights to reproduce, distribute, +display, and perform the specification solely for the purpose of +developing products based on such documents. + +Source Code Distribution Conditions: + +* Redistributions of Source Code must retain the above copyright +licenses, this list of conditions and the following disclaimers. + +* Redistributions in binary form must reproduce the above copyright +licenses, this list of conditions and the following disclaimers in the +documentation and/or other materials provided with the distribution. + +Disclaimers: + +* THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF +LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH +RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) +THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR +OTHERWISE. Contact TCG Administration +(admin@trustedcomputinggroup.org) for information on specification +licensing rights available through TCG membership agreements. + +* THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED +WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR +NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY +OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. + +* Without limitation, TCG and its members and licensors disclaim all +liability, including liability for infringement of any proprietary +rights, relating to use of information in this specification and to +the implementation of this specification, and TCG disclaims all +liability for cost of procurement of substitute goods or services, +lost profits, loss of use, loss of data or any incidental, +consequential, direct, indirect, or special damages, whether under +contract, tort, warranty or otherwise, arising in any way out of use +or reliance upon this specification or any information herein. + +Any marks and brands contained herein are the property of their +respective owners. diff --git a/vendor/github.com/google/go-tpm-tools/client/BUILD.bazel b/vendor/github.com/google/go-tpm-tools/client/BUILD.bazel new file mode 100644 index 000000000000..593a264af64c --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "attest.go", + "close.go", + "eventlog.go", + "eventlog_linux.go", + "eventlog_other.go", + "handles.go", + "import.go", + "keys.go", + "pcr.go", + "session.go", + "signer.go", + "template.go", + ], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm-tools/client", + importpath = "github.com/google/go-tpm-tools/client", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/google/go-tpm-tools/internal:go_default_library", + "//vendor/github.com/google/go-tpm-tools/proto/attest:go_default_library", + "//vendor/github.com/google/go-tpm-tools/proto/tpm:go_default_library", + "//vendor/github.com/google/go-tpm/tpm2:go_default_library", + "//vendor/github.com/google/go-tpm/tpmutil:go_default_library", + ], +) diff --git a/vendor/github.com/google/go-tpm-tools/client/attest.go b/vendor/github.com/google/go-tpm-tools/client/attest.go new file mode 100644 index 000000000000..5610b8de9c7e --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/attest.go @@ -0,0 +1,57 @@ +package client + +import ( + "fmt" + + pb "github.com/google/go-tpm-tools/proto/attest" +) + +// AttestOpts allows for customizing the functionality of Attest. +type AttestOpts struct { + // A unique, application-specific nonce used to guarantee freshness of the + // attestation. This must not be empty, and should generally be long enough + // to make brute force attacks infeasible. + // + // For security reasons, applications should not allow for attesting with + // arbitrary, externally-provided nonces. The nonce should be prefixed or + // otherwise bound (i.e. via a KDF) to application-specific data. For more + // information on why this is an issue, see this paper on robust remote + // attestation protocols: + // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4562&rep=rep1&type=pdf + Nonce []byte +} + +// Attest generates an Attestation containing the TCG Event Log and a Quote over +// all PCR banks. The provided nonce can be used to guarantee freshness of the +// attestation. This function will return an error if the key is not a +// restricted signing key. +// +// AttestOpts is used for additional configuration of the Attestation process. +// This is primarily used to pass the attestation's nonce: +// +// attestation, err := key.Attest(client.AttestOpts{Nonce: my_nonce}) +func (k *Key) Attest(opts AttestOpts) (*pb.Attestation, error) { + if len(opts.Nonce) == 0 { + return nil, fmt.Errorf("provided nonce must not be empty") + } + sels, err := implementedPCRs(k.rw) + if err != nil { + return nil, err + } + + attestation := pb.Attestation{} + if attestation.AkPub, err = k.PublicArea().Encode(); err != nil { + return nil, fmt.Errorf("failed to encode public area: %w", err) + } + for _, sel := range sels { + quote, err := k.Quote(sel, opts.Nonce) + if err != nil { + return nil, err + } + attestation.Quotes = append(attestation.Quotes, quote) + } + if attestation.EventLog, err = GetEventLog(k.rw); err != nil { + return nil, fmt.Errorf("failed to retrieve TCG Event Log: %w", err) + } + return &attestation, nil +} diff --git a/vendor/github.com/google/go-tpm-tools/client/close.go b/vendor/github.com/google/go-tpm-tools/client/close.go new file mode 100644 index 000000000000..31700c33ca10 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/close.go @@ -0,0 +1,29 @@ +package client + +import ( + "io" + "testing" + + "github.com/google/go-tpm/tpm2" +) + +// CheckedClose closes the simulator and asserts that there were no leaked handles. +func CheckedClose(tb testing.TB, rwc io.ReadWriteCloser) { + for _, t := range []tpm2.HandleType{ + tpm2.HandleTypeLoadedSession, + tpm2.HandleTypeSavedSession, + tpm2.HandleTypeTransient, + } { + handles, err := Handles(rwc, t) + if err != nil { + tb.Errorf("failed to fetch handles of type %v: %v", t, err) + } + if len(handles) != 0 { + tb.Errorf("tests leaked handles: %v", handles) + } + } + + if err := rwc.Close(); err != nil { + tb.Errorf("when closing simulator: %v", err) + } +} diff --git a/vendor/github.com/google/go-tpm-tools/client/eventlog.go b/vendor/github.com/google/go-tpm-tools/client/eventlog.go new file mode 100644 index 000000000000..9c74e0bba2d1 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/eventlog.go @@ -0,0 +1,19 @@ +package client + +import "io" + +// GetEventLog grabs the crypto-agile TCG event log for the system. The TPM can +// override this implementation by implementing EventLogGetter. +func GetEventLog(rw io.ReadWriter) ([]byte, error) { + if elg, ok := rw.(EventLogGetter); ok { + return elg.EventLog() + } + return getRealEventLog() +} + +// EventLogGetter allows a TPM (io.ReadWriter) to specify a particular +// implementation for GetEventLog(). This is useful for testing and necessary +// for Windows Event Log support (which requires a handle to the TPM). +type EventLogGetter interface { + EventLog() ([]byte, error) +} diff --git a/vendor/github.com/google/go-tpm-tools/client/eventlog_linux.go b/vendor/github.com/google/go-tpm-tools/client/eventlog_linux.go new file mode 100644 index 000000000000..357ca08f0fc0 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/eventlog_linux.go @@ -0,0 +1,9 @@ +package client + +import ( + "io/ioutil" +) + +func getRealEventLog() ([]byte, error) { + return ioutil.ReadFile("/sys/kernel/security/tpm0/binary_bios_measurements") +} diff --git a/vendor/github.com/google/go-tpm-tools/client/eventlog_other.go b/vendor/github.com/google/go-tpm-tools/client/eventlog_other.go new file mode 100644 index 000000000000..c6e7960c1de1 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/eventlog_other.go @@ -0,0 +1,10 @@ +//go:build !linux +// +build !linux + +package client + +import "errors" + +func getRealEventLog() ([]byte, error) { + return nil, errors.New("failed to get event log: only Linux supported") +} diff --git a/vendor/github.com/google/go-tpm-tools/client/handles.go b/vendor/github.com/google/go-tpm-tools/client/handles.go new file mode 100644 index 000000000000..75f72430cbc7 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/handles.go @@ -0,0 +1,60 @@ +package client + +import ( + "fmt" + "io" + "math" + + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +// Reserved Handles from "TCG TPM v2.0 Provisioning Guidance" - v1r1 - Table 2 +const ( + EKReservedHandle = tpmutil.Handle(0x81010001) + EKECCReservedHandle = tpmutil.Handle(0x81010002) + SRKReservedHandle = tpmutil.Handle(0x81000001) + SRKECCReservedHandle = tpmutil.Handle(0x81000002) +) + +// Picked available handles from TPM 2.0 Handles and Localities 2.3.1 - Table 11 +// go-tpm-tools will use handles in the range from 0x81008F00 to 0x81008FFF +const ( + DefaultAKECCHandle = tpmutil.Handle(0x81008F00) + DefaultAKRSAHandle = tpmutil.Handle(0x81008F01) +) + +// NV Indices holding GCE AK Templates +const ( + GceAKTemplateNVIndexRSA uint32 = 0x01c10001 + GceAKTemplateNVIndexECC uint32 = 0x01c10003 +) + +func isHierarchy(h tpmutil.Handle) bool { + return h == tpm2.HandleOwner || h == tpm2.HandleEndorsement || + h == tpm2.HandlePlatform || h == tpm2.HandleNull +} + +// Handles returns a slice of tpmutil.Handle objects of all handles within +// the TPM rw of type handleType. +func Handles(rw io.ReadWriter, handleType tpm2.HandleType) ([]tpmutil.Handle, error) { + // Handle type is determined by the most-significant octet (MSO) of the property. + property := uint32(handleType) << 24 + + vals, moreData, err := tpm2.GetCapability(rw, tpm2.CapabilityHandles, math.MaxUint32, property) + if err != nil { + return nil, err + } + if moreData { + return nil, fmt.Errorf("tpm2.GetCapability() should never return moreData==true for tpm2.CapabilityHandles") + } + handles := make([]tpmutil.Handle, len(vals)) + for i, v := range vals { + handle, ok := v.(tpmutil.Handle) + if !ok { + return nil, fmt.Errorf("unable to assert type tpmutil.Handle of value %#v", v) + } + handles[i] = handle + } + return handles, nil +} diff --git a/vendor/github.com/google/go-tpm-tools/client/import.go b/vendor/github.com/google/go-tpm-tools/client/import.go new file mode 100644 index 000000000000..72f796c36ba3 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/import.go @@ -0,0 +1,83 @@ +package client + +import ( + "fmt" + + "github.com/google/go-tpm-tools/internal" + pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +func loadHandle(k *Key, blob *pb.ImportBlob) (tpmutil.Handle, error) { + auth, err := k.session.Auth() + if err != nil { + return tpm2.HandleNull, err + } + private, err := tpm2.Import(k.rw, k.Handle(), auth, blob.PublicArea, blob.Duplicate, blob.EncryptedSeed, nil, nil) + if err != nil { + return tpm2.HandleNull, fmt.Errorf("import failed: %w", err) + } + + auth, err = k.session.Auth() + if err != nil { + return tpm2.HandleNull, err + } + handle, _, err := tpm2.LoadUsingAuth(k.rw, k.Handle(), auth, blob.PublicArea, private) + if err != nil { + return tpm2.HandleNull, fmt.Errorf("load failed: %w", err) + } + return handle, nil +} + +// Import decrypts the secret contained in an encoded import request. +// The key used must be an encryption key (signing keys cannot be used). +// The req parameter should come from server.CreateImportBlob. +func (k *Key) Import(blob *pb.ImportBlob) ([]byte, error) { + handle, err := loadHandle(k, blob) + if err != nil { + return nil, err + } + defer tpm2.FlushContext(k.rw, handle) + + unsealSession, err := newPCRSession(k.rw, internal.PCRSelection(blob.Pcrs)) + if err != nil { + return nil, err + } + defer unsealSession.Close() + + auth, err := unsealSession.Auth() + if err != nil { + return nil, err + } + out, err := tpm2.UnsealWithSession(k.rw, auth.Session, handle, "") + if err != nil { + return nil, fmt.Errorf("unseal failed: %w", err) + } + return out, nil +} + +// ImportSigningKey returns the signing key contained in an encoded import request. +// The parent key must be an encryption key (signing keys cannot be used). +// The req parameter should come from server.CreateSigningKeyImportBlob. +func (k *Key) ImportSigningKey(blob *pb.ImportBlob) (key *Key, err error) { + handle, err := loadHandle(k, blob) + if err != nil { + return nil, err + } + key = &Key{rw: k.rw, handle: handle} + + defer func() { + if err != nil { + key.Close() + } + }() + + if key.pubArea, _, _, err = tpm2.ReadPublic(k.rw, handle); err != nil { + return + } + if key.session, err = newPCRSession(k.rw, internal.PCRSelection(blob.Pcrs)); err != nil { + return + } + return key, key.finish() +} diff --git a/vendor/github.com/google/go-tpm-tools/client/keys.go b/vendor/github.com/google/go-tpm-tools/client/keys.go new file mode 100644 index 000000000000..7e0078185906 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/keys.go @@ -0,0 +1,429 @@ +// Package client contains some high-level TPM 2.0 functions. +package client + +import ( + "bytes" + "crypto" + "crypto/subtle" + "fmt" + "io" + + "github.com/google/go-tpm-tools/internal" + pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +// Key wraps an active asymmetric TPM2 key. This can either be a signing key or +// an encryption key. Users of Key should be sure to call Close() when the Key +// is no longer needed, so that the underlying TPM handle can be freed. +type Key struct { + rw io.ReadWriter + handle tpmutil.Handle + pubArea tpm2.Public + pubKey crypto.PublicKey + name tpm2.Name + session session +} + +// EndorsementKeyRSA generates and loads a key from DefaultEKTemplateRSA. +func EndorsementKeyRSA(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleEndorsement, DefaultEKTemplateRSA(), EKReservedHandle) +} + +// EndorsementKeyECC generates and loads a key from DefaultEKTemplateECC. +func EndorsementKeyECC(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleEndorsement, DefaultEKTemplateECC(), EKECCReservedHandle) +} + +// StorageRootKeyRSA generates and loads a key from SRKTemplateRSA. +func StorageRootKeyRSA(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleOwner, SRKTemplateRSA(), SRKReservedHandle) +} + +// StorageRootKeyECC generates and loads a key from SRKTemplateECC. +func StorageRootKeyECC(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleOwner, SRKTemplateECC(), SRKECCReservedHandle) +} + +// AttestationKeyRSA generates and loads a key from AKTemplateRSA in the Owner hierarchy. +func AttestationKeyRSA(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleOwner, AKTemplateRSA(), DefaultAKRSAHandle) +} + +// AttestationKeyECC generates and loads a key from AKTemplateECC in the Owner hierarchy. +func AttestationKeyECC(rw io.ReadWriter) (*Key, error) { + return NewCachedKey(rw, tpm2.HandleOwner, AKTemplateECC(), DefaultAKECCHandle) +} + +// EndorsementKeyFromNvIndex generates and loads an endorsement key using the +// template stored at the provided nvdata index. This is useful for TPMs which +// have a preinstalled AK template. +func EndorsementKeyFromNvIndex(rw io.ReadWriter, idx uint32) (*Key, error) { + return KeyFromNvIndex(rw, tpm2.HandleEndorsement, idx) +} + +// GceAttestationKeyRSA generates and loads the GCE RSA AK. Note that this +// function will only work on a GCE VM. Unlike AttestationKeyRSA, this key uses +// the Endorsement Hierarchy and its template loaded from GceAKTemplateNVIndexRSA. +func GceAttestationKeyRSA(rw io.ReadWriter) (*Key, error) { + return EndorsementKeyFromNvIndex(rw, GceAKTemplateNVIndexRSA) +} + +// GceAttestationKeyECC generates and loads the GCE ECC AK. Note that this +// function will only work on a GCE VM. Unlike AttestationKeyECC, this key uses +// the Endorsement Hierarchy and its template loaded from GceAKTemplateNVIndexECC. +func GceAttestationKeyECC(rw io.ReadWriter) (*Key, error) { + return EndorsementKeyFromNvIndex(rw, GceAKTemplateNVIndexECC) +} + +// KeyFromNvIndex generates and loads a key under the provided parent +// (possibly a hierarchy root tpm2.Handle{Owner|Endorsement|Platform|Null}) +// using the template stored at the provided nvdata index. +func KeyFromNvIndex(rw io.ReadWriter, parent tpmutil.Handle, idx uint32) (*Key, error) { + data, err := tpm2.NVReadEx(rw, tpmutil.Handle(idx), tpm2.HandleOwner, "", 0) + if err != nil { + return nil, fmt.Errorf("read error at index %d: %w", idx, err) + } + template, err := tpm2.DecodePublic(data) + if err != nil { + return nil, fmt.Errorf("index %d data was not a TPM key template: %w", idx, err) + } + return NewKey(rw, parent, template) +} + +// NewCachedKey is almost identical to NewKey, except that it initially tries to +// see if the a key matching the provided template is at cachedHandle. If so, +// that key is returned. If not, the key is created as in NewKey, and that key +// is persisted to the cachedHandle, overwriting any existing key there. +func NewCachedKey(rw io.ReadWriter, parent tpmutil.Handle, template tpm2.Public, cachedHandle tpmutil.Handle) (k *Key, err error) { + owner := tpm2.HandleOwner + if parent == tpm2.HandlePlatform { + owner = tpm2.HandlePlatform + } else if parent == tpm2.HandleNull { + return nil, fmt.Errorf("cannot cache objects in the null hierarchy") + } + + cachedPub, _, _, err := tpm2.ReadPublic(rw, cachedHandle) + if err == nil { + if cachedPub.MatchesTemplate(template) { + k = &Key{rw: rw, handle: cachedHandle, pubArea: cachedPub} + return k, k.finish() + } + // Kick out old cached key if it does not match + if err = tpm2.EvictControl(rw, "", owner, cachedHandle, cachedHandle); err != nil { + return nil, err + } + } + + k, err = NewKey(rw, parent, template) + if err != nil { + return nil, err + } + defer tpm2.FlushContext(rw, k.handle) + + if err = tpm2.EvictControl(rw, "", owner, k.handle, cachedHandle); err != nil { + return nil, err + } + k.handle = cachedHandle + return k, nil +} + +// NewKey generates a key from the template and loads that key into the TPM +// under the specified parent. NewKey can call many different TPM commands: +// - If parent is tpm2.Handle{Owner|Endorsement|Platform|Null} a primary key +// is created in the specified hierarchy (using CreatePrimary). +// - If parent is a valid key handle, a normal key object is created under +// that parent (using Create and Load). NOTE: Not yet supported. +// This function also assumes that the desired key: +// - Does not have its usage locked to specific PCR values +// - Usable with empty authorization sessions (i.e. doesn't need a password) +func NewKey(rw io.ReadWriter, parent tpmutil.Handle, template tpm2.Public) (k *Key, err error) { + if !isHierarchy(parent) { + // TODO add support for normal objects with Create() and Load() + return nil, fmt.Errorf("unsupported parent handle: %x", parent) + } + + handle, pubArea, _, _, _, _, err := + tpm2.CreatePrimaryEx(rw, parent, tpm2.PCRSelection{}, "", "", template) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + tpm2.FlushContext(rw, handle) + } + }() + + k = &Key{rw: rw, handle: handle} + if k.pubArea, err = tpm2.DecodePublic(pubArea); err != nil { + return + } + return k, k.finish() +} + +func (k *Key) finish() error { + var err error + if k.pubKey, err = k.pubArea.Key(); err != nil { + return err + } + if k.name, err = k.pubArea.Name(); err != nil { + return err + } + // We determine the right type of session based on the auth policy + if k.session == nil { + if bytes.Equal(k.pubArea.AuthPolicy, defaultEKAuthPolicy()) { + if k.session, err = newEKSession(k.rw); err != nil { + return err + } + } else if len(k.pubArea.AuthPolicy) == 0 { + k.session = nullSession{} + } else { + return fmt.Errorf("unknown auth policy when creating key") + } + } + return nil +} + +// Handle allows this key to be used directly with other go-tpm commands. +func (k *Key) Handle() tpmutil.Handle { + return k.handle +} + +// Name is hash of this key's public area. Only the Digest field will ever be +// populated. It is useful for various TPM commands related to authorization. +// This is equivalent to k.PublicArea.Name(), except that is cannot fail. +func (k *Key) Name() tpm2.Name { + return k.name +} + +// PublicArea exposes the key's entire public area. This is useful for +// determining additional properties of the underlying TPM key. +func (k *Key) PublicArea() tpm2.Public { + return k.pubArea +} + +// PublicKey provides a go interface to the loaded key's public area. +func (k *Key) PublicKey() crypto.PublicKey { + return k.pubKey +} + +// Close should be called when the key is no longer needed. This is important to +// do as most TPMs can only have a small number of key simultaneously loaded. +func (k *Key) Close() { + if k.session != nil { + k.session.Close() + } + tpm2.FlushContext(k.rw, k.handle) +} + +// Seal seals the sensitive byte buffer to a key. This key must be an SRK (we +// currently do not support sealing to EKs). Optionally, the SealOpts struct can +// be modified to provide sealed-to PCRs. In this case, the sensitive data can +// only be unsealed if the seal-time PCRs are in the SealOpts-specified state. +// There must not be overlap in PCRs between SealOpts' Current and Target. +// During the sealing process, certification data will be created allowing +// Unseal() to validate the state of the TPM during the sealing process. +func (k *Key) Seal(sensitive []byte, opts SealOpts) (*pb.SealedBytes, error) { + var pcrs *pb.PCRs + var err error + var auth []byte + + pcrs, err = mergePCRSelAndProto(k.rw, opts.Current, opts.Target) + if err != nil { + return nil, fmt.Errorf("invalid SealOpts: %v", err) + } + if len(pcrs.GetPcrs()) > 0 { + auth = internal.PCRSessionAuth(pcrs, SessionHashAlg) + } + certifySel := FullPcrSel(CertifyHashAlgTpm) + sb, err := sealHelper(k.rw, k.Handle(), auth, sensitive, certifySel) + if err != nil { + return nil, err + } + + for pcrNum := range pcrs.GetPcrs() { + sb.Pcrs = append(sb.Pcrs, pcrNum) + } + sb.Hash = pcrs.GetHash() + sb.Srk = pb.ObjectType(k.pubArea.Type) + return sb, nil +} + +func sealHelper(rw io.ReadWriter, parentHandle tpmutil.Handle, auth []byte, sensitive []byte, certifyPCRsSel tpm2.PCRSelection) (*pb.SealedBytes, error) { + inPublic := tpm2.Public{ + Type: tpm2.AlgKeyedHash, + NameAlg: SessionHashAlgTpm, + Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent, + AuthPolicy: auth, + } + if auth == nil { + inPublic.Attributes |= tpm2.FlagUserWithAuth + } else { + inPublic.Attributes |= tpm2.FlagAdminWithPolicy + } + + priv, pub, creationData, _, ticket, err := tpm2.CreateKeyWithSensitive(rw, parentHandle, certifyPCRsSel, "", "", inPublic, sensitive) + if err != nil { + return nil, fmt.Errorf("failed to create key: %w", err) + } + certifiedPcr, err := ReadPCRs(rw, certifyPCRsSel) + if err != nil { + return nil, fmt.Errorf("failed to read PCRs: %w", err) + } + computedDigest := internal.PCRDigest(certifiedPcr, SessionHashAlg) + + decodedCreationData, err := tpm2.DecodeCreationData(creationData) + if err != nil { + return nil, fmt.Errorf("failed to decode creation data: %w", err) + } + + // make sure PCRs haven't being altered after sealing + if subtle.ConstantTimeCompare(computedDigest, decodedCreationData.PCRDigest) == 0 { + return nil, fmt.Errorf("PCRs have been modified after sealing") + } + + sb := &pb.SealedBytes{} + sb.CertifiedPcrs = certifiedPcr + sb.Priv = priv + sb.Pub = pub + sb.CreationData = creationData + if sb.Ticket, err = tpmutil.Pack(ticket); err != nil { + return nil, err + } + return sb, nil +} + +// Unseal attempts to reverse the process of Seal(), using the PCRs, public, and +// private data in proto.SealedBytes. Optionally, the UnsealOpts parameter can +// be used to verify the state of the TPM when the data was sealed. The +// zero-value UnsealOpts can be passed to skip certification. +func (k *Key) Unseal(in *pb.SealedBytes, opts UnsealOpts) ([]byte, error) { + if in.Srk != pb.ObjectType(k.pubArea.Type) { + return nil, fmt.Errorf("expected key of type %v, got %v", in.Srk, k.pubArea.Type) + } + sealed, _, err := tpm2.Load( + k.rw, + k.Handle(), + /*parentPassword=*/ "", + in.GetPub(), + in.GetPriv()) + if err != nil { + return nil, fmt.Errorf("failed to load sealed object: %w", err) + } + defer tpm2.FlushContext(k.rw, sealed) + + pcrs, err := mergePCRSelAndProto(k.rw, opts.CertifyCurrent, opts.CertifyExpected) + if err != nil { + return nil, fmt.Errorf("invalid UnsealOpts: %v", err) + } + if len(pcrs.GetPcrs()) > 0 { + if err := internal.CheckSubset(pcrs, in.GetCertifiedPcrs()); err != nil { + return nil, fmt.Errorf("failed to certify PCRs: %w", err) + } + + var ticket tpm2.Ticket + if _, err = tpmutil.Unpack(in.GetTicket(), &ticket); err != nil { + return nil, fmt.Errorf("ticket unpack failed: %w", err) + } + creationHash := SessionHashAlg.New() + creationHash.Write(in.GetCreationData()) + + _, _, certErr := tpm2.CertifyCreation(k.rw, "", sealed, tpm2.HandleNull, nil, creationHash.Sum(nil), tpm2.SigScheme{}, ticket) + // There is a bug in some older TPMs, where they are unable to + // CertifyCreation when using a Null signing handle (despite this + // being allowed by all versions of the TPM spec). To work around + // this bug, we use a temporary signing key and ignore the signed + // result. To reduce the cost of this workaround, we use a cached + // ECC signing key. + // We can detect this bug, as it triggers a RCInsufficient + // Unmarshaling error. + if paramErr, ok := certErr.(tpm2.ParameterError); ok && paramErr.Code == tpm2.RCInsufficient { + signer, err := AttestationKeyECC(k.rw) + if err != nil { + return nil, fmt.Errorf("failed to create fallback signing key: %w", err) + } + defer signer.Close() + _, _, certErr = tpm2.CertifyCreation(k.rw, "", sealed, signer.Handle(), nil, creationHash.Sum(nil), tpm2.SigScheme{}, ticket) + } + if certErr != nil { + return nil, fmt.Errorf("failed to certify creation: %w", certErr) + } + + // verify certify PCRs haven't been modified + decodedCreationData, err := tpm2.DecodeCreationData(in.GetCreationData()) + if err != nil { + return nil, fmt.Errorf("failed to decode creation data: %w", err) + } + if !internal.SamePCRSelection(in.GetCertifiedPcrs(), decodedCreationData.PCRSelection) { + return nil, fmt.Errorf("certify PCRs does not match the PCR selection in the creation data") + } + expectedDigest := internal.PCRDigest(in.GetCertifiedPcrs(), SessionHashAlg) + if subtle.ConstantTimeCompare(decodedCreationData.PCRDigest, expectedDigest) == 0 { + return nil, fmt.Errorf("certify PCRs digest does not match the digest in the creation data") + } + } + + sel := tpm2.PCRSelection{Hash: tpm2.Algorithm(in.GetHash())} + for _, pcr := range in.GetPcrs() { + sel.PCRs = append(sel.PCRs, int(pcr)) + } + + session, err := newPCRSession(k.rw, sel) + if err != nil { + return nil, fmt.Errorf("failed to create session: %w", err) + } + defer session.Close() + + auth, err := session.Auth() + if err != nil { + return nil, err + } + return tpm2.UnsealWithSession(k.rw, auth.Session, sealed, "") +} + +// Quote will tell TPM to compute a hash of a set of given PCR selection, together with +// some extra data (typically a nonce), sign it with the given signing key, and return +// the signature and the attestation data. This function will return an error if +// the key is not a restricted signing key. +func (k *Key) Quote(selpcr tpm2.PCRSelection, extraData []byte) (*pb.Quote, error) { + // Make sure that we have a valid signing key before trying quote + var err error + if _, err = internal.GetSigningHashAlg(k.pubArea); err != nil { + return nil, err + } + if !k.hasAttribute(tpm2.FlagRestricted) { + return nil, fmt.Errorf("unrestricted keys are insecure to use with Quote") + } + + quote := &pb.Quote{} + quote.Quote, quote.RawSig, err = tpm2.QuoteRaw(k.rw, k.Handle(), "", "", extraData, selpcr, tpm2.AlgNull) + if err != nil { + return nil, fmt.Errorf("failed to quote: %w", err) + } + quote.Pcrs, err = ReadPCRs(k.rw, selpcr) + if err != nil { + return nil, fmt.Errorf("failed to read PCRs: %w", err) + } + // Verify the quote client-side to make sure we didn't mess things up. + // NOTE: the quote still must be verified server-side as well. + if err := internal.VerifyQuote(quote, k.PublicKey(), extraData); err != nil { + return nil, fmt.Errorf("failed to verify quote: %w", err) + } + return quote, nil +} + +// Reseal is a shortcut to call Unseal() followed by Seal(). +// CertifyOpt(nillable) will be used in Unseal(), and SealOpt(nillable) +// will be used in Seal() +func (k *Key) Reseal(in *pb.SealedBytes, uOpts UnsealOpts, sOpts SealOpts) (*pb.SealedBytes, error) { + sensitive, err := k.Unseal(in, uOpts) + if err != nil { + return nil, fmt.Errorf("failed to unseal: %w", err) + } + return k.Seal(sensitive, sOpts) +} + +func (k *Key) hasAttribute(attr tpm2.KeyProp) bool { + return k.pubArea.Attributes&attr != 0 +} diff --git a/vendor/github.com/google/go-tpm-tools/client/pcr.go b/vendor/github.com/google/go-tpm-tools/client/pcr.go new file mode 100644 index 000000000000..1e0b3b00f48d --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/pcr.go @@ -0,0 +1,166 @@ +package client + +import ( + "crypto" + "fmt" + "io" + "math" + + pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-tpm/tpm2" +) + +// NumPCRs is set to the spec minimum of 24, as that's all go-tpm supports. +const NumPCRs = 24 + +// We hard-code SHA256 as the policy session hash algorithms. Note that this +// differs from the PCR hash algorithm (which selects the bank of PCRs to use) +// and the Public area Name algorithm. We also chose this for compatibility with +// github.com/google/go-tpm/tpm2, as it hardcodes the nameAlg as SHA256 in +// several places. Two constants are used to avoid repeated conversions. +const ( + SessionHashAlg = crypto.SHA256 + SessionHashAlgTpm = tpm2.AlgSHA256 +) + +// CertifyHashAlgTpm is the hard-coded algorithm used in certify PCRs. +const CertifyHashAlgTpm = tpm2.AlgSHA256 + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// Get a list of selections corresponding to the TPM's implemented PCRs +func implementedPCRs(rw io.ReadWriter) ([]tpm2.PCRSelection, error) { + caps, moreData, err := tpm2.GetCapability(rw, tpm2.CapabilityPCRs, math.MaxUint32, 0) + if err != nil { + return nil, fmt.Errorf("listing implemented PCR banks: %w", err) + } + if moreData { + return nil, fmt.Errorf("extra data from GetCapability") + } + sels := make([]tpm2.PCRSelection, len(caps)) + for i, cap := range caps { + sel, ok := cap.(tpm2.PCRSelection) + if !ok { + return nil, fmt.Errorf("unexpected data from GetCapability") + } + sels[i] = sel + } + return sels, nil +} + +// ReadPCRs fetches all the PCR values specified in sel, making multiple calls +// to the TPM if necessary. +func ReadPCRs(rw io.ReadWriter, sel tpm2.PCRSelection) (*pb.PCRs, error) { + pl := pb.PCRs{ + Hash: pb.HashAlgo(sel.Hash), + Pcrs: map[uint32][]byte{}, + } + + for i := 0; i < len(sel.PCRs); i += 8 { + end := min(i+8, len(sel.PCRs)) + pcrSel := tpm2.PCRSelection{ + Hash: sel.Hash, + PCRs: sel.PCRs[i:end], + } + + pcrMap, err := tpm2.ReadPCRs(rw, pcrSel) + if err != nil { + return nil, err + } + + for pcr, val := range pcrMap { + pl.Pcrs[uint32(pcr)] = val + } + } + + return &pl, nil +} + +// ReadAllPCRs fetches all the PCR values from all implemented PCR banks. +func ReadAllPCRs(rw io.ReadWriter) ([]*pb.PCRs, error) { + sels, err := implementedPCRs(rw) + if err != nil { + return nil, err + } + + allPcrs := make([]*pb.PCRs, len(sels)) + for i, sel := range sels { + allPcrs[i], err = ReadPCRs(rw, sel) + if err != nil { + return nil, fmt.Errorf("reading bank %x PCRs: %w", sel.Hash, err) + } + } + return allPcrs, nil +} + +// SealOpts specifies the PCR values that should be used for Seal(). +type SealOpts struct { + // Current seals data to the current specified PCR selection. + Current tpm2.PCRSelection + // Target predictively seals data to the given specified PCR values. + Target *pb.PCRs +} + +// UnsealOpts specifies the options that should be used for Unseal(). +// Currently, it specifies the PCRs that need to pass certification in order to +// successfully unseal. +// CertifyHashAlgTpm is the hard-coded algorithm that must be used with +// UnsealOpts. +type UnsealOpts struct { + // CertifyCurrent certifies that a selection of current PCRs have the same + // value when sealing. + CertifyCurrent tpm2.PCRSelection + // CertifyExpected certifies that the TPM had a specific set of PCR values when sealing. + CertifyExpected *pb.PCRs +} + +// FullPcrSel will return a full PCR selection based on the total PCR number +// of the TPM with the given hash algo. +func FullPcrSel(hash tpm2.Algorithm) tpm2.PCRSelection { + sel := tpm2.PCRSelection{Hash: hash} + for i := 0; i < NumPCRs; i++ { + sel.PCRs = append(sel.PCRs, int(i)) + } + return sel +} + +func mergePCRSelAndProto(rw io.ReadWriter, sel tpm2.PCRSelection, proto *pb.PCRs) (*pb.PCRs, error) { + if proto == nil || len(proto.GetPcrs()) == 0 { + return ReadPCRs(rw, sel) + } + if len(sel.PCRs) == 0 { + return proto, nil + } + if sel.Hash != tpm2.Algorithm(proto.Hash) { + return nil, fmt.Errorf("current hash (%v) differs from target hash (%v)", + sel.Hash, tpm2.Algorithm(proto.Hash)) + } + + // At this point, both sel and proto are non-empty. + // Verify no overlap in sel and proto PCR indexes. + overlap := make([]int, 0) + targetMap := proto.GetPcrs() + for _, pcrVal := range sel.PCRs { + if _, found := targetMap[uint32(pcrVal)]; found { + overlap = append(overlap, pcrVal) + } + } + if len(overlap) != 0 { + return nil, fmt.Errorf("found PCR overlap: %v", overlap) + } + + currentPcrs, err := ReadPCRs(rw, sel) + if err != nil { + return nil, err + } + + for pcr, val := range proto.GetPcrs() { + currentPcrs.Pcrs[pcr] = val + } + return currentPcrs, nil +} diff --git a/vendor/github.com/google/go-tpm-tools/client/session.go b/vendor/github.com/google/go-tpm-tools/client/session.go new file mode 100644 index 000000000000..1803ef3166c1 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/session.go @@ -0,0 +1,89 @@ +package client + +import ( + "io" + + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +type session interface { + io.Closer + Auth() (tpm2.AuthCommand, error) +} + +func startAuthSession(rw io.ReadWriter) (session tpmutil.Handle, err error) { + // This session assumes the bus is trusted, so we: + // - use nil for tpmKey, encrypted salt, and symmetric + // - use and all-zeros caller nonce, and ignore the returned nonce + // As we are creating a plain TPM session, we: + // - setup a policy session + // - don't bind the session to any particular key + session, _, err = tpm2.StartAuthSession( + rw, + /*tpmKey=*/ tpm2.HandleNull, + /*bindKey=*/ tpm2.HandleNull, + /*nonceCaller=*/ make([]byte, SessionHashAlg.Size()), + /*encryptedSalt=*/ nil, + /*sessionType=*/ tpm2.SessionPolicy, + /*symmetric=*/ tpm2.AlgNull, + /*authHash=*/ SessionHashAlgTpm) + return +} + +type pcrSession struct { + rw io.ReadWriter + session tpmutil.Handle + sel tpm2.PCRSelection +} + +func newPCRSession(rw io.ReadWriter, sel tpm2.PCRSelection) (session, error) { + if len(sel.PCRs) == 0 { + return nullSession{}, nil + } + session, err := startAuthSession(rw) + return pcrSession{rw, session, sel}, err +} + +func (p pcrSession) Auth() (auth tpm2.AuthCommand, err error) { + if err = tpm2.PolicyPCR(p.rw, p.session, nil, p.sel); err != nil { + return + } + return tpm2.AuthCommand{Session: p.session, Attributes: tpm2.AttrContinueSession}, nil +} + +func (p pcrSession) Close() error { + return tpm2.FlushContext(p.rw, p.session) +} + +type ekSession struct { + rw io.ReadWriter + session tpmutil.Handle +} + +func newEKSession(rw io.ReadWriter) (session, error) { + session, err := startAuthSession(rw) + return ekSession{rw, session}, err +} + +func (e ekSession) Auth() (auth tpm2.AuthCommand, err error) { + nullAuth := tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession} + if _, err = tpm2.PolicySecret(e.rw, tpm2.HandleEndorsement, nullAuth, e.session, nil, nil, nil, 0); err != nil { + return + } + return tpm2.AuthCommand{Session: e.session, Attributes: tpm2.AttrContinueSession}, nil +} + +func (e ekSession) Close() error { + return tpm2.FlushContext(e.rw, e.session) +} + +type nullSession struct{} + +func (n nullSession) Auth() (auth tpm2.AuthCommand, err error) { + return tpm2.AuthCommand{Session: tpm2.HandlePasswordSession, Attributes: tpm2.AttrContinueSession}, nil +} + +func (n nullSession) Close() error { + return nil +} diff --git a/vendor/github.com/google/go-tpm-tools/client/signer.go b/vendor/github.com/google/go-tpm-tools/client/signer.go new file mode 100644 index 000000000000..a2de5201d0ee --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/signer.go @@ -0,0 +1,146 @@ +package client + +import ( + "crypto" + "crypto/rsa" + "encoding/asn1" + "fmt" + "io" + "math/big" + "sync" + + "github.com/google/go-tpm-tools/internal" + "github.com/google/go-tpm/tpm2" +) + +// Global mutex to protect against concurrent TPM access. +var signerMutex sync.Mutex + +type tpmSigner struct { + Key *Key + Hash crypto.Hash +} + +// Public returns the tpmSigners public key. +func (signer *tpmSigner) Public() crypto.PublicKey { + return signer.Key.PublicKey() +} + +// Sign uses the TPM key to sign the digest. +// The digest must be hashed from the same hash algorithm as the keys scheme. +// The opts hash function must also match the keys scheme (or be nil). +// Concurrent use of Sign is thread safe, but it is not safe to access the TPM +// from other sources while Sign is executing. +// For RSAPSS signatures, you cannot specify custom salt lengths. The salt +// length will be (keyBits/8) - digestSize - 2, unless that is less than the +// digestSize in which case, saltLen will be digestSize. The only normal case +// where saltLen is not digestSize is when using 1024 keyBits with SHA512. +func (signer *tpmSigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { + if pssOpts, ok := opts.(*rsa.PSSOptions); ok { + if signer.Key.pubArea.RSAParameters == nil { + return nil, fmt.Errorf("invalid options: PSSOptions can only be used with RSA keys") + } + if signer.Key.pubArea.RSAParameters.Sign.Alg != tpm2.AlgRSAPSS { + return nil, fmt.Errorf("invalid options: PSSOptions cannot be used with signing alg: %v", signer.Key.pubArea.RSAParameters.Sign.Alg) + } + if pssOpts.SaltLength != rsa.PSSSaltLengthAuto { + return nil, fmt.Errorf("salt length must be rsa.PSSSaltLengthAuto") + } + } + if opts != nil && opts.HashFunc() != signer.Hash { + return nil, fmt.Errorf("hash algorithm: got %v, want %v", opts.HashFunc(), signer.Hash) + } + if len(digest) != signer.Hash.Size() { + return nil, fmt.Errorf("digest length: got %d, want %d", digest, signer.Hash.Size()) + } + + signerMutex.Lock() + defer signerMutex.Unlock() + + auth, err := signer.Key.session.Auth() + if err != nil { + return nil, err + } + + sig, err := tpm2.SignWithSession(signer.Key.rw, auth.Session, signer.Key.handle, "", digest, nil, nil) + if err != nil { + return nil, err + } + return getSignature(sig) +} + +// GetSigner returns a crypto.Signer wrapping the loaded TPM Key. +// Concurrent use of one or more Signers is thread safe, but it is not safe to +// access the TPM from other sources while using a Signer. +// The returned Signer lasts the lifetime of the Key, and will no longer work +// once the Key has been closed. +func (k *Key) GetSigner() (crypto.Signer, error) { + if k.hasAttribute(tpm2.FlagRestricted) { + return nil, fmt.Errorf("restricted keys are not supported") + } + hashAlg, err := internal.GetSigningHashAlg(k.pubArea) + if err != nil { + return nil, err + } + // For crypto.Signer, Go does the hashing. Make sure the hash is supported. + hash, err := hashAlg.Hash() + if err != nil { + return nil, err + } + return &tpmSigner{k, hash}, nil +} + +// SignData signs a data buffer with a TPM loaded key. Unlike GetSigner, this +// method works with restricted and unrestricted keys. If this method is called +// on a restriced key, the TPM itself will hash the provided data, failing the +// signing operation if the data begins with TPM_GENERATED_VALUE. +func (k *Key) SignData(data []byte) ([]byte, error) { + hashAlg, err := internal.GetSigningHashAlg(k.pubArea) + if err != nil { + return nil, err + } + + var digest []byte + var ticket *tpm2.Ticket + if k.hasAttribute(tpm2.FlagRestricted) { + // Restricted keys can only sign data hashed by the TPM. We use the + // owner hierarchy for the Ticket, but any non-Null hierarchy would do. + digest, ticket, err = tpm2.Hash(k.rw, hashAlg, data, tpm2.HandleOwner) + if err != nil { + return nil, err + } + } else { + // Unrestricted keys can sign any digest, no need for TPM hashing. + hash, err := hashAlg.Hash() + if err != nil { + return nil, err + } + hasher := hash.New() + hasher.Write(data) + digest = hasher.Sum(nil) + } + + auth, err := k.session.Auth() + if err != nil { + return nil, err + } + sig, err := tpm2.SignWithSession(k.rw, auth.Session, k.handle, "", digest, ticket, nil) + if err != nil { + return nil, err + } + return getSignature(sig) +} + +func getSignature(sig *tpm2.Signature) ([]byte, error) { + switch sig.Alg { + case tpm2.AlgRSASSA: + return sig.RSA.Signature, nil + case tpm2.AlgRSAPSS: + return sig.RSA.Signature, nil + case tpm2.AlgECDSA: + sigStruct := struct{ R, S *big.Int }{sig.ECC.R, sig.ECC.S} + return asn1.Marshal(sigStruct) + default: + return nil, fmt.Errorf("unsupported signing algorithm: %v", sig.Alg) + } +} diff --git a/vendor/github.com/google/go-tpm-tools/client/template.go b/vendor/github.com/google/go-tpm-tools/client/template.go new file mode 100644 index 000000000000..a82632883a71 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/client/template.go @@ -0,0 +1,143 @@ +package client + +import ( + "crypto/sha256" + + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +// Calculations from Credential_Profile_EK_V2.0, section 2.1.5.3 - authPolicy +func defaultEKAuthPolicy() []byte { + buf, err := tpmutil.Pack(tpm2.CmdPolicySecret, tpm2.HandleEndorsement) + if err != nil { + panic(err) + } + digest1 := sha256.Sum256(append(make([]byte, 32), buf...)) + // We would normally append the policy buffer to digest1, but the + // policy buffer is empty for the default Auth Policy. + digest2 := sha256.Sum256(digest1[:]) + return digest2[:] +} + +func defaultEKAttributes() tpm2.KeyProp { + // The EK is a storage key that must use session-based authorization. + return (tpm2.FlagStorageDefault | tpm2.FlagAdminWithPolicy) & ^tpm2.FlagUserWithAuth +} + +func defaultSRKAttributes() tpm2.KeyProp { + // FlagNoDA doesn't do anything (as the AuthPolicy is nil). However, this is + // what Windows does, and we don't want to conflict. + return tpm2.FlagStorageDefault | tpm2.FlagNoDA +} + +func defaultSymScheme() *tpm2.SymScheme { + return &tpm2.SymScheme{ + Alg: tpm2.AlgAES, + KeyBits: 128, + Mode: tpm2.AlgCFB, + } +} + +func defaultRSAParams() *tpm2.RSAParams { + return &tpm2.RSAParams{ + Symmetric: defaultSymScheme(), + KeyBits: 2048, + ModulusRaw: make([]byte, 256), // public.unique must be all zeros + } +} + +func defaultECCParams() *tpm2.ECCParams { + return &tpm2.ECCParams{ + Symmetric: defaultSymScheme(), + CurveID: tpm2.CurveNISTP256, + Point: tpm2.ECPoint{ + XRaw: make([]byte, 32), + YRaw: make([]byte, 32), + }, + } +} + +// DefaultEKTemplateRSA returns the default Endorsement Key (EK) template as +// specified in Credential_Profile_EK_V2.0, section 2.1.5.1 - authPolicy. +// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf +func DefaultEKTemplateRSA() tpm2.Public { + return tpm2.Public{ + Type: tpm2.AlgRSA, + NameAlg: tpm2.AlgSHA256, + Attributes: defaultEKAttributes(), + AuthPolicy: defaultEKAuthPolicy(), + RSAParameters: defaultRSAParams(), + } +} + +// DefaultEKTemplateECC returns the default Endorsement Key (EK) template as +// specified in Credential_Profile_EK_V2.0, section 2.1.5.2 - authPolicy. +// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf +func DefaultEKTemplateECC() tpm2.Public { + return tpm2.Public{ + Type: tpm2.AlgECC, + NameAlg: tpm2.AlgSHA256, + Attributes: defaultEKAttributes(), + AuthPolicy: defaultEKAuthPolicy(), + ECCParameters: defaultECCParams(), + } +} + +// AKTemplateRSA returns a potential Attestation Key (AK) template. +// This is very similar to DefaultEKTemplateRSA, except that this will be a +// signing key instead of an encrypting key. +func AKTemplateRSA() tpm2.Public { + return tpm2.Public{ + Type: tpm2.AlgRSA, + NameAlg: tpm2.AlgSHA256, + Attributes: tpm2.FlagSignerDefault, + RSAParameters: &tpm2.RSAParams{ + Sign: &tpm2.SigScheme{ + Alg: tpm2.AlgRSASSA, + Hash: tpm2.AlgSHA256, + }, + KeyBits: 2048, + }, + } +} + +// AKTemplateECC returns a potential Attestation Key (AK) template. +// This is very similar to DefaultEKTemplateECC, except that this will be a +// signing key instead of an encrypting key. +func AKTemplateECC() tpm2.Public { + params := defaultECCParams() + params.Symmetric = nil + params.Sign = &tpm2.SigScheme{ + Alg: tpm2.AlgECDSA, + Hash: tpm2.AlgSHA256, + } + return tpm2.Public{ + Type: tpm2.AlgECC, + NameAlg: tpm2.AlgSHA256, + Attributes: tpm2.FlagSignerDefault, + ECCParameters: params, + } +} + +// SRKTemplateRSA returns a standard Storage Root Key (SRK) template. +// This is based upon the advice in the TCG's TPM v2.0 Provisioning Guidance. +func SRKTemplateRSA() tpm2.Public { + return tpm2.Public{ + Type: tpm2.AlgRSA, + NameAlg: tpm2.AlgSHA256, + Attributes: defaultSRKAttributes(), + RSAParameters: defaultRSAParams(), + } +} + +// SRKTemplateECC returns a standard Storage Root Key (SRK) template. +// This is based upon the advice in the TCG's TPM v2.0 Provisioning Guidance. +func SRKTemplateECC() tpm2.Public { + return tpm2.Public{ + Type: tpm2.AlgECC, + NameAlg: tpm2.AlgSHA256, + Attributes: defaultSRKAttributes(), + ECCParameters: defaultECCParams(), + } +} diff --git a/vendor/github.com/google/go-tpm-tools/internal/BUILD.bazel b/vendor/github.com/google/go-tpm-tools/internal/BUILD.bazel new file mode 100644 index 000000000000..f07a110bc072 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/internal/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "pcrs.go", + "public.go", + "quote.go", + ], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm-tools/internal", + importpath = "github.com/google/go-tpm-tools/internal", + visibility = ["//vendor/github.com/google/go-tpm-tools:__subpackages__"], + deps = [ + "//vendor/github.com/google/go-tpm-tools/proto/tpm:go_default_library", + "//vendor/github.com/google/go-tpm/tpm2:go_default_library", + "//vendor/github.com/google/go-tpm/tpmutil:go_default_library", + ], +) diff --git a/vendor/github.com/google/go-tpm-tools/internal/pcrs.go b/vendor/github.com/google/go-tpm-tools/internal/pcrs.go new file mode 100644 index 000000000000..8a1015b4dc3a --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/internal/pcrs.go @@ -0,0 +1,130 @@ +// Package internal contains private helper functions needed in client and server +package internal + +import ( + "bytes" + "crypto" + "fmt" + "io" + + pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +const minPCRIndex = uint32(0) + +func maxPCRIndex(p *pb.PCRs) uint32 { + max := minPCRIndex + for idx := range p.GetPcrs() { + if idx > max { + max = idx + } + } + return max +} + +// FormatPCRs writes a multiline representation of the PCR values to w. +func FormatPCRs(w io.Writer, p *pb.PCRs) error { + if _, err := fmt.Fprintf(w, "%v:\n", p.Hash); err != nil { + return err + } + for idx := minPCRIndex; idx <= maxPCRIndex(p); idx++ { + if val, ok := p.GetPcrs()[idx]; ok { + if _, err := fmt.Fprintf(w, " %2d: 0x%X\n", idx, val); err != nil { + return err + } + } + } + return nil +} + +// CheckSubset verifies if the pcrs PCRs are a valid "subset" of the provided +// "superset" of PCRs. The PCR values must match (if present), and all PCRs must +// be present in the superset. This function will return an error containing the +// first missing or mismatched PCR number. +func CheckSubset(subset, superset *pb.PCRs) error { + if subset.GetHash() != superset.GetHash() { + return fmt.Errorf("PCR hash algo not matching: %v, %v", subset.GetHash(), superset.GetHash()) + } + for pcrNum, pcrVal := range subset.GetPcrs() { + if expectedVal, ok := superset.GetPcrs()[pcrNum]; ok { + if !bytes.Equal(expectedVal, pcrVal) { + return fmt.Errorf("PCR %d mismatch: expected %v, got %v", pcrNum, expectedVal, pcrVal) + } + } else { + return fmt.Errorf("PCR %d mismatch: value missing from the superset PCRs", pcrNum) + } + } + return nil +} + +// PCRSelection returns the corresponding tpm2.PCRSelection for the PCR data. +func PCRSelection(p *pb.PCRs) tpm2.PCRSelection { + sel := tpm2.PCRSelection{Hash: tpm2.Algorithm(p.GetHash())} + + for pcrNum := range p.GetPcrs() { + sel.PCRs = append(sel.PCRs, int(pcrNum)) + } + return sel +} + +// SamePCRSelection checks if the Pcrs has the same PCRSelection as the +// provided given tpm2.PCRSelection (including the hash algorithm). +func SamePCRSelection(p *pb.PCRs, sel tpm2.PCRSelection) bool { + if tpm2.Algorithm(p.GetHash()) != sel.Hash { + return false + } + if len(p.GetPcrs()) != len(sel.PCRs) { + return false + } + for _, pcr := range sel.PCRs { + if _, ok := p.Pcrs[uint32(pcr)]; !ok { + return false + } + } + return true +} + +// PCRSessionAuth calculates the authorization value for the given PCRs. +func PCRSessionAuth(p *pb.PCRs, hashAlg crypto.Hash) []byte { + // Start with all zeros, we only use a single policy command on our session. + oldDigest := make([]byte, hashAlg.Size()) + ccPolicyPCR, _ := tpmutil.Pack(tpm2.CmdPolicyPCR) + + // Extend the policy digest, see TPM2_PolicyPCR in Part 3 of the spec. + hash := hashAlg.New() + hash.Write(oldDigest) + hash.Write(ccPolicyPCR) + hash.Write(encodePCRSelection(PCRSelection(p))) + hash.Write(PCRDigest(p, hashAlg)) + newDigest := hash.Sum(nil) + return newDigest[:] +} + +// PCRDigest computes the digest of the Pcrs. Note that the digest hash +// algorithm may differ from the PCRs' hash (which denotes the PCR bank). +func PCRDigest(p *pb.PCRs, hashAlg crypto.Hash) []byte { + hash := hashAlg.New() + for i := uint32(0); i < 24; i++ { + if pcrValue, exists := p.GetPcrs()[i]; exists { + hash.Write(pcrValue) + } + } + return hash.Sum(nil) +} + +// Encode a tpm2.PCRSelection as if it were a TPML_PCR_SELECTION +func encodePCRSelection(sel tpm2.PCRSelection) []byte { + // Encode count, pcrSelections.hash and pcrSelections.sizeofSelect fields + buf, _ := tpmutil.Pack(uint32(1), sel.Hash, byte(3)) + // Encode pcrSelect bitmask + pcrBits := make([]byte, 3) + for _, pcr := range sel.PCRs { + byteNum := pcr / 8 + bytePos := 1 << uint(pcr%8) + pcrBits[byteNum] |= byte(bytePos) + } + + return append(buf, pcrBits...) +} diff --git a/vendor/github.com/google/go-tpm-tools/internal/public.go b/vendor/github.com/google/go-tpm-tools/internal/public.go new file mode 100644 index 000000000000..729981d15adc --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/internal/public.go @@ -0,0 +1,35 @@ +package internal + +import ( + "fmt" + + "github.com/google/go-tpm/tpm2" +) + +// GetSigningHashAlg returns the hash algorithm used for a signing key. Returns +// an error if an algorithm isn't supported, or the key is not a signing key. +func GetSigningHashAlg(pubArea tpm2.Public) (tpm2.Algorithm, error) { + if pubArea.Attributes&tpm2.FlagSign == 0 { + return tpm2.AlgNull, fmt.Errorf("non-signing key used with signing operation") + } + + var sigScheme *tpm2.SigScheme + switch pubArea.Type { + case tpm2.AlgRSA: + sigScheme = pubArea.RSAParameters.Sign + case tpm2.AlgECC: + sigScheme = pubArea.ECCParameters.Sign + default: + return tpm2.AlgNull, fmt.Errorf("unsupported key type: %v", pubArea.Type) + } + + if sigScheme == nil { + return tpm2.AlgNull, fmt.Errorf("unsupported null signing scheme") + } + switch sigScheme.Alg { + case tpm2.AlgRSAPSS, tpm2.AlgRSASSA, tpm2.AlgECDSA: + return sigScheme.Hash, nil + default: + return tpm2.AlgNull, fmt.Errorf("unsupported signing algorithm: %v", sigScheme.Alg) + } +} diff --git a/vendor/github.com/google/go-tpm-tools/internal/quote.go b/vendor/github.com/google/go-tpm-tools/internal/quote.go new file mode 100644 index 000000000000..e32c67ee65f6 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/internal/quote.go @@ -0,0 +1,109 @@ +package internal + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/subtle" + "fmt" + + pb "github.com/google/go-tpm-tools/proto/tpm" + "github.com/google/go-tpm/tpm2" +) + +// VerifyQuote performs the following checks to validate a Quote: +// - the provided signature is generated by the trusted AK public key +// - the signature signs the provided quote data +// - the quote data starts with TPM_GENERATED_VALUE +// - the quote data is a valid TPMS_QUOTE_INFO +// - the quote data was taken over the provided PCRs +// - the provided PCR values match the quote data internal digest +// - the provided extraData matches that in the quote data +// Note that the caller must have already established trust in the provided +// public key before validating the Quote. +// +// VerifyQuote supports ECDSA and RSASSA signature verification. +func VerifyQuote(q *pb.Quote, trustedPub crypto.PublicKey, extraData []byte) error { + sig, err := tpm2.DecodeSignature(bytes.NewBuffer(q.GetRawSig())) + if err != nil { + return fmt.Errorf("signature decoding failed: %v", err) + } + + var hash crypto.Hash + switch pub := trustedPub.(type) { + case *ecdsa.PublicKey: + hash, err = sig.ECC.HashAlg.Hash() + if err != nil { + return err + } + if err = verifyECDSAQuoteSignature(pub, hash, q.GetQuote(), sig); err != nil { + return err + } + case *rsa.PublicKey: + hash, err = sig.RSA.HashAlg.Hash() + if err != nil { + return err + } + if err = verifyRSASSAQuoteSignature(pub, hash, q.GetQuote(), sig); err != nil { + return err + } + default: + return fmt.Errorf("only RSA and ECC public keys are currently supported, received type: %T", pub) + + } + + // Decode and check for magic TPMS_GENERATED_VALUE. + attestationData, err := tpm2.DecodeAttestationData(q.GetQuote()) + if err != nil { + return fmt.Errorf("decoding attestation data failed: %v", err) + } + if attestationData.Type != tpm2.TagAttestQuote { + return fmt.Errorf("expected quote tag, got: %v", attestationData.Type) + } + attestedQuoteInfo := attestationData.AttestedQuoteInfo + if attestedQuoteInfo == nil { + return fmt.Errorf("attestation data does not contain quote info") + } + if subtle.ConstantTimeCompare(attestationData.ExtraData, extraData) == 0 { + return fmt.Errorf("quote extraData did not match expected extraData") + } + return validatePCRDigest(attestedQuoteInfo, q.GetPcrs(), hash) +} + +func verifyECDSAQuoteSignature(ecdsaPub *ecdsa.PublicKey, hash crypto.Hash, quoted []byte, sig *tpm2.Signature) error { + if sig.Alg != tpm2.AlgECDSA { + return fmt.Errorf("signature scheme 0x%x is not supported, only ECDSA is supported", sig.Alg) + } + + hashConstructor := hash.New() + hashConstructor.Write(quoted) + if !ecdsa.Verify(ecdsaPub, hashConstructor.Sum(nil), sig.ECC.R, sig.ECC.S) { + return fmt.Errorf("ECC signature verification failed") + } + return nil +} + +func verifyRSASSAQuoteSignature(rsaPub *rsa.PublicKey, hash crypto.Hash, quoted []byte, sig *tpm2.Signature) error { + if sig.Alg != tpm2.AlgRSASSA { + return fmt.Errorf("signature scheme 0x%x is not supported, only RSASSA (PKCS#1 v1.5) is supported", sig.Alg) + } + + hashConstructor := hash.New() + hashConstructor.Write(quoted) + if err := rsa.VerifyPKCS1v15(rsaPub, hash, hashConstructor.Sum(nil), sig.RSA.Signature); err != nil { + return fmt.Errorf("RSASSA signature verification failed: %v", err) + } + return nil +} + +func validatePCRDigest(quoteInfo *tpm2.QuoteInfo, pcrs *pb.PCRs, hash crypto.Hash) error { + if !SamePCRSelection(pcrs, quoteInfo.PCRSelection) { + return fmt.Errorf("given PCRs and Quote do not have the same PCR selection") + } + pcrDigest := PCRDigest(pcrs, hash) + if subtle.ConstantTimeCompare(quoteInfo.PCRDigest, pcrDigest) == 0 { + return fmt.Errorf("given PCRs digest not matching") + } + return nil +} diff --git a/vendor/github.com/google/go-tpm-tools/proto/attest/BUILD.bazel b/vendor/github.com/google/go-tpm-tools/proto/attest/BUILD.bazel new file mode 100644 index 000000000000..4481842dc917 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/proto/attest/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["attest.pb.go"], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm-tools/proto/attest", + importpath = "github.com/google/go-tpm-tools/proto/attest", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/google/go-tpm-tools/proto/tpm:go_default_library", + "//vendor/google.golang.org/protobuf/reflect/protoreflect:go_default_library", + "//vendor/google.golang.org/protobuf/runtime/protoimpl:go_default_library", + ], +) diff --git a/vendor/github.com/google/go-tpm-tools/proto/attest/attest.pb.go b/vendor/github.com/google/go-tpm-tools/proto/attest/attest.pb.go new file mode 100644 index 000000000000..82a1501e45cf --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/proto/attest/attest.pb.go @@ -0,0 +1,854 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: attest.proto + +package attest + +import ( + tpm "github.com/google/go-tpm-tools/proto/tpm" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Type of hardware technology used to protect this instance +type GCEConfidentialTechnology int32 + +const ( + GCEConfidentialTechnology_NONE GCEConfidentialTechnology = 0 + GCEConfidentialTechnology_AMD_SEV GCEConfidentialTechnology = 1 + GCEConfidentialTechnology_AMD_SEV_ES GCEConfidentialTechnology = 2 +) + +// Enum value maps for GCEConfidentialTechnology. +var ( + GCEConfidentialTechnology_name = map[int32]string{ + 0: "NONE", + 1: "AMD_SEV", + 2: "AMD_SEV_ES", + } + GCEConfidentialTechnology_value = map[string]int32{ + "NONE": 0, + "AMD_SEV": 1, + "AMD_SEV_ES": 2, + } +) + +func (x GCEConfidentialTechnology) Enum() *GCEConfidentialTechnology { + p := new(GCEConfidentialTechnology) + *p = x + return p +} + +func (x GCEConfidentialTechnology) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GCEConfidentialTechnology) Descriptor() protoreflect.EnumDescriptor { + return file_attest_proto_enumTypes[0].Descriptor() +} + +func (GCEConfidentialTechnology) Type() protoreflect.EnumType { + return &file_attest_proto_enumTypes[0] +} + +func (x GCEConfidentialTechnology) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GCEConfidentialTechnology.Descriptor instead. +func (GCEConfidentialTechnology) EnumDescriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{0} +} + +// Information uniquely identifying a GCE instance. Can be used to create an +// instance URL, which can then be used with GCE APIs. Formatted like: +// https://www.googleapis.com/compute/v1/projects/{project_id}/zones/{zone}/instances/{instance_name} +type GCEInstanceInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Zone string `protobuf:"bytes,1,opt,name=zone,proto3" json:"zone,omitempty"` + ProjectId string `protobuf:"bytes,2,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` + ProjectNumber uint64 `protobuf:"varint,3,opt,name=project_number,json=projectNumber,proto3" json:"project_number,omitempty"` + InstanceName string `protobuf:"bytes,4,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` + InstanceId uint64 `protobuf:"varint,5,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` +} + +func (x *GCEInstanceInfo) Reset() { + *x = GCEInstanceInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GCEInstanceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GCEInstanceInfo) ProtoMessage() {} + +func (x *GCEInstanceInfo) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GCEInstanceInfo.ProtoReflect.Descriptor instead. +func (*GCEInstanceInfo) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{0} +} + +func (x *GCEInstanceInfo) GetZone() string { + if x != nil { + return x.Zone + } + return "" +} + +func (x *GCEInstanceInfo) GetProjectId() string { + if x != nil { + return x.ProjectId + } + return "" +} + +func (x *GCEInstanceInfo) GetProjectNumber() uint64 { + if x != nil { + return x.ProjectNumber + } + return 0 +} + +func (x *GCEInstanceInfo) GetInstanceName() string { + if x != nil { + return x.InstanceName + } + return "" +} + +func (x *GCEInstanceInfo) GetInstanceId() uint64 { + if x != nil { + return x.InstanceId + } + return 0 +} + +type Attestation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Attestation Key (AK) Public Area, encoded as a TPMT_PUBLIC + AkPub []byte `protobuf:"bytes,1,opt,name=ak_pub,json=akPub,proto3" json:"ak_pub,omitempty"` + // Quotes over all supported PCR banks + Quotes []*tpm.Quote `protobuf:"bytes,2,rep,name=quotes,proto3" json:"quotes,omitempty"` + // TCG Event Log, encoded in the raw binary format + EventLog []byte `protobuf:"bytes,3,opt,name=event_log,json=eventLog,proto3" json:"event_log,omitempty"` + // Optional information about a GCE instance, unused outside of GCE + InstanceInfo *GCEInstanceInfo `protobuf:"bytes,4,opt,name=instance_info,json=instanceInfo,proto3" json:"instance_info,omitempty"` +} + +func (x *Attestation) Reset() { + *x = Attestation{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Attestation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attestation) ProtoMessage() {} + +func (x *Attestation) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attestation.ProtoReflect.Descriptor instead. +func (*Attestation) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{1} +} + +func (x *Attestation) GetAkPub() []byte { + if x != nil { + return x.AkPub + } + return nil +} + +func (x *Attestation) GetQuotes() []*tpm.Quote { + if x != nil { + return x.Quotes + } + return nil +} + +func (x *Attestation) GetEventLog() []byte { + if x != nil { + return x.EventLog + } + return nil +} + +func (x *Attestation) GetInstanceInfo() *GCEInstanceInfo { + if x != nil { + return x.InstanceInfo + } + return nil +} + +// The platform/firmware state for this instance +type PlatformState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Firmware: + // *PlatformState_ScrtmVersionId + // *PlatformState_GceVersion + Firmware isPlatformState_Firmware `protobuf_oneof:"firmware"` + // Set to NONE on non-GCE instances or non-Confidential Shielded GCE instances + Technology GCEConfidentialTechnology `protobuf:"varint,3,opt,name=technology,proto3,enum=attest.GCEConfidentialTechnology" json:"technology,omitempty"` + // Only set for GCE instances + InstanceInfo *GCEInstanceInfo `protobuf:"bytes,4,opt,name=instance_info,json=instanceInfo,proto3" json:"instance_info,omitempty"` +} + +func (x *PlatformState) Reset() { + *x = PlatformState{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlatformState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlatformState) ProtoMessage() {} + +func (x *PlatformState) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlatformState.ProtoReflect.Descriptor instead. +func (*PlatformState) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{2} +} + +func (m *PlatformState) GetFirmware() isPlatformState_Firmware { + if m != nil { + return m.Firmware + } + return nil +} + +func (x *PlatformState) GetScrtmVersionId() []byte { + if x, ok := x.GetFirmware().(*PlatformState_ScrtmVersionId); ok { + return x.ScrtmVersionId + } + return nil +} + +func (x *PlatformState) GetGceVersion() uint32 { + if x, ok := x.GetFirmware().(*PlatformState_GceVersion); ok { + return x.GceVersion + } + return 0 +} + +func (x *PlatformState) GetTechnology() GCEConfidentialTechnology { + if x != nil { + return x.Technology + } + return GCEConfidentialTechnology_NONE +} + +func (x *PlatformState) GetInstanceInfo() *GCEInstanceInfo { + if x != nil { + return x.InstanceInfo + } + return nil +} + +type isPlatformState_Firmware interface { + isPlatformState_Firmware() +} + +type PlatformState_ScrtmVersionId struct { + // Raw S-CRTM version identifier (EV_S_CRTM_VERSION) + ScrtmVersionId []byte `protobuf:"bytes,1,opt,name=scrtm_version_id,json=scrtmVersionId,proto3,oneof"` +} + +type PlatformState_GceVersion struct { + // Virtual GCE firmware version (parsed from S-CRTM version id) + GceVersion uint32 `protobuf:"varint,2,opt,name=gce_version,json=gceVersion,proto3,oneof"` +} + +func (*PlatformState_ScrtmVersionId) isPlatformState_Firmware() {} + +func (*PlatformState_GceVersion) isPlatformState_Firmware() {} + +// A parsed event from the TCG event log +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The Platform Control Register (PCR) this event was extended into. + PcrIndex uint32 `protobuf:"varint,1,opt,name=pcr_index,json=pcrIndex,proto3" json:"pcr_index,omitempty"` + // The type of this event. Note that this value is not verified, so it should + // only be used as a hint during event parsing. + UntrustedType uint32 `protobuf:"varint,2,opt,name=untrusted_type,json=untrustedType,proto3" json:"untrusted_type,omitempty"` + // The raw data associated to this event. The meaning of this data is + // specific to the type of the event. + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // The event digest actually extended into the TPM. This is often the hash of + // the data field, but in some cases it may have a type-specific calculation. + Digest []byte `protobuf:"bytes,4,opt,name=digest,proto3" json:"digest,omitempty"` + // This is true if hash(data) == digest. + DigestVerified bool `protobuf:"varint,5,opt,name=digest_verified,json=digestVerified,proto3" json:"digest_verified,omitempty"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{3} +} + +func (x *Event) GetPcrIndex() uint32 { + if x != nil { + return x.PcrIndex + } + return 0 +} + +func (x *Event) GetUntrustedType() uint32 { + if x != nil { + return x.UntrustedType + } + return 0 +} + +func (x *Event) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Event) GetDigest() []byte { + if x != nil { + return x.Digest + } + return nil +} + +func (x *Event) GetDigestVerified() bool { + if x != nil { + return x.DigestVerified + } + return false +} + +// The verified state of a booted machine, obtained from an Attestation +type MachineState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform *PlatformState `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + // The complete parsed TCG Event Log, including those events used to + // create the PlatformState. + RawEvents []*Event `protobuf:"bytes,3,rep,name=raw_events,json=rawEvents,proto3" json:"raw_events,omitempty"` + // The hash algorithm used when verifying the Attestation. This indicates: + // - which PCR bank was used for for quote validation and event log replay + // - the hash algorithm used to calculate event digests + Hash tpm.HashAlgo `protobuf:"varint,4,opt,name=hash,proto3,enum=tpm.HashAlgo" json:"hash,omitempty"` +} + +func (x *MachineState) Reset() { + *x = MachineState{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MachineState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MachineState) ProtoMessage() {} + +func (x *MachineState) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MachineState.ProtoReflect.Descriptor instead. +func (*MachineState) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{4} +} + +func (x *MachineState) GetPlatform() *PlatformState { + if x != nil { + return x.Platform + } + return nil +} + +func (x *MachineState) GetRawEvents() []*Event { + if x != nil { + return x.RawEvents + } + return nil +} + +func (x *MachineState) GetHash() tpm.HashAlgo { + if x != nil { + return x.Hash + } + return tpm.HashAlgo(0) +} + +// A policy dictating which values of PlatformState to allow +type PlatformPolicy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If PlatformState.firmware contains a scrtm_version_id, it must appear + // in this list. For use with a GCE VM, minimum_gce_firmware_version is + // often a better alternative. + AllowedScrtmVersionIds [][]byte `protobuf:"bytes,1,rep,name=allowed_scrtm_version_ids,json=allowedScrtmVersionIds,proto3" json:"allowed_scrtm_version_ids,omitempty"` + // If PlatformState.firmware contains a minimum_gce_firmware_version, it must + // be greater than or equal to this value. Currently, the max version is 1. + MinimumGceFirmwareVersion uint32 `protobuf:"varint,2,opt,name=minimum_gce_firmware_version,json=minimumGceFirmwareVersion,proto3" json:"minimum_gce_firmware_version,omitempty"` + // The PlatformState's technology must be at least as secure as + // the specified minimum_technology (i.e. AMD_SEV_ES > AMD_SEV > NONE). + MinimumTechnology GCEConfidentialTechnology `protobuf:"varint,3,opt,name=minimum_technology,json=minimumTechnology,proto3,enum=attest.GCEConfidentialTechnology" json:"minimum_technology,omitempty"` +} + +func (x *PlatformPolicy) Reset() { + *x = PlatformPolicy{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlatformPolicy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlatformPolicy) ProtoMessage() {} + +func (x *PlatformPolicy) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlatformPolicy.ProtoReflect.Descriptor instead. +func (*PlatformPolicy) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{5} +} + +func (x *PlatformPolicy) GetAllowedScrtmVersionIds() [][]byte { + if x != nil { + return x.AllowedScrtmVersionIds + } + return nil +} + +func (x *PlatformPolicy) GetMinimumGceFirmwareVersion() uint32 { + if x != nil { + return x.MinimumGceFirmwareVersion + } + return 0 +} + +func (x *PlatformPolicy) GetMinimumTechnology() GCEConfidentialTechnology { + if x != nil { + return x.MinimumTechnology + } + return GCEConfidentialTechnology_NONE +} + +// A policy dictating which type of MachineStates to allow +type Policy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Platform *PlatformPolicy `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` +} + +func (x *Policy) Reset() { + *x = Policy{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Policy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Policy) ProtoMessage() {} + +func (x *Policy) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Policy.ProtoReflect.Descriptor instead. +func (*Policy) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{6} +} + +func (x *Policy) GetPlatform() *PlatformPolicy { + if x != nil { + return x.Platform + } + return nil +} + +var File_attest_proto protoreflect.FileDescriptor + +var file_attest_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x74, 0x70, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x0f, 0x47, 0x43, 0x45, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0xa3, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6b, 0x5f, 0x70, 0x75, 0x62, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x6b, 0x50, 0x75, 0x62, 0x12, 0x22, 0x0a, 0x06, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x74, + 0x70, 0x6d, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x06, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x3c, 0x0a, + 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x43, + 0x45, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xeb, 0x01, 0x0a, 0x0d, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, + 0x10, 0x73, 0x63, 0x72, 0x74, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x63, 0x72, 0x74, 0x6d, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0b, 0x67, 0x63, 0x65, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, + 0x52, 0x0a, 0x67, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0a, + 0x74, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x21, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x52, 0x0a, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, + 0x3c, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x47, 0x43, 0x45, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x0a, 0x0a, + 0x08, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x22, 0xa0, 0x01, 0x0a, 0x05, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x63, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x63, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x64, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x92, 0x01, 0x0a, + 0x0c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, + 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x12, 0x2c, 0x0a, 0x0a, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x09, 0x72, 0x61, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, + 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, + 0x70, 0x6d, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x22, 0xde, 0x01, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, + 0x73, 0x63, 0x72, 0x74, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x53, 0x63, 0x72, 0x74, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, + 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x67, 0x63, 0x65, 0x5f, 0x66, + 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x47, 0x63, + 0x65, 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x50, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x74, 0x65, 0x63, 0x68, + 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, + 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x22, 0x3c, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x32, 0x0a, 0x08, + 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x2a, 0x42, 0x0a, 0x19, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x08, 0x0a, + 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x44, 0x5f, 0x53, + 0x45, 0x56, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x56, 0x5f, + 0x45, 0x53, 0x10, 0x02, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x74, 0x70, 0x6d, + 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_attest_proto_rawDescOnce sync.Once + file_attest_proto_rawDescData = file_attest_proto_rawDesc +) + +func file_attest_proto_rawDescGZIP() []byte { + file_attest_proto_rawDescOnce.Do(func() { + file_attest_proto_rawDescData = protoimpl.X.CompressGZIP(file_attest_proto_rawDescData) + }) + return file_attest_proto_rawDescData +} + +var file_attest_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_attest_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_attest_proto_goTypes = []interface{}{ + (GCEConfidentialTechnology)(0), // 0: attest.GCEConfidentialTechnology + (*GCEInstanceInfo)(nil), // 1: attest.GCEInstanceInfo + (*Attestation)(nil), // 2: attest.Attestation + (*PlatformState)(nil), // 3: attest.PlatformState + (*Event)(nil), // 4: attest.Event + (*MachineState)(nil), // 5: attest.MachineState + (*PlatformPolicy)(nil), // 6: attest.PlatformPolicy + (*Policy)(nil), // 7: attest.Policy + (*tpm.Quote)(nil), // 8: tpm.Quote + (tpm.HashAlgo)(0), // 9: tpm.HashAlgo +} +var file_attest_proto_depIdxs = []int32{ + 8, // 0: attest.Attestation.quotes:type_name -> tpm.Quote + 1, // 1: attest.Attestation.instance_info:type_name -> attest.GCEInstanceInfo + 0, // 2: attest.PlatformState.technology:type_name -> attest.GCEConfidentialTechnology + 1, // 3: attest.PlatformState.instance_info:type_name -> attest.GCEInstanceInfo + 3, // 4: attest.MachineState.platform:type_name -> attest.PlatformState + 4, // 5: attest.MachineState.raw_events:type_name -> attest.Event + 9, // 6: attest.MachineState.hash:type_name -> tpm.HashAlgo + 0, // 7: attest.PlatformPolicy.minimum_technology:type_name -> attest.GCEConfidentialTechnology + 6, // 8: attest.Policy.platform:type_name -> attest.PlatformPolicy + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_attest_proto_init() } +func file_attest_proto_init() { + if File_attest_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_attest_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GCEInstanceInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Attestation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlatformState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MachineState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlatformPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_attest_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*PlatformState_ScrtmVersionId)(nil), + (*PlatformState_GceVersion)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_attest_proto_rawDesc, + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_attest_proto_goTypes, + DependencyIndexes: file_attest_proto_depIdxs, + EnumInfos: file_attest_proto_enumTypes, + MessageInfos: file_attest_proto_msgTypes, + }.Build() + File_attest_proto = out.File + file_attest_proto_rawDesc = nil + file_attest_proto_goTypes = nil + file_attest_proto_depIdxs = nil +} diff --git a/vendor/github.com/google/go-tpm-tools/proto/tpm/BUILD.bazel b/vendor/github.com/google/go-tpm-tools/proto/tpm/BUILD.bazel new file mode 100644 index 000000000000..df7073475564 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/proto/tpm/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["tpm.pb.go"], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm-tools/proto/tpm", + importpath = "github.com/google/go-tpm-tools/proto/tpm", + visibility = ["//visibility:public"], + deps = [ + "//vendor/google.golang.org/protobuf/reflect/protoreflect:go_default_library", + "//vendor/google.golang.org/protobuf/runtime/protoimpl:go_default_library", + ], +) diff --git a/vendor/github.com/google/go-tpm-tools/proto/tpm/tpm.pb.go b/vendor/github.com/google/go-tpm-tools/proto/tpm/tpm.pb.go new file mode 100644 index 000000000000..dd71194e1ef7 --- /dev/null +++ b/vendor/github.com/google/go-tpm-tools/proto/tpm/tpm.pb.go @@ -0,0 +1,595 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: tpm.proto + +package tpm + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Enum values come from TCG Algorithm Registry - v1.27 - Table 3 +type ObjectType int32 + +const ( + ObjectType_OBJECT_INVALID ObjectType = 0 + ObjectType_RSA ObjectType = 1 + ObjectType_ECC ObjectType = 35 +) + +// Enum value maps for ObjectType. +var ( + ObjectType_name = map[int32]string{ + 0: "OBJECT_INVALID", + 1: "RSA", + 35: "ECC", + } + ObjectType_value = map[string]int32{ + "OBJECT_INVALID": 0, + "RSA": 1, + "ECC": 35, + } +) + +func (x ObjectType) Enum() *ObjectType { + p := new(ObjectType) + *p = x + return p +} + +func (x ObjectType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ObjectType) Descriptor() protoreflect.EnumDescriptor { + return file_tpm_proto_enumTypes[0].Descriptor() +} + +func (ObjectType) Type() protoreflect.EnumType { + return &file_tpm_proto_enumTypes[0] +} + +func (x ObjectType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ObjectType.Descriptor instead. +func (ObjectType) EnumDescriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{0} +} + +type HashAlgo int32 + +const ( + HashAlgo_HASH_INVALID HashAlgo = 0 + HashAlgo_SHA1 HashAlgo = 4 + HashAlgo_SHA256 HashAlgo = 11 + HashAlgo_SHA384 HashAlgo = 12 + HashAlgo_SHA512 HashAlgo = 13 +) + +// Enum value maps for HashAlgo. +var ( + HashAlgo_name = map[int32]string{ + 0: "HASH_INVALID", + 4: "SHA1", + 11: "SHA256", + 12: "SHA384", + 13: "SHA512", + } + HashAlgo_value = map[string]int32{ + "HASH_INVALID": 0, + "SHA1": 4, + "SHA256": 11, + "SHA384": 12, + "SHA512": 13, + } +) + +func (x HashAlgo) Enum() *HashAlgo { + p := new(HashAlgo) + *p = x + return p +} + +func (x HashAlgo) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (HashAlgo) Descriptor() protoreflect.EnumDescriptor { + return file_tpm_proto_enumTypes[1].Descriptor() +} + +func (HashAlgo) Type() protoreflect.EnumType { + return &file_tpm_proto_enumTypes[1] +} + +func (x HashAlgo) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use HashAlgo.Descriptor instead. +func (HashAlgo) EnumDescriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{1} +} + +// SealedBytes stores the result of a TPM2_Seal. The private portion (priv) has +// already been encrypted and is no longer sensitive. The hash algorithm is +// assumed to be SHA256. +type SealedBytes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Priv []byte `protobuf:"bytes,1,opt,name=priv,proto3" json:"priv,omitempty"` + Pub []byte `protobuf:"bytes,2,opt,name=pub,proto3" json:"pub,omitempty"` + Pcrs []uint32 `protobuf:"varint,3,rep,packed,name=pcrs,proto3" json:"pcrs,omitempty"` + Hash HashAlgo `protobuf:"varint,4,opt,name=hash,proto3,enum=tpm.HashAlgo" json:"hash,omitempty"` + Srk ObjectType `protobuf:"varint,5,opt,name=srk,proto3,enum=tpm.ObjectType" json:"srk,omitempty"` + CertifiedPcrs *PCRs `protobuf:"bytes,6,opt,name=certified_pcrs,json=certifiedPcrs,proto3" json:"certified_pcrs,omitempty"` + CreationData []byte `protobuf:"bytes,7,opt,name=creation_data,json=creationData,proto3" json:"creation_data,omitempty"` + Ticket []byte `protobuf:"bytes,8,opt,name=ticket,proto3" json:"ticket,omitempty"` +} + +func (x *SealedBytes) Reset() { + *x = SealedBytes{} + if protoimpl.UnsafeEnabled { + mi := &file_tpm_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SealedBytes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SealedBytes) ProtoMessage() {} + +func (x *SealedBytes) ProtoReflect() protoreflect.Message { + mi := &file_tpm_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SealedBytes.ProtoReflect.Descriptor instead. +func (*SealedBytes) Descriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{0} +} + +func (x *SealedBytes) GetPriv() []byte { + if x != nil { + return x.Priv + } + return nil +} + +func (x *SealedBytes) GetPub() []byte { + if x != nil { + return x.Pub + } + return nil +} + +func (x *SealedBytes) GetPcrs() []uint32 { + if x != nil { + return x.Pcrs + } + return nil +} + +func (x *SealedBytes) GetHash() HashAlgo { + if x != nil { + return x.Hash + } + return HashAlgo_HASH_INVALID +} + +func (x *SealedBytes) GetSrk() ObjectType { + if x != nil { + return x.Srk + } + return ObjectType_OBJECT_INVALID +} + +func (x *SealedBytes) GetCertifiedPcrs() *PCRs { + if x != nil { + return x.CertifiedPcrs + } + return nil +} + +func (x *SealedBytes) GetCreationData() []byte { + if x != nil { + return x.CreationData + } + return nil +} + +func (x *SealedBytes) GetTicket() []byte { + if x != nil { + return x.Ticket + } + return nil +} + +type ImportBlob struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Duplicate []byte `protobuf:"bytes,1,opt,name=duplicate,proto3" json:"duplicate,omitempty"` + EncryptedSeed []byte `protobuf:"bytes,2,opt,name=encrypted_seed,json=encryptedSeed,proto3" json:"encrypted_seed,omitempty"` + PublicArea []byte `protobuf:"bytes,3,opt,name=public_area,json=publicArea,proto3" json:"public_area,omitempty"` + Pcrs *PCRs `protobuf:"bytes,4,opt,name=pcrs,proto3" json:"pcrs,omitempty"` +} + +func (x *ImportBlob) Reset() { + *x = ImportBlob{} + if protoimpl.UnsafeEnabled { + mi := &file_tpm_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportBlob) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportBlob) ProtoMessage() {} + +func (x *ImportBlob) ProtoReflect() protoreflect.Message { + mi := &file_tpm_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportBlob.ProtoReflect.Descriptor instead. +func (*ImportBlob) Descriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{1} +} + +func (x *ImportBlob) GetDuplicate() []byte { + if x != nil { + return x.Duplicate + } + return nil +} + +func (x *ImportBlob) GetEncryptedSeed() []byte { + if x != nil { + return x.EncryptedSeed + } + return nil +} + +func (x *ImportBlob) GetPublicArea() []byte { + if x != nil { + return x.PublicArea + } + return nil +} + +func (x *ImportBlob) GetPcrs() *PCRs { + if x != nil { + return x.Pcrs + } + return nil +} + +type Quote struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // TPM2 quote, encoded as a TPMS_ATTEST + Quote []byte `protobuf:"bytes,1,opt,name=quote,proto3" json:"quote,omitempty"` + // TPM2 signature, encoded as a TPMT_SIGNATURE + RawSig []byte `protobuf:"bytes,2,opt,name=raw_sig,json=rawSig,proto3" json:"raw_sig,omitempty"` + // PCR values of the bank being quoted + Pcrs *PCRs `protobuf:"bytes,3,opt,name=pcrs,proto3" json:"pcrs,omitempty"` +} + +func (x *Quote) Reset() { + *x = Quote{} + if protoimpl.UnsafeEnabled { + mi := &file_tpm_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Quote) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Quote) ProtoMessage() {} + +func (x *Quote) ProtoReflect() protoreflect.Message { + mi := &file_tpm_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Quote.ProtoReflect.Descriptor instead. +func (*Quote) Descriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{2} +} + +func (x *Quote) GetQuote() []byte { + if x != nil { + return x.Quote + } + return nil +} + +func (x *Quote) GetRawSig() []byte { + if x != nil { + return x.RawSig + } + return nil +} + +func (x *Quote) GetPcrs() *PCRs { + if x != nil { + return x.Pcrs + } + return nil +} + +type PCRs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash HashAlgo `protobuf:"varint,1,opt,name=hash,proto3,enum=tpm.HashAlgo" json:"hash,omitempty"` + Pcrs map[uint32][]byte `protobuf:"bytes,2,rep,name=pcrs,proto3" json:"pcrs,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PCRs) Reset() { + *x = PCRs{} + if protoimpl.UnsafeEnabled { + mi := &file_tpm_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PCRs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PCRs) ProtoMessage() {} + +func (x *PCRs) ProtoReflect() protoreflect.Message { + mi := &file_tpm_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PCRs.ProtoReflect.Descriptor instead. +func (*PCRs) Descriptor() ([]byte, []int) { + return file_tpm_proto_rawDescGZIP(), []int{3} +} + +func (x *PCRs) GetHash() HashAlgo { + if x != nil { + return x.Hash + } + return HashAlgo_HASH_INVALID +} + +func (x *PCRs) GetPcrs() map[uint32][]byte { + if x != nil { + return x.Pcrs + } + return nil +} + +var File_tpm_proto protoreflect.FileDescriptor + +var file_tpm_proto_rawDesc = []byte{ + 0x0a, 0x09, 0x74, 0x70, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x74, 0x70, 0x6d, + 0x22, 0xfc, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x61, 0x6c, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x72, 0x69, 0x76, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x70, 0x72, 0x69, 0x76, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x75, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x70, 0x75, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x63, 0x72, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x63, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x48, + 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, + 0x03, 0x73, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x74, 0x70, 0x6d, + 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x03, 0x73, 0x72, 0x6b, + 0x12, 0x30, 0x0a, 0x0e, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x70, 0x63, + 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x50, + 0x43, 0x52, 0x73, 0x52, 0x0d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x50, 0x63, + 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x69, 0x63, 0x6b, 0x65, + 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x22, + 0x91, 0x01, 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, + 0x0a, 0x09, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x53, + 0x65, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x72, + 0x65, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x41, 0x72, 0x65, 0x61, 0x12, 0x1d, 0x0a, 0x04, 0x70, 0x63, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x50, 0x43, 0x52, 0x73, 0x52, 0x04, 0x70, + 0x63, 0x72, 0x73, 0x22, 0x55, 0x0a, 0x05, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, 0x75, 0x6f, + 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x04, 0x70, + 0x63, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x74, 0x70, 0x6d, 0x2e, + 0x50, 0x43, 0x52, 0x73, 0x52, 0x04, 0x70, 0x63, 0x72, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x04, 0x50, + 0x43, 0x52, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x0d, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, + 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x04, 0x70, 0x63, 0x72, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x50, 0x43, 0x52, 0x73, 0x2e, + 0x50, 0x63, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x70, 0x63, 0x72, 0x73, 0x1a, + 0x37, 0x0a, 0x09, 0x50, 0x63, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x32, 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, + 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x53, + 0x41, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x43, 0x43, 0x10, 0x23, 0x2a, 0x4a, 0x0a, 0x08, + 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x12, 0x10, 0x0a, 0x0c, 0x48, 0x41, 0x53, 0x48, + 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x48, + 0x41, 0x31, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x0b, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x10, 0x0c, 0x12, 0x0a, 0x0a, 0x06, + 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0x0d, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, + 0x2d, 0x74, 0x70, 0x6d, 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x74, 0x70, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_tpm_proto_rawDescOnce sync.Once + file_tpm_proto_rawDescData = file_tpm_proto_rawDesc +) + +func file_tpm_proto_rawDescGZIP() []byte { + file_tpm_proto_rawDescOnce.Do(func() { + file_tpm_proto_rawDescData = protoimpl.X.CompressGZIP(file_tpm_proto_rawDescData) + }) + return file_tpm_proto_rawDescData +} + +var file_tpm_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_tpm_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_tpm_proto_goTypes = []interface{}{ + (ObjectType)(0), // 0: tpm.ObjectType + (HashAlgo)(0), // 1: tpm.HashAlgo + (*SealedBytes)(nil), // 2: tpm.SealedBytes + (*ImportBlob)(nil), // 3: tpm.ImportBlob + (*Quote)(nil), // 4: tpm.Quote + (*PCRs)(nil), // 5: tpm.PCRs + nil, // 6: tpm.PCRs.PcrsEntry +} +var file_tpm_proto_depIdxs = []int32{ + 1, // 0: tpm.SealedBytes.hash:type_name -> tpm.HashAlgo + 0, // 1: tpm.SealedBytes.srk:type_name -> tpm.ObjectType + 5, // 2: tpm.SealedBytes.certified_pcrs:type_name -> tpm.PCRs + 5, // 3: tpm.ImportBlob.pcrs:type_name -> tpm.PCRs + 5, // 4: tpm.Quote.pcrs:type_name -> tpm.PCRs + 1, // 5: tpm.PCRs.hash:type_name -> tpm.HashAlgo + 6, // 6: tpm.PCRs.pcrs:type_name -> tpm.PCRs.PcrsEntry + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_tpm_proto_init() } +func file_tpm_proto_init() { + if File_tpm_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_tpm_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SealedBytes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tpm_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportBlob); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tpm_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Quote); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tpm_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PCRs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_tpm_proto_rawDesc, + NumEnums: 2, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_tpm_proto_goTypes, + DependencyIndexes: file_tpm_proto_depIdxs, + EnumInfos: file_tpm_proto_enumTypes, + MessageInfos: file_tpm_proto_msgTypes, + }.Build() + File_tpm_proto = out.File + file_tpm_proto_rawDesc = nil + file_tpm_proto_goTypes = nil + file_tpm_proto_depIdxs = nil +} diff --git a/vendor/github.com/google/go-tpm/LICENSE b/vendor/github.com/google/go-tpm/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/vendor/github.com/google/go-tpm/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/google/go-tpm/tpm2/BUILD.bazel b/vendor/github.com/google/go-tpm/tpm2/BUILD.bazel new file mode 100644 index 000000000000..c5b51f7201cc --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "constants.go", + "error.go", + "kdf.go", + "open_other.go", + "open_windows.go", + "structures.go", + "tpm2.go", + ], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm/tpm2", + importpath = "github.com/google/go-tpm/tpm2", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/google/go-tpm/tpmutil:go_default_library", + ] + select({ + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/github.com/google/go-tpm/tpmutil/tbs:go_default_library", + ], + "//conditions:default": [], + }), +) diff --git a/vendor/github.com/google/go-tpm/tpm2/README.md b/vendor/github.com/google/go-tpm/tpm2/README.md new file mode 100644 index 000000000000..4d0ff8befa7d --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/README.md @@ -0,0 +1,35 @@ +# TPM 2.0 client library + +## Tests + +This library contains unit tests in `github.com/google/go-tpm/tpm2`, which just +tests that various encoding and error checking functions work correctly. It also +contains more comprehensive integration tests in +`github.com/google/go-tpm/tpm2/test`, which run actual commands on a TPM. + +By default, these integration tests are run against the +[`go-tpm-tools`](https://github.com/google/go-tpm-tools) +simulator, which is baesed on the +[Microsoft Reference TPM2 code](https://github.com/microsoft/ms-tpm-20-ref). To +run both the unit and integration tests, run (in this directory) +```bash +go test . ./test +``` + +These integration tests can also be run against a real TPM device. This is +slightly more complex as the tests often need to be built as a normal user and +then executed as root. For example, +```bash +# Build the test binary without running it +go test -c github.com/google/go-tpm/tpm2/test +# Execute the test binary as root +sudo ./test.test --tpm-path=/dev/tpmrm0 +``` +On Linux, The `--tpm-path` causes the integration tests to be run against a +real TPM located at that path (usually `/dev/tpmrm0` or `/dev/tpm0`). On Windows, the story is similar, execept that +the `--use-tbs` flag is used instead. + +Tip: if your TPM host is remote and you don't want to install Go on it, this +same two-step process can be used. The test binary can be copied to a remote +host and run without extra installation (as the test binary has very few +*runtime* dependancies). diff --git a/vendor/github.com/google/go-tpm/tpm2/constants.go b/vendor/github.com/google/go-tpm/tpm2/constants.go new file mode 100644 index 000000000000..716ea47198ce --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/constants.go @@ -0,0 +1,500 @@ +// 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 + +import ( + "crypto" + "crypto/elliptic" + "fmt" + "strings" + + // Register the relevant hash implementations to prevent a runtime failure. + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + + "github.com/google/go-tpm/tpmutil" +) + +var hashInfo = []struct { + alg Algorithm + hash crypto.Hash +}{ + {AlgSHA1, crypto.SHA1}, + {AlgSHA256, crypto.SHA256}, + {AlgSHA384, crypto.SHA384}, + {AlgSHA512, crypto.SHA512}, + {AlgSHA3_256, crypto.SHA3_256}, + {AlgSHA3_384, crypto.SHA3_384}, + {AlgSHA3_512, crypto.SHA3_512}, +} + +// MAX_DIGEST_BUFFER is the maximum size of []byte request or response fields. +// Typically used for chunking of big blobs of data (such as for hashing or +// encryption). +const maxDigestBuffer = 1024 + +// Algorithm represents a TPM_ALG_ID value. +type Algorithm uint16 + +// HashToAlgorithm looks up the TPM2 algorithm corresponding to the provided crypto.Hash +func HashToAlgorithm(hash crypto.Hash) (Algorithm, error) { + for _, info := range hashInfo { + if info.hash == hash { + return info.alg, nil + } + } + return AlgUnknown, fmt.Errorf("go hash algorithm #%d has no TPM2 algorithm", hash) +} + +// IsNull returns true if a is AlgNull or zero (unset). +func (a Algorithm) IsNull() bool { + return a == AlgNull || a == AlgUnknown +} + +// UsesCount returns true if a signature algorithm uses count value. +func (a Algorithm) UsesCount() bool { + return a == AlgECDAA +} + +// UsesHash returns true if the algorithm requires the use of a hash. +func (a Algorithm) UsesHash() bool { + return a == AlgOAEP +} + +// Hash returns a crypto.Hash based on the given TPM_ALG_ID. +// An error is returned if the given algorithm is not a hash algorithm or is not available. +func (a Algorithm) Hash() (crypto.Hash, error) { + for _, info := range hashInfo { + if info.alg == a { + if !info.hash.Available() { + return crypto.Hash(0), fmt.Errorf("go hash algorithm #%d not available", info.hash) + } + return info.hash, nil + } + } + return crypto.Hash(0), fmt.Errorf("hash algorithm not supported: 0x%x", a) +} + +// Supported Algorithms. +const ( + AlgUnknown Algorithm = 0x0000 + AlgRSA Algorithm = 0x0001 + AlgSHA1 Algorithm = 0x0004 + AlgHMAC Algorithm = 0x0005 + AlgAES Algorithm = 0x0006 + AlgKeyedHash Algorithm = 0x0008 + AlgXOR Algorithm = 0x000A + AlgSHA256 Algorithm = 0x000B + AlgSHA384 Algorithm = 0x000C + AlgSHA512 Algorithm = 0x000D + AlgNull Algorithm = 0x0010 + AlgRSASSA Algorithm = 0x0014 + AlgRSAES Algorithm = 0x0015 + AlgRSAPSS Algorithm = 0x0016 + AlgOAEP Algorithm = 0x0017 + AlgECDSA Algorithm = 0x0018 + AlgECDH Algorithm = 0x0019 + AlgECDAA Algorithm = 0x001A + AlgKDF2 Algorithm = 0x0021 + AlgECC Algorithm = 0x0023 + AlgSymCipher Algorithm = 0x0025 + AlgSHA3_256 Algorithm = 0x0027 + AlgSHA3_384 Algorithm = 0x0028 + AlgSHA3_512 Algorithm = 0x0029 + AlgCTR Algorithm = 0x0040 + AlgOFB Algorithm = 0x0041 + AlgCBC Algorithm = 0x0042 + AlgCFB Algorithm = 0x0043 + AlgECB Algorithm = 0x0044 +) + +// HandleType defines a type of handle. +type HandleType uint8 + +// Supported handle types +const ( + HandleTypePCR HandleType = 0x00 + HandleTypeNVIndex HandleType = 0x01 + HandleTypeHMACSession HandleType = 0x02 + HandleTypeLoadedSession HandleType = 0x02 + HandleTypePolicySession HandleType = 0x03 + HandleTypeSavedSession HandleType = 0x03 + HandleTypePermanent HandleType = 0x40 + HandleTypeTransient HandleType = 0x80 + HandleTypePersistent HandleType = 0x81 +) + +// SessionType defines the type of session created in StartAuthSession. +type SessionType uint8 + +// Supported session types. +const ( + SessionHMAC SessionType = 0x00 + SessionPolicy SessionType = 0x01 + SessionTrial SessionType = 0x03 +) + +// SessionAttributes represents an attribute of a session. +type SessionAttributes byte + +// Session Attributes (Structures 8.4 TPMA_SESSION) +const ( + AttrContinueSession SessionAttributes = 1 << iota + AttrAuditExclusive + AttrAuditReset + _ // bit 3 reserved + _ // bit 4 reserved + AttrDecrypt + AttrEcrypt + AttrAudit +) + +// EmptyAuth represents the empty authorization value. +var EmptyAuth []byte + +// KeyProp is a bitmask used in Attributes field of key templates. Individual +// flags should be OR-ed to form a full mask. +type KeyProp uint32 + +// Key properties. +const ( + FlagFixedTPM KeyProp = 0x00000002 + FlagStClear KeyProp = 0x00000004 + FlagFixedParent KeyProp = 0x00000010 + FlagSensitiveDataOrigin KeyProp = 0x00000020 + FlagUserWithAuth KeyProp = 0x00000040 + FlagAdminWithPolicy KeyProp = 0x00000080 + FlagNoDA KeyProp = 0x00000400 + FlagRestricted KeyProp = 0x00010000 + FlagDecrypt KeyProp = 0x00020000 + FlagSign KeyProp = 0x00040000 + + FlagSealDefault = FlagFixedTPM | FlagFixedParent + FlagSignerDefault = FlagSign | FlagRestricted | FlagFixedTPM | + FlagFixedParent | FlagSensitiveDataOrigin | FlagUserWithAuth + FlagStorageDefault = FlagDecrypt | FlagRestricted | FlagFixedTPM | + FlagFixedParent | FlagSensitiveDataOrigin | FlagUserWithAuth +) + +// TPMProp represents a Property Tag (TPM_PT) used with calls to GetCapability(CapabilityTPMProperties). +type TPMProp uint32 + +// TPM Capability Properties, see TPM 2.0 Spec, Rev 1.38, Table 23. +// Fixed TPM Properties (PT_FIXED) +const ( + FamilyIndicator TPMProp = 0x100 + iota + SpecLevel + SpecRevision + SpecDayOfYear + SpecYear + Manufacturer + VendorString1 + VendorString2 + VendorString3 + VendorString4 + VendorTPMType + FirmwareVersion1 + FirmwareVersion2 + InputMaxBufferSize + TransientObjectsMin + PersistentObjectsMin + LoadedObjectsMin + ActiveSessionsMax + PCRCount + PCRSelectMin + ContextGapMax + _ // (PT_FIXED + 21) is skipped + NVCountersMax + NVIndexMax + MemoryMethod + ClockUpdate + ContextHash + ContextSym + ContextSymSize + OrderlyCount + CommandMaxSize + ResponseMaxSize + DigestMaxSize + ObjectContextMaxSize + SessionContextMaxSize + PSFamilyIndicator + PSSpecLevel + PSSpecRevision + PSSpecDayOfYear + PSSpecYear + SplitSigningMax + TotalCommands + LibraryCommands + VendorCommands + NVMaxBufferSize + TPMModes + CapabilityMaxBufferSize +) + +// Variable TPM Properties (PT_VAR) +const ( + TPMAPermanent TPMProp = 0x200 + iota + TPMAStartupClear + HRNVIndex + HRLoaded + HRLoadedAvail + HRActive + HRActiveAvail + HRTransientAvail + CurrentPersistent + AvailPersistent + NVCounters + NVCountersAvail + AlgorithmSet + LoadedCurves + LockoutCounter + MaxAuthFail + LockoutInterval + LockoutRecovery + NVWriteRecovery + AuditCounter0 + AuditCounter1 +) + +// Allowed ranges of different kinds of Handles (TPM_HANDLE) +// These constants have type TPMProp for backwards compatibility. +const ( + PCRFirst TPMProp = 0x00000000 + HMACSessionFirst TPMProp = 0x02000000 + LoadedSessionFirst TPMProp = 0x02000000 + PolicySessionFirst TPMProp = 0x03000000 + ActiveSessionFirst TPMProp = 0x03000000 + TransientFirst TPMProp = 0x80000000 + PersistentFirst TPMProp = 0x81000000 + PersistentLast TPMProp = 0x81FFFFFF + PlatformPersistent TPMProp = 0x81800000 + NVIndexFirst TPMProp = 0x01000000 + NVIndexLast TPMProp = 0x01FFFFFF + PermanentFirst TPMProp = 0x40000000 + PermanentLast TPMProp = 0x4000010F +) + +// Reserved Handles. +const ( + HandleOwner tpmutil.Handle = 0x40000001 + iota + HandleRevoke + HandleTransport + HandleOperator + HandleAdmin + HandleEK + HandleNull + HandleUnassigned + HandlePasswordSession + HandleLockout + HandleEndorsement + HandlePlatform +) + +// Capability identifies some TPM property or state type. +type Capability uint32 + +// TPM Capabilities. +const ( + CapabilityAlgs Capability = iota + CapabilityHandles + CapabilityCommands + CapabilityPPCommands + CapabilityAuditCommands + CapabilityPCRs + CapabilityTPMProperties + CapabilityPCRProperties + CapabilityECCCurves + CapabilityAuthPolicies +) + +// TPM Structure Tags. Tags are used to disambiguate structures, similar to Alg +// values: tag value defines what kind of data lives in a nested field. +const ( + TagNull tpmutil.Tag = 0x8000 + TagNoSessions tpmutil.Tag = 0x8001 + TagSessions tpmutil.Tag = 0x8002 + TagAttestCertify tpmutil.Tag = 0x8017 + TagAttestQuote tpmutil.Tag = 0x8018 + TagAttestCreation tpmutil.Tag = 0x801a + TagHashCheck tpmutil.Tag = 0x8024 +) + +// StartupType instructs the TPM on how to handle its state during Shutdown or +// Startup. +type StartupType uint16 + +// Startup types +const ( + StartupClear StartupType = iota + StartupState +) + +// EllipticCurve identifies specific EC curves. +type EllipticCurve uint16 + +// ECC curves supported by TPM 2.0 spec. +const ( + CurveNISTP192 = EllipticCurve(iota + 1) + CurveNISTP224 + CurveNISTP256 + CurveNISTP384 + CurveNISTP521 + + CurveBNP256 = EllipticCurve(iota + 10) + CurveBNP638 + + CurveSM2P256 = EllipticCurve(0x0020) +) + +var toGoCurve = map[EllipticCurve]elliptic.Curve{ + CurveNISTP224: elliptic.P224(), + CurveNISTP256: elliptic.P256(), + CurveNISTP384: elliptic.P384(), + CurveNISTP521: elliptic.P521(), +} + +// Supported TPM operations. +const ( + CmdNVUndefineSpaceSpecial tpmutil.Command = 0x0000011F + CmdEvictControl tpmutil.Command = 0x00000120 + CmdUndefineSpace tpmutil.Command = 0x00000122 + CmdClear tpmutil.Command = 0x00000126 + CmdHierarchyChangeAuth tpmutil.Command = 0x00000129 + CmdDefineSpace tpmutil.Command = 0x0000012A + CmdCreatePrimary tpmutil.Command = 0x00000131 + CmdIncrementNVCounter tpmutil.Command = 0x00000134 + CmdWriteNV tpmutil.Command = 0x00000137 + CmdWriteLockNV tpmutil.Command = 0x00000138 + CmdDictionaryAttackLockReset tpmutil.Command = 0x00000139 + CmdDictionaryAttackParameters tpmutil.Command = 0x0000013A + CmdPCREvent tpmutil.Command = 0x0000013C + CmdSequenceComplete tpmutil.Command = 0x0000013E + CmdStartup tpmutil.Command = 0x00000144 + CmdShutdown tpmutil.Command = 0x00000145 + CmdActivateCredential tpmutil.Command = 0x00000147 + CmdCertify tpmutil.Command = 0x00000148 + CmdCertifyCreation tpmutil.Command = 0x0000014A + CmdReadNV tpmutil.Command = 0x0000014E + CmdReadLockNV tpmutil.Command = 0x0000014F + CmdPolicySecret tpmutil.Command = 0x00000151 + CmdCreate tpmutil.Command = 0x00000153 + CmdECDHZGen tpmutil.Command = 0x00000154 + CmdImport tpmutil.Command = 0x00000156 + CmdLoad tpmutil.Command = 0x00000157 + CmdQuote tpmutil.Command = 0x00000158 + CmdRSADecrypt tpmutil.Command = 0x00000159 + CmdSequenceUpdate tpmutil.Command = 0x0000015C + CmdSign tpmutil.Command = 0x0000015D + CmdUnseal tpmutil.Command = 0x0000015E + CmdContextLoad tpmutil.Command = 0x00000161 + CmdContextSave tpmutil.Command = 0x00000162 + CmdECDHKeyGen tpmutil.Command = 0x00000163 + CmdEncryptDecrypt tpmutil.Command = 0x00000164 + CmdFlushContext tpmutil.Command = 0x00000165 + CmdLoadExternal tpmutil.Command = 0x00000167 + CmdMakeCredential tpmutil.Command = 0x00000168 + CmdReadPublicNV tpmutil.Command = 0x00000169 + CmdPolicyCommandCode tpmutil.Command = 0x0000016C + CmdPolicyOr tpmutil.Command = 0x00000171 + CmdReadPublic tpmutil.Command = 0x00000173 + CmdRSAEncrypt tpmutil.Command = 0x00000174 + CmdStartAuthSession tpmutil.Command = 0x00000176 + CmdGetCapability tpmutil.Command = 0x0000017A + CmdGetRandom tpmutil.Command = 0x0000017B + CmdHash tpmutil.Command = 0x0000017D + CmdPCRRead tpmutil.Command = 0x0000017E + CmdPolicyPCR tpmutil.Command = 0x0000017F + CmdReadClock tpmutil.Command = 0x00000181 + CmdPCRExtend tpmutil.Command = 0x00000182 + CmdEventSequenceComplete tpmutil.Command = 0x00000185 + CmdHashSequenceStart tpmutil.Command = 0x00000186 + CmdPolicyGetDigest tpmutil.Command = 0x00000189 + CmdPolicyPassword tpmutil.Command = 0x0000018C + CmdEncryptDecrypt2 tpmutil.Command = 0x00000193 +) + +// Regular TPM 2.0 devices use 24-bit mask (3 bytes) for PCR selection. +const sizeOfPCRSelect = 3 + +const defaultRSAExponent = 1<<16 + 1 + +// NVAttr is a bitmask used in Attributes field of NV indexes. Individual +// flags should be OR-ed to form a full mask. +type NVAttr uint32 + +// NV Attributes +const ( + AttrPPWrite NVAttr = 0x00000001 + AttrOwnerWrite NVAttr = 0x00000002 + AttrAuthWrite NVAttr = 0x00000004 + AttrPolicyWrite NVAttr = 0x00000008 + AttrPolicyDelete NVAttr = 0x00000400 + AttrWriteLocked NVAttr = 0x00000800 + AttrWriteAll NVAttr = 0x00001000 + AttrWriteDefine NVAttr = 0x00002000 + AttrWriteSTClear NVAttr = 0x00004000 + AttrGlobalLock NVAttr = 0x00008000 + AttrPPRead NVAttr = 0x00010000 + AttrOwnerRead NVAttr = 0x00020000 + AttrAuthRead NVAttr = 0x00040000 + AttrPolicyRead NVAttr = 0x00080000 + AttrNoDA NVAttr = 0x02000000 + AttrOrderly NVAttr = 0x04000000 + AttrClearSTClear NVAttr = 0x08000000 + AttrReadLocked NVAttr = 0x10000000 + AttrWritten NVAttr = 0x20000000 + AttrPlatformCreate NVAttr = 0x40000000 + AttrReadSTClear NVAttr = 0x80000000 +) + +var permMap = map[NVAttr]string{ + AttrPPWrite: "PPWrite", + AttrOwnerWrite: "OwnerWrite", + AttrAuthWrite: "AuthWrite", + AttrPolicyWrite: "PolicyWrite", + AttrPolicyDelete: "PolicyDelete", + AttrWriteLocked: "WriteLocked", + AttrWriteAll: "WriteAll", + AttrWriteDefine: "WriteDefine", + AttrWriteSTClear: "WriteSTClear", + AttrGlobalLock: "GlobalLock", + AttrPPRead: "PPRead", + AttrOwnerRead: "OwnerRead", + AttrAuthRead: "AuthRead", + AttrPolicyRead: "PolicyRead", + AttrNoDA: "No Do", + AttrOrderly: "Oderly", + AttrClearSTClear: "ClearSTClear", + AttrReadLocked: "ReadLocked", + AttrWritten: "Writte", + AttrPlatformCreate: "PlatformCreate", + AttrReadSTClear: "ReadSTClear", +} + +// String returns a textual representation of the set of NVAttr +func (p NVAttr) String() string { + var retString strings.Builder + for iterator, item := range permMap { + if (p & iterator) != 0 { + retString.WriteString(item + " + ") + } + } + if retString.String() == "" { + return "Permission/s not found" + } + return strings.TrimSuffix(retString.String(), " + ") + +} diff --git a/vendor/github.com/google/go-tpm/tpm2/error.go b/vendor/github.com/google/go-tpm/tpm2/error.go new file mode 100644 index 000000000000..2bdcf3aadf3e --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/error.go @@ -0,0 +1,362 @@ +package tpm2 + +import ( + "fmt" + + "github.com/google/go-tpm/tpmutil" +) + +type ( + // RCFmt0 holds Format 0 error codes + RCFmt0 uint8 + + // RCFmt1 holds Format 1 error codes + RCFmt1 uint8 + + // RCWarn holds error codes used in warnings + RCWarn uint8 + + // RCIndex is used to reference arguments, handles and sessions in errors + RCIndex uint8 +) + +// Format 0 error codes. +const ( + RCInitialize RCFmt0 = 0x00 + RCFailure = 0x01 + RCSequence = 0x03 + RCPrivate = 0x0B + RCHMAC = 0x19 + RCDisabled = 0x20 + RCExclusive = 0x21 + RCAuthType = 0x24 + RCAuthMissing = 0x25 + RCPolicy = 0x26 + RCPCR = 0x27 + RCPCRChanged = 0x28 + RCUpgrade = 0x2D + RCTooManyContexts = 0x2E + RCAuthUnavailable = 0x2F + RCReboot = 0x30 + RCUnbalanced = 0x31 + RCCommandSize = 0x42 + RCCommandCode = 0x43 + RCAuthSize = 0x44 + RCAuthContext = 0x45 + RCNVRange = 0x46 + RCNVSize = 0x47 + RCNVLocked = 0x48 + RCNVAuthorization = 0x49 + RCNVUninitialized = 0x4A + RCNVSpace = 0x4B + RCNVDefined = 0x4C + RCBadContext = 0x50 + RCCPHash = 0x51 + RCParent = 0x52 + RCNeedsTest = 0x53 + RCNoResult = 0x54 + RCSensitive = 0x55 +) + +var fmt0Msg = map[RCFmt0]string{ + RCInitialize: "TPM not initialized by TPM2_Startup or already initialized", + RCFailure: "commands not being accepted because of a TPM failure", + RCSequence: "improper use of a sequence handle", + RCPrivate: "not currently used", + RCHMAC: "not currently used", + RCDisabled: "the command is disabled", + RCExclusive: "command failed because audit sequence required exclusivity", + RCAuthType: "authorization handle is not correct for command", + RCAuthMissing: "5 command requires an authorization session for handle and it is not present", + RCPolicy: "policy failure in math operation or an invalid authPolicy value", + RCPCR: "PCR check fail", + RCPCRChanged: "PCR have changed since checked", + RCUpgrade: "TPM is in field upgrade mode unless called via TPM2_FieldUpgradeData(), then it is not in field upgrade mode", + RCTooManyContexts: "context ID counter is at maximum", + RCAuthUnavailable: "authValue or authPolicy is not available for selected entity", + RCReboot: "a _TPM_Init and Startup(CLEAR) is required before the TPM can resume operation", + RCUnbalanced: "the protection algorithms (hash and symmetric) are not reasonably balanced; the digest size of the hash must be larger than the key size of the symmetric algorithm", + RCCommandSize: "command commandSize value is inconsistent with contents of the command buffer; either the size is not the same as the octets loaded by the hardware interface layer or the value is not large enough to hold a command header", + RCCommandCode: "command code not supported", + RCAuthSize: "the value of authorizationSize is out of range or the number of octets in the Authorization Area is greater than required", + RCAuthContext: "use of an authorization session with a context command or another command that cannot have an authorization session", + RCNVRange: "NV offset+size is out of range", + RCNVSize: "Requested allocation size is larger than allowed", + RCNVLocked: "NV access locked", + RCNVAuthorization: "NV access authorization fails in command actions", + RCNVUninitialized: "an NV Index is used before being initialized or the state saved by TPM2_Shutdown(STATE) could not be restored", + RCNVSpace: "insufficient space for NV allocation", + RCNVDefined: "NV Index or persistent object already defined", + RCBadContext: "context in TPM2_ContextLoad() is not valid", + RCCPHash: "cpHash value already set or not correct for use", + RCParent: "handle for parent is not a valid parent", + RCNeedsTest: "some function needs testing", + RCNoResult: "returned when an internal function cannot process a request due to an unspecified problem; this code is usually related to invalid parameters that are not properly filtered by the input unmarshaling code", + RCSensitive: "the sensitive area did not unmarshal correctly after decryption", +} + +// Format 1 error codes. +const ( + RCAsymmetric = 0x01 + RCAttributes = 0x02 + RCHash = 0x03 + RCValue = 0x04 + RCHierarchy = 0x05 + RCKeySize = 0x07 + RCMGF = 0x08 + RCMode = 0x09 + RCType = 0x0A + RCHandle = 0x0B + RCKDF = 0x0C + RCRange = 0x0D + RCAuthFail = 0x0E + RCNonce = 0x0F + RCPP = 0x10 + RCScheme = 0x12 + RCSize = 0x15 + RCSymmetric = 0x16 + RCTag = 0x17 + RCSelector = 0x18 + RCInsufficient = 0x1A + RCSignature = 0x1B + RCKey = 0x1C + RCPolicyFail = 0x1D + RCIntegrity = 0x1F + RCTicket = 0x20 + RCReservedBits = 0x21 + RCBadAuth = 0x22 + RCExpired = 0x23 + RCPolicyCC = 0x24 + RCBinding = 0x25 + RCCurve = 0x26 + RCECCPoint = 0x27 +) + +var fmt1Msg = map[RCFmt1]string{ + RCAsymmetric: "asymmetric algorithm not supported or not correct", + RCAttributes: "inconsistent attributes", + RCHash: "hash algorithm not supported or not appropriate", + RCValue: "value is out of range or is not correct for the context", + RCHierarchy: "hierarchy is not enabled or is not correct for the use", + RCKeySize: "key size is not supported", + RCMGF: "mask generation function not supported", + RCMode: "mode of operation not supported", + RCType: "the type of the value is not appropriate for the use", + RCHandle: "the handle is not correct for the use", + RCKDF: "unsupported key derivation function or function not appropriate for use", + RCRange: "value was out of allowed range", + RCAuthFail: "the authorization HMAC check failed and DA counter incremented", + RCNonce: "invalid nonce size or nonce value mismatch", + RCPP: "authorization requires assertion of PP", + RCScheme: "unsupported or incompatible scheme", + RCSize: "structure is the wrong size", + RCSymmetric: "unsupported symmetric algorithm or key size, or not appropriate for instance", + RCTag: "incorrect structure tag", + RCSelector: "union selector is incorrect", + RCInsufficient: "the TPM was unable to unmarshal a value because there were not enough octets in the input buffer", + RCSignature: "the signature is not valid", + RCKey: "key fields are not compatible with the selected use", + RCPolicyFail: "a policy check failed", + RCIntegrity: "integrity check failed", + RCTicket: "invalid ticket", + RCReservedBits: "reserved bits not set to zero as required", + RCBadAuth: "authorization failure without DA implications", + RCExpired: "the policy has expired", + RCPolicyCC: "the commandCode in the policy is not the commandCode of the command or the command code in a policy command references a command that is not implemented", + RCBinding: "public and sensitive portions of an object are not cryptographically bound", + RCCurve: "curve not supported", + RCECCPoint: "point is not on the required curve", +} + +// Warning codes. +const ( + RCContextGap RCWarn = 0x01 + RCObjectMemory = 0x02 + RCSessionMemory = 0x03 + RCMemory = 0x04 + RCSessionHandles = 0x05 + RCObjectHandles = 0x06 + RCLocality = 0x07 + RCYielded = 0x08 + RCCanceled = 0x09 + RCTesting = 0x0A + RCReferenceH0 = 0x10 + RCReferenceH1 = 0x11 + RCReferenceH2 = 0x12 + RCReferenceH3 = 0x13 + RCReferenceH4 = 0x14 + RCReferenceH5 = 0x15 + RCReferenceH6 = 0x16 + RCReferenceS0 = 0x18 + RCReferenceS1 = 0x19 + RCReferenceS2 = 0x1A + RCReferenceS3 = 0x1B + RCReferenceS4 = 0x1C + RCReferenceS5 = 0x1D + RCReferenceS6 = 0x1E + RCNVRate = 0x20 + RCLockout = 0x21 + RCRetry = 0x22 + RCNVUnavailable = 0x23 +) + +var warnMsg = map[RCWarn]string{ + RCContextGap: "gap for context ID is too large", + RCObjectMemory: "out of memory for object contexts", + RCSessionMemory: "out of memory for session contexts", + RCMemory: "out of shared object/session memory or need space for internal operations", + RCSessionHandles: "out of session handles", + RCObjectHandles: "out of object handles", + RCLocality: "bad locality", + RCYielded: "the TPM has suspended operation on the command; forward progress was made and the command may be retried", + RCCanceled: "the command was canceled", + RCTesting: "TPM is performing self-tests", + RCReferenceH0: "the 1st handle in the handle area references a transient object or session that is not loaded", + RCReferenceH1: "the 2nd handle in the handle area references a transient object or session that is not loaded", + RCReferenceH2: "the 3rd handle in the handle area references a transient object or session that is not loaded", + RCReferenceH3: "the 4th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH4: "the 5th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH5: "the 6th handle in the handle area references a transient object or session that is not loaded", + RCReferenceH6: "the 7th handle in the handle area references a transient object or session that is not loaded", + RCReferenceS0: "the 1st authorization session handle references a session that is not loaded", + RCReferenceS1: "the 2nd authorization session handle references a session that is not loaded", + RCReferenceS2: "the 3rd authorization session handle references a session that is not loaded", + RCReferenceS3: "the 4th authorization session handle references a session that is not loaded", + RCReferenceS4: "the 5th authorization session handle references a session that is not loaded", + RCReferenceS5: "the 6th authorization session handle references a session that is not loaded", + RCReferenceS6: "the 7th authorization session handle references a session that is not loaded", + RCNVRate: "the TPM is rate-limiting accesses to prevent wearout of NV", + RCLockout: "authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode", + RCRetry: "the TPM was not able to start the command", + RCNVUnavailable: "the command may require writing of NV and NV is not current accessible", +} + +// Indexes for arguments, handles and sessions. +const ( + RC1 RCIndex = iota + 1 + RC2 + RC3 + RC4 + RC5 + RC6 + RC7 + RC8 + RC9 + RCA + RCB + RCC + RCD + RCE + RCF +) + +const unknownCode = "unknown error code" + +// Error is returned for all Format 0 errors from the TPM. It is used for general +// errors not specific to a parameter, handle or session. +type Error struct { + Code RCFmt0 +} + +func (e Error) Error() string { + msg := fmt0Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("error code 0x%x : %s", e.Code, msg) +} + +// VendorError represents a vendor-specific error response. These types of responses +// are not decoded and Code contains the complete response code. +type VendorError struct { + Code uint32 +} + +func (e VendorError) Error() string { + return fmt.Sprintf("vendor error code 0x%x", e.Code) +} + +// Warning is typically used to report transient errors. +type Warning struct { + Code RCWarn +} + +func (w Warning) Error() string { + msg := warnMsg[w.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("warning code 0x%x : %s", w.Code, msg) +} + +// ParameterError describes an error related to a parameter, and the parameter number. +type ParameterError struct { + Code RCFmt1 + Parameter RCIndex +} + +func (e ParameterError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("parameter %d, error code 0x%x : %s", e.Parameter, e.Code, msg) +} + +// HandleError describes an error related to a handle, and the handle number. +type HandleError struct { + Code RCFmt1 + Handle RCIndex +} + +func (e HandleError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("handle %d, error code 0x%x : %s", e.Handle, e.Code, msg) +} + +// SessionError describes an error related to a session, and the session number. +type SessionError struct { + Code RCFmt1 + Session RCIndex +} + +func (e SessionError) Error() string { + msg := fmt1Msg[e.Code] + if msg == "" { + msg = unknownCode + } + return fmt.Sprintf("session %d, error code 0x%x : %s", e.Session, e.Code, msg) +} + +// Decode a TPM2 response code and return the appropriate error. Logic +// according to the "Response Code Evaluation" chart in Part 1 of the TPM 2.0 +// spec. +func decodeResponse(code tpmutil.ResponseCode) error { + if code == tpmutil.RCSuccess { + return nil + } + if code&0x180 == 0 { // Bits 7:8 == 0 is a TPM1 error + return fmt.Errorf("response status 0x%x", code) + } + if code&0x80 == 0 { // Bit 7 unset + if code&0x400 > 0 { // Bit 10 set, vendor specific code + return VendorError{uint32(code)} + } + if code&0x800 > 0 { // Bit 11 set, warning with code in bit 0:6 + return Warning{RCWarn(code & 0x7f)} + } + // error with code in bit 0:6 + return Error{RCFmt0(code & 0x7f)} + } + if code&0x40 > 0 { // Bit 6 set, code in 0:5, parameter number in 8:11 + return ParameterError{RCFmt1(code & 0x3f), RCIndex((code & 0xf00) >> 8)} + } + if code&0x800 == 0 { // Bit 11 unset, code in 0:5, handle in 8:10 + return HandleError{RCFmt1(code & 0x3f), RCIndex((code & 0x700) >> 8)} + } + // Code in 0:5, Session in 8:10 + return SessionError{RCFmt1(code & 0x3f), RCIndex((code & 0x700) >> 8)} +} diff --git a/vendor/github.com/google/go-tpm/tpm2/kdf.go b/vendor/github.com/google/go-tpm/tpm2/kdf.go new file mode 100644 index 000000000000..846cf346d57f --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/kdf.go @@ -0,0 +1,89 @@ +// 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 + +import ( + "crypto/hmac" + "encoding/binary" + "hash" +) + +// 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. +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 + } + mac := hmac.New(h.New, key) + + out := kdf(mac, bits, func() { + mac.Write([]byte(label)) + mac.Write([]byte{0}) // Terminating null character for C-string. + mac.Write(contextU) + mac.Write(contextV) + binary.Write(mac, binary.BigEndian, uint32(bits)) + }) + return out, nil +} + +// 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 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. +func KDFe(hashAlg Algorithm, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) ([]byte, error) { + createHash, 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 +} + +func kdf(h hash.Hash, bits int, update func()) []byte { + bytes := (bits + 7) / 8 + out := []byte{} + + for counter := 1; len(out) < bytes; counter++ { + h.Reset() + binary.Write(h, binary.BigEndian, uint32(counter)) + update() + + 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 = out[:bytes] + + // As mentioned in the KDFa and KDFe specs mentioned above, + // the unused bits of the most significant octet are masked off. + if maskBits := uint8(bits % 8); maskBits > 0 { + out[0] &= (1 << maskBits) - 1 + } + return out +} diff --git a/vendor/github.com/google/go-tpm/tpm2/open_other.go b/vendor/github.com/google/go-tpm/tpm2/open_other.go new file mode 100644 index 000000000000..759ea97c7211 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/open_other.go @@ -0,0 +1,42 @@ +// +build !windows + +// Copyright (c) 2019, 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 + +import ( + "fmt" + "io" + + "github.com/google/go-tpm/tpmutil" +) + +// 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. +func OpenTPM(path string) (io.ReadWriteCloser, error) { + rwc, err := tpmutil.OpenTPM(path) + if err != nil { + return nil, err + } + + // Make sure this is a TPM 2.0 + _, err = GetManufacturer(rwc) + if err != nil { + rwc.Close() + return nil, fmt.Errorf("open %s: device is not a TPM 2.0", path) + } + return rwc, nil +} diff --git a/vendor/github.com/google/go-tpm/tpm2/open_windows.go b/vendor/github.com/google/go-tpm/tpm2/open_windows.go new file mode 100644 index 000000000000..c243fa78d727 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/open_windows.go @@ -0,0 +1,37 @@ +// 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 + +import ( + "fmt" + "io" + + "github.com/google/go-tpm/tpmutil" + "github.com/google/go-tpm/tpmutil/tbs" +) + +// OpenTPM opens a channel to the TPM. +func OpenTPM() (io.ReadWriteCloser, error) { + info, err := tbs.GetDeviceInfo() + if err != nil { + return nil, err + } + + if info.TPMVersion != tbs.TPMVersion20 { + return nil, fmt.Errorf("openTPM: device is not a TPM 2.0") + } + + return tpmutil.OpenTPM() +} diff --git a/vendor/github.com/google/go-tpm/tpm2/structures.go b/vendor/github.com/google/go-tpm/tpm2/structures.go new file mode 100644 index 000000000000..4040a23a6417 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/structures.go @@ -0,0 +1,1063 @@ +// 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 + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "encoding/binary" + "errors" + "fmt" + "math/big" + "reflect" + + "github.com/google/go-tpm/tpmutil" +) + +// NVPublic contains the public area of an NV index. +type NVPublic struct { + NVIndex tpmutil.Handle + NameAlg Algorithm + Attributes NVAttr + AuthPolicy tpmutil.U16Bytes + DataSize uint16 +} + +type tpmsSensitiveCreate struct { + UserAuth tpmutil.U16Bytes + Data tpmutil.U16Bytes +} + +// PCRSelection contains a slice of PCR indexes and a hash algorithm used in +// them. +type PCRSelection struct { + Hash Algorithm + PCRs []int +} + +type tpmsPCRSelection struct { + Hash Algorithm + Size byte + PCRs tpmutil.RawBytes +} + +// Public contains the public area of an object. +type Public struct { + Type Algorithm + NameAlg Algorithm + Attributes KeyProp + AuthPolicy tpmutil.U16Bytes + + // Exactly one of the following fields should be set + // When encoding/decoding, one will be picked based on Type. + RSAParameters *RSAParams + ECCParameters *ECCParams + SymCipherParameters *SymCipherParams + KeyedHashParameters *KeyedHashParams +} + +// Encode serializes a Public structure in TPM wire format. +func (p Public) Encode() ([]byte, error) { + head, err := tpmutil.Pack(p.Type, p.NameAlg, p.Attributes, p.AuthPolicy) + if err != nil { + return nil, fmt.Errorf("encoding Type, NameAlg, Attributes, AuthPolicy: %v", err) + } + var params []byte + switch p.Type { + case AlgRSA: + params, err = p.RSAParameters.encode() + case AlgKeyedHash: + params, err = p.KeyedHashParameters.encode() + case AlgECC: + params, err = p.ECCParameters.encode() + case AlgSymCipher: + params, err = p.SymCipherParameters.encode() + default: + err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", p.Type) + } + if err != nil { + return nil, fmt.Errorf("encoding RSAParameters, ECCParameters, SymCipherParameters or KeyedHash: %v", err) + } + return concat(head, params) +} + +// Key returns the (public) key from the public area of an object. +func (p Public) Key() (crypto.PublicKey, error) { + var pubKey crypto.PublicKey + switch p.Type { + case AlgRSA: + // Endianness of big.Int.Bytes/SetBytes and modulus in the TPM is the same + // (big-endian). + pubKey = &rsa.PublicKey{N: p.RSAParameters.Modulus(), E: int(p.RSAParameters.Exponent())} + case AlgECC: + curve, ok := toGoCurve[p.ECCParameters.CurveID] + if !ok { + return nil, fmt.Errorf("can't map TPM EC curve ID 0x%x to Go elliptic.Curve value", p.ECCParameters.CurveID) + } + pubKey = &ecdsa.PublicKey{ + X: p.ECCParameters.Point.X(), + Y: p.ECCParameters.Point.Y(), + Curve: curve, + } + default: + return nil, fmt.Errorf("unsupported public key type 0x%x", p.Type) + } + return pubKey, nil +} + +// Name computes the Digest-based Name from the public area of an object. +func (p Public) Name() (Name, error) { + pubEncoded, err := p.Encode() + if err != nil { + return Name{}, err + } + hash, err := p.NameAlg.Hash() + if err != nil { + return Name{}, err + } + nameHash := hash.New() + nameHash.Write(pubEncoded) + return Name{ + Digest: &HashValue{ + Alg: p.NameAlg, + Value: nameHash.Sum(nil), + }, + }, nil +} + +// MatchesTemplate checks if the Public area has the same algorithms and +// parameters as the provided template. Note that this does not necessarily +// mean that the key was created from this template, as the Unique field is +// both provided in the template and overriden in the key creation process. +func (p Public) MatchesTemplate(template Public) bool { + if p.Type != template.Type || + p.NameAlg != template.NameAlg || + p.Attributes != template.Attributes || + !bytes.Equal(p.AuthPolicy, template.AuthPolicy) { + return false + } + switch p.Type { + case AlgRSA: + return p.RSAParameters.matchesTemplate(template.RSAParameters) + case AlgECC: + return p.ECCParameters.matchesTemplate(template.ECCParameters) + case AlgSymCipher: + return p.SymCipherParameters.matchesTemplate(template.SymCipherParameters) + case AlgKeyedHash: + return p.KeyedHashParameters.matchesTemplate(template.KeyedHashParameters) + default: + return true + } +} + +// DecodePublic decodes a TPMT_PUBLIC message. No error is returned if +// the input has extra trailing data. +func DecodePublic(buf []byte) (Public, error) { + in := bytes.NewBuffer(buf) + var pub Public + var err error + if err = tpmutil.UnpackBuf(in, &pub.Type, &pub.NameAlg, &pub.Attributes, &pub.AuthPolicy); err != nil { + return pub, fmt.Errorf("decoding TPMT_PUBLIC: %v", err) + } + + switch pub.Type { + case AlgRSA: + pub.RSAParameters, err = decodeRSAParams(in) + case AlgECC: + pub.ECCParameters, err = decodeECCParams(in) + case AlgSymCipher: + pub.SymCipherParameters, err = decodeSymCipherParams(in) + case AlgKeyedHash: + pub.KeyedHashParameters, err = decodeKeyedHashParams(in) + default: + err = fmt.Errorf("unsupported type in TPMT_PUBLIC: 0x%x", pub.Type) + } + return pub, err +} + +// RSAParams represents parameters of an RSA key pair. +// +// Symmetric and Sign may be nil, depending on key Attributes in Public. +// +// ExponentRaw and ModulusRaw are the actual data encoded in the template, which +// is useful for templates that differ in zero-padding, for example. +type RSAParams struct { + Symmetric *SymScheme + Sign *SigScheme + KeyBits uint16 + ExponentRaw uint32 + ModulusRaw tpmutil.U16Bytes +} + +// Exponent returns the RSA exponent value represented by ExponentRaw, handling +// the fact that an exponent of 0 represents a value of 65537 (2^16 + 1). +func (p *RSAParams) Exponent() uint32 { + if p.ExponentRaw == 0 { + return defaultRSAExponent + } + return p.ExponentRaw +} + +// Modulus returns the RSA modulus value represented by ModulusRaw, handling the +// fact that the same modulus value can have multiple different representations. +func (p *RSAParams) Modulus() *big.Int { + return new(big.Int).SetBytes(p.ModulusRaw) +} + +func (p *RSAParams) matchesTemplate(t *RSAParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) && + reflect.DeepEqual(p.Sign, t.Sign) && + p.KeyBits == t.KeyBits && p.ExponentRaw == t.ExponentRaw +} + +func (p *RSAParams) encode() ([]byte, error) { + if p == nil { + return nil, nil + } + sym, err := p.Symmetric.encode() + if err != nil { + return nil, fmt.Errorf("encoding Symmetric: %v", err) + } + sig, err := p.Sign.encode() + if err != nil { + return nil, fmt.Errorf("encoding Sign: %v", err) + } + rest, err := tpmutil.Pack(p.KeyBits, p.ExponentRaw, p.ModulusRaw) + if err != nil { + return nil, fmt.Errorf("encoding KeyBits, Exponent, Modulus: %v", err) + } + return concat(sym, sig, rest) +} + +func decodeRSAParams(in *bytes.Buffer) (*RSAParams, error) { + var params RSAParams + var err error + + if params.Symmetric, err = decodeSymScheme(in); err != nil { + return nil, fmt.Errorf("decoding Symmetric: %v", err) + } + if params.Sign, err = decodeSigScheme(in); err != nil { + return nil, fmt.Errorf("decoding Sign: %v", err) + } + if err := tpmutil.UnpackBuf(in, ¶ms.KeyBits, ¶ms.ExponentRaw, ¶ms.ModulusRaw); err != nil { + return nil, fmt.Errorf("decoding KeyBits, Exponent, Modulus: %v", err) + } + return ¶ms, nil +} + +// ECCParams represents parameters of an ECC key pair. +// +// Symmetric, Sign and KDF may be nil, depending on key Attributes in Public. +type ECCParams struct { + Symmetric *SymScheme + Sign *SigScheme + CurveID EllipticCurve + KDF *KDFScheme + Point ECPoint +} + +// ECPoint represents a ECC coordinates for a point using byte buffers. +type ECPoint struct { + XRaw, YRaw tpmutil.U16Bytes +} + +// X returns the X Point value reprsented by XRaw. +func (p ECPoint) X() *big.Int { + return new(big.Int).SetBytes(p.XRaw) +} + +// Y returns the Y Point value reprsented by YRaw. +func (p ECPoint) Y() *big.Int { + return new(big.Int).SetBytes(p.YRaw) +} + +func (p *ECCParams) matchesTemplate(t *ECCParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) && + reflect.DeepEqual(p.Sign, t.Sign) && + p.CurveID == t.CurveID && reflect.DeepEqual(p.KDF, t.KDF) +} + +func (p *ECCParams) encode() ([]byte, error) { + if p == nil { + return nil, nil + } + sym, err := p.Symmetric.encode() + if err != nil { + return nil, fmt.Errorf("encoding Symmetric: %v", err) + } + sig, err := p.Sign.encode() + if err != nil { + return nil, fmt.Errorf("encoding Sign: %v", err) + } + curve, err := tpmutil.Pack(p.CurveID) + if err != nil { + return nil, fmt.Errorf("encoding CurveID: %v", err) + } + kdf, err := p.KDF.encode() + if err != nil { + return nil, fmt.Errorf("encoding KDF: %v", err) + } + point, err := tpmutil.Pack(p.Point.XRaw, p.Point.YRaw) + if err != nil { + return nil, fmt.Errorf("encoding Point: %v", err) + } + return concat(sym, sig, curve, kdf, point) +} + +func decodeECCParams(in *bytes.Buffer) (*ECCParams, error) { + var params ECCParams + var err error + + if params.Symmetric, err = decodeSymScheme(in); err != nil { + return nil, fmt.Errorf("decoding Symmetric: %v", err) + } + if params.Sign, err = decodeSigScheme(in); err != nil { + return nil, fmt.Errorf("decoding Sign: %v", err) + } + if err := tpmutil.UnpackBuf(in, ¶ms.CurveID); err != nil { + return nil, fmt.Errorf("decoding CurveID: %v", err) + } + if params.KDF, err = decodeKDFScheme(in); err != nil { + return nil, fmt.Errorf("decoding KDF: %v", err) + } + if err := tpmutil.UnpackBuf(in, ¶ms.Point.XRaw, ¶ms.Point.YRaw); err != nil { + return nil, fmt.Errorf("decoding Point: %v", err) + } + return ¶ms, nil +} + +// SymCipherParams represents parameters of a symmetric block cipher TPM object. +type SymCipherParams struct { + Symmetric *SymScheme + Unique tpmutil.U16Bytes +} + +func (p *SymCipherParams) matchesTemplate(t *SymCipherParams) bool { + return reflect.DeepEqual(p.Symmetric, t.Symmetric) +} + +func (p *SymCipherParams) encode() ([]byte, error) { + sym, err := p.Symmetric.encode() + if err != nil { + return nil, fmt.Errorf("encoding Symmetric: %v", err) + } + unique, err := tpmutil.Pack(p.Unique) + if err != nil { + return nil, fmt.Errorf("encoding Unique: %v", err) + } + return concat(sym, unique) +} + +func decodeSymCipherParams(in *bytes.Buffer) (*SymCipherParams, error) { + var params SymCipherParams + var err error + + if params.Symmetric, err = decodeSymScheme(in); err != nil { + return nil, fmt.Errorf("decoding Symmetric: %v", err) + } + if err := tpmutil.UnpackBuf(in, ¶ms.Unique); err != nil { + return nil, fmt.Errorf("decoding Unique: %v", err) + } + return ¶ms, nil +} + +// KeyedHashParams represents parameters of a keyed hash TPM object. +type KeyedHashParams struct { + Alg Algorithm + Hash Algorithm + KDF Algorithm + Unique tpmutil.U16Bytes +} + +func (p *KeyedHashParams) matchesTemplate(t *KeyedHashParams) bool { + if p.Alg != t.Alg { + return false + } + switch p.Alg { + case AlgHMAC: + return p.Hash == t.Hash + case AlgXOR: + return p.Hash == t.Hash && p.KDF == t.KDF + default: + return true + } +} + +func (p *KeyedHashParams) encode() ([]byte, error) { + if p == nil { + return tpmutil.Pack(AlgNull, tpmutil.U16Bytes(nil)) + } + var params []byte + var err error + switch p.Alg { + case AlgNull: + params, err = tpmutil.Pack(p.Alg) + case AlgHMAC: + params, err = tpmutil.Pack(p.Alg, p.Hash) + case AlgXOR: + params, err = tpmutil.Pack(p.Alg, p.Hash, p.KDF) + default: + err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg) + } + if err != nil { + return nil, fmt.Errorf("encoding Alg Params: %v", err) + } + unique, err := tpmutil.Pack(p.Unique) + if err != nil { + return nil, fmt.Errorf("encoding Unique: %v", err) + } + return concat(params, unique) +} + +func decodeKeyedHashParams(in *bytes.Buffer) (*KeyedHashParams, error) { + var p KeyedHashParams + var err error + if err = tpmutil.UnpackBuf(in, &p.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + switch p.Alg { + case AlgNull: + err = nil + case AlgHMAC: + err = tpmutil.UnpackBuf(in, &p.Hash) + case AlgXOR: + err = tpmutil.UnpackBuf(in, &p.Hash, &p.KDF) + default: + err = fmt.Errorf("unsupported KeyedHash Algorithm: 0x%x", p.Alg) + } + if err != nil { + return nil, fmt.Errorf("decoding Alg Params: %v", err) + } + if err = tpmutil.UnpackBuf(in, &p.Unique); err != nil { + return nil, fmt.Errorf("decoding Unique: %v", err) + } + return &p, nil +} + +// SymScheme represents a symmetric encryption scheme. +// Known in the specification by TPMT_SYM_DEF_OBJECT. +type SymScheme struct { + Alg Algorithm + KeyBits uint16 + Mode Algorithm +} + +func (s *SymScheme) encode() ([]byte, error) { + if s == nil || s.Alg.IsNull() { + return tpmutil.Pack(AlgNull) + } + return tpmutil.Pack(s.Alg, s.KeyBits, s.Mode) +} + +func decodeSymScheme(in *bytes.Buffer) (*SymScheme, error) { + var scheme SymScheme + if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + if scheme.Alg == AlgNull { + return nil, nil + } + if err := tpmutil.UnpackBuf(in, &scheme.KeyBits, &scheme.Mode); err != nil { + return nil, fmt.Errorf("decoding KeyBits, Mode: %v", err) + } + return &scheme, nil +} + +// AsymScheme represents am asymmetric encryption scheme. +type AsymScheme struct { + Alg Algorithm + Hash Algorithm +} + +func (s *AsymScheme) encode() ([]byte, error) { + if s == nil || s.Alg.IsNull() { + return tpmutil.Pack(AlgNull) + } + if s.Alg.UsesHash() { + return tpmutil.Pack(s.Alg, s.Hash) + } + return tpmutil.Pack(s.Alg) +} + +// SigScheme represents a signing scheme. +type SigScheme struct { + Alg Algorithm + Hash Algorithm + Count uint32 +} + +func (s *SigScheme) encode() ([]byte, error) { + if s == nil || s.Alg.IsNull() { + return tpmutil.Pack(AlgNull) + } + if s.Alg.UsesCount() { + return tpmutil.Pack(s.Alg, s.Hash, s.Count) + } + return tpmutil.Pack(s.Alg, s.Hash) +} + +func decodeSigScheme(in *bytes.Buffer) (*SigScheme, error) { + var scheme SigScheme + if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + if scheme.Alg == AlgNull { + return nil, nil + } + if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil { + return nil, fmt.Errorf("decoding Hash: %v", err) + } + if scheme.Alg.UsesCount() { + if err := tpmutil.UnpackBuf(in, &scheme.Count); err != nil { + return nil, fmt.Errorf("decoding Count: %v", err) + } + } + return &scheme, nil +} + +// KDFScheme represents a KDF (Key Derivation Function) scheme. +type KDFScheme struct { + Alg Algorithm + Hash Algorithm +} + +func (s *KDFScheme) encode() ([]byte, error) { + if s == nil || s.Alg.IsNull() { + return tpmutil.Pack(AlgNull) + } + return tpmutil.Pack(s.Alg, s.Hash) +} + +func decodeKDFScheme(in *bytes.Buffer) (*KDFScheme, error) { + var scheme KDFScheme + if err := tpmutil.UnpackBuf(in, &scheme.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + if scheme.Alg == AlgNull { + return nil, nil + } + if err := tpmutil.UnpackBuf(in, &scheme.Hash); err != nil { + return nil, fmt.Errorf("decoding Hash: %v", err) + } + return &scheme, nil +} + +// Signature combines all possible signatures from RSA and ECC keys. Only one +// of RSA or ECC will be populated. +type Signature struct { + Alg Algorithm + RSA *SignatureRSA + ECC *SignatureECC +} + +// DecodeSignature decodes a serialized TPMT_SIGNATURE structure. +func DecodeSignature(in *bytes.Buffer) (*Signature, error) { + var sig Signature + if err := tpmutil.UnpackBuf(in, &sig.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + switch sig.Alg { + case AlgRSASSA, AlgRSAPSS: + sig.RSA = new(SignatureRSA) + if err := tpmutil.UnpackBuf(in, sig.RSA); err != nil { + return nil, fmt.Errorf("decoding RSA: %v", err) + } + case AlgECDSA: + sig.ECC = new(SignatureECC) + var r, s tpmutil.U16Bytes + if err := tpmutil.UnpackBuf(in, &sig.ECC.HashAlg, &r, &s); err != nil { + return nil, fmt.Errorf("decoding ECC: %v", err) + } + sig.ECC.R = big.NewInt(0).SetBytes(r) + sig.ECC.S = big.NewInt(0).SetBytes(s) + default: + return nil, fmt.Errorf("unsupported signature algorithm 0x%x", sig.Alg) + } + return &sig, nil +} + +// SignatureRSA is an RSA-specific signature value. +type SignatureRSA struct { + HashAlg Algorithm + Signature tpmutil.U16Bytes +} + +// SignatureECC is an ECC-specific signature value. +type SignatureECC struct { + HashAlg Algorithm + R *big.Int + S *big.Int +} + +// Private contains private section of a TPM key. +type Private struct { + Type Algorithm + AuthValue tpmutil.U16Bytes + SeedValue tpmutil.U16Bytes + Sensitive tpmutil.U16Bytes +} + +// Encode serializes a Private structure in TPM wire format. +func (p Private) Encode() ([]byte, error) { + if p.Type.IsNull() { + return nil, nil + } + return tpmutil.Pack(p) +} + +// AttestationData contains data attested by TPM commands (like Certify). +type AttestationData struct { + Magic uint32 + Type tpmutil.Tag + QualifiedSigner Name + ExtraData tpmutil.U16Bytes + ClockInfo ClockInfo + FirmwareVersion uint64 + AttestedCertifyInfo *CertifyInfo + AttestedQuoteInfo *QuoteInfo + AttestedCreationInfo *CreationInfo +} + +// DecodeAttestationData decode a TPMS_ATTEST message. No error is returned if +// the input has extra trailing data. +func DecodeAttestationData(in []byte) (*AttestationData, error) { + buf := bytes.NewBuffer(in) + + var ad AttestationData + if err := tpmutil.UnpackBuf(buf, &ad.Magic, &ad.Type); err != nil { + return nil, fmt.Errorf("decoding Magic/Type: %v", err) + } + // All attestation structures have the magic prefix + // TPMS_GENERATED_VALUE to symbolize they were created by + // the TPM when signed with an AK. + if ad.Magic != 0xff544347 { + return nil, fmt.Errorf("incorrect magic value: %x", ad.Magic) + } + + n, err := DecodeName(buf) + if err != nil { + return nil, fmt.Errorf("decoding QualifiedSigner: %v", err) + } + ad.QualifiedSigner = *n + if err := tpmutil.UnpackBuf(buf, &ad.ExtraData, &ad.ClockInfo, &ad.FirmwareVersion); err != nil { + return nil, fmt.Errorf("decoding ExtraData/ClockInfo/FirmwareVersion: %v", err) + } + + // The spec specifies several other types of attestation data. We only need + // parsing of Certify & Creation attestation data for now. If you need + // support for other attestation types, add them here. + switch ad.Type { + case TagAttestCertify: + if ad.AttestedCertifyInfo, err = decodeCertifyInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCertifyInfo: %v", err) + } + case TagAttestCreation: + if ad.AttestedCreationInfo, err = decodeCreationInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCreationInfo: %v", err) + } + case TagAttestQuote: + if ad.AttestedQuoteInfo, err = decodeQuoteInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedQuoteInfo: %v", err) + } + default: + return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) + } + + return &ad, nil +} + +// Encode serializes an AttestationData structure in TPM wire format. +func (ad AttestationData) Encode() ([]byte, error) { + head, err := tpmutil.Pack(ad.Magic, ad.Type) + if err != nil { + return nil, fmt.Errorf("encoding Magic, Type: %v", err) + } + signer, err := ad.QualifiedSigner.Encode() + if err != nil { + return nil, fmt.Errorf("encoding QualifiedSigner: %v", err) + } + tail, err := tpmutil.Pack(ad.ExtraData, ad.ClockInfo, ad.FirmwareVersion) + if err != nil { + return nil, fmt.Errorf("encoding ExtraData, ClockInfo, FirmwareVersion: %v", err) + } + + var info []byte + switch ad.Type { + case TagAttestCertify: + if info, err = ad.AttestedCertifyInfo.encode(); err != nil { + return nil, fmt.Errorf("encoding AttestedCertifyInfo: %v", err) + } + case TagAttestCreation: + if info, err = ad.AttestedCreationInfo.encode(); err != nil { + return nil, fmt.Errorf("encoding AttestedCreationInfo: %v", err) + } + default: + return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) + } + + return concat(head, signer, tail, info) +} + +// CreationInfo contains Creation-specific data for TPMS_ATTEST. +type CreationInfo struct { + Name Name + // Most TPM2B_Digest structures contain a TPMU_HA structure + // and get parsed to HashValue. This is never the case for the + // digest in TPMS_CREATION_INFO. + OpaqueDigest tpmutil.U16Bytes +} + +func decodeCreationInfo(in *bytes.Buffer) (*CreationInfo, error) { + var ci CreationInfo + + n, err := DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding Name: %v", err) + } + ci.Name = *n + + if err := tpmutil.UnpackBuf(in, &ci.OpaqueDigest); err != nil { + return nil, fmt.Errorf("decoding Digest: %v", err) + } + + return &ci, nil +} + +func (ci CreationInfo) encode() ([]byte, error) { + n, err := ci.Name.Encode() + if err != nil { + return nil, fmt.Errorf("encoding Name: %v", err) + } + + d, err := tpmutil.Pack(ci.OpaqueDigest) + if err != nil { + return nil, fmt.Errorf("encoding Digest: %v", err) + } + + return concat(n, d) +} + +// CertifyInfo contains Certify-specific data for TPMS_ATTEST. +type CertifyInfo struct { + Name Name + QualifiedName Name +} + +func decodeCertifyInfo(in *bytes.Buffer) (*CertifyInfo, error) { + var ci CertifyInfo + + n, err := DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding Name: %v", err) + } + ci.Name = *n + + n, err = DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding QualifiedName: %v", err) + } + ci.QualifiedName = *n + + return &ci, nil +} + +func (ci CertifyInfo) encode() ([]byte, error) { + n, err := ci.Name.Encode() + if err != nil { + return nil, fmt.Errorf("encoding Name: %v", err) + } + qn, err := ci.QualifiedName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding QualifiedName: %v", err) + } + return concat(n, qn) +} + +// QuoteInfo represents a TPMS_QUOTE_INFO structure. +type QuoteInfo struct { + PCRSelection PCRSelection + PCRDigest tpmutil.U16Bytes +} + +func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) { + var out QuoteInfo + sel, err := decodeOneTPMLPCRSelection(in) + if err != nil { + return nil, fmt.Errorf("decoding PCRSelection: %v", err) + } + out.PCRSelection = sel + + if err := tpmutil.UnpackBuf(in, &out.PCRDigest); err != nil { + return nil, fmt.Errorf("decoding PCRDigest: %v", err) + } + return &out, nil +} + +// IDObject represents an encrypted credential bound to a TPM object. +type IDObject struct { + IntegrityHMAC tpmutil.U16Bytes + // EncIdentity is packed raw, as the bytes representing the size + // of the credential value are present within the encrypted blob. + EncIdentity tpmutil.RawBytes +} + +// CreationData describes the attributes and environment for an object created +// on the TPM. This structure encodes/decodes to/from TPMS_CREATION_DATA. +type CreationData struct { + PCRSelection PCRSelection + PCRDigest tpmutil.U16Bytes + Locality byte + ParentNameAlg Algorithm + ParentName Name + ParentQualifiedName Name + OutsideInfo tpmutil.U16Bytes +} + +func (cd *CreationData) encode() ([]byte, error) { + sel, err := encodeTPMLPCRSelection(cd.PCRSelection) + if err != nil { + return nil, fmt.Errorf("encoding PCRSelection: %v", err) + } + d, err := tpmutil.Pack(cd.PCRDigest, cd.Locality, cd.ParentNameAlg) + if err != nil { + return nil, fmt.Errorf("encoding PCRDigest, Locality, ParentNameAlg: %v", err) + } + pn, err := cd.ParentName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding ParentName: %v", err) + } + pqn, err := cd.ParentQualifiedName.Encode() + if err != nil { + return nil, fmt.Errorf("encoding ParentQualifiedName: %v", err) + } + o, err := tpmutil.Pack(cd.OutsideInfo) + if err != nil { + return nil, fmt.Errorf("encoding OutsideInfo: %v", err) + } + return concat(sel, d, pn, pqn, o) +} + +// DecodeCreationData decodes a TPMS_CREATION_DATA message. No error is +// returned if the input has extra trailing data. +func DecodeCreationData(buf []byte) (*CreationData, error) { + in := bytes.NewBuffer(buf) + var out CreationData + + sel, err := decodeOneTPMLPCRSelection(in) + if err != nil { + return nil, fmt.Errorf("decodeOneTPMLPCRSelection returned error %v", err) + } + out.PCRSelection = sel + + if err := tpmutil.UnpackBuf(in, &out.PCRDigest, &out.Locality, &out.ParentNameAlg); err != nil { + return nil, fmt.Errorf("decoding PCRDigest, Locality, ParentNameAlg: %v", err) + } + + n, err := DecodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding ParentName: %v", err) + } + out.ParentName = *n + if n, err = DecodeName(in); err != nil { + return nil, fmt.Errorf("decoding ParentQualifiedName: %v", err) + } + out.ParentQualifiedName = *n + + if err := tpmutil.UnpackBuf(in, &out.OutsideInfo); err != nil { + return nil, fmt.Errorf("decoding OutsideInfo: %v", err) + } + + return &out, nil +} + +// Name represents a TPM2B_NAME, a name for TPM entities. Only one of +// Handle or Digest should be set. +type Name struct { + Handle *tpmutil.Handle + Digest *HashValue +} + +// DecodeName deserializes a Name hash from the TPM wire format. +func DecodeName(in *bytes.Buffer) (*Name, error) { + var nameBuf tpmutil.U16Bytes + if err := tpmutil.UnpackBuf(in, &nameBuf); err != nil { + return nil, err + } + + name := new(Name) + switch len(nameBuf) { + case 0: + // No name is present. + case 4: + name.Handle = new(tpmutil.Handle) + if err := tpmutil.UnpackBuf(bytes.NewBuffer(nameBuf), name.Handle); err != nil { + return nil, fmt.Errorf("decoding Handle: %v", err) + } + default: + var err error + name.Digest, err = decodeHashValue(bytes.NewBuffer(nameBuf)) + if err != nil { + return nil, fmt.Errorf("decoding Digest: %v", err) + } + } + return name, nil +} + +// Encode serializes a Name hash into the TPM wire format. +func (n Name) Encode() ([]byte, error) { + var buf []byte + var err error + switch { + case n.Handle != nil: + if buf, err = tpmutil.Pack(*n.Handle); err != nil { + return nil, fmt.Errorf("encoding Handle: %v", err) + } + case n.Digest != nil: + if buf, err = n.Digest.Encode(); err != nil { + return nil, fmt.Errorf("encoding Digest: %v", err) + } + default: + // Name is empty, which is valid. + } + return tpmutil.Pack(tpmutil.U16Bytes(buf)) +} + +// MatchesPublic compares Digest in Name against given Public structure. Note: +// this only works for regular Names, not Qualified Names. +func (n Name) MatchesPublic(p Public) (bool, error) { + if n.Digest == nil { + return false, errors.New("Name doesn't have a Digest, can't compare to Public") + } + expected, err := p.Name() + if err != nil { + return false, err + } + // No secrets, so no constant-time comparison + return bytes.Equal(expected.Digest.Value, n.Digest.Value), nil +} + +// HashValue is an algorithm-specific hash value. +type HashValue struct { + Alg Algorithm + Value tpmutil.U16Bytes +} + +func decodeHashValue(in *bytes.Buffer) (*HashValue, error) { + var hv HashValue + if err := tpmutil.UnpackBuf(in, &hv.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + hfn, err := hv.Alg.Hash() + if err != nil { + return nil, err + } + hv.Value = make(tpmutil.U16Bytes, hfn.Size()) + if _, err := in.Read(hv.Value); err != nil { + return nil, fmt.Errorf("decoding Value: %v", err) + } + return &hv, nil +} + +// Encode represents the given hash value as a TPMT_HA structure. +func (hv HashValue) Encode() ([]byte, error) { + return tpmutil.Pack(hv.Alg, tpmutil.RawBytes(hv.Value)) +} + +// ClockInfo contains TPM state info included in AttestationData. +type ClockInfo struct { + Clock uint64 + ResetCount uint32 + RestartCount uint32 + Safe byte +} + +// AlgorithmAttributes represents a TPMA_ALGORITHM value. +type AlgorithmAttributes uint32 + +// AlgorithmDescription represents a TPMS_ALGORITHM_DESCRIPTION structure. +type AlgorithmDescription struct { + ID Algorithm + Attributes AlgorithmAttributes +} + +// TaggedProperty represents a TPMS_TAGGED_PROPERTY structure. +type TaggedProperty struct { + Tag TPMProp + Value uint32 +} + +// Ticket represents evidence the TPM previously processed +// information. +type Ticket struct { + Type tpmutil.Tag + Hierarchy tpmutil.Handle + Digest tpmutil.U16Bytes +} + +// AuthCommand represents a TPMS_AUTH_COMMAND. This structure encapsulates parameters +// which authorize the use of a given handle or parameter. +type AuthCommand struct { + Session tpmutil.Handle + Nonce tpmutil.U16Bytes + Attributes SessionAttributes + Auth tpmutil.U16Bytes +} + +// TPMLDigest represents the TPML_Digest structure +// It is used to convey a list of digest values. +// This type is used in TPM2_PolicyOR() and in TPM2_PCR_Read() +type TPMLDigest struct { + Digests []tpmutil.U16Bytes +} + +// Encode converts the TPMLDigest structure into a byte slice +func (list *TPMLDigest) Encode() ([]byte, error) { + res, err := tpmutil.Pack(uint32(len(list.Digests))) + if err != nil { + return nil, err + } + for _, item := range list.Digests { + b, err := tpmutil.Pack(item) + if err != nil { + return nil, err + } + res = append(res, b...) + + } + return res, nil +} + +// DecodeTPMLDigest decodes a TPML_Digest part of a message. +func DecodeTPMLDigest(buf []byte) (*TPMLDigest, error) { + in := bytes.NewBuffer(buf) + var tpmld TPMLDigest + var count uint32 + if err := binary.Read(in, binary.BigEndian, &count); err != nil { + return nil, fmt.Errorf("decoding TPML_Digest: %v", err) + } + for in.Len() > 0 { + var hash tpmutil.U16Bytes + if err := hash.TPMUnmarshal(in); err != nil { + return nil, err + } + tpmld.Digests = append(tpmld.Digests, hash) + } + if count != uint32(len(tpmld.Digests)) { + return nil, fmt.Errorf("expected size and read size does not match") + } + return &tpmld, nil +} diff --git a/vendor/github.com/google/go-tpm/tpm2/tpm2.go b/vendor/github.com/google/go-tpm/tpm2/tpm2.go new file mode 100644 index 000000000000..009260018275 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpm2/tpm2.go @@ -0,0 +1,2266 @@ +// 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 +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/BUILD.bazel b/vendor/github.com/google/go-tpm/tpmutil/BUILD.bazel new file mode 100644 index 000000000000..7e10a0afedef --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "encoding.go", + "poll_other.go", + "poll_unix.go", + "run.go", + "run_other.go", + "run_windows.go", + "structures.go", + ], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm/tpmutil", + importpath = "github.com/google/go-tpm/tpmutil", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:android": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:ios": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/github.com/google/go-tpm/tpmutil/tbs:go_default_library", + ], + "//conditions:default": [], + }), +) diff --git a/vendor/github.com/google/go-tpm/tpmutil/encoding.go b/vendor/github.com/google/go-tpm/tpmutil/encoding.go new file mode 100644 index 000000000000..5983cc215cc8 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/encoding.go @@ -0,0 +1,211 @@ +// 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 tpmutil + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "reflect" +) + +var ( + selfMarshalerType = reflect.TypeOf((*SelfMarshaler)(nil)).Elem() + handlesAreaType = reflect.TypeOf((*[]Handle)(nil)) +) + +// packWithHeader takes a header and a sequence of elements that are either of +// fixed length or slices of fixed-length types and packs them into a single +// byte array using binary.Write. It updates the CommandHeader to have the right +// length. +func packWithHeader(ch commandHeader, cmd ...interface{}) ([]byte, error) { + hdrSize := binary.Size(ch) + body, err := Pack(cmd...) + if err != nil { + return nil, fmt.Errorf("couldn't pack message body: %v", err) + } + bodySize := len(body) + ch.Size = uint32(hdrSize + bodySize) + header, err := Pack(ch) + if err != nil { + return nil, fmt.Errorf("couldn't pack message header: %v", err) + } + return append(header, body...), nil +} + +// Pack encodes a set of elements into a single byte array, using +// encoding/binary. This means that all the elements must be encodeable +// according to the rules of encoding/binary. +// +// It has one difference from encoding/binary: it encodes byte slices with a +// prepended length, to match how the TPM encodes variable-length arrays. If +// you wish to add a byte slice without length prefix, use RawBytes. +func Pack(elts ...interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + if err := packType(buf, elts...); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// tryMarshal attempts to use a TPMMarshal() method defined on the type +// to pack v into buf. True is returned if the method exists and the +// marshal was attempted. +func tryMarshal(buf io.Writer, v reflect.Value) (bool, error) { + t := v.Type() + if t.Implements(selfMarshalerType) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return true, fmt.Errorf("cannot call TPMMarshal on a nil pointer of type %T", v) + } + return true, v.Interface().(SelfMarshaler).TPMMarshal(buf) + } + + // We might have a non-pointer struct field, but we dont have a + // pointer with which to implement the interface. + // If the pointer of the type implements the interface, we should be + // able to construct a value to call TPMMarshal() with. + // TODO(awly): Try and avoid blowing away private data by using Addr() instead of Set() + if reflect.PtrTo(t).Implements(selfMarshalerType) { + tmp := reflect.New(t) + tmp.Elem().Set(v) + return true, tmp.Interface().(SelfMarshaler).TPMMarshal(buf) + } + + return false, nil +} + +func packValue(buf io.Writer, v reflect.Value) error { + if v.Type() == handlesAreaType { + v = v.Convert(reflect.TypeOf((*handleList)(nil))) + } + if canMarshal, err := tryMarshal(buf, v); canMarshal { + return err + } + + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return fmt.Errorf("cannot pack nil %s", v.Type().String()) + } + return packValue(buf, v.Elem()) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if err := packValue(buf, f); err != nil { + return err + } + } + default: + return binary.Write(buf, binary.BigEndian, v.Interface()) + } + return nil +} + +func packType(buf io.Writer, elts ...interface{}) error { + for _, e := range elts { + if err := packValue(buf, reflect.ValueOf(e)); err != nil { + return err + } + } + + return nil +} + +// tryUnmarshal attempts to use TPMUnmarshal() to perform the +// unpack, if the given value implements SelfMarshaler. +// True is returned if v implements SelfMarshaler & TPMUnmarshal +// was called, along with an error returned from TPMUnmarshal. +func tryUnmarshal(buf io.Reader, v reflect.Value) (bool, error) { + t := v.Type() + if t.Implements(selfMarshalerType) { + if v.Kind() == reflect.Ptr && v.IsNil() { + return true, fmt.Errorf("cannot call TPMUnmarshal on a nil pointer") + } + return true, v.Interface().(SelfMarshaler).TPMUnmarshal(buf) + } + + // We might have a non-pointer struct field, which is addressable, + // If the pointer of the type implements the interface, and the + // value is addressable, we should be able to call TPMUnmarshal(). + if v.CanAddr() && reflect.PtrTo(t).Implements(selfMarshalerType) { + return true, v.Addr().Interface().(SelfMarshaler).TPMUnmarshal(buf) + } + + return false, nil +} + +// Unpack is a convenience wrapper around UnpackBuf. Unpack returns the number +// of bytes read from b to fill elts and error, if any. +func Unpack(b []byte, elts ...interface{}) (int, error) { + buf := bytes.NewBuffer(b) + err := UnpackBuf(buf, elts...) + read := len(b) - buf.Len() + return read, err +} + +func unpackValue(buf io.Reader, v reflect.Value) error { + if v.Type() == handlesAreaType { + v = v.Convert(reflect.TypeOf((*handleList)(nil))) + } + if didUnmarshal, err := tryUnmarshal(buf, v); didUnmarshal { + return err + } + + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return fmt.Errorf("cannot unpack nil %s", v.Type().String()) + } + return unpackValue(buf, v.Elem()) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if err := unpackValue(buf, f); err != nil { + return err + } + } + return nil + default: + // binary.Read can only set pointer values, so we need to take the address. + if !v.CanAddr() { + return fmt.Errorf("cannot unpack unaddressable leaf type %q", v.Type().String()) + } + return binary.Read(buf, binary.BigEndian, v.Addr().Interface()) + } +} + +// UnpackBuf recursively unpacks types from a reader just as encoding/binary +// does under binary.BigEndian, but with one difference: it unpacks a byte +// slice by first reading an integer with lengthPrefixSize bytes, then reading +// that many bytes. It assumes that incoming values are pointers to values so +// that, e.g., underlying slices can be resized as needed. +func UnpackBuf(buf io.Reader, elts ...interface{}) error { + for _, e := range elts { + v := reflect.ValueOf(e) + if v.Kind() != reflect.Ptr { + return fmt.Errorf("non-pointer value %q passed to UnpackBuf", v.Type().String()) + } + if v.IsNil() { + return errors.New("nil pointer passed to UnpackBuf") + } + + if err := unpackValue(buf, v); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/poll_other.go b/vendor/github.com/google/go-tpm/tpmutil/poll_other.go new file mode 100644 index 000000000000..6840f6f8a15f --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/poll_other.go @@ -0,0 +1,10 @@ +// +build !linux,!darwin + +package tpmutil + +import ( + "os" +) + +// Not implemented on Windows. +func poll(f *os.File) error { return nil } diff --git a/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go b/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go new file mode 100644 index 000000000000..7fe56fdecbb1 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/poll_unix.go @@ -0,0 +1,32 @@ +// +build linux darwin + +package tpmutil + +import ( + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +// poll blocks until the file descriptor is ready for reading or an error occurs. +func poll(f *os.File) error { + var ( + fds = []unix.PollFd{{ + Fd: int32(f.Fd()), + Events: 0x1, // POLLIN + }} + timeout = -1 // Indefinite timeout + ) + + if _, err := unix.Poll(fds, timeout); err != nil { + return err + } + + // Revents is filled in by the kernel. + // If the expected event happened, Revents should match Events. + if fds[0].Revents != fds[0].Events { + return fmt.Errorf("unexpected poll Revents 0x%x", fds[0].Revents) + } + return nil +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/run.go b/vendor/github.com/google/go-tpm/tpmutil/run.go new file mode 100644 index 000000000000..21a9c6657eb0 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/run.go @@ -0,0 +1,76 @@ +// 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 tpmutil provides common utility functions for both TPM 1.2 and TPM 2.0 devices. +package tpmutil + +import ( + "errors" + "io" + "os" +) + +// maxTPMResponse is the largest possible response from the TPM. We need to know +// this because we don't always know the length of the TPM response, and +// /dev/tpm insists on giving it all back in a single value rather than +// 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) { + 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 + } + + if _, err := rw.Write(inb); err != nil { + return nil, 0, 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 f, ok := rw.(*os.File); ok { + if err = poll(f); err != nil { + return nil, 0, err + } + } + + outb := make([]byte, maxTPMResponse) + outlen, err := rw.Read(outb) + if err != nil { + return nil, 0, err + } + // Resize the buffer to match the amount read from the TPM. + outb = outb[:outlen] + + var rh responseHeader + read, err := Unpack(outb, &rh) + if err != nil { + return nil, 0, err + } + outb = outb[read:] + + if rh.Res != RCSuccess { + return nil, rh.Res, nil + } + + return outb, rh.Res, nil +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/run_other.go b/vendor/github.com/google/go-tpm/tpmutil/run_other.go new file mode 100644 index 000000000000..1b8dc2e802cd --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/run_other.go @@ -0,0 +1,111 @@ +// +build !windows + +// 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 tpmutil + +import ( + "fmt" + "io" + "net" + "os" +) + +// 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. +func OpenTPM(path string) (io.ReadWriteCloser, error) { + // If it's a regular file, then open it + var rwc io.ReadWriteCloser + fi, err := os.Stat(path) + if err != nil { + return nil, err + } + + if fi.Mode()&os.ModeDevice != 0 { + var f *os.File + f, err = os.OpenFile(path, os.O_RDWR, 0600) + if err != nil { + return nil, err + } + rwc = io.ReadWriteCloser(f) + } else if fi.Mode()&os.ModeSocket != 0 { + rwc = NewEmulatorReadWriteCloser(path) + } else { + return nil, fmt.Errorf("unsupported TPM file mode %s", fi.Mode().String()) + } + + return rwc, nil +} + +// dialer abstracts the net.Dial call so test code can provide its own net.Conn +// implementation. +type dialer func(network, path string) (net.Conn, error) + +// EmulatorReadWriteCloser manages connections with a TPM emulator over a Unix +// domain socket. These emulators often operate in a write/read/disconnect +// sequence, so the Write method always connects, and the Read method always +// closes. EmulatorReadWriteCloser is not thread safe. +type EmulatorReadWriteCloser struct { + path string + conn net.Conn + dialer dialer +} + +// NewEmulatorReadWriteCloser stores information about a Unix domain socket to +// write to and read from. +func NewEmulatorReadWriteCloser(path string) *EmulatorReadWriteCloser { + return &EmulatorReadWriteCloser{ + path: path, + dialer: net.Dial, + } +} + +// Read implements io.Reader by reading from the Unix domain socket and closing +// it. +func (erw *EmulatorReadWriteCloser) Read(p []byte) (int, error) { + // Read is always the second operation in a Write/Read sequence. + if erw.conn == nil { + return 0, fmt.Errorf("Must call Write then Read in an alternating sequence") + } + n, err := erw.conn.Read(p) + erw.conn.Close() + erw.conn = nil + return n, err +} + +// Write implements io.Writer by connecting to the Unix domain socket and +// writing. +func (erw *EmulatorReadWriteCloser) Write(p []byte) (int, error) { + if erw.conn != nil { + return 0, fmt.Errorf("Must call Write then Read in an alternating sequence") + } + var err error + erw.conn, err = erw.dialer("unix", erw.path) + if err != nil { + return 0, err + } + return erw.conn.Write(p) +} + +// Close implements io.Closer by closing the Unix domain socket if one is open. +func (erw *EmulatorReadWriteCloser) Close() error { + if erw.conn == nil { + return fmt.Errorf("Cannot call Close when no connection is open") + } + err := erw.conn.Close() + erw.conn = nil + return err +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/run_windows.go b/vendor/github.com/google/go-tpm/tpmutil/run_windows.go new file mode 100644 index 000000000000..f355b8101237 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/run_windows.go @@ -0,0 +1,84 @@ +// 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 tpmutil + +import ( + "io" + + "github.com/google/go-tpm/tpmutil/tbs" +) + +// winTPMBuffer is a ReadWriteCloser to access the TPM in Windows. +type winTPMBuffer struct { + context tbs.Context + outBuffer []byte +} + +// Executes the TPM command specified by commandBuffer (at Normal Priority), returning the number +// of bytes in the command and any error code returned by executing the TPM command. Command +// response can be read by calling Read(). +func (rwc *winTPMBuffer) Write(commandBuffer []byte) (int, error) { + // TPM spec defines longest possible response to be maxTPMResponse. + rwc.outBuffer = rwc.outBuffer[:maxTPMResponse] + + outBufferLen, err := rwc.context.SubmitCommand( + tbs.NormalPriority, + commandBuffer, + rwc.outBuffer, + ) + + if err != nil { + rwc.outBuffer = rwc.outBuffer[:0] + return 0, err + } + // Shrink outBuffer so it is length of response. + rwc.outBuffer = rwc.outBuffer[:outBufferLen] + return len(commandBuffer), nil +} + +// Provides TPM response from the command called in the last Write call. +func (rwc *winTPMBuffer) Read(responseBuffer []byte) (int, error) { + if len(rwc.outBuffer) == 0 { + return 0, io.EOF + } + lenCopied := copy(responseBuffer, rwc.outBuffer) + // Cut out the piece of slice which was just read out, maintaining original slice capacity. + rwc.outBuffer = append(rwc.outBuffer[:0], rwc.outBuffer[lenCopied:]...) + return lenCopied, nil +} + +func (rwc *winTPMBuffer) Close() error { + return rwc.context.Close() +} + +// OpenTPM creates a new instance of a ReadWriteCloser which can interact with a +// Windows TPM. +func OpenTPM() (io.ReadWriteCloser, error) { + tpmContext, err := tbs.CreateContext(tbs.TPMVersion20, tbs.IncludeTPM12|tbs.IncludeTPM20) + rwc := &winTPMBuffer{ + context: tpmContext, + outBuffer: make([]byte, 0, maxTPMResponse), + } + return rwc, err +} + +// FromContext creates a new instance of a ReadWriteCloser which can +// interact with a Windows TPM, using the specified TBS handle. +func FromContext(ctx tbs.Context) io.ReadWriteCloser { + return &winTPMBuffer{ + context: ctx, + outBuffer: make([]byte, 0, maxTPMResponse), + } +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/structures.go b/vendor/github.com/google/go-tpm/tpmutil/structures.go new file mode 100644 index 000000000000..d7d05fb4a892 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/structures.go @@ -0,0 +1,185 @@ +// 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 tpmutil + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +// maxBytesBufferSize sets a sane upper bound on the size of a U32Bytes +// buffer. This limit exists to prevent a maliciously large size prefix +// from resulting in a massive memory allocation, potentially causing +// an OOM condition on the system. +// We expect no buffer from a TPM to approach 1Mb in size. +const maxBytesBufferSize = 1024 * 1024 // 1Mb. + +// RawBytes is for Pack and RunCommand arguments that are already encoded. +// Compared to []byte, RawBytes will not be prepended with slice length during +// encoding. +type RawBytes []byte + +// U16Bytes is a byte slice with a 16-bit header +type U16Bytes []byte + +// TPMMarshal packs U16Bytes +func (b *U16Bytes) TPMMarshal(out io.Writer) error { + size := uint16(len([]byte(*b))) + if err := binary.Write(out, binary.BigEndian, size); err != nil { + return err + } + + n, err := out.Write(*b) + if err != nil { + return err + } + if n != int(size) { + return fmt.Errorf("unable to write all contents of U16Bytes") + } + return nil +} + +// TPMUnmarshal unpacks a U16Bytes +func (b *U16Bytes) TPMUnmarshal(in io.Reader) error { + var tmpSize uint16 + if err := binary.Read(in, binary.BigEndian, &tmpSize); err != nil { + return err + } + size := int(tmpSize) + + if len(*b) >= size { + *b = (*b)[:size] + } else { + *b = append(*b, make([]byte, size-len(*b))...) + } + + n, err := in.Read(*b) + if err != nil { + return err + } + if n != size { + return io.ErrUnexpectedEOF + } + return nil +} + +// U32Bytes is a byte slice with a 32-bit header +type U32Bytes []byte + +// TPMMarshal packs U32Bytes +func (b *U32Bytes) TPMMarshal(out io.Writer) error { + size := uint32(len([]byte(*b))) + if err := binary.Write(out, binary.BigEndian, size); err != nil { + return err + } + + n, err := out.Write(*b) + if err != nil { + return err + } + if n != int(size) { + return fmt.Errorf("unable to write all contents of U32Bytes") + } + return nil +} + +// TPMUnmarshal unpacks a U32Bytes +func (b *U32Bytes) TPMUnmarshal(in io.Reader) error { + var tmpSize uint32 + if err := binary.Read(in, binary.BigEndian, &tmpSize); err != nil { + return err + } + size := int(tmpSize) + + if size > maxBytesBufferSize { + return bytes.ErrTooLarge + } + + if len(*b) >= size { + *b = (*b)[:size] + } else { + *b = append(*b, make([]byte, size-len(*b))...) + } + + n, err := in.Read(*b) + if err != nil { + return err + } + if n != size { + return fmt.Errorf("unable to read all contents in to U32Bytes") + } + return nil +} + +// Tag is a command tag. +type Tag uint16 + +// Command is an identifier of a TPM command. +type Command uint32 + +// A commandHeader is the header for a TPM command. +type commandHeader struct { + Tag Tag + Size uint32 + Cmd Command +} + +// ResponseCode is a response code returned by TPM. +type ResponseCode uint32 + +// RCSuccess is response code for successful command. Identical for TPM 1.2 and +// 2.0. +const RCSuccess ResponseCode = 0x000 + +// A responseHeader is a header for TPM responses. +type responseHeader struct { + Tag Tag + Size uint32 + Res ResponseCode +} + +// A Handle is a reference to a TPM object. +type Handle uint32 + +type handleList []Handle + +func (l *handleList) TPMMarshal(out io.Writer) error { + return fmt.Errorf("TPMMarhsal on []Handle is not supported yet") +} + +func (l *handleList) TPMUnmarshal(in io.Reader) error { + var numHandles uint16 + if err := binary.Read(in, binary.BigEndian, &numHandles); err != nil { + return err + } + + // Make len(e) match size exactly. + size := int(numHandles) + if len(*l) >= size { + *l = (*l)[:size] + } else { + *l = append(*l, make([]Handle, size-len(*l))...) + } + return binary.Read(in, binary.BigEndian, *l) +} + +// SelfMarshaler allows custom types to override default encoding/decoding +// behavior in Pack, Unpack and UnpackBuf. +type SelfMarshaler interface { + TPMMarshal(out io.Writer) error + TPMUnmarshal(in io.Reader) error +} diff --git a/vendor/github.com/google/go-tpm/tpmutil/tbs/BUILD.bazel b/vendor/github.com/google/go-tpm/tpmutil/tbs/BUILD.bazel new file mode 100644 index 000000000000..a9cd1f984a44 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/tbs/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["tbs_windows.go"], + importmap = "k8s.io/kops/vendor/github.com/google/go-tpm/tpmutil/tbs", + importpath = "github.com/google/go-tpm/tpmutil/tbs", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go new file mode 100644 index 000000000000..4e9049089260 --- /dev/null +++ b/vendor/github.com/google/go-tpm/tpmutil/tbs/tbs_windows.go @@ -0,0 +1,250 @@ +// 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 tbs provides an low-level interface directly mapping to Windows +// Tbs.dll system library commands: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal +// Public field descriptions contain links to the high-level Windows documentation. +package tbs + +import ( + "fmt" + "syscall" + "unsafe" +) + +// Context references the current TPM context +type Context uintptr + +// Version of TPM being used by the application. +type Version uint32 + +// Flag indicates TPM verisions that are supported by the application. +type Flag uint32 + +// CommandPriority is used to determine which pending command to submit whenever the TPM is free. +type CommandPriority uint32 + +// Command parameters: +// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/tbs.h +const ( + // https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/ns-tbs-tdtbs_context_params2 + // OR flags to use multiple. + RequestRaw Flag = 1 << iota // Add flag to request raw context + IncludeTPM12 // Add flag to support TPM 1.2 + IncludeTPM20 // Add flag to support TPM 2 + + TPMVersion12 Version = 1 // For TPM 1.2 applications + TPMVersion20 Version = 2 // For TPM 2 applications or applications using multiple TPM versions + + // https://docs.microsoft.com/en-us/windows/desktop/tbs/command-scheduling + // https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command#parameters + LowPriority CommandPriority = 100 // For low priority application use + NormalPriority CommandPriority = 200 // For normal priority application use + HighPriority CommandPriority = 300 // For high priority application use + SystemPriority CommandPriority = 400 // For system tasks that access the TPM + + commandLocalityZero uint32 = 0 // Windows currently only supports TBS_COMMAND_LOCALITY_ZERO. +) + +// Error is the return type of all functions in this package. +type Error uint32 + +func (err Error) Error() string { + if description, ok := errorDescriptions[err]; ok { + return fmt.Sprintf("TBS Error 0x%X: %s", uint32(err), description) + } + return fmt.Sprintf("Unrecognized TBS Error 0x%X", uint32(err)) +} + +func getError(err uintptr) error { + // tbs.dll uses 0x0 as the return value for success. + if err == 0 { + return nil + } + return Error(err) +} + +// TBS Return Codes: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tbs-return-codes +const ( + ErrInternalError Error = 0x80284001 + ErrBadParameter Error = 0x80284002 + ErrInvalidOutputPointer Error = 0x80284003 + ErrInvalidContext Error = 0x80284004 + ErrInsufficientBuffer Error = 0x80284005 + ErrIOError Error = 0x80284006 + ErrInvalidContextParam Error = 0x80284007 + ErrServiceNotRunning Error = 0x80284008 + ErrTooManyTBSContexts Error = 0x80284009 + ErrTooManyResources Error = 0x8028400A + ErrServiceStartPending Error = 0x8028400B + ErrPPINotSupported Error = 0x8028400C + ErrCommandCanceled Error = 0x8028400D + ErrBufferTooLarge Error = 0x8028400E + ErrTPMNotFound Error = 0x8028400F + ErrServiceDisabled Error = 0x80284010 + ErrNoEventLog Error = 0x80284011 + ErrAccessDenied Error = 0x80284012 + ErrProvisioningNotAllowed Error = 0x80284013 + ErrPPIFunctionUnsupported Error = 0x80284014 + ErrOwnerauthNotFound Error = 0x80284015 +) + +var errorDescriptions = map[Error]string{ + ErrInternalError: "An internal software error occurred.", + ErrBadParameter: "One or more parameter values are not valid.", + ErrInvalidOutputPointer: "A specified output pointer is bad.", + ErrInvalidContext: "The specified context handle does not refer to a valid context.", + ErrInsufficientBuffer: "The specified output buffer is too small.", + ErrIOError: "An error occurred while communicating with the TPM.", + ErrInvalidContextParam: "A context parameter that is not valid was passed when attempting to create a TBS context.", + ErrServiceNotRunning: "The TBS service is not running and could not be started.", + ErrTooManyTBSContexts: "A new context could not be created because there are too many open contexts.", + ErrTooManyResources: "A new virtual resource could not be created because there are too many open virtual resources.", + ErrServiceStartPending: "The TBS service has been started but is not yet running.", + ErrPPINotSupported: "The physical presence interface is not supported.", + ErrCommandCanceled: "The command was canceled.", + ErrBufferTooLarge: "The input or output buffer is too large.", + ErrTPMNotFound: "A compatible Trusted Platform Module (TPM) Security Device cannot be found on this computer.", + ErrServiceDisabled: "The TBS service has been disabled.", + ErrNoEventLog: "The TBS event log is not available.", + ErrAccessDenied: "The caller does not have the appropriate rights to perform the requested operation.", + ErrProvisioningNotAllowed: "The TPM provisioning action is not allowed by the specified flags.", + ErrPPIFunctionUnsupported: "The Physical Presence Interface of this firmware does not support the requested method.", + ErrOwnerauthNotFound: "The requested TPM OwnerAuth value was not found.", +} + +// Tbs.dll provides an API for making calls to the TPM: +// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal +var ( + tbsDLL = syscall.NewLazyDLL("Tbs.dll") + tbsGetDeviceInfo = tbsDLL.NewProc("Tbsi_GetDeviceInfo") + tbsCreateContext = tbsDLL.NewProc("Tbsi_Context_Create") + tbsContextClose = tbsDLL.NewProc("Tbsip_Context_Close") + tbsSubmitCommand = tbsDLL.NewProc("Tbsip_Submit_Command") + tbsGetTCGLog = tbsDLL.NewProc("Tbsi_Get_TCG_Log") +) + +// Returns the address of the beginning of a slice or 0 for a nil slice. +func sliceAddress(s []byte) uintptr { + if len(s) == 0 { + return 0 + } + return uintptr(unsafe.Pointer(&(s[0]))) +} + +// Declaration of TPM_DEVICE_INFO from tbs.h +type DeviceInfo struct { + StructVersion uint32 + TPMVersion Version + TPMInterfaceType uint32 + TPMImpRevision uint32 +} + +func GetDeviceInfo() (*DeviceInfo, error) { + info := DeviceInfo{} + // TBS_RESULT Tbsi_GetDeviceInfo( + // UINT32 Size, + // PVOID Info + // ); + result, _, _ := tbsGetDeviceInfo.Call( + unsafe.Sizeof(info), + uintptr(unsafe.Pointer(&info)), + ) + return &info, getError(result) +} + +// CreateContext creates a new TPM context: +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_context_create +func CreateContext(version Version, flag Flag) (Context, error) { + var context Context + params := struct { + Version + Flag + }{version, flag} + // TBS_RESULT Tbsi_Context_Create( + // _In_ PCTBS_CONTEXT_PARAMS pContextParams, + // _Out_ PTBS_HCONTEXT *phContext + // ); + result, _, _ := tbsCreateContext.Call( + uintptr(unsafe.Pointer(¶ms)), + uintptr(unsafe.Pointer(&context)), + ) + return context, getError(result) +} + +// Close closes an existing TPM context: +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_context_close +func (context Context) Close() error { + // TBS_RESULT Tbsip_Context_Close( + // _In_ TBS_HCONTEXT hContext + // ); + result, _, _ := tbsContextClose.Call(uintptr(context)) + return getError(result) +} + +// SubmitCommand sends commandBuffer to the TPM, returning the number of bytes +// written to responseBuffer. ErrInsufficientBuffer is returned if the +// responseBuffer is too short. ErrInvalidOutputPointer is returned if the +// responseBuffer is nil. On failure, the returned length is unspecified. +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command +func (context Context) SubmitCommand( + priority CommandPriority, + commandBuffer []byte, + responseBuffer []byte, +) (uint32, error) { + responseBufferLen := uint32(len(responseBuffer)) + + // TBS_RESULT Tbsip_Submit_Command( + // _In_ TBS_HCONTEXT hContext, + // _In_ TBS_COMMAND_LOCALITY Locality, + // _In_ TBS_COMMAND_PRIORITY Priority, + // _In_ const PCBYTE *pabCommand, + // _In_ UINT32 cbCommand, + // _Out_ PBYTE *pabResult, + // _Inout_ UINT32 *pcbOutput + // ); + result, _, _ := tbsSubmitCommand.Call( + uintptr(context), + uintptr(commandLocalityZero), + uintptr(priority), + sliceAddress(commandBuffer), + uintptr(len(commandBuffer)), + sliceAddress(responseBuffer), + uintptr(unsafe.Pointer(&responseBufferLen)), + ) + return responseBufferLen, getError(result) +} + +// GetTCGLog gets the system event log, returning the number of bytes written +// to logBuffer. If logBuffer is nil, the size of the TCG log is returned. +// ErrInsufficientBuffer is returned if the logBuffer is too short. On failure, +// the returned length is unspecified. +// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_get_tcg_log +func (context Context) GetTCGLog(logBuffer []byte) (uint32, error) { + logBufferLen := uint32(len(logBuffer)) + + // TBS_RESULT Tbsi_Get_TCG_Log( + // TBS_HCONTEXT hContext, + // PBYTE pOutputBuf, + // PUINT32 pOutputBufLen + // ); + result, _, _ := tbsGetTCGLog.Call( + uintptr(context), + sliceAddress(logBuffer), + uintptr(unsafe.Pointer(&logBufferLen)), + ) + return logBufferLen, getError(result) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 1d370cc20f59..cc3c71f56f72 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -368,6 +368,17 @@ github.com/google/go-containerregistry/pkg/v1/stream github.com/google/go-containerregistry/pkg/v1/types # github.com/google/go-querystring v1.0.0 github.com/google/go-querystring/query +# github.com/google/go-tpm v0.3.2 +## explicit +github.com/google/go-tpm/tpm2 +github.com/google/go-tpm/tpmutil +github.com/google/go-tpm/tpmutil/tbs +# github.com/google/go-tpm-tools v0.3.0-beta1 +## explicit +github.com/google/go-tpm-tools/client +github.com/google/go-tpm-tools/internal +github.com/google/go-tpm-tools/proto/attest +github.com/google/go-tpm-tools/proto/tpm # github.com/google/gofuzz v1.2.0 github.com/google/gofuzz github.com/google/gofuzz/bytesource