Abstract Introduction Provisioning Runtime extended provisioning Sealing application secret Unsealing application secret Demonstration script using tpm2-tools Credits
This tutorial demonstrates a method to protect application secrets on the TPM. TPM’s RTS protected-storage guarantees protection of information on the TPM. This tutorial explains a method to protect the secrets on the interface between the CPU and the TPM using provisions in the TPM standard.
In the method we discuss the following protections for an application secret:
The application secret is sealed to a trusted TPM device whose identity is known to be valid.
The TPM device and the CPU are bound with a secret. The authorization policy for the sealing object is made dependent on the secret. This offers protection against the attacker ability to transplant the TPM to an attacker controlled CPU to retrieve the secrets.
Inhibit the attacker ability to retrieve or replay any of the secrets used for the provisioning and the application-secrets on the CPU<==>TPM interface.
The method works by sealing the application secret to a sealing-object whose auth policy dictates that the content of an NV index must be the hash of a secret value that is known only to a CPU.
The rest of the mechanism details the encryption of the sensitive content transferred between the CPU and the TPM with a verified identity.
There are four distinct steps to the method, namely:
Provisioning: At this step, the NV index with specific attributes and authorization-value and authorization-policy is created by the CPU(RTM).
Runtime-extended-provisioning: At this step, the NV index of the type “Extend” is extended with the NV secret value derived from the CPU known secret.
Sealing-data: A sealing object is created with the policy-command-code (TPM2_CC_PolicyNV AND TPM2_CC_Unseal)
Unsealing-data: The authorization-policy for unsealing is satisfied and the secret is unsealed.

sequenceDiagram
Note over CPU(RTM): (NVIndex==MISSING || Attributes==MISSING)
Note over CPU(RTM): State = PROVISIONING
CPU(RTM)->>+TPM: TPM2_CC_GetCapability<br>TPM2_CC_NVReadPublic
Note over CPU(RTM): Get/verify EK against EK certificate
CPU(RTM)->>+TPM: TPM2_CC_CreatePrimary<br>TPM2_CC_NVRead
Note over CPU(RTM): Retrieve NV-index access policy
PROTECTED-STORAGE->>+CPU(RTM):PolicyOr(A|B|C)<br>A: TPM2_CC_PolicyCommandCode = TPM2_CC_NVRead<br>B: TPM2_CC_PolicyCommandCode = TPM2_CC_NVExtend<br>C: TPM2_CC_PolicyCommandCode = TPM2_CC_PolicyNV
Note over CPU(RTM): Read CPU secret-seed
PROTECTED-STORAGE->>+CPU(RTM): CPU-SECRET-SEED
Note over CPU(RTM): Lock further access to the secret seed
Note over CPU(RTM):1. Derive HOST_SECRET
Note over CPU(RTM):2. Start a salted session
Note over CPU(RTM):3. Encrypt HOST_SECRET
Note over CPU(RTM):4. Define/provision NVIndex with auth
CPU(RTM)->>+TPM: TPM2_CC_StartAuthSession<br>TPM2_CC_NV_DefineSpace<br>PlatformCreate|NT=Extend|Orderly|StClear|NoDa|<br>PolicyRead|PolicyWrite|AuthRead|Authwrite

sequenceDiagram
Note over CPU(RTM): (NVIndex==PRESENT || Attributes==SET)
Note over CPU(RTM): State = RUNTIME-PROVISIONING
CPU(RTM)->>+TPM: TPM2_CC_GetCapability<br>TPM2_CC_NVReadPublic
Note over CPU(RTM): Read CPU secret-seed
PROTECTED-STORAGE->>+CPU(RTM): CPU-SECRET-SEED
Note over CPU(RTM): Lock further access to the secret seed
Note over CPU(RTM): 1. Derive HOST_SECRET
Note over CPU(RTM): 2. Derive NV_SECRET
Note over CPU(RTM): 3. Start a bounded session to encrypt NV_SECRET
CPU(RTM)->>+TPM: TPM2_CC_StartAuthSession<br>Bind-object = NV-index<br>Bind-object-auth = HOST_SECRET
Note over CPU(RTM): 4. Satisfy the NV access policy
CPU(RTM)->>+TPM: TPM2_CC_PolicyCommandCode == TPM2_CC_PolicyNV<br>TPM2_CC_PolicyOR
Note over CPU(RTM): 5. Populate/Extend NVIndex
CPU(RTM)->>+TPM: TPM2_CC_NV_Extend

sequenceDiagram
Note over CPU(Application): 1. Generate sealing-policy
CPU(Application)->>+TPM: (D and E)<br>D: TPM2_CC_PolicyNV <specify Hash(NV-Extend-Secret)><br>E: TPM2_CC_PolicyCommandCode = TPM2_CC_Unseal
Note over CPU(Application):2. Started a salted session `tpmkey = EKpub`
CPU(Application)->>+TPM: TPM2_CC_StartAuthSession
Note over CPU(Application):3. Choose/encrypt application secret
Note over CPU(Application):4. Create the sealing object with auth = sealing-policy
CPU(Application)->>+TPM: TPM2_CC_CreatePrimary<br>TPM2_CC_Create

