in core/attestation/aux_attestation.c [341:506]
int aux_attestation_unseal (struct aux_attestation *aux, const struct hash_engine *hash,
struct pcr_store *pcr, 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)
{
uint8_t secret[AUX_ATTESTATION_KEY_BYTES];
int secret_length = 0;
struct hmac_engine run_hmac;
uint8_t signing_key[AUX_ATTESTATION_KEY_256BIT];
uint8_t payload_hmac[SHA256_HASH_LENGTH];
uint8_t pcr_value[PCR_MAX_DIGEST_LENGTH];
int pcr_length;
bool bypass;
size_t i;
int j;
int status;
if ((aux == NULL) || (hash == NULL) || (pcr == NULL) || (seed == NULL) || (seed_length == 0) ||
(hmac == NULL) || (ciphertext == NULL) || (cipher_length == 0) || (sealing == NULL) ||
(pcr_count == 0) || (key == NULL)) {
return AUX_ATTESTATION_INVALID_ARGUMENT;
}
if (key_type != AUX_ATTESTATION_KEY_256BIT) {
return AUX_ATTESTATION_UNSUPPORTED_KEY_LENGTH;
}
if (hmac_type != HMAC_SHA256) {
return AUX_ATTESTATION_UNSUPPORTED_HMAC;
}
if (key_length < AUX_ATTESTATION_KEY_256BIT) {
return AUX_ATTESTATION_BUFFER_TOO_SMALL;
}
/* Get the key derivation seed. */
switch (seed_type) {
case AUX_ATTESTATION_SEED_RSA: {
enum hash_type padding;
switch (seed_param) {
case AUX_ATTESTATION_PARAM_OAEP_SHA1:
padding = HASH_TYPE_SHA1;
break;
case AUX_ATTESTATION_PARAM_OAEP_SHA256:
padding = HASH_TYPE_SHA256;
break;
default:
return AUX_ATTESTATION_BAD_SEED_PARAM;
}
secret_length = aux_attestation_decrypt (aux, seed, seed_length, NULL, 0, padding,
secret, sizeof (secret));
break;
}
case AUX_ATTESTATION_SEED_ECDH:
if ((seed_param != AUX_ATTESTATION_PARAM_ECDH_RAW) &&
(seed_param != AUX_ATTESTATION_PARAM_ECDH_SHA256)) {
return AUX_ATTESTATION_BAD_SEED_PARAM;
}
secret_length = aux_attestation_generate_ecdh_seed (aux, seed, seed_length,
(seed_param == AUX_ATTESTATION_PARAM_ECDH_SHA256) ? hash : NULL, secret,
sizeof (secret));
break;
default:
return AUX_ATTESTATION_UNKNOWN_SEED;
}
if (ROT_IS_ERROR (secret_length)) {
return secret_length;
}
/* Derive the signing key. */
status = kdf_nist800_108_counter_mode (hash, HMAC_SHA256, secret, secret_length,
(const uint8_t*) AUX_ATTESTATION_SIGNING_LABEL, sizeof (AUX_ATTESTATION_SIGNING_LABEL) - 1,
NULL, 0, signing_key, sizeof (signing_key));
if (status != 0) {
return status;
}
/* Validate the payload. */
status = hash_hmac_init (&run_hmac, hash, HMAC_SHA256, signing_key, SHA256_HASH_LENGTH);
if (status != 0) {
return status;
}
status = hash_hmac_update (&run_hmac, ciphertext, cipher_length);
if (status != 0) {
goto hmac_error;
}
status = hash_hmac_update (&run_hmac, sealing[0], 64 * pcr_count);
if (status != 0) {
goto hmac_error;
}
status = hash_hmac_finish (&run_hmac, payload_hmac, sizeof (payload_hmac));
if (status != 0) {
return status;
}
if (buffer_compare (hmac, payload_hmac, SHA256_HASH_LENGTH) != 0) {
return AUX_ATTESTATION_HMAC_MISMATCH;
}
for (i = 0; i < pcr_count; i++) {
j = 0;
bypass = true;
pcr_length = pcr_store_get_pcr_digest_length (pcr, i);
if (pcr_length == PCR_INVALID_PCR) {
/* The PCR is not supported by the device, but is present in the sealing data. This
* is fine as long as the unsupported PCR is bypassed by the sealing. If it's not, this
* same error will get triggered when attempting to calculate the PCR value.
*
* For now, just use the maximum length, which skips the zero-padding check on this PCR,
* since it's not meaningful. There are going to either be all zeros and bypassed or
* not all zeros and trigger an invalid PCR error (which is more descriptive than a
* length mismatch error). */
pcr_length = 64;
}
while (bypass && (j < 64)) {
if (sealing[i][j] != 0) {
if (j < (64 - pcr_length)) {
/* When the PCR length is shorter than the sealing policy, the first sealing
* bytes are unused and must be 0. */
return AUX_ATTESTATION_PCR_LENGTH_MISMATCH;
}
bypass = false;
}
j++;
}
if (!bypass) {
status = pcr_store_compute_pcr (pcr, hash, i, pcr_value, sizeof (pcr_value));
if (ROT_IS_ERROR (status)) {
return status;
}
if (buffer_compare (pcr_value, &sealing[i][(64 - pcr_length)], pcr_length) != 0) {
return AUX_ATTESTATION_PCR_MISMATCH;
}
}
}
/* Derive the encryption key. */
status = kdf_nist800_108_counter_mode (hash, HMAC_SHA256, secret, secret_length,
(const uint8_t*) AUX_ATTESTATION_ENCRYPTION_LABEL,
sizeof (AUX_ATTESTATION_ENCRYPTION_LABEL) - 1, NULL, 0, key, SHA256_HASH_LENGTH);
return status;
hmac_error:
hash_hmac_cancel (&run_hmac);
return status;
}