[RFC] tools: add tpm2_policy tool for invoking libpolicy
Create a tpm2_policy tool that can read the FAPI JSON style policies and:
-
Instantiate them -> This process fills in anything missing in the template. TODO: How does this get handled, do we need to tweak any of the callbacks?
-
Calculate them -> This process produces a list of hashes... TODO: Why? Is this a list of all the subordinate policies or can the json file have N policies where N > 1?
-
Execute them -> Execute the policy on a session. TODO: Who is supposed to start the policy session as it seems to be 0?
Their are a lot of TODO items in this code. I'm looking for how we want to use this, different tools, like tpm2_policyinit tpm2_policycalc and tpm2_policyexec? Or an all in-one tool. Currently note that ONLY the Execute needs an ESYS context, but instantiate should be filling stuff in so it likely needs a context or the callbacks handled?
Signed-off-by: William Roberts [email protected]
Wondering if this can be part of tpm2_createpolicy tool. There too we take an input to specify policy type. Except we rely on the TPM trial session to calculate the policy -- So that needs to change and also need to add the json parser to iterate through the policies.
What were the thoughts on executing policies? Why do we need this? I think we should limit the extent of this tool to calculate policies. It can quickly get complicated when the policy engine needs to interleave to record parameters set by other commands/ tools.
The tpm2_* commands need to not have any dependency on data files not named on the command-line. If using FAPI means having to have files in /etc, that won't work for us.
My preference would be something like this:
- get policyDigest:
tpm2 policyexec --trial -L policy-digest-file policy-file.json - execute policy:
tpm2 policyexec --session session.ctx policy-file.json
A way to specify files from which to get passwords for any passwords that need prompting might be useful. I would use a JSON file for that too, or I would provide an option to set the passwords right in the policy file (which can be done with jq or whatever).
I recommend leaving a policy creation CLI for last. Alternation (TPM2_PolicyOr()) is bound to make a policy creation command-line tool tricky. But if you want some ideas:
- add a
--policy-json FILEoption to existingtpm2_policy*commands that alter the policy to add to it an entry for the current command - make
tpm2_policyorw/--policy-json FILEoption make it so all the alternatives must be specified as JSON files and then write a single entry to the output JSON file (which must not exist already) with all the alternatives.
I believe you'll find that for commands like tpm2_policysigned and so on you'll need to add options for specifying priv/pub/PEM files that should be loaded at policy evaluation time and which then need to have their contents included in the JSON. Support for objects that should be loaded with TPM2_Load() and TPM2_LoadExternal() should be included.
As for FAPI, I think the context setup function takes a URI string value that currently has to be NULL. If you made this support passing in a directory, that would help make it more usable. Basically, there is no reason to expect N different TPM-using applications on a system to need to see one DB of extra metadata. Nor is there a reason to expect only one TPM to be used, especially with remote TPMs and encrypted sessions (though even for local TPMs we need encrypted sessions). The world of applications using a given TPM, and the world of objects that can exist transiently on that TPM , are unbounded, and different subsets can be distinct.
This is why I like the tpm2_* tools so much: the only abstraction is the automatic loading and saving of sessions and objects, leaving us users with an extremely thin abstraction on TPM 2.0 commands, which means just the TCG TPM 2.0 library parts 1 and 3 are enough to guide one to create complex policies and applications.
The
tpm2_*commands need to not have any dependency on data files not named on the command-line. If using FAPI means having to have files in/etc, that won't work for us.
I want to make this very clear: this is not using FAPI. What it's doing is ripping out the policy engine part of FAPI to make it a consumable library. You pass it the policy file, and eventually through callbacks into the tool for things like passwords will happen. So I think you could program it with expect or we could come up with another interface. This will handle all the complexities of policies.
Ill have to read through the rest of comments and think about this more.
I want to make this very clear: this is not using FAPI.
Got it. Thanks.
BTW, I do like the idea of policies expressible in JSON, but it's not a very human readable language.
The language I've been play-thinking about looks like this:
declare command $signature: curl ...
declare prompt $pw: enter password:
declare PEM signer:
--BEGIN PUBLIC KEY--
...
--END PUBLIC KEY--
or:
( secret --load-private some-priv-file --load-public some-pub-file --authvalue $pw )
( signed --nonce-tpm --signer $signer --signature $signature --policyref "...")
The idea is that each policy command is almost like a shell command, but with the ability to reference various data, some defined inline, some obtained by prompting, some obtained by running some external command. Conjunction would be done using ; between commands. Alternation would be as shown above.
I've got an implementation of a significantly simpler subset, written in bash. Bash is not a good language for this. But bash + jq is a decent combination for implementing an evaluator for policies expressed in JSON.
FWIW: https://github.com/osresearch/safeboot/pull/144
This is an exploration of what I'd like in a policy language. Yes, it's a prototype written in Bash (because it's in Safeboot, which is Bash).
Things I definitely want:
- to use artifacts such as public keys of signers for
TPM2_PolicySigned()andTPM2_PolicyAuthorize(),policyRefvalues, PCR values, etc., and:- to allow the caller to provide some such artifacts (in my prototype the caller of
sbin/tpm2-policycan provide these) - to allow the policy to include in it some such artifacts (in my prototype the policy script can embed these as here documents)
- to allow the caller to provide some such artifacts (in my prototype the caller of
- to be able to construct artifacts during the execution of the policy (e.g., names/handles/saved contexts for keys imported and loaded during execution of the policy) (in my prototype the script can create artifacts in the current directory only, and can use artifacts only from it too, and the current directory is a temporary directory)
- to refer to external sub-policies (e.g., ones authorized by
TPM2_PolicyAuthorize()- possibly to download them as necessary
- to prompt for
authValues (passwords) - support for
TPM2_PolicyOr(), of course - some PKIX support: for validating certificates for policy signers/authorizers, for example
I don't need looping. Conditionals are mostly interesting for reasons to do with TPM2_PolicyOr().
Safeboot.dev PR #144 isn't ready, and may well never finish, but it has much of the above and could easily be extended to do what it doesn't already.
An epiphany I had recently is that for most TPM use cases there is one or maybe two commands that one wishes to execute with some saved key(s) that might have policies associated with them that might use other keys that have policies associated with them, and so on. It's not just TPM 2.0 EA policy that needs a language, but that even a sequence of desired commands, each with various parameters, needs a language.
Given such a language it should be possible to have a trivial API/CLI to execute the desired commands and all the possibly many commands impliedly necessary.
The only things the user should have to furnish are:
- the description of the command(s) desired
- values for any parameters required by those commands' objects/policies
- these could be prompted for when not given explicitly
Then using TPMs could be very very easy.
One thing I think we need to never lose track of is the need to as much as possible always use a tpmKey for TPM2_StartAuthSession so that all sensitive parameters can be encrypted and so that all command / response parameters can be integrity protected via HMAC sessions. Obviously some commands must sometimes be executed outside the protection of HMAC sessions, like TPM2_CreatePrimary to "create" the tpmKey objects, but it should always be possible to check that the created primary's public key matches expectations (i.e., previously enrolled EKs, etc.). But in general it should be possible to authenticate the TPM to the user and to secure the communications with the TPM so that even if there is a local attacker -or even if the TPM is remote over a public network- the execution of the desired commands is secure.
An epiphany I had recently is that for most TPM use cases there is one or maybe two commands that one wishes to execute with some saved key(s) that might have policies associated with them that might use other keys that have policies associated with them, and so on. It's not just TPM 2.0 EA policy that needs a language, but that even a sequence of desired commands, each with various parameters, needs a language.
That sounds suspiciously like a programming language. The proposal would be some language to instruct which commands the host processor should send to the TPM.
Given such a language it should be possible to have a trivial API/CLI to execute the desired commands and all the possibly many commands impliedly necessary.
The only things the user should have to furnish are:
the description of the command(s) desired
values for any parameters required by those commands' objects/policies
- these could be prompted for when not given explicitly
Then using TPMs could be very very easy.
Yes a meta language could be useful, but I don't see it making the TPM 2.0 easier to use. the CLI tools currently imply and figure out a lot of things based on what the user passes for options. The Python API is simpler compared to the C API as well. The TPM is hard to use becuase it's wildly complex with lots of corner cases. The OpenSSL engine/provider and PKCS11 all provide a mechanism to use the TPM without really knowing how to use the TPM. Granted they only solve the crypto cases really.
One thing I think we need to never lose track of is the need to as much as possible always use a
tpmKeyfor
Bind key can be just as valid and since it's started off of a secret theirs no need to verify it. The secret is never sent over the wire as well.
TPM2_StartAuthSessionso that all sensitive parameters can be encrypted and so that all command / response parameters can be integrity protected via HMAC sessions. Obviously some commands must sometimes be executed outside the protection of HMAC sessions, likeTPM2_CreatePrimaryto "create" thetpmKeyobjects, but it should always be possible to check that the created primary's public key matches expectations (i.e., previously enrolled EKs, etc.). But in general it should be possible to authenticate the TPM to the user and to secure the communications with the TPM so that even if there is a local attacker -or even if the TPM is remote over a public network- the execution of the desired commands is secure.
Their was discussions on making a TCTI that modifies the commands being set to include session encryption and protections. This was users just get support without needing to worry about anything. FAPI does this as well for users.
That sounds suspiciously like a programming language. The proposal would be some language to instruct which commands the host processor should send to the TPM.
There's no need for conditionals, loops, subroutines, lambdas, or variables, so it wouldn't be a programming language. But it would be a language, yes, or a schema for JSON (or XML, or YAML, or...).
In JSON imagine something like:
{
"command":"TPM2_Sign",
"keypriv":"...",
"keypub":"...",
"keypolicy":{"key policy description here":"..."}
}
or maybe
{
"command":"TPM2_Sign",
"key":"/path/to/key/description.json"
}
with the key descriptor having the key private/public/context parts and the policy descriptor.
If the key has an authValue and userWithAuth then the processor would have to prompt for that (or the user would have to provide it up front). Maybe we need a label for the prompt. Maybe we need a name for the key, so maybe all we need is a command and the name of a key, then you can find the key descriptor in some search path.
The policy descriptor itself might reference other keys. For example, a TPM2_PolicySigned, which might come with minimal instructions for how to get the signature (e.g., use a smartcard, or POST to some URI [possibly requiring authentication], etc.). Or TPM2_PolicySecret referencing a second key so that the user has to provide two passwords.
I believe a schema general enough for this is feasible.
The OpenSSL engine/provider and PKCS11 all provide a mechanism to use the TPM without really knowing how to use the TPM.
That's a whole other topic for discussion. There is an enormous impedance mismatch between PKCS#11 and TPM 2.0 called policies. The only way to use a key with a complex policy through PKCS#11 would be if the only thing to prompt for is a password so that that can be the PKCS#11 PIN, and any other interactions would have to be out of band (like modal dialogs in a GUI display). Also, the tpm2-pkcs11 provider only supports keys created via it, and that makes it very difficult to do things like: create keys with some duplication policy, or import duplicated or external keys.
Bind key can be just as valid and since it's started off of a secret theirs no need to verify it. The secret is never sent over the wire as well.
It's very dangerous to use a binKey and not a tpmKey: if the secret has low entropy then an eavesdropper can mount an offline dictionary attack.
Their was discussions on making a TCTI that modifies the commands being set to include session encryption and protections. This was users just get support without needing to worry about anything. FAPI does this as well for users.
How could that possibly work if the app uses a tpmKey? I don't think it can work. The TCTI layer is too low in the stack for this.
Yes a meta language could be useful, but I don't see it making the TPM 2.0 easier to use. the CLI tools currently imply and figure out a lot of things based on what the user passes for options.
Yes, the CLI tools are quite smart, but once you add a policy language (which is what this issue is about), you'll need to make sure that you have enough metadata about keys referenced by policies that the policy executor can be as smart as the utilities.
That sounds suspiciously like a programming language. The proposal would be some language to instruct which commands the host processor should send to the TPM.
There's no need for conditionals, loops, subroutines, lambdas, or variables, so it wouldn't be a programming language. But it would be a language, yes, or a schema for JSON (or XML, or YAML, or...).
In JSON imagine something like:
{ "command":"TPM2_Sign", "keypriv":"...", "keypub":"...", "keypolicy":{"key policy description here":"..."} }or maybe
{ "command":"TPM2_Sign", "key":"/path/to/key/description.json" }with the key descriptor having the key private/public/context parts and the policy descriptor.
If the key has an
authValueanduserWithAuththen the processor would have to prompt for that (or the user would have to provide it up front). Maybe we need a label for the prompt. Maybe we need a name for the key, so maybe all we need is a command and the name of a key, then you can find the key descriptor in some search path.The policy descriptor itself might reference other keys. For example, a
TPM2_PolicySigned, which might come with minimal instructions for how to get the signature (e.g., use a smartcard, orPOSTto some URI [possibly requiring authentication], etc.). OrTPM2_PolicySecretreferencing a second key so that the user has to provide two passwords.I believe a schema general enough for this is feasible.
It would be very easy to build something with pytss
The OpenSSL engine/provider and PKCS11 all provide a mechanism to use the TPM without really knowing how to use the TPM.
That's a whole other topic for discussion. There is an enormous impedance mismatch between PKCS#11 and TPM 2.0 called policies. The only way to use a key with a complex policy through PKCS#11 would be if the only thing to prompt for is a password so that that can be the PKCS#11 PIN, and any other interactions would have to be out of band (like modal dialogs in a GUI display).
I mean yes, but that's just PKCS11 and it has a provision that allows this in the spec. Some smartcards or readers have the pin pad or fingerprint readers, etc. It would all be out of band, but doable. Add a field for the key that contains the JSON policy and execute it, setting the callbacks to some out of band mechanism.
Also, the tpm2-pkcs11 provider only supports keys created via it, and that makes it very difficult to do things like: create keys with some duplication policy, or import duplicated or external keys.
That's not true, you can link in other keys from tpm2-tools or openssl engine/provider. You can even import raw keys. As long as the auth for the key is a simple password, it should just work.
Bind key can be just as valid and since it's started off of a secret theirs no need to verify it. The secret is never sent over the wire as well.
It's very dangerous to use a
binKeyand not atpmKey: if the secret has low entropy then an eavesdropper can mount an offline dictionary attack.
If one it relying on bindKey, the auth shouldn't be trivial. That's on the user, so while it can be dangerous, theirs a lot of dangers in crypto.
Also, the tpm2-pkcs11 provider only supports keys created via it, and that makes it very difficult to do things like: create keys with some duplication policy, or import duplicated or external keys.
That's not true, you can link in other keys from tpm2-tools or openssl engine/provider. You can even import raw keys. As long as the auth for the key is a simple password, it should just work.
Ah excellent. Sorry I got that wrong!
Yes a meta language could be useful, but I don't see it making the TPM 2.0 easier to use. the CLI tools currently imply and figure out a lot of things based on what the user passes for options.
Yes, the CLI tools are quite smart, but once you add a policy language (which is what this issue is about), you'll need to make sure that you have enough metadata about keys referenced by policies that the policy executor can be as smart as the utilities.
FAPI tss2 tools bind the policy to the key, while the tpm2-tools don't with the policy engine one could provide it as a separate argument and it would be on the caller to keep those things coupled unfortunately. However, if we add support for the tss2 pem format, I think we did IIRC, we could make a version that contains the JSON policy as well to couple things more easily.
I would recommend building something with pytss, and then make tpm2py-tools package [1]. I would love to re-write a set of command line tools in Python, while they can't be used everywhere, they'd be much simpler and way simpler to add features than the C tools. Or even Rust, now where rust is, i'd have no qualms using that language.
- If you would like a repo in this namespace, just let us know.
Bind key can be just as valid and since it's started off of a secret theirs no need to verify it. The secret is never sent over the wire as well.
It's very dangerous to use a
binKeyand not atpmKey: if the secret has low entropy then an eavesdropper can mount an offline dictionary attack.If one it relying on bindKey, the auth shouldn't be trivial. That's on the user, so while it can be dangerous, theirs a lot of dangers in crypto.
One really has to always use HMAC sessions with tpmKey and parameter encryption to avoid active attackers (yes, even on the local bus). One can use TPMs remotely over a public network too, and the tools can't really know if that's the case, so one really has to authenticate the TPM to the client application, and if the bindAuth authValue is too simple then one really has to use a tpmKey.
There's a chicken-egg situation, of course, in that the first time one learns the public area of a tpmKey one may not be able to authenticate the TPM. If there are platform certificates then, yes, one can use those to securely learn the EKpub of the platform's TPM, but if not then one can only realistically learn the EKpub (and EKcert) via an ssh-style trust-on-first-use (TOFU). Once one has the EKpub one can then create other primaries over a session with the EK as the tpmKey. Once one has written down the EKpub and other primaries' public areas in local storage, one can use the TPM with a tpmKey every time for most things anyways -- everything but [re-]creating those keys.
EDIT: Imagine using a fast networked TPM for doing things like password validation in high-traffic sites. Clearly one would not use a slow, local dTPM for such things -- too slow, that! But if you're using a TPM over a network... OK, one could use TLS, and one probably should, but TPM2_StartAuthSession is a micro-TLS protocol, and policies can make it a very rich protocol.
There have been highly publicized active, local, on-the-bus attack techniques to compromise servers. It is essential, for example, that BMCs and BIOSes use tpmKey or very strong bindAuth to avoid these attacks, and not just the BMCs and BIOSes but also the kernel and TPM applications.
I would recommend building something with pytss [...]
My colleagues and I have completely soured on Python for these things primarily on account of the bloat it leads to. For example, Safeboot is mostly bash-coded using tpm2-tools utilities so as to make it very small. Nowadays Rust and Go would be much better choices. @osresearch is particularly interested in keeping boot images small enough to be able to realistically use for PXE booting.
I would recommend building something with pytss [...]
My colleagues and I have completely soured on Python for these things primarily on account of the bloat it leads to. For example, Safeboot is mostly bash-coded using tpm2-tools utilities so as to make it very small. Nowadays Rust and Go would be much better choices. @osresearch is particularly interested in keeping boot images small enough to be able to realistically use for PXE booting.
Yeah if your environment is early boot, Python is usually out and even Go isn't optimal for size constraints. Rust would be a good choice.
Yeah if your environment is early boot, Python is usually out and even Go isn't optimal for size constraints. Rust would be a good choice.
As long as the boot image can be <10MB it's probably fine. Python easily blows right through that.
Bind key can be just as valid and since it's started off of a secret theirs no need to verify it. The secret is never sent over the wire as well.
It's very dangerous to use a
binKeyand not atpmKey: if the secret has low entropy then an eavesdropper can mount an offline dictionary attack.If one it relying on bindKey, the auth shouldn't be trivial. That's on the user, so while it can be dangerous, theirs a lot of dangers in crypto.
One really has to always use HMAC sessions with
tpmKeyand parameter encryption to avoid active attackers (yes, even on the local bus). One can use TPMs remotely over a public network too, and the tools can't really know if that's the case, so one really has to authenticate the TPM to the client application, and if thebindAuthauthValueis too simple then one really has to use atpmKey.
Sure, bindKey only means you must have strong entropy as that is the only protection you have. But in some environments you may not have or can afford the time costs of asymmetric crypto.
There's a chicken-egg situation, of course, in that the first time one learns the public area of a
tpmKeyone may not be able to authenticate the TPM. If there are platform certificates then, yes, one can use those to securely learn the EKpub of the platform's TPM, but if not then one can only realistically learn the EKpub (and EKcert) via an ssh-style trust-on-first-use (TOFU). Once one has the EKpub one can then create other primaries over a session with the EK as thetpmKey. Once one has written down the EKpub and other primaries' public areas in local storage, one can use the TPM with atpmKeyevery time for most things anyways -- everything but [re-]creating those keys.
TOFU is generally the model a lot of folks gravitate towards, myself included. I just recently added this into systemd, waiting on the PR to get merged: https://github.com/systemd/systemd/pull/26185
EDIT: Imagine using a fast networked TPM for doing things like password validation in high-traffic sites. Clearly one would not use a slow, local dTPM for such things -- too slow, that! But if you're using a TPM over a network... OK, one could use TLS, and one probably should, but
TPM2_StartAuthSessionis a micro-TLS protocol, and policies can make it a very rich protocol.
With the cmd TCTI it's easy, you can get an SSH tunnel to the endpoint and then, I always suggest using sessions so you get those protections within the endpoint. Ie you don't need to trust your end point, just the TPM. The endpoint could only dos you.
There have been highly publicized active, local, on-the-bus attack techniques to compromise servers. It is essential, for example, that BMCs and BIOSes use
tpmKeyor very strongbindAuthto avoid these attacks, and not just the BMCs and BIOSes but also the kernel and TPM applications.
Yep, totally agree. I am in favor of the tpmKey and salted sessions when possible. I also prefer sessions over things like SPDM, because their are parts of designs where you may be concerned about introspection/alteration not on the bus itself. Consider an SGX/TDX design where bytes leaving the "enclave" are of concern, SPDM doesn't help you there.
Sure, bindKey only means you must have strong entropy as that is the only protection you have. But in some environments you may not have or can afford the time costs of asymmetric crypto.
Right, and for that one should first use a session with a tpmKey to create a key with a high-entropy authValue, record the authValue in local storage, then use that key as the bindKey for symmetrically keying sessions.
TOFU is generally the model a lot of folks gravitate towards, myself included. I just recently added this into systemd, waiting on the PR to get merged: https://github.com/systemd/systemd/pull/26185
Some platforms have platform certificates, but there's no standard way to retrieve them :( Still, if provided a platform certificate, it'd be good to use it for bootstrapping trust!
SPDM
As in https://www.dmtf.org/standards/spdm ?
Sure, bindKey only means you must have strong entropy as that is the only protection you have. But in some environments you may not have or can afford the time costs of asymmetric crypto.
Right, and for that one should first use a session with a
tpmKeyto create a key with a high-entropyauthValue, record theauthValuein local storage, then use that key as thebindKeyfor symmetrically keying sessions.TOFU is generally the model a lot of folks gravitate towards, myself included. I just recently added this into systemd, waiting on the PR to get merged: systemd/systemd#26185
Some platforms have platform certificates, but there's no standard way to retrieve them :( Still, if provided a platform certificate, it'd be good to use it for bootstrapping trust!
Yes, that's what FAPI does. Unfortunately in early boot requirements it's difficult to validate the whole cert chain.
SPDM
As in https://www.dmtf.org/standards/spdm ?
Yeah, I think that's it. It's essentially bus level protection.
Their was discussions on making a TCTI that modifies the commands being set to include session encryption and protections. This was users just get support without needing to worry about anything. FAPI does this as well for users.
How could that possibly work if the app uses a
tpmKey? I don't think it can work. The TCTI layer is too low in the stack for this.
If the app enables sessions you leave it be, else you modify the command and response buffers.
If the app enables sessions you leave it be, else you modify the command and response buffers.
But the app might be using an HMAC session in order to satisfy an authorization requirement that it prove knowledge of an authValue that might be weak.
It's important to separate session keying for over-the-bus (or over-the-network) security from proving knowledge of weak passwords.
If the app enables sessions you leave it be, else you modify the command and response buffers.
But the app might be using an HMAC session in order to satisfy an authorization requirement that it prove knowledge of an
authValuethat might be weak.It's important to separate session keying for over-the-bus (or over-the-network) security from proving knowledge of weak passwords.
Yes, it's a best effort. We won't be able to do it for every invocation based on the callers use of sessions. Our idea was to run in pedantic mode and return an error if we can't, so users know where things could be weak, then allow an ignore mode to bypass it.
The goal would be able to take a.legacy app that makes no use of sessions other than the implicitly started password session and enable session protections for them.
The goal would be able to take a.legacy app that makes no use of sessions other than the implicitly started password session and enable session protections for them.
A worthy goal, but if you also provide the library that the apps use why not just do it all in the library?
I fear that trying to add security in the TCTI layer is going to make people unjustifiably feel good about weak applications. This also adds a fair bit of complexity at that layer.