CCF
Loading...
Searching...
No Matches
snapshot_serdes.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/crypto/cose.h"
7#include "ccf/crypto/pem.h"
10#include "crypto/cose.h"
11#include "ds/internal_logger.h"
12#include "ds/serialized.h"
13#include "kv/kv_types.h"
15#include "node/cose_common.h"
16#include "node/history.h"
18
19#include <nlohmann/json.hpp>
20
21namespace ccf
22{
24 {
26 std::vector<uint8_t> raw;
27
28 StartupSnapshotInfo(ccf::kv::Version s, std::vector<uint8_t>&& r) :
29 seqno(s),
30 raw(std::move(r))
31 {}
32 };
33
35 {
36 std::span<const uint8_t> header_and_body;
37 std::span<const uint8_t> receipt;
38 };
39
40 static SnapshotSegments separate_segments(
41 const std::vector<uint8_t>& snapshot)
42 {
43 const auto* data = snapshot.data();
44 auto size = snapshot.size();
45
46 auto tx_hdr = serialized::peek<ccf::kv::SerialisedEntryHeader>(data, size);
47 auto store_snapshot_size =
48 sizeof(ccf::kv::SerialisedEntryHeader) + tx_hdr.size;
49
50 if (tx_hdr.size == 0)
51 {
52 throw std::logic_error("Snapshot transaction size should not be zero");
53 }
54
55 if (store_snapshot_size > size)
56 {
57 throw std::invalid_argument(fmt::format(
58 "Snapshot transaction header claims size {} which exceeds available "
59 "buffer size {}",
60 store_snapshot_size,
61 size));
62 }
63
64 const auto* receipt_data = data + store_snapshot_size;
65 auto receipt_size = size - store_snapshot_size;
66
67 if (receipt_size == 0)
68 {
69 throw std::logic_error("No receipt included in snapshot");
70 }
71
72 std::span<const uint8_t> header_and_body{data, store_snapshot_size};
73 std::span<const uint8_t> receipt{receipt_data, receipt_size};
74
75 return SnapshotSegments{header_and_body, receipt};
76 }
77
78 static void verify_cose_snapshot_receipt(
79 const SnapshotSegments& segments,
80 const std::optional<std::vector<uint8_t>>& prev_service_identity)
81 {
82 auto receipt = ccf::cose::decode_ccf_receipt(
83 {segments.receipt.begin(), segments.receipt.end()},
84 /* recompute_root */ true);
85
86 auto snapshot_digest = ccf::crypto::Sha256Hash(
87 segments.header_and_body.data(), segments.header_and_body.size());
88 if (
89 receipt.claims_digest.size() != ccf::crypto::Sha256Hash::SIZE ||
90 std::memcmp(
91 snapshot_digest.h.data(),
92 receipt.claims_digest.data(),
94 {
95 throw std::logic_error(fmt::format(
96 "Snapshot digest ({}) does not match receipt claim ({})",
97 snapshot_digest,
98 ds::to_hex(receipt.claims_digest)));
99 }
100
101 if (prev_service_identity)
102 {
104 ccf::crypto::Pem(*prev_service_identity));
105 if (!verifier->verify_detached(segments.receipt, receipt.merkle_root))
106 {
107 throw std::logic_error(
108 "Previous service identity does not match the service identity that "
109 "signed the snapshot");
110 }
111 LOG_DEBUG_FMT("Previous service identity matches snapshot signer");
112 }
113 }
114
115 static void verify_json_snapshot_receipt(
116 const SnapshotSegments& segments,
117 const std::optional<std::vector<uint8_t>>& prev_service_identity)
118 {
119 auto j =
120 nlohmann::json::parse(segments.receipt.begin(), segments.receipt.end());
121 auto receipt_p = j.get<ReceiptPtr>();
122 auto receipt = std::dynamic_pointer_cast<ccf::ProofReceipt>(receipt_p);
123 if (receipt == nullptr)
124 {
125 throw std::logic_error(
126 "Unexpected receipt type: missing expanded claims");
127 }
128
129 auto snapshot_digest = ccf::crypto::Sha256Hash(
130 segments.header_and_body.data(), segments.header_and_body.size());
131 auto snapshot_digest_claim = receipt->leaf_components.claims_digest.value();
132 if (snapshot_digest != snapshot_digest_claim)
133 {
134 throw std::logic_error(fmt::format(
135 "Snapshot digest ({}) does not match receipt claim ({})",
136 snapshot_digest,
137 snapshot_digest_claim));
138 }
139
140 auto root = receipt->calculate_root();
141
142 auto v = ccf::crypto::make_unique_verifier(receipt->cert);
143 if (!v->verify_hash(
144 root.h.data(),
145 root.h.size(),
146 receipt->signature.data(),
147 receipt->signature.size(),
149 {
150 throw std::logic_error(
151 "Signature verification failed for snapshot receipt");
152 }
153
154 if (prev_service_identity)
155 {
156 ccf::crypto::Pem prev_pem(*prev_service_identity);
157 if (!v->verify_certificate(
158 {&prev_pem}, {}, true /* ignore_time */
159 ))
160 {
161 throw std::logic_error(
162 "Previous service identity does not endorse the node identity "
163 "that signed the snapshot");
164 }
165 LOG_DEBUG_FMT("Previous service identity endorses snapshot signer");
166 }
167 }
168
169 static void verify_snapshot(
170 const SnapshotSegments& segments,
171 std::optional<std::vector<uint8_t>> prev_service_identity = std::nullopt)
172 {
174 "Deserialising snapshot receipt (size: {}).", segments.receipt.size());
175 constexpr size_t max_printed_size = 1024;
176 if (segments.receipt.size() > max_printed_size)
177 {
179 "Receipt size ({}) exceeds max printed size ({}), only printing "
180 "first {} bytes",
181 segments.receipt.size(),
182 max_printed_size,
183 max_printed_size);
184 }
185 auto printed_size =
186 std::min<size_t>(segments.receipt.size(), max_printed_size);
188 "{}",
189 ds::to_hex(
190 segments.receipt.data(), segments.receipt.data() + printed_size));
191
192 if (segments.receipt.empty())
193 {
194 throw std::logic_error("Empty snapshot receipt");
195 }
196
197 auto first_byte = segments.receipt[0];
198 constexpr uint8_t ENCODED_COSE_SIGN1_TAG = 0xD2;
199 if (first_byte == ENCODED_COSE_SIGN1_TAG)
200 {
201 LOG_DEBUG_FMT("Snapshot with COSE receipt detected");
202 verify_cose_snapshot_receipt(segments, prev_service_identity);
203 }
204 else if (first_byte == '{')
205 {
206 LOG_DEBUG_FMT("Snapshot with JSON receipt detected");
207 verify_json_snapshot_receipt(segments, prev_service_identity);
208 }
209 else
210 {
211 throw std::logic_error(fmt::format(
212 "Invalid snapshot receipt: unrecognised format (first byte: 0x{:02X})",
213 first_byte));
214 }
215 }
216
217 static void deserialise_snapshot(
218 const std::shared_ptr<ccf::kv::Store>& store,
219 const SnapshotSegments& segments,
221 std::vector<ccf::kv::Version>* view_history = nullptr,
222 bool public_only = false)
223 {
224 const auto* data = segments.header_and_body.data();
225 const auto size = segments.header_and_body.size();
226
227 // Log full size as this snapshot appears in file, but after that ignore the
228 // receipt segment
230 "Deserialising snapshot (size: {}, public only: {})",
231 size + segments.receipt.size(),
232 public_only);
233
234 auto rc =
235 store->deserialise_snapshot(data, size, hooks, view_history, public_only);
237 {
238 throw std::logic_error(fmt::format("Failed to apply snapshot: {}", rc));
239 }
240
242 "Snapshot successfully deserialised at seqno {}",
243 store->current_version());
244 };
245
246 static void deserialise_snapshot(
247 const std::shared_ptr<ccf::kv::Store>& store,
248 const std::vector<uint8_t>& snapshot,
250 std::vector<ccf::kv::Version>* view_history = nullptr,
251 bool public_only = false)
252 {
253 const auto segments = separate_segments(snapshot);
254 deserialise_snapshot(store, segments, hooks, view_history, public_only);
255 }
256
257 static std::vector<uint8_t> build_and_serialise_receipt(
258 const std::vector<uint8_t>& cose_sig,
259 const std::vector<uint8_t>& tree,
260 ccf::kv::Version seqno,
261 const ccf::crypto::Sha256Hash& write_set_digest,
262 const std::string& commit_evidence,
263 ccf::crypto::Sha256Hash&& claims_digest)
264 {
265 ccf::MerkleTreeHistory history(tree);
266 auto proof = history.get_proof(seqno);
268 // NOLINTNEXTLINE(performance-move-const-arg)
269 cd.set(std::move(claims_digest));
270 ccf::TxReceiptImpl tx_receipt(
271 {},
272 cose_sig,
273 proof.get_root(),
274 proof.get_path(),
275 {},
276 std::nullopt,
277 write_set_digest,
278 commit_evidence,
279 cd);
280
281 // To be replaced with 'describe_cose_receipt' once 7700 is merged.
282 auto cose_signature = ccf::describe_cose_signature_v1(tx_receipt);
283 if (!cose_signature.has_value())
284 {
285 throw std::logic_error(
286 "No COSE signature available for snapshot receipt");
287 }
288 auto merkle_proof = ccf::describe_merkle_proof_v1(tx_receipt);
289 if (!merkle_proof.has_value())
290 {
291 return *cose_signature;
292 }
293
295 ccf::cose::edit::pos::AtKey{ccf::cose::header::iana::INCLUSION_PROOFS},
296 ccf::cose::header::iana::VDP,
297 *merkle_proof};
298 return ccf::cose::edit::set_unprotected_header(*cose_signature, desc);
299 }
300}
Definition claims_digest.h:10
void set(Digest &&digest_)
Definition claims_digest.h:21
Definition history.h:436
Definition pem.h:18
Definition sha256_hash.h:16
static constexpr size_t SIZE
Definition sha256_hash.h:18
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
std::vector< uint8_t > set_unprotected_header(const std::span< const uint8_t > &cose_input, const desc::Type &descriptor)
Definition cose.cpp:14
VerifierUniquePtr make_unique_verifier(const std::vector< uint8_t > &cert)
Definition verifier.cpp:13
COSEVerifierUniquePtr make_cose_verifier_from_pem_cert(const Pem &pem)
Definition cose_verifier.cpp:280
uint64_t Version
Definition version.h:10
@ PASS
Definition kv_types.h:306
std::vector< ConsensusHookPtr > ConsensusHookPtrs
Definition hooks.h:22
Definition app_interface.h:13
std::optional< std::vector< uint8_t > > describe_merkle_proof_v1(const TxReceiptImpl &receipt)
Definition historical_queries_adapter.cpp:226
std::shared_ptr< Receipt > ReceiptPtr
Definition receipt.h:128
std::optional< SerialisedCoseSignature > describe_cose_signature_v1(const TxReceiptImpl &receipt)
Definition historical_queries_adapter.cpp:268
STL namespace.
Definition snapshot_serdes.h:35
std::span< const uint8_t > header_and_body
Definition snapshot_serdes.h:36
std::span< const uint8_t > receipt
Definition snapshot_serdes.h:37
Definition snapshot_serdes.h:24
StartupSnapshotInfo(ccf::kv::Version s, std::vector< uint8_t > &&r)
Definition snapshot_serdes.h:28
std::vector< uint8_t > raw
Definition snapshot_serdes.h:26
ccf::kv::Version seqno
Definition snapshot_serdes.h:25
Definition tx_receipt_impl.h:14
Definition cose.h:32
Definition cose.h:18
Definition serialised_entry_format.h:21