Chapter 9

Using Symmetric Keys

The TPM provides an excellent facility to create, store, and manage asymmetric keys, but when it comes time to encrypt large amounts of data, using the TPM may not be as obvious. Due to the speed of operations for asymmetric cryptography, it is not well suited for this task. This is where symmetric cryptography steps in to handle the load. Symmetric algorithms such as Triple-DES, AES, and Blowfish are many times faster for encryption and decryption, making them ideal to provide confidentiality for large amounts of data.

Although the TPM has no native support for symmetric encryption, it can play an important role in providing a secure way to store, use, and transport symmetric keys. Binding or sealing symmetric keys can prevent unauthorized access to keys, as well as providing a convenient way to store them securely while not in use. This chapter will lay out many of the issues surrounding use of TPM asymmetric keys to wrap symmetric keys and hopefully make use of the TPM for this purpose easier. In this chapter, we’re given examples of how to do this, including the following:

  • How to use the random number generator to create a symmetric key
  • How to use a binding key to lock the symmetric key to a pass phrase
  • How to use a sealing key to lock the symmetric key to a platform state

Data Binding

Using the TPM to bind, or to encrypt by using an asymmetric key, is the simplest use of the TPM in asymmetric cryptography. The TSS APIs involved are Tspi_Data_Bind and Tspi_Data_Unbind. When using a TSS binding key, Tspi_Data_Bind will create a TPM_BOUND_DATA structure (called TCPA_BOUND_DATA in the 1.1 specification), copy the data into the structure, and encrypt the structure using the public key. The TPM_BOUND_DATA structure will be expected by the TPM at unbind time when the TPM detects that you’re using a binding key. This is not the case with legacy keys, however. Data bound by a legacy key is simply decrypted by the TPM during an Unbind operation based on the padding type of the key. No bound data structure is checked for by the TPM.

There are a few things to be aware of when using asymmetric TPM keys to bind your symmetric keys using the TSS:

  • The size of your TPM key
  • The size of your symmetric key
  • The size of the TPM_BOUND_DATA structure
  • The type of TPM key
  • The padding type of your TPM key

Current TPMs support RSA key sizes from 512 bits up to and including 2048 bits. Natively, this would allow symmetric keys from 64 bytes to 256 bytes to be encrypted. However, there are several factors to take into account even when not using a TPM. The first is RSA padding type. There are two main RSA padding types in use today: PKCS#1 version 1.5 and Optimal Asymmetric Encryption Padding, or OAEP.[1] Each of these types use different algorithms to pad the data to be encrypted in such a way that the possibility of the RSA key used to encrypt being exposed is minimized. Both of these types are supported by current TPMs, and the type of padding must be specified before creating your TPM key in the TPM if it is different from the default. The TPM uses the SHA-1 digest as its Message Generation Function, or MGF. Table 9.1 shows the default padding types when creating a TPM key.

Table 9.1. Default Encryption Schemes Set by the TSS for Each of the TSS Key Types

image

Each padding type will use a different amount of data to pad the data to be encrypted. Table 9.2 shows the padding amount used for PKCS#1 v1.5 and OAEP.

Table 9.2. Padding Amounts in Bytes for Each of the TPM-Supported RSA Padding Types

image

In Table 9.2, 20 bytes is the size of a SHA-1 hash, which is the MGF used by the TPM in OAEP padding the data to be encrypted.

As you can see, OAEP padding incurs more of a cost in padding; however, OAEP is considered more secure and all newly implemented applications should be using it.

In addition to having to take into account the padding used for asymmetric encryption, you should be aware of how a TPM expects to receive data in an Unbind command. The TPM expects data to be unbound to be inside a TPM_BOUND_DATA structure when the key used is a binding key. Here’s the definition of the TPM_BOUND_DATA structure:

        typedef struct tdTPM_BOUND_DATA {
                TPM_VERSION ver;
                TPM_PAYLOAD_TYPE payload;
                BYTE *payloadData;
        } TPM_BOUND_DATA;

