Skip to content

Commit abf1048

Browse files
Add support for validating Genoa attestations (#7051)
Co-authored-by: Amaury Chamayou <amaury@xargs.fr> Co-authored-by: Amaury Chamayou <amchamay@microsoft.com>
1 parent 48c3f17 commit abf1048

File tree

6 files changed

+461
-5
lines changed

6 files changed

+461
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
### Fixed
1313

1414
- Nodes will now avoid re-parsing `.committed` files in the main directory if they have established a later commit point in the `read_only` directories. This should significantly reduce start-up time for nodes with large existing ledgers.
15+
- Added support for validating Genoa attestations (#7051).
1516

1617
### Changed
1718

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,11 @@ if(BUILD_TESTS)
712712
)
713713
endif()
714714

715+
add_unit_test(
716+
snp_attestation_test
717+
${CMAKE_CURRENT_SOURCE_DIR}/src/pal/test/snp_attestation_validation.cpp
718+
)
719+
715720
add_unit_test(map_test ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/test/map_test.cpp)
716721

717722
add_unit_test(

include/ccf/pal/attestation.h

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "ccf/ds/hex.h"
99
#include "ccf/ds/logger.h"
1010
#include "ccf/ds/quote_info.h"
11+
#include "ccf/pal/attestation_sev_snp.h"
1112
#include "ccf/pal/measurement.h"
1213
#include "ccf/pal/snp_ioctl.h"
1314

@@ -108,20 +109,45 @@ namespace ccf::pal
108109
throw std::logic_error(fmt::format(
109110
"Expected 3 endorsement certificates but got {}", certificates.size()));
110111
}
112+
113+
// chip_cert (VCEK) <-signs- sev_version (ASK)
114+
// ASK <-signs- root_certificate (ARK)
111115
auto chip_certificate = certificates[0];
112116
auto sev_version_certificate = certificates[1];
113117
auto root_certificate = certificates[2];
114118

115119
auto root_cert_verifier = ccf::crypto::make_verifier(root_certificate);
116120

117-
if (
118-
root_cert_verifier->public_key_pem().str() !=
119-
snp::amd_milan_root_signing_public_key)
121+
std::string expected_root_public_key;
122+
if (quote.version < 3)
123+
{
124+
// before version 3 there are no cpuid fields so we must assume that it is
125+
// milan
126+
expected_root_public_key = snp::amd_milan_root_signing_public_key;
127+
}
128+
else
129+
{
130+
auto key = snp::amd_root_signing_keys.find(
131+
std::make_pair(quote.cpuid_fam_id, quote.cpuid_mod_id));
132+
if (key == snp::amd_root_signing_keys.end())
133+
{
134+
throw std::logic_error(fmt::format(
135+
"SEV-SNP: Unsupported CPUID family {} model {}",
136+
quote.cpuid_fam_id,
137+
quote.cpuid_mod_id));
138+
}
139+
expected_root_public_key = key->second;
140+
}
141+
if (root_cert_verifier->public_key_pem().str() != expected_root_public_key)
120142
{
121143
throw std::logic_error(fmt::format(
122144
"SEV-SNP: The root of trust public key for this attestation was not "
123-
"the expected one {}",
124-
root_cert_verifier->public_key_pem().str()));
145+
"the expected one for v{} {} {}: {} != {}",
146+
quote.version,
147+
quote.cpuid_fam_id,
148+
quote.cpuid_mod_id,
149+
root_cert_verifier->public_key_pem().str(),
150+
expected_root_public_key));
125151
}
126152

127153
if (!root_cert_verifier->verify_certificate({&root_certificate}))
@@ -140,6 +166,7 @@ namespace ccf::pal
140166
"attestation is broken");
141167
}
142168

