]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Got rid of most bare pointers => now mostly unique_ptr.
authorAdam Kupczyk <akupczyk@mirantis.com>
Fri, 5 Aug 2016 12:21:51 +0000 (14:21 +0200)
committerAdam Kupczyk <akupczyk@mirantis.com>
Wed, 5 Apr 2017 16:31:17 +0000 (18:31 +0200)
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>
src/rgw/rgw_crypt.cc
src/rgw/rgw_crypt.h
src/rgw/rgw_keystone.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rados.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/test/rgw/CMakeLists.txt
src/test/rgw/test_rgw_crypto.cc [new file with mode: 0644]

index 8c9da69d23ff4b44883be73041cc743da1a74d71..d174de454446775109b051e7cafcdc2f9df924e4 100644 (file)
@@ -92,7 +92,7 @@ public:
       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;
@@ -170,7 +170,7 @@ public:
 #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);
   }
@@ -215,8 +215,29 @@ CryptoAccelRef get_crypto_accel(CephContext *cct)
 
 
 /**
- * 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;
@@ -244,10 +265,13 @@ public:
 
 #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);
@@ -262,10 +286,13 @@ public:
 
 #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;
@@ -317,14 +344,14 @@ public:
 #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) {
@@ -347,41 +374,44 @@ public:
     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++) {
@@ -399,57 +429,49 @@ public:
     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) {
@@ -463,7 +485,7 @@ public:
   }
 
 
-  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;
@@ -478,13 +500,28 @@ public:
   }
 };
 
+
+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 {
@@ -556,18 +593,22 @@ bool AES_256_ECB_encrypt(CephContext* cct, uint8_t* key, size_t key_size, uint8_
 #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();
@@ -600,8 +641,8 @@ 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;
+    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])) {
@@ -631,8 +672,8 @@ int RGWGetObj_BlockDecrypt::fixup_range(off_t& bl_ofs, off_t& bl_end) {
   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);
   }
@@ -641,6 +682,7 @@ int RGWGetObj_BlockDecrypt::fixup_range(off_t& bl_ofs, off_t& bl_end) {
   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;
@@ -664,7 +706,13 @@ int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_l
       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;
@@ -675,11 +723,10 @@ int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_l
   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);
@@ -691,7 +738,6 @@ int RGWGetObj_BlockDecrypt::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_l
       res = next->handle_data(data, enc_begin_skip, send_size);
       enc_begin_skip = 0;
       ofs += aligned_size;
-
       if (res != 0)
         return res;
     }
@@ -724,14 +770,19 @@ int RGWGetObj_BlockDecrypt::flush() {
   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) {
@@ -790,14 +841,14 @@ int RGWPutObj_BlockEncrypt::handle_data(bufferlist& bl, off_t in_ofs, void **pha
       /*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;
 }
@@ -868,7 +919,10 @@ int request_key_from_barbican(CephContext *cct,
   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;
@@ -995,6 +1049,7 @@ boost::string_ref rgw_trim_whitespace(const boost::string_ref& src)
   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)
@@ -1018,15 +1073,15 @@ static boost::string_ref get_crypt_attribute(RGWEnv* env,
   }
 }
 
+
 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);
@@ -1062,9 +1117,9 @@ int s3_prepare_encrypt(struct req_state* s,
       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";
@@ -1103,9 +1158,9 @@ int s3_prepare_encrypt(struct req_state* s,
       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;
     }
@@ -1132,9 +1187,9 @@ int s3_prepare_encrypt(struct req_state* s,
         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;
@@ -1144,18 +1199,18 @@ int s3_prepare_encrypt(struct req_state* s,
   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;
@@ -1173,6 +1228,7 @@ int s3_prepare_decrypt(
       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());
@@ -1183,9 +1239,9 @@ int s3_prepare_decrypt(
       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;
@@ -1209,10 +1265,10 @@ int s3_prepare_decrypt(
       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;
@@ -1240,10 +1296,9 @@ int s3_prepare_decrypt(
       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;
   }
 
index 31066b672b5b4d33a74341b6ba45728b6ab6450e..5ad4d948332c0c2e55f7cad68840723e830a9493 100644 (file)
 #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
index 9ea60c7afd4ed89df85a80b82714884583f125e8..933308240e2ca08a616c69f3511ae2df04887254 100644 (file)
@@ -447,7 +447,7 @@ bool TokenCache::find_barbican(rgw::keystone::TokenEnvelope& token)
 {
   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,
@@ -494,7 +494,7 @@ void TokenCache::add_barbican(const rgw::keystone::TokenEnvelope& token)
   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)
index de41b8b4f4e1ffdd43eb801d14a75cea5c581a67..04515f57c179ae1c882d2ecff04e887c861af500 100644 (file)
@@ -1370,7 +1370,7 @@ void RGWGetObj::execute()
   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);
@@ -1435,20 +1435,10 @@ void RGWGetObj::execute()
     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;
@@ -1498,6 +1488,16 @@ void RGWGetObj::execute()
     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;
@@ -1518,12 +1518,10 @@ void RGWGetObj::execute()
   if (op_ret < 0) {
     goto done_err;
   }
-  delete decrypt;
   return;
 
 done_err:
   send_response_data_error();
-  delete decrypt;
 }
 
 int RGWGetObj::init_common()
@@ -2814,9 +2812,10 @@ int RGWPutObj::get_data(const off_t fst, const off_t lst, bufferlist& bl)
   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;
@@ -2852,6 +2851,18 @@ int RGWPutObj::get_data(const off_t fst, const off_t lst, bufferlist& bl)
     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;
@@ -2894,7 +2905,7 @@ void RGWPutObj::execute()
 {
   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];
@@ -2994,23 +3005,26 @@ void RGWPutObj::execute()
 
   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;
@@ -3073,9 +3087,6 @@ void RGWPutObj::execute()
       /* 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;
 
@@ -3091,18 +3102,18 @@ void RGWPutObj::execute()
         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;
@@ -3247,9 +3258,6 @@ void RGWPutObj::execute()
 
 done:
   dispose_processor(processor);
-  if (encrypt) {
-    delete encrypt;
-  }
   perfcounter->tinc(l_rgw_put_lat,
                    (ceph_clock_now() - s->time));
 }
@@ -3282,14 +3290,15 @@ void RGWPostObj::pre_exec()
 
 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();
@@ -3329,25 +3338,25 @@ void RGWPostObj::execute()
   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) {
@@ -3417,10 +3426,6 @@ void RGWPostObj::execute()
 
   op_ret = processor.complete(s->obj_size, etag, NULL, real_time(), attrs,
                               (delete_at ? *delete_at : real_time()));
-done:
-  if (encrypt) {
-    delete encrypt;
-  }
 }
 
 
index e15f30525407cc1fc188b29aa4683a5636fa3680..8f8f2ef6ee169cbcb62a2b6c75aa2dee8e6cb264 100644 (file)
@@ -270,8 +270,8 @@ public:
   /**
    * 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;
   }
 };
@@ -964,8 +964,16 @@ public:
   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;
   }
 
@@ -1035,12 +1043,8 @@ public:
   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;
index 634202057578d12508191a86fc77d41b968cbbbe..71138f2b68ddce182f885d301ce05cd9fa8a9414 100644 (file)
@@ -6749,6 +6749,8 @@ class RGWRadosPutObj : public RGWGetDataCB
   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 *);
@@ -6756,21 +6758,48 @@ class RGWRadosPutObj : public RGWGetDataCB
   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);
@@ -6785,6 +6814,11 @@ public:
       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;
       }
@@ -6816,7 +6850,6 @@ public:
           /* could not renew state! might have been marked as cancelled */
           return ret;
         }
