// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/fido/enclave/attestation.h"

#include <optional>
#include <vector>

#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/memory/raw_ref.h"
#include "base/strings/string_view_util.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "components/cbor/reader.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "crypto/evp.h"
#include "device/fido/enclave/attestation_report.h"
#include "device/fido/enclave/proto/evidence.pb.h"
#include "device/fido/public/fido_constants.h"
#include "device/fido/public_key.h"
#include "net/cert/asn1_util.h"
#include "third_party/boringssl/src/include/openssl/asn1.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/ec_key.h"
#include "third_party/boringssl/src/include/openssl/ecdsa.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/obj.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
#include "third_party/boringssl/src/include/openssl/sha.h"
#include "third_party/boringssl/src/pki/input.h"
#include "third_party/boringssl/src/pki/parse_values.h"
#include "third_party/boringssl/src/pki/parser.h"

namespace device::enclave {

// This code processes an AMD SEV-SNP attestation from the enclave. The
// attestation is processed in a top-down fashion by `ProcessAttestation`, with
// each level validating the next.
//
//   * The root of trust is `kMilanSPKI`, which is a public key published by
//     AMD. That signs...
//   * The VCEK (Versioned Chip Endorsement Key) certificate: each AMD system
//     (probably in the PSP chip) contains a symmetric secret from the factory.
//     The chip hashes this secret, along with a few firmware version numbers,
//     to generate a private signing key. AMD also holds this system and so can
//     generate the same private key. AMD runs an online service which will do
//     this on demand, derive the public key, and sign a certificate containing
//     that public key with `kMilanSPKI`. That certificate is the VCEK
//     certificate. The production enclave service fetches it and includes it in
//     the attestation response. So the public key in the VCEK certificate signs
//     the...
//   * Attestation report: this binary structure is generated by the CPU/PSP and
//     contains a bunch of data. (See `attestation_report.h`.) One of the fields
//     is some opaque data from the kernel, which happens to be the hash of the
//     stage0's public key...
//   * Oak DICE data: the Oak elements produce a series of COSE structures where
//     each layer signs the public key of the next layer. Each can be validated
//     in turn until we reach the application signing public key...
//   * The enclave application itself signs a public key (called the "leaf
//     public key" here) that it then uses to sign each handshake.

namespace {

// The first certificate from the download at
// https://kdsintf.amd.com/vcek/v1/Milan/cert_chain, run through
// `net::asn1::ExtractSPKIFromDERCert`. This serves as the root of trust.
const uint8_t kMilanSPKI[] = {
    0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
    0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
    0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0x9d, 0x4d, 0x9d,
    0xae, 0xb3, 0x53, 0x7d, 0xb8, 0x4d, 0x40, 0x89, 0x65, 0x7f, 0xe5, 0xb6,
    0xcb, 0xe4, 0x4e, 0x09, 0xb4, 0xb3, 0x21, 0xdd, 0x5a, 0x29, 0x97, 0xed,
    0xd9, 0x3f, 0x73, 0x8d, 0x94, 0x0e, 0xce, 0x31, 0x9c, 0x72, 0x5d, 0x7b,
    0x8b, 0x59, 0x88, 0x29, 0x69, 0x7b, 0x35, 0x37, 0x01, 0xd1, 0x56, 0x17,
    0xb7, 0x72, 0x71, 0x65, 0x2c, 0xce, 0x66, 0x3b, 0x23, 0x2c, 0xd5, 0x40,
    0x10, 0xdd, 0x8c, 0x1a, 0x3f, 0x53, 0x89, 0xe7, 0x4b, 0xf9, 0x07, 0xb0,
    0x29, 0x95, 0xf4, 0x26, 0x64, 0x04, 0xb9, 0x88, 0xe6, 0xf9, 0x62, 0xa4,
    0xb0, 0xbb, 0x71, 0x81, 0xd2, 0xe9, 0xf4, 0x4e, 0xc4, 0x64, 0xdc, 0x0d,
    0x0e, 0xa5, 0x75, 0xaf, 0x4a, 0x91, 0x3f, 0x9b, 0x41, 0xf0, 0xe5, 0xa4,
    0xc9, 0x06, 0xc8, 0x74, 0xb7, 0xae, 0xe1, 0xa0, 0xb3, 0xee, 0x3f, 0xd2,
    0x97, 0x51, 0x64, 0x07, 0x2b, 0x5e, 0xbf, 0xdb, 0x1b, 0x14, 0x6c, 0xed,
    0xed, 0xcc, 0x27, 0x8f, 0x38, 0xbd, 0x9b, 0xb9, 0xe8, 0xaa, 0xc9, 0x3e,
    0xb9, 0x15, 0x41, 0xa7, 0x7f, 0x88, 0x9f, 0x7e, 0x50, 0x3d, 0xd7, 0x23,
    0xf1, 0x87, 0xe5, 0x12, 0x69, 0xc7, 0x04, 0xdb, 0xee, 0x50, 0x32, 0x61,
    0x2c, 0x22, 0x4c, 0x5b, 0xc2, 0x8e, 0x8c, 0xfe, 0xbe, 0xf8, 0xf8, 0x5b,
    0xb3, 0x78, 0x82, 0x8a, 0xd2, 0x5c, 0x00, 0xd1, 0x2d, 0x5b, 0x8a, 0x93,
    0x34, 0x5a, 0x0a, 0x5b, 0x70, 0x87, 0x95, 0xb7, 0x12, 0x0a, 0x34, 0xcc,
    0xf0, 0xab, 0x0d, 0x6d, 0x4c, 0x77, 0x03, 0xc7, 0xa4, 0xe4, 0x45, 0x4b,
    0x8d, 0x95, 0x87, 0xd6, 0x9b, 0x7d, 0x13, 0x74, 0xdf, 0xa5, 0x1e, 0x97,
    0xc9, 0xf4, 0x0a, 0x9d, 0x8e, 0xa4, 0x97, 0x96, 0x84, 0x20, 0xfc, 0x1d,
    0x5b, 0x77, 0x85, 0x61, 0xaa, 0x82, 0x14, 0xfa, 0xc8, 0xa3, 0xda, 0x50,
    0x4f, 0xa5, 0xae, 0x0d, 0x23, 0xf8, 0x24, 0x26, 0x09, 0x6d, 0x99, 0xde,
    0x28, 0xa2, 0x1b, 0x66, 0x3c, 0xd7, 0x90, 0x9b, 0x77, 0x35, 0x01, 0xb7,
    0xd8, 0x4b, 0xa4, 0x60, 0x89, 0x81, 0x6f, 0xd4, 0x82, 0x92, 0x6f, 0x7e,
    0x7d, 0x2e, 0x4b, 0x64, 0x58, 0x3d, 0xa2, 0x3c, 0xee, 0x6c, 0xc5, 0xf7,
    0xf8, 0xd9, 0x01, 0xa1, 0x25, 0xc8, 0xec, 0xe3, 0xef, 0x9c, 0x73, 0x31,
    0x8e, 0xea, 0x7d, 0x9b, 0x0e, 0x6c, 0x7b, 0xa4, 0x14, 0x57, 0xb1, 0xaa,
    0xa4, 0x20, 0x36, 0x4c, 0xdc, 0xa9, 0xa2, 0x59, 0xae, 0x43, 0xe7, 0x00,
    0x6b, 0x15, 0x7b, 0x26, 0xa1, 0xc4, 0xf1, 0xd9, 0x7d, 0xa5, 0x67, 0xfe,
    0x63, 0x76, 0xab, 0x6f, 0xef, 0x62, 0x88, 0x50, 0xb0, 0x16, 0xde, 0x25,
    0x02, 0x52, 0x70, 0xe4, 0x3d, 0x02, 0x4d, 0x14, 0xc2, 0xd5, 0x8e, 0x58,
    0x58, 0x50, 0xc1, 0x0a, 0xb0, 0x3f, 0xbc, 0x69, 0xe6, 0x4e, 0x7a, 0x86,
    0xf0, 0x2f, 0xa3, 0x8d, 0x40, 0x12, 0xed, 0xc8, 0xe3, 0x47, 0xca, 0xb8,
    0x38, 0xf8, 0x72, 0x0e, 0x62, 0x51, 0x3e, 0x76, 0x82, 0xfd, 0x91, 0xb9,
    0xb8, 0xfd, 0x0a, 0xd3, 0x3e, 0x86, 0xa0, 0xef, 0xf7, 0xb9, 0xe9, 0xfd,
    0xc2, 0xab, 0x21, 0x0a, 0xe1, 0xde, 0x80, 0xb4, 0xe9, 0x39, 0xe1, 0xc4,
    0x25, 0x12, 0xab, 0x40, 0x5a, 0xf8, 0x3d, 0x52, 0x30, 0x54, 0xe0, 0x74,
    0xf9, 0xe6, 0xce, 0xe4, 0x58, 0x28, 0xc8, 0xec, 0x38, 0xde, 0x7c, 0x85,
    0x0f, 0x95, 0x0a, 0x04, 0x3c, 0x44, 0x07, 0x80, 0x4a, 0xa3, 0xfd, 0x2e,
    0x02, 0x22, 0x87, 0x2d, 0x1b, 0xef, 0x80, 0xb6, 0xce, 0x45, 0xb5, 0x3c,
    0x28, 0x04, 0x48, 0xb3, 0x51, 0x28, 0xa8, 0x48, 0xe6, 0x17, 0xc5, 0x2d,
    0xae, 0x64, 0x16, 0x57, 0x68, 0x54, 0x8f, 0x0c, 0xea, 0xc2, 0xf1, 0xc5,
    0x7a, 0x2f, 0x2b, 0x3f, 0x13, 0x02, 0x03, 0x01, 0x00, 0x01};

// Verify that `ecdsa_sig` is a valid signature of `message` by `ec_key`.
base::expected<void, const char*> VerifySignatureFromEC_KEY(
    base::span<const uint8_t> message,
    const ECDSA_SIG* ecdsa_sig,
    const EC_KEY* ec_key) {
  std::array<uint8_t, SHA256_DIGEST_LENGTH> hash;
  SHA256(message.data(), message.size(), hash.data());

  const int verify_result =
      ECDSA_do_verify(hash.data(), hash.size(), ecdsa_sig, ec_key);
  if (verify_result == 1) {
    return base::ok();
  } else if (verify_result < 0) {
    return base::unexpected(
        "Error occurred during ECDSA signature verification");
  } else {
    return base::unexpected("signature invalid");
  }
}

// Verify that `signature`, which must be a 64-byte concatenation of the r and s
// values, is a valid signature of `message` by `ec_key`.
base::expected<void, const char*> VerifyConcatSignatureFromEC_KEY(
    base::span<const uint8_t> message,
    base::span<const uint8_t> signature,
    const EC_KEY* ec_key) {
  constexpr size_t kP256FieldElementBytes = 32;
  if (signature.size() != kP256FieldElementBytes * 2) {
    return base::unexpected("Invalid signature length; expected 64 bytes");
  }
  bssl::UniquePtr<ECDSA_SIG> ecdsa_sig(ECDSA_SIG_new());
  auto r_span = signature.first<kP256FieldElementBytes>();
  auto s_span = signature.subspan<kP256FieldElementBytes>();

  bssl::UniquePtr<BIGNUM> r(BN_bin2bn(r_span.data(), r_span.size(), nullptr));
  bssl::UniquePtr<BIGNUM> s(BN_bin2bn(s_span.data(), s_span.size(), nullptr));

  CHECK(ECDSA_SIG_set0(ecdsa_sig.get(), r.release(), s.release()));
  return VerifySignatureFromEC_KEY(message, ecdsa_sig.get(), ec_key);
}

// Verify that `signature`, which must be an ASN.1 ECDSA signature, is a valid
// signature of `message` by `ec_key`.
base::expected<void, const char*> VerifyDERSignatureFromEC_KEY(
    base::span<const uint8_t> message,
    base::span<const uint8_t> signature,
    const EC_KEY* ec_key) {
  CBS cbs;
  CBS_init(&cbs, signature.data(), signature.size());
  bssl::UniquePtr<ECDSA_SIG> ecdsa_sig(ECDSA_SIG_parse(&cbs));
  if (!ecdsa_sig || CBS_len(&cbs) != 0) {
    return base::unexpected("failed to parse ECDSA signature");
  }
  return VerifySignatureFromEC_KEY(message, ecdsa_sig.get(), ec_key);
}

// Verify that `signature`, which must be an ASN.1 ECDSA signature, is a valid
// signature of `handshake_hash` by `ec_key`.
base::expected<void, const char*> VerifyHandshakeHash(
    base::span<const uint8_t> handshake_hash,
    base::span<const uint8_t> signature,
    const EC_KEY* ec_key) {
  const char kPrefix[] = "Handshake signature";
  // Terminating NUL from `kPrefix` is deliberately included.
  base::span<const uint8_t> prefix_bytes = base::as_byte_span(kPrefix);
  std::vector<uint8_t> message(prefix_bytes.begin(), prefix_bytes.end());
  message.insert(message.end(), handshake_hash.begin(), handshake_hash.end());
  return VerifyDERSignatureFromEC_KEY(message, signature, ec_key);
}

// Parse an `EC_KEY` from an X9.62-encoded curve point.
base::expected<bssl::UniquePtr<EC_KEY>, const char*> EC_KEYFromX962(
    base::span<const uint8_t> public_key) {
  const EC_GROUP* group = EC_group_p256();
  bssl::UniquePtr<EC_POINT> ec_point(EC_POINT_new(group));
  if (EC_POINT_oct2point(group, ec_point.get(), public_key.data(),
                         public_key.size(), nullptr) != 1) {
    return base::unexpected("Failed to parse public key");
  }
  bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new());
  if (!EC_KEY_set_group(ec_key.get(), group)) {
    return base::unexpected("Failed to set EC_GROUP for EC_KEY");
  }
  if (!EC_KEY_set_public_key(ec_key.get(), ec_point.get())) {
    return base::unexpected("Failed to set public key in EC_KEY");
  }
  return ec_key;
}

// Parse an `EC_KEY` from a COSE public key.
base::expected<bssl::UniquePtr<EC_KEY>, const char*> EC_KEYFromCOSE(
    base::span<const uint8_t> cose_bytes) {
  std::optional<cbor::Value> cose_val = cbor::Reader::Read(cose_bytes);
  if (!cose_val.has_value() || !cose_val->is_map()) {
    return base::unexpected("failed to parse COSE structure");
  }
  std::unique_ptr<PublicKey> pubkey = PublicKey::FromCOSEKey(
      /*algorithm=*/-7, cose_bytes, cose_val->GetMap());
  if (!pubkey || !pubkey->der_bytes) {
    return base::unexpected("failed to parse COSE key");
  }

  // If `ExtractFromCOSEKey` was happy with the key then none of the following
  // should fail.
  bssl::UniquePtr<EVP_PKEY> public_key =
      crypto::evp::PublicKeyFromBytes(*pubkey->der_bytes);
  CHECK(public_key);
  CHECK(EVP_PKEY_id(public_key.get()) == EVP_PKEY_EC);
  bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(public_key.get()));
  CHECK(ec_key && EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key.get())) ==
                      NID_X9_62_prime256v1);

  return ec_key;
}

