Chapter 7

The TCG Software Stack

The entry point for any programmer writing a trusted computing-enabled application is the Trusted Computing Group Software Stack (TSS). The TSS specifications define an architecture that makes accessing the TPM simple and direct, while exposing all the functionality that the TPM provides in a vendor-neutral way. The TSS also provides APIs for functionality on top of that provided by the TPM, such as

  • The ability to store key objects persistently on disk
  • Connecting with TPMs on both local and remote machines
  • Conversion of data blobs between portable formats (TSS 1.2 only)

In this chapter, we cover the following topics:

  • The overall design and architecture of the TSS
  • TSS API conventions
  • TSS object types and their uses
  • Simple TSS programming examples
  • TSS 1.2 additions and programming concerns

TSS Design Overview

The TSS is composed of three logical components: the TCG device driver library (TDDL), the TCG core service (TCS), and the TCG service provider (TSP). See Figure 7.1 for an architectural overview of the TSS.

Figure 7.1. Architectural overview of the TSS

image

The TDDL is a library that provides an API to interface with the TPM device driver. Generally, TPM vendors will ship a TDDL library along with their TPM device driver so that TSS implementers can easily interface with it. The TDDL offers a small set of APIs to open and close the device driver, send and receive data blobs, query the device driver’s properties, and cancel a submitted TPM command. For embedded applications where a full TSS doesn’t exist and only a small subset of TPM commands will be used, the TDDL may be the best choice for interfacing with the TPM. In most cases, however, the TCS is the sole user of the TDDL.

The TCS layer has several jobs. It provides management of the TPM’s resources, such as authorization session and key context swapping. It also provides a TPM command blob generator, which converts TCS API requests into the necessary byte streams that a TPM understands. It provides a system-wide key storage facility and synchronizes application access from the TSP layer. If the operating system supports it, the TCS layer must be implemented as a system service and should be the sole user of the TDDL. For more information on the TCS layer, see Chapter 10, “The TSS Core Service (TCS).”

The TSP layer is called directly by the application and is implemented as a shared object, or dynamic linked library. The TSP interface (referred to as the “Tspi”) exposes all the TPM’s capabilities and some of its own, such as key storage and pop-up dialog boxes for authorization data. In this chapter, we’ll focus on the practicalities of programming to the Tspi and also its theory of operation.

The TCG Service Provider Interface (Tspi)

The Tspi is designed such that each API is associated with an object type. There are seven object types defined by the 1.1 TSS spec: context, data, TPM, policy, PCR composite, hash, and key objects. The TSS 1.2 specification adds object types for certified migratable key data, non-volatile data, Direct Anonymous Attestation, and delegation families. Each Tspi API is named so that the programmer knows which object type it is operating on. For example, the simplest possible TSS application is shown in Listing 7.1, where both APIs shown operate on the TSP’s context object. Tspi_Context_Create tells the TSP to generate a new context handle for the application to use and return it to the application. All other APIs in the TSP require an object that is associated with some TSP context; therefore, every TSS application must first call Tspi_Context_Create.Tspi_Context_Close frees all resources associated with a context.

Listing 7.1. The Simplest Possible TSS Application. A Context Handle Is Opened with the TSP Library and Then Closed.

TSS_HCONTEXT hContext;

Tspi_Context_Create(&hContext);

Tspi_Context_Close(hContext);

APIs that operate on data objects will begin with Tspi_Data_, those that operate on key objects will begin with Tspi_Key_, and so on. Support functions to get and set attributes of objects are prefixed only by Tspi_, such as Tspi_SetAttribData and Tspi_GetAttribUint32.

TSP Object Types

Each of the TSP object types plays a role in the function of the TSP library. Let’s walk through the roles of each of the different types of objects and how to use them. See Table 7.1 for the list of object types and the C language data type used to represent them in the Tspi.

Table 7.1. TSP Object Types

image

In this section, the code fragments used to demonstrate the usage of each object type will contain some references to objects that are not defined. These examples are meant to be illustrative only—you should see the latest TSS specification for information on the use of each API.[1]

Context Objects

The context object is used to maintain a handle to the current TSP library, connect to local and remote TCS providers, load keys, store and retrieve key objects from disk, and create new working objects. Each newly created object becomes associated with the context that it was created by. To create a new context object, use Tspi_Context_Create, and to close the context, use Tspi_Context_Close. See Listing 7.1 for an example. When Tspi_Context_Create is called, two things happen: A new context object is generated by the TSP, and a policy object is created and associated with the context. This policy object becomes the default policy for all authorized objects created in the context. When a new authorized object is created using Tspi_Context_CreateObject, it is associated with the context’s default policy by default. If the use of that object requires authorization, the context’s default policy is queried by the TSP for the authorization data.

In order to send commands to a TPM, a TSP’s context must connect to a TCS provider. This is done using the Tspi_Context_Connect API. Tspi_Context_Connect takes a context handle and the UTF-16–encoded hostname or IP address of the destination system. To connect to the TCS on the local machine, a NULL pointer is used as the destination address. If a connection to the TCS provider is successful, a TPM object is implicitly created by the TSP and associated with the context.