-
         need_opstate = false;
       }
 
@@ -6830,6 +6863,8 @@ public:
 
   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;
   }
@@ -7131,7 +7166,6 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
 
   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;
@@ -7189,8 +7223,6 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
   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") {
@@ -7198,13 +7230,10 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
     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;
@@ -7228,7 +7257,6 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
     }
   }
 
   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 */,
@@ -7241,42 +7269,27 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
   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) {
@@ -7284,16 +7297,16 @@ int RGWRados::fetch_remote_obj(RGWObjectCtx& obj_ctx,
   }
 
   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) {
index ec147c303370cc5d6de5d52d681c2f097d7db9ce..213fb9cdb0a198f7f667d5d86a9dd264fd8dca53 100644 (file)
@@ -314,21 +314,21 @@ send_data:
   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);
+          }
         }
       }
     }
@@ -1424,8 +1424,37 @@ static inline void set_attr(map<string, bufferlist>& attrs, const char* key, con
   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);
@@ -1442,20 +1471,24 @@ int RGWPutObj_ObjStore_S3::get_encrypt_filter(RGWPutObjDataProcessor** filter, R
       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;
@@ -2151,14 +2184,18 @@ done:
   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;
 }
 
index 8ac0a1d2bb417b9468861e2bf677dcd34f30f862..291b04b9281ea90fa0d24f18affe1fa9cb827af7 100644 (file)
@@ -44,7 +44,9 @@ public:
   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 {
@@ -185,7 +187,12 @@ public:
   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 {
@@ -242,7 +249,8 @@ public:
 
   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 {
index e5af903005e559f7b7e2ae7d8de5da07bcade45a..d8f9a5daf87037491eead07d7efee0bc2a6226e7 100644 (file)
@@ -79,3 +79,32 @@ target_link_libraries(ceph_test_rgw_obj
   )
 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})
diff --git a/src/test/rgw/test_rgw_crypto.cc b/src/test/rgw/test_rgw_crypto.cc
new file mode 100644 (file)
index 0000000..3241e01
--- /dev/null
@@ -0,0 +1,658 @@
+// -*- 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();
+}
+