// Parse `cbor_bytes` as a COSE Web Token. Check that it's issued by
// `issuer_public_key` and, if so, return the enclosed public key.
base::expected<bssl::UniquePtr<EC_KEY>, const char*> ProcessCWT(
    base::span<const uint8_t> cbor_bytes,
    const EC_KEY* issuer_public_key) {
  std::optional<cbor::Value> cwt_value = cbor::Reader::Read(cbor_bytes);
  if (!cwt_value) {
    return base::unexpected("top-level parse failed");
  }
  if (!cwt_value->is_array()) {
    return base::unexpected("top-level is not an array");
  }
  const cbor::Value::ArrayValue& top_level(cwt_value->GetArray());
  if (top_level.size() != 4) {
    return base::unexpected("top-level has incorrect length");
  }
  const cbor::Value& protected_headers_bytes = top_level.at(0);
  const cbor::Value& body = top_level.at(2);
  const cbor::Value& signature = top_level.at(3);
  if (!protected_headers_bytes.is_bytestring() || !body.is_bytestring() ||
      !signature.is_bytestring()) {
    return base::unexpected("CWT fields are not bytestrings");
  }

  std::optional<cbor::Value> protected_headers =
      cbor::Reader::Read(protected_headers_bytes.GetBytestring());
  if (!protected_headers) {
    return base::unexpected("failed to parse protected headers");
  }
  if (!protected_headers->is_map()) {
    return base::unexpected("protected headers is not a map");
  }
  const cbor::Value::MapValue& protected_headers_map(
      protected_headers->GetMap());
  auto it = protected_headers_map.find(cbor::Value(1));
  if (it == protected_headers_map.end()) {
    return base::unexpected("no key 1 in protected headers");
  }
  if (!it->second.is_integer()) {
    return base::unexpected("protected headers key 1 is not an int");
  }
  if (it->second.GetInteger() !=
      static_cast<int>(CoseAlgorithmIdentifier::kEs256)) {
    return base::unexpected("incorrect algorithm in protected headers");
  }

  // Build signed message.
  // See https://www.rfc-editor.org/rfc/rfc8152#section-4.4
  cbor::Value::ArrayValue signed_array;
  signed_array.emplace_back("Signature1");
  signed_array.emplace_back(protected_headers_bytes.GetBytestring());
  signed_array.emplace_back(std::vector<uint8_t>());
  signed_array.emplace_back(body.Clone());

  std::optional<std::vector<uint8_t>> signed_message =
      cbor::Writer::Write(cbor::Value(std::move(signed_array)));
  if (!signed_message) {
    return base::unexpected("failed to serialize signed message");
  }

  // Validate signature over the CWT.
  RETURN_IF_ERROR(VerifyConcatSignatureFromEC_KEY(
      *signed_message, signature.GetBytestring(), issuer_public_key));

  // Get next public key from the body.
  std::optional<cbor::Value> body_value =
      cbor::Reader::Read(body.GetBytestring());
  if (!body_value) {
    return base::unexpected("body parse failed");
  }
  if (!body_value->is_map()) {
    return base::unexpected("body is not a map");
  }
  const cbor::Value::MapValue& body_map = body_value->GetMap();

  it = body_map.find(cbor::Value(-4670552));
  if (it == body_map.end()) {
    return base::unexpected("no subject public key in body");
  }
  if (!it->second.is_bytestring()) {
    return base::unexpected("subject public key in body is not a bytestring");
  }

  return EC_KEYFromCOSE(it->second.GetBytestring());
}

