Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions include/ccf/pal/attestation_sev_snp.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,25 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
-----END PUBLIC KEY-----
)";

inline const std::map<ProductName, const char*> amd_root_signing_keys{
{ProductName::Milan, amd_milan_root_signing_public_key},
{ProductName::Genoa, amd_genoa_root_signing_public_key},
{ProductName::Turin, amd_turin_root_signing_public_key},
struct AmdRootSigningKey
{
const char* public_key;
const char* issuer;
};

inline const std::map<ProductName, AmdRootSigningKey> amd_root_signing_keys{
{ProductName::Milan,
Comment thread
achamayou marked this conversation as resolved.
{amd_milan_root_signing_public_key,
"CN=ARK-Milan,O=Advanced Micro Devices,ST=CA,L=Santa Clara,C=US,"
"OU=Engineering"}},
{ProductName::Genoa,
{amd_genoa_root_signing_public_key,
"CN=ARK-Genoa,O=Advanced Micro Devices,ST=CA,L=Santa Clara,C=US,"
"OU=Engineering"}},
{ProductName::Turin,
{amd_turin_root_signing_public_key,
"CN=ARK-Turin,O=Advanced Micro Devices,ST=CA,L=Santa Clara,C=US,"
"OU=Engineering"}},
};

#pragma pack(push, 1)
Expand Down
77 changes: 66 additions & 11 deletions src/pal/attestation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
#include "ccf/pal/attestation.h"

#include "ccf/crypto/ecdsa.h"
#include "ccf/crypto/key_pair.h"
#include "ccf/crypto/openssl/openssl_wrappers.h"
#include "ccf/crypto/verifier.h"
#include "ccf/ds/hex.h"
#include "ccf/pal/attestation_sev_snp.h"
#include "ccf/pal/sev_snp_cpuid.h"

#include <cstdint>
#include <openssl/objects.h>

namespace ccf::pal
{
Expand All @@ -18,6 +21,54 @@ namespace ccf::pal
using Unique_ASN1_INTEGER = ccf::crypto::OpenSSL::
Unique_SSL_OBJECT<ASN1_INTEGER, ASN1_INTEGER_new, ASN1_INTEGER_free>;

namespace
{
std::string x509_name_to_rfc2253_string(X509_NAME* name)
{
ccf::crypto::OpenSSL::CHECKNULL(name);

ccf::crypto::OpenSSL::Unique_BIO mem;
const auto rc = X509_NAME_print_ex(mem, name, 0, XN_FLAG_RFC2253);
if (rc < 0)
{
const auto ec = ERR_get_error();
throw std::runtime_error(fmt::format(
"OpenSSL error (rc={}, ec={}): {}",
rc,
ec,
ccf::crypto::OpenSSL::error_string(ec)));
}

BUF_MEM* bptr = nullptr;
ccf::crypto::OpenSSL::CHECK1(BIO_get_mem_ptr(mem, &bptr));
ccf::crypto::OpenSSL::CHECKNULL(bptr);

return {bptr->data, bptr->length};
}

void verify_ark_certificate_matches_pinned_metadata(
const crypto::Pem& ark_cert,
snp::ProductName product_family,
const snp::AmdRootSigningKey& expected_ark)
{
ccf::crypto::OpenSSL::Unique_BIO mem_bio(ark_cert);
ccf::crypto::OpenSSL::Unique_X509 x509(
mem_bio, true, true /* check_null */);

const auto issuer =
x509_name_to_rfc2253_string(X509_get_issuer_name(x509));
if (issuer != expected_ark.issuer)
{
throw std::logic_error(fmt::format(
"SEV-SNP: The root of trust issuer for this attestation was not "
"the expected one for {}: {} != {}",
product_family,
issuer,
expected_ark.issuer));
}
}
}

void verify_virtual_attestation_report(
const QuoteInfo& quote_info,
PlatformAttestationMeasurement& measurement,
Expand Down Expand Up @@ -259,28 +310,32 @@ namespace ccf::pal

auto ark_verifier = ccf::crypto::make_verifier(ark_cert);

std::string expected_ark;
auto key = snp::amd_root_signing_keys.find(product_family);
if (key == snp::amd_root_signing_keys.end())
{
auto key = snp::amd_root_signing_keys.find(product_family);
if (key == snp::amd_root_signing_keys.end())
{
throw std::logic_error(fmt::format(
"SEV-SNP: No known root certificate for {}", product_family));
}
expected_ark = key->second;
throw std::logic_error(fmt::format(
"SEV-SNP: No known root certificate for {}", product_family));
}
if (ark_verifier->public_key_pem().str() != expected_ark)
const auto& expected_ark = key->second;
const auto actual_public_key_der = ark_verifier->public_key_der();
const auto expected_public_key_der =
ccf::crypto::make_public_key(ccf::crypto::Pem(expected_ark.public_key))
->public_key_der();
if (actual_public_key_der != expected_public_key_der)
{
throw std::logic_error(fmt::format(
"SEV-SNP: The root of trust public key for this attestation was not "
"the expected one for v{} {} {}: {} != {}",
quote.version,
quote.cpuid_fam_id,
quote.cpuid_mod_id,
ark_verifier->public_key_pem().str(),
expected_ark));
ccf::ds::to_hex(actual_public_key_der),
ccf::ds::to_hex(expected_public_key_der)));
}

verify_ark_certificate_matches_pinned_metadata(
ark_cert, product_family, expected_ark);

if (!ark_verifier->verify_certificate({&ark_cert}))
{
throw std::logic_error(
Expand Down
100 changes: 100 additions & 0 deletions src/pal/test/snp_attestation_validation.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.

#include "ccf/crypto/openssl/openssl_wrappers.h"
#include "ccf/crypto/verifier.h"
#include "ccf/ds/hex.h"
#include "ccf/ds/logger.h"
#include "ccf/ds/quote_info.h"
Expand All @@ -16,9 +18,91 @@
#include "pal/test/attestation_sev_snp_endorsements.h"
#include "pal/test/snp_attestation_validation_data.h"

#include <algorithm>
#include <array>
#include <memory>

#define DOCTEST_CONFIG_IMPLEMENT
#include <doctest/doctest.h>

namespace
{
std::vector<ccf::crypto::Pem> milan_endorsement_certs()
{
return ccf::crypto::split_x509_cert_bundle(
ccf::pal::snp::testing::milan_endorsements);
}

std::vector<uint8_t> endorsement_bundle_with_ark(
const ccf::crypto::Pem& ark_cert)
{
auto certs = milan_endorsement_certs();
REQUIRE(certs.size() == 3);

certs[2] = ark_cert;
std::string bundle;
for (const auto& cert : certs)
{
bundle += cert.str();
}

return {bundle.begin(), bundle.end()};
}

ccf::QuoteInfo milan_quote_info_with_ark(const ccf::crypto::Pem& ark_cert)
{
return {
.format = ccf::QuoteFormat::amd_sev_snp_v1,
.quote = ccf::pal::snp::testing::milan_attestation,
.endorsements = endorsement_bundle_with_ark(ark_cert),
.uvm_endorsements = std::nullopt,
};
}

ccf::crypto::Pem pem_from_x509(X509* x509)
{
ccf::crypto::OpenSSL::CHECKNULL(x509);

ccf::crypto::OpenSSL::Unique_BIO mem;
ccf::crypto::OpenSSL::CHECK1(PEM_write_bio_X509(mem, x509));

BUF_MEM* bptr = nullptr;
ccf::crypto::OpenSSL::CHECK1(BIO_get_mem_ptr(mem, &bptr));
ccf::crypto::OpenSSL::CHECKNULL(bptr);

return ccf::crypto::Pem(
reinterpret_cast<const uint8_t*>(bptr->data), bptr->length);
}

ccf::crypto::Pem ark_with_extra_issuer_entry()
{
const auto certs = milan_endorsement_certs();
REQUIRE(certs.size() == 3);

ccf::crypto::OpenSSL::Unique_BIO mem_bio(certs[2]);
ccf::crypto::OpenSSL::Unique_X509 x509(
mem_bio, true, true /* check_null */);

std::unique_ptr<X509_NAME, decltype(&X509_NAME_free)> issuer(
X509_NAME_dup(X509_get_issuer_name(x509)), X509_NAME_free);
ccf::crypto::OpenSSL::CHECKNULL(issuer.get());

static constexpr auto unexpected_ou = "Unexpected";
ccf::crypto::OpenSSL::CHECK1(X509_NAME_add_entry_by_txt(
issuer.get(),
"OU",
MBSTRING_ASC,
reinterpret_cast<const unsigned char*>(unexpected_ou),
-1,
-1,
0));
ccf::crypto::OpenSSL::CHECK1(X509_set_issuer_name(x509, issuer.get()));

return pem_from_x509(x509);
}

}

TEST_CASE("milan validation")
{
using namespace ccf;
Expand Down Expand Up @@ -104,6 +188,22 @@ TEST_CASE("Mismatched attestation and endorsements fail")
std::logic_error);
}

TEST_CASE("ARK with unexpected issuer fails")
{
auto quote_info = milan_quote_info_with_ark(ark_with_extra_issuer_entry());

ccf::pal::PlatformAttestationMeasurement measurement;
ccf::pal::PlatformAttestationReportData report_data;

CHECK_THROWS_WITH_AS(
ccf::pal::verify_snp_attestation_report(
quote_info, measurement, report_data),
doctest::Contains(
"SEV-SNP: The root of trust issuer for this attestation was not "
"the expected one"),
std::logic_error);
}

TEST_CASE("Parsing of Tcb versions from strings")
{
const auto milan_tcb_version_raw =
Expand Down