Any number of contexts may be created by an application and connected to any number of TCS providers. In a 1.2 TSS, there is a notion of the context’s connection version, which is used to control the type of objects created using the context. By default, the context’s connection version is 1.1, meaning that regardless of the version of TPM that you connect to using Tspi_Context_Connect, objects created by the context will be compatible with a 1.1 TPM. See Table 7.2 for the type of objects created when the connection version changes. To set the TSP context so that it will always create TSS 1.2 compatible objects, do the following:

        Tspi_SetAttribUint32(hContext,
                             TSS_TSPATTRIB_CONTEXT_VERSION_MODE, 0,
                             TSS_TSPATTRIB_CONTEXT_VERSION_V1_2);

Table 7.2. The Types of Objects Created by Tspi_Context_CreateObject Based on the TSP Context’s Connection Version

image

Or to tell the TSP to auto detect and create objects based on the version of TPM it is connected to, do the following:

        Tspi_SetAttribUint32(hContext,
                             TSS_TSPATTRIB_CONTEXT_VERSION_MODE, 0,
                             TSS_TSPATTRIB_CONTEXT_VERSION_AUTO);

You may need to select which types of objects are created by your context depending on whether you need compatibility with migration of keys to different versions of TPMs. For instance, you may want to continue to create TPM 1.1 keys on 1.2 TPMs as new machines are rolled out so that you can guarantee migration between them.

TPM Objects

A TPM object is created implicitly when a TSP context is connected to a TCS provider. To retrieve a handle to the TPM object after a TCS connection is made, use Tspi_Context_GetTPMObject. When the TPM object is created, it also gets its own policy object, which is typically used to hold the authorization data for the TPM owner. Listing 7.2 shows the set of Tspi calls necessary to set up an owner-authorized command.

Listing 7.2. TSP API Calls Necessary to Set Up an Owner-Authorized Command

TSS_HCONTEXT hContext;
TSS_HTPM     hTPM;
TSS_HPOLICY  hOwnerPolicy;

Tspi_Context_Create(&hContext);

/* Connect to the local TCS provider */
Tspi_Context_Connect(hContext, NULL);

/* Get the handle to the implicitly created TPM object */
Tspi_Context_GetTPMObject(hContext, &hTPM);

Tspi_GetPolicyObject(hTPM, TSS_POLICY_USAGE, &hOwnerPolicy);

/* See listing 7.3 for the details of setting secrets */
Tspi_Policy_SetSecret(hOwnerPolicy, ...);

/* Put your owner-authorized commands here... */

Policy Objects

Policy objects hold authorization data for use in commands that require it. Authorization data is needed to load keys, migrate keys, encrypt and decrypt data, take ownership of a TPM, and get and set sensitive properties of the TPM. Policies come in three types: usage, migration, and operator. Migration policies are used only when creating migratable keys. The operator policy is used to hold authorization data for the TSS 1.2’s Tspi_TPM_SetTempDeactivated API. All other policies are considered usage policies.

The method a policy uses for getting its authorization data is called its secret mode. By default, all policies are created with secret mode TSS_SECRET_MODE_POPUP, which means that when it’s used, the TSP will provide a graphical pop-up dialog box to retrieve the authorization data from the user. After retrieving the authorization data through the pop-up dialog the first time, the authorization data is saved and used whenever that policy is accessed. Unless Tspi_Policy_FlushSecret is called on the policy to flush the secret data, the pop-up will not be triggered again. Table 7.3 shows all the possible secret modes for a policy.

Table 7.3. Secret Modes for a Policy

image

The context object and TPM object each have their own policies created implicitly by the TSP. All newly created TSP objects that use policies (keys and data) then get a reference to the context object’s policy. This means that for each object that needs a unique password, a new policy must be created and assigned to it. Listing 7.3 shows the process of creating a new key with a unique password.

Listing 7.3. Creating a Key with a Password Set to “Secret”

TSS_HCONTEXT hContext;
TSS_HKEY     hSRK, hKey;
TSS_HPOLICY  hPolicy;
BYTE         *secret = "secret";

Tspi_Context_Create(&hContext);

/* Connect to the local TCS provider */
Tspi_Context_Connect(hContext, NULL);
/* Load the new key's parent key, the Storage Root Key */
Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
                           SRK_UUID, &hSRK);

/* Create the software key object */
Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY,
                          TSS_KEY_SIZE_2048 |
                          TSS_KEY_TYPE_SIGNING |
                          TSS_KEY_AUTHORIZATION,

                          &hKey);
/* Create the usage policy object for the key */
Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_POLICY,
                          TSS_POLICY_USAGE, &hPolicy);

/* Set the secret in the policy */
Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_PLAIN,
                      strlen(secret), secret);

/* Assign the policy object to the key */
Tspi_Policy_AssignToObject(hPolicy, hKey);

/* Call down to the TPM to generate the key */
Tspi_Key_CreateKey(hKey, hSRK, 0);

Policy object secrets can also be set to expire based on the number of times used or a number of seconds. For example, to create a policy that expires in 10 seconds, call:

        Tspi_SetAttribUint32(hPolicy,
                             TSS_TSPATTRIB_POLICY_SECRET_LIFETIME,
                             TSS_TSPATTRIB_POLSECRET_LIFETIME_TIMER, 10);

And to create a policy that expires after one use, call:

        Tspi_SetAttribUint32(hPolicy,
                             TSS_TSPATTRIB_POLICY_SECRET_LIFETIME,
                             TSS_TSPATTRIB_POLSECRET_LIFETIME_COUNTER,1);

