Fixed bugs that appeared on unit tests. Added unit tests.
These are created to improve code coverage achieved using s3-tests.
Added comments and improved formatting.
Fixed improper processing when last 0-15 bytes are decoded alone.
Fixed problem with PUT with x-amz-copy-source when source object is compressed.
Fixed problem with not decrypting data on S3 copy operation
Fixed problem with syncing when object is compressed and encrypted at the same time.
Fixed retrieving original length when compression is on.
Signed-off-by: Adam Kupczyk <akupczyk@mirantis.com>
off_t cnt = std::min((off_t)(iter->length() - plaintext_pos), (off_t)(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++) {
+ for (off_t i = 0; i < cnt; i++) {
dst[i] ^= src[i];
}
++iter;
#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);
}
/**
- * Encryption in CBC mode. Chunked to 4K blocks. offset is used as IV for 4K block.
+ * 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
*/
+
class AES_256_CBC : public BlockCrypt {
public:
static const size_t AES_256_KEYSIZE = 256 / 8;
#ifdef USE_CRYPTOPP
- bool cbc_transform(unsigned char* out, const unsigned char* in, size_t size,
+ 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 encrypt)
+ {
if (encrypt) {
CBC_Mode< AES >::Encryption e;
e.SetKeyWithIV(key, AES_256_KEYSIZE, iv, AES_256_IVSIZE);
#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 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;
#error Must define USE_CRYPTOPP or USE_NSS
#endif
-
-
- bool cbc_transform(unsigned char* out, const unsigned char* in, size_t size,
+ 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) {
+ bool encrypt)
+ {
static CryptoAccelRef crypto_accel = get_crypto_accel(cct);
- //compressor(Compressor::create(c, c->_conf->async_compressor_type))
bool result = true;
unsigned char iv[AES_256_IVSIZE];
for (size_t offset = 0; result && (offset < size); offset += CHUNK_SIZE) {
return result;
}
- bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset)
+
+ 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());
unsigned char* input_raw = reinterpret_cast<unsigned char*>(input.c_str());
- unsigned char iv[AES_256_IVSIZE];
+
+ /* 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)) {
- if (aligned_size > 0) {
- /*use last chunk for unaligned part*/
- static_assert(sizeof(iv) >= AES_256_IVSIZE, "Must fit counter");
- memset(iv, 0, AES_256_IVSIZE);
- result = cbc_transform(
- buf_raw + aligned_size,
- buf_raw + aligned_size - AES_256_IVSIZE,
- AES_256_IVSIZE,
- iv, 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 {
- /*use IV as base for unaligned part*/
- unsigned char fake_iv[AES_256_IVSIZE] = {0};
- prepare_iv(iv, stream_offset);
- result = cbc_transform(
- buf_raw + aligned_size,
- iv,
- AES_256_IVSIZE,
- fake_iv, key, true);
+ /* 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++) {
return result;
}
- bool decrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset)
+
+ 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());
- unsigned char iv[AES_256_IVSIZE];
- if (aligned_size > 0) {
- if (unaligned_rest_size == 0) {
- /* size of data is just perfect */
- prepare_iv(iv, stream_offset);
- result = cbc_transform(
- (unsigned char*)buf.raw_c_str(),
- (unsigned char*)input.c_str() + in_ofs,
- aligned_size,
- stream_offset, key, false);
- } else {
+ /* 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*/
- static_assert(sizeof(iv) >= AES_256_IVSIZE, "Must fit counter");
- memset(iv, 0, AES_256_IVSIZE);
- result = cbc_transform(
- buf_raw + aligned_size,
- input_raw + in_ofs + aligned_size - AES_256_IVSIZE,
- AES_256_IVSIZE,
- iv, key, true);
- if (result) {
- prepare_iv(iv, stream_offset);
- result = cbc_transform(buf_raw,
- input_raw + in_ofs,
- aligned_size,
- stream_offset, key, false);
- }
+ 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);
}
- } else {
- /*0 full blocks, use IV as base for unaligned part*/
- unsigned char fake_iv[AES_256_IVSIZE] = {0};
- prepare_iv(iv, stream_offset);
- result = cbc_transform(
- buf_raw + aligned_size,
- iv,
- AES_256_IVSIZE,
- fake_iv, key, true);
- }
- if (result) {
- for(size_t i = aligned_size; i < size; i++) {
- *(buf_raw + i) ^= *(input_raw + in_ofs + i);
+ if (result) {
+ for(size_t i = aligned_size; i < size; i++) {
+ *(buf_raw + i) ^= *(input_raw + in_ofs + i);
+ }
}
}
if (result) {
}
- void prepare_iv(byte iv[AES_256_IVSIZE], off_t 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;
}
};
+
+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, uint8_t* key, size_t key_size, uint8_t* data_in, uint8_t* data_out, size_t data_size) {
+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 {
#endif
-
-
-
-RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext* cct, RGWGetDataCB* next, BlockCrypt* crypt):
+RGWGetObj_BlockDecrypt::RGWGetObj_BlockDecrypt(CephContext* cct,
+ RGWGetDataCB* next,
+ std::unique_ptr<BlockCrypt> crypt):
RGWGetObj_Filter(next),
cct(cct),
- crypt(crypt),
- enc_begin_skip(0), ofs(0), end(0), cache() {
- block_size = crypt->get_block_size();
- }
+ crypt(std::move(crypt)),
+ enc_begin_skip(0),
+ ofs(0),
+ end(0),
+ cache()
+{
+ block_size = this->crypt->get_block_size();
+}
-RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {}
+RGWGetObj_BlockDecrypt::~RGWGetObj_BlockDecrypt() {
+}
int RGWGetObj_BlockDecrypt::read_manifest(bufferlist& manifest_bl) {
parts_len.clear();
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;
+ 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])) {
else
{
enc_begin_skip = bl_ofs & (block_size - 1);
- ofs=bl_ofs & ~(block_size - 1);
- end=bl_end;
+ 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);
}
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;
bufferlist data;
crypt->decrypt(cache, 0, block_size, data, part_ofs);
part_ofs += block_size;
- res = next->handle_data(data, enc_begin_skip, block_size - enc_begin_skip);
+
+ off_t send_size = block_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;
cache.clear();
ofs += block_size;
if (bl_len > 0) {
off_t aligned_size = bl_len & ~(block_size - 1);
//save remainder
- off_t remainder = bl.length() - (bl_ofs + aligned_size);
+ off_t remainder = bl_len - aligned_size;
if(remainder > 0) {
cache.append(bl.get_contiguous(bl_ofs + aligned_size, remainder), remainder);
}
-
if (aligned_size > 0) {
bufferlist data;
crypt->decrypt(bl, bl_ofs, aligned_size, data, part_ofs);
res = next->handle_data(data, enc_begin_skip, send_size);
enc_begin_skip = 0;
ofs += aligned_size;
-
if (res != 0)
return res;
}
return res;
}
-RGWPutObj_BlockEncrypt::RGWPutObj_BlockEncrypt(CephContext* cct, RGWPutObjDataProcessor* next, BlockCrypt* crypt):
- RGWPutObj_Filter(next), cct(cct), crypt(crypt),
- ofs(0), cache() {
- block_size = crypt->get_block_size();
+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() {
- delete crypt;
}
int RGWPutObj_BlockEncrypt::handle_data(bufferlist& bl, off_t in_ofs, void **phandle, rgw_obj *pobj, bool *again) {
/*flush cached data*/
bufferlist data;
crypt->encrypt(cache, 0, cache.length(), data, ofs);
- res=next->handle_data(data, ofs, phandle, pobj, again);
- ofs+=cache.length();
+ res = next->handle_data(data, ofs, phandle, pobj, again);
+ ofs += cache.length();
cache.clear();
if (res != 0)
return res;
}
/*replicate 0-sized handle_data*/
- res=next->handle_data(cache, ofs, phandle, pobj, again);
+ res = next->handle_data(cache, ofs, phandle, pobj, again);
}
return res;
}
return res;
}
-int get_actual_key_from_kms(CephContext *cct, boost::string_ref key_id, boost::string_ref key_selector, std::string& actual_key)
+int get_actual_key_from_kms(CephContext *cct,
+ boost::string_ref key_id,
+ boost::string_ref key_selector,
+ std::string& actual_key)
{
int res = 0;
ldout(cct, 20) << "Getting KMS encryption key for key=" << key_id << dendl;
return src.substr(start, end - start + 1);
}
+
static boost::string_ref get_crypt_attribute(RGWEnv* env,
map<string, post_form_part, const ltstr_nocase>* parts,
crypt_option_e option)
}
}
+
int s3_prepare_encrypt(struct req_state* s,
map<string, bufferlist>& attrs,
map<string, post_form_part, const ltstr_nocase>* parts,
- BlockCrypt** block_crypt,
+ std::unique_ptr<BlockCrypt>* block_crypt,
std::map<std::string, std::string>& crypt_http_responses)
{
int res = 0;
crypt_http_responses.clear();
- if (block_crypt) *block_crypt = nullptr;
{
boost::string_ref req_sse_ca =
get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM);
set_attr(attrs, RGW_ATTR_CRYPT_KEYMD5, keymd5_bin);
if (block_crypt) {
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
+ 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 = aes;
+ *block_crypt = std::move(aes);
}
crypt_http_responses["x-amz-server-side-encryption-customer-algorithm"] = "AES256";
set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
if (block_crypt) {
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
+ 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 = aes;
+ *block_crypt = std::move(aes);
}
goto done;
}
goto done;
}
if (block_crypt) {
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
- aes->set_key(actual_key, AES_256_KEYSIZE);
- *block_crypt = aes;
+ 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);
}
goto done;
return res;
}
-int s3_prepare_decrypt(
- struct req_state* s,
- map<string, bufferlist>& attrs,
- BlockCrypt** block_crypt,
- std::map<std::string, std::string>& crypt_http_responses)
+
+int 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;
-
if (stored_mode == "SSE-C-AES256") {
const char *req_cust_alg = s->info.env->get("HTTP_X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM", NULL);
+
if ((nullptr == req_cust_alg) || (strcmp(req_cust_alg, "AES256") != 0)) {
res = -ERR_INVALID_REQUEST;
goto done;
res = -ERR_INVALID_DIGEST;
goto done;
}
+
MD5 key_hash;
uint8_t key_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
key_hash.Update((uint8_t*)key_bin.c_str(), key_bin.size());
res = -ERR_INVALID_DIGEST;
goto done;
}
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
+ auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
aes->set_key((uint8_t*)key_bin.c_str(), AES_256_CBC::AES_256_KEYSIZE);
- if (block_crypt) *block_crypt = aes;
+ 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;
goto done;
}
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
+ 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);
- if (block_crypt) *block_crypt = aes;
+ 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;
res = -EIO;
goto done;
}
- AES_256_CBC* aes = new AES_256_CBC(s->cct);
+ auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
aes->set_key(actual_key, AES_256_KEYSIZE);
-
- if (block_crypt) *block_crypt = aes;
+ if (block_crypt) *block_crypt = std::move(aes);
goto done;
}
#include <rgw/rgw_op.h>
#include <rgw/rgw_rest_s3.h>
#include <boost/utility/string_ref.hpp>
-
+/**
+ * \brief Interface for block encryption methods
+ *
+ * Encrypts and decrypts data.
+ * Operations are performed in context of larger stream being divided into blocks.
+ * Each block can be processed independently, but only as a whole.
+ * Part block cannot be properly processed.
+ * Each request must start on block-aligned offset.
+ * Each request should have length that is multiply of block size.
+ * Request with unaligned length is only acceptable for last part of stream.
+ */
class BlockCrypt {
public:
BlockCrypt(){};
virtual ~BlockCrypt(){};
+
/**
* Determines size of encryption block.
- * This usually is multiply of key size.
+ * This is usually multiply of key size.
* It determines size of chunks that should be passed to \ref encrypt and \ref decrypt.
*/
virtual size_t get_block_size() = 0;
+
/**
- * Encrypts packet of data.
- * This is basic encryption of wider stream of data.
- * Offset shows location of <src,src+size) in stream. Offset must be multiply of basic block size \ref get_block_size.
- * Usually size is also multiply of \ref get_block_size, unless encrypting last part of stream.
- * Src and dst may be equal (in place encryption), but otherwise <src,src+size) and <dst,dst+size) may not overlap.
+ * Encrypts data.
+ * Argument \ref stream_offset shows where in generalized stream chunk is located.
+ * Input for encryption is \ref input buffer, with relevant data in range <in_ofs, in_ofs+size).
+ * \ref input and \output may not be the same buffer.
*
* \params
- * src - source of data
- * size - size of data
- * dst - destination to encrypt to
- * offset - location of <src,src+size) chunk in data stream
+ * input - source buffer of data
+ * in_ofs - offset of chunk inside input
+ * size - size of chunk, must be chunk-aligned unless last part is processed
+ * output - destination buffer to encrypt to
+ * stream_offset - location of <in_ofs,in_ofs+size) chunk in data stream, must be chunk-aligned
+ * \return true iff successfully encrypted
*/
- virtual bool encrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) = 0;
+ virtual bool encrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) = 0;
+
/**
- * Decrypts packet of data.
- * This is basic decryption of wider stream of data.
- * Offset shows location of <src,src+size) in stream. Offset must be multiply of basic block size \ref get_block_size.
- * Usually size is also multiply of \ref get_block_size, unless decrypting last part of stream.
- * Src and dst may be equal (in place encryption), but otherwise <src,src+size) and <dst,dst+size) may not overlap.
+ * Decrypts data.
+ * Argument \ref stream_offset shows where in generalized stream chunk is located.
+ * Input for decryption is \ref input buffer, with relevant data in range <in_ofs, in_ofs+size).
+ * \ref input and \output may not be the same buffer.
*
* \params
- * src - source of data
- * size - size of data
- * dst - destination to decrypt to
- * offset - location of <src,src+size) chunk in data stream
+ * input - source buffer of data
+ * in_ofs - offset of chunk inside input
+ * size - size of chunk, must be chunk-aligned unless last part is processed
+ * output - destination buffer to encrypt to
+ * stream_offset - location of <in_ofs,in_ofs+size) chunk in data stream, must be chunk-aligned
+ * \return true iff successfully encrypted
*/
- virtual bool decrypt(bufferlist& input, off_t in_ofs, size_t size, bufferlist& output, off_t stream_offset) = 0;
+ virtual bool decrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) = 0;
};
static const size_t AES_256_KEYSIZE = 256 / 8;
-bool AES_256_ECB_encrypt(uint8_t* key, size_t key_size, uint8_t* data_in, uint8_t* data_out, size_t data_size);
+bool AES_256_ECB_encrypt(const uint8_t* key,
+ size_t key_size,
+ uint8_t* data_in,
+ uint8_t* data_out,
+ size_t data_size);
class RGWGetObj_BlockDecrypt : public RGWGetObj_Filter {
CephContext* cct;
- BlockCrypt* crypt;
- off_t enc_begin_skip;
- off_t ofs;
- off_t end;
- bufferlist cache;
- size_t block_size;
- std::vector<size_t> parts_len;
+
+ std::unique_ptr<BlockCrypt> crypt; /**< already configured stateless BlockCrypt
+ for operations when enough data is accumulated */
+ off_t enc_begin_skip; /**< amount of data to skip from beginning of received data */
+ off_t ofs; /**< stream offset of data we expect to show up next through \ref handle_data */
+ off_t end; /**< stream offset of last byte that is requested */
+ bufferlist cache; /**< stores extra data that could not (yet) be processed by BlockCrypt */
+ size_t block_size; /**< snapshot of \ref BlockCrypt.get_block_size() */
+ std::vector<size_t> parts_len; /**< size of parts of multipart object, parsed from manifest */
public:
- RGWGetObj_BlockDecrypt(CephContext* cct, RGWGetDataCB* next, BlockCrypt* crypt);
+ RGWGetObj_BlockDecrypt(CephContext* cct,
+ RGWGetDataCB* next,
+ std::unique_ptr<BlockCrypt> crypt);
virtual ~RGWGetObj_BlockDecrypt();
- virtual int fixup_range(off_t& bl_ofs, off_t& bl_end) override;
- virtual int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override;
+ virtual int fixup_range(off_t& bl_ofs,
+ off_t& bl_end) override;
+ virtual int handle_data(bufferlist& bl,
+ off_t bl_ofs,
+ off_t bl_len) override;
virtual int flush() override;
int read_manifest(bufferlist& manifest_bl);
}; /* RGWGetObj_BlockDecrypt */
+
class RGWPutObj_BlockEncrypt : public RGWPutObj_Filter
{
CephContext* cct;
- BlockCrypt* crypt;
- off_t ofs;
- bufferlist cache;
- size_t block_size;
+ std::unique_ptr<BlockCrypt> crypt; /**< already configured stateless BlockCrypt
+ for operations when enough data is accumulated */
+ off_t ofs; /**< stream offset of data we expect to show up next through \ref handle_data */
+ bufferlist cache; /**< stores extra data that could not (yet) be processed by BlockCrypt */
+ size_t block_size; /**< snapshot of \ref BlockCrypt.get_block_size() */
public:
- RGWPutObj_BlockEncrypt(CephContext* cct, RGWPutObjDataProcessor* next, BlockCrypt* crypt);
+ RGWPutObj_BlockEncrypt(CephContext* cct,
+ RGWPutObjDataProcessor* next,
+ std::unique_ptr<BlockCrypt> crypt);
virtual ~RGWPutObj_BlockEncrypt();
- virtual int handle_data(bufferlist& bl, off_t ofs, void **phandle, rgw_obj *pobj, bool *again) override;
- virtual int throttle_data(void *handle, const rgw_obj& obj, uint64_t size, bool need_to_wait) override;
+ virtual int handle_data(bufferlist& bl,
+ off_t ofs,
+ void **phandle,
+ rgw_obj *pobj,
+ bool *again) override;
+ virtual int throttle_data(void *handle,
+ const rgw_obj& obj,
+ uint64_t size,
+ bool need_to_wait) override;
}; /* RGWPutObj_BlockEncrypt */
-std::string create_random_key_selector();
-//int get_actual_key_from_kms(CephContext *cct, const std::string& key_id, const std::string& key_selector, std::string& actual_key);
-int get_actual_key_from_kms(CephContext *cct, boost::string_ref key_id, boost::string_ref key_selector, std::string& actual_key);
int s3_prepare_encrypt(struct req_state* s,
map<string, bufferlist>& attrs,
map<string, post_form_part, const ltstr_nocase>* parts,
- BlockCrypt** block_crypt,
+ std::unique_ptr<BlockCrypt>* block_crypt,
std::map<std::string, std::string>& crypt_http_responses);
+
int s3_prepare_decrypt(struct req_state* s,
map<string, bufferlist>& attrs,
- BlockCrypt** block_crypt,
+ std::unique_ptr<BlockCrypt>* block_crypt,
std::map<std::string, std::string>& crypt_http_responses);
-
#endif
{
Mutex::Locker l(lock);
- return find(barbican_token_id, token);
+ return find_locked(barbican_token_id, token);
}
void TokenCache::add(const std::string& token_id,
Mutex::Locker l(lock);
rgw_get_token_id(token.token.id, barbican_token_id);
- add(barbican_token_id, token);
+ add_locked(barbican_token_id, token);
}
void TokenCache::invalidate(const std::string& token_id)
RGWGetObj_CB cb(this);
RGWGetDataCB* filter = (RGWGetDataCB*)&cb;
boost::optional<RGWGetObj_Decompress> decompress;
- RGWGetDataCB* decrypt = nullptr;
+ std::unique_ptr<RGWGetDataCB> decrypt;
map<string, bufferlist>::iterator attr_iter;
perfcounter->inc(l_rgw_get);
goto done_err;
}
if (need_decompress) {
- s->obj_size = cs_info.orig_size;
- decompress.emplace(s->cct, &cs_info, partial_content, filter);
- filter = &*decompress;
- }
- attr_iter = attrs.find(RGW_ATTR_MANIFEST);
- op_ret = this->get_decrypt_filter(&decrypt, filter,
- attr_iter != attrs.end() ? &(attr_iter->second) : nullptr);
- if (decrypt != nullptr) {
- filter = decrypt;
+ s->obj_size = cs_info.orig_size;
+ decompress.emplace(s->cct, &cs_info, partial_content, filter);
+ filter = &*decompress;
}
- if (op_ret < 0) {
- goto done_err;
- }
-
// for range requests with obj size 0
if (range_str && !(s->obj_size)) {
total_len = 0;
return;
}
+ attr_iter = attrs.find(RGW_ATTR_MANIFEST);
+ op_ret = this->get_decrypt_filter(&decrypt, filter,
+ attr_iter != attrs.end() ? &(attr_iter->second) : nullptr);
+ if (decrypt != nullptr) {
+ filter = decrypt.get();
+ }
+ if (op_ret < 0) {
+ goto done_err;
+ }
+
perfcounter->inc(l_rgw_get_b, end - ofs);
ofs_x = ofs;
if (op_ret < 0) {
goto done_err;
}
- delete decrypt;
return;
done_err:
send_response_data_error();
- delete decrypt;
}
int RGWGetObj::init_common()
RGWPutObj_CB cb(this);
RGWGetDataCB* filter = &cb;
boost::optional<RGWGetObj_Decompress> decompress;
+ std::unique_ptr<RGWGetDataCB> decrypt;
RGWCompressionInfo cs_info;
map<string, bufferlist> attrs;
-
+ map<string, bufferlist>::iterator attr_iter;
int ret = 0;
uint64_t obj_size;
filter = &*decompress;
}
+ attr_iter = attrs.find(RGW_ATTR_MANIFEST);
+ op_ret = this->get_decrypt_filter(&decrypt,
+ filter,
+ attrs,
+ attr_iter != attrs.end() ? &(attr_iter->second) : nullptr);
+ if (decrypt != nullptr) {
+ filter = decrypt.get();
+ }
+ if (op_ret < 0) {
+ return ret;
+ }
+
ret = read_op.range_to_ofs(obj_size, new_ofs, new_end);
if (ret < 0)
return ret;
{
RGWPutObjProcessor *processor = NULL;
RGWPutObjDataProcessor *filter = nullptr;
- RGWPutObjDataProcessor *encrypt = nullptr;
+ std::unique_ptr<RGWPutObjDataProcessor> encrypt;
char supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1];
char supplied_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
fst = copy_source_range_fst;
lst = copy_source_range_lst;
- if (compression_type != "none") {
- plugin = get_compressor_plugin(s, compression_type);
- if (!plugin) {
- ldout(s->cct, 1) << "Cannot load plugin for compression type "
- << compression_type << dendl;
- } else {
- compressor.emplace(s->cct, plugin, filter);
- filter = &*compressor;
- }
- }
- op_ret = get_encrypt_filter(&encrypt, filter);
- if (encrypt != nullptr)
- filter = encrypt;
+ op_ret = get_encrypt_filter(&encrypt, filter);
if (op_ret < 0) {
goto done;
}
+ if (encrypt != nullptr) {
+ filter = encrypt.get();
+ } else {
+ //no encryption, we can try compression
+ if (compression_type != "none") {
+ plugin = get_compressor_plugin(s, compression_type);
+ if (!plugin) {
+ ldout(s->cct, 1) << "Cannot load plugin for compression type "
+ << compression_type << dendl;
+ } else {
+ compressor.emplace(s->cct, plugin, filter);
+ filter = &*compressor;
+ }
+ }
+ }
do {
bufferlist data_in;
/* restart processing with different oid suffix */
dispose_processor(processor);
- if (encrypt) {
- delete encrypt;
- }
processor = select_processor(*static_cast<RGWObjectCtx *>(s->obj_ctx), &multipart);
filter = processor;
goto done;
}
- if (compressor) {
- compressor.emplace(s->cct, plugin, filter);
- filter = &*compressor;
- }
-
op_ret = get_encrypt_filter(&encrypt, filter);
- if (encrypt != nullptr)
- filter = encrypt;
if (op_ret < 0) {
- goto done;
+ goto done;
+ }
+ if (encrypt != nullptr) {
+ filter = encrypt.get();
+ } else {
+ if (compressor) {
+ compressor.emplace(s->cct, plugin, filter);
+ filter = &*compressor;
+ }
}
-
op_ret = put_data_and_throttle(filter, data, ofs, false);
if (op_ret < 0) {
goto done;
done:
dispose_processor(processor);
- if (encrypt) {
- delete encrypt;
- }
perfcounter->tinc(l_rgw_put_lat,
(ceph_clock_now() - s->time));
}
void RGWPostObj::execute()
{
- RGWPutObjDataProcessor *filter = NULL;
- RGWPutObjDataProcessor *encrypt = nullptr;
+ RGWPutObjDataProcessor *filter = nullptr;
+ std::unique_ptr<RGWPutObjDataProcessor> encrypt;
char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
MD5 hash;
buffer::list bl, aclbl;
int len = 0;
boost::optional<RGWPutObj_Compress> compressor;
+ CompressorRef plugin;
// read in the data from the POST form
op_ret = get_params();
if (op_ret < 0)
return;
- const auto& compression_type = store->get_zone_params().get_compression_type(
- s->bucket_info.placement_rule);
- CompressorRef plugin;
- if (compression_type != "none") {
- plugin = Compressor::create(s->cct, compression_type);
- if (!plugin) {
- ldout(s->cct, 1) << "Cannot load plugin for compression type "
- << compression_type << dendl;
- } else {
- compressor.emplace(s->cct, plugin, filter);
- filter = &*compressor;
- }
- }
-
op_ret = get_encrypt_filter(&encrypt, filter);
- if (encrypt != nullptr)
- filter = encrypt;
if (op_ret < 0) {
- goto done;
+ return;
+ }
+ if (encrypt != nullptr) {
+ filter = encrypt.get();
+ } else {
+ const auto& compression_type = store->get_zone_params().get_compression_type(
+ s->bucket_info.placement_rule);
+ if (compression_type != "none") {
+ plugin = Compressor::create(s->cct, compression_type);
+ if (!plugin) {
+ ldout(s->cct, 1) << "Cannot load plugin for compression type "
+ << compression_type << dendl;
+ } else {
+ compressor.emplace(s->cct, plugin, filter);
+ filter = &*compressor;
+ }
+ }
}
while (data_pending) {
op_ret = processor.complete(s->obj_size, etag, NULL, real_time(), attrs,
(delete_at ? *delete_at : real_time()));
-done:
- if (encrypt) {
- delete encrypt;
- }
}
/**
* calculates filter used to decrypt RGW objects data
*/
- virtual int get_decrypt_filter(RGWGetDataCB** filter, RGWGetDataCB* cb, bufferlist* manifest_bl) {
- *filter = NULL;
+ virtual int get_decrypt_filter(std::unique_ptr<RGWGetDataCB>* filter, RGWGetDataCB* cb, bufferlist* manifest_bl) {
+ *filter = nullptr;
return 0;
}
};
void pre_exec() override;
void execute() override;
- virtual int get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) {
- *filter = NULL;
+ /* this is for cases when copying data from other object */
+ virtual int get_decrypt_filter(std::unique_ptr<RGWGetDataCB>* filter,
+ RGWGetDataCB* cb,
+ map<string, bufferlist>& attrs,
+ bufferlist* manifest_bl) {
+ *filter = nullptr;
+ return 0;
+ }
+ virtual int get_encrypt_filter(std::unique_ptr<RGWPutObjDataProcessor> *filter, RGWPutObjDataProcessor* cb) {
+ *filter = nullptr;
return 0;
}
void pre_exec() override;
void execute() override;
-#if AKAKAK
- RGWPutObjProcessor *select_processor(RGWObjectCtx& obj_ctx);
- void dispose_processor(RGWPutObjProcessor *processor);
-#endif
- virtual int get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) {
- *filter = NULL;
+ virtual int get_encrypt_filter(std::unique_ptr<RGWPutObjDataProcessor> *filter, RGWPutObjDataProcessor* cb) {
+ *filter = nullptr;
return 0;
}
virtual int get_params() = 0;
CephContext* cct;
rgw_obj obj;
RGWPutObjDataProcessor *filter;
+ boost::optional<RGWPutObj_Compress>& compressor;
+ CompressorRef& plugin;
RGWPutObjProcessor_Atomic *processor;
RGWOpStateSingleOp *opstate;
void (*progress_cb)(off_t, void *);
bufferlist extra_data_bl;
uint64_t extra_data_len;
uint64_t data_len;
+ map<string, bufferlist> src_attrs;
public:
RGWRadosPutObj(CephContext* cct,
- RGWPutObjDataProcessor *filter,
+ CompressorRef& plugin,
+ boost::optional<RGWPutObj_Compress>& compressor,
RGWPutObjProcessor_Atomic *p,
RGWOpStateSingleOp *_ops,
void (*_progress_cb)(off_t, void *),
void *_progress_data) :
cct(cct),
- filter(filter),
+ filter(nullptr),
+ compressor(compressor),
+ plugin(plugin),
processor(p),
opstate(_ops),
progress_cb(_progress_cb),
progress_data(_progress_data),
extra_data_len(0),
data_len(0) {}
+
+ int process_attrs(void) {
+ if (extra_data_bl.length()) {
+ JSONParser jp;
+ if (!jp.parse(extra_data_bl.c_str(), extra_data_bl.length())) {
+ ldout(cct, 0) << "failed to parse response extra data. len=" << extra_data_bl.length() << " data=" << extra_data_bl.c_str() << dendl;
+ return -EIO;
+ }
+
+ JSONDecoder::decode_json("attrs", src_attrs, &jp);
+
+ src_attrs.erase(RGW_ATTR_COMPRESSION);
+ src_attrs.erase(RGW_ATTR_MANIFEST); // not interested in original object layout
+ }
+
+ if (plugin && src_attrs.find(RGW_ATTR_CRYPT_MODE) == src_attrs.end()) {
+ //do not compress if object is encrypted
+ compressor = boost::in_place(cct, plugin, filter);
+ filter = &*compressor;
+ }
+ return 0;
+ }
+
int handle_data(bufferlist& bl, off_t ofs, off_t len) override {
if (progress_cb) {
progress_cb(ofs, progress_data);
extra_data_bl.append(extra);
extra_data_len -= extra_len;
+ if (extra_data_len == 0) {
+ int res = process_attrs();
+ if (res < 0)
+ return res;
+ }
if (bl.length() == 0) {
return 0;
}
/* could not renew state! might have been marked as cancelled */
return ret;
}
-
need_opstate = false;
}
bufferlist& get_extra_data() { return extra_data_bl; }
+ map<string, bufferlist>& get_attrs() { return src_attrs; }
+
void set_extra_data_len(uint64_t len) override {
extra_data_len = len;
}
RGWRESTStreamRWRequest *in_stream_req;
string tag;
- map<string, bufferlist> src_attrs;
int i;
append_rand_alpha(cct, tag, tag, 32);
obj_time_weight set_mtime_weight;
boost::optional<RGWPutObj_Compress> compressor;
CompressorRef plugin;
- RGWPutObjDataProcessor *filter = &processor;
-
const auto& compression_type = zone_params.get_compression_type(
dest_bucket_info.placement_rule);
if (compression_type != "none") {
if (!plugin) {
ldout(cct, 1) << "Cannot load plugin for compression type "
<< compression_type << dendl;
- } else {
- compressor = boost::in_place(cct, plugin, filter);
- filter = &*compressor;
}
}
- RGWRadosPutObj cb(cct, filter, &processor, opstate, progress_cb, progress_data);
+ RGWRadosPutObj cb(cct, plugin, compressor, &processor, opstate, progress_cb, progress_data);
string etag;
map<string, string> req_headers;
}
}
-
ret = conn->get_obj(user_id, info, src_obj, pmod, unmod_ptr,
dest_mtime_weight.zone_short_id, dest_mtime_weight.pg_ver,
true /* prepend_meta */, true /* GET */, false /* rgwx-stat */,
if (ret < 0) {
goto set_err_state;
}
+ if (compressor && compressor->is_compressed()) {
+ bufferlist tmp;
+ RGWCompressionInfo cs_info;
+ cs_info.compression_type = plugin->get_type_name();
+ cs_info.orig_size = cb.get_data_len();
+ cs_info.blocks = move(compressor->get_compression_blocks());
+ ::encode(cs_info, tmp);
+ cb.get_attrs()[RGW_ATTR_COMPRESSION] = tmp;
+ }
- { /* opening scope so that we can do goto, sorry */
- bufferlist& extra_data_bl = cb.get_extra_data();
- if (extra_data_bl.length()) {
- JSONParser jp;
- if (!jp.parse(extra_data_bl.c_str(), extra_data_bl.length())) {
- ldout(cct, 0) << "failed to parse response extra data. len=" << extra_data_bl.length() << " data=" << extra_data_bl.c_str() << dendl;
- goto set_err_state;
- }
-
- JSONDecoder::decode_json("attrs", src_attrs, &jp);
-
- src_attrs.erase(RGW_ATTR_COMPRESSION);
- src_attrs.erase(RGW_ATTR_MANIFEST); // not interested in original object layout
- if (source_zone.empty()) { /* need to preserve expiration if copy in the same zonegroup */
- src_attrs.erase(RGW_ATTR_DELETE_AT);
- } else {
- map<string, bufferlist>::iterator iter = src_attrs.find(RGW_ATTR_DELETE_AT);
- if (iter != src_attrs.end()) {
- try {
- ::decode(delete_at, iter->second);
- } catch (buffer::error& err) {
- ldout(cct, 0) << "ERROR: failed to decode delete_at field in intra zone copy" << dendl;
- }
- }
+ if (source_zone.empty()) { /* need to preserve expiration if copy in the same zonegroup */
+ cb.get_attrs().erase(RGW_ATTR_DELETE_AT);
+ } else {
+ map<string, bufferlist>::iterator iter = cb.get_attrs().find(RGW_ATTR_DELETE_AT);
+ if (iter != cb.get_attrs().end()) {
+ try {
+ ::decode(delete_at, iter->second);
+ } catch (buffer::error& err) {
+ ldout(cct, 0) << "ERROR: failed to decode delete_at field in intra zone copy" << dendl;
}
}
- if (compressor && compressor->is_compressed()) {
- bufferlist tmp;
- RGWCompressionInfo cs_info;
- cs_info.compression_type = plugin->get_type_name();
- cs_info.orig_size = cb.get_data_len();
- cs_info.blocks = move(compressor->get_compression_blocks());
- ::encode(cs_info, tmp);
- src_attrs[RGW_ATTR_COMPRESSION] = tmp;
- }
}
if (src_mtime) {
}
if (petag) {
- const auto iter = src_attrs.find(RGW_ATTR_ETAG);
- if (iter != src_attrs.end()) {
+ const auto iter = cb.get_attrs().find(RGW_ATTR_ETAG);
+ if (iter != cb.get_attrs().end()) {
*petag = iter->second;
}
}
if (source_zone.empty()) {
- set_copy_attrs(src_attrs, attrs, attrs_mod);
+ set_copy_attrs(cb.get_attrs(), attrs, attrs_mod);
} else {
- attrs = src_attrs;
+ attrs = cb.get_attrs();
}
if (copy_if_newer) {
return 0;
}
-int RGWGetObj_ObjStore_S3::get_decrypt_filter(RGWGetDataCB** filter, RGWGetDataCB* cb, bufferlist* manifest_bl)
+int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetDataCB> *filter, RGWGetDataCB* cb, bufferlist* manifest_bl)
{
int res = 0;
- BlockCrypt* block_crypt = nullptr;
+ std::unique_ptr<BlockCrypt> block_crypt;
res = s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
if (res == 0) {
if (block_crypt != nullptr) {
- RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, block_crypt);
+ auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
+ //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
if (f != nullptr) {
- if (manifest_bl != nullptr)
+ if (manifest_bl != nullptr) {
res = f->read_manifest(*manifest_bl);
- if (res == 0) {
- *filter = f;
- } else {
- delete f;
+ if (res == 0) {
+ *filter = std::move(f);
+ }
}
}
}
attrs.emplace(key, std::move(bl));
}
+int RGWPutObj_ObjStore_S3::get_decrypt_filter(
+ std::unique_ptr<RGWGetDataCB>* filter,
+ RGWGetDataCB* cb,
+ map<string, bufferlist>& attrs,
+ bufferlist* manifest_bl)
+{
+ std::map<std::string, std::string> crypt_http_responses_unused;
-int RGWPutObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb)
+ int res = 0;
+ std::unique_ptr<BlockCrypt> block_crypt;
+ res = s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
+ if (res == 0) {
+ if (block_crypt != nullptr) {
+ auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
+ //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
+ if (f != nullptr) {
+ if (manifest_bl != nullptr) {
+ res = f->read_manifest(*manifest_bl);
+ if (res == 0) {
+ *filter = std::move(f);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+int RGWPutObj_ObjStore_S3::get_encrypt_filter(
+ std::unique_ptr<RGWPutObjDataProcessor>* filter,
+ RGWPutObjDataProcessor* cb)
{
int res = 0;
RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast<RGWPutObjProcessor_Multipart*>(cb);
obj.set_in_extra_data(true);
res = get_obj_attrs(store, s, obj, xattrs);
if (res == 0) {
- BlockCrypt* block_crypt = nullptr;
+ std::unique_ptr<BlockCrypt> block_crypt;
+ /* We are adding to existing object.
+ * We use crypto mode that configured as if we were decrypting. */
res = s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
if (res == 0 && block_crypt != nullptr)
- *filter=new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt);
+ *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
+ new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
}
/* it is ok, to not have encryption at all */
}
else
{
- BlockCrypt* block_crypt = nullptr;
+ std::unique_ptr<BlockCrypt> block_crypt;
res = s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
- if (res == 0 && block_crypt!=nullptr) {
- *filter = new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt);
+ if (res == 0 && block_crypt != nullptr) {
+ *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
+ new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
}
return res;
rgw_flush_formatter_and_reset(s, s->formatter);
}
-int RGWPostObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb)
+int RGWPostObj_ObjStore_S3::get_encrypt_filter(
+ std::unique_ptr<RGWPutObjDataProcessor>* filter, RGWPutObjDataProcessor* cb)
{
int res = 0;
- BlockCrypt* block_crypt = nullptr;
+ std::unique_ptr<BlockCrypt> block_crypt;
res = s3_prepare_encrypt(s, attrs, &parts, &block_crypt, crypt_http_responses);
if (res == 0 && block_crypt != nullptr) {
- *filter = new RGWPutObj_BlockEncrypt(s->cct, cb, block_crypt);
+ *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
+ new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
}
+ else
+ *filter = nullptr;
return res;
}
int send_response_data_error() override;
int send_response_data(bufferlist& bl, off_t ofs, off_t len) override;
void set_custom_http_response(int http_ret) { custom_http_ret = http_ret; }
- virtual int get_decrypt_filter(RGWGetDataCB** filter, RGWGetDataCB* cb, bufferlist* manifest_bl) override;
+ int get_decrypt_filter(std::unique_ptr<RGWGetDataCB>* filter,
+ RGWGetDataCB* cb,
+ bufferlist* manifest_bl) override;
};
class RGWListBuckets_ObjStore_S3 : public RGWListBuckets_ObjStore {
int validate_and_unwrap_available_aws4_chunked_data(bufferlist& bl_in,
bufferlist& bl_out);
- int get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) override;
+ int get_encrypt_filter(std::unique_ptr<RGWPutObjDataProcessor>* filter,
+ RGWPutObjDataProcessor* cb) override;
+ int get_decrypt_filter(std::unique_ptr<RGWGetDataCB>* filter,
+ RGWGetDataCB* cb,
+ map<string, bufferlist>& attrs,
+ bufferlist* manifest_bl) override;
};
struct post_part_field {
void send_response() override;
int get_data(bufferlist& bl) override;
- int get_encrypt_filter(RGWPutObjDataProcessor** filter, RGWPutObjDataProcessor* cb) override;
+ int get_encrypt_filter(std::unique_ptr<RGWPutObjDataProcessor>* filter,
+ RGWPutObjDataProcessor* cb) override;
};
class RGWDeleteObj_ObjStore_S3 : public RGWDeleteObj_ObjStore {
)
set_target_properties(ceph_test_rgw_obj PROPERTIES COMPILE_FLAGS
${UNITTEST_CXX_FLAGS})
+
+# ceph_test_rgw_crypto
+set(test_rgw_crypto_srcs test_rgw_crypto.cc)
+add_executable(unittest_rgw_crypto
+ ${test_rgw_crypto_srcs}
+ )
+add_ceph_unittest(unittest_rgw_crypto ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_rgw_crypto)
+
+target_link_libraries(unittest_rgw_crypto
+ rgw_a
+ cls_rgw_client
+ cls_lock_client
+ cls_refcount_client
+ cls_log_client
+ cls_statelog_client
+ cls_version_client
+ cls_replica_log_client
+ cls_user_client
+ librados
+ global
+ curl
+ uuid
+ expat
+ ${CMAKE_DL_LIBS}
+ ${UNITTEST_LIBS}
+ ${CRYPTO_LIBS}
+ )
+set_target_properties(unittest_rgw_crypto PROPERTIES COMPILE_FLAGS
+ ${UNITTEST_CXX_FLAGS})
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 Mirantis <akupczyk@mirantis.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+#include <iostream>
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "rgw/rgw_common.h"
+#include "rgw/rgw_rados.h"
+#include "rgw/rgw_crypt.h"
+#include <gtest/gtest.h>
+#include "include/assert.h"
+#define dout_subsys ceph_subsys_rgw
+
+using namespace std;
+
+
+std::unique_ptr<BlockCrypt> AES_256_CBC_create(CephContext* cct, const uint8_t* key, size_t len);
+
+
+class ut_get_sink : public RGWGetDataCB {
+ std::stringstream sink;
+public:
+ ut_get_sink() {}
+ virtual ~ut_get_sink() {}
+
+ int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override
+ {
+ sink << boost::string_ref(bl.c_str()+bl_ofs, bl_len);
+ return 0;
+ }
+ std::string get_sink()
+ {
+ return sink.str();
+ }
+};
+
+class ut_put_sink: public RGWPutObjDataProcessor
+{
+ std::stringstream sink;
+public:
+ ut_put_sink(){}
+ virtual ~ut_put_sink(){}
+ int handle_data(bufferlist& bl, off_t ofs, void **phandle, rgw_obj *pobj, bool *again) override
+ {
+ sink << boost::string_ref(bl.c_str(),bl.length());
+ *again = false;
+ return 0;
+ }
+ int throttle_data(void *handle, const rgw_obj& obj, bool need_to_wait) override
+ {
+ return 0;
+ }
+ std::string get_sink()
+ {
+ return sink.str();
+ }
+};
+
+
+class BlockCryptNone: public BlockCrypt {
+public:
+ BlockCryptNone(){};
+ virtual ~BlockCryptNone(){};
+ size_t get_block_size() override
+ {
+ return 256;
+ }
+ bool encrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) override
+ {
+ output.clear();
+ output.append(input.c_str(), input.length());
+ return true;
+ }
+ bool decrypt(bufferlist& input,
+ off_t in_ofs,
+ size_t size,
+ bufferlist& output,
+ off_t stream_offset) override
+ {
+ output.clear();
+ output.append(input.c_str(), input.length());
+ return true;
+ }
+};
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0);
+
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = (r*r*r*r*r % test_range);
+ begin = begin - begin % block_size;
+ off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+ if (r % 3)
+ end = end - end % block_size;
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0);
+
+ bufferlist encrypted;
+ ASSERT_TRUE(aes->encrypt(input, begin, end - begin, encrypted, offset));
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->decrypt(encrypted, 0, end - begin, decrypted, offset));
+
+ ASSERT_EQ(decrypted.length(), end - begin);
+ ASSERT_EQ(boost::string_ref(input.c_str() + begin, end - begin),
+ boost::string_ref(decrypted.c_str(), end - begin) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_2)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0);
+
+ for (size_t end = 1; end < 6096 ; end+=3)
+ {
+ off_t begin = 0;
+ off_t offset = end*end*end*end*end % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0);
+
+ bufferlist encrypted;
+ ASSERT_TRUE(aes->encrypt(input, begin, end, encrypted, offset));
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->decrypt(encrypted, 0, end, decrypted, offset));
+
+ ASSERT_EQ(decrypted.length(), end);
+ ASSERT_EQ(boost::string_ref(input.c_str(), end),
+ boost::string_ref(decrypted.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_3)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0);
+ size_t rr = 111;
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = begin + r*r*r*r*r*r*r % (test_range - begin);
+ //sometimes make aligned
+ if (r % 3)
+ end = end - end % block_size;
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0);
+
+ bufferlist encrypted1;
+ bufferlist encrypted2;
+
+ off_t pos = begin;
+ off_t chunk;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted1.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+
+ pos = begin;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted2.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ ASSERT_EQ(encrypted1.length(), end);
+ ASSERT_EQ(encrypted2.length(), end);
+ ASSERT_EQ(boost::string_ref(encrypted1.c_str(), end),
+ boost::string_ref(encrypted2.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_size_0_15)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0);
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = begin + r*r*r*r*r*r*r % (16);
+
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0);
+
+ bufferlist encrypted;
+ bufferlist decrypted;
+ ASSERT_TRUE(aes->encrypt(input, 0, end, encrypted, offset));
+ ASSERT_TRUE(aes->encrypt(encrypted, 0, end, decrypted, offset));
+ ASSERT_EQ(encrypted.length(), end);
+ ASSERT_EQ(decrypted.length(), end);
+ ASSERT_EQ(boost::string_ref(input.c_str(), end),
+ boost::string_ref(decrypted.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_AES_256_CBC_identity_last_block)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ buffer::ptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ for (unsigned int step : {1, 2, 3, 5, 7, 11, 13, 17})
+ {
+ //make some random key
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i*step;
+
+ auto aes(AES_256_CBC_create(g_ceph_context, &key[0], 32));
+ ASSERT_NE(aes.get(), nullptr);
+
+ size_t block_size = aes->get_block_size();
+ ASSERT_NE(block_size, 0);
+ size_t rr = 111;
+ for (size_t r = 97; r < 123 ; r++)
+ {
+ off_t begin = 0;
+ off_t end = r*r*r*r*r*r*r % (test_range - 16);
+ end = end - end % block_size;
+ end = end + (r+3)*(r+5)*(r+7) % 16;
+
+ off_t offset = r*r*r*r*r*r*r*r % (1000*1000*1000);
+ offset = offset - offset % block_size;
+
+ ASSERT_EQ(begin % block_size, 0);
+ ASSERT_LE(end, test_range);
+ ASSERT_EQ(offset % block_size, 0);
+
+ bufferlist encrypted1;
+ bufferlist encrypted2;
+
+ off_t pos = begin;
+ off_t chunk;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+17)*(rr+71)*(rr+123)*(rr+131) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted1.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ pos = begin;
+ while (pos < end) {
+ chunk = block_size + (rr/3)*(rr+97)*(rr+151)*(rr+213)*(rr+251) % 50000;
+ chunk = chunk - chunk % block_size;
+ if (pos + chunk > end)
+ chunk = end - pos;
+ bufferlist tmp;
+ ASSERT_TRUE(aes->encrypt(input, pos, chunk, tmp, offset + pos));
+ encrypted2.append(tmp);
+ pos += chunk;
+ rr++;
+ }
+ ASSERT_EQ(encrypted1.length(), end);
+ ASSERT_EQ(encrypted2.length(), end);
+ ASSERT_EQ(boost::string_ref(encrypted1.c_str(), end),
+ boost::string_ref(encrypted2.c_str(), end) );
+ }
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_ranges)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ bufferlist encrypted;
+ ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_get_sink get_sink;
+ auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
+
+ //random ranges
+ off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+ off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+ off_t f_begin = begin;
+ off_t f_end = end;
+ decrypt.fixup_range(f_begin, f_end);
+ decrypt.handle_data(encrypted, f_begin, f_end - f_begin + 1);
+ decrypt.flush();
+ const std::string& decrypted = get_sink.get_sink();
+ ASSERT_EQ(decrypted.length(), end - begin + 1);
+ ASSERT_EQ(decrypted, boost::string_ref(input.c_str()+begin,end - begin + 1));
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_RGWGetObj_BlockDecrypt_chunks)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ bufferlist encrypted;
+ ASSERT_TRUE(cbc->encrypt(input, 0, test_range, encrypted, 0));
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_get_sink get_sink;
+ auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink, std::move(cbc) );
+
+ //random
+ off_t begin = (r/3)*r*(r+13)*(r+23)*(r+53)*(r+71) % test_range;
+ off_t end = begin + (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - begin) - 1;
+
+ off_t f_begin = begin;
+ off_t f_end = end;
+ decrypt.fixup_range(f_begin, f_end);
+ off_t pos = f_begin;
+ do
+ {
+ off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+ size = (pos + 1117) * (pos + 2229) % size + 1;
+ if (pos + size > f_end + 1)
+ size = f_end + 1 - pos;
+
+ decrypt.handle_data(encrypted, pos, size);
+ pos = pos + size;
+ } while (pos < f_end + 1);
+ decrypt.flush();
+
+ const std::string& decrypted = get_sink.get_sink();
+ ASSERT_EQ(decrypted.length(), end - begin + 1);
+ ASSERT_EQ(decrypted, boost::string_ref(input.c_str()+begin,end - begin + 1));
+ }
+}
+
+
+using range_t = std::pair<off_t, off_t>;
+
+// call filter->fixup_range() and return the range as a pair. this makes it easy
+// to fit on a single line for ASSERT_EQ()
+range_t fixup_range(RGWGetObj_BlockDecrypt *decrypt, off_t ofs, off_t end)
+{
+ decrypt->fixup_range(ofs, end);
+ return {ofs, end};
+}
+
+TEST(TestRGWCrypto, check_RGWGetObj_BlockDecrypt_fixup)
+{
+ ut_get_sink get_sink;
+ auto nonecrypt = std::unique_ptr<BlockCrypt>(new BlockCryptNone);
+ RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink,
+ std::move(nonecrypt));
+ ASSERT_EQ(fixup_range(&decrypt,0,0), range_t(0,255));
+ ASSERT_EQ(fixup_range(&decrypt,1,256), range_t(0,511));
+ ASSERT_EQ(fixup_range(&decrypt,0,255), range_t(0,255));
+ ASSERT_EQ(fixup_range(&decrypt,255,256), range_t(0,511));
+ ASSERT_EQ(fixup_range(&decrypt,511,1023), range_t(256,1023));
+ ASSERT_EQ(fixup_range(&decrypt,513,1024), range_t(512,1024+255));
+}
+
+
+TEST(TestRGWCrypto, verify_RGWPutObj_BlockEncrypt_chunks)
+{
+ //create some input for encryption
+ const size_t test_range = 1024*1024;
+ bufferptr buf(test_range);
+ char* p = buf.c_str();
+ for(size_t i = 0; i < buf.length(); i++)
+ p[i] = i + i*i + (i >> 2);
+
+ bufferlist input;
+ input.append(buf);
+
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i] = i;
+
+ for (off_t r = 93; r < 150; r++ )
+ {
+ ut_put_sink put_sink;
+ auto cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+ RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
+ std::move(cbc) );
+
+ off_t test_size = (r/5)*(r+7)*(r+13)*(r+101)*(r*103) % (test_range - 1) + 1;
+ off_t pos = 0;
+ do
+ {
+ off_t size = 2 << ((pos * 17 + pos / 113 + r) % 16);
+ size = (pos + 1117) * (pos + 2229) % size + 1;
+ if (pos + size > test_size)
+ size = test_size - pos;
+
+ bufferlist bl;
+ bl.append(input.c_str()+pos, size);
+ void* handle;
+ bool again = false;
+ rgw_obj ro;
+ encrypt.handle_data(bl, 0, &handle, nullptr, &again);
+ encrypt.throttle_data(handle, ro, false);
+
+ pos = pos + size;
+ } while (pos < test_size);
+ bufferlist bl;
+ void* handle;
+ bool again = false;
+ encrypt.handle_data(bl, 0, &handle, nullptr, &again);
+
+ ASSERT_EQ(put_sink.get_sink().length(), test_size);
+
+ cbc = AES_256_CBC_create(g_ceph_context, &key[0], 32);
+ ASSERT_NE(cbc.get(), nullptr);
+
+ bufferlist encrypted;
+ bufferlist decrypted;
+ encrypted.append(put_sink.get_sink());
+ ASSERT_TRUE(cbc->decrypt(encrypted, 0, test_size, decrypted, 0));
+
+ ASSERT_EQ(decrypted.length(), test_size);
+ ASSERT_EQ(boost::string_ref(decrypted.c_str(), test_size),
+ boost::string_ref(input.c_str(), test_size));
+ }
+}
+
+
+TEST(TestRGWCrypto, verify_Encrypt_Decrypt)
+{
+ uint8_t key[32];
+ for(size_t i=0;i<sizeof(key);i++)
+ key[i]=i;
+
+ size_t fi_a = 0;
+ size_t fi_b = 1;
+ size_t test_size;
+ do
+ {
+ //fibonacci
+ size_t tmp = fi_b;
+ fi_b = fi_a + fi_b;
+ fi_a = tmp;
+
+ test_size = fi_b;
+
+ uint8_t* test_in = new uint8_t[test_size];
+ //fill with something
+ memset(test_in, test_size & 0xff, test_size);
+
+ ut_put_sink put_sink;
+ RGWPutObj_BlockEncrypt encrypt(g_ceph_context, &put_sink,
+ std::move(AES_256_CBC_create(g_ceph_context, &key[0], 32)) );
+ bufferlist bl;
+ bl.append((char*)test_in, test_size);
+ void* handle;
+ bool again = false;
+ rgw_obj ro;
+ encrypt.handle_data(bl, 0, &handle, nullptr, &again);
+ encrypt.throttle_data(handle, ro, false);
+ bl.clear();
+ encrypt.handle_data(bl, 0, &handle, nullptr, &again);
+ ASSERT_EQ(put_sink.get_sink().length(), test_size);
+
+ bl.append(put_sink.get_sink().data(), put_sink.get_sink().length());
+ ASSERT_EQ(bl.length(), test_size);
+
+ ut_get_sink get_sink;
+ RGWGetObj_BlockDecrypt decrypt(g_ceph_context, &get_sink,
+ std::move(AES_256_CBC_create(g_ceph_context, &key[0], 32)) );
+
+ off_t bl_ofs = 0;
+ off_t bl_end = test_size - 1;
+ decrypt.fixup_range(bl_ofs, bl_end);
+ decrypt.handle_data(bl, 0, bl.length());
+ decrypt.flush();
+ ASSERT_EQ(get_sink.get_sink().length(), test_size);
+ ASSERT_EQ(get_sink.get_sink(), boost::string_ref((char*)test_in,test_size));
+ }
+ while (test_size < 20000);
+}
+
+
+int main(int argc, char **argv) {
+ vector<const char*> args;
+ argv_to_vec(argc, (const char **)argv, args);
+
+ global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+