diff --git a/_posts/2020-06-12-Remote-Attestation-With-tpm2-tools.md b/_posts/2020-06-12-Remote-Attestation-With-tpm2-tools.md new file mode 100644 index 0000000..d0bad33 --- /dev/null +++ b/_posts/2020-06-12-Remote-Attestation-With-tpm2-tools.md @@ -0,0 +1,1718 @@ +# Table of Contents + +- [Introduction](#introduction) + * [Software required](#software-required) + * [Tools and utilities used from the tpm2-tools project](#tools-and-utilities-used-from-the-tpm2-tools-project) +- [Attestation-Goals](#attestation-goals) + * [Privacy considerations](#privacy-considerations) + + [Why anonymity or privacy matters?](#why-anonymity-or-privacy-matters) + + [Solution](#solution) +- [TPM attestation](#tpm-attestation) + * [What is a PCR and how are PCR values generated](#what-is-a-pcr-and-how-are-pcr-values-generated) + + [Initial state of the PCR](#initial-state-of-the-pcr) + + [Extending values into PCR indices](#extending-values-into-pcr-indices) + + [Golden or reference PCR](#golden-or-reference-pcr) + * [System software state](#system-software-state) + * [Roots of trust for reporting (RTR)](#roots-of-trust-for-reporting-(rtr)) +- [Roles identified in the bare bone remote attestation model](#roles-identified-in-the-bare-bone-remote-attestation-model) + * [Device service registration](#device-service-registration) + * [Service request Part 1 (Platform Anonymous Identity Validation)](#service-request-part-1-platform-anonymous-identity-validation) + * [Service request Part 2 (Platform Software State Validation)](#service-request-part-2-(platform-software-state-validation)) + * [Service delivery](#service-delivery) +- [Simple attestation with tpm2-tools](#simple-attestation-with-tpm2-tools) +- [Scripts for implementation of the simple attestation framework](#scripts-for-implementation-of-the-simple-attestation-framework) + * [Device-Node](#device-node) + * [Service-Provider](#service-provider) + * [Privacy-CA](#privacy-ca) +- [FAQ](#FAQ) + +# Introduction + +This article shows how to use the utilities/ tools from the +[tpm2-tools](https://github.com/tpm2-software/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. + +![Attestation-demo](/images/tpm2-attestation-demo/tpm2-attestation-demo.gif) + +## Software required +* [tpm2-tss](https://github.com/tpm2-software/tpm2-tss) +* [tpm2-abrmd](https://github.com/tpm2-software/tpm2-abrmd) +* [tpm2-tools](https://github.com/tpm2-software/tpm2-tools) +* [ibmswTpm — for demo purposes](https://sourceforge.net/projects/ibmswtpm2/) +* [openssl](https://linux.die.net/man/1/openssl) +* [openssh](https://linux.die.net/man/1/ssh) + +## Tools and utilities used from the tpm2-tools project +* [tpm2_createprimary](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createprimary.1.md) +* [tpm2_create](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_create.1.md) +* [tpm2_createek](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createek.1.md) +* [tpm2_createak](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createak.1.md) +* [tpm2_getekcertificate](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_getekcertificate.1.md) +* [tpm2_makecredential](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_makecredential.1.md) +* [tpm2_activatecredential](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_activatecredential.1.md) +* [tpm2_pcrextend](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_pcrextend.1.md) +* [tpm2_pcrread](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_pcrread.1.md) +* [tpm2_quote](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_quote.1.md) +* [tpm2_checkquote](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_checkquote.1.md) +* [tpm2_getrandom](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_getrandom.1.md) +* [tpm2_readpublic](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_readpublic.1.md) + +# 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-defintion]((https://www.lexico.com/en/definition/attestation)) +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 three 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 in to 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](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_import.1.md) + that demonstrates a way to import external keys into the TPM. +2. Every user has a unique private key, however, all the keys map to a single + public key. This is currently only possible with + [Intel® EPID](https://eprint.iacr.org/2009/095.pdf) technology. It also + affords a mechanism to revoke a single key or all the platforms in the group. +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. + +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](https://www.pcmag.com/how-to/how-to-hide-your-ip-address) 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](https://tpm2-software.github.io/tpm2-tss/getting-started/2019/12/18/Remote-Attestation.html). +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 in to 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](https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf). +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 which is mostly zeroes. 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. + +```bash +tpm2_pcrread -l 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 zero values 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](https://en.wikipedia.org/wiki/Intel_vPro#Intel_Boot_Guard) +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. + +```bash +# +# 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 $CONCATENATE | 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 two 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. + +```bash +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 +``` + +Note that if only 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. + +2. Entirely without the TPM as shown below. + +```bash +CONCATENATE_ALL_DIGESTS=`\ +echo $SHA1_DATA +echo $SHA1_DATA +echo $SHA1_DATA +echo $SHA256_DATA +echo $SHA256_DATA +echo $SHA256_DATA +` +echo $CONCATENATE_ALL_DIGESTS | xxd -r -p | openssl -dgst -sha256 +``` + +In either case the golden/reference value is calculated as +"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](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_eventlog.1.md) +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](https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_Credential_Profile_EK_V2.1_R13.pdf). +As already mentioned, the EK is a primary key and so can be created using the +[tpm2_createprimary](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createprimary.1.md) +tool by supplying the right attributes and authorization policy. In order to +simplify attestation, a tool +[tpm2_createek](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createek.1.md) +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](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_create.1.md). +Alternatively, the key can also be created using the [tpm2_createak](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_createak.1.md) +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](https://tpm2-software.github.io/2020/04/13/Disk-Encryption.html) +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“ send it's 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](/images/tpm2-attestation-demo/registration.png) + +## 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](/images/tpm2-attestation-demo/identity-validation.png) + +## Service request Part 2 (Platform Software State Validation) + +At this stage, the “Service-Provider“ has been 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. + +```yaml +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. + +```yaml +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](/images/tpm2-attestation-demo/software-state-validation.png) + +## Service delivery + +At this stage, the “Sevice-Provider“ is ascertained of the "Device-Node" AIK as +well as the system-software state. Thus, AIK is now used to wrap a shared secret +called SERVICE-SECRET to encrypt service data content. The "Device-Node" once +decrypts this SERVICE-SECRET can now decrypt the service data content. + +![Service-delivery](/images/tpm2-attestation-demo/service-delivery.png) + +# 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 in to the tpm2-tools. + +1. “Device-Node“ creating the endorsement-key and the attestation-identity-key. + +```bash + +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 +``` + +2. “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. + +```bash +# 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 +``` + +3. “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. + +```bash +# 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 +``` + +4. “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“. + +```bash +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 +``` + +5. “Service-Provider“ verifying the attestation quote generated and signed by +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 to make the determination of the software-state of the +“Device-Node“. + +```bash +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 + +CAUTION: The software below is strictly for demonstration purpose. It has not +been evaluated for production use. + +## Device-Node +```bash +#!/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_startauthsession -S session.ctx --policy-session -Q + tpm2_policysecret -S session.ctx -c e -Q + tpm2_create -C rsa_ek.ctx -c rsa_ak.ctx -u rsa_ak.pub -r rsa_ak.priv \ + -P session:session.ctx -Q + tpm2_readpublic -c rsa_ak.ctx -f pem -o rsa_ak.pub -n rsa_ak.name -Q + tpm2_flushcontext session.ctx -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 \ + $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 rsa_ak.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 +} + +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="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 +```bash +#!/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 +} + +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" + + # Encrypt service data content and deliver + echo "$SERVICE_CONTENT" > service-content.plain + openssl rsautl -encrypt -inkey d_s_service_aik.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 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 + +```bash +#!/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 + + 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](https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_readpublic.1.md) + 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. + +2. ***Why is tpm2_createak tool not used to create the AIK in the demo scripts?*** + + In our demo example we intend to have an AIK with following properties: + a. It will have to be validated for anonymous identity relationship with EK. + b. It has to be a signing key for it to be used to sign an attestation quote. + c. It has to be usable as RSA encrypt/ decrypt key. + + The combination of the above properties is not the default attributes chosen + in the tpm2_createak tool. Specifically, the key generated with tpm2_createak + cannot be used as a decryption key. + + Note that the authorization for using the endorsement key which is the parent + of the attestation identity key needs to be satisfied to be able to create + the AIK and is satisfied through a policy session using a policy + "policysecret" to reference the authorization of the endorsement hierarchy. \ No newline at end of file diff --git a/images/tpm2-attestation-demo/identity-validation.png b/images/tpm2-attestation-demo/identity-validation.png new file mode 100644 index 0000000..7f7c514 Binary files /dev/null and b/images/tpm2-attestation-demo/identity-validation.png differ diff --git a/images/tpm2-attestation-demo/registration.png b/images/tpm2-attestation-demo/registration.png new file mode 100644 index 0000000..a75c5ce Binary files /dev/null and b/images/tpm2-attestation-demo/registration.png differ diff --git a/images/tpm2-attestation-demo/service-delivery.png b/images/tpm2-attestation-demo/service-delivery.png new file mode 100644 index 0000000..eb74ba5 Binary files /dev/null and b/images/tpm2-attestation-demo/service-delivery.png differ diff --git a/images/tpm2-attestation-demo/software-state-validation.png b/images/tpm2-attestation-demo/software-state-validation.png new file mode 100644 index 0000000..4e753eb Binary files /dev/null and b/images/tpm2-attestation-demo/software-state-validation.png differ diff --git a/images/tpm2-attestation-demo/tpm2-attestation-demo.gif b/images/tpm2-attestation-demo/tpm2-attestation-demo.gif new file mode 100644 index 0000000..eefe03e Binary files /dev/null and b/images/tpm2-attestation-demo/tpm2-attestation-demo.gif differ