Secrets are processed in different ways depending on their type, but they all end up as a SHA-1 hash internally in the TSP library. This SHA-1 hash is used in an HMAC calculation to establish an authorization session with the TPM. Unfortunately, the difference in processing for each secret type can lead to an incorrect hash being created and the authorization session failing. Table 7.4 documents the hash creation process for each secret type.

Table 7.4. Hash Creation Process for Each Secret Type

image

If your TSS application needs to operate in an environment with mixed user interaction (sometimes using pop-up policies, and other times not), it is prudent to convert your plain-text secrets to UTF-16LE before setting them in their policies. Also, in mixed TSS 1.1 and 1.2 environments, pay close attention to whether the NULL termination characters are included with your secrets. In the TSS version 1.2, an attribute of the context object, TSS_TSPATTRIB_SECRET_HASH_MODE, can be used to control whether the NULL terminating characters are included in secrets received from pop-up dialogs. To force the TSS 1.2 stack to include NULL terminating characters for compatibility with TSS 1.1, call:

        Tspi_SetAttribUint32(hContext, TSS_TSPATTRIB_SECRET_HASH_MODE,
                             TSS_TSPATTRIB_SECRET_HASH_MODE_POPUP,
                             TSS_TSPATTRIB_HASH_MODE_NULL);

After making this call, all policies created explicitly or implicitly by the context hContext will include the terminating NULL characters in the pop-up secret data they hash.

Key Objects

Key objects are used to represent TPM keys, which are actually RSA public/private key pairs. For a description of the different key types, see Chapter 3, “An Overview of the Trusted Platform Module Capabilities.”

Key objects can be created like other objects, by calling Tspi_Context_CreateObject, or using calls to the TSS’s persistent storage functions, such as Tspi_Context_GetRegisteredKeyByUUID. To have a new 1024-bit binding key pair created by the TPM, at least two calls are necessary:

        TSS_HKEY hKey;

        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY,

        TSS_KEY_SIZE_1024|TSS_KEY_TYPE_BIND|TSS_KEY_NO_AUTHORIZATION,
                                  &hKey);

        */ Create hKey, with hParentKey as its parent key (which must
         * already be loaded into the TPM) and 0 for the PCR composite
         * object handle in order to keep the key from being bound to
         * any PCRs */
        Tspi_Key_CreateKey(hKey, hParentKey, 0);

The call to Tspi_Context_CreateObject creates a skeleton software key object inside the TSP library with the properties requested. The call to Tspi_Key_CreateKey will send that skeleton key to the TPM. The TPM will generate the key pair based on the properties of the software key and return the generated pair to the TSP. The TSP will then add the generated key pair data to its software object. Note that hParentKey must be loaded into the TPM prior to the Tspi_Key_CreateKey call.

Key objects are exported from the TSS in “blob” form—that is, the byte stream format used by the TPM. To retrieve a key in its blob form, use Tspi_GetAttribData:

        BYTE   *keyBlob;
        UINT32 keyBlobLen;

        Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB,
                           TSS_TSPATTRIB_KEYBLOB_BLOB,
                           &keyBlobLen, &keyBlob);

After the call successfully returns, keyBlob will point to an allocated memory area of size keyBlobLen holding a serialized TCPA_KEY structure. There are other flags for Tspi_GetAttribData to get only the public key portion (a TCPA_PUBKEY blob) of the key:

        BYTE   *pubkeyBlob;
        UINT32 pubkeyBlobLen;

        Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB,
                           TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
                           &pubkeyBlobLen, &pubkeyBlob);

And only the RSA modulus:

        BYTE   *modulus;
        UINT32 modulusLen;

        Tspi_GetAttribData(hKey, TSS_TSPATTRIB_RSAKEY_INFO,
                           TSS_TSPATTRIB_KEYINFO_RSA_MODULUS,
                           &modulusLen, &modulus);

The difference between the TCPA_PUBKEY structure and the RSA modulus is that the TCPA_PUBKEY structure contains a TCPA_KEY_PARMS structure, which holds the encryption and signature schemes for the key as well as other properties of the key.

Encrypted Data Objects

Data objects are used to hold sealed and bound data blobs during a seal or bind operation. Data can be inserted or extracted from them using Tspi_SetAttribData and Tspi_GetAttribData, respectively. During a bind or seal operation, the data encrypted will automatically be inserted into the encrypted data object by the TSS:

        TSS_HENCDATA hEncData;
        BYTE         *blob, *data = "data";
        UINT32       blobLen;

        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA,
                                  TSS_ENCDATA_BIND, &hEncData);

        /* Call the TSS to bind the data, storing the encrypted data
         * blob in hEncData */
        Tspi_Data_Bind(hEncData, hKey, strlen(data), data);

        /* Retrieve the encrypted data blob to store elsewhere */
        Tspi_GetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB,
                           TSS_TSPATTRIB_ENCDATABLOB_BLOB,
                           &blobLen, &blob);

The preceding example is simplified, not showing the creation of the key or context. Creating and using encrypted data objects is very straightforward, but there is one thing to remember—always free the data returned by Tspi_GetAttribData using Tspi_Context_FreeMemory. See the “TSS Memory Management” section of this chapter for more information.

Hash Objects

