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