CCF
Loading...
Searching...
No Matches
enclave_lfs_access.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/crypto/sha256.h"
8#include "ccf/ds/hex.h"
9#include "ccf/pal/locking.h"
10#include "ds/messaging.h"
13
14#include <optional>
15#include <set>
16#include <unordered_map>
17
18// Uncomment to disable encryption and obfuscation, writing cache content
19// directly unencrypted to host disk
20// #define PLAINTEXT_CACHE
21
22#if defined(PLAINTEXT_CACHE)
23# pragma message( \
24 "PLAINTEXT_CACHE should only be used for debugging, and not enabled for enclave builds")
25#endif
26
27namespace ccf::indexing
28{
29 static inline bool verify_and_decrypt(
30 ccf::crypto::KeyAesGcm& encryption_key,
31 const LFSKey& key,
32 LFSEncryptedContents&& encrypted,
33 std::vector<uint8_t>& plaintext)
34 {
36 gcm.deserialise(encrypted);
37
38#ifdef PLAINTEXT_CACHE
39 plaintext = gcm.cipher;
40 auto success = true;
41#else
42 auto success = encryption_key.decrypt(
43 gcm.hdr.get_iv(), gcm.hdr.tag, gcm.cipher, {}, plaintext);
44#endif
45
46 // Check key prefix in plaintext
47 {
48 const auto encoded_prefix_size = sizeof(key.size()) + key.size();
49 if (plaintext.size() < encoded_prefix_size)
50 {
51 return false;
52 }
53
54 auto data = (const uint8_t*)plaintext.data();
55 auto size = plaintext.size();
56 const auto prefix_size = serialized::read<size_t>(data, size);
57 if (prefix_size != key.size())
58 {
59 success = false;
60 }
61 else
62 {
63 if (memcmp(data, key.data(), key.size()) != 0)
64 {
65 success = false;
66 }
67
68 plaintext.erase(
69 plaintext.begin(), plaintext.begin() + encoded_prefix_size);
70 }
71 }
72
73 if (success)
74 {
75 return true;
76 }
77 else
78 {
79 LOG_TRACE_FMT("Decryption failed for {}", key);
80 return false;
81 }
82 }
83
85 {
86 protected:
87 using PendingResult = std::weak_ptr<FetchResult>;
88
89 std::unordered_map<LFSKey, PendingResult> pending;
91
93
95 std::unique_ptr<ccf::crypto::KeyAesGcm> encryption_key;
96
98 {
99 // Prefix the contents with the key, to be checked during decryption
100 {
101 std::vector<uint8_t> key_prefix(sizeof(key.size()) + key.size());
102 auto data = key_prefix.data();
103 auto size = key_prefix.size();
104 serialized::write(data, size, key);
105 contents.insert(contents.begin(), key_prefix.begin(), key_prefix.end());
106 }
107
108 ccf::crypto::GcmCipher gcm(contents.size());
109
110 // Use a random IV for each call
111 gcm.hdr.set_random_iv();
112
113 encryption_key->encrypt(
114 gcm.hdr.get_iv(), contents, {}, gcm.cipher, gcm.hdr.tag);
115
116#ifdef PLAINTEXT_CACHE
117 gcm.cipher = contents;
118#endif
119
120 return gcm.serialise();
121 }
122
123 public:
125 to_host(writer),
126 entropy_src(ccf::crypto::get_entropy())
127 {
128 // Generate a fresh random key. Only this specific instance, in this
129 // enclave, can read these files!
132 }
133
136 {
138 dispatcher, LFSMsg::response, [this](const uint8_t* data, size_t size) {
139 auto [obfuscated, encrypted] =
140 ringbuffer::read_message<LFSMsg::response>(data, size);
141 std::lock_guard<ccf::pal::Mutex> guard(pending_access);
142 auto it = pending.find(obfuscated);
143 if (it != pending.end())
144 {
145 auto result = it->second.lock();
146 if (result != nullptr)
147 {
148 if (result->fetch_result == FetchResult::Fetching)
149 {
150 const auto success = verify_and_decrypt(
152 obfuscated,
153 std::move(encrypted),
154 result->contents);
155 if (success)
156 {
157 result->fetch_result = FetchResult::Loaded;
158 }
159 else
160 {
161 result->fetch_result = FetchResult::Corrupt;
163 "Cache was given invalid contents for {} (aka {})",
164 obfuscated,
165 result->key);
166 }
167 }
168 else
169 {
171 "Retained result for {} (aka {}) in state {}",
172 obfuscated,
173 result->key,
174 result->fetch_result);
175 }
176 }
177 else
178 {
180 "Received response for {}, but caller has already dropped "
181 "result",
182 obfuscated);
183 }
184 pending.erase(it);
185 }
186 else
187 {
189 "Ignoring response message for unrequested key {}", obfuscated);
190 }
191 });
192
194 dispatcher,
195 LFSMsg::not_found,
196 [this](const uint8_t* data, size_t size) {
197 auto [obfuscated] =
198 ringbuffer::read_message<LFSMsg::not_found>(data, size);
199 std::lock_guard<ccf::pal::Mutex> guard(pending_access);
200 auto it = pending.find(obfuscated);
201 if (it != pending.end())
202 {
203 auto result = it->second.lock();
204 if (result != nullptr)
205 {
206 if (result->fetch_result == FetchResult::Fetching)
207 {
209 "Host has no contents for key {} (aka {})",
210 obfuscated,
211 result->key);
212 result->fetch_result = FetchResult::NotFound;
213 }
214 else
215 {
217 "Retained result for {} (aka {}) in state {}",
218 obfuscated,
219 result->key,
220 result->fetch_result);
221 }
222 }
223 else
224 {
226 "Received not_found for {}, but caller has already dropped "
227 "result",
228 obfuscated);
229 }
230 pending.erase(it);
231 }
232 else
233 {
235 "Ignoring not_found message for unrequested key {}", obfuscated);
236 }
237 });
238 }
239
240 static inline LFSKey obfuscate_key(const LFSKey& key)
241 {
242#ifdef PLAINTEXT_CACHE
243 return key;
244#else
245 const auto h =
246 ccf::crypto::sha256((const uint8_t*)key.data(), key.size());
247 return ds::to_hex(h);
248#endif
249 }
250
251 void store(const LFSKey& key, LFSContents&& contents) override
252 {
253 const auto obfuscated = obfuscate_key(key);
255 LFSMsg::store,
256 to_host,
257 // To avoid leaking potentially confidential information to the host,
258 // all cached data is encrypted and stored at an obfuscated key
259 obfuscated,
260 encrypt(obfuscated, std::move(contents)));
261 }
262
263 FetchResultPtr fetch(const LFSKey& key) override
264 {
265 const auto obfuscated = obfuscate_key(key);
266 std::lock_guard<ccf::pal::Mutex> guard(pending_access);
267 auto it = pending.find(obfuscated);
268
269 FetchResultPtr result;
270
271 if (it != pending.end())
272 {
273 result = it->second.lock();
274 if (result != nullptr)
275 {
276 if (key != result->key)
277 {
278 throw std::runtime_error(fmt::format(
279 "Obfuscation collision for unique keys '{}' and '{}', both "
280 "obfuscated to '{}'",
281 key,
282 result->key,
283 obfuscated));
284 }
285
286 return result;
287 }
288 else
289 {
290 result = std::make_shared<FetchResult>();
291 it->second = result;
292 }
293 }
294 else
295 {
296 result = std::make_shared<FetchResult>();
297 pending.emplace(obfuscated, result);
298 }
299
300 result->fetch_result = FetchResult::Fetching;
301 result->key = key;
302 RINGBUFFER_WRITE_MESSAGE(LFSMsg::get, to_host, obfuscated);
303 return result;
304 }
305 };
306}
Definition symmetric_key.h:70
virtual bool decrypt(std::span< const uint8_t > iv, const uint8_t tag[GCM_SIZE_TAG], std::span< const uint8_t > cipher, std::span< const uint8_t > aad, std::vector< uint8_t > &plain) const =0
Definition lfs_interface.h:40
Definition enclave_lfs_access.h:85
std::unique_ptr< ccf::crypto::KeyAesGcm > encryption_key
Definition enclave_lfs_access.h:95
std::weak_ptr< FetchResult > PendingResult
Definition enclave_lfs_access.h:87
EnclaveLFSAccess(const ringbuffer::WriterPtr &writer)
Definition enclave_lfs_access.h:124
void store(const LFSKey &key, LFSContents &&contents) override
Definition enclave_lfs_access.h:251
ringbuffer::WriterPtr to_host
Definition enclave_lfs_access.h:92
FetchResultPtr fetch(const LFSKey &key) override
Definition enclave_lfs_access.h:263
std::unordered_map< LFSKey, PendingResult > pending
Definition enclave_lfs_access.h:89
ccf::pal::Mutex pending_access
Definition enclave_lfs_access.h:90
ccf::crypto::EntropyPtr entropy_src
Definition enclave_lfs_access.h:94
void register_message_handlers(messaging::Dispatcher< ringbuffer::Message > &dispatcher)
Definition enclave_lfs_access.h:134
LFSEncryptedContents encrypt(const LFSKey &key, LFSContents &&contents)
Definition enclave_lfs_access.h:97
static LFSKey obfuscate_key(const LFSKey &key)
Definition enclave_lfs_access.h:240
Definition messaging.h:38
#define LOG_TRACE_FMT
Definition logger.h:356
#define LOG_FAIL_FMT
Definition logger.h:363
#define DISPATCHER_SET_MESSAGE_HANDLER(DISP, MSG,...)
Definition messaging.h:316
std::unique_ptr< KeyAesGcm > make_key_aes_gcm(std::span< const uint8_t > rawKey)
Free function implementation.
Definition symmetric_key.cpp:102
std::shared_ptr< Entropy > EntropyPtr
Definition entropy.h:33
HashBytes sha256(const std::span< uint8_t const > &data)
Definition hash.cpp:24
constexpr size_t GCM_DEFAULT_KEY_SIZE
Definition symmetric_key.h:12
Definition indexer_interface.h:14
std::string LFSKey
Definition lfs_interface.h:17
std::vector< uint8_t > LFSContents
Definition lfs_interface.h:19
std::shared_ptr< FetchResult > FetchResultPtr
Definition lfs_interface.h:37
std::vector< uint8_t > LFSEncryptedContents
Definition lfs_interface.h:18
uint8_t * key
Definition kv_helpers.h:78
std::mutex Mutex
Definition locking.h:12
Definition app_interface.h:14
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:150
void write(uint8_t *&data, size_t &size, const T &v)
Definition serialized.h:106
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:255
void set_random_iv(EntropyPtr entropy=ccf::crypto::get_entropy())
Definition symmetric_key.h:47
Definition symmetric_key.h:57
void deserialise(const std::vector< uint8_t > &serial)
Definition symmetric_key.cpp:93
std::vector< uint8_t > serialise()
Definition symmetric_key.cpp:78
StandardGcmHeader hdr
Definition symmetric_key.h:58
std::vector< uint8_t > cipher
Definition symmetric_key.h:59
uint8_t tag[GCM_SIZE_TAG]
Definition symmetric_key.h:18
std::span< const uint8_t > get_iv() const
Definition symmetric_key.cpp:34
@ Fetching
Definition lfs_interface.h:25
@ NotFound
Definition lfs_interface.h:27
@ Corrupt
Definition lfs_interface.h:28
@ Loaded
Definition lfs_interface.h:26