// Verify that `leaf_signature` is a valid signature of an enclave public key in
// `leaf_public_key_bytes` given the final public key of the DICE chain.
base::expected<void, const char*> CheckLeafPublicKey(
    base::span<const uint8_t> leaf_public_key_bytes,
    base::span<const uint8_t> leaf_signature,
    const EC_KEY* application_signing_key) {
  const char kLeafSignatureContext[] = "Leaf dice signature";
  // Terminating NUL in `kLeafSignatureContext` is deliberately included.
  base::span<const uint8_t> context = base::as_byte_span(kLeafSignatureContext);
  std::vector<uint8_t> leaf_public_key_signed(context.begin(), context.end());
  leaf_public_key_signed.insert(leaf_public_key_signed.end(),
                                leaf_public_key_bytes.begin(),
                                leaf_public_key_bytes.end());
  return VerifyConcatSignatureFromEC_KEY(leaf_public_key_signed, leaf_signature,
                                         application_signing_key);
}

// Helper function to convert a dotted-form OID string to its DER-encoded
// content bytes. Returns true on success and fills |der_oid_out| with the
// content bytes. Returns false on failure.
bool ConvertDottedOidToContentBytes(const char* dotted_oid,
                                    std::vector<uint8_t>* der_oid_out) {
  bssl::UniquePtr<ASN1_OBJECT> obj(OBJ_txt2obj(dotted_oid, 1));
  if (!obj) {
    return false;
  }

  // Get the DER-encoded content bytes (without tag and length).
  const uint8_t* data = OBJ_get0_data(obj.get());
  const int len = OBJ_length(obj.get());
  if (len <= 0 || !data) {
    return false;
  }

  // SAFETY: we have to trust that `OBJ_length` returns the correct number of
  // valid bytes. This is a C API and thus cannot return a span.
  UNSAFE_BUFFERS(der_oid_out->assign(data, data + len));
  return true;
}

