Skip to content
Draft
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
13 changes: 10 additions & 3 deletions doc/operations/platforms/snp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ AMD SEV-SNP
How to use the AMD SEV-SNP platform
-----------------------------------

CCF must run on an AMD CPU which supports SEV-SNP, such as `Azure confidential containers <https://learn.microsoft.com/en-us/azure/confidential-computing/confidential-containers>`_ or `Azure Kubernetes Service with Confidential Containers <https://learn.microsoft.com/en-us/azure/aks/confidential-containers-overview>`_.
CCF must run on an AMD CPU which supports SEV-SNP, such as `Azure confidential containers <https://learn.microsoft.com/en-us/azure/confidential-computing/confidential-containers>`_, `Azure Kubernetes Service with Confidential Containers <https://learn.microsoft.com/en-us/azure/aks/confidential-containers-overview>`_, or AWS EC2 SEV-SNP instances.

CCF will use the SEV-SNP platform features automatically on the supported hardware.

Expand Down Expand Up @@ -48,7 +48,12 @@ AMD VCEK endorsements must be fetched, preferably from the THIM service, but con
Non-Azure Deployment
~~~~~~~~~~~~~~~~~~~~

For non-Azure deployments, the certificate chain for VCEK can be retrieved either from file, if already cached, or from an endorsement server, as specified in the :ref:`operations/configuration:``attestation.snp_endorsements_servers``` configuration section. For example, for the `well-known AMD endorsement server <https://docs.amd.com/v/u/en-US/57230>`_, the value should be set to:
For non-Azure deployments, configure an endorsement server in the :ref:`operations/configuration:``attestation.snp_endorsements_servers``` configuration section. CCF selects the collateral flow from the signing key recorded in the attestation report:

- For VCEK-signed reports, CCF retrieves the chip-specific VCEK and certificate chain.
- For VLEK-signed reports, such as reports from AWS EC2 shared-tenancy SEV-SNP instances, CCF obtains the VLEK leaf certificate from the host through ``SNP_GET_EXT_REPORT`` and retrieves the ASVK/ARK chain from AMD. The leaf is read from the certificate table published by the hypervisor, under either the VLEK or the VCEK GUID and in either big-endian or mixed-endian byte order, since hosts differ in which slot and encoding they use.

For the `well-known AMD endorsement server <https://docs.amd.com/v/u/en-US/57230>`_, the value should be set to:

.. code-block:: json

Expand All @@ -66,7 +71,9 @@ For non-Azure deployments, the certificate chain for VCEK can be retrieved eithe

.. tip:: See :ccf_repo:`samples/config/start_config_amd_sev_snp.json` for a sample node configuration for non-Azure deployments.

.. note:: If no local file is available, the CCF node will fetch the AMD VCEK endorsements from the server on startup, which may cause substantial deployment delays (up to tens of seconds) depending on network latency and endpoint throttling.
.. note:: CCF fetches the AMD certificate chain from the server on startup, which may cause substantial deployment delays (up to tens of seconds) depending on network latency and endpoint throttling.

.. note:: The ``snp_endorsements_file`` option contains VCEK collateral and is not used for VLEK-signed reports.

Governance Proposals
~~~~~~~~~~~~~~~~~~~~
Expand Down
72 changes: 58 additions & 14 deletions include/ccf/pal/attestation_sev_snp.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
"Cannot cast GuestPolicy to uint64_t");

static constexpr uint8_t attestation_flags_signing_key_vcek = 0;
static constexpr uint8_t attestation_flags_signing_key_vlek = 1;

#pragma pack(push, 1)
struct Flags
Expand Down Expand Up @@ -489,24 +490,44 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==

EndorsementEndpointsConfiguration config;

auto chip_id_hex =
fmt::format("{:02x}", fmt::join(quote.get_chip_id_for_vcek(), ""));
auto reported_tcb = fmt::format(
"{:0x}", *reinterpret_cast<const uint64_t*>(&quote.reported_tcb));
const bool is_vlek =
quote.flags.signing_key == attestation_flags_signing_key_vlek;

std::optional<std::string> chip_id_hex = std::nullopt;
if (!is_vlek)
{
chip_id_hex =
fmt::format("{:02x}", fmt::join(quote.get_chip_id_for_vcek(), ""));
}

constexpr size_t default_max_retries_count = 10;
static const ds::SizeString default_max_client_response_size =
ds::SizeString("100mb");

if (endorsements_servers.empty())
{
// Default to Azure server if no servers are specified
config.servers.emplace_back(make_azure_endorsements_server(
default_azure_endorsements_endpoint,
chip_id_hex,
reported_tcb,
default_max_retries_count,
default_max_client_response_size));
if (is_vlek)
{
const auto product =
get_sev_snp_product(quote.cpuid_fam_id, quote.cpuid_mod_id);
config.servers.emplace_back(make_amd_vlek_endorsements_server(
default_amd_endorsements_endpoint,
product,
default_max_retries_count,
default_max_client_response_size));
}
else
{
// Default to Azure server if no servers are specified
config.servers.emplace_back(make_azure_endorsements_server(
default_azure_endorsements_endpoint,
chip_id_hex.value(),
reported_tcb,
default_max_retries_count,
default_max_client_response_size));
}
return config;
}

Expand All @@ -521,11 +542,17 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
{
case EndorsementsEndpointType::Azure:
{
if (is_vlek)
{
throw std::logic_error(
"Azure endorsements endpoints do not support VLEK-signed "
"attestation reports");
}
auto loc =
get_endpoint_loc(server, default_azure_endorsements_endpoint);
config.servers.emplace_back(make_azure_endorsements_server(
loc,
chip_id_hex,
chip_id_hex.value(),
reported_tcb,
max_retries_count,
max_client_response_size));
Expand All @@ -536,6 +563,15 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
auto product =
get_sev_snp_product(quote.cpuid_fam_id, quote.cpuid_mod_id);

auto loc =
get_endpoint_loc(server, default_amd_endorsements_endpoint);
if (is_vlek)
{
config.servers.emplace_back(make_amd_vlek_endorsements_server(
loc, product, max_retries_count, max_client_response_size));
break;
}

std::string boot_loader;
std::string tee;
std::string snp;
Expand Down Expand Up @@ -570,11 +606,9 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
}
}

auto loc =
get_endpoint_loc(server, default_amd_endorsements_endpoint);
config.servers.emplace_back(make_amd_endorsements_server(
loc,
chip_id_hex,
chip_id_hex.value(),
boot_loader,
tee,
snp,
Expand All @@ -587,11 +621,17 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
}
case EndorsementsEndpointType::THIM:
{
if (is_vlek)
{
throw std::logic_error(
"THIM endorsements endpoints do not support VLEK-signed "
"attestation reports");
}
auto loc =
get_endpoint_loc(server, default_thim_endorsements_endpoint);
config.servers.emplace_back(make_thim_endorsements_server(
loc,
chip_id_hex,
chip_id_hex.value(),
reported_tcb,
max_retries_count,
max_client_response_size));
Expand All @@ -613,6 +653,10 @@ pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
public:
[[nodiscard]] virtual const snp::Attestation& get() const = 0;
virtual std::vector<uint8_t> get_raw() = 0;
virtual std::vector<std::vector<uint8_t>> get_endorsements()
{
return {};
}

virtual ~AttestationInterface() = default;
};
Expand Down
19 changes: 19 additions & 0 deletions include/ccf/pal/attestation_sev_snp_endorsements.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,25 @@ namespace ccf::pal::snp
return server;
}

static EndorsementEndpointsConfiguration::Server
make_amd_vlek_endorsements_server(
const HostPort& endpoint,
const ProductName& product_name,
size_t max_retries_count,
size_t max_client_response_size)
{
EndorsementEndpointsConfiguration::EndpointInfo chain{
.host = endpoint.host,
.port = endpoint.port,
.uri = fmt::format("/vlek/v1/{}/cert_chain", to_string(product_name)),
.params = {},
.headers = {}};
chain.max_retries_count = max_retries_count;
chain.max_client_response_size = max_client_response_size;

return {chain};
}

static HostPort default_thim_endorsements_endpoint = {
"169.254.169.254", "80"};

Expand Down
Loading
Loading