sequenceDiagram
Note over CPU(Application): 1. Load the sealing object on the TPM
CPU(Application)->>+TPM: TPM2_CC_CreatePrimary<br>TPM2_CC_Load
Note over CPU(Application):2. Start a salted session `tpmkey = EKpub` to protect unsealed data
CPU(Application)->>+TPM: TPM2_CC_StartAuthSession
Note over CPU(Application):3. Start a policy session for NV access with TPM2_CC_PolicyNV
CPU(Application)->>+TPM: TPM2_CC_StartAuthSession<br>TPM2_CC_PolicyCommandCode<br>TPM2_CC_PolicyOr
Note over CPU(Application): 4. Satisfy the auth policy for sealing object
CPU(Application)->>+TPM: PolicyAnd(D and E)<br>`D: PolicyNV <Hash(NV_SECRET), NV-access-policy-session>`<br>`E: PolicyCommandCode = TPM2_CC_Unseal`
Note over CPU(Application): 5. Unseal the data from loaded object
TPM->>+CPU(Application): Encrypt and send the sealing-blob back to the application
Note over CPU(Application): 6. Decrypt and consume the sealed data
#!/bin/bash
#
# Filename : tpm2-software-secret-protection-demo.sh
# Author : [email protected], github.com/idesai
#
set -E
tpm-secret-protection-demo-help() {
echo "
#
# Flows:
#
# RTM checks for the NVIndex and its properties and triggers either:
#
# (1) cpu_secret_provisioning : Creates NVIndex with required auths & attributes
# OR
# (2) runtime_provisioning : Extends NVIndex with data only known to the CPU
#
# Applications can now reference the NVIndex in a PolicyNV and perform either:
#
# (1) seal_data : Create a sealing object and seal data
#
# (2) unseal_data : Load the sealing object and unseal data
#
#
# NOTE: Source this script to avail all the functions
"
}
#
# Globals
#
# The NV index should ideally be created as a platform hierarchy object
NVIndex=0x1500018
# This is one of the two secrets derived from a seed value ideally accessible
# only to the CPU. This serves as the NV index auth that can be used as bind obj
HOST_SECRET="host-secret"
# This is the second secret derived from a seed value ideally accessible
# only to the CPU. When extended to the NV index, a hash of this value can be
# used in PolicyNV to authorize unsealing of application secret. Only a CPU with
# access to the HOST_SECRET will be able to extend the required value.
NV_SECRET="nv-secret"
# The application secret to be sealed to the TPM. So long as the CPU extends the
# required NV_SECRET value to the TPM, the unsealing operation is allowed.
SEALBLOB="app-secret"
cleanup() {
tpm2_clear -Q
tpm2_nvreadpublic | grep -q $NVIndex
if [ $? == 0 ]; then
tpm2_nvundefine -Q -C p $NVIndex
fi
rm -f ek.ctx salted_session.ctx policycc_nv_session.ctx A.policy B.policy \
C.policy nvaccess_policy_generation_session.ctx nvaccess.policy \
bounded_policy_session.ctx unseal_policy_generate_session.ctx \
unseal.policy oprim.ctx seal_obj.ctx nvread_session.ctx
}
trap cleanup EXIT
#
# Salted session for encrypting sensitive information when:
# 1. Creating the NV index
# 2. Creating the sealing object
# 3. Unsealing the application secret
#
setup_salted_param_encrypt_session_with_ek() {
tpm2_createek -Q --key-algorithm rsa --ek-context ek.ctx
tpm2_startauthsession -Q --session salted_session.ctx $1 \
--tpmkey-context ek.ctx
rm -f ek.ctx
tpm2_sessionconfig -Q salted_session.ctx --enable-decrypt
}
#
# Starting the PolicyNV policy session required when:
# 1. Generating the policy for the sealing object at creation
# 2. Unsealing the application secret
#
nvaccess_policycc_policynv() {
tpm2_startauthsession -Q --session policycc_nv_session.ctx --policy-session
tpm2_policycommandcode -Q --session policycc_nv_session.ctx TPM2_CC_PolicyNV
tpm2_policyor -Q --session policycc_nv_session.ctx \
--policy-list sha256:A.policy,B.policy,C.policy
}
#
# Generate the policy paths for accessing read/ write operations on NV index
# A.policy ==> PolicyCommandCode = TPM2_CC_NV_Read
# B.policy ==> PolicyCommandCode = TPM2_CC_NV_Extend
# C.policy ==> PolicyCommandCode = TPM2_CC_PolicyNV
# Access-Policy = A||B||C
#
generate_nv_access_policy() {
tpm2_startauthsession -Q --session nvaccess_policy_generation_session.ctx
tpm2_policycommandcode -Q TPM2_CC_NV_Read --policy A.policy \
--session nvaccess_policy_generation_session.ctx
tpm2_flushcontext -Q nvaccess_policy_generation_session.ctx
tpm2_startauthsession -Q --session nvaccess_policy_generation_session.ctx
tpm2_policycommandcode -Q TPM2_CC_NV_Extend --policy B.policy \
--session nvaccess_policy_generation_session.ctx
tpm2_flushcontext -Q nvaccess_policy_generation_session.ctx
tpm2_startauthsession -Q --session nvaccess_policy_generation_session.ctx
tpm2_policycommandcode -Q TPM2_CC_PolicyNV --policy C.policy \
--session nvaccess_policy_generation_session.ctx
tpm2_flushcontext -Q nvaccess_policy_generation_session.ctx
tpm2_startauthsession -Q --session nvaccess_policy_generation_session.ctx
tpm2_policyor -Q --session nvaccess_policy_generation_session.ctx \
--policy-list sha256:A.policy,B.policy,C.policy --policy nvaccess.policy
tpm2_flushcontext -Q nvaccess_policy_generation_session.ctx
rm -f nvaccess_policy_generation_session.ctx
}
#
# This provisioning step is done once under RTM control
#
cpu_secret_provisioning() {
generate_nv_access_policy
setup_salted_param_encrypt_session_with_ek --hmac-session
tpm2_nvdefine -Q --session salted_session.ctx -C p -p $HOST_SECRET $NVIndex \
-a "orderly|clear_stclear|platformcreate|no_da|nt=extend|policyread|policywrite|authread|authwrite" \
--policy nvaccess.policy
tpm2_flushcontext -Q salted_session.ctx
rm -f salted_session.ctx nvaccess.policy
}
#
# This step is done once at every TPM restart under RTM control
# Satisfy policy to be able to extend the NV index in bounded policy session
# Note: Auth specified in the bounded session generation is used to
# calculate the sessionvalue by ESAPI. The auth is not exposed on
# TPM interface.
#
runtime_provisioning() {
tpm2_startauthsession -Q --session bounded_policy_session.ctx \
--policy-session --bind-context $NVIndex --bind-auth $HOST_SECRET
tpm2_sessionconfig -Q bounded_policy_session.ctx \
--enable-decrypt --enable-encrypt
tpm2_policycommandcode -Q --session bounded_policy_session.ctx \
TPM2_CC_NV_Extend
tpm2_policyor -Q --session bounded_policy_session.ctx \
--policy-list sha256:A.policy,B.policy,C.policy
echo -n $NV_SECRET|tpm2_nvextend -Q -C $NVIndex -i- $NVIndex \
-P session:bounded_policy_session.ctx
tpm2_flushcontext -Q bounded_policy_session.ctx
rm -f bounded_policy_session.ctx
}
#
# Satisfy policy to be able to read the NV Index
#
nvread_session_setup() {
tpm2_startauthsession -Q --session nvread_session.ctx --policy-session
tpm2_policycommandcode -Q --session nvread_session.ctx TPM2_CC_NV_Read
tpm2_policyor -Q --session nvread_session.ctx \
--policy-list sha256:A.policy,B.policy,C.policy
}
#
# Sealing-object-policy:
# PolicyCommandCode == (TPM2_CC_PolicyNV && TPM2_CC_Unseal)
#
seal_data() {
nvaccess_policycc_policynv
tpm2_startauthsession -Q --session unseal_policy_generate_session.ctx
nvread_session_setup
tpm2_nvread $NVIndex -P session:nvread_session.ctx | tpm2_policynv -Q -i- \
$NVIndex eq --session unseal_policy_generate_session.ctx \
-P session:policycc_nv_session.ctx --policy unseal.policy
tpm2_flushcontext -Q policycc_nv_session.ctx
tpm2_flushcontext -Q nvread_session.ctx
rm -f policycc_nv_session.ctx
rm -f nvread_session.ctx
tpm2_policycommandcode -Q --session unseal_policy_generate_session.ctx \
--policy unseal.policy TPM2_CC_Unseal
tpm2_flushcontext -Q unseal_policy_generate_session.ctx
rm -f unseal_policy_generate_session.ctx
setup_salted_param_encrypt_session_with_ek --hmac-session
tpm2_createprimary -Q -C o -c oprim.ctx
echo -n $SEALBLOB | tpm2_create -Q -C oprim.ctx --policy unseal.policy \
-u seal_obj.pub -r seal_obj.priv --session salted_session.ctx -i-
rm -f oprim.ctx
rm -f unseal.policy
tpm2_flushcontext -Q salted_session.ctx
rm -f salted_session.ctx
}
load_sealing_object() {
tpm2_createprimary -Q -C o -c oprim.ctx
tpm2_load -Q -C oprim.ctx -c seal_obj.ctx -u seal_obj.pub -r seal_obj.priv
rm -f oprim.ctx
}
#
# 1. Read NV index <non secret data>
# 2. Satisfy PolicyNV
# 3. Unseal
#
unseal_data() {
nvaccess_policycc_policynv
setup_salted_param_encrypt_session_with_ek --policy-session
nvread_session_setup
tpm2_nvread -C $NVIndex -P session:nvread_session.ctx $NVIndex | \
tpm2_policynv -Q -i- $NVIndex eq --session salted_session.ctx \
-P session:policycc_nv_session.ctx
tpm2_flushcontext -Q policycc_nv_session.ctx
tpm2_flushcontext -Q nvread_session.ctx
rm -f policycc_nv_session.ctx
rm -f nvread_session.ctx
tpm2_policycommandcode -Q --session salted_session.ctx TPM2_CC_Unseal
load_sealing_object
UNSEALBLOB=$(tpm2_unseal -Q -c seal_obj.ctx -p session:salted_session.ctx)
echo "UNSEALBLOB=$UNSEALBLOB"
tpm2_flushcontext -Q salted_session.ctx
rm -f seal_obj.ctx
}
tpm-secret-protection-demo-help
This document is a culmination of thoughts and ideas of the members of the TCG Device Driver Working Group (DDWG). Thanks to the contributors:
End-to-end development with physical hardware can be challenging due to a myriad of factors. Things like persistent state, physical wear, slow and difficult to update hardware bugs, lagging features, etc can pose additional hurdles to development tasks.
A potential way to overcome this, is to use QEMU instance with an attached virtualized TPM2.0 device. This device is made available to the guest OS, and with the appropriate versions of Linux, will expose the familiar /dev/tpm0 and /dev/tpmrm0 interfaces.
In this brief tutorial, we provide instructions on how to build such a system by leveraging other documentation as required.
Prior art does exist on this topic, and details used in this tutorial have references from the following resources:
In this tutorial, we demonstrate how to leverage the swtpm as the TPM simulator. The project wiki has instructions for building and installing the simulator and its dependency, libtpms.
Next, you need to install QEMU. This is operating system dependent. Details on installing QEMU can be found by visiting their website:
The minimum version of QEMU to support this is 4.0. In this tutorial, the author tested with version 5.2.
The author installed Ubuntu 20.04, so the commands will be specific to that ISO, but another ISO could be substituted. Additionally, the naming convention on things like hard-drive could be changed to reflect your environment more closely.
Install the guest OS. This will be guest-OS specific. The general commands are to build a virtual disk:
qemu-img create -f qcow2 ubuntu-20.04-amd64.img 30G
Then attach it to a VM and start it with the installation media, usually an ISO:
qemu-system-x86_64 -hda ~/qemu-images/ubuntu-20.04-amd64.img -boot d -cdrom ~/Downloads/ubuntu-20.04.1-desktop-amd64.iso -m 2048 -enable-kvm
Now start the guest with a virtualized TPM2.0 device. To do this, one needs to start the SWTPM simulator in tpm2 mode using the option
--tpm2, like so:
mkdir /tmp/emulated_tpm
swtpm socket --tpmstate dir=/tmp/emulated_tpm --ctrl type=unixio,path=/tmp/emulated_tpm/swtpm-sock --log level=20 --tpm2
Then start the guest:
qemu-system-x86_64 -hda ~/qemu-images/ubuntu-20.04-amd64.img -boot d -m 2048 -enable-kvm \
-chardev socket,id=chrtpm,path=/tmp/emulated_tpm/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0
Now verify that the device nodes are present in the guest VM by opening a console and running the following command:
ls /dev/tpm*
You should see /dev/tpm0 and /dev/tpmrm0 devices in the output of the ls command.
One of the major benefits to the emulated environment is being able to test end-to-end development without the need for physical hardware and it’s associated drawbacks across a wide variety of environments. QEMU has the ability to emulate multiple physical CPU architectures. Couple that with the ability to install a wide array of operating systems, and you have a flexible system for debugging and building new features from the lowest portions of the stack all they way to end client applications.
William Roberts
]]>The Feature API (FAPI) contains the concept of cryptographic profiles.
These define a combination of cryptographic algorithms and properties that shall be used.
On a typical installation they are located under /etc/tpm2-tss/fapi-profiles.
One of these profiles is selected using the fapi config file; key profile_name in /etc/tpm2-tss/fapi-config.json.
When calling Fapi_Profision() the keystore of Fapi is initialized, the SRKs are created and the EK is checked based on the currently selected profile. From that point on, the parameters of the profile are used for all relevant operations.
The FAPI includes a concept for cryptographic transition. The purpose is that if the crypto policy changes one can transition to the new policy whilst retaining access to old key material. As such, one can change the profile_name in the fapi-config.json and call Fapi_Provision() in order to populate the new keystore.
Note that you have to make sure that the persistent handles between the old and the new profile in /etc/tpm2-tss/fapi-profiles do not collide. On a standard installation they both are set to 0x81000001.
Once the new profile is provisioned, the old profile can be access by prefixing all keypaths accordings, e.g. /P_OLDPROFILE/HS/SRK/...
At the time of this writing only the RSA crypto profiles support asymmetric encryption and decryption. The reason is that even though the Elliptic Curve Integrated Encryption Scheme (ECIES) seems to be well established, there exists no one standard for the exact semantics and data encoding formats. See also OpenSSL for a reference: https://github.com/openssl/openssl/issues/9314#issuecomment-508724551
Thus in order to utilize the functions Fapi_Encrypt() and Fapi_Decrypt() the RSA profile must be selected. Note that multiple profiles can be provisioned at once, as mentioned above.
This page may be updated in the future with more information.
Andreas Fuchs @Fraunhofer SIT
]]>This article shows how to use the utilities/ tools from the tpm2-tools project to set up a bare bone remote attestation of the system software state as reflected by the TPM2 platform-configuration-registers (PCR). The intent is to provide a general guidance on the topic and does not discuss or reference any attestation framework in particular.
CAUTION: All code samples for the sample attestation framework are strictly for demonstration purpose. It has not been evaluated for production use.

