Skip to content
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

Complex policy expressions #134

Closed
wants to merge 6 commits into from

Conversation

nicowilliams
Copy link
Contributor

@nicowilliams nicowilliams commented Jul 23, 2021

This allows one to express complex policies (ones with alternations)
using a simple language.

The pattern is simply that conjunctions are tpm2 policy* command-lines
joined with a ';' argument, and alternations are tpm2 policyor
commands with arguments that are themselves policies surrounded by '('
and ')' arguments.

For example:

    $ sbin/tpm2-policy							\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"

which allows an entity sporting such a policy to be used for signing or
decryption only, and only when PCR#11 is cleared.

The same, verbosely:

    $ sbin/tpm2-policy -v						\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"
        Running: (AND) exec_policyOR_helper ( tpm2 policycommandcode TPM2_CC_Sign ) ( tpm2 policycommandcode TPM2_CC_RSA_Decrypt ) ;
            Running: (AND) tpm2 policycommandcode --session /tmp/tmp.jbXmSKOtzR/session-0-0 --policy /tmp/tmp.jbXmSKOtzR/policy-0-0 TPM2_CC_Sign
    cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
    cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
            Running: (AND) tpm2 policycommandcode --session /tmp/tmp.jbXmSKOtzR/session-0-1 --policy /tmp/tmp.jbXmSKOtzR/policy-0-1 TPM2_CC_RSA_Decrypt
    3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
    3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
        ORing: cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811 3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
        Running: tpm2 policyor --session /tmp/tmp.jbXmSKOtzR/session --policy /tmp/tmp.jbXmSKOtzR/policy sha256:/tmp/tmp.jbXmSKOtzR/policy-0-0,/tmp/tmp.jbXmSKOtzR/policy-0-1
    39faada14fb6d5deba4315d8bce0247813262ebf15e1aee35477d26759f1b29e
        Running: (AND) tpm2 policypcr --session /tmp/tmp.jbXmSKOtzR/session --policy /tmp/tmp.jbXmSKOtzR/policy -l sha256:11
    3b9ea8dca851fac5d077d7b6925b8cc38847619e4b9a0f026ca5cc6262ff8a1c
    3b9ea8dca851fac5d077d7b6925b8cc38847619e4b9a0f026ca5cc6262ff8a1c
    $

One can also execute a policy in a policy session using sbin/tpm2-policy:

: ; /safeboot/sbin/tpm2-policy -e sessdup                                       \
        tpm2 policyor '(' tpm2 policycommandcode TPM2_CC_Duplicate ';'          \
                          tpm2 policysecret --object-context primary.ctx ')'    \
                      '(' tpm2 policycommandcode TPM2_CC_Sign ')'
bef56b8c1cc84e11edd717528d2cd99356bd2bbf8f015209c3f84aeeaba8e8a2
6152da7336d14b50adc37165c3b3a75af43426f7fb285b14e0f3e7145c8abf97
6152da7336d14b50adc37165c3b3a75af43426f7fb285b14e0f3e7145c8abf97
cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
30f6e1c393911768102f2a8a1ca5218eb6b837e12527d97cc38aae12027c7c9b
30f6e1c393911768102f2a8a1ca5218eb6b837e12527d97cc38aae12027c7c9b

Internally we have exec_policy in functions.sh that can execute a
policy in either a trial session or in policy session, and if in a
policy session then the caller must supply the indices of alternatives
to take in the policy, otherwise the caller must not supply those.

I.e.,

    # Compute policyDigest of the policy above:
    tpm2_flushall
    tpm2 startauthsession --session session
    exec_policy session policy						\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"
    # Execute the policy above with the second alternative:
    tpm2_flushall
    tpm2 startauthsession --session session
    exec_policy 1 session policy					\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"

This is recursive, so it generalizes to policies of arbitrary
complexity, limited to nesting alternations up to nine (9) times, or the
shell's recursion limit if it be lower.