TPM_VERSION is a 4-byte field and TPM_PAYLOAD_TYPE is a 1-byte field, reducing the number of bytes you’re able to encrypt even further. As an example, let’s say that you’re using a 512-bit TPM binding key to encrypt your symmetric key. A 512-bit RSA key can encrypt 64 bytes, minus 5 bytes to take into account the TPM_BOUND_DATA structure, minus 38 bytes to take into account the OAEP padding, leaving a total of 21 bytes available to encrypt your key! This may not be acceptable, depending on the symmetric algorithm you choose to use. For instance, a Triple-DES key is 24 bytes long and an AES-256 key is 32 bytes long, making them impossible to encrypt using a 512-bit TPM binding key (in one operation). However, we do have options. Table 9.3 lays out the maximum amount of data encryptable with several different combinations of keys and encryption schemes.

Table 9.3. Showing the Maximum Size of Data Encryptable by Keys of Varying Sizes and Types

image

So, after all that, which type of key should you choose? In short, any currently strong symmetric key can be bound by a TPM key of at least 1024 bits, regardless of padding type. This includes the largest key size for AES, Triple-DES, Serpent, and Blowfish. Most modern software is now using AES, the Advanced Encryption Algorithm, chosen for use by NIST and the NSA.

Sample Code

Let’s create a TPM key that we’d like to use for binding a symmetric key (see Listing 9.1). We’d like to use a 512-bit key and give ourselves a little extra room for data to encrypt by using PKCS#1v1.5 padding. The key will be a child of the SRK.

Listing 9.1. Creating a TPM Key

TSS_HCONTEXT  hContext;
TSS_HTPM      hTPM = 0;
TSS_FLAGS     initFlags;
TSS_HKEY      hSRK = 0;
TSS_HKEY      hKey;
TSS_UUID      SRK_UUID = TSS_UUID_SRK;

Tspi_Context_Create(&hContext);
Tspi_Context_Connect(hContext, NULL);
Tspi_Context_GetTpmObject(hContext,&hTPM);

Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, SRK_UUID,
                           &hSRK );

initFlags = TSS_KEY_TYPE_BIND | TSS_KEY_SIZE_512 |
            TSS_KEY_NO_AUTHORIZATION | TSS_KEY_NOT_MIGRATABLE;

Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY,
                          initFlags, &hKey);

/* Set the padding type before calling the TPM to create the key. This
* is a requirement, since the key padding type is set at key creation
* time and cannot be changed afterwards. */
Tspi_SetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO,
                     TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
                     TSS_ES_RSAESPKCSV15);

/* Create the key, not bound to any PCRs */
Tspi_Key_CreateKey(hKey, hSRK, 0);

Now let’s use our binding key to bind some data. This function will take a binding key and some data and return the data bound using the key. This function assumes that the key is a binding key already loaded into the TPM and that hContext refers to a global TSP handle.

        int
        MyFunc_BindData(TSS_HKEY hKey, UINT32 in_size, BYTE *in,
                              UINT32 *out_size, BYTE *out)
        {
               TSS_HENCDATA hEncData;
               UINT32 keySize, tmp_out_size;
               BYTE *tmp_out;
        
               /* Create the encrypted data object in the TSP */
               Tspi_Context_CreateObject( hContext,
                                        TSS_OBJECT_TYPE_ENCDATA,
                                        TSS_ENCDATA_BIND, &hEncData );
        
               Tspi_GetAttribUint32( hKey, TSS_TSPATTRIB_KEY_INFO,
                               TSS_TSPATTRIB_KEYINFO_SIZE, &keySize );
        
               /* Make sure the data is small enough to be bound by
                * this key, taking into account the PKCS#1v1.
                * padding size (11) and the size of the TPM_BOUND_DATA
                * structure (5) */
               if (in_size > keySize – 16) {
                      LogError("Data to be encrypted is too big!");
                      return -1;
               }
        
               Tspi_Data_Bind( hEncData, hKey, in_size, in);
        
               /* Now hEncData contains an encrypted blob, lets
                * extract it */
               Tspi_GetAttribData( hEncData,
                                   TSS_TSPATTRIB_ENCDATA_BLOB,
                                   TSS_TSPATTRIB_ENCDATABLOB_BLOB,
                                   &tmp_out_size,&tmp_out);
        
               if (tmp_out_size > *out_size) {
                       LogError("Encrypted data blob is too big!");
                       return -1;
               }
        
               /* Copy the encrypted data blob to the user's buffer */
               memcpy(out, tmp_out, tmp_out_size);
               *out_size = tmp_out_size;
        
               /* Free the blob returned by the TSP */
               Tspi_Context_FreeMemory( hContext, tmp_out);
        
               /* Close the encrypted data object, it will no longer
                * be used */
               Tspi_Context_CloseObject( hContext, hEncData);
        
            return 0;
        }

