Translate

Image of Modern Operating Systems (3rd Edition)
Image of Android Wireless Application Development
Image of Advanced Programming in the UNIX Environment, Second Edition (Addison-Wesley Professional Computing Series)
Image of RHCE Red Hat Certified Engineer Linux Study Guide (Exam RH302) (Certification Press)

Accessing TPM Functionality From UEFI Shell - Part 1

A Trusted Platform Module (TPM) is, traditionally, a hardware device (chip) designed to enable commodity computing platforms (think laptop or personal computer) to achieve greater levels of security than non-TPM equipped platform. There are over 600 million installed TPMs, mostly in high-end laptops made by Lenovo, HP, Dell, Toshiba and others.

TPMs are manufactured by many chip producers including Atmel, STMicroelectronics and Toshiba. Via it’s Trusted Execution Technology (TXT), Intel now incorporates TPM functionality in many of its current processors.

TPM technology is specified by the Trusted Computing Group (TCG), an industry consortium that includes Intel, Microsoft, AMD, IBM, HP, Lenovo, and over 100 other members. TCG is the successor organization of the Trusted Computing Platform Alliance (TCPA). It was founded in 2003.

So what do we mean by the term Trusted Computing (TC)? According to the TCG:

A Trusted (Computing) Platform is a computing platform that can be trusted to report its properties.

The TCG defines trust as follows:

Trust is the expectation that a device will behave in a particular manner for a specific purpose.

The goal of the TCG is to develop specifications which enable people or organizations to trust computing devices (Trusted Platforms) that they interact with. In this context, the notion of TC usually is used to imply a set of technologies specified by the TCG. Required properties of a Trusted Platform (TP) include:

  • Shielded Locations: Places on a platform where operations on sensitive data can be carried out securely.
  • Protected Capabilities: The set of commands that has exclusive permissions to operate on shielded locations.
  • Integrity Measurement: Uses protected capabilities to obtain and store metrics of platform characteristics that affect the trustworthiness of a platform.
  • Integrity Reporting: Enables metrics obtained through integrity measurement to be transferred to a third party. Usually the third party will use the data for attestation of the reporting device.

Together with the platform firmware, a TPM forms a Root of Trust (ROT). A ROT has to be trusted, because misbehavior of that component is impossible to detect. Typically a TP has the following three ROTs:

  • Root of Trust for Measurement (RTM): Responsible for measuring the platform’s integrity state and storing it into shielded locations.
  • Root of Trust for Storage (RTS): Responsible for providing storage to hold summary values of integrity measurements and the sequence of their execution.
  • Root of Trust for Reporting (RTR): Responsible for reliably reporting information that resides in the RTS.

A TPM provides protected capabilities and shielded locations togehter with a RTS and RTR. Since an RTM is usually platform dependent, the firmware has to provide the starting point for measurement. This staring point is called the Core Rout of Trust fur Measurement (CRTM).

Here is a block diagram of the main components of a TPM:

I did not develop the above diagram. Prof. Dr.-Ing. Ahmad-Reza Sadeghi of Techische Universität Darmstadt did.

Here is a more detailed block diagram of the components of a TPM, specifically a STMicroelectronics ST19NP18 device:

The CPU interfaces with the on-chip memories RAM, ROM and EEPROM via the internal bus through two separate Firewalls. The first Firewall protects the on chip memories and controls access from any memory area to another memory area. The second Firewall protects against unauthorized jumps to sensitive chip resources. A specific security block in the microcontroller provides an extremely high level of protection against software and hardware attacks. The device also includes two Random Number Generators, three 8-bit fully programmable Timers, a CRC module, a Modular Arithmetic Processor (MAP) and a SHA-1 Secure Hash Accelerator. CMOS technology is used as it allows more than 500,000 erase and program operations on each byte. Data retention is a minimum of 10 years.

The device supports communication with the host using the Low-Pin Count (LPC) interface recommended by the TCG for PC Client TPM Specific implementations. The LPC interface complies with the Intel Low Pin Count Interface specification (Revision 1.1, August 2002). How does software communicate with such a device? For this purpose, TCG has defined a standardized TPM interface to access a TPM. The TCG PC Client Specific TPM Interface Specification V1.2 (TIS) describes this interface. TIS specifies that the TPM is mapped in a memory space in the address range FED4-0000h to FED4-4FFFh. Note, however, when accessing a TPM from the UEFI shell, you do not use this interface.