Hash objects are used to hold hash values, compute the hash of data, and sign and verify hashes using keys. Natively, the TSS supports hashing data using the SHA1 algorithm only, although it can sign and verify hashes of any type. To create a SHA1 hash of some data, use Tspi_Hash_UpdateHashValue:

        TSS_HHASH hHash;
        BYTE      *digest, *data = "data to hash";
        UINT32    digestLen;

        /* Create the hash object */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH,
                                  TSS_HASH_SHA1, &hHash);

        /* Hash the data.  The TSS knows to use SHA1 as the algorithm
         * because the object is of type TSS_HASH_SHA1 */
        Tspi_Hash_UpdateHashValue(hHash, strlen(data), data);

        /* Retrieve the digest from the hash object */
        Tspi_Hash_GetHashValue(hHash, &digestLen, &digest);

Although the TPM supports it, the SHA1 hashing in the TSP is done by the TSS, not the TPM. The TPM’s SHA1 hashing routines are provided mainly for use before a full operating system environment is available, such as during BIOS load, since adding an implementation of the SHA1 algorithm would be too expensive in those environments. Note that calling Tspi_Hash_UpdateHashValue implies that the TSS knows how to calculate the hash value for the hash algorithm associated with the TSP hash object. Because the SHA1 algorithm is the only supported algorithm in TSS 1.1 and 1.2, calling Tspi_Hash_UpdateHashValue on a hash object of any other type will return TSS_E_INTERNAL_ERROR.

If you need to sign or verify a hash value made using another hashing algorithm such as md5 or SHA256, you cannot use the TSS to create the hash value. You will need an external library that supports hashing using your required algorithm. Once the hash value is created using your required algorithm, use TSS_HASH_OTHER and Tspi_Hash_SetHashValue to set the hash value in the TSS object:

        TSS_HHASH hHash;
        BYTE      *digest = /* hash value */;
        UINT32    digestLen = /* hash len */;

        /* Create the hash object as some type other than SHA1 */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH,
                                  TSS_HASH_OTHER, &hHash);
        /* Set the digest in the hash object */
        Tspi_Hash_SetHashValue(hHash, digestLen, digest);

Note that once a hash object is created as type TSS_HASH_OTHER, Tspi_Hash_UpdateHashValue cannot be called on it, since Tspi_Hash_UpdateHashValue only operates on SHA1 hash objects.

For information on signing and verifying hashes and data using the TSS, see the section later in the chapter, “Signing and Verifying.”

PCR Composite Objects

PCR composite objects represent an array of hashes and their composite digest value. Creating a PCR composite object is done for one of two possible operations: those that need only a selection of PCR indices, such as Tspi_TPM_Quote, and those that require a selection of indices and a set of values, such as Tspi_Data_Seal. In the case of doing a quote operation, where the TPM signs a set of PCR values using a specified key, all the TPM needs to know is which PCRs to sign. In this case, use Tspi_PcrComposite_SelectPcrIndex:

        TSS_HPCRS      hPcrs;
        TSS_VALIDATION validationData; /* initialization not shown */
        UINT32         i, pcrsToSelect[3] = { 7, 8, 9 };

        /* Create a PCR composite object. */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, 0,
                                  &hPcrs);

        /* Select PCR indices 7, 8 and 9 */
        for (i = 0; i < 3; i++)
            Tspi_PcrComposite_SelectPcrIndex(hPcrs, pcrsToSelect[i]);

        /* Quote on PCRs 7, 8 and 9.  See the section below for info
         * on validation data */
        Tspi_TPM_Quote(hTPM, hKey, hPcrs, &validationData);

Selecting an index of a PCR composite object merely flips a bit in the object, indicating that the PCR index should be considered by the TPM in any operation that uses it. For operations that require specific PCR values to be set in an object, see Listing 7.4.

Listing 7.4. Setting Up a PCR Composite Object for Use in Sealing Data

TSS_HPCRS   hPcrs;
TCPA_DIGEST digestValues[3] = { ..., ..., ... };
UINT32      i, pcrsToSelect[3] = { 7, 8, 9 };

/* Create the PCR composite object */
Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS, 0,
                          &hPcrs);

/* Select PCR indices 7, 8 and 9 and set their values in the
* object */
for (i = 0; i < 3; i++) {
    Tspi_PcrComposite_SelectPcrIndex(hPcrs, pcrsToSelect[i]);

    Tspi_PcrComposite_SetPcrValues(hPcrs, pcrsToSelect[i],
                                   sizeof(TCPA_DIGEST),
                                   digestValues[i]);
}

/* Seal some data to the PCR values in the digestValues
* array on PCRs 7, 8 and 9 */
Tspi_Data_Seal(hEncData, hKey, ulDataLength, rgbDataToSeal,
               hPcrs);

In Listing 7.4, the digestValues array contains three 20-byte SHA-1 hash values. These three hash values are passed to Tspi_PcrComposite_SetPcrValue, which then sets them as values for PCR registers 7, 8, and 9 inside the PCR composite object. You might notice that no initialization flags were passed to Tspi_Context_CreateObject when creating the PCRs object. In the 1.1 TSS, there were no valid flags for a PCRs object. In version 1.2 of the TSS, there are three flags to control which type of structure is associated with the PCRs object. To create a TCPA_PCR_INFO structure, either pass 0 for the initialization flags as you did in TSS 1.1, or use TSS_PCRS_STRUCT_INFO:

        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
                                  TSS_PCRS_STRUCT_INFO, &hPcrs);

To create a TPM_PCR_INFO_LONG structure, use TSS_PCRS_STRUCT_INFO_LONG:

        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
                                  TSS_PCRS_STRUCT_INFO_LONG, &hPcrs);