Data Sealing

Data sealing suffers from the same type of problems that binding does when using a TPM, but is even less flexible. Although you can use a legacy key or binding key to bind your data, only storage keys can be used to seal data. This means that the length requirements for using sealing keys are more restrictive. In addition, the size of a TPM_SEALED_DATA structure, which is used to wrap data before sealing by the TPM, is larger than the TPM_BOUND_DATA structure. Here’s the layout of a TPM_SEALED_DATA structure:

        typedef struct tdTPM_SEALED_DATA
        {
               TPM_PAYLOAD_TYPE        payload;
               TPM_SECRET       authData;
               TPM_NONCE        tpmProof;
               TPM_DIGEST       storedDigest;
               UINT32           dataSize;
               BYTE             *data;
        } TPM_SEALED_DATA;

The total size of the additional elements of the TPM_SEALED_DATA structure is 65 bytes. Compare that to the 5 byte size of the TPM_BOUND_DATA structure, and you may be feeling the pinch when it comes times to seal your symmetric key. Table 9.4 illustrates the amount of data that can be sealed using each RSA key size.

Table 9.4. Showing the Maximum Amount of Data Sealable by Keys of Various Sizes

image

Here, a 2048-bit key will suffice for any current symmetric key size; however, due to the size of the TPM_SEALED_DATA structure, no other key sizes can be used. Despite the size restrictions, though, data sealing is considerably more powerful than binding, allowing you to seal individual data blobs to any number of PCRs regardless of whether the key itself is bound to any PCRs.

Sample Code

Let’s create a TPM key that we’d like to use for sealing a symmetric key. We’d like to use a 2048-bit key, which must be a storage key. The key will be a child of the SRK.

        TSS_HCONTEXT  hContext;
        TSS_HTPM      hTPM = 0;
        TSS_FLAGS     initFlags;
        TSS_HKEY      hSRK = 0;
        TSS_HKEY      hKey;
        TSS_UUID      SRK_UUID = TSS_UUID_SRK;

        Tspi_Context_Create(&hContext);
        Tspi_Context_Connect(hContext, NULL);
        Tspi_Context_GetTpmObject(hContext,&hTPM);

        Tspi_Context_LoadKeyByUUID (hContext, TSS_PS_TYPE_SYSTEM,
                                    SRK_UUID, &hSRK );

        initFlags = TSS_KEY_TYPE_STORAGE | TSS_KEY_SIZE_2048 |
                    TSS_KEY_NO_AUTHORIZATION |
                    TSS_KEY_NOT_MIGRATABLE;

        Tspi_Context_CreateObject (hContext, TSS_OBJECT_TYPE_RSAKEY,
                                   initFlags, &hKey );

        /* Create the key, not bound to any PCRs. That can be done on
         * a blob by blob basis */
        Tspi_Key_CreateKey (hKey, hSRK, 0 );

