Coverage Report

Created: 2026-03-31 07:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/env/env_encryption.cc
Line
Count
Source
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
#include "rocksdb/env_encryption.h"
7
8
#include <algorithm>
9
#include <cassert>
10
#include <cctype>
11
#include <iostream>
12
13
#include "env/composite_env_wrapper.h"
14
#include "env/env_encryption_ctr.h"
15
#include "monitoring/perf_context_imp.h"
16
#include "rocksdb/convenience.h"
17
#include "rocksdb/io_status.h"
18
#include "rocksdb/system_clock.h"
19
#include "rocksdb/utilities/customizable_util.h"
20
#include "rocksdb/utilities/options_type.h"
21
#include "util/aligned_buffer.h"
22
#include "util/coding.h"
23
#include "util/random.h"
24
#include "util/string_util.h"
25
26
namespace ROCKSDB_NAMESPACE {
27
std::shared_ptr<EncryptionProvider> EncryptionProvider::NewCTRProvider(
28
0
    const std::shared_ptr<BlockCipher>& cipher) {
29
0
  return std::make_shared<CTREncryptionProvider>(cipher);
30
0
}
31
32
IOStatus EncryptedSequentialFile::Read(size_t n, const IOOptions& options,
33
                                       Slice* result, char* scratch,
34
0
                                       IODebugContext* dbg) {
35
0
  assert(scratch);
36
0
  IOStatus io_s = file_->Read(n, options, result, scratch, dbg);
37
0
  if (!io_s.ok()) {
38
0
    return io_s;
39
0
  }
40
0
  {
41
0
    PERF_TIMER_GUARD(decrypt_data_nanos);
42
0
    io_s = status_to_io_status(
43
0
        stream_->Decrypt(offset_, (char*)result->data(), result->size()));
44
0
  }
45
0
  if (io_s.ok()) {
46
0
    offset_ += result->size();  // We've already read data from disk, so update
47
                                // offset_ even if decryption fails.
48
0
  }
49
0
  return io_s;
50
0
}
51
52
0
IOStatus EncryptedSequentialFile::Skip(uint64_t n) {
53
0
  auto status = file_->Skip(n);
54
0
  if (!status.ok()) {
55
0
    return status;
56
0
  }
57
0
  offset_ += n;
58
0
  return status;
59
0
}
60
61
0
bool EncryptedSequentialFile::use_direct_io() const {
62
0
  return file_->use_direct_io();
63
0
}
64
65
0
size_t EncryptedSequentialFile::GetRequiredBufferAlignment() const {
66
0
  return file_->GetRequiredBufferAlignment();
67
0
}
68
69
IOStatus EncryptedSequentialFile::InvalidateCache(size_t offset,
70
0
                                                  size_t length) {
71
0
  return file_->InvalidateCache(offset + prefixLength_, length);
72
0
}
73
74
IOStatus EncryptedSequentialFile::PositionedRead(uint64_t offset, size_t n,
75
                                                 const IOOptions& options,
76
                                                 Slice* result, char* scratch,
77
0
                                                 IODebugContext* dbg) {
78
0
  assert(scratch);
79
0
  offset += prefixLength_;  // Skip prefix
80
0
  auto io_s = file_->PositionedRead(offset, n, options, result, scratch, dbg);
81
0
  if (!io_s.ok()) {
82
0
    return io_s;
83
0
  }
84
0
  offset_ = offset + result->size();
85
0
  {
86
0
    PERF_TIMER_GUARD(decrypt_data_nanos);
87
0
    io_s = status_to_io_status(
88
0
        stream_->Decrypt(offset, (char*)result->data(), result->size()));
89
0
  }
90
0
  return io_s;
91
0
}
92
93
IOStatus EncryptedRandomAccessFile::Read(uint64_t offset, size_t n,
94
                                         const IOOptions& options,
95
                                         Slice* result, char* scratch,
96
0
                                         IODebugContext* dbg) const {
97
0
  assert(scratch);
98
0
  offset += prefixLength_;
99
0
  auto io_s = file_->Read(offset, n, options, result, scratch, dbg);
100
0
  if (!io_s.ok()) {
101
0
    return io_s;
102
0
  }
103
0
  {
104
0
    PERF_TIMER_GUARD(decrypt_data_nanos);
105
0
    io_s = status_to_io_status(
106
0
        stream_->Decrypt(offset, (char*)result->data(), result->size()));
107
0
  }
108
0
  return io_s;
109
0
}
110
111
IOStatus EncryptedRandomAccessFile::Prefetch(uint64_t offset, size_t n,
112
                                             const IOOptions& options,
113
0
                                             IODebugContext* dbg) {
114
0
  return file_->Prefetch(offset + prefixLength_, n, options, dbg);
115
0
}
116
117
0
size_t EncryptedRandomAccessFile::GetUniqueId(char* id, size_t max_size) const {
118
0
  return file_->GetUniqueId(id, max_size);
119
0
}
120
121
0
void EncryptedRandomAccessFile::Hint(AccessPattern pattern) {
122
0
  file_->Hint(pattern);
123
0
}
124
125
0
bool EncryptedRandomAccessFile::use_direct_io() const {
126
0
  return file_->use_direct_io();
127
0
}
128
129
0
size_t EncryptedRandomAccessFile::GetRequiredBufferAlignment() const {
130
0
  return file_->GetRequiredBufferAlignment();
131
0
}
132
133
IOStatus EncryptedRandomAccessFile::InvalidateCache(size_t offset,
134
0
                                                    size_t length) {
135
0
  return file_->InvalidateCache(offset + prefixLength_, length);
136
0
}
137
138
IOStatus EncryptedWritableFile::Append(const Slice& data,
139
                                       const IOOptions& options,
140
0
                                       IODebugContext* dbg) {
141
0
  AlignedBuffer buf;
142
0
  Slice dataToAppend(data);
143
0
  if (data.size() > 0) {
144
0
    auto offset = file_->GetFileSize(options, dbg);  // size including prefix
145
    // Encrypt in cloned buffer
146
0
    buf.Alignment(GetRequiredBufferAlignment());
147
0
    buf.AllocateNewBuffer(data.size());
148
    // TODO (sagar0): Modify AlignedBuffer.Append to allow doing a memmove
149
    // so that the next two lines can be replaced with buf.Append().
150
0
    memmove(buf.BufferStart(), data.data(), data.size());
151
0
    buf.Size(data.size());
152
0
    IOStatus io_s;
153
0
    {
154
0
      PERF_TIMER_GUARD(encrypt_data_nanos);
155
0
      io_s = status_to_io_status(
156
0
          stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
157
0
    }
158
0
    if (!io_s.ok()) {
159
0
      return io_s;
160
0
    }
161
0
    dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
162
0
  }
163
0
  return file_->Append(dataToAppend, options, dbg);
164
0
}
165
166
IOStatus EncryptedWritableFile::PositionedAppend(const Slice& data,
167
                                                 uint64_t offset,
168
                                                 const IOOptions& options,
169
0
                                                 IODebugContext* dbg) {
170
0
  AlignedBuffer buf;
171
0
  Slice dataToAppend(data);
172
0
  offset += prefixLength_;
173
0
  if (data.size() > 0) {
174
    // Encrypt in cloned buffer
175
0
    buf.Alignment(GetRequiredBufferAlignment());
176
0
    buf.AllocateNewBuffer(data.size());
177
0
    memmove(buf.BufferStart(), data.data(), data.size());
178
0
    buf.Size(data.size());
179
0
    IOStatus io_s;
180
0
    {
181
0
      PERF_TIMER_GUARD(encrypt_data_nanos);
182
0
      io_s = status_to_io_status(
183
0
          stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
184
0
    }
185
0
    if (!io_s.ok()) {
186
0
      return io_s;
187
0
    }
188
0
    dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize());
189
0
  }
190
0
  return file_->PositionedAppend(dataToAppend, offset, options, dbg);
191
0
}
192
193
0
bool EncryptedWritableFile::use_direct_io() const {
194
0
  return file_->use_direct_io();
195
0
}
196
197
0
bool EncryptedWritableFile::IsSyncThreadSafe() const {
198
0
  return file_->IsSyncThreadSafe();
199
0
}
200
201
0
size_t EncryptedWritableFile::GetRequiredBufferAlignment() const {
202
0
  return file_->GetRequiredBufferAlignment();
203
0
}
204
205
uint64_t EncryptedWritableFile::GetFileSize(const IOOptions& options,
206
0
                                            IODebugContext* dbg) {
207
0
  return file_->GetFileSize(options, dbg) - prefixLength_;
208
0
}
209
210
IOStatus EncryptedWritableFile::Truncate(uint64_t size,
211
                                         const IOOptions& options,
212
0
                                         IODebugContext* dbg) {
213
0
  return file_->Truncate(size + prefixLength_, options, dbg);
214
0
}
215
216
0
IOStatus EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) {
217
0
  return file_->InvalidateCache(offset + prefixLength_, length);
218
0
}
219
220
IOStatus EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes,
221
                                          const IOOptions& options,
222
0
                                          IODebugContext* dbg) {
223
0
  return file_->RangeSync(offset + prefixLength_, nbytes, options, dbg);
224
0
}
225
226
void EncryptedWritableFile::PrepareWrite(size_t offset, size_t len,
227
                                         const IOOptions& options,
228
0
                                         IODebugContext* dbg) {
229
0
  file_->PrepareWrite(offset + prefixLength_, len, options, dbg);
230
0
}
231
232
0
void EncryptedWritableFile::SetPreallocationBlockSize(size_t size) {
233
  // the size here doesn't need to include prefixLength_, as it's a
234
  // configuration will be use for `PrepareWrite()`.
235
0
  file_->SetPreallocationBlockSize(size);
236
0
}
237
238
void EncryptedWritableFile::GetPreallocationStatus(
239
0
    size_t* block_size, size_t* last_allocated_block) {
240
0
  file_->GetPreallocationStatus(block_size, last_allocated_block);
241
0
}
242
243
IOStatus EncryptedWritableFile::Allocate(uint64_t offset, uint64_t len,
244
                                         const IOOptions& options,
245
0
                                         IODebugContext* dbg) {
246
0
  return file_->Allocate(offset + prefixLength_, len, options, dbg);
247
0
}
248
249
IOStatus EncryptedWritableFile::Flush(const IOOptions& options,
250
0
                                      IODebugContext* dbg) {
251
0
  return file_->Flush(options, dbg);
252
0
}
253
254
IOStatus EncryptedWritableFile::Sync(const IOOptions& options,
255
0
                                     IODebugContext* dbg) {
256
0
  return file_->Sync(options, dbg);
257
0
}
258
259
IOStatus EncryptedWritableFile::Close(const IOOptions& options,
260
0
                                      IODebugContext* dbg) {
261
0
  return file_->Close(options, dbg);
262
0
}
263
264
0
bool EncryptedRandomRWFile::use_direct_io() const {
265
0
  return file_->use_direct_io();
266
0
}
267
268
0
size_t EncryptedRandomRWFile::GetRequiredBufferAlignment() const {
269
0
  return file_->GetRequiredBufferAlignment();
270
0
}
271
272
IOStatus EncryptedRandomRWFile::Write(uint64_t offset, const Slice& data,
273
                                      const IOOptions& options,
274
0
                                      IODebugContext* dbg) {
275
0
  AlignedBuffer buf;
276
0
  Slice dataToWrite(data);
277
0
  offset += prefixLength_;
278
0
  if (data.size() > 0) {
279
    // Encrypt in cloned buffer
280
0
    buf.Alignment(GetRequiredBufferAlignment());
281
0
    buf.AllocateNewBuffer(data.size());
282
0
    memmove(buf.BufferStart(), data.data(), data.size());
283
0
    buf.Size(data.size());
284
0
    IOStatus io_s;
285
0
    {
286
0
      PERF_TIMER_GUARD(encrypt_data_nanos);
287
0
      io_s = status_to_io_status(
288
0
          stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()));
289
0
    }
290
0
    if (!io_s.ok()) {
291
0
      return io_s;
292
0
    }
293
0
    dataToWrite = Slice(buf.BufferStart(), buf.CurrentSize());
294
0
  }
295
0
  return file_->Write(offset, dataToWrite, options, dbg);
296
0
}
297
298
IOStatus EncryptedRandomRWFile::Read(uint64_t offset, size_t n,
299
                                     const IOOptions& options, Slice* result,
300
0
                                     char* scratch, IODebugContext* dbg) const {
301
0
  assert(scratch);
302
0
  offset += prefixLength_;
303
0
  auto status = file_->Read(offset, n, options, result, scratch, dbg);
304
0
  if (!status.ok()) {
305
0
    return status;
306
0
  }
307
0
  {
308
0
    PERF_TIMER_GUARD(decrypt_data_nanos);
309
0
    status = status_to_io_status(
310
0
        stream_->Decrypt(offset, (char*)result->data(), result->size()));
311
0
  }
312
0
  return status;
313
0
}
314
315
IOStatus EncryptedRandomRWFile::Flush(const IOOptions& options,
316
0
                                      IODebugContext* dbg) {
317
0
  return file_->Flush(options, dbg);
318
0
}
319
320
IOStatus EncryptedRandomRWFile::Sync(const IOOptions& options,
321
0
                                     IODebugContext* dbg) {
322
0
  return file_->Sync(options, dbg);
323
0
}
324
325
IOStatus EncryptedRandomRWFile::Fsync(const IOOptions& options,
326
0
                                      IODebugContext* dbg) {
327
0
  return file_->Fsync(options, dbg);
328
0
}
329
330
IOStatus EncryptedRandomRWFile::Close(const IOOptions& options,
331
0
                                      IODebugContext* dbg) {
332
0
  return file_->Close(options, dbg);
333
0
}
334
335
namespace {
336
static std::unordered_map<std::string, OptionTypeInfo> encrypted_fs_type_info =
337
    {
338
        {"provider",
339
         OptionTypeInfo::AsCustomSharedPtr<EncryptionProvider>(
340
             0 /* No offset, whole struct*/, OptionVerificationType::kByName,
341
             OptionTypeFlags::kNone)},
342
};
343
// EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
344
// to files stored on disk.
345
class EncryptedFileSystemImpl : public EncryptedFileSystem {
346
 public:
347
0
  const char* Name() const override {
348
0
    return EncryptedFileSystem::kClassName();
349
0
  }
350
  // Returns the raw encryption provider that should be used to write the input
351
  // encrypted file.  If there is no such provider, NotFound is returned.
352
  IOStatus GetWritableProvider(const std::string& /*fname*/,
353
0
                               EncryptionProvider** result) {
354
0
    if (provider_) {
355
0
      *result = provider_.get();
356
0
      return IOStatus::OK();
357
0
    } else {
358
0
      *result = nullptr;
359
0
      return IOStatus::NotFound("No WriteProvider specified");
360
0
    }
361
0
  }
362
363
  // Returns the raw encryption provider that should be used to read the input
364
  // encrypted file.  If there is no such provider, NotFound is returned.
365
  IOStatus GetReadableProvider(const std::string& /*fname*/,
366
0
                               EncryptionProvider** result) {
367
0
    if (provider_) {
368
0
      *result = provider_.get();
369
0
      return IOStatus::OK();
370
0
    } else {
371
0
      *result = nullptr;
372
0
      return IOStatus::NotFound("No Provider specified");
373
0
    }
374
0
  }
375
376
  // Creates a CipherStream for the underlying file/name using the options
377
  // If a writable provider is found and encryption is enabled, uses
378
  // this provider to create a cipher stream.
379
  // @param fname         Name of the writable file
380
  // @param underlying    The underlying "raw" file
381
  // @param options       Options for creating the file/cipher
382
  // @param prefix_length Returns the length of the encryption prefix used for
383
  // this file
384
  // @param stream        Returns the cipher stream to use for this file if it
385
  // should be encrypted
386
  // @return OK on success, non-OK on failure.
387
  template <class TypeFile>
388
  IOStatus CreateWritableCipherStream(
389
      const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
390
      const FileOptions& options, size_t* prefix_length,
391
0
      std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
392
0
    EncryptionProvider* provider = nullptr;
393
0
    *prefix_length = 0;
394
0
    IOStatus status = GetWritableProvider(fname, &provider);
395
0
    if (!status.ok()) {
396
0
      return status;
397
0
    } else if (provider != nullptr) {
398
      // Initialize & write prefix (if needed)
399
0
      AlignedBuffer buffer;
400
0
      Slice prefix;
401
0
      *prefix_length = provider->GetPrefixLength();
402
0
      if (*prefix_length > 0) {
403
        // Initialize prefix
404
0
        buffer.Alignment(underlying->GetRequiredBufferAlignment());
405
0
        buffer.AllocateNewBuffer(*prefix_length);
406
0
        status = status_to_io_status(provider->CreateNewPrefix(
407
0
            fname, buffer.BufferStart(), *prefix_length));
408
0
        if (status.ok()) {
409
0
          buffer.Size(*prefix_length);
410
0
          prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
411
          // Write prefix
412
0
          status = underlying->Append(prefix, options.io_options, dbg);
413
0
        }
414
0
        if (!status.ok()) {
415
0
          return status;
416
0
        }
417
0
      }
418
      // Create cipher stream
419
0
      status = status_to_io_status(
420
0
          provider->CreateCipherStream(fname, options, prefix, stream));
421
0
    }
422
0
    return status;
423
0
  }
424
425
  template <class TypeFile>
426
  IOStatus CreateWritableEncryptedFile(const std::string& fname,
427
                                       std::unique_ptr<TypeFile>& underlying,
428
                                       const FileOptions& options,
429
                                       std::unique_ptr<TypeFile>* result,
430
0
                                       IODebugContext* dbg) {
431
    // Create cipher stream
432
0
    std::unique_ptr<BlockAccessCipherStream> stream;
433
0
    size_t prefix_length;
434
0
    IOStatus status = CreateWritableCipherStream(fname, underlying, options,
435
0
                                                 &prefix_length, &stream, dbg);
436
0
    if (status.ok()) {
437
0
      if (stream) {
438
0
        result->reset(new EncryptedWritableFile(
439
0
            std::move(underlying), std::move(stream), prefix_length));
440
0
      } else {
441
0
        result->reset(underlying.release());
442
0
      }
443
0
    }
444
0
    return status;
445
0
  }
446
447
  // Creates a CipherStream for the underlying file/name using the options
448
  // If a writable provider is found and encryption is enabled, uses
449
  // this provider to create a cipher stream.
450
  // @param fname         Name of the writable file
451
  // @param underlying    The underlying "raw" file
452
  // @param options       Options for creating the file/cipher
453
  // @param prefix_length Returns the length of the encryption prefix used for
454
  // this file
455
  // @param stream        Returns the cipher stream to use for this file if it
456
  // should be encrypted
457
  // @return OK on success, non-OK on failure.
458
  template <class TypeFile>
459
  IOStatus CreateRandomWriteCipherStream(
460
      const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
461
      const FileOptions& options, size_t* prefix_length,
462
0
      std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
463
0
    EncryptionProvider* provider = nullptr;
464
0
    *prefix_length = 0;
465
0
    IOStatus io_s = GetWritableProvider(fname, &provider);
466
0
    if (!io_s.ok()) {
467
0
      return io_s;
468
0
    } else if (provider != nullptr) {
469
      // Initialize & write prefix (if needed)
470
0
      AlignedBuffer buffer;
471
0
      Slice prefix;
472
0
      *prefix_length = provider->GetPrefixLength();
473
0
      if (*prefix_length > 0) {
474
        // Initialize prefix
475
0
        buffer.Alignment(underlying->GetRequiredBufferAlignment());
476
0
        buffer.AllocateNewBuffer(*prefix_length);
477
0
        io_s = status_to_io_status(provider->CreateNewPrefix(
478
0
            fname, buffer.BufferStart(), *prefix_length));
479
0
        if (io_s.ok()) {
480
0
          buffer.Size(*prefix_length);
481
0
          prefix = Slice(buffer.BufferStart(), buffer.CurrentSize());
482
          // Write prefix
483
0
          io_s = underlying->Write(0, prefix, options.io_options, dbg);
484
0
        }
485
0
        if (!io_s.ok()) {
486
0
          return io_s;
487
0
        }
488
0
      }
489
      // Create cipher stream
490
0
      io_s = status_to_io_status(
491
0
          provider->CreateCipherStream(fname, options, prefix, stream));
492
0
    }
493
0
    return io_s;
494
0
  }
495
496
  // Creates a CipherStream for the underlying file/name using the options
497
  // If a readable provider is found and the file is encrypted, uses
498
  // this provider to create a cipher stream.
499
  // @param fname         Name of the writable file
500
  // @param underlying    The underlying "raw" file
501
  // @param options       Options for creating the file/cipher
502
  // @param prefix_length Returns the length of the encryption prefix used for
503
  // this file
504
  // @param stream        Returns the cipher stream to use for this file if it
505
  // is encrypted
506
  // @return OK on success, non-OK on failure.
507
  template <class TypeFile>
508
  IOStatus CreateSequentialCipherStream(
509
      const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
510
      const FileOptions& options, size_t* prefix_length,
511
0
      std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
512
    // Read prefix (if needed)
513
0
    AlignedBuffer buffer;
514
0
    Slice prefix;
515
0
    *prefix_length = provider_->GetPrefixLength();
516
0
    if (*prefix_length > 0) {
517
      // Read prefix
518
0
      buffer.Alignment(underlying->GetRequiredBufferAlignment());
519
0
      buffer.AllocateNewBuffer(*prefix_length);
520
0
      IOStatus status = underlying->Read(*prefix_length, options.io_options,
521
0
                                         &prefix, buffer.BufferStart(), dbg);
522
0
      if (!status.ok()) {
523
0
        return status;
524
0
      }
525
0
      buffer.Size(*prefix_length);
526
0
    }
527
0
    return status_to_io_status(
528
0
        provider_->CreateCipherStream(fname, options, prefix, stream));
529
0
  }
530
531
  // Creates a CipherStream for the underlying file/name using the options
532
  // If a readable provider is found and the file is encrypted, uses
533
  // this provider to create a cipher stream.
534
  // @param fname         Name of the writable file
535
  // @param underlying    The underlying "raw" file
536
  // @param options       Options for creating the file/cipher
537
  // @param prefix_length Returns the length of the encryption prefix used for
538
  // this file
539
  // @param stream        Returns the cipher stream to use for this file if it
540
  // is encrypted
541
  // @return OK on success, non-OK on failure.
542
  template <class TypeFile>
543
  IOStatus CreateRandomReadCipherStream(
544
      const std::string& fname, const std::unique_ptr<TypeFile>& underlying,
545
      const FileOptions& options, size_t* prefix_length,
546
0
      std::unique_ptr<BlockAccessCipherStream>* stream, IODebugContext* dbg) {
547
    // Read prefix (if needed)
548
0
    AlignedBuffer buffer;
549
0
    Slice prefix;
550
0
    *prefix_length = provider_->GetPrefixLength();
551
0
    if (*prefix_length > 0) {
552
      // Read prefix
553
0
      buffer.Alignment(underlying->GetRequiredBufferAlignment());
554
0
      buffer.AllocateNewBuffer(*prefix_length);
555
0
      IOStatus status = underlying->Read(0, *prefix_length, options.io_options,
556
0
                                         &prefix, buffer.BufferStart(), dbg);
557
0
      if (!status.ok()) {
558
0
        return status;
559
0
      }
560
0
      buffer.Size(*prefix_length);
561
0
    }
562
0
    return status_to_io_status(
563
0
        provider_->CreateCipherStream(fname, options, prefix, stream));
564
0
  }
Unexecuted instantiation: env_encryption.cc:rocksdb::IOStatus rocksdb::(anonymous namespace)::EncryptedFileSystemImpl::CreateRandomReadCipherStream<rocksdb::FSRandomAccessFile>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::unique_ptr<rocksdb::FSRandomAccessFile, std::__1::default_delete<rocksdb::FSRandomAccessFile> > const&, rocksdb::FileOptions const&, unsigned long*, std::__1::unique_ptr<rocksdb::BlockAccessCipherStream, std::__1::default_delete<rocksdb::BlockAccessCipherStream> >*, rocksdb::IODebugContext*)
Unexecuted instantiation: env_encryption.cc:rocksdb::IOStatus rocksdb::(anonymous namespace)::EncryptedFileSystemImpl::CreateRandomReadCipherStream<rocksdb::FSRandomRWFile>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::unique_ptr<rocksdb::FSRandomRWFile, std::__1::default_delete<rocksdb::FSRandomRWFile> > const&, rocksdb::FileOptions const&, unsigned long*, std::__1::unique_ptr<rocksdb::BlockAccessCipherStream, std::__1::default_delete<rocksdb::BlockAccessCipherStream> >*, rocksdb::IODebugContext*)
565
566
 public:
567
  EncryptedFileSystemImpl(const std::shared_ptr<FileSystem>& base,
568
                          const std::shared_ptr<EncryptionProvider>& provider)
569
0
      : EncryptedFileSystem(base) {
570
0
    provider_ = provider;
571
0
    RegisterOptions("EncryptionProvider", &provider_, &encrypted_fs_type_info);
572
0
  }
573
574
  Status AddCipher(const std::string& descriptor, const char* cipher,
575
0
                   size_t len, bool for_write) override {
576
0
    return provider_->AddCipher(descriptor, cipher, len, for_write);
577
0
  }
578
579
  IOStatus NewSequentialFile(const std::string& fname,
580
                             const FileOptions& options,
581
                             std::unique_ptr<FSSequentialFile>* result,
582
0
                             IODebugContext* dbg) override {
583
0
    result->reset();
584
0
    if (options.use_mmap_reads) {
585
0
      return IOStatus::InvalidArgument();
586
0
    }
587
    // Open file using underlying Env implementation
588
0
    std::unique_ptr<FSSequentialFile> underlying;
589
0
    auto status =
590
0
        FileSystemWrapper::NewSequentialFile(fname, options, &underlying, dbg);
591
0
    if (!status.ok()) {
592
0
      return status;
593
0
    }
594
0
    uint64_t file_size;
595
0
    status = FileSystemWrapper::GetFileSize(fname, options.io_options,
596
0
                                            &file_size, dbg);
597
0
    if (!status.ok()) {
598
0
      return status;
599
0
    }
600
0
    if (!file_size) {
601
0
      *result = std::move(underlying);
602
0
      return status;
603
0
    }
604
    // Create cipher stream
605
0
    std::unique_ptr<BlockAccessCipherStream> stream;
606
0
    size_t prefix_length;
607
0
    status = CreateSequentialCipherStream(fname, underlying, options,
608
0
                                          &prefix_length, &stream, dbg);
609
0
    if (status.ok()) {
610
0
      result->reset(new EncryptedSequentialFile(
611
0
          std::move(underlying), std::move(stream), prefix_length));
612
0
    }
613
0
    return status;
614
0
  }
615
616
  IOStatus NewRandomAccessFile(const std::string& fname,
617
                               const FileOptions& options,
618
                               std::unique_ptr<FSRandomAccessFile>* result,
619
0
                               IODebugContext* dbg) override {
620
0
    result->reset();
621
0
    if (options.use_mmap_reads) {
622
0
      return IOStatus::InvalidArgument();
623
0
    }
624
    // Open file using underlying Env implementation
625
0
    std::unique_ptr<FSRandomAccessFile> underlying;
626
0
    auto status = FileSystemWrapper::NewRandomAccessFile(fname, options,
627
0
                                                         &underlying, dbg);
628
0
    if (!status.ok()) {
629
0
      return status;
630
0
    }
631
0
    std::unique_ptr<BlockAccessCipherStream> stream;
632
0
    size_t prefix_length;
633
0
    status = CreateRandomReadCipherStream(fname, underlying, options,
634
0
                                          &prefix_length, &stream, dbg);
635
0
    if (status.ok()) {
636
0
      if (stream) {
637
0
        result->reset(new EncryptedRandomAccessFile(
638
0
            std::move(underlying), std::move(stream), prefix_length));
639
0
      } else {
640
0
        result->reset(underlying.release());
641
0
      }
642
0
    }
643
0
    return status;
644
0
  }
645
646
  IOStatus NewWritableFile(const std::string& fname, const FileOptions& options,
647
                           std::unique_ptr<FSWritableFile>* result,
648
0
                           IODebugContext* dbg) override {
649
0
    result->reset();
650
0
    if (options.use_mmap_writes) {
651
0
      return IOStatus::InvalidArgument();
652
0
    }
653
    // Open file using underlying Env implementation
654
0
    std::unique_ptr<FSWritableFile> underlying;
655
0
    IOStatus status =
656
0
        FileSystemWrapper::NewWritableFile(fname, options, &underlying, dbg);
657
0
    if (!status.ok()) {
658
0
      return status;
659
0
    }
660
0
    return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
661
0
  }
662
663
  IOStatus ReopenWritableFile(const std::string& fname,
664
                              const FileOptions& options,
665
                              std::unique_ptr<FSWritableFile>* result,
666
0
                              IODebugContext* dbg) override {
667
0
    result->reset();
668
0
    if (options.use_mmap_reads || options.use_mmap_writes) {
669
0
      return IOStatus::InvalidArgument();
670
0
    }
671
672
0
    size_t prefix_length = 0;
673
0
    std::unique_ptr<BlockAccessCipherStream> stream;
674
675
    // Open file using underlying Env implementation
676
0
    std::unique_ptr<FSWritableFile> underlying;
677
0
    auto status =
678
0
        FileSystemWrapper::ReopenWritableFile(fname, options, &underlying, dbg);
679
0
    if (!status.ok()) {
680
0
      return status;
681
0
    }
682
683
0
    if (underlying->GetFileSize(options.io_options, dbg) != 0) {
684
      // read the cipher stream from file for non-empty file
685
0
      std::unique_ptr<FSRandomAccessFile> underlying_file_reader;
686
0
      status = FileSystemWrapper::NewRandomAccessFile(
687
0
          fname, options, &underlying_file_reader, dbg);
688
0
      if (!status.ok()) {
689
0
        return status;
690
0
      }
691
692
0
      status = CreateRandomReadCipherStream(
693
0
          fname, underlying_file_reader, options, &prefix_length, &stream, dbg);
694
695
0
      if (!status.ok()) {
696
0
        return status;
697
0
      }
698
0
    } else {
699
      // create cipher stream for new or empty file
700
0
      status = CreateWritableCipherStream(fname, underlying, options,
701
0
                                          &prefix_length, &stream, dbg);
702
0
      if (!status.ok()) {
703
0
        return status;
704
0
      }
705
0
    }
706
707
0
    if (stream) {
708
0
      result->reset(new EncryptedWritableFile(
709
0
          std::move(underlying), std::move(stream), prefix_length));
710
0
    } else {
711
0
      result->reset(underlying.release());
712
0
    }
713
0
    return status;
714
0
  }
715
716
  IOStatus ReuseWritableFile(const std::string& fname,
717
                             const std::string& old_fname,
718
                             const FileOptions& options,
719
                             std::unique_ptr<FSWritableFile>* result,
720
0
                             IODebugContext* dbg) override {
721
0
    result->reset();
722
0
    if (options.use_mmap_writes) {
723
0
      return IOStatus::InvalidArgument();
724
0
    }
725
    // Open file using underlying Env implementation
726
0
    std::unique_ptr<FSWritableFile> underlying;
727
0
    auto status = FileSystemWrapper::ReuseWritableFile(
728
0
        fname, old_fname, options, &underlying, dbg);
729
0
    if (!status.ok()) {
730
0
      return status;
731
0
    }
732
0
    return CreateWritableEncryptedFile(fname, underlying, options, result, dbg);
733
0
  }
734
735
  IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options,
736
                           std::unique_ptr<FSRandomRWFile>* result,
737
0
                           IODebugContext* dbg) override {
738
0
    result->reset();
739
0
    if (options.use_mmap_reads || options.use_mmap_writes) {
740
0
      return IOStatus::InvalidArgument();
741
0
    }
742
    // Check file exists
743
0
    bool isNewFile = !FileExists(fname, options.io_options, dbg).ok();
744
745
    // Open file using underlying Env implementation
746
0
    std::unique_ptr<FSRandomRWFile> underlying;
747
0
    auto status =
748
0
        FileSystemWrapper::NewRandomRWFile(fname, options, &underlying, dbg);
749
0
    if (!status.ok()) {
750
0
      return status;
751
0
    }
752
    // Create cipher stream
753
0
    std::unique_ptr<BlockAccessCipherStream> stream;
754
0
    size_t prefix_length = 0;
755
0
    if (!isNewFile) {
756
      // File already exists, read prefix
757
0
      status = CreateRandomReadCipherStream(fname, underlying, options,
758
0
                                            &prefix_length, &stream, dbg);
759
0
    } else {
760
0
      status = CreateRandomWriteCipherStream(fname, underlying, options,
761
0
                                             &prefix_length, &stream, dbg);
762
0
    }
763
0
    if (status.ok()) {
764
0
      if (stream) {
765
0
        result->reset(new EncryptedRandomRWFile(
766
0
            std::move(underlying), std::move(stream), prefix_length));
767
0
      } else {
768
0
        result->reset(underlying.release());
769
0
      }
770
0
    }
771
0
    return status;
772
0
  }