To default to the type of object that should be created based on the context’s connection version, use TSS_PCR_STRUCT_DEFAULT:

        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
                                  TSS_PCRS_STRUCT_DEFAULT, &hPcrs);

See the earlier “Context Objects” section for more information on setting the context’s connection version.

Non-Volatile Data Objects (TSS 1.2)

Non-volatile data objects represent areas of flash storage (NVRAM) inside the TPM itself. NVRAM storage is a feature of 1.2 TPMs that’s meant to replace the less-flexible Data Integrity Register (DIR) functionality found in 1.1 TPMs. DIRs were 160-bit areas of flash inside the TPM that could be written to and read from using owner authorization and the TSS’s Tspi_TPM_DirRead and Tspi_TPM_DirWrite APIs. NVRAM objects have expanded this functionality to allow predefined areas of flash inside the TPM for storing certificates and data as well as unused areas that can be defined by the TSS. Unlike DIRs, NVRAM areas can also be associated with PCR values, so that the TPM can enforce reading or writing based on its PCR state.

You might notice that although the TPM command to define a new NVRAM space in the TPM is owner authorized, there is no TPM object associated with the NV Tspi APIs to receive the owner’s authorization. This was a slight oversight by the TSS working group of the TCG, but to work around it, Tspi_NV_DefineSpace will transparently look up the context object associated with the TSS_HNVSTORE object passed in, find the TPM object associated with that context, and use the owner’s authorization data from its policy to authorize the command. If you’d like to define NVRAM space that requires authorization, set the secret for the space in the NVRAM object’s policy before calling Tspi_NV_DefineSpace.

To create an NVRAM object and use it to store data that requires no authorization to read, do the following:

        TSS_HCONTEXT hContext;
        TSS_HTPM     hTPM;
        TSS_HPOLICY  hOwnerPolicy;
        TSS_HNVSTORE hNvStore;
        BYTE         *data = /* some data */;
        UINT32       dataLen = /* length of the data */;

        Tspi_Context_Create(&hContext);

        /* Connect to the local TCS provider */
        Tspi_Context_Connect(hContext, NULL);

        /* Get the handle to the implicitly created TPM object */
        Tspi_Context_GetTPMObject(hContext, &hTPM);

        Tspi_GetPolicyObject(hTPM, TSS_POLICY_USAGE, &hOwnerPolicy);

        /* See listing 7.3 for the details of setting secrets */
        Tspi_Policy_SetSecret(hOwnerPolicy, ...);

        /* Create the NVRAM object */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_NV, 0,
                                  &hNvStore);

        /* Set the attribute in the NVRAM object so that NV index 1 is
         * used. Selecting index 1 is essentially an arbitrary choice.
         * If you choose an index that already exists,
         * Tspi_NV_DefineSpace will return an error */
        Tspi_SetAttribUint32(hNvStore, TSS_TSPATTRIB_NV_INDEX, 0, 1);

        /* Set the permissions to require authorization to write to the
         * NVRAM area.  This may require a policy object to be
         * associated with the hNvStore object to hold the secret data */
        Tspi_SetAttribUint32(hNvStore, TSS_TSPATTRIB_NV_PERMISSIONS, 0,
                             TPM_NV_PER_AUTHWRITE);

        /* Set the size of the area we're about to define */
        Tspi_SetAttribUint32(hNvStore, TSS_TSPATTRIB_NV_DATASIZE, 0,
                             dataLen);

        /* Call down to the TPM to define the space.  Pass in zeroes
         * for the handles to PCR objects – we aren't associating this
         * NVRAM space with any PCR values */
        Tspi_NV_DefineSpace(hNvStore, 0, 0);

        /* Write the data into the defined flash area */
        Tspi_NV_WriteValue(hNvStore, 0, dataLen, data);

Migratable Data Objects (TSS 1.2)

Migratable data objects are used with Certified Migratable Keys (CMKs) to hold properties and migratable data blobs when passing them between APIs. Attributes of migratable data objects are set and retrieved using Tspi_SetAttribData and Tspi_GetAttribData in the same way as other objects. Refer to the TSS specification for information on the various attributes of migratable data objects.

Delegation Family Objects (TSS 1.2)

Delegation family objects are used to manage the delegation tables used by the TPM. Attributes of delegation family objects are set and retrieved using Tspi_SetAttribData and Tspi_GetAttribData in the same way as other objects. See Chapter 16 for detailed information and example code for use in delegating TPM ordinals and authorizations.

Direct Anonymous Attestation (DAA) Objects (TSS 1.2)

There are three different types of objects used to represent the keys and certificates used in the DAA protocols: TSS_HDAA_CREDENTIAL, TSS_HDAA_ISSUER_KEY, and TSS_HDAA_ARA_KEY. The key data types used with DAA key and certificate objects are set and retrieved from their TSP objects using Tspi_SetAttribData and Tspi_GetAttribData in the same way as key objects. See the “Key Objects” section for some examples.

TSS Return Codes

The values returned from Tspi APIs are unsigned 32-bit integers, represented by the TSS_RESULT type. These values contain encoded data indicating which TSS layer produced the error, the error value itself, and some operating system-specific data. Due to this packing of data into one value, some care needs to be taken when checking for specific error codes to prevent mistakes. Figure 7.2 shows the layout of a TSS_RESULT.

Figure 7.2. Layout of a TSS_RESULT

image

