]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/env/env_encryption.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / rocksdb / env / env_encryption.cc
CommitLineData
11fdf7f2
TL
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 <algorithm>
9#include <cctype>
10#include <iostream>
11
12#include "rocksdb/env_encryption.h"
13#include "util/aligned_buffer.h"
14#include "util/coding.h"
15#include "util/random.h"
16
17#endif
18
19namespace rocksdb {
20
21#ifndef ROCKSDB_LITE
22
23class EncryptedSequentialFile : public SequentialFile {
24 private:
25 std::unique_ptr<SequentialFile> file_;
26 std::unique_ptr<BlockAccessCipherStream> stream_;
27 uint64_t offset_;
28 size_t prefixLength_;
29
30 public:
31 // Default ctor. Given underlying sequential file is supposed to be at
32 // offset == prefixLength.
33 EncryptedSequentialFile(SequentialFile* f, BlockAccessCipherStream* s, size_t prefixLength)
34 : file_(f), stream_(s), offset_(prefixLength), prefixLength_(prefixLength) {
35 }
36
37 // Read up to "n" bytes from the file. "scratch[0..n-1]" may be
38 // written by this routine. Sets "*result" to the data that was
39 // read (including if fewer than "n" bytes were successfully read).
40 // May set "*result" to point at data in "scratch[0..n-1]", so
41 // "scratch[0..n-1]" must be live when "*result" is used.
42 // If an error was encountered, returns a non-OK status.
43 //
44 // REQUIRES: External synchronization
45 virtual Status Read(size_t n, Slice* result, char* scratch) override {
46 assert(scratch);
47 Status status = file_->Read(n, result, scratch);
48 if (!status.ok()) {
49 return status;
50 }
51 status = stream_->Decrypt(offset_, (char*)result->data(), result->size());
52 offset_ += result->size(); // We've already ready data from disk, so update offset_ even if decryption fails.
53 return status;
54 }
55
56 // Skip "n" bytes from the file. This is guaranteed to be no
57 // slower that reading the same data, but may be faster.
58 //
59 // If end of file is reached, skipping will stop at the end of the
60 // file, and Skip will return OK.
61 //
62 // REQUIRES: External synchronization
63 virtual Status Skip(uint64_t n) override {
64 auto status = file_->Skip(n);
65 if (!status.ok()) {
66 return status;
67 }
68 offset_ += n;
69 return status;
70 }
71
72 // Indicates the upper layers if the current SequentialFile implementation
73 // uses direct IO.
74 virtual bool use_direct_io() const override {
75 return file_->use_direct_io();
76 }
77
78 // Use the returned alignment value to allocate
79 // aligned buffer for Direct I/O
80 virtual size_t GetRequiredBufferAlignment() const override {
81 return file_->GetRequiredBufferAlignment();
82 }
83
84 // Remove any kind of caching of data from the offset to offset+length
85 // of this file. If the length is 0, then it refers to the end of file.
86 // If the system is not caching the file contents, then this is a noop.
87 virtual Status InvalidateCache(size_t offset, size_t length) override {
88 return file_->InvalidateCache(offset + prefixLength_, length);
89 }
90
91 // Positioned Read for direct I/O
92 // If Direct I/O enabled, offset, n, and scratch should be properly aligned
93 virtual Status PositionedRead(uint64_t offset, size_t n, Slice* result, char* scratch) override {
94 assert(scratch);
95 offset += prefixLength_; // Skip prefix
96 auto status = file_->PositionedRead(offset, n, result, scratch);
97 if (!status.ok()) {
98 return status;
99 }
100 offset_ = offset + result->size();
101 status = stream_->Decrypt(offset, (char*)result->data(), result->size());
102 return status;
103 }
104
105};
106
107// A file abstraction for randomly reading the contents of a file.
108class EncryptedRandomAccessFile : public RandomAccessFile {
109 private:
110 std::unique_ptr<RandomAccessFile> file_;
111 std::unique_ptr<BlockAccessCipherStream> stream_;
112 size_t prefixLength_;
113
114 public:
115 EncryptedRandomAccessFile(RandomAccessFile* f, BlockAccessCipherStream* s, size_t prefixLength)
116 : file_(f), stream_(s), prefixLength_(prefixLength) { }
117
118 // Read up to "n" bytes from the file starting at "offset".
119 // "scratch[0..n-1]" may be written by this routine. Sets "*result"
120 // to the data that was read (including if fewer than "n" bytes were
121 // successfully read). May set "*result" to point at data in
122 // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when
123 // "*result" is used. If an error was encountered, returns a non-OK
124 // status.
125 //
126 // Safe for concurrent use by multiple threads.
127 // If Direct I/O enabled, offset, n, and scratch should be aligned properly.
128 virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const override {
129 assert(scratch);
130 offset += prefixLength_;
131 auto status = file_->Read(offset, n, result, scratch);
132 if (!status.ok()) {
133 return status;
134 }
135 status = stream_->Decrypt(offset, (char*)result->data(), result->size());
136 return status;
137 }
138
139 // Readahead the file starting from offset by n bytes for caching.
140 virtual Status Prefetch(uint64_t offset, size_t n) override {
141 //return Status::OK();
142 return file_->Prefetch(offset + prefixLength_, n);
143 }
144
145 // Tries to get an unique ID for this file that will be the same each time
146 // the file is opened (and will stay the same while the file is open).
147 // Furthermore, it tries to make this ID at most "max_size" bytes. If such an
148 // ID can be created this function returns the length of the ID and places it
149 // in "id"; otherwise, this function returns 0, in which case "id"
150 // may not have been modified.
151 //
152 // This function guarantees, for IDs from a given environment, two unique ids
153 // cannot be made equal to each other by adding arbitrary bytes to one of
154 // them. That is, no unique ID is the prefix of another.
155 //
156 // This function guarantees that the returned ID will not be interpretable as
157 // a single varint.
158 //
159 // Note: these IDs are only valid for the duration of the process.
160 virtual size_t GetUniqueId(char* id, size_t max_size) const override {
161 return file_->GetUniqueId(id, max_size);
162 };
163
164 virtual void Hint(AccessPattern pattern) override {
165 file_->Hint(pattern);
166 }
167
168 // Indicates the upper layers if the current RandomAccessFile implementation
169 // uses direct IO.
170 virtual bool use_direct_io() const override {
171 return file_->use_direct_io();
172 }
173
174 // Use the returned alignment value to allocate
175 // aligned buffer for Direct I/O
176 virtual size_t GetRequiredBufferAlignment() const override {
177 return file_->GetRequiredBufferAlignment();
178 }
179
180 // Remove any kind of caching of data from the offset to offset+length
181 // of this file. If the length is 0, then it refers to the end of file.
182 // If the system is not caching the file contents, then this is a noop.
183 virtual Status InvalidateCache(size_t offset, size_t length) override {
184 return file_->InvalidateCache(offset + prefixLength_, length);
185 }
186};
187
188// A file abstraction for sequential writing. The implementation
189// must provide buffering since callers may append small fragments
190// at a time to the file.
191class EncryptedWritableFile : public WritableFileWrapper {
192 private:
193 std::unique_ptr<WritableFile> file_;
194 std::unique_ptr<BlockAccessCipherStream> stream_;
195 size_t prefixLength_;
196
197 public:
198 // Default ctor. Prefix is assumed to be written already.
199 EncryptedWritableFile(WritableFile* f, BlockAccessCipherStream* s, size_t prefixLength)
200 : WritableFileWrapper(f), file_(f), stream_(s), prefixLength_(prefixLength) { }
201
202 Status Append(const Slice& data) override {
203 AlignedBuffer buf;
204 Status status;
205 Slice dataToAppend(data);
206 if (data.size() > 0) {
207 auto offset = file_->GetFileSize(); // size including prefix
208 // Encrypt in cloned buffer
209 buf.Alignment(GetRequiredBufferAlignment());
210 buf.AllocateNewBuffer(data.size());
211 memmove(buf.BufferStart(), data.data(), data.size());
212 status = stream_->Encrypt(offset, buf.BufferStart(), data.size());
213 if (!status.ok()) {
214 return status;
215 }
216 dataToAppend = Slice(buf.BufferStart(), data.size());
217 }
218 status = file_->Append(dataToAppend);
219 if (!status.ok()) {
220 return status;
221 }
222 return status;
223 }
224
225 Status PositionedAppend(const Slice& data, uint64_t offset) override {
226 AlignedBuffer buf;
227 Status status;
228 Slice dataToAppend(data);
229 offset += prefixLength_;
230 if (data.size() > 0) {
231 // Encrypt in cloned buffer
232 buf.Alignment(GetRequiredBufferAlignment());
233 buf.AllocateNewBuffer(data.size());
234 memmove(buf.BufferStart(), data.data(), data.size());
235 status = stream_->Encrypt(offset, buf.BufferStart(), data.size());
236 if (!status.ok()) {
237 return status;
238 }
239 dataToAppend = Slice(buf.BufferStart(), data.size());
240 }
241 status = file_->PositionedAppend(dataToAppend, offset);
242 if (!status.ok()) {
243 return status;
244 }
245 return status;
246 }
247
248 // Indicates the upper layers if the current WritableFile implementation
249 // uses direct IO.
250 virtual bool use_direct_io() const override { return file_->use_direct_io(); }
251
252 // Use the returned alignment value to allocate
253 // aligned buffer for Direct I/O
254 virtual size_t GetRequiredBufferAlignment() const override { return file_->GetRequiredBufferAlignment(); }
255
256 /*
257 * Get the size of valid data in the file.
258 */
259 virtual uint64_t GetFileSize() override {
260 return file_->GetFileSize() - prefixLength_;
261 }
262
263 // Truncate is necessary to trim the file to the correct size
264 // before closing. It is not always possible to keep track of the file
265 // size due to whole pages writes. The behavior is undefined if called
266 // with other writes to follow.
267 virtual Status Truncate(uint64_t size) override {
268 return file_->Truncate(size + prefixLength_);
269 }
270
271 // Remove any kind of caching of data from the offset to offset+length
272 // of this file. If the length is 0, then it refers to the end of file.
273 // If the system is not caching the file contents, then this is a noop.
274 // This call has no effect on dirty pages in the cache.
275 virtual Status InvalidateCache(size_t offset, size_t length) override {
276 return file_->InvalidateCache(offset + prefixLength_, length);
277 }
278
279 // Sync a file range with disk.
280 // offset is the starting byte of the file range to be synchronized.
281 // nbytes specifies the length of the range to be synchronized.
282 // This asks the OS to initiate flushing the cached data to disk,
283 // without waiting for completion.
284 // Default implementation does nothing.
285 virtual Status RangeSync(uint64_t offset, uint64_t nbytes) override {
286 return file_->RangeSync(offset + prefixLength_, nbytes);
287 }
288
289 // PrepareWrite performs any necessary preparation for a write
290 // before the write actually occurs. This allows for pre-allocation
291 // of space on devices where it can result in less file
292 // fragmentation and/or less waste from over-zealous filesystem
293 // pre-allocation.
294 virtual void PrepareWrite(size_t offset, size_t len) override {
295 file_->PrepareWrite(offset + prefixLength_, len);
296 }
297
298 // Pre-allocates space for a file.
299 virtual Status Allocate(uint64_t offset, uint64_t len) override {
300 return file_->Allocate(offset + prefixLength_, len);
301 }
302};
303
304// A file abstraction for random reading and writing.
305class EncryptedRandomRWFile : public RandomRWFile {
306 private:
307 std::unique_ptr<RandomRWFile> file_;
308 std::unique_ptr<BlockAccessCipherStream> stream_;
309 size_t prefixLength_;
310
311 public:
312 EncryptedRandomRWFile(RandomRWFile* f, BlockAccessCipherStream* s, size_t prefixLength)
313 : file_(f), stream_(s), prefixLength_(prefixLength) {}
314
315 // Indicates if the class makes use of direct I/O
316 // If false you must pass aligned buffer to Write()
317 virtual bool use_direct_io() const override { return file_->use_direct_io(); }
318
319 // Use the returned alignment value to allocate
320 // aligned buffer for Direct I/O
321 virtual size_t GetRequiredBufferAlignment() const override {
322 return file_->GetRequiredBufferAlignment();
323 }
324
325 // Write bytes in `data` at offset `offset`, Returns Status::OK() on success.
326 // Pass aligned buffer when use_direct_io() returns true.
327 virtual Status Write(uint64_t offset, const Slice& data) override {
328 AlignedBuffer buf;
329 Status status;
330 Slice dataToWrite(data);
331 offset += prefixLength_;
332 if (data.size() > 0) {
333 // Encrypt in cloned buffer
334 buf.Alignment(GetRequiredBufferAlignment());
335 buf.AllocateNewBuffer(data.size());
336 memmove(buf.BufferStart(), data.data(), data.size());
337 status = stream_->Encrypt(offset, buf.BufferStart(), data.size());
338 if (!status.ok()) {
339 return status;
340 }
341 dataToWrite = Slice(buf.BufferStart(), data.size());
342 }
343 status = file_->Write(offset, dataToWrite);
344 return status;
345 }
346
347 // Read up to `n` bytes starting from offset `offset` and store them in
348 // result, provided `scratch` size should be at least `n`.
349 // Returns Status::OK() on success.
350 virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const override {
351 assert(scratch);
352 offset += prefixLength_;
353 auto status = file_->Read(offset, n, result, scratch);
354 if (!status.ok()) {
355 return status;
356 }
357 status = stream_->Decrypt(offset, (char*)result->data(), result->size());
358 return status;
359 }
360
361 virtual Status Flush() override {
362 return file_->Flush();
363 }
364
365 virtual Status Sync() override {
366 return file_->Sync();
367 }
368
369 virtual Status Fsync() override {
370 return file_->Fsync();
371 }
372
373 virtual Status Close() override {
374 return file_->Close();
375 }
376};
377
378// EncryptedEnv implements an Env wrapper that adds encryption to files stored on disk.
379class EncryptedEnv : public EnvWrapper {
380 public:
381 EncryptedEnv(Env* base_env, EncryptionProvider *provider)
382 : EnvWrapper(base_env) {
383 provider_ = provider;
384 }
385
386 // NewSequentialFile opens a file for sequential reading.
387 virtual Status NewSequentialFile(const std::string& fname,
388 std::unique_ptr<SequentialFile>* result,
389 const EnvOptions& options) override {
390 result->reset();
391 if (options.use_mmap_reads) {
392 return Status::InvalidArgument();
393 }
394 // Open file using underlying Env implementation
395 std::unique_ptr<SequentialFile> underlying;
396 auto status = EnvWrapper::NewSequentialFile(fname, &underlying, options);
397 if (!status.ok()) {
398 return status;
399 }
400 // Read prefix (if needed)
401 AlignedBuffer prefixBuf;
402 Slice prefixSlice;
403 size_t prefixLength = provider_->GetPrefixLength();
404 if (prefixLength > 0) {
405 // Read prefix
406 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
407 prefixBuf.AllocateNewBuffer(prefixLength);
408 status = underlying->Read(prefixLength, &prefixSlice, prefixBuf.BufferStart());
409 if (!status.ok()) {
410 return status;
411 }
412 }
413 // Create cipher stream
414 std::unique_ptr<BlockAccessCipherStream> stream;
415 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
416 if (!status.ok()) {
417 return status;
418 }
419 (*result) = std::unique_ptr<SequentialFile>(new EncryptedSequentialFile(underlying.release(), stream.release(), prefixLength));
420 return Status::OK();
421 }
422
423 // NewRandomAccessFile opens a file for random read access.
424 virtual Status NewRandomAccessFile(const std::string& fname,
425 unique_ptr<RandomAccessFile>* result,
426 const EnvOptions& options) override {
427 result->reset();
428 if (options.use_mmap_reads) {
429 return Status::InvalidArgument();
430 }
431 // Open file using underlying Env implementation
432 std::unique_ptr<RandomAccessFile> underlying;
433 auto status = EnvWrapper::NewRandomAccessFile(fname, &underlying, options);
434 if (!status.ok()) {
435 return status;
436 }
437 // Read prefix (if needed)
438 AlignedBuffer prefixBuf;
439 Slice prefixSlice;
440 size_t prefixLength = provider_->GetPrefixLength();
441 if (prefixLength > 0) {
442 // Read prefix
443 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
444 prefixBuf.AllocateNewBuffer(prefixLength);
445 status = underlying->Read(0, prefixLength, &prefixSlice, prefixBuf.BufferStart());
446 if (!status.ok()) {
447 return status;
448 }
449 }
450 // Create cipher stream
451 std::unique_ptr<BlockAccessCipherStream> stream;
452 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
453 if (!status.ok()) {
454 return status;
455 }
456 (*result) = std::unique_ptr<RandomAccessFile>(new EncryptedRandomAccessFile(underlying.release(), stream.release(), prefixLength));
457 return Status::OK();
458 }
459
460 // NewWritableFile opens a file for sequential writing.
461 virtual Status NewWritableFile(const std::string& fname,
462 unique_ptr<WritableFile>* result,
463 const EnvOptions& options) override {
464 result->reset();
465 if (options.use_mmap_writes) {
466 return Status::InvalidArgument();
467 }
468 // Open file using underlying Env implementation
469 std::unique_ptr<WritableFile> underlying;
470 Status status = EnvWrapper::NewWritableFile(fname, &underlying, options);
471 if (!status.ok()) {
472 return status;
473 }
474 // Initialize & write prefix (if needed)
475 AlignedBuffer prefixBuf;
476 Slice prefixSlice;
477 size_t prefixLength = provider_->GetPrefixLength();
478 if (prefixLength > 0) {
479 // Initialize prefix
480 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
481 prefixBuf.AllocateNewBuffer(prefixLength);
482 provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
483 prefixSlice = Slice(prefixBuf.BufferStart(), prefixLength);
484 // Write prefix
485 status = underlying->Append(prefixSlice);
486 if (!status.ok()) {
487 return status;
488 }
489 }
490 // Create cipher stream
491 std::unique_ptr<BlockAccessCipherStream> stream;
492 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
493 if (!status.ok()) {
494 return status;
495 }
496 (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
497 return Status::OK();
498 }
499
500 // Create an object that writes to a new file with the specified
501 // name. Deletes any existing file with the same name and creates a
502 // new file. On success, stores a pointer to the new file in
503 // *result and returns OK. On failure stores nullptr in *result and
504 // returns non-OK.
505 //
506 // The returned file will only be accessed by one thread at a time.
507 virtual Status ReopenWritableFile(const std::string& fname,
508 unique_ptr<WritableFile>* result,
509 const EnvOptions& options) override {
510 result->reset();
511 if (options.use_mmap_writes) {
512 return Status::InvalidArgument();
513 }
514 // Open file using underlying Env implementation
515 std::unique_ptr<WritableFile> underlying;
516 Status status = EnvWrapper::ReopenWritableFile(fname, &underlying, options);
517 if (!status.ok()) {
518 return status;
519 }
520 // Initialize & write prefix (if needed)
521 AlignedBuffer prefixBuf;
522 Slice prefixSlice;
523 size_t prefixLength = provider_->GetPrefixLength();
524 if (prefixLength > 0) {
525 // Initialize prefix
526 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
527 prefixBuf.AllocateNewBuffer(prefixLength);
528 provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
529 prefixSlice = Slice(prefixBuf.BufferStart(), prefixLength);
530 // Write prefix
531 status = underlying->Append(prefixSlice);
532 if (!status.ok()) {
533 return status;
534 }
535 }
536 // Create cipher stream
537 std::unique_ptr<BlockAccessCipherStream> stream;
538 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
539 if (!status.ok()) {
540 return status;
541 }
542 (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
543 return Status::OK();
544 }
545
546 // Reuse an existing file by renaming it and opening it as writable.
547 virtual Status ReuseWritableFile(const std::string& fname,
548 const std::string& old_fname,
549 unique_ptr<WritableFile>* result,
550 const EnvOptions& options) override {
551 result->reset();
552 if (options.use_mmap_writes) {
553 return Status::InvalidArgument();
554 }
555 // Open file using underlying Env implementation
556 std::unique_ptr<WritableFile> underlying;
557 Status status = EnvWrapper::ReuseWritableFile(fname, old_fname, &underlying, options);
558 if (!status.ok()) {
559 return status;
560 }
561 // Initialize & write prefix (if needed)
562 AlignedBuffer prefixBuf;
563 Slice prefixSlice;
564 size_t prefixLength = provider_->GetPrefixLength();
565 if (prefixLength > 0) {
566 // Initialize prefix
567 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
568 prefixBuf.AllocateNewBuffer(prefixLength);
569 provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
570 prefixSlice = Slice(prefixBuf.BufferStart(), prefixLength);
571 // Write prefix
572 status = underlying->Append(prefixSlice);
573 if (!status.ok()) {
574 return status;
575 }
576 }
577 // Create cipher stream
578 std::unique_ptr<BlockAccessCipherStream> stream;
579 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
580 if (!status.ok()) {
581 return status;
582 }
583 (*result) = std::unique_ptr<WritableFile>(new EncryptedWritableFile(underlying.release(), stream.release(), prefixLength));
584 return Status::OK();
585 }
586
587 // Open `fname` for random read and write, if file doesn't exist the file
588 // will be created. On success, stores a pointer to the new file in
589 // *result and returns OK. On failure returns non-OK.
590 //
591 // The returned file will only be accessed by one thread at a time.
592 virtual Status NewRandomRWFile(const std::string& fname,
593 unique_ptr<RandomRWFile>* result,
594 const EnvOptions& options) override {
595 result->reset();
596 if (options.use_mmap_reads || options.use_mmap_writes) {
597 return Status::InvalidArgument();
598 }
599 // Check file exists
600 bool isNewFile = !FileExists(fname).ok();
601
602 // Open file using underlying Env implementation
603 std::unique_ptr<RandomRWFile> underlying;
604 Status status = EnvWrapper::NewRandomRWFile(fname, &underlying, options);
605 if (!status.ok()) {
606 return status;
607 }
608 // Read or Initialize & write prefix (if needed)
609 AlignedBuffer prefixBuf;
610 Slice prefixSlice;
611 size_t prefixLength = provider_->GetPrefixLength();
612 if (prefixLength > 0) {
613 prefixBuf.Alignment(underlying->GetRequiredBufferAlignment());
614 prefixBuf.AllocateNewBuffer(prefixLength);
615 if (!isNewFile) {
616 // File already exists, read prefix
617 status = underlying->Read(0, prefixLength, &prefixSlice, prefixBuf.BufferStart());
618 if (!status.ok()) {
619 return status;
620 }
621 } else {
622 // File is new, initialize & write prefix
623 provider_->CreateNewPrefix(fname, prefixBuf.BufferStart(), prefixLength);
624 prefixSlice = Slice(prefixBuf.BufferStart(), prefixLength);
625 // Write prefix
626 status = underlying->Write(0, prefixSlice);
627 if (!status.ok()) {
628 return status;
629 }
630 }
631 }
632 // Create cipher stream
633 std::unique_ptr<BlockAccessCipherStream> stream;
634 status = provider_->CreateCipherStream(fname, options, prefixSlice, &stream);
635 if (!status.ok()) {
636 return status;
637 }
638 (*result) = std::unique_ptr<RandomRWFile>(new EncryptedRandomRWFile(underlying.release(), stream.release(), prefixLength));
639 return Status::OK();
640 }
641
642 // Store in *result the attributes of the children of the specified directory.
643 // In case the implementation lists the directory prior to iterating the files
644 // and files are concurrently deleted, the deleted files will be omitted from
645 // result.
646 // The name attributes are relative to "dir".
647 // Original contents of *results are dropped.
648 // Returns OK if "dir" exists and "*result" contains its children.
649 // NotFound if "dir" does not exist, the calling process does not have
650 // permission to access "dir", or if "dir" is invalid.
651 // IOError if an IO Error was encountered
652 virtual Status GetChildrenFileAttributes(const std::string& dir, std::vector<FileAttributes>* result) override {
653 auto status = EnvWrapper::GetChildrenFileAttributes(dir, result);
654 if (!status.ok()) {
655 return status;
656 }
657 size_t prefixLength = provider_->GetPrefixLength();
658 for (auto it = std::begin(*result); it!=std::end(*result); ++it) {
659 assert(it->size_bytes >= prefixLength);
660 it->size_bytes -= prefixLength;
661 }
662 return Status::OK();
663 }
664
665 // Store the size of fname in *file_size.
666 virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
667 auto status = EnvWrapper::GetFileSize(fname, file_size);
668 if (!status.ok()) {
669 return status;
670 }
671 size_t prefixLength = provider_->GetPrefixLength();
672 assert(*file_size >= prefixLength);
673 *file_size -= prefixLength;
674 return Status::OK();
675 }
676
677 private:
678 EncryptionProvider *provider_;
679};
680
681
682// Returns an Env that encrypts data when stored on disk and decrypts data when
683// read from disk.
684Env* NewEncryptedEnv(Env* base_env, EncryptionProvider* provider) {
685 return new EncryptedEnv(base_env, provider);
686}
687
688// Encrypt one or more (partial) blocks of data at the file offset.
689// Length of data is given in dataSize.
690Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char *data, size_t dataSize) {
691 // Calculate block index
692 auto blockSize = BlockSize();
693 uint64_t blockIndex = fileOffset / blockSize;
694 size_t blockOffset = fileOffset % blockSize;
695 unique_ptr<char[]> blockBuffer;
696
697 std::string scratch;
698 AllocateScratch(scratch);
699
700 // Encrypt individual blocks.
701 while (1) {
702 char *block = data;
703 size_t n = std::min(dataSize, blockSize - blockOffset);
704 if (n != blockSize) {
705 // We're not encrypting a full block.
706 // Copy data to blockBuffer
707 if (!blockBuffer.get()) {
708 // Allocate buffer
709 blockBuffer = unique_ptr<char[]>(new char[blockSize]);
710 }
711 block = blockBuffer.get();
712 // Copy plain data to block buffer
713 memmove(block + blockOffset, data, n);
714 }
715 auto status = EncryptBlock(blockIndex, block, (char*)scratch.data());
716 if (!status.ok()) {
717 return status;
718 }
719 if (block != data) {
720 // Copy encrypted data back to `data`.
721 memmove(data, block + blockOffset, n);
722 }
723 dataSize -= n;
724 if (dataSize == 0) {
725 return Status::OK();
726 }
727 data += n;
728 blockOffset = 0;
729 blockIndex++;
730 }
731}
732
733// Decrypt one or more (partial) blocks of data at the file offset.
734// Length of data is given in dataSize.
735Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char *data, size_t dataSize) {
736 // Calculate block index
737 auto blockSize = BlockSize();
738 uint64_t blockIndex = fileOffset / blockSize;
739 size_t blockOffset = fileOffset % blockSize;
740 unique_ptr<char[]> blockBuffer;
741
742 std::string scratch;
743 AllocateScratch(scratch);
744
745 // Decrypt individual blocks.
746 while (1) {
747 char *block = data;
748 size_t n = std::min(dataSize, blockSize - blockOffset);
749 if (n != blockSize) {
750 // We're not decrypting a full block.
751 // Copy data to blockBuffer
752 if (!blockBuffer.get()) {
753 // Allocate buffer
754 blockBuffer = unique_ptr<char[]>(new char[blockSize]);
755 }
756 block = blockBuffer.get();
757 // Copy encrypted data to block buffer
758 memmove(block + blockOffset, data, n);
759 }
760 auto status = DecryptBlock(blockIndex, block, (char*)scratch.data());
761 if (!status.ok()) {
762 return status;
763 }
764 if (block != data) {
765 // Copy decrypted data back to `data`.
766 memmove(data, block + blockOffset, n);
767 }
768 dataSize -= n;
769 if (dataSize == 0) {
770 return Status::OK();
771 }
772 data += n;
773 blockOffset = 0;
774 blockIndex++;
775 }
776}
777
778// Encrypt a block of data.
779// Length of data is equal to BlockSize().
780Status ROT13BlockCipher::Encrypt(char *data) {
781 for (size_t i = 0; i < blockSize_; ++i) {
782 data[i] += 13;
783 }
784 return Status::OK();
785}
786
787// Decrypt a block of data.
788// Length of data is equal to BlockSize().
789Status ROT13BlockCipher::Decrypt(char *data) {
790 return Encrypt(data);
791}
792
793// Allocate scratch space which is passed to EncryptBlock/DecryptBlock.
794void CTRCipherStream::AllocateScratch(std::string& scratch) {
795 auto blockSize = cipher_.BlockSize();
796 scratch.reserve(blockSize);
797}
798
799// Encrypt a block of data at the given block index.
800// Length of data is equal to BlockSize();
801Status CTRCipherStream::EncryptBlock(uint64_t blockIndex, char *data, char* scratch) {
802
803 // Create nonce + counter
804 auto blockSize = cipher_.BlockSize();
805 memmove(scratch, iv_.data(), blockSize);
806 EncodeFixed64(scratch, blockIndex + initialCounter_);
807
808 // Encrypt nonce+counter
809 auto status = cipher_.Encrypt(scratch);
810 if (!status.ok()) {
811 return status;
812 }
813
814 // XOR data with ciphertext.
815 for (size_t i = 0; i < blockSize; i++) {
816 data[i] = data[i] ^ scratch[i];
817 }
818 return Status::OK();
819}
820
821// Decrypt a block of data at the given block index.
822// Length of data is equal to BlockSize();
823Status CTRCipherStream::DecryptBlock(uint64_t blockIndex, char *data, char* scratch) {
824 // For CTR decryption & encryption are the same
825 return EncryptBlock(blockIndex, data, scratch);
826}
827
828// GetPrefixLength returns the length of the prefix that is added to every file
829// and used for storing encryption options.
830// For optimal performance, the prefix length should be a multiple of
831// the page size.
832size_t CTREncryptionProvider::GetPrefixLength() {
833 return defaultPrefixLength;
834}
835
836// decodeCTRParameters decodes the initial counter & IV from the given
837// (plain text) prefix.
838static void decodeCTRParameters(const char *prefix, size_t blockSize, uint64_t &initialCounter, Slice &iv) {
839 // First block contains 64-bit initial counter
840 initialCounter = DecodeFixed64(prefix);
841 // Second block contains IV
842 iv = Slice(prefix + blockSize, blockSize);
843}
844
845// CreateNewPrefix initialized an allocated block of prefix memory
846// for a new file.
847Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/,
848 char* prefix,
849 size_t prefixLength) {
850 // Create & seed rnd.
851 Random rnd((uint32_t)Env::Default()->NowMicros());
852 // Fill entire prefix block with random values.
853 for (size_t i = 0; i < prefixLength; i++) {
854 prefix[i] = rnd.Uniform(256) & 0xFF;
855 }
856 // Take random data to extract initial counter & IV
857 auto blockSize = cipher_.BlockSize();
858 uint64_t initialCounter;
859 Slice prefixIV;
860 decodeCTRParameters(prefix, blockSize, initialCounter, prefixIV);
861
862 // Now populate the rest of the prefix, starting from the third block.
863 PopulateSecretPrefixPart(prefix + (2 * blockSize), prefixLength - (2 * blockSize), blockSize);
864
865 // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial counter & IV unencrypted)
866 CTRCipherStream cipherStream(cipher_, prefixIV.data(), initialCounter);
867 auto status = cipherStream.Encrypt(0, prefix + (2 * blockSize), prefixLength - (2 * blockSize));
868 if (!status.ok()) {
869 return status;
870 }
871 return Status::OK();
872}
873
874// PopulateSecretPrefixPart initializes the data into a new prefix block
875// in plain text.
876// Returns the amount of space (starting from the start of the prefix)
877// that has been initialized.
878size_t CTREncryptionProvider::PopulateSecretPrefixPart(char* /*prefix*/,
879 size_t /*prefixLength*/,
880 size_t /*blockSize*/) {
881 // Nothing to do here, put in custom data in override when needed.
882 return 0;
883}
884
885Status CTREncryptionProvider::CreateCipherStream(const std::string& fname, const EnvOptions& options, Slice &prefix, unique_ptr<BlockAccessCipherStream>* result) {
886 // Read plain text part of prefix.
887 auto blockSize = cipher_.BlockSize();
888 uint64_t initialCounter;
889 Slice iv;
890 decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv);
891
892 // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1 with initial counter & IV are unencrypted)
893 CTRCipherStream cipherStream(cipher_, iv.data(), initialCounter);
894 auto status = cipherStream.Decrypt(0, (char*)prefix.data() + (2 * blockSize), prefix.size() - (2 * blockSize));
895 if (!status.ok()) {
896 return status;
897 }
898
899 // Create cipher stream
900 return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv, prefix, result);
901}
902
903// CreateCipherStreamFromPrefix creates a block access cipher stream for a file given
904// given name and options. The given prefix is already decrypted.
905Status CTREncryptionProvider::CreateCipherStreamFromPrefix(
906 const std::string& /*fname*/, const EnvOptions& /*options*/,
907 uint64_t initialCounter, const Slice& iv, const Slice& /*prefix*/,
908 unique_ptr<BlockAccessCipherStream>* result) {
909 (*result) = unique_ptr<BlockAccessCipherStream>(new CTRCipherStream(cipher_, iv.data(), initialCounter));
910 return Status::OK();
911}
912
913#endif // ROCKSDB_LITE
914
915} // namespace rocksdb