Jekyll2025-11-05T16:46:24+00:00https://tpm2-software.github.io/feed.xmltpm2-software communityThe community around the TPM Software Stack 2.0 and its tpm2-tss open source implementation and all the tools and software that build upon it.Protecting Secrets At Tpm Interface2021-02-17T00:00:00+00:002021-02-17T00:00:00+00:00https://tpm2-software.github.io/2021/02/17/Protecting-secrets-at-TPM-interfaceProtecting secrets at the TPM interface

Abstract Introduction Provisioning Runtime extended provisioning Sealing application secret Unsealing application secret Demonstration script using tpm2-tools Credits

Abstract

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.

Introduction

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:

  1. Provisioning: At this step, the NV index with specific attributes and authorization-value and authorization-policy is created by the CPU(RTM).

  2. 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.

  3. Sealing-data: A sealing object is created with the policy-command-code (TPM2_CC_PolicyNV AND TPM2_CC_Unseal)

  4. Unsealing-data: The authorization-policy for unsealing is satisfied and the secret is unsealed.

Provisioning

Provisioning

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

Runtime extended provisioning

Runtime-extended-provisioning

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

Sealing application secret

Sealing-app-secret

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

Unsealing application secret

Unseal-app-secret

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

Demonstration script using tpm2-tools

#!/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

Credits

This document is a culmination of thoughts and ideas of the members of the TCG Device Driver Working Group (DDWG). Thanks to the contributors:

  • Liran Perez(Intel)
  • Ken Goldman (IBM)
  • Ronald Aigner (Microsoft)
  • Amy Nelson (Dell)
  • Imran Desai (Intel)
]]>
Tpm2 Device Emulation With Qemu2020-10-19T00:00:00+00:002020-10-19T00:00:00+00:00https://tpm2-software.github.io/2020/10/19/TPM2-Device-Emulation-With-QEMUIntroduction

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.

Prerequisites

Prior art does exist on this topic, and details used in this tutorial have references from the following resources:

  • https://www.qemu.org/docs/master/specs/tpm.html
  • https://graspingtech.com/ubuntu-desktop-18.04-virtual-machine-macos-qemu/

Install the proper TPM2.0 Simulator

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.

Install QEMU

Next, you need to install QEMU. This is operating system dependent. Details on installing QEMU can be found by visiting their website:

  • https://www.qemu.org/

The minimum version of QEMU to support this is 4.0. In this tutorial, the author tested with version 5.2.

Install The Guest OS

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

Start the Guest with a TPM2.0 Device

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.

Conclusion

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.

Author

William Roberts

]]>
Fapi_crypto_profiles2020-07-22T00:00:00+00:002020-07-22T00:00:00+00:00https://tpm2-software.github.io/2020/07/22/Fapi_Crypto_ProfilesBasics

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.

Multiple Profiles

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/...

Asymmetric Encryption

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.

Updates

This page may be updated in the future with more information.

Author

Andreas Fuchs @Fraunhofer SIT

]]>
Remote Attestation With Tpm2 Tools2020-06-12T00:00:00+00:002020-06-12T00:00:00+00:00https://tpm2-software.github.io/2020/06/12/Remote-Attestation-With-tpm2-toolsTable of Contents

Introduction

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-demo

Software required

Tools and utilities used from the tpm2-tools project

Attestation-Goals

“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:

  1. 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.

  2. 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.

Privacy considerations

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.

Why anonymity or privacy matters?

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:

  1. Two-factor-authentication(2FA).
  2. Facial-recognition.
  3. Device-Logs.

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.

Solution

