本页提供了一些额外的详细信息和指南,以帮助 KeyMint 硬件抽象层 (HAL) 的实现人员。HAL 的主要文档是 AIDL 接口规范。
API 滥用
调用方可以创建具有有效授权(作为 API 参数)的 KeyMint 密钥,但这些授权会使生成的密钥不安全或无法使用。在这种情况下,KeyMint 实现无需失败或发出诊断。实现不应诊断以下情况:使用过小的密钥、指定不相关的输入参数、重复使用 IV 或 Nonce、生成密钥时未指定目的(因此生成的密钥没有用处),以及类似情况。
应用、框架和 Android 密钥库需负责确保对 KeyMint 模块的调用是合理的,而且是有用的。
addRngEntropy 入口点
addRngEntropy
入口点将调用方提供的熵添加到 KeyMint 实现生成随机数(用作密钥和 IV)所用的池中。
KeyMint 实现需要将收到的熵安全地混合到所使用的池中,该池中还必须包含由硬件随机数生成器在内部生成的熵。对混合操作的处理应该实现:即使攻击者能够完全控制 addRngEntropy
提供的数位或硬件生成的数位(但不能同时控制这两者),他们在预测从熵池生成的数位方面也不具有明显的优势。
密钥特性
创建 KeyMint 密钥的每种机制(generateKey
、importKey
和 importWrappedKey
)都会返回新创建的密钥的特征,并根据强制执行每项特征的安全级别进行适当划分。返回的特征包含为创建密钥而指定的所有参数,但 Tag::APPLICATION_ID
和 Tag::APPLICATION_DATA
除外。如果密钥参数中包含这两个标记,为确保无法通过查看返回的密钥 blob 找到这两个标记的值,系统会将这两个标记从返回的特性中移除。不过,这两个标记以加密形式绑定到密钥 blob,以便在使用相应密钥时,如果未提供正确的值,使用会失败。同样,Tag::ROOT_OF_TRUST
同样会以加密形式绑定到相应密钥,但在生成或导入密钥期间可以不指定此标记,并且在任何情况下都不会返回此标记。
除了收到的标记外,KeyMint 实现还会添加 Tag::ORIGIN
,用于指明密钥的创建方式(KeyOrigin::GENERATED
、KeyOrigin::IMPORTED
或 KeyOrigin::SECURELY_IMPORTED
)。
抗回滚
抗回滚由 Tag::ROLLBACK_RESISTANCE
指示,意味着在使用 deleteKey
或 deleteAllKeys
删除密钥后,安全硬件可确保该密钥永远无法再使用。
KeyMint 实现会将生成或导入的密钥材料作为密钥 Blob(一种经过加密和身份验证的形式)返回给调用程序。当密钥库删除密钥 blob 后,相应密钥将会消失,但之前已设法获取密钥材料的攻击者有可能可以将相应密钥材料恢复到设备上。
如果安全硬件确保被删除的密钥以后无法被恢复,那么相应密钥便可抗回滚。安全硬件通常是通过将额外的密钥元数据存储在攻击者无法操控的可信位置来做到这一点。在移动设备上,用于实现这一点的机制通常为 Replay Protected Memory Block (RPMB)。由于可创建的密钥数量基本上没有限制,而用于抗回滚的可信存储空间的大小则可能有限制,因此,当存储空间已满时,实现可以使创建抗回滚密钥的请求失败。
begin
begin()
入口点使用指定的密钥、针对指定的目的并使用指定的参数(视情况而定)开始执行加密操作。它会返回一个新的 IKeyMintOperation
Binder 对象,用于完成操作。此外,还会返回一个质询值,该值在经过身份验证的操作中用作身份验证令牌的一部分。
KeyMint 实现支持至少 16 个并发操作。密钥库最多使用 15 个,留一个给 vold
用于对密码进行加密。当密钥库有 15 个操作正在进行(已调用 begin()
,但尚未调用 finish
或 abort
)时,如果密钥库收到开始执行第 16 个操作的请求,它会对最近使用最少的操作调用 abort()
,以便将进行中的操作减少到 14 个,然后再调用 begin()
来开始执行新请求的操作。
如果在生成或导入密钥期间指定了 Tag::APPLICATION_ID
或 Tag::APPLICATION_DATA
,那么对 begin()
的调用必须包含这两个标记,标记的值为最初在 params
参数中为此方法指定的值。
错误处理
如果 IKeyMintOperation
上的某个方法返回除 ErrorCode::OK
之外的错误代码,那么相应操作会被中止,操作 Binder 对象也会变为无效。如果以后再使用该对象,则会返回 ErrorCode::INVALID_OPERATION_HANDLE
。
密钥授权强制执行
密钥授权强制执行主要在 begin()
中进行。不过,如果密钥具有一个或多个 Tag::USER_SECURE_ID
值,但没有 Tag::AUTH_TIMEOUT
值,则属于例外情况。
在这种情况下,对于每一项操作,密钥都会要求提供授权,并且 update()
或 finish()
方法会在 authToken
实参中收到身份验证令牌。为确保令牌有效,KeyMint 实现:
- 验证身份验证令牌上的 HMAC 签名。
- 检查令牌是否包含与密钥关联的安全用户 ID。
- 检查令牌的身份验证类型是否与密钥的
Tag::USER_AUTH_TYPE
一致。 - 检查令牌是否包含质询字段中当前操作的质询值。
如果不满足这些条件,KeyMint 会返回 ErrorCode::KEY_USER_NOT_AUTHENTICATED
。
调用方会在每次调用 update()
和 finish()
时提供身份验证令牌。实现只能对令牌验证一次。