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.
8 #include <rgw/rgw_op.h>
9 #include <rgw/rgw_crypt.h>
10 #include <auth/Crypto.h>
11 #include <rgw/rgw_b64.h>
12 #include <rgw/rgw_rest_s3.h>
13 #include "include/ceph_assert.h"
14 #include <boost/utility/string_view.hpp>
15 #include "crypto/crypto_accel.h"
16 #include "crypto/crypto_plugin.h"
17 #include "rgw/rgw_kms.h"
19 #include <openssl/evp.h>
21 #define dout_context g_ceph_context
22 #define dout_subsys ceph_subsys_rgw
27 CryptoAccelRef
get_crypto_accel(CephContext
*cct
)
29 CryptoAccelRef ca_impl
= nullptr;
31 PluginRegistry
*reg
= cct
->get_plugin_registry();
32 string crypto_accel_type
= cct
->_conf
->plugin_crypto_accelerator
;
34 CryptoPlugin
*factory
= dynamic_cast<CryptoPlugin
*>(reg
->get_with_load("crypto", crypto_accel_type
));
35 if (factory
== nullptr) {
36 lderr(cct
) << __func__
<< " cannot load crypto accelerator of type " << crypto_accel_type
<< dendl
;
39 int err
= factory
->factory(&ca_impl
, &ss
);
41 lderr(cct
) << __func__
<< " factory return error " << err
<<
42 " with description: " << ss
.str() << dendl
;
48 template <std::size_t KeySizeV
, std::size_t IvSizeV
>
50 bool evp_sym_transform(CephContext
* const cct
,
51 const EVP_CIPHER
* const type
,
52 unsigned char* const out
,
53 const unsigned char* const in
,
55 const unsigned char* const iv
,
56 const unsigned char* const key
,
60 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)>;
61 pctx_t pctx
{ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free
};
67 if (1 != EVP_CipherInit_ex(pctx
.get(), type
, nullptr,
68 nullptr, nullptr, encrypt
)) {
69 ldout(cct
, 5) << "EVP: failed to 1st initialization stage" << dendl
;
73 // we want to support ciphers that don't use IV at all like AES-256-ECB
74 if constexpr (static_cast<bool>(IvSizeV
)) {
75 ceph_assert(EVP_CIPHER_CTX_iv_length(pctx
.get()) == IvSizeV
);
76 ceph_assert(EVP_CIPHER_CTX_block_size(pctx
.get()) == IvSizeV
);
78 ceph_assert(EVP_CIPHER_CTX_key_length(pctx
.get()) == KeySizeV
);
80 if (1 != EVP_CipherInit_ex(pctx
.get(), nullptr, nullptr, key
, iv
, encrypt
)) {
81 ldout(cct
, 5) << "EVP: failed to 2nd initialization stage" << dendl
;
86 if (1 != EVP_CIPHER_CTX_set_padding(pctx
.get(), 0)) {
87 ldout(cct
, 5) << "EVP: cannot disable PKCS padding" << dendl
;
93 ceph_assert(size
<= static_cast<size_t>(std::numeric_limits
<int>::max()));
94 if (1 != EVP_CipherUpdate(pctx
.get(), out
, &written
, in
, size
)) {
95 ldout(cct
, 5) << "EVP: EVP_CipherUpdate failed" << dendl
;
99 int finally_written
= 0;
100 static_assert(sizeof(*out
) == 1);
101 if (1 != EVP_CipherFinal_ex(pctx
.get(), out
+ written
, &finally_written
)) {
102 ldout(cct
, 5) << "EVP: EVP_CipherFinal_ex failed" << dendl
;
106 // padding is disabled so EVP_CipherFinal_ex should not append anything
107 ceph_assert(finally_written
== 0);
108 return (written
+ finally_written
) == static_cast<int>(size
);
113 * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block.
118 * 1. Input is split to 4K chunks + remainder in one, smaller chunk
119 * 2. Each full chunk is encrypted separately with CBC chained mode, with initial IV derived from offset
120 * 3. Last chunk is 16*m + n.
121 * 4. 16*m bytes are encrypted with CBC chained mode, with initial IV derived from offset
122 * 5. Last n bytes are xor-ed with pattern obtained by CBC encryption of
123 * last encrypted 16 byte block <16m-16, 16m-15) with IV = {0}.
124 * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern
125 * obtained by CBC encryption of {0} with IV derived from offset
128 * 1. Input is split to 4K chunks + remainder in one, smaller chunk
129 * 2. Each full chunk is decrypted separately with CBC chained mode, with initial IV derived from offset
130 * 3. Last chunk is 16*m + n.
131 * 4. 16*m bytes are decrypted with CBC chained mode, with initial IV derived from offset
132 * 5. Last n bytes are xor-ed with pattern obtained by CBC ENCRYPTION of
133 * last (still encrypted) 16 byte block <16m-16,16m-15) with IV = {0}
134 * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern
135 * obtained by CBC ENCRYPTION of {0} with IV derived from offset
137 class AES_256_CBC
: public BlockCrypt
{
139 static const size_t AES_256_KEYSIZE
= 256 / 8;
140 static const size_t AES_256_IVSIZE
= 128 / 8;
141 static const size_t CHUNK_SIZE
= 4096;
143 static const uint8_t IV
[AES_256_IVSIZE
];
145 uint8_t key
[AES_256_KEYSIZE
];
147 explicit AES_256_CBC(CephContext
* cct
): cct(cct
) {
150 ::ceph::crypto::zeroize_for_security(key
, AES_256_KEYSIZE
);
152 bool set_key(const uint8_t* _key
, size_t key_size
) {
153 if (key_size
!= AES_256_KEYSIZE
) {
156 memcpy(key
, _key
, AES_256_KEYSIZE
);
159 size_t get_block_size() {
163 bool cbc_transform(unsigned char* out
,
164 const unsigned char* in
,
166 const unsigned char (&iv
)[AES_256_IVSIZE
],
167 const unsigned char (&key
)[AES_256_KEYSIZE
],
170 return evp_sym_transform
<AES_256_KEYSIZE
, AES_256_IVSIZE
>(
171 cct
, EVP_aes_256_cbc(), out
, in
, size
, iv
, key
, encrypt
);
174 bool cbc_transform(unsigned char* out
,
175 const unsigned char* in
,
178 const unsigned char (&key
)[AES_256_KEYSIZE
],
181 static std::atomic
<bool> failed_to_get_crypto(false);
182 CryptoAccelRef crypto_accel
;
183 if (! failed_to_get_crypto
.load())
185 crypto_accel
= get_crypto_accel(cct
);
187 failed_to_get_crypto
= true;
190 unsigned char iv
[AES_256_IVSIZE
];
191 for (size_t offset
= 0; result
&& (offset
< size
); offset
+= CHUNK_SIZE
) {
192 size_t process_size
= offset
+ CHUNK_SIZE
<= size
? CHUNK_SIZE
: size
- offset
;
193 prepare_iv(iv
, stream_offset
+ offset
);
194 if (crypto_accel
!= nullptr) {
196 result
= crypto_accel
->cbc_encrypt(out
+ offset
, in
+ offset
,
197 process_size
, iv
, key
);
199 result
= crypto_accel
->cbc_decrypt(out
+ offset
, in
+ offset
,
200 process_size
, iv
, key
);
203 result
= cbc_transform(
204 out
+ offset
, in
+ offset
, process_size
,
212 bool encrypt(bufferlist
& input
,
219 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
220 size_t unaligned_rest_size
= size
- aligned_size
;
222 buffer::ptr
buf(aligned_size
+ AES_256_IVSIZE
);
223 unsigned char* buf_raw
= reinterpret_cast<unsigned char*>(buf
.c_str());
224 const unsigned char* input_raw
= reinterpret_cast<const unsigned char*>(input
.c_str());
226 /* encrypt main bulk of data */
227 result
= cbc_transform(buf_raw
,
230 stream_offset
, key
, true);
231 if (result
&& (unaligned_rest_size
> 0)) {
232 /* remainder to encrypt */
233 if (aligned_size
% CHUNK_SIZE
> 0) {
234 /* use last chunk for unaligned part */
235 unsigned char iv
[AES_256_IVSIZE
] = {0};
236 result
= cbc_transform(buf_raw
+ aligned_size
,
237 buf_raw
+ aligned_size
- AES_256_IVSIZE
,
241 /* 0 full blocks in current chunk, use IV as base for unaligned part */
242 unsigned char iv
[AES_256_IVSIZE
] = {0};
243 unsigned char data
[AES_256_IVSIZE
];
244 prepare_iv(data
, stream_offset
+ aligned_size
);
245 result
= cbc_transform(buf_raw
+ aligned_size
,
251 for(size_t i
= aligned_size
; i
< size
; i
++) {
252 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
257 ldout(cct
, 25) << "Encrypted " << size
<< " bytes"<< dendl
;
258 buf
.set_length(size
);
261 ldout(cct
, 5) << "Failed to encrypt" << dendl
;
267 bool decrypt(bufferlist
& input
,
274 size_t aligned_size
= size
/ AES_256_IVSIZE
* AES_256_IVSIZE
;
275 size_t unaligned_rest_size
= size
- aligned_size
;
277 buffer::ptr
buf(aligned_size
+ AES_256_IVSIZE
);
278 unsigned char* buf_raw
= reinterpret_cast<unsigned char*>(buf
.c_str());
279 unsigned char* input_raw
= reinterpret_cast<unsigned char*>(input
.c_str());
281 /* decrypt main bulk of data */
282 result
= cbc_transform(buf_raw
,
285 stream_offset
, key
, false);
286 if (result
&& unaligned_rest_size
> 0) {
287 /* remainder to decrypt */
288 if (aligned_size
% CHUNK_SIZE
> 0) {
289 /*use last chunk for unaligned part*/
290 unsigned char iv
[AES_256_IVSIZE
] = {0};
291 result
= cbc_transform(buf_raw
+ aligned_size
,
292 input_raw
+ in_ofs
+ aligned_size
- AES_256_IVSIZE
,
296 /* 0 full blocks in current chunk, use IV as base for unaligned part */
297 unsigned char iv
[AES_256_IVSIZE
] = {0};
298 unsigned char data
[AES_256_IVSIZE
];
299 prepare_iv(data
, stream_offset
+ aligned_size
);
300 result
= cbc_transform(buf_raw
+ aligned_size
,
306 for(size_t i
= aligned_size
; i
< size
; i
++) {
307 *(buf_raw
+ i
) ^= *(input_raw
+ in_ofs
+ i
);
312 ldout(cct
, 25) << "Decrypted " << size
<< " bytes"<< dendl
;
313 buf
.set_length(size
);
316 ldout(cct
, 5) << "Failed to decrypt" << dendl
;
322 void prepare_iv(unsigned char (&iv
)[AES_256_IVSIZE
], off_t offset
) {
323 off_t index
= offset
/ AES_256_IVSIZE
;
324 off_t i
= AES_256_IVSIZE
- 1;
326 unsigned int carry
= 0;
328 val
= (index
& 0xff) + IV
[i
] + carry
;
338 std::unique_ptr
<BlockCrypt
> AES_256_CBC_create(CephContext
* cct
, const uint8_t* key
, size_t len
)
340 auto cbc
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(cct
));
341 cbc
->set_key(key
, AES_256_KEYSIZE
);
346 const uint8_t AES_256_CBC::IV
[AES_256_CBC::AES_256_IVSIZE
] =
347 { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' };
350 bool AES_256_ECB_encrypt(CephContext
* cct
,
353 const uint8_t* data_in
,
357 if (key_size
== AES_256_KEYSIZE
) {
358 return evp_sym_transform
<AES_256_KEYSIZE
, 0 /* no IV in ECB */>(
359 cct
, EVP_aes_256_ecb(), data_out
, data_in
, data_size
,
360 nullptr /* no IV in ECB */, key
, true /* encrypt */);
362 ldout(cct
, 5) << "Key size must be 256 bits long" << dendl
;
368 RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext
* cct
,
369 RGWGetObj_Filter
* next
,
370 std::unique_ptr
<BlockCrypt
> crypt
):
371 RGWGetObj_Filter(next
),
373 crypt(std::move(crypt
)),
379 block_size
= this->crypt
->get_block_size();
382 RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {
385 int RGWGetObj_BlockDecrypt::read_manifest(bufferlist
& manifest_bl
) {
387 RGWObjManifest manifest
;
388 if (manifest_bl
.length()) {
389 auto miter
= manifest_bl
.cbegin();
391 decode(manifest
, miter
);
392 } catch (buffer::error
& err
) {
393 ldout(cct
, 0) << "ERROR: couldn't decode manifest" << dendl
;
396 RGWObjManifest::obj_iterator mi
;
397 for (mi
= manifest
.obj_begin(); mi
!= manifest
.obj_end(); ++mi
) {
398 if (mi
.get_cur_stripe() == 0) {
399 parts_len
.push_back(0);
401 parts_len
.back() += mi
.get_stripe_size();
403 if (cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 20>()) {
404 for (size_t i
= 0; i
<parts_len
.size(); i
++) {
405 ldout(cct
, 20) << "Manifest part " << i
<< ", size=" << parts_len
[i
] << dendl
;
412 int RGWGetObj_BlockDecrypt::fixup_range(off_t
& bl_ofs
, off_t
& bl_end
) {
413 off_t inp_ofs
= bl_ofs
;
414 off_t inp_end
= bl_end
;
415 if (parts_len
.size() > 0) {
416 off_t in_ofs
= bl_ofs
;
417 off_t in_end
= bl_end
;
420 while (i
<parts_len
.size() && (in_ofs
>= (off_t
)parts_len
[i
])) {
421 in_ofs
-= parts_len
[i
];
424 //in_ofs is inside block i
426 while (j
<(parts_len
.size() - 1) && (in_end
>= (off_t
)parts_len
[j
])) {
427 in_end
-= parts_len
[j
];
430 //in_end is inside part j, OR j is the last part
432 size_t rounded_end
= ( in_end
& ~(block_size
- 1) ) + (block_size
- 1);
433 if (rounded_end
> parts_len
[j
]) {
434 rounded_end
= parts_len
[j
] - 1;
437 enc_begin_skip
= in_ofs
& (block_size
- 1);
438 ofs
= bl_ofs
- enc_begin_skip
;
440 bl_end
+= rounded_end
- in_end
;
441 bl_ofs
= std::min(bl_ofs
- enc_begin_skip
, bl_end
);
445 enc_begin_skip
= bl_ofs
& (block_size
- 1);
446 ofs
= bl_ofs
& ~(block_size
- 1);
448 bl_ofs
= bl_ofs
& ~(block_size
- 1);
449 bl_end
= ( bl_end
& ~(block_size
- 1) ) + (block_size
- 1);
451 ldout(cct
, 20) << "fixup_range [" << inp_ofs
<< "," << inp_end
452 << "] => [" << bl_ofs
<< "," << bl_end
<< "]" << dendl
;
456 int RGWGetObj_BlockDecrypt::process(bufferlist
& in
, size_t part_ofs
, size_t size
)
459 if (!crypt
->decrypt(in
, 0, size
, data
, part_ofs
)) {
460 return -ERR_INTERNAL_ERROR
;
462 off_t send_size
= size
- enc_begin_skip
;
463 if (ofs
+ enc_begin_skip
+ send_size
> end
+ 1) {
464 send_size
= end
+ 1 - ofs
- enc_begin_skip
;
466 int res
= next
->handle_data(data
, enc_begin_skip
, send_size
);
473 int RGWGetObj_BlockDecrypt::handle_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
474 ldout(cct
, 25) << "Decrypt " << bl_len
<< " bytes" << dendl
;
475 bl
.begin(bl_ofs
).copy(bl_len
, cache
);
478 size_t part_ofs
= ofs
;
479 for (size_t part
: parts_len
) {
480 if (part_ofs
>= part
) {
482 } else if (part_ofs
+ cache
.length() >= part
) {
483 // flush data up to part boundaries, aligned or not
484 res
= process(cache
, part_ofs
, part
- part_ofs
);
493 // write up to block boundaries, aligned only
494 off_t aligned_size
= cache
.length() & ~(block_size
- 1);
495 if (aligned_size
> 0) {
496 res
= process(cache
, part_ofs
, aligned_size
);
502 * flush remainder of data to output
504 int RGWGetObj_BlockDecrypt::flush() {
505 ldout(cct
, 25) << "Decrypt flushing " << cache
.length() << " bytes" << dendl
;
507 size_t part_ofs
= ofs
;
508 for (size_t part
: parts_len
) {
509 if (part_ofs
>= part
) {
511 } else if (part_ofs
+ cache
.length() >= part
) {
512 // flush data up to part boundaries, aligned or not
513 res
= process(cache
, part_ofs
, part
- part_ofs
);
522 // flush up to block boundaries, aligned or not
523 if (cache
.length() > 0) {
524 res
= process(cache
, part_ofs
, cache
.length());
529 RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext
* cct
,
530 rgw::putobj::DataProcessor
*next
,
531 std::unique_ptr
<BlockCrypt
> crypt
)
534 crypt(std::move(crypt
)),
535 block_size(this->crypt
->get_block_size())
539 int RGWPutObj_BlockEncrypt::process(bufferlist
&& data
, uint64_t logical_offset
)
541 ldout(cct
, 25) << "Encrypt " << data
.length() << " bytes" << dendl
;
543 // adjust logical offset to beginning of cached data
544 ceph_assert(logical_offset
>= cache
.length());
545 logical_offset
-= cache
.length();
547 const bool flush
= (data
.length() == 0);
548 cache
.claim_append(data
);
550 uint64_t proc_size
= cache
.length() & ~(block_size
- 1);
552 proc_size
= cache
.length();
556 cache
.splice(0, proc_size
, &in
);
557 if (!crypt
->encrypt(in
, 0, proc_size
, out
, logical_offset
)) {
558 return -ERR_INTERNAL_ERROR
;
560 int r
= Pipe::process(std::move(out
), logical_offset
);
561 logical_offset
+= proc_size
;
567 /*replicate 0-sized handle_data*/
568 return Pipe::process({}, logical_offset
);
574 std::string
create_random_key_selector(CephContext
* const cct
) {
575 char random
[AES_256_KEYSIZE
];
576 cct
->random()->get_bytes(&random
[0], sizeof(random
));
577 return std::string(random
, sizeof(random
));
580 static inline void set_attr(map
<string
, bufferlist
>& attrs
,
582 boost::string_view value
)
585 bl
.append(value
.data(), value
.size());
586 attrs
[key
] = std::move(bl
);
589 static inline std::string
get_str_attribute(map
<string
, bufferlist
>& attrs
,
592 auto iter
= attrs
.find(name
);
593 if (iter
== attrs
.end()) {
596 return iter
->second
.to_str();
600 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM
=0,
601 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
,
602 X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
,
603 X_AMZ_SERVER_SIDE_ENCRYPTION
,
604 X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
,
605 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
609 const char* http_header_name
;
610 const std::string post_part_name
;
611 } crypt_option_names
;
613 static const crypt_option_names crypt_options
[] = {
614 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", "x-amz-server-side-encryption-customer-algorithm"},
615 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "x-amz-server-side-encryption-customer-key"},
616 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "x-amz-server-side-encryption-customer-key-md5"},
617 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", "x-amz-server-side-encryption"},
618 {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID", "x-amz-server-side-encryption-aws-kms-key-id"},
621 static boost::string_view
get_crypt_attribute(
623 std::map
<std::string
,
624 RGWPostObj_ObjStore::post_form_part
,
625 const ltstr_nocase
>* parts
,
626 crypt_option_e option
)
629 X_AMZ_SERVER_SIDE_ENCRYPTION_LAST
== sizeof(crypt_options
)/sizeof(*crypt_options
),
630 "Missing items in crypt_options");
631 if (parts
!= nullptr) {
633 = parts
->find(crypt_options
[option
].post_part_name
);
634 if (iter
== parts
->end())
635 return boost::string_view();
636 bufferlist
& data
= iter
->second
.data
;
637 boost::string_view str
= boost::string_view(data
.c_str(), data
.length());
638 return rgw_trim_whitespace(str
);
640 const char* hdr
= env
->get(crypt_options
[option
].http_header_name
, nullptr);
641 if (hdr
!= nullptr) {
642 return boost::string_view(hdr
);
644 return boost::string_view();
650 int rgw_s3_prepare_encrypt(struct req_state
* s
,
651 std::map
<std::string
, ceph::bufferlist
>& attrs
,
652 std::map
<std::string
,
653 RGWPostObj_ObjStore::post_form_part
,
654 const ltstr_nocase
>* parts
,
655 std::unique_ptr
<BlockCrypt
>* block_crypt
,
656 std::map
<std::string
, std::string
>& crypt_http_responses
)
659 crypt_http_responses
.clear();
661 boost::string_view req_sse_ca
=
662 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM
);
663 if (! req_sse_ca
.empty()) {
664 if (req_sse_ca
!= "AES256") {
665 ldout(s
->cct
, 5) << "ERROR: Invalid value for header "
666 << "x-amz-server-side-encryption-customer-algorithm"
668 s
->err
.message
= "The requested encryption algorithm is not valid, must be AES256.";
669 return -ERR_INVALID_ENCRYPTION_ALGORITHM
;
671 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
672 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
673 ldout(s
->cct
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
674 return -ERR_INVALID_REQUEST
;
679 key_bin
= from_base64(
680 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
) );
682 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption "
683 << "key which contains character that is not base64 encoded."
685 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
686 "provided keys must provide an appropriate secret key.";
690 if (key_bin
.size() != AES_256_CBC::AES_256_KEYSIZE
) {
691 ldout(s
->cct
, 5) << "ERROR: invalid encryption key size" << dendl
;
692 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
693 "provided keys must provide an appropriate secret key.";
697 boost::string_view keymd5
=
698 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
);
700 std::string keymd5_bin
;
702 keymd5_bin
= from_base64(keymd5
);
704 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key "
705 << "md5 which contains character that is not base64 encoded."
707 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
708 "provided keys must provide an appropriate secret key md5.";
712 if (keymd5_bin
.size() != CEPH_CRYPTO_MD5_DIGESTSIZE
) {
713 ldout(s
->cct
, 5) << "ERROR: Invalid key md5 size" << dendl
;
714 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
715 "provided keys must provide an appropriate secret key md5.";
720 unsigned char key_hash_res
[CEPH_CRYPTO_MD5_DIGESTSIZE
];
721 key_hash
.Update(reinterpret_cast<const unsigned char*>(key_bin
.c_str()), key_bin
.size());
722 key_hash
.Final(key_hash_res
);
724 if (memcmp(key_hash_res
, keymd5_bin
.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE
) != 0) {
725 ldout(s
->cct
, 5) << "ERROR: Invalid key md5 hash" << dendl
;
726 s
->err
.message
= "The calculated MD5 hash of the key did not match the hash that was provided.";
730 set_attr(attrs
, RGW_ATTR_CRYPT_MODE
, "SSE-C-AES256");
731 set_attr(attrs
, RGW_ATTR_CRYPT_KEYMD5
, keymd5_bin
);
734 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
735 aes
->set_key(reinterpret_cast<const uint8_t*>(key_bin
.c_str()), AES_256_KEYSIZE
);
736 *block_crypt
= std::move(aes
);
739 crypt_http_responses
["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
740 crypt_http_responses
["x-amz-server-side-encryption-customer-key-MD5"] = keymd5
.to_string();
743 boost::string_view customer_key
=
744 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY
);
745 if (!customer_key
.empty()) {
746 ldout(s
->cct
, 5) << "ERROR: SSE-C encryption request is missing the header "
747 << "x-amz-server-side-encryption-customer-algorithm"
749 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
750 "provided keys must provide a valid encryption algorithm.";
754 boost::string_view customer_key_md5
=
755 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5
);
756 if (!customer_key_md5
.empty()) {
757 ldout(s
->cct
, 5) << "ERROR: SSE-C encryption request is missing the header "
758 << "x-amz-server-side-encryption-customer-algorithm"
760 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
761 "provided keys must provide a valid encryption algorithm.";
766 /* AMAZON server side encryption with KMS (key management service) */
767 boost::string_view req_sse
=
768 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION
);
769 if (! req_sse
.empty()) {
771 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
772 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
773 ldout(s
->cct
, 5) << "ERROR: insecure request, rgw_crypt_require_ssl is set" << dendl
;
774 return -ERR_INVALID_REQUEST
;
777 if (req_sse
== "aws:kms") {
778 boost::string_view key_id
=
779 get_crypt_attribute(s
->info
.env
, parts
, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
);
780 if (key_id
.empty()) {
781 ldout(s
->cct
, 5) << "ERROR: not provide a valid key id" << dendl
;
782 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
783 "HTTP header x-amz-server-side-encryption-aws-kms-key-id";
784 return -ERR_INVALID_ACCESS_KEY
;
786 /* try to retrieve actual key */
787 std::string key_selector
= create_random_key_selector(s
->cct
);
788 std::string actual_key
;
789 res
= get_actual_key_from_kms(s
->cct
, key_id
, key_selector
, actual_key
);
791 ldout(s
->cct
, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id
<< dendl
;
792 s
->err
.message
= "Failed to retrieve the actual key, kms-keyid: " + key_id
.to_string();
795 if (actual_key
.size() != AES_256_KEYSIZE
) {
796 ldout(s
->cct
, 5) << "ERROR: key obtained from key_id:" <<
797 key_id
<< " is not 256 bit size" << dendl
;
798 s
->err
.message
= "KMS provided an invalid key for the given kms-keyid.";
799 return -ERR_INVALID_ACCESS_KEY
;
801 set_attr(attrs
, RGW_ATTR_CRYPT_MODE
, "SSE-KMS");
802 set_attr(attrs
, RGW_ATTR_CRYPT_KEYID
, key_id
);
803 set_attr(attrs
, RGW_ATTR_CRYPT_KEYSEL
, key_selector
);
806 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
807 aes
->set_key(reinterpret_cast<const uint8_t*>(actual_key
.c_str()), AES_256_KEYSIZE
);
808 *block_crypt
= std::move(aes
);
810 actual_key
.replace(0, actual_key
.length(), actual_key
.length(), '\000');
812 crypt_http_responses
["x-amz-server-side-encryption"] = "aws:kms";
813 crypt_http_responses
["x-amz-server-side-encryption-aws-kms-key-id"] = key_id
.to_string();
815 } else if (req_sse
== "AES256") {
816 /* if a default encryption key was provided, we will use it for SSE-S3 */
818 ldout(s
->cct
, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption"
820 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
821 "HTTP header x-amz-server-side-encryption : aws:kms or AES256";
825 /* x-amz-server-side-encryption not present or empty */
826 boost::string_view key_id
=
827 get_crypt_attribute(s
->info
.env
, parts
,
828 X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
);
829 if (!key_id
.empty()) {
830 ldout(s
->cct
, 5) << "ERROR: SSE-KMS encryption request is missing the header "
831 << "x-amz-server-side-encryption"
833 s
->err
.message
= "Server Side Encryption with KMS managed key requires "
834 "HTTP header x-amz-server-side-encryption : aws:kms";
839 /* no other encryption mode, check if default encryption is selected */
840 if (s
->cct
->_conf
->rgw_crypt_default_encryption_key
!= "") {
841 std::string master_encryption_key
;
843 master_encryption_key
= from_base64(s
->cct
->_conf
->rgw_crypt_default_encryption_key
);
845 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key "
846 << "which contains character that is not base64 encoded."
848 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
849 "provided keys must provide an appropriate secret key.";
853 if (master_encryption_key
.size() != 256 / 8) {
854 ldout(s
->cct
, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl
;
855 /* not an error to return; missing encryption does not inhibit processing */
859 set_attr(attrs
, RGW_ATTR_CRYPT_MODE
, "RGW-AUTO");
860 std::string key_selector
= create_random_key_selector(s
->cct
);
861 set_attr(attrs
, RGW_ATTR_CRYPT_KEYSEL
, key_selector
);
863 uint8_t actual_key
[AES_256_KEYSIZE
];
864 if (AES_256_ECB_encrypt(s
->cct
,
865 reinterpret_cast<const uint8_t*>(master_encryption_key
.c_str()), AES_256_KEYSIZE
,
866 reinterpret_cast<const uint8_t*>(key_selector
.c_str()),
867 actual_key
, AES_256_KEYSIZE
) != true) {
868 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
872 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
873 aes
->set_key(reinterpret_cast<const uint8_t*>(actual_key
), AES_256_KEYSIZE
);
874 *block_crypt
= std::move(aes
);
876 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
885 int rgw_s3_prepare_decrypt(struct req_state
* s
,
886 map
<string
, bufferlist
>& attrs
,
887 std::unique_ptr
<BlockCrypt
>* block_crypt
,
888 std::map
<std::string
, std::string
>& crypt_http_responses
)
891 std::string stored_mode
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_MODE
);
892 ldout(s
->cct
, 15) << "Encryption mode: " << stored_mode
<< dendl
;
894 const char *req_sse
= s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", NULL
);
895 if (nullptr != req_sse
&& (s
->op
== OP_GET
|| s
->op
== OP_HEAD
)) {
896 return -ERR_INVALID_REQUEST
;
899 if (stored_mode
== "SSE-C-AES256") {
900 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
901 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
902 ldout(s
->cct
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
903 return -ERR_INVALID_REQUEST
;
905 const char *req_cust_alg
=
906 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL
);
908 if (nullptr == req_cust_alg
) {
909 ldout(s
->cct
, 5) << "ERROR: Request for SSE-C encrypted object missing "
910 << "x-amz-server-side-encryption-customer-algorithm"
912 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
913 "provided keys must provide a valid encryption algorithm.";
915 } else if (strcmp(req_cust_alg
, "AES256") != 0) {
916 ldout(s
->cct
, 5) << "ERROR: The requested encryption algorithm is not valid, must be AES256." << dendl
;
917 s
->err
.message
= "The requested encryption algorithm is not valid, must be AES256.";
918 return -ERR_INVALID_ENCRYPTION_ALGORITHM
;
923 key_bin
= from_base64(s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", ""));
925 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key "
926 << "which contains character that is not base64 encoded."
928 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
929 "provided keys must provide an appropriate secret key.";
933 if (key_bin
.size() != AES_256_CBC::AES_256_KEYSIZE
) {
934 ldout(s
->cct
, 5) << "ERROR: Invalid encryption key size" << dendl
;
935 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
936 "provided keys must provide an appropriate secret key.";
941 s
->info
.env
->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "");
942 std::string keymd5_bin
;
944 keymd5_bin
= from_base64(keymd5
);
946 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 "
947 << "which contains character that is not base64 encoded."
949 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
950 "provided keys must provide an appropriate secret key md5.";
955 if (keymd5_bin
.size() != CEPH_CRYPTO_MD5_DIGESTSIZE
) {
956 ldout(s
->cct
, 5) << "ERROR: Invalid key md5 size " << dendl
;
957 s
->err
.message
= "Requests specifying Server Side Encryption with Customer "
958 "provided keys must provide an appropriate secret key md5.";
963 uint8_t key_hash_res
[CEPH_CRYPTO_MD5_DIGESTSIZE
];
964 key_hash
.Update(reinterpret_cast<const unsigned char*>(key_bin
.c_str()), key_bin
.size());
965 key_hash
.Final(key_hash_res
);
967 if ((memcmp(key_hash_res
, keymd5_bin
.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE
) != 0) ||
968 (get_str_attribute(attrs
, RGW_ATTR_CRYPT_KEYMD5
) != keymd5_bin
)) {
969 s
->err
.message
= "The calculated MD5 hash of the key did not match the hash that was provided.";
972 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
973 aes
->set_key(reinterpret_cast<const uint8_t*>(key_bin
.c_str()), AES_256_CBC::AES_256_KEYSIZE
);
974 if (block_crypt
) *block_crypt
= std::move(aes
);
976 crypt_http_responses
["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
977 crypt_http_responses
["x-amz-server-side-encryption-customer-key-MD5"] = keymd5
;
981 if (stored_mode
== "SSE-KMS") {
982 if (s
->cct
->_conf
->rgw_crypt_require_ssl
&&
983 !rgw_transport_is_secure(s
->cct
, *s
->info
.env
)) {
984 ldout(s
->cct
, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl
;
985 return -ERR_INVALID_REQUEST
;
987 /* try to retrieve actual key */
988 std::string key_id
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_KEYID
);
989 std::string key_selector
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_KEYSEL
);
990 std::string actual_key
;
991 res
= get_actual_key_from_kms(s
->cct
, key_id
, key_selector
, actual_key
);
993 ldout(s
->cct
, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id
<< dendl
;
994 s
->err
.message
= "Failed to retrieve the actual key, kms-keyid: " + key_id
;
997 if (actual_key
.size() != AES_256_KEYSIZE
) {
998 ldout(s
->cct
, 0) << "ERROR: key obtained from key_id:" <<
999 key_id
<< " is not 256 bit size" << dendl
;
1000 s
->err
.message
= "KMS provided an invalid key for the given kms-keyid.";
1001 return -ERR_INVALID_ACCESS_KEY
;
1004 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
1005 aes
->set_key(reinterpret_cast<const uint8_t*>(actual_key
.c_str()), AES_256_KEYSIZE
);
1006 actual_key
.replace(0, actual_key
.length(), actual_key
.length(), '\000');
1007 if (block_crypt
) *block_crypt
= std::move(aes
);
1009 crypt_http_responses
["x-amz-server-side-encryption"] = "aws:kms";
1010 crypt_http_responses
["x-amz-server-side-encryption-aws-kms-key-id"] = key_id
;
1014 if (stored_mode
== "RGW-AUTO") {
1015 std::string master_encryption_key
;
1017 master_encryption_key
= from_base64(std::string(s
->cct
->_conf
->rgw_crypt_default_encryption_key
));
1019 ldout(s
->cct
, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key "
1020 << "which contains character that is not base64 encoded."
1022 s
->err
.message
= "The default encryption key is not valid base64.";
1026 if (master_encryption_key
.size() != 256 / 8) {
1027 ldout(s
->cct
, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl
;
1030 std::string attr_key_selector
= get_str_attribute(attrs
, RGW_ATTR_CRYPT_KEYSEL
);
1031 if (attr_key_selector
.size() != AES_256_CBC::AES_256_KEYSIZE
) {
1032 ldout(s
->cct
, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL
<< dendl
;
1035 uint8_t actual_key
[AES_256_KEYSIZE
];
1036 if (AES_256_ECB_encrypt(s
->cct
,
1037 reinterpret_cast<const uint8_t*>(master_encryption_key
.c_str()),
1039 reinterpret_cast<const uint8_t*>(attr_key_selector
.c_str()),
1040 actual_key
, AES_256_KEYSIZE
) != true) {
1041 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
1044 auto aes
= std::unique_ptr
<AES_256_CBC
>(new AES_256_CBC(s
->cct
));
1045 aes
->set_key(actual_key
, AES_256_KEYSIZE
);
1046 ::ceph::crypto::zeroize_for_security(actual_key
, sizeof(actual_key
));
1047 if (block_crypt
) *block_crypt
= std::move(aes
);