There are at least four possible ways to resolve this.

  1. Every user uses the same private/public key pair to sign attestation blobs. Cryptographically, this method is as strong as the process and controls involved in duplicating the private key into multiple platforms. Additionally, the strength is also dependent on the robustness of the methods and mechanisms to store the private key once imported in the platform. Revocation of the key implies that all the platforms having the key must be revoked and re-provisioned with new key. Refer tpm2_import that demonstrates a way to import external keys into the TPM.
  2. Every user has a unique private key, however, all the private keys keys map to a single public key. This is possible with Intel® EPID technology. EPID also affords a mechanism to revoke a single key or all the platforms in the group. A similar direct-anonymous-attestation (DAA) based on elliptic curve cryptography scheme is also available in the TPM and is called ECDAA; it is not discussed in this tutorial.
  3. Every user generates and uses a new key (ephemeral key) every time an attestation blob has to be signed. There are some unique challenges with this. If every attestation blob is signed with a brand-new key, how to infer the anonymous identity at the minimum to determine the genuineness of the platform and hence the attestation. It then follows that we need an anonymous identity that is cryptographically bound to a unique trusted identity and that the unique identity is never revealed to any entity other than the Privacy-CA.
  4. A variant of #3. One AIK per party that’s verifying attestations. Allows abstracting the client ID, but the verifier knows it’s the same client on subsequent verifications. This is useful in applications like banking etc.

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.

TPM attestation

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.

What is a PCR and how are PCR values generated

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).

Initial state of the PCR

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

Extending values into PCR indices

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:

  1. tpm2_pcrextend: A pre-calculated digest of data is presented to the TPM.
  2. tpm2_pcrevent: The data is directly presented to the TPM and the TPM calculates the data-digest prior to extending.
  3. tpm2_pcrreset: The PCR index value is reset to zero. Not all PCR indices are resettable.

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

Golden or reference PCR

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:

  1. Using the tpm2_quote tool. The signing scheme used to sign the quote determines the digest algorithm for the quote. This should be done once on a reference platform.
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`
  1. If only the PCR data is to be read from the reference platform, it can always be done using tpm2_pcrread tool as well. Also, the output of the tpm2_pcrread tool can be passed to the tpm2_checkquote tool directly. However, when doing so, specifying the PCR-selection information to the tpm2_checkquote tool is a must. Calculating the golden/ reference PCR data using this method is shown below.
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`
  1. Entirely without the TPM as shown below.
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”.

System software state

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:

  1. Calculates the hash of the software module that will be loaded for execution.
  2. Sends over the digest to the TPM and requests it be extended in the PCR.

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.

Roots of trust for reporting (RTR)

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:

  1. TCG EK cannot be used as a signing key.
  2. TCG EK authentication is a policy that references endorsement hierarchy authorization.
  3. TCG EK are certified by the TPM manufacturer. More details on the key can be found from TCG EK credential specification. As already mentioned, the EK is a primary key and so can be created using the tpm2_createprimary tool by supplying the right attributes and authorization policy. In order to simplify attestation, a tool tpm2_createek is also available that has all the defaults specified by TCG EK credential spec.

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,

  1. The TPM manufacturer EK certificate vouches for unique identity; while the credential activation process vouches the association of the AIK to the EK and hence the TPM.
  2. tpm2_createek and tpm2_createak tools can be used to create EK and AIK. Any further customizations to the keys outside the chosen defaults can be done by creating the objects with tpm2_createprimary and tpm2_create respectively.

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.

Roles identified in the bare bone remote attestation model

The primary goals for the minimal bare bone remote attestation are:

  1. To demonstrate verification of an attestation quote.
  2. The quote should contain the digest of the PCR values representing system software state.
  3. Signer of the quote should be anonymous to the verifier; yet the verifier should be able to make a determination that the quote was signed by a valid signer.

To achieve the goals it is sufficient to have 3 players, namely:

  1. Device-Node: The edge platform with a TPM whose system software state is of interest. It generates the attestation structures with digest of PCR data included in the quotes. The platform signs the quotes with an attestation identity key for anonymity. AIK is cryptographically bound to the unique identity key on the same platform. The unique identity key is the endorsement-key (EK).
  2. Privacy-CA: The only trusted entity that can prove the association of an AIK to a valid EK without disclosing the EK to the “Service-Provider”. This is also the only entity in addition to the “Device-Node” that knows the EK for a given AIK from the “Device-Node“.
  3. Service-Provider: The entity that the “Device-Node“ communicates with to avail services. The “Service-Provider“ need to ensure the following:
    a. Entity requesting services has registered its unique identity with the “Privacy-CA“.
    b. Anonymous identity belongs to the pool of registered unique identities that the “Privacy-CA“ stores.
    c. System software state of the “Device-Node“ is an acceptable one.

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.

  1. Service-registration.
  2. Service-request Part 1 (Anonymous identity validation).
  3. Service-request Part 2 (Platform software state validation).
  4. Service-delivery.

