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 const auto &o
{d
.GetObject()};
114 auto b
{o
.begin()},e
{o
.end()};
115 std::vector
<MyMember
*> q
;
116 for (auto &m
: d
.GetObject())
118 std::sort(q
.begin(), q
.end(), cmp_functor
);
120 assert(m
->name
.IsString());
121 if (!(r
= writer
.Key(m
->name
.GetString(), m
->name
.GetStringLength())))
123 if (!(r
= sort_and_write(m
->value
, writer
, ccs
)))
126 r
= writer
.EndObject();
128 case rapidjson::kArrayType
:
129 if (!(r
= writer
.StartArray()))
131 for (auto &v
: d
.GetArray()) {
132 if (!(r
= sort_and_write(v
, writer
, ccs
)))
135 r
= writer
.EndArray();
138 r
= d
.Accept(writer
);
145 enum struct mec_option
{
146 empty
= 0, number_ok
= 1
149 enum struct mec_error
{
150 success
= 0, conversion
, number
154 make_everything_canonical(rapidjson::Value
&d
, rapidjson::Document::AllocatorType
&a
, canonical_char_sorter
<MyMember
>& ccs
, mec_option f
= mec_option::empty
)
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
;
165 if ((r
= make_everything_canonical(m
.value
, a
, ccs
, f
)) != mec_error::success
)
169 case rapidjson::kArrayType
:
170 for (auto &v
: d
.GetArray()) {
171 if ((r
= make_everything_canonical(v
, a
, ccs
, f
)) != mec_error::success
)
175 case rapidjson::kStringType
:
176 if (!ccs
.make_string_canonical(d
, a
)) {
177 r
= mec_error::conversion
;
181 case rapidjson::kNumberType
:
182 if (static_cast<int>(f
) & static_cast<int>(mec_option::number_ok
))
184 r
= mec_error::number
;
189 r
= mec_error::success
;
195 add_object_to_context(rgw_obj
&obj
, rapidjson::Document
&d
)
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
;
205 if (d
.HasMember(aws_s3_arn
))
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
);
213 static inline const std::string
&
214 get_tenant_or_id(req_state
*s
)
216 const std::string
&tenant
{ s
->user
->get_tenant() };
217 if (!tenant
.empty()) return tenant
;
218 return s
->user
->get_id().id
;
222 make_canonical_context(struct req_state
*s
,
223 std::string_view
&context
,
224 std::string
&cooked_context
)
226 rapidjson::Document d
;
229 //mec_option::number_ok : SEE BOTTOM OF FILE
232 std::ostringstream oss
;
233 canonical_char_sorter
<MyMember
> ccs
{s
->cct
};
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();
239 rapidjson::Document::AllocatorType
&allocator
{ d
.GetAllocator() };
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
;
248 rapidjson::StringStream
isw(iline
.c_str());
251 // else if (qflag) SEE BOTTOM OF FILE
252 // d.ParseStream<rapidjson::kParseNumbersAsStringsFlag>(isw);
254 d
.ParseStream
<rapidjson::kParseFullPrecisionFlag
>(isw
);
255 if (isw
.Tell() != iline
.length()) {
256 oss
<< "bad context: did not consume all of input: @ "
258 s
->err
.message
= oss
.str();
259 return -ERR_INVALID_REQUEST
;
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
;
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
;
274 b
= make_everything_canonical(d
, allocator
, ccs
, options
) == mec_error::success
;
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
;
281 b
= sort_and_write(d
, writer
, ccs
);
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
;
288 cooked_context
= rgw::to_base64(buf
.GetString());
293 CryptoAccelRef
get_crypto_accel(CephContext
*cct
)
295 CryptoAccelRef ca_impl
= nullptr;
297 PluginRegistry
*reg
= cct
->get_plugin_registry();
298 string crypto_accel_type
= cct
->_conf
->plugin_crypto_accelerator
;
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
;
305 int err
= factory
->factory(&ca_impl
, &ss
);
307 lderr(cct
) << __func__
<< " factory return error " << err
<<
308 " with description: " << ss
.str() << dendl
;
314 template <std::size_t KeySizeV
, std::size_t IvSizeV
>
316 bool evp_sym_transform(CephContext
* const cct
,
317 const EVP_CIPHER
* const type
,
318 unsigned char* const out
,
319 const unsigned char* const in
,
321 const unsigned char* const iv
,
322 const unsigned char* const key
,
326 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)>;
327 pctx_t pctx
{ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free
};
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
;
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
);
344 ceph_assert(EVP_CIPHER_CTX_key_length(pctx
.get()) == KeySizeV
);
346 if (1 != EVP_CipherInit_ex(pctx
.get(), nullptr, nullptr, key
, iv
, encrypt
)) {
347 ldout(cct
, 5) << "EVP: failed to 2nd initialization stage" << dendl
;
352 if (1 != EVP_CIPHER_CTX_set_padding(pctx
.get(), 0)) {
353 ldout(cct
, 5) << "EVP: cannot disable PKCS padding" << dendl
;
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
;
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
;
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
);
379 * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block.
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
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
403 class AES_256_CBC
: public BlockCrypt
{
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;
409 static const uint8_t IV
[AES_256_IVSIZE
];
411 uint8_t key
[AES_256_KEYSIZE
];
413 explicit AES_256_CBC(CephContext
* cct
): cct(cct
) {
416 ::ceph::crypto::zeroize_for_security(key
, AES_256_KEYSIZE
);
418 bool set_key(const uint8_t* _key
, size_t key_size
) {
419 if (key_size
!= AES_256_KEYSIZE
) {
422 memcpy(key
, _key
, AES_256_KEYSIZE
);
425 size_t get_block_size() {
429 bool cbc_transform(unsigned char* out
,
430 const unsigned char* in
,
432 const unsigned char (&iv
)[AES_256_IVSIZE
],
433 const unsigned char (&key
)[AES_256_KEYSIZE
],
436 return evp_sym_transform
<AES_256_KEYSIZE
, AES_256_IVSIZE
>(
437 cct
, EVP_aes_256_cbc(), out
, in
, size
, iv
, key
, encrypt
);
440 bool cbc_transform(unsigned char* out
,
441 const unsigned char* in
,
444 const unsigned char (&key
)[AES_256_KEYSIZE
],
447 static std::atomic
<bool> failed_to_get_crypto(false);
448 CryptoAccelRef crypto_accel
;
449 if (! failed_to_get_crypto
.load())
451 crypto_accel
= get_crypto_accel(cct
);
453 failed_to_get_crypto
= 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) {
462 result
= crypto_accel
->cbc_encrypt(out
+ offset
, in
+ offset
,
463 process_size
, iv
, key
);
465 result
= crypto_accel
->cbc_decrypt(out
+ offset
, in
+ offset
,
466 process_size
, iv
, key
);
469 result
= cbc_transform(
470 out
+ offset
, in
+ offset
, process_size
,
478 bool encrypt(bufferlist
& input
,
485 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
486 size_t unaligned_rest_size
= size
- aligned_size
;
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());
492 /* encrypt main bulk of data */
493 result
= cbc_transform(buf_raw
,
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
,
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
,
517 for(size_t i
= aligned_size
; i
< size
; i
++) {
518 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
523 ldout(cct
, 25) << "Encrypted " << size
<< " bytes"<< dendl
;
524 buf
.set_length(size
);
527 ldout(cct
, 5) << "Failed to encrypt" << dendl
;
533 bool decrypt(bufferlist
& input
,
540 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
541 size_t unaligned_rest_size
= size
- aligned_size
;
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());
547 /* decrypt main bulk of data */
548 result
= cbc_transform(buf_raw
,
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
,
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
,
572 for(size_t i
= aligned_size
; i
< size
; i
++) {
573 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
578 ldout(cct
, 25) << "Decrypted " << size
<< " bytes"<< dendl
;
579 buf
.set_length(size
);
582 ldout(cct
, 5) << "Failed to decrypt" << dendl
;
588 void prepare_iv(unsigned char (&iv
)[AES_256_IVSIZE
], off_t offset
) {
589 off_t index
= offset
/ AES_256_IVSIZE
;
590 off_t i
= AES_256_IVSIZE
- 1;
592 unsigned int carry
= 0;
594 val
= (index
& 0xff) + IV
[i
] + carry
;
604 std::unique_ptr
<BlockCrypt
> AES_256_CBC_create(CephContext
* cct
, const uint8_t* key
, size_t len
)
606 auto cbc
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(cct
));
607 cbc
->set_key(key
, AES_256_KEYSIZE
);
612 const 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' };
616 bool AES_256_ECB_encrypt(CephContext
* cct
,
619 const uint8_t* data_in
,
623 if (key_size
== AES_256_KEYSIZE
) {
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 */);
628 ldout(cct
, 5) << "Key size must be 256 bits long" << dendl
;
634 RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext
* cct
,
635 RGWGetObj_Filter
* next
,
636 std::unique_ptr
<BlockCrypt
> crypt
):
637 RGWGetObj_Filter(next
),
639 crypt(std::move(crypt
)),
645 block_size
= this->crypt
->get_block_size();
648 RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {
651 int RGWGetObj_BlockDecrypt::read_manifest(const DoutPrefixProvider
*dpp
, bufferlist
& manifest_bl
) {
653 RGWObjManifest manifest
;
654 if (manifest_bl
.length()) {
655 auto miter
= manifest_bl
.cbegin();
657 decode(manifest
, miter
);
658 } catch (buffer::error
& err
) {
659 ldpp_dout(dpp
, 0) << "ERROR: couldn't decode manifest" << dendl
;
662 RGWObjManifest::obj_iterator mi
;
663 for (mi
= manifest
.obj_begin(dpp
); mi
!= manifest
.obj_end(dpp
); ++mi
) {
664 if (mi
.get_cur_stripe() == 0) {
665 parts_len
.push_back(0);
667 parts_len
.back() += mi
.get_stripe_size();
669 if (cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 20>()) {
670 for (size_t i
= 0; i
<parts_len
.size(); i
++) {
671 ldpp_dout(dpp
, 20) << "Manifest part " << i
<< ", size=" << parts_len
[i
] << dendl
;
678 int 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
;
686 while (i
<parts_len
.size() && (in_ofs
>= (off_t
)parts_len
[i
])) {
687 in_ofs
-= parts_len
[i
];
690 //in_ofs is inside block i
692 while (j
<(parts_len
.size() - 1) && (in_end
>= (off_t
)parts_len
[j
])) {
693 in_end
-= parts_len
[j
];
696 //in_end is inside part j, OR j is the last part
698 size_t rounded_end
= ( in_end
& ~(block_size
- 1) ) + (block_size
- 1);
699 if (rounded_end
> parts_len
[j
]) {
700 rounded_end
= parts_len
[j
] - 1;
703 enc_begin_skip
= in_ofs
& (block_size
- 1);
704 ofs
= bl_ofs
- enc_begin_skip
;
706 bl_end
+= rounded_end
- in_end
;
707 bl_ofs
= std::min(bl_ofs
- enc_begin_skip
, bl_end
);
711 enc_begin_skip
= bl_ofs
& (block_size
- 1);
712 ofs
= bl_ofs
& ~(block_size
- 1);
714 bl_ofs
= bl_ofs
& ~(block_size
- 1);
715 bl_end
= ( bl_end
& ~(block_size
- 1) ) + (block_size
- 1);
717 ldout(cct
, 20) << "fixup_range [" << inp_ofs
<< "," << inp_end
718 << "] => [" << bl_ofs
<< "," << bl_end
<< "]" << dendl
;
722 int RGWGetObj_BlockDecrypt::process(bufferlist
& in
, size_t part_ofs
, size_t size
)
725 if (!crypt
->decrypt(in
, 0, size
, data
, part_ofs
)) {
726 return -ERR_INTERNAL_ERROR
;
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
;
732 int res
= next
->handle_data(data
, enc_begin_skip
, send_size
);
739 int RGWGetObj_BlockDecrypt::handle_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
740 ldout(cct
, 25) << "Decrypt " << bl_len
<< " bytes" << dendl
;
741 bl
.begin(bl_ofs
).copy(bl_len
, cache
);
744 size_t part_ofs
= ofs
;
745 for (size_t part
: parts_len
) {
746 if (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
);
759 // write up to block boundaries, aligned only
760 off_t aligned_size
= cache
.length() & ~(block_size
- 1);
761 if (aligned_size
> 0) {
762 res
= process(cache
, part_ofs
, aligned_size
);
768 * flush remainder of data to output
770 int RGWGetObj_BlockDecrypt::flush() {
771 ldout(cct
, 25) << "Decrypt flushing " << cache
.length() << " bytes" << dendl
;
773 size_t part_ofs
= ofs
;
774 for (size_t part
: parts_len
) {
775 if (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
);
788 // flush up to block boundaries, aligned or not
789 if (cache
.length() > 0) {
790 res
= process(cache
, part_ofs
, cache
.length());
795 RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext
* cct
,
796 rgw::putobj::DataProcessor
*next
,
797 std::unique_ptr
<BlockCrypt
> crypt
)
800 crypt(std::move(crypt
)),
801 block_size(this->crypt
->get_block_size())
805 int RGWPutObj_BlockEncrypt::process(bufferlist
&& data
, uint64_t logical_offset
)
807 ldout(cct
, 25) << "Encrypt " << data
.length() << " bytes" << dendl
;
809 // adjust logical offset to beginning of cached data
810 ceph_assert(logical_offset
>= cache
.length());
811 logical_offset
-= cache
.length();
813 const bool flush
= (data
.length() == 0);
814 cache
.claim_append(data
);
816 uint64_t proc_size
= cache
.length() & ~(block_size
- 1);
818 proc_size
= cache
.length();
822 cache
.splice(0, proc_size
, &in
);
823 if (!crypt
->encrypt(in
, 0, proc_size
, out
, logical_offset
)) {
824 return -ERR_INTERNAL_ERROR
;
826 int r
= Pipe::process(std::move(out
), logical_offset
);
827 logical_offset
+= proc_size
;
833 /*replicate 0-sized handle_data*/
834 return Pipe::process({}, logical_offset
);
840 std::string
create_random_key_selector(CephContext
* const cct
) {
841 char random
[AES_256_KEYSIZE
];
842 cct
->random()->get_bytes(&random
[0], sizeof(random
));
843 return std::string(random
, sizeof(random
));
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
,
852 X_AMZ_SERVER_SIDE_ENCRYPTION_CONTEXT
,
853 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
857 const char* http_header_name
;
858 const std::string post_part_name
;
859 } crypt_option_names
;
861 static 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"},
867 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CONTEXT", "x-amz-server-side-encryption-context"},
870 static std::string_view
get_crypt_attribute(
872 std::map
<std::string
,
873 RGWPostObj_ObjStore::post_form_part
,
874 const ltstr_nocase
>* parts
,
875 crypt_option_e option
)
878 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
== sizeof(crypt_options
)/sizeof(*crypt_options
),
879 "Missing items in crypt_options");
880 if (parts
!= nullptr) {
882 = parts
->find(crypt_options
[option
].post_part_name
);
883 if (iter
== parts
->end())
884 return std::string_view();
885 bufferlist
& data
= iter
->second
.data
;
886 std::string_view str
= std::string_view(data
.c_str(), data
.length());
887 return rgw_trim_whitespace(str
);
889 const char* hdr
= env
->get(crypt_options
[option
].http_header_name
, nullptr);
890 if (hdr
!= nullptr) {
891 return std::string_view(hdr
);
893 return std::string_view();
899 int 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
)
908 crypt_http_responses
.clear();
910 std::string_view req_sse_ca
=
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") {
914 ldpp_dout(s
, 5) << "ERROR: Invalid value for header "
915 << "x-amz-server-side-encryption-customer-algorithm"
917 s
->err
.message
= "The requested encryption algorithm is not valid, must be AES256.";
918 return -ERR_INVALID_ENCRYPTION_ALGORITHM
;
920 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
921 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
922 ldpp_dout(s
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
923 return -ERR_INVALID_REQUEST
;
928 key_bin
= from_base64(
929 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
) );
931 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption "
932 << "key which contains character that is not base64 encoded."
934 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
935 "provided keys must provide an appropriate secret key.";
939 if (key_bin
.size() != AES_256_CBC::AES_256_KEYSIZE
) {
940 ldpp_dout(s
, 5) << "ERROR: invalid encryption key size" << dendl
;
941 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
942 "provided keys must provide an appropriate secret key.";
946 std::string_view keymd5
=
947 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
);
949 std::string keymd5_bin
;
951 keymd5_bin
= from_base64(keymd5
);
953 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key "
954 << "md5 which contains character that is not base64 encoded."
956 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
957 "provided keys must provide an appropriate secret key md5.";
961 if (keymd5_bin
.size() != CEPH_CRYPTO_MD5_DIGESTSIZE
) {
962 ldpp_dout(s
, 5) << "ERROR: Invalid key md5 size" << dendl
;
963 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
964 "provided keys must provide an appropriate secret key md5.";
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());
971 key_hash
.Final(key_hash_res
);
973 if (memcmp(key_hash_res
, keymd5_bin
.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE
) != 0) {
974 ldpp_dout(s
, 5) << "ERROR: Invalid key md5 hash" << dendl
;
975 s
->err
.message
= "The calculated MD5 hash of the key did not match the hash that was provided.";
979 set_attr(attrs
, RGW_ATTR_CRYPT_MODE
, "SSE-C-AES256");
980 set_attr(attrs
, RGW_ATTR_CRYPT_KEYMD5
, keymd5_bin
);
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
);
988 crypt_http_responses
["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
989 crypt_http_responses
["x-amz-server-side-encryption-customer-key-MD5"] = std::string(keymd5
);
992 std::string_view customer_key
=
993 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
);
994 if (!customer_key
.empty()) {
995 ldpp_dout(s
, 5) << "ERROR: SSE-C encryption request is missing the header "
996 << "x-amz-server-side-encryption-customer-algorithm"
998 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
999 "provided keys must provide a valid encryption algorithm.";
1003 std::string_view customer_key_md5
=
1004 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
);
1005 if (!customer_key_md5
.empty()) {
1006 ldpp_dout(s
, 5) << "ERROR: SSE-C encryption request is missing the header "
1007 << "x-amz-server-side-encryption-customer-algorithm"
1009 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1010 "provided keys must provide a valid encryption algorithm.";
1015 /* AMAZON server side encryption with KMS (key management service) */
1016 std::string_view req_sse
=
1017 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION
);
1018 if (! req_sse
.empty()) {
1020 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
1021 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
1022 ldpp_dout(s
, 5) << "ERROR: insecure request, rgw_crypt_require_ssl is set" << dendl
;
1023 return -ERR_INVALID_REQUEST
;
1026 if (req_sse
== "aws:kms") {
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
)))
1032 std::string_view key_id
=
1033 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
);
1034 if (key_id
.empty()) {
1035 ldpp_dout(s
, 5) << "ERROR: not provide a valid key id" << dendl
;
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
;
1040 /* try to retrieve actual key */
1041 std::string key_selector
= create_random_key_selector(s
->cct
);
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
);
1046 std::string actual_key
;
1047 res
= make_actual_key_from_kms(s
->cct
, attrs
, actual_key
);
1049 ldpp_dout(s
, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id
<< dendl
;
1050 s
->err
.message
= "Failed to retrieve the actual key, kms-keyid: " + std::string(key_id
);
1053 if (actual_key
.size() != AES_256_KEYSIZE
) {
1054 ldpp_dout(s
, 5) << "ERROR: key obtained from key_id:" <<
1055 key_id
<< " is not 256 bit size" << dendl
;
1056 s
->err
.message
= "KMS provided an invalid key for the given kms-keyid.";
1057 return -ERR_INVALID_ACCESS_KEY
;
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
);
1065 ::ceph::crypto::zeroize_for_security(actual_key
.data(), actual_key
.length());
1067 crypt_http_responses
["x-amz-server-side-encryption"] = "aws:kms";
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
);
1071 } else if (req_sse
== "AES256") {
1072 /* if a default encryption key was provided, we will use it for SSE-S3 */
1074 ldpp_dout(s
, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption"
1076 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
1077 "HTTP header x-amz-server-side-encryption : aws:kms or AES256";
1081 /* x-amz-server-side-encryption not present or empty */
1082 std::string_view key_id
=
1083 get_crypt_attribute(s
->info
.env
, parts
,
1084 X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
);
1085 if (!key_id
.empty()) {
1086 ldpp_dout(s
, 5) << "ERROR: SSE-KMS encryption request is missing the header "
1087 << "x-amz-server-side-encryption"
1089 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
1090 "HTTP header x-amz-server-side-encryption : aws:kms";
1095 /* no other encryption mode, check if default encryption is selected */
1096 if (s
->cct
->_conf
->rgw_crypt_default_encryption_key
!= "") {
1097 std::string master_encryption_key
;
1099 master_encryption_key
= from_base64(s
->cct
->_conf
->rgw_crypt_default_encryption_key
);
1101 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key "
1102 << "which contains character that is not base64 encoded."
1104 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1105 "provided keys must provide an appropriate secret key.";
1109 if (master_encryption_key
.size() != 256 / 8) {
1110 ldpp_dout(s
, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl
;
1111 /* not an error to return; missing encryption does not inhibit processing */
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
);
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) {
1124 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
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
);
1132 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
1141 int 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
)
1147 std::string stored_mode
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_MODE
);
1148 ldpp_dout(s
, 15) << "Encryption mode: " << stored_mode
<< dendl
;
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
;
1155 if (stored_mode
== "SSE-C-AES256") {
1156 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
1157 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
1158 ldpp_dout(s
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
1159 return -ERR_INVALID_REQUEST
;
1161 const char *req_cust_alg
=
1162 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL
);
1164 if (nullptr == req_cust_alg
) {
1165 ldpp_dout(s
, 5) << "ERROR: Request for SSE-C encrypted object missing "
1166 << "x-amz-server-side-encryption-customer-algorithm"
1168 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1169 "provided keys must provide a valid encryption algorithm.";
1171 } else if (strcmp(req_cust_alg
, "AES256") != 0) {
1172 ldpp_dout(s
, 5) << "ERROR: The requested encryption algorithm is not valid, must be AES256." << dendl
;
1173 s
->err
.message
= "The requested encryption algorithm is not valid, must be AES256.";
1174 return -ERR_INVALID_ENCRYPTION_ALGORITHM
;
1177 std::string key_bin
;
1179 key_bin
= from_base64(s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""));
1181 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key "
1182 << "which contains character that is not base64 encoded."
1184 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1185 "provided keys must provide an appropriate secret key.";
1189 if (key_bin
.size() != AES_256_CBC::AES_256_KEYSIZE
) {
1190 ldpp_dout(s
, 5) << "ERROR: Invalid encryption key size" << dendl
;
1191 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1192 "provided keys must provide an appropriate secret key.";
1196 std::string keymd5
=
1197 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "");
1198 std::string keymd5_bin
;
1200 keymd5_bin
= from_base64(keymd5
);
1202 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 "
1203 << "which contains character that is not base64 encoded."
1205 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1206 "provided keys must provide an appropriate secret key md5.";
1211 if (keymd5_bin
.size() != CEPH_CRYPTO_MD5_DIGESTSIZE
) {
1212 ldpp_dout(s
, 5) << "ERROR: Invalid key md5 size " << dendl
;
1213 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
1214 "provided keys must provide an appropriate secret key md5.";
1219 uint8_t key_hash_res
[CEPH_CRYPTO_MD5_DIGESTSIZE
];
1220 key_hash
.Update(reinterpret_cast<const unsigned char*>(key_bin
.c_str()), key_bin
.size());
1221 key_hash
.Final(key_hash_res
);
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
)) {
1225 s
->err
.message
= "The calculated MD5 hash of the key did not match the hash that was provided.";
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
);
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
;
1237 if (stored_mode
== "SSE-KMS") {
1238 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
1239 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
1240 ldpp_dout(s
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
1241 return -ERR_INVALID_REQUEST
;
1243 /* try to retrieve actual key */
1244 std::string key_id
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_KEYID
);
1245 std::string actual_key
;
1246 res
= reconstitute_actual_key_from_kms(s
->cct
, attrs
, actual_key
);
1248 ldpp_dout(s
, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id
<< dendl
;
1249 s
->err
.message
= "Failed to retrieve the actual key, kms-keyid: " + key_id
;
1252 if (actual_key
.size() != AES_256_KEYSIZE
) {
1253 ldpp_dout(s
, 0) << "ERROR: key obtained from key_id:" <<
1254 key_id
<< " is not 256 bit size" << dendl
;
1255 s
->err
.message
= "KMS provided an invalid key for the given kms-keyid.";
1256 return -ERR_INVALID_ACCESS_KEY
;
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
);
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
;
1269 if (stored_mode
== "RGW-AUTO") {
1270 std::string master_encryption_key
;
1272 master_encryption_key
= from_base64(std::string(s
->cct
->_conf
->rgw_crypt_default_encryption_key
));
1274 ldpp_dout(s
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key "
1275 << "which contains character that is not base64 encoded."
1277 s
->err
.message
= "The default encryption key is not valid base64.";
1281 if (master_encryption_key
.size() != 256 / 8) {
1282 ldpp_dout(s
, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl
;
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
) {
1287 ldpp_dout(s
, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL
<< dendl
;
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()),
1294 reinterpret_cast<const uint8_t*>(attr_key_selector
.c_str()),
1295 actual_key
, AES_256_KEYSIZE
) != true) {
1296 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
1299 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
1300 aes
->set_key(actual_key
, AES_256_KEYSIZE
);
1301 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
1302 if (block_crypt
) *block_crypt
= std::move(aes
);
1309 /*********************************************************************
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.
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.
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.
1342 *********************************************************************/