10#include <qcbor/qcbor.h>
11#include <qcbor/qcbor_spiffy_decode.h>
14#include <t_cose/t_cose_common.h>
20 static constexpr int64_t PARAM_ALG = 1;
21 static constexpr int64_t PARAM_CONTENT_TYPE = 3;
22 static constexpr int64_t PARAM_KID = 4;
23 static constexpr int64_t PARAM_X5CHAIN = 33;
24 static constexpr int64_t PARAM_VDP = 396;
25 static constexpr int64_t PARAM_INCLUSION_PROOFS = -1;
27 static constexpr auto CONTENT_TYPE_APPLICATION_JSON_VALUE =
33 static std::string qcbor_buf_to_string(
const UsefulBufC& buf)
35 return {
reinterpret_cast<const char*
>(buf.ptr), buf.len};
38 static std::vector<uint8_t> qcbor_buf_to_byte_vector(
const UsefulBufC& buf)
40 const auto* ptr =
static_cast<const uint8_t*
>(buf.ptr);
41 return {ptr, ptr + buf.len};
44 static bool is_ecdsa_alg(int64_t cose_alg)
46 return cose_alg == T_COSE_ALGORITHM_ES256 ||
47 cose_alg == T_COSE_ALGORITHM_ES384 || cose_alg == T_COSE_ALGORITHM_ES512;
50 static bool is_rsa_alg(int64_t cose_alg)
52 return cose_alg == T_COSE_ALGORITHM_PS256 ||
53 cose_alg == T_COSE_ALGORITHM_PS384 || cose_alg == T_COSE_ALGORITHM_PS512;
64 std::runtime_error(msg)
68 static std::string tstring_to_string(QCBORItem& item)
71 static_cast<const char*
>(item.val.string.ptr),
72 static_cast<const char*
>(item.val.string.ptr) + item.val.string.len};
90 std::vector<uint8_t>
kid;
106 std::vector<std::pair<int64_t, std::vector<uint8_t>>>
path;
115 static std::vector<uint8_t> recompute_merkle_root(
const MerkleProof& proof)
122 "Unsupported write set digest size in Merkle proof leaf: {}",
127 throw COSEDecodeError(fmt::format(
128 "Unsupported claims digest size in Merkle proof leaf: {}",
132 std::span<const uint8_t, ccf::crypto::Sha256Hash::SIZE> wsd{
134 std::span<const uint8_t, ccf::crypto::Sha256Hash::SIZE> cd{
141 for (
const auto& element : proof.path)
145 std::span<const uint8_t, ccf::crypto::Sha256Hash::SIZE> sibling{
152 std::span<const uint8_t, ccf::crypto::Sha256Hash::SIZE> sibling{
159 return {leaf_digest.h.begin(), leaf_digest.h.end()};
162 static void decode_receipt_top_level_phdr(
163 QCBORDecodeContext& ctx, CcfCoseReceiptPhdr& phdr)
174 QCBORItem header_items[END_INDEX + 1];
176 header_items[ALG_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_ALG;
177 header_items[ALG_INDEX].uLabelType = QCBOR_TYPE_INT64;
178 header_items[ALG_INDEX].uDataType = QCBOR_TYPE_INT64;
180 header_items[KID_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_ID;
181 header_items[KID_INDEX].uLabelType = QCBOR_TYPE_INT64;
182 header_items[KID_INDEX].uDataType = QCBOR_TYPE_BYTE_STRING;
184 header_items[CWT_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_CWT;
185 header_items[CWT_INDEX].uLabelType = QCBOR_TYPE_INT64;
186 header_items[CWT_INDEX].uDataType = QCBOR_TYPE_MAP;
188 header_items[VDS_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_VDS;
189 header_items[VDS_INDEX].uLabelType = QCBOR_TYPE_INT64;
190 header_items[VDS_INDEX].uDataType = QCBOR_TYPE_INT64;
192 header_items[END_INDEX].uLabelType = QCBOR_TYPE_NONE;
194 QCBORDecode_GetItemsInMap(&ctx, header_items);
196 auto qcbor_result = QCBORDecode_GetError(&ctx);
197 if (qcbor_result != QCBOR_SUCCESS)
200 "Failed to decode protected header: {}",
201 qcbor_err_to_str(qcbor_result)));
204 if (header_items[ALG_INDEX].uDataType == QCBOR_TYPE_NONE)
207 "COSE receipt missing 'alg' in protected header");
209 phdr.alg = header_items[ALG_INDEX].val.int64;
211 if (header_items[KID_INDEX].uDataType == QCBOR_TYPE_NONE)
214 "COSE receipt missing 'kid' in protected header");
217 ccf::cose::qcbor_buf_to_byte_vector(header_items[KID_INDEX].val.string);
219 if (header_items[VDS_INDEX].uDataType == QCBOR_TYPE_NONE)
222 "COSE receipt missing 'vds' in protected header");
224 phdr.vds = header_items[VDS_INDEX].val.int64;
226 if (phdr.vds != ccf::crypto::COSE_PHEADER_VDS_CCF_LEDGER_SHA256)
229 "Expected VDS={} (CCF_LEDGER_SHA256), got {}",
230 ccf::crypto::COSE_PHEADER_VDS_CCF_LEDGER_SHA256,
235 static void decode_cwt_claims(QCBORDecodeContext& ctx, CwtClaims& cwt)
237 QCBORDecode_EnterMapFromMapN(&ctx, crypto::COSE_PHEADER_KEY_CWT);
238 auto decode_error = QCBORDecode_GetError(&ctx);
239 if (decode_error != QCBOR_SUCCESS)
241 throw COSEDecodeError(
242 fmt::format(
"Failed to decode CWT claims: {}", decode_error));
253 QCBORItem cwt_items[END_CWT_INDEX + 1];
255 cwt_items[IAT_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_IAT;
256 cwt_items[IAT_INDEX].uLabelType = QCBOR_TYPE_INT64;
257 cwt_items[IAT_INDEX].uDataType = QCBOR_TYPE_INT64;
259 cwt_items[ISS_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_ISS;
260 cwt_items[ISS_INDEX].uLabelType = QCBOR_TYPE_INT64;
261 cwt_items[ISS_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
263 cwt_items[SUB_INDEX].label.int64 = ccf::crypto::COSE_PHEADER_KEY_SUB;
264 cwt_items[SUB_INDEX].uLabelType = QCBOR_TYPE_INT64;
265 cwt_items[SUB_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
267 cwt_items[END_CWT_INDEX].uLabelType = QCBOR_TYPE_NONE;
269 QCBORDecode_GetItemsInMap(&ctx, cwt_items);
270 decode_error = QCBORDecode_GetError(&ctx);
271 if (decode_error != QCBOR_SUCCESS)
273 throw COSEDecodeError(
274 fmt::format(
"Failed to decode CWT claims: {}", decode_error));
277 if (cwt_items[IAT_INDEX].uDataType == QCBOR_TYPE_NONE)
281 cwt.iat = cwt_items[IAT_INDEX].val.int64;
283 if (cwt_items[ISS_INDEX].uDataType == QCBOR_TYPE_NONE)
287 cwt.iss = tstring_to_string(cwt_items[ISS_INDEX]);
289 if (cwt_items[SUB_INDEX].uDataType == QCBOR_TYPE_NONE)
293 cwt.sub = tstring_to_string(cwt_items[SUB_INDEX]);
295 QCBORDecode_ExitMap(&ctx);
298 static void decode_ccf_claims(QCBORDecodeContext& ctx, CcfClaims&
ccf)
300 QCBORDecode_EnterMapFromMapSZ(
301 &ctx, ccf::crypto::COSE_PHEADER_KEY_CCF.c_str());
302 auto decode_error = QCBORDecode_GetError(&ctx);
303 if (decode_error != QCBOR_SUCCESS)
305 throw COSEDecodeError(
306 fmt::format(
"Failed to decode CCF claims: {}", decode_error));
315 QCBORItem ccf_items[END_CCF_INDEX + 1];
317 ccf_items[TXID_INDEX].label.string = UsefulBufC{
318 ccf::crypto::COSE_PHEADER_KEY_TXID.data(),
319 ccf::crypto::COSE_PHEADER_KEY_TXID.size()};
320 ccf_items[TXID_INDEX].uLabelType = QCBOR_TYPE_TEXT_STRING;
321 ccf_items[TXID_INDEX].uDataType = QCBOR_TYPE_TEXT_STRING;
323 ccf_items[END_CCF_INDEX].uLabelType = QCBOR_TYPE_NONE;
325 QCBORDecode_GetItemsInMap(&ctx, ccf_items);
326 decode_error = QCBORDecode_GetError(&ctx);
327 if (decode_error != QCBOR_SUCCESS)
329 throw COSEDecodeError(
330 fmt::format(
"Failed to decode CCF claims: {}", decode_error));
333 if (ccf_items[TXID_INDEX].uDataType == QCBOR_TYPE_NONE)
337 ccf.txid = tstring_to_string(ccf_items[TXID_INDEX]);
339 QCBORDecode_ExitMap(&ctx);
342 static CcfCoseReceiptPhdr decode_ccf_receipt_phdr(QCBORDecodeContext& ctx)
344 QCBORDecode_EnterBstrWrapped(
345 &ctx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
nullptr);
346 QCBORDecode_EnterMap(&ctx,
nullptr);
348 CcfCoseReceiptPhdr phdr{};
350 decode_receipt_top_level_phdr(ctx, phdr);
351 decode_cwt_claims(ctx, phdr.cwt);
352 decode_ccf_claims(ctx, phdr.ccf);
354 QCBORDecode_ExitMap(&ctx);
355 QCBORDecode_ExitBstrWrapped(&ctx);
361 static std::vector<MerkleProof> decode_merkle_proofs(QCBORDecodeContext& ctx)
363 QCBORDecode_EnterMap(&ctx,
nullptr);
364 auto err = QCBORDecode_GetError(&ctx);
365 if (err != QCBOR_SUCCESS)
367 throw COSEDecodeError(
368 fmt::format(
"Failed to enter unprotected header map: {}", err));
371 QCBORDecode_EnterMapFromMapN(&ctx, headers::PARAM_VDP);
372 err = QCBORDecode_GetError(&ctx);
373 if (err != QCBOR_SUCCESS)
375 throw COSEDecodeError(
376 fmt::format(
"Failed to enter Merkle proofs map: {}", err));
379 QCBORDecode_EnterArrayFromMapN(&ctx, headers::PARAM_INCLUSION_PROOFS);
380 err = QCBORDecode_GetError(&ctx);
381 if (err != QCBOR_SUCCESS)
383 throw COSEDecodeError(
384 fmt::format(
"Failed to enter Merkle proofs array: {}", err));
387 std::vector<uint8_t> root;
388 std::vector<MerkleProof> proofs;
391 QCBORDecode_EnterBstrWrapped(
392 &ctx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
nullptr);
393 err = QCBORDecode_GetError(&ctx);
394 if (err != QCBOR_SUCCESS)
396 err = QCBORDecode_GetAndResetError(&ctx);
397 if (err != QCBOR_ERR_NO_MORE_ITEMS)
399 throw COSEDecodeError(fmt::format(
400 "Expected NO_MORE_ITEMS after reading Merkle proofs, got {}", err));
405 QCBORDecode_EnterMap(&ctx,
nullptr);
406 err = QCBORDecode_GetError(&ctx);
407 if (err != QCBOR_SUCCESS)
409 throw COSEDecodeError(fmt::format(
"Failed to enter leaf map: {}", err));
412 QCBORDecode_EnterArrayFromMapN(
418 QCBORDecode_GetNext(&ctx, &item);
419 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
421 throw COSEDecodeError(fmt::format(
422 "Expected byte string for write_set_digest, got {}", item.uDataType));
424 proof.leaf.write_set_digest =
425 ccf::cose::qcbor_buf_to_byte_vector(item.val.string);
427 QCBORDecode_GetNext(&ctx, &item);
428 if (item.uDataType != QCBOR_TYPE_TEXT_STRING)
430 throw COSEDecodeError(fmt::format(
431 "Expected text string for commit_evidence, got {}", item.uDataType));
434 proof.leaf.commit_evidence =
435 ccf::cose::qcbor_buf_to_string(item.val.string);
437 QCBORDecode_GetNext(&ctx, &item);
438 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
440 throw COSEDecodeError(fmt::format(
441 "Expected byte string for claims_digest, got {}", item.uDataType));
444 proof.leaf.claims_digest =
445 ccf::cose::qcbor_buf_to_byte_vector(item.val.string);
447 QCBORDecode_ExitArray(&ctx);
449 QCBORDecode_EnterArrayFromMapN(
451 err = QCBORDecode_GetError(&ctx);
452 if (err != QCBOR_SUCCESS)
454 throw COSEDecodeError(
455 fmt::format(
"Failed to enter path array: {}", err));
460 QCBORDecode_EnterArray(&ctx, &item);
461 if (QCBORDecode_GetError(&ctx) != QCBOR_SUCCESS)
463 err = QCBORDecode_GetAndResetError(&ctx);
464 if (err != QCBOR_ERR_NO_MORE_ITEMS)
466 throw COSEDecodeError(fmt::format(
467 "Expected NO_MORE_ITEMS after reading path, got {}", err));
472 std::pair<int64_t, std::vector<uint8_t>> path_item;
474 err = QCBORDecode_GetNext(&ctx, &item);
475 if (err != QCBOR_SUCCESS)
477 throw COSEDecodeError(
478 fmt::format(
"Failed to get path direction item: {}", err));
481 if (item.uDataType == CBOR_SIMPLEV_TRUE)
485 else if (item.uDataType == CBOR_SIMPLEV_FALSE)
492 throw COSEDecodeError(fmt::format(
493 "Invalid path direction in Merkle proof: {}", item.uDataType));
496 err = QCBORDecode_GetNext(&ctx, &item);
497 if (err != QCBOR_SUCCESS)
499 throw COSEDecodeError(
500 fmt::format(
"Failed to get path hash item: {}", err));
502 if (item.uDataType != QCBOR_TYPE_BYTE_STRING)
504 throw COSEDecodeError(fmt::format(
505 "Expected byte string for path hash, got {}", item.uDataType));
508 path_item.second = ccf::cose::qcbor_buf_to_byte_vector(item.val.string);
509 proof.path.push_back(path_item);
511 QCBORDecode_ExitArray(&ctx);
512 err = QCBORDecode_GetError(&ctx);
513 if (err != QCBOR_SUCCESS)
515 throw COSEDecodeError(
516 fmt::format(
"Failed to exit path item array: {}", err));
520 QCBORDecode_ExitArray(&ctx);
521 err = QCBORDecode_GetError(&ctx);
522 if (err != QCBOR_SUCCESS)
524 throw COSEDecodeError(
525 fmt::format(
"Failed to exit path array: {}", err));
528 QCBORDecode_ExitMap(&ctx);
529 err = QCBORDecode_GetError(&ctx);
530 if (err != QCBOR_SUCCESS)
532 throw COSEDecodeError(fmt::format(
"Failed to exit proof map: {}", err));
535 QCBORDecode_ExitBstrWrapped(&ctx);
536 err = QCBORDecode_GetError(&ctx);
537 if (err != QCBOR_SUCCESS)
539 throw COSEDecodeError(
540 fmt::format(
"Failed to exit wrapped proof: {}", err));
543 proofs.push_back(proof);
546 QCBORDecode_ExitArray(&ctx);
547 err = QCBORDecode_GetError(&ctx);
548 if (err != QCBOR_SUCCESS)
550 throw COSEDecodeError(
551 fmt::format(
"Failed to exit proofs array: {}", err));
554 QCBORDecode_ExitMap(&ctx);
555 err = QCBORDecode_GetError(&ctx);
556 if (err != QCBOR_SUCCESS)
558 throw COSEDecodeError(fmt::format(
"Failed to exit VDP map: {}", err));
561 QCBORDecode_ExitMap(&ctx);
562 err = QCBORDecode_GetError(&ctx);
563 if (err != QCBOR_SUCCESS)
565 throw COSEDecodeError(fmt::format(
"Failed to exit uhdr map: {}", err));
571 static CcfCoseReceipt decode_ccf_receipt(
572 const std::vector<uint8_t>& cose_sign1,
bool recompute_root)
574 QCBORError qcbor_result = QCBOR_SUCCESS;
575 QCBORDecodeContext ctx;
577 QCBORDecode_Init(&ctx, buf, QCBOR_DECODE_MODE_NORMAL);
579 QCBORDecode_EnterArray(&ctx,
nullptr);
580 qcbor_result = QCBORDecode_GetError(&ctx);
581 if (qcbor_result != QCBOR_SUCCESS)
583 throw COSEDecodeError(
"Failed to parse COSE_Sign1 outer array");
586 uint64_t tag = QCBORDecode_GetNthTagOfLast(&ctx, 0);
587 if (tag != CBOR_TAG_COSE_SIGN1)
589 throw COSEDecodeError(
"COSE_Sign1 is not tagged");
592 CcfCoseReceipt receipt;
594 receipt.phdr = decode_ccf_receipt_phdr(ctx);
598 auto proofs = decode_merkle_proofs(ctx);
601 throw COSEDecodeError(
"No Merkle proofs found in COSE receipt");
604 receipt.merkle_root = recompute_merkle_root(proofs[0]);
605 for (
size_t i = 1; i < proofs.size(); ++i)
607 auto root = recompute_merkle_root(proofs[i]);
608 if (root != receipt.merkle_root)
610 throw COSEDecodeError(
611 "Inconsistent Merkle roots computed from COSE receipt proofs");
Definition sha256_hash.h:16
static Sha256Hash from_span(const std::span< const uint8_t, SIZE > &sp)
Definition sha256_hash.cpp:69
static constexpr size_t SIZE
Definition sha256_hash.h:18
Definition cose_signatures_config_interface.h:12
std::span< const uint8_t > Signature
Definition cose_common.h:31
uint64_t element
Definition sharing.cpp:20
std::vector< uint8_t > cose_sign1(const ECKeyPair_OpenSSL &key, const std::vector< std::shared_ptr< COSEParametersFactory > > &protected_headers, std::span< const uint8_t > payload, bool detached_payload)
Definition cose_sign.cpp:231
Definition app_interface.h:14
@ MERKLE_PROOF_LEAF_LABEL
Definition receipt.h:142
@ MERKLE_PROOF_PATH_LABEL
Definition receipt.h:143
Definition cose_common.h:57
COSEDecodeError(const std::string &msg)
Definition cose_common.h:58
Definition cose_common.h:62
COSESignatureValidationError(const std::string &msg)
Definition cose_common.h:63
Definition cose_common.h:83
std::string txid
Definition cose_common.h:84
Definition cose_common.h:88
CwtClaims cwt
Definition cose_common.h:91
std::vector< uint8_t > kid
Definition cose_common.h:90
int vds
Definition cose_common.h:93
int alg
Definition cose_common.h:89
Definition cose_common.h:110
std::vector< uint8_t > merkle_root
Definition cose_common.h:112
CcfCoseReceiptPhdr phdr
Definition cose_common.h:111
Definition cose_common.h:76
std::string sub
Definition cose_common.h:79
int64_t iat
Definition cose_common.h:77
std::string iss
Definition cose_common.h:78
Definition cose_common.h:97
std::vector< uint8_t > claims_digest
Definition cose_common.h:100
std::string commit_evidence
Definition cose_common.h:99
std::vector< uint8_t > write_set_digest
Definition cose_common.h:98
Definition cose_common.h:104
std::vector< std::pair< int64_t, std::vector< uint8_t > > > path
Definition cose_common.h:106
Leaf leaf
Definition cose_common.h:105