Now let’s use our sealing key to seal some data. This function will take a sealing key, a PCRs object, and some data and return the data sealed using the key. This function assumes that the key is a sealing key already loaded into the TPM and that hContext refers to a global TSP handle.

        int
        MyFunc_SealData(TSS_HKEY hKey,
                        TSS_HPCRS hPcrs,
                        UINT32 in_size,
                        BYTE *in,
                        UINT32 *out_size,
                        BYTE *out)
        {
            TSS_HENCDATA hEncData;
            UINT32 keySize, tmp_out_size;
            BYTE *tmp_out;

            /* Create the encrypted data object in the TSP */
            Tspi_Context_CreateObject(hContext,
                                      TSS_OBJECT_TYPE_ENCDATA,
                                      TSS_ENCDATA_SEAL, &hEncData );

            Tspi_GetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO,
                                 TSS_TSPATTRIB_KEYINFO_SIZE,
                                 &keySize);

            /* Make sure the data is small enough to be bound by this
             * key,taking into account the OAEP padding size (38) and
             * the size of the TPM_SEALED_DATA structure (65) */
            if (in_size > keySize – 103) {
                LogError("Data to be encrypted is too big!");
                return -1;
            }

            Tspi_Data_Seal(hEncData, hKey, in_size, in, hPcrs);
            /* Now hEncData contains an encrypted blob, let's extract
             * it */
            Tspi_GetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB,
                               TSS_TSPATTRIB_ENCDATABLOB_BLOB,
                               &tmp_out_size, &tmp_out);
            if (tmp_out_size > *out_size) {
                LogError("Encrypted data blob is too big!");
                return -1;
            }

            /* Copy the encrypted data blob to the user's buffer */
            memcpy(out, tmp_out, tmp_out_size);
            *out_size = tmp_out_size;

            /* Free the blob returned by the TSP */
            Tspi_Context_FreeMemory( hContext, tmp_out);

            /* Close the encrypted data object, it will no longer
             * be used */
            Tspi_Context_CloseObject( hContext, hEncData);

            return 0;
        }

Sealing a symmetric key to a set of TPM PCRs is nearly as straightforward as binding. The two main differences are that there’s less flexibility in the types of key you can use, and a PCR composite object must be created to use in sealing. We’ve covered key creation before; now let’s create a handy way to set up a PCR composite object.

MyFunc_CreatePcrs takes an array of UINT32s, which represent the indices of PCR registers to set in the created PCR composite object. The PCR composite object is then created by querying all the PCR indices specified and setting those values in the object. This function will again assume a global hContext already created and a global handle to the TPM object hTPM.

        int
        MyFunc_CreatePcrs(UINT32 num_pcrs,
                          UINT32 *pcrs,
                          TSS_HPCRS *hPcrs)
        {
            UINT32  numPcrs, subCap, i;
            UINT32  ulPcrValueLength;
            BYTE    *rgbPcrValue, *rgbNumPcrs;

            Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_PCRS,
                                      0, hPcrs);
            // Retrieve number of PCRs from the TPM
            subCap = TSS_TPMCAP_PROP_PCR;
            Tspi_TPM_GetCapability(hTPM, TSS_TPMCAP_PROPERTY,
                                   sizeof(UINT32), (BYTE *)&subCap,
                                   &ulPcrValueLength, &rgbNumPcrs);

            numPcrs = *(UINT32 *)rgbNumPcrs;

            Tspi_Context_FreeMemory( hContext, rgbNumPcrs );

            for (i = 0; i < num; i++) {
                if (pcrs[i] >= numPcrs) {
                    LogError("PCR value %u is too big!", pcrs[i]);
                    Tspi_Context_CloseObject(hContext, *hPcrs);
                    return -1;
                }

                Tspi_TPM_PcrRead(hTPM, pcrs[i], &ulPcrValueLength,
                                 &rgbPcrValue);

                Tspi_PcrComposite_SetPcrValue(*hPcrs, pcrs[i],
                                              ulPcrValueLength,
                                              rgbPcrValue);
                Tspi_Context_FreeMemory( hContext, rgbPcrValue );
            }

            return 0;
        }

