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