// Helper function to parse an ASN.1 INTEGER into a uint64_t.
// Returns true on success and fills |value_out| with the parsed integer.
// Returns false on failure.
bool ParseAsn1Integer(const std::string_view& data, uint64_t* out_value) {
  CBS cbs;
  CBS_init(&cbs, reinterpret_cast<const uint8_t*>(data.data()), data.size());

  if (!CBS_get_asn1_uint64(&cbs, out_value) || CBS_len(&cbs) != 0) {
    return false;
  }

  return true;
}

// Structure to hold the extension values parsed from a VCEK certificate.
struct VCEKCertificateExtensions {
  uint64_t bootloader;
  uint64_t tee;
  uint64_t snp;
  uint64_t microcode;
  std::vector<uint8_t> hardware_id;
};

base::expected<VCEKCertificateExtensions, const char*>
ExtractVCEKCertificateExtensions(base::span<const uint8_t> cert) {
  const char BL_SPL_str[] = "1.3.6.1.4.1.3704.1.3.1";
  const char TEE_SPL_str[] = "1.3.6.1.4.1.3704.1.3.2";
  const char SNP_SPL_str[] = "1.3.6.1.4.1.3704.1.3.3";
  const char UCODE_SPL_str[] = "1.3.6.1.4.1.3704.1.3.8";
  const char HW_ID_str[] = "1.3.6.1.4.1.3704.1.4";

  std::vector<uint8_t> BL_SPL_der, TEE_SPL_der, SNP_SPL_der, UCODE_SPL_der,
      HW_ID_der;
  CHECK(ConvertDottedOidToContentBytes(BL_SPL_str, &BL_SPL_der) &&
        ConvertDottedOidToContentBytes(TEE_SPL_str, &TEE_SPL_der) &&
        ConvertDottedOidToContentBytes(SNP_SPL_str, &SNP_SPL_der) &&
        ConvertDottedOidToContentBytes(UCODE_SPL_str, &UCODE_SPL_der) &&
        ConvertDottedOidToContentBytes(HW_ID_str, &HW_ID_der));

  auto extract_extension = [&](const std::vector<uint8_t>& der_oid)
      -> base::expected<std::string_view, const char*> {
    bool present = false;
    bool unused_is_critical;
    std::string_view contents;
    if (!net::asn1::ExtractExtensionFromDERCert(
            base::as_string_view(cert), base::as_string_view(der_oid), &present,
            &unused_is_critical, &contents)) {
      return base::unexpected(
          "failed to extract required certificate extension");
    }
    if (!present) {
      return base::unexpected("required certificate extension missing");
    }
    return contents;
  };

  ASSIGN_OR_RETURN(auto bl_contents, extract_extension(BL_SPL_der));
  ASSIGN_OR_RETURN(auto tee_contents, extract_extension(TEE_SPL_der));
  ASSIGN_OR_RETURN(auto snp_contents, extract_extension(SNP_SPL_der));
  ASSIGN_OR_RETURN(auto ucode_contents, extract_extension(UCODE_SPL_der));
  ASSIGN_OR_RETURN(auto hwid_contents, extract_extension(HW_ID_der));

  VCEKCertificateExtensions result;
  base::span<const uint8_t> hw_id_span(base::as_byte_span(hwid_contents));
  result.hardware_id.assign(hw_id_span.begin(), hw_id_span.end());

  if (!ParseAsn1Integer(bl_contents, &result.bootloader) ||
      !ParseAsn1Integer(tee_contents, &result.tee) ||
      !ParseAsn1Integer(snp_contents, &result.snp) ||
      !ParseAsn1Integer(ucode_contents, &result.microcode)) {
    return base::unexpected("failed to parse certificate extension as integer");
  }

  return result;
}

