受信任的执行环境 EFI 协议
许可:Microsoft 同意以合理且非歧视条款向你授予对其必需声明的免费、免版税许可证,仅用于制作、使用、销售、标价出售、导入或分发本规范的任何实现。 “必需声明”是 Microsoft 拥有或 Microsoft 控制的专利的声明,这些专利从技术上来说,是实现本规范所需部分所必需的(还包括可选部分的必需元素),其中导致侵害的功能进行了详细介绍,而不仅仅是在本规范中进行引用。
1.0 简介
本文档指定了一种 EFI 协议,用于与受信任的执行环境交互 (TrEE) 进行交互(按受信任的计算组 (TCG) 受信任的平台模块 2.0 库规范的子集实现 TPM 2.0 功能)。 本文档还指定了平台固件度量要求。 此处定义的 EFI 协议在很大程度上利用了 [TCG06a] 和 [TCG06b]。
2.0 数据结构和首字母缩写词
2.1 数据结构
与 [TCG06a] 中一样,所有数据值都应该以 Little-Endian 格式表示。 字符串应该表示为 ASCII 字节数组,其中最左侧字符处于最低内存位置。
2.2 首字母缩写词和约定
(对于未在此处定义的首字母缩写词,请参阅 [TCG06a])
TrEE 受信任的执行环境
本文档中“必须”和“应该”术语的使用按照 [RFC2119] 进行解释。
3.0 EFI TrEE 协议
此部分提供 EFI_TREE_PROTOCOL 和 EFI_TREE_SERVICE_BINDING_PROTOCOL 的详细说明。 EFI TrEE 协议用于与 TrEE 通信。
3.1 TrEE EFI 服务绑定协议
此部分定义 TrEE EFI 服务绑定协议。
摘要 -EFI TrEE 服务绑定协议用于找到 EFI TrEE 协议驱动程序支持的 TrEE 设备,以及创建和销毁可以使用基础 TrEE 设备的 EFI TrEE 协议子驱动程序实例。
GUID - #define EFI_TREE_SERVICE_BINDING_PROTOCOL_GUID \ {0x4cf01d0a, 0xc48c, 0x4271, 0xa2, 0x2a, 0xad, 0x8e, 0x55, 0x97,\ 0x81, 0x88}
说明 需要 TrEE 服务的应用程序(或驱动程序)可以使用一个协议处理程序服务(例如 BS->LocateHandleBuffer())搜索发布 EFI TrEE 服务绑定协议的设备。 每个具有已发布 EFI TrEE 服务绑定协议的设备都支持 EFI TrEE 协议,可供使用。
成功调用 EFI_TREE_SERVICE_BINDING_PROTOCOL.CreateChild() 函数后,子 EFI TrEE 协议驱动程序实例便准备就绪,可供使用。
在 EFI 应用程序或驱动程序终止执行之前,对 EFI_TREE_SERVICE_BINDING_PROTOCOL.CreateChild() 函数的每个成功调用都必须与 EFI_TREE_SERVICE_BINDING_PROTOCOL.DestroyChild() 函数调用匹配。
3.2 TrEE EFI 协议
摘要 - EFI TrEE 协议用于与 TrEE 通信,以便将命令发送到 TrEE、将它用于受信任的执行操作以及提供对 TrEE 中扩展的度量固件日志的访问。 该协议使用与 TCG 1.2 TCG 事件日志相同的格式(请参阅 [TCG06b])维护在 TrEE 中记录的度量事件日志;在本规范中称为 TrEE 事件日志格式 TCG 1.2 事件日志。 实现者可以使用其他格式创建附加事件日志,但此版本的协议未定义检索它们的方法。
GUID - #define EFI_TREE_PROTOCOL_GUID \ {0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2,\ 0x72, 0x0f}
协议接口结构 -
typedef struct _EFI_TREE_PROTOCOL {
EFI_TREE_GET_CAPABILITYGetCapability;
EFI_TREE_GET_EVENT_LOGGetEventLog;
EFI_TREE_HASH_LOG_EXTEND_EVENTHashLogExtendEvent;
EFI_TREE_SUBMIT_COMMANDSubmitCommand;
} EFI_TREE_PROTOCOL;
Parameters
GetCapability |
此服务提供有关 TrEE 和固件功能的信息 |
GetEventLog |
获取指向固件事件日志的指针 |
HashLogExtendEvent |
此服务会导致 EFI TrEE 驱动程序扩展事件并(选择性地)将事件写入 TrEE 日志。 |
SubmitCommand |
此服务将命令直接提交到 TrEE。 |
说明 - EFI_TREE_PROTOCOL 抽象化 TrEE 活动。 此协议实例提供启动服务,并实例化为启动服务驱动程序。
启动服务驱动程序在调用了 ExitBootServices ( ) 时终止,启动服务驱动程序所使用的所有内存资源都会在操作系统环境中释放,以供使用。
此启动服务必须创建 EVT_SIGNAL_EXIT_BOOT_SERVICES 事件。 调用 ExitBootServices ( ) 时,系统会通知此事件。
EVT_SIGNAL_EXIT_BOOT_SERVICES 是一种同步事件,用于确保在调用特定接口函数后进行特定活动;在此例中,这是为响应 ExitBootServices ( ) 函数而需要执行的清理操作。 ExitBootServices ( ) 无法代表加载到系统中的驱动程序进行清理。 驱动程序必须通过创建类型为 EVT_SIGNAL_EXIT_BOOT_SERVICES 并且其通知函数是驱动程序本身中的函数的事件,来自行执行该操作。 随后在 ExitBootServices ( ) 完成清理后,它会向事件类型 EVT_SIGNAL_EXIT_BOOT_SERVICES 发出信号。
有关如何实例化为 EFI 驱动程序的启动服务如何创建这一所需的 EVT_SIGNAL_EXIT_BOOT_SERVICES 事件的实现详细信息,请参阅 [UEFI12] 的第 6.1 节。
3.3 EFI_TREE_PROTOCOL.GetCapability
EFI_TREE_PROTOCOL GetCapability 函数调用提供协议功能信息和有关 TrEE 的状态信息。
原型
typedef
EFI_STATUS
(EFIAPI *EFI_TREE_GET_CAPABILITY) (
IN EFI_TREE_PROTOCOL *This,
IN OUT TREE_BOOT_SERVICE_CAPABILITY*ProtocolCapability,
);
参数
This |
指示调用上下文。 |
ProtocolCapability |
调用方为 TREE_BOOT_SERVICE_CAPABILITY 结构分配内存,并将 size 字段设置为所分配的结构的大小。 被调用方中使用 EFI 协议功能信息和当前 TrEE 状态信息填充字段,直到字段数符合传入结构的大小。 |
相关定义
typedef struct _TREE_VERSION {
UINT8 Major;
UINT8 Minor;
} TREE_VERSION;
typedef UINT64 EFI_PHYSICAL_ADDRESS;
typedef UINT32 TREE_EVENT_LOG_BITMAP;
typedef UINT32 TREE_EVENT_LOG_FORMAT;
#define TREE_EVENT_LOG_FORMAT_TCG_1_2 0x00000001
typedef struct _TREE_BOOT_SERVICE_CAPABILITY {
UINT8 Size;
TREE_VERSION StructureVersion;
TREE_VERSION ProtocolVersion;
UINT32 HashAlgorithmBitmap;
TREE_EVENT_LOG_BITMAPSupportedEventLogs;
BOOLEAN TrEEPresentFlag;
UINT16MaxCommandSize;
UINT16MaxResponseSize;
UINT32ManufacturerID;
} TREE_BOOT_SERVICE_CAPABILITY;
#define TREE_BOOT_HASH_ALG_SHA1 0x00000001
#define TREE_BOOT_HASH_ALG_SHA256 0x00000002
#define TREE_BOOT_HASH_ALG_SHA384 0x00000004
#define TREE_BOOT_HASH_ALG_SHA512 0x00000008
大小 |
传入结构的分配大小 |
StructureVersion |
TREE_BOOT_SERVICE_CAPABILITY 结构本身的版本。 对于此版本的协议,主要版本应该设置为 1,次要版本应该设置为 0。 |
ProtocolVersion |
TrEE 协议的版本。 对于此版本的协议,主要版本应该设置为 1,次要版本应该设置为 0。 |
HashAlgorithmBitMap |
支持的哈希算法 |
SupportedEventLogs |
支持的事件日志格式的位图(请参阅上文) |
TrEEPresentFlag |
False = TrEE 不存在 |
MaxCommandSize |
可发送到 TrEE 的命令的最大大小(以字节为单位) |
MaxResponseSize |
可由 TrEE 提供的响应的最大大小(以字节为单位) |
ManufacturerID |
4 字节供应商 ID(请参阅 [TCG07] 的“TPM 功能供应商 ID”部分) |
说明
EFI_TREE_PROTOCOL 获取功能函数调用会提供 EFI 协议版本和功能信息以及有关 TrEE 的状态信息。 调用方必须设置所分配的 TREE_BOOT_SERVICE_CAPABILITY 结构的 Size 字段。 预计此函数调用的未来版本可能会向结构添加其他字段。 传入的 Size 值会使函数只填充调用方为其分配内存的字段。 例如:
ProtocolCapability.Size = sizeof(TREE_BOOT_SERVICE_CAPABILITY);
对于此版本的规范:
如果 This 或 ProtocolCapability 参数为 NULL,则函数调用会返回 EFI_INVALID_PARAMETER。
如果输入的 ProtocolCapability.Size < sizeof(TREE_BOOT_SERVICE_CAPABILITY),则函数会将 ProtocolCapability.Size 设置为等于此规范中定义的 sizeof(TREE_BOOT_SERVICE_CAPABILITY),并返回错误代码 EFI_BUFFER_TOO_SMALL,剩余字段的值未定义。
必须设置以下返回值:
ProtocolCapability.StructureVersion.Major = 1
ProtocolCapability.StructureVersion.Minor = 0
ProtocolCapability.ProtocolVersion.Major = 1
ProtocolCapability.ProtocolVersion.Minor = 0
如果平台没有 TrEE,则必须返回以下值:
ProtocolCapability.SupportedEventLogs = 0
ProtocolCapability.HashAlgorithmBitmap = 0
ProtocolCapability.TrEEPresentFlag = FALSE
ProtocolCapability.MaxCommandSize = 0
ProtocolCapability.MaxResponseSize = 0
ProtocolCapability.ManufacturerID = 0
对于 Windows,最小 MaxCommandSize 和 MaxResponseSize 必须为 0x500(或更大)。
返回的状态代码
EFI_SUCCESS |
操作已成功完成。 |
EFI_DEVICE_ERROR |
命令未成功。 ProtocolCapability 变量不会进行填充。 |
EFI_INVALID_PARAMETER |
一个或多个参数不正确。 ProtocolCapability 变量不会进行填充。 |
EFI_BUFFER_TOO_SMALL |
ProtocolCapability 变量太小,无法容纳完整响应。 它会进行部分填充(会设置必需的 Size 字段)。 |
3.4 EFI_TREE_PROTOCOL.GetEventLog
通过 EFI_TREE_PROTOCOL 获取事件日志函数调用,调用方可以检索给定事件日志的地址及其最后一个条目。
原型
typedef
EFI_STATUS
(EFIAPI *EFI_TREE_GET_EVENT_LOG) (
IN EFI_TREE_PROTOCOL *This,
IN TREE_EVENT_LOG_FORMATEventLogFormat,
OUT EFI_PHYSICAL_ADDRESS*EventLogLocation,
OUT EFI_PHYSICAL_ADDRESS*EventLogLastEntry,
OUT BOOLEAN*EventLogTruncated
);
Parameters
EventLogFormat |
请求获取其信息的事件日志的类型。 |
EventLogLocation |
指向事件日志的内存地址的指针。 |
EventLogLastEntry |
如果事件日志包含多个条目,则这是指向事件日志中最后一个条目在内存中的起始地址的指针。 有关在空事件日志或仅包含一个条目的事件日志的特殊情况中,此参数中返回的值的信息,请参阅下面的“说明”部分。 |
EventLogTruncated |
如果事件日志由于事件已超出为事件分配的区域而缺少至少一个条目,则此值设置为 TRUE。 否则,该值会是 FALSE,事件日志会完整。 |
说明
固件会管理启动期间 TrEE 中记录的度量事件日志。 启动期间,在 UEFI 平台初始化之前,会在事件日志中为 TrEE 中扩展的每个度量创建一个条目。 在 UEFI 环境中,每次调用 HashLogExtendEvent 以在 TrEE 中扩展度量时,事件通常记录在包含扩展度量的事件日志中。 如果固件为事件日志分配的区域太小,无法容纳添加的所有事件,则函数调用会指示事件日志已截断,缺少条目。 此版本的规范仅要求维护 SHA1 度量的事件日志。 此规范的未来版本可能会维护支持不同哈希算法的其他事件日志。
此函数返回的事件日志区域会在调用 ExitBootServices ( ) 时释放。 调用 ExitBootServices ( ) 后,此方法的调用方不得访问访问该区域。对于此版本的规范:
如果 EventLogFormat 不等于 TREE_EVENT_LOG_FORMAT_TCG_1_2,则函数调用必须返回 EFI_INVALID_PARAMETER。
如果不存在 TrEE,则函数必须设置以下值并返回 EFI_SUCCESS:
EventLogLocation = NULL
EventLogLastEntry = NULL
EventLogTruncated = FALSE
EventLogLocation 值必须设置为指定事件日志格式在内存中的起始地址
如果指定事件日志:
不包含任何事件,则 EventLogLastEntry 必须设置为 0
恰好包含一个条目,则 EventLogLastEntry 必须设置为与 EventLogLocation 相同的值
包含多个事件,则 EventLogLastEntry 必须设置为指定事件日志的最后一个事件的起始地址
如果之前的 EFI_TREE_PROTOCOL.HashLogExtendEvent 调用返回 EFI_VOLUME_FULL,则 EventLogTruncated 必须设置为 TRUE,否则必须设置为 FALSE。
返回的状态代码
EFI_SUCCESS |
操作已成功完成。 |
EFI_INVALID_PARAMETER |
一个或多个参数不正确(例如请求获得不支持其格式的事件日志)。 |
3.5 EFI_TREE_PROTOCOL.HashLogExtendEvent
EFI_TREE_PROTOCOL HashLogExtendEvent 函数调用使调用方可以扩展事件并(选择性地)记录事件,而无需了解实际 TPM 命令。即使此函数无法创建事件日志条目(例如由于事件日志已满),也会进行扩展操作。
原型
typedef
EFI_STATUS
(EFIAPI * EFI_TREE_HASH_LOG_EXTEND_EVENT) (
IN EFI_TREE_PROTOCOL*This,
IN UINT64Flags,
IN EFI_PHYSICAL_ADDRESSDataToHash,
IN UINT64DataToHashLen,
IN TrEE_EVENT*Event,
);
Parameters
This |
指示调用上下文。 |
标记 |
提供其他信息的位图(请参阅下文)。 |
DataToHash |
要进行哈希操作的数据缓冲区起始位置 |
的物理地址。 |
|
DataToHashLen |
DataToHash 引用的缓冲区的长度(以字节为单位)。 |
事件 |
指向包含有关事件的信息的数据缓冲区的指针。 |
相关定义
#pragma pack(1)
typedef struct _TrEE_EVENT {
UINT32Size;
TrEE_EVENT_HEADERHeader;
UINT8Event[ANYSIZE_ARRAY];
} TrEE_EVENT;
typedef struct _TrEE_EVENT_HEADER {
UINT32HeaderSize;
UINT16HeaderVersion;
TrEE_PCRINDEXPCRIndex;
TrEE_EVENTTYPEEventType;
} TrEE_EVENT_HEADER;
#pragma pack()
typedef UINT32 TrEE_PCRINDEX;
typedef UINT32 TrEE_EVENTTYPE;
大小 |
事件的总大小,包括 Size 组件、标头和事件数据。 |
HeaderSize |
事件标头本身的大小大小 (sizeof(TrEE_EVENT_HEADER))。 |
HeaderVersion |
标头版本。 对于此版本的此规范,该值应该为 1。 |
PCRIndex |
应该扩展的 PCR 的索引 (0 - 23)。 |
EventType |
应该扩展(并选择性地记录)的事件的类型。 |
标志值
Flags 变量是提供其他数据的位图,如下所示:
#define TREE_EXTEND_ONLY 0x0000000000000001
当应该扩展但不记录事件时,应该设置此位。
#define PE_COFF_IMAGE 0x0000000000000010
当意向是度量 PE/COFF 映像时,应该设置此位。
说明
EFI_TREE_PROTOCOL 哈希日志扩展事件函数调用会计算数据缓冲区(可能包含 PE/COFF 二进制文件映像)的度量并会使 TrEE 驱动程序扩展度量。 此外,该服务可为服务支持的每种事件日志格式选择性地创建事件日志条目并将其附加到事件日志。 该服务使调用方可以在不了解有关特定 TrEE 命令的任何信息的情况下使用 TrEE。
必须先使用此函数度量 PE/COFF 映像,然后才能对映像应用重定位。 注意:使用此方法度量 PE/COFF 映像时请小心。 通常,加载 PE/COFF 映像的实现会在加载过程中从映像中去除重要数据,可能会更改内存中的映像部分对齐方式。 最终结果是计算内存中映像的哈希与从存储媒体加载映像时正确计算的映像的实际度量不匹配。
调用时,函数应该执行以下操作:
如果参数 This、DataToHash 或 Event 中的任何一个为 NULL,则函数必须返回 EFI_INVALID_PARAMETER。
如果 Event.Size 小于 Event.Header.HeaderSize + sizeof(UINT32),则函数必须返回 EFI_INVALID_PARAMETER。
如果 Event.Header.PCRIndex 不是 0 到 23(含),则函数必须返回 EFI_INVALID_PARAMETER。
如果 Flags 位图设置了 PE_COFF_IMAGE 位,但 PE/COFF 映像已损坏或不理解,则函数必须返回 EFI_UNSUPPORTED。
函数允许将任何值用于 Event.Header.EventType 参数。
函数必须计算从 DataToHash 开始的数据的摘要(度量),长度为 DataToHashLen。 设置 PE_COFF_IMAGE 位后,函数必须根据下面附录 A 中的“度量 PE/COFF 映像”来计算 PE/COFF 映像的度量。
函数必须成功地将 TPM2_PCR_Extend 命令发送到 TrEE,以使用度量摘要扩展 Event.Header.PCRIndex 指示的 PCR。 如果无法成功发送该命令,则函数必须返回 EFI_DEVICE_ERROR。 如果固件支持比 SHA1 更多的算法,则可以使用其他算法计算摘要,并且也对其进行扩展。
如果以前对此函数的调用返回 EFI_VOLUME_FULL 并且在 Flags 参数中设置了 TREE_EXTEND_ONLY 位,则函数必须返回 EFI_VOLUME_FULL。 (不尝试将事件日志条目添加到事件日志中。)
函数必须按如下所示生成 TCG 事件日志条目:(注意:TCG_PCR_EVENT 结构在 [TCG06b] 中进行定义,应该被视为按字节对齐。)
TCG_PCR_EVENT.PCRIndex = Event.Header.PCRIndex
TCG_PCR_EVENT.EventType = Event.Header.EventType
TCG_PCR_EVENT.Digest = 上面计算的 SHA1 度量摘要<>
TCG_PCR_EVENT.EventSize = Event.Size - sizeof(UINT32) - Event.Header.HeaderSize
TCG_PCR_EVENT.Event = Event.Event(注意:这是 EventSize 字节的内存副本)
函数可能会为其他支持的事件日志格式生成类似的事件日志条目。
如果上面创建的 TCG_PCR_EVENT 事件日志条目无法容纳于为 TrEE 事件日志格式 TCG 1.2 事件日志分配的区域,则函数必须返回 EFI_VOLUME_FULL。
如果固件支持其他事件日志格式,并且为这些事件日志创建的任何事件超过为事件日志分配的区域,则函数必须返回 EFI_VOLUME_FULL。
函数必须将创建的事件附加到对应事件日志,并且服务必须将其内部指针更新为每个事件日志最后一个事件的起始地址。
返回的状态代码。
EFI_SUCCESS |
操作已成功完成。 |
EFI_DEVICE_ERROR |
命令未成功。 |
EFI_VOLUME_FULL |
发生了扩展操作,但无法将事件写入一个或多个事件日志中。 |
EFI_INVALID_PARAMETER |
一个或多个参数不正确。 |
EFI_UNSUPPORTED |
不支持 PE/COFF 映像类型。 |
3.6 EFI_TREE_PROTOCOL.SubmitCommand
通过此服务可以将命令发送到 TrEE。
原型
typedef
EFI_STATUS
(EFIAPI *EFI_TREE_SUBMIT_COMMAND) (
IN EFI_TREE_PROTOCOL*This,
IN UINT32InputParameterBlockSize,
IN UINT8*InputParameterBlock,
IN UINT32OutputParameterBlockSize,
IN UINT8*OutputParameterBlock
);
Parameters
This |
指示调用上下文。 |
InputParameterBlockSize |
TrEE 输入参数块的大小。 |
InputParameterBlock |
指向 TrEE 输入参数块的指针。 |
OutputParameterBlockSize |
TrEE 输出参数块的大小。 |
OutputParameterBlock |
指向 TrEE 输出参数块的指针。 |
说明
EFI_TREE_PROTOCOL 提交命令函数调用会提供从调用方到系统 TrEE 的传递功能。
调用方负责生成要发送到 TrEE 的命令字节流,还负责解释 TrEE 返回的结果字节流。 每个 TrEE 命令的 TrEE 输入和输出操作数在其他位置进行定义。
请注意,返回的状态代码反映函数调用的结果,而不是基础 TrEE 命令的成功(或失败)。
完成对 ExitBootServices() 的调用之前,TPM 2.0 不得返回 TPM2_RC_RETRY。 (存在此要求的原因是,返回代码在 TPM 命令可以完成之前会阻止启动过程。)
在 ExitBootServices 调用完成之前,TPM 2.0 必须可以访问其持久存储。 如果在调用 ExitBootServices 后,TPM 2.0 实现可能无法访问持久存储,请联系 Microsoft 以了解其他要求。
返回的状态代码
EFI_SUCCESS |
已成功将命令字节流发送到设备,并且已成功收到响应。 |
EFI_DEVICE_ERROR |
命令未成功发送到设备,或者未成功从设备收到响应。 |
EFI_INVALID_PARAMETER |
一个或多个参数不正确。 |
EFI_BUFFER_TOO_SMALL |
输出参数块太小。 |
参考
[MSFT08] |
Microsoft Corporation,“Windows 验证码可移植可执行签名格式”,版本 1.0,2008 年 3 月 21 日。 |
[RFC2119] |
Bradner, S.,“RFC 中用于指示要求级别的关键字”,IETF RFC 2119,1997 年 3 月。 |
[TCG06a] |
受信任的计算组,“TCG EFI 协议”,版本 1.20 修订版 1.00,2006 年 6 月 9 日。 |
[TCG06b] |
受信任的计算组,“TCG EFI 平台规范”,版本 1.20 修订版 1.0,2006 年 6 月 7 日。 |
[TCG07] |
受信任的计算组,“TCG 供应商 ID 注册表”,版本 1.0,修订版 0.1,2007 年 8 月 31 日。 |
[UEFI12] |
UEFI,“统一可扩展固件接口规范”,版本 2.3.1 勘误表 C, |
2012 年 6 月。 |
附录 A:静态信任根度量
重要
对于 InstantGo 系统,PCR[7] 度量的附录 A 实现是必需的。
在高级别上,固件负责在启动过程中度量以下组件:
包含或度量 UEFI 启动服务和 UEFI 运行时服务的平台固件
与平台固件关联的安全性相关变量
单独加载的 UEFI 驱动程序或启动应用程序
与单独加载的 UEFI 驱动程序或 UEFI 启动应用程序关联的变量
以上度量由 TCG EFI 平台规范 [TCG06b] 第 5.1 - 5.5 节进行定义,此处未进一步提及。 根据平台配置,可选择度量到 PCR[1] 和 PCR[3]。
对于 Windows,PCR[7] 用于反映 UEFI 2.3.1 安全启动策略。 此策略依赖于验证在 UEFI 环境之前启动的所有启动组件的固件以及以不变方式将安全启动策略信息记录到 PCR[7] 中的 UEFI 平台初始化代码(或更早版本的固件代码)。
因此,遵循策略的平台固件必须将以下值度量到 PCR[7]:
PK 变量的内容
KEK 变量的内容
EFI_IMAGE_SECURITY_DATABASE 变量的内容
EFI_IMAGE_SECURITY_DATABASE1 变量的内容
EFI_IMAGE_SECURITY_DATABASE 中用于在启动路径中验证 EFI 驱动程序或 EFI 启动应用程序的条目
SecureBoot 变量的内容
由于上述原因,UEFI 变量 PK、KEK、EFI_IMAGE_SECURITY_DATABASE、EFI_IMAGE_SECURITY_DATABASE1 和 SecureBoot 不应度量到 PCR[3]。
此外,如果平台提供了可能在 UEFI 环境之前启动的固件调试程序,则必须在 PCR[7] 中记录此情况。 同样,如果平台为 UEFI 环境提供调试程序,则必须在 PCR[7] 中记录调试程序的启动。
实现说明
UEFI LoadImage 函数必须对 [TCG06b] 中介绍的每个事件在 PCR[2] 或 PCR[4] 中记录度量,以及对下面“将 UEFI 配置度量到 PCR[7]”部分中介绍的每个事件在 PCR[7] 中记录度量。 若要确定映像度量是否适用于 PCR[2] 或 PCR[4],LoadImage 必须在 PE/COFF 映像中检查子系统字段。 值 IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER、IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 和 IMAGE_SUBSYSTEM_EFI_ROM 与 PCR[2] 对应。 值 IMAGE_SUBSYSTEM_EFI_APPLICATION 与 PCR[4] 对应。 如果加载的映像是某种其他类型,则必须将它记录在 PCR[4] 中。 LoadImage 由于 (a) 签名验证失败或 (b) 映像不符合当前强制实施的 UEFI 2.3.1 安全启动策略而未能加载的映像无需在 PCR 中进行度量。
****相关定义
#define EV_EFI_VARIABLE_DRIVER_CONFIG \
0x80000001 /* Defined in [TCG06b] */
#define EV_EFI_ACTION 0x80000007 /* Defined in [TCG06b] */
#define EV_EFI_VARIABLE_AUTHORITY 0x800000E0
This specification requires a modified TCG structure definition for EFI_VARIABLE_DATA. The revised structure is:
typedef struct {
EFI_GUIDVariableName;
UINT64 UnicodeNameLength; // The TCG Defintion used UINTN
UINT64 VariableDataLength; // The TCG Defintion used UINTN
CHAR16 UnicodeName[1];
INT8 VariableData[1];
} EFI_VARIABLE_DATA;
度量 PE/COFF 映像
度量 PE/COFF 映像时,EventType 应该按照 [TCG06b] 中进行定义(例如,度量 EFI 启动应用程序时,EventType 应该是 EV_EFI_BOOT_SERVICES_APPLICATION),并且 Event 值应该是 [TCG06b] 中定义的 EFI_IMAGE_LOAD_EVENT 结构的值。
HashLogExtendEvent 服务必须按照 [MSFT08] 的“计算 PE 映像哈希”部分中指定的过程对 PE/COFF 映像进行哈希操作。
将 UEFI 配置度量到 PCR[7]
对于所有 EFI 变量值事件,EventType 应该是前面定义的 EV_EFI_VARIABLE_DRIVER_CONFIG,Event 值应该是在此规范前面部分中定义的 EFI_VARIABLE_DATA 结构的值(此结构应该被视为按字节对齐)。 度量摘要应该是作为 EFI_VARIABLE_DATA 结构的事件数据的 SHA-1 哈希。 (注意:这与 [TCG06b]指定的摘要不同,) EFI_VARIABLE_DATA。UnicodeNameLength 值是 CHAR16 字符数 (而不是) 字节数。 EFI_VARIABLE_DATA.UnicodeName 内容不得包含 null 终止符。 如果读取 EFI 变量时返回 EFI_NOT_FOUND,则 EFI_VARIABLE_DATA.VariableDataLength 字段必须设置为零,并且 EFI_VARIABLE_DATA.VariableData 字段的大小会为零。
如果平台提供了可能会在 UEFI 环境之前使用的固件调试程序模式,或是如果平台为 UEFI 环境提供了调试程序,则平台应该先将 [TCG06b] 中指定的 EV_EFI_ACTION 事件扩展到 PCR[7],然后才允许使用调试程序。 事件字符串应是“UEFI Debug Mode”。 此外,平台必须创建 TCG 事件日志条目,如下所示:
TCG_PCR_EVENT.PCRIndex = 7
TCG_PCR_EVENT.EventType = EV_EFI_ACTION
TCG_PCR_EVENT.Digest = 字符串值“UEFI Debug Mode”的 SHA-1 摘要,没有终止 NULL 字符<>
TCG_PCR_EVENT.EventSize = strlen("UEFI Debug Mode")
TCG_PCR_EVENT.Event = "UEFI Debug Mode"
平台可能会为其他支持的事件日志格式生成类似的事件日志条目。
在执行平台制造商提供的未以加密方式进行验证的任何代码之前,平台制造商固件必须使用 EV_EFI_VARIABLE_DRIVER_CONFIG 事件类型按列出的顺序将以下值度量到 PCR[7]:
SecureBoot 变量值
PK 变量值
KEK 变量值
EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE 变量值
EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE1 变量值
如果平台支持在 PCR [7] 中最初进行度量之后,并且在完成 ExitBootServices ( ) 之前更改以下任何 UEFI 测量变量,而无需无条件地重新启动平台,则必须在更改时立即再次度量变量。 此外,设置以下任何 UEFI 变量的正常更新过程必须在 PCR[7] 中进行初始度量之前或是在完成 ExitBootServices() 调用之后进行。
SecureBoot 变量值
PK 变量值
KEK 变量值
EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE 变量值
EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE1 变量值
系统应该在 PCR[7] 中度量 EV_SEPARATOR 事件。 (这会在将分隔符度量到 PCR[0] 到 PCR[7] 的同时进行。)
在启动 EFI 驱动程序或 EFI 启动应用程序之前(并且无论启动是由于 EFI 启动管理器从 DriverOrder 或 BootOrder UEFI 变量选取了映像,还是由于已启动的映像调用了 UEFI LoadImage() 函数),UEFI 固件应该将用于验证 EFI 映像的 EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE 变量中的条目度量到 PCR[7]。 度量应该与映像加载一起进行。 对于此事件,EventType 应该为 EV_EFI_VARIABLE_AUTHORITY,Event 值应该为 EFI_VARIABLE_DATA 结构的值(该结构在本规范前面部分中定义,其定义与 TCG 规范不同)。 EFI_VARIABLE_DATA.VariableData 值应该是包含用于验证映像的授权的 EFI_SIGNATURE_LIST 中的 EFI_SIGNATURE_DATA 值,EFI_VARIABLE_DATA.VariableName 应该设置为 EFI_IMAGE_SECURITY_DATABASE_GUID。 EFI_VARIABLE_DATA.UnicodeName 应该设置为 EFI_IMAGE_SECURITY_DATABASE 的值。 此值不应该包含终止 NULL 字符。
在启动任何其他 EFI 驱动程序或 EFI 启动应用程序之前,UEFI 固件应该检查验证 EFI 映像的 EFI_IMAGE_SECURITY_DATABASE_GUID/EFI_IMAGE_SECURITY_DATABASE 变量中的条目以前是否已使用 EV_EFI_VARIABLE_AUTHORITY 事件类型在 PCR[7] 中进行了度量。 如果尚未进行度量,则必须按照上一步骤中所述进行度量。 如果以前进行过度量,则不得再次度量。
注意
可请求从 Microsoft 获得 PCR[7] 度量的度量示例。