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