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