Device service registration

This is the stage where the “Device-Node“ requests services from the “Service-Provider“. The “Service-Provider“ has two requirements at this stage.

  1. The “Device-Node“ sends its unique key to the “Privacy-CA“
  2. The “Privacy-CA“ verifies the genuineness of the EK from the EK certificate validation and also the presence of the EK on the platform through the credential-activation process.

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. Registration

Service request Part 1 (Platform Anonymous Identity Validation)

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:

  1. The AIK is a valid one and that the Privacy-CA can prove that the AIK is bound to an EK from the Privacy-CA’s trusted pool of EKs.
  2. The AIK is from a TPM currently accessible to the platform.

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. Identity-validation

Service request Part 2 (Platform Software State Validation)

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.)

  • pcrSelections: This is a list of PCR selection structures.
    1. hash: The hash algorithm associated with the selection bank.

    2. 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.

    3. pcrSelect: The bit map of selected PCR (the least significant byte first)

  • pcrDigest: The digest of selected PCR using hash algorithm of the signing key.

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. Software-state-validation

Service delivery

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.

Service-delivery

Simple attestation with tpm2-tools

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.

  1. “Device-Node“ creating the endorsement-key and the attestation-identity-key.

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
  1. “Device-Node“ retrieving the endorsement-key-certificate to send to the “Privacy-CA“. There are two possible locations where the endorsement key certificates are provided by the TPM manufacturer. While most TPM manufacturers store them in the TCG specified NV indices , some make it available for download through a web hosting. Let’s look at both these methods.
# 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
  1. “Privacy-CA“ and the “Device-Node“ performing a credential activation challenge in order to verify the AIK is bound to the EK from the EK-certificate originally shared by the “Device-Node“. This is done in two different instances in the proposed simple-attestation-framework — Once when the “Service-Provider“ requests the “Device-Node“ to send over the identities as part of the service registration process. And the second time when the “Device-Node“ sends its AIK to the “Service-Provider“ and the “Service-Provider“ in turn sends it over to the “Privacy-CA“ in order to verify the anonymous identity.
# 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
  1. “Device-Node“ generating the PCR attestation quote on request from the “Service-Provider“. The “Service-Provider“ specifies the PCR-banks, PCR-indices, and the ephemeral NONCE data. The NONCE is to ensure there is no possibility of a replay attack on the quote verification and validation process. Validity of the signing key for attestation quote is ascertained to be a valid one by the “Privacy-CA“.
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
  1. “Service-Provider“ verifying the attestation quote generated and signed by the “Device-Node“. To make the determination of the software-state of the “Device-Node“, after the signature and nonce verification process, the “Service-Provider“ validates the digest of the PCR values in the quote against a known-good-valid —the golden/ reference value ascertained previously.
tpm2_checkquote \
--public rsa_ak.pub \
--message pcr_quote.plain \
--signature pcr_quote.signature \
--qualification SERVICE_PROVIDER_NONCE \
--pcr pcr.bin

Scripts for implementation of the simple attestation framework

Device-Node

#!/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

Service-Provider

#!/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

Privacy-CA

#!/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

FAQ

  1. 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.

Author

Imran Desai

]]>
Disk Encryption2020-04-13T00:00:00+00:002020-04-13T00:00:00+00:00https://tpm2-software.github.io/2020/04/13/Disk-EncryptionIntroduction

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.

Trusted Platform Module (TPM)

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.

Trusted Software Stack (TSS) and TPM2 Software (tpm2-software):

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.