The upper 16 bits of a TSS_RESULT store the operating system-specific information. The next 4 bits are used to define the TSS layer that the error occurred in (TSP, TCS, TDDL, or TPM) and the lowest 12 bits represent the actual error code. In the TSS 1.1 and 1.2 specifications, only MS Windows® has defined use of the operating system-specific space. All other operating systems are advised to set this area to 0. See the latest TSS specification for more information on use of the OS-specific section.

In the TSS 1.2 header files, some preprocessor macros have been defined to make error code processing easier. ERROR_CODE and ERROR_LAYER are bit masks that strip away all but the indicated information. So, for example, a TSS application writer who wants to check for TSS_E_PS_KEY_NOTFOUND, which can occur at the TSP or TCS level, might naively write this segment of code:

        result = Tspi_Context_GetRegisteredKeyByUUID(...);
        if (result == TSS_E_PS_KEY_NOTFOUND) {
            /* This code cannot be reached! */
        }

The preceding code would never pass into the error-handling section, since there will always be error layer information included in the return value. The following modification to the previous code will fix the problem:

        result = Tspi_Context_GetRegisteredKeyByUUID(...);
        if (ERROR_CODE(result) == TSS_E_PS_KEY_NOTFOUND) {
            /* process the error */
        }

Adding the ERROR_CODE wrapper around the result strips off the error layer information and the operating system-specific information and the comparison to the return value is made correctly.

TSS Memory Management

Many Tspi APIs return data to the application that has been allocated internally by the TSP. This data is tracked by the TSP’s internal memory management routines and must be freed using the Tspi_Context_FreeMemory API. Calling the system’s free routine on memory allocated by the TSP will likely cause double-free errors and corrupt your application’s memory space when the TSS closes.

Portable Data

When passing data in between TSSs on different platforms, it may be necessary to encode the data in some platform-neutral way so that the data can be consumed by both TSSs. The TSS 1.2 specification provides just such a mechanism in the Tspi_EncodeDER_TssBlob and Tspi_DecodeBER_TssBlob APIs. By default, TSS APIs will return data in blob form according to the “Portable Data” section of the TSS specification. For instance, when Tspi_SetAttribData is called to retrieve a TCPA_PUBKEY blob from a TSP key object, the TSS specification states that the data returned should be in the same form as the TPM would output:

        BYTE   *pubKeyBlob;
        UINT32 pubKeyBlobLen;

        /* Pull the TCPA_PUBKEY blob out of the key object */
        Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB,
                           TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
                           &pubKeyBlobLen, &pubKeyBlob);

Using this public key blob in the same application will be straightforward, since the current application will know what the data blob represents, but how will another TSS know what’s in it? The other TSS could try parsing it to determine what it is, but that approach can be difficult. Instead of exporting the blob directly, the application should call Tspi_EncodeDER_TssBlob to encode the data:

        BYTE   *pubKeyBlob, encodedBlob[512];
        UINT32 pubKeyBlobLen, encodedBlobLen = 512;

        /* Pull the TCPA_PUBKEY blob out of the key object */
        Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB,
                           TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
                           &pubKeyBlobLen, &pubKeyBlob);

        /* Call the blob encoding API to produce a platform neutral
         * encoding of the data */
        Tspi_EncodeDER_TssBlob(pubKeyBlobLen, pubKeyBlob,
                               TSS_BLOB_TYPE_PUBKEY, &encodedBlobLen,
                               encodedBlob);

Data produced by Tspi_EncodeDER_TssBlob can be passed to another TSS without any description of the data being necessary. To determine what data you’ve received from another TSS, use Tspi_DecodeBER_TssBlob:

        BYTE   rawBlob[512];
        UINT32 blobType, rawBlobLen = 512;

        Tspi_DecodeBER_TssBlob(encodedBlobLen, encodedBlob, &blobType,
                               &rawBlobLen, rawBlob);

        switch (blobType) {
            case TSS_BLOB_TYPE_PUBKEY:
                /* handle the TCPA_PUBKEY blob */
                break;

            /* ... */
        }

Use of the blob-encoding routines will enable your TSS application to interact with other TSS applications regardless of their platform type or endianess. Unfortunately, these routines are only specified by the TSS 1.2 specification. If you’re targeting a TSS 1.1 implementation for your app, you’ll need to create your own mechanism for passing data around safely.

Persistent Key Storage

One service that the TSS offers independent of a TPM is key storage. At the TCS level, keys are stored (or registered) persistently on disk in what is called system persistent storage. The system persistent key store can be accessed by any application without restriction, due to the fact that the TPM is not involved in the operation of key registration to make any enforcement decisions. Because a rogue application could unregister any key in the system persistent store, it is generally of limited usefulness. The user persistent store, however, is managed by the TSP library and is private to the user of the application accessing it. Although its location is TSS specific, the user persistent key store is generally kept in a location accessible to the owner of the process that the TSP is operating as, such as that user’s home directory.

Both user and system key stores use the TSS_UUID structure to address keys in the store. Each key in persistent storage has a UUID that is unique to that key store. If an application attempts to store a key using a UUID that already exists, the API will return TSS_E_KEY_ALREADY_REGISTERED. The TCG has predefined several UUID values to address well-known keys, such as the TPM’s storage root key. To load the storage root key, call Tspi_Context_LoadKeyByUUID:

        TSS_HKEY hSRK;
        /* TSS_UUID_SRK is a well-known UUID defined in the TSS header
         * files */
        TSS_UUID SRK_UUID = TSS_UUID_SRK;

        /* Create an application handle to the SRK */
        Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM,
                                   SRK_UUID, &hSRK);