// Verify that the extension values taken from the VCEK certificate match the
// values in the CPU's attestation report.
base::expected<void, const char*> VerifyExtensions(
    const AttestationReportData& report,
    const VCEKCertificateExtensions& extensions) {
  const TcbVersion& reported_tcb = report.reported_tcb;

  if (static_cast<uint64_t>(reported_tcb.boot_loader) !=
      extensions.bootloader) {
    return base::unexpected("Bootloader version mismatch");
  }

  if (static_cast<uint64_t>(reported_tcb.tee) != extensions.tee) {
    return base::unexpected("TEE version mismatch");
  }

  if (static_cast<uint64_t>(reported_tcb.snp) != extensions.snp) {
    return base::unexpected("SNP version mismatch");
  }

  if (static_cast<uint64_t>(reported_tcb.microcode) != extensions.microcode) {
    return base::unexpected("Microcode version mismatch");
  }

  if (!std::equal(report.chip_id.begin(), report.chip_id.end(),
                  extensions.hardware_id.begin(),
                  extensions.hardware_id.end())) {
    return base::unexpected("Hardware ID mismatch");
  }

  return base::ok();
}

// Verify that the root DICE public key matches the value stored in the CPU's
// attestation report.
base::expected<void, const char*> CheckRootKeyHash(
    base::span<const uint8_t> root_key,
    base::span<const uint8_t, REPORT_DATA_SIZE> report_data) {
  std::array<uint8_t, SHA256_DIGEST_LENGTH> digest;
  SHA256(root_key.data(), root_key.size(), digest.data());
  static_assert(REPORT_DATA_SIZE >= sizeof(digest));
  if (!std::ranges::equal(digest, report_data.first<sizeof(digest)>())) {
    return base::unexpected("root key hash incorrect");
  }
  // The rest of the report data should be zeros.
  if (!std::ranges::all_of(report_data.subspan(sizeof(digest)),
                           [](uint8_t b) { return b == 0; })) {
    return base::unexpected("report data is not zero-padded");
  }

  return base::ok();
}

