-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathippcp_provider.cc
226 lines (188 loc) · 7.94 KB
/
ippcp_provider.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// Copyright (c) 2020 Intel Corporation
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#ifndef ROCKSDB_LITE
#include "ippcp_provider.h"
#include <emmintrin.h>
#include <ippcp.h>
#include <rocksdb/utilities/object_registry.h>
#include <memory>
#endif
namespace ROCKSDB_NAMESPACE {
#ifndef ROCKSDB_LITE
extern "C" FactoryFunc<EncryptionProvider> ippcp_reg;
FactoryFunc<EncryptionProvider> ippcp_reg =
ObjectLibrary::Default()->AddFactory<EncryptionProvider>(
IppcpProvider::kName(),
[](const std::string& /* uri */, std::unique_ptr<EncryptionProvider>* f,
std::string* /* errmsg */) {
*f = IppcpProvider::CreateProvider();
return f->get();
});
// IppcpCipherStream implements BlockAccessCipherStream using AES block
// cipher and a CTR mode of operation.
//
// Since ipp-crypto can handle block sizes larger than kBlockSize (16 bytes for
// AES) by chopping them internally into KBlockSize bytes, there is no need to
// support the EncryptBlock and DecryptBlock member functions (and they will
// never be called).
//
// See https://github.com/intel/ipp-crypto#documentation
class IppcpCipherStream : public BlockAccessCipherStream {
public:
static constexpr size_t kBlockSize = 16; // in bytes
static constexpr size_t kCounterLen = 64; // in bits
IppcpCipherStream(IppsAESSpec* aes_ctx, const char* init_vector);
virtual Status Encrypt(uint64_t fileOffset, char* data,
size_t dataSize) override;
virtual Status Decrypt(uint64_t fileOffset, char* data,
size_t dataSize) override;
virtual size_t BlockSize() override { return kBlockSize; }
protected:
// These functions are not needed and will never be called!
virtual void AllocateScratch(std::string&) override {}
virtual Status EncryptBlock(uint64_t, char*, char*) override {
return Status::NotSupported("Operation not supported.");
}
virtual Status DecryptBlock(uint64_t, char*, char*) override {
return Status::NotSupported("Operation not supported.");
}
private:
IppsAESSpec* aes_ctx_;
__m128i init_vector_;
};
IppcpCipherStream::IppcpCipherStream(IppsAESSpec* aes_ctx,
const char* init_vector)
: aes_ctx_(aes_ctx) {
init_vector_ = _mm_loadu_si128((__m128i*)init_vector);
}
Status IppcpCipherStream::Encrypt(uint64_t fileOffset, char* data,
size_t dataSize) {
if (dataSize == 0) return Status::OK();
size_t index = fileOffset / kBlockSize;
size_t offset = fileOffset % kBlockSize;
Ipp8u ctr_block[kBlockSize];
// evaluate the counter block from the block index
__m128i counter = _mm_add_epi64(init_vector_, _mm_cvtsi64_si128(index));
Ipp8u* ptr_counter = (Ipp8u*)&counter;
for (size_t i = 0; i < kBlockSize; ++i)
ctr_block[i] = ptr_counter[kBlockSize - 1 - i];
IppStatus ipp_status = ippStsNoErr;
//- If offset is != 0, that means we would have first encrypt a partial block at the
//beginning of the offset. That requires us to take the block index at that position and
//manually do the xor operation – first we encrypt a block (called zero_block), and then
//xor it starting at the offset.
//Once that block is encrypted, we may exit (if the dataSize is less than kBlockSize) or
//let ippcrypto start encrypting beginning a kBlockSize aligned offset
//kCounterLen is 64 bits same as index size so that 64 bits are incremented
// and counter stream generated by ipp and above match
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
if (offset == 0) {
ipp_status = ippsAESEncryptCTR((Ipp8u*)(data), (Ipp8u*)data, dataSize,
aes_ctx_, ctr_block, kCounterLen);
} else {
Ipp8u zero_block[kBlockSize]{0};
ipp_status = ippsAESEncryptCTR(zero_block, zero_block, kBlockSize, aes_ctx_,
ctr_block, kCounterLen);
if (ipp_status != ippStsNoErr)
return Status::Aborted(ippcpGetStatusString(ipp_status));
size_t n = std::min(kBlockSize - offset, dataSize);
for (size_t i = 0; i < n; ++i) data[i] ^= zero_block[offset + i];
memset(zero_block, 0, kBlockSize);
n = kBlockSize - offset;
if (dataSize > n) {
Ipp8u* ptr = (Ipp8u*)(data + n);
ipp_status = ippsAESEncryptCTR(ptr, ptr, dataSize - n, aes_ctx_,
ctr_block, kCounterLen);
}
}
if (ipp_status == ippStsNoErr) return Status::OK();
return Status::Aborted(ippcpGetStatusString(ipp_status));
}
Status IppcpCipherStream::Decrypt(uint64_t fileOffset, char* data,
size_t dataSize) {
// Decryption is implemented as encryption in CTR mode of operation
return Encrypt(fileOffset, data, dataSize);
}
std::unique_ptr<EncryptionProvider> IppcpProvider::CreateProvider() {
return std::unique_ptr<EncryptionProvider>(new IppcpProvider);
}
Status IppcpProvider::AddCipher(const std::string& /*descriptor*/,
const char* cipher, size_t len,
bool /*for_write*/) {
// We currently don't support more than one encryption key
if (aes_ctx_ != nullptr) {
return Status::InvalidArgument("Multiple encryption keys not supported.");
}
// AES supports key sizes of only 16, 24, or 32 bytes
if (len != 16 && len != 24 && len != 32) {
return Status::InvalidArgument("Invalid key size in provider.");
}
// len is in bytes
switch (len) {
case 16:
key_size_ = KeySize::AES_128;
break;
case 24:
key_size_ = KeySize::AES_192;
break;
case 32:
key_size_ = KeySize::AES_256;
break;
}
// get size for context
IppStatus ipp_status = ippsAESGetSize(&ctx_size_);
if (ipp_status != ippStsNoErr) {
return Status::Aborted("Failed to create provider.");
}
// allocate memory for context
aes_ctx_ = (IppsAESSpec*)(new Ipp8u[ctx_size_]);
assert(aes_ctx_ != nullptr);
// initialize context
const Ipp8u* key = (const Ipp8u*)(cipher);
ipp_status =
ippsAESInit(key, static_cast<int>(key_size_), aes_ctx_, ctx_size_);
if (ipp_status != ippStsNoErr) {
// clean up context and abort!
ippsAESInit(0, static_cast<int>(key_size_), aes_ctx_, ctx_size_);
delete[](Ipp8u*) aes_ctx_;
return Status::Aborted("Failed to create provider.");
}
return Status::OK();
}
Status IppcpProvider::CreateNewPrefix(const std::string& /*fname*/,
char* prefix, size_t prefixLength) const {
IppStatus ipp_status;
Ipp32u rnd;
const size_t rnd_size = sizeof(Ipp32u);
assert(prefixLength % rnd_size == 0);
for (size_t i = 0; i < prefixLength; i += rnd_size) {
// generate a cryptographically secured random number
ipp_status = ippsPRNGenRDRAND(&rnd, rnd_size << 3, nullptr);
if (ipp_status != ippStsNoErr)
return Status::Aborted(ippcpGetStatusString(ipp_status));
memcpy(prefix + i, &rnd, rnd_size);
}
IppcpCipherStream cs(aes_ctx_, prefix);
return cs.Encrypt(0, prefix + IppcpCipherStream::kBlockSize,
prefixLength - IppcpCipherStream::kBlockSize);
}
Status IppcpProvider::CreateCipherStream(
const std::string& /*fname*/, const EnvOptions& /*options*/, Slice& prefix,
std::unique_ptr<BlockAccessCipherStream>* result) {
assert(result != nullptr);
assert(prefix.size() >= IppcpCipherStream::kBlockSize);
result->reset(new IppcpCipherStream(aes_ctx_, prefix.data()));
Status ipp_status = (*result)->Decrypt(
0, (char*)prefix.data() + IppcpCipherStream::kBlockSize,
prefix.size() - IppcpCipherStream::kBlockSize);
return ipp_status;
}
IppcpProvider::~IppcpProvider() {
ippsAESInit(0, static_cast<int>(key_size_), aes_ctx_, ctx_size_);
delete[](Ipp8u*) aes_ctx_;
}
#endif // ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE