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