169+
// According to Table 134 (2025-06-12) only ecdsa_p384_sha384 is supported
143170
if (quote.signature_algo != snp::SignatureAlgorithm::ecdsa_p384_sha384)
144171
{
145172
throw std::logic_error(fmt::format(

include/ccf/pal/attestation_sev_snp.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "ccf/pal/report_data.h"
99

1010
#include <array>
11+
#include <cstdint>
1112
#include <map>
1213
#include <string>
1314

@@ -35,6 +36,48 @@ pCCoMNit2uLo9M18fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXo
3536
QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==
3637
-----END PUBLIC KEY-----
3738
)";
39+
constexpr auto amd_genoa_root_signing_public_key =
40+
R"(-----BEGIN PUBLIC KEY-----
41+
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3Cd95S/uFOuRIskW9vz9
42+
VDBF69NDQF79oRhL/L2PVQGhK3YdfEBgpF/JiwWFBsT/fXDhzA01p3LkcT/7Ldjc
43+
RfKXjHl+0Qq/M4dZkh6QDoUeKzNBLDcBKDDGWo3v35NyrxbA1DnkYwUKU5AAk4P9
44+
4tKXLp80oxt84ahyHoLmc/LqsGsp+oq1Bz4PPsYLwTG4iMKVaaT90/oZ4I8oibSr
45+
u92vJhlqWO27d/Rxc3iUMyhNeGToOvgx/iUo4gGpG61NDpkEUvIzuKcaMx8IdTpW
46+
g2DF6SwF0IgVMffnvtJmA68BwJNWo1E4PLJdaPfBifcJpuBFwNVQIPQEVX3aP89H
47+
JSp8YbY9lySS6PlVEqTBBtaQmi4ATGmMR+n2K/e+JAhU2Gj7jIpJhOkdH9firQDn
48+
mlA2SFfJ/Cc0mGNzW9RmIhyOUnNFoclmkRhl3/AQU5Ys9Qsan1jT/EiyT+pCpmnA
49+
+y9edvhDCbOG8F2oxHGRdTBkylungrkXJGYiwGrR8kaiqv7NN8QhOBMqYjcbrkEr
50+
0f8QMKklIS5ruOfqlLMCBw8JLB3LkjpWgtD7OpxkzSsohN47Uom86RY6lp72g8eX
51+
HP1qYrnvhzaG1S70vw6OkbaaC9EjiH/uHgAJQGxon7u0Q7xgoREWA/e7JcBQwLg8
52+
0Hq/sbRuqesxz7wBWSY254cCAwEAAQ==
53+
-----END PUBLIC KEY-----
54+
)";
55+
constexpr auto amd_turin_root_signing_public_key =
56+
R"(-----BEGIN PUBLIC KEY-----
57+
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwaAriB7EIuVc4ZB1wD3Y
58+
fDxL+9eyS7+izm0Jj3W772NINCWl8Bj3w/JD2ZjmbRxWdIq/4d9iarCKorXloJUB
59+
1jRdgxqccTx1aOoig4+2w1XhVVJT7K457wT5ZLNJgQaxqa9Etkwjd6+9sOhlCDE9
60+
l43kQ0R2BikVJa/uyyVOSwEk5w5tXKOuG9jvq6QtAMJasW38wlqRDaKEGtZ9VUgG
61+
on27ZuL4sTJuC/azz9/iQBw8kEilzOl95AiTkeY5jSEBDWbAqnZk5qlM7kISKG20
62+
kgQm14mhNKDI2p2oua+zuAG7i52epoRF2GfU0TYk/yf+vCNB2tnechFQuP2e8bLk
63+
95ZdqPi9/UWw4JXjtdEA4u2JYplSSUPQVAXKt6LVqujtJcM59JKr2u0XQ75KwxcM
64+
p15gSXhBfInvPAwuAY4dEwwGqT8oIg4esPHwEsmChhYeDIxPG9R4fx9O0q6p8Gb+
65+
HXlTiS47P9YNeOpidOUKzDl/S1OvyhDtSL8LJc24QATFydo/iD/KUdvFTRlD0crk
66+
AMkZLoWQ8hLDGc6BZJXsdd7Zf2e4UW3tI/1oh/2t23Ot3zyhTcv5gDbABu0LjVe9
67+
8uRnS15SMwK//lJt9e5BqKvgABkSoABf+B4VFtPVEX0ygrYaFaI9i5ABrxnVBmzX
68+
pRb21iI1NlNCfOGUPIhVpWECAwEAAQ==
69+
-----END PUBLIC KEY-----
70+
)";
71+
72+
using AMDFamily = uint8_t;
73+
using AMDModel = uint8_t;
74+
inline const std::map<std::pair<AMDFamily, AMDModel>, const char*>
75+
amd_root_signing_keys{
76+
{{0x19, 0x01}, amd_milan_root_signing_public_key},
77+
{{0x19, 0x11}, amd_genoa_root_signing_public_key},
78+
// Disabled until we can test this
79+
//{{0x1A, 0x02}, amd_turin_root_signing_public_key},
80+
};
3881