// Verify the signature on the CPU's attestation report, which comes from the
// public key in the VCEK certificate.
base::expected<void, const char*> VerifyAttestationReportSignature(
    base::span<const uint8_t> message,
    const EcdsaSignature& signature,
    base::span<const uint8_t> vcek_cert) {
  std::string_view spki;
  if (!net::asn1::ExtractSPKIFromDERCert(base::as_string_view(vcek_cert),
                                         &spki)) {
    return base::unexpected("Failed to extract SPKI from certificate");
  }

  bssl::UniquePtr<EVP_PKEY> evp_key =
      crypto::evp::PublicKeyFromBytes(base::as_byte_span(spki));
  if (!evp_key) {
    return base::unexpected("Failed to parse public key from SPKI.");
  }

  bssl::UniquePtr<EC_KEY> ec_key(EVP_PKEY_get1_EC_KEY(evp_key.get()));
  if (!ec_key) {
    return base::unexpected("Failed to extract EC_KEY from EVP_PKEY.");
  }
  if (EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key.get())) !=
      NID_secp384r1) {
    return base::unexpected("certificate public key is not P-384");
  }

  constexpr size_t kP384ElementBytes = 48;
  // The integers in AMD's format are little-endian encoded, which is different
  // from nearly all other uses of ECDSA.
  bssl::UniquePtr<BIGNUM> bn_r(BN_lebin2bn(
      signature.r.data(), static_cast<int>(kP384ElementBytes), nullptr));
  bssl::UniquePtr<BIGNUM> bn_s(BN_lebin2bn(
      signature.s.data(), static_cast<int>(kP384ElementBytes), nullptr));
  bssl::UniquePtr<ECDSA_SIG> ecdsa_sig(ECDSA_SIG_new());
  CHECK(ECDSA_SIG_set0(ecdsa_sig.get(), bn_r.release(), bn_s.release()));

  std::array<uint8_t, SHA384_DIGEST_LENGTH> digest;
  SHA384(message.data(), message.size(), digest.data());

  const int verify_result = ECDSA_do_verify(digest.data(), digest.size(),
                                            ecdsa_sig.get(), ec_key.get());
  if (verify_result == 1) {
    return base::ok();
  } else if (verify_result < 0) {
    return base::unexpected(
        "Error occurred during ECDSA signature verification");
  } else {
    return base::unexpected("invalid signature on attestation report");
  }
}

