-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce Direct TPM2 API #266
Conversation
This commit introduces a new method of interacting with a TPM 2.0. Instead of plumbing each TPM API into one or more Go functions, this code defines structures for every TPM 2.0 command request and response. These map 1:1 with the actual command parameters comprehended by the TPM, so any invocation of any command is possible (once all the command structures are written). This commit introduces enough of the TPM 2.0 API surface to put together some interesting end-to-end tests, mostly around sealing. Another objective of the Direct API is to facilitate use of the TPM's session-based command transport features (e.g., audit and encryption sessions). See the test code for examples of how to easily use these APIs to, e.g., set up an EK-salted session for session-encrypted unseal. Change-Id: I1549dd596869d79ddd41ff3c5f9ffdadc9628ed4
tpm2/direct/structures.go
Outdated
// SET (1): an algorithm that may be used as an object type | ||
// CLEAR (0): an algorithm that is not used as an object type | ||
Object bool `gotpm:"bit=3"` | ||
Reserved1 uint8 `gotpm:"bit=7:4"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Open question: should we omit this from the definition, and check that reserved bits are all zeros?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved with @josephlr's suggestion to embed an unexported type whose job it is to store reserved bits.
* WIP: put stuff into subpackages to make the names nice * use internal monolithic package to avoid cycles * complete the tpm2 package * fix test references to renamed types * switch to command.Execute pattern * fix some unkeyed field values detected by go vet * stop panicking from Hash
@alexmwu, @josephlr, @jkl73: I've refactored this code quite a bit according to our discussion a couple of weeks ago. I've not yet resolved the issue of representing reserved bits in the struct in a future-compatible way, because I'm still thinking about how to do that. However I don't think that blocks you all from reviewing the code now. |
For handling Bit-fields I think we can have a nice solution for forwards/backwards compatibility: We have private struct members func GetBit(g Getter, pos int) bool
func SetBit(s Setter, pos int, val bool) Where something like Our struct would look like: type Algorithm struct {
reserved32
Asymmetric bool `gotpm:"bit=0"`
Symmetric bool `gotpm:"bit=1"`
Hash bool `gotpm:"bit=2"`
Object bool `gotpm:"bit=3"`
Signing bool `gotpm:"bit=8"`
Encrypting bool `gotpm:"bit=9"`
Method bool `gotpm:"bit=10"`
} Example test: func TestSetBit(t *testing.T) {
a := Algorithm{Object: true, Symmetric: true}
SetBit(&a, 0, true)
SetBit(&a, 3, false)
SetBit(&a, 11, true)
if !a.Asymmetric {
t.Error("Asymmetric should be set")
}
if a.Object {
t.Error("Object should be unset")
}
if !GetBit(a, 11) {
t.Error("Bit 11 should be set")
}
if GetBit(a, 12) {
t.Error("Bit 12 should be unset")
}
if !GetBit(a, 1) {
t.Error("Bit 1 (Symmetric) should be set")
}
} See https://go.dev/play/p/qwMzWqHgWj_k for the full implementation |
I like this solution very much, thank you! I'll implement this change here and let you know if I run into any unforeseen problems, but I think your prototype is pretty complete. |
I've prepared a draft of @josephlr's awesome suggestion - it works great. The embedded unexported field allows us to put these "SetBit" and "GitBit" methods onto the tpma types that completely hides that problem from the user. I still need to clean up the code a bit more and address some of the other comments. |
Thanks for your patience, everyone. I've made the cleanups suggested by @josephlr's last round of review. In prototyping some other projects that use this library, I noticed a very inconvenient pattern: nvw := tpm2.SomePolicyCommand{
PolicySession: tpm2.Handle{Handle: sess.Handle()},
}
... The word nvw := tpm2.SomePolicyCommand{
PolicySession: sess.Handle(),
}
... Under the hood,
Two helper types are provided for when callers want to provide a nontrivial (i.e., not just the handle value) Name: |
For these reasons:
I will go ahead and merge this to |
* Introduce Direct TPM2 API This commit introduces a new method of interacting with a TPM 2.0. Instead of plumbing each TPM API into one or more Go functions, this code defines structures for every TPM 2.0 command request and response. These map 1:1 with the actual command parameters comprehended by the TPM, so any invocation of any command is possible (once all the command structures are written). This commit introduces enough of the TPM 2.0 API surface to put together some interesting end-to-end tests, mostly around sealing. Another objective of the Direct API is to facilitate use of the TPM's session-based command transport features (e.g., audit and encryption sessions). See the test code for examples of how to easily use these APIs to, e.g., set up an EK-salted session for session-encrypted unseal. Change-Id: I1549dd596869d79ddd41ff3c5f9ffdadc9628ed4 * fix problems identified by go vet * fix some more issues identified by go vet * fix some more issues surfaced by vet * fix more go vet issues * one last round of go vet fixes * Use subpackages and put the Execute function on the command types (#1) * WIP: put stuff into subpackages to make the names nice * use internal monolithic package to avoid cycles * complete the tpm2 package * fix test references to renamed types * switch to command.Execute pattern * fix some unkeyed field values detected by go vet * stop panicking from Hash * Draft implementation of Joe's embedded-reserved-field bitwise solution * Turn command handles into an interface to avoid caller stuttering
What is currently missing from getting this merged into master? |
Not much, sorry for the delays. We are very close though. We plan to merge tpmdirect to main and release it as go-tpm 0.9.0 very soon as a preview for 1.0.0 (in case there is last minute feedback on the design) |
Awesome :) Thanks for the work! |
Apologies that this is a humongous code review. I thought about splitting up the commits, but that would not decrease the total review work. One PR makes it easier to understand how the "big dumb" part of the code (structures.go, commands.go) are designed to support the "small smart" part of the code (sessions.go, reflect.go).
This PR introduces a new method of interacting with a TPM 2.0.
Instead of plumbing each TPM API into one or more Go functions, this
code defines structures for every TPM 2.0 command request and response.
These map 1:1 with the actual command parameters comprehended by the
TPM, so any invocation of any command is possible (once all the command
structures are written).
This PR introduces enough of the TPM 2.0 API surface to put together
some interesting end-to-end tests, mostly around sealing.
Another objective of the Direct API is to facilitate use of the TPM's
session-based command transport features (e.g., audit and encryption
sessions). See the test code for examples of how to easily use these
APIs to, e.g., set up an EK-salted session for session-encrypted unseal.
An excerpt of how this API supports advanced interactions with the TPM, based on sealing_test.go:
Fixes #259