3982
#pragma pack(push, 1)
4083
// Table 3
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
4+
#include "ccf/ds/hex.h"
5+
#include "ccf/ds/quote_info.h"
6+
#include "ccf/pal/attestation.h"
7+
#include "ccf/pal/measurement.h"
8+
#include "ccf/pal/report_data.h"
9+
#include "crypto/openssl/hash.h"
10+
#include "pal/test/snp_attestation_validation_data.h"
11+
12+
#define DOCTEST_CONFIG_IMPLEMENT
13+
#include <doctest/doctest.h>
14+
15+
TEST_CASE("milan validation")
16+
{
17+
using namespace ccf;
18+
19+
auto milan_quote_info = QuoteInfo{
20+
.format = QuoteFormat::amd_sev_snp_v1,
21+
.quote = pal::snp::testing::milan_attestation,
22+
.endorsements = std::vector<uint8_t>(
23+
pal::snp::testing::milan_endorsements.begin(),
24+
pal::snp::testing::milan_endorsements.end()),
25+
.uvm_endorsements = std::nullopt,
26+
};
27+
28+
pal::PlatformAttestationMeasurement measurement;
29+
pal::PlatformAttestationReportData report_data;
30+
31+
pal::verify_snp_attestation_report(
32+
milan_quote_info, measurement, report_data);
33+
}
34+
35+
TEST_CASE("genoa validation")
36+
{
37+
using namespace ccf;
38+
39+
auto genoa_quote_info = QuoteInfo{
40+
.format = QuoteFormat::amd_sev_snp_v1,
41+
.quote = pal::snp::testing::genoa_attestation,
42+
.endorsements = std::vector<uint8_t>(
43+
pal::snp::testing::genoa_endorsements.begin(),
44+
pal::snp::testing::genoa_endorsements.end()),
45+
.uvm_endorsements = std::nullopt,
46+
};
47+
48+
pal::PlatformAttestationMeasurement measurement;
49+
pal::PlatformAttestationReportData report_data;
50+
51+
pal::verify_snp_attestation_report(
52+
genoa_quote_info, measurement, report_data);
53+
}
54+
55+
TEST_CASE("Mismatched attestation and endorsements fail")
56+
{
57+
using namespace ccf;
58+
59+
auto mismatched_quote = QuoteInfo{
60+
.format = QuoteFormat::amd_sev_snp_v1,
61+
.quote = pal::snp::testing::milan_attestation,
62+
.endorsements = std::vector<uint8_t>(
63+
pal::snp::testing::genoa_endorsements.begin(),
64+
pal::snp::testing::genoa_endorsements.end()),
65+
.uvm_endorsements = std::nullopt,
66+
};
67+
68+
pal::PlatformAttestationMeasurement measurement;
69+
pal::PlatformAttestationReportData report_data;
70+
71+
try
72+
{
73+
pal::verify_snp_attestation_report(
74+
mismatched_quote, measurement, report_data);
75+
}
76+
catch (const std::logic_error& e)
77+
{
78+
const std::string what = e.what();
79+
CHECK(
80+
what.find("SEV-SNP: The root of trust public key for this attestation "
81+
"was not the expected one") != std::string::npos);
82+
}
83+
}
84+
85+
int main(int argc, char** argv)
86+
{
87+
ccf::crypto::openssl_sha256_init();
88+
doctest::Context context;
89+
context.applyCommandLine(argc, argv);
90+
int res = context.run();
91+
ccf::crypto::openssl_sha256_shutdown();
92+
return res;
93+
}

0 commit comments

Comments
 (0)