CCF
Loading...
Searching...
No Matches
attestation.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/crypto/ecdsa.h"
6#include "ccf/crypto/pem.h"
8#include "ccf/ds/hex.h"
9#include "ccf/ds/logger.h"
10#include "ccf/ds/quote_info.h"
12#include "ccf/pal/measurement.h"
13#include "ccf/pal/snp_ioctl.h"
14
15#include <fcntl.h>
16#include <functional>
17#include <sys/ioctl.h>
18
19namespace ccf::pal
20{
21 // Caller-supplied callback used to retrieve endorsements as specified by
22 // the config argument. When called back, the quote_info argument will have
23 // already been populated with the raw quote.
24 using RetrieveEndorsementCallback = std::function<void(
25 const QuoteInfo& quote_info,
27
28 static void verify_virtual_attestation_report(
29 const QuoteInfo& quote_info,
32 {
33 auto j = nlohmann::json::parse(quote_info.quote);
34
35 const auto s_measurement = j["measurement"].get<std::string>();
36 measurement.data =
37 std::vector<uint8_t>(s_measurement.begin(), s_measurement.end());
38 report_data = VirtualAttestationReportData(
39 j["report_data"].get<std::vector<uint8_t>>());
40 }
41
42 // Verifying SNP attestation report is available on all platforms.
43 static void verify_snp_attestation_report(
44 const QuoteInfo& quote_info,
45 PlatformAttestationMeasurement& measurement,
46 PlatformAttestationReportData& report_data)
47 {
48 if (quote_info.format != QuoteFormat::amd_sev_snp_v1)
49 {
50 throw std::logic_error(fmt::format(
51 "Unexpected attestation report to verify for SEV-SNP: {}",
52 quote_info.format));
53 }
54
55 if (quote_info.quote.size() != sizeof(snp::Attestation))
56 {
57 throw std::logic_error(fmt::format(
58 "Input SEV-SNP attestation report is not of expected size {}: {}",
59 sizeof(snp::Attestation),
60 quote_info.quote.size()));
61 }
62
63 auto quote =
64 *reinterpret_cast<const snp::Attestation*>(quote_info.quote.data());
65
66 if (quote.version < snp::minimum_attestation_version)
67 {
68 throw std::logic_error(fmt::format(
69 "SEV-SNP: Attestation version is {} not >= expected minimum {}",
70 quote.version,
71 snp::minimum_attestation_version));
72 }
73
74 if (quote.flags.signing_key != snp::attestation_flags_signing_key_vcek)
75 {
76 throw std::logic_error(fmt::format(
77 "SEV-SNP: Attestation report must be signed by VCEK: {}",
78 static_cast<uint8_t>(quote.flags.signing_key)));
79 }
80
81 if (quote.flags.mask_chip_key != 0)
82 {
83 throw std::logic_error(
84 fmt::format("SEV-SNP: Mask chip key must not be set"));
85 }
86
87 // Introduced in
88 // https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/56860.pdf
89 // The guest sets the VMPL field to a value from 0 thru 3 which indicates a
90 // request from the guest. For a Guest requested attestation report this
91 // field will contain the value (0-3). A Host requested attestation report
92 // will have a value of 0xffffffff. CCF current always sets VMPL to 0, and
93 // rejects non-guest values.
94 if (quote.vmpl > 3)
95 {
96 throw std::logic_error(fmt::format(
97 "SEV-SNP: VMPL for guest attestations must be in 0-3 range, not {}",
98 quote.vmpl));
99 }
100
101 report_data = SnpAttestationReportData(quote.report_data);
102 measurement = SnpAttestationMeasurement(quote.measurement);
103
104 auto certificates = ccf::crypto::split_x509_cert_bundle(std::string_view(
105 reinterpret_cast<const char*>(quote_info.endorsements.data()),
106 quote_info.endorsements.size()));
107 if (certificates.size() != 3)
108 {
109 throw std::logic_error(fmt::format(
110 "Expected 3 endorsement certificates but got {}", certificates.size()));
111 }
112
113 // chip_cert (VCEK) <-signs- sev_version (ASK)
114 // ASK <-signs- root_certificate (ARK)
115 auto chip_certificate = certificates[0];
116 auto sev_version_certificate = certificates[1];
117 auto root_certificate = certificates[2];
118
119 auto root_cert_verifier = ccf::crypto::make_verifier(root_certificate);
120
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 {
131 snp::get_sev_snp_product(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)
142 {
143 throw std::logic_error(fmt::format(
144 "SEV-SNP: The root of trust public key for this attestation was not "
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));
151 }
152
153 if (!root_cert_verifier->verify_certificate({&root_certificate}))
154 {
155 throw std::logic_error(
156 "SEV-SNP: The root of trust public key for this attestation was not "
157 "self signed as expected");
158 }
159
160 auto chip_cert_verifier = ccf::crypto::make_verifier(chip_certificate);
161 if (!chip_cert_verifier->verify_certificate(
162 {&root_certificate}, {&sev_version_certificate}))
163 {
164 throw std::logic_error(
165 "SEV-SNP: The chain of signatures from the root of trust to this "
166 "attestation is broken");
167 }
168
169 // According to Table 134 (2025-06-12) only ecdsa_p384_sha384 is supported
170 if (quote.signature_algo != snp::SignatureAlgorithm::ecdsa_p384_sha384)
171 {
172 throw std::logic_error(fmt::format(
173 "SEV-SNP: Unsupported signature algorithm: {} (supported: {})",
174 quote.signature_algo,
176 }
177
178 // Make ASN1 DER signature
179 auto quote_signature = ccf::crypto::ecdsa_sig_from_r_s(
180 quote.signature.r,
181 sizeof(quote.signature.r),
182 quote.signature.s,
183 sizeof(quote.signature.s),
184 false /* little endian */
185 );
186
187 std::span quote_without_signature{
188 quote_info.quote.data(),
189 quote_info.quote.size() - sizeof(quote.signature)};
190 if (!chip_cert_verifier->verify(quote_without_signature, quote_signature))
191 {
192 throw std::logic_error(
193 "SEV-SNP: Chip certificate (VCEK) did not sign this attestation");
194 }
195
196 // We should check this (although not security critical) but the guest
197 // policy ABI is currently set to 0.31, although we are targeting 1.54
198 // if (quote.policy.abi_major < snp::attestation_policy_abi_major)
199 // {
200 // throw std::logic_error(fmt::format(
201 // "SEV-SNP: Attestation guest policy ABI major {} must be greater than
202 // " "or equal to {}", quote.policy.abi_major,
203 // snp::attestation_policy_abi_major));
204 // }
205
206 if (quote.policy.debug != 0)
207 {
208 throw std::logic_error(
209 "SEV-SNP: SNP attestation report guest policy debugging must not be "
210 "enabled");
211 }
212
213 if (quote.policy.migrate_ma != 0)
214 {
215 throw std::logic_error("SEV-SNP: Migration agents must not be enabled");
216 }
217
218 // Only has value when endorsements are retrieved from environment
219 if (quote_info.endorsed_tcb.has_value())
220 {
221 const auto& endorsed_tcb = quote_info.endorsed_tcb.value();
222 auto raw_tcb = ds::from_hex(quote_info.endorsed_tcb.value());
223
224 if (raw_tcb.size() != sizeof(snp::TcbVersionRaw))
225 {
226 throw std::logic_error(fmt::format(
227 "SEV-SNP: TCB of size {}, expected {}",
228 raw_tcb.size(),
229 sizeof(snp::TcbVersionRaw)));
230 }
231
232 if (
233 memcmp(
234 raw_tcb.data(), &quote.reported_tcb, sizeof(snp::TcbVersionRaw)) != 0)
235 {
236 auto* reported_tcb = reinterpret_cast<uint8_t*>(&quote.reported_tcb);
237 throw std::logic_error(fmt::format(
238 "SEV-SNP: endorsed TCB {} does not match reported TCB {}",
239 endorsed_tcb,
240 ds::to_hex(
241 {reported_tcb, reported_tcb + sizeof(quote.reported_tcb)})));
242 }
243 }
244 }
245
246 static void verify_quote(
247 const QuoteInfo& quote_info,
248 PlatformAttestationMeasurement& measurement,
249 PlatformAttestationReportData& report_data)
250 {
251 if (quote_info.format == QuoteFormat::insecure_virtual)
252 {
253 verify_virtual_attestation_report(quote_info, measurement, report_data);
254 }
255 else if (quote_info.format == QuoteFormat::amd_sev_snp_v1)
256 {
257 verify_snp_attestation_report(quote_info, measurement, report_data);
258 }
259 else
260 {
261 throw std::logic_error(
262 "SGX attestation reports are no longer supported from 6.0.0 onwards");
263 }
264 }
265
266 class AttestationCollateralFetchingTimeout : public std::exception
267 {
268 private:
269 std::string msg;
270
271 public:
272 AttestationCollateralFetchingTimeout(const std::string& msg_) : msg(msg_) {}
273
274 virtual const char* what() const throw()
275 {
276 return msg.c_str();
277 }
278 };
279}
virtual const char * what() const
Definition attestation.h:274
AttestationCollateralFetchingTimeout(const std::string &msg_)
Definition attestation.h:272
std::vector< ccf::crypto::Pem > split_x509_cert_bundle(const std::string_view &pem)
Definition pem.cpp:37
VerifierPtr make_verifier(const std::vector< uint8_t > &cert)
Definition verifier.cpp:18
std::vector< uint8_t > ecdsa_sig_from_r_s(const uint8_t *r, size_t r_size, const uint8_t *s, size_t s_size, bool big_endian=true)
Definition ecdsa.cpp:14
uint8_t * key
Definition kv_helpers.h:78
constexpr auto amd_milan_root_signing_public_key
Definition attestation_sev_snp.h:31
ProductName get_sev_snp_product(AMDFamily family, AMDModel model)
Definition sev_snp_cpuid.h:118
const std::map< ProductName, const char * > amd_root_signing_keys
Definition attestation_sev_snp.h:80
Definition attestation.h:20
std::function< void(const QuoteInfo &quote_info, const snp::EndorsementEndpointsConfiguration &config)> RetrieveEndorsementCallback
Definition attestation.h:26
AttestationReportData< virtual_attestation_report_data_size > VirtualAttestationReportData
Definition report_data.h:42
AttestationMeasurement< snp_attestation_measurement_size > SnpAttestationMeasurement
Definition measurement.h:111
AttestationReportData< snp_attestation_report_data_size > SnpAttestationReportData
Definition report_data.h:52
Describes a quote (attestation) from trusted hardware.
Definition quote_info.h:26
QuoteFormat format
Quote format.
Definition quote_info.h:28
std::optional< std::string > endorsed_tcb
Endorsed TCB (hex-encoded) (SNP-only)
Definition quote_info.h:36
std::vector< uint8_t > quote
Enclave quote.
Definition quote_info.h:30
std::vector< uint8_t > endorsements
Quote endorsements.
Definition quote_info.h:32
Definition measurement.h:120
std::vector< uint8_t > data
Definition measurement.h:121
Definition report_data.h:56
Definition attestation_sev_snp_endorsements.h:39