“Attestation is the evidence or proof of something. It is a declaration that
something exists or is the case. It is the action of being a witness to or
formally certifying something.”
The literary-definition very much applies in the context of this tutorial which discusses attesting of the PCR contents in a TPM.
An attestation has two critical parts for it to hold true, namely:
Attestor identity: To believe something is true, one needs to vouch the authenticity of what is being attested. This inherently means the identity of the one who attests is known and or trusted.
Attestation data integrity: To believe something is true, both the process to generate the information and the process to protect the information from tampering need to be inherently trusted.
While private or sensitive portion of keys used to sign attestation blobs must remain confidential or secret, it is possible to identify a unique signer with the public key used to verify the signature over the attestation blobs. This defeats the anonymity or privacy of the platform user.
Let’s use a hypothetical example of a smart-lock device that has an embedded application. It has integrated multiple microservices from different providers that provide various services, namely:
If the device uses the same key across all the microservices for public key authentication and or signing attestation quotes. The service provider or a third party analytics could potentially stage a privacy violation knowingly or unknowingly simply by tracking usage of same public key. And so it can be determined that the device owner uses facial recognition for 2FA and operates the device consistently certain times of the day which updates the device logs.
There are at least four possible ways to resolve this.
Note: Platform anonymity can also be defeated if the platform’s host-name and or ip-address remains the same on every connection. This is not a topic of discussion here, yet there is an assumption made here that sufficient measures are taken by the platform user to resolve this using a VPN or other reasonable mechanisms.
Depending on the robustness and privacy rules of the system, platform anonymity may not be mandatory and so privacy considerations don’t apply. If that is the case or if methods #1 or #2 discussed above suffice your attestation and privacy needs, subsequent sections detailing information on TCG endorsement keys (EK) and attestation identity keys (AIK) are irrelevant.
Note: A more detailed discussion on TPM attestation terminologies can be found here. This document expands on that discussion to demonstrate how one can set up a minimal or bare bones attestation framework using tpm2-tools.
With these definitions in mind, let’s dive straight into TPM attestation topic. Of the various object types in the TPM, the following discussion is restricted to how the TPM PCR data can be attested securely and anonymously using method #3.
PCR or platform configuration registers are special TPM2 objects that can only be modified or written to by hash extension mechanism. In that, the incoming new value is concatenated with the existing value in the PCR and hashed. The new hash value now replaces the old value. This means that though it is a single location, the final value reflects a history of all the hash extensions. PCRs are arranged in banks, one each for a specific hash algorithm SHA1, SHA256, etc. Every bank has up to 32 PCRs. The Trusted Computing Group publishes a guideline of what part of system software should be extended to specific PCR indices in the PC Client specification. There is also a debug PCR at index 16 that can be reset to 0 by issuing the tpm2_pcrreset command. Other PCRs reset on TPM resets.
The fact that the only way to modify a PCR is to extend a hash and that the robustness mechanisms that prevent physical tampering of the values, make the TPM a root of trust for storage (RTS).
The initial values in a PCR index is determined by the platform-specific specification. Values at a given PCR index can be read using the tpm2_pcrread tool and specifying the PCR-selection string. Relevant to our example, let’s read PCR indices 0,1,2 in SHA1 and SHA256 banks.
tpm2_pcrread sha1:0,1,2+sha256:0,1,2
If running it on a simulator, the PCR are in the initial state, in that the PCR are not extended just yet. Hence, the default values (00/ FF) should be displayed as shown below.
SHA1
0 : 0000000000000000000000000000000000000000
1 : 0000000000000000000000000000000000000000
2 : 0000000000000000000000000000000000000000
SHA256
0 : 0000000000000000000000000000000000000000000000000000000000000000
1 : 0000000000000000000000000000000000000000000000000000000000000000
2 : 0000000000000000000000000000000000000000000000000000000000000000
A PCR index can be modified only by extending it. In that, it cannot be set to an absolute value like a register. One of the three tools can modify the PCR values, namely:
In the case of tpm2_pcrextend and tpm2_pcrevent, the TPM ultimately concatenates the incoming data-digest with the current value at the PCR index, hashes the concatenation-data and replaces the PCR index value in place.
It should be noted that the entity extending the PCR is inherently trusted. This trusted entity is called a Root-of-trust-for-measurement (RTM) in TCG terminology. Intel (R) Bootguard is an example. The tamper resistant code extends the initial measurements of the critical portions of the pre-boot software.
Relevant to our example scripts in this tutorial below is an example of how to use tpm2_pcrextend to modify PCR indices.
#
# Let's suppose the critical portion of software to be extended is a plain text
# "CRITICAL-DATA". We need to first calculate the hash using a crypto lib/tool
# like openssl and then pass it to the tpm2_pcrextend command.
#
SHA256_DATA=`echo "CRITICAL-DATA" | openssl dgst -sha256 -binary | xxd -p -c 32`
SHA1_DATA=`echo "CRITICAL-DATA" | openssl dgst -sha1 -binary | xxd -p -c 20`
tpm2_pcrextend 0:sha1=$SHA1_DATA,sha256=$SHA256_DATA
tpm2_pcrextend 1:sha1=$SHA1_DATA,sha256=$SHA256_DATA
tpm2_pcrextend 2:sha1=$SHA1_DATA,sha256=$SHA256_DATA
# Let's read the PCR values now
tpm2_pcrread sha1:0,1,2+sha256:0,1,2
#
# The new PCR values should be as follows:
#sha1:
# 0 : 0xA3EBF00F6520B2C85DBBF3D32B6A8B3A30ABB748
# 1 : 0xA3EBF00F6520B2C85DBBF3D32B6A8B3A30ABB748
# 2 : 0xA3EBF00F6520B2C85DBBF3D32B6A8B3A30ABB748
#sha256:
# 0 : 0xAF42D77065F4791B6738DA5944E6B4074E3190F0993B5EE5D42DC4FBED424ABA
# 1 : 0xAF42D77065F4791B6738DA5944E6B4074E3190F0993B5EE5D42DC4FBED424ABA
# 2 : 0xAF42D77065F4791B6738DA5944E6B4074E3190F0993B5EE5D42DC4FBED424ABA
#
# Let's see how we got one of these values as a demonstration of PCR extension.
INITIAL_SHA1_DATA="0000000000000000000000000000000000000000"
CONCATENATED=`echo -ne $INITIAL_SHA1_DATA; echo $SHA1_DATA`
echo $CONCATENATED | xxd -r -p | openssl dgst -sha1
# This should output 0xA3EBF00F6520B2C85DBBF3D32B6A8B3A30ABB748
The term golden/ reference is not a formal terminology. It is simply a digest of the content of the PCR indices in a PCR selection. The selection is a choice of PCR indices across all the PCR banks. Values in a PCR index itself is a digest of the binary blob of software/ data that represents a known good state. The digest algorithm for individual PCR indices is the same as that of the specific bank. Whereas, the digest of the golden/ reference state has to be specified.
The golden/reference PCR state can be calculated in one of three ways:
tpm2_quote \
--key-context rsa_ak.ctx \
--pcr-list sha1:0,1,2+sha256:0,1,2 \
--message pcr_quote.plain \
--signature pcr_quote.signature \
--qualification SERVICE_PROVIDER_NONCE \
--hash-algorithm sha256 \
--pcr pcr.bin
GOLDEN_PCR=`cat pcr.bin | openssl dgst -sha256 -binary | xxd -p -c 32`
tpm2_pcrread sha1:0,1,2+sha256:0,1,2 -o pcr.bin
GOLDEN_PCR=`cat pcr.bin | openssl dgst -sha256 -binary | xxd -p -c 32`
CONCATENATE_ALL_DIGESTS=`\
echo $SHA1_DATA
echo $SHA1_DATA
echo $SHA1_DATA
echo $SHA256_DATA
echo $SHA256_DATA
echo $SHA256_DATA
`
GOLDEN_PCR=`\
echo $CONCATENATE_ALL_DIGESTS |
xxd -r -p | openssl -dgst -sha256 -binary | xxd -p -c 32
`
Using any of the 3 methods above the GOLDEN_PCR value is “e756e3af77a4f15a3f2ed489a7411a93d91d619506b6d1ed1121faaeaf45d8de”.
In a nutshell, this is a single digest aka measurement of interesting/ critical pieces of system software. To do this, a trusted portion of the software does the following:
Note: The trusted portion of the system software extending measurements is termed root-of-trust-for-measurement (RTM). This is inherently trusted.
Throughout the platform boot process, a log of all executable code and relevant configuration data is created and extended into PCRs. Each time a PCR is extended, a log entry is made in the TCG event log. This allows a challenger to see how the final PCR digests were built. The event log can be examined using the tpm2_eventlog tool. The event log is typically stored at this location /sys/kernel/security/tpm0/binary_bios_measurements.
The private or sensitive portion of the TPM key object is protected by the TPM. Couple this with authentication and enhanced authorization models the TPM design affords, the signatures generated from such keys are deemed trustworthy. As discussed earlier that identity is yet another facet to the trustworthiness of the signature and hence the data. We also determined that in order to preserve the privacy of the end user there is need for both anonymous identity and a unique identity that are cryptographically bound. Together these requirements form a criterion for defining a TPM object and these properties make the TPM a root of trust for reporting (RTR).
Of the four hierarchies the TPM is partitioned into, the endorsement hierarchy is to be used by the privacy administrator. The primary key created under the endorsement hierarchy provides the unique identity and is called the endorsement key (EK). Following properties make EKs special amongst other primary keys that can be created under any of the hierarchies:
The anonymous identity is the attestation identity key (AIK) created with the EK as its parent. There is no specific key template that is mandated by TCG that determines the AIK key attributes or authorization model. Since the key is typically used in privacy sensitive operations like quoting/ signing/ certifying , the key is a signing key created under endorsement hierarchy with privacy administrator controls. And so it’s authorization model typically references the authorization of the endorsement hierarchy through a policy. The association of the AIK to an EK is done using a cryptographical method called credential activation. Unlike EK which is a constant/ unique primary key that can be re-created, the AIK keys are ephemeral. In that, every time an AIK is created it results in a brand-new key and thus makes the key anonymous. The AIK as mentioned is a child of the EK primary key and can be created with the tool tpm2_create. Alternatively, the key can also be created using the tpm2_createak in order to simply attestation. The tool implements the TPM2_CC_Create command with the most commonly used AIK properties.
In summary,
Note: System software state can also be deemed as a system’s trusted identity without requiring a signing key. Such an identity is only useful for self attestation of the system. This is also known as local attestation. Local attestation has use cases like sealing-encryption-secrets or using PCR state as proxy authentication model to make authorization for TPM objects valid as long as system identity aka system software state does not change.
The primary goals for the minimal bare bone remote attestation are:
To achieve the goals it is sufficient to have 3 players, namely:
In practice, however the various roles as shown here can be further broken down. As an example, all the verifications for anonymous-identity and system-software state can be handed off to an additional “Verifier“ role.
Let’s now look at the various stages of our example attestation framework.
This is the stage where the “Device-Node“ requests services from the “Service-Provider“. The “Service-Provider“ has two requirements at this stage.
In order to verify that #1 & #2 above happened, the “Service-Provider“ creates an ephemeral secret called REGISTRATION-TOKEN that it shares only with the Privacy-CA who in turn reveals it to the “Device-Node“ if and only if all verifications on EK-certificate and Credential-Activation pass which are only possible if “Device-Node“ communicates the platform EK and AIK with the Privacy-CA. The “Device-Node“ then presents the REGISTRATION-TOKEN to the “Service-Provider“ to complete registration.
Note: The “Service-Provider“ has no further information recorded about the platform at the end of the registration. It only knows that a registration request was made by some platform and that it’s EK is registered in the pool of valid EKs with the Privacy-CA.
The sequence diagram below shows the interactions during the registration.

This is the stage where a registered platform makes a service request to the “Service-Provider“ and sends an ephemeral AIK to securely procure the services. The “Service-Provider“ needs to know the following:
In order to achieve this, The Privacy-CA needs to request the EK from the platform and also ensure the AIK is present on the platform through the credential activation process.
In order to verify these interactions between the “Privacy-CA“ and the “Device-Node“ occurred, the “Service-Provider“ creates an ephemeral secret called SERVICE-TOKEN that it shares with the “Privacy-CA“ who in turn reveals it to the “Device-Node“ after a successful credential activation process. The “Device-Node“ now proves the validity of the service request by presenting the SERVICE-TOKEN back to the “Service-Provider“.
The sequence diagram below shows the interactions during the service-request.

At this stage, by validating the “SERVICE-TOKEN” presented by the device, the “Service-Provider“ has ascertained the AIK comes from a registered platform and that it is a trusted signing key. Now the “Service-Provider“ needs to assured of the system-software state of the “Device-Node“. To achieve this the “Service-Provider“ requests an attestation quote from the “Device-Node“ that has to be signed with the AIK. In order to prevent replay attacks, the “Service-Provider“ generates a NONCE that needs to be added to the attestation quote before signing.
Below is a YAML representation of PCR included attestation data returned by tpm2_quote.
TPM2B_ATTEST_DATA:
size
attestationData:
magic
type
qualifiedSigner
extraData
clockInfo:
clock
resetCount
restartCount
safe
firmwareVersion
quote:
pcrSelect
count
pcrSelections[TPM2_NUM_PCR_BANKS]:
hash
sizeofSelect
pcrSelect[TPM2_PCR_SELECT_MAX]
pcrDigest
Shown above in yaml representation is all the information included in an attestation quote. Brief description of the important fields follow:
magic: the indication that this structure was created by a TPM always TPM2_GENERATED_VALUE.
type: The type of the attestation structure. It is TPM2_ST_ATTEST_QUOTE for the type that has the PCR information and being discussed at length here in.
qualifiedSigner: Qualified Name of the signing key. The term qualified name is the digest of all the Names of all the ancestor keys back to the Primary Seed at the root of the hierarchy.
extraData: External information supplied by caller. The NONCE generated by the “Service-Provider“ is added here in this field.
Clock: The time in milliseconds during which the TPM has been powered. This value is reset to zero when the Storage Primary Seed is changed TPM2_Clear.
resetCount: The number of occurrences of TPM Reset since the last TPM2_Clear.
restartCount: The number of times that TPM2_Shutdown or _TPM_Hash_Start have occurred since the last TPM Reset or TPM2_Clear.
Safe: Indicates that no value of Clock greater than the current value of Clock has been previously reported by the TPM. Set to YES on TPM2_Clear.
firmwareVersion: TPM vendor-specific value identifying the version number of the firmware.
pcrSelect: The information on algID, PCR selected, and the digest.
count: The number of selection structures. A value of zero is allowed. This indicates the count of selected PCR banks (SHA1, SHA256, etc.)
hash: The hash algorithm associated with the selection bank.
sizeofSelect: The size in octets of the pcrSelect array. This indicates number of bytes required to represent all the PCRs in a bank. Every PCR is represented as a bit. E.g. For 24 PCRs per bank the sizeofselect should be 3 bytes.
pcrSelect: The bit map of selected PCR (the least significant byte first)
Below is an example dump from an attestation quote for PCR0 from SHA1 bank and PCR0, PCR1 from SHA256 bank.
magic: ff544347
type: 8018
qualifiedSigner: 000bfebea9500be6aff07565dc09537ae5c887e1cc550a1a4653a618f86486ac28fe
extraData: 12345678
clockInfo:
clock: 2260078
resetCount: 0
restartCount: 0
safe: 1
firmwareVersion: 3636160019061720
attested:
quote:
pcrSelect:
count: 2
pcrSelections:
0:
hash: 11 (sha256)
sizeofSelect: 3
pcrSelect: 010000
1:
hash: 4 (sha1)
sizeofSelect: 3
pcrSelect: 030000
pcrDigest: 834a709ba2534ebe3ee1397fd4f7bd288b2acc1d20a08d6c862dcd99b6f04400
The sequence diagram below shown the interactions during this stage.

