14#include <didx509cpp/didx509cpp.h>
15#include <nlohmann/json.hpp>
16#include <qcbor/qcbor.h>
17#include <qcbor/qcbor_spiffy_decode.h>
21#include <t_cose/t_cose_sign1_verify.h>
46 static std::vector<UVMEndorsements> default_uvm_roots_of_trust = {
48 {
"did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6."
49 "1.4.1.311.76.59.1.2",
50 "ContainerPlat-AMD-UVM",
53 {
"did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6."
54 "1.4.1.311.76.59.1.5",
60 const std::vector<UVMEndorsements>& uvm_roots_of_trust)
63 uvm_roots_of_trust.begin(),
64 uvm_roots_of_trust.end(),
65 [&](
const auto& uvm_root_of_trust) {
66 size_t root_of_trust_svn = 0;
69 static_assert(sizeof(size_t) == sizeof(unsigned long));
70 root_of_trust_svn = std::stoul(uvm_root_of_trust.svn);
72 catch (
const std::logic_error&)
74 throw std::runtime_error(fmt::format(
75 "Unable to parse svn value {} to unsigned in UVM root of trust",
76 uvm_root_of_trust.svn));
79 size_t endorsement_svn = 0;
82 static_assert(
sizeof(size_t) ==
sizeof(
unsigned long));
83 endorsement_svn = std::stoul(endorsements.
svn);
85 catch (
const std::logic_error&)
87 throw std::runtime_error(fmt::format(
88 "Unable to parse svn value {} to unsigned in UVM endorsements",
92 return uvm_root_of_trust.did == endorsements.
did &&
93 uvm_root_of_trust.feed == endorsements.
feed &&
94 root_of_trust_svn <= endorsement_svn;
100 static constexpr auto HEADER_PARAM_ISSUER =
"iss";
101 static constexpr auto HEADER_PARAM_FEED =
"feed";
103 static std::vector<std::vector<uint8_t>> decode_x5chain(
104 QCBORDecodeContext& ctx,
const QCBORItem& x5chain)
106 std::vector<std::vector<uint8_t>> parsed;
108 if (x5chain.uDataType == QCBOR_TYPE_ARRAY)
110 QCBORDecode_EnterArrayFromMapN(&ctx, headers::PARAM_X5CHAIN);
114 auto result = QCBORDecode_GetNext(&ctx, &item);
115 if (result == QCBOR_ERR_NO_MORE_ITEMS)
119 if (result != QCBOR_SUCCESS)
121 throw COSEDecodeError(
"Item in x5chain is not well-formed");
123 if (item.uDataType == QCBOR_TYPE_BYTE_STRING)
125 parsed.push_back(qcbor_buf_to_byte_vector(item.val.string));
129 throw COSEDecodeError(
130 "Next item in x5chain was not of type byte string");
133 QCBORDecode_ExitArray(&ctx);
136 throw COSEDecodeError(
"x5chain array length was 0 in COSE header");
139 else if (x5chain.uDataType == QCBOR_TYPE_BYTE_STRING)
141 parsed.push_back(qcbor_buf_to_byte_vector(x5chain.val.string));
145 throw COSEDecodeError(fmt::format(
146 "Value type {} of x5chain in COSE header is not array or byte string",
153 static UvmEndorsementsProtectedHeader decode_protected_header(
154 const std::vector<uint8_t>& uvm_endorsements_raw)
156 UsefulBufC msg{uvm_endorsements_raw.data(), uvm_endorsements_raw.size()};
158 QCBORError qcbor_result;
160 QCBORDecodeContext ctx;
161 QCBORDecode_Init(&ctx, msg, QCBOR_DECODE_MODE_NORMAL);
163 QCBORDecode_EnterArray(&ctx,
nullptr);
164 qcbor_result = QCBORDecode_GetError(&ctx);
165 if (qcbor_result != QCBOR_SUCCESS)
167 throw COSEDecodeError(
"Failed to parse COSE_Sign1 outer array");
170 uint64_t tag = QCBORDecode_GetNthTagOfLast(&ctx, 0);
171 if (tag != CBOR_TAG_COSE_SIGN1)
173 throw COSEDecodeError(
"Failed to parse COSE_Sign1 tag");
176 struct q_useful_buf_c protected_parameters;
177 QCBORDecode_EnterBstrWrapped(
178 &ctx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &protected_parameters);
179 QCBORDecode_EnterMap(&ctx, NULL);
190 QCBORItem header_items[END_INDEX + 1];
192 header_items[ALG_INDEX].label.int64 = headers::PARAM_ALG;
193 header_items[ALG_INDEX].uLabelType = QCBOR_TYPE_INT64;
194 header_items[ALG_INDEX].uDataType = QCBOR_TYPE_INT64;
196 header_items[CONTENT_TYPE_INDEX].label.int64 =
197 headers::PARAM_CONTENT_TYPE;
198 header_items[CONTENT_TYPE_INDEX].uLabelType = QCBOR_TYPE_INT64;
199 header_items[CONTENT_TYPE_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
201 header_items[X5_CHAIN_INDEX].label.int64 = headers::PARAM_X5CHAIN;
202 header_items[X5_CHAIN_INDEX].uLabelType = QCBOR_TYPE_INT64;
203 header_items[X5_CHAIN_INDEX].uDataType = QCBOR_TYPE_ANY;
205 header_items[ISS_INDEX].label.string =
206 UsefulBuf_FromSZ(HEADER_PARAM_ISSUER);
207 header_items[ISS_INDEX].uLabelType = QCBOR_TYPE_TEXT_STRING;
208 header_items[ISS_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
210 header_items[FEED_INDEX].label.string =
211 UsefulBuf_FromSZ(HEADER_PARAM_FEED);
212 header_items[FEED_INDEX].uLabelType = QCBOR_TYPE_TEXT_STRING;
213 header_items[FEED_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
215 header_items[END_INDEX].uLabelType = QCBOR_TYPE_NONE;
217 QCBORDecode_GetItemsInMap(&ctx, header_items);
218 qcbor_result = QCBORDecode_GetError(&ctx);
219 if (qcbor_result != QCBOR_SUCCESS)
221 throw COSEDecodeError(
"Failed to decode protected header");
224 UvmEndorsementsProtectedHeader phdr = {};
226 if (header_items[ALG_INDEX].uDataType != QCBOR_TYPE_NONE)
228 phdr.alg = header_items[ALG_INDEX].val.int64;
231 if (header_items[CONTENT_TYPE_INDEX].uDataType != QCBOR_TYPE_NONE)
234 qcbor_buf_to_string(header_items[CONTENT_TYPE_INDEX].val.string);
237 if (header_items[X5_CHAIN_INDEX].uDataType != QCBOR_TYPE_NONE)
239 phdr.x5_chain = decode_x5chain(ctx, header_items[X5_CHAIN_INDEX]);
242 if (header_items[ISS_INDEX].uDataType != QCBOR_TYPE_NONE)
244 phdr.iss = qcbor_buf_to_string(header_items[ISS_INDEX].val.string);
247 if (header_items[FEED_INDEX].uDataType != QCBOR_TYPE_NONE)
249 phdr.feed = qcbor_buf_to_string(header_items[FEED_INDEX].val.string);
252 QCBORDecode_ExitMap(&ctx);
253 QCBORDecode_ExitBstrWrapped(&ctx);
255 qcbor_result = QCBORDecode_GetError(&ctx);
256 if (qcbor_result != QCBOR_SUCCESS)
258 throw COSEDecodeError(
259 fmt::format(
"Failed to decode protected header: {}", qcbor_result));
266 static std::span<const uint8_t> verify_uvm_endorsements_signature(
268 const std::vector<uint8_t>& uvm_endorsements_raw)
272 std::span<uint8_t> payload;
273 if (!verifier->verify(uvm_endorsements_raw, payload))
275 throw cose::COSESignatureValidationError(
"Signature verification failed");
281 static UVMEndorsements verify_uvm_endorsements(
282 const std::vector<uint8_t>& uvm_endorsements_raw,
283 const pal::PlatformAttestationMeasurement& uvm_measurement,
284 const std::vector<UVMEndorsements>& uvm_roots_of_trust =
285 default_uvm_roots_of_trust)
287 auto phdr = cose::decode_protected_header(uvm_endorsements_raw);
289 if (!(cose::is_rsa_alg(phdr.alg) || cose::is_ecdsa_alg(phdr.alg)))
291 throw std::logic_error(fmt::format(
292 "Signature algorithm {} is not one of expected: RSA, ECDSA", phdr.alg));
295 std::string pem_chain;
296 for (
auto const& c : phdr.x5_chain)
301 const auto& did = phdr.iss;
303 auto did_document_str =
304 didx509::resolve(pem_chain, did,
true );
305 did::DIDDocument did_document = nlohmann::json::parse(did_document_str);
307 if (did_document.verification_method.empty())
309 throw std::logic_error(fmt::format(
310 "Could not find verification method for DID document: {}",
315 for (
auto const& vm : did_document.verification_method)
317 if (vm.controller == did && vm.public_key_jwk.has_value())
338 throw std::logic_error(fmt::format(
339 "Unsupported public key type ({}) for DID {}", jwk.kty, did));
347 throw std::logic_error(fmt::format(
348 "Could not find matching public key for DID {} in {}",
354 verify_uvm_endorsements_signature(pubk, uvm_endorsements_raw);
356 if (phdr.content_type != cose::headers::CONTENT_TYPE_APPLICATION_JSON_VALUE)
358 throw std::logic_error(fmt::format(
359 "Unexpected payload content type {}, expected {}",
361 cose::headers::CONTENT_TYPE_APPLICATION_JSON_VALUE));
364 auto payload = nlohmann::json::parse(raw_payload);
365 std::string sevsnpvm_launch_measurement =
366 payload[
"x-ms-sevsnpvm-launchmeasurement"].get<std::string>();
367 auto sevsnpvm_guest_svn_obj = payload[
"x-ms-sevsnpvm-guestsvn"];
368 std::string sevsnpvm_guest_svn;
369 if (sevsnpvm_guest_svn_obj.is_string())
371 sevsnpvm_guest_svn = sevsnpvm_guest_svn_obj.get<std::string>();
375 static_assert(
sizeof(size_t) ==
sizeof(
unsigned long));
376 uintval = std::stoul(sevsnpvm_guest_svn);
378 catch (
const std::logic_error&)
380 throw std::logic_error(fmt::format(
381 "Unable to parse sevsnpvm_guest_svn value {} to unsigned in UVM "
384 sevsnpvm_guest_svn));
387 else if (sevsnpvm_guest_svn_obj.is_number_unsigned())
389 sevsnpvm_guest_svn = std::to_string(sevsnpvm_guest_svn_obj.get<
size_t>());
393 throw std::logic_error(fmt::format(
394 "Unexpected type {} for sevsnpvm_guest_svn in UVM endorsements "
395 "payload, expected string or unsigned integer",
396 sevsnpvm_guest_svn_obj.type_name()));
399 if (sevsnpvm_launch_measurement != uvm_measurement.hex_str())
401 throw std::logic_error(fmt::format(
402 "Launch measurement in UVM endorsements payload {} is not equal "
403 "to UVM attestation measurement {}",
404 sevsnpvm_launch_measurement,
405 uvm_measurement.hex_str()));
409 "Successfully verified endorsements for attested measurement {} against "
410 "{}, feed {}, svn {}",
411 sevsnpvm_launch_measurement,
416 UVMEndorsements end{did, phdr.feed, sevsnpvm_guest_svn};
420 throw std::logic_error(fmt::format(
421 "UVM endorsements did {}, feed {}, svn {} "
422 "do not match any of the known UVM roots of trust",
bool empty() const
Definition pem.h:66
const std::string & str() const
Definition pem.h:46
#define DECLARE_JSON_REQUIRED_FIELDS(TYPE,...)
Definition json.h:712
#define DECLARE_JSON_TYPE(TYPE)
Definition json.h:661
#define LOG_INFO_FMT
Definition logger.h:395
ccf::crypto::Pem cert_der_to_pem(const std::vector< uint8_t > &der)
Definition verifier.cpp:33
COSEVerifierUniquePtr make_cose_verifier_from_key(const Pem &public_key)
Definition cose_verifier.cpp:222
PublicKeyPtr make_public_key(const Pem &pem)
Definition key_pair.cpp:20
RSAPublicKeyPtr make_rsa_public_key(const Pem &pem)
Definition rsa_key_pair.cpp:13
Definition app_interface.h:15
bool matches_uvm_roots_of_trust(const UVMEndorsements &endorsements, const std::vector< UVMEndorsements > &uvm_roots_of_trust)
Definition uvm_endorsements.h:58
std::string DID
Definition uvm_endorsements.h:20
std::string Feed
Definition uvm_endorsements.h:21
Definition uvm_endorsements.h:26
Feed feed
Definition uvm_endorsements.h:28
std::string svn
Definition uvm_endorsements.h:29
bool operator==(const UVMEndorsements &) const =default
DID did
Definition uvm_endorsements.h:27