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