22// Licensed under the Apache 2.0 License.
33#pragma once
44
5+ #include " ccf/crypto/cose.h"
6+ #include " ccf/crypto/cose_verifier.h"
57#include " ccf/historical_queries_adapter.h"
68#include " ccf/service/tables/nodes.h"
9+ #include " crypto/cose.h"
710#include " ds/internal_logger.h"
811#include " ds/serialized.h"
912#include " kv/kv_types.h"
1013#include " kv/serialised_entry_format.h"
14+ #include " node/cose_common.h"
1115#include " node/history.h"
1216#include " node/tx_receipt_impl.h"
1317
@@ -61,29 +65,47 @@ namespace ccf
6165 return SnapshotSegments{header_and_body, receipt};
6266 }
6367
64- static void verify_snapshot (
68+ static void verify_cose_snapshot_receipt (
6569 const SnapshotSegments& segments,
66- std::optional<std::vector<uint8_t >> prev_service_identity = std:: nullopt )
70+ const std::optional<std::vector<uint8_t >>& prev_service_identity)
6771 {
68- LOG_INFO_FMT (
69- " Deserialising snapshot receipt (size: {})." , segments.receipt .size ());
70- constexpr size_t max_printed_size = 1024 ;
71- if (segments.receipt .size () > max_printed_size)
72+ auto receipt = ccf::cose::decode_ccf_receipt (
73+ {segments.receipt .begin (), segments.receipt .end ()},
74+ /* recompute_root */ true );
75+
76+ auto snapshot_digest = ccf::crypto::Sha256Hash (
77+ segments.header_and_body .data (), segments.header_and_body .size ());
78+ if (
79+ receipt.claims_digest .size () != ccf::crypto::Sha256Hash::SIZE ||
80+ std::memcmp (
81+ snapshot_digest.h .data (),
82+ receipt.claims_digest .data (),
83+ ccf::crypto::Sha256Hash::SIZE) != 0 )
7284 {
73- LOG_INFO_FMT (
74- " Receipt size ({}) exceeds max printed size ({}), only printing "
75- " first {} bytes" ,
76- segments.receipt .size (),
77- max_printed_size,
78- max_printed_size);
85+ throw std::logic_error (fmt::format (
86+ " Snapshot digest ({}) does not match receipt claim ({})" ,
87+ snapshot_digest,
88+ ds::to_hex (receipt.claims_digest )));
7989 }
80- auto printed_size =
81- std::min<size_t >(segments.receipt .size (), max_printed_size);
82- LOG_INFO_FMT (
83- " {}" ,
84- ds::to_hex (
85- segments.receipt .data (), segments.receipt .data () + printed_size));
8690
91+ if (prev_service_identity)
92+ {
93+ auto verifier =
94+ ccf::crypto::make_cose_verifier_from_cert (*prev_service_identity);
95+ if (!verifier->verify_detached (segments.receipt , receipt.merkle_root ))
96+ {
97+ throw std::logic_error (
98+ " Previous service identity does not match the service identity that "
99+ " signed the snapshot" );
100+ }
101+ LOG_DEBUG_FMT (" Previous service identity matches snapshot signer" );
102+ }
103+ }
104+
105+ static void verify_json_snapshot_receipt (
106+ const SnapshotSegments& segments,
107+ const std::optional<std::vector<uint8_t >>& prev_service_identity)
108+ {
87109 auto j =
88110 nlohmann::json::parse (segments.receipt .begin (), segments.receipt .end ());
89111 auto receipt_p = j.get <ReceiptPtr>();
@@ -106,7 +128,6 @@ namespace ccf
106128 }
107129
108130 auto root = receipt->calculate_root ();
109- auto raw_sig = receipt->signature ;
110131
111132 auto v = ccf::crypto::make_unique_verifier (receipt->cert );
112133 if (!v->verify_hash (
@@ -135,6 +156,54 @@ namespace ccf
135156 }
136157 }
137158
159+ static void verify_snapshot (
160+ const SnapshotSegments& segments,
161+ std::optional<std::vector<uint8_t >> prev_service_identity = std::nullopt )
162+ {
163+ LOG_INFO_FMT (
164+ " Deserialising snapshot receipt (size: {})." , segments.receipt .size ());
165+ constexpr size_t max_printed_size = 1024 ;
166+ if (segments.receipt .size () > max_printed_size)
167+ {
168+ LOG_INFO_FMT (
169+ " Receipt size ({}) exceeds max printed size ({}), only printing "
170+ " first {} bytes" ,
171+ segments.receipt .size (),
172+ max_printed_size,
173+ max_printed_size);
174+ }
175+ auto printed_size =
176+ std::min<size_t >(segments.receipt .size (), max_printed_size);
177+ LOG_INFO_FMT (
178+ " {}" ,
179+ ds::to_hex (
180+ segments.receipt .data (), segments.receipt .data () + printed_size));
181+
182+ if (segments.receipt .empty ())
183+ {
184+ throw std::logic_error (" Empty snapshot receipt" );
185+ }
186+
187+ auto first_byte = segments.receipt [0 ];
188+ constexpr uint8_t ENCODED_COSE_SIGN1_TAG = 0xD2 ;
189+ if (first_byte == ENCODED_COSE_SIGN1_TAG)
190+ {
191+ LOG_DEBUG_FMT (" Snapshot with COSE receipt detected" );
192+ verify_cose_snapshot_receipt (segments, prev_service_identity);
193+ }
194+ else if (first_byte == ' {' )
195+ {
196+ LOG_DEBUG_FMT (" Snapshot with JSON receipt detected" );
197+ verify_json_snapshot_receipt (segments, prev_service_identity);
198+ }
199+ else
200+ {
201+ throw std::logic_error (fmt::format (
202+ " Invalid snapshot receipt: unrecognised format (first byte: 0x{:02X})" ,
203+ first_byte));
204+ }
205+ }
206+
138207 static void deserialise_snapshot (
139208 const std::shared_ptr<ccf::kv::Store>& store,
140209 const SnapshotSegments& segments,
@@ -176,10 +245,8 @@ namespace ccf
176245 }
177246
178247 static std::vector<uint8_t > build_and_serialise_receipt (
179- const std::vector<uint8_t >& sig ,
248+ const std::vector<uint8_t >& cose_sig ,
180249 const std::vector<uint8_t >& tree,
181- const NodeId& node_id,
182- const ccf::crypto::Pem& node_cert,
183250 ccf::kv::Version seqno,
184251 const ccf::crypto::Sha256Hash& write_set_digest,
185252 const std::string& commit_evidence,
@@ -191,18 +258,33 @@ namespace ccf
191258 // NOLINTNEXTLINE(performance-move-const-arg)
192259 cd.set (std::move (claims_digest));
193260 ccf::TxReceiptImpl tx_receipt (
194- sig ,
195- std:: nullopt , // cose
261+ {} ,
262+ cose_sig,
196263 proof.get_root (),
197264 proof.get_path (),
198- node_id ,
199- node_cert ,
265+ {} ,
266+ std:: nullopt ,
200267 write_set_digest,
201268 commit_evidence,
202269 cd);
203270
204- auto receipt = ccf::describe_receipt_v1 (tx_receipt);
205- const auto receipt_str = receipt.dump ();
206- return {receipt_str.begin (), receipt_str.end ()};
271+ // To be replaced with 'describe_cose_receipt' once 7700 is merged.
272+ auto cose_signature = ccf::describe_cose_signature_v1 (tx_receipt);
273+ if (!cose_signature.has_value ())
274+ {
275+ throw std::logic_error (
276+ " No COSE signature available for snapshot receipt" );
277+ }
278+ auto merkle_proof = ccf::describe_merkle_proof_v1 (tx_receipt);
279+ if (!merkle_proof.has_value ())
280+ {
281+ return *cose_signature;
282+ }
283+
284+ ccf::cose::edit::desc::Value desc{
285+ ccf::cose::edit::pos::AtKey{ccf::cose::header::iana::INCLUSION_PROOFS},
286+ ccf::cose::header::iana::VDP,
287+ *merkle_proof};
288+ return ccf::cose::edit::set_unprotected_header (*cose_signature, desc);
207289 }
208290}
0 commit comments