773
774
  IOStatus GetChildrenFileAttributes(const std::string& dir,
775
                                     const IOOptions& options,
776
                                     std::vector<FileAttributes>* result,
777
0
                                     IODebugContext* dbg) override {
778
0
    auto status =
779
0
        FileSystemWrapper::GetChildrenFileAttributes(dir, options, result, dbg);
780
0
    if (!status.ok()) {
781
0
      return status;
782
0
    }
783
0
    for (auto it = std::begin(*result); it != std::end(*result); ++it) {
784
      // assert(it->size_bytes >= prefixLength);
785
      //  breaks env_basic_test when called on directory containing
786
      //  directories
787
      // which makes subtraction of prefixLength worrisome since
788
      // FileAttributes does not identify directories
789
0
      EncryptionProvider* provider;
790
0
      status = GetReadableProvider(it->name, &provider);
791
0
      if (!status.ok()) {
792
0
        return status;
793
0
      } else if (provider != nullptr) {
794
0
        it->size_bytes -= provider->GetPrefixLength();
795
0
      }
796
0
    }
797
0
    return IOStatus::OK();
798
0
  }
799
800
  IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
801
0
                       uint64_t* file_size, IODebugContext* dbg) override {
802
0
    auto status =
803
0
        FileSystemWrapper::GetFileSize(fname, options, file_size, dbg);
804
0
    if (!status.ok() || !(*file_size)) {
805
0
      return status;
806
0
    }
807
0
    EncryptionProvider* provider;
808
0
    status = GetReadableProvider(fname, &provider);
809
0
    if (provider != nullptr && status.ok()) {
810
0
      size_t prefixLength = provider->GetPrefixLength();
811
0
      assert(*file_size >= prefixLength);
812
0
      *file_size -= prefixLength;
813
0
    }
814
0
    return status;
815
0
  }
816
817
 private:
818
  std::shared_ptr<EncryptionProvider> provider_;