@nicowilliams nicowilliams force-pushed the experiment branch 5 times, most recently from 19ae7ad to 5de2990 Compare July 23, 2021 21:17
@nicowilliams
Copy link
Contributor Author

It's possible that the -e option won't work for policies with several more alternations than I tried.

The correct way to evaluate a policy with alternations would be to first evaluate all the alternatives not-taken in trial sessions, flushing loaded sessions as one goes, then start a policy session for all the taken alternatives and execute those, with any tpm2 policyors referring to not-taken alternatives by their already-computed policyDigests. This way there is no risk of running out of loaded session handle space on the TPM.

@nicowilliams nicowilliams force-pushed the experiment branch 2 times, most recently from ad575b8 to 4678119 Compare July 28, 2021 02:38
@nicowilliams
Copy link
Contributor Author

nicowilliams commented Jul 28, 2021

The correct way to evaluate a policy with alternations would be to first evaluate all the alternatives not-taken in trial sessions, flushing loaded sessions as one goes, then start a policy session for all the taken alternatives and execute those, with any tpm2 policyors referring to not-taken alternatives by their already-computed policyDigests. This way there is no risk of running out of loaded session handle space on the TPM.

Fixed. Now the policy is first executed in trial sessions (one per alternation + one for the top-level), the policy digests are saved, then the chosen alternatives are executed in a single policy session and the previously computed policy digests are used for the alternatives not-taken. This minimizes the number of saved session contexts used that must be available concurrently.

EDIT: Well... the current implementation uses N trial sessions concurrently, where N is the alternation depth. This could be reduced to 1 by calling first executing all the leaf alternations, then their parents, and so on.

@nicowilliams nicowilliams force-pushed the experiment branch 2 times, most recently from 3b4643d to 318f048 Compare July 29, 2021 04:46
@nicowilliams
Copy link
Contributor Author

I don't know how to fix this:

In /github/workspace/tests/test-enroll.sh line 16:
. "$TOP/functions.sh"
  ^-----------------^ SC1091: Not following: functions.sh was not specified as input (see shellcheck -x).

The make shellcheck target wasn't even listing that file, but even now that I added it...

This allows one to express complex policies (ones with alternations)
using a simple language.

The pattern is simply that conjunctions are {tpm2 policy*} command-lines
joined with a ';' argument, and alternations are {tpm2 policyor}
commands with arguments that are themselves policies surrounded by '('
and ')' arguments.

For example:

    $ sbin/tpm2-policy							\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"

which allows an entity sporting such a policy to be used for signing or
decryption only, and only when PCR#11 is cleared.

The same, verbosely:

    $ sbin/tpm2-policy -v						\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"
        Running: (AND) exec_policyOR_helper ( tpm2 policycommandcode TPM2_CC_Sign ) ( tpm2 policycommandcode TPM2_CC_RSA_Decrypt ) ;
            Running: (AND) tpm2 policycommandcode --session /tmp/tmp.jbXmSKOtzR/session-0-0 --policy /tmp/tmp.jbXmSKOtzR/policy-0-0 TPM2_CC_Sign
    cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
    cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811
            Running: (AND) tpm2 policycommandcode --session /tmp/tmp.jbXmSKOtzR/session-0-1 --policy /tmp/tmp.jbXmSKOtzR/policy-0-1 TPM2_CC_RSA_Decrypt
    3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
    3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
        ORing: cc6918b226273b08f5bd406d7f10cf160f0a7d13dfd83b7770ccbcd1aa80d811 3c29869a1312094782b86df5a430caae587f5dfb16dfa3f7204151054c1340d2
        Running: tpm2 policyor --session /tmp/tmp.jbXmSKOtzR/session --policy /tmp/tmp.jbXmSKOtzR/policy sha256:/tmp/tmp.jbXmSKOtzR/policy-0-0,/tmp/tmp.jbXmSKOtzR/policy-0-1
    39faada14fb6d5deba4315d8bce0247813262ebf15e1aee35477d26759f1b29e
        Running: (AND) tpm2 policypcr --session /tmp/tmp.jbXmSKOtzR/session --policy /tmp/tmp.jbXmSKOtzR/policy -l sha256:11
    3b9ea8dca851fac5d077d7b6925b8cc38847619e4b9a0f026ca5cc6262ff8a1c
    3b9ea8dca851fac5d077d7b6925b8cc38847619e4b9a0f026ca5cc6262ff8a1c
    $

One can also execute a policy in a policy session using
sbin/tpm2-policy.

Internally we have `exec_policy` in functions.sh that can execute a
policy in either a trial session or in policy session, and if in a
policy session then the caller must supply the indices of alternatives
to take in the policy, otherwise the caller must not supply those.

I.e.,

    # Compute policyDigest of the policy above:
    tpm2_flushall
    tpm2 startauthsession --session session
    exec_policy session policy						\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"

    # Execute the policy above with the second alternative:
    tpm2_flushall
    tpm2 startauthsession --session session
    exec_policy 1 session policy					\
	tpm2 policyor							\
	    '(' tpm2 policycommandcode TPM2_CC_Sign ')'			\
	    '(' tpm2 policycommandcode TPM2_CC_RSA_Decrypt ')' ';'	\
	tpm2 policypcr -l "sha256:11"

This is recursive, so it generalizes to policies of arbitrary
complexity, limited to nesting alternations up to nine (9) times, or the
shell's recursion limit if it be lower.
Good suggestion by Erik Larsson.
@nicowilliams
Copy link
Contributor Author

@osresearch has requested making the language pithier by not requiring that every sub-command be tpm2_policy* or tpm2 policy*. So one would write tpm2-policy secret ... instead of tpm2-policy tpm2 policysecret ....

@nicowilliams
Copy link
Contributor Author

Also, clearly the language will need something like symbolic bindings for user-provided items, and/or maybe inlined literals like public keys.

For example, suppose a policy uses TPM2_PolicySigned(), and needs to specify a public key for the signer... The key in that case might need to be inlined in the policy language as a literal (line-joined PEM?) or as a reference to a literal that follows the policy in the same file (like a here-doc), and since we might need several such literals, we might need to name them and be able to refer to them by $name in the sub-commands. Items such as policyRefs might need to be caller-provided, in which case we'd need a way to refer to them by $name in the sub-commands, with a way for the user to provide them on the tpm2-policy command-line.

We then might as well have the alternatives-taken argument for policy session evaluation be given as a tpm2-policy command option.

@nicowilliams
Copy link
Contributor Author

nicowilliams commented Aug 30, 2021

So we might have:

$ cat > policy.def
or ( secret endorsement ) ( signed $signer $policyRef )
--
signer <<EOS
<PEM of signer pubkey here>
EOS
^D
$ tpm2-policy -s session.ctx -L policy.dat -a 1 -V policyRef=abcd ./policy.def

or

$ cat > policy.def
or ( secret $entity ) ( signed $signer $policyRef )
--
entity: load <<EOPRIV <<EOPUB
<base64-encoded private part>
EOPRIV
<base64-encoded public part>
EOPUB
signer <<EOS
<PEM of signer pubkey here>
EOS
^D
$ tpm2-policy -s session.ctx -L policy.dat -a 1 -V policyRef=abcd ./policy.def

Any prompting for passwords/authValues would have to be handled too.

@nicowilliams
Copy link
Contributor Author

@williamcroberts maybe tpm2-tools could use something like this, a policy language for EA policies. I'm just playing here, and currently it's really half-baked.

@kgoldman maybe we can even design an EA policy language that TCG might standardize?

@williamcroberts
Copy link

@williamcroberts maybe tpm2-tools could use something like this, a policy language for EA policies. I'm just playing here, and currently it's really half-baked.

@kgoldman maybe we can even design an EA policy language that TCG might standardize?

It already exists inside of FAPI and the TSS WG is actually working on pulling that out of FAPI and standardizing the policy language and a library for handling it in a separate spec.

@nicowilliams
Copy link
Contributor Author

@williamcroberts maybe tpm2-tools could use something like this, a policy language for EA policies. I'm just playing here, and currently it's really half-baked.
@kgoldman maybe we can even design an EA policy language that TCG might standardize?

It already exists inside of FAPI and the TSS WG is actually working on pulling that out of FAPI and standardizing the policy language and a library for handling it in a separate spec.

Oh! Ok, I'll take a look. Thanks for the tip.

@williamcroberts
Copy link

@williamcroberts maybe tpm2-tools could use something like this, a policy language for EA policies. I'm just playing here, and currently it's really half-baked.
@kgoldman maybe we can even design an EA policy language that TCG might standardize?

It already exists inside of FAPI and the TSS WG is actually working on pulling that out of FAPI and standardizing the policy language and a library for handling it in a separate spec.

Oh! Ok, I'll take a look. Thanks for the tip.

FYI theirs also a policy generator GUI: https://tpm2-software.github.io/fapipolicies/

@nicowilliams
Copy link
Contributor Author

@williamcroberts does the tpm2-tools stack support FAPI JSON policies?

@williamcroberts
Copy link

williamcroberts commented Aug 31, 2021

@williamcroberts does the tpm2-tools stack support FAPI JSON policies?

IIUC, The tss2 prefixed tools do. However, we wanted to create a new tpm2 policy tool that takes the engine out of FAPI and supports it.

@nicowilliams
Copy link
Contributor Author

IIUC, The tss2 prefixed tools do. However, we wanted to create a new tpm2 policy tool that takes the engine out of FAPI and supports it.

Yes. In fact, I'm thinking of writing a bash + jq script to do just that using existing tpm2_* commands.

I'm not keen on the tss2 tools' abstraction of "let's pretend there's a database of metadata-rich entities". I really like the very thin tpm2 tools' abstraction of just adding automatic context save/load to TPM2_*() commands. The tss2 tool approach cannot and, really, MUST NOT know about everything a TPM is used for, so this idea of storing a DB of extra metadata in /etc/ just does not work. The tpm2 tooling's thin abstraction means the user is on the hook for flushing contexts out of the TPM (though that too could be automated), but otherwise by being such a thin abstraction I can focus on the TCG TPM 2.0 Library parts 1 and 3 and easily build complex applications out of simple tools.

@williamcroberts
Copy link

IIUC, The tss2 prefixed tools do. However, we wanted to create a new tpm2 policy tool that takes the engine out of FAPI and supports it.

Yes. In fact, I'm thinking of writing a bash + jq script to do just that using existing tpm2_* commands.

I'm not keen on the tss2 tools' abstraction of "let's pretend there's a database of metadata-rich entities". I really like the very thin tpm2 tools' abstraction of just adding automatic context save/load to TPM2_*() commands. The tss2 tool approach cannot and, really, MUST NOT know about everything a TPM is used for, so this idea of storing a DB of extra metadata in /etc/ just does not work. The tpm2 tooling's thin abstraction means the user is on the hook for flushing contexts out of the TPM (though that too could be automated), but otherwise by being such a thin abstraction I can focus on the TCG TPM 2.0 Library parts 1 and 3 and easily build complex applications out of simple tools.

The policy engine code in FAPI that we can rip out just calls callback functions that would be plumbed to TPM commands. So it would keep that thin abstraction while automatically reading the json policy file. It keeps one from having to write a bash + jq thing. I can take a look at ripping this library out today.

@williamcroberts
Copy link

Ahh ifapi_policyutil_execute_prepare depends on a FAPI context, and we want to break that dependency. Let me bring this up with Andreas and see what we can come up with.

@nicowilliams
Copy link
Contributor Author

Ahh ifapi_policyutil_execute_prepare depends on a FAPI context, and we want to break that dependency. Let me bring this up with Andreas and see what we can come up with.

Sure. But I think I can go with jq + bash for now. Basically a bash script that calls a jq program to emit a set of tpm2 commands to eval in order using the jq @sh shell quoting feature.

@williamcroberts
Copy link

