Add API to allow setting unprotected headers (#6586)

Co-authored-by: Max <maxtropets@gmail.com>
This commit is contained in:
Amaury Chamayou 2024-10-24 16:03:29 +01:00 коммит произвёл GitHub
Родитель da1b2adbd5
Коммит 1bf76ddd7e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 611 добавлений и 6 удалений

Просмотреть файл

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed
- Set VMPL value when creating SNP attestations, and check VMPL value is in guest range when verifiying attestation, since recent [updates allow host-initiated attestations](https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/56860.pdf) (#6583).
- Added ccf::cose::edit::set_unprotected_header() API, to allow easy injection of proofs in signatures, and of receipts in signed statements (#6586).
## [6.0.0-dev2]

Просмотреть файл

@ -864,6 +864,13 @@ if(BUILD_TESTS)
)
target_link_libraries(base64_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})
add_unit_test(
cose_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/cose.cpp
)
target_link_libraries(
cose_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} ccfcrypto.host qcbor.host
)
add_unit_test(pem_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/pem.cpp)
target_link_libraries(pem_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})

50
cddl/ccf-receipt.cddl Normal file
Просмотреть файл

@ -0,0 +1,50 @@
ccf-cose-root-signature-tagged = #6.18(ccf-cose-root-signature)
ccf-cose-root-signature = [
phdr : bstr .cbor protected-headers, ; bstr-wrapped protected headers
uhdr : unprotected-headers, ; unwrappeed (plain map) unprotected headers
payload : nil, ; signed Merkle tree root hash, *detached* payload
signature : bstr ; COSE-signature
]
unprotected-headers = {
&(vdp: 396) => verifiable-proofs
}
inclusion-proofs = [ + bstr .cbor ccf-inclusion-proof ]
verifiable-proofs = {
&(inclusion-proof: -1) => inclusion-proofs
}
protected-headers = {
&(alg: 1) => int, ; signing algoritm ID, as per RFC8152
&(kid: 4) => bstr, ; signing key hash
&(cwt: 15) => cwt-map, ; CWT claims, as per RFC8392
&(vds: 395) => int, ; verifiable data structure, as per COSE Receipts (draft) RFC (https://datatracker.ietf.org/doc/draft-ietf-cose-merkle-tree-proofs/)
"ccf.v1" => ccf-map ; a set of CCF-specific parameters
}
cwt-map = {
&(iat: 6) => int ; "issued at", number of seconds since the epoch
}
ccf-map = {
&(last-signed-txid: "txid") => tstr ; last committed transaction ID this COSE-signature signs
}
ccf-inclusion-proof = {
&(leaf: 1) => ccf-leaf
&(path: 2) => [+ ccf-proof-element]
}
ccf-leaf = [
internal-transaction-hash: bstr .size 32 ; a string of HASH_SIZE(32) bytes
internal-evidence: tstr .size (1..1024) ; a string of at most 1024 bytes
data-hash: bstr .size 32 ; a string of HASH_SIZE(32) bytes
]
ccf-proof-element = [
left: bool ; position of the element
hash: bstr .size 32 ; hash of the proof element (string of HASH_SIZE(32) bytes)
]

Просмотреть файл

@ -15,6 +15,7 @@ set(CCFCRYPTO_SRC
${CCF_DIR}/src/crypto/hmac.cpp
${CCF_DIR}/src/crypto/pem.cpp
${CCF_DIR}/src/crypto/ecdsa.cpp
${CCF_DIR}/src/crypto/cose.cpp
${CCF_DIR}/src/crypto/openssl/symmetric_key.cpp
${CCF_DIR}/src/crypto/openssl/public_key.cpp
${CCF_DIR}/src/crypto/openssl/key_pair.cpp

Просмотреть файл

@ -158,3 +158,9 @@ HTTP Entity Tags Matching
.. doxygenclass:: ccf::http::Matcher
:project: CCF
:members:
COSE
----
.. doxygenfunction:: ccf::cose::edit::set_unprotected_header
:project: CCF

Просмотреть файл

@ -295,7 +295,7 @@
"info": {
"description": "This CCF sample app implements a simple logging application, securely recording messages at client-specified IDs. It demonstrates most of the features available to CCF apps.",
"title": "CCF Sample Logging App",
"version": "2.4.3"
"version": "2.5.0"
},
"openapi": "3.0.0",
"paths": {
@ -1273,6 +1273,30 @@
}
}
},
"/app/log/public/cose_receipt": {
"get": {
"operationId": "GetAppLogPublicCoseReceipt",
"responses": {
"204": {
"description": "Default response description"
},
"default": {
"$ref": "#/components/responses/default"
}
},
"security": [
{
"jwt": []
},
{
"user_cose_sign1": []
}
],
"x-ccf-forwarding": {
"$ref": "#/components/x-ccf-forwarding/never"
}
}
},
"/app/log/public/cose_signature": {
"get": {
"operationId": "GetAppLogPublicCoseSignature",

47
include/ccf/crypto/cose.h Normal file
Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include <cstdint>
#include <span>
#include <variant>
#include <vector>
namespace ccf::cose::edit
{
namespace pos
{
struct InArray
{};
struct AtKey
{
int64_t key;
};
using Type = std::variant<InArray, AtKey>;
}
/**
* Set the unprotected header of a COSE_Sign1 message, to a map containing
* @p key and depending on the value of @p position, either an array
* containing
* @p value, or a map with key @p subkey and value @p value.
*
* Useful to add a proof to a signature to turn it into a receipt, or to
* add a receipt to a signed statement to turn it into a transparent
* statement.
*
* @param cose_input The COSE_Sign1 message to edit.
* @param key The key at which to insert either an array or a map.
* @param position Either InArray or AtKey, to determine whether to insert an
* array or a map.
*
* @return The COSE_Sign1 message with the new unprotected header.
*/
std::vector<uint8_t> set_unprotected_header(
const std::span<const uint8_t>& cose_input,
int64_t key,
pos::Type position,
const std::vector<uint8_t> value);
}

Просмотреть файл

@ -7,6 +7,7 @@
// CCF
#include "ccf/app_interface.h"
#include "ccf/common_auth_policies.h"
#include "ccf/crypto/cose.h"
#include "ccf/crypto/verifier.h"
#include "ccf/ds/hash.h"
#include "ccf/endpoints/authentication/all_of_auth.h"
@ -458,7 +459,7 @@ namespace loggingapp
"recording messages at client-specified IDs. It demonstrates most of "
"the features available to CCF apps.";
openapi_info.document_version = "2.4.3";
openapi_info.document_version = "2.5.0";
index_per_public_key = std::make_shared<RecordsIndexingStrategy>(
PUBLIC_RECORDS, context, 10000, 20);
@ -2038,6 +2039,53 @@ namespace loggingapp
.set_auto_schema<void, LoggingGetCoseSignature::Out>()
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
.install();
auto get_cose_receipt = [this](
ccf::endpoints::ReadOnlyEndpointContext& ctx,
ccf::historical::StatePtr historical_state) {
auto historical_tx = historical_state->store->create_read_only_tx();
assert(historical_state->receipt);
auto signature = describe_cose_signature_v1(*historical_state->receipt);
if (!signature.has_value())
{
ctx.rpc_ctx->set_error(
HTTP_STATUS_NOT_FOUND,
ccf::errors::ResourceNotFound,
"No COSE signature available for this transaction");
return;
}
auto proof = describe_merkle_proof_v1(*historical_state->receipt);
if (!proof.has_value())
{
ctx.rpc_ctx->set_error(
HTTP_STATUS_NOT_FOUND,
ccf::errors::ResourceNotFound,
"No merkle proof available for this transaction");
return;
}
size_t vdp = 396;
auto inclusion_proof = ccf::cose::edit::pos::AtKey{-1};
auto cose_receipt = ccf::cose::edit::set_unprotected_header(
*signature, vdp, inclusion_proof, *proof);
ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
ctx.rpc_ctx->set_response_header(
ccf::http::headers::CONTENT_TYPE,
ccf::http::headervalues::contenttype::COSE);
ctx.rpc_ctx->set_response_body(cose_receipt);
};
make_read_only_endpoint(
"/log/public/cose_receipt",
HTTP_GET,
ccf::historical::read_only_adapter_v4(
get_cose_receipt, context, is_tx_committed),
auth_policies)
.set_auto_schema<void, void>()
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
.install();
}
};
}

146
src/crypto/cose.cpp Normal file
Просмотреть файл

@ -0,0 +1,146 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "ccf/crypto/cose.h"
#include <optional>
#include <qcbor/qcbor_decode.h>
#include <qcbor/qcbor_encode.h>
#include <qcbor/qcbor_spiffy_decode.h>
#include <stdexcept>
namespace ccf::cose::edit
{
std::vector<uint8_t> set_unprotected_header(
const std::span<const uint8_t>& cose_input,
int64_t key,
pos::Type pos,
const std::vector<uint8_t> value)
{
UsefulBufC buf{cose_input.data(), cose_input.size()};
QCBORError err;
QCBORDecodeContext ctx;
QCBORDecode_Init(&ctx, buf, QCBOR_DECODE_MODE_NORMAL);
size_t pos_start = 0;
size_t pos_end = 0;
QCBORDecode_EnterArray(&ctx, nullptr);
err = QCBORDecode_GetError(&ctx);
if (err != QCBOR_SUCCESS)
{
throw std::logic_error("Failed to parse COSE_Sign1 outer array");
}
auto tag = QCBORDecode_GetNthTagOfLast(&ctx, 0);
if (tag != CBOR_TAG_COSE_SIGN1)
{
throw std::logic_error("Failed to parse COSE_Sign1 tag");
}
QCBORItem item;
err = QCBORDecode_GetNext(&ctx, &item);
if (err != QCBOR_SUCCESS || item.uDataType != QCBOR_TYPE_BYTE_STRING)
{
throw std::logic_error(
"Failed to parse COSE_Sign1 protected header as bstr");
}
UsefulBufC phdr = {item.val.string.ptr, item.val.string.len};
// Skip unprotected header
QCBORDecode_VGetNextConsume(&ctx, &item);
err = QCBORDecode_PartialFinish(&ctx, &pos_start);
if (err != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED)
{
throw std::logic_error("Failed to find start of payload");
}
QCBORDecode_VGetNextConsume(&ctx, &item);
err = QCBORDecode_PartialFinish(&ctx, &pos_end);
if (err != QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED)
{
throw std::logic_error("Failed to find end of payload");
}
UsefulBufC payload = {cose_input.data() + pos_start, pos_end - pos_start};
// QCBORDecode_PartialFinish() before and after should allow constructing a
// span of the encoded payload, which can perhaps then be passed to
// QCBOREncode_AddEncoded and would allow blindly copying the payload
// without parsing it.
err = QCBORDecode_GetNext(&ctx, &item);
if (err != QCBOR_SUCCESS && item.uDataType != QCBOR_TYPE_BYTE_STRING)
{
throw std::logic_error("Failed to parse COSE_Sign1 signature");
}
UsefulBufC signature = {item.val.string.ptr, item.val.string.len};
QCBORDecode_ExitArray(&ctx);
err = QCBORDecode_Finish(&ctx);
if (err != QCBOR_SUCCESS)
{
throw std::logic_error("Failed to parse COSE_Sign1");
}
// Maximum expected size of the additional map, sub-map is the
// worst-case scenario
const size_t additional_map_size = QCBOR_HEAD_BUFFER_SIZE + // map
QCBOR_HEAD_BUFFER_SIZE + // key
sizeof(key) + // key
QCBOR_HEAD_BUFFER_SIZE + // submap
QCBOR_HEAD_BUFFER_SIZE + // subkey
sizeof(pos::AtKey::key) + // subkey
QCBOR_HEAD_BUFFER_SIZE + // value
value.size(); // value
// We add one extra QCBOR_HEAD_BUFFER_SIZE, because we parse and re-encode
// the protected header bstr, which involves variable integer encoding, just
// in case the library does not pick the most compact encoding.
std::vector<uint8_t> output(
cose_input.size() + additional_map_size + QCBOR_HEAD_BUFFER_SIZE);
UsefulBuf output_buf{output.data(), output.size()};
QCBOREncodeContext ectx;
QCBOREncode_Init(&ectx, output_buf);
QCBOREncode_AddTag(&ectx, CBOR_TAG_COSE_SIGN1);
QCBOREncode_OpenArray(&ectx);
QCBOREncode_AddBytes(&ectx, phdr);
QCBOREncode_OpenMap(&ectx);
if (std::holds_alternative<pos::InArray>(pos))
{
QCBOREncode_OpenArrayInMapN(&ectx, key);
QCBOREncode_AddBytes(&ectx, {value.data(), value.size()});
QCBOREncode_CloseArray(&ectx);
}
else if (std::holds_alternative<pos::AtKey>(pos))
{
QCBOREncode_OpenMapInMapN(&ectx, key);
auto subkey = std::get<pos::AtKey>(pos).key;
QCBOREncode_OpenArrayInMapN(&ectx, subkey);
QCBOREncode_AddBytes(&ectx, {value.data(), value.size()});
QCBOREncode_CloseArray(&ectx);
QCBOREncode_CloseMap(&ectx);
}
else
{
throw std::logic_error("Invalid COSE_Sign1 edit operation");
}
QCBOREncode_CloseMap(&ectx);
QCBOREncode_AddEncoded(&ectx, payload);
QCBOREncode_AddBytes(&ectx, signature);
QCBOREncode_CloseArray(&ectx);
UsefulBufC cose_output;
err = QCBOREncode_Finish(&ectx, &cose_output);
if (err != QCBOR_SUCCESS)
{
throw std::logic_error("Failed to encode COSE_Sign1");
}
output.resize(cose_output.len);
output.shrink_to_fit();
return output;
};
}

219
src/crypto/test/cose.cpp Normal file
Просмотреть файл

@ -0,0 +1,219 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "ccf/crypto/cose.h"
#include "crypto/openssl/cose_sign.h"
#include "crypto/openssl/cose_verifier.h"
#include <cstdint>
#include <doctest/doctest.h>
#include <fstream>
#include <limits>
#include <qcbor/qcbor_decode.h>
#include <qcbor/qcbor_spiffy_decode.h>
#include <string>
#include <vector>
static const std::vector<int64_t> keys = {
42, std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()};
static const std::vector<ccf::cose::edit::pos::Type> positions = {
ccf::cose::edit::pos::AtKey{42},
ccf::cose::edit::pos::AtKey{std::numeric_limits<int64_t>::min()},
ccf::cose::edit::pos::AtKey{std::numeric_limits<int64_t>::max()},
ccf::cose::edit::pos::InArray{}};
const std::vector<uint8_t> value = {1, 2, 3, 4};
enum class PayloadType
{
Detached,
Flat,
NestedCBOR // Useful to test the payload transfer
};
struct Signer
{
ccf::crypto::KeyPair_OpenSSL kp;
std::vector<uint8_t> payload;
bool detached_payload = false;
Signer(PayloadType type) : kp(ccf::crypto::CurveID::SECP384R1)
{
switch (type)
{
case PayloadType::Detached:
detached_payload = true;
payload = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
break;
case PayloadType::Flat:
payload = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
break;
case PayloadType::NestedCBOR:
{
payload.resize(1024);
QCBOREncodeContext ctx;
QCBOREncode_Init(&ctx, {payload.data(), payload.size()});
QCBOREncode_OpenArray(&ctx);
QCBOREncode_AddInt64(&ctx, 1);
QCBOREncode_OpenArray(&ctx);
QCBOREncode_AddInt64(&ctx, 2);
QCBOREncode_AddInt64(&ctx, 3);
QCBOREncode_CloseArray(&ctx);
QCBOREncode_CloseArray(&ctx);
UsefulBufC result;
QCBOREncode_Finish(&ctx, &result);
payload.resize(result.len);
payload.shrink_to_fit();
}
break;
}
}
std::vector<uint8_t> make_cose_sign1()
{
const auto pheaders = {
ccf::crypto::cose_params_int_bytes(300, value),
ccf::crypto::cose_params_int_int(301, 34)};
return ccf::crypto::cose_sign1(kp, pheaders, payload, detached_payload);
};
void verify(const std::vector<uint8_t>& cose_sign1)
{
auto verifier =
ccf::crypto::make_cose_verifier_from_key(kp.public_key_pem());
if (detached_payload)
{
verifier->verify_detached(cose_sign1, payload);
}
else
{
std::span<uint8_t> payload_;
REQUIRE(verifier->verify(cose_sign1, payload_));
std::vector<uint8_t> payload_copy(payload_.begin(), payload_.end());
REQUIRE(payload == payload_copy);
}
};
};
TEST_CASE("Verification and payload invariant")
{
for (auto type :
{PayloadType::Detached, PayloadType::Flat, PayloadType::NestedCBOR})
{
Signer signer(type);
auto csp = signer.make_cose_sign1();
signer.verify(csp);
for (const auto& key : keys)
{
for (const auto& position : positions)
{
auto csp_set =
ccf::cose::edit::set_unprotected_header(csp, key, position, value);
signer.verify(csp_set);
}
}
}
}
TEST_CASE("Idempotence")
{
for (auto type :
{PayloadType::Detached, PayloadType::Flat, PayloadType::NestedCBOR})
{
Signer signer(type);
auto csp = signer.make_cose_sign1();
for (const auto& key : keys)
{
for (const auto& position : positions)
{
auto csp_set_once =
ccf::cose::edit::set_unprotected_header(csp, key, position, value);
auto csp_set_twice = ccf::cose::edit::set_unprotected_header(
csp_set_once, key, position, value);
REQUIRE(csp_set_once == csp_set_twice);
}
}
}
}
TEST_CASE("Check unprotected header")
{
for (auto type :
{PayloadType::Detached, PayloadType::Flat, PayloadType::NestedCBOR})
{
Signer signer(type);
auto csp = signer.make_cose_sign1();
for (const auto& key : keys)
{
for (const auto& position : positions)
{
auto csp_set =
ccf::cose::edit::set_unprotected_header(csp, key, position, value);
std::vector<uint8_t> ref(1024);
{
// Create expected reference value for the unprotected header
UsefulBuf ref_buf{ref.data(), ref.size()};
QCBOREncodeContext ctx;
QCBOREncode_Init(&ctx, ref_buf);
QCBOREncode_OpenMap(&ctx);
if (std::holds_alternative<ccf::cose::edit::pos::InArray>(position))
{
QCBOREncode_OpenArrayInMapN(&ctx, key);
QCBOREncode_AddBytes(&ctx, {value.data(), value.size()});
QCBOREncode_CloseArray(&ctx);
}
else if (std::holds_alternative<ccf::cose::edit::pos::AtKey>(
position))
{
QCBOREncode_OpenMapInMapN(&ctx, key);
auto subkey = std::get<ccf::cose::edit::pos::AtKey>(position).key;
QCBOREncode_OpenArrayInMapN(&ctx, subkey);
QCBOREncode_AddBytes(&ctx, {value.data(), value.size()});
QCBOREncode_CloseArray(&ctx);
QCBOREncode_CloseMap(&ctx);
}
QCBOREncode_CloseMap(&ctx);
UsefulBufC ref_buf_c;
QCBOREncode_Finish(&ctx, &ref_buf_c);
ref.resize(ref_buf_c.len);
ref.shrink_to_fit();
}
size_t uhdr_start, uhdr_end;
QCBORError err;
QCBORItem item;
QCBORDecodeContext ctx;
UsefulBufC buf{csp_set.data(), csp_set.size()};
QCBORDecode_Init(&ctx, buf, QCBOR_DECODE_MODE_NORMAL);
QCBORDecode_EnterArray(&ctx, nullptr);
QCBORDecode_GetNthTagOfLast(&ctx, 0);
// Protected header
QCBORDecode_VGetNextConsume(&ctx, &item);
// Unprotected header
QCBORDecode_PartialFinish(&ctx, &uhdr_start);
QCBORDecode_VGetNextConsume(&ctx, &item);
QCBORDecode_PartialFinish(&ctx, &uhdr_end);
std::vector<uint8_t> uhdr{
csp_set.data() + uhdr_start, csp_set.data() + uhdr_end};
REQUIRE(uhdr == ref);
// Payload
QCBORDecode_VGetNextConsume(&ctx, &item);
// Signature
QCBORDecode_VGetNextConsume(&ctx, &item);
QCBORDecode_ExitArray(&ctx);
err = QCBORDecode_Finish(&ctx);
REQUIRE(err == QCBOR_SUCCESS);
}
}
}
}

Просмотреть файл

@ -202,9 +202,10 @@ namespace ccf
std::optional<std::vector<uint8_t>> describe_merkle_proof_v1(
const TxReceiptImpl& receipt)
{
constexpr size_t buf_size = 2048;
constexpr size_t buf_size = 2048; // TBD: calculate why this is enough
std::vector<uint8_t> underlying_buffer(buf_size);
q_useful_buf buffer{underlying_buffer.data(), buf_size};
UsefulBuf buffer{underlying_buffer.data(), underlying_buffer.size()};
assert(buffer.len == buf_size);
QCBOREncodeContext ctx;
QCBOREncode_Init(&ctx, buffer);
@ -232,7 +233,7 @@ namespace ccf
QCBOREncode_CloseMap(&ctx);
struct q_useful_buf_c result;
UsefulBufC result;
auto qerr = QCBOREncode_Finish(&ctx, &result);
if (qerr)
{

Просмотреть файл

@ -1978,7 +1978,7 @@ TEST_CASE("Valid merkle proof from receipts")
REQUIRE_EQ(
ccf::ds::to_hex(decoded.claims_digest),
historical_state->receipt->claims_digest.value()
.hex_str()); // HEX as workaround emmpy claims (set flag).
.hex_str()); // HEX as workaround empty claims (set flag).
auto it = decoded.path.begin();
for (const auto& node : *historical_state->receipt->path)

Просмотреть файл

@ -1011,6 +1011,58 @@ def test_cose_signature_schema(network, args):
return network
@reqs.description("Check COSE receipt CDDL schema")
def test_cose_receipt_schema(network, args):
primary, _ = network.find_nodes()
with primary.client("user0") as client:
r = client.get("/commit")
assert r.status_code == http.HTTPStatus.OK
last_txid = TxID.from_str(r.body.json()["transaction_id"])
for seqno in range(last_txid.seqno, last_txid.seqno - 10, -1):
txid = f"{last_txid.view}.{seqno}"
LOG.debug(f"Trying to get COSE receipt for txid {txid}")
max_retries = 10
found_proof = False
for _ in range(max_retries):
r = client.get(
"/log/public/cose_receipt",
headers={infra.clients.CCF_TX_ID_HEADER: txid},
log_capture=[], # Do not emit raw binary to stdout
)
if r.status_code == http.HTTPStatus.OK:
cbor_proof = r.body.data()
cbor_proof_filename = os.path.join(
network.common_dir, f"receipt_{txid}.cose"
)
with open(cbor_proof_filename, "wb") as f:
f.write(cbor_proof)
subprocess.run(
["cddl", "../cddl/ccf-receipt.cddl", "v", cbor_proof_filename],
check=True,
)
found_proof = True
LOG.debug(f"Checked COSE receipt for txid {txid}")
break
elif r.status_code == http.HTTPStatus.ACCEPTED:
LOG.debug(f"Transaction {txid} accepted, retrying")
time.sleep(0.1)
elif r.status_code == http.HTTPStatus.NOT_FOUND:
LOG.debug(f"Transaction {txid} is a signature")
break
else:
assert (
False
), f"Failed to get receipt for txid {txid} after {max_retries} retries"
if found_proof:
break
else:
assert False, "Failed to find a non-signature in the last 10 transactions"
return network
@reqs.description("Read range of historical state")
@reqs.supports_methods("/app/log/public", "/app/log/public/historical/range")
def test_historical_query_range(network, args):
@ -2194,6 +2246,7 @@ def run_main_tests(network, args):
if args.package == "samples/apps/logging/liblogging":
test_cbor_merkle_proof(network, args)
test_cose_signature_schema(network, args)
test_cose_receipt_schema(network, args)
# HTTP2 doesn't support forwarding
if not args.http2:

Просмотреть файл

@ -19,6 +19,7 @@ set(CCFCRYPTO_SRC
${CCF_DIR}/src/crypto/key_wrap.cpp
${CCF_DIR}/src/crypto/hmac.cpp
${CCF_DIR}/src/crypto/pem.cpp
${CCF_DIR}/src/crypto/cose.cpp
${CCF_DIR}/src/crypto/openssl/symmetric_key.cpp
${CCF_DIR}/src/crypto/openssl/public_key.cpp
${CCF_DIR}/src/crypto/openssl/key_pair.cpp
@ -32,6 +33,7 @@ set(CCFCRYPTO_SRC
add_library(stdcxxccfcrypto.host STATIC "${CCFCRYPTO_SRC}")
target_link_libraries(stdcxxccfcrypto.host PUBLIC crypto)
target_link_libraries(stdcxxccfcrypto.host PUBLIC ssl)
target_link_libraries(stdcxxccfcrypto.host PUBLIC qcbor.host)
target_link_libraries(
submit PRIVATE stdcxxhttp_parser.host stdcxxccfcrypto.host arrow parquet