1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
5 * Crypto filters for Put/Post/Get operations.
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
25 #include <openssl/evp.h>
27 #define dout_context g_ceph_context
28 #define dout_subsys ceph_subsys_rgw
34 class canonical_char_sorter
{
36 const icu::Normalizer2
* normalizer
;
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
;
48 bool compare_helper (const M
*, const M
*);
49 bool make_string_canonical(rapidjson::Value
&,
50 rapidjson::Document::AllocatorType
&);
55 canonical_char_sorter
<M
>::compare_helper (const M
*a
, const M
*b
)
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
);
73 canonical_char_sorter
<M
>::make_string_canonical (rapidjson::Value
&v
, rapidjson::Document::AllocatorType
&a
)
75 UErrorCode status
= U_ZERO_ERROR
;
76 const std::string as
{v
.GetString(), v
.GetStringLength()};
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
;
89 v
.SetString(ans
.c_str(), ans
.length(), a
);
94 rapidjson::GenericMember
<rapidjson::UTF8
<>, rapidjson::MemoryPoolAllocator
<> >
99 sort_and_write(rapidjson::Value
&d
, H
&writer
, canonical_char_sorter
<MyMember
>& ccs
)
102 switch(d
.GetType()) {
103 case rapidjson::kObjectType
: {
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
);
111 if (!(r
= writer
.StartObject()))
113 std::vector
<MyMember
*> q
;
114 for (auto &m
: d
.GetObject())
116 std::sort(q
.begin(), q
.end(), cmp_functor
);
118 assert(m
->name
.IsString());
119 if (!(r
= writer
.Key(m
->name
.GetString(), m
->name
.GetStringLength())))
121 if (!(r
= sort_and_write(m
->value
, writer
, ccs
)))
124 r
= writer
.EndObject();
126 case rapidjson::kArrayType
:
127 if (!(r
= writer
.StartArray()))
129 for (auto &v
: d
.GetArray()) {
130 if (!(r
= sort_and_write(v
, writer
, ccs
)))
133 r
= writer
.EndArray();
136 r
= d
.Accept(writer
);
143 enum struct mec_option
{
144 empty
= 0, number_ok
= 1
147 enum struct mec_error
{
148 success
= 0, conversion
, number
152 make_everything_canonical(rapidjson::Value
&d
, rapidjson::Document::AllocatorType
&a
, canonical_char_sorter
<MyMember
>& ccs
, mec_option f
= mec_option::empty
)
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
;
163 if ((r
= make_everything_canonical(m
.value
, a
, ccs
, f
)) != mec_error::success
)
167 case rapidjson::kArrayType
:
168 for (auto &v
: d
.GetArray()) {
169 if ((r
= make_everything_canonical(v
, a
, ccs
, f
)) != mec_error::success
)
173 case rapidjson::kStringType
:
174 if (!ccs
.make_string_canonical(d
, a
)) {
175 r
= mec_error::conversion
;
179 case rapidjson::kNumberType
:
180 if (static_cast<int>(f
) & static_cast<int>(mec_option::number_ok
))
182 r
= mec_error::number
;
187 r
= mec_error::success
;
193 add_object_to_context(rgw_obj
&obj
, rapidjson::Document
&d
)
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
;
203 if (d
.HasMember(aws_s3_arn
))
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
);
211 static inline const std::string
&
212 get_tenant_or_id(req_state
*s
)
214 const std::string
&tenant
{ s
->user
->get_tenant() };
215 if (!tenant
.empty()) return tenant
;
216 return s
->user
->get_id().id
;
220 make_canonical_context(struct req_state
*s
,
221 std::string_view
&context
,
222 std::string
&cooked_context
)
224 rapidjson::Document d
;
227 //mec_option::number_ok : SEE BOTTOM OF FILE
230 std::ostringstream oss
;
231 canonical_char_sorter
<MyMember
> ccs
{s
->cct
};
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();
237 rapidjson::Document::AllocatorType
&allocator
{ d
.GetAllocator() };
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
;
246 rapidjson::StringStream
isw(iline
.c_str());
249 // else if (qflag) SEE BOTTOM OF FILE
250 // d.ParseStream<rapidjson::kParseNumbersAsStringsFlag>(isw);
252 d
.ParseStream
<rapidjson::kParseFullPrecisionFlag
>(isw
);
253 if (isw
.Tell() != iline
.length()) {
254 oss
<< "bad context: did not consume all of input: @ "
256 s
->err
.message
= oss
.str();
257 return -ERR_INVALID_REQUEST
;
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
;
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
;
272 b
= make_everything_canonical(d
, allocator
, ccs
, options
) == mec_error::success
;
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
;
279 b
= sort_and_write(d
, writer
, ccs
);
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
;
286 cooked_context
= rgw::to_base64(buf
.GetString());
291 CryptoAccelRef
get_crypto_accel(CephContext
*cct
)
293 CryptoAccelRef ca_impl
= nullptr;
295 PluginRegistry
*reg
= cct
->get_plugin_registry();
296 string crypto_accel_type
= cct
->_conf
->plugin_crypto_accelerator
;
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
;
303 int err
= factory
->factory(&ca_impl
, &ss
);
305 lderr(cct
) << __func__
<< " factory return error " << err
<<
306 " with description: " << ss
.str() << dendl
;
312 template <std::size_t KeySizeV
, std::size_t IvSizeV
>
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
,
319 const unsigned char* const iv
,
320 const unsigned char* const key
,
324 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)>;
325 pctx_t pctx
{ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free
};
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
;
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
);
342 ceph_assert(EVP_CIPHER_CTX_key_length(pctx
.get()) == KeySizeV
);
344 if (1 != EVP_CipherInit_ex(pctx
.get(), nullptr, nullptr, key
, iv
, encrypt
)) {
345 ldout(cct
, 5) << "EVP: failed to 2nd initialization stage" << dendl
;
350 if (1 != EVP_CIPHER_CTX_set_padding(pctx
.get(), 0)) {
351 ldout(cct
, 5) << "EVP: cannot disable PKCS padding" << dendl
;
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
;
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
;
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
);
377 * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block.
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
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
401 class AES_256_CBC
: public BlockCrypt
{
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;
407 static const uint8_t IV
[AES_256_IVSIZE
];
409 uint8_t key
[AES_256_KEYSIZE
];
411 explicit AES_256_CBC(CephContext
* cct
): cct(cct
) {
414 ::ceph::crypto::zeroize_for_security(key
, AES_256_KEYSIZE
);
416 bool set_key(const uint8_t* _key
, size_t key_size
) {
417 if (key_size
!= AES_256_KEYSIZE
) {
420 memcpy(key
, _key
, AES_256_KEYSIZE
);
423 size_t get_block_size() {
427 bool cbc_transform(unsigned char* out
,
428 const unsigned char* in
,
430 const unsigned char (&iv
)[AES_256_IVSIZE
],
431 const unsigned char (&key
)[AES_256_KEYSIZE
],
434 return evp_sym_transform
<AES_256_KEYSIZE
, AES_256_IVSIZE
>(
435 cct
, EVP_aes_256_cbc(), out
, in
, size
, iv
, key
, encrypt
);
438 bool cbc_transform(unsigned char* out
,
439 const unsigned char* in
,
442 const unsigned char (&key
)[AES_256_KEYSIZE
],
445 static std::atomic
<bool> failed_to_get_crypto(false);
446 CryptoAccelRef crypto_accel
;
447 if (! failed_to_get_crypto
.load())
449 crypto_accel
= get_crypto_accel(cct
);
451 failed_to_get_crypto
= 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) {
460 result
= crypto_accel
->cbc_encrypt(out
+ offset
, in
+ offset
,
461 process_size
, iv
, key
);
463 result
= crypto_accel
->cbc_decrypt(out
+ offset
, in
+ offset
,
464 process_size
, iv
, key
);
467 result
= cbc_transform(
468 out
+ offset
, in
+ offset
, process_size
,
476 bool encrypt(bufferlist
& input
,
483 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
484 size_t unaligned_rest_size
= size
- aligned_size
;
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());
490 /* encrypt main bulk of data */
491 result
= cbc_transform(buf_raw
,
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
,
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
,
515 for(size_t i
= aligned_size
; i
< size
; i
++) {
516 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
521 ldout(cct
, 25) << "Encrypted " << size
<< " bytes"<< dendl
;
522 buf
.set_length(size
);
525 ldout(cct
, 5) << "Failed to encrypt" << dendl
;
531 bool decrypt(bufferlist
& input
,
538 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
539 size_t unaligned_rest_size
= size
- aligned_size
;
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());
545 /* decrypt main bulk of data */
546 result
= cbc_transform(buf_raw
,
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
,
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
,
570 for(size_t i
= aligned_size
; i
< size
; i
++) {
571 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
576 ldout(cct
, 25) << "Decrypted " << size
<< " bytes"<< dendl
;
577 buf
.set_length(size
);
580 ldout(cct
, 5) << "Failed to decrypt" << dendl
;
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;
590 unsigned int carry
= 0;
592 val
= (index
& 0xff) + IV
[i
] + carry
;
602 std::unique_ptr
<BlockCrypt
> AES_256_CBC_create(CephContext
* cct
, const uint8_t* key
, size_t len
)
604 auto cbc
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(cct
));
605 cbc
->set_key(key
, AES_256_KEYSIZE
);
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' };
614 bool AES_256_ECB_encrypt(CephContext
* cct
,
617 const uint8_t* data_in
,
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 */);
626 ldout(cct
, 5) << "Key size must be 256 bits long" << dendl
;
632 RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext
* cct
,
633 RGWGetObj_Filter
* next
,
634 std::unique_ptr
<BlockCrypt
> crypt
):
635 RGWGetObj_Filter(next
),
637 crypt(std::move(crypt
)),
643 block_size
= this->crypt
->get_block_size();
646 RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {
649 int RGWGetObj_BlockDecrypt::read_manifest(const DoutPrefixProvider
*dpp
, bufferlist
& manifest_bl
) {
651 RGWObjManifest manifest
;
652 if (manifest_bl
.length()) {
653 auto miter
= manifest_bl
.cbegin();
655 decode(manifest
, miter
);
656 } catch (buffer::error
& err
) {
657 ldpp_dout(dpp
, 0) << "ERROR: couldn't decode manifest" << dendl
;
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);
665 parts_len
.back() += mi
.get_stripe_size();
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
;
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
;
684 while (i
<parts_len
.size() && (in_ofs
>= (off_t
)parts_len
[i
])) {
685 in_ofs
-= parts_len
[i
];
688 //in_ofs is inside block i
690 while (j
<(parts_len
.size() - 1) && (in_end
>= (off_t
)parts_len
[j
])) {
691 in_end
-= parts_len
[j
];
694 //in_end is inside part j, OR j is the last part
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;
701 enc_begin_skip
= in_ofs
& (block_size
- 1);
702 ofs
= bl_ofs
- enc_begin_skip
;
704 bl_end
+= rounded_end
- in_end
;
705 bl_ofs
= std::min(bl_ofs
- enc_begin_skip
, bl_end
);
709 enc_begin_skip
= bl_ofs
& (block_size
- 1);
710 ofs
= bl_ofs
& ~(block_size
- 1);
712 bl_ofs
= bl_ofs
& ~(block_size
- 1);
713 bl_end
= ( bl_end
& ~(block_size
- 1) ) + (block_size
- 1);
715 ldout(cct
, 20) << "fixup_range [" << inp_ofs
<< "," << inp_end
716 << "] => [" << bl_ofs
<< "," << bl_end
<< "]" << dendl
;
720 int RGWGetObj_BlockDecrypt::process(bufferlist
& in
, size_t part_ofs
, size_t size
)
723 if (!crypt
->decrypt(in
, 0, size
, data
, part_ofs
)) {
724 return -ERR_INTERNAL_ERROR
;
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
;
730 int res
= next
->handle_data(data
, enc_begin_skip
, send_size
);
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
);
742 size_t part_ofs
= ofs
;
743 for (size_t part
: parts_len
) {
744 if (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
);
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
);
766 * flush remainder of data to output
768 int RGWGetObj_BlockDecrypt::flush() {
769 ldout(cct
, 25) << "Decrypt flushing " << cache
.length() << " bytes" << dendl
;
771 size_t part_ofs
= ofs
;
772 for (size_t part
: parts_len
) {
773 if (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
);
786 // flush up to block boundaries, aligned or not
787 if (cache
.length() > 0) {
788 res
= process(cache
, part_ofs
, cache
.length());
793 RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext
* cct
,
794 rgw::putobj::DataProcessor
*next
,
795 std::unique_ptr
<BlockCrypt
> crypt
)
798 crypt(std::move(crypt
)),
799 block_size(this->crypt
->get_block_size())
803 int RGWPutObj_BlockEncrypt::process(bufferlist
&& data
, uint64_t logical_offset
)
805 ldout(cct
, 25) << "Encrypt " << data
.length() << " bytes" << dendl
;
807 // adjust logical offset to beginning of cached data
808 ceph_assert(logical_offset
>= cache
.length());
809 logical_offset
-= cache
.length();
811 const bool flush
= (data
.length() == 0);
812 cache
.claim_append(data
);
814 uint64_t proc_size
= cache
.length() & ~(block_size
- 1);
816 proc_size
= cache
.length();
820 cache
.splice(0, proc_size
, &in
);
821 if (!crypt
->encrypt(in
, 0, proc_size
, out
, logical_offset
)) {
822 return -ERR_INTERNAL_ERROR
;
824 int r
= Pipe::process(std::move(out
), logical_offset
);
825 logical_offset
+= proc_size
;
831 /*replicate 0-sized handle_data*/
832 return Pipe::process({}, logical_offset
);
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
));
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
855 const char* http_header_name
;
856 const std::string post_part_name
;
857 } crypt_option_names
;
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"},
868 static std::string_view
get_crypt_attribute(
870 std::map
<std::string
,
871 RGWPostObj_ObjStore::post_form_part
,
872 const ltstr_nocase
>* parts
,
873 crypt_option_e option
)
876 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
== sizeof(crypt_options
)/sizeof(*crypt_options
),
877 "Missing items in crypt_options");
878 if (parts
!= nullptr) {
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
);
887 const char* hdr
= env
->get(crypt_options
[option
].http_header_name
, nullptr);
888 if (hdr
!= nullptr) {
889 return std::string_view(hdr
);
891 return std::string_view();
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
)
906 crypt_http_responses
.clear();
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"
915 s
->err
.message
= "The requested encryption algorithm is not valid, must be AES256.";
916 return -ERR_INVALID_ENCRYPTION_ALGORITHM
;
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
;
926 key_bin
= from_base64(
927 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
) );
929 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption "
930 << "key which contains character that is not base64 encoded."
932 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
933 "provided keys must provide an appropriate secret key.";
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.";
944 std::string_view keymd5
=
945 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
);
947 std::string keymd5_bin
;
949 keymd5_bin
= from_base64(keymd5
);
951 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key "
952 << "md5 which contains character that is not base64 encoded."
954 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
955 "provided keys must provide an appropriate secret key md5.";
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.";
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
);
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.";
977 set_attr(attrs
, RGW_ATTR_CRYPT_MODE
, "SSE-C-AES256");
978 set_attr(attrs
, RGW_ATTR_CRYPT_KEYMD5
, keymd5_bin
);
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
);
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
);
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"
996 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
997 "provided keys must provide a valid encryption algorithm.";
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"
1007 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1008 "provided keys must provide a valid encryption algorithm.";
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()) {
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
;
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
)))
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
;
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
);
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
);
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
;
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
);
1063 ::ceph::crypto::zeroize_for_security(actual_key
.data(), actual_key
.length());
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
);
1069 } else if (req_sse
== "AES256") {
1070 /* if a default encryption key was provided, we will use it for SSE-S3 */
1072 ldpp_dout(s
, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption"
1074 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
1075 "HTTP header x-amz-server-side-encryption : aws:kms or AES256";
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"
1087 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
1088 "HTTP header x-amz-server-side-encryption : aws:kms";
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
;
1097 master_encryption_key
= from_base64(s
->cct
->_conf
->rgw_crypt_default_encryption_key
);
1099 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key "
1100 << "which contains character that is not base64 encoded."
1102 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1103 "provided keys must provide an appropriate secret key.";
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 */
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
);
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
));
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
);
1130 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
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
)
1145 std::string stored_mode
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_MODE
);
1146 ldpp_dout(s
, 15) << "Encryption mode: " << stored_mode
<< dendl
;
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
;
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
;
1159 const char *req_cust_alg
=
1160 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL
);
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"
1166 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1167 "provided keys must provide a valid encryption algorithm.";
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
;
1175 std::string key_bin
;
1177 key_bin
= from_base64(s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""));
1179 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key "
1180 << "which contains character that is not base64 encoded."
1182 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1183 "provided keys must provide an appropriate secret key.";
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.";
1194 std::string keymd5
=
1195 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "");
1196 std::string keymd5_bin
;
1198 keymd5_bin
= from_base64(keymd5
);
1200 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 "
1201 << "which contains character that is not base64 encoded."
1203 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1204 "provided keys must provide an appropriate secret key md5.";
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.";
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
);
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.";
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
);
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
;
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
;
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
);
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
;
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
;
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
);
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
;
1267 if (stored_mode
== "RGW-AUTO") {
1268 std::string master_encryption_key
;
1270 master_encryption_key
= from_base64(std::string(s
->cct
->_conf
->rgw_crypt_default_encryption_key
));
1272 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key "
1273 << "which contains character that is not base64 encoded."
1275 s
->err
.message
= "The default encryption key is not valid base64.";
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
;
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
;
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()),
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
));
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
);
1307 /*********************************************************************
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.
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.
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.
1340 *********************************************************************/