]>
Commit | Line | Data |
---|---|---|
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 | ||
20effc67 TL |
8 | #include "rocksdb/env_encryption.h" |
9 | ||
11fdf7f2 | 10 | #include <algorithm> |
f67539c2 | 11 | #include <cassert> |
11fdf7f2 TL |
12 | #include <cctype> |
13 | #include <iostream> | |
14 | ||
20effc67 TL |
15 | #include "env/env_encryption_ctr.h" |
16 | #include "monitoring/perf_context_imp.h" | |
17 | #include "rocksdb/convenience.h" | |
11fdf7f2 TL |
18 | #include "util/aligned_buffer.h" |
19 | #include "util/coding.h" | |
20 | #include "util/random.h" | |
20effc67 | 21 | #include "util/string_util.h" |
11fdf7f2 TL |
22 | |
23 | #endif | |
24 | ||
f67539c2 | 25 | namespace ROCKSDB_NAMESPACE { |
11fdf7f2 TL |
26 | |
27 | #ifndef ROCKSDB_LITE | |
20effc67 TL |
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 | } | |
11fdf7f2 | 51 | |
20effc67 TL |
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://")); | |
11fdf7f2 | 60 | } |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
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 | |
20effc67 TL |
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()) { | |
11fdf7f2 TL |
91 | return status; |
92 | } | |
20effc67 TL |
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 | } | |
11fdf7f2 | 101 | |
20effc67 TL |
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()) { | |
11fdf7f2 TL |
112 | return status; |
113 | } | |
20effc67 TL |
114 | offset_ += n; |
115 | return status; | |
116 | } | |
11fdf7f2 | 117 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 123 | |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
133 | Status EncryptedSequentialFile::InvalidateCache(size_t offset, size_t length) { |
134 | return file_->InvalidateCache(offset + prefixLength_, length); | |
135 | } | |
11fdf7f2 TL |
136 | |
137 | // Positioned Read for direct I/O | |
138 | // If Direct I/O enabled, offset, n, and scratch should be properly aligned | |
20effc67 TL |
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()) { | |
11fdf7f2 TL |
145 | return status; |
146 | } | |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
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()) { | |
11fdf7f2 TL |
171 | return status; |
172 | } | |
20effc67 TL |
173 | { |
174 | PERF_TIMER_GUARD(decrypt_data_nanos); | |
175 | status = stream_->Decrypt(offset, (char*)result->data(), result->size()); | |
176 | } | |
177 | return status; | |
178 | } | |
11fdf7f2 TL |
179 | |
180 | // Readahead the file starting from offset by n bytes for caching. | |
20effc67 TL |
181 | Status EncryptedRandomAccessFile::Prefetch(uint64_t offset, size_t n) { |
182 | // return Status::OK(); | |
183 | return file_->Prefetch(offset + prefixLength_, n); | |
184 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
201 | size_t EncryptedRandomAccessFile::GetUniqueId(char* id, size_t max_size) const { |
202 | return file_->GetUniqueId(id, max_size); | |
203 | }; | |
11fdf7f2 | 204 | |
20effc67 TL |
205 | void EncryptedRandomAccessFile::Hint(AccessPattern pattern) { |
206 | file_->Hint(pattern); | |
207 | } | |
11fdf7f2 TL |
208 | |
209 | // Indicates the upper layers if the current RandomAccessFile implementation | |
210 | // uses direct IO. | |
20effc67 TL |
211 | bool EncryptedRandomAccessFile::use_direct_io() const { |
212 | return file_->use_direct_io(); | |
213 | } | |
11fdf7f2 TL |
214 | |
215 | // Use the returned alignment value to allocate | |
216 | // aligned buffer for Direct I/O | |
20effc67 TL |
217 | size_t EncryptedRandomAccessFile::GetRequiredBufferAlignment() const { |
218 | return file_->GetRequiredBufferAlignment(); | |
219 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
224 | Status EncryptedRandomAccessFile::InvalidateCache(size_t offset, |
225 | size_t length) { | |
226 | return file_->InvalidateCache(offset + prefixLength_, length); | |
227 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
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); | |
f67539c2 | 247 | status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()); |
11fdf7f2 | 248 | } |
11fdf7f2 TL |
249 | if (!status.ok()) { |
250 | return status; | |
251 | } | |
20effc67 TL |
252 | dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize()); |
253 | } | |
254 | status = file_->Append(dataToAppend); | |
255 | if (!status.ok()) { | |
11fdf7f2 TL |
256 | return status; |
257 | } | |
20effc67 TL |
258 | return status; |
259 | } | |
11fdf7f2 | 260 | |
20effc67 TL |
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); | |
f67539c2 | 275 | status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()); |
11fdf7f2 | 276 | } |
11fdf7f2 TL |
277 | if (!status.ok()) { |
278 | return status; | |
279 | } | |
20effc67 TL |
280 | dataToAppend = Slice(buf.BufferStart(), buf.CurrentSize()); |
281 | } | |
282 | status = file_->PositionedAppend(dataToAppend, offset); | |
283 | if (!status.ok()) { | |
11fdf7f2 TL |
284 | return status; |
285 | } | |
20effc67 TL |
286 | return status; |
287 | } | |
11fdf7f2 TL |
288 | |
289 | // Indicates the upper layers if the current WritableFile implementation | |
290 | // uses direct IO. | |
20effc67 TL |
291 | bool EncryptedWritableFile::use_direct_io() const { |
292 | return file_->use_direct_io(); | |
293 | } | |
11fdf7f2 TL |
294 | |
295 | // Use the returned alignment value to allocate | |
296 | // aligned buffer for Direct I/O | |
20effc67 TL |
297 | size_t EncryptedWritableFile::GetRequiredBufferAlignment() const { |
298 | return file_->GetRequiredBufferAlignment(); | |
299 | } | |
11fdf7f2 | 300 | |
20effc67 TL |
301 | /* |
302 | * Get the size of valid data in the file. | |
303 | */ | |
304 | uint64_t EncryptedWritableFile::GetFileSize() { | |
305 | return file_->GetFileSize() - prefixLength_; | |
306 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
312 | Status EncryptedWritableFile::Truncate(uint64_t size) { |
313 | return file_->Truncate(size + prefixLength_); | |
314 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
320 | Status EncryptedWritableFile::InvalidateCache(size_t offset, size_t length) { |
321 | return file_->InvalidateCache(offset + prefixLength_, length); | |
322 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
330 | Status EncryptedWritableFile::RangeSync(uint64_t offset, uint64_t nbytes) { |
331 | return file_->RangeSync(offset + prefixLength_, nbytes); | |
332 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
339 | void EncryptedWritableFile::PrepareWrite(size_t offset, size_t len) { |
340 | file_->PrepareWrite(offset + prefixLength_, len); | |
341 | } | |
11fdf7f2 TL |
342 | |
343 | // Pre-allocates space for a file. | |
20effc67 TL |
344 | Status EncryptedWritableFile::Allocate(uint64_t offset, uint64_t len) { |
345 | return file_->Allocate(offset + prefixLength_, len); | |
346 | } | |
11fdf7f2 TL |
347 | |
348 | // A file abstraction for random reading and writing. | |
11fdf7f2 | 349 | |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
355 | |
356 | // Use the returned alignment value to allocate | |
357 | // aligned buffer for Direct I/O | |
20effc67 TL |
358 | size_t EncryptedRandomRWFile::GetRequiredBufferAlignment() const { |
359 | return file_->GetRequiredBufferAlignment(); | |
360 | } | |
11fdf7f2 TL |
361 | |
362 | // Write bytes in `data` at offset `offset`, Returns Status::OK() on success. | |
363 | // Pass aligned buffer when use_direct_io() returns true. | |
20effc67 TL |
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); | |
f67539c2 | 377 | status = stream_->Encrypt(offset, buf.BufferStart(), buf.CurrentSize()); |
11fdf7f2 | 378 | } |
20effc67 TL |
379 | if (!status.ok()) { |
380 | return status; | |
381 | } | |
382 | dataToWrite = Slice(buf.BufferStart(), buf.CurrentSize()); | |
11fdf7f2 | 383 | } |
20effc67 TL |
384 | status = file_->Write(offset, dataToWrite); |
385 | return status; | |
386 | } | |
11fdf7f2 TL |
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. | |
20effc67 TL |
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); | |
11fdf7f2 TL |
462 | if (!status.ok()) { |
463 | return status; | |
20effc67 TL |
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); | |
11fdf7f2 | 487 | } |
11fdf7f2 TL |
488 | return status; |
489 | } | |
490 | ||
20effc67 TL |
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 | } | |
11fdf7f2 | 511 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 559 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 593 | |
20effc67 TL |
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 | } | |
11fdf7f2 | 627 | |
11fdf7f2 | 628 | public: |
20effc67 TL |
629 | EncryptedEnvImpl(Env* base_env, |
630 | const std::shared_ptr<EncryptionProvider>& provider) | |
11fdf7f2 TL |
631 | : EnvWrapper(base_env) { |
632 | provider_ = provider; | |
633 | } | |
634 | ||
635 | // NewSequentialFile opens a file for sequential reading. | |
20effc67 TL |
636 | virtual Status NewSequentialFile(const std::string& fname, |
637 | std::unique_ptr<SequentialFile>* result, | |
638 | const EnvOptions& options) override { | |
11fdf7f2 TL |
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 | } | |
11fdf7f2 TL |
649 | // Create cipher stream |
650 | std::unique_ptr<BlockAccessCipherStream> stream; | |
20effc67 TL |
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)); | |
11fdf7f2 | 657 | } |
20effc67 | 658 | return status; |
11fdf7f2 TL |
659 | } |
660 | ||
661 | // NewRandomAccessFile opens a file for random read access. | |
20effc67 TL |
662 | virtual Status NewRandomAccessFile(const std::string& fname, |
663 | std::unique_ptr<RandomAccessFile>* result, | |
664 | const EnvOptions& options) override { | |
11fdf7f2 TL |
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 | } | |
11fdf7f2 | 675 | std::unique_ptr<BlockAccessCipherStream> stream; |
20effc67 TL |
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 | } | |
11fdf7f2 | 686 | } |
20effc67 | 687 | return status; |
11fdf7f2 | 688 | } |
494da23a | 689 | |
11fdf7f2 | 690 | // NewWritableFile opens a file for sequential writing. |
20effc67 TL |
691 | virtual Status NewWritableFile(const std::string& fname, |
692 | std::unique_ptr<WritableFile>* result, | |
693 | const EnvOptions& options) override { | |
11fdf7f2 TL |
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 | } | |
20effc67 | 704 | return CreateWritableEncryptedFile(fname, underlying, options, result); |
11fdf7f2 TL |
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. | |
20effc67 TL |
714 | virtual Status ReopenWritableFile(const std::string& fname, |
715 | std::unique_ptr<WritableFile>* result, | |
716 | const EnvOptions& options) override { | |
11fdf7f2 TL |
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 | } | |
20effc67 | 727 | return CreateWritableEncryptedFile(fname, underlying, options, result); |
11fdf7f2 TL |
728 | } |
729 | ||
730 | // Reuse an existing file by renaming it and opening it as writable. | |
20effc67 TL |
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 { | |
11fdf7f2 TL |
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; | |
20effc67 TL |
741 | Status status = |
742 | EnvWrapper::ReuseWritableFile(fname, old_fname, &underlying, options); | |
11fdf7f2 TL |
743 | if (!status.ok()) { |
744 | return status; | |
745 | } | |
20effc67 | 746 | return CreateWritableEncryptedFile(fname, underlying, options, result); |
11fdf7f2 TL |
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. | |
20effc67 TL |
754 | virtual Status NewRandomRWFile(const std::string& fname, |
755 | std::unique_ptr<RandomRWFile>* result, | |
756 | const EnvOptions& options) override { | |
11fdf7f2 TL |
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 | } | |
11fdf7f2 TL |
770 | // Create cipher stream |
771 | std::unique_ptr<BlockAccessCipherStream> stream; | |
20effc67 TL |
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); | |
11fdf7f2 | 780 | } |
20effc67 TL |
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; | |
11fdf7f2 TL |
790 | } |
791 | ||
20effc67 TL |
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 | |
11fdf7f2 TL |
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. | |
20effc67 TL |
802 | // NotFound if "dir" does not exist, the calling process does not |
803 | // have | |
11fdf7f2 TL |
804 | // permission to access "dir", or if "dir" is invalid. |
805 | // IOError if an IO Error was encountered | |
20effc67 | 806 | virtual Status GetChildrenFileAttributes( |
494da23a | 807 | const std::string& dir, std::vector<FileAttributes>* result) override { |
11fdf7f2 TL |
808 | auto status = EnvWrapper::GetChildrenFileAttributes(dir, result); |
809 | if (!status.ok()) { | |
810 | return status; | |
811 | } | |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
825 | } |
826 | return Status::OK(); | |
494da23a | 827 | } |
11fdf7f2 TL |
828 | |
829 | // Store the size of fname in *file_size. | |
20effc67 TL |
830 | virtual Status GetFileSize(const std::string& fname, |
831 | uint64_t* file_size) override { | |
11fdf7f2 TL |
832 | auto status = EnvWrapper::GetFileSize(fname, file_size); |
833 | if (!status.ok()) { | |
834 | return status; | |
835 | } | |
20effc67 TL |
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; | |
11fdf7f2 TL |
844 | } |
845 | ||
846 | private: | |
20effc67 | 847 | std::shared_ptr<EncryptionProvider> provider_; |
11fdf7f2 TL |
848 | }; |
849 | ||
f67539c2 | 850 | // Returns an Env that encrypts data when stored on disk and decrypts data when |
11fdf7f2 | 851 | // read from disk. |
20effc67 TL |
852 | Env* NewEncryptedEnv(Env* base_env, |
853 | const std::shared_ptr<EncryptionProvider>& provider) { | |
854 | return new EncryptedEnvImpl(base_env, provider); | |
11fdf7f2 TL |
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; | |
494da23a | 864 | std::unique_ptr<char[]> blockBuffer; |
11fdf7f2 TL |
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) { | |
f67539c2 | 874 | // We're not encrypting a full block. |
11fdf7f2 TL |
875 | // Copy data to blockBuffer |
876 | if (!blockBuffer.get()) { | |
494da23a TL |
877 | // Allocate buffer |
878 | blockBuffer = std::unique_ptr<char[]>(new char[blockSize]); | |
11fdf7f2 TL |
879 | } |
880 | block = blockBuffer.get(); | |
f67539c2 | 881 | // Copy plain data to block buffer |
11fdf7f2 TL |
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; | |
494da23a | 909 | std::unique_ptr<char[]> blockBuffer; |
11fdf7f2 TL |
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) { | |
f67539c2 | 919 | // We're not decrypting a full block. |
11fdf7f2 TL |
920 | // Copy data to blockBuffer |
921 | if (!blockBuffer.get()) { | |
494da23a TL |
922 | // Allocate buffer |
923 | blockBuffer = std::unique_ptr<char[]>(new char[blockSize]); | |
11fdf7f2 TL |
924 | } |
925 | block = blockBuffer.get(); | |
f67539c2 | 926 | // Copy encrypted data to block buffer |
11fdf7f2 TL |
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 | } | |
494da23a TL |
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 | ||
11fdf7f2 TL |
945 | dataSize -= n; |
946 | if (dataSize == 0) { | |
947 | return Status::OK(); | |
948 | } | |
949 | data += n; | |
950 | blockOffset = 0; | |
951 | blockIndex++; | |
952 | } | |
953 | } | |
954 | ||
20effc67 TL |
955 | const char* ROT13BlockCipher::Name() const { return kROT13CipherName; } |
956 | ||
11fdf7f2 TL |
957 | // Encrypt a block of data. |
958 | // Length of data is equal to BlockSize(). | |
20effc67 | 959 | Status ROT13BlockCipher::Encrypt(char* data) { |
11fdf7f2 | 960 | for (size_t i = 0; i < blockSize_; ++i) { |
20effc67 | 961 | data[i] += 13; |
11fdf7f2 TL |
962 | } |
963 | return Status::OK(); | |
964 | } | |
965 | ||
966 | // Decrypt a block of data. | |
967 | // Length of data is equal to BlockSize(). | |
20effc67 | 968 | Status ROT13BlockCipher::Decrypt(char* data) { return Encrypt(data); } |
11fdf7f2 TL |
969 | |
970 | // Allocate scratch space which is passed to EncryptBlock/DecryptBlock. | |
971 | void CTRCipherStream::AllocateScratch(std::string& scratch) { | |
20effc67 | 972 | auto blockSize = cipher_->BlockSize(); |
11fdf7f2 TL |
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(); | |
20effc67 TL |
978 | Status CTRCipherStream::EncryptBlock(uint64_t blockIndex, char* data, |
979 | char* scratch) { | |
11fdf7f2 | 980 | // Create nonce + counter |
20effc67 | 981 | auto blockSize = cipher_->BlockSize(); |
11fdf7f2 TL |
982 | memmove(scratch, iv_.data(), blockSize); |
983 | EncodeFixed64(scratch, blockIndex + initialCounter_); | |
984 | ||
f67539c2 | 985 | // Encrypt nonce+counter |
20effc67 | 986 | auto status = cipher_->Encrypt(scratch); |
11fdf7f2 TL |
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(); | |
20effc67 TL |
1000 | Status CTRCipherStream::DecryptBlock(uint64_t blockIndex, char* data, |
1001 | char* scratch) { | |
f67539c2 | 1002 | // For CTR decryption & encryption are the same |
11fdf7f2 TL |
1003 | return EncryptBlock(blockIndex, data, scratch); |
1004 | } | |
1005 | ||
20effc67 TL |
1006 | const char* CTREncryptionProvider::Name() const { return kCTRProviderName; } |
1007 | ||
11fdf7f2 TL |
1008 | // GetPrefixLength returns the length of the prefix that is added to every file |
1009 | // and used for storing encryption options. | |
f67539c2 | 1010 | // For optimal performance, the prefix length should be a multiple of |
11fdf7f2 | 1011 | // the page size. |
20effc67 | 1012 | size_t CTREncryptionProvider::GetPrefixLength() const { |
11fdf7f2 TL |
1013 | return defaultPrefixLength; |
1014 | } | |
1015 | ||
20effc67 TL |
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 | ||
11fdf7f2 TL |
1038 | // decodeCTRParameters decodes the initial counter & IV from the given |
1039 | // (plain text) prefix. | |
20effc67 TL |
1040 | static void decodeCTRParameters(const char* prefix, size_t blockSize, |
1041 | uint64_t& initialCounter, Slice& iv) { | |
11fdf7f2 TL |
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 | ||
f67539c2 | 1048 | // CreateNewPrefix initialized an allocated block of prefix memory |
11fdf7f2 TL |
1049 | // for a new file. |
1050 | Status CTREncryptionProvider::CreateNewPrefix(const std::string& /*fname*/, | |
1051 | char* prefix, | |
20effc67 TL |
1052 | size_t prefixLength) const { |
1053 | if (!cipher_) { | |
1054 | return Status::InvalidArgument("Encryption Cipher is missing"); | |
1055 | } | |
11fdf7f2 TL |
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 | |
20effc67 | 1063 | auto blockSize = cipher_->BlockSize(); |
11fdf7f2 TL |
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. | |
20effc67 TL |
1069 | PopulateSecretPrefixPart(prefix + (2 * blockSize), |
1070 | prefixLength - (2 * blockSize), blockSize); | |
11fdf7f2 | 1071 | |
20effc67 TL |
1072 | // Encrypt the prefix, starting from block 2 (leave block 0, 1 with initial |
1073 | // counter & IV unencrypted) | |
11fdf7f2 | 1074 | CTRCipherStream cipherStream(cipher_, prefixIV.data(), initialCounter); |
20effc67 TL |
1075 | Status status; |
1076 | { | |
1077 | PERF_TIMER_GUARD(encrypt_data_nanos); | |
1078 | status = cipherStream.Encrypt(0, prefix + (2 * blockSize), | |
1079 | prefixLength - (2 * blockSize)); | |
1080 | } | |
11fdf7f2 TL |
1081 | if (!status.ok()) { |
1082 | return status; | |
1083 | } | |
1084 | return Status::OK(); | |
1085 | } | |
1086 | ||
f67539c2 | 1087 | // PopulateSecretPrefixPart initializes the data into a new prefix block |
11fdf7f2 TL |
1088 | // in plain text. |
1089 | // Returns the amount of space (starting from the start of the prefix) | |
1090 | // that has been initialized. | |
20effc67 TL |
1091 | size_t CTREncryptionProvider::PopulateSecretPrefixPart( |
1092 | char* /*prefix*/, size_t /*prefixLength*/, size_t /*blockSize*/) const { | |
11fdf7f2 TL |
1093 | // Nothing to do here, put in custom data in override when needed. |
1094 | return 0; | |
1095 | } | |
1096 | ||
494da23a TL |
1097 | Status CTREncryptionProvider::CreateCipherStream( |
1098 | const std::string& fname, const EnvOptions& options, Slice& prefix, | |
1099 | std::unique_ptr<BlockAccessCipherStream>* result) { | |
20effc67 TL |
1100 | if (!cipher_) { |
1101 | return Status::InvalidArgument("Encryption Cipher is missing"); | |
1102 | } | |
11fdf7f2 | 1103 | // Read plain text part of prefix. |
20effc67 | 1104 | auto blockSize = cipher_->BlockSize(); |
11fdf7f2 TL |
1105 | uint64_t initialCounter; |
1106 | Slice iv; | |
1107 | decodeCTRParameters(prefix.data(), blockSize, initialCounter, iv); | |
1108 | ||
494da23a TL |
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) { | |
f67539c2 TL |
1113 | return Status::Corruption("Unable to read from file " + fname + |
1114 | ": read attempt would read beyond file bounds"); | |
494da23a TL |
1115 | } |
1116 | ||
20effc67 TL |
1117 | // Decrypt the encrypted part of the prefix, starting from block 2 (block 0, 1 |
1118 | // with initial counter & IV are unencrypted) | |
11fdf7f2 | 1119 | CTRCipherStream cipherStream(cipher_, iv.data(), initialCounter); |
20effc67 TL |
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 | } | |
11fdf7f2 TL |
1126 | if (!status.ok()) { |
1127 | return status; | |
1128 | } | |
1129 | ||
f67539c2 | 1130 | // Create cipher stream |
20effc67 TL |
1131 | return CreateCipherStreamFromPrefix(fname, options, initialCounter, iv, |
1132 | prefix, result); | |
11fdf7f2 TL |
1133 | } |
1134 | ||
20effc67 TL |
1135 | // CreateCipherStreamFromPrefix creates a block access cipher stream for a file |
1136 | // given given name and options. The given prefix is already decrypted. | |
11fdf7f2 TL |
1137 | Status CTREncryptionProvider::CreateCipherStreamFromPrefix( |
1138 | const std::string& /*fname*/, const EnvOptions& /*options*/, | |
1139 | uint64_t initialCounter, const Slice& iv, const Slice& /*prefix*/, | |
494da23a TL |
1140 | std::unique_ptr<BlockAccessCipherStream>* result) { |
1141 | (*result) = std::unique_ptr<BlockAccessCipherStream>( | |
1142 | new CTRCipherStream(cipher_, iv.data(), initialCounter)); | |
11fdf7f2 TL |
1143 | return Status::OK(); |
1144 | } | |
1145 | ||
1146 | #endif // ROCKSDB_LITE | |
1147 | ||
f67539c2 | 1148 | } // namespace ROCKSDB_NAMESPACE |