TPM2 Software:

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:

  • tpm2-tss: OSS implementation of the TCG TPM2 Software Stack (TSS2).
  • tpm2-abrmd: TPM2 Access Broker & Resource Management Daemon implementing the TCG specification.
  • tpm2-tools: The source repository for the Trusted Platform Module (TPM2.0) tools.
  • tpm2-pkcs11: A PKCS#11 interface for TPM2 hardware.
  • tpm2-tss-engine: OpenSSL Engine for TPM2 devices.
  • tpm2-tcti-uefi: TCTI module for use with TSS2 libraries in UEFI environment.
  • tpm2-pytss: Python bindings for TSS.
  • tpm2-swig: SWIG interface for high level language bindings.
  • tpm2-totp: Attest the trustworthiness of a device against a human using time-based one-time passwords.
  • tpm2-software-container: Metadata and scripts used to generate the container images used for continuous integration (CI) by the various tpm2-software projects.
  • tpm2-software.github.io: This is the source for the community website of the tpm2-software namespace on GitHub. It uses github pages (with Jekyl).

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.

A simple use case - Creating an RSA Key pair and securely persisting in TPM

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

  1. tpm2_createprimary --hierarchy=o --key-algorithm=rsa --key-context=prim.ctx

Save it to the TPM persistent memory
{:start=”2”}

  1. tpm2_evictcontrol --hierarchy=o --object-context=prim.ctx 0x81010001

NOTE:

  • The sensitive portion of the key is never released outside of the TPM2 device. This makes it a more secure way of generating and protecting the key compared to the conventional way of using system software like openssl.
  • Once the key is generated and persisted in the TPM2 device, it can now be used throughout the boot sequence as soon as TPM becomes available which is much earlier than the operating system loads.

Use case - Disk encryption using Linux Unified Key Setup (LUKS) with TPM2 as the encryption key protector:

The need for Disk Encryption:

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:

  • Make it difficult for attacker to dump sensitive plain text on disk.
  • Choose a strong pass-phrase & a vetted disk encryption software.
  • Protect the disk encryption pass-phrase on the platform: Just-in-time OR secure-key-store.
  • Protect pass-phrase access with restricted access to specific platform and specific system software state
  • Flexibility in unsealing pass-phrase during intended system software state updates/ changes.

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”}

  1. dd if=/dev/zero of=plain.disk bs=1M count=10
  2. mkfs.ext4 plain.disk
  3. mkdir -p mountpoint
  4. sudo mount plain.disk mountpoint
  5. sudo touch mountpoint/plain.txt
  6. sudo chmod 777 mountpoint/plain.txt
  7. sudo echo "This is my plain text" > mountpoint/plain.txt
  8. sudo umount mountpoint

At 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:

  1. strings plain.disk

NOTE:

  • Having a week authentication mechanism for login control is not mitigated by disk encryption. Mounted encrypted volumes without user intervention are available in clear to the logged in user.
  • Data assets especially storage must remain confidential either with disk/ file encryption. Choosing and protecting a strong pass-phrase for encryption is paramount.

Simple disk encryption scheme using Linux Unified Key Setup (LUKS) - Plaintext pass-phrase on disk:

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”}

  1. dd if=/dev/zero of=enc.disk bs=1M count=10
  2. dd if=/dev/urandom of=disk.key bs=1 count=32
  3. loopdevice=$(losetup -f) && sudo losetup $loopdevice enc.disk
  4. sudo cryptsetup luksFormat --key-file=disk.key $loopdevice

At 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”}

  1. sudo cryptsetup luksOpen --key-file=disk.key $loopdevice enc_volume
  2. sudo mkfs.ext4 -j /dev/mapper/enc_volume
  3. sudo mount /dev/mapper/enc_volume mountpoint

Now lets create a plain text file again and add user content to it:
{:start=”19”}

  1. sudo touch mountpoint/plain.txt
  2. sudo chmod 777 mountpoint/plain.txt
  3. sudo echo "This is my plain text" > mountpoint/plain.txt
  4. sudo umount mountpoint
  5. sudo cryptsetup remove enc_volume
  6. sudo losetup -d $loopdevice
