From 937c62143bf000556a1441e948b2ba28053d43f3 Mon Sep 17 00:00:00 2001 From: Max Tropets Date: Tue, 27 Aug 2024 10:12:28 +0000 Subject: [PATCH] WIP --- src/kv/deserialise.h | 19 +++++++++++--- src/node/history.h | 18 ++++++++++++++ src/service/tables/signatures.h | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/kv/deserialise.h b/src/kv/deserialise.h index f3c4e8855cd0..55eb842edb2b 100644 --- a/src/kv/deserialise.h +++ b/src/kv/deserialise.h @@ -121,12 +121,15 @@ namespace ccf::kv auto success = ApplyResult::PASS; auto search = changes.find(ccf::Tables::SIGNATURES); + // TODO + if (search != changes.end()) { - // Transactions containing a signature must only contain the signature - // and the serialised Merkle tree and must be verified + // Transactions containing a signature must only contain the signature, + // COSE signature (optional), and the serialised Merkle tree and must be + // verified if ( - changes.size() > 2 || + changes.size() > 3 || changes.find(ccf::Tables::SERIALISED_MERKLE_TREE) == changes.end()) { LOG_FAIL_FMT("Failed to deserialise"); @@ -135,6 +138,16 @@ namespace ccf::kv return ApplyResult::FAIL; } + if ( + changes.size() == 3 && + changes.find(ccf::Tables::COSE_SIGNATURES) == changes.end()) + { + LOG_FAIL_FMT("Failed to deserialise"); + LOG_DEBUG_FMT( + "Unexpected contents in signature transaction {}", version); + return ApplyResult::FAIL; + } + if (history) { if (!history->verify(&term)) diff --git a/src/node/history.h b/src/node/history.h index 89f2fee6c816..b00fe2adc57a 100644 --- a/src/node/history.h +++ b/src/node/history.h @@ -5,7 +5,9 @@ #include "ccf/ds/logger.h" #include "ccf/pal/locking.h" #include "ccf/service/tables/nodes.h" +#include "crypto/openssl/cose_sign.h" #include "crypto/openssl/hash.h" +#include "crypto/openssl/key_pair.h" #include "ds/thread_messaging.h" #include "endian.h" #include "kv/kv_types.h" @@ -105,6 +107,7 @@ namespace ccf auto sig = store.create_reserved_tx(txid); auto signatures = sig.template wo(ccf::Tables::SIGNATURES); + // TODO auto serialised_tree = sig.template wo( ccf::Tables::SERIALISED_MERKLE_TREE); PrimarySignature sig_value(id, txid.version); @@ -336,6 +339,8 @@ namespace ccf auto sig = store.create_reserved_tx(txid); auto signatures = sig.template wo(ccf::Tables::SIGNATURES); + auto cose_signatures = + sig.template wo(ccf::Tables::COSE_SIGNATURES); auto serialised_tree = sig.template wo( ccf::Tables::SERIALISED_MERKLE_TREE); ccf::crypto::Sha256Hash root = history.get_replicated_state_root(); @@ -353,7 +358,20 @@ namespace ccf primary_sig, endorsed_cert); + const auto root_str = root.hex_str(); + std::vector root_bytes{ + (const uint8_t*)root_str.c_str(), + (const uint8_t*)root_str.c_str() + root_str.size()}; + + ccf::crypto::KeyPair_OpenSSL kp2( + kp.private_key_pem()); // why can't we expose EVP_KEY from key pair? + auto cose_sign = crypto::cose_sign1(kp2, {}, root_bytes); + + CoseSignature cose_sig_value( + id, txid.version, txid.term, root, cose_sign, endorsed_cert); + signatures->put(sig_value); + cose_signatures->put(cose_sig_value); serialised_tree->put(history.serialise_tree(txid.version - 1)); return sig.commit_reserved(); } diff --git a/src/service/tables/signatures.h b/src/service/tables/signatures.h index 665fe56cee78..ef39a7504ba0 100644 --- a/src/service/tables/signatures.h +++ b/src/service/tables/signatures.h @@ -61,9 +61,53 @@ namespace ccf using SerialisedMerkleTree = ccf::kv::RawCopySerialisedValue>; + struct CoseSignature : public NodeSignature + { + /// Sequence number of the signature transaction + ccf::SeqNo seqno = 0; + /// View of the signature transaction + ccf::View view = 0; + /// Root of the Merkle Tree as of seqno - 1 + ccf::crypto::Sha256Hash root; + /// Service-endorsed certificate of the node which produced the signature + ccf::crypto::Pem cert; + + CoseSignature() {} + + CoseSignature(const ccf::NodeId& node_, ccf::SeqNo seqno_) : + NodeSignature(node_), + seqno(seqno_) + {} + + CoseSignature(const ccf::crypto::Sha256Hash& root_) : root(root_) {} + + CoseSignature( + const ccf::NodeId& node_, + ccf::SeqNo seqno_, + ccf::View view_, + const ccf::crypto::Sha256Hash root_, + const std::vector& sig_, + const ccf::crypto::Pem& cert_) : + NodeSignature(sig_, node_, Nonce{}), + seqno(seqno_), + view(view_), + root(root_), + cert(cert_) + {} + }; + + DECLARE_JSON_TYPE_WITH_BASE_AND_OPTIONAL_FIELDS(CoseSignature, NodeSignature) + DECLARE_JSON_REQUIRED_FIELDS(CoseSignature, seqno, view, root) + DECLARE_JSON_OPTIONAL_FIELDS(CoseSignature, cert); + + // Most recent COSE signature is a single Value in the KV + using CoseSignatures = ServiceValue; + namespace Tables { static constexpr auto SIGNATURES = "public:ccf.internal.signatures"; + static constexpr auto COSE_SIGNATURES = + "public:ccf.internal.cose_signatures"; static constexpr auto SERIALISED_MERKLE_TREE = "public:ccf.internal.tree"; } } \ No newline at end of file