pax_global_header00006660000000000000000000000064147304045630014520gustar00rootroot0000000000000052 comment=a21381d5aaa61c04bcb8d2a853f20e6960cd9d3c go-tpm-0.9.3/000077500000000000000000000000001473040456300127345ustar00rootroot00000000000000go-tpm-0.9.3/.cirrus.yml000066400000000000000000000006121473040456300150430ustar00rootroot00000000000000container: dockerfile: Dockerfile env: GOPROXY: https://proxy.golang.org GO111MODULE: on test_task: modules_cache: fingerprint_script: cat go.sum folder: $GOPATH/pkg/mod build_script: go build -v ./... test_script: go test -p 1 -v ./... lint_task: env: matrix: - GOOS: linux - GOOS: windows lint_script: golangci-lint run ./tpmutil/... ./tpm2/... go-tpm-0.9.3/.gitignore000066400000000000000000000004771473040456300147340ustar00rootroot00000000000000*.test .vscode* # Swap [._]*.s[a-v][a-z] !*.svg # comment out if you don't need vector files [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] # Session Session.vim Sessionx.vim # Temporary .netrwhist *~ # Auto-generated tag files tags # Persistent undo [._]*.un~ # Generated files constants_string.go go-tpm-0.9.3/.golangci.yml000066400000000000000000000005271473040456300153240ustar00rootroot00000000000000linters: disable: - errcheck enable: - stylecheck - goimports - misspell - revive linters-settings: revive: rules: - name: dot-imports disabled: true issues: exclude-use-default: false exclude: - stutters - underscores - unexported-return max-issues-per-linter: 0 max-same-issues: 0 go-tpm-0.9.3/CODEOWNERS000066400000000000000000000006541473040456300143340ustar00rootroot00000000000000# Being an admin also gives you global code ownership. * @google/go-tpm-admin # The TPM 1.2 maintainers own the TPM 1.2 files. If this is updated, the access # permissions to the repository should also be updated. /tpm/ @zaolin @flanfly @ChriMarMe @google/go-tpm-admin /examples/tpm-*/ @zaolin @flanfly @ChriMarMe @google/go-tpm-admin /tpm2/ @alexmwu @jkl73 @google/go-tpm-admin go-tpm-0.9.3/CONTRIBUTING.md000066400000000000000000000060651473040456300151740ustar00rootroot00000000000000# How to contribute # We'd love to accept your patches and contributions to this project. There are a just a few small guidelines you need to follow. ## Contributor License Agreement ## Contributions to any Google project must be accompanied by a Contributor License Agreement. This is not a copyright **assignment**, it simply gives Google permission to use and redistribute your contributions as part of the project. * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA][]. * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA][]. You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. [individual CLA]: https://developers.google.com/open-source/cla/individual [corporate CLA]: https://developers.google.com/open-source/cla/corporate ## Submitting a patch ## 1. It's generally best to start by opening a new issue describing the bug or feature you're intending to fix. Even if you think it's relatively minor, it's helpful to know what people are working on. Mention in the initial issue that you are planning to work on that bug or feature so that it can be assigned to you. 1. Follow the normal process of [forking][] the project, and setup a new branch to work in. It's important that each group of changes be done in separate branches in order to ensure that a pull request only includes the commits related to that bug or feature. 1. Go makes it very simple to ensure properly formatted code, so always run `go fmt` on your code before committing it. You should also run [golint][] over your code. As noted in the [golint readme][], it's not strictly necessary that your code be completely "lint-free", but this will help you find common style issues. 1. Any significant changes should almost always be accompanied by tests. Look at some of the existing tests if you're unsure how to go about it. [gocov][] and [gocov-html][] are invaluable tools for seeing which parts of your code aren't being exercised by your tests. 1. Do your best to have [well-formed commit messages][] for each change. This provides consistency throughout the project, and ensures that commit messages are able to be formatted properly by various git tools. 1. Finally, push the commits to your fork and submit a [pull request][]. [forking]: https://help.github.com/articles/fork-a-repo [golint]: https://github.com/golang/lint [golint readme]: https://github.com/golang/lint/blob/master/README [gocov]: https://github.com/axw/gocov [gocov-html]: https://github.com/matm/gocov-html [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits [pull request]: https://help.github.com/articles/creating-a-pull-request go-tpm-0.9.3/Dockerfile000066400000000000000000000010571473040456300147310ustar00rootroot00000000000000FROM golang:1.22 # We need OpenSSL headers to build the simulator RUN apt-get update && apt-get install -y \ libssl-dev \ && rm -rf /var/lib/apt/lists/* # We need golangci-lint for linting ARG VERSION=1.56.2 RUN curl -SL \ https://github.com/golangci/golangci-lint/releases/download/v${VERSION}/golangci-lint-${VERSION}-linux-amd64.tar.gz \ --output golangci.tar.gz \ && tar --extract --verbose \ --file=golangci.tar.gz \ --directory=/usr/local/bin \ --strip-components=1 \ --wildcards "*/golangci-lint" \ && rm golangci.tar.gz go-tpm-0.9.3/LICENSE000066400000000000000000000261361473040456300137510ustar00rootroot00000000000000 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. go-tpm-0.9.3/README.md000066400000000000000000000023711473040456300142160ustar00rootroot00000000000000Go-TPM ====== Go-TPM is a Go library that communicates directly with a TPM device on Linux or Windows machines. The libraries don't implement the entire spec for neither 1.2 nor 2.0. **If you need a command that's missing, contributions are welcome!** Please note that this is not an official Google product. ## Structure The `tpm` directory contains TPM 1.2 client library. This library is in ["maintenance mode"](#tpm-1.2). The `legacy/tpm2` directory contains the legacy TPM 2.0 client library. The `tpm2` directory contains the prototype "TPMDirect" TPM 2.0 API, which is intended to (eventually) be 1:1 with the TPM 2.0 spec. Please report issues, complaints, or suggestions using the label https://github.com/google/go-tpm/labels/tpmdirect. The `examples` directory contains some simple examples for both legacy versions of the spec. ## TPM 1.2 TPM 1.2 support currently has no maintainer. None of the TPM 2.0 maintainers have expertise on 1.2 either. As such, TPM 1.2 library is in "maintenance" mode - all PRs with new functionality or non-critical fixes will be rejected. **If you'd like to volunteer to maintain the TPM 1.2 library, you can do so via an [issue](https://github.com/google/go-tpm/issues).** You don't have to be a Googler to volunteer. go-tpm-0.9.3/examples/000077500000000000000000000000001473040456300145525ustar00rootroot00000000000000go-tpm-0.9.3/examples/README.md000066400000000000000000000004751473040456300160370ustar00rootroot00000000000000# go-tpm usage examples This directory contains binaries that show how to use go-tpm. ## Versions Directories that start with `tpm-` are for TPM 1.x devices. Directories that start with `tpm2-` are for TPM 2.x devices. They are not compatible. For example, running `tpm-sign` against a TPM 2.x device will fail. go-tpm-0.9.3/examples/tpm-clear/000077500000000000000000000000001473040456300164365ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm-clear/clear.go000066400000000000000000000024761473040456300200640ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2014, 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 main import ( "crypto/sha1" "flag" "fmt" "os" "github.com/google/go-tpm/tpm" ) var ownerAuthEnvVar = "TPM_OWNER_AUTH" func main() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") flag.Parse() rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } var ownerAuth [20]byte ownerInput := os.Getenv(ownerAuthEnvVar) if ownerInput != "" { oa := sha1.Sum([]byte(ownerInput)) copy(ownerAuth[:], oa[:]) } if err := tpm.OwnerClear(rwc, ownerAuth); err != nil { fmt.Fprintf(os.Stderr, "Couldn't clear the TPM using owner auth: %s\n", err) return } return } go-tpm-0.9.3/examples/tpm-genaik/000077500000000000000000000000001473040456300166065ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm-genaik/genaik.go000066400000000000000000000040111473040456300203670ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2014, 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 main import ( "crypto/sha1" "flag" "fmt" "os" "github.com/google/go-tpm/tpm" ) var ( ownerAuthEnvVar = "TPM_OWNER_AUTH" srkAuthEnvVar = "TPM_SRK_AUTH" aikAuthEnvVar = "TPM_AIK_AUTH" ) func main() { var blobname = flag.String("blob", "aikblob", "The name of the file to create") var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") flag.Parse() rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } // Compute the auth values as needed. var ownerAuth [20]byte ownerInput := os.Getenv(ownerAuthEnvVar) if ownerInput != "" { oa := sha1.Sum([]byte(ownerInput)) copy(ownerAuth[:], oa[:]) } var srkAuth [20]byte srkInput := os.Getenv(srkAuthEnvVar) if srkInput != "" { sa := sha1.Sum([]byte(srkInput)) copy(srkAuth[:], sa[:]) } var aikAuth [20]byte aikInput := os.Getenv(aikAuthEnvVar) if aikInput != "" { aa := sha1.Sum([]byte(aikInput)) copy(aikAuth[:], aa[:]) } // TODO(tmroeder): add support for Privacy CAs. blob, err := tpm.MakeIdentity(rwc, srkAuth[:], ownerAuth[:], aikAuth[:], nil, nil) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't make an new AIK: %s\n", err) return } if err := os.WriteFile(*blobname, blob, 0600); err != nil { fmt.Fprintf(os.Stderr, "Couldn't write to file %s: %s\n", *blobname, err) return } return } go-tpm-0.9.3/examples/tpm-keys/000077500000000000000000000000001473040456300163235ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm-keys/tpm-keys.go000066400000000000000000000031141473040456300204220ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2016, Kevin Walsh. 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 main implements a program to clear key handles from a TPM. package main import ( "flag" "fmt" "os" "github.com/google/go-tpm/tpm" ) func main() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") var closekey = flag.Bool("close", false, "Close (unload) all existing key handles") flag.Parse() rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } handles, err := tpm.GetKeys(rwc) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't enumerate loaded TPM keys: %s\n", err) return } fmt.Printf("%d keys loaded in the TPM\n", len(handles)) for i, h := range handles { fmt.Printf(" (%d) Key handle %d\n", i+1, h) if *closekey { if err = tpm.CloseKey(rwc, h); err != nil { fmt.Fprintf(os.Stderr, "Couldn't close TPM key handle %d\n", h) } else { fmt.Printf(" Closed handle %d\n", h) } } } return } go-tpm-0.9.3/examples/tpm-sign/000077500000000000000000000000001473040456300163105ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm-sign/README.md000066400000000000000000000021071473040456300175670ustar00rootroot00000000000000# tpm-sign This example shows how you can generate keys inside the TPM and use them for signature/verification operations. This utility supports `sign`, `verify`, `generate`, and `extendPcr` actions. Use `./tpm-sign --help` for advanced usage of each action. ## Basic Usage The following snippet shows how you can generate a key, sign data with it, and verify the signature. ``` $ ./tpm-sign generate Writing keyblob to keyblob Writing public key to publickey $ echo test_data | ./tpm-sign sign Writing signature to sig.data $ echo test_data | ./tpm-sign verify Signature valid. ``` ## Binding against PCRs This example shows how you can generate a key that is bound against PCR values. ``` $ ./tpm-sign extendPcr --reset --pcr 16 $ ./tpm-sign generate --pcrs 0,16 Writing keyblob to keyblob Writing public key to publickey $ echo test_data | ./tpm-sign sign Writing signature to sig.data $ echo test_measurement | ./tpm-sign extendPcr --pcr 16 $ echo test_data | ./tpm-sign sign Could not perform sign operation: tpm: the named PCR value does not match the current PCR value ``` go-tpm-0.9.3/examples/tpm-sign/common.go000066400000000000000000000007171473040456300201340ustar00rootroot00000000000000//go:build !windows package main import ( "crypto" ) const ( srkAuthEnvVar = "TPM_SRK_AUTH" usageAuthEnvVar = "TPM_USAGE_AUTH" migrationAuthEnvVar = "TPM_MIGRATION_AUTH" ) var hashNames = map[string]crypto.Hash{ "MD5": crypto.MD5, "SHA1": crypto.SHA1, "SHA224": crypto.SHA224, "SHA256": crypto.SHA256, "SHA384": crypto.SHA384, "SHA512": crypto.SHA512, "MD5SHA1": crypto.MD5SHA1, "RIPEMD160": crypto.RIPEMD160, } go-tpm-0.9.3/examples/tpm-sign/extend_pcr.go000066400000000000000000000035231473040456300207750ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2018, Ian Haken. 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 main import ( "crypto/sha1" "flag" "fmt" "io" "os" "github.com/google/go-tpm/tpm" ) func extendPcrAction() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") var pcrNum = flag.Int("pcr", 16, "PCR number to extend") var reset = flag.Bool("reset", false, "Reset the PCR rather than extending it") var dataPath = flag.String("data", "", "Path to the data that will be used to extend the PCR. If empty or omitted, the data will be read from stdin.") flag.CommandLine.Parse(os.Args[2:]) rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } defer rwc.Close() if *reset { if err = tpm.PcrReset(rwc, []int{*pcrNum}); err != nil { fmt.Fprintf(os.Stderr, "Unable to reset PCR: %s\n", err) return } } else { var data []byte if *dataPath == "" { data, err = io.ReadAll(os.Stdin) } else { data, err = os.ReadFile(*dataPath) } if err != nil { fmt.Fprintf(os.Stderr, "Unable to read input: %s\n", err) return } if _, err = tpm.PcrExtend(rwc, uint32(*pcrNum), sha1.Sum(data)); err != nil { fmt.Fprintf(os.Stderr, "Error extending PCR: %s\n", err) return } } } go-tpm-0.9.3/examples/tpm-sign/generate.go000066400000000000000000000060771473040456300204430ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2018, Ian Haken. 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 main import ( "crypto/sha1" "crypto/x509" "flag" "fmt" "os" "strconv" "strings" "github.com/google/go-tpm/tpm" ) func generateAction() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") var keyblobPath = flag.String("keyblob", "keyblob", "Output path of the generated keyblob") var pubKeyPath = flag.String("public-key", "publickey", "Output path of the generated keyblob's public key") var pcrsStr = flag.String("pcrs", "", "A comma-separated list of PCR numbers against which the generated key will be bound. If blank, it will not be bound to any PCR values.") flag.CommandLine.Parse(os.Args[2:]) var pcrs []int if *pcrsStr != "" { for _, pcr := range strings.Split(*pcrsStr, ",") { pcrNum, err := strconv.Atoi(pcr) if err != nil { fmt.Fprintf(os.Stderr, "Bad value in pcrs argument: %s\n", pcr) return } pcrs = append(pcrs, pcrNum) } } rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } defer rwc.Close() // Compute the auth values as needed. var srkAuth [20]byte srkInput := os.Getenv(srkAuthEnvVar) if srkInput != "" { sa := sha1.Sum([]byte(srkInput)) copy(srkAuth[:], sa[:]) } var usageAuth [20]byte usageInput := os.Getenv(usageAuthEnvVar) if usageInput != "" { ua := sha1.Sum([]byte(usageInput)) copy(usageAuth[:], ua[:]) } var migrationAuth [20]byte migrationInput := os.Getenv(migrationAuthEnvVar) if migrationInput != "" { ma := sha1.Sum([]byte(migrationInput)) copy(migrationAuth[:], ma[:]) } keyblob, err := tpm.CreateWrapKey(rwc, srkAuth[:], usageAuth, migrationAuth, pcrs) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't make a new signing key: %s\n", err) return } fmt.Printf("Writing keyblob to %s\n", *keyblobPath) if err = os.WriteFile(*keyblobPath, keyblob, 0644); err != nil { fmt.Fprintf(os.Stderr, "Error writing keyblob file: %s\n", err) return } pubKey, err := tpm.UnmarshalRSAPublicKey(keyblob) if err != nil { fmt.Fprintf(os.Stderr, "Could not get public key: %s\n", err) return } pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey) if err != nil { fmt.Fprintf(os.Stderr, "Could not marshal public key: %s\n", err) return } fmt.Printf("Writing public key to %s\n", *pubKeyPath) if err = os.WriteFile(*pubKeyPath, pubKeyBytes, 0644); err != nil { fmt.Fprintf(os.Stderr, "Error writing public key file: %s\n", err) return } } go-tpm-0.9.3/examples/tpm-sign/sign.go000066400000000000000000000062261473040456300176050ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2018, Ian Haken. 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 main import ( "crypto/sha1" "flag" "fmt" "io" "os" "github.com/google/go-tpm/tpm" ) func signAction() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") var keyblobPath = flag.String("keyblob", "keyblob", "Input path of the keyblob to use") var signaturePath = flag.String("signature", "sig.data", "Output path of the signature") var hashAlgArg = flag.String("hash", "SHA256", "Hash algorithm to use when generating the signature") var dataPath = flag.String("data", "", "Path to the data that will be signed. If empty or omitted, the data will be read from stdin.") flag.CommandLine.Parse(os.Args[2:]) hashAlg, ok := hashNames[*hashAlgArg] if !ok { fmt.Fprintf(os.Stderr, "Invalid hash algorithm: %s\n", *hashAlgArg) return } rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } defer rwc.Close() // Compute the auth values as needed. var srkAuth [20]byte srkInput := os.Getenv(srkAuthEnvVar) if srkInput != "" { sa := sha1.Sum([]byte(srkInput)) copy(srkAuth[:], sa[:]) } var usageAuth [20]byte usageInput := os.Getenv(usageAuthEnvVar) if usageInput != "" { ua := sha1.Sum([]byte(usageInput)) copy(usageAuth[:], ua[:]) } var migrationAuth [20]byte migrationInput := os.Getenv(migrationAuthEnvVar) if migrationInput != "" { ma := sha1.Sum([]byte(migrationInput)) copy(migrationAuth[:], ma[:]) } keyblob, err := os.ReadFile(*keyblobPath) if err != nil { fmt.Fprintf(os.Stderr, "Error reading keyblob file: %s\n", err) return } keyHandle, err := tpm.LoadKey2(rwc, keyblob, srkAuth[:]) if err != nil { fmt.Fprintf(os.Stderr, "Could not load keyblob: %s\n", err) return } defer tpm.CloseKey(rwc, keyHandle) var data []byte if *dataPath == "" { data, err = io.ReadAll(os.Stdin) } else { data, err = os.ReadFile(*dataPath) } if err != nil { fmt.Fprintf(os.Stderr, "Error reading input data: %s\n", err) return } hash := hashAlg.New() if _, err = hash.Write(data); err != nil { fmt.Fprintf(os.Stderr, "Error building hash of data: %s\n", err) return } hashed := hash.Sum(nil) signature, err := tpm.Sign(rwc, usageAuth[:], keyHandle, hashAlg, hashed[:]) if err != nil { fmt.Fprintf(os.Stderr, "Could not perform sign operation: %s\n", err) return } fmt.Printf("Writing signature to %s\n", *signaturePath) if err = os.WriteFile(*signaturePath, signature, 0644); err != nil { fmt.Fprintf(os.Stderr, "Unable to write signature to file: %s\n", err) return } } go-tpm-0.9.3/examples/tpm-sign/tpm-sign.go000066400000000000000000000020111473040456300203670ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2018, Ian Haken. 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 main import ( "fmt" "os" ) func showUsage() { fmt.Println("Usage: ./tpm-sign [...]") } func main() { if len(os.Args) < 2 { showUsage() } else { switch os.Args[1] { case "sign": signAction() case "verify": verifyAction() case "generate": generateAction() case "extendPcr": extendPcrAction() default: showUsage() } } } go-tpm-0.9.3/examples/tpm-sign/verify.go000066400000000000000000000047361473040456300201550ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2018, Ian Haken. 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 main import ( "crypto/rsa" "crypto/x509" "flag" "fmt" "io" "os" ) func verifyAction() { var pubKeyPath = flag.String("public-key", "publickey", "Input path of public key file") var hashAlgArg = flag.String("hash", "SHA256", "Hash algorithm to use when verifying the signature") var signaturePath = flag.String("signature", "sig.data", "Input path of previously generated signature") var dataPath = flag.String("data", "", "Path to the data that was signed. If empty or omitted, the data will be read from stdin.") flag.CommandLine.Parse(os.Args[2:]) hashAlg, ok := hashNames[*hashAlgArg] if !ok { fmt.Fprintf(os.Stderr, "Invalid hash algorithm: %s\n", *hashAlgArg) return } var err error var data []byte if *dataPath == "" { data, err = io.ReadAll(os.Stdin) } else { data, err = os.ReadFile(*dataPath) } if err != nil { fmt.Fprintf(os.Stderr, "Error reading input data: %s\n", err) return } signature, err := os.ReadFile(*signaturePath) if err != nil { fmt.Fprintf(os.Stderr, "Error reading signature file: %s\n", err) return } pubKeyBytes, err := os.ReadFile(*pubKeyPath) if err != nil { fmt.Fprintf(os.Stderr, "Error reading public key file: %s\n", err) return } pubKey, err := x509.ParsePKIXPublicKey(pubKeyBytes) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing public key: %s\n", err) return } rsaPubKey, ok := pubKey.(*rsa.PublicKey) if !ok { fmt.Fprintf(os.Stderr, "Expected public key to be an RSA key, but was %T\n", pubKey) return } hash := hashAlg.New() if _, err = hash.Write(data); err != nil { fmt.Fprintf(os.Stderr, "Error building hash of data: %s\n", err) return } hashed := hash.Sum(nil) if err = rsa.VerifyPKCS1v15(rsaPubKey, hashAlg, hashed[:], signature); err != nil { fmt.Fprintf(os.Stderr, "Error from verification: %s\n", err) return } fmt.Printf("Signature valid.\n") } go-tpm-0.9.3/examples/tpm-takeownership/000077500000000000000000000000001473040456300202335ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm-takeownership/own.go000066400000000000000000000032661473040456300213740ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2014, 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 main import ( "crypto/sha1" "flag" "fmt" "os" "github.com/google/go-tpm/tpm" ) var ( ownerAuthEnvVar = "TPM_OWNER_AUTH" srkAuthEnvVar = "TPM_SRK_AUTH" ) func main() { var tpmname = flag.String("tpm", "/dev/tpm0", "The path to the TPM device to use") flag.Parse() rwc, err := tpm.OpenTPM(*tpmname) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't open the TPM file %s: %s\n", *tpmname, err) return } // Compute the auth values as needed. var ownerAuth [20]byte ownerInput := os.Getenv(ownerAuthEnvVar) if ownerInput != "" { oa := sha1.Sum([]byte(ownerInput)) copy(ownerAuth[:], oa[:]) } var srkAuth [20]byte srkInput := os.Getenv(srkAuthEnvVar) if srkInput != "" { sa := sha1.Sum([]byte(srkInput)) copy(srkAuth[:], sa[:]) } pubEK, err := tpm.ReadPubEK(rwc) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't read the endorsement key: %s\n", err) return } if err := tpm.TakeOwnership(rwc, ownerAuth, srkAuth, pubEK); err != nil { fmt.Fprintf(os.Stderr, "Couldn't take ownership of the TPM: %s\n", err) return } return } go-tpm-0.9.3/examples/tpm2-ekcert/000077500000000000000000000000001473040456300167075ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm2-ekcert/main.go000066400000000000000000000065341473040456300201720ustar00rootroot00000000000000//go:build !windows // Binary tpm2-ekcert reads an x509 certificate from a specific NVRAM index. package main import ( "crypto" "crypto/x509" "errors" "flag" "fmt" "os" "reflect" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) var ( tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket)") // Default value is defined in section 7.8, "NV Memory" of the latest version pdf on: // https://trustedcomputinggroup.org/resource/tcg-tpm-v2-0-provisioning-guidance/ certIndex = flag.Uint("cert-index", 0x01C00002, "NVRAM index of the certificate file") tmplIndex = flag.Uint("template-index", 0, "NVRAM index of the EK template; if zero, default RSA EK template is used") outPath = flag.String("output", "", "File path for output; leave blank to write to stdout") // Default EK template defined in: // https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf defaultEKTemplate = tpm2.Public{ Type: tpm2.AlgRSA, NameAlg: tpm2.AlgSHA256, Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagAdminWithPolicy | tpm2.FlagRestricted | tpm2.FlagDecrypt, AuthPolicy: []byte{ 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, }, RSAParameters: &tpm2.RSAParams{ Symmetric: &tpm2.SymScheme{ Alg: tpm2.AlgAES, KeyBits: 128, Mode: tpm2.AlgCFB, }, KeyBits: 2048, ModulusRaw: make([]byte, 256), }, } ) func main() { flag.Parse() cert, err := readEKCert(*tpmPath, uint32(*certIndex), uint32(*tmplIndex)) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if *outPath == "" { fmt.Println(string(cert)) return } if err := os.WriteFile(*outPath, cert, os.ModePerm); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func readEKCert(path string, certIdx, tmplIdx uint32) ([]byte, error) { rwc, err := tpm2.OpenTPM(path) if err != nil { return nil, fmt.Errorf("can't open TPM at %q: %v", path, err) } defer rwc.Close() ekCert, err := tpm2.NVRead(rwc, tpmutil.Handle(certIdx)) if err != nil { return nil, fmt.Errorf("reading EK cert: %v", err) } // Sanity-check that this is a valid certificate. cert, err := x509.ParseCertificate(ekCert) if err != nil { return nil, fmt.Errorf("parsing EK cert: %v", err) } // Initialize EK and compare public key to ekCert.PublicKey. var ekh tpmutil.Handle var ekPub crypto.PublicKey if tmplIdx != 0 { ekTemplate, err := tpm2.NVRead(rwc, tpmutil.Handle(tmplIdx)) if err != nil { return nil, fmt.Errorf("reading EK template: %v", err) } ekh, ekPub, err = tpm2.CreatePrimaryRawTemplate(rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", ekTemplate) if err != nil { return nil, fmt.Errorf("creating EK: %v", err) } } else { ekh, ekPub, err = tpm2.CreatePrimary(rwc, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", defaultEKTemplate) if err != nil { return nil, fmt.Errorf("creating EK: %v", err) } } defer tpm2.FlushContext(rwc, ekh) if !reflect.DeepEqual(ekPub, cert.PublicKey) { return nil, errors.New("public key in EK certificate differs from public key created via EK template") } return ekCert, nil } go-tpm-0.9.3/examples/tpm2-nvread/000077500000000000000000000000001473040456300167115ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm2-nvread/main.go000066400000000000000000000017611473040456300201710ustar00rootroot00000000000000//go:build !windows // Binary tpm2-nvread reads data from NVRAM at a specified index. The data is // printed out hex-encoded. package main import ( "flag" "fmt" "os" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) var ( tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket)") index = flag.Uint("index", 0, "NVRAM index of read") ) func main() { flag.Parse() if *index == 0 { fmt.Fprintln(os.Stderr, "--index must be set") os.Exit(1) } val, err := nvRead(*tpmPath, uint32(*index)) if err != nil { fmt.Fprintf(os.Stderr, "reading from index 0x%x: %v\n", *index, err) os.Exit(1) } fmt.Printf("NVRAM value at index 0x%x (hex encoded):\n%x\n", *index, val) } func nvRead(path string, index uint32) ([]byte, error) { rwc, err := tpm2.OpenTPM(path) if err != nil { return nil, fmt.Errorf("can't open TPM at %q: %v", path, err) } defer rwc.Close() return tpm2.NVRead(rwc, tpmutil.Handle(index)) } go-tpm-0.9.3/examples/tpm2-seal-unseal/000077500000000000000000000000001473040456300176435ustar00rootroot00000000000000go-tpm-0.9.3/examples/tpm2-seal-unseal/main.go000066400000000000000000000167141473040456300211270ustar00rootroot00000000000000//go: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. // Command tpm2-seal-unseal illustrates utilizing the TPM2 API to seal and unseal data. package main import ( "flag" "fmt" "io" "os" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) var ( // Default EK template defined in: // https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf // Shared SRK template based off of EK template and specified in: // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf srkTemplate = tpm2.Public{ Type: tpm2.AlgRSA, NameAlg: tpm2.AlgSHA256, Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagRestricted | tpm2.FlagDecrypt | tpm2.FlagNoDA, AuthPolicy: nil, RSAParameters: &tpm2.RSAParams{ Symmetric: &tpm2.SymScheme{ Alg: tpm2.AlgAES, KeyBits: 128, Mode: tpm2.AlgCFB, }, KeyBits: 2048, ModulusRaw: make([]byte, 256), }, } tpmPath = flag.String("tpm-path", "/dev/tpm0", "Path to the TPM device (character device or a Unix socket).") pcr = flag.Int("pcr", -1, "PCR to seal data to. Must be within [0, 23].") ) func main() { flag.Parse() if *pcr < 0 || *pcr > 23 { fmt.Fprintf(os.Stderr, "Invalid flag 'pcr': value %d is out of range", *pcr) os.Exit(1) } err := run(*pcr, *tpmPath) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } func run(pcr int, tpmPath string) (retErr error) { // Open the TPM rwc, err := tpm2.OpenTPM(tpmPath) if err != nil { return fmt.Errorf("can't open TPM %q: %v", tpmPath, err) } defer func() { if err := rwc.Close(); err != nil { retErr = fmt.Errorf("%v\ncan't close TPM %q: %v", retErr, tpmPath, err) } }() // Create the parent key against which to seal the data srkPassword := "" srkHandle, _, err := tpm2.CreatePrimary(rwc, tpm2.HandleOwner, tpm2.PCRSelection{}, "", srkPassword, srkTemplate) if err != nil { return fmt.Errorf("can't create primary key: %v", err) } defer func() { if err := tpm2.FlushContext(rwc, srkHandle); err != nil { retErr = fmt.Errorf("%v\nunable to flush SRK handle %q: %v", retErr, srkHandle, err) } }() fmt.Printf("Created parent key with handle: 0x%x\n", srkHandle) // Note the value of the pcr against which we will seal the data pcrVal, err := tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256) if err != nil { return fmt.Errorf("unable to read PCR: %v", err) } fmt.Printf("PCR %v value: 0x%x\n", pcr, pcrVal) // Get the authorization policy that will protect the data to be sealed objectPassword := "objectPassword" sessHandle, policy, err := policyPCRPasswordSession(rwc, pcr, objectPassword) if err != nil { return fmt.Errorf("unable to get policy: %v", err) } if err := tpm2.FlushContext(rwc, sessHandle); err != nil { return fmt.Errorf("unable to flush session: %v", err) } fmt.Printf("Created authorization policy: 0x%x\n", policy) // Seal the data to the parent key and the policy dataToSeal := []byte("secret") fmt.Printf("Data to be sealed: 0x%x\n", dataToSeal) privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, objectPassword, policy, dataToSeal) if err != nil { return fmt.Errorf("unable to seal data: %v", err) } fmt.Printf("Sealed data: 0x%x\n", privateArea) // Load the sealed data into the TPM. objectHandle, _, err := tpm2.Load(rwc, srkHandle, srkPassword, publicArea, privateArea) if err != nil { return fmt.Errorf("unable to load data: %v", err) } defer func() { if err := tpm2.FlushContext(rwc, objectHandle); err != nil { retErr = fmt.Errorf("%v\nunable to flush object handle %q: %v", retErr, objectHandle, err) } }() fmt.Printf("Loaded sealed data with handle: 0x%x\n", objectHandle) // Unseal the data unsealedData, err := unseal(rwc, pcr, objectPassword, objectHandle) if err != nil { return fmt.Errorf("unable to unseal data: %v", err) } fmt.Printf("Unsealed data: 0x%x\n", unsealedData) // Try to unseal the data with the wrong password _, err = unseal(rwc, pcr, "wrong-password", objectHandle) fmt.Printf("Trying to unseal with wrong password resulted in: %v\n", err) // Extend the PCR if err := tpm2.PCREvent(rwc, tpmutil.Handle(pcr), []byte{1}); err != nil { return fmt.Errorf("unable to extend PCR: %v", err) } fmt.Printf("Extended PCR %d\n", pcr) // Note the new value of the pcr pcrVal, err = tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256) if err != nil { return fmt.Errorf("unable to read PCR: %v", err) } fmt.Printf("PCR %d value: 0x%x\n", pcr, pcrVal) // Try to unseal the data with the PCR in the wrong state _, err = unseal(rwc, pcr, objectPassword, objectHandle) fmt.Printf("Trying to unseal with wrong PCR state resulted in: %v\n", err) return } // Returns the unsealed data func unseal(rwc io.ReadWriteCloser, pcr int, objectPassword string, objectHandle tpmutil.Handle) (data []byte, retErr error) { // Create the authorization session sessHandle, _, err := policyPCRPasswordSession(rwc, pcr, objectPassword) if err != nil { return nil, fmt.Errorf("unable to get auth session: %v", err) } defer func() { if err := tpm2.FlushContext(rwc, sessHandle); err != nil { retErr = fmt.Errorf("%v\nunable to flush session: %v", retErr, err) } }() // Unseal the data unsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword) if err != nil { return nil, fmt.Errorf("unable to unseal data: %v", err) } return unsealedData, nil } // Returns session handle and policy digest. func policyPCRPasswordSession(rwc io.ReadWriteCloser, pcr int, password string) (sessHandle tpmutil.Handle, policy []byte, retErr error) { // FYI, this is not a very secure session. sessHandle, _, err := tpm2.StartAuthSession( rwc, tpm2.HandleNull, /*tpmKey*/ tpm2.HandleNull, /*bindKey*/ make([]byte, 16), /*nonceCaller*/ nil, /*secret*/ tpm2.SessionPolicy, tpm2.AlgNull, tpm2.AlgSHA256) if err != nil { return tpm2.HandleNull, nil, fmt.Errorf("unable to start session: %v", err) } defer func() { if sessHandle != tpm2.HandleNull && err != nil { if err := tpm2.FlushContext(rwc, sessHandle); err != nil { retErr = fmt.Errorf("%v\nunable to flush session: %v", retErr, err) } } }() pcrSelection := tpm2.PCRSelection{ Hash: tpm2.AlgSHA256, PCRs: []int{pcr}, } // An empty expected digest means that digest verification is skipped. if err := tpm2.PolicyPCR(rwc, sessHandle, nil /*expectedDigest*/, pcrSelection); err != nil { return sessHandle, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err) } if err := tpm2.PolicyPassword(rwc, sessHandle); err != nil { return sessHandle, nil, fmt.Errorf("unable to require password for auth policy: %v", err) } policy, err = tpm2.PolicyGetDigest(rwc, sessHandle) if err != nil { return sessHandle, nil, fmt.Errorf("unable to get policy digest: %v", err) } return sessHandle, policy, nil } go-tpm-0.9.3/go.mod000066400000000000000000000002661473040456300140460ustar00rootroot00000000000000module github.com/google/go-tpm go 1.22 require ( github.com/google/go-cmp v0.5.9 github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba golang.org/x/sys v0.8.0 ) go-tpm-0.9.3/go.sum000066400000000000000000000033641473040456300140750ustar00rootroot00000000000000github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0= github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc= github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= go-tpm-0.9.3/legacy/000077500000000000000000000000001473040456300142005ustar00rootroot00000000000000go-tpm-0.9.3/legacy/tpm2/000077500000000000000000000000001473040456300150625ustar00rootroot00000000000000go-tpm-0.9.3/legacy/tpm2/README.md000066400000000000000000000027241473040456300163460ustar00rootroot00000000000000# 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). go-tpm-0.9.3/legacy/tpm2/constants.go000066400000000000000000000412471473040456300174350ustar00rootroot00000000000000// 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) } func (a Algorithm) String() string { var s strings.Builder var err error switch a { case AlgUnknown: _, err = s.WriteString("AlgUnknown") case AlgRSA: _, err = s.WriteString("RSA") case AlgSHA1: _, err = s.WriteString("SHA1") case AlgHMAC: _, err = s.WriteString("HMAC") case AlgAES: _, err = s.WriteString("AES") case AlgKeyedHash: _, err = s.WriteString("KeyedHash") case AlgXOR: _, err = s.WriteString("XOR") case AlgSHA256: _, err = s.WriteString("SHA256") case AlgSHA384: _, err = s.WriteString("SHA384") case AlgSHA512: _, err = s.WriteString("SHA512") case AlgNull: _, err = s.WriteString("AlgNull") case AlgRSASSA: _, err = s.WriteString("RSASSA") case AlgRSAES: _, err = s.WriteString("RSAES") case AlgRSAPSS: _, err = s.WriteString("RSAPSS") case AlgOAEP: _, err = s.WriteString("OAEP") case AlgECDSA: _, err = s.WriteString("ECDSA") case AlgECDH: _, err = s.WriteString("ECDH") case AlgECDAA: _, err = s.WriteString("ECDAA") case AlgKDF2: _, err = s.WriteString("KDF2") case AlgECC: _, err = s.WriteString("ECC") case AlgSymCipher: _, err = s.WriteString("SymCipher") case AlgSHA3_256: _, err = s.WriteString("SHA3_256") case AlgSHA3_384: _, err = s.WriteString("SHA3_384") case AlgSHA3_512: _, err = s.WriteString("SHA3_512") case AlgCTR: _, err = s.WriteString("CTR") case AlgOFB: _, err = s.WriteString("OFB") case AlgCBC: _, err = s.WriteString("CBC") case AlgCFB: _, err = s.WriteString("CFB") case AlgECB: _, err = s.WriteString("ECB") default: return fmt.Sprintf("Alg?<%d>", int(a)) } if err != nil { return fmt.Sprintf("Writing to string builder failed: %v", err) } return s.String() } // 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 TagAuthSecret tpmutil.Tag = 0x8023 TagHashCheck tpmutil.Tag = 0x8024 TagAuthSigned tpmutil.Tag = 0x8025 ) // 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 CmdPCRReset tpmutil.Command = 0x0000013D 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 CmdPolicySigned tpmutil.Command = 0x00000160 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(), " + ") } go-tpm-0.9.3/legacy/tpm2/credactivation/000077500000000000000000000000001473040456300200615ustar00rootroot00000000000000go-tpm-0.9.3/legacy/tpm2/credactivation/credential_activation.go000066400000000000000000000150711473040456300247470ustar00rootroot00000000000000// 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 credactivation implements generation of data blobs to be used // when invoking the ActivateCredential command, on a TPM. package credactivation import ( "crypto" "crypto/aes" "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" "crypto/hmac" "crypto/rand" "crypto/rsa" "errors" "fmt" "io" "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) // Labels for use in key derivation or OAEP encryption. const ( labelIdentity = "IDENTITY" labelStorage = "STORAGE" labelIntegrity = "INTEGRITY" ) // Generate returns a TPM2B_ID_OBJECT & TPM2B_ENCRYPTED_SECRET for use in // credential activation. // This has been tested on EKs compliant with TCG 2.0 EK Credential Profile // specification, revision 14. // The pub parameter must be a pointer to rsa.PublicKey. // The secret parameter must not be longer than the longest digest size implemented // by the TPM. A 32 byte secret is a safe, recommended default. // // This function implements Credential Protection as defined in section 24 of the TPM // specification revision 2 part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ func Generate(aik *tpm2.HashValue, pub crypto.PublicKey, symBlockSize int, secret []byte) ([]byte, []byte, error) { return generate(aik, pub, symBlockSize, secret, rand.Reader) } func generate(aik *tpm2.HashValue, pub crypto.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) { var seed, encSecret []byte var err error switch ekKey := pub.(type) { case *ecdh.PublicKey: seed, encSecret, err = createECSeed(aik, ekKey, rnd) if err != nil { return nil, nil, fmt.Errorf("creating seed: %v", err) } case *ecdsa.PublicKey: ecdhKey, err := ekKey.ECDH() if err != nil { return nil, nil, fmt.Errorf("transmuting ecdsa key to ecdh key: %v", err) } return generate(aik, ecdhKey, symBlockSize, secret, rnd) case *rsa.PublicKey: seed, encSecret, err = createRSASeed(aik, ekKey, symBlockSize, rnd) if err != nil { return nil, nil, fmt.Errorf("creating seed: %v", err) } default: return nil, nil, errors.New("only RSA and EC public keys are supported for credential activation") } // Generate the encrypted credential by convolving the seed with the digest of // the AIK, and using the result as the key to encrypt the secret. // See section 24.4 of TPM 2.0 specification, part 1. aikNameEncoded, err := aik.Encode() if err != nil { return nil, nil, fmt.Errorf("encoding aikName: %v", err) } symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, symBlockSize*8) if err != nil { return nil, nil, fmt.Errorf("generating symmetric key: %v", err) } c, err := aes.NewCipher(symmetricKey) if err != nil { return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err) } cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret)) if err != nil { return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err) } // IV is all null bytes. encIdentity represents the encrypted credential. encIdentity := make([]byte, len(cv)) cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv) // Generate the integrity HMAC, which is used to protect the integrity of the // encrypted structure. // See section 24.5 of the TPM 2.0 specification. cryptohash, err := aik.Alg.Hash() if err != nil { return nil, nil, err } macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, cryptohash.Size()*8) if err != nil { return nil, nil, fmt.Errorf("generating HMAC key: %v", err) } mac := hmac.New(cryptohash.New, macKey) mac.Write(encIdentity) mac.Write(aikNameEncoded) integrityHMAC := mac.Sum(nil) idObject := &tpm2.IDObject{ IntegrityHMAC: integrityHMAC, EncIdentity: encIdentity, } id, err := tpmutil.Pack(idObject) if err != nil { return nil, nil, fmt.Errorf("encoding IDObject: %v", err) } packedID, err := tpmutil.Pack(tpmutil.U16Bytes(id)) if err != nil { return nil, nil, fmt.Errorf("packing id: %v", err) } packedEncSecret, err := tpmutil.Pack(tpmutil.U16Bytes(encSecret)) if err != nil { return nil, nil, fmt.Errorf("packing encSecret: %v", err) } return packedID, packedEncSecret, nil } func createRSASeed(aik *tpm2.HashValue, ek *rsa.PublicKey, symBlockSize int, rnd io.Reader) ([]byte, []byte, error) { crypothash, err := aik.Alg.Hash() if err != nil { return nil, nil, err } // The seed length should match the keysize used by the EKs symmetric cipher. // For typical RSA EKs, this will be 128 bits (16 bytes). // Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1. seed := make([]byte, symBlockSize) if _, err := io.ReadFull(rnd, seed); err != nil { return nil, nil, fmt.Errorf("generating seed: %v", err) } // Encrypt the seed value using the provided public key. // See annex B, section 10.4 of the TPM specification revision 2 part 1. label := append([]byte(labelIdentity), 0) encryptedSeed, err := rsa.EncryptOAEP(crypothash.New(), rnd, ek, seed, label) if err != nil { return nil, nil, fmt.Errorf("generating encrypted seed: %v", err) } encryptedSeed, err = tpmutil.Pack(encryptedSeed) return seed, encryptedSeed, err } func createECSeed(ak *tpm2.HashValue, ek *ecdh.PublicKey, rnd io.Reader) (seed, encryptedSeed []byte, err error) { ephemeralPriv, err := ek.Curve().GenerateKey(rnd) if err != nil { return nil, nil, err } ephemeralX, ephemeralY := deconstructECDHPublicKey(ephemeralPriv.PublicKey()) z, err := ephemeralPriv.ECDH(ek) if err != nil { return nil, nil, err } ekX, _ := deconstructECDHPublicKey(ek) crypothash, err := ak.Alg.Hash() if err != nil { return nil, nil, err } seed, err = tpm2.KDFe( ak.Alg, z, labelIdentity, ephemeralX, ekX, crypothash.Size()*8) if err != nil { return nil, nil, err } encryptedSeed, err = tpmutil.Pack(tpmutil.U16Bytes(ephemeralX), tpmutil.U16Bytes(ephemeralY)) return seed, encryptedSeed, err } func deconstructECDHPublicKey(key *ecdh.PublicKey) (x []byte, y []byte) { b := key.Bytes()[1:] return b[:len(b)/2], b[len(b)/2:] } go-tpm-0.9.3/legacy/tpm2/credactivation/credential_activation_test.go000066400000000000000000000066061473040456300260120ustar00rootroot00000000000000// 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 credactivation import ( "bytes" "crypto/x509" "encoding/base64" "encoding/pem" "testing" "github.com/google/go-tpm/legacy/tpm2" ) var ( eccEKPub = []byte(`-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsIfixqsUp8cJBSeYDhJKCZ32eAHF 3rS7HdTMOnoFj1MrX+PutPTxa6SFdhWGLnhEQyfcwRni8veQX/dSP2on2w== -----END PUBLIC KEY-----`) rsaEKPub = []byte(`-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArIqmuAvuIcakIEPd2hZl avob21ehQ7zaHduJQNbNuKVSc1HTlvw9DkWN03b0SktcRIfsjw/omqPl60RhCx0j qxsYnf5Gk4jhfCnUeQVicAqHnUGrKjMkLIGTZVOpyqBEXHsdhugw6M5HVIKyfwNO KhvLZKRH8JkvtElVhLQ6E2+H83XoSpkt9oCnGPyN2Z5qRP+fhQiRylMCD8Rz8ABn YVqGBBrG+2cBt/0uFLjxHx2mm/4sI/1scG5xrcrDLva9WZB40MehW5VlS6Fwqq05 dKtLGpk7ludjH38m2zhM5/UdKZ34skJaS/Aiyj+P5AT1BpJL2ZtjCbBdnMDUSbRF 1QIDAQAB -----END PUBLIC KEY-----`) ) type zeroReader struct{} func (zeroReader) Read(b []byte) (int, error) { for i := range b { b[i] = 0 } return len(b), nil } func mustDecodeBase64(in string, t *testing.T) []byte { d, err := base64.StdEncoding.DecodeString(in) if err != nil { t.Fatal(err) } return d } func TestCredentialActivation(t *testing.T) { var activateTests = []struct { ekPub []byte expected string }{ { eccEKPub, "AEQAIE4SquOcMAzLi7f3ru6P8nuIpSmzr8OTACXpzLo3PTOe/oBazv+fZF2JiKDZqNPNeHISCsZMdEtEfvyYjqmzZ/CB7ABEACAeGDfvDRlRiDV1cbXlVFsSLo8JZ/2nJCA+slYczpcoXgAg+CstT57xB59sS1uDVuIyQulYttdJprVoGkEDVmvcWok=", }, { rsaEKPub, "AEQAIFjKZAUo3Wmgxu+CqHFzsQZr7BqawtprBmpXpZa77nb5S+iN6IcSPQLCKPZMNuunv7BIb4/VJA/xjMrj8RnQbjspCwEAJcQogjACOfStYTVjmR4p61ZbTTRt7ZNG5nc6iifq+TfnyfoU+E3T6Kount4M8fSUdMWlKx5A24Ms4ndi1VYOA+s4inPusyn1X1ZCHe5tNwT1E9jpVxc0jaUAVad6Q5cOgUyAp4qvc8wmaYXcIa/PzVfa6teF4iXxNqVDAYqpdmbP68v0Hk5gRqCa/tHAdg5avE3C20DP1SSvPitumWROL6mHMooVxjsyjPnHEBLo7y/BKwezEO/15xnBvPOvWs7ARIu1KdER+zrCJX9SMCPbn4cVMfLdrX70xko7XjdhV7pXtAeUeKmmKSYE45m5ZN0h83YgHXGDjf+ynWse10okyA==", }, } for _, test := range activateTests { p, _ := pem.Decode(test.ekPub) public, err := x509.ParsePKIXPublicKey(p.Bytes) if err != nil { t.Fatal(err) } aikDigest := mustDecodeBase64("5snpf9qRfKD2Tb72eLAZqC/a/MyUhg+IvdwDZkTJK9w=", t) expected := mustDecodeBase64(test.expected, t) secret := mustDecodeBase64("AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=", t) aikName := &tpm2.HashValue{ Alg: tpm2.AlgSHA256, Value: aikDigest, } idObject, wrappedCredential, err := generate(aikName, public, 16, secret, zeroReader{}) if err != nil { t.Fatal(err) } activationBlob := append(idObject, wrappedCredential...) if !bytes.Equal(expected, activationBlob) { t.Errorf("generate(%v, %v, %v) returned incorrect result", aikName, public, secret) t.Logf(" Got: %v", base64.StdEncoding.EncodeToString(activationBlob)) t.Logf(" Want: %v", base64.StdEncoding.EncodeToString(expected)) } } } go-tpm-0.9.3/legacy/tpm2/encoding_test.go000066400000000000000000000424611473040456300202450ustar00rootroot00000000000000// 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/elliptic" "crypto/rand" "encoding/hex" "reflect" "testing" "github.com/google/go-tpm/tpmutil" ) var ( pcrSelection0 = PCRSelection{Hash: AlgSHA1, PCRs: []int{0}} pcrSelection1 = PCRSelection{Hash: AlgSHA1, PCRs: []int{1}} pcrSelection7 = PCRSelection{Hash: AlgSHA1, PCRs: []int{7}} defaultPassword = "\x01\x02\x03\x04" ) func TestEncodeDecodeCreationData(t *testing.T) { parentQualified := tpmutil.Handle(101) cd := CreationData{ PCRSelection: PCRSelection{Hash: AlgSHA1, PCRs: []int{7}}, PCRDigest: []byte{1, 2, 3}, Locality: 32, ParentNameAlg: AlgSHA1, ParentName: Name{ Digest: &HashValue{ Alg: AlgSHA1, Value: make([]byte, crypto.SHA1.Size()), }, }, ParentQualifiedName: Name{ Handle: &parentQualified, }, OutsideInfo: []byte{7, 8, 9}, } encoded, err := cd.EncodeCreationData() if err != nil { t.Fatalf("error encoding CreationData: %v", err) } decoded, err := DecodeCreationData(encoded) if err != nil { t.Fatalf("error decoding CreationData: %v", err) } if !reflect.DeepEqual(*decoded, cd) { t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, cd) } } func TestDecodeReadPCRs(t *testing.T) { testRespBytes, err := hex.DecodeString("800100000032000000000000001400000001000403800000000000010014427d27fe15f8f69736e02b6007b8f6ea674c0745") if err != nil { t.Fatal(err) } if _, err = decodeReadPCRs(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestSingleEncodeDecodeTPMLSelection(t *testing.T) { buf, err := encodeTPMLPCRSelection(pcrSelection7) if err != nil { t.Fatal(err) } got, err := decodeOneTPMLPCRSelection(bytes.NewBuffer(buf)) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, pcrSelection7) { t.Errorf("after decoding: %+v, before encoding: %+v", got, pcrSelection7) } } func TestMultiArgsEncodeDecodeTPMLSelection(t *testing.T) { multipcrselection := []PCRSelection{pcrSelection0, pcrSelection1, pcrSelection7} buf, err := encodeTPMLPCRSelection(multipcrselection...) if err != nil { t.Fatal(err) } got, err := decodeTPMLPCRSelection(bytes.NewBuffer(buf)) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, multipcrselection) { t.Errorf("after decoding: %+v, before encoding %+v", got, multipcrselection) } } func TestDecodeReadClock(t *testing.T) { testRespBytes, err := hex.DecodeString("8001000000230000000000000001011380d00000001d1f57f84d000000530000000001") if err != nil { t.Fatal(err) } if _, _, err = decodeReadClock(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestDecodeGetCapability(t *testing.T) { testRespBytes, err := hex.DecodeString("80010000001300000000000000000100000000") if err != nil { t.Fatal(err) } handles, moreData, err := decodeGetCapability(testRespBytes[10:]) if err != nil { t.Fatal(err) } if len(handles) != 0 || moreData { t.Fatalf("got: (%v, %v), want: (%v, %v)", handles, moreData, 0, false) } } func TestEncodeLoad(t *testing.T) { testCmdBytes, err := hex.DecodeString("8002000000b300000157800000000000000d40000009000001000401020" + "304005a0014450ecdce5f1ce202e4f8db15e2bde9a1241f85f30010faf6" + "2244fedc13fe0abb526e64b10b2de030b6f02be278e23365ef663febe7e" + "b4ddae935ca627ce4c40af9f5244dafbc7f47ceb84de87e72a75c7f1032" + "d3e7faddde0036000800040000001200140debb4cc9d2158cf7051a19ca" + "24b31e35d53b64d001000140b0758c7e4ce32c9d249151e91b72e35a6372fed") if err != nil { t.Fatal(err) } privateBlob := testCmdBytes[33:123] publicBlob := testCmdBytes[125:] auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)} cmdBytes, err := encodeLoad(tpmutil.Handle(0x80000000), auth, publicBlob, privateBlob) if err != nil { t.Fatalf("encodeLoad failed %s", err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got:\n%v\nwant:\n%v", cmdBytes, testCmdBytes[10:]) } } func TestDecodeLoad(t *testing.T) { testRespBytes, err := hex.DecodeString("80020000002f000000008000000100000018001600049bc5e230c250b7d984d757f6450f575a5a896ad00000010000") if err != nil { t.Fatal(err) } if _, _, err = decodeLoad(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestEncodeCreate(t *testing.T) { testCmdBytes, err := hex.DecodeString("80020000004d00000131400000010000000940000009000001000000090004010203040001ff001a000100040003007200000006008000430010040000010001000000045445535400000001000403800000") if err != nil { t.Fatal(err) } params := Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagStorageDefault, RSAParameters: &RSAParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, KeyBits: 1024, ExponentRaw: defaultRSAExponent, }, } auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession} cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, auth, defaultPassword, []byte{255} /*sensitiveData*/, params, []byte("TEST") /*OutsideInfo*/) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got: %v, want: %v", cmdBytes, testCmdBytes[10:]) } } func TestDecodeCreatePrimary(t *testing.T) { testRespBytes, err := hex.DecodeString("80020000013c000000008000000000000125009a00010" + "00400030072000000060080004300100400000100010080afe42d93b037f25f5f4" + "a92bd65d61b417b51041f057e08670da98bb4720df166d8c0e12cd651196e0e577" + "828e65f0e9b0a0da4181bc6553e35970f8b4a6c1790c6132359c62f45952a6e377" + "9256de208b996bf2d216fdcfbddd4bdcb0e0cf9fd454caa9604d867e7d7901353d" + "1ccd23e16c7a53788f57b602449b0ecaf0590fb003100000001000403800000001" + "4bbf70aea75095f280ea3b835afda4a195279ab2c0100100004400000010004400" + "00001000000141a1ea8de55d7410287405c3b54057d578d76444a8021400000010" + "020e74aa1a8f272b604d6c0cf55b271211a130c011a12b0ba632cc1448c4de8371" + "3001600043adbc7b1296c49aac7c154371fd99aeb6e58a9f50000010000") if err != nil { t.Fatal(err) } if _, _, _, _, _, _, err = decodeCreatePrimary(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestEncodePolicyPCR(t *testing.T) { testCmdBytes, err := hex.DecodeString("80010000001a0000017f03000000000000000001000403800000") if err != nil { t.Fatal(err) } cmdBytes, err := encodePolicyPCR(tpmutil.Handle(0x03000000), []byte(nil), pcrSelection7) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got: %v, want: %v", cmdBytes, testCmdBytes[10:]) } } func TestDecodeStartAuthSession(t *testing.T) { testRespBytes, err := hex.DecodeString("800100000020000000000300000000106cf0c90c419ce1a96d5205eb870ec527") if err != nil { t.Fatal(err) } if _, _, err = decodeStartAuthSession(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestDecodeCreate(t *testing.T) { testRespBytes, err := hex.DecodeString("8002000001ba00000000000001a70076001405f2c6b6035d4" + "fab43fdc2ed0b6544de59ebd07100100e88a20eb9f58f0f13474a8ab6135144f7c" + "49b80f0f1c2f4900458e2c573c94e7d81e413a06031c634890ccf47e6d02762366" + "aedaa902f7e369950b6397e5a5884a0e888ab42fbc38b2d703d265bb539d3d8567" + "f766c7aac4046327c6a6b009800010004000400720000001000140004040000010" + "0010080e1189c2d7b301ecc75e2ab3a5f07484d6399fd5601e95af66d567a5ff40" + "78dd5edd0f38c6a7002370ba8e65eb8700aa5b0b41ddc33ba48543dc00cc855b3e" + "efa62985b75e720f62dcf2ac48d8aeb022610dea42bb9091cd304e3d13f6e85e95" + "63c2744591bccee343da9d8d0b183ed6409314ce19e990d644e115d78a51b225b0" + "055000000010004038000000014bbf70aea75095f280ea3b835afda4a195279ab2" + "c010004001600043adbc7b1296c49aac7c154371fd99aeb6e58a9f500160004cfc" + "b68f91fb12789154c722d4dbb528420ca211a0000001409987adb82d9864dbbdf5" + "15545798e3fe3e55a418021400000010020e504b9a055eb465316328cfa9d9cbb2" + "0706db0160457fa3dfe7e7aca34a334370000010000") if err != nil { t.Fatal(err) } if _, _, _, _, _, err = decodeCreate(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestEncodeUnseal(t *testing.T) { testCmdBytes, err := hex.DecodeString("80020000001f0000015e800000010000000d40000009000001000401020304") if err != nil { t.Fatal(err) } cmdBytes, err := encodeUnseal(HandlePasswordSession, tpmutil.Handle(0x80000001), defaultPassword) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got: %v, want: %v", cmdBytes, testCmdBytes[10:]) } } func TestDecodeUnseal(t *testing.T) { testRespBytes, err := hex.DecodeString("800200000035000000000000001200100102030405060708090a0b0c0d0e0f100010ea78d080f9f77d9d85e1f80350247ecb010000") if err != nil { t.Fatal(err) } if _, err = decodeUnseal(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestEncodeQuote(t *testing.T) { testCmdBytes, err := hex.DecodeString("80020000003d00000158800000010000000d4000000900000100040102030400100102030405060708090a0b0c0d0e0f10001000000001000403800000") if err != nil { t.Fatal(err) } toQuote := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10} cmdBytes, err := encodeQuote(tpmutil.Handle(0x80000001), defaultPassword, toQuote, pcrSelection7, 0x0010) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got: %v, want: %v", cmdBytes, testCmdBytes[10:]) } } func TestDecodeQuote(t *testing.T) { testRespBytes, err := hex.DecodeString("80020000010400000000000000f10069ff544347801800160004" + "7705bde86e3780577632421d34e5db4759667c8900100102030405060708090a0b" + "0c0d0e0f1000000000000fe8f99cf4968c1d6e516100eb40a3278641a1c6000000" + "010004038000000014ae2edb7e23d7e8f58daa87af87775993a426722500140004" + "00804e49bb73712bc6acca4778005741b586ee6da2c98fe4dd1a3babdd9dd58c2d" + "6fed9441a5bfb3c07ae0c7a5f2aff3d46b97429cff515caa12726fec6021b439c9" + "856ebdd2f006b9159b5bfcbb8ca16c6a8f4a5953669d6af769593c00249e240f50" + "09735b03abff38917de1c43bfdcc7a488fa6474c1011d3f399939e033930bb0000" + "010000") if err != nil { t.Fatal(err) } if _, _, err = decodeQuote(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestDecodeReadPublic(t *testing.T) { testRespBytes, err := hex.DecodeString("80010000016e00000000011a0001000b000300720000000600800043" + "00100800000100010100c02b360837e3bfcb42c509eeafc89561cd3b68b0e257d77" + "488d99515f4135149adb64a419aea5f1d254819123b4a9e7df8c9f0c7ae11f128d6" + "8fef78c318cf62cee8aef48236027d0e6c8c63c4eec24b35c939017156a18b4a3f7" + "d0279e2ee79bfe9fa7680228490ad0bde089220ed59644b7a27667ddcca899e87bd" + "564fb96114884ad4534e2c4b4d838a3403b8f50508a2c18d0c405b5837b05349905" + "11112d1b1b961061ae9b24f01ad5cbae911e91fd7ee02507bd6b86df96ece3c9d47" + "f312ec0b2855cd203605fbab5c887d0f912674e17e8e76c50b0053da2b616746365" + "c49bc58ac80d1bac7f19b410feee62a048ccbfafd006af04988901d0852a0f30022" + "000bcc5923a0993903ea7754f3243ad11ab20c84e30c82a0bc0a443049e5f452782" + "00022000bcc514224b2eda95f3ef72174e551ecb5f5370d1886b06a68e54581bef5592bbe") if err != nil { t.Fatal(err) } if _, _, _, err = decodeReadPublic(testRespBytes[10:]); err != nil { t.Fatal(err) } } func TestEncodeEvictControl(t *testing.T) { testCmdBytes, err := hex.DecodeString("8002000000230000012040000001810003e800000009400000090000010000810003e8") if err != nil { t.Fatal(err) } cmdBytes, err := encodeEvictControl("", tpmutil.Handle(0x40000001), tpmutil.Handle(0x810003e8), tpmutil.Handle(0x810003e8)) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes[10:]) { t.Fatalf("got: %v, want: %v", cmdBytes, testCmdBytes[10:]) } } func TestEncodeAuthArea(t *testing.T) { pwAuth, err := encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)}) if err != nil { t.Fatal(err) } want := []byte{0, 0, 0, 0xd, 0x40, 0, 0, 9, 0, 0, 1, 0, 4, 1, 2, 3, 4} if !bytes.Equal(want, pwAuth) { t.Fatalf("got: %v, want: %v", pwAuth, want) } pwAuth, err = encodeAuthArea(AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte("")}) if err != nil { t.Fatal(err) } want = []byte{0, 0, 0, 0x9, 0x40, 0, 0, 9, 0, 0, 1, 0, 0} if !bytes.Equal(want, pwAuth) { t.Fatalf("got: %v, want: %v", pwAuth, want) } } func TestEncodeSensitiveArea(t *testing.T) { s, err := encodeSensitiveArea(tpmsSensitiveCreate{UserAuth: []byte(defaultPassword)}) if err != nil { t.Fatal(err) } want := []byte{0, 8, 0, 4, 1, 2, 3, 4, 0, 0} if !bytes.Equal(want, s) { t.Fatalf("got: %v, want: %v", s, want) } } func TestEncodeTPMLPCRSelection(t *testing.T) { s, err := encodeTPMLPCRSelection(pcrSelection7) if err != nil { t.Fatal(err) } want := []byte{0, 0, 0, 1, 0, 4, 3, 0x80, 0, 0} if !bytes.Equal(want, s) { t.Fatalf("got: %v, want: %v", s, want) } } func TestECCParamsEncodeDecode(t *testing.T) { pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } params := &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA1, }, CurveID: CurveNISTP256, Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, } buf, err := params.encode() if err != nil { t.Fatalf("encode: %v", err) } got, err := decodeECCParams(bytes.NewBuffer(buf)) if err != nil { t.Fatalf("decodeECCParams: %v", err) } if params.Point.X().Cmp(pk.PublicKey.X) != 0 { t.Fatalf("the deserialized X(big.Int) from ECCParams didn't match the X(Int) generated by ecdsa. got: %+v\nwant: %+v", params.Point.X(), pk.PublicKey.X) } if params.Point.Y().Cmp(pk.PublicKey.Y) != 0 { t.Fatalf("the deserialized Y(big.Int) from ECCParams didn't match the Y(Int) generated by ecdsa. got: %+v\nwant: %+v", params.Point.Y(), pk.PublicKey.Y) } if params.Point.X().Cmp(got.Point.X()) != 0 { t.Fatalf("the deserialized X(big.Int) from ECCParams after encode/decode didn't match the X(big.Int) before. got: %+v\nwant: %+v", got.Point.X(), params.Point.X()) } if params.Point.Y().Cmp(got.Point.Y()) != 0 { t.Fatalf("the deserialized Y(big.Int) from ECCParams after encode/decode didn't match the Y(big.Int) before. got: %+v\nwant: %+v", got.Point.Y(), params.Point.Y()) } if !reflect.DeepEqual(got, params) { t.Fatalf("got: %+v\nwant: %+v", got, params) } } func TestSignEncode(t *testing.T) { // The expected output blobs (testCmdBytes) were recorded while successfully running Sign against a real TPM device. t.Run("AlgNull", func(t *testing.T) { testCmdBytes, err := hex.DecodeString("800000010000000d40000009000001000401020304000301020300108024400000070000") if err != nil { t.Fatal(err) } digest := []byte{0x01, 0x02, 0x03} cmdBytes, err := encodeSign(HandlePasswordSession, tpmutil.Handle(0x80000001), defaultPassword, digest, nil, nil) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes) { t.Fatalf("encoded Sign message did not match golden value. got: %s, want: %s", hex.EncodeToString(cmdBytes), hex.EncodeToString(testCmdBytes)) } }) t.Run("AlgSHA256", func(t *testing.T) { testCmdBytes, err := hex.DecodeString("800000010000000d4000000900000100040102030400030102030014000b8024400000070000") if err != nil { t.Fatal(err) } digest := []byte{0x01, 0x02, 0x03} cmdBytes, err := encodeSign(HandlePasswordSession, tpmutil.Handle(0x80000001), defaultPassword, digest, &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, nil) if err != nil { t.Fatal(err) } if !bytes.Equal(cmdBytes, testCmdBytes) { t.Fatalf("encoded Sign message did not match golden value. got: %s, want: %s", hex.EncodeToString(cmdBytes), hex.EncodeToString(testCmdBytes)) } }) } func TestTPMLDigestEncode(t *testing.T) { hashA := []byte{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 255} hashB := []byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 255} hl := TPMLDigest{Digests: []tpmutil.U16Bytes{ hashA, hashB}, } want := []byte{0, 0, 0, 2, 0, 20, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 255, 0, 20, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 255} got, err := hl.Encode() if err != nil { t.Errorf("hl.Encode() failed: %v", err) } if !bytes.Equal(want, got) { t.Errorf("TPMLDigest{%v}.Encode() = %v, want: %v", hl, got, want) } } func TestTPMLDigestDecode(t *testing.T) { hashA := []byte{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 255} hashB := []byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 255} want := &TPMLDigest{Digests: []tpmutil.U16Bytes{ hashA, hashB}, } b := []byte{0, 0, 0, 2, 0, 20, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 255, 0, 20, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 255} got, err := DecodeTPMLDigest(b) if err != nil { t.Errorf("DecodeTPMDigest(b) failed: %v", err) } if len(got.Digests) != len(want.Digests) || !bytes.Equal(got.Digests[0], want.Digests[0]) || !bytes.Equal(got.Digests[1], want.Digests[1]) { t.Errorf("Digests are not the same.\n Have:\n %v\n - Want:\n %v", got.Digests, want.Digests) } } go-tpm-0.9.3/legacy/tpm2/error.go000066400000000000000000000334221473040456300165460ustar00rootroot00000000000000package 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 RCFmt0 = 0x01 RCSequence RCFmt0 = 0x03 RCPrivate RCFmt0 = 0x0B RCHMAC RCFmt0 = 0x19 RCDisabled RCFmt0 = 0x20 RCExclusive RCFmt0 = 0x21 RCAuthType RCFmt0 = 0x24 RCAuthMissing RCFmt0 = 0x25 RCPolicy RCFmt0 = 0x26 RCPCR RCFmt0 = 0x27 RCPCRChanged RCFmt0 = 0x28 RCUpgrade RCFmt0 = 0x2D RCTooManyContexts RCFmt0 = 0x2E RCAuthUnavailable RCFmt0 = 0x2F RCReboot RCFmt0 = 0x30 RCUnbalanced RCFmt0 = 0x31 RCCommandSize RCFmt0 = 0x42 RCCommandCode RCFmt0 = 0x43 RCAuthSize RCFmt0 = 0x44 RCAuthContext RCFmt0 = 0x45 RCNVRange RCFmt0 = 0x46 RCNVSize RCFmt0 = 0x47 RCNVLocked RCFmt0 = 0x48 RCNVAuthorization RCFmt0 = 0x49 RCNVUninitialized RCFmt0 = 0x4A RCNVSpace RCFmt0 = 0x4B RCNVDefined RCFmt0 = 0x4C RCBadContext RCFmt0 = 0x50 RCCPHash RCFmt0 = 0x51 RCParent RCFmt0 = 0x52 RCNeedsTest RCFmt0 = 0x53 RCNoResult RCFmt0 = 0x54 RCSensitive RCFmt0 = 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 RCWarn = 0x02 RCSessionMemory RCWarn = 0x03 RCMemory RCWarn = 0x04 RCSessionHandles RCWarn = 0x05 RCObjectHandles RCWarn = 0x06 RCLocality RCWarn = 0x07 RCYielded RCWarn = 0x08 RCCanceled RCWarn = 0x09 RCTesting RCWarn = 0x0A RCReferenceH0 RCWarn = 0x10 RCReferenceH1 RCWarn = 0x11 RCReferenceH2 RCWarn = 0x12 RCReferenceH3 RCWarn = 0x13 RCReferenceH4 RCWarn = 0x14 RCReferenceH5 RCWarn = 0x15 RCReferenceH6 RCWarn = 0x16 RCReferenceS0 RCWarn = 0x18 RCReferenceS1 RCWarn = 0x19 RCReferenceS2 RCWarn = 0x1A RCReferenceS3 RCWarn = 0x1B RCReferenceS4 RCWarn = 0x1C RCReferenceS5 RCWarn = 0x1D RCReferenceS6 RCWarn = 0x1E RCNVRate RCWarn = 0x20 RCLockout RCWarn = 0x21 RCRetry RCWarn = 0x22 RCNVUnavailable RCWarn = 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)} } go-tpm-0.9.3/legacy/tpm2/error_test.go000066400000000000000000000016621473040456300176060ustar00rootroot00000000000000package tpm2 import ( "reflect" "testing" "github.com/google/go-tpm/tpmutil" ) func TestError(t *testing.T) { tests := []struct { response tpmutil.ResponseCode expected error }{ {0x501, VendorError{Code: 0x501}}, {0x922, Warning{Code: RCRetry}}, {0x100, Error{Code: RCInitialize}}, {0xfc1, ParameterError{Code: RCAsymmetric, Parameter: RCF}}, {0x7a3, HandleError{Code: RCExpired, Handle: RC7}}, {0xfa2, SessionError{Code: RCBadAuth, Session: RC7}}, } for _, test := range tests { err := decodeResponse(test.response) if !reflect.DeepEqual(err, test.expected) { t.Fatalf("decodeResponse(0x%x) = %#v, want %#v", test.response, err, test.expected) } } } // nil ReadWriter handle causes tpmutil.RunCommand to return an error. func TestRunCommandErr(t *testing.T) { if _, err := runCommand(nil, TagSessions, CmdSign); err == nil { t.Error("runCommand returned nil error on error from tpmutil.RunCommand") } } go-tpm-0.9.3/legacy/tpm2/kdf.go000066400000000000000000000103121473040456300161520ustar00rootroot00000000000000// 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/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. // Deprecated: Use KDFaHash. func KDFa(hashAlg Algorithm, key []byte, label string, contextU, contextV []byte, bits int) ([]byte, error) { h, err := hashAlg.Hash() if err != nil { return nil, err } return KDFaHash(h, key, label, contextU, contextV, bits), nil } // KDFe implements TPM 2.0's ECDH key derivation function, as defined in // section 11.4.9.3 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The z parameter is the x coordinate of one party's private ECC key multiplied // by the other party's public ECC point. // The use parameter is a non-null-terminated string. // The partyUInfo and partyVInfo are the x coordinates of the initiator's and // Deprecated: Use KDFeHash. func KDFe(hashAlg Algorithm, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) ([]byte, error) { h, err := hashAlg.Hash() if err != nil { return nil, err } return KDFeHash(h, z, use, partyUInfo, partyVInfo, bits), nil } // KDFaHash implements TPM 2.0's default key derivation function, as defined in // section 11.4.9.2 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The key & label parameters must not be zero length. // The label parameter is a non-null-terminated string. // The contextU & contextV parameters are optional. func KDFaHash(h crypto.Hash, key []byte, label string, contextU, contextV []byte, bits int) []byte { mac := hmac.New(h.New, key) out := kdf(mac, bits, func() { 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 } // KDFeHash implements TPM 2.0's ECDH key derivation function, as defined in // section 11.4.9.3 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The z parameter is the x coordinate of one party's private ECC key multiplied // by the other party's public ECC point. // The use parameter is a non-null-terminated string. // The partyUInfo and partyVInfo are the x coordinates of the initiator's and // the responder's ECC points, respectively. func KDFeHash(h crypto.Hash, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) []byte { hash := h.New() out := kdf(hash, bits, func() { hash.Write(z) hash.Write([]byte(use)) hash.Write([]byte{0}) // Terminating null character for C-string. hash.Write(partyUInfo) hash.Write(partyVInfo) }) return out } 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 } go-tpm-0.9.3/legacy/tpm2/open_other.go000066400000000000000000000032251473040456300175550ustar00rootroot00000000000000//go: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 ( "errors" "fmt" "io" "os" "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. // // This function may also be invoked with no paths, as tpm2.OpenTPM(). In this // case, the default paths on Linux (/dev/tpmrm0 then /dev/tpm0), will be used. func OpenTPM(path ...string) (tpm io.ReadWriteCloser, err error) { switch len(path) { case 0: tpm, err = tpmutil.OpenTPM("/dev/tpmrm0") if errors.Is(err, os.ErrNotExist) { tpm, err = tpmutil.OpenTPM("/dev/tpm0") } case 1: tpm, err = tpmutil.OpenTPM(path[0]) default: return nil, errors.New("cannot specify multiple paths to tpm2.OpenTPM") } if err != nil { return nil, err } // Make sure this is a TPM 2.0 _, err = GetManufacturer(tpm) if err != nil { tpm.Close() return nil, fmt.Errorf("open %s: device is not a TPM 2.0", path) } return tpm, nil } go-tpm-0.9.3/legacy/tpm2/open_windows.go000066400000000000000000000020221473040456300201200ustar00rootroot00000000000000//go: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 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() } go-tpm-0.9.3/legacy/tpm2/structures.go000066400000000000000000000751451473040456300176500ustar00rootroot00000000000000// 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 contains both [rsa]parameters and [rsa]unique. RSAParameters *RSAParams // ECCParameters contains both [ecc]parameters and [ecc]unique. ECCParameters *ECCParams // SymCipherParameters contains both [sym]parameters and [sym]unique. SymCipherParameters *SymCipherParams // KeyedHashParameters contains both [keyedHash]parameters and [keyedHash]unique. 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 overridden 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: // both the TPMS_RSA_PARMS and the TPM2B_PUBLIC_KEY_RSA. // // 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: // both the TPMS_ECC_PARMS and the TPMS_ECC_POINT. // // 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: // both the TPMS_SYMCIPHER_PARMS and the TPM2B_DIGEST (hash of the key). 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: // both the TPMS_KEYEDHASH_PARMS and the TPM2B_DIGEST (hash of the key). 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 } // Encode serializes a Signature structure in TPM wire format. func (s Signature) Encode() ([]byte, error) { head, err := tpmutil.Pack(s.Alg) if err != nil { return nil, fmt.Errorf("encoding Alg: %v", err) } var signature []byte switch s.Alg { case AlgRSASSA, AlgRSAPSS: if signature, err = tpmutil.Pack(s.RSA); err != nil { return nil, fmt.Errorf("encoding RSA: %v", err) } case AlgECDSA: signature, err = tpmutil.Pack(s.ECC.HashAlg, tpmutil.U16Bytes(s.ECC.R.Bytes()), tpmutil.U16Bytes(s.ECC.S.Bytes())) if err != nil { return nil, fmt.Errorf("encoding ECC: %v", err) } } return concat(head, signature) } // 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 Quote, 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) } case TagAttestQuote: if info, err = ad.AttestedQuoteInfo.encode(); err != nil { return nil, fmt.Errorf("encoding AttestedQuoteInfo: %v", err) } default: return nil, fmt.Errorf("only Quote, 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 } func (qi QuoteInfo) encode() ([]byte, error) { sel, err := encodeTPMLPCRSelection(qi.PCRSelection) if err != nil { return nil, fmt.Errorf("encoding PCRSelection: %v", err) } digest, err := tpmutil.Pack(qi.PCRDigest) if err != nil { return nil, fmt.Errorf("encoding PCRDigest: %v", err) } return concat(sel, digest) } // 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 } // EncodeCreationData encodes byte array to TPMS_CREATION_DATA message. func (cd *CreationData) EncodeCreationData() ([]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 } go-tpm-0.9.3/legacy/tpm2/test/000077500000000000000000000000001473040456300160415ustar00rootroot00000000000000go-tpm-0.9.3/legacy/tpm2/test/benchmark_test.go000066400000000000000000000045311473040456300213640ustar00rootroot00000000000000// 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/sha256" "testing" . "github.com/google/go-tpm/legacy/tpm2" ) func BenchmarkRSA2048Signing(b *testing.B) { b.StopTimer() rw := openTPM(b) defer rw.Close() pub := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } signerHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) if err != nil { b.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, signerHandle) digest := sha256.Sum256([]byte("randomString")) b.StartTimer() for i := 0; i < b.N; i++ { if _, err := Sign(rw, signerHandle, defaultPassword, digest[:], nil, pub.RSAParameters.Sign); err != nil { b.Fatalf("Signing failed: %v", err) } } } func BenchmarkECCNISTP256Signing(b *testing.B) { b.StopTimer() rw := openTPM(b) defer rw.Close() skipOnUnsupportedAlg(b, rw, AlgECC) pub := Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, } signerHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) if err != nil { b.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, signerHandle) digest := sha256.Sum256([]byte("randomString")) b.StartTimer() for i := 0; i < b.N; i++ { if _, err := Sign(rw, signerHandle, defaultPassword, digest[:], nil, pub.ECCParameters.Sign); err != nil { b.Fatalf("Signing failed: %v", err) } } } go-tpm-0.9.3/legacy/tpm2/test/kdf_test.go000066400000000000000000000417571473040456300202110ustar00rootroot00000000000000// 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" "testing" . "github.com/google/go-tpm/legacy/tpm2" ) func TestKDFa(t *testing.T) { tcs := []struct { hashAlg Algorithm key []byte contextU []byte contextV []byte label string bits int expected []byte }{ { hashAlg: AlgSHA256, key: []byte{'y', 'o', 'l', 'o', 0}, contextU: []byte{'k', 'e', 'k', 0}, contextV: []byte{'y', 'o', 'y', 'o', 0}, label: "IDENTITY", bits: 128, expected: []byte{0xd2, 0xd7, 0x2c, 0xc7, 0xa8, 0xa5, 0xeb, 0x09, 0xe8, 0xc7, 0x90, 0x12, 0xe2, 0xda, 0x9f, 0x22}, }, { hashAlg: AlgSHA256, key: []byte{'c', 'a', 0}, contextU: []byte{'a', 'b', 'c', 0}, label: "IDENTITY", bits: 1024, expected: []byte{0x1a, 0xae, 0x71, 0x51, 0xac, 0x1a, 0x56, 0x90, 0xed, 0xa7, 0xdc, 0xab, 0xd5, 0x68, 0x00, 0xc1, 0x1c, 0x56, 0xa3, 0x81, 0x0b, 0xa0, 0x59, 0x82, 0x6f, 0xe4, 0x77, 0x63, 0x48, 0xd6, 0xae, 0x8e, 0x5d, 0x5d, 0x18, 0xc7, 0xcc, 0xf8, 0x37, 0x3f, 0x7b, 0x94, 0x2a, 0xda, 0x8b, 0x91, 0x2b, 0x12, 0xda, 0x56, 0xfb, 0x37, 0xf6, 0x4b, 0x93, 0x58, 0x72, 0x84, 0x1e, 0xc0, 0x7d, 0x38, 0xe1, 0xfb, 0x8e, 0x7e, 0xc8, 0x6e, 0xfc, 0xbf, 0xb4, 0x44, 0x75, 0x6b, 0xc8, 0x86, 0x3f, 0x85, 0x8d, 0x26, 0x90, 0xa6, 0x21, 0xc9, 0xaf, 0xb9, 0x83, 0xcd, 0x77, 0xe7, 0xa1, 0x04, 0x8a, 0xe1, 0xa7, 0x59, 0x8a, 0xc8, 0x95, 0x32, 0x3d, 0x44, 0xc1, 0x02, 0x27, 0xaf, 0x0a, 0x00, 0x14, 0x4c, 0xab, 0x55, 0x11, 0x10, 0x75, 0xdc, 0x6b, 0x72, 0xad, 0x6e, 0xb1, 0x63, 0xc7, 0x45, 0x8b, 0x87, 0x8e, 0x8c}, }, { hashAlg: AlgSHA1, key: []byte{'c', 'a', 0}, contextU: []byte{'a', 'b', 'c', 0}, label: "IDENTITY", bits: 256, expected: []byte{0x83, 0xf3, 0x54, 0xaf, 0xcf, 0x92, 0x3d, 0xe2, 0x11, 0x2e, 0x08, 0x91, 0x43, 0x4c, 0xd0, 0xbd, 0xc8, 0xac, 0xbf, 0x01, 0xb8, 0x11, 0xc0, 0xe8, 0xcd, 0x06, 0x2d, 0xed, 0x39, 0xe3, 0x1f, 0x7f}, }, { // Based on an example on Page 44. Note that the upper 7 bits of the zeroth byte are clear. hashAlg: AlgSHA1, key: []byte{0x27, 0x1f, 0xa0, 0x8b, 0xbd, 0xc5, 0x6, 0xe, 0xc3, 0xdf, 0xa9, 0x28, 0xff, 0x9b, 0x73, 0x12, 0x3a, 0x12, 0xda, 0xc}, contextU: []byte{0xce, 0x24, 0x4f, 0x39, 0x5d, 0xca, 0x73, 0x91}, contextV: []byte{0xda, 0x50, 0x40, 0x31, 0xdd, 0xf1, 0x2e, 0x83}, label: "KDFSELFTESTLABEL", bits: 521, expected: []byte{0x1, 0x71, 0x4e, 0x86, 0x50, 0x72, 0xc1, 0xe4, 0xc4, 0xb, 0x36, 0x70, 0x28, 0x11, 0x12, 0x63, 0x2f, 0xc9, 0xec, 0xba, 0x25, 0xe6, 0x6a, 0xf6, 0x8d, 0x18, 0xa2, 0xb6, 0x2d, 0xe9, 0xcb, 0xb4, 0x45, 0x21, 0xda, 0x2b, 0xc9, 0xa4, 0x96, 0x86, 0x2e, 0xb3, 0xf3, 0x6, 0x94, 0x9f, 0x9e, 0xfe, 0x8a, 0x9e, 0x1c, 0xcb, 0xce, 0x3b, 0x4d, 0x66, 0x8f, 0xfd, 0x75, 0xc9, 0x39, 0x4b, 0xa5, 0x94, 0x58, 0xfe}, }, } for _, tc := range tcs { o, err := KDFa(tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits) if err != nil { t.Fatalf("KDFa(%v, %v, %q, %v, %v, %v) returned error: %v", tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits, err) } if !bytes.Equal(tc.expected, o) { t.Errorf("Test with KDFa(%v, %v, %q, %v, %v, %v) returned incorrect result", tc.hashAlg, tc.key, tc.label, tc.contextU, tc.contextV, tc.bits) t.Logf(" Got: %v", o) t.Logf(" Want: %v", tc.expected) } } } func TestKDFe(t *testing.T) { label := "DUPLICATE" // Test vectors taken from running the microsoft TPM simulator, ms-tpm-20-ref. tests := []struct { name string bits int hashAlg Algorithm z []byte partyUInfo []byte partyVInfo []byte expected []byte }{ { name: "NIST-P224-SHA256", bits: 256, hashAlg: AlgSHA256, z: []byte{0x6e, 0xd1, 0x5c, 0x60, 0xfd, 0x43, 0x3f, 0x5d, 0xdb, 0x28, 0xd, 0x7b, 0xe4, 0x3f, 0x8a, 0xc5, 0xa4, 0x52, 0x4c, 0x13, 0xb9, 0x2f, 0xf2, 0x93, 0x61, 0x94, 0x29, 0xef}, partyUInfo: []byte{0xaf, 0x6, 0xe1, 0xa4, 0x22, 0xed, 0xbe, 0x6f, 0x41, 0xe1, 0xf8, 0xb3, 0xce, 0xa, 0xf2, 0x1f, 0xc8, 0xb1, 0x1, 0x3c, 0x1f, 0xc8, 0xd5, 0x50, 0xcc, 0xae, 0xe6, 0x6d}, partyVInfo: []byte{0xa0, 0x2e, 0x47, 0x5e, 0xc7, 0x53, 0x44, 0x4d, 0x1b, 0xc1, 0xad, 0x10, 0xbc, 0xa3, 0xa7, 0xda, 0x72, 0xee, 0x65, 0x29, 0x7b, 0x4, 0xd5, 0xf4, 0x2a, 0xa8, 0x81, 0x2c}, expected: []byte{0x33, 0xa1, 0x99, 0xf3, 0x24, 0xbe, 0x56, 0x22, 0x49, 0x4e, 0x7c, 0x79, 0xcb, 0xc, 0x8c, 0x84, 0x22, 0x73, 0xd7, 0x68, 0x8d, 0x3a, 0x64, 0xda, 0x97, 0xfb, 0x48, 0xea, 0xea, 0x44, 0xf0, 0xa3}, }, { name: "NIST-P224-SHA1", bits: 160, hashAlg: AlgSHA1, z: []byte{0x15, 0xf6, 0xca, 0x83, 0x6a, 0x67, 0x92, 0xec, 0x4a, 0xb2, 0xc9, 0x89, 0x84, 0xd9, 0x8c, 0xd4, 0xb8, 0xf, 0xe9, 0x6c, 0xf, 0x40, 0x26, 0xd5, 0xe7, 0x5c, 0x6f, 0x4f}, partyUInfo: []byte{0x12, 0x2f, 0xdd, 0x75, 0x5, 0xe1, 0xde, 0xd5, 0x81, 0x1e, 0xac, 0x38, 0x35, 0x11, 0x6c, 0x9c, 0x1c, 0xd5, 0xbf, 0xc6, 0x85, 0x74, 0x84, 0xba, 0x31, 0x53, 0xf, 0xb8}, partyVInfo: []byte{0x58, 0x27, 0xb4, 0x21, 0x43, 0x0, 0x63, 0x6e, 0xd6, 0x15, 0xe7, 0x56, 0xd, 0xd7, 0xaa, 0xc4, 0xbb, 0x94, 0xc7, 0xc2, 0x73, 0xdf, 0xf1, 0x10, 0xd2, 0xc3, 0x80, 0x8a}, expected: []byte{0x3c, 0x1e, 0x8e, 0x76, 0xde, 0x44, 0xaf, 0x9e, 0xfe, 0xea, 0x6e, 0xa7, 0xce, 0x6b, 0x43, 0x39, 0x63, 0x59, 0xe1, 0xc2}, }, { name: "NIST-P256-SHA256", bits: 256, hashAlg: AlgSHA256, z: []byte{0x69, 0xfd, 0x74, 0xd0, 0x52, 0xa5, 0xcd, 0x9d, 0x6a, 0xdd, 0xf4, 0xea, 0x79, 0x1f, 0x47, 0x37, 0xeb, 0x29, 0x72, 0x46, 0xdc, 0xe6, 0x4, 0xf6, 0x38, 0x5c, 0x13, 0x92, 0xe0, 0xfb, 0x56, 0xb9}, partyUInfo: []byte{0xc2, 0x9f, 0x8c, 0xff, 0x1c, 0xe0, 0x54, 0xed, 0x59, 0x45, 0xab, 0xd3, 0x93, 0xee, 0x2e, 0xed, 0x3f, 0x67, 0x43, 0x7d, 0x9d, 0xb0, 0x7b, 0x93, 0x21, 0x56, 0xc6, 0x5d, 0x32, 0x92, 0x13, 0x73}, partyVInfo: []byte{0x10, 0x79, 0x37, 0xdc, 0x44, 0xe5, 0xbb, 0x50, 0x94, 0xd3, 0xd3, 0xa, 0x55, 0xef, 0xac, 0x77, 0xb6, 0xdb, 0x32, 0x53, 0xba, 0x42, 0xda, 0xbc, 0x80, 0x44, 0x46, 0xb9, 0x38, 0x64, 0xe0, 0x55}, expected: []byte{0xb1, 0x52, 0x67, 0xe3, 0xf9, 0x60, 0xf8, 0xf8, 0xf3, 0xb9, 0x4f, 0xf3, 0xfc, 0x64, 0x11, 0x9f, 0x68, 0x76, 0x97, 0x4f, 0x98, 0x4f, 0x82, 0xd5, 0xb, 0xd5, 0x4a, 0xbc, 0x7a, 0x64, 0x6, 0xe8}, }, { name: "NIST-P256-SHA1", bits: 160, hashAlg: AlgSHA1, z: []byte{0x82, 0x1f, 0xd7, 0x93, 0x2a, 0x44, 0xcf, 0x38, 0x33, 0x73, 0x68, 0x5b, 0x71, 0x92, 0xd4, 0x6c, 0xe9, 0xc9, 0xc4, 0x7a, 0x4b, 0x99, 0x76, 0x1e, 0x88, 0x78, 0xbd, 0xbf, 0x25, 0xc8, 0xb8, 0x68}, partyUInfo: []byte{0x68, 0xd1, 0x23, 0x7b, 0x42, 0xf4, 0xf8, 0x2d, 0xde, 0x83, 0x9f, 0xd2, 0x77, 0x87, 0xa, 0x4f, 0x21, 0xc0, 0x2e, 0x6b, 0x2d, 0x19, 0xe2, 0xf4, 0x25, 0x1d, 0x7b, 0x66, 0x87, 0xe5, 0x1d, 0x6a}, partyVInfo: []byte{0xfb, 0xf1, 0xc3, 0xa7, 0x29, 0xcb, 0xd4, 0xe0, 0xbd, 0xcf, 0x8d, 0xbf, 0xc9, 0x47, 0xf4, 0xbf, 0x95, 0x88, 0xb4, 0x2, 0x9c, 0xbb, 0x3d, 0x65, 0x8f, 0xd5, 0x7e, 0xda, 0x36, 0xb1, 0x41, 0xbd}, expected: []byte{0x78, 0x9b, 0x33, 0x6d, 0x41, 0x19, 0x8f, 0x37, 0x33, 0xd2, 0xbe, 0x8f, 0x5c, 0xb3, 0x5c, 0x98, 0xc4, 0x2b, 0x51, 0x9e}, }, { name: "NIST-P384-SHA256", bits: 256, hashAlg: AlgSHA256, z: []byte{0xbb, 0x3a, 0x3f, 0x26, 0xd6, 0x81, 0xb, 0xff, 0x29, 0x67, 0x63, 0x49, 0x95, 0xfd, 0x57, 0x9f, 0x80, 0x6f, 0xa6, 0x8f, 0xd, 0x9d, 0xb5, 0xbd, 0x62, 0xd1, 0xc, 0x30, 0x5c, 0xea, 0xa9, 0xda, 0x8e, 0xec, 0x67, 0x3e, 0xe0, 0x89, 0x51, 0xbc, 0x63, 0xae, 0x9c, 0x6f, 0xb5, 0xfe, 0x98, 0xb1}, partyUInfo: []byte{0x20, 0xf0, 0xf4, 0xc, 0xf1, 0x85, 0xca, 0x7, 0xa4, 0x56, 0x99, 0xf7, 0x4a, 0x7, 0x19, 0xc6, 0x21, 0x84, 0x3e, 0x9d, 0x7, 0x1e, 0x4f, 0x6b, 0xe8, 0xc3, 0x7a, 0xbd, 0xa8, 0x38, 0xee, 0x1a, 0x2a, 0x9, 0x82, 0x4d, 0x22, 0x6b, 0x36, 0xa7, 0xae, 0x50, 0x18, 0xaa, 0x9, 0x97, 0xd6, 0x17}, partyVInfo: []byte{0xcb, 0x67, 0x7d, 0x12, 0x74, 0x25, 0x9f, 0x55, 0x2a, 0xa4, 0x52, 0x60, 0xd9, 0x65, 0x46, 0xc5, 0x3f, 0xe1, 0xa0, 0x4a, 0xf7, 0x8a, 0xc, 0xc, 0xbb, 0xb4, 0xf4, 0x3b, 0x32, 0x71, 0x63, 0xc5, 0xd, 0x76, 0x8, 0xbc, 0x2c, 0xf6, 0x4c, 0x1f, 0x2c, 0xbe, 0x79, 0x9a, 0xaa, 0x9f, 0xbe, 0x6d}, expected: []byte{0x94, 0x54, 0xb7, 0x2e, 0xb5, 0xf, 0xb, 0x25, 0x66, 0x24, 0x69, 0x76, 0xa7, 0x44, 0x5d, 0xa1, 0xa1, 0xeb, 0xfa, 0x82, 0xc9, 0x14, 0xc4, 0x65, 0xac, 0x75, 0x4e, 0x94, 0x1d, 0xe0, 0xfa, 0xaa}, }, { name: "NIST-P384-SHA1", bits: 160, hashAlg: AlgSHA1, z: []byte{0x6a, 0x21, 0xc6, 0x9, 0x50, 0x1e, 0x63, 0x6e, 0x65, 0x20, 0x49, 0xd1, 0xac, 0xc9, 0xd9, 0xea, 0x23, 0xc2, 0x7f, 0xf8, 0xc5, 0x8e, 0xf4, 0xe6, 0xb2, 0x91, 0x41, 0xa7, 0x63, 0xda, 0xf7, 0x94, 0x40, 0x16, 0xcd, 0x4, 0x4f, 0xa5, 0x8b, 0xf8, 0x5a, 0x59, 0x82, 0xfc, 0x11, 0x8d, 0xb3, 0x9e}, partyUInfo: []byte{0x9f, 0xc, 0x9f, 0xcf, 0xd8, 0xd5, 0xa6, 0x21, 0x88, 0x78, 0xe7, 0xfc, 0xb5, 0x13, 0x9b, 0xd5, 0xe8, 0xc5, 0x8d, 0xcb, 0x9e, 0x5f, 0x16, 0xd2, 0xa7, 0x6b, 0xe5, 0xc5, 0x6f, 0x9b, 0x1e, 0x13, 0x6e, 0x99, 0x78, 0x4b, 0xe3, 0xf1, 0x98, 0x1f, 0xe, 0x74, 0xa5, 0xc9, 0x5d, 0x1, 0xce, 0x88}, partyVInfo: []byte{0x2, 0x31, 0x9a, 0x6a, 0xb9, 0xc7, 0x3e, 0x59, 0xd9, 0x93, 0x3e, 0x3f, 0x70, 0x2b, 0xa4, 0x33, 0xea, 0x8a, 0xc8, 0x55, 0x52, 0xbe, 0x9, 0x85, 0x5e, 0x7a, 0xe2, 0xe1, 0x4e, 0xce, 0x3a, 0x97, 0xf7, 0xe9, 0xd4, 0x8a, 0x93, 0xd5, 0x25, 0x7e, 0x62, 0x76, 0x73, 0x79, 0x85, 0xa1, 0x39, 0xa5}, expected: []byte{0x2, 0x7e, 0x4b, 0xb0, 0x36, 0x25, 0xd6, 0x19, 0x2, 0xe8, 0x6d, 0x80, 0xf7, 0xe2, 0xe2, 0x8d, 0xfa, 0x8f, 0x1a, 0xf2}, }, { name: "NIST-P521-SHA256", bits: 256, hashAlg: AlgSHA256, z: []byte{0x1, 0xaf, 0x5f, 0x2a, 0x95, 0x8d, 0xee, 0x7f, 0x66, 0x17, 0xa5, 0x52, 0x2c, 0xaa, 0x91, 0xe4, 0x72, 0x21, 0xe2, 0xf2, 0xf6, 0x39, 0xb, 0xec, 0x39, 0x5d, 0xc6, 0xf0, 0x34, 0x86, 0x48, 0xc9, 0x73, 0xf1, 0x99, 0x68, 0xe4, 0x8f, 0x1d, 0x89, 0x43, 0xc7, 0xf5, 0xcd, 0x6a, 0x92, 0x88, 0xe9, 0x78, 0x3b, 0x73, 0x3a, 0xd2, 0x44, 0x3b, 0xab, 0x63, 0xef, 0x28, 0x5d, 0x26, 0x10, 0x21, 0x7a, 0x8, 0xeb}, partyUInfo: []byte{0xf, 0xcb, 0xe5, 0xe4, 0xca, 0xf1, 0xe1, 0x1f, 0x46, 0xfe, 0xeb, 0x72, 0x62, 0xd4, 0xb4, 0x27, 0x6c, 0x6, 0x50, 0x9c, 0x96, 0xc7, 0x43, 0xdb, 0x23, 0x8e, 0x48, 0xa0, 0x90, 0xf1, 0x24, 0x3f, 0x6d, 0x33, 0xbe, 0xff, 0x9, 0x40, 0xf0, 0xa6, 0xfb, 0xcc, 0x61, 0x63, 0x72, 0x92, 0x3a, 0x3a, 0xde, 0x83, 0xe5, 0xa8, 0xa6, 0x6c, 0x37, 0xb5, 0x53, 0x0, 0x4, 0x2f, 0x10, 0xcb, 0x39, 0x2, 0x13}, partyVInfo: []byte{0x0, 0xee, 0xd7, 0x6b, 0x36, 0x5c, 0xba, 0xeb, 0xdd, 0xf5, 0x5a, 0xd2, 0xa0, 0xb6, 0xcb, 0x95, 0xb2, 0x85, 0xce, 0xd2, 0x64, 0xe2, 0x39, 0x5, 0x74, 0x7b, 0x8d, 0x4, 0x1a, 0xa9, 0x68, 0xbd, 0x66, 0xec, 0x69, 0x3d, 0xd9, 0xf8, 0x7f, 0xd4, 0xad, 0x43, 0x3a, 0x23, 0xe8, 0xcc, 0xa6, 0x4d, 0xa1, 0x65, 0xd, 0xac, 0xf0, 0x84, 0x7a, 0x4d, 0x4a, 0x8a, 0xf4, 0x2e, 0x73, 0xb2, 0x37, 0x85, 0x67, 0x8b}, expected: []byte{0xbc, 0x34, 0x57, 0x10, 0xa2, 0x4, 0x54, 0xf, 0xc5, 0x9a, 0xe8, 0x5e, 0x23, 0x7, 0x1d, 0x0, 0xfe, 0xfb, 0x28, 0xc, 0xe6, 0xa1, 0xd0, 0xbb, 0x52, 0x73, 0x9f, 0x8c, 0xf9, 0x5, 0xda, 0x30}, }, { name: "NIST-P521-SHA1", bits: 160, hashAlg: AlgSHA1, z: []byte{0x1, 0x3d, 0x67, 0x71, 0x7e, 0x61, 0xf0, 0x2b, 0xdd, 0x24, 0xb8, 0xd7, 0x2b, 0x45, 0x5, 0x89, 0xad, 0x52, 0x69, 0x40, 0xc, 0x86, 0x98, 0x90, 0xa2, 0xf4, 0xaf, 0xad, 0xda, 0xb8, 0x4d, 0x95, 0x84, 0x38, 0xc9, 0x24, 0x80, 0x16, 0x88, 0xec, 0x9, 0x85, 0x2e, 0x47, 0xf9, 0x25, 0xb5, 0xe3, 0xf, 0xc1, 0x78, 0xaa, 0xa5, 0x84, 0x83, 0x9a, 0x1d, 0x4c, 0x15, 0x97, 0x3f, 0xec, 0x59, 0x24, 0xe1, 0xb}, partyUInfo: []byte{0x0, 0x8a, 0xe6, 0xa1, 0xad, 0x3e, 0xef, 0xa0, 0xaf, 0x49, 0x41, 0x85, 0x33, 0xe9, 0x18, 0x8a, 0xa4, 0x36, 0x95, 0x4c, 0xb9, 0x41, 0x42, 0xd6, 0xe4, 0x66, 0xa1, 0x48, 0xb1, 0x65, 0x8d, 0xdc, 0xba, 0x9c, 0x25, 0x72, 0x3, 0xf6, 0xde, 0x1c, 0x4, 0x66, 0x7d, 0x3b, 0x7d, 0x5a, 0x8c, 0x82, 0x8d, 0x1f, 0x9f, 0x46, 0xc4, 0x0, 0x99, 0xef, 0xc3, 0xa1, 0xca, 0xe, 0x98, 0xa0, 0x23, 0x8b, 0x6b, 0x5e}, partyVInfo: []byte{0x1, 0x56, 0xf, 0x5e, 0xcf, 0xef, 0xb4, 0x4, 0x94, 0x32, 0xb1, 0x7b, 0x24, 0x86, 0x92, 0x41, 0xba, 0x2d, 0xfc, 0xb2, 0x3a, 0xce, 0x96, 0xee, 0x52, 0x23, 0x51, 0xcf, 0x4a, 0x3e, 0x1d, 0xf4, 0xb8, 0x27, 0xe6, 0xa7, 0xb5, 0xb9, 0xce, 0x37, 0x4d, 0xa, 0xc4, 0xb9, 0x20, 0x2d, 0x3f, 0xa, 0x14, 0x47, 0x7f, 0x51, 0x29, 0x2c, 0x3e, 0x41, 0x7a, 0x1, 0x11, 0x23, 0x9c, 0x8, 0x23, 0xfe, 0xef, 0xa3}, expected: []byte{0xee, 0x5, 0xca, 0xe6, 0x4c, 0xa5, 0x6, 0xfe, 0x89, 0x51, 0x7d, 0x7b, 0x8e, 0xf9, 0x73, 0xf1, 0xb, 0x39, 0xd4, 0x5c}, }, { name: "SHORTER-THAN-HASH", bits: 100, hashAlg: AlgSHA256, z: []byte{0x9e, 0xc0, 0x69, 0x1d, 0x3c, 0x5d, 0x35, 0xfb, 0xb2, 0xdc, 0xf0, 0x82, 0xb0, 0xbb, 0x4d, 0x1d, 0xf0, 0x7e, 0xe0, 0xc5, 0xbf, 0x27, 0x25, 0x1f, 0xff, 0xa9, 0x73, 0xee, 0x33, 0x9e, 0x5e, 0x62}, partyUInfo: []byte{0x68, 0xd6, 0x2d, 0x34, 0x49, 0xe0, 0xdb, 0x8c, 0x1a, 0xf5, 0x7a, 0xdb, 0xa1, 0x53, 0x43, 0xbc, 0x34, 0xf2, 0xa6, 0xe7, 0x6a, 0x97, 0x50, 0xf4, 0x76, 0xe6, 0x18, 0x62, 0xdb, 0x8f, 0xb, 0xec}, partyVInfo: []byte{0xaa, 0xb7, 0xca, 0xb3, 0x61, 0xd8, 0xf3, 0x1d, 0xf, 0x6a, 0x98, 0xcc, 0x3c, 0x11, 0xbb, 0xe9, 0x98, 0x3b, 0xf9, 0x1f, 0xc4, 0xc5, 0x3e, 0x90, 0xd5, 0xcb, 0x10, 0xeb, 0x74, 0xfd, 0x5c, 0x3a}, expected: []byte{0x5, 0x0, 0x54, 0xd3, 0xd7, 0x78, 0x5, 0x91, 0x20, 0xaf, 0x6d, 0x3, 0x13}, }, { name: "LONGER-THAN-HASH", bits: 1600, hashAlg: AlgSHA256, z: []byte{0x1e, 0xb2, 0xa, 0x75, 0x56, 0xd7, 0xb7, 0xc9, 0x1d, 0x7d, 0x69, 0x74, 0xc9, 0x4a, 0x9b, 0xfb, 0xfa, 0xf1, 0x25, 0x63, 0xcb, 0xde, 0x93, 0xb4, 0x5, 0x6b, 0xb9, 0xb6, 0x2b, 0x2b, 0x23, 0x82}, partyUInfo: []byte{0x57, 0xea, 0xfe, 0xe9, 0xcb, 0x12, 0xae, 0xb7, 0x43, 0x7d, 0xbc, 0xe8, 0x40, 0xaa, 0x9d, 0xbd, 0x59, 0x6f, 0x3b, 0x10, 0x32, 0xa, 0xb0, 0x65, 0x75, 0xb2, 0x37, 0xf3, 0x38, 0xfa, 0xe6, 0xc2}, partyVInfo: []byte{0x30, 0x99, 0xaa, 0x56, 0xbc, 0x2b, 0xe9, 0xbc, 0xca, 0x32, 0x4f, 0x3, 0x10, 0x3a, 0x56, 0xac, 0x3, 0x31, 0x4d, 0x10, 0x5f, 0x4d, 0xbc, 0x90, 0x32, 0xfe, 0x87, 0x9f, 0xe9, 0xc0, 0xdf, 0x68}, expected: []byte{0xe0, 0xd2, 0x56, 0xf0, 0x5f, 0xce, 0xda, 0x6c, 0x5f, 0xe6, 0xb0, 0x9f, 0xb5, 0xaa, 0xbf, 0xab, 0xa5, 0xe4, 0xca, 0x7c, 0x43, 0x12, 0xec, 0xbe, 0x89, 0xf, 0x7c, 0x57, 0x47, 0xf5, 0xca, 0xad, 0xa0, 0x4e, 0xbc, 0x13, 0xff, 0x15, 0x7f, 0xdb, 0x73, 0x76, 0xdc, 0xdc, 0xf5, 0x61, 0x59, 0x35, 0xd, 0x7c, 0xfb, 0x1c, 0x2, 0xba, 0xe0, 0x18, 0x28, 0x7c, 0xc3, 0x4b, 0x67, 0xf3, 0x2b, 0xf0, 0xd8, 0x9d, 0x7f, 0x36, 0xe8, 0x3f, 0x5b, 0xcf, 0x76, 0xe7, 0x2, 0x34, 0xac, 0xda, 0x4a, 0xe5, 0x9d, 0xa6, 0x1, 0x93, 0x28, 0x17, 0x80, 0xa, 0xf3, 0x4b, 0xd7, 0x54, 0x36, 0xc6, 0x59, 0x1e, 0xbd, 0xbb, 0x97, 0x88, 0x66, 0x4, 0x14, 0x4, 0x4b, 0xe, 0x26, 0xf0, 0x6d, 0xeb, 0x8a, 0x34, 0xca, 0xa4, 0xe0, 0xa4, 0x90, 0xae, 0x3, 0xdd, 0x11, 0x80, 0xac, 0x17, 0x50, 0xa0, 0x1b, 0x8d, 0xeb, 0x7a, 0x4a, 0x79, 0x96, 0x91, 0x64, 0x17, 0xc4, 0x21, 0xe0, 0xf6, 0x5b, 0x57, 0x1, 0xb5, 0xec, 0x14, 0xd7, 0xb1, 0x19, 0x5d, 0x6e, 0xc5, 0x33, 0x7f, 0x3, 0xaa, 0x41, 0x9d, 0x72, 0x7d, 0x20, 0xa5, 0x75, 0xde, 0xfd, 0xe2, 0x2e, 0xe4, 0x54, 0x2b, 0xbf, 0x9e, 0xad, 0x57, 0xd0, 0x3e, 0x2e, 0x76, 0x28, 0xd8, 0x58, 0x80, 0xaf, 0x53, 0x1b, 0x2d, 0x3, 0xc3, 0xd7, 0xe7, 0x57, 0x9c, 0xcf, 0xf6, 0xf9, 0x7e, 0x7f, 0xa8, 0x2e, 0x54}, }, { // Based on an example on Page 44. Note that the upper 7 bits of the zeroth byte are clear. name: "NOT-MULTIPLE", bits: 521, hashAlg: AlgSHA256, z: []byte{0x94, 0xb2, 0x61, 0x44, 0xbf, 0xa0, 0x6f, 0x0, 0x94, 0x10, 0x67, 0x54, 0xb7, 0x9c, 0x24, 0xc3, 0xb9, 0xcb, 0x20, 0x1, 0xf2, 0xba, 0xd, 0x28, 0x42, 0x51, 0xc1, 0x70, 0xdc, 0xae, 0x2f, 0x34}, partyUInfo: []byte{0x4d, 0x2f, 0xe0, 0x86, 0x78, 0x22, 0xb1, 0x5d, 0xbe, 0x6a, 0x14, 0x7c, 0xd5, 0x19, 0xf7, 0x68, 0x41, 0x25, 0x6e, 0xb, 0xe3, 0x2b, 0x2d, 0x53, 0x13, 0x31, 0xc8, 0xaa, 0xc2, 0xf9, 0x71, 0x25}, partyVInfo: []byte{0xbc, 0xca, 0x60, 0x47, 0xb4, 0xe0, 0x7d, 0x5f, 0x92, 0x88, 0x6a, 0xc3, 0x6a, 0x3d, 0x48, 0xaf, 0x5d, 0x6f, 0x34, 0x10, 0xc7, 0x7c, 0x7d, 0x4f, 0xa6, 0xe0, 0xd3, 0x4d, 0x6a, 0x8f, 0xbd, 0xdc}, expected: []byte{0x1, 0x24, 0xfc, 0x70, 0xf4, 0xf0, 0x26, 0xcd, 0x92, 0x45, 0x14, 0x2a, 0xdc, 0xa8, 0x3a, 0x0, 0x77, 0x32, 0x16, 0x3, 0x8, 0x92, 0xaf, 0x57, 0x92, 0xae, 0x48, 0xf5, 0xd0, 0x40, 0x74, 0x6d, 0x64, 0xe0, 0x68, 0x44, 0x2d, 0x49, 0x9, 0x99, 0x29, 0xfa, 0x2d, 0x8a, 0x4d, 0xd5, 0x6f, 0x46, 0xbf, 0x48, 0x3d, 0x1f, 0xc6, 0x58, 0x90, 0x3f, 0xd6, 0x2d, 0xc, 0x32, 0x6, 0x4b, 0xca, 0xa1, 0xa1, 0x3}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result, err := KDFe(test.hashAlg, test.z, label, test.partyUInfo, test.partyVInfo, test.bits) if err != nil { t.Fatal(err) } if !bytes.Equal(result, test.expected) { t.Errorf("got: %v\n expected: %v", result, test.expected) } }) } } go-tpm-0.9.3/legacy/tpm2/test/tpm2_other_test.go000066400000000000000000000021761473040456300215200ustar00rootroot00000000000000//go: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 tpm2 import ( "flag" "io" "testing" . "github.com/google/go-tpm/legacy/tpm2" ) var tpmPath = flag.String("tpm-path", "", "Path to TPM character device. Most Linux systems expose it under /dev/tpm0. Empty value (default) will disable all integration tests.") func useDeviceTPM() bool { return *tpmPath != "" } func openDeviceTPM(tb testing.TB) io.ReadWriteCloser { tb.Helper() rw, err := OpenTPM(*tpmPath) if err != nil { tb.Fatalf("Open TPM at %s failed: %s\n", *tpmPath, err) } return rw } go-tpm-0.9.3/legacy/tpm2/test/tpm2_test.go000066400000000000000000002151341473040456300203170ustar00rootroot00000000000000// 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/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "encoding/binary" "flag" "fmt" "hash" "io" "math" "math/big" "reflect" "strings" "testing" "github.com/google/go-tpm-tools/simulator" . "github.com/google/go-tpm/legacy/tpm2" "github.com/google/go-tpm/tpmutil" ) var ( runClear = flag.Bool("run-clear", false, "Set to run tests which will clear hierarchy and lockout authorizations") ) func init() { testing.Init() flag.Parse() } func openTPM(tb testing.TB) io.ReadWriteCloser { tb.Helper() if useDeviceTPM() { return openDeviceTPM(tb) } simulator, err := simulator.Get() if err != nil { tb.Fatalf("Simulator initialization failed: %v", err) } return simulator } var ( // PCR7 is for SecureBoot. pcrSelection7 = PCRSelection{Hash: AlgSHA1, PCRs: []int{7}} pcrSelectionAll = PCRSelection{Hash: AlgSHA1, PCRs: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}} defaultKeyParams = Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagStorageDefault, RSAParameters: &RSAParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, KeyBits: 2048, ExponentRaw: 1<<16 + 1, }, } defaultPassword = "\x01\x02\x03\x04" emptyPassword = "" defaultRsaSignerParams = Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } defaultEccSignerParams = Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, } nullTicketSigned = Ticket{Type: TagAuthSigned, Hierarchy: HandleNull} nullTicketSecret = Ticket{Type: TagAuthSecret, Hierarchy: HandleNull} ) func min(a, b int) int { if a < b { return a } return b } func TestGetRandom(t *testing.T) { rw := openTPM(t) defer rw.Close() if _, err := GetRandom(rw, 16); err != nil { t.Fatalf("GetRandom failed: %v", err) } } func TestReadPCRs(t *testing.T) { rw := openTPM(t) defer rw.Close() pcrs, err := ReadPCRs(rw, pcrSelection7) if err != nil { t.Errorf("ReadPCRs failed: %s", err) } if !useDeviceTPM() { return // PCR 7 is initialized to 0 in the simulator. } for pcr, val := range pcrs { if empty := make([]byte, len(val)); reflect.DeepEqual(empty, val) { t.Errorf("Value of PCR %d is empty", pcr) } } } func TestReadAllPCRs(t *testing.T) { rw := openTPM(t) defer rw.Close() numPCRs := len(pcrSelectionAll.PCRs) out := map[uint32][]byte{} for i := 0; i < numPCRs; i += 8 { // Build a selection structure, specifying 8 PCRs at a time end := min(i+8, numPCRs) pcrSel := PCRSelection{ Hash: pcrSelectionAll.Hash, PCRs: pcrSelectionAll.PCRs[i:end], } // Ask the TPM for those PCR values. ret, err := ReadPCRs(rw, pcrSel) if err != nil { t.Errorf("ReadPCRs(%+v) failed: %v", pcrSel, err) } // Keep track of the PCRs we were actually given. for pcr, digest := range ret { out[uint32(pcr)] = digest } } if len(out) != numPCRs { t.Errorf("Failed to read all PCRs, only read %d", len(out)) } } func TestReadClock(t *testing.T) { rw := openTPM(t) defer rw.Close() if _, _, err := ReadClock(rw); err != nil { t.Fatalf("ReadClock failed: %s", err) } } func TestGetCapability(t *testing.T) { rw := openTPM(t) defer rw.Close() for _, tt := range []struct { capa Capability count uint32 property uint32 typ interface{} }{ {CapabilityHandles, 1, uint32(HandleTypeTransient) << 24, tpmutil.Handle(0)}, {CapabilityAlgs, 1, 0, AlgorithmDescription{}}, {CapabilityTPMProperties, 1, uint32(NVMaxBufferSize), TaggedProperty{}}, } { l, _, err := GetCapability(rw, tt.capa, tt.count, tt.property) if err != nil { t.Fatalf("GetCapability(%v, %d, %d) = _, %v; want _, nil", tt.capa, tt.count, tt.property, err) } for _, i := range l { if reflect.TypeOf(i) != reflect.TypeOf(tt.typ) { t.Fatalf("GetCapability(%v, %d, %d) returned an element with the wrong type: %v; want %v", tt.capa, tt.count, tt.property, reflect.TypeOf(i), reflect.TypeOf(tt.typ)) } } } } func TestCombinedKeyTest(t *testing.T) { rw := openTPM(t) defer rw.Close() parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, parentHandle) privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, defaultPassword, defaultPassword, defaultKeyParams) if err != nil { t.Fatalf("CreateKey failed: %s", err) } keyHandle, name, err := Load(rw, parentHandle, defaultPassword, publicBlob, privateBlob) if err != nil { t.Fatalf("Load failed: %s", err) } defer FlushContext(rw, keyHandle) if _, err := DecodeName(bytes.NewBuffer(name)); err != nil { t.Errorf("DecodeName failed: %v", err) } if _, _, _, err := ReadPublic(rw, keyHandle); err != nil { t.Fatalf("ReadPublic failed: %s", err) } } func TestCombinedEndorsementTest(t *testing.T) { rw := openTPM(t) defer rw.Close() parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, parentHandle) privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, emptyPassword, defaultPassword, defaultKeyParams) if err != nil { t.Fatalf("CreateKey failed: %s", err) } keyHandle, nameData, err := Load(rw, parentHandle, emptyPassword, publicBlob, privateBlob) if err != nil { t.Fatalf("Load failed: %s", err) } defer FlushContext(rw, keyHandle) if _, err := DecodeName(bytes.NewBuffer(nameData)); err != nil { t.Errorf("DecodeName failed: %v", err) } _, name, _, err := ReadPublic(rw, keyHandle) if err != nil { t.Fatalf("ReadPublic failed: %s", err) } // Generate Credential credential := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10} credBlob, encryptedSecret0, err := MakeCredential(rw, parentHandle, credential, name) if err != nil { t.Fatalf("MakeCredential failed: %v", err) } recoveredCredential1, err := ActivateCredential(rw, keyHandle, parentHandle, defaultPassword, emptyPassword, credBlob, encryptedSecret0) if err != nil { t.Fatalf("ActivateCredential failed: %v", err) } if !bytes.Equal(credential, recoveredCredential1) { t.Fatalf("Credential and recovered credential differ: got %v, want %v", recoveredCredential1, credential) } recoveredCredential2, err := ActivateCredentialUsingAuth(rw, []AuthCommand{ {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(defaultPassword)}, {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, }, keyHandle, parentHandle, credBlob, encryptedSecret0) if err != nil { t.Fatalf("ActivateCredentialWithAuth failed: %v", err) } if !bytes.Equal(credential, recoveredCredential2) { t.Errorf("Credential and recovered credential differ: got %v, want %v", recoveredCredential2, credential) } _, err = ActivateCredentialUsingAuth(rw, []AuthCommand{ {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte("incorrect password")}, {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, }, keyHandle, parentHandle, credBlob, encryptedSecret0) if err == nil { t.Fatal("ActivateCredentialUsingAuth: error == nil, expected authorization failure") } if !strings.Contains(err.Error(), "the authorization HMAC check failed") { t.Errorf("ActivateCredentialUsingAuth: error = %v, expected authorization failure", err) } _, err = ActivateCredentialUsingAuth(rw, []AuthCommand{ {Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword)}, }, keyHandle, parentHandle, credBlob, encryptedSecret0) if err == nil { t.Fatal("ActivateCredentialUsingAuth: error == nil, expected response status 0x98e (authorization failure)") } if !strings.Contains(err.Error(), "len(auth) = 1, want 2") { t.Errorf("ActivateCredentialUsingAuth: error = %v, expected len(auth) = 1, want 2", err) } } func TestCreatePrimaryEx(t *testing.T) { rw := openTPM(t) defer rw.Close() keyHandle, pub1, creation, _, _, name, err := CreatePrimaryEx(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, keyHandle) pub, _, _, err := ReadPublic(rw, keyHandle) if err != nil { t.Fatalf("ReadPublic failed: %s", err) } pub2, err := pub.Encode() if err != nil { t.Fatalf("Failed to encode public: %v", err) } if !bytes.Equal(pub1, pub2) { t.Error("Mismatch between public returned from CreatePrimaryEx() & ReadPublic()") t.Logf("CreatePrimaryEx: %v", pub1) t.Logf("ReadPublic: %v", pub2) } if _, err := DecodeName(bytes.NewBuffer(name)); err != nil { t.Errorf("Failed to decode name: %v", err) } if _, err := DecodeCreationData(creation); err != nil { t.Fatalf("DecodeCreationData() returned err: %v", err) } } func TestCombinedContextTest(t *testing.T) { rw := openTPM(t) defer rw.Close() rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, rootHandle) // CreateKey (Quote Key) quotePrivate, quotePublic, _, _, _, err := CreateKey(rw, rootHandle, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreateKey failed: %v", err) } quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) if err != nil { t.Fatalf("Load failed: %v", err) } defer FlushContext(rw, quoteHandle) saveArea, err := ContextSave(rw, quoteHandle) if err != nil { t.Fatalf("ContextSave failed: %v", err) } FlushContext(rw, quoteHandle) if _, err = ContextLoad(rw, saveArea); err != nil { t.Fatalf("Load failed: %v", err) } } func TestEvictControl(t *testing.T) { rw := openTPM(t) defer rw.Close() rootHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, rootHandle) // CreateKey (Quote Key) quotePrivate, quotePublic, _, _, _, err := CreateKey(rw, rootHandle, pcrSelection7, emptyPassword, emptyPassword, defaultKeyParams) if err != nil { t.Fatalf("CreateKey failed: %v", err) } quoteHandle, _, err := Load(rw, rootHandle, emptyPassword, quotePublic, quotePrivate) if err != nil { t.Fatalf("Load failed: %v", err) } defer FlushContext(rw, quoteHandle) persistentHandle := tpmutil.Handle(0x817FFFFF) // Evict persistent key, if there is one already (e.g. last test run failed). if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { t.Logf("(expected) EvictControl failed: %v", err) } // Make key persistent. if err := EvictControl(rw, emptyPassword, HandleOwner, quoteHandle, persistentHandle); err != nil { t.Fatalf("EvictControl failed: %v", err) } // Evict persistent key. if err := EvictControl(rw, emptyPassword, HandleOwner, persistentHandle, persistentHandle); err != nil { t.Fatalf("EvictControl failed: %v", err) } } func TestHash(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, data []byte, hierarchy tpmutil.Handle, wantValidation bool) { gotDigest, gotValidation, err := Hash(rw, AlgSHA256, data, hierarchy) if err != nil { t.Fatalf("Hash failed: %v", err) } wantDigest := sha256.Sum256(data) if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("Hash(%q) returned digest %x, want %x", data, gotDigest, wantDigest) } if wantValidation && len(gotValidation.Digest) == 0 { t.Errorf("Hash(%q) unexpectedly returned empty validation ticket", data) } if !wantValidation && len(gotValidation.Digest) != 0 { t.Errorf("Hash(%q) unexpectedly returned non-empty validation ticket", data) } } t.Run("Null hierarchy", func(t *testing.T) { run(t, []byte("foobarbaz"), HandleNull, false) }) t.Run("Owner hierarchy", func(t *testing.T) { run(t, []byte("foobarbaz"), HandleOwner, true) }) t.Run("Starts with TPM_GENERATED_VALUE", func(t *testing.T) { run(t, []byte("\xffTCGbarbaz"), HandleOwner, false) }) } func testHashSequence(t *testing.T, rw io.ReadWriter, hierarchy tpmutil.Handle, hashAlg Algorithm, data []byte) ([]byte, *Ticket) { const ( maxDigestBuffer = 1024 seqAuth = "" ) seq, err := HashSequenceStart(rw, seqAuth, hashAlg) if err != nil { t.Fatalf("HashSequenceStart failed: %v", err) } defer FlushContext(rw, seq) for len(data) > maxDigestBuffer { if err = SequenceUpdate(rw, seqAuth, seq, data[:maxDigestBuffer]); err != nil { t.Fatalf("SequenceUpdate failed: %v", err) } data = data[maxDigestBuffer:] } digest, ticket, err := SequenceComplete(rw, seqAuth, seq, hierarchy, data) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } return digest, ticket } func TestHashSequence(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, data []byte, hierarchy tpmutil.Handle, wantValidation bool) { gotDigest, gotValidation := testHashSequence(t, rw, hierarchy, AlgSHA256, data) wantDigest := sha256.Sum256(data) if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("Hash(%q) returned digest %x, want %x", data, gotDigest, wantDigest) } if wantValidation && len(gotValidation.Digest) == 0 { t.Errorf("Hash(%q) unexpectedly returned empty validation ticket", data) } if !wantValidation && len(gotValidation.Digest) != 0 { t.Errorf("Hash(%q) unexpectedly returned non-empty validation ticket", data) } } bufferSizes := []int{512, 1024, 2048, 4096} for _, bufferSize := range bufferSizes { buffer := make([]byte, bufferSize) if _, err := rand.Read(buffer); err != nil { t.Fatalf("rand.Read failed: %v", err) } t.Run(fmt.Sprintf("Null hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, buffer, HandleNull, false) }) t.Run(fmt.Sprintf("Owner hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, buffer, HandleOwner, true) }) // TCG generated values from now on copy(buffer, []byte("\xffTCG")) t.Run(fmt.Sprintf("Starts with TPM_GENERATED_VALUE [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, buffer, HandleOwner, false) }) } } func skipOnUnsupportedAlg(t testing.TB, rw io.ReadWriter, alg Algorithm) { moreData := true for i := uint32(0); moreData; i++ { var err error var descs []interface{} descs, moreData, err = GetCapability(rw, CapabilityAlgs, 1, i) if err != nil { t.Fatalf("Could not get TPM algorithm capability: %v", err) } for _, desc := range descs { if desc.(AlgorithmDescription).ID == alg { return } } if !moreData { break } } t.Skipf("Algorithm %v is not supported by the TPM", alg) } func TestLoadExternalPublicKey(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, public Public, private Private) { t.Helper() h, _, err := LoadExternal(rw, public, private, HandleNull) if err != nil { t.Fatal(err) } defer FlushContext(rw, h) } t.Run("RSA", func(t *testing.T) { pk, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } rp := Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA1, }, KeyBits: 2048, ExponentRaw: uint32(pk.PublicKey.E), ModulusRaw: pk.PublicKey.N.Bytes(), }, } private := Private{ Type: AlgRSA, Sensitive: pk.Primes[0].Bytes(), } run(t, rp, private) }) t.Run("ECC", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } public := Public{ Type: AlgECC, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA1, }, CurveID: CurveNISTP256, Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, }, } private := Private{ Type: AlgECC, Sensitive: pk.D.Bytes(), } run(t, public, private) }) } func TestCertify(t *testing.T) { rw := openTPM(t) defer rw.Close() params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, params) if err != nil { t.Fatalf("CreatePrimary(signer) failed: %s", err) } defer FlushContext(rw, signerHandle) subjectHandle, subjectPub, err := CreatePrimary(rw, HandlePlatform, pcrSelection7, emptyPassword, defaultPassword, params) if err != nil { t.Fatalf("CreatePrimary(subject) failed: %s", err) } defer FlushContext(rw, subjectHandle) attest, sigRaw, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil) if err != nil { t.Errorf("Certify failed: %s", err) return } sig, err := DecodeSignature(bytes.NewBuffer(sigRaw)) if err != nil { t.Errorf("DecodeSignature failed: %s", err) return } attestHash := sha256.Sum256(attest) if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } t.Run("DecodeAttestationData", func(t *testing.T) { ad, err := DecodeAttestationData(attest) if err != nil { t.Fatal("DecodeAttestationData:", err) } params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, // Note: we don't include Exponent because CreatePrimary also // returns Public without it. ModulusRaw: subjectPub.(*rsa.PublicKey).N.Bytes(), }, } matches, err := ad.AttestedCertifyInfo.Name.MatchesPublic(params) if err != nil { t.Fatalf("AttestedCertifyInfo.Name.MatchesPublic error: %v", err) } if !matches { t.Error("Name in AttestationData doesn't match Public structure of subject") } }) } func TestCertifyEx(t *testing.T) { rw := openTPM(t) defer rw.Close() restrictedKeySignerFlags := FlagSignerDefault unrestrictedKeySignerFlags := FlagSign | FlagFixedTPM | FlagFixedParent | FlagSensitiveDataOrigin | FlagUserWithAuth testCases := []struct { description string attributes KeyProp keyScheme SigScheme passedScheme SigScheme shouldPass bool }{ {"Null-SHA1", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA1}, true}, {"Null-SHA256", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, true}, {"Null-Null", unrestrictedKeySignerFlags, SigScheme{Alg: AlgNull}, SigScheme{Alg: AlgNull}, false}, {"SHA256-Null", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgNull}, true}, {"SHA256-SHA256", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, true}, {"SHA256-SHA1", restrictedKeySignerFlags, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256}, SigScheme{Alg: AlgRSASSA, Hash: AlgSHA1}, false}, } for _, testCase := range testCases { params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: testCase.attributes, RSAParameters: &RSAParams{ Sign: &testCase.keyScheme, KeyBits: 2048, }, } t.Run(testCase.description, func(t *testing.T) { signerHandle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, defaultPassword, params) if err != nil { t.Fatalf("CreatePrimary(signer) failed: %s", err) } defer FlushContext(rw, signerHandle) subjectHandle, _, err := CreatePrimary(rw, HandlePlatform, PCRSelection{}, emptyPassword, defaultPassword, params) if err != nil { t.Fatalf("CreatePrimary(subject) failed: %s", err) } defer FlushContext(rw, subjectHandle) _, _, err = CertifyEx(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil, testCase.passedScheme) if err != nil && testCase.shouldPass { t.Errorf("CertifyEx expected to succeed but failed: %s", err) } else if err == nil && !testCase.shouldPass { t.Errorf("CertifyEx expected to fail but succeeded") } }) } } func TestCertifyExternalKey(t *testing.T) { rw := openTPM(t) defer rw.Close() params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, params) if err != nil { t.Fatalf("CreatePrimary(signer) failed: %s", err) } defer FlushContext(rw, signerHandle) run := func(t *testing.T, public Public, private Private) { t.Helper() subjectHandle, _, err := LoadExternal(rw, public, private, HandleNull) if err != nil { t.Fatalf("LoadExternal: %v", err) } defer FlushContext(rw, subjectHandle) attest, sigRaw, err := Certify(rw, emptyPassword, defaultPassword, subjectHandle, signerHandle, nil) if err != nil { t.Errorf("Certify failed: %s", err) return } sig, err := DecodeSignature(bytes.NewBuffer(sigRaw)) if err != nil { t.Errorf("DecodeSignature failed: %s", err) return } attestHash := sha256.Sum256(attest) if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } } t.Run("RSA", func(t *testing.T) { pk, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } public := Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA1, }, KeyBits: 2048, ExponentRaw: uint32(pk.PublicKey.E), ModulusRaw: pk.PublicKey.N.Bytes(), }, } private := Private{ Type: AlgRSA, Sensitive: pk.Primes[0].Bytes(), } run(t, public, private) }) t.Run("ECC", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } public := Public{ Type: AlgECC, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA1, }, CurveID: CurveNISTP256, Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, }, } private := Private{ Type: AlgECC, Sensitive: pk.D.Bytes(), } run(t, public, private) }) } func TestSign(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, pub Public) { signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, signerHandle) digest := sha256.Sum256([]byte("heyo")) sig, err := Sign(rw, signerHandle, defaultPassword, digest[:], nil, nil) if err != nil { t.Fatalf("Sign failed: %s", err) } switch signerPub := signerPub.(type) { case *rsa.PublicKey: switch pub.RSAParameters.Sign.Alg { case AlgRSASSA: if err := rsa.VerifyPKCS1v15(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } case AlgRSAPSS: if err := rsa.VerifyPSS(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature, nil); err != nil { t.Errorf("Signature verification failed: %v", err) } default: t.Errorf("unsupported signature algorithm 0x%x", pub.RSAParameters.Sign.Alg) } case *ecdsa.PublicKey: if !ecdsa.Verify(signerPub, digest[:], sig.ECC.R, sig.ECC.S) { t.Error("Signature verification failed") } } } t.Run("RSA SSA", func(t *testing.T) { run(t, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, }) }) t.Run("RSA PSS", func(t *testing.T) { run(t, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSAPSS, Hash: AlgSHA256, }, KeyBits: 2048, }, }) }) t.Run("ECC", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) run(t, Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, }) }) } func TestSignWithAttestationKey(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, data []byte, wantErr bool, pub Public) { signerHandle, signerPub, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, pub) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, signerHandle) digest, validation, err := Hash(rw, AlgSHA256, data, HandleOwner) if err != nil { t.Fatalf("Hash failed unexpectedly: %v", err) } sig, err := Sign(rw, signerHandle, defaultPassword, digest, validation, nil) if err != nil && !wantErr { t.Fatalf("Sign failed unexpectedly: %v", err) } if err == nil && wantErr { t.Fatalf("Sign succeeded unexpectedly: %v", sig) } if !wantErr { switch signerPub := signerPub.(type) { case *rsa.PublicKey: switch pub.RSAParameters.Sign.Alg { case AlgRSASSA: if err := rsa.VerifyPKCS1v15(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } case AlgRSAPSS: if err := rsa.VerifyPSS(signerPub, crypto.SHA256, digest[:], sig.RSA.Signature, nil); err != nil { t.Errorf("Signature verification failed: %v", err) } default: t.Errorf("unsupported signature algorithm 0x%x", pub.RSAParameters.Sign.Alg) } case *ecdsa.PublicKey: if !ecdsa.Verify(signerPub, digest[:], sig.ECC.R, sig.ECC.S) { t.Error("Signature verification failed") } } } } rsassa := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagRestricted | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } rsapss := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSign | FlagRestricted | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSAPSS, Hash: AlgSHA256, }, KeyBits: 2048, }, } ecdsa := Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSign | FlagRestricted | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, } t.Run("RSA SSA", func(t *testing.T) { run(t, []byte("test123"), false, rsassa) }) t.Run("RSA PSS", func(t *testing.T) { run(t, []byte("test123"), false, rsapss) }) t.Run("ECC", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) run(t, []byte("test123"), false, ecdsa) }) t.Run("RSA SSA with TPM_GENERATED_VALUE", func(t *testing.T) { run(t, []byte("\xffTCG123"), true, rsassa) }) t.Run("RSA PSS with TPM_GENERATED_VALUE", func(t *testing.T) { run(t, []byte("\xffTCG123"), true, rsapss) }) t.Run("ECC with TPM_GENERATED_VALUE", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) run(t, []byte("\xffTCG123"), true, ecdsa) }) } func TestPCREvent(t *testing.T) { rw := openTPM(t) defer rw.Close() debugPCR := uint32(16) arbitraryBytes := []byte{1} if err := PCREvent(rw, tpmutil.Handle(debugPCR), arbitraryBytes); err != nil { t.Fatal(err) } } func TestPCRExtend(t *testing.T) { rw := openTPM(t) defer rw.Close() tests := []struct { desc string hashAlg Algorithm hashSize int hashSum func([]byte) []byte }{ { desc: "SHA1", hashAlg: AlgSHA1, hashSize: sha1.Size, hashSum: func(in []byte) []byte { s := sha1.Sum(in) return s[:] }, }, { desc: "SHA256", hashAlg: AlgSHA256, hashSize: sha256.Size, hashSum: func(in []byte) []byte { s := sha256.Sum256(in) return s[:] }, }, } const pcr = int(16) for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { pcrValue := bytes.Repeat([]byte{0xF}, tt.hashSize) oldPCRValue, err := ReadPCR(rw, pcr, tt.hashAlg) if err != nil { t.Fatalf("Can't read PCR %d from the TPM: %s", pcr, err) } if err = PCRExtend(rw, tpmutil.Handle(pcr), tt.hashAlg, pcrValue, ""); err != nil { t.Fatalf("Failed to extend PCR %d: %s", pcr, err) } newPCRValue, err := ReadPCR(rw, pcr, tt.hashAlg) if err != nil { t.Fatalf("Can't read PCR %d from the TPM: %s", pcr, err) } finalPCR := tt.hashSum(append(oldPCRValue, pcrValue...)) if !bytes.Equal(finalPCR, newPCRValue) { t.Fatalf("PCRs not equal, got %x, want %x", finalPCR, newPCRValue) } }) } } func TestReadPCR(t *testing.T) { rw := openTPM(t) defer rw.Close() pcrVal, err := ReadPCR(rw, 16 /*pcr*/, AlgSHA256) if err != nil { t.Fatal(err) } if len(pcrVal) != 32 { t.Fatalf("Expected a 32 byte PCR value but got: %v", pcrVal) } } func TestPCRReset(t *testing.T) { rw := openTPM(t) defer rw.Close() allZeroBytes := make([]byte, 32) debugPCR := 16 var fakeHashSum [32]byte err := PCRExtend(rw, tpmutil.Handle(debugPCR), AlgSHA256, fakeHashSum[:], "") if err != nil { t.Fatal(err) } pcrVal, err := ReadPCR(rw, debugPCR, AlgSHA256) if err != nil { t.Fatal(err) } if bytes.Equal(allZeroBytes, pcrVal) { t.Fatal("PCR shouldn't be all zeros after PCRExtend") } err = PCRReset(rw, tpmutil.Handle(debugPCR)) if err != nil { t.Fatal(err) } pcrVal, err = ReadPCR(rw, debugPCR, AlgSHA256) if err != nil { t.Fatal(err) } if !bytes.Equal(allZeroBytes, pcrVal) { t.Fatal("PCR should be all zeros after PCRReset") } } func makeAttestationData() AttestationData { signer := tpmutil.Handle(100) return AttestationData{ Magic: 0xff544347, QualifiedSigner: Name{ Handle: &signer, }, ExtraData: []byte("foo"), ClockInfo: ClockInfo{ Clock: 3, ResetCount: 4, RestartCount: 5, Safe: 6, }, FirmwareVersion: 7, } } func TestEncodeDecodeCertifyAttestationData(t *testing.T) { ciQualifiedName := tpmutil.Handle(101) ad := makeAttestationData() ad.Type = TagAttestCertify ad.AttestedCertifyInfo = &CertifyInfo{ Name: Name{ Digest: &HashValue{ Alg: AlgSHA1, Value: make([]byte, crypto.SHA1.Size()), }, }, QualifiedName: Name{ Handle: &ciQualifiedName, }, } encoded, err := ad.Encode() if err != nil { t.Fatalf("error encoding AttestationData: %v", err) } decoded, err := DecodeAttestationData(encoded) if err != nil { t.Fatalf("error decoding AttestationData: %v", err) } if !reflect.DeepEqual(*decoded, ad) { t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, ad) } } func TestEncodeDecodeCreationAttestationData(t *testing.T) { ad := makeAttestationData() ad.Type = TagAttestCreation ad.AttestedCreationInfo = &CreationInfo{ Name: Name{ Digest: &HashValue{ Alg: AlgSHA1, Value: make([]byte, crypto.SHA1.Size()), }, }, OpaqueDigest: []byte{7, 8, 9}, } encoded, err := ad.Encode() if err != nil { t.Fatalf("error encoding AttestationData: %v", err) } decoded, err := DecodeAttestationData(encoded) if err != nil { t.Fatalf("error decoding AttestationData: %v", err) } if !reflect.DeepEqual(*decoded, ad) { t.Errorf("got decoded value:\n%v\nwant:\n%v", decoded, ad) } } func TestEncodeDecodePublicDefaultRSAExponent(t *testing.T) { p := Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA1, }, KeyBits: 2048, ExponentRaw: 1<<16 + 1, ModulusRaw: []byte{1, 2, 3, 4, 7, 8, 9, 9}, }, } e, err := p.Encode() if err != nil { t.Fatalf("Public{%+v}.Encode() returned error: %v", p, err) } d, err := DecodePublic(e) if err != nil { t.Fatalf("DecodePublic(%v) returned error: %v", e, err) } if !reflect.DeepEqual(p, d) { t.Errorf("RSA TPMT_PUBLIC with default exponent changed after being encoded+decoded") t.Logf("\tGot: %+v", d) t.Logf("\tWant: %+v", p) } } func TestEncodeDecodeSignature(t *testing.T) { randRSASig := func() []byte { // Key size 2048 bits var size uint16 = 256 sizeU16 := make([]byte, 2) binary.BigEndian.PutUint16(sizeU16, size) key := make([]byte, size) rand.Read(key) return append(sizeU16, key...) } run := func(t *testing.T, s Signature) { e, err := s.Encode() if err != nil { t.Fatalf("Signature{%+v}.Encode() returned error: %v", s, err) } d, err := DecodeSignature(bytes.NewBuffer(e)) if err != nil { t.Fatalf("DecodeSignature{%v} returned error: %v", e, err) } if !reflect.DeepEqual(s, *d) { t.Errorf("got decoded value:\n%v\nwant:\n%v", d, s) } } t.Run("RSASSA", func(t *testing.T) { run(t, Signature{ Alg: AlgRSASSA, RSA: &SignatureRSA{ HashAlg: AlgSHA256, Signature: randRSASig(), }, }) }) t.Run("RSAPSS", func(t *testing.T) { run(t, Signature{ Alg: AlgRSAPSS, RSA: &SignatureRSA{ HashAlg: AlgSHA256, Signature: randRSASig(), }, }) }) t.Run("ECDSA", func(t *testing.T) { // Key size 256 bits size := 32 randBytes := make([]byte, size) rand.Read(randBytes) r := big.NewInt(0).SetBytes(randBytes) rand.Read(randBytes) s := big.NewInt(0).SetBytes(randBytes) run(t, Signature{ Alg: AlgECDSA, ECC: &SignatureECC{ HashAlg: AlgSHA256, R: r, S: s, }, }) }) } func TestCreateKeyWithSensitive(t *testing.T) { rw := openTPM(t) defer rw.Close() parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagRestricted | FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, RSAParameters: &RSAParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, KeyBits: 2048, }, }) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, parentHandle) _, _, _, _, _, err = CreateKeyWithSensitive(rw, parentHandle, pcrSelection7, defaultPassword, defaultPassword, Public{ Type: AlgKeyedHash, NameAlg: AlgSHA256, Attributes: FlagFixedTPM | FlagFixedParent, AuthPolicy: nil, }, []byte("test_secret")) if err != nil { t.Errorf("CreateKeyWithSensitive failed: %s", err) } } func TestCreateAndCertifyCreation(t *testing.T) { rw := openTPM(t) defer rw.Close() params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault | FlagNoDA, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } keyHandle, pub, _, creationHash, tix, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params) if err != nil { t.Fatalf("CreatePrimaryEx failed: %s", err) } defer FlushContext(rw, keyHandle) scheme := SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256, Count: 0} attestation, sigRaw, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) if err != nil { t.Fatalf("CertifyCreation failed: %s", err) } att, err := DecodeAttestationData(attestation) if err != nil { t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) } signature, err := DecodeSignature(bytes.NewBuffer(sigRaw)) if err != nil { t.Errorf("DecodeSignature failed: %s", err) return } if att.Type != TagAttestCreation { t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type) } p, err := DecodePublic(pub) if err != nil { t.Fatalf("DecodePublic failed: %v", err) } match, err := att.AttestedCreationInfo.Name.MatchesPublic(p) if err != nil { t.Fatalf("MatchesPublic failed: %v", err) } if !match { t.Error("Attested name does not match returned public key.") t.Logf("Name: %v", att.AttestedCreationInfo.Name) t.Logf("Public: %v", p) } rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent()), N: p.RSAParameters.Modulus()} hsh := crypto.SHA256.New() hsh.Write(attestation) if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature.RSA.Signature); err != nil { t.Errorf("VerifyPKCS1v15 failed: %v", err) } } func TestCreateAndCertifyCreationECC(t *testing.T) { rw := openTPM(t) defer rw.Close() params := Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, } keyHandle, pub, _, creationHash, tix, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params) if err != nil { t.Fatalf("CreatePrimaryEx failed: %s", err) } defer FlushContext(rw, keyHandle) scheme := SigScheme{Alg: AlgECDSA, Hash: AlgSHA256, Count: 0} attestation, sigRaw, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) if err != nil { t.Fatalf("CertifyCreation failed: %s", err) } att, err := DecodeAttestationData(attestation) if err != nil { t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) } signature, err := DecodeSignature(bytes.NewBuffer(sigRaw)) if err != nil { t.Errorf("DecodeSignature failed: %s", err) return } if att.Type != TagAttestCreation { t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type) } p, err := DecodePublic(pub) if err != nil { t.Fatalf("DecodePublic failed: %v", err) } match, err := att.AttestedCreationInfo.Name.MatchesPublic(p) if err != nil { t.Fatalf("MatchesPublic failed: %v", err) } if !match { t.Error("Attested name does not match returned public key.") t.Logf("Name: %v", att.AttestedCreationInfo.Name) t.Logf("Public: %v", p) } var pkEcdsa ecdsa.PublicKey var hsh hash.Hash pkEcdsa = ecdsa.PublicKey{Curve: elliptic.P256(), X: p.ECCParameters.Point.X(), Y: p.ECCParameters.Point.Y()} signHash, err := p.ECCParameters.Sign.Hash.Hash() if err != nil { t.Fatalf("Hash failed: %v", err) } hsh = signHash.New() hsh.Write(attestation) if !ecdsa.Verify(&pkEcdsa, hsh.Sum(nil), signature.ECC.R, signature.ECC.S) { t.Fatalf("Verify failed") } } func TestNVReadWriteAndLocks(t *testing.T) { rw := openTPM(t) defer rw.Close() var ( idx tpmutil.Handle = 0x1500000 data = []byte("testdata") attr = AttrOwnerWrite | AttrOwnerRead | AttrWriteSTClear | AttrReadSTClear ) // Undefine the space, just in case the previous run of this test failed // to clean up. if err := NVUndefineSpace(rw, emptyPassword, HandleOwner, idx); err != nil { t.Logf("(not a failure) NVUndefineSpace at index 0x%x failed: %v", idx, err) } // Define space in NV storage and clean up afterwards or subsequent runs will fail. if err := NVDefineSpace(rw, HandleOwner, idx, emptyPassword, emptyPassword, nil, attr, uint16(len(data)), ); err != nil { t.Fatalf("NVDefineSpace failed: %v", err) } defer NVUndefineSpace(rw, emptyPassword, HandleOwner, idx) // Write the data if err := NVWrite(rw, HandleOwner, idx, emptyPassword, data, 0); err != nil { t.Fatalf("NVWrite failed: %v", err) } // Enable write lock if err := NVWriteLock(rw, HandleOwner, idx, emptyPassword); err != nil { t.Fatalf("NVWriteLock failed: %v", err) } // Write the data again. Should fail now because it's write-locked. err := NVWrite(rw, HandleOwner, idx, emptyPassword, data, 0) switch err := err.(type) { case nil: t.Fatal("NVWrite succeeded after NVWriteLock") case Error: if err.Code != RCNVLocked { t.Fatalf("NVWrite: unexpected error; want RCNVLocked, got %v", err) } default: t.Fatalf("NVWrite: unexpected error; want RCNVLocked, got %v", err) } // Make sure the public area of the index can be read pub, err := NVReadPublic(rw, idx) if err != nil { t.Fatalf("NVReadPublic failed: %v", err) } if int(pub.DataSize) != len(data) { t.Fatalf("public NV data size mismatch, got %d, want %d, ", pub.DataSize, len(data)) } // Read all of the data with NVReadEx and compare to what was written outdata, err := NVReadEx(rw, idx, HandleOwner, emptyPassword, 0) if err != nil { t.Fatalf("NVReadEx failed: %v", err) } if !bytes.Equal(data, outdata) { t.Fatalf("data read from NV index does not match, got %x, want %x", outdata, data) } // Enable read lock if err := NVReadLock(rw, HandleOwner, idx, emptyPassword); err != nil { t.Fatalf("NVReadLock failed: %v (%T)", err, err) } // Read the data again. Should fail now because it's read-locked. if _, err := NVReadEx(rw, idx, HandleOwner, emptyPassword, 0); err == nil { t.Fatal("NVRead succeeded after NVReadLock") } else if !strings.HasSuffix(err.Error(), ": NV access locked") { t.Fatalf("NVRead: unexpected error; want RCNVLocked, got %v", err) } } func TestPolicySecret(t *testing.T) { rw := openTPM(t) defer rw.Close() expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} for _, expiration := range expirations { t.Run(t.Name()+fmt.Sprint(expiration), func(t *testing.T) { _, tkt := testPolicySecret(t, rw, expiration) // Part 3: policyTicket is produced if the command succeeds and expiration in // the command was non-zero. // If expiration is non-negative, a NULL Ticket is returned. if expiration < 0 && len(tkt.Digest) == 0 { t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicketSecret) { t.Fatalf("Got ticket with non-empty digest, expected NULL ticket") } }) } } func testPolicySecret(t *testing.T, rw io.ReadWriter, expiration int32) ([]byte, *Ticket) { sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) if err != nil { t.Fatalf("StartAuthSession() failed: %v", err) } defer FlushContext(rw, sessHandle) timeout, tkt, err := PolicySecret(rw, HandleEndorsement, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, sessHandle, nil, nil, nil, expiration) if err != nil { t.Fatalf("PolicySecret() failed: %v", err) } return timeout, tkt } func TestPolicySigned(t *testing.T) { rw := openTPM(t) defer rw.Close() signers := map[string]Public{ "RSA": defaultRsaSignerParams, "ECC": defaultEccSignerParams, } expirations := []int32{math.MinInt32, math.MinInt32 + 1, -1, 0, 1, math.MaxInt32} for _, expiration := range expirations { for signerType, params := range signers { t.Run(t.Name()+signerType+fmt.Sprint(expiration), func(t *testing.T) { _, tkt := testPolicySigned(t, rw, expiration, params) // Part 3: policyTicket is produced if the command succeeds and expiration in // the command was non-zero. // If expiration is non-negative, a NULL Ticket is returned. if expiration < 0 && len(tkt.Digest) == 0 { t.Fatalf("Got empty ticket digest, expected ticket with auth expiry") } else if expiration >= 0 && !reflect.DeepEqual(*tkt, nullTicketSigned) { t.Fatalf("Got ticket with non-empty digest, expected NULL ticket") } }) } } } func testPolicySigned(t *testing.T, rw io.ReadWriter, expiration int32, signerParams Public) ([]byte, *Ticket) { handle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, emptyPassword, signerParams) if err != nil { t.Fatalf("CreatePrimary() failed: %s", err) } defer FlushContext(rw, handle) sessHandle, nonce, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) if err != nil { t.Fatalf("StartAuthSession() failed: %v", err) } defer FlushContext(rw, sessHandle) // Sign the hash of the command parameters, as described in the TPM 2.0 spec, Part 3, Section 23.3. // We only use expiration here. expBytes := make([]byte, 4) binary.BigEndian.PutUint32(expBytes, uint32(expiration)) // TPM2.0 spec, Revision 1.38, Part 3 nonce must be present if expiration is non-zero. // aHash ≔ HauthAlg(nonceTPM || expiration || cpHashA || policyRef) toDigest := append(nonce, expBytes...) digest := sha256.Sum256(toDigest) sig, err := Sign(rw, handle, emptyPassword, digest[:], nil, nil) if err != nil { t.Fatalf("Sign failed: %s", err) } signature, err := sig.Encode() if err != nil { t.Fatalf("Encode() failed: %v", err) } timeout, tkt, err := PolicySigned(rw, handle, sessHandle, nonce, nil, nil, expiration, signature) if err != nil { t.Fatalf("PolicySigned() failed: %v", err) } return timeout, tkt } func TestQuote(t *testing.T) { rw := openTPM(t) defer rw.Close() params := Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault | FlagNoDA, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } keyHandle, pub, _, _, _, _, err := CreatePrimaryEx(rw, HandleEndorsement, pcrSelection7, emptyPassword, emptyPassword, params) if err != nil { t.Fatalf("CreatePrimaryEx failed: %s", err) } defer FlushContext(rw, keyHandle) attestation, signature, err := Quote(rw, keyHandle, emptyPassword, emptyPassword, nil, pcrSelection7, AlgNull) if err != nil { t.Fatalf("Quote failed: %v", err) } att, err := DecodeAttestationData(attestation) if err != nil { t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) } if att.Type != TagAttestQuote { t.Errorf("Got att.Type = %v, want TagAttestQuote", att.Type) } if att.AttestedQuoteInfo == nil { t.Error("AttestedQuoteInfo = nil, want non-nil") } p, err := DecodePublic(pub) if err != nil { t.Fatalf("DecodePublic failed: %v", err) } rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent()), N: p.RSAParameters.Modulus()} hsh := crypto.SHA256.New() hsh.Write(attestation) if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature.RSA.Signature); err != nil { t.Errorf("VerifyPKCS1v15 failed: %v", err) } } func TestReadPublicKey(t *testing.T) { rw := openTPM(t) defer rw.Close() run := func(t *testing.T, public Public, private Private, pubKeyIn crypto.PublicKey) { t.Helper() h, _, err := LoadExternal(rw, public, private, HandleNull) if err != nil { t.Fatal(err) } defer FlushContext(rw, h) pub, _, _, err := ReadPublic(rw, h) if err != nil { t.Fatalf("ReadPublic failed: %s", err) } pubKeyOut, err := pub.Key() if err != nil { t.Fatalf("Public.Key() failed: %s", err) } if !reflect.DeepEqual(pubKeyIn, pubKeyOut) { t.Fatalf("Public.Key() = %#v; want %#v", pubKeyOut, pubKeyIn) } } t.Run("RSA", func(t *testing.T) { pk, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } rp := Public{ Type: AlgRSA, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA1, }, KeyBits: 2048, ExponentRaw: uint32(pk.PublicKey.E), ModulusRaw: pk.PublicKey.N.Bytes(), }, } private := Private{ Type: AlgRSA, Sensitive: pk.Primes[0].Bytes(), } run(t, rp, private, &pk.PublicKey) }) t.Run("ECC", func(t *testing.T) { skipOnUnsupportedAlg(t, rw, AlgECC) pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } public := Public{ Type: AlgECC, NameAlg: AlgSHA1, Attributes: FlagSign | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA1, }, CurveID: CurveNISTP256, Point: ECPoint{XRaw: pk.PublicKey.X.Bytes(), YRaw: pk.PublicKey.Y.Bytes()}, }, } private := Private{ Type: AlgECC, Sensitive: pk.D.Bytes(), } run(t, public, private, &pk.PublicKey) }) } func TestEncryptDecrypt(t *testing.T) { rw := openTPM(t) defer rw.Close() parentHandle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagRestricted | FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, RSAParameters: &RSAParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, KeyBits: 2048, }, }) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, parentHandle) privateBlob, publicBlob, _, _, _, err := CreateKey(rw, parentHandle, pcrSelection7, defaultPassword, defaultPassword, Public{ Type: AlgSymCipher, NameAlg: AlgSHA256, Attributes: FlagDecrypt | FlagSign | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, SymCipherParameters: &SymCipherParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, }, }) if err != nil { t.Fatalf("CreateKey failed: %s", err) } key, _, err := Load(rw, parentHandle, defaultPassword, publicBlob, privateBlob) if err != nil { t.Fatalf("Load failed: %s", err) } defer FlushContext(rw, key) data := bytes.Repeat([]byte("a"), 1e4) // 10KB iv := make([]byte, 16) encrypted, err := EncryptSymmetric(rw, defaultPassword, key, iv, data) if err != nil { t.Fatalf("EncryptSymmetric failed: %s", err) } if bytes.Equal(encrypted, data) { t.Error("encrypted blob matches unenecrypted data") } decrypted, err := DecryptSymmetric(rw, defaultPassword, key, iv, encrypted) if err != nil { t.Fatalf("DecryptSymmetric failed: %s", err) } if !bytes.Equal(decrypted, data) { t.Errorf("got decrypted data: %q, want: %q", decrypted, data) } } func TestRSAEncryptDecrypt(t *testing.T) { rw := openTPM(t) defer rw.Close() handle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgNull, Hash: AlgNull, }, KeyBits: 2048, }, }) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, handle) tests := map[string]struct { scheme *AsymScheme data []byte label string }{ "No padding": { scheme: &AsymScheme{Alg: AlgNull}, data: bytes.Repeat([]byte("a"), 256), }, "RSAES-PKCS1": { scheme: &AsymScheme{Alg: AlgRSAES}, data: bytes.Repeat([]byte("a"), 245), }, "RSAES-OAEP-SHA1": { scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA1}, data: bytes.Repeat([]byte("a"), 214), }, "RSAES-OAEP-SHA256": { scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA256}, data: bytes.Repeat([]byte("a"), 190), }, "RSAES-OAEP-SHA256 with label": { scheme: &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA256}, data: bytes.Repeat([]byte("a"), 190), label: "label", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { encrypted, err := RSAEncrypt(rw, handle, test.data, test.scheme, test.label) if err != nil { t.Fatal("RSAEncrypt failed:", err) } decrypted, err := RSADecrypt(rw, handle, defaultPassword, encrypted, test.scheme, test.label) if err != nil { t.Fatal("RSADecrypt failed:", err) } if !bytes.Equal(decrypted, test.data) { t.Errorf("got decrypted data: %q, want: %q", decrypted, test.data) } }) } } func TestECDHKeyGen(t *testing.T) { rw := openTPM(t) defer rw.Close() // Generate my key and load the public point into the TPM. myPriv, myPubX, myPubY, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("GenerateKey failed: %v", err) } handle, _, err := LoadExternal(rw, Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagDecrypt, ECCParameters: &ECCParams{ CurveID: CurveNISTP256, Point: ECPoint{XRaw: myPubX.Bytes(), YRaw: myPubY.Bytes()}, }, }, Private{}, HandleOwner) if err != nil { t.Fatalf("LoadExternal failed: %v", err) } defer FlushContext(rw, handle) // Ask the TPM to multiply an ephemeral priv by our pub. yourZ, yourPub, err := ECDHKeyGen(rw, handle) if err != nil { t.Fatalf("ECDHKeyGen failed: %v", err) } // Same calculation on our side: multiply our priv by the TPM's ephemeral pub. myZX, myZY := elliptic.P256().ScalarMult(yourPub.X(), yourPub.Y(), myPriv) if myZX.Cmp(yourZ.X()) != 0 || myZY.Cmp(yourZ.Y()) != 0 { t.Errorf("want (%x, %x) got (%x, %x)", myZX.Bytes(), myZY.Bytes(), yourZ.X().Bytes(), yourZ.Y().Bytes()) } } func TestECDHZGen(t *testing.T) { rw := openTPM(t) defer rw.Close() // Generate our key. myPriv, myPubX, myPubY, err := elliptic.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("GenerateKey failed: %v", err) } // Generate a key in the TPM. handle, _, err := CreatePrimary(rw, HandleOwner, PCRSelection{}, emptyPassword, defaultPassword, Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagDecrypt | FlagSensitiveDataOrigin | FlagUserWithAuth, ECCParameters: &ECCParams{ CurveID: CurveNISTP256, }, }) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, handle) // Read the public key from the TPM. yourPub, _, _, err := ReadPublic(rw, handle) if err != nil { t.Fatalf("ReadPublic failed: %v", err) } // Ask the TPM to multiply our public point by its private key. yourZ, err := ECDHZGen(rw, handle, defaultPassword, ECPoint{ XRaw: myPubX.Bytes(), YRaw: myPubY.Bytes(), }) if err != nil { t.Fatalf("ECDHZGen failed: %v", err) } // Same calculation on our side: multiply our priv by the TPM's pub. myZX, myZY := elliptic.P256().ScalarMult(yourPub.ECCParameters.Point.X(), yourPub.ECCParameters.Point.Y(), myPriv) if myZX.Cmp(yourZ.X()) != 0 || myZY.Cmp(yourZ.Y()) != 0 { t.Errorf("want (%x, %x) got (%x, %x)", myZX.Bytes(), myZY.Bytes(), yourZ.X().Bytes(), yourZ.Y().Bytes()) } } func TestCreatePrimaryRawTemplate(t *testing.T) { rw := openTPM(t) defer rw.Close() pubRaw, err := defaultKeyParams.Encode() if err != nil { t.Fatal(err) } kh, pub, err := CreatePrimaryRawTemplate(rw, HandleEndorsement, PCRSelection{}, "", "", pubRaw) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } defer FlushContext(rw, kh) pubRSA, ok := pub.(*rsa.PublicKey) if !ok { t.Fatalf("got public key type %T, want *rsa.PublicKey", pub) } gotKeySize, wantKeySize := pubRSA.Size(), int(defaultKeyParams.RSAParameters.KeyBits/8) if gotKeySize != wantKeySize { t.Errorf("got key size %v, want %v", gotKeySize, wantKeySize) } if pubRSA.E != int(defaultKeyParams.RSAParameters.Exponent()) { t.Errorf("got key exponent %v, want %v", pubRSA.E, defaultKeyParams.RSAParameters.Exponent()) } } func TestMatchesTemplate(t *testing.T) { tests := []struct { name string makePublic func() Public goodChange func(*Public) badChange func(*Public) }{ { "RSA", func() Public { return Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgRSASSA, Hash: AlgSHA256, }, KeyBits: 2048, }, } }, func(pub *Public) { pub.RSAParameters.ModulusRaw = make([]byte, 256) }, func(pub *Public) { pub.RSAParameters.KeyBits = 1024 }, }, { "ECC", func() Public { return Public{ Type: AlgECC, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, ECCParameters: &ECCParams{ Sign: &SigScheme{ Alg: AlgECDSA, Hash: AlgSHA256, }, CurveID: CurveNISTP256, }, } }, func(pub *Public) { pub.ECCParameters.Point.XRaw = make([]byte, 32) }, func(pub *Public) { pub.ECCParameters.CurveID = CurveNISTP384 }, }, { "SymCipher", func() Public { return Public{ Type: AlgSymCipher, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, SymCipherParameters: &SymCipherParams{ Symmetric: &SymScheme{ Alg: AlgAES, KeyBits: 128, Mode: AlgCFB, }, }, } }, func(pub *Public) { pub.SymCipherParameters.Unique = make([]byte, 256) }, func(pub *Public) { pub.SymCipherParameters.Symmetric.KeyBits = 256 }, }, { "KeyedHash", func() Public { return Public{ Type: AlgKeyedHash, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, KeyedHashParameters: &KeyedHashParams{ Alg: AlgHMAC, Hash: AlgSHA256, }, } }, func(pub *Public) { pub.KeyedHashParameters.Unique = make([]byte, 256) }, func(pub *Public) { pub.KeyedHashParameters.Hash = AlgSHA1 }, }, { "TypeMismatch", func() Public { return Public{ Type: AlgKeyedHash, NameAlg: AlgSHA256, Attributes: FlagSignerDefault, KeyedHashParameters: &KeyedHashParams{Alg: AlgNull}, } }, func(pub *Public) { pub.KeyedHashParameters.Unique = make([]byte, 256) }, func(pub *Public) { pub.Type = AlgRSA }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { template := test.makePublic() pub := test.makePublic() test.goodChange(&pub) if !pub.MatchesTemplate(template) { t.Error("Change should not cause template mismatch") } encTmpl, err := template.Encode() if err != nil { t.Fatal(err) } decTmpl, err := DecodePublic(encTmpl) if err != nil { t.Fatal(err) } if !pub.MatchesTemplate(decTmpl) { t.Error("Encoding/Decoding should not cause template mismatch") } test.badChange(&pub) if pub.MatchesTemplate(template) { t.Error("Change should cause template mismatch") } }) } } func TestPlainImport(t *testing.T) { // As this test imports a key without using an inner or outer wrapper, the // sensitive data is NOT encrypted. This setup should not actually be used. rw := openTPM(t) defer rw.Close() // Create an EK that doesn't require authorization ekHandle, _, err := CreatePrimary(rw, HandleEndorsement, PCRSelection{}, "", "", defaultKeyParams) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, ekHandle) emptyAuth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession} inBuff := make([]byte, 18) io.ReadFull(rand.Reader, inBuff) // Create a private area containing the input private := Private{ Type: AlgKeyedHash, AuthValue: nil, SeedValue: make([]byte, 32), Sensitive: inBuff, } io.ReadFull(rand.Reader, private.SeedValue) privArea, err := private.Encode() if err != nil { t.Fatalf("Private encoding error: %s", err) } // We just use a plain Pack here, as there is no integrity checking. duplicate, err := tpmutil.Pack(tpmutil.U16Bytes(privArea)) if err != nil { t.Fatalf("Duplicate encoding error: %s", err) } // The corresponding Public area contains the hash of the Private area. privHash := crypto.SHA256.New() privHash.Write(private.SeedValue) privHash.Write(private.Sensitive) public := Public{ Type: AlgKeyedHash, NameAlg: AlgSHA256, Attributes: FlagUserWithAuth, KeyedHashParameters: &KeyedHashParams{ Alg: AlgNull, Unique: privHash.Sum(nil), }, } pubArea, err := public.Encode() if err != nil { t.Fatalf("Public encoding error: %s", err) } privInternal, err := Import(rw, ekHandle, emptyAuth, pubArea, duplicate, nil, nil, nil) if err != nil { t.Fatalf("Import failed: %s", err) } newHandle, _, err := Load(rw, ekHandle, "", pubArea, privInternal) if err != nil { t.Fatalf("Load failed: %s", err) } defer FlushContext(rw, newHandle) outBuff, err := Unseal(rw, newHandle, "") if err != nil { t.Fatalf("Unseal failed: %s", err) } if !bytes.Equal(outBuff, inBuff) { t.Errorf("Got %X, expected %X", outBuff, inBuff) } } func TestClear(t *testing.T) { if !*runClear { t.Skip("Missing flag: run-clear. Test skipped") } rw := openTPM(t) defer rw.Close() err := Clear(rw, HandleLockout, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}) if err != nil { t.Fatalf("Clear failed: %v", err) } } func TestHierarchyChangeAuth(t *testing.T) { if !*runClear { t.Skip("Missing flag: run-clear. Test skipped") } rw := openTPM(t) defer rw.Close() err := Clear(rw, HandleLockout, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}) if err != nil { t.Fatalf("Clear failed: %v", err) } err = HierarchyChangeAuth(rw, HandleOwner, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, "pass1") if err != nil { t.Fatalf("HierarchyChangeAuth failed: %v", err) } // try to set password again, providing invalid auth err = HierarchyChangeAuth(rw, HandleOwner, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}, "pass2") if err == nil { t.Fatal("Expected HierarchyChangeAuth to fail") } // set password again, providing valid auth err = HierarchyChangeAuth(rw, HandleOwner, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte("pass1")}, "pass3") if err != nil { t.Fatalf("HierarchyChangeAuth failed: %v", err) } err = Clear(rw, HandleLockout, AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession}) if err != nil { t.Fatalf("Clear failed: %v", err) } } func TestPolicyPCR(t *testing.T) { rw := openTPM(t) defer rw.Close() sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA1) if err != nil { t.Fatalf("StartAuthSession failed: %v", err) } defer FlushContext(rw, sessHandle) sel := PCRSelection{Hash: AlgSHA1, PCRs: []int{0, 1, 2, 3}} pcrs, err := ReadPCRs(rw, sel) if err != nil { t.Fatalf("ReadPCRs failed: %s", err) } // From TPM 2.0 Part 1, Selecting Multiple PCR: // "The list of selectors is processed in order. The selected PCR are // concatenated, with the lowest numbered PCR in the first selector being // the first in the list and the highest numbered PCR in the last selector // being the last." // // Concatenate all selected PCR values before hashing. sel.PCRs is already // sorted in ascending order. var expectedVal []byte for _, pcr := range sel.PCRs { expectedVal = append(expectedVal, pcrs[pcr]...) } t.Logf("expectedVal=%x", expectedVal) // Hash algorithm must match the one in StartAuthSession. expectedDigest := sha1.Sum(expectedVal) t.Logf("expectedDigest=%x", expectedDigest) tests := []struct { desc string expectedDigest []byte wantErr bool }{ // PolicyPCR expects a *digest* of the PCR value, not the value itself. // Make sure PolicyPCR with value actually fails. {desc: "success: digest passed in expectedDigest", expectedDigest: expectedDigest[:]}, {desc: "fail: value passed in expectedDigest", expectedDigest: expectedVal, wantErr: true}, {desc: "success: nil passed in expectedDigest", expectedDigest: nil}, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { err := PolicyPCR(rw, sessHandle, tt.expectedDigest, sel) if (err != nil) != tt.wantErr { t.Fatalf("got error: %v, want error: %v", err != nil, tt.wantErr) } }) } } func TestDictionaryAttackParameters(t *testing.T) { rw := openTPM(t) defer rw.Close() testCases := []struct { name string maxTries uint32 recoveryTime uint32 lockoutRecovery uint32 }{ { name: "0 0 0", maxTries: 0, recoveryTime: 0, lockoutRecovery: 0, }, { name: "5 10 10", maxTries: 5, recoveryTime: 10, lockoutRecovery: 10, }, } auth := AuthCommand{ Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword), } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if err := DictionaryAttackParameters(rw, auth, tc.maxTries, tc.recoveryTime, tc.lockoutRecovery); err != nil { t.Fatalf("DictionaryAttackParameters failed: %v", err) } caps, _, err := GetCapability(rw, CapabilityTPMProperties, 3, uint32(MaxAuthFail)) if err != nil { t.Fatalf("GetCapability failed: %v", err) } if caps[0].(TaggedProperty).Value != tc.maxTries { t.Fatalf("got %d, expected %d", caps[0].(TaggedProperty).Value, tc.maxTries) } if caps[1].(TaggedProperty).Value != tc.recoveryTime { t.Fatalf("got %d, expected %d", caps[1].(TaggedProperty).Value, tc.recoveryTime) } if caps[2].(TaggedProperty).Value != tc.lockoutRecovery { t.Fatalf("got %d, expected %d", caps[2].(TaggedProperty).Value, tc.lockoutRecovery) } }) } } func TestDictionaryAttackLockReset(t *testing.T) { rw := openTPM(t) defer rw.Close() auth := AuthCommand{ Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(emptyPassword), } err := DictionaryAttackParameters(rw, auth, 5, 60, 60) if err != nil { t.Fatalf("DictionaryAttackParameters failed: %v", err) } handle, _, err := CreatePrimary(rw, HandleOwner, pcrSelection7, emptyPassword, defaultPassword, Public{ Type: AlgRSA, NameAlg: AlgSHA256, Attributes: FlagDecrypt | FlagUserWithAuth | FlagFixedParent | FlagFixedTPM | FlagSensitiveDataOrigin, RSAParameters: &RSAParams{ Sign: &SigScheme{ Alg: AlgNull, Hash: AlgNull, }, KeyBits: 2048, }, }) if err != nil { t.Fatalf("CreatePrimary failed: %s", err) } defer FlushContext(rw, handle) scheme := &AsymScheme{Alg: AlgOAEP, Hash: AlgSHA256} label := "label" encrypted, err := RSAEncrypt(rw, handle, bytes.Repeat([]byte("a"), 190), scheme, label) if err != nil { t.Fatalf("RSAEncrypt failed: %v", err) } // try RSADecrypt with bad password if _, err = RSADecrypt(rw, handle, "bad password", encrypted, scheme, label); err != nil { if serr, ok := err.(SessionError); !ok || serr.Code != RCAuthFail { t.Fatalf("RSADecrypt fails with unexpected error: %v", err) } } caps, _, err := GetCapability(rw, CapabilityTPMProperties, 1, uint32(LockoutCounter)) if err != nil { t.Fatalf("GetCapability failed: %v", err) } if caps[0].(TaggedProperty).Value != 1 { t.Fatalf("got %d, expected 1", caps[0].(TaggedProperty).Value) } if err = DictionaryAttackLockReset(rw, auth); err != nil { t.Fatalf("DictionaryAttackLockReset failed: %v", err) } caps, _, err = GetCapability(rw, CapabilityTPMProperties, 1, uint32(LockoutCounter)) if err != nil { t.Fatalf("GetCapability failed: %v", err) } if caps[0].(TaggedProperty).Value != 0 { t.Fatalf("got %d, expected 0", caps[0].(TaggedProperty).Value) } } func TestPolicyOr(t *testing.T) { rw := openTPM(t) defer rw.Close() hashAlg, err := AlgSHA256.Hash() if err != nil { t.Errorf("looking up hash algo with error: %v", err) } zeroHash := make([]byte, hashAlg.Size()) customHash := make([]byte, hashAlg.Size()) _, err = rand.Read(customHash) if err != nil { t.Errorf("random read into byte slice with error: %v", err) } tpml := TPMLDigest{Digests: []tpmutil.U16Bytes{ zeroHash, customHash}, } sessHandle, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) defer FlushContext(rw, sessHandle) if err != nil { t.Errorf("StartAuthSession failed: %v", err) } err = PolicyOr(rw, sessHandle, tpml) if err != nil { t.Errorf("PolicyOr with error: %v", err) } } func TestNVUndefineSpaceSpecial(t *testing.T) { rw := openTPM(t) defer rw.Close() // Set up trial policy session for creation of PolicyCommandCode policy. The policy will be bound as 'delete policy' to the nv index. sess, _, err := StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionTrial, AlgNull, AlgSHA256) if err != nil { t.Errorf("StartAuthSession() for policy generation failed: %v", err) } err = PolicyCommandCode(rw, sess, CmdNVUndefineSpaceSpecial) if err != nil { t.Errorf("PolicyCommandCode() failed: %v", err) } pol, err := PolicyGetDigest(rw, sess) if err != nil { t.Errorf("PolicyGetDigest() failed: %v", err) } if err := FlushContext(rw, sess); err != nil { t.Errorf("FlushContext() failed: %v", err) } // Test index with pubIndex := NVPublic{ NVIndex: 0x1500000, NameAlg: AlgSHA256, Attributes: AttrPlatformCreate | AttrPolicyDelete | AttrPolicyWrite | AttrNoDA | AttrAuthRead, AuthPolicy: pol, DataSize: 10, } // AuthCommand for platform hierarchy platformAuthCmd := AuthCommand{ Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: EmptyAuth, } // Create the index err = NVDefineSpaceEx(rw, HandlePlatform, "", pubIndex, platformAuthCmd) if err != nil { t.Errorf("NVDefineSpaceEx() failed: %v", err) } // Start new auth session for authentication of nv index access sess, _, err = StartAuthSession(rw, HandleNull, HandleNull, make([]byte, 16), nil, SessionPolicy, AlgNull, AlgSHA256) if err != nil { t.Errorf("StartAuthSession() for nv index auth failed: %v", err) } // Authorize UndefineSpaceSpecial by policy since it can't be authorized by auth value due to the attribute POLICY_DELETE on the index err = PolicyCommandCode(rw, sess, CmdNVUndefineSpaceSpecial) if err != nil { t.Errorf("PolicyCommandCode() or nv index auth failed: %v", err) } indexAuthCmd := AuthCommand{ Session: sess, Attributes: AttrContinueSession, Auth: EmptyAuth, } // Delete the index err = NVUndefineSpaceSpecial(rw, pubIndex.NVIndex, indexAuthCmd, platformAuthCmd) if err != nil { t.Errorf("NVUndefineSpaceSpecial() failed: %v", err) } } go-tpm-0.9.3/legacy/tpm2/test/tpm2_windows_test.go000066400000000000000000000020371473040456300220650ustar00rootroot00000000000000// 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 ( "flag" "io" "testing" . "github.com/google/go-tpm/legacy/tpm2" ) var runTPMTests = flag.Bool("use-tbs", false, "Run integration tests against Windows TPM Base Services (TBS). Defaults to false.") func useDeviceTPM() bool { return *runTPMTests } func openDeviceTPM(tb testing.TB) io.ReadWriteCloser { tb.Helper() rw, err := OpenTPM() if err != nil { tb.Fatalf("Open TPM failed: %s\n", err) } return rw } go-tpm-0.9.3/legacy/tpm2/tpm2.go000066400000000000000000002320551473040456300163020ustar00rootroot00000000000000// 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, outsideInfo []byte) ([]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 } outsideInfoBlob, err := tpmutil.Pack(tpmutil.U16Bytes(outsideInfo)) if err != nil { return nil, err } creationPCR, err := encodeTPMLPCRSelection(sel) if err != nil { return nil, err } return concat( parent, encodedAuth, inSensitive, publicBlob, outsideInfoBlob, 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, nil /*OutsideInfo*/) 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, outsideInfo []byte) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub, outsideInfo) 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, nil /*OutsideInfo*/) } // 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, nil /*OutsideInfo*/) } // 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, nil /*OutsideInfo*/) } // CreateKeyWithOutsideInfo is very similar to CreateKey, except // that it returns the outside information. func CreateKeyWithOutsideInfo(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public, outsideInfo []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, nil /*inSensitive*/, pub, sel, outsideInfo) } // 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{}, nil /*OutsideInfo*/) 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 } // Re-encode the name as a TPM2B_NAME so it can be parsed by DecodeName(). b := &bytes.Buffer{} if err := name.TPMMarshal(b); err != nil { return 0, nil, err } return handle, b.Bytes(), 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) ([]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, nil, fmt.Errorf("decoding timeout: %v", err) } var t Ticket if err := tpmutil.UnpackBuf(buf, &t); err != nil { return nil, nil, fmt.Errorf("decoding ticket: %v", err) } return timeout, &t, nil } // PolicySecret sets a secret authorization requirement on the provided entity. func PolicySecret(rw io.ReadWriter, entityHandle tpmutil.Handle, entityAuth AuthCommand, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32) ([]byte, *Ticket, error) { Cmd, err := encodePolicySecret(entityHandle, entityAuth, policyHandle, policyNonce, cpHash, policyRef, expiry) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagSessions, CmdPolicySecret, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodePolicySecret(resp) } func encodePolicySigned(validationKeyHandle tpmutil.Handle, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef tpmutil.U16Bytes, expiry int32, auth []byte) ([]byte, error) { handles, err := tpmutil.Pack(validationKeyHandle, policyHandle) if err != nil { return nil, err } params, err := tpmutil.Pack(policyNonce, cpHash, policyRef, expiry, auth) if err != nil { return nil, err } return concat(handles, params) } func decodePolicySigned(in []byte) ([]byte, *Ticket, error) { buf := bytes.NewBuffer(in) var timeout tpmutil.U16Bytes if err := tpmutil.UnpackBuf(buf, &timeout); err != nil { return nil, nil, fmt.Errorf("decoding timeout: %v", err) } var t Ticket if err := tpmutil.UnpackBuf(buf, &t); err != nil { return nil, nil, fmt.Errorf("decoding ticket: %v", err) } return timeout, &t, nil } // PolicySigned sets a signed authorization requirement on the provided policy. func PolicySigned(rw io.ReadWriter, validationKeyHandle tpmutil.Handle, policyHandle tpmutil.Handle, policyNonce, cpHash, policyRef []byte, expiry int32, signedAuth []byte) ([]byte, *Ticket, error) { Cmd, err := encodePolicySigned(validationKeyHandle, policyHandle, policyNonce, cpHash, policyRef, expiry, signedAuth) if err != nil { return nil, nil, err } resp, err := runCommand(rw, TagNoSessions, CmdPolicySigned, tpmutil.RawBytes(Cmd)) if err != nil { return nil, nil, err } return decodePolicySigned(resp) } 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, signerAuth 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(signerAuth)}) 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, signerAuth, unused string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, *Signature, error) { // TODO: Remove "unused" parameter on next breaking change. attest, sigRaw, err := QuoteRaw(rw, signingHandle, signerAuth, unused, 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, signerAuth, _ string, toQuote []byte, sel PCRSelection, sigAlg Algorithm) ([]byte, []byte, error) { // TODO: Remove "unused" parameter on next breaking change. Cmd, err := encodeQuote(signingHandle, signerAuth, 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 tpmutil.U16Bytes 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); err != nil { return nil, nil, err } return attest, buf.Bytes(), 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 } func encodePCRReset(pcr tpmutil.Handle) ([]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 } return concat(ha, auth) } // PCRReset resets the value of the given PCR. Usually, only PCR 16 (Debug) and // PCR 23 (Application) are resettable on the default locality. func PCRReset(rw io.ReadWriter, pcr tpmutil.Handle) error { Cmd, err := encodePCRReset(pcr) if err != nil { return err } _, err = runCommand(rw, TagSessions, CmdPCRReset, tpmutil.RawBytes(Cmd)) return err } // 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(sessionHandle, 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: sessionHandle, 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) { return RSADecryptWithSession(rw, HandlePasswordSession, key, password, message, scheme, label) } // RSADecryptWithSession 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 RSADecryptWithSession(rw io.ReadWriter, sessionHandle, key tpmutil.Handle, password string, message []byte, scheme *AsymScheme, label string) ([]byte, error) { Cmd, err := encodeRSADecrypt(sessionHandle, 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 } go-tpm-0.9.3/tpm/000077500000000000000000000000001473040456300135345ustar00rootroot00000000000000go-tpm-0.9.3/tpm/commands.go000066400000000000000000000400531473040456300156660ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "encoding/binary" "errors" "io" "github.com/google/go-tpm/tpmutil" ) // submitTPMRequest sends a structure to the TPM device file and gets results // back, interpreting them as a new provided structure. func submitTPMRequest(rw io.ReadWriter, tag uint16, ord uint32, in []interface{}, out []interface{}) (uint32, error) { resp, code, err := tpmutil.RunCommand(rw, tpmutil.Tag(tag), tpmutil.Command(ord), in...) if err != nil { return 0, err } if code != tpmutil.RCSuccess { return uint32(code), tpmError(code) } _, err = tpmutil.Unpack(resp, out...) return 0, err } // oiap sends an OIAP command to the TPM and gets back an auth value and a // nonce. func oiap(rw io.ReadWriter) (*oiapResponse, error) { var resp oiapResponse out := []interface{}{&resp} // In this case, we don't need to check ret, since all the information is // contained in err. if _, err := submitTPMRequest(rw, tagRQUCommand, ordOIAP, nil, out); err != nil { return nil, err } return &resp, nil } // osap sends an OSAPCommand to the TPM and gets back authentication // information in an OSAPResponse. func osap(rw io.ReadWriter, osap *osapCommand) (*osapResponse, error) { in := []interface{}{osap} var resp osapResponse out := []interface{}{&resp} // In this case, we don't need to check the ret value, since all the // information is contained in err. if _, err := submitTPMRequest(rw, tagRQUCommand, ordOSAP, in, out); err != nil { return nil, err } return &resp, nil } // seal performs a seal operation on the TPM. func seal(rw io.ReadWriter, sc *sealCommand, pcrs *pcrInfoLong, data tpmutil.U32Bytes, ca *commandAuth) (*tpmStoredData, *responseAuth, uint32, error) { pcrsize := binary.Size(pcrs) if pcrsize < 0 { return nil, nil, 0, errors.New("couldn't compute the size of a pcrInfoLong") } // TODO(tmroeder): special-case pcrInfoLong in pack/unpack so we don't have // to write out the length explicitly here. in := []interface{}{sc, uint32(pcrsize), pcrs, data, ca} var tsd tpmStoredData var ra responseAuth out := []interface{}{&tsd, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordSeal, in, out) if err != nil { return nil, nil, 0, err } return &tsd, &ra, ret, nil } // unseal data sealed by the TPM. func unseal(rw io.ReadWriter, keyHandle tpmutil.Handle, sealed *tpmStoredData, ca1 *commandAuth, ca2 *commandAuth) ([]byte, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{keyHandle, sealed, ca1, ca2} var outb tpmutil.U32Bytes var ra1 responseAuth var ra2 responseAuth out := []interface{}{&outb, &ra1, &ra2} ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordUnseal, in, out) if err != nil { return nil, nil, nil, 0, err } return outb, &ra1, &ra2, ret, nil } // authorizeMigrationKey authorizes a public key for migrations. func authorizeMigrationKey(rw io.ReadWriter, migrationScheme MigrationScheme, migrationKey pubKey, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { in := []interface{}{migrationScheme, migrationKey, ca} var ra responseAuth var migrationAuth migrationKeyAuth out := []interface{}{&migrationAuth, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordAuthorizeMigrationKey, in, out) if err != nil { return nil, nil, 0, err } authBlob, err := tpmutil.Pack(migrationAuth) if err != nil { return nil, nil, 0, err } return authBlob, &ra, ret, nil } // createMigrationBlob migrates a key from the TPM. func createMigrationBlob(rw io.ReadWriter, parentHandle tpmutil.Handle, migrationScheme MigrationScheme, migrationKey []byte, encData tpmutil.U32Bytes, ca1 *commandAuth, ca2 *commandAuth) ([]byte, []byte, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{parentHandle, migrationScheme, migrationKey, encData, ca1, ca2} var rand tpmutil.U32Bytes var outData tpmutil.U32Bytes var ra1 responseAuth var ra2 responseAuth out := []interface{}{&rand, &outData, &ra1, &ra2} ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordCreateMigrationBlob, in, out) if err != nil { return nil, nil, nil, nil, 0, err } return rand, outData, &ra1, &ra2, ret, nil } // flushSpecific removes a handle from the TPM. Note that removing a handle // doesn't require any authentication. func flushSpecific(rw io.ReadWriter, handle tpmutil.Handle, resourceType uint32) error { // In this case, all the information is in err, so we don't check the // specific return-value details. _, err := submitTPMRequest(rw, tagRQUCommand, ordFlushSpecific, []interface{}{handle, resourceType}, nil) return err } // loadKey2 loads a key into the TPM. It's a tagRQUAuth1Command, so it only // needs one auth parameter. // TODO(tmroeder): support key12, too. func loadKey2(rw io.ReadWriter, k *key, ca *commandAuth) (tpmutil.Handle, *responseAuth, uint32, error) { // We always load our keys with the SRK as the parent key. in := []interface{}{khSRK, k, ca} var keyHandle tpmutil.Handle var ra responseAuth out := []interface{}{&keyHandle, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordLoadKey2, in, out) if err != nil { return 0, nil, 0, err } return keyHandle, &ra, ret, nil } // getPubKey gets a public key from the TPM func getPubKey(rw io.ReadWriter, keyHandle tpmutil.Handle, ca *commandAuth) (*pubKey, *responseAuth, uint32, error) { in := []interface{}{keyHandle, ca} var pk pubKey var ra responseAuth out := []interface{}{&pk, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordGetPubKey, in, out) if err != nil { return nil, nil, 0, err } return &pk, &ra, ret, nil } // getCapability reads the requested capability and sub-capability from the TPM func getCapability(rw io.ReadWriter, cap, subcap uint32) ([]byte, error) { subCapBytes, err := tpmutil.Pack(subcap) if err != nil { return nil, err } var b tpmutil.U32Bytes in := []interface{}{cap, tpmutil.U32Bytes(subCapBytes)} out := []interface{}{&b} if _, err := submitTPMRequest(rw, tagRQUCommand, ordGetCapability, in, out); err != nil { return nil, err } return b, nil } // nvDefineSpace allocates space in NVRAM func nvDefineSpace(rw io.ReadWriter, nvData NVDataPublic, enc Digest, ca *commandAuth) (*responseAuth, uint32, error) { var ra responseAuth in := []interface{}{nvData, enc} if ca != nil { in = append(in, ca) } out := []interface{}{&ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVDefineSpace, in, out) if err != nil { return nil, 0, err } return &ra, ret, nil } // nvReadValue reads from the NVRAM // If TPM isn't locked, and for some nv permission no authentication is needed. // See TPM-Main-Part-3-Commands-20.4 func nvReadValue(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { var b tpmutil.U32Bytes var ra responseAuth var ret uint32 var err error in := []interface{}{index, offset, len} out := []interface{}{&b} // Auth is needed if ca != nil { in = append(in, ca) out = append(out, ra) ret, err = submitTPMRequest(rw, tagRQUAuth1Command, ordNVReadValue, in, out) } else { // Auth is not needed ret, err = submitTPMRequest(rw, tagRQUCommand, ordNVReadValue, in, out) } if err != nil { return nil, nil, 0, err } return b, &ra, ret, nil } // nvReadValueAuth reads nvram with enforced authentication. // No Owner needs to be present. // See TPM-Main-Part-3-Commands-20.5 func nvReadValueAuth(rw io.ReadWriter, index, offset, len uint32, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { var b tpmutil.U32Bytes var ra responseAuth in := []interface{}{index, offset, len, ca} out := []interface{}{&b, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVReadValueAuth, in, out) if err != nil { return nil, nil, 0, err } return b, &ra, ret, nil } // nvWriteValue writes to the NVRAM // If TPM isn't locked, no authentication is needed. // See TPM-Main-Part-3-Commands-20.2 func nvWriteValue(rw io.ReadWriter, index, offset, len uint32, data []byte, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { var b tpmutil.U32Bytes var ra responseAuth in := []interface{}{index, offset, len, data} if ca != nil { in = append(in, ca) } out := []interface{}{&b, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVWriteValue, in, out) if err != nil { return nil, nil, 0, err } return b, &ra, ret, nil } // nvWriteValue writes to the NVRAM // If TPM isn't locked, no authentication is needed. // See TPM-Main-Part-3-Commands-20.3 func nvWriteValueAuth(rw io.ReadWriter, index, offset, len uint32, data []byte, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { var b tpmutil.U32Bytes var ra responseAuth in := []interface{}{index, offset, len, data, ca} out := []interface{}{&b, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordNVWriteValueAuth, in, out) if err != nil { return nil, nil, 0, err } return b, &ra, ret, nil } // quote2 signs arbitrary data under a given set of PCRs and using a key // specified by keyHandle. It returns information about the PCRs it signed // under, the signature, auth information, and optionally information about the // TPM itself. Note that the input to quote2 must be exactly 20 bytes, so it is // normally the SHA1 hash of the data. func quote2(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcrSelection, addVersion byte, ca *commandAuth) (*pcrInfoShort, *CapVersionInfo, []byte, []byte, *responseAuth, uint32, error) { in := []interface{}{keyHandle, hash, pcrs, addVersion, ca} var pcrShort pcrInfoShort var capInfo CapVersionInfo var capBytes tpmutil.U32Bytes var sig tpmutil.U32Bytes var ra responseAuth out := []interface{}{&pcrShort, &capBytes, &sig, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordQuote2, in, out) if err != nil { return nil, nil, nil, nil, nil, 0, err } // Deserialize the capInfo, if any. if len([]byte(capBytes)) == 0 { return &pcrShort, nil, capBytes, sig, &ra, ret, nil } err = capInfo.Decode(capBytes) if err != nil { return nil, nil, nil, nil, nil, 0, err } return &pcrShort, &capInfo, capBytes, sig, &ra, ret, nil } // quote performs a TPM 1.1 quote operation: it signs data using the // TPM_QUOTE_INFO structure for the current values of a selected set of PCRs. func quote(rw io.ReadWriter, keyHandle tpmutil.Handle, hash [20]byte, pcrs *pcrSelection, ca *commandAuth) (*pcrComposite, []byte, *responseAuth, uint32, error) { in := []interface{}{keyHandle, hash, pcrs, ca} var pcrc pcrComposite var sig tpmutil.U32Bytes var ra responseAuth out := []interface{}{&pcrc, &sig, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordQuote, in, out) if err != nil { return nil, nil, nil, 0, err } return &pcrc, sig, &ra, ret, nil } // makeIdentity requests that the TPM create a new AIK. It returns the handle to // this new key. func makeIdentity(rw io.ReadWriter, encAuth Digest, idDigest Digest, k *key, ca1 *commandAuth, ca2 *commandAuth) (*key, []byte, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{encAuth, idDigest, k, ca1, ca2} var aik key var sig tpmutil.U32Bytes var ra1 responseAuth var ra2 responseAuth out := []interface{}{&aik, &sig, &ra1, &ra2} ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordMakeIdentity, in, out) if err != nil { return nil, nil, nil, nil, 0, err } return &aik, sig, &ra1, &ra2, ret, nil } // activateIdentity provides the TPM with an EK encrypted challenge and asks it to // decrypt the challenge and return the secret (symmetric key). func activateIdentity(rw io.ReadWriter, keyHandle tpmutil.Handle, blob tpmutil.U32Bytes, ca1 *commandAuth, ca2 *commandAuth) (*symKey, *responseAuth, *responseAuth, uint32, error) { in := []interface{}{keyHandle, blob, ca1, ca2} var symkey symKey var ra1 responseAuth var ra2 responseAuth out := []interface{}{&symkey, &ra1, &ra2} ret, err := submitTPMRequest(rw, tagRQUAuth2Command, ordActivateIdentity, in, out) if err != nil { return nil, nil, nil, 0, err } return &symkey, &ra1, &ra2, ret, nil } // resetLockValue resets the dictionary-attack lock in the TPM, using owner // auth. func resetLockValue(rw io.ReadWriter, ca *commandAuth) (*responseAuth, uint32, error) { in := []interface{}{ca} var ra responseAuth out := []interface{}{&ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordResetLockValue, in, out) if err != nil { return nil, 0, err } return &ra, ret, nil } // ownerReadInternalPub uses owner auth and OSAP to read either the endorsement // key (using khEK) or the SRK (using khSRK). func ownerReadInternalPub(rw io.ReadWriter, kh tpmutil.Handle, ca *commandAuth) (*pubKey, *responseAuth, uint32, error) { in := []interface{}{kh, ca} var pk pubKey var ra responseAuth out := []interface{}{&pk, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordOwnerReadInternalPub, in, out) if err != nil { return nil, nil, 0, err } return &pk, &ra, ret, nil } // readPubEK requests the public part of the endorsement key from the TPM. Note // that this call can only be made when there is no owner in the TPM. Once an // owner is established, the endorsement key can be retrieved using // ownerReadInternalPub. func readPubEK(rw io.ReadWriter, n Nonce) (*pubKey, Digest, uint32, error) { in := []interface{}{n} var pk pubKey var d Digest out := []interface{}{&pk, &d} ret, err := submitTPMRequest(rw, tagRQUCommand, ordReadPubEK, in, out) if err != nil { return nil, d, 0, err } return &pk, d, ret, nil } // ownerClear uses owner auth to clear the TPM. After this operation, a caller // can take ownership of the TPM with TPM_TakeOwnership. func ownerClear(rw io.ReadWriter, ca *commandAuth) (*responseAuth, uint32, error) { in := []interface{}{ca} var ra responseAuth out := []interface{}{&ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordOwnerClear, in, out) if err != nil { return nil, 0, err } return &ra, ret, nil } // takeOwnership takes ownership of the TPM and establishes a new SRK and // owner auth. This operation can only be performed if there is no owner. The // TPM can be put into this state using TPM_OwnerClear. The encOwnerAuth and // encSRKAuth values must be encrypted using the endorsement key. func takeOwnership(rw io.ReadWriter, encOwnerAuth tpmutil.U32Bytes, encSRKAuth tpmutil.U32Bytes, srk *key, ca *commandAuth) (*key, *responseAuth, uint32, error) { in := []interface{}{pidOwner, encOwnerAuth, encSRKAuth, srk, ca} var k key var ra responseAuth out := []interface{}{&k, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordTakeOwnership, in, out) if err != nil { return nil, nil, 0, err } return &k, &ra, ret, nil } // Creates a wrapped key under the SRK. func createWrapKey(rw io.ReadWriter, encUsageAuth Digest, encMigrationAuth Digest, keyInfo *key, ca *commandAuth) (*key, *responseAuth, uint32, error) { in := []interface{}{khSRK, encUsageAuth, encMigrationAuth, keyInfo, ca} var k key var ra responseAuth out := []interface{}{&k, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordCreateWrapKey, in, out) if err != nil { return nil, nil, 0, err } return &k, &ra, ret, nil } func sign(rw io.ReadWriter, keyHandle tpmutil.Handle, data tpmutil.U32Bytes, ca *commandAuth) ([]byte, *responseAuth, uint32, error) { in := []interface{}{keyHandle, data, ca} var signature tpmutil.U32Bytes var ra responseAuth out := []interface{}{&signature, &ra} ret, err := submitTPMRequest(rw, tagRQUAuth1Command, ordSign, in, out) if err != nil { return nil, nil, 0, err } return signature, &ra, ret, nil } func pcrReset(rw io.ReadWriter, pcrs *pcrSelection) error { _, err := submitTPMRequest(rw, tagRQUCommand, ordPcrReset, []interface{}{pcrs}, nil) if err != nil { return err } return nil } go-tpm-0.9.3/tpm/constants.go000066400000000000000000000206761473040456300161120ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "fmt" "strings" "github.com/google/go-tpm/tpmutil" ) // Supported TPM commands. const ( tagPCRInfoLong uint16 = 0x06 tagNVAttributes uint16 = 0x0017 tagNVDataPublic uint16 = 0x0018 tagRQUCommand uint16 = 0x00C1 tagRQUAuth1Command uint16 = 0x00C2 tagRQUAuth2Command uint16 = 0x00C3 tagRSPCommand uint16 = 0x00C4 tagRSPAuth1Command uint16 = 0x00C5 tagRSPAuth2Command uint16 = 0x00C6 ) // Supported TPM operations. const ( ordOIAP uint32 = 0x0000000A ordOSAP uint32 = 0x0000000B ordTakeOwnership uint32 = 0x0000000D ordExtend uint32 = 0x00000014 ordPCRRead uint32 = 0x00000015 ordQuote uint32 = 0x00000016 ordSeal uint32 = 0x00000017 ordUnseal uint32 = 0x00000018 ordCreateWrapKey uint32 = 0x0000001F ordGetPubKey uint32 = 0x00000021 ordCreateMigrationBlob uint32 = 0x00000028 ordAuthorizeMigrationKey uint32 = 0x0000002b ordSign uint32 = 0x0000003C ordQuote2 uint32 = 0x0000003E ordResetLockValue uint32 = 0x00000040 ordLoadKey2 uint32 = 0x00000041 ordGetRandom uint32 = 0x00000046 ordOwnerClear uint32 = 0x0000005B ordForceClear uint32 = 0x0000005D ordGetCapability uint32 = 0x00000065 ordCreateEndorsementKeyPair uint32 = 0x00000078 ordMakeIdentity uint32 = 0x00000079 ordActivateIdentity uint32 = 0x0000007A ordReadPubEK uint32 = 0x0000007C ordOwnerReadInternalPub uint32 = 0x00000081 ordStartup uint32 = 0x00000099 ordFlushSpecific uint32 = 0x000000BA ordNVDefineSpace uint32 = 0x000000CC ordPcrReset uint32 = 0x000000C8 ordNVWriteValue uint32 = 0x000000CD ordNVWriteValueAuth uint32 = 0x000000CE ordNVReadValue uint32 = 0x000000CF ordNVReadValueAuth uint32 = 0x000000D0 ) // Capability types. const ( CapAlg uint32 = 0x00000002 CapProperty uint32 = 0x00000005 CapFlag uint32 = 0x00000004 CapNVList uint32 = 0x0000000D CapNVIndex uint32 = 0x00000011 CapHandle uint32 = 0x00000014 CapVersion uint32 = 0x0000001A ) // SubCapabilities const ( SubCapPropManufacturer uint32 = 0x00000103 SubCapFlagPermanent uint32 = 0x00000108 ) // Permission type type Permission uint32 // NV Permissions and Operations // Note: Permissions are summable const ( NVPerPPWrite Permission = 0x00000001 NVPerOwnerWrite Permission = 0x00000002 NVPerAuthWrite Permission = 0x00000004 NVPerWriteAll Permission = 0x00000800 // Warning: The Value 0x00001000 is // defined in the spec as // TPM_NV_PER_WRITEDEFINE, but it is // not included directly in this // code because it locks the given // NV Index permanently if used // incorrectly. This operation can't // be undone in any way. Do not use // this value unless you know what // you're doing! NVPerWriteSTClear Permission = 0x00002000 NVPerGlobalLock Permission = 0x00004000 NVPerPPRead Permission = 0x00008000 NVPerOwnerRead Permission = 0x00100000 NVPerAuthRead Permission = 0x00200000 NVPerReadSTClear Permission = 0x80000000 ) // permMap : Map of TPM_NV_Permissions to its strings for convenience var permMap = map[Permission]string{ NVPerPPWrite: "PPWrite", NVPerOwnerWrite: "OwnerWrite", NVPerAuthWrite: "AuthWrite", NVPerWriteAll: "WriteAll", NVPerWriteSTClear: " WriteSTClear", NVPerGlobalLock: "GlobalLock", NVPerPPRead: "PPRead", NVPerOwnerRead: "OwnerRead", NVPerAuthRead: "AuthRead", NVPerReadSTClear: "ReadSTClear", } // String returns a textual representation of the set of permissions func (p Permission) 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(), " + ") } // Entity types. The LSB gives the entity type, and the MSB (currently fixed to // 0x00) gives the ADIP type. ADIP type 0x00 is XOR. const ( _ uint16 = iota etKeyHandle etOwner etData etSRK etKey etRevoke ) // Resource types. const ( _ uint32 = iota rtKey rtAuth rtHash rtTrans ) // Locality type type Locality byte // Values of locality // Note: Localities are summable const ( LocZero Locality = 1 << iota LocOne LocTwo LocThree LocFour ) // LocaMap maps Locality values to strings for convenience var locaMap = map[Locality]string{ LocZero: "Locality 0", LocOne: "Locality 1", LocTwo: "Locality 2", LocThree: "Locality 3", LocFour: "Locality 4", } // // String returns a textual representation of the set of Localities func (l Locality) String() string { var retString strings.Builder for iterator, item := range locaMap { if l&iterator != 0 { retString.WriteString(item + " + ") } } if retString.String() == "" { return fmt.Sprintf("locality %d", int(l)) } return strings.TrimSuffix(retString.String(), " + ") } // Entity values. const ( khSRK tpmutil.Handle = 0x40000000 khOwner tpmutil.Handle = 0x40000001 khRevokeTrust tpmutil.Handle = 0x40000002 khEK tpmutil.Handle = 0x40000006 ) // Protocol IDs. const ( _ uint16 = iota pidOIAP pidOSAP pidADIP pidADCP pidOwner pidDSAP pidTransport ) // Algorithm type for more convenient handling. // see Algorithm ID for possible values. type Algorithm uint32 // Algorithm ID values. const ( _ Algorithm = iota AlgRSA _ // was DES _ // was 3DES in EDE mode AlgSHA AlgHMAC AlgAES128 AlgMGF1 AlgAES192 AlgAES256 AlgXOR ) // AlgMap Map of Algorithms to Strings for nicer output and comparisons, etc. var AlgMap = map[Algorithm]string{ AlgRSA: "RSA", AlgSHA: "SHA1", AlgHMAC: "HMAC", AlgAES128: "AER128", AlgMGF1: "MFG1", AlgAES192: "AES192", AlgAES256: "AES256", } func (a Algorithm) String() string { n, ok := AlgMap[a] if !ok { return "unknown_algorithm" } return n } // Encryption schemes. The values esNone and the two that contain the string // "RSA" are only valid under AlgRSA. The other two are symmetric encryption // schemes. const ( _ uint16 = iota esNone esRSAEsPKCSv15 esRSAEsOAEPSHA1MGF1 esSymCTR esSymOFB esSymCBCPKCS5 = 0xff // esSymCBCPKCS5 was taken from go-tspi ) // Signature schemes. These are only valid under AlgRSA. const ( _ uint16 = iota ssNone ssRSASaPKCS1v15SHA1 ssRSASaPKCS1v15DER ssRSASaPKCS1v15INFO ) // KeyUsage types for TPM_KEY (the key type). const ( keySigning uint16 = 0x0010 keyStorage uint16 = 0x0011 keyIdentity uint16 = 0x0012 keyAuthChange uint16 = 0x0013 keyBind uint16 = 0x0014 keyLegacy uint16 = 0x0015 keyMigrate uint16 = 0x0016 ) const ( authNever byte = 0x00 authAlways byte = 0x01 authPrivUseOnly byte = 0x03 ) // KeyFlags represents TPM_KEY_FLAGS. type KeyFlags uint32 const ( keyRedirection KeyFlags = 0x00000001 keyMigratable KeyFlags = 0x00000002 keyIsVolatile KeyFlags = 0x00000004 keyPcrIgnoredOnRead KeyFlags = 0x00000008 keyMigrateAuthority KeyFlags = 0x00000010 ) // MigrationScheme represents TPM_MIGRATE_SCHEME. type MigrationScheme uint16 const ( msMigrate MigrationScheme = 0x0001 msRewrap MigrationScheme = 0x0002 msMaint MigrationScheme = 0x0003 msRestrictMigrate MigrationScheme = 0x0004 msRestrictApprove MigrationScheme = 0x0005 ) // fixedQuote is the fixed constant string used in quoteInfo. var fixedQuote = [4]byte{byte('Q'), byte('U'), byte('O'), byte('T')} // quoteVersion is the fixed version string for quoteInfo. const quoteVersion uint32 = 0x01010000 // oaepLabel is the label used for OEAP encryption in esRSAEsOAEPSHA1MGF1 var oaepLabel = []byte{byte('T'), byte('C'), byte('P'), byte('A')} go-tpm-0.9.3/tpm/errors.go000066400000000000000000000247051473040456300154070ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "strconv" ) // A tpmError is an error value from the TPM. type tpmError uint32 // Error produces a string for the given TPM Error code func (o tpmError) Error() string { if s, ok := tpmErrMsgs[o]; ok { return "tpm: " + s } return "tpm: unknown error code " + strconv.Itoa(int(o)) } // These are the TPM error codes from the spec. const ( _ = iota errAuthFail tpmError = iota errBadIndex errBadParameter errAuditFailure errClearDisabled errDeactivated errDisabled errDisabledCmd errFail errBadOrdinal errInstallDisabled errInvalidKeyHandle errKeyNotFound errInappropriateEnc errMigrateFail errInvalidPCRInfo errNoSpace errNoSRK errNotSealedBlob errOwnerSet errResources errShortRandom errSize errWrongPCRVal errBadParamSize errSHAThread errSHAError errFailedSelfTest errAuth2Fail errBadTag errIOError errEncryptError errDecryptError errInvalidAuthHandle errNoEndorsement errInvalidKeyUsage errWrongEntityType errInvalidPostInit errInappropriateSig errBadKeyProperty errBadMigration errBadScheme errBadDatasize errBadMode errBadPresence errBadVersion errNoWrapTransport errAuditFailUnsuccessful errAuditFailSuccessful errNotResettable errNotLocal errBadType errInvalidResource errNotFIPS errInvalidFamily errNoNVPermission errRequiresSign errKeyNotSupported errAuthConflict errAreaLocked errBadLocality errReadOnly errPerNoWrite errFamilyCount errWriteLocked errBadAttributes errInvalidStructure errKeyOwnerControl errBadCounter errNotFullWrite errContextGap errMaxNVWrites errNoOperator errResourceMissing errDelegateLock errDelegateFamily errDelegateAdmin errTransportNotExclusive errOwnerControl errDAAResources errDAAInputData0 errDAAInputData1 errDAAIssuerSettings errDAASettings errDAAState errDAAIssuerValidity errDAAWrongW errBadHandle errBadDelegate errBadContext errTooManyContexts errMATicketSignature errMADestination errMASource errMAAuthority ) // Extra messages the TPM might return. const errDefendLockRunning tpmError = 2051 // tpmErrMsgs maps tpmError codes to their associated error strings. var tpmErrMsgs = map[tpmError]string{ errAuthFail: "authentication failed", errBadIndex: "the index to a PCR, DIR or other register is incorrect", errBadParameter: "one or more parameter is bad", errAuditFailure: "an operation completed successfully but the auditing of that operation failed", errClearDisabled: "the clear disable flag is set and all clear operations now require physical access", errDeactivated: "the TPM is deactivated", errDisabled: "the TPM is disabled", errDisabledCmd: "the target command has been disabled", errFail: "the operation failed", errBadOrdinal: "the ordinal was unknown or inconsistent", errInstallDisabled: "the ability to install an owner is disabled", errInvalidKeyHandle: "the key handle can not be interpreted", errKeyNotFound: "the key handle points to an invalid key", errInappropriateEnc: "unacceptable encryption scheme", errMigrateFail: "migration authorization failed", errInvalidPCRInfo: "PCR information could not be interpreted", errNoSpace: "no room to load key", errNoSRK: "there is no SRK set", errNotSealedBlob: "an encrypted blob is invalid or was not created by this TPM", errOwnerSet: "there is already an Owner", errResources: "the TPM has insufficient internal resources to perform the requested action", errShortRandom: "a random string was too short", errSize: "the TPM does not have the space to perform the operation", errWrongPCRVal: "the named PCR value does not match the current PCR value", errBadParamSize: "the paramSize argument to the command has the incorrect value", errSHAThread: "there is no existing SHA-1 thread", errSHAError: "the calculation is unable to proceed because the existing SHA-1 thread has already encountered an error", errFailedSelfTest: "self-test has failed and the TPM has shutdown", errAuth2Fail: "the authorization for the second key in a 2 key function failed authorization", errBadTag: "the tag value sent to for a command is invalid", errIOError: "an IO error occurred transmitting information to the TPM", errEncryptError: "the encryption process had a problem", errDecryptError: "the decryption process had a problem", errInvalidAuthHandle: "an invalid handle was used", errNoEndorsement: "the TPM does not have an EK installed", errInvalidKeyUsage: "the usage of a key is not allowed", errWrongEntityType: "the submitted entity type is not allowed", errInvalidPostInit: "the command was received in the wrong sequence relative to Init and a subsequent Startup", errInappropriateSig: "signed data cannot include additional DER information", errBadKeyProperty: "the key properties in KEY_PARAMs are not supported by this TPM", errBadMigration: "the migration properties of this key are incorrect", errBadScheme: "the signature or encryption scheme for this key is incorrect or not permitted in this situation", errBadDatasize: "the size of the data (or blob) parameter is bad or inconsistent with the referenced key", errBadMode: "a mode parameter is bad, such as capArea or subCapArea for GetCapability, physicalPresence parameter for PhysicalPresence, or migrationType for CreateMigrationBlob", errBadPresence: "either the physicalPresence or physicalPresenceLock bits have the wrong value", errBadVersion: "the TPM cannot perform this version of the capability", errNoWrapTransport: "the TPM does not allow for wrapped transport sessions", errAuditFailUnsuccessful: "TPM audit construction failed and the underlying command was returning a failure code also", errAuditFailSuccessful: "TPM audit construction failed and the underlying command was returning success", errNotResettable: "attempt to reset a PCR register that does not have the resettable attribute", errNotLocal: "attempt to reset a PCR register that requires locality and locality modifier not part of command transport", errBadType: "make identity blob not properly typed", errInvalidResource: "when saving context identified resource type does not match actual resource", errNotFIPS: "the TPM is attempting to execute a command only available when in FIPS mode", errInvalidFamily: "the command is attempting to use an invalid family ID", errNoNVPermission: "the permission to manipulate the NV storage is not available", errRequiresSign: "the operation requires a signed command", errKeyNotSupported: "wrong operation to load an NV key", errAuthConflict: "NV_LoadKey blob requires both owner and blob authorization", errAreaLocked: "the NV area is locked and not writeable", errBadLocality: "the locality is incorrect for the attempted operation", errReadOnly: "the NV area is read only and can't be written to", errPerNoWrite: "there is no protection on the write to the NV area", errFamilyCount: "the family count value does not match", errWriteLocked: "the NV area has already been written to", errBadAttributes: "the NV area attributes conflict", errInvalidStructure: "the structure tag and version are invalid or inconsistent", errKeyOwnerControl: "the key is under control of the TPM Owner and can only be evicted by the TPM Owner", errBadCounter: "the counter handle is incorrect", errNotFullWrite: "the write is not a complete write of the area", errContextGap: "the gap between saved context counts is too large", errMaxNVWrites: "the maximum number of NV writes without an owner has been exceeded", errNoOperator: "no operator AuthData value is set", errResourceMissing: "the resource pointed to by context is not loaded", errDelegateLock: "the delegate administration is locked", errDelegateFamily: "attempt to manage a family other than the delegated family", errDelegateAdmin: "delegation table management not enabled", errTransportNotExclusive: "there was a command executed outside of an exclusive transport session", errOwnerControl: "attempt to context save a owner evict controlled key", errDAAResources: "the DAA command has no resources available to execute the command", errDAAInputData0: "the consistency check on DAA parameter inputData0 has failed", errDAAInputData1: "the consistency check on DAA parameter inputData1 has failed", errDAAIssuerSettings: "the consistency check on DAA_issuerSettings has failed", errDAASettings: "the consistency check on DAA_tpmSpecific has failed", errDAAState: "the atomic process indicated by the submitted DAA command is not the expected process", errDAAIssuerValidity: "the issuer's validity check has detected an inconsistency", errDAAWrongW: "the consistency check on w has failed", errBadHandle: "the handle is incorrect", errBadDelegate: "delegation is not correct", errBadContext: "the context blob is invalid", errTooManyContexts: "too many contexts held by the TPM", errMATicketSignature: "migration authority signature validation failure", errMADestination: "migration destination not authenticated", errMASource: "migration source incorrect", errMAAuthority: "incorrect migration authority", errDefendLockRunning: "the TPM is defending against dictionary attacks and is in some time-out period", } go-tpm-0.9.3/tpm/open_other.go000066400000000000000000000031371473040456300162310ustar00rootroot00000000000000//go: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 tpm 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) { return openAndStartupTPM(path, false) } // openAndStartupTPM opens the TPM and optionally runs TPM_Startup if needed. // This feature is implemented only for testing. func openAndStartupTPM(path string, doStartup bool) (io.ReadWriteCloser, error) { rwc, err := tpmutil.OpenTPM(path) if err != nil { return nil, err } // Make sure this is a TPM 1.2 _, err = GetManufacturer(rwc) if doStartup && err == tpmError(errInvalidPostInit) { if err = startup(rwc); err == nil { _, err = GetManufacturer(rwc) } } if err != nil { rwc.Close() return nil, fmt.Errorf("open %s: device is not a TPM 1.2: %v", path, err) } return rwc, nil } go-tpm-0.9.3/tpm/open_windows.go000066400000000000000000000017751473040456300166100ustar00rootroot00000000000000// 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 tpm 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.TPMVersion12 { return nil, fmt.Errorf("openTPM: device is not a TPM 1.2") } return tpmutil.OpenTPM() } go-tpm-0.9.3/tpm/pcrs.go000066400000000000000000000127651473040456300150450ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "crypto/sha1" "errors" "fmt" "io" "strconv" "github.com/google/go-tpm/tpmutil" ) // setPCR sets a PCR value as selected in a given mask. func (pm *pcrMask) setPCR(i int) error { if i >= 24 || i < 0 { return errors.New("can't set PCR " + strconv.Itoa(i)) } (*pm)[i/8] |= 1 << uint(i%8) return nil } // isPCRSet checks to see if a given PCR is included in this mask. func (pm pcrMask) isPCRSet(i int) (bool, error) { if i >= 24 || i < 0 { return false, errors.New("can't check PCR " + strconv.Itoa(i)) } n := byte(1 << uint(i%8)) return pm[i/8]&n == n, nil } // String returns a string representation of a pcrSelection func (p pcrSelection) String() string { return fmt.Sprintf("pcrSelection{Size: %x, Mask: % x}", p.Size, p.Mask) } // newPCRSelection creates a new pcrSelection for the given set of PCRs. func newPCRSelection(pcrVals []int) (*pcrSelection, error) { pcrs := &pcrSelection{Size: 3} for _, v := range pcrVals { if err := pcrs.Mask.setPCR(v); err != nil { return nil, err } } return pcrs, nil } // createPCRComposite composes a set of PCRs by prepending a pcrSelection and a // length, then computing the SHA1 hash and returning its output. func createPCRComposite(mask pcrMask, pcrs []byte) ([]byte, error) { if len(pcrs)%PCRSize != 0 { return nil, errors.New("pcrs must be a multiple of " + strconv.Itoa(PCRSize)) } pcrc := pcrComposite{ Selection: pcrSelection{3, mask}, Values: pcrs, } b, err := tpmutil.Pack(pcrc) if err != nil { return nil, err } h := sha1.Sum(b) return h[:], nil } // String returns a string representation of a pcrInfoLong. func (pcri pcrInfoLong) String() string { return fmt.Sprintf("pcrInfoLong{Tag: %x, LocAtCreation: %x, LocAtRelease: %x, PCRsAtCreation: %s, PCRsAtRelease: %s, DigestAtCreation: % x, DigestAtRelease: % x}", pcri.Tag, pcri.LocAtCreation, pcri.LocAtRelease, pcri.PCRsAtCreation, pcri.PCRsAtRelease, pcri.DigestAtCreation, pcri.DigestAtRelease) } // String returns a string representation of a pcrInfoShort. func (pcri pcrInfoShort) String() string { return fmt.Sprintf("pcrInfoShort{LocAtRelease: %x, PCRsAtRelease: %s, DigestAtRelease: % x}", pcri.LocAtRelease, pcri.PCRsAtRelease, pcri.DigestAtRelease) } // createPCRInfoLong creates a pcrInfoLong structure from a mask and some PCR // values that match this mask, along with a TPM locality. func createPCRInfoLong(loc Locality, mask pcrMask, pcrVals []byte) (*pcrInfoLong, error) { d, err := createPCRComposite(mask, pcrVals) if err != nil { return nil, err } pcri := &pcrInfoLong{ Tag: tagPCRInfoLong, LocAtCreation: loc, LocAtRelease: loc, PCRsAtCreation: pcrSelection{3, mask}, PCRsAtRelease: pcrSelection{3, mask}, } copy(pcri.DigestAtRelease[:], d) copy(pcri.DigestAtCreation[:], d) return pcri, nil } // createPCRInfoShort creates a pcrInfoShort structure from a mask and some PCR // values that match this mask, alon with a TPM locality func createPCRInfoShort(loc Locality, mask pcrMask, pcrVals []byte) (*pcrInfoShort, error) { d, err := createPCRComposite(mask, pcrVals) if err != nil { return nil, err } pcri := &pcrInfoShort{ PCRsAtRelease: pcrSelection{3, mask}, LocAtRelease: loc, } copy(pcri.DigestAtRelease[:], d) return pcri, nil } // newPCRInfoLong creates and returns a pcrInfoLong structure for the given PCR // values. func newPCRInfoLong(rw io.ReadWriter, loc Locality, pcrNums []int) (*pcrInfoLong, error) { var mask pcrMask for _, pcr := range pcrNums { if err := mask.setPCR(pcr); err != nil { return nil, err } } pcrVals, err := FetchPCRValues(rw, pcrNums) if err != nil { return nil, err } return createPCRInfoLong(loc, mask, pcrVals) } func newPCRInfoShort(rw io.ReadWriter, loc Locality, pcrNums []int) (*pcrInfoShort, error) { var mask pcrMask for _, pcr := range pcrNums { if err := mask.setPCR(pcr); err != nil { return nil, err } } pcrVals, err := FetchPCRValues(rw, pcrNums) if err != nil { return nil, err } return createPCRInfoShort(loc, mask, pcrVals) } func newPCRInfo(rw io.ReadWriter, pcrNums []int) (*pcrInfo, error) { var mask pcrMask for _, pcr := range pcrNums { if err := mask.setPCR(pcr); err != nil { return nil, err } } pcrVals, err := FetchPCRValues(rw, pcrNums) if err != nil { return nil, err } d, err := createPCRComposite(mask, pcrVals) if err != nil { return nil, err } pcri := &pcrInfo{ PcrSelection: pcrSelection{3, mask}, } copy(pcri.DigestAtRelease[:], d) copy(pcri.DigestAtCreation[:], d) return pcri, nil } // newPCRInfoLongWithHashes creates and returns a pcrInfoLong structure for the // given PCRs and hashes. func newPCRInfoLongWithHashes(loc Locality, pcrs map[int][]byte) (*pcrInfoLong, error) { var mask pcrMask var hashes []byte for index, hash := range pcrs { if err := mask.setPCR(index); err != nil { return nil, err } hashes = append(hashes, hash...) } return createPCRInfoLong(loc, mask, hashes) } go-tpm-0.9.3/tpm/pcrs_test.go000066400000000000000000000076011473040456300160750ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "testing" ) func TestPCRMask(t *testing.T) { var mask pcrMask if err := mask.setPCR(-1); err == nil { t.Fatal("Incorrectly allowed non-existent PCR -1 to be set") } if err := mask.setPCR(24); err == nil { t.Fatal("Incorrectly allowed non-existent PCR 24 to be set") } if err := mask.setPCR(0); err != nil { t.Fatal("Couldn't set PCR 0 in the mask:", err) } set, err := mask.isPCRSet(0) if err != nil { t.Fatal("Couldn't check to see if PCR 0 was set:", err) } if !set { t.Fatal("Incorrectly said PCR wasn't set when it should have been") } if err := mask.setPCR(18); err != nil { t.Fatal("Couldn't set PCR 18 in the mask:", err) } set, err = mask.isPCRSet(18) if err != nil { t.Fatal("Couldn't check to see if PCR 18 was set:", err) } if !set { t.Fatal("Incorrectly said PCR wasn't set when it should have been") } if _, err := mask.isPCRSet(-1); err == nil { t.Fatal("Incorrectly permitted a check for PCR -1") } if _, err := mask.isPCRSet(400); err == nil { t.Fatal("Incorrectly permitted a check for PCR 400") } } func TestNewPCRSelection(t *testing.T) { pcrs, err := newPCRSelection([]int{17, 18}) if err != nil { t.Fatal("Couldn't set up a PCR selection with PCRs 17 and 18") } if pcrs.Size != 3 { t.Fatal("Incorrectly size in a PCR selection") } set, err := pcrs.Mask.isPCRSet(17) if err != nil { t.Fatal("Couldn't check a PCR on a mask in a PCR selection") } if !set { t.Fatal("PCR 17 wasn't set in a PCR selection after setting it") } set, err = pcrs.Mask.isPCRSet(20) if err != nil { t.Fatal("Couldn't check an unset PCR on a mask in a PCR selection") } if set { t.Fatal("PCR 20 was incorrectly set in a PCR mask in a PCR selection") } } func TestIncorrectCreatePCRComposite(t *testing.T) { pcrs, err := newPCRSelection([]int{17, 18}) if err != nil { t.Fatal("Couldn't set up a PCR selection with PCRs 17 and 18") } // This byte array is far too long and isn't a multiple of PCRSize, since it // is of prime size. pcrValues := make([]byte, 541) if _, err := createPCRComposite(pcrs.Mask, pcrValues); err == nil { t.Fatal("Incorrectly created a PCR composite with wrong PCR length") } } func TestWrongCreatePCRInfoLong(t *testing.T) { pcrs, err := newPCRSelection([]int{17, 18}) if err != nil { t.Fatal("Couldn't set up a PCR selection with PCRs 17 and 18") } // This byte array is far too long and isn't a multiple of PCRSize, since it // is of prime size. pcrValues := make([]byte, 541) if _, err := createPCRInfoLong(0, pcrs.Mask, pcrValues); err == nil { t.Fatal("Incorrectly created a PCR composite with wrong PCR length") } } func TestWrongNewPCRInfoLong(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() if _, err := newPCRInfoLong(rwc, 0, []int{400}); err == nil { t.Fatal("Incorrectly created a pcrInfoLong for PCR 400") } // This case uses a reasonable PCR value but a nil file. if _, err := newPCRInfoLong(nil, 0, []int{17}); err == nil { t.Fatal("Incorrectly created a pcrInfoLong using a nil file") } } func TestNewPCRInfoLongWithHashes(t *testing.T) { pcrMap := make(map[int][]byte) pcrMap[23] = make([]byte, 20) pcrMap[16] = make([]byte, 20) if _, err := newPCRInfoLongWithHashes(LocZero, pcrMap); err != nil { t.Fatal("Couldn't create pcrInfoLong structure") } } go-tpm-0.9.3/tpm/structures.go000066400000000000000000000266161473040456300163210ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "bytes" "crypto" "crypto/rsa" "encoding/binary" "errors" "fmt" "io" "math/big" "github.com/google/go-tpm/tpmutil" ) // A pcrValue is the fixed-size value of a PCR. type pcrValue [20]byte // PCRSize gives the fixed size (20 bytes) of a PCR. const PCRSize int = 20 // A pcrMask represents a set of PCR choices, one bit per PCR out of the 24 // possible PCR values. type pcrMask [3]byte // A pcrSelection is the first element in the input a PCR composition, which is // A pcrSelection, followed by the combined length of the PCR values, // followed by the PCR values, all hashed under SHA-1. type pcrSelection struct { Size uint16 Mask pcrMask } // pcrInfoLong stores detailed information about PCRs. type pcrInfoLong struct { Tag uint16 LocAtCreation Locality LocAtRelease Locality PCRsAtCreation pcrSelection PCRsAtRelease pcrSelection DigestAtCreation Digest DigestAtRelease Digest } // pcrInfoShort stores detailed information about PCRs. type pcrInfoShort struct { PCRsAtRelease pcrSelection LocAtRelease Locality DigestAtRelease Digest } type pcrInfo struct { PcrSelection pcrSelection DigestAtRelease Digest DigestAtCreation Digest } type capVersionByte byte // A capVersionInfo contains information about the TPM itself. Note that this // is deserialized specially, since it has a variable-length byte array but no // length. It is preceded with a length in the response to the Quote2 command. type capVersion struct { Major capVersionByte Minor capVersionByte RevMajor byte RevMinor byte } // CapVersionInfo implements TPM_CAP_VERSION_INFO from spec. Part 2 - Page 174 type CapVersionInfo struct { Tag tpmutil.Tag Version capVersion SpecLevel uint16 ErrataRev byte TPMVendorID [4]byte VendorSpecific tpmutil.U16Bytes } // Decode reads TPM_CAP_VERSION_INFO into CapVersionInfo. func (c *CapVersionInfo) Decode(data []byte) error { var cV capVersion buf := bytes.NewReader(data) err := binary.Read(buf, binary.LittleEndian, &c.Tag) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &cV.Major) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &cV.Minor) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &cV.RevMajor) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &cV.RevMinor) if err != nil { return err } c.Version = cV err = binary.Read(buf, binary.LittleEndian, &c.SpecLevel) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &c.ErrataRev) if err != nil { return err } err = binary.Read(buf, binary.LittleEndian, &c.TPMVendorID) if err != nil { return err } var venspecificLen uint16 err = binary.Read(buf, binary.LittleEndian, &venspecificLen) if err != nil { return err } venSpecData := make([]byte, venspecificLen) err = binary.Read(buf, binary.LittleEndian, &venSpecData) if err != nil { return err } c.VendorSpecific = venSpecData return nil } // PermanentFlags contains persistent TPM properties type PermanentFlags struct { Tag uint16 Disable bool Ownership bool Deactivated bool ReadPubEK bool DisableOwnerClear bool AllowMaintenance bool PhysicalPresenceLifetimeLock bool PhysicalPresenceHWEnable bool PhysicalPresenceCMDEnable bool CEKPUsed bool TPMPost bool TPMPostLock bool FIPS bool Operator bool EnableRevokeEK bool NVLocked bool ReadSRKPub bool TPMEstablished bool MaintenanceDone bool DisableFullDALogicInfo bool } // nvAttributes implements struct of TPM_NV_ATTRIBUTES // See: TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011, P.140 type nvAttributes struct { Tag uint16 Attributes Permission } // NVDataPublic implements the structure of TPM_NV_DATA_PUBLIC // as described in TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011, P. 142 type NVDataPublic struct { Tag uint16 NVIndex uint32 PCRInfoRead pcrInfoShort PCRInfoWrite pcrInfoShort Permission nvAttributes ReadSTClear bool WriteSTClear bool WriteDefine bool Size uint32 } // CloseKey flushes the key associated with the tpmutil.Handle. func CloseKey(rw io.ReadWriter, h tpmutil.Handle) error { return flushSpecific(rw, h, rtKey) } // A Nonce is a 20-byte value. type Nonce [20]byte // An oiapResponse is a response to an OIAP command. type oiapResponse struct { AuthHandle tpmutil.Handle NonceEven Nonce } // String returns a string representation of an oiapResponse. func (opr oiapResponse) String() string { return fmt.Sprintf("oiapResponse{AuthHandle: %x, NonceEven: % x}", opr.AuthHandle, opr.NonceEven) } // Close flushes the auth handle associated with an OIAP session. func (opr *oiapResponse) Close(rw io.ReadWriter) error { return flushSpecific(rw, opr.AuthHandle, rtAuth) } // An osapCommand is a command sent for OSAP authentication. type osapCommand struct { EntityType uint16 EntityValue tpmutil.Handle OddOSAP Nonce } // String returns a string representation of an osapCommand. func (opc osapCommand) String() string { return fmt.Sprintf("osapCommand{EntityType: %x, EntityValue: %x, OddOSAP: % x}", opc.EntityType, opc.EntityValue, opc.OddOSAP) } // An osapResponse is a TPM reply to an osapCommand. type osapResponse struct { AuthHandle tpmutil.Handle NonceEven Nonce EvenOSAP Nonce } // String returns a string representation of an osapResponse. func (opr osapResponse) String() string { return fmt.Sprintf("osapResponse{AuthHandle: %x, NonceEven: % x, EvenOSAP: % x}", opr.AuthHandle, opr.NonceEven, opr.EvenOSAP) } // Close flushes the AuthHandle associated with an OSAP session. func (opr *osapResponse) Close(rw io.ReadWriter) error { return flushSpecific(rw, opr.AuthHandle, rtAuth) } // A Digest is a 20-byte SHA1 value. type Digest [20]byte // An AuthValue is a 20-byte value used for authentication. type authValue [20]byte const authSize uint32 = 20 // A sealCommand is the command sent to the TPM to seal data. type sealCommand struct { KeyHandle tpmutil.Handle EncAuth authValue } // String returns a string representation of a sealCommand. func (sc sealCommand) String() string { return fmt.Sprintf("sealCommand{KeyHandle: %x, EncAuth: % x}", sc.KeyHandle, sc.EncAuth) } // commandAuth stores the auth information sent with a command. Commands with // tagRQUAuth1Command tags use one of these auth structures, and commands with // tagRQUAuth2Command use two. type commandAuth struct { AuthHandle tpmutil.Handle NonceOdd Nonce ContSession byte Auth authValue } // String returns a string representation of a sealCommandAuth. func (ca commandAuth) String() string { return fmt.Sprintf("commandAuth{AuthHandle: %x, NonceOdd: % x, ContSession: %x, Auth: % x}", ca.AuthHandle, ca.NonceOdd, ca.ContSession, ca.Auth) } // responseAuth contains the auth information returned from a command. type responseAuth struct { NonceEven Nonce ContSession byte Auth authValue } // String returns a string representation of a responseAuth. func (ra responseAuth) String() string { return fmt.Sprintf("responseAuth{NonceEven: % x, ContSession: %x, Auth: % x}", ra.NonceEven, ra.ContSession, ra.Auth) } // These are the parameters of a TPM key. type keyParams struct { AlgID Algorithm EncScheme uint16 SigScheme uint16 Params tpmutil.U32Bytes // Serialized rsaKeyParams or symmetricKeyParams. } // An rsaKeyParams encodes the length of the RSA prime in bits, the number of // primes in its factored form, and the exponent used for public-key // encryption. type rsaKeyParams struct { KeyLength uint32 NumPrimes uint32 Exponent tpmutil.U32Bytes } type symmetricKeyParams struct { KeyLength uint32 BlockSize uint32 IV tpmutil.U32Bytes } // A key is a TPM representation of a key. type key struct { Version uint32 KeyUsage uint16 KeyFlags KeyFlags AuthDataUsage byte AlgorithmParams keyParams PCRInfo tpmutil.U32Bytes PubKey tpmutil.U32Bytes EncData tpmutil.U32Bytes } // A key12 is a newer TPM representation of a key. type key12 struct { Tag uint16 Zero uint16 // Always all 0. KeyUsage uint16 KeyFlags uint32 AuthDataUsage byte AlgorithmParams keyParams PCRInfo tpmutil.U32Bytes // This must be a serialization of a pcrInfoLong. PubKey tpmutil.U32Bytes EncData tpmutil.U32Bytes } // A pubKey represents a public key known to the TPM. type pubKey struct { AlgorithmParams keyParams Key tpmutil.U32Bytes } // A migrationKeyAuth represents the target of a migration. type migrationKeyAuth struct { MigrationKey pubKey MigrationScheme MigrationScheme Digest Digest } // A symKey is a TPM representation of a symmetric key. type symKey struct { AlgID Algorithm EncScheme uint16 Key tpmutil.U16Bytes // TPM_SYMMETRIC_KEY uses a 16-bit header for Key data } // A tpmStoredData holds sealed data from the TPM. type tpmStoredData struct { Version uint32 Info tpmutil.U32Bytes Enc tpmutil.U32Bytes } // String returns a string representation of a tpmStoredData. func (tsd tpmStoredData) String() string { return fmt.Sprintf("tpmStoreddata{Version: %x, Info: % x, Enc: % x\n", tsd.Version, tsd.Info, tsd.Enc) } // A quoteInfo structure is the structure signed by the TPM. type quoteInfo struct { // The Version must be 0x01010000 Version uint32 // Fixed is always 'QUOT'. Fixed [4]byte // The CompositeDigest is computed by ComputePCRComposite. CompositeDigest Digest // The Nonce is either a random Nonce or the SHA1 hash of data to sign. Nonce Nonce } // A pcrComposite stores a selection of PCRs with the selected PCR values. type pcrComposite struct { Selection pcrSelection Values tpmutil.U32Bytes } // convertPubKey converts a public key into TPM form. Currently, this function // only supports 2048-bit RSA keys. func convertPubKey(pk crypto.PublicKey) (*pubKey, error) { pkRSA, ok := pk.(*rsa.PublicKey) if !ok { return nil, errors.New("the provided Privacy CA public key was not an RSA key") } if pkRSA.N.BitLen() != 2048 { return nil, errors.New("The provided Privacy CA RSA public key was not a 2048-bit key") } rsakp := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, Exponent: big.NewInt(int64(pkRSA.E)).Bytes(), } rsakpb, err := tpmutil.Pack(rsakp) if err != nil { return nil, err } kp := keyParams{ AlgID: AlgRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15SHA1, Params: rsakpb, } pubKey := pubKey{ AlgorithmParams: kp, Key: pkRSA.N.Bytes(), } return &pubKey, nil } go-tpm-0.9.3/tpm/testing.md000066400000000000000000000044011473040456300155320ustar00rootroot00000000000000# Testing TPM 1.2 Functionality **TODO(https://github.com/google/go-tpm/issues/91):** Support for testing the TPM 1.2 stack against a simulator is a work in progress. Today, it requires several manual steps. ## Overview As TPM 1.2s are phased out of common developer devices, testing changes to the TPM 1.2 stack is difficult without special hardware. To support development on the TPM 1.2 stack without special hardware, a TPM 1.2 simulator or emulator may be used. This document discusses how to use [IBM's TPM 1.2 simulator](http://ibmswtpm.sourceforge.net) (on a Linux or Mac OS device, Windows is not yet supported) to run the go-tpm TPM 1.2 tests (in the `go-tpm/tpm/` directory). ## Downloading, building, and using the IBM TPM 1.2 Simulator * Download the latest release of the [IBM TPM 1.2 Simulator](https://sourceforge.net/projects/ibmswtpm/), unpack the tarball, and `cd` into it. * Add `-DTPM_UNIX_DOMAIN_SOCKET` to `tpm/makefile-en-ac`. * Build the simulator with `make -f tpm/makefile-en-ac` * Set `TEMP_TPM=/tmp/tpm` or some other suitable temporary location for the TPM state files and Unix domain socket. * Start the simulator with `TPM_PATH=${TEMP_TPM} TPM_PORT=${TEMP_TPM}/tpm.sock` ## Running the TPM 1.2 tests against the IBM TPM 1.2 Simulator * Comment out the line `t.Skip()` in `TestTakeOwnership`. This test normally does not work on physical TPMs, so it is normally disabled. * Use `TestTakeOwnership` to take ownership of the simulated TPM with `TPM_PATH=${TEMP_TPM}/tpm.sock go test -v ./tpm/... -run TestTakeOwnership -count=1` * Run the full test suite with `TPM_PATH=${TEMP_TPM}/tpm.sock go test -v ./tpm/...` ## Future Improvements * Add setup logic to the TPM 1.2 tests to take ownership of an unowned TPM under test. * Wrap a TPM 1.2 simulator somewhere (possibly in https://github.com/google/go-tpm-tools) and integrate it into test setup for the TPM 1.2 tests. * Resolve issues that necessitated the use of `t.Skip()` in current tests. * Either add an informative comment along with a skip when a test fails for an expected reason, or remove the test. * Resolve issues with current tests that fail on the simulator (such as `TestGetAlgs`). * Automate the use of a simulator in a Continuous Integration environment that is accessible to GitHub.go-tpm-0.9.3/tpm/tpm.go000066400000000000000000001473521473040456300146770ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm supports direct communication with a tpm device under Linux. package tpm import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/rsa" "crypto/sha1" "encoding/binary" "errors" "fmt" "io" "github.com/google/go-tpm/tpmutil" ) // GetKeys gets the list of handles for currently-loaded TPM keys. func GetKeys(rw io.ReadWriter) ([]tpmutil.Handle, error) { b, err := getCapability(rw, CapHandle, rtKey) if err != nil { return nil, err } var handles []tpmutil.Handle if _, err := tpmutil.Unpack(b, &handles); err != nil { return nil, err } return handles, err } // PcrExtend extends a value into the right PCR by index. func PcrExtend(rw io.ReadWriter, pcrIndex uint32, pcr pcrValue) ([]byte, error) { in := []interface{}{pcrIndex, pcr} var d pcrValue out := []interface{}{&d} if _, err := submitTPMRequest(rw, tagRQUCommand, ordExtend, in, out); err != nil { return nil, err } return d[:], nil } // ReadPCR reads a PCR value from the TPM. func ReadPCR(rw io.ReadWriter, pcrIndex uint32) ([]byte, error) { in := []interface{}{pcrIndex} var v pcrValue out := []interface{}{&v} // There's no need to check the ret value here, since the err value contains // all the necessary information. if _, err := submitTPMRequest(rw, tagRQUCommand, ordPCRRead, in, out); err != nil { return nil, err } return v[:], nil } // FetchPCRValues gets a given sequence of PCR values. func FetchPCRValues(rw io.ReadWriter, pcrVals []int) ([]byte, error) { var pcrs []byte for _, v := range pcrVals { pcr, err := ReadPCR(rw, uint32(v)) if err != nil { return nil, err } pcrs = append(pcrs, pcr...) } return pcrs, nil } // GetRandom gets random bytes from the TPM. func GetRandom(rw io.ReadWriter, size uint32) ([]byte, error) { var b tpmutil.U32Bytes in := []interface{}{size} out := []interface{}{&b} // There's no need to check the ret value here, since the err value // contains all the necessary information. if _, err := submitTPMRequest(rw, tagRQUCommand, ordGetRandom, in, out); err != nil { return nil, err } return b, nil } // LoadKey2 loads a key blob (a serialized TPM_KEY or TPM_KEY12) into the TPM // and returns a handle for this key. func LoadKey2(rw io.ReadWriter, keyBlob []byte, srkAuth []byte) (tpmutil.Handle, error) { // Deserialize the keyBlob as a key var k key if _, err := tpmutil.Unpack(keyBlob, &k); err != nil { return 0, err } // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. LoadKey2 needs an // OSAP session for the SRK because the private part of a TPM_KEY or // TPM_KEY12 is sealed against the SRK. sharedSecret, osapr, err := newOSAPSession(rw, etSRK, khSRK, srkAuth) if err != nil { return 0, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) authIn := []interface{}{ordLoadKey2, k} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return 0, err } handle, ra, ret, err := loadKey2(rw, &k, ca) if err != nil { return 0, err } // Check the response authentication. raIn := []interface{}{ret, ordLoadKey2} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return 0, err } return handle, nil } // Quote2 performs a quote operation on the TPM for the given data, // under the key associated with the handle and for the pcr values // specified in the call. func Quote2(rw io.ReadWriter, handle tpmutil.Handle, data []byte, pcrVals []int, addVersion byte, aikAuth []byte) ([]byte, error) { // Run OSAP for the handle, reading a random OddOSAP for our initial // command and getting back a secret and a response. sharedSecret, osapr, err := newOSAPSession(rw, etKeyHandle, handle, aikAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) // Hash the data to get the value to pass to quote2. hash := sha1.Sum(data) pcrSel, err := newPCRSelection(pcrVals) if err != nil { return nil, err } authIn := []interface{}{ordQuote2, hash, pcrSel, addVersion} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } // TODO(tmroeder): use the returned CapVersion. pcrShort, _, capBytes, sig, ra, ret, err := quote2(rw, handle, hash, pcrSel, addVersion, ca) if err != nil { return nil, err } // Check response authentication. raIn := []interface{}{ret, ordQuote2, pcrShort, tpmutil.U32Bytes(capBytes), tpmutil.U32Bytes(sig)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } return sig, nil } // GetPubKey retrieves an opaque blob containing a public key corresponding to // a handle from the TPM. func GetPubKey(rw io.ReadWriter, keyHandle tpmutil.Handle, srkAuth []byte) ([]byte, error) { // Run OSAP for the handle, reading a random OddOSAP for our initial // command and getting back a secret and a response. sharedSecret, osapr, err := newOSAPSession(rw, etKeyHandle, keyHandle, srkAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) authIn := []interface{}{ordGetPubKey} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } pk, ra, ret, err := getPubKey(rw, keyHandle, ca) if err != nil { return nil, err } // Check response authentication for TPM_GetPubKey. raIn := []interface{}{ret, ordGetPubKey, pk} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } b, err := tpmutil.Pack(*pk) if err != nil { return nil, err } return b, err } // newOSAPSession starts a new OSAP session and derives a shared key from it. func newOSAPSession(rw io.ReadWriter, entityType uint16, entityValue tpmutil.Handle, srkAuth []byte) ([20]byte, *osapResponse, error) { osapc := &osapCommand{ EntityType: entityType, EntityValue: entityValue, } var sharedSecret [20]byte if _, err := rand.Read(osapc.OddOSAP[:]); err != nil { return sharedSecret, nil, err } osapr, err := osap(rw, osapc) if err != nil { return sharedSecret, nil, err } // A shared secret is computed as // // sharedSecret = HMAC-SHA1(srkAuth, evenosap||oddosap) // // where srkAuth is the hash of the SRK authentication (which hash is all 0s // for the well-known SRK auth value) and even and odd OSAP are the // values from the OSAP protocol. osapData, err := tpmutil.Pack(osapr.EvenOSAP, osapc.OddOSAP) if err != nil { return sharedSecret, nil, err } hm := hmac.New(sha1.New, srkAuth) hm.Write(osapData) // Note that crypto/hash.Sum returns a slice rather than an array, so we // have to copy this into an array to make sure that serialization doesn't // prepend a length in tpmutil.Pack(). sharedSecretBytes := hm.Sum(nil) copy(sharedSecret[:], sharedSecretBytes) return sharedSecret, osapr, nil } // newCommandAuth creates a new commandAuth structure over the given // parameters, using the given secret and the given odd nonce, if provided, // for the HMAC. If no odd nonce is provided, one is randomly generated. func newCommandAuth(authHandle tpmutil.Handle, nonceEven Nonce, nonceOdd *Nonce, key []byte, params []interface{}) (*commandAuth, error) { // Auth = HMAC-SHA1(key, SHA1(params) || NonceEven || NonceOdd || ContSession) digestBytes, err := tpmutil.Pack(params...) if err != nil { return nil, err } digest := sha1.Sum(digestBytes) // Use the passed-in nonce if non-nil, otherwise generate it now. var odd Nonce if nonceOdd != nil { odd = *nonceOdd } else { if _, err := rand.Read(odd[:]); err != nil { return nil, err } } ca := &commandAuth{ AuthHandle: authHandle, NonceOdd: odd, } authBytes, err := tpmutil.Pack(digest, nonceEven, ca.NonceOdd, ca.ContSession) if err != nil { return nil, err } hm2 := hmac.New(sha1.New, key) hm2.Write(authBytes) auth := hm2.Sum(nil) copy(ca.Auth[:], auth[:]) return ca, nil } // verify checks that the response authentication was correct. // It computes the SHA1 of params, and computes the HMAC-SHA1 of this digest // with the authentication parameters of ra along with the given odd nonce. func (ra *responseAuth) verify(nonceOdd Nonce, key []byte, params []interface{}) error { // Auth = HMAC-SHA1(key, SHA1(params) || ra.NonceEven || NonceOdd || ra.ContSession) digestBytes, err := tpmutil.Pack(params...) if err != nil { return err } digest := sha1.Sum(digestBytes) authBytes, err := tpmutil.Pack(digest, ra.NonceEven, nonceOdd, ra.ContSession) if err != nil { return err } hm2 := hmac.New(sha1.New, key) hm2.Write(authBytes) auth := hm2.Sum(nil) if !hmac.Equal(ra.Auth[:], auth) { return errors.New("the computed response HMAC didn't match the provided HMAC") } return nil } // zeroBytes zeroes a byte array. func zeroBytes(b []byte) { for i := range b { b[i] = 0 } } func sealHelper(rw io.ReadWriter, pcrInfo *pcrInfoLong, data []byte, srkAuth []byte) ([]byte, error) { // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etSRK, khSRK, srkAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) // EncAuth for a seal command is computed as // // encAuth = XOR(srkAuth, SHA1(sharedSecret || )) // // In this case, the last even nonce is NonceEven from OSAP. xorData, err := tpmutil.Pack(sharedSecret, osapr.NonceEven) if err != nil { return nil, err } defer zeroBytes(xorData) encAuthData := sha1.Sum(xorData) sc := &sealCommand{KeyHandle: khSRK} for i := range sc.EncAuth { sc.EncAuth[i] = srkAuth[i] ^ encAuthData[i] } // The digest input for seal authentication is // // digest = SHA1(ordSeal || encAuth || binary.Size(pcrInfo) || pcrInfo || // len(data) || data) // authIn := []interface{}{ordSeal, sc.EncAuth, uint32(binary.Size(pcrInfo)), pcrInfo, tpmutil.U32Bytes(data)} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } sealed, ra, ret, err := seal(rw, sc, pcrInfo, data, ca) if err != nil { return nil, err } // Check the response authentication. raIn := []interface{}{ret, ordSeal, sealed} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } sealedBytes, err := tpmutil.Pack(*sealed) if err != nil { return nil, err } return sealedBytes, nil } // Seal encrypts data against a given locality and PCRs and returns the sealed data. func Seal(rw io.ReadWriter, loc Locality, pcrs []int, data []byte, srkAuth []byte) ([]byte, error) { pcrInfo, err := newPCRInfoLong(rw, loc, pcrs) if err != nil { return nil, err } return sealHelper(rw, pcrInfo, data, srkAuth) } // Reseal takes a pre-calculated PCR map and locality in order to seal data // with a srkAuth. This function is necessary for PCR pre-calculation and later // sealing to provide a way of updating software which is part of a measured // boot process. func Reseal(rw io.ReadWriter, loc Locality, pcrs map[int][]byte, data []byte, srkAuth []byte) ([]byte, error) { pcrInfo, err := newPCRInfoLongWithHashes(loc, pcrs) if err != nil { return nil, err } return sealHelper(rw, pcrInfo, data, srkAuth) } // Unseal decrypts data encrypted by the TPM. func Unseal(rw io.ReadWriter, sealed []byte, srkAuth []byte) ([]byte, error) { // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etSRK, khSRK, srkAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) // The unseal command needs an OIAP session in addition to the OSAP session. oiapr, err := oiap(rw) if err != nil { return nil, err } defer oiapr.Close(rw) // Convert the sealed value into a tpmStoredData. var tsd tpmStoredData if _, err := tpmutil.Unpack(sealed, &tsd); err != nil { return nil, errors.New("couldn't convert the sealed data into a tpmStoredData struct") } // The digest for auth1 and auth2 for the unseal command is computed as // digest = SHA1(ordUnseal || tsd) authIn := []interface{}{ordUnseal, tsd} // The first commandAuth uses the shared secret as an HMAC key. ca1, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } // The second commandAuth is based on OIAP instead of OSAP and uses the // SRK auth value as an HMAC key instead of the shared secret. ca2, err := newCommandAuth(oiapr.AuthHandle, oiapr.NonceEven, nil, srkAuth, authIn) if err != nil { return nil, err } unsealed, ra1, ra2, ret, err := unseal(rw, khSRK, &tsd, ca1, ca2) if err != nil { return nil, err } // Check the response authentication. raIn := []interface{}{ret, ordUnseal, tpmutil.U32Bytes(unsealed)} if err := ra1.verify(ca1.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } if err := ra2.verify(ca2.NonceOdd, srkAuth, raIn); err != nil { return nil, err } return unsealed, nil } // Quote produces a TPM quote for the given data under the given PCRs. It uses // AIK auth and a given AIK handle. func Quote(rw io.ReadWriter, handle tpmutil.Handle, data []byte, pcrNums []int, aikAuth []byte) ([]byte, []byte, error) { // Run OSAP for the handle, reading a random OddOSAP for our initial // command and getting back a secret and a response. sharedSecret, osapr, err := newOSAPSession(rw, etKeyHandle, handle, aikAuth) if err != nil { return nil, nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) // Hash the data to get the value to pass to quote2. hash := sha1.Sum(data) pcrSel, err := newPCRSelection(pcrNums) if err != nil { return nil, nil, err } authIn := []interface{}{ordQuote, hash, pcrSel} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, nil, err } pcrc, sig, ra, ret, err := quote(rw, handle, hash, pcrSel, ca) if err != nil { return nil, nil, err } // Check response authentication. raIn := []interface{}{ret, ordQuote, pcrc, tpmutil.U32Bytes(sig)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, nil, err } return sig, pcrc.Values, nil } // MakeIdentity creates a new AIK with the given new auth value, and the given // parameters for the privacy CA that will be used to attest to it. // If both pk and label are nil, then the TPM_CHOSENID_HASH is set to all 0s as // a special case. MakeIdentity returns a key blob for the newly-created key. // The caller must be authorized to use the SRK, since the private part of the // AIK is sealed against the SRK. // TODO(tmroeder): currently, this code can only create 2048-bit RSA keys. func MakeIdentity(rw io.ReadWriter, srkAuth []byte, ownerAuth []byte, aikAuth []byte, pk crypto.PublicKey, label []byte) ([]byte, error) { // Run OSAP for the SRK, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretSRK, osaprSRK, err := newOSAPSession(rw, etSRK, khSRK, srkAuth) if err != nil { return nil, err } defer osaprSRK.Close(rw) defer zeroBytes(sharedSecretSRK[:]) // Run OSAP for the Owner, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth) if err != nil { return nil, err } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) // EncAuth for a MakeIdentity command is computed as // // encAuth = XOR(aikAuth, SHA1(sharedSecretOwn || )) // // In this case, the last even nonce is NonceEven from OSAP for the Owner. xorData, err := tpmutil.Pack(sharedSecretOwn, osaprOwn.NonceEven) if err != nil { return nil, err } defer zeroBytes(xorData) encAuthData := sha1.Sum(xorData) var encAuth Digest for i := range encAuth { encAuth[i] = aikAuth[i] ^ encAuthData[i] } var caDigest Digest if (pk != nil) != (label != nil) { return nil, errors.New("inconsistent null values between the pk and the label") } if pk != nil { pubKey, err := convertPubKey(pk) if err != nil { return nil, err } // We can't pack the pair of values directly, since the label is // included directly as bytes, without any length. fullpkb, err := tpmutil.Pack(pubKey) if err != nil { return nil, err } caDigestBytes := append(label, fullpkb...) caDigest = sha1.Sum(caDigestBytes) } rsaAIKParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, //Exponent: big.NewInt(0x10001).Bytes(), // 65537. Implicit? } packedParams, err := tpmutil.Pack(rsaAIKParams) if err != nil { return nil, err } aikParams := keyParams{ AlgID: AlgRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15SHA1, Params: packedParams, } aik := &key{ Version: 0x01010000, KeyUsage: keyIdentity, KeyFlags: 0, AuthDataUsage: authAlways, AlgorithmParams: aikParams, } // The digest input for MakeIdentity authentication is // // digest = SHA1(ordMakeIdentity || encAuth || caDigest || aik) // authIn := []interface{}{ordMakeIdentity, encAuth, caDigest, aik} ca1, err := newCommandAuth(osaprSRK.AuthHandle, osaprSRK.NonceEven, nil, sharedSecretSRK[:], authIn) if err != nil { return nil, err } ca2, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return nil, err } k, sig, ra1, ra2, ret, err := makeIdentity(rw, encAuth, caDigest, aik, ca1, ca2) if err != nil { return nil, err } // Check response authentication. raIn := []interface{}{ret, ordMakeIdentity, k, tpmutil.U32Bytes(sig)} if err := ra1.verify(ca1.NonceOdd, sharedSecretSRK[:], raIn); err != nil { return nil, err } if err := ra2.verify(ca2.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return nil, err } // TODO(tmroeder): check the signature against the pubEK. blob, err := tpmutil.Pack(k) if err != nil { return nil, err } return blob, nil } func unloadTrspiCred(blob []byte) ([]byte, error) { /* * Trousers expects the asym blob to have an additional data in the header. * The relevant data is duplicated in the TPM_SYMMETRIC_KEY struct so we parse * and throw the header away. * TODO(dkarch): Trousers is not doing credential activation correctly. We should * remove this and instead expose the asymmetric decryption and symmetric decryption * so that anyone generating a challenge for Trousers can unload the header themselves * and send us a correctly formatted challenge. */ var header struct { Credsize uint32 AlgID uint32 EncScheme uint16 SigScheme uint16 Parmsize uint32 } symbuf := bytes.NewReader(blob) if err := binary.Read(symbuf, binary.BigEndian, &header); err != nil { return nil, err } // Unload the symmetric key parameters. parms := make([]byte, header.Parmsize) if err := binary.Read(symbuf, binary.BigEndian, parms); err != nil { return nil, err } // Unload and return the symmetrically encrypted secret. cred := make([]byte, header.Credsize) if err := binary.Read(symbuf, binary.BigEndian, cred); err != nil { return nil, err } return cred, nil } // ActivateIdentity asks the TPM to decrypt an EKPub encrypted symmetric session key // which it uses to decrypt the symmetrically encrypted secret. func ActivateIdentity(rw io.ReadWriter, aikAuth []byte, ownerAuth []byte, aik tpmutil.Handle, asym, sym []byte) ([]byte, error) { // Run OIAP for the AIK. oiaprAIK, err := oiap(rw) if err != nil { return nil, fmt.Errorf("failed to start OIAP session: %v", err) } // Run OSAP for the owner, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth) if err != nil { return nil, fmt.Errorf("failed to start OSAP session: %v", err) } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) authIn := []interface{}{ordActivateIdentity, tpmutil.U32Bytes(asym)} ca1, err := newCommandAuth(oiaprAIK.AuthHandle, oiaprAIK.NonceEven, nil, aikAuth, authIn) if err != nil { return nil, fmt.Errorf("newCommandAuth failed: %v", err) } ca2, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return nil, fmt.Errorf("newCommandAuth failed: %v", err) } symkey, ra1, ra2, ret, err := activateIdentity(rw, aik, asym, ca1, ca2) if err != nil { return nil, fmt.Errorf("activateIdentity failed: %v", err) } // Check response authentication. raIn := []interface{}{ret, ordActivateIdentity, symkey} if err := ra1.verify(ca1.NonceOdd, aikAuth, raIn); err != nil { return nil, fmt.Errorf("aik resAuth failed to verify: %v", err) } if err := ra2.verify(ca2.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return nil, fmt.Errorf("owner resAuth failed to verify: %v", err) } cred, err := unloadTrspiCred(sym) if err != nil { return nil, fmt.Errorf("unloadTrspiCred failed: %v", err) } var ( block cipher.Block iv []byte ciphertxt []byte secret []byte ) switch id := symkey.AlgID; id { case AlgAES128: block, err = aes.NewCipher(symkey.Key) if err != nil { return nil, fmt.Errorf("aes.NewCipher failed: %v", err) } iv = cred[:aes.BlockSize] ciphertxt = cred[aes.BlockSize:] secret = ciphertxt default: return nil, fmt.Errorf("%v is not a supported session key algorithm", id) } switch es := symkey.EncScheme; es { case esSymCTR: stream := cipher.NewCTR(block, iv) stream.XORKeyStream(secret, ciphertxt) case esSymOFB: stream := cipher.NewOFB(block, iv) stream.XORKeyStream(secret, ciphertxt) case esSymCBCPKCS5: mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(secret, ciphertxt) // Remove PKCS5 padding. padlen := int(secret[len(secret)-1]) secret = secret[:len(secret)-padlen] default: return nil, fmt.Errorf("%v is not a supported encryption scheme", es) } return secret, nil } // ResetLockValue resets the dictionary-attack value in the TPM; this allows the // TPM to start working again after authentication errors without waiting for // the dictionary-attack defenses to time out. This requires owner // authentication. func ResetLockValue(rw io.ReadWriter, ownerAuth Digest) error { // Run OSAP for the Owner, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth[:]) if err != nil { return err } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) // The digest input for ResetLockValue auth is // // digest = SHA1(ordResetLockValue) // authIn := []interface{}{ordResetLockValue} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return err } ra, ret, err := resetLockValue(rw, ca) if err != nil { return err } // Check response authentication. raIn := []interface{}{ret, ordResetLockValue} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return err } return nil } // ownerReadInternalHelper sets up command auth and checks response auth for // OwnerReadInternalPub. It's not exported because OwnerReadInternalPub only // supports two fixed key handles: khEK and khSRK. func ownerReadInternalHelper(rw io.ReadWriter, kh tpmutil.Handle, ownerAuth Digest) (*pubKey, error) { // Run OSAP for the Owner, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth[:]) if err != nil { return nil, err } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) // The digest input for OwnerReadInternalPub is // // digest = SHA1(ordOwnerReadInternalPub || kh) // authIn := []interface{}{ordOwnerReadInternalPub, kh} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return nil, err } pk, ra, ret, err := ownerReadInternalPub(rw, kh, ca) if err != nil { return nil, err } // Check response authentication. raIn := []interface{}{ret, ordOwnerReadInternalPub, pk} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return nil, err } return pk, nil } // OwnerReadSRK uses owner auth to get a blob representing the SRK. func OwnerReadSRK(rw io.ReadWriter, ownerAuth Digest) ([]byte, error) { pk, err := ownerReadInternalHelper(rw, khSRK, ownerAuth) if err != nil { return nil, err } return tpmutil.Pack(pk) } // ReadEKCert reads the EKCert from the NVRAM. // The TCG PC Client specifies additional headers that are to be stored with the EKCert, we parse them // here and return only the DER encoded certificate. // TCG PC Client Specific Implementation Specification for Conventional BIOS 7.4.4 // https://www.trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf func ReadEKCert(rw io.ReadWriter, ownAuth Digest) ([]byte, error) { const ( certIndex = 0x1000f000 // TPM_NV_INDEX_EKCert (TPM Main Part 2 TPM Structures 19.1.2) certTagPCClientStoredCert = 0x1001 // TCG_TAG_PCCLIENT_STORED_CERT certTagPCClientFullCert = 0x1002 // TCG_TAG_PCCLIENT_FULL_CERT tcgFullCert = 0 // TCG_FULL_CERT tcgPartialSmallCert = 1 // TCG_PARTIAL_SMALL_CERT ) offset := uint32(0) var header struct { Tag uint16 CertType uint8 CertSize uint16 } data, err := NVReadValue(rw, certIndex, offset, uint32(binary.Size(header)), []byte(ownAuth[:])) if err != nil { return nil, err } offset = offset + uint32(binary.Size(header)) buff := bytes.NewReader(data) if err := binary.Read(buff, binary.BigEndian, &header); err != nil { return nil, err } if header.Tag != certTagPCClientStoredCert { return nil, fmt.Errorf("invalid certificate") } var bufSize uint32 switch header.CertType { case tcgFullCert: var tag uint16 data, err := NVReadValue(rw, certIndex, offset, uint32(binary.Size(tag)), []byte(ownAuth[:])) if err != nil { return nil, err } bufSize = uint32(header.CertSize) + offset offset = offset + uint32(binary.Size(tag)) buff = bytes.NewReader(data) if err := binary.Read(buff, binary.BigEndian, &tag); err != nil { return nil, err } if tag != certTagPCClientFullCert { return nil, fmt.Errorf("certificate type and tag do not match") } case tcgPartialSmallCert: return nil, fmt.Errorf("certType is not TCG_FULL_CERT: currently do not support partial certs") default: return nil, fmt.Errorf("invalid certType: 0x%x", header.CertType) } var ekbuf []byte for offset < bufSize { length := bufSize - offset // TPMs can only read so much memory per command so we read in 128byte chunks. // 128 was taken from go-tspi. The actual max read seems to be platform dependent // but cannot be queried on TPM1.2 (and does not seem to appear in any documentation). if length > 128 { length = 128 } data, err = NVReadValue(rw, certIndex, offset, length, []byte(ownAuth[:])) if err != nil { return nil, err } ekbuf = append(ekbuf, data...) offset += length } return ekbuf, nil } // NVDefineSpace implements the reservation of NVRAM as specified in: // TPM-Main-Part-3-Commands_v1.2_rev116_01032011, P. 212 func NVDefineSpace(rw io.ReadWriter, nvData NVDataPublic, ownAuth []byte) error { var ra *responseAuth var ret uint32 if ownAuth == nil { } else { sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownAuth[:]) if err != nil { return fmt.Errorf("failed to start new auth session: %v", err) } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) // encAuth: NV_Define_Space is a special case where no encryption is used. // See spec: TPM-Main-Part-1-Design-Principles_v1.2_rev116_01032011, P. 81 xorData, err := tpmutil.Pack(sharedSecretOwn, osaprOwn.NonceEven) if err != nil { return err } defer zeroBytes(xorData) encAuthData := sha1.Sum(xorData) authIn := []interface{}{ordNVDefineSpace, nvData, encAuthData} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return err } ra, ret, err = nvDefineSpace(rw, nvData, encAuthData, ca) if err != nil { return fmt.Errorf("failed to define space in NVRAM: %v", err) } raIn := []interface{}{ret, ordNVDefineSpace} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return fmt.Errorf("failed to verify authenticity of response: %v", err) } } return nil } // NVReadValue returns the value from a given index, offset, and length in NVRAM. // See TPM-Main-Part-2-TPM-Structures 19.1. // If TPM isn't locked, no authentication is needed. // This is for platform suppliers only. // See TPM-Main-Part-3-Commands-20.4 func NVReadValue(rw io.ReadWriter, index, offset, len uint32, ownAuth []byte) ([]byte, error) { if ownAuth == nil { data, _, _, err := nvReadValue(rw, index, offset, len, nil) if err != nil { return nil, fmt.Errorf("failed to read from NVRAM: %v", err) } return data, nil } sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownAuth[:]) if err != nil { return nil, fmt.Errorf("failed to start new auth session: %v", err) } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) authIn := []interface{}{ordNVReadValue, index, offset, len} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return nil, fmt.Errorf("failed to construct owner auth fields: %v", err) } data, ra, ret, err := nvReadValue(rw, index, offset, len, ca) if err != nil { return nil, fmt.Errorf("failed to read from NVRAM: %v", err) } raIn := []interface{}{ret, ordNVReadValue, tpmutil.U32Bytes(data)} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return nil, fmt.Errorf("failed to verify authenticity of response: %v", err) } return data, nil } // NVReadValueAuth returns the value from a given index, offset, and length in NVRAM. // See TPM-Main-Part-2-TPM-Structures 19.1. // If TPM is locked, authentication is mandatory. // See TPM-Main-Part-3-Commands-20.5 func NVReadValueAuth(rw io.ReadWriter, index, offset, len uint32, auth []byte) ([]byte, error) { if auth == nil { return nil, fmt.Errorf("no auth value given but mandatory") } sharedSecret, osapr, err := newOSAPSession(rw, etOwner, khOwner, auth[:]) if err != nil { return nil, fmt.Errorf("failed to start new auth session: %v", err) } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) authIn := []interface{}{ordNVReadValueAuth, index, offset, len} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, fmt.Errorf("failed to construct auth fields: %v", err) } data, ra, ret, err := nvReadValue(rw, index, offset, len, ca) if err != nil { return nil, fmt.Errorf("failed to read from NVRAM: %v", err) } raIn := []interface{}{ret, ordNVReadValueAuth, tpmutil.U32Bytes(data)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, fmt.Errorf("failed to verify authenticity of response: %v", err) } return data, nil } // NVWriteValue for writing to the NVRAM. Needs a index for a defined space in NVRAM. // See TPM-Main-Part-3-Commands_v1.2_rev116_01032011, P216 func NVWriteValue(rw io.ReadWriter, index, offset uint32, data []byte, ownAuth []byte) error { if ownAuth == nil { if _, _, _, err := nvWriteValue(rw, index, offset, uint32(len(data)), data, nil); err != nil { return fmt.Errorf("failed to write to NVRAM: %v", err) } return nil } sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownAuth[:]) if err != nil { return fmt.Errorf("failed to start new auth session: %v", err) } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) authIn := []interface{}{ordNVWriteValue, index, offset, len(data), data} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return fmt.Errorf("failed to construct owner auth fields: %v", err) } data, ra, ret, err := nvWriteValue(rw, index, offset, uint32(len(data)), data, ca) if err != nil { return fmt.Errorf("failed to write to NVRAM: %v", err) } raIn := []interface{}{ret, ordNVWriteValue, tpmutil.U32Bytes(data)} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return fmt.Errorf("failed to verify authenticity of response: %v", err) } return nil } // NVWriteValueAuth for authenticated writing to the NVRAM. // Needs a index of a defined space in NVRAM. // See TPM-Main-Part-2-TPM-Structures 19.1. // If TPM is locked, authentification is mandatory. // See TPM-Main-Part-3-Commands_v1.2_rev116_01032011, P216 func NVWriteValueAuth(rw io.ReadWriter, index, offset uint32, data []byte, auth []byte) error { if auth == nil { return fmt.Errorf("no auth value given but mandatory") } sharedSecret, osapr, err := newOSAPSession(rw, etOwner, khOwner, auth[:]) if err != nil { return fmt.Errorf("failed to start new auth session: %v", err) } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) authIn := []interface{}{ordNVWriteValueAuth, index, offset, len(data), data} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return fmt.Errorf("failed to construct auth fields: %v", err) } data, ra, ret, err := nvWriteValue(rw, index, offset, uint32(len(data)), data, ca) if err != nil { return fmt.Errorf("failed to write to NVRAM: %v", err) } raIn := []interface{}{ret, ordNVWriteValueAuth, tpmutil.U32Bytes(data)} if err := ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return fmt.Errorf("failed to verify authenticity of response: %v", err) } return nil } // OwnerReadPubEK uses owner auth to get a blob representing the public part of the // endorsement key. func OwnerReadPubEK(rw io.ReadWriter, ownerAuth Digest) ([]byte, error) { pk, err := ownerReadInternalHelper(rw, khEK, ownerAuth) if err != nil { return nil, err } return tpmutil.Pack(pk) } // ReadPubEK reads the public part of the endorsement key when no owner is // established. func ReadPubEK(rw io.ReadWriter) ([]byte, error) { var n Nonce if _, err := rand.Read(n[:]); err != nil { return nil, err } pk, d, _, err := readPubEK(rw, n) if err != nil { return nil, err } // Recompute the hash of the pk and the nonce to defend against replay // attacks. b, err := tpmutil.Pack(pk, n) if err != nil { return nil, err } s := sha1.Sum(b) // There's no need for constant-time comparison of these hash values, // since no secret is involved. if !bytes.Equal(s[:], d[:]) { return nil, errors.New("the ReadPubEK operation failed the replay check") } return tpmutil.Pack(pk) } // GetManufacturer returns the manufacturer ID func GetManufacturer(rw io.ReadWriter) ([]byte, error) { return getCapability(rw, CapProperty, SubCapPropManufacturer) } // GetPermanentFlags returns the TPM_PERMANENT_FLAGS structure. func GetPermanentFlags(rw io.ReadWriter) (PermanentFlags, error) { var ret PermanentFlags raw, err := getCapability(rw, CapFlag, SubCapFlagPermanent) if err != nil { return ret, err } _, err = tpmutil.Unpack(raw, &ret) return ret, err } // GetAlgs returns a list of algorithms supported by the TPM device. func GetAlgs(rw io.ReadWriter) ([]Algorithm, error) { var algs []Algorithm for i := AlgRSA; i <= AlgXOR; i++ { buf, err := getCapability(rw, CapAlg, uint32(i)) if err != nil { return nil, err } if uint8(buf[0]) > 0 { algs = append(algs, Algorithm(i)) } } return algs, nil } // GetCapVersionVal returns the decoded contents of TPM_CAP_VERSION_INFO. func GetCapVersionVal(rw io.ReadWriter) (*CapVersionInfo, error) { var capVer CapVersionInfo buf, err := getCapability(rw, CapVersion, 0) if err != nil { return nil, err } if err := capVer.Decode(buf); err != nil { return nil, err } return &capVer, nil } // GetNVList returns a list of TPM_NV_INDEX values that // are currently allocated NV storage through TPM_NV_DefineSpace. func GetNVList(rw io.ReadWriter) ([]uint32, error) { buf, err := getCapability(rw, CapNVList, 0) if err != nil { return nil, err } nvList := make([]uint32, len(buf)/4) for i := range nvList { nvList[i] = uint32(binary.BigEndian.Uint32(buf[i*4 : (i+1)*4])) } return nvList, err } // GetNVIndex returns the structure of NVDataPublic which contains // information about the requested NV Index. // See: TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011, P.167 func GetNVIndex(rw io.ReadWriter, nvIndex uint32) (*NVDataPublic, error) { var nvInfo NVDataPublic buf, _ := getCapability(rw, CapNVIndex, nvIndex) if _, err := tpmutil.Unpack(buf, &nvInfo); err != nil { return &nvInfo, err } return &nvInfo, nil } // GetCapabilityRaw reads the requested capability and sub-capability from the // TPM and returns it as a []byte. Where possible, prefer the convenience // functions above, which return higher-level structs for easier handling. func GetCapabilityRaw(rw io.ReadWriter, cap, subcap uint32) ([]byte, error) { return getCapability(rw, cap, subcap) } // OwnerClear uses owner auth to clear the TPM. After this operation, the TPM // can change ownership. func OwnerClear(rw io.ReadWriter, ownerAuth Digest) error { // Run OSAP for the Owner, reading a random OddOSAP for our initial command // and getting back a secret and a handle. sharedSecretOwn, osaprOwn, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth[:]) if err != nil { return err } defer osaprOwn.Close(rw) defer zeroBytes(sharedSecretOwn[:]) // The digest input for OwnerClear is // // digest = SHA1(ordOwnerClear) // authIn := []interface{}{ordOwnerClear} ca, err := newCommandAuth(osaprOwn.AuthHandle, osaprOwn.NonceEven, nil, sharedSecretOwn[:], authIn) if err != nil { return err } ra, ret, err := ownerClear(rw, ca) if err != nil { return err } // Check response authentication. raIn := []interface{}{ret, ordOwnerClear} if err := ra.verify(ca.NonceOdd, sharedSecretOwn[:], raIn); err != nil { return err } return nil } // TakeOwnership takes over a TPM and inserts a new owner auth value and // generates a new SRK, associating it with a new SRK auth value. This // operation can only be performed if there isn't already an owner for the TPM. // The pub EK blob can be acquired by calling ReadPubEK if there is no owner, or // OwnerReadPubEK if there is. func TakeOwnership(rw io.ReadWriter, newOwnerAuth Digest, newSRKAuth Digest, pubEK []byte) error { // Encrypt the owner and SRK auth with the endorsement key. ek, err := UnmarshalPubRSAPublicKey(pubEK) if err != nil { return err } encOwnerAuth, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, ek, newOwnerAuth[:], oaepLabel) if err != nil { return err } encSRKAuth, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, ek, newSRKAuth[:], oaepLabel) if err != nil { return err } // The params for the SRK have very tight requirements: // - KeyLength must be 2048 // - alg must be RSA // - Enc must be OAEP SHA1 MGF1 // - Sig must be None // - Key usage must be Storage // - Key must not be migratable srkRSAParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, } srkpb, err := tpmutil.Pack(srkRSAParams) if err != nil { return err } srkParams := keyParams{ AlgID: AlgRSA, EncScheme: esRSAEsOAEPSHA1MGF1, SigScheme: ssNone, Params: srkpb, } srk := &key{ Version: 0x01010000, KeyUsage: keyStorage, KeyFlags: 0, AuthDataUsage: authAlways, AlgorithmParams: srkParams, } // Get command auth using OIAP with the new owner auth. oiapr, err := oiap(rw) if err != nil { return err } defer oiapr.Close(rw) // The digest for TakeOwnership is // // SHA1(ordTakeOwnership || pidOwner || encOwnerAuth || encSRKAuth || srk) authIn := []interface{}{ordTakeOwnership, pidOwner, tpmutil.U32Bytes(encOwnerAuth), tpmutil.U32Bytes(encSRKAuth), srk} ca, err := newCommandAuth(oiapr.AuthHandle, oiapr.NonceEven, nil, newOwnerAuth[:], authIn) if err != nil { return err } k, ra, ret, err := takeOwnership(rw, encOwnerAuth, encSRKAuth, srk, ca) if err != nil { return err } raIn := []interface{}{ret, ordTakeOwnership, k} return ra.verify(ca.NonceOdd, newOwnerAuth[:], raIn) } func createWrapKeyHelper(rw io.ReadWriter, srkAuth []byte, keyFlags KeyFlags, usageAuth Digest, migrationAuth Digest, pcrs []int) (*key, error) { // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etSRK, khSRK, srkAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) xorData, err := tpmutil.Pack(sharedSecret, osapr.NonceEven) if err != nil { return nil, err } defer zeroBytes(xorData) // We have to come up with NonceOdd early to encrypt the migration auth. var nonceOdd Nonce if _, err := rand.Read(nonceOdd[:]); err != nil { return nil, err } // ADIP (Authorization Data Insertion Protocol) is based on NonceEven for the first auth value // encrypted by the protocol, and NonceOdd for the second auth value. This is so that the two // keystreams are independent - otherwise, an eavesdropping attacker could XOR the two encrypted // values together to cancel out the key and calculate (usageAuth ^ migrationAuth). xorData2, err := tpmutil.Pack(sharedSecret, nonceOdd) if err != nil { return nil, err } defer zeroBytes(xorData2) encAuthDataKey := sha1.Sum(xorData) defer zeroBytes(encAuthDataKey[:]) encAuthDataKey2 := sha1.Sum(xorData2) defer zeroBytes(encAuthDataKey2[:]) var encUsageAuth Digest for i := range usageAuth { encUsageAuth[i] = encAuthDataKey[i] ^ usageAuth[i] } var encMigrationAuth Digest for i := range migrationAuth { encMigrationAuth[i] = encAuthDataKey2[i] ^ migrationAuth[i] } rParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, } rParamsPacked, err := tpmutil.Pack(&rParams) if err != nil { return nil, err } var pcrInfoBytes []byte if len(pcrs) > 0 { pcrInfo, err := newPCRInfo(rw, pcrs) if err != nil { return nil, err } pcrInfoBytes, err = tpmutil.Pack(pcrInfo) if err != nil { return nil, err } } keyInfo := &key{ Version: 0x01010000, KeyUsage: keySigning, KeyFlags: keyFlags, AuthDataUsage: authAlways, AlgorithmParams: keyParams{ AlgID: AlgRSA, EncScheme: esNone, SigScheme: ssRSASaPKCS1v15DER, Params: rParamsPacked, }, PCRInfo: pcrInfoBytes, } authIn := []interface{}{ordCreateWrapKey, encUsageAuth, encMigrationAuth, keyInfo} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, &nonceOdd, sharedSecret[:], authIn) if err != nil { return nil, err } k, ra, ret, err := createWrapKey(rw, encUsageAuth, encMigrationAuth, keyInfo, ca) if err != nil { return nil, err } raIn := []interface{}{ret, ordCreateWrapKey, k} if err = ra.verify(ca.NonceOdd, sharedSecret[:], raIn); err != nil { return nil, err } return k, nil } // CreateWrapKey creates a new RSA key for signatures inside the TPM. It is // wrapped by the SRK (which is to say, the SRK is the parent key). The key can // be bound to the specified PCR numbers so that it can only be used for // signing if the PCR values of those registers match. The pcrs parameter can // be nil in which case the key is not bound to any PCRs. The usageAuth // parameter defines the auth key for using this new key. The migrationAuth // parameter would be used for authorizing migration of the key (although this // code currently disables migration). func CreateWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth Digest, migrationAuth Digest, pcrs []int) ([]byte, error) { k, err := createWrapKeyHelper(rw, srkAuth, 0, usageAuth, migrationAuth, pcrs) if err != nil { return nil, err } keyblob, err := tpmutil.Pack(k) if err != nil { return nil, err } return keyblob, nil } // CreateMigratableWrapKey creates a new RSA key as in CreateWrapKey, but the // key is migratable (with the given migration auth). // Returns the loadable KeyBlob as well as just the encrypted private part, for // migration. func CreateMigratableWrapKey(rw io.ReadWriter, srkAuth []byte, usageAuth Digest, migrationAuth Digest, pcrs []int) ([]byte, []byte, error) { k, err := createWrapKeyHelper(rw, srkAuth, keyMigratable, usageAuth, migrationAuth, pcrs) if err != nil { return nil, nil, err } keyblob, err := tpmutil.Pack(k) if err != nil { return nil, nil, err } return keyblob, k.EncData, nil } // AuthorizeMigrationKey authorizes a given public key for use in migrating // migratable keys. The scheme is REWRAP. func AuthorizeMigrationKey(rw io.ReadWriter, ownerAuth Digest, migrationKey crypto.PublicKey) ([]byte, error) { // Run OSAP for the OwnerAuth, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etOwner, khOwner, ownerAuth[:]) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) var pub *pubKey if migrationKey != nil { pub, err = convertPubKey(migrationKey) if err != nil { return nil, err } // convertPubKey is designed for signing keys. pub.AlgorithmParams.EncScheme = esRSAEsOAEPSHA1MGF1 pub.AlgorithmParams.SigScheme = ssNone rsaParams := rsaKeyParams{ KeyLength: 2048, NumPrimes: 2, //Exponent: default (omit) } pub.AlgorithmParams.Params, err = tpmutil.Pack(rsaParams) if err != nil { return nil, err } } scheme := msRewrap // The digest for auth for the authorizeMigrationKey command is computed as // SHA1(ordAuthorizeMigrationkey || migrationScheme || migrationKey) authIn := []interface{}{ordAuthorizeMigrationKey, scheme, pub} // The commandAuth uses the shared secret as an HMAC key. ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } migrationAuth, _, _, err := authorizeMigrationKey(rw, scheme, *pub, ca) if err != nil { return nil, err } // For now, ignore the response authentication. return migrationAuth, nil } // CreateMigrationBlob performs a Rewrap migration of the given key blob. func CreateMigrationBlob(rw io.ReadWriter, srkAuth Digest, migrationAuth Digest, keyBlob []byte, migrationKeyBlob []byte) ([]byte, error) { // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etSRK, khSRK, srkAuth[:]) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) // The createMigrationBlob command needs an OIAP session in addition to the // OSAP session. oiapr, err := oiap(rw) if err != nil { return nil, err } defer oiapr.Close(rw) encData := tpmutil.U32Bytes(keyBlob) // The digest for auth1 and auth2 for the createMigrationBlob command is // SHA1(ordCreateMigrationBlob || migrationScheme || migrationKeyBlob || encData) authIn := []interface{}{ordCreateMigrationBlob, msRewrap, migrationKeyBlob, encData} // The first commandAuth uses the shared secret as an HMAC key. ca1, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } // The second commandAuth is based on OIAP instead of OSAP and uses the // migration auth as the HMAC key. ca2, err := newCommandAuth(oiapr.AuthHandle, oiapr.NonceEven, nil, migrationAuth[:], authIn) if err != nil { return nil, err } _, outData, _, _, _, err := createMigrationBlob(rw, khSRK, msRewrap, migrationKeyBlob, encData, ca1, ca2) if err != nil { return nil, err } // For now, ignore the response authenticatino. return outData, nil } // https://golang.org/src/crypto/rsa/pkcs1v15.go?s=8762:8862#L204 var hashPrefixes = map[crypto.Hash][]byte{ crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c}, crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix. crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14}, } // Sign will sign a digest using the supplied key handle. Uses PKCS1v15 signing, which means the hash OID is prefixed to the // hash before it is signed. Therefore the hash used needs to be passed as the hash parameter to determine the right // prefix. func Sign(rw io.ReadWriter, keyAuth []byte, keyHandle tpmutil.Handle, hash crypto.Hash, hashed []byte) ([]byte, error) { prefix, ok := hashPrefixes[hash] if !ok { return nil, errors.New("Unsupported hash") } data := append(prefix, hashed...) // Run OSAP for the SRK, reading a random OddOSAP for our initial // command and getting back a secret and a handle. sharedSecret, osapr, err := newOSAPSession(rw, etKeyHandle, keyHandle, keyAuth) if err != nil { return nil, err } defer osapr.Close(rw) defer zeroBytes(sharedSecret[:]) authIn := []interface{}{ordSign, tpmutil.U32Bytes(data)} ca, err := newCommandAuth(osapr.AuthHandle, osapr.NonceEven, nil, sharedSecret[:], authIn) if err != nil { return nil, err } signature, ra, ret, err := sign(rw, keyHandle, data, ca) if err != nil { return nil, err } raIn := []interface{}{ret, ordSign, tpmutil.U32Bytes(signature)} err = ra.verify(ca.NonceOdd, sharedSecret[:], raIn) if err != nil { return nil, err } return signature, nil } // PcrReset resets the given PCRs. Given typical locality restrictions, this can usually only be 16 or 23. func PcrReset(rw io.ReadWriter, pcrs []int) error { pcrSelect, err := newPCRSelection(pcrs) if err != nil { return err } err = pcrReset(rw, pcrSelect) if err != nil { return err } return nil } // ForceClear is normally used by firmware but on some platforms // vendors got it wrong and didn't call TPM_DisableForceClear. // It removes forcefully the ownership of the TPM. func ForceClear(rw io.ReadWriter) error { in := []interface{}{} out := []interface{}{} _, err := submitTPMRequest(rw, tagRQUCommand, ordForceClear, in, out) return err } // Startup performs TPM_Startup(TPM_ST_CLEAR) to initialize the TPM. func startup(rw io.ReadWriter) error { var typ uint16 = 0x0001 // TPM_ST_CLEAR in := []interface{}{typ} out := []interface{}{} _, err := submitTPMRequest(rw, tagRQUCommand, ordStartup, in, out) return err } // createEK performs TPM_CreateEndorsementKeyPair to create the EK in the TPM. func createEK(rw io.ReadWriter) error { antiReplay := Nonce{} keyInfo := []byte{ 0x00, 0x00, 0x00, 0x01, // Algorithm = RSA 0x00, 0x03, // EncScheme = OAEP 0x00, 0x01, // SigScheme = None 0x00, 0x00, 0x00, 0x0c, // ParamsSize = 12 0x00, 0x00, 0x08, 0x00, // KeyLength = 2048 0x00, 0x00, 0x00, 0x02, // NumPrimes = 2 0x00, 0x00, 0x00, 0x00, // ExponentSize = 0 (default 65537 exponent) } in := []interface{}{antiReplay, keyInfo} out := []interface{}{} _, err := submitTPMRequest(rw, tagRQUCommand, ordCreateEndorsementKeyPair, in, out) return err } go-tpm-0.9.3/tpm/tpm_other_test.go000066400000000000000000000020101473040456300171140ustar00rootroot00000000000000//go:build !windows // Copyright (c) 2014, 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 tpm import ( "io" "os" "testing" ) // Skip the test if we can't open the TPM. func openTPMOrSkip(t *testing.T) io.ReadWriteCloser { tpmPath := os.Getenv(tpmPathEnvVar) if tpmPath == "" { tpmPath = "/dev/tpm0" } rwc, err := openAndStartupTPM(tpmPath, true) if err != nil { t.Skipf("Skipping test, since we can't open %s for read/write: %s\n", tpmPath, err) } return rwc } go-tpm-0.9.3/tpm/tpm_test.go000066400000000000000000000447651473040456300157420ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "bytes" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" mathrand "math/rand" "os" "testing" "github.com/google/go-tpm/tpmutil" ) var ( ownerAuthEnvVar = "TPM_OWNER_AUTH" srkAuthEnvVar = "TPM_SRK_AUTH" aikAuthEnvVar = "TPM_AIK_AUTH" tpmPathEnvVar = "TPM_PATH" ) // getAuth looks in the environment variables to find a given auth input value. // If the environment variable is not present, then getAuth returns the // well-known auth value of 20 bytes of zeros. func getAuth(name string) Digest { var auth Digest authInput := os.Getenv(name) if authInput != "" { aa := sha1.Sum([]byte(authInput)) copy(auth[:], aa[:]) } return auth } func TestGetKeys(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() handles, err := GetKeys(rwc) if err != nil { t.Fatal("Couldn't enumerate keys in the TPM:", err) } t.Logf("Got %d keys: % d\n", len(handles), handles) } func TestGetManufacturer(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() vendorID, err := GetManufacturer(rwc) if err != nil { t.Fatal("Couldn't read VendorID from TPM:", err) } t.Logf("TPM VendorID: %v\n", vendorID) } func TestGetAlgs(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() algs, err := GetAlgs(rwc) if err != nil { t.Fatalf("Couldn't read Algorithms: %v", err) } want := []Algorithm{AlgRSA, AlgSHA, AlgHMAC, AlgMGF1} outer: for _, alg := range want { for _, got := range algs { if got == alg { continue outer } t.Errorf("GetAlgs returned %v, which does not contain a required algorithm %v", algs, alg.String()) } } } func TestGetNVList(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() nvList, err := GetNVList(rwc) if err != nil { t.Fatalf("Couldn't read NVList %v", err) } t.Logf("NVList is: %v", nvList) } func TestGetNVIndex(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() nvList, err := GetNVList(rwc) if err != nil { t.Fatalf("Couldn't read NVList %v", err) } t.Logf("NVList: %v", nvList) var nvInfo []NVDataPublic for _, nvEntry := range nvList { index, err := GetNVIndex(rwc, nvEntry) if err != nil { t.Fatalf("Can't read NVDataPublic of index: %v with: %v", nvEntry, err) } nvInfo = append(nvInfo, *index) } t.Logf("NVIndices with Attributes:%v", nvInfo) } func TestPcrExtend(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() var pcrValue [20]byte var value = "FFFFFFFFFFFFFFFFFFFF" copy(pcrValue[:], value) oldPcrValue, err := ReadPCR(rwc, 12) if err != nil { t.Fatal("Couldn't read PCR 12 from the TPM:", err) } newPcrValue, err := PcrExtend(rwc, 12, pcrValue) if err != nil { t.Fatal("Couldn't extend PCR 12 from the TPM:", err) } finalPcr := sha1.Sum(append(oldPcrValue, pcrValue[:]...)) if bytes.Equal(finalPcr[:], newPcrValue) { t.Logf("PCR are equal!\n") } else { t.Fatal("PCR are not equal! Test failed.\n") } } func TestReadEKCert(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() ownAuth := getAuth(ownerAuthEnvVar) cert, err := ReadEKCert(rwc, ownAuth) if err != nil { t.Fatal("Unable to read EKCert from NVRAM:", err) } x509cert, err := x509.ParseCertificate(cert) if err != nil { t.Logf("Malformed certificate: %v\n", err) } else { t.Logf("Certificate: %v\n", x509cert) } } func TestReadPCR(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() res, err := ReadPCR(rwc, 18) if err != nil { t.Fatal("Couldn't read PCR 18 from the TPM:", err) } t.Logf("Got PCR 18 value % x\n", res) } func TestFetchPCRValues(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() var mask pcrMask if err := mask.setPCR(17); err != nil { t.Fatal("Couldn't set PCR 17:", err) } pcrs, err := FetchPCRValues(rwc, []int{17}) if err != nil { t.Fatal("Couldn't get PCRs 17:", err) } comp, err := createPCRComposite(mask, pcrs) if err != nil { t.Fatal("Couldn't create PCR composite") } if len(comp) != 20 { t.Fatal("Invalid PCR composite") } var loc Locality _, err = createPCRInfoLong(loc, mask, pcrs) if err != nil { t.Fatal("Couldn't create a pcrInfoLong structure for these PCRs") } } func TestGetRandom(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Try to get 16 bytes of randomness from the TPM. b, err := GetRandom(rwc, 16) if err != nil { t.Fatal("Couldn't get 16 bytes of randomness from the TPM:", err) } if len(b) != 16 { t.Fatal("Couldn't get 16 bytes of randomness from the TPM") } } func TestNVDefineSpace(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Address reserved for testing as described in spec // TPM-Main-Part-2-TPM-Structures_v1.2_rev116_01032011, P.139 owner := getAuth(ownerAuthEnvVar) nvIndex := uint32(0x0000F004) pcrInfoRead, err := newPCRInfoShort(rwc, LocZero, []int{1}) if err != nil { t.Fatalf("Couldn't create PCRInfoRead in define space") } pcrInfoWrite, err := newPCRInfoShort(rwc, LocZero, []int{1}) if err != nil { t.Fatalf("Couldn't create PCRInfoWrite in define space") } nvAtt := &nvAttributes{tagNVAttributes, NVPerOwnerWrite} nvData := NVDataPublic{tagNVDataPublic, nvIndex, *pcrInfoRead, *pcrInfoWrite, *nvAtt, true, true, false, uint32(20)} err = NVDefineSpace(rwc, nvData, []byte(owner[:])) if err != nil { t.Fatalf("Couldn't define space in NV RAM %v", err) } t.Log(err) } func TestOIAP(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Get auth info from OIAP. oiapr, err := oiap(rwc) if err != nil { t.Fatal("Couldn't run OIAP:", err) } defer oiapr.Close(rwc) } func TestOSAP(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Try to run OSAP for the SRK. osapc := &osapCommand{ EntityType: etSRK, EntityValue: khSRK, } if _, err := rand.Read(osapc.OddOSAP[:]); err != nil { t.Fatal("Couldn't get a random odd OSAP nonce") } osapr, err := osap(rwc, osapc) if err != nil { t.Fatal("Couldn't run OSAP:", err) } defer osapr.Close(rwc) } func TestResizeableSlice(t *testing.T) { // Set up an encoded slice with a byte array. ra := &responseAuth{ NonceEven: [20]byte{}, ContSession: 1, Auth: [20]byte{}, } b := make([]byte, 322) if _, err := rand.Read(b); err != nil { t.Fatal("Couldn't read random bytes into the byte array") } bb, err := tpmutil.Pack(ra, tpmutil.U32Bytes(b)) if err != nil { t.Fatal("Couldn't pack the bytes:", err) } var ra2 responseAuth var b2 tpmutil.U32Bytes if _, err := tpmutil.Unpack(bb, &ra2, &b2); err != nil { t.Fatal("Couldn't unpack the resizeable values:", err) } if !bytes.Equal(b2, b) { t.Fatal("ResizeableSlice was not resized or copied correctly") } } func TestSeal(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() data := make([]byte, 64) data[0] = 137 data[1] = 138 data[2] = 139 srkAuth := getAuth(srkAuthEnvVar) sealed, err := Seal(rwc, LocZero, []int{17} /* PCR 17 */, data, srkAuth[:]) if err != nil { t.Fatal("Couldn't seal the data:", err) } data2, err := Unseal(rwc, sealed, srkAuth[:]) if err != nil { t.Fatal("Couldn't unseal the data:", err) } if !bytes.Equal(data2, data) { t.Fatal("Unsealed data doesn't match original data") } } func TestReseal(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() data := make([]byte, 64) data[0] = 137 data[1] = 138 data[2] = 139 pcrMap := make(map[int][]byte) pcrMap[23] = make([]byte, 20) pcrMap[16] = make([]byte, 20) srkAuth := getAuth(srkAuthEnvVar) sealed, err := Reseal(rwc, LocZero, pcrMap, data, srkAuth[:]) if err != nil { t.Fatal("Couldn't seal the data:", err) } data2, err := Unseal(rwc, sealed, srkAuth[:]) if err != nil { t.Fatal("Couldn't unseal the data:", err) } if !bytes.Equal(data2, data) { t.Fatal("Unsealed data doesn't match original data") } } func TestLoadKey2(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Get the key from aikblob, assuming it exists. Otherwise, skip the test. blob, err := os.ReadFile("./aikblob") if err != nil { t.Skip("No aikblob file; skipping test") } // We're using the well-known authenticator of 20 bytes of zeros. srkAuth := getAuth(srkAuthEnvVar) handle, err := LoadKey2(rwc, blob, srkAuth[:]) if err != nil { t.Fatal("Couldn't load the AIK into the TPM and get a handle for it:", err) } if err := CloseKey(rwc, handle); err != nil { t.Fatal("Couldn't flush the AIK from the TPM:", err) } } func TestQuote2(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Get the key from aikblob, assuming it exists. Otherwise, skip the test. blob, err := os.ReadFile("./aikblob") if err != nil { t.Skip("No aikblob file; skipping test") } // Load the AIK for the quote. // We're using the well-known authenticator of 20 bytes of zeros. srkAuth := getAuth(srkAuthEnvVar) handle, err := LoadKey2(rwc, blob, srkAuth[:]) if err != nil { t.Fatal("Couldn't load the AIK into the TPM and get a handle for it:", err) } defer CloseKey(rwc, handle) // Data to quote. data := []byte(`The OS says this test is good`) aikAuth := getAuth(aikAuthEnvVar) q, err := Quote2(rwc, handle, data, []int{17, 18}, 1 /* addVersion */, aikAuth[:]) if err != nil { t.Fatal("Couldn't quote the data:", err) } if len(q) == 0 { t.Fatal("Couldn't get a quote using an AIK") } } func TestGetPubKey(t *testing.T) { // For testing purposes, use the aikblob if it exists. Otherwise, just skip // this test. TODO(tmroeder): implement AIK creation so we can always run // this test. rwc := openTPMOrSkip(t) defer rwc.Close() // Get the key from aikblob, assuming it exists. Otherwise, skip the test. blob, err := os.ReadFile("./aikblob") if err != nil { t.Skip("No aikblob file; skipping test") } // Load the AIK for the quote. // We're using the well-known authenticator of 20 bytes of zeros. srkAuth := getAuth(srkAuthEnvVar) handle, err := LoadKey2(rwc, blob, srkAuth[:]) if err != nil { t.Fatal("Couldn't load the AIK into the TPM and get a handle for it:", err) } defer CloseKey(rwc, handle) k, err := GetPubKey(rwc, handle, srkAuth[:]) if err != nil { t.Fatal("Couldn't get the pub key for the AIK") } if len(k) == 0 { t.Fatal("Couldn't get a pubkey blob from an AIK") } } func TestQuote(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // Get the key from aikblob, assuming it exists. Otherwise, skip the test. blob, err := os.ReadFile("./aikblob") if err != nil { t.Skip("No aikblob file; skipping test") } // Load the AIK for the quote. srkAuth := getAuth(srkAuthEnvVar) handle, err := LoadKey2(rwc, blob, srkAuth[:]) if err != nil { t.Fatal("Couldn't load the AIK into the TPM and get a handle for it:", err) } defer CloseKey(rwc, handle) // Data to quote. data := []byte(`The OS says this test is good`) pcrNums := []int{17, 18} aikAuth := getAuth(aikAuthEnvVar) q, values, err := Quote(rwc, handle, data, pcrNums, aikAuth[:]) if err != nil { t.Fatal("Couldn't quote the data:", err) } // Verify the quote. pk, err := UnmarshalRSAPublicKey(blob) if err != nil { t.Fatal("Couldn't extract an RSA key from the AIK blob:", err) } if err := VerifyQuote(pk, data, q, pcrNums, values); err != nil { t.Fatal("The quote didn't pass verification:", err) } } func TestUnmarshalRSAPublicKey(t *testing.T) { // Get the key from aikblob, assuming it exists. Otherwise, skip the test. blob, err := os.ReadFile("./aikblob") if err != nil { t.Skip("No aikblob file; skipping test") } if _, err := UnmarshalRSAPublicKey(blob); err != nil { t.Fatal("Couldn't extract an RSA key from the AIK blob:", err) } } func TestMakeIdentity(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() srkAuth := getAuth(srkAuthEnvVar) ownerAuth := getAuth(ownerAuthEnvVar) aikAuth := getAuth(aikAuthEnvVar) // In the simplest case, we pass in nil for the Privacy CA key and the // label. blob, err := MakeIdentity(rwc, srkAuth[:], ownerAuth[:], aikAuth[:], nil, nil) if err != nil { t.Fatal("Couldn't make a new AIK in the TPM:", err) } handle, err := LoadKey2(rwc, blob, srkAuth[:]) if err != nil { t.Fatal("Couldn't load the freshly-generated AIK into the TPM and get a handle for it:", err) } defer CloseKey(rwc, handle) // Data to quote. data := []byte(`The OS says this test and new AIK is good`) pcrNums := []int{17, 18} q, values, err := Quote(rwc, handle, data, pcrNums, aikAuth[:]) if err != nil { t.Fatal("Couldn't quote the data:", err) } // Verify the quote. pk, err := UnmarshalRSAPublicKey(blob) if err != nil { t.Fatal("Couldn't extract an RSA key from the AIK blob:", err) } if err := VerifyQuote(pk, data, q, pcrNums, values); err != nil { t.Fatal("The quote didn't pass verification:", err) } } func TestResetLockValue(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // This test code assumes that the owner auth is the well-known value. ownerAuth := getAuth(ownerAuthEnvVar) if err := ResetLockValue(rwc, ownerAuth); err != nil { t.Fatal("Couldn't reset the lock value:", err) } } func TestOwnerReadSRK(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // This test code assumes that the owner auth is the well-known value. ownerAuth := getAuth(ownerAuthEnvVar) srkb, err := OwnerReadSRK(rwc, ownerAuth) if err != nil { t.Fatal("Couldn't read the SRK using owner auth:", err) } if len(srkb) == 0 { t.Fatal("Couldn't get an SRK blob from the TPM") } } func TestOwnerReadPubEK(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() // This test code assumes that the owner auth is the well-known value. ownerAuth := getAuth(ownerAuthEnvVar) pkb, err := OwnerReadPubEK(rwc, ownerAuth) if err != nil { t.Fatal("Couldn't read the pub EK using owner auth:", err) } pk, err := UnmarshalPubRSAPublicKey(pkb) if err != nil { t.Fatal("Couldn't unmarshal the endorsement key:", err) } if pk.N.BitLen() != 2048 { t.Fatal("Invalid endorsement key: not a 2048-bit RSA key") } } func TestOwnerClear(t *testing.T) { // Only enable this if you know what you're doing. t.Skip() rwc := openTPMOrSkip(t) defer rwc.Close() // This test code assumes that the owner auth is the well-known value. ownerAuth := getAuth(ownerAuthEnvVar) if err := OwnerClear(rwc, ownerAuth); err != nil { t.Fatal("Couldn't clear the TPM using owner auth:", err) } } func TestTakeOwnership(t *testing.T) { // This only works in limited circumstances, so it's disabled in general. t.Skip() rwc := openTPMOrSkip(t) defer rwc.Close() ownerAuth := getAuth(ownerAuthEnvVar) srkAuth := getAuth(srkAuthEnvVar) // This test assumes that the TPM has been cleared using OwnerClear. pubEK, err := ReadPubEK(rwc) // Create the EK if needed. if err == tpmError(errNoEndorsement) { if err = createEK(rwc); err == nil { pubEK, err = ReadPubEK(rwc) } } if err != nil { t.Fatal("Couldn't read the public endorsement key from the TPM:", err) } if err := TakeOwnership(rwc, ownerAuth, srkAuth, pubEK); err != nil { t.Fatal("Couldn't take ownership of the TPM:", err) } } func TestForceClear(t *testing.T) { // Only enable this if you know what you're doing. // TPM force clear clears the ownership of the TPM. // Beware of running this test on a production system. t.Skip() rwc := openTPMOrSkip(t) defer rwc.Close() if err := ForceClear(rwc); err != nil { t.Fatal("Couldn't clear the TPM without owner auth in physical presence mode:", err) } } func TestNVWriteValue(t *testing.T) { mathrand.Seed(42) rwc := openTPMOrSkip(t) defer rwc.Close() nvList, err := GetNVList(rwc) if err != nil { t.Fatalf("Couldn't open TPM connection: %v", err) } // If no indices are set, skip the test. // Need DefineSpace-Function for implementing the other case if len(nvList) == 0 { t.Skip() } rndIndex := nvList[mathrand.Intn(len(nvList))] rndIndexInfo, err := GetNVIndex(rwc, rndIndex) if err != nil { t.Fatalf("Couldn't retrieve index information: %v", err) } indexValue, err := NVReadValue(rwc, rndIndex, 0, rndIndexInfo.Size, nil) if err != nil { t.Fatalf("Couldn't read index value @ index: %v with error: %v", rndIndex, err) } err = NVWriteValue(rwc, rndIndex, 0, indexValue, nil) if err != nil { t.Errorf("Couldn't write to index: %v with error: %v", rndIndex, err) } } func TestNVReadValueNoAuth(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() //Get NVList nvList, err := GetNVList(rwc) if err != nil { t.Fatalf("error reading nvlist: %v", err) } for _, item := range nvList { pubData, err := GetNVIndex(rwc, item) if err != nil { t.Errorf("reading index: %v has no data: %v", item, err) } indexvalue, err := NVReadValue(rwc, pubData.NVIndex, 0, pubData.Size, nil) if err != nil { t.Logf("cant read index: %v", err) } t.Logf("indexvalue: %v", indexvalue) } if len(nvList) == 0 { t.Logf("no values to read in nvram") } } func TestKeyMigration(t *testing.T) { rwc := openTPMOrSkip(t) defer rwc.Close() ownerAuth := getAuth(ownerAuthEnvVar) srkAuth := getAuth(srkAuthEnvVar) // Generate some random migration and usage auth values. migrationAuth := Digest{} usageAuth := Digest{} rand.Read(migrationAuth[:]) rand.Read(usageAuth[:]) // First generate our private key we'll use for migration. mk, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("error generating migration key: %v", err) } // Then get the key endorsed by the TPM for doing migrations. mb, err := AuthorizeMigrationKey(rwc, ownerAuth, mk.Public()) if err != nil { t.Fatalf("error authorizing migration key: %v", err) } // Create a migratable key in the TPM. _, privkey, err := CreateMigratableWrapKey( rwc, srkAuth[:], usageAuth, migrationAuth, []int{}, // no PCR's ) if err != nil { t.Fatalf("error creating key: %v", err) } // Migrate the key to the saved migration key. encPriv, err := CreateMigrationBlob(rwc, srkAuth, migrationAuth, privkey, mb) if err != nil { t.Fatalf("Error migrating created key: %v", err) } // Decrypt the key. Successful OAEP decryption implies successful migration. _, err = rsa.DecryptOAEP(sha1.New(), nil, mk, encPriv, []byte{0x54, 0x43, 0x50, 0x41} /* 'TCPA', see TPM 1.2 spec */) if err != nil { t.Errorf("Error decrypting migrated key blob: %v", err) } } go-tpm-0.9.3/tpm/tpm_windows_test.go000066400000000000000000000015601473040456300174760ustar00rootroot00000000000000// 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 tpm import ( "io" "testing" ) // Skip the test if we can't open the TPM. func openTPMOrSkip(t *testing.T) io.ReadWriteCloser { rwc, err := OpenTPM() if err != nil { t.Skipf("Skipping test, since we can't access the TPM: %s\n", err) } return rwc } go-tpm-0.9.3/tpm/verify.go000066400000000000000000000112761473040456300153760ustar00rootroot00000000000000// Copyright (c) 2014, 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 tpm import ( "crypto" "crypto/rsa" "crypto/sha1" "errors" "math/big" "github.com/google/go-tpm/tpmutil" ) // This file provides functions to extract a crypto/rsa public key from a key // blob or a TPM_KEY of the right type. It also provides a function for // verifying a quote value given a public key for the key it was signed with. // UnmarshalRSAPublicKey takes in a blob containing a serialized RSA TPM_KEY and // converts it to a crypto/rsa.PublicKey. func UnmarshalRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) { // Parse the blob as a key. var k key if _, err := tpmutil.Unpack(keyBlob, &k); err != nil { return nil, err } return k.unmarshalRSAPublicKey() } // unmarshalRSAPublicKey unmarshals a TPM key into a crypto/rsa.PublicKey. func (k *key) unmarshalRSAPublicKey() (*rsa.PublicKey, error) { // Currently, we only support algRSA if k.AlgorithmParams.AlgID != AlgRSA { return nil, errors.New("only TPM_ALG_RSA is supported") } // This means that k.AlgorithmsParams.Params is an rsaKeyParams, which is // enough to create the exponent, and k.PubKey contains the key. var rsakp rsaKeyParams if _, err := tpmutil.Unpack(k.AlgorithmParams.Params, &rsakp); err != nil { return nil, err } // Make sure that the exponent will fit into an int before using it blindly. if len(rsakp.Exponent) > 4 { return nil, errors.New("exponent value doesn't fit into an int") } pk := &rsa.PublicKey{ N: new(big.Int).SetBytes(k.PubKey), // The exponent isn't set here, but it's fixed to 0x10001 E: 0x10001, } return pk, nil } // UnmarshalPubRSAPublicKey takes in a blob containing a serialized RSA // TPM_PUBKEY and converts it to a crypto/rsa.PublicKey. func UnmarshalPubRSAPublicKey(keyBlob []byte) (*rsa.PublicKey, error) { // Parse the blob as a key. var pk pubKey if _, err := tpmutil.Unpack(keyBlob, &pk); err != nil { return nil, err } return pk.unmarshalRSAPublicKey() } // unmarshalRSAPublicKey unmarshals a TPM pub key into a crypto/rsa.PublicKey. // This is almost identical to the identically named function for a TPM key. func (pk *pubKey) unmarshalRSAPublicKey() (*rsa.PublicKey, error) { // Currently, we only support AlgRSA if pk.AlgorithmParams.AlgID != AlgRSA { return nil, errors.New("only TPM_ALG_RSA is supported") } // This means that pk.AlgorithmsParams.Params is an rsaKeyParams, which is // enough to create the exponent, and pk.Key contains the key. var rsakp rsaKeyParams if _, err := tpmutil.Unpack(pk.AlgorithmParams.Params, &rsakp); err != nil { return nil, err } // Make sure that the exponent will fit into an int before using it blindly. if len(rsakp.Exponent) > 4 { return nil, errors.New("exponent value doesn't fit into an int") } rsapk := &rsa.PublicKey{ N: new(big.Int).SetBytes(pk.Key), // The exponent isn't set here, but it's fixed to 0x10001 E: 0x10001, } return rsapk, nil } // NewQuoteInfo computes a quoteInfo structure for a given pair of data and PCR // values. func NewQuoteInfo(data []byte, pcrNums []int, pcrs []byte) ([]byte, error) { // Compute the composite hash for these PCRs. pcrSel, err := newPCRSelection(pcrNums) if err != nil { return nil, err } comp, err := createPCRComposite(pcrSel.Mask, pcrs) if err != nil { return nil, err } qi := "eInfo{ Version: quoteVersion, Fixed: fixedQuote, Nonce: sha1.Sum(data), } copy(qi.CompositeDigest[:], comp) return tpmutil.Pack(qi) } // VerifyQuote verifies a quote against a given set of PCRs. func VerifyQuote(pk *rsa.PublicKey, data []byte, quote []byte, pcrNums []int, pcrs []byte) error { p, err := NewQuoteInfo(data, pcrNums, pcrs) if err != nil { return err } s := sha1.Sum(p) // Try to do a direct encryption to reverse the value and see if it's padded // with PKCS1v1.5. return rsa.VerifyPKCS1v15(pk, crypto.SHA1, s[:], quote) } // TODO(tmroeder): add VerifyQuote2 instead of VerifyQuote. This means I'll // probably have to look at the signature scheme and use that to choose how to // verify the signature, whether PKCS1v1.5 or OAEP. And this will have to be set // on the key before it's passed to ordQuote2 // TODO(tmroeder): handle key12 go-tpm-0.9.3/tpm2/000077500000000000000000000000001473040456300136165ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/audit.go000066400000000000000000000044771473040456300152670ustar00rootroot00000000000000package tpm2 import ( "bytes" "fmt" "reflect" ) // CommandAudit represents an audit session for attesting the execution of a // series of commands in the TPM. It is useful for both command and session // auditing. type CommandAudit struct { hash TPMIAlgHash digest []byte } // NewAudit initializes a new CommandAudit with the specified hash algorithm. func NewAudit(hash TPMIAlgHash) (*CommandAudit, error) { h, err := hash.Hash() if err != nil { return nil, err } return &CommandAudit{ hash: hash, digest: make([]byte, h.Size()), }, nil } // AuditCommand extends the audit digest with the given command and response. // Go Generics do not allow type parameters on methods, otherwise this would be // a method on CommandAudit. // See https://github.com/golang/go/issues/49085 for more information. func AuditCommand[C Command[R, *R], R any](a *CommandAudit, cmd C, rsp *R) error { cc := cmd.Command() cpHash, err := auditCPHash[R](cc, a.hash, cmd) if err != nil { return err } rpHash, err := auditRPHash(cc, a.hash, rsp) if err != nil { return err } ha, err := a.hash.Hash() if err != nil { return err } h := ha.New() h.Write(a.digest) h.Write(cpHash) h.Write(rpHash) a.digest = h.Sum(nil) return nil } // Digest returns the current digest of the audit. func (a *CommandAudit) Digest() []byte { return a.digest } // auditCPHash calculates the command parameter hash for a given command with // the given hash algorithm. The command is assumed to not have any decrypt // sessions. func auditCPHash[R any](cc TPMCC, h TPMIAlgHash, c Command[R, *R]) ([]byte, error) { names, err := cmdNames(c) if err != nil { return nil, err } parms, err := cmdParameters(c, nil) if err != nil { return nil, err } return cpHash(h, cc, names, parms) } // auditRPHash calculates the response parameter hash for a given response with // the given hash algorithm. The command is assumed to be successful and to not // have any encrypt sessions. func auditRPHash(cc TPMCC, h TPMIAlgHash, r any) ([]byte, error) { var parms bytes.Buffer parameters := taggedMembers(reflect.ValueOf(r).Elem(), "handle", true) for i, parameter := range parameters { if err := marshal(&parms, parameter); err != nil { return nil, fmt.Errorf("marshalling parameter %v: %w", i+1, err) } } return rpHash(h, TPMRCSuccess, cc, parms.Bytes()) } go-tpm-0.9.3/tpm2/bitfield.go000066400000000000000000000037271473040456300157400ustar00rootroot00000000000000package tpm2 import ( "fmt" ) // Bitfield represents a TPM bitfield (i.e., TPMA_*) type. type Bitfield interface { // Length returns the length of the bitfield. Length() int } // BitGetter represents a TPM bitfield (i.e., TPMA_*) type that can be read. type BitGetter interface { Bitfield // GetReservedBit returns the value of the given reserved bit. // If the bit is not reserved, returns false. GetReservedBit(pos int) bool } // BitSetter represents a TPM bitfield (i.e., TPMA_*) type that can be written. type BitSetter interface { Bitfield // GetReservedBit sets the value of the given reserved bit. SetReservedBit(pos int, val bool) } func checkPos(pos int, len int) { if pos >= len || pos < 0 { panic(fmt.Errorf("bit %d out of range for %d-bit field", pos, len)) } } // bitfield8 represents an 8-bit bitfield which may have reserved bits. // 8-bit TPMA_* types embed this one, and the reserved bits are stored in it. type bitfield8 uint8 // Length implements the Bitfield interface. func (bitfield8) Length() int { return 8 } // GetReservedBit implements the BitGetter interface. func (r bitfield8) GetReservedBit(pos int) bool { checkPos(pos, 8) return r&(1<> 8) r &= 0xFFFFF0FF return true, TPMFmt1Error{ canonical: r, subject: subj, index: idx, } } // IsWarning returns true if the error is a warning code. // This usually indicates a problem with the TPM state, and not the command. // Retrying the command later may succeed. func (r TPMRC) IsWarning() bool { if isFmt1, _ := r.isFmt1Error(); isFmt1 { // There aren't any format-1 warnings. return false } return (r&rcVer1) == rcVer1 && (r&rcWarn) == rcWarn } // Error produces a nice human-readable representation of the error, parsing TPM // FMT1 errors as needed. func (r TPMRC) Error() string { if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { return fmt1.Error() } if r.isFmt0Error() { desc, ok := fmt0Descs[r] if !ok { return fmt.Sprintf("unknown format-0 error code (0x%x)", uint32(r)) } return fmt.Sprintf("%s: %s", desc.name, desc.description) } if r.IsWarning() { desc, ok := warnDescs[r] if !ok { return fmt.Sprintf("unknown warning (0x%x)", uint32(r)) } return fmt.Sprintf("%s: %s", desc.name, desc.description) } return fmt.Sprintf("unrecognized error code (0x%x)", uint32(r)) } // Is returns whether the TPMRC (which may be a FMT1 error) is equal to the // given canonical error. func (r TPMRC) Is(target error) bool { targetTPMRC, ok := target.(TPMRC) if !ok { return false } if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { return fmt1.canonical == targetTPMRC } return r == targetTPMRC } // As returns whether the error can be assigned to the given interface type. // If supported, it updates the value pointed at by target. // Supports the Fmt1Error type. func (r TPMRC) As(target interface{}) bool { pFmt1, ok := target.(*TPMFmt1Error) if !ok { return false } isFmt1, fmt1 := r.isFmt1Error() if !isFmt1 { return false } *pFmt1 = fmt1 return true } go-tpm-0.9.3/tpm2/kdf.go000066400000000000000000000024351473040456300147150ustar00rootroot00000000000000package tpm2 import ( "crypto" legacy "github.com/google/go-tpm/legacy/tpm2" ) // 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(h crypto.Hash, key []byte, label string, contextU, contextV []byte, bits int) []byte { return legacy.KDFaHash(h, key, label, contextU, contextV, bits) } // KDFe implements TPM 2.0's ECDH key derivation function, as defined in // section 11.4.9.3 of the TPM revision 2 specification part 1. // See: https://trustedcomputinggroup.org/resource/tpm-library-specification/ // The z parameter is the x coordinate of one party's private ECC key multiplied // by the other party's public ECC point. // The use parameter is a non-null-terminated string. // The partyUInfo and partyVInfo are the x coordinates of the initiator's and // the responder's ECC points, respectively. func KDFe(h crypto.Hash, z []byte, use string, partyUInfo, partyVInfo []byte, bits int) []byte { return legacy.KDFeHash(h, z, use, partyUInfo, partyVInfo, bits) } go-tpm-0.9.3/tpm2/marshalling.go000066400000000000000000000100341473040456300164440ustar00rootroot00000000000000package tpm2 import ( "bytes" "fmt" "reflect" ) // Marshallable represents any TPM type that can be marshalled. type Marshallable interface { // marshal will serialize the given value, appending onto the given buffer. // Returns an error if the value is not marshallable. marshal(buf *bytes.Buffer) } // marshallableWithHint represents any TPM type that can be marshalled, // but that requires a selector ("hint") value when marshalling. Most TPMU_ are // an example of this. type marshallableWithHint interface { // get will return the corresponding union member by copy. If the union is // uninitialized, it will initialize a new zero-valued one. get(hint int64) (reflect.Value, error) } // Unmarshallable represents any TPM type that can be marshalled or unmarshalled. type Unmarshallable interface { Marshallable // marshal will deserialize the given value from the given buffer. // Returns an error if there was an unmarshalling error or if there was not // enough data in the buffer. unmarshal(buf *bytes.Buffer) error } // unmarshallableWithHint represents any TPM type that can be marshalled or unmarshalled, // but that requires a selector ("hint") value when unmarshalling. Most TPMU_ are // an example of this. type unmarshallableWithHint interface { marshallableWithHint // create will instantiate and return the corresponding union member. create(hint int64) (reflect.Value, error) } // Marshal will serialize the given values, returning them as a byte slice. func Marshal(v Marshallable) []byte { var buf bytes.Buffer if err := marshal(&buf, reflect.ValueOf(v)); err != nil { panic(fmt.Sprintf("unexpected error marshalling %v: %v", reflect.TypeOf(v).Name(), err)) } return buf.Bytes() } // Unmarshal unmarshals the given type from the byte array. // Returns an error if the buffer does not contain enough data to satisfy the // types, or if the types are not unmarshallable. func Unmarshal[T Marshallable, P interface { *T Unmarshallable }](data []byte) (*T, error) { buf := bytes.NewBuffer(data) var t T value := reflect.New(reflect.TypeOf(t)) if err := unmarshal(buf, value.Elem()); err != nil { return nil, err } return value.Interface().(*T), nil } // marshallableByReflection is a placeholder interface, to hint to the unmarshalling // library that it is supposed to use reflection. type marshallableByReflection interface { reflectionSafe() } // marshalByReflection is embedded into any type that can be marshalled by reflection, // needing no custom logic. type marshalByReflection struct{} func (marshalByReflection) reflectionSafe() {} // These placeholders are required because a type constraint cannot union another interface // that contains methods. // Otherwise, marshalByReflection would not implement Unmarshallable, and the Marshal/Unmarshal // functions would accept interface{ Marshallable | marshallableByReflection } instead. // Placeholder: because this type implements the defaultMarshallable interface, // the reflection library knows not to call this. func (marshalByReflection) marshal(_ *bytes.Buffer) { panic("not implemented") } // Placeholder: because this type implements the defaultMarshallable interface, // the reflection library knows not to call this. func (*marshalByReflection) unmarshal(_ *bytes.Buffer) error { panic("not implemented") } // boxed is a helper type for corner cases such as unions, where all members must be structs. type boxed[T any] struct { Contents *T } // box will put a value into a box. func box[T any](contents *T) boxed[T] { return boxed[T]{ Contents: contents, } } // unbox will take a value out of a box. func (b *boxed[T]) unbox() *T { return b.Contents } // marshal implements the Marshallable interface. func (b *boxed[T]) marshal(buf *bytes.Buffer) { if b.Contents == nil { var contents T marshal(buf, reflect.ValueOf(&contents)) } else { marshal(buf, reflect.ValueOf(b.Contents)) } } // unmarshal implements the Unmarshallable interface. func (b *boxed[T]) unmarshal(buf *bytes.Buffer) error { b.Contents = new(T) return unmarshal(buf, reflect.ValueOf(b.Contents)) } go-tpm-0.9.3/tpm2/marshalling_test.go000066400000000000000000000077771473040456300175270ustar00rootroot00000000000000package tpm2 import ( "bytes" "testing" ) func TestMarshal2B(t *testing.T) { // Define some TPMT_Public pub := TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, NoDA: true, }, } // Get the wire-format version pubBytes := Marshal(pub) // Create two versions of the same 2B: // one instantiated by the actual TPMTPublic // one instantiated by the contents var boxed1 TPM2BPublic var boxed2 TPM2BPublic boxed1 = New2B(pub) boxed2 = BytesAs2B[TPMTPublic](pubBytes) boxed1Bytes := Marshal(boxed1) boxed2Bytes := Marshal(boxed2) if !bytes.Equal(boxed1Bytes, boxed2Bytes) { t.Errorf("got %x want %x", boxed2Bytes, boxed1Bytes) } z, err := Unmarshal[TPM2BPublic](boxed1Bytes) if err != nil { t.Fatalf("could not unmarshal TPM2BPublic: %v", err) } t.Logf("%v", z) boxed3Bytes := Marshal(z) if !bytes.Equal(boxed1Bytes, boxed3Bytes) { t.Errorf("got %x want %x", boxed3Bytes, boxed1Bytes) } // Make a nonsense 2B_Public, demonstrating that the library doesn't have to understand the serialization BytesAs2B[TPMTPublic]([]byte{0xff}) } func unwrap[T any](f func() (*T, error)) *T { t, err := f() if err != nil { panic(err.Error()) } return t } func TestMarshalT(t *testing.T) { // Define some TPMT_Public pub := TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), Unique: NewTPMUPublicID( // This happens to be a P256 EKpub from the simulator TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{}, Y: TPM2BECCParameter{}, }, ), } // Marshal each component of the parameters symBytes := Marshal(&unwrap(pub.Parameters.ECCDetail).Symmetric) t.Logf("Symmetric: %x\n", symBytes) sym, err := Unmarshal[TPMTSymDefObject](symBytes) if err != nil { t.Fatalf("could not unmarshal TPMTSymDefObject: %v", err) } symBytes2 := Marshal(sym) if !bytes.Equal(symBytes, symBytes2) { t.Errorf("want %x\ngot %x", symBytes, symBytes2) } schemeBytes := Marshal(&unwrap(pub.Parameters.ECCDetail).Scheme) t.Logf("Scheme: %x\n", symBytes) scheme, err := Unmarshal[TPMTECCScheme](schemeBytes) if err != nil { t.Fatalf("could not unmarshal TPMTECCScheme: %v", err) } schemeBytes2 := Marshal(scheme) if !bytes.Equal(schemeBytes, schemeBytes2) { t.Errorf("want %x\ngot %x", schemeBytes, schemeBytes2) } kdfBytes := Marshal(&unwrap(pub.Parameters.ECCDetail).KDF) t.Logf("KDF: %x\n", kdfBytes) kdf, err := Unmarshal[TPMTKDFScheme](kdfBytes) if err != nil { t.Fatalf("could not unmarshal TPMTKDFScheme: %v", err) } kdfBytes2 := Marshal(kdf) if !bytes.Equal(kdfBytes, kdfBytes2) { t.Errorf("want %x\ngot %x", kdfBytes, kdfBytes2) } // Marshal the parameters parmsBytes := Marshal(unwrap(pub.Parameters.ECCDetail)) t.Logf("Parms: %x\n", parmsBytes) parms, err := Unmarshal[TPMSECCParms](parmsBytes) if err != nil { t.Fatalf("could not unmarshal TPMSECCParms: %v", err) } parmsBytes2 := Marshal(parms) if !bytes.Equal(parmsBytes, parmsBytes2) { t.Errorf("want %x\ngot %x", parmsBytes, parmsBytes2) } // Marshal the unique area uniqueBytes := Marshal(unwrap(pub.Unique.ECC)) t.Logf("Unique: %x\n", uniqueBytes) unique, err := Unmarshal[TPMSECCPoint](uniqueBytes) if err != nil { t.Fatalf("could not unmarshal TPMSECCPoint: %v", err) } uniqueBytes2 := Marshal(unique) if !bytes.Equal(uniqueBytes, uniqueBytes2) { t.Errorf("want %x\ngot %x", uniqueBytes, uniqueBytes2) } // Get the wire-format version of the whole thing pubBytes := Marshal(&pub) pub2, err := Unmarshal[TPMTPublic](pubBytes) if err != nil { t.Fatalf("could not unmarshal TPMTPublic: %v", err) } // Some default fields might have been populated in the round-trip. Get the wire-format again and compare. pub2Bytes := Marshal(pub2) if !bytes.Equal(pubBytes, pub2Bytes) { t.Errorf("want %x\ngot %x", pubBytes, pub2Bytes) } } go-tpm-0.9.3/tpm2/names.go000066400000000000000000000030101473040456300152420ustar00rootroot00000000000000package tpm2 import ( "bytes" "encoding/binary" "reflect" ) // HandleName returns the TPM Name of a PCR, session, or permanent value // (e.g., hierarchy) handle. func HandleName(h TPMHandle) TPM2BName { result := make([]byte, 4) binary.BigEndian.PutUint32(result, uint32(h)) return TPM2BName{ Buffer: result, } } // objectOrNVName calculates the Name of an NV index or object. // pub is a pointer to either a TPMTPublic or TPMSNVPublic. func objectOrNVName(alg TPMAlgID, pub interface{}) (*TPM2BName, error) { h, err := alg.Hash() if err != nil { return nil, err } // Create a byte slice with the correct reserved size and marshal the // NameAlg to it. result := make([]byte, 2, 2+h.Size()) binary.BigEndian.PutUint16(result, uint16(alg)) // Calculate the hash of the entire Public contents and append it to the // result. ha := h.New() var buf bytes.Buffer if err := marshal(&buf, reflect.ValueOf(pub)); err != nil { return nil, err } ha.Write(buf.Bytes()) result = ha.Sum(result) return &TPM2BName{ Buffer: result, }, nil } // ObjectName returns the TPM Name of an object. func ObjectName(p *TPMTPublic) (*TPM2BName, error) { return objectOrNVName(p.NameAlg, p) } // NVName returns the TPM Name of an NV index. func NVName(p *TPMSNVPublic) (*TPM2BName, error) { return objectOrNVName(p.NameAlg, p) } // PrimaryHandleName returns the TPM Name of a primary handle. func PrimaryHandleName(h TPMHandle) []byte { result := make([]byte, 4) binary.BigEndian.PutUint32(result, uint32(h)) return result } go-tpm-0.9.3/tpm2/pcrs.go000066400000000000000000000037211473040456300151170ustar00rootroot00000000000000package tpm2 // pcrSelectionFormatter is a Platform TPM Profile-specific interface for // formatting TPM PCR selections. // This interface isn't (yet) part of the go-tpm public interface. After we // add a second implementation, we should consider making it public. type pcrSelectionFormatter interface { // PCRs returns the TPM PCR selection bitmask associated with the given PCR indices. PCRs(pcrs ...uint) []byte } // PCClientCompatible is a pcrSelectionFormatter that formats PCR selections // suitable for use in PC Client PTP-compatible TPMs (the vast majority): // https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ // PC Client mandates at least 24 PCRs but does not provide an upper limit. var PCClientCompatible pcrSelectionFormatter = pcClient{} type pcClient struct{} // The TPM requires all PCR selections to be at least big enough to select all // the PCRs in the minimum PCR allocation. const pcClientMinimumPCRCount = 24 func (pcClient) PCRs(pcrs ...uint) []byte { // Find the biggest PCR we selected. maxPCR := uint(0) for _, pcr := range pcrs { if pcr > maxPCR { maxPCR = pcr } } selectionSize := maxPCR/8 + 1 // Enforce the minimum PCR selection size. if selectionSize < (pcClientMinimumPCRCount / 8) { selectionSize = (pcClientMinimumPCRCount / 8) } // Allocate a byte array to store the bitfield, that has at least // enough bits to store our selections. selection := make([]byte, selectionSize) for _, pcr := range pcrs { // The PCR selection mask is byte-wise little-endian: // select[0] contains bits representing the selection of PCRs 0 through 7 // select[1] contains PCRs 8 through 15, and so on. byteIdx := pcr / 8 // Within the byte, the PCR selection is bit-wise big-endian: // bit 0 of select[0] contains the selection of PCR 0 // bit 1 of select[0] contains the selection of PCR 1, and so on. bitIdx := pcr % 8 selection[byteIdx] |= (1 << bitIdx) } return selection } go-tpm-0.9.3/tpm2/policy.go000066400000000000000000000026021473040456300154440ustar00rootroot00000000000000package tpm2 import ( "bytes" "crypto" "reflect" ) // PolicyCalculator represents a TPM 2.0 policy that needs to be calculated // synthetically (i.e., without a TPM). type PolicyCalculator struct { alg TPMIAlgHash hash crypto.Hash state []byte } // NewPolicyCalculator creates a fresh policy using the given hash algorithm. func NewPolicyCalculator(alg TPMIAlgHash) (*PolicyCalculator, error) { hash, err := alg.Hash() if err != nil { return nil, err } return &PolicyCalculator{ alg: alg, hash: hash, state: make([]byte, hash.Size()), }, nil } // Reset resets the internal state of the policy hash to all 0x00. func (p *PolicyCalculator) Reset() { p.state = make([]byte, p.hash.Size()) } // Update updates the internal state of the policy hash by appending the // current state with the given contents, and updating the new state to the // hash of that. func (p *PolicyCalculator) Update(data ...interface{}) error { hash := p.hash.New() hash.Write(p.state) var buf bytes.Buffer for _, d := range data { if err := marshal(&buf, reflect.ValueOf(d)); err != nil { return err } } hash.Write(buf.Bytes()) p.state = hash.Sum(nil) return nil } // Hash returns the current state of the policy hash. func (p *PolicyCalculator) Hash() *TPMTHA { result := TPMTHA{ HashAlg: p.alg, Digest: make([]byte, len(p.state)), } copy(result.Digest, p.state) return &result } go-tpm-0.9.3/tpm2/reflect.go000066400000000000000000001064141473040456300155770ustar00rootroot00000000000000// Package tpm2 provides 1:1 mapping to TPM 2.0 APIs. package tpm2 import ( "bytes" "encoding/binary" "fmt" "math" "reflect" "strconv" "strings" "github.com/google/go-tpm/tpm2/transport" ) const ( // Chosen based on MAX_DIGEST_BUFFER, the length of the longest // reasonable list returned by the reference implementation. // The maxListLength must be greater than MAX_CONTEXT_SIZE = 1344, // in order to allow for the unmarshalling of Context. maxListLength uint32 = 4096 ) // execute sends the provided command and returns the TPM's response. func execute[R any](t transport.TPM, cmd Command[R, *R], rsp *R, extraSess ...Session) error { cc := cmd.Command() sess, err := cmdAuths(cmd) if err != nil { return err } sess = append(sess, extraSess...) if len(sess) > 3 { return fmt.Errorf("too many sessions: %v", len(sess)) } hasSessions := len(sess) > 0 // Initialize the sessions, if needed for i, s := range sess { if err := s.Init(t); err != nil { return fmt.Errorf("initializing session %d: %w", i, err) } if err := s.NewNonceCaller(); err != nil { return err } } handles, err := cmdHandles(cmd) if err != nil { return err } parms, err := cmdParameters(cmd, sess) if err != nil { return err } var names []TPM2BName var sessions []byte if hasSessions { var err error names, err = cmdNames(cmd) if err != nil { return err } sessions, err = cmdSessions(sess, cc, names, parms) if err != nil { return err } } hdr := cmdHeader(hasSessions, 10 /* size of command header */ +len(handles)+len(sessions)+len(parms), cc) command := append(hdr, handles...) command = append(command, sessions...) command = append(command, parms...) // Send the command via the transport. response, err := t.Send(command) if err != nil { return err } // Parse the command tpm2ly into the response structure. rspBuf := bytes.NewBuffer(response) err = rspHeader(rspBuf) if err != nil { var bonusErrs []string // Emergency cleanup, then return. for _, s := range sess { if err := s.CleanupFailure(t); err != nil { bonusErrs = append(bonusErrs, err.Error()) } } if len(bonusErrs) != 0 { return fmt.Errorf("%w - additional errors encountered during cleanup: %v", err, strings.Join(bonusErrs, ", ")) } return err } err = rspHandles(rspBuf, rsp) if err != nil { return err } rspParms, err := rspParametersArea(hasSessions, rspBuf) if err != nil { return err } if hasSessions { // We don't need the TPM RC here because we would have errored // out from rspHeader // TODO: Authenticate the error code with sessions, if desired. err = rspSessions(rspBuf, TPMRCSuccess, cc, names, rspParms, sess) if err != nil { return err } } err = rspParameters(rspParms, sess, rsp) if err != nil { return err } return nil } func isMarshalledByReflection(v reflect.Value) bool { var mbr marshallableByReflection if v.Type().AssignableTo(reflect.TypeOf(&mbr).Elem()) { return true } // basic types are also marshalled by reflection, as are empty structs switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Array, reflect.Slice, reflect.Ptr: return true case reflect.Struct: if v.NumField() == 0 { return true } } return false } // marshal will serialize the given value, appending onto the given buffer. // Returns an error if the value is not marshallable. func marshal(buf *bytes.Buffer, v reflect.Value) error { // If the type is not marshalled by reflection, try to call the custom marshal method. if !isMarshalledByReflection(v) { u, ok := v.Interface().(Marshallable) if ok { u.marshal(buf) return nil } if v.CanAddr() { // Maybe we got an addressable value whose pointer implements Marshallable pu, ok := v.Addr().Interface().(Marshallable) if ok { pu.marshal(buf) return nil } } return fmt.Errorf("can't marshal: type %v does not implement Marshallable or marshallableByReflection", v.Type().Name()) } // Otherwise, use reflection. switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return marshalNumeric(buf, v) case reflect.Array, reflect.Slice: return marshalArray(buf, v) case reflect.Struct: return marshalStruct(buf, v) case reflect.Ptr: return marshal(buf, v.Elem()) case reflect.Interface: // Special case: there are very few TPM types which, for TPM spec // backwards-compatibility reasons, are implemented as Go interfaces // so that callers can ergonomically satisfy cases where the TPM spec // allows a parameter to literally be one of a couple of types. // In a few of these cases, we want the caller to be able to sensibly // omit the data, and fill in reasonable defaults. // These cases are enumerated here. if v.IsNil() { switch v.Type().Name() { case "TPMUSensitiveCreate": return marshal(buf, reflect.ValueOf(TPM2BSensitiveData{})) default: return fmt.Errorf("missing required value for %v interface", v.Type().Name()) } } return marshal(buf, v.Elem()) default: return fmt.Errorf("not marshallable: %#v", v) } } // marshalOptional will serialize the given value, appending onto the given // buffer. // Special case: Part 3 specifies some input/output // parameters as "optional", which means that they are // sized fields that can be zero-length, even if the // enclosed type has no legal empty serialization. // When nil, marshal the zero size. // Returns an error if the value is not marshallable. func marshalOptional(buf *bytes.Buffer, v reflect.Value) error { if v.Kind() == reflect.Ptr && v.IsNil() { return marshalArray(buf, reflect.ValueOf([2]byte{})) } return marshal(buf, v) } func marshalNumeric(buf *bytes.Buffer, v reflect.Value) error { return binary.Write(buf, binary.BigEndian, v.Interface()) } func marshalArray(buf *bytes.Buffer, v reflect.Value) error { for i := 0; i < v.Len(); i++ { if err := marshal(buf, v.Index(i)); err != nil { return fmt.Errorf("marshalling element %d of %v: %v", i, v.Type(), err) } } return nil } // Marshals the members of the struct, handling sized and bitwise fields. func marshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Check if this is a bitwise-defined structure. This requires all the // members to be bitwise-defined. numBitwise := 0 numChecked := 0 for i := 0; i < v.NumField(); i++ { // Ignore embedded Bitfield hints. if !v.Type().Field(i).IsExported() { //if _, isBitfield := v.Field(i).Interface().(TPMABitfield); isBitfield { continue } thisBitwise := hasTag(v.Type().Field(i), "bit") if thisBitwise { numBitwise++ if hasTag(v.Type().Field(i), "sized") || hasTag(v.Type().Field(i), "sized8") { return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", v.Type().Name(), v.Type().Field(i).Name) } if hasTag(v.Type().Field(i), "tag") { return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", v.Type().Name(), v.Type().Field(i).Name) } } numChecked++ } if numBitwise != numChecked && numBitwise != 0 { return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) } if numBitwise > 0 { return marshalBitwise(buf, v) } // Make a pass to create a map of tag values // UInt64-valued fields with values greater than MaxInt64 cannot be // selectors. possibleSelectors := make(map[string]int64) for i := 0; i < v.NumField(); i++ { // Special case: Treat a zero-valued nullable field as // TPMAlgNull for union selection. // This allows callers to omit uninteresting scheme structures. if v.Field(i).IsZero() && hasTag(v.Type().Field(i), "nullable") { possibleSelectors[v.Type().Field(i).Name] = int64(TPMAlgNull) continue } switch v.Field(i).Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: possibleSelectors[v.Type().Field(i).Name] = v.Field(i).Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: val := v.Field(i).Uint() if val <= math.MaxInt64 { possibleSelectors[v.Type().Field(i).Name] = int64(val) } } } for i := 0; i < v.NumField(); i++ { if hasTag(v.Type().Field(i), "skip") { continue } list := hasTag(v.Type().Field(i), "list") sized := hasTag(v.Type().Field(i), "sized") sized8 := hasTag(v.Type().Field(i), "sized8") tag, _ := tag(v.Type().Field(i), "tag") // Serialize to a temporary buffer, in case we need to size it // (Better to simplify this complex reflection-based marshalling // code than to save some unnecessary copying before talking to // a low-speed device like a TPM) var res bytes.Buffer if list { binary.Write(&res, binary.BigEndian, uint32(v.Field(i).Len())) } if tag != "" { // Check that the tagged value was present (and numeric // and smaller than MaxInt64) tagValue, ok := possibleSelectors[tag] // Don't marshal anything if the tag value was TPM_ALG_NULL if tagValue == int64(TPMAlgNull) { continue } if !ok { return fmt.Errorf("union tag '%v' for member '%v' of struct '%v' did not reference "+ "a numeric field of int64-compatible value", tag, v.Type().Field(i).Name, v.Type().Name()) } if u, ok := v.Field(i).Interface().(marshallableWithHint); ok { v, err := u.get(tagValue) if err != nil { return err } if err := marshal(buf, v); err != nil { return err } } } else if v.Field(i).IsZero() && v.Field(i).Kind() == reflect.Uint32 && hasTag(v.Type().Field(i), "nullable") { // Special case: Anything with the same underlying type // as TPMHandle's zero value is TPM_RH_NULL. // This allows callers to omit uninteresting handles // instead of specifying them as TPM_RH_NULL. if err := binary.Write(&res, binary.BigEndian, uint32(TPMRHNull)); err != nil { return err } } else if v.Field(i).IsZero() && v.Field(i).Kind() == reflect.Uint16 && hasTag(v.Type().Field(i), "nullable") { // Special case: Anything with the same underlying type // as TPMAlg's zero value is TPM_ALG_NULL. // This allows callers to omit uninteresting // algorithms/schemes instead of specifying them as // TPM_ALG_NULL. if err := binary.Write(&res, binary.BigEndian, uint16(TPMAlgNull)); err != nil { return err } } else if hasTag(v.Type().Field(i), "optional") { if err := marshalOptional(&res, v.Field(i)); err != nil { return err } } else { if err := marshal(&res, v.Field(i)); err != nil { return err } } if sized { if err := binary.Write(buf, binary.BigEndian, uint16(res.Len())); err != nil { return err } } if sized8 { if err := binary.Write(buf, binary.BigEndian, uint8(res.Len())); err != nil { return err } } buf.Write(res.Bytes()) } return nil } // Marshals a bitwise-defined struct. func marshalBitwise(buf *bytes.Buffer, v reflect.Value) error { bg, ok := v.Interface().(BitGetter) if !ok { return fmt.Errorf("'%v' was not a BitGetter", v.Type().Name()) } bitArray := make([]bool, bg.Length()) // Marshal the defined fields for i := 0; i < v.NumField(); i++ { if !v.Type().Field(i).IsExported() { continue } high, low, _ := rangeTag(v.Type().Field(i), "bit") var buf bytes.Buffer if err := marshal(&buf, v.Field(i)); err != nil { return err } b := buf.Bytes() for i := 0; i <= (high - low); i++ { bitArray[low+i] = ((b[len(b)-i/8-1] >> (i % 8)) & 1) == 1 } } // Also marshal the reserved values for i := 0; i < len(bitArray); i++ { if bg.GetReservedBit(i) { bitArray[i] = true } } result := make([]byte, len(bitArray)/8) for i, bit := range bitArray { if bit { result[len(result)-(i/8)-1] |= (1 << (i % 8)) } } buf.Write(result) return nil } // unmarshal will deserialize the given value from the given buffer. // Returns an error if the buffer does not contain enough data to satisfy the // type. func unmarshal(buf *bytes.Buffer, v reflect.Value) error { // If the type is not marshalled by reflection, try to call the custom unmarshal method. if !isMarshalledByReflection(v) { if u, ok := v.Addr().Interface().(Unmarshallable); ok { return u.unmarshal(buf) } return fmt.Errorf("can't unmarshal: type %v does not implement Unmarshallable or marshallableByReflection", v.Type().Name()) } switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if err := unmarshalNumeric(buf, v); err != nil { return err } case reflect.Slice: var length uint32 // special case for byte slices: just read the entire // rest of the buffer if v.Type().Elem().Kind() == reflect.Uint8 { length = uint32(buf.Len()) } else { err := unmarshalNumeric(buf, reflect.ValueOf(&length).Elem()) if err != nil { return fmt.Errorf("deserializing size for field of type '%v': %w", v.Type(), err) } } if length > uint32(math.MaxInt32) || length > maxListLength { return fmt.Errorf("could not deserialize slice of length %v", length) } // Go's reflect library doesn't allow increasing the // capacity of an existing slice. // Since we can't be sure that the capacity of the // passed-in value was enough, allocate // a new temporary one of the correct length, unmarshal // to it, and swap it in. tmp := reflect.MakeSlice(v.Type(), int(length), int(length)) if err := unmarshalArray(buf, tmp); err != nil { return err } v.Set(tmp) return nil case reflect.Array: return unmarshalArray(buf, v) case reflect.Struct: return unmarshalStruct(buf, v) case reflect.Ptr: return unmarshal(buf, v.Elem()) default: return fmt.Errorf("not unmarshallable: %v", v.Type()) } return nil } func unmarshalNumeric(buf *bytes.Buffer, v reflect.Value) error { return binary.Read(buf, binary.BigEndian, v.Addr().Interface()) } // For slices, the slice's length must already be set to the expected amount of // data. func unmarshalArray(buf *bytes.Buffer, v reflect.Value) error { for i := 0; i < v.Len(); i++ { if err := unmarshal(buf, v.Index(i)); err != nil { return fmt.Errorf("deserializing slice/array index %v: %w", i, err) } } return nil } func unmarshalStruct(buf *bytes.Buffer, v reflect.Value) error { // Check if this is a bitwise-defined structure. This requires all the // exported members to be bitwise-defined. numBitwise := 0 numChecked := 0 for i := 0; i < v.NumField(); i++ { // Ignore embedded Bitfield hints. // Ignore embedded Bitfield hints. if !v.Type().Field(i).IsExported() { //if _, isBitfield := v.Field(i).Interface().(TPMABitfield); isBitfield { continue } thisBitwise := hasTag(v.Type().Field(i), "bit") if thisBitwise { numBitwise++ if hasTag(v.Type().Field(i), "sized") { return fmt.Errorf("struct '%v' field '%v' is both bitwise and sized", v.Type().Name(), v.Type().Field(i).Name) } if hasTag(v.Type().Field(i), "tag") { return fmt.Errorf("struct '%v' field '%v' is both bitwise and a tagged union", v.Type().Name(), v.Type().Field(i).Name) } } numChecked++ } if numBitwise != numChecked && numBitwise != 0 { return fmt.Errorf("struct '%v' has mixture of bitwise and non-bitwise members", v.Type().Name()) } if numBitwise > 0 { return unmarshalBitwise(buf, v) } for i := 0; i < v.NumField(); i++ { if hasTag(v.Type().Field(i), "skip") { continue } list := hasTag(v.Type().Field(i), "list") if list && (v.Field(i).Kind() != reflect.Slice) { return fmt.Errorf("field '%v' of struct '%v' had the 'list' tag but was not a slice", v.Type().Field(i).Name, v.Type().Name()) } // Slices of anything but byte/uint8 must have the 'list' tag. if !list && (v.Field(i).Kind() == reflect.Slice) && (v.Type().Field(i).Type.Elem().Kind() != reflect.Uint8) { return fmt.Errorf("field '%v' of struct '%v' was a slice of non-byte but did not have the 'list' tag", v.Type().Field(i).Name, v.Type().Name()) } if hasTag(v.Type().Field(i), "optional") { // Special case: Part 3 specifies some input/output // parameters as "optional", which means that they are // (2B-) sized fields that can be zero-length, even if the // enclosed type has no legal empty serialization. // When unmarshalling an optional field, test for zero size // and skip if empty. if buf.Len() < 2 { if binary.BigEndian.Uint16(buf.Bytes()) == 0 { // Advance the buffer past the zero size and skip to the // next field of the struct. buf.Next(2) continue } // If non-zero size, proceed to unmarshal the contents below. } } sized := hasTag(v.Type().Field(i), "sized") sized8 := hasTag(v.Type().Field(i), "sized8") // If sized, unmarshal a size field first, then restrict // unmarshalling to the given size bufToReadFrom := buf if sized { var expectedSize uint16 binary.Read(buf, binary.BigEndian, &expectedSize) sizedBufArray := make([]byte, int(expectedSize)) n, err := buf.Read(sizedBufArray) if n != int(expectedSize) { return fmt.Errorf("ran out of data reading sized parameter '%v' inside struct of type '%v'", v.Type().Field(i).Name, v.Type().Name()) } if err != nil { return fmt.Errorf("error reading data for parameter '%v' inside struct of type '%v'", v.Type().Field(i).Name, v.Type().Name()) } bufToReadFrom = bytes.NewBuffer(sizedBufArray) } if sized8 { var expectedSize uint8 binary.Read(buf, binary.BigEndian, &expectedSize) sizedBufArray := make([]byte, int(expectedSize)) n, err := buf.Read(sizedBufArray) if n != int(expectedSize) { return fmt.Errorf("ran out of data reading sized parameter '%v' inside struct of type '%v'", v.Type().Field(i).Name, v.Type().Name()) } if err != nil { return fmt.Errorf("error reading data for parameter '%v' inside struct of type '%v'", v.Type().Field(i).Name, v.Type().Name()) } bufToReadFrom = bytes.NewBuffer(sizedBufArray) } tag, _ := tag(v.Type().Field(i), "tag") if tag != "" { // Make a pass to create a map of tag values // UInt64-valued fields with values greater than // MaxInt64 cannot be selectors. possibleSelectors := make(map[string]int64) for j := 0; j < v.NumField(); j++ { switch v.Field(j).Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: possibleSelectors[v.Type().Field(j).Name] = v.Field(j).Int() case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: val := v.Field(j).Uint() if val <= math.MaxInt64 { possibleSelectors[v.Type().Field(j).Name] = int64(val) } } } // Check that the tagged value was present (and numeric // and smaller than MaxInt64) tagValue, ok := possibleSelectors[tag] // Don't marshal anything if the tag value was TPM_ALG_NULL if tagValue == int64(TPMAlgNull) { continue } if !ok { return fmt.Errorf("union tag '%v' for member '%v' of struct '%v' did not reference "+ "a numeric field of in64-compatible value", tag, v.Type().Field(i).Name, v.Type().Name()) } var uwh unmarshallableWithHint if v.Field(i).CanAddr() && v.Field(i).Addr().Type().AssignableTo(reflect.TypeOf(&uwh).Elem()) { u := v.Field(i).Addr().Interface().(unmarshallableWithHint) contents, err := u.create(tagValue) if err != nil { return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) } err = unmarshal(buf, contents) if err != nil { return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) } } else if v.Field(i).Type().AssignableTo(reflect.TypeOf(&uwh).Elem()) { u := v.Field(i).Interface().(unmarshallableWithHint) contents, err := u.create(tagValue) if err != nil { return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) } err = unmarshal(buf, contents) if err != nil { return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) } } } else { if err := unmarshal(bufToReadFrom, v.Field(i)); err != nil { return fmt.Errorf("unmarshalling field %v of struct of type '%v', %w", i, v.Type(), err) } } if sized || sized8 { if bufToReadFrom.Len() != 0 { return fmt.Errorf("extra data at the end of sized parameter '%v' inside struct of type '%v'", v.Type().Field(i).Name, v.Type().Name()) } } } return nil } // Unmarshals a bitwise-defined struct. func unmarshalBitwise(buf *bytes.Buffer, v reflect.Value) error { bs, ok := v.Addr().Interface().(BitSetter) if !ok { return fmt.Errorf("'%v' was not a BitSetter", v.Addr().Type()) } bitArray := make([]bool, bs.Length()) // We will read big-endian, starting from the last byte and working our // way down. for i := len(bitArray)/8 - 1; i >= 0; i-- { b, err := buf.ReadByte() if err != nil { return fmt.Errorf("error %d bits into field '%v' of struct '%v': %w", i, v.Type().Field(i).Name, v.Type().Name(), err) } for j := 0; j < 8; j++ { bitArray[8*i+j] = (((b >> j) & 1) == 1) } } // Unmarshal the defined fields and clear the bits from the array as we // read them. for i := 0; i < v.NumField(); i++ { if !v.Type().Field(i).IsExported() { continue } high, low, _ := rangeTag(v.Type().Field(i), "bit") var val uint64 for j := 0; j <= high-low; j++ { if bitArray[low+j] { val |= (1 << j) } bitArray[low+j] = false } if v.Field(i).Kind() == reflect.Bool { v.Field(i).SetBool((val & 1) == 1) } else { v.Field(i).SetUint(val) } } // Unmarshal the remaining uncleared bits as reserved bits. for i := 0; i < len(bitArray); i++ { bs.SetReservedBit(i, bitArray[i]) } return nil } // Looks up the given gotpm tag on a field. // Some tags are settable (with "="). For these, the value is the RHS. // For all others, the value is the empty string. func tag(t reflect.StructField, query string) (string, bool) { allTags, ok := t.Tag.Lookup("gotpm") if !ok { return "", false } tags := strings.Split(allTags, ",") for _, tag := range tags { // Split on the equals sign for settable tags. // If the split returns a slice of length 1, this is an // un-settable tag or an empty tag (which we'll ignore). // If the split returns a slice of length 2, this is a settable // tag. if tag == query { return "", true } if strings.HasPrefix(tag, query+"=") { assignment := strings.SplitN(tag, "=", 2) return assignment[1], true } } return "", false } // hasTag looks up to see if the type's gotpm-namespaced tag contains the // given value. // Returns false if there is no gotpm-namespaced tag on the type. func hasTag(t reflect.StructField, query string) bool { _, ok := tag(t, query) return ok } // Returns the range on a tag like 4:3 or 4. // If there is no colon, the low and high part of the range are equal. func rangeTag(t reflect.StructField, query string) (int, int, bool) { val, ok := tag(t, query) if !ok { return 0, 0, false } vals := strings.Split(val, ":") high, err := strconv.Atoi(vals[0]) if err != nil { return 0, 0, false } low := high if len(vals) > 1 { low, err = strconv.Atoi(vals[1]) if err != nil { return 0, 0, false } } if low > high { low, high = high, low } return high, low, true } // taggedMembers will return a slice of all the members of the given // structure that contain (or don't contain) the given tag in the "gotpm" // namespace. // Panics if v's Kind is not Struct. func taggedMembers(v reflect.Value, tag string, invert bool) []reflect.Value { var result []reflect.Value t := v.Type() for i := 0; i < t.NumField(); i++ { // Add this one to the list if it has the tag and we're not // inverting, or if it doesn't have the tag and we are // inverting. if hasTag(t.Field(i), tag) != invert { result = append(result, v.Field(i)) } } return result } // cmdAuths returns the authorization sessions of the command. func cmdAuths[R any](cmd Command[R, *R]) ([]Session, error) { authHandles := taggedMembers(reflect.ValueOf(cmd), "auth", false) var result []Session for i, authHandle := range authHandles { // TODO: A cleaner way to do this would be to have an interface method that // returns a Session. if h, ok := authHandle.Interface().(AuthHandle); ok { if h.Auth == nil { return nil, fmt.Errorf("missing auth for '%v' parameter", reflect.ValueOf(cmd).Type().Field(i).Name) } result = append(result, h.Auth) } else { result = append(result, PasswordAuth(nil)) } } return result, nil } func asHandle(value reflect.Value) (handle, error) { // Special case: `handle`-typed members. // Since `handle` is an interface, the zero-value is nil. // https://go.dev/ref/spec#Type_assertions in this case will return false. // Similarly, reflect.AssignableTo() will panic. // Workaround: treat any nil interface value annotated as a `handle` as TPMRHNull. var h handle if value.Kind() == reflect.Interface && value.IsNil() { h = TPMRHNull } else { var ok bool h, ok = value.Interface().(handle) if !ok { return nil, fmt.Errorf("value of type %q does not satisfy handle", value.Type()) } } return h, nil } // cmdHandles returns the handles area of the command. func cmdHandles[R any](cmd Command[R, *R]) ([]byte, error) { handles := taggedMembers(reflect.ValueOf(cmd), "handle", false) // Initial capacity is enough to hold 3 handles result := bytes.NewBuffer(make([]byte, 0, 12)) for _, maybeHandle := range handles { h, err := asHandle(maybeHandle) if err != nil { return nil, fmt.Errorf("invalid 'handle'-tagged member of %q: %v", reflect.TypeOf(cmd), err) } binary.Write(result, binary.BigEndian, h.HandleValue()) } return result.Bytes(), nil } // cmdNames returns the names of the entities referenced by the handles of the command. func cmdNames[R any](cmd Command[R, *R]) ([]TPM2BName, error) { handles := taggedMembers(reflect.ValueOf(cmd), "handle", false) var result []TPM2BName for i, maybeHandle := range handles { h, err := asHandle(maybeHandle) if err != nil { return nil, fmt.Errorf("invalid 'handle'-tagged member of %q: %v", reflect.TypeOf(cmd), err) } // Special case: handles with an empty name buffer (anonymous:anon) // See part 1: Architecture, section 32.4.5: // The Name of a sequence object is an Empty Buffer (sized array with no // data; indicated by a size field of zero followed by an array // containing no elements) if hasTag(reflect.ValueOf(cmd).Type().Field(i), "anon") { continue } name := h.KnownName() if name == nil { return nil, fmt.Errorf("missing Name for '%v' parameter", reflect.ValueOf(cmd).Type().Field(i).Name) } result = append(result, *name) } return result, nil } // TODO: Extract the logic of "marshal the Nth field of some struct after the handles" // For now, we duplicate some logic from marshalStruct here. func marshalParameter[R any](buf *bytes.Buffer, cmd Command[R, *R], i int) error { numHandles := len(taggedMembers(reflect.ValueOf(cmd), "handle", false)) if numHandles+i >= reflect.TypeOf(cmd).NumField() { return fmt.Errorf("invalid parameter index %v", i) } parm := reflect.ValueOf(cmd).Field(numHandles + i) field := reflect.TypeOf(cmd).Field(numHandles + i) if hasTag(field, "optional") { return marshalOptional(buf, parm) } else if parm.IsZero() && parm.Kind() == reflect.Uint32 && hasTag(field, "nullable") { return marshal(buf, reflect.ValueOf(TPMRHNull)) } else if parm.IsZero() && parm.Kind() == reflect.Uint16 && hasTag(field, "nullable") { return marshal(buf, reflect.ValueOf(TPMAlgNull)) } return marshal(buf, parm) } // cmdParameters returns the parameters area of the command. // The first parameter may be encrypted by one of the sessions. func cmdParameters[R any](cmd Command[R, *R], sess []Session) ([]byte, error) { parms := taggedMembers(reflect.ValueOf(cmd), "handle", true) if len(parms) == 0 { return nil, nil } var firstParm bytes.Buffer if err := marshalParameter(&firstParm, cmd, 0); err != nil { return nil, err } firstParmBytes := firstParm.Bytes() // Encrypt the first parameter if there are any decryption sessions. encrypted := false for i, s := range sess { if s.IsDecryption() { if encrypted { // Only one session may be used for decryption. return nil, fmt.Errorf("too many decrypt sessions") } if len(firstParmBytes) < 2 { return nil, fmt.Errorf("this command's first parameter is not a tpm2b") } err := s.Encrypt(firstParmBytes[2:]) if err != nil { return nil, fmt.Errorf("encrypting with session %d: %w", i, err) } encrypted = true } } var result bytes.Buffer result.Write(firstParmBytes) // Write the rest of the parameters normally. for i := 1; i < len(parms); i++ { if err := marshalParameter(&result, cmd, i); err != nil { return nil, err } } return result.Bytes(), nil } // cmdSessions returns the authorization area of the command. func cmdSessions(sess []Session, cc TPMCC, names []TPM2BName, parms []byte) ([]byte, error) { // There is no authorization area if there are no sessions. if len(sess) == 0 { return nil, nil } // Find the non-first-session encryption and decryption session // nonceTPMs, if any. var encNonceTPM, decNonceTPM []byte if len(sess) > 0 { for i := 1; i < len(sess); i++ { s := sess[i] if s.IsEncryption() { if encNonceTPM != nil { // Only one encrypt session is permitted. return nil, fmt.Errorf("too many encrypt sessions") } encNonceTPM = s.NonceTPM().Buffer // A session used for both encryption and // decryption only needs its nonce counted once. continue } if s.IsDecryption() { if decNonceTPM != nil { // Only one decrypt session is permitted. return nil, fmt.Errorf("too many decrypt sessions") } decNonceTPM = s.NonceTPM().Buffer } } } buf := bytes.NewBuffer(make([]byte, 0, 1024)) // Skip space to write the size later buf.Write(make([]byte, 4)) // Calculate the authorization HMAC for each session for i, s := range sess { var addNonces []byte // Special case: the HMAC on the first authorization session of // a command also includes any decryption and encryption // nonceTPMs, too. if i == 0 { addNonces = append(addNonces, decNonceTPM...) addNonces = append(addNonces, encNonceTPM...) } auth, err := s.Authorize(cc, parms, addNonces, names, i) if err != nil { return nil, fmt.Errorf("session %d: %w", i, err) } marshal(buf, reflect.ValueOf(auth).Elem()) } result := buf.Bytes() // Write the size binary.BigEndian.PutUint32(result[0:], uint32(buf.Len()-4)) return result, nil } // cmdHeader returns the structured TPM command header. func cmdHeader(hasSessions bool, length int, cc TPMCC) []byte { tag := TPMSTNoSessions if hasSessions { tag = TPMSTSessions } hdr := TPMCmdHeader{ Tag: tag, Length: uint32(length), CommandCode: cc, } buf := bytes.NewBuffer(make([]byte, 0, 8)) marshal(buf, reflect.ValueOf(hdr)) return buf.Bytes() } // rspHeader parses the response header. If the TPM returned an error, // returns an error here. // rsp is updated to point to the rest of the response after the header. func rspHeader(rsp *bytes.Buffer) error { var hdr TPMRspHeader if err := unmarshal(rsp, reflect.ValueOf(&hdr).Elem()); err != nil { return fmt.Errorf("unmarshalling TPM response: %w", err) } if hdr.ResponseCode != TPMRCSuccess { return hdr.ResponseCode } return nil } // rspHandles parses the response handles area into the response structure. // If there is a mismatch between the expected and actual amount of handles, // returns an error here. // rsp is updated to point to the rest of the response after the handles. func rspHandles(rsp *bytes.Buffer, rspStruct any) error { handles := taggedMembers(reflect.ValueOf(rspStruct).Elem(), "handle", false) for i, handle := range handles { if err := unmarshal(rsp, handle); err != nil { return fmt.Errorf("unmarshalling handle %v: %w", i, err) } } return nil } // rspParametersArea fetches, but does not manipulate, the parameters area // from the response. If there is a mismatch between the response's // indicated parameters area size and the actual size, returns an error here. // rsp is updated to point to the rest of the response after the handles. func rspParametersArea(hasSessions bool, rsp *bytes.Buffer) ([]byte, error) { var length uint32 if hasSessions { if err := binary.Read(rsp, binary.BigEndian, &length); err != nil { return nil, fmt.Errorf("reading length of parameter area: %w", err) } } else { // If there are no sessions, there is no length-of-parameters // field, because the whole rest of the response is the // parameters area. length = uint32(rsp.Len()) } if length > uint32(rsp.Len()) { return nil, fmt.Errorf("response indicated %d bytes of parameters but there "+ "were only %d more bytes of response", length, rsp.Len()) } if length > math.MaxInt32 { return nil, fmt.Errorf("invalid length of parameter area: %d", length) } parms := make([]byte, int(length)) if n, err := rsp.Read(parms); err != nil { return nil, fmt.Errorf("reading parameter area: %w", err) } else if n != len(parms) { return nil, fmt.Errorf("only read %d bytes of parameters, expected %d", n, len(parms)) } return parms, nil } // rspSessions fetches the sessions area of the response and updates all // the sessions with it. If there is a response validation error, returns // an error here. // rsp is updated to point to the rest of the response after the sessions. func rspSessions(rsp *bytes.Buffer, rc TPMRC, cc TPMCC, names []TPM2BName, parms []byte, sess []Session) error { for i, s := range sess { var auth TPMSAuthResponse if err := unmarshal(rsp, reflect.ValueOf(&auth).Elem()); err != nil { return fmt.Errorf("reading auth session %d: %w", i, err) } if err := s.Validate(rc, cc, parms, names, i, &auth); err != nil { return fmt.Errorf("validating auth session %d: %w", i, err) } } if rsp.Len() != 0 { return fmt.Errorf("%d unaccounted-for bytes at the end of the TPM response", rsp.Len()) } return nil } // rspParameters decrypts (if needed) the parameters area of the response // into the response structure. If there is a mismatch between the expected // and actual response structure, returns an error here. func rspParameters(parms []byte, sess []Session, rspStruct any) error { numHandles := len(taggedMembers(reflect.ValueOf(rspStruct).Elem(), "handle", false)) // Use the heuristic of "does interpreting the first 2 bytes of response // as a length make any sense" to attempt encrypted parameter // decryption. // If the command supports parameter encryption, the first parameter is // a 2B. if len(parms) < 2 { return nil } length := binary.BigEndian.Uint16(parms[0:]) // TODO: Make this nice using structure tagging. if int(length)+2 <= len(parms) { for i, s := range sess { if !s.IsEncryption() { continue } if err := s.Decrypt(parms[2 : 2+length]); err != nil { return fmt.Errorf("decrypting first parameter with session %d: %w", i, err) } } } buf := bytes.NewBuffer(parms) for i := numHandles; i < reflect.TypeOf(rspStruct).Elem().NumField(); i++ { parmsField := reflect.ValueOf(rspStruct).Elem().Field(i) if parmsField.Kind() == reflect.Ptr && hasTag(reflect.TypeOf(rspStruct).Elem().Field(i), "optional") { if binary.BigEndian.Uint16(buf.Bytes()) == 0 { // Advance the buffer past the zero size and skip to the // next field of the struct. buf.Next(2) continue } } if err := unmarshal(buf, parmsField); err != nil { return err } } return nil } go-tpm-0.9.3/tpm2/sessions.go000066400000000000000000001006421473040456300160160ustar00rootroot00000000000000package tpm2 import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/rsa" "encoding/binary" "fmt" "github.com/google/go-tpm/tpm2/transport" ) // Session represents a session in the TPM. type Session interface { // Initializes the session, if needed. Has no effect if not needed or // already done. Some types of sessions may need to be initialized // just-in-time, e.g., to support calling patterns that help the user // securely authorize their actions without writing a lot of code. Init(tpm transport.TPM) error // Cleans up the session, if needed. // Some types of session need to be cleaned up if the command failed, // again to support calling patterns that help the user securely // authorize their actions without writing a lot of code. CleanupFailure(tpm transport.TPM) error // The last nonceTPM for this session. NonceTPM() TPM2BNonce // Updates nonceCaller to a new random value. NewNonceCaller() error // Computes the authorization HMAC for the session. // If this is the first authorization session for a command, and // there is another session (or sessions) for parameter // decryption and/or encryption, then addNonces contains the // nonceTPMs from each of them, respectively (see Part 1, 19.6.5) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) // Validates the response for the session. // Updates NonceTPM for the session. Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error // Returns true if this is an encryption session. IsEncryption() bool // Returns true if this is a decryption session. IsDecryption() bool // If this session is used for parameter decryption, encrypts the // parameter. Otherwise, does not modify the parameter. Encrypt(parameter []byte) error // If this session is used for parameter encryption, encrypts the // parameter. Otherwise, does not modify the parameter. Decrypt(parameter []byte) error // Returns the handle value of this session. Handle() TPMHandle } // CPHash calculates the TPM command parameter hash for a given Command. // N.B. Authorization sessions on handles are ignored, but names aren't. func CPHash[R any](alg TPMIAlgHash, cmd Command[R, *R]) (*TPM2BDigest, error) { cc := cmd.Command() names, err := cmdNames(cmd) if err != nil { return nil, err } parms, err := cmdParameters(cmd, nil) if err != nil { return nil, err } digest, err := cpHash(alg, cc, names, parms) if err != nil { return nil, err } return &TPM2BDigest{ Buffer: digest, }, nil } // pwSession represents a password-pseudo-session. type pwSession struct { auth []byte } // PasswordAuth assembles a password pseudo-session with the given auth value. func PasswordAuth(auth []byte) Session { return &pwSession{ auth: auth, } } // Init is not required and has no effect for a password session. func (s *pwSession) Init(_ transport.TPM) error { return nil } // Cleanup is not required and has no effect for a password session. func (s *pwSession) CleanupFailure(_ transport.TPM) error { return nil } // NonceTPM normally returns the last nonceTPM value from the session. // Since a password session is a pseudo-session with the auth value stuffed // in where the HMAC should go, this is not used. func (s *pwSession) NonceTPM() TPM2BNonce { return TPM2BNonce{} } // NewNonceCaller updates the nonceCaller for this session. // Password sessions don't have nonces. func (s *pwSession) NewNonceCaller() error { return nil } // Computes the authorization structure for the session. func (s *pwSession) Authorize(_ TPMCC, _, _ []byte, _ []TPM2BName, _ int) (*TPMSAuthCommand, error) { return &TPMSAuthCommand{ Handle: TPMRSPW, Nonce: TPM2BNonce{}, Attributes: TPMASession{}, Authorization: TPM2BData{ Buffer: s.auth, }, }, nil } // Validates the response session structure for the session. func (s *pwSession) Validate(_ TPMRC, _ TPMCC, _ []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { if len(auth.Nonce.Buffer) != 0 { return fmt.Errorf("expected empty nonce in response auth to PW session, got %x", auth.Nonce) } expectedAttrs := TPMASession{ ContinueSession: true, } if auth.Attributes != expectedAttrs { return fmt.Errorf("expected only ContinueSession in response auth to PW session, got %v", auth.Attributes) } if len(auth.Authorization.Buffer) != 0 { return fmt.Errorf("expected empty HMAC in response auth to PW session, got %x", auth.Authorization) } return nil } // IsEncryption returns true if this is an encryption session. // Password sessions can't be used for encryption. func (s *pwSession) IsEncryption() bool { return false } // IsDecryption returns true if this is a decryption session. // Password sessions can't be used for decryption. func (s *pwSession) IsDecryption() bool { return false } // If this session is used for parameter decryption, encrypts the // parameter. Otherwise, does not modify the parameter. // Password sessions can't be used for decryption. func (s *pwSession) Encrypt(_ []byte) error { return nil } // If this session is used for parameter encryption, encrypts the // parameter. Otherwise, does not modify the parameter. // Password sessions can't be used for encryption. func (s *pwSession) Decrypt(_ []byte) error { return nil } // Handle returns the handle value associated with this session. // In the case of a password session, this is always TPM_RS_PW. func (s *pwSession) Handle() TPMHandle { return TPMRSPW } // cpHash calculates the TPM command parameter hash. // cpHash = hash(CC || names || parms) func cpHash(alg TPMIAlgHash, cc TPMCC, names []TPM2BName, parms []byte) ([]byte, error) { ha, err := alg.Hash() if err != nil { return nil, err } h := ha.New() binary.Write(h, binary.BigEndian, cc) for _, name := range names { h.Write(name.Buffer) } h.Write(parms) return h.Sum(nil), nil } // rpHash calculates the TPM response parameter hash. // rpHash = hash(RC || CC || parms) func rpHash(alg TPMIAlgHash, rc TPMRC, cc TPMCC, parms []byte) ([]byte, error) { ha, err := alg.Hash() if err != nil { return nil, err } h := ha.New() binary.Write(h, binary.BigEndian, rc) binary.Write(h, binary.BigEndian, cc) h.Write(parms) return h.Sum(nil), nil } // sessionOptions represents extra options used when setting up an HMAC or policy session. type sessionOptions struct { auth []byte password bool bindHandle TPMIDHEntity bindName TPM2BName bindAuth []byte saltHandle TPMIDHObject saltPub TPMTPublic attrs TPMASession symmetric TPMTSymDef trialPolicy bool } // defaultOptions represents the default options used when none are provided. func defaultOptions() sessionOptions { return sessionOptions{ symmetric: TPMTSymDef{ Algorithm: TPMAlgNull, }, bindHandle: TPMRHNull, saltHandle: TPMRHNull, } } // AuthOption is an option for setting up an auth session variadically. type AuthOption func(*sessionOptions) // Auth uses the session to prove knowledge of the object's auth value. func Auth(auth []byte) AuthOption { return func(o *sessionOptions) { o.auth = auth } } // Password is a policy-session-only option that specifies to provide the // object's auth value in place of the authorization HMAC when authorizing. // For HMAC sessions, has the same effect as using Auth. // Deprecated: This is not recommended and is only provided for completeness; // use Auth instead. func Password(auth []byte) AuthOption { return func(o *sessionOptions) { o.auth = auth o.password = true } } // Bound specifies that this session's session key should depend on the auth // value of the given object. func Bound(handle TPMIDHEntity, name TPM2BName, auth []byte) AuthOption { return func(o *sessionOptions) { o.bindHandle = handle o.bindName = name o.bindAuth = auth } } // Salted specifies that this session's session key should depend on an // encrypted seed value using the given public key. // 'handle' must refer to a loaded RSA or ECC key. func Salted(handle TPMIDHObject, pub TPMTPublic) AuthOption { return func(o *sessionOptions) { o.saltHandle = handle o.saltPub = pub } } // parameterEncryptiontpm2ion specifies whether the session-encrypted // parameters are encrypted on the way into the TPM, out of the TPM, or both. type parameterEncryptiontpm2ion int const ( // EncryptIn specifies a decrypt session. EncryptIn parameterEncryptiontpm2ion = 1 + iota // EncryptOut specifies an encrypt session. EncryptOut // EncryptInOut specifies a decrypt+encrypt session. EncryptInOut ) // AESEncryption uses the session to encrypt the first parameter sent to/from // the TPM. // Note that only commands whose first command/response parameter is a 2B can // support session encryption. func AESEncryption(keySize TPMKeyBits, dir parameterEncryptiontpm2ion) AuthOption { return func(o *sessionOptions) { o.attrs.Decrypt = (dir == EncryptIn || dir == EncryptInOut) o.attrs.Encrypt = (dir == EncryptOut || dir == EncryptInOut) o.symmetric = TPMTSymDef{ Algorithm: TPMAlgAES, KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(keySize), ), Mode: NewTPMUSymMode( TPMAlgAES, TPMAlgCFB, ), } } } // Audit uses the session to compute extra HMACs. // An Audit session can be used with GetSessionAuditDigest to obtain attestation // over a sequence of commands. func Audit() AuthOption { return func(o *sessionOptions) { o.attrs.Audit = true } } // AuditExclusive is like an audit session, but even more powerful. // This allows an audit session to additionally indicate that no other auditable // commands were executed other than the ones described by the audit hash. func AuditExclusive() AuthOption { return func(o *sessionOptions) { o.attrs.Audit = true o.attrs.AuditExclusive = true } } // Trial indicates that the policy session should be in trial-mode. // This allows using the TPM to calculate policy hashes. // This option has no effect on non-Policy sessions. func Trial() AuthOption { return func(o *sessionOptions) { o.trialPolicy = true } } // hmacSession generally implements the HMAC session. type hmacSession struct { sessionOptions hash TPMIAlgHash nonceSize int handle TPMHandle sessionKey []byte // last nonceCaller nonceCaller TPM2BNonce // last nonceTPM nonceTPM TPM2BNonce } // HMAC sets up a just-in-time HMAC session that is used only once. // A real session is created, but just in time and it is flushed when used. func HMAC(hash TPMIAlgHash, nonceSize int, opts ...AuthOption) Session { // Set up a one-off session that knows the auth value. sess := hmacSession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, handle: TPMRHNull, } for _, opt := range opts { opt(&sess.sessionOptions) } return &sess } // HMACSession sets up a reusable HMAC session that needs to be closed. func HMACSession(t transport.TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := hmacSession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, handle: TPMRHNull, } for _, opt := range opts { opt(&sess.sessionOptions) } // This session is reusable and is closed with the function we'll // return. sess.sessionOptions.attrs.ContinueSession = true // Initialize the session. if err := sess.Init(t); err != nil { return nil, nil, err } closer := func() error { _, err := (&FlushContext{FlushHandle: sess.handle}).Execute(t) return err } return &sess, closer, nil } // Part 1, B.10.2 func getEncryptedSaltRSA(nameAlg TPMIAlgHash, parms *TPMSRSAParms, pub *TPM2BPublicKeyRSA) (*TPM2BEncryptedSecret, []byte, error) { rsaPub, err := RSAPub(parms, pub) if err != nil { return nil, nil, fmt.Errorf("could not encrypt salt to RSA key: %w", err) } // Odd special case: the size of the salt depends on the RSA scheme's // hash alg. var hAlg TPMIAlgHash switch parms.Scheme.Scheme { case TPMAlgRSASSA: rsassa, err := parms.Scheme.Details.RSASSA() if err != nil { return nil, nil, err } hAlg = rsassa.HashAlg case TPMAlgRSAES: hAlg = nameAlg case TPMAlgRSAPSS: rsapss, err := parms.Scheme.Details.RSAPSS() if err != nil { return nil, nil, err } hAlg = rsapss.HashAlg case TPMAlgOAEP: oaep, err := parms.Scheme.Details.OAEP() if err != nil { return nil, nil, err } hAlg = oaep.HashAlg case TPMAlgNull: hAlg = nameAlg default: return nil, nil, fmt.Errorf("unsupported RSA salt key scheme: %v", parms.Scheme.Scheme) } ha, err := hAlg.Hash() if err != nil { return nil, nil, err } salt := make([]byte, ha.Size()) if _, err := rand.Read(salt); err != nil { return nil, nil, fmt.Errorf("generating random salt: %w", err) } // Part 1, section 4.6 specifies the trailing NULL byte for the label. encSalt, err := rsa.EncryptOAEP(ha.New(), rand.Reader, rsaPub, salt, []byte("SECRET\x00")) if err != nil { return nil, nil, fmt.Errorf("encrypting salt: %w", err) } return &TPM2BEncryptedSecret{ Buffer: encSalt, }, salt, nil } // Part 1, 19.6.13 func getEncryptedSaltECC(nameAlg TPMIAlgHash, parms *TPMSECCParms, pub *TPMSECCPoint) (*TPM2BEncryptedSecret, []byte, error) { curve, err := parms.CurveID.ECDHCurve() if err != nil { return nil, nil, fmt.Errorf("ecc salt: param curve: %w", err) } eccPub, err := ECDHPub(parms, pub) if err != nil { return nil, nil, fmt.Errorf("ecc salt: unmarshaling tpm ecc key: %w", err) } // Generate new ECDH key ephPriv, err := curve.GenerateKey(rand.Reader) if err != nil { return nil, nil, fmt.Errorf("ecc salt: generating ecc private key: %w", err) } ephPubX, ephPubY, err := ECCPoint(ephPriv.PublicKey()) if err != nil { return nil, nil, fmt.Errorf("ecc salt: ecc pubkey: %w", err) } // Calculate Z (ECDH key * TPM pub) z, err := ephPriv.ECDH(eccPub) if err != nil { return nil, nil, fmt.Errorf("ecc salt: z calc: %w", err) } ha, err := nameAlg.Hash() if err != nil { return nil, nil, err } salt := KDFe(ha, z, "SECRET", ephPubX.Bytes(), pub.X.Buffer, ha.Size()*8) var encSalt bytes.Buffer binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubX.Bytes()))) encSalt.Write(ephPubX.Bytes()) binary.Write(&encSalt, binary.BigEndian, uint16(len(ephPubY.Bytes()))) encSalt.Write(ephPubY.Bytes()) return &TPM2BEncryptedSecret{ Buffer: encSalt.Bytes(), }, salt, nil } // getEncryptedSalt creates a salt value for salted sessions. // Returns the encrypted salt and plaintext salt, or an error value. func getEncryptedSalt(pub TPMTPublic) (*TPM2BEncryptedSecret, []byte, error) { switch pub.Type { case TPMAlgRSA: rsaParms, err := pub.Parameters.RSADetail() if err != nil { return nil, nil, err } rsaPub, err := pub.Unique.RSA() if err != nil { return nil, nil, err } return getEncryptedSaltRSA(pub.NameAlg, rsaParms, rsaPub) case TPMAlgECC: eccParms, err := pub.Parameters.ECCDetail() if err != nil { return nil, nil, err } eccPub, err := pub.Unique.ECC() if err != nil { return nil, nil, err } return getEncryptedSaltECC(pub.NameAlg, eccParms, eccPub) default: return nil, nil, fmt.Errorf("salt encryption alg '%v' not supported", pub.Type) } } // Init initializes the session, just in time, if needed. func (s *hmacSession) Init(t transport.TPM) error { if s.handle != TPMRHNull { // Session is already initialized. return nil } // Get a high-quality nonceCaller for our use. // Store it with the session object for later reference. s.nonceCaller = TPM2BNonce{ Buffer: make([]byte, s.nonceSize), } if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { return err } // Start up the actual auth session. sasCmd := StartAuthSession{ TPMKey: s.saltHandle, Bind: s.bindHandle, NonceCaller: s.nonceCaller, SessionType: TPMSEHMAC, Symmetric: s.symmetric, AuthHash: s.hash, } var salt []byte if s.saltHandle != TPMRHNull { var err error var encSalt *TPM2BEncryptedSecret encSalt, salt, err = getEncryptedSalt(s.saltPub) if err != nil { return err } sasCmd.EncryptedSalt = *encSalt } sasRsp, err := sasCmd.Execute(t) if err != nil { return err } s.handle = TPMHandle(sasRsp.SessionHandle.HandleValue()) s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 ha, err := s.hash.Hash() if err != nil { return err } if s.bindHandle != TPMRHNull || len(salt) != 0 { var authSalt []byte authSalt = append(authSalt, s.bindAuth...) authSalt = append(authSalt, salt...) s.sessionKey = KDFa(ha, authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, ha.Size()*8) } return nil } // Cleanup cleans up the session, if needed. func (s *hmacSession) CleanupFailure(t transport.TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil } fc := FlushContext{FlushHandle: s.handle} if _, err := fc.Execute(t); err != nil { return err } s.handle = TPMRHNull return nil } // NonceTPM returns the last nonceTPM value from the session. // May be nil, if the session hasn't been initialized yet. func (s *hmacSession) NonceTPM() TPM2BNonce { return s.nonceTPM } // To avoid a circular dependency on gotpm by tpm2, implement a // tiny serialization by hand for TPMASession here func attrsToBytes(attrs TPMASession) []byte { var res byte if attrs.ContinueSession { res |= (1 << 0) } if attrs.AuditExclusive { res |= (1 << 1) } if attrs.AuditReset { res |= (1 << 2) } if attrs.Decrypt { res |= (1 << 5) } if attrs.Encrypt { res |= (1 << 6) } if attrs.Audit { res |= (1 << 7) } return []byte{res} } // computeHMAC computes an authorization HMAC according to various equations in // Part 1. // This applies to both commands and responses. // The value of key depends on whether the session is bound and/or salted. // pHash cpHash for a command, or an rpHash for a response. // nonceNewer in a command is the new nonceCaller sent in the command session packet. // nonceNewer in a response is the new nonceTPM sent in the response session packet. // nonceOlder in a command is the last nonceTPM sent by the TPM for this session. // This may be when the session was created, or the last time it was used. // nonceOlder in a response is the corresponding nonceCaller sent in the command. func computeHMAC(alg TPMIAlgHash, key, pHash, nonceNewer, nonceOlder, addNonces []byte, attrs TPMASession) ([]byte, error) { ha, err := alg.Hash() if err != nil { return nil, err } mac := hmac.New(ha.New, key) mac.Write(pHash) mac.Write(nonceNewer) mac.Write(nonceOlder) mac.Write(addNonces) mac.Write(attrsToBytes(attrs)) return mac.Sum(nil), nil } // Trim trailing zeros from the auth value. Part 1, 19.6.5, Note 2 // Does not allocate a new underlying byte array. func hmacKeyFromAuthValue(auth []byte) []byte { key := auth for i := len(key) - 1; i >= 0; i-- { if key[i] == 0 { key = key[:i] } } return key } // NewNonceCaller updates the nonceCaller for this session. func (s *hmacSession) NewNonceCaller() error { _, err := rand.Read(s.nonceCaller.Buffer) return err } // Authorize computes the authorization structure for the session. // Unlike the TPM spec, authIndex is zero-based. func (s *hmacSession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, authIndex int) (*TPMSAuthCommand, error) { if s.handle == TPMRHNull { // Session is not initialized. return nil, fmt.Errorf("session not initialized") } // Part 1, 19.6 // HMAC key is (sessionKey || auth) unless this session is authorizing // its bind target var hmacKey []byte hmacKey = append(hmacKey, s.sessionKey...) if len(s.bindName.Buffer) == 0 || authIndex >= len(names) || !bytes.Equal(names[authIndex].Buffer, s.bindName.Buffer) { hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) } // Compute the authorization HMAC. cph, err := cpHash(s.hash, cc, names, parms) if err != nil { return nil, err } hmac, err := computeHMAC(s.hash, hmacKey, cph, s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) if err != nil { return nil, err } result := TPMSAuthCommand{ Handle: s.handle, Nonce: s.nonceCaller, Attributes: s.attrs, Authorization: TPM2BData{ Buffer: hmac, }, } return &result, nil } // Validate validates the response session structure for the session. // It updates nonceTPM from the TPM's response. func (s *hmacSession) Validate(rc TPMRC, cc TPMCC, parms []byte, names []TPM2BName, authIndex int, auth *TPMSAuthResponse) error { // Track the new nonceTPM for the session. s.nonceTPM = auth.Nonce // Track the session being automatically flushed. if !auth.Attributes.ContinueSession { s.handle = TPMRHNull } // Part 1, 19.6 // HMAC key is (sessionKey || auth) unless this session is authorizing // its bind target var hmacKey []byte hmacKey = append(hmacKey, s.sessionKey...) if len(s.bindName.Buffer) == 0 || authIndex >= len(names) || !bytes.Equal(names[authIndex].Buffer, s.bindName.Buffer) { hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) } // Compute the authorization HMAC. rph, err := rpHash(s.hash, rc, cc, parms) if err != nil { return err } mac, err := computeHMAC(s.hash, hmacKey, rph, s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) if err != nil { return err } // Compare the HMAC (constant time) if !hmac.Equal(mac, auth.Authorization.Buffer) { return fmt.Errorf("incorrect authorization HMAC") } return nil } // IsEncryption returns true if this is an encryption session. func (s *hmacSession) IsEncryption() bool { return s.attrs.Encrypt } // IsDecryption returns true if this is a decryption session. func (s *hmacSession) IsDecryption() bool { return s.attrs.Decrypt } // Encrypt decrypts the parameter in place, if this session is used for // parameter decryption. Otherwise, it does not modify the parameter. func (s *hmacSession) Encrypt(parameter []byte) error { if !s.IsDecryption() { return nil } // Only AES-CFB is supported. bits, err := s.symmetric.KeyBits.AES() if err != nil { return err } keyBytes := *bits / 8 keyIVBytes := int(keyBytes) + 16 var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) ha, err := s.hash.Hash() if err != nil { return err } keyIV := KDFa(ha, sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err } stream := cipher.NewCFBEncrypter(key, keyIV[keyBytes:]) stream.XORKeyStream(parameter, parameter) return nil } // Decrypt encrypts the parameter in place, if this session is used for // parameter encryption. Otherwise, it does not modify the parameter. func (s *hmacSession) Decrypt(parameter []byte) error { if !s.IsEncryption() { return nil } // Only AES-CFB is supported. bits, err := s.symmetric.KeyBits.AES() if err != nil { return err } keyBytes := *bits / 8 keyIVBytes := int(keyBytes) + 16 // Part 1, 21.1 var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) ha, err := s.hash.Hash() if err != nil { return err } keyIV := KDFa(ha, sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err } stream := cipher.NewCFBDecrypter(key, keyIV[keyBytes:]) stream.XORKeyStream(parameter, parameter) return nil } // Handle returns the handle value of the session. // If the session is created with HMAC (instead of HMACSession) this will be // TPM_RH_NULL. func (s *hmacSession) Handle() TPMHandle { return s.handle } // PolicyCallback represents an object's policy in the form of a function. // This function makes zero or more TPM policy commands and returns error. type PolicyCallback = func(tpm transport.TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error // policySession generally implements the policy session. type policySession struct { sessionOptions hash TPMIAlgHash nonceSize int handle TPMHandle sessionKey []byte // last nonceCaller nonceCaller TPM2BNonce // last nonceTPM nonceTPM TPM2BNonce callback *PolicyCallback } // Policy sets up a just-in-time policy session that created each time it's // needed. // Each time the policy is created, the callback is invoked to authorize the // session. // A real session is created, but just in time, and it is flushed when used. func Policy(hash TPMIAlgHash, nonceSize int, callback PolicyCallback, opts ...AuthOption) Session { // Set up a one-off session that knows the auth value. sess := policySession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, handle: TPMRHNull, callback: &callback, } for _, opt := range opts { opt(&sess.sessionOptions) } return &sess } // PolicySession opens a policy session that needs to be closed. // The caller is responsible to call whichever policy commands they want in the // session. // Note that the TPM resets a policy session after it is successfully used. func PolicySession(t transport.TPM, hash TPMIAlgHash, nonceSize int, opts ...AuthOption) (s Session, close func() error, err error) { // Set up a not-one-off session that knows the auth value. sess := policySession{ sessionOptions: defaultOptions(), hash: hash, nonceSize: nonceSize, handle: TPMRHNull, } for _, opt := range opts { opt(&sess.sessionOptions) } // This session is reusable and is closed with the function we'll // return. sess.sessionOptions.attrs.ContinueSession = true // Initialize the session. if err := sess.Init(t); err != nil { return nil, nil, err } closer := func() error { _, err := (&FlushContext{sess.handle}).Execute(t) return err } return &sess, closer, nil } // Init initializes the session, just in time, if needed. func (s *policySession) Init(t transport.TPM) error { if s.handle != TPMRHNull { // Session is already initialized. return nil } // Get a high-quality nonceCaller for our use. // Store it with the session object for later reference. s.nonceCaller = TPM2BNonce{ Buffer: make([]byte, s.nonceSize), } if _, err := rand.Read(s.nonceCaller.Buffer); err != nil { return err } sessType := TPMSEPolicy if s.sessionOptions.trialPolicy { sessType = TPMSETrial } // Start up the actual auth session. sasCmd := StartAuthSession{ TPMKey: s.saltHandle, Bind: s.bindHandle, NonceCaller: s.nonceCaller, SessionType: sessType, Symmetric: s.symmetric, AuthHash: s.hash, } var salt []byte if s.saltHandle != TPMRHNull { var err error var encSalt *TPM2BEncryptedSecret encSalt, salt, err = getEncryptedSalt(s.saltPub) if err != nil { return err } sasCmd.EncryptedSalt = *encSalt } sasRsp, err := sasCmd.Execute(t) if err != nil { return err } s.handle = TPMHandle(sasRsp.SessionHandle.HandleValue()) s.nonceTPM = sasRsp.NonceTPM // Part 1, 19.6 if s.bindHandle != TPMRHNull || len(salt) != 0 { var authSalt []byte authSalt = append(authSalt, s.bindAuth...) authSalt = append(authSalt, salt...) ha, err := s.hash.Hash() if err != nil { return err } s.sessionKey = KDFa(ha, authSalt, "ATH", s.nonceTPM.Buffer, s.nonceCaller.Buffer, ha.Size()*8) } // Call the callback to execute the policy, if needed if s.callback != nil { if err := (*s.callback)(t, s.handle, s.nonceTPM); err != nil { return fmt.Errorf("executing policy: %w", err) } } return nil } // CleanupFailure cleans up the session, if needed. func (s *policySession) CleanupFailure(t transport.TPM) error { // The user is already responsible to clean up this session. if s.attrs.ContinueSession { return nil } fc := FlushContext{FlushHandle: s.handle} if _, err := fc.Execute(t); err != nil { return err } s.handle = TPMRHNull return nil } // NonceTPM returns the last nonceTPM value from the session. // May be nil, if the session hasn't been initialized yet. func (s *policySession) NonceTPM() TPM2BNonce { return s.nonceTPM } // NewNonceCaller updates the nonceCaller for this session. func (s *policySession) NewNonceCaller() error { _, err := rand.Read(s.nonceCaller.Buffer) return err } // Authorize computes the authorization structure for the session. func (s *policySession) Authorize(cc TPMCC, parms, addNonces []byte, names []TPM2BName, _ int) (*TPMSAuthCommand, error) { if s.handle == TPMRHNull { // Session is not initialized. return nil, fmt.Errorf("session not initialized") } var hmac []byte if s.password { hmac = s.auth } else { // Part 1, 19.6 // HMAC key is (sessionKey || auth). var hmacKey []byte hmacKey = append(hmacKey, s.sessionKey...) hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) // Compute the authorization HMAC. cph, err := cpHash(s.hash, cc, names, parms) if err != nil { return nil, err } hmac, err = computeHMAC(s.hash, hmacKey, cph, s.nonceCaller.Buffer, s.nonceTPM.Buffer, addNonces, s.attrs) if err != nil { return nil, err } } result := TPMSAuthCommand{ Handle: s.handle, Nonce: s.nonceCaller, Attributes: s.attrs, Authorization: TPM2BData{ Buffer: hmac, }, } return &result, nil } // Validate valitades the response session structure for the session. // Updates nonceTPM from the TPM's response. func (s *policySession) Validate(rc TPMRC, cc TPMCC, parms []byte, _ []TPM2BName, _ int, auth *TPMSAuthResponse) error { // Track the new nonceTPM for the session. s.nonceTPM = auth.Nonce // Track the session being automatically flushed. if !auth.Attributes.ContinueSession { s.handle = TPMRHNull } if s.password { // If we used a password, expect no nonce and no response HMAC. if len(auth.Nonce.Buffer) != 0 { return fmt.Errorf("expected empty nonce in response auth to PW policy, got %x", auth.Nonce) } if len(auth.Authorization.Buffer) != 0 { return fmt.Errorf("expected empty HMAC in response auth to PW policy, got %x", auth.Authorization) } } else { // Part 1, 19.6 // HMAC key is (sessionKey || auth). var hmacKey []byte hmacKey = append(hmacKey, s.sessionKey...) hmacKey = append(hmacKey, hmacKeyFromAuthValue(s.auth)...) // Compute the authorization HMAC. rph, err := rpHash(s.hash, rc, cc, parms) if err != nil { return err } mac, err := computeHMAC(s.hash, hmacKey, rph, s.nonceTPM.Buffer, s.nonceCaller.Buffer, nil, auth.Attributes) if err != nil { return err } // Compare the HMAC (constant time) if !hmac.Equal(mac, auth.Authorization.Buffer) { return fmt.Errorf("incorrect authorization HMAC") } } return nil } // IsEncryption returns true if this is an encryption session. func (s *policySession) IsEncryption() bool { return s.attrs.Encrypt } // IsDecryption returns true if this is a decryption session. func (s *policySession) IsDecryption() bool { return s.attrs.Decrypt } // Encrypt encrypts the parameter in place, if this session is used for // parameter decryption. Otherwise, it does not modify the parameter. func (s *policySession) Encrypt(parameter []byte) error { if !s.IsDecryption() { return nil } // Only AES-CFB is supported. bits, err := s.symmetric.KeyBits.AES() if err != nil { return err } keyBytes := *bits / 8 keyIVBytes := int(keyBytes) + 16 var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) ha, err := s.hash.Hash() if err != nil { return err } keyIV := KDFa(ha, sessionValue, "CFB", s.nonceCaller.Buffer, s.nonceTPM.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err } stream := cipher.NewCFBEncrypter(key, keyIV[keyBytes:]) stream.XORKeyStream(parameter, parameter) return nil } // Decrypt decrypts the parameter in place, if this session is used for // parameter encryption. Otherwise, it does not modify the parameter. func (s *policySession) Decrypt(parameter []byte) error { if !s.IsEncryption() { return nil } // Only AES-CFB is supported. bits, err := s.symmetric.KeyBits.AES() if err != nil { return err } keyBytes := *bits / 8 keyIVBytes := int(keyBytes) + 16 // Part 1, 21.1 var sessionValue []byte sessionValue = append(sessionValue, s.sessionKey...) sessionValue = append(sessionValue, s.auth...) ha, err := s.hash.Hash() if err != nil { return err } keyIV := KDFa(ha, sessionValue, "CFB", s.nonceTPM.Buffer, s.nonceCaller.Buffer, keyIVBytes*8) key, err := aes.NewCipher(keyIV[:keyBytes]) if err != nil { return err } stream := cipher.NewCFBDecrypter(key, keyIV[keyBytes:]) stream.XORKeyStream(parameter, parameter) return nil } // Handle returns the handle value of the session. // If the session is created with Policy (instead of PolicySession) this will be // TPM_RH_NULL. func (s *policySession) Handle() TPMHandle { return s.handle } go-tpm-0.9.3/tpm2/structures.go000066400000000000000000003242131473040456300163750ustar00rootroot00000000000000// Package tpm2 defines all the TPM 2.0 structures together to avoid import cycles package tpm2 import ( "bytes" "crypto" "crypto/ecdh" "crypto/elliptic" "encoding/binary" "reflect" // Register the relevant hash implementations. _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" "fmt" ) // TPMCmdHeader is the header structure in front of any TPM command. // It is described in Part 1, Architecture. type TPMCmdHeader struct { marshalByReflection Tag TPMISTCommandTag Length uint32 CommandCode TPMCC } // TPMRspHeader is the header structure in front of any TPM response. // It is described in Part 1, Architecture. type TPMRspHeader struct { marshalByReflection Tag TPMISTCommandTag Length uint32 ResponseCode TPMRC } // TPMAlgorithmID represents a TPM_ALGORITHM_ID // this is the 1.2 compatible form of the TPM_ALG_ID // See definition in Part 2, Structures, section 5.3. type TPMAlgorithmID uint32 // TPMModifierIndicator represents a TPM_MODIFIER_INDICATOR. // See definition in Part 2, Structures, section 5.3. type TPMModifierIndicator uint32 // TPMAuthorizationSize represents a TPM_AUTHORIZATION_SIZE. // the authorizationSize parameter in a command // See definition in Part 2, Structures, section 5.3. type TPMAuthorizationSize uint32 // TPMParameterSize represents a TPM_PARAMETER_SIZE. // the parameterSize parameter in a command // See definition in Part 2, Structures, section 5.3. type TPMParameterSize uint32 // TPMKeySize represents a TPM_KEY_SIZE. // a key size in octets // See definition in Part 2, Structures, section 5.3. type TPMKeySize uint16 // TPMKeyBits represents a TPM_KEY_BITS. // a key size in bits // See definition in Part 2, Structures, section 5.3. type TPMKeyBits uint16 // TPMGenerated represents a TPM_GENERATED. // See definition in Part 2: Structures, section 6.2. type TPMGenerated uint32 // Generated values come from Part 2: Structures, section 6.2. const ( TPMGeneratedValue TPMGenerated = 0xff544347 ) // Check verifies that a TPMGenerated value is correct, and returns an error // otherwise. func (g TPMGenerated) Check() error { if g != TPMGeneratedValue { return fmt.Errorf("TPM_GENERATED value should be 0x%x, was 0x%x", TPMGeneratedValue, g) } return nil } // Curve returns the elliptic.Curve associated with a TPMECCCurve. func (c TPMECCCurve) Curve() (elliptic.Curve, error) { switch c { case TPMECCNistP224: return elliptic.P224(), nil case TPMECCNistP256: return elliptic.P256(), nil case TPMECCNistP384: return elliptic.P384(), nil case TPMECCNistP521: return elliptic.P521(), nil default: return nil, fmt.Errorf("unsupported ECC curve: %v", c) } } // ECDHCurve returns the ecdh.Curve associated with a TPMECCCurve. func (c TPMECCCurve) ECDHCurve() (ecdh.Curve, error) { switch c { case TPMECCNistP256: return ecdh.P256(), nil case TPMECCNistP384: return ecdh.P384(), nil case TPMECCNistP521: return ecdh.P521(), nil default: return nil, fmt.Errorf("unsupported ECC curve: %v", c) } } // HandleValue returns the handle value. This behavior is intended to satisfy // an interface that can be implemented by other, more complex types as well. func (h TPMHandle) HandleValue() uint32 { return uint32(h) } // KnownName returns the TPM Name associated with the handle, if it can be known // based only on the handle. This depends upon the value of the handle: // only PCR, session, and permanent values have known constant Names. // See definition in part 1: Architecture, section 16. func (h TPMHandle) KnownName() *TPM2BName { switch (TPMHT)(h >> 24) { case TPMHTPCR, TPMHTHMACSession, TPMHTPolicySession, TPMHTPermanent: result := make([]byte, 4) binary.BigEndian.PutUint32(result, h.HandleValue()) return &TPM2BName{Buffer: result} } return nil } // TPMAAlgorithm represents a TPMA_ALGORITHM. // See definition in Part 2: Structures, section 8.2. type TPMAAlgorithm struct { bitfield32 marshalByReflection // SET (1): an asymmetric algorithm with public and private portions // CLEAR (0): not an asymmetric algorithm Asymmetric bool `gotpm:"bit=0"` // SET (1): a symmetric block cipher // CLEAR (0): not a symmetric block cipher Symmetric bool `gotpm:"bit=1"` // SET (1): a hash algorithm // CLEAR (0): not a hash algorithm Hash bool `gotpm:"bit=2"` // SET (1): an algorithm that may be used as an object type // CLEAR (0): an algorithm that is not used as an object type Object bool `gotpm:"bit=3"` // SET (1): a signing algorithm. The setting of asymmetric, // symmetric, and hash will indicate the type of signing algorithm. // CLEAR (0): not a signing algorithm Signing bool `gotpm:"bit=8"` // SET (1): an encryption/decryption algorithm. The setting of // asymmetric, symmetric, and hash will indicate the type of // encryption/decryption algorithm. // CLEAR (0): not an encryption/decryption algorithm Encrypting bool `gotpm:"bit=9"` // SET (1): a method such as a key derivative function (KDF) // CLEAR (0): not a method Method bool `gotpm:"bit=10"` } // TPMAObject represents a TPMA_OBJECT. // See definition in Part 2: Structures, section 8.3.2. type TPMAObject struct { bitfield32 marshalByReflection // SET (1): The hierarchy of the object, as indicated by its // Qualified Name, may not change. // CLEAR (0): The hierarchy of the object may change as a result // of this object or an ancestor key being duplicated for use in // another hierarchy. FixedTPM bool `gotpm:"bit=1"` // SET (1): Previously saved contexts of this object may not be // loaded after Startup(CLEAR). // CLEAR (0): Saved contexts of this object may be used after a // Shutdown(STATE) and subsequent Startup(). STClear bool `gotpm:"bit=2"` // SET (1): The parent of the object may not change. // CLEAR (0): The parent of the object may change as the result of // a TPM2_Duplicate() of the object. FixedParent bool `gotpm:"bit=4"` // SET (1): Indicates that, when the object was created with // TPM2_Create() or TPM2_CreatePrimary(), the TPM generated all of // the sensitive data other than the authValue. // CLEAR (0): A portion of the sensitive data, other than the // authValue, was provided by the caller. SensitiveDataOrigin bool `gotpm:"bit=5"` // SET (1): Approval of USER role actions with this object may be // with an HMAC session or with a password using the authValue of // the object or a policy session. // CLEAR (0): Approval of USER role actions with this object may // only be done with a policy session. UserWithAuth bool `gotpm:"bit=6"` // SET (1): Approval of ADMIN role actions with this object may // only be done with a policy session. // CLEAR (0): Approval of ADMIN role actions with this object may // be with an HMAC session or with a password using the authValue // of the object or a policy session. AdminWithPolicy bool `gotpm:"bit=7"` // SET (1): The object exists only within a firmware-limited hierarchy. // CLEAR (0): The object can exist outside a firmware-limited hierarchy. FirmwareLimited bool `gotpm:"bit=8"` // SET (1): The object is not subject to dictionary attack // protections. // CLEAR (0): The object is subject to dictionary attack // protections. NoDA bool `gotpm:"bit=10"` // SET (1): If the object is duplicated, then symmetricAlg shall // not be TPM_ALG_NULL and newParentHandle shall not be // TPM_RH_NULL. // CLEAR (0): The object may be duplicated without an inner // wrapper on the private portion of the object and the new parent // may be TPM_RH_NULL. EncryptedDuplication bool `gotpm:"bit=11"` // SET (1): Key usage is restricted to manipulate structures of // known format; the parent of this key shall have restricted SET. // CLEAR (0): Key usage is not restricted to use on special // formats. Restricted bool `gotpm:"bit=16"` // SET (1): The private portion of the key may be used to decrypt. // CLEAR (0): The private portion of the key may not be used to // decrypt. Decrypt bool `gotpm:"bit=17"` // SET (1): For a symmetric cipher object, the private portion of // the key may be used to encrypt. For other objects, the private // portion of the key may be used to sign. // CLEAR (0): The private portion of the key may not be used to // sign or encrypt. SignEncrypt bool `gotpm:"bit=18"` // SET (1): An asymmetric key that may not be used to sign with // TPM2_Sign() CLEAR (0): A key that may be used with TPM2_Sign() // if sign is SET // NOTE: This attribute only has significance if sign is SET. X509Sign bool `gotpm:"bit=19"` } // TPMASession represents a TPMA_SESSION. // See definition in Part 2: Structures, section 8.4. type TPMASession struct { bitfield8 marshalByReflection // SET (1): In a command, this setting indicates that the session // is to remain active after successful completion of the command. // In a response, it indicates that the session is still active. // If SET in the command, this attribute shall be SET in the response. // CLEAR (0): In a command, this setting indicates that the TPM should // close the session and flush any related context when the command // completes successfully. In a response, it indicates that the // session is closed and the context is no longer active. // This attribute has no meaning for a password authorization and the // TPM will allow any setting of the attribute in the command and SET // the attribute in the response. ContinueSession bool `gotpm:"bit=0"` // SET (1): In a command, this setting indicates that the command // should only be executed if the session is exclusive at the start of // the command. In a response, it indicates that the session is // exclusive. This setting is only allowed if the audit attribute is // SET (TPM_RC_ATTRIBUTES). // CLEAR (0): In a command, indicates that the session need not be // exclusive at the start of the command. In a response, indicates that // the session is not exclusive. AuditExclusive bool `gotpm:"bit=1"` // SET (1): In a command, this setting indicates that the audit digest // of the session should be initialized and the exclusive status of the // session SET. This setting is only allowed if the audit attribute is // SET (TPM_RC_ATTRIBUTES). // CLEAR (0): In a command, indicates that the audit digest should not // be initialized. This bit is always CLEAR in a response. AuditReset bool `gotpm:"bit=2"` // SET (1): In a command, this setting indicates that the first // parameter in the command is symmetrically encrypted using the // parameter encryption scheme described in TPM 2.0 Part 1. The TPM will // decrypt the parameter after performing any HMAC computations and // before unmarshaling the parameter. In a response, the attribute is // copied from the request but has no effect on the response. // CLEAR (0): Session not used for encryption. // For a password authorization, this attribute will be CLEAR in both the // command and response. Decrypt bool `gotpm:"bit=5"` // SET (1): In a command, this setting indicates that the TPM should use // this session to encrypt the first parameter in the response. In a // response, it indicates that the attribute was set in the command and // that the TPM used the session to encrypt the first parameter in the // response using the parameter encryption scheme described in TPM 2.0 // Part 1. // CLEAR (0): Session not used for encryption. // For a password authorization, this attribute will be CLEAR in both the // command and response. Encrypt bool `gotpm:"bit=6"` // SET (1): In a command or response, this setting indicates that the // session is for audit and that auditExclusive and auditReset have // meaning. This session may also be used for authorization, encryption, // or decryption. The encrypted and encrypt fields may be SET or CLEAR. // CLEAR (0): Session is not used for audit. // If SET in the command, then this attribute will be SET in the response. Audit bool `gotpm:"bit=7"` } // TPMALocality represents a TPMA_LOCALITY. // See definition in Part 2: Structures, section 8.5. type TPMALocality struct { bitfield8 marshalByReflection TPMLocZero bool `gotpm:"bit=0"` TPMLocOne bool `gotpm:"bit=1"` TPMLocTwo bool `gotpm:"bit=2"` TPMLocThree bool `gotpm:"bit=3"` TPMLocFour bool `gotpm:"bit=4"` // If any of these bits is set, an extended locality is indicated Extended uint8 `gotpm:"bit=7:5"` } // TPMACC represents a TPMA_CC. // See definition in Part 2: Structures, section 8.9. type TPMACC struct { bitfield32 marshalByReflection // indicates the command being selected CommandIndex uint16 `gotpm:"bit=15:0"` // SET (1): indicates that the command may write to NV // CLEAR (0): indicates that the command does not write to NV NV bool `gotpm:"bit=22"` // SET (1): This command could flush any number of loaded contexts. // CLEAR (0): no additional changes other than indicated by the flushed attribute Extensive bool `gotpm:"bit=23"` // SET (1): The context associated with any transient handle in the command will be flushed when this command completes. // CLEAR (0): No context is flushed as a side effect of this command. Flushed bool `gotpm:"bit=24"` // indicates the number of the handles in the handle area for this command CHandles uint8 `gotpm:"bit=27:25"` // SET (1): indicates the presence of the handle area in the response RHandle bool `gotpm:"bit=28"` // SET (1): indicates that the command is vendor-specific // CLEAR (0): indicates that the command is defined in a version of this specification V bool `gotpm:"bit=29"` } // TPMAACT represents a TPMA_ACT. // See definition in Part 2: Structures, section 8.12. type TPMAACT struct { bitfield32 marshalByReflection // SET (1): The ACT has signaled // CLEAR (0): The ACT has not signaled Signaled bool `gotpm:"bit=0"` // SET (1): The ACT signaled bit is preserved over a power cycle // CLEAR (0): The ACT signaled bit is not preserved over a power cycle PreserveSignaled bool `gotpm:"bit=1"` } // TPMIYesNo represents a TPMI_YES_NO. // See definition in Part 2: Structures, section 9.2. // Use native bool for TPMI_YES_NO; encoding/binary already treats this as 8 bits wide. type TPMIYesNo = bool // TPMIDHObject represents a TPMI_DH_OBJECT. // See definition in Part 2: Structures, section 9.3. type TPMIDHObject = TPMHandle // TPMIDHPersistent represents a TPMI_DH_PERSISTENT. // See definition in Part 2: Structures, section 9.5. type TPMIDHPersistent = TPMHandle // TPMIDHEntity represents a TPMI_DH_ENTITY. // See definition in Part 2: Structures, section 9.6. type TPMIDHEntity = TPMHandle // TPMISHAuthSession represents a TPMI_SH_AUTH_SESSION. // See definition in Part 2: Structures, section 9.8. type TPMISHAuthSession = TPMHandle // TPMISHHMAC represents a TPMI_SH_HMAC. // See definition in Part 2: Structures, section 9.9. type TPMISHHMAC = TPMHandle // TPMISHPolicy represents a TPMI_SH_POLICY. // See definition in Part 2: Structures, section 9.10. type TPMISHPolicy = TPMHandle // TPMIDHContext represents a TPMI_DH_CONTEXT. // See definition in Part 2: Structures, section 9.11. type TPMIDHContext = TPMHandle // TPMIDHSaved represents a TPMI_DH_SAVED. // See definition in Part 2: Structures, section 9.12. type TPMIDHSaved = TPMHandle // TPMIRHHierarchy represents a TPMI_RH_HIERARCHY. // See definition in Part 2: Structures, section 9.13. type TPMIRHHierarchy = TPMHandle // TPMIRHEnables represents a TPMI_RH_ENABLES. // See definition in Part 2: Structures, section 9.14. type TPMIRHEnables = TPMHandle // TPMIRHHierarchyAuth represents a TPMI_RH_HIERARCHY_AUTH. // See definition in Part 2: Structures, section 9.15. type TPMIRHHierarchyAuth = TPMHandle // TPMIRHHierarchyPolicy represents a TPMI_RH_HIERARCHY_POLICY. // See definition in Part 2: Structures, section 9.16. type TPMIRHHierarchyPolicy = TPMHandle // TPMIRHPlatform represents a TPMI_RH_PLATFORM. // See definition in Part 2: Structures, section 9.17. type TPMIRHPlatform = TPMHandle // TPMIRHOwner represents a TPMI_RH_OWNER. // See definition in Part 2: Structures, section 9.18. type TPMIRHOwner = TPMHandle // TPMIRHEndorsement represents a TPMI_RH_ENDORSEMENT. // See definition in Part 2: Structures, section 9.19. type TPMIRHEndorsement = TPMHandle // TPMIRHProvision represents a TPMI_RH_PROVISION. // See definition in Part 2: Structures, section 9.20. type TPMIRHProvision = TPMHandle // TPMIRHClear represents a TPMI_RH_CLEAR. // See definition in Part 2: Structures, section 9.21. type TPMIRHClear = TPMHandle // TPMIRHNVAuth represents a TPMI_RH_NV_AUTH. // See definition in Part 2: Structures, section 9.22. type TPMIRHNVAuth = TPMHandle // TPMIRHLockout represents a TPMI_RH_LOCKOUT. // See definition in Part 2: Structures, section 9.23. type TPMIRHLockout = TPMHandle // TPMIRHNVIndex represents a TPMI_RH_NV_INDEX. // See definition in Part 2: Structures, section 9.24. type TPMIRHNVIndex = TPMHandle // TPMIRHAC represents a TPMI_RH_AC. // See definition in Part 2: Structures, section 9.25. type TPMIRHAC = TPMHandle // TPMIRHACT represents a TPMI_RH_ACT. // See definition in Part 2: Structures, section 9.26. type TPMIRHACT = TPMHandle // TPMIAlgHash represents a TPMI_ALG_HASH. // See definition in Part 2: Structures, section 9.27. type TPMIAlgHash = TPMAlgID // Hash returns the crypto.Hash associated with a TPMIAlgHash. func (a TPMIAlgHash) Hash() (crypto.Hash, error) { switch TPMAlgID(a) { case TPMAlgSHA1: return crypto.SHA1, nil case TPMAlgSHA256: return crypto.SHA256, nil case TPMAlgSHA384: return crypto.SHA384, nil case TPMAlgSHA512: return crypto.SHA512, nil } return crypto.SHA256, fmt.Errorf("unsupported hash algorithm: %v", a) } // TPMIAlgSym represents a TPMI_ALG_SYM. // See definition in Part 2: Structures, section 9.29. type TPMIAlgSym = TPMAlgID // TPMIAlgSymObject represents a TPMI_ALG_SYM_OBJECT. // See definition in Part 2: Structures, section 9.30. type TPMIAlgSymObject = TPMAlgID // TPMIAlgSymMode represents a TPMI_ALG_SYM_MODE. // See definition in Part 2: Structures, section 9.31. type TPMIAlgSymMode = TPMAlgID // TPMIAlgKDF represents a TPMI_ALG_KDF. // See definition in Part 2: Structures, section 9.32. type TPMIAlgKDF = TPMAlgID // TPMIAlgSigScheme represents a TPMI_ALG_SIG_SCHEME. // See definition in Part 2: Structures, section 9.33. type TPMIAlgSigScheme = TPMAlgID // TPMISTCommandTag represents a TPMI_ST_COMMAND_TAG. // See definition in Part 2: Structures, section 9.35. type TPMISTCommandTag = TPMST // TPMSEmpty represents a TPMS_EMPTY. // See definition in Part 2: Structures, section 10.1. type TPMSEmpty struct { marshalByReflection } // TPMTHA represents a TPMT_HA. // See definition in Part 2: Structures, section 10.3.2. type TPMTHA struct { marshalByReflection // selector of the hash contained in the digest that implies the size of the digest HashAlg TPMIAlgHash `gotpm:"nullable"` // the digest data // NOTE: For convenience, this is not implemented as a union. Digest []byte } // TPM2BDigest represents a TPM2B_DIGEST. // See definition in Part 2: Structures, section 10.4.2. type TPM2BDigest TPM2BData // TPM2BData represents a TPM2B_DATA. // See definition in Part 2: Structures, section 10.4.3. type TPM2BData struct { marshalByReflection // size in octets of the buffer field; may be 0 Buffer []byte `gotpm:"sized"` } // TPM2BNonce represents a TPM2B_NONCE. // See definition in Part 2: Structures, section 10.4.4. type TPM2BNonce TPM2BDigest // TPM2BEvent represents a TPM2B_EVENT. // See definition in Part 2: Structures, section 10.4.7. type TPM2BEvent TPM2BData // TPM2BTimeout represents a TPM2B_TIMEOUT. // See definition in Part 2: Structures, section 10.4.10. type TPM2BTimeout TPM2BData // TPM2BAuth represents a TPM2B_AUTH. // See definition in Part 2: Structures, section 10.4.5. type TPM2BAuth TPM2BDigest // TPM2BOperand represents a TPM2B_Operand. // See definition in Part 2: Structures, section 10.4.6. type TPM2BOperand TPM2BDigest // TPM2BMaxBuffer represents a TPM2B_MAX_BUFFER. // See definition in Part 2: Structures, section 10.4.8. type TPM2BMaxBuffer TPM2BData // TPM2BMaxNVBuffer represents a TPM2B_MAX_NV_BUFFER. // See definition in Part 2: Structures, section 10.4.9. type TPM2BMaxNVBuffer TPM2BData // TPM2BIV represents a TPM2B_IV. // See definition in Part 2: Structures, section 10.4.11. type TPM2BIV TPM2BData // TPM2BName represents a TPM2B_NAME. // See definition in Part 2: Structures, section 10.5.3. // NOTE: This structure does not contain a TPMUName, because that union // is not tagged with a selector. Instead, TPM2B_Name is flattened and // all TPMDirect helpers that deal with names will deal with them as so. type TPM2BName TPM2BData // TPMSPCRSelection represents a TPMS_PCR_SELECTION. // See definition in Part 2: Structures, section 10.6.2. type TPMSPCRSelection struct { marshalByReflection Hash TPMIAlgHash PCRSelect []byte `gotpm:"sized8"` } // TPMTTKCreation represents a TPMT_TK_CREATION. // See definition in Part 2: Structures, section 10.7.3. type TPMTTKCreation struct { marshalByReflection // ticket structure tag Tag TPMST // the hierarchy containing name Hierarchy TPMIRHHierarchy // This shall be the HMAC produced using a proof value of hierarchy. Digest TPM2BDigest } // TPMTTKVerified represents a TPMT_TK_Verified. // See definition in Part 2: Structures, section 10.7.4. type TPMTTKVerified struct { marshalByReflection // ticket structure tag Tag TPMST // the hierarchy containing keyName Hierarchy TPMIRHHierarchy // This shall be the HMAC produced using a proof value of hierarchy. Digest TPM2BDigest } // TPMTTKAuth represents a TPMT_TK_AUTH. // See definition in Part 2: Structures, section 10.7.5. type TPMTTKAuth struct { marshalByReflection // ticket structure tag Tag TPMST // the hierarchy of the object used to produce the ticket Hierarchy TPMIRHHierarchy `gotpm:"nullable"` // This shall be the HMAC produced using a proof value of hierarchy. Digest TPM2BDigest } // TPMTTKHashCheck represents a TPMT_TK_HASHCHECK. // See definition in Part 2: Structures, section 10.7.6. type TPMTTKHashCheck struct { marshalByReflection // ticket structure tag Tag TPMST // the hierarchy Hierarchy TPMIRHHierarchy `gotpm:"nullable"` // This shall be the HMAC produced using a proof value of hierarchy. Digest TPM2BDigest } // TPMSAlgProperty represents a TPMS_ALG_PROPERTY. // See definition in Part 2: Structures, section 10.8.1. type TPMSAlgProperty struct { marshalByReflection // an algorithm identifier Alg TPMAlgID // the attributes of the algorithm AlgProperties TPMAAlgorithm } // TPMSTaggedProperty represents a TPMS_TAGGED_PROPERTY. // See definition in Part 2: Structures, section 10.8.2. type TPMSTaggedProperty struct { marshalByReflection // a property identifier Property TPMPT // the value of the property Value uint32 } // TPMSTaggedPCRSelect represents a TPMS_TAGGED_PCR_SELECT. // See definition in Part 2: Structures, section 10.8.3. type TPMSTaggedPCRSelect struct { marshalByReflection // the property identifier Tag TPMPTPCR // the bit map of PCR with the identified property PCRSelect []byte `gotpm:"sized8"` } // TPMSTaggedPolicy represents a TPMS_TAGGED_POLICY. // See definition in Part 2: Structures, section 10.8.4. type TPMSTaggedPolicy struct { marshalByReflection // a permanent handle Handle TPMHandle // the policy algorithm and hash PolicyHash TPMTHA } // TPMSACTData represents a TPMS_ACT_DATA. // See definition in Part 2: Structures, section 10.8.5. type TPMSACTData struct { marshalByReflection // a permanent handle Handle TPMHandle // the current timeout of the ACT Timeout uint32 // the state of the ACT Attributes TPMAACT } // TPMLCC represents a TPML_CC. // See definition in Part 2: Structures, section 10.9.1. type TPMLCC struct { marshalByReflection CommandCodes []TPMCC `gotpm:"list"` } // TPMLCCA represents a TPML_CCA. // See definition in Part 2: Structures, section 10.9.2. type TPMLCCA struct { marshalByReflection CommandAttributes []TPMACC `gotpm:"list"` } // TPMLAlg represents a TPML_ALG. // See definition in Part 2: Structures, section 10.9.3. type TPMLAlg struct { marshalByReflection Algorithms []TPMAlgID `gotpm:"list"` } // TPMLHandle represents a TPML_HANDLE. // See definition in Part 2: Structures, section 10.9.4. type TPMLHandle struct { marshalByReflection Handle []TPMHandle `gotpm:"list"` } // TPMLDigest represents a TPML_DIGEST. // See definition in Part 2: Structures, section 10.9.5. type TPMLDigest struct { marshalByReflection // a list of digests Digests []TPM2BDigest `gotpm:"list"` } // TPMLDigestValues represents a TPML_DIGEST_VALUES. // See definition in Part 2: Structures, section 10.9.6. type TPMLDigestValues struct { marshalByReflection // a list of tagged digests Digests []TPMTHA `gotpm:"list"` } // TPMLPCRSelection represents a TPML_PCR_SELECTION. // See definition in Part 2: Structures, section 10.9.7. type TPMLPCRSelection struct { marshalByReflection PCRSelections []TPMSPCRSelection `gotpm:"list"` } // TPMLAlgProperty represents a TPML_ALG_PROPERTY. // See definition in Part 2: Structures, section 10.9.8. type TPMLAlgProperty struct { marshalByReflection AlgProperties []TPMSAlgProperty `gotpm:"list"` } // TPMLTaggedTPMProperty represents a TPML_TAGGED_TPM_PROPERTY. // See definition in Part 2: Structures, section 10.9.9. type TPMLTaggedTPMProperty struct { marshalByReflection TPMProperty []TPMSTaggedProperty `gotpm:"list"` } // TPMLTaggedPCRProperty represents a TPML_TAGGED_PCR_PROPERTY. // See definition in Part 2: Structures, section 10.9.10. type TPMLTaggedPCRProperty struct { marshalByReflection PCRProperty []TPMSTaggedPCRSelect `gotpm:"list"` } // TPMLECCCurve represents a TPML_ECC_CURVE. // See definition in Part 2: Structures, section 10.9.11. type TPMLECCCurve struct { marshalByReflection ECCCurves []TPMECCCurve `gotpm:"list"` } // TPMLTaggedPolicy represents a TPML_TAGGED_POLICY. // See definition in Part 2: Structures, section 10.9.12. type TPMLTaggedPolicy struct { marshalByReflection Policies []TPMSTaggedPolicy `gotpm:"list"` } // TPMLACTData represents a TPML_ACT_DATA. // See definition in Part 2: Structures, section 10.9.13. type TPMLACTData struct { marshalByReflection ACTData []TPMSACTData `gotpm:"list"` } // TPMUCapabilities represents a TPMU_CAPABILITIES. // See definition in Part 2: Structures, section 10.10.1. type TPMUCapabilities struct { selector TPMCap contents Marshallable } // CapabilitiesContents is a type constraint representing the possible contents of TPMUCapabilities. type CapabilitiesContents interface { Marshallable *TPMLAlgProperty | *TPMLHandle | *TPMLCCA | *TPMLCC | *TPMLPCRSelection | *TPMLTaggedTPMProperty | *TPMLTaggedPCRProperty | *TPMLECCCurve | *TPMLTaggedPolicy | *TPMLACTData } // create implements the unmarshallableWithHint interface. func (u *TPMUCapabilities) create(hint int64) (reflect.Value, error) { switch TPMCap(hint) { case TPMCapAlgs: contents := TPMLAlgProperty{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapHandles: contents := TPMLHandle{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapCommands: contents := TPMLCCA{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapPPCommands, TPMCapAuditCommands: contents := TPMLCC{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapPCRs: contents := TPMLPCRSelection{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapTPMProperties: contents := TPMLTaggedTPMProperty{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapPCRProperties: contents := TPMLTaggedPCRProperty{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapECCCurves: contents := TPMLECCCurve{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapAuthPolicies: contents := TPMLTaggedPolicy{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil case TPMCapACT: contents := TPMLACTData{} u.contents = &contents u.selector = TPMCap(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUCapabilities) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMCap(hint) { case TPMCapAlgs: contents := TPMLAlgProperty{} if u.contents != nil { contents = *u.contents.(*TPMLAlgProperty) } return reflect.ValueOf(&contents), nil case TPMCapHandles: contents := TPMLHandle{} if u.contents != nil { contents = *u.contents.(*TPMLHandle) } return reflect.ValueOf(&contents), nil case TPMCapCommands: contents := TPMLCCA{} if u.contents != nil { contents = *u.contents.(*TPMLCCA) } return reflect.ValueOf(&contents), nil case TPMCapPPCommands, TPMCapAuditCommands: contents := TPMLCC{} if u.contents != nil { contents = *u.contents.(*TPMLCC) } return reflect.ValueOf(&contents), nil case TPMCapPCRs: contents := TPMLPCRSelection{} if u.contents != nil { contents = *u.contents.(*TPMLPCRSelection) } return reflect.ValueOf(&contents), nil case TPMCapTPMProperties: contents := TPMLTaggedTPMProperty{} if u.contents != nil { contents = *u.contents.(*TPMLTaggedTPMProperty) } return reflect.ValueOf(&contents), nil case TPMCapPCRProperties: contents := TPMLTaggedPCRProperty{} if u.contents != nil { contents = *u.contents.(*TPMLTaggedPCRProperty) } return reflect.ValueOf(&contents), nil case TPMCapECCCurves: contents := TPMLECCCurve{} if u.contents != nil { contents = *u.contents.(*TPMLECCCurve) } return reflect.ValueOf(&contents), nil case TPMCapAuthPolicies: contents := TPMLTaggedPolicy{} if u.contents != nil { contents = *u.contents.(*TPMLTaggedPolicy) } return reflect.ValueOf(&contents), nil case TPMCapACT: contents := TPMLACTData{} if u.contents != nil { contents = *u.contents.(*TPMLACTData) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUCapabilities instantiates a TPMUCapabilities with the given contents. func NewTPMUCapabilities[C CapabilitiesContents](selector TPMCap, contents C) TPMUCapabilities { return TPMUCapabilities{ selector: selector, contents: contents, } } // Algorithms returns the 'algorithms' member of the union. func (u *TPMUCapabilities) Algorithms() (*TPMLAlgProperty, error) { if u.selector == TPMCapAlgs { return u.contents.(*TPMLAlgProperty), nil } return nil, fmt.Errorf("did not contain algorithms (selector value was %v)", u.selector) } // Handles returns the 'handles' member of the union. func (u *TPMUCapabilities) Handles() (*TPMLHandle, error) { if u.selector == TPMCapHandles { return u.contents.(*TPMLHandle), nil } return nil, fmt.Errorf("did not contain handles (selector value was %v)", u.selector) } // Command returns the 'command' member of the union. func (u *TPMUCapabilities) Command() (*TPMLCCA, error) { if u.selector == TPMCapCommands { return u.contents.(*TPMLCCA), nil } return nil, fmt.Errorf("did not contain command (selector value was %v)", u.selector) } // PPCommands returns the 'ppCommands' member of the union. func (u *TPMUCapabilities) PPCommands() (*TPMLCC, error) { if u.selector == TPMCapPPCommands { return u.contents.(*TPMLCC), nil } return nil, fmt.Errorf("did not contain ppCommands (selector value was %v)", u.selector) } // AuditCommands returns the 'auditCommands' member of the union. func (u *TPMUCapabilities) AuditCommands() (*TPMLCC, error) { if u.selector == TPMCapAuditCommands { return u.contents.(*TPMLCC), nil } return nil, fmt.Errorf("did not contain auditCommands (selector value was %v)", u.selector) } // AssignedPCR returns the 'assignedPCR' member of the union. func (u *TPMUCapabilities) AssignedPCR() (*TPMLPCRSelection, error) { if u.selector == TPMCapPCRs { return u.contents.(*TPMLPCRSelection), nil } return nil, fmt.Errorf("did not contain assignedPCR (selector value was %v)", u.selector) } // TPMProperties returns the 'tpmProperties' member of the union. func (u *TPMUCapabilities) TPMProperties() (*TPMLTaggedTPMProperty, error) { if u.selector == TPMCapTPMProperties { return u.contents.(*TPMLTaggedTPMProperty), nil } return nil, fmt.Errorf("did not contain tpmProperties (selector value was %v)", u.selector) } // PCRProperties returns the 'pcrProperties' member of the union. func (u *TPMUCapabilities) PCRProperties() (*TPMLTaggedPCRProperty, error) { if u.selector == TPMCapPCRProperties { return u.contents.(*TPMLTaggedPCRProperty), nil } return nil, fmt.Errorf("did not contain pcrProperties (selector value was %v)", u.selector) } // ECCCurves returns the 'eccCurves' member of the union. func (u *TPMUCapabilities) ECCCurves() (*TPMLECCCurve, error) { if u.selector == TPMCapECCCurves { return u.contents.(*TPMLECCCurve), nil } return nil, fmt.Errorf("did not contain eccCurves (selector value was %v)", u.selector) } // AuthPolicies returns the 'authPolicies' member of the union. func (u *TPMUCapabilities) AuthPolicies() (*TPMLTaggedPolicy, error) { if u.selector == TPMCapAuthPolicies { return u.contents.(*TPMLTaggedPolicy), nil } return nil, fmt.Errorf("did not contain authPolicies (selector value was %v)", u.selector) } // ACTData returns the 'actData' member of the union. func (u *TPMUCapabilities) ACTData() (*TPMLACTData, error) { if u.selector == TPMCapAuthPolicies { return u.contents.(*TPMLACTData), nil } return nil, fmt.Errorf("did not contain actData (selector value was %v)", u.selector) } // TPMSCapabilityData represents a TPMS_CAPABILITY_DATA. // See definition in Part 2: Structures, section 10.10.2. type TPMSCapabilityData struct { marshalByReflection // the capability Capability TPMCap // the capability data Data TPMUCapabilities `gotpm:"tag=Capability"` } // TPMSClockInfo represents a TPMS_CLOCK_INFO. // See definition in Part 2: Structures, section 10.11.1. type TPMSClockInfo struct { marshalByReflection // time value in milliseconds that advances while the TPM is powered Clock uint64 // number of occurrences of TPM Reset since the last TPM2_Clear() ResetCount uint32 // number of times that TPM2_Shutdown() or _TPM_Hash_Start have // occurred since the last TPM Reset or TPM2_Clear(). RestartCount uint32 // no value of Clock greater than the current value of Clock has been // previously reported by the TPM. Set to YES on TPM2_Clear(). Safe TPMIYesNo } // TPMSTimeInfo represents a TPMS_TIMEzINFO. // See definition in Part 2: Structures, section 10.11.6. type TPMSTimeInfo struct { marshalByReflection // time in milliseconds since the TIme circuit was last reset Time uint64 // a structure containing the clock information ClockInfo TPMSClockInfo } // TPMSTimeAttestInfo represents a TPMS_TIME_ATTEST_INFO. // See definition in Part 2: Structures, section 10.12.2. type TPMSTimeAttestInfo struct { marshalByReflection // the Time, Clock, resetCount, restartCount, and Safe indicator Time TPMSTimeInfo // a TPM vendor-specific value indicating the version number of the firmware FirmwareVersion uint64 } // TPMSCertifyInfo represents a TPMS_CERTIFY_INFO. // See definition in Part 2: Structures, section 10.12.3. type TPMSCertifyInfo struct { marshalByReflection // Name of the certified object Name TPM2BName // Qualified Name of the certified object QualifiedName TPM2BName } // TPMSQuoteInfo represents a TPMS_QUOTE_INFO. // See definition in Part 2: Structures, section 10.12.4. type TPMSQuoteInfo struct { marshalByReflection // information on algID, PCR selected and digest PCRSelect TPMLPCRSelection // digest of the selected PCR using the hash of the signing key PCRDigest TPM2BDigest } // TPMSCommandAuditInfo represents a TPMS_COMMAND_AUDIT_INFO. // See definition in Part 2: Structures, section 10.12.5. type TPMSCommandAuditInfo struct { marshalByReflection // the monotonic audit counter AuditCounter uint64 // hash algorithm used for the command audit DigestAlg TPMAlgID // the current value of the audit digest AuditDigest TPM2BDigest // digest of the command codes being audited using digestAlg CommandDigest TPM2BDigest } // TPMSSessionAuditInfo represents a TPMS_SESSION_AUDIT_INFO. // See definition in Part 2: Structures, section 10.12.6. type TPMSSessionAuditInfo struct { marshalByReflection // current exclusive status of the session ExclusiveSession TPMIYesNo // the current value of the session audit digest SessionDigest TPM2BDigest } // TPMSCreationInfo represents a TPMS_CREATION_INFO. // See definition in Part 2: Structures, section 10.12.7. type TPMSCreationInfo struct { marshalByReflection // Name of the object ObjectName TPM2BName // creationHash CreationHash TPM2BDigest } // TPMSNVCertifyInfo represents a TPMS_NV_CERTIFY_INFO. // See definition in Part 2: Structures, section 10.12.8. type TPMSNVCertifyInfo struct { marshalByReflection // Name of the NV Index IndexName TPM2BName // the offset parameter of TPM2_NV_Certify() Offset uint16 // contents of the NV Index NVContents TPM2BData } // TPMSNVDigestCertifyInfo represents a TPMS_NV_DIGEST_CERTIFY_INFO. // See definition in Part 2: Structures, section 10.12.9. type TPMSNVDigestCertifyInfo struct { marshalByReflection // Name of the NV Index IndexName TPM2BName // hash of the contents of the index NVDigest TPM2BDigest } // TPMISTAttest represents a TPMI_ST_ATTEST. // See definition in Part 2: Structures, section 10.12.10. type TPMISTAttest = TPMST // TPMUAttest represents a TPMU_ATTEST. // See definition in Part 2: Structures, section 10.12.11. type TPMUAttest struct { selector TPMST contents Marshallable } // AttestContents is a type constraint representing the possible contents of TPMUAttest. type AttestContents interface { Marshallable *TPMSNVCertifyInfo | *TPMSCommandAuditInfo | *TPMSSessionAuditInfo | *TPMSCertifyInfo | *TPMSQuoteInfo | *TPMSTimeAttestInfo | *TPMSCreationInfo | *TPMSNVDigestCertifyInfo } // create implements the unmarshallableWithHint interface. func (u *TPMUAttest) create(hint int64) (reflect.Value, error) { switch TPMST(hint) { case TPMSTAttestNV: contents := TPMSNVCertifyInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestCommandAudit: contents := TPMSCommandAuditInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestSessionAudit: contents := TPMSSessionAuditInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestCertify: contents := TPMSCertifyInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestQuote: contents := TPMSQuoteInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestTime: contents := TPMSTimeAttestInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestCreation: contents := TPMSCreationInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil case TPMSTAttestNVDigest: contents := TPMSNVDigestCertifyInfo{} u.contents = &contents u.selector = TPMST(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUAttest) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMST(hint) { case TPMSTAttestNV: contents := TPMSNVCertifyInfo{} if u.contents != nil { contents = *u.contents.(*TPMSNVCertifyInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestCommandAudit: contents := TPMSCommandAuditInfo{} if u.contents != nil { contents = *u.contents.(*TPMSCommandAuditInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestSessionAudit: contents := TPMSSessionAuditInfo{} if u.contents != nil { contents = *u.contents.(*TPMSSessionAuditInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestCertify: contents := TPMSCertifyInfo{} if u.contents != nil { contents = *u.contents.(*TPMSCertifyInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestQuote: contents := TPMSQuoteInfo{} if u.contents != nil { contents = *u.contents.(*TPMSQuoteInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestTime: contents := TPMSTimeAttestInfo{} if u.contents != nil { contents = *u.contents.(*TPMSTimeAttestInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestCreation: contents := TPMSCreationInfo{} if u.contents != nil { contents = *u.contents.(*TPMSCreationInfo) } return reflect.ValueOf(&contents), nil case TPMSTAttestNVDigest: contents := TPMSNVDigestCertifyInfo{} if u.contents != nil { contents = *u.contents.(*TPMSNVDigestCertifyInfo) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUAttest instantiates a TPMUAttest with the given contents. func NewTPMUAttest[C AttestContents](selector TPMST, contents C) TPMUAttest { return TPMUAttest{ selector: selector, contents: contents, } } // Certify returns the 'certify' member of the union. func (u *TPMUAttest) Certify() (*TPMSCertifyInfo, error) { if u.selector == TPMSTAttestCertify { return u.contents.(*TPMSCertifyInfo), nil } return nil, fmt.Errorf("did not contain certify (selector value was %v)", u.selector) } // Creation returns the 'creation' member of the union. func (u *TPMUAttest) Creation() (*TPMSCreationInfo, error) { if u.selector == TPMSTAttestCreation { return u.contents.(*TPMSCreationInfo), nil } return nil, fmt.Errorf("did not contain creation (selector value was %v)", u.selector) } // Quote returns the 'quote' member of the union. func (u *TPMUAttest) Quote() (*TPMSQuoteInfo, error) { if u.selector == TPMSTAttestQuote { return u.contents.(*TPMSQuoteInfo), nil } return nil, fmt.Errorf("did not contain quote (selector value was %v)", u.selector) } // CommandAudit returns the 'commandAudit' member of the union. func (u *TPMUAttest) CommandAudit() (*TPMSCommandAuditInfo, error) { if u.selector == TPMSTAttestCommandAudit { return u.contents.(*TPMSCommandAuditInfo), nil } return nil, fmt.Errorf("did not contain commandAudit (selector value was %v)", u.selector) } // SessionAudit returns the 'sessionAudit' member of the union. func (u *TPMUAttest) SessionAudit() (*TPMSSessionAuditInfo, error) { if u.selector == TPMSTAttestSessionAudit { return u.contents.(*TPMSSessionAuditInfo), nil } return nil, fmt.Errorf("did not contain sessionAudit (selector value was %v)", u.selector) } // Time returns the 'time' member of the union. func (u *TPMUAttest) Time() (*TPMSTimeAttestInfo, error) { if u.selector == TPMSTAttestTime { return u.contents.(*TPMSTimeAttestInfo), nil } return nil, fmt.Errorf("did not contain time (selector value was %v)", u.selector) } // NV returns the 'nv' member of the union. func (u *TPMUAttest) NV() (*TPMSNVCertifyInfo, error) { if u.selector == TPMSTAttestNV { return u.contents.(*TPMSNVCertifyInfo), nil } return nil, fmt.Errorf("did not contain nv (selector value was %v)", u.selector) } // NVDigest returns the 'nvDigest' member of the union. func (u *TPMUAttest) NVDigest() (*TPMSNVDigestCertifyInfo, error) { if u.selector == TPMSTAttestNVDigest { return u.contents.(*TPMSNVDigestCertifyInfo), nil } return nil, fmt.Errorf("did not contain nvDigest (selector value was %v)", u.selector) } // TPMSAttest represents a TPMS_ATTEST. // See definition in Part 2: Structures, section 10.12.12. type TPMSAttest struct { marshalByReflection // the indication that this structure was created by a TPM (always TPM_GENERATED_VALUE) Magic TPMGenerated `gotpm:"check"` // type of the attestation structure Type TPMISTAttest // Qualified Name of the signing key QualifiedSigner TPM2BName // external information supplied by caller ExtraData TPM2BData // Clock, resetCount, restartCount, and Safe ClockInfo TPMSClockInfo // TPM-vendor-specific value identifying the version number of the firmware FirmwareVersion uint64 // the type-specific attestation information Attested TPMUAttest `gotpm:"tag=Type"` } // TPM2BAttest represents a TPM2B_ATTEST. // See definition in Part 2: Structures, section 10.12.13. type TPM2BAttest = TPM2B[TPMSAttest, *TPMSAttest] // TPMSAuthCommand represents a TPMS_AUTH_COMMAND. // See definition in Part 2: Structures, section 10.13.2. type TPMSAuthCommand struct { marshalByReflection Handle TPMISHAuthSession Nonce TPM2BNonce Attributes TPMASession Authorization TPM2BData } // TPMSAuthResponse represents a TPMS_AUTH_RESPONSE. // See definition in Part 2: Structures, section 10.13.3. type TPMSAuthResponse struct { marshalByReflection Nonce TPM2BNonce Attributes TPMASession Authorization TPM2BData } // TPMUSymKeyBits represents a TPMU_SYM_KEY_BITS. // See definition in Part 2: Structures, section 11.1.3. type TPMUSymKeyBits struct { selector TPMAlgID contents Marshallable } // SymKeyBitsContents is a type constraint representing the possible contents of TPMUSymKeyBits. type SymKeyBitsContents interface { TPMKeyBits | TPMAlgID } // create implements the unmarshallableWithHint interface. func (u *TPMUSymKeyBits) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgAES: var contents boxed[TPMKeyBits] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents boxed[TPMAlgID] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSymKeyBits) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgAES: var contents boxed[TPMKeyBits] if u.contents != nil { contents = *u.contents.(*boxed[TPMKeyBits]) } return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents boxed[TPMAlgID] if u.contents != nil { contents = *u.contents.(*boxed[TPMAlgID]) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSymKeyBits instantiates a TPMUSymKeyBits with the given contents. func NewTPMUSymKeyBits[C SymKeyBitsContents](selector TPMAlgID, contents C) TPMUSymKeyBits { boxed := box(&contents) return TPMUSymKeyBits{ selector: selector, contents: &boxed, } } // AES returns the 'aes' member of the union. func (u *TPMUSymKeyBits) AES() (*TPMKeyBits, error) { if u.selector == TPMAlgAES { value := u.contents.(*boxed[TPMKeyBits]).unbox() return value, nil } return nil, fmt.Errorf("did not contain aes (selector value was %v)", u.selector) } // XOR returns the 'xor' member of the union. func (u *TPMUSymKeyBits) XOR() (*TPMAlgID, error) { if u.selector == TPMAlgXOR { value := u.contents.(*boxed[TPMAlgID]).unbox() return value, nil } return nil, fmt.Errorf("did not contain xor (selector value was %v)", u.selector) } // TPMUSymMode represents a TPMU_SYM_MODE. // See definition in Part 2: Structures, section 11.1.4. type TPMUSymMode struct { selector TPMAlgID contents Marshallable } // SymModeContents is a type constraint representing the possible contents of TPMUSymMode. type SymModeContents interface { TPMIAlgSymMode | TPMSEmpty } // create implements the unmarshallableWithHint interface. func (u *TPMUSymMode) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgAES: var contents boxed[TPMAlgID] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents boxed[TPMSEmpty] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSymMode) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgAES: var contents boxed[TPMAlgID] if u.contents != nil { contents = *u.contents.(*boxed[TPMAlgID]) } return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents boxed[TPMSEmpty] if u.contents != nil { contents = *u.contents.(*boxed[TPMSEmpty]) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSymMode instantiates a TPMUSymMode with the given contents. func NewTPMUSymMode[C SymModeContents](selector TPMAlgID, contents C) TPMUSymMode { boxed := box(&contents) return TPMUSymMode{ selector: selector, contents: &boxed, } } // AES returns the 'aes' member of the union. func (u *TPMUSymMode) AES() (*TPMIAlgSymMode, error) { if u.selector == TPMAlgAES { value := u.contents.(*boxed[TPMIAlgSymMode]).unbox() return value, nil } return nil, fmt.Errorf("did not contain aes (selector value was %v)", u.selector) } // TPMUSymDetails represents a TPMU_SYM_DETAILS. // See definition in Part 2: Structures, section 11.1.5. type TPMUSymDetails struct { selector TPMAlgID contents Marshallable } // SymDetailsContents is a type constraint representing the possible contents of TPMUSymDetails. type SymDetailsContents interface { TPMSEmpty } // create implements the unmarshallableWithHint interface. func (u *TPMUSymDetails) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgAES: var contents boxed[TPMSEmpty] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents boxed[TPMSEmpty] u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSymDetails) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgAES, TPMAlgXOR: var contents boxed[TPMSEmpty] if u.contents != nil { contents = *u.contents.(*boxed[TPMSEmpty]) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSymDetails instantiates a TPMUSymDetails with the given contents. func NewTPMUSymDetails[C SymDetailsContents](selector TPMAlgID, contents C) TPMUSymMode { boxed := box(&contents) return TPMUSymMode{ selector: selector, contents: &boxed, } } // TPMTSymDef represents a TPMT_SYM_DEF. // See definition in Part 2: Structures, section 11.1.6. type TPMTSymDef struct { marshalByReflection // indicates a symmetric algorithm Algorithm TPMIAlgSym `gotpm:"nullable"` // the key size KeyBits TPMUSymKeyBits `gotpm:"tag=Algorithm"` // the mode for the key Mode TPMUSymMode `gotpm:"tag=Algorithm"` // contains the additional algorithm details Details TPMUSymDetails `gotpm:"tag=Algorithm"` } // TPMTSymDefObject represents a TPMT_SYM_DEF_OBJECT. // See definition in Part 2: Structures, section 11.1.7. type TPMTSymDefObject struct { marshalByReflection // selects a symmetric block cipher // When used in the parameter area of a parent object, this shall // be a supported block cipher and not TPM_ALG_NULL Algorithm TPMIAlgSymObject `gotpm:"nullable"` // the key size KeyBits TPMUSymKeyBits `gotpm:"tag=Algorithm"` // default mode // When used in the parameter area of a parent object, this shall // be TPM_ALG_CFB. Mode TPMUSymMode `gotpm:"tag=Algorithm"` // contains the additional algorithm details, if any Details TPMUSymDetails `gotpm:"tag=Algorithm"` } // TPM2BSymKey represents a TPM2B_SYM_KEY. // See definition in Part 2: Structures, section 11.1.8. type TPM2BSymKey TPM2BData // TPMSSymCipherParms represents a TPMS_SYMCIPHER_PARMS. // See definition in Part 2: Structures, section 11.1.9. type TPMSSymCipherParms struct { marshalByReflection // a symmetric block cipher Sym TPMTSymDefObject } // TPM2BLabel represents a TPM2B_LABEL. // See definition in Part 2: Structures, section 11.1.10. type TPM2BLabel TPM2BData // TPMSDerive represents a TPMS_DERIVE. // See definition in Part 2: Structures, section 11.1.11. type TPMSDerive struct { marshalByReflection Label TPM2BLabel Context TPM2BLabel } // TPM2BDerive represents a TPM2B_DERIVE. // See definition in Part 2: Structures, section 11.1.12. type TPM2BDerive = TPM2B[TPMSDerive, *TPMSDerive] // TPMUSensitiveCreate represents a TPMU_SENSITIVE_CREATE. // See definition in Part 2: Structures, section 11.1.13. type TPMUSensitiveCreate struct { contents Marshallable } // SensitiveCreateContents is a type constraint representing the possible contents of TPMUSensitiveCreate. type SensitiveCreateContents interface { Marshallable *TPM2BDerive | *TPM2BSensitiveData } // marshal implements the Marshallable interface. func (u TPMUSensitiveCreate) marshal(buf *bytes.Buffer) { if u.contents != nil { buf.Write(Marshal(u.contents)) } else { // If this is a zero-valued structure, marshal a default TPM2BSensitiveData. var defaultValue TPM2BSensitiveData buf.Write(Marshal(&defaultValue)) } } // NewTPMUSensitiveCreate instantiates a TPMUSensitiveCreate with the given contents. func NewTPMUSensitiveCreate[C SensitiveCreateContents](contents C) TPMUSensitiveCreate { return TPMUSensitiveCreate{contents: contents} } // TPM2BSensitiveData represents a TPM2B_SENSITIVE_DATA. // See definition in Part 2: Structures, section 11.1.14. type TPM2BSensitiveData TPM2BData // TPMSSensitiveCreate represents a TPMS_SENSITIVE_CREATE. // See definition in Part 2: Structures, section 11.1.15. type TPMSSensitiveCreate struct { marshalByReflection // the USER auth secret value. UserAuth TPM2BAuth // data to be sealed, a key, or derivation values. Data TPMUSensitiveCreate } // TPM2BSensitiveCreate represents a TPM2B_SENSITIVE_CREATE. // See definition in Part 2: Structures, section 11.1.16. // This is a structure instead of an alias to TPM2B[TPMSSensitiveCreate], // because it has custom marshalling logic for zero-valued parameters. type TPM2BSensitiveCreate struct { Sensitive *TPMSSensitiveCreate } // Quirk: When this structure is zero-valued, we need to marshal // a 2B-wrapped zero-valued TPMS_SENSITIVE_CREATE instead of // [0x00, 0x00] (a zero-valued 2B). func (c TPM2BSensitiveCreate) marshal(buf *bytes.Buffer) { var marshalled TPM2B[TPMSSensitiveCreate, *TPMSSensitiveCreate] if c.Sensitive != nil { marshalled = New2B(*c.Sensitive) } else { // If no value was provided (i.e., this is a zero-valued structure), // provide an 2B containing a zero-valued TPMS_SensitiveCreate. marshalled = New2B(TPMSSensitiveCreate{ Data: NewTPMUSensitiveCreate(&TPM2BSensitiveData{}), }) } marshalled.marshal(buf) } // TPMSSchemeHash represents a TPMS_SCHEME_HASH. // See definition in Part 2: Structures, section 11.1.17. type TPMSSchemeHash struct { marshalByReflection // the hash algorithm used to digest the message HashAlg TPMIAlgHash } // TPMSSchemeECDAA represents a TPMS_SCHEME_ECDAA. // See definition in Part 2: Structures, section 11.1.18. type TPMSSchemeECDAA struct { marshalByReflection // the hash algorithm used to digest the message HashAlg TPMIAlgHash // the counter value that is used between TPM2_Commit() // and the sign operation Count uint16 } // TPMIAlgKeyedHashScheme represents a TPMI_ALG_KEYEDHASH_SCHEME. // See definition in Part 2: Structures, section 11.1.19. type TPMIAlgKeyedHashScheme = TPMAlgID // TPMSSchemeHMAC represents a TPMS_SCHEME_HMAC. // See definition in Part 2: Structures, section 11.1.20. type TPMSSchemeHMAC TPMSSchemeHash // TPMSSchemeXOR represents a TPMS_SCHEME_XOR. // See definition in Part 2: Structures, section 11.1.21. type TPMSSchemeXOR struct { marshalByReflection // the hash algorithm used to digest the message HashAlg TPMIAlgHash // the key derivation function KDF TPMIAlgKDF } // TPMUSchemeKeyedHash represents a TPMU_SCHEME_KEYEDHASH. // See definition in Part 2: Structures, section 11.1.22. type TPMUSchemeKeyedHash struct { selector TPMAlgID contents Marshallable } // SchemeKeyedHashContents is a type constraint representing the possible contents of TPMUSchemeKeyedHash. type SchemeKeyedHashContents interface { Marshallable *TPMSSchemeHMAC | *TPMSSchemeXOR } // create implements the unmarshallableWithHint interface. func (u *TPMUSchemeKeyedHash) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMSSchemeHMAC u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents TPMSSchemeXOR u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSchemeKeyedHash) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMSSchemeHMAC if u.contents != nil { contents = *u.contents.(*TPMSSchemeHMAC) } return reflect.ValueOf(&contents), nil case TPMAlgXOR: var contents TPMSSchemeXOR if u.contents != nil { contents = *u.contents.(*TPMSSchemeXOR) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSchemeKeyedHash instantiates a TPMUSchemeKeyedHash with the given contents. func NewTPMUSchemeKeyedHash[C SchemeKeyedHashContents](selector TPMAlgID, contents C) TPMUSchemeKeyedHash { return TPMUSchemeKeyedHash{ selector: selector, contents: contents, } } // HMAC returns the 'hmac' member of the union. func (u *TPMUSchemeKeyedHash) HMAC() (*TPMSSchemeHMAC, error) { if u.selector == TPMAlgHMAC { value := u.contents.(*TPMSSchemeHMAC) return value, nil } return nil, fmt.Errorf("did not contain hmac (selector value was %v)", u.selector) } // XOR returns the 'xor' member of the union. func (u *TPMUSchemeKeyedHash) XOR() (*TPMSSchemeXOR, error) { if u.selector == TPMAlgXOR { value := u.contents.(*TPMSSchemeXOR) return value, nil } return nil, fmt.Errorf("did not contain xor (selector value was %v)", u.selector) } // TPMTKeyedHashScheme represents a TPMT_KEYEDHASH_SCHEME. // See definition in Part 2: Structures, section 11.1.23. type TPMTKeyedHashScheme struct { marshalByReflection Scheme TPMIAlgKeyedHashScheme `gotpm:"nullable"` Details TPMUSchemeKeyedHash `gotpm:"tag=Scheme"` } // TPMSSigSchemeRSASSA represents a TPMS_SIG_SCHEME_RSASSA. // See definition in Part 2: Structures, section 11.2.1.2. type TPMSSigSchemeRSASSA TPMSSchemeHash // TPMSSigSchemeRSAPSS represents a TPMS_SIG_SCHEME_RSAPSS. // See definition in Part 2: Structures, section 11.2.1.2. type TPMSSigSchemeRSAPSS TPMSSchemeHash // TPMSSigSchemeECDSA represents a TPMS_SIG_SCHEME_ECDSA. // See definition in Part 2: Structures, section 11.2.1.3. type TPMSSigSchemeECDSA TPMSSchemeHash // TPMUSigScheme represents a TPMU_SIG_SCHEME. // See definition in Part 2: Structures, section 11.2.1.4. type TPMUSigScheme struct { selector TPMAlgID contents Marshallable } // SigSchemeContents is a type constraint representing the possible contents of TPMUSigScheme. type SigSchemeContents interface { Marshallable *TPMSSchemeHMAC | *TPMSSchemeHash | *TPMSSchemeECDAA } // create implements the unmarshallableWithHint interface. func (u *TPMUSigScheme) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMSSchemeHMAC u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSASSA, TPMAlgRSAPSS, TPMAlgECDSA: var contents TPMSSchemeHash u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDAA: var contents TPMSSchemeECDAA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSigScheme) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMSSchemeHMAC if u.contents != nil { contents = *u.contents.(*TPMSSchemeHMAC) } return reflect.ValueOf(&contents), nil case TPMAlgRSASSA, TPMAlgRSAPSS, TPMAlgECDSA: var contents TPMSSchemeHash if u.contents != nil { contents = *u.contents.(*TPMSSchemeHash) } return reflect.ValueOf(&contents), nil case TPMAlgECDAA: var contents TPMSSchemeECDAA if u.contents != nil { contents = *u.contents.(*TPMSSchemeECDAA) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSigScheme instantiates a TPMUSigScheme with the given contents. func NewTPMUSigScheme[C SigSchemeContents](selector TPMAlgID, contents C) TPMUSigScheme { return TPMUSigScheme{ selector: selector, contents: contents, } } // HMAC returns the 'hmac' member of the union. func (u *TPMUSigScheme) HMAC() (*TPMSSchemeHMAC, error) { if u.selector == TPMAlgHMAC { return u.contents.(*TPMSSchemeHMAC), nil } return nil, fmt.Errorf("did not contain hmac (selector value was %v)", u.selector) } // RSASSA returns the 'rsassa' member of the union. func (u *TPMUSigScheme) RSASSA() (*TPMSSchemeHash, error) { if u.selector == TPMAlgRSASSA { return u.contents.(*TPMSSchemeHash), nil } return nil, fmt.Errorf("did not contain rsassa (selector value was %v)", u.selector) } // RSAPSS returns the 'rsapss' member of the union. func (u *TPMUSigScheme) RSAPSS() (*TPMSSchemeHash, error) { if u.selector == TPMAlgRSAPSS { return u.contents.(*TPMSSchemeHash), nil } return nil, fmt.Errorf("did not contain rsapss (selector value was %v)", u.selector) } // ECDSA returns the 'ecdsa' member of the union. func (u *TPMUSigScheme) ECDSA() (*TPMSSchemeHash, error) { if u.selector == TPMAlgECDSA { return u.contents.(*TPMSSchemeHash), nil } return nil, fmt.Errorf("did not contain ecdsa (selector value was %v)", u.selector) } // ECDAA returns the 'ecdaa' member of the union. func (u *TPMUSigScheme) ECDAA() (*TPMSSchemeECDAA, error) { if u.selector == TPMAlgECDAA { return u.contents.(*TPMSSchemeECDAA), nil } return nil, fmt.Errorf("did not contain ecdaa (selector value was %v)", u.selector) } // TPMTSigScheme represents a TPMT_SIG_SCHEME. // See definition in Part 2: Structures, section 11.2.1.5. type TPMTSigScheme struct { marshalByReflection Scheme TPMIAlgSigScheme `gotpm:"nullable"` Details TPMUSigScheme `gotpm:"tag=Scheme"` } // TPMSEncSchemeRSAES represents a TPMS_ENC_SCHEME_RSAES. // See definition in Part 2: Structures, section 11.2.2.2. type TPMSEncSchemeRSAES TPMSEmpty // TPMSEncSchemeOAEP represents a TPMS_ENC_SCHEME_OAEP. // See definition in Part 2: Structures, section 11.2.2.2. type TPMSEncSchemeOAEP TPMSSchemeHash // TPMSKeySchemeECDH represents a TPMS_KEY_SCHEME_ECDH. // See definition in Part 2: Structures, section 11.2.2.3. type TPMSKeySchemeECDH TPMSSchemeHash // TPMSKDFSchemeMGF1 represents a TPMS_KDF_SCHEME_MGF1. // See definition in Part 2: Structures, section 11.2.3.1. type TPMSKDFSchemeMGF1 TPMSSchemeHash // TPMSKDFSchemeECDH represents a TPMS_KDF_SCHEME_ECDH. // See definition in Part 2: Structures, section 11.2.3.1. type TPMSKDFSchemeECDH TPMSSchemeHash // TPMSKDFSchemeKDF1SP80056A represents a TPMS_KDF_SCHEME_KDF1SP80056A. // See definition in Part 2: Structures, section 11.2.3.1. type TPMSKDFSchemeKDF1SP80056A TPMSSchemeHash // TPMSKDFSchemeKDF2 represents a TPMS_KDF_SCHEME_KDF2. // See definition in Part 2: Structures, section 11.2.3.1. type TPMSKDFSchemeKDF2 TPMSSchemeHash // TPMSKDFSchemeKDF1SP800108 represents a TPMS_KDF_SCHEME_KDF1SP800108. // See definition in Part 2: Structures, section 11.2.3.1. type TPMSKDFSchemeKDF1SP800108 TPMSSchemeHash // TPMUKDFScheme represents a TPMU_KDF_SCHEME. // See definition in Part 2: Structures, section 11.2.3.2. type TPMUKDFScheme struct { selector TPMAlgID contents Marshallable } // KDFSchemeContents is a type constraint representing the possible contents of TPMUKDFScheme. type KDFSchemeContents interface { Marshallable *TPMSKDFSchemeMGF1 | *TPMSKDFSchemeECDH | *TPMSKDFSchemeKDF1SP80056A | *TPMSKDFSchemeKDF2 | *TPMSKDFSchemeKDF1SP800108 } // create implements the unmarshallableWithHint interface. func (u *TPMUKDFScheme) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgMGF1: var contents TPMSKDFSchemeMGF1 u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDH: var contents TPMSKDFSchemeECDH u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgKDF1SP80056A: var contents TPMSKDFSchemeKDF1SP80056A u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgKDF2: var contents TPMSKDFSchemeKDF2 u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgKDF1SP800108: var contents TPMSKDFSchemeKDF1SP800108 u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUKDFScheme) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgMGF1: var contents TPMSKDFSchemeMGF1 if u.contents != nil { contents = *u.contents.(*TPMSKDFSchemeMGF1) } return reflect.ValueOf(&contents), nil case TPMAlgECDH: var contents TPMSKDFSchemeECDH if u.contents != nil { contents = *u.contents.(*TPMSKDFSchemeECDH) } return reflect.ValueOf(&contents), nil case TPMAlgKDF1SP80056A: var contents TPMSKDFSchemeKDF1SP80056A if u.contents != nil { contents = *u.contents.(*TPMSKDFSchemeKDF1SP80056A) } return reflect.ValueOf(&contents), nil case TPMAlgKDF2: var contents TPMSKDFSchemeKDF2 if u.contents != nil { contents = *u.contents.(*TPMSKDFSchemeKDF2) } return reflect.ValueOf(&contents), nil case TPMAlgKDF1SP800108: var contents TPMSKDFSchemeKDF1SP800108 if u.contents != nil { contents = *u.contents.(*TPMSKDFSchemeKDF1SP800108) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUKDFScheme instantiates a TPMUKDFScheme with the given contents. func NewTPMUKDFScheme[C KDFSchemeContents](selector TPMAlgID, contents C) TPMUKDFScheme { return TPMUKDFScheme{ selector: selector, contents: contents, } } // MGF1 returns the 'mgf1' member of the union. func (u *TPMUKDFScheme) MGF1() (*TPMSKDFSchemeMGF1, error) { if u.selector == TPMAlgMGF1 { return u.contents.(*TPMSKDFSchemeMGF1), nil } return nil, fmt.Errorf("did not contain mgf1 (selector value was %v)", u.selector) } // ECDH returns the 'ecdh' member of the union. func (u *TPMUKDFScheme) ECDH() (*TPMSKDFSchemeECDH, error) { if u.selector == TPMAlgECDH { return u.contents.(*TPMSKDFSchemeECDH), nil } return nil, fmt.Errorf("did not contain ecdh (selector value was %v)", u.selector) } // KDF1SP80056A returns the 'kdf1sp80056a' member of the union. func (u *TPMUKDFScheme) KDF1SP80056A() (*TPMSKDFSchemeKDF1SP80056A, error) { if u.selector == TPMAlgMGF1 { return u.contents.(*TPMSKDFSchemeKDF1SP80056A), nil } return nil, fmt.Errorf("did not contain kdf1sp80056a (selector value was %v)", u.selector) } // KDF2 returns the 'kdf2' member of the union. func (u *TPMUKDFScheme) KDF2() (*TPMSKDFSchemeKDF2, error) { if u.selector == TPMAlgMGF1 { return u.contents.(*TPMSKDFSchemeKDF2), nil } return nil, fmt.Errorf("did not contain mgf1 (selector value was %v)", u.selector) } // KDF1SP800108 returns the 'kdf1sp800108' member of the union. func (u *TPMUKDFScheme) KDF1SP800108() (*TPMSKDFSchemeKDF1SP800108, error) { if u.selector == TPMAlgMGF1 { return u.contents.(*TPMSKDFSchemeKDF1SP800108), nil } return nil, fmt.Errorf("did not contain kdf1sp800108 (selector value was %v)", u.selector) } // TPMTKDFScheme represents a TPMT_KDF_SCHEME. // See definition in Part 2: Structures, section 11.2.3.3. type TPMTKDFScheme struct { marshalByReflection // scheme selector Scheme TPMIAlgKDF `gotpm:"nullable"` // scheme parameters Details TPMUKDFScheme `gotpm:"tag=Scheme"` } // TPMUAsymScheme represents a TPMU_ASYM_SCHEME. // See definition in Part 2: Structures, section 11.2.3.5. type TPMUAsymScheme struct { selector TPMAlgID contents Marshallable } // AsymSchemeContents is a type constraint representing the possible contents of TPMUAsymScheme. type AsymSchemeContents interface { Marshallable *TPMSSigSchemeRSASSA | *TPMSEncSchemeRSAES | *TPMSSigSchemeRSAPSS | *TPMSEncSchemeOAEP | *TPMSSigSchemeECDSA | *TPMSKeySchemeECDH | *TPMSSchemeECDAA } // create implements the unmarshallableWithHint interface. func (u *TPMUAsymScheme) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgRSASSA: var contents TPMSSigSchemeRSASSA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSAES: var contents TPMSEncSchemeRSAES u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSAPSS: var contents TPMSSigSchemeRSAPSS u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgOAEP: var contents TPMSEncSchemeOAEP u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDSA: var contents TPMSSigSchemeECDSA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDH: var contents TPMSKeySchemeECDH u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDAA: var contents TPMSSchemeECDAA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUAsymScheme) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgRSASSA: var contents TPMSSigSchemeRSASSA if u.contents != nil { contents = *u.contents.(*TPMSSigSchemeRSASSA) } return reflect.ValueOf(&contents), nil case TPMAlgRSAES: var contents TPMSEncSchemeRSAES if u.contents != nil { contents = *u.contents.(*TPMSEncSchemeRSAES) } return reflect.ValueOf(&contents), nil case TPMAlgRSAPSS: var contents TPMSSigSchemeRSAPSS if u.contents != nil { contents = *u.contents.(*TPMSSigSchemeRSAPSS) } return reflect.ValueOf(&contents), nil case TPMAlgOAEP: var contents TPMSEncSchemeOAEP if u.contents != nil { contents = *u.contents.(*TPMSEncSchemeOAEP) } return reflect.ValueOf(&contents), nil case TPMAlgECDSA: var contents TPMSSigSchemeECDSA if u.contents != nil { contents = *u.contents.(*TPMSSigSchemeECDSA) } return reflect.ValueOf(&contents), nil case TPMAlgECDH: var contents TPMSKeySchemeECDH if u.contents != nil { contents = *u.contents.(*TPMSKeySchemeECDH) } return reflect.ValueOf(&contents), nil case TPMAlgECDAA: var contents TPMSSchemeECDAA if u.contents != nil { contents = *u.contents.(*TPMSSchemeECDAA) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUAsymScheme instantiates a TPMUAsymScheme with the given contents. func NewTPMUAsymScheme[C AsymSchemeContents](selector TPMAlgID, contents C) TPMUAsymScheme { return TPMUAsymScheme{ selector: selector, contents: contents, } } // RSASSA returns the 'rsassa' member of the union. func (u *TPMUAsymScheme) RSASSA() (*TPMSSigSchemeRSASSA, error) { if u.selector == TPMAlgRSASSA { return u.contents.(*TPMSSigSchemeRSASSA), nil } return nil, fmt.Errorf("did not contain rsassa (selector value was %v)", u.selector) } // RSAES returns the 'rsaes' member of the union. func (u *TPMUAsymScheme) RSAES() (*TPMSEncSchemeRSAES, error) { if u.selector == TPMAlgRSAES { return u.contents.(*TPMSEncSchemeRSAES), nil } return nil, fmt.Errorf("did not contain rsaes (selector value was %v)", u.selector) } // RSAPSS returns the 'rsapss' member of the union. func (u *TPMUAsymScheme) RSAPSS() (*TPMSSigSchemeRSAPSS, error) { if u.selector == TPMAlgRSAPSS { return u.contents.(*TPMSSigSchemeRSAPSS), nil } return nil, fmt.Errorf("did not contain rsapss (selector value was %v)", u.selector) } // OAEP returns the 'oaep' member of the union. func (u *TPMUAsymScheme) OAEP() (*TPMSEncSchemeOAEP, error) { if u.selector == TPMAlgOAEP { return u.contents.(*TPMSEncSchemeOAEP), nil } return nil, fmt.Errorf("did not contain oaep (selector value was %v)", u.selector) } // ECDSA returns the 'ecdsa' member of the union. func (u *TPMUAsymScheme) ECDSA() (*TPMSSigSchemeECDSA, error) { if u.selector == TPMAlgECDSA { return u.contents.(*TPMSSigSchemeECDSA), nil } return nil, fmt.Errorf("did not contain rsassa (selector value was %v)", u.selector) } // ECDH returns the 'ecdh' member of the union. func (u *TPMUAsymScheme) ECDH() (*TPMSKeySchemeECDH, error) { if u.selector == TPMAlgRSASSA { return u.contents.(*TPMSKeySchemeECDH), nil } return nil, fmt.Errorf("did not contain ecdh (selector value was %v)", u.selector) } // ECDAA returns the 'ecdaa' member of the union. func (u *TPMUAsymScheme) ECDAA() (*TPMSSchemeECDAA, error) { if u.selector == TPMAlgECDAA { return u.contents.(*TPMSSchemeECDAA), nil } return nil, fmt.Errorf("did not contain rsassa (selector value was %v)", u.selector) } // TPMIAlgRSAScheme represents a TPMI_ALG_RSA_SCHEME. // See definition in Part 2: Structures, section 11.2.4.1. type TPMIAlgRSAScheme = TPMAlgID // TPMTRSAScheme represents a TPMT_RSA_SCHEME. // See definition in Part 2: Structures, section 11.2.4.2. type TPMTRSAScheme struct { marshalByReflection // scheme selector Scheme TPMIAlgRSAScheme `gotpm:"nullable"` // scheme parameters Details TPMUAsymScheme `gotpm:"tag=Scheme"` } // TPMIAlgRSADecrypt represents a TPMI_ALG_RSA_DECRYPT. // See definition in Part 2: Structures, section 11.2.4.3. type TPMIAlgRSADecrypt = TPMAlgID // TPMTRSADecrypt represents a TPMT_RSA_DECRYPT. // See definition in Part 2: Structures, section 11.2.4.4. type TPMTRSADecrypt struct { marshalByReflection // scheme selector Scheme TPMIAlgRSADecrypt `gotpm:"nullable"` // scheme parameters Details TPMUAsymScheme `gotpm:"tag=Scheme"` } // TPM2BPublicKeyRSA represents a TPM2B_PUBLIC_KEY_RSA. // See definition in Part 2: Structures, section 11.2.4.5. type TPM2BPublicKeyRSA TPM2BData // TPMIRSAKeyBits represents a TPMI_RSA_KEY_BITS. // See definition in Part 2: Structures, section 11.2.4.6. type TPMIRSAKeyBits = TPMKeyBits // TPM2BPrivateKeyRSA representsa a TPM2B_PRIVATE_KEY_RSA. // See definition in Part 2: Structures, section 11.2.4.7. type TPM2BPrivateKeyRSA TPM2BData // TPM2BECCParameter represents a TPM2B_ECC_PARAMETER. // See definition in Part 2: Structures, section 11.2.5.1. type TPM2BECCParameter TPM2BData // TPMSECCPoint represents a TPMS_ECC_POINT. // See definition in Part 2: Structures, section 11.2.5.2. type TPMSECCPoint struct { marshalByReflection // X coordinate X TPM2BECCParameter // Y coordinate Y TPM2BECCParameter } // TPM2BECCPoint represents a TPM2B_ECC_POINT. // See definition in Part 2: Structures, section 11.2.5.3. type TPM2BECCPoint = TPM2B[TPMSECCPoint, *TPMSECCPoint] // TPMIAlgECCScheme represents a TPMI_ALG_ECC_SCHEME. // See definition in Part 2: Structures, section 11.2.5.4. type TPMIAlgECCScheme = TPMAlgID // TPMIECCCurve represents a TPMI_ECC_CURVE. // See definition in Part 2: Structures, section 11.2.5.5. type TPMIECCCurve = TPMECCCurve // TPMTECCScheme represents a TPMT_ECC_SCHEME. // See definition in Part 2: Structures, section 11.2.5.6. type TPMTECCScheme struct { marshalByReflection // scheme selector Scheme TPMIAlgECCScheme `gotpm:"nullable"` // scheme parameters Details TPMUAsymScheme `gotpm:"tag=Scheme"` } // TPMSSignatureRSA represents a TPMS_SIGNATURE_RSA. // See definition in Part 2: Structures, section 11.3.1. type TPMSSignatureRSA struct { marshalByReflection // the hash algorithm used to digest the message Hash TPMIAlgHash // The signature is the size of a public key. Sig TPM2BPublicKeyRSA } // TPMSSignatureECC represents a TPMS_SIGNATURE_ECC. // See definition in Part 2: Structures, section 11.3.2. type TPMSSignatureECC struct { marshalByReflection // the hash algorithm used in the signature process Hash TPMIAlgHash SignatureR TPM2BECCParameter SignatureS TPM2BECCParameter } // TPMUSignature represents a TPMU_SIGNATURE. // See definition in Part 2: Structures, section 11.3.3. type TPMUSignature struct { selector TPMAlgID contents Marshallable } // SignatureContents is a type constraint representing the possible contents of TPMUSignature. type SignatureContents interface { Marshallable *TPMTHA | *TPMSSignatureRSA | *TPMSSignatureECC } // create implements the unmarshallableWithHint interface. func (u *TPMUSignature) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMTHA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSASSA, TPMAlgRSAPSS: var contents TPMSSignatureRSA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECDSA, TPMAlgECDAA: var contents TPMSSignatureECC u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSignature) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgHMAC: var contents TPMTHA if u.contents != nil { contents = *u.contents.(*TPMTHA) } return reflect.ValueOf(&contents), nil case TPMAlgRSASSA, TPMAlgRSAPSS: var contents TPMSSignatureRSA if u.contents != nil { contents = *u.contents.(*TPMSSignatureRSA) } return reflect.ValueOf(&contents), nil case TPMAlgECDSA, TPMAlgECDAA: var contents TPMSSignatureECC if u.contents != nil { contents = *u.contents.(*TPMSSignatureECC) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSignature instantiates a TPMUSignature with the given contents. func NewTPMUSignature[C SignatureContents](selector TPMAlgID, contents C) TPMUSignature { return TPMUSignature{ selector: selector, contents: contents, } } // HMAC returns the 'hmac' member of the union. func (u *TPMUSignature) HMAC() (*TPMTHA, error) { if u.selector == TPMAlgHMAC { return u.contents.(*TPMTHA), nil } return nil, fmt.Errorf("did not contain hmac (selector value was %v)", u.selector) } // RSASSA returns the 'rsassa' member of the union. func (u *TPMUSignature) RSASSA() (*TPMSSignatureRSA, error) { if u.selector == TPMAlgRSASSA { return u.contents.(*TPMSSignatureRSA), nil } return nil, fmt.Errorf("did not contain rsassa (selector value was %v)", u.selector) } // RSAPSS returns the 'rsapss' member of the union. func (u *TPMUSignature) RSAPSS() (*TPMSSignatureRSA, error) { if u.selector == TPMAlgRSAPSS { return u.contents.(*TPMSSignatureRSA), nil } return nil, fmt.Errorf("did not contain rsapss (selector value was %v)", u.selector) } // ECDSA returns the 'ecdsa' member of the union. func (u *TPMUSignature) ECDSA() (*TPMSSignatureECC, error) { if u.selector == TPMAlgECDSA { return u.contents.(*TPMSSignatureECC), nil } return nil, fmt.Errorf("did not contain ecdsa (selector value was %v)", u.selector) } // ECDAA returns the 'ecdaa' member of the union. func (u *TPMUSignature) ECDAA() (*TPMSSignatureECC, error) { if u.selector == TPMAlgECDAA { return u.contents.(*TPMSSignatureECC), nil } return nil, fmt.Errorf("did not contain ecdaa (selector value was %v)", u.selector) } // TPMTSignature represents a TPMT_SIGNATURE. // See definition in Part 2: Structures, section 11.3.4. type TPMTSignature struct { marshalByReflection // selector of the algorithm used to construct the signature SigAlg TPMIAlgSigScheme `gotpm:"nullable"` // This shall be the actual signature information. Signature TPMUSignature `gotpm:"tag=SigAlg"` } // TPM2BEncryptedSecret represents a TPM2B_ENCRYPTED_SECRET. // See definition in Part 2: Structures, section 11.4.33. type TPM2BEncryptedSecret TPM2BData // TPMIAlgPublic represents a TPMI_ALG_PUBLIC. // See definition in Part 2: Structures, section 12.2.2. type TPMIAlgPublic = TPMAlgID // TPMUPublicID represents a TPMU_PUBLIC_ID. // See definition in Part 2: Structures, section 12.2.3.2. type TPMUPublicID struct { selector TPMAlgID contents Marshallable } // PublicIDContents is a type constraint representing the possible contents of TPMUPublicID. type PublicIDContents interface { Marshallable *TPM2BDigest | *TPM2BPublicKeyRSA | *TPMSECCPoint } // create implements the unmarshallableWithHint interface. func (u *TPMUPublicID) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgKeyedHash: var contents TPM2BDigest u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPM2BDigest u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSA: var contents TPM2BPublicKeyRSA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPMSECCPoint u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUPublicID) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgKeyedHash: var contents TPM2BDigest if u.contents != nil { contents = *u.contents.(*TPM2BDigest) } return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPM2BDigest if u.contents != nil { contents = *u.contents.(*TPM2BDigest) } return reflect.ValueOf(&contents), nil case TPMAlgRSA: var contents TPM2BPublicKeyRSA if u.contents != nil { contents = *u.contents.(*TPM2BPublicKeyRSA) } return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPMSECCPoint if u.contents != nil { contents = *u.contents.(*TPMSECCPoint) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUPublicID instantiates a TPMUPublicID with the given contents. func NewTPMUPublicID[C PublicIDContents](selector TPMAlgID, contents C) TPMUPublicID { return TPMUPublicID{ selector: selector, contents: contents, } } // KeyedHash returns the 'keyedHash' member of the union. func (u *TPMUPublicID) KeyedHash() (*TPM2BDigest, error) { if u.selector == TPMAlgKeyedHash { return u.contents.(*TPM2BDigest), nil } return nil, fmt.Errorf("did not contain keyedHash (selector value was %v)", u.selector) } // SymCipher returns the 'symCipher' member of the union. func (u *TPMUPublicID) SymCipher() (*TPM2BDigest, error) { if u.selector == TPMAlgSymCipher { return u.contents.(*TPM2BDigest), nil } return nil, fmt.Errorf("did not contain symCipher (selector value was %v)", u.selector) } // RSA returns the 'rsa' member of the union. func (u *TPMUPublicID) RSA() (*TPM2BPublicKeyRSA, error) { if u.selector == TPMAlgRSA { return u.contents.(*TPM2BPublicKeyRSA), nil } return nil, fmt.Errorf("did not contain rsa (selector value was %v)", u.selector) } // ECC returns the 'ecc' member of the union. func (u *TPMUPublicID) ECC() (*TPMSECCPoint, error) { if u.selector == TPMAlgECC { return u.contents.(*TPMSECCPoint), nil } return nil, fmt.Errorf("did not contain ecc (selector value was %v)", u.selector) } // TPMSKeyedHashParms represents a TPMS_KEYEDHASH_PARMS. // See definition in Part 2: Structures, section 12.2.3.3. type TPMSKeyedHashParms struct { marshalByReflection // Indicates the signing method used for a keyedHash signing // object. This field also determines the size of the data field // for a data object created with TPM2_Create() or // TPM2_CreatePrimary(). Scheme TPMTKeyedHashScheme } // TPMSRSAParms represents a TPMS_RSA_PARMS. // See definition in Part 2: Structures, section 12.2.3.5. type TPMSRSAParms struct { marshalByReflection // for a restricted decryption key, shall be set to a supported // symmetric algorithm, key size, and mode. // if the key is not a restricted decryption key, this field shall // be set to TPM_ALG_NULL. Symmetric TPMTSymDefObject // scheme.scheme shall be: // for an unrestricted signing key, either TPM_ALG_RSAPSS // TPM_ALG_RSASSA or TPM_ALG_NULL // for a restricted signing key, either TPM_ALG_RSAPSS or // TPM_ALG_RSASSA // for an unrestricted decryption key, TPM_ALG_RSAES, TPM_ALG_OAEP, // or TPM_ALG_NULL unless the object also has the sign attribute // for a restricted decryption key, TPM_ALG_NULL Scheme TPMTRSAScheme // number of bits in the public modulus KeyBits TPMIRSAKeyBits // the public exponent // A prime number greater than 2. Exponent uint32 } // TPMSECCParms represents a TPMS_ECC_PARMS. // See definition in Part 2: Structures, section 12.2.3.6. type TPMSECCParms struct { marshalByReflection // for a restricted decryption key, shall be set to a supported // symmetric algorithm, key size. and mode. // if the key is not a restricted decryption key, this field shall // be set to TPM_ALG_NULL. Symmetric TPMTSymDefObject // If the sign attribute of the key is SET, then this shall be a // valid signing scheme. Scheme TPMTECCScheme // ECC curve ID CurveID TPMIECCCurve // an optional key derivation scheme for generating a symmetric key // from a Z value // If the kdf parameter associated with curveID is not TPM_ALG_NULL // then this is required to be NULL. KDF TPMTKDFScheme } // TPMUPublicParms represents a TPMU_PUBLIC_PARMS. // See definition in Part 2: Structures, section 12.2.3.7. type TPMUPublicParms struct { selector TPMAlgID contents Marshallable } // PublicParmsContents is a type constraint representing the possible contents of TPMUPublicParms. type PublicParmsContents interface { Marshallable *TPMSKeyedHashParms | *TPMSSymCipherParms | *TPMSRSAParms | *TPMSECCParms } // create implements the unmarshallableWithHint interface. func (u *TPMUPublicParms) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgKeyedHash: var contents TPMSKeyedHashParms u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPMSSymCipherParms u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgRSA: var contents TPMSRSAParms u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPMSECCParms u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUPublicParms) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgKeyedHash: var contents TPMSKeyedHashParms if u.contents != nil { contents = *u.contents.(*TPMSKeyedHashParms) } return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPMSSymCipherParms if u.contents != nil { contents = *u.contents.(*TPMSSymCipherParms) } return reflect.ValueOf(&contents), nil case TPMAlgRSA: var contents TPMSRSAParms if u.contents != nil { contents = *u.contents.(*TPMSRSAParms) } return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPMSECCParms if u.contents != nil { contents = *u.contents.(*TPMSECCParms) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUPublicParms instantiates a TPMUPublicParms with the given contents. func NewTPMUPublicParms[C PublicParmsContents](selector TPMAlgID, contents C) TPMUPublicParms { return TPMUPublicParms{ selector: selector, contents: contents, } } // KeyedHashDetail returns the 'keyedHashDetail' member of the union. func (u *TPMUPublicParms) KeyedHashDetail() (*TPMSKeyedHashParms, error) { if u.selector == TPMAlgKeyedHash { return u.contents.(*TPMSKeyedHashParms), nil } return nil, fmt.Errorf("did not contain keyedHashDetail (selector value was %v)", u.selector) } // SymDetail returns the 'symDetail' member of the union. func (u *TPMUPublicParms) SymDetail() (*TPMSSymCipherParms, error) { if u.selector == TPMAlgSymCipher { return u.contents.(*TPMSSymCipherParms), nil } return nil, fmt.Errorf("did not contain symDetail (selector value was %v)", u.selector) } // RSADetail returns the 'rsaDetail' member of the union. func (u *TPMUPublicParms) RSADetail() (*TPMSRSAParms, error) { if u.selector == TPMAlgRSA { return u.contents.(*TPMSRSAParms), nil } return nil, fmt.Errorf("did not contain rsaDetail (selector value was %v)", u.selector) } // ECCDetail returns the 'eccDetail' member of the union. func (u *TPMUPublicParms) ECCDetail() (*TPMSECCParms, error) { if u.selector == TPMAlgECC { return u.contents.(*TPMSECCParms), nil } return nil, fmt.Errorf("did not contain eccDetail (selector value was %v)", u.selector) } // TPMTPublicParms represents a TPMT_PUBLIC_PARMS. // See definition in Part 2: Structures, section 12.2.3.8. type TPMTPublicParms struct { marshalByReflection // algorithm to be tested Type TPMIAlgPublic // algorithm details Parameters TPMUPublicParms `gotpm:"tag=Type"` } // TPMTPublic represents a TPMT_PUBLIC. // See definition in Part 2: Structures, section 12.2.4. type TPMTPublic struct { marshalByReflection // “algorithm” associated with this object Type TPMIAlgPublic // algorithm used for computing the Name of the object NameAlg TPMIAlgHash // attributes that, along with type, determine the manipulations // of this object ObjectAttributes TPMAObject // optional policy for using this key // The policy is computed using the nameAlg of the object. AuthPolicy TPM2BDigest // the algorithm or structure details Parameters TPMUPublicParms `gotpm:"tag=Type"` // the unique identifier of the structure // For an asymmetric key, this would be the public key. Unique TPMUPublicID `gotpm:"tag=Type"` } // TPM2BPublic represents a TPM2B_PUBLIC. // See definition in Part 2: Structures, section 12.2.5. type TPM2BPublic = TPM2B[TPMTPublic, *TPMTPublic] // TPM2BTemplate represents a TPM2B_TEMPLATE. // See definition in Part 2: Structures, section 12.2.6. type TPM2BTemplate TPM2BData // TemplateContents is a type constraint representing the possible contents of TPMUTemplate. type TemplateContents interface { Marshallable *TPMTPublic | *TPMTTemplate } // TPMTTemplate represents a TPMT_TEMPLATE. It is not defined in the spec. // It represents the alternate form of TPMT_PUBLIC for TPM2B_TEMPLATE as // described in Part 2: Structures, 12.2.6. type TPMTTemplate struct { marshalByReflection // “algorithm” associated with this object Type TPMIAlgPublic // algorithm used for computing the Name of the object NameAlg TPMIAlgHash // attributes that, along with type, determine the manipulations // of this object ObjectAttributes TPMAObject // optional policy for using this key // The policy is computed using the nameAlg of the object. AuthPolicy TPM2BDigest // the algorithm or structure details Parameters TPMUPublicParms `gotpm:"tag=Type"` // the derivation parameters Unique TPMSDerive } // New2BTemplate creates a TPM2BTemplate with the given data. func New2BTemplate[C TemplateContents](data C) TPM2BTemplate { return TPM2BTemplate{ Buffer: Marshal(data), } } // Sym returns the 'sym' member of the union. func (u *TPMUSensitiveComposite) Sym() (*TPM2BSymKey, error) { if u.selector == TPMAlgSymCipher { return u.contents.(*TPM2BSymKey), nil } return nil, fmt.Errorf("did not contain sym (selector value was %v)", u.selector) } // Bits returns the 'bits' member of the union. func (u *TPMUSensitiveComposite) Bits() (*TPM2BSensitiveData, error) { if u.selector == TPMAlgKeyedHash { return u.contents.(*TPM2BSensitiveData), nil } return nil, fmt.Errorf("did not contain bits (selector value was %v)", u.selector) } // RSA returns the 'rsa' member of the union. func (u *TPMUSensitiveComposite) RSA() (*TPM2BPrivateKeyRSA, error) { if u.selector == TPMAlgRSA { return u.contents.(*TPM2BPrivateKeyRSA), nil } return nil, fmt.Errorf("did not contain rsa (selector value was %v)", u.selector) } // ECC returns the 'ecc' member of the union. func (u *TPMUSensitiveComposite) ECC() (*TPM2BECCParameter, error) { if u.selector == TPMAlgECC { return u.contents.(*TPM2BECCParameter), nil } return nil, fmt.Errorf("did not contain ecc (selector value was %v)", u.selector) } // TPMUSensitiveComposite represents a TPMU_SENSITIVE_COMPOSITE. // See definition in Part 2: Structures, section 12.3.2.3. type TPMUSensitiveComposite struct { selector TPMAlgID contents Marshallable } // SensitiveCompositeContents is a type constraint representing the possible contents of TPMUSensitiveComposite. type SensitiveCompositeContents interface { Marshallable *TPM2BPrivateKeyRSA | *TPM2BECCParameter | *TPM2BSensitiveData | *TPM2BSymKey } // create implements the unmarshallableWithHint interface. func (u *TPMUSensitiveComposite) create(hint int64) (reflect.Value, error) { switch TPMAlgID(hint) { case TPMAlgRSA: var contents TPM2BPrivateKeyRSA u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPM2BECCParameter u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgKeyedHash: var contents TPM2BSensitiveData u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPM2BSymKey u.contents = &contents u.selector = TPMAlgID(hint) return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // get implements the marshallableWithHint interface. func (u TPMUSensitiveComposite) get(hint int64) (reflect.Value, error) { if u.selector != 0 && hint != int64(u.selector) { return reflect.ValueOf(nil), fmt.Errorf("incorrect union tag %v, is %v", hint, u.selector) } switch TPMAlgID(hint) { case TPMAlgRSA: var contents TPM2BPrivateKeyRSA if u.contents != nil { contents = *u.contents.(*TPM2BPrivateKeyRSA) } return reflect.ValueOf(&contents), nil case TPMAlgECC: var contents TPM2BECCParameter if u.contents != nil { contents = *u.contents.(*TPM2BECCParameter) } return reflect.ValueOf(&contents), nil case TPMAlgKeyedHash: var contents TPM2BSensitiveData if u.contents != nil { contents = *u.contents.(*TPM2BSensitiveData) } return reflect.ValueOf(&contents), nil case TPMAlgSymCipher: var contents TPM2BSymKey if u.contents != nil { contents = *u.contents.(*TPM2BSymKey) } return reflect.ValueOf(&contents), nil } return reflect.ValueOf(nil), fmt.Errorf("no union member for tag %v", hint) } // NewTPMUSensitiveComposite instantiates a TPMUSensitiveComposite with the given contents. func NewTPMUSensitiveComposite[C SensitiveCompositeContents](selector TPMAlgID, contents C) TPMUSensitiveComposite { return TPMUSensitiveComposite{ selector: selector, contents: contents, } } // RSA returns the 'rsa' member of the union. func (u *TPMUKDFScheme) RSA() (*TPM2BPrivateKeyRSA, error) { if u.selector == TPMAlgRSA { return u.contents.(*TPM2BPrivateKeyRSA), nil } return nil, fmt.Errorf("did not contain rsa (selector value was %v)", u.selector) } // ECC returns the 'ecc' member of the union. func (u *TPMUKDFScheme) ECC() (*TPM2BECCParameter, error) { if u.selector == TPMAlgECC { return u.contents.(*TPM2BECCParameter), nil } return nil, fmt.Errorf("did not contain ecc (selector value was %v)", u.selector) } // Bits returns the 'bits' member of the union. func (u *TPMUKDFScheme) Bits() (*TPM2BSensitiveData, error) { if u.selector == TPMAlgKeyedHash { return u.contents.(*TPM2BSensitiveData), nil } return nil, fmt.Errorf("did not contain bits (selector value was %v)", u.selector) } // Sym returns the 'sym' member of the union. func (u *TPMUKDFScheme) Sym() (*TPM2BSymKey, error) { if u.selector == TPMAlgSymCipher { return u.contents.(*TPM2BSymKey), nil } return nil, fmt.Errorf("did not contain sym (selector value was %v)", u.selector) } // TPMTSensitive represents a TPMT_SENSITIVE. // See definition in Part 2: Structures, section 12.3.2.4. type TPMTSensitive struct { marshalByReflection // identifier for the sensitive area SensitiveType TPMIAlgPublic // user authorization data AuthValue TPM2BAuth // for a parent object, the optional protection seed; for other objects, // the obfuscation value SeedValue TPM2BDigest // the type-specific private data Sensitive TPMUSensitiveComposite `gotpm:"tag=SensitiveType"` } // TPM2BSensitive represents a TPM2B_SENSITIVE. // See definition in Part 2: Structures, section 12.3.3. type TPM2BSensitive = TPM2B[TPMTSensitive, *TPMTSensitive] // TPM2BPrivate represents a TPM2B_PRIVATE. // See definition in Part 2: Structures, section 12.3.7. type TPM2BPrivate TPM2BData // TPMSCreationData represents a TPMS_CREATION_DATA. // See definition in Part 2: Structures, section 15.1. type TPMSCreationData struct { marshalByReflection // list indicating the PCR included in pcrDigest PCRSelect TPMLPCRSelection // digest of the selected PCR using nameAlg of the object for which // this structure is being created PCRDigest TPM2BDigest // the locality at which the object was created Locality TPMALocality // nameAlg of the parent ParentNameAlg TPMAlgID // Name of the parent at time of creation ParentName TPM2BName // Qualified Name of the parent at the time of creation ParentQualifiedName TPM2BName // association with additional information added by the key OutsideInfo TPM2BData } // TPM2BIDObject represents a TPM2B_ID_OBJECT. // See definition in Part 2: Structures, section 12.4.3. type TPM2BIDObject TPM2BData // TPMANV represents a TPMA_NV. // See definition in Part 2: Structures, section 13.4. type TPMANV struct { bitfield32 marshalByReflection // SET (1): The Index data can be written if Platform Authorization is // provided. // CLEAR (0): Writing of the Index data cannot be authorized with // Platform Authorization. PPWrite bool `gotpm:"bit=0"` // SET (1): The Index data can be written if Owner Authorization is // provided. // CLEAR (0): Writing of the Index data cannot be authorized with Owner // Authorization. OwnerWrite bool `gotpm:"bit=1"` // SET (1): Authorizations to change the Index contents that require // USER role may be provided with an HMAC session or password. // CLEAR (0): Authorizations to change the Index contents that require // USER role may not be provided with an HMAC session or password. AuthWrite bool `gotpm:"bit=2"` // SET (1): Authorizations to change the Index contents that require // USER role may be provided with a policy session. // CLEAR (0): Authorizations to change the Index contents that require // USER role may not be provided with a policy session. PolicyWrite bool `gotpm:"bit=3"` // The type of the index. NT TPMNT `gotpm:"bit=7:4"` // SET (1): Index may not be deleted unless the authPolicy is satisfied // using TPM2_NV_UndefineSpaceSpecial(). // CLEAR (0): Index may be deleted with proper platform or owner // authorization using TPM2_NV_UndefineSpace(). PolicyDelete bool `gotpm:"bit=10"` // SET (1): Index cannot be written. // CLEAR (0): Index can be written. WriteLocked bool `gotpm:"bit=11"` // SET (1): A partial write of the Index data is not allowed. The write // size shall match the defined space size. // CLEAR (0): Partial writes are allowed. This setting is required if // the .dataSize of the Index is larger than NV_MAX_BUFFER_SIZE for the // implementation. WriteAll bool `gotpm:"bit=12"` // SET (1): TPM2_NV_WriteLock() may be used to prevent further writes // to this location. // CLEAR (0): TPM2_NV_WriteLock() does not block subsequent writes if // TPMA_NV_WRITE_STCLEAR is also CLEAR. WriteDefine bool `gotpm:"bit=13"` // SET (1): TPM2_NV_WriteLock() may be used to prevent further writes // to this location until the next TPM Reset or TPM Restart. // CLEAR (0): TPM2_NV_WriteLock() does not block subsequent writes if // TPMA_NV_WRITEDEFINE is also CLEAR. WriteSTClear bool `gotpm:"bit=14"` // SET (1): If TPM2_NV_GlobalWriteLock() is successful, // TPMA_NV_WRITELOCKED is set. // CLEAR (0): TPM2_NV_GlobalWriteLock() has no effect on the writing of // the data at this Index. GlobalLock bool `gotpm:"bit=15"` // SET (1): The Index data can be read if Platform Authorization is // provided. // CLEAR (0): Reading of the Index data cannot be authorized with // Platform Authorization. PPRead bool `gotpm:"bit=16"` // SET (1): The Index data can be read if Owner Authorization is // provided. // CLEAR (0): Reading of the Index data cannot be authorized with Owner // Authorization. OwnerRead bool `gotpm:"bit=17"` // SET (1): The Index data may be read if the authValue is provided. // CLEAR (0): Reading of the Index data cannot be authorized with the // Index authValue. AuthRead bool `gotpm:"bit=18"` // SET (1): The Index data may be read if the authPolicy is satisfied. // CLEAR (0): Reading of the Index data cannot be authorized with the // Index authPolicy. PolicyRead bool `gotpm:"bit=19"` // SET (1): Authorization failures of the Index do not affect the DA // logic and authorization of the Index is not blocked when the TPM is // in Lockout mode. // CLEAR (0): Authorization failures of the Index will increment the // authorization failure counter and authorizations of this Index are // not allowed when the TPM is in Lockout mode. NoDA bool `gotpm:"bit=25"` // SET (1): NV Index state is only required to be saved when the TPM // performs an orderly shutdown (TPM2_Shutdown()). // CLEAR (0): NV Index state is required to be persistent after the // command to update the Index completes successfully (that is, the NV // update is synchronous with the update command). Orderly bool `gotpm:"bit=26"` // SET (1): TPMA_NV_WRITTEN for the Index is CLEAR by TPM Reset or TPM // Restart. // CLEAR (0): TPMA_NV_WRITTEN is not changed by TPM Restart. ClearSTClear bool `gotpm:"bit=27"` // SET (1): Reads of the Index are blocked until the next TPM Reset or // TPM Restart. // CLEAR (0): Reads of the Index are allowed if proper authorization is // provided. ReadLocked bool `gotpm:"bit=28"` // SET (1): Index has been written. // CLEAR (0): Index has not been written. Written bool `gotpm:"bit=29"` // SET (1): This Index may be undefined with Platform Authorization // but not with Owner Authorization. // CLEAR (0): This Index may be undefined using Owner Authorization but // not with Platform Authorization. PlatformCreate bool `gotpm:"bit=30"` // SET (1): TPM2_NV_ReadLock() may be used to SET TPMA_NV_READLOCKED // for this Index. // CLEAR (0): TPM2_NV_ReadLock() has no effect on this Index. ReadSTClear bool `gotpm:"bit=31"` } // TPMSNVPublic represents a TPMS_NV_PUBLIC. // See definition in Part 2: Structures, section 13.5. type TPMSNVPublic struct { marshalByReflection // the handle of the data area NVIndex TPMIRHNVIndex // hash algorithm used to compute the name of the Index and used for // the authPolicy. For an extend index, the hash algorithm used for the // extend. NameAlg TPMIAlgHash // the Index attributes Attributes TPMANV // optional access policy for the Index AuthPolicy TPM2BDigest // the size of the data area DataSize uint16 } // TPM2BNVPublic represents a TPM2B_NV_PUBLIC. // See definition in Part 2: Structures, section 13.6. type TPM2BNVPublic = TPM2B[TPMSNVPublic, *TPMSNVPublic] // TPM2BContextSensitive represents a TPM2B_CONTEXT_SENSITIVE // See definition in Part 2: Structures, section 14.2. type TPM2BContextSensitive TPM2BData // TPMSContextData represents a TPMS_CONTEXT_DATA // See definition in Part 2: Structures, section 14.3. type TPMSContextData struct { marshalByReflection // the integrity value Integrity TPM2BDigest // the sensitive area Encrypted TPM2BContextSensitive } // TPM2BContextData represents a TPM2B_CONTEXT_DATA // See definition in Part 2: Structures, section 14.4. // Represented here as a flat buffer because how a TPM chooses // to represent its context data is implementation-dependent. type TPM2BContextData TPM2BData // TPMSContext represents a TPMS_CONTEXT // See definition in Part 2: Structures, section 14.5. type TPMSContext struct { marshalByReflection // the sequence number of the context Sequence uint64 // a handle indicating if the context is a session, object, or sequence object SavedHandle TPMIDHSaved // the hierarchy of the context Hierarchy TPMIRHHierarchy // the context data and integrity HMAC ContextBlob TPM2BContextData } type tpm2bCreationData = TPM2B[TPMSCreationData, *TPMSCreationData] go-tpm-0.9.3/tpm2/templates.go000066400000000000000000000113721473040456300161470ustar00rootroot00000000000000package tpm2 var ( // RSASRKTemplate contains the TCG reference RSA-2048 SRK template. // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf RSASRKTemplate = TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, AdminWithPolicy: false, NoDA: true, EncryptedDuplication: false, Restricted: true, Decrypt: true, SignEncrypt: false, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Symmetric: TPMTSymDefObject{ Algorithm: TPMAlgAES, KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), Mode: NewTPMUSymMode( TPMAlgAES, TPMAlgCFB, ), }, KeyBits: 2048, }, ), Unique: NewTPMUPublicID( TPMAlgRSA, &TPM2BPublicKeyRSA{ Buffer: make([]byte, 256), }, ), } // RSAEKTemplate contains the TCG reference RSA-2048 EK template. RSAEKTemplate = TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: false, AdminWithPolicy: true, NoDA: false, EncryptedDuplication: false, Restricted: true, Decrypt: true, SignEncrypt: false, }, AuthPolicy: TPM2BDigest{ Buffer: []byte{ // TPM2_PolicySecret(RH_ENDORSEMENT) 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, }, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Symmetric: TPMTSymDefObject{ Algorithm: TPMAlgAES, KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), Mode: NewTPMUSymMode( TPMAlgAES, TPMAlgCFB, ), }, KeyBits: 2048, }, ), Unique: NewTPMUPublicID( TPMAlgRSA, &TPM2BPublicKeyRSA{ Buffer: make([]byte, 256), }, ), } // ECCSRKTemplate contains the TCG reference ECC-P256 SRK template. // https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf ECCSRKTemplate = TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, AdminWithPolicy: false, NoDA: true, EncryptedDuplication: false, Restricted: true, Decrypt: true, SignEncrypt: false, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Symmetric: TPMTSymDefObject{ Algorithm: TPMAlgAES, KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), Mode: NewTPMUSymMode( TPMAlgAES, TPMAlgCFB, ), }, CurveID: TPMECCNistP256, }, ), Unique: NewTPMUPublicID( TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{ Buffer: make([]byte, 32), }, Y: TPM2BECCParameter{ Buffer: make([]byte, 32), }, }, ), } // ECCEKTemplate contains the TCG reference ECC-P256 EK template. ECCEKTemplate = TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: false, AdminWithPolicy: true, NoDA: false, EncryptedDuplication: false, Restricted: true, Decrypt: true, SignEncrypt: false, }, AuthPolicy: TPM2BDigest{ Buffer: []byte{ // TPM2_PolicySecret(RH_ENDORSEMENT) 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14, 0x69, 0xAA, }, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Symmetric: TPMTSymDefObject{ Algorithm: TPMAlgAES, KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), Mode: NewTPMUSymMode( TPMAlgAES, TPMAlgCFB, ), }, CurveID: TPMECCNistP256, }, ), Unique: NewTPMUPublicID( TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{ Buffer: make([]byte, 32), }, Y: TPM2BECCParameter{ Buffer: make([]byte, 32), }, }, ), } ) go-tpm-0.9.3/tpm2/test/000077500000000000000000000000001473040456300145755ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/test/activate_credential_test.go000066400000000000000000000037351473040456300221650ustar00rootroot00000000000000package tpm2test import ( "bytes" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestActivateCredential(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() ekCreate := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: New2B(ECCEKTemplate), } ekCreateRsp, err := ekCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate EK: %v", err) } defer func() { flush := FlushContext{ FlushHandle: ekCreateRsp.ObjectHandle, } _, err := flush.Execute(thetpm) if err != nil { t.Fatalf("could not flush EK: %v", err) } }() srkCreate := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(ECCSRKTemplate), } srkCreateRsp, err := srkCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } defer func() { flush := FlushContext{ FlushHandle: srkCreateRsp.ObjectHandle, } _, err := flush.Execute(thetpm) if err != nil { t.Fatalf("could not flush SRK: %v", err) } }() secret := TPM2BDigest{Buffer: []byte("Secrets!!!")} mc := MakeCredential{ Handle: ekCreateRsp.ObjectHandle, Credential: secret, ObjectNamae: srkCreateRsp.Name, } mcRsp, err := mc.Execute(thetpm) if err != nil { t.Fatalf("could not make credential: %v", err) } ac := ActivateCredential{ ActivateHandle: NamedHandle{ Handle: srkCreateRsp.ObjectHandle, Name: srkCreateRsp.Name, }, KeyHandle: AuthHandle{ Handle: ekCreateRsp.ObjectHandle, Name: ekCreateRsp.Name, Auth: Policy(TPMAlgSHA256, 16, ekPolicy), }, CredentialBlob: mcRsp.CredentialBlob, Secret: mcRsp.Secret, } acRsp, err := ac.Execute(thetpm) if err != nil { t.Fatalf("could not activate credential: %v", err) } if !bytes.Equal(acRsp.CertInfo.Buffer, secret.Buffer) { t.Errorf("want %x got %x", secret.Buffer, acRsp.CertInfo.Buffer) } } go-tpm-0.9.3/tpm2/test/audit_test.go000066400000000000000000000060361473040456300172760ustar00rootroot00000000000000package tpm2test import ( "bytes" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestAuditSession(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Create the audit session sess, cleanup, err := HMACSession(thetpm, TPMAlgSHA256, 16, Audit()) if err != nil { t.Fatalf("%v", err) } defer cleanup() // Create the AK for audit createAKCmd := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, AdminWithPolicy: false, NoDA: true, EncryptedDuplication: false, Restricted: true, Decrypt: false, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Scheme: TPMTECCScheme{ Scheme: TPMAlgECDSA, Details: NewTPMUAsymScheme( TPMAlgECDSA, &TPMSSigSchemeECDSA{ HashAlg: TPMAlgSHA256, }, ), }, CurveID: TPMECCNistP256, }, ), }, ), } createAKRsp, err := createAKCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } defer func() { // Flush the AK flush := FlushContext{FlushHandle: createAKRsp.ObjectHandle} if _, err := flush.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() audit, err := NewAudit(TPMAlgSHA256) if err != nil { t.Fatalf("%v", err) } // Call GetCapability a bunch of times with the audit session and make sure it extends like // we expect it to. props := []TPMPT{ TPMPTFamilyIndicator, TPMPTLevel, TPMPTRevision, TPMPTDayofYear, TPMPTYear, TPMPTManufacturer, } for _, prop := range props { getCmd := GetCapability{ Capability: TPMCapTPMProperties, Property: uint32(prop), PropertyCount: 1, } getRsp, err := getCmd.Execute(thetpm, sess) if err != nil { t.Fatalf("%v", err) } if err := AuditCommand(audit, getCmd, getRsp); err != nil { t.Fatalf("%v", err) } // Get the audit digest signed by the AK getAuditCmd := GetSessionAuditDigest{ PrivacyAdminHandle: TPMRHEndorsement, SignHandle: NamedHandle{ Handle: createAKRsp.ObjectHandle, Name: createAKRsp.Name, }, SessionHandle: sess.Handle(), QualifyingData: TPM2BData{Buffer: []byte("foobar")}, } getAuditRsp, err := getAuditCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } // TODO check the signature with the AK pub attest, err := getAuditRsp.AuditInfo.Contents() if err != nil { t.Fatalf("%v", err) } aud, err := attest.Attested.SessionAudit() if err != nil { t.Fatalf("%v", err) } want := audit.Digest() got := aud.SessionDigest.Buffer if !bytes.Equal(want, got) { t.Errorf("unexpected audit value:\ngot %x\nwant %x", got, want) } } } go-tpm-0.9.3/tpm2/test/certify_test.go000066400000000000000000000251061473040456300176340ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto" "crypto/rsa" "crypto/sha256" "testing" "github.com/google/go-cmp/cmp" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestCertify(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() Auth := []byte("password") public := New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, Restricted: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }, ) pcrSelection := TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: TPMAlgSHA256, PCRSelect: PCClientCompatible.PCRs(7), }, }, } createPrimarySigner := CreatePrimary{ PrimaryHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: Auth, }, }, }, InPublic: public, CreationPCR: pcrSelection, } rspSigner, err := createPrimarySigner.Execute(thetpm) if err != nil { t.Fatalf("Failed to create primary: %v", err) } flushContextSigner := FlushContext{FlushHandle: rspSigner.ObjectHandle} defer flushContextSigner.Execute(thetpm) createPrimarySubject := CreatePrimary{ PrimaryHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: Auth, }, }, }, InPublic: public, CreationPCR: pcrSelection, } unique := NewTPMUPublicID( TPMAlgRSA, &TPM2BPublicKeyRSA{ Buffer: []byte("subject key"), }, ) inPub, err := createPrimarySubject.InPublic.Contents() if err != nil { t.Fatalf("%v", err) } inPub.Unique = unique rspSubject, err := createPrimarySubject.Execute(thetpm) if err != nil { t.Fatalf("Failed to create primary: %v", err) } flushContextSubject := FlushContext{FlushHandle: rspSubject.ObjectHandle} defer flushContextSubject.Execute(thetpm) originalBuffer := []byte("test nonce") certify := Certify{ ObjectHandle: AuthHandle{ Handle: rspSubject.ObjectHandle, Name: rspSubject.Name, Auth: PasswordAuth(Auth), }, SignHandle: AuthHandle{ Handle: rspSigner.ObjectHandle, Name: rspSigner.Name, Auth: PasswordAuth(Auth), }, QualifyingData: TPM2BData{ Buffer: originalBuffer, }, InScheme: TPMTSigScheme{ Scheme: TPMAlgNull, }, } rspCert, err := certify.Execute(thetpm) if err != nil { t.Fatalf("Failed to certify: %v", err) } certifyInfo, err := rspCert.CertifyInfo.Contents() if err != nil { t.Fatalf("%v", err) } info := Marshal(certifyInfo) attestHash := sha256.Sum256(info) pub, err := rspSigner.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } rsaDetail, err := pub.Parameters.RSADetail() if err != nil { t.Fatalf("%v", err) } rsaUnique, err := pub.Unique.RSA() if err != nil { t.Fatalf("%v", err) } rsaPub, err := RSAPub(rsaDetail, rsaUnique) if err != nil { t.Fatalf("%v", err) } rsassa, err := rspCert.Signature.Signature.RSASSA() if err != nil { t.Fatalf("%v", err) } if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, attestHash[:], rsassa.Sig.Buffer); err != nil { t.Errorf("Signature verification failed: %v", err) } if !cmp.Equal(originalBuffer, certifyInfo.ExtraData.Buffer) { t.Errorf("Attested buffer is different from original buffer") } } func TestCreateAndCertifyCreation(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() public := New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, Restricted: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, NoDA: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }) pcrSelection := TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: TPMAlgSHA1, PCRSelect: PCClientCompatible.PCRs(7), }, }, } createPrimary := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: public, CreationPCR: pcrSelection, } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("Failed to create primary: %v", err) } flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer flushContext.Execute(thetpm) inScheme := TPMTSigScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUSigScheme( TPMAlgRSASSA, &TPMSSchemeHash{ HashAlg: TPMAlgSHA256, }, ), } certifyCreation := CertifyCreation{ SignHandle: AuthHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, Auth: PasswordAuth(nil), }, ObjectHandle: NamedHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, }, CreationHash: rspCP.CreationHash, InScheme: inScheme, CreationTicket: rspCP.CreationTicket, } rspCC, err := certifyCreation.Execute(thetpm) if err != nil { t.Fatalf("Failed to certify creation: %v", err) } certifyInfo, err := rspCC.CertifyInfo.Contents() if err != nil { t.Fatalf("%v", err) } creationInfo, err := certifyInfo.Attested.Creation() if err != nil { t.Fatalf("%v", err) } attName := creationInfo.ObjectName.Buffer pubName := rspCP.Name.Buffer if !bytes.Equal(attName, pubName) { t.Fatalf("Attested name: %v does not match returned public key: %v.", attName, pubName) } info := Marshal(certifyInfo) if err != nil { t.Fatalf("Failed to marshal: %v", err) } attestHash := sha256.Sum256(info) pub, err := rspCP.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } rsaDetail, err := pub.Parameters.RSADetail() if err != nil { t.Fatalf("%v", err) } rsaUnique, err := pub.Unique.RSA() if err != nil { t.Fatalf("%v", err) } rsaPub, err := RSAPub(rsaDetail, rsaUnique) if err != nil { t.Fatalf("Failed to retrieve Public Key: %v", err) } rsassa, err := rspCC.Signature.Signature.RSASSA() if err != nil { t.Fatalf("%v", err) } if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, attestHash[:], rsassa.Sig.Buffer); err != nil { t.Errorf("Signature verification failed: %v", err) } } func TestNVCertify(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() Auth := []byte("password") public := New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, Restricted: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }) createPrimarySigner := CreatePrimary{ PrimaryHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: Auth, }, }, }, InPublic: public, } rspSigner, err := createPrimarySigner.Execute(thetpm) if err != nil { t.Fatalf("Failed to create primary: %v", err) } flushContextSigner := FlushContext{FlushHandle: rspSigner.ObjectHandle} defer flushContextSigner.Execute(thetpm) def := NVDefineSpace{ AuthHandle: TPMRHOwner, PublicInfo: New2B( TPMSNVPublic{ NVIndex: TPMHandle(0x0180000F), NameAlg: TPMAlgSHA256, Attributes: TPMANV{ OwnerWrite: true, OwnerRead: true, AuthWrite: true, AuthRead: true, NT: TPMNTOrdinary, NoDA: true, }, DataSize: 4, }), } if _, err := def.Execute(thetpm); err != nil { t.Fatalf("Calling TPM2_NV_DefineSpace: %v", err) } readPub := NVReadPublic{ NVIndex: TPMHandle(0x0180000F), } nvPub, err := readPub.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) } nvPublic, err := def.PublicInfo.Contents() if err != nil { t.Fatalf("%v", err) } prewrite := NVWrite{ AuthHandle: AuthHandle{ Handle: nvPublic.NVIndex, Name: nvPub.NVName, Auth: PasswordAuth(nil), }, NVIndex: NamedHandle{ Handle: nvPublic.NVIndex, Name: nvPub.NVName, }, Data: TPM2BMaxNVBuffer{ Buffer: []byte{0x01, 0x02, 0x03, 0x04}, }, Offset: 0, } if _, err := prewrite.Execute(thetpm); err != nil { t.Errorf("Calling TPM2_NV_Write: %v", err) } nvPub, err = readPub.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) } nvCertify := NVCertify{ AuthHandle: AuthHandle{ Handle: TPMHandle(0x0180000F), Name: nvPub.NVName, Auth: PasswordAuth(nil), }, SignHandle: AuthHandle{ Handle: rspSigner.ObjectHandle, Name: rspSigner.Name, Auth: PasswordAuth(Auth), }, NVIndex: NamedHandle{ Handle: TPMHandle(0x0180000F), Name: nvPub.NVName, }, QualifyingData: TPM2BData{ Buffer: []byte("nonce"), }, } rspCert, err := nvCertify.Execute(thetpm) if err != nil { t.Fatalf("Failed to certify: %v", err) } certInfo, err := rspCert.CertifyInfo.Contents() if err != nil { t.Fatalf("%v", err) } info := Marshal(certInfo) attestHash := sha256.Sum256(info) pub, err := rspSigner.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } rsaDetail, err := pub.Parameters.RSADetail() if err != nil { t.Fatalf("%v", err) } rsaUnique, err := pub.Unique.RSA() if err != nil { t.Fatalf("%v", err) } rsaPub, err := RSAPub(rsaDetail, rsaUnique) if err != nil { t.Fatalf("Failed to retrieve Public Key: %v", err) } rsassa, err := rspCert.Signature.Signature.RSASSA() if err != nil { t.Fatalf("%v", err) } if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, attestHash[:], rsassa.Sig.Buffer); err != nil { t.Errorf("Signature verification failed: %v", err) } if !cmp.Equal([]byte("nonce"), certInfo.ExtraData.Buffer) { t.Errorf("Attested buffer is different from original buffer") } } go-tpm-0.9.3/tpm2/test/clear_test.go000066400000000000000000000023371473040456300172560ustar00rootroot00000000000000package tpm2test import ( "bytes" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestClear(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() srkCreate := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(ECCSRKTemplate), } srkCreateRsp, err := srkCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } srkName1 := srkCreateRsp.Name clear := Clear{ AuthHandle: AuthHandle{ Handle: TPMRHLockout, Auth: PasswordAuth(nil), }, } _, err = clear.Execute(thetpm) if err != nil { t.Fatalf("could not clear TPM: %v", err) } srkCreateRsp, err = srkCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } defer func() { flush := FlushContext{ FlushHandle: srkCreateRsp.ObjectHandle, } _, err := flush.Execute(thetpm) if err != nil { t.Fatalf("could not flush SRK: %v", err) } }() srkName2 := srkCreateRsp.Name if bytes.Equal(srkName1.Buffer, srkName2.Buffer) { t.Errorf("SRK Name did not change across clear, was %x both times", srkName1.Buffer) } } go-tpm-0.9.3/tpm2/test/combined_context_test.go000066400000000000000000000046321473040456300215140ustar00rootroot00000000000000package tpm2test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) func ReadPublicName(t *testing.T, handle TPMHandle, thetpm transport.TPM) TPM2BName { readPublic := ReadPublic{ ObjectHandle: handle, } rspRP, err := readPublic.Execute(thetpm) if err != nil { t.Fatalf("Failed to read public: %v", err) } return rspRP.Name } func TestCombinedContext(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }), CreationPCR: TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: TPMAlgSHA1, PCRSelect: PCClientCompatible.PCRs(7), }, }, }, } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not create key: %v", err) } flushContextObject := FlushContext{FlushHandle: rspCP.ObjectHandle} defer flushContextObject.Execute(thetpm) contextSave := ContextSave{ SaveHandle: rspCP.ObjectHandle, } rspCS, err := contextSave.Execute(thetpm) if err != nil { t.Fatalf("ContextSave failed: %v", err) } contextLoad := ContextLoad{ Context: rspCS.Context, } rspCL, err := contextLoad.Execute(thetpm) if err != nil { t.Fatalf("ContextLoad failed: %v", err) } flushContextLoaded := FlushContext{FlushHandle: rspCL.LoadedHandle} defer flushContextLoaded.Execute(thetpm) rspCLName := ReadPublicName(t, rspCL.LoadedHandle, thetpm) rspCPName := ReadPublicName(t, rspCP.ObjectHandle, thetpm) if !cmp.Equal(rspCLName, rspCPName, cmpopts.IgnoreUnexported(rspCLName)) { t.Error("Mismatch between public returned from ContextLoad & CreateLoaded") } } go-tpm-0.9.3/tpm2/test/commit_test.go000066400000000000000000000042701473040456300174560ustar00rootroot00000000000000package tpm2test import ( "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestCommit(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() password := []byte("hello") create := CreateLoaded{ ParentHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: password, }, }, }, InPublic: New2BTemplate( &TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, SensitiveDataOrigin: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Symmetric: TPMTSymDefObject{ Algorithm: TPMAlgNull, }, Scheme: TPMTECCScheme{ Scheme: TPMAlgECDAA, Details: NewTPMUAsymScheme( TPMAlgECDAA, &TPMSSchemeECDAA{ HashAlg: TPMAlgSHA256, }, ), }, CurveID: TPMECCBNP256, KDF: TPMTKDFScheme{ Scheme: TPMAlgNull, }, }, ), }), } rspCP, err := create.Execute(thetpm) if err != nil { t.Fatalf("could not create key: %v", err) } flushContextCP := FlushContext{FlushHandle: rspCP.ObjectHandle} defer flushContextCP.Execute(thetpm) commit := Commit{ SignHandle: AuthHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, Auth: PasswordAuth(password), }, P1: New2B( TPMSECCPoint{ X: TPM2BECCParameter{ Buffer: []byte{1}, }, Y: TPM2BECCParameter{ Buffer: []byte{2}, }, }), S2: TPM2BSensitiveData{ Buffer: []byte{}, }, Y2: TPM2BECCParameter{ Buffer: []byte{}, }, } resp, err := commit.Execute(thetpm) if err != nil { t.Fatalf("could not commit: %v", err) } firstCounter := resp.Counter resp, err = commit.Execute(thetpm) if err != nil { t.Fatalf("could not commit: %v", err) } secondCounter := resp.Counter if firstCounter+1 != secondCounter { t.Fatalf("counter did not increment") } } go-tpm-0.9.3/tpm2/test/create_loaded_test.go000066400000000000000000000074041473040456300207430ustar00rootroot00000000000000package tpm2test import ( "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) func getDeriver(t *testing.T, thetpm transport.TPM) NamedHandle { t.Helper() cl := CreateLoaded{ ParentHandle: TPMRHOwner, InPublic: New2BTemplate( &TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SensitiveDataOrigin: true, UserWithAuth: true, Decrypt: true, Restricted: true, }, Parameters: NewTPMUPublicParms( TPMAlgKeyedHash, &TPMSKeyedHashParms{ Scheme: TPMTKeyedHashScheme{ Scheme: TPMAlgXOR, Details: NewTPMUSchemeKeyedHash( TPMAlgXOR, &TPMSSchemeXOR{ HashAlg: TPMAlgSHA256, KDF: TPMAlgKDF1SP800108, }, ), }, }, ), }), } rsp, err := cl.Execute(thetpm) if err != nil { t.Fatalf("could not create derivation parent: %v:", err) } return NamedHandle{ Handle: rsp.ObjectHandle, Name: rsp.Name, } } func TestCreateLoaded(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() deriver := getDeriver(t, thetpm) derive := New2B( TPMSDerive{ Label: TPM2BLabel{ Buffer: []byte("label"), }, Context: TPM2BLabel{ Buffer: []byte("context"), }, }) createLoadeds := map[string]*CreateLoaded{ "PrimaryKey": { ParentHandle: TPMRHEndorsement, InPublic: New2BTemplate(&ECCEKTemplate), }, "NoParentPrimaryKey": { // Make the object in the null hierarchy and ensure that go-tpm supports not providing the parent handle at all. InPublic: New2BTemplate(&ECCEKTemplate), }, "OrdinaryKey": { ParentHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: []byte("p@ssw0rd"), }, }, }, InPublic: New2BTemplate( &TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SensitiveDataOrigin: true, UserWithAuth: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), }), }, "DataBlob": { ParentHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: []byte("p@ssw0rd"), }, Data: NewTPMUSensitiveCreate(&TPM2BSensitiveData{ Buffer: []byte("secrets"), }), }, }, InPublic: New2BTemplate( &TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ UserWithAuth: true, }, }), }, "Derived": { ParentHandle: deriver, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: []byte("p@ssw0rd"), }, Data: NewTPMUSensitiveCreate(&derive), }, }, InPublic: New2BTemplate( &TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedParent: true, UserWithAuth: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), }), }, } for name, createLoaded := range createLoadeds { t.Run(name, func(t *testing.T) { rsp, err := createLoaded.Execute(thetpm) if err != nil { t.Fatalf("error from CreateLoaded: %v", err) } if _, err = (FlushContext{FlushHandle: rsp.ObjectHandle}).Execute(thetpm); err != nil { t.Errorf("error from FlushContext: %v", err) } }) } } go-tpm-0.9.3/tpm2/test/duplicate_test.go000066400000000000000000000106331473040456300201400ustar00rootroot00000000000000package tpm2test import ( "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) // TestDuplicate creates an object under Owner->SRK and duplicates it to // Endorsement->SRK. func TestDuplicate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() t.Log("### Create Owner SRK") srkCreateResp, err := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(ECCSRKTemplate), }.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } srk := NamedHandle{ Handle: srkCreateResp.ObjectHandle, Name: srkCreateResp.Name, } policy, err := dupPolicyDigest(thetpm) if err != nil { t.Fatalf("dupPolicyDigest: %v", err) } keyPass := []byte("foo") t.Log("### Create Object to be duplicated") objectCreateLoadedResp, err := CreateLoaded{ ParentHandle: srk, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: keyPass, }, }, }, InPublic: New2BTemplate(&TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: false, FixedParent: false, SensitiveDataOrigin: true, UserWithAuth: true, Decrypt: true, SignEncrypt: true, }, AuthPolicy: TPM2BDigest{Buffer: policy}, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), Unique: NewTPMUPublicID( TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{Buffer: make([]byte, 32)}, Y: TPM2BECCParameter{Buffer: make([]byte, 32)}, }, ), }), }.Execute(thetpm) if err != nil { t.Fatalf("TPM2_CreateLoaded: %v", err) } // We don't need the owner SRK handle anymore. FlushContext{FlushHandle: srkCreateResp.ObjectHandle}.Execute(thetpm) t.Log("### Create Endorsement SRK (New Parent)") srk2CreateResp, err := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: New2B(ECCSRKTemplate), }.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } defer FlushContext{FlushHandle: srk2CreateResp.ObjectHandle}.Execute(thetpm) srk2 := NamedHandle{ Handle: srk2CreateResp.ObjectHandle, Name: srk2CreateResp.Name, } t.Log("### Duplicate Object") duplicateResp, err := Duplicate{ ObjectHandle: AuthHandle{ Handle: objectCreateLoadedResp.ObjectHandle, Name: objectCreateLoadedResp.Name, Auth: Policy(TPMAlgSHA256, 16, PolicyCallback(func(tpm transport.TPM, handle TPMISHPolicy, _ TPM2BNonce) error { _, err := PolicyCommandCode{ PolicySession: handle, Code: TPMCCDuplicate, }.Execute(tpm) return err })), }, NewParentHandle: srk2, Symmetric: TPMTSymDef{ Algorithm: TPMAlgNull, }, }.Execute(thetpm) if err != nil { t.Fatalf("TPM2_Duplicate: %v", err) } // We don't need the original object handle anymore. FlushContext{FlushHandle: objectCreateLoadedResp.ObjectHandle}.Execute(thetpm) t.Log("### Import Object") importResp, err := Import{ ParentHandle: AuthHandle{ Handle: srk2.Handle, Name: srk2.Name, Auth: PasswordAuth(nil), }, ObjectPublic: objectCreateLoadedResp.OutPublic, Duplicate: duplicateResp.Duplicate, InSymSeed: duplicateResp.OutSymSeed, Symmetric: TPMTSymDef{ Algorithm: TPMAlgNull, }, }.Execute(thetpm) if err != nil { t.Fatalf("TPM2_Import: %v", err) } t.Log("### Load Imported Object") loadResp, err := Load{ ParentHandle: srk2, InPrivate: importResp.OutPrivate, InPublic: objectCreateLoadedResp.OutPublic, }.Execute(thetpm) if err != nil { t.Fatalf("TPM2_Load: %v", err) } defer FlushContext{FlushHandle: loadResp.ObjectHandle}.Execute(thetpm) } func dupPolicyDigest(thetpm transport.TPM) ([]byte, error) { sess, cleanup, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { return nil, err } defer cleanup() _, err = PolicyCommandCode{ PolicySession: sess.Handle(), Code: TPMCCDuplicate, }.Execute(thetpm) if err != nil { return nil, err } pgd, err := PolicyGetDigest{ PolicySession: sess.Handle(), }.Execute(thetpm) if err != nil { return nil, err } _, err = FlushContext{FlushHandle: sess.Handle()}.Execute(thetpm) if err != nil { return nil, err } return pgd.PolicyDigest.Buffer, nil } go-tpm-0.9.3/tpm2/test/ecdh_test.go000066400000000000000000000056401473040456300170730ustar00rootroot00000000000000package tpm2test import ( "crypto/ecdh" "crypto/rand" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestECDH(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Create a TPM ECDH key tpmCreate := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, AdminWithPolicy: false, NoDA: true, EncryptedDuplication: false, Restricted: false, Decrypt: true, SignEncrypt: false, X509Sign: false, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, Scheme: TPMTECCScheme{ Scheme: TPMAlgECDH, Details: NewTPMUAsymScheme( TPMAlgECDH, &TPMSKeySchemeECDH{ HashAlg: TPMAlgSHA256, }, ), }, }, ), }), } // Use NIST P-256 curve := ecdh.P256() tpmCreateRsp, err := tpmCreate.Execute(thetpm) if err != nil { t.Fatalf("could not create the TPM key: %v", err) } outPub, err := tpmCreateRsp.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } tpmPub, err := outPub.Unique.ECC() if err != nil { t.Fatalf("%v", err) } eccParms, err := outPub.Parameters.ECCDetail() if err != nil { t.Fatalf("%v", err) } tpmPubKey, err := ECDHPub(eccParms, tpmPub) if err != nil { t.Fatalf("could not unmarshal pubkey: %v", err) } // Create a SW ECDH key swPriv, err := curve.GenerateKey(rand.Reader) if err != nil { t.Fatalf("could not create the SW key: %v", err) } x, y, err := ECCPoint(swPriv.PublicKey()) if err != nil { t.Fatalf("could not get SW key point: %v", err) } swPub := TPMSECCPoint{ X: TPM2BECCParameter{Buffer: x.FillBytes(make([]byte, 32))}, Y: TPM2BECCParameter{Buffer: y.FillBytes(make([]byte, 32))}, } // Calculate Z based on the SW priv * TPM pub zx, err := swPriv.ECDH(tpmPubKey) if err != nil { t.Fatalf("ecdh exchange: %v", err) } z := TPMSECCPoint{ X: TPM2BECCParameter{Buffer: zx}, } // Calculate Z based on TPM priv * SW pub ecdh := ECDHZGen{ KeyHandle: AuthHandle{ Handle: tpmCreateRsp.ObjectHandle, Name: tpmCreateRsp.Name, Auth: PasswordAuth(nil), }, InPoint: New2B(swPub), } ecdhRsp, err := ecdh.Execute(thetpm) if err != nil { t.Fatalf("ECDH_ZGen failed: %v", err) } outPoint, err := ecdhRsp.OutPoint.Contents() if err != nil { t.Fatalf("%v", err) } if !cmp.Equal(z.X, outPoint.X, cmpopts.IgnoreUnexported(z.X)) { t.Errorf("want %x got %x", z, outPoint) } } go-tpm-0.9.3/tpm2/test/ek_test.go000066400000000000000000000153731473040456300165730ustar00rootroot00000000000000package tpm2test import ( "errors" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) // Test creating a sealed data blob on the standard-template EK using its policy. func TestEKPolicy(t *testing.T) { templates := map[string]TPMTPublic{ "RSA": RSAEKTemplate, "ECC": ECCEKTemplate, } // Run the whole test for each of RSA and ECC EKs. for name, ekTemplate := range templates { t.Run(name, func(t *testing.T) { ekTest(t, ekTemplate) }) } } func ekPolicy(t transport.TPM, handle TPMISHPolicy, nonceTPM TPM2BNonce) error { cmd := PolicySecret{ AuthHandle: TPMRHEndorsement, PolicySession: handle, NonceTPM: nonceTPM, } _, err := cmd.Execute(t) return err } // This function tests a lot of combinations of authorizing the EK policy. func ekTest(t *testing.T, ekTemplate TPMTPublic) { type ekTestCase struct { name string // Use Policy instead of PolicySession, passing the callback instead of // managing it ourselves? jitPolicySession bool // Use the policy session for decrypt? (Incompatible with decryptAnotherSession) decryptPolicySession bool // Use another session for decrypt? (Incompatible with decryptPolicySession) decryptAnotherSession bool // Use a bound session? bound bool // Use a salted session? salted bool } var cases []ekTestCase for jit := 0; jit < 2; jit++ { for decryptPol := 0; decryptPol < 2; decryptPol++ { for decryptAnother := 0; decryptAnother < 2; decryptAnother++ { if decryptPol != 0 && decryptAnother != 0 { continue } for bound := 0; bound < 2; bound++ { for salted := 0; salted < 2; salted++ { nextCase := ekTestCase{ name: "test", jitPolicySession: jit != 0, decryptPolicySession: decryptPol != 0, decryptAnotherSession: decryptAnother != 0, bound: bound != 0, salted: salted != 0, } if nextCase.jitPolicySession { nextCase.name += "-jit" } else { nextCase.name += "-standalone" } if nextCase.decryptPolicySession { nextCase.name += "-decrypt-same" } if nextCase.decryptAnotherSession { nextCase.name += "-decrypt-another" } if nextCase.bound { nextCase.name += "-bound" } if nextCase.salted { nextCase.name += "-salted" } cases = append(cases, nextCase) } } } } } thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() for _, c := range cases { t.Run(c.name, func(t *testing.T) { // Create the EK createEKCmd := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: New2B(ekTemplate), } createEKRsp, err := createEKCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } outPub, err := createEKRsp.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } switch outPub.Type { case TPMAlgRSA: rsa, err := outPub.Unique.RSA() if err != nil { t.Fatalf("%v", err) } t.Logf("EK pub:\n%x\n", rsa.Buffer) case TPMAlgECC: ecc, err := outPub.Unique.ECC() if err != nil { t.Fatalf("%v", err) } t.Logf("EK pub:\n%x\n%x\n", ecc.X, ecc.Y) } t.Logf("EK name: %x", createEKRsp.Name) defer func() { // Flush the EK flush := FlushContext{FlushHandle: createEKRsp.ObjectHandle} if _, err := flush.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() // Exercise the EK's auth policy (PolicySecret[RH_ENDORSEMENT]) // by creating an object under it data := []byte("secrets") createBlobCmd := Create{ ParentHandle: NamedHandle{ Handle: createEKRsp.ObjectHandle, Name: createEKRsp.Name, }, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ Data: NewTPMUSensitiveCreate(&TPM2BSensitiveData{ Buffer: data, }), }, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, NoDA: true, }, }), } var sessions []Session if c.decryptAnotherSession { sessions = append(sessions, HMAC(TPMAlgSHA1, 16, AESEncryption(128, EncryptIn))) } var options []AuthOption if c.decryptPolicySession { options = append(options, AESEncryption(128, EncryptIn)) } if c.bound { options = append(options, Bound(createEKRsp.ObjectHandle, createEKRsp.Name, nil)) } if c.salted { options = append(options, Salted(createEKRsp.ObjectHandle, *outPub)) } var s Session if c.jitPolicySession { // Use the convenience function to pass a policy callback. s = Policy(TPMAlgSHA256, 16, ekPolicy, options...) } else { // Set up a session we have to execute and clean up ourselves. var cleanup func() error var err error s, cleanup, err = PolicySession(thetpm, TPMAlgSHA256, 16, options...) if err != nil { t.Fatalf("creating session: %v", err) } // Clean up the session at the end of the test. defer func() { if err := cleanup(); err != nil { t.Fatalf("cleaning up policy session: %v", err) } }() // Execute the same callback ourselves. if err = ekPolicy(thetpm, s.Handle(), s.NonceTPM()); err != nil { t.Fatalf("executing EK policy: %v", err) } } createBlobCmd.ParentHandle = AuthHandle{ Handle: createEKRsp.ObjectHandle, Name: createEKRsp.Name, Auth: s, } if _, err := createBlobCmd.Execute(thetpm, sessions...); err != nil { t.Fatalf("%v", err) } if !c.jitPolicySession { // If we're not using a "just-in-time" session with a callback, // we have to re-initialize the session. if err = ekPolicy(thetpm, s.Handle(), s.NonceTPM()); err != nil { t.Fatalf("executing EK policy: %v", err) } } // Try again and make sure it succeeds again. if _, err = createBlobCmd.Execute(thetpm, sessions...); err != nil { t.Fatalf("%v", err) } if !c.jitPolicySession { // Finally, for non-JIT policy sessions, make sure we fail if // we don't re-initialize the session. // This is because after using a policy session, it's as if // PolicyRestart was called. _, err = createBlobCmd.Execute(thetpm, sessions...) if !errors.Is(err, TPMRCPolicyFail) { t.Errorf("want TPM_RC_POLICY_FAIL, got %v", err) } var fmt1 TPMFmt1Error if !errors.As(err, &fmt1) { t.Errorf("want a Fmt1Error, got %v", err) } else if isSession, session := fmt1.Session(); !isSession || session != 1 { t.Errorf("want TPM_RC_POLICY_FAIL on session 1, got %v", err) } } }) } } go-tpm-0.9.3/tpm2/test/evict_control_test.go000066400000000000000000000014251473040456300210370ustar00rootroot00000000000000package tpm2test import ( "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestEvictControl(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() srkCreate := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(ECCSRKTemplate), } srkCreateRsp, err := srkCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } _, err = EvictControl{ Auth: TPMRHOwner, ObjectHandle: &NamedHandle{ Handle: srkCreateRsp.ObjectHandle, Name: srkCreateRsp.Name, }, PersistentHandle: 0x81000000, }.Execute(thetpm) if err != nil { t.Fatalf("could not persist: %v", err) } } go-tpm-0.9.3/tpm2/test/get_random_test.go000066400000000000000000000006761473040456300203130ustar00rootroot00000000000000package tpm2test import ( "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestGetRandom(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() grc := GetRandom{ BytesRequested: 16, } if _, err := grc.Execute(thetpm); err != nil { t.Fatalf("GetRandom failed: %v", err) } } go-tpm-0.9.3/tpm2/test/hash_sequence_hash_test.go000066400000000000000000000127251473040456300220100ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto/rand" "crypto/sha256" "fmt" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestHash(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() run := func(t *testing.T, data []byte, hierarchy TPMHandle, thetpm transport.TPM) { hash := Hash{ Data: TPM2BMaxBuffer{Buffer: data}, HashAlg: TPMAlgSHA256, Hierarchy: hierarchy, } rspHash, err := hash.Execute(thetpm) if err != nil { t.Fatalf("Hash failed: %v", err) } gotDigest := rspHash.OutHash.Buffer wantDigest := sha256.Sum256(data) if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("Hash(%q) returned digest %x, want %x", data, gotDigest, wantDigest) } } t.Run("Null hierarchy", func(t *testing.T) { run(t, []byte("fiona"), TPMRHNull, thetpm) }) t.Run("Owner hierarchy", func(t *testing.T) { run(t, []byte("charlie"), TPMRHOwner, thetpm) }) } func TestHashNullHierarchy(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() data := []byte("carolyn") hash := Hash{ Data: TPM2BMaxBuffer{Buffer: data}, HashAlg: TPMAlgSHA256, } rspHash, err := hash.Execute(thetpm) if err != nil { t.Fatalf("Hash failed: %v", err) } gotDigest := rspHash.OutHash.Buffer wantDigest := sha256.Sum256(data) if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("Hash(%q) returned digest %x, want %x", data, gotDigest, wantDigest) } } func TestHashSequence(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() run := func(t *testing.T, bufferSize int, password string, hierarchy TPMHandle, thetpm transport.TPM) { maxDigestBuffer := 1024 Auth := []byte(password) hashSequenceStart := HashSequenceStart{ Auth: TPM2BAuth{ Buffer: Auth, }, HashAlg: TPMAlgSHA256, } rspHSS, err := hashSequenceStart.Execute(thetpm) if err != nil { t.Fatalf("HashSequenceStart failed: %v", err) } authHandle := AuthHandle{ Handle: rspHSS.SequenceHandle, Name: TPM2BName{ Buffer: Auth, }, Auth: PasswordAuth(Auth), } data := make([]byte, bufferSize) rand.Read(data) wantDigest := sha256.Sum256(data) for len(data) > maxDigestBuffer { sequenceUpdate := SequenceUpdate{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data[:maxDigestBuffer], }, } _, err = sequenceUpdate.Execute(thetpm) if err != nil { t.Fatalf("SequenceUpdate failed: %v", err) } data = data[maxDigestBuffer:] } sequenceComplete := SequenceComplete{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data, }, Hierarchy: hierarchy, } rspSC, err := sequenceComplete.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } gotDigest := rspSC.Result.Buffer if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("The resulting digest %x, is not expected %x", gotDigest, wantDigest) } } bufferSizes := []int{512, 1024, 2048, 4096} password := "password" for _, bufferSize := range bufferSizes { t.Run(fmt.Sprintf("Null hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, bufferSize, password, TPMRHNull, thetpm) }) t.Run(fmt.Sprintf("Owner hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, bufferSize, password, TPMRHOwner, thetpm) }) } } func TestHashSequenceNullHierarchy(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() run := func(t *testing.T, bufferSize int, password string, thetpm transport.TPM) { maxDigestBuffer := 1024 Auth := []byte(password) hashSequenceStart := HashSequenceStart{ Auth: TPM2BAuth{ Buffer: Auth, }, HashAlg: TPMAlgSHA256, } rspHSS, err := hashSequenceStart.Execute(thetpm) if err != nil { t.Fatalf("HashSequenceStart failed: %v", err) } authHandle := AuthHandle{ Handle: rspHSS.SequenceHandle, Name: TPM2BName{ Buffer: Auth, }, Auth: PasswordAuth(Auth), } data := make([]byte, bufferSize) rand.Read(data) wantDigest := sha256.Sum256(data) for len(data) > maxDigestBuffer { sequenceUpdate := SequenceUpdate{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data[:maxDigestBuffer], }, } _, err = sequenceUpdate.Execute(thetpm) if err != nil { t.Fatalf("SequenceUpdate failed: %v", err) } data = data[maxDigestBuffer:] } sequenceComplete := SequenceComplete{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data, }, } rspSC, err := sequenceComplete.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } gotDigest := rspSC.Result.Buffer if !bytes.Equal(gotDigest, wantDigest[:]) { t.Errorf("The resulting digest %x, is not expected %x", gotDigest, wantDigest) } } // t *testing.T, bufferSize int, password string, hierarchy TPMHandle, thetpm transport.TPM bufferSizes := []int{512, 1024, 2048, 4096} password := "password" for _, bufferSize := range bufferSizes { t.Run(fmt.Sprintf("Null hierarchy [bufferSize=%d]", bufferSize), func(t *testing.T) { run(t, bufferSize, password, thetpm) }) } } go-tpm-0.9.3/tpm2/test/hierarchy_change_auth_test.go000066400000000000000000000025371473040456300224760ustar00rootroot00000000000000package tpm2test import ( "errors" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestHierarchyChangeAuth(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() authKey := []byte("authkey") newAuthKey := []byte("newAuthKey") t.Run("HierarchyChangeAuthOwner", func(t *testing.T) { hca := HierarchyChangeAuth{ AuthHandle: TPMRHOwner, NewAuth: TPM2BAuth{ Buffer: authKey, }, } _, err := hca.Execute(thetpm) if err != nil { t.Errorf("failed HierarchyChangeAuth: %v", err) } }) t.Run("HierarchyChangeAuthOwnerUnauth", func(t *testing.T) { hca := HierarchyChangeAuth{ AuthHandle: TPMRHOwner, NewAuth: TPM2BAuth{ Buffer: newAuthKey, }, } _, err := hca.Execute(thetpm) if !errors.Is(err, TPMRCBadAuth) { t.Errorf("failed HierarchyChangeAuthWithoutAuth: want TPM_RC_BAD_AUTH, got %v", err) } }) t.Run("HierarchyChangeAuthOwnerAuth", func(t *testing.T) { hca := HierarchyChangeAuth{ AuthHandle: AuthHandle{ Handle: TPMRHOwner, Auth: PasswordAuth(authKey), }, NewAuth: TPM2BAuth{ Buffer: newAuthKey, }, } _, err := hca.Execute(thetpm) if err != nil { t.Errorf("failed HierarchyChangeAuthWithAuth: %v", err) } }) } go-tpm-0.9.3/tpm2/test/hmac_start_test.go000066400000000000000000000212331473040456300203110ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto/rand" "fmt" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestHmacStart(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() run := func(t *testing.T, data []byte, password []byte, hierarchy TPMHandle, thetpm transport.TPM) []byte { maxInputBuffer := 1024 sas, sasCloser, err := HMACSession(thetpm, TPMAlgSHA256, 16) if err != nil { t.Fatalf("could not create hmac key authorization session: %v", err) } defer func() { _ = sasCloser() }() createPrimary := CreatePrimary{ PrimaryHandle: AuthHandle{ Handle: hierarchy, Auth: sas, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms(TPMAlgKeyedHash, &TPMSKeyedHashParms{ Scheme: TPMTKeyedHashScheme{ Scheme: TPMAlgHMAC, Details: NewTPMUSchemeKeyedHash(TPMAlgHMAC, &TPMSSchemeHMAC{ HashAlg: TPMAlgSHA256, }), }, }), }), } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("CreatePrimary HMAC key failed: %v", err) } flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer func() { _, _ = flushContext.Execute(thetpm) }() hmacStart := HmacStart{ Handle: AuthHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, Auth: sas, }, Auth: TPM2BAuth{ Buffer: password, }, HashAlg: TPMAlgNull, } rspHS, err := hmacStart.Execute(thetpm) if err != nil { t.Fatalf("HmacStart failed: %v", err) } authHandle := AuthHandle{ Handle: rspHS.SequenceHandle, Auth: PasswordAuth(password), } for len(data) > maxInputBuffer { sequenceUpdate := SequenceUpdate{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data[:maxInputBuffer], }, } _, err = sequenceUpdate.Execute(thetpm) if err != nil { t.Fatalf("SequenceUpdate failed: %v", err) } data = data[maxInputBuffer:] } sequenceComplete := SequenceComplete{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data, }, Hierarchy: hierarchy, } rspSC, err := sequenceComplete.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } return rspSC.Result.Buffer } bufferSizes := []int{512, 1024, 2048, 4096} password := make([]byte, 8) _, _ = rand.Read(password) hierarchies := map[string]TPMHandle{ "Null": TPMRHNull, "Owner": TPMRHOwner, "Endorsement": TPMRHEndorsement} for _, bufferSize := range bufferSizes { data := make([]byte, bufferSize) for name, hierarchy := range hierarchies { t.Run(fmt.Sprintf("%s hierarchy [bufferSize=%d]", name, bufferSize), func(t *testing.T) { _, _ = rand.Read(data) // HMAC Key is not exported and can not be externally validated, // run HMAC twice with same data and confirm they are the same hmac1 := run(t, data, password, hierarchy, thetpm) hmac2 := run(t, data, password, hierarchy, thetpm) if !bytes.Equal(hmac1, hmac2) { t.Errorf("hmac %x is not expected %x", hmac1, hmac2) } }) } } t.Run("Same key multiple sequences", func(t *testing.T) { sas, sasCloser, err := HMACSession(thetpm, TPMAlgSHA256, 16) if err != nil { t.Fatalf("could not create hmac key authorization session: %v", err) } defer func() { _ = sasCloser() }() createPrimary := CreatePrimary{ PrimaryHandle: AuthHandle{ Handle: TPMRHNull, Auth: sas, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms(TPMAlgKeyedHash, &TPMSKeyedHashParms{ Scheme: TPMTKeyedHashScheme{ Scheme: TPMAlgHMAC, Details: NewTPMUSchemeKeyedHash(TPMAlgHMAC, &TPMSSchemeHMAC{ HashAlg: TPMAlgSHA256, }), }, }), }), } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("CreatePrimary HMAC key failed: %v", err) } flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer func() { _, _ = flushContext.Execute(thetpm) }() hmacStart := HmacStart{ Handle: AuthHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, Auth: sas, }, Auth: TPM2BAuth{ Buffer: password, }, HashAlg: TPMAlgNull, } rspHS1, err := hmacStart.Execute(thetpm) if err != nil { t.Fatalf("HmacStart failed: %v", err) } authHandle1 := AuthHandle{ Handle: rspHS1.SequenceHandle, Auth: PasswordAuth(password), } rspHS2, err := hmacStart.Execute(thetpm) if err != nil { t.Fatalf("HmacStart failed: %v", err) } authHandle2 := AuthHandle{ Handle: rspHS2.SequenceHandle, Auth: PasswordAuth(password), } if rspHS1.SequenceHandle.HandleValue() == rspHS2.SequenceHandle.HandleValue() { t.Error("sequence handles are not unique") } sequenceComplete1 := SequenceComplete{ SequenceHandle: authHandle1, } _, err = sequenceComplete1.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } sequenceComplete2 := SequenceComplete{ SequenceHandle: authHandle2, } _, err = sequenceComplete2.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } }) } func TestHmacStartNoKeyAuth(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() run := func(t *testing.T, data []byte, password []byte, hierarchy TPMHandle, thetpm transport.TPM) []byte { maxInputBuffer := 1024 createPrimary := CreatePrimary{ PrimaryHandle: hierarchy, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms(TPMAlgKeyedHash, &TPMSKeyedHashParms{ Scheme: TPMTKeyedHashScheme{ Scheme: TPMAlgHMAC, Details: NewTPMUSchemeKeyedHash(TPMAlgHMAC, &TPMSSchemeHMAC{ HashAlg: TPMAlgSHA256, }), }, }), }), } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("CreatePrimary HMAC key failed: %v", err) } flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer func() { _, _ = flushContext.Execute(thetpm) }() hmacStart := HmacStart{ Handle: NamedHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, }, Auth: TPM2BAuth{ Buffer: password, }, HashAlg: TPMAlgNull, } rspHS, err := hmacStart.Execute(thetpm) if err != nil { t.Fatalf("HmacStart failed: %v", err) } authHandle := AuthHandle{ Handle: rspHS.SequenceHandle, Auth: PasswordAuth(password), } for len(data) > maxInputBuffer { sequenceUpdate := SequenceUpdate{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data[:maxInputBuffer], }, } _, err = sequenceUpdate.Execute(thetpm) if err != nil { t.Fatalf("SequenceUpdate failed: %v", err) } data = data[maxInputBuffer:] } sequenceComplete := SequenceComplete{ SequenceHandle: authHandle, Buffer: TPM2BMaxBuffer{ Buffer: data, }, Hierarchy: hierarchy, } rspSC, err := sequenceComplete.Execute(thetpm) if err != nil { t.Fatalf("SequenceComplete failed: %v", err) } return rspSC.Result.Buffer } password := make([]byte, 8) _, _ = rand.Read(password) hierarchies := map[string]TPMHandle{ "Null": TPMRHNull, "Owner": TPMRHOwner, "Endorsement": TPMRHEndorsement} data := make([]byte, 1024) for name, hierarchy := range hierarchies { t.Run(fmt.Sprintf("%s hierarchy [bufferSize=1024]", name), func(t *testing.T) { _, _ = rand.Read(data) // HMAC Key is not exported and can not be externally validated, // run HMAC twice with same data and confirm they are the same hmac1 := run(t, data, password, hierarchy, thetpm) hmac2 := run(t, data, password, hierarchy, thetpm) if !bytes.Equal(hmac1, hmac2) { t.Errorf("hmac %x is not expected %x", hmac1, hmac2) } }) } } go-tpm-0.9.3/tpm2/test/import_test.go000066400000000000000000000042011473040456300174720ustar00rootroot00000000000000package tpm2test import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestImport(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() srkCreate := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(ECCSRKTemplate), } srkCreateRsp, err := srkCreate.Execute(thetpm) if err != nil { t.Fatalf("could not generate SRK: %v", err) } defer func() { flush := FlushContext{ FlushHandle: srkCreateRsp.ObjectHandle, } _, err := flush.Execute(thetpm) if err != nil { t.Fatalf("could not flush SRK: %v", err) } }() pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("failed to generate ecdsa key: %v", err) } sens2B := Marshal(TPMTSensitive{ SensitiveType: TPMAlgECC, Sensitive: NewTPMUSensitiveComposite( TPMAlgECC, &TPM2BECCParameter{Buffer: pk.D.FillBytes(make([]byte, 32))}, ), }) l := Marshal(TPM2BPrivate{Buffer: sens2B}) _, err = Import{ ParentHandle: &AuthHandle{ Handle: srkCreateRsp.ObjectHandle, Name: srkCreateRsp.Name, Auth: PasswordAuth(nil), }, Duplicate: TPM2BPrivate{Buffer: l}, ObjectPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, SensitiveDataOrigin: false, EncryptedDuplication: false, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, Scheme: TPMTECCScheme{ Scheme: TPMAlgECDSA, Details: NewTPMUAsymScheme( TPMAlgECDSA, &TPMSSigSchemeECDSA{ HashAlg: TPMAlgSHA256, }, ), }, }, ), Unique: NewTPMUPublicID( TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{ Buffer: pk.X.FillBytes(make([]byte, 32)), }, Y: TPM2BECCParameter{ Buffer: pk.Y.FillBytes(make([]byte, 32)), }, }, ), }), }.Execute(thetpm) if err != nil { t.Fatalf("could not import: %v", err) } } go-tpm-0.9.3/tpm2/test/load_external_test.go000066400000000000000000000043341473040456300210100ustar00rootroot00000000000000package tpm2test import ( "encoding/hex" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func decodeHex(t *testing.T, h string) []byte { t.Helper() data, err := hex.DecodeString(h) if err != nil { t.Fatalf("could not decode '%v' as hex data: %v", h, err) } return data } func TestLoadExternal(t *testing.T) { loads := map[string]*LoadExternal{ "ECCNoSensitive": { InPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), Unique: NewTPMUPublicID( // This happens to be a P256 EKpub from the simulator TPMAlgECC, &TPMSECCPoint{ X: TPM2BECCParameter{Buffer: decodeHex(t, "9855efa3514873b88067ab127b2d4692864a395db3d9e4ccad0592478a245c16")}, Y: TPM2BECCParameter{Buffer: decodeHex(t, "e802a26649839a2d7b13c812a5dc0b61c110cbe62db784d96e60a823448c8993")}, }, ), }), }, "KeyedHashSensitive": { InPrivate: New2B( TPMTSensitive{ SensitiveType: TPMAlgKeyedHash, SeedValue: TPM2BDigest{ Buffer: []byte("obfuscation is my middle name!!!"), }, Sensitive: NewTPMUSensitiveComposite( TPMAlgKeyedHash, &TPM2BSensitiveData{ Buffer: []byte("secrets"), }, ), }), InPublic: New2B( TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, Unique: NewTPMUPublicID( TPMAlgKeyedHash, &TPM2BDigest{ // SHA256("obfuscation is my middle name!!!secrets") Buffer: decodeHex(t, "ed4fe8e2bff97665e7bfbe27c2365d07a9be91dd92d997cd91cc706b6074eb08"), }, ), }), }, } thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() for name, load := range loads { t.Run(name, func(t *testing.T) { rsp, err := load.Execute(thetpm) if err != nil { t.Fatalf("error from LoadExternal: %v", err) } if _, err = (FlushContext{FlushHandle: rsp.ObjectHandle}).Execute(thetpm); err != nil { t.Errorf("error from FlushContext: %v", err) } }) } } go-tpm-0.9.3/tpm2/test/names_test.go000066400000000000000000000043771473040456300173010ustar00rootroot00000000000000package tpm2test import ( "bytes" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestHandleName(t *testing.T) { want := []byte{0x40, 0x00, 0x00, 0x0B} name := HandleName(TPMRHEndorsement) if !bytes.Equal(want, name.Buffer) { t.Errorf("Incorrect name for RH_ENDORSEMENT (want %x got %x)", want, name.Buffer) } } func TestObjectName(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: New2B(ECCEKTemplate), } rsp, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not call TPM2_CreatePrimary: %v", err) } flush := FlushContext{FlushHandle: rsp.ObjectHandle} defer flush.Execute(thetpm) public := rsp.OutPublic want := rsp.Name pub, err := public.Contents() if err != nil { t.Fatalf("%v", err) } name, err := ObjectName(pub) if err != nil { t.Fatalf("error from ObjectName: %v", err) } if !bytes.Equal(want.Buffer, name.Buffer) { t.Errorf("Incorrect name for ECC EK (want %x got %x)", want.Buffer, name.Buffer) } } func TestNVName(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() public := New2B( TPMSNVPublic{ NVIndex: TPMHandle(0x0180000F), NameAlg: TPMAlgSHA256, Attributes: TPMANV{ OwnerWrite: true, OwnerRead: true, NT: TPMNTOrdinary, }, DataSize: 4, }) defineSpace := NVDefineSpace{ AuthHandle: TPMRHOwner, PublicInfo: public, } if _, err := defineSpace.Execute(thetpm); err != nil { t.Fatalf("could not call TPM2_DefineSpace: %v", err) } pub, err := public.Contents() if err != nil { t.Fatalf("%v", err) } readPublic := NVReadPublic{ NVIndex: pub.NVIndex, } rsp, err := readPublic.Execute(thetpm) if err != nil { t.Fatalf("could not call TPM2_ReadPublic: %v", err) } want := rsp.NVName name, err := NVName(pub) if err != nil { t.Fatalf("error from NVIndexName: %v", err) } if !bytes.Equal(want.Buffer, name.Buffer) { t.Errorf("Incorrect name for NV index (want %x got %x)", want.Buffer, name.Buffer) } } go-tpm-0.9.3/tpm2/test/nv_test.go000066400000000000000000000107261473040456300166140ustar00rootroot00000000000000package tpm2test import ( "bytes" "encoding/binary" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestNVAuthWrite(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() def := NVDefineSpace{ AuthHandle: TPMRHOwner, Auth: TPM2BAuth{ Buffer: []byte("p@ssw0rd"), }, PublicInfo: New2B( TPMSNVPublic{ NVIndex: TPMHandle(0x0180000F), NameAlg: TPMAlgSHA256, Attributes: TPMANV{ OwnerWrite: true, OwnerRead: true, AuthWrite: true, AuthRead: true, NT: TPMNTOrdinary, NoDA: true, }, DataSize: 4, }), } if _, err := def.Execute(thetpm); err != nil { t.Fatalf("Calling TPM2_NV_DefineSpace: %v", err) } pub, err := def.PublicInfo.Contents() if err != nil { t.Fatalf("%v", err) } nvName, err := NVName(pub) if err != nil { t.Fatalf("Calculating name of NV index: %v", err) } prewrite := NVWrite{ AuthHandle: AuthHandle{ Handle: pub.NVIndex, Name: *nvName, Auth: PasswordAuth([]byte("p@ssw0rd")), }, NVIndex: NamedHandle{ Handle: pub.NVIndex, Name: *nvName, }, Data: TPM2BMaxNVBuffer{ Buffer: []byte{0x01, 0x02, 0x03, 0x04}, }, Offset: 0, } if _, err := prewrite.Execute(thetpm); err != nil { t.Errorf("Calling TPM2_NV_Write: %v", err) } read := NVReadPublic{ NVIndex: pub.NVIndex, } readRsp, err := read.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) } t.Logf("Name: %x", readRsp.NVName.Buffer) write := NVWrite{ AuthHandle: AuthHandle{ Handle: TPMRHOwner, Auth: HMAC(TPMAlgSHA256, 16, Auth([]byte{})), }, NVIndex: NamedHandle{ Handle: pub.NVIndex, Name: readRsp.NVName, }, Data: TPM2BMaxNVBuffer{ Buffer: []byte{0x01, 0x02, 0x03, 0x04}, }, Offset: 0, } if _, err := write.Execute(thetpm); err != nil { t.Errorf("Calling TPM2_NV_Write: %v", err) } } func TestNVAuthIncrement(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Define the counter space def := NVDefineSpace{ AuthHandle: TPMRHOwner, Auth: TPM2BAuth{ Buffer: []byte("p@ssw0rd"), }, PublicInfo: New2B( TPMSNVPublic{ NVIndex: TPMHandle(0x0180000F), NameAlg: TPMAlgSHA256, Attributes: TPMANV{ OwnerWrite: true, OwnerRead: true, AuthWrite: true, AuthRead: true, NT: TPMNTCounter, NoDA: true, }, DataSize: 8, }), } if _, err := def.Execute(thetpm); err != nil { t.Fatalf("Calling TPM2_NV_DefineSpace: %v", err) } pub, err := def.PublicInfo.Contents() if err != nil { t.Fatalf("%v", err) } // Calculate the Name of the index as of its creation // (i.e., without NV_WRITTEN set). nvName, err := NVName(pub) if err != nil { t.Fatalf("Calculating name of NV index: %v", err) } incr := NVIncrement{ AuthHandle: AuthHandle{ Handle: TPMRHOwner, Auth: HMAC(TPMAlgSHA256, 16, Auth([]byte{})), }, NVIndex: NamedHandle{ Handle: pub.NVIndex, Name: *nvName, }, } if _, err := incr.Execute(thetpm); err != nil { t.Errorf("Calling TPM2_NV_Increment: %v", err) } // The NV index's Name has changed. Ask the TPM for it. readPub := NVReadPublic{ NVIndex: pub.NVIndex, } readPubRsp, err := readPub.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_ReadPublic: %v", err) } incr.NVIndex = NamedHandle{ Handle: pub.NVIndex, Name: readPubRsp.NVName, } read := NVRead{ AuthHandle: AuthHandle{ Handle: TPMRHOwner, Auth: HMAC(TPMAlgSHA256, 16, Auth([]byte{})), }, NVIndex: NamedHandle{ Handle: pub.NVIndex, Name: readPubRsp.NVName, }, Size: 8, } readRsp, err := read.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_Read: %v", err) } if _, err := incr.Execute(thetpm); err != nil { t.Errorf("Calling TPM2_NV_Increment: %v", err) } var val1 uint64 err = binary.Read(bytes.NewReader(readRsp.Data.Buffer), binary.BigEndian, &val1) if err != nil { t.Fatalf("Parsing counter: %v", err) } readRsp, err = read.Execute(thetpm) if err != nil { t.Fatalf("Calling TPM2_NV_Read: %v", err) } var val2 uint64 err = binary.Read(bytes.NewReader(readRsp.Data.Buffer), binary.BigEndian, &val2) if err != nil { t.Fatalf("Parsing counter: %v", err) } if val2 != (val1 + 1) { t.Errorf("want %v got %v", val1+1, val2) } } go-tpm-0.9.3/tpm2/test/object_change_auth_test.go000066400000000000000000000117751473040456300217720ustar00rootroot00000000000000package tpm2test import ( "bytes" "errors" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestObjectChangeAuth(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Create the SRK // Put a password on the SRK to test more of the flows. createSRKCmd := CreatePrimary{ PrimaryHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: []byte(nil), }, }, }, InPublic: New2B(ECCSRKTemplate), } createSRKRsp, err := createSRKCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } defer func() { // Flush the SRK flushSRKCmd := FlushContext{FlushHandle: createSRKRsp.ObjectHandle} if _, err := flushSRKCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() // Data we are sealing data := []byte("secrets") // Original auth for the key auth := []byte("oldauth") // New auth we are changing to newauth := []byte("newauth") // Create a sealed blob under the SRK createBlobCmd := Create{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: PasswordAuth(nil), }, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: auth, }, Data: NewTPMUSensitiveCreate(&TPM2BSensitiveData{ Buffer: data, }), }, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, NoDA: true, }, }), } var createBlobRsp *CreateResponse t.Run("Create", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) loadBlobCmd := Load{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: PasswordAuth(nil), }, InPrivate: createBlobRsp.OutPrivate, InPublic: createBlobRsp.OutPublic, } loadBlobRsp, err := loadBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } defer func() { // Flush the blob flushBlobCmd := FlushContext{FlushHandle: loadBlobRsp.ObjectHandle} if _, err := flushBlobCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() unsealCmd := Unseal{ ItemHandle: NamedHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, }, } // Unseal the blob with a password session t.Run("WithPassword", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth(auth), } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } }) // Change the auth of the object t.Run("ObjectChangeAuth", func(t *testing.T) { oca := ObjectChangeAuth{ ObjectHandle: AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth(auth), }, ParentHandle: NamedHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, }, NewAuth: TPM2BAuth{ Buffer: newauth, }, } ocaRsp, err := oca.Execute(thetpm) if err != nil { t.Fatalf("failed objectchangeauthrequest: %v", err) } // Flush the old handle flushBlobCmd := FlushContext{FlushHandle: loadBlobRsp.ObjectHandle} if _, err := flushBlobCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } // Load the new private blob, and the old public blob loadBlobCmd := Load{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: PasswordAuth(nil), }, InPrivate: ocaRsp.OutPrivate, InPublic: createBlobRsp.OutPublic, } loadBlobRsp, err = loadBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Unseal the blob with a password session t.Run("WithOldPassword", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth(auth), } _, err := unsealCmd.Execute(thetpm) if !errors.Is(err, TPMRCBadAuth) { t.Errorf("want TPM_RC_BAD_AUTH, got %v", err) } var fmt1 TPMFmt1Error if !errors.As(err, &fmt1) { t.Errorf("want a Fmt1Error, got %v", err) } else if isSession, session := fmt1.Session(); !isSession || session != 1 { t.Errorf("want TPM_RC_BAD_AUTH on session 1, got %v", err) } }) t.Run("WithNewPassword", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth(newauth), } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } }) } go-tpm-0.9.3/tpm2/test/pcr_test.go000066400000000000000000000122001473040456300167420ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto/sha1" "crypto/sha256" "crypto/sha512" "fmt" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestPCRs(t *testing.T) { for i, tc := range []struct { pcrs []uint wantSelect []byte }{ { pcrs: nil, wantSelect: []byte{0x00, 0x00, 0x00}, }, { pcrs: []uint{0}, wantSelect: []byte{0x01, 0x00, 0x00}, }, { pcrs: []uint{0, 1, 2}, wantSelect: []byte{0x07, 0x00, 0x00}, }, { pcrs: []uint{0, 7}, wantSelect: []byte{0x81, 0x00, 0x00}, }, { pcrs: []uint{8}, wantSelect: []byte{0x00, 0x01, 0x00}, }, { pcrs: []uint{1, 8, 9}, wantSelect: []byte{0x02, 0x03, 0x00}, }, { pcrs: []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, wantSelect: []byte{0xff, 0xff, 0xff}, }, { pcrs: []uint{255}, wantSelect: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, }, } { t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { selection := PCClientCompatible.PCRs(tc.pcrs...) if !bytes.Equal(selection, tc.wantSelect) { t.Errorf("PCRs() = 0x%x, want 0x%x", selection, tc.wantSelect) } }) } } var extendstpm2 = map[TPMAlgID][]struct { digest []byte }{ TPMAlgSHA1: { {bytes.Repeat([]byte{0x00}, sha1.Size)}, {bytes.Repeat([]byte{0x01}, sha1.Size)}, {bytes.Repeat([]byte{0x02}, sha1.Size)}}, TPMAlgSHA256: { {bytes.Repeat([]byte{0x00}, sha256.Size)}, {bytes.Repeat([]byte{0x01}, sha256.Size)}, {bytes.Repeat([]byte{0x02}, sha256.Size)}}, TPMAlgSHA384: { {bytes.Repeat([]byte{0x00}, sha512.Size384)}, {bytes.Repeat([]byte{0x01}, sha512.Size384)}, {bytes.Repeat([]byte{0x02}, sha512.Size384)}}, } func allZero(s []byte) bool { for _, v := range s { if v != 0 { return false } } return true } func TestPCRReset(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() DebugPCR := uint(16) cases := []struct { name string hashalg TPMAlgID }{ {"SHA1", TPMAlgSHA1}, {"SHA256", TPMAlgSHA256}, {"SHA384", TPMAlgSHA384}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { authHandle := AuthHandle{ Handle: TPMHandle(DebugPCR), Auth: PasswordAuth(nil), } pcrRead := PCRRead{ PCRSelectionIn: TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: c.hashalg, PCRSelect: PCClientCompatible.PCRs(DebugPCR), }, }, }, } // Extending PCR 16 for _, d := range extendstpm2[c.hashalg] { pcrExtend := PCRExtend{ PCRHandle: authHandle, Digests: TPMLDigestValues{ Digests: []TPMTHA{ { HashAlg: c.hashalg, Digest: d.digest, }, }, }, } if _, err := pcrExtend.Execute(thetpm); err != nil { t.Fatalf("failed to extend pcr for test %v", err) } } pcrReadRsp, err := pcrRead.Execute(thetpm) if err != nil { t.Fatalf("failed to read PCRs") } postExtendPCR16 := pcrReadRsp.PCRValues.Digests[0].Buffer if allZero(postExtendPCR16) { t.Errorf("postExtendPCR16 not expected to be all Zero: %v", postExtendPCR16) } // Resetting PCR 16 pcrReset := PCRReset{ PCRHandle: authHandle, } if _, err := pcrReset.Execute(thetpm); err != nil { t.Fatalf("pcrReset failed: %v", err) } if pcrReadRsp, err = pcrRead.Execute(thetpm); err != nil { t.Fatalf("failed to read PCRs") } postResetPCR16 := pcrReadRsp.PCRValues.Digests[0].Buffer if !allZero(postResetPCR16) { t.Errorf("postResetPCR16 expected to be all Zero: %v", postExtendPCR16) } }) } } func TestPCREvent(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() cases := []struct { name string hashalg TPMAlgID }{ {"SHA1", TPMAlgSHA1}, {"SHA256", TPMAlgSHA256}, {"SHA384", TPMAlgSHA384}, } // Extend every SRTM PCR with TPM2_PCR_Event for _, c := range cases { t.Run(c.name, func(t *testing.T) { for i := 0; i < 17; i++ { t.Run(fmt.Sprintf("PCR%02d", i), func(t *testing.T) { pcrRead := PCRRead{ PCRSelectionIn: TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: c.hashalg, PCRSelect: PCClientCompatible.PCRs(20), }, }, }, } pcrEvent := PCREvent{ PCRHandle: TPMHandle(i), EventData: TPM2BEvent{Buffer: []byte("hello")}, } if _, err := pcrEvent.Execute(thetpm); err != nil { t.Fatalf("failed to extend pcr for test %v", err) } pcrReadRsp, err := pcrRead.Execute(thetpm) if err != nil { t.Fatalf("failed to read PCRs") } postExtendPCR16 := pcrReadRsp.PCRValues.Digests[0].Buffer if allZero(postExtendPCR16) { t.Errorf("postExtendPCR16 not expected to be all Zero: %v", postExtendPCR16) } }) } }) } } go-tpm-0.9.3/tpm2/test/policy_test.go000066400000000000000000000660521473040456300174730ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto/sha1" "crypto/sha256" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) // This test isn't interesting, but it checks that you can omit the handles on `StartAuthSession`. func TestCreatePolicySession(t *testing.T) { for _, tc := range []struct { name string typ TPMSE }{ { "trial", TPMSETrial, }, { "policy", TPMSEPolicy, }, } { t.Run(tc.name, func(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() sas, err := StartAuthSession{ SessionType: tc.typ, AuthHash: TPMAlgSHA256, NonceCaller: TPM2BNonce{ Buffer: make([]byte, 16), }, }.Execute(thetpm) if err != nil { t.Fatalf("StartAuthSession() = %v", err) } pgd, err := PolicyGetDigest{ PolicySession: sas.SessionHandle, }.Execute(thetpm) if err != nil { t.Fatalf("PolicyGetDigest() = %v", err) } if digest := pgd.PolicyDigest.Buffer; !bytes.Equal(digest, make([]byte, len(digest))) { t.Errorf("PolicyGetDigest() = %x, want all zeros", digest) } _, err = FlushContext{ FlushHandle: sas.SessionHandle, }.Execute(thetpm) if err != nil { t.Errorf("FlushContext() = %v", err) } }) } } func signingKey(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) { t.Helper() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Scheme: TPMTECCScheme{ Scheme: TPMAlgECDSA, Details: NewTPMUAsymScheme( TPMAlgECDSA, &TPMSSigSchemeECDSA{ HashAlg: TPMAlgSHA256, }, ), }, CurveID: TPMECCNistP256, }, ), }), } rsp, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not create key: %v", err) } cleanup := func() { t.Helper() flush := FlushContext{ FlushHandle: rsp.ObjectHandle, } if _, err := flush.Execute(thetpm); err != nil { t.Errorf("could not flush signing key: %v", err) } } return NamedHandle{ Handle: rsp.ObjectHandle, Name: rsp.Name, }, cleanup } func nvIndex(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) { t.Helper() defSpace := NVDefineSpace{ AuthHandle: TPMRHOwner, PublicInfo: New2B( TPMSNVPublic{ NVIndex: 0x01800001, NameAlg: TPMAlgSHA256, Attributes: TPMANV{ OwnerWrite: true, AuthRead: true, NT: TPMNTOrdinary, }, }), } if _, err := defSpace.Execute(thetpm); err != nil { t.Fatalf("could not create NV index: %v", err) } pub, err := defSpace.PublicInfo.Contents() if err != nil { t.Fatalf("%v", err) } readPub := NVReadPublic{ NVIndex: pub.NVIndex, } readRsp, err := readPub.Execute(thetpm) if err != nil { t.Fatalf("could not read NV index public info: %v", err) } cleanup := func() { t.Helper() undefine := NVUndefineSpace{ AuthHandle: TPMRHOwner, NVIndex: NamedHandle{ Handle: pub.NVIndex, Name: readRsp.NVName, }, } if _, err := undefine.Execute(thetpm); err != nil { t.Errorf("could not undefine NV index: %v", err) } } return NamedHandle{ Handle: pub.NVIndex, Name: readRsp.NVName, }, cleanup } func primaryRSASRK(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) { t.Helper() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(RSASRKTemplate), } rsp, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not create primary key: %v", err) } cleanup := func() { t.Helper() flush := FlushContext{ FlushHandle: rsp.ObjectHandle, } if _, err := flush.Execute(thetpm); err != nil { t.Errorf("could not flush primary key: %v", err) } } return NamedHandle{ Handle: rsp.ObjectHandle, Name: rsp.Name, }, cleanup } func primaryRSAEK(t *testing.T, thetpm transport.TPM) (NamedHandle, func()) { t.Helper() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHEndorsement, InPublic: New2B(RSAEKTemplate), } rsp, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not create primary key: %v", err) } cleanup := func() { t.Helper() flush := FlushContext{ FlushHandle: rsp.ObjectHandle, } if _, err := flush.Execute(thetpm); err != nil { t.Errorf("could not flush primary key: %v", err) } } return NamedHandle{ Handle: rsp.ObjectHandle, Name: rsp.Name, }, cleanup } func TestPolicySignedUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() sk, cleanup := signingKey(t, thetpm) defer cleanup() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policySigned := PolicySigned{ AuthObject: sk, PolicySession: sess.Handle(), PolicyRef: TPM2BNonce{Buffer: []byte{5, 6, 7, 8}}, Auth: TPMTSignature{ SigAlg: TPMAlgECDSA, Signature: NewTPMUSignature( TPMAlgECDSA, &TPMSSignatureECC{ Hash: TPMAlgSHA256, }, ), }, } if _, err := policySigned.Execute(thetpm); err != nil { t.Fatalf("executing PolicySigned: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policySigned.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policySigned.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicySecretUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() sk, cleanup := signingKey(t, thetpm) defer cleanup() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policySecret := PolicySecret{ AuthHandle: NamedHandle{ Handle: sk.Handle, Name: sk.Name, }, PolicySession: sess.Handle(), PolicyRef: TPM2BNonce{Buffer: []byte{5, 6, 7, 8}}, } if _, err := policySecret.Execute(thetpm); err != nil { t.Fatalf("executing PolicySecret: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policySecret.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policySecret.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyOrUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policyOr := PolicyOr{ PolicySession: sess.Handle(), PHashList: TPMLDigest{ Digests: []TPM2BDigest{ {Buffer: []byte{1, 2, 3}}, {Buffer: []byte{4, 5, 6}}, }, }, } if _, err := policyOr.Execute(thetpm); err != nil { t.Fatalf("executing PolicyOr: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyOr.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policyOr.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func getExpectedPCRDigest(t *testing.T, thetpm transport.TPM, selection TPMLPCRSelection, hashAlg TPMAlgID) []byte { t.Helper() pcrRead := PCRRead{ PCRSelectionIn: selection, } pcrReadRsp, err := pcrRead.Execute(thetpm) if err != nil { t.Fatalf("failed to read PCRs") } var expectedVal []byte for _, digest := range pcrReadRsp.PCRValues.Digests { expectedVal = append(expectedVal, digest.Buffer...) } cryptoHashAlg, err := hashAlg.Hash() if err != nil { t.Fatalf("failed to get crypto hash") } hash := cryptoHashAlg.New() hash.Write(expectedVal) return hash.Sum(nil) } func TestPolicyPCR(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() selection := TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: TPMAlgSHA1, PCRSelect: PCClientCompatible.PCRs(0, 1, 2, 3, 7), }, }, } expectedDigest := getExpectedPCRDigest(t, thetpm, selection, TPMAlgSHA1) wrongDigest := sha1.Sum(expectedDigest[:]) tests := []struct { name string authOption []AuthOption pcrDigest []byte callShouldSucceed bool }{ {"TrialCorrect", []AuthOption{Trial()}, expectedDigest, true}, {"TrialIncorrect", []AuthOption{Trial()}, wrongDigest[:], true}, {"TrialEmpty", []AuthOption{Trial()}, nil, true}, {"RealCorrect", nil, expectedDigest, true}, {"RealIncorrect", nil, wrongDigest[:], false}, {"RealEmpty", nil, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA1, 16, tt.authOption...) if err != nil { t.Fatalf("setting up policy session: %v", err) } policyPCR := PolicyPCR{ PolicySession: sess.Handle(), PcrDigest: TPM2BDigest{ Buffer: tt.pcrDigest, }, Pcrs: selection, } _, err = policyPCR.Execute(thetpm) if tt.callShouldSucceed { if err != nil { t.Fatalf("executing PolicyPCR: %v", err) } } else { if err == nil { t.Fatalf("expected PolicyPCR to return error, got nil") } return } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // If the pcrDigest is empty: see TPM 2.0 Part 3, 23.7. if tt.pcrDigest == nil { expectedDigest := getExpectedPCRDigest(t, thetpm, selection, TPMAlgSHA1) t.Logf("expectedDigest=%x", expectedDigest) // Create a populated policyPCR for the PolicyCalculator policyPCR.PcrDigest.Buffer = expectedDigest[:] } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA1) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyPCR.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policyPCR.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }) } } func TestPolicyCpHashUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policyCpHash := PolicyCPHash{ PolicySession: sess.Handle(), CPHashA: TPM2BDigest{Buffer: []byte{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}, } if _, err := policyCpHash.Execute(thetpm); err != nil { t.Fatalf("executing PolicyCpHash: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyCpHash.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policyCpHash.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyAuthorizeUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() sk, cleanup := signingKey(t, thetpm) defer cleanup() policyAuthorize := PolicyAuthorize{ PolicySession: sess.Handle(), PolicyRef: TPM2BDigest{Buffer: []byte{5, 6, 7, 8}}, KeySign: sk.Name, CheckTicket: TPMTTKVerified{ Tag: TPMSTVerified, Hierarchy: TPMRHEndorsement, }, } if _, err := policyAuthorize.Execute(thetpm); err != nil { t.Fatalf("executing PolicyAuthorize: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyAuthorize.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("policyAuthorize.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyNVWrittenUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policyNVWritten := PolicyNVWritten{ PolicySession: sess.Handle(), WrittenSet: true, } if _, err := policyNVWritten.Execute(thetpm); err != nil { t.Fatalf("executing PolicyNVWritten: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyNVWritten.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("PolicyNVWritten.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyNVUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() nv, cleanup := nvIndex(t, thetpm) defer cleanup() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policyNV := PolicyNV{ AuthHandle: NamedHandle{Handle: nv.Handle, Name: nv.Name}, PolicySession: sess.Handle(), NVIndex: nv, OperandB: TPM2BOperand{Buffer: []byte("operandB")}, Offset: 2, Operation: TPMEOSignedLE, } if _, err := policyNV.Execute(thetpm); err != nil { t.Fatalf("executing PolicyAuthorizeNV: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyNV.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("PolicyAuthorizeNV.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyAuthorizeNVUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() nv, cleanup := nvIndex(t, thetpm) defer cleanup() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() policyAuthorizeNV := PolicyAuthorizeNV{ AuthHandle: NamedHandle{Handle: nv.Handle, Name: nv.Name}, PolicySession: sess.Handle(), NVIndex: nv, } if _, err := policyAuthorizeNV.Execute(thetpm); err != nil { t.Fatalf("executing PolicyAuthorizeNV: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } policyAuthorizeNV.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("PolicyAuthorizeNV.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyCommandCodeUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Use a trial session to calculate this policy sess, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer func() { t.Helper() if err := cleanup2(); err != nil { t.Errorf("cleaning up policy session: %v", err) } }() pcc := PolicyCommandCode{ PolicySession: sess.Handle(), Code: TPMCCCreate, } if _, err := pcc.Execute(thetpm); err != nil { t.Fatalf("executing PolicyCommandCode: %v", err) } pgd := PolicyGetDigest{ PolicySession: sess.Handle(), } want, err := pgd.Execute(thetpm) if err != nil { t.Fatalf("executing PolicyGetDigest: %v", err) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } pcc.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, want.PolicyDigest.Buffer) { t.Errorf("PolicyCommandCode.Hash() = %x,\nwant %x", got.Digest, want.PolicyDigest.Buffer) } } func TestPolicyAuthValue(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() password := []byte("foo") wrongPassword := []byte("bar") tests := []struct { name string password []byte authOption []AuthOption callShouldSucceed bool }{ {"PasswordCorrect", password, []AuthOption{Auth(password)}, true}, {"PasswordIncorrect", wrongPassword, []AuthOption{Auth(password)}, false}, {"PasswordEmpty", nil, []AuthOption{Auth(password)}, false}, {"AuthOptionEmpty", password, []AuthOption{}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pk, pkcleanup := primaryRSASRK(t, thetpm) defer pkcleanup() // create a trial policy with PolicyAuthValue sess, cleanup1, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("setting up trial session: %v", err) } defer func() { t.Helper() if err := cleanup1(); err != nil { t.Errorf("cleaning up trial session: %v", err) } }() pav := PolicyAuthValue{ PolicySession: sess.Handle(), } _, err = pav.Execute(thetpm) if err != nil { t.Fatalf("error executing policyAuthValue: %v", err) } // verify the digest pgd, err := PolicyGetDigest{ PolicySession: sess.Handle(), }.Execute(thetpm) if err != nil { t.Fatalf("error executing PolicyGetDigest: %v", pgd) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } pav.Update(pol) got := pol.Hash() if !bytes.Equal(got.Digest, pgd.PolicyDigest.Buffer) { t.Errorf("PolicyAuthValue.Hash() = %x,\nwant %x", got.Digest, pgd.PolicyDigest.Buffer) } // now apply the policy to a new key rsaTemplate := TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, AuthPolicy: pgd.PolicyDigest, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), } k, err := CreateLoaded{ ParentHandle: AuthHandle{ Handle: pk.Handle, Name: pk.Name, Auth: PasswordAuth(nil), }, InPublic: New2BTemplate(&rsaTemplate), InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: tt.password, }, }, }, }.Execute(thetpm) if err != nil { t.Fatalf("error creating key %v", err) } defer func() { t.Helper() _, err := FlushContext{ FlushHandle: k.ObjectHandle, }.Execute(thetpm) if err != nil { t.Errorf("error cleaning up key: %v", err) } }() // create a real policy session and use the password through the authOption sess2, cleanup2, err := PolicySession(thetpm, TPMAlgSHA256, 16, tt.authOption...) if err != nil { t.Fatalf("setting up policy session: %v", err) } defer cleanup2() policyAuthValue2 := PolicyAuthValue{ PolicySession: sess2.Handle(), } _, err = policyAuthValue2.Execute(thetpm) if err != nil { t.Fatalf("executing policyAuthValue: %v", err) } // sign some data with the key using the session data := []byte("somedata") digest := sha256.Sum256(data) sign := Sign{ KeyHandle: AuthHandle{ Handle: k.ObjectHandle, Name: k.Name, Auth: sess2, }, Digest: TPM2BDigest{ Buffer: digest[:], }, InScheme: TPMTSigScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUSigScheme( TPMAlgRSASSA, &TPMSSchemeHash{ HashAlg: TPMAlgSHA256, }, ), }, Validation: TPMTTKHashCheck{ Tag: TPMSTHashCheck, }, } _, err = sign.Execute(thetpm) if tt.callShouldSucceed { if err != nil { t.Fatalf("expected no error for PolicyAuthValue but got: %v", err) } } else { if err == nil { t.Fatalf("expected error for PolicyAuthValue, got nil") } return } }) } } func TestPolicyDuplicationSelectUpdate(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() ek, ekcleanup := primaryRSAEK(t, thetpm) defer ekcleanup() pk, pkcleanup := primaryRSASRK(t, thetpm) defer pkcleanup() k, err := CreateLoaded{ ParentHandle: AuthHandle{ Handle: pk.Handle, Name: pk.Name, Auth: PasswordAuth(nil), }, InPublic: New2BTemplate(&TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: false, FixedParent: false, SensitiveDataOrigin: true, UserWithAuth: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }), }.Execute(thetpm) if err != nil { t.Fatalf("error creating key %v", err) } defer func() { t.Helper() _, err := FlushContext{ FlushHandle: k.ObjectHandle, }.Execute(thetpm) if err != nil { t.Errorf("error cleaning up key: %v", err) } }() tests := []struct { name string objectName TPM2BName includeObject bool }{ {"IncludeObjectFalse", TPM2BName{}, false}, {"IncludeObjectTrue", k.Name, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // create a trial policy with PolicyDuplicationSelect sess, cleanup1, err := PolicySession(thetpm, TPMAlgSHA256, 16, Trial()) if err != nil { t.Fatalf("error setting up trial session: %v", err) } defer func() { t.Helper() if err := cleanup1(); err != nil { t.Errorf("error cleaning up trial session: %v", err) } }() pds := PolicyDuplicationSelect{ PolicySession: sess.Handle(), NewParentName: ek.Name, ObjectName: tt.objectName, IncludeObject: tt.includeObject, } _, err = pds.Execute(thetpm) if err != nil { t.Fatalf("error executing PolicyDuplicationSelect: %v", err) } pdr, err := PolicyGetDigest{ PolicySession: sess.Handle(), }.Execute(thetpm) if err != nil { t.Fatalf("error executing PolicyGetDigest: %v", pdr) } // Use the policy helper to calculate the same policy pol, err := NewPolicyCalculator(TPMAlgSHA256) if err != nil { t.Fatalf("creating policy calculator: %v", err) } err = pds.Update(pol) if err != nil { t.Fatalf("error updating policy calculator: %v", err) } got := pol.Hash() if !bytes.Equal(got.Digest, pdr.PolicyDigest.Buffer) { t.Errorf("PolicyAuthValue.Hash() = %x,\nwant %x", got.Digest, pdr.PolicyDigest.Buffer) } }) } } go-tpm-0.9.3/tpm2/test/read_public_test.go000066400000000000000000000067641473040456300204510ustar00rootroot00000000000000package tpm2test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) // TestReadPublicKey compares the CreatePrimary response parameter outPublic with the output of ReadPublic outPublic. func TestReadPublicKey(t *testing.T) { // Open simulated TPM for testing. thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } // Defer the close of the simulated TPM to after use. // Without this, other programs/tests may not be able to get a handle to the TPM. defer thetpm.Close() // Fill in the CreatePrimary struct. // See definition in Part 3, Commands, section 24.1. // See tpm2/templates/go for more TPMTPublic examples. createPrimary := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgECC, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ Scheme: TPMTECCScheme{ Scheme: TPMAlgECDSA, Details: NewTPMUAsymScheme( TPMAlgECDSA, &TPMSSigSchemeECDSA{ HashAlg: TPMAlgSHA256, }, ), }, CurveID: TPMECCNistP256, }, ), }), } // Executing the command uses reflection to pack the bytes into a // TPM2_CreatePrimary command, returns a TPM2_CreatePrimary Response. // This response is also decoded so you are again working with structs // that can be found in Part 3, Commands, section 24.1. rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("CreatePrimary failed: %v", err) } // The TPM can only hold so much in nonvolatile memory, thus we must // flush the handle after we are done using it to prevent overloading. // Again we defer the flush to after we are done using the object. // It is generally good practice to defer the cleanup immediately // after loading an object or creating an Authorization Session. // See Part 1, Architecture, section 30.4 flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer flushContext.Execute(thetpm) // Fill in the ReadPublic struct. // See definition in Part 3, Commands, section 12.4. readPublic := ReadPublic{ ObjectHandle: rspCP.ObjectHandle, } // Executing the command uses reflection to pack the bytes into a // TPM2_ReadPublic command, returns a TPM2_ReadPublic Response. // This response is also decoded so you are again working with structs // that can be found in Part 3, Commands, section 12.4. rspRP, err := readPublic.Execute(thetpm) if err != nil { t.Fatalf("ReadPublic failed: %v", err) } // PublicArea.Unique represents the unique identifier of the TPMTPublic. // Notice how this test uses verification of another TPM command that is // able to produce similar results to validate the response. pubCreate, err := rspCP.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } pubRead, err := rspRP.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } eccCreate, err := pubCreate.Unique.ECC() if err != nil { t.Fatalf("%v", err) } eccRead, err := pubRead.Unique.ECC() if err != nil { t.Fatalf("%v", err) } if !cmp.Equal(eccCreate.X, eccRead.X, cmpopts.IgnoreUnexported(eccCreate.X)) { t.Error("Mismatch between public returned from CreatePrimary & ReadPublic") } } go-tpm-0.9.3/tpm2/test/rsa_encryption_test.go000066400000000000000000000061111473040456300212210ustar00rootroot00000000000000package tpm2test import ( "bytes" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestRSAEncryption(t *testing.T) { theTpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } t.Cleanup(func() { if err := theTpm.Close(); err != nil { t.Errorf("%v", err) } }) createPrimaryCmd := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(RSASRKTemplate), } createPrimaryRsp, err := createPrimaryCmd.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } t.Cleanup(func() { flushContextCmd := FlushContext{FlushHandle: createPrimaryRsp.ObjectHandle} if _, err := flushContextCmd.Execute(theTpm); err != nil { t.Errorf("%v", err) } }) createCmd := Create{ ParentHandle: NamedHandle{ Handle: createPrimaryRsp.ObjectHandle, Name: createPrimaryRsp.Name, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, STClear: false, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, AdminWithPolicy: false, NoDA: true, EncryptedDuplication: false, Restricted: false, Decrypt: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ KeyBits: 2048, }, ), Unique: NewTPMUPublicID( TPMAlgRSA, &TPM2BPublicKeyRSA{ Buffer: make([]byte, 256), }, ), }), } createRsp, err := createCmd.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } loadCmd := Load{ ParentHandle: NamedHandle{ Handle: createPrimaryRsp.ObjectHandle, Name: createPrimaryRsp.Name, }, InPrivate: createRsp.OutPrivate, InPublic: createRsp.OutPublic, } loadRsp, err := loadCmd.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } t.Cleanup(func() { flushContextCmd := FlushContext{FlushHandle: loadRsp.ObjectHandle} if _, err := flushContextCmd.Execute(theTpm); err != nil { t.Errorf("%v", err) } }) message := []byte("secret") encryptCmd := RSAEncrypt{ KeyHandle: loadRsp.ObjectHandle, Message: TPM2BPublicKeyRSA{Buffer: message}, InScheme: TPMTRSADecrypt{ Scheme: TPMAlgOAEP, Details: NewTPMUAsymScheme( TPMAlgOAEP, &TPMSEncSchemeOAEP{ HashAlg: TPMAlgSHA256, }, ), }, } encryptRsp, err := encryptCmd.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } decryptCmd := RSADecrypt{ KeyHandle: NamedHandle{ Handle: loadRsp.ObjectHandle, Name: loadRsp.Name, }, CipherText: TPM2BPublicKeyRSA{Buffer: encryptRsp.OutData.Buffer}, InScheme: TPMTRSADecrypt{ Scheme: TPMAlgOAEP, Details: NewTPMUAsymScheme( TPMAlgOAEP, &TPMSEncSchemeOAEP{ HashAlg: TPMAlgSHA256, }, ), }, } decryptRsp, err := decryptCmd.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } if !bytes.Equal(message, decryptRsp.Message.Buffer) { t.Errorf("want %x got %x", message, decryptRsp.Message.Buffer) } } go-tpm-0.9.3/tpm2/test/sealing_test.go000066400000000000000000000305431473040456300176120ustar00rootroot00000000000000package tpm2test import ( "bytes" "errors" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) // Test creating and unsealing a sealed data blob with a password and HMAC. func TestUnseal(t *testing.T) { templates := map[string]TPMTPublic{ "RSA": RSASRKTemplate, "ECC": ECCSRKTemplate, } // Run the whole test for each of RSA and ECC SRKs. for name, srkTemplate := range templates { t.Run(name, func(t *testing.T) { unsealingTest(t, srkTemplate) }) } } func unsealingTest(t *testing.T, srkTemplate TPMTPublic) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() // Create the SRK // Put a password on the SRK to test more of the flows. srkAuth := []byte("mySRK") createSRKCmd := CreatePrimary{ PrimaryHandle: TPMRHOwner, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: srkAuth, }, }, }, InPublic: New2B(srkTemplate), } createSRKRsp, err := createSRKCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } t.Logf("SRK name: %x", createSRKRsp.Name) defer func() { // Flush the SRK flushSRKCmd := FlushContext{FlushHandle: createSRKRsp.ObjectHandle} if _, err := flushSRKCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() // Create a sealed blob under the SRK data := []byte("secrets") // Include some trailing zeros to exercise the TPM's trimming of them from auth values. auth := []byte("p@ssw0rd\x00\x00") auth2 := []byte("p@ssw0rd") createBlobCmd := Create{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: PasswordAuth(srkAuth), }, InSensitive: TPM2BSensitiveCreate{ Sensitive: &TPMSSensitiveCreate{ UserAuth: TPM2BAuth{ Buffer: auth, }, Data: NewTPMUSensitiveCreate(&TPM2BSensitiveData{ Buffer: data, }), }, }, InPublic: New2B(TPMTPublic{ Type: TPMAlgKeyedHash, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, NoDA: true, }, }), } var createBlobRsp *CreateResponse // Create the blob with password auth, without any session encryption t.Run("Create", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob using an hmac auth session also for audit t.Run("CreateAudit", func(t *testing.T) { createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AuditExclusive()), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob, using the auth session also for decryption t.Run("CreateDecrypt", func(t *testing.T) { createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptIn)), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob, using the auth session also for encryption t.Run("CreateEncrypt", func(t *testing.T) { createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptOut)), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob, using the auth session also for decrypt and encrypt t.Run("CreateDecryptEncrypt", func(t *testing.T) { createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut)), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with decrypt and encrypt session t.Run("CreateDecryptEncryptAudit", func(t *testing.T) { createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), Audit()), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with decrypt and encrypt session bound to SRK t.Run("CreateDecryptEncryptSalted", func(t *testing.T) { outPub, err := createSRKRsp.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth), AESEncryption(128, EncryptInOut), Salted(createSRKRsp.ObjectHandle, *outPub)), } createBlobRsp, err = createBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } }) // Use HMAC auth to authorize the rest of the Create commands // Exercise re-using a use-once HMAC structure (which will spin up the session each time) createBlobCmd.ParentHandle = AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth)), } // Create the blob with a separate decrypt and encrypt session t.Run("CreateDecryptEncryptSeparate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut))) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with a separate decrypt and encrypt session, and another for audit t.Run("CreateDecryptEncryptAuditSeparate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), HMAC(TPMAlgSHA256, 16, Audit())) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with a separate decrypt and encrypt session, and another for exclusive audit t.Run("CreateDecryptEncryptAuditExclusiveSeparate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, HMAC(TPMAlgSHA256, 16, AESEncryption(128, EncryptInOut)), HMAC(TPMAlgSHA256, 16, AuditExclusive())) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with separate decrypt and encrypt sessions. t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, // Get weird with the algorithm and nonce choices. Mix lots of things together. HMAC(TPMAlgSHA1, 20, AESEncryption(128, EncryptIn)), HMAC(TPMAlgSHA384, 23, AESEncryption(128, EncryptOut))) if err != nil { t.Fatalf("%v", err) } }) // Create the blob with separate encrypt and decrypt sessions. // (The TPM spec orders some extra nonces included in the first session in the order // nonceTPM_decrypt, nonceTPM_encrypt, so this exercises that) t.Run("CreateDecryptEncrypt2Separate", func(t *testing.T) { createBlobRsp, err = createBlobCmd.Execute(thetpm, HMAC(TPMAlgSHA1, 17, AESEncryption(128, EncryptOut)), HMAC(TPMAlgSHA256, 32, AESEncryption(128, EncryptIn))) if err != nil { t.Fatalf("%v", err) } }) // Load the sealed blob loadBlobCmd := Load{ ParentHandle: AuthHandle{ Handle: createSRKRsp.ObjectHandle, Name: createSRKRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(srkAuth)), }, InPrivate: createBlobRsp.OutPrivate, InPublic: createBlobRsp.OutPublic, } loadBlobRsp, err := loadBlobCmd.Execute(thetpm) if err != nil { t.Fatalf("%v", err) } defer func() { // Flush the blob flushBlobCmd := FlushContext{FlushHandle: loadBlobRsp.ObjectHandle} if _, err := flushBlobCmd.Execute(thetpm); err != nil { t.Errorf("%v", err) } }() unsealCmd := Unseal{ ItemHandle: NamedHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, }, } // Unseal the blob with a password session t.Run("WithPassword", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth(auth), } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } }) // Unseal the blob with an incorrect password session t.Run("WithWrongPassword", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: PasswordAuth([]byte("NotThePassword")), } _, err := unsealCmd.Execute(thetpm) if !errors.Is(err, TPMRCBadAuth) { t.Errorf("want TPM_RC_BAD_AUTH, got %v", err) } var fmt1 TPMFmt1Error if !errors.As(err, &fmt1) { t.Errorf("want a Fmt1Error, got %v", err) } else if isSession, session := fmt1.Session(); !isSession || session != 1 { t.Errorf("want TPM_RC_BAD_AUTH on session 1, got %v", err) } }) // Unseal the blob with a use-once HMAC session t.Run("WithHMAC", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(auth2)), } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } }) // Unseal the blob with a use-once HMAC session with encryption t.Run("WithHMACEncrypt", func(t *testing.T) { unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: HMAC(TPMAlgSHA256, 16, Auth(auth2), AESEncryption(128, EncryptOut)), } unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } }) // Unseal the blob with a standalone HMAC session, re-using the session. t.Run("WithHMACSession", func(t *testing.T) { sess, cleanup, err := HMACSession(thetpm, TPMAlgSHA1, 20, Auth(auth2)) if err != nil { t.Fatalf("%v", err) } defer cleanup() unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: sess, } // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } } }) // Unseal the blob with a standalone bound HMAC session, re-using the session. // Also, use session encryption. t.Run("WithHMACSessionEncrypt", func(t *testing.T) { sess, cleanup, err := HMACSession(thetpm, TPMAlgSHA256, 16, Auth(auth2), AESEncryption(128, EncryptOut), Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) if err != nil { t.Fatalf("%v", err) } defer cleanup() unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: sess, } // It should be possible to use the session multiple times. for i := 0; i < 3; i++ { unsealRsp, err := unsealCmd.Execute(thetpm) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } } }) // Unseal the blob with a standalone HMAC session, re-using the session. // Spin up another bound session for encryption. t.Run("WithHMACSessionEncryptSeparate", func(t *testing.T) { sess1, cleanup1, err := HMACSession(thetpm, TPMAlgSHA1, 16, Auth(auth2)) if err != nil { t.Fatalf("%v", err) } defer cleanup1() sess2, cleanup2, err := HMACSession(thetpm, TPMAlgSHA384, 16, AESEncryption(128, EncryptOut), Bound(createSRKRsp.ObjectHandle, createSRKRsp.Name, srkAuth)) if err != nil { t.Fatalf("%v", err) } defer cleanup2() unsealCmd.ItemHandle = AuthHandle{ Handle: loadBlobRsp.ObjectHandle, Name: loadBlobRsp.Name, Auth: sess1, } // It should be possible to use the sessions multiple times. for i := 0; i < 3; i++ { unsealRsp, err := unsealCmd.Execute(thetpm, sess2) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(unsealRsp.OutData.Buffer, data) { t.Errorf("want %x got %x", data, unsealRsp.OutData.Buffer) } } }) } go-tpm-0.9.3/tpm2/test/sign_test.go000066400000000000000000000047341473040456300171330ustar00rootroot00000000000000package tpm2test import ( "crypto" "crypto/rsa" "crypto/sha256" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestSign(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() createPrimary := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgRSA, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ SignEncrypt: true, FixedTPM: true, FixedParent: true, SensitiveDataOrigin: true, UserWithAuth: true, }, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ Scheme: TPMTRSAScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUAsymScheme( TPMAlgRSASSA, &TPMSSigSchemeRSASSA{ HashAlg: TPMAlgSHA256, }, ), }, KeyBits: 2048, }, ), }), CreationPCR: TPMLPCRSelection{ PCRSelections: []TPMSPCRSelection{ { Hash: TPMAlgSHA1, PCRSelect: PCClientCompatible.PCRs(7), }, }, }, } rspCP, err := createPrimary.Execute(thetpm) if err != nil { t.Fatalf("could not create key: %v", err) } flushContext := FlushContext{FlushHandle: rspCP.ObjectHandle} defer flushContext.Execute(thetpm) digest := sha256.Sum256([]byte("migrationpains")) sign := Sign{ KeyHandle: NamedHandle{ Handle: rspCP.ObjectHandle, Name: rspCP.Name, }, Digest: TPM2BDigest{ Buffer: digest[:], }, InScheme: TPMTSigScheme{ Scheme: TPMAlgRSASSA, Details: NewTPMUSigScheme( TPMAlgRSASSA, &TPMSSchemeHash{ HashAlg: TPMAlgSHA256, }, ), }, Validation: TPMTTKHashCheck{ Tag: TPMSTHashCheck, }, } rspSign, err := sign.Execute(thetpm) if err != nil { t.Fatalf("Failed to Sign Digest: %v", err) } pub, err := rspCP.OutPublic.Contents() if err != nil { t.Fatalf("%v", err) } rsaDetail, err := pub.Parameters.RSADetail() if err != nil { t.Fatalf("%v", err) } rsaUnique, err := pub.Unique.RSA() if err != nil { t.Fatalf("%v", err) } rsaPub, err := RSAPub(rsaDetail, rsaUnique) if err != nil { t.Fatalf("Failed to retrieve Public Key: %v", err) } rsassa, err := rspSign.Signature.Signature.RSASSA() if err != nil { t.Fatalf("%v", err) } if err := rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], rsassa.Sig.Buffer); err != nil { t.Errorf("Signature verification failed: %v", err) } } go-tpm-0.9.3/tpm2/test/symmetric_encryption_test.go000066400000000000000000000114671473040456300224620ustar00rootroot00000000000000package tpm2test import ( "bytes" "crypto/aes" "crypto/rand" "io" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/simulator" ) const maxDigestBuffer = 1024 func TestAESEncryption(t *testing.T) { theTpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } t.Cleanup(func() { if err := theTpm.Close(); err != nil { t.Errorf("%v", err) } }) primary, err := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgSymCipher, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, SensitiveDataOrigin: true, Decrypt: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgSymCipher, &TPMSSymCipherParms{ Sym: TPMTSymDefObject{ Algorithm: TPMAlgAES, Mode: NewTPMUSymMode(TPMAlgAES, TPMAlgCFB), KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), }, }, ), }), }.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } t.Cleanup(func() { flushContextCmd := FlushContext{FlushHandle: primary.ObjectHandle} if _, err := flushContextCmd.Execute(theTpm); err != nil { t.Errorf("%v", err) } }) message := []byte("secret") iv := make([]byte, aes.BlockSize) _, err = io.ReadFull(rand.Reader, iv) if err != nil { t.Errorf("%v", err) } keyAuth := AuthHandle{ Handle: primary.ObjectHandle, Name: primary.Name, Auth: PasswordAuth(nil), } // test encryption encryptRsp, err := EncryptDecrypt2{ KeyHandle: keyAuth, Message: TPM2BMaxBuffer{ Buffer: message, }, Mode: TPMAlgCFB, Decrypt: false, IV: TPM2BIV{ Buffer: iv, }, }.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } // test decryption decryptRsp, err := EncryptDecrypt2{ KeyHandle: keyAuth, Message: TPM2BMaxBuffer{ Buffer: encryptRsp.OutData.Buffer, }, Mode: TPMAlgCFB, Decrypt: true, IV: TPM2BIV{ Buffer: iv, }, }.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } if !bytes.Equal(message, decryptRsp.OutData.Buffer) { t.Errorf("want %x got %x", message, decryptRsp.OutData.Buffer) } } func TestAESEncryptionBlock(t *testing.T) { theTpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } t.Cleanup(func() { if err := theTpm.Close(); err != nil { t.Errorf("%v", err) } }) primary, err := CreatePrimary{ PrimaryHandle: TPMRHOwner, InPublic: New2B(TPMTPublic{ Type: TPMAlgSymCipher, NameAlg: TPMAlgSHA256, ObjectAttributes: TPMAObject{ FixedTPM: true, FixedParent: true, UserWithAuth: true, SensitiveDataOrigin: true, Decrypt: true, SignEncrypt: true, }, Parameters: NewTPMUPublicParms( TPMAlgSymCipher, &TPMSSymCipherParms{ Sym: TPMTSymDefObject{ Algorithm: TPMAlgAES, Mode: NewTPMUSymMode(TPMAlgAES, TPMAlgCFB), KeyBits: NewTPMUSymKeyBits( TPMAlgAES, TPMKeyBits(128), ), }, }, ), }), }.Execute(theTpm) if err != nil { t.Fatalf("%v", err) } t.Cleanup(func() { flushContextCmd := FlushContext{FlushHandle: primary.ObjectHandle} if _, err := flushContextCmd.Execute(theTpm); err != nil { t.Errorf("%v", err) } }) message := make([]byte, 2048) _, err = rand.Read(message) if err != nil { t.Errorf("%v", err) } iv := make([]byte, aes.BlockSize) _, err = io.ReadFull(rand.Reader, iv) if err != nil { t.Errorf("%v", err) } keyAuth := AuthHandle{ Handle: primary.ObjectHandle, Name: primary.Name, Auth: PasswordAuth(nil), } // test encryption encrypted, err := encryptDecryptSymmetric(theTpm, keyAuth, iv, message, TPMAlgCFB, false) if err != nil { t.Errorf("%v", err) } decrypted, err := encryptDecryptSymmetric(theTpm, keyAuth, iv, encrypted, TPMAlgCFB, true) if err != nil { t.Errorf("%v", err) } if !bytes.Equal(message, decrypted) { t.Errorf("want %x got %x", message, decrypted) } } func encryptDecryptSymmetric(rwr transport.TPM, keyAuth AuthHandle, iv, data []byte, mode TPMAlgID, decrypt bool) ([]byte, error) { var out, block []byte for rest := data; len(rest) > 0; { if len(rest) > maxDigestBuffer { block, rest = rest[:maxDigestBuffer], rest[maxDigestBuffer:] } else { block, rest = rest, nil } r, err := EncryptDecrypt2{ KeyHandle: keyAuth, Message: TPM2BMaxBuffer{ Buffer: block, }, Mode: mode, Decrypt: decrypt, IV: TPM2BIV{ Buffer: iv, }, }.Execute(rwr) if err != nil { return nil, err } block = r.OutData.Buffer iv = r.IV.Buffer out = append(out, block...) } return out, nil } go-tpm-0.9.3/tpm2/test/test_parms_test.go000066400000000000000000000034621473040456300203510ustar00rootroot00000000000000package tpm2test import ( "errors" "testing" . "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport/simulator" ) func TestTestParms(t *testing.T) { thetpm, err := simulator.OpenSimulator() if err != nil { t.Fatalf("could not connect to TPM simulator: %v", err) } defer thetpm.Close() for _, tt := range []struct { name string parms TPMTPublicParms wantErr error }{ { "p256", TPMTPublicParms{ Type: TPMAlgECC, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP256, }, ), }, nil, }, { "p364", TPMTPublicParms{ Type: TPMAlgECC, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP384, }, ), }, nil, }, { "p521", TPMTPublicParms{ Type: TPMAlgECC, Parameters: NewTPMUPublicParms( TPMAlgECC, &TPMSECCParms{ CurveID: TPMECCNistP521, }, ), }, nil, }, { "rsa2048", TPMTPublicParms{ Type: TPMAlgRSA, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ KeyBits: 2048, }, ), }, nil, }, { "rsa3072 - unsupported", TPMTPublicParms{ Type: TPMAlgRSA, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ KeyBits: 3072, }, ), }, TPMRCValue, }, { "rsa4096 - unsupported", TPMTPublicParms{ Type: TPMAlgRSA, Parameters: NewTPMUPublicParms( TPMAlgRSA, &TPMSRSAParms{ KeyBits: 4096, }, ), }, TPMRCValue, }, } { t.Run(tt.name, func(t *testing.T) { grc := TestParms{Parameters: tt.parms} _, err := grc.Execute(thetpm) if !errors.Is(err, tt.wantErr) { t.Fatalf("TestParms failed failed. Expecting err %v got %v", tt.wantErr, err) } }) } } go-tpm-0.9.3/tpm2/tpm2.go000066400000000000000000002075561473040456300150460ustar00rootroot00000000000000// Package tpm2 contains TPM 2.0 commands and structures. package tpm2 import ( "bytes" "encoding/binary" "github.com/google/go-tpm/tpm2/transport" ) // handle represents a TPM handle as comprehended in Part 3: Commands. // In the context of TPM commands, handles are special parameters for which // there is a known associated name. // This is not an exported interface, because the reflection logic has special // behavior for AuthHandle, due to the fact that referencing Session from this // interface would break the ability to make TPMHandle implement it. type handle interface { // HandleValue is the numeric concrete handle value in the TPM. HandleValue() uint32 // KnownName is the TPM Name of the associated entity. See Part 1, section 16. KnownName() *TPM2BName } // NamedHandle represents an associated pairing of TPM handle and known Name. type NamedHandle struct { Handle TPMHandle Name TPM2BName } // HandleValue implements the handle interface. func (h NamedHandle) HandleValue() uint32 { return h.Handle.HandleValue() } // KnownName implements the handle interface. func (h NamedHandle) KnownName() *TPM2BName { return &h.Name } // AuthHandle allows the caller to add an authorization session onto a handle. type AuthHandle struct { Handle TPMHandle Name TPM2BName Auth Session } // HandleValue implements the handle interface. func (h AuthHandle) HandleValue() uint32 { return h.Handle.HandleValue() } // KnownName implements the handle interface. // If Name is not provided (i.e., only Auth), then rely on the underlying // TPMHandle. func (h AuthHandle) KnownName() *TPM2BName { if len(h.Name.Buffer) != 0 { return &h.Name } return h.Handle.KnownName() } // Command is an interface for any TPM command, parameterized by its response // type. type Command[R any, PR *R] interface { // The TPM command code associated with this command. Command() TPMCC // Executes the command and returns the response. Execute(t transport.TPM, s ...Session) (PR, error) } // PolicyCommand is a TPM command that can be part of a TPM policy. type PolicyCommand interface { // Update updates the given policy hash according to the command // parameters. Update(policy *PolicyCalculator) error } // Shutdown is the input to TPM2_Shutdown. // See definition in Part 3, Commands, section 9.4. type Shutdown struct { // TPM_SU_CLEAR or TPM_SU_STATE ShutdownType TPMSU } // Command implements the Command interface. func (Shutdown) Command() TPMCC { return TPMCCShutdown } // Execute executes the command and returns the response. func (cmd Shutdown) Execute(t transport.TPM, s ...Session) (*ShutdownResponse, error) { var rsp ShutdownResponse err := execute[ShutdownResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // ShutdownResponse is the response from TPM2_Shutdown. type ShutdownResponse struct{} // Startup is the input to TPM2_Startup. // See definition in Part 3, Commands, section 9.3. type Startup struct { // TPM_SU_CLEAR or TPM_SU_STATE StartupType TPMSU } // Command implements the Command interface. func (Startup) Command() TPMCC { return TPMCCStartup } // Execute executes the command and returns the response. func (cmd Startup) Execute(t transport.TPM, s ...Session) (*StartupResponse, error) { var rsp StartupResponse err := execute[StartupResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // StartupResponse is the response from TPM2_Startup. type StartupResponse struct{} // StartAuthSession is the input to TPM2_StartAuthSession. // See definition in Part 3, Commands, section 11.1 type StartAuthSession struct { // handle of a loaded decrypt key used to encrypt salt // may be TPM_RH_NULL TPMKey handle `gotpm:"handle"` // entity providing the authValue // may be TPM_RH_NULL Bind handle `gotpm:"handle"` // initial nonceCaller, sets nonceTPM size for the session // shall be at least 16 octets NonceCaller TPM2BNonce // value encrypted according to the type of tpmKey // If tpmKey is TPM_RH_NULL, this shall be the Empty Buffer. EncryptedSalt TPM2BEncryptedSecret // indicates the type of the session; simple HMAC or policy (including // a trial policy) SessionType TPMSE // the algorithm and key size for parameter encryption // may select transport.TPM_ALG_NULL Symmetric TPMTSymDef // hash algorithm to use for the session // Shall be a hash algorithm supported by the TPM and not transport.TPM_ALG_NULL AuthHash TPMIAlgHash } // Command implements the Command interface. func (StartAuthSession) Command() TPMCC { return TPMCCStartAuthSession } // Execute executes the command and returns the response. func (cmd StartAuthSession) Execute(t transport.TPM, s ...Session) (*StartAuthSessionResponse, error) { var rsp StartAuthSessionResponse if err := execute[StartAuthSessionResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // StartAuthSessionResponse is the response from TPM2_StartAuthSession. type StartAuthSessionResponse struct { // handle for the newly created session SessionHandle TPMISHAuthSession `gotpm:"handle"` // the initial nonce from the TPM, used in the computation of the sessionKey NonceTPM TPM2BNonce } // Create is the input to TPM2_Create. // See definition in Part 3, Commands, section 12.1 type Create struct { // handle of parent for new object ParentHandle handle `gotpm:"handle,auth"` // the sensitive data InSensitive TPM2BSensitiveCreate // the public template InPublic TPM2BPublic // data that will be included in the creation data for this // object to provide permanent, verifiable linkage between this // object and some object owner data OutsideInfo TPM2BData // PCR that will be used in creation data CreationPCR TPMLPCRSelection } // Command implements the Command interface. func (Create) Command() TPMCC { return TPMCCCreate } // Execute executes the command and returns the response. func (cmd Create) Execute(t transport.TPM, s ...Session) (*CreateResponse, error) { var rsp CreateResponse if err := execute[CreateResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CreateResponse is the response from TPM2_Create. type CreateResponse struct { // the private portion of the object OutPrivate TPM2BPrivate // the public portion of the created object OutPublic TPM2BPublic // contains a TPMS_CREATION_DATA CreationData tpm2bCreationData // digest of creationData using nameAlg of outPublic CreationHash TPM2BDigest // ticket used by TPM2_CertifyCreation() to validate that the // creation data was produced by the TPM. CreationTicket TPMTTKCreation } // Load is the input to TPM2_Load. // See definition in Part 3, Commands, section 12.2 type Load struct { // handle of parent for new object ParentHandle handle `gotpm:"handle,auth"` // the private portion of the object InPrivate TPM2BPrivate // the public portion of the object InPublic TPM2BPublic } // Command implements the Command interface. func (Load) Command() TPMCC { return TPMCCLoad } // Execute executes the command and returns the response. func (cmd Load) Execute(t transport.TPM, s ...Session) (*LoadResponse, error) { var rsp LoadResponse if err := execute[LoadResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // LoadResponse is the response from TPM2_Load. type LoadResponse struct { // handle of type TPM_HT_TRANSIENT for loaded object ObjectHandle TPMHandle `gotpm:"handle"` // Name of the loaded object Name TPM2BName } // LoadExternal is the input to TPM2_LoadExternal. // See definition in Part 3, Commands, section 12.3 type LoadExternal struct { // the sensitive portion of the object (optional) InPrivate TPM2BSensitive `gotpm:"optional"` // the public portion of the object InPublic TPM2BPublic // hierarchy with which the object area is associated Hierarchy TPMIRHHierarchy `gotpm:"nullable"` } // Command implements the Command interface. func (LoadExternal) Command() TPMCC { return TPMCCLoadExternal } // Execute executes the command and returns the response. func (cmd LoadExternal) Execute(t transport.TPM, s ...Session) (*LoadExternalResponse, error) { var rsp LoadExternalResponse if err := execute[LoadExternalResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // LoadExternalResponse is the response from TPM2_LoadExternal. type LoadExternalResponse struct { // handle of type TPM_HT_TRANSIENT for loaded object ObjectHandle TPMHandle `gotpm:"handle"` // Name of the loaded object Name TPM2BName } // ReadPublic is the input to TPM2_ReadPublic. // See definition in Part 3, Commands, section 12.4 type ReadPublic struct { // TPM handle of an object ObjectHandle TPMIDHObject `gotpm:"handle"` } // Command implements the Command interface. func (ReadPublic) Command() TPMCC { return TPMCCReadPublic } // Execute executes the command and returns the response. func (cmd ReadPublic) Execute(t transport.TPM, s ...Session) (*ReadPublicResponse, error) { var rsp ReadPublicResponse if err := execute[ReadPublicResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ReadPublicResponse is the response from TPM2_ReadPublic. type ReadPublicResponse struct { // structure containing the public area of an object OutPublic TPM2BPublic // name of object Name TPM2BName // the Qualified Name of the object QualifiedName TPM2BName } // ActivateCredential is the input to TPM2_ActivateCredential. // See definition in Part 3, Commands, section 12.5. type ActivateCredential struct { // handle of the object associated with certificate in credentialBlob ActivateHandle handle `gotpm:"handle,auth"` // loaded key used to decrypt the TPMS_SENSITIVE in credentialBlob KeyHandle handle `gotpm:"handle,auth"` // the credential CredentialBlob TPM2BIDObject // keyHandle algorithm-dependent encrypted seed that protects credentialBlob Secret TPM2BEncryptedSecret } // Command implements the Command interface. func (ActivateCredential) Command() TPMCC { return TPMCCActivateCredential } // Execute executes the command and returns the response. func (cmd ActivateCredential) Execute(t transport.TPM, s ...Session) (*ActivateCredentialResponse, error) { var rsp ActivateCredentialResponse if err := execute[ActivateCredentialResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ActivateCredentialResponse is the response from TPM2_ActivateCredential. type ActivateCredentialResponse struct { // the decrypted certificate information CertInfo TPM2BDigest } // MakeCredential is the input to TPM2_MakeCredential. // See definition in Part 3, Commands, section 12.6. type MakeCredential struct { // loaded public area, used to encrypt the sensitive area containing the credential key Handle TPMIDHObject `gotpm:"handle"` // the credential information Credential TPM2BDigest // Name of the object to which the credential applies ObjectNamae TPM2BName } // Command implements the Command interface. func (MakeCredential) Command() TPMCC { return TPMCCMakeCredential } // Execute executes the command and returns the response. func (cmd MakeCredential) Execute(t transport.TPM, s ...Session) (*MakeCredentialResponse, error) { var rsp MakeCredentialResponse if err := execute[MakeCredentialResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // MakeCredentialResponse is the response from TPM2_MakeCredential. type MakeCredentialResponse struct { // the credential CredentialBlob TPM2BIDObject // handle algorithm-dependent data that wraps the key that encrypts credentialBlob Secret TPM2BEncryptedSecret } // Unseal is the input to TPM2_Unseal. // See definition in Part 3, Commands, section 12.7 type Unseal struct { ItemHandle handle `gotpm:"handle,auth"` } // Command implements the Command interface. func (Unseal) Command() TPMCC { return TPMCCUnseal } // Execute executes the command and returns the response. func (cmd Unseal) Execute(t transport.TPM, s ...Session) (*UnsealResponse, error) { var rsp UnsealResponse if err := execute[UnsealResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // UnsealResponse is the response from TPM2_Unseal. type UnsealResponse struct { OutData TPM2BSensitiveData } // ObjectChangeAuth is the input to TPM2_ObjectChangeAuth. // See definition in Part 3, Commands, section 12.8 type ObjectChangeAuth struct { // TPM handle of an object ObjectHandle handle `gotpm:"handle,auth"` // handle of the parent ParentHandle handle `gotpm:"handle"` // new authorization value NewAuth TPM2BAuth } // Command implements the Command interface. func (ObjectChangeAuth) Command() TPMCC { return TPMCCObjectChangeAuth } // Execute executes the command and returns the response. func (cmd ObjectChangeAuth) Execute(t transport.TPM, s ...Session) (*ObjectChangeAuthResponse, error) { var rsp ObjectChangeAuthResponse if err := execute[ObjectChangeAuthResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ObjectChangeAuthResponse the response from TPM2_ObjectChangeAuth. type ObjectChangeAuthResponse struct { // private area containing the new authorization value OutPrivate TPM2BPrivate } // CreateLoaded is the input to TPM2_CreateLoaded. // See definition in Part 3, Commands, section 12.9 type CreateLoaded struct { // Handle of a transient storage key, a persistent storage key, // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, or TPM_RH_NULL ParentHandle handle `gotpm:"handle,auth"` // the sensitive data, see TPM 2.0 Part 1 Sensitive Values InSensitive TPM2BSensitiveCreate // the public template InPublic TPM2BTemplate } // Command implements the Command interface. func (CreateLoaded) Command() TPMCC { return TPMCCCreateLoaded } // Execute executes the command and returns the response. func (cmd CreateLoaded) Execute(t transport.TPM, s ...Session) (*CreateLoadedResponse, error) { var rsp CreateLoadedResponse if err := execute[CreateLoadedResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CreateLoadedResponse is the response from TPM2_CreateLoaded. type CreateLoadedResponse struct { // handle of type TPM_HT_TRANSIENT for loaded object ObjectHandle TPMHandle `gotpm:"handle"` // the sensitive area of the object (optional) OutPrivate TPM2BPrivate `gotpm:"optional"` // the public portion of the created object OutPublic TPM2BPublic // the name of the created object Name TPM2BName } // EncryptDecrypt2 is the input to TPM2_EncryptDecrypt2 type EncryptDecrypt2 struct { // reference to public portion of symmetric key to use for encryption KeyHandle handle `gotpm:"handle,auth"` Message TPM2BMaxBuffer Decrypt TPMIYesNo Mode TPMIAlgSymMode `gotpm:"nullable"` IV TPM2BIV } // Command implements the Command interface. func (EncryptDecrypt2) Command() TPMCC { return TPMCCEncryptDecrypt2 } // Execute executes the command and returns the response. func (cmd EncryptDecrypt2) Execute(t transport.TPM, s ...Session) (*EncryptDecrypt2Response, error) { var rsp EncryptDecrypt2Response err := execute[EncryptDecrypt2Response](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // EncryptDecrypt2Response is the response from TPM2_EncryptDecrypt2. type EncryptDecrypt2Response struct { OutData TPM2BMaxBuffer IV TPM2BIV } // RSAEncrypt is the input to TPM2_RSA_Encrypt // See definition in Part 3, Commands, section 14.2. type RSAEncrypt struct { // reference to public portion of RSA key to use for encryption KeyHandle handle `gotpm:"handle"` // message to be encrypted Message TPM2BPublicKeyRSA // the padding scheme to use if scheme associated with keyHandle is TPM_ALG_NULL InScheme TPMTRSADecrypt `gotpm:"nullable"` // optional label L to be associated with the message Label TPM2BData `gotpm:"optional"` } // Command implements the Command interface. func (RSAEncrypt) Command() TPMCC { return TPMCCRSAEncrypt } // Execute executes the command and returns the response. func (cmd RSAEncrypt) Execute(t transport.TPM, s ...Session) (*RSAEncryptResponse, error) { var rsp RSAEncryptResponse if err := execute[RSAEncryptResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // RSAEncryptResponse is the response from TPM2_RSA_Encrypt type RSAEncryptResponse struct { // encrypted output OutData TPM2BPublicKeyRSA } // RSADecrypt is the input to TPM2_RSA_Decrypt // See definition in Part 3, Commands, section 14.3. type RSADecrypt struct { // RSA key to use for decryption KeyHandle handle `gotpm:"handle,auth"` // cipher text to be decrypted CipherText TPM2BPublicKeyRSA // the padding scheme to use if scheme associated with keyHandle is TPM_ALG_NULL InScheme TPMTRSADecrypt `gotpm:"nullable"` // label whose association with the message is to be verified Label TPM2BData `gotpm:"optional"` } // Command implements the Command interface. func (RSADecrypt) Command() TPMCC { return TPMCCRSADecrypt } // Execute executes the command and returns the response. func (cmd RSADecrypt) Execute(t transport.TPM, s ...Session) (*RSADecryptResponse, error) { var rsp RSADecryptResponse if err := execute[RSADecryptResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // RSADecryptResponse is the response from TPM2_RSA_Decrypt type RSADecryptResponse struct { // decrypted output Message TPM2BPublicKeyRSA } // ECDHZGen is the input to TPM2_ECDHZGen. // See definition in Part 3, Commands, section 14.5 type ECDHZGen struct { // handle of a loaded ECC key KeyHandle handle `gotpm:"handle,auth"` // a public key InPoint TPM2BECCPoint } // Command implements the Command interface. func (ECDHZGen) Command() TPMCC { return TPMCCECDHZGen } // Execute executes the command and returns the response. func (cmd ECDHZGen) Execute(t transport.TPM, s ...Session) (*ECDHZGenResponse, error) { var rsp ECDHZGenResponse if err := execute[ECDHZGenResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ECDHZGenResponse is the response from TPM2_ECDHZGen. type ECDHZGenResponse struct { // X and Y coordinates of the product of the multiplication OutPoint TPM2BECCPoint } // Hash is the input to TPM2_Hash. // See definition in Part 3, Commands, section 15.4 type Hash struct { //data to be hashed Data TPM2BMaxBuffer // algorithm for the hash being computed - shall not be TPM_ALH_NULL HashAlg TPMIAlgHash // hierarchy to use for the ticket (TPM_RH_NULL_allowed) Hierarchy TPMIRHHierarchy `gotpm:"nullable"` } // Command implements the Command interface. func (Hash) Command() TPMCC { return TPMCCHash } // Execute executes the command and returns the response. func (cmd Hash) Execute(t transport.TPM, s ...Session) (*HashResponse, error) { var rsp HashResponse if err := execute[HashResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // HashResponse is the response from TPM2_Hash. type HashResponse struct { // results OutHash TPM2BDigest // ticket indicating that the sequence of octets used to // compute outDigest did not start with TPM_GENERATED_VALUE Validation TPMTTKHashCheck } // GetRandom is the input to TPM2_GetRandom. // See definition in Part 3, Commands, section 16.1 type GetRandom struct { // number of octets to return BytesRequested uint16 } // Command implements the Command interface. func (GetRandom) Command() TPMCC { return TPMCCGetRandom } // Execute executes the command and returns the response. func (cmd GetRandom) Execute(t transport.TPM, s ...Session) (*GetRandomResponse, error) { var rsp GetRandomResponse if err := execute[GetRandomResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // GetRandomResponse is the response from TPM2_GetRandom. type GetRandomResponse struct { // the random octets RandomBytes TPM2BDigest } // HashSequenceStart is the input to TPM2_HashSequenceStart. // See definition in Part 3, Commands, section 17.3 type HashSequenceStart struct { // authorization value for subsequent use of the sequence Auth TPM2BAuth // the hash algorithm to use for the hash sequence // An Event Sequence starts if this is TPM_ALG_NULL. HashAlg TPMIAlgHash } // Command implements the Command interface. func (HashSequenceStart) Command() TPMCC { return TPMCCHashSequenceStart } // Execute executes the command and returns the response. func (cmd HashSequenceStart) Execute(t transport.TPM, s ...Session) (*HashSequenceStartResponse, error) { var rsp HashSequenceStartResponse if err := execute[HashSequenceStartResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // HashSequenceStartResponse is the response from TPM2_StartHashSequence. type HashSequenceStartResponse struct { // a handle to reference the sequence SequenceHandle TPMIDHObject } // HmacStart is the input to TPM2_HMAC_Start. // See definition in Part 3, Commands, section 17.2.2 type HmacStart struct { // HMAC key handle Handle handle `gotpm:"handle,auth"` // authorization value for subsequent use of the sequence Auth TPM2BAuth // the hash algorithm to use for the hmac sequence HashAlg TPMIAlgHash `gotpm:"nullable"` } // Command implements the Command interface. func (HmacStart) Command() TPMCC { return TPMCCHMACStart } // Execute executes the command and returns the response. func (cmd HmacStart) Execute(t transport.TPM, s ...Session) (*HmacStartResponse, error) { var rsp HmacStartResponse if err := execute[HmacStartResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // HmacStartResponse is the response from TPM2_HMAC_Start. // See definition in Part 3, Commands, section 17.2.2 type HmacStartResponse struct { // a handle to reference the sequence SequenceHandle TPMIDHObject `gotpm:"handle"` } // SequenceUpdate is the input to TPM2_SequenceUpdate. // See definition in Part 3, Commands, section 17.4 type SequenceUpdate struct { // handle for the sequence object SequenceHandle handle `gotpm:"handle,auth,anon"` // data to be added to hash Buffer TPM2BMaxBuffer } // Command implements the Command interface. func (SequenceUpdate) Command() TPMCC { return TPMCCSequenceUpdate } // Execute executes the command and returns the response. func (cmd SequenceUpdate) Execute(t transport.TPM, s ...Session) (*SequenceUpdateResponse, error) { var rsp SequenceUpdateResponse if err := execute[SequenceUpdateResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // SequenceUpdateResponse is the response from TPM2_SequenceUpdate. type SequenceUpdateResponse struct{} // SequenceComplete is the input to TPM2_SequenceComplete. // See definition in Part 3, Commands, section 17.5 type SequenceComplete struct { // authorization for the sequence SequenceHandle handle `gotpm:"handle,auth,anon"` // data to be added to the hash/HMAC Buffer TPM2BMaxBuffer // hierarchy of the ticket for a hash Hierarchy TPMIRHHierarchy `gotpm:"nullable"` } // Command implements the Command interface. func (SequenceComplete) Command() TPMCC { return TPMCCSequenceComplete } // Execute executes the command and returns the response. func (cmd SequenceComplete) Execute(t transport.TPM, s ...Session) (*SequenceCompleteResponse, error) { var rsp SequenceCompleteResponse if err := execute[SequenceCompleteResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // SequenceCompleteResponse is the response from TPM2_SequenceComplete. type SequenceCompleteResponse struct { // the returned HMAC or digest in a sized buffer Result TPM2BDigest // ticket indicating that the sequence of octets used to // compute outDigest did not start with TPM_GENERATED_VALUE Validation TPMTTKHashCheck } // Certify is the input to TPM2_Certify. // See definition in Part 3, Commands, section 18.2. type Certify struct { // handle of the object to be certified ObjectHandle handle `gotpm:"handle,auth"` // handle of the key used to sign the attestation structure SignHandle handle `gotpm:"handle,auth"` // user provided qualifying data QualifyingData TPM2BData // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL InScheme TPMTSigScheme } // Command implements the Command interface. func (Certify) Command() TPMCC { return TPMCCCertify } // Execute executes the command and returns the response. func (cmd Certify) Execute(t transport.TPM, s ...Session) (*CertifyResponse, error) { var rsp CertifyResponse if err := execute[CertifyResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CertifyResponse is the response from TPM2_Certify. type CertifyResponse struct { // the structure that was signed CertifyInfo TPM2BAttest // the asymmetric signature over certifyInfo using the key referenced by signHandle Signature TPMTSignature } // CertifyCreation is the input to TPM2_CertifyCreation. // See definition in Part 3, Commands, section 18.3. type CertifyCreation struct { // handle of the key that will sign the attestation block SignHandle handle `gotpm:"handle,auth"` // the object associated with the creation data ObjectHandle handle `gotpm:"handle"` // user-provided qualifying data QualifyingData TPM2BData // hash of the creation data produced by TPM2_Create() or TPM2_CreatePrimary() CreationHash TPM2BDigest // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL InScheme TPMTSigScheme // ticket produced by TPM2_Create() or TPM2_CreatePrimary() CreationTicket TPMTTKCreation } // Command implements the Command interface. func (CertifyCreation) Command() TPMCC { return TPMCCCertifyCreation } // Execute executes the command and returns the response. func (cmd CertifyCreation) Execute(t transport.TPM, s ...Session) (*CertifyCreationResponse, error) { var rsp CertifyCreationResponse if err := execute[CertifyCreationResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CertifyCreationResponse is the response from TPM2_CertifyCreation. type CertifyCreationResponse struct { // the structure that was signed CertifyInfo TPM2BAttest // the signature over certifyInfo Signature TPMTSignature } // Quote is the input to TPM2_Quote. // See definition in Part 3, Commands, section 18.4 type Quote struct { // handle of key that will perform signature SignHandle handle `gotpm:"handle,auth"` // data supplied by the caller QualifyingData TPM2BData // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL InScheme TPMTSigScheme // PCR set to quote PCRSelect TPMLPCRSelection } // Command implements the Command interface. func (Quote) Command() TPMCC { return TPMCCQuote } // Execute executes the command and returns the response. func (cmd Quote) Execute(t transport.TPM, s ...Session) (*QuoteResponse, error) { var rsp QuoteResponse if err := execute[QuoteResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // QuoteResponse is the response from TPM2_Quote. type QuoteResponse struct { // the quoted information Quoted TPM2BAttest // the signature over quoted Signature TPMTSignature } // GetSessionAuditDigest is the input to TPM2_GetSessionAuditDigest. // See definition in Part 3, Commands, section 18.5 type GetSessionAuditDigest struct { // handle of the privacy administrator (TPM_RH_ENDORSEMENT) PrivacyAdminHandle handle `gotpm:"handle,auth"` // handle of the signing key SignHandle handle `gotpm:"handle,auth"` // handle of the audit session SessionHandle handle `gotpm:"handle"` // user-provided qualifying data – may be zero-length QualifyingData TPM2BData // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL InScheme TPMTSigScheme } // Command implements the Command interface. func (GetSessionAuditDigest) Command() TPMCC { return TPMCCGetSessionAuditDigest } // Execute executes the command and returns the response. func (cmd GetSessionAuditDigest) Execute(t transport.TPM, s ...Session) (*GetSessionAuditDigestResponse, error) { var rsp GetSessionAuditDigestResponse if err := execute[GetSessionAuditDigestResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // GetSessionAuditDigestResponse is the response from // TPM2_GetSessionAuditDigest. type GetSessionAuditDigestResponse struct { // the audit information that was signed AuditInfo TPM2BAttest // the signature over auditInfo Signature TPMTSignature } // Commit is the input to TPM2_Commit. // See definition in Part 3, Commands, section 19.2. type Commit struct { // handle of the key that will be used in the signing operation SignHandle handle `gotpm:"handle,auth"` // a point (M) on the curve used by signHandle P1 TPM2BECCPoint // octet array used to derive x-coordinate of a base point S2 TPM2BSensitiveData // y coordinate of the point associated with s2 Y2 TPM2BECCParameter } // Command implements the Command interface. func (Commit) Command() TPMCC { return TPMCCCommit } // Execute executes the command and returns the response. func (cmd Commit) Execute(t transport.TPM, s ...Session) (*CommitResponse, error) { var rsp CommitResponse if err := execute[CommitResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CommitResponse is the response from TPM2_Commit. type CommitResponse struct { // ECC point K ≔ [ds](x2, y2) K TPM2BECCPoint // ECC point L ≔ [r](x2, y2) L TPM2BECCPoint // ECC point E ≔ [r]P1 E TPM2BECCPoint // least-significant 16 bits of commitCount Counter uint16 } // VerifySignature is the input to TPM2_VerifySignature. // See definition in Part 3, Commands, section 20.1 type VerifySignature struct { // handle of public key that will be used in the validation KeyHandle handle `gotpm:"handle"` // digest of the signed message Digest TPM2BDigest // signature to be tested Signature TPMTSignature } // Command implements the Command interface. func (VerifySignature) Command() TPMCC { return TPMCCVerifySignature } // Execute executes the command and returns the response. func (cmd VerifySignature) Execute(t transport.TPM, s ...Session) (*VerifySignatureResponse, error) { var rsp VerifySignatureResponse if err := execute[VerifySignatureResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // VerifySignatureResponse is the response from TPM2_VerifySignature. type VerifySignatureResponse struct { Validation TPMTTKVerified } // Sign is the input to TPM2_Sign. // See definition in Part 3, Commands, section 20.2. type Sign struct { // Handle of key that will perform signing KeyHandle handle `gotpm:"handle,auth"` // digest to be signed Digest TPM2BDigest // signing scheme to use if the scheme for keyHandle is TPM_ALG_NULL InScheme TPMTSigScheme `gotpm:"nullable"` // proof that digest was created by the TPM. // If keyHandle is not a restricted signing key, then this // may be a NULL Ticket with tag = TPM_ST_CHECKHASH. Validation TPMTTKHashCheck } // Command implements the Command interface. func (Sign) Command() TPMCC { return TPMCCSign } // Execute executes the command and returns the response. func (cmd Sign) Execute(t transport.TPM, s ...Session) (*SignResponse, error) { var rsp SignResponse if err := execute[SignResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // SignResponse is the response from TPM2_Sign. type SignResponse struct { // the signature Signature TPMTSignature } // PCRExtend is the input to TPM2_PCR_Extend. // See definition in Part 3, Commands, section 22.2 type PCRExtend struct { // handle of the PCR PCRHandle handle `gotpm:"handle,auth"` // list of tagged digest values to be extended Digests TPMLDigestValues } // Command implements the Command interface. func (PCRExtend) Command() TPMCC { return TPMCCPCRExtend } // Execute executes the command and returns the response. func (cmd PCRExtend) Execute(t transport.TPM, s ...Session) (*PCRExtendResponse, error) { var rsp PCRExtendResponse err := execute[PCRExtendResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // PCRExtendResponse is the response from TPM2_PCR_Extend. type PCRExtendResponse struct{} // PCREvent is the input to TPM2_PCR_Event. // See definition in Part 3, Commands, section 22.3 type PCREvent struct { // Handle of the PCR PCRHandle handle `gotpm:"handle,auth"` // Event data in sized buffer EventData TPM2BEvent } // Command implements the Command interface. func (PCREvent) Command() TPMCC { return TPMCCPCREvent } // Execute executes the command and returns the response. func (cmd PCREvent) Execute(t transport.TPM, s ...Session) (*PCREventResponse, error) { var rsp PCREventResponse err := execute[PCREventResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // PCREventResponse is the response from TPM2_PCR_Event. type PCREventResponse struct{} // PCRRead is the input to TPM2_PCR_Read. // See definition in Part 3, Commands, section 22.4 type PCRRead struct { // The selection of PCR to read PCRSelectionIn TPMLPCRSelection } // Command implements the Command interface. func (PCRRead) Command() TPMCC { return TPMCCPCRRead } // Execute executes the command and returns the response. func (cmd PCRRead) Execute(t transport.TPM, s ...Session) (*PCRReadResponse, error) { var rsp PCRReadResponse if err := execute[PCRReadResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // PCRReadResponse is the response from TPM2_PCR_Read. type PCRReadResponse struct { // the current value of the PCR update counter PCRUpdateCounter uint32 // the PCR in the returned list PCRSelectionOut TPMLPCRSelection // the contents of the PCR indicated in pcrSelectOut-> pcrSelection[] as tagged digests PCRValues TPMLDigest } // PCRReset is the input to TPM2_PCRReset. // See definition in Part 3, Commands, section 22.8. type PCRReset struct { // the PCR to reset PCRHandle handle `gotpm:"handle,auth"` } // Command implements the Command interface. func (PCRReset) Command() TPMCC { return TPMCCPCRReset } // Execute executes the command and returns the response. func (cmd PCRReset) Execute(t transport.TPM, s ...Session) (*PCRResetResponse, error) { var rsp PCRResetResponse if err := execute[PCRResetResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // PCRResetResponse is the response from TPM2_PCRReset. type PCRResetResponse struct{} // PolicySigned is the input to TPM2_PolicySigned. // See definition in Part 3, Commands, section 23.3. type PolicySigned struct { // handle for an entity providing the authorization AuthObject handle `gotpm:"handle"` // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the policy nonce for the session NonceTPM TPM2BNonce // digest of the command parameters to which this authorization is limited CPHashA TPM2BDigest // a reference to a policy relating to the authorization – may be the Empty Buffer PolicyRef TPM2BNonce // time when authorization will expire, measured in seconds from the time // that nonceTPM was generated Expiration int32 // signed authorization (not optional) Auth TPMTSignature } // Command implements the Command interface. func (PolicySigned) Command() TPMCC { return TPMCCPolicySigned } // Execute executes the command and returns the response. func (cmd PolicySigned) Execute(t transport.TPM, s ...Session) (*PolicySignedResponse, error) { var rsp PolicySignedResponse if err := execute[PolicySignedResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // policyUpdate implements the PolicyUpdate helper for the several TPM policy // commands as described in Part 3, 23.2.3. func policyUpdate(policy *PolicyCalculator, cc TPMCC, arg2, arg3 []byte) error { if err := policy.Update(cc, arg2); err != nil { return err } return policy.Update(arg3) } // Update implements the PolicyCommand interface. func (cmd PolicySigned) Update(policy *PolicyCalculator) error { return policyUpdate(policy, TPMCCPolicySigned, cmd.AuthObject.KnownName().Buffer, cmd.PolicyRef.Buffer) } // PolicySignedResponse is the response from TPM2_PolicySigned. type PolicySignedResponse struct { // implementation-specific time value used to indicate to the TPM when the ticket expires Timeout TPM2BTimeout // produced if the command succeeds and expiration in the command was non-zero PolicyTicket TPMTTKAuth } // PolicySecret is the input to TPM2_PolicySecret. // See definition in Part 3, Commands, section 23.4. type PolicySecret struct { // handle for an entity providing the authorization AuthHandle handle `gotpm:"handle,auth"` // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the policy nonce for the session NonceTPM TPM2BNonce // digest of the command parameters to which this authorization is limited CPHashA TPM2BDigest // a reference to a policy relating to the authorization – may be the Empty Buffer PolicyRef TPM2BNonce // time when authorization will expire, measured in seconds from the time // that nonceTPM was generated Expiration int32 } // Command implements the Command interface. func (PolicySecret) Command() TPMCC { return TPMCCPolicySecret } // Execute executes the command and returns the response. func (cmd PolicySecret) Execute(t transport.TPM, s ...Session) (*PolicySecretResponse, error) { var rsp PolicySecretResponse if err := execute[PolicySecretResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicySecret) Update(policy *PolicyCalculator) { policyUpdate(policy, TPMCCPolicySecret, cmd.AuthHandle.KnownName().Buffer, cmd.PolicyRef.Buffer) } // PolicySecretResponse is the response from TPM2_PolicySecret. type PolicySecretResponse struct { // implementation-specific time value used to indicate to the TPM when the ticket expires Timeout TPM2BTimeout // produced if the command succeeds and expiration in the command was non-zero PolicyTicket TPMTTKAuth } // PolicyOr is the input to TPM2_PolicyOR. // See definition in Part 3, Commands, section 23.6. type PolicyOr struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the list of hashes to check for a match PHashList TPMLDigest } // Command implements the Command interface. func (PolicyOr) Command() TPMCC { return TPMCCPolicyOR } // Execute executes the command and returns the response. func (cmd PolicyOr) Execute(t transport.TPM, s ...Session) (*PolicyOrResponse, error) { var rsp PolicyOrResponse err := execute[PolicyOrResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyOr) Update(policy *PolicyCalculator) error { policy.Reset() var digests bytes.Buffer for _, digest := range cmd.PHashList.Digests { digests.Write(digest.Buffer) } return policy.Update(TPMCCPolicyOR, digests.Bytes()) } // PolicyOrResponse is the response from TPM2_PolicyOr. type PolicyOrResponse struct{} // PolicyPCR is the input to TPM2_PolicyPCR. // See definition in Part 3, Commands, section 23.7. type PolicyPCR struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // expected digest value of the selected PCR using the // hash algorithm of the session; may be zero length PcrDigest TPM2BDigest // the PCR to include in the check digest Pcrs TPMLPCRSelection } // Command implements the Command interface. func (PolicyPCR) Command() TPMCC { return TPMCCPolicyPCR } // Execute executes the command and returns the response. func (cmd PolicyPCR) Execute(t transport.TPM, s ...Session) (*PolicyPCRResponse, error) { var rsp PolicyPCRResponse err := execute[PolicyPCRResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyPCR) Update(policy *PolicyCalculator) error { return policy.Update(TPMCCPolicyPCR, cmd.Pcrs, cmd.PcrDigest.Buffer) } // PolicyPCRResponse is the response from TPM2_PolicyPCR. type PolicyPCRResponse struct{} // PolicyAuthValue is the input to TPM2_PolicyAuthValue. // See definition in Part 3, Commands, section 23.17. type PolicyAuthValue struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` } // Command implements the Command interface. func (PolicyAuthValue) Command() TPMCC { return TPMCCPolicyAuthValue } // Execute executes the command and returns the response. func (cmd PolicyAuthValue) Execute(t transport.TPM, s ...Session) (*PolicyAuthValueResponse, error) { var rsp PolicyAuthValueResponse err := execute[PolicyAuthValueResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyAuthValue interface. func (cmd PolicyAuthValue) Update(policy *PolicyCalculator) error { return policy.Update(TPMCCPolicyAuthValue) } // PolicyAuthValueResponse is the response from TPM2_PolicyAuthValue. type PolicyAuthValueResponse struct{} // PolicyDuplicationSelect is the input to TPM2_PolicyDuplicationSelect. // See definition in Part 3, Commands, section 23.15. type PolicyDuplicationSelect struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` ObjectName TPM2BName NewParentName TPM2BName IncludeObject TPMIYesNo } // Command implements the Command interface. func (PolicyDuplicationSelect) Command() TPMCC { return TPMCCPolicyDuplicationSelect } // Execute executes the command and returns the response. func (cmd PolicyDuplicationSelect) Execute(t transport.TPM, s ...Session) (*PolicyDuplicationSelectResponse, error) { var rsp PolicyDuplicationSelectResponse err := execute[PolicyDuplicationSelectResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyDuplicationSelect interface. func (cmd PolicyDuplicationSelect) Update(policy *PolicyCalculator) error { if cmd.IncludeObject { return policy.Update(TPMCCPolicyDuplicationSelect, cmd.ObjectName.Buffer, cmd.NewParentName.Buffer, cmd.IncludeObject) } return policy.Update(TPMCCPolicyDuplicationSelect, cmd.NewParentName.Buffer, cmd.IncludeObject) } // PolicyDuplicationSelectResponse is the response from TPM2_PolicyDuplicationSelect. type PolicyDuplicationSelectResponse struct{} // PolicyNV is the input to TPM2_PolicyNV. // See definition in Part 3, Commands, section 23.9. type PolicyNV struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV Index of the area to read NVIndex handle `gotpm:"handle"` // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the second operand OperandB TPM2BOperand // the octet offset in the NV Index for the start of operand A Offset uint16 // the comparison to make Operation TPMEO } // Command implements the Command interface. func (PolicyNV) Command() TPMCC { return TPMCCPolicyNV } // Execute executes the command and returns the response. func (cmd PolicyNV) Execute(t transport.TPM, s ...Session) (*PolicyNVResponse, error) { var rsp PolicyNVResponse err := execute[PolicyNVResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyNV) Update(policy *PolicyCalculator) error { alg, err := policy.alg.Hash() if err != nil { return err } h := alg.New() h.Write(cmd.OperandB.Buffer) binary.Write(h, binary.BigEndian, cmd.Offset) binary.Write(h, binary.BigEndian, cmd.Operation) args := h.Sum(nil) return policy.Update(TPMCCPolicyNV, args, cmd.NVIndex.KnownName().Buffer) } // PolicyNVResponse is the response from TPM2_PolicyPCR. type PolicyNVResponse struct{} // PolicyCommandCode is the input to TPM2_PolicyCommandCode. // See definition in Part 3, Commands, section 23.11. type PolicyCommandCode struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the allowed commandCode Code TPMCC } // Command implements the Command interface. func (PolicyCommandCode) Command() TPMCC { return TPMCCPolicyCommandCode } // Execute executes the command and returns the response. func (cmd PolicyCommandCode) Execute(t transport.TPM, s ...Session) (*PolicyCommandCodeResponse, error) { var rsp PolicyCommandCodeResponse err := execute[PolicyCommandCodeResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyCommandCode) Update(policy *PolicyCalculator) error { return policy.Update(TPMCCPolicyCommandCode, cmd.Code) } // PolicyCommandCodeResponse is the response from TPM2_PolicyCommandCode. type PolicyCommandCodeResponse struct{} // PolicyCPHash is the input to TPM2_PolicyCpHash. // See definition in Part 3, Commands, section 23.13. type PolicyCPHash struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // the cpHash added to the policy CPHashA TPM2BDigest } // Command implements the Command interface. func (PolicyCPHash) Command() TPMCC { return TPMCCPolicyCpHash } // Execute executes the command and returns the response. func (cmd PolicyCPHash) Execute(t transport.TPM, s ...Session) (*PolicyCPHashResponse, error) { var rsp PolicyCPHashResponse err := execute[PolicyCPHashResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyCPHash) Update(policy *PolicyCalculator) error { return policy.Update(TPMCCPolicyCpHash, cmd.CPHashA.Buffer) } // PolicyCPHashResponse is the response from TPM2_PolicyCpHash. type PolicyCPHashResponse struct{} // PolicyAuthorize is the input to TPM2_PolicySigned. // See definition in Part 3, Commands, section 23.16. type PolicyAuthorize struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // digest of the policy being approved ApprovedPolicy TPM2BDigest // a policy qualifier PolicyRef TPM2BDigest // Name of a key that can sign a policy addition KeySign TPM2BName // ticket validating that approvedPolicy and policyRef were signed by keySign CheckTicket TPMTTKVerified } // Command implements the Command interface. func (PolicyAuthorize) Command() TPMCC { return TPMCCPolicyAuthorize } // Execute executes the command and returns the response. func (cmd PolicyAuthorize) Execute(t transport.TPM, s ...Session) (*PolicyAuthorizeResponse, error) { var rsp PolicyAuthorizeResponse err := execute[PolicyAuthorizeResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyAuthorize) Update(policy *PolicyCalculator) error { return policyUpdate(policy, TPMCCPolicyAuthorize, cmd.KeySign.Buffer, cmd.PolicyRef.Buffer) } // PolicyAuthorizeResponse is the response from TPM2_PolicyAuthorize. type PolicyAuthorizeResponse struct{} // PolicyGetDigest is the input to TPM2_PolicyGetDigest. // See definition in Part 3, Commands, section 23.19. type PolicyGetDigest struct { // handle for the policy session PolicySession handle `gotpm:"handle"` } // Command implements the Command interface. func (PolicyGetDigest) Command() TPMCC { return TPMCCPolicyGetDigest } // Execute executes the command and returns the response. func (cmd PolicyGetDigest) Execute(t transport.TPM, s ...Session) (*PolicyGetDigestResponse, error) { var rsp PolicyGetDigestResponse if err := execute[PolicyGetDigestResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // PolicyGetDigestResponse is the response from TPM2_PolicyGetDigest. type PolicyGetDigestResponse struct { // the current value of the policySession→policyDigest PolicyDigest TPM2BDigest } // PolicyNVWritten is the input to TPM2_PolicyNvWritten. // See definition in Part 3, Commands, section 23.20. type PolicyNVWritten struct { // handle for the policy session being extended PolicySession handle `gotpm:"handle"` // YES if NV Index is required to have been written // NO if NV Index is required not to have been written WrittenSet TPMIYesNo } // Command implements the Command interface. func (PolicyNVWritten) Command() TPMCC { return TPMCCPolicyNvWritten } // Execute executes the command and returns the response. func (cmd PolicyNVWritten) Execute(t transport.TPM, s ...Session) (*PolicyNVWrittenResponse, error) { var rsp PolicyNVWrittenResponse if err := execute[PolicyNVWrittenResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyNVWritten) Update(policy *PolicyCalculator) error { return policy.Update(TPMCCPolicyNvWritten, cmd.WrittenSet) } // PolicyNVWrittenResponse is the response from TPM2_PolicyNvWritten. type PolicyNVWrittenResponse struct { } // PolicyAuthorizeNV is the input to TPM2_PolicyAuthorizeNV. // See definition in Part 3, Commands, section 23.22. type PolicyAuthorizeNV struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV Index of the area to read NVIndex handle `gotpm:"handle"` // handle for the policy session being extended PolicySession handle `gotpm:"handle"` } // Command implements the Command interface. func (PolicyAuthorizeNV) Command() TPMCC { return TPMCCPolicyAuthorizeNV } // Execute executes the command and returns the response. func (cmd PolicyAuthorizeNV) Execute(t transport.TPM, s ...Session) (*PolicyAuthorizeNVResponse, error) { var rsp PolicyAuthorizeNVResponse err := execute[PolicyAuthorizeNVResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // Update implements the PolicyCommand interface. func (cmd PolicyAuthorizeNV) Update(policy *PolicyCalculator) error { policy.Reset() return policy.Update(TPMCCPolicyAuthorizeNV, cmd.NVIndex.KnownName().Buffer) } // PolicyAuthorizeNVResponse is the response from TPM2_PolicyAuthorizeNV. type PolicyAuthorizeNVResponse struct{} // CreatePrimary is the input to TPM2_CreatePrimary. // See definition in Part 3, Commands, section 24.1 type CreatePrimary struct { // TPM_RH_ENDORSEMENT, TPM_RH_OWNER, TPM_RH_PLATFORM+{PP}, // TPM_RH_NULL, TPM_RH_FW_ENDORSEMENT, TPM_RH_FW_OWNER // TPM_RH_FW_PLATFORM+{PP} or TPM_RH_FW_NULL PrimaryHandle handle `gotpm:"handle,auth"` // the sensitive data InSensitive TPM2BSensitiveCreate // the public template InPublic TPM2BPublic // data that will be included in the creation data for this // object to provide permanent, verifiable linkage between this // object and some object owner data OutsideInfo TPM2BData // PCR that will be used in creation data CreationPCR TPMLPCRSelection } // Command implements the Command interface. func (CreatePrimary) Command() TPMCC { return TPMCCCreatePrimary } // Execute executes the command and returns the response. func (cmd CreatePrimary) Execute(t transport.TPM, s ...Session) (*CreatePrimaryResponse, error) { var rsp CreatePrimaryResponse if err := execute[CreatePrimaryResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // CreatePrimaryResponse is the response from TPM2_CreatePrimary. type CreatePrimaryResponse struct { // handle of type TPM_HT_TRANSIENT for created Primary Object ObjectHandle TPMHandle `gotpm:"handle"` // the public portion of the created object OutPublic TPM2BPublic // contains a TPMS_CREATION_DATA CreationData tpm2bCreationData // digest of creationData using nameAlg of outPublic CreationHash TPM2BDigest // ticket used by TPM2_CertifyCreation() to validate that the // creation data was produced by the TPM. CreationTicket TPMTTKCreation // the name of the created object Name TPM2BName } // Clear is the input to TPM2_Clear. // See definition in Part 3, Commands, section 24.6 type Clear struct { // TPM_RH_LOCKOUT or TPM_RH_PLATFORM+{PP} AuthHandle handle `gotpm:"handle,auth"` } // Command implements the Command interface. func (Clear) Command() TPMCC { return TPMCCClear } // Execute executes the command and returns the response. func (cmd Clear) Execute(t transport.TPM, s ...Session) (*ClearResponse, error) { var rsp ClearResponse err := execute[ClearResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // ClearResponse is the response from TPM2_Clear. type ClearResponse struct{} // HierarchyChangeAuth is the input to TPM2_HierarchyChangeAuth. // See definition in Part 3, Commands, section 24.8 type HierarchyChangeAuth struct { // TPM_RH_ENDORSEMENT, TPM_RH_LOCKOUT, TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} AuthHandle handle `gotpm:"handle,auth"` // new authorization value NewAuth TPM2BAuth } // Command implements the Command interface. func (HierarchyChangeAuth) Command() TPMCC { return TPMCCHierarchyChanegAuth } // Execute executes the command and returns the response. func (cmd HierarchyChangeAuth) Execute(t transport.TPM, s ...Session) (*HierarchyChangeAuthResponse, error) { var rsp HierarchyChangeAuthResponse if err := execute[HierarchyChangeAuthResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // HierarchyChangeAuthResponse is the response from TPM2_HierarchyChangeAuth. type HierarchyChangeAuthResponse struct{} // ContextSave is the input to TPM2_ContextSave. // See definition in Part 3, Commands, section 28.2 type ContextSave struct { // handle of the resource to save SaveHandle TPMIDHContext } // Command implements the Command interface. func (ContextSave) Command() TPMCC { return TPMCCContextSave } // Execute executes the command and returns the response. func (cmd ContextSave) Execute(t transport.TPM, s ...Session) (*ContextSaveResponse, error) { var rsp ContextSaveResponse if err := execute[ContextSaveResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ContextSaveResponse is the response from TPM2_ContextSave. type ContextSaveResponse struct { Context TPMSContext } // ContextLoad is the input to TPM2_ContextLoad. // See definition in Part 3, Commands, section 28.3 type ContextLoad struct { // the context blob Context TPMSContext } // Command implements the Command interface. func (ContextLoad) Command() TPMCC { return TPMCCContextLoad } // Execute executes the command and returns the response. func (cmd ContextLoad) Execute(t transport.TPM, s ...Session) (*ContextLoadResponse, error) { var rsp ContextLoadResponse if err := execute[ContextLoadResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // ContextLoadResponse is the response from TPM2_ContextLoad. type ContextLoadResponse struct { // the handle assigned to the resource after it has been successfully loaded LoadedHandle TPMIDHContext } // FlushContext is the input to TPM2_FlushContext. // See definition in Part 3, Commands, section 28.4 type FlushContext struct { // the handle of the item to flush FlushHandle handle `gotpm:"handle"` } // Command implements the Command interface. func (FlushContext) Command() TPMCC { return TPMCCFlushContext } // Execute executes the command and returns the response. func (cmd FlushContext) Execute(t transport.TPM, s ...Session) (*FlushContextResponse, error) { var rsp FlushContextResponse err := execute[FlushContextResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // FlushContextResponse is the response from TPM2_FlushContext. type FlushContextResponse struct{} // EvictControl is the input to TPM2_EvictControl. // See definition in Part 3, Commands, section 28.5 type EvictControl struct { // TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} Auth handle `gotpm:"handle,auth"` ObjectHandle handle `gotpm:"handle"` PersistentHandle TPMIDHPersistent } // EvictControlResponse is the response from TPM2_EvictControl. type EvictControlResponse struct{} // Command implements the Command interface. func (EvictControl) Command() TPMCC { return TPMCCEvictControl } // Execute executes the command and returns the response. func (cmd EvictControl) Execute(t transport.TPM, s ...Session) (*EvictControlResponse, error) { var rsp EvictControlResponse if err := execute[EvictControlResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // Duplicate is the input to TPM2_Duplicate. // See definition in Part 3, Commands, section 13.1 type Duplicate struct { // ObjectHandle is the handle of the object to dupliate. ObjectHandle handle `gotpm:"handle,auth"` // NewParentHandle is the handle of the new parent. NewParentHandle handle `gotpm:"handle"` // EncryptionKeyIn is the optional symmetric encryption key used as the // inner wrapper. If SymmetricAlg is TPM_ALG_NULL, then this parameter // shall be the Empty Buffer. EncryptionKeyIn TPM2BData // Definition of the symmetric algorithm to use for the inner wrapper. // It may be TPM_ALG_NULL if no inner wrapper is applied. Symmetric TPMTSymDef } // DuplicateResponse is the response from TPM2_Duplicate. type DuplicateResponse struct { // EncryptionKeyOut is the symmetric encryption key used as the // inner wrapper. If SymmetricAlg is TPM_ALG_NULL, this value // shall be the Empty Buffer. EncryptionKeyOut TPM2BData // Duplicate is the private area of the object. It may be encrypted by // EncryptionKeyIn and may be doubly encrypted. Duplicate TPM2BPrivate // OutSymSeed is the seed protected by the asymmetric algorithms of new // parent. OutSymSeed TPM2BEncryptedSecret } // Command implements the Command interface. func (Duplicate) Command() TPMCC { return TPMCCDuplicate } // Execute executes the command and returns the response. func (cmd Duplicate) Execute(t transport.TPM, s ...Session) (*DuplicateResponse, error) { var rsp DuplicateResponse if err := execute[DuplicateResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // Import is the input to TPM2_Import. // See definition in Part 3, Commands, section 13.3 type Import struct { // handle of parent for new object ParentHandle handle `gotpm:"handle,auth"` // The optional symmetric encryption key used as the inner wrapper for duplicate // If SymmetricAlg is TPM_ALG_NULL, then this parametert shall be the Empty Buffer EncryptionKey TPM2BData // The public area of the object to be imported ObjectPublic TPM2BPublic // The symmetrically encrypted duplicate object that may contain an inner // symmetric wrapper Duplicate TPM2BPrivate // The seed for the symmetric key and HMAC key InSymSeed TPM2BEncryptedSecret // Definition of the symmetric algorithm to use for the inner wrapper Symmetric TPMTSymDef } // ImportResponse is the response from TPM2_Import. type ImportResponse struct { // the private portion of the object OutPrivate TPM2BPrivate } // Command implements the Command interface. func (Import) Command() TPMCC { return TPMCCImport } // Execute executes the command and returns the response. func (cmd Import) Execute(t transport.TPM, s ...Session) (*ImportResponse, error) { var rsp ImportResponse if err := execute[ImportResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // GetCapability is the input to TPM2_GetCapability. // See definition in Part 3, Commands, section 30.2 type GetCapability struct { // group selection; determines the format of the response Capability TPMCap // further definition of information Property uint32 // number of properties of the indicated type to return PropertyCount uint32 } // Command implements the Command interface. func (GetCapability) Command() TPMCC { return TPMCCGetCapability } // Execute executes the command and returns the response. func (cmd GetCapability) Execute(t transport.TPM, s ...Session) (*GetCapabilityResponse, error) { var rsp GetCapabilityResponse if err := execute[GetCapabilityResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // GetCapabilityResponse is the response from TPM2_GetCapability. type GetCapabilityResponse struct { // flag to indicate if there are more values of this type MoreData TPMIYesNo // the capability data CapabilityData TPMSCapabilityData } // TestParms is the input to TPM2_TestParms. // See definition in Part 3, Commands, section 30.3 type TestParms struct { // Algorithms parameters to be validates Parameters TPMTPublicParms } // Command implements the Command interface. func (TestParms) Command() TPMCC { return TPMCCTestParms } // Execute executes the command and returns the response. func (cmd TestParms) Execute(t transport.TPM, s ...Session) (*TestParmsResponse, error) { var rsp TestParmsResponse if err := execute[TestParmsResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // TestParmsResponse is the response from TPM2_TestParms. type TestParmsResponse struct{} // NVDefineSpace is the input to TPM2_NV_DefineSpace. // See definition in Part 3, Commands, section 31.3. type NVDefineSpace struct { // TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} AuthHandle handle `gotpm:"handle,auth"` // the authorization value Auth TPM2BAuth // the public parameters of the NV area PublicInfo TPM2BNVPublic } // Command implements the Command interface. func (NVDefineSpace) Command() TPMCC { return TPMCCNVDefineSpace } // Execute executes the command and returns the response. func (cmd NVDefineSpace) Execute(t transport.TPM, s ...Session) (*NVDefineSpaceResponse, error) { var rsp NVDefineSpaceResponse err := execute[NVDefineSpaceResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVDefineSpaceResponse is the response from TPM2_NV_DefineSpace. type NVDefineSpaceResponse struct{} // NVUndefineSpace is the input to TPM2_NV_UndefineSpace. // See definition in Part 3, Commands, section 31.4. type NVUndefineSpace struct { // TPM_RH_OWNER or TPM_RH_PLATFORM+{PP} AuthHandle handle `gotpm:"handle,auth"` // the NV Index to remove from NV space NVIndex handle `gotpm:"handle"` } // Command implements the Command interface. func (NVUndefineSpace) Command() TPMCC { return TPMCCNVUndefineSpace } // Execute executes the command and returns the response. func (cmd NVUndefineSpace) Execute(t transport.TPM, s ...Session) (*NVUndefineSpaceResponse, error) { var rsp NVUndefineSpaceResponse err := execute[NVUndefineSpaceResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVUndefineSpaceResponse is the response from TPM2_NV_UndefineSpace. type NVUndefineSpaceResponse struct{} // NVUndefineSpaceSpecial is the input to TPM2_NV_UndefineSpaceSpecial. // See definition in Part 3, Commands, section 31.5. type NVUndefineSpaceSpecial struct { // Index to be deleted NVIndex handle `gotpm:"handle,auth"` // TPM_RH_PLATFORM+{PP} Platform handle `gotpm:"handle,auth"` } // Command implements the Command interface. func (NVUndefineSpaceSpecial) Command() TPMCC { return TPMCCNVUndefineSpaceSpecial } // Execute executes the command and returns the response. func (cmd NVUndefineSpaceSpecial) Execute(t transport.TPM, s ...Session) (*NVUndefineSpaceSpecialResponse, error) { var rsp NVUndefineSpaceSpecialResponse err := execute[NVUndefineSpaceSpecialResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVUndefineSpaceSpecialResponse is the response from TPM2_NV_UndefineSpaceSpecial. type NVUndefineSpaceSpecialResponse struct{} // NVReadPublic is the input to TPM2_NV_ReadPublic. // See definition in Part 3, Commands, section 31.6. type NVReadPublic struct { // the NV index NVIndex handle `gotpm:"handle"` } // Command implements the Command interface. func (NVReadPublic) Command() TPMCC { return TPMCCNVReadPublic } // Execute executes the command and returns the response. func (cmd NVReadPublic) Execute(t transport.TPM, s ...Session) (*NVReadPublicResponse, error) { var rsp NVReadPublicResponse if err := execute[NVReadPublicResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // NVReadPublicResponse is the response from TPM2_NV_ReadPublic. type NVReadPublicResponse struct { NVPublic TPM2BNVPublic NVName TPM2BName } // NVWrite is the input to TPM2_NV_Write. // See definition in Part 3, Commands, section 31.7. type NVWrite struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV index of the area to write NVIndex handle `gotpm:"handle"` // the data to write Data TPM2BMaxNVBuffer // the octet offset into the NV Area Offset uint16 } // Command implements the Command interface. func (NVWrite) Command() TPMCC { return TPMCCNVWrite } // Execute executes the command and returns the response. func (cmd NVWrite) Execute(t transport.TPM, s ...Session) (*NVWriteResponse, error) { var rsp NVWriteResponse err := execute[NVWriteResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVWriteResponse is the response from TPM2_NV_Write. type NVWriteResponse struct{} // NVIncrement is the input to TPM2_NV_Increment. // See definition in Part 3, Commands, section 31.8. type NVIncrement struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV index of the area to write NVIndex handle `gotpm:"handle"` } // Command implements the Command interface. func (NVIncrement) Command() TPMCC { return TPMCCNVIncrement } // Execute executes the command and returns the response. func (cmd NVIncrement) Execute(t transport.TPM, s ...Session) (*NVIncrementResponse, error) { var rsp NVIncrementResponse err := execute[NVIncrementResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVIncrementResponse is the response from TPM2_NV_Increment. type NVIncrementResponse struct{} // NVWriteLock is the input to TPM2_NV_WriteLock. // See definition in Part 3, Commands, section 31.11. type NVWriteLock struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV index of the area to lock NVIndex handle `gotpm:"handle"` } // Command implements the Command interface. func (NVWriteLock) Command() TPMCC { return TPMCCNVWriteLock } // Execute executes the command and returns the response. func (cmd NVWriteLock) Execute(t transport.TPM, s ...Session) (*NVWriteLockResponse, error) { var rsp NVWriteLockResponse err := execute[NVWriteLockResponse](t, cmd, &rsp, s...) if err != nil { return nil, err } return &rsp, nil } // NVWriteLockResponse is the response from TPM2_NV_WriteLock. type NVWriteLockResponse struct{} // NVRead is the input to TPM2_NV_Read. // See definition in Part 3, Commands, section 31.13. type NVRead struct { // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // the NV index to read NVIndex handle `gotpm:"handle"` // number of octets to read Size uint16 // octet offset into the NV area Offset uint16 } // Command implements the Command interface. func (NVRead) Command() TPMCC { return TPMCCNVRead } // Execute executes the command and returns the response. func (cmd NVRead) Execute(t transport.TPM, s ...Session) (*NVReadResponse, error) { var rsp NVReadResponse if err := execute[NVReadResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // NVReadResponse is the response from TPM2_NV_Read. type NVReadResponse struct { // the data read Data TPM2BMaxNVBuffer } // NVCertify is the input to TPM2_NV_Certify. // See definition in Part 3, Commands, section 31.16. type NVCertify struct { // handle of the key used to sign the attestation structure SignHandle handle `gotpm:"handle,auth"` // handle indicating the source of the authorization value AuthHandle handle `gotpm:"handle,auth"` // Index for the area to be certified NVIndex handle `gotpm:"handle"` // user-provided qualifying data QualifyingData TPM2BData // signing scheme to use if the scheme for signHandle is TPM_ALG_NULL InScheme TPMTSigScheme `gotpm:"nullable"` // number of octets to certify Size uint16 // octet offset into the NV area Offset uint16 } // Command implements the Command interface. func (NVCertify) Command() TPMCC { return TPMCCNVCertify } // Execute executes the command and returns the response. func (cmd NVCertify) Execute(t transport.TPM, s ...Session) (*NVCertifyResponse, error) { var rsp NVCertifyResponse if err := execute[NVCertifyResponse](t, cmd, &rsp, s...); err != nil { return nil, err } return &rsp, nil } // NVCertifyResponse is the response from TPM2_NV_Read. type NVCertifyResponse struct { // the structure that was signed CertifyInfo TPM2BAttest // the asymmetric signature over certifyInfo using the key referenced by signHandle Signature TPMTSignature } go-tpm-0.9.3/tpm2/tpm2b.go000066400000000000000000000040461473040456300151750ustar00rootroot00000000000000package tpm2 import ( "bytes" "encoding/binary" "fmt" "io" ) // TPM2B is a helper type for all sized TPM structures. It can be instantiated with either a raw byte buffer or the actual struct. type TPM2B[T Marshallable, P interface { *T Unmarshallable }] struct { contents *T buffer []byte } // New2B creates a new TPM2B containing the given contents. func New2B[T Marshallable, P interface { *T Unmarshallable }](t T) TPM2B[T, P] { return TPM2B[T, P]{contents: &t} } // BytesAs2B creates a new TPM2B containing the given byte array. func BytesAs2B[T Marshallable, P interface { *T Unmarshallable }](b []byte) TPM2B[T, P] { return TPM2B[T, P]{buffer: b} } // Contents returns the structured contents of the TPM2B. // It can fail if the TPM2B was instantiated with an invalid byte buffer. func (value *TPM2B[T, P]) Contents() (*T, error) { if value.contents != nil { return value.contents, nil } if value.buffer == nil { return nil, fmt.Errorf("TPMB had no contents or buffer") } contents, err := Unmarshal[T, P](value.buffer) if err != nil { return nil, err } // Cache the result value.contents = (*T)(contents) return value.contents, nil } // Bytes returns the inner contents of the TPM2B as a byte array, not including the length field. func (value *TPM2B[T, P]) Bytes() []byte { if value.buffer != nil { return value.buffer } if value.contents == nil { return []byte{} } // Cache the result value.buffer = Marshal(*value.contents) return value.buffer } // marshal implements the tpm2.Marshallable interface. func (value TPM2B[T, P]) marshal(buf *bytes.Buffer) { b := value.Bytes() binary.Write(buf, binary.BigEndian, uint16(len(b))) buf.Write(b) } // unmarshal implements the tpm2.Unmarshallable interface. // Note: the structure contents are not validated during unmarshalling. func (value *TPM2B[T, P]) unmarshal(buf *bytes.Buffer) error { var size uint16 binary.Read(buf, binary.BigEndian, &size) value.contents = nil value.buffer = make([]byte, size) _, err := io.ReadAtLeast(buf, value.buffer, int(size)) return err } go-tpm-0.9.3/tpm2/transport/000077500000000000000000000000001473040456300156525ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/linuxtpm/000077500000000000000000000000001473040456300175325ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/linuxtpm/linuxtpm.go000066400000000000000000000014241473040456300217420ustar00rootroot00000000000000//go:build !windows // Package linuxtpm provides access to a physical TPM device via the device file. package linuxtpm import ( "errors" "fmt" "os" "github.com/google/go-tpm/tpm2/transport" ) var ( // ErrFileIsNotDevice indicates that the TPM file mode was not a device. ErrFileIsNotDevice = errors.New("TPM file is not a device") ) // Open opens the TPM device file at the given path. func Open(path string) (transport.TPMCloser, error) { fi, err := os.Stat(path) if err != nil { return nil, err } if fi.Mode()&os.ModeDevice == 0 { return nil, fmt.Errorf("%w: %s (%s)", ErrFileIsNotDevice, fi.Mode().String(), path) } var f *os.File f, err = os.OpenFile(path, os.O_RDWR, 0600) if err != nil { return nil, err } return transport.FromReadWriteCloser(f), nil } go-tpm-0.9.3/tpm2/transport/linuxtpm/linuxtpm_test.go000066400000000000000000000011341473040456300227770ustar00rootroot00000000000000//go:build !windows package linuxtpm import ( "os" "testing" "github.com/google/go-tpm/tpm2/transport" testhelper "github.com/google/go-tpm/tpm2/transport/test" ) func open(path string) func() (transport.TPMCloser, error) { return func() (transport.TPMCloser, error) { return Open(path) } } func TestLocalTPM(t *testing.T) { testhelper.RunTest(t, []error{os.ErrNotExist, os.ErrPermission, ErrFileIsNotDevice}, open("/dev/tpm0")) } func TestLocalResourceManagedTPM(t *testing.T) { testhelper.RunTest(t, []error{os.ErrNotExist, os.ErrPermission, ErrFileIsNotDevice}, open("/dev/tpmrm0")) } go-tpm-0.9.3/tpm2/transport/linuxudstpm/000077500000000000000000000000001473040456300202465ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/linuxudstpm/linuxudstpm.go000066400000000000000000000052611473040456300231750ustar00rootroot00000000000000//go:build !windows // Package linuxudstpm provides access to a TPM device via a Unix domain socket. package linuxudstpm import ( "errors" "fmt" "net" "os" "github.com/google/go-tpm/tpm2/transport" ) var ( // ErrFileIsNotSocket indicates that the TPM file is not a socket. ErrFileIsNotSocket = errors.New("TPM file is not a socket") // ErrMustCallWriteThenRead indicates that the file was not written-then-read in the expected pattern. ErrMustCallWriteThenRead = errors.New("must call Write then Read in an alternating sequence") // ErrNotOpen indicates that the TPM file is not currently open. ErrNotOpen = errors.New("no connection is open") ) // Open opens the TPM socket at the given path. func Open(path string) (transport.TPMCloser, error) { fi, err := os.Stat(path) if err != nil { return nil, err } if fi.Mode()&os.ModeSocket == 0 { return nil, fmt.Errorf("%w: %s (%s)", ErrFileIsNotSocket, fi.Mode().String(), path) } return transport.FromReadWriteCloser(newEmulatorReadWriteCloser(path)), 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 the io.Reader interface. 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, ErrMustCallWriteThenRead } n, err := erw.conn.Read(p) erw.conn.Close() erw.conn = nil return n, err } // Write implements the io.Writer interface. func (erw *emulatorReadWriteCloser) Write(p []byte) (int, error) { if erw.conn != nil { return 0, ErrMustCallWriteThenRead } var err error erw.conn, err = erw.dialer("unix", erw.path) if err != nil { return 0, err } return erw.conn.Write(p) } // Close implements the io.Closer interface. func (erw *emulatorReadWriteCloser) Close() error { if erw.conn == nil { // This is an expected possible state, e.g., if someone sent the TPM a command and didn't read the response. return nil } err := erw.conn.Close() erw.conn = nil return err } go-tpm-0.9.3/tpm2/transport/linuxudstpm/linuxudstpm_test.go000066400000000000000000000011661473040456300242340ustar00rootroot00000000000000//go:build !windows package linuxudstpm import ( "flag" "os" "syscall" "testing" "github.com/google/go-tpm/tpm2/transport" testhelper "github.com/google/go-tpm/tpm2/transport/test" ) var tpmSocket = flag.String("tpm_socket", "/dev/tpm0", "path to the TPM simulator UDS") func TestMain(m *testing.M) { flag.Parse() os.Exit(m.Run()) } func open() func() (transport.TPMCloser, error) { return func() (transport.TPMCloser, error) { return Open(*tpmSocket) } } func TestLocalUDSTPM(t *testing.T) { testhelper.RunTest(t, []error{os.ErrNotExist, os.ErrPermission, ErrFileIsNotSocket, syscall.ECONNREFUSED}, open()) } go-tpm-0.9.3/tpm2/transport/open_other.go000066400000000000000000000007561473040456300203530ustar00rootroot00000000000000//go:build !windows package transport import ( legacy "github.com/google/go-tpm/legacy/tpm2" ) // OpenTPM opens the TPM at the given path. If no path is provided, it will // attempt to use reasonable defaults. // // Deprecated: Please use the individual transport packages (e.g., // go-tpm/tpm2/transport/linuxtpm). func OpenTPM(path ...string) (TPMCloser, error) { rwc, err := legacy.OpenTPM(path...) if err != nil { return nil, err } return &wrappedRWC{ transport: rwc, }, nil } go-tpm-0.9.3/tpm2/transport/open_windows.go000066400000000000000000000006161473040456300207170ustar00rootroot00000000000000//go:build windows package transport import ( legacy "github.com/google/go-tpm/legacy/tpm2" ) // OpenTPM opens the local system TPM. // // Deprecated: Please use the individual transport packages (e.g., // go-tpm/tpm2/transport/windowstpm). func OpenTPM() (TPMCloser, error) { rwc, err := legacy.OpenTPM() if err != nil { return nil, err } return &wrappedRWC{ transport: rwc, }, nil } go-tpm-0.9.3/tpm2/transport/simulator/000077500000000000000000000000001473040456300176715ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/simulator/simulator.go000066400000000000000000000014301473040456300222350ustar00rootroot00000000000000// Package simulator provides access to a local simulator for TPM testing. package simulator import ( "io" "github.com/google/go-tpm-tools/simulator" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpmutil" ) // TPM represents a connection to a TPM simulator. type TPM struct { transport io.ReadWriteCloser } // Send implements the TPM interface. func (t *TPM) Send(input []byte) ([]byte, error) { return tpmutil.RunCommandRaw(t.transport, input) } // OpenSimulator starts and opens a TPM simulator. func OpenSimulator() (transport.TPMCloser, error) { sim, err := simulator.Get() if err != nil { return nil, err } return &TPM{ transport: sim, }, nil } // Close implements the TPM interface. func (t *TPM) Close() error { return t.transport.Close() } go-tpm-0.9.3/tpm2/transport/test/000077500000000000000000000000001473040456300166315ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/test/helper.go000066400000000000000000000030421473040456300204360ustar00rootroot00000000000000// Package testhelper provides some helper code for TPM transport tests. package testhelper import ( "bytes" "encoding/binary" "errors" "testing" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" ) // RunTest checks that the connection to the given TPM seems to be working. func RunTest(t *testing.T, skipErrs []error, tpmOpener func() (transport.TPMCloser, error)) { tpm, err := tpmOpener() for _, skipErr := range skipErrs { if errors.Is(err, skipErr) { t.Skipf("%v", err) } } if err != nil { t.Fatalf("Failed to open TPM: %v", err) } defer func(tpm transport.TPMCloser) { if err := tpm.Close(); err != nil { t.Fatalf("tpm.Close() = %v", err) } }(tpm) // Ping the TPM to ask it what the manufacturer is, as a basic consistency check. cap, err := tpm2.GetCapability{ Capability: tpm2.TPMCapTPMProperties, Property: uint32(tpm2.TPMPTManufacturer), PropertyCount: 1, }.Execute(tpm) // We might run into one of the known "skip if this error" cases. for _, skipErr := range skipErrs { if errors.Is(err, skipErr) { t.Skipf("%v", err) } } if err != nil { t.Fatalf("GetCapability() = %v", err) } props, err := cap.CapabilityData.Data.TPMProperties() if err != nil { t.Fatalf("cap.TPMProperties() = %v", err) } if len(props.TPMProperty) != 1 { t.Fatalf("GetCapability() = %v properties, want 1", len(props.TPMProperty)) } var idBuf bytes.Buffer idBuf.Grow(4) binary.Write(&idBuf, binary.BigEndian, props.TPMProperty[0].Value) t.Logf("Manufacturer ID: %q", idBuf.String()) } go-tpm-0.9.3/tpm2/transport/tpm.go000066400000000000000000000044711473040456300170070ustar00rootroot00000000000000// Package transport implements types for physically talking to TPMs. package transport import ( "io" "github.com/google/go-tpm/tpmutil" ) // TPM represents a logical connection to a TPM. type TPM interface { Send(input []byte) ([]byte, error) } // TPMCloser represents a logical connection to a TPM and you can close it. type TPMCloser interface { TPM io.Closer } // wrappedRW represents a struct that wraps an io.ReadWriter // to a transport.TPM to be compatible with tpmdirect. type wrappedRW struct { transport io.ReadWriter } // wrappedRWC represents a struct that wraps an io.ReadWriteCloser // to a transport.TPM to be compatible with tpmdirect. type wrappedRWC struct { transport io.ReadWriteCloser } // wrappedTPM represents a struct that wraps a transport.TPM's underlying // transport to use with legacy code that expects an io.ReadWriter. type wrappedTPM struct { response []byte tpm TPM } // FromReadWriter takes in a io.ReadWriter and returns a // transport.TPM wrapping the io.ReadWriter. func FromReadWriter(rw io.ReadWriter) TPM { return &wrappedRW{transport: rw} } // FromReadWriteCloser takes in a io.ReadWriteCloser and returns a // transport.TPMCloser wrapping the io.ReadWriteCloser. func FromReadWriteCloser(rwc io.ReadWriteCloser) TPMCloser { return &wrappedRWC{transport: rwc} } // ToReadWriter takes in a transport TPM and returns an // io.ReadWriter wrapping the transport TPM. func ToReadWriter(tpm TPM) io.ReadWriter { return &wrappedTPM{tpm: tpm} } // Read copies t.response into the p buffer and return the appropriate length. func (t *wrappedTPM) Read(p []byte) (int, error) { n := copy(p, t.response) t.response = t.response[n:] if len(t.response) == 0 { return n, io.EOF } return n, nil } // Write implements the io.ReadWriter interface. func (t *wrappedTPM) Write(p []byte) (n int, err error) { t.response, err = t.tpm.Send(p) if err != nil { return 0, err } return len(p), nil } // Send implements the TPM interface. func (t *wrappedRW) Send(input []byte) ([]byte, error) { return tpmutil.RunCommandRaw(t.transport, input) } // Send implements the TPM interface. func (t *wrappedRWC) Send(input []byte) ([]byte, error) { return tpmutil.RunCommandRaw(t.transport, input) } // Close implements the TPM interface. func (t *wrappedRWC) Close() error { return t.transport.Close() } go-tpm-0.9.3/tpm2/transport/windowstpm/000077500000000000000000000000001473040456300200655ustar00rootroot00000000000000go-tpm-0.9.3/tpm2/transport/windowstpm/windowstpm.go000066400000000000000000000045351473040456300226360ustar00rootroot00000000000000//go:build windows // Package windowstpm implements the TPM transport on Windows using tbs.dll. package windowstpm import ( "errors" "fmt" "io" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpmutil/tbs" ) var ( // ErrNotTPM20 indicates that a TPM 2.0 was not found. ErrNotTPM20 = errors.New("device is not a TPM 2.0") ) const ( maxTPMResponse = 4096 ) // Open opens a channel to the TPM via TBS. func Open() (transport.TPMCloser, error) { info, err := tbs.GetDeviceInfo() if err != nil { return nil, err } if info.TPMVersion != tbs.TPMVersion20 { return nil, fmt.Errorf("%w: %v", ErrNotTPM20, info.TPMVersion) } tpmContext, err := tbs.CreateContext(tbs.TPMVersion20, tbs.IncludeTPM20) rwc := &winTPMBuffer{ context: tpmContext, outBuffer: make([]byte, 0, maxTPMResponse), } return transport.FromReadWriteCloser(rwc), err } // winTPMBuffer is a ReadWriteCloser to access the TPM in Windows. type winTPMBuffer struct { context tbs.Context outBuffer []byte } // Write implements the io.Writer interface. // // 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 } // Read implements the io.Reader interface. // // 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 } // Close implements the io.Closer interface. func (rwc *winTPMBuffer) Close() error { return rwc.context.Close() } go-tpm-0.9.3/tpm2/transport/windowstpm/windowstpm_test.go000066400000000000000000000003721473040456300236700ustar00rootroot00000000000000//go:build windows package windowstpm import ( "os" "testing" testhelper "github.com/google/go-tpm/tpm2/transport/test" ) func TestLocalTPM(t *testing.T) { testhelper.RunTest(t, []error{os.ErrNotExist, os.ErrPermission, ErrNotTPM20}, Open) } go-tpm-0.9.3/tpmutil/000077500000000000000000000000001473040456300144325ustar00rootroot00000000000000go-tpm-0.9.3/tpmutil/emulator_read_write_closer_test.go000066400000000000000000000113751473040456300234330ustar00rootroot00000000000000//go: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 tpmutil import ( "fmt" "net" "testing" "time" ) // mockConn records the number of bytes that are read from it and written to it // and tracks whether or not it has been closed. type mockConn struct { network string path string open bool } // dialMockConn returns a mockConn that holds the given network and path info. func dialMockConn(network, path string) (net.Conn, error) { return &mockConn{ network: network, path: path, open: true, }, nil } // Read implements a mock version of Read. func (mc *mockConn) Read(b []byte) (int, error) { // Always read zeros into the bytes for the given length. for i := range b { b[i] = 0 } return len(b), nil } // Write implements a mock version of Write. func (mc *mockConn) Write(b []byte) (int, error) { return len(b), nil } // Close implements a mock version of Close. func (mc *mockConn) Close() error { if !mc.open { return fmt.Errorf("mockConn is already closed") } mc.open = false return nil } // LocalAddr returns nil. func (mc *mockConn) LocalAddr() net.Addr { return nil } // RemoteAddr returns nil. func (mc *mockConn) RemoteAddr() net.Addr { return nil } // SetDeadline returns nil. func (mc *mockConn) SetDeadline(_ time.Time) error { return nil } // SetReadDeadline returns nil. func (mc *mockConn) SetReadDeadline(_ time.Time) error { return nil } // SetWriteDeadline returns nil. func (mc *mockConn) SetWriteDeadline(_ time.Time) error { return nil } func newMockEmulator() *EmulatorReadWriteCloser { path := "/dev/null/fake" rwc := NewEmulatorReadWriteCloser(path) rwc.dialer = dialMockConn return rwc } var ( input = []byte(`input`) output = make([]byte, 1) ) func TestEmulatorReadWriteCloserMultipleReads(t *testing.T) { rwc := newMockEmulator() n, err := rwc.Write(input) if err != nil { t.Errorf("failed to write: %v", err) } if n != len(input) { t.Errorf("wrong number of bytes written: got %d, expected %d", n, len(input)) } n, err = rwc.Read(output) if err != nil { t.Errorf("failed to read: %v", err) } if n != len(output) { t.Errorf("wrong number of bytes read: got %d, expected %d", n, len(output)) } n, err = rwc.Write(input) if err != nil { t.Errorf("failed to write: %v", err) } if n != len(input) { t.Errorf("wrong number of bytes written: got %d, expected %d", n, len(input)) } n, err = rwc.Read(output) if err != nil { t.Errorf("failed to read: %v", err) } if n != len(output) { t.Errorf("wrong number of bytes read: got %d, expected %d", n, len(output)) } } func TestEmulatorReadWriteCloserClose(t *testing.T) { rwc := newMockEmulator() if err := rwc.Close(); err == nil { t.Errorf("incorrectly closed a connection that hadn't been opened") } if _, err := rwc.Write(input); err != nil { t.Errorf("failed to write: %v", err) } if err := rwc.Close(); err != nil { t.Errorf("failed to close an open connection: %v", err) } if err := rwc.Close(); err == nil { t.Errorf("incorrectly closed a connection that had already been closed") } } func TestEmulatorReadWriteCloseReadAfterClose(t *testing.T) { rwc := newMockEmulator() if _, err := rwc.Write(input); err != nil { t.Errorf("failed to write: %v", err) } if err := rwc.Close(); err != nil { t.Errorf("failed to close the connection: %v", err) } if _, err := rwc.Read(output); err == nil { t.Errorf("incorrectly read on a closed connection") } } func TestEmulatorReadWriteCloserReadBeforeWrite(t *testing.T) { rwc := newMockEmulator() b := make([]byte, 1) if _, err := rwc.Read(b); err == nil { t.Errorf("incorrectly read on a connection before writing") } } func TestEmulatorReadWriteCloserDoubleWrite(t *testing.T) { rwc := newMockEmulator() if _, err := rwc.Write(input); err != nil { t.Errorf("failed to write: %v", err) } if _, err := rwc.Write(input); err == nil { t.Errorf("incorrectly wrote a second time without reading in between") } } func TestEmulatorReadWriteCloserDialerError(t *testing.T) { rwc := newMockEmulator() rwc.dialer = func(_, _ string) (net.Conn, error) { return nil, fmt.Errorf("invalid") } if _, err := rwc.Write(input); err == nil { t.Errorf("incorrectly wrote when the dialer returned an error") } } go-tpm-0.9.3/tpmutil/encoding.go000066400000000000000000000150161473040456300165520ustar00rootroot00000000000000// 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 } go-tpm-0.9.3/tpmutil/encoding_test.go000066400000000000000000000232401473040456300176070ustar00rootroot00000000000000// 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" "io" "reflect" "testing" ) type invalidPacked struct { A []int B uint32 } func TestEncodingPackTypeInvalid(t *testing.T) { d := io.Discard // The packedSize function doesn't handle slices to anything other than bytes. var invalid []int if err := packType(d, invalid); err == nil { t.Fatal("The packing function incorrectly succeeds for a slice of integers") } if err := packType(d, &invalid); err == nil { t.Fatal("The packing function incorrectly succeeds for a pointer to a slice of integers") } invalid2 := invalidPacked{ A: make([]int, 10), B: 137, } if err := packType(d, invalid2); err == nil { t.Fatal("The packing function incorrectly succeeds for a struct that contains an integer slice") } if err := packType(d, &invalid2); err == nil { t.Fatal("The packing function incorrectly succeeds for a pointer to a struct that contains an integer slice") } } type simplePacked struct { A uint32 B uint32 } type nestedPacked struct { SP simplePacked C uint32 } type nestedSlice struct { A uint32 S U32Bytes } func TestEncodingPackType(t *testing.T) { buf := make([]byte, 10) inputs := []interface{}{ uint32(3), buf, &buf, simplePacked{137, 138}, nestedPacked{simplePacked{137, 138}, 139}, nestedSlice{137, buf}, []byte(nil), RawBytes(buf), } for _, i := range inputs { if err := packType(io.Discard, i); err != nil { t.Errorf("packType(%#v): %v", i, err) } } } func TestEncodingPackTypeWriteFail(t *testing.T) { u32WithOneByte := U32Bytes([]byte{1}) u32Empty := U32Bytes([]byte(nil)) tests := []struct { limit int in interface{} }{ {4, &u32WithOneByte}, {3, &u32Empty}, } for _, tt := range tests { if err := packType(&limitedDiscard{tt.limit}, tt.in); err == nil { t.Errorf("packType(%#v) with write size limit %d returned nil, want error", tt.in, tt.limit) } } } // limitedDiscard is an implementation of io.Writer that accepts a given number // of bytes before returning errors. type limitedDiscard struct { remaining int } // Write writes p to the limitedDiscard instance. func (l *limitedDiscard) Write(p []byte) (n int, err error) { n = len(p) if n > l.remaining { n = l.remaining err = io.EOF } l.remaining -= n return } func TestEncodingCommandHeaderInvalidBody(t *testing.T) { var invalid []int ch := commandHeader{1, 0, 2} _, err := packWithHeader(ch, invalid) if err == nil { t.Fatal("packWithHeader incorrectly packed a body that with an invalid int slice member") } } func TestEncodingInvalidPack(t *testing.T) { var invalid []int ch := commandHeader{1, 0, 2} _, err := packWithHeader(ch, invalid) if err == nil { t.Fatal("packWithHeader incorrectly packed a body that with an invalid int slice member") } _, err = Pack(invalid) if err == nil { t.Fatal("pack incorrectly packed a slice of int") } } func TestEncodingCommandHeaderEncoding(t *testing.T) { ch := commandHeader{1, 0, 2} var c uint32 = 137 in := c b, err := packWithHeader(ch, in) if err != nil { t.Fatal("Couldn't pack the bytes:", err) } var hdr commandHeader var size uint32 if _, err := Unpack(b, &hdr, &size); err != nil { t.Fatal("Couldn't unpack the packed bytes") } if size != 137 { t.Fatal("Got the wrong size back") } } func TestEncodingInvalidUnpack(t *testing.T) { var i *uint32 // The value ui is a serialization of uint32(0). ui := []byte{0, 0, 0, 0} uiBuf := bytes.NewBuffer(ui) if err := UnpackBuf(uiBuf, i); err == nil { t.Fatal("UnpackBuf incorrectly deserialized into a nil pointer") } var ii uint32 if err := UnpackBuf(uiBuf, ii); err == nil { t.Fatal("UnpackBuf incorrectly deserialized into a non pointer") } var b U32Bytes var empty []byte emptyBuf := bytes.NewBuffer(empty) if err := UnpackBuf(emptyBuf, &b); err == nil { t.Fatal("UnpackBuf incorrectly deserialized an empty byte array into U32Bytes") } // Try to deserialize a byte array that has a length but not enough bytes. // The slice ui represents uint32(1), which is the length of an empty byte array. ui2 := []byte{0, 0, 0, 1} uiBuf2 := bytes.NewBuffer(ui2) if err := UnpackBuf(uiBuf2, &b); err == nil { t.Fatal("UnpackBuf incorrectly deserialized a byte array that didn't have enough bytes available") } var iii []int ui3 := []byte{0, 0, 0, 1} uiBuf3 := bytes.NewBuffer(ui3) if err := UnpackBuf(uiBuf3, &iii); err == nil { t.Fatal("UnpackBuf incorrectly deserialized into a slice of ints (only byte slices are supported)") } } func TestSelfMarshaler(t *testing.T) { var empty16 U16Bytes var empty32 U32Bytes subTests := []struct { encoded []byte decoded interface{} }{ {[]byte{0, 0}, &empty16}, {[]byte{0, 1, 137}, &empty16}, {[]byte{0, 0, 0, 0}, &empty32}, {[]byte{0, 0, 0, 1, 137}, &empty32}, } for _, st := range subTests { t.Logf("Attempting to Marshal/Unmarshal %#v into %T", st.encoded, st.decoded) buffer := bytes.NewBuffer(st.encoded) if err := UnpackBuf(buffer, st.decoded); err != nil { t.Fatalf("UnpackBuf failed: %v", err) } packed, err := Pack(st.decoded) if err != nil { t.Fatalf("Pack failed: %v", err) } if !bytes.Equal(packed, st.encoded) { t.Fatalf("Pack failed: got %#v, want: %#v", packed, st.encoded) } } } func TestEncodingUnpack(t *testing.T) { // Deserialize the empty byte array. var b U32Bytes // The slice ui represents uint32(0), which is the length of an empty byte array. ui := []byte{0, 0, 0, 0} uiBuf := bytes.NewBuffer(ui) if err := UnpackBuf(uiBuf, &b); err != nil { t.Fatal("UnpackBuf failed to unpack the empty byte array") } // A byte slice of length 1 with a single entry: b[0] == 137 ui2 := []byte{0, 0, 0, 1, 137} uiBuf2 := bytes.NewBuffer(ui2) if err := UnpackBuf(uiBuf2, &b); err != nil { t.Fatal("UnpackBuf failed to unpack a byte array with a single value in it") } if !bytes.Equal([]byte(b), []byte{137}) { t.Fatal("UnpackBuf unpacked a small byte array incorrectly") } sp := simplePacked{137, 138} bsp, err := Pack(sp) if err != nil { t.Fatal("Couldn't pack a simple struct:", err) } var sp2 simplePacked if _, err := Unpack(bsp, &sp2); err != nil { t.Fatal("Couldn't unpack a simple struct:", err) } if sp.A != sp2.A || sp.B != sp2.B { t.Fatal("Unpacked simple struct didn't match the original") } // Try unpacking a version that's missing a byte at the end. if _, err := Unpack(bsp[:len(bsp)-1], &sp2); err == nil { t.Fatal("unpack incorrectly unpacked from a byte array that didn't have enough values") } np := nestedPacked{sp, 139} bnp, err := Pack(np) if err != nil { t.Fatal("Couldn't pack a nested struct") } var np2 nestedPacked if _, err := Unpack(bnp, &np2); err != nil { t.Fatal("Couldn't unpack a nested struct:", err) } if np.SP.A != np2.SP.A || np.SP.B != np2.SP.B || np.C != np2.C { t.Fatal("Unpacked nested struct didn't match the original") } ns := nestedSlice{137, b} bns, err := Pack(&ns) if err != nil { t.Fatal("Couldn't pack a struct with a nested byte slice:", err) } var ns2 nestedSlice if _, err := Unpack(bns, &ns2); err != nil { t.Fatal("Couldn't unpacked a struct with a nested slice:", err) } if ns.A != ns2.A || !bytes.Equal(ns.S, ns2.S) { t.Logf("original = %+v", ns) t.Logf("decoded = %+v", ns2) t.Fatal("Unpacked struct with nested slice didn't match the original") } var hs []Handle if _, err := Unpack([]byte{0, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, &hs); err != nil { t.Fatal("Couldn't unpack a list of Handles:", err) } if want := []Handle{0x01020304, 0x05060708, 0x090a0b0c}; !reflect.DeepEqual(want, hs) { t.Fatalf("Unpacking []Handle: got %v, want %v", hs, want) } } func TestPartialUnpack(t *testing.T) { u1, u2 := uint32(1), uint32(2) buf, err := Pack(u1, u2) if err != nil { t.Fatalf("packing uint32 value: %v", err) } var gu1, gu2 uint32 read1, err := Unpack(buf, &gu1) if err != nil { t.Fatalf("unpacking first uint32 value: %v", err) } if gu1 != u1 { t.Errorf("first unpacked value: got %d, want %d", gu1, u1) } read2, err := Unpack(buf[read1:], &gu2) if err != nil { t.Fatalf("unpacking second uint32 value: %v", err) } if gu2 != u2 { t.Errorf("second unpacked value: got %d, want %d", gu2, u2) } if read1+read2 != len(buf) { t.Errorf("sum of bytes read doesn't ad up to total packed size: got %d+%d=%d, want %d", read1, read2, read1+read2, len(buf)) } } func TestUnpackHandlesArea(t *testing.T) { buf := []byte{ 0, 2, 0, 0, 0, 1, 0, 0, 5, 57, } var out []Handle if _, err := Unpack(buf, &out); err != nil { t.Fatalf("Unpack(%v, %T) failed: %v", buf, &out, err) } if want := []Handle{1, 1337}; !reflect.DeepEqual(out, want) { t.Errorf("Unpack(%v, %T): %T = %v, want %v", buf, &out, out, out, want) } } func TestUnpackMalformedBytes(t *testing.T) { // buf is malformed because the leading size prefix is illegally large. buf := []byte{0xff, 0xff, 0xff, 0xff, 0x20} var u32 U32Bytes if _, err := Unpack(buf, &u32); err != bytes.ErrTooLarge { t.Errorf("Unpack(U32Bytes{}) returned %q, want %q", err, bytes.ErrTooLarge) } var u16 U16Bytes if _, err := Unpack(buf, &u16); err != io.ErrUnexpectedEOF { t.Errorf("Unpack(U16Bytes{}) returned %q, want %q", err, io.ErrUnexpectedEOF) } } go-tpm-0.9.3/tpmutil/mssim/000077500000000000000000000000001473040456300155625ustar00rootroot00000000000000go-tpm-0.9.3/tpmutil/mssim/mssim.go000066400000000000000000000136031473040456300172440ustar00rootroot00000000000000// 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 mssim implements the Microsoft simulator TPM2 Transmission Interface // // The Microsoft simulator TPM Command Transmission Interface (TCTI) is a // remote procedure interface donated to the TPM2 Specification by Microsoft. // Its primary implementation is the tpm_server maintained by IBM. // // https://sourceforge.net/projects/ibmswtpm2/ // // This package implements client code to communicate with server code described // in the document "TPM2 Specification Part 4: Supporting Routines – Code" // // https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-4-Supporting-Routines-01.38-code.pdf package mssim import ( "bytes" "encoding/binary" "fmt" "io" "net" ) // Constants defined in "D.3.2. Typedefs and Defines" const ( tpmSignalPowerOn uint32 = 1 tpmSignalPowerOff uint32 = 2 tpmSendCommand uint32 = 8 tpmSignalNVOn uint32 = 11 tpmSessionEnd uint32 = 20 ) // Config holds configuration parameters for connecting to the simulator. type Config struct { // Addresses of the command and platform handlers. // // Defaults to port 2321 and 2322 on localhost. CommandAddress string PlatformAddress string } // Open creates connections to the simulator's command and platform ports and // power cycles the simulator to initialize it. func Open(config Config) (*Conn, error) { cmdAddr := config.CommandAddress if cmdAddr == "" { cmdAddr = "127.0.0.1:2321" } platformAddr := config.PlatformAddress if platformAddr == "" { platformAddr = "127.0.0.1:2322" } conn, err := net.Dial("tcp", platformAddr) if err != nil { return nil, fmt.Errorf("dial platform address: %v", err) } defer conn.Close() // Startup the simulator. This order of commands copies IBM's TPM2 tool's // "powerup" command line tool and will reset the simulator. // // https://sourceforge.net/projects/ibmtpm20tss/ if err := sendPlatformCommand(conn, tpmSignalPowerOff); err != nil { return nil, fmt.Errorf("power off platform command failed: %v", err) } if err := sendPlatformCommand(conn, tpmSignalPowerOn); err != nil { return nil, fmt.Errorf("power on platform command failed: %v", err) } if err := sendPlatformCommand(conn, tpmSignalNVOn); err != nil { return nil, fmt.Errorf("nv on platform command failed: %v", err) } // Gracefully close the connection. if err := binary.Write(conn, binary.BigEndian, tpmSessionEnd); err != nil { return nil, fmt.Errorf("shutdown platform connection failed: %v", err) } cmdConn, err := net.Dial("tcp", cmdAddr) if err != nil { return nil, fmt.Errorf("dial command address: %v", err) } return &Conn{conn: cmdConn}, nil } // sendPlatformCommand sends device management commands to the simulator. // // See: "D.4.3.2. PlatformServer()" func sendPlatformCommand(conn net.Conn, u uint32) error { if err := binary.Write(conn, binary.BigEndian, u); err != nil { return fmt.Errorf("write platform command: %v", err) } var rc uint32 if err := binary.Read(conn, binary.BigEndian, &rc); err != nil { return fmt.Errorf("read platform command: %v", err) } if rc != 0 { return fmt.Errorf("unexpected platform command response code: 0x%x", rc) } return nil } // Conn is a Microsoft Simulator client that can be used as a connection for the // tpm2 package. type Conn struct { // Cached connection conn net.Conn // Response bytes left over from the previous read. prevRead *bytes.Reader } // Read a response from the simulator. If the response is longer than the provided // buffer, the remainder will be cached for the next read. func (c *Conn) Read(b []byte) (int, error) { if c.prevRead != nil && c.prevRead.Len() > 0 { return c.prevRead.Read(b) } // Response frame: // - uint32 (size of response) // - []byte (response) // - uint32 (always 0) var respLen uint32 if err := binary.Read(c.conn, binary.BigEndian, &respLen); err != nil { return 0, fmt.Errorf("read MS simulator response header: %v", err) } resp := make([]byte, int(respLen)) if _, err := io.ReadFull(c.conn, resp[:]); err != nil { return 0, fmt.Errorf("read MS simulator response: %v", err) } var rc uint32 if err := binary.Read(c.conn, binary.BigEndian, &rc); err != nil { return 0, fmt.Errorf("read MS simulator return code: %v", err) } if rc != 0 { return 0, fmt.Errorf("MS simulator returned invalid return code: 0x%x", rc) } c.prevRead = bytes.NewReader(resp) return c.prevRead.Read(b) } // Write a raw command to the simulator. Commands must be written in a single call // to Write. Commands split over multiple calls will result in multiple framed // requests. func (c *Conn) Write(b []byte) (int, error) { // See: D.4.3.12. TpmServer() buff := &bytes.Buffer{} // "send command" flag binary.Write(buff, binary.BigEndian, tpmSendCommand) // locality 0 buff.WriteByte(0) // size of the command binary.Write(buff, binary.BigEndian, uint32(len(b))) // raw command buff.Write(b) if _, err := buff.WriteTo(c.conn); err != nil { return 0, fmt.Errorf("write MS simulator command: %v", err) } return len(b), nil } // Close closes any outgoing connections to the TPM simulator. func (c *Conn) Close() error { // See: D.4.3.12. TpmServer() // Gracefully close the connection. if err := binary.Write(c.conn, binary.BigEndian, tpmSessionEnd); err != nil { c.conn.Close() return fmt.Errorf("shutdown platform connection failed: %v", err) } return c.conn.Close() } go-tpm-0.9.3/tpmutil/poll_other.go000066400000000000000000000002131473040456300171240ustar00rootroot00000000000000//go:build !linux && !darwin package tpmutil import ( "os" ) // Not implemented on Windows. func poll(_ *os.File) error { return nil } go-tpm-0.9.3/tpmutil/poll_unix.go000066400000000000000000000011721473040456300167730ustar00rootroot00000000000000//go: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 } go-tpm-0.9.3/tpmutil/poll_unix_test.go000066400000000000000000000007561473040456300200410ustar00rootroot00000000000000//go:build linux || darwin package tpmutil import ( "os" "testing" ) func TestPoll(t *testing.T) { r, w, err := os.Pipe() if err != nil { t.Fatal(err) } defer r.Close() defer w.Close() if _, err := w.Write([]byte("hi")); err != nil { t.Fatalf("error writing to pipe: %v", err) } if err := poll(r); err != nil { t.Errorf("error polling reader side of the pipe: %v", err) } if err := r.Close(); err != nil { t.Fatalf("error closing reader side of the pipe: %v", err) } } go-tpm-0.9.3/tpmutil/run.go000066400000000000000000000057611473040456300155760ustar00rootroot00000000000000// 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" "time" ) // 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 // RunCommandRaw executes the given raw command and returns the raw response. // Does not check the response code except to execute retry logic. func RunCommandRaw(rw io.ReadWriter, inb []byte) ([]byte, error) { if rw == nil { return nil, errors.New("nil TPM handle") } // f(t) = (2^t)ms, up to 2s var backoffFac uint var rh responseHeader var outb []byte for { if _, err := rw.Write(inb); err != nil { return nil, err } // If the TPM is a real device, it may not be ready for reading // immediately after writing the command. Wait until the file // descriptor is ready to be read from. if f, ok := rw.(*os.File); ok { if err := poll(f); err != nil { return nil, err } } outb = make([]byte, maxTPMResponse) outlen, err := rw.Read(outb) if err != nil { return nil, err } // Resize the buffer to match the amount read from the TPM. outb = outb[:outlen] _, err = Unpack(outb, &rh) if err != nil { return nil, err } // If TPM is busy, retry the command after waiting a few ms. if rh.Res == RCRetry { if backoffFac < 11 { dur := (1 << backoffFac) * time.Millisecond time.Sleep(dur) backoffFac++ } else { return nil, err } } else { break } } return outb, nil } // RunCommand executes cmd with given tag and arguments. Returns TPM response // body (without response header) and response code from the header. Returned // error may be nil if response code is not RCSuccess; caller should check // both. func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]byte, ResponseCode, error) { inb, err := packWithHeader(commandHeader{tag, 0, cmd}, in...) if err != nil { return nil, 0, err } outb, err := RunCommandRaw(rw, inb) if err != nil { return nil, 0, err } var rh responseHeader read, err := Unpack(outb, &rh) if err != nil { return nil, 0, err } if rh.Res != RCSuccess { return nil, rh.Res, nil } return outb[read:], rh.Res, nil } go-tpm-0.9.3/tpmutil/run_other.go000066400000000000000000000063551473040456300167770ustar00rootroot00000000000000//go: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 } go-tpm-0.9.3/tpmutil/run_windows.go000066400000000000000000000052111473040456300173360ustar00rootroot00000000000000// 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), } } go-tpm-0.9.3/tpmutil/structures.go000066400000000000000000000113051473040456300172040ustar00rootroot00000000000000// 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 uint32 = 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 := len([]byte(*b)) if err := binary.Write(out, binary.BigEndian, uint16(size)); err != nil { return err } n, err := out.Write(*b) if err != nil { return err } if n != 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 := len([]byte(*b)) if err := binary.Write(out, binary.BigEndian, uint32(size)); err != nil { return err } n, err := out.Write(*b) if err != nil { return err } if n != 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 } if tmpSize > maxBytesBufferSize { return bytes.ErrTooLarge } // We can now safely cast to an int on 32-bit or 64-bit machines 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 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 // RCRetry is response code for TPM is busy. const RCRetry ResponseCode = 0x922 // 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 // HandleValue returns the handle value. This behavior is intended to satisfy // an interface that can be implemented by other, more complex types as well. func (h Handle) HandleValue() uint32 { return uint32(h) } type handleList []Handle func (l *handleList) TPMMarshal(_ 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 } go-tpm-0.9.3/tpmutil/tbs/000077500000000000000000000000001473040456300152225ustar00rootroot00000000000000go-tpm-0.9.3/tpmutil/tbs/tbs_windows.go000066400000000000000000000242701473040456300201200ustar00rootroot00000000000000// 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 versions 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]))) } // DeviceInfo is TPM_DEVICE_INFO from tbs.h type DeviceInfo struct { StructVersion uint32 TPMVersion Version TPMInterfaceType uint32 TPMImpRevision uint32 } // GetDeviceInfo gets the DeviceInfo of the current TPM: // https://docs.microsoft.com/en-us/windows/win32/api/tbs/nf-tbs-tbsi_getdeviceinfo func GetDeviceInfo() (*DeviceInfo, error) { info := DeviceInfo{} // TBS_RESULT Tbsi_GetDeviceInfo( // UINT32 Size, // PVOID Info // ); if err := tbsGetDeviceInfo.Find(); err != nil { return nil, err } 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 // ); if err := tbsCreateContext.Find(); err != nil { return context, err } 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 // ); if err := tbsContextClose.Find(); err != nil { return err } 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 // ); if err := tbsSubmitCommand.Find(); err != nil { return 0, err } 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 // ); if err := tbsGetTCGLog.Find(); err != nil { return 0, err } result, _, _ := tbsGetTCGLog.Call( uintptr(context), sliceAddress(logBuffer), uintptr(unsafe.Pointer(&logBufferLen)), ) return logBufferLen, getError(result) } go-tpm-0.9.3/tpmutil/tbs/tbs_windows_test.go000066400000000000000000000061231473040456300211540ustar00rootroot00000000000000package tbs import ( "bytes" "os" "testing" ) // Encodes a call to Getrandom() with a buffer of length zero, making this // command an effective no-op. var getRandomRawCommand = []byte{128, 1, 0, 0, 0, 12, 0, 0, 1, 123, 0, 0} func getContext(t *testing.T) Context { ctx, err := CreateContext(TPMVersion20, IncludeTPM12|IncludeTPM20) if err != nil { t.Skipf("Skipping test as we couldn't access the TPM: %v", err) } return ctx } // Get the log by passing in progressively larger buffers func TestGetLogLargeBuffer(t *testing.T) { ctx := getContext(t) defer ctx.Close() log := make([]byte, os.Getpagesize()) for { logLen, err := ctx.GetTCGLog(log) if err == nil { if logLen == 0 { t.Fatal("Expected positive TCGLog length") } return } if err != ErrInsufficientBuffer { t.Fatalf("GetTCGLog failed: %v", err) } log = make([]byte, 2*len(log)) } } // Get the log by passing in nil, checking the size, and then getting the log. func TestGetLogWithNilSlice(t *testing.T) { ctx := getContext(t) defer ctx.Close() logLen, err := ctx.GetTCGLog(nil) if err != nil { t.Fatalf("First GetTCGLog failed: %v", err) } if logLen == 0 { t.Fatal("Expected positive TCGLog length") } log := make([]byte, logLen) if _, err := ctx.GetTCGLog(log); err != nil { t.Fatalf("Second GetTCGLog failed: %v", err) } } // SubmitCommand can handle a nil command buffer. func TestSubmitCommandNilCommand(t *testing.T) { ctx := getContext(t) defer ctx.Close() rawResponse := make([]byte, os.Getpagesize()) _, err := ctx.SubmitCommand(NormalPriority, nil, rawResponse) if err != ErrBadParameter { t.Fatalf("SubmitCommand failed with %v: expected ErrBadParameter", err) } } // SubmitCommand can handle a nil response buffer. func TestSubmitCommandNilResponse(t *testing.T) { ctx := getContext(t) defer ctx.Close() _, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, nil) if err != ErrInvalidOutputPointer { t.Fatalf("SubmitCommand failed with %v: expected ErrInvalidOutputPointer", err) } } // SubmitCommand can handle a response buffer that is shorter than necessary. func TestSubmitCommandShortResponse(t *testing.T) { ctx := getContext(t) defer ctx.Close() rawResponse := make([]byte, 1) _, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, rawResponse) if err != ErrInsufficientBuffer { t.Fatalf("SubmitCommand failed with %v: expected ErrInsufficientBuffer", err) } } // SubmitCommand can handle a response buffer that is longer than necessary. func TestSubmitCommandLongResponse(t *testing.T) { ctx := getContext(t) defer ctx.Close() rawResponse := make([]byte, os.Getpagesize()) responseLen, err := ctx.SubmitCommand(NormalPriority, getRandomRawCommand, rawResponse) if err != nil { t.Fatalf("SubmitCommand failed: %v", err) } rawResponse = rawResponse[:responseLen] // Expected response buffer for getRandomRawCommand expectedGetRandomRawResponse := []byte{128, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0} if !bytes.Equal(rawResponse, expectedGetRandomRawResponse) { t.Fatalf("Got response of %v, expected %v", rawResponse, expectedGetRandomRawResponse) } }