TPM-JS lets you experiment with a software Trusted Platform Module (TPM) in your browser. TPM-JS includes the following libraries:
The TSS library provides high-level APIs to the TPM. It manages TPM resources, marshals command buffers and unmarshals response buffers.
The simulator executes TPM commands. It maintains internal state and uses the BoringSSL library for cryptographic operations.
TPMs are remarkable! They are passive, low-end devices that offer strong security guarantees.
In this site we'll learn how TPMs work. We'll see how to use the TPM as a cryptographic device. We'll cover topics such as key generation, measured boot, PCRs, remote attestation and key sealing.
TPM is a discrete device soldered on the motherboard. It is a cheap (costs less than a dollar to manufacture), low-end device that communicates with the main CPU over a slow, low-bandwidth channel.
TPM is a passive device: it doesn't monitor the system and it can't halt CPU execution. For it to work, it must be fed data.
TPM has limited storage for runtime state and persistent data: its non-volatile storage is about 64KB in size. TPM can only hold a limited number of objects at the same time. For this reason, a dedicated software layer on the host (resource manager) loads and unloads session objects at runtime.
TPM command execution is single-threaded: it executes one command at a time. Commands cannot be queued or batched together - each command must wait for the currently running command to finish. Note that command execution can be canceled.
Despite these unfavorable conditions, the TPM is designed to support critical security workflows. TPMs support two main use-cases: secure key generation and remote system attestation.
Secure Key Generation
TPM is a cryptographic device. It can securely generate new cryptographic keys: the keys are only available to the TPM - private key material never leaves the device in plain form.
TPM can do crypto operations such as encryption and signing. TPM can certify new keys, so in a way a TPM acts as a certificate authority (CA). Trust in these keys is rooted in a primary key provisioned by the manufacturer or the owner of the TPM.
Remote System Attestation
TPM can capture the host system state: this is done by storing a sequence of measurements in a special set of registers called Platform Configuration Registers (PCRs). The TPM can later report its PCR values to a remote party. It is done in a secure way such that the remote attester can verify the report is fresh, genuine, and has not been tampered with.
TPM properties can be queried using the
TPM2_CC_GetCapability command with
The following snippet reads the TPM specification level (1.46) and the manufacturer id ("IBM ").
To test whether a particular algorithm is implemented by the TPM, use the
The following snippet calls
TPM2_ALG_HMAC and different hash algorithms. SHA1, SHA256 are both implemented by the TPM. SHA512, however, is not.
As a dedicated cryptographic device, the TPM can generate secure random data. Use
TPM2_CC_GetRandom to generate random bytes.
Note that a single call can generate at most 48 bytes, the size of the largest hash digest implemented by the TPM (TPM2_ALG_SHA384 in this case).
Random data is a critical building block of cryptographic systems. The TPM uses its pool of random data to generate symmetric and asymmetric key material.
TPM generates strong, secure cryptographic keys. Strong in the sense that the key is derived from true random source and large key space. In case of 3DES, TPM also checks that keys are not known weak keys. Secure in the sense that the private key material never leaves the TPM secure boundary in plain form. When a key leaves the TPM - in order to be loaded and used later - it is wrapped (encrypted) by its parent key.
Keys, therefore, form a tree: each key is wrapped by its parent, all the way to the root of the tree, where the primary key is derived from a fixed seed. The seed is stored in the TPM's NVDATA, under a reserved index, and cannot be read externally.
The TPM stores keys on one of four hierarchies:
- Endorsement hierarchy.
- Platform hierarchy.
- Owner hierarchy, also known as storage hierarchy.
- Null hierarchy.
The platform hierarchy is reserved for objects created and certified by the OEM that builds the host platform. The platform seed (pseed) is randomly generated at manufacturing time, but can be changed by the OEM by calling
The owner hierarchy is reserved for us - the primary users of the TPM. When a user takes ownership, for example, when the IT department provisions a new host on the network, the owner hierarchy is reset. This is done by calling
TPM2_CC_Clear. In this critical setup step, two user keys should be provisioned and certified by the owner: these form the root of trust for all the keys generated on the owner hierarchy.
The null hierarchy is reserved for ephemeral keys. The null seed is re-generated each time the host reboots.
Let's see this in action. First, open the Seeds window by either clicking on the View menu on the right, or by running the next snippet.
We use our direct access to the simulator in order to read the secret seed values. Users cannot read these secrets in a normal setting.Note the seed values. We simulate a host reset (power-off, power-on cycle) in the next snippet. Note how the null seed is re-generated.
We clear the owner hierarchy in the next snippet. Note how the owner seed is re-generated.
TPM2_CC_Clear should only be called when the user takes ownership of the TPM.
Finally, we simulate a manufacturer reset. Note how all the seeds are re-created.
A manufacturer reset cannot be triggered by the user. Here we use our direct access to the simulator in order to invoke this function.
As mentioned, primary keys are derived from the primary seeds using a deterministic key derivation function (KDF). More accurately, the KDF takes as input the fixed seed and the key's template that describes its properties.
If either the seed or the template changes, a completely different primary key is created
The template is defined in the
TPM2B_PUBLIC structure. Important fields include the type that describes whether this is a symmetric or asymmetric key, the objectAttributes that describes whether the key is used for encryption or signing, the parameters that describes the key size and unique that is used as an entropy value.
By changing the entropy field, the TPM can generate an unlimited number of primary keys of a given type (say RSA-2048).
A primary key is created with the
TPM2_CC_CreatePrimary command. Let's create a RSA-2048 key on the OWNER hierarchy:
TPM2_CC_CreatePrimary creates and loads the key to the TPM: note how the object's handle is returned. The TPM can hold only a limited number (3) of objects at a time. Use
TPM2_CC_FlushContext to unload an object.
Instead of comparing the public key material (rsa_public_n which is copied from out_public.publicArea.unique.rsa.buffer), we should instead compare the key's name. The name, defined in
TPM2B_NAMEstructure is the hash of the canonical form of TPM2B_PUBLIC that describes the key's attributes, parameters and public key material.
The name is the key's fingerprintThe owner's seed doesn't change after a host restart, therefore, the primary key doesn't change after a restart:
Let's see how different keys are created if different template entropy values are used:
Finally, let's see how different keys are created if different seed values are used. We'll use the null hierarchy for this experiment. We'll reboot the host platform between key creation:
Object attributes bitmap field is part of
TPM2B_PUBLIC. For our experiments it's important to understand the following attributes:
- Decrypt (TPMA_OBJECT_DECRYPT). Specifies an encryption key.
- Sign (TPMA_OBJECT_SIGN_ENCRYPT). Specifies a signing key.
- Restricted (TPMA_OBJECT_RESTRICTED). Restricts the key to signing/encrypting only internal TPM data.
Non primary keys are created with the
TPM2_CC_Create command. The command takes as input a handle to the parent key, and the key's TPM2B_PUBLIC template.
TPM2_CC_Create creates but doesn't load the key onto the TPM. The command returns
TPM2B_PRIVATE which is the key material wrapped (encrypted) by the parent key. Use
TPM2_CC_Load to load the key onto the TPM.
TPM supports additional commands to create new keys:
TPM2_CC_Import. We will not cover them here.
Now that we know how to create keys, we can use them to perform cryptographic operations like encryption and signing.
The following snippet shows how to use a non-restricted, symmetric encryption key. It uses the
The following snippet shows how to use an asymmetric key for signing and verification. It uses
We can ask the TPM to sign the message, then verify the signature externally using the public key. Following code uses the SJCL library.
RSA signing works in much the same way:
We can ask the TPM to sign the message, then verify the signature externally using the public key. Following code uses the node-forge library.
To encrypt and decrypt using an RSA encryption key, use the
TPM2_CC_RSA_Decrypt commands. They correctly encode the message before applying the RSA function.
You may have noticed that it takes time to create asymmetric keys, especially long RSA keys. To improve performance you may use
TPM2_CC_EvitControl command: it moves a key from volatile memory to nonvolatile memory so it can remain loaded through power cycles.
Due to the size limitations of NVDATA only a handful of keys can be made persistent.
You can save loaded objects with
TPM2_CC_ContextLoad, however, we'll not discuss that here.
Cryptographic keys are just numbers - by themselves they don't convey any meaning of trust. Trust in cryptographic keys is gained through public key certificates.
As we've seen, keys generated by the TPM are protected by their parent keys - if you trust the parent key is not compromised, then you can trust all the keys rooted under it. What about the primary key? How do you know it's coming from a genuine TPM device, that's conformant to the spec? The answer is the primary key certificate, issued by the TPM manufacturer.
The EK certificate certifies the primary endorsement key. It is issued by the TPM vendor during manufacturing, and signed by a key that's only available to the vendor. The vendor's root key certificate is usually distributed on the vendor's website, where it can be downloaded over SSL.
EK certificate provisioning process looks like this:
At the end, a signed EK certificate is stored in the TPM's NVDATA.
The TPM user can, at any time, read the EK certificate from NVDATA, and verify its signature chain:
If the certificate is valid, and you trust the vendor - then the primary endorsement key can be trusted.
If the EK is trusted, then all the keys protected by it can be trusted.
EK is derived from the eseed and a TPM2B_PUBLIC template. TCG defines two default templates for EKs, one for RSA keys and one for ECC keys. If a vendor certifies a key other than the default RSA-EK or ECC-EK, they have to store the template used for creation and its unique value in NVDATA, next to the EK certificate.
There are reserved NV indices for RSA-EK certificate and ECC-EK certificate. There are reserved NV indices for the EK templates.
EK Certificate Example
Let's see this in action. First, we'll create the root keys and self-signed certificate on the vendor side:
At manufacturing time, the TPM's EK certificate is provisioned and stored on the device:
Writing data to NV storage is done with
At runtime, the EK certificate is read from the device, and verified against the vendor's root certificate:
Reading data from NV storage is done with
The EK protects keys on the endorsement hierarchy. It is trusted thanks to its certificate. Keys on the owner hierarchy, however, cannot be trusted because they're not protected by the EK. It is the owner's responsibility to provision and certify the primary encryption key on the owner hierarchy (here, "owner" refers to the end-user of the TPM - us).
The Storage Rook Key (SRK), is a primary, restricted encryption key on the owner hierarchy, derived from the oseed. The SRK protects keys on the owner hierarchy. It is trusted because the owner certified the key while when ownership.
As we'll see later, having a trusted, restricted signing key is useful for remote system attestation. It's, therefore, recommended to create and certify a restricted signing key during provisioning time. This trusted signing key is also called Attestation Identity Key (AIK).
It's good practice to perform this setup step in a clean environment (OS booted from CD, host is not connected to the network).
The process includes the following steps:
- Read and validate the EK certificate. Proceed only if you trust the vendor.
- Clear the owner hierarchy with
- Create a primary, restricted asymmetric encryption key on the owner hierarchy (
Call this the SRK.
- Certify the SRK with your enterprise CA: create a Certificate Signing Request (CSR) with SRKpub, and ask your internal, trusted CA to sign it. Store the certificate in NV data (
Alternatively, you can store the SRK public key in a remote database. Use this database to verify the SRK's authenticity in the future.
- Make the SRK persistent by evicting it to NVDATA (
- Create a restricted asymmetric signing key under the SRK (
Call this the AIK.
- Certify the AIK with your enterprise CA: create a Certificate Signing Request (CSR) with AIKpub, and ask your internal, trusted CA to sign it. Store the certificate in NV data (
TPM2_CC_NV_Write). Alternatively, you can store the AIK public key in a remote database. Use this database to verify the AIK's authenticity in the future.
It's possible to use the SRK-cert as root of trust for the AIK, however, having an AIKcert can simplify some workflows.
- Make the AIK persistent by evicting it to NVDATA (
Please note, these keys should be set as fixed (non-duplicable): use
TPMA_OBJECT_FIXEDPARENTin the objectAttributes field in their templates.
At this point we have two trusted keys we can use: one for storage (protecting other TPM keys) and one for signing TPM statements.
Platform Configuration Registers (PCRs) are a set of fixed-sized registered in the TPM. Unlike regular registers, PCR values cannot be set to arbitrary values. The only operation they support (besides Read), is Extend.
Thanks to extend cryptographic properties, PCR values cannot be forged. For this reason, PCRs are used to efficiently capture the host system state: a digest of a sequence of measurements is securely stored in the TPM's PCRs.
PCRs start with an initial, well known value, usually zero. Extend takes as input a digest M, and computes the hash of the current PCR value, concatenated with the input value. The result is stored in the PCR. This is called a folding-hash.
- On startup:
- PCR ← 0
- On extend:
- PCR ← Hash(PCR || M)
Hash is a collision resistant cryptographic hash function. A PCR stores a digest of a sequence of measurements. Therefore, PCR extend operation is sensitive to any change in the sequence: if a single bit changes, or the order of the measurements changes, the end result is a completely different hash value.
Let's see this in action. First, open the PCRs window by either clicking on the View menu on the right, or by running the next snippet.
We use our direct access to the simulator in order to read PCR values. We'll later see how to read these values securely through regular TPM commands.
Note how PCRs are initialized to zero. Now we'll extend PCR1 with the digest of the string "Hello".
app.ExtendPcrcomputes the SHA256 hash of the given string, and extends the given PCR with
Try running the previous cell multiple times. See how the PCR value is updated.
Folding hash computation is deterministic: measuring the same sequence results in the same output. We restart the TPM in order to initialize the PCRs to zeros.
Folding hash computation is sensitive to changes: changing any measurement leads to a different output. We restart the TPM in order to initialize the PCRs to zeros.
Reading PCR Values
There's one TPM command you should definitely not use:
The reason: it returns PCR values, but we no security guarantees. An attacker can MITM your communication with the TPM, and forge arbitrary TPM2_CC_PCR_Read responses.
The correct way to read PCR values is through quotes. A quote is a signed statement from the TPM, attesting to its internal state. Use
TPM2_CC_Quoteto read fresh PCR values. We are going to use the AIK, our trusted signing key, to sign the PCR quote.
This shows how the first TPM functionality - performing secure crypto operations - complements its second functionality - reporting host system state.
TPM2_CC_Quote also takes a nonce as input. It's important to use a random nonce for each quote operation. This ensures the quote reports fresh PCR values, and protects against replay attacks.
In the following experiment we'll see how a remote party (also called "remote attester", "remote verifier" or just "verifier") asks for a fresh PCR quote. The process looks like this:
In the previous section we saw how PCRs efficiently record a sequence of measurements. We also saw how to securely read PCR values from the TPM. The last piece of the puzzle is how to get meaningful measurements into the TPM.
The solution is a standardized technology supported by modern firmwares and operating systems called "measured boot".
In measured boot each element in the boot process measures its successor's code and data regions before handing off execution to that element. Measured boot proves the integrity state of the host platform: a compromised OS will be detected because a malicious boot element (bootkit / rootkit) is measured before it has a chance to modify the system.
For our discussion we'll simplify the boot process to have only three components: firmware → bootloader → kernel.
Measured boot as depicted above raises the following questions:
- Who measures the firmware? Or generally, how do we trust the firmware?
- TPM concisely records the host state: it stores a folding hash of all measurements. How do we interpret it? How do we know what was measured?
There are two general answers to the first question. The first, you trust the firmware. The firmware measures it self into PCR0. Firmware vendors are expected to publish their "good know" measurements for firmware blobs they distribute. Using these, one can verify a good firmware was loaded. The second answer involves an immutable code component called Core Root of Trust for Measurement (CRTM). It is the first piece of code that executes on the main processor during the boot process. It measures the firmware blob before passing executing to it.
The answer to the second question is PCR numbers and the boot event log. Remember that we have a (limited) number of PCRs at our disposal. Instead of measuring everything to a single PCR, measured boot measures different things to different PCRs. For instance, the firmware is measured into PCR0. The disk layout is measured into PCR5 and the boot loader is measured into PCR4. This provides a crude way to figure out what component of the system changed during boot. The second way to interpret PCR values is via the boot event log, which we'll discuss next.
The boot event log provides context for measurements. The log details each measurement sent to the TPM. It's maintained by the firmware, bootloader and later the OS. Users can query the OS for its boot event log, and read what components were measured during boot.
A simplified event log might look like:
Fimware blob "firmware.bin", PCR8, digest=0x11223344. Boot app "/EFI/Windows/Bootloader.efi", PCR8, digest=0x55667788. Kernel image "c:\Windows\ntoskrnl.exe", PCR8, digest=0x99AABBCC.
To validate the event log's integrity, you should playback the digests extended into a PCR. Then, read (quote) the actual PCR value from the TPM, and compare it with the expected value from the log. If they match, the event log's integrity is valid, and therefore, can be trusted.
Remote Attestation Protocol
Remote attestation (RA) protocol combines all that we've learned: it lets a remote party query the integrity state of a target host, without assuming any trust on that host. In RA, the remote attester reads the boot event log and a fresh PCR quote. It validates the event log's integrity by comparing the actual PCR values to the expected ones from the log. It then evaluates the integrity state of the host: do we trust this particular firmware that was loaded? This particular boot loader and OS kernel? It can also set a policy around these measurements, and enforce, for instance, that an up-to-date kernel is running.
A compromised host will be detected. An attacker can deny access to the TPM or to the boot event log, but this denial of service will result in an untrusted host.
A session is an internal TPM object that encodes an authorization value.
Until now we've been implicitly using the
TPM2_RS_PW session handle to
authorize commands with either an empty or a user defined password.
The mechanics of using a session is as follows. First, we create a trial session object
TPM2_StartAuthSession command. A trial session accepts user defined PCR values
TPM2_PolicyPCR command. This command updates a folding hash field in the session
object called policy_digest. The policy digest value can be read through
Finally, we plug the policy digest into
publicArea.authPolicy field in
The authPolicy field protects the key object: any authorized command that uses the key must include a handle to a session with the expected auth digest. We'll see how it works in the following sections.
Calculating the authPolicy digest using a trial session is summarized in the following diagram:
A trial session doesn't include any secrets, it's, therefore, possible to compute the expected authPolicy digest offline, without a TPM.
Once a key is protected with authPolicy, it can only be used with a session that encodes the expected
auth digest. Like before, we create a new session object using
this time we create a
TPM2_SE_POLICY session and not a
A call to
TPM2_PolicyPCR hashes the contents of the actual PCR values into the
session policy_digest field. Finally, when we call
TPM2_Unseal, the TPM evaluates the
session's policy digest and compares it against the key's expected authPolicy digest. If they match,
the sealed data is returned to the user, otherwise, it fails with an error.
Policy session with good (expected) PCR values:
Policy session with bad (unexpected) PCR values:
Note that UserWithAuth bit in the object attributes struct should be cleared when calling
TPM2_Create with an auth_policy. This makes the policy mandatory.
Previously we saw how keys protect PCR reports, now we'll see how PCRs protect keys. The two main functionalities provided by the TPM complement each other:
To seal a data blob we've used a
TPM2_ALG_KEYEDHASH key with authPolicy. To seal a key, we're going to use a
TPM2_ALG_SYMCIPHER key with authPolicy. Encryption/decryption operations with this key are protected by a policy session.
Protecting Keys With A Sealed Password
A common practice is to protect keys with a randomly generated password, then seal that password data blob to PCR values. This scheme has the advantage of having an easy, password less access to the key when the host is in a good know state. Additionally, by storing the password offline, the user has a recovery solution: access to the key is always granted when the password is entered manually. We'll build on examples from the previous sections on authorization and sealing.
- Attestation Identity Key. Signing key provided and certified by the TPM owner. Used to sign PCR quotes and certify other keys loaded into the TPM.
- Core Root of Trust for Measurement. Immutable code that measures the firmware.
- Dictionary Attack. Keys protected with user-authorization value are also protected against a dictionary attack: the TPM enters a lockout mode after 3 failed attempts.
- Endorsement Key. The primary encryption key derived from the endorsement hierarchy's seed. Used to identify the machine since it doesn't change during the lifetime of the device. Provisioned and certified by the TPM manufacturer.
- Key Derivation Function. Used to derive key material from a fixed seed.
- Non-Volatile Data. Set of data variables that persist between power cycles. NV data saves private and public variables. Private data includes the TPM's system state and secret seeds. Public data includes certificates and evicted key objects. NV data is limited in size (a few KB).
- Platform Configuration Register. A register inside the TPM used to store system measurements. Folding-hash semantics.
- Remote Attestation. A protocol that lets a remote party read the boot event log and a fresh PCR quote securely. Protocol is used for host integrity verification.
- Trusted Computing Group. Organization that develops and defines industry specifications and standards, supportive of hardware based root of trust.
- Trusted Platform Module. A dedicated cryptographic device. Supports secure key generation and remote system attestation.
- TPM Software Stack. Host software layer that manages TPM resources (objects), serializes commands buffers and unserializes response buffers.