To store a key in system persistent storage, use Tspi_Context_RegisterKey. The application must pass in UUID information for the key it wants to store, as well as that key’s parent key. So, to store a child of the storage root key in user persistent storage, do the following:

        TSS_UUID SRK_UUID = TSS_UUID_SRK;
        TSS_UUID keyUUID = /* Unique UUID value */;

        Tspi_Context_RegisterKey(hContext, hKey, TSS_PS_TYPE_USER,
                                 keyUUID, TSS_PS_TYPE_SYSTEM,
        SRK_UUID);

Then to load the key from storage:

        TSS_HKEY hKey;
        TSS_UUID keyUUID = /* UUID value */;
        /* Create an application handle to the key and load it into
         * the TPM */
        Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_USER,
                                   keyUUID, &hKey);

Using the TSS’s persistent storage functionality is a handy way to save and load keys for any application. Managing a large store of keys is made easier by other TSS functions that can query and return description information on the entire TSS key hierarchy, such as Tspi_Context_GetRegisteredKeysByUUID:

        TSS_KM_KEYINFO *info;
        UINT32         infoLen;


        Tspi_Context_GetRegisteredKeysByUUID(hContext,
                                             TSS_PS_TYPE_SYSTEM, NULL,
                                             &infoLen, &info);

Making the preceding call will return an array of TSS_KM_KEYINFO structures that describe every key stored in the system persistent key store. The key info structure contains the UUID of each key and its parent, whether the key requires authorization, whether the key is loaded or not, and optional vendor data that can be set by the TSS. The preceding code examples will work for a TSS 1.1 or 1.2 application. In addition, the TSS 1.2 specification offers a TSS_KM_KEYINFO2 structure, which contains all the information as the TSS_KM_KEYINFO structure, but additionally includes the persistent storage type flag (TSS_PS_TYPE_SYSTEM or TSS_PS_TYPE_USER) that each key is stored in. To retrieve a TSS_KM_KEYINFO2 structure, use Tspi_Context_GetRegisteredKeysByUUID2.

Signing and Verifying

The process of signing and verifying data is made slightly more complex when using the TSS due to the fact that there are different behaviors, depending on the signature scheme associated with the key you use. Because the TPM cannot know all possible hash algorithms, it only provides support for signing SHA1 hashes and leaves support for all others up to the TSS and application. The general process for creating the digital signature of a hash value is the following:[2]

1. Create H1, the hash value to sign.

2. Create D1 by concatenating the Object Identifier (OID) for the hash algorithm used to generate H1 with H1 itself.

3. Create P1 by padding D1 using PKCS#1 v1.5 padding.

4. Create the signature by encrypting P1 with the private key.

When signing using the TSS, this process is kept intact. For signatures on hash values other than SHA1, however, the TSS must execute step 2 manually, since the TPM doesn’t know which hash algorithm it’s signing. Here’s some sample code to demonstrate signing a SHA1 hash:

        TSS_HHASH hHash;
        BYTE      *sig, *digest = /* hash value */;
        UINT32    sigLen, digestLen = 20;

        /* Create the hash object as type SHA1 */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH,
                                  TSS_HASH_SHA1, &hHash);

        /* Set the digest in the hash object */
        Tspi_Hash_SetHashValue(hHash, digestLen, digest);

        /* Sign the hash using hKey, a signing key with signature
         * scheme TSS_SS_RSASSAPKCS1V15_SHA1 */
        Tspi_Hash_Sign(hHash, hKey, &sigLen, &sig);

In this example, the data flow between the application, TSS, and TPM looks like Figure 7.3.

Figure 7.3. Signature flow of a SHA1 hash between the application and TPM

image

As you can see, the hash data is passed down to the TPM, which adds the SHA1 OID and padding and then does the encryption. To sign data other than a SHA1 hash, the signature scheme for the key used should be TSS_SS_RSASSAPKCS1V15_DER and the OID for the algorithm should be added manually by the application:

        TSS_HHASH hHash;
        BYTE      *sig, *digestAndOID = /* hash value || OID */;
        UINT32    sigLen, digestLen = /* Length of digestAndOID */;

        /* Create the hash object as some type other than SHA1 */
        Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH,
                                  TSS_HASH_OTHER, &hHash);

        /* Set the digest in the hash object */
        Tspi_Hash_SetHashValue(hHash, digestLen, digestAndOID);

        /* Sign the hash using hKey, a signing key with signature
         * scheme TSS_SS_RSASSAPKCS1V15_DER */
        Tspi_Hash_Sign(hHash, hKey, &sigLen, &sig);

In this example, the TPM pads and signs only the data it receives; therefore, this process can be used to sign arbitrary data. Figure 7.4 shows the data flow between the application, TSP library, and TPM when using the _DER signature scheme.

Figure 7.4. Signature flow of a non-SHA1 hash between the application and TPM

image

The two code examples shown previously are compatible with both TSS 1.1 and 1.2, but the TSS 1.2 specification has added an attribute of the hash object to aid in adding the OID. To have the TSS automatically add an OID to the hash value you set using Tspi_Hash_SetHashValue, use TSS_TSPATTRIB_ALG_IDENTIFIER:

        BYTE   *oid = /* Your hash algorithm's OID */;
        UINT32 oidLen = /* Length of oid */;

        Tspi_SetAttribData(hHash, TSS_TSPATTRIB_ALG_IDENTIFIER, 0,
                           oidLen, oid);

