int aux_attestation_unseal()

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;
}