Most of the functionality of a TPM and how to access that functionality is also specified. The first version of the TPM main specification was released by the TCPA in 2001. The TCG took over publishing the specification, renaming it to TPM Main Specification Level 2 Version 1.2, in late 2003. The International Organization for Standardization (ISO) and International Electrotechnical Commission (IEC) standardized the specification as ISO/IEC 11889 in 2009.

This specification is still very relevant today and is currently at revision 116. It is specified in three documents totaling about 800 pages.

  • Part 1 – Design Principles
  • Part 2 – Structures of the TPM
  • Part 3 – Commands
  • Part 4 – Conformance

The above two TPM block diagrams refer to a TPM 1.2 device.

TPM 1.2 included some features that limited its use to PCs and other PC-like platforms. To overcome those limitations, TCG developed the TPM 2.0 specifications which are intended to be usable by a very broad range of platforms from embedded systems to mobile devices to PCs to servers. Major changes and enhancements include:

  • Support for additional cryptographic algorithms
  • Enhancements to the availability of the TPM to applications
  • Enhanced authorization mechanisms
  • Simplified TPM management
  • Additional capabilities to enhance the security of platform services

The specification was named Trusted Platform Module Library Specification 2.0 (TPM 2.0). It consists of the following documents:

  • TPM Library Part 1 – Architecture
  • TPM Library Part 2 – Structures
  • TPM Library Part 3 – Commands
  • TPM Library Part 4 – Supporting Routines

JTC 1, the joint committee of the International Organization for Standardization, or ISO, and IEC, the International Electrotechnical Commission, approved the TPM 2.0 as International Standard ISO/IEC 11889:2015, Parts 1-4, in 2015.

The TPM 1.2 and TPM 2.0 documents make no reference to UEFI protocols. Instead, there are 3 other specifications detailing the UEFI protocols, i.e. interfaces, to TPM functionality:

  • TCG EFI Protocol Version 1.20. For TPM Family 1.1 and 1.2
  • Microsoft Trusted Execution Environment EFI Protocol
  • TCG EFI Protocol Specification Family 2.0

The first specification was released in 2006. The TrEE specification was released circa the Windows 8.1 development cycle. It specifies an EFI protocol for interacting with a Trusted Execution Environment (TrEE), implementing TPM 2.0 functionality per a subset of the TCG Trusted Platform Module 2.0 Library specification. The TCG EFI Protocol Specification Family 2.0 is not yet a formal specification; it just finished it’s public review period. Some differences between the TrEE specification and this draft specification still need resolving; one major issue is whether all structures need to be packed or not. Both specifications use the same protocol GUID – so this is an important issue.

Here is the relevant protocol definitions in UDK2015 for the 3 EFI TPM specifications as they relate to sending commands to, and receiving the results of these commands from, TPM devices:

// ../Include/Protocol/TcgService.h

#define EFI_TCG_PROTOCOL_GUID  {0xf541796d, 0xa62e, 0x4954, { 0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd } }

struct _EFI_TCG_PROTOCOL {
  EFI_TCG_STATUS_CHECK              StatusCheck;
  EFI_TCG_HASH_ALL                  HashAll;
  EFI_TCG_LOG_EVENT                 LogEvent;
  EFI_TCG_PASS_THROUGH_TO_TPM       PassThroughToTpm;
  EFI_TCG_HASH_LOG_EXTEND_EVENT     HashLogExtendEvent;
};

/**
  This service is a proxy for commands to the TPM.

  @param  This                        Indicates the calling context.
  @param  TpmInputParameterBlockSize  Size of the TPM input parameter block.
  @param  TpmInputParameterBlock      The pointer to the TPM input parameter block.
  @param  TpmOutputParameterBlockSize Size of the TPM output parameter block.
  @param  TpmOutputParameterBlock     The pointer to the TPM output parameter block.

  @retval EFI_SUCCESS            The operation completed successfully.
  @retval EFI_INVALID_PARAMETER  Invalid ordinal.
  @retval EFI_UNSUPPORTED        Current Task Priority Level  >= EFI_TPL_CALLBACK.
  @retval EFI_TIMEOUT            The TIS timed-out.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_TCG_PASS_THROUGH_TO_TPM)(
  IN      EFI_TCG_PROTOCOL          *This,
  IN      UINT32                    TpmInputParameterBlockSize,
  IN      UINT8                     *TpmInputParameterBlock,
  IN      UINT32                    TpmOutputParameterBlockSize,
  IN      UINT8                     *TpmOutputParameterBlock
  );

// ../Include/Protocol/Tcg2Protocol.h

#define EFI_TCG2_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }}

struct tdEFI_TCG2_PROTOCOL {
  EFI_TCG2_GET_CAPABILITY                     GetCapability;
  EFI_TCG2_GET_EVENT_LOG                      GetEventLog;
  EFI_TCG2_HASH_LOG_EXTEND_EVENT              HashLogExtendEvent;
  EFI_TCG2_SUBMIT_COMMAND                     SubmitCommand;
  EFI_TCG2_GET_ACTIVE_PCR_BANKS               GetActivePcrBanks;
  EFI_TCG2_SET_ACTIVE_PCR_BANKS               SetActivePcrBanks;
  EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks;
};

**
  This service enables the sending of commands to the TPM.

  @param[in]  This                     Indicates the calling context
  @param[in]  InputParameterBlockSize  Size of the TPM input parameter block.
  @param[in]  InputParameterBlock      Pointer to the TPM input parameter block.
  @param[in]  OutputParameterBlockSize Size of the TPM output parameter block.
  @param[in]  OutputParameterBlock     Pointer to the TPM output parameter block.

  @retval EFI_SUCCESS            The command byte stream was successfully sent to the device and a response was successfully received.
  @retval EFI_DEVICE_ERROR       The command was not successfully sent to the device or a response was not successfully received from the device.
  @retval EFI_INVALID_PARAMETER  One or more of the parameters are incorrect.
  @retval EFI_BUFFER_TOO_SMALL   The output parameter block is too small.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_TCG2_SUBMIT_COMMAND) (
  IN EFI_TCG2_PROTOCOL *This,
  IN UINT32            InputParameterBlockSize,
  IN UINT8             *InputParameterBlock,
  IN UINT32            OutputParameterBlockSize,
  IN UINT8             *OutputParameterBlock
  );


// ../Include/Protocol/TrEEProtocol.h
#define EFI_TREE_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f}

struct _EFI_TREE_PROTOCOL {
  EFI_TREE_GET_CAPABILITY        GetCapability;
  EFI_TREE_GET_EVENT_LOG         GetEventLog;
  EFI_TREE_HASH_LOG_EXTEND_EVENT HashLogExtendEvent;
  EFI_TREE_SUBMIT_COMMAND        SubmitCommand;
};

/**
  This service enables the sending of commands to the TrEE.

  @param[in]  This                     Indicates the calling context
  @param[in]  InputParameterBlockSize  Size of the TrEE input parameter block.
  @param[in]  InputParameterBlock      Pointer to the TrEE input parameter block.
  @param[in]  OutputParameterBlockSize Size of the TrEE output parameter block.
  @param[in]  OutputParameterBlock     Pointer to the TrEE output parameter block.

  @retval EFI_SUCCESS            The command byte stream was successfully sent to the device and a response was successfully received.
  @retval EFI_DEVICE_ERROR       The command was not successfully sent to the device or a response was not successfully received from the device.
  @retval EFI_INVALID_PARAMETER  One or more of the parameters are incorrect.
  @retval EFI_BUFFER_TOO_SMALL   The output parameter block is too small.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_TREE_SUBMIT_COMMAND) (
  IN EFI_TREE_PROTOCOL *This,
  IN UINT32            InputParameterBlockSize,
  IN UINT8             *InputParameterBlock,
  IN UINT32            OutputParameterBlockSize,
  IN UINT8             *OutputParameterBlock
  );

OK, now it is time for some actual code! We start with a very simple example.

A TPM contains a number of permanent flags (Booleans) which can be retrieved by the TPM_GetCapability command. The following UEFI application retrieves all such flags from a TPM 1.2 comformant device.

//
//  Copyright (c) 2016  Finnbarr P. Murphy.   All rights reserved.
//
//  Retrieve TPM 1.2 permanent flags
//
//  License: BSD License
//

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/EfiShell.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/TcgService.h>
#include <IndustryStandard/UefiTcgPlatform.h>

#define UTILITY_VERSION L"0.8"


VOID
Usage(CHAR16 *Str)
{
    Print(L"Usage: %s [--version]\n", Str);
}


INTN
EFIAPI
ShellAppMain(UINTN Argc, CHAR16 **Argv)
{
    EFI_STATUS Status = EFI_SUCCESS;
    EFI_TCG_PROTOCOL *TcgProtocol;
    EFI_GUID gEfiTcgProtocolGuid = EFI_TCG_PROTOCOL_GUID;

    TPM_RSP_COMMAND_HDR *TpmRsp;
    UINT32              TpmSendSize;
    TPM_PERMANENT_FLAGS *TpmPermanentFlags;
    UINT8               CmdBuf[64];


    if (Argc == 2) {
        if (!StrCmp(Argv[1], L"--version")) {
            Print(L"Version: %s\n", UTILITY_VERSION);
            return Status;
        }
        if (!StrCmp(Argv[1], L"--help") ||
            !StrCmp(Argv[1], L"-h") ||
            !StrCmp(Argv[1], L"-?")) {
            Usage(Argv[0]);
            return Status;
        }
    }

    Status = gBS->LocateProtocol( &gEfiTcgProtocolGuid, 
                                  NULL, 
                                  (VOID **) &TcgProtocol);
    if (EFI_ERROR (Status)) {
        Print(L"Failed to locate EFI_TCG_PROTOCOL [%d]\n", Status);
        return Status;
    }  

    TpmSendSize           = sizeof (TPM_RQU_COMMAND_HDR) + sizeof (UINT32) * 3;
    *(UINT16*)&CmdBuf[0]  = SwapBytes16 (TPM_TAG_RQU_COMMAND);
    *(UINT32*)&CmdBuf[2]  = SwapBytes32 (TpmSendSize);
    *(UINT32*)&CmdBuf[6]  = SwapBytes32 (TPM_ORD_GetCapability);
    *(UINT32*)&CmdBuf[10] = SwapBytes32 (TPM_CAP_FLAG);
    *(UINT32*)&CmdBuf[14] = SwapBytes32 (sizeof (TPM_CAP_FLAG_PERMANENT));
    *(UINT32*)&CmdBuf[18] = SwapBytes32 (TPM_CAP_FLAG_PERMANENT);

    Status = TcgProtocol->PassThroughToTpm( TcgProtocol,
                                            TpmSendSize,
                                            CmdBuf,
                                            sizeof (CmdBuf),
                                            CmdBuf);
    if (EFI_ERROR (Status)) {
        Print(L"ERROR: PassThroughToTpm failed [%d]\n", Status);
        return Status;
    }
    TpmRsp = (TPM_RSP_COMMAND_HDR *) &CmdBuf[0];
    if ((TpmRsp->tag != SwapBytes16(TPM_TAG_RSP_COMMAND)) || (TpmRsp->returnCode != 0)) {
        Print(L"ERROR: TPM command result [%d]\n", SwapBytes16(TpmRsp->returnCode));
        return EFI_DEVICE_ERROR;
    }

    TpmPermanentFlags = (TPM_PERMANENT_FLAGS *) &CmdBuf[sizeof (TPM_RSP_COMMAND_HDR) + sizeof (UINT32)];

    Print(L"\nTPM Permanent Flags\n\n");
    Print(L"Disabled: %s\n", TpmPermanentFlags->disable ? L"Yes" : L"No");
    Print(L"Ownership: %s\n", TpmPermanentFlags->ownership ? L"Yes" : L"No");
    Print(L"Deactivated: %s\n", TpmPermanentFlags->deactivated ? L"Yes" : L"No");
    Print(L"ReadPubEK: %s\n", TpmPermanentFlags->readPubek ? L"Yes" : L"No");
    Print(L"DisableOwnerClear: %s\n", TpmPermanentFlags->disableOwnerClear ? L"Yes" : L"No");
    Print(L"AllowMaintenance: %s\n", TpmPermanentFlags->allowMaintenance ? L"Yes" : L"No");
    Print(L"PhysicalPresenceLifetimeLock: %s\n", TpmPermanentFlags->physicalPresenceLifetimeLock ? L"Yes" : L"No");
    Print(L"PhysicalPresenceHWEnable: %s\n", TpmPermanentFlags->physicalPresenceHWEnable ? L"Yes" : L"No");
    Print(L"PhysicalPresenceCMDEnable: %s\n", TpmPermanentFlags->physicalPresenceCMDEnable ? L"Yes" : L"No");
    Print(L"CEKPUsed: %s\n", TpmPermanentFlags->CEKPUsed ? L"Yes" : L"No");
    Print(L"TPMpost: %s\n", TpmPermanentFlags->TPMpost ? L"Yes" : L"No");
    Print(L"TPMpostLock: %s\n", TpmPermanentFlags->TPMpostLock ? L"Yes" : L"No");
    Print(L"FIPS: %s\n", TpmPermanentFlags->FIPS ? L"Yes" : L"No");
    Print(L"Operator: %s\n", TpmPermanentFlags->operator ? L"Yes" : L"No");
    Print(L"EnableRevokeEK: %s\n", TpmPermanentFlags->enableRevokeEK ? L"Yes" : L"No");
    Print(L"NvLocked: %s\n", TpmPermanentFlags->nvLocked ? L"Yes" : L"No");
    Print(L"ReadSRKPub: %s\n", TpmPermanentFlags->readSRKPub ? L"Yes" : L"No");
    Print(L"TpmEstablished: %s\n", TpmPermanentFlags->tpmEstablished ? L"Yes" : L"No");
    Print(L"MaintenanceDone: %s\n", TpmPermanentFlags->maintenanceDone ? L"Yes" : L"No");
    Print(L"DisableFullDALogicInfo: %s\n", TpmPermanentFlags->disableFullDALogicInfo ? L"Yes" : L"No");
    Print(L"\n");

    return Status;
}

Here in the UDK2015 build (INF) file for the above application. The next application uses a similar INF file. Just replace tpm_getpermflags with tpm_getrandom.

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = tpm_getpermflags 
  FILE_GUID                      = 4ea87c51-7395-4ccd-0355-747010f3ce51
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib
  VALID_ARCHITECTURES            = X64

[Sources]
  tpm_getpermflags.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  ShellCEntryLib
  ShellLib
  BaseLib
  BaseMemoryLib
  UefiLib

[Protocols]

[BuildOptions]

[Pcd]

So how do you figure out what parameters the TPM expects and what output is returned by the TPM? For that, you have to read the TPM 1.2 Part 3 – Commands specification document. Basically, you pass a structure size and structure to the TPM and get back a structure size and structure from the TPM. All structures must be packed and in big endian format. UEFI is a little endian platform so some version between little endian and big endian and visa-versa is required.

Here the relevant text from the TPM 1.2 specification:

All constants and data SHALL be represented as little-endian bit format, which requires the low-order bit on the far left of a constant or data item and the high-order bit on the far right. Exceptions to this, if any, will be explicit in this specification.
All strings SHALL be represented as an array of ASCII bytes with the left-most character placed in the lowest memory location.
In some memory layout descriptions, certain fields are marked reserved. Software must initialize such fields to zero, and ignore them when read. On an update operation, software must preserve any reserved field.

A TPM contains one or more Random number Generators (RNG). These are often known as True RNGs (TRNG). TRNGs are necessary for advanced authentication, signature and encryption techniques. For example, the hardware TRNG implemented in the STMicroelectronics ST19N family is AIS-31 compliant.

Here is the relevant text from the TPM 1.2 specification for the TPM_GetRandom command:

The following UEFI application uses this command to retrieve a series of 20 random bytes. Such a sequence of numbers could be used, for example, to populate a TPM_NONCE.

//
//  Copyright (c) 2016  Finnbarr P. Murphy.   All rights reserved.
//
//  Demo accessing TPM 1.2 Random Number Generator 
//
//  License: BSD License
//

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/EfiShell.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/TcgService.h>
#include <IndustryStandard/UefiTcgPlatform.h>

#define UTILITY_VERSION L"0.8"
#define NO_RANDOM_BYTES 20 

#pragma pack(1)

typedef struct {
    TPM_RQU_COMMAND_HDR Header;
    UINT32              BytesRequested;
} TPM_COMMAND;


typedef struct {
    TPM_RSP_COMMAND_HDR Header;
    UINT32              RandomBytesSize;
    UINT8               RandomBytes[NO_RANDOM_BYTES];
} TPM_RESPONSE;

#pragma pack()


VOID
Usage(CHAR16 *Str)
{
    Print(L"Usage: %s [--version]\n", Str);
}


INTN
EFIAPI
ShellAppMain(UINTN Argc, CHAR16 **Argv)
{
    EFI_STATUS Status = EFI_SUCCESS;
    EFI_TCG_PROTOCOL *TcgProtocol;
    EFI_GUID gEfiTcgProtocolGuid = EFI_TCG_PROTOCOL_GUID;

    TPM_COMMAND  InBuffer;
    TPM_RESPONSE OutBuffer;
    UINT32       InBufferSize;
    UINT32       OutBufferSize;
    int          RandomBytesSize = 0;

    if (Argc == 2) {
        if (!StrCmp(Argv[1], L"--version")) {
            Print(L"Version: %s\n", UTILITY_VERSION);
            return Status; 
        }
        if (!StrCmp(Argv[1], L"--help") ||
            !StrCmp(Argv[1], L"-h") ||
            !StrCmp(Argv[1], L"-?")) {
            Usage(Argv[0]);
            return Status;
        }
    }

    Status = gBS->LocateProtocol( &gEfiTcgProtocolGuid, 
                                  NULL, 
                                  (VOID **) &TcgProtocol);
    if (EFI_ERROR (Status)) {
        Print(L"Failed to locate EFI_TCG_PROTOCOL [%d]\n", Status);
        return Status;
    }  

    InBufferSize = sizeof(TPM_COMMAND);
    OutBufferSize = sizeof(TPM_RESPONSE);

    InBuffer.Header.tag       = SwapBytes16(TPM_TAG_RQU_COMMAND);
    InBuffer.Header.paramSize = SwapBytes32(InBufferSize);
    InBuffer.Header.ordinal   = SwapBytes32(TPM_ORD_GetRandom);
    InBuffer.BytesRequested   = SwapBytes32(NO_RANDOM_BYTES);

    Status = TcgProtocol->PassThroughToTpm( TcgProtocol,
                                            InBufferSize,
                                            (UINT8 *)&InBuffer,
                                            OutBufferSize,
                                            (UINT8 *)&OutBuffer);
    if (EFI_ERROR (Status)) {
        Print(L"ERROR: PassThroughToTpm failed [%d]\n", Status);
        return Status;
    }

    if ((OutBuffer.Header.tag != SwapBytes16 (TPM_TAG_RSP_COMMAND)) || (OutBuffer.Header.returnCode != 0)) {
        Print(L"ERROR: TPM command result [%d]\n", SwapBytes32(OutBuffer.Header.returnCode));
        return EFI_DEVICE_ERROR;
    }

    RandomBytesSize = SwapBytes32(OutBuffer.RandomBytesSize);

    Print(L"Number of Random Bytes Requested: %d\n", SwapBytes32(InBuffer.BytesRequested));
    Print(L" Number of Random Bytes Received: %d\n", RandomBytesSize);
    Print(L"           Ramdom Bytes Received: ");
    for (int i = 0; i < RandomBytesSize; i++) {
        Print(L"%02x ", OutBuffer.RandomBytes[i]);
    }
    Print(L"\n");

    return Status;
}


Note the difference in structure usage between this application and the previous application. Here, I fully specify two distinct packed structures – a command input structure and a command output structure. This is the methodology which I will normally use from here on when using a TPM command.

All code in this post has been build using UDK2015 on an Intel X64 platform and tested on a Lenovo 440 using the discrete TPM 1.2 device. I assume you are familiar with UEFI, EDK2 and UDK2015 if you are reading this post.

Here is sample output:

fs0> tpm_getpermflags.efi

TPM Permanent Flags

Disabled: No
Ownership: Yes
Deactivated: No
ReadPubEK: No
DisableOwnerClear: Yes
AllowMaintenance: No
PhysicalPresenceLifetimeLock: Yes
PhysicalPresenceHWEnable: No
PhysicalPresenceCMDEnable: Yes
CEKPUsed: No
TPMpost: No
TPMpostLock: No
FIPS: No
Operator: No
EnableRevokeEK: No
NvLocked: Yes
ReadSRKPub: Yes
TpmEstablished: No
MaintenanceDone: No
DisableFullDALogicInfo: No


fs0> tpm_getrandom.efi

Number of Random Bytes Requested: 20
 Number of Random Bytes Received: 20
           Ramdom Bytes Received: 4F 1E 19 80 2D 94 60 5B 2B 39 DF 82 8C 4B DE 4E 9C AC E2 0C 

Why an I writing this series of posts? Because there are few published examples of working UEFI code that interacts with a TPM. Such example code is useful to security researchers and computer forensics practitioners.