819
};
820
}  // namespace
821
822
Status NewEncryptedFileSystemImpl(
823
    const std::shared_ptr<FileSystem>& base,
824
    const std::shared_ptr<EncryptionProvider>& provider,
825
0
    std::unique_ptr<FileSystem>* result) {
826
0
  result->reset(new EncryptedFileSystemImpl(base, provider));
827
0
  return Status::OK();
828
0
}
829
830
std::shared_ptr<FileSystem> NewEncryptedFS(
831
    const std::shared_ptr<FileSystem>& base,
832
0
    const std::shared_ptr<EncryptionProvider>& provider) {
833
0
  std::unique_ptr<FileSystem> efs;
834
0
  Status s = NewEncryptedFileSystemImpl(base, provider, &efs);
835
0
  if (s.ok()) {
836
0
    s = efs->PrepareOptions(ConfigOptions());
837
0
  }
838
0
  if (s.ok()) {
839
0
    std::shared_ptr<FileSystem> result(efs.release());
840
0
    return result;
841
0
  } else {
842
0
    return nullptr;
843
0
  }
844
0
}
845
846
Env* NewEncryptedEnv(Env* base_env,
847
0
                     const std::shared_ptr<EncryptionProvider>& provider) {
848
0
  return new CompositeEnvWrapper(
849
0
      base_env, NewEncryptedFS(base_env->GetFileSystem(), provider));
850
0
}
851
852
Status BlockAccessCipherStream::Encrypt(uint64_t fileOffset, char* data,
853
0
                                        size_t dataSize) {
854
  // Calculate block index
855
0
  auto blockSize = BlockSize();
856
0
  uint64_t blockIndex = fileOffset / blockSize;
857
0
  size_t blockOffset = fileOffset % blockSize;
858
0
  std::unique_ptr<char[]> blockBuffer;
859
860
0
  std::string scratch;
861
0
  AllocateScratch(scratch);
862
863
  // Encrypt individual blocks.
864
0
  while (true) {
865
0
    char* block = data;
866
0
    size_t n = std::min(dataSize, blockSize - blockOffset);
867
0
    if (n != blockSize) {
868
      // We're not encrypting a full block.
869
      // Copy data to blockBuffer
870
0
      if (!blockBuffer) {
871
        // Allocate buffer
872
0
        blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
873
0
      }
874
0
      block = blockBuffer.get();
875
      // Copy plain data to block buffer
876
0
      memmove(block + blockOffset, data, n);
877
0
    }
878
0
    auto status = EncryptBlock(blockIndex, block, (char*)scratch.data());
879
0
    if (!status.ok()) {
880
0
      return status;
881
0
    }
882
0
    if (block != data) {
883
      // Copy encrypted data back to `data`.
884
0
      memmove(data, block + blockOffset, n);
885
0
    }
886
0
    dataSize -= n;
887
0
    if (dataSize == 0) {
888
0
      return Status::OK();
889
0
    }
890
0
    data += n;
891
0
    blockOffset = 0;
892
0
    blockIndex++;
893
0
  }
894
0
}
895
896
Status BlockAccessCipherStream::Decrypt(uint64_t fileOffset, char* data,
897
0
                                        size_t dataSize) {
898
  // Calculate block index
899
0
  auto blockSize = BlockSize();
900
0
  uint64_t blockIndex = fileOffset / blockSize;
901
0
  size_t blockOffset = fileOffset % blockSize;
902
0
  std::unique_ptr<char[]> blockBuffer;
903
904
0
  std::string scratch;
905
0
  AllocateScratch(scratch);
906
907
  // Decrypt individual blocks.
908
0
  while (true) {
909
0
    char* block = data;
910
0
    size_t n = std::min(dataSize, blockSize - blockOffset);
911
0
    if (n != blockSize) {
912
      // We're not decrypting a full block.
913
      // Copy data to blockBuffer
914
0
      if (!blockBuffer) {
915
        // Allocate buffer
916
0
        blockBuffer = std::unique_ptr<char[]>(new char[blockSize]);
917
0
      }
918
0
      block = blockBuffer.get();
919
      // Copy encrypted data to block buffer
920
0
      memmove(block + blockOffset, data, n);
921
0
    }
922
0
    auto status = DecryptBlock(blockIndex, block, (char*)scratch.data());
923
0
    if (!status.ok()) {
924
0
      return status;
925
0
    }
926
0
    if (block != data) {
927
      // Copy decrypted data back to `data`.
928
0
      memmove(data, block + blockOffset, n);
929
0
    }
930
931
    // Simply decrementing dataSize by n could cause it to underflow,
932
    // which will very likely make it read over the original bounds later
933
0
    assert(dataSize >= n);
934
0
    if (dataSize < n) {
935
0
      return Status::Corruption("Cannot decrypt data at given offset");
936
0
    }
937
938
0
    dataSize -= n;
939
0
    if (dataSize == 0) {
940
0
      return Status::OK();
941
0
    }
942
0
    data += n;
943
0
    blockOffset = 0;
944
0
    blockIndex++;
945
0
  }
946
0
}
947
948
namespace {
949
static std::unordered_map<std::string, OptionTypeInfo>
950
    rot13_block_cipher_type_info = {
951
        {"block_size",
952
         {0 /* No offset, whole struct*/, OptionType::kInt,
953
          OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
954
};
955
956
// Implements a BlockCipher using ROT13.
957
//
958
// Note: This is a sample implementation of BlockCipher,
959
// it is NOT considered safe and should NOT be used in production.
960
class ROT13BlockCipher : public BlockCipher {
961
 private:
962
  size_t blockSize_;
963
964
 public:
965
0
  explicit ROT13BlockCipher(size_t blockSize) : blockSize_(blockSize) {
966
0
    RegisterOptions("ROT13BlockCipherOptions", &blockSize_,
967
0
                    &rot13_block_cipher_type_info);
968
0
  }
969
970
0
  static const char* kClassName() { return "ROT13"; }
971
0
  const char* Name() const override { return kClassName(); }
972
973
0
  size_t BlockSize() override { return blockSize_; }
974
0
  Status Encrypt(char* data) override {
975
0
    for (size_t i = 0; i < blockSize_; ++i) {
976
0
      data[i] += 13;
977
0
    }
978
0
    return Status::OK();
979
0
  }
980
0
  Status Decrypt(char* data) override { return Encrypt(data); }
981
};
982
983
static const std::unordered_map<std::string, OptionTypeInfo>
984
    ctr_encryption_provider_type_info = {
985
        {"cipher",
986
         OptionTypeInfo::AsCustomSharedPtr<BlockCipher>(
987
             0 /* No offset, whole struct*/, OptionVerificationType::kByName,
988
             OptionTypeFlags::kNone)},
989
};
990
}  // anonymous namespace
991
992
0
void CTRCipherStream::AllocateScratch(std::string& scratch) {
993
0
  auto blockSize = cipher_->BlockSize();
994
0
  scratch.reserve(blockSize);
995
0
}
996
997
Status CTRCipherStream::EncryptBlock(uint64_t blockIndex, char* data,
998
0
                                     char* scratch) {
999
  // Create nonce + counter
1000
0
  auto blockSize = cipher_->BlockSize();
1001
0
  memmove(scratch, iv_.data(), blockSize);
1002
0
  EncodeFixed64(scratch, blockIndex + initialCounter_);
1003
1004
  // Encrypt nonce + counter
1005
0
  auto status = cipher_->Encrypt(scratch);
1006
0
  if (!status.ok()) {
1007
0
    return status;
1008
0
  }
1009
1010
  // XOR data with ciphertext.
1011
0
  for (size_t i = 0; i < blockSize; i++) {
1012
0
    data[i] = data[i] ^ scratch[i];
1013
0
  }
1014
0
  return Status::OK();
1015
0
}
1016
1017
Status CTRCipherStream::DecryptBlock(uint64_t blockIndex, char* data,
1018
0
                                     char* scratch) {
1019
  // For CTR decryption & encryption are the same
1020
0
  return EncryptBlock(blockIndex, data, scratch);
1021
0
}
1022
1023
CTREncryptionProvider::CTREncryptionProvider(
1024
    const std::shared_ptr<BlockCipher>& c)
1025
0
    : cipher_(c) {
1026
0
  RegisterOptions("Cipher", &cipher_, &ctr_encryption_provider_type_info);
1027
0
}
1028
1029
0
bool CTREncryptionProvider::IsInstanceOf(const std::string& name) const {
1030
  // Special case for test purposes.
1031
0
  if (name == "1://test" && cipher_ != nullptr) {
1032
0
    return cipher_->IsInstanceOf(ROT13BlockCipher::kClassName());
1033
0
  } else {
1034
0
    return EncryptionProvider::IsInstanceOf(name);
1035
0
  }
1036
0
}
1037
1038
0
size_t CTREncryptionProvider::GetPrefixLength() const {
1039
0
  return defaultPrefixLength;
1040
0
}
1041
1042
Status CTREncryptionProvider::AddCipher(const std::string& /*descriptor*/,
1043
                                        const char* cipher, size_t len,
1044
0
                                        bool /*for_write*/) {
1045
0
  if (cipher_) {
1046
0
    return Status::NotSupported("Cannot add keys to CTREncryptionProvider");
1047
0
  } else if (strcmp(ROT13BlockCipher::kClassName(), cipher) == 0) {
1048
0
    cipher_.reset(new ROT13BlockCipher(len));
1049
0
    return Status::OK();
1050
0
  } else {
1051
0
    return BlockCipher::CreateFromString(ConfigOptions(), std::string(cipher),
1052
0
                                         &cipher_);
1053
0
  }
1054
0
}
1055
1056
// decodeCTRParameters decodes the initial counter & IV from the given
1057
// (plain text) prefix.
1058
static void decodeCTRParameters(const char* prefix, size_t blockSize,
1059
0
                                uint64_t& initialCounter, Slice& iv) {
1060
  // First block contains 64-bit initial counter
1061
0
  initialCounter = DecodeFixed64(prefix);
1062
  // Second block contains IV
1063
0
  iv = Slice(prefix + blockSize, blockSize);
1064
0
}
1065
1066
Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/,
1067
                                              char* prefix,
1068
0
                                              size_t prefixLength) const {
1069
0
  if (!cipher_) {
1070
0
    return Status::InvalidArgument("Encryption Cipher is missing");
1071
0
  }
1072
  // Create & seed rnd.
1073
0
  Random rnd((uint32_t)SystemClock::Default()->NowMicros());
1074
  // Fill entire prefix block with random values.
1075
0
  for (size_t i = 0; i < prefixLength; i++) {
1076
0
    prefix[i] = rnd.Uniform(256) & 0xFF;
1077
0
  }
1078
  // Take random data to extract initial counter & IV
1079
0
  auto blockSize = cipher_->BlockSize();
1080
0
  uint64_t initialCounter;
1081
0
  Slice prefixIV;
1082
0
  decodeCTRParameters(prefix, blockSize, initialCounter, prefixIV);
1083
1084
  // Now populate the rest of the prefix, starting from the third block.
1085
0
  PopulateSecretPrefixPart(prefix + (2 * blockSize),
1086
0
                           prefixLength - (2 * blockSize), blockSize);
1087
1088
  // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial
1089
  // counter & IV unencrypted)
1090
0
  CTRCipherStream cipherStream(cipher_, prefixIV.data(), initialCounter);
1091
0
  Status status;
1092
0
  {
1093
0
    PERF_TIMER_GUARD(encrypt_data_nanos);
1094
0
    status = cipherStream.Encrypt(0, prefix + (2 * blockSize),
1095
0
                                  prefixLength - (2 * blockSize));
1096
0
  }
1097
1098
0
  return status;
1099
0
}
1100
1101
// PopulateSecretPrefixPart initializes the data into a new prefix block
1102
// in plain text.
1103
// Returns the amount of space (starting from the start of the prefix)
1104
// that has been initialized.
1105
size_t CTREncryptionProvider::PopulateSecretPrefixPart(
1106
0
    char* /*prefix*/, size_t /*prefixLength*/, size_t /*blockSize*/) const {
1107
  // Nothing to do here, put in custom data in override when needed.
1108
0
  return 0;
1109
0
}
1110
1111
Status CTREncryptionProvider::CreateCipherStream(
1112
    const std::string& fname, const EnvOptions& options, Slice& prefix,
1113
0
    std::unique_ptr<BlockAccessCipherStream>* result) {
1114
0
  if (!cipher_) {
1115
0
    return Status::InvalidArgument("Encryption Cipher is missing");
1116
0
  }
1117
  // Read plain text part of prefix.
1118
0
  auto blockSize = cipher_->BlockSize();
1119
0
  uint64_t initialCounter;
1120
0
  Slice iv;
1121
0
  decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv);
1122
1123
  // If the prefix is smaller than twice the block size, we would below read a
1124
  // very large chunk of the file (and very likely read over the bounds)
1125
0
  assert(prefix.size() >= 2 * blockSize);
1126
0
  if (prefix.size() < 2 * blockSize) {
1127
0
    return Status::Corruption("Unable to read from file " + fname +
1128
0
                              ": read attempt would read beyond file bounds");
1129
0
  }
1130
1131
  // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1
1132
  // with initial counter & IV are unencrypted)
1133
0
  CTRCipherStream cipherStream(cipher_, iv.data(), initialCounter);
1134
0
  Status status;
1135
0
  {
1136
0
    PERF_TIMER_GUARD(decrypt_data_nanos);
1137
0
    status = cipherStream.Decrypt(0, (char*)prefix.data() + (2 * blockSize),
1138
0
                                  prefix.size() - (2 * blockSize));
1139
0
  }
1140
0
  if (!status.ok()) {
1141
0
    return status;
1142
0
  }
1143
1144
  // Create cipher stream
1145
0
  return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv,
1146
0
                                      prefix, result);