struct CertificateParts {
  base::raw_span<const uint8_t> signed_part;
  base::raw_span<const uint8_t> signature;
};

// Shallow-parse the VCEK certificate, returning the "to be signed" (TBS) part
// and the signature from the issuing certificate.
base::expected<CertificateParts, const char*> ParseVcekCertificate(
    base::span<const uint8_t> der_bytes) {
  bssl::der::Parser parser(
      bssl::der::Input(der_bytes.data(), der_bytes.size()));
  bssl::der::Parser certificate;
  if (!parser.ReadSequence(&certificate)) {
    return base::unexpected("top-level parse failed for VCEK");
  }
  if (parser.HasMore()) {
    return base::unexpected("VCEK has trailing data");
  }

  bssl::der::Input tbs_certificate;
  if (!certificate.ReadRawTLV(&tbs_certificate)) {
    return base::unexpected("cannot get TBS from VCEK");
  }

  bssl::der::Parser signature_algorithm;
  if (!certificate.ReadSequence(&signature_algorithm)) {
    return base::unexpected("cannot get signature algorithm from VCEK");
  }
  // This code assumes that the VCEK is signed by RSA PSS so the reported
  // signature algorithm is ignored. If this assumption is incorrect, the
  // signature validation will fail.

  std::optional<bssl::der::BitString> signature = certificate.ReadBitString();
  if (!signature) {
    return base::unexpected("cannot get signature from VCEK");
  }

  if (certificate.HasMore()) {
    return base::unexpected("trailing data inside VCEK certificate");
  }

  return CertificateParts{tbs_certificate, signature->bytes()};
}

// Verify the signature on the VCEK certificate by the issuing certificate.
// (This uses an RSA PSS signature, awkwardly.)
base::expected<void, const char*> VerifyVcekSignature(
    base::span<const uint8_t> tbs_cert,
    base::span<const uint8_t> signature,
    base::span<const uint8_t> spki) {
  bssl::UniquePtr<EVP_PKEY> public_key = crypto::evp::PublicKeyFromBytes(spki);
  if (!public_key || EVP_PKEY_id(public_key.get()) != EVP_PKEY_RSA) {
    return base::unexpected("invalid SPKI");
  }

  bssl::ScopedEVP_MD_CTX md_ctx;
  EVP_PKEY_CTX* pkey_ctx;
  if (!EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha384(), nullptr,
                            public_key.get())) {
    return base::unexpected("EVP_DigestVerifyInit failed");
  }
  if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
    return base::unexpected("EVP_PKEY_CTX_set_rsa_padding failed");
  }
  if (!EVP_DigestVerifyUpdate(md_ctx.get(), tbs_cert.data(), tbs_cert.size()) ||
      !EVP_DigestVerifyFinal(md_ctx.get(), signature.data(),
                             signature.size())) {
    return base::unexpected("VCEK signature invalid");
  }

  return base::ok();
}

using BinaryValue = cbor::Value::BinaryValue;

// These are the bytestrings contained within the CBOR attestation sent by the
// enclave.
struct AttestationValues {
  raw_ref<const BinaryValue> handshake_signature;
  raw_ref<const BinaryValue> leaf_public_key_bytes;
  raw_ref<const BinaryValue> leaf_signature;
  raw_ref<const BinaryValue> evidence_bytes;
  raw_ref<const BinaryValue> vcek_cert;
};

