CCF
Loading...
Searching...
No Matches
internal_tables_access.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/ds/hex.h"
17#include "ccf/tx.h"
20#include "node/ledger_secrets.h"
24
25#include <algorithm>
26#include <ostream>
27#include <stdexcept>
28
29namespace ccf
30{
31 /* We can't query the past epochs' TXs if the service hasn't been opened
32 * yet. We do guess values based on epoch value and seqno changing rules. */
34 {
35 return ccf::TxID{
36 .view = txid.view - aft::starting_view_change, .seqno = txid.seqno - 1};
37 }
39 {
40 return ccf::TxID{
41 .view = txid.view + aft::starting_view_change, .seqno = txid.seqno + 1};
42 }
43
44 // This class provides functions for interacting with various internal
45 // service-governance tables. Specifically, it aims to maintain some
46 // invariants amongst these tables (eg - keys being present in multiple
47 // tables) despite access by distinct callers. These tables may be accessed
48 // directly with a Tx object, but it is recommended to use these methods where
49 // available.
51 {
52 public:
53 // This class is purely a container for static methods, should not be
54 // instantiated
56
58 {
59 auto* nodes = tx.rw<ccf::Nodes>(Tables::NODES);
60
61 std::map<NodeId, NodeInfo> nodes_to_delete;
62 nodes->foreach([&nodes_to_delete](const NodeId& nid, const NodeInfo& ni) {
63 // Only retire nodes that have not already been retired
65 {
66 nodes_to_delete[nid] = ni;
67 }
68 return true;
69 });
70
71 for (auto [nid, ni] : nodes_to_delete)
72 {
74 nodes->put(nid, ni);
75 }
76 }
77
79 ccf::kv::ReadOnlyTx& tx, const MemberId& member_id)
80 {
81 auto* member_encryption_public_keys =
83 Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
84
85 return member_encryption_public_keys->get(member_id).has_value();
86 }
87
89 ccf::kv::ReadOnlyTx& tx, const MemberId& member_id)
90 {
91 return is_recovery_participant_or_owner(tx, member_id) &&
92 !is_recovery_owner(tx, member_id);
93 }
94
95 static bool is_recovery_owner(
96 ccf::kv::ReadOnlyTx& tx, const MemberId& member_id)
97 {
98 auto* member_info = tx.ro<ccf::MemberInfo>(Tables::MEMBER_INFO);
99 auto mi = member_info->get(member_id);
100 if (!mi.has_value())
101 {
102 return false;
103 }
104
105 return mi->recovery_role.has_value() &&
106 mi->recovery_role.value() == MemberRecoveryRole::Owner;
107 }
108
109 static bool is_active_member(
110 ccf::kv::ReadOnlyTx& tx, const MemberId& member_id)
111 {
112 auto* member_info = tx.ro<ccf::MemberInfo>(Tables::MEMBER_INFO);
113 auto mi = member_info->get(member_id);
114 if (!mi.has_value())
115 {
116 return false;
117 }
118
119 return mi->status == MemberStatus::ACTIVE;
120 }
121
122 static std::map<MemberId, ccf::crypto::Pem>
124 {
125 auto* member_info = tx.ro<ccf::MemberInfo>(Tables::MEMBER_INFO);
126 auto* member_encryption_public_keys =
128 Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
129
130 std::map<MemberId, ccf::crypto::Pem> active_recovery_participants;
131
132 member_encryption_public_keys->foreach(
133 [&active_recovery_participants,
134 &member_info](const auto& mid, const auto& pem) {
135 auto info = member_info->get(mid);
136 if (!info.has_value())
137 {
138 throw std::logic_error(
139 fmt::format("Recovery member {} has no member info", mid));
140 }
141
142 if (
143 info->status == MemberStatus::ACTIVE &&
144 info->recovery_role.value_or(MemberRecoveryRole::Participant) ==
146 {
147 active_recovery_participants[mid] = pem;
148 }
149 return true;
150 });
151 return active_recovery_participants;
152 }
153
154 static std::map<MemberId, ccf::crypto::Pem> get_active_recovery_owners(
156 {
157 auto* member_info = tx.ro<ccf::MemberInfo>(Tables::MEMBER_INFO);
158 auto* member_encryption_public_keys =
160 Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
161
162 std::map<MemberId, ccf::crypto::Pem> active_recovery_owners;
163
164 member_encryption_public_keys->foreach(
165 [&active_recovery_owners,
166 &member_info](const auto& mid, const auto& pem) {
167 auto info = member_info->get(mid);
168 if (!info.has_value())
169 {
170 throw std::logic_error(
171 fmt::format("Recovery member {} has no member info", mid));
172 }
173
174 if (
175 info->status == MemberStatus::ACTIVE &&
176 info->recovery_role.value_or(MemberRecoveryRole::Participant) ==
178 {
179 active_recovery_owners[mid] = pem;
180 }
181 return true;
182 });
183 return active_recovery_owners;
184 }
185
187 ccf::kv::Tx& tx, const NewMember& member_pub_info)
188 {
189 auto* member_certs = tx.rw<ccf::MemberCerts>(Tables::MEMBER_CERTS);
190 auto* member_info = tx.rw<ccf::MemberInfo>(Tables::MEMBER_INFO);
191 auto* member_acks = tx.rw<ccf::MemberAcks>(Tables::MEMBER_ACKS);
192 auto* signatures = tx.ro<ccf::Signatures>(Tables::SIGNATURES);
193
194 auto member_cert_der =
195 ccf::crypto::make_verifier(member_pub_info.cert)->cert_der();
196 auto id = ccf::crypto::Sha256Hash(member_cert_der).hex_str();
197
198 auto member = member_certs->get(id);
199 if (member.has_value())
200 {
201 // No effect if member already exists
202 return id;
203 }
204
205 if (member_pub_info.recovery_role.has_value())
206 {
207 auto member_recovery_role = member_pub_info.recovery_role.value();
208 if (!member_pub_info.encryption_pub_key.has_value())
209 {
210 if (member_recovery_role != ccf::MemberRecoveryRole::NonParticipant)
211 {
212 throw std::logic_error(fmt::format(
213 "Member {} cannot be added as recovery_role has a value set but "
214 "no "
215 "encryption public key is specified",
216 id));
217 }
218 }
219 else
220 {
221 if (
222 member_recovery_role != ccf::MemberRecoveryRole::Participant &&
223 member_recovery_role != ccf::MemberRecoveryRole::Owner)
224 {
225 throw std::logic_error(fmt::format(
226 "Recovery member {} cannot be added as with recovery role value "
227 "of "
228 "{}",
229 id,
230 member_recovery_role));
231 }
232 }
233 }
234
235 member_certs->put(id, member_pub_info.cert);
236 member_info->put(
237 id,
239 member_pub_info.member_data,
240 member_pub_info.recovery_role});
241
242 if (member_pub_info.encryption_pub_key.has_value())
243 {
244 auto* member_encryption_public_keys =
246 Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
247 member_encryption_public_keys->put(
248 id, member_pub_info.encryption_pub_key.value());
249 }
250
251 auto s = signatures->get();
252 if (!s)
253 {
254 member_acks->put(id, MemberAck());
255 }
256 else
257 {
258 member_acks->put(id, MemberAck(s->root));
259 }
260 return id;
261 }
262
263 static bool activate_member(ccf::kv::Tx& tx, const MemberId& member_id)
264 {
265 auto* member_info = tx.rw<ccf::MemberInfo>(Tables::MEMBER_INFO);
266
267 auto member = member_info->get(member_id);
268 if (!member.has_value())
269 {
270 throw std::logic_error(fmt::format(
271 "Member {} cannot be activated as they do not exist", member_id));
272 }
273
274 const auto newly_active = member->status != MemberStatus::ACTIVE;
275
276 member->status = MemberStatus::ACTIVE;
277 member_info->put(member_id, member.value());
278
279 return newly_active;
280 }
281
282 static bool remove_member(ccf::kv::Tx& tx, const MemberId& member_id)
283 {
284 auto* member_certs = tx.rw<ccf::MemberCerts>(Tables::MEMBER_CERTS);
285 auto* member_encryption_public_keys =
287 Tables::MEMBER_ENCRYPTION_PUBLIC_KEYS);
288 auto* member_info = tx.rw<ccf::MemberInfo>(Tables::MEMBER_INFO);
289 auto* member_acks = tx.rw<ccf::MemberAcks>(Tables::MEMBER_ACKS);
290 auto* member_gov_history =
291 tx.rw<ccf::GovernanceHistory>(Tables::GOV_HISTORY);
292
293 auto member_to_remove = member_info->get(member_id);
294 if (!member_to_remove.has_value())
295 {
296 // The remove member proposal is idempotent so if the member does not
297 // exist, the proposal should succeed with no effect
299 "Could not remove member {}: member does not exist", member_id);
300 return true;
301 }
302
303 // If the member was active and had a recovery share, check that
304 // the new number of active members is still sufficient for
305 // recovery
306 if (member_to_remove->status == MemberStatus::ACTIVE)
307 {
308 if (is_recovery_participant(tx, member_id))
309 {
310 size_t active_recovery_participants_count_after =
312 auto recovery_threshold = get_recovery_threshold(tx);
313 auto active_recovery_owners_count =
315 if (
316 active_recovery_participants_count_after == 0 &&
317 active_recovery_owners_count > 0 && recovery_threshold == 1)
318 {
319 // Its fine to remove all active recovery particiants as long as
320 // recover owner(s) exist with a threshold of 1.
322 "Allowing last active recovery participant member {}: to "
323 "be removed as active recovery owner members ({}) are present "
324 "with recovery threshold ({}).",
325 member_id,
326 active_recovery_owners_count,
327 recovery_threshold);
328 }
329 else if (
330 active_recovery_participants_count_after < recovery_threshold)
331 {
332 // Because the member to remove is active, there is at least one
333 // active member (i.e. active_recovery_participants_count_after >=
334 // 0)
336 "Failed to remove recovery member {}: number of active recovery "
337 "participant members ({}) would be less than recovery threshold "
338 "({})",
339 member_id,
340 active_recovery_participants_count_after,
341 recovery_threshold);
342 return false;
343 }
344 }
345 else if (is_recovery_owner(tx, member_id))
346 {
347 size_t active_recovery_owners_count_after =
348 get_active_recovery_owners(tx).size() - 1;
349 auto recovery_threshold = get_recovery_threshold(tx);
350 auto active_recovery_participants_count =
352 if (active_recovery_owners_count_after == 0)
353 {
354 if (active_recovery_participants_count > 0)
355 {
357 "Allowing last active recovery owner member {}: to "
358 "be removed as active recovery owner participants ({}) are "
359 "present with recovery threshold ({}).",
360 member_id,
361 active_recovery_participants_count,
362 recovery_threshold);
363 }
364 else
365 {
367 "Failed to remove last active recovery owner member {}: number "
368 "of active recovery participant members ({}) would be less "
369 "than recovery threshold ({})",
370 member_id,
371 active_recovery_participants_count,
372 recovery_threshold);
373 return false;
374 }
375 }
376 }
377 }
378
379 member_info->remove(member_id);
380 member_encryption_public_keys->remove(member_id);
381 member_certs->remove(member_id);
382 member_acks->remove(member_id);
383 member_gov_history->remove(member_id);
384
385 return true;
386 }
387
388 static UserId add_user(ccf::kv::Tx& tx, const NewUser& new_user)
389 {
390 auto* user_certs = tx.rw<ccf::UserCerts>(Tables::USER_CERTS);
391
392 auto user_cert_der =
393 ccf::crypto::make_verifier(new_user.cert)->cert_der();
394 auto id = ccf::crypto::Sha256Hash(user_cert_der).hex_str();
395
396 auto user_cert = user_certs->get(id);
397 if (user_cert.has_value())
398 {
399 throw std::logic_error(
400 fmt::format("Certificate already exists for user {}", id));
401 }
402
403 user_certs->put(id, new_user.cert);
404
405 if (new_user.user_data != nullptr)
406 {
407 auto* user_info = tx.rw<ccf::UserInfo>(Tables::USER_INFO);
408 auto ui = user_info->get(id);
409 if (ui.has_value())
410 {
411 throw std::logic_error(
412 fmt::format("User data already exists for user {}", id));
413 }
414
415 user_info->put(id, {new_user.user_data});
416 }
417
418 return id;
419 }
420
421 static void remove_user(ccf::kv::Tx& tx, const UserId& user_id)
422 {
423 // Has no effect if the user does not exist
424 auto* user_certs = tx.rw<ccf::UserCerts>(Tables::USER_CERTS);
425 auto* user_info = tx.rw<ccf::UserInfo>(Tables::USER_INFO);
426
427 user_certs->remove(user_id);
428 user_info->remove(user_id);
429 }
430
431 static void add_node(
432 ccf::kv::Tx& tx, const NodeId& id, const NodeInfo& node_info)
433 {
434 auto* node = tx.rw<ccf::Nodes>(Tables::NODES);
435 node->put(id, node_info);
436 }
437
438 static std::map<NodeId, NodeInfo> get_trusted_nodes(ccf::kv::ReadOnlyTx& tx)
439 {
440 std::map<NodeId, NodeInfo> active_nodes;
441
442 auto* nodes = tx.ro<ccf::Nodes>(Tables::NODES);
443
444 nodes->foreach(
445 [&active_nodes, &nodes](const NodeId& nid, const NodeInfo& ni) {
447 {
448 active_nodes[nid] = ni;
449 }
450 else if (ni.status == ccf::NodeStatus::RETIRED)
451 {
452 // If a node is retired, but knowledge of their retirement has not
453 // yet been globally committed, they are still considered active.
454 auto cni = nodes->get_globally_committed(nid);
455 if (cni.has_value() && !cni->retired_committed)
456 {
457 active_nodes[nid] = ni;
458 }
459 }
460 return true;
461 });
462
463 return active_nodes;
464 }
465
466 // Service status should use a state machine, very much like NodeState.
467 static void create_service(
468 ccf::kv::Tx& tx,
469 const ccf::crypto::Pem& service_cert,
470 ccf::TxID create_txid,
471 nlohmann::json service_data = nullptr,
472 bool recovering = false)
473 {
474 auto* service = tx.rw<ccf::Service>(Tables::SERVICE);
475
476 size_t recovery_count = 0;
477 std::optional<ccf::kv::Version> prev_service_created_at = std::nullopt;
478
479 if (service->has())
480 {
481 const auto prev_service_info = service->get();
482 if (!prev_service_info.has_value())
483 {
484 throw std::logic_error("Failed to get previous service info");
485 }
486
487 if (!prev_service_info->current_service_create_txid.has_value())
488 {
489 throw std::logic_error(
490 "Starting TX for the previous service doesn't have "
491 "current_service_create_txid recorded");
492 }
493 prev_service_created_at =
494 prev_service_info->current_service_create_txid.value().seqno;
495
496 auto* previous_service_identity = tx.wo<ccf::PreviousServiceIdentity>(
497 ccf::Tables::PREVIOUS_SERVICE_IDENTITY);
498 previous_service_identity->put(prev_service_info->cert);
499
500 auto* last_signed_root = tx.wo<ccf::PreviousServiceLastSignedRoot>(
501 ccf::Tables::PREVIOUS_SERVICE_LAST_SIGNED_ROOT);
502 auto* sigs = tx.ro<ccf::Signatures>(ccf::Tables::SIGNATURES);
503 if (!sigs->has())
504 {
505 throw std::logic_error(
506 "Previous service doesn't have any signed transactions");
507 }
508 auto sig_opt = sigs->get();
509 if (!sig_opt.has_value())
510 {
511 throw std::logic_error(
512 "Previous service doesn't have signature value");
513 }
514 last_signed_root->put(sig_opt->root);
515
516 // Record number of recoveries for service. If the value does
517 // not exist in the table (i.e. pre 2.x ledger), assume it is the
518 // first recovery.
519 recovery_count = prev_service_info->recovery_count.value_or(0) + 1;
520 }
521
522 service->put(
523 {service_cert,
525 prev_service_created_at,
526 recovery_count,
527 service_data,
528 create_txid});
529 }
530
532 ccf::kv::ReadOnlyTx& tx, const ccf::crypto::Pem& expected_service_cert)
533 {
534 auto* service = tx.ro<ccf::Service>(Tables::SERVICE);
535 auto service_info = service->get();
536 return service_info.has_value() &&
537 service_info->cert == expected_service_cert;
538 }
539
541 ccf::kv::Tx& tx, const ccf::crypto::ECKeyPair_OpenSSL& service_key)
542 {
543 auto* service = tx.ro<ccf::Service>(Tables::SERVICE);
544 auto active_service = service->get();
545
546 auto* previous_identity_endorsement =
548 ccf::Tables::PREVIOUS_SERVICE_IDENTITY_ENDORSEMENT);
549
550 ccf::CoseEndorsement endorsement{};
551 std::vector<uint8_t> key_to_endorse{};
552 std::vector<uint8_t> previous_root{};
553
554 endorsement.endorsing_key = service_key.public_key_der();
555
556 if (previous_identity_endorsement->has())
557 {
558 const auto prev_endorsement = previous_identity_endorsement->get();
559 if (!prev_endorsement.has_value())
560 {
561 throw std::logic_error("Failed to get previous endorsement");
562 }
563
564 if (
565 !active_service.has_value() ||
566 !active_service->current_service_create_txid.has_value())
567 {
568 throw std::logic_error(
569 "Active service or current_service_create_txid is not set");
570 }
571
572 endorsement.endorsement_epoch_begin =
573 prev_endorsement->endorsement_epoch_end.has_value() ?
574 next_tx_if_recovery(prev_endorsement->endorsement_epoch_end.value()) :
575 prev_endorsement->endorsement_epoch_begin;
576
577 endorsement.endorsement_epoch_end = previous_tx_if_recovery(
578 active_service->current_service_create_txid.value());
579
580 endorsement.previous_version =
581 previous_identity_endorsement->get_version_of_previous_write();
582
583 key_to_endorse = prev_endorsement->endorsing_key;
584
585 auto* previous_service_last_signed_root =
587 ccf::Tables::PREVIOUS_SERVICE_LAST_SIGNED_ROOT);
588 if (!previous_service_last_signed_root->has())
589 {
591 "Failed to sign previous service identity: no last signed root");
592 return false;
593 }
594
595 auto root_opt = previous_service_last_signed_root->get();
596 if (!root_opt.has_value())
597 {
599 "Failed to sign previous service identity: no last signed root "
600 "value");
601 return false;
602 }
603 const auto root = root_opt.value();
604 previous_root.assign(root.h.begin(), root.h.end());
605 }
606 else
607 {
608 // There's no `epoch_end` for the a self-endorsement, leave it
609 // open-ranged and sign the current service key.
610
611 if (
612 !active_service.has_value() ||
613 !active_service->current_service_create_txid.has_value())
614 {
615 throw std::logic_error(
616 "Active service or current_service_create_txid is not set");
617 }
618
619 endorsement.endorsement_epoch_begin =
620 active_service->current_service_create_txid.value();
621
622 key_to_endorse = endorsement.endorsing_key;
623 }
624
625 std::vector<std::shared_ptr<ccf::crypto::COSEParametersFactory>>
626 ccf_headers_arr{};
627 ccf_headers_arr.push_back(ccf::crypto::cose_params_string_string(
628 ccf::crypto::COSE_PHEADER_KEY_RANGE_BEGIN,
629 endorsement.endorsement_epoch_begin.to_str()));
630 if (endorsement.endorsement_epoch_end)
631 {
632 ccf_headers_arr.push_back(ccf::crypto::cose_params_string_string(
633 ccf::crypto::COSE_PHEADER_KEY_RANGE_END,
634 endorsement.endorsement_epoch_end->to_str()));
635 }
636 if (!previous_root.empty())
637 {
638 ccf_headers_arr.push_back(ccf::crypto::cose_params_string_bytes(
639 ccf::crypto::COSE_PHEADER_KEY_EPOCH_LAST_MERKLE_ROOT, previous_root));
640 }
641
642 const auto time_since_epoch =
643 std::chrono::duration_cast<std::chrono::seconds>(
644 std::chrono::system_clock::now().time_since_epoch())
645 .count();
646
647 auto cwt_headers =
648 std::static_pointer_cast<ccf::crypto::COSEParametersFactory>(
649 std::make_shared<ccf::crypto::COSEParametersMap>(
650 std::make_shared<ccf::crypto::COSEMapIntKey>(
651 ccf::crypto::COSE_PHEADER_KEY_CWT),
653 ccf::crypto::COSE_PHEADER_KEY_IAT, time_since_epoch)}));
654
655 auto ccf_headers =
656 std::static_pointer_cast<ccf::crypto::COSEParametersFactory>(
657 std::make_shared<ccf::crypto::COSEParametersMap>(
658 std::make_shared<ccf::crypto::COSEMapStringKey>(
659 ccf::crypto::COSE_PHEADER_KEY_CCF),
660 ccf_headers_arr));
661
662 ccf::crypto::COSEHeadersArray pheaders{cwt_headers, ccf_headers};
663
664 try
665 {
666 endorsement.endorsement = cose_sign1(
667 service_key,
668 pheaders,
669 key_to_endorse,
670 false // detached payload
671 );
672 }
673 catch (const ccf::crypto::COSESignError& e)
674 {
675 LOG_FAIL_FMT("Failed to sign previous service identity: {}", e.what());
676 return false;
677 }
678
679 previous_identity_endorsement->put(endorsement);
680 return true;
681 }
682
683 static bool open_service(ccf::kv::Tx& tx)
684 {
685 auto* service = tx.rw<ccf::Service>(Tables::SERVICE);
686
687 auto active_recovery_participants_count =
689 auto active_recovery_owners_count = get_active_recovery_owners(tx).size();
690 if (
691 active_recovery_participants_count == 0 &&
692 active_recovery_owners_count != 0)
693 {
694 if (get_recovery_threshold(tx) > 1)
695 {
697 "Cannot open network as a network with only active recovery owners "
698 "({}) can have "
699 "a recovery threshold of 1 but current recovery threshold value is "
700 "({})",
701 active_recovery_owners_count,
703 }
704 }
705 else if (active_recovery_participants_count < get_recovery_threshold(tx))
706 {
708 "Cannot open network as number of active recovery members ({}) is "
709 "less than recovery threshold ({})",
710 active_recovery_participants_count,
712 return false;
713 }
714
715 auto active_service = service->get();
716 if (!active_service.has_value())
717 {
718 LOG_FAIL_FMT("Failed to get active service");
719 return false;
720 }
721
722 if (active_service->status == ServiceStatus::OPEN)
723 {
724 // If the service is already open, return with no effect
725 return true;
726 }
727
728 if (
729 active_service->status != ServiceStatus::OPENING &&
730 active_service->status != ServiceStatus::WAITING_FOR_RECOVERY_SHARES)
731 {
733 "Could not open current service: status is not OPENING or "
734 "WAITING_FOR_RECOVERY_SHARES");
735 return false;
736 }
737
738 active_service->status = ServiceStatus::OPEN;
739 service->put(active_service.value());
740
741 return true;
742 }
743
744 static std::optional<ServiceStatus> get_service_status(
746 {
747 auto* service = tx.ro<ccf::Service>(Tables::SERVICE);
748 auto active_service = service->get();
749 if (!active_service.has_value())
750 {
751 LOG_FAIL_FMT("Failed to get active service");
752 return {};
753 }
754
755 return active_service->status;
756 }
757
758 static void trust_node(
759 ccf::kv::Tx& tx,
760 const NodeId& node_id,
761 ccf::kv::Version latest_ledger_secret_seqno)
762 {
763 auto* nodes = tx.rw<ccf::Nodes>(Tables::NODES);
764 auto node_info = nodes->get(node_id);
765
766 if (!node_info.has_value())
767 {
768 throw std::logic_error(fmt::format("Node {} does not exist", node_id));
769 }
770
771 if (node_info->status == NodeStatus::RETIRED)
772 {
773 throw std::logic_error(fmt::format("Node {} is retired", node_id));
774 }
775
776 node_info->status = NodeStatus::TRUSTED;
777 node_info->ledger_secret_seqno = latest_ledger_secret_seqno;
778 nodes->put(node_id, node_info.value());
779
780 LOG_INFO_FMT("Node {} is now {}", node_id, node_info->status);
781 }
782
783 static void set_constitution(
784 ccf::kv::Tx& tx, const std::string& constitution)
785 {
786 tx.rw<ccf::Constitution>(Tables::CONSTITUTION)->put(constitution);
787 }
788
790 ccf::kv::Tx& tx,
791 const pal::PlatformAttestationMeasurement& node_measurement,
792 const QuoteFormat& platform)
793 {
794 switch (platform)
795 {
797 {
798 tx.wo<VirtualMeasurements>(Tables::NODE_VIRTUAL_MEASUREMENTS)
799 ->put(
801 node_measurement.data.begin(), node_measurement.data.end()),
803 break;
804 }
806 {
807 tx.wo<CodeIDs>(Tables::NODE_CODE_IDS)
808 ->put(
809 pal::SgxAttestationMeasurement(node_measurement),
811 break;
812 }
814 {
815 tx.wo<SnpMeasurements>(Tables::NODE_SNP_MEASUREMENTS)
816 ->put(
817 pal::SnpAttestationMeasurement(node_measurement),
819 break;
820 }
821 default:
822 {
823 throw std::logic_error(fmt::format(
824 "Unexpected quote format {} when trusting node code id", platform));
825 }
826 }
827 }
828
830 ccf::kv::Tx& tx, const HostData& host_data)
831 {
832 auto* host_data_table =
833 tx.wo<ccf::VirtualHostDataMap>(Tables::VIRTUAL_HOST_DATA);
834 host_data_table->insert(host_data);
835 }
836
838 ccf::kv::Tx& tx,
839 const HostData& host_data,
840 const std::optional<HostDataMetadata>& security_policy = std::nullopt)
841 {
842 auto* host_data_table = tx.wo<ccf::SnpHostDataMap>(Tables::HOST_DATA);
843 if (security_policy.has_value())
844 {
845 auto raw_security_policy =
846 ccf::crypto::raw_from_b64(security_policy.value());
847 host_data_table->put(
848 host_data, {raw_security_policy.begin(), raw_security_policy.end()});
849 }
850 else
851 {
852 LOG_TRACE_FMT("Trusting node with unset policy");
853 host_data_table->put(host_data, pal::snp::NO_SECURITY_POLICY);
854 }
855 }
856
858 ccf::kv::Tx& tx,
859 const std::optional<pal::UVMEndorsements>& uvm_endorsements)
860 {
861 if (!uvm_endorsements.has_value())
862 {
863 // UVM endorsements are optional
864 return;
865 }
866
867 auto* uvme =
868 tx.rw<ccf::SNPUVMEndorsements>(Tables::NODE_SNP_UVM_ENDORSEMENTS);
869 uvme->put(
870 uvm_endorsements->did,
871 {{uvm_endorsements->feed, {uvm_endorsements->svn}}});
872 }
873
875 ccf::kv::Tx& tx, pal::snp::Attestation& attestation)
876 {
877 if (attestation.version < pal::snp::minimum_attestation_version)
878 {
879 throw std::logic_error(fmt::format(
880 "SEV-SNP: attestation version {} is not supported. Minimum "
881 "supported version is {}",
882 attestation.version,
883 pal::snp::minimum_attestation_version));
884 }
885 // As cpuid -> attestation cpuid is surjective, we must use the local
886 // cpuid and validate it against the attestation's cpuid
887 auto cpuid = pal::snp::get_cpuid_untrusted();
888 if (
889 cpuid.get_family_id() != attestation.cpuid_fam_id ||
890 cpuid.get_model_id() != attestation.cpuid_mod_id ||
891 cpuid.stepping != attestation.cpuid_step)
892 {
893 throw std::runtime_error(fmt::format(
894 "CPU-sourced cpuid does not match attestation cpuid ({} != {}, {}, "
895 "{})",
896 cpuid.hex_str(),
897 attestation.cpuid_fam_id,
898 attestation.cpuid_mod_id,
899 attestation.cpuid_step));
900 }
901 auto* h = tx.wo<ccf::SnpTcbVersionMap>(Tables::SNP_TCB_VERSIONS);
902 auto product = pal::snp::get_sev_snp_product(cpuid);
903 h->put(cpuid.hex_str(), attestation.reported_tcb.to_policy(product));
904 }
905
907 ccf::kv::Tx& tx, const ServiceConfiguration& configuration)
908 {
909 auto* config = tx.rw<ccf::Configuration>(Tables::CONFIGURATION);
910 if (config->has())
911 {
912 throw std::logic_error(
913 "Cannot initialise service configuration: configuration already "
914 "exists");
915 }
916
917 config->put(configuration);
918 }
919
920 static bool set_recovery_threshold(ccf::kv::Tx& tx, size_t threshold)
921 {
922 auto* config = tx.rw<ccf::Configuration>(Tables::CONFIGURATION);
923
924 if (threshold == 0)
925 {
926 LOG_FAIL_FMT("Cannot set recovery threshold to 0");
927 return false;
928 }
929
930 auto service_status = get_service_status(tx);
931 if (!service_status.has_value())
932 {
933 LOG_FAIL_FMT("Failed to get active service");
934 return false;
935 }
936
937 if (service_status.value() == ServiceStatus::WAITING_FOR_RECOVERY_SHARES)
938 {
939 // While waiting for recovery shares, the recovery threshold cannot be
940 // modified. Otherwise, the threshold could be passed without triggering
941 // the end of recovery procedure
943 "Cannot set recovery threshold: service is currently waiting for "
944 "recovery shares");
945 return false;
946 }
947 if (service_status.value() == ServiceStatus::OPEN)
948 {
949 auto active_recovery_participants_count =
950 get_active_recovery_participants(tx).size();
951 auto active_recovery_owners_count =
952 get_active_recovery_owners(tx).size();
953
954 if (
955 active_recovery_owners_count != 0 &&
956 active_recovery_participants_count == 0)
957 {
958 if (threshold > 1)
959 {
961 "Cannot set recovery threshold to {} when only "
962 "active consortium members ({}) that are of type recovery owner "
963 "exist.",
964 threshold,
965 active_recovery_owners_count);
966 return false;
967 }
968 }
969 else if (threshold > active_recovery_participants_count)
970 {
972 "Cannot set recovery threshold to {} as it is greater than the "
973 "number of active recovery participant members ({})",
974 threshold,
975 active_recovery_participants_count);
976 return false;
977 }
978 }
979
980 auto current_config = config->get();
981 if (!current_config.has_value())
982 {
983 throw std::logic_error("Configuration should already be set");
984 }
985
986 current_config->recovery_threshold = threshold;
987 config->put(current_config.value());
988 return true;
989 }
990
992 {
993 auto* config = tx.ro<ccf::Configuration>(Tables::CONFIGURATION);
994 auto current_config = config->get();
995 if (!current_config.has_value())
996 {
997 throw std::logic_error(
998 "Failed to get recovery threshold: No active configuration found");
999 }
1000 return current_config->recovery_threshold;
1001 }
1002 };
1003}
Definition internal_tables_access.h:51
static bool is_service_created(ccf::kv::ReadOnlyTx &tx, const ccf::crypto::Pem &expected_service_cert)
Definition internal_tables_access.h:531
static std::map< MemberId, ccf::crypto::Pem > get_active_recovery_participants(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:123
static bool set_recovery_threshold(ccf::kv::Tx &tx, size_t threshold)
Definition internal_tables_access.h:920
static bool is_recovery_owner(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id)
Definition internal_tables_access.h:95
static std::optional< ServiceStatus > get_service_status(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:744
static bool open_service(ccf::kv::Tx &tx)
Definition internal_tables_access.h:683
static void init_configuration(ccf::kv::Tx &tx, const ServiceConfiguration &configuration)
Definition internal_tables_access.h:906
static std::map< NodeId, NodeInfo > get_trusted_nodes(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:438
static UserId add_user(ccf::kv::Tx &tx, const NewUser &new_user)
Definition internal_tables_access.h:388
static void trust_node_snp_host_data(ccf::kv::Tx &tx, const HostData &host_data, const std::optional< HostDataMetadata > &security_policy=std::nullopt)
Definition internal_tables_access.h:837
static bool remove_member(ccf::kv::Tx &tx, const MemberId &member_id)
Definition internal_tables_access.h:282
static MemberId add_member(ccf::kv::Tx &tx, const NewMember &member_pub_info)
Definition internal_tables_access.h:186
static void set_constitution(ccf::kv::Tx &tx, const std::string &constitution)
Definition internal_tables_access.h:783
static void trust_node_uvm_endorsements(ccf::kv::Tx &tx, const std::optional< pal::UVMEndorsements > &uvm_endorsements)
Definition internal_tables_access.h:857
static bool is_recovery_participant(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id)
Definition internal_tables_access.h:88
static std::map< MemberId, ccf::crypto::Pem > get_active_recovery_owners(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:154
static void create_service(ccf::kv::Tx &tx, const ccf::crypto::Pem &service_cert, ccf::TxID create_txid, nlohmann::json service_data=nullptr, bool recovering=false)
Definition internal_tables_access.h:467
static bool is_active_member(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id)
Definition internal_tables_access.h:109
static void trust_node_measurement(ccf::kv::Tx &tx, const pal::PlatformAttestationMeasurement &node_measurement, const QuoteFormat &platform)
Definition internal_tables_access.h:789
static void retire_active_nodes(ccf::kv::Tx &tx)
Definition internal_tables_access.h:57
static void add_node(ccf::kv::Tx &tx, const NodeId &id, const NodeInfo &node_info)
Definition internal_tables_access.h:431
static void trust_node_virtual_host_data(ccf::kv::Tx &tx, const HostData &host_data)
Definition internal_tables_access.h:829
static bool endorse_previous_identity(ccf::kv::Tx &tx, const ccf::crypto::ECKeyPair_OpenSSL &service_key)
Definition internal_tables_access.h:540
static bool activate_member(ccf::kv::Tx &tx, const MemberId &member_id)
Definition internal_tables_access.h:263
static void trust_node_snp_tcb_version(ccf::kv::Tx &tx, pal::snp::Attestation &attestation)
Definition internal_tables_access.h:874
static void trust_node(ccf::kv::Tx &tx, const NodeId &node_id, ccf::kv::Version latest_ledger_secret_seqno)
Definition internal_tables_access.h:758
static bool is_recovery_participant_or_owner(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id)
Definition internal_tables_access.h:78
static size_t get_recovery_threshold(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:991
static void remove_user(ccf::kv::Tx &tx, const UserId &user_id)
Definition internal_tables_access.h:421
Definition ec_key_pair.h:16
std::vector< uint8_t > public_key_der() const override
Definition ec_key_pair.cpp:127
Definition pem.h:18
Definition sha256_hash.h:16
std::string hex_str() const
Definition sha256_hash.cpp:57
Definition tx.h:159
M::ReadOnlyHandle * ro(M &m)
Definition tx.h:168
Definition tx.h:200
M::Handle * rw(M &m)
Definition tx.h:211
M::WriteOnlyHandle * wo(M &m)
Definition tx.h:232
Definition map.h:30
Definition set.h:33
Definition value.h:32
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_FAIL_FMT
Definition internal_logger.h:16
std::shared_ptr< COSEParametersFactory > cose_params_string_bytes(const std::string &key, std::span< const uint8_t > value)
Definition cose_sign.cpp:217
std::shared_ptr< COSEParametersFactory > cose_params_int_int(int64_t key, int64_t value)
Definition cose_sign.cpp:154
std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.cpp:12
std::shared_ptr< COSEParametersFactory > cose_params_string_string(const std::string &key, const std::string &value)
Definition cose_sign.cpp:191
VerifierPtr make_verifier(const std::vector< uint8_t > &cert)
Definition verifier.cpp:18
std::vector< std::shared_ptr< ccf::crypto::COSEParametersFactory > > COSEHeadersArray
Definition cose_sign.h:165
uint64_t Version
Definition version.h:8
std::string VirtualAttestationMeasurement
Definition measurement.h:97
Definition app_interface.h:14
ccf::TxID next_tx_if_recovery(ccf::TxID txid)
Definition internal_tables_access.h:38
ccf::TxID previous_tx_if_recovery(ccf::TxID txid)
Definition internal_tables_access.h:33
QuoteFormat
Definition quote_info.h:12
Definition previous_service_identity.h:18
std::vector< uint8_t > endorsing_key
Service key at the moment of endorsing.
Definition previous_service_identity.h:23
Definition members.h:128
Definition members.h:42
std::optional< MemberRecoveryRole > recovery_role
Definition members.h:49
std::optional< ccf::crypto::Pem > encryption_pub_key
Definition members.h:46
ccf::crypto::Pem cert
Definition members.h:43
nlohmann::json member_data
Definition members.h:47
Definition users.h:14
nlohmann::json user_data
Definition users.h:16
ccf::crypto::Pem cert
Definition users.h:15
Definition node_info.h:30
NodeStatus status
Node status.
Definition node_info.h:36
Definition service_config.h:14
Definition tx_id.h:44
SeqNo seqno
Definition tx_id.h:46
View view
Definition tx_id.h:45
Definition cose_sign.h:168
Definition measurement.h:17
Definition measurement.h:123
std::vector< uint8_t > data
Definition measurement.h:124
Definition attestation_sev_snp.h:387
TcbVersionRaw reported_tcb
Definition attestation_sev_snp.h:406
uint32_t version
Definition attestation_sev_snp.h:388
uint8_t cpuid_fam_id
Definition attestation_sev_snp.h:407
uint8_t cpuid_step
Definition attestation_sev_snp.h:409
uint8_t cpuid_mod_id
Definition attestation_sev_snp.h:408
TcbVersionPolicy to_policy(ProductName product) const
Definition attestation_sev_snp.h:252