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).
8 #include "rocksdb/env_encryption.h"
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"
29 namespace ROCKSDB_NAMESPACE
{
31 std::shared_ptr
<EncryptionProvider
> EncryptionProvider::NewCTRProvider(
32 const std::shared_ptr
<BlockCipher
>& cipher
) {
33 return std::make_shared
<CTREncryptionProvider
>(cipher
);
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.
43 // REQUIRES: External synchronization
44 IOStatus
EncryptedSequentialFile::Read(size_t n
, const IOOptions
& options
,
45 Slice
* result
, char* scratch
,
46 IODebugContext
* dbg
) {
48 IOStatus io_s
= file_
->Read(n
, options
, result
, scratch
, dbg
);
53 PERF_TIMER_GUARD(decrypt_data_nanos
);
54 io_s
= status_to_io_status(
55 stream_
->Decrypt(offset_
, (char*)result
->data(), result
->size()));
58 offset_
+= result
->size(); // We've already ready data from disk, so update
59 // offset_ even if decryption fails.
64 // Skip "n" bytes from the file. This is guaranteed to be no
65 // slower that reading the same data, but may be faster.
67 // If end of file is reached, skipping will stop at the end of the
68 // file, and Skip will return OK.
70 // REQUIRES: External synchronization
71 IOStatus
EncryptedSequentialFile::Skip(uint64_t n
) {
72 auto status
= file_
->Skip(n
);
80 // Indicates the upper layers if the current SequentialFile implementation
82 bool EncryptedSequentialFile::use_direct_io() const {
83 return file_
->use_direct_io();
86 // Use the returned alignment value to allocate
87 // aligned buffer for Direct I/O
88 size_t EncryptedSequentialFile::GetRequiredBufferAlignment() const {
89 return file_
->GetRequiredBufferAlignment();
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.
95 IOStatus
EncryptedSequentialFile::InvalidateCache(size_t offset
,
97 return file_
->InvalidateCache(offset
+ prefixLength_
, length
);
100 // Positioned Read for direct I/O
101 // If Direct I/O enabled, offset, n, and scratch should be properly aligned
102 IOStatus
EncryptedSequentialFile::PositionedRead(uint64_t offset
, size_t n
,
103 const IOOptions
& options
,
104 Slice
* result
, char* scratch
,
105 IODebugContext
* dbg
) {
107 offset
+= prefixLength_
; // Skip prefix
108 auto io_s
= file_
->PositionedRead(offset
, n
, options
, result
, scratch
, dbg
);
112 offset_
= offset
+ result
->size();
114 PERF_TIMER_GUARD(decrypt_data_nanos
);
115 io_s
= status_to_io_status(
116 stream_
->Decrypt(offset
, (char*)result
->data(), result
->size()));
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
129 // Safe for concurrent use by multiple threads.
130 // If Direct I/O enabled, offset, n, and scratch should be aligned properly.
131 IOStatus
EncryptedRandomAccessFile::Read(uint64_t offset
, size_t n
,
132 const IOOptions
& options
,
133 Slice
* result
, char* scratch
,
134 IODebugContext
* dbg
) const {
136 offset
+= prefixLength_
;
137 auto io_s
= file_
->Read(offset
, n
, options
, result
, scratch
, dbg
);
142 PERF_TIMER_GUARD(decrypt_data_nanos
);
143 io_s
= status_to_io_status(
144 stream_
->Decrypt(offset
, (char*)result
->data(), result
->size()));
149 // Readahead the file starting from offset by n bytes for caching.
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
);
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.
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.
168 // This function guarantees that the returned ID will not be interpretable as
171 // Note: these IDs are only valid for the duration of the process.
172 size_t EncryptedRandomAccessFile::GetUniqueId(char* id
, size_t max_size
) const {
173 return file_
->GetUniqueId(id
, max_size
);
176 void EncryptedRandomAccessFile::Hint(AccessPattern pattern
) {
177 file_
->Hint(pattern
);
180 // Indicates the upper layers if the current RandomAccessFile implementation
182 bool EncryptedRandomAccessFile::use_direct_io() const {
183 return file_
->use_direct_io();
186 // Use the returned alignment value to allocate
187 // aligned buffer for Direct I/O
188 size_t EncryptedRandomAccessFile::GetRequiredBufferAlignment() const {
189 return file_
->GetRequiredBufferAlignment();
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.
195 IOStatus
EncryptedRandomAccessFile::InvalidateCache(size_t offset
,
197 return file_
->InvalidateCache(offset
+ prefixLength_
, length
);
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.
203 IOStatus
EncryptedWritableFile::Append(const Slice
& data
,
204 const IOOptions
& options
,
205 IODebugContext
* dbg
) {
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());
219 PERF_TIMER_GUARD(encrypt_data_nanos
);
220 io_s
= status_to_io_status(
221 stream_
->Encrypt(offset
, buf
.BufferStart(), buf
.CurrentSize()));
226 dataToAppend
= Slice(buf
.BufferStart(), buf
.CurrentSize());
228 return file_
->Append(dataToAppend
, options
, dbg
);
231 IOStatus
EncryptedWritableFile::PositionedAppend(const Slice
& data
,
233 const IOOptions
& options
,
234 IODebugContext
* dbg
) {
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());
246 PERF_TIMER_GUARD(encrypt_data_nanos
);
247 io_s
= status_to_io_status(
248 stream_
->Encrypt(offset
, buf
.BufferStart(), buf
.CurrentSize()));
253 dataToAppend
= Slice(buf
.BufferStart(), buf
.CurrentSize());
255 return file_
->PositionedAppend(dataToAppend
, offset
, options
, dbg
);
258 // Indicates the upper layers if the current WritableFile implementation
260 bool EncryptedWritableFile::use_direct_io() const {
261 return file_
->use_direct_io();
264 // true if Sync() and Fsync() are safe to call concurrently with Append()
266 bool EncryptedWritableFile::IsSyncThreadSafe() const {
267 return file_
->IsSyncThreadSafe();
270 // Use the returned alignment value to allocate
271 // aligned buffer for Direct I/O
272 size_t EncryptedWritableFile::GetRequiredBufferAlignment() const {
273 return file_
->GetRequiredBufferAlignment();
277 * Get the size of valid data in the file.
279 uint64_t EncryptedWritableFile::GetFileSize(const IOOptions
& options
,
280 IODebugContext
* dbg
) {
281 return file_
->GetFileSize(options
, dbg
) - prefixLength_
;
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.
288 IOStatus
EncryptedWritableFile::Truncate(uint64_t size
,
289 const IOOptions
& options
,
290 IODebugContext
* dbg
) {
291 return file_
->Truncate(size
+ prefixLength_
, options
, dbg
);
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.
298 IOStatus
EncryptedWritableFile::InvalidateCache(size_t offset
, size_t length
) {
299 return file_
->InvalidateCache(offset
+ prefixLength_
, length
);
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.
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
);
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
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
);
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
);
331 void EncryptedWritableFile::GetPreallocationStatus(
332 size_t* block_size
, size_t* last_allocated_block
) {
333 file_
->GetPreallocationStatus(block_size
, last_allocated_block
);
336 // Pre-allocates space for a file.
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
);
343 IOStatus
EncryptedWritableFile::Flush(const IOOptions
& options
,
344 IODebugContext
* dbg
) {
345 return file_
->Flush(options
, dbg
);
348 IOStatus
EncryptedWritableFile::Sync(const IOOptions
& options
,
349 IODebugContext
* dbg
) {
350 return file_
->Sync(options
, dbg
);
353 IOStatus
EncryptedWritableFile::Close(const IOOptions
& options
,
354 IODebugContext
* dbg
) {
355 return file_
->Close(options
, dbg
);
358 // A file abstraction for random reading and writing.
360 // Indicates if the class makes use of direct I/O
361 // If false you must pass aligned buffer to Write()
362 bool EncryptedRandomRWFile::use_direct_io() const {
363 return file_
->use_direct_io();
366 // Use the returned alignment value to allocate
367 // aligned buffer for Direct I/O
368 size_t EncryptedRandomRWFile::GetRequiredBufferAlignment() const {
369 return file_
->GetRequiredBufferAlignment();
372 // Write bytes in `data` at offset `offset`, Returns Status::OK() on success.
373 // Pass aligned buffer when use_direct_io() returns true.
374 IOStatus
EncryptedRandomRWFile::Write(uint64_t offset
, const Slice
& data
,
375 const IOOptions
& options
,
376 IODebugContext
* dbg
) {
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());
388 PERF_TIMER_GUARD(encrypt_data_nanos
);
389 io_s
= status_to_io_status(
390 stream_
->Encrypt(offset
, buf
.BufferStart(), buf
.CurrentSize()));
395 dataToWrite
= Slice(buf
.BufferStart(), buf
.CurrentSize());
397 return file_
->Write(offset
, dataToWrite
, options
, dbg
);
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.
403 IOStatus
EncryptedRandomRWFile::Read(uint64_t offset
, size_t n
,
404 const IOOptions
& options
, Slice
* result
,
405 char* scratch
, IODebugContext
* dbg
) const {
407 offset
+= prefixLength_
;
408 auto status
= file_
->Read(offset
, n
, options
, result
, scratch
, dbg
);
413 PERF_TIMER_GUARD(decrypt_data_nanos
);
414 status
= status_to_io_status(
415 stream_
->Decrypt(offset
, (char*)result
->data(), result
->size()));
420 IOStatus
EncryptedRandomRWFile::Flush(const IOOptions
& options
,
421 IODebugContext
* dbg
) {
422 return file_
->Flush(options
, dbg
);
425 IOStatus
EncryptedRandomRWFile::Sync(const IOOptions
& options
,
426 IODebugContext
* dbg
) {
427 return file_
->Sync(options
, dbg
);
430 IOStatus
EncryptedRandomRWFile::Fsync(const IOOptions
& options
,
431 IODebugContext
* dbg
) {
432 return file_
->Fsync(options
, dbg
);
435 IOStatus
EncryptedRandomRWFile::Close(const IOOptions
& options
,
436 IODebugContext
* dbg
) {
437 return file_
->Close(options
, dbg
);
441 static std::unordered_map
<std::string
, OptionTypeInfo
> encrypted_fs_type_info
=
444 OptionTypeInfo::AsCustomSharedPtr
<EncryptionProvider
>(
445 0 /* No offset, whole struct*/, OptionVerificationType::kByName
,
446 OptionTypeFlags::kNone
)},
448 // EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
449 // to files stored on disk.
450 class EncryptedFileSystemImpl
: public EncryptedFileSystem
{
452 const char* Name() const override
{
453 return EncryptedFileSystem::kClassName();
455 // Returns the raw encryption provider that should be used to write the input
456 // encrypted file. If there is no such provider, NotFound is returned.
457 IOStatus
GetWritableProvider(const std::string
& /*fname*/,
458 EncryptionProvider
** result
) {
460 *result
= provider_
.get();
461 return IOStatus::OK();
464 return IOStatus::NotFound("No WriteProvider specified");
468 // Returns the raw encryption provider that should be used to read the input
469 // encrypted file. If there is no such provider, NotFound is returned.
470 IOStatus
GetReadableProvider(const std::string
& /*fname*/,
471 EncryptionProvider
** result
) {
473 *result
= provider_
.get();
474 return IOStatus::OK();
477 return IOStatus::NotFound("No Provider specified");
481 // Creates a CipherStream for the underlying file/name using the options
482 // If a writable provider is found and encryption is enabled, uses
483 // this provider to create a cipher stream.
484 // @param fname Name of the writable file
485 // @param underlying The underlying "raw" file
486 // @param options Options for creating the file/cipher
487 // @param prefix_length Returns the length of the encryption prefix used for
489 // @param stream Returns the cipher stream to use for this file if it
490 // should be encrypted
491 // @return OK on success, non-OK on failure.
492 template <class TypeFile
>
493 IOStatus
CreateWritableCipherStream(
494 const std::string
& fname
, const std::unique_ptr
<TypeFile
>& underlying
,
495 const FileOptions
& options
, size_t* prefix_length
,
496 std::unique_ptr
<BlockAccessCipherStream
>* stream
, IODebugContext
* dbg
) {
497 EncryptionProvider
* provider
= nullptr;
499 IOStatus status
= GetWritableProvider(fname
, &provider
);
502 } else if (provider
!= nullptr) {
503 // Initialize & write prefix (if needed)
504 AlignedBuffer buffer
;
506 *prefix_length
= provider
->GetPrefixLength();
507 if (*prefix_length
> 0) {
509 buffer
.Alignment(underlying
->GetRequiredBufferAlignment());
510 buffer
.AllocateNewBuffer(*prefix_length
);
511 status
= status_to_io_status(provider
->CreateNewPrefix(
512 fname
, buffer
.BufferStart(), *prefix_length
));
514 buffer
.Size(*prefix_length
);
515 prefix
= Slice(buffer
.BufferStart(), buffer
.CurrentSize());
517 status
= underlying
->Append(prefix
, options
.io_options
, dbg
);
523 // Create cipher stream
524 status
= status_to_io_status(
525 provider
->CreateCipherStream(fname
, options
, prefix
, stream
));
530 template <class TypeFile
>
531 IOStatus
CreateWritableEncryptedFile(const std::string
& fname
,
532 std::unique_ptr
<TypeFile
>& underlying
,
533 const FileOptions
& options
,
534 std::unique_ptr
<TypeFile
>* result
,
535 IODebugContext
* dbg
) {
536 // Create cipher stream
537 std::unique_ptr
<BlockAccessCipherStream
> stream
;
538 size_t prefix_length
;
539 IOStatus status
= CreateWritableCipherStream(fname
, underlying
, options
,
540 &prefix_length
, &stream
, dbg
);
543 result
->reset(new EncryptedWritableFile(
544 std::move(underlying
), std::move(stream
), prefix_length
));
546 result
->reset(underlying
.release());
552 // Creates a CipherStream for the underlying file/name using the options
553 // If a writable provider is found and encryption is enabled, uses
554 // this provider to create a cipher stream.
555 // @param fname Name of the writable file
556 // @param underlying The underlying "raw" file
557 // @param options Options for creating the file/cipher
558 // @param prefix_length Returns the length of the encryption prefix used for
560 // @param stream Returns the cipher stream to use for this file if it
561 // should be encrypted
562 // @return OK on success, non-OK on failure.
563 template <class TypeFile
>
564 IOStatus
CreateRandomWriteCipherStream(
565 const std::string
& fname
, const std::unique_ptr
<TypeFile
>& underlying
,
566 const FileOptions
& options
, size_t* prefix_length
,
567 std::unique_ptr
<BlockAccessCipherStream
>* stream
, IODebugContext
* dbg
) {
568 EncryptionProvider
* provider
= nullptr;
570 IOStatus io_s
= GetWritableProvider(fname
, &provider
);
573 } else if (provider
!= nullptr) {
574 // Initialize & write prefix (if needed)
575 AlignedBuffer buffer
;
577 *prefix_length
= provider
->GetPrefixLength();
578 if (*prefix_length
> 0) {
580 buffer
.Alignment(underlying
->GetRequiredBufferAlignment());
581 buffer
.AllocateNewBuffer(*prefix_length
);
582 io_s
= status_to_io_status(provider
->CreateNewPrefix(
583 fname
, buffer
.BufferStart(), *prefix_length
));
585 buffer
.Size(*prefix_length
);
586 prefix
= Slice(buffer
.BufferStart(), buffer
.CurrentSize());
588 io_s
= underlying
->Write(0, prefix
, options
.io_options
, dbg
);
594 // Create cipher stream
595 io_s
= status_to_io_status(
596 provider
->CreateCipherStream(fname
, options
, prefix
, stream
));
601 // Creates a CipherStream for the underlying file/name using the options
602 // If a readable provider is found and the file is encrypted, uses
603 // this provider to create a cipher stream.
604 // @param fname Name of the writable file
605 // @param underlying The underlying "raw" file
606 // @param options Options for creating the file/cipher
607 // @param prefix_length Returns the length of the encryption prefix used for
609 // @param stream Returns the cipher stream to use for this file if it
611 // @return OK on success, non-OK on failure.
612 template <class TypeFile
>
613 IOStatus
CreateSequentialCipherStream(
614 const std::string
& fname
, const std::unique_ptr
<TypeFile
>& underlying
,
615 const FileOptions
& options
, size_t* prefix_length
,
616 std::unique_ptr
<BlockAccessCipherStream
>* stream
, IODebugContext
* dbg
) {
617 // Read prefix (if needed)
618 AlignedBuffer buffer
;
620 *prefix_length
= provider_
->GetPrefixLength();
621 if (*prefix_length
> 0) {
623 buffer
.Alignment(underlying
->GetRequiredBufferAlignment());
624 buffer
.AllocateNewBuffer(*prefix_length
);
625 IOStatus status
= underlying
->Read(*prefix_length
, options
.io_options
,
626 &prefix
, buffer
.BufferStart(), dbg
);
630 buffer
.Size(*prefix_length
);
632 return status_to_io_status(
633 provider_
->CreateCipherStream(fname
, options
, prefix
, stream
));
636 // Creates a CipherStream for the underlying file/name using the options
637 // If a readable provider is found and the file is encrypted, uses
638 // this provider to create a cipher stream.
639 // @param fname Name of the writable file
640 // @param underlying The underlying "raw" file
641 // @param options Options for creating the file/cipher
642 // @param prefix_length Returns the length of the encryption prefix used for
644 // @param stream Returns the cipher stream to use for this file if it
646 // @return OK on success, non-OK on failure.
647 template <class TypeFile
>
648 IOStatus
CreateRandomReadCipherStream(
649 const std::string
& fname
, const std::unique_ptr
<TypeFile
>& underlying
,
650 const FileOptions
& options
, size_t* prefix_length
,
651 std::unique_ptr
<BlockAccessCipherStream
>* stream
, IODebugContext
* dbg
) {
652 // Read prefix (if needed)
653 AlignedBuffer buffer
;
655 *prefix_length
= provider_
->GetPrefixLength();
656 if (*prefix_length
> 0) {
658 buffer
.Alignment(underlying
->GetRequiredBufferAlignment());
659 buffer
.AllocateNewBuffer(*prefix_length
);
660 IOStatus status
= underlying
->Read(0, *prefix_length
, options
.io_options
,
661 &prefix
, buffer
.BufferStart(), dbg
);
665 buffer
.Size(*prefix_length
);
667 return status_to_io_status(
668 provider_
->CreateCipherStream(fname
, options
, prefix
, stream
));
672 EncryptedFileSystemImpl(const std::shared_ptr
<FileSystem
>& base
,
673 const std::shared_ptr
<EncryptionProvider
>& provider
)
674 : EncryptedFileSystem(base
) {
675 provider_
= provider
;
676 RegisterOptions("EncryptionProvider", &provider_
, &encrypted_fs_type_info
);
679 Status
AddCipher(const std::string
& descriptor
, const char* cipher
,
680 size_t len
, bool for_write
) override
{
681 return provider_
->AddCipher(descriptor
, cipher
, len
, for_write
);
684 // NewSequentialFile opens a file for sequential reading.
685 IOStatus
NewSequentialFile(const std::string
& fname
,
686 const FileOptions
& options
,
687 std::unique_ptr
<FSSequentialFile
>* result
,
688 IODebugContext
* dbg
) override
{
690 if (options
.use_mmap_reads
) {
691 return IOStatus::InvalidArgument();
693 // Open file using underlying Env implementation
694 std::unique_ptr
<FSSequentialFile
> underlying
;
696 FileSystemWrapper::NewSequentialFile(fname
, options
, &underlying
, dbg
);
701 status
= FileSystemWrapper::GetFileSize(fname
, options
.io_options
,
707 *result
= std::move(underlying
);
710 // Create cipher stream
711 std::unique_ptr
<BlockAccessCipherStream
> stream
;
712 size_t prefix_length
;
713 status
= CreateSequentialCipherStream(fname
, underlying
, options
,
714 &prefix_length
, &stream
, dbg
);
716 result
->reset(new EncryptedSequentialFile(
717 std::move(underlying
), std::move(stream
), prefix_length
));
722 // NewRandomAccessFile opens a file for random read access.
723 IOStatus
NewRandomAccessFile(const std::string
& fname
,
724 const FileOptions
& options
,
725 std::unique_ptr
<FSRandomAccessFile
>* result
,
726 IODebugContext
* dbg
) override
{
728 if (options
.use_mmap_reads
) {
729 return IOStatus::InvalidArgument();
731 // Open file using underlying Env implementation
732 std::unique_ptr
<FSRandomAccessFile
> underlying
;
733 auto status
= FileSystemWrapper::NewRandomAccessFile(fname
, options
,
738 std::unique_ptr
<BlockAccessCipherStream
> stream
;
739 size_t prefix_length
;
740 status
= CreateRandomReadCipherStream(fname
, underlying
, options
,
741 &prefix_length
, &stream
, dbg
);
744 result
->reset(new EncryptedRandomAccessFile(
745 std::move(underlying
), std::move(stream
), prefix_length
));
747 result
->reset(underlying
.release());
753 // NewWritableFile opens a file for sequential writing.
754 IOStatus
NewWritableFile(const std::string
& fname
, const FileOptions
& options
,
755 std::unique_ptr
<FSWritableFile
>* result
,
756 IODebugContext
* dbg
) override
{
758 if (options
.use_mmap_writes
) {
759 return IOStatus::InvalidArgument();
761 // Open file using underlying Env implementation
762 std::unique_ptr
<FSWritableFile
> underlying
;
764 FileSystemWrapper::NewWritableFile(fname
, options
, &underlying
, dbg
);
768 return CreateWritableEncryptedFile(fname
, underlying
, options
, result
, dbg
);
771 // Create an object that writes to a new file with the specified
772 // name. Deletes any existing file with the same name and creates a
773 // new file. On success, stores a pointer to the new file in
774 // *result and returns OK. On failure stores nullptr in *result and
777 // The returned file will only be accessed by one thread at a time.
778 IOStatus
ReopenWritableFile(const std::string
& fname
,
779 const FileOptions
& options
,
780 std::unique_ptr
<FSWritableFile
>* result
,
781 IODebugContext
* dbg
) override
{
783 if (options
.use_mmap_writes
) {
784 return IOStatus::InvalidArgument();
786 // Open file using underlying Env implementation
787 std::unique_ptr
<FSWritableFile
> underlying
;
789 FileSystemWrapper::ReopenWritableFile(fname
, options
, &underlying
, dbg
);
793 return CreateWritableEncryptedFile(fname
, underlying
, options
, result
, dbg
);
796 // Reuse an existing file by renaming it and opening it as writable.
797 IOStatus
ReuseWritableFile(const std::string
& fname
,
798 const std::string
& old_fname
,
799 const FileOptions
& options
,
800 std::unique_ptr
<FSWritableFile
>* result
,
801 IODebugContext
* dbg
) override
{
803 if (options
.use_mmap_writes
) {
804 return IOStatus::InvalidArgument();
806 // Open file using underlying Env implementation
807 std::unique_ptr
<FSWritableFile
> underlying
;
808 auto status
= FileSystemWrapper::ReuseWritableFile(
809 fname
, old_fname
, options
, &underlying
, dbg
);
813 return CreateWritableEncryptedFile(fname
, underlying
, options
, result
, dbg
);
816 // Open `fname` for random read and write, if file doesn't exist the file
817 // will be created. On success, stores a pointer to the new file in
818 // *result and returns OK. On failure returns non-OK.
820 // The returned file will only be accessed by one thread at a time.
821 IOStatus
NewRandomRWFile(const std::string
& fname
, const FileOptions
& options
,
822 std::unique_ptr
<FSRandomRWFile
>* result
,
823 IODebugContext
* dbg
) override
{
825 if (options
.use_mmap_reads
|| options
.use_mmap_writes
) {
826 return IOStatus::InvalidArgument();
829 bool isNewFile
= !FileExists(fname
, options
.io_options
, dbg
).ok();
831 // Open file using underlying Env implementation
832 std::unique_ptr
<FSRandomRWFile
> underlying
;
834 FileSystemWrapper::NewRandomRWFile(fname
, options
, &underlying
, dbg
);
838 // Create cipher stream
839 std::unique_ptr
<BlockAccessCipherStream
> stream
;
840 size_t prefix_length
= 0;
842 // File already exists, read prefix
843 status
= CreateRandomReadCipherStream(fname
, underlying
, options
,
844 &prefix_length
, &stream
, dbg
);
846 status
= CreateRandomWriteCipherStream(fname
, underlying
, options
,
847 &prefix_length
, &stream
, dbg
);
851 result
->reset(new EncryptedRandomRWFile(
852 std::move(underlying
), std::move(stream
), prefix_length
));
854 result
->reset(underlying
.release());
860 // Store in *result the attributes of the children of the specified
862 // In case the implementation lists the directory prior to iterating the
864 // and files are concurrently deleted, the deleted files will be omitted
867 // The name attributes are relative to "dir".
868 // Original contents of *results are dropped.
869 // Returns OK if "dir" exists and "*result" contains its children.
870 // NotFound if "dir" does not exist, the calling process does not
872 // permission to access "dir", or if "dir" is invalid.
873 // IOError if an IO Error was encountered
874 IOStatus
GetChildrenFileAttributes(const std::string
& dir
,
875 const IOOptions
& options
,
876 std::vector
<FileAttributes
>* result
,
877 IODebugContext
* dbg
) override
{
879 FileSystemWrapper::GetChildrenFileAttributes(dir
, options
, result
, dbg
);
883 for (auto it
= std::begin(*result
); it
!= std::end(*result
); ++it
) {
884 // assert(it->size_bytes >= prefixLength);
885 // breaks env_basic_test when called on directory containing
887 // which makes subtraction of prefixLength worrisome since
888 // FileAttributes does not identify directories
889 EncryptionProvider
* provider
;
890 status
= GetReadableProvider(it
->name
, &provider
);
893 } else if (provider
!= nullptr) {
894 it
->size_bytes
-= provider
->GetPrefixLength();
897 return IOStatus::OK();
900 // Store the size of fname in *file_size.
901 IOStatus
GetFileSize(const std::string
& fname
, const IOOptions
& options
,
902 uint64_t* file_size
, IODebugContext
* dbg
) override
{
904 FileSystemWrapper::GetFileSize(fname
, options
, file_size
, dbg
);
905 if (!status
.ok() || !(*file_size
)) {
908 EncryptionProvider
* provider
;
909 status
= GetReadableProvider(fname
, &provider
);
910 if (provider
!= nullptr && status
.ok()) {
911 size_t prefixLength
= provider
->GetPrefixLength();
912 assert(*file_size
>= prefixLength
);
913 *file_size
-= prefixLength
;
919 std::shared_ptr
<EncryptionProvider
> provider_
;
923 Status
NewEncryptedFileSystemImpl(
924 const std::shared_ptr
<FileSystem
>& base
,
925 const std::shared_ptr
<EncryptionProvider
>& provider
,
926 std::unique_ptr
<FileSystem
>* result
) {
927 result
->reset(new EncryptedFileSystemImpl(base
, provider
));
931 std::shared_ptr
<FileSystem
> NewEncryptedFS(
932 const std::shared_ptr
<FileSystem
>& base
,
933 const std::shared_ptr
<EncryptionProvider
>& provider
) {
934 std::unique_ptr
<FileSystem
> efs
;
935 Status s
= NewEncryptedFileSystemImpl(base
, provider
, &efs
);
937 s
= efs
->PrepareOptions(ConfigOptions());
940 std::shared_ptr
<FileSystem
> result(efs
.release());
946 // Returns an Env that encrypts data when stored on disk and decrypts data when
948 Env
* NewEncryptedEnv(Env
* base_env
,
949 const std::shared_ptr
<EncryptionProvider
>& provider
) {
950 return new CompositeEnvWrapper(
951 base_env
, NewEncryptedFS(base_env
->GetFileSystem(), provider
));
954 // Encrypt one or more (partial) blocks of data at the file offset.
955 // Length of data is given in dataSize.
956 Status
BlockAccessCipherStream::Encrypt(uint64_t fileOffset
, char* data
,
958 // Calculate block index
959 auto blockSize
= BlockSize();
960 uint64_t blockIndex
= fileOffset
/ blockSize
;
961 size_t blockOffset
= fileOffset
% blockSize
;
962 std::unique_ptr
<char[]> blockBuffer
;
965 AllocateScratch(scratch
);
967 // Encrypt individual blocks.
970 size_t n
= std::min(dataSize
, blockSize
- blockOffset
);
971 if (n
!= blockSize
) {
972 // We're not encrypting a full block.
973 // Copy data to blockBuffer
974 if (!blockBuffer
.get()) {
976 blockBuffer
= std::unique_ptr
<char[]>(new char[blockSize
]);
978 block
= blockBuffer
.get();
979 // Copy plain data to block buffer
980 memmove(block
+ blockOffset
, data
, n
);
982 auto status
= EncryptBlock(blockIndex
, block
, (char*)scratch
.data());
987 // Copy encrypted data back to `data`.
988 memmove(data
, block
+ blockOffset
, n
);
1000 // Decrypt one or more (partial) blocks of data at the file offset.
1001 // Length of data is given in dataSize.
1002 Status
BlockAccessCipherStream::Decrypt(uint64_t fileOffset
, char* data
,
1004 // Calculate block index
1005 auto blockSize
= BlockSize();
1006 uint64_t blockIndex
= fileOffset
/ blockSize
;
1007 size_t blockOffset
= fileOffset
% blockSize
;
1008 std::unique_ptr
<char[]> blockBuffer
;
1010 std::string scratch
;
1011 AllocateScratch(scratch
);
1013 // Decrypt individual blocks.
1016 size_t n
= std::min(dataSize
, blockSize
- blockOffset
);
1017 if (n
!= blockSize
) {
1018 // We're not decrypting a full block.
1019 // Copy data to blockBuffer
1020 if (!blockBuffer
.get()) {
1022 blockBuffer
= std::unique_ptr
<char[]>(new char[blockSize
]);
1024 block
= blockBuffer
.get();
1025 // Copy encrypted data to block buffer
1026 memmove(block
+ blockOffset
, data
, n
);
1028 auto status
= DecryptBlock(blockIndex
, block
, (char*)scratch
.data());
1032 if (block
!= data
) {
1033 // Copy decrypted data back to `data`.
1034 memmove(data
, block
+ blockOffset
, n
);
1037 // Simply decrementing dataSize by n could cause it to underflow,
1038 // which will very likely make it read over the original bounds later
1039 assert(dataSize
>= n
);
1041 return Status::Corruption("Cannot decrypt data at given offset");
1045 if (dataSize
== 0) {
1046 return Status::OK();
1055 static std::unordered_map
<std::string
, OptionTypeInfo
>
1056 rot13_block_cipher_type_info
= {
1058 {0 /* No offset, whole struct*/, OptionType::kInt
,
1059 OptionVerificationType::kNormal
, OptionTypeFlags::kNone
}},
1061 // Implements a BlockCipher using ROT13.
1063 // Note: This is a sample implementation of BlockCipher,
1064 // it is NOT considered safe and should NOT be used in production.
1065 class ROT13BlockCipher
: public BlockCipher
{
1070 explicit ROT13BlockCipher(size_t blockSize
) : blockSize_(blockSize
) {
1071 RegisterOptions("ROT13BlockCipherOptions", &blockSize_
,
1072 &rot13_block_cipher_type_info
);
1075 static const char* kClassName() { return "ROT13"; }
1076 const char* Name() const override
{ return kClassName(); }
1077 // BlockSize returns the size of each block supported by this cipher stream.
1078 size_t BlockSize() override
{ return blockSize_
; }
1080 // Encrypt a block of data.
1081 // Length of data is equal to BlockSize().
1082 Status
Encrypt(char* data
) override
{
1083 for (size_t i
= 0; i
< blockSize_
; ++i
) {
1086 return Status::OK();
1089 // Decrypt a block of data.
1090 // Length of data is equal to BlockSize().
1091 Status
Decrypt(char* data
) override
{ return Encrypt(data
); }
1093 static const std::unordered_map
<std::string
, OptionTypeInfo
>
1094 ctr_encryption_provider_type_info
= {
1096 OptionTypeInfo::AsCustomSharedPtr
<BlockCipher
>(
1097 0 /* No offset, whole struct*/, OptionVerificationType::kByName
,
1098 OptionTypeFlags::kNone
)},
1100 } // anonymous namespace
1102 // Allocate scratch space which is passed to EncryptBlock/DecryptBlock.
1103 void CTRCipherStream::AllocateScratch(std::string
& scratch
) {
1104 auto blockSize
= cipher_
->BlockSize();
1105 scratch
.reserve(blockSize
);
1108 // Encrypt a block of data at the given block index.
1109 // Length of data is equal to BlockSize();
1110 Status
CTRCipherStream::EncryptBlock(uint64_t blockIndex
, char* data
,
1112 // Create nonce + counter
1113 auto blockSize
= cipher_
->BlockSize();
1114 memmove(scratch
, iv_
.data(), blockSize
);
1115 EncodeFixed64(scratch
, blockIndex
+ initialCounter_
);
1117 // Encrypt nonce+counter
1118 auto status
= cipher_
->Encrypt(scratch
);
1123 // XOR data with ciphertext.
1124 for (size_t i
= 0; i
< blockSize
; i
++) {
1125 data
[i
] = data
[i
] ^ scratch
[i
];
1127 return Status::OK();
1130 // Decrypt a block of data at the given block index.
1131 // Length of data is equal to BlockSize();
1132 Status
CTRCipherStream::DecryptBlock(uint64_t blockIndex
, char* data
,
1134 // For CTR decryption & encryption are the same
1135 return EncryptBlock(blockIndex
, data
, scratch
);
1138 CTREncryptionProvider::CTREncryptionProvider(
1139 const std::shared_ptr
<BlockCipher
>& c
)
1141 RegisterOptions("Cipher", &cipher_
, &ctr_encryption_provider_type_info
);
1144 bool CTREncryptionProvider::IsInstanceOf(const std::string
& name
) const {
1145 // Special case for test purposes.
1146 if (name
== "1://test" && cipher_
!= nullptr) {
1147 return cipher_
->IsInstanceOf(ROT13BlockCipher::kClassName());
1149 return EncryptionProvider::IsInstanceOf(name
);
1153 // GetPrefixLength returns the length of the prefix that is added to every file
1154 // and used for storing encryption options.
1155 // For optimal performance, the prefix length should be a multiple of
1157 size_t CTREncryptionProvider::GetPrefixLength() const {
1158 return defaultPrefixLength
;
1161 Status
CTREncryptionProvider::AddCipher(const std::string
& /*descriptor*/,
1162 const char* cipher
, size_t len
,
1163 bool /*for_write*/) {
1165 return Status::NotSupported("Cannot add keys to CTREncryptionProvider");
1166 } else if (strcmp(ROT13BlockCipher::kClassName(), cipher
) == 0) {
1167 cipher_
.reset(new ROT13BlockCipher(len
));
1168 return Status::OK();
1170 return BlockCipher::CreateFromString(ConfigOptions(), std::string(cipher
),
1175 // decodeCTRParameters decodes the initial counter & IV from the given
1176 // (plain text) prefix.
1177 static void decodeCTRParameters(const char* prefix
, size_t blockSize
,
1178 uint64_t& initialCounter
, Slice
& iv
) {
1179 // First block contains 64-bit initial counter
1180 initialCounter
= DecodeFixed64(prefix
);
1181 // Second block contains IV
1182 iv
= Slice(prefix
+ blockSize
, blockSize
);
1185 // CreateNewPrefix initialized an allocated block of prefix memory
1187 Status
CTREncryptionProvider::CreateNewPrefix(const std::string
& /*fname*/,
1189 size_t prefixLength
) const {
1191 return Status::InvalidArgument("Encryption Cipher is missing");
1193 // Create & seed rnd.
1194 Random
rnd((uint32_t)SystemClock::Default()->NowMicros());
1195 // Fill entire prefix block with random values.
1196 for (size_t i
= 0; i
< prefixLength
; i
++) {
1197 prefix
[i
] = rnd
.Uniform(256) & 0xFF;
1199 // Take random data to extract initial counter & IV
1200 auto blockSize
= cipher_
->BlockSize();
1201 uint64_t initialCounter
;
1203 decodeCTRParameters(prefix
, blockSize
, initialCounter
, prefixIV
);
1205 // Now populate the rest of the prefix, starting from the third block.
1206 PopulateSecretPrefixPart(prefix
+ (2 * blockSize
),
1207 prefixLength
- (2 * blockSize
), blockSize
);
1209 // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial
1210 // counter & IV unencrypted)
1211 CTRCipherStream
cipherStream(cipher_
, prefixIV
.data(), initialCounter
);
1214 PERF_TIMER_GUARD(encrypt_data_nanos
);
1215 status
= cipherStream
.Encrypt(0, prefix
+ (2 * blockSize
),
1216 prefixLength
- (2 * blockSize
));
1221 return Status::OK();
1224 // PopulateSecretPrefixPart initializes the data into a new prefix block
1226 // Returns the amount of space (starting from the start of the prefix)
1227 // that has been initialized.
1228 size_t CTREncryptionProvider::PopulateSecretPrefixPart(
1229 char* /*prefix*/, size_t /*prefixLength*/, size_t /*blockSize*/) const {
1230 // Nothing to do here, put in custom data in override when needed.
1234 Status
CTREncryptionProvider::CreateCipherStream(
1235 const std::string
& fname
, const EnvOptions
& options
, Slice
& prefix
,
1236 std::unique_ptr
<BlockAccessCipherStream
>* result
) {
1238 return Status::InvalidArgument("Encryption Cipher is missing");
1240 // Read plain text part of prefix.
1241 auto blockSize
= cipher_
->BlockSize();
1242 uint64_t initialCounter
;
1244 decodeCTRParameters(prefix
.data(), blockSize
, initialCounter
, iv
);
1246 // If the prefix is smaller than twice the block size, we would below read a
1247 // very large chunk of the file (and very likely read over the bounds)
1248 assert(prefix
.size() >= 2 * blockSize
);
1249 if (prefix
.size() < 2 * blockSize
) {
1250 return Status::Corruption("Unable to read from file " + fname
+
1251 ": read attempt would read beyond file bounds");
1254 // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1
1255 // with initial counter & IV are unencrypted)
1256 CTRCipherStream
cipherStream(cipher_
, iv
.data(), initialCounter
);
1259 PERF_TIMER_GUARD(decrypt_data_nanos
);
1260 status
= cipherStream
.Decrypt(0, (char*)prefix
.data() + (2 * blockSize
),
1261 prefix
.size() - (2 * blockSize
));
1267 // Create cipher stream
1268 return CreateCipherStreamFromPrefix(fname
, options
, initialCounter
, iv
,
1272 // CreateCipherStreamFromPrefix creates a block access cipher stream for a file
1273 // given given name and options. The given prefix is already decrypted.
1274 Status
CTREncryptionProvider::CreateCipherStreamFromPrefix(
1275 const std::string
& /*fname*/, const EnvOptions
& /*options*/,
1276 uint64_t initialCounter
, const Slice
& iv
, const Slice
& /*prefix*/,
1277 std::unique_ptr
<BlockAccessCipherStream
>* result
) {
1278 (*result
) = std::unique_ptr
<BlockAccessCipherStream
>(
1279 new CTRCipherStream(cipher_
, iv
.data(), initialCounter
));
1280 return Status::OK();
1284 static void RegisterEncryptionBuiltins() {
1285 static std::once_flag once
;
1286 std::call_once(once
, [&]() {
1287 auto lib
= ObjectRegistry::Default()->AddLibrary("encryption");
1288 // Match "CTR" or "CTR://test"
1289 lib
->AddFactory
<EncryptionProvider
>(
1290 ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true)
1291 .AddSuffix("://test"),
1292 [](const std::string
& uri
, std::unique_ptr
<EncryptionProvider
>* guard
,
1293 std::string
* /*errmsg*/) {
1294 if (EndsWith(uri
, "://test")) {
1295 std::shared_ptr
<BlockCipher
> cipher
=
1296 std::make_shared
<ROT13BlockCipher
>(32);
1297 guard
->reset(new CTREncryptionProvider(cipher
));
1299 guard
->reset(new CTREncryptionProvider());
1301 return guard
->get();
1304 lib
->AddFactory
<EncryptionProvider
>(
1305 "1://test", [](const std::string
& /*uri*/,
1306 std::unique_ptr
<EncryptionProvider
>* guard
,
1307 std::string
* /*errmsg*/) {
1308 std::shared_ptr
<BlockCipher
> cipher
=
1309 std::make_shared
<ROT13BlockCipher
>(32);
1310 guard
->reset(new CTREncryptionProvider(cipher
));
1311 return guard
->get();
1314 // Match "ROT13" or "ROT13:[0-9]+"
1315 lib
->AddFactory
<BlockCipher
>(
1316 ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true)
1318 [](const std::string
& uri
, std::unique_ptr
<BlockCipher
>* guard
,
1319 std::string
* /* errmsg */) {
1320 size_t colon
= uri
.find(':');
1321 if (colon
!= std::string::npos
) {
1322 size_t block_size
= ParseSizeT(uri
.substr(colon
+ 1));
1323 guard
->reset(new ROT13BlockCipher(block_size
));
1325 guard
->reset(new ROT13BlockCipher(32));
1328 return guard
->get();
1334 Status
BlockCipher::CreateFromString(const ConfigOptions
& config_options
,
1335 const std::string
& value
,
1336 std::shared_ptr
<BlockCipher
>* result
) {
1337 RegisterEncryptionBuiltins();
1338 return LoadSharedObject
<BlockCipher
>(config_options
, value
, nullptr, result
);
1341 Status
EncryptionProvider::CreateFromString(
1342 const ConfigOptions
& config_options
, const std::string
& value
,
1343 std::shared_ptr
<EncryptionProvider
>* result
) {
1344 RegisterEncryptionBuiltins();
1345 return LoadSharedObject
<EncryptionProvider
>(config_options
, value
, nullptr,
1349 #endif // ROCKSDB_LITE
1351 } // namespace ROCKSDB_NAMESPACE