Ahh ifapi_policyutil_execute_prepare depends on a FAPI context, and we want to break that dependency. Let me bring this up with Andreas and see what we can come up with.

Sure. But I think I can go with jq + bash for now. Basically a bash script that calls a jq program to emit a set of tpm2 commands to eval in order using the jq @sh shell quoting feature.

I was hoping to spur development of a permanent tool so you don't have to maintain both things. You also pick up TPM-less policy calculations.

@nicowilliams
Copy link
Contributor Author

I was hoping to spur development of a permanent tool so you don't have to maintain both things. You also pick up TPM-less policy calculations.

I'm starting to think that software operations (make credential, trial policies, duplicate) should be implemented in a completely separate codebase. The reason for this is that it could be a very small and clean codebase that way.

As for executing policies from a description language, I do think too that scripting with jq is very tempting as a first prototype, and if the command-line tools are good enough, why not?

@williamcroberts
Copy link

I was hoping to spur development of a permanent tool so you don't have to maintain both things. You also pick up TPM-less policy calculations.

I'm starting to think that software operations (make credential, trial policies, duplicate) should be implemented in a completely separate codebase. The reason for this is that it could be a very small and clean codebase that way.

I thought you guys had a script for this no?

As for executing policies from a description language, I do think too that scripting with jq is very tempting as a first prototype, and if the command-line tools are good enough, why not?

prototype yes, i'm trying to spur long term thoughts here and conformity so theirs not 10 ways to do something. Since their already is a format for policies, building something to do that generically for all users has a promising future and would reduce your overall maintenance burden.

@AndreasFuchsTPM
Copy link

Hi all... Jumping onto the train here.
We already have a running implementation of a policy calculation and execution inside the tss (used internally by libtss2-fapi). That being said, we made sure that this module does not have any other internal dependencies (besides some helpers, but nothing state related), because we already planned for making this standalone at some point.

Long story short, here are the three function we defined (that are knowing-working code) that we could extract into a separate library:
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_instantiate.h#L73-L81
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_calculate.h#L22-L28
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_execute.h#L161-L170

Would you be willing to spend some effort on helping me extract the code into a separate library instead of coding a second implementation ?

@williamcroberts
Copy link

Hi all... Jumping onto the train here.
We already have a running implementation of a policy calculation and execution inside the tss (used internally by libtss2-fapi). That being said, we made sure that this module does not have any other internal dependencies (besides some helpers, but nothing state related), because we already planned for making this standalone at some point.

Long story short, here are the three function we defined (that are knowing-working code) that we could extract into a separate library:
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_instantiate.h#L73-L81
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_calculate.h#L22-L28
https://github.com/tpm2-software/tpm2-tss/blob/ec58f0a43931b18de9c3c5e0cd884892c218041a/src/tss2-fapi/ifapi_policy_execute.h#L161-L170

Would you be willing to spend some effort on helping me extract the code into a separate library instead of coding a second implementation ?

in progress...

@williamcroberts
Copy link

So I have something early started:

Theirs a lot of todos and its very raw and only partially working. But I'd like to get feedback early on direction.

@nicowilliams
Copy link
Contributor Author

I'm toying with a scheme like so:

  • rbin/* -- helper scripts
    • one for every policy command, and also one for loadexternal, and another for verifysignature
    • links to sha256sum, xxd, and such utils
  • every policy is a directory with:
    • any supporting artifacts, like public keys to loadexternal
    • a script to be run with rbash (restricted Bash) with just rbin in PATH
  • sbin/tpm2-policy will eval policies, in both trial and policy sessions as requested and necessary
    • it would ultimately (cd $policy && rbash ./policy), with temp files (e.g., loaded key contexts, tickets) in $TMPDIR

For EAs the alternatives to execute would be given to sbin/tpm2-policy as an argument but communicated to the rbash and rbin scripts via env vars. $TMPDIR would be where to put temp items, as one would expect. $TMPDIR/policy-session.ctx would be the policy session. Etc.

@nicowilliams
Copy link
Contributor Author

Superseded by #144.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants