core/riot/riot_core_common.c (304 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "platform_api.h" #include "riot_core_common.h" #include "crypto/kdf.h" /** * The label to use for the Device ID KDF from the CDI hash. */ static const char DEVICE_ID_KDF_LABEL[] = "DEVICE ID"; /** * The label to use for the Alias KDF from the FWID HMAC. */ static const char ALIAS_KDF_LABEL[] = "ALIAS KEY"; /** * The context to use for DICE KDFs. */ static const char DICE_KDF_CONTEXT[] = "DICE"; /** * Create the self-signed X.509 certificate for the Device ID key. Before the certificate can be * generated the following must be initialized: * - state->tcb needs to have the layer 0 TcbInfo structure. * - state->cdi_hash needs to contain the hash of the CDI. * - state->dev_id needs to have an initialized ECC private key context. * * @param riot The DICE layer 0 handler to use for cert creation. * * @return 0 if the certificate was created successfully or an error code. */ int riot_core_common_create_device_id_certificate (const struct riot_core_common *riot) { uint8_t serial_num[HASH_MAX_HASH_LEN]; char common_name[BASE64_LENGTH (SHA512_HASH_LENGTH)]; int status; riot->state->dev_id_valid = true; status = riot->ecc->get_private_key_der (riot->ecc, &riot->state->dev_id, &riot->state->dev_id_der, &riot->state->dev_id_length); if (status != 0) { return status; } status = hash_generate_hmac (riot->hash, riot->state->cdi_hash, riot->state->digest_length, RIOT_CORE_SERIAL_NUM_KDF_DATA, RIOT_CORE_SERIAL_NUM_KDF_DATA_LENGTH, riot->state->kdf_algo, serial_num, sizeof (serial_num)); if (status != 0) { return status; } status = riot->base64->encode (riot->base64, serial_num, riot->state->digest_length, (uint8_t*) common_name, sizeof (common_name)); if (status != 0) { return status; } if (strlen (common_name) > X509_MAX_COMMON_NAME) { memcpy (riot->state->dev_id_name, common_name, X509_MAX_COMMON_NAME); riot->state->dev_id_name[X509_MAX_COMMON_NAME] = '\0'; } else { strcpy (riot->state->dev_id_name, common_name); } status = riot->x509->create_self_signed_certificate (riot->x509, &riot->state->dev_id_cert, riot->state->dev_id_der, riot->state->dev_id_length, riot->state->hash_algo, serial_num, 8, riot->state->dev_id_name, X509_CERT_CA, riot->dev_id_ext, riot->dev_id_ext_count); if (status != 0) { return status; } riot->state->dev_id_cert_valid = true; return 0; } int riot_core_common_generate_device_id (const struct riot_core *riot, const uint8_t *cdi, size_t length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; uint8_t cdi_kdf[ECC_MAX_KEY_LENGTH]; int status; uint8_t first; if ((core == NULL) || (length == 0)) { return RIOT_CORE_INVALID_ARGUMENT; } /* In order to accommodate CDI buffers that are at address 0, we can't just directly hash the * buffer pointer. Instead, we copy the first byte locally and add it to the hash, then add * the rest of the buffer. This removes the need to copy the CDI locally, which could be * dangerous since the CDI length is a parameter. */ status = hash_start_new_hash (core->hash, core->state->hash_algo); if (status != 0) { return status; } first = *cdi; status = core->hash->update (core->hash, &first, 1); if (status != 0) { goto cdi_error; } riot_core_clear (&first, 1); status = core->hash->update (core->hash, cdi + 1, length - 1); if (status != 0) { goto cdi_error; } status = core->hash->finish (core->hash, core->state->cdi_hash, sizeof (core->state->cdi_hash)); if (status != 0) { goto cdi_error; } status = kdf_nist800_108_counter_mode (core->hash, core->state->kdf_algo, core->state->cdi_hash, core->state->digest_length, (uint8_t*) DEVICE_ID_KDF_LABEL, sizeof (DEVICE_ID_KDF_LABEL) - 1, (uint8_t*) DICE_KDF_CONTEXT, sizeof (DICE_KDF_CONTEXT) - 1, cdi_kdf, core->key_length); if (status != 0) { return status; } if (core->key_length == ECC_KEY_LENGTH_521) { /* For ECC-521 keys, 528 bits of key data is generated. The upper 7 bits need to be masked * off. */ cdi_kdf[0] &= 0x01; } status = core->ecc->generate_derived_key_pair (core->ecc, cdi_kdf, core->key_length, &core->state->dev_id, NULL); if (status != 0) { return status; } riot_core_clear (cdi_kdf, sizeof (cdi_kdf)); return riot_core_common_create_device_id_certificate (core); cdi_error: core->hash->cancel (core->hash); return status; } int riot_core_common_get_device_id_csr (const struct riot_core *riot, const uint8_t *oid, size_t oid_length, uint8_t **csr, size_t *length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; if ((core == NULL) || (csr == NULL) || (length == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } if (core->state->dev_id_der == NULL) { return RIOT_CORE_NO_DEVICE_ID; } return core->x509->create_csr (core->x509, core->state->dev_id_der, core->state->dev_id_length, core->state->hash_algo, core->state->dev_id_name, X509_CERT_CA, oid, oid_length, core->dev_id_ext, core->dev_id_ext_count, csr, length); } int riot_core_common_get_device_id_cert (const struct riot_core *riot, uint8_t **device_id, size_t *length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; if ((core == NULL) || (device_id == NULL) || (length == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } if (!core->state->dev_id_cert_valid) { return RIOT_CORE_NO_DEVICE_ID; } return core->x509->get_certificate_der (core->x509, &core->state->dev_id_cert, device_id, length); } int riot_core_common_generate_alias_key (const struct riot_core *riot, const uint8_t *fwid, size_t length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; uint8_t fwid_hmac[HASH_MAX_HASH_LEN]; uint8_t alias_kdf[ECC_MAX_KEY_LENGTH]; char common_name[BASE64_LENGTH (SHA512_HASH_LENGTH)]; int status; if ((riot == NULL) || (fwid == NULL) || (length == 0)) { return RIOT_CORE_INVALID_ARGUMENT; } if (!core->state->dev_id_cert_valid) { return RIOT_CORE_NO_DEVICE_ID; } status = hash_generate_hmac (core->hash, core->state->cdi_hash, core->state->digest_length, fwid, length, core->state->kdf_algo, fwid_hmac, sizeof (fwid_hmac)); if (status != 0) { return status; } status = kdf_nist800_108_counter_mode (core->hash, core->state->kdf_algo, fwid_hmac, core->state->digest_length, (uint8_t*) ALIAS_KDF_LABEL, sizeof (ALIAS_KDF_LABEL) - 1, (uint8_t*) DICE_KDF_CONTEXT, sizeof (DICE_KDF_CONTEXT) - 1, alias_kdf, core->key_length); if (status != 0) { return status; } if (core->key_length == ECC_KEY_LENGTH_521) { /* For ECC-521 keys, 528 bits of key data is generated. The upper 7 bits need to be masked * off. */ alias_kdf[0] &= 0x01; } status = core->ecc->generate_derived_key_pair (core->ecc, alias_kdf, core->key_length, &core->state->alias_key, NULL); if (status != 0) { return status; } core->state->alias_key_valid = true; status = core->ecc->get_private_key_der (core->ecc, &core->state->alias_key, &core->state->alias_der, &core->state->alias_length); if (status != 0) { return status; } status = hash_generate_hmac (core->hash, fwid_hmac, core->state->digest_length, RIOT_CORE_SERIAL_NUM_KDF_DATA, RIOT_CORE_SERIAL_NUM_KDF_DATA_LENGTH, core->state->kdf_algo, alias_kdf, sizeof (alias_kdf)); if (status != 0) { return status; } status = core->base64->encode (core->base64, alias_kdf, core->state->digest_length, (uint8_t*) common_name, sizeof (common_name)); if (status != 0) { return status; } if (strlen (common_name) > X509_MAX_COMMON_NAME) { common_name[X509_MAX_COMMON_NAME] = '\0'; } status = core->x509->create_ca_signed_certificate (core->x509, &core->state->alias_cert, core->state->alias_der, core->state->alias_length, alias_kdf, 8, common_name, X509_CERT_END_ENTITY, core->state->dev_id_der, core->state->dev_id_length, core->state->hash_algo, &core->state->dev_id_cert, core->alias_ext, core->alias_ext_count); if (status != 0) { return status; } core->state->alias_cert_valid = true; return 0; } int riot_core_common_get_alias_key (const struct riot_core *riot, uint8_t **key, size_t *length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; if ((core == NULL) || (key == NULL) || (length == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } if (core->state->alias_der == NULL) { return RIOT_CORE_NO_ALIAS_KEY; } *key = platform_malloc (core->state->alias_length); if (*key == NULL) { return RIOT_CORE_NO_MEMORY; } memcpy (*key, core->state->alias_der, core->state->alias_length); *length = core->state->alias_length; return 0; } int riot_core_common_get_alias_key_cert (const struct riot_core *riot, uint8_t **alias_key, size_t *length) { const struct riot_core_common *core = (const struct riot_core_common*) riot; if ((core == NULL) || (alias_key == NULL) || (length == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } if (!core->state->alias_cert_valid) { return RIOT_CORE_NO_ALIAS_KEY; } return core->x509->get_certificate_der (core->x509, &core->state->alias_cert, alias_key, length); } /** * Initialize RIoT Core to be ready for RIoT operations. * * @param riot RIoT Core instance to initialize. * @param state Variable context for the DICE handler. This must be uninitialized. * @param hash The hash engine to use with RIoT Core. * @param ecc The ECC engine to use with RIoT Core. * @param x509 The X.509 certificate engine to use with RIoT Core. * @param base64 The base64 encoding engine to use with RIoT Core. * @param key_length Length of the DICE keys that should be created. * @param device_id_ext A list of additional, custom extensions that should be added to the * Device ID certificate and CSR. At minimum, this should include the DICE TcbInfo extension for * layer 0. * @param device_id_ext_count The number of custom extensions to add to the Device ID certificate * and CSR. * @param alias_ext A list of additional, custom extensions that should be added to the * Alias certificate. At minimum, this should include the DICE TcbInfo extension for layer 1. * @param alias_ext_count The number of custom extensions to add to the Alias certificate. * * @return 0 if RIoT Core was been initialize successfully or an error code. */ int riot_core_common_init (struct riot_core_common *riot, struct riot_core_common_state *state, const struct hash_engine *hash, const struct ecc_engine *ecc, const struct x509_engine *x509, const struct base64_engine *base64, size_t key_length, const struct x509_extension_builder *const *device_id_ext, size_t device_id_ext_count, const struct x509_extension_builder *const *alias_ext, size_t alias_ext_count) { if ((riot == NULL) || (state == NULL) || (hash == NULL) || (ecc == NULL) || (x509 == NULL) || (base64 == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } memset (riot, 0, sizeof (struct riot_core_common)); riot->state = state; riot->hash = hash; riot->ecc = ecc; riot->base64 = base64; riot->x509 = x509; riot->dev_id_ext = device_id_ext; riot->dev_id_ext_count = device_id_ext_count; riot->alias_ext = alias_ext; riot->alias_ext_count = alias_ext_count; riot->key_length = key_length; riot->base.generate_device_id = riot_core_common_generate_device_id; riot->base.get_device_id_csr = riot_core_common_get_device_id_csr; riot->base.get_device_id_cert = riot_core_common_get_device_id_cert; riot->base.generate_alias_key = riot_core_common_generate_alias_key; riot->base.get_alias_key = riot_core_common_get_alias_key; riot->base.get_alias_key_cert = riot_core_common_get_alias_key_cert; return riot_core_common_init_state (riot); } /** * Initialize only the variable state of a DICE layer 0 handler. The rest of the DICE handler * is assumed to have already been initialized. * * This would generally be used with a statically initialized instance. * * @param riot The DICE handler that contains the state to initialize. * * @return 0 if the DICE state was successfully initialized or an error code. */ int riot_core_common_init_state (const struct riot_core_common *riot) { if ((riot == NULL) || (riot->state == NULL) || (riot->hash == NULL) || (riot->ecc == NULL) || (riot->x509 == NULL) || (riot->base64 == NULL)) { return RIOT_CORE_INVALID_ARGUMENT; } /* These cases should also be caught by the X.509 engine, but check it here to fail fast. */ if (((riot->dev_id_ext_count != 0) && (riot->dev_id_ext == NULL)) || ((riot->alias_ext_count != 0) && (riot->alias_ext == NULL))) { return RIOT_CORE_INVALID_ARGUMENT; } memset (riot->state, 0, sizeof (struct riot_core_common_state)); switch (riot->key_length) { case ECC_KEY_LENGTH_256: riot->state->hash_algo = HASH_TYPE_SHA256; riot->state->kdf_algo = HMAC_SHA256; break; #if (ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_384) && defined HASH_ENABLE_SHA384 case ECC_KEY_LENGTH_384: riot->state->hash_algo = HASH_TYPE_SHA384; riot->state->kdf_algo = HMAC_SHA384; break; #endif #if (ECC_MAX_KEY_LENGTH >= ECC_KEY_LENGTH_521) && defined HASH_ENABLE_SHA512 case ECC_KEY_LENGTH_521: riot->state->hash_algo = HASH_TYPE_SHA512; riot->state->kdf_algo = HMAC_SHA512; break; #endif default: return RIOT_CORE_UNSUPPORTED_KEY_LENGTH; } /* The hash algorithm is known to be good, so this won't fail. */ riot->state->digest_length = hash_get_hash_length (riot->state->hash_algo); return 0; } /** * Release RIoT core and zeroize all internal state with private data. * * It is imperative that all RIoT Core instances be released before starting the next application * stage, even if it's not necessary from a resource management perspective. Releasing the RIoT * Core instance ensures that private data in memory is zeroized. * * @param riot The RIoT Core to release. */ void riot_core_common_release (const struct riot_core_common *riot) { if (riot != NULL) { if (riot->state->dev_id_der) { riot_core_clear (riot->state->dev_id_der, riot->state->dev_id_length); platform_free (riot->state->dev_id_der); } if (riot->state->dev_id_valid) { riot->ecc->release_key_pair (riot->ecc, &riot->state->dev_id, NULL); } if (riot->state->dev_id_cert_valid) { riot->x509->release_certificate (riot->x509, &riot->state->dev_id_cert); } if (riot->state->alias_der) { platform_free (riot->state->alias_der); } if (riot->state->alias_key_valid) { riot->ecc->release_key_pair (riot->ecc, &riot->state->alias_key, NULL); } if (riot->state->alias_cert_valid) { riot->x509->release_certificate (riot->x509, &riot->state->alias_cert); } riot_core_clear (riot->state, sizeof (struct riot_core_common_state)); } }