]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/env/env_encryption.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rocksdb / env / env_encryption.cc
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
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
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
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.
82 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
88 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.
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
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.
131 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.
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.
172 size_t EncryptedRandomAccessFile::GetUniqueId(char* id, size_t max_size) const {
173 return file_->GetUniqueId(id, max_size);
174 };
175
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.
182 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
188 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.
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.
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
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.
260 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().
266 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
272 size_t EncryptedWritableFile::GetRequiredBufferAlignment() const {
273 return file_->GetRequiredBufferAlignment();
274 }
275
276 /*
277 * Get the size of valid data in the file.
278 */
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.
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.
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.
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.
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
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
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.
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
343 IOStatus EncryptedWritableFile::Flush(const IOOptions& options,
344 IODebugContext* dbg) {
345 return file_->Flush(options, dbg);
346 }
347
348 IOStatus EncryptedWritableFile::Sync(const IOOptions& options,
349 IODebugContext* dbg) {
350 return file_->Sync(options, dbg);
351 }
352
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()
362 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
368 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.
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.
403 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
420 IOStatus EncryptedRandomRWFile::Flush(const IOOptions& options,
421 IODebugContext* dbg) {
422 return file_->Flush(options, dbg);
423 }
424
425 IOStatus EncryptedRandomRWFile::Sync(const IOOptions& options,
426 IODebugContext* dbg) {
427 return file_->Sync(options, dbg);
428 }
429
430 IOStatus EncryptedRandomRWFile::Fsync(const IOOptions& options,
431 IODebugContext* dbg) {
432 return file_->Fsync(options, dbg);
433 }
434
435 IOStatus EncryptedRandomRWFile::Close(const IOOptions& options,
436 IODebugContext* dbg) {
437 return file_->Close(options, dbg);
438 }
439
440 namespace {
441 static std::unordered_map<std::string, OptionTypeInfo> encrypted_fs_type_info =
442 {
443 {"provider",
444 OptionTypeInfo::AsCustomSharedPtr<EncryptionProvider>(
445 0 /* No offset, whole struct*/, OptionVerificationType::kByName,
446 OptionTypeFlags::kNone)},
447 };
448 // EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
449 // to files stored on disk.
450 class EncryptedFileSystemImpl : public EncryptedFileSystem {
451 public:
452 const char* Name() const override {
453 return EncryptedFileSystem::kClassName();
454 }
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) {
459 if (provider_) {
460 *result = provider_.get();
461 return IOStatus::OK();
462 } else {
463 *result = nullptr;
464 return IOStatus::NotFound("No WriteProvider specified");
465 }
466 }
467
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) {
472 if (provider_) {
473 *result = provider_.get();
474 return IOStatus::OK();
475 } else {
476 *result = nullptr;
477 return IOStatus::NotFound("No Provider specified");
478 }
479 }
480
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
488 // this file
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;
498 *prefix_length = 0;
499 IOStatus status = GetWritableProvider(fname, &provider);
500 if (!status.ok()) {
501 return status;
502 } else if (provider != nullptr) {
503 // Initialize & write prefix (if needed)
504 AlignedBuffer buffer;
505 Slice prefix;
506 *prefix_length = provider->GetPrefixLength();
507 if (*prefix_length > 0) {
508 // Initialize prefix
509 buffer.Alignment(underlying->GetRequiredBufferAlignment());
510 buffer.AllocateNewBuffer(*prefix_length);
511 status = status_to_io_status(provider->CreateNewPrefix(
512 fname, buffer.BufferStart(), *prefix_length));
513 if (status.ok()) {
514 buffer.Size(*prefix_length);
515 prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
516 // Write prefix
517 status = underlying->Append(prefix, options.io_options, dbg);
518 }
519 if (!status.ok()) {
520 return status;
521 }
522 }
523 // Create cipher stream
524 status = status_to_io_status(
525 provider->CreateCipherStream(fname, options, prefix, stream));
526 }
527 return status;
528 }
529
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);
541 if (status.ok()) {
542 if (stream) {
543 result->reset(new EncryptedWritableFile(
544 std::move(underlying), std::move(stream), prefix_length));
545 } else {
546 result->reset(underlying.release());
547 }
548 }
549 return status;
550 }
551
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
559 // this file
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;
569 *prefix_length = 0;
570 IOStatus io_s = GetWritableProvider(fname, &provider);
571 if (!io_s.ok()) {
572 return io_s;
573 } else if (provider != nullptr) {
574 // Initialize & write prefix (if needed)
575 AlignedBuffer buffer;
576 Slice prefix;
577 *prefix_length = provider->GetPrefixLength();
578 if (*prefix_length > 0) {
579 // Initialize prefix
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));
584 if (io_s.ok()) {
585 buffer.Size(*prefix_length);
586 prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
587 // Write prefix
588 io_s = underlying->Write(0, prefix, options.io_options, dbg);
589 }
590 if (!io_s.ok()) {
591 return io_s;
592 }
593 }
594 // Create cipher stream
595 io_s = status_to_io_status(
596 provider->CreateCipherStream(fname, options, prefix, stream));
597 }
598 return io_s;
599 }
600
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
608 // this file
609 // @param stream Returns the cipher stream to use for this file if it
610 // is encrypted
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;
619 Slice prefix;
620 *prefix_length = provider_->GetPrefixLength();
621 if (*prefix_length > 0) {
622 // Read prefix
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);
627 if (!status.ok()) {
628 return status;
629 }
630 buffer.Size(*prefix_length);
631 }
632 return status_to_io_status(
633 provider_->CreateCipherStream(fname, options, prefix, stream));
634 }
635
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
643 // this file
644 // @param stream Returns the cipher stream to use for this file if it
645 // is encrypted
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;
654 Slice prefix;
655 *prefix_length = provider_->GetPrefixLength();
656 if (*prefix_length > 0) {
657 // Read prefix
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);
662 if (!status.ok()) {
663 return status;
664 }
665 buffer.Size(*prefix_length);
666 }
667 return status_to_io_status(
668 provider_->CreateCipherStream(fname, options, prefix, stream));
669 }
670
671 public:
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);
677 }
678
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);
682 }
683
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 {
689 result->reset();
690 if (options.use_mmap_reads) {
691 return IOStatus::InvalidArgument();
692 }
693 // Open file using underlying Env implementation
694 std::unique_ptr<FSSequentialFile> underlying;
695 auto status =
696 FileSystemWrapper::NewSequentialFile(fname, options, &underlying, dbg);
697 if (!status.ok()) {
698 return status;
699 }
700 uint64_t file_size;
701 status = FileSystemWrapper::GetFileSize(fname, options.io_options,
702 &file_size, dbg);
703 if (!status.ok()) {
704 return status;
705 }
706 if (!file_size) {
707 *result = std::move(underlying);
708 return status;
709 }
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);
715 if (status.ok()) {
716 result->reset(new EncryptedSequentialFile(
717 std::move(underlying), std::move(stream), prefix_length));
718 }
719 return status;
720 }
721
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 {
727 result->reset();
728 if (options.use_mmap_reads) {
729 return IOStatus::InvalidArgument();
730 }
731 // Open file using underlying Env implementation
732 std::unique_ptr<FSRandomAccessFile> underlying;
733 auto status = FileSystemWrapper::NewRandomAccessFile(fname, options,
734 &underlying, dbg);
735 if (!status.ok()) {
736 return status;
737 }
738 std::unique_ptr<BlockAccessCipherStream> stream;
739 size_t prefix_length;
740 status = CreateRandomReadCipherStream(fname, underlying, options,
741 &prefix_length, &stream, dbg);
742 if (status.ok()) {
743 if (stream) {
744 result->reset(new EncryptedRandomAccessFile(
745 std::move(underlying), std::move(stream), prefix_length));
746 } else {
747 result->reset(underlying.release());
748 }
749 }
750 return status;
751 }
752
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 {
757 result->reset();
758 if (options.use_mmap_writes) {
759 return IOStatus::InvalidArgument();
760 }
761 // Open file using underlying Env implementation
762 std::unique_ptr<FSWritableFile> underlying;
763 IOStatus status =
764 FileSystemWrapper::NewWritableFile(fname, options, &underlying, dbg);
765 if (!status.ok()) {
766 return status;
767 }
768 return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
769 }
770
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
775 // returns non-OK.
776 //
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 {
782 result->reset();
783 if (options.use_mmap_writes) {
784 return IOStatus::InvalidArgument();
785 }
786 // Open file using underlying Env implementation
787 std::unique_ptr<FSWritableFile> underlying;
788 IOStatus status =
789 FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg);
790 if (!status.ok()) {
791 return status;
792 }
793 return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
794 }
795
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 {
802 result->reset();
803 if (options.use_mmap_writes) {
804 return IOStatus::InvalidArgument();
805 }
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);
810 if (!status.ok()) {
811 return status;
812 }
813 return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
814 }
815
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.
819 //
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 {
824 result->reset();
825 if (options.use_mmap_reads || options.use_mmap_writes) {
826 return IOStatus::InvalidArgument();
827 }
828 // Check file exists
829 bool isNewFile = !FileExists(fname, options.io_options, dbg).ok();
830
831 // Open file using underlying Env implementation
832 std::unique_ptr<FSRandomRWFile> underlying;
833 auto status =
834 FileSystemWrapper::NewRandomRWFile(fname, options, &underlying, dbg);
835 if (!status.ok()) {
836 return status;
837 }
838 // Create cipher stream
839 std::unique_ptr<BlockAccessCipherStream> stream;
840 size_t prefix_length = 0;
841 if (!isNewFile) {
842 // File already exists, read prefix
843 status = CreateRandomReadCipherStream(fname, underlying, options,
844 &prefix_length, &stream, dbg);
845 } else {
846 status = CreateRandomWriteCipherStream(fname, underlying, options,
847 &prefix_length, &stream, dbg);
848 }
849 if (status.ok()) {
850 if (stream) {
851 result->reset(new EncryptedRandomRWFile(
852 std::move(underlying), std::move(stream), prefix_length));
853 } else {
854 result->reset(underlying.release());
855 }
856 }
857 return status;
858 }
859
860 // Store in *result the attributes of the children of the specified
861 // directory.
862 // In case the implementation lists the directory prior to iterating the
863 // files
864 // and files are concurrently deleted, the deleted files will be omitted
865 // from
866 // result.
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
871 // have
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 {
878 auto status =
879 FileSystemWrapper::GetChildrenFileAttributes(dir, options, result, dbg);
880 if (!status.ok()) {
881 return status;
882 }
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
886 // directories
887 // which makes subtraction of prefixLength worrisome since
888 // FileAttributes does not identify directories
889 EncryptionProvider* provider;
890 status = GetReadableProvider(it->name, &provider);
891 if (!status.ok()) {
892 return status;
893 } else if (provider != nullptr) {
894 it->size_bytes -= provider->GetPrefixLength();
895 }
896 }
897 return IOStatus::OK();
898 }
899
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 {
903 auto status =
904 FileSystemWrapper::GetFileSize(fname, options, file_size, dbg);
905 if (!status.ok() || !(*file_size)) {
906 return status;
907 }
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;
914 }
915 return status;
916 }
917
918 private:
919 std::shared_ptr<EncryptionProvider> provider_;
920 };
921 } // namespace
922
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));
928 return Status::OK();
929 }
930
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);
936 if (s.ok()) {
937 s = efs->PrepareOptions(ConfigOptions());
938 }
939 if (s.ok()) {
940 std::shared_ptr<FileSystem> result(efs.release());
941 return result;
942 } else {
943 return nullptr;
944 }
945 }
946 // Returns an Env that encrypts data when stored on disk and decrypts data when
947 // read from disk.
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));
952 }
953
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,
957 size_t dataSize) {
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;
963
964 std::string scratch;
965 AllocateScratch(scratch);
966
967 // Encrypt individual blocks.
968 while (1) {
969 char* block = data;
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()) {
975 // Allocate buffer
976 blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
977 }
978 block = blockBuffer.get();
979 // Copy plain data to block buffer
980 memmove(block + blockOffset, data, n);
981 }
982 auto status = EncryptBlock(blockIndex, block, (char*)scratch.data());
983 if (!status.ok()) {
984 return status;
985 }
986 if (block != data) {
987 // Copy encrypted data back to `data`.
988 memmove(data, block + blockOffset, n);
989 }
990 dataSize -= n;
991 if (dataSize == 0) {
992 return Status::OK();
993 }
994 data += n;
995 blockOffset = 0;
996 blockIndex++;
997 }
998 }
999
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,
1003 size_t dataSize) {
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;
1009
1010 std::string scratch;
1011 AllocateScratch(scratch);
1012
1013 // Decrypt individual blocks.
1014 while (1) {
1015 char* block = data;
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()) {
1021 // Allocate buffer
1022 blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
1023 }
1024 block = blockBuffer.get();
1025 // Copy encrypted data to block buffer
1026 memmove(block + blockOffset, data, n);
1027 }
1028 auto status = DecryptBlock(blockIndex, block, (char*)scratch.data());
1029 if (!status.ok()) {
1030 return status;
1031 }
1032 if (block != data) {
1033 // Copy decrypted data back to `data`.
1034 memmove(data, block + blockOffset, n);
1035 }
1036
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);
1040 if (dataSize < n) {
1041 return Status::Corruption("Cannot decrypt data at given offset");
1042 }
1043
1044 dataSize -= n;
1045 if (dataSize == 0) {
1046 return Status::OK();
1047 }
1048 data += n;
1049 blockOffset = 0;
1050 blockIndex++;
1051 }
1052 }
1053
1054 namespace {
1055 static std::unordered_map<std::string, OptionTypeInfo>
1056 rot13_block_cipher_type_info = {
1057 {"block_size",
1058 {0 /* No offset, whole struct*/, OptionType::kInt,
1059 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
1060 };
1061 // Implements a BlockCipher using ROT13.
1062 //
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 {
1066 private:
1067 size_t blockSize_;
1068
1069 public:
1070 explicit ROT13BlockCipher(size_t blockSize) : blockSize_(blockSize) {
1071 RegisterOptions("ROT13BlockCipherOptions", &blockSize_,
1072 &rot13_block_cipher_type_info);
1073 }
1074
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_; }
1079
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) {
1084 data[i] += 13;
1085 }
1086 return Status::OK();
1087 }
1088
1089 // Decrypt a block of data.
1090 // Length of data is equal to BlockSize().
1091 Status Decrypt(char* data) override { return Encrypt(data); }
1092 };
1093 static const std::unordered_map<std::string, OptionTypeInfo>
1094 ctr_encryption_provider_type_info = {
1095 {"cipher",
1096 OptionTypeInfo::AsCustomSharedPtr<BlockCipher>(
1097 0 /* No offset, whole struct*/, OptionVerificationType::kByName,
1098 OptionTypeFlags::kNone)},
1099 };
1100 } // anonymous namespace
1101
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);
1106 }
1107
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,
1111 char* scratch) {
1112 // Create nonce + counter
1113 auto blockSize = cipher_->BlockSize();
1114 memmove(scratch, iv_.data(), blockSize);
1115 EncodeFixed64(scratch, blockIndex + initialCounter_);
1116
1117 // Encrypt nonce+counter
1118 auto status = cipher_->Encrypt(scratch);
1119 if (!status.ok()) {
1120 return status;
1121 }
1122
1123 // XOR data with ciphertext.
1124 for (size_t i = 0; i < blockSize; i++) {
1125 data[i] = data[i] ^ scratch[i];
1126 }
1127 return Status::OK();
1128 }
1129
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,
1133 char* scratch) {
1134 // For CTR decryption & encryption are the same
1135 return EncryptBlock(blockIndex, data, scratch);
1136 }
1137
1138 CTREncryptionProvider::CTREncryptionProvider(
1139 const std::shared_ptr<BlockCipher>& c)
1140 : cipher_(c) {
1141 RegisterOptions("Cipher", &cipher_, &ctr_encryption_provider_type_info);
1142 }
1143
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());
1148 } else {
1149 return EncryptionProvider::IsInstanceOf(name);
1150 }
1151 }
1152
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
1156 // the page size.
1157 size_t CTREncryptionProvider::GetPrefixLength() const {
1158 return defaultPrefixLength;
1159 }
1160
1161 Status CTREncryptionProvider::AddCipher(const std::string& /*descriptor*/,
1162 const char* cipher, size_t len,
1163 bool /*for_write*/) {
1164 if (cipher_) {
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();
1169 } else {
1170 return BlockCipher::CreateFromString(ConfigOptions(), std::string(cipher),
1171 &cipher_);
1172 }
1173 }
1174
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);
1183 }
1184
1185 // CreateNewPrefix initialized an allocated block of prefix memory
1186 // for a new file.
1187 Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/,
1188 char* prefix,
1189 size_t prefixLength) const {
1190 if (!cipher_) {
1191 return Status::InvalidArgument("Encryption Cipher is missing");
1192 }
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;
1198 }
1199 // Take random data to extract initial counter & IV
1200 auto blockSize = cipher_->BlockSize();
1201 uint64_t initialCounter;
1202 Slice prefixIV;
1203 decodeCTRParameters(prefix, blockSize, initialCounter, prefixIV);
1204
1205 // Now populate the rest of the prefix, starting from the third block.
1206 PopulateSecretPrefixPart(prefix + (2 * blockSize),
1207 prefixLength - (2 * blockSize), blockSize);
1208
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);
1212 Status status;
1213 {
1214 PERF_TIMER_GUARD(encrypt_data_nanos);
1215 status = cipherStream.Encrypt(0, prefix + (2 * blockSize),
1216 prefixLength - (2 * blockSize));
1217 }
1218 if (!status.ok()) {
1219 return status;
1220 }
1221 return Status::OK();
1222 }
1223
1224 // PopulateSecretPrefixPart initializes the data into a new prefix block
1225 // in plain text.
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.
1231 return 0;
1232 }
1233
1234 Status CTREncryptionProvider::CreateCipherStream(
1235 const std::string& fname, const EnvOptions& options, Slice& prefix,
1236 std::unique_ptr<BlockAccessCipherStream>* result) {
1237 if (!cipher_) {
1238 return Status::InvalidArgument("Encryption Cipher is missing");
1239 }
1240 // Read plain text part of prefix.
1241 auto blockSize = cipher_->BlockSize();
1242 uint64_t initialCounter;
1243 Slice iv;
1244 decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv);
1245
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");
1252 }
1253
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);
1257 Status status;
1258 {
1259 PERF_TIMER_GUARD(decrypt_data_nanos);
1260 status = cipherStream.Decrypt(0, (char*)prefix.data() + (2 * blockSize),
1261 prefix.size() - (2 * blockSize));
1262 }
1263 if (!status.ok()) {
1264 return status;
1265 }
1266
1267 // Create cipher stream
1268 return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv,
1269 prefix, result);
1270 }
1271
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();
1281 }
1282
1283 namespace {
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));
1298 } else {
1299 guard->reset(new CTREncryptionProvider());
1300 }
1301 return guard->get();
1302 });
1303
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();
1312 });
1313
1314 // Match "ROT13" or "ROT13:[0-9]+"
1315 lib->AddFactory<BlockCipher>(
1316 ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true)
1317 .AddNumber(":"),
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));
1324 } else {
1325 guard->reset(new ROT13BlockCipher(32));
1326 }
1327
1328 return guard->get();
1329 });
1330 });
1331 }
1332 } // namespace
1333
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);
1339 }
1340
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,
1346 result);
1347 }
1348
1349 #endif // ROCKSDB_LITE
1350
1351 } // namespace ROCKSDB_NAMESPACE