If the algorithm identifier attribute of the hash object has been set, the TSS will add that data to the hash value before sending it to the TPM for signing. Otherwise, the TSS assumes that you added the OID data in the Tspi_Hash_SetHashValue call.

Setting Callback Functions

The Tspi allows applications to use callbacks for several different operations, such as the authorization process and the identity key creation process. Callbacks are available to keep the application from having to reveal secret data to the TSS. In the TSS version 1.1, all callbacks were set using Tspi_SetAttribUint32 on the appropriate object. Unfortunately, this did not allow applications compiled on platforms with 64-bit pointer types to use callbacks. The problem was solved in the TSS 1.2 specification by introducing the TSS_CALLBACK structure:

        typedef struct tdTSS_CALLBACK
        {
            PVOID            callback;
            PVOID            appData;
            TSS_ALGORITHM_ID alg;
        } TSS_CALLBACK;

For applications written for the TSS 1.2 specification, create a TSS_CALLBACK structure and set it in the TSP object using Tspi_SetAttribData:

        TSS_CALLBACK cb;

        cb.callback = my_callback_func;
        cb.appData = my_data;
        cb.alg = TSS_ALG_3DES;

        Tspi_SetAttribData(hPolicy, TSS_TSPATTRIB_POLICY_CALLBACK_HMAC,
                           0, sizeof(TSS_CALLBACK), &cb);

Since the TSS 1.2 interface is more portable, it should be used wherever possible by applications. If your application must be compatible with TSS 1.1, use the deprecated TSS 1.1 interface, Tspi_SetAttribUint32:

        /* This will not work on 64-bit platforms! */
        Tspi_SetAttribUint32(hPolicy,
                             TSS_TSPATTRIB_POLICY_CALLBACK_HMAC,
                             my_data, my_callback_func);

To clear a callback function you’ve already set, pass NULL (or 0) as the address of the callback using either Tspi_SetAttribData or Tspi_SetAttribUint32:

        Tspi_SetAttribData(hPolicy,TSS_TSPATTRIB_POLICY_CALLBACK_HMAC,
                           0, 0, NULL);

or

        Tspi_SetAttribUint32(hPolicy,
                             TSS_TSPATTRIB_POLICY_CALLBACK_HMAC,
                             0, 0);

After clearing the callback address in the preceding examples, the policy’s type will still be set to TSS_SECRET_MODE_CALLBACK. If the policy is used by the TSS, an error will occur. To keep this from happening, set the policy’s secret mode to something else after clearing the callback address.

The TSS Validation Data Structure

The Tspi needed a way to allow applications to provide data to some APIs for use in their operations. For instance, when Tspi_TPM_GetPubEndorsementKey is called, the TSS passes a 20-byte nonce to the TPM so that the TPM can include it in a hash. The TPM calculates

        checksum = SHA1(TSS_Nonce || TCPA_PUBKEY(EK))

and returns the calculated checksum data to the TSS. When the TSS receives that data back from the TCS, it computes the same hash value using the returned EK’s TCPA_PUBKEY structure and compares it with the checksum returned by the TPM. This allows the TSS to verify the integrity of the data returned from the TPM, but what if the application cannot trust the TSS? To allow the application to pass data into the TSS for uses such as this, use the TSS_VALIDATION structure:

        typedef struct tdTSS_VALIDATION
        {
            TSS_VERSION versionInfo;
            UINT32      ulExternalDataLength;
            BYTE*       rgbExternalData;
            UINT32      ulDataLength;
            BYTE*       rgbData;
            UINT32      ulValidationDataLength;
            BYTE*       rgbValidationData;
        } TSS_VALIDATION;

To have the TSS pass a nonce of your choosing to the TPM, set up a TSS_VALIDATION structure by setting the external (application provided) data parameters and pass it to Tspi_TPM_GetPubEndorsementKey:

        TSS_VALIDATION vData;

        vData.rgbExternalData = /* 20 byte nonce */;
        vData.ulExternalDataLength = 20;

        Tspi_TPM_GetPubEndorsementKey(hTPM, FALSE, &vData, &hPubEK);

After the API is called, the data and validation data fields of the structure will have been filled in by the TSS. The rgbData field will contain the provided nonce and the data returned from the TPM. The rgbValidationData field will contain the checksum value returned from the TPM. It’s then the application’s responsibility to verify that:

        vData.rgbValidationData = SHA1(vData.rgbData)

The validation structure has different uses depending on which API it is passed to. For instance, when passing it to the Tspi_TPM_Quote operation, the external data is included in the signature made by the TPM over the selected PCRs. Upon returning, the validation data structure will contain the external data, the PCR data that was signed, and the signature itself. It’s then up to the application to use the public part of the signing key to verify that the signature matches. See the TSS specification for more information on each API’s use of the validation structure.

Summary

The TCG Software Stack is a vendor-neutral API that provides the entry point for an application’s use of TPM hardware. Its high-level interface abstracts away the details of authorization handling, TPM byte stream generation, and many other aspects that would be difficult for an application to manage. In this chapter, we discussed the architecture of the TSS, common errors, and misconceptions, as well as many of the concepts you might encounter when programming to the TSS API, such as the following:

  • TSP object types
  • TSS return codes
  • TSS memory management
  • Persistent key storage
  • Signing and verifying data using the TSS
  • Use of the TSS_VALIDATION structure
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.219.166