X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Frgw%2Frgw_crypt.cc;fp=src%2Fceph%2Fsrc%2Frgw%2Frgw_crypt.cc;h=81a84ad698af6939832aa81fd9badabc8efbeee4;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/rgw/rgw_crypt.cc b/src/ceph/src/rgw/rgw_crypt.cc new file mode 100644 index 0000000..81a84ad --- /dev/null +++ b/src/ceph/src/rgw/rgw_crypt.cc @@ -0,0 +1,1414 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/** + * Crypto filters for Put/Post/Get operations. + */ +#include +#include +#include +#include +#include +#include "include/assert.h" +#include +#include +#include "include/str_map.h" +#include "crypto/crypto_accel.h" +#include "crypto/crypto_plugin.h" +#ifdef USE_NSS +# include +# include +# include +#endif + +#ifdef USE_CRYPTOPP +#include +#include +#include +using namespace CryptoPP; +#endif + +#define dout_context g_ceph_context +#define dout_subsys ceph_subsys_rgw + +using namespace rgw; + +/** + * Encryption in CTR mode. offset is used as IV for each block. + */ +#warning "TODO: move this code to auth/Crypto for others to reuse." + +class AES_256_CTR : public BlockCrypt { +public: + static const size_t AES_256_KEYSIZE = 256 / 8; + static const size_t AES_256_IVSIZE = 128 / 8; +private: + static const uint8_t IV[AES_256_IVSIZE]; + CephContext* cct; + uint8_t key[AES_256_KEYSIZE]; +public: + AES_256_CTR(CephContext* cct): cct(cct) { + } + ~AES_256_CTR() { + memset(key, 0, AES_256_KEYSIZE); + } + bool set_key(const uint8_t* _key, size_t key_size) { + if (key_size != AES_256_KEYSIZE) { + return false; + } + memcpy(key, _key, AES_256_KEYSIZE); + return true; + } + size_t get_block_size() { + return AES_256_IVSIZE; + } + +#ifdef USE_CRYPTOPP + + bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) { + byte iv[AES_256_IVSIZE]; + ldout(cct, 25) + << "Encrypt in_ofs " << in_ofs + << " size=" << size + << " stream_offset=" << stream_offset + << dendl; + if (input.length() < in_ofs + size) { + return false; + } + + output.clear(); + buffer::ptr buf((size + AES_256_KEYSIZE - 1) / AES_256_KEYSIZE * AES_256_KEYSIZE); + /*create CTR mask*/ + prepare_iv(iv, stream_offset); + CTR_Mode< AES >::Encryption e; + e.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); + buf.zero(); + e.ProcessData((byte*)buf.c_str(), (byte*)buf.c_str(), buf.length()); + buf.set_length(size); + off_t plaintext_pos = in_ofs; + off_t crypt_pos = 0; + auto iter = input.buffers().begin(); + //skip unaffected begin + while ((iter != input.buffers().end()) && (plaintext_pos >= iter->length())) { + plaintext_pos -= iter->length(); + ++iter; + } + while (iter != input.buffers().end()) { + off_t cnt = std::min(iter->length() - plaintext_pos, size - crypt_pos); + byte* src = (byte*)iter->c_str() + plaintext_pos; + byte* dst = (byte*)buf.c_str() + crypt_pos; + for (off_t i = 0; i < cnt; i++) { + dst[i] ^= src[i]; + } + ++iter; + plaintext_pos = 0; + crypt_pos += cnt; + } + output.append(buf); + return true; + } + +#elif defined(USE_NSS) + + bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) + { + bool result = false; + PK11SlotInfo *slot; + SECItem keyItem; + PK11SymKey *symkey; + CK_AES_CTR_PARAMS ctr_params = {0}; + SECItem ivItem; + SECItem *param; + SECStatus ret; + PK11Context *ectx; + int written; + unsigned int written2; + + slot = PK11_GetBestSlot(CKM_AES_CTR, NULL); + if (slot) { + keyItem.type = siBuffer; + keyItem.data = key; + keyItem.len = AES_256_KEYSIZE; + + symkey = PK11_ImportSymKey(slot, CKM_AES_CTR, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); + if (symkey) { + static_assert(sizeof(ctr_params.cb) >= AES_256_IVSIZE, "Must fit counter"); + ctr_params.ulCounterBits = 128; + prepare_iv(reinterpret_cast(&ctr_params.cb), stream_offset); + + ivItem.type = siBuffer; + ivItem.data = (unsigned char*)&ctr_params; + ivItem.len = sizeof(ctr_params); + + param = PK11_ParamFromIV(CKM_AES_CTR, &ivItem); + if (param) { + ectx = PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, symkey, param); + if (ectx) { + buffer::ptr buf((size + AES_256_KEYSIZE - 1) / AES_256_KEYSIZE * AES_256_KEYSIZE); + ret = PK11_CipherOp(ectx, + (unsigned char*)buf.c_str(), &written, buf.length(), + (unsigned char*)input.c_str() + in_ofs, size); + if (ret == SECSuccess) { + ret = PK11_DigestFinal(ectx, + (unsigned char*)buf.c_str() + written, &written2, + buf.length() - written); + if (ret == SECSuccess) { + buf.set_length(written + written2); + output.append(buf); + result = true; + } + } + PK11_DestroyContext(ectx, PR_TRUE); + } + SECITEM_FreeItem(param, PR_TRUE); + } + PK11_FreeSymKey(symkey); + } + PK11_FreeSlot(slot); + } + if (result == false) { + ldout(cct, 5) << "Failed to perform AES-CTR encryption: " << PR_GetError() << dendl; + } + return result; + } + +#else +#error Must define USE_CRYPTOPP or USE_NSS +#endif + /* in CTR encrypt is the same as decrypt */ + bool decrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) { + return encrypt(input, in_ofs, size, output, stream_offset); + } + + void prepare_iv(byte iv[AES_256_IVSIZE], off_t offset) { + off_t index = offset / AES_256_IVSIZE; + off_t i = AES_256_IVSIZE - 1; + unsigned int val; + unsigned int carry = 0; + while (i>=0) { + val = (index & 0xff) + IV[i] + carry; + iv[i] = val; + carry = val >> 8; + index = index >> 8; + i--; + } + } +}; + +const uint8_t AES_256_CTR::IV[AES_256_CTR::AES_256_IVSIZE] = + { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; + + +CryptoAccelRef get_crypto_accel(CephContext *cct) +{ + CryptoAccelRef ca_impl = nullptr; + stringstream ss; + PluginRegistry *reg = cct->get_plugin_registry(); + string crypto_accel_type = cct->_conf->plugin_crypto_accelerator; + + CryptoPlugin *factory = dynamic_cast(reg->get_with_load("crypto", crypto_accel_type)); + if (factory == nullptr) { + lderr(cct) << __func__ << " cannot load crypto accelerator of type " << crypto_accel_type << dendl; + return nullptr; + } + int err = factory->factory(&ca_impl, &ss); + if (err) { + lderr(cct) << __func__ << " factory return error " << err << + " with description: " << ss.str() << dendl; + } + return ca_impl; +} + + +/** + * Encryption in CBC mode. Chunked to 4K blocks. Offset is used as IV for each 4K block. + * + * + * + * A. Encryption + * 1. Input is split to 4K chunks + remainder in one, smaller chunk + * 2. Each full chunk is encrypted separately with CBC chained mode, with initial IV derived from offset + * 3. Last chunk is 16*m + n. + * 4. 16*m bytes are encrypted with CBC chained mode, with initial IV derived from offset + * 5. Last n bytes are xor-ed with pattern obtained by CBC encryption of + * last encrypted 16 byte block <16m-16, 16m-15) with IV = {0}. + * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern + * obtained by CBC encryption of {0} with IV derived from offset + * + * B. Decryption + * 1. Input is split to 4K chunks + remainder in one, smaller chunk + * 2. Each full chunk is decrypted separately with CBC chained mode, with initial IV derived from offset + * 3. Last chunk is 16*m + n. + * 4. 16*m bytes are decrypted with CBC chained mode, with initial IV derived from offset + * 5. Last n bytes are xor-ed with pattern obtained by CBC ENCRYPTION of + * last (still encrypted) 16 byte block <16m-16,16m-15) with IV = {0} + * 6. (Special case) If m == 0 then last n bytes are xor-ed with pattern + * obtained by CBC ENCRYPTION of {0} with IV derived from offset + */ +#warning "TODO: use auth/Crypto instead of reimplementing." +class AES_256_CBC : public BlockCrypt { +public: + static const size_t AES_256_KEYSIZE = 256 / 8; + static const size_t AES_256_IVSIZE = 128 / 8; + static const size_t CHUNK_SIZE = 4096; +private: + static const uint8_t IV[AES_256_IVSIZE]; + CephContext* cct; + uint8_t key[AES_256_KEYSIZE]; +public: + AES_256_CBC(CephContext* cct): cct(cct) { + } + ~AES_256_CBC() { + memset(key, 0, AES_256_KEYSIZE); + } + bool set_key(const uint8_t* _key, size_t key_size) { + if (key_size != AES_256_KEYSIZE) { + return false; + } + memcpy(key, _key, AES_256_KEYSIZE); + return true; + } + size_t get_block_size() { + return CHUNK_SIZE; + } + +#ifdef USE_CRYPTOPP + + bool cbc_transform(unsigned char* out, + const unsigned char* in, + size_t size, + const unsigned char (&iv)[AES_256_IVSIZE], + const unsigned char (&key)[AES_256_KEYSIZE], + bool encrypt) + { + if (encrypt) { + CBC_Mode< AES >::Encryption e; + e.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); + e.ProcessData((byte*)out, (byte*)in, size); + } else { + CBC_Mode< AES >::Decryption d; + d.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE); + d.ProcessData((byte*)out, (byte*)in, size); + } + return true; + } + +#elif defined(USE_NSS) + + bool cbc_transform(unsigned char* out, + const unsigned char* in, + size_t size, + const unsigned char (&iv)[AES_256_IVSIZE], + const unsigned char (&key)[AES_256_KEYSIZE], + bool encrypt) + { + bool result = false; + PK11SlotInfo *slot; + SECItem keyItem; + PK11SymKey *symkey; + CK_AES_CBC_ENCRYPT_DATA_PARAMS ctr_params = {0}; + SECItem ivItem; + SECItem *param; + SECStatus ret; + PK11Context *ectx; + int written; + + slot = PK11_GetBestSlot(CKM_AES_CBC, NULL); + if (slot) { + keyItem.type = siBuffer; + keyItem.data = const_cast(&key[0]); + keyItem.len = AES_256_KEYSIZE; + symkey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); + if (symkey) { + memcpy(ctr_params.iv, iv, AES_256_IVSIZE); + ivItem.type = siBuffer; + ivItem.data = (unsigned char*)&ctr_params; + ivItem.len = sizeof(ctr_params); + + param = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); + if (param) { + ectx = PK11_CreateContextBySymKey(CKM_AES_CBC, encrypt?CKA_ENCRYPT:CKA_DECRYPT, symkey, param); + if (ectx) { + ret = PK11_CipherOp(ectx, + out, &written, size, + in, size); + if ((ret == SECSuccess) && (written == (int)size)) { + result = true; + } + PK11_DestroyContext(ectx, PR_TRUE); + } + SECITEM_FreeItem(param, PR_TRUE); + } + PK11_FreeSymKey(symkey); + } + PK11_FreeSlot(slot); + } + if (result == false) { + ldout(cct, 5) << "Failed to perform AES-CBC encryption: " << PR_GetError() << dendl; + } + return result; + } + +#else +#error Must define USE_CRYPTOPP or USE_NSS +#endif + + bool cbc_transform(unsigned char* out, + const unsigned char* in, + size_t size, + off_t stream_offset, + const unsigned char (&key)[AES_256_KEYSIZE], + bool encrypt) + { + static std::atomic failed_to_get_crypto(false); + CryptoAccelRef crypto_accel; + if (! failed_to_get_crypto.load()) + { + crypto_accel = get_crypto_accel(cct); + if (!crypto_accel) + failed_to_get_crypto = true; + } + bool result = true; + unsigned char iv[AES_256_IVSIZE]; + for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) { + size_t process_size = offset + CHUNK_SIZE <= size ? CHUNK_SIZE : size - offset; + prepare_iv(iv, stream_offset + offset); + if (crypto_accel != nullptr) { + if (encrypt) { + result = crypto_accel->cbc_encrypt(out + offset, in + offset, + process_size, iv, key); + } else { + result = crypto_accel->cbc_decrypt(out + offset, in + offset, + process_size, iv, key); + } + } else { + result = cbc_transform( + out + offset, in + offset, process_size, + iv, key, encrypt); + } + } + return result; + } + + + bool encrypt(bufferlist& input, + off_t in_ofs, + size_t size, + bufferlist& output, + off_t stream_offset) + { + bool result = false; + size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; + size_t unaligned_rest_size = size - aligned_size; + output.clear(); + buffer::ptr buf(aligned_size + AES_256_IVSIZE); + unsigned char* buf_raw = reinterpret_cast(buf.c_str()); + const unsigned char* input_raw = reinterpret_cast(input.c_str()); + + /* encrypt main bulk of data */ + result = cbc_transform(buf_raw, + input_raw + in_ofs, + aligned_size, + stream_offset, key, true); + if (result && (unaligned_rest_size > 0)) { + /* remainder to encrypt */ + if (aligned_size % CHUNK_SIZE > 0) { + /* use last chunk for unaligned part */ + unsigned char iv[AES_256_IVSIZE] = {0}; + result = cbc_transform(buf_raw + aligned_size, + buf_raw + aligned_size - AES_256_IVSIZE, + AES_256_IVSIZE, + iv, key, true); + } else { + /* 0 full blocks in current chunk, use IV as base for unaligned part */ + unsigned char iv[AES_256_IVSIZE] = {0}; + unsigned char data[AES_256_IVSIZE]; + prepare_iv(data, stream_offset + aligned_size); + result = cbc_transform(buf_raw + aligned_size, + data, + AES_256_IVSIZE, + iv, key, true); + } + if (result) { + for(size_t i = aligned_size; i < size; i++) { + *(buf_raw + i) ^= *(input_raw + in_ofs + i); + } + } + } + if (result) { + ldout(cct, 25) << "Encrypted " << size << " bytes"<< dendl; + buf.set_length(size); + output.append(buf); + } else { + ldout(cct, 5) << "Failed to encrypt" << dendl; + } + return result; + } + + + bool decrypt(bufferlist& input, + off_t in_ofs, + size_t size, + bufferlist& output, + off_t stream_offset) + { + bool result = false; + size_t aligned_size = size / AES_256_IVSIZE * AES_256_IVSIZE; + size_t unaligned_rest_size = size - aligned_size; + output.clear(); + buffer::ptr buf(aligned_size + AES_256_IVSIZE); + unsigned char* buf_raw = reinterpret_cast(buf.c_str()); + unsigned char* input_raw = reinterpret_cast(input.c_str()); + + /* decrypt main bulk of data */ + result = cbc_transform(buf_raw, + input_raw + in_ofs, + aligned_size, + stream_offset, key, false); + if (result && unaligned_rest_size > 0) { + /* remainder to decrypt */ + if (aligned_size % CHUNK_SIZE > 0) { + /*use last chunk for unaligned part*/ + unsigned char iv[AES_256_IVSIZE] = {0}; + result = cbc_transform(buf_raw + aligned_size, + input_raw + in_ofs + aligned_size - AES_256_IVSIZE, + AES_256_IVSIZE, + iv, key, true); + } else { + /* 0 full blocks in current chunk, use IV as base for unaligned part */ + unsigned char iv[AES_256_IVSIZE] = {0}; + unsigned char data[AES_256_IVSIZE]; + prepare_iv(data, stream_offset + aligned_size); + result = cbc_transform(buf_raw + aligned_size, + data, + AES_256_IVSIZE, + iv, key, true); + } + if (result) { + for(size_t i = aligned_size; i < size; i++) { + *(buf_raw + i) ^= *(input_raw + in_ofs + i); + } + } + } + if (result) { + ldout(cct, 25) << "Decrypted " << size << " bytes"<< dendl; + buf.set_length(size); + output.append(buf); + } else { + ldout(cct, 5) << "Failed to decrypt" << dendl; + } + return result; + } + + + void prepare_iv(byte (&iv)[AES_256_IVSIZE], off_t offset) { + off_t index = offset / AES_256_IVSIZE; + off_t i = AES_256_IVSIZE - 1; + unsigned int val; + unsigned int carry = 0; + while (i>=0) { + val = (index & 0xff) + IV[i] + carry; + iv[i] = val; + carry = val >> 8; + index = index >> 8; + i--; + } + } +}; + + +std::unique_ptr AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len) +{ + auto cbc = std::unique_ptr(new AES_256_CBC(cct)); + cbc->set_key(key, AES_256_KEYSIZE); + return std::move(cbc); +} + + +const uint8_t AES_256_CBC::IV[AES_256_CBC::AES_256_IVSIZE] = + { 'a', 'e', 's', '2', '5', '6', 'i', 'v', '_', 'c', 't', 'r', '1', '3', '3', '7' }; + + +#ifdef USE_CRYPTOPP + +bool AES_256_ECB_encrypt(CephContext* cct, + const uint8_t* key, + size_t key_size, + const uint8_t* data_in, + uint8_t* data_out, + size_t data_size) +{ + bool res = false; + if (key_size == AES_256_KEYSIZE) { + try { + ECB_Mode< AES >::Encryption e; + e.SetKey( key, key_size ); + e.ProcessData(data_out, data_in, data_size); + res = true; + } catch( CryptoPP::Exception& ex ) { + ldout(cct, 5) << "AES-ECB encryption failed with: " << ex.GetWhat() << dendl; + } + } + return res; +} + +#elif defined USE_NSS + +bool AES_256_ECB_encrypt(CephContext* cct, + const uint8_t* key, + size_t key_size, + const uint8_t* data_in, + uint8_t* data_out, + size_t data_size) { + bool result = false; + PK11SlotInfo *slot; + SECItem keyItem; + PK11SymKey *symkey; + SECItem *param; + SECStatus ret; + PK11Context *ectx; + int written; + unsigned int written2; + if (key_size == AES_256_KEYSIZE) { + slot = PK11_GetBestSlot(CKM_AES_ECB, NULL); + if (slot) { + keyItem.type = siBuffer; + keyItem.data = const_cast(key); + keyItem.len = AES_256_KEYSIZE; + + param = PK11_ParamFromIV(CKM_AES_ECB, NULL); + if (param) { + symkey = PK11_ImportSymKey(slot, CKM_AES_ECB, PK11_OriginUnwrap, CKA_UNWRAP, &keyItem, NULL); + if (symkey) { + ectx = PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_ENCRYPT, symkey, param); + if (ectx) { + ret = PK11_CipherOp(ectx, + data_out, &written, data_size, + data_in, data_size); + if (ret == SECSuccess) { + ret = PK11_DigestFinal(ectx, + data_out + written, &written2, + data_size - written); + if (ret == SECSuccess) { + result = true; + } + } + PK11_DestroyContext(ectx, PR_TRUE); + } + PK11_FreeSymKey(symkey); + } + SECITEM_FreeItem(param, PR_TRUE); + } + PK11_FreeSlot(slot); + } + if (result == false) { + ldout(cct, 5) << "Failed to perform AES-ECB encryption: " << PR_GetError() << dendl; + } + } else { + ldout(cct, 5) << "Key size must be 256 bits long" << dendl; + } + return result; +} + +#else +#error Must define USE_CRYPTOPP or USE_NSS +#endif + + +RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext* cct, + RGWGetDataCB* next, + std::unique_ptr crypt): + RGWGetObj_Filter(next), + cct(cct), + crypt(std::move(crypt)), + enc_begin_skip(0), + ofs(0), + end(0), + cache() +{ + block_size = this->crypt->get_block_size(); +} + +RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() { +} + +int RGWGetObj_BlockDecrypt::read_manifest(bufferlist& manifest_bl) { + parts_len.clear(); + RGWObjManifest manifest; + if (manifest_bl.length()) { + bufferlist::iterator miter = manifest_bl.begin(); + try { + ::decode(manifest, miter); + } catch (buffer::error& err) { + ldout(cct, 0) << "ERROR: couldn't decode manifest" << dendl; + return -EIO; + } + RGWObjManifest::obj_iterator mi; + for (mi = manifest.obj_begin(); mi != manifest.obj_end(); ++mi) { + if (mi.get_cur_stripe() == 0) { + parts_len.push_back(0); + } + parts_len.back() += mi.get_stripe_size(); + } + if (cct->_conf->subsys.should_gather(ceph_subsys_rgw, 20)) { + for (size_t i = 0; i [" << bl_ofs << "," << bl_end << "]" << dendl; + return 0; +} + + +int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) { + int res = 0; + ldout(cct, 25) << "Decrypt " << bl_len << " bytes" << dendl; + size_t part_ofs = ofs; + size_t i = 0; + while (i= parts_len[i])) { + part_ofs -= parts_len[i]; + i++; + } + bl.copy(bl_ofs, bl_len, cache); + off_t aligned_size = cache.length() & ~(block_size - 1); + if (aligned_size > 0) { + bufferlist data; + if (! crypt->decrypt(cache, 0, aligned_size, data, part_ofs) ) { + return -ERR_INTERNAL_ERROR; + } + off_t send_size = aligned_size - enc_begin_skip; + if (ofs + enc_begin_skip + send_size > end + 1) { + send_size = end + 1 - ofs - enc_begin_skip; + } + res = next->handle_data(data, enc_begin_skip, send_size); + enc_begin_skip = 0; + ofs += aligned_size; + cache.splice(0, aligned_size); + } + return res; +} + +/** + * flush remainder of data to output + */ +int RGWGetObj_BlockDecrypt::flush() { + int res = 0; + size_t part_ofs = ofs; + size_t i = 0; + while (i parts_len[i])) { + part_ofs -= parts_len[i]; + i++; + } + if (cache.length() > 0) { + bufferlist data; + if (! crypt->decrypt(cache, 0, cache.length(), data, part_ofs) ) { + return -ERR_INTERNAL_ERROR; + } + off_t send_size = cache.length() - enc_begin_skip; + if (ofs + enc_begin_skip + send_size > end + 1) { + send_size = end + 1 - ofs - enc_begin_skip; + } + res = next->handle_data(data, enc_begin_skip, send_size); + enc_begin_skip = 0; + ofs += send_size; + } + return res; +} + +RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext* cct, + RGWPutObjDataProcessor* next, + std::unique_ptr crypt): + RGWPutObj_Filter(next), + cct(cct), + crypt(std::move(crypt)), + ofs(0), + cache() +{ + block_size = this->crypt->get_block_size(); +} + +RGWPutObj_BlockEncrypt::~RGWPutObj_BlockEncrypt() { +} + +int RGWPutObj_BlockEncrypt::handle_data(bufferlist& bl, + off_t in_ofs, + void **phandle, + rgw_raw_obj *pobj, + bool *again) { + int res = 0; + ldout(cct, 25) << "Encrypt " << bl.length() << " bytes" << dendl; + + if (*again) { + bufferlist no_data; + res = next->handle_data(no_data, in_ofs, phandle, pobj, again); + //if *again is not set to false, we will have endless loop + //drop info on log + if (*again) { + ldout(cct, 20) << "*again==true" << dendl; + } + return res; + } + + cache.append(bl); + off_t proc_size = cache.length() & ~(block_size - 1); + if (bl.length() == 0) { + proc_size = cache.length(); + } + if (proc_size > 0) { + bufferlist data; + if (! crypt->encrypt(cache, 0, proc_size, data, ofs) ) { + return -ERR_INTERNAL_ERROR; + } + res = next->handle_data(data, ofs, phandle, pobj, again); + ofs += proc_size; + cache.splice(0, proc_size); + if (res < 0) + return res; + } + + if (bl.length() == 0) { + /*replicate 0-sized handle_data*/ + res = next->handle_data(bl, ofs, phandle, pobj, again); + } + return res; +} + +int RGWPutObj_BlockEncrypt::throttle_data(void *handle, + const rgw_raw_obj& obj, + uint64_t size, + bool need_to_wait) { + return next->throttle_data(handle, obj, size, need_to_wait); +} + +std::string create_random_key_selector(CephContext * const cct) { + char random[AES_256_KEYSIZE]; + if (get_random_bytes(&random[0], sizeof(random)) != 0) { + ldout(cct, 0) << "ERROR: cannot get_random_bytes. " << dendl; + for (char& v:random) v=rand(); + } + return std::string(random, sizeof(random)); +} + +static int get_barbican_url(CephContext * const cct, + std::string& url) +{ + url = cct->_conf->rgw_barbican_url; + if (url.empty()) { + ldout(cct, 0) << "ERROR: conf rgw_barbican_url is not set" << dendl; + return -EINVAL; + } + + if (url.back() != '/') { + url.append("/"); + } + + return 0; +} + +static int request_key_from_barbican(CephContext *cct, + boost::string_view key_id, + boost::string_view key_selector, + const std::string& barbican_token, + std::string& actual_key) { + std::string secret_url; + int res; + res = get_barbican_url(cct, secret_url); + if (res < 0) { + return res; + } + secret_url += "v1/secrets/" + std::string(key_id); + + bufferlist secret_bl; + RGWHTTPTransceiver secret_req(cct, &secret_bl); + secret_req.append_header("Accept", "application/octet-stream"); + secret_req.append_header("X-Auth-Token", barbican_token); + + res = secret_req.process("GET", secret_url.c_str()); + if (res < 0) { + return res; + } + if (secret_req.get_http_status() == + RGWHTTPTransceiver::HTTP_STATUS_UNAUTHORIZED) { + return -EACCES; + } + + if (secret_req.get_http_status() >=200 && + secret_req.get_http_status() < 300 && + secret_bl.length() == AES_256_KEYSIZE) { + actual_key.assign(secret_bl.c_str(), secret_bl.length()); + memset(secret_bl.c_str(), 0, secret_bl.length()); + } else { + res = -EACCES; + } + return res; +} + +static map get_str_map(const string &str) { + map m; + get_str_map(str, &m, ";, \t"); + return m; +} + +static int get_actual_key_from_kms(CephContext *cct, + boost::string_view key_id, + boost::string_view key_selector, + std::string& actual_key) +{ + int res = 0; + ldout(cct, 20) << "Getting KMS encryption key for key=" << key_id << dendl; + static map str_map = get_str_map( + cct->_conf->rgw_crypt_s3_kms_encryption_keys); + + map::iterator it = str_map.find(std::string(key_id)); + if (it != str_map.end() ) { + std::string master_key; + try { + master_key = from_base64((*it).second); + } catch (...) { + ldout(cct, 5) << "ERROR: get_actual_key_from_kms invalid encryption key id " + << "which contains character that is not base64 encoded." + << dendl; + return -EINVAL; + } + + if (master_key.length() == AES_256_KEYSIZE) { + uint8_t _actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt(cct, + reinterpret_cast(master_key.c_str()), AES_256_KEYSIZE, + reinterpret_cast(key_selector.data()), + _actual_key, AES_256_KEYSIZE)) { + actual_key = std::string((char*)&_actual_key[0], AES_256_KEYSIZE); + } else { + res = -EIO; + } + memset(_actual_key, 0, sizeof(_actual_key)); + } else { + ldout(cct, 20) << "Wrong size for key=" << key_id << dendl; + res = -EIO; + } + } else { + std::string token; + if (rgw::keystone::Service::get_keystone_barbican_token(cct, token) < 0) { + ldout(cct, 5) << "Failed to retrieve token for barbican" << dendl; + res = -EINVAL; + return res; + } + + res = request_key_from_barbican(cct, key_id, key_selector, token, actual_key); + if (res != 0) { + ldout(cct, 5) << "Failed to retrieve secret from barbican:" << key_id << dendl; + } + } + return res; +} + +static inline void set_attr(map& attrs, + const char* key, + boost::string_view value) +{ + bufferlist bl; + bl.append(value.data(), value.size()); + attrs[key] = std::move(bl); +} + +static inline std::string get_str_attribute(map& attrs, + const char *name) +{ + auto iter = attrs.find(name); + if (iter == attrs.end()) { + return {}; + } + return iter->second.to_str(); +} + +typedef enum { + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM=0, + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, + X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, + X_AMZ_SERVER_SIDE_ENCRYPTION, + X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID, + X_AMZ_SERVER_SIDE_ENCRYPTION_LAST +} crypt_option_e; + +typedef struct { + const char* http_header_name; + const std::string post_part_name; +} crypt_option_names; + +static const crypt_option_names crypt_options[] = { + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", "x-amz-server-side-encryption-customer-algorithm"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "x-amz-server-side-encryption-customer-key"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", "x-amz-server-side-encryption-customer-key-md5"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", "x-amz-server-side-encryption"}, + {"HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID", "x-amz-server-side-encryption-aws-kms-key-id"}, +}; + +static boost::string_view get_crypt_attribute( + const RGWEnv* env, + std::map* parts, + crypt_option_e option) +{ + static_assert( + X_AMZ_SERVER_SIDE_ENCRYPTION_LAST == sizeof(crypt_options)/sizeof(*crypt_options), + "Missing items in crypt_options"); + if (parts != nullptr) { + auto iter + = parts->find(crypt_options[option].post_part_name); + if (iter == parts->end()) + return boost::string_view(); + bufferlist& data = iter->second.data; + boost::string_view str = boost::string_view(data.c_str(), data.length()); + return rgw_trim_whitespace(str); + } else { + const char* hdr = env->get(crypt_options[option].http_header_name, nullptr); + if (hdr != nullptr) { + return boost::string_view(hdr); + } else { + return boost::string_view(); + } + } +} + + +int rgw_s3_prepare_encrypt(struct req_state* s, + std::map& attrs, + std::map* parts, + std::unique_ptr* block_crypt, + std::map& crypt_http_responses) +{ + int res = 0; + crypt_http_responses.clear(); + { + boost::string_view req_sse_ca = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM); + if (! req_sse_ca.empty()) { + if (req_sse_ca != "AES256") { + ldout(s->cct, 5) << "ERROR: Invalid value for header " + << "x-amz-server-side-encryption-customer-algorithm" + << dendl; + s->err.message = "The requested encryption algorithm is not valid, must be AES256."; + return -ERR_INVALID_ENCRYPTION_ALGORITHM; + } + if (s->cct->_conf->rgw_crypt_require_ssl && + !s->info.env->exists("SERVER_PORT_SECURE")) { + ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + + std::string key_bin; + try { + key_bin = from_base64( + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY) ); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption " + << "key which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { + ldout(s->cct, 5) << "ERROR: invalid encryption key size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + boost::string_view keymd5 = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5); + + std::string keymd5_bin; + try { + keymd5_bin = from_base64(keymd5); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid encryption key " + << "md5 which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { + ldout(s->cct, 5) << "ERROR: Invalid key md5 size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + MD5 key_hash; + byte key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; + key_hash.Update(reinterpret_cast(key_bin.c_str()), key_bin.size()); + key_hash.Final(key_hash_res); + + if (memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) { + ldout(s->cct, 5) << "ERROR: Invalid key md5 hash" << dendl; + s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; + return -EINVAL; + } + + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-C-AES256"); + set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin); + + if (block_crypt) { + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(reinterpret_cast(key_bin.c_str()), AES_256_KEYSIZE); + *block_crypt = std::move(aes); + } + + crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; + crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5.to_string(); + return 0; + } else { + boost::string_view customer_key = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY); + if (!customer_key.empty()) { + ldout(s->cct, 5) << "ERROR: SSE-C encryption request is missing the header " + << "x-amz-server-side-encryption-customer-algorithm" + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide a valid encryption algorithm."; + return -EINVAL; + } + + boost::string_view customer_key_md5 = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5); + if (!customer_key_md5.empty()) { + ldout(s->cct, 5) << "ERROR: SSE-C encryption request is missing the header " + << "x-amz-server-side-encryption-customer-algorithm" + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide a valid encryption algorithm."; + return -EINVAL; + } + } + + /* AMAZON server side encryption with KMS (key management service) */ + boost::string_view req_sse = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION); + if (! req_sse.empty()) { + if (req_sse != "aws:kms") { + ldout(s->cct, 5) << "ERROR: Invalid value for header x-amz-server-side-encryption" + << dendl; + s->err.message = "Server Side Encryption with KMS managed key requires " + "HTTP header x-amz-server-side-encryption : aws:kms"; + return -EINVAL; + } + if (s->cct->_conf->rgw_crypt_require_ssl && + !s->info.env->exists("SERVER_PORT_SECURE")) { + ldout(s->cct, 5) << "ERROR: insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + boost::string_view key_id = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID); + if (key_id.empty()) { + ldout(s->cct, 5) << "ERROR: not provide a valid key id" << dendl; + s->err.message = "Server Side Encryption with KMS managed key requires " + "HTTP header x-amz-server-side-encryption-aws-kms-key-id"; + return -ERR_INVALID_ACCESS_KEY; + } + /* try to retrieve actual key */ + std::string key_selector = create_random_key_selector(s->cct); + std::string actual_key; + res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); + if (res != 0) { + ldout(s->cct, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl; + s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id.to_string(); + return res; + } + if (actual_key.size() != AES_256_KEYSIZE) { + ldout(s->cct, 5) << "ERROR: key obtained from key_id:" << + key_id << " is not 256 bit size" << dendl; + s->err.message = "KMS provided an invalid key for the given kms-keyid."; + return -ERR_INVALID_ACCESS_KEY; + } + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "SSE-KMS"); + set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id); + set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); + + if (block_crypt) { + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(reinterpret_cast(actual_key.c_str()), AES_256_KEYSIZE); + *block_crypt = std::move(aes); + } + actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); + + crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; + crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id.to_string(); + return 0; + } else { + boost::string_view key_id = + get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID); + if (!key_id.empty()) { + ldout(s->cct, 5) << "ERROR: SSE-KMS encryption request is missing the header " + << "x-amz-server-side-encryption" + << dendl; + s->err.message = "Server Side Encryption with KMS managed key requires " + "HTTP header x-amz-server-side-encryption : aws:kms"; + return -EINVAL; + } + } + + /* no other encryption mode, check if default encryption is selected */ + if (s->cct->_conf->rgw_crypt_default_encryption_key != "") { + std::string master_encryption_key; + try { + master_encryption_key = from_base64(s->cct->_conf->rgw_crypt_default_encryption_key); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_encrypt invalid default encryption key " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + if (master_encryption_key.size() != 256 / 8) { + ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; + /* not an error to return; missing encryption does not inhibit processing */ + return 0; + } + + set_attr(attrs, RGW_ATTR_CRYPT_MODE, "RGW-AUTO"); + std::string key_selector = create_random_key_selector(s->cct); + set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector); + + uint8_t actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt(s->cct, + reinterpret_cast(master_encryption_key.c_str()), AES_256_KEYSIZE, + reinterpret_cast(key_selector.c_str()), + actual_key, AES_256_KEYSIZE) != true) { + memset(actual_key, 0, sizeof(actual_key)); + return -EIO; + } + if (block_crypt) { + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(reinterpret_cast(actual_key), AES_256_KEYSIZE); + *block_crypt = std::move(aes); + } + memset(actual_key, 0, sizeof(actual_key)); + return 0; + } + } + /*no encryption*/ + return 0; +} + + +int rgw_s3_prepare_decrypt(struct req_state* s, + map& attrs, + std::unique_ptr* block_crypt, + std::map& crypt_http_responses) +{ + int res = 0; + std::string stored_mode = get_str_attribute(attrs, RGW_ATTR_CRYPT_MODE); + ldout(s->cct, 15) << "Encryption mode: " << stored_mode << dendl; + + const char *req_sse = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION", NULL); + if (nullptr != req_sse && (s->op == OP_GET || s->op == OP_HEAD)) { + return -ERR_INVALID_REQUEST; + } + + if (stored_mode == "SSE-C-AES256") { + if (s->cct->_conf->rgw_crypt_require_ssl && + !s->info.env->exists("SERVER_PORT_SECURE")) { + ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + const char *req_cust_alg = + s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL); + + if (nullptr == req_cust_alg) { + ldout(s->cct, 5) << "ERROR: Request for SSE-C encrypted object missing " + << "x-amz-server-side-encryption-customer-algorithm" + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide a valid encryption algorithm."; + return -EINVAL; + } else if (strcmp(req_cust_alg, "AES256") != 0) { + ldout(s->cct, 5) << "ERROR: The requested encryption algorithm is not valid, must be AES256." << dendl; + s->err.message = "The requested encryption algorithm is not valid, must be AES256."; + return -ERR_INVALID_ENCRYPTION_ALGORITHM; + } + + std::string key_bin; + try { + key_bin = from_base64(s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY", "")); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + if (key_bin.size() != AES_256_CBC::AES_256_KEYSIZE) { + ldout(s->cct, 5) << "ERROR: Invalid encryption key size" << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key."; + return -EINVAL; + } + + std::string keymd5 = + s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5", ""); + std::string keymd5_bin; + try { + keymd5_bin = from_base64(keymd5); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid encryption key md5 " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + + if (keymd5_bin.size() != CEPH_CRYPTO_MD5_DIGESTSIZE) { + ldout(s->cct, 5) << "ERROR: Invalid key md5 size " << dendl; + s->err.message = "Requests specifying Server Side Encryption with Customer " + "provided keys must provide an appropriate secret key md5."; + return -EINVAL; + } + + MD5 key_hash; + uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE]; + key_hash.Update(reinterpret_cast(key_bin.c_str()), key_bin.size()); + key_hash.Final(key_hash_res); + + if ((memcmp(key_hash_res, keymd5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) || + (get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYMD5) != keymd5_bin)) { + s->err.message = "The calculated MD5 hash of the key did not match the hash that was provided."; + return -EINVAL; + } + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(reinterpret_cast(key_bin.c_str()), AES_256_CBC::AES_256_KEYSIZE); + if (block_crypt) *block_crypt = std::move(aes); + + crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256"; + crypt_http_responses["x-amz-server-side-encryption-customer-key-MD5"] = keymd5; + return 0; + } + + if (stored_mode == "SSE-KMS") { + if (s->cct->_conf->rgw_crypt_require_ssl && + !s->info.env->exists("SERVER_PORT_SECURE")) { + ldout(s->cct, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl; + return -ERR_INVALID_REQUEST; + } + /* try to retrieve actual key */ + std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID); + std::string key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); + std::string actual_key; + res = get_actual_key_from_kms(s->cct, key_id, key_selector, actual_key); + if (res != 0) { + ldout(s->cct, 10) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl; + s->err.message = "Failed to retrieve the actual key, kms-keyid: " + key_id; + return res; + } + if (actual_key.size() != AES_256_KEYSIZE) { + ldout(s->cct, 0) << "ERROR: key obtained from key_id:" << + key_id << " is not 256 bit size" << dendl; + s->err.message = "KMS provided an invalid key for the given kms-keyid."; + return -ERR_INVALID_ACCESS_KEY; + } + + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(reinterpret_cast(actual_key.c_str()), AES_256_KEYSIZE); + actual_key.replace(0, actual_key.length(), actual_key.length(), '\000'); + if (block_crypt) *block_crypt = std::move(aes); + + crypt_http_responses["x-amz-server-side-encryption"] = "aws:kms"; + crypt_http_responses["x-amz-server-side-encryption-aws-kms-key-id"] = key_id; + return 0; + } + + if (stored_mode == "RGW-AUTO") { + std::string master_encryption_key; + try { + master_encryption_key = from_base64(std::string(s->cct->_conf->rgw_crypt_default_encryption_key)); + } catch (...) { + ldout(s->cct, 5) << "ERROR: rgw_s3_prepare_decrypt invalid default encryption key " + << "which contains character that is not base64 encoded." + << dendl; + s->err.message = "The default encryption key is not valid base64."; + return -EINVAL; + } + + if (master_encryption_key.size() != 256 / 8) { + ldout(s->cct, 0) << "ERROR: failed to decode 'rgw crypt default encryption key' to 256 bit string" << dendl; + return -EIO; + } + std::string attr_key_selector = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYSEL); + if (attr_key_selector.size() != AES_256_CBC::AES_256_KEYSIZE) { + ldout(s->cct, 0) << "ERROR: missing or invalid " RGW_ATTR_CRYPT_KEYSEL << dendl; + return -EIO; + } + uint8_t actual_key[AES_256_KEYSIZE]; + if (AES_256_ECB_encrypt(s->cct, + reinterpret_cast(master_encryption_key.c_str()), + AES_256_KEYSIZE, + reinterpret_cast(attr_key_selector.c_str()), + actual_key, AES_256_KEYSIZE) != true) { + memset(actual_key, 0, sizeof(actual_key)); + return -EIO; + } + auto aes = std::unique_ptr(new AES_256_CBC(s->cct)); + aes->set_key(actual_key, AES_256_KEYSIZE); + memset(actual_key, 0, sizeof(actual_key)); + if (block_crypt) *block_crypt = std::move(aes); + return 0; + } + /*no decryption*/ + return 0; +}