At this stage, by validating the “SERVICE-TOKEN“ and the attestation-quote it
received from the “Device-Node“, the “Service-Provider“ has ascertained the
“Device-Node“ anonymous identity and system-software state. With the system in
a known state, the “Service-Provider“ can now wrap the “SERVICE-SECRET“ with a service-content-key signed by the
device AIK.

The following demonstration shows the tpm2-tools involved in the sequence diagrams discussed above. Following the demonstration of the tpm2-tools in this section, there is a subsequent section showing the scripts for the different roles and their interactions in the simple attestation framework. It involves scripting the tools and the logic to move identities, quotes, tokens, nonce, etc. between the “Service-Provider“, “Device-Node“ and the “Privacy-CA“.
With that preface, let’s dive straight into the tpm2-tools.
tpm2_createek \
--ek-context rsa_ek.ctx \
--key-algorithm rsa \
--public rsa_ek.pub
tpm2_createak \
--ek-context rsa_ek.ctx \
--ak-context rsa_ak.ctx \
--key-algorithm rsa \
--hash-algorithm sha256 \
--signing-algorithm rsassa \
--public rsa_ak.pub \
--private rsa_ak.priv \
--ak-name rsa_ak.name
# Location 1 - TPM2 NV Index 0x1c00002 is the TCG specified location for RSA-EK-certificate.
RSA_EK_CERT_NV_INDEX=0x01C00002
NV_SIZE=`tpm2_nvreadpublic $RSA_EK_CERT_NV_INDEX | grep size | awk '{print $2}'`
tpm2_nvread \
--hierarchy owner \
--size $NV_SIZE \
--output rsa_ek_cert.bin \
$RSA_EK_CERT_NV_INDEX
# Location 2 - Web hosting. This applies specifically to Intel(R) PTT RSA-EK-certificate.
tpm2_getekcertificate \
--ek-public rsa_ek.pub \
--offline \
--allow-unverified \
--ek-certificate rsa_ek_cert.bin
## convert to a standard DER format
sed 's/-/+/g;s/_/\//g;s/%3D/=/g;s/^{.*certificate":"//g;s/"}$//g;' \
rsa_ek_cert.bin | base64 --decode > rsa_ek_cert.bin
# Privacy-CA creating the wrapped credential and encryption key
file_size=`stat --printf="%s" rsa_ak.name`
loaded_key_name=`cat rsa_ak.name | xxd -p -c $file_size`
echo "this is my secret" > file_input.data
tpm2_makecredential \
--tcti none \
--encryption-key rsa_ek.pub \
--secret file_input.data \
--name $loaded_key_name \
--credential-blob cred.out
# Device-Node activating the credential
tpm2_startauthsession \
--policy-session \
--session session.ctx
TPM2_RH_ENDORSEMENT=0x4000000B
tpm2_policysecret -S session.ctx -c $TPM2_RH_ENDORSEMENT
tpm2_activatecredential \
--credentialedkey-context rsa_ak.ctx \
--credentialkey-context rsa_ek.ctx \
--credential-blob cred.out \
--certinfo-data actcred.out \
--credentialkey-auth "session:session.ctx"
tpm2_flushcontext session.ctx
echo "12345678" > SERVICE_PROVIDER_NONCE
tpm2_quote \
--key-context rsa_ak.ctx \
--pcr-list sha1:0,1,2+sha256:0,1,2 \
--message pcr_quote.plain \
--signature pcr_quote.signature \
--qualification SERVICE_PROVIDER_NONCE \
--hash-algorithm sha256 \
--pcr pcr.bin
tpm2_checkquote \
--public rsa_ak.pub \
--message pcr_quote.plain \
--signature pcr_quote.signature \
--qualification SERVICE_PROVIDER_NONCE \
--pcr pcr.bin
#!/bin/bash
# Fixed location
service_provider_location="$PWD/../SP"
# PCA location
privacy_ca_location=""
# Location for node 1, node 2, etc.
device_location="$PWD"
# State
event_file_found=0
device_registration_request=0
device_service_request=0
wait_loop() {
counter=1
until [ $counter -gt $1 ]
do
test -f $2
if [ $? == 0 ];then
event_file_found=1
break
else
echo -ne "Waiting $1 seconds: $counter"'\r'
fi
((counter++))
sleep 1
done
}
LOG_ERROR() {
errorstring=$1
echo -e "\033[31mFAIL: \e[97m${errorstring}\e[0m"
}
LOG_INFO() {
messagestring=$1
echo -e "\033[93mPASS: \e[97m${messagestring}\e[0m"
}
await_and_compelete_credential_challenge() {
# Wait for credential challenge
cred_status_string="Encrypted credential receipt from Privacy-CA."
max_wait=60
wait_loop $max_wait cred.out
if [ $event_file_found == 0 ];then
LOG_ERROR "$cred_status_string"
return 1
fi
event_file_found=0
LOG_INFO "$cred_status_string"
tpm2_startauthsession --policy-session --session session.ctx -Q
TPM2_RH_ENDORSEMENT=0x4000000B
tpm2_policysecret -S session.ctx -c $TPM2_RH_ENDORSEMENT -Q
tpm2_activatecredential --credentialedkey-context rsa_ak.ctx \
--credentialkey-context rsa_ek.ctx --credential-blob cred.out \
--certinfo-data actcred.out --credentialkey-auth "session:session.ctx" -Q
rm -f cred.out
tpm2_flushcontext session.ctx -Q
rm -f session.ctx
}
device_registration() {
# Send device location to service-provider
echo "device_location: $device_location" > d_s_registration.txt
cp d_s_registration.txt $service_provider_location/.
rm -f d_s_registration.txt
# Wait for PCA location information from service provider
max_wait=60
wait_loop $max_wait s_d_registration.txt
registration_status_string="Privacy-CA information receipt from Service-Provider."
if [ $event_file_found == 0 ];then
LOG_ERROR "$registration_status_string"
return 1
fi
event_file_found=0
LOG_INFO "$registration_status_string"
privacy_ca_location=`grep privacy_ca_location s_d_registration.txt | \
awk '{print $2}'`
rm -f s_d_registration.txt
registration_status_string="Acknowledgement reciept from Privacy-CA."
wait_loop $max_wait p_d_pca_ready.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$registration_status_string"
return 1
fi
event_file_found=0
LOG_INFO "$registration_status_string"
rm -f p_d_pca_ready.txt
#Ready EKcertificate, EK and AIK and set ready status so PCA can pull
tpm2_createek --ek-context rsa_ek.ctx --key-algorithm rsa \
--public rsa_ek.pub -Q
tpm2_createak \
--ek-context rsa_ek.ctx \
--ak-context rsa_ak.ctx \
--key-algorithm rsa \
--hash-algorithm sha256 \
--signing-algorithm rsassa \
--public rsa_ak.pub \
--private rsa_ak.priv \
--ak-name rsa_ak.name \
-Q
tpm2_readpublic -c rsa_ak.ctx -f pem -o rsa_ak.pub -Q
touch fake_ek_certificate.txt
touch d_p_device_ready.txt
cp d_p_device_ready.txt $privacy_ca_location/.
rm -f d_p_device_ready.txt
registration_status_string="Credential activation challenge."
await_and_compelete_credential_challenge
if [ $? == 0 ];then
LOG_INFO "$registration_status_string"
cp actcred.out $privacy_ca_location/.
rm -f actcred.out
return 0
else
LOG_ERROR "$registration_status_string"
return 1
fi
}
request_device_registration () {
device_registration
if [ $? == 1 ];then
return 1
fi
device_registration_status_string="Registration token receipt from Privacy-CA."
max_wait=60
wait_loop $max_wait p_d_registration_token.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$device_registration_status_string"
return 1
fi
LOG_INFO "$device_registration_status_string"
event_file_found=0
cp p_d_registration_token.txt \
$service_provider_location/d_s_registration_token.txt
rm -f p_d_registration_token.txt
return 0
}
#
# Request service with the Service-Provider
# Read the Privacy-CA location from Service-Provider
# Deliver EK, AIK, EKcertificate to the Privacy-CA
# Complete credential challenge with the Privacy-CA
# Retrieve the SERVICE-TOKEN from the Privacy-CA
# Present the SEVICE-TOKEN to the Service-Provider
#
process_device_anonymous_identity_challenge() {
# Start device service
test -f $device_service_aik
if [ $? == 1 ];then
LOG_ERROR "Aborting service request - AIK could not be found."
return 1
else
echo "device_location: $device_location" > d_s_service.txt
cp d_s_service.txt $service_provider_location/.
rm -f d_s_service.txt
cp $device_service_aik $service_provider_location/d_s_service_aik.pub
fi
identity_challenge_status_string="Privacy-CA information receipt from Service-Provider."
max_wait=60
wait_loop $max_wait s_d_service.txt
if [ $event_file_found == 1 ];then
event_file_found=0
privacy_ca_location=`grep privacy_ca_location s_d_service.txt | \
awk '{print $2}'`
rm -f s_d_service.txt
LOG_INFO "$identity_challenge_status_string"
else
LOG_ERROR "$identity_challenge_status_string"
return 1
fi
identity_challenge_status_string="Acknowledgement receipt from Privacy-CA."
wait_loop $max_wait p_d_pca_ready.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$identity_challenge_status_string"
return 1
fi
LOG_INFO "$identity_challenge_status_string"
event_file_found=0
rm -f p_d_pca_ready.txt
touch d_p_device_ready.txt
cp d_p_device_ready.txt $privacy_ca_location/.
rm -f d_p_device_ready.txt
identity_challenge_status_string="Credential activation challenge."
await_and_compelete_credential_challenge
if [ $? == 0 ];then
LOG_INFO "$identity_challenge_status_string"
cp actcred.out $privacy_ca_location/.
rm -f actcred.out
else
LOG_ERROR "$identity_challenge_status_string"
rm -f actcred.out
return 1
fi
identity_challenge_status_string="Service-Token receipt from Privacy-CA."
wait_loop $max_wait p_d_service_token.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$identity_challenge_status_string"
return 1
fi
LOG_INFO "$identity_challenge_status_string"
event_file_found=0
cp p_d_service_token.txt \
$service_provider_location/d_s_service_token.txt
rm -f p_d_service_token.txt
return 0
}
process_device_software_state_validation_request() {
software_state_string="PCR selection list receipt from Service-Provider"
max_wait=60
wait_loop $max_wait s_d_pcrlist.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$software_state_string"
return 1
fi
LOG_INFO "$software_state_string"
event_file_found=0
pcr_selection=`grep pcr-selection s_d_pcrlist.txt | \
awk '{print $2}'`
service_provider_nonce=`grep nonce s_d_pcrlist.txt | \
awk '{print $2}'`
rm -f s_d_pcrlist.txt
tpm2_quote --key-context rsa_ak.ctx --message attestation_quote.dat \
--signature attestation_quote.signature \
--qualification "$service_provider_nonce" \
--pcr-list "$pcr_selection" \
--pcr pcr.bin -Q
cp attestation_quote.dat attestation_quote.signature pcr.bin \
$service_provider_location/.
return 0
}
process_encrypted_service_data_content() {
service_data_status_string="Encrypted service-data-content receipt from Service-Provider"
max_wait=6
wait_loop $max_wait s_d_service_content.encrypted
if [ $event_file_found == 0 ];then
LOG_ERROR "$service_data_status_string"
return 1
fi
LOG_INFO "$service_data_status_string"
event_file_found=0
service_data_status_string="Decryption of service-data-content receipt from Service-Provider"
tpm2 rsadecrypt -c service_content_key.ctx -o s_d_service_content.decrypted \
s_d_service_content.encrypted -Q
if [ $? == 1 ];then
LOG_ERROR "$service_data_status_string"
rm -f s_d_service_content.encrypted
return 1
fi
LOG_INFO "$service_data_status_string"
SERVICE_CONTENT=`cat s_d_service_content.decrypted`
LOG_INFO "Service-content: \e[5m$SERVICE_CONTENT"
rm -f s_d_service_content.*
return 0
}
process_generate_service_content_key() {
tpm2_create \
-C n \
-c service_content_key.ctx \
-u service_content_key.pub \
-r service_content_key.priv \
-Q
tpm2_readpublic \
-c service_content_key.ctx \
-f pem \
-o d_s_service_content_key.pub \
-Q
cp d_s_service_content_key.pub $service_provider_location/.
tpm2_sign \
-c rsa_ak.ctx \
-g sha256 \
-s rsassa \
-f plain \
-o d_s_service_content_key_pub.sig \
d_s_service_content_key.pub
cp d_s_service_content_key_pub.sig $service_provider_location/.
return 0
}
request_device_service() {
request_service_status_string="Device anonymous identity challenge."
process_device_anonymous_identity_challenge
if [ $? == 1 ];then
LOG_ERROR "$request_service_status_string"
return 1
fi
LOG_INFO "$request_service_status_string"
request_service_status_string="Device software state validation"
process_device_software_state_validation_request
if [ $? == 1 ];then
LOG_ERROR "$request_service_status_string"
return 1
fi
LOG_INFO "$request_service_status_string"
request_service_status_string="Generating certified service key"
process_generate_service_content_key
if [ $? == 1 ];then
LOG_ERROR "$request_service_status_string"
return 1
fi
LOG_INFO "$request_service_status_string"
request_service_status_string="Service data content processing"
process_encrypted_service_data_content
if [ $? == 1 ];then
LOG_ERROR "$request_service_status_string"
return 1
fi
return 0
}
tput sc
read -r -p "Demonstration purpose only, not for production. Continue? [y/N] " response
tput rc
tput el
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
echo "===================== DEVICE-NODE ====================="
else
exit
fi
while getopts ":hrt:" opt; do
case ${opt} in
h )
echo "Pass 'r' for registration or 't' for service request"
;;
r )
device_registration_request=1
;;
t )
device_service_request=1
device_service_aik=$OPTARG
;;
esac
done
shift $(( OPTIND - 1 ))
if [ $device_registration_request == 1 ];then
if [ $device_service_request == 1 ];then
echo "Specify either 'registration' or 'service' request not both"
exit 1
fi
fi
status_string="Device registration request."
if [ $device_registration_request == 1 ];then
request_device_registration
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
LOG_INFO "$status_string"
fi
status_string="Device service request."
if [ $device_service_request == 1 ];then
request_device_service
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
fi
if [ $device_registration_request == 0 ];then
if [ $device_service_request == 0 ];then
echo "Usage: device-node.sh [-h] [-r] [-t AIK.pub]"
exit 1
fi
fi
# No errors
exit 0
#!/bin/bash
# Fixed location
pca_location="$PWD/../PCA"
# Device Location not fixed
device_location=""
# State
event_file_found=0
device_registration_request=0
device_service_request=0
# Attestation Data
GOLDEN_PCR_SELECTION="sha1:0,1,2+sha256:0,1,2"
GOLDEN_PCR="59bf9091f4cbbd2a8796bfe086a501c57226c42739dcf8ad323e7493ad51e38f"
# Service Data
SERVICE_CONTENT="Hello world!"
wait_loop() {
counter=1
until [ $counter -gt $1 ]
do
test -f $2
if [ $? == 0 ];then
event_file_found=1
break
else
echo -ne "Waiting $1 seconds: $counter"'\r'
fi
((counter++))
sleep 1
done
}
LOG_ERROR() {
errorstring=$1
echo -e "\033[31mFAIL: \e[97m${errorstring}\e[0m"
}
LOG_INFO() {
messagestring=$1
echo -e "\033[93mPASS: \e[97m${messagestring}\e[0m"
}
device_registration() {
REGISTRATION_TOKEN=`dd if=/dev/urandom bs=1 count=32 status=none | \
xxd -p -c32`
device_location=`grep device_location d_s_registration.txt | \
awk '{print $2}'`
rm -f d_s_registration.txt
data_to_privacy_ca="
device_location: $device_location
registration_token: $REGISTRATION_TOKEN
"
echo "$data_to_privacy_ca" > s_p_registration.txt
cp s_p_registration.txt $pca_location/.
rm -f s_p_registration.txt
# Send privacy-CA information to device
echo "privacy_ca_location: $pca_location" > s_d_registration.txt
cp s_d_registration.txt $device_location/.
rm -f s_d_registration.txt
# Wait for device_registration_token from device
registration_status_string="Registration-Token reciept from device."
wait_loop $max_wait d_s_registration_token.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$registration_status_string"
return 1
fi
LOG_INFO "$registration_status_string"
event_file_found=0
test_registration_token=`grep registration_token \
d_s_registration_token.txt | awk '{print $2}'`
rm -f d_s_registration_token.txt
registration_status_string="Registration-Token validation"
if [ $test_registration_token == $REGISTRATION_TOKEN ];then
LOG_INFO "$registration_status_string"
return 0
else
LOG_ERROR "$registration_status_string"
return 1
fi
}
device_node_identity_challenge() {
SERVICE_TOKEN=`dd if=/dev/urandom bs=1 count=32 status=none | \
xxd -p -c32`
device_location=`grep device_location d_s_service.txt | \
awk '{print $2}'`
rm -f d_s_service.txt
data_to_privacy_ca="
device_location: $device_location
service_token: $SERVICE_TOKEN
"
echo "$data_to_privacy_ca" > s_p_service.txt
cp s_p_service.txt $pca_location/.
rm -f s_p_service.txt
# Send privacy-CA information to device
echo "privacy_ca_location: $pca_location" > s_d_service.txt
cp s_d_service.txt $device_location
rm -f s_d_service.txt
identity_challenge_status_string="Aborting service request - AIK not found."
test -f d_s_service_aik.pub
if [ $? == 1 ];then
LOG_ERROR "$identity_challenge_status_string"
return 1
else
cp d_s_service_aik.pub $pca_location/s_p_service_aik.pub
fi
identity_challenge_status_string="Service-Token receipt from device."
wait_loop $max_wait d_s_service_token.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$identity_challenge_status_string"
return 1
fi
LOG_INFO "$identity_challenge_status_string"
event_file_found=0
test_service_token=`grep service-token \
d_s_service_token.txt | awk '{print $2}'`
rm -f d_s_service_token.txt
identity_challenge_status_string="Service-Token validation."
if [ $test_service_token == $SERVICE_TOKEN ];then
LOG_INFO "$identity_challenge_status_string"
return 0
fi
LOG_ERROR "$identity_challenge_status_string"
return 1
}
system_software_state_validation() {
rm -f attestation_quote.dat attestation_quote.signature
echo "pcr-selection: $GOLDEN_PCR_SELECTION" > s_d_pcrlist.txt
NONCE=`dd if=/dev/urandom bs=1 count=32 status=none | xxd -p -c32`
echo "nonce: $NONCE" >> s_d_pcrlist.txt
cp s_d_pcrlist.txt $device_location/.
rm -f s_d_pcrlist.txt
software_status_string="Attestation data receipt from device"
max_wait=60
wait_loop $max_wait attestation_quote.dat
if [ $event_file_found == 0 ];then
LOG_ERROR "$software_status_string"
return 1
fi
LOG_INFO "$software_status_string"
event_file_found=0
software_status_string="Attestation signature receipt from device"
max_wait=60
wait_loop $max_wait attestation_quote.signature
if [ $event_file_found == 0 ];then
LOG_ERROR "$software_status_string"
return 1
fi
LOG_INFO "$software_status_string"
event_file_found=0
software_status_string="Attestation quote signature validation"
tpm2_checkquote --public d_s_service_aik.pub --qualification "$NONCE" \
--message attestation_quote.dat --signature attestation_quote.signature \
--pcr pcr.bin -Q
retval=$?
rm -f attestation_quote.signature
if [ $retval == 1 ];then
LOG_ERROR "$software_status_string"
return 1
fi
LOG_INFO "$software_status_string"
software_status_string="Verification of PCR from quote against golden reference"
testpcr=`tpm2_print -t TPMS_ATTEST attestation_quote.dat | \
grep pcrDigest | awk '{print $2}'`
rm -f attestation_quote.dat
if [ "$testpcr" == "$GOLDEN_PCR" ];then
LOG_INFO "$software_status_string"
else
LOG_ERROR "$software_status_string"
echo -e " \e[97mDevice-PCR: $testpcr\e[0m"
echo -e " \e[97mGolden-PCR: $GOLDEN_PCR\e[0m"
return 1
fi
return 0
}
device_service_content_key_validation() {
request_service_content_key_string="Retrieving service content key from device"
max_wait=60
wait_loop $max_wait d_s_service_content_key.pub
if [ $event_file_found == 0 ];then
LOG_ERROR "$request_service_content_key_string"
return 1
fi
event_file_found=0
LOG_INFO "$request_service_content_key_string"
max_wait=60
wait_loop $max_wait d_s_service_content_key_pub.sig
if [ $event_file_found == 0 ];then
LOG_ERROR "$request_service_content_key_string"
return 1
fi
event_file_found=0
LOG_INFO "$request_service_content_key_string"
openssl dgst -sha256 -binary d_s_service_content_key.pub > service_content_key.pub.digest
openssl pkeyutl \
-verify \
-in service_content_key.pub.digest \
-sigfile d_s_service_content_key_pub.sig \
-pubin \
-inkey d_s_service_aik.pub \
-keyform pem \
-pkeyopt digest:sha256
if [ $? == 1 ];then
return 1
fi
return 0
}
request_device_service() {
# Start device service registration with device identity challenge
request_device_service_status_string="Anonymous identity validation by Privacy-CA."
device_node_identity_challenge
if [ $? == 1 ];then
LOG_ERROR "$request_device_service_status_string"
rm -f d_s_service_aik.pub
return 1
fi
LOG_INFO "$request_device_service_status_string"
# Check the device software state by getting a device quote
request_device_service_status_string="Device system software validation."
system_software_state_validation
if [ $? == 1 ];then
LOG_ERROR "$request_device_service_status_string"
rm -f d_s_service_aik.pub
return 1
fi
LOG_INFO "$request_device_service_status_string"
# Verify service content key from the device
request_device_service_status_string="Device service content key validation."
device_service_content_key_validation
if [ $? == 1 ];then
LOG_ERROR "$request_device_service_status_string"
rm -f d_s_service_aik.pub
rm -f d_s_service_content_key.pub
return 1
fi
LOG_INFO "$request_device_service_status_string"
# Encrypt service data content and deliver
echo "$SERVICE_CONTENT" > service-content.plain
openssl rsautl -encrypt -inkey d_s_service_content_key.pub -pubin \
-in service-content.plain -out s_d_service_content.encrypted
cp s_d_service_content.encrypted $device_location/.
rm -f d_s_service_aik.pub
rm -f d_s_service_content_key.pub
rm -f s_d_service_content.encrypted
rm -f service-content.plain
LOG_INFO "Sending service-content: \e[5m$SERVICE_CONTENT"
return 0
}
tput sc
read -r -p "Demonstration purpose only, not for production. Continue? [y/N] " response
tput rc
tput el
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
echo "===================== SERVICE-PROVIDER ====================="
else
exit
fi
counter=1
max_wait=60
until [ $counter -gt $max_wait ]
do
! test -f d_s_registration.txt
device_registration_request=$?
! test -f d_s_service.txt
device_service_request=$?
status_string="Device registration request."
if [ $device_registration_request == 1 ];then
device_registration
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
LOG_INFO "$status_string"
break
elif [ $device_service_request == 1 ];then
status_string="Device service request."
request_device_service
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
LOG_INFO "$status_string"
break
else
echo -ne "Waiting $1 seconds: $counter"'\r'
fi
((counter++))
sleep 1
done
if [ $device_registration_request == 0 ];then
if [ $device_service_request == 0 ];then
LOG_ERROR "Exiting as there are no device requests to process"
exit 1
fi
fi
# No errors
exit 0
#!/bin/bash
# Fixed location
service_provider_location="$PWD/../SP"
# Location for node 1, node 2, etc.
device_location=""
registration_token=""
# State
event_file_found=0
wait_loop() {
counter=1
until [ $counter -gt $1 ]
do
test -f $2
if [ $? == 0 ];then
event_file_found=1
break
else
echo -ne "Waiting $1 seconds: $counter"'\r'
fi
((counter++))
sleep 1
done
}
LOG_ERROR() {
errorstring=$1
echo -e "\033[31mFAIL: \e[97m${errorstring}\e[0m"
}
LOG_INFO() {
messagestring=$1
echo -e "\033[93mPASS: \e[97m${messagestring}\e[0m"
}
process_device_registration_request_from_service_provider() {
device_location=`grep device_location s_p_registration.txt | \
awk '{print $2}'`
registration_token=`grep registration_token s_p_registration.txt | \
awk '{print $2}'`
rm -f s_p_registration.txt
return 0
}
credential_challenge() {
file_size=`stat --printf="%s" rsa_ak.name`
loaded_key_name=`cat rsa_ak.name | xxd -p -c $file_size`
echo "this is my secret" > file_input.data
tpm2_makecredential \
--tcti none \
--encryption-key rsa_ek.pub \
--secret file_input.data \
--name $loaded_key_name \
--credential-blob cred.out \
-Q
cp cred.out $device_location/.
credential_status_string="Activated credential receipt from device."
max_wait=60
wait_loop $max_wait actcred.out
if [ $event_file_found == 0 ];then
LOG_ERROR "$credential_status_string"
return 1
fi
LOG_INFO "$credential_status_string"
event_file_found=0
diff file_input.data actcred.out
test=$?
rm -f rsa_ak.* file_input.data actcred.out cred.out
credential_status_string="Credential activation challenge."
if [ $test == 0 ];then
LOG_INFO "$credential_status_string"
return 0
else
LOG_ERROR "$credential_status_string"
return 1
fi
}
process_device_registration_processing_with_device() {
touch p_d_pca_ready.txt
cp p_d_pca_ready.txt $device_location/.
rm -f p_d_pca_ready.txt
process_registration_status_string="Device-ready acknowledgement receipt from device."
max_wait=60
wait_loop $max_wait d_p_device_ready.txt
if [ $event_file_found == 0 ];then
LOG_ERROR "$process_registration_status_string"
return 1
fi
LOG_INFO "$process_registration_status_string"
event_file_found=0
rm -f d_p_device_ready.txt
cp $device_location/rsa_ek.pub .
#cp $device_location/rsa_ak.pub .
cp $device_location/rsa_ak.name .
LOG_INFO "Received EKcertificate EK and AIK from device"
credential_challenge
if [ $? == 1 ];then
return 1
fi
return 0
}
request_device_registration() {
mkdir -p Registered_EK_Pool
registration_request_status_string="Device info and registration-token receipt from service-provider."
process_device_registration_request_from_service_provider
if [ $? == 1 ];then
LOG_ERROR "$registration_request_status_string"
return 1
fi
LOG_INFO "$registration_request_status_string"
registration_request_status_string="Registration-token dispatch to device."
process_device_registration_processing_with_device
if [ $? == 1 ];then
LOG_ERROR "$registration_request_status_string"
return 1
else
LOG_INFO "$registration_request_status_string"
echo "registration_token: $registration_token" > \
p_d_registration_token.txt
cp p_d_registration_token.txt $device_location/.
rm -f p_d_registration_token.txt
fi
mv rsa_ek.pub Registered_EK_Pool/$registration_token
fdupes --recurse --omitfirst --noprompt --delete --quiet \
Registered_EK_Pool | grep -q rsa_ek.pub
return 0
}
request_device_service() {
device_location=`grep device_location s_p_service.txt | \
awk '{print $2}'`
service_token=`grep service_token s_p_service.txt | \
awk '{print $2}'`
rm -f s_p_service.txt
cp s_p_service_aik.pub $device_location/rsa_ak.pub
rm -f s_p_service_aik.pub
process_device_registration_processing_with_device
if [ $? == 1 ];then
LOG_ERROR "AIK received from service provider is not on the device"
return 1
fi
cp rsa_ek.pub Registered_EK_Pool
fdupes --recurse --omitfirst --noprompt --delete --quiet \
Registered_EK_Pool | grep -q rsa_ek.pub
retval=$?
rm -f rsa_ek.pub Registered_EK_Pool/rsa_ek.pub
if [ $retval == 1 ];then
LOG_ERROR "EK from device does not belong to the registered EK pool"
return 1
fi
echo "service-token: $service_token" > p_d_service_token.txt
cp p_d_service_token.txt $device_location
rm -f p_d_service_token.txt
return 0
}
tput sc
read -r -p "Demonstration purpose only, not for production. Continue? [y/N] " response
tput rc
tput el
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
echo "===================== PRIVACY-CA ====================="
else
exit
fi
device_registration_request=0
device_service_request=0
counter=1
max_wait=60
until [ $counter -gt $max_wait ]
do
! test -f s_p_registration.txt
device_registration_request=$?
! test -f s_p_service.txt
device_service_request=$?
if [ $device_registration_request == 1 ];then
status_string="Device registration request."
request_device_registration
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
LOG_INFO "$status_string"
break
elif [ $device_service_request == 1 ];then
status_string="Device service request received."
request_device_service
if [ $? == 1 ];then
LOG_ERROR "$status_string"
exit 1
fi
LOG_INFO "$status_string"
break
else
echo -ne "Waiting $1 seconds: $counter"'\r'
fi
((counter++))
sleep 1
done
if [ $device_registration_request == 0 ];then
if [ $device_service_request == 0 ];then
LOG_ERROR "Exiting as there are no service provider requests to process."
exit 1
fi
fi
# No errors
exit 0
If the EK or AIK had already been generated but the public key file isn’t available, is there a way to generate the public key?
It is possible to recreate public key from an already created EK or AIK key. There are two possible forms for a TPM object to reside on the TPM. Both of these eventually translate as a TPM handle number that can be invoked by specific TPM commands.
Persistent-handles: As the term implies, these reside in the TPM and can be invoked using TPM persistent-handle at location range 81xxxxxxx. Since these reside on the TPM NV, they survive TPM resets and restarts.
Transient handles, on the contrary, do not reside on the TPM NV. However, they are still protected objects that need additional steps of loading and validating integrity prior to their use. Such an object has 3 important parts before it is translated into a usable handle. a. The wrapped sensitive portion of the key object (termed as private in the tpm2-tools). b. The non-sensitive portion which includes key attributes, authorization policy digest, etc (termed as public in the tpm2-tools). c. A context blob that is generated when such an object is successfully created–>loaded–>offloaded from the TPM. Once ready to use the object, the context blob is referenced in the tpm2 commands that then get assigned a transient handle for use just like the persistent handles but at location range 80xxxxxx.
In either of the cases above, a tool called [tpm2_readpublic](tpm2_readpublic can be used to view and or dump a public portion in tss or pem format by passing the persistent handle or the context file as an input. In fact, this tool has been used in the device-node.sh scripts in this tutorial as well to generate a pem formatted file.
Imran Desai
]]>In an increasingly data heavy world we live in today, data security has become critical for connected-devices, corporations, and individuals alike. It is important to keep data protected at rest especially at source and also in transit. Hence digital encryption schemes have become paramount for protecting the confidentiality and integrity of the data. Of the multitude of encryption mechanisms for protecting the confidentiality AES XTS (cipher text stealing) mode is most commonly used. Encryption schemes function by virtue of protecting the encryption secret which is also termed encryption key.
The robustness rules for protecting the encryption secret or keys involves both physical access protection to the key data as well as access restrictions to sensitive operations with the key like decryption and digital signing enforced through authentication mechanisms. These requirements were termed as root of trust for storage (RTS) and root of trust for reporting (RTR) by the Trusted Computing Group (TCG). TCG is a consortium of industry and academic expertise coming together to define specifications for a security module that achieves these objectives. This security module is called the Trusted Platform Module or TPM. The TPM came about in two major standard specifications, the 1.2 and the now 2.0. Algorithm flexibility and enhanced authorization schemes are of the major advancements in 2.0. Secure persistent storage, platform configuration register or PCR for recording system software state(more on this later), a per TPM unique certified key for digital identity, and hierarchies for partitioning roles based access to TPM objects are of the notable TPM properties. TPM is a limited resourced device.
TPMs have a secure persistent storage of about 14KB and a smaller fast memory or
RAM to support a maximum 3 sessions that quickly exhaust depending on the
application requirement. In addition to the TPM specifications, TCG also created
a specification for a software stack to optimally function with the limited
resourced TPM device. This is called the Trusted Software Stack or TSS. The spec
defines not only the mechanisms and interfaces to manage the TPM resources, it
also details how the software partitions the stack into well defined interfaces
for device communication, command response buffer transport, access brokering,
resource management, session management and feature profiles for most common use
cases.
Feature API or FAPI:The abstraction should profile common use cases and
provides context management.
Enhanced System API or ESAPI or ESYS: Provides cryptographic functions for
sessions and session management.
System API or SAPI: A one to one mapping of all the available TPM2 commands.
While sufficient it requires expert TPM knowledge for managing sessions,
contexts and cryptographic functions. Marshalling and unmarshalling of command
and response buffer is also handled here.
Trusted Access Broker and Resource Manager or TAB/RM: Abstracts resource
limitation. In a multi application setup vying from TPM access, it ensures TPM
commands are singularly processed. There is a user space dbus based
implementation as well an in kernel implementation.
TPM Command Transmission Interface or TCTI: Decouples the API generating the
TPM command and responses and instead focuses on the transport mechanism.
Trusted Device Driver or TDD: This is the primary device interface handling
the command-response buffering, control registers and interface management. This
is part of the mainline kernel tree in the character device driver category.
The community supported tpm2-software project on github is an implementation of
the trusted software stack specification by TCG. It is getting widespread
adoption in the linux community and mainstream distributions. Following is a
growing list of the tpm2-software projects:
All the projects use autotools since it follows the normal build conventions and to make it easier to distribute code portably by creating automatic dependency generation. For all the projects above: bootstrap, configure make and make install are generally sufficient. The bootstrap and configure scripts should inform the user of missing libraries and packages. The user can then install them using the package manager specific to the distribution in use.
Once you have all the necessary tpm2 software installed, it is fairly simple to
create an RSA key pair and store it in the TPM persitent memory.
Create the RSA key
tpm2_createprimary --hierarchy=o --key-algorithm=rsa --key-context=prim.ctxSave it to the TPM persistent memory
{:start=”2”}
tpm2_evictcontrol --hierarchy=o --object-context=prim.ctx 0x81010001NOTE:
Disk encryption protects storage devices from an attacker with intention to dump sensitive information by mounting the storage device on alternative operating environments under attacker control. Of the objectives for disk encryption are:
To elicit the importance of protecting disk encryption keys on a TPM, let’s
first setup a disk image without encryption and see if we can extract user
generated content.
Create a disk image and write some content:
{:start=”3”}
dd if=/dev/zero of=plain.disk bs=1M count=10mkfs.ext4 plain.diskmkdir -p mountpointsudo mount plain.disk mountpointsudo touch mountpoint/plain.txtsudo chmod 777 mountpoint/plain.txtsudo echo "This is my plain text" > mountpoint/plain.txtsudo umount mountpointAt this point we have a disk with a file plain.txt which has the content “This
is my plain text”. Let’s suppose this is our private and sensitive data. We can
see that it is not protected by dumping the disk and looking at the content to
find the plain.txt. To do this simply do the following:
strings plain.diskNOTE:
Most standard Linux implementations use the LUKS specification. LUKS ensures
storage data confidentiality at rest. LUKS stores setup info in partition header
to aid easy migration. LUKS uses the kernel device mapper subsystem. 8 separate
authentication slots can be specified for single volume with support for
revocation. LUKS volumes can be automatically mounted at startup with standard
linux provisions like services, daemons, etc. Authentication/ Passphrase can be
provided as a password just in time (default) and or specified as key file
(command line argument). The cryptsetup user space utility aids creating and
managing LUKS volumes.
Let’s setup a new LUKS volume with a simple passphrase as key protector:
{:start=”12”}
dd if=/dev/zero of=enc.disk bs=1M count=10dd if=/dev/urandom of=disk.key bs=1 count=32loopdevice=$(losetup -f) && sudo losetup $loopdevice enc.disksudo cryptsetup luksFormat --key-file=disk.key $loopdeviceAt this point you have setup the luks volume and it should pop a warning about
overriding the data. Next let’s open the LUKS volume by authenticating with the
disk.key and complete the setting up the disk with a filesystem.
{:start=”16”}
sudo cryptsetup luksOpen --key-file=disk.key $loopdevice enc_volumesudo mkfs.ext4 -j /dev/mapper/enc_volumesudo mount /dev/mapper/enc_volume mountpointNow lets create a plain text file again and add user content to it:
{:start=”19”}
sudo touch mountpoint/plain.txtsudo chmod 777 mountpoint/plain.txtsudo echo "This is my plain text" > mountpoint/plain.txtsudo umount mountpointsudo cryptsetup remove enc_volumesudo losetup -d $loopdevicestrings enc.disk | grep -i plainNOTE:
Auto mount would necessitate providing pass-phrase/ key to cryptsetup at runtime
without user intervention, hence the secret has to be provided to LUKS in clear.
The goal then to prevent an attacker mount the storage on another system can be
trivially defeated by giving access to the encryption secret in clear on the
file-system.
Solution:
a. Seal the secret into a TPM device.
b. Unseal the secret in memory and pass it to cryptsetup.
Let’s start with creating and persisting a sealing object and sealing a random
byte sequence as the disk key.
{:start=”26”}
tpm2_createprimary -Q -C o -c prim.ctxdd if=/dev/urandom bs=1 count=32 status=none | tpm2_create -Q -g sha256 -u seal.pub -r seal.priv -i- -C prim.ctxtpm2_load -Q -C prim.ctx -u seal.pub -r seal.priv -n seal.name -c seal.ctxtpm2_evictcontrol -C o -c seal.ctx 0x81010001Now lets change the authentication from previously created disk.key to the new
sealed secret and after that shred the disk.key since it’s no longer useful:
tpm2_unseal -Q -c 0x81010001 | sudo cryptsetup luksChangeKey enc.disk --key-file disk.keyshred disk.key; rm -f disk.keyNow let’s mount the volume with the new authentication sealed up in the tpm:
{:start=”32”}
sudo losetup $loopdevice enc.disktpm2_unseal -Q -c 0x81010001 |sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volumesudo mount /dev/mapper/enc_volume mountpointYou can now see that disk access is granted with the new secret:
{:start=”35”}
ls mountpoint
Finally unmount the disk:
{:start=”36”}
sudo umount mountpointsudo cryptsetup remove enc_volumesudo losetup -d $loopdeviceAttacker now additionally needs the TPM on platform along with the disk since decryption key isn’t on the disk, its safely stored on the TPM anchored to the specific platform.
NOTE:
We can add a passphrase to the sealing object. But that necessitates storing it
in clear on the file-system. TPM cannot tell apart who presents the
authentication if any is set for a TPM object; and access is simply granted. At
a higher robustness level, the concern may also be that the attacker can now on
the same system, boot to an alternative OS whose login credentials are fully
controlled by the attacker. In this scenario, the sealing object without any
authentication defeats the security objective of keeping pass-phrase a secret to
the system.
Solution:
a. Implement Measured boot in pre-boot software and measure critical components
including OS-bootloader, kernel, initrd, etc. to PCRs so any changes would
reflect in PCR0.
b. Use PCR as proxy authentication for the TPM sealing object.
c. After unsealing the pass-phrase; extend the sealing PCRs so that the
pass-phrase cannot be unsealed gain.
Let’s begin with creating a pcr policy with current value in PCR0 sha256 bank
{:start=”39”}
tpm2_startauthsession -S session.ctxtpm2_policypcr -Q -S session.ctx -l sha256:0 -L pcr0.sha256.policytpm2_flushcontext session.ctxNow replace the seal object in TPM NV memory protecting the disk encryption
secret with a new one that adds the pcr policy we just created as an
authentication mechanism to access the sealed secret.
{:start=”42”}
tpm2_unseal -c 0x81010001 | tpm2_create -Q -g sha256 -u pcr_seal_key.pub -r pcr_seal_key.priv -i- -C prim.ctx -L pcr0.sha256.policy tpm2_evictcontrol -C o -c 0x81010001tpm2_load -Q -C prim.ctx -u pcr_seal_key.pub -r pcr_seal_key.priv -n pcr_seal_key.name -c pcr_seal_key.ctxtpm2_evictcontrol -c pcr_seal_key.ctx 0x81010001 -C oNow let’s try to mount the encrypted disk again but this time the secret is
unsealed off a TPM object whose unsealing operation can only be accessed by
satisfying the PCR policy; in other words authenticating by virtue of intended
system software state being unchanged as reflected by the PCR value.
{:start=”46”}
sudo losetup $loopdevice enc.disktpm2_startauthsession --policy-session -S session.ctx tpm2_policypcr -Q -S session.ctx -l sha256:0At this point ideally you would want unseal the secret in memory and pipe it
directly to the cryptsetup app like this –> “tpm2_unseal -p session:session.ctx
-c 0x81010001 | sudo cryptsetup luksOpen –key-file=- $loopdevice enc_volume”.
However for the purpose of demonstrating flexible PCR in a later section we will
make a copy of the unsealed secret at this point to seal it with a new object
with flexible pcr policy. This breakdown to two steps
{:start=”49”}
tpm2_unseal -p session:session.ctx -c 0x81010001 > disk_secret.bkupcat disk_secret.bkup | sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volumetpm2_flushcontext session.ctx sudo mount /dev/mapper/enc_volume mountpoint/ls mountpoint/To prevent further attempts of unsealing the disk encryption secret, at this
point the RTM (trusted system software) is expected to extend a random value to
the PCR to prevent further unsealing. This is the same consequence of unintended
or malicious software state reflecting in the PCR. Thus causing the intended
consequence of failed policy check and thus a failed unsealing attempt.
Let’s look at the PCR state prior to extending it and then again after
extending:
{:start=”54”}
tpm2_pcrlist -l sha256:0tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000tpm2_pcrlist -l sha256:0Now let’s try to unseal the sealed disk encryption secret with the dirty
PCR:
{:start=”57”}
tpm2_startauthsession --policy-session -S session.ctx tpm2_policypcr -Q -S session.ctx -l sha256:0tpm2_unseal -p session:session.ctx -c 0x81010001tpm2_flushcontext session.ctxNOTE:
System software updates is an indispensable part of sane system design. However,
it presents a problem when it comes to sealing secrets to PCR reflecting a
specific system software state. In that, you now are in state where you have a
new set of valid PCRs value that cannot be known ahead of time and yet the
authentication of the TPM sealing object must be tied to the PCR somehow.
Solution:
Authorized PCR policy as an authentication mechanism for the TPM sealing object.
Instead of using a rigid PCR policy tied to raw PCR values we now seal it to a
PCR signature. The PCR sets are signed by the system designer and verified by
the TPM. This is achieved in following steps:
a. Get the new set of PCR and sign the pcr policy with signer private key.
{:start=”61”}
tpm2_startauthsession -S session.ctxtpm2_policypcr -Q -S session.ctx -l sha256:0 -L set2.pcr.policytpm2_flushcontext session.ctxopenssl genrsa -out signing_key_private.pem 2048openssl rsa -in signing_key_private.pem -out signing_key_public.pem -puboutWe now need the name which is a digest of the TCG public key format of the
public key to include in the policy. We can use the loadexternal tool for this:
{:start=”66”}
tpm2_loadexternal -G rsa -C o -u signing_key_public.pem -c signing_key.ctx -n signing_key.nameLet’s now create the signer policy:
{:start=”67”}
tpm2_startauthsession -S session.ctxtpm2_policyauthorize -S session.ctx -L authorized.policy -n signing_key.name -i set2.pcr.policytpm2_flushcontext session.ctxLet’s create a new sealing object with the authorized policy which will also
require the sealing secret for which we will use the disk_secret.bkup we created
at #49 earlier to avoid rebooting the platform to match the PCR we originally
had prior to extending.
{:start=”70”}
cat disk_secret.bkup | tpm2_create -g sha256 -u auth_pcr_seal_key.pub -r auth_pcr_seal_key.priv -i- -C prim.ctx -L authorized.policyLet’s replace the old persistent sealing object with the one we created
above with policy_authorize policy associated with signer public key:
{:start=”71”}
tpm2_evictcontrol -C o -c 0x81010001tpm2_load -Q -C prim.ctx -u auth_pcr_seal_key.pub -r auth_pcr_seal_key.priv -n auth_pcr_seal_key.name -c auth_pcr_seal_key.ctxtpm2_evictcontrol -c auth_pcr_seal_key.ctx 0x81010001 -C oopenssl dgst -sha256 -sign signing_key_private.pem -out set2.pcr.signature set2.pcr.policyb. Load the signer public key to the tpm and verify the signature on the pcr
and get the tpm verification tkt:
{:start=”75”}
tpm2_loadexternal -G rsa -C o -u signing_key_public.pem -c signing_key.ctx -n signing_key.nametpm2_verifysignature -c signing_key.ctx -g sha256 -m set2.pcr.policy -s set2.pcr.signature -t verification.tkt -f rsassac. Satisfy the authorized policy and then run policyauthorize:
{:start=”77”}
tpm2_startauthsession --policy-session -S session.ctxtpm2_policypcr -l sha256:0 -S session.ctxtpm2_policyauthorize -S session.ctx -i set2.pcr.policy -n signing_key.name -t verification.tktd. Pipe unseal output to the cryptsetup application:
{:start=”80”}
sudo losetup $loopdevice enc.disktpm2_unseal -p session:session.ctx -c 0x81010001 | sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volumetpm2_flushcontext session.ctx sudo mount /dev/mapper/enc_volume mountpoint/ls mountpoint/Disk encryption is a system designer’s liability. Loss of the encryption key is an unintended consequence of denial of service since in this scenario an attacker cannot be told apart. It may generally be a good idea to save a rescue key; LUKS affords this flexibility by providing up to 8 slots of authentication token, all of which can decrypt the disk. Look in to cryptsetup luksAdd option.
a. Initialize disk with LUKS :
commands: #12 through #21
b. Create pcr policy.
commands: #61, #62, #63
c. Create signing authority for signing the policies and provision public key in system.
commands: #64, #65
d. Create authorized policy for creating the sealing object.
commands: $67, #68, #69
e. Create secure passphrase and seal to the sealing object.
commands: #70
a. Sign valid pcr policies and provide the policy and the signature.
commands: #74
a. Load signer public key
commands: #75
b. Verify signature on the PCR policy and generate verification ticket.
commands: #76
c. Satisfy PCR policy and authorized policy.
commands: #77, #78, #79
d. Unseal the secret and pipe to cryptsetup to open the LUKS encrypted volume.
commands: #80, #81, #82, #83
Imran Desai
]]>There are many cases where it is crucial to verify the correctness of software and hardware configuration of a computer system. For instance, to improve the security of a corporate network, companies need to ensure that the systems that are allowed to connect to the corporate network are legitimate, and that the software that runs on the system is in a valid, known state. There are many different methods to ensure the above. This document describes a method that uses a Trusted Platform Module (TPM). TPM device can be used to validate a system integrity by implementing an attestation protocol. Trusted Computing Group (TCG) published a Trusted Attestation Protocol Information Model (TAP), which can be found at [1]. The TCG specification describes roles and message flows. The specific data format and binding to a protocol require additional “binding” specifications. The two main roles in this scenario are Verifier and Attester. A Verifier sends a challenge query to a system that selects the scope of attestation. The Attester collects attestation evidence (within the expected scope) that is to be verified (Attester). The Attester typically describes its boot state, current configuration and identity. Machine identities can establish supply chain provenance if the machine identity credentials are provisioned during manufacturing. One of the identities is known as the Endorsement Key (EK) certificate. The EK key may be used to provision attestation identities known as an Attestation Key certificate. Alternatively, the manufacturer may pre-provision an Attestation Key known as an Initial Attestation Key (IAK) which avoids having to provision it at onboarding or deployment. The Attestation Key is used to sign attestation evidence that is conveyed to the Verifier. After receiving the response, the Verifier can cryptographically check if the evidence satisfies the Verifier’s trust appraisal policy and if the device originated from an approved manufacturer. The manufacturer embedded EK and/or IAK and associated certificate path is what establishes device provenance to a vendor.
A platform with a TPM root-of-trust may be pre-provisioned with attestation and identities or they may be provisioned as part of device onboarding and deployment. Pre-provisioned keys and credentials are typically referred to as “Initial” while credentials provisioned at onboarding typically are referred to as “local”. However, a TPM pre-provisioned with only an EK may provision “Initial” keys at different stages in the supply chain including as part of device onboarding. Hence, TPM supports provisioning that happens over different stages. There are keys that are instantiated during manufacturing time by the TPM Vendor, other keys need to be instantiated during system assembly by the OEM, and other keys are instantiated by the system owner or user. TCG Provisioning Guidance [2] specification provides recommendations for key provisioning. Usually, TPM vendors provision the TPM device with a Primary Endorsement Key (PEK), and generate a certificate for that key (EK cert) in x.509 format at the time of manufacture. The EK Certificate contains the public part of the PEK, as well as other useful fields, such as TPM manufacturer name, part model, part version, key id, etc. This information can be used to uniquely identify the TPM and if the device OEM securely attaches a TPM to the device it can be used as a device identifier. The EK certificate may be stored in the TPM Non Volatile Memory (NVM), where it can be made available to client software or it could be made available online via the OEM’s website. The EK certificate is issued (cryptographically signed) by the TPM vendor’s CA and verifiers are assumed to trust the vendor’s root certificate. It is important to note that the private part of the EK key is expected to never be exposed outside of the TPM hardware. This ensures an attack TPM cannot easily masquerade as coming from a trusted vendor. To improve interoperability between vendors and adopters TCG also provides recommendations and guidelines for EK Credential values, which can be found in the TCG EK Credential Profile [3]. The Vendor may also instantiate an IDevID Key, that is used to uniquely identify the TPM device. At a later point the user may create more keys in the Endorsement hierarchy, that will be protected (wrapped) by the Primary EK. Tpm2_createek command can be used to generate a new EK. Note that Tpm2_createek will create the same key when invoked with the same template and the same Endorsement Primary Seed. Next, when the TPM is integrated into a system, the OEM generates a Primary Attestation Key (PAK) that is also stored in the TPM’s Non Volatile Memory and Attestation Key certificate, which references the Primary EK stored in the TPM. The OEM may also generate Initial Attestation Key (IAK) and IAK Certificate, which along with IDevID Key are used to platform identification, and signs it with the OEM’s preferred CA. Provisioning of other keys, such as Primary Storage Key (PSK), that are not used in attestation process are out of scope of this document. These keys can be generated on demand by the owner/user, don’t need to be signed by third party (Vendor, OEM), and no certificates are needed for them. Diagram 1 depictures the steps how a new TPM device is provisioned when it is installed in a system.

Before a system can be attested the owner needs to obtain the public key of the TPM Vendor generated Endorsement Key and OEM generated Attestation Key along with the appropriate certificates. Certificates can be obtained by reading the TPM NonVolatile memory at a predefined location, or by obtaining them from Vendor/OEM websites. The Verifier normally stores these keys and certificates in a local database, usually integrated with the corporate PKI infrastructure. Another object typically stored in the Verifier’s local database is a policy that identifies an expected initial state of the device as defined by the software installed on the Attester system. The various vendor(s) of boot software contribute digests of the software to Verifiers to be used as a reference for comparing digests to attestation evidence. Boot software needs to be measured during platform setup stage, and include measurements (digests) for the bootloader, OS, drivers, and appropriate user space applications. The measurements are stored in a local database and are used later, during attestation, to make determination if a system is in a good state. The user may also create a policy that will make the Attestation Key bound, or sealed to the initial state of the system. For a data center deployments, where many nodes run on similar hardware configuration and run the same software, this can be measured for a single platform and then used as base measurement for different systems with similar hardware and software configuration. Another piece of functionality that needs to be enabled on the to be attested systems is measured boot. If it is enabled the system software will measure the images of every software module, on different levels during the boot process, record the measured values in the Platform Configuration Registers (PCRs) within the TPM, and created a system Event Log with a separate entry for every software image measured. First ‘Secure Boot’ option needs to be enabled in the BIOS configuration, and it also needs to be supported by the bootloader and the Operation System. Most of the recent UEFI based platforms, bootloaders and Operating Systems do support measured/secure boot. The attestation operation is initialized by the verifier. It can be started on a time based interval or in response to a network resource access request. The verifier requests the attester’s AK and EK certificates, validate them against the corporate Root CA, and if they are valid it creates a challenge including key id, PCR selection, and random nonce. The attestor loads the Endorsement Key (EK) and Attestation Key (AK), if they are not already loaded, and issues a TPM2_Quote command to the TPM, referencing the Attestation Key. TPM will only be able to use the key if the given PCRs will be in a valid state, defined at the time when the PCR policy is created. After that TPM creates and signs an instance of TPMS_ATTEST structure as defined by [5] and sends it back to the verifier. The Verifier uses the information included in the TPMS_ATTEST structure to perform appraisal by validating the public part of the AK and comparing the software measurement logs (Event Log) against the good-know-state of the system software configuration stored in the local database. Diagram 2 shows the attestation challenge flow.

The verify can compare every entry in the event log stored in the database with the corresponding entry in the TPMS_ATTEST structure to verify the state of bootloader, Operating System, user applications, and make arbitrary decisions based on the results of the verification process. The Diagram 3 shows the verification step performed by the verifier.

When planning the implementation of the attestation in the corporate infrastructure it is also important to plan for platform firmware upgrades, operating system, or user space application updates. It is also important to plan recovery procedures in case of major failures, 0-days vulnerability disclosures, etc. Just as any other piece of corporate infrastructure TPM can become a target for an attack. Recently published TPM fail [6] paper showed that even some hardware TPMs can be vulnerable to timing attacks. In case of any of the mentioned situations it might be required to re-instantiate the affected TPM keys, and even the TPM modules itself, measure the system hardware and software configuration state and make appropriate adjustments in the local database, in which the measurements, keys, and key certificates are stored. TPM key upgrade and recovery should be part of an enterprise key management strategy. TPM device can generate and store keys that are non-migratable, and EK is an example of a non-migratable key. In this case it is recommended to follow the TPM vendor’s recommendations and procedures for managing the keys. When making a decision on what TPM vendor to choose it is important to check beforehand what level of support is provided, and what are the recommended recovery procedures [7].
Stay put for future updates.
git clone https://github.com/tpm2-software/tpm2-tss
cd tpm2-tss
./bootstrap
./configure --enable-integration
make -j$(nproc)
make checkCheck out the tpm2-tss README and tpm2-tss INSTALL for more information.
After successful installation a standalone application using ESAPI can be compiled as follows:
gcc standalone-example.c -L=/usr/local/lib/ -ltss2-esys -o standalone-exampleSimple example standalone application:
#include <stdlib.h>
#include <stdio.h>
#include <tss2/tss2_esys.h>
int main() {
TSS2_RC r;
/* Initialize the ESAPI context */
ESYS_CONTEXT *ctx;
r = Esys_Initialize(&ctx, NULL, NULL);
if (r != TSS2_RC_SUCCESS){
printf("\nError: Esys_Initializen\n");
exit(1);
}
/* Get random data */
TPM2B_DIGEST *random_bytes;
r = Esys_GetRandom(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, 20,
&random_bytes);
if (r != TSS2_RC_SUCCESS){
printf("\nError: Esys_GetRandom\n");
exit(1);
}
printf("\n");
for (int i = 0; i < random_bytes->size; i++) {
printf("0x%x ", random_bytes->buffer[i]);
}
printf("\n");
exit(0);
}