Encrypting Files

Now let’s pull together all the code that we’ve written so far and do something useful with it. What we’d like to do is have a function that takes a path to a file and a TPM key handle and creates a new symmetric key, encrypts that file using the symmetric key, and seals the symmetric key with the TPM key. We’d like to invoke the function like this:

        MyFunc_SealFile(FILE_PATH, hKey, NULL, 0);

or to seal the symmetric key to PCRs 8, 9, and 10:

        UINT32 pcrs[] = { 8, 9, 10 };
        UINT32 num_pcrs = 3;

        MyFunc_SealFile(FILE_PATH, hKey, pcrs, num_pcrs);

For this example, we’ll just write the encrypted file out to FILE_PATH.enc, and we’ll write the encrypted symmetric key blob to FILE_PATH.key. Also, we’ll use a dummy function to do the symmetric encryption, since that will have to be provided by an external library.

        int
        MyFunc_SealFile(char *file, TSS_HKEY hKey, UINT32 *pcrs,
                        UINT32 num_pcrs)
        {
            FILE *fin, *fout;
            UINT32 inSize, outSize, encSymKeySize = SYM_KEY_SIZE;
            BYTE *symKey, in[BUF_SIZE], out[BUF_SIZE],
                          encSymKey[SYM_KEY_SIZE];
            TSS_HPCRS hPcrs;
            char *outFileName;

            inSize = outSize = BUF_SIZE;

            outFileName = malloc(strlen(file) + 5);

            sprintf(outFileName, "%s.enc", file);

            fin = fopen( file, "r" );
            fout = fopen( outFileName, "w" );

            Tspi_TPM_GetRandom( hTPM, SYM_KEY_SIZE, &symKey );

            /* Note: depending on the symmetric algorithm you choose
             * to use, you should check the returned random number
             * against a database of known weak keys for that
             * algorithm. It's possible that data encrypted with a
             * weak key could allow an attacker to discover the key
             * by analyzing the encrypted text */

            while ((inSize = read(fileno(fin), in, inSize) ==
                    BUF_SIZE)) {
                /* Call our external library to do the bulk encryption
                 * using the symmetric key */
                symmetric_encrypt(symKey, in, inSize, out, outSize);
                /* Write the encrypted file back out */

                write(fileno(fout), out, outSize);
            }

            fclose(fin);
            fclose(fout);

            /* Create the PCR composite object to seal the symmetric
               key with */
            MyFunc_CreatePcrs(num_pcrs, pcrs, &hPcrs);

            /* Now seal the symmetric key using our TPM key */
            MyFunc_SealData(hKey, hPcrs, SYM_KEY_SIZE, symKey,
                            &encSymKeySize, &encSymKey);

            /* Write out the encrypted symmetric key blob */
            sprintf(outFileName, "%s.key", file);
            fout = fopen( outFileName, "w" );
            write(fileno(fout), encSymKey, encSymKeySize);
            fclose(fout);

            return 0;
        }

Protecting a file using symmetric keys sealed to the TPM couldn’t be much easier. Some simple scripts or further wrapping of MyFunc_SealFile could add support for piping data between other programs or even across a network via a socket.

Summary

Despite lacking support for doing symmetric encryption itself, the TPM can be very useful for securing symmetric keys and thereby securing your symmetrically encrypted data. The most important issues to be aware of when selecting TPM keys to seal or bind your symmetric keys are the following:

  • The size of your TPM key
  • The size of your symmetric key
  • The size of the structure used to house your key (TPM_BOUND_DATA or TPM_SEALED_DATA)
  • The type of TPM key
  • The padding type of your TPM key
..................Content has been hidden....................

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