Intel TME&MKTME Spec 翻译

TME & MKTME

本文是对Intel TME&MKTME的翻译,夹带了很多个人理解(Q&A),不周之处还望包含.

overview

在BIOS中配置并lock,一旦lock,SoC中external memory bus上的所有数据均会加密(以加密key的方式),加密方式是128bit/256bit AES XTS算法,具体取决于平台提供的算法和BIOS中的配置。

key由SoC中实现的hardware random number generator产生,这些key无法被软件或者接入到SoC上的硬件接口读到。

TME能够以AES-XTS算法对external memory bus上的数据以及DIMM中的数据进行保护。

MKTME基于TME实现,并支持了更多的key。

软件可以通过配置SoC来使用一定数量(<=SoC支持的数量)的key。

Ewan:

Q: 这些key是平台提供好的,软件从其中选择,还是软件自己产生的?

A: 软件可以通过配置,决定是否使用软件产生的key,或硬件产生的key,或混合模式。

软件可以使用这些key加密任何page

Ewan:

Q: 这里的加密粒度为什么是page而不是字节或者其他的什么粒度?

A: 这其实由key加密的原理决定。加密引擎通过识别发送到memory controller上的物理地址,提取其中的keyID字段,并查询自己维护的key table,来决定是否要对这笔memory access进行加密,因此理论上加密粒度可以精确到字节,因为通过物理地址可以访问任何一个字节。但是,通常来说系统中的memory都是由分页模式管理的,如果想要加密某个字节,必须对该字节所在的物理地址的keyid field进行填充,而该物理地址一定属于某个page,所以必须将该物理地址&~0xFFF(假设页大小是4K)的结果写入最后一级页表entry,所以写入最后一级页表entry的物理地址是带keyID的,最后12个bit为0的一个地址,如果keyID存在,那么memory controller会对最后一级页表的某个entry指向的一个page全部加密。

spec这里只是说了最寻常的情况是加密任何page,其实还可能加密更小的粒度,例如如果在实模式下,通过写CS/DS/FS/GS段寄存器中的物理地址的keyid field,就可以加密整个段。

MKTME默认使用TME,除非软件指定了使用MKTME.

MKTME还支持软件提供一个key,这种能力适合对非易失性memory的加密,或与认证机制结合的加密,或密钥提供服务的使用

Q: 第2,3种场景解释?

A: 2: 设想先开机,然后输入密码,如果输入密码失败,则无法开机。 3: 密钥提供软件提供一个密钥,并对全内存或某page进行加密。

在虚拟化场景下,VMM应当使用MKTME功能以透明的向虚拟机提供TME功能。

OS可以通过修改部分code达到在native和虚拟化环境下同时使用MKTME,如果使用得当,在虚拟机中就可以使用MKTME。

TME Introduction

AES XTS加密引擎就在DRAM controller和CPU Core的数据访问通路上,因此所有进出SoC的memory操作都会加密。SoC内部的数据,包括在cache中的,都保留了原始样子,因此适用于当前所有的软件和IO模型。

这些加密key由CPU自己产生,因此软件不可见,如果使用NVRAM,当该NVRAM被当作普通RAM时,可以使用CPU产生的这些key加密,如果该NVRAM被当作非易失性memory时,可以选择在重启系统时,或进入cpu power cycle时,使用同样的key,即不用再重新生成key。

MKTME Introduction

MKTME和TME的硬件架构是一样的,只是支持了更多的key。

MKTME在native OS和虚拟化场景下都能用,图中展示的是在虚拟化场景下使用的一个例子。

一般来说,VMM使用keyID0来加密自己的memory,也就是TME所使用的。当然VMM也可以使用别的keyID来加密自己的memroy。图中,VM1使用keyID1来加密自己的私有memory,VM2使用KeyID2。共有memory,使用keyID3加密。当然,VM1也可以使用KeyID0来加密自己的任何page。

KeyID包含在page table entry中,是物理地址field的高位。

KeyID对物理地址高位的利用,也可以扩展到IA pages(最普通的页,即MMU通过页表管理的页)和IOMMU pages。只要在SoC内部,KeyID总是留在物理地址的高位,除了在tweak时,以及在external bus上时。在SoC外部或在对AES XTS做tweak的时候,不会使用keyID.[tweak**即微调,是AES加密算法的一个步骤]

TME & MKTME: 枚举,控制寄存器

枚举

  • 是否支持TME,MKTME
  • MKTME支持的最大keys数量

BIOS必须在早期boot阶段通过MSR激活TME/MKTME,并选择具体支持多少keys。一旦激活TME/MKTME,挂在SoC/CPU上的所有memory(除了TME Exclusion Range)都会使用CPU生成的key加密,并且在接下来的每次boot时都会使用CPU生成的加密key加密。注意可以通过设置MSR来绕过加密,即所有使用keyID0的范围跟都不会加密。

可以使用MKTME枚举来发现CPU和集成到external memory controller上的部分memory的capabilities,但是不需要在这个枚举过程中体现external memory controller的features,部分集成在这些controller上的memory也不需要体现。BIOS查询某memory是否可以加密,可以通过基于UEFI2.8协议,去查询该memory的EFI_MEMORY_CPU_CRYPTO属性,该bit为1表示可以加密,为0表示不能加密。BIOS/OS在加密某memory之前,应该查询该memory到底能不能被CPU加密。

TME的枚举

CPUID.TME (CPUID.(EAX=07H, ECX=0H): ECX[13]) 指示以下4个MSR是否存在:

• IA32_TME_CAPABILITY – Address 981H

• IA32_TME_ACTIVATE – Address 982H

• IA32_TME_EXCLUDE_MASK – Address 983H

• IA32_TME_EXCLUDE_BASE – Address 984H

MKTME的枚举

CPUID.TME说明了 IA32_TME_CAPABILITY MSR是否存在,该MSR会指示TME的一些feature,以及MKTME是否存在及MKTME的features。MKTME由BIOS使用 IA32_TME_ACTIVATE这个MSR激活,激活MKTME的先决条件是激活TME。

IA32_TME_CAPABILITY MSR

keyID所使用的位数不会影响CPUID物理地址最大位数的报告【CPUID(EAX = 0x80000008)的bit0-bit7】,无论上面那个MSR中报告的keyID bits是多少位。

IA32_TME_ACTIVATE MSR

该MSR用于lock以下MSR,在lock之后,任何对这些MSR的读操作将会被ignored。如果CPU reset了,这个lock也会reset。

• IA32_TME_ACTIVATE

• IA32_TME_EXCLUDE_MASK

• IA32_TME_EXCLUDE_BASE

这俩exclude MSR应该在activate MSR之前配置好。

对activate MSR进行WRMSR写操作会导致的情况。

上表即:

  • Activate MSR没被枚举,却写该MSR。#GP
  • Activate MSR中的lock=1,却写该MSR。 #GP
  • 向该MSR中的reserved bits写1.
  • 写不支持的加密算法
  • 写该MSR,但bypass = 1. 不会报错,但TME会diabled,并且之后对该MSR的读会返回xxxx01b.
  • 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=0(即产生一个new key),并且RNG(随机数产生器)成功,这是正常状态,即TME activate成功。之后的读取会返回xxxx11b.
  • 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=0(即产生一个new key),并且RNG(随机数产生器)失败,这是错误状态,即TME activate失败。之后的读取会返回xxxx00b.
  • 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=1(即从storage 加载一个key),并且从storage加载了一个非0 key,这是正常状态,即TME activate成功之后的读取会返回xxxx111b.
  • 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=1(即从storage 加载一个key),并且从storage加载了一个值为0 key,这是错误状态,即TME activate失败。之后的读取会返回xxxx100b.
  • 写该MSR,值为其它合理值,之后的读会返回写的值以及lock =1.
  • 写该MSR,设置的MKTME的keyid bits大于在枚举时的最大bits,产生#GP
  • 写该MSR,设置了keyid bits,但是TME并未enable(bypass = 0 && hardware encryption enable = 1),产生#GP
  • 写该MSR,但是lock没被set(hardware没有成功set该bit),则表示write没有被成功submit。

MK_TME_CORE_ACTIVATE MSR

这是一个BIOS-only MSR(每个core一个), 在成功写IA32_TME_ACTIVATE之后,MK_TME_CORE_ACTIVATE 应该被WRMSR EDX:EAX==0,如果这次WRMSR fail,会导致不可预测的行为。如果不支持MKTME,对这个MSR的访问行为会导致#GP。

每一个SMI都会导致core上的该MSR被load with pakage MSR value。(what is pakage MSR?--- a shadow MSR to store value that was written in)

IA32_TME_EXCLUDE_MASK MSR

TME或MKTME(keyid=0时)支持对某些memory加密,同时对特定范围的memory不加密(通过IA32_TME_EXCLUDE_MASK MSR实现)。特别的,在用MKTME时,keyid!=0时,TME exclusion Range MSR不会起作用。

起初这个exclude功能是为了exclude那些不给OS展示的memory,当然,TME/MKTME并没有限制对这个exclude功能的使用仅限于不给OS展示的memory,可以随便用。软件可以通过读这个MSR确认是否有exclude功能。该range 寄存器定义在许多其它range resiter后面。

定义了base和mask,怎么算出一个范围呢?公式如下:

Address_in_range & TMEEMASK = TMEEBASE & TMEEMASK

例如TMEEMASK = 0x0FFF_F000,TMEEBASE = 0x1000_0000

那么根据这个公式,TMEEBASE & TMEEMASK = 0,只要与TMEEMASK做与运算,结果为0的地址都在这个范围内,那么会有多个范围,如0x0000_0000到0x0000_0FFF,0x1000_0000到0x1000_0FFF…

spec规定TME/MKTME exclude的memory范围必须是连续的,所以给MASK设计值时注意连续性。如将TMEEMASK设置为0xFFFF_0000,以将范围固定为0x1000_0000到0x1000_FFFF.

MKTME 的运行时行为

在MKTME由BIOS激活之后,与没有MKTME相比,处理器的行为在运行时会有一些不同。

物理地址规范的改变

MKTME最重要的改变还是对物理地址的重利用,使用物理地址的高bits与memory controller中的加解密引擎交互,基于这种重利用,为了保证物理地址行为的正确性,需要部分硬件和软件行为的变化。

当MKTME激活之后,从MAX_PA_BIT(CPUID报的)开始的几位会被用于KeyID。

即CPU上本来的物理地址宽度不会改变,例如CPUID报这个CPU上的物理地址宽度为48bit,本来这48bit都用来寻址物理memory,但在激活MKTME之后,bit47-bit44就会用于keyID,而不是原来的物理memory寻址。

普通分页模式行为的改变

如果使用分页模式,但不使用EPT(既使用EPT又使用分页模式,说明当前处于虚拟化场景下,即Guest memory管理),物理地址的MAX_PA_BIT-bit0本来用于寻址各级页表,但激活MKTME之后,从MAX_PA_BIT开始的几位会被用于keyID,包括CR3中的物理地址的高几bit也会用于keyID。

注意如果在使用EPT,即处于Guest memory管理上下文,IA paging实际使用的是GPA,由于GPA并没有被MKTME修改,因此GPA只单纯用于EPT页表寻址,而不参与加解密。EPT table walk的优先级比使能MKTME高。

EPT 分页模式行为的改变

如果在non-root下使用EPT,进行EPT table walk时的物理地址的高几个bit,会用于KeyID,包括EPTP中的物理地址的高几bit,也会用于KeyID。注意如果Guest OS中 使用了MKTME,那么EPT使用的物理地址中就会包含KeyID.

即在L1 guest中使用EPT,那么其实进行EPT table walk的物理地址是否包含keyID bits,取决于L1 Guest是否使用了MKTME,也就是L0要向L1提供MKTME功能。

其它物理地址行为

其它物理地址,类似于VMCS pointers,物理地址定位的bitmaps等,他们的物理地址中,都会包含从MAX_PA_BIT开始的keyID。注意所有的reserved bit checking保持不变,这些checking都会基于Max_PA_BIT进行reserved bits检测。例如MAX_PA_BIT = 47,那么63-48为reserved,此时reserved bit check都是检查bit48-bit63是否被set。当然reserved bit分布于各个bit,但是高位bit与我们的话题联系更加紧密。

MKTME Key 的编程

MKTME 引擎肚子里有一个key table,无法被software访问。该table可以存储和每个keyID关联的key和加密模式。每个keyID应该从3种加密模式中选一个:

  1. 使用指定key加密
  2. 不加密
  3. 使用TME key加密

未来的实现中可能会包含更多加密模式。可以使用PCONFIG指令来编辑KEYID属性,目前PCONFIG指令只是用于MKTME,以后会引入更多PCONFIG可以操作的目标。因此,PCONFIG指令的枚举与MKTME的枚举是独立的。

所以PCONFIG中的P意思是platform,即对该platform上的某个功能集进行设置,并没有特定指示MKTME。

PCONFIG 指令描述

软件可以使用PCONFIG指令对platform上的features进行配置。

PCONFIG支持多个leaves(叶),叶可以在执行PCONFIG指令时使用RAX寄存器传入。RBX,RCX,RDX有leaf特定的用途。PCONFIG是一个“pakage scope”的指令,执行时需要在每个物理pakage中执行一次,来配置这个pakage中所有CPU的功能。

目前PCONFIG只有一个leaf function,就是MKTME_KEY_PROGRAM,其leaf号码,也就是要写入RAX的号码为0。

除了leaf function,PCONFIG还有一个重要概念,叫做PCONFIG Target,这个Target即platform上的一个功能集,target=0时,表明该target是一个invalid target, target=1时,表明该target是MKTME target。 target的其他值目前未使用。

PCONFIG 的MKTME_KEY_PROGRAM leaf用于软件管理和某个keyID关联的key。即在执行PCONFIG时,给RAX写0,并给RBX写入MKTME_KEY_PROGRAM_STRUCT的地址。

PCONFIG的成功执行,会clear掉RAX中的值(在MKTME leaf中,RAX本身就是0,因此实际执行时看不出效果),并且clear掉ZF,CF,PF,AF,OF,SF。如果PCONFIG的执行出了问题,具体错误原因会写入RAX,同时ZF置1,CF,PF,AF,OF,SF清0.

回到MKTME leaf。该leaf的操作目标时MKTME_KEY_PROGRAM_STRUCT,该结构如下表所示。

所以可以看到该struct占据的地址空间大小为128+64 = 192个字节。实际情况下,该struct需要以256字节对齐。

COMMAND field,即KEYID_CTRL的第一个字节,有4种取值情况。

  1. KEYID_SET_KEY_DIRECT(0) 软件尝试直接将自己提供的keyID与自己提供的Key绑定。

  2. KEYID_SET_KEY_RANDOM (1) 软件尝试命令硬件产生一个随机的key,与自己提供的KeyID绑定。每次对同一keyID运行PCONFIG,都会导致key的变更。

  3. KEYID_CLEAR_KEY (2) 软件尝试将key(自己提供给加密引擎的)与当前KeyID进行解绑,解绑之后,当前KeyID与KeyID=0的加密行为相同,即只是用TME加密/绕过加密。

  4. KEYID_NO_ENCRPT(3)软件尝试配置:使用此keyID的memory不用加密。

CRYPTO_ALG field,即KEYID_CTRL的第2,3个字节,允许软件从activated 算法种选择一种加密算法,与当前的keyID结合,即当前keyID对应的key,会由CRYPTO_ALG field选择的算法生成。可以选择的加密算法是由IA32_TME_ACTIVATE MSR 的bit64-bit48 mask出来供该field选择的算法。注意这里不包括用于keyID=0时负责加密的算法,KeyID=0时的加密算法是由IA32_TME_ACTIVATE MSR的bit7:4进行设置的。

Ewan:

结合COMMAND field,我们可以推断出哪种command模式需要软件提供key及选择加密算法,哪种不需要。

KEYID_SET_KEY_DIRECT**: 需要提供key,需要选择加密算法,加密引擎使用软件提供的key及加密算法进行加密。

KEYID_SET_KEY_RANDOM**: 需要提供key的熵(optional),需要选择加密算法,加密引擎随机生成一个key并与熵混合。

KEYID_CLEAR_KEY**: 需要提供key,需要选择加密算法,因为加密引擎需要这两个数据以清除当前keyID与之前软件提供的key的关联。

KEYID_NO_ENCRPT**: 不需要提供key,但需要选择加密算法,这是spec规定。

Q: 这里spec强调软件需要在KEYID_CLEAR_KEY和KEYID_NO_ENCRPT时选择加密算法的意思是,“即使这两种看起来完全不需要加密算法的命令也需要选择加密算法,”还是“只针对这两种任务,需要选择加密算法?”

A: 我觉得是第1种意思。

KEY_FIELD_1

当COMMAND为 KEYID_SET_KEY_DIRECT时,该filed提供软件传入的用于加密的key。

当COMMAND为KEYID_SET_KEY_RANDOM时,该filed提供软件传入的用于与硬件生成的加密key混合的熵。

软件传入的用于加密的key以及熵,可能会导致weak key的问题,但是这得软件自己保证。如果使用AES XTS-128加密算法,该field的高48字节应该在软件执行PCONFIG前清0. 如果使用AES XTS-256加密算法,该field的高32字节应该在软件执行PCONFIG前清0.

KEY_FIELD_2

当COMMAND为 KEYID_SET_KEY_DIRECT时,该field提供用于加密算法的tweak key。

当COMMAND为KEYID_SET_KEY_RANDOM时,该field提供用于与硬件随机生成的加密key混合的熵。

软件传入的用于加密的key以及熵,可能会导致weak key的问题,但是这得软件自己保证。如果使用AES XTS-128加密算法,该field的高48字节应该在软件执行PCONFIG前清0. 如果使用AES XTS-256加密算法,该field的高32字节应该在软件执行PCONFIG前清0.

所有的keyid的加密行为默认情况下使用TME加密,即如果我们在PCONFIG MKTME_TARGET时,只设置了keyid field而没有设置其它field时,该keyid的默认加密行为就是TME加密。软件可以在任何时候修改keyID对应的key及加密方式。对keyID对应的key的修改不会修改TLB,cache,memory pipeline种的key,因此需要软件在执行PCONFIG前执行适当的指令来保证修改key这一行为的有效性。第7章介绍了软件流的一个例子。

下表展示了在执行PCONFIG MKTME_TARGET后,RAX中的取值对应的情况。

PCONFIG 虚拟化

VMM可以对PCONFIG指令进行虚拟化,硬件在VMCS中提供了2个control,用于支持PCONFIG虚拟化。

  1. PCONFIG_Enable. 该control为1,表示可以在guest中执行PCONFIG指令
  2. PCONFIG_Exiting. 如果PCONFIG_ENABLE为1,那么guest中执行PCONFIG指令是否发生vmexit取决于该filed是否为1,1表示在guest中执行PCONFIG会vmexit。

PCONFIG的枚举

CPUID[EAX = 7, ECX = 0].EDX[bit18] 指明当前平台是否支持PCONFIG指令。

CPUID[EAX = 0x1B. ].subleaves 返回PCONFIG指令支持的的一些feature信息。

执行CPUID[EAX=0x1B,ECX=n(n>=0)],则:

  • EAX的bits11:0 指明该platform是否支持号码为n的PCONFIG Target. 1-支持,0-不支持
  • 如果EAX的bits11:0为1,表示支持该PCONFIG Target,那么EAX[11:0]为1,EAX[31:12]为0,EBX,ECX,EDX分别为该PCONFIG Target的Target_ID_1,2,3.
  • 如果EAX的bits 11:0为0,表示不支持该PCONFIG Target,比n大的PCONFIG Target均不支持。

软件应该使用CPUID[EAX=0x1B,ECX=n]扫描支持的PCONFIG TARGET,直到遇到第一个不支持的target为止。

PCONFIG的并发性

如果多个core同时执行PCONFIG MKTME_KEY_PROGRAM,即PCONFIG EAX = 0,RBX =MKTME_KEY_PROGRAM_STRUCT的地址,那么只会有一个core成功更新这个key table(即key table在硬件中只有一份),在其它未成功的core上,PCONFIG会返回DEVICE_BUSY(error_code),软件必须retry来更新key table。

硬件使用了一个lock来保证key table同时只能被一个core更新。

PCONFIG具体操作(硬件伪代码)

/* #UD if PCONFIG is not enumerated or CPL>0 */
if (CPUID.7.0:EDX[18] == 0 OR CPL > 0) 
    #UD;
if (in VMX non-root mode)
{
    if (VMCS.PCONFIG_ENABLE == 1)
    {
        /* PCONFIG Target 大于 62的部分的exit,均由PCONFIG_EXITING[63]控制*/
        if ((EAX > 62 AND VMCS.PCONFIG_EXITING[63] ==1) OR
        (EAX < 63 AND VMCS.PCONFIG_EXITING[EAX] == 1))
        {
            Set VMCS.EXIT_REASON = PCONFIG; //No Exit qualification
            Deliver VMEXIT;
        }
    }
    else
    { // 在PCONFIG_ENABLE == 0的情况下,在non-root执行PCONFIG
        #UD
    }
}

/* #GP(0) for an unsupported leaf 现行CPU只支持MKTME这一个subleaf*/
if(EAX != 0) 
    #GP(0)

if (EAX == 0)
{
    /* #GP(0) if TME_ACTIVATE MSR is not locked or does not enable hardware
    encryption or multiple keys are not enabled */
    if (IA32_TME_ACTIVATE.LOCK != 1 OR IA32_TME_ACTIVATE.ENABLE != 1 OR
    IA32_TME_ACTIVATE.MK_TME_KEYID_BITS == 0) 
        #GP(0)
    /* Check MKTME_KEY_PROGRAM_STRUCT is 256B aligned */
    if(DS:RBX is not 256B aligned) 
        #GP(0);

    /* Check that MKTME_KEY_PROGRAM_STRUCT is read accessible */
    // DS: RBX should be read accessible
    /* Copy MKTME_KEY_PROGRAM_STRUCT to a temporary variable */
    TMP_KEY_PROGRAM_STRUCT = DS:RBX.*;

    /* RSVD field check */
    if(TMP_KEY_PROGRAM_STRUCT.RSVD != 0) 
        #GP(0);
    if(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.RSVD !=0) 
        #GP(0);
    
    /* KEYID_FIELD字段检查 */
    if (TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.ENC_ALG[0] == 1)
    { // AES XTS-128 
        if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[63:16] != 0) 
            #GP(0);
        if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[63:16] != 0) 
            #GP(0);
    }
    if (TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.ENC_ALG[2] == 1)
    {   // AES XTS-256
        if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[63:32] != 0) 
            #GP(0);
        if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[63:32] != 0) 
            #GP(0);
    }
    /* Check for a valid command 目前来说是4选1*/
    if(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.COMMAND is not a valid command)
    {
        RFLAGS.ZF = 1;
        RAX = INVALID_PROG_CMD;
        goto EXIT;
    }
    /* Check that the KEYID being operated upon is a valid KEYID */
    if(TMP_KEY_PROGRAM_STRUCT.KEYID >
    2^IA32_TME_ACTIVATE.MK_TME_KEYID_BITS – 1 // keyid bit数应该合规
    OR TMP_KEY_PROGRAM_STRUCT.KEYID >
    IA32_TME_CAPABILITY.MK_TME_MAX_KEYS // keyid本身的数值应该合规
    OR TMP_KEY_PROGRAM_STRUCT.KEYID == 0) // 不应该对keyid=0的key属性进行编辑
    {
        RFLAGS.ZF = 1;
        RAX = INVALID_KEYID;
        goto EXIT;
    }
    /* Check that only one algorithm is requested for the KeyID and it is
    One of the activated algorithms */
    if(NUM_BITS(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.CRYPTO_ALG) != 1 || // 只能选择一个算法
    (TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.CRYPTO_ALG &
    IA32_TME_ACTIVATE. MK_TME_CRYPTO_ALGS == 0)) // 不能选择编外算法
    {
        RFLAGS.ZF = 1;
        RAX = INVALID_CRYPTO_ALG;
        goto EXIT;
    }
    /* Try to acquire exclusive lock */
    if (NOT KEY_TABLE_LOCK.ACQUIRE(WRITE))
    {
        //PCONFIG failure
        RFLAGS.ZF = 1;
        RAX = DEVICE_BUSY;
        goto EXIT;
    }
    /* Lock is acquired and key table will be updated as per the command
    Before this point no changes to the key table are made */
    switch(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.COMMAND)
    {
        /* KEYID_SET_KEY_DIRECT下,tweak_key来自key_field2, data_key来自key_field1,
            加密模式为“使用keyID_key加密”
        */
        case KEYID_SET_KEY_DIRECT:
            <<Write
            DATA_KEY=TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1,
            TWEAK_KEY=TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2,
            ENCRYPTION_MODE=ENCRYPT_WITH_KEYID_KEY,
            to MKTME Key table at index TMP_KEY_PROGRAM_STRUCT.KEYID
            >>
            break;
        /* KEYID_SET_KEY_RANDOM下,tweak_key和data_key均是由硬件产生的tweak和data与用户提供的
            tweak和data混合形成的,混合方法为亦或。加密模式为“使用keyID_key加密”。
        */
        case KEYID_SET_KEY_RANDOM:
            TMP_RND_DATA_KEY = <<Generate a random key using hardware RNG>>
            if (NOT ENOUGH ENTROPY)
            {
                RFLAGS.ZF = 1;
                RAX = ENTROPY_ERROR;
                goto EXIT;
            }
            TMP_RND_TWEAK_KEY = <<Generate a random key using hardware RNG>>
            if (NOT ENOUGH ENTROPY)
            {
                RFLAGS.ZF = 1;
                RAX = ENTROPY_ERROR;
                goto EXIT;
            }

            /* Mix user supplied entropy to the data key and tweak key */
            TMP_RND_DATA_KEY = TMP_RND_DATA_KEY XOR
                TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[15:0];
            TMP_RND_TWEAK_KEY = TMP_RND_TWEAK_KEY XOR
                TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[15:0];

            <<Write
            DATA_KEY=TMP_RND_DATA_KEY,
            TWEAK_KEY=TMP_RND_TWEAK_KEY,
            ENCRYPTION_MODE=ENCRYPT_WITH_KEYID_KEY,
            to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
            >>
            break;


        /* 使用TME加密,至于是加密还是绕过,取决于TME的设置 */
        case KEYID_CLEAR_KEY:
            <<Write
            DATA_KEY=’0,
            TWEAK_KEY=’0,
            ENCRYPTION_MODE = ENCRYPT_WITH_TME_KEY_OR_BYPASS,
            to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
            >>
            break;
        
        /* 不加密 */
        case KEYID_NO_ENCRYPT:
        <<Write
        DATA_KEY=’0,
        TWEAK_KEY=’0,
        ENCRYPTION_MODE=NO_ENCRYPTION,
        to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
        >>
        break;
}
RAX = 0;
RFLAGS.ZF = 0;
//Release Lock
KEY_TABLE_LOCK(RELEASE);
EXIT:
RFLAGS.CF=0;
RFLAGS.PF=0;
RFLAGS.AF=0;
RFLAGS.OF=0;
RFLAGS.SF=0;
}
end_of_flow

Spec的6.2.6,7,8,9,10,11,12分别列举了:

  • PCONFIG 指令会影响的flags位
  • 对PCONFIG指令加不同前缀造成的影响
  • 保护模式下,实模式下,virtual-8086模式下,兼容模式下,64bit模式下,PCONFIG指令可能会产生的异常以及对应的原因。

软件生命周期: 用keyID管理pages.

KeyID是物理地址的组成部分,这意味着keyID不仅存在于页表中,也存在于TLB,cache中。因此,软件必须采取适当的措施来保护软件的正确和安全。

限制和cache管理

如果2/多个keyID map的是同一块物理页,硬件不会保证这两个keyID映射到的页的cache一致性。软件必须精心保证这两块mapped的页的cache一致性,如果其中一个页的内容发生变化,另一个页的内容理应也发生变化。

如果两个物理地址,除了keyID不一样之外其它都一样,即指向同一个物理地址,但是CPU会把这两个地址当作不一样的地址,这种情况下,软件必须精心设计以满足安全性和准确性。MKTME没有对cache和TLB的行为做任何修改,只是单纯的多了一个keyID bits。

Q: 如何将keyID map到page?

A: mmap一个page,并更新该page对应的页表项内容,加上keyid。

对映射到同一物理地址的keyid mappings的软件指导

  1. 避免将多个keyID映射到同一个物理地址
  2. 如果软件必须将多个keyID映射到同一个物理地址,应该将那些页(keyid page)标记为read-only,只有一个带keyid的物理地址能读真正的物理页上的内容。
  3. 如果这些所有的页(keyid page)必须为可读写的,那么软件必须保证所有每次只有一个keyid page发起的写操作(用锁实现。)

增加页:将Keyid和page关联

  1. 如果软件暂时未拥有一个free key,就用PCONFIG新建一个keyid及对应的key。
  2. 如果某个物理page尚未被VMM拥有,那就通过更新页表(包含keyid),将该page映射到VMM的地址空间。
  3. 确保第一步完成(可能会有疑问说你第2步都已经用了这个keyid了,却在这里检查第一步是不是正确完成?我的想法是,由于OS并不会在使用前真正分配物理页,所以在这里检查没有问题。)
  4. 使用带keyid的物理地址访问该物理页,写全0,以避免keyID之间的数据泄露
  5. 使该页对一个新的VM可用,只需要通过EPT将该页映射到新的VM即可,EPT页表结构中的物理地址应该包含keyid。

注意PCONFIG应该在每个pakage的一个logical processor上进行。 本质上是一个memory controller,一个PCONFIG。

删除页: 将keyid与page解关联

  1. 通过更新EPT页表,保证该物理页无法被VM访问。
  2. invalid所有page mappings(包括使用INVEPT invalid所有EPT cache,以及对IOMMU对应的页表的cache进行invalid)
  3. 如果该页没有被映射到VMM,就通过更新页表结构来使该页映射到VMM地址空间。(这一步可能会有疑问说,你这个物理页都已经被VM用了好久了,怎么可能没有映射到VMM,但实际情况下,存在刚刚将keyid和page关联,就要将keyid与page解关联,这种时候尚未建立页表映射。)
  4. OS/VMM 清掉 dirty cache lines(使用旧的keyid)。如果该页在使用期间一直被EPT PML监控着,或一直被VMM监控该页的accessed/dirty flags,那么就可以省略这一步。
  5. 该页可以用于与新的keyid关联了。

OS/VMM 访问Guest Memory

出于模拟MMIO便利的需求,VMM可能需要访问Guest Memory,这时VMM可以通过使用guest的keyid设置VMM自己的页表内容,来访问guest memory。

OS/VMM可以使用keyid=0来建立在VMM和Guest之间共享的memory,对于direct io,OS/VMM应该使IOMMU中的页表中包含的keyid和EPT页表中包含的keyid一致,这样Guest可以直接访问该memory,而device也可以直接访问该memory。

所以IOMMU-MKTME的精髓在EPT和IOMMU table中对于访问同一块memory使用相同的keyid。

Reference

  1. Multi-Key-Total-Memory-Encryption-Spec.pdf
posted @ 2022-06-03 22:03  EwanHai  阅读(886)  评论(0编辑  收藏  举报