CCF
Loading...
Searching...
No Matches
node_state.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
6#include "ccf/crypto/pem.h"
9#include "ccf/ds/json.h"
10#include "ccf/ds/logger.h"
11#include "ccf/js/core/context.h"
14#include "ccf/pal/locking.h"
15#include "ccf/pal/platform.h"
16#include "ccf/pal/snp_ioctl.h"
22#include "ccf/tx.h"
23#include "ccf_acme_client.h"
24#include "consensus/aft/raft.h"
26#include "crypto/certs.h"
27#include "ds/ccf_assert.h"
28#include "ds/files.h"
29#include "ds/state_machine.h"
31#include "encryptor.h"
32#include "history.h"
33#include "http/http_parser.h"
34#include "indexing/indexer.h"
35#include "js/global_class_ids.h"
36#include "network_state.h"
37#include "node/hooks.h"
40#include "node/ledger_secret.h"
41#include "node/ledger_secrets.h"
42#include "node/local_sealing.h"
44#include "node/snapshotter.h"
45#include "node_to_node.h"
48#include "rpc/frontend.h"
49#include "rpc/serialization.h"
50#include "secret_broadcast.h"
53#include "share_manager.h"
54#include "uvm_endorsements.h"
55
56#include <optional>
57
58#ifdef USE_NULL_ENCRYPTOR
59# include "kv/test/null_encryptor.h"
60#endif
61
62#include <atomic>
63#include <chrono>
64#define FMT_HEADER_ONLY
65#include <fmt/format.h>
66#include <nlohmann/json.hpp>
67#include <stdexcept>
68#include <unordered_set>
69#include <vector>
70
71namespace ccf
72{
74
80
81 void reset_data(std::vector<uint8_t>& data)
82 {
83 data.clear();
84 data.shrink_to_fit();
85 }
86
88 {
89 private:
90 //
91 // this node's core state
92 //
94 pal::Mutex lock;
95 StartType start_type;
96
97 ccf::crypto::CurveID curve_id;
98 std::vector<ccf::crypto::SubjectAltName> subject_alt_names = {};
99
100 std::shared_ptr<ccf::crypto::KeyPair_OpenSSL> node_sign_kp;
101 NodeId self;
102 std::shared_ptr<ccf::crypto::RSAKeyPair> node_encrypt_kp;
103 ccf::crypto::Pem self_signed_node_cert;
104 std::optional<ccf::crypto::Pem> endorsed_node_cert = std::nullopt;
105 QuoteInfo quote_info;
107 std::optional<pal::snp::TcbVersionRaw> snp_tcb_version = std::nullopt;
108 ccf::StartupConfig config;
109 std::optional<pal::UVMEndorsements> snp_uvm_endorsements = std::nullopt;
110 std::vector<uint8_t> startup_snapshot;
111 std::shared_ptr<QuoteEndorsementsClient> quote_endorsements_client =
112 nullptr;
113
114 std::atomic<bool> stop_noticed = false;
115
116 struct NodeStateMsg
117 {
118 NodeStateMsg(
119 NodeState& self_,
120 View create_view_ = 0,
121 bool create_consortium_ = true) :
122 self(self_),
123 create_view(create_view_),
124 create_consortium(create_consortium_)
125 {}
126 NodeState& self;
127 View create_view;
128 bool create_consortium;
129 };
130
131 //
132 // kv store, replication, and I/O
133 //
134 ringbuffer::AbstractWriterFactory& writer_factory;
135 ringbuffer::WriterPtr to_host;
136 ccf::consensus::Configuration consensus_config;
137 size_t sig_tx_interval;
138 size_t sig_ms_interval;
139
140 NetworkState& network;
141
142 std::shared_ptr<ccf::kv::Consensus> consensus;
143 std::shared_ptr<RPCMap> rpc_map;
144 std::shared_ptr<indexing::Indexer> indexer;
145 std::shared_ptr<NodeToNode> n2n_channels;
146 std::shared_ptr<Forwarder<NodeToNode>> cmd_forwarder;
147 std::shared_ptr<RPCSessions> rpcsessions;
148
149 std::shared_ptr<ccf::kv::TxHistory> history;
150 std::shared_ptr<ccf::kv::AbstractTxEncryptor> encryptor;
151
152 ShareManager share_manager;
153 std::shared_ptr<Snapshotter> snapshotter;
154
155 //
156 // recovery
157 //
158 std::shared_ptr<ccf::kv::Store> recovery_store;
159
160 ccf::kv::Version recovery_v;
161 ccf::crypto::Sha256Hash recovery_root;
162 std::vector<ccf::kv::Version> view_history;
163 ::consensus::Index last_recovered_signed_idx = 0;
164 RecoveredEncryptedLedgerSecrets recovered_encrypted_ledger_secrets = {};
165 ::consensus::Index last_recovered_idx = 0;
166 static const size_t recovery_batch_size = 100;
167
168 //
169 // JWT key auto-refresh
170 //
171 std::shared_ptr<JwtKeyAutoRefresh> jwt_key_auto_refresh;
172
173 std::unique_ptr<StartupSnapshotInfo> startup_snapshot_info = nullptr;
174 // Set to the snapshot seqno when a node starts from one and remembered for
175 // the lifetime of the node
176 ccf::kv::Version startup_seqno = 0;
177
178 // ACME certificate endorsement client
179 std::map<NodeInfoNetwork::RpcInterfaceID, std::shared_ptr<ACMEClient>>
180 acme_clients;
181 std::map<
183 std::shared_ptr<ACMEChallengeHandler>>
184 acme_challenge_handlers;
185 size_t num_acme_interfaces = 0;
186
187 std::shared_ptr<ccf::kv::AbstractTxEncryptor> make_encryptor()
188 {
189#ifdef USE_NULL_ENCRYPTOR
190 return std::make_shared<ccf::kv::NullTxEncryptor>();
191#else
192 return std::make_shared<NodeEncryptor>(network.ledger_secrets);
193#endif
194 }
195
196 // Returns true if the snapshot is already verified (via embedded receipt)
197 void initialise_startup_snapshot(bool recovery = false)
198 {
199 std::shared_ptr<ccf::kv::Store> snapshot_store;
200 if (!recovery)
201 {
202 // Create a new store to verify the snapshot only
203 snapshot_store = make_store();
204 auto snapshot_history = std::make_shared<MerkleTxHistory>(
205 *snapshot_store.get(),
206 self,
207 *node_sign_kp,
208 sig_tx_interval,
209 sig_ms_interval,
210 false /* No signature timer on snapshot_history */);
211
212 auto snapshot_encryptor = make_encryptor();
213
214 snapshot_store->set_history(snapshot_history);
215 snapshot_store->set_encryptor(snapshot_encryptor);
216 }
217 else
218 {
219 snapshot_store = network.tables;
220 }
221
223 startup_snapshot_info = initialise_from_snapshot(
224 snapshot_store,
225 std::move(startup_snapshot),
226 hooks,
227 &view_history,
228 true,
230
231 startup_seqno = startup_snapshot_info->seqno;
232 last_recovered_idx = startup_seqno;
233 last_recovered_signed_idx = last_recovered_idx;
234 }
235
236 public:
238 ringbuffer::AbstractWriterFactory& writer_factory,
239 NetworkState& network,
240 std::shared_ptr<RPCSessions> rpcsessions,
241 ccf::crypto::CurveID curve_id_) :
242 sm("NodeState", NodeStartupState::uninitialized),
243 curve_id(curve_id_),
244 node_sign_kp(std::make_shared<ccf::crypto::KeyPair_OpenSSL>(curve_id_)),
245 self(compute_node_id_from_kp(node_sign_kp)),
246 node_encrypt_kp(ccf::crypto::make_rsa_key_pair()),
247 writer_factory(writer_factory),
248 to_host(writer_factory.create_writer_to_outside()),
249 network(network),
250 rpcsessions(rpcsessions),
251 share_manager(network.ledger_secrets)
252 {}
253
256 const QuoteInfo& quote_info_,
257 const std::vector<uint8_t>& expected_node_public_key_der,
258 pal::PlatformAttestationMeasurement& measurement) override
259 {
261 tx, quote_info_, expected_node_public_key_der, measurement);
262 }
263
264 //
265 // funcs in state "uninitialized"
266 //
268 const ccf::consensus::Configuration& consensus_config_,
269 std::shared_ptr<RPCMap> rpc_map_,
270 std::shared_ptr<AbstractRPCResponder> rpc_sessions_,
271 std::shared_ptr<indexing::Indexer> indexer_,
272 size_t sig_tx_interval_,
273 size_t sig_ms_interval_)
274 {
275 std::lock_guard<pal::Mutex> guard(lock);
277
278 consensus_config = consensus_config_;
279 rpc_map = rpc_map_;
280 indexer = indexer_;
281 sig_tx_interval = sig_tx_interval_;
282 sig_ms_interval = sig_ms_interval_;
283
284 n2n_channels = std::make_shared<NodeToNodeChannelManager>(writer_factory);
285
286 cmd_forwarder = std::make_shared<Forwarder<NodeToNode>>(
287 rpc_sessions_, n2n_channels, rpc_map);
288
290
291 for (auto& [actor, fe] : rpc_map->frontends())
292 {
293 fe->set_sig_intervals(sig_tx_interval, sig_ms_interval);
294 fe->set_cmd_forwarder(cmd_forwarder);
295 }
296 }
297
298 //
299 // funcs in state "initialized"
300 //
302 {
303 auto measurement = AttestationProvider::get_measurement(quote_info);
304 if (measurement.has_value())
305 {
306 node_measurement = measurement.value();
307 }
308 else
309 {
310 throw std::logic_error("Failed to extract code id from quote");
311 }
312
313 auto snp_attestation =
315 if (snp_attestation.has_value())
316 {
317 snp_tcb_version = snp_attestation.value().reported_tcb;
318 }
319
320 // Verify that the security policy matches the quoted digest of the policy
321 if (!config.attestation.environment.security_policy.has_value())
322 {
324 "Security policy not set, skipping check against attestation host "
325 "data");
326 }
327 else
328 {
329 auto quoted_digest = AttestationProvider::get_host_data(quote_info);
330 if (!quoted_digest.has_value())
331 {
332 throw std::logic_error("Unable to find host data in attestation");
333 }
334
335 auto const& security_policy =
337
338 auto security_policy_digest =
339 quote_info.format == QuoteFormat::amd_sev_snp_v1 ?
341 ccf::crypto::Sha256Hash(security_policy);
342 if (security_policy_digest != quoted_digest.value())
343 {
344 throw std::logic_error(fmt::format(
345 "Digest of decoded security policy \"{}\" {} does not match "
346 "attestation host data {}",
347 security_policy,
348 security_policy_digest.hex_str(),
349 quoted_digest.value().hex_str()));
350 }
352 "Successfully verified attested security policy {}",
353 security_policy_digest);
354 }
355
356 if (quote_info.format == QuoteFormat::amd_sev_snp_v1)
357 {
358 if (!config.attestation.environment.uvm_endorsements.has_value())
359 {
361 "UVM endorsements not set, skipping check against attestation "
362 "measurement");
363 }
364 else
365 {
366 try
367 {
368 auto uvm_endorsements_raw = ccf::crypto::raw_from_b64(
370 // A node at this stage does not have a notion of what UVM
371 // descriptor is acceptable. That is decided either by the Joinee,
372 // or by Consortium endorsing the Start or Recovery node. For that
373 // reason, we extract an endorsement descriptor from the UVM
374 // endorsements and make it available in the ledger's initial or
375 // recovery transaction.
376 snp_uvm_endorsements = pal::verify_uvm_endorsements_descriptor(
377 uvm_endorsements_raw, node_measurement);
378 quote_info.uvm_endorsements = uvm_endorsements_raw;
380 "Successfully verified attested UVM endorsements: {}",
381 snp_uvm_endorsements->to_str());
382 }
383 catch (const std::exception& e)
384 {
385 throw std::logic_error(
386 fmt::format("Error verifying UVM endorsements: {}", e.what()));
387 }
388 }
389 }
390
391 switch (start_type)
392 {
393 case StartType::Start:
394 {
395 create_and_send_boot_request(
396 aft::starting_view_change, true /* Create new consortium */);
397 return;
398 }
399 case StartType::Join:
400 {
401 if (!startup_snapshot.empty())
402 {
403 initialise_startup_snapshot();
404 }
405
408 return;
409 }
411 {
412 setup_recovery_hook();
413 if (!startup_snapshot.empty())
414 {
415 initialise_startup_snapshot(true);
416 snapshotter->set_last_snapshot_idx(last_recovered_idx);
417 }
418
421 return;
422 }
423 default:
424 {
425 throw std::logic_error(
426 fmt::format("Node was launched in unknown mode {}", start_type));
427 }
428 }
429 }
430
432 {
433 auto fetch_endorsements = [this](
434 const QuoteInfo& qi,
435 const pal::snp::
436 EndorsementEndpointsConfiguration&
437 endpoint_config) {
438 // Note: Node lock is already taken here as this is called back
439 // synchronously with the call to pal::generate_quote
440 this->quote_info = qi;
441
442 if (quote_info.format == QuoteFormat::amd_sev_snp_v1)
443 {
444 // Use endorsements retrieved from file, if available
445 if (config.attestation.environment.snp_endorsements.has_value())
446 {
447 try
448 {
449 const auto raw_data = ccf::crypto::raw_from_b64(
451
452 const auto j = nlohmann::json::parse(raw_data);
453 const auto aci_endorsements =
455
456 // Check that tcbm in endorsement matches reported TCB in our
457 // retrieved attestation
458 auto* quote = reinterpret_cast<const ccf::pal::snp::Attestation*>(
459 quote_info.quote.data());
460 const auto reported_tcb = quote->reported_tcb;
461
462 // tcbm is a single hex value, like DB18000000000004. To match
463 // that with a TcbVersion, reverse the bytes.
464 const uint8_t* tcb_begin =
465 reinterpret_cast<const uint8_t*>(&reported_tcb);
466 const std::span<const uint8_t> tcb_bytes{
467 tcb_begin, tcb_begin + sizeof(reported_tcb)};
468 auto tcb_as_hex = fmt::format(
469 "{:02x}", fmt::join(tcb_bytes.rbegin(), tcb_bytes.rend(), ""));
470 ccf::nonstd::to_upper(tcb_as_hex);
471
472 if (tcb_as_hex == aci_endorsements.tcbm)
473 {
475 "Using SNP endorsements loaded from file, endorsing TCB {}",
476 tcb_as_hex);
477
478 auto& endorsements_pem = quote_info.endorsements;
479 endorsements_pem.insert(
480 endorsements_pem.end(),
481 aci_endorsements.vcek_cert.begin(),
482 aci_endorsements.vcek_cert.end());
483 endorsements_pem.insert(
484 endorsements_pem.end(),
485 aci_endorsements.certificate_chain.begin(),
486 aci_endorsements.certificate_chain.end());
487
488 try
489 {
490 launch_node();
491 return;
492 }
493 catch (const std::exception& e)
494 {
495 LOG_FAIL_FMT("Failed to launch node: {}", e.what());
496 throw;
497 }
498 }
499 else
500 {
502 "SNP endorsements loaded from disk ({}) contained tcbm {}, "
503 "which does not match reported TCB of current attestation "
504 "{}. "
505 "Falling back to fetching fresh endorsements from server.",
506 config.attestation.snp_endorsements_file.value(),
507 aci_endorsements.tcbm,
508 tcb_as_hex);
509 }
510 }
511 catch (const std::exception& e)
512 {
514 "Error attempting to use SNP endorsements from file: {}",
515 e.what());
516 }
517 }
518
519 if (config.attestation.snp_endorsements_servers.empty())
520 {
521 throw std::runtime_error(
522 "One or more SNP endorsements servers must be specified to fetch "
523 "the collateral for the attestation");
524 }
525 // On SEV-SNP, fetch endorsements from servers if specified
526 quote_endorsements_client = std::make_shared<QuoteEndorsementsClient>(
527 rpcsessions,
528 endpoint_config,
529 [this](std::vector<uint8_t>&& endorsements) {
530 std::lock_guard<pal::Mutex> guard(lock);
531 quote_info.endorsements = std::move(endorsements);
532 try
533 {
534 launch_node();
535 }
536 catch (const std::exception& e)
537 {
538 LOG_FAIL_FMT("{}", e.what());
539 throw;
540 }
541 quote_endorsements_client.reset();
542 });
543
544 quote_endorsements_client->fetch_endorsements();
545 return;
546 }
547 else // Non-SNP
548 {
549 if (!((quote_info.format == QuoteFormat::oe_sgx_v1 &&
550 !quote_info.endorsements.empty()) ||
551 (quote_info.format != QuoteFormat::oe_sgx_v1 &&
552 quote_info.endorsements.empty())))
553 {
554 throw std::runtime_error(
555 "SGX quote generation should have already fetched endorsements");
556 }
557
558 launch_node();
559 }
560 };
561
563 ccf::crypto::Sha256Hash((node_sign_kp->public_key_der()));
564
565 pal::generate_quote(
566 report_data,
567 fetch_endorsements,
569 }
570
572 StartType start_type_,
573 const ccf::StartupConfig& config_,
574 std::vector<uint8_t>&& startup_snapshot_)
575 {
576 std::lock_guard<pal::Mutex> guard(lock);
578 start_type = start_type_;
579
580 config = config_;
581 startup_snapshot = std::move(startup_snapshot_);
582 subject_alt_names = get_subject_alternative_names();
583
585 self_signed_node_cert = create_self_signed_cert(
586 node_sign_kp,
588 subject_alt_names,
589 config.startup_host_time,
591
592 accept_node_tls_connections();
593 open_frontend(ActorsType::nodes);
594
595 // Signatures are only emitted on a timer once the public ledger has been
596 // recovered
597 setup_history();
598 setup_snapshotter();
599 setup_encryptor();
600
601 setup_acme_clients();
602
604
605 switch (start_type)
606 {
607 case StartType::Start:
608 {
609 network.identity = std::make_unique<ccf::NetworkIdentity>(
611 curve_id,
612 config.startup_host_time,
614
615 network.ledger_secrets->init();
616 // Safe as initiate_quote_generation has previously set the
617 // snp_tcb_version
618 seal_ledger_secret(network.ledger_secrets->get_first());
619
620 history->set_service_signing_identity(
621 network.identity->get_key_pair(), config.cose_signatures);
622
623 setup_consensus(
626 false,
627 endorsed_node_cert);
628
629 // Become the primary and force replication
630 consensus->force_become_primary();
631
632 LOG_INFO_FMT("Created new node {}", self);
633 return {self_signed_node_cert, network.identity->cert};
634 }
635 case StartType::Join:
636 {
637 LOG_INFO_FMT("Created join node {}", self);
638 return {self_signed_node_cert, {}};
639 }
641 {
643 {
644 throw std::logic_error(
645 "Recovery requires the certificate of the previous service "
646 "identity");
647 }
648
649 ccf::crypto::Pem previous_service_identity_cert(
650 config.recover.previous_service_identity.value());
651
652 network.identity = std::make_unique<ccf::NetworkIdentity>(
653 ccf::crypto::get_subject_name(previous_service_identity_cert),
654 curve_id,
655 config.startup_host_time,
657
658 LOG_INFO_FMT("Created recovery node {}", self);
659 return {self_signed_node_cert, network.identity->cert};
660 }
661 default:
662 {
663 throw std::logic_error(
664 fmt::format("Node was started in unknown mode {}", start_type));
665 }
666 }
667 }
668
669 //
670 // funcs in state "pending"
671 //
672
674 {
676
677 auto network_ca = std::make_shared<::tls::CA>(std::string(
678 config.join.service_cert.begin(), config.join.service_cert.end()));
679
680 auto join_client_cert = std::make_unique<::tls::Cert>(
681 network_ca,
682 self_signed_node_cert,
683 node_sign_kp->private_key_pem(),
684 config.join.target_rpc_address);
685
686 // Create RPC client and connect to remote node
687 // Note: For now, assume that target node accepts same application
688 // protocol as this node's main RPC interface
689 auto join_client = rpcsessions->create_client(
690 std::move(join_client_cert),
691 rpcsessions->get_app_protocol_main_interface());
692
693 auto [target_host, target_port] =
694 split_net_address(config.join.target_rpc_address);
695
696 join_client->connect(
697 target_host,
698 target_port,
699 [this](
700 ccf::http_status status,
701 http::HeaderMap&& headers,
702 std::vector<uint8_t>&& data) {
703 std::lock_guard<pal::Mutex> guard(lock);
705 {
706 return;
707 }
708
709 if (is_http_status_client_error(status))
710 {
711 auto error_msg = fmt::format(
712 "Join request to {} returned {} Bad Request: {}. Shutting "
713 "down node gracefully.",
715 status,
716 std::string(data.begin(), data.end()));
717 LOG_FAIL_FMT("{}", error_msg);
719 AdminMessage::fatal_error_msg, to_host, error_msg);
720 }
721 else if (status != HTTP_STATUS_OK)
722 {
723 const auto& location = headers.find(http::headers::LOCATION);
724 if (
725 config.join.follow_redirect &&
726 (status == HTTP_STATUS_PERMANENT_REDIRECT ||
727 status == HTTP_STATUS_TEMPORARY_REDIRECT) &&
728 location != headers.end())
729 {
730 const auto& url = ::http::parse_url_full(location->second);
731 config.join.target_rpc_address =
732 make_net_address(url.host, url.port);
733 LOG_INFO_FMT("Target node redirected to {}", location->second);
734 }
735 else
736 {
738 "An error occurred while joining the network: {} {}{}",
739 status,
740 ccf::http_status_str(status),
741 data.empty() ?
742 "" :
743 fmt::format(" '{}'", std::string(data.begin(), data.end())));
744 }
745 return;
746 }
747
748 auto j = nlohmann::json::parse(data);
749
751 try
752 {
753 resp = j.get<JoinNetworkNodeToNode::Out>();
754 }
755 catch (const std::exception& e)
756 {
758 "An error occurred while parsing the join network response");
760 "An error occurred while parsing the join network response: {}",
761 j.dump());
762 return;
763 }
764
765 // Set network secrets, node id and become part of network.
767 {
768 if (!resp.network_info.has_value())
769 {
770 throw std::logic_error("Expected network info in join response");
771 }
772
773 network.identity = std::make_unique<ccf::NetworkIdentity>(
774 resp.network_info->identity);
775 seal_ledger_secret(*resp.network_info->ledger_secrets.rbegin());
776 network.ledger_secrets->init_from_map(
777 std::move(resp.network_info->ledger_secrets));
778
779 history->set_service_signing_identity(
780 network.identity->get_key_pair(),
781 resp.network_info->cose_signatures_config.value_or(
783
784 ccf::crypto::Pem n2n_channels_cert;
785 if (!resp.network_info->endorsed_certificate.has_value())
786 {
787 // Endorsed certificate was added to join response in 2.x
788 throw std::logic_error(
789 "Expected endorsed certificate in join response");
790 }
791 n2n_channels_cert = resp.network_info->endorsed_certificate.value();
792
793 setup_consensus(
794 resp.network_info->service_status.value_or(
797 resp.network_info->public_only,
798 n2n_channels_cert);
800
801 if (resp.network_info->public_only)
802 {
803 last_recovered_signed_idx =
804 resp.network_info->last_recovered_signed_idx;
805 setup_recovery_hook();
806 snapshotter->set_snapshot_generation(false);
807 }
808
810 std::vector<ccf::kv::Version> view_history_ = {};
811 if (startup_snapshot_info)
812 {
813 // It is only possible to deserialise the entire snapshot then,
814 // once the ledger secrets have been passed in by the network
816 deserialise_snapshot(
817 network.tables,
818 startup_snapshot_info->raw,
819 hooks,
820 &view_history_,
821 resp.network_info->public_only,
823
824 for (auto& hook : hooks)
825 {
826 hook->call(consensus.get());
827 }
828
829 auto tx = network.tables->create_read_only_tx();
830 auto signatures = tx.ro(network.signatures);
831 auto sig = signatures->get();
832 if (!sig.has_value())
833 {
834 throw std::logic_error(
835 fmt::format("No signatures found after applying snapshot"));
836 }
837 view = sig->view;
838
839 if (!resp.network_info->public_only)
840 {
841 // Only clear snapshot if not recovering. When joining the
842 // public network the snapshot is used later to initialise the
843 // recovery store
844 startup_snapshot_info.reset();
845 }
846
848 "Joiner successfully resumed from snapshot at seqno {} and "
849 "view {}",
850 network.tables->current_version(),
851 view);
852 }
853
854 consensus->init_as_backup(
855 network.tables->current_version(),
856 view,
857 view_history_,
858 last_recovered_signed_idx);
859
860 snapshotter->set_last_snapshot_idx(
861 network.tables->current_version());
862 history->start_signature_emit_timer();
863
864 if (resp.network_info->public_only)
865 {
867 }
868 else
869 {
870 reset_data(quote_info.quote);
871 reset_data(quote_info.endorsements);
873 }
874
876 "Node has now joined the network as node {}: {}",
877 self,
878 (resp.network_info->public_only ? "public only" : "all domains"));
879 }
880 else if (resp.node_status == NodeStatus::PENDING)
881 {
883 "Node {} is waiting for votes of members to be trusted", self);
884 }
885 },
886 [this](const std::string& error_msg) {
887 std::lock_guard<pal::Mutex> guard(lock);
888 auto long_error_msg = fmt::format(
889 "Early error when joining existing network at {}: {}. Shutting "
890 "down node gracefully...",
892 error_msg);
893 LOG_FAIL_FMT("{}", long_error_msg);
895 AdminMessage::fatal_error_msg, to_host, long_error_msg);
896 });
897
898 // Send RPC request to remote node to join the network.
899 JoinNetworkNodeToNode::In join_params;
900
901 join_params.node_info_network = config.network;
902 join_params.public_encryption_key = node_encrypt_kp->public_key_pem();
903 join_params.quote_info = quote_info;
904 join_params.startup_seqno = startup_seqno;
905 join_params.certificate_signing_request = node_sign_kp->create_csr(
906 config.node_certificate.subject_name, subject_alt_names);
907 join_params.node_data = config.node_data;
908
910 "Sending join request to {}", config.join.target_rpc_address);
911
912 const auto body = nlohmann::json(join_params).dump();
913
914 LOG_DEBUG_FMT("Sending join request body: {}", body);
915
917 fmt::format("/{}/{}", get_actor_prefix(ActorsType::nodes), "join"));
918 r.set_header(
919 http::headers::CONTENT_TYPE, http::headervalues::contenttype::JSON);
920 r.set_body(body);
921
922 join_client->send_request(std::move(r));
923 }
924
926 {
927 std::lock_guard<pal::Mutex> guard(lock);
929 }
930
932 {
934
935 auto timer_msg = std::make_unique<::threading::Tmsg<NodeStateMsg>>(
936 [](std::unique_ptr<::threading::Tmsg<NodeStateMsg>> msg) {
937 std::lock_guard<pal::Mutex> guard(msg->data.self.lock);
938 if (msg->data.self.sm.check(NodeStartupState::pending))
939 {
940 msg->data.self.initiate_join_unsafe();
941 auto delay = std::chrono::milliseconds(
942 msg->data.self.config.join.retry_timeout);
943
945 std::move(msg), delay);
946 }
947 },
948 *this);
949
951 std::move(timer_msg), config.join.retry_timeout);
952 }
953
955 {
956 if (!consensus)
957 {
959 "JWT key auto-refresh: consensus not initialized, not starting "
960 "auto-refresh");
961 return;
962 }
963 jwt_key_auto_refresh = std::make_shared<JwtKeyAutoRefresh>(
965 network,
966 consensus,
967 rpcsessions,
968 rpc_map,
969 node_sign_kp,
970 self_signed_node_cert);
971 jwt_key_auto_refresh->start();
972
973 network.tables->set_map_hook(
974 network.jwt_issuers.get_name(),
977 jwt_key_auto_refresh->schedule_once();
978 return ccf::kv::ConsensusHookPtr(nullptr);
979 });
980 }
981
982 size_t get_jwt_attempts() override
983 {
984 return jwt_key_auto_refresh->get_attempts();
985 }
986
987 //
988 // funcs in state "readingPublicLedger"
989 //
991 {
993 {
994 throw std::logic_error(fmt::format(
995 "Node should be in state {} to start reading ledger",
997 }
998
999 LOG_INFO_FMT("Starting to read public ledger");
1000
1001 read_ledger_entries(
1002 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
1003 }
1004
1005 void recover_public_ledger_entries(const std::vector<uint8_t>& entries)
1006 {
1007 std::lock_guard<pal::Mutex> guard(lock);
1008
1010
1011 auto data = entries.data();
1012 auto size = entries.size();
1013
1014 if (size == 0)
1015 {
1017 return;
1018 }
1019
1020 while (size > 0)
1021 {
1022 auto entry = ::consensus::LedgerEnclave::get_entry(data, size);
1023
1025 "Deserialising public ledger entry #{} [{} bytes]",
1026 last_recovered_idx,
1027 entry.size());
1028
1029 // When reading the private ledger, deserialise in the recovery store
1030
1032 try
1033 {
1034 auto r = network.tables->deserialize(entry, true);
1035 result = r->apply();
1036 if (result == ccf::kv::ApplyResult::FAIL)
1037 {
1039 "Failed to deserialise public ledger entry: {}", result);
1041 return;
1042 }
1043 ++last_recovered_idx;
1044
1045 // Not synchronised because consensus isn't effectively running then
1046 for (auto& hook : r->get_hooks())
1047 {
1048 hook->call(consensus.get());
1049 }
1050 }
1051 catch (const std::exception& e)
1052 {
1054 "Failed to deserialise public ledger entry: {}", e.what());
1056 return;
1057 }
1058
1059 // If the ledger entry is a signature, it is safe to compact the store
1061 {
1062 // If the ledger entry is a signature, it is safe to compact the store
1063 network.tables->compact(last_recovered_idx);
1064 auto tx = network.tables->create_read_only_tx();
1065 auto last_sig = tx.ro(network.signatures)->get();
1066
1067 if (!last_sig.has_value())
1068 {
1069 throw std::logic_error("Signature missing");
1070 }
1071
1073 "Read signature at {} for view {}",
1074 last_recovered_idx,
1075 last_sig->view);
1076 // Initial transactions, before the first signature, must have
1077 // happened in the first signature's view (eg - if the first
1078 // signature is at seqno 20 in view 4, then transactions 1->19 must
1079 // also have been in view 4). The brief justification is that while
1080 // the first node may start in an arbitrarily high view (it does not
1081 // necessarily start in view 1), it cannot _change_ view before a
1082 // valid signature.
1083 const auto view_start_idx =
1084 view_history.empty() ? 1 : last_recovered_signed_idx + 1;
1086 last_sig->view >= 0,
1087 "last_sig->view is invalid, {}",
1088 last_sig->view);
1089 for (auto i = view_history.size();
1090 i < static_cast<size_t>(last_sig->view);
1091 ++i)
1092 {
1093 view_history.push_back(view_start_idx);
1094 }
1095 last_recovered_signed_idx = last_recovered_idx;
1096 }
1097 }
1098
1099 read_ledger_entries(
1100 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
1101 }
1102
1104 {
1105 std::lock_guard<pal::Mutex> guard(lock);
1107 history->start_signature_emit_timer();
1109 }
1110
1112 {
1113 std::lock_guard<pal::Mutex> guard(lock);
1115 history->start_signature_emit_timer();
1117 reset_data(quote_info.quote);
1118 reset_data(quote_info.endorsements);
1120 }
1121
1123 {
1125
1126 // When reaching the end of the public ledger, truncate to last signed
1127 // index
1128 const auto last_recovered_term = view_history.size();
1129 auto new_term = last_recovered_term + aft::starting_view_change;
1130 LOG_INFO_FMT("Setting term on public recovery store to {}", new_term);
1131
1132 // Note: KV term must be set before the first Tx is committed
1133 network.tables->rollback(
1134 {last_recovered_term, last_recovered_signed_idx}, new_term);
1135 ledger_truncate(last_recovered_signed_idx, true);
1136 snapshotter->rollback(last_recovered_signed_idx);
1137
1139 "End of public ledger recovery - Truncating ledger to last signed "
1140 "TxID: {}.{}",
1141 last_recovered_term,
1142 last_recovered_signed_idx);
1143
1144 auto tx = network.tables->create_read_only_tx();
1145 network.ledger_secrets->init(last_recovered_signed_idx + 1);
1146
1147 // Initialise snapshotter after public recovery
1148 snapshotter->init_after_public_recovery();
1149 snapshotter->set_snapshot_generation(false);
1150
1151 ccf::kv::Version index = 0;
1152 ccf::kv::Term view = 0;
1153
1154 auto ls = tx.ro(network.signatures)->get();
1155 if (ls.has_value())
1156 {
1157 auto s = ls.value();
1158 index = s.seqno;
1159 view = s.view;
1160 }
1161 else
1162 {
1163 throw std::logic_error("No signature found after recovery");
1164 }
1165
1167 auto lcs = tx.ro(network.cose_signatures)->get();
1168 if (lcs.has_value())
1169 {
1170 CoseSignature cs = lcs.value();
1171 LOG_INFO_FMT("COSE signature found after recovery");
1172 try
1173 {
1174 auto [issuer, subject] = cose::extract_iss_sub_from_sig(cs);
1176 "COSE signature issuer: {}, subject: {}", issuer, subject);
1177 cs_cfg = ccf::COSESignaturesConfig{issuer, subject};
1178 }
1179 catch (const cose::COSEDecodeError& e)
1180 {
1181 LOG_FAIL_FMT("COSE signature decode error: {}", e.what());
1182 throw;
1183 }
1184 }
1185 else
1186 {
1187 LOG_INFO_FMT("No COSE signature found after recovery");
1188 }
1189
1190 history->set_service_signing_identity(
1191 network.identity->get_key_pair(), cs_cfg);
1192
1193 auto h = dynamic_cast<MerkleTxHistory*>(history.get());
1194 if (h)
1195 {
1196 h->set_node_id(self);
1197 }
1198
1199 auto service_config = tx.ro(network.config)->get();
1200
1201 setup_consensus(
1204 true);
1206
1207 LOG_DEBUG_FMT("Restarting consensus at view: {} seqno: {}", view, index);
1208
1209 consensus->force_become_primary(index, view, view_history, index);
1210
1211 create_and_send_boot_request(
1212 new_term, false /* Restore consortium from ledger */);
1213 }
1214
1215 //
1216 // funcs in state "readingPrivateLedger"
1217 //
1218 void recover_private_ledger_entries(const std::vector<uint8_t>& entries)
1219 {
1220 std::lock_guard<pal::Mutex> guard(lock);
1222 {
1224 "Node in state {} cannot recover private ledger entries", sm.value());
1225 return;
1226 }
1227
1228 auto data = entries.data();
1229 auto size = entries.size();
1230
1231 if (size == 0)
1232 {
1234 return;
1235 }
1236
1237 while (size > 0)
1238 {
1239 auto entry = ::consensus::LedgerEnclave::get_entry(data, size);
1240
1242 "Deserialising private ledger entry {} [{}]",
1243 last_recovered_idx + 1,
1244 entry.size());
1245
1246 // When reading the private ledger, deserialise in the recovery store
1248 try
1249 {
1250 result = recovery_store->deserialize(entry)->apply();
1251 if (result == ccf::kv::ApplyResult::FAIL)
1252 {
1254 "Failed to deserialise private ledger entry: {}", result);
1255 // Note: rollback terms do not matter here as recovery store is
1256 // about to be discarded
1257 recovery_store->rollback({0, last_recovered_idx}, 0);
1259 return;
1260 }
1261 ++last_recovered_idx;
1262 }
1263 catch (const std::exception& e)
1264 {
1266 "Failed to deserialise private ledger entry: {}", e.what());
1268 return;
1269 }
1270
1272 {
1273 recovery_store->compact(last_recovered_idx);
1274 }
1275 }
1276
1277 if (recovery_store->current_version() == recovery_v)
1278 {
1279 LOG_INFO_FMT("Reached recovery final version at {}", recovery_v);
1281 }
1282 else
1283 {
1284 read_ledger_entries(
1285 last_recovered_idx + 1,
1286 std::min(last_recovered_idx + recovery_batch_size, recovery_v));
1287 }
1288 }
1289
1291 {
1292 // When reaching the end of the private ledger, make sure the same
1293 // ledger has been read and swap in private state
1294
1296
1298 "Try end private recovery at {}. Is primary: {}",
1299 recovery_v,
1300 consensus->is_primary());
1301
1302 if (recovery_v != recovery_store->current_version())
1303 {
1304 throw std::logic_error(fmt::format(
1305 "Private recovery did not reach public ledger seqno: {}/{}",
1306 recovery_store->current_version(),
1307 recovery_v));
1308 }
1309
1310 auto h =
1311 dynamic_cast<MerkleTxHistory*>(recovery_store->get_history().get());
1312 if (h->get_replicated_state_root() != recovery_root)
1313 {
1314 throw std::logic_error(fmt::format(
1315 "Root of public store does not match root of private store at {}",
1316 recovery_v));
1317 }
1318
1319 network.tables->swap_private_maps(*recovery_store.get());
1320 recovery_store.reset();
1321
1322 // Raft should deserialise all security domains when network is opened
1323 consensus->enable_all_domains();
1324
1325 // Snapshots are only generated after recovery is complete
1326 snapshotter->set_snapshot_generation(true);
1327
1328 // Open the service
1329 if (consensus->can_replicate())
1330 {
1332 "Try end private recovery at {}. Trigger service opening",
1333 recovery_v);
1334
1335 auto tx = network.tables->create_tx();
1336
1337 {
1338 // Ensure this transition happens at-most-once, by checking that no
1339 // other node has already advanced the state
1340 auto service = tx.ro<ccf::Service>(Tables::SERVICE);
1341 auto active_service = service->get();
1342
1343 if (!active_service.has_value())
1344 {
1345 throw std::logic_error(fmt::format(
1346 "Error in {}: no value in {}", __func__, Tables::SERVICE));
1347 }
1348
1349 if (
1350 active_service->status !=
1352 {
1353 throw std::logic_error(fmt::format(
1354 "Error in {}: current service status is {}",
1355 __func__,
1356 active_service->status));
1357 }
1358 }
1359
1360 // Clear recovery shares that were submitted to initiate the recovery
1361 // procedure
1363
1364 // Shares for the new ledger secret can only be issued now, once the
1365 // previous ledger secrets have been recovered
1366 share_manager.issue_recovery_shares(tx);
1367
1368 if (
1371 tx, *network.identity->get_key_pair()))
1372 {
1373 throw std::logic_error("Service could not be opened");
1374 }
1375
1376 // Trigger a snapshot (at next signature) to ensure we have a working
1377 // snapshot signed by the current (now new) service identity, in case
1378 // we need to recover soon again.
1379 trigger_snapshot(tx);
1380
1381 if (tx.commit() != ccf::kv::CommitResult::SUCCESS)
1382 {
1383 throw std::logic_error(
1384 "Could not commit transaction when finishing network recovery");
1385 }
1386 }
1387 recovered_encrypted_ledger_secrets.clear();
1388 reset_data(quote_info.quote);
1389 reset_data(quote_info.endorsements);
1391 }
1392
1394 {
1395 // This hook is necessary to adjust the version at which the last ledger
1396 // secret before recovery is recorded in the store. This can only be
1397 // fired once, after the recovery shares for the post-recovery ledger
1398 // secret are issued.
1399 network.tables->set_map_hook(
1402 [this](
1403 ccf::kv::Version version,
1406 if (!w.has_value())
1407 {
1408 throw std::logic_error(fmt::format(
1409 "Unexpected removal from {} table",
1410 network.encrypted_ledger_secrets.get_name()));
1411 }
1412
1413 network.ledger_secrets->adjust_previous_secret_stored_version(
1414 version);
1415
1416 network.tables->unset_map_hook(
1418
1419 return ccf::kv::ConsensusHookPtr(nullptr);
1420 }));
1421 }
1422
1423 //
1424 // funcs in state "readingPublicLedger" or "readingPrivateLedger"
1425 //
1427 {
1428 std::lock_guard<pal::Mutex> guard(lock);
1429
1430 if (is_reading_public_ledger())
1431 {
1432 recover_public_ledger_end_unsafe();
1433 }
1434 else if (is_reading_private_ledger())
1435 {
1436 recover_private_ledger_end_unsafe();
1437 }
1438 else
1439 {
1441 "Node in state {} cannot finalise ledger recovery", sm.value());
1442 return;
1443 }
1444 }
1445
1446 //
1447 // funcs in state "partOfPublicNetwork"
1448 //
1450 {
1451 recovery_store = std::make_shared<ccf::kv::Store>(
1452 true /* Check transactions in order */,
1453 true /* Make use of historical secrets */);
1454 auto recovery_history = std::make_shared<MerkleTxHistory>(
1455 *recovery_store.get(),
1456 self,
1457 *node_sign_kp,
1458 sig_tx_interval,
1459 sig_ms_interval,
1460 false /* No signature timer on recovery_history */);
1461
1462 auto recovery_encryptor = make_encryptor();
1463
1464 recovery_store->set_history(recovery_history);
1465 recovery_store->set_encryptor(recovery_encryptor);
1466
1467 // Record real store version and root
1468 recovery_v = network.tables->current_version();
1469 auto h = dynamic_cast<MerkleTxHistory*>(history.get());
1470 recovery_root = h->get_replicated_state_root();
1471
1472 if (startup_snapshot_info)
1473 {
1474 std::vector<ccf::kv::Version> view_history_;
1476 deserialise_snapshot(
1477 recovery_store,
1478 startup_snapshot_info->raw,
1479 hooks,
1480 &view_history_,
1481 false,
1482 config.recover.previous_service_identity);
1483 startup_snapshot_info.reset();
1484 }
1485
1487 "Recovery store successfully setup at {}. Target recovery seqno: {}",
1488 recovery_store->current_version(),
1489 recovery_v);
1490 }
1491
1493 {
1494 share_manager.shuffle_recovery_shares(tx);
1495 }
1496
1498 {
1499 auto tx_ = static_cast<ccf::kv::CommittableTx*>(&tx);
1500 if (tx_ == nullptr)
1501 {
1502 throw std::logic_error("Could not cast tx to CommittableTx");
1503 }
1504 tx_->set_tx_flag(
1506 }
1507
1509 {
1510 auto committable_tx = static_cast<ccf::kv::CommittableTx*>(&tx);
1511 if (committable_tx == nullptr)
1512 {
1513 throw std::logic_error("Could not cast tx to CommittableTx");
1514 }
1515 committable_tx->set_tx_flag(
1517 }
1518
1520 ccf::kv::Tx& tx,
1521 const std::optional<std::vector<std::string>>& interfaces =
1522 std::nullopt) override
1523 {
1524 if (!network.identity)
1525 {
1526 return;
1527 }
1528
1529 num_acme_interfaces = 0;
1530
1531 for (const auto& [iname, interface] : config.network.rpc_interfaces)
1532 {
1533 if (
1534 !interface.endorsement ||
1535 interface.endorsement->authority != Authority::ACME ||
1536 !interface.endorsement->acme_configuration)
1537 {
1538 continue;
1539 }
1540
1541 num_acme_interfaces++;
1542
1543 if (
1544 !interfaces ||
1545 std::find(interfaces->begin(), interfaces->end(), iname) !=
1546 interfaces->end())
1547 {
1548 auto challenge_frontend = find_acme_challenge_frontend();
1549
1550 const std::string& cfg_name =
1551 *interface.endorsement->acme_configuration;
1552 auto cit = config.network.acme->configurations.find(cfg_name);
1553 if (cit == config.network.acme->configurations.end())
1554 {
1555 LOG_INFO_FMT("Unknown ACME configuration '{}'", cfg_name);
1556 continue;
1557 }
1558
1559 if (
1560 !cit->second.directory_url.empty() &&
1561 acme_clients.find(cfg_name) == acme_clients.end())
1562 {
1563 const auto& cfg = cit->second;
1564
1565 auto client = std::make_shared<ACMEClient>(
1566 cfg_name,
1567 cfg,
1568 rpc_map,
1569 rpcsessions,
1570 challenge_frontend,
1571 network.tables,
1572 node_sign_kp);
1573
1574 auto chit = acme_challenge_handlers.find(iname);
1575 if (chit != acme_challenge_handlers.end())
1576 {
1577 client->install_custom_challenge_handler(chit->second);
1578 }
1579
1580 acme_clients.emplace(cfg_name, client);
1581 }
1582
1583 auto client = acme_clients[cfg_name];
1584 if (client && !client->has_active_orders())
1585 {
1586 client->get_certificate(
1587 make_key_pair(network.identity->priv_key), true);
1588 }
1589 }
1590 }
1591 }
1592
1594 const std::vector<std::string>& args,
1595 const std::vector<uint8_t>& input) override
1596 {
1597 HostProcessArguments msg{args};
1598 nlohmann::json j = msg;
1599 auto json = j.dump();
1601 "Triggering host process launch: {} size={}", json, input.size());
1603 AppMessage::launch_host_process, to_host, json, input);
1604 }
1605
1607 ccf::kv::Tx& tx,
1609 {
1610 std::lock_guard<pal::Mutex> guard(lock);
1611
1612 auto service = tx.rw<Service>(Tables::SERVICE);
1613 auto service_info = service->get();
1614 if (!service_info.has_value())
1615 {
1616 throw std::logic_error(
1617 "Service information cannot be found to transition service to "
1618 "open");
1619 }
1620
1621 // Idempotence: if the service is already open or waiting for recovery
1622 // shares, this function should succeed with no effect
1623 if (
1624 service_info->status == ServiceStatus::WAITING_FOR_RECOVERY_SHARES ||
1625 service_info->status == ServiceStatus::OPEN)
1626 {
1628 "Service in state {} is already open", service_info->status);
1629 return;
1630 }
1631
1632 if (service_info->status == ServiceStatus::RECOVERING)
1633 {
1634 const auto prev_ident =
1635 tx.ro<PreviousServiceIdentity>(Tables::PREVIOUS_SERVICE_IDENTITY)
1636 ->get();
1637 if (!prev_ident.has_value() || !identities.previous.has_value())
1638 {
1639 throw std::logic_error(
1640 "Recovery with service certificates requires both, a previous "
1641 "service identity written to the KV during recovery genesis and a "
1642 "transition_service_to_open proposal that contains previous and "
1643 "next service certificates");
1644 }
1645
1646 const ccf::crypto::Pem from_proposal(
1647 identities.previous->data(), identities.previous->size());
1648 if (prev_ident.value() != from_proposal)
1649 {
1650 throw std::logic_error(fmt::format(
1651 "Previous service identity does not match.\nActual:\n{}\nIn "
1652 "proposal:\n{}",
1653 prev_ident->str(),
1654 from_proposal.str()));
1655 }
1656 }
1657
1658 if (identities.next != service_info->cert)
1659 {
1660 throw std::logic_error(fmt::format(
1661 "Service identity mismatch: the next service identity in the "
1662 "transition_service_to_open proposal does not match the current "
1663 "service identity:\nNext:\n{}\nCurrent:\n{}",
1664 identities.next.str(),
1665 service_info->cert.str()));
1666 }
1667
1668 service_info->previous_service_identity_version =
1669 service->get_version_of_previous_write();
1670
1671 if (is_part_of_public_network())
1672 {
1673 // If the node is in public mode, start accepting member recovery
1674 // shares
1676 service_info->status = ServiceStatus::WAITING_FOR_RECOVERY_SHARES;
1677 service->put(service_info.value());
1678 if (config.recover.previous_sealed_ledger_secret_location.has_value())
1679 {
1680 tx.wo<LastRecoveryType>(Tables::LAST_RECOVERY_TYPE)
1682 auto unsealed_ls = unseal_ledger_secret();
1683 LOG_INFO_FMT("Unsealed ledger secret, initiating private recovery");
1684 initiate_private_recovery_unsafe(tx, unsealed_ls);
1685 }
1686 else
1687 {
1688 tx.wo<LastRecoveryType>(Tables::LAST_RECOVERY_TYPE)
1690 }
1691 return;
1692 }
1693 else if (is_part_of_network())
1694 {
1695 // Otherwise, if the node is part of the network. Open the network
1696 // straight away. Recovery shares are allocated to each recovery
1697 // member.
1698 try
1699 {
1700 share_manager.issue_recovery_shares(tx);
1701 }
1702 catch (const std::logic_error& e)
1703 {
1704 throw std::logic_error(
1705 fmt::format("Failed to issue recovery shares: {}", e.what()));
1706 }
1707
1710 tx, *network.identity->get_key_pair());
1711 trigger_snapshot(tx);
1712 return;
1713 }
1714 else
1715 {
1716 throw std::logic_error(
1717 fmt::format("Node in state {} cannot open service", sm.value()));
1718 }
1719 }
1720
1721 // Decrypts chain of ledger secrets, and writes those to the ledger
1722 // encrypted for each node. On a commit hook for this write, each node
1723 // (including this one!) will begin_private_recovery().
1725 ccf::kv::Tx& tx,
1726 const std::optional<LedgerSecretPtr>& unsealed_ledger_secret =
1727 std::nullopt)
1728 {
1730 LedgerSecretsMap recovered_ledger_secrets =
1731 share_manager.restore_recovery_shares_info(
1732 tx, recovered_encrypted_ledger_secrets, unsealed_ledger_secret);
1733
1734 // Broadcast decrypted ledger secrets to other nodes for them to
1735 // initiate private recovery too
1738 tx.wo(network.secrets),
1739 std::move(recovered_ledger_secrets));
1740 }
1741
1743 ccf::kv::Tx& tx,
1744 const std::optional<LedgerSecretPtr>& unsealed_ledger_secret =
1745 std::nullopt) override
1746 {
1747 std::lock_guard<pal::Mutex> guard(lock);
1748 initiate_private_recovery_unsafe(tx, unsealed_ledger_secret);
1749 }
1750
1751 //
1752 // funcs in state "partOfNetwork" or "partOfPublicNetwork"
1753 //
1754 void tick(std::chrono::milliseconds elapsed)
1755 {
1756 if (
1760 {
1761 return;
1762 }
1763
1764 consensus->periodic(elapsed);
1765
1766 if (sm.check(NodeStartupState::partOfNetwork))
1767 {
1768 const auto tx_id = consensus->get_committed_txid();
1769 indexer->update_strategies(elapsed, {tx_id.first, tx_id.second});
1770 }
1771
1772 n2n_channels->tick(elapsed);
1773 }
1774
1776 {
1777 if (
1781 {
1782 return;
1783 }
1784
1785 consensus->periodic_end();
1786 }
1787
1788 void stop_notice() override
1789 {
1790 stop_noticed = true;
1791 }
1792
1794 {
1795 return stop_noticed;
1796 }
1797
1798 void recv_node_inbound(const uint8_t* data, size_t size)
1799 {
1800 auto [msg_type, from, payload] =
1801 ringbuffer::read_message<node_inbound>(data, size);
1802
1803 auto payload_data = payload.data;
1804 auto payload_size = payload.size;
1805
1806 if (msg_type == NodeMsgType::forwarded_msg)
1807 {
1808 cmd_forwarder->recv_message(from, payload_data, payload_size);
1809 }
1810 else
1811 {
1812 // Only process messages once part of network
1813 if (
1817 {
1819 "Ignoring node msg received too early - current state is {}",
1820 sm.value());
1821 return;
1822 }
1823
1824 switch (msg_type)
1825 {
1826 case channel_msg:
1827 {
1828 n2n_channels->recv_channel_message(
1829 from, payload_data, payload_size);
1830 break;
1831 }
1832
1833 case consensus_msg:
1834 {
1835 consensus->recv_message(from, payload_data, payload_size);
1836 break;
1837 }
1838
1839 default:
1840 {
1841 LOG_FAIL_FMT("Unknown node message type: {}", msg_type);
1842 return;
1843 }
1844 }
1845 }
1846 }
1847
1848 //
1849 // always available
1850 //
1851 bool is_primary() const override
1852 {
1853 return (
1857 consensus->is_primary());
1858 }
1859
1860 bool can_replicate() override
1861 {
1862 return (
1866 consensus->can_replicate());
1867 }
1868
1869 bool is_in_initialised_state() const override
1870 {
1871 return sm.check(NodeStartupState::initialized);
1872 }
1873
1874 bool is_part_of_network() const override
1875 {
1876 return sm.check(NodeStartupState::partOfNetwork);
1877 }
1878
1879 bool is_reading_public_ledger() const override
1880 {
1882 }
1883
1884 bool is_reading_private_ledger() const override
1885 {
1887 }
1888
1889 bool is_part_of_public_network() const override
1890 {
1892 }
1893
1894 bool is_accessible_to_members() const override
1895 {
1896 const auto val = sm.value();
1897 return val == NodeStartupState::partOfNetwork ||
1900 }
1901
1903 {
1904 std::lock_guard<pal::Mutex> guard(lock);
1905 auto s = sm.value();
1907 {
1908 return {s, recovery_v, recovery_store->current_version()};
1909 }
1910 else
1911 {
1912 return {s, std::nullopt, std::nullopt};
1913 }
1914 }
1915
1916 bool rekey_ledger(ccf::kv::Tx& tx) override
1917 {
1918 std::lock_guard<pal::Mutex> guard(lock);
1920
1921 // The ledger should not be re-keyed when the service is not open
1922 // because:
1923 // - While waiting for recovery shares, the submitted shares are stored
1924 // in a public table, encrypted with the ledger secret generated at
1925 // startup of the first recovery node
1926 // - On recovery, historical ledger secrets can only be looked up in the
1927 // ledger once all ledger secrets have been restored
1928 const auto service_status = InternalTablesAccess::get_service_status(tx);
1929 if (
1930 !service_status.has_value() ||
1931 service_status.value() != ServiceStatus::OPEN)
1932 {
1933 LOG_FAIL_FMT("Cannot rekey ledger while the service is not open");
1934 return false;
1935 }
1936
1937 // Effects of ledger rekey are only observed from the next transaction,
1938 // once the local hook on the secrets table has been triggered.
1939
1940 auto new_ledger_secret = make_ledger_secret();
1941 share_manager.issue_recovery_shares(tx, new_ledger_secret);
1944 tx.wo(network.secrets),
1945 std::move(new_ledger_secret));
1946
1947 return true;
1948 }
1949
1951 {
1952 return self;
1953 }
1954
1956 {
1957 std::lock_guard<pal::Mutex> guard(lock);
1958 return startup_seqno;
1959 }
1960
1962 {
1963 return rpcsessions->get_session_metrics();
1964 }
1965
1967 {
1968 std::lock_guard<pal::Mutex> guard(lock);
1969 return self_signed_node_cert;
1970 }
1971
1973 {
1974 if (history == nullptr)
1975 {
1976 throw std::logic_error(
1977 "Attempting to access COSE signatures config before history has been "
1978 "constructed");
1979 }
1980
1981 return history->get_cose_signatures_config();
1982 }
1983
1984 private:
1985 bool is_ip(const std::string_view& hostname)
1986 {
1987 // IP address components are purely numeric. DNS names may be largely
1988 // numeric, but at least the final component (TLD) must not be
1989 // all-numeric. So this distinguishes "1.2.3.4" (an IP address) from
1990 // "1.2.3.c4m" (a DNS name). "1.2.3." is invalid for either, and will
1991 // throw. Attempts to handle IPv6 by also splitting on ':', but this is
1992 // untested.
1993 const auto final_component =
1994 ccf::nonstd::split(ccf::nonstd::split(hostname, ".").back(), ":")
1995 .back();
1996 if (final_component.empty())
1997 {
1998 throw std::runtime_error(fmt::format(
1999 "{} has a trailing period, is not a valid hostname", hostname));
2000 }
2001 for (const auto c : final_component)
2002 {
2003 if (c < '0' || c > '9')
2004 {
2005 return false;
2006 }
2007 }
2008
2009 return true;
2010 }
2011
2012 std::vector<ccf::crypto::SubjectAltName> get_subject_alternative_names()
2013 {
2014 // If no Subject Alternative Name (SAN) is passed in at node creation,
2015 // default to using node's RPC address as single SAN. Otherwise, use
2016 // specified SANs.
2017 if (!config.node_certificate.subject_alt_names.empty())
2018 {
2019 return ccf::crypto::sans_from_string_list(
2020 config.node_certificate.subject_alt_names);
2021 }
2022 else
2023 {
2024 // Construct SANs from RPC interfaces, manually detecting whether each
2025 // is a domain name or IP
2026 std::vector<ccf::crypto::SubjectAltName> sans;
2027 for (const auto& [_, interface] : config.network.rpc_interfaces)
2028 {
2029 auto host = split_net_address(interface.published_address).first;
2030 sans.push_back({host, is_ip(host)});
2031 }
2032 return sans;
2033 }
2034 }
2035
2036 void accept_node_tls_connections()
2037 {
2038 // Accept TLS connections, presenting self-signed (i.e. non-endorsed)
2039 // node certificate.
2040 rpcsessions->set_node_cert(
2041 self_signed_node_cert, node_sign_kp->private_key_pem());
2042 LOG_INFO_FMT("Node TLS connections now accepted");
2043 }
2044
2045 void accept_network_tls_connections()
2046 {
2047 // Accept TLS connections, presenting node certificate signed by network
2048 // certificate
2050 endorsed_node_cert.has_value(),
2051 "Node certificate should be endorsed before accepting endorsed "
2052 "client "
2053 "connections");
2054 rpcsessions->set_network_cert(
2055 endorsed_node_cert.value(), node_sign_kp->private_key_pem());
2056 LOG_INFO_FMT("Network TLS connections now accepted");
2057 }
2058
2059 auto find_frontend(ActorsType actor)
2060 {
2061 auto fe = rpc_map->find(actor);
2062 if (!fe.has_value())
2063 {
2064 throw std::logic_error(
2065 fmt::format("Cannot find {} frontend", (int)actor));
2066 }
2067 return fe.value();
2068 }
2069
2070 void open_frontend(ActorsType actor)
2071 {
2072 find_frontend(actor)->open();
2073 }
2074
2075 void open_user_frontend()
2076 {
2077 open_frontend(ActorsType::users);
2078 }
2079
2080 bool is_member_frontend_open_unsafe()
2081 {
2082 return find_frontend(ActorsType::members)->is_open();
2083 }
2084
2085 bool is_member_frontend_open() override
2086 {
2087 std::lock_guard<pal::Mutex> guard(lock);
2088 return is_member_frontend_open_unsafe();
2089 }
2090
2091 bool is_user_frontend_open() override
2092 {
2093 std::lock_guard<pal::Mutex> guard(lock);
2094 return find_frontend(ActorsType::users)->is_open();
2095 }
2096
2097 std::shared_ptr<ACMERpcFrontend> find_acme_challenge_frontend()
2098 {
2099 auto acme_challenge_opt = rpc_map->find(ActorsType::acme_challenge);
2100 if (!acme_challenge_opt)
2101 {
2102 throw std::runtime_error("Missing ACME challenge frontend");
2103 }
2104 return std::static_pointer_cast<ACMERpcFrontend>(*acme_challenge_opt);
2105 }
2106
2107 void open_acme_challenge_frontend()
2108 {
2109 if (config.network.acme && !config.network.acme->configurations.empty())
2110 {
2111 auto fe = find_frontend(ActorsType::acme_challenge);
2112 if (fe)
2113 {
2114 fe->open();
2115 }
2116 }
2117 }
2118
2119 std::vector<uint8_t> serialize_create_request(
2120 View create_view, bool create_consortium = true)
2121 {
2122 CreateNetworkNodeToNode::In create_params;
2123
2124 // False on recovery where the consortium is read from the existing
2125 // ledger
2126 if (create_consortium)
2127 {
2128 create_params.genesis_info = config.start;
2129 }
2130
2131 create_params.node_id = self;
2132 create_params.certificate_signing_request = node_sign_kp->create_csr(
2133 config.node_certificate.subject_name, subject_alt_names);
2134 create_params.node_endorsed_certificate =
2135 ccf::crypto::create_endorsed_cert(
2136 create_params.certificate_signing_request,
2137 config.startup_host_time,
2138 config.node_certificate.initial_validity_days,
2139 network.identity->priv_key,
2140 network.identity->cert);
2141
2142 // Even though endorsed certificate is updated on (global) hook, history
2143 // requires it to generate signatures
2144 history->set_endorsed_certificate(
2145 create_params.node_endorsed_certificate);
2146
2147 create_params.public_key = node_sign_kp->public_key_pem();
2148 create_params.service_cert = network.identity->cert;
2149 create_params.quote_info = quote_info;
2150 create_params.public_encryption_key = node_encrypt_kp->public_key_pem();
2151 create_params.measurement = node_measurement;
2152 create_params.snp_uvm_endorsements = snp_uvm_endorsements;
2153 create_params.snp_security_policy =
2154 config.attestation.environment.security_policy;
2155
2156 create_params.node_info_network = config.network;
2157 create_params.node_data = config.node_data;
2158 create_params.service_data = config.service_data;
2159 create_params.create_txid = {create_view, last_recovered_signed_idx + 1};
2160
2161 const auto body = nlohmann::json(create_params).dump();
2162
2163 ::http::Request request(
2164 fmt::format("/{}/{}", get_actor_prefix(ActorsType::nodes), "create"));
2165 request.set_header(
2166 ccf::http::headers::CONTENT_TYPE,
2167 ccf::http::headervalues::contenttype::JSON);
2168
2169 request.set_body(body);
2170
2171 return request.build_request();
2172 }
2173
2174 bool extract_create_result(const std::shared_ptr<RpcContext>& ctx)
2175 {
2176 if (ctx == nullptr)
2177 {
2178 LOG_FAIL_FMT("Expected non-null context");
2179 return false;
2180 }
2181
2182 const auto status = ctx->get_response_status();
2183 const auto& raw_body = ctx->get_response_body();
2184 if (status != HTTP_STATUS_OK)
2185 {
2187 "Create response is error: {} {}\n{}",
2188 status,
2189 ccf::http_status_str((ccf::http_status)status),
2190 std::string(raw_body.begin(), raw_body.end()));
2191 return false;
2192 }
2193
2194 const auto body = nlohmann::json::parse(raw_body);
2195 if (!body.is_boolean())
2196 {
2197 LOG_FAIL_FMT("Expected boolean body in create response");
2199 "Expected boolean body in create response: {}", body.dump());
2200 return false;
2201 }
2202
2203 return body;
2204 }
2205
2206 bool send_create_request(const std::vector<uint8_t>& packed)
2207 {
2208 auto node_session = std::make_shared<SessionContext>(
2209 InvalidSessionId, self_signed_node_cert.raw());
2210 auto ctx = make_rpc_context(node_session, packed);
2211
2212 std::shared_ptr<ccf::RpcHandler> search =
2213 ::http::fetch_rpc_handler(ctx, this->rpc_map);
2214
2215 search->process(ctx);
2216
2217 return extract_create_result(ctx);
2218 }
2219
2220 void create_and_send_boot_request(
2221 View create_view, bool create_consortium = true)
2222 {
2223 // Service creation transaction is asynchronous to avoid deadlocks
2224 // (e.g. https://github.com/microsoft/CCF/issues/3788)
2225 auto msg = std::make_unique<::threading::Tmsg<NodeStateMsg>>(
2226 [](std::unique_ptr<::threading::Tmsg<NodeStateMsg>> msg) {
2227 if (!msg->data.self.send_create_request(
2228 msg->data.self.serialize_create_request(
2229 msg->data.create_view, msg->data.create_consortium)))
2230 {
2231 throw std::runtime_error(
2232 "Service creation request could not be committed");
2233 }
2234 if (msg->data.create_consortium)
2235 {
2236 msg->data.self.advance_part_of_network();
2237 }
2238 else
2239 {
2240 msg->data.self.advance_part_of_public_network();
2241 }
2242 },
2243 *this,
2244 create_view,
2245 create_consortium);
2246
2248 threading::get_current_thread_id(), std::move(msg));
2249 }
2250
2251 void begin_private_recovery()
2252 {
2254
2255 LOG_INFO_FMT("Beginning private recovery");
2256
2257 setup_private_recovery_store();
2258
2259 reset_recovery_hook();
2260 setup_one_off_secret_hook();
2261
2262 // Start reading private security domain of ledger
2264 last_recovered_idx = recovery_store->current_version();
2265 read_ledger_entries(
2266 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
2267 }
2268
2269 void setup_basic_hooks()
2270 {
2271 network.tables->set_map_hook(
2272 network.secrets.get_name(),
2273 network.secrets.wrap_map_hook(
2274 [this](ccf::kv::Version hook_version, const Secrets::Write& w)
2276 // Used to rekey the ledger on a live service
2277 if (!is_part_of_network())
2278 {
2279 // Ledger rekey is not allowed during recovery
2280 return ccf::kv::ConsensusHookPtr(nullptr);
2281 }
2282
2283 const auto& ledger_secrets_for_nodes = w;
2284 if (!ledger_secrets_for_nodes.has_value())
2285 {
2286 throw std::logic_error(fmt::format(
2287 "Unexpected removal from {} table",
2288 network.secrets.get_name()));
2289 }
2290
2291 for (const auto& [node_id, encrypted_ledger_secrets] :
2292 ledger_secrets_for_nodes.value())
2293 {
2294 if (node_id != self)
2295 {
2296 // Only consider ledger secrets for this node
2297 continue;
2298 }
2299
2300 for (const auto& encrypted_ledger_secret :
2301 encrypted_ledger_secrets)
2302 {
2303 auto plain_ledger_secret = LedgerSecretsBroadcast::decrypt(
2304 node_encrypt_kp, encrypted_ledger_secret.encrypted_secret);
2305
2306 // When rekeying, set the encryption key for the next version
2307 // onward (backups deserialise this transaction with the
2308 // previous ledger secret)
2309 auto ledger_secret = std::make_shared<LedgerSecret>(
2310 std::move(plain_ledger_secret), hook_version);
2311 seal_ledger_secret(hook_version + 1, ledger_secret);
2312 network.ledger_secrets->set_secret(
2313 hook_version + 1, std::move(ledger_secret));
2314 }
2315 }
2316
2317 return ccf::kv::ConsensusHookPtr(nullptr);
2318 }));
2319
2320 network.tables->set_global_hook(
2321 network.secrets.get_name(),
2322 network.secrets.wrap_commit_hook([this](
2323 ccf::kv::Version hook_version,
2324 const Secrets::Write& w) {
2325 // Used on recovery to initiate private recovery on backup nodes.
2326 if (!is_part_of_public_network())
2327 {
2328 return;
2329 }
2330
2331 const auto& ledger_secrets_for_nodes = w;
2332 if (!ledger_secrets_for_nodes.has_value())
2333 {
2334 throw std::logic_error(fmt::format(
2335 "Unexpected removal from {} table", network.secrets.get_name()));
2336 }
2337
2338 for (const auto& [node_id, encrypted_ledger_secrets] :
2339 ledger_secrets_for_nodes.value())
2340 {
2341 if (node_id != self)
2342 {
2343 // Only consider ledger secrets for this node
2344 continue;
2345 }
2346
2347 LedgerSecretsMap restored_ledger_secrets = {};
2348 for (const auto& encrypted_ledger_secret : encrypted_ledger_secrets)
2349 {
2350 // On rekey, the version is inferred from the version at which
2351 // the hook is executed. Otherwise, on recovery, use the
2352 // version read from the write set.
2353 if (!encrypted_ledger_secret.version.has_value())
2354 {
2355 throw std::logic_error(fmt::format(
2356 "Commit hook at seqno {} for table {}: no version for "
2357 "encrypted ledger secret",
2358 hook_version,
2359 network.secrets.get_name()));
2360 }
2361
2362 auto plain_ledger_secret = LedgerSecretsBroadcast::decrypt(
2363 node_encrypt_kp, encrypted_ledger_secret.encrypted_secret);
2364
2365 restored_ledger_secrets.emplace(
2366 encrypted_ledger_secret.version.value(),
2367 std::make_shared<LedgerSecret>(
2368 std::move(plain_ledger_secret),
2369 encrypted_ledger_secret.previous_secret_stored_version));
2370 }
2371
2372 if (!restored_ledger_secrets.empty())
2373 {
2374 // When recovering, restore ledger secrets and trigger end of
2375 // recovery protocol (backup only)
2376 network.ledger_secrets->restore_historical(
2377 std::move(restored_ledger_secrets));
2378 begin_private_recovery();
2379 return;
2380 }
2381 }
2382
2384 "Found no ledger secrets for this node ({}) in global commit hook "
2385 "for {} @ {}",
2386 self,
2387 network.secrets.get_name(),
2388 hook_version);
2389 }));
2390
2391 network.tables->set_global_hook(
2392 network.nodes.get_name(),
2393 network.nodes.wrap_commit_hook(
2394 [this](ccf::kv::Version hook_version, const Nodes::Write& w) {
2395 std::vector<NodeId> retired_committed_nodes;
2396 for (const auto& [node_id, node_info] : w)
2397 {
2398 if (node_info.has_value() && node_info->retired_committed)
2399 {
2400 retired_committed_nodes.push_back(node_id);
2401 }
2402 }
2403 consensus->set_retired_committed(
2404 hook_version, retired_committed_nodes);
2405 }));
2406
2407 // Service-endorsed certificate is passed to history as early as _local_
2408 // commit since a new node may become primary (and thus, e.g. generate
2409 // signatures) before the transaction that added it is _globally_
2410 // committed (see https://github.com/microsoft/CCF/issues/4063). It is OK
2411 // if this transaction is rolled back as the node will no longer be part
2412 // of the service.
2413 network.tables->set_map_hook(
2414 network.node_endorsed_certificates.get_name(),
2415 network.node_endorsed_certificates.wrap_map_hook(
2416 [this](
2417 ccf::kv::Version hook_version,
2421 "[local] node_endorsed_certificates local hook at version {}, "
2422 "with {} writes",
2423 hook_version,
2424 w.size());
2425 for (auto const& [node_id, endorsed_certificate] : w)
2426 {
2427 if (node_id != self)
2428 {
2430 "[local] Ignoring endorsed certificate for other node {}",
2431 node_id);
2432 continue;
2433 }
2434
2435 if (!endorsed_certificate.has_value())
2436 {
2438 "[local] Endorsed cert for self ({}) has been deleted", self);
2439 throw std::logic_error(fmt::format(
2440 "Could not find endorsed node certificate for {}", self));
2441 }
2442
2443 std::lock_guard<pal::Mutex> guard(lock);
2444
2445 if (endorsed_node_cert.has_value())
2446 {
2448 "[local] Previous endorsed node cert was:\n{}",
2449 endorsed_node_cert->str());
2450 }
2451
2452 endorsed_node_cert = endorsed_certificate.value();
2454 "[local] Under lock, setting endorsed node cert to:\n{}",
2455 endorsed_node_cert->str());
2456 history->set_endorsed_certificate(endorsed_node_cert.value());
2457 n2n_channels->set_endorsed_node_cert(endorsed_node_cert.value());
2458 }
2459
2460 return ccf::kv::ConsensusHookPtr(nullptr);
2461 }));
2462
2463 network.tables->set_global_hook(
2464 network.node_endorsed_certificates.get_name(),
2465 network.node_endorsed_certificates.wrap_commit_hook(
2466 [this](
2467 ccf::kv::Version hook_version,
2470 "[global] node_endorsed_certificates global hook at version {}, "
2471 "with {} writes",
2472 hook_version,
2473 w.size());
2474 for (auto const& [node_id, endorsed_certificate] : w)
2475 {
2476 if (node_id != self)
2477 {
2479 "[global] Ignoring endorsed certificate for other node {}",
2480 node_id);
2481 continue;
2482 }
2483
2484 if (!endorsed_certificate.has_value())
2485 {
2487 "[global] Endorsed cert for self ({}) has been deleted",
2488 self);
2489 throw std::logic_error(fmt::format(
2490 "Could not find endorsed node certificate for {}", self));
2491 }
2492
2493 std::lock_guard<pal::Mutex> guard(lock);
2494
2495 LOG_INFO_FMT("[global] Accepting network connections");
2496 accept_network_tls_connections();
2497
2498 if (is_member_frontend_open_unsafe())
2499 {
2500 // Also, automatically refresh self-signed node certificate,
2501 // using the same validity period as the endorsed certificate.
2502 // Note that this is only done when the certificate is renewed
2503 // via proposal (i.e. when the member frontend is open), and not
2504 // for the initial addition of the node (the self-signed
2505 // certificate is output to disk then).
2506 auto [valid_from, valid_to] =
2507 ccf::crypto::make_verifier(endorsed_node_cert.value())
2508 ->validity_period();
2510 "[global] Member frontend is open, so refreshing self-signed "
2511 "node cert");
2513 "[global] Previously:\n{}", self_signed_node_cert.str());
2514 self_signed_node_cert = create_self_signed_cert(
2515 node_sign_kp,
2516 config.node_certificate.subject_name,
2517 subject_alt_names,
2518 valid_from,
2519 valid_to);
2520 LOG_INFO_FMT("[global] Now:\n{}", self_signed_node_cert.str());
2521
2522 LOG_INFO_FMT("[global] Accepting node connections");
2523 accept_node_tls_connections();
2524 }
2525 else
2526 {
2527 LOG_INFO_FMT("[global] Member frontend is NOT open");
2529 "[global] Self-signed node cert remains:\n{}",
2530 self_signed_node_cert.str());
2531 }
2532
2533 LOG_INFO_FMT("[global] Opening members frontend");
2534 open_frontend(ActorsType::members);
2535 }
2536 }));
2537
2538 network.tables->set_global_hook(
2539 network.service.get_name(),
2540 network.service.wrap_commit_hook(
2541 [this](ccf::kv::Version hook_version, const Service::Write& w) {
2542 if (!w.has_value())
2543 {
2544 throw std::logic_error("Unexpected deletion in service value");
2545 }
2546
2547 // Service open on historical service has no effect
2548 auto hook_pubk_pem = ccf::crypto::public_key_pem_from_cert(
2550 auto current_pubk_pem =
2551 ccf::crypto::make_key_pair(network.identity->priv_key)
2552 ->public_key_pem();
2553 if (hook_pubk_pem != current_pubk_pem)
2554 {
2556 "Ignoring historical service open at seqno {} for {}",
2557 hook_version,
2558 w->cert.str());
2559 return;
2560 }
2561
2563 "Executing global hook for service table at {}, to service "
2564 "status {}. Cert is:\n{}",
2565 hook_version,
2566 w->status,
2567 w->cert.str());
2568
2569 network.identity->set_certificate(w->cert);
2570 if (w->status == ServiceStatus::OPEN)
2571 {
2572 open_user_frontend();
2573
2574 RINGBUFFER_WRITE_MESSAGE(::consensus::ledger_open, to_host);
2575 LOG_INFO_FMT("Service open at seqno {}", hook_version);
2576 }
2577 }));
2578
2579 network.tables->set_global_hook(
2580 network.acme_certificates.get_name(),
2581 network.acme_certificates.wrap_commit_hook(
2582 [this](
2583 ccf::kv::Version hook_version, const ACMECertificates::Write& w) {
2584 for (auto const& [interface_id, interface] :
2585 config.network.rpc_interfaces)
2586 {
2587 if (interface.endorsement->acme_configuration)
2588 {
2589 auto cit = w.find(*interface.endorsement->acme_configuration);
2590 if (cit != w.end())
2591 {
2593 "ACME: new certificate for interface '{}' with "
2594 "configuration '{}'",
2595 interface_id,
2596 *interface.endorsement->acme_configuration);
2597 rpcsessions->set_cert(
2599 *cit->second,
2600 network.identity->priv_key,
2601 cit->first);
2602 }
2603 }
2604 }
2605 }));
2606 }
2607
2608 ccf::kv::Version get_last_recovered_signed_idx() override
2609 {
2610 // On recovery, only one node recovers the public ledger and is thus
2611 // aware of the version at which the new ledger secret is applicable
2612 // from. If the primary changes while the network is public-only, the
2613 // new primary should also know at which version the new ledger secret
2614 // is applicable from.
2615 std::lock_guard<pal::Mutex> guard(lock);
2616 return last_recovered_signed_idx;
2617 }
2618
2619 void setup_recovery_hook()
2620 {
2621 network.tables->set_map_hook(
2622 network.encrypted_ledger_secrets.get_name(),
2623 network.encrypted_ledger_secrets.wrap_map_hook(
2624 [this](
2625 ccf::kv::Version version,
2628 auto encrypted_ledger_secret_info = w;
2629 if (!encrypted_ledger_secret_info.has_value())
2630 {
2631 throw std::logic_error(fmt::format(
2632 "Unexpected removal from {} table",
2633 network.encrypted_ledger_secrets.get_name()));
2634 }
2635
2636 // If the version of the next ledger secret is not set, deduce it
2637 // from the hook version (i.e. ledger rekey)
2638 if (!encrypted_ledger_secret_info->next_version.has_value())
2639 {
2640 encrypted_ledger_secret_info->next_version = version + 1;
2641 }
2642
2643 if (encrypted_ledger_secret_info->previous_ledger_secret
2644 .has_value())
2645 {
2646 LOG_DEBUG_FMT(
2647 "Recovering encrypted ledger secret valid at seqno {}",
2648 encrypted_ledger_secret_info->previous_ledger_secret->version);
2649 }
2650
2651 recovered_encrypted_ledger_secrets.emplace_back(
2652 std::move(encrypted_ledger_secret_info.value()));
2653
2654 return ccf::kv::ConsensusHookPtr(nullptr);
2655 }));
2656 }
2657
2658 void reset_recovery_hook()
2659 {
2660 network.tables->unset_map_hook(
2661 network.encrypted_ledger_secrets.get_name());
2662 }
2663
2664 void setup_n2n_channels(
2665 const std::optional<ccf::crypto::Pem>& endorsed_node_certificate_ =
2666 std::nullopt)
2667 {
2668 // If the endorsed node certificate is available at the time the
2669 // consensus/node-to-node channels are initialised, use it (i.e. join).
2670 // Otherwise, specify it later, on endorsed certificate table hook (i.e.
2671 // start or recover).
2672 n2n_channels->initialize(
2673 self, network.identity->cert, node_sign_kp, endorsed_node_certificate_);
2674 }
2675
2676 void setup_cmd_forwarder()
2677 {
2678 cmd_forwarder->initialize(self);
2679 }
2680
2681 void setup_history()
2682 {
2683 if (history)
2684 {
2685 throw std::logic_error("History already initialised");
2686 }
2687
2688 history = std::make_shared<MerkleTxHistory>(
2689 *network.tables.get(),
2690 self,
2691 *node_sign_kp,
2692 sig_tx_interval,
2693 sig_ms_interval,
2694 false /* start timed signatures after first tx */);
2695 network.tables->set_history(history);
2696 }
2697
2698 void setup_encryptor()
2699 {
2700 if (encryptor)
2701 {
2702 throw std::logic_error("Encryptor already initialised");
2703 }
2704
2705 encryptor = make_encryptor();
2706 network.tables->set_encryptor(encryptor);
2707 }
2708
2709 void setup_consensus(
2710 ServiceStatus service_status,
2711 ccf::ReconfigurationType reconfiguration_type,
2712 bool public_only = false,
2713 const std::optional<ccf::crypto::Pem>& endorsed_node_certificate_ =
2714 std::nullopt)
2715 {
2716 setup_n2n_channels(endorsed_node_certificate_);
2717 setup_cmd_forwarder();
2718
2719 auto shared_state = std::make_shared<aft::State>(self);
2720
2721 auto node_client = std::make_shared<HTTPNodeClient>(
2722 rpc_map, node_sign_kp, self_signed_node_cert, endorsed_node_cert);
2723
2724 ccf::kv::MembershipState membership_state =
2726
2727 consensus = std::make_shared<RaftType>(
2728 consensus_config,
2729 std::make_unique<aft::Adaptor<ccf::kv::Store>>(network.tables),
2730 std::make_unique<::consensus::LedgerEnclave>(writer_factory),
2731 n2n_channels,
2732 shared_state,
2733 node_client,
2734 public_only,
2735 membership_state,
2736 reconfiguration_type);
2737
2738 network.tables->set_consensus(consensus);
2739 network.tables->set_snapshotter(snapshotter);
2740
2741 // When a node is added, even locally, inform consensus so that it
2742 // can add a new active configuration.
2743 network.tables->set_map_hook(
2744 network.nodes.get_name(),
2745 network.nodes.wrap_map_hook(
2746 [](ccf::kv::Version version, const Nodes::Write& w)
2748 return std::make_unique<ConfigurationChangeHook>(version, w);
2749 }));
2750
2751 // Note: The Signatures hook and SerialisedMerkleTree hook are separate
2752 // because the signature and the Merkle tree are recorded in distinct
2753 // tables (for serialisation performance reasons). However here, they are
2754 // expected to always be called together and for the same version as they
2755 // are always written by each signature transaction.
2756
2757 network.tables->set_map_hook(
2758 network.signatures.get_name(),
2759 network.signatures.wrap_map_hook(
2760 [s = this->snapshotter](
2761 ccf::kv::Version version, const Signatures::Write& w) {
2762 assert(w.has_value());
2763 auto sig = w.value();
2764 s->record_signature(version, sig.sig, sig.node, sig.cert);
2765 return ccf::kv::ConsensusHookPtr(nullptr);
2766 }));
2767
2768 network.tables->set_map_hook(
2769 network.serialise_tree.get_name(),
2770 network.serialise_tree.wrap_map_hook(
2771 [s = this->snapshotter](
2773 assert(w.has_value());
2774 auto tree = w.value();
2775 s->record_serialised_tree(version, tree);
2776 return ccf::kv::ConsensusHookPtr(nullptr);
2777 }));
2778
2779 network.tables->set_map_hook(
2780 network.snapshot_evidence.get_name(),
2781 network.snapshot_evidence.wrap_map_hook(
2782 [s = this->snapshotter](
2783 ccf::kv::Version version, const SnapshotEvidence::Write& w) {
2784 assert(w.has_value());
2785 auto snapshot_evidence = w.value();
2786 s->record_snapshot_evidence_idx(version, snapshot_evidence);
2787 return ccf::kv::ConsensusHookPtr(nullptr);
2788 }));
2789
2790 setup_basic_hooks();
2791 }
2792
2793 void setup_snapshotter()
2794 {
2795 if (snapshotter)
2796 {
2797 throw std::logic_error("Snapshotter already initialised");
2798 }
2799 snapshotter = std::make_shared<Snapshotter>(
2800 writer_factory, network.tables, config.snapshots.tx_count);
2801 }
2802
2803 void read_ledger_entries(::consensus::Index from, ::consensus::Index to)
2804 {
2806 ::consensus::ledger_get_range,
2807 to_host,
2808 from,
2809 to,
2811 }
2812
2813 void ledger_truncate(::consensus::Index idx, bool recovery_mode = false)
2814 {
2816 ::consensus::ledger_truncate, to_host, idx, recovery_mode);
2817 }
2818
2819 void setup_acme_clients()
2820 {
2821 if (!config.network.acme || config.network.acme->configurations.empty())
2822 {
2823 return;
2824 }
2825
2826 open_acme_challenge_frontend();
2827
2828 const auto& ifaces = config.network.rpc_interfaces;
2829 num_acme_interfaces =
2830 std::count_if(ifaces.begin(), ifaces.end(), [](const auto& id_iface) {
2831 return id_iface.second.endorsement->authority == Authority::ACME;
2832 });
2833
2834 if (num_acme_interfaces > 0)
2835 {
2836 using namespace threading;
2837
2838 // Start task to periodically check whether any of the certs are
2839 // expired.
2840 auto msg = std::make_unique<::threading::Tmsg<NodeStateMsg>>(
2841 [](std::unique_ptr<::threading::Tmsg<NodeStateMsg>> msg) {
2842 auto& state = msg->data.self;
2843
2844 if (state.consensus && state.consensus->can_replicate())
2845 {
2846 if (state.acme_clients.size() != state.num_acme_interfaces)
2847 {
2848 auto tx = state.network.tables->create_tx();
2849 state.trigger_acme_refresh(tx);
2850 tx.commit();
2851 }
2852 else
2853 {
2854 for (auto& [cfg_name, client] : state.acme_clients)
2855 {
2856 if (client)
2857 {
2858 client->check_expiry(
2859 state.network.tables, state.network.identity);
2860 }
2861 }
2862 }
2863 }
2864
2865 auto delay = std::chrono::minutes(1);
2867 std::move(msg), delay);
2868 },
2869 *this);
2870
2872 std::move(msg), std::chrono::seconds(2));
2873 }
2874 }
2875
2876 void seal_ledger_secret(const VersionedLedgerSecret& ledger_secret)
2877 {
2878 seal_ledger_secret(ledger_secret.first, ledger_secret.second);
2879 }
2880
2881 void seal_ledger_secret(
2882 const kv::Version& version, const LedgerSecretPtr& ledger_secret)
2883 {
2884 if (!config.sealed_ledger_secret_location.has_value())
2885 {
2886 return;
2887 }
2888
2889 CCF_ASSERT(
2890 snp_tcb_version.has_value(), "TCB version must be set before sealing");
2891
2893 config.sealed_ledger_secret_location.value(),
2894 snp_tcb_version.value(),
2895 version,
2896 ledger_secret);
2897 }
2898
2899 LedgerSecretPtr unseal_ledger_secret()
2900 {
2901 CCF_ASSERT(
2902 snp_tcb_version.has_value(),
2903 "TCB version must be set when unsealing ledger secret");
2904
2905 CCF_ASSERT(
2906 config.recover.previous_sealed_ledger_secret_location.has_value(),
2907 "Previous sealed ledger secret location must be set");
2908 auto ledger_secret_path =
2909 config.recover.previous_sealed_ledger_secret_location.value();
2910
2911 auto max_version = network.tables->current_version();
2912
2914 config.recover.previous_sealed_ledger_secret_location.value(),
2915 max_version);
2916 }
2917
2918 public:
2919 void set_n2n_message_limit(size_t message_limit)
2920 {
2921 n2n_channels->set_message_limit(message_limit);
2922 }
2923
2924 void set_n2n_idle_timeout(std::chrono::milliseconds idle_timeout)
2925 {
2926 n2n_channels->set_idle_timeout(idle_timeout);
2927 }
2928
2929 virtual const ccf::StartupConfig& get_node_config() const override
2930 {
2931 return config;
2932 }
2933
2935 {
2936 return network.identity->cert;
2937 }
2938
2940 const ccf::NodeInfoNetwork::RpcInterfaceID& interface_id,
2941 std::shared_ptr<ACMEChallengeHandler> h) override
2942 {
2943 acme_challenge_handlers[interface_id] = h;
2944 }
2945
2946 // Stop-gap until it becomes easier to use other HTTP clients
2947 virtual void make_http_request(
2948 const ::http::URL& url,
2949 ::http::Request&& req,
2950 std::function<bool(
2951 ccf::http_status status, http::HeaderMap&&, std::vector<uint8_t>&&)>
2952 callback,
2953 const std::vector<std::string>& ca_certs = {},
2954 const std::string& app_protocol = "HTTP1",
2955 bool authenticate_as_node_client_certificate = false) override
2956 {
2957 std::optional<ccf::crypto::Pem> client_cert = std::nullopt;
2958 std::optional<ccf::crypto::Pem> client_cert_key = std::nullopt;
2959 if (authenticate_as_node_client_certificate)
2960 {
2961 client_cert =
2962 endorsed_node_cert ? *endorsed_node_cert : self_signed_node_cert;
2963 client_cert_key = node_sign_kp->private_key_pem();
2964 }
2965
2966 auto ca = std::make_shared<::tls::CA>(ca_certs, true);
2967 std::shared_ptr<::tls::Cert> ca_cert =
2968 std::make_shared<::tls::Cert>(ca, client_cert, client_cert_key);
2969 auto client = rpcsessions->create_client(ca_cert, app_protocol);
2970 client->connect(
2971 url.host,
2972 url.port,
2973 [callback](
2974 ccf::http_status status,
2975 http::HeaderMap&& headers,
2976 std::vector<uint8_t>&& data) {
2977 return callback(status, std::move(headers), std::move(data));
2978 });
2979 client->send_request(std::move(req));
2980 }
2981
2982 void write_snapshot(std::span<uint8_t> snapshot_buf, size_t request_id)
2983 {
2984 snapshotter->write_snapshot(snapshot_buf, request_id);
2985 }
2986
2987 virtual std::shared_ptr<ccf::kv::Store> get_store() override
2988 {
2989 return network.tables;
2990 }
2991
2993 {
2994 return writer_factory;
2995 }
2996 };
2997}
#define CCF_ASSERT_FMT(expr,...)
Definition ccf_assert.h:10
#define CCF_ASSERT(expr, msg)
Definition ccf_assert.h:14
Definition raft_types.h:48
Definition raft.h:96
Definition node_interface.h:23
static std::optional< pal::snp::Attestation > get_snp_attestation(const QuoteInfo &quote_info)
Definition quote.cpp:145
static std::optional< HostData > get_host_data(const QuoteInfo &quote_info)
Definition quote.cpp:168
static QuoteVerificationResult verify_quote_against_store(ccf::kv::ReadOnlyTx &tx, const QuoteInfo &quote_info, const std::vector< uint8_t > &expected_node_public_key_der, pal::PlatformAttestationMeasurement &measurement)
Definition quote.cpp:317
static std::optional< pal::PlatformAttestationMeasurement > get_measurement(const QuoteInfo &quote_info)
Definition quote.cpp:128
Definition history.h:567
void set_node_id(const NodeId &id_)
Definition history.h:717
ccf::crypto::Sha256Hash get_replicated_state_root() override
Definition history.h:757
static bool endorse_previous_identity(ccf::kv::Tx &tx, const ccf::crypto::KeyPair_OpenSSL &service_key)
Definition internal_tables_access.h:514
static std::optional< ServiceStatus > get_service_status(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:692
static bool open_service(ccf::kv::Tx &tx)
Definition internal_tables_access.h:629
static std::map< NodeId, NodeInfo > get_trusted_nodes(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:435
static void broadcast_some(std::map< NodeId, NodeInfo > &&nodes, SecretsWriteHandle *secrets, const LedgerSecretsMap &some_ledger_secrets)
Definition secret_broadcast.h:19
static void broadcast_new(std::map< NodeId, NodeInfo > &&nodes, SecretsWriteHandle *secrets, LedgerSecretPtr &&new_ledger_secret)
Definition secret_broadcast.h:46
static std::vector< uint8_t > decrypt(const ccf::crypto::RSAKeyPairPtr &encryption_key, const std::vector< uint8_t > &cipher)
Definition secret_broadcast.h:70
Definition node_state.h:88
void write_snapshot(std::span< uint8_t > snapshot_buf, size_t request_id)
Definition node_state.h:2982
void start_ledger_recovery_unsafe()
Definition node_state.h:990
bool rekey_ledger(ccf::kv::Tx &tx) override
Definition node_state.h:1916
bool is_part_of_public_network() const override
Definition node_state.h:1889
NodeState(ringbuffer::AbstractWriterFactory &writer_factory, NetworkState &network, std::shared_ptr< RPCSessions > rpcsessions, ccf::crypto::CurveID curve_id_)
Definition node_state.h:237
void setup_one_off_secret_hook()
Definition node_state.h:1393
void tick_end()
Definition node_state.h:1775
virtual std::shared_ptr< ccf::kv::Store > get_store() override
Definition node_state.h:2987
void recover_private_ledger_entries(const std::vector< uint8_t > &entries)
Definition node_state.h:1218
virtual const ccf::StartupConfig & get_node_config() const override
Definition node_state.h:2929
void trigger_acme_refresh(ccf::kv::Tx &tx, const std::optional< std::vector< std::string > > &interfaces=std::nullopt) override
Definition node_state.h:1519
void recv_node_inbound(const uint8_t *data, size_t size)
Definition node_state.h:1798
bool is_reading_private_ledger() const override
Definition node_state.h:1884
void trigger_ledger_chunk(ccf::kv::Tx &tx) override
Definition node_state.h:1497
virtual ringbuffer::AbstractWriterFactory & get_writer_factory() override
Definition node_state.h:2992
bool is_primary() const override
Definition node_state.h:1851
virtual ccf::crypto::Pem get_network_cert() override
Definition node_state.h:2934
void recover_public_ledger_end_unsafe()
Definition node_state.h:1122
SessionMetrics get_session_metrics() override
Definition node_state.h:1961
void initiate_join()
Definition node_state.h:925
void advance_part_of_network()
Definition node_state.h:1111
void advance_part_of_public_network()
Definition node_state.h:1103
ccf::kv::Version get_startup_snapshot_seqno() override
Definition node_state.h:1955
const ccf::COSESignaturesConfig & get_cose_signatures_config() override
Definition node_state.h:1972
void auto_refresh_jwt_keys()
Definition node_state.h:954
void recover_ledger_end()
Definition node_state.h:1426
void initiate_private_recovery(ccf::kv::Tx &tx, const std::optional< LedgerSecretPtr > &unsealed_ledger_secret=std::nullopt) override
Definition node_state.h:1742
void initiate_join_unsafe()
Definition node_state.h:673
void stop_notice() override
Definition node_state.h:1788
virtual void make_http_request(const ::http::URL &url, ::http::Request &&req, std::function< bool(ccf::http_status status, http::HeaderMap &&, std::vector< uint8_t > &&)> callback, const std::vector< std::string > &ca_certs={}, const std::string &app_protocol="HTTP1", bool authenticate_as_node_client_certificate=false) override
Definition node_state.h:2947
void set_n2n_message_limit(size_t message_limit)
Definition node_state.h:2919
bool can_replicate() override
Definition node_state.h:1860
void transition_service_to_open(ccf::kv::Tx &tx, AbstractGovernanceEffects::ServiceIdentities identities) override
Definition node_state.h:1606
QuoteVerificationResult verify_quote(ccf::kv::ReadOnlyTx &tx, const QuoteInfo &quote_info_, const std::vector< uint8_t > &expected_node_public_key_der, pal::PlatformAttestationMeasurement &measurement) override
Definition node_state.h:254
bool is_reading_public_ledger() const override
Definition node_state.h:1879
void launch_node()
Definition node_state.h:301
void initiate_quote_generation()
Definition node_state.h:431
void initialize(const ccf::consensus::Configuration &consensus_config_, std::shared_ptr< RPCMap > rpc_map_, std::shared_ptr< AbstractRPCResponder > rpc_sessions_, std::shared_ptr< indexing::Indexer > indexer_, size_t sig_tx_interval_, size_t sig_ms_interval_)
Definition node_state.h:267
void setup_private_recovery_store()
Definition node_state.h:1449
void trigger_snapshot(ccf::kv::Tx &tx) override
Definition node_state.h:1508
bool is_in_initialised_state() const override
Definition node_state.h:1869
virtual void install_custom_acme_challenge_handler(const ccf::NodeInfoNetwork::RpcInterfaceID &interface_id, std::shared_ptr< ACMEChallengeHandler > h) override
Definition node_state.h:2939
bool has_received_stop_notice() override
Definition node_state.h:1793
bool is_part_of_network() const override
Definition node_state.h:1874
void start_join_timer()
Definition node_state.h:931
ExtendedState state() override
Definition node_state.h:1902
void trigger_recovery_shares_refresh(ccf::kv::Tx &tx) override
Definition node_state.h:1492
void set_n2n_idle_timeout(std::chrono::milliseconds idle_timeout)
Definition node_state.h:2924
NodeId get_node_id() const
Definition node_state.h:1950
void tick(std::chrono::milliseconds elapsed)
Definition node_state.h:1754
NodeCreateInfo create(StartType start_type_, const ccf::StartupConfig &config_, std::vector< uint8_t > &&startup_snapshot_)
Definition node_state.h:571
void trigger_host_process_launch(const std::vector< std::string > &args, const std::vector< uint8_t > &input) override
Definition node_state.h:1593
void recover_public_ledger_entries(const std::vector< uint8_t > &entries)
Definition node_state.h:1005
void recover_private_ledger_end_unsafe()
Definition node_state.h:1290
void initiate_private_recovery_unsafe(ccf::kv::Tx &tx, const std::optional< LedgerSecretPtr > &unsealed_ledger_secret=std::nullopt)
Definition node_state.h:1724
ccf::crypto::Pem get_self_signed_certificate() override
Definition node_state.h:1966
size_t get_jwt_attempts() override
Definition node_state.h:982
bool is_accessible_to_members() const override
Definition node_state.h:1894
Definition share_manager.h:167
static void clear_submitted_recovery_shares(ccf::kv::Tx &tx)
Definition share_manager.h:649
void issue_recovery_shares(ccf::kv::Tx &tx)
Definition share_manager.h:455
Definition key_pair.h:17
Definition pem.h:18
const std::string & str() const
Definition pem.h:46
Definition sha256_hash.h:16
Definition committable_tx.h:18
Definition tx.h:160
M::ReadOnlyHandle * ro(M &m)
Definition tx.h:169
Definition tx.h:201
M::Handle * rw(M &m)
Definition tx.h:212
M::WriteOnlyHandle * wo(M &m)
Definition tx.h:233
std::map< K, std::optional< V > > Write
Definition map.h:40
Definition value.h:32
static ccf::kv::untyped::MapHook wrap_map_hook(const MapHook &hook)
Definition value.h:72
std::optional< V > Write
Definition value.h:38
static std::vector< uint8_t > get_entry(const uint8_t *&data, size_t &size)
Definition ledger_enclave.h:26
Definition state_machine.h:14
void expect(T state_) const
Definition state_machine.h:24
T value() const
Definition state_machine.h:39
void advance(T state_)
Definition state_machine.h:44
bool check(T state_) const
Definition state_machine.h:34
void set_header(std::string k, const std::string &v)
Definition http_builder.h:45
void set_body(const std::vector< uint8_t > *b, bool overwrite_content_length=true)
Definition http_builder.h:74
Definition http_builder.h:118
Definition ring_buffer_types.h:153
static ThreadMessaging & instance()
Definition thread_messaging.h:283
TaskQueue::TimerEntry add_task_after(std::unique_ptr< Tmsg< Payload > > msg, std::chrono::milliseconds ms)
Definition thread_messaging.h:326
void add_task(uint16_t tid, std::unique_ptr< Tmsg< Payload > > msg)
Definition thread_messaging.h:318
StartType
Definition enclave_interface_types.h:92
@ Join
Definition enclave_interface_types.h:94
@ Recover
Definition enclave_interface_types.h:95
@ Start
Definition enclave_interface_types.h:93
#define LOG_INFO_FMT
Definition logger.h:362
#define LOG_TRACE_FMT
Definition logger.h:356
#define LOG_DEBUG_FMT
Definition logger.h:357
#define LOG_FAIL_FMT
Definition logger.h:363
std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.cpp:12
ccf::crypto::Pem public_key_pem_from_cert(const std::vector< uint8_t > &der)
Definition verifier.cpp:48
VerifierPtr make_verifier(const std::vector< uint8_t > &cert)
Definition verifier.cpp:18
CurveID
Definition curve.h:18
KeyPairPtr make_key_pair(CurveID curve_id=service_identity_curve_choice)
Definition key_pair.cpp:35
std::string get_subject_name(const Pem &cert)
Definition verifier.cpp:53
std::vector< uint8_t > cert_pem_to_der(const Pem &pem)
Definition verifier.cpp:38
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
if(argc !=1)
Definition kv_helpers.h:71
void register_class_ids()
Definition global_class_ids.cpp:17
std::map< ccf::kv::serialisers::SerialisedEntry, std::optional< ccf::kv::serialisers::SerialisedEntry > > Write
Definition untyped.h:16
uint64_t Term
Definition kv_types.h:48
std::unique_ptr< ConsensusHook > ConsensusHookPtr
Definition hooks.h:21
uint64_t Version
Definition version.h:8
@ SUCCESS
Definition kv_types.h:249
ApplyResult
Definition kv_types.h:341
@ PASS_SIGNATURE
Definition kv_types.h:343
@ FAIL
Definition kv_types.h:350
MembershipState
Definition kv_types.h:155
std::vector< ConsensusHookPtr > ConsensusHookPtrs
Definition hooks.h:22
UVMEndorsements verify_uvm_endorsements_descriptor(const std::vector< uint8_t > &uvm_endorsements_raw, const pal::PlatformAttestationMeasurement &uvm_measurement)
Definition uvm_endorsements.cpp:304
std::mutex Mutex
Definition locking.h:12
uint16_t get_current_thread_id()
Definition thread_local.cpp:15
Definition app_interface.h:14
LedgerSecretPtr find_and_unseal_ledger_secret_from_disk(const std::string &sealed_secret_dir, kv::Version max_version)
Definition local_sealing.h:191
constexpr auto get_actor_prefix(ActorsType at)
Definition actors.h:31
NodeId compute_node_id_from_kp(const ccf::crypto::KeyPairPtr &node_sign_kp)
Definition nodes.h:43
std::shared_ptr< ccf::kv::Store > make_store()
Definition network_tables.h:40
std::vector< uint8_t > CoseSignature
Definition signatures.h:64
QuoteVerificationResult
Definition quote.h:18
std::tuple< NodeStartupState, std::optional< ccf::kv::Version >, std::optional< ccf::kv::Version > > ExtendedState
Definition node_operation_interface.h:20
std::list< EncryptedLedgerSecretInfo > RecoveredEncryptedLedgerSecrets
Definition share_manager.h:157
view
Definition signatures.h:54
std::map< ccf::kv::Version, LedgerSecretPtr > LedgerSecretsMap
Definition ledger_secrets.h:20
void reset_data(std::vector< uint8_t > &data)
Definition node_state.h:81
LedgerSecretPtr make_ledger_secret()
Definition ledger_secret.h:77
ReconfigurationType
Definition reconfiguration_type.h:10
@ ONE_TRANSACTION
Definition reconfiguration_type.h:11
llhttp_status http_status
Definition http_status.h:9
@ RECOVERY_SHARES
Definition recovery_type.h:14
@ LOCAL_UNSEALING
Definition recovery_type.h:15
std::shared_ptr< LedgerSecret > LedgerSecretPtr
Definition ledger_secret.h:75
constexpr View VIEW_UNKNOWN
Definition tx_id.h:26
NodeStartupState
Definition node_startup_state.h:10
uint64_t View
Definition tx_id.h:23
void seal_ledger_secret_to_disk(const std::string &sealed_secret_dir, const ccf::pal::snp::TcbVersionRaw &tcb_version, const kv::Version &version, const LedgerSecretPtr &ledger_secret)
Definition local_sealing.h:107
ActorsType
Definition actors.h:11
@ channel_msg
Definition node_types.h:20
@ consensus_msg
Definition node_types.h:21
@ forwarded_msg
Definition node_types.h:22
std::shared_ptr<::http::HttpRpcContext > make_rpc_context(std::shared_ptr< ccf::SessionContext > s, const std::vector< uint8_t > &packed)
Definition http_rpc_context.h:392
Definition perf_client.h:12
Definition consensus_types.h:23
uint64_t Index
Definition ledger_enclave_types.h:11
@ Recovery
Definition ledger_enclave_types.h:15
Definition configuration.h:14
URL parse_url_full(const std::string &url)
Definition http_parser.h:145
Definition json_schema.h:15
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:150
STL namespace.
Definition thread_messaging.h:14
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:255
Definition interface.h:51
Definition gov_effects_interface.h:22
std::optional< ccf::crypto::Pem > previous
Definition gov_effects_interface.h:23
ccf::crypto::Pem next
Definition gov_effects_interface.h:24
std::optional< std::string > security_policy
Definition startup_config.h:82
std::optional< std::string > uvm_endorsements
Definition startup_config.h:83
std::optional< std::string > snp_endorsements
Definition startup_config.h:84
ccf::pal::snp::EndorsementsServers snp_endorsements_servers
Definition startup_config.h:73
std::optional< std::string > snp_endorsements_file
Definition startup_config.h:76
Environment environment
Definition startup_config.h:88
ccf::ds::TimeString key_refresh_interval
Definition startup_config.h:65
std::string subject_name
Definition startup_config.h:35
size_t initial_validity_days
Definition startup_config.h:38
ccf::NodeInfoNetwork network
Definition startup_config.h:31
JWT jwt
Definition startup_config.h:69
Attestation attestation
Definition startup_config.h:92
NodeCertificateInfo node_certificate
Definition startup_config.h:42
Definition cose_signatures_config.h:12
Definition node_call_types.h:83
QuoteInfo quote_info
Definition node_call_types.h:85
std::optional< ccf::crypto::Pem > certificate_signing_request
Definition node_call_types.h:88
std::optional< ccf::kv::Version > startup_seqno
Definition node_call_types.h:87
NodeInfoNetwork node_info_network
Definition node_call_types.h:84
nlohmann::json node_data
Definition node_call_types.h:90
ccf::crypto::Pem public_encryption_key
Definition node_call_types.h:86
Definition node_call_types.h:94
std::optional< NetworkInfo > network_info
Definition node_call_types.h:150
NodeStatus node_status
Definition node_call_types.h:95
Definition network_state.h:12
std::shared_ptr< LedgerSecrets > ledger_secrets
Definition network_state.h:14
std::unique_ptr< NetworkIdentity > identity
Definition network_state.h:13
std::shared_ptr< ccf::kv::Store > tables
Definition network_tables.h:47
const JwtIssuers jwt_issuers
Definition network_tables.h:167
const Configuration config
Definition network_tables.h:201
const CoseSignatures cose_signatures
Definition network_tables.h:239
const EncryptedLedgerSecretsInfo encrypted_ledger_secrets
Definition network_tables.h:230
const Signatures signatures
Definition network_tables.h:238
Definition node_state.h:76
ccf::crypto::Pem service_cert
Definition node_state.h:78
ccf::crypto::Pem self_signed_node_cert
Definition node_state.h:77
std::string RpcInterfaceID
Definition node_info_network.h:87
Describes a quote (attestation) from trusted hardware.
Definition quote_info.h:26
QuoteFormat format
Quote format.
Definition quote_info.h:28
std::optional< std::vector< uint8_t > > uvm_endorsements
UVM endorsements (SNP-only)
Definition quote_info.h:34
std::vector< uint8_t > quote
Enclave quote.
Definition quote_info.h:30
std::vector< uint8_t > endorsements
Quote endorsements.
Definition quote_info.h:32
Definition session_metrics.h:13
std::vector< uint8_t > service_cert
Definition startup_config.h:138
bool follow_redirect
Definition startup_config.h:139
ccf::NodeInfoNetwork::NetAddress target_rpc_address
Definition startup_config.h:136
ccf::ds::TimeString retry_timeout
Definition startup_config.h:137
std::optional< std::vector< uint8_t > > previous_service_identity
Definition startup_config.h:145
Definition startup_config.h:106
size_t initial_service_certificate_validity_days
Definition startup_config.h:114
ccf::COSESignaturesConfig cose_signatures
Definition startup_config.h:116
Join join
Definition startup_config.h:141
nlohmann::json node_data
Definition startup_config.h:122
Recover recover
Definition startup_config.h:150
std::string startup_host_time
Definition startup_config.h:110
std::string service_subject_name
Definition startup_config.h:115
Definition consensus_config.h:11
Definition cose_common.h:52
size_t count_s() const
Definition unit_strings.h:190
const std::string & get_name() const
Definition get_name.h:18
Definition measurement.h:120
Definition report_data.h:56
Definition attestation_sev_snp_endorsements.h:20
Definition attestation_sev_snp.h:361