1147
0
}
1148
1149
// CreateCipherStreamFromPrefix creates a block access cipher stream for a file
1150
// given name and options. The given prefix is already decrypted.
1151
Status CTREncryptionProvider::CreateCipherStreamFromPrefix(
1152
    const std::string& /*fname*/, const EnvOptions& /*options*/,
1153
    uint64_t initialCounter, const Slice& iv, const Slice& /*prefix*/,
1154
0
    std::unique_ptr<BlockAccessCipherStream>* result) {
1155
0
  (*result) = std::unique_ptr<BlockAccessCipherStream>(
1156
0
      new CTRCipherStream(cipher_, iv.data(), initialCounter));
1157
0
  return Status::OK();
1158
0
}
1159
1160
namespace {
1161
0
static void RegisterEncryptionBuiltins() {
1162
0
  static std::once_flag once;
1163
0
  std::call_once(once, [&]() {
1164
0
    auto lib = ObjectRegistry::Default()->AddLibrary("encryption");
1165
    // Match "CTR" or "CTR://test"
1166
0
    lib->AddFactory<EncryptionProvider>(
1167
0
        ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true)
1168
0
            .AddSuffix("://test"),
1169
0
        [](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard,
1170
0
           std::string* /*errmsg*/) {
1171
0
          if (EndsWith(uri, "://test")) {
1172
0
            std::shared_ptr<BlockCipher> cipher =
1173
0
                std::make_shared<ROT13BlockCipher>(32);
1174
0
            guard->reset(new CTREncryptionProvider(cipher));
1175
0
          } else {
1176
0
            guard->reset(new CTREncryptionProvider());
1177
0
          }
1178
0
          return guard->get();
1179
0
        });
1180
1181
0
    lib->AddFactory<EncryptionProvider>(
1182
0
        "1://test", [](const std::string& /*uri*/,
1183
0
                       std::unique_ptr<EncryptionProvider>* guard,
1184
0
                       std::string* /*errmsg*/) {
1185
0
          std::shared_ptr<BlockCipher> cipher =
1186
0
              std::make_shared<ROT13BlockCipher>(32);
1187
0
          guard->reset(new CTREncryptionProvider(cipher));
1188
0
          return guard->get();
1189
0
        });
1190
1191
    // Match "ROT13" or "ROT13:[0-9]+"
1192
0
    lib->AddFactory<BlockCipher>(
1193
0
        ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true)
1194
0
            .AddNumber(":"),
1195
0
        [](const std::string& uri, std::unique_ptr<BlockCipher>* guard,
1196
0
           std::string* /* errmsg */) {
1197
0
          size_t colon = uri.find(':');
1198
0
          if (colon != std::string::npos) {
1199
0
            size_t block_size = ParseSizeT(uri.substr(colon + 1));
1200
0
            guard->reset(new ROT13BlockCipher(block_size));
1201
0
          } else {
1202
0
            guard->reset(new ROT13BlockCipher(32));
1203
0
          }
1204
1205
0
          return guard->get();
1206
0
        });
1207
0
  });
1208
0
}
1209
}  // namespace
1210
1211
Status BlockCipher::CreateFromString(const ConfigOptions& config_options,
1212
                                     const std::string& value,
1213
0
                                     std::shared_ptr<BlockCipher>* result) {
1214
0
  RegisterEncryptionBuiltins();
1215
0
  return LoadSharedObject<BlockCipher>(config_options, value, result);
1216
0
}
1217
1218
Status EncryptionProvider::CreateFromString(
1219
    const ConfigOptions& config_options, const std::string& value,
1220
0
    std::shared_ptr<EncryptionProvider>* result) {
1221
0
  RegisterEncryptionBuiltins();
1222
0
  return LoadSharedObject<EncryptionProvider>(config_options, value, result);
1223
0
}
1224
1225
}  // namespace ROCKSDB_NAMESPACE