可信和加密密钥

可信和加密密钥是添加到现有内核密钥环服务的两种新密钥类型。 这两种新类型都是可变长度的对称密钥,并且在这两种情况下,所有密钥都在内核中创建,用户空间仅查看、存储和加载加密的 blob。 可信密钥需要可信源以获得更高的安全性,而加密密钥可以在任何系统上使用。 为了方便起见,所有用户级别 blob 都以十六进制 ASCII 显示和加载,并且会进行完整性验证。

可信源

可信源为可信密钥提供安全来源。 本节列出了当前支持的可信源,以及它们的安全性考虑因素。 可信源是否足够安全取决于其实现的强度和正确性,以及特定用例的威胁环境。 由于内核不知道环境是什么,也没有信任的度量标准,因此取决于可信密钥的使用者来确定可信源是否足够安全。

  • 存储的信任根

    1. TPM(可信平台模块:硬件设备)

      扎根于存储根密钥 (SRK),该密钥永远不会离开 TPM,它提供加密操作以建立存储的信任根。

    2. TEE(可信执行环境:基于 Arm TrustZone 的 OP-TEE)

      扎根于硬件唯一密钥 (HUK),该密钥通常烧录在片上熔丝中,并且只能由 TEE 访问。

    3. CAAM(加密加速和保证模块:NXP SoC 上的 IP)

      当启用高保证启动 (HAB) 且 CAAM 处于安全模式时,信任扎根于 OTPMK,这是一个永不公开的 256 位密钥,在制造时随机生成并熔入每个 SoC。 否则,将使用通用的固定测试密钥。

    4. DCP(数据协处理器:各种 i.MX SoC 的加密加速器)

      扎根于一次性可编程密钥 (OTP),该密钥通常烧录在片上熔丝中,并且只能由 DCP 加密引擎访问。 DCP 提供两个可以用作信任根的密钥:OTP 密钥和 UNIQUE 密钥。 默认使用 UNIQUE 密钥,但可以通过模块参数 (dcp_use_otp_key) 选择 OTP 密钥。

  • 执行隔离

    1. TPM

      在隔离的执行环境中运行的固定操作集。

    2. TEE

      在通过安全/可信启动过程验证的隔离执行环境中运行的可自定义操作集。

    3. CAAM

      在隔离的执行环境中运行的固定操作集。

    4. DCP

      在隔离的执行环境中运行的固定加密操作集。 只有基本的 blob 密钥加密在那里执行。 实际的密钥密封/解封是在主处理器/内核空间中完成的。

  • 可选绑定到平台完整性状态

    1. TPM

      密钥可以有选择地密封到指定的 PCR(完整性度量)值,并且只有在 PCR 和 blob 完整性验证匹配时才能由 TPM 解封。 加载的可信密钥可以使用新的(未来的)PCR 值进行更新,因此密钥可以轻松地迁移到新的 PCR 值,例如在更新内核和 initramfs 时。 同一个密钥可以在不同的 PCR 值下保存多个 blob,因此可以轻松支持多次启动。

    2. TEE

      依赖于安全/可信启动过程来实现平台完整性。 可以使用基于 TEE 的度量启动过程进行扩展。

    3. CAAM

      依赖于 NXP SoC 的高保证启动 (HAB) 机制来实现平台完整性。

    4. DCP

      依赖于安全/可信启动过程(供应商称为 HAB)来实现平台完整性。

  • 接口和 API

    1. TPM

      TPM 具有完善的文档记录、标准化的接口和 API。

    2. TEE

      TEE 具有完善的文档记录、标准化的客户端接口和 API。 有关更多详细信息,请参阅 Documentation/driver-api/tee.rst

    3. CAAM

      接口特定于芯片供应商。

    4. DCP

      供应商特定的 API,作为 drivers/crypto/mxs-dcp.c 中 DCP 加密驱动程序的一部分实现。

  • 威胁模型

    在使用特定可信源来保护安全相关数据时,必须评估其强度和对特定用途的适用性。

密钥生成

可信密钥

新密钥由随机数创建。 它们使用存储密钥层次结构中的子密钥进行加密/解密。 子密钥的加密和解密必须受到可信源内强大的访问控制策略的保护。 使用的随机数生成器根据所选的可信源而有所不同

  • TPM:基于硬件设备的 RNG

    密钥在 TPM 中生成。 随机数的强度可能因设备制造商而异。

  • TEE:基于 Arm TrustZone 的 OP-TEE RNG

    RNG 可根据平台需要进行自定义。 它可以是来自平台特定硬件 RNG 的直接输出,也可以是通过多个熵源播种的基于软件的 Fortuna CSPRNG。

  • CAAM:内核 RNG

    使用正常的内核随机数生成器。 要从 CAAM HWRNG 播种它,请启用 CRYPTO_DEV_FSL_CAAM_RNG_API 并确保探测到该设备。

  • DCP(数据协处理器:各种 i.MX SoC 的加密加速器)

    DCP 硬件设备本身不提供专用的 RNG 接口,因此使用内核默认 RNG。 像 i.MX6ULL 这样的具有 DCP 的 SoC 确实具有独立的硬件 RNG,可以启用该硬件 RNG 来支持内核 RNG。

用户可以通过在内核命令行中指定 trusted.rng=kernel 来覆盖此设置,以使用内核的随机数池覆盖所使用的 RNG。

加密密钥

加密密钥不依赖于可信源,并且速度更快,因为它们使用 AES 进行加密/解密。 新密钥从内核生成的随机数或用户提供的解密数据创建,并使用指定的“主”密钥进行加密/解密。“主”密钥可以是可信密钥或用户密钥类型。 加密密钥的主要缺点是,如果它们没有扎根于可信密钥,那么它们的安全性仅与加密它们的用户密钥一样高。 因此,主用户密钥应以尽可能安全的方式加载,最好在启动早期加载。

用法

可信密钥用法:TPM

TPM 1.2:默认情况下,可信密钥在 SRK 下密封,该 SRK 具有默认授权值(20 字节的 0)。 可以使用 TrouSerS 实用程序在 takeownership 时设置此值:“tpm_takeownership -u -z”。

TPM 2.0:用户必须首先创建一个存储密钥并使其持久化,以便该密钥在重新启动后可用。 这可以使用以下命令完成。

使用 IBM TSS 2 堆栈

#> tsscreateprimary -hi o -st
Handle 80000000
#> tssevictcontrol -hi o -ho 80000000 -hp 81000001

或使用 Intel TSS 2 堆栈

#> tpm2_createprimary --hierarchy o -G rsa2048 -c key.ctxt
[...]
#> tpm2_evictcontrol -c key.ctxt 0x81000001
persistentHandle: 0x81000001

用法

keyctl add trusted name "new keylen [options]" ring
keyctl add trusted name "load hex_blob [pcrlock=pcrnum]" ring
keyctl update key "update [options]"
keyctl print keyid

options:
   keyhandle=    ascii hex value of sealing key
                   TPM 1.2: default 0x40000000 (SRK)
                   TPM 2.0: no default; must be passed every time
   keyauth=      ascii hex auth for sealing key default 0x00...i
                 (40 ascii zeros)
   blobauth=     ascii hex auth for sealed data default 0x00...
                 (40 ascii zeros)
   pcrinfo=      ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
   pcrlock=      pcr number to be extended to "lock" blob
   migratable=   0|1 indicating permission to reseal to new PCR values,
                 default 1 (resealing allowed)
   hash=         hash algorithm name as a string. For TPM 1.x the only
                 allowed value is sha1. For TPM 2.x the allowed values
                 are sha1, sha256, sha384, sha512 and sm3-256.
   policydigest= digest for the authorization policy. must be calculated
                 with the same hash algorithm as specified by the 'hash='
                 option.
   policyhandle= handle to an authorization policy session that defines the
                 same policy and with the same hash algorithm as was used to
                 seal the key.

“keyctl print”返回密封密钥的 ASCII 十六进制副本,该副本采用标准 TPM_STORED_DATA 格式。 新密钥的密钥长度始终以字节为单位。 可信密钥可以是 32 - 128 字节(256 - 1024 位),上限是适应 2048 位 SRK (RSA) 密钥长度,以及所有必要的结构/填充。

可信密钥用法:TEE

用法

keyctl add trusted name "new keylen" ring
keyctl add trusted name "load hex_blob" ring
keyctl print keyid

“keyctl print”返回密封密钥的 ASCII 十六进制副本,该副本采用特定于 TEE 设备实现的格式。 新密钥的密钥长度始终以字节为单位。 可信密钥可以是 32 - 128 字节(256 - 1024 位)。

可信密钥用法:CAAM

用法

keyctl add trusted name "new keylen" ring
keyctl add trusted name "load hex_blob" ring
keyctl print keyid

“keyctl print”返回密封密钥的 ASCII 十六进制副本,该副本采用 CAAM 特定的格式。 新密钥的密钥长度始终以字节为单位。 可信密钥可以是 32 - 128 字节(256 - 1024 位)。

可信密钥用法:DCP

用法

keyctl add trusted name "new keylen" ring
keyctl add trusted name "load hex_blob" ring
keyctl print keyid

“keyctl print”返回密封密钥的 ASCII 十六进制副本,该副本采用特定于此 DCP 密钥 blob 实现的格式。 新密钥的密钥长度始终以字节为单位。 可信密钥可以是 32 - 128 字节(256 - 1024 位)。

加密密钥用法

加密密钥的解密部分可以包含简单的对称密钥或更复杂的结构。 更复杂结构的格式是特定于应用程序的,由“格式”标识。

用法

keyctl add encrypted name "new [format] key-type:master-key-name keylen"
    ring
keyctl add encrypted name "new [format] key-type:master-key-name keylen
    decrypted-data" ring
keyctl add encrypted name "load hex_blob" ring
keyctl update keyid "update key-type:master-key-name"

其中

format:= 'default | ecryptfs | enc32'
key-type:= 'trusted' | 'user'

可信和加密密钥使用示例

创建并保存一个名为“kmk”的长度为 32 字节的可信密钥。

注意:当使用具有句柄 0x81000001 的持久密钥的 TPM 2.0 时,请在引号之间的语句中附加“keyhandle=0x81000001”,例如“new 32 keyhandle=0x81000001”。

$ keyctl add trusted kmk "new 32" @u
440502848

$ keyctl show
Session Keyring
       -3 --alswrv    500   500  keyring: _ses
 97833714 --alswrv    500    -1   \_ keyring: _uid.500
440502848 --alswrv    500   500       \_ trusted: kmk

$ keyctl print 440502848
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

$ keyctl pipe 440502848 > kmk.blob

从保存的 blob 加载可信密钥

$ keyctl add trusted kmk "load `cat kmk.blob`" @u
268728824

$ keyctl print 268728824
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

在新 PCR 值下重新密封(TPM 特定)可信密钥

$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
$ keyctl print 268728824
010100000000002c0002800093c35a09b70fff26e7a98ae786c641e678ec6ffb6b46d805
77c8a6377aed9d3219c6dfec4b23ffe3000001005d37d472ac8a44023fbb3d18583a4f73
d3a076c0858f6f1dcaa39ea0f119911ff03f5406df4f7f27f41da8d7194f45c9f4e00f2e
df449f266253aa3f52e55c53de147773e00f0f9aca86c64d94c95382265968c354c5eab4
9638c5ae99c89de1e0997242edfb0b501744e11ff9762dfd951cffd93227cc513384e7e6
e782c29435c7ec2edafaa2f4c1fe6e7a781b59549ff5296371b42133777dcc5b8b971610
94bc67ede19e43ddb9dc2baacad374a36feaf0314d700af0a65c164b7082401740e489c9
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8

可信密钥的最初使用者是 EVM,它在启动时需要一个高质量的对称密钥来对文件元数据进行 HMAC 保护。 使用可信密钥可以有力地保证 EVM 密钥没有被用户级别问题泄露,并且当密封到平台完整性状态时,可以防止启动和离线攻击。 使用上面的可信密钥“kmk”创建并保存一个加密密钥“evm”

选项 1:省略“format”

$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175

选项 2:显式将“format”定义为“default”

$ keyctl add encrypted evm "new default trusted:kmk 32" @u
159771175

$ keyctl print 159771175
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

$ keyctl pipe 159771175 > evm.blob

从保存的 blob 加载加密密钥“evm”

$ keyctl add encrypted evm "load `cat evm.blob`" @u
831684262

$ keyctl print 831684262
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

使用用户提供的解密数据实例化加密密钥“evm”

$ evmkey=$(dd if=/dev/urandom bs=1 count=32 | xxd -c32 -p)
$ keyctl add encrypted evm "new default user:kmk 32 $evmkey" @u
794890253

$ keyctl print 794890253
default user:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382d
bbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0247
17c64 5972dcb82ab2dde83376d82b2e3c09ffc

预计可将可信加密密钥用于其他用途,例如磁盘和文件加密。特别是,为了使用加密密钥挂载 eCryptfs 文件系统,定义了新的格式 'ecryptfs'。有关用法的更多详细信息,请参阅文件 Documentation/security/keys/ecryptfs.rst

为了支持有效负载大小为 32 字节的加密密钥,定义了另一种新的格式 'enc32'。 这最初将用于 nvdimm 安全,但也可能扩展到其他需要 32 字节有效负载的用途。

TPM 2.0 ASN.1 密钥格式

TPM 2.0 ASN.1 密钥格式的设计易于识别,即使以二进制形式(修复了我们之前在 TPM 1.2 ASN.1 格式中遇到的问题),并且可以扩展以添加诸如可导入密钥和策略之类的功能。

TPMKey ::= SEQUENCE {
    type            OBJECT IDENTIFIER
    emptyAuth       [0] EXPLICIT BOOLEAN OPTIONAL
    parent          INTEGER
    pubkey          OCTET STRING
    privkey         OCTET STRING
}

类型是区分密钥的依据,即使以二进制形式,因为 TCG 提供的 OID 是唯一的,因此在密钥偏移量 3 处形成可识别的二进制模式。 目前可用的 OID 是:

2.23.133.10.1.3 TPM Loadable key.  This is an asymmetric key (Usually
                RSA2048 or Elliptic Curve) which can be imported by a
                TPM2_Load() operation.

2.23.133.10.1.4 TPM Importable Key.  This is an asymmetric key (Usually
                RSA2048 or Elliptic Curve) which can be imported by a
                TPM2_Import() operation.

2.23.133.10.1.5 TPM Sealed Data.  This is a set of data (up to 128
                bytes) which is sealed by the TPM.  It usually
                represents a symmetric key and must be unsealed before
                use.

可信密钥代码仅使用 TPM 密封数据 OID。

如果密钥具有众所周知的授权“”,则 emptyAuth 为 true。如果为 false 或不存在,则密钥需要明确的授权短语。大多数用户空间使用者会使用此信息来决定是否提示输入密码。

parent 表示父密钥句柄,在 0x81 MSO 空间中,例如 RSA 主存储密钥的 0x81000001。用户空间程序也支持在 0x40 MSO 空间中指定主句柄。如果发生这种情况,则会使用 TCG 定义的模板,动态生成主密钥的椭圆曲线变体到易失对象中,并用作父密钥。当前的内核代码仅支持 0x81 MSO 形式。

pubkey 是 TPM2B_PRIVATE 的二进制表示形式,不包括初始 TPM2B 标头,该标头可以从 ASN.1 八位字节字符串长度重建。

privkey 是 TPM2B_PUBLIC 的二进制表示形式,不包括初始 TPM2B 标头,该标头可以从 ASN.1 八位字节字符串长度重建。

DCP Blob 格式

数据协处理器 (DCP) 仅使用其 AES 加密引擎提供硬件绑定的 AES 密钥。它不提供直接的密钥密封/解封。为了使 DCP 硬件加密密钥可用作信任源,我们定义了我们自己的自定义格式,该格式使用硬件绑定的密钥来保护存储在密钥 blob 中的密封密钥。

每当使用 DCP 生成新的可信密钥时,我们都会生成一个随机的 128 位 blob 加密密钥 (BEK) 和 128 位 nonce。 BEK 和 nonce 用于使用 AES-128-GCM 加密可信密钥有效负载。

BEK 本身使用 DCP 的 AES 加密引擎和 AES-128-ECB,使用硬件绑定的密钥进行加密。 加密的 BEK、生成的 nonce、BEK 加密的有效负载和身份验证标签,连同版本号、有效负载长度和身份验证标签一起构成了 blob 格式。

struct dcp_blob_fmt

DCP BLOB 格式。

定义:

struct dcp_blob_fmt {
    __u8 fmt_version;
    __u8 blob_key[AES_KEYSIZE_128];
    __u8 nonce[AES_KEYSIZE_128];
    __le32 payload_len;
    __u8 payload[];
};

成员

fmt_version

格式版本,目前为 1

blob_key

随机 AES 128 密钥,用于加密 payloadblob_key 本身由 DCP 以 AES-128-ECB 模式使用 OTP 或 UNIQUE 设备密钥进行加密。

nonce

用于 payload 加密的随机 nonce。

payload_len

纯文本 payload 的长度。

payload

payload 本身,使用 AES-128-GCM 和 blob_key 加密,大小为 DCP_BLOB_AUTHLEN 的 GCM 身份验证标记附加在其末尾。

描述

DCP BLOB 的总大小为 sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN。