+++ /dev/null
-// -*- 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 <rgw/rgw_op.h>
-#include <rgw/rgw_crypt.h>
-#include <auth/Crypto.h>
-#include <rgw/rgw_b64.h>
-#include <rgw/rgw_rest_s3.h>
-#include "include/assert.h"
-#include <boost/utility/string_view.hpp>
-#include <rgw/rgw_keystone.h>
-#include "include/str_map.h"
-#include "crypto/crypto_accel.h"
-#include "crypto/crypto_plugin.h"
-#ifdef USE_NSS
-# include <nspr.h>
-# include <nss.h>
-# include <pk11pub.h>
-#endif
-
-#ifdef USE_CRYPTOPP
-#include <cryptopp/cryptlib.h>
-#include <cryptopp/modes.h>
-#include <cryptopp/aes.h>
-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<off_t>(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<unsigned char*>(&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<CryptoPlugin*>(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<unsigned char*>(&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<bool> 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<unsigned char*>(buf.c_str());
- const unsigned char* input_raw = reinterpret_cast<const unsigned char*>(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<unsigned char*>(buf.c_str());
- unsigned char* input_raw = reinterpret_cast<unsigned char*>(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<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len)
-{
- auto cbc = std::unique_ptr<AES_256_CBC>(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<uint8_t*>(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<BlockCrypt> 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<parts_len.size(); i++) {
- ldout(cct, 20) << "Manifest part " << i << ", size=" << parts_len[i] << dendl;
- }
- }
- }
- return 0;
-}
-
-int RGWGetObj_BlockDecrypt::fixup_range(off_t& bl_ofs, off_t& bl_end) {
- off_t inp_ofs = bl_ofs;
- off_t inp_end = bl_end;
- if (parts_len.size() > 0) {
- off_t in_ofs = bl_ofs;
- off_t in_end = bl_end;
-
- size_t i = 0;
- while (i<parts_len.size() && (in_ofs > (off_t)parts_len[i])) {
- in_ofs -= parts_len[i];
- i++;
- }
- //in_ofs is inside block i
- size_t j = 0;
- while (j<parts_len.size() && (in_end > (off_t)parts_len[j])) {
- in_end -= parts_len[j];
- j++;
- }
- //in_end is inside block j
-
- size_t rounded_end;
- rounded_end = ( in_end & ~(block_size - 1) ) + (block_size - 1);
- if (rounded_end + 1 >= parts_len[j]) {
- rounded_end = parts_len[j] - 1;
- }
-
- enc_begin_skip = in_ofs & (block_size - 1);
- ofs = bl_ofs - enc_begin_skip;
- end = bl_end;
- bl_ofs = bl_ofs - enc_begin_skip;
- bl_end += rounded_end - in_end;
- }
- else
- {
- enc_begin_skip = bl_ofs & (block_size - 1);
- ofs = bl_ofs & ~(block_size - 1);
- end = bl_end;
- bl_ofs = bl_ofs & ~(block_size - 1);
- bl_end = ( bl_end & ~(block_size - 1) ) + (block_size - 1);
- }
- ldout(cct, 20) << "fixup_range [" << inp_ofs << "," << inp_end
- << "] => [" << 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.size() && (part_ofs >= 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.size() && (part_ofs > 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<BlockCrypt> 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<string,string> get_str_map(const string &str) {
- map<string,string> 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<string,string> str_map = get_str_map(
- cct->_conf->rgw_crypt_s3_kms_encryption_keys);
-
- map<string, string>::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<const uint8_t*>(master_key.c_str()), AES_256_KEYSIZE,
- reinterpret_cast<const uint8_t*>(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<string, bufferlist>& 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<string, bufferlist>& 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<std::string,
- RGWPostObj_ObjStore::post_form_part,
- const ltstr_nocase>* 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<std::string, ceph::bufferlist>& attrs,
- std::map<std::string,
- RGWPostObj_ObjStore::post_form_part,
- const ltstr_nocase>* parts,
- std::unique_ptr<BlockCrypt>* block_crypt,
- std::map<std::string, std::string>& 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<const byte*>(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<AES_256_CBC>(new AES_256_CBC(s->cct));
- aes->set_key(reinterpret_cast<const uint8_t*>(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<AES_256_CBC>(new AES_256_CBC(s->cct));
- aes->set_key(reinterpret_cast<const uint8_t*>(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<const uint8_t*>(master_encryption_key.c_str()), AES_256_KEYSIZE,
- reinterpret_cast<const uint8_t*>(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<AES_256_CBC>(new AES_256_CBC(s->cct));
- aes->set_key(reinterpret_cast<const uint8_t*>(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<string, bufferlist>& attrs,
- std::unique_ptr<BlockCrypt>* block_crypt,
- std::map<std::string, std::string>& 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<const byte*>(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<AES_256_CBC>(new AES_256_CBC(s->cct));
- aes->set_key(reinterpret_cast<const uint8_t*>(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<AES_256_CBC>(new AES_256_CBC(s->cct));
- aes->set_key(reinterpret_cast<const uint8_t*>(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<const uint8_t*>(master_encryption_key.c_str()),
- AES_256_KEYSIZE,
- reinterpret_cast<const uint8_t*>(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<AES_256_CBC>(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;
-}