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