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