Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SEV-SNP ACI: Retrieve attestation report endorsements from environment #4940

Merged
merged 26 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b708c4
Configuration file
Jan 31, 2023
13f5bc3
Get environment variable
Jan 31, 2023
414d809
WIP - segfault
Jan 31, 2023
53b83fd
Working
Feb 1, 2023
8f41eed
Works
Feb 1, 2023
838d29c
Better
Feb 1, 2023
628e99e
Cleanup
Feb 1, 2023
178ab04
Test and fixes
Feb 1, 2023
029af82
Merge branch 'main' of github.com:microsoft/CCF into snp_quote_endors…
Feb 1, 2023
6dc3db3
Fixes
Feb 1, 2023
bb1a12c
Changelog
Feb 1, 2023
2fa96c8
Merge branch 'main' of github.com:microsoft/CCF into snp_quote_endors…
Feb 2, 2023
a460a89
Merge branch 'snp_quote_endorsement_envvar' of github.com:jumaffre/CC…
Feb 2, 2023
118e529
Fix compilation on SGX
Feb 2, 2023
3e0fa47
Fix env
Feb 2, 2023
630b1a7
Merge branch 'main' into snp_quote_endorsement_envvar
jumaffre Feb 2, 2023
7ee0392
Merge branch 'main' into snp_quote_endorsement_envvar
jumaffre Feb 2, 2023
4a94b69
Merge branch 'main' of github.com:microsoft/CCF into snp_quote_endors…
Feb 3, 2023
63590ab
Merge branch 'main' into snp_quote_endorsement_envvar
jumaffre Feb 3, 2023
49aad7a
Merge branch 'main' into snp_quote_endorsement_envvar
jumaffre Feb 3, 2023
4f1220d
Merge branch 'main' into snp_quote_endorsement_envvar
DomAyre Feb 6, 2023
75ef7be
Merge branch 'main' into snp_quote_endorsement_envvar
DomAyre Feb 7, 2023
53f6c01
Merge branch 'main' into snp_quote_endorsement_envvar
DomAyre Feb 7, 2023
2d1619d
D'oh
Feb 7, 2023
25a6847
Merge branch 'snp_quote_endorsement_envvar' of github.com:jumaffre/CC…
Feb 7, 2023
00f22d8
Merge branch 'main' into snp_quote_endorsement_envvar
jumaffre Feb 7, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added `ccf.enableUntrustedDateTime` to JS API. After calling `ccf.enableUntrustedDateTime(true)`, the `Date` global object will use the untrusted host time to retrieve the current time.
- Add new `ccf.crypto.jwkToPem`, `ccf.crypto.pubJwkToPem`, `ccf.crypto.rsaJwkToPem`, `ccf.crypto.pubRsaJwkToPem`, `ccf.crypto.eddsaJwkToPem`, `ccf.crypto.pubEddsaJwkToPem` to JavaScript/TypesScript API to convert EC/RSA/EdDSA keys from PEM to Json Web Key (#4876).
- Add new constructors to cryptography C++ API to generate EC/RSA/EdDSA keys from Json Web Key (#4876).
- Endorsement certificates for SEV-SNP attestation report can now be retrieved via an environment variable, as specified by `attestation.environment.report_endorsements` configuration entry (#4940).

## [4.0.0-dev3]

Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ if(COMPILE_TARGET STREQUAL "sgx")
ccf.enclave ${CCF_IMPL_SOURCE} ${CCF_GENERATED_DIR}/ccf_t.cpp
)

target_compile_definitions(ccf.enclave PUBLIC PLATFORM_SGX)
jumaffre marked this conversation as resolved.
Show resolved Hide resolved

add_warning_checks(ccf.enclave)

target_include_directories(
Expand Down
36 changes: 20 additions & 16 deletions doc/host_config_schema/cchost_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,25 @@
"attestation": {
"type": "object",
"properties": {
"environment": {
"type": "object",
"properties": {
"report_endorsements": {
"type": ["string", "null"],
"description": "Name of environment variable (e.g. ``UVM_HOST_AMD_CERTIFICATE``) containing base64-encoded attestation report endorsements as JSON object (Azure Container Instance SEV-SNP only). If this is set, specified ``snp_endorsements_servers`` are ignored"
},
"security_policy": {
"type": ["string", "null"],
"description": "Name of environment variable (e.g. ``UVM_SECURITY_POLICY``) containing base64-encoded security policy (Azure Container Instance SEV-SNP only)"
},
"uvm_endorsements": {
"type": ["string", "null"],
"description": "Name of environment variable (e.g. ``UVM_REFERENCE_INFO``) containing base64-encoded UVM endorsements as COSE Sign1 document (Azure Container Instance SEV-SNP only)"
}
},
"description": "Environment variables required to provide best auditability and serviceability for Azure Container Instance deployments (SEV-SNP only)",
"additionalProperties": false
},
"snp_endorsements_servers": {
"type": "array",
"items": {
Expand All @@ -459,22 +478,7 @@
"required": ["url"],
"additionalProperties": false
},
"description": "List of servers used to retrieve attestation report endorsement certificates (SEV-SNP only). The first server in the list is always used and other servers are only specified as fallback"
},
"environment": {
"type": "object",
"properties": {
"security_policy": {
"type": ["string", "null"],
"description": "Name of environment variable (e.g. ``UVM_SECURITY_POLICY``) containing base64-encoded security policy (Azure Container Instance SEV-SNP only)"
},
"uvm_endorsements": {
"type": ["string", "null"],
"description": "Name of environment variable (e.g. ``UVM_REFERENCE_INFO``) containing base64-encoded UVM endorsements (Azure Container Instance SEV-SNP only)"
}
},
"description": "Environment variables required to provide best auditability and serviceability for Azure Container Instance deployments (SEV-SNP only)",
"additionalProperties": false
"description": "List of servers used to retrieve attestation report endorsement certificates (SEV-SNP only). The first server in the list is always used and other servers are only specified as fallback. Ignored if ``environment.report_endorsements`` is set"
}
},
"description": "This section includes configuration for the attestation for AMD SEV-SNP platform (ignored for SGX)",
Expand Down
1 change: 1 addition & 0 deletions include/ccf/node/startup_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct CCFConfig
{
std::optional<std::string> security_policy = std::nullopt;
std::optional<std::string> uvm_endorsements = std::nullopt;
std::optional<std::string> report_endorsements = std::nullopt;

bool operator==(const Environment&) const = default;
};
Expand Down
5 changes: 5 additions & 0 deletions include/ccf/pal/attestation.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ namespace ccf::pal
auto certificates = crypto::split_x509_cert_bundle(std::string_view(
reinterpret_cast<const char*>(quote_info.endorsements.data()),
quote_info.endorsements.size()));
if (certificates.size() != 3)
{
throw std::logic_error(fmt::format(
"Expected 3 endorsement certificates but got {}", certificates.size()));
}
auto chip_certificate = certificates[0];
auto sev_version_certificate = certificates[1];
auto root_certificate = certificates[2];
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 @@ -16,6 +16,25 @@ namespace ccf::pal::snp
{
constexpr auto product_name = "Milan";

struct ACIReportEndorsements
{
std::string cache_control;
std::string vcek_cert;
std::string certificate_chain;
std::string tcbm;
};
DECLARE_JSON_TYPE(ACIReportEndorsements);
DECLARE_JSON_REQUIRED_FIELDS_WITH_RENAMES(
ACIReportEndorsements,
cache_control,
"cacheControl",
vcek_cert,
"vcekCert",
certificate_chain,
"certificateChain",
tcbm,
"tcbm");

struct EndorsementEndpointsConfiguration
{
struct EndpointInfo
Expand Down
34 changes: 34 additions & 0 deletions include/ccf/pal/platform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once

#include "ccf/ds/json.h"

namespace ccf::pal
{
enum class Platform
{
SGX = 0,
SNP = 1,
Virtual = 2,
Unknown = 3,
};
DECLARE_JSON_ENUM(
Platform,
{{Platform::SGX, "SGX"},
{Platform::SNP, "SNP"},
{Platform::Virtual, "Virtual"},
{Platform::Unknown, "Unknown"}});

constexpr static auto platform =
#if defined(PLATFORM_SGX)
Platform::SGX
#elif defined(PLATFORM_SNP)
Platform::SNP
#elif defined(PLATFORM_VIRTUAL)
Platform::Virtual
#else
Platform::Unknown
#endif
;
}
1 change: 1 addition & 0 deletions scripts/azure_deployment/arm_aci.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def append_envvar_to_well_known_file(envvar):
return [
append_envvar_to_well_known_file("UVM_SECURITY_POLICY"),
append_envvar_to_well_known_file("UVM_REFERENCE_INFO"),
append_envvar_to_well_known_file("UVM_HOST_AMD_CERIFICATE"),
]


Expand Down
5 changes: 4 additions & 1 deletion src/common/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ DECLARE_JSON_OPTIONAL_FIELDS(CCFConfig::JWT, key_refresh_interval);
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(CCFConfig::Attestation::Environment);
DECLARE_JSON_REQUIRED_FIELDS(CCFConfig::Attestation::Environment);
DECLARE_JSON_OPTIONAL_FIELDS(
CCFConfig::Attestation::Environment, security_policy, uvm_endorsements);
CCFConfig::Attestation::Environment,
security_policy,
uvm_endorsements,
report_endorsements);

DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(CCFConfig::Attestation);
DECLARE_JSON_REQUIRED_FIELDS(CCFConfig::Attestation);
Expand Down
26 changes: 18 additions & 8 deletions src/host/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "ccf/ds/logger.h"
#include "ccf/pal/attestation.h"
#include "ccf/pal/platform.h"
#include "ccf/version.h"
#include "config_schema.h"
#include "configuration.h"
Expand Down Expand Up @@ -52,14 +53,7 @@ void print_version(size_t)
{
std::cout << "CCF host: " << ccf::ccf_version << std::endl;
std::cout << "Platform: "
<<
#if defined(PLATFORM_SGX)
"SGX"
#elif defined(PLATFORM_SNP)
"SNP"
#elif defined(PLATFORM_VIRTUAL)
"Virtual"
#endif
<< nlohmann::json(ccf::pal::platform).get<std::string>()
<< std::endl;
exit(0);
}
Expand Down Expand Up @@ -448,6 +442,22 @@ int main(int argc, char** argv)
"UVM endorsements");
}

if (config.attestation.environment.report_endorsements.has_value())
{
startup_config.attestation.environment.report_endorsements =
read_required_environment_variable(
config.attestation.environment.report_endorsements.value(),
"attestation report endorsements");
}
else if (
ccf::pal::platform == ccf::pal::Platform::SNP &&
config.attestation.snp_endorsements_servers.empty())
{
LOG_FATAL_FMT(
"On SEV-SNP, either one of report endorsements environment variable or "
"endorsements server should be set");
}

if (config.node_data_json_file.has_value())
{
startup_config.node_data =
Expand Down
94 changes: 64 additions & 30 deletions src/node/node_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "ccf/ds/logger.h"
#include "ccf/pal/attestation.h"
#include "ccf/pal/locking.h"
#include "ccf/pal/platform.h"
#include "ccf/serdes.h"
#include "ccf/service/node_info_network.h"
#include "ccf/service/tables/acme_certificates.h"
Expand Down Expand Up @@ -374,42 +375,74 @@ namespace ccf
{
auto fetch_endorsements =
[this](
const QuoteInfo& quote_info_,
const QuoteInfo& qi,
const pal::snp::EndorsementEndpointsConfiguration& endpoint_config) {
if (quote_info_.format != QuoteFormat::amd_sev_snp_v1)
// Note: Node lock is already taken here as this is called back
// synchronously with the call to pal::generate_quote

if (
qi.format == QuoteFormat::amd_sev_snp_v1 &&
!config.attestation.environment.report_endorsements.has_value())
{
// Note: Node lock is already taken here as this is called back
// synchronously with the call to pal::generate_quote
CCF_ASSERT_FMT(
quote_info_.format == QuoteFormat::insecure_virtual ||
!quote_info_.endorsements.empty(),
"SGX quote generation should have already fetched endorsements");
quote_info = quote_info_;
launch_node();
// On SEV-SNP, if no attestation report endorsements are set via
// environment, those need to be fetched
quote_endorsements_client =
std::make_shared<QuoteEndorsementsClient>(
rpcsessions,
endpoint_config,
[this, qi](std::vector<uint8_t>&& endorsements) {
std::lock_guard<pal::Mutex> guard(lock);
quote_info = qi;
quote_info.endorsements = std::move(endorsements);
try
{
launch_node();
}
catch (const std::exception& e)
{
LOG_FAIL_FMT("{}", e.what());
throw;
}
quote_endorsements_client.reset();
});

quote_endorsements_client->fetch_endorsements();
return;
}

quote_endorsements_client = std::make_shared<QuoteEndorsementsClient>(
rpcsessions,
endpoint_config,
[this, quote_info_](std::vector<uint8_t>&& endorsements) {
// Note: Only called for SEV-SNP
std::lock_guard<pal::Mutex> guard(lock);
quote_info = quote_info_;
quote_info.endorsements = std::move(endorsements);
try
{
launch_node();
}
catch (const std::exception& e)
{
LOG_FAIL_FMT("{}", e.what());
throw;
}
quote_endorsements_client.reset();
});
CCF_ASSERT_FMT(
(qi.format == QuoteFormat::oe_sgx_v1 && !qi.endorsements.empty()) ||
(qi.format != QuoteFormat::oe_sgx_v1 && qi.endorsements.empty()),
"SGX quote generation should have already fetched endorsements");

quote_endorsements_client->fetch_endorsements();
quote_info = qi;

if (
quote_info.format == QuoteFormat::amd_sev_snp_v1 &&
config.attestation.environment.report_endorsements.has_value())
{
// On SEV-SNP, if reports endorsements are passed via
// environment, read those rather than fetching them from
// endorsement server
pal::snp::ACIReportEndorsements endorsements =
nlohmann::json::parse(crypto::raw_from_b64(
config.attestation.environment.report_endorsements.value()));

CCF_ASSERT_FMT(
quote_info.endorsements.empty(),
"No endorsements should be set by quote generation");

quote_info.endorsements.insert(
quote_info.endorsements.end(),
endorsements.vcek_cert.begin(),
endorsements.vcek_cert.end());
jumaffre marked this conversation as resolved.
Show resolved Hide resolved
quote_info.endorsements.insert(
quote_info.endorsements.end(),
endorsements.certificate_chain.begin(),
endorsements.certificate_chain.end());
}

launch_node();
};

pal::attestation_report_data report_data = {};
Expand All @@ -418,6 +451,7 @@ namespace ccf
node_pub_key_hash.h.begin(),
node_pub_key_hash.h.end(),
report_data.begin());

pal::generate_quote(
report_data,
fetch_endorsements,
Expand Down
2 changes: 1 addition & 1 deletion src/node/quote_endorsements_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace ccf

// Maximum number of retries per remote server before giving up and moving
// on to the next server.
static constexpr size_t max_server_retries_count = 2;
static constexpr size_t max_server_retries_count = 3;

std::shared_ptr<RPCSessions> rpcsessions;

Expand Down
4 changes: 2 additions & 2 deletions tests/code_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def test_add_node_without_security_policy(network, args):
args.package,
args,
timeout=3,
security_policy_envvar=None,
set_snp_security_policy_envvar=True,
)
network.trust_node(new_node, args)
return network
Expand Down Expand Up @@ -162,7 +162,7 @@ def test_start_node_with_mismatched_host_data(network, args):
timeout=3,
snp_security_policy=b64encode(b"invalid_security_policy").decode(),
)
except TimeoutError:
except (TimeoutError, RuntimeError):
LOG.info("As expected, node with invalid security policy failed to startup")
else:
raise AssertionError("Node startup unexpectedly succeeded")
Expand Down
3 changes: 2 additions & 1 deletion tests/config.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"environment":
{
"security_policy": {{ snp_security_policy_envvar|tojson }},
"uvm_endorsements": {{ snp_uvm_endorsements_envvar|tojson }}
"uvm_endorsements": {{ snp_uvm_endorsements_envvar|tojson }},
"report_endorsements": {{ snp_report_endorsements_envvar|tojson }}
}
},
"service_data_json_file": {{ service_data_json_file|tojson }},
Expand Down
Loading