171 std::shared_ptr<LedgerSecrets> ledger_secrets;
179 auto active_recovery_participants_info =
182 size_t share_index = 0;
183 for (
auto const& [member_id, enc_pub_key] :
184 active_recovery_participants_info)
187 auto raw_share = std::vector<uint8_t>(
188 shares[share_index].begin(), shares[share_index].end());
189 encrypted_shares[member_id] = member_enc_pubk->rsa_oaep_wrap(raw_share);
190 OPENSSL_cleanse(raw_share.data(), raw_share.size());
191 OPENSSL_cleanse(shares[share_index].data(), shares[share_index].size());
195 auto active_recovery_owners_info =
197 if (!active_recovery_owners_info.empty())
199 std::vector<uint8_t> full_share_serialised(
203 for (
auto const& [member_id, enc_pub_key] : active_recovery_owners_info)
206 encrypted_shares[member_id] =
207 member_enc_pubk->rsa_oaep_wrap(full_share_serialised);
211 full_share_serialised.data(), full_share_serialised.size());
214 return encrypted_shares;
217 void shuffle_recovery_shares(
220 auto active_recovery_participants_info =
222 auto active_recovery_owners_info =
224 size_t recovery_threshold =
228 active_recovery_participants_info.empty() &&
229 active_recovery_owners_info.empty())
231 throw std::logic_error(
232 "There should be at least one active recovery member to issue "
236 if (recovery_threshold == 0)
238 throw std::logic_error(
239 "Recovery threshold should be set before recovery "
240 "shares are computed");
243 size_t num_shares = 0;
244 if (!active_recovery_participants_info.empty())
246 if (recovery_threshold > active_recovery_participants_info.size())
248 throw std::logic_error(fmt::format(
249 "Recovery threshold {} should be equal to or less than the number "
250 "of active recovery members {}",
252 active_recovery_participants_info.size()));
255 num_shares = active_recovery_participants_info.size();
259 if (recovery_threshold > 1)
261 throw std::logic_error(fmt::format(
262 "Recovery threshold {} cannot be greater than 1 when the "
263 "consortium consists of only active recovery owner members ({})",
265 active_recovery_owners_info.size()));
271 auto ls_wrapping_key =
274 auto wrapped_latest_ls = ls_wrapping_key.
wrap(latest_ledger_secret);
276 recovery_shares->put(
278 compute_encrypted_shares(tx, ls_wrapping_key),
279 latest_ledger_secret->previous_secret_stored_version});
282 void set_recovery_shares_info(
285 const std::optional<VersionedLedgerSecret>& previous_ledger_secret =
287 std::optional<ccf::kv::Version> latest_ls_version = std::nullopt)
294 shuffle_recovery_shares(tx, latest_ledger_secret);
303 Tables::ENCRYPTED_PAST_LEDGER_SECRET);
305 std::vector<uint8_t> encrypted_previous_secret = {};
307 if (previous_ledger_secret.has_value())
309 version_previous_secret = previous_ledger_secret->first;
312 previous_ledger_secret->second->raw_key.size());
315 latest_ledger_secret->key->encrypt(
317 previous_ledger_secret->second->raw_key,
319 encrypted_previous_ls.
cipher,
320 encrypted_previous_ls.
hdr.
tag);
322 encrypted_previous_secret = encrypted_previous_ls.
serialise();
325 std::move(encrypted_previous_secret),
326 version_previous_secret,
327 encrypted_ls->get_version_of_previous_write()),
332 encrypted_ls->put({std::nullopt, latest_ls_version});
336 std::vector<uint8_t> encrypt_submitted_share(
337 const std::vector<uint8_t>& submitted_share,
345 current_ledger_secret->key->encrypt(
349 encrypted_submitted_share.
cipher,
350 encrypted_submitted_share.
hdr.
tag);
352 return encrypted_submitted_share.
serialise();
355 std::vector<uint8_t> decrypt_submitted_share(
356 const std::vector<uint8_t>& encrypted_submitted_share,
360 encrypted_share.
deserialise(encrypted_submitted_share);
361 std::vector<uint8_t> decrypted_share;
363 if (!current_ledger_secret->key->decrypt(
370 throw std::logic_error(
"Decrypting submitted shares failed");
373 return decrypted_share;
377 combine_from_encrypted_submitted_shares(
ccf::kv::Tx& tx)
380 Tables::ENCRYPTED_SUBMITTED_SHARES);
383 std::optional<ccf::crypto::sharing::Share> full_share;
384 std::vector<ccf::crypto::sharing::Share> new_shares = {};
385 encrypted_submitted_shares->foreach(
386 [&new_shares, &full_share, &tx,
this](
388 auto decrypted_share = decrypt_submitted_share(
389 encrypted_share, ledger_secrets->get_latest(tx).second);
390 switch (decrypted_share.size())
406 new_shares.emplace_back(decrypted_share);
412 OPENSSL_cleanse(decrypted_share.data(), decrypted_share.size());
413 throw std::logic_error(fmt::format(
414 "Error combining recovery shares: decrypted share of {} bytes "
415 "is not an {}-byte long new-style share.",
416 decrypted_share.size(),
420 OPENSSL_cleanse(decrypted_share.data(), decrypted_share.size());
421 return !full_share.has_value();
424 if (full_share.has_value())
426 return {full_share.value()};
429 auto num_shares = new_shares.size();
431 auto config_val = config->get();
432 if (!config_val.has_value())
434 throw std::logic_error(
"Configuration is not set");
436 auto recovery_threshold = config_val->recovery_threshold;
437 if (recovery_threshold > num_shares)
439 throw std::logic_error(fmt::format(
440 "Error combining recovery shares: only {} recovery shares were "
441 "submitted but recovery threshold is {}",
443 recovery_threshold));
446 return {std::move(new_shares), recovery_threshold};
451 ledger_secrets(ledger_secrets_)
462 auto [latest, penultimate] =
463 ledger_secrets->get_latest_and_penultimate(tx);
465 set_recovery_shares_info(tx, latest.second, penultimate, latest.first);
482 set_recovery_shares_info(
483 tx, new_ledger_secret, ledger_secrets->get_latest(tx));
494 shuffle_recovery_shares(tx, ledger_secrets->get_latest(tx).second);
500 auto recovery_shares_info =
502 if (!recovery_shares_info.has_value())
504 throw std::logic_error(
505 "Failed to retrieve current recovery shares info");
508 auto search = recovery_shares_info->encrypted_shares.find(member_id);
509 if (search == recovery_shares_info->encrypted_shares.end())
514 return search->second;
525 auto recovery_shares_info =
527 if (!recovery_shares_info.has_value())
529 throw std::logic_error(
530 "Failed to retrieve current recovery shares info");
534 combine_from_encrypted_submitted_shares(tx).
unwrap(
535 recovery_shares_info->wrapped_latest_ledger_secret);
537 tx, recovery_ledger_secrets, restored_ls);
545 if (recovery_ledger_secrets.empty())
547 throw std::logic_error(
"No recovery ledger secrets");
551 "Recovering {} encrypted ledger secrets",
552 recovery_ledger_secrets.size());
554 const auto& current_ledger_secret_version =
555 recovery_ledger_secrets.back().next_version;
556 if (!current_ledger_secret_version.has_value())
560 throw std::logic_error(
"Current ledger secret version should be set");
563 auto* encrypted_previous_ledger_secret =
565 Tables::ENCRYPTED_PAST_LEDGER_SECRET);
568 auto s = restored_ledger_secrets.emplace(
569 current_ledger_secret_version.value(),
570 std::make_shared<LedgerSecret>(
571 std::move(restored_ls->raw_key),
572 encrypted_previous_ledger_secret->get_version_of_previous_write()));
573 auto latest_ls = s.first->second;
575 for (
const auto& recovery_ledger_secret :
576 std::ranges::reverse_view(recovery_ledger_secrets))
578 if (!recovery_ledger_secret.previous_ledger_secret.has_value())
584 const auto& prev_secret =
585 recovery_ledger_secret.previous_ledger_secret.value();
588 "Recovering encrypted ledger secret valid at seqno {}",
589 prev_secret.version);
592 restored_ledger_secrets.find(prev_secret.version) !=
593 restored_ledger_secrets.end())
597 "Skipping, already decrypted ledger secret with version {}",
598 prev_secret.version);
603 latest_ls, prev_secret.encrypted_data);
605 auto secret = restored_ledger_secrets.emplace(
607 std::make_shared<LedgerSecret>(
608 std::move(decrypted_ls_raw),
609 prev_secret.previous_secret_stored_version));
610 latest_ls = secret.first->second;
613 return restored_ledger_secrets;
617 const std::vector<uint8_t>& submitted_recovery_share)
620 submitted_recovery_share.size() ==
637 const std::vector<uint8_t>& submitted_recovery_share)
641 Tables::ENCRYPTED_SUBMITTED_SHARES);
642 auto active_service = service->get();
643 if (!active_service.has_value())
645 throw std::logic_error(
"Failed to get active service");
648 encrypted_submitted_shares->put(
650 encrypt_submitted_share(
651 submitted_recovery_share, ledger_secrets->get_latest(tx).second));
653 return encrypted_submitted_shares->size();
659 Tables::ENCRYPTED_SUBMITTED_SHARES);
660 encrypted_submitted_shares->clear();