core/manifest/manifest_verification.c (284 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 "manifest_logging.h"
#include "manifest_verification.h"
#include "common/buffer_util.h"
#include "common/type_cast.h"
#include "common/unused.h"
int manifest_verification_verify_signature (const struct signature_verification *verification,
const uint8_t *digest, size_t length, const uint8_t *signature, size_t sig_length)
{
const struct manifest_verification *manifest =
(const struct manifest_verification*) verification;
int status;
if ((verification == NULL) || (digest == NULL) || (length == 0) || (signature == NULL) ||
(sig_length == 0)) {
return MANIFEST_VERIFICATION_INVALID_ARGUMENT;
}
platform_mutex_lock (&manifest->state->lock);
if (manifest->state->stored_key.key_data) {
status = manifest->manifest_verify->set_verification_key (manifest->manifest_verify,
&manifest->state->stored_key.key->pub_key, manifest->state->stored_key.pub_key_length);
if (status != 0) {
goto exit;
}
status = manifest->manifest_verify->verify_signature (manifest->manifest_verify, digest,
length, signature, sig_length);
if ((status == 0) || (status != SIG_VERIFICATION_BAD_SIGNATURE)) {
/* If verification was successful or verification encountered some error that caused it
* to not complete, bail on any further verification. */
goto exit;
}
}
if (manifest->state->default_valid) {
status = manifest->manifest_verify->set_verification_key (manifest->manifest_verify,
&manifest->default_key->key->pub_key, manifest->default_key->pub_key_length);
if (status != 0) {
goto exit;
}
status = manifest->manifest_verify->verify_signature (manifest->manifest_verify, digest,
length, signature, sig_length);
}
exit:
manifest->manifest_verify->set_verification_key (manifest->manifest_verify, NULL, 0);
platform_mutex_unlock (&manifest->state->lock);
return status;
}
int manifest_verification_set_verification_key (const struct signature_verification *verification,
const uint8_t *key, size_t length)
{
UNUSED (verification);
UNUSED (key);
UNUSED (length);
return SIG_VERIFICATION_UNSUPPORTED;
}
int manifest_verification_is_key_valid (const struct signature_verification *verification,
const uint8_t *key, size_t length)
{
UNUSED (verification);
UNUSED (key);
UNUSED (length);
return SIG_VERIFICATION_UNSUPPORTED;
}
void manifest_verification_on_manifest_activated (const struct pfm_observer *observer,
const struct manifest *active)
{
const struct manifest_verification *manifest =
TO_DERIVED_TYPE (observer, const struct manifest_verification, base_observer);
uint8_t digest[HASH_MAX_HASH_LEN] = {0};
int digest_length;
uint8_t signature[RSA_MAX_KEY_LENGTH] = {0}; // Using RSA should support all known signature lengths.
int sig_length;
int status = 0;
/* Whenever a new manifest is activated, check to see if the stored key should be revoked in
* favor of the default manifest key. Execute the revocation, if appropriate.
*
* No need for null checks since the observable manifest will ensure valid arguments. */
platform_mutex_lock (&manifest->state->lock);
if (manifest->state->stored_key.key_data && manifest->state->default_valid) {
digest_length = active->get_hash (active, manifest->hash, digest, sizeof (digest));
if (ROT_IS_ERROR (digest_length)) {
status = digest_length;
goto exit;
}
sig_length = active->get_signature (active, signature, sizeof (signature));
if (ROT_IS_ERROR (sig_length)) {
status = sig_length;
goto exit;
}
status = manifest->manifest_verify->set_verification_key (manifest->manifest_verify,
&manifest->default_key->key->pub_key, manifest->default_key->pub_key_length);
if (status != 0) {
goto exit;
}
status = manifest->manifest_verify->verify_signature (manifest->manifest_verify, digest,
digest_length, signature, sig_length);
manifest->manifest_verify->set_verification_key (manifest->manifest_verify, NULL, 0);
if (status == 0) {
status = manifest->keystore->save_key (manifest->keystore, manifest->key_id,
manifest->default_key->key_data, manifest->default_key->key_data_length);
platform_free ((void*) manifest->state->stored_key.key_data);
manifest->state->stored_key.key_data = NULL;
manifest->state->save_failed = (status != 0);
}
if (status == SIG_VERIFICATION_BAD_SIGNATURE) {
/* If the verification or key revocation could not be completed, fail and log the
* error. A bad signature does not represent an error case. */
status = 0;
}
}
else if (manifest->state->save_failed) {
status = manifest->keystore->save_key (manifest->keystore, manifest->key_id,
manifest->default_key->key_data, manifest->default_key->key_data_length);
if (status == 0) {
manifest->state->save_failed = false;
}
}
exit:
buffer_zeroize (digest, sizeof (digest));
buffer_zeroize (signature, sizeof (signature));
platform_mutex_unlock (&manifest->state->lock);
if (status != 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_MANIFEST,
MANIFEST_LOGGING_KEY_REVOCATION_FAIL, manifest->key_id, status);
}
}
void manifest_verification_on_update_start (const struct firmware_update_observer *observer,
int *update_allowed)
{
const struct manifest_verification *manifest =
TO_DERIVED_TYPE (observer, const struct manifest_verification, base_update);
int status;
/* Block firmware updates if the active verification key has not been successfully saved to the
* keystore. Otherwise, a firmware update could change the verification key and cause
* verification errors for the active manifest. */
platform_mutex_lock (&manifest->state->lock);
if (manifest->state->save_failed) {
status = manifest->keystore->save_key (manifest->keystore, manifest->key_id,
manifest->default_key->key_data, manifest->default_key->key_data_length);
if (status == 0) {
manifest->state->save_failed = false;
}
else if (*update_allowed == 0) {
*update_allowed = status;
}
}
platform_mutex_unlock (&manifest->state->lock);
}
/**
* Verify that a key to be used for manifest verification is valid.
*
* @param verification The instance to use for key verification.
* @param key The key to check.
* @param root_key The root key used to verify the manifest verification keys.
* @param root_key_length Length of the root verification key.
*
* @return 0 if the key is valid or an error code.
*/
static int manifest_verification_verify_key (const struct manifest_verification *verification,
const struct manifest_verification_key *key, const uint8_t *root_key, size_t root_key_length)
{
int status;
/* Check that the key is endorsed by the root key. */
status = signature_verification_verify_message (verification->manifest_verify,
verification->hash, key->sig_hash, key->key_data, key->key_data_length - key->sig_length,
root_key, root_key_length, key->signature, key->sig_length);
if (status != 0) {
return status;
}
/* Ensure the key is compatible with the verification instance. */
return verification->manifest_verify->is_key_valid (verification->manifest_verify,
&key->key->pub_key, key->pub_key_length);
}
/**
* Initialize verification and key management for firmware manifests.
*
* @param verification The verification instance to initialize.
* @param state Variable context for verification. This must be uninitialized.
* @param hash The hash engine to use for validation.
* @param sig_verify The handler to use for key and manifest signature verification.
* @param root_key The root key used to verify the manifest verification keys.
* @param root_key_length Length of the root verification key.
* @param manifest_key Manifest verification key that will be used if no other key is active. This
* key can also revoke an existing key if it is a higher version.
* @param manifest_keystore The keystore that holds the active manifest validation key.
* @param key_id The ID of the manifest key in the keystore.
*
* @return 0 if manifest verification was successfully initialized or an error code.
*/
int manifest_verification_init (struct manifest_verification *verification,
struct manifest_verification_state *state, const struct hash_engine *hash,
const struct signature_verification *sig_verify, const uint8_t *root_key,
size_t root_key_length, const struct manifest_verification_key *manifest_key,
const struct keystore *manifest_keystore, int key_id)
{
if (verification == NULL) {
return MANIFEST_VERIFICATION_INVALID_ARGUMENT;
}
memset (verification, 0, sizeof (struct manifest_verification));
verification->base_verify.verify_signature = manifest_verification_verify_signature;
verification->base_verify.set_verification_key = manifest_verification_set_verification_key;
verification->base_verify.is_key_valid = manifest_verification_is_key_valid;
/* PFM, CFM, and PCD observers have the same structure and this module only depends on common
* manifest APIs, so save memory by using the same observer instance and handler function. */
verification->base_observer.on_pfm_activated =
(void (*) (const struct pfm_observer*, const struct pfm*))
manifest_verification_on_manifest_activated;
verification->base_update.on_update_start = manifest_verification_on_update_start;
verification->state = state;
verification->default_key = manifest_key;
verification->manifest_verify = sig_verify;
verification->hash = hash;
verification->keystore = manifest_keystore;
verification->key_id = key_id;
return manifest_verification_init_state (verification, root_key, root_key_length);
}
/**
* Initialize only the variable state for firmware manifest signature verification. The rest of the
* verification instance is assumed to have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* @param verification The verification instance that contains the state to initialize.
* @param root_key The root key used to verify the manifest verification keys.
* @param root_key_length Length of the root verification key.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int manifest_verification_init_state (const struct manifest_verification *verification,
const uint8_t *root_key, size_t root_key_length)
{
int status;
if ((verification == NULL) || (root_key == NULL) || (root_key_length == 0) ||
(verification->state == NULL) || (verification->hash == NULL) ||
(verification->manifest_verify == NULL) || (verification->default_key == NULL) ||
(verification->keystore == NULL)) {
return MANIFEST_VERIFICATION_INVALID_ARGUMENT;
}
memset (verification->state, 0, sizeof (struct manifest_verification_state));
status = manifest_verification_verify_key (verification, verification->default_key, root_key,
root_key_length);
if (status != 0) {
return status;
}
verification->state->default_valid = true;
status = verification->keystore->load_key (verification->keystore, verification->key_id,
(uint8_t**) &verification->state->stored_key.key_data,
&verification->state->stored_key.key_data_length);
if ((status != 0) && (status != KEYSTORE_NO_KEY) && (status != KEYSTORE_BAD_KEY)) {
return status;
}
if (status == 0) {
/* Set up the manifest_verification_key structure for the key loaded from the keystore.
* Lengths and hash type are determined from the values set in the default manifest key. */
verification->state->stored_key.key =
(struct manifest_verification_key_header*) verification->state->stored_key.key_data;
verification->state->stored_key.pub_key_length = verification->default_key->pub_key_length;
verification->state->stored_key.signature = verification->state->stored_key.key_data +
sizeof (uint32_t) + verification->state->stored_key.pub_key_length;
verification->state->stored_key.sig_length = verification->default_key->sig_length;
verification->state->stored_key.sig_hash = verification->default_key->sig_hash;
if (verification->state->stored_key.key_data_length ==
verification->default_key->key_data_length) {
status = manifest_verification_verify_key (verification,
&verification->state->stored_key, root_key, root_key_length);
}
else {
status = MANIFEST_VERIFICATION_INVALID_STORED_KEY;
}
if (status != 0) {
platform_free ((void*) verification->state->stored_key.key_data);
if ((status == SIG_VERIFICATION_BAD_SIGNATURE) ||
(status == SIG_VERIFICATION_INVALID_KEY) ||
(status == MANIFEST_VERIFICATION_INVALID_STORED_KEY)) {
verification->state->stored_key.key_data = NULL;
}
else {
return status;
}
}
}
if (!verification->state->stored_key.key_data) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_MANIFEST,
MANIFEST_LOGGING_NO_STORED_MANIFEST_KEY, verification->key_id, status);
status = verification->keystore->save_key (verification->keystore, verification->key_id,
verification->default_key->key_data, verification->default_key->key_data_length);
if (status != 0) {
return status;
}
}
else if (verification->state->stored_key.key->id >= verification->default_key->key->id) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_MANIFEST,
MANIFEST_LOGGING_MANIFEST_KEY_REVOKED, verification->key_id,
verification->state->stored_key.key->id);
verification->state->default_valid = false;
}
status = platform_mutex_init (&verification->state->lock);
if (status != 0) {
platform_free ((void*) verification->state->stored_key.key_data);
return status;
}
return 0;
}
/**
* Release the resources used for manifest verification.
*
* @param verification The verification instance to release.
*/
void manifest_verification_release (const struct manifest_verification *verification)
{
if (verification) {
platform_free ((void*) verification->state->stored_key.key_data);
}
}
/**
* Get the observer for PFM events.
*
* @param verification The verification instance to query.
*
* @return The PFM observer or null if the verification instance is not valid.
*/
const struct pfm_observer* manifest_verification_get_pfm_observer (
const struct manifest_verification *verification)
{
if (verification) {
return &verification->base_observer;
}
else {
return NULL;
}
}
/**
* Get the observer for CFM events.
*
* @param verification The verification instance to query.
*
* @return The CFM observer or null if the verification instance is not valid.
*/
const struct cfm_observer* manifest_verification_get_cfm_observer (
const struct manifest_verification *verification)
{
if (verification) {
return (const struct cfm_observer*) &verification->base_observer;
}
else {
return NULL;
}
}
/**
* Get the observer for PCD events.
*
* @param verification The verification instance to query.
*
* @return The PCD observer or null if the verification instance is not valid.
*/
const struct pcd_observer* manifest_verification_get_pcd_observer (
const struct manifest_verification *verification)
{
if (verification) {
return (const struct pcd_observer*) &verification->base_observer;
}
else {
return NULL;
}
}