]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_crypt.cc
import ceph 16.2.6
[ceph.git] / ceph / src / rgw / rgw_crypt.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 /**
5 * Crypto filters for Put/Post/Get operations.
6 */
7
8 #include <string_view>
9
10 #include <rgw/rgw_op.h>
11 #include <rgw/rgw_crypt.h>
12 #include <auth/Crypto.h>
13 #include <rgw/rgw_b64.h>
14 #include <rgw/rgw_rest_s3.h>
15 #include "include/ceph_assert.h"
16 #include "crypto/crypto_accel.h"
17 #include "crypto/crypto_plugin.h"
18 #include "rgw/rgw_kms.h"
19 #include "rapidjson/document.h"
20 #include "rapidjson/writer.h"
21 #include "rapidjson/error/error.h"
22 #include "rapidjson/error/en.h"
23 #include <unicode/normalizer2.h> // libicu
24
25 #include <openssl/evp.h>
26
27 #define dout_context g_ceph_context
28 #define dout_subsys ceph_subsys_rgw
29
30 using namespace rgw;
31
32
33 template<typename M>
34 class canonical_char_sorter {
35 private:
36 const icu::Normalizer2* normalizer;
37 CephContext *cct;
38
39 public:
40 canonical_char_sorter(CephContext *cct) : cct(cct) {
41 UErrorCode status = U_ZERO_ERROR;
42 normalizer = icu::Normalizer2::getNFCInstance(status);
43 if (U_FAILURE(status)) {
44 lderr(cct) << "ERROR: can't get nfc instance, error = " << status << dendl;
45 normalizer = 0;
46 }
47 }
48 bool compare_helper (const M *, const M *);
49 bool make_string_canonical(rapidjson::Value &,
50 rapidjson::Document::AllocatorType&);
51 };
52
53 template<typename M>
54 bool
55 canonical_char_sorter<M>::compare_helper (const M*a, const M*b)
56 {
57 UErrorCode status = U_ZERO_ERROR;
58 const std::string as{a->name.GetString(), a->name.GetStringLength()},
59 bs{b->name.GetString(), b->name.GetStringLength()};
60 icu::UnicodeString aw{icu::UnicodeString::fromUTF8(as)}, bw{icu::UnicodeString::fromUTF8(bs)};
61 int32_t afl{ aw.countChar32()}, bfl{bw.countChar32()};
62 std::u32string af, bf;
63 af.resize(afl); bf.resize(bfl);
64 auto *astr{af.c_str()}, *bstr{bf.c_str()};
65 aw.toUTF32((int32_t*)astr, afl, status);
66 bw.toUTF32((int32_t*)bstr, bfl, status);
67 bool r{af < bf};
68 return r;
69 }
70
71 template<typename M>
72 bool
73 canonical_char_sorter<M>::make_string_canonical (rapidjson::Value &v, rapidjson::Document::AllocatorType&a)
74 {
75 UErrorCode status = U_ZERO_ERROR;
76 const std::string as{v.GetString(), v.GetStringLength()};
77
78 if (!normalizer)
79 return false;
80 const icu::UnicodeString aw{icu::UnicodeString::fromUTF8(as)};
81 icu::UnicodeString an{normalizer->normalize(aw, status)};
82 if (U_FAILURE(status)) {
83 ldout(cct, 5) << "conversion error; code=" << status <<
84 " on string " << as << dendl;
85 return false;
86 }
87 std::string ans;
88 an.toUTF8String(ans);
89 v.SetString(ans.c_str(), ans.length(), a);
90 return true;
91 }
92
93 typedef
94 rapidjson::GenericMember<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<> >
95 MyMember;
96
97 template<typename H>
98 bool
99 sort_and_write(rapidjson::Value &d, H &writer, canonical_char_sorter<MyMember>& ccs)
100 {
101 bool r;
102 switch(d.GetType()) {
103 case rapidjson::kObjectType: {
104 struct comparer {
105 canonical_char_sorter<MyMember> &r;
106 comparer(canonical_char_sorter<MyMember> &r) : r(r) {};
107 bool operator()(const MyMember*a, const MyMember*b) {
108 return r.compare_helper(a,b);
109 }
110 } cmp_functor{ccs};
111 if (!(r = writer.StartObject()))
112 break;
113 std::vector<MyMember*> q;
114 for (auto &m: d.GetObject())
115 q.push_back(&m);
116 std::sort(q.begin(), q.end(), cmp_functor);
117 for (auto m: q) {
118 assert(m->name.IsString());
119 if (!(r = writer.Key(m->name.GetString(), m->name.GetStringLength())))
120 goto Done;
121 if (!(r = sort_and_write(m->value, writer, ccs)))
122 goto Done;
123 }
124 r = writer.EndObject();
125 break; }
126 case rapidjson::kArrayType:
127 if (!(r = writer.StartArray()))
128 break;
129 for (auto &v: d.GetArray()) {
130 if (!(r = sort_and_write(v, writer, ccs)))
131 goto Done;
132 }
133 r = writer.EndArray();
134 break;
135 default:
136 r = d.Accept(writer);
137 break;
138 }
139 Done:
140 return r;
141 }
142
143 enum struct mec_option {
144 empty = 0, number_ok = 1
145 };
146
147 enum struct mec_error {
148 success = 0, conversion, number
149 };
150
151 mec_error
152 make_everything_canonical(rapidjson::Value &d, rapidjson::Document::AllocatorType&a, canonical_char_sorter<MyMember>& ccs, mec_option f = mec_option::empty )
153 {
154 mec_error r;
155 switch(d.GetType()) {
156 case rapidjson::kObjectType:
157 for (auto &m: d.GetObject()) {
158 assert(m.name.IsString());
159 if (!ccs.make_string_canonical(m.name, a)) {
160 r = mec_error::conversion;
161 goto Error;
162 }
163 if ((r = make_everything_canonical(m.value, a, ccs, f)) != mec_error::success)
164 goto Error;
165 }
166 break;
167 case rapidjson::kArrayType:
168 for (auto &v: d.GetArray()) {
169 if ((r = make_everything_canonical(v, a, ccs, f)) != mec_error::success)
170 goto Error;
171 }
172 break;
173 case rapidjson::kStringType:
174 if (!ccs.make_string_canonical(d, a)) {
175 r = mec_error::conversion;
176 goto Error;
177 }
178 break;
179 case rapidjson::kNumberType:
180 if (static_cast<int>(f) & static_cast<int>(mec_option::number_ok))
181 break;
182 r = mec_error::number;
183 goto Error;
184 default:
185 break;
186 }
187 r = mec_error::success;
188 Error:
189 return r;
190 }
191
192 bool
193 add_object_to_context(rgw_obj &obj, rapidjson::Document &d)
194 {
195 ARN a{obj};
196 const char aws_s3_arn[] { "aws:s3:arn" };
197 std::string as{a.to_string()};
198 rapidjson::Document::AllocatorType &allocator { d.GetAllocator() };
199 rapidjson::Value name, val;
200
201 if (!d.IsObject())
202 return false;
203 if (d.HasMember(aws_s3_arn))
204 return true;
205 val.SetString(as.c_str(), as.length(), allocator);
206 name.SetString(aws_s3_arn, sizeof aws_s3_arn - 1, allocator);
207 d.AddMember(name, val, allocator);
208 return true;
209 }
210
211 static inline const std::string &
212 get_tenant_or_id(req_state *s)
213 {
214 const std::string &tenant{ s->user->get_tenant() };
215 if (!tenant.empty()) return tenant;
216 return s->user->get_id().id;
217 }
218
219 int
220 make_canonical_context(struct req_state *s,
221 std::string_view &context,
222 std::string &cooked_context)
223 {
224 rapidjson::Document d;
225 bool b = false;
226 mec_option options {
227 //mec_option::number_ok : SEE BOTTOM OF FILE
228 mec_option::empty };
229 rgw_obj obj;
230 std::ostringstream oss;
231 canonical_char_sorter<MyMember> ccs{s->cct};
232
233 obj.bucket.tenant = get_tenant_or_id(s);
234 obj.bucket.name = s->bucket->get_name();
235 obj.key.name = s->object->get_name();
236 std::string iline;
237 rapidjson::Document::AllocatorType &allocator { d.GetAllocator() };
238
239 try {
240 iline = rgw::from_base64(context);
241 } catch (const std::exception& e) {
242 oss << "bad context: " << e.what();
243 s->err.message = oss.str();
244 return -ERR_INVALID_REQUEST;
245 }
246 rapidjson::StringStream isw(iline.c_str());
247 if (!iline.length())
248 d.SetObject();
249 // else if (qflag) SEE BOTTOM OF FILE
250 // d.ParseStream<rapidjson::kParseNumbersAsStringsFlag>(isw);
251 else
252 d.ParseStream<rapidjson::kParseFullPrecisionFlag>(isw);
253 if (isw.Tell() != iline.length()) {
254 oss << "bad context: did not consume all of input: @ "
255 << isw.Tell();
256 s->err.message = oss.str();
257 return -ERR_INVALID_REQUEST;
258 }
259 if (d.HasParseError()) {
260 oss << "bad context: parse error: @ " << d.GetErrorOffset()
261 << " " << rapidjson::GetParseError_En(d.GetParseError());
262 s->err.message = oss.str();
263 return -ERR_INVALID_REQUEST;
264 }
265 rapidjson::StringBuffer buf;
266 rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
267 if (!add_object_to_context(obj, d)) {
268 lderr(s->cct) << "ERROR: can't add default value to context" << dendl;
269 s->err.message = "context: internal error adding defaults";
270 return -ERR_INVALID_REQUEST;
271 }
272 b = make_everything_canonical(d, allocator, ccs, options) == mec_error::success;
273 if (!b) {
274 lderr(s->cct) << "ERROR: can't make canonical json <"
275 << context << ">" << dendl;
276 s->err.message = "context: can't make canonical";
277 return -ERR_INVALID_REQUEST;
278 }
279 b = sort_and_write(d, writer, ccs);
280 if (!b) {
281 ldout(s->cct, 5) << "format error <" << context
282 << ">: partial.results=" << buf.GetString() << dendl;
283 s->err.message = "unable to reformat json";
284 return -ERR_INVALID_REQUEST;
285 }
286 cooked_context = rgw::to_base64(buf.GetString());
287 return 0;
288 }
289
290
291 CryptoAccelRef get_crypto_accel(CephContext *cct)
292 {
293 CryptoAccelRef ca_impl = nullptr;
294 stringstream ss;
295 PluginRegistry *reg = cct->get_plugin_registry();
296 string crypto_accel_type = cct->_conf->plugin_crypto_accelerator;
297
298 CryptoPlugin *factory = dynamic_cast<CryptoPlugin*>(reg->get_with_load("crypto", crypto_accel_type));
299 if (factory == nullptr) {
300 lderr(cct) << __func__ << " cannot load crypto accelerator of type " << crypto_accel_type << dendl;
301 return nullptr;
302 }
303 int err = factory->factory(&ca_impl, &ss);
304 if (err) {
305 lderr(cct) << __func__ << " factory return error " << err <<
306 " with description: " << ss.str() << dendl;
307 }
308 return ca_impl;
309 }
310
311
312 template <std::size_t KeySizeV, std::size_t IvSizeV>
313 static inline
314 bool evp_sym_transform(CephContext* const cct,
315 const EVP_CIPHER* const type,
316 unsigned char* const out,
317 const unsigned char* const in,
318 const size_t size,
319 const unsigned char* const iv,
320 const unsigned char* const key,
321 const bool encrypt)
322 {
323 using pctx_t = \
324 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>;
325 pctx_t pctx{ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free };
326
327 if (!pctx) {
328 return false;
329 }
330
331 if (1 != EVP_CipherInit_ex(pctx.get(), type, nullptr,
332 nullptr, nullptr, encrypt)) {
333 ldout(cct, 5) << "EVP: failed to 1st initialization stage" << dendl;
334 return false;
335 }
336
337 // we want to support ciphers that don't use IV at all like AES-256-ECB
338 if constexpr (static_cast<bool>(IvSizeV)) {
339 ceph_assert(EVP_CIPHER_CTX_iv_length(pctx.get()) == IvSizeV);
340 ceph_assert(EVP_CIPHER_CTX_block_size(pctx.get()) == IvSizeV);
341 }
342 ceph_assert(EVP_CIPHER_CTX_key_length(pctx.get()) == KeySizeV);
343
344 if (1 != EVP_CipherInit_ex(pctx.get(), nullptr, nullptr, key, iv, encrypt)) {
345 ldout(cct, 5) << "EVP: failed to 2nd initialization stage" << dendl;
346 return false;
347 }
348
349 // disable padding
350 if (1 != EVP_CIPHER_CTX_set_padding(pctx.get(), 0)) {
351 ldout(cct, 5) << "EVP: cannot disable PKCS padding" << dendl;
352 return false;
353 }
354
355 // operate!
356 int written = 0;
357 ceph_assert(size <= static_cast<size_t>(std::numeric_limits<int>::max()));
358 if (1 != EVP_CipherUpdate(pctx.get(), out, &written, in, size)) {
359 ldout(cct, 5) << "EVP: EVP_CipherUpdate failed" << dendl;
360 return false;
361 }
362
363 int finally_written = 0;
364 static_assert(sizeof(*out) == 1);
365 if (1 != EVP_CipherFinal_ex(pctx.get(), out + written, &finally_written)) {
366 ldout(cct, 5) << "EVP: EVP_CipherFinal_ex failed" << dendl;
367 return false;
368 }
369
370 // padding is disabled so EVP_CipherFinal_ex should not append anything
371 ceph_assert(finally_written == 0);
372 return (written + finally_written) == static_cast<int>(size);
373 }
374
375
376 /**
377 * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block.
378 *
379 *
380 *
381 * A. Encryption
382 * 1. Input is split to 4K chunks + remainder in one, smaller chunk
383 * 2. Each full chunk is encrypted separately with CBC chained mode, with initial IV derived from offset
384 * 3. Last chunk is 16*m + n.
385 * 4. 16*m bytes are encrypted with CBC chained mode, with initial IV derived from offset
386 * 5. Last n bytes are xor-ed with pattern obtained by CBC encryption of
387 * last encrypted 16 byte block <16m-16, 16m-15) with IV = {0}.
388 * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern
389 * obtained by CBC encryption of {0} with IV derived from offset
390 *
391 * B. Decryption
392 * 1. Input is split to 4K chunks + remainder in one, smaller chunk
393 * 2. Each full chunk is decrypted separately with CBC chained mode, with initial IV derived from offset
394 * 3. Last chunk is 16*m + n.
395 * 4. 16*m bytes are decrypted with CBC chained mode, with initial IV derived from offset
396 * 5. Last n bytes are xor-ed with pattern obtained by CBC ENCRYPTION of
397 * last (still encrypted) 16 byte block <16m-16,16m-15) with IV = {0}
398 * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern
399 * obtained by CBC ENCRYPTION of {0} with IV derived from offset
400 */
401 class AES_256_CBC : public BlockCrypt {
402 public:
403 static const size_t AES_256_KEYSIZE = 256 / 8;
404 static const size_t AES_256_IVSIZE = 128 / 8;
405 static const size_t CHUNK_SIZE = 4096;
406 private:
407 static const uint8_t IV[AES_256_IVSIZE];
408 CephContext* cct;
409 uint8_t key[AES_256_KEYSIZE];
410 public:
411 explicit AES_256_CBC(CephContext* cct): cct(cct) {
412 }
413 ~AES_256_CBC() {
414 ::ceph::crypto::zeroize_for_security(key, AES_256_KEYSIZE);
415 }
416 bool set_key(const uint8_t* _key, size_t key_size) {
417 if (key_size != AES_256_KEYSIZE) {
418 return false;
419 }
420 memcpy(key, _key, AES_256_KEYSIZE);
421 return true;
422 }
423 size_t get_block_size() {
424 return CHUNK_SIZE;
425 }
426
427 bool cbc_transform(unsigned char* out,
428 const unsigned char* in,
429 const size_t size,
430 const unsigned char (&iv)[AES_256_IVSIZE],
431 const unsigned char (&key)[AES_256_KEYSIZE],
432 bool encrypt)
433 {
434 return evp_sym_transform<AES_256_KEYSIZE, AES_256_IVSIZE>(
435 cct, EVP_aes_256_cbc(), out, in, size, iv, key, encrypt);
436 }
437
438 bool cbc_transform(unsigned char* out,
439 const unsigned char* in,
440 size_t size,
441 off_t stream_offset,
442 const unsigned char (&key)[AES_256_KEYSIZE],
443 bool encrypt)
444 {
445 static std::atomic<bool> failed_to_get_crypto(false);
446 CryptoAccelRef crypto_accel;
447 if (! failed_to_get_crypto.load())
448 {
449 crypto_accel = get_crypto_accel(cct);
450 if (!crypto_accel)
451 failed_to_get_crypto = true;
452 }
453 bool result = true;
454 unsigned char iv[AES_256_IVSIZE];
455 for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) {
456 size_t process_size = offset + CHUNK_SIZE <= size ? CHUNK_SIZE : size - offset;
457 prepare_iv(iv, stream_offset + offset);
458 if (crypto_accel != nullptr) {
459 if (encrypt) {
460 result = crypto_accel->cbc_encrypt(out + offset, in + offset,
461 process_size, iv, key);
462 } else {
463 result = crypto_accel->cbc_decrypt(out + offset, in + offset,
464 process_size, iv, key);
465 }
466 } else {
467 result = cbc_transform(
468 out + offset, in + offset, process_size,
469 iv, key, encrypt);
470 }
471 }
472 return result;
473 }
474
475
476 bool encrypt(bufferlist& input,
477 off_t in_ofs,
478 size_t size,
479 bufferlist& output,
480 off_t stream_offset)
481 {
482 bool result = false;
483 size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE;
484 size_t unaligned_rest_size = size - aligned_size;
485 output.clear();
486 buffer::ptr buf(aligned_size + AES_256_IVSIZE);
487 unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str());
488 const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(input.c_str());
489
490 /* encrypt main bulk of data */
491 result = cbc_transform(buf_raw,
492 input_raw + in_ofs,
493 aligned_size,
494 stream_offset, key, true);
495 if (result && (unaligned_rest_size > 0)) {
496 /* remainder to encrypt */
497 if (aligned_size % CHUNK_SIZE > 0) {
498 /* use last chunk for unaligned part */
499 unsigned char iv[AES_256_IVSIZE] = {0};
500 result = cbc_transform(buf_raw + aligned_size,
501 buf_raw + aligned_size - AES_256_IVSIZE,
502 AES_256_IVSIZE,
503 iv, key, true);
504 } else {
505 /* 0 full blocks in current chunk, use IV as base for unaligned part */
506 unsigned char iv[AES_256_IVSIZE] = {0};
507 unsigned char data[AES_256_IVSIZE];
508 prepare_iv(data, stream_offset + aligned_size);
509 result = cbc_transform(buf_raw + aligned_size,
510 data,
511 AES_256_IVSIZE,
512 iv, key, true);
513 }
514 if (result) {
515 for(size_t i = aligned_size; i < size; i++) {
516 *(buf_raw + i) ^= *(input_raw + in_ofs + i);
517 }
518 }
519 }
520 if (result) {
521 ldout(cct, 25) << "Encrypted " << size << " bytes"<< dendl;
522 buf.set_length(size);
523 output.append(buf);
524 } else {
525 ldout(cct, 5) << "Failed to encrypt" << dendl;
526 }
527 return result;
528 }
529
530
531 bool decrypt(bufferlist& input,
532 off_t in_ofs,
533 size_t size,
534 bufferlist& output,
535 off_t stream_offset)
536 {
537 bool result = false;
538 size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE;
539 size_t unaligned_rest_size = size - aligned_size;
540 output.clear();
541 buffer::ptr buf(aligned_size + AES_256_IVSIZE);
542 unsigned char* buf_raw = reinterpret_cast<unsigned char*>(buf.c_str());
543 unsigned char* input_raw = reinterpret_cast<unsigned char*>(input.c_str());
544
545 /* decrypt main bulk of data */
546 result = cbc_transform(buf_raw,
547 input_raw + in_ofs,
548 aligned_size,
549 stream_offset, key, false);
550 if (result && unaligned_rest_size > 0) {
551 /* remainder to decrypt */
552 if (aligned_size % CHUNK_SIZE > 0) {
553 /*use last chunk for unaligned part*/
554 unsigned char iv[AES_256_IVSIZE] = {0};
555 result = cbc_transform(buf_raw + aligned_size,
556 input_raw + in_ofs + aligned_size - AES_256_IVSIZE,
557 AES_256_IVSIZE,
558 iv, key, true);
559 } else {
560 /* 0 full blocks in current chunk, use IV as base for unaligned part */
561 unsigned char iv[AES_256_IVSIZE] = {0};
562 unsigned char data[AES_256_IVSIZE];
563 prepare_iv(data, stream_offset + aligned_size);
564 result = cbc_transform(buf_raw + aligned_size,
565 data,
566 AES_256_IVSIZE,
567 iv, key, true);
568 }
569 if (result) {
570 for(size_t i = aligned_size; i < size; i++) {
571 *(buf_raw + i) ^= *(input_raw + in_ofs + i);
572 }
573 }
574 }
575 if (result) {
576 ldout(cct, 25) << "Decrypted " << size << " bytes"<< dendl;
577 buf.set_length(size);
578 output.append(buf);
579 } else {
580 ldout(cct, 5) << "Failed to decrypt" << dendl;
581 }
582 return result;
583 }
584
585
586 void prepare_iv(unsigned char (&iv)[AES_256_IVSIZE], off_t offset) {
587 off_t index = offset / AES_256_IVSIZE;
588 off_t i = AES_256_IVSIZE - 1;
589 unsigned int val;
590 unsigned int carry = 0;
591 while (i>=0) {
592 val = (index & 0xff) + IV[i] + carry;
593 iv[i] = val;
594 carry = val >> 8;
595 index = index >> 8;
596 i--;
597 }
598 }
599 };
600
601
602 std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len)
603 {
604 auto cbc = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(cct));
605 cbc->set_key(key, AES_256_KEYSIZE);
606 return cbc;
607 }
608
609
610 const uint8_t AES_256_CBC::IV[AES_256_CBC::AES_256_IVSIZE] =
611 { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' };
612
613
614 bool AES_256_ECB_encrypt(CephContext* cct,
615 const uint8_t* key,
616 size_t key_size,
617 const uint8_t* data_in,
618 uint8_t* data_out,
619 size_t data_size)
620 {
621 if (key_size == AES_256_KEYSIZE) {
622 return evp_sym_transform<AES_256_KEYSIZE, 0 /* no IV in ECB */>(
623 cct, EVP_aes_256_ecb(), data_out, data_in, data_size,
624 nullptr /* no IV in ECB */, key, true /* encrypt */);
625 } else {
626 ldout(cct, 5) << "Key size must be 256 bits long" << dendl;
627 return false;
628 }
629 }
630
631
632 RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext* cct,
633 RGWGetObj_Filter* next,
634 std::unique_ptr<BlockCrypt> crypt):
635 RGWGetObj_Filter(next),
636 cct(cct),
637 crypt(std::move(crypt)),
638 enc_begin_skip(0),
639 ofs(0),
640 end(0),
641 cache()
642 {
643 block_size = this->crypt->get_block_size();
644 }
645
646 RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {
647 }
648
649 int RGWGetObj_BlockDecrypt::read_manifest(const DoutPrefixProvider *dpp, bufferlist& manifest_bl) {
650 parts_len.clear();
651 RGWObjManifest manifest;
652 if (manifest_bl.length()) {
653 auto miter = manifest_bl.cbegin();
654 try {
655 decode(manifest, miter);
656 } catch (buffer::error& err) {
657 ldpp_dout(dpp, 0) << "ERROR: couldn't decode manifest" << dendl;
658 return -EIO;
659 }
660 RGWObjManifest::obj_iterator mi;
661 for (mi = manifest.obj_begin(dpp); mi != manifest.obj_end(dpp); ++mi) {
662 if (mi.get_cur_stripe() == 0) {
663 parts_len.push_back(0);
664 }
665 parts_len.back() += mi.get_stripe_size();
666 }
667 if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
668 for (size_t i = 0; i<parts_len.size(); i++) {
669 ldpp_dout(dpp, 20) << "Manifest part " << i << ", size=" << parts_len[i] << dendl;
670 }
671 }
672 }
673 return 0;
674 }
675
676 int RGWGetObj_BlockDecrypt::fixup_range(off_t& bl_ofs, off_t& bl_end) {
677 off_t inp_ofs = bl_ofs;
678 off_t inp_end = bl_end;
679 if (parts_len.size() > 0) {
680 off_t in_ofs = bl_ofs;
681 off_t in_end = bl_end;
682
683 size_t i = 0;
684 while (i<parts_len.size() && (in_ofs >= (off_t)parts_len[i])) {
685 in_ofs -= parts_len[i];
686 i++;
687 }
688 //in_ofs is inside block i
689 size_t j = 0;
690 while (j<(parts_len.size() - 1) && (in_end >= (off_t)parts_len[j])) {
691 in_end -= parts_len[j];
692 j++;
693 }
694 //in_end is inside part j, OR j is the last part
695
696 size_t rounded_end = ( in_end & ~(block_size - 1) ) + (block_size - 1);
697 if (rounded_end > parts_len[j]) {
698 rounded_end = parts_len[j] - 1;
699 }
700
701 enc_begin_skip = in_ofs & (block_size - 1);
702 ofs = bl_ofs - enc_begin_skip;
703 end = bl_end;
704 bl_end += rounded_end - in_end;
705 bl_ofs = std::min(bl_ofs - enc_begin_skip, bl_end);
706 }
707 else
708 {
709 enc_begin_skip = bl_ofs & (block_size - 1);
710 ofs = bl_ofs & ~(block_size - 1);
711 end = bl_end;
712 bl_ofs = bl_ofs & ~(block_size - 1);
713 bl_end = ( bl_end & ~(block_size - 1) ) + (block_size - 1);
714 }
715 ldout(cct, 20) << "fixup_range [" << inp_ofs << "," << inp_end
716 << "] => [" << bl_ofs << "," << bl_end << "]" << dendl;
717 return 0;
718 }
719
720 int RGWGetObj_BlockDecrypt::process(bufferlist& in, size_t part_ofs, size_t size)
721 {
722 bufferlist data;
723 if (!crypt->decrypt(in, 0, size, data, part_ofs)) {
724 return -ERR_INTERNAL_ERROR;
725 }
726 off_t send_size = size - enc_begin_skip;
727 if (ofs + enc_begin_skip + send_size > end + 1) {
728 send_size = end + 1 - ofs - enc_begin_skip;
729 }
730 int res = next->handle_data(data, enc_begin_skip, send_size);
731 enc_begin_skip = 0;
732 ofs += size;
733 in.splice(0, size);
734 return res;
735 }
736
737 int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
738 ldout(cct, 25) << "Decrypt " << bl_len << " bytes" << dendl;
739 bl.begin(bl_ofs).copy(bl_len, cache);
740
741 int res = 0;
742 size_t part_ofs = ofs;
743 for (size_t part : parts_len) {
744 if (part_ofs >= part) {
745 part_ofs -= part;
746 } else if (part_ofs + cache.length() >= part) {
747 // flush data up to part boundaries, aligned or not
748 res = process(cache, part_ofs, part - part_ofs);
749 if (res < 0) {
750 return res;
751 }
752 part_ofs = 0;
753 } else {
754 break;
755 }
756 }
757 // write up to block boundaries, aligned only
758 off_t aligned_size = cache.length() & ~(block_size - 1);
759 if (aligned_size > 0) {
760 res = process(cache, part_ofs, aligned_size);
761 }
762 return res;
763 }
764
765 /**
766 * flush remainder of data to output
767 */
768 int RGWGetObj_BlockDecrypt::flush() {
769 ldout(cct, 25) << "Decrypt flushing " << cache.length() << " bytes" << dendl;
770 int res = 0;
771 size_t part_ofs = ofs;
772 for (size_t part : parts_len) {
773 if (part_ofs >= part) {
774 part_ofs -= part;
775 } else if (part_ofs + cache.length() >= part) {
776 // flush data up to part boundaries, aligned or not
777 res = process(cache, part_ofs, part - part_ofs);
778 if (res < 0) {
779 return res;
780 }
781 part_ofs = 0;
782 } else {
783 break;
784 }
785 }
786 // flush up to block boundaries, aligned or not
787 if (cache.length() > 0) {
788 res = process(cache, part_ofs, cache.length());
789 }
790 return res;
791 }
792
793 RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext* cct,
794 rgw::putobj::DataProcessor *next,
795 std::unique_ptr<BlockCrypt> crypt)
796 : Pipe(next),
797 cct(cct),
798 crypt(std::move(crypt)),
799 block_size(this->crypt->get_block_size())
800 {
801 }
802
803 int RGWPutObj_BlockEncrypt::process(bufferlist&& data, uint64_t logical_offset)
804 {
805 ldout(cct, 25) << "Encrypt " << data.length() << " bytes" << dendl;
806
807 // adjust logical offset to beginning of cached data
808 ceph_assert(logical_offset >= cache.length());
809 logical_offset -= cache.length();
810
811 const bool flush = (data.length() == 0);
812 cache.claim_append(data);
813
814 uint64_t proc_size = cache.length() & ~(block_size - 1);
815 if (flush) {
816 proc_size = cache.length();
817 }
818 if (proc_size > 0) {
819 bufferlist in, out;
820 cache.splice(0, proc_size, &in);
821 if (!crypt->encrypt(in, 0, proc_size, out, logical_offset)) {
822 return -ERR_INTERNAL_ERROR;
823 }
824 int r = Pipe::process(std::move(out), logical_offset);
825 logical_offset += proc_size;
826 if (r < 0)
827 return r;
828 }
829
830 if (flush) {
831 /*replicate 0-sized handle_data*/
832 return Pipe::process({}, logical_offset);
833 }
834 return 0;
835 }
836
837
838 std::string create_random_key_selector(CephContext * const cct) {
839 char random[AES_256_KEYSIZE];
840 cct->random()->get_bytes(&random[0], sizeof(random));
841 return std::string(random, sizeof(random));
842 }
843
844 typedef enum {
845 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM=0,
846 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
847 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
848 X_AMZ_SERVER_SIDE_ENCRYPTION,
849 X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID,
850 X_AMZ_SERVER_SIDE_ENCRYPTION_CONTEXT,
851 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
852 } crypt_option_e;
853
854 typedef struct {
855 const char* http_header_name;
856 const std::string post_part_name;
857 } crypt_option_names;
858
859 static const crypt_option_names crypt_options[] = {
860 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", "x-amz-server-side-encryption-customer-algorithm"},
861 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "x-amz-server-side-encryption-customer-key"},
862 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "x-amz-server-side-encryption-customer-key-md5"},
863 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", "x-amz-server-side-encryption"},
864 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID", "x-amz-server-side-encryption-aws-kms-key-id"},
865 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CONTEXT", "x-amz-server-side-encryption-context"},
866 };
867
868 static std::string_view get_crypt_attribute(
869 const RGWEnv* env,
870 std::map<std::string,
871 RGWPostObj_ObjStore::post_form_part,
872 const ltstr_nocase>* parts,
873 crypt_option_e option)
874 {
875 static_assert(
876 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST == sizeof(crypt_options)/sizeof(*crypt_options),
877 "Missing items in crypt_options");
878 if (parts != nullptr) {
879 auto iter
880 = parts->find(crypt_options[option].post_part_name);
881 if (iter == parts->end())
882 return std::string_view();
883 bufferlist& data = iter->second.data;
884 std::string_view str = std::string_view(data.c_str(), data.length());
885 return rgw_trim_whitespace(str);
886 } else {
887 const char* hdr = env->get(crypt_options[option].http_header_name, nullptr);
888 if (hdr != nullptr) {
889 return std::string_view(hdr);
890 } else {
891 return std::string_view();
892 }
893 }
894 }
895
896
897 int rgw_s3_prepare_encrypt(struct req_state* s,
898 std::map<std::string, ceph::bufferlist>& attrs,
899 std::map<std::string,
900 RGWPostObj_ObjStore::post_form_part,
901 const ltstr_nocase>* parts,
902 std::unique_ptr<BlockCrypt>* block_crypt,
903 std::map<std::string, std::string>& crypt_http_responses)
904 {
905 int res = 0;
906 crypt_http_responses.clear();
907 {
908 std::string_view req_sse_ca =
909 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM);
910 if (! req_sse_ca.empty()) {
911 if (req_sse_ca != "AES256") {
912 ldpp_dout(s, 5) << "ERROR: Invalid value for header "
913 << "x-amz-server-side-encryption-customer-algorithm"
914 << dendl;
915 s->err.message = "The requested encryption algorithm is not valid, must be AES256.";
916 return -ERR_INVALID_ENCRYPTION_ALGORITHM;
917 }
918 if (s->cct->_conf->rgw_crypt_require_ssl &&
919 !rgw_transport_is_secure(s->cct, *s->info.env)) {
920 ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
921 return -ERR_INVALID_REQUEST;
922 }
923
924 std::string key_bin;
925 try {
926 key_bin = from_base64(
927 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY) );
928 } catch (...) {
929 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption "
930 << "key which contains character that is not base64 encoded."
931 << dendl;
932 s->err.message = "Requests specifying Server Side Encryption with Customer "
933 "provided keys must provide an appropriate secret key.";
934 return -EINVAL;
935 }
936
937 if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) {
938 ldpp_dout(s, 5) << "ERROR: invalid encryption key size" << dendl;
939 s->err.message = "Requests specifying Server Side Encryption with Customer "
940 "provided keys must provide an appropriate secret key.";
941 return -EINVAL;
942 }
943
944 std::string_view keymd5 =
945 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5);
946
947 std::string keymd5_bin;
948 try {
949 keymd5_bin = from_base64(keymd5);
950 } catch (...) {
951 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key "
952 << "md5 which contains character that is not base64 encoded."
953 << dendl;
954 s->err.message = "Requests specifying Server Side Encryption with Customer "
955 "provided keys must provide an appropriate secret key md5.";
956 return -EINVAL;
957 }
958
959 if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) {
960 ldpp_dout(s, 5) << "ERROR: Invalid key md5 size" << dendl;
961 s->err.message = "Requests specifying Server Side Encryption with Customer "
962 "provided keys must provide an appropriate secret key md5.";
963 return -EINVAL;
964 }
965
966 MD5 key_hash;
967 unsigned char key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
968 key_hash.Update(reinterpret_cast<const unsigned char*>(key_bin.c_str()), key_bin.size());
969 key_hash.Final(key_hash_res);
970
971 if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) {
972 ldpp_dout(s, 5) << "ERROR: Invalid key md5 hash" << dendl;
973 s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided.";
974 return -EINVAL;
975 }
976
977 set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256");
978 set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin);
979
980 if (block_crypt) {
981 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
982 aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_KEYSIZE);
983 *block_crypt = std::move(aes);
984 }
985
986 crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
987 crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = std::string(keymd5);
988 return 0;
989 } else {
990 std::string_view customer_key =
991 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY);
992 if (!customer_key.empty()) {
993 ldpp_dout(s, 5) << "ERROR: SSE-C encryption request is missing the header "
994 << "x-amz-server-side-encryption-customer-algorithm"
995 << dendl;
996 s->err.message = "Requests specifying Server Side Encryption with Customer "
997 "provided keys must provide a valid encryption algorithm.";
998 return -EINVAL;
999 }
1000
1001 std::string_view customer_key_md5 =
1002 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5);
1003 if (!customer_key_md5.empty()) {
1004 ldpp_dout(s, 5) << "ERROR: SSE-C encryption request is missing the header "
1005 << "x-amz-server-side-encryption-customer-algorithm"
1006 << dendl;
1007 s->err.message = "Requests specifying Server Side Encryption with Customer "
1008 "provided keys must provide a valid encryption algorithm.";
1009 return -EINVAL;
1010 }
1011 }
1012
1013 /* AMAZON server side encryption with KMS (key management service) */
1014 std::string_view req_sse =
1015 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION);
1016 if (! req_sse.empty()) {
1017
1018 if (s->cct->_conf->rgw_crypt_require_ssl &&
1019 !rgw_transport_is_secure(s->cct, *s->info.env)) {
1020 ldpp_dout(s, 5) << "ERROR: insecure request, rgw_crypt_require_ssl is set" << dendl;
1021 return -ERR_INVALID_REQUEST;
1022 }
1023
1024 if (req_sse == "aws:kms") {
1025 std::string_view context =
1026 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CONTEXT);
1027 std::string cooked_context;
1028 if ((res = make_canonical_context(s, context, cooked_context)))
1029 return res;
1030 std::string_view key_id =
1031 get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID);
1032 if (key_id.empty()) {
1033 ldpp_dout(s, 5) << "ERROR: not provide a valid key id" << dendl;
1034 s->err.message = "Server Side Encryption with KMS managed key requires "
1035 "HTTP header x-amz-server-side-encryption-aws-kms-key-id";
1036 return -ERR_INVALID_ACCESS_KEY;
1037 }
1038 /* try to retrieve actual key */
1039 std::string key_selector = create_random_key_selector(s->cct);
1040 set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS");
1041 set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id);
1042 set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
1043 set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, cooked_context);
1044 std::string actual_key;
1045 res = make_actual_key_from_kms(s->cct, attrs, actual_key);
1046 if (res != 0) {
1047 ldpp_dout(s, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl;
1048 s->err.message = "Failed to retrieve the actual key, kms-keyid: " + std::string(key_id);
1049 return res;
1050 }
1051 if (actual_key.size() != AES_256_KEYSIZE) {
1052 ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" <<
1053 key_id << " is not 256 bit size" << dendl;
1054 s->err.message = "KMS provided an invalid key for the given kms-keyid.";
1055 return -ERR_INVALID_ACCESS_KEY;
1056 }
1057
1058 if (block_crypt) {
1059 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
1060 aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
1061 *block_crypt = std::move(aes);
1062 }
1063 ::ceph::crypto::zeroize_for_security(actual_key.data(), actual_key.length());
1064
1065 crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms";
1066 crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = std::string(key_id);
1067 crypt_http_responses["x-amz-server-side-encryption-context"] = std::move(cooked_context);
1068 return 0;
1069 } else if (req_sse == "AES256") {
1070 /* if a default encryption key was provided, we will use it for SSE-S3 */
1071 } else {
1072 ldpp_dout(s, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption"
1073 << dendl;
1074 s->err.message = "Server Side Encryption with KMS managed key requires "
1075 "HTTP header x-amz-server-side-encryption : aws:kms or AES256";
1076 return -EINVAL;
1077 }
1078 } else {
1079 /* x-amz-server-side-encryption not present or empty */
1080 std::string_view key_id =
1081 get_crypt_attribute(s->info.env, parts,
1082 X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID);
1083 if (!key_id.empty()) {
1084 ldpp_dout(s, 5) << "ERROR: SSE-KMS encryption request is missing the header "
1085 << "x-amz-server-side-encryption"
1086 << dendl;
1087 s->err.message = "Server Side Encryption with KMS managed key requires "
1088 "HTTP header x-amz-server-side-encryption : aws:kms";
1089 return -EINVAL;
1090 }
1091 }
1092
1093 /* no other encryption mode, check if default encryption is selected */
1094 if (s->cct->_conf->rgw_crypt_default_encryption_key != "") {
1095 std::string master_encryption_key;
1096 try {
1097 master_encryption_key = from_base64(s->cct->_conf->rgw_crypt_default_encryption_key);
1098 } catch (...) {
1099 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key "
1100 << "which contains character that is not base64 encoded."
1101 << dendl;
1102 s->err.message = "Requests specifying Server Side Encryption with Customer "
1103 "provided keys must provide an appropriate secret key.";
1104 return -EINVAL;
1105 }
1106
1107 if (master_encryption_key.size() != 256 / 8) {
1108 ldpp_dout(s, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl;
1109 /* not an error to return; missing encryption does not inhibit processing */
1110 return 0;
1111 }
1112
1113 set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO");
1114 std::string key_selector = create_random_key_selector(s->cct);
1115 set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
1116
1117 uint8_t actual_key[AES_256_KEYSIZE];
1118 if (AES_256_ECB_encrypt(s->cct,
1119 reinterpret_cast<const uint8_t*>(master_encryption_key.c_str()), AES_256_KEYSIZE,
1120 reinterpret_cast<const uint8_t*>(key_selector.c_str()),
1121 actual_key, AES_256_KEYSIZE) != true) {
1122 ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
1123 return -EIO;
1124 }
1125 if (block_crypt) {
1126 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
1127 aes->set_key(reinterpret_cast<const uint8_t*>(actual_key), AES_256_KEYSIZE);
1128 *block_crypt = std::move(aes);
1129 }
1130 ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
1131 return 0;
1132 }
1133 }
1134 /*no encryption*/
1135 return 0;
1136 }
1137
1138
1139 int rgw_s3_prepare_decrypt(struct req_state* s,
1140 map<string, bufferlist>& attrs,
1141 std::unique_ptr<BlockCrypt>* block_crypt,
1142 std::map<std::string, std::string>& crypt_http_responses)
1143 {
1144 int res = 0;
1145 std::string stored_mode = get_str_attribute(attrs, RGW_ATTR_CRYPT_MODE);
1146 ldpp_dout(s, 15) << "Encryption mode: " << stored_mode << dendl;
1147
1148 const char *req_sse = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", NULL);
1149 if (nullptr != req_sse && (s->op == OP_GET || s->op == OP_HEAD)) {
1150 return -ERR_INVALID_REQUEST;
1151 }
1152
1153 if (stored_mode == "SSE-C-AES256") {
1154 if (s->cct->_conf->rgw_crypt_require_ssl &&
1155 !rgw_transport_is_secure(s->cct, *s->info.env)) {
1156 ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
1157 return -ERR_INVALID_REQUEST;
1158 }
1159 const char *req_cust_alg =
1160 s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL);
1161
1162 if (nullptr == req_cust_alg) {
1163 ldpp_dout(s, 5) << "ERROR: Request for SSE-C encrypted object missing "
1164 << "x-amz-server-side-encryption-customer-algorithm"
1165 << dendl;
1166 s->err.message = "Requests specifying Server Side Encryption with Customer "
1167 "provided keys must provide a valid encryption algorithm.";
1168 return -EINVAL;
1169 } else if (strcmp(req_cust_alg, "AES256") != 0) {
1170 ldpp_dout(s, 5) << "ERROR: The requested encryption algorithm is not valid, must be AES256." << dendl;
1171 s->err.message = "The requested encryption algorithm is not valid, must be AES256.";
1172 return -ERR_INVALID_ENCRYPTION_ALGORITHM;
1173 }
1174
1175 std::string key_bin;
1176 try {
1177 key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""));
1178 } catch (...) {
1179 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key "
1180 << "which contains character that is not base64 encoded."
1181 << dendl;
1182 s->err.message = "Requests specifying Server Side Encryption with Customer "
1183 "provided keys must provide an appropriate secret key.";
1184 return -EINVAL;
1185 }
1186
1187 if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) {
1188 ldpp_dout(s, 5) << "ERROR: Invalid encryption key size" << dendl;
1189 s->err.message = "Requests specifying Server Side Encryption with Customer "
1190 "provided keys must provide an appropriate secret key.";
1191 return -EINVAL;
1192 }
1193
1194 std::string keymd5 =
1195 s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "");
1196 std::string keymd5_bin;
1197 try {
1198 keymd5_bin = from_base64(keymd5);
1199 } catch (...) {
1200 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 "
1201 << "which contains character that is not base64 encoded."
1202 << dendl;
1203 s->err.message = "Requests specifying Server Side Encryption with Customer "
1204 "provided keys must provide an appropriate secret key md5.";
1205 return -EINVAL;
1206 }
1207
1208
1209 if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) {
1210 ldpp_dout(s, 5) << "ERROR: Invalid key md5 size " << dendl;
1211 s->err.message = "Requests specifying Server Side Encryption with Customer "
1212 "provided keys must provide an appropriate secret key md5.";
1213 return -EINVAL;
1214 }
1215
1216 MD5 key_hash;
1217 uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
1218 key_hash.Update(reinterpret_cast<const unsigned char*>(key_bin.c_str()), key_bin.size());
1219 key_hash.Final(key_hash_res);
1220
1221 if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) ||
1222 (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) {
1223 s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided.";
1224 return -EINVAL;
1225 }
1226 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
1227 aes->set_key(reinterpret_cast<const uint8_t*>(key_bin.c_str()), AES_256_CBC::AES_256_KEYSIZE);
1228 if (block_crypt) *block_crypt = std::move(aes);
1229
1230 crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
1231 crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5;
1232 return 0;
1233 }
1234
1235 if (stored_mode == "SSE-KMS") {
1236 if (s->cct->_conf->rgw_crypt_require_ssl &&
1237 !rgw_transport_is_secure(s->cct, *s->info.env)) {
1238 ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
1239 return -ERR_INVALID_REQUEST;
1240 }
1241 /* try to retrieve actual key */
1242 std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID);
1243 std::string actual_key;
1244 res = reconstitute_actual_key_from_kms(s->cct, attrs, actual_key);
1245 if (res != 0) {
1246 ldpp_dout(s, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl;
1247 s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id;
1248 return res;
1249 }
1250 if (actual_key.size() != AES_256_KEYSIZE) {
1251 ldpp_dout(s, 0) << "ERROR: key obtained from key_id:" <<
1252 key_id << " is not 256 bit size" << dendl;
1253 s->err.message = "KMS provided an invalid key for the given kms-keyid.";
1254 return -ERR_INVALID_ACCESS_KEY;
1255 }
1256
1257 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
1258 aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
1259 actual_key.replace(0, actual_key.length(), actual_key.length(), '\000');
1260 if (block_crypt) *block_crypt = std::move(aes);
1261
1262 crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms";
1263 crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id;
1264 return 0;
1265 }
1266
1267 if (stored_mode == "RGW-AUTO") {
1268 std::string master_encryption_key;
1269 try {
1270 master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key));
1271 } catch (...) {
1272 ldpp_dout(s, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key "
1273 << "which contains character that is not base64 encoded."
1274 << dendl;
1275 s->err.message = "The default encryption key is not valid base64.";
1276 return -EINVAL;
1277 }
1278
1279 if (master_encryption_key.size() != 256 / 8) {
1280 ldpp_dout(s, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl;
1281 return -EIO;
1282 }
1283 std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL);
1284 if (attr_key_selector.size() != AES_256_CBC::AES_256_KEYSIZE) {
1285 ldpp_dout(s, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl;
1286 return -EIO;
1287 }
1288 uint8_t actual_key[AES_256_KEYSIZE];
1289 if (AES_256_ECB_encrypt(s->cct,
1290 reinterpret_cast<const uint8_t*>(master_encryption_key.c_str()),
1291 AES_256_KEYSIZE,
1292 reinterpret_cast<const uint8_t*>(attr_key_selector.c_str()),
1293 actual_key, AES_256_KEYSIZE) != true) {
1294 ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
1295 return -EIO;
1296 }
1297 auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
1298 aes->set_key(actual_key, AES_256_KEYSIZE);
1299 ::ceph::crypto::zeroize_for_security(actual_key, sizeof(actual_key));
1300 if (block_crypt) *block_crypt = std::move(aes);
1301 return 0;
1302 }
1303 /*no decryption*/
1304 return 0;
1305 }
1306
1307 /*********************************************************************
1308 * "BOTTOM OF FILE"
1309 * I've left some commented out lines above. They are there for
1310 * a reason, which I will explain. The "canonical" json constructed
1311 * by the code above as a crypto context must take a json object and
1312 * turn it into a unique determinstic fixed form. For most json
1313 * types this is easy. The hardest problem that is handled above is
1314 * detailing with unicode strings; they must be turned into
1315 * NFC form and sorted in a fixed order. Numbers, however,
1316 * are another story. Json makes no distinction between integers
1317 * and floating point, and both types have their problems.
1318 * Integers can overflow, so very large numbers are a problem.
1319 * Floating point is even worse; not all floating point numbers
1320 * can be represented accurately in c++ data types, and there
1321 * are many quirks regarding how overflow, underflow, and loss
1322 * of significance are handled.
1323 *
1324 * In this version of the code, I took the simplest answer, I
1325 * reject all numbers altogether. This is not ideal, but it's
1326 * the only choice that is guaranteed to be future compatible.
1327 * AWS S3 does not guarantee to support numbers at all; but it
1328 * actually converts all numbers into strings right off.
1329 * This has the interesting property that 7 and 007 are different,
1330 * but that 007 and "007" are the same. I would rather
1331 * treat numbers as a string of digits and have logic
1332 * to produce the "most compact" equivalent form. This can
1333 * fix all the overflow/underflow problems, but it requires
1334 * fixing the json parser part, and I put that problem off.
1335 *
1336 * The commented code above indicates places in this code that
1337 * will need to be revised depending on future work in this area.
1338 * Removing those comments makes that work harder.
1339 * February 25, 2021
1340 *********************************************************************/