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