本页中提供了一些对 Keymaster HAL 实现人员很有帮助的详细信息,其中介绍了 HAL 中的每个标记和每个函数。

授权标记

除非标记说明中另有注明,否则以下所有标记都是在密钥生成期间用于指定密钥特性。

KM_TAG_PURPOSE

用于指定相应密钥可用于哪些目的。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_PURPOSE_ENCRYPT = 0,
    KM_PURPOSE_DECRYPT = 1,
    KM_PURPOSE_SIGN = 2,
    KM_PURPOSE_VERIFY = 3,
} keymaster_purpose_t;

此标记可重复使用。可以生成具有多个值的密钥,不过一项操作只有一个目的。当调用 begin 函数来启动某项操作时,要指定操作的目的。如果为操作指定的目的未通过相应密钥授权,操作必须失败并显示 KM_ERROR_INCOMPATIBLE_PURPOSE

KM_TAG_ALGORITHM

用于指定与相应密钥配合使用的加密算法。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_ALGORITHM_RSA = 1,
    KM_ALGORITHM_EC = 3,
    KM_ALGORITHM_AES = 32,
    KM_ALGORITHM_HMAC = 128,
} keymaster_algorithm_t;

此标记不可重复使用。

KM_TAG_KEY_SIZE

用于指定相应密钥的大小(以位数计,按适用于相应密钥算法的一般方式衡量)。例如,对于 RSA 密钥,KM_TAG_KEY_SIZE 用于指定公开模数的大小。对于 AES 密钥,此标记用于指定密钥私密材料的长度。

此标记不可重复使用。

KM_TAG_BLOCK_MODE

用于指定可与相应密钥配合使用的分块加密模式。此标记仅与 AES 密钥有关。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_MODE_ECB = 1,
    KM_MODE_CBC = 2,
    KM_MODE_CTR = 3,
    KM_MODE_GCM = 32,
} keymaster_block_mode_t;

此标记可重复使用。对于 AES 密钥操作,必须要在 beginadditional_params 参数中指定模式。如果指定的模式不在相应密钥的关联模式之列,操作必须失败并显示 KM_ERROR_INCOMPATIBLE_BLOCK_MODE

KM_TAG_DIGEST

用于指定可与相应密钥配合使用以执行签名和验证操作的摘要算法。此标记与 RSA 密钥、ECDSA 密钥和 HMAC 密钥有关。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_DIGEST_NONE = 0,
    KM_DIGEST_MD5 = 1,
    KM_DIGEST_SHA1 = 2,
    KM_DIGEST_SHA_2_224 = 3,
    KM_DIGEST_SHA_2_256 = 4,
    KM_DIGEST_SHA_2_384 = 5,
    KM_DIGEST_SHA_2_512 = 6,
}
keymaster_digest_t;

此标记可重复使用。对于签名和验证操作,必须要在 beginadditional_params 参数中指定摘要。如果指定的摘要不在相应密钥的关联摘要之列,操作必须失败并显示 KM_ERROR_INCOMPATIBLE_DIGEST

KM_TAG_PADDING

用于指定可与相应密钥配合使用的填充模式。此标记与 RSA 密钥和 AES 密钥有关。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_PAD_NONE = 1,
    KM_PAD_RSA_OAEP = 2,
    KM_PAD_RSA_PSS = 3,
    KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4,
    KM_PAD_RSA_PKCS1_1_5_SIGN = 5,
    KM_PAD_PKCS7 = 64,
} keymaster_padding_t;

KM_PAD_RSA_OAEPKM_PAD_RSA_PKCS1_1_5_ENCRYPT 仅用于 RSA 加密/解密密钥,分别用来指定 RSA PKCS#1v2 OAEP 填充和 RSA PKCS#1 v1.5 随机填充。KM_PAD_RSA_PSSKM_PAD_RSA_PKCS1_1_5_SIGN 仅用于 RSA 签名/验证密钥,分别用来指定 RSA PKCS#1v2 PSS 填充和 RSA PKCS#1 v1.5 确定性填充。另外请注意,RSA PSS 填充模式与 KM_DIGEST_NONE 不兼容。

KM_PAD_NONE 可与 RSA 密钥或 AES 密钥配合使用。对于 AES 密钥,如果将 KM_PAD_NONE 与分块模式 ECB 或 CBC 配合使用,并且要加密或解密的数据的长度不是 AES 分块大小的倍数,调用 finish 必须失败并显示 KM_ERROR_INVALID_INPUT_LENGTH

KM_PAD_PKCS7 只能与 AES 密钥以及 ECB 和 CBC 模式配合使用。

此标记可重复使用。在调用 begin 时必须指定填充模式。如果指定的模式未针对相应密钥获得授权,操作必须失败并显示 KM_ERROR_INCOMPATIBLE_BLOCK_MODE

KM_TAG_CALLER_NONCE

用于指定调用程序可以为需要随机数的操作提供随机数。

此标记为布尔值,因此可能的值为 true(如果此标记存在)和 false(如果此标记不存在)。

此标记仅用于 AES 密钥,并且仅与 CBC、CTR 和 GCM 分块模式有关。如果此标记不存在,实现应拒绝执行向 begin 提供 KM_TAG_NONCE 的所有操作,并显示 KM_ERROR_CALLER_NONCE_PROHIBITED

此标记不可重复使用。

KM_TAG_MIN_MAC_LENGTH

此标记是支持 GCM 模式的 HMAC 密钥和 AES 密钥所必需的标记,用于指定可通过相应密钥请求或验证的 MAC 的最小长度。

此标记的值是 MAC 的最小长度(以位数计)。这个值必须是 8 的倍数。对于 HMAC 密钥,这个值不得小于 64。对于 GCM 密钥,这个值必须介于 96 到 128 之间。

KM_TAG_RSA_PUBLIC_EXPONENT

用于为 RSA 密钥对指定公开指数的值。此标记仅与 RSA 密钥有关,而且是所有 RSA 密钥都必需的标记。

此标记的值是一个 64 位的未签名整数,并且必须符合 RSA 公开指数方面的要求。由于这个值是由调用程序指定的,因此无法由实现来选择。这个值必须是质数。Trustlet 必须要支持 2^16+1 这个值,而且最好还支持其他合理的值,尤其是 3。如果未指定指数或指定的指数不受支持,密钥生成操作必须失败并显示 KM_ERROR_INVALID_ARGUMENT

此标记不可重复使用。

KM_TAG_BLOB_USAGE_REQUIREMENTS

用于指定必须满足哪些系统环境条件才能使用生成的密钥。

可能的值是通过以下枚举定义的:

typedef enum {
    KM_BLOB_STANDALONE = 0,
    KM_BLOB_REQUIRES_FILE_SYSTEM = 1,
} keymaster_key_blob_usage_requirements_t;

可以在密钥生成期间指定此标记,以便要求只有在指定条件下才可以使用生成的密钥。此标记必须要和密钥特性一起返回(通过 generate_keyget_key_characteristics)。如果调用程序指定了 KM_TAG_BLOB_USAGE_REQUIREMENTS(值为 KM_BLOB_STANDALONE),Trustlet 必须返回一个可在没有文件系统支持的情况下使用的密钥 Blob。这对于具有已加密磁盘的设备至关重要:在使用 Keymaster 密钥解密磁盘之前,此类设备的文件系统可能一直无法使用。

此标记不可重复使用。

KM_TAG_BOOTLOADER_ONLY

用于指定只有引导加载程序能够使用相应密钥。

此标记为布尔值,因此可能的值为 true(如果此标记存在)和 false(如果此标记不存在)。

尝试从 Android 系统使用带有 KM_TAG_BOOTLOADER_ONLY 标记的密钥时,操作必须失败并显示 KM_ERROR_INVALID_KEY_BLOB

此标记不可重复使用。

KM_TAG_ACTIVE_DATETIME

用于指定相应密钥变为有效状态的日期和时间。在此之前,尝试使用相应密钥时,操作必须失败并显示 KM_ERROR_KEY_NOT_YET_VALID

此标记的值是一个 64 位的整数,表示距 1970 年 1 月 1 日的毫秒数。

此标记不可重复使用。

KM_TAG_ORIGINATION_EXPIRE_DATETIME

用于指定相应密钥无法再用于签名和加密目的的过期日期和时间。如果向 begin 提供的目的是 KM_PURPOSE_SIGNKM_PURPOSE_ENCRYPT,那么在此之后,尝试使用相应密钥时,操作必须失败并显示 KM_ERROR_KEY_EXPIRED

此标记的值是一个 64 位的整数,表示距 1970 年 1 月 1 日的毫秒数。

此标记不可重复使用。

KM_TAG_USAGE_EXPIRE_DATETIME

用于指定相应密钥无法再用于验证和解密目的的过期日期和时间。如果向 begin 提供的目的是 KM_PURPOSE_VERIFYKM_PURPOSE DECRYPT,那么在此之后,尝试使用相应密钥时,操作必须失败并显示 KM_ERROR_KEY_EXPIRED

此标记的值是一个 64 位的整数,表示距 1970 年 1 月 1 日的毫秒数。

此标记不可重复使用。

KM_TAG_MIN_SECONDS_BETWEEN_OPS

用于指定至少必须间隔多长时间才能再次将密钥用于允许的操作。在不限制密钥使用次数可能会给暴力破解攻击以可乘之机的环境中,可以使用此标记来限制密钥的使用次数。

此标记的值是一个 32 位的整数,表示允许的操作之间间隔的秒数。

当有操作使用带有此标记的某个密钥时,计时器应在 finishabort 调用期间启动。在计时器表明通过 KM_TAG_MIN_SECONDS_BETWEEN_OPS 指定的间隔时间已过去之前收到的所有 begin 调用都必须失败并显示 KM_ERROR_KEY_RATE_LIMIT_EXCEEDED。这项要求意味着 Trustlet 必须要为带有此标记的密钥维护一份计时器表格。由于 Keymaster 内存的大小通常有限制,因此该表格可以具有固定的最大大小,并且当该表格被占满时,如果有操作尝试使用带有此标记的密钥,Keymaster 可以使这些操作失败。该表格必须能够容纳至少 32 个使用中的密钥,而且当密钥最小使用间隔到期时,必须主动重复使用该表格中的位置。如果某项操作因该表格已被占满而失败,Keymaster 应返回 KM_ERROR_TOO_MANY_OPERATIONS

此标记不可重复使用。

KM_TAG_MAX_USES_PER_BOOT

用于指定在两次系统重启之间可以使用相应密钥的最大次数。这是另一种限制密钥使用次数的机制。

此标记的值是一个 32 位的整数,表示在每次系统启动后可以使用相应密钥的次数。

当有操作使用带有此标记的某个密钥时,与该密钥关联的计数器应在 begin 调用期间递增。当密钥计数器超出此标记的值后,所有尝试使用该密钥的后续操作都必须失败并显示 KM_ERROR_MAX_OPS_EXCEEDED,直到设备重启为止。这项要求意味着 Trustlet 必须要为带有此标记的密钥维护一份使用次数计数器表格。由于 Keymaster 内存的大小通常有限制,因此该表格可以具有固定的最大大小,并且当该表格被占满时,如果有操作尝试使用带有此标记的密钥,Keymaster 可以使这些操作失败。该表格必须能够容纳至少 16 个密钥。如果某项操作因该表格已被占满而失败,Keymaster 应返回 KM_ERROR_TOO_MANY_OPERATIONS

此标记不可重复使用。

KM_TAG_USER_SECURE_ID

用于指定只能在某个安全的用户身份验证状态下使用相应密钥。此标记与 KM_TAG_NO_AUTH_REQUIRED 互斥。

此标记的值是一个 64 位的整数,用于指定在通过 KM_TAG_AUTH_TOKENbegin 提供的身份验证令牌中必须存在哪个身份验证政策状态值,身份验证程序才会授权使用相应密钥。如果在调用 begin 时未提供身份验证令牌,或提供的身份验证令牌没有匹配的政策状态值,但所用密钥带有此标记,该调用必须失败。

此标记可重复使用。如果提供的值中有任何一个与身份验证令牌中的任何政策状态值一致,身份验证程序即会授权使用相应密钥。否则,操作必须失败并显示 KM_ERROR_KEY_USER_NOT_AUTHENTICATED

KM_TAG_NO_AUTH_REQUIRED

用于指定无需进行身份验证即可使用相应密钥。此标记与 KM_TAG_USER_SECURE_ID 互斥。

此标记为布尔值,因此可能的值为 true(如果此标记存在)和 false(如果此标记不存在)。

此标记不可重复使用。

KM_TAG_USER_AUTH_TYPE

用于指定可以使用哪些类型的用户身份验证程序来授权使用相应密钥。请求 Keymaster 执行所用密钥带有此标记的操作时,必须要为 Keymaster 提供一个身份验证令牌,并且该令牌的 authenticator_type 字段必须与此标记中的值一致。准确来说就是,(ntoh(token.authenticator_type) & auth_type_tag_value) != 0 必须为 true,其中 ntoh 是一个函数,用于将按网络字节序保存的整数转换成按主机字节序保存的整数,而 auth_type_tag_value 是此标记的值。

此标记的值是以下枚举值的位掩码(32 位整数):

typedef enum {
    HW_AUTH_NONE = 0,
    HW_AUTH_PASSWORD = 1 << 0,
    HW_AUTH_FINGERPRINT = 1 << 1,
    // Additional entries should be powers of 2.
    HW_AUTH_ANY = UINT32_MAX,
} hw_authenticator_type_t;

此标记不可重复使用。

KM_TAG_AUTH_TIMEOUT

用于指定授权在多长时间内使用相应密钥(以秒数计,从通过身份验证开始算起)。如果 KM_TAG_USER_SECURE_ID 存在而此标记不存在,那么每次使用相应密钥时都需要通过身份验证(要详细了解各项操作的身份验证流程,请参阅 begin)。

此标记的值是一个 32 位的整数,用于指定可在多长时间内使用相应密钥(以秒数计,从使用通过 KM_TAG_USER_AUTH_TYPE 指定的身份验证方法对通过 KM_TAG_USER_SECURE_ID 指定的用户成功进行身份验证开始算起)。

此标记不可重复使用。

KM_TAG_ALL_APPLICATIONS

预留标记,供将来使用。

此标记不可重复使用。

KM_TAG_APPLICATION_ID

当提供给 generate_keyimport_key 时,此标记用于指定使用相应密钥时必须要提供的数据。具体来说就是,调用 export_keyget_key_characteristics 时必须要在 client_id 参数中提供相同的值,而调用 begin 时则必须要提供此标记以及相同的相关数据(作为 in_params 集的一部分)。如果未收到正确的数据,函数必须返回 KM_ERROR_INVALID_KEY_BLOB

此标记的内容必须要以加密形式绑定到相应密钥,这意味着,如果有不轨人士有权访问安全域的所有机密内容,但无权访问此标记的内容,必须要确保他们无法解密相应密钥(在不对此标记的内容进行暴力破解攻击的情况下)。

此标记的值是一个 Blob(任意长度的字节数数组)。

此标记不可重复使用。

KM_TAG_APPLICATION_DATA

当提供给 generate_keyimport_key 时,此标记用于指定使用相应密钥时必须要提供的数据。具体来说就是,调用 export_keyget_key_characteristics 时必须要在 client_id 参数中提供相同的值,而调用 begin 时则必须要提供此标记以及相同的相关数据(作为 in_params 集的一部分)。如果未收到正确的数据,函数必须返回 KM_ERROR_INVALID_KEY_BLOB

此标记的内容必须要以加密形式绑定到相应密钥,这意味着,如果有不轨人士有权访问安全域的所有机密内容,但无权访问此标记的内容,必须要确保他们无法解密相应密钥(在不对此标记的内容进行暴力破解攻击的情况下)。

此标记的值是一个 Blob(任意长度的字节数数组)。

此标记不可重复使用。

KM_TAG_CREATION_DATETIME

用于指定相应密钥的创建日期和时间(以距 1970 年 1 月 1 日的毫秒数计)。此标记为可选标记,仅供参考。

此标记不可重复使用。

KM_TAG_ORIGIN

用于指定相应密钥是在哪里创建的(如果知道)。在生成或导入密钥期间可以不指定此标记,但此标记必须要由 Trustlet 添加到密钥特性中。

可能的值是在 keymaster_origin_t 中定义的:

typedef enum {
    KM_ORIGIN_GENERATED = 0,
    KM_ORIGIN_IMPORTED = 2,
    KM_ORIGIN_UNKNOWN = 3,
} keymaster_key_origin_t

此标记的值的完整含义不仅取决于值本身,还取决于值是位于由硬件强制执行的特性列表中,还是位于由软件强制执行的特性列表中。

KM_ORIGIN_GENERATED 表示相应密钥是由 Keymaster 生成的。如果它位于由硬件强制执行的列表中,那么相应密钥是在安全硬件中生成的,并且已永久绑定到硬件。如果它位于由软件强制执行的列表中,那么相应密钥是在 SoftKeymaster 中生成的,并且没有绑定到硬件。

KM_ORIGIN_IMPORTED 表示相应密钥是在 Keymaster 之外生成的,并且导入到了 Keymaster 中。如果它位于由硬件强制执行的列表中,那么相应密钥已永久绑定到硬件,不过可能存在位于安全硬件之外的副本。如果它位于由软件强制执行的列表中,那么相应密钥已导入到 SoftKeymaster 中,并且没有绑定到硬件。

KM_ORIGIN_UNKNOWN 应当仅出现在由硬件强制执行的列表中。它表示相应密钥已绑定到硬件,但不知道相应密钥原本就是在安全硬件中生成的,还是导入的。只有在使用 keymaster0 硬件模拟 keymaster1 服务时,才会出现这种情况。

此标记不可重复使用。

KM_TAG_ROLLBACK_RESISTANT

用于表明相应密钥可抗回滚,也就是说,当通过 delete_keydelete_all_keys 删除相应密钥后,可保证相应密钥已被永久删除且无法再使用。如果密钥不带此标记,那么在被删除后,可能能够从备份中恢复。

此标记为布尔值,因此可能的值为 true(如果此标记存在)和 false(如果此标记不存在)。

此标记不可重复使用。

KM_TAG_ROOT_OF_TRUST

用于指定“信任根”,即经过验证的启动程序在验证操作系统是否已启动时使用的键(如果有)。在任何情况下,都不可以通过密钥特性将此标记提供给 Keymaster,也不可以通过密钥特性从 Keymaster 返回此标记。

KM_TAG_ASSOCIATED_DATA

用于提供进行 AES-GCM 加密或解密时使用的“相关数据”。可以将此标记提供给 update,以便指定在计算 GCM 标记时使用的未加密/解密的数据。

此标记的值是一个 Blob(任意长度的字节数数组)。

此标记不可重复使用。

KM_TAG_NONCE

用于提供或返回进行 AES GCM、CBC 或 CTR 加密/解密时使用的随机数或初始化矢量 (IV)。在加密和解密操作期间,可以将此标记提供给 begin。仅当相应密钥带有 KM_TAG_CALLER_NONCE 时,才可以将此标记提供给 begin。如果调用程序未提供此标记,Keymaster 将随机生成适当的随机数或 IV 并通过 begin 将其返回。

此标记的值是一个 Blob(任意长度的字节数数组)。所允许的长度取决于模式:GCM 随机数的长度为 12 个字节;CBC IV 和 CTR IV 的长度为 16 个字节。

此标记不可重复使用。

KM_TAG_AUTH_TOKEN

用于向 beginupdatefinish 提供身份验证令牌(请参阅“身份验证”页面),以便向要求用户通过身份验证的密钥操作(密钥带有 KM_TAG_USER_SECURE_ID)证明相应用户已通过身份验证。

此标记的值是一个包含 hw_auth_token_t 结构的 Blob。

此标记不可重复使用。

KM_TAG_MAC_LENGTH

用于提供 MAC 或 GCM 身份验证标记的请求长度(以位数计)。

此标记的值是 MAC 长度(以位数计)。这个值必须是 8 的倍数,并且不得小于与相应密钥关联的 KM_TAG_MIN_MAC_LENGTH 的值。

函数

已弃用的函数

虽然以下函数位于 keymaster1_device_t 定义中,但不应实现这些函数。这些函数的指针应设为 NULL

常规实现准则

以下准则适用于 API 中的所有函数。

输入指针参数

进行指定调用时不使用的输入指针参数可以是 NULL。调用程序无需提供占位符。例如,某些密钥类型和模式可能不会使用 beginin_params 参数中的任何值,因此调用程序可以将 in_params 设为 NULL 或提供一个空的参数集。调用程序也可以提供不使用的参数,而 Keymaster 方法不得发出错误。

如果所需的输入参数为 NULL,Keymaster 方法应返回 KM_ERROR_UNEXPECTED_NULL_POINTER

输出指针参数

与输入指针参数类似,不使用的输出指针参数可以是 NULL。如果某个方法需要在某个输出参数中返回数据,但发现该参数为 NULL,则应返回 KM_ERROR_OUTPUT_PARAMETER_NULL

API 滥用

调用程序可以通过多种方式提出虽然不合理或很荒谬但技术上并没有错误的请求。在这种情况下,keymaster1 实现无需失败或发出诊断。实现不应诊断以下情况:使用过小的密钥、指定不相关的输入参数、重复使用 IV 或随机数、生成密钥时未指定目的(因此生成的密钥没有用处),以及类似情况。但必须诊断以下情况:缺少必需的参数、指定无效的必需参数,以及类似错误。

应用、框架和 Android Keystore 需负责确保对 Keymaster 模块的调用是合理的,而且是有用的。

get_supported_algorithms

用于返回一个列表,其中包含 Keymaster 硬件实现支持的算法。如果是软件实现,则必须返回一个空列表;如果是混合实现,则必须返回一个仅包含硬件支持的算法的列表。

keymaster1 实现必须要支持 RSA、EC、AES 和 HMAC。

get_supported_block_modes

用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的 AES 分块模式。

对于不是分块加密算法的 RSA、EC 和 HMAC,无论是任何有效目的,此方法都必须返回一个空列表。如果目的无效,则应导致此方法返回 KM_ERROR_INVALID_PURPOSE

keymaster1 实现必须要支持使用 ECB、CBC、CTR 和 GCM 进行 AES 加密和解密。

get_supported_padding_modes

用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的填充模式。

HMAC 和 EC 并没有填充这一概念,因此针对所有有效目的,此方法都必须返回一个空列表。如果目的无效,则应导致此方法返回 KM_ERROR_INVALID_PURPOSE

对于 RSA,keymaster1 实现必须要支持:

对于采用 ECB 和 CBC 模式的 AES 算法,keymaster1 实现必须要支持无填充和 PKCS#7 填充。CTR 和 GCM 模式必须仅支持无填充。

get_supported_digests

用于返回一个列表,其中包含对于指定的算法和目的,Keymaster 硬件实现支持的摘要模式。

任何 AES 模式都不支持摘要,也不需要摘要,因此无论是任何有效目的,此方法都必须返回一个空列表。

keymaster1 实现可以只实现一部分已定义的摘要,但必须要提供 SHA-256。强烈建议 keymaster1 实现提供 MD5、SHA1、SHA-224、SHA-256、SHA384 和 SHA512(完整的已定义摘要集)。

get_supported_import_formats

用于返回一个列表,其中包含指定算法的 Keymaster 硬件实现支持的导入格式。

keymaster1 实现必须要支持 PKCS#8 格式(无密码保护),以便导入 RSA 密钥对和 EC 密钥对,并且必须要支持以原始格式导入 AES 密钥材料和 HMAC 密钥材料。

get_supported_export_formats

用于返回一个列表,其中包含指定算法的 Keymaster 硬件实现支持的导出格式。

keymaster1 实现必须要支持 X.509 格式,以便导出 RSA 公钥和 EC 公钥。不得支持导出私钥或非对称密钥。

add_rng_entropy

用于将调用程序提供的熵添加到 keymaster1 实现生成随机数(在密钥中使用)、IV 以及其他内容时使用的池中。

Keymaster1 实现必须将收到的熵安全地混合到所使用的池中,该池中必须还要包含由硬件随机数生成器在内部生成的熵。混合操作必须具有以下特性:即使攻击者能够完全控制通过 add_rng_entropy 提供的位数或硬件生成的位数(但不能同时控制这两者),他们能够预测出通过熵池生成的位数的概率也不得超过 ½。

尝试估算内部池中的熵的 keymaster1 实现必须假定通过 add_rng_entropy 提供的数据不包含熵。

generate_key

用于生成一个新的加密密钥,同时指定将永久绑定到该密钥的关联授权。keymaster1 实现必须能够确保无法通过任何与生成密钥时指定的授权不一致的方式使用相应密钥。对于安全硬件无法强制执行的授权,安全硬件的义务仅限于确保与相应密钥关联的无法强制执行的授权不能被修改,以便每次调用 get_key_characteristics 时都会返回原始值。此外,通过 generate_key 返回的特性必须将授权正确地分配到由硬件强制执行的列表和由软件强制执行的列表中。如需更多详细信息,请参阅 get_key_characteristics

必须要向 generate_key 提供的参数取决于要生成的密钥的类型。这一部分将概括介绍每种类型的密钥必需的标记以及允许使用的标记。KM_TAG_ALGORITHM 始终为必需的标记,用于指定类型。

RSA 密钥

以下参数是生成 RSA 密钥时必需的参数。

以下参数不是生成 RSA 密钥时必需的参数,但如果在缺少这些参数的情况下生成 RSA 密钥,生成的密钥将无法使用。如果缺少这些参数,generate_key 函数不应返回错误。

ECDSA 密钥

只有 KM_TAG_KEY_SIZE 是生成 ECDSA 密钥时必需的参数。此参数用于选择 EC 组。实现必须要支持 224、256、384 和 521,这些值分别表示 NIST p-224、p-256、p-384 和 p521 曲线。

为了使生成的 ECDSA 密钥可以使用,还需要 KM_TAG_DIGEST,但此参数不是生成 ECDSA 密钥时必需的参数。

AES 密钥

只有 KM_TAG_KEY_SIZE 是生成 AES 密钥时必需的参数。如果缺少此参数,方法必须返回 KM_ERROR_UNSUPPORTED_KEY_SIZE。必须要支持 128 和 256。建议支持 192 位 AES 密钥。

以下参数仅与 AES 密钥有关,但它们并不是生成 AES 密钥时必需的参数:

如果指定的是 GCM 分块模式,则必须要提供 KM_TAG_MIN_MAC_LENGTH。如果缺少此参数,方法必须返回 KM_ERROR_MISSING_MIN_MAC_LENGTH。此标记的值必须是 8 的倍数,并且必须介于 96 到 128 之间。

HMAC 密钥

以下参数是生成 HMAC 密钥时必需的参数:

密钥特性

如果特性参数为非 NULL 值,generate_key 必须返回新生成密钥的特性(适当地划分到由硬件强制执行的列表和由软件强制执行的列表中)。要了解哪些特性会划分到哪个列表中,请参阅 get_key_characteristics。返回的特性必须要包含为生成密钥而指定的所有参数,KM_TAG_APPLICATION_IDKM_TAG_APPLICATION_DATA 除外。如果这两个标记包含在密钥参数中,则必须要将其从返回的特性中移除;必须要确保无法通过查看返回的密钥 Blob 找出这两个标记的值。不过,这两个标记必须要以加密形式绑定到密钥 Blob,以便在使用相应密钥时,如果未提供正确的值,使用将会失败。同样,KM_TAG_ROOT_OF_TRUST 也必须要以加密形式绑定到相应密钥,但在生成或导入密钥期间可以不指定此标记,并且在任何情况下都不得返回此标记。

除了收到的标记外,Trustlet 还必须要添加 KM_TAG_ORIGIN(值为 KM_ORIGIN_GENERATED);如果相应密钥可抗回滚,还要添加 KM_TAG_ROLLBACK_RESISTANT

抗回滚

抗回滚意味着,相应密钥通过 delete_keydelete_all_keys 被删除后,安全硬件将保证它绝对无法再使用。不采用抗回滚的实现通常会将生成或导入的密钥材料作为密钥 Blob(一种经过加密和身份验证的形式)返回给调用程序。当 Keystore 删除密钥 Blob 后,相应密钥将会消失,但之前已设法获取密钥材料的攻击者可能能够将相应密钥材料恢复到设备上。

如果安全硬件保证被删除的密钥以后无法被恢复,那么相应密钥便可抗回滚。安全硬件通常是通过将额外的密钥元数据存储在攻击者无法操控的可信位置来做到这一点。在移动设备上,用于实现这一点的机制通常为 Replay Protected Memory Block (RPMB)。由于可创建的密钥数量基本上没有限制,而用于抗回滚的可信存储空间的大小可能有限制,因此即使无法为新密钥提供抗回滚功能,此方法也必须可以成功。在这种情况下,不得将 KM_TAG_ROLLBACK_RESISTANT 添加到密钥特性中。

get_key_characteristics

用于返回与收到的密钥关联的参数和授权,并且返回的参数和授权会划分为两组:一组由硬件强制执行,一组由软件强制执行。此处的说明同样适用于通过 generate_keyimport_key 返回的密钥特性列表。

如果在密钥生成或导入期间提供了 KM_TAG_APPLICATION_ID,则必须要在 client_id 参数中为此方法提供相同的值。否则,此方法必须返回 KM_ERROR_INVALID_KEY_BLOB。同样,如果在生成或导入密钥期间提供了 KM_TAG_APPLICATION_DATA ,则必须要在 app_data 参数中为此方法提供相同的值。

此方法返回的特性完整地说明了指定密钥的类型和用法。

要确定某个指定标记是属于由硬件强制执行的列表,还是属于由软件强制执行的列表,一般规则是:如果该标记的含义完全由安全硬件来保证,则属于由硬件强制执行的列表,否则属于由软件强制执行的列表。下面列出了可能无法明确确定到底属于哪个列表的具体标记:

import_key

用于将密钥材料导入到 Keymaster 硬件中。密钥定义参数和输出特性的处理方式与 generate_key 相同,但存在以下例外情况:

export_key

用于从 Keymaster RSA 密钥对或 EC 密钥对中导出公钥。

如果在密钥生成或导入期间提供了 KM_TAG_APPLICATION_ID,则必须要在 client_id 参数中为此方法提供相同的值。否则,此方法必须返回 KM_ERROR_INVALID_KEY_BLOB。同样,如果在生成或导入密钥期间提供了 KM_TAG_APPLICATION_DATA,则必须要在 app_data 参数中为此方法提供相同的值。

delete_key

用于删除收到的密钥。此方法为可选方法,可能只能由提供抗回滚功能的 Keymaster 模块来实现。

delete_all_keys

用于删除所有密钥。此方法为可选方法,可能只能由提供抗回滚功能的 Keymaster 模块来实现。

begin

用于开始使用指定的密钥和参数(视情况而定)针对指定的目的进行加密操作,并返回与 updatefinish 配合使用以完成操作的操作句柄。该操作句柄还会在经过身份验证的操作中用作“质询”令牌,并且对于此类操作,该操作句柄必须包含在身份验证令牌的 challenge 字段中。

Keymaster 实现必须要支持至少 16 个并行操作。Keystore 最多使用 15 个,留一个给 vold 用于对密码进行加密。当 Keystore 有 15 个操作正在进行(已调用 begin,但尚未调用 finishabort)时,如果收到开始第 16 个操作的请求,它将对最近使用最少的操作调用 abort,以便将进行中的操作减少到 14 个,然后再调用 begin 来开始执行新收到的操作请求。

如果在密钥生成或导入期间指定了 KM_TAG_APPLICATION_IDKM_TAG_APPLICATION_DATA,那么调用 begin 时必须要包含这两个标记以及最初在此方法的 in_params 参数中指定的值。

密钥授权强制执行

在执行此方法期间,如果实现将以下密钥授权放入到了“由硬件强制执行的”特性中,并且相应操作不是公钥操作,那么这些授权必须要由 Trustlet 来强制执行。即使不符合授权要求,也必须要允许公钥操作(即使用 RSA 或 EC 密钥进行的 KM_PURPOSE_ENCRYPTKM_PURPOSE_VERIFY)成功完成。

RSA 密钥

执行任何 RSA 密钥操作时,都必须要在 in_params 中指定一种且只能指定一种填充模式。如果未指定或指定了多次,方法必须返回 KM_ERROR_UNSUPPORTED_PADDING_MODE

RSA 签名和验证操作需要摘要,正如使用 OAEP 填充模式进行 RSA 加密和解密操作时一样。对于这些情况,调用程序必须要在 in_params 中指定一种且只能指定一种摘要。如果未指定或指定了多次,方法必须返回 KM_ERROR_UNSUPPORTED_DIGEST

私钥操作(KM_PURPOSE_DECYPTKM_PURPOSE_SIGN)要求摘要和填充获得授权,也就是说,指定的值必须要在密钥授权中。否则,方法必须视情况返回 KM_ERROR_INCOMPATIBLE_DIGESTKM_ERROR_INCOMPATIBLE_PADDING。公钥操作(KM_PURPOSE_ENCRYPTKM_PURPOSE_VERIFY)可以使用未经授权的摘要或填充。

除了 KM_PAD_NONE 之外,所有 RSA 填充模式都仅适用于特定目的。具体来说就是,KM_PAD_RSA_PKCS1_1_5_SIGNKM_PAD_RSA_PSS 仅支持签名和验证,而 KM_PAD_RSA_PKCS1_1_1_5_ENCRYPTKM_PAD_RSA_OAEP 仅支持加密和解密。如果指定的模式不支持指定的目的,方法必须返回 KM_ERROR_UNSUPPORTED_PADDING_MODE

填充模式与摘要之间存在以下非常重要的相互关系:

EC 密钥

执行任何 EC 密钥操作时,都必须要在 in_params 中指定一种且只能指定一种填充模式。如果未指定或指定了多次,则返回 KM_ERROR_UNSUPPORTED_PADDING_MODE

私钥操作 (KM_PURPOSE_SIGN) 要求摘要获得授权,也就是说,指定的值必须要在密钥授权中。否则返回 KM_ERROR_INCOMPATIBLE_DIGEST。公钥操作 (KM_PURPOSE_VERIFY) 可以使用未经授权的摘要或填充。

AES 密钥

执行 AES 密钥操作时,必须要在 in_params 中指定一种且只能指定一种分块模式和填充模式。如果有任何一项未指定或指定了多次,则返回 KM_ERROR_UNSUPPORTED_BLOCK_MODEKM_ERROR_UNSUPPORTED_PADDING_MODE。指定的模式必须要已通过相应密钥授权。否则,方法必须返回 KM_ERROR_INCOMPATIBLE_BLOCK_MODEKM_ERROR_INCOMPATIBLE_PADDING_MODE

如果分块模式是 KM_MODE_GCM,则必须要在 in_params 中指定 KM_TAG_MAC_LENGTH。指定的值必须是 8 的倍数,并且不得大于 128,也不得小于密钥授权中 KM_TAG_MIN_MAC_LENGTH 的值。如果 MAC 长度大于 128 或不是 8 的倍数,则返回 KM_ERROR_UNSUPPORTED_MAC_LENGTH。如果 MAC 长度小于密钥最小长度,则返回 KM_ERROR_INVALID_MAC_LENGTH

如果分块模式是 KM_MODE_GCMKM_MODE_CTR,那么指定的填充模式必须是 KM_PAD_NONE。如果分块模式是 KM_MODE_ECBKM_MODE_CBC,那么指定的填充模式可以是 KM_PAD_NONEKM_PAD_PKCS7。如果填充模式不符合这些要求,则返回 KM_ERROR_INCOMPATIBLE_PADDING_MODE

如果分块模式是 KM_MODE_CBCKM_MODE_CTRKM_MODE_GCM,则需要初始化矢量或随机数。在大多数情况下,调用程序都不应提供 IV 或随机数,而 Keymaster 实现必须要生成一个随机 IV 或随机数,并通过 out_params 中的 KM_TAG_NONCE 将其返回。CBC IV 和 CTR IV 均为 16 个字节。GCM 随机数为 12 个字节。如果密钥授权包含 KM_TAG_CALLER_NONCE,那么调用程序可以通过 in_params 中的 KM_TAG_NONCE 提供 IV/随机数。如果在 KM_TAG_CALLER_NONCE 未获得授权时提供了随机数,则返回 KM_ERROR_CALLER_NONCE_PROHIBITED。如果在 KM_TAG_CALLER_NONCE 获得了授权的情况下未提供随机数,则生成一个随机 IV/随机数。

HMAC 密钥

执行 HMAC 密钥操作时,必须要在 in_params 中指定 KM_TAG_MAC_LENGTH。指定的值必须是 8 的倍数,并且不得大于摘要长度,也不得小于密钥授权中 KM_TAG_MIN_MAC_LENGTH 的值。如果 MAC 长度大于摘要长度或不是 8 的倍数,则返回 KM_ERROR_UNSUPPORTED_MAC_LENGTH。如果 MAC 长度小于密钥最小长度,则返回 KM_ERROR_INVALID_MAC_LENGTH

update

用于提供要在通过 begin 开始且正在进行的操作中处理的数据。操作是通过 operation_handle 参数指定的。

为了更灵活地处理缓冲区,此方法的实现可以选择不消耗完收到的数据。调用程序负责执行循环操作,以便在后续调用中馈送其余数据。必须要在 input_consumed 参数中返回所消耗的输入数据量。实现必须始终消耗至少一个字节,除非相应操作无法再接受更多字节;如果收到了零个以上的字节,但消耗了零字节,调用程序会将此视为错误并中止相应操作。

实现还可以选择返回多少数据(作为 update 的结果)。这仅与加密和解密操作有关,因为在调用 finish 之前,签名和验证操作不会返回任何数据。建议尽早返回数据,而不是缓冲数据。

错误处理

如果此方法返回除 KM_ERROR_OK 之外的错误代码,那么相应操作将被中止,操作句柄也必须变为无效。如果以后再将该句柄与此方法、finishabort 配合使用,都必须返回 KM_ERROR_INVALID_OPERATION_HANDLE

密钥授权强制执行

密钥授权强制执行主要在 begin 中进行。不过,密钥存在以下情况时例外:

在这种情况下,密钥需要针对各项操作的授权,并且 update 方法必须要在 in_params 参数中收到 KM_TAG_AUTH_TOKEN。该令牌必须有效(HMAC 必须验证),必须包含匹配的安全用户 ID,必须与密钥的 KM_TAG_USER_AUTH_TYPE 匹配,并且必须包含质询字段中当前操作的操作句柄。如果不符合这些要求,则返回 KM_ERROR_KEY_USER_NOT_AUTHENTICATED

调用程序必须要在每次调用 updatefinish 时提供身份验证令牌。实现只需对该令牌验证一次(如果它倾向于这么做)。

RSA 密钥

对于使用 KM_DIGEST_NONE 的签名和验证操作,此方法必须要在单次 update 中接受要签署或验证的整个分块。此方法不能只消耗分块的一部分。不过,如果调用程序选择在多次 update 中提供数据,此方法仍必须要在多次 update 中接受相应数据。如果调用程序提供的要签署的数据多于可以消耗的数据(数据长度超出 RSA 密钥大小),则返回 KM_ERROR_INVALID_INPUT_LENGTH

ECDSA 密钥

对于使用 KM_DIGEST_NONE 的签名和验证操作,此方法必须要在单次 update 中接受要签署或验证的整个分块。此方法不能只消耗分块的一部分。

不过,如果调用程序选择在多次 update 中提供数据,此方法仍必须要在多次 update 中接受相应数据。如果调用程序提供的要签署的数据多于可以消耗的数据,则应以静默方式截断这些数据。(这与处理在类似 RSA 操作中提供的超量数据不同,因为此方法与旧版客户端兼容。)

AES 密钥

AES GCM 模式支持通过 in_params 参数中的 KM_TAG_ASSOCIATED_DATA 标记提供的“相关身份验证数据”。可以在重复调用(如果数据太大而无法在单个分块中发送,那么重复调用非常重要)中提供相关数据,但必须始终先于要加密或解密的数据提供。update 调用可以同时接收相关数据以及要加密/解密的数据,但后续 update 中不得包含相关数据。如果调用程序已在某次调用 update 时提供了要加密/解密的数据,若再次向 update 调用提供相关数据,则返回 KM_ERROR_INVALID_TAG

对于 GCM 加密,此标记会通过 finish 附加到密文中。在解密期间,向上一次 update 调用提供的数据的最后 KM_TAG_MAC_LENGTH 个字节就是此标记。由于 update 的指定调用无法得知自己是否为最后一次调用,因此它必须处理除标记长度之外的所有数据,并缓冲可能的标记数据以便在调用 finish 期间进行处理。

finish

用于完成通过 begin 开始且正在进行的操作,负责处理通过 update 提供的所有尚未处理的数据。

此方法是操作期间调用的最后一个方法,因此必须返回所有处理后的数据。

无论是成功完成还是返回错误,此方法都会结束相应操作,从而使收到的操作句柄无效。如果以后再将该句柄与此方法、updateabort 配合使用,都必须返回 KM_ERROR_INVALID_OPERATION_HANDLE

签名操作将返回签名作为输出。验证操作将接受 signature 参数中的签名,并且不会返回任何输出。

密钥授权强制执行

密钥授权强制执行主要在 begin 中进行。不过,密钥存在以下情况时例外:

在这种情况下,密钥需要针对各项操作的授权,并且 update 方法必须要在 in_params 参数中收到 KM_TAG_AUTH_TOKEN。该令牌必须有效(HMAC 必须验证),必须包含匹配的安全用户 ID,必须与密钥的 KM_TAG_USER_AUTH_TYPE 匹配,并且必须包含质询字段中当前操作的操作句柄。如果不符合这些要求,则返回 KM_ERROR_KEY_USER_NOT_AUTHENTICATED

调用程序必须要在每次调用 updatefinish 时提供身份验证令牌。实现只需对该令牌验证一次(如果它倾向于这么做)。

RSA 密钥

有一些附加要求,具体取决于填充模式:

ECDSA 密钥

如果为非填充式签名或验证操作提供的数据太长,则要将其截断。

AES 密钥

有一些附加要求,具体取决于分块模式:

abort

用于中止正在进行的操作。在调用 abort 之后,如果后续再将收到的操作句柄与 updatefinishabort 配合使用,则返回 KM_ERROR_INVALID_OPERATION_HANDLE