/src/rocksdb/env/env_encryption.cc
Line | Count | Source |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | |
6 | | #include "rocksdb/env_encryption.h" |
7 | | |
8 | | #include <algorithm> |
9 | | #include <cassert> |
10 | | #include <cctype> |
11 | | #include <iostream> |
12 | | |
13 | | #include "env/composite_env_wrapper.h" |
14 | | #include "env/env_encryption_ctr.h" |
15 | | #include "monitoring/perf_context_imp.h" |
16 | | #include "rocksdb/convenience.h" |
17 | | #include "rocksdb/io_status.h" |
18 | | #include "rocksdb/system_clock.h" |
19 | | #include "rocksdb/utilities/customizable_util.h" |
20 | | #include "rocksdb/utilities/options_type.h" |
21 | | #include "util/aligned_buffer.h" |
22 | | #include "util/coding.h" |
23 | | #include "util/random.h" |
24 | | #include "util/string_util.h" |
25 | | |
26 | | namespace ROCKSDB_NAMESPACE { |
27 | | std::shared_ptr<EncryptionProvider> EncryptionProvider::NewCTRProvider( |
28 | 0 | const std::shared_ptr<BlockCipher>& cipher) { |
29 | 0 | return std::make_shared<CTREncryptionProvider>(cipher); |
30 | 0 | } |
31 | | |
32 | | IOStatus EncryptedSequentialFile::Read(size_t n, const IOOptions& options, |
33 | | Slice* result, char* scratch, |
34 | 0 | IODebugContext* dbg) { |
35 | 0 | assert(scratch); |
36 | 0 | IOStatus io_s = file_->Read(n, options, result, scratch, dbg); |
37 | 0 | if (!io_s.ok()) { |
38 | 0 | return io_s; |
39 | 0 | } |
40 | 0 | { |
41 | 0 | PERF_TIMER_GUARD(decrypt_data_nanos); |
42 | 0 | io_s = status_to_io_status( |
43 | 0 | stream_->Decrypt(offset_, (char*)result->data(), result->size())); |
44 | 0 | } |
45 | 0 | if (io_s.ok()) { |
46 | 0 | offset_ += result->size(); // We've already read data from disk, so update |
47 | | // offset_ even if decryption fails. |
48 | 0 | } |
49 | 0 | return io_s; |
50 | 0 | } |
51 | | |
52 | 0 | IOStatus EncryptedSequentialFile::Skip(uint64_t n) { |
53 | 0 | auto status = file_->Skip(n); |
54 | 0 | if (!status.ok()) { |
55 | 0 | return status; |
56 | 0 | } |
57 | 0 | offset_ += n; |
58 | 0 | return status; |
59 | 0 | } |
60 | | |
61 | 0 | bool EncryptedSequentialFile::use_direct_io() const { |
62 | 0 | return file_->use_direct_io(); |
63 | 0 | } |
64 | | |
65 | 0 | size_t EncryptedSequentialFile::GetRequiredBufferAlignment() const { |
66 | 0 | return file_->GetRequiredBufferAlignment(); |
67 | 0 | } |
68 | | |
69 | | IOStatus EncryptedSequentialFile::InvalidateCache(size_t offset, |
70 | 0 | size_t length) { |
71 | 0 | return file_->InvalidateCache(offset + prefixLength_, length); |
72 | 0 | } |
73 | | |
74 | | IOStatus EncryptedSequentialFile::PositionedRead(uint64_t offset, size_t n, |
75 | | const IOOptions& options, |
76 | | Slice* result, char* scratch, |
77 | 0 | IODebugContext* dbg) { |
78 | 0 | assert(scratch); |
79 | 0 | offset += prefixLength_; // Skip prefix |
80 | 0 | auto io_s = file_->PositionedRead(offset, n, options, result, scratch, dbg); |
81 | 0 | if (!io_s.ok()) { |
82 | 0 | return io_s; |
83 | 0 | } |
84 | 0 | offset_ = offset + result->size(); |
85 | 0 | { |
86 | 0 | PERF_TIMER_GUARD(decrypt_data_nanos); |
87 | 0 | io_s = status_to_io_status( |
88 | 0 | stream_->Decrypt(offset, (char*)result->data(), result->size())); |
89 | 0 | } |
90 | 0 | return io_s; |
91 | 0 | } |
92 | | |
93 | | IOStatus EncryptedRandomAccessFile::Read(uint64_t offset, size_t n, |
94 | | const IOOptions& options, |
95 | | Slice* result, char* scratch, |
96 | 0 | IODebugContext* dbg) const { |
97 | 0 | assert(scratch); |
98 | 0 | offset += prefixLength_; |
99 | 0 | auto io_s = file_->Read(offset, n, options, result, scratch, dbg); |
100 | 0 | if (!io_s.ok()) { |
101 | 0 | return io_s; |
102 | 0 | } |
103 | 0 | { |
104 | 0 | PERF_TIMER_GUARD(decrypt_data_nanos); |
105 | 0 | io_s = status_to_io_status( |
106 | 0 | stream_->Decrypt(offset, (char*)result->data(), result->size())); |
107 | 0 | } |
108 | 0 | return io_s; |
109 | 0 | } |
110 | | |
111 | | IOStatus EncryptedRandomAccessFile::Prefetch(uint64_t offset, size_t n, |
112 | | const IOOptions& options, |
113 | 0 | IODebugContext* dbg) { |
114 | 0 | return file_->Prefetch(offset + prefixLength_, n, options, dbg); |
115 | 0 | } |
116 | | |
117 | 0 | size_t EncryptedRandomAccessFile::GetUniqueId(char* id, size_t max_size) const { |
118 | 0 | return file_->GetUniqueId(id, max_size); |
119 | 0 | } |
120 | | |
121 | 0 | void EncryptedRandomAccessFile::Hint(AccessPattern pattern) { |
122 | 0 | file_->Hint(pattern); |
123 | 0 | } |
124 | | |
125 | 0 | bool EncryptedRandomAccessFile::use_direct_io() const { |
126 | 0 | return file_->use_direct_io(); |
127 | 0 | } |
128 | | |
129 | 0 | size_t EncryptedRandomAccessFile::GetRequiredBufferAlignment() const { |
130 | 0 | return file_->GetRequiredBufferAlignment(); |
131 | 0 | } |
132 | | |
133 | | IOStatus EncryptedRandomAccessFile::InvalidateCache(size_t offset, |
134 | 0 | size_t length) { |
135 | 0 | return file_->InvalidateCache(offset + prefixLength_, length); |
136 | 0 | } |
137 | | |
138 | | IOStatus EncryptedWritableFile::Append(const Slice& data, |
139 | | const IOOptions& options, |
140 | 0 | IODebugContext* dbg) { |
141 | 0 | AlignedBuffer buf; |
142 | 0 | Slice dataToAppend(data); |
143 | 0 | if (data.size() > 0) { |
144 | 0 | auto offset = file_->GetFileSize(options, dbg); // size including prefix |
145 | | // Encrypt in cloned buffer |
146 | 0 | buf.Alignment(GetRequiredBufferAlignment()); |
147 | 0 | buf.AllocateNewBuffer(data.size()); |
148 | | // TODO (sagar0): Modify AlignedBuffer.Append to allow doing a memmove |
149 | | // so that the next two lines can be replaced with buf.Append(). |
150 | 0 | memmove(buf.BufferStart(), data.data(), data.size()); |
151 | 0 | buf.Size(data.size()); |
152 | 0 | IOStatus io_s; |
153 | 0 | { |
154 | 0 | PERF_TIMER_GUARD(encrypt_data_nanos); |
155 | 0 | io_s = status_to_io_status( |
156 | 0 | stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize())); |
157 | 0 | } |
158 | 0 | if (!io_s.ok()) { |
159 | 0 | return io_s; |
160 | 0 | } |
161 | 0 | dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize()); |
162 | 0 | } |
163 | 0 | return file_->Append(dataToAppend, options, dbg); |
164 | 0 | } |
165 | | |
166 | | IOStatus EncryptedWritableFile::PositionedAppend(const Slice& data, |
167 | | uint64_t offset, |
168 | | const IOOptions& options, |
169 | 0 | IODebugContext* dbg) { |
170 | 0 | AlignedBuffer buf; |
171 | 0 | Slice dataToAppend(data); |
172 | 0 | offset += prefixLength_; |
173 | 0 | if (data.size() > 0) { |
174 | | // Encrypt in cloned buffer |
175 | 0 | buf.Alignment(GetRequiredBufferAlignment()); |
176 | 0 | buf.AllocateNewBuffer(data.size()); |
177 | 0 | memmove(buf.BufferStart(), data.data(), data.size()); |
178 | 0 | buf.Size(data.size()); |
179 | 0 | IOStatus io_s; |
180 | 0 | { |
181 | 0 | PERF_TIMER_GUARD(encrypt_data_nanos); |
182 | 0 | io_s = status_to_io_status( |
183 | 0 | stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize())); |
184 | 0 | } |
185 | 0 | if (!io_s.ok()) { |
186 | 0 | return io_s; |
187 | 0 | } |
188 | 0 | dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize()); |
189 | 0 | } |
190 | 0 | return file_->PositionedAppend(dataToAppend, offset, options, dbg); |
191 | 0 | } |
192 | | |
193 | 0 | bool EncryptedWritableFile::use_direct_io() const { |
194 | 0 | return file_->use_direct_io(); |
195 | 0 | } |
196 | | |
197 | 0 | bool EncryptedWritableFile::IsSyncThreadSafe() const { |
198 | 0 | return file_->IsSyncThreadSafe(); |
199 | 0 | } |
200 | | |
201 | 0 | size_t EncryptedWritableFile::GetRequiredBufferAlignment() const { |
202 | 0 | return file_->GetRequiredBufferAlignment(); |
203 | 0 | } |
204 | | |
205 | | uint64_t EncryptedWritableFile::GetFileSize(const IOOptions& options, |
206 | 0 | IODebugContext* dbg) { |
207 | 0 | return file_->GetFileSize(options, dbg) - prefixLength_; |
208 | 0 | } |
209 | | |
210 | | IOStatus EncryptedWritableFile::Truncate(uint64_t size, |
211 | | const IOOptions& options, |
212 | 0 | IODebugContext* dbg) { |
213 | 0 | return file_->Truncate(size + prefixLength_, options, dbg); |
214 | 0 | } |
215 | | |
216 | 0 | IOStatus EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) { |
217 | 0 | return file_->InvalidateCache(offset + prefixLength_, length); |
218 | 0 | } |
219 | | |
220 | | IOStatus EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes, |
221 | | const IOOptions& options, |
222 | 0 | IODebugContext* dbg) { |
223 | 0 | return file_->RangeSync(offset + prefixLength_, nbytes, options, dbg); |
224 | 0 | } |
225 | | |
226 | | void EncryptedWritableFile::PrepareWrite(size_t offset, size_t len, |
227 | | const IOOptions& options, |
228 | 0 | IODebugContext* dbg) { |
229 | 0 | file_->PrepareWrite(offset + prefixLength_, len, options, dbg); |
230 | 0 | } |
231 | | |
232 | 0 | void EncryptedWritableFile::SetPreallocationBlockSize(size_t size) { |
233 | | // the size here doesn't need to include prefixLength_, as it's a |
234 | | // configuration will be use for `PrepareWrite()`. |
235 | 0 | file_->SetPreallocationBlockSize(size); |
236 | 0 | } |
237 | | |
238 | | void EncryptedWritableFile::GetPreallocationStatus( |
239 | 0 | size_t* block_size, size_t* last_allocated_block) { |
240 | 0 | file_->GetPreallocationStatus(block_size, last_allocated_block); |
241 | 0 | } |
242 | | |
243 | | IOStatus EncryptedWritableFile::Allocate(uint64_t offset, uint64_t len, |
244 | | const IOOptions& options, |
245 | 0 | IODebugContext* dbg) { |
246 | 0 | return file_->Allocate(offset + prefixLength_, len, options, dbg); |
247 | 0 | } |
248 | | |
249 | | IOStatus EncryptedWritableFile::Flush(const IOOptions& options, |
250 | 0 | IODebugContext* dbg) { |
251 | 0 | return file_->Flush(options, dbg); |
252 | 0 | } |
253 | | |
254 | | IOStatus EncryptedWritableFile::Sync(const IOOptions& options, |
255 | 0 | IODebugContext* dbg) { |
256 | 0 | return file_->Sync(options, dbg); |
257 | 0 | } |
258 | | |
259 | | IOStatus EncryptedWritableFile::Close(const IOOptions& options, |
260 | 0 | IODebugContext* dbg) { |
261 | 0 | return file_->Close(options, dbg); |
262 | 0 | } |
263 | | |
264 | 0 | bool EncryptedRandomRWFile::use_direct_io() const { |
265 | 0 | return file_->use_direct_io(); |
266 | 0 | } |
267 | | |
268 | 0 | size_t EncryptedRandomRWFile::GetRequiredBufferAlignment() const { |
269 | 0 | return file_->GetRequiredBufferAlignment(); |
270 | 0 | } |
271 | | |
272 | | IOStatus EncryptedRandomRWFile::Write(uint64_t offset, const Slice& data, |
273 | | const IOOptions& options, |
274 | 0 | IODebugContext* dbg) { |
275 | 0 | AlignedBuffer buf; |
276 | 0 | Slice dataToWrite(data); |
277 | 0 | offset += prefixLength_; |
278 | 0 | if (data.size() > 0) { |
279 | | // Encrypt in cloned buffer |
280 | 0 | buf.Alignment(GetRequiredBufferAlignment()); |
281 | 0 | buf.AllocateNewBuffer(data.size()); |
282 | 0 | memmove(buf.BufferStart(), data.data(), data.size()); |
283 | 0 | buf.Size(data.size()); |
284 | 0 | IOStatus io_s; |
285 | 0 | { |
286 | 0 | PERF_TIMER_GUARD(encrypt_data_nanos); |
287 | 0 | io_s = status_to_io_status( |
288 | 0 | stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize())); |
289 | 0 | } |
290 | 0 | if (!io_s.ok()) { |
291 | 0 | return io_s; |
292 | 0 | } |
293 | 0 | dataToWrite = Slice(buf.BufferStart(), buf.CurrentSize()); |
294 | 0 | } |
295 | 0 | return file_->Write(offset, dataToWrite, options, dbg); |
296 | 0 | } |
297 | | |
298 | | IOStatus EncryptedRandomRWFile::Read(uint64_t offset, size_t n, |
299 | | const IOOptions& options, Slice* result, |
300 | 0 | char* scratch, IODebugContext* dbg) const { |
301 | 0 | assert(scratch); |
302 | 0 | offset += prefixLength_; |
303 | 0 | auto status = file_->Read(offset, n, options, result, scratch, dbg); |
304 | 0 | if (!status.ok()) { |
305 | 0 | return status; |
306 | 0 | } |
307 | 0 | { |
308 | 0 | PERF_TIMER_GUARD(decrypt_data_nanos); |
309 | 0 | status = status_to_io_status( |
310 | 0 | stream_->Decrypt(offset, (char*)result->data(), result->size())); |
311 | 0 | } |
312 | 0 | return status; |
313 | 0 | } |
314 | | |
315 | | IOStatus EncryptedRandomRWFile::Flush(const IOOptions& options, |
316 | 0 | IODebugContext* dbg) { |
317 | 0 | return file_->Flush(options, dbg); |
318 | 0 | } |
319 | | |
320 | | IOStatus EncryptedRandomRWFile::Sync(const IOOptions& options, |
321 | 0 | IODebugContext* dbg) { |
322 | 0 | return file_->Sync(options, dbg); |
323 | 0 | } |
324 | | |
325 | | IOStatus EncryptedRandomRWFile::Fsync(const IOOptions& options, |
326 | 0 | IODebugContext* dbg) { |
327 | 0 | return file_->Fsync(options, dbg); |
328 | 0 | } |
329 | | |
330 | | IOStatus EncryptedRandomRWFile::Close(const IOOptions& options, |
331 | 0 | IODebugContext* dbg) { |
332 | 0 | return file_->Close(options, dbg); |
333 | 0 | } |
334 | | |
335 | | namespace { |
336 | | static std::unordered_map<std::string, OptionTypeInfo> encrypted_fs_type_info = |
337 | | { |
338 | | {"provider", |
339 | | OptionTypeInfo::AsCustomSharedPtr<EncryptionProvider>( |
340 | | 0 /* No offset, whole struct*/, OptionVerificationType::kByName, |
341 | | OptionTypeFlags::kNone)}, |
342 | | }; |
343 | | // EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption |
344 | | // to files stored on disk. |
345 | | class EncryptedFileSystemImpl : public EncryptedFileSystem { |
346 | | public: |
347 | 0 | const char* Name() const override { |
348 | 0 | return EncryptedFileSystem::kClassName(); |
349 | 0 | } |
350 | | // Returns the raw encryption provider that should be used to write the input |
351 | | // encrypted file. If there is no such provider, NotFound is returned. |
352 | | IOStatus GetWritableProvider(const std::string& /*fname*/, |
353 | 0 | EncryptionProvider** result) { |
354 | 0 | if (provider_) { |
355 | 0 | *result = provider_.get(); |
356 | 0 | return IOStatus::OK(); |
357 | 0 | } else { |
358 | 0 | *result = nullptr; |
359 | 0 | return IOStatus::NotFound("No WriteProvider specified"); |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | // Returns the raw encryption provider that should be used to read the input |
364 | | // encrypted file. If there is no such provider, NotFound is returned. |
365 | | IOStatus GetReadableProvider(const std::string& /*fname*/, |
366 | 0 | EncryptionProvider** result) { |
367 | 0 | if (provider_) { |
368 | 0 | *result = provider_.get(); |
369 | 0 | return IOStatus::OK(); |
370 | 0 | } else { |
371 | 0 | *result = nullptr; |
372 | 0 | return IOStatus::NotFound("No Provider specified"); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | // Creates a CipherStream for the underlying file/name using the options |
377 | | // If a writable provider is found and encryption is enabled, uses |
378 | | // this provider to create a cipher stream. |
379 | | // @param fname Name of the writable file |
380 | | // @param underlying The underlying "raw" file |
381 | | // @param options Options for creating the file/cipher |
382 | | // @param prefix_length Returns the length of the encryption prefix used for |
383 | | // this file |
384 | | // @param stream Returns the cipher stream to use for this file if it |
385 | | // should be encrypted |
386 | | // @return OK on success, non-OK on failure. |
387 | | template <class TypeFile> |
388 | | IOStatus CreateWritableCipherStream( |
389 | | const std::string& fname, const std::unique_ptr<TypeFile>& underlying, |
390 | | const FileOptions& options, size_t* prefix_length, |
391 | 0 | std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) { |
392 | 0 | EncryptionProvider* provider = nullptr; |
393 | 0 | *prefix_length = 0; |
394 | 0 | IOStatus status = GetWritableProvider(fname, &provider); |
395 | 0 | if (!status.ok()) { |
396 | 0 | return status; |
397 | 0 | } else if (provider != nullptr) { |
398 | | // Initialize & write prefix (if needed) |
399 | 0 | AlignedBuffer buffer; |
400 | 0 | Slice prefix; |
401 | 0 | *prefix_length = provider->GetPrefixLength(); |
402 | 0 | if (*prefix_length > 0) { |
403 | | // Initialize prefix |
404 | 0 | buffer.Alignment(underlying->GetRequiredBufferAlignment()); |
405 | 0 | buffer.AllocateNewBuffer(*prefix_length); |
406 | 0 | status = status_to_io_status(provider->CreateNewPrefix( |
407 | 0 | fname, buffer.BufferStart(), *prefix_length)); |
408 | 0 | if (status.ok()) { |
409 | 0 | buffer.Size(*prefix_length); |
410 | 0 | prefix = Slice(buffer.BufferStart(), buffer.CurrentSize()); |
411 | | // Write prefix |
412 | 0 | status = underlying->Append(prefix, options.io_options, dbg); |
413 | 0 | } |
414 | 0 | if (!status.ok()) { |
415 | 0 | return status; |
416 | 0 | } |
417 | 0 | } |
418 | | // Create cipher stream |
419 | 0 | status = status_to_io_status( |
420 | 0 | provider->CreateCipherStream(fname, options, prefix, stream)); |
421 | 0 | } |
422 | 0 | return status; |
423 | 0 | } |
424 | | |
425 | | template <class TypeFile> |
426 | | IOStatus CreateWritableEncryptedFile(const std::string& fname, |
427 | | std::unique_ptr<TypeFile>& underlying, |
428 | | const FileOptions& options, |
429 | | std::unique_ptr<TypeFile>* result, |
430 | 0 | IODebugContext* dbg) { |
431 | | // Create cipher stream |
432 | 0 | std::unique_ptr<BlockAccessCipherStream> stream; |
433 | 0 | size_t prefix_length; |
434 | 0 | IOStatus status = CreateWritableCipherStream(fname, underlying, options, |
435 | 0 | &prefix_length, &stream, dbg); |
436 | 0 | if (status.ok()) { |
437 | 0 | if (stream) { |
438 | 0 | result->reset(new EncryptedWritableFile( |
439 | 0 | std::move(underlying), std::move(stream), prefix_length)); |
440 | 0 | } else { |
441 | 0 | result->reset(underlying.release()); |
442 | 0 | } |
443 | 0 | } |
444 | 0 | return status; |
445 | 0 | } |
446 | | |
447 | | // Creates a CipherStream for the underlying file/name using the options |
448 | | // If a writable provider is found and encryption is enabled, uses |
449 | | // this provider to create a cipher stream. |
450 | | // @param fname Name of the writable file |
451 | | // @param underlying The underlying "raw" file |
452 | | // @param options Options for creating the file/cipher |
453 | | // @param prefix_length Returns the length of the encryption prefix used for |
454 | | // this file |
455 | | // @param stream Returns the cipher stream to use for this file if it |
456 | | // should be encrypted |
457 | | // @return OK on success, non-OK on failure. |
458 | | template <class TypeFile> |
459 | | IOStatus CreateRandomWriteCipherStream( |
460 | | const std::string& fname, const std::unique_ptr<TypeFile>& underlying, |
461 | | const FileOptions& options, size_t* prefix_length, |
462 | 0 | std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) { |
463 | 0 | EncryptionProvider* provider = nullptr; |
464 | 0 | *prefix_length = 0; |
465 | 0 | IOStatus io_s = GetWritableProvider(fname, &provider); |
466 | 0 | if (!io_s.ok()) { |
467 | 0 | return io_s; |
468 | 0 | } else if (provider != nullptr) { |
469 | | // Initialize & write prefix (if needed) |
470 | 0 | AlignedBuffer buffer; |
471 | 0 | Slice prefix; |
472 | 0 | *prefix_length = provider->GetPrefixLength(); |
473 | 0 | if (*prefix_length > 0) { |
474 | | // Initialize prefix |
475 | 0 | buffer.Alignment(underlying->GetRequiredBufferAlignment()); |
476 | 0 | buffer.AllocateNewBuffer(*prefix_length); |
477 | 0 | io_s = status_to_io_status(provider->CreateNewPrefix( |
478 | 0 | fname, buffer.BufferStart(), *prefix_length)); |
479 | 0 | if (io_s.ok()) { |
480 | 0 | buffer.Size(*prefix_length); |
481 | 0 | prefix = Slice(buffer.BufferStart(), buffer.CurrentSize()); |
482 | | // Write prefix |
483 | 0 | io_s = underlying->Write(0, prefix, options.io_options, dbg); |
484 | 0 | } |
485 | 0 | if (!io_s.ok()) { |
486 | 0 | return io_s; |
487 | 0 | } |
488 | 0 | } |
489 | | // Create cipher stream |
490 | 0 | io_s = status_to_io_status( |
491 | 0 | provider->CreateCipherStream(fname, options, prefix, stream)); |
492 | 0 | } |
493 | 0 | return io_s; |
494 | 0 | } |
495 | | |
496 | | // Creates a CipherStream for the underlying file/name using the options |
497 | | // If a readable provider is found and the file is encrypted, uses |
498 | | // this provider to create a cipher stream. |
499 | | // @param fname Name of the writable file |
500 | | // @param underlying The underlying "raw" file |
501 | | // @param options Options for creating the file/cipher |
502 | | // @param prefix_length Returns the length of the encryption prefix used for |
503 | | // this file |
504 | | // @param stream Returns the cipher stream to use for this file if it |
505 | | // is encrypted |
506 | | // @return OK on success, non-OK on failure. |
507 | | template <class TypeFile> |
508 | | IOStatus CreateSequentialCipherStream( |
509 | | const std::string& fname, const std::unique_ptr<TypeFile>& underlying, |
510 | | const FileOptions& options, size_t* prefix_length, |
511 | 0 | std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) { |
512 | | // Read prefix (if needed) |
513 | 0 | AlignedBuffer buffer; |
514 | 0 | Slice prefix; |
515 | 0 | *prefix_length = provider_->GetPrefixLength(); |
516 | 0 | if (*prefix_length > 0) { |
517 | | // Read prefix |
518 | 0 | buffer.Alignment(underlying->GetRequiredBufferAlignment()); |
519 | 0 | buffer.AllocateNewBuffer(*prefix_length); |
520 | 0 | IOStatus status = underlying->Read(*prefix_length, options.io_options, |
521 | 0 | &prefix, buffer.BufferStart(), dbg); |
522 | 0 | if (!status.ok()) { |
523 | 0 | return status; |
524 | 0 | } |
525 | 0 | buffer.Size(*prefix_length); |
526 | 0 | } |
527 | 0 | return status_to_io_status( |
528 | 0 | provider_->CreateCipherStream(fname, options, prefix, stream)); |
529 | 0 | } |
530 | | |
531 | | // Creates a CipherStream for the underlying file/name using the options |
532 | | // If a readable provider is found and the file is encrypted, uses |
533 | | // this provider to create a cipher stream. |
534 | | // @param fname Name of the writable file |
535 | | // @param underlying The underlying "raw" file |
536 | | // @param options Options for creating the file/cipher |
537 | | // @param prefix_length Returns the length of the encryption prefix used for |
538 | | // this file |
539 | | // @param stream Returns the cipher stream to use for this file if it |
540 | | // is encrypted |
541 | | // @return OK on success, non-OK on failure. |
542 | | template <class TypeFile> |
543 | | IOStatus CreateRandomReadCipherStream( |
544 | | const std::string& fname, const std::unique_ptr<TypeFile>& underlying, |
545 | | const FileOptions& options, size_t* prefix_length, |
546 | 0 | std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) { |
547 | | // Read prefix (if needed) |
548 | 0 | AlignedBuffer buffer; |
549 | 0 | Slice prefix; |
550 | 0 | *prefix_length = provider_->GetPrefixLength(); |
551 | 0 | if (*prefix_length > 0) { |
552 | | // Read prefix |
553 | 0 | buffer.Alignment(underlying->GetRequiredBufferAlignment()); |
554 | 0 | buffer.AllocateNewBuffer(*prefix_length); |
555 | 0 | IOStatus status = underlying->Read(0, *prefix_length, options.io_options, |
556 | 0 | &prefix, buffer.BufferStart(), dbg); |
557 | 0 | if (!status.ok()) { |
558 | 0 | return status; |
559 | 0 | } |
560 | 0 | buffer.Size(*prefix_length); |
561 | 0 | } |
562 | 0 | return status_to_io_status( |
563 | 0 | provider_->CreateCipherStream(fname, options, prefix, stream)); |
564 | 0 | } Unexecuted instantiation: env_encryption.cc:rocksdb::IOStatus rocksdb::(anonymous namespace)::EncryptedFileSystemImpl::CreateRandomReadCipherStream<rocksdb::FSRandomAccessFile>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::unique_ptr<rocksdb::FSRandomAccessFile, std::__1::default_delete<rocksdb::FSRandomAccessFile> > const&, rocksdb::FileOptions const&, unsigned long*, std::__1::unique_ptr<rocksdb::BlockAccessCipherStream, std::__1::default_delete<rocksdb::BlockAccessCipherStream> >*, rocksdb::IODebugContext*) Unexecuted instantiation: env_encryption.cc:rocksdb::IOStatus rocksdb::(anonymous namespace)::EncryptedFileSystemImpl::CreateRandomReadCipherStream<rocksdb::FSRandomRWFile>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::unique_ptr<rocksdb::FSRandomRWFile, std::__1::default_delete<rocksdb::FSRandomRWFile> > const&, rocksdb::FileOptions const&, unsigned long*, std::__1::unique_ptr<rocksdb::BlockAccessCipherStream, std::__1::default_delete<rocksdb::BlockAccessCipherStream> >*, rocksdb::IODebugContext*) |
565 | | |
566 | | public: |
567 | | EncryptedFileSystemImpl(const std::shared_ptr<FileSystem>& base, |
568 | | const std::shared_ptr<EncryptionProvider>& provider) |
569 | 0 | : EncryptedFileSystem(base) { |
570 | 0 | provider_ = provider; |
571 | 0 | RegisterOptions("EncryptionProvider", &provider_, &encrypted_fs_type_info); |
572 | 0 | } |
573 | | |
574 | | Status AddCipher(const std::string& descriptor, const char* cipher, |
575 | 0 | size_t len, bool for_write) override { |
576 | 0 | return provider_->AddCipher(descriptor, cipher, len, for_write); |
577 | 0 | } |
578 | | |
579 | | IOStatus NewSequentialFile(const std::string& fname, |
580 | | const FileOptions& options, |
581 | | std::unique_ptr<FSSequentialFile>* result, |
582 | 0 | IODebugContext* dbg) override { |
583 | 0 | result->reset(); |
584 | 0 | if (options.use_mmap_reads) { |
585 | 0 | return IOStatus::InvalidArgument(); |
586 | 0 | } |
587 | | // Open file using underlying Env implementation |
588 | 0 | std::unique_ptr<FSSequentialFile> underlying; |
589 | 0 | auto status = |
590 | 0 | FileSystemWrapper::NewSequentialFile(fname, options, &underlying, dbg); |
591 | 0 | if (!status.ok()) { |
592 | 0 | return status; |
593 | 0 | } |
594 | 0 | uint64_t file_size; |
595 | 0 | status = FileSystemWrapper::GetFileSize(fname, options.io_options, |
596 | 0 | &file_size, dbg); |
597 | 0 | if (!status.ok()) { |
598 | 0 | return status; |
599 | 0 | } |
600 | 0 | if (!file_size) { |
601 | 0 | *result = std::move(underlying); |
602 | 0 | return status; |
603 | 0 | } |
604 | | // Create cipher stream |
605 | 0 | std::unique_ptr<BlockAccessCipherStream> stream; |
606 | 0 | size_t prefix_length; |
607 | 0 | status = CreateSequentialCipherStream(fname, underlying, options, |
608 | 0 | &prefix_length, &stream, dbg); |
609 | 0 | if (status.ok()) { |
610 | 0 | result->reset(new EncryptedSequentialFile( |
611 | 0 | std::move(underlying), std::move(stream), prefix_length)); |
612 | 0 | } |
613 | 0 | return status; |
614 | 0 | } |
615 | | |
616 | | IOStatus NewRandomAccessFile(const std::string& fname, |
617 | | const FileOptions& options, |
618 | | std::unique_ptr<FSRandomAccessFile>* result, |
619 | 0 | IODebugContext* dbg) override { |
620 | 0 | result->reset(); |
621 | 0 | if (options.use_mmap_reads) { |
622 | 0 | return IOStatus::InvalidArgument(); |
623 | 0 | } |
624 | | // Open file using underlying Env implementation |
625 | 0 | std::unique_ptr<FSRandomAccessFile> underlying; |
626 | 0 | auto status = FileSystemWrapper::NewRandomAccessFile(fname, options, |
627 | 0 | &underlying, dbg); |
628 | 0 | if (!status.ok()) { |
629 | 0 | return status; |
630 | 0 | } |
631 | 0 | std::unique_ptr<BlockAccessCipherStream> stream; |
632 | 0 | size_t prefix_length; |
633 | 0 | status = CreateRandomReadCipherStream(fname, underlying, options, |
634 | 0 | &prefix_length, &stream, dbg); |
635 | 0 | if (status.ok()) { |
636 | 0 | if (stream) { |
637 | 0 | result->reset(new EncryptedRandomAccessFile( |
638 | 0 | std::move(underlying), std::move(stream), prefix_length)); |
639 | 0 | } else { |
640 | 0 | result->reset(underlying.release()); |
641 | 0 | } |
642 | 0 | } |
643 | 0 | return status; |
644 | 0 | } |
645 | | |
646 | | IOStatus NewWritableFile(const std::string& fname, const FileOptions& options, |
647 | | std::unique_ptr<FSWritableFile>* result, |
648 | 0 | IODebugContext* dbg) override { |
649 | 0 | result->reset(); |
650 | 0 | if (options.use_mmap_writes) { |
651 | 0 | return IOStatus::InvalidArgument(); |
652 | 0 | } |
653 | | // Open file using underlying Env implementation |
654 | 0 | std::unique_ptr<FSWritableFile> underlying; |
655 | 0 | IOStatus status = |
656 | 0 | FileSystemWrapper::NewWritableFile(fname, options, &underlying, dbg); |
657 | 0 | if (!status.ok()) { |
658 | 0 | return status; |
659 | 0 | } |
660 | 0 | return CreateWritableEncryptedFile(fname, underlying, options, result, dbg); |
661 | 0 | } |
662 | | |
663 | | IOStatus ReopenWritableFile(const std::string& fname, |
664 | | const FileOptions& options, |
665 | | std::unique_ptr<FSWritableFile>* result, |
666 | 0 | IODebugContext* dbg) override { |
667 | 0 | result->reset(); |
668 | 0 | if (options.use_mmap_reads || options.use_mmap_writes) { |
669 | 0 | return IOStatus::InvalidArgument(); |
670 | 0 | } |
671 | | |
672 | 0 | size_t prefix_length = 0; |
673 | 0 | std::unique_ptr<BlockAccessCipherStream> stream; |
674 | | |
675 | | // Open file using underlying Env implementation |
676 | 0 | std::unique_ptr<FSWritableFile> underlying; |
677 | 0 | auto status = |
678 | 0 | FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg); |
679 | 0 | if (!status.ok()) { |
680 | 0 | return status; |
681 | 0 | } |
682 | | |
683 | 0 | if (underlying->GetFileSize(options.io_options, dbg) != 0) { |
684 | | // read the cipher stream from file for non-empty file |
685 | 0 | std::unique_ptr<FSRandomAccessFile> underlying_file_reader; |
686 | 0 | status = FileSystemWrapper::NewRandomAccessFile( |
687 | 0 | fname, options, &underlying_file_reader, dbg); |
688 | 0 | if (!status.ok()) { |
689 | 0 | return status; |
690 | 0 | } |
691 | | |
692 | 0 | status = CreateRandomReadCipherStream( |
693 | 0 | fname, underlying_file_reader, options, &prefix_length, &stream, dbg); |
694 | |
|
695 | 0 | if (!status.ok()) { |
696 | 0 | return status; |
697 | 0 | } |
698 | 0 | } else { |
699 | | // create cipher stream for new or empty file |
700 | 0 | status = CreateWritableCipherStream(fname, underlying, options, |
701 | 0 | &prefix_length, &stream, dbg); |
702 | 0 | if (!status.ok()) { |
703 | 0 | return status; |
704 | 0 | } |
705 | 0 | } |
706 | | |
707 | 0 | if (stream) { |
708 | 0 | result->reset(new EncryptedWritableFile( |
709 | 0 | std::move(underlying), std::move(stream), prefix_length)); |
710 | 0 | } else { |
711 | 0 | result->reset(underlying.release()); |
712 | 0 | } |
713 | 0 | return status; |
714 | 0 | } |
715 | | |
716 | | IOStatus ReuseWritableFile(const std::string& fname, |
717 | | const std::string& old_fname, |
718 | | const FileOptions& options, |
719 | | std::unique_ptr<FSWritableFile>* result, |
720 | 0 | IODebugContext* dbg) override { |
721 | 0 | result->reset(); |
722 | 0 | if (options.use_mmap_writes) { |
723 | 0 | return IOStatus::InvalidArgument(); |
724 | 0 | } |
725 | | // Open file using underlying Env implementation |
726 | 0 | std::unique_ptr<FSWritableFile> underlying; |
727 | 0 | auto status = FileSystemWrapper::ReuseWritableFile( |
728 | 0 | fname, old_fname, options, &underlying, dbg); |
729 | 0 | if (!status.ok()) { |
730 | 0 | return status; |
731 | 0 | } |
732 | 0 | return CreateWritableEncryptedFile(fname, underlying, options, result, dbg); |
733 | 0 | } |
734 | | |
735 | | IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options, |
736 | | std::unique_ptr<FSRandomRWFile>* result, |
737 | 0 | IODebugContext* dbg) override { |
738 | 0 | result->reset(); |
739 | 0 | if (options.use_mmap_reads || options.use_mmap_writes) { |
740 | 0 | return IOStatus::InvalidArgument(); |
741 | 0 | } |
742 | | // Check file exists |
743 | 0 | bool isNewFile = !FileExists(fname, options.io_options, dbg).ok(); |
744 | | |
745 | | // Open file using underlying Env implementation |
746 | 0 | std::unique_ptr<FSRandomRWFile> underlying; |
747 | 0 | auto status = |
748 | 0 | FileSystemWrapper::NewRandomRWFile(fname, options, &underlying, dbg); |
749 | 0 | if (!status.ok()) { |
750 | 0 | return status; |
751 | 0 | } |
752 | | // Create cipher stream |
753 | 0 | std::unique_ptr<BlockAccessCipherStream> stream; |
754 | 0 | size_t prefix_length = 0; |
755 | 0 | if (!isNewFile) { |
756 | | // File already exists, read prefix |
757 | 0 | status = CreateRandomReadCipherStream(fname, underlying, options, |
758 | 0 | &prefix_length, &stream, dbg); |
759 | 0 | } else { |
760 | 0 | status = CreateRandomWriteCipherStream(fname, underlying, options, |
761 | 0 | &prefix_length, &stream, dbg); |
762 | 0 | } |
763 | 0 | if (status.ok()) { |
764 | 0 | if (stream) { |
765 | 0 | result->reset(new EncryptedRandomRWFile( |
766 | 0 | std::move(underlying), std::move(stream), prefix_length)); |
767 | 0 | } else { |
768 | 0 | result->reset(underlying.release()); |
769 | 0 | } |
770 | 0 | } |
771 | 0 | return status; |
772 | 0 | } |
773 | | |
774 | | IOStatus GetChildrenFileAttributes(const std::string& dir, |
775 | | const IOOptions& options, |
776 | | std::vector<FileAttributes>* result, |
777 | 0 | IODebugContext* dbg) override { |
778 | 0 | auto status = |
779 | 0 | FileSystemWrapper::GetChildrenFileAttributes(dir, options, result, dbg); |
780 | 0 | if (!status.ok()) { |
781 | 0 | return status; |
782 | 0 | } |
783 | 0 | for (auto it = std::begin(*result); it != std::end(*result); ++it) { |
784 | | // assert(it->size_bytes >= prefixLength); |
785 | | // breaks env_basic_test when called on directory containing |
786 | | // directories |
787 | | // which makes subtraction of prefixLength worrisome since |
788 | | // FileAttributes does not identify directories |
789 | 0 | EncryptionProvider* provider; |
790 | 0 | status = GetReadableProvider(it->name, &provider); |
791 | 0 | if (!status.ok()) { |
792 | 0 | return status; |
793 | 0 | } else if (provider != nullptr) { |
794 | 0 | it->size_bytes -= provider->GetPrefixLength(); |
795 | 0 | } |
796 | 0 | } |
797 | 0 | return IOStatus::OK(); |
798 | 0 | } |
799 | | |
800 | | IOStatus GetFileSize(const std::string& fname, const IOOptions& options, |
801 | 0 | uint64_t* file_size, IODebugContext* dbg) override { |
802 | 0 | auto status = |
803 | 0 | FileSystemWrapper::GetFileSize(fname, options, file_size, dbg); |
804 | 0 | if (!status.ok() || !(*file_size)) { |
805 | 0 | return status; |
806 | 0 | } |
807 | 0 | EncryptionProvider* provider; |
808 | 0 | status = GetReadableProvider(fname, &provider); |
809 | 0 | if (provider != nullptr && status.ok()) { |
810 | 0 | size_t prefixLength = provider->GetPrefixLength(); |
811 | 0 | assert(*file_size >= prefixLength); |
812 | 0 | *file_size -= prefixLength; |
813 | 0 | } |
814 | 0 | return status; |
815 | 0 | } |
816 | | |
817 | | private: |
818 | | std::shared_ptr<EncryptionProvider> provider_; |
819 | | }; |
820 | | } // namespace |
821 | | |
822 | | Status NewEncryptedFileSystemImpl( |
823 | | const std::shared_ptr<FileSystem>& base, |
824 | | const std::shared_ptr<EncryptionProvider>& provider, |
825 | 0 | std::unique_ptr<FileSystem>* result) { |
826 | 0 | result->reset(new EncryptedFileSystemImpl(base, provider)); |
827 | 0 | return Status::OK(); |
828 | 0 | } |
829 | | |
830 | | std::shared_ptr<FileSystem> NewEncryptedFS( |
831 | | const std::shared_ptr<FileSystem>& base, |
832 | 0 | const std::shared_ptr<EncryptionProvider>& provider) { |
833 | 0 | std::unique_ptr<FileSystem> efs; |
834 | 0 | Status s = NewEncryptedFileSystemImpl(base, provider, &efs); |
835 | 0 | if (s.ok()) { |
836 | 0 | s = efs->PrepareOptions(ConfigOptions()); |
837 | 0 | } |
838 | 0 | if (s.ok()) { |
839 | 0 | std::shared_ptr<FileSystem> result(efs.release()); |
840 | 0 | return result; |
841 | 0 | } else { |
842 | 0 | return nullptr; |
843 | 0 | } |
844 | 0 | } |
845 | | |
846 | | Env* NewEncryptedEnv(Env* base_env, |
847 | 0 | const std::shared_ptr<EncryptionProvider>& provider) { |
848 | 0 | return new CompositeEnvWrapper( |
849 | 0 | base_env, NewEncryptedFS(base_env->GetFileSystem(), provider)); |
850 | 0 | } |
851 | | |
852 | | Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char* data, |
853 | 0 | size_t dataSize) { |
854 | | // Calculate block index |
855 | 0 | auto blockSize = BlockSize(); |
856 | 0 | uint64_t blockIndex = fileOffset / blockSize; |
857 | 0 | size_t blockOffset = fileOffset % blockSize; |
858 | 0 | std::unique_ptr<char[]> blockBuffer; |
859 | |
|
860 | 0 | std::string scratch; |
861 | 0 | AllocateScratch(scratch); |
862 | | |
863 | | // Encrypt individual blocks. |
864 | 0 | while (true) { |
865 | 0 | char* block = data; |
866 | 0 | size_t n = std::min(dataSize, blockSize - blockOffset); |
867 | 0 | if (n != blockSize) { |
868 | | // We're not encrypting a full block. |
869 | | // Copy data to blockBuffer |
870 | 0 | if (!blockBuffer) { |
871 | | // Allocate buffer |
872 | 0 | blockBuffer = std::unique_ptr<char[]>(new char[blockSize]); |
873 | 0 | } |
874 | 0 | block = blockBuffer.get(); |
875 | | // Copy plain data to block buffer |
876 | 0 | memmove(block + blockOffset, data, n); |
877 | 0 | } |
878 | 0 | auto status = EncryptBlock(blockIndex, block, (char*)scratch.data()); |
879 | 0 | if (!status.ok()) { |
880 | 0 | return status; |
881 | 0 | } |
882 | 0 | if (block != data) { |
883 | | // Copy encrypted data back to `data`. |
884 | 0 | memmove(data, block + blockOffset, n); |
885 | 0 | } |
886 | 0 | dataSize -= n; |
887 | 0 | if (dataSize == 0) { |
888 | 0 | return Status::OK(); |
889 | 0 | } |
890 | 0 | data += n; |
891 | 0 | blockOffset = 0; |
892 | 0 | blockIndex++; |
893 | 0 | } |
894 | 0 | } |
895 | | |
896 | | Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char* data, |
897 | 0 | size_t dataSize) { |
898 | | // Calculate block index |
899 | 0 | auto blockSize = BlockSize(); |
900 | 0 | uint64_t blockIndex = fileOffset / blockSize; |
901 | 0 | size_t blockOffset = fileOffset % blockSize; |
902 | 0 | std::unique_ptr<char[]> blockBuffer; |
903 | |
|
904 | 0 | std::string scratch; |
905 | 0 | AllocateScratch(scratch); |
906 | | |
907 | | // Decrypt individual blocks. |
908 | 0 | while (true) { |
909 | 0 | char* block = data; |
910 | 0 | size_t n = std::min(dataSize, blockSize - blockOffset); |
911 | 0 | if (n != blockSize) { |
912 | | // We're not decrypting a full block. |
913 | | // Copy data to blockBuffer |
914 | 0 | if (!blockBuffer) { |
915 | | // Allocate buffer |
916 | 0 | blockBuffer = std::unique_ptr<char[]>(new char[blockSize]); |
917 | 0 | } |
918 | 0 | block = blockBuffer.get(); |
919 | | // Copy encrypted data to block buffer |
920 | 0 | memmove(block + blockOffset, data, n); |
921 | 0 | } |
922 | 0 | auto status = DecryptBlock(blockIndex, block, (char*)scratch.data()); |
923 | 0 | if (!status.ok()) { |
924 | 0 | return status; |
925 | 0 | } |
926 | 0 | if (block != data) { |
927 | | // Copy decrypted data back to `data`. |
928 | 0 | memmove(data, block + blockOffset, n); |
929 | 0 | } |
930 | | |
931 | | // Simply decrementing dataSize by n could cause it to underflow, |
932 | | // which will very likely make it read over the original bounds later |
933 | 0 | assert(dataSize >= n); |
934 | 0 | if (dataSize < n) { |
935 | 0 | return Status::Corruption("Cannot decrypt data at given offset"); |
936 | 0 | } |
937 | | |
938 | 0 | dataSize -= n; |
939 | 0 | if (dataSize == 0) { |
940 | 0 | return Status::OK(); |
941 | 0 | } |
942 | 0 | data += n; |
943 | 0 | blockOffset = 0; |
944 | 0 | blockIndex++; |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | | namespace { |
949 | | static std::unordered_map<std::string, OptionTypeInfo> |
950 | | rot13_block_cipher_type_info = { |
951 | | {"block_size", |
952 | | {0 /* No offset, whole struct*/, OptionType::kInt, |
953 | | OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
954 | | }; |
955 | | |
956 | | // Implements a BlockCipher using ROT13. |
957 | | // |
958 | | // Note: This is a sample implementation of BlockCipher, |
959 | | // it is NOT considered safe and should NOT be used in production. |
960 | | class ROT13BlockCipher : public BlockCipher { |
961 | | private: |
962 | | size_t blockSize_; |
963 | | |
964 | | public: |
965 | 0 | explicit ROT13BlockCipher(size_t blockSize) : blockSize_(blockSize) { |
966 | 0 | RegisterOptions("ROT13BlockCipherOptions", &blockSize_, |
967 | 0 | &rot13_block_cipher_type_info); |
968 | 0 | } |
969 | | |
970 | 0 | static const char* kClassName() { return "ROT13"; } |
971 | 0 | const char* Name() const override { return kClassName(); } |
972 | | |
973 | 0 | size_t BlockSize() override { return blockSize_; } |
974 | 0 | Status Encrypt(char* data) override { |
975 | 0 | for (size_t i = 0; i < blockSize_; ++i) { |
976 | 0 | data[i] += 13; |
977 | 0 | } |
978 | 0 | return Status::OK(); |
979 | 0 | } |
980 | 0 | Status Decrypt(char* data) override { return Encrypt(data); } |
981 | | }; |
982 | | |
983 | | static const std::unordered_map<std::string, OptionTypeInfo> |
984 | | ctr_encryption_provider_type_info = { |
985 | | {"cipher", |
986 | | OptionTypeInfo::AsCustomSharedPtr<BlockCipher>( |
987 | | 0 /* No offset, whole struct*/, OptionVerificationType::kByName, |
988 | | OptionTypeFlags::kNone)}, |
989 | | }; |
990 | | } // anonymous namespace |
991 | | |
992 | 0 | void CTRCipherStream::AllocateScratch(std::string& scratch) { |
993 | 0 | auto blockSize = cipher_->BlockSize(); |
994 | 0 | scratch.reserve(blockSize); |
995 | 0 | } |
996 | | |
997 | | Status CTRCipherStream::EncryptBlock(uint64_t blockIndex, char* data, |
998 | 0 | char* scratch) { |
999 | | // Create nonce + counter |
1000 | 0 | auto blockSize = cipher_->BlockSize(); |
1001 | 0 | memmove(scratch, iv_.data(), blockSize); |
1002 | 0 | EncodeFixed64(scratch, blockIndex + initialCounter_); |
1003 | | |
1004 | | // Encrypt nonce + counter |
1005 | 0 | auto status = cipher_->Encrypt(scratch); |
1006 | 0 | if (!status.ok()) { |
1007 | 0 | return status; |
1008 | 0 | } |
1009 | | |
1010 | | // XOR data with ciphertext. |
1011 | 0 | for (size_t i = 0; i < blockSize; i++) { |
1012 | 0 | data[i] = data[i] ^ scratch[i]; |
1013 | 0 | } |
1014 | 0 | return Status::OK(); |
1015 | 0 | } |
1016 | | |
1017 | | Status CTRCipherStream::DecryptBlock(uint64_t blockIndex, char* data, |
1018 | 0 | char* scratch) { |
1019 | | // For CTR decryption & encryption are the same |
1020 | 0 | return EncryptBlock(blockIndex, data, scratch); |
1021 | 0 | } |
1022 | | |
1023 | | CTREncryptionProvider::CTREncryptionProvider( |
1024 | | const std::shared_ptr<BlockCipher>& c) |
1025 | 0 | : cipher_(c) { |
1026 | 0 | RegisterOptions("Cipher", &cipher_, &ctr_encryption_provider_type_info); |
1027 | 0 | } |
1028 | | |
1029 | 0 | bool CTREncryptionProvider::IsInstanceOf(const std::string& name) const { |
1030 | | // Special case for test purposes. |
1031 | 0 | if (name == "1://test" && cipher_ != nullptr) { |
1032 | 0 | return cipher_->IsInstanceOf(ROT13BlockCipher::kClassName()); |
1033 | 0 | } else { |
1034 | 0 | return EncryptionProvider::IsInstanceOf(name); |
1035 | 0 | } |
1036 | 0 | } |
1037 | | |
1038 | 0 | size_t CTREncryptionProvider::GetPrefixLength() const { |
1039 | 0 | return defaultPrefixLength; |
1040 | 0 | } |
1041 | | |
1042 | | Status CTREncryptionProvider::AddCipher(const std::string& /*descriptor*/, |
1043 | | const char* cipher, size_t len, |
1044 | 0 | bool /*for_write*/) { |
1045 | 0 | if (cipher_) { |
1046 | 0 | return Status::NotSupported("Cannot add keys to CTREncryptionProvider"); |
1047 | 0 | } else if (strcmp(ROT13BlockCipher::kClassName(), cipher) == 0) { |
1048 | 0 | cipher_.reset(new ROT13BlockCipher(len)); |
1049 | 0 | return Status::OK(); |
1050 | 0 | } else { |
1051 | 0 | return BlockCipher::CreateFromString(ConfigOptions(), std::string(cipher), |
1052 | 0 | &cipher_); |
1053 | 0 | } |
1054 | 0 | } |
1055 | | |
1056 | | // decodeCTRParameters decodes the initial counter & IV from the given |
1057 | | // (plain text) prefix. |
1058 | | static void decodeCTRParameters(const char* prefix, size_t blockSize, |
1059 | 0 | uint64_t& initialCounter, Slice& iv) { |
1060 | | // First block contains 64-bit initial counter |
1061 | 0 | initialCounter = DecodeFixed64(prefix); |
1062 | | // Second block contains IV |
1063 | 0 | iv = Slice(prefix + blockSize, blockSize); |
1064 | 0 | } |
1065 | | |
1066 | | Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/, |
1067 | | char* prefix, |
1068 | 0 | size_t prefixLength) const { |
1069 | 0 | if (!cipher_) { |
1070 | 0 | return Status::InvalidArgument("Encryption Cipher is missing"); |
1071 | 0 | } |
1072 | | // Create & seed rnd. |
1073 | 0 | Random rnd((uint32_t)SystemClock::Default()->NowMicros()); |
1074 | | // Fill entire prefix block with random values. |
1075 | 0 | for (size_t i = 0; i < prefixLength; i++) { |
1076 | 0 | prefix[i] = rnd.Uniform(256) & 0xFF; |
1077 | 0 | } |
1078 | | // Take random data to extract initial counter & IV |
1079 | 0 | auto blockSize = cipher_->BlockSize(); |
1080 | 0 | uint64_t initialCounter; |
1081 | 0 | Slice prefixIV; |
1082 | 0 | decodeCTRParameters(prefix, blockSize, initialCounter, prefixIV); |
1083 | | |
1084 | | // Now populate the rest of the prefix, starting from the third block. |
1085 | 0 | PopulateSecretPrefixPart(prefix + (2 * blockSize), |
1086 | 0 | prefixLength - (2 * blockSize), blockSize); |
1087 | | |
1088 | | // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial |
1089 | | // counter & IV unencrypted) |
1090 | 0 | CTRCipherStream cipherStream(cipher_, prefixIV.data(), initialCounter); |
1091 | 0 | Status status; |
1092 | 0 | { |
1093 | 0 | PERF_TIMER_GUARD(encrypt_data_nanos); |
1094 | 0 | status = cipherStream.Encrypt(0, prefix + (2 * blockSize), |
1095 | 0 | prefixLength - (2 * blockSize)); |
1096 | 0 | } |
1097 | |
|
1098 | 0 | return status; |
1099 | 0 | } |
1100 | | |
1101 | | // PopulateSecretPrefixPart initializes the data into a new prefix block |
1102 | | // in plain text. |
1103 | | // Returns the amount of space (starting from the start of the prefix) |
1104 | | // that has been initialized. |
1105 | | size_t CTREncryptionProvider::PopulateSecretPrefixPart( |
1106 | 0 | char* /*prefix*/, size_t /*prefixLength*/, size_t /*blockSize*/) const { |
1107 | | // Nothing to do here, put in custom data in override when needed. |
1108 | 0 | return 0; |
1109 | 0 | } |
1110 | | |
1111 | | Status CTREncryptionProvider::CreateCipherStream( |
1112 | | const std::string& fname, const EnvOptions& options, Slice& prefix, |
1113 | 0 | std::unique_ptr<BlockAccessCipherStream>* result) { |
1114 | 0 | if (!cipher_) { |
1115 | 0 | return Status::InvalidArgument("Encryption Cipher is missing"); |
1116 | 0 | } |
1117 | | // Read plain text part of prefix. |
1118 | 0 | auto blockSize = cipher_->BlockSize(); |
1119 | 0 | uint64_t initialCounter; |
1120 | 0 | Slice iv; |
1121 | 0 | decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv); |
1122 | | |
1123 | | // If the prefix is smaller than twice the block size, we would below read a |
1124 | | // very large chunk of the file (and very likely read over the bounds) |
1125 | 0 | assert(prefix.size() >= 2 * blockSize); |
1126 | 0 | if (prefix.size() < 2 * blockSize) { |
1127 | 0 | return Status::Corruption("Unable to read from file " + fname + |
1128 | 0 | ": read attempt would read beyond file bounds"); |
1129 | 0 | } |
1130 | | |
1131 | | // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1 |
1132 | | // with initial counter & IV are unencrypted) |
1133 | 0 | CTRCipherStream cipherStream(cipher_, iv.data(), initialCounter); |
1134 | 0 | Status status; |
1135 | 0 | { |
1136 | 0 | PERF_TIMER_GUARD(decrypt_data_nanos); |
1137 | 0 | status = cipherStream.Decrypt(0, (char*)prefix.data() + (2 * blockSize), |
1138 | 0 | prefix.size() - (2 * blockSize)); |
1139 | 0 | } |
1140 | 0 | if (!status.ok()) { |
1141 | 0 | return status; |
1142 | 0 | } |
1143 | | |
1144 | | // Create cipher stream |
1145 | 0 | return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv, |
1146 | 0 | prefix, result); |
1147 | 0 | } |
1148 | | |
1149 | | // CreateCipherStreamFromPrefix creates a block access cipher stream for a file |
1150 | | // given name and options. The given prefix is already decrypted. |
1151 | | Status CTREncryptionProvider::CreateCipherStreamFromPrefix( |
1152 | | const std::string& /*fname*/, const EnvOptions& /*options*/, |
1153 | | uint64_t initialCounter, const Slice& iv, const Slice& /*prefix*/, |
1154 | 0 | std::unique_ptr<BlockAccessCipherStream>* result) { |
1155 | 0 | (*result) = std::unique_ptr<BlockAccessCipherStream>( |
1156 | 0 | new CTRCipherStream(cipher_, iv.data(), initialCounter)); |
1157 | 0 | return Status::OK(); |
1158 | 0 | } |
1159 | | |
1160 | | namespace { |
1161 | 0 | static void RegisterEncryptionBuiltins() { |
1162 | 0 | static std::once_flag once; |
1163 | 0 | std::call_once(once, [&]() { |
1164 | 0 | auto lib = ObjectRegistry::Default()->AddLibrary("encryption"); |
1165 | | // Match "CTR" or "CTR://test" |
1166 | 0 | lib->AddFactory<EncryptionProvider>( |
1167 | 0 | ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true) |
1168 | 0 | .AddSuffix("://test"), |
1169 | 0 | [](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard, |
1170 | 0 | std::string* /*errmsg*/) { |
1171 | 0 | if (EndsWith(uri, "://test")) { |
1172 | 0 | std::shared_ptr<BlockCipher> cipher = |
1173 | 0 | std::make_shared<ROT13BlockCipher>(32); |
1174 | 0 | guard->reset(new CTREncryptionProvider(cipher)); |
1175 | 0 | } else { |
1176 | 0 | guard->reset(new CTREncryptionProvider()); |
1177 | 0 | } |
1178 | 0 | return guard->get(); |
1179 | 0 | }); |
1180 | |
|
1181 | 0 | lib->AddFactory<EncryptionProvider>( |
1182 | 0 | "1://test", [](const std::string& /*uri*/, |
1183 | 0 | std::unique_ptr<EncryptionProvider>* guard, |
1184 | 0 | std::string* /*errmsg*/) { |
1185 | 0 | std::shared_ptr<BlockCipher> cipher = |
1186 | 0 | std::make_shared<ROT13BlockCipher>(32); |
1187 | 0 | guard->reset(new CTREncryptionProvider(cipher)); |
1188 | 0 | return guard->get(); |
1189 | 0 | }); |
1190 | | |
1191 | | // Match "ROT13" or "ROT13:[0-9]+" |
1192 | 0 | lib->AddFactory<BlockCipher>( |
1193 | 0 | ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true) |
1194 | 0 | .AddNumber(":"), |
1195 | 0 | [](const std::string& uri, std::unique_ptr<BlockCipher>* guard, |
1196 | 0 | std::string* /* errmsg */) { |
1197 | 0 | size_t colon = uri.find(':'); |
1198 | 0 | if (colon != std::string::npos) { |
1199 | 0 | size_t block_size = ParseSizeT(uri.substr(colon + 1)); |
1200 | 0 | guard->reset(new ROT13BlockCipher(block_size)); |
1201 | 0 | } else { |
1202 | 0 | guard->reset(new ROT13BlockCipher(32)); |
1203 | 0 | } |
1204 | |
|
1205 | 0 | return guard->get(); |
1206 | 0 | }); |
1207 | 0 | }); |
1208 | 0 | } |
1209 | | } // namespace |
1210 | | |
1211 | | Status BlockCipher::CreateFromString(const ConfigOptions& config_options, |
1212 | | const std::string& value, |
1213 | 0 | std::shared_ptr<BlockCipher>* result) { |
1214 | 0 | RegisterEncryptionBuiltins(); |
1215 | 0 | return LoadSharedObject<BlockCipher>(config_options, value, result); |
1216 | 0 | } |
1217 | | |
1218 | | Status EncryptionProvider::CreateFromString( |
1219 | | const ConfigOptions& config_options, const std::string& value, |
1220 | 0 | std::shared_ptr<EncryptionProvider>* result) { |
1221 | 0 | RegisterEncryptionBuiltins(); |
1222 | 0 | return LoadSharedObject<EncryptionProvider>(config_options, value, result); |
1223 | 0 | } |
1224 | | |
1225 | | } // namespace ROCKSDB_NAMESPACE |