core/attestation/attestation_responder.c (440 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdint.h>
#include <string.h>
#include "attestation_responder.h"
#include "platform_api.h"
#include "common/unused.h"
#include "crypto/ecdsa.h"
static int attestation_responder_get_digests (struct attestation_responder *attestation,
uint8_t slot_num, uint8_t *buf, size_t buf_len, uint8_t *num_cert)
{
const struct riot_keys *keys;
const struct der_cert *root_ca;
const struct der_cert *int_ca;
const struct der_cert *aux_cert = NULL;
size_t offset = 0;
int status;
if ((attestation == NULL) || (buf == NULL) || (num_cert == NULL)) {
return ATTESTATION_INVALID_ARGUMENT;
}
if (slot_num > ATTESTATION_AUX_SLOT_NUM) {
return ATTESTATION_INVALID_SLOT_NUM;
}
#ifdef ATTESTATION_SUPPORT_RSA_UNSEAL
aux_cert = aux_attestation_get_certificate (attestation->aux);
#endif
if (slot_num == ATTESTATION_AUX_SLOT_NUM) {
if (attestation->aux == NULL) {
return ATTESTATION_INVALID_SLOT_NUM;
}
else if (aux_cert == NULL) {
return ATTESTATION_CERT_NOT_AVAILABLE;
}
}
keys = riot_key_manager_get_riot_keys (attestation->riot);
if ((keys->devid_cert == NULL) || (keys->devid_cert_length == 0)) {
status = ATTESTATION_CERT_NOT_AVAILABLE;
goto exit;
}
else if ((slot_num == ATTESTATION_RIOT_SLOT_NUM) &&
((keys->alias_cert == NULL) || (keys->alias_cert_length == 0))) {
status = ATTESTATION_CERT_NOT_AVAILABLE;
goto exit;
}
root_ca = riot_key_manager_get_root_ca (attestation->riot);
int_ca = riot_key_manager_get_intermediate_ca (attestation->riot);
*num_cert = 2;
if (root_ca != NULL) {
*num_cert += 1;
}
if (int_ca != NULL) {
*num_cert += 1;
}
if (buf_len < (SHA256_HASH_LENGTH * (*num_cert))) {
status = ATTESTATION_BUF_TOO_SMALL;
goto exit;
}
platform_mutex_lock (&attestation->lock);
if (root_ca != NULL) {
status = attestation->hash->calculate_sha256 (attestation->hash, root_ca->cert,
root_ca->length, buf, SHA256_HASH_LENGTH);
if (status != 0) {
goto unlock;
}
offset += SHA256_HASH_LENGTH;
}
if (int_ca != NULL) {
status = attestation->hash->calculate_sha256 (attestation->hash, int_ca->cert,
int_ca->length, &buf[offset], SHA256_HASH_LENGTH);
if (status != 0) {
goto unlock;
}
offset += SHA256_HASH_LENGTH;
}
status = attestation->hash->calculate_sha256 (attestation->hash, keys->devid_cert,
keys->devid_cert_length, &buf[offset], SHA256_HASH_LENGTH);
if (status != 0) {
goto unlock;
}
offset += SHA256_HASH_LENGTH;
switch (slot_num) {
case ATTESTATION_RIOT_SLOT_NUM:
status = attestation->hash->calculate_sha256 (attestation->hash, keys->alias_cert,
keys->alias_cert_length, &buf[offset], SHA256_HASH_LENGTH);
break;
case ATTESTATION_AUX_SLOT_NUM:
status = attestation->hash->calculate_sha256 (attestation->hash, aux_cert->cert,
aux_cert->length, &buf[offset], SHA256_HASH_LENGTH);
break;
}
if (status != 0) {
goto unlock;
}
status = offset + SHA256_HASH_LENGTH;
unlock:
platform_mutex_unlock (&attestation->lock);
exit:
riot_key_manager_release_riot_keys (attestation->riot, keys);
return status;
}
/**
* Get the last certificate in the specified chain.
*
* @param attestation The attestation instance.
* @param slot_num The certificate chain being queried.
* @param riot Keys for RIoT attestation.
* @param aux_cert Certificate for auxiliary attestation. This must not be null.
* @param cert Output for the certificate information.
*/
static void attestation_responder_get_last_certificate (struct attestation_responder *attestation,
uint8_t slot_num, const struct riot_keys *riot, const struct der_cert *aux_cert,
struct der_cert *cert)
{
UNUSED (attestation);
switch (slot_num) {
case ATTESTATION_RIOT_SLOT_NUM:
cert->cert = riot->alias_cert;
cert->length = riot->alias_cert_length;
break;
case ATTESTATION_AUX_SLOT_NUM:
cert->cert = aux_cert->cert;
cert->length = aux_cert->length;
break;
}
}
static int attestation_responder_get_certificate (struct attestation_responder *attestation,
uint8_t slot_num, uint8_t cert_num, struct der_cert *cert)
{
const struct riot_keys *keys;
const struct der_cert *int_ca;
const struct der_cert *root_ca;
const struct der_cert *aux_cert = NULL;
int status = 0;
if ((attestation == NULL) || (cert == NULL)) {
return ATTESTATION_INVALID_ARGUMENT;
}
if (slot_num > ATTESTATION_AUX_SLOT_NUM) {
return ATTESTATION_INVALID_SLOT_NUM;
}
root_ca = riot_key_manager_get_root_ca (attestation->riot);
int_ca = riot_key_manager_get_intermediate_ca (attestation->riot);
if (int_ca) {
if (cert_num > 3) {
return ATTESTATION_INVALID_CERT_NUM;
}
}
else if (root_ca) {
if (cert_num > 2) {
return ATTESTATION_INVALID_CERT_NUM;
}
}
else if (cert_num > 1) {
return ATTESTATION_INVALID_CERT_NUM;
}
#ifdef ATTESTATION_SUPPORT_RSA_UNSEAL
aux_cert = aux_attestation_get_certificate (attestation->aux);
#endif
if (slot_num == ATTESTATION_AUX_SLOT_NUM) {
if (attestation->aux == NULL) {
return ATTESTATION_INVALID_SLOT_NUM;
}
else if (aux_cert == NULL) {
return ATTESTATION_CERT_NOT_AVAILABLE;
}
}
keys = riot_key_manager_get_riot_keys (attestation->riot);
if ((keys->devid_cert == NULL) || (keys->devid_cert_length == 0)) {
status = ATTESTATION_CERT_NOT_AVAILABLE;
goto exit;
}
else if ((slot_num == ATTESTATION_RIOT_SLOT_NUM) &&
((keys->alias_cert == NULL) || (keys->alias_cert_length == 0))) {
status = ATTESTATION_CERT_NOT_AVAILABLE;
goto exit;
}
memset (cert, 0, sizeof (struct der_cert));
switch (cert_num) {
case 0:
if (root_ca) {
cert->cert = root_ca->cert;
cert->length = root_ca->length;
}
else {
cert->cert = keys->devid_cert;
cert->length = keys->devid_cert_length;
}
break;
case 1:
if (int_ca) {
cert->cert = int_ca->cert;
cert->length = int_ca->length;
}
else if (root_ca) {
cert->cert = keys->devid_cert;
cert->length = keys->devid_cert_length;
}
else {
attestation_responder_get_last_certificate (attestation, slot_num, keys, aux_cert,
cert);
}
break;
case 2:
if (int_ca) {
cert->cert = keys->devid_cert;
cert->length = keys->devid_cert_length;
}
else {
attestation_responder_get_last_certificate (attestation, slot_num, keys, aux_cert,
cert);
}
break;
case 3:
attestation_responder_get_last_certificate (attestation, slot_num, keys, aux_cert,
cert);
break;
}
exit:
riot_key_manager_release_riot_keys (attestation->riot, keys);
return status;
}
static int attestation_responder_challenge_response (struct attestation_responder *attestation,
uint8_t *buf, size_t buf_len)
{
struct attestation_challenge *challenge = (struct attestation_challenge*) buf;
struct attestation_response *response = (struct attestation_response*) buf;
const struct riot_keys *keys;
uint8_t measurement[PCR_MAX_DIGEST_LENGTH] = {0};
uint16_t response_len;
uint8_t slot_num;
int num_measurements;
int measurement_length;
int status;
if ((attestation == NULL) || (buf == NULL)) {
return ATTESTATION_INVALID_ARGUMENT;
}
if (buf_len < sizeof (struct attestation_challenge)) {
return ATTESTATION_BAD_LENGTH;
}
slot_num = challenge->slot_num;
if (slot_num != ATTESTATION_RIOT_SLOT_NUM) {
return ATTESTATION_INVALID_SLOT_NUM;
}
platform_mutex_lock (&attestation->lock);
num_measurements = pcr_store_get_num_pcr_measurements (attestation->pcr_store, 0);
if (num_measurements == 0) {
/* The PCR is just a single value, but report 1 measurement in the response payload. */
num_measurements = 1;
}
measurement_length = pcr_store_compute_pcr (attestation->pcr_store, attestation->hash, 0,
measurement, sizeof (measurement));
if (ROT_IS_ERROR (measurement_length)) {
status = measurement_length;
goto unlock;
}
response_len = sizeof (struct attestation_response) + measurement_length;
if (buf_len <= response_len) {
status = ATTESTATION_BUF_TOO_SMALL;
goto unlock;
}
status = attestation->hash->start_sha256 (attestation->hash);
if (status != 0) {
goto unlock;
}
status = attestation->hash->update (attestation->hash, (uint8_t*) challenge,
sizeof (struct attestation_challenge));
if (status != 0) {
goto cleanup;
}
memset (buf, 0, response_len);
response->slot_num = slot_num;
response->slot_mask = 1;
response->min_protocol_version = attestation->min_protocol_version;
response->max_protocol_version = attestation->max_protocol_version;
response->num_digests = num_measurements;
response->digests_size = measurement_length;
status = attestation->rng->generate_random_buffer (attestation->rng, ATTESTATION_NONCE_LEN,
response->nonce);
if (status != 0) {
goto cleanup;
}
memcpy (buf + sizeof (struct attestation_response), measurement, measurement_length);
status = attestation->hash->update (attestation->hash, buf, response_len);
if (status != 0) {
goto cleanup;
}
keys = riot_key_manager_get_riot_keys (attestation->riot);
status = ecdsa_sign_hash_and_finish (attestation->ecc, attestation->hash, NULL, keys->alias_key,
keys->alias_key_length, buf + response_len, buf_len - response_len);
riot_key_manager_release_riot_keys (attestation->riot, keys);
if (ROT_IS_ERROR (status)) {
goto unlock;
}
platform_mutex_unlock (&attestation->lock);
return response_len + status;
cleanup:
attestation->hash->cancel (attestation->hash);
unlock:
platform_mutex_unlock (&attestation->lock);
return status;
}
static int attestation_responder_aux_attestation_unseal (struct attestation_responder *attestation,
const struct hash_engine *hash, enum aux_attestation_key_length key_type, const uint8_t *seed,
size_t seed_length, enum aux_attestation_seed_type seed_type,
enum aux_attestation_seed_param seed_param, const uint8_t *hmac, enum hmac_hash hmac_type,
const uint8_t *ciphertext, size_t cipher_length, const uint8_t sealing[][64], size_t pcr_count,
uint8_t *key, size_t key_length)
{
if (attestation == NULL) {
return ATTESTATION_INVALID_ARGUMENT;
}
return aux_attestation_unseal (attestation->aux, hash, attestation->pcr_store, key_type, seed,
seed_length, seed_type, seed_param, hmac, hmac_type, ciphertext, cipher_length, sealing,
pcr_count, key, key_length);
}
static int attestation_responder_aux_attestation_unseal_unsupported (
struct attestation_responder *attestation, const struct hash_engine *hash,
enum aux_attestation_key_length key_type, const uint8_t *seed, size_t seed_length,
enum aux_attestation_seed_type seed_type, enum aux_attestation_seed_param seed_param,
const uint8_t *hmac, enum hmac_hash hmac_type, const uint8_t *ciphertext, size_t cipher_length,
const uint8_t sealing[][64], size_t pcr_count, uint8_t *key, size_t key_length)
{
UNUSED (attestation);
UNUSED (hash);
UNUSED (key_type);
UNUSED (seed);
UNUSED (seed_length);
UNUSED (seed_type);
UNUSED (seed_param);
UNUSED (hmac);
UNUSED (hmac_type);
UNUSED (ciphertext);
UNUSED (cipher_length);
UNUSED (sealing);
UNUSED (pcr_count);
UNUSED (key);
UNUSED (key_length);
return ATTESTATION_UNSUPPORTED_OPERATION;
}
static int attestation_responder_aux_decrypt (struct attestation_responder *attestation,
const uint8_t *encrypted, size_t len_encrypted, const uint8_t *label, size_t len_label,
enum hash_type pad_hash, uint8_t *decrypted, size_t len_decrypted)
{
if (attestation == NULL) {
return ATTESTATION_INVALID_ARGUMENT;
}
return aux_attestation_decrypt (attestation->aux, encrypted, len_encrypted, label, len_label,
pad_hash, decrypted, len_decrypted);
}
static int attestation_responder_aux_decrypt_unsupported (struct attestation_responder *attestation,
const uint8_t *encrypted, size_t len_encrypted, const uint8_t *label, size_t len_label,
enum hash_type pad_hash, uint8_t *decrypted, size_t len_decrypted)
{
UNUSED (attestation);
UNUSED (encrypted);
UNUSED (len_encrypted);
UNUSED (label);
UNUSED (len_label);
UNUSED (pad_hash);
UNUSED (decrypted);
UNUSED (len_decrypted);
return ATTESTATION_UNSUPPORTED_OPERATION;
}
static int attestation_responder_generate_ecdh_seed (struct attestation_responder *attestation,
const uint8_t *pub_key, size_t key_length, bool hash_seed, uint8_t *seed, size_t seed_length)
{
if (attestation == NULL) {
return ATTESTATION_INVALID_ARGUMENT;
}
return aux_attestation_generate_ecdh_seed (attestation->aux, pub_key, key_length,
(hash_seed) ? attestation->hash : NULL, seed, seed_length);
}
static int attestation_responder_generate_ecdh_seed_unsupported (
struct attestation_responder *attestation, const uint8_t *pub_key, size_t key_length,
bool hash_seed, uint8_t *seed, size_t seed_length)
{
UNUSED (attestation);
UNUSED (pub_key);
UNUSED (key_length);
UNUSED (hash_seed);
UNUSED (seed);
UNUSED (seed_length);
return ATTESTATION_UNSUPPORTED_OPERATION;
}
/**
* Initialize the common components for the attestation responder.
*
* @param attestation Slave attestation manager instance to initialize.
* @param riot RIoT key manager.
* @param hash The hash engine to utilize.
* @param ecc The ECC engine to utilize.
* @param rng The RNG engine to utilize.
* @param store PCR store to utilize.
* @param min_protocol_version Minimum protocol version supported by the device.
* @param max_protocol_version Maximum protocol version supported by the device.
*
* @return Initialization status, 0 if success or an error code.
*/
static int attestation_responder_init_common (struct attestation_responder *attestation,
const struct riot_key_manager *riot, const struct hash_engine *hash,
const struct ecc_engine *ecc, const struct rng_engine *rng, struct pcr_store *store,
uint8_t min_protocol_version, uint8_t max_protocol_version)
{
int status;
if ((attestation == NULL) || (riot == NULL) || (hash == NULL) || (ecc == NULL) ||
(rng == NULL) || (store == NULL)) {
return ATTESTATION_INVALID_ARGUMENT;
}
memset (attestation, 0, sizeof (struct attestation_responder));
status = platform_mutex_init (&attestation->lock);
if (status != 0) {
return status;
}
attestation->riot = riot;
attestation->hash = hash;
attestation->ecc = ecc;
attestation->rng = rng;
attestation->pcr_store = store;
attestation->min_protocol_version = min_protocol_version;
attestation->max_protocol_version = max_protocol_version;
attestation->get_digests = attestation_responder_get_digests;
attestation->get_certificate = attestation_responder_get_certificate;
attestation->challenge_response = attestation_responder_challenge_response;
return 0;
}
/**
* Initialize an attestation responder instance.
*
* @param attestation Attestation responder instance to initialize.
* @param riot RIoT key manager.
* @param hash The hash engine to utilize.
* @param ecc The ECC engine to utilize.
* @param rng The RNG engine to utilize.
* @param store PCR store to utilize.
* @param aux Aux attestation service handler to utilize.
* @param min_protocol_version Minimum protocol version supported by the device.
* @param max_protocol_version Maximum protocol version supported by the device.
*
* @return Initialization status, 0 if success or an error code.
*/
int attestation_responder_init (struct attestation_responder *attestation,
const struct riot_key_manager *riot, const struct hash_engine *hash,
const struct ecc_engine *ecc, const struct rng_engine *rng, struct pcr_store *store,
struct aux_attestation *aux, uint8_t min_protocol_version, uint8_t max_protocol_version)
{
int status;
if (aux == NULL) {
return ATTESTATION_INVALID_ARGUMENT;
}
status = attestation_responder_init_common (attestation, riot, hash, ecc, rng, store,
min_protocol_version, max_protocol_version);
if (status != 0) {
return status;
}
attestation->aux = aux;
attestation->aux_attestation_unseal = attestation_responder_aux_attestation_unseal;
attestation->aux_decrypt = attestation_responder_aux_decrypt;
attestation->generate_ecdh_seed = attestation_responder_generate_ecdh_seed;
return 0;
}
/**
* Initialize an attestation responder instance that does not support auxiliary attestation
* requests.
*
* @param attestation Attestation responder instance to initialize.
* @param riot RIoT key manager.
* @param hash The hash engine to utilize.
* @param ecc The ECC engine to utilize.
* @param rng The RNG engine to utilize.
* @param store PCR store to utilize.
* @param min_protocol_version Minimum protocol version supported by the device.
* @param max_protocol_version Maximum protocol version supported by the device.
*
* @return Initialization status, 0 if success or an error code.
*/
int attestation_responder_init_no_aux (struct attestation_responder *attestation,
const struct riot_key_manager *riot, const struct hash_engine *hash,
const struct ecc_engine *ecc, const struct rng_engine *rng, struct pcr_store *store,
uint8_t min_protocol_version, uint8_t max_protocol_version)
{
int status;
status = attestation_responder_init_common (attestation, riot, hash, ecc, rng, store,
min_protocol_version, max_protocol_version);
if (status != 0) {
return status;
}
attestation->aux_attestation_unseal = attestation_responder_aux_attestation_unseal_unsupported;
attestation->aux_decrypt = attestation_responder_aux_decrypt_unsupported;
attestation->generate_ecdh_seed = attestation_responder_generate_ecdh_seed_unsupported;
return 0;
}
/**
* Release attestation responder instance
*
* @param attestation Attestation responder instance to release
*/
void attestation_responder_release (struct attestation_responder *attestation)
{
if (attestation) {
platform_mutex_free (&attestation->lock);
}
}