The Trusted Platform Module (TPM) is the product of a specification from the Trusted Computing Group (TCG 2006a), designed to enhance system security by moving many sensitive cryptographic operations into hardware. Windows Vista supports TPM 1.2. The most wellknown feature that uses the TPM, if one is available, is BitLocker Drive Encryption and its secure start-up capability (Microsoft 2006e).
Tip | BitLocker can be used without a TPM, but it only offers drive encryption, not secure start-up. |
There are numerous security technologies–in the form of software and internal and external hardware–that provide some form of “trust” for the applications. There is, however, general consensus that software-based roots of trust are significantly less resilient than hardware-based solutions. For widespread industry adoption that balances reasonable cost and a quality user experience an integrated hardware solution that is a part of the motherboard and that has antitampering characteristics is ideal. A standardized implementation is, clearly, very important.
There are significant software product and service opportunities that require such hardware-based security, but without being able to depend on either general presence or an adoptable standard, these features will either not be created at all or insecure software alternatives will be used with limited acceptance.
The Trusted Computing Group is a non-profit organization that was formed to develop, define, and help promote an open standard for a hardware-based root of trust that allowed for innovation, and it has met most if not all of the needs detailed earlier. Their most well-known result is the specification describing the TPM 1.2 chip.
At the highest level, the TPM is a microcontroller that stores keys, cryptographic hashes, and digital certificates. It can provide a unique public and private key pair, verify data integrity, and release keys dependent on an unchanged environmental state as well as offering other cryptographic services. Strong keys and secure decision making are fundamental to all security features. The TPM is the only standard hardware of its kind that is attachable to the motherboard.
As it stands in Windows Vista, the TPM is not exposed in a general-purpose fashion that can be used to take full advantage of the capabilities the TPM provides. Using the TPM’s ability to create and store cryptographic keys or perform cryptographic operations requires additional development effort.
Tip | You can only successfully call TPM functionality from elevated processes. |
What is exposed today in Windows Vista are WMI interfaces (Microsoft 2006f) to build TPM and BitLocker management tools. The following C# code shows how to query a TPM using WMI:
using System.Management; static private ManagementObject m_wmiObject = null; static Boolean BooleanMethod(String method) { ManagementBaseObject outParams = m_wmiObject.InvokeMethod(method, null, null); return (0 == (UInt32)outParams["ReturnValue"]) ? false : true; } static void Main(string[] args) { try { string tpm = "root\\cimv2\\security\\microsofttpm:Win32_Tpm=@"; m_wmiObject = new ManagementObject(tpm); String version = m_wmiObject.GetPropertyValue("SpecVersion").ToString(); Boolean fEnabled = BooleanMethod("IsEnabled"); UInt32 id = (UInt32)m_wmiObject.GetPropertyValue("ManufacturerId"); } catch (ManagementException me) { Console.WriteLine(me.Message); } }
Here is something similar, but using VBScript:
Set objWMIServices = GetObject("winmgmts:\\.\root\cimv2\Security\MicrosoftTpm") Set objWMIInstance = objWMIServices.Get("Win32_Tpm=@") If Err.Number<>0 Then WScript.Echo "Cannot find a TPM in this PC." Wscript.Quit End If WScript.Echo "TPM Found" WScript.Echo "TPM Enabled: " + CStr(objWMIInstance.IsEnabled) WScript.Echo "TPM Owned : " + CStr(objWMIInstance.IsOwned)
These WMI providers sit atop the Windows Vista TPM Base Services (TBS), which runs as a system service and exposes a small number of APIs (Microsoft 2006g) to software developers wanting to build TPM-aware applications.
You can program directly to the TPM using the TBS APIs, or you can use a vendor-provided TCG Software Stack (TSS) (TCG 2006b) to make development easier. The TSS is an abstraction layer that makes writing to the TPM much simpler. The TBS in Windows Vista behaves like an “old-school” driver, and code written this way can be complicated and very time-consuming, as you will see in the next code sample. A TSS package helps by reducing the complexity of TPM commands and provides a much simpler API for a TPM application developer to use.
The following C++ example shows how to write to the TBS directly. This sample code gets the TPM version information and 32 random bytes.
// returned data starts at byte 10 #define TPM_DATA_OFFSET 10 #ifdef _DEBUG // Display TPM data hex dump void TpmDisplayRawResult( __in_bcount(cbResult) BYTE *pbResult, UINT32 cbResult) { if (!cbResult || !pbResult) return; if (cbResult >= TPM_DATA_OFFSET) { wprintf(L"Tag : %02X %02X\n", pbResult[0], pbResult[1]); wprintf(L"Len : %02X %02X %02X %02X\n", pbResult[2], pbResult[3], pbResult[4], pbResult[5]); wprintf(L"Err : %02X %02X %02X %02X\n", pbResult[6], pbResult[7], pbResult[8], pbResult[9]); if (cbResult > TPM_DATA_OFFSET) { wprintf(L"Data: "); for (UINT32 i=TPM_DATA_OFFSET; i < cbResult-TPM_DATA_OFFSET; i++) wprintf(L"%02X ",pbResult[i]); wprintf(L"\n"); } } } #endif #define MAX_RNG_BUFF 64 #define TPM_RNG_OFFSET 14 HRESULT TpmGetRandomData( TBS_HCONTEXT hContext, __inout_bcount(cData) BYTE *pData, UINT32 cData) { if (!hContext || !pData || !cData || cData > MAX_RNG_BUFF) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); BYTE bCmd[] = {0x00, 0xc1, // TPM_TAG_RQU_COMMAND 0x00, 0x00, 0x00, 0x0e, // blob length in bytes 0x00, 0x00, 0x00, 0x46, // TPM API code (TPM_ORD_GetRandom) 0x00, 0x00, 0x00, (BYTE)cData};// # bytes UINT32 cbCmd = sizeof bCmd; BYTE bResult[128] = {0}; UINT32 cbResult = sizeof bResult; HRESULT hr = Tbsip_Submit_Command(hContext, TBS_COMMAND_LOCALITY_ZERO, TBS_COMMAND_PRIORITY_NORMAL, bCmd, cbCmd, bResult, &cbResult); #ifdef _DEBUG if (FAILED(hr)) wprintf(L"Tbsip_Submit_Command failed %X",hr); else TpmDisplayRawResult(bResult,cbResult); #endif if (SUCCEEDED(hr)) memcpy(pData,TPM_RNG_OFFSET+bResult,cData); return hr; } HRESULT TpmGetVersion( TBS_HCONTEXT hContext, __in_ecount(cVersion) wchar_t *wszVersion, UINT32 cVersion) { if (!hContext || !wszVersion || !cVersion) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); BYTE bCmd[] = {0x00, 0xc1, // TPM_TAG_RQU_COMMAND 0x00, 0x00, 0x00, 0x12, // blob length in bytes 0x00, 0x00, 0x00, 0x65, // TPM API code (TPM_ORD_GetCapability) 0x00, 0x00, 0x00, 0x06, // TCPA_CAP_VERSION 0x00, 0x00, 0x00, 0x00};// no sub capability UINT32 cbCmd = sizeof bCmd; BYTE bResult[128] = {0}; UINT32 cbResult = sizeof bResult; HRESULT hr = Tbsip_Submit_Command(hContext TBS_COMMAND_LOCALITY_ZERO, TBS_COMMAND_PRIORITY_NORMAL, bCmd, cbCmd, bResult, &cbResult); #ifdef _DEBUG if (FAILED(hr)) wprintf(L"Tbsip_Submit_Command failed %X",hr); else TpmDisplayRawResult(bResult,cbResult); #endif if (SUCCEEDED(hr)) swprintf_s(wszVersion,cVersion,L"TCG v%d.%d, Firmware v%d.%d", (UINT32)bResult[14], (UINT32)bResult[15], (UINT32)bResult[16], (UINT32)bResult[17]); return hr; } int main(int argc, char* argv[]) { argv; argc; // Create a TBS context TBS_HCONTEXT hContext = 0; TBS_CONTEXT_PARAMS contextParams = {0}; contextParams.version = TBS_CONTEXT_VERSION_ONE; HRESULT hr = Tbsi_Context_Create(&contextParams, &hContext); if (FAILED(hr)) return hr; wchar_t wszVersion[32]; if (SUCCEEDED(TpmGetVersion(hContext,wszVersion,_countof(wszVersion)))) wprintf(L"%s\n",wszVersion); BYTE buff[64]; UINT32 cBuff = sizeof buff; if (SUCCEEDED(TpmGetRandomData(hContext,buff,cBuff))) for (UINT32 i=0; i < cBuff; i++) wprintf(L"%02X ",buff[i]); if (hContext) Tbsip_Context_Close(hContext); return 0; }
When calling into the TBS, you must set up an input buffer that contains function arguments, pass that to the Tbsip_Submit_Command function, and then query the response buffer for status and results.
Note | TBS functions that start with Tbsip result in a direct TPM call, and functions that start with Tbsi do not directly call the TPM. |
The format of the input buffer is shown in Table 9-1.
Data Size (bytes) | TPM Type | Comment |
---|---|---|
2 | TPM_TAG | Packet type (e.g., a request, TPM_TAG_RQU_COMMAND, 0xC1) |
4 | UINT32 | Total packet length in bytes |
4 | TPM_CMD_CODE | TPM command code (e.g., TPM_ORD_GetCapability, 0x65 or TPM_ORD_GetRandom, 0x46) |
Variable | Often BYTE[] or UINT32 | Arguments for the command |
The format of the output buffer is shown in Table 9-2.
Data Size (bytes) | TPM Type | Comment |
---|---|---|
2 | TPM_TAG | Packet type (e.g., a response, TPM_TAG_RSP_COMMAND, 0xC4) |
4 | UINT32 | Total packet length in bytes |
4 | TPM_RESULT | Error value. |
Variable | Often BYTE[] or UINT32 | Returned data from command. |
The sample code shown earlier issues a TPM command to get TPM chip version information. To learn more about the different TPM commands and the in and out buffers, you should refer to the TCG documentation (TCG 2006c).
To summarize this TPM material, most software developers will not need to write any TPM-aware software because it’s very specialized; for those who need to create TPM-aware applications, though, you can use high-level WMI interfaces, a TCG Software Stack (TSS) ported to work with the TBS, or the low-level TPM Base Service function calls in Windows Vista.