CCF
Loading...
Searching...
No Matches
ledger_secrets.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/pal/locking.h"
7#include "ccf/tx.h"
8#include "ds/ccf_assert.h"
10#include "kv/kv_types.h"
11#include "ledger_secret.h"
14
15#include <algorithm>
16#include <map>
17#include <optional>
18
19namespace ccf
20{
21 using LedgerSecretsMap = std::map<ccf::kv::Version, LedgerSecretPtr>;
22 using VersionedLedgerSecret = LedgerSecretsMap::value_type;
23
25 {
26 private:
27 ccf::pal::Mutex lock;
28 LedgerSecretsMap ledger_secrets;
29
30 // Set once when the LedgerSecrets are initialised. This prevents a backup
31 // node to rollback not-yet-applicable ledger secrets when catching up.
32 // All rollback that would result in the removal of some of these secrets
33 // would imply that the transaction that added the node itself was rolled
34 // back.
35 ccf::kv::Version initial_latest_ledger_secret_version = 0;
36
37 std::optional<LedgerSecretsMap::iterator> last_used_secret_it =
38 std::nullopt;
39
40 LedgerSecretPtr get_secret_for_version(
41 ccf::kv::Version version, bool historical_hint = false)
42 {
43 if (ledger_secrets.empty())
44 {
45 LOG_FAIL_FMT("Ledger secrets map is empty");
46 return nullptr;
47 }
48
49 if (!historical_hint && last_used_secret_it.has_value())
50 {
51 // Fast path for non-historical queries as both primary and backup nodes
52 // encrypt/decrypt transactions in order, it is sufficient to keep an
53 // iterator on the last used secret to access ledger secrets in constant
54 // time.
55 auto& last_used_secret_it_ = last_used_secret_it.value();
56 if (
57 std::next(last_used_secret_it_) != ledger_secrets.end() &&
58 version >= std::next(last_used_secret_it_)->first)
59 {
60 // Across a rekey, start using the next key
61 ++last_used_secret_it_;
62 }
63
64 return last_used_secret_it_->second;
65 }
66
67 // Slow path, e.g. for historical queries. The ledger secret used to
68 // encrypt/decrypt a transaction at a given version is the one with the
69 // highest version that is lower than the given version (e.g. if
70 // ledger_secrets contains two keys for version 0 and 10 then the key
71 // associated with version 0 is used for version [0..9] and version 10 for
72 // versions 10+)
73 auto search = std::upper_bound(
74 ledger_secrets.begin(),
75 ledger_secrets.end(),
76 version,
77 [](auto a, const auto& b) { return b.first > a; });
78
79 if (search == ledger_secrets.begin())
80 {
81 LOG_FAIL_FMT("Could not find ledger secret for seqno {}", version);
82 return nullptr;
83 }
84
85 if (!historical_hint)
86 {
87 // Only update the last secret iterator on non-historical queries so
88 // that the fast path is always preserved for transactions on the main
89 // store
90 last_used_secret_it = std::prev(search);
91 }
92
93 return std::prev(search)->second;
94 }
95
96 void take_dependency_on_secrets(ccf::kv::ReadOnlyTx& tx)
97 {
98 // Ledger secrets are not stored in the KV. Instead, they are
99 // cached in a unique LedgerSecrets instance that can be accessed
100 // without reading the KV. However, it is possible that the ledger
101 // secrets are updated (e.g. rekey tx) concurrently to their access by
102 // another tx. To prevent conflicts, accessing the ledger secrets
103 // require access to a tx object, which must take a dependency on the
104 // secrets table.
105 auto* secrets = tx.ro<Secrets>(Tables::ENCRYPTED_LEDGER_SECRETS);
106 secrets->get();
107 }
108
109 public:
110 LedgerSecrets() = default;
111
112 void init(ccf::kv::Version initial_version = 1)
113 {
114 std::lock_guard<ccf::pal::Mutex> guard(lock);
115
116 ledger_secrets.emplace(initial_version, make_ledger_secret());
117 initial_latest_ledger_secret_version = initial_version;
118 }
119
120 void init_from_map(LedgerSecretsMap&& ledger_secrets_)
121 {
122 std::lock_guard<ccf::pal::Mutex> guard(lock);
123
125 ledger_secrets.empty(), "Should only init an empty LedgerSecrets");
126
127 ledger_secrets = std::move(ledger_secrets_);
128 initial_latest_ledger_secret_version = ledger_secrets.rbegin()->first;
129 }
130
132 {
133 // To be able to lookup the last active ledger secret before the service
134 // crashed, the ledger secret created after the public recovery is
135 // complete should point to the version at which the past ledger secret
136 // has just been written to the store. This can only be done once the
137 // private recovery is complete.
138 std::lock_guard<ccf::pal::Mutex> guard(lock);
139
140 if (ledger_secrets.empty())
141 {
142 throw std::logic_error(
143 "There should be at least one ledger secret to adjust");
144 }
145
146 ledger_secrets.rbegin()->second->previous_secret_stored_version = version;
147 }
148
149 bool is_empty()
150 {
151 std::lock_guard<ccf::pal::Mutex> guard(lock);
152
153 return ledger_secrets.empty();
154 }
155
157 {
158 // This does not need a transaction as the first ledger secret is
159 // considered stable with regards to concurrent rekey transactions
160 std::lock_guard<ccf::pal::Mutex> guard(lock);
161
162 if (ledger_secrets.empty())
163 {
164 throw std::logic_error(
165 "Could not retrieve first ledger secret: no secret set");
166 }
167
168 return *ledger_secrets.begin();
169 }
170
172 {
173 std::lock_guard<ccf::pal::Mutex> guard(lock);
174
175 take_dependency_on_secrets(tx);
176
177 if (ledger_secrets.empty())
178 {
179 throw std::logic_error(
180 "Could not retrieve latest ledger secret: no secret set");
181 }
182
183 return *ledger_secrets.rbegin();
184 }
185
186 std::pair<VersionedLedgerSecret, std::optional<VersionedLedgerSecret>>
188 {
189 std::lock_guard<ccf::pal::Mutex> guard(lock);
190
191 take_dependency_on_secrets(tx);
192
193 if (ledger_secrets.empty())
194 {
195 throw std::logic_error(
196 "Could not retrieve latest ledger secret: no secret set");
197 }
198
199 const auto& latest_ledger_secret = ledger_secrets.rbegin();
200 if (ledger_secrets.size() < 2)
201 {
202 return std::make_pair(*latest_ledger_secret, std::nullopt);
203 }
204 return std::make_pair(
205 *latest_ledger_secret, *std::next(latest_ledger_secret));
206 }
207
210 std::optional<ccf::kv::Version> up_to = std::nullopt)
211 {
212 std::lock_guard<ccf::pal::Mutex> guard(lock);
213
214 take_dependency_on_secrets(tx);
215
216 if (!up_to.has_value())
217 {
218 return ledger_secrets;
219 }
220
221 auto search = ledger_secrets.find(up_to.value());
222 if (search == ledger_secrets.end())
223 {
224 throw std::logic_error(
225 fmt::format("No ledger secrets at {}", up_to.has_value()));
226 }
227
228 return {ledger_secrets.begin(), ++search};
229 }
230
231 void restore_historical(LedgerSecretsMap&& restored_ledger_secrets)
232 {
233 std::lock_guard<ccf::pal::Mutex> guard(lock);
234
235 if (
236 !ledger_secrets.empty() && !restored_ledger_secrets.empty() &&
237 restored_ledger_secrets.rbegin()->first >=
238 ledger_secrets.begin()->first)
239 {
240 throw std::logic_error(fmt::format(
241 "Last restored version {} is greater than first existing version "
242 "{}",
243 restored_ledger_secrets.rbegin()->first,
244 ledger_secrets.begin()->first));
245 }
246
247 ledger_secrets.merge(restored_ledger_secrets);
248 }
249
250 std::shared_ptr<ccf::crypto::KeyAesGcm> get_encryption_key_for(
251 ccf::kv::Version version, bool historical_hint = false)
252 {
253 std::lock_guard<ccf::pal::Mutex> guard(lock);
254 auto ls = get_secret_for_version(version, historical_hint);
255 if (ls == nullptr)
256 {
257 return nullptr;
258 }
259 return ls->key;
260 }
261
263 ccf::kv::Version version, bool historical_hint = false)
264 {
265 std::lock_guard<ccf::pal::Mutex> guard(lock);
266 return get_secret_for_version(version, historical_hint);
267 }
268
270 {
271 std::lock_guard<ccf::pal::Mutex> guard(lock);
272
274 ledger_secrets.find(version) == ledger_secrets.end(),
275 "Ledger secret at seqno {} already exists",
276 version);
277
278 ledger_secrets.emplace(version, std::move(secret));
279
280 LOG_INFO_FMT("Added new ledger secret at seqno {}", version);
281 }
282
284 {
285 std::lock_guard<ccf::pal::Mutex> guard(lock);
286 if (ledger_secrets.empty())
287 {
288 return;
289 }
290
291 if (version < ledger_secrets.begin()->first)
292 {
294 "Cannot rollback ledger secrets at {}: first secret is at {}",
295 version,
296 ledger_secrets.begin()->first);
297 return;
298 }
299
300 while (ledger_secrets.size() > 1)
301 {
302 auto k = ledger_secrets.rbegin();
303 if (
304 k->first <= version ||
305 k->first <= initial_latest_ledger_secret_version)
306 {
307 break;
308 }
309
310 LOG_TRACE_FMT("Rollback ledger secrets at seqno {}", k->first);
311 ledger_secrets.erase(k->first);
312 }
313
314 // Invalidate last used ledger secret iterator. Next key usage will need
315 // to find the appropriate key on the slow path.
316 last_used_secret_it = std::nullopt;
317 }
318 };
319}
#define CCF_ASSERT_FMT(expr,...)
Definition ccf_assert.h:10
Definition ledger_secrets.h:25
void init_from_map(LedgerSecretsMap &&ledger_secrets_)
Definition ledger_secrets.h:120
LedgerSecrets()=default
void set_secret(ccf::kv::Version version, LedgerSecretPtr &&secret)
Definition ledger_secrets.h:269
void rollback(ccf::kv::Version version)
Definition ledger_secrets.h:283
std::shared_ptr< ccf::crypto::KeyAesGcm > get_encryption_key_for(ccf::kv::Version version, bool historical_hint=false)
Definition ledger_secrets.h:250
std::pair< VersionedLedgerSecret, std::optional< VersionedLedgerSecret > > get_latest_and_penultimate(ccf::kv::ReadOnlyTx &tx)
Definition ledger_secrets.h:187
void adjust_previous_secret_stored_version(ccf::kv::Version version)
Definition ledger_secrets.h:131
bool is_empty()
Definition ledger_secrets.h:149
void init(ccf::kv::Version initial_version=1)
Definition ledger_secrets.h:112
VersionedLedgerSecret get_first()
Definition ledger_secrets.h:156
void restore_historical(LedgerSecretsMap &&restored_ledger_secrets)
Definition ledger_secrets.h:231
VersionedLedgerSecret get_latest(ccf::kv::ReadOnlyTx &tx)
Definition ledger_secrets.h:171
LedgerSecretsMap get(ccf::kv::ReadOnlyTx &tx, std::optional< ccf::kv::Version > up_to=std::nullopt)
Definition ledger_secrets.h:208
LedgerSecretPtr get_secret_for(ccf::kv::Version version, bool historical_hint=false)
Definition ledger_secrets.h:262
Definition tx.h:159
M::ReadOnlyHandle * ro(M &m)
Definition tx.h:168
Definition value.h:32
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
#define LOG_FAIL_FMT
Definition internal_logger.h:16
uint64_t Version
Definition version.h:8
std::mutex Mutex
Definition locking.h:12
Definition app_interface.h:14
std::map< ccf::kv::Version, LedgerSecretPtr > LedgerSecretsMap
Definition ledger_secrets.h:21
LedgerSecretPtr make_ledger_secret()
Definition ledger_secret.h:81
LedgerSecretsMap::value_type VersionedLedgerSecret
Definition ledger_secrets.h:22
std::shared_ptr< LedgerSecret > LedgerSecretPtr
Definition ledger_secret.h:79