]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /** | |
4 | * Crypto filters for Put/Post/Get operations. | |
5 | */ | |
6 | #include <rgw/rgw_op.h> | |
7 | #include <rgw/rgw_crypt.h> | |
8 | #include <auth/Crypto.h> | |
9 | #include <rgw/rgw_b64.h> | |
10 | #include <rgw/rgw_rest_s3.h> | |
11 | #include "include/assert.h" | |
31f18b77 | 12 | #include <boost/utility/string_view.hpp> |
7c673cae FG |
13 | #include <rgw/rgw_keystone.h> |
14 | #include "include/str_map.h" | |
15 | #include "crypto/crypto_accel.h" | |
16 | #include "crypto/crypto_plugin.h" | |
17 | #ifdef USE_NSS | |
18 | # include <nspr.h> | |
19 | # include <nss.h> | |
20 | # include <pk11pub.h> | |
21 | #endif | |
22 | ||
23 | #ifdef USE_CRYPTOPP | |
24 | #include <cryptopp/cryptlib.h> | |
25 | #include <cryptopp/modes.h> | |
26 | #include <cryptopp/aes.h> | |
27 | using namespace CryptoPP; | |
28 | #endif | |
29 | ||
30 | #define dout_context g_ceph_context | |
31 | #define dout_subsys ceph_subsys_rgw | |
32 | ||
33 | using namespace rgw; | |
34 | ||
35 | /** | |
36 | * Encryption in CTR mode. offset is used as IV for each block. | |
37 | */ | |
38 | #warning "TODO: move this code to auth/Crypto for others to reuse." | |
39 | ||
40 | class AES_256_CTR : public BlockCrypt { | |
41 | public: | |
42 | static const size_t AES_256_KEYSIZE = 256 / 8; | |
43 | static const size_t AES_256_IVSIZE = 128 / 8; | |
44 | private: | |
45 | static const uint8_t IV[AES_256_IVSIZE]; | |
46 | CephContext* cct; | |
47 | uint8_t key[AES_256_KEYSIZE]; | |
48 | public: | |
49 | AES_256_CTR(CephContext* cct): cct(cct) { | |
50 | } | |
51 | ~AES_256_CTR() { | |
52 | memset(key, 0, AES_256_KEYSIZE); | |
53 | } | |
54 | bool set_key(const uint8_t* _key, size_t key_size) { | |
55 | if (key_size != AES_256_KEYSIZE) { | |
56 | return false; | |
57 | } | |
58 | memcpy(key, _key, AES_256_KEYSIZE); | |
59 | return true; | |
60 | } | |
61 | size_t get_block_size() { | |
62 | return AES_256_IVSIZE; | |
63 | } | |
64 | ||
65 | #ifdef USE_CRYPTOPP | |
66 | ||
67 | bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) { | |
68 | byte iv[AES_256_IVSIZE]; | |
69 | ldout(cct, 25) | |
70 | << "Encrypt in_ofs " << in_ofs | |
71 | << " size=" << size | |
72 | << " stream_offset=" << stream_offset | |
73 | << dendl; | |
74 | if (input.length() < in_ofs + size) { | |
75 | return false; | |
76 | } | |
77 | ||
78 | output.clear(); | |
79 | buffer::ptr buf((size + AES_256_KEYSIZE - 1) / AES_256_KEYSIZE * AES_256_KEYSIZE); | |
80 | /*create CTR mask*/ | |
81 | prepare_iv(iv, stream_offset); | |
82 | CTR_Mode< AES >::Encryption e; | |
83 | e.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); | |
84 | buf.zero(); | |
85 | e.ProcessData((byte*)buf.c_str(), (byte*)buf.c_str(), buf.length()); | |
86 | buf.set_length(size); | |
87 | off_t plaintext_pos = in_ofs; | |
88 | off_t crypt_pos = 0; | |
89 | auto iter = input.buffers().begin(); | |
90 | //skip unaffected begin | |
91 | while ((iter != input.buffers().end()) && (plaintext_pos >= iter->length())) { | |
92 | plaintext_pos -= iter->length(); | |
93 | ++iter; | |
94 | } | |
95 | while (iter != input.buffers().end()) { | |
96 | off_t cnt = std::min<off_t>(iter->length() - plaintext_pos, size - crypt_pos); | |
97 | byte* src = (byte*)iter->c_str() + plaintext_pos; | |
98 | byte* dst = (byte*)buf.c_str() + crypt_pos; | |
99 | for (off_t i = 0; i < cnt; i++) { | |
100 | dst[i] ^= src[i]; | |
101 | } | |
102 | ++iter; | |
103 | plaintext_pos = 0; | |
104 | crypt_pos += cnt; | |
105 | } | |
106 | output.append(buf); | |
107 | return true; | |
108 | } | |
109 | ||
110 | #elif defined(USE_NSS) | |
111 | ||
112 | bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) | |
113 | { | |
114 | bool result = false; | |
115 | PK11SlotInfo *slot; | |
116 | SECItem keyItem; | |
117 | PK11SymKey *symkey; | |
118 | CK_AES_CTR_PARAMS ctr_params = {0}; | |
119 | SECItem ivItem; | |
120 | SECItem *param; | |
121 | SECStatus ret; | |
122 | PK11Context *ectx; | |
123 | int written; | |
124 | unsigned int written2; | |
125 | ||
126 | slot = PK11_GetBestSlot(CKM_AES_CTR, NULL); | |
127 | if (slot) { | |
128 | keyItem.type = siBuffer; | |
129 | keyItem.data = key; | |
130 | keyItem.len = AES_256_KEYSIZE; | |
131 | ||
132 | symkey = PK11_ImportSymKey(slot, CKM_AES_CTR, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); | |
133 | if (symkey) { | |
134 | static_assert(sizeof(ctr_params.cb) >= AES_256_IVSIZE, "Must fit counter"); | |
135 | ctr_params.ulCounterBits = 128; | |
136 | prepare_iv(reinterpret_cast<unsigned char*>(&ctr_params.cb), stream_offset); | |
137 | ||
138 | ivItem.type = siBuffer; | |
139 | ivItem.data = (unsigned char*)&ctr_params; | |
140 | ivItem.len = sizeof(ctr_params); | |
141 | ||
142 | param = PK11_ParamFromIV(CKM_AES_CTR, &ivItem); | |
143 | if (param) { | |
144 | ectx = PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, symkey, param); | |
145 | if (ectx) { | |
146 | buffer::ptr buf((size + AES_256_KEYSIZE - 1) / AES_256_KEYSIZE * AES_256_KEYSIZE); | |
147 | ret = PK11_CipherOp(ectx, | |
148 | (unsigned char*)buf.c_str(), &written, buf.length(), | |
149 | (unsigned char*)input.c_str() + in_ofs, size); | |
150 | if (ret == SECSuccess) { | |
151 | ret = PK11_DigestFinal(ectx, | |
152 | (unsigned char*)buf.c_str() + written, &written2, | |
153 | buf.length() - written); | |
154 | if (ret == SECSuccess) { | |
155 | buf.set_length(written + written2); | |
156 | output.append(buf); | |
157 | result = true; | |
158 | } | |
159 | } | |
160 | PK11_DestroyContext(ectx, PR_TRUE); | |
161 | } | |
162 | SECITEM_FreeItem(param, PR_TRUE); | |
163 | } | |
164 | PK11_FreeSymKey(symkey); | |
165 | } | |
166 | PK11_FreeSlot(slot); | |
167 | } | |
168 | if (result == false) { | |
169 | ldout(cct, 5) << "Failed to perform AES-CTR encryption: " << PR_GetError() << dendl; | |
170 | } | |
171 | return result; | |
172 | } | |
173 | ||
174 | #else | |
175 | #error Must define USE_CRYPTOPP or USE_NSS | |
176 | #endif | |
177 | /* in CTR encrypt is the same as decrypt */ | |
178 | bool decrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) { | |
179 | return encrypt(input, in_ofs, size, output, stream_offset); | |
180 | } | |
181 | ||
182 | void prepare_iv(byte iv[AES_256_IVSIZE], off_t offset) { | |
183 | off_t index = offset / AES_256_IVSIZE; | |
184 | off_t i = AES_256_IVSIZE - 1; | |
185 | unsigned int val; | |
186 | unsigned int carry = 0; | |
187 | while (i>=0) { | |
188 | val = (index & 0xff) + IV[i] + carry; | |
189 | iv[i] = val; | |
190 | carry = val >> 8; | |
191 | index = index >> 8; | |
192 | i--; | |
193 | } | |
194 | } | |
195 | }; | |
196 | ||
197 | const uint8_t AES_256_CTR::IV[AES_256_CTR::AES_256_IVSIZE] = | |
198 | { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; | |
199 | ||
200 | ||
201 | CryptoAccelRef get_crypto_accel(CephContext *cct) | |
202 | { | |
203 | CryptoAccelRef ca_impl = nullptr; | |
204 | stringstream ss; | |
205 | PluginRegistry *reg = cct->get_plugin_registry(); | |
206 | string crypto_accel_type = cct->_conf->plugin_crypto_accelerator; | |
207 | ||
208 | CryptoPlugin *factory = dynamic_cast<CryptoPlugin*>(reg->get_with_load("crypto", crypto_accel_type)); | |
209 | if (factory == nullptr) { | |
210 | lderr(cct) << __func__ << " cannot load crypto accelerator of type " << crypto_accel_type << dendl; | |
211 | return nullptr; | |
212 | } | |
213 | int err = factory->factory(&ca_impl, &ss); | |
214 | if (err) { | |
215 | lderr(cct) << __func__ << " factory return error " << err << | |
216 | " with description: " << ss.str() << dendl; | |
217 | } | |
218 | return ca_impl; | |
219 | } | |
220 | ||
221 | ||
222 | /** | |
223 | * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block. | |
224 | * | |
225 | * | |
226 | * | |
227 | * A. Encryption | |
228 | * 1. Input is split to 4K chunks + remainder in one, smaller chunk | |
229 | * 2. Each full chunk is encrypted separately with CBC chained mode, with initial IV derived from offset | |
230 | * 3. Last chunk is 16*m + n. | |
231 | * 4. 16*m bytes are encrypted with CBC chained mode, with initial IV derived from offset | |
232 | * 5. Last n bytes are xor-ed with pattern obtained by CBC encryption of | |
233 | * last encrypted 16 byte block <16m-16, 16m-15) with IV = {0}. | |
234 | * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern | |
235 | * obtained by CBC encryption of {0} with IV derived from offset | |
236 | * | |
237 | * B. Decryption | |
238 | * 1. Input is split to 4K chunks + remainder in one, smaller chunk | |
239 | * 2. Each full chunk is decrypted separately with CBC chained mode, with initial IV derived from offset | |
240 | * 3. Last chunk is 16*m + n. | |
241 | * 4. 16*m bytes are decrypted with CBC chained mode, with initial IV derived from offset | |
242 | * 5. Last n bytes are xor-ed with pattern obtained by CBC ENCRYPTION of | |
243 | * last (still encrypted) 16 byte block <16m-16,16m-15) with IV = {0} | |
244 | * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern | |
245 | * obtained by CBC ENCRYPTION of {0} with IV derived from offset | |
246 | */ | |
247 | #warning "TODO: use auth/Crypto instead of reimplementing." | |
248 | class AES_256_CBC : public BlockCrypt { | |
249 | public: | |
250 | static const size_t AES_256_KEYSIZE = 256 / 8; | |
251 | static const size_t AES_256_IVSIZE = 128 / 8; | |
252 | static const size_t CHUNK_SIZE = 4096; | |
253 | private: | |
254 | static const uint8_t IV[AES_256_IVSIZE]; | |
255 | CephContext* cct; | |
256 | uint8_t key[AES_256_KEYSIZE]; | |
257 | public: | |
258 | AES_256_CBC(CephContext* cct): cct(cct) { | |
259 | } | |
260 | ~AES_256_CBC() { | |
261 | memset(key, 0, AES_256_KEYSIZE); | |
262 | } | |
263 | bool set_key(const uint8_t* _key, size_t key_size) { | |
264 | if (key_size != AES_256_KEYSIZE) { | |
265 | return false; | |
266 | } | |
267 | memcpy(key, _key, AES_256_KEYSIZE); | |
268 | return true; | |
269 | } | |
270 | size_t get_block_size() { | |
271 | return CHUNK_SIZE; | |
272 | } | |
273 | ||
274 | #ifdef USE_CRYPTOPP | |
275 | ||
276 | bool cbc_transform(unsigned char* out, | |
277 | const unsigned char* in, | |
278 | size_t size, | |
279 | const unsigned char (&iv)[AES_256_IVSIZE], | |
280 | const unsigned char (&key)[AES_256_KEYSIZE], | |
281 | bool encrypt) | |
282 | { | |
283 | if (encrypt) { | |
284 | CBC_Mode< AES >::Encryption e; | |
285 | e.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); | |
286 | e.ProcessData((byte*)out, (byte*)in, size); | |
287 | } else { | |
288 | CBC_Mode< AES >::Decryption d; | |
289 | d.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); | |
290 | d.ProcessData((byte*)out, (byte*)in, size); | |
291 | } | |
292 | return true; | |
293 | } | |
294 | ||
295 | #elif defined(USE_NSS) | |
296 | ||
297 | bool cbc_transform(unsigned char* out, | |
298 | const unsigned char* in, | |
299 | size_t size, | |
300 | const unsigned char (&iv)[AES_256_IVSIZE], | |
301 | const unsigned char (&key)[AES_256_KEYSIZE], | |
302 | bool encrypt) | |
303 | { | |
304 | bool result = false; | |
305 | PK11SlotInfo *slot; | |
306 | SECItem keyItem; | |
307 | PK11SymKey *symkey; | |
308 | CK_AES_CBC_ENCRYPT_DATA_PARAMS ctr_params = {0}; | |
309 | SECItem ivItem; | |
310 | SECItem *param; | |
311 | SECStatus ret; | |
312 | PK11Context *ectx; | |
313 | int written; | |
314 | ||
315 | slot = PK11_GetBestSlot(CKM_AES_CBC, NULL); | |
316 | if (slot) { | |
317 | keyItem.type = siBuffer; | |
318 | keyItem.data = const_cast<unsigned char*>(&key[0]); | |
319 | keyItem.len = AES_256_KEYSIZE; | |
320 | symkey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); | |
321 | if (symkey) { | |
322 | memcpy(ctr_params.iv, iv, AES_256_IVSIZE); | |
323 | ivItem.type = siBuffer; | |
324 | ivItem.data = (unsigned char*)&ctr_params; | |
325 | ivItem.len = sizeof(ctr_params); | |
326 | ||
327 | param = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); | |
328 | if (param) { | |
329 | ectx = PK11_CreateContextBySymKey(CKM_AES_CBC, encrypt?CKA_ENCRYPT:CKA_DECRYPT, symkey, param); | |
330 | if (ectx) { | |
331 | ret = PK11_CipherOp(ectx, | |
332 | out, &written, size, | |
333 | in, size); | |
334 | if ((ret == SECSuccess) && (written == (int)size)) { | |
335 | result = true; | |
336 | } | |
337 | PK11_DestroyContext(ectx, PR_TRUE); | |
338 | } | |
339 | SECITEM_FreeItem(param, PR_TRUE); | |
340 | } | |
341 | PK11_FreeSymKey(symkey); | |
342 | } | |
343 | PK11_FreeSlot(slot); | |
344 | } | |
345 | if (result == false) { | |
346 | ldout(cct, 5) << "Failed to perform AES-CBC encryption: " << PR_GetError() << dendl; | |
347 | } | |
348 | return result; | |
349 | } | |
350 | ||
351 | #else | |
352 | #error Must define USE_CRYPTOPP or USE_NSS | |
353 | #endif | |
354 | ||
355 | bool cbc_transform(unsigned char* out, | |
356 | const unsigned char* in, | |
357 | size_t size, | |
358 | off_t stream_offset, | |
359 | const unsigned char (&key)[AES_256_KEYSIZE], | |
360 | bool encrypt) | |
361 | { | |
362 | static std::atomic<bool> failed_to_get_crypto(false); | |
363 | CryptoAccelRef crypto_accel; | |
364 | if (! failed_to_get_crypto.load()) | |
365 | { | |
366 | crypto_accel = get_crypto_accel(cct); | |
367 | if (!crypto_accel) | |
368 | failed_to_get_crypto = true; | |
369 | } | |
370 | bool result = true; | |
371 | unsigned char iv[AES_256_IVSIZE]; | |
372 | for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) { | |
373 | size_t process_size = offset + CHUNK_SIZE <= size ? CHUNK_SIZE : size - offset; | |
374 | prepare_iv(iv, stream_offset + offset); | |
375 | if (crypto_accel != nullptr) { | |
376 | if (encrypt) { | |
377 | result = crypto_accel->cbc_encrypt(out + offset, in + offset, | |
378 | process_size, iv, key); | |
379 | } else { | |
380 | result = crypto_accel->cbc_decrypt(out + offset, in + offset, | |
381 | process_size, iv, key); | |
382 | } | |
383 | } else { | |
384 | result = cbc_transform( | |
385 | out + offset, in + offset, process_size, | |
386 | iv, key, encrypt); | |
387 | } | |
388 | } | |
389 | return result; | |
390 | } | |
391 | ||
392 | ||
393 | bool encrypt(bufferlist& input, | |
394 | off_t in_ofs, | |
395 | size_t size, | |
396 | bufferlist& output, | |
397 | off_t stream_offset) | |
398 | { | |
399 | bool result = false; | |
400 | size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; | |
401 | size_t unaligned_rest_size = size - aligned_size; | |
402 | output.clear(); | |
403 | buffer::ptr buf(aligned_size + AES_256_IVSIZE); | |
404 | unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str()); | |
405 | const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str()); | |
406 | ||
407 | /* encrypt main bulk of data */ | |
408 | result = cbc_transform(buf_raw, | |
409 | input_raw + in_ofs, | |
410 | aligned_size, | |
411 | stream_offset, key, true); | |
412 | if (result && (unaligned_rest_size > 0)) { | |
413 | /* remainder to encrypt */ | |
414 | if (aligned_size % CHUNK_SIZE > 0) { | |
415 | /* use last chunk for unaligned part */ | |
416 | unsigned char iv[AES_256_IVSIZE] = {0}; | |
417 | result = cbc_transform(buf_raw + aligned_size, | |
418 | buf_raw + aligned_size - AES_256_IVSIZE, | |
419 | AES_256_IVSIZE, | |
420 | iv, key, true); | |
421 | } else { | |
422 | /* 0 full blocks in current chunk, use IV as base for unaligned part */ | |
423 | unsigned char iv[AES_256_IVSIZE] = {0}; | |
424 | unsigned char data[AES_256_IVSIZE]; | |
425 | prepare_iv(data, stream_offset + aligned_size); | |
426 | result = cbc_transform(buf_raw + aligned_size, | |
427 | data, | |
428 | AES_256_IVSIZE, | |
429 | iv, key, true); | |
430 | } | |
431 | if (result) { | |
432 | for(size_t i = aligned_size; i < size; i++) { | |
433 | *(buf_raw + i) ^= *(input_raw + in_ofs + i); | |
434 | } | |
435 | } | |
436 | } | |
437 | if (result) { | |
438 | ldout(cct, 25) << "Encrypted " << size << " bytes"<< dendl; | |
439 | buf.set_length(size); | |
440 | output.append(buf); | |
441 | } else { | |
442 | ldout(cct, 5) << "Failed to encrypt" << dendl; | |
443 | } | |
444 | return result; | |
445 | } | |
446 | ||
447 | ||
448 | bool decrypt(bufferlist& input, | |
449 | off_t in_ofs, | |
450 | size_t size, | |
451 | bufferlist& output, | |
452 | off_t stream_offset) | |
453 | { | |
454 | bool result = false; | |
455 | size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; | |
456 | size_t unaligned_rest_size = size - aligned_size; | |
457 | output.clear(); | |
458 | buffer::ptr buf(aligned_size + AES_256_IVSIZE); | |
459 | unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str()); | |
460 | unsigned char* input_raw = reinterpret_cast<unsigned char*>(input.c_str()); | |
461 | ||
462 | /* decrypt main bulk of data */ | |
463 | result = cbc_transform(buf_raw, | |
464 | input_raw + in_ofs, | |
465 | aligned_size, | |
466 | stream_offset, key, false); | |
467 | if (result && unaligned_rest_size > 0) { | |
468 | /* remainder to decrypt */ | |
469 | if (aligned_size % CHUNK_SIZE > 0) { | |
470 | /*use last chunk for unaligned part*/ | |
471 | unsigned char iv[AES_256_IVSIZE] = {0}; | |
472 | result = cbc_transform(buf_raw + aligned_size, | |
473 | input_raw + in_ofs + aligned_size - AES_256_IVSIZE, | |
474 | AES_256_IVSIZE, | |
475 | iv, key, true); | |
476 | } else { | |
477 | /* 0 full blocks in current chunk, use IV as base for unaligned part */ | |
478 | unsigned char iv[AES_256_IVSIZE] = {0}; | |
479 | unsigned char data[AES_256_IVSIZE]; | |
480 | prepare_iv(data, stream_offset + aligned_size); | |
481 | result = cbc_transform(buf_raw + aligned_size, | |
482 | data, | |
483 | AES_256_IVSIZE, | |
484 | iv, key, true); | |
485 | } | |
486 | if (result) { | |
487 | for(size_t i = aligned_size; i < size; i++) { | |
488 | *(buf_raw + i) ^= *(input_raw + in_ofs + i); | |
489 | } | |
490 | } | |
491 | } | |
492 | if (result) { | |
493 | ldout(cct, 25) << "Decrypted " << size << " bytes"<< dendl; | |
494 | buf.set_length(size); | |
495 | output.append(buf); | |
496 | } else { | |
497 | ldout(cct, 5) << "Failed to decrypt" << dendl; | |
498 | } | |
499 | return result; | |
500 | } | |
501 | ||
502 | ||
503 | void prepare_iv(byte (&iv)[AES_256_IVSIZE], off_t offset) { | |
504 | off_t index = offset / AES_256_IVSIZE; | |
505 | off_t i = AES_256_IVSIZE - 1; | |
506 | unsigned int val; | |
507 | unsigned int carry = 0; | |
508 | while (i>=0) { | |
509 | val = (index & 0xff) + IV[i] + carry; | |
510 | iv[i] = val; | |
511 | carry = val >> 8; | |
512 | index = index >> 8; | |
513 | i--; | |
514 | } | |
515 | } | |
516 | }; | |
517 | ||
518 | ||
519 | std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len) | |
520 | { | |
521 | auto cbc = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(cct)); | |
522 | cbc->set_key(key, AES_256_KEYSIZE); | |
523 | return std::move(cbc); | |
524 | } | |
525 | ||
526 | ||
527 | const uint8_t AES_256_CBC::IV[AES_256_CBC::AES_256_IVSIZE] = | |
528 | { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; | |
529 | ||
530 | ||
531 | #ifdef USE_CRYPTOPP | |
532 | ||
533 | bool AES_256_ECB_encrypt(CephContext* cct, | |
534 | const uint8_t* key, | |
535 | size_t key_size, | |
536 | const uint8_t* data_in, | |
537 | uint8_t* data_out, | |
538 | size_t data_size) | |
539 | { | |
540 | bool res = false; | |
541 | if (key_size == AES_256_KEYSIZE) { | |
542 | try { | |
543 | ECB_Mode< AES >::Encryption e; | |
544 | e.SetKey( key, key_size ); | |
545 | e.ProcessData(data_out, data_in, data_size); | |
546 | res = true; | |
547 | } catch( CryptoPP::Exception& ex ) { | |
548 | ldout(cct, 5) << "AES-ECB encryption failed with: " << ex.GetWhat() << dendl; | |
549 | } | |
550 | } | |
551 | return res; | |
552 | } | |
553 | ||
554 | #elif defined USE_NSS | |
555 | ||
556 | bool AES_256_ECB_encrypt(CephContext* cct, | |
557 | const uint8_t* key, | |
558 | size_t key_size, | |
559 | const uint8_t* data_in, | |
560 | uint8_t* data_out, | |
561 | size_t data_size) { | |
562 | bool result = false; | |
563 | PK11SlotInfo *slot; | |
564 | SECItem keyItem; | |
565 | PK11SymKey *symkey; | |
566 | SECItem *param; | |
567 | SECStatus ret; | |
568 | PK11Context *ectx; | |
569 | int written; | |
570 | unsigned int written2; | |
571 | if (key_size == AES_256_KEYSIZE) { | |
572 | slot = PK11_GetBestSlot(CKM_AES_ECB, NULL); | |
573 | if (slot) { | |
574 | keyItem.type = siBuffer; | |
575 | keyItem.data = const_cast<uint8_t*>(key); | |
576 | keyItem.len = AES_256_KEYSIZE; | |
577 | ||
578 | param = PK11_ParamFromIV(CKM_AES_ECB, NULL); | |
579 | if (param) { | |
580 | symkey = PK11_ImportSymKey(slot, CKM_AES_ECB, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); | |
581 | if (symkey) { | |
582 | ectx = PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_ENCRYPT, symkey, param); | |
583 | if (ectx) { | |
584 | ret = PK11_CipherOp(ectx, | |
585 | data_out, &written, data_size, | |
586 | data_in, data_size); | |
587 | if (ret == SECSuccess) { | |
588 | ret = PK11_DigestFinal(ectx, | |
589 | data_out + written, &written2, | |
590 | data_size - written); | |
591 | if (ret == SECSuccess) { | |
592 | result = true; | |
593 | } | |
594 | } | |
595 | PK11_DestroyContext(ectx, PR_TRUE); | |
596 | } | |
597 | PK11_FreeSymKey(symkey); | |
598 | } | |
599 | SECITEM_FreeItem(param, PR_TRUE); | |
600 | } | |
601 | PK11_FreeSlot(slot); | |
602 | } | |
603 | if (result == false) { | |
604 | ldout(cct, 5) << "Failed to perform AES-ECB encryption: " << PR_GetError() << dendl; | |
605 | } | |
606 | } else { | |
607 | ldout(cct, 5) << "Key size must be 256 bits long" << dendl; | |
608 | } | |
609 | return result; | |
610 | } | |
611 | ||
612 | #else | |
613 | #error Must define USE_CRYPTOPP or USE_NSS | |
614 | #endif | |
615 | ||
616 | ||
617 | RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext* cct, | |
618 | RGWGetDataCB* next, | |
619 | std::unique_ptr<BlockCrypt> crypt): | |
620 | RGWGetObj_Filter(next), | |
621 | cct(cct), | |
622 | crypt(std::move(crypt)), | |
623 | enc_begin_skip(0), | |
624 | ofs(0), | |
625 | end(0), | |
626 | cache() | |
627 | { | |
628 | block_size = this->crypt->get_block_size(); | |
629 | } | |
630 | ||
631 | RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() { | |
632 | } | |
633 | ||
634 | int RGWGetObj_BlockDecrypt::read_manifest(bufferlist& manifest_bl) { | |
635 | parts_len.clear(); | |
636 | RGWObjManifest manifest; | |
637 | if (manifest_bl.length()) { | |
638 | bufferlist::iterator miter = manifest_bl.begin(); | |
639 | try { | |
640 | ::decode(manifest, miter); | |
641 | } catch (buffer::error& err) { | |
642 | ldout(cct, 0) << "ERROR: couldn't decode manifest" << dendl; | |
643 | return -EIO; | |
644 | } | |
645 | RGWObjManifest::obj_iterator mi; | |
646 | for (mi = manifest.obj_begin(); mi != manifest.obj_end(); ++mi) { | |
647 | if (mi.get_cur_stripe() == 0) { | |
648 | parts_len.push_back(0); | |
649 | } | |
650 | parts_len.back() += mi.get_stripe_size(); | |
651 | } | |
652 | if (cct->_conf->subsys.should_gather(ceph_subsys_rgw, 20)) { | |
653 | for (size_t i = 0; i<parts_len.size(); i++) { | |
654 | ldout(cct, 20) << "Manifest part " << i << ", size=" << parts_len[i] << dendl; | |
655 | } | |
656 | } | |
657 | } | |
658 | return 0; | |
659 | } | |
660 | ||
661 | int RGWGetObj_BlockDecrypt::fixup_range(off_t& bl_ofs, off_t& bl_end) { | |
662 | off_t inp_ofs = bl_ofs; | |
663 | off_t inp_end = bl_end; | |
664 | if (parts_len.size() > 0) { | |
665 | off_t in_ofs = bl_ofs; | |
666 | off_t in_end = bl_end; | |
667 | ||
668 | size_t i = 0; | |
669 | while (i<parts_len.size() && (in_ofs > (off_t)parts_len[i])) { | |
670 | in_ofs -= parts_len[i]; | |
671 | i++; | |
672 | } | |
673 | //in_ofs is inside block i | |
674 | size_t j = 0; | |
675 | while (j<parts_len.size() && (in_end > (off_t)parts_len[j])) { | |
676 | in_end -= parts_len[j]; | |
677 | j++; | |
678 | } | |
679 | //in_end is inside block j | |
680 | ||
681 | size_t rounded_end; | |
682 | rounded_end = ( in_end & ~(block_size - 1) ) + (block_size - 1); | |
683 | if (rounded_end + 1 >= parts_len[j]) { | |
684 | rounded_end = parts_len[j] - 1; | |
685 | } | |
686 | ||
687 | enc_begin_skip = in_ofs & (block_size - 1); | |
688 | ofs = bl_ofs - enc_begin_skip; | |
689 | end = bl_end; | |
690 | bl_ofs = bl_ofs - enc_begin_skip; | |
691 | bl_end += rounded_end - in_end; | |
692 | } | |
693 | else | |
694 | { | |
695 | enc_begin_skip = bl_ofs & (block_size - 1); | |
696 | ofs = bl_ofs & ~(block_size - 1); | |
697 | end = bl_end; | |
698 | bl_ofs = bl_ofs & ~(block_size - 1); | |
699 | bl_end = ( bl_end & ~(block_size - 1) ) + (block_size - 1); | |
700 | } | |
701 | ldout(cct, 20) << "fixup_range [" << inp_ofs << "," << inp_end | |
702 | << "] => [" << bl_ofs << "," << bl_end << "]" << dendl; | |
703 | return 0; | |
704 | } | |
705 | ||
706 | ||
707 | int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) { | |
708 | int res = 0; | |
709 | ldout(cct, 25) << "Decrypt " << bl_len << " bytes" << dendl; | |
710 | size_t part_ofs = ofs; | |
711 | size_t i = 0; | |
712 | while (i<parts_len.size() && (part_ofs >= parts_len[i])) { | |
713 | part_ofs -= parts_len[i]; | |
714 | i++; | |
715 | } | |
31f18b77 FG |
716 | bl.copy(bl_ofs, bl_len, cache); |
717 | off_t aligned_size = cache.length() & ~(block_size - 1); | |
718 | if (aligned_size > 0) { | |
719 | bufferlist data; | |
720 | if (! crypt->decrypt(cache, 0, aligned_size, data, part_ofs) ) { | |
721 | return -ERR_INTERNAL_ERROR; | |
7c673cae | 722 | } |
31f18b77 FG |
723 | off_t send_size = aligned_size - enc_begin_skip; |
724 | if (ofs + enc_begin_skip + send_size > end + 1) { | |
725 | send_size = end + 1 - ofs - enc_begin_skip; | |
7c673cae | 726 | } |
31f18b77 FG |
727 | res = next->handle_data(data, enc_begin_skip, send_size); |
728 | enc_begin_skip = 0; | |
729 | ofs += aligned_size; | |
730 | cache.splice(0, aligned_size); | |
7c673cae | 731 | } |
31f18b77 | 732 | return res; |
7c673cae FG |
733 | } |
734 | ||
735 | /** | |
736 | * flush remainder of data to output | |
737 | */ | |
738 | int RGWGetObj_BlockDecrypt::flush() { | |
739 | int res = 0; | |
740 | size_t part_ofs = ofs; | |
741 | size_t i = 0; | |
742 | while (i<parts_len.size() && (part_ofs > parts_len[i])) { | |
743 | part_ofs -= parts_len[i]; | |
744 | i++; | |
745 | } | |
746 | if (cache.length() > 0) { | |
747 | bufferlist data; | |
748 | if (! crypt->decrypt(cache, 0, cache.length(), data, part_ofs) ) { | |
749 | return -ERR_INTERNAL_ERROR; | |
750 | } | |
751 | off_t send_size = cache.length() - enc_begin_skip; | |
752 | if (ofs + enc_begin_skip + send_size > end + 1) { | |
753 | send_size = end + 1 - ofs - enc_begin_skip; | |
754 | } | |
755 | res = next->handle_data(data, enc_begin_skip, send_size); | |
756 | enc_begin_skip = 0; | |
757 | ofs += send_size; | |
758 | } | |
759 | return res; | |
760 | } | |
761 | ||
762 | RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext* cct, | |
763 | RGWPutObjDataProcessor* next, | |
764 | std::unique_ptr<BlockCrypt> crypt): | |
765 | RGWPutObj_Filter(next), | |
766 | cct(cct), | |
767 | crypt(std::move(crypt)), | |
768 | ofs(0), | |
769 | cache() | |
770 | { | |
771 | block_size = this->crypt->get_block_size(); | |
772 | } | |
773 | ||
774 | RGWPutObj_BlockEncrypt::~RGWPutObj_BlockEncrypt() { | |
775 | } | |
776 | ||
777 | int RGWPutObj_BlockEncrypt::handle_data(bufferlist& bl, | |
778 | off_t in_ofs, | |
779 | void **phandle, | |
780 | rgw_raw_obj *pobj, | |
781 | bool *again) { | |
782 | int res = 0; | |
783 | ldout(cct, 25) << "Encrypt " << bl.length() << " bytes" << dendl; | |
784 | ||
785 | if (*again) { | |
786 | bufferlist no_data; | |
787 | res = next->handle_data(no_data, in_ofs, phandle, pobj, again); | |
788 | //if *again is not set to false, we will have endless loop | |
789 | //drop info on log | |
790 | if (*again) { | |
791 | ldout(cct, 20) << "*again==true" << dendl; | |
792 | } | |
793 | return res; | |
794 | } | |
31f18b77 FG |
795 | |
796 | cache.append(bl); | |
797 | off_t proc_size = cache.length() & ~(block_size - 1); | |
798 | if (bl.length() == 0) { | |
799 | proc_size = cache.length(); | |
7c673cae | 800 | } |
31f18b77 FG |
801 | if (proc_size > 0) { |
802 | bufferlist data; | |
803 | if (! crypt->encrypt(cache, 0, proc_size, data, ofs) ) { | |
804 | return -ERR_INTERNAL_ERROR; | |
7c673cae | 805 | } |
31f18b77 FG |
806 | res = next->handle_data(data, ofs, phandle, pobj, again); |
807 | ofs += proc_size; | |
808 | cache.splice(0, proc_size); | |
809 | if (res < 0) | |
810 | return res; | |
7c673cae | 811 | } |
31f18b77 | 812 | |
7c673cae | 813 | if (bl.length() == 0) { |
7c673cae | 814 | /*replicate 0-sized handle_data*/ |
31f18b77 | 815 | res = next->handle_data(bl, ofs, phandle, pobj, again); |
7c673cae FG |
816 | } |
817 | return res; | |
818 | } | |
819 | ||
820 | int RGWPutObj_BlockEncrypt::throttle_data(void *handle, | |
821 | const rgw_raw_obj& obj, | |
822 | uint64_t size, | |
823 | bool need_to_wait) { | |
824 | return next->throttle_data(handle, obj, size, need_to_wait); | |
825 | } | |
826 | ||
827 | std::string create_random_key_selector(CephContext * const cct) { | |
828 | char random[AES_256_KEYSIZE]; | |
829 | if (get_random_bytes(&random[0], sizeof(random)) != 0) { | |
830 | ldout(cct, 0) << "ERROR: cannot get_random_bytes. " << dendl; | |
831 | for (char& v:random) v=rand(); | |
832 | } | |
833 | return std::string(random, sizeof(random)); | |
834 | } | |
835 | ||
836 | static int get_barbican_url(CephContext * const cct, | |
837 | std::string& url) | |
838 | { | |
839 | url = cct->_conf->rgw_barbican_url; | |
840 | if (url.empty()) { | |
841 | ldout(cct, 0) << "ERROR: conf rgw_barbican_url is not set" << dendl; | |
842 | return -EINVAL; | |
843 | } | |
844 | ||
845 | if (url.back() != '/') { | |
846 | url.append("/"); | |
847 | } | |
848 | ||
849 | return 0; | |
850 | } | |
851 | ||
852 | static int request_key_from_barbican(CephContext *cct, | |
31f18b77 FG |
853 | boost::string_view key_id, |
854 | boost::string_view key_selector, | |
7c673cae FG |
855 | const std::string& barbican_token, |
856 | std::string& actual_key) { | |
857 | std::string secret_url; | |
858 | int res; | |
859 | res = get_barbican_url(cct, secret_url); | |
860 | if (res < 0) { | |
861 | return res; | |
862 | } | |
863 | secret_url += "v1/secrets/" + std::string(key_id); | |
864 | ||
865 | bufferlist secret_bl; | |
866 | RGWHTTPTransceiver secret_req(cct, &secret_bl); | |
867 | secret_req.append_header("Accept", "application/octet-stream"); | |
868 | secret_req.append_header("X-Auth-Token", barbican_token); | |
869 | ||
870 | res = secret_req.process("GET", secret_url.c_str()); | |
871 | if (res < 0) { | |
872 | return res; | |
873 | } | |
874 | if (secret_req.get_http_status() == | |
875 | RGWHTTPTransceiver::HTTP_STATUS_UNAUTHORIZED) { | |
876 | return -EACCES; | |
877 | } | |
878 | ||
879 | if (secret_req.get_http_status() >=200 && | |
880 | secret_req.get_http_status() < 300 && | |
881 | secret_bl.length() == AES_256_KEYSIZE) { | |
882 | actual_key.assign(secret_bl.c_str(), secret_bl.length()); | |
883 | memset(secret_bl.c_str(), 0, secret_bl.length()); | |
884 | } else { | |
885 | res = -EACCES; | |
886 | } | |
887 | return res; | |
888 | } | |
889 | ||
890 | static map<string,string> get_str_map(const string &str) { | |
891 | map<string,string> m; | |
892 | get_str_map(str, &m, ";, \t"); | |
893 | return m; | |
894 | } | |
895 | ||
896 | static int get_actual_key_from_kms(CephContext *cct, | |
31f18b77 FG |
897 | boost::string_view key_id, |
898 | boost::string_view key_selector, | |
7c673cae FG |
899 | std::string& actual_key) |
900 | { | |
901 | int res = 0; | |
902 | ldout(cct, 20) << "Getting KMS encryption key for key=" << key_id << dendl; | |
903 | static map<string,string> str_map = get_str_map( | |
904 | cct->_conf->rgw_crypt_s3_kms_encryption_keys); | |
905 | ||
906 | map<string, string>::iterator it = str_map.find(std::string(key_id)); | |
907 | if (it != str_map.end() ) { | |
3efd9988 FG |
908 | std::string master_key; |
909 | try { | |
910 | master_key = from_base64((*it).second); | |
911 | } catch (...) { | |
912 | ldout(cct, 5) << "ERROR: get_actual_key_from_kms invalid encryption key id " | |
913 | << "which contains character that is not base64 encoded." | |
914 | << dendl; | |
915 | return -EINVAL; | |
916 | } | |
917 | ||
7c673cae FG |
918 | if (master_key.length() == AES_256_KEYSIZE) { |
919 | uint8_t _actual_key[AES_256_KEYSIZE]; | |
920 | if (AES_256_ECB_encrypt(cct, | |
921 | reinterpret_cast<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE, | |
922 | reinterpret_cast<const uint8_t*>(key_selector.data()), | |
923 | _actual_key, AES_256_KEYSIZE)) { | |
924 | actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE); | |
925 | } else { | |
926 | res = -EIO; | |
927 | } | |
928 | memset(_actual_key, 0, sizeof(_actual_key)); | |
929 | } else { | |
930 | ldout(cct, 20) << "Wrong size for key=" << key_id << dendl; | |
931 | res = -EIO; | |
932 | } | |
933 | } else { | |
934 | std::string token; | |
935 | if (rgw::keystone::Service::get_keystone_barbican_token(cct, token) < 0) { | |
936 | ldout(cct, 5) << "Failed to retrieve token for barbican" << dendl; | |
937 | res = -EINVAL; | |
938 | return res; | |
939 | } | |
940 | ||
941 | res = request_key_from_barbican(cct, key_id, key_selector, token, actual_key); | |
942 | if (res != 0) { | |
943 | ldout(cct, 5) << "Failed to retrieve secret from barbican:" << key_id << dendl; | |
944 | } | |
945 | } | |
946 | return res; | |
947 | } | |
948 | ||
949 | static inline void set_attr(map<string, bufferlist>& attrs, | |
950 | const char* key, | |
31f18b77 | 951 | boost::string_view value) |
7c673cae FG |
952 | { |
953 | bufferlist bl; | |
954 | bl.append(value.data(), value.size()); | |
955 | attrs[key] = std::move(bl); | |
956 | } | |
957 | ||
958 | static inline std::string get_str_attribute(map<string, bufferlist>& attrs, | |
959 | const char *name) | |
960 | { | |
961 | auto iter = attrs.find(name); | |
962 | if (iter == attrs.end()) { | |
963 | return {}; | |
964 | } | |
965 | return iter->second.to_str(); | |
966 | } | |
967 | ||
968 | typedef enum { | |
969 | X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM=0, | |
970 | X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, | |
971 | X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, | |
972 | X_AMZ_SERVER_SIDE_ENCRYPTION, | |
973 | X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID, | |
974 | X_AMZ_SERVER_SIDE_ENCRYPTION_LAST | |
975 | } crypt_option_e; | |
976 | ||
977 | typedef struct { | |
978 | const char* http_header_name; | |
979 | const std::string post_part_name; | |
980 | } crypt_option_names; | |
981 | ||
982 | static const crypt_option_names crypt_options[] = { | |
983 | {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", "x-amz-server-side-encryption-customer-algorithm"}, | |
984 | {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "x-amz-server-side-encryption-customer-key"}, | |
985 | {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "x-amz-server-side-encryption-customer-key-md5"}, | |
986 | {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", "x-amz-server-side-encryption"}, | |
987 | {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID", "x-amz-server-side-encryption-aws-kms-key-id"}, | |
988 | }; | |
989 | ||
31f18b77 FG |
990 | static boost::string_view get_crypt_attribute( |
991 | const RGWEnv* env, | |
7c673cae FG |
992 | std::map<std::string, |
993 | RGWPostObj_ObjStore::post_form_part, | |
994 | const ltstr_nocase>* parts, | |
995 | crypt_option_e option) | |
996 | { | |
997 | static_assert( | |
998 | X_AMZ_SERVER_SIDE_ENCRYPTION_LAST == sizeof(crypt_options)/sizeof(*crypt_options), | |
999 | "Missing items in crypt_options"); | |
1000 | if (parts != nullptr) { | |
1001 | auto iter | |
1002 | = parts->find(crypt_options[option].post_part_name); | |
1003 | if (iter == parts->end()) | |
31f18b77 | 1004 | return boost::string_view(); |
7c673cae | 1005 | bufferlist& data = iter->second.data; |
31f18b77 | 1006 | boost::string_view str = boost::string_view(data.c_str(), data.length()); |
7c673cae FG |
1007 | return rgw_trim_whitespace(str); |
1008 | } else { | |
1009 | const char* hdr = env->get(crypt_options[option].http_header_name, nullptr); | |
1010 | if (hdr != nullptr) { | |
31f18b77 | 1011 | return boost::string_view(hdr); |
7c673cae | 1012 | } else { |
31f18b77 | 1013 | return boost::string_view(); |
7c673cae FG |
1014 | } |
1015 | } | |
1016 | } | |
1017 | ||
1018 | ||
1019 | int rgw_s3_prepare_encrypt(struct req_state* s, | |
1020 | std::map<std::string, ceph::bufferlist>& attrs, | |
1021 | std::map<std::string, | |
1022 | RGWPostObj_ObjStore::post_form_part, | |
1023 | const ltstr_nocase>* parts, | |
1024 | std::unique_ptr<BlockCrypt>* block_crypt, | |
1025 | std::map<std::string, std::string>& crypt_http_responses) | |
1026 | { | |
1027 | int res = 0; | |
1028 | crypt_http_responses.clear(); | |
1029 | { | |
31f18b77 | 1030 | boost::string_view req_sse_ca = |
7c673cae FG |
1031 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM); |
1032 | if (! req_sse_ca.empty()) { | |
1033 | if (req_sse_ca != "AES256") { | |
d2e6a577 FG |
1034 | ldout(s->cct, 5) << "ERROR: Invalid value for header " |
1035 | << "x-amz-server-side-encryption-customer-algorithm" | |
1036 | << dendl; | |
3efd9988 FG |
1037 | s->err.message = "The requested encryption algorithm is not valid, must be AES256."; |
1038 | return -ERR_INVALID_ENCRYPTION_ALGORITHM; | |
7c673cae FG |
1039 | } |
1040 | if (s->cct->_conf->rgw_crypt_require_ssl && | |
f64942e4 | 1041 | !rgw_transport_is_secure(s->cct, *s->info.env)) { |
d2e6a577 | 1042 | ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; |
7c673cae FG |
1043 | return -ERR_INVALID_REQUEST; |
1044 | } | |
3efd9988 FG |
1045 | |
1046 | std::string key_bin; | |
1047 | try { | |
1048 | key_bin = from_base64( | |
7c673cae | 1049 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY) ); |
3efd9988 FG |
1050 | } catch (...) { |
1051 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption " | |
1052 | << "key which contains character that is not base64 encoded." | |
1053 | << dendl; | |
1054 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1055 | "provided keys must provide an appropriate secret key."; | |
1056 | return -EINVAL; | |
1057 | } | |
1058 | ||
7c673cae | 1059 | if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { |
d2e6a577 | 1060 | ldout(s->cct, 5) << "ERROR: invalid encryption key size" << dendl; |
3efd9988 FG |
1061 | s->err.message = "Requests specifying Server Side Encryption with Customer " |
1062 | "provided keys must provide an appropriate secret key."; | |
1063 | return -EINVAL; | |
7c673cae | 1064 | } |
3efd9988 | 1065 | |
31f18b77 | 1066 | boost::string_view keymd5 = |
7c673cae | 1067 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5); |
3efd9988 FG |
1068 | |
1069 | std::string keymd5_bin; | |
1070 | try { | |
1071 | keymd5_bin = from_base64(keymd5); | |
1072 | } catch (...) { | |
1073 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key " | |
1074 | << "md5 which contains character that is not base64 encoded." | |
1075 | << dendl; | |
1076 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1077 | "provided keys must provide an appropriate secret key md5."; | |
1078 | return -EINVAL; | |
1079 | } | |
1080 | ||
7c673cae | 1081 | if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { |
d2e6a577 | 1082 | ldout(s->cct, 5) << "ERROR: Invalid key md5 size" << dendl; |
3efd9988 FG |
1083 | s->err.message = "Requests specifying Server Side Encryption with Customer " |
1084 | "provided keys must provide an appropriate secret key md5."; | |
1085 | return -EINVAL; | |
7c673cae | 1086 | } |
3efd9988 | 1087 | |
7c673cae FG |
1088 | MD5 key_hash; |
1089 | byte key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; | |
1090 | key_hash.Update(reinterpret_cast<const byte*>(key_bin.c_str()), key_bin.size()); | |
1091 | key_hash.Final(key_hash_res); | |
1092 | ||
1093 | if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { | |
d2e6a577 | 1094 | ldout(s->cct, 5) << "ERROR: Invalid key md5 hash" << dendl; |
3efd9988 FG |
1095 | s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; |
1096 | return -EINVAL; | |
7c673cae FG |
1097 | } |
1098 | ||
1099 | set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); | |
1100 | set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); | |
1101 | ||
1102 | if (block_crypt) { | |
1103 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1104 | aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE); | |
1105 | *block_crypt = std::move(aes); | |
1106 | } | |
1107 | ||
1108 | crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; | |
1109 | crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5.to_string(); | |
1110 | return 0; | |
3efd9988 FG |
1111 | } else { |
1112 | boost::string_view customer_key = | |
1113 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY); | |
1114 | if (!customer_key.empty()) { | |
1115 | ldout(s->cct, 5) << "ERROR: SSE-C encryption request is missing the header " | |
1116 | << "x-amz-server-side-encryption-customer-algorithm" | |
1117 | << dendl; | |
1118 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1119 | "provided keys must provide a valid encryption algorithm."; | |
1120 | return -EINVAL; | |
1121 | } | |
1122 | ||
1123 | boost::string_view customer_key_md5 = | |
1124 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5); | |
1125 | if (!customer_key_md5.empty()) { | |
1126 | ldout(s->cct, 5) << "ERROR: SSE-C encryption request is missing the header " | |
1127 | << "x-amz-server-side-encryption-customer-algorithm" | |
1128 | << dendl; | |
1129 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1130 | "provided keys must provide a valid encryption algorithm."; | |
1131 | return -EINVAL; | |
1132 | } | |
7c673cae | 1133 | } |
3efd9988 | 1134 | |
7c673cae | 1135 | /* AMAZON server side encryption with KMS (key management service) */ |
31f18b77 | 1136 | boost::string_view req_sse = |
7c673cae FG |
1137 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION); |
1138 | if (! req_sse.empty()) { | |
1139 | if (req_sse != "aws:kms") { | |
d2e6a577 FG |
1140 | ldout(s->cct, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption" |
1141 | << dendl; | |
3efd9988 FG |
1142 | s->err.message = "Server Side Encryption with KMS managed key requires " |
1143 | "HTTP header x-amz-server-side-encryption : aws:kms"; | |
1144 | return -EINVAL; | |
7c673cae FG |
1145 | } |
1146 | if (s->cct->_conf->rgw_crypt_require_ssl && | |
f64942e4 | 1147 | !rgw_transport_is_secure(s->cct, *s->info.env)) { |
d2e6a577 | 1148 | ldout(s->cct, 5) << "ERROR: insecure request, rgw_crypt_require_ssl is set" << dendl; |
7c673cae FG |
1149 | return -ERR_INVALID_REQUEST; |
1150 | } | |
31f18b77 | 1151 | boost::string_view key_id = |
7c673cae FG |
1152 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID); |
1153 | if (key_id.empty()) { | |
3efd9988 FG |
1154 | ldout(s->cct, 5) << "ERROR: not provide a valid key id" << dendl; |
1155 | s->err.message = "Server Side Encryption with KMS managed key requires " | |
1156 | "HTTP header x-amz-server-side-encryption-aws-kms-key-id"; | |
7c673cae FG |
1157 | return -ERR_INVALID_ACCESS_KEY; |
1158 | } | |
1159 | /* try to retrieve actual key */ | |
1160 | std::string key_selector = create_random_key_selector(s->cct); | |
1161 | std::string actual_key; | |
1162 | res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); | |
3efd9988 FG |
1163 | if (res != 0) { |
1164 | ldout(s->cct, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl; | |
1165 | s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id.to_string(); | |
7c673cae | 1166 | return res; |
3efd9988 | 1167 | } |
7c673cae FG |
1168 | if (actual_key.size() != AES_256_KEYSIZE) { |
1169 | ldout(s->cct, 5) << "ERROR: key obtained from key_id:" << | |
1170 | key_id << " is not 256 bit size" << dendl; | |
3efd9988 | 1171 | s->err.message = "KMS provided an invalid key for the given kms-keyid."; |
7c673cae FG |
1172 | return -ERR_INVALID_ACCESS_KEY; |
1173 | } | |
1174 | set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS"); | |
1175 | set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id); | |
1176 | set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); | |
1177 | ||
1178 | if (block_crypt) { | |
1179 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1180 | aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE); | |
1181 | *block_crypt = std::move(aes); | |
1182 | } | |
1183 | actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); | |
3efd9988 FG |
1184 | |
1185 | crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; | |
1186 | crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id.to_string(); | |
7c673cae | 1187 | return 0; |
3efd9988 FG |
1188 | } else { |
1189 | boost::string_view key_id = | |
1190 | get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID); | |
1191 | if (!key_id.empty()) { | |
1192 | ldout(s->cct, 5) << "ERROR: SSE-KMS encryption request is missing the header " | |
1193 | << "x-amz-server-side-encryption" | |
1194 | << dendl; | |
1195 | s->err.message = "Server Side Encryption with KMS managed key requires " | |
1196 | "HTTP header x-amz-server-side-encryption : aws:kms"; | |
1197 | return -EINVAL; | |
1198 | } | |
7c673cae FG |
1199 | } |
1200 | ||
1201 | /* no other encryption mode, check if default encryption is selected */ | |
1202 | if (s->cct->_conf->rgw_crypt_default_encryption_key != "") { | |
3efd9988 FG |
1203 | std::string master_encryption_key; |
1204 | try { | |
1205 | master_encryption_key = from_base64(s->cct->_conf->rgw_crypt_default_encryption_key); | |
1206 | } catch (...) { | |
1207 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key " | |
1208 | << "which contains character that is not base64 encoded." | |
1209 | << dendl; | |
1210 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1211 | "provided keys must provide an appropriate secret key."; | |
1212 | return -EINVAL; | |
1213 | } | |
1214 | ||
7c673cae FG |
1215 | if (master_encryption_key.size() != 256 / 8) { |
1216 | ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; | |
1217 | /* not an error to return; missing encryption does not inhibit processing */ | |
1218 | return 0; | |
1219 | } | |
1220 | ||
1221 | set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); | |
1222 | std::string key_selector = create_random_key_selector(s->cct); | |
1223 | set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); | |
1224 | ||
1225 | uint8_t actual_key[AES_256_KEYSIZE]; | |
1226 | if (AES_256_ECB_encrypt(s->cct, | |
1227 | reinterpret_cast<const uint8_t*>(master_encryption_key.c_str()), AES_256_KEYSIZE, | |
1228 | reinterpret_cast<const uint8_t*>(key_selector.c_str()), | |
1229 | actual_key, AES_256_KEYSIZE) != true) { | |
1230 | memset(actual_key, 0, sizeof(actual_key)); | |
1231 | return -EIO; | |
1232 | } | |
1233 | if (block_crypt) { | |
1234 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1235 | aes->set_key(reinterpret_cast<const uint8_t*>(actual_key), AES_256_KEYSIZE); | |
1236 | *block_crypt = std::move(aes); | |
1237 | } | |
1238 | memset(actual_key, 0, sizeof(actual_key)); | |
1239 | return 0; | |
1240 | } | |
1241 | } | |
1242 | /*no encryption*/ | |
1243 | return 0; | |
1244 | } | |
1245 | ||
1246 | ||
1247 | int rgw_s3_prepare_decrypt(struct req_state* s, | |
1248 | map<string, bufferlist>& attrs, | |
1249 | std::unique_ptr<BlockCrypt>* block_crypt, | |
1250 | std::map<std::string, std::string>& crypt_http_responses) | |
1251 | { | |
1252 | int res = 0; | |
1253 | std::string stored_mode = get_str_attribute(attrs, RGW_ATTR_CRYPT_MODE); | |
1254 | ldout(s->cct, 15) << "Encryption mode: " << stored_mode << dendl; | |
181888fb FG |
1255 | |
1256 | const char *req_sse = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", NULL); | |
1257 | if (nullptr != req_sse && (s->op == OP_GET || s->op == OP_HEAD)) { | |
1258 | return -ERR_INVALID_REQUEST; | |
1259 | } | |
1260 | ||
7c673cae FG |
1261 | if (stored_mode == "SSE-C-AES256") { |
1262 | if (s->cct->_conf->rgw_crypt_require_ssl && | |
f64942e4 | 1263 | !rgw_transport_is_secure(s->cct, *s->info.env)) { |
d2e6a577 | 1264 | ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; |
7c673cae FG |
1265 | return -ERR_INVALID_REQUEST; |
1266 | } | |
1267 | const char *req_cust_alg = | |
1268 | s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); | |
1269 | ||
3efd9988 FG |
1270 | if (nullptr == req_cust_alg) { |
1271 | ldout(s->cct, 5) << "ERROR: Request for SSE-C encrypted object missing " | |
d2e6a577 FG |
1272 | << "x-amz-server-side-encryption-customer-algorithm" |
1273 | << dendl; | |
3efd9988 FG |
1274 | s->err.message = "Requests specifying Server Side Encryption with Customer " |
1275 | "provided keys must provide a valid encryption algorithm."; | |
1276 | return -EINVAL; | |
1277 | } else if (strcmp(req_cust_alg, "AES256") != 0) { | |
1278 | ldout(s->cct, 5) << "ERROR: The requested encryption algorithm is not valid, must be AES256." << dendl; | |
1279 | s->err.message = "The requested encryption algorithm is not valid, must be AES256."; | |
1280 | return -ERR_INVALID_ENCRYPTION_ALGORITHM; | |
1281 | } | |
1282 | ||
1283 | std::string key_bin; | |
1284 | try { | |
1285 | key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); | |
1286 | } catch (...) { | |
1287 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key " | |
1288 | << "which contains character that is not base64 encoded." | |
1289 | << dendl; | |
1290 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1291 | "provided keys must provide an appropriate secret key."; | |
1292 | return -EINVAL; | |
7c673cae FG |
1293 | } |
1294 | ||
7c673cae | 1295 | if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { |
d2e6a577 | 1296 | ldout(s->cct, 5) << "ERROR: Invalid encryption key size" << dendl; |
3efd9988 FG |
1297 | s->err.message = "Requests specifying Server Side Encryption with Customer " |
1298 | "provided keys must provide an appropriate secret key."; | |
1299 | return -EINVAL; | |
7c673cae FG |
1300 | } |
1301 | ||
1302 | std::string keymd5 = | |
1303 | s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); | |
3efd9988 FG |
1304 | std::string keymd5_bin; |
1305 | try { | |
1306 | keymd5_bin = from_base64(keymd5); | |
1307 | } catch (...) { | |
1308 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 " | |
1309 | << "which contains character that is not base64 encoded." | |
1310 | << dendl; | |
1311 | s->err.message = "Requests specifying Server Side Encryption with Customer " | |
1312 | "provided keys must provide an appropriate secret key md5."; | |
1313 | return -EINVAL; | |
1314 | } | |
1315 | ||
1316 | ||
7c673cae | 1317 | if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { |
d2e6a577 | 1318 | ldout(s->cct, 5) << "ERROR: Invalid key md5 size " << dendl; |
3efd9988 FG |
1319 | s->err.message = "Requests specifying Server Side Encryption with Customer " |
1320 | "provided keys must provide an appropriate secret key md5."; | |
1321 | return -EINVAL; | |
7c673cae FG |
1322 | } |
1323 | ||
1324 | MD5 key_hash; | |
1325 | uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; | |
1326 | key_hash.Update(reinterpret_cast<const byte*>(key_bin.c_str()), key_bin.size()); | |
1327 | key_hash.Final(key_hash_res); | |
1328 | ||
1329 | if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) || | |
1330 | (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) { | |
3efd9988 FG |
1331 | s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; |
1332 | return -EINVAL; | |
7c673cae FG |
1333 | } |
1334 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1335 | aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_CBC::AES_256_KEYSIZE); | |
1336 | if (block_crypt) *block_crypt = std::move(aes); | |
1337 | ||
1338 | crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; | |
1339 | crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5; | |
1340 | return 0; | |
1341 | } | |
1342 | ||
1343 | if (stored_mode == "SSE-KMS") { | |
1344 | if (s->cct->_conf->rgw_crypt_require_ssl && | |
f64942e4 | 1345 | !rgw_transport_is_secure(s->cct, *s->info.env)) { |
d2e6a577 | 1346 | ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; |
7c673cae FG |
1347 | return -ERR_INVALID_REQUEST; |
1348 | } | |
1349 | /* try to retrieve actual key */ | |
1350 | std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID); | |
1351 | std::string key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); | |
1352 | std::string actual_key; | |
1353 | res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); | |
1354 | if (res != 0) { | |
3efd9988 FG |
1355 | ldout(s->cct, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl; |
1356 | s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id; | |
7c673cae FG |
1357 | return res; |
1358 | } | |
1359 | if (actual_key.size() != AES_256_KEYSIZE) { | |
1360 | ldout(s->cct, 0) << "ERROR: key obtained from key_id:" << | |
1361 | key_id << " is not 256 bit size" << dendl; | |
3efd9988 | 1362 | s->err.message = "KMS provided an invalid key for the given kms-keyid."; |
7c673cae FG |
1363 | return -ERR_INVALID_ACCESS_KEY; |
1364 | } | |
1365 | ||
1366 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1367 | aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE); | |
1368 | actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); | |
1369 | if (block_crypt) *block_crypt = std::move(aes); | |
1370 | ||
1371 | crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; | |
1372 | crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id; | |
1373 | return 0; | |
1374 | } | |
1375 | ||
1376 | if (stored_mode == "RGW-AUTO") { | |
3efd9988 FG |
1377 | std::string master_encryption_key; |
1378 | try { | |
1379 | master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); | |
1380 | } catch (...) { | |
1381 | ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key " | |
1382 | << "which contains character that is not base64 encoded." | |
1383 | << dendl; | |
1384 | s->err.message = "The default encryption key is not valid base64."; | |
1385 | return -EINVAL; | |
1386 | } | |
1387 | ||
7c673cae FG |
1388 | if (master_encryption_key.size() != 256 / 8) { |
1389 | ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; | |
1390 | return -EIO; | |
1391 | } | |
1392 | std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); | |
1393 | if (attr_key_selector.size() != AES_256_CBC::AES_256_KEYSIZE) { | |
1394 | ldout(s->cct, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl; | |
1395 | return -EIO; | |
1396 | } | |
1397 | uint8_t actual_key[AES_256_KEYSIZE]; | |
1398 | if (AES_256_ECB_encrypt(s->cct, | |
1399 | reinterpret_cast<const uint8_t*>(master_encryption_key.c_str()), | |
1400 | AES_256_KEYSIZE, | |
1401 | reinterpret_cast<const uint8_t*>(attr_key_selector.c_str()), | |
1402 | actual_key, AES_256_KEYSIZE) != true) { | |
1403 | memset(actual_key, 0, sizeof(actual_key)); | |
1404 | return -EIO; | |
1405 | } | |
1406 | auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct)); | |
1407 | aes->set_key(actual_key, AES_256_KEYSIZE); | |
1408 | memset(actual_key, 0, sizeof(actual_key)); | |
1409 | if (block_crypt) *block_crypt = std::move(aes); | |
1410 | return 0; | |
1411 | } | |
1412 | /*no decryption*/ | |
1413 | return 0; | |
1414 | } |