blob: c9073319bcb8965867545de3a3e1f5d8bfa9fa92 [file] [log] [blame] [edit]
/* Copyright 2025 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <string.h>
#include "console.h"
#include "endian.h"
#include "extension.h"
#include "link_defs.h"
#include "strongbox.h"
#include "dcrypto.h"
#include "internal.h"
#include "nvmem_vars.h"
#include "board_id_features.h"
#include "cbor_basic.h"
#include "cbor_boot_param.h"
#include "boot_param.h"
#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ##args)
struct slice {
const void *p;
size_t n;
};
#define SLICE(pp, nn) ((struct slice){ .p = (pp), .n = (nn) })
#define SLICE_OBJ(X) SLICE(X, sizeof(X))
#define SLICE_STR(X) SLICE(X, sizeof(X) - 1)
/* Configuration for key blob encryption and integrity. */
#define KM_AES_ENCRYPT_KEY_BITS 256
#define KM_HMAC_SIGN_KEY_BITS 256
#define KM_AES_IV_SIZE 16
#define KM_AES_ENCRYPT_KEY_SIZE (KM_AES_ENCRYPT_KEY_BITS / CHAR_BIT)
#define KM_HMAC_SIGN_KEY_SIZE (KM_HMAC_SIGN_KEY_BITS / CHAR_BIT)
#define KM_AES_IV_WORDS (KM_AES_IV_SIZE / sizeof(uint32_t))
#define KM_AES_ENCRYPT_KEY_WORDS (KM_AES_ENCRYPT_KEY_SIZE / sizeof(uint32_t))
#define KM_HMAC_SIGN_KEY_WORDS (KM_HMAC_SIGN_KEY_SIZE / sizeof(uint32_t))
#define KM_MAX_CHALLENGE_LEN 64
#define KM_MAX_CHALLENGE_WORDS (KM_MAX_CHALLENGE_LEN / sizeof(uint32_t))
struct km_key_attr {
uint64_t rsa_exponent;
uint32_t purpose_flags;
uint32_t algorithm;
uint32_t key_size;
uint32_t curve_id;
uint32_t digest_flags;
bool caller_nonce;
};
/* Corresponds to `KeyAttributes` struct. Filled during tag parsing. */
struct km_key_params {
struct km_key_attr attrs;
struct slice application_id;
struct slice application_data;
struct slice certificate_subject;
struct slice certificate_issuer;
struct slice certificate_serial;
/* TAG::ROOT_OF_TRUST */
struct slice rootOfTrust;
struct slice attestationChallenge;
struct slice uniqueId;
/* Tag::ATTESTATION_APPLICATION_ID [709] */
struct slice attestationApplicationId;
/* Tag::ATTESTATION_ID_BRAND [710] */
struct slice attestationIdBrand;
/* Tag::ATTESTATION_ID_DEVICE [711]*/
struct slice attestationIdDevice;
/* Tag::ATTESTATION_ID_PRODUCT [712] */
struct slice attestationIdProduct;
/* Tag::ATTESTATION_ID_SERIAL [713]*/
struct slice attestationIdSerial;
/* Tag::ATTESTATION_ID_IMEI [714] */
struct slice attestationIdImei;
/* Tag::ATTESTATION_ID_MEID [715] */
struct slice attestationIdMeid;
/* Tag::ATTESTATION_ID_MANUFACTURER [716] */
struct slice attestationIdManufacturer;
/* Tag::ATTESTATION_ID_MODEL [717] */
struct slice attestationIdModel;
/* Tag::MODULE_HASH [724] */
struct slice moduleHash;
/* Tag::CERTIFICATE_NOT_AFTER */
uint64_t date_not_after;
/* Tag::CERTIFICATE_NOT_BEFORE */
uint64_t date_not_before;
/* Tag::ACTIVE_DATETIME [400] */
uint64_t activeDateTime;
/* Tag::ORIGINATION_EXPIRE_DATETIME [401] */
uint64_t originationExpireDateTime;
/* Tag::CREATION_DATETIME [701] */
uint64_t creationDateTime;
/* Tag::OS_VERSION, [705] */
uint32_t osVersion;
/* Tag::PATCHLEVEL [706] */
uint32_t osPatchLevel;
/* Tag::VENDOR_PATCHLEVEL [718] */
uint32_t vendorPatchLevel;
/* Tag::BOOT_PATCHLEVEL [719] */
uint32_t bootPatchLevel;
/* TAG::ORIGIN [702] */
enum km_origin origin;
/* Tag::DEVICE_UNIQUE_ATTESTATION [720] */
bool deviceUniqueAttestation;
/* Tag::ROLLBACK_RESISTANCE [303] */
bool rollbackResistance;
/* Tag::EARLY_BOOT_ONLY [304] */
bool earlyBootOnly;
bool noAuthRequired;
bool includeUniqueId;
};
struct tag_offset {
enum km_tag tag;
size_t offset;
};
#define TAG_OFFSET(T, F) \
{ .tag = T, .offset = offsetof(struct km_key_params, F) }
static const struct tag_offset slice_tags[] = {
TAG_OFFSET(KM_TAG_APPLICATION_ID, application_id),
TAG_OFFSET(KM_TAG_APPLICATION_DATA, application_data),
TAG_OFFSET(KM_TAG_ROOT_OF_TRUST, rootOfTrust),
TAG_OFFSET(KM_TAG_ATTESTATION_APPLICATION_ID, attestationApplicationId),
TAG_OFFSET(KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_BRAND, attestationIdBrand),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_DEVICE, attestationIdDevice),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_PRODUCT, attestationIdProduct),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_SERIAL, attestationIdSerial),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_IMEI, attestationIdImei),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_MEID, attestationIdMeid),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_MANUFACTURER,
attestationIdManufacturer),
TAG_OFFSET(KM_TAG_ATTESTATION_ID_MODEL, attestationIdModel),
/* Ignore KM_TAG_ATTESTATION_ID_SECOND_IMEI */
TAG_OFFSET(KM_TAG_MODULE_HASH, moduleHash),
TAG_OFFSET(KM_TAG_UNIQUE_ID, uniqueId),
/* Ignore KM_TAG_ASSOCIATED_DATA */
/* Ignore KM_TAG_NONCE */
/* Ignore KM_TAG_CONFIRMATION_TOKEN */
TAG_OFFSET(KM_TAG_CERTIFICATE_SERIAL, certificate_serial),
TAG_OFFSET(KM_TAG_CERTIFICATE_SUBJECT, certificate_subject),
};
/* Starting index of the tag going into certificate.
* Skip APPLICATION_ID and APPLICATION_DATA.
*/
#define KM_SLICE_TAG_CERT_START 2
static const struct tag_offset uint64t_tags[] = {
TAG_OFFSET(KM_TAG_RSA_PUBLIC_EXPONENT, attrs.rsa_exponent),
TAG_OFFSET(KM_TAG_CERTIFICATE_NOT_AFTER, date_not_after),
TAG_OFFSET(KM_TAG_CERTIFICATE_NOT_BEFORE, date_not_before),
TAG_OFFSET(KM_TAG_ACTIVE_DATETIME, activeDateTime),
TAG_OFFSET(KM_TAG_ORIGINATION_EXPIRE_DATETIME,
originationExpireDateTime),
TAG_OFFSET(KM_TAG_CREATION_DATETIME, creationDateTime),
};
static const struct tag_offset uint32t_tags[] = {
TAG_OFFSET(KM_TAG_ALGORITHM, attrs.algorithm),
TAG_OFFSET(KM_TAG_KEY_SIZE, attrs.key_size),
TAG_OFFSET(KM_TAG_EC_CURVE, attrs.curve_id),
TAG_OFFSET(KM_TAG_OS_VERSION, osVersion),
TAG_OFFSET(KM_TAG_OS_PATCHLEVEL, osPatchLevel),
TAG_OFFSET(KM_TAG_VENDOR_PATCHLEVEL, vendorPatchLevel),
TAG_OFFSET(KM_TAG_BOOT_PATCHLEVEL, bootPatchLevel),
};
static const struct tag_offset bool_tags[] = {
TAG_OFFSET(KM_TAG_CALLER_NONCE, attrs.caller_nonce),
TAG_OFFSET(KM_TAG_DEVICE_UNIQUE_ATTESTATION, deviceUniqueAttestation),
TAG_OFFSET(KM_TAG_ROLLBACK_RESISTANCE, rollbackResistance),
TAG_OFFSET(KM_TAG_EARLY_BOOT_ONLY, earlyBootOnly),
TAG_OFFSET(KM_TAG_NO_AUTH_REQUIRED, noAuthRequired),
TAG_OFFSET(KM_TAG_INCLUDE_UNIQUE_ID, includeUniqueId),
};
/* Make DIGEST::NONE to use same space as SHA256 context */
struct digest_none_ctx {
uint32_t update_size;
uint32_t update_context[sizeof(struct sha512_ctx) / 4 - 1];
};
struct km_operation {
uint32_t operation_id;
/* Note, attr.digest is actual digest, not a flag! */
struct km_key_attr attrs;
enum km_purpose purpose;
uint32_t key[8];
union {
struct digest_none_ctx none_ctx;
struct sha256_ctx sha256_ctx;
struct sha512_ctx sha512_ctx;
};
};
#define KM_MAX_OPS 4
/* Persistent configuration parameters */
struct keymint_config {
uint32_t hmac_tag_key[8];
uint32_t drbg_seed_key[8];
};
/* Keymint context */
struct km {
uint32_t os_version;
uint32_t os_patchlevel;
uint32_t vendor_patchlevel;
uint32_t boot_patchlevel;
struct keymint_config c;
/* Bit mask for used slots in `ops`. */
uint32_t used_slots;
/* Public P256 for the last created key. */
p256_int last_pk_x, last_pk_y;
struct km_operation ops[KM_MAX_OPS];
bool initialized;
};
/* Keymint context */
static struct km km;
static size_t SB_cert_name(const p256_int *d, const p256_int *pk_x,
const p256_int *pk_y,
const struct km_key_params *params, uint8_t *cert,
const size_t n);
static const uint8_t g_keymint_var_name[] = { 'K', 'M' };
/* Set the default subject:
* SEQUENCE 0x30 <size = 0x1f>
* SET 0x31 <size = 0x1d>
* SEQUENCE 0x30 <size = 0x1b>
* OBJECT 0x06 <size = 0x03> 0x55 0x04 0x03 (:commonName)
* PRINTABLESTRING 0x13 <size = 0x14> "Android Keystore Key"
*/
static const char g_default_subject_data[] = {
0x30, 0x1f, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
0x03, 0x13, 0x14, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20,
0x4b, 0x65, 0x79
};
static const struct slice g_default_subject =
SLICE(g_default_subject_data, sizeof(g_default_subject_data));
static enum strongbox_error keymint_init(void)
{
const struct tuple *var;
memset(&km, 0, sizeof(km));
var = getvar(g_keymint_var_name, sizeof(g_keymint_var_name));
if (var) {
if (var->val_len == sizeof(km.c)) {
memcpy(&km.c, tuple_val(var), sizeof(km.c));
freevar(var);
km.initialized = true;
return SB_OK;
}
} else
freevar(var);
/* No keymint data yet, let's create it. */
if (!fips_trng_bytes(&km.c, sizeof(km.c)))
return SBERR_HardwareTypeUnavailable;
if (setvar(g_keymint_var_name, sizeof(g_keymint_var_name),
(uint8_t *)&km.c, sizeof(km.c)) != 0)
return SBERR_SecureHwBusy;
km.initialized = true;
return SB_OK;
}
void keymint_deinit(void)
{
km.initialized = false;
}
uint32_t extension_route_strongbox_command(struct vendor_cmd_params *p)
{
const struct strongbox_command *cmd_p;
const struct strongbox_command *end_p;
uint32_t board_cfg;
size_t buf_size_words = p->out_size / sizeof(uint32_t);
#ifdef DEBUG_EXTENSION
CPRINTS("%s(%d,%s) is=%d os=%d", __func__, p->code,
p->flags & VENDOR_CMD_FROM_USB ? "USB" : "AP", p->in_size,
p->out_size);
#endif
/* Set the response size to 0 for all error cases. */
p->out_size = 0;
/* Check that command came from valid interface in a valid state. */
if ((p->flags & (VENDOR_CMD_FROM_USB | VENDOR_CMD_FROM_ALT_IF))
#ifdef CONFIG_BOARD_ID_SUPPORT
|| board_id_is_mismatched()
#endif
)
return SBERR_HardwareNotYetAvailable;
board_cfg = get_board_cfg();
if (board_cfg & BOARD_CFG_SB_DISABLE_SET)
return SBERR_HardwareNotYetAvailable;
if (!(board_cfg & BOARD_CFG_SB_ENABLE_SET))
return SBERR_HardwareNotYetAvailable;
/* Find the command handler */
cmd_p = (const struct strongbox_command *)&__strongbox_cmds;
end_p = (const struct strongbox_command *)&__strongbox_cmds_end;
while (cmd_p != end_p) {
if (cmd_p->command_code == p->code) {
/* Check that input size is aligned, which is convention
* for all SB commands.
*/
if (p->in_size & (sizeof(uint32_t) - 1))
return SBERR_InvalidArgument;
if (!km.initialized) {
enum strongbox_error err = keymint_init();
if (err != SB_OK)
return err;
}
return cmd_p->handler(&km, p->buffer, buf_size_words,
p->in_size / sizeof(uint32_t),
&p->out_size);
}
cmd_p++;
}
/* Command not found or not allowed */
return SBERR_Unimplemented;
}
/**
* @brief Set HW Enforced Parameters
*
* Input:
* - [ 1 byte ] Non-zero value enables Strongbox, zero disables.
* No output produced.
* @return VENDOR_RC_SUCCESS on success, or an error code on failure.
*/
static enum vendor_cmd_rc vc_SetStrongboxState(enum vendor_cmd_cc code,
void *buf, size_t input_size,
size_t *response_size)
{
uint8_t *b = (uint8_t *)buf;
uint32_t board_cfg = get_board_cfg();
*response_size = 0;
if (input_size != 1)
return VENDOR_RC_BOGUS_ARGS;
/* Don't allow to enable if was disabled previously. */
if (b[0]) {
if (board_cfg & BOARD_CFG_SB_DISABLE_SET)
return VENDOR_RC_NOT_ALLOWED;
store_board_id_features(board_cfg | BOARD_CFG_SB_ENABLE_SET);
} else {
/* Set SB_DISABLE, clear SB_ENABLE */
store_board_id_features((board_cfg & ~BOARD_CFG_SB_ENABLE_SET) |
BOARD_CFG_SB_DISABLE_SET);
}
return VENDOR_RC_SUCCESS;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_STRONGBOX_STATE, vc_SetStrongboxState);
/**
* @brief Implements IKeyMintDevice::getHardwareInfo.
*
* Provides information about the KeyMint hardware.
* Doesn't take any parameters.
*
* Output Buffer:
* A 32-byte structure containing hardware details. The caller must provide an
* output buffer of at least 32 bytes.
* - [ 4 bytes ] uint32_t version
* - [ 4 bytes ] enum km_security_level (Strongbox)
* - [ 4 bytes ] uint32_t keymint_name_len (value: 4)
* - [ 4 bytes ] char[] keymint_name ("CR50")
* - [ 4 bytes ] uint32_t keymint_author_len (value: 6)
* - [ 8 bytes ] char[] keymint_author ("GOOGLE\0\0")
* - [ 4 bytes ] uint32_t timestamp_token_required (false)
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
enum strongbox_error sb_GetHardwareInfo(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
/* All values are aligned to 32-bit */
static const uint8_t r[32] = { /* version */
0x00, 0x00, 0x00, 0x00,
/* enum km_security_level Strongbox */
0x00, 0x00, 0x00, 0x02,
/* Keymint name, length 0x00000004 */
0x00, 0x00, 0x00, 0x04, 'C', 'R', '5',
'0',
/* Key mint author, data length
* 0x00000006, actual length 8 bytes
*/
0x00, 0x00, 0x00, 0x06, 'G', 'O', 'O',
'G', 'L', 'E', 0x00, 0x00,
/* timestamp_token_required = false */
0x00, 0x00, 0x00, 0x00
};
(void)km;
if (buf_size_words < sizeof(r) / sizeof(uint32_t))
return SBERR_InvalidArgument;
/* No arguments are expected for the command. */
if (req_len_words)
return SBERR_InvalidArgument;
*out_len_bytes = sizeof(r);
memcpy(buf, &r, sizeof(r));
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_DeviceGetHardwareInfo, sb_GetHardwareInfo);
/**
* @brief Set HW Enforced Parameters
*
* Input:
* - [ 4 bytes ] OS Version
* - [ 4 bytes ] OS Patch level
* - [ 4 bytes ] Boot patch level
* - [ 4 bytes ] Vendor patch level
*
* No output produced.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
enum strongbox_error sb_SetHalBootInfo(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
/* 4 32-bit word arguments are expected for the command. */
if (req_len_words != 4)
return SBERR_InvalidArgument;
km->os_version = buf[0];
km->os_patchlevel = buf[1];
km->boot_patchlevel = buf[2];
km->vendor_patchlevel = buf[3];
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_SetHalBootInfo, sb_SetHalBootInfo);
/**
* @brief Checks if a tag is disallowed during key generation/import.
*/
static bool tag_is_gen_disallowed(enum km_tag tag)
{
switch (tag) {
case KM_TAG_INVALID:
case KM_TAG_MAX_USES_PER_BOOT:
case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
case KM_TAG_ORIGIN:
case KM_TAG_ROOT_OF_TRUST:
case KM_TAG_OS_VERSION:
case KM_TAG_OS_PATCHLEVEL:
case KM_TAG_BOOT_PATCHLEVEL:
case KM_TAG_VENDOR_PATCHLEVEL:
case KM_TAG_RESET_SINCE_ID_ROTATION:
return true;
default:
return false;
}
}
/**
* @brief Checks if a tag is software-enforced.
*/
static bool tag_is_sw_enforced(enum km_tag tag)
{
/* Any tag not HW-enforced is considered SW-enforced for this
* translation.
*/
switch (tag) {
case KM_TAG_ACTIVE_DATETIME:
case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
case KM_TAG_USAGE_EXPIRE_DATETIME:
case KM_TAG_USER_ID:
case KM_TAG_ALLOW_WHILE_ON_BODY:
case KM_TAG_CREATION_DATETIME:
case KM_TAG_MAX_BOOT_LEVEL:
case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
return true;
default:
return false;
}
}
/**
* @brief Checks if a tag is hardware-enforced.
*/
static bool tag_is_hw_enforced(enum km_tag tag)
{
static const enum km_tag hw_enforced_tags[] = {
KM_TAG_USER_SECURE_ID,
KM_TAG_ALGORITHM,
KM_TAG_EC_CURVE,
KM_TAG_USER_AUTH_TYPE,
KM_TAG_ORIGIN,
KM_TAG_PURPOSE,
KM_TAG_BLOCK_MODE,
KM_TAG_DIGEST,
KM_TAG_PADDING,
KM_TAG_RSA_OAEP_MGF_DIGEST,
KM_TAG_KEY_SIZE,
KM_TAG_MIN_MAC_LENGTH,
KM_TAG_MAX_USES_PER_BOOT,
KM_TAG_AUTH_TIMEOUT,
KM_TAG_OS_VERSION,
KM_TAG_OS_PATCHLEVEL,
KM_TAG_VENDOR_PATCHLEVEL,
KM_TAG_BOOT_PATCHLEVEL,
KM_TAG_RSA_PUBLIC_EXPONENT,
KM_TAG_CALLER_NONCE,
KM_TAG_BOOTLOADER_ONLY,
KM_TAG_ROLLBACK_RESISTANCE,
KM_TAG_EARLY_BOOT_ONLY,
KM_TAG_NO_AUTH_REQUIRED,
KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED,
KM_TAG_TRUSTED_CONFIRMATION_REQUIRED,
KM_TAG_STORAGE_KEY
};
for (size_t i = 0; i < ARRAY_SIZE(hw_enforced_tags); i++) {
if (tag == hw_enforced_tags[i])
return true;
}
return false;
}
static enum strongbox_error process_tag(enum km_tag tag,
struct km_key_params *params,
const uint32_t *p_tag)
{
const struct tag_offset *t, *t_end;
t = slice_tags;
t_end = t + ARRAY_SIZE(slice_tags);
do {
if (t->tag == tag) {
uint32_t tag_word = p_tag[0];
struct slice *slice =
(struct slice *)(((uint8_t *)params) +
t->offset);
if (slice->n)
return SBERR_InvalidTag;
*slice = SLICE(&p_tag[1],
prefix_get_data_len_bytes(tag_word));
return SB_OK;
}
t++;
} while (t < t_end);
t = uint64t_tags;
t_end = t + ARRAY_SIZE(uint64t_tags);
do {
if (t->tag == tag) {
uint64_t *val =
(uint64_t *)(((uint8_t *)params) + t->offset);
if (*val)
return SBERR_InvalidTag;
*val = make64(p_tag[2], p_tag[1]);
return SB_OK;
}
t++;
} while (t < t_end);
t = uint32t_tags;
t_end = t + ARRAY_SIZE(uint32t_tags);
do {
if (t->tag == tag) {
uint32_t *val =
(uint32_t *)(((uint8_t *)params) + t->offset);
if (*val)
return SBERR_InvalidTag;
*val = p_tag[1];
return SB_OK;
}
t++;
} while (t < t_end);
t = bool_tags;
t_end = t + ARRAY_SIZE(bool_tags);
do {
if (t->tag == tag) {
bool *val = (bool *)(((uint8_t *)params) + t->offset);
if (*val)
return SBERR_InvalidTag;
*val = true;
return SB_OK;
}
t++;
} while (t < t_end);
switch (tag) {
case KM_TAG_ROLLBACK_RESISTANCE:
return SBERR_RollbackResistanceUnavailable;
case KM_TAG_PURPOSE: {
uint32_t purpose = p_tag[1];
if (purpose >= 32)
return SBERR_InvalidTag;
params->attrs.purpose_flags |= (1 << purpose);
break;
}
case KM_TAG_DIGEST: {
uint32_t digest = p_tag[1];
if (digest > KM_DIGEST_SHA_2_512)
return SBERR_InvalidTag;
params->attrs.digest_flags |= (1 << digest);
break;
}
default:
break;
}
return SB_OK;
}
/* Fixed tags added to generated KeyCharacteristics */
#define KM_KEY_CHARACTERISTICS_WORDS 12
/**
* @brief Parse Key parameters tags and produce KeyCharacteristics.
*
* This function parses a list of Keymint tags from an input buffer. It
* validates the tags, extracts key attributes, and separates the tags into
* hardware-enforced and software-enforced characteristics. The resulting
* KeyCharacteristics are serialized into the output buffer.
*
* @param km The keymint context, containing device state like OS version.
* @param params Output parameter. A struct to be filled with the parsed key
* attributes.
* @param origin The origin of the key (e.g., generated, imported).
* @param buf Input buffer containing the key parameters as a series of tags.
* @param req_len_words The length of the input buffer in words.
* @param out Output buffer where the serialized KeyCharacteristics will be
* written.
* @param out_len_words The size of the output buffer in words.
* @param out_written_words Output parameter. The number of words written to the
* output buffer.
* Input Buffer (buf) Format: [size in 32-bit words] [tags]
*
* The input buffer buf is expected to contain a sequence of Keymint tags. The
* first word buf[0] indicates the total length of the tag list in 32-bit words.
* Each tag is a 32-bit words with following data. Data is always aligned to
* 32-bits. For BYTES/BIGNUM tags, the length of data is encoded in the middle
* 12 bits, see `prefix_get_word_len()`.
*
* The function iterates through the tags in buf until it has processed
* tags_len_words. The output buffer out stores the serialized
* KeyCharacteristics, separated into hardware-enforced and software-enforced
* sections. The format is as follows:
*
* Hardware-Enforced Section:
* out[0]: Security Level (always KM_SECURITY_STRONGBOX)
* out[1]: Length of the hardware-enforced section in words (excluding out[0]
* and out[1]) Subsequent words: Keymint tags that are hardware-enforced. These
* tags have the same format as in the input buffer (tag word followed by data
* payload).
* Software-Enforced Section (starts immediately after the hardware-enforced
* section):
* out[x]: Security Level (always KM_SECURITY_KEYSTORE)
* out[x + 1]: Length of the software-enforced section in words (excluding
* out[x] and out[x + 1])
* Subsequent words: Keymint tags that are software-enforced, using the
* same format as above.
* @return SB_OK on success, or an error code on failure.
*/
static enum strongbox_error process_gen_import_tags(
struct km *km, struct km_key_params *params, enum km_origin origin,
const uint32_t *buf, size_t req_len_words, uint32_t *out,
size_t out_len_words, size_t *out_written_words)
{
size_t tags_len_words;
size_t hw_tag_start;
size_t sw_enforced_tag_size_words;
size_t tag_pos;
size_t sw_out_start;
size_t sw_tag_write_pos;
/* 14 words is the minimal size of the result. */
if (req_len_words < 1 || out_len_words < 14)
return SBERR_InvalidTag;
memset(params, 0, sizeof(*params));
tags_len_words = buf[0];
if (tags_len_words > req_len_words - 1)
return SBERR_InvalidTag;
/* These tags go into TEE/Strongbox KeyCharacteristics */
out[0] = (uint32_t)KM_SECURITY_STRONGBOX;
/* out[1] is length, filled later */
out[2] = (uint32_t)KM_TAG_ORIGIN;
out[3] = origin;
params->origin = origin;
out[4] = (uint32_t)KM_TAG_OS_VERSION;
out[5] = km->os_version;
out[6] = (uint32_t)KM_TAG_OS_PATCHLEVEL;
out[7] = km->os_patchlevel;
out[8] = (uint32_t)KM_TAG_VENDOR_PATCHLEVEL;
out[9] = km->vendor_patchlevel;
out[10] = (uint32_t)KM_TAG_BOOT_PATCHLEVEL;
out[11] = km->boot_patchlevel;
hw_tag_start = KM_KEY_CHARACTERISTICS_WORDS;
sw_enforced_tag_size_words = 0;
tag_pos = 1; /* start after length word */
/* Pass 1: Parse tags, copy HW tags, count SW tags, extract attributes
*/
while (tag_pos < tags_len_words + 1) {
const uint32_t *p_tag = &buf[tag_pos];
uint32_t tag_word = *p_tag;
enum km_tag tag = prefix_get_tag(tag_word);
size_t word_len = prefix_get_word_len(tag_word);
enum strongbox_error err;
if (tag_is_gen_disallowed(tag))
return SBERR_InvalidTag;
err = process_tag(tag, params, p_tag);
if (err != SB_OK)
return err;
if (tag_is_hw_enforced(tag)) {
if (hw_tag_start + word_len > out_len_words) {
/* Not enough space */
return SBERR_InvalidArgument;
}
memcpy(&out[hw_tag_start], p_tag,
word_len * sizeof(uint32_t));
hw_tag_start += word_len;
} else if (tag_is_sw_enforced(tag)) {
sw_enforced_tag_size_words += word_len;
}
tag_pos += word_len;
}
if ((params->attrs.purpose_flags & (1 << KM_PURPOSE_ATTEST_KEY)) != 0 &&
(params->attrs.purpose_flags != (1 << KM_PURPOSE_ATTEST_KEY))) {
return SBERR_IncompatiblePurpose;
}
if (params->attrs.algorithm == 0)
return SBERR_InvalidArgument;
/* Set length of HW-enforced tags */
out[1] = (hw_tag_start - 2);
if (out_len_words < hw_tag_start + 2)
return SBERR_InvalidArgument;
/* Mark start of SW-enforced tags */
out[hw_tag_start] = KM_SECURITY_KEYSTORE;
out[hw_tag_start + 1] = sw_enforced_tag_size_words;
sw_out_start = hw_tag_start + 2;
if (out_len_words < sw_out_start + sw_enforced_tag_size_words)
return SBERR_InvalidArgument;
/* Pass 2: Copy SW enforced tags */
sw_tag_write_pos = 0;
tag_pos = 1;
while (tag_pos < tags_len_words + 1) {
const uint32_t *p_tag = &buf[tag_pos];
uint32_t tag_word = *p_tag;
enum km_tag tag = prefix_get_tag(tag_word);
size_t word_len = prefix_get_word_len(tag_word);
if (tag_is_sw_enforced(tag)) {
if (sw_out_start + sw_tag_write_pos + word_len >
out_len_words)
return SBERR_InvalidArgument;
memcpy(&out[sw_out_start + sw_tag_write_pos], p_tag,
word_len * sizeof(uint32_t));
sw_tag_write_pos += word_len;
}
tag_pos += word_len;
}
*out_written_words = sw_out_start + sw_enforced_tag_size_words;
return SB_OK;
}
/* Derive key encryption key and tag key. */
enum dcrypto_result cryptokey_derive_wrapping(
const struct km *km, const void *tag_data_ptr, size_t tag_size_bytes,
const struct slice application_id, const struct slice application_data,
void *hmac_key, size_t hmac_key_len, void *aes_key, size_t aes_key_len)
{
struct drbg_ctx drbg;
enum dcrypto_result result = DCRYPTO_FAIL;
hmac_drbg_init(&drbg, km->c.drbg_seed_key, sizeof(km->c.drbg_seed_key),
tag_data_ptr, tag_size_bytes, application_id.p,
application_id.n, 4);
/* Mix in KM_TAG_APPLICATION_DATA ingredient */
result = hmac_drbg_generate(&drbg, hmac_key, hmac_key_len,
application_data.p, application_data.n);
if (result != DCRYPTO_OK)
return result;
result = hmac_drbg_generate(&drbg, aes_key, aes_key_len, NULL, 0);
drbg_exit(&drbg);
return result;
}
static enum dcrypto_result cryptokey_generate(struct km *km,
enum km_algorithm alg, void *key,
size_t key_len)
{
enum dcrypto_result result;
do {
/* Generated keys are randomly created */
if (!(fips_rand_bytes(key, key_len)))
return DCRYPTO_FAIL;
result = DCRYPTO_OK;
/* ECC P256 require test of the key candidate to be in range */
if (alg == KM_ALG_EC) {
p256_int d;
if (key_len != sizeof(d))
return DCRYPTO_FAIL;
/* Test P256 key candidate and save its public key. */
result = DCRYPTO_p256_key_from_bytes(
&km->last_pk_x, &km->last_pk_y, &d, key);
memcpy(key, &d, sizeof(d));
if (result != DCRYPTO_RETRY)
break;
}
} while (result != DCRYPTO_OK);
return result;
}
/**
* @brief Generate key and export it into hw-bound blob
*
* @param km Keymint state
* @param alg Key algorithm (only KM_ALG_EC supported)
* @param tags_start Start of Keymint tags used for binding
* @param tag_words Number of Keymint tags in words
* @param application_id KM_TAG_APPLICATION_ID slice
* @param application_data KM_TAG_APPLICATION_DATA slice
* @param blob_size_words in: max size of output; out: actual size
* @param out_buf output buffer for encrypted key blob
* @param key output key buffer for the raw key generated
* @return enum dcrypto_result
*/
static enum dcrypto_result cryptokey_export_bound(
struct km *km, enum km_algorithm alg, uint32_t *tags_start,
size_t tag_words, const struct slice application_id,
const struct slice application_data, size_t *blob_size_words,
uint32_t *out_buf, uint32_t *key)
{
enum dcrypto_result result = DCRYPTO_FAIL;
const struct sha256_digest *digest;
struct {
uint32_t aes_key[KM_AES_ENCRYPT_KEY_WORDS],
hmac_key[KM_HMAC_SIGN_KEY_WORDS];
} k;
uint32_t *blob_size = out_buf;
uint8_t *iv;
struct hmac_sha256_ctx sha;
/* Format of the blob:
* size 1x32 in bytes | iv 4x32 | encrypted key 8x32 | tag 8x32
*/
if (*blob_size_words <
(1 + KM_AES_IV_WORDS + P256_NDIGITS + SHA256_DIGEST_WORDS))
return DCRYPTO_FAIL;
/* Reserve 1 word for size field (in 32-bit words) */
out_buf += 1;
iv = (uint8_t *)out_buf;
result = cryptokey_derive_wrapping(km, tags_start,
tag_words * sizeof(uint32_t),
application_id, application_data,
k.aes_key, sizeof(k.aes_key),
k.hmac_key, sizeof(k.hmac_key));
if (result != DCRYPTO_OK)
goto clean;
result = DCRYPTO_FAIL;
/* Create random IV for AES-CTR */
if (!fips_rand_bytes(iv, KM_AES_IV_SIZE))
goto clean;
out_buf += KM_AES_IV_WORDS;
/* So far hardcoded to 256-bit key */
result = cryptokey_generate(km, alg, key, P256_NBYTES);
if (result != DCRYPTO_OK)
goto clean;
/* Encrypt key */
result = DCRYPTO_aes_ctr((uint8_t *)out_buf, (uint8_t *)k.aes_key,
KM_AES_ENCRYPT_KEY_BITS, iv, (uint8_t *)key,
P256_NBYTES);
if (result != DCRYPTO_OK)
goto clean;
out_buf += P256_NBYTES / sizeof(uint32_t);
/* Add an HMAC tag for integrity */
result = DCRYPTO_hw_hmac_sha256_init(&sha, k.hmac_key,
sizeof(k.hmac_key));
if (result != DCRYPTO_OK)
goto clean;
HMAC_SHA256_update(&sha, key, P256_NBYTES);
digest = HMAC_SHA256_final(&sha);
memcpy(out_buf, digest->b8, SHA256_DIGEST_SIZE);
out_buf += SHA256_DIGEST_WORDS;
*blob_size = (uint8_t *)out_buf - (uint8_t *)iv;
/* Return total size of the blob including length field. */
*blob_size_words = out_buf - blob_size;
result = DCRYPTO_OK;
clean:
always_memset(&k, 0xAA, sizeof(k));
return result;
}
enum dcrypto_result cryptokey_import_bound(
struct km *km, const uint32_t *tags_start, size_t tag_words,
const struct slice application_id, const struct slice application_data,
const uint32_t *blob, size_t blob_size_words, uint32_t *key)
{
enum dcrypto_result result = DCRYPTO_FAIL;
const struct sha256_digest *digest;
struct {
uint32_t aes_key[KM_AES_ENCRYPT_KEY_WORDS],
hmac_key[KM_HMAC_SIGN_KEY_WORDS];
} k;
uint32_t blob_size = blob[0];
uint8_t *iv;
struct hmac_sha256_ctx sha;
size_t key_size;
/* Format of the blob:
* size 1x32 in bytes | iv 4x32 | encrypted key 8x32 | tag 8x32
* Unlike export which is hardcoded, only check for size of fixed sizes
*/
if (blob_size < 1 + KM_AES_IV_WORDS + SHA256_DIGEST_WORDS)
return DCRYPTO_FAIL;
key_size =
(blob_size_words - 1 - KM_AES_IV_WORDS - SHA256_DIGEST_WORDS) *
sizeof(uint32_t);
/* Reserve 1 word for size field (in 32-bit words) */
blob += 1;
iv = (uint8_t *)blob;
result = cryptokey_derive_wrapping(km, tags_start,
tag_words * sizeof(uint32_t),
application_id, application_data,
k.aes_key, sizeof(k.aes_key),
k.hmac_key, sizeof(k.hmac_key));
if (result != DCRYPTO_OK)
goto clean;
result = DCRYPTO_FAIL;
blob += KM_AES_IV_WORDS;
/* Decrypt key */
result = DCRYPTO_aes_ctr((uint8_t *)key, (uint8_t *)k.aes_key,
KM_AES_ENCRYPT_KEY_BITS, iv, (uint8_t *)blob,
key_size);
if (result != DCRYPTO_OK)
goto clean;
blob += key_size / sizeof(uint32_t);
/* Verify integrity using HMAC tag */
result = DCRYPTO_hw_hmac_sha256_init(&sha, k.hmac_key,
sizeof(k.hmac_key));
if (result != DCRYPTO_OK)
goto clean;
HMAC_SHA256_update(&sha, key, key_size);
digest = HMAC_SHA256_final(&sha);
if (memcmp(digest->b8, blob, SHA256_DIGEST_SIZE) != 0) {
CPRINTS("blob tag doesn't match");
result = DCRYPTO_FAIL;
} else
result = DCRYPTO_OK;
clean:
always_memset(&k, 0xAA, sizeof(k));
return result;
}
/* Common primitive to parse Keymint tags */
static enum strongbox_error parse_params(const uint32_t *buf, size_t buf_len,
struct km_key_params *params)
{
uint32_t tag_pos = 0;
while (tag_pos < buf_len) {
const uint32_t *p_tag = &buf[tag_pos];
uint32_t tag_word = *p_tag;
enum km_tag tag = prefix_get_tag(tag_word);
size_t word_len = prefix_get_word_len(tag_word);
enum strongbox_error err;
err = process_tag(tag, params, p_tag);
if (err != SB_OK)
return err;
tag_pos += word_len;
}
return SB_OK;
}
/**
* @brief Parses and decrypts a key blob.
*
* This function validates the structure of a provided key blob, which is
* expected to contain serialized hardware and software-enforced key
* characteristics, along with the encrypted key material. It extracts the
* key parameters into the `params` struct and decrypts the key into the
* `key` buffer.
*
* @param km The keymint context, used for deriving the decryption key.
* @param buf Pointer to the buffer containing the key blob to import.
* @param blob_words The size of the key blob buffer in 32-bit words.
* @param param_tags Pointer to a buffer with additional parameters for the
* import operation (e.g., application ID). Note, that some
* parameters have special rules to combine.
* @param param_tags_words The size of the `param_tags` buffer in 32-bit words.
* @param params Output parameter. A struct to be filled with the parsed key
* attributes from the blob.
* @param key Output parameter. A buffer where the decrypted key material will
* be stored.
* @return SB_OK on successful import, or a strongbox_error code on failure
* (e.g., SBERR_InvalidKeyBlob if the blob is malformed).
*/
static enum strongbox_error import_blob(struct km *km, const uint32_t *buf,
size_t blob_words,
const uint32_t *param_tags,
size_t param_tags_words,
struct km_key_params *params,
uint32_t *key)
{
uint32_t hw_tag_words, sw_tag_words, tag_words, key_blob_size;
enum dcrypto_result result;
enum strongbox_error err;
uint32_t digest_flags, purpose_flags, key_alg, key_size;
/* Blobs have a fixed elements in structure.
* see `process_gen_import_tags`
*/
if (blob_words < KM_KEY_CHARACTERISTICS_WORDS + KM_AES_IV_WORDS +
SHA256_DIGEST_WORDS)
return SBERR_InvalidKeyBlob;
if (buf[0] != KM_SECURITY_STRONGBOX)
return SBERR_InvalidKeyBlob;
hw_tag_words = buf[1];
/* Skip KM_SECURITY_STRONGBOX word + size word */
tag_words = hw_tag_words + 2;
/* Check we have enough to read sw_tag_words */
if (tag_words + 1 >= blob_words)
return SBERR_InvalidKeyBlob;
if (buf[tag_words] != KM_SECURITY_KEYSTORE)
return SBERR_InvalidKeyBlob;
sw_tag_words = buf[tag_words + 1];
/* Skip KM_SECURITY_KEYSTORE word + size word */
tag_words += sw_tag_words + 2;
/* Check we have enough to read key_blob_size */
if (tag_words >= blob_words)
return SBERR_InvalidKeyBlob;
/* Actual encrypted key blob size */
key_blob_size = buf[tag_words];
if (key_blob_size / sizeof(uint32_t) + tag_words + 1 != blob_words)
return SBERR_InvalidKeyBlob;
/* Parse HW-enforced tags only */
err = parse_params(buf + 2, hw_tag_words, params);
if (err != SB_OK)
return err;
/* Additional parameters have special rules to combine. */
digest_flags = params->attrs.digest_flags;
params->attrs.digest_flags = 0;
purpose_flags = params->attrs.purpose_flags;
key_alg = params->attrs.algorithm;
key_size = params->attrs.key_size;
params->attrs.algorithm = 0;
params->attrs.key_size = 0;
/* Process additional parameters to get application id and data */
err = parse_params(param_tags, param_tags_words, params);
if (err != SB_OK)
return err;
/* Only allow bits that were allowed in HW enforced params.
* If no digest is specified, than use HW enforced digest.
*/
if (params->attrs.digest_flags)
params->attrs.digest_flags &= digest_flags;
else
params->attrs.digest_flags = digest_flags;
if (params->attrs.algorithm && params->attrs.algorithm != key_alg)
return SBERR_IncompatibleAlgorithm;
if (params->attrs.key_size && params->attrs.key_size != key_size)
return SBERR_IncompatibleAlgorithm;
params->attrs.algorithm = key_alg;
params->attrs.key_size = key_size;
/* Purpose is HW enforced, don't update it. */
params->attrs.purpose_flags = purpose_flags;
result = cryptokey_import_bound(km, buf, tag_words,
params->application_id,
params->application_data,
buf + tag_words, blob_words - tag_words,
key);
if (result != DCRYPTO_OK)
return SBERR_UnknownError;
return SB_OK;
}
enum attest {
ATTEST_FALSE,
ATTEST_TRUE,
ATTEST_NO_KEY,
};
/**
* @brief Generates a key blob based on provided key parameters.
*
* This function takes a set of KeyMint tags, processes them to create
* KeyCharacteristics, generates a new cryptographic key according to these
* parameters, and then encrypts the key material. The final output is a
* single "key blob" containing both the characteristics and the encrypted key,
* which is written to the output buffer.
*
* @param km The keymint context, used for cryptographic operations.
* @param tags A pointer to a buffer of KeyMint tags specifying the parameters
* for the key to be generated.
* @param tag_words The length of the `tags` buffer in 32-bit words.
* @param out_buf The output buffer where the generated key blob will be
* written. The blob includes a size prefix, key
* characteristics, and the encrypted key material.
* @param out_words As an input, the size of `out_buf` in words. On successful
* return, this is updated with the total number of words
* written to `out_buf`.
* @return SB_OK on success, or a strongbox_error code on failure (e.g.,
* SBERR_UnsupportedKeySize).
*/
static enum strongbox_error generate_key_blob(
struct km *km, const uint32_t *tags, size_t tag_words,
uint32_t *out_buf, size_t *out_words, enum attest attest)
{
struct {
struct km_key_params params;
struct km_key_params sign_params;
p256_int attest_key;
uint32_t key[P256_NDIGITS];
} k;
uint32_t *key_blob_size = out_buf;
size_t blob_start_words = 0;
size_t blob_size_words;
size_t total_words;
size_t out_buf_words = *out_words;
uint32_t *cert_size;
enum dcrypto_result result;
enum strongbox_error err;
always_memset(&k, 0, sizeof(k));
/* Reserve 1 word for size field (in 32-bit words) */
out_buf += 1;
out_buf_words -= 1;
k.params.attrs.algorithm = -1;
err = process_gen_import_tags(km, &k.params, KM_ORIGIN_GENERATED, tags,
tag_words, out_buf, out_buf_words,
&blob_start_words);
if (err != SB_OK)
return err;
err = SBERR_Unimplemented;
/* Return more specific error for unsupported RSA and AES keys.*/
if (k.params.attrs.algorithm == KM_ALG_RSA) {
if (k.params.attrs.key_size != 1024 &&
k.params.attrs.key_size != 2048)
err = SBERR_UnsupportedKeySize;
if (k.params.attrs.rsa_exponent != 3 &&
k.params.attrs.rsa_exponent != 65537)
err = SBERR_InvalidArgument;
}
if (k.params.attrs.algorithm == KM_ALG_AES) {
if ((k.params.attrs.key_size != 128 &&
k.params.attrs.key_size != 192 &&
k.params.attrs.key_size != 256))
err = SBERR_UnsupportedKeySize;
}
if (k.params.attrs.algorithm != KM_ALG_EC)
goto cleanup;
/* Need at least one of the curve_id or
* key size to be specified.
*/
if (k.params.attrs.curve_id != KM_EC_CURVE_P_256 &&
k.params.attrs.key_size != 256)
goto cleanup;
err = SBERR_InsufficientBufferSpace;
if (blob_start_words >= out_buf_words)
goto cleanup;
if (attest == ATTEST_TRUE) {
size_t attest_key_space, attest_key_size = 0;
size_t issuer_subject_start, issuer_subject_size = 0;
/* Check below is already done, but keep it for safety */
if (tags[0] > tag_words - 1)
goto cleanup; /* return SBERR_UnknownError; */
attest_key_space = tag_words - tags[0] - 1;
if (attest_key_space >= 1) {
/* peek into blob's total size */
attest_key_size = tags[tags[0] + 1];
};
if (attest_key_size == 0) {
/* Either we don't have attestation key at all, or its
* size is set to zero. In this case mark that we don't
* have attestation key, so will return an empty
* certificate.
*/
attest = ATTEST_NO_KEY;
} else {
/* Check if attest key blob is plausible */
if (attest_key_size + 1 > attest_key_space) {
err = SBERR_InvalidKeyBlob;
goto cleanup;
}
/* Import attestation key, skipping length */
err = import_blob(km, tags + tags[0] + 2,
attest_key_size, NULL, 0,
&k.sign_params, k.attest_key.a);
if (err != SB_OK)
goto cleanup;
if ((k.sign_params.attrs.purpose_flags &
((1 << KM_PURPOSE_SIGN) |
(1 << KM_PURPOSE_ATTEST_KEY))) == 0) {
err = SBERR_IncompatiblePurpose;
goto cleanup;
}
/* The remainder is the issuer_subject */
attest_key_space -= attest_key_size + 1;
if (attest_key_space < 1) {
err = SBERR_MissingIssuerSubject;
goto cleanup;
}
issuer_subject_start = tags[0] + 2 + attest_key_size;
issuer_subject_size = tags[issuer_subject_start];
if ((issuer_subject_size + 3) / 4 + 1 !=
attest_key_space) {
err = SBERR_InvalidIssuerSubject;
goto cleanup;
}
if (issuer_subject_size > 0)
k.params.certificate_issuer =
SLICE(tags + issuer_subject_start + 1,
issuer_subject_size);
}
}
/* Set max size for the actual key blob */
blob_size_words = out_buf_words - blob_start_words;
/* Create a random key and place it into encrypted key blob with
* security tag. Encrypted blob is bound to all the tags and the
* Application ID and Application Data which are excluded from
* the key characteristics, but provided later with the `begin`
* operation.
*/
result = cryptokey_export_bound(
km, k.params.attrs.algorithm, out_buf, blob_start_words,
k.params.application_id, k.params.application_data,
&blob_size_words, &out_buf[blob_start_words], k.key);
if (result != DCRYPTO_OK) {
err = SBERR_UnknownError;
goto cleanup;
}
if (attest == ATTEST_NO_KEY &&
(k.params.attrs.purpose_flags &
((1 << KM_PURPOSE_SIGN) | (1 << KM_PURPOSE_ATTEST_KEY)))) {
/* If no attestation key is provided and generated key is SIGN
* or ATTEST, use self-signed certificate.
*/
memcpy(k.attest_key.a, k.key, sizeof(k.attest_key));
k.sign_params = k.params;
attest = ATTEST_TRUE;
if (!k.params.certificate_subject.p)
k.params.certificate_subject = g_default_subject;
if (!k.params.certificate_issuer.p)
k.params.certificate_issuer =
k.params.certificate_subject;
}
total_words = blob_start_words + blob_size_words;
/* Total size of key blob in 32-bit words */
*key_blob_size = total_words;
cert_size = out_buf + total_words;
if (attest == ATTEST_TRUE) {
/**
* Tag::CERTIFICATE_SUBJECT the certificate subject. The value
* is a DER encoded SEQUENCE. This value is used when
* generating a self signed certificates. This tag may be
* specified during generateKey and importKey. If not provided
* the subject name shall default to CN="Android Keystore Key".
*/
if (!k.params.certificate_subject.p)
k.params.certificate_subject = g_default_subject;
if (!k.params.certificate_issuer.p) {
err = SBERR_MissingIssuerSubject;
goto cleanup;
}
*cert_size = SB_cert_name(
&k.attest_key, &km->last_pk_x, &km->last_pk_y,
&k.params, (uint8_t *)(cert_size + 1),
(out_buf_words - total_words - 1) * sizeof(uint32_t));
/* Prepend certificate with 32-bit little-endian size field. */
total_words += (*cert_size + 3 + sizeof(*cert_size)) /
sizeof(uint32_t);
} else if (attest == ATTEST_NO_KEY) {
/* Add word for cert_size of 0 */
total_words += 1;
*cert_size = 0;
}
*out_words = total_words + 1; /* + blob size */
err = SB_OK;
cleanup:
always_memset(&k, 0, sizeof(k));
return err;
};
/**
* @brief Implements the core logic for IKeyMintDevice.generateKey().
*
* Parses key generation parameters, creates a hardware-backed key, and returns
* it as an encrypted, versioned, and integrity-protected key blob.
*
* Input Buffer (`buf`):
* A serialized list of Key parameters(tags).
* - [ 4 bytes ] uint32_t tags_len_words: The size of the tag list that
* follows, in 32-bit words.
* - [ n bytes ] Serialized KeyMint tags. Each tag consists of a 32-bit tag
* word followed by an optional, 32-bit aligned payload.
*
* Output Buffer (`buf`):
* A KeyCreationResult structure containing the key blob and an empty
* certificate chain.
* - [ 4 bytes ] uint32_t key_blob_total_words: Total size of the key blob
* and characteristics that follow, in 32-bit words.
* - [ n bytes ] Key blob, containing HW and SW enforced KeyCharacteristics.
* Note: that key blob include KeyCharacteristics as its part, but in AIDL
* KeyCharacteristics are returned separately as key blob format is opaque.
*
* Hardware-Enforced Section:
* out[0]: Security Level (always KM_SECURITY_STRONGBOX)
* out[1]: Length of the hardware-enforced section in words (excluding out[0]
* and out[1]) Subsequent words: Keymint tags that are hardware-enforced. These
* tags have the same format as in the input buffer (tag word followed by data
* payload).
* Software-Enforced Section (starts immediately after the hardware-enforced
* section):
* out[x]: Security Level (always KM_SECURITY_KEYSTORE)
* out[x + 1]: Length of the software-enforced section in words (excluding
* out[x] and out[x + 1])
* Subsequent words: Keymint tags that are software-enforced, using the
* same format as above.
* - [ m bytes ] Encrypted key material, prefixed with its size in bytes.
* - [ 4 bytes ] uint32_t cert_chain_len_bytes: The size of the certificate
* chain in bytes.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
static enum strongbox_error sb_GenerateKey(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
uint32_t out_buf[512];
size_t total_words = ARRAY_SIZE(out_buf);
enum strongbox_error err;
always_memset(out_buf, 0, sizeof(out_buf));
err = generate_key_blob(km, buf, req_len_words, out_buf, &total_words,
true);
if (err != SB_OK)
return err;
if (total_words > buf_size_words)
return SBERR_UnknownError;
memcpy(buf, out_buf, total_words * sizeof(uint32_t));
*out_len_bytes = total_words * sizeof(uint32_t);
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_DeviceGenerateKey, sb_GenerateKey);
/**
* @brief Implements the core logic for IKeyMintDevice::begin().
*
* Starts a cryptographic operation (e.g., signing) with the provided key.
*
* Input Buffer (`buf`):
* - [ 4 bytes ] enum km_purpose: The purpose of the operation (e.g., SIGN).
* - [ 4 bytes ] uint32_t key_blob_words: The size of the key blob that
* follows, in 32-bit words.
* - [ n bytes ] The key blob generated by `generateKey`.
* - [ 4 bytes ] uint32_t additional_params_words: The size of the
* additional parameters list that follows, in 32-bit words.
* - [ m bytes ] Serialized KeyMint tags for additional parameters
* (e.g., APPLICATION_ID).
*
* Output Buffer (`buf`):
* A BeginResult structure.
* - [ 8 bytes ] uint64_t challenge: Currently unused, set to 0.
* - [ 4 bytes ] uint32_t key_params_size_bytes: Size of KeyParameters that
* follow. Currently 0.
* - [ n bytes ] KeyParameters (e.g., IV). Currently empty.
* - [ 4 bytes ] uint32_t operation_id: A unique handle for this operation.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
static enum strongbox_error sb_Begin(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
uint32_t purpose, blob_words, params_words;
struct km_key_params params = { 0 };
enum strongbox_error err;
uint32_t slot;
bool unique = true;
uint32_t operation_id;
/* Minimum size of the input parameters: purpose, blob, sizes */
if (req_len_words < 3 + KM_KEY_CHARACTERISTICS_WORDS)
return SBERR_InvalidArgument;
/* Minimum size of the output: challenge, key params, operation_id */
if (buf_size_words < 4)
return SBERR_InvalidArgument;
/* Check if we have free slots for the operation. */
if (km->used_slots == ((1U << KM_MAX_OPS) - 1))
return SBERR_KeyMaxOpsExceeded;
/* Find free slot */
slot = count_trailing_zeros(~km->used_slots);
purpose = buf[0];
blob_words = buf[1];
if (blob_words + 2 >= req_len_words)
return SBERR_InvalidKeyBlob;
params_words = buf[blob_words + 2];
if (params_words > req_len_words ||
(params_words + blob_words + 2) > req_len_words)
return SBERR_InvalidArgument;
err = import_blob(km, buf + 2, blob_words, buf + blob_words + 3,
params_words, &params, km->ops[slot].key);
if (err != SB_OK)
return err;
/* Check that requested purpose is compatible with the key. */
if ((params.attrs.purpose_flags & (1 << purpose)) == 0)
return SBERR_IncompatiblePurpose;
/* We only support P256 for now */
if (params.attrs.algorithm != KM_ALG_EC)
return SBERR_UnsupportedAlgorithm;
/* TODO: EC keys can be Sign or Agree */
if (purpose != KM_PURPOSE_SIGN)
return SBERR_IncompatiblePurpose;
/* We should have only single flag - SHA256, SHA512 or NONE. */
if (params.attrs.digest_flags != KM_DIGESTFLAG_SHA_2_256 &&
params.attrs.digest_flags != KM_DIGESTFLAG_SHA_2_512 &&
params.attrs.digest_flags != KM_DIGESTFLAG_NONE)
return SBERR_IncompatibleDigest;
/* Generate random and unique operation id. */
do {
if (!fips_rand_bytes(&operation_id, sizeof(operation_id)))
return SBERR_UnknownError;
for (size_t i = 0; i < ARRAY_SIZE(km->ops); i++)
if (km->ops[i].operation_id == operation_id) {
unique = false;
break;
};
} while (!unique);
/* Mark slot as allocated. */
km->used_slots |= 1U << slot;
km->ops[slot].attrs = params.attrs;
/* Initialize hash if requested. */
km->ops[slot].none_ctx.update_size = 0;
if (params.attrs.digest_flags == KM_DIGESTFLAG_SHA_2_256)
SHA256_sw_init(&km->ops[slot].sha256_ctx);
else if (params.attrs.digest_flags == KM_DIGESTFLAG_SHA_2_512)
SHA512_sw_init(&km->ops[slot].sha512_ctx);
km->ops[slot].operation_id = operation_id;
km->ops[slot].purpose = purpose;
/* Unused challenge */
buf[0] = 0;
buf[1] = 0;
/* Size of unused KeyParameters (IV, etc) */
buf[2] = 0;
/* Keymint Operation ID */
buf[3] = operation_id;
*out_len_bytes = 4 * sizeof(buf[0]);
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_DeviceBegin, sb_Begin);
/**
* @brief Implements the core logic for IKeyMintOperation::update().
*
* Provides data to an ongoing cryptographic operation. For signing operations,
* this data is added to the internal digest calculation.
*
* Input Buffer (`buf`):
* - [ 4 bytes ] uint32_t operation_id: The handle from `begin()`.
* - [ 4 bytes ] uint32_t update_size_bytes: The size of the data to process.
* - [ n bytes ] The input data to be added to the operation.
*
* Output Buffer (`buf`):
* - Empty. `*out_len_bytes` is set to 0.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
static enum strongbox_error sb_Update(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
size_t op_index, update_size;
if (req_len_words < 2)
return SBERR_InvalidArgument;
update_size = buf[1];
if (update_size > (req_len_words - 2) * 4)
return SBERR_InvalidArgument;
for (op_index = 0; op_index < ARRAY_SIZE(km->ops); op_index++)
if (km->ops[op_index].operation_id == buf[0])
break;
if (op_index >= ARRAY_SIZE(km->ops))
return SBERR_InvalidOperationHandle;
if (km->ops[op_index].attrs.digest_flags == KM_DIGESTFLAG_SHA_2_256) {
SHA256_sw_update(&km->ops[op_index].sha256_ctx,
(uint8_t *)(buf + 2), update_size);
} else if (km->ops[op_index].attrs.digest_flags ==
KM_DIGESTFLAG_SHA_2_512) {
SHA512_sw_update(&km->ops[op_index].sha512_ctx,
(uint8_t *)(buf + 2), update_size);
} else if (km->ops[op_index].attrs.digest_flags == KM_DIGESTFLAG_NONE) {
if (update_size + km->ops[op_index].none_ctx.update_size >
sizeof(km->ops[op_index].none_ctx.update_context))
return SBERR_InvalidArgument;
memcpy((uint8_t *)(km->ops[op_index].none_ctx.update_context) +
km->ops[op_index].none_ctx.update_size,
buf + 2, update_size);
km->ops[op_index].none_ctx.update_size += update_size;
} else
return SBERR_UnsupportedAlgorithm;
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_OperationUpdate, sb_Update);
/**
* @brief Implements the core logic for IKeyMintOperation::finish().
*
* Finalizes a cryptographic operation. For a signing operation, it processes
* the last piece of data, computes the final digest, signs it, and returns
* the signature.
*
* Input Buffer (`buf`):
* - [ 4 bytes ] uint32_t operation_id: The handle from `begin()`.
* - [ 4 bytes ] uint32_t update_size_bytes: The size of the final data.
* - [ n bytes ] The final input data for the operation. Can be empty.
*
* Output Buffer (`buf`):
* The result of the operation. Specifically for the Strongbox-subset it is:
* - [ 64 bytes ] An ECDSA P-256 signature, composed of the 32-byte 'r'
* value followed by the 32-byte 's' value.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
static enum strongbox_error sb_Finish(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
size_t op_index, update_size;
enum dcrypto_result result;
p256_int p256_digest, p256_r, p256_s;
const uint8_t *sign_data = NULL;
/* Minimum 2 words - Operation Handler and Input Size */
if (req_len_words < 2)
return SBERR_InvalidArgument;
update_size = buf[1];
if (update_size > (req_len_words - 2) * 4)
return SBERR_InvalidArgument;
if (buf_size_words < ECDSA_SIG_BYTES / sizeof(uint32_t))
return SBERR_InvalidArgument;
for (op_index = 0; op_index < ARRAY_SIZE(km->ops); op_index++)
if (km->ops[op_index].operation_id == buf[0])
break;
if (op_index >= ARRAY_SIZE(km->ops))
return SBERR_InvalidOperationHandle;
/* Cleanup slot if found, early, so it is freed on error too. */
km->used_slots &= ~(1u << op_index);
if (km->ops[op_index].attrs.algorithm != KM_ALG_EC)
return SBERR_UnsupportedAlgorithm;
if (km->ops[op_index].attrs.digest_flags == KM_DIGESTFLAG_SHA_2_256) {
SHA256_sw_update(&km->ops[op_index].sha256_ctx,
(uint8_t *)(buf + 2), update_size);
sign_data = SHA256_sw_final(&km->ops[op_index].sha256_ctx)->b8;
} else if (km->ops[op_index].attrs.digest_flags ==
KM_DIGESTFLAG_SHA_2_512) {
SHA512_sw_update(&km->ops[op_index].sha512_ctx,
(uint8_t *)(buf + 2), update_size);
/* Truncate to 32 bytes implicitly */
sign_data = SHA512_sw_final(&km->ops[op_index].sha512_ctx)->b8;
} else if (km->ops[op_index].attrs.digest_flags == KM_DIGESTFLAG_NONE) {
if (update_size + km->ops[op_index].none_ctx.update_size >
sizeof(km->ops[op_index].none_ctx.update_context))
return SBERR_InvalidArgument;
memcpy((uint8_t *)(km->ops[op_index].none_ctx.update_context) +
km->ops[op_index].none_ctx.update_size,
buf + 2, update_size);
km->ops[op_index].none_ctx.update_size += update_size;
sign_data =
(uint8_t *)km->ops[op_index].none_ctx.update_context;
} else
return SBERR_UnsupportedAlgorithm;
p256_from_bin(sign_data, &p256_digest);
result = DCRYPTO_p256_ecdsa_sign((p256_int *)km->ops[op_index].key,
&p256_digest, &p256_r, &p256_s);
p256_to_bin(&p256_r, (uint8_t *)&buf[0]);
p256_to_bin(&p256_s, (uint8_t *)&buf[8]);
if (result != DCRYPTO_OK)
return SBERR_UnknownError;
/* Clean up everything by operation id */
always_memset(&km->ops[op_index].attrs, 0,
sizeof(km->ops[op_index]) -
offsetof(struct km_operation, attrs));
*out_len_bytes = ECDSA_SIG_BYTES;
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_OperationFinish, sb_Finish);
#define U32(a, b, c, d) (((a) | (b << 8) | (c << 16) | (d << 24)))
/**
*
* Definition of the command arguments from AIDL:
* @param out MacedPublicKey macedPublicKey contains the public key of the
* generated key pair, MACed so that generateCertificateRequest can easily
* verify, without the privateKeyHandle, that the contained public key is for
* remote certification.
*
* @return data representing a handle to the private key. The format is
* implementation-defined, but note that specific services may define a
* required format. KeyMint does.
*
* byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey
* macedPublicKey);
*
* The data returned by this function is a concatenation of two parts:
* 1. A handle to the private key, which is a key blob.
* 2. The MacedPublicKey.
*
* The format is as follows, with all fields being 32-bit aligned:
*
* - [ variable size ] Key Blob: A handle to the private key. The first 4 bytes
* of the key blob contain its total size in 32-bit words.
*
* - [ 4 bytes ] MacedPublicKey Length: The size of the MacedPublicKey data in
* bytes.
*
* - [ variable size ] MacedPublicKey Data: The CBOR-encoded MacedPublicKey.
*/
static enum strongbox_error sb_GenerateKeyPair(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
static const uint32_t attest_key_params[] = {
17,
KM_TAG(KM_TAG_PURPOSE, 0),
KM_PURPOSE_ATTEST_KEY,
KM_TAG(KM_TAG_ALGORITHM, 0),
KM_ALG_EC,
KM_TAG(KM_TAG_KEY_SIZE, 0),
256,
KM_TAG(KM_TAG_EC_CURVE, 0),
KM_EC_CURVE_P_256,
KM_TAG(KM_TAG_NO_AUTH_REQUIRED, 0),
KM_TAG(KM_TAG_DIGEST, 0),
KM_DIGEST_SHA_2_256,
KM_TAG(KM_TAG_CERTIFICATE_NOT_BEFORE, 0),
/* We use Unix epoch as the start date of an undefined
* certificate validity period.
*/
0,
0,
KM_TAG(KM_TAG_CERTIFICATE_NOT_AFTER, 0),
/* Per RFC 5280 4.1.2.5, an undefined expiration (not-after)
* field should be set to 9999-12-31T23:59:59Z, which is
* 253402300799000 = 0xE677 D21FD818
*/
0xD21FD818,
0xE677,
};
size_t total_words = buf_size_words;
uint32_t *b32;
uint8_t *b8;
enum strongbox_error err;
struct hmac_sha256_ctx sha;
enum dcrypto_result result;
const struct sha256_digest *digest;
err = generate_key_blob(km, attest_key_params,
ARRAY_SIZE(attest_key_params), buf,
&total_words, false);
if (err != SB_OK)
return err;
/*; - P256 is BE: https://www.secg.org/sec1-v2.pdf#page=19
* (section 2.3.7) PublicKey = { ; COSE_Key [RFC9052 s7]
* 1 : 2, ; Key type : EC2
* 3 : -7, ; Algorithm : ES256
* -1 : 1, ; Curve : P256
* -2 : bstr, ; X coordinate, big-endian
* -3 : bstr, ; Y coordinate, big-endian
* }
* Size of of the CBOR encoding of the `PublicKey`
*/
#define CBOR_PUBLIC_KEY_LEN (13 + ECDSA_SIG_BYTES)
#define CBOR_PUBLIC_KEY_WORDS \
((CBOR_PUBLIC_KEY_LEN + sizeof(uint32_t) - 1) / sizeof(uint32_t))
/**
* MacedPublicKey = [ ; COSE_Mac0 [RFC9052 s6.2]
* protected: bstr .cbor { 1 : 5}, ; Algorithm : HMAC-256
* unprotected: { },
* payload : bstr .cbor PublicKey,
* tag : bstr ; HMAC-256(K_mac, MAC_structure)
* ]
* Size of the CBOR encoding of the MacedPublicKey
*/
#define CBOR_MACED_SIGNED_LEN (CBOR_PUBLIC_KEY_LEN + 10)
#define CBOR_MACED_KEY_LEN (CBOR_MACED_SIGNED_LEN + SHA256_DIGEST_SIZE)
#define CBOR_MACED_KEY_WORDS \
((CBOR_MACED_KEY_LEN + sizeof(uint32_t) - 1) / sizeof(uint32_t))
/* After the key blob add MacedPublicKey, but check we have space. */
if (total_words + 1 + CBOR_MACED_KEY_WORDS > buf_size_words)
return SBERR_UnknownError;
always_memset(buf + total_words, 0,
(1 + CBOR_MACED_KEY_WORDS) * sizeof(uint32_t));
/* Place the length of the Mac'ed key in bytes. */
buf[total_words] = CBOR_MACED_KEY_LEN;
b32 = buf + total_words + 1;
b8 = (uint8_t *)b32;
b32[0] = U32(CBOR_HDR1(CBOR_MAJOR_ARR, 4),
CBOR_HDR1(CBOR_MAJOR_BSTR, 3),
CBOR_HDR1(CBOR_MAJOR_MAP, 1), CBOR_UINT0(1));
b32[1] = U32(CBOR_UINT0(5),
/* unprotected: { } */
CBOR_HDR1(CBOR_MAJOR_MAP, 0),
CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1),
CBOR_PUBLIC_KEY_LEN);
b32[2] = U32(/* Public key, Map header: 5 entries*/
CBOR_HDR1(CBOR_MAJOR_MAP, 5), COSE_KEY_LABEL_KTY,
CBOR_UINT0(2), /* EC2 */
COSE_KEY_LABEL_ALG);
b32[3] = U32(CBOR_NINT0(-7), /* ECDSA w/SHA-256 */
CBOR_NINT0(-1), CBOR_UINT0(1) /* -1 : 1 P256 */,
CBOR_NINT0(-2));
b32[4] = U32(CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1),
32 /* -2 : bstr(32)*/, 0, 0);
p256_to_bin(&km->last_pk_x, b8 + 18);
b8[50] = CBOR_NINT0(-3); /* Y coordinate */
b8[51] = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
b8[52] = 32;
p256_to_bin(&km->last_pk_y, b8 + 53);
/* tag */
b8[85] = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
b8[86] = SHA256_DIGEST_SIZE;
/* Add an HMAC tag for integrity */
result = DCRYPTO_hw_hmac_sha256_init(&sha, km->c.hmac_tag_key,
sizeof(km->c.hmac_tag_key));
if (result != DCRYPTO_OK)
return SBERR_UnknownError;
HMAC_SHA256_update(&sha, b32, CBOR_MACED_SIGNED_LEN);
digest = HMAC_SHA256_final(&sha);
memcpy(b8 + CBOR_MACED_SIGNED_LEN, digest->b8, SHA256_DIGEST_SIZE);
*out_len_bytes = /* key blob, 4-byte len, MACed Key */
(total_words + 1 + CBOR_MACED_KEY_WORDS) * sizeof(uint32_t);
return err;
}
DECLARE_STRONGBOX_COMMAND(SB_RpcGenerateEcdsaP256KeyPair, sb_GenerateKeyPair);
/**
* Implementation of the GSC specific GetDiceChain command.
*
* Due to limits on the TPM command size and the fact that DICE chain is a
* constant for given firmware/device, move it out of the
* GenerateCertificateV2Request to separate command which result can be cached
* by the Strongbox TA.
* This command doesn't take any arguments and returns DICE chain in CBOR
* encoding as is.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*/
enum strongbox_error sb_GetDiceChain(struct km *km, uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
/* No arguments are expected for the command. */
if (req_len_words)
return SBERR_InvalidArgument;
/* DiceCertChain */
*out_len_bytes = get_dice_chain_bytes_for_chain(
(uint8_t *)buf, 0, buf_size_words * sizeof(uint32_t),
BOOT_PARAM_DICE_CHAIN_GSC);
return (*out_len_bytes) ? SB_OK : SBERR_UnknownError;
}
DECLARE_STRONGBOX_COMMAND(SB_GetDiceChain, sb_GetDiceChain);
/**
* Implementation for GenerateCertificateV2Request command.
*
* @param km KeyMint context.
* @param buf Input/Output buffer.
* @param buf_size_words Size of the I/O buffer in 32-bit words.
* @param req_len_words Size of the input data in 32-bit words.
* @param out_len_bytes On success, the number of bytes written to the buffer.
* @return SB_OK on success, or an error code on failure.
*
* generateCertificateRequestV2 creates a certificate signing request to be sent
* to the provisioning server. Implements:
* byte[] generateCertificateRequestV2(in MacedPublicKey[] keysToSign, in byte[]
* challenge);
*
* Input Buffer (`buf`) Format:
* The input buffer is a sequence of fields, all 32-bit aligned.
* - [ 4 bytes ] uint32_t key_count: Number of MACed public keys.
* @param in MacedPublicKey[] keysToSign contains the set of keys to certify.
* The IRemotelyProvisionedComponent must validate the MACs on each key. If any
* entry in the array lacks a valid MAC, the method must return
* STATUS_INVALID_MAC. This method must not accept test keys. If any entry in
* the array is a test key, the method must return
* - [ n times ] Repeated for each key:
* - [ 4 bytes ] uint32_t maced_key_len_bytes: Length of the following
* MACed key.
* - [ m bytes ] uint8_t maced_key[]: The MACed public key data, padded
* to a 4-byte boundary.
* STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
* @param in challenge contains a byte string from the provisioning server which
* will be included in the signed data of the CSR structure. Different
* provisioned backends may use different semantic data for this field, but the
* supported sizes must be between 0 and 64 bytes, inclusive.
* - [ 4 bytes ] uint32_t challenge_len_bytes: Length of the challenge.
* - [ p bytes ] uint8_t challenge[]: The challenge data, padded to a 4-byte
* boundary.
* Strongbox-subset specific:
* - [ 4 bytes ] uint32_t device_info_len_bytes: Length of the DeviceInfo.
* - [ q bytes ] uint8_t device_info[]: The CBOR-encoded DeviceInfo data.
* In the DeviceInfo map fields:
* - "security_level": "strongbox",
* - "vb_state": "green",
* - "bootloader_state": "locked"
* have to be placed last, as future version would append these fields based
* on the actual GSC state.
* generateCertificateRequestV2 creates a SignedData<[challenge,
* CSR]> part of the CSR to be sent to the provisioning server.
* This is part need to be prepended with the
* AuthenticatedRequest<CsrPayload> structure
* [ version: 1, UdsCerts, DiceCertChain, {SignedData<Data>} ]
*
* @return a CBOR Certificate Signing Request (Csr) Signed Data
* serialized into a byte array.
* See generateCertificateRequestV2.cddl for CDDL definitions.
*
*/
static inline struct slice_ref_s slice(const size_t size, const uint8_t *data)
{
return (struct slice_ref_s){ .data = data, .size = size };
}
#define MAX_CSR_PUB_KEYS 20
static enum strongbox_error sb_GenerateCertificateReq(struct km *km,
uint32_t *buf,
size_t buf_size_words,
size_t req_len_words,
size_t *out_len_bytes)
{
size_t key_count, maced_len, device_info_len;
enum dcrypto_result result;
const struct sha256_digest *digest;
struct hmac_sha256_ctx sha;
uint32_t public_key[MAX_CSR_PUB_KEYS][CBOR_PUBLIC_KEY_WORDS];
uint32_t challenge[KM_MAX_CHALLENGE_WORDS], challenge_words,
challenge_len;
uint8_t *b8, *data_to_sign, *data_start;
size_t index, data_len, data_len_csr, data_len_to_sign, prefix_bytes;
static const uint8_t kSigStructFixedHdr[] = {
/* Array header: 4 elements */
CBOR_HDR1(CBOR_MAJOR_ARR, 4),
/* 1. Context: tstr("Signature1") */
CBOR_HDR1(CBOR_MAJOR_TSTR, CDI_SIG_STRUCT_CONTEXT_VALUE_LEN),
'S',
'i',
'g',
'n',
'a',
't',
'u',
'r',
'e',
'1',
/* 2. Body protected: bstr(COSE param) */
/* BSTR of size 3 - see the rest of the struct */
CBOR_HDR1(CBOR_MAJOR_BSTR, 3),
/* Map header: 1 elem */
CBOR_HDR1(CBOR_MAJOR_MAP, 1),
/* Alg: uint(1) => nint(-7) */
COSE_PARAM_LABEL_ALG,
CBOR_NINT0(-7), /* ECDSA w/ SHA-256 */
/* 3. External AAD: Bstr(0 bytes) */
CBOR_HDR1(CBOR_MAJOR_BSTR, 0),
};
/* We should have at least key count + challenge len + DeviceInfo len */
if (req_len_words < 3)
return SBERR_InvalidArgument;
key_count = buf[0];
if (key_count > MAX_CSR_PUB_KEYS)
return SBERR_InvalidArgument;
/* Req is big enough to contain key count, all keys and challenge len */
if (1 + key_count * (CBOR_MACED_KEY_WORDS + 1) >= req_len_words)
return SBERR_InvalidArgument;
/* Check that we have at least space for all the keys */
if (key_count * (CBOR_PUBLIC_KEY_WORDS + 1) > buf_size_words)
return SBERR_InvalidArgument;
index = 1;
for (size_t i = 0; i < key_count; i++) {
maced_len = buf[index];
/* Only support MAC'ed keys we produce with fixed size */
if (maced_len != CBOR_MACED_KEY_LEN)
return SBERR_InvalidArgument;
b8 = (uint8_t *)(buf + index + 1);
/* Check HMAC tag for integrity */
result = DCRYPTO_hw_hmac_sha256_init(
&sha, km->c.hmac_tag_key, sizeof(km->c.hmac_tag_key));
if (result != DCRYPTO_OK)
return SBERR_UnknownError;
HMAC_SHA256_update(&sha, b8, CBOR_MACED_SIGNED_LEN);
digest = HMAC_SHA256_final(&sha);
if (memcmp(digest->b8, b8 + CBOR_MACED_SIGNED_LEN,
SHA256_DIGEST_SIZE) != 0)
return SBERR_RKP_STATUS_INVALID_MAC;
/* Copy public key structure locally */
memcpy(public_key[i], b8 + 8, CBOR_PUBLIC_KEY_LEN);
index += CBOR_MACED_KEY_WORDS + 1;
}
/* Already checked that req is big enough to contain challenge len */
challenge_len = buf[index];
challenge_words =
(challenge_len + sizeof(uint32_t) - 1) / sizeof(uint32_t);
/* Req is big enough to contain challenge (w/ len) + DeviceInfo len */
if (challenge_len > KM_MAX_CHALLENGE_LEN ||
(challenge_words + index + 1 >= req_len_words))
return SBERR_InvalidArgument;
memcpy(challenge, buf + index + 1, challenge_len);
index += challenge_words + 1;
/* Already checked that req is big enough to contain DeviceInfo len */
device_info_len = buf[index];
if (device_info_len / sizeof(uint32_t) + index > req_len_words)
return SBERR_InvalidArgument;
/**
* Csr = AuthenticatedRequest<CsrPayload>
*
* AuthenticatedRequest<T> = [
* version: 1, ; The AuthenticatedRequest CDDL Schema version.
* UdsCerts,
* DiceCertChain,
* --- we only provide SignedData part
* SignedData<[
* challenge: bstr .size (0..64),
* bstr .cbor T,
* ]>,
* ];
*
* ; COSE_Sign1 (untagged) [RFC9052 s4.2]
* SignedData<Data> = [
* protected: bstr .cbor { 1 : AlgorithmES256 },
* unprotected: {},
* payload: bstr .cbor Data / nil,
* ; ECDSA(CDI_Leaf_Priv, SignedDataSigStruct<Data>)
* signature: bstr
* ]
*/
buf[0] = U32(
/* SignedData<Data>, 4 elements array */
CBOR_HDR1(CBOR_MAJOR_ARR, 4),
/* bstr .cbor { 1 : AlgorithmES256 } 43 A10126 */
CBOR_HDR1(CBOR_MAJOR_BSTR, 3), CBOR_HDR1(CBOR_MAJOR_MAP, 1),
CBOR_HDR1(CBOR_MAJOR_UINT, 1));
b8 = (uint8_t *)(buf + 1);
*b8++ = CBOR_NINT0(-7);
/* unprotected: {} */
*b8++ = CBOR_HDR1(CBOR_MAJOR_MAP, 0);
prefix_bytes = 6;
/*
* CsrPayload = [
* version: 3, CertificateType: tstr, ; "keymint" - 10 bytes
* DeviceInfo - device_info_len
* KeysToSign: [ *PublicKey] - 1 + key_count * CBOR_PUBLIC_KEY_LEN
* ]
*/
data_len_csr =
10 + device_info_len + 1 + key_count * CBOR_PUBLIC_KEY_LEN;
/* Calculate length of the payload for .bstr wrapping
* payload: bstr .cbor Data / nil,
* Data is array [challenge, .bstr CsrPayload]
* +1 byte for 2-value array, +2 for challenge .bstr len,
* +2 for CsrPayload .bstr (min)
*/
data_len = 1 + 2 + challenge_len + data_len_csr + 2;
/* Account for the .bstr len in the CsrPayload*/
if (data_len_csr > 255)
data_len++;
if (data_len > 255) {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES2);
*b8++ = (uint8_t)(((data_len) & 0xFF00) >> 8);
*b8++ = (uint8_t)((data_len) & 0x00FF);
prefix_bytes += 3;
} else {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
*b8++ = data_len;
prefix_bytes += 2;
}
data_to_sign = b8;
/* Now temporarily put here prefix of SignedDataSigStruct<Data> */
/* ; Sig_structure for SignedData [ RFC9052 s4.4]
* SignedDataSigStruct<Data> = [
* context: "Signature1",
* protected: bstr .cbor { 1 : AlgorithmES256 },
* external_aad: bstr .size 0,
* payload: bstr .cbor Data / nil,
* ]
*/
memcpy(b8, kSigStructFixedHdr, sizeof(kSigStructFixedHdr));
b8 += sizeof(kSigStructFixedHdr);
if (data_len > 255) {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES2);
*b8++ = (uint8_t)(((data_len) & 0xFF00) >> 8);
*b8++ = (uint8_t)((data_len) & 0x00FF);
data_len_to_sign = data_len + sizeof(kSigStructFixedHdr) + 3;
} else {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
*b8++ = data_len;
data_len_to_sign = data_len + sizeof(kSigStructFixedHdr) + 2;
}
data_start = b8;
/* challenge: bstr .size (0..64) */
*b8++ = CBOR_HDR1(CBOR_MAJOR_ARR, 2);
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
*b8++ = challenge_len;
memcpy(b8, challenge, challenge_len);
b8 += challenge_len;
/* Wrap CsrPayload into .bstr */
if (data_len_csr > 255) {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES2);
*b8++ = (uint8_t)(((data_len_csr) & 0xFF00) >> 8);
*b8++ = (uint8_t)((data_len_csr) & 0x00FF);
} else {
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
*b8++ = data_len_csr;
}
/*
* CsrPayload = [
* version: 3, CertificateType: tstr, ; "keymint"
* DeviceInfo, KeysToSign,
* ]
*/
*b8++ = CBOR_HDR1(CBOR_MAJOR_ARR, 4);
*b8++ = CBOR_HDR1(CBOR_MAJOR_UINT, 3);
*b8++ = CBOR_HDR1(CBOR_MAJOR_TSTR, 7);
*b8++ = 'k';
*b8++ = 'e';
*b8++ = 'y';
*b8++ = 'm';
*b8++ = 'i';
*b8++ = 'n';
*b8++ = 't';
/*
* DeviceInfo = {
* "brand" : tstr,
* "manufacturer" : tstr,
* "product" : tstr,
* "model" : tstr,
* "device" : tstr,
* ; Taken from the AVB values
* "vbmeta_digest": bstr,
* ; Same as android.os.Build.VERSION.release. Not optional for TEE.
* ? "os_version" : tstr, ;
* ; YYYYMM, must match KeyMint OS_PATCHLEVEL
* "system_patch_level" : uint,
* ; YYYYMMDD, must match KeyMint BOOT_PATCHLEVEL
* "boot_patch_level" : uint,
* ; YYYYMMDD, must match KeyMint VENDOR_PATCHLEVEL
* "vendor_patch_level" : uint,
* "security_level" : "tee" / "strongbox",
* ; 1 if secure boot is enforced for the processor that the IRPC
* "fused": 1 / 0,
* "vb_state" : "green" / "yellow" / "orange",
* "bootloader_state" : "locked" / "unlocked",
* }
*
*/
/* Move DeviceInfo - we can make it in-place */
memmove(b8, buf + index + 1, device_info_len);
b8 += device_info_len;
/* KeysToSign = [ * PublicKey ] */
*b8++ = CBOR_HDR1(CBOR_MAJOR_ARR, key_count);
for (size_t i = 0; i < key_count; i++) {
memcpy(b8, public_key[i], CBOR_PUBLIC_KEY_LEN);
b8 += CBOR_PUBLIC_KEY_LEN;
}
*b8++ = CBOR_HDR1(CBOR_MAJOR_BSTR, CBOR_BYTES1);
*b8++ = ECDSA_SIG_BYTES;
if (!sign_with_cdi_key(BOOT_PARAM_DICE_CHAIN_GSC,
slice(data_len_to_sign, data_to_sign), b8))
return SBERR_UnknownError;
/* Now remove signing prefix */
memmove(data_to_sign, data_start, data_len + 2 + ECDSA_SIG_BYTES);
*out_len_bytes = prefix_bytes + data_len + ECDSA_SIG_BYTES + 2;
return SB_OK;
}
DECLARE_STRONGBOX_COMMAND(SB_RpcGenerateCertificateV2Request,
sb_GenerateCertificateReq);
/* Limit the size of long form encoded objects to < 64 kB. */
#define MAX_ASN1_OBJ_LEN_BYTES 3
/* Reserve space for TLV encoding */
#define SEQ_SMALL 2 /* < 128 bytes (1B type, 1B 7-bit length) */
#define SEQ_MEDIUM 3 /* < 256 bytes (1B type, 1B length size, 1B length) */
#define SEQ_LARGE 4 /* < 65536 bytes (1B type, 1B length size, 2B length) */
/* Tag related constants.
* Bits 0..4 - number
* Bit 5 - constructed (1) / primitive (0)
* Bits 6..7 - Class
* 00 - Universal,
* 01 - Application
* 10 - Context-specific
* 11 - Private
*/
enum {
V_ASN1_BOOL = 0x01,
V_ASN1_INT = 0x02,
V_ASN1_BIT_STRING = 0x03,
V_ASN1_BYTES = 0x04, /* OCTET STRING*/
V_ASN1_NULL = 0x05,
V_ASN1_OBJ = 0x06,
V_ASN1_OBJ_DESC = 0x07,
V_ASN1_ENUM = 0x0a,
V_ASN1_UTF8 = 0x0c,
V_ASN1_SEQUENCE = 0x10,
V_ASN1_SET = 0x11,
V_ASN1_ASCII = 0x13,
V_ASN1_TIME = 0x18,
/* Tag number is greater than 30 */
V_ASN1_HIGH_TAG = 0x1F,
V_ASN1_CONSTRUCTED = 0x20,
/* Bits 6-7, Tag class */
V_ASN1_APPLICATION = 0x40,
V_ASN1_CONTEXT_SPECIFIC = 0x80,
V_ASN1_CONTEXT_PRIVATE = 0xC0,
/* Short helpers */
V_BITS = V_ASN1_BIT_STRING,
V_SEQ = V_ASN1_CONSTRUCTED | V_ASN1_SEQUENCE,
V_SET = V_ASN1_CONSTRUCTED | V_ASN1_SET,
/* Context-specific, Constructed, High-tag (tag >=30)*/
V_ASN1_CCH = V_ASN1_CONTEXT_SPECIFIC | V_ASN1_CONSTRUCTED |
V_ASN1_HIGH_TAG,
/* Context-specific, Constructed, low-tag (tag <= 30) */
V_ASN1_CCL = V_ASN1_CONTEXT_SPECIFIC | V_ASN1_CONSTRUCTED,
};
struct asn1 {
uint8_t *p;
size_t n;
};
#define SEQ_START(X, T, L) \
do { \
int __old = (X).n; \
uint8_t __t = (T); \
int __l = (L); \
(X).n += __l;
#define SEQ_END(X) \
(X).n = asn1_seq((X).p + __old, __t, __l, (X).n - __old - __l) + \
__old; \
} \
while (0)
#define OID(X) SLICE(OID_##X, sizeof(OID_##X))
static const uint8_t OID_ecdsa_with_SHA256[8] = { 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x04, 0x03, 0x02 };
static const uint8_t OID_id_ecPublicKey[7] = { 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x02, 0x01 };
static const uint8_t OID_prime256v1[8] = { 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x03, 0x01, 0x07 };
static const uint8_t OID_keyUsage[3] = { 0x55, 0x1D, 0x0F };
static const uint8_t OID_keymint[10] = {
0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x01, 0x11,
};
/**
* Encoding OID: 1.3.6.1.4.1.11129.2.1.30
* The extension value consists of Concise Binary Object Representation (CBOR)
* data that conforms to this Concise Data Definition Language (CDDL) schema:
* {1 : int, ; certificates issued
* 4 : string, ; validated attested entity (STRONG_BOX/TEE) }
* The map is unversioned and new optional fields may be added.
* certs_issued - An approximate number of certificates issued to the device in
* the last 30 days. This value can be used as a signal for potential abuse if
* the value is greater than average by some orders of magnitude.
* validated_attested_entity -
* The validated attested entity is a string that describes the type of device
* that was confirmed by the provisioning server to be attested. For example,
* STRONG_BOX or TEE.
* https://source.android.com/docs/security/features/keystore/attestation#provisioninginfo_extension_schema
*/
static const uint8_t OID_strongbox[10] = {
0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x01, 0x1e,
};
static const uint8_t CBOR_strongbox[15] = { CBOR_HDR1(CBOR_MAJOR_MAP, 2),
CBOR_HDR1(CBOR_MAJOR_UINT, 1),
CBOR_HDR1(CBOR_MAJOR_UINT, 10),
CBOR_HDR1(CBOR_MAJOR_UINT, 4),
CBOR_HDR1(CBOR_MAJOR_TSTR, 10),
'S',
'T',
'R',
'O',
'N',
'G',
'_',
'B',
'O',
'X' };
/* start a tag and return write ptr */
static uint8_t *asn1_tag(struct asn1 *ctx, uint8_t tag)
{
ctx->p[(ctx->n)++] = tag;
return ctx->p + ctx->n;
}
static size_t asn1_len_len(size_t size)
{
if (size < 128)
return 1;
if (size < 256)
return 2;
return 3;
}
/* DER encode length and return encoded size thereof */
static size_t asn1_len(uint8_t *p, size_t size)
{
if (size < 128) {
p[0] = size;
return 1;
} else if (size < 256) {
p[0] = 0x81;
p[1] = size;
return 2;
}
p[0] = 0x82;
p[1] = size >> 8;
p[2] = size;
return 3;
}
/*
* close sequence and move encapsulated data if needed
* return total length.
*/
static size_t asn1_seq(uint8_t *p, uint8_t tag, size_t l, size_t size)
{
size_t tl;
p[0] = tag;
tl = asn1_len(p + 1, size) + 1;
/* TODO: tl > l fail */
if (tl < l)
memmove(p + tl, p + l, size);
return tl + size;
}
static void asn1_be_int(struct asn1 *ctx, uint8_t tag, const uint8_t *b,
size_t n)
{
uint8_t *p = asn1_tag(ctx, tag);
size_t i;
for (i = 0; i < n; ++i) {
if (b[i] != 0)
break;
}
if (i == n) {
/* Special case for zero bytes.*/
*p++ = 1;
*p++ = 0;
} else if (b[i] & 0x80) {
*p++ = n - i + 1;
*p++ = 0;
} else {
*p++ = n - i;
}
for (; i < n; ++i)
*p++ = b[i];
ctx->n = p - ctx->p;
}
/* DER encode (small positive) integer */
static void asn1_int(struct asn1 *ctx, uint32_t val)
{
uint32_t be_val = htobe32(val);
asn1_be_int(ctx, V_ASN1_INT, (const uint8_t *)&be_val, sizeof(be_val));
}
static void asn1_int64(struct asn1 *ctx, uint64_t val)
{
uint32_t be_val[2];
be_val[1] = htobe32((uint32_t)val);
be_val[0] = htobe32((uint32_t)(val >> 32));
asn1_be_int(ctx, V_ASN1_INT, (const uint8_t *)&be_val, sizeof(be_val));
}
static void asn1_enum(struct asn1 *ctx, uint32_t val)
{
uint32_t be_val = htobe32(val);
asn1_be_int(ctx, V_ASN1_ENUM, (const uint8_t *)&be_val, sizeof(be_val));
}
/* DER encode positive p256_int */
static void asn1_p256_int(struct asn1 *ctx, const p256_int *n)
{
uint8_t bn[P256_NBYTES];
p256_to_bin(n, bn);
asn1_be_int(ctx, V_ASN1_INT, bn, P256_NBYTES);
}
/* DER encode p256 signature */
static void asn1_sig(struct asn1 *ctx, const p256_int *r, const p256_int *s)
{
SEQ_START(*ctx, V_SEQ, SEQ_SMALL)
{
asn1_p256_int(ctx, r);
asn1_p256_int(ctx, s);
}
SEQ_END(*ctx);
}
/* DER encode bytes */
static void asn1_bytes(struct asn1 *ctx, uint8_t tag, struct slice slice)
{
const uint8_t *s = (const uint8_t *)slice.p;
size_t n = slice.n;
uint8_t *p = asn1_tag(ctx, tag);
p += asn1_len(p, n);
while (n--)
*p++ = *s++;
ctx->n = p - ctx->p;
}
/* DER encode printable string */
static void asn1_string(struct asn1 *ctx, uint8_t tag, const char *s)
{
size_t n = strlen(s);
asn1_bytes(ctx, tag, SLICE((const uint8_t *)s, n));
}
/* DER encode bytes */
static void asn1_object(struct asn1 *ctx, struct slice obj)
{
uint8_t *p = asn1_tag(ctx, V_ASN1_OBJ);
size_t n = obj.n;
const uint8_t *b = (const uint8_t *)obj.p;
p += asn1_len(p, n);
while (n--)
*p++ = *b++;
ctx->n = p - ctx->p;
}
/* DER encode p256 pk */
static void asn1_pub(struct asn1 *ctx, const p256_int *x, const p256_int *y)
{
uint8_t *p = asn1_tag(ctx, 4); /* uncompressed format */
p256_to_bin(x, p);
p += P256_NBYTES;
p256_to_bin(y, p);
p += P256_NBYTES;
ctx->n = p - ctx->p;
}
/* Verify that the `seq` slice is an asn.1 DER SEQ with the valid length.
* Add it to `ctx` if the verification was successful and return 1.
* Otherwise, return 0.
*
* Only shotr and long length forms are allowed, with up to 4 length bytes,
* indefinite form for the length is not supported.
*/
static int verify_and_add_seq(struct asn1 *ctx, const struct slice *seq)
{
unsigned char *p = (char *)seq->p;
/* 1. Verify */
if (p == NULL || seq->n < 2)
return 0;
if (p[0] != V_SEQ)
return 0;
if (p[1] < 128) {
/* short length form: 0x30 N <N bytes> */
size_t len = p[1];
if (seq->n != 2 + len)
return 0;
} else {
/* long or indefinite length form:
* 0x30 0x80|M <M len bytes = N> <N bytes>
*/
size_t len_len = p[1] & 0x7f;
size_t len;
size_t i;
if (len_len == 0 || len_len > 4)
return 0;
if (seq->n < 2 + len_len)
return 0;
len = p[2 + len_len];
for (i = 1; i < len_len; i++) {
len <<= 8;
len |= p[2 + len_len + i];
}
if (seq->n != 2 + len_len + len)
return 0;
}
/* 2. Add the verified sequence */
memcpy(ctx->p + ctx->n, seq->p, seq->n);
ctx->n += seq->n;
return 1;
}
/*
* Manually writes a context-specific, constructed tag.
* Returns the number of bytes written (1-3).
* Assumes tag_num is > 0.
*
* Example:
* [706] -> BF 85 42 (constructed, context-specific, tag 706)
* [1] -> A1 (constructed, context-specific, tag 1)
*/
static int asn1_write_context_tag(struct asn1 *ctx, uint32_t tag_num)
{
/* Check for 3-byte tag (BF 85 42) */
if (tag_num > 127) {
/* Context-specific, Constructed, High-tag */
ctx->p[ctx->n++] = V_ASN1_CCH;
ctx->p[ctx->n++] = 0x80 | (tag_num >> 7);
ctx->p[ctx->n++] = tag_num & 0x7F;
return 3;
}
/* Check for 2-byte tag (BF 0F) */
if (tag_num > 30) {
ctx->p[ctx->n++] = V_ASN1_CCH;
ctx->p[ctx->n++] = (uint8_t)tag_num;
return 2;
}
/* Check for 1-byte tag (A1)
* Context-specific, Constructed, Low-tag
*/
ctx->p[ctx->n++] = V_ASN1_CCL | (uint8_t)tag_num;
return 1;
}
/*
* Helper to encode: [tag_num] EXPLICIT NULL
*/
static void asn1_explicit_null(struct asn1 *ctx, uint32_t tag_num)
{
/* 1. Write the EXPLICIT tag (e.g., [303]) */
asn1_write_context_tag(ctx, tag_num);
/* 2. Write the length of the inner payload (which is 2 bytes) */
asn1_len(ctx->p + ctx->n, 2);
/* asn1_len for <128 is 1 byte */
ctx->n += 1;
/* 3. Write the inner NULL TLV */
asn1_tag(ctx, V_ASN1_NULL);
/* Length 0 */
ctx->p[ctx->n++] = 0x00;
}
/*
* Helper to encode: [tag_num] EXPLICIT INTEGER
*/
static void asn1_explicit_int(struct asn1 *ctx, uint32_t tag_num, uint64_t val)
{
/* 8-byte int + 0-padding + TLV = max ~12 bytes */
uint8_t int_buf[15];
struct asn1 int_ctx = {
int_buf,
0,
};
/* 1. Encode the inner INTEGER into a temp buffer */
/* int_ctx.n now holds the length of the inner TLV */
asn1_int64(&int_ctx, val);
/* 2. Write the EXPLICIT tag (e.g., [706]) */
asn1_write_context_tag(ctx, tag_num);
/* 3. Write the outer length (which is the length of the inner TLV) */
ctx->n += asn1_len(ctx->p + ctx->n, int_ctx.n);
/* 4. Write the inner TLV (the value) */
memcpy(ctx->p + ctx->n, int_buf, int_ctx.n);
ctx->n += int_ctx.n;
}
/*
* Helper to encode: [tag_num] EXPLICIT OCTET_STRING
* Used for: [709] attestationApplicationId, [710] attestationIdBrand, etc.
*/
static void asn1_explicit_bytes(struct asn1 *ctx, uint32_t tag_num,
struct slice data)
{
/* Calculate wrapped length */
size_t len = 1 + data.n + asn1_len_len(data.n);
/* Write the EXPLICIT tag (e.g., [709]) */
asn1_write_context_tag(ctx, tag_num);
/* Write the outer length (the length of the inner TLV) */
ctx->n += asn1_len(ctx->p + ctx->n, len);
/* Write actual data*/
asn1_bytes(ctx, V_ASN1_BYTES, data);
}
static void add_key_purpose(struct asn1 *ctx, uint32_t km_purpose)
{
uint32_t purpose = 0;
/* id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
* KeyUsage ::= BIT STRING {
* digitalSignature (0),
* nonRepudiation (1), -- recent editions of X.509 have
* -- renamed this bit to contentCommitment
* keyEncipherment (2),
* dataEncipherment (3),
* keyAgreement (4),
* keyCertSign (5),
* cRLSign (6),
* encipherOnly (7),
* decipherOnly (8) }
*/
if (km_purpose & (1U << KM_PURPOSE_SIGN))
purpose |= 0x80;
if (km_purpose & (1U << KM_PURPOSE_ATTEST_KEY))
purpose |= 0x04;
if (km_purpose & (1U << KM_PURPOSE_AGREE_KEY))
purpose |= 0x08;
if (km_purpose & (1U << KM_PURPOSE_WRAP_KEY))
purpose |= 0x20;
if ((km_purpose &
((1U << KM_PURPOSE_ENCRYPT) | (1U << KM_PURPOSE_DECRYPT))) ==
((1U << KM_PURPOSE_ENCRYPT) | (1U << KM_PURPOSE_DECRYPT)))
purpose |= 0x10;
else if (km_purpose & (1U << KM_PURPOSE_ENCRYPT))
purpose |= 0x01;
else if (km_purpose & (1U << KM_PURPOSE_DECRYPT))
purpose |= 0x8000;
/* Wrap in OCTET_STRING */
SEQ_START(*ctx, V_ASN1_BYTES, SEQ_MEDIUM)
{
SEQ_START(*ctx, V_BITS, SEQ_MEDIUM)
{
if (purpose > 0xff) {
/* 7 unused bits */
ctx->p[ctx->n++] = 7;
ctx->p[ctx->n++] = purpose & 0xff;
ctx->p[ctx->n++] = (purpose >> 8) & 0xff;
} else {
ctx->p[ctx->n++] = __builtin_ctz(purpose);
ctx->p[ctx->n++] = purpose & 0xff;
}
}
SEQ_END(*ctx);
}
SEQ_END(*ctx);
}
static void add_set_int(struct asn1 *ctx, uint32_t tag, uint32_t value)
{
uint8_t *p;
/* Write the EXPLICIT tag [1] */
asn1_write_context_tag(ctx, tag);
/* Location where to output length. It is known to be short */
p = ctx->p + ctx->n;
ctx->n++;
SEQ_START(*ctx, V_SET, SEQ_SMALL)
{
/* Up to 30 bits in length to have 1-byte encoding. */
for (size_t i = 0; i < 30; i++) {
if (value & (1U << i))
asn1_int(ctx, i);
}
}
SEQ_END(*ctx);
*p = ctx->n - (p - ctx->p) - 1;
}
static void add_root_of_trust(struct asn1 *ctx, bool device_locked,
enum km_vb_state verified, struct slice data)
{
/* Calculate wrapped length */
size_t len = 10 + (data.n + asn1_len_len(data.n)) * 2;
/* Write the EXPLICIT tag [704] */
asn1_write_context_tag(ctx, 704);
/* Write the outer length (the length of the inner TLV) */
ctx->n += asn1_len(ctx->p + ctx->n, len);
/*
* RootOfTrust ::= SEQUENCE {
* // A secure hash of the public key used to verify the integrity and
* // authenticity of all code that executes during device boot up as
* // part of Verified Boot.
* verifiedBootKey OCTET_STRING,
* deviceLocked BOOLEAN,
* verifiedBootState VerifiedBootState,
* // A digest of all data protected by Verified Boot. For devices that
* // use the Android Verified Boot reference implementation, this
* // field contains the VBMeta digest.
* verifiedBootHash OCTET_STRING, }
* VerifiedBootState ::= ENUMERATED {
* Verified (0),
* SelfSigned (1),
* Unverified (2),
* Failed (3), }
*/
SEQ_START(*ctx, V_SEQ, SEQ_SMALL)
{
/* TODO: Should we use some other constant? */
asn1_bytes(ctx, V_ASN1_BYTES, data);
ctx->p[ctx->n++] = V_ASN1_BOOL;
ctx->p[ctx->n++] = 1;
ctx->p[ctx->n++] = (device_locked) ? 255 : 0;
asn1_be_int(ctx, V_ASN1_ENUM, (uint8_t *)&verified, 1);
asn1_bytes(ctx, V_ASN1_BYTES, data);
}
SEQ_END(*ctx);
}
static void add_km_enforcements(struct asn1 *ctx,
const struct km_key_params *params)
{
/* softwareEnforced */
SEQ_START(*ctx, V_SEQ, SEQ_LARGE)
{
if (params->creationDateTime)
asn1_explicit_int(ctx, 701, params->creationDateTime);
if (params->attestationApplicationId.n)
asn1_explicit_bytes(ctx, 709,
params->attestationApplicationId);
if (params->moduleHash.n)
asn1_explicit_bytes(ctx, 724, params->moduleHash);
}
SEQ_END(*ctx);
/* hardwareEnforced */
SEQ_START(*ctx, V_SEQ, SEQ_LARGE)
{
add_set_int(ctx, 1, params->attrs.purpose_flags);
asn1_explicit_int(ctx, 2, params->attrs.algorithm);
if (params->attrs.key_size)
asn1_explicit_int(ctx, 3, params->attrs.key_size);
if (params->attrs.digest_flags)
add_set_int(ctx, 5, params->attrs.digest_flags);
if (params->attrs.curve_id)
asn1_explicit_int(ctx, 10, params->attrs.curve_id);
if (params->rollbackResistance)
asn1_explicit_null(ctx, 303);
if (params->earlyBootOnly)
asn1_explicit_null(ctx, 304);
if (params->noAuthRequired)
asn1_explicit_null(ctx, 503);
asn1_explicit_int(ctx, 702, params->origin);
add_root_of_trust(ctx, true, KM_VB_VERIFIED,
params->rootOfTrust);
if (params->osVersion)
asn1_explicit_int(ctx, 705, params->osVersion);
if (params->osPatchLevel)
asn1_explicit_int(ctx, 706, params->osPatchLevel);
if (params->attestationIdBrand.n)
asn1_explicit_bytes(ctx, 710,
params->attestationIdBrand);
if (params->attestationIdDevice.n)
asn1_explicit_bytes(ctx, 711,
params->attestationIdDevice);
if (params->attestationIdProduct.n)
asn1_explicit_bytes(ctx, 712,
params->attestationIdProduct);
if (params->attestationIdSerial.n)
asn1_explicit_bytes(ctx, 713,
params->attestationIdSerial);
if (params->attestationIdImei.n)
asn1_explicit_bytes(ctx, 714,
params->attestationIdImei);
if (params->attestationIdMeid.n)
asn1_explicit_bytes(ctx, 715,
params->attestationIdMeid);
if (params->attestationIdManufacturer.n)
asn1_explicit_bytes(ctx, 716,
params->attestationIdManufacturer);
if (params->attestationIdModel.n)
asn1_explicit_bytes(ctx, 717,
params->attestationIdModel);
if (params->vendorPatchLevel)
asn1_explicit_int(ctx, 718, params->vendorPatchLevel);
if (params->bootPatchLevel)
asn1_explicit_int(ctx, 719, params->vendorPatchLevel);
}
SEQ_END(*ctx);
}
/* https://source.android.com/docs/security/features/keystore/attestation */
static void add_km_extension(struct asn1 *ctx,
const struct km_key_params *params)
{
/* Wrap in OCTET_STRING */
SEQ_START(*ctx, V_ASN1_BYTES, SEQ_LARGE)
{
SEQ_START(*ctx, V_SEQ, SEQ_LARGE)
{
/* attestationVersion */
asn1_int(ctx, 400);
/* attestationSecurityLevel */
asn1_enum(ctx, KM_SECURITY_STRONGBOX);
/* keyMintVersion */
asn1_int(ctx, 400);
/* keyMintSecurityLevel */
asn1_enum(ctx, KM_SECURITY_STRONGBOX);
asn1_bytes(ctx, V_ASN1_BYTES,
params->attestationChallenge);
/* A privacy-sensitive device identifier that system
* apps can request at key generation time. If the
* unique ID is not requested, this field is empty.
* The Unique ID is a 128-bit value that identifies the
* device, but only for a limited period of time. The
* value is computed with: HMAC_SHA256(T || C || R, HBK)
*/
asn1_bytes(ctx, V_ASN1_BYTES, params->uniqueId);
add_km_enforcements(ctx, params);
}
SEQ_END(*ctx);
}
SEQ_END(*ctx);
}
static void add_cert_extension(struct asn1 *ctx,
const struct km_key_params *params)
{
/* Certificate extension:
* extensions [3] EXPLICIT Extensions OPTIONAL
*/
SEQ_START(*ctx, V_ASN1_CCL | 3, SEQ_LARGE)
{
/* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */
SEQ_START(*ctx, V_SEQ, SEQ_LARGE)
{
/*
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING }
*/
SEQ_START(*ctx, V_SEQ, SEQ_SMALL)
{
asn1_object(ctx, OID(keyUsage));
add_key_purpose(ctx,
params->attrs.purpose_flags);
}
SEQ_END(*ctx);
SEQ_START(*ctx, V_SEQ, SEQ_LARGE)
{
asn1_object(ctx, OID(keymint));
add_km_extension(ctx, params);
}
SEQ_END(*ctx);
SEQ_START(*ctx, V_SEQ, SEQ_SMALL)
{
asn1_object(ctx, OID(strongbox));
asn1_bytes(ctx, V_ASN1_BYTES,
SLICE_OBJ(CBOR_strongbox));
}
SEQ_END(*ctx);
}
SEQ_END(*ctx);
}
SEQ_END(*ctx);
}
static size_t SB_cert_name(const p256_int *d, const p256_int *pk_x,
const p256_int *pk_y,
const struct km_key_params *params, uint8_t *cert,
const size_t n)
{
struct asn1 ctx = { cert, 0 };
struct sha256_ctx sha;
p256_int h, r, s;
struct drbg_ctx drbg;
enum dcrypto_result result;
size_t total_slice_len = 0;
for (size_t i = KM_SLICE_TAG_CERT_START; i < ARRAY_SIZE(slice_tags);
i++) {
struct slice *slice = (struct slice *)(((uint8_t *)params) +
slice_tags[i].offset);
total_slice_len += slice->n;
}
/* Rough check that total variable inputs + known fixed sized fields
* would fit into available space. It is not exact and adds small
* buffer to account for variable length ASN.1. */
if (total_slice_len + 532 > n)
return 0;
/**
* RFC 5280: https://datatracker.ietf.org/doc/html/rfc5280
* Certificate ::= SEQUENCE {
* tbsCertificate TBSCertificate,
* signatureAlgorithm AlgorithmIdentifier,
* signatureValue BIT STRING }
*/
SEQ_START(ctx, V_SEQ, SEQ_LARGE)
{ /* outer seq */
/*
* Grab current pointer to data to hash later.
* Note this will fail if cert body + cert sign is less
* than 256 bytes (SEQ_MEDIUM) -- not likely.
*/
uint8_t *body = ctx.p + ctx.n;
/**
* TBSCertificate ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* serialNumber CertificateSerialNumber,
* signature AlgorithmIdentifier,
* issuer Name,
* validity Validity,
* subject Name,
* subjectPublicKeyInfo SubjectPublicKeyInfo,
* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version MUST be v2 or v3
* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
* -- If present, version MUST be v2 or v3
* extensions [3] EXPLICIT Extensions OPTIONAL
* -- If present, version MUST be v3
* }
*/
SEQ_START(ctx, V_SEQ, SEQ_LARGE)
{
/* X509 v3 */
SEQ_START(ctx, 0xa0, SEQ_SMALL)
{
asn1_int(&ctx, 2);
}
SEQ_END(ctx);
/* Serial number */
if (params->certificate_serial.n)
asn1_be_int(&ctx, V_ASN1_INT,
params->certificate_serial.p,
params->certificate_serial.n);
else
asn1_int(&ctx, 1);
/* Signature algorithm
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm
* OPTIONAL }
*/
SEQ_START(ctx, V_SEQ, SEQ_SMALL)
{
asn1_object(&ctx, OID(ecdsa_with_SHA256));
}
SEQ_END(ctx);
/* Issuer: Same as the subject field of the batch
* attestation key.
*/
if (!verify_and_add_seq(&ctx,
&params->certificate_issuer))
return 0;
/* Expiry */
SEQ_START(ctx, V_SEQ, SEQ_SMALL)
{
asn1_string(&ctx, V_ASN1_TIME,
"20000101000000Z");
asn1_string(&ctx, V_ASN1_TIME,
"20991231235959Z");
}
SEQ_END(ctx);
/* Subject */
if (!verify_and_add_seq(&ctx,
&params->certificate_subject))
return 0;
/* Subject pk */
SEQ_START(ctx, V_SEQ, SEQ_SMALL)
{
/* pk parameters */
SEQ_START(ctx, V_SEQ, SEQ_SMALL)
{
asn1_object(&ctx, OID(id_ecPublicKey));
asn1_object(&ctx, OID(prime256v1));
}
SEQ_END(ctx);
/* pk bits */
SEQ_START(ctx, V_BITS, SEQ_SMALL)
{
/* No unused bit at the end */
asn1_tag(&ctx, 0);
asn1_pub(&ctx, pk_x, pk_y);
}
SEQ_END(ctx);
}
SEQ_END(ctx);
add_cert_extension(&ctx, params);
}
SEQ_END(ctx); /* Cert body */
/* Sign all of cert body */
SHA256_sw_init(&sha);
SHA256_sw_update(&sha, body, (ctx.p + ctx.n) - body);
p256_from_bin(SHA256_sw_final(&sha)->b8, &h);
hmac_drbg_init_rfc6979(&drbg, d, &h);
result = dcrypto_p256_ecdsa_sign(&drbg, d, &h, &r, &s);
drbg_exit(&drbg);
if (result != DCRYPTO_OK)
return 0;
/* Append X509 signature */
SEQ_START(ctx, V_SEQ, SEQ_SMALL);
asn1_object(&ctx, OID(ecdsa_with_SHA256));
SEQ_END(ctx);
SEQ_START(ctx, V_BITS, SEQ_SMALL)
{
/* Number of unused bits at the end (0-7). */
asn1_tag(&ctx, 0);
asn1_sig(&ctx, &r, &s);
}
SEQ_END(ctx);
}
SEQ_END(ctx); /* end of outer seq */
return ctx.n;
}