core/riot/riot_key_manager.c (304 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "platform_api.h"
#include "riot_key_manager.h"
#include "riot_logging.h"
#include "common/certificate.h"
/**
* Free the memory used for CA certificate data.
*
* @param cert The certificate data to free.
*/
static void riot_key_manager_free_ca_cert (struct der_cert *cert)
{
platform_free ((void*) cert->cert);
cert->cert = NULL;
}
/**
* Load the certificates from keystore and check if they create a authenticated certificate chain.
* If the chain is authenticated, update the identity keys using the stored certificates.
*
* @param riot The key manager to authenticate.
*
* @return 0 if authentication completed without errors or an error code.
*/
static int riot_key_manager_authenticate_stored_certificates (const struct riot_key_manager *riot)
{
uint8_t *signed_devid;
size_t devid_length;
struct x509_ca_certs chain;
struct x509_certificate cert;
int status;
platform_mutex_lock (&riot->state->store_lock);
/* Load the keys we have in the keystore. */
status = riot->keystore->load_key (riot->keystore, 0, &signed_devid, &devid_length);
if (status == 0) {
status = riot->keystore->load_key (riot->keystore, 1,
(uint8_t**) &riot->state->root_ca.cert, &riot->state->root_ca.length);
if (status == 0) {
status = riot->keystore->load_key (riot->keystore, 2,
(uint8_t**) &riot->state->intermediate_ca.cert,
&riot->state->intermediate_ca.length);
if ((status != 0) && (status != KEYSTORE_NO_KEY) && (status != KEYSTORE_BAD_KEY)) {
riot_key_manager_free_ca_cert (&riot->state->root_ca);
platform_free (signed_devid);
platform_mutex_unlock (&riot->state->store_lock);
return status;
}
}
else {
platform_free (signed_devid);
if ((status == KEYSTORE_NO_KEY) || (status == KEYSTORE_BAD_KEY)) {
status = RIOT_KEY_MANAGER_NO_ROOT_CA;
}
platform_mutex_unlock (&riot->state->store_lock);
return status;
}
}
else {
if ((status == KEYSTORE_NO_KEY) || (status == KEYSTORE_BAD_KEY)) {
status = RIOT_KEY_MANAGER_NO_SIGNED_DEVICE_ID;
}
platform_mutex_unlock (&riot->state->store_lock);
return status;
}
platform_mutex_unlock (&riot->state->store_lock);
/* Validate that the signed Device ID is valid for the current certificate chain. */
status = riot->x509->load_certificate (riot->x509, &cert, riot->state->keys.alias_cert,
riot->state->keys.alias_cert_length);
if (status != 0) {
goto auth_load_error;
}
status = riot->x509->init_ca_cert_store (riot->x509, &chain);
if (status != 0) {
goto auth_free_cert;
}
status = riot->x509->add_root_ca (riot->x509, &chain, riot->state->root_ca.cert,
riot->state->root_ca.length);
if (status != 0) {
goto auth_free_store;
}
if (riot->state->intermediate_ca.cert) {
status = riot->x509->add_intermediate_ca (riot->x509, &chain,
riot->state->intermediate_ca.cert, riot->state->intermediate_ca.length);
if (status != 0) {
goto auth_free_store;
}
}
status = riot->x509->add_intermediate_ca (riot->x509, &chain, signed_devid, devid_length);
if (status != 0) {
goto auth_free_store;
}
status = riot->x509->authenticate (riot->x509, &cert, &chain);
if (status == 0) {
platform_mutex_lock (&riot->state->auth_lock);
if (!riot->state->static_devid) {
platform_free ((void*) riot->state->keys.devid_cert);
}
riot->state->keys.devid_cert = signed_devid;
riot->state->keys.devid_cert_length = devid_length;
riot->state->static_devid = false;
platform_mutex_unlock (&riot->state->auth_lock);
riot->x509->release_ca_cert_store (riot->x509, &chain);
riot->x509->release_certificate (riot->x509, &cert);
}
else {
goto auth_free_store;
}
return 0;
auth_free_store:
riot->x509->release_ca_cert_store (riot->x509, &chain);
auth_free_cert:
riot->x509->release_certificate (riot->x509, &cert);
auth_load_error:
platform_free (signed_devid);
riot_key_manager_free_ca_cert (&riot->state->root_ca);
riot_key_manager_free_ca_cert (&riot->state->intermediate_ca);
return status;
}
/**
* Store and authenticate an identity certificate.
*
* @param riot The key manager to update.
* @param id The ID for the certificate to store.
* @param cert The certificate DER data.
* @param length The length of the certificate data.
*
* @return 0 if the certificate was stored and authentication completed successfully or an error
* code.
*/
static int riot_key_manager_store_certificate (const struct riot_key_manager *riot, int id,
const uint8_t *cert, size_t length)
{
int status;
if ((riot == NULL) || (cert == NULL) || (length == 0)) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
if (riot->state->root_ca.cert) {
return RIOT_KEY_MANAGER_KEYSTORE_LOCKED;
}
platform_mutex_lock (&riot->state->store_lock);
status = riot->keystore->save_key (riot->keystore, id, cert, length);
platform_mutex_unlock (&riot->state->store_lock);
return status;
}
/**
* Initialize DICE device key management state and load signed certificates from the keystore, if
* available.
*
* @param riot The key manager with state to initialize.
* @param keys The device keys generated by DICE layer 0.
* @param static_keys Flag indicating if the keys are stored in static buffers.
*
* @return 0 if the manager was successfully initialized or an error code.
*/
static int riot_key_manager_init_cert_state (const struct riot_key_manager *riot,
const struct riot_keys *keys, bool static_keys)
{
int status;
if ((riot == NULL) || (keys == NULL) || (riot->state == NULL) || (riot->keystore == NULL) ||
(riot->x509 == NULL)) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
memset (riot->state, 0, sizeof (*riot->state));
status = platform_mutex_init (&riot->state->store_lock);
if (status != 0) {
return status;
}
status = platform_mutex_init (&riot->state->auth_lock);
if (status != 0) {
platform_mutex_free (&riot->state->store_lock);
return status;
}
riot->state->static_keys = static_keys;
riot->state->static_devid = static_keys;
memcpy (&riot->state->keys, keys, sizeof (riot->state->keys));
status = riot_key_manager_authenticate_stored_certificates (riot);
debug_log_create_entry ((status == 0) ? DEBUG_LOG_SEVERITY_INFO : DEBUG_LOG_SEVERITY_WARNING,
DEBUG_LOG_COMPONENT_RIOT, RIOT_LOGGING_DEVID_AUTH_STATUS, status, 0);
return 0;
}
/**
* Initialize RIoT device key management and load signed certificates from the keystore, if
* available.
*
* @param riot The RIoT key manager to initialize.
* @param state Variable context for the key manager.
* @param keystore The storage to use for identity certificates.
* @param keys The device keys generated by DICE layer 0.
* @param x509 The X.509 engine to use for certificate operations.
* @param extra_csr Optional list of CSRs (or other binary data) that can be exported by the device.
* The CSRs in this list will be accessible starting with CSR command index 1, since 0 is for the
* Device ID CSR.
* @param csr_count The number of extra CSRs in the list.
* @param static_keys Flag indicating if the keys are stored in static buffers.
*
* @return 0 if the manager was successfully initialized or an error code.
*/
static int riot_key_manager_init_certs (struct riot_key_manager *riot,
struct riot_key_manager_state *state, const struct keystore *keystore,
const struct riot_keys *keys, const struct x509_engine *x509,
const struct der_cert *const extra_csr, size_t csr_count, bool static_keys)
{
if ((riot == NULL) || (state == NULL) || (keystore == NULL) || (keys == NULL) ||
(x509 == NULL)) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
memset (riot, 0, sizeof (struct riot_key_manager));
riot->state = state;
riot->keystore = keystore;
riot->x509 = x509;
riot->extra_csr = extra_csr;
riot->csr_count = csr_count;
return riot_key_manager_init_cert_state (riot, keys, static_keys);
}
/**
* Initialize the manager for DICE device identity keys.
*
* Keys are provided in dynamically allocated buffers that will be owned by the key manager.
* Releasing the key manager will also release these key buffers.
*
* @param riot The identity key manager to initialize.
* @param state Variable context for the identity key manager. This must be uninitialized.
* @param keystore The storage to use for identity certificates.
* @param keys The device keys generated by DICE layer 0. This structure should not be accessed
* externally after a successful call. It is best to make this a temporary structure.
* @param x509 The X.509 engine to use for certificate operations.
* @param extra_csr Optional list of CSRs (or other binary data) that can be exported by the device.
* The CSRs in this list will be accessible starting with CSR command index 1, since 0 is for the
* Device ID CSR.
* @param csr_count The number of extra CSRs in the list.
*
* @return 0 if the manager was successfully initialized or an error code.
*/
int riot_key_manager_init (struct riot_key_manager *riot, struct riot_key_manager_state *state,
const struct keystore *keystore, const struct riot_keys *keys, const struct x509_engine *x509,
const struct der_cert *const extra_csr, size_t csr_count)
{
return riot_key_manager_init_certs (riot, state, keystore, keys, x509, extra_csr, csr_count,
false);
}
/**
* Initialize the manager for DICE device identity keys.
*
* Keys are provided in static buffers.
*
* @param riot The identity key manager to initialize.
* @param state Variable context for the identity key manager. This must be uninitialized.
* @param keystore The storage to use for identity certificates.
* @param keys The device keys generated by DICE layer 0. This structure should not be accessed
* externally after a successful call. It is best to make this a temporary structure.
* @param x509 The X.509 engine to use for certificate operations.
* @param extra_csr Optional list of CSRs (or other binary data) that can be exported by the device.
* The CSRs in this list will be accessible starting with CSR command index 1, since 0 is for the
* Device ID CSR.
* @param csr_count The number of extra CSRs in the list.
*
* @return 0 if the manager was successfully initialized or an error code.
*/
int riot_key_manager_init_static_keys (struct riot_key_manager *riot,
struct riot_key_manager_state *state, const struct keystore *keystore,
const struct riot_keys *keys, const struct x509_engine *x509,
const struct der_cert *const extra_csr, size_t csr_count)
{
return riot_key_manager_init_certs (riot, state, keystore, keys, x509, extra_csr, csr_count,
true);
}
/**
* Initialize only the state fora DICE device identity key manager. The rest of the manager is
* assumed to have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* Keys are provided in dynamically allocated buffers that will be owned by the key manager.
* Releasing the key manager will also release these key buffers.
*
* @param riot The key manager that contains the state to initialize.
* @param keys The device keys generated by DICE layer 0. This structure should not be accessed
* externally after a successful call. It is best to make this a temporary structure.
*
* @return 0 if the manager state was successfully initialized or an error code.
*/
int riot_key_manager_init_state (const struct riot_key_manager *riot, const struct riot_keys *keys)
{
return riot_key_manager_init_cert_state (riot, keys, false);
}
/**
* Initialize only the state fora DICE device identity key manager. The rest of the manager is
* assumed to have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* Keys are provided in static buffers.
*
* @param riot The key manager that contains the state to initialize.
* @param keys The device keys generated by DICE layer 0. This structure should not be accessed
* externally after a successful call. It is best to make this a temporary structure.
*
* @return 0 if the manager state was successfully initialized or an error code.
*/
int riot_key_manager_init_state_static_keys (const struct riot_key_manager *riot,
const struct riot_keys *keys)
{
return riot_key_manager_init_cert_state (riot, keys, true);
}
/**
* Release the resources used by a device identity key manager. The stored device certificates will
* not be released.
*
* @param riot The key manager to initialize.
*/
void riot_key_manager_release (const struct riot_key_manager *riot)
{
if (riot) {
if (!riot->state->static_devid) {
platform_free ((void*) riot->state->keys.devid_cert);
}
if (!riot->state->static_keys) {
platform_free ((void*) riot->state->keys.devid_csr);
platform_free ((void*) riot->state->keys.alias_key);
platform_free ((void*) riot->state->keys.alias_cert);
}
riot_key_manager_free_ca_cert (&riot->state->root_ca);
riot_key_manager_free_ca_cert (&riot->state->intermediate_ca);
platform_mutex_free (&riot->state->store_lock);
platform_mutex_free (&riot->state->auth_lock);
}
}
/**
* Store the signed Device ID certificate.
*
* Once an authorized chain has been stored, further attempts to store a signed Device ID
* certificate will fail.
*
* @param riot The key manager to update.
* @param dev_id The DER encoded data for the signed Device ID X.509 certificate.
* @param length The length of the certificate DER.
*
* @return 0 if the Device ID certificate was successfully stored or an error code.
*/
int riot_key_manager_store_signed_device_id (const struct riot_key_manager *riot,
const uint8_t *dev_id, size_t length)
{
return riot_key_manager_store_certificate (riot, 0, dev_id, length);
}
/**
* Store the Root CA certificate used to authenticate the Device ID.
*
* Once an authorized chain has been stored, further attempts to store a Root CA certificate will
* fail.
*
* @param riot The key manager to update.
* @param dev_id The DER encoded data for the Root CA X.509 certificate.
* @param length The length of the certificate DER.
*
* @return 0 if the Root CA certificate was successfully stored or an error code.
*/
int riot_key_manager_store_root_ca (const struct riot_key_manager *riot, const uint8_t *root_ca,
size_t length)
{
return riot_key_manager_store_certificate (riot, 1, root_ca, length);
}
/**
* Store the Intermediate CA certificate used to sign the Device ID.
*
* Once an authorized chain has been stored, further attempts to store a Root CA certificate will
* fail.
*
* @param riot The key manager to update.
* @param dev_id The DER encoded data for the Intermediate CA X.509 certificate.
* @param length The length of the certificate DER.
*
* @return 0 if the Intermediate CA certificate was successfully stored or an error code.
*/
int riot_key_manager_store_intermediate_ca (const struct riot_key_manager *riot,
const uint8_t *intr_ca, size_t length)
{
return riot_key_manager_store_certificate (riot, 2, intr_ca, length);
}
/**
* Verify the the stored certificate chain. If all stored certificates create an authorized chain,
* the identity certificates exposed by the manager will be updated to match the stored ones.
*
* Once the certificate chain has been verified, further attempts to store certificates will fail.
*
* Updates to the certificate chain will be blocked until the device identity keys are not in use.
*
* @param riot The key manager that will be verified.
*
* @return 0 if the chain was successfully verified or an error code.
*/
int riot_key_manager_verify_stored_certs (const struct riot_key_manager *riot)
{
if (riot == NULL) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
return riot_key_manager_authenticate_stored_certificates (riot);
}
/**
* Erase all stored certificates. Certificates loaded in memory will not be affected.
*
* @param riot The key manager to update.
*
* @return 0 if all certificates were successfully erased or an error code.
*/
int riot_key_manager_erase_all_certificates (const struct riot_key_manager *riot)
{
int status;
if (riot == NULL) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
platform_mutex_lock (&riot->state->store_lock);
status = riot->keystore->erase_key (riot->keystore, 0);
if (status != 0) {
goto exit;
}
status = riot->keystore->erase_key (riot->keystore, 1);
if (status != 0) {
goto exit;
}
status = riot->keystore->erase_key (riot->keystore, 2);
exit:
platform_mutex_unlock (&riot->state->store_lock);
return status;
}
/**
* Get the device identity keys. Updates to the RIoT keys will be blocked until the caller
* indicates they are finished using them by calling riot_key_manager_release_riot_keys().
*
* @param riot The key manager to query.
*
* @return The device keys or null. If not null, riot_key_manager_release_riot_keys() must be
* called after the keys are used.
*/
const struct riot_keys* riot_key_manager_get_riot_keys (const struct riot_key_manager *riot)
{
if (riot) {
/* Since a lock is being taken, only one external component can access the keys at a time.
* If multiple modules ever need to access the keys simultaneously, this lock will need to
* only be used to protect a reference count on the keys structure. */
platform_mutex_lock (&riot->state->auth_lock);
return &riot->state->keys;
}
else {
return NULL;
}
}
/**
* Indicate that the device identity keys are not currently in use.
*
* @param riot The key manager to update.
* @param keys The keys being released.
*/
void riot_key_manager_release_riot_keys (const struct riot_key_manager *riot,
const struct riot_keys *keys)
{
if ((riot != NULL) && (keys == &riot->state->keys)) {
platform_mutex_unlock (&riot->state->auth_lock);
}
}
/**
* Get the root CA certificate for identity keys.
*
* @param riot The key manager to query.
*
* @return The root CA certificate or null if there is no root CA. The certificate memory is owned
* by the key manager and must not be freed by the user.
*/
const struct der_cert* riot_key_manager_get_root_ca (const struct riot_key_manager *riot)
{
if (riot && riot->state->root_ca.cert) {
return &riot->state->root_ca;
}
else {
return NULL;
}
}
/**
* Get the intermediate CA certificate used to sign the Device ID.
*
* @param riot The key manager to query.
*
* @return The intermediate CA certificate or null if there is no intermediate CA. The certificate
* memory is owned by the key manager and must not be freed by the user.
*/
const struct der_cert* riot_key_manager_get_intermediate_ca (const struct riot_key_manager *riot)
{
if (riot && riot->state->intermediate_ca.cert) {
return &riot->state->intermediate_ca;
}
else {
return NULL;
}
}
/**
* Get the data for a specific CSR provided by the device.
*
* @param riot The key manager to query.
* @param index Index for the requested CSR. Index 0 will always be the Device ID CSR.
* @param csr Output for the CSR data. The CSR memory is owned by the key manager and must not be
* freed or modified by the user.
*
* @return 0 if the CSR data was retrieved successfully or an error code.
*/
int riot_key_manager_get_csr (const struct riot_key_manager *riot, size_t index,
struct der_cert *csr)
{
if ((riot == NULL) || (csr == NULL)) {
return RIOT_KEY_MANAGER_INVALID_ARGUMENT;
}
if (index == 0) {
csr->cert = riot->state->keys.devid_csr;
csr->length = riot->state->keys.devid_csr_length;
}
else if (riot->extra_csr != NULL) {
if (index <= riot->csr_count) {
memcpy (csr, &riot->extra_csr[index - 1], sizeof (*csr));
}
else {
return RIOT_KEY_MANAGER_UNKNOWN_CSR;
}
}
else {
return RIOT_KEY_MANAGER_UNKNOWN_CSR;
}
return 0;
}