// Extract the five bytestrings from the attestation CBOR map returned by the
// enclave. Note that the returned references point to into `attestation_value`.
base::expected<AttestationValues, const char*> GetAttestationValues(
    const cbor::Value& attestation_value) {
  if (!attestation_value.is_map()) {
    return base::unexpected("attestation is not a map");
  }
  const cbor::Value::MapValue& attestation = attestation_value.GetMap();

  auto get_value = [&attestation](int key, const char* error)
      -> base::expected<const BinaryValue*, const char*> {
    auto it = attestation.find(cbor::Value(key));
    if (it == attestation.end() || !it->second.is_bytestring()) {
      return base::unexpected(error);
    }
    return &it->second.GetBytestring();
  };

  ASSIGN_OR_RETURN(const BinaryValue* handshake_signature,
                   get_value(0, "missing handshake signature"));
  ASSIGN_OR_RETURN(const BinaryValue* leaf_public_key_bytes,
                   get_value(1, "missing leaf public key"));
  ASSIGN_OR_RETURN(const BinaryValue* leaf_signature,
                   get_value(2, "missing leaf public key signature"));
  ASSIGN_OR_RETURN(const BinaryValue* evidence_bytes,
                   get_value(3, "missing DICE data"));
  ASSIGN_OR_RETURN(const BinaryValue* vcek_cert,
                   get_value(4, "missing VCEK certificate"));

  return AttestationValues{
      raw_ref(*handshake_signature), raw_ref(*leaf_public_key_bytes),
      raw_ref(*leaf_signature), raw_ref(*evidence_bytes), raw_ref(*vcek_cert)};
}

}  // namespace

base::expected<AttestationResult, const char*> ProcessAttestation(
    base::span<const uint8_t> attestation_bytes,
    base::span<const uint8_t, 32> handshake_hash) {
  std::optional<cbor::Value> attestation_value =
      cbor::Reader::Read(attestation_bytes);
  if (!attestation_value) {
    return base::unexpected("failed to parse attestation");
  }

  ASSIGN_OR_RETURN(auto values, GetAttestationValues(*attestation_value));
  // These references point into `attestation_value`:

  ASSIGN_OR_RETURN(const CertificateParts vcek_parts,
                   ParseVcekCertificate(*values.vcek_cert));
  RETURN_IF_ERROR(VerifyVcekSignature(vcek_parts.signed_part,
                                      vcek_parts.signature, kMilanSPKI));

  Evidence evidence;
  if (!evidence.ParseFromArray((*values.evidence_bytes).data(),
                               (*values.evidence_bytes).size())) {
    return base::unexpected("failed to parse Evidence proto");
  }
  if (evidence.root_layer().platform() != TeePlatform::AMD_SEV_SNP) {
    return base::unexpected("non-SEV platform");
  }
  if (evidence.layers_size() != 1) {
    return base::unexpected("expected exactly one layer in Evidence");
  }

  auto amd_attestation =
      base::as_byte_span(evidence.root_layer().remote_attestation_report());
  if (amd_attestation.size() != sizeof(AttestationReport)) {
    return base::unexpected("attestation report has incorrect size");
  }
  AttestationReport attestation_report;
  base::byte_span_from_ref(attestation_report)
      .copy_from_nonoverlapping(amd_attestation);

  ASSIGN_OR_RETURN(auto vcek_extensions,
                   ExtractVCEKCertificateExtensions(*values.vcek_cert));
  RETURN_IF_ERROR(VerifyExtensions(attestation_report.data, vcek_extensions));

  // See table 22 in
  // https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf
  // for this length. It covers all the non-signature bytes in the attestation
  // report.
  static_assert(sizeof(attestation_report.data) == 0x2a0);
  RETURN_IF_ERROR(VerifyAttestationReportSignature(
      amd_attestation.first<sizeof(attestation_report.data)>(),
      attestation_report.signature, *values.vcek_cert));

  base::span<const uint8_t> root_cose_key =
      base::as_byte_span(evidence.root_layer().eca_public_key());
  RETURN_IF_ERROR(
      CheckRootKeyHash(root_cose_key, attestation_report.data.report_data));
  ASSIGN_OR_RETURN(auto root_public_key, EC_KEYFromCOSE(root_cose_key));
  ASSIGN_OR_RETURN(
      auto kernel_public_key,
      ProcessCWT(base::as_byte_span(evidence.layers().at(0).eca_certificate()),
                 root_public_key.get()));
  ASSIGN_OR_RETURN(
      auto application_public_key,
      ProcessCWT(
          base::as_byte_span(
              evidence.application_keys().signing_public_key_certificate()),
          kernel_public_key.get()));
  RETURN_IF_ERROR(CheckLeafPublicKey(*values.leaf_public_key_bytes,
                                     *values.leaf_signature,
                                     application_public_key.get()));
  ASSIGN_OR_RETURN(auto leaf_public_key,
                   EC_KEYFromX962(*values.leaf_public_key_bytes));
  RETURN_IF_ERROR(VerifyHandshakeHash(
      handshake_hash, *values.handshake_signature, leaf_public_key.get()));

  AttestationResult result;
  result.boot_loader = attestation_report.data.reported_tcb.boot_loader;
  result.tee = attestation_report.data.reported_tcb.tee;
  result.snp = attestation_report.data.reported_tcb.snp;
  result.microcode = attestation_report.data.reported_tcb.microcode;

  return result;
}

}  // namespace device::enclave
