PCR值验证
在PCR值足够静态或者验证者有证明者的PCR标准值的情况下,可以使用PCR值验证的方式验证平台的可信度。在PCR复合验证中,这种方法要求验证者拥有一个固定的断言PCR复合值列表,这样证明者就只发送报价(带有PCR值的哈希值),而不必发送完整的PCR值或事件日志。或者,如果PCR值足够静态,则可以读取PCR值并将其发送给验证器,并由报价进行验证。如果验证者不知道固定的PCR复合值,并且PCR不是静态的,则应使用2.2.4中所述的事件日志验证,因为无论如何都需要包含事件日志。
PCR值验证的流程如下:
-
证明人
-
证明人的软件执行报价操作(来自验证者的质询),生成Quote
-
证明人将报价和AK证书发送给验证者。
-
验证者
- 验证者使用AK证书对报价进行签名验证。这将验证消息上的签名是否正确,以及它是否来自证明人的TPM。
- 验证器从消息中提取挑战,并将其与新鲜度的预期值进行比较。
- 从RIM数据库中的参考完整性测量(RIM)中获得预期的PCR值。
- 验证器中的评估器组件对所选的预期PCR值进行哈希运算,并将该值与报价消息中的值进行比较。
请注意,RIM可能包含一组预期PCR值。一些RIMs可能包含多个预期PCR值,每个预期PCR值都与一个PCR指数配对,其中所有预期PCR值必须与其各自的PCR相匹配。RIM还可能包含OR运算符,其中RIM包含一组可接受的PCR值(例如,平台可能有多个授权版本的固件)。如果这组允许的值太大,验证器将难以尝试所有可能的组合,最好使用事件日志方法。
事件日志(IML)验证
下图显示了通过“PCR回放”技术对照报价检查的事件日志,其中根据事件日志计算预期的PCR值,并将这些计算值与提供的报价进行比较。然后对照RIM DB检查事件日志中的摘要。RIM数据库包含从可信RIM捆绑包处理的参考测量值
Appraiser:评估组件决定了认证者的整体信任状态。评估者Appraiser必须做到:
- 检索认证器的相应RIM。请注意,这可以异步完成(例如由断言者推送),也可以由证明者作为每个证明的一部分同步发送。
- 从认证者处获取并验证一份新的报价,该报价签署了当前的PCR值并提出质疑。
- 获取事件日志并将其解析为离散事件,并验证每个条目的格式是否正确。
- 通过将报价中所选PCR的哈希值与通过PCR回放技术从单个事件摘要中计算的哈希值进行比较,验证整个日志的完整性。
- 根据RIM数据或预期的事件摘要和本地策略验证每个日志条目。
注意:并非所有日志条目都适用于本地策略,因此有些条目可能会被跳过。
然而,在信任任何特定的日志条目之前,验证整个偶数日志(如上文第4节所述)是否至关重要。
在正常的事件日志验证中,证明人只向验证器发送报价和事件日志,因为日志足以计算所声称的PCR值,报价可以验证这些声明。通常不需要发送PCR值,这简化了证明者,因为报价和读取PCR之间没有竞争条件。如果事件日志未经报价验证,则单独发送PCR值可能有助于调试事件日志本身,只要PCR值经过报价验证即可。(由于报价只含有一个通过选定的PCR值产生的哈希,它无法分辨日志中哪些PCR被错误地表示,只能通过单独的已验证PCR值列表来帮助解决定位问题)。
下一节给出了一个详细的例子。此示例将涵盖正常的事件日志验证,其中仅发送报价和事件日志。
事件日志验证的RIM类似于为PCR值验证提供的RIM,除了它包含一组预期完整性测量值,而不是提供一组预期的PCR值。策略(可能由评估师提供,也可能在RIM内部)可能允许或禁止变更,如事件顺序、允许或禁止额外事件等。
可以在RIM中提供元数据以完善验证器的策略。例如,如果RIM提供了事件类型,验证器还可以将其与事件记录的事件类型进行比较。虽然事件类型不是事件的表示,但如果由RIM提供,则可用于检测存储或传输中事件日志的篡改。
验证实例
前面的部分提供了完整性日志的收集和验证的一般抽象描述。本节详细介绍了在运行Fedora Linux的基于UEFI的示例PC上的收集和验证,其中打开了securebot,使用了Microsoft签名的垫片,使用了GRUB和Fedora签名的内核。它提供了如何解释和使用相应TCG标准以安全实施验证的指导。此示例将仅涵盖UEFI收集的PCClient事件。为了简单起见,它不会涵盖后续GRUB和Linux生成的事件。它将展示如何在引导后从Linux系统收集事件日志和证明数据的示例。
1. Linux系统上的PCClient启动时完整性收集:
在查看日志条目的验证之前,我们必须首先准确了解收集了什么以及如何收集。
UEFI系统提供HashLogExtendEvent服务,用于在引导过程中简化基于TPM的事件日志记录。它如图10所示,并在TCG EFI协议规范[7]中进行了详细描述。此日志服务不仅由UEFI(BIOS)本身使用,而且在操作系统能够创建自己的日志之前,还由各种引导加载程序阶段(如Shim和GRUB)使用。TCG PC客户端平台固件配置文件规范[3]详细说明了所需的BIOS事件日志记录,而引导加载程序和操作系统日志条目不是TCG标准化的,因此本例中不会涉及。
HashLogExtendEvent服务有三个主要参数:
此服务对提供的缓冲区进行哈希运算,并使用结果扩展指示的PCR。然后将摘要和事件字段添加到事件日志中。结果记录在TCG_PCR_EVENT2结构中,如下图所示。
请注意,记录的TCG_PCR_EVENT2结构中的绿色字段可由TPM完全验证,而红色字段则不可验证,因为它们不包含在发送到的摘要中。
HashLogExtendEvent被设计为便于调用者使用,并支持多个用例,其中远程证明只是其中之一。它想对调用者隐藏加密、TPM和事件日志处理的详细信息。它还希望不会导致更长的启动时间,因此重点放在重用UEFI已经计算出的数据和哈希值上,作为安全启动的一部分。但是,由于只有摘要对TPM签名的PCR有贡献,因此eventType和Event数据可能完全未经验证。事件类型和事件数据字段中包含的确切内容的详细信息在[3]中指定。在某些情况下,数据与DataToHash缓冲区相同,因此内容是完全可验证的。在某些情况下,Event数据字段与哈希缓冲区不同,但它包含的数据并非严格用于验证。在所有情况下,验证器必须小心使用eventType和Event数据字段,因为即使事件日志中的所有摘要都与Quoted PCR值匹配,这也不一定会验证声明的eventType或Event数据字段。
UEFI收集的日志存储在ACPI系统中,操作系统可以在引导后读取它。Linux内核使原始(二进制)日志可以作为伪文件在以下位置读取:/sys/kernel/security/tpm0/binary_bios_measurements
2. Linux系统PCClient启动时完整性日志分析:
给定事件日志为二进制blob,我们如何验证它?之前列出了事件日志验证的七个步骤。让我们详细看看示例系统的这些步骤。
从PC客户端认证器获取RIM:
验证者在执行验证之前,需要参考测量。有几种方法可用于检索证明者的RIM捆绑包:
- 事件日志包含一个tdTCG_Sp800_155_PlatformId_Event,其中包含一个ReferenceManifestGuid,验证器可以使用该GUID将事件日志与特定RIM Bundle的tagId属性相匹配。
- 对于PC客户端系统,OEM可以在创建设备时将RIM捆绑包放置在EFS分区中。PC客户端RIM捆绑包包括一个遵循事件日志格式的支持RIM文件和一个签名的基础RIM,该基础RIM通过其有效载荷中的哈希提供事件日志的完整性保护。基本RIM包括一个pcUriGlobal属性,其中提供了一个位置,可以从该位置获得补充和补丁RIM捆绑包。
- 如果设备包含TCG平台属性证书,则验证者可以使用证书中的制造商名称、型号和版本将证明者与特定的RIM捆绑包相匹配。平台属性证书包含一个PlatformConfigUri属性,该属性提供了一个位置,用于查找特定类型的证明者的RIM捆绑包。
RIM捆绑包可以由验证器存储,也可以处理并放置在RIM数据库(DB)中。RIM数据库应保存从证明人处检索RIM数据所需的任何元数据,包括事件摘要值和事件日志验证所需的所有元数据。对于这个例子,我们假设所有必要的数据都已经在评估师的RIM数据库中。在以下部分中,我们将重点介绍示例系统何时以及需要哪些RIM数据。如图所示,只需要少量的RIM数据。示例系统所需的最小值是引导加载程序的可信EFI哈希(shim.EFI)。所有其他事件都可以根据TPM的PCR值进行验证。
从证明者处获取并验证新的TPM2_报价:
对于Linux,首先通过使用TPM2工具包中的TPM2_fote和TPM2_checkquote命令行工具进行实验,最容易理解获取和验证TPM2_Quote。
一个简单的例子是:
证明者attester:
#tpm2_createprimary -C e -c primary.ctx
#tpm2_create -C primary.ctx -u key.pub -r key.priv
#tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
#tpm2_quote -Q -c key.ctx -l 0x0004:10 -m msg.bin -s sig.bin -q deadbeef
这里的key.pub是公钥,验证者将使用它来检查签名。“msg.bin”是包含所选PCRS(在这种情况下,只是PCR10的sha-1库)上的挑战和哈希的消息。“sig.bin”是TPM在msg.bin上的签名。“deadbife”是来自验证器的十六进制新鲜度挑战。
验证者verifier:
tpm2_checkquote -u key.pub -m msg.bin -s sig.bin -q deadbeef
此时,验证者知道msg.bin和sig.bin来自正确的TPM,并且没有经过篡改。验证者接下来应该检查msg.bin是否包含预期的挑战。此外,验证者应提取所选PCR的哈希值。
在这个例子中,PCR-10中的sha-1值为
EA3A8AB9A29C7AF24A492CAFB8FD52A85DC6B370
预期的sha256哈希值应该是
e0a0879966d6498ed6937ca5860f0abe872d14fd8a010a57d8eefee054703a33
以下是msg.bin的十六进制转储,黄色显示挑战,绿色显示哈希
现在验证者知道此报价是新的,并且知道报价PCRs的哈希值。
下图展示这个实例的简要虚拟数据流
首先,证明者创建一个证明密钥(“key”),并将公共部分(key.pub)发送给验证器。对于每个证明,验证者都会随机选择一个随机数。证明者使用tpm2_fquote,它输出签名(sig.bin)和已签名的整个消息(msg.bin)。这些信息被发送给验证器,验证器使用tpm2_checkquote来验证报价及其公钥和随机数的可信副本。
假设验证成功,验证器知道pcrs.bin包含所选PCR值的哈希值,可以根据该哈希值验证事件日志。对于生产使用,TCG可信证明协议(TAP)定义了在证明者和验证者之间发送的一组信息元素及其标准化编码。开发人员可以基于客户端推送或验证器拉取证明或组合来操作协议。存在几种证明的参考实现,包括制作和验证报价,包括HIRS[8]和IBMACS[9]
同步事件:在这个例子中,我们只关注操作系统之前的事件。操作系统运行后,这些事件是稳定的,不需要TPM2_Quote和读取PCClient日志之间的同步。但是,如果还要验证操作系统级事件,在读取测量日志之前,首先获取TPM2_Quote是至关重要的。在Linux上,TPM2_Quote操作是与读取测量日志独立的异步操作。如果首先完成TPM2_Quote,那么我们知道测量列表必须包含相同或更多的事件,但不能少于。验证器可以在日志中的每个事件后进行检查,查看是否与报价匹配。日志末尾可能有额外的事件,但在某些时候必须与报价相匹配。如果列表末尾有额外的未验证事件,验证者应该简单地忽略它们,因为它们无法被验证。(在运行中的系统上,证明始终是一种“时间点”验证,应理解为在报价时发生)。
另一方面,如果我们先阅读列表,然后获取报价,日志可能会缺少最新事件,并且无法根据报价验证列表,因此需要新的证明。
3. 获取日志并将其解析为离散事件
Linux中/sys/kernel/security/tpm0/binary_bios_measurements处的事件日志是一个二进制数据流,如图所示。该数据流可以可选地由证明应用程序转换为[11]中指定的规范事件日志格式(CEL)。为简单起见,本指导文档仅描述了原始的原生格式。CEL规范给出了具有详细解析信息的本地和翻译格式的日志的详细示例。必须首先将本机日志解析为单个事件
为了向后兼容,第一个事件总是使用固定的SHA1摘要字段记录。所有后续事件都以哈希敏捷格式记录,该格式支持不同类型的多个哈希。事件数据和摘要字段是不同长度的字节数组。所有其他字段都是小Endian格式的UINT32。
由于事件日志在此阶段未经验证,因此必须非常小心地避免数据驱动的攻击,特别是对于可变长度事件数据和可变计数摘要字段。
4. 通过与实际PCR值进行比较,验证整个日志的完整性
一旦解析了单个事件,就可以通过扩展所有事件的指示PCR的事件摘要来计算虚拟PCR值(PCR回放)。如果计算出的PCR值与报价中的哈希PCR值匹配,那么我们就知道摘要没有经过篡改。如果任何PCR值不匹配,则整个日志都是可疑的。
计算PCR值需要模拟TPM执行的扩展操作。其伪代码为:
Initialization: PCR = 0;
For each extend operation: PCR = hash(PCR | digest)
也就是说,所有PCR都以零值开始,每个新值都是旧值与新事件摘要连接的哈希值的结果。如7.2.2所述,该验证通常应检查匹配每次事件后的报价,以处理报价后发生的事件。在这个特定的例子中,我们只验证过去的固件事件,因此报价后不应该有任何事件,但在每个事件后进行检查是一个好习惯,可以处理一般情况。
5. 验证每个日志条目
验证所选日志条目的完整性和正确性
一旦事件摘要与报价进行了验证,我们就知道事件摘要、PCR索引和顺序都没有被篡改。下一步是检查每个事件,并尝试通过摘要进行验证,要么是因为摘要是RIM中的二进制文件,要么是由于摘要直接与事件数据匹配。
首先,重要的是要记住,eventType字段未经验证,可能已被攻击者更改。
验证器只能将其用作验证事件数据的提示。例如,如果eventType为EV_EFI_BOOT_SERVICES_APPLICATION,则验证器将尝试在EFI哈希的RIM列表中查找摘要。
由于摘要已经过验证,在RIM的EFI哈希中找到摘要将有力地验证eventType是正确的。
由于某些字段未经摘要验证,根据HashLogExtendEvent的使用情况,我们可能必须以其他方式验证其余事件字段的完整性和正确性。对于示例系统,事件日志条目可以通过以下五种方式之一进行验证:
- CONTENT_MATCHES_DIGEST
在大多数情况下,事件内容很小,与哈希处理的数据相同,为事件数据内容提供了完全的加密保护。这种保护并不完美:在这些事件中,事件类型标签仍然没有像刚才讨论的那样受到保护。幸运的是,在所有情况下,摘要和事件数据都足以提供安全的验证。 - HASH_MATCHES_EFI
在这些事件中,一个大型二进制文件被散列,事件内容中有关于二进制文件的未经身份验证的“提示”,例如加载文件的EFI路径。虽然攻击者可以自由修改声明的路径,但哈希值由批准的EFI二进制文件的RIM DB进行身份验证,然后验证事件内容是否与“批准”的路径匹配。攻击者只能强制内容不匹配。在示例日志中,RIM必须为shim.EFI提供EFI哈希。请注意,这些哈希必须是“规范化”的EFI哈希,它不是文件的简单哈希,而是文件的哈希,不包括EFI签名的空间。sbsigntools存储库[10]中实用程序sbverify的源代码可以用作如何计算文件的规范化EFI哈希的示例。 - AUTHORITY_FOUND
在这种情况下,事件内容是用于验证加载的EFI映像的实际二进制证书数据。
事件内容既与摘要匹配,又直接与包含“已批准”证书数据的EFI变量匹配。验证者可以止步于此,但它甚至可以进一步验证证书链本身。如前所述,此经过强身份验证的证书不能直接用于验证加载的二进制文件,因为签名本身没有记录,因此验证器仍需要在受信任的RIM数据库中找到二进制文件的哈希值。 - NONE
根据规范,第一个事件(EV_NO_ACTION)完全未经身份验证——没有扩展PCR,因此无法验证摘要,内容也不会被散列。攻击者可以任意更改此内容,验证者必须忽略其内容。表3总结了附录A中示例日志中使用的事件验证方法。
总之,附录A中的事件日志显示了26个事件。只要RIM数据包括三个HASH_MATCHES_EFI事件的哈希值(如/boot/EFI/EFI/fedora/simp.EFI),那么所有事件都可以被完全验证,并理解给定的EFI路径不应被信任。
验证整个日志是否正确(符合TCG内容和订购标准)
在这个阶段,我们知道列表是不受限制的,每个事件都是正确的。下一步是验证整个日志是否包含判断证明者安全性所需的所有条目。一种方法是检查日志是否符合相应的标准,以便它包括所有强制性(必要)条目,并且所有条目的顺序都正确。事件的遗漏或排序错误可能会在测量的引导序列中打开漏洞,否则仅仅通过验证每个单独的事件就不会注意到这些漏洞。
核查人员应注意整个清单中的任何此类问题。
虽然[3]中规定了详细的事件日志要求,但以下是其中一些要求的示例。
PCClient必需事件:必须包括以下事件:
EV_NO_ACTION
EV_S_CRTM_VERSION
EV_EFI_VARIABLE_DRIVER_CONFIG (must log PK, KEK, DB, and DBX variables)
EV_POST_CODE
EV_EFI_GPT_EVENT
EV_EFI_VARIABLE_BOOT (must log BootOrder and Boot#### variables)
EV_SEPARATOR
EV_EFI_VARIABLE_AUTHORITY
EV_EFI_BOOT_SERVICES_APPLICATION
PCClient保留或弃用事件(不应包括在内):
EV_PREBOOT_CERT
EV_UNUSED
EV_IPL
EV_IPL_PARTITION_DATA
PC客户端要求的顺序:
EV_NO_ACTION must be the first event
EV_SEPARATOR for a given PCR must come after all events for that PCR and before the first EFI application
验证整个日志是否符合策略
最后,可以在整个日志上执行本地策略。虽然这些标准规定了事件类型和一些顺序,但它们没有涵盖可选事件中的操作系统要求或其他语义。例如,附录A中的日志表明Fedora 32的启动应包括以下启动顺序:
(CRTM) -> EFI Config -> EFI Boot Variable -> shim.efi
本地策略可能要求所有这些元素按此顺序包含在日志中,这超出了TCG规范中的基本要求。同样,这种验证可能需要获得一个已知的良好参考日志进行比较
6. 事件日志验证总结
附录A中的列表是示例系统的验证器输出,其中验证器遵循了7.2中的先前步骤。鉴于对完整性收集阶段的理解,验证者了解给定日志的哪些部分可由报价验证,哪些部分不可验证至关重要。在此列表中,黄色突出显示表示日志中未经报价验证的部分。验证者在其分析中不得信任或依赖任何黄色部分。绿色突出显示了通过直接匹配RIM数据的摘要进行强烈验证的条目,例如加载的EFI二进制文件和受信任的权威证书。
为简单起见,此列表显示了基本摘要,其中每条记录都包括PCR编号、事件类型以及事件数据和验证结果的摘要。这26个事件来自BIOS,符合TCG规范。
由于此事件日志的计算PCR值与实际PCR值匹配,我们知道序列号、PCR号和摘要没有被篡改。绿色高亮显示了直接从与RIM数据匹配的摘要中验证的事件数据。黄色突出显示的是无法验证的事件类型和提示。尽管HashLogExtendEvent存在局限性,但在给定可信的RIM哈希/路径数据的情况下,除初始EV_NO_ACTION事件之外的所有事件都是可验证的。7.2.5中的表总结了该日志中所有事件的事件类型和验证,段落解释了表中列出的不同类型的验证结果。
事件中的大多数黄色字段都没有真正的问题——虽然路径没有经过验证,但二进制文件经过了身份验证。关键事件是绿色显示的事件,用于验证加载的EFI二进制文件。这也包括提供安全引导中使用的证书的各个AUTHORITY事件。