From 6363b9bbdc1fb90584332e2dc1e6a22d780e7119 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Thu, 3 Jun 2021 23:21:48 +0200 Subject: [PATCH 1/3] add example code + basic concept of passing a secret to a TPM just knowing the EK Signed-off-by: Erik Larsson --- Attestation/ek-secret.md | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Attestation/ek-secret.md diff --git a/Attestation/ek-secret.md b/Attestation/ek-secret.md new file mode 100644 index 0000000..b7217fd --- /dev/null +++ b/Attestation/ek-secret.md @@ -0,0 +1,175 @@ +# `Passing a secret to a TPM using only the EK` + +This is example code to pass a secret to a system by just knowing the endorsenment 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. + +## Concept +The concept is that the key being activated by a call to ActivateCredential on the remote system doesn't have to be generated on the TPM, as long as the public and private parts are loaded it will succeed. +By generating a temporary key pair on the local system we can run MakeCredential with the remote system EK, name of the locally generated key and the secret. + +## 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 + +## Issues +there is no TPM based protection against replay attacks in this example From aaa14e4bf48f7218f8e60ea19d561f15993706d9 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Fri, 4 Jun 2021 20:54:23 -0500 Subject: [PATCH 2/3] Review comments --- Attestation/ek-secret.md | 230 ++++++++++++++++++++++++++++++++++++- Attestation/send-to-tpm.sh | 131 +++++++++++++++++++++ Attestation/tpm-receive.sh | 175 ++++++++++++++++++++++++++++ 3 files changed, 531 insertions(+), 5 deletions(-) create mode 100755 Attestation/send-to-tpm.sh create mode 100755 Attestation/tpm-receive.sh diff --git a/Attestation/ek-secret.md b/Attestation/ek-secret.md index b7217fd..cb74a27 100644 --- a/Attestation/ek-secret.md +++ b/Attestation/ek-secret.md @@ -1,17 +1,105 @@ -# `Passing a secret to a TPM using only the EK` +# 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 the endorsenment key. +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 -The concept is that the key being activated by a call to ActivateCredential on the remote system doesn't have to be generated on the TPM, as long as the public and private parts are loaded it will succeed. -By generating a temporary key pair on the local system we can run MakeCredential with the remote system EK, name of the locally generated key and the secret. + +`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 @@ -79,6 +167,7 @@ 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 @@ -171,5 +260,136 @@ 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 -there is no TPM based protection against replay attacks in this example + + - 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" From a6604ffaeff9dda2573652af1bd7709e2422c093 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Sun, 6 Jun 2021 00:08:35 -0500 Subject: [PATCH 3/3] Add intro text about authorization roles --- Intro/README.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/Intro/README.md b/Intro/README.md index d3efdd0..9649665 100644 --- a/Intro/README.md +++ b/Intro/README.md @@ -534,6 +534,87 @@ loaded and unloaded as needed) that represents the current policy construction or evaluation hash extension digest (the `policyDigest`), and the objects that have been granted access. +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.