You will now see that you cannot dump the information from the disk image simply

{:start=”25”}
  1. strings enc.disk | grep -i plain

NOTE:

  • Do not store the pass-phrase in clear on the disk. In this case the disk.key passphrase file needs to be secured.
  • Either securely provide it just-in-time or seal it on a security token that anchors to the platform, like a TPM.

LUKS disk encryption scheme with pass-phrase stored in TPM2 as the protector.

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”}

  1. tpm2_createprimary -Q -C o -c prim.ctx
  2. dd if=/dev/urandom bs=1 count=32 status=none | tpm2_create -Q -g sha256 -u seal.pub -r seal.priv -i- -C prim.ctx
  3. tpm2_load -Q -C prim.ctx -u seal.pub -r seal.priv -n seal.name -c seal.ctx
  4. tpm2_evictcontrol -C o -c seal.ctx 0x81010001

Now 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:

  1. tpm2_unseal -Q -c 0x81010001 | sudo cryptsetup luksChangeKey enc.disk --key-file disk.key
  2. shred disk.key; rm -f disk.key

Now let’s mount the volume with the new authentication sealed up in the tpm:
{:start=”32”}

  1. sudo losetup $loopdevice enc.disk
  2. tpm2_unseal -Q -c 0x81010001 |sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volume
  3. sudo mount /dev/mapper/enc_volume mountpoint

You can now see that disk access is granted with the new secret:
{:start=”35”}

  1. ls mountpoint

Finally unmount the disk:
{:start=”36”}

  1. sudo umount mountpoint
  2. sudo cryptsetup remove enc_volume
  3. sudo losetup -d $loopdevice

Attacker 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:

  • Access control to security token has similar challenges as protecting pass-phrases especially if it needs to be provided just-in-time.
  • An authentication mechanism that anchors to trusted system state and the platform can be achieved with a TPM and it’s PCR sealing provision.

PCR policy authentication - Access control of sealed pass-phrase on TPM2 with PCR sealing

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”}

  1. tpm2_startauthsession -S session.ctx
  2. tpm2_policypcr -Q -S session.ctx -l sha256:0 -L pcr0.sha256.policy
  3. tpm2_flushcontext session.ctx

Now 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”}

  1. 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
  2. tpm2_evictcontrol -C o -c 0x81010001
  3. tpm2_load -Q -C prim.ctx -u pcr_seal_key.pub -r pcr_seal_key.priv -n pcr_seal_key.name -c pcr_seal_key.ctx
  4. tpm2_evictcontrol -c pcr_seal_key.ctx 0x81010001 -C o

Now 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”}

  1. sudo losetup $loopdevice enc.disk
  2. tpm2_startauthsession --policy-session -S session.ctx
  3. tpm2_policypcr -Q -S session.ctx -l sha256:0

At 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”}

  1. tpm2_unseal -p session:session.ctx -c 0x81010001 > disk_secret.bkup
  2. cat disk_secret.bkup | sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volume
  3. tpm2_flushcontext session.ctx
  4. sudo mount /dev/mapper/enc_volume mountpoint/
  5. 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”}

  1. tpm2_pcrlist -l sha256:0
  2. tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
  3. tpm2_pcrlist -l sha256:0

Now let’s try to unseal the sealed disk encryption secret with the dirty PCR:
{:start=”57”}

  1. tpm2_startauthsession --policy-session -S session.ctx
  2. tpm2_policypcr -Q -S session.ctx -l sha256:0
    The following operation should result in policy check failure preventing the unseal operation:
    {:start=”59”}
  3. tpm2_unseal -p session:session.ctx -c 0x81010001
  4. tpm2_flushcontext session.ctx

NOTE:

  • The goal is to prevent unsealing of the secret during unintended software change causing resulting PCR changes.
  • However this scheme would break with intended software change and resulting PCR changes.

Addressing PCR brittleness from intentional software updates with authorized policies

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”}

  1. tpm2_startauthsession -S session.ctx
  2. tpm2_policypcr -Q -S session.ctx -l sha256:0 -L set2.pcr.policy
  3. tpm2_flushcontext session.ctx
  4. openssl genrsa -out signing_key_private.pem 2048
  5. openssl rsa -in signing_key_private.pem -out signing_key_public.pem -pubout

We 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”}

  1. tpm2_loadexternal -G rsa -C o -u signing_key_public.pem -c signing_key.ctx -n signing_key.name

Let’s now create the signer policy:
{:start=”67”}

  1. tpm2_startauthsession -S session.ctx
  2. tpm2_policyauthorize -S session.ctx -L authorized.policy -n signing_key.name -i set2.pcr.policy
  3. tpm2_flushcontext session.ctx

Let’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”}

  1. 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.policy

Let’s replace the old persistent sealing object with the one we created above with policy_authorize policy associated with signer public key:
{:start=”71”}

  1. tpm2_evictcontrol -C o -c 0x81010001
  2. tpm2_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.ctx
  3. tpm2_evictcontrol -c auth_pcr_seal_key.ctx 0x81010001 -C o
    Let’s now sign the pcr_policy with the signer private key:
  4. openssl dgst -sha256 -sign signing_key_private.pem -out set2.pcr.signature set2.pcr.policy

b. Load the signer public key to the tpm and verify the signature on the pcr and get the tpm verification tkt:
{:start=”75”}

  1. tpm2_loadexternal -G rsa -C o -u signing_key_public.pem -c signing_key.ctx -n signing_key.name
  2. tpm2_verifysignature -c signing_key.ctx -g sha256 -m set2.pcr.policy -s set2.pcr.signature -t verification.tkt -f rsassa

c. Satisfy the authorized policy and then run policyauthorize:
{:start=”77”}

  1. tpm2_startauthsession --policy-session -S session.ctx
  2. tpm2_policypcr -l sha256:0 -S session.ctx
  3. tpm2_policyauthorize -S session.ctx -i set2.pcr.policy -n signing_key.name -t verification.tkt

d. Pipe unseal output to the cryptsetup application:
{:start=”80”}

  1. sudo losetup $loopdevice enc.disk
  2. tpm2_unseal -p session:session.ctx -c 0x81010001 | sudo cryptsetup luksOpen --key-file=- $loopdevice enc_volume
  3. tpm2_flushcontext session.ctx
  4. sudo mount /dev/mapper/enc_volume mountpoint/
  5. ls mountpoint/

Summarizing the various roles in setting up the disk encryption

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.

Platform Provisioning

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

System/ Software integrator

a. Sign valid pcr policies and provide the policy and the signature.
commands: #74

Platform runtime

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

Author

Imran Desai

]]>
Remote Attestation2019-12-18T12:24:05+00:002019-12-18T12:24:05+00:00https://tpm2-software.github.io/tpm2-tss/getting-started/2019/12/18/Remote-AttestationRemote Attestation Best Known Methods

Introduction

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.

Key Provisioning

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.

Diagram 1:  Attestation key provisioning

Attestation Procedure

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.

Diagram 2:  Attestation challenge

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.

Diagram 3: Verification of the quote

Field upgrade and disaster recovery

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].

References

  1. TCG, Trusted Attestation Protocol (TAP)
  2. TCG, TPM v2.0 Provisioning Guidance
  3. TCG, EK Credential Profile
  4. TCG, TPM2.0 Library Specification, part 1- Architecture
  5. TCG, TPM2.0 Library Specification, part 2- Structures
  6. Timing and Lattice Attacks on the TPM
  7. Infineon, TPM Key backup and recovery
]]>
Getting Started2019-02-05T17:28:05+00:002019-02-05T17:28:05+00:00https://tpm2-software.github.io/tpm2-tss/getting-started/2019/02/05/Getting-StartedThis tutorial is pretty much just a placeholder to show how future tutorials can be created here inside jekyll.

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 check

Check 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-example

Simple 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);
}
]]>