diff --git a/Attestation/ek-secret.md b/Attestation/ek-secret.md new file mode 100644 index 0000000..cb74a27 --- /dev/null +++ b/Attestation/ek-secret.md @@ -0,0 +1,395 @@ +# Passing a secret to a TPM using only the public key of Endorsement Key (EK) + +This is example code to pass a secret to a system by just knowing its endorsenment key's public key. +We will be using the current (commit 07a92e9fa75548ea102ce90b3b6182093b3f7a73 or later) master branch of https://github.com/tpm2-software/tpm2-pytss + +The terms for the systems are `client`, the system we want to pass the secret to and `server`, the system which has the secret but doesn't need a TPM. +One assumtion that will be made is that you already have the EKpub for the remote system on the local system, and trust it. +While we will use the EK in this guide any key accepted by ActivateCredential should work. + +## Background + +What we want is something akin to asymmetric encryption, with the local +system encrypting to the public key of the remote system. The local +system would send the ciphertext to the remote system, and the remote +system would decrypt it using its private key. + +The TPM does support plain asymmetric decryption using +`TPM2_RSA_Decrypt()`. However, the `EK` is a [restricted +key](/Intro/README.md#Restricted-Cryptographic-Keys), specifically a +[restricted decryption key](/Intro/README.md#Restricted-Decryption-Keys) +which means that `TPM2_RSA_Decrypt()` will not work. + +The TPM supports two constrained asymmetric decryption operations with +[restricted decryption +keys](/Intro/README.md#Restricted-Decryption-Keys): + + - [`TPM2_Import()`](/TPM-Commands/TPM2_Import.md) + - [`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md) + +The sender sides of those two functions are, respectively: + + - [`TPM2_Duplicate()`](/TPM-Commands/TPM2_Duplicate.md) + - [`TPM2_MakeCredential()`](/TPM-Commands/TPM2_MakeCredential.md) + +`TPM2_Duplicate()`/`TPM2_Import()` are specifically about sharing +private key objects from one TPM to another. We won't use those here. + +[`TPM2_MakeCredential()`](/TPM-Commands/TPM2_MakeCredential.md) allows +us to encrypt a small secret (e.g., an AES key) to a remote system's +`EKpub`, and then the remote system can decrypt that with its `EK` using +[`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md). + +The key background concepts here are: + + - [restricted decryption keys](/Intro/README.md#Restricted-Decryption-Keys), + - and access controlled decryption with restricted decryption keys. + +Most importantly, +[`TPM2_MakeCredential()`](/TPM-Commands/TPM2_MakeCredential.md) allows +the sender to specify an authorization policy that the caller of +[`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md) +must meet in order for it to be willing to decrypt the ciphertext. + +> Note that `TPM2_MakeCredential()` can be implemented entirely in +> software. + +> Note that duplicating a key that is fixed to TPMs requires using +> `TPM2_Duplicate()` on that TPM, otherwise if the key is not fixed to +> the TPM then `TPM2_Duplicate()` can be implemented in software. + +## Concept + +`TPM2_MakeCredential()` requires three inputs. Besides the target's +`EKpub` and the small secret to send to it, `TPM2_MakeCredential()` also +requires the [cryptographic name](/Intro/README.md#Cryptographic-Object-Naming) +of a key object that must reside on the target system's TPM -- this is +known as the _activation object_. + +The key insight is that the actual public key of the object named by the +activation object name input of `TPM2_MakeCredential()` is not used at +all. Neither does `TPM2_ActivateCredential()` use the private key of +that object. The only things that matter about the activation object +are that: + +a) it must exist on the target system, +b) its cryptographic name must be the same as was used on the sender side, +c) and that the caller of `TPM2_ActivateCredential()` must satisfy the activation object's [_authorization policy_](/Intro/README.md#Policies) (_if_ `adminWithPolicy` is set as an attribute of the activation object). + +> NOTE: The cryptographic name of an object binds the authorization +> policy set on that object. Therefore the caller of +> `TPM2_MakeCredential()` specifies an authorization policy that the +> caller of `TPM2_ActivateCredential()` must meet if the +> `adminWithPolicy` attribute is set on the activation object. + +> NOTE: Learn more about [restricted keys](/Intro/README.md#Restricted-Cryptographic-Keys), +> [authorization policies](/Intro/README.md#Policies), and +> user roles in our [introductory tutorial](/Intro/README.md). + +Since the private and public key parts of the activation object are not +used and are irrelevant, they can even be fixed and published for all to +see, even the private key. + +By using a well-known activation key we can avoid having to know the +cryptographic name of some unique object on the remote system's TPM! + +Or we can generate a unique key but send its private part in the clear +to the remote system. + +Thus we need only know the target system's TPM's `EKpub`. + +## server script + +```python +#!/usr/bin/python3 + +import sys +from tpm2_pytss import * +from tpm2_pytss.makecred import MakeCredential +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat, PrivateFormat, NoEncryption + +def main(ekpath, publicpath, sensitivepath, credpath, secretpath, oursecret): + # first read the EK and unmarshal it + with open(ekpath, 'rb') as ef: + ekb = ef.read() + ekpub, _ = TPM2B_PUBLIC.Unmarshal(ekb) + + # Now we generate the temporary key pair + # We are using ECC keys here as they are generally fast to generate, but RSA should work as well + # We will use the curve SECP256R1 as it should work on all TPMs + # One could use a well known/the same pre-generated key for multiple systems + privatekey = ec.generate_private_key(ec.SECP256R1, backend=default_backend()) + publickey = privatekey.public_key() + + # Now it's time to TPM structures from the keys + # First we need to encode it due to how the tpm2_pytss API currently works + privateenc = privatekey.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption()) + publicenc = publickey.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) + sensitive = TPM2B_SENSITIVE.fromPEM(privateenc) + # by objectAttributes to 0 we reduce the change that keys will be used for anything + public = TPM2B_PUBLIC.fromPEM(publicenc, objectAttributes=0) + # the same applices to authPolicy + public.publicArea.authPolicy = b"\x00" * 32 + + # now it's time to run the MakeCredential part, using the software implementation in tpm2_pytss + # the API is slight different to the standard, but behaves the same + credblob, secret = MakeCredential(ekpub, oursecret, bytes(public.getName())) + + # time to marshal the structures and save them to disk so we can send them the remote system + pubb = public.Marshal() + with open(publicpath, 'xb') as pubf: + pubf.write(pubb) + sensb = sensitive.Marshal() + with open(sensitivepath, 'xb') as sensf: + sensf.write(sensb) + credb = credblob.Marshal() + with open(credpath, 'xb') as credf: + credf.write(credb) + secretb = secret.Marshal() + with open(secretpath, 'xb') as secretf: + secretf.write(secretb) + + +if __name__ == '__main__': + if len(sys.argv) < 6: + sys.stderr.write(f"usage: {sys.argv[0]} ek-public temp-public temp-sensitive credblob secret\n") + exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], b"example secret") +``` + +Arguments to the script is the following: +ek-public: the path to the public part of the EK +temp-public: where to save the public part of the temporary key +temp-sensitive: where to save the sensitive part of the temporary key +credlob: where to save the encrypted credential generated by MakeCredential +secret: where to save the encrypted secret generated by MakeCredential + +## client script + +```python +#!/usr/bin/python3 + + +import sys +from tpm2_pytss import * + +def unmarshal_tools_context(ekb): + ekctx = TPMS_CONTEXT() + magic = int.from_bytes(ekb[0:4], byteorder='big') + version = int.from_bytes(ekb[4:8], byteorder='big') + ekctx.hierarchy = int.from_bytes(ekb[8:12], byteorder='big') + ekctx.savedHandle = int.from_bytes(ekb[12:16], byteorder='big') + ekctx.sequence = int.from_bytes(ekb[16:24], byteorder='big') + ekctx.contextBlob, _ = TPM2B_CONTEXT_DATA.Unmarshal(ekb[24:]) + return ekctx + +def eksession(ectx): + session = ectx.StartAuthSession( + ESYS_TR.NONE, + ESYS_TR.NONE, + None, + TPM2_SE.POLICY, + TPMT_SYM_DEF(algorithm=TPM2_ALG.NULL), + TPM2_ALG.SHA256, + ) + + ectx.PolicySecret( + ESYS_TR.RH_ENDORSEMENT, + session, + TPM2B_NONCE()._cdata, + TPM2B_DIGEST()._cdata, + TPM2B_NONCE()._cdata, + 0, + session1=ESYS_TR.PASSWORD, + ) + + return session + +def main(ekpath, publicpath, sensitivepath, credpath, secretpath): + # time to setup a ESAPI context, we will use the default TCTI for the system + ectx = ESAPI() + + # Time to load the EK context, by using tpm2_createek there is no reason the implement the whole setup in this example code + with open(ekpath, 'rb') as ekf: + ekb = ekf.read() + ekctx = unmarshal_tools_context(ekb) + ekhandle = ectx.ContextLoad(ekctx) + + # now lets setup the standard EK policy session + session = eksession(ectx) + + # Now we should read, unmarshal and load the temporary key pair + with open(publicpath, 'rb') as pubf: + pubb = pubf.read() + public, _ = TPM2B_PUBLIC.Unmarshal(pubb) + with open(sensitivepath, 'rb') as sensf: + sensb = sensf.read() + sensitive, _ = TPM2B_SENSITIVE.Unmarshal(sensb) + print(sensitive.sensitiveArea.authValue.size, public.publicArea.authPolicy.size) + # We will load it under the NULL hierarchy as that is the only hierarchy allowing both the public and private part to be loaded for external keys + handle = ectx.LoadExternal(sensitive, public, ESYS_TR.RH_NULL) + + + # Time to read and unmarshal the credential and secret for ActivateCredential + with open(credpath, 'rb') as credf: + credb = credf.read() + credblob, _ = TPM2B_ID_OBJECT.Unmarshal(credb) + with open(secretpath, 'rb') as secretf: + secretb = secretf.read() + secret, _ = TPM2B_ENCRYPTED_SECRET.Unmarshal(secretb) + + # Well, now there is nothing left but calling ActivateCredential and getting our secret on the remove system! + oursecret = ectx.ActivateCredential(handle, ekhandle, credblob, secret, session2=session) + + print(f"we got the secret: {bytes(oursecret)}") + +if __name__ == '__main__': + if len(sys.argv) < 6: + sys.stderr.write(f"usage: {sys.argv[0]} ek-ctx temp-public temp-sensitive credblob secret\n") + exit(1) + main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]) +``` + +Generate the EK context with `tpm2_createek -c ek.ctx` +The arguments are: +ek-ctx: the context generated by tpm2_createek +temp-public: the temp-public output from the local system script +temp-sensitive: the temp-sensitive output from the local system script +credblob: the credblob output from the local system script +secret: the secret output from the local system script + +## Example (bash) + +This example uses two bash scripts: + + - [`send-to-tpm.sh`](send-to-tpm.sh) + - [`tpm-receive.sh`](tpm-receive.sh) + +Usage messages for those two scripts: + +``` +Usage: send-to-tpm.sh EK-PUB-FILE SECRET-FILE OUT-FILE [POLICY-CMD [ARGS [\; ...]]] + send-to-tpm.sh -P well-known-key-name EK-PUB-FILE SECRET-FILE OUT-FILE + + Options: + + -h This help message. + -P WKname Use the given cryptographic name binding a policy for + recipient to meet. + -f Overwrite OUT-FILE. + -x Trace this script. +``` + +``` +Usage: receive.sh CIPHERTEXT-FILE OUT-FILE [POLICY-CMD [ARGS] [;] ...] + + "Activates" (decrypts) CIPHERTEXT-FILE made with TPM2_MakeCredential and + writes the plaintext to OUT-FILE. + + The POLICY-CMD and arguments are one or more commands that must + leave a policy digest in a file named 'policy' in the current + directory (which will be a temporary directory). + + Options: + + -h This help message. + -f Overwrite OUT-FILE. + -x Trace this script. +``` + +Example (without policy, both scripts running on the same system): + +``` +: ; # NOTE: The shell prompt ($PS1) is set to ': ; ' to make it easy to +: ; # cut-and-paste. +: ; +: ; # Get the EKpub: +: ; tpm2 createek --ek-context ek.ctx --public ek.pub +: ; +: ; # Make a small secret: +: ; echo hello world > secret.txt +: ; +: ; # Make ciphertext: +: ; /tmp/send-to-tpm.sh -f ek.pub /tmp/secret /tmp/cipher +: ; +: ; # Decrypt ciphertext: +: ; /tmp/receive.sh -f /tmp/cipher /tmp/plain +name: +000b9f40e7a7a85bcc39bba777b7eda5764d91a28512d91d395ca114b14621ae321e +837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa +certinfodata:68656c6c6f20776f726c640a +: ; +: ; # Show plaintext: +: ; cat /tmp/plain +hello world +``` + +Example (with policy, both scripts running on the same system): + +``` +: ; # NOTE: The shell prompt ($PS1) is set to ': ; ' to make it easy to +: ; # cut-and-paste. +: ; +: ; # Get the EKpub: +: ; tpm2 createek --ek-context ek.ctx --public ek.pub +: ; +: ; # Make a small secret: +: ; echo hello world > secret.txt +: ; +: ; /tmp/send-to-tpm.sh -f ek.pub /tmp/secret /tmp/cipher \ +> tpm2 policysecret --session session.ctx \ +> --object-context endorsement -L policy \; \ +> tpm2 policycommandcode -S session.ctx -L policy \ +> TPM2_CC_ActivateCredential +837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa +cd9917cf18c3848c3a2e606986a066c68142f9bc2710a278287a650ca3bbf245 +: ; +: ; /tmp/tpm-receive.sh -f /tmp/cipher /tmp/plain \ +> tpm2 policysecret --session session.ctx \ + --object-context endorsement \ + -L policy \; \ + tpm2 policycommandcode -S session.ctx -L policy \ + TPM2_CC_ActivateCredential +837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa +cd9917cf18c3848c3a2e606986a066c68142f9bc2710a278287a650ca3bbf245 +name: 000bec987554f57b9918285794542c05549aa778832be169351494066907d6d95abf +837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa +837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa +cd9917cf18c3848c3a2e606986a066c68142f9bc2710a278287a650ca3bbf245 +certinfodata:68656c6c6f20776f726c640a +: ; cat /tmp/plain +hello world +: ; +``` + +You can pass policy commands to the `send-to-tpm.sh` and `tpm-receive.sh` +commands as arguments, with multiple policy commands separated by a +single semi-colon (quoted, to avoid evaluation by the shell): + +```bash +send-to-tpm.sh ek.pub /tmp/secret /tmp/cipher \ + tpm2 policypcr -S session.ctx -l "sha256:0,1,2,3" -f $PWD/pcr.dat \ + -L policy \; \ + tpm2 policycommandcode -S session.ctx -L policy TPM2_CC_ActivateCredential +``` + +## Issues + + - The secret sent this way has to be small: no larger than the digest + size for the digest algorithm being used. + + If the application needs to send larger secrets, then it should + generate an AES key and send that as the small secret, then encrypt + the larger secret in the AES key and send that ciphertext. (But + don't forget to also include an HMAC or MAC of the ciphertext to make + detection of errors / tampering possible.) + + - There is no protection against replay attacks in this example. + + Replay protection can be added by adding a timestamp to the secret + data, and by using a replay cache on the remote system. + + - There is no authentication of the sender. To authenticate the sender + simply add a digital signature of the ciphertext. diff --git a/Attestation/send-to-tpm.sh b/Attestation/send-to-tpm.sh new file mode 100755 index 0000000..9e40f14 --- /dev/null +++ b/Attestation/send-to-tpm.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +PROG=${0##*/} + +set -euo pipefail + +function usage { + ((${1:-1} > 0)) && exec 1>&2 + cat <= 3)) || usage +ekpub_file=$1 +secret_file=$2 +out_file=$3 +shift 3 + +function err { + echo "ERROR: $*" 1>&2 + exit 1 +} + +[[ -f ${ekpub_file:-} ]] || usage +[[ -f ${secret_file:-} ]] || usage +[[ -f ${out_file:-} ]] && $force && rm -f "${out_file:-}" +[[ -f ${out_file:-} ]] && err "output file ($out_file) exists" + +# Make a temp dir and remove it when we exit: +d= +trap 'rm -rf "$d"' EXIT +d=$(mktemp -d) + +function exec_policy { + while (($# > 0)); do + cmd=() + while (($# > 0)) && [[ $1 != ';' ]]; do + cmd+=("$1") + shift + done + (($# > 0)) && shift + # Run the policy command in the temp dir. It -or the last command- must + # leave a file there named 'policy'. + (cd "$d" && "${cmd[@]}") + done +} + +function make_policyDigest { + # Start a trial session, execute the given policy commands, save the + # policyDigest. + tpm2 flushcontext --transient-object + tpm2 flushcontext --loaded-session + tpm2 startauthsession --session "${d}/session.ctx" + exec_policy "$@" +} + +function wkname { + # This is the WK. It was generated with: + # openssl genpkey -genparam \ + # -algorithm EC \ + # -out "${d}/ecp.pem" \ + # -pkeyopt ec_paramgen_curve:secp384r1 \ + # -pkeyopt ec_param_enc:named_curve + # openssl genpkey -paramfile "${d}/ecp.pem" + cat > "${d}/wkpriv.pem" < 0)); then + make_policyDigest "$@" 1>&2 + loadexternal_args+=(-L "${d}/policy") + attrs='adminwithpolicy|decrypt|sign' + fi + + # Load the WK + tpm2 flushcontext --transient-object 1>&2 + tpm2 flushcontext --loaded-session 1>&2 + tpm2 loadexternal -C n \ + -Gecc \ + -r "${d}/wkpriv.pem" \ + "${loadexternal_args[@]}" \ + -a "$attrs" \ + -c "${d}/wk.ctx" | + grep ^name: | + cut -d' ' -f2 +} + +[[ -z $wkname ]] && wkname=$(wkname "$@") + +tpm2 makecredential \ + --tcti "none" \ + --encryption-key "${ekpub_file}" \ + --name "$wkname" \ + --secret "${secret_file}" \ + --credential-blob "$out_file" diff --git a/Attestation/tpm-receive.sh b/Attestation/tpm-receive.sh new file mode 100755 index 0000000..7caae06 --- /dev/null +++ b/Attestation/tpm-receive.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +PROG=${0##*/} + +set -euo pipefail + +function usage { + echo "Usage: $PROG [OPTIONS] CIPHERTEXT-FILE OUT-FILE [POLICY-CMD [ARGS] [\; ...]]" + cat <= 2)) || usage +ciphertext_file=$1 +out_file=$2 +shift 2 + +[[ -f ${ciphertext_file:-} ]] || usage +[[ -f ${out_file:-} ]] && $force && rm -f "$out_file" +[[ -f ${out_file:-} ]] && usage + +d= +trap 'rm -rf "$d"' EXIT +d=$(mktemp -d) + +function v { + if $verbose; then + printf 'Running:' + printf ' %q' "$@" + printf '\n' + fi >/dev/tty || true + if "$@"; then + $verbose && printf '(SUCCESS)\n' >/dev/tty || true + else + stat=$? + printf 'ERROR: Command exited with %d\n' $stat >/dev/tty || true + return $stat + fi +} + +function exec_policy { + while (($# > 0)); do + cmd=() + while (($# > 0)) && [[ $1 != ';' ]]; do + cmd+=("$1") + shift + done + (($# > 0)) && shift + # Run the policy command in the temp dir. It -or the last command- must + # leave a file there named 'policy'. + if (v cd "$d" && v "${cmd[@]}" 1> "${d}/out" 2> "${d}/err"); then + cat "${d}/out" >/dev/tty || true + else + stat=$? + echo "ERROR: Failed to run \"${cmd[0]} ...\":" + cat "${d}/out" + cat "${d}/err" 1>&2 + exit $stat + fi + done +} + +function make_policyDigest { + tpm2 flushcontext --transient-object + tpm2 flushcontext --loaded-session + v tpm2 startauthsession --session "${d}/session.ctx" + exec_policy "$@" +} + +# Get the EK handle: +tpm2 flushcontext --transient-object +tpm2 flushcontext --loaded-session +tpm2 createek --key-algorithm rsa \ + --ek-context "${d}/ek.ctx" \ + --public "${d}/ek.pub" + +# Make policyDigest and load WK +attrs='decrypt|sign' +loadexternal_args=() +if (($# > 0)); then + make_policyDigest "$@" + loadexternal_args+=(-L "${d}/policy") + attrs='adminwithpolicy|decrypt|sign' +fi + +rm -f "${d}/session.ctx" + +# This is the WK. It was generated with: +# openssl genpkey -genparam \ +# -algorithm EC \ +# -out "${d}/ecp.pem" \ +# -pkeyopt ec_paramgen_curve:secp384r1 \ +# -pkeyopt ec_param_enc:named_curve +# openssl genpkey -paramfile "${d}/ecp.pem" +cat > "${d}/wkpriv.pem" <&2 +tpm2 flushcontext --loaded-session 1>&2 +if v tpm2 loadexternal -C n \ + -Gecc \ + -r "${d}/wkpriv.pem" \ + "${loadexternal_args[@]}" \ + -a "$attrs" \ + -c "${d}/wk.ctx" > "${d}/out" 2> "${d}/err"; then + cat "${d}/out" 1>&2 +else + stat=$? + echo "ERROR: Failed to load WK:" 1>&2 + cat "${d}/out" + cat "${d}/err" 1>&2 + exit $stat +fi + +# Create empty auth session for EK +v tpm2 flushcontext --transient-object +v tpm2 flushcontext --loaded-session +v tpm2 startauthsession --session "${d}/sessionek.ctx" --policy-session +v tpm2 policysecret --session "${d}/sessionek.ctx" --object-context endorsement + +activatecredential_args=() +if (($# > 0)); then + activatecredential_args+=(--credentialedkey-auth session:"${d}/session.ctx") + # Create auth session for the WK, since it has adminWithPolicy + v tpm2 flushcontext --transient-object + v tpm2 flushcontext --loaded-session + v tpm2 startauthsession --session "${d}/session.ctx" --policy-session + exec_policy "$@" + v tpm2 flushcontext --transient-object + v tpm2 flushcontext --loaded-session +fi +# Finally, ActivateCredential +$verbose && tpm2 readpublic -c "${d}/wk.ctx" | grep name: +v tpm2 activatecredential --credentialedkey-context "${d}/wk.ctx" \ + "${activatecredential_args[@]}" \ + --credentialkey-context "${d}/ek.ctx" \ + --credentialkey-auth session:"${d}/sessionek.ctx" \ + --credential-blob "$ciphertext_file" \ + -o "$out_file" diff --git a/Intro/README.md b/Intro/README.md index 9469107..1b37e27 100644 --- a/Intro/README.md +++ b/Intro/README.md @@ -696,6 +696,87 @@ TPM, such as when the TPM is remote. > TODO: Discuss key exchange options, etc. +Alternatively a session can be for encryption of command inputs/outputs, +which is useful when the path to the TPM is not secure. + +### Authorization Roles + +How a TPM authorizes some particular command and its use of its input +handles varies by command and according to two attributes of the objects +identified by the command's input handles. + +A "role" is really a set of rules that will be applied to authorization +for a given object/command. + +There are three types of authorization roles that can apply in any case: + + - `USER` role + + This means that if the object in question has an `authValue` set, and + it has the `userWithAuth` attribute set, then the application can get + access by demonstrating knowledge of the corresponding password. And + anyways, if the object has a policy then the user can get access by + satisfying the policy even w/o knowing the password. + + This is the most commonly applied authorization role. + + - `ADMIN` role + + This means that if the object in question has an `authValue` set, and + it has the `adminWithPolicy` attribute _not_ set, then the + application can get access by demonstrating knowledge of the + corresponding password or by satisfying its `authPolicy` if one is + set. But if the object has a policy and the `adminWithPolicy` + attribute set then the user _must_ satisfy the policy to get access. + + In the `adminWithPolicy` attribute set case, the caller _must_ also + have called `TPM2_PolicyCommandCode()` with the code of the command + that the caller wishes to execute. + + Only three commands apply `ADMIN` role to any of the objects + identified by their input handle parameters: + + - `TPM2_Certify()` requires `ADMIN` role for its `objectHandle` + input parameter. + + Whereas the `signHandle` input parameter requires `USER` role. + + - `TPM2_ActivateCredential()` requires `ADMIN` role for its + `activateHandle` input parameter. + + Whereas the `keyHandle` input parameter requires `USER` role. + + - `TPM2_ObjectChangeAuth()` requires `ADMIN` role for its + `objectHandle` input parameter. + + - `DUP` role + + This is just for the + [`TPM2_Duplicate()`](/TPM-Commands/TPM2_Duplicate.md) command. The + caller of `TPM2_Duplicate()` must satisfy the key object's + `authPolicy`, and must have called `TPM2_PolicyCommandCode()` with + the code of the `TPM2_Duplicate()` command (`TPM_CC_Duplicate`). + + > `DUP` is very similar to `ADMIN` when the `adminWithPolicy` + > attribute is set. + +For example, the +[`TPM2_ActivateCredential()`](/TPM-Commands/TPM2_ActivateCredential.md) +command requires `USER` role for the `keyHandle` input and `ADMIN` role +for the `activateHandle`. If the `keyHandle` is the `EK`, then since +the `EK` has a default `authValue`, use will be allowed. If the +`activateHandle` is for an object with an `authPolicy` and the +`adminWithPolicy` attribute set, then the caller must execute that +policy's commands (yielding, on success, a session whose `policyDigest` +matches that object's `authPolicy`) and must have called +`TPM2_PolicyCommandCode(TPM_CC_ActivateCredential)` on that same +session. + +> NOTE: Every handle argument to a TPM command can require its own +> authorization, therefore there can be zero, one, or two authorization +> sessions as inputs to any TPM command (some TPM commands have no input +> handle parameters, some have one, and some have two). + ## Restricted Cryptographic Keys Cryptographic keys can either be unrestricted or restricted.