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