本页提供了一些对 Keymaster 硬件抽象层 (HAL) 实现人员很有帮助的详细信息。其中介绍了 API 中的每个函数、提供函数的 Keymaster 版本以及函数的默认实现方法。如需了解标记,请参阅 Keymaster 标记页面。
常规实现准则
以下准则适用于 API 中的所有函数。
输入指针参数
版本:1、2
进行指定调用时不使用的输入指针参数可以是 NULL
。调用方无需提供占位符。例如,某些密钥类型和模式可能不会使用 inParams
参数中的任何值来调用 begin,因此调用方可以将 inParams
设为 NULL
或提供一个空的参数集。调用方也可以提供不使用的参数,而 Keymaster 方法不得发出错误。
如果所需的输入参数为 NULL,Keymaster 方法应返回 ErrorCode::UNEXPECTED_NULL_POINTER
。
从 Keymaster 3 开始,便已没有指针参数。所有参数均通过值或常量引用传递。
输出指针参数
版本:1、2
与输入指针参数类似,不使用的输出指针参数可以是 NULL
。如果某个方法需要在某个输出参数中返回数据,但发现该参数为 NULL
,则应返回 ErrorCode::OUTPUT_PARAMETER_NULL
。
从 Keymaster 3 开始,便已没有指针参数。所有参数均通过值或常量引用传递。
API 滥用
版本:1、2、3
调用程序可以通过多种方式提出虽然不合理或很荒谬但技术上并没有错误的请求。在这种情况下,Keymaster 实现无需失败或发出诊断。实现不应诊断以下情况:使用过小的密钥、指定不相关的输入参数、重复使用 IV 或 Nonce、生成密钥时未指定目的(因此生成的密钥没有用处),以及类似情况。但必须诊断以下情况:缺少必需的参数、指定无效的必需参数,以及类似错误。
应用、框架和 Android 密钥库需负责确保对 Keymaster 模块的调用是合理的,而且是有用的。
函数
getHardwareFeatures
版本:3
全新的 getHardwareFeatures
方法向客户端披露了底层安全硬件的一些重要特性。该方法不接受任何参数,且返回四个值(都是布尔值):
- 如果密钥始终存储在 TEE 等安全硬件中,则
isSecure
为true
。 - 如果硬件支持采用 NIST 曲线(P-224、P-256、P-384 和 P-521)的椭圆曲线加密,则
supportsEllipticCurve
为true
。 - 如果硬件支持对称加密(包括 AES 和 HMAC),则
supportsSymmetricCryptography
为true
。 - 如果硬件支持生成使用注入安全环境中的密钥进行签名的 Keymaster 公钥认证证书,则
supportsAttestation
为true
。
该方法仅可能返回以下错误代码:ErrorCode:OK
、ErrorCode::KEYMASTER_NOT_CONFIGURED
或指示无法与安全硬件通信的错误代码之一。
getHardwareFeatures() generates(bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography, bool supportsAttestation, bool supportsAllDigests, string keymasterName, string keymasterAuthorName);
配置
版本:2
该函数在 Keymaster 2 中引入,并在 Keymaster 3 中被弃用,因为这些信息已在系统属性文件中提供,制造商实现会在启动期间读取这些文件。
配置 Keymaster。该方法在打开设备之后、使用设备之前被调用一次。可使用此方法向 Keymaster 提供 KM_TAG_OS_VERSION 和 KM_TAG_OS_PATCHLEVEL。在该方法被调用之前,所有其他方法会返回 KM_ERROR_KEYMASTER_NOT_CONFIGURED
。该方法提供的值仅在每次启动时被 Keymaster 接受一次。之后的调用都会返回 KM_ERROR_OK
,但不执行任何操作。
如果 Keymaster 实现在安全的硬件中运行,且提供的操作系统版本和补丁程序级别值与引导加载程序向安全硬件提供的值不匹配(或者引导加载程序未提供任何值),该方法会返回 KM_ERROR_INVALID_ARGUMENT
,所有其他方法仍然返回 KM_ERROR_KEYMASTER_NOT_CONFIGURED
。
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
addRngEntropy
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 add_rng_entropy
),并在 Keymaster 3 中重新命名。
该函数将调用方提供的熵添加到 Keymaster 1 实现生成随机数(用作密钥、IV 等)所用的池中。
Keymaster 实现需要将收到的熵安全地混合到所使用的池中,该池中还必须包含由硬件随机数生成器在内部生成的熵。对混合操作的处理应该实现:即使攻击者能够完全控制 addRngEntropy
提供的数位或硬件生成的数位(但不能同时控制这两者),他们在预测从熵池生成的数位方面也不具有明显的优势。
尝试估算内部池中的熵的 Keymaster 实现假定 addRngEntropy
提供的数据不包含熵。如果在单次调用中向 Keymaster 实现提供的数据超过 2 KiB,这些实现可能会返回 ErrorCode::INVALID_INPUT_LENGTH
。
generateKey
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 generate_key
),并在 Keymaster 3 中重新命名。
该函数会生成一个新的加密密钥,同时指定关联的授权,这些授权会永久绑定到该密钥。Keymaster 实现确保无法通过任何与生成密钥时指定的授权不一致的方式使用密钥。对于安全硬件无法强制执行的授权,安全硬件的义务仅限于确保与密钥关联的无法强制执行的授权不能被修改,以便每次调用 getKeyCharacteristics 时都会返回原始值。此外,由 generateKey
返回的特性将授权正确地分配到由硬件强制执行的列表和由软件强制执行的列表。如需了解详情,请参阅 getKeyCharacteristics。
向 generateKey
提供的参数取决于要生成的密钥的类型。本部分总结了每种类型密钥的必需和可选标记。Tag::ALGORITHM 始终为必需的标记,用于指定类型。
RSA 密钥
以下参数是生成 RSA 密钥所必需的参数。
- Tag::KEY_SIZE 用于指定公开模数的大小(以位计)。如果缺少此参数,该方法会返回
ErrorCode::UNSUPPORTED_KEY_SIZE
。支持的值包括 1024、2048、3072 和 4096。最好支持所有为 8 的倍数的密钥大小。 - Tag::RSA_PUBLIC_EXPONENT 用于指定 RSA 公开指数的值。如果缺少此参数,该方法会返回
ErrorCode::INVALID_ARGUMENT
。支持的值包括 3 和 65537。最好支持不超过 2^64 的所有质数值。
以下参数不是生成 RSA 密钥所必需的参数,但如果在缺少这些参数的情况下生成 RSA 密钥,生成的密钥将无法使用。不过,如果缺少这些参数,generateKey
函数不会返回错误。
- Tag::PURPOSE 用于指定允许的目的。对于 RSA 密钥,需要支持采用任意组合的所有目的。
- Tag::DIGEST 用于指定可与新密钥配合使用的摘要算法。不支持任何摘要算法的实现需要接受包含不受支持的摘要的密钥生成请求。不受支持的摘要应被放入“由软件强制执行”的列表内返回的密钥特性中。这是因为相应密钥能够与其他摘要配合使用,但添加摘要会在软件中进行。然后,将调用硬件按
Digest::NONE
摘要算法执行相应操作。 - Tag::PADDING 用于指定可与新密钥配合使用的填充模式。如果指定了任何不受支持的摘要算法,不支持任何摘要算法的实现需要将
PaddingMode::RSA_PSS
和PaddingMode::RSA_OAEP
放入由软件强制执行的密钥特性列表中。
ECDSA 密钥
只有 Tag::KEY_SIZE 是生成 ECDSA 密钥所必需的参数。此参数用于选择 EC 组。支持的值包括 224、256、384 和 521,这些值分别表示 NIST p-224、p-256、p-384 和 p521 曲线。
为了使生成的 ECDSA 密钥可以使用,还需要 Tag::DIGEST,但此参数不是生成 ECDSA 密钥所必需的参数。
AES 密钥
只有 Tag::KEY_SIZE 是生成 AES 密钥所必需的参数。如果缺少此参数,该方法会返回 ErrorCode::UNSUPPORTED_KEY_SIZE
。支持的值包括 128 和 256,可以选择支持 192 位 AES 密钥。
以下参数仅与 AES 密钥相关,但它们并不是生成 AES 密钥所必需的参数:
Tag::BLOCK_MODE
用于指定可与新密钥配合使用的分块模式。Tag::PADDING
用于指定可以使用的填充模式。此参数仅与 ECB 和 CBC 模式相关。
如果指定的是 GCM 分块模式,则提供 Tag::MIN_MAC_LENGTH。如果缺少此参数,该方法会返回 ErrorCode::MISSING_MIN_MAC_LENGTH
。该标记的值为 8 的倍数且介于 96 和 128 之间。
HMAC 密钥
以下参数是生成 HMAC 密钥所必需的参数:
- Tag::KEY_SIZE 用于指定密钥大小(以位计)。不支持小于 64 的值以及不是 8 的倍数的值。支持介于 64 和 512 之间并且是 8 的倍数的所有值。可能会支持更大的值。
- Tag::MIN_MAC_LENGTH 用于指定可通过相应密钥生成或验证的 MAC 的最小长度。此参数的值是 8 的倍数,并且不小于 64。
- Tag::DIGEST 用于指定相应密钥的摘要算法。只能指定一种摘要,否则返回
ErrorCode::UNSUPPORTED_DIGEST
。如果 Trustlet 不支持指定的摘要,则返回ErrorCode::UNSUPPORTED_DIGEST
。
密钥特性
如果特性参数为非 null 值,generateKey
会返回新生成密钥的特性(相应划分到由硬件强制执行的列表和由软件强制执行的列表中)。如需了解哪些特性会划分到哪个列表中,请参阅 getKeyCharacteristics。返回的特性包含为生成密钥而指定的所有参数,但 Tag::APPLICATION_ID 和 Tag::APPLICATION_DATA 除外。如果密钥参数中包含这两个标记,为确保无法通过查看返回的密钥 blob 找到这两个标记的值,系统会将这两个标记从返回的特性中移除。不过,这两个标记以加密形式绑定到密钥 blob,以便在使用相应密钥时,如果未提供正确的值,使用会失败。Tag::ROOT_OF_TRUST 同样会以加密形式绑定到相应密钥,但在生成或导入密钥期间可以不指定此标记,并且在任何情况下都不会返回此标记。
除了收到的标记外,Trustlet 还会添加 Tag::ORIGIN(值为 KeyOrigin::GENERATED
);如果相应密钥可抗回滚,还会添加
抗回滚
抗回滚意味着,在使用 deleteKey 或 deleteAllKeys 删除密钥后,可以通过安全硬件保证绝对无法再使用该等密钥。不具抗回滚功能的实现通常会将生成或导入的密钥材料作为密钥 Blob(一种经过加密和身份验证的形式)返回给调用程序。当密钥库删除密钥 blob 后,相应密钥将会消失,但之前已设法获取密钥材料的攻击者可能能够将相应密钥材料恢复到设备上。
如果安全硬件保证被删除的密钥以后无法被恢复,那么相应密钥便可抗回滚。安全硬件通常是通过将额外的密钥元数据存储在攻击者无法操控的可信位置来做到这一点。在移动设备上,用于实现这一点的机制通常为 Replay Protected Memory Block (RPMB)。由于可创建的密钥数量基本上没有限制,而用于抗回滚的可信存储空间的大小则可能有限制,因此,该方法需要在即使无法为新密钥提供抗回滚功能的情况下同样取得成功。在这种情况下,不应将 Tag::ROLLBACK_RESISTANT 添加到密钥特性中。
getKeyCharacteristics
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 get_key_characteristics
),并在 Keymaster 3 中重新命名。
用于返回与收到的密钥关联的参数和授权,并且返回的参数和授权会划分为两组:一组由硬件强制执行,一组由软件强制执行。此处的说明同样适用于通过 generateKey 和 importKey 返回的密钥特性列表。
如果在生成或导入密钥期间提供了 Tag::APPLICATION_ID
,则在 clientId
参数中为此方法提供相同的值。否则,此方法会返回 ErrorCode::INVALID_KEY_BLOB
。同样,如果在生成或导入密钥期间提供了 Tag::APPLICATION_DATA
,则在 appData
参数中为此方法提供相同的值。
此方法返回的特性完整地说明了指定密钥的类型和用法。
要确定某个指定标记是属于由硬件强制执行的列表,还是属于由软件强制执行的列表,一般规则是:如果该标记的含义完全由安全硬件来保证,则属于由硬件强制执行的列表,否则属于由软件强制执行的列表。下面列出了可能无法明确确定到底属于哪个列表的具体标记:
- Tag::ALGORITHM、Tag::KEY_SIZE 和 Tag::RSA_PUBLIC_EXPONENT 是密钥的固有属性。对于任何由硬件来保障安全的密钥,这些标记都将位于由硬件强制执行的列表中。
- 由安全硬件支持的 Tag::DIGEST 值位于由硬件支持的列表中。不受支持的摘要则位于由软件支持的列表中。
- Tag::PADDING 的值通常位于由硬件支持的列表中,除非存在这样一种可能性,即某种特定的填充模式可能必须要由软件来执行。在这种情况下,这些值将位于由软件强制执行的列表中。如果 RSA 密钥允许使用不是由安全硬件支持的摘要算法进行 PSS 或 OAEP 填充,则存在这种可能性。
- Tag::USER_SECURE_ID 和 Tag::USER_AUTH_TYPE 仅在用户身份验证是由硬件强制执行时才会由硬件强制执行。如需让用户身份验证由硬件强制执行,Keymaster Trustlet 和相关的身份验证 Trustlet 都必须是安全的,且共用一个用于签署和验证身份验证令牌的 HMAC 密钥。如需了解详情,请参阅身份验证页面。
- Tag::ACTIVE_DATETIME、Tag::ORIGINATION_EXPIRE_DATETIME 和 Tag::USAGE_EXPIRE_DATETIME 标记要求能够访问可验证正确无误的挂钟。大多数安全硬件只能访问由非安全操作系统提供的时间信息,这意味着这些标记由软件强制执行。
- 对于绑定到硬件的密钥,Tag::ORIGIN 始终位于硬件列表中。如果此标记出现在硬件列表中,更高的层级便可据此确定相应密钥是由硬件支持的。
importKey
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 import_key
),并在 Keymaster 3 中重新命名。
用于将密钥材料导入到 Keymaster 硬件中。密钥定义参数和输出特性的处理方式与 generateKey
相同,但存在以下例外情况:
- Tag::KEY_SIZE 和 Tag::RSA_PUBLIC_EXPONENT(仅限 RSA 密钥)不是输入参数中必需的标记。如果未收到这两个标记,Trustlet 会根据收到的密钥材料推导出这两个标记的值,并将适当的标记和值添加到密钥特性中。如果收到了这两个参数,Trustlet 会根据密钥材料对其进行验证。如果收到的值与推导出的值不一致,该方法会返回
ErrorCode::IMPORT_PARAMETER_MISMATCH
。 - 返回的 Tag::ORIGIN 与
KeyOrigin::IMPORTED
的值相同。
exportKey
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 export_key
),并在 Keymaster 3 中重新命名。
用于从 Keymaster RSA 密钥对或 EC 密钥对中导出公钥。
如果在生成或导入密钥期间提供了 Tag::APPLICATION_ID
,则在 clientId
参数中为此方法提供相同的值。否则,此方法会返回 ErrorCode::INVALID_KEY_BLOB
。同样,如果在生成或导入密钥期间提供了 Tag::APPLICATION_DATA
,则在 appData
参数中为此方法提供相同的值。
deleteKey
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 delete_key
),并在 Keymaster 3 中重新命名。
用于删除收到的密钥。此方法为可选方法,只能由提供抗回滚功能的 Keymaster 模块来实现。
deleteAllKeys
版本:1、2、3
该函数在 Keymaster 1 中引入(名为 delete_all_keys
),并在 Keymaster 3 中重新命名。
用于删除所有密钥。此方法为可选方法,只能由提供抗回滚功能的 Keymaster 模块来实现。
destroyAttestationIds
版本:3
可以使用 destroyAttestationIds()
方法永久停用新的 ID 认证功能。该功能是一项可选功能,但我们强烈建议您启用。如果 TEE 无法确保在调用此方法后永久停用相应的 ID 认证,则一定不得实现 ID 认证,在这种情况下,此方法不执行任何操作并返回 ErrorCode::UNIMPLEMENTED
。如果支持 ID 认证,则需要实现此方法且必须永久停用未来尝试进行 ID 认证的所有操作。此方法的调用次数不受限制。如果已永久停用 ID 认证,此方法不会执行任何操作并返回 ErrorCode::OK
。
此方法只可能返回以下错误代码:ErrorCode::UNIMPLEMENTED
(如果不支持 ID 认证)、ErrorCode:OK
、ErrorCode::KEYMASTER_NOT_CONFIGURED
或指示无法与安全硬件通信的错误代码之一。
begin
版本:1、2、3
使用指定的密钥和参数(视情况而定)针对指定的目的开始执行加密操作,并返回与 update 和 finish 配合使用来完成操作的操作句柄。该操作句柄还会在经过身份验证的操作中用作“质询”令牌,并且对于此类操作,该操作句柄包含在身份验证令牌的 challenge
字段中。
Keymaster 实现支持至少 16 个并行操作。密钥库最多使用 15 个,留一个给 vold 用于对密码进行加密。当密钥库有 15 个操作正在进行(已调用 begin
,但尚未调用 finish
或 abort
)时,如果密钥库收到开始执行第 16 个操作的请求,它会对最近使用最少的操作调用 abort
,以便将进行中的操作减少到 14 个,然后再调用 begin
来开始执行新请求的操作。
如果在生成或导入密钥期间指定了 Tag::APPLICATION_ID 或 Tag::APPLICATION_DATA,那么调用 begin
时会包含这两个标记,标记的值为最初在 inParams
参数中为此方法指定的值。
密钥授权强制执行
在执行此方法期间,如果实现将以下密钥授权放入到了“由硬件强制执行的”特性中,并且相应操作不是公钥操作,那么这些密钥授权将由 Trustlet 来强制执行。即使不符合授权要求,也必须允许公钥操作(即,使用 RSA 或 EC 密钥进行的 KeyPurpose::ENCRYPT
和 KeyPurpose::VERIFY
)成功完成。
- Tag::PURPOSE:除非请求的操作是公钥操作,否则在
begin()
调用中指定的目的必须与密钥授权中的某个目的一致。如果指定的用途不一致且操作并非公钥操作,begin
会返回ErrorCode::UNSUPPORTED_PURPOSE
。公钥操作是不对称加密或验证操作。 - 只有可信 UTC 时间源可用时才能强制执行 Tag::ACTIVE_DATETIME。如果当前日期和时间早于此标记的值,该方法会返回
ErrorCode::KEY_NOT_YET_VALID
。 - 只有可信 UTC 时间源可用时才能强制执行 Tag::ORIGINATION_EXPIRE_DATETIME。如果当前日期和时间晚于此标记的值,并且目的是
KeyPurpose::ENCRYPT
或KeyPurpose::SIGN
,该方法会返回ErrorCode::KEY_EXPIRED
。 - 只有可信 UTC 时间源可用时才能强制执行 Tag::USAGE_EXPIRE_DATETIME。如果当前日期和时间晚于此标记的值,并且目的是
KeyPurpose::DECRYPT
或KeyPurpose::VERIFY
,该方法会返回ErrorCode::KEY_EXPIRED
。 - Tag::MIN_SECONDS_BETWEEN_OPS 会与指明相应密钥上次使用时间的可信相对计时器进行比较。如果上次使用时间加上此标记的值小于当前时间,该方法会返回
ErrorCode::KEY_RATE_LIMIT_EXCEEDED
。如需查看重要的实现详情,请参阅标记说明。 - Tag::MAX_USES_PER_BOOT 会与用于跟踪自系统启动以来相应密钥使用次数的安全计数器进行比较。如果之前的使用次数已超过此标记的值,该方法会返回
ErrorCode::KEY_MAX_OPS_EXCEEDED
。 - 仅当相应密钥还具有 Tag::AUTH_TIMEOUT 时,此方法才会强制执行 Tag::USER_SECURE_ID。如果相应密钥同时具有这两个标记,此方法必须在
inParams
中收到有效的 Tag::AUTH_TOKEN。若要使身份验证令牌有效,必须满足以下所有条件:- HMAC 字段可正确验证。
- 相应密钥中有至少一个 Tag::USER_SECURE_ID 值与令牌中至少一个安全 ID 值一致。
- 相应密钥具有与令牌中的身份验证类型一致的 Tag::USER_AUTH_TYPE。
如果上述条件中有任何一个不满足,该方法就会返回
ErrorCode::KEY_USER_NOT_AUTHENTICATED
。 - Tag::CALLER_NONCE 允许调用方指定随机数或初始化矢量 (IV)。如果相应密钥没有此标记,但调用方向此方法提供了 Tag::NONCE,则返回
ErrorCode::CALLER_NONCE_PROHIBITED
。 - Tag::BOOTLOADER_ONLY 用于指定相应密钥只能由引导加载程序使用。如果在引导加载程序执行完毕后调用此方法,并且提供的是仅限引导加载程序使用的密钥,则返回
ErrorCode::INVALID_KEY_BLOB
。
RSA 密钥
执行任何 RSA 密钥操作时,都会在 inParams
中指定一种且只能指定一种填充模式。如果未指定或指定了多次,该方法会返回 ErrorCode::UNSUPPORTED_PADDING_MODE
。
RSA 签名和验证操作需要摘要,就像使用 OAEP 填充模式进行 RSA 加密和解密操作一样。在这类情况下,调用方会在 inParams
中指定一种且只能指定一种摘要。如果未指定或指定了多次,该方法会返回 ErrorCode::UNSUPPORTED_DIGEST
。
私钥操作(KeyPurpose::DECYPT
和 KeyPurpose::SIGN
)要求摘要和填充获得授权,也就是说,密钥授权需要包含指定的值。否则,该方法会根据具体情况返回 ErrorCode::INCOMPATIBLE_DIGEST
或 ErrorCode::INCOMPATIBLE_PADDING
。公钥操作(KeyPurpose::ENCRYPT
和 KeyPurpose::VERIFY
)可以使用未经授权的摘要或填充。
除了 PaddingMode::NONE
之外,所有 RSA 填充模式都仅适用于特定目的。具体来说就是,PaddingMode::RSA_PKCS1_1_5_SIGN
和 PaddingMode::RSA_PSS
仅支持签名和验证,而 PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT
和 PaddingMode::RSA_OAEP
仅支持加密和解密。如果指定的模式不支持指定的目的,该方法会返回 ErrorCode::UNSUPPORTED_PADDING_MODE
。
填充模式与摘要之间存在以下非常重要的相互关系:
PaddingMode::NONE
表示执行“原始”RSA 操作。如果是进行签名或验证,则指定Digest::NONE
这种摘要。如果是进行非填充式加密或解密,则不需要摘要。PaddingMode::RSA_PKCS1_1_5_SIGN
填充需要摘要。摘要可以是Digest::NONE
,在这种情况下,Keymaster 实现无法构建适当的 PKCS#1 v1.5 签名结构,因为它无法添加 DigestInfo 结构。不过,实现会构建0x00 || 0x01 || PS || 0x00 || M
,其中 M 是收到的消息,PS 是填充字符串。RSA 密钥的大小必须比消息至少多 11 个字节,否则该方法会返回ErrorCode::INVALID_INPUT_LENGTH
。PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT
填充不需要摘要。PaddingMode::RSA_PSS
填充需要摘要,并且摘要不能是Digest::NONE
。如果指定了Digest::NONE
,该方法会返回ErrorCode::INCOMPATIBLE_DIGEST
。此外,RSA 密钥的大小必须至少比摘要的输出大小多 2 + D 个字节,其中 D 表示摘要的大小(以字节计)。否则,此方法会返回ErrorCode::INCOMPATIBLE_DIGEST
。盐的大小为 D。PaddingMode::RSA_OAEP
填充需要摘要,并且摘要不能是Digest::NONE
。如果指定了Digest::NONE
,该方法会返回ErrorCode::INCOMPATIBLE_DIGEST
。
EC 密钥
执行 EC 密钥操作时,会在 inParams
中指定一种且只能指定一种填充模式。如果未指定或指定了多次,该方法会返回 ErrorCode::UNSUPPORTED_PADDING_MODE
。
私钥操作 (KeyPurpose::SIGN
) 要求摘要和填充获得授权,也就是说,密钥授权需要包含指定的值。否则返回 ErrorCode::INCOMPATIBLE_DIGEST
。公钥操作 (KeyPurpose::VERIFY
) 可以使用未经授权的摘要或填充。
AES 密钥
执行 AES 密钥操作时,会在 inParams
中指定一种且只能指定一种分块模式和填充模式。如果有任何一项未指定或指定了多次,则返回 ErrorCode::UNSUPPORTED_BLOCK_MODE
或 ErrorCode::UNSUPPORTED_PADDING_MODE
。指定的模式必须通过相应密钥授权,否则,该方法会返回 ErrorCode::INCOMPATIBLE_BLOCK_MODE
或 ErrorCode::INCOMPATIBLE_PADDING_MODE
。
如果分块模式是 BlockMode::GCM
,则在 inParams
中指定 Tag::MAC_LENGTH
。指定的值是 8 的倍数,并且不大于 128,也不小于密钥授权中 Tag::MIN_MAC_LENGTH
的值。如果 MAC 长度大于 128 或不是 8 的倍数,则返回 ErrorCode::UNSUPPORTED_MAC_LENGTH
。如果 MAC 长度小于密钥最小长度,则返回 ErrorCode::INVALID_MAC_LENGTH
。
如果分块模式是 BlockMode::GCM
或 BlockMode::CTR
,那么指定的填充模式必须是 PaddingMode::NONE
。如果分块模式是 BlockMode::ECB
或 BlockMode::CBC
,那么指定的填充模式可以是 PaddingMode::NONE
或 PaddingMode::PKCS7
。如果填充模式不符合这些条件,就会返回 ErrorCode::INCOMPATIBLE_PADDING_MODE
。
如果分块模式是 BlockMode::CBC
、BlockMode::CTR
或 BlockMode::GCM
,就需要初始化矢量或随机数。在大多数情况下,调用方都不应提供 IV 或随机数。在这种情况下,Keymaster 实现会生成一个随机 IV 或 Nonce,并通过 outParams
中的 Tag::NONCE 将其返回。CBC 和 CTR IV 均为 16 个字节。GCM 随机数为 12 个字节。如果密钥授权包含 Tag::CALLER_NONCE,调用方可能会通过 inParams
中的 Tag::NONCE 提供 IV 或 Nonce。如果在 Tag::CALLER_NONCE 未获得授权的情况下提供了 Nonce,则会返回 ErrorCode::CALLER_NONCE_PROHIBITED
。如果在 Tag::CALLER_NONCE 获得授权的情况下未提供 Nonce,则会生成随机 IV/Nonce。
HMAC 密钥
执行 HMAC 密钥操作时,会在 inParams
中指定 Tag::MAC_LENGTH
。指定的值必须是 8 的倍数,并且不大于摘要长度,也不小于密钥授权中 Tag::MIN_MAC_LENGTH
的值。如果 MAC 长度大于摘要长度或不是 8 的倍数,则返回 ErrorCode::UNSUPPORTED_MAC_LENGTH
。如果 MAC 长度小于密钥最小长度,则返回 ErrorCode::INVALID_MAC_LENGTH
。
update
版本:1、2、3
用于提供要在通过 begin 开始且正在进行的操作中处理的数据。操作是通过 operationHandle
参数指定的。
为了更灵活地处理缓冲区,此方法的实现可以选择不消耗完收到的数据。调用方负责执行循环操作,以便在后续调用中馈送其余数据。在 inputConsumed
参数中返回所消耗的输入数据量。实现始终消耗至少一个字节,除非相应操作无法再接受更多字节;如果收到了零个以上的字节,但消耗了零字节,调用方会将此视为错误并中止相应操作。
实现还可以选择返回多少数据(作为 update 的结果)。这仅与加密和解密操作有关,因为在调用 finish 之前,签名和验证操作不会返回任何数据。尽早返回数据,而不是缓冲数据。
错误处理
如果此方法返回除 ErrorCode::OK
之外的错误代码,那么相应操作会被中止,操作句柄也会变为无效。如果以后再将该句柄与此方法、finish 或 abort 配合使用,都会返回 ErrorCode::INVALID_OPERATION_HANDLE
。
密钥授权强制执行
密钥授权强制执行主要在 begin 中进行。不过,密钥存在以下情况时例外:
- 有一个或多个 Tag::USER_SECURE_IDs,并且
- 没有 Tag::AUTH_TIMEOUT
在这种情况下,对于每一项操作,密钥都会要求提供授权,并且 update 方法会在 inParams
参数中收到 Tag::AUTH_TOKEN。HMAC 会验证该令牌有效且包含匹配的安全用户 ID,与密钥的 Tag::USER_AUTH_TYPE 匹配,并且包含质询字段中当前操作的操作句柄。如果不符合这些条件,就会返回 ErrorCode::KEY_USER_NOT_AUTHENTICATED
。
调用方会在每次调用 update 和 finish 时提供身份验证令牌。实现只需对该令牌验证一次(如果它倾向于这么做)。
RSA 密钥
对于使用 Digest::NONE
的签名和验证操作,此方法会在单次 update 中接受要签署或验证的整个分块。此方法不会只消耗分块的一部分。不过,如果调用方选择在多次 update 中提供数据,此方法会接受相应数据。如果调用方提供的要签署的数据多于可以消耗的数据(数据长度超出 RSA 密钥大小),则返回 ErrorCode::INVALID_INPUT_LENGTH
。
ECDSA 密钥
对于使用 Digest::NONE
的签名和验证操作,此方法会在单次 update 中接受要签署或验证的整个分块。此方法不会只消耗分块的一部分。
不过,如果调用程序选择在多次 update 中提供数据,此方法会接受相应数据。如果调用程序提供的要签署的数据多于可以消耗的数据,则以静默方式截断这些数据。(这与处理在类似 RSA 操作中提供的超量数据不同,因为此方法与旧版客户端兼容。)
AES 密钥
AES GCM 模式支持通过 inParams
参数中的 Tag::ASSOCIATED_DATA 标记提供的“相关身份验证数据”。可以在重复调用(如果数据太大而无法在单个分块中发送,那么重复调用非常重要)中提供相关数据,但始终先于要加密或解密的数据提供。update 调用可以同时接收相关数据以及要加密/解密的数据,但后续 update 中不会包含相关数据。如果调用方已在某次调用 update 时提供了要加密/解密的数据,若再次向 update 调用提供相关数据,就会返回 ErrorCode::INVALID_TAG
。
对于 GCM 加密,此标记会通过 finish 附加到密文中。在解密期间,向最后一次 update 调用提供的数据的最后 Tag::MAC_LENGTH
个字节就是此标记。由于特定的 update 调用无法得知自己是否为最后一次调用,因此它会处理除标记长度之外的所有数据,并缓冲调用 finish 期间可能用到的标记数据。
完成
版本:1、2、3
用于完成通过 begin 开始且正在进行的操作,负责处理 update 所提供的所有尚未处理的数据。
此方法是操作期间调用的最后一个方法,因此会返回所有处理后的数据。
无论是成功完成还是返回错误,此方法都会结束相应操作,从而使收到的操作句柄无效。如果以后再将该句柄与此方法、update 或 abort 配合使用,都会返回 ErrorCode::INVALID_OPERATION_HANDLE
。
签名操作将返回签名作为输出。验证操作将接受 signature
参数中的签名,并且不会返回任何输出。
密钥授权强制执行
密钥授权强制执行主要在 begin 中进行。不过,密钥存在以下情况时例外:
- 一个或多个 Tag::USER_SECURE_IDs,且
- 没有 Tag::AUTH_TIMEOUT
在这种情况下,对于每一项操作,密钥都会要求提供授权,并且 update 方法会在 inParams
参数中收到 Tag::AUTH_TOKEN。HMAC 会验证该令牌有效且包含匹配的安全用户 ID,与密钥的 Tag::USER_AUTH_TYPE 匹配,并且包含质询字段中当前操作的操作句柄。如果不符合这些条件,则返回 ErrorCode::KEY_USER_NOT_AUTHENTICATED
。
调用方会在每次调用 update 和 finish 时提供身份验证令牌。实现只需对该令牌验证一次(如果它倾向于这么做)。
RSA 密钥
有一些附加要求,具体取决于填充模式:
PaddingMode::NONE
:对于非填充式签名和加密操作,如果收到的数据比密钥短,那么在签名/加密之前,在数据左侧填充零来补齐。如果数据与密钥一样长度,但数值较大,则返回ErrorCode::INVALID_ARGUMENT
。对于验证和解密操作,数据必须与密钥一样长。否则返回ErrorCode::INVALID_INPUT_LENGTH.
。PaddingMode::RSA_PSS
:对于 PSS 填充式签名操作,PSS 盐是消息摘要的大小,并且是随机生成的。 调用 begin 时使用inputParams
中的 Tag::DIGEST 指定的摘要会用作 PSS 摘要算法以及 MGF1 摘要算法。PaddingMode::RSA_OAEP
:调用 begin 时使用inputParams
中的 Tag::DIGEST 指定的摘要会用作 OAEP 摘要算法,而 SHA1 会用作 MGF1 摘要算法。
ECDSA 密钥
如果为非填充式签名或验证操作提供的数据太长,则要将其截断。
AES 密钥
有一些附加条件,具体取决于分块模式:
BlockMode::ECB
或BlockMode::CBC
:如果填充模式是PaddingMode::NONE
,并且数据长度不是 AES 分块大小的倍数,则返回ErrorCode::INVALID_INPUT_LENGTH
。如果填充模式是PaddingMode::PKCS7
,则按照 PKCS#7 规范填充数据。请注意,PKCS#7 建议,如果数据长度是分块长度的倍数,则添加一个额外的填充分块。BlockMode::GCM
:在加密期间,处理所有明文之后,会计算此标记(Tag::MAC_LENGTH 个字节)并将其附加到返回的密文中。在解密期间,会将最后 Tag::MAC_LENGTH 个字节作为标记处理。如果标记验证失败,则返回ErrorCode::VERIFICATION_FAILED
。
abort
版本:1、2、3
用于中止正在进行的操作。在调用 abort 之后,如果后续再将收到的操作句柄与 update、finish 或 abort 配合使用,都会返回 ErrorCode::INVALID_OPERATION_HANDLE
。
get_supported_algorithms
版本:1
用于返回一个列表,其中包含 Keymaster 硬件实现支持的算法。如果是软件实现,则返回一个空列表;如果是混合实现,则返回一个仅包含硬件支持的算法的列表。
Keymaster 1 实现支持 RSA、EC、AES 和 HMAC。
get_supported_block_modes
版本:1
用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的 AES 分块模式。
对于不是分块加密算法的 RSA、EC 和 HMAC,无论是任何有效目的,此方法都会返回一个空列表。如果目的无效,应导致此方法返回 ErrorCode::INVALID_PURPOSE
。
Keymaster 1 实现支持使用 ECB、CBC、CTR 和 GCM 进行 AES 加密和解密。
get_supported_padding_modes
版本:1
用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的填充模式。
HMAC 和 EC 并没有填充这一概念,因此针对所有有效目的,此方法都会返回一个空列表。如果目的无效,应导致此方法返回 ErrorCode::INVALID_PURPOSE
。
对于 RSA,Keymaster 1 实现支持:
- 非填充式加密、解密、签名和验证。对于非填充式加密和签名,如果消息比公开模数短,实现必须要在消息左侧填充零来补齐。对于非填充式解密和验证,输入长度必须与公开模数的大小一致。
- PKCS#1 v1.5 加密和签名填充模式
- 盐最小长度为 20 的 PSS
- OAEP
对于采用 ECB 和 CBC 模式的 AES 算法,Keymaster 1 实现支持无填充和 PKCS#7 填充。CTR 和 GCM 模式仅支持无填充。
get_supported_digests
版本:1
用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的摘要模式。
任何 AES 模式都不支持摘要,也不需要摘要,因此无论是任何有效目的,此方法都会返回一个空列表。
Keymaster 1 实现可以只实现一部分已定义的摘要。实现会提供 SHA-256,且可以提供 MD5、SHA1、SHA-224、SHA-256、SHA384 和 SHA512(完整的已定义摘要集)。
get_supported_import_formats
版本:1
用于返回一个列表,其中包含指定算法的 Keymaster 硬件实现支持的导入格式。
Keymaster 1 实现支持 PKCS#8 格式(无密码保护),以便导入 RSA 密钥对和 EC 密钥对,并且支持以原始格式导入 AES 密钥材料和 HMAC 密钥材料。
get_supported_export_formats
版本:1
用于返回一个列表,其中包含指定算法的 Keymaster 硬件实现支持的导出格式。
Keymaster1 实现支持 X.509 格式,以便导出 RSA 公钥和 EC 公钥。不支持导出私钥或非对称密钥。
历史函数
Keymaster 0
以下函数属于最初的 Keymaster 0 定义。它们出现在 Keymaster 1 结构 keymaster1_device_t 中。不过,Keymaster 1.0 中并没有实现这些函数,且它们的函数指针设为了 NULL。
generate_keypair
import_keypair
get_keypair_public
delete_keypair
delete_all
sign_data
Verify_data
Keymaster 1
以下函数属于 Keymaster 1 定义,但在 Keymaster 2 中已被移除,一同被移除的还有上述 Keymaster 0 函数。
get_supported_algorithms
get_supported_block_modes
get_supported_padding_modes
get_supported_digests
get_supported_import_formats
get_supported_export_formats
Keymaster 2
以下函数属于 Keymaster 2 定义,但在 Keymaster 3 中已被移除,一同被移除的还有上述 Keymaster 1 函数。
configure