CCF
Loading...
Searching...
No Matches
recovery.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
9
10namespace ccf::gov::endpoints
11{
14 ShareManager& share_manager,
15 ccf::AbstractNodeContext& node_context)
16 {
17 auto get_encrypted_share_for_member =
18 [&](auto& ctx, ApiVersion api_version) {
19 switch (api_version)
20 {
22 case ApiVersion::v1:
23 default:
24 {
25 ccf::MemberId member_id;
26 if (!detail::try_parse_member_id(ctx.rpc_ctx, member_id))
27 {
28 return;
29 }
30
31 auto encrypted_share =
32 ShareManager::get_encrypted_share(ctx.tx, member_id);
33
34 if (!encrypted_share.has_value())
35 {
36 detail::set_gov_error(
37 ctx.rpc_ctx,
38 HTTP_STATUS_NOT_FOUND,
39 ccf::errors::ResourceNotFound,
40 fmt::format(
41 "Recovery share not found for member {}.", member_id));
42 return;
43 }
44
45 auto response_body = nlohmann::json::object();
46 response_body["memberId"] = member_id;
47 response_body["encryptedShare"] =
48 ccf::crypto::b64_from_raw(encrypted_share.value());
49
50 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
51 return;
52 }
53 }
54 };
55 registry
57 "/recovery/encrypted-shares/{memberId}",
58 HTTP_GET,
59 api_version_adapter(get_encrypted_share_for_member),
60 ccf::no_auth_required)
62 .install();
63
64 auto submit_recovery_share = [&](auto& ctx, ApiVersion api_version) {
65 switch (api_version)
66 {
68 case ApiVersion::v1:
69 default:
70 {
71 if (
74 {
75 detail::set_gov_error(
76 ctx.rpc_ctx,
77 HTTP_STATUS_FORBIDDEN,
78 errors::ServiceNotWaitingForRecoveryShares,
79 "Service is not waiting for recovery shares.");
80 return;
81 }
82
83 auto node_operation =
85 if (node_operation == nullptr)
86 {
87 detail::set_gov_error(
88 ctx.rpc_ctx,
89 HTTP_STATUS_INTERNAL_SERVER_ERROR,
90 ccf::errors::InternalError,
91 "Could not access NodeOperation subsystem.");
92 return;
93 }
94
95 if (node_operation->is_reading_private_ledger())
96 {
97 detail::set_gov_error(
98 ctx.rpc_ctx,
99 HTTP_STATUS_FORBIDDEN,
100 errors::NodeAlreadyRecovering,
101 "Node is already recovering private ledger.");
102 return;
103 }
104
105 ccf::MemberId member_id;
106 if (!detail::try_parse_member_id(ctx.rpc_ctx, member_id))
107 {
108 return;
109 }
110
111 const auto& cose_ident =
112 ctx.template get_caller<ccf::MemberCOSESign1AuthnIdentity>();
113
114 auto params = nlohmann::json::parse(cose_ident.content);
115 if (cose_ident.member_id != member_id)
116 {
117 detail::set_gov_error(
118 ctx.rpc_ctx,
119 HTTP_STATUS_BAD_REQUEST,
120 ccf::errors::InvalidAuthenticationInfo,
121 fmt::format(
122 "Member ID from path parameter ({}) does not match "
123 "member ID from body signature ({}).",
124 member_id,
125 cose_ident.member_id));
126 return;
127 }
128
129 auto raw_recovery_share = ccf::crypto::raw_from_b64(
130 params["share"].template get<std::string>());
131
132 size_t submitted_shares_count = 0;
133 bool full_key_submitted = false;
134 try
135 {
136 submitted_shares_count = share_manager.submit_recovery_share(
137 ctx.tx, member_id, raw_recovery_share);
138
139 full_key_submitted = ShareManager::is_full_key(raw_recovery_share);
140
141 OPENSSL_cleanse(
142 raw_recovery_share.data(), raw_recovery_share.size());
143 }
144 catch (const std::exception& e)
145 {
146 OPENSSL_cleanse(
147 raw_recovery_share.data(), raw_recovery_share.size());
148
149 constexpr auto error_msg = "Error submitting recovery shares.";
150 GOV_FAIL_FMT(error_msg);
151 GOV_DEBUG_FMT("Error: {}", e.what());
152 detail::set_gov_error(
153 ctx.rpc_ctx,
154 HTTP_STATUS_INTERNAL_SERVER_ERROR,
155 errors::InternalError,
156 error_msg);
157 return;
158 }
159
160 const auto threshold =
162
163 std::string message;
164 if (full_key_submitted)
165 {
166 message = "Full recovery key successfully submitted";
167 }
168 else
169 {
170 // Same format of message, whether this is sufficient to trigger
171 // recovery or not
172 message = fmt::format(
173 "{}/{} recovery shares successfully submitted",
174 submitted_shares_count,
175 threshold);
176 }
177
178 if (submitted_shares_count >= threshold || full_key_submitted)
179 {
180 message += "\nEnd of recovery procedure initiated";
181 GOV_INFO_FMT("{} - initiating recovery", message);
182
183 // Initiate recovery
184 try
185 {
186 node_operation->initiate_private_recovery(ctx.tx);
187 }
188 catch (const std::exception& e)
189 {
190 // Clear the submitted shares if combination fails so that members
191 // can start over.
192 constexpr auto error_msg = "Failed to initiate private recovery.";
193 GOV_FAIL_FMT(error_msg);
194 GOV_DEBUG_FMT("Error: {}", e.what());
196 ctx.rpc_ctx->set_apply_writes(true);
197 detail::set_gov_error(
198 ctx.rpc_ctx,
199 HTTP_STATUS_INTERNAL_SERVER_ERROR,
200 errors::InternalError,
201 error_msg);
202 return;
203 }
204 }
205
206 auto response_body = nlohmann::json::object();
207 response_body["message"] = message;
208 response_body["submittedCount"] = submitted_shares_count;
209 response_body["recoveryThreshold"] = threshold;
210 response_body["fullKeySubmitted"] = full_key_submitted;
211
212 ctx.rpc_ctx->set_response_json(response_body, HTTP_STATUS_OK);
213 return;
214 }
215 }
216 };
217 registry
219 "/recovery/members/{memberId}:recover",
220 HTTP_POST,
221 api_version_adapter(submit_recovery_share),
223 .set_openapi_hidden(true)
224 .install();
225 }
226}
Definition node_operation_interface.h:23
Definition base_endpoint_registry.h:121
static std::optional< ServiceStatus > get_service_status(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:692
static size_t get_recovery_threshold(ccf::kv::ReadOnlyTx &tx)
Definition internal_tables_access.h:973
Definition share_manager.h:167
static std::optional< EncryptedShare > get_encrypted_share(ccf::kv::ReadOnlyTx &tx, const MemberId &member_id)
Definition share_manager.h:492
size_t submit_recovery_share(ccf::kv::Tx &tx, MemberId member_id, const std::vector< uint8_t > &submitted_recovery_share)
Definition share_manager.h:627
static bool is_full_key(const std::vector< uint8_t > &submitted_recovery_share)
Definition share_manager.h:609
static void clear_submitted_recovery_shares(ccf::kv::Tx &tx)
Definition share_manager.h:649
virtual Endpoint make_endpoint(const std::string &method, RESTVerb verb, const EndpointFunction &f, const AuthnPolicies &ap)
Definition endpoint_registry.cpp:204
virtual Endpoint make_read_only_endpoint(const std::string &method, RESTVerb verb, const ReadOnlyEndpointFunction &f, const AuthnPolicies &ap)
Definition endpoint_registry.cpp:235
#define GOV_FAIL_FMT
Definition gov_logging.h:11
#define GOV_DEBUG_FMT
Definition gov_logging.h:8
#define GOV_INFO_FMT
Definition gov_logging.h:10
std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.cpp:12
std::string b64_from_raw(const uint8_t *data, size_t size)
Definition base64.cpp:41
bool try_parse_member_id(const std::shared_ptr< ccf::RpcContext > &rpc_ctx, ccf::MemberId &member_id)
Definition helpers.h:63
AuthnPolicies active_member_sig_only_policies(const std::string &gov_msg_type)
Definition helpers.h:16
Definition api_version.h:11
void init_recovery_handlers(ccf::BaseEndpointRegistry &registry, ShareManager &share_manager, ccf::AbstractNodeContext &node_context)
Definition recovery.h:12
auto api_version_adapter(Fn &&f, ApiVersion min_accepted=ApiVersion::MIN)
Definition api_version.h:101
ApiVersion
Definition api_version.h:13
Definition node_context.h:12
std::shared_ptr< T > get_subsystem(const std::string &name) const
Definition node_context.h:37
Endpoint & set_openapi_hidden(bool hidden)
Definition endpoint.cpp:10
void install()
Definition endpoint.cpp:122