]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: crypto format api semantics change 39950/head
authorOr Ozeri <oro@il.ibm.com>
Tue, 9 Mar 2021 20:14:49 +0000 (22:14 +0200)
committerJason Dillaman <dillaman@redhat.com>
Mon, 15 Mar 2021 12:29:43 +0000 (08:29 -0400)
This commit alters the semantics of the encryption format api
to also load the encryption after format completes.
Additionally, several other small changes in librbd crypto are included,
in preparation of supporting clone formatting.

Signed-off-by: Or Ozeri <oro@il.ibm.com>
(cherry picked from commit bb8c66772abaa5890f4caa23bda0eb2ad3279221)

38 files changed:
src/include/rbd/librbd.h
src/librbd/CMakeLists.txt
src/librbd/ImageCtx.h
src/librbd/api/Image.cc
src/librbd/crypto/BlockCrypto.h
src/librbd/crypto/CryptoContextPool.h
src/librbd/crypto/CryptoInterface.h
src/librbd/crypto/DataCryptor.h
src/librbd/crypto/EncryptionFormat.h
src/librbd/crypto/FormatRequest.cc
src/librbd/crypto/FormatRequest.h
src/librbd/crypto/LoadRequest.cc
src/librbd/crypto/LoadRequest.h
src/librbd/crypto/ShutDownCryptoRequest.cc [new file with mode: 0644]
src/librbd/crypto/ShutDownCryptoRequest.h [new file with mode: 0644]
src/librbd/crypto/Utils.cc [new file with mode: 0644]
src/librbd/crypto/Utils.h [new file with mode: 0644]
src/librbd/crypto/luks/EncryptionFormat.cc
src/librbd/crypto/luks/EncryptionFormat.h
src/librbd/crypto/luks/FormatRequest.cc
src/librbd/crypto/luks/FormatRequest.h
src/librbd/crypto/luks/Header.cc
src/librbd/crypto/luks/Header.h
src/librbd/crypto/luks/LoadRequest.cc
src/librbd/crypto/openssl/DataCryptor.cc
src/librbd/crypto/openssl/DataCryptor.h
src/librbd/migration/S3Stream.cc
src/test/librbd/CMakeLists.txt
src/test/librbd/crypto/luks/test_mock_FormatRequest.cc
src/test/librbd/crypto/luks/test_mock_LoadRequest.cc
src/test/librbd/crypto/test_mock_FormatRequest.cc
src/test/librbd/crypto/test_mock_LoadRequest.cc
src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc [new file with mode: 0644]
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/mock/crypto/MockCryptoInterface.h
src/test/librbd/mock/crypto/MockDataCryptor.h
src/test/librbd/mock/crypto/MockEncryptionFormat.h
src/test/librbd/test_librbd.cc

index 86b60eb27f08d11b2d90fd8a98c1c0309c8f8ca0..8618e659641a644f7932765020e3f0939e79e655 100644 (file)
@@ -48,6 +48,7 @@ extern "C" {
 #define LIBRBD_SUPPORTS_WATCH 0
 #define LIBRBD_SUPPORTS_WRITESAME 1
 #define LIBRBD_SUPPORTS_WRITE_ZEROES 1
+#define LIBRBD_SUPPORTS_ENCRYPTION 1
 
 #if __GNUC__ >= 4
   #define CEPH_RBD_API          __attribute__ ((visibility ("default")))
index 9e9365d1f95e187c8af2f427e009c13678a28043..724b6b36de9e459ad61c7b78483ad6ae5996840b 100644 (file)
@@ -66,6 +66,8 @@ set(librbd_internal_srcs
   crypto/CryptoObjectDispatch.cc
   crypto/FormatRequest.cc
   crypto/LoadRequest.cc
+  crypto/ShutDownCryptoRequest.cc
+  crypto/Utils.cc
   crypto/openssl/DataCryptor.cc
   deep_copy/ImageCopyRequest.cc
   deep_copy/MetadataCopyRequest.cc
index 5623f2e768f6f57e5f560c29d1a1df12ae20a553..31d9a67547d62998e98da6feb18fc3043e254aba 100644 (file)
@@ -57,6 +57,7 @@ namespace librbd {
   template <typename> class PluginRegistry;
 
   namespace asio { struct ContextWQ; }
+  namespace crypto { class CryptoInterface; }
   namespace exclusive_lock { struct Policy; }
   namespace io {
   class AioCompletion;
@@ -233,6 +234,8 @@ namespace librbd {
 
     ZTracer::Endpoint trace_endpoint;
 
+    crypto::CryptoInterface* crypto = nullptr;
+
     // unit test mock helpers
     static ImageCtx* create(const std::string &image_name,
                             const std::string &image_id,
index b234d4f18f736975c45aac7a1b53c6c0b086fff6..7df0f4fc69e2e85e04a40e46131ed5abdb0b6ed5 100644 (file)
@@ -938,6 +938,11 @@ template <typename I>
 int Image<I>::encryption_format(I* ictx, encryption_format_t format,
                                 encryption_options_t opts, size_t opts_size,
                                 bool c_api) {
+  if (ictx->parent != nullptr) {
+    lderr(ictx->cct) << "cannot format a cloned image" << dendl;
+    return -ENOTSUP;
+  }
+
   crypto::EncryptionFormat<I>* result_format;
   auto r = util::create_encryption_format(
           ictx->cct, format, opts, opts_size, c_api, &result_format);
index 31b39a2b3a16265e4d5b301c922ebe1245c153e1..0bbdd2524cd5d48b67617dbc525131b9c4965d70 100644 (file)
@@ -34,6 +34,14 @@ public:
       return m_data_offset;
     }
 
+    const unsigned char* get_key() const override {
+      return m_data_cryptor->get_key();
+    }
+
+    int get_key_length() const override {
+      return m_data_cryptor->get_key_length();
+    }
+
 private:
     CephContext* m_cct;
     DataCryptor<T>* m_data_cryptor;
index ce5abfa2ef4ef0a17734c09efd86df38bc19717d..c0ebce0c2333eac7123a9a57c66b6cc68e3e85a4 100644 (file)
@@ -28,6 +28,12 @@ public:
     inline uint32_t get_iv_size() const override {
       return m_data_cryptor->get_iv_size();
     }
+    inline int get_key_length() const override {
+      return m_data_cryptor->get_key_length();
+    }
+    inline const unsigned char* get_key() const override {
+      return m_data_cryptor->get_key();
+    }
     inline int init_context(T* ctx, const unsigned char* iv,
                             uint32_t iv_length) const override {
       return m_data_cryptor->init_context(ctx, iv, iv_length);
index 8b1f57924b1f2fff53845b593a3569528f69a384..170a5bf28e9eb934a454c8a08eaa1a00610a2a48 100644 (file)
@@ -19,6 +19,8 @@ public:
   virtual int decrypt(ceph::bufferlist* data, uint64_t image_offset) = 0;
   virtual uint64_t get_block_size() const = 0;
   virtual uint64_t get_data_offset() const = 0;
+  virtual const unsigned char* get_key() const = 0;
+  virtual int get_key_length() const = 0;
 
   inline std::pair<uint64_t, uint64_t> get_pre_and_post_align(
           uint64_t off, uint64_t len) {
index cf293858907baaa9aada91effd041802f8bb95dc..ffcc57ce4c96757a9faece1bbfeccf7957ac43fe 100644 (file)
@@ -19,6 +19,9 @@ public:
 
   virtual uint32_t get_block_size() const = 0;
   virtual uint32_t get_iv_size() const = 0;
+  virtual const unsigned char* get_key() const = 0;
+  virtual int get_key_length() const = 0;
+
   virtual T* get_context(CipherMode mode) = 0;
   virtual void return_context(T* ctx, CipherMode mode) = 0;
 
index 1ac962216c8e1e6da937d17b27928ae1af516cb6..ba57a92524f42e5d2d35dd61fd69522b0dbb2625 100644 (file)
@@ -19,9 +19,9 @@ struct EncryptionFormat {
   }
 
   virtual void format(ImageCtxT* ictx, Context* on_finish) = 0;
-  virtual void load(ImageCtxT* ictx,
-                    ceph::ref_t<CryptoInterface>* result_crypto,
-                    Context* on_finish) = 0;
+  virtual void load(ImageCtxT* ictx, Context* on_finish) = 0;
+
+  virtual ceph::ref_t<CryptoInterface> get_crypto() = 0;
 };
 
 } // namespace crypto
index d23f95096bbeccafccbc8c23b7e7600abd83ce16..53dda58aae0e51bdc9dddf13f0b477d84cc2c663 100644 (file)
@@ -7,7 +7,12 @@
 #include "common/errno.h"
 #include "librbd/ImageCtx.h"
 #include "librbd/Utils.h"
+#include "librbd/crypto/ShutDownCryptoRequest.h"
+#include "librbd/crypto/Utils.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
 #include "librbd/io/ObjectDispatcherInterface.h"
+#include "librbd/io/Types.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -29,33 +34,81 @@ FormatRequest<I>::FormatRequest(
 
 template <typename I>
 void FormatRequest<I>::send() {
-  if (m_image_ctx->io_object_dispatcher->exists(
-          io::OBJECT_DISPATCH_LAYER_CRYPTO)) {
-    lderr(m_image_ctx->cct) << "cannot format with already loaded encryption"
-                            << dendl;
-    finish(-EEXIST);
+  if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
+    lderr(m_image_ctx->cct) << "cannot use encryption with journal" << dendl;
+    finish(-ENOTSUP);
     return;
   }
 
-  if (m_image_ctx->parent != nullptr) {
-    lderr(m_image_ctx->cct) << "cannot format a cloned image" << dendl;
-    finish(-ENOTSUP);
+  if (m_image_ctx->crypto == nullptr) {
+    format();
     return;
   }
 
-  if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
-    lderr(m_image_ctx->cct) << "cannot use encryption with journal" << dendl;
-    finish(-ENOTSUP);
+  auto ctx = create_context_callback<
+          FormatRequest<I>, &FormatRequest<I>::handle_shutdown_crypto>(this);
+  auto *req = ShutDownCryptoRequest<I>::create(m_image_ctx, ctx);
+  req->send();
+}
+
+template <typename I>
+void FormatRequest<I>::handle_shutdown_crypto(int r) {
+  if (r != 0) {
+    lderr(m_image_ctx->cct) << "unable to unload existing crypto: "
+                            << cpp_strerror(r) << dendl;
+    finish(r);
     return;
   }
 
+  format();
+}
+
+template <typename I>
+void FormatRequest<I>::format() {
   auto ctx = create_context_callback<
-          FormatRequest<I>, &FormatRequest<I>::finish>(this);
+          FormatRequest<I>, &FormatRequest<I>::handle_format>(this);
   m_format->format(m_image_ctx, ctx);
 }
 
+template <typename I>
+void FormatRequest<I>::handle_format(int r) {
+  if (r != 0) {
+    lderr(m_image_ctx->cct) << "unable to format image: " << cpp_strerror(r)
+                            << dendl;
+    finish(r);
+    return;
+  }
+
+  flush();
+}
+
+template <typename I>
+void FormatRequest<I>::flush() {
+  auto ctx = create_context_callback<
+          FormatRequest<I>, &FormatRequest<I>::handle_flush>(this);
+  auto aio_comp = io::AioCompletion::create_and_start(
+    ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_FLUSH);
+  auto req = io::ImageDispatchSpec::create_flush(
+    *m_image_ctx, io::IMAGE_DISPATCH_LAYER_INTERNAL_START, aio_comp,
+    io::FLUSH_SOURCE_INTERNAL, {});
+  req->send();
+}
+
+template <typename I>
+void FormatRequest<I>::handle_flush(int r) {
+  if (r != 0) {
+    lderr(m_image_ctx->cct) << "unable to flush image: " << cpp_strerror(r)
+                            << dendl;
+  }
+
+  finish(r);
+}
+
 template <typename I>
 void FormatRequest<I>::finish(int r) {
+  if (r == 0) {
+    util::set_crypto(m_image_ctx, m_format->get_crypto());
+  }
   m_on_finish->complete(r);
   delete this;
 }
index fc4263a9ec569cb3d50ceb0f1ae91bf4ae460a41..cfd7978d80b294d1e5035830b965eb5de2f94433 100644 (file)
@@ -27,6 +27,11 @@ public:
     FormatRequest(I* image_ctx, std::unique_ptr<EncryptionFormat<I>> format,
                   Context* on_finish);
     void send();
+    void handle_shutdown_crypto(int r);
+    void format();
+    void handle_format(int r);
+    void flush();
+    void handle_flush(int r);
     void finish(int r);
 
 private:
index 591e08d57a01eec68f6d9541e086e99253d48a39..c42011f62587f4a082531894d47cfec11ff5fa71 100644 (file)
@@ -7,10 +7,7 @@
 #include "common/errno.h"
 #include "librbd/Utils.h"
 #include "librbd/ImageCtx.h"
-#include "librbd/crypto/CryptoImageDispatch.h"
-#include "librbd/crypto/CryptoObjectDispatch.h"
-#include "librbd/io/ImageDispatcherInterface.h"
-#include "librbd/io/ObjectDispatcherInterface.h"
+#include "librbd/crypto/Utils.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -32,8 +29,7 @@ LoadRequest<I>::LoadRequest(
 
 template <typename I>
 void LoadRequest<I>::send() {
-  if (m_image_ctx->io_object_dispatcher->exists(
-          io::OBJECT_DISPATCH_LAYER_CRYPTO)) {
+  if (m_image_ctx->crypto != nullptr) {
     lderr(m_image_ctx->cct) << "encryption already loaded" << dendl;
     finish(-EEXIST);
     return;
@@ -52,22 +48,18 @@ void LoadRequest<I>::send() {
 
   auto ctx = create_context_callback<
           LoadRequest<I>, &LoadRequest<I>::finish>(this);
-  m_format->load(m_image_ctx, &m_crypto, ctx);
+  m_format->load(m_image_ctx, ctx);
 }
 
 template <typename I>
 void LoadRequest<I>::finish(int r) {
+
   if (r == 0) {
     // load crypto layers to image and its ancestors
+    auto crypto = m_format->get_crypto();
     auto ictx = m_image_ctx;
     while (ictx != nullptr) {
-      auto object_dispatch = CryptoObjectDispatch<I>::create(
-              ictx, m_crypto);
-      auto image_dispatch = CryptoImageDispatch::create(
-            m_crypto->get_data_offset());
-      ictx->io_object_dispatcher->register_dispatch(object_dispatch);
-      ictx->io_image_dispatcher->register_dispatch(image_dispatch);
-
+      util::set_crypto(ictx, crypto);
       ictx = ictx->parent;
     }
   }
index 6bbe980ea7916532f221324d96b71709752f977e..50d1dad84ec67de0a75b9c5f2703e1612c8bc49c 100644 (file)
@@ -34,7 +34,6 @@ private:
     I* m_image_ctx;
     std::unique_ptr<EncryptionFormat<I>> m_format;
     Context* m_on_finish;
-    ceph::ref_t<crypto::CryptoInterface> m_crypto;
 };
 
 } // namespace crypto
diff --git a/src/librbd/crypto/ShutDownCryptoRequest.cc b/src/librbd/crypto/ShutDownCryptoRequest.cc
new file mode 100644 (file)
index 0000000..dad6e2f
--- /dev/null
@@ -0,0 +1,105 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "ShutDownCryptoRequest.h"
+
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/crypto/CryptoImageDispatch.h"
+#include "librbd/crypto/CryptoObjectDispatch.h"
+#include "librbd/io/ImageDispatcherInterface.h"
+#include "librbd/io/ObjectDispatcherInterface.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::crypto::ShutDownCryptoRequest: " \
+                           << this << " " << __func__ << ": "
+
+namespace librbd {
+namespace crypto {
+
+using librbd::util::create_context_callback;
+
+template <typename I>
+ShutDownCryptoRequest<I>::ShutDownCryptoRequest(
+        I* image_ctx, Context* on_finish) : m_image_ctx(image_ctx),
+                                            m_on_finish(on_finish) {
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::send() {
+  m_image_ctx->image_lock.lock_shared();
+  m_crypto = m_image_ctx->crypto;
+  m_image_ctx->image_lock.unlock_shared();
+
+  shut_down_object_dispatch();
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::shut_down_object_dispatch() {
+   auto ctx = create_context_callback<
+          ShutDownCryptoRequest<I>,
+          &ShutDownCryptoRequest<I>::handle_shut_down_object_dispatch>(this);
+  if (!m_image_ctx->io_object_dispatcher->exists(
+          io::OBJECT_DISPATCH_LAYER_CRYPTO)) {
+    finish(0);
+    return;
+  }
+
+  m_image_ctx->io_object_dispatcher->shut_down_dispatch(
+          io::OBJECT_DISPATCH_LAYER_CRYPTO, ctx);
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::handle_shut_down_object_dispatch(int r) {
+  if (r < 0) {
+    lderr(m_image_ctx->cct) << "failed to shut down object dispatch: "
+                            << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  shut_down_image_dispatch();
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::shut_down_image_dispatch() {
+  if (!m_image_ctx->io_image_dispatcher->exists(
+          io::IMAGE_DISPATCH_LAYER_CRYPTO)) {
+    finish(0);
+    return;
+  }
+
+  auto ctx = create_context_callback<
+        ShutDownCryptoRequest<I>,
+        &ShutDownCryptoRequest<I>::handle_shut_down_image_dispatch>(this);
+  m_image_ctx->io_image_dispatcher->shut_down_dispatch(
+          io::IMAGE_DISPATCH_LAYER_CRYPTO, ctx);
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::handle_shut_down_image_dispatch(int r) {
+  if (r < 0) {
+    lderr(m_image_ctx->cct) << "failed to shut down image dispatch: "
+                            << cpp_strerror(r) << dendl;
+  }
+  finish(r);
+}
+
+template <typename I>
+void ShutDownCryptoRequest<I>::finish(int r) {
+  if (r == 0) {
+    std::unique_lock image_locker{m_image_ctx->image_lock};
+    m_image_ctx->crypto = nullptr;
+  }
+
+  m_on_finish->complete(r);
+  delete this;
+}
+
+} // namespace crypto
+} // namespace librbd
+
+template class librbd::crypto::ShutDownCryptoRequest<librbd::ImageCtx>;
diff --git a/src/librbd/crypto/ShutDownCryptoRequest.h b/src/librbd/crypto/ShutDownCryptoRequest.h
new file mode 100644 (file)
index 0000000..6814280
--- /dev/null
@@ -0,0 +1,45 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H
+#define CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H
+
+#include "librbd/crypto/CryptoInterface.h"
+
+struct Context;
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace crypto {
+
+class CryptoInterface;
+
+template <typename I>
+class ShutDownCryptoRequest {
+public:
+    static ShutDownCryptoRequest* create(I* image_ctx, Context* on_finish) {
+      return new ShutDownCryptoRequest(image_ctx, on_finish);
+    }
+
+    ShutDownCryptoRequest(I* image_ctx, Context* on_finish);
+    void send();
+    void shut_down_object_dispatch();
+    void handle_shut_down_object_dispatch(int r);
+    void shut_down_image_dispatch();
+    void handle_shut_down_image_dispatch(int r);
+    void finish(int r);
+
+private:
+    I* m_image_ctx;
+    Context* m_on_finish;
+    ceph::ref_t<CryptoInterface> m_crypto;
+};
+
+} // namespace crypto
+} // namespace librbd
+
+extern template class librbd::crypto::ShutDownCryptoRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_CRYPTO_SHUT_DOWN_CRYPTO_REQUEST_H
diff --git a/src/librbd/crypto/Utils.cc b/src/librbd/crypto/Utils.cc
new file mode 100644 (file)
index 0000000..76cc9f1
--- /dev/null
@@ -0,0 +1,73 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "Utils.h"
+
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/crypto/BlockCrypto.h"
+#include "librbd/crypto/CryptoImageDispatch.h"
+#include "librbd/crypto/CryptoObjectDispatch.h"
+#include "librbd/crypto/openssl/DataCryptor.h"
+#include "librbd/io/ImageDispatcherInterface.h"
+#include "librbd/io/ObjectDispatcherInterface.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::crypto::util: " << __func__ << ": "
+
+namespace librbd {
+namespace crypto {
+namespace util {
+
+template <typename I>
+void set_crypto(I *image_ctx, ceph::ref_t<CryptoInterface> crypto) {
+  {
+    std::unique_lock image_locker{image_ctx->image_lock};
+    ceph_assert(image_ctx->crypto == nullptr);
+    image_ctx->crypto = crypto.get();
+  }
+  auto object_dispatch = CryptoObjectDispatch<I>::create(image_ctx, crypto);
+  auto image_dispatch = CryptoImageDispatch::create(crypto->get_data_offset());
+  image_ctx->io_object_dispatcher->register_dispatch(object_dispatch);
+  image_ctx->io_image_dispatcher->register_dispatch(image_dispatch);
+}
+
+int build_crypto(
+        CephContext* cct, const unsigned char* key, uint32_t key_length,
+        uint64_t block_size, uint64_t data_offset,
+        ceph::ref_t<CryptoInterface>* result_crypto) {
+  const char* cipher_suite;
+  switch (key_length) {
+    case 32:
+      cipher_suite = "aes-128-xts";
+      break;
+    case 64:
+      cipher_suite = "aes-256-xts";
+      break;
+    default:
+      lderr(cct) << "unsupported key length: " << key_length << dendl;
+      return -ENOTSUP;
+  }
+
+  auto data_cryptor = new openssl::DataCryptor(cct);
+  int r = data_cryptor->init(cipher_suite, key, key_length);
+  if (r != 0) {
+    lderr(cct) << "error initializing data cryptor: " << cpp_strerror(r)
+               << dendl;
+    delete data_cryptor;
+    return r;
+  }
+
+  *result_crypto = BlockCrypto<EVP_CIPHER_CTX>::create(
+          cct, data_cryptor, block_size, data_offset);
+  return 0;
+}
+
+} // namespace util
+} // namespace crypto
+} // namespace librbd
+
+template void librbd::crypto::util::set_crypto(
+    librbd::ImageCtx *image_ctx, ceph::ref_t<CryptoInterface> crypto);
diff --git a/src/librbd/crypto/Utils.h b/src/librbd/crypto/Utils.h
new file mode 100644 (file)
index 0000000..aed3d47
--- /dev/null
@@ -0,0 +1,29 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_CRYPTO_UTILS_H
+#define CEPH_LIBRBD_CRYPTO_UTILS_H
+
+#include "include/Context.h"
+#include "librbd/crypto/CryptoInterface.h"
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace crypto {
+namespace util {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+void set_crypto(ImageCtxT *image_ctx, ceph::ref_t<CryptoInterface> crypto);
+
+int build_crypto(
+        CephContext* cct, const unsigned char* key, uint32_t key_length,
+        uint64_t block_size, uint64_t data_offset,
+        ceph::ref_t<CryptoInterface>* result_crypto);
+
+} // namespace util
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_UTILS_H
index d89ef3639d538392b166414fe63c8315f6ce1e9e..8b1b1580c0635482ec2492263e2eb07d5e5e3db4 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "EncryptionFormat.h"
+#include "include/compat.h"
 #include "librbd/crypto/luks/FormatRequest.h"
 #include "librbd/crypto/luks/LoadRequest.h"
 
@@ -16,20 +17,24 @@ EncryptionFormat<I>::EncryptionFormat(
                                     m_passphrase(std::move(passphrase)) {
 }
 
+template <typename I>
+EncryptionFormat<I>::~EncryptionFormat() {
+  ceph_memzero_s(
+          &m_passphrase[0], m_passphrase.capacity(), m_passphrase.size());
+}
+
 template <typename I>
 void EncryptionFormat<I>::format(I* image_ctx, Context* on_finish) {
   auto req = luks::FormatRequest<I>::create(
-          image_ctx, get_format(), m_alg, std::move(m_passphrase), on_finish,
-          false);
+          image_ctx, get_format(), m_alg, std::move(m_passphrase), &m_crypto,
+          on_finish, false);
   req->send();
 }
 
 template <typename I>
-void EncryptionFormat<I>::load(
-        I* image_ctx, ceph::ref_t<CryptoInterface>* result_crypto,
-        Context* on_finish) {
+void EncryptionFormat<I>::load(I* image_ctx, Context* on_finish) {
   auto req = luks::LoadRequest<I>::create(
-          image_ctx, get_format(), std::move(m_passphrase), result_crypto,
+          image_ctx, get_format(), std::move(m_passphrase), &m_crypto,
           on_finish);
   req->send();
 }
index a45a3c91201f5fe1f9374985eaff9e794338a5b1..8c45cf9ca00a10d4cfc36718ef55aeb948f6c1ac 100644 (file)
@@ -19,16 +19,21 @@ class EncryptionFormat : public crypto::EncryptionFormat<ImageCtxT> {
 
 public:
     EncryptionFormat(encryption_algorithm_t alg, std::string&& passphrase);
+    ~EncryptionFormat();
 
     void format(ImageCtxT* ictx, Context* on_finish) override;
-    void load(ImageCtxT* ictx, ceph::ref_t<CryptoInterface>* result_crypto,
-              Context* on_finish) override;
+    void load(ImageCtxT* ictx, Context* on_finish) override;
+
+    ceph::ref_t<CryptoInterface> get_crypto() override {
+      return m_crypto;
+    }
 
 private:
     virtual encryption_format_t get_format() = 0;
 
     encryption_algorithm_t m_alg;
     std::string m_passphrase;
+    ceph::ref_t<CryptoInterface> m_crypto;
 };
 
 template <typename ImageCtxT>
index b383d12ca887afad0c17b046b75d1fc5a574fe1f..be2452fd924c37118e080332e857b434ed93ef2a 100644 (file)
@@ -3,9 +3,13 @@
 
 #include "FormatRequest.h"
 
+#include <stdlib.h>
+#include <openssl/rand.h>
 #include "common/dout.h"
 #include "common/errno.h"
+#include "include/compat.h"
 #include "librbd/Utils.h"
+#include "librbd/crypto/Utils.h"
 #include "librbd/crypto/luks/Header.h"
 #include "librbd/io/AioCompletion.h"
 #include "librbd/io/ImageDispatchSpec.h"
@@ -24,10 +28,12 @@ using librbd::util::create_context_callback;
 template <typename I>
 FormatRequest<I>::FormatRequest(
         I* image_ctx, encryption_format_t format, encryption_algorithm_t alg,
-        std::string&& passphrase, Context* on_finish,
+        std::string&& passphrase, ceph::ref_t<CryptoInterface>* result_crypto,
+        Context* on_finish,
         bool insecure_fast_mode) : m_image_ctx(image_ctx), m_format(format),
                                    m_alg(alg),
                                    m_passphrase(std::move(passphrase)),
+                                   m_result_crypto(result_crypto),
                                    m_on_finish(on_finish),
                                    m_insecure_fast_mode(insecure_fast_mode),
                                    m_header(image_ctx->cct) {
@@ -71,6 +77,15 @@ void FormatRequest<I>::send() {
       return;
   }
 
+  // generate encryption key
+  unsigned char* key = (unsigned char*)alloca(key_size);
+  if (RAND_bytes((unsigned char *)key, key_size) != 1) {
+    lderr(m_image_ctx->cct) << "cannot generate random encryption key"
+                            << dendl;
+    finish(-EAGAIN);
+    return;
+  }
+
   // setup interface with libcryptsetup
   auto r = m_header.init();
   if (r < 0) {
@@ -79,7 +94,8 @@ void FormatRequest<I>::send() {
   }
 
   // format (create LUKS header)
-  r = m_header.format(type, cipher, key_size, "xts-plain64", sector_size,
+  r = m_header.format(type, cipher, reinterpret_cast<char*>(key), key_size,
+                      "xts-plain64", sector_size,
                       m_image_ctx->get_object_size(), m_insecure_fast_mode);
   if (r != 0) {
     finish(r);
@@ -104,6 +120,15 @@ void FormatRequest<I>::send() {
     return;
   }
 
+  r = util::build_crypto(m_image_ctx->cct, key, key_size,
+                         m_header.get_sector_size(),
+                         m_header.get_data_offset(), m_result_crypto);
+  ceph_memzero_s(key, key_size, key_size);
+  if (r != 0) {
+    finish(r);
+    return;
+  }
+
   // read header from libcryptsetup interface
   ceph::bufferlist bl;
   r = m_header.read(&bl);
@@ -116,7 +141,7 @@ void FormatRequest<I>::send() {
   auto ctx = create_context_callback<
           FormatRequest<I>, &FormatRequest<I>::handle_write_header>(this);
   auto aio_comp = io::AioCompletion::create_and_start(
-          ctx, util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE);
+          ctx, librbd::util::get_image_ctx(m_image_ctx), io::AIO_TYPE_WRITE);
 
   ZTracer::Trace trace;
   auto req = io::ImageDispatchSpec::create_write(
@@ -140,7 +165,8 @@ void FormatRequest<I>::handle_write_header(int r) {
 
 template <typename I>
 void FormatRequest<I>::finish(int r) {
-  ceph_memzero_s(&m_passphrase[0], m_passphrase.capacity(), m_passphrase.size());
+  ceph_memzero_s(
+          &m_passphrase[0], m_passphrase.capacity(), m_passphrase.size());
   m_on_finish->complete(r);
   delete this;
 }
index 1723185422da417615e915172878c20d67c59d3e..a782dc818fcfcaefa95df86b09e4210d97d9b5c8 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "include/rbd/librbd.hpp"
 #include "librbd/ImageCtx.h"
+#include "librbd/crypto/CryptoInterface.h"
 #include "librbd/crypto/luks/Header.h"
 
 namespace librbd {
@@ -21,13 +22,15 @@ public:
     static FormatRequest* create(
             I* image_ctx, encryption_format_t format,
             encryption_algorithm_t alg, std::string&& passphrase,
-            Context* on_finish, bool insecure_fast_mode) {
+            ceph::ref_t<CryptoInterface>* result_crypto, Context* on_finish,
+            bool insecure_fast_mode) {
       return new FormatRequest(image_ctx, format, alg, std::move(passphrase),
-                               on_finish, insecure_fast_mode);
+                               result_crypto, on_finish, insecure_fast_mode);
     }
 
     FormatRequest(I* image_ctx, encryption_format_t format,
                   encryption_algorithm_t alg, std::string&& passphrase,
+                  ceph::ref_t<CryptoInterface>* result_crypto,
                   Context* on_finish, bool insecure_fast_mode);
     void send();
     void finish(int r);
@@ -38,6 +41,7 @@ private:
     encryption_format_t m_format;
     encryption_algorithm_t m_alg;
     std::string m_passphrase;
+    ceph::ref_t<CryptoInterface>* m_result_crypto;
     Context* m_on_finish;
     bool m_insecure_fast_mode;
     Header m_header;
index 4307844f50467bec19898b2042d8b9eae1847d2a..6d00074efcf80157b98bb28f25019f44dade1104 100644 (file)
@@ -114,9 +114,10 @@ ssize_t Header::read(ceph::bufferlist* bl) {
   return r;
 }
 
-int Header::format(const char* type, const char* alg, size_t key_size,
-                   const char* cipher_mode, uint32_t sector_size,
-                   uint32_t data_alignment, bool insecure_fast_mode) {
+int Header::format(const char* type, const char* alg, const char* key,
+                   size_t key_size, const char* cipher_mode,
+                   uint32_t sector_size, uint32_t data_alignment,
+                   bool insecure_fast_mode) {
   ceph_assert(m_cd != nullptr);
 
   ldout(m_cct, 20) << "sector size: " << sector_size << ", data alignment: "
@@ -168,7 +169,7 @@ int Header::format(const char* type, const char* alg, size_t key_size,
   }
 
   auto r = crypt_format(
-          m_cd, type, alg, cipher_mode, NULL, NULL, key_size, params);
+          m_cd, type, alg, cipher_mode, NULL, key, key_size, params);
   if (r != 0) {
     lderr(m_cct) << "crypt_format failed: " << cpp_strerror(r) << dendl;
     return r;
index 13ef8fd20d31be3e3ae59f1a521a0cebaef51955..cee80a8e4628fdf06b37348d20808088e46aaafb 100644 (file)
@@ -21,8 +21,8 @@ public:
     int write(const ceph::bufferlist& bl);
     ssize_t read(ceph::bufferlist* bl);
 
-    int format(const char* type, const char* alg, size_t key_size,
-               const char* cipher_mode, uint32_t sector_size,
+    int format(const char* type, const char* alg, const char* key,
+               size_t key_size, const char* cipher_mode, uint32_t sector_size,
                uint32_t data_alignment, bool insecure_fast_mode);
     int add_keyslot(const char* passphrase, size_t passphrase_size);
     int load(const char* type);
index f23b7ec8b01eeaa24343df046a7dfb9562b6dce2..10339469d3de130fbe2c57fd8080d42b0235eced 100644 (file)
@@ -6,8 +6,7 @@
 #include "common/dout.h"
 #include "common/errno.h"
 #include "librbd/Utils.h"
-#include "librbd/crypto/BlockCrypto.h"
-#include "librbd/crypto/openssl/DataCryptor.h"
+#include "librbd/crypto/Utils.h"
 #include "librbd/io/AioCompletion.h"
 #include "librbd/io/ImageDispatchSpec.h"
 #include "librbd/io/ReadResult.h"
@@ -59,7 +58,8 @@ template <typename I>
 void LoadRequest<I>::read(uint64_t end_offset, Context* on_finish) {
   auto length = end_offset - m_offset;
   auto aio_comp = io::AioCompletion::create_and_start(
-          on_finish, util::get_image_ctx(m_image_ctx), io::AIO_TYPE_READ);
+          on_finish, librbd::util::get_image_ctx(m_image_ctx),
+          io::AIO_TYPE_READ);
   ZTracer::Trace trace;
   auto req = io::ImageDispatchSpec::create_read(
           *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp,
@@ -175,39 +175,11 @@ void LoadRequest<I>::read_volume_key() {
     return;
   }
 
-  const char* cipher_suite;
-  switch (volume_key_size) {
-    case 32:
-      cipher_suite = "aes-128-xts";
-      break;
-    case 64:
-      cipher_suite = "aes-256-xts";
-      break;
-    default:
-      lderr(m_image_ctx->cct) << "unsupported volume key size: "
-                              << volume_key_size << dendl;
-      finish(-ENOTSUP);
-      return;
-  }
-
-
-  auto data_cryptor = new openssl::DataCryptor(m_image_ctx->cct);
-  r = data_cryptor->init(
-          cipher_suite, reinterpret_cast<unsigned char*>(volume_key),
-          volume_key_size);
-  if (r != 0) {
-    lderr(m_image_ctx->cct) << "error initializing data cryptor: " << r
-                            << dendl;
-    delete data_cryptor;
-    finish(r);
-    return;
-  }
-
-  auto sector_size = m_header.get_sector_size();
-  auto data_offset = m_header.get_data_offset();
-  *m_result_crypto = BlockCrypto<EVP_CIPHER_CTX>::create(
-          m_image_ctx->cct, data_cryptor, sector_size, data_offset);
-  finish(0);
+  r = util::build_crypto(
+          m_image_ctx->cct, reinterpret_cast<unsigned char*>(volume_key),
+          volume_key_size, m_header.get_sector_size(),
+          m_header.get_data_offset(), m_result_crypto);
+  finish(r);
 }
 
 template <typename I>
index 6d2ce0ff732986e6967affd6f961c658c451cf95..0c164b048f9cdad73202183fa1254053ae243338 100644 (file)
@@ -62,6 +62,14 @@ uint32_t DataCryptor::get_iv_size() const {
   return m_iv_size;
 }
 
+const unsigned char* DataCryptor::get_key() const {
+  return m_key;
+}
+
+int DataCryptor::get_key_length() const {
+  return EVP_CIPHER_key_length(m_cipher);
+}
+
 EVP_CIPHER_CTX* DataCryptor::get_context(CipherMode mode) {
   int enc;
   switch(mode) {
@@ -130,7 +138,8 @@ void DataCryptor::log_errors() const {
     if (error == 0) {
       break;
     }
-    lderr(m_cct) << "OpenSSL error: " << error << dendl;
+    lderr(m_cct) << "OpenSSL error: " << ERR_error_string(error, nullptr)
+                 << dendl;
   }
 }
 
index 0c2795ef63e1196d36dc97856b4a03d48c062c1d..c9a177ef595b4099609cf819cd40f9aa07cfc294 100644 (file)
@@ -22,6 +22,9 @@ public:
              uint16_t key_length);
     uint32_t get_block_size() const override;
     uint32_t get_iv_size() const override;
+    const unsigned char* get_key() const override;
+    int get_key_length() const override;
+
     EVP_CIPHER_CTX* get_context(CipherMode mode) override;
     void return_context(EVP_CIPHER_CTX* ctx, CipherMode mode) override;
     int init_context(EVP_CIPHER_CTX* ctx, const unsigned char* iv,
index 46165f208f9d1a9066754094218acba52d3b2baf..3b4db0cefe19226a91e6840c7849288fa4c050c7 100644 (file)
@@ -171,7 +171,7 @@ void S3Stream<I>::process_request(HttpRequest& http_request) {
 
   // create HMAC-SHA1 signature from secret key + string-to-sign
   sha1_digest_t digest;
-  crypto::HMACSHA1 hmac(
+  ceph::crypto::HMACSHA1 hmac(
     reinterpret_cast<const unsigned char*>(m_secret_key.data()),
     m_secret_key.size());
   hmac.Update(reinterpret_cast<const unsigned char*>(string_to_sign.data()),
index 68b1b75dba51daa5cb4eb0a8a043690302ca2528..6954d22da46bdebe11c1785691aeda4cb6cdc539 100644 (file)
@@ -58,6 +58,7 @@ set(unittest_librbd_srcs
   crypto/test_mock_CryptoObjectDispatch.cc
   crypto/test_mock_FormatRequest.cc
   crypto/test_mock_LoadRequest.cc
+  crypto/test_mock_ShutDownCryptoRequest.cc
   crypto/openssl/test_DataCryptor.cc
   deep_copy/test_mock_ImageCopyRequest.cc
   deep_copy/test_mock_MetadataCopyRequest.cc
index 8987163660993f661d9bdcd371ffc3c69b8f173a..189c666debc5e0f90311873104311fa8b47f0eaa 100644 (file)
@@ -38,6 +38,7 @@ struct TestMockCryptoLuksFormatRequest : public TestMockFixture {
   Context *on_finish = &finished_cond;
   io::AioCompletion* aio_comp;
   ceph::bufferlist header_bl;
+  ceph::ref_t<CryptoInterface> crypto;
 
   void SetUp() override {
     TestMockFixture::SetUp();
@@ -105,14 +106,20 @@ struct TestMockCryptoLuksFormatRequest : public TestMockFixture {
     ASSERT_EQ(0, header.read_volume_key(
             passphrase_cstr, strlen(passphrase_cstr),
             reinterpret_cast<char*>(volume_key), &volume_key_size));
+
+    ASSERT_EQ(expected_key_length, crypto->get_key_length());
+    ASSERT_EQ(0, std::memcmp(
+            volume_key, crypto->get_key(), expected_key_length));
+    ASSERT_EQ(expected_sector_size, crypto->get_block_size());
+    ASSERT_EQ(header.get_data_offset(), crypto->get_data_offset());
   }
 };
 
 TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) {
   auto mock_format_request = MockFormatRequest::create(
           mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1,
-          RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), on_finish,
-          true);
+          RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto,
+          on_finish, true);
   expect_get_object_size();
   expect_get_image_size(IMAGE_SIZE);
   expect_image_write();
@@ -126,8 +133,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) {
 TEST_F(TestMockCryptoLuksFormatRequest, AES128) {
   auto mock_format_request = MockFormatRequest::create(
           mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
-          RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), on_finish,
-          true);
+          RBD_ENCRYPTION_ALGORITHM_AES128, std::move(passphrase), &crypto,
+          on_finish, true);
   expect_get_object_size();
   expect_get_image_size(IMAGE_SIZE);
   expect_image_write();
@@ -141,8 +148,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, AES128) {
 TEST_F(TestMockCryptoLuksFormatRequest, AES256) {
   auto mock_format_request = MockFormatRequest::create(
           mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
-          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish,
-          true);
+          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+          on_finish, true);
   expect_get_object_size();
   expect_get_image_size(IMAGE_SIZE);
   expect_image_write();
@@ -150,14 +157,14 @@ TEST_F(TestMockCryptoLuksFormatRequest, AES256) {
   ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
   complete_aio(0);
   ASSERT_EQ(0, finished_cond.wait());
-  ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 62, 4096));
+  ASSERT_NO_FATAL_FAILURE(verify_header(CRYPT_LUKS2, 64, 4096));
 }
 
 TEST_F(TestMockCryptoLuksFormatRequest, ImageTooSmall) {
   auto mock_format_request = MockFormatRequest::create(
           mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
-          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish,
-          true);
+          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+          on_finish, true);
   expect_get_object_size();
   expect_get_image_size(1024*1024);
   mock_format_request->send();
@@ -167,8 +174,8 @@ TEST_F(TestMockCryptoLuksFormatRequest, ImageTooSmall) {
 TEST_F(TestMockCryptoLuksFormatRequest, WriteFail) {
   auto mock_format_request = MockFormatRequest::create(
           mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2,
-          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), on_finish,
-          true);
+          RBD_ENCRYPTION_ALGORITHM_AES256, std::move(passphrase), &crypto,
+          on_finish, true);
   expect_get_object_size();
   expect_get_image_size(IMAGE_SIZE);
   expect_image_write();
index 6f79f3c1f5855e1bb4a33fb3ed1399df6361f22b..b67b5862fdd4cb5a078a8699585b14423c139a31 100644 (file)
@@ -67,8 +67,8 @@ struct TestMockCryptoLuksLoadRequest : public TestMockFixture {
     Header header(mock_image_ctx->cct);
 
     ASSERT_EQ(0, header.init());
-    ASSERT_EQ(0, header.format(type, alg, key_size, cipher_mode, sector_size,
-                               OBJECT_SIZE, true));
+    ASSERT_EQ(0, header.format(type, alg, nullptr, key_size, cipher_mode,
+                               sector_size, OBJECT_SIZE, true));
     ASSERT_EQ(0, header.add_keyslot(passphrase_cstr, strlen(passphrase_cstr)));
     ASSERT_LE(0, header.read(&header_bl));
 
index 722f768da544ca7b8a460a970b951ce88a4109d4..2008deb68c2c74addd87745b501c70b415977405 100644 (file)
@@ -4,26 +4,73 @@
 #include "test/librbd/test_mock_fixture.h"
 #include "test/librbd/test_support.h"
 #include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
 #include "test/librbd/mock/crypto/MockEncryptionFormat.h"
+#include "librbd/crypto/Utils.h"
+
+namespace librbd {
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+  return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
 
 #include "librbd/crypto/FormatRequest.cc"
 
 namespace librbd {
 namespace crypto {
 
+namespace util {
+
+template <> void set_crypto(
+        MockImageCtx *image_ctx, ceph::ref_t<CryptoInterface> crypto) {
+  image_ctx->crypto = crypto.get();
+}
+
+} // namespace util
+
 using ::testing::_;
 using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::WithArg;
 
+template <>
+struct ShutDownCryptoRequest<MockImageCtx> {
+  Context *on_finish = nullptr;
+  static ShutDownCryptoRequest *s_instance;
+  static ShutDownCryptoRequest *create(
+          MockImageCtx* image_ctx, Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  ShutDownCryptoRequest() {
+    s_instance = this;
+  }
+};
+
+ShutDownCryptoRequest<MockImageCtx> *ShutDownCryptoRequest<
+        MockImageCtx>::s_instance = nullptr;
+
 struct TestMockCryptoFormatRequest : public TestMockFixture {
   typedef FormatRequest<librbd::MockImageCtx> MockFormatRequest;
+  typedef ShutDownCryptoRequest<MockImageCtx> MockShutDownCryptoRequest;
 
   MockImageCtx* mock_image_ctx;
   C_SaferCond finished_cond;
   Context *on_finish = &finished_cond;
   MockEncryptionFormat* mock_encryption_format;
   Context* format_context;
+  MockCryptoInterface* crypto;
+  MockCryptoInterface* old_crypto;
+  MockFormatRequest* mock_format_request;
+  std::string key = std::string(64, '0');
 
   void SetUp() override {
     TestMockFixture::SetUp();
@@ -32,18 +79,22 @@ struct TestMockCryptoFormatRequest : public TestMockFixture {
     ASSERT_EQ(0, open_image(m_image_name, &ictx));
     mock_image_ctx = new MockImageCtx(*ictx);
     mock_encryption_format = new MockEncryptionFormat();
+    crypto = new MockCryptoInterface();
+    old_crypto = new MockCryptoInterface();
+    mock_image_ctx->crypto = old_crypto;
+    mock_format_request = MockFormatRequest::create(
+          mock_image_ctx,
+          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
+          on_finish);
   }
 
   void TearDown() override {
+    crypto->put();
+    old_crypto->put();
     delete mock_image_ctx;
     TestMockFixture::TearDown();
   }
 
-  void expect_crypto_layer_exists_check(bool exists) {
-    EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists(
-            io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
-  }
-
   void expect_test_journal_feature(bool has_journal=false) {
     EXPECT_CALL(*mock_image_ctx, test_features(
             RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal));
@@ -56,66 +107,88 @@ struct TestMockCryptoFormatRequest : public TestMockFixture {
                       format_context = ctx;
     })));
   }
-};
 
-TEST_F(TestMockCryptoFormatRequest, CryptoAlreadyLoaded) {
-  auto mock_format_request = MockFormatRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(true);
-  mock_format_request->send();
-  ASSERT_EQ(-EEXIST, finished_cond.wait());
-}
+  void expect_image_flush(int r) {
+    EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_)).WillOnce(
+            Invoke([r](io::ImageDispatchSpec* spec) {
+              ASSERT_TRUE(boost::get<io::ImageDispatchSpec::Flush>(
+                      &spec->request) != nullptr);
+              spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+              spec->aio_comp->set_request_count(1);
+              spec->aio_comp->add_request();
+              spec->aio_comp->complete_request(r);
+            }));
+  }
+};
 
 TEST_F(TestMockCryptoFormatRequest, JournalEnabled) {
-  auto mock_format_request = MockFormatRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
   expect_test_journal_feature(true);
   mock_format_request->send();
   ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+  ASSERT_EQ(old_crypto, mock_image_ctx->crypto);
 }
 
-TEST_F(TestMockCryptoFormatRequest, CloneFormat) {
-  mock_image_ctx->parent = mock_image_ctx;
-  auto mock_format_request = MockFormatRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
+TEST_F(TestMockCryptoFormatRequest, FailShutDownCrypto) {
+  expect_test_journal_feature(false);
+  MockShutDownCryptoRequest mock_shutdown_crypto_request;
+  EXPECT_CALL(mock_shutdown_crypto_request, send());
   mock_format_request->send();
-  ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  mock_shutdown_crypto_request.on_finish->complete(-EIO);
+  ASSERT_EQ(-EIO, finished_cond.wait());
+  ASSERT_EQ(old_crypto, mock_image_ctx->crypto);
 }
 
 TEST_F(TestMockCryptoFormatRequest, FormatFail) {
-  auto mock_format_request = MockFormatRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
+  mock_image_ctx->crypto = nullptr;
   expect_test_journal_feature(false);
   expect_encryption_format();
   mock_format_request->send();
   ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
   format_context->complete(-EIO);
   ASSERT_EQ(-EIO, finished_cond.wait());
+  ASSERT_EQ(nullptr, mock_image_ctx->crypto);
 }
 
 TEST_F(TestMockCryptoFormatRequest, Success) {
-  auto mock_format_request = MockFormatRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
+  mock_image_ctx->crypto = nullptr;
+  expect_test_journal_feature(false);
+  expect_encryption_format();
+  mock_format_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_image_flush(0);
+  EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto));
+  format_context->complete(0);
+  ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(crypto, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockCryptoFormatRequest, FailFlush) {
+  mock_image_ctx->crypto = nullptr;
   expect_test_journal_feature(false);
   expect_encryption_format();
   mock_format_request->send();
   ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_image_flush(-EIO);
+  format_context->complete(0);
+  ASSERT_EQ(-EIO, finished_cond.wait());
+  ASSERT_EQ(nullptr, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockCryptoFormatRequest, CryptoAlreadyLoaded) {
+  expect_test_journal_feature(false);
+  MockShutDownCryptoRequest mock_shutdown_crypto_request;
+  EXPECT_CALL(mock_shutdown_crypto_request, send());
+  mock_format_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_encryption_format();
+  mock_shutdown_crypto_request.on_finish->complete(0);
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_image_flush(0);
+  EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto));
   format_context->complete(0);
   ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(crypto, mock_image_ctx->crypto);
 }
 
 } // namespace crypto
index ac36b65dbb849fb2dbbe65016e757ff2398bf2f0..ddf57d26de74ed909e6ebc581b4ac0a2b71cb128 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "librbd/crypto/CryptoObjectDispatch.h"
+#include "librbd/crypto/Utils.h"
 #include "test/librbd/test_mock_fixture.h"
 #include "test/librbd/test_support.h"
 #include "test/librbd/mock/MockImageCtx.h"
@@ -44,7 +45,9 @@ struct TestMockCryptoLoadRequest : public TestMockFixture {
   C_SaferCond finished_cond;
   Context *on_finish = &finished_cond;
   MockEncryptionFormat* mock_encryption_format;
+  MockCryptoInterface* crypto;
   Context* load_context;
+  MockLoadRequest* mock_load_request;
 
   void SetUp() override {
     TestMockFixture::SetUp();
@@ -53,63 +56,52 @@ struct TestMockCryptoLoadRequest : public TestMockFixture {
     ASSERT_EQ(0, open_image(m_image_name, &ictx));
     mock_image_ctx = new MockImageCtx(*ictx);
     mock_encryption_format = new MockEncryptionFormat();
+    crypto = new MockCryptoInterface();
+    mock_load_request = MockLoadRequest::create(
+          mock_image_ctx,
+          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
+          on_finish);
   }
 
   void TearDown() override {
+    crypto->put();
     delete mock_image_ctx;
     TestMockFixture::TearDown();
   }
 
-  void expect_crypto_layer_exists_check(bool exists) {
-    EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists(
-            io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+  void expect_test_journal_feature() {
+    expect_test_journal_feature(mock_image_ctx, false);
   }
 
-  void expect_test_journal_feature(bool has_journal=false) {
-    EXPECT_CALL(*mock_image_ctx, test_features(
+  void expect_test_journal_feature(MockImageCtx* ctx, bool has_journal=false) {
+    EXPECT_CALL(*ctx, test_features(
             RBD_FEATURE_JOURNALING)).WillOnce(Return(has_journal));
   }
 
   void expect_encryption_load() {
     EXPECT_CALL(*mock_encryption_format, load(
-            mock_image_ctx, _, _)).WillOnce(
-                    WithArgs<1, 2>(Invoke([this](
-                            ceph::ref_t<CryptoInterface>* result_crypto,
-                            Context* ctx) {
+            mock_image_ctx, _)).WillOnce(
+                    WithArgs<1>(Invoke([this](Context* ctx) {
                       load_context = ctx;
-                      *result_crypto = new MockCryptoInterface();
     })));
   }
+
 };
 
 TEST_F(TestMockCryptoLoadRequest, CryptoAlreadyLoaded) {
-  auto mock_load_request = MockLoadRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(true);
+  mock_image_ctx->crypto = new MockCryptoInterface();
   mock_load_request->send();
   ASSERT_EQ(-EEXIST, finished_cond.wait());
 }
 
 TEST_F(TestMockCryptoLoadRequest, JournalEnabled) {
-  auto mock_load_request = MockLoadRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
-  expect_test_journal_feature(true);
+  expect_test_journal_feature(mock_image_ctx, true);
   mock_load_request->send();
   ASSERT_EQ(-ENOTSUP, finished_cond.wait());
 }
 
 TEST_F(TestMockCryptoLoadRequest, LoadFail) {
-  auto mock_load_request = MockLoadRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
-  expect_test_journal_feature(false);
+  expect_test_journal_feature();
   expect_encryption_load();
   mock_load_request->send();
   ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
@@ -118,19 +110,15 @@ TEST_F(TestMockCryptoLoadRequest, LoadFail) {
 }
 
 TEST_F(TestMockCryptoLoadRequest, Success) {
-  auto mock_load_request = MockLoadRequest::create(
-          mock_image_ctx,
-          std::unique_ptr<MockEncryptionFormat>(mock_encryption_format),
-          on_finish);
-  expect_crypto_layer_exists_check(false);
-  expect_test_journal_feature(false);
+  mock_image_ctx->parent = nullptr;
+  expect_test_journal_feature(mock_image_ctx, false);
   expect_encryption_load();
   mock_load_request->send();
   ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
-  EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, register_dispatch(_));
-  EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, register_dispatch(_));
+  EXPECT_CALL(*mock_encryption_format, get_crypto()).WillOnce(Return(crypto));
   load_context->complete(0);
   ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(crypto, mock_image_ctx->crypto);
 }
 
 } // namespace crypto
diff --git a/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc b/src/test/librbd/crypto/test_mock_ShutDownCryptoRequest.cc
new file mode 100644 (file)
index 0000000..4a98873
--- /dev/null
@@ -0,0 +1,141 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/Utils.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/crypto/MockCryptoInterface.h"
+
+#include "librbd/crypto/ShutDownCryptoRequest.cc"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  MockTestImageCtx(librbd::ImageCtx &image_ctx)
+    : librbd::MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+
+namespace crypto {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+struct TestMockShutDownCryptoRequest : public TestMockFixture {
+  typedef ShutDownCryptoRequest<MockTestImageCtx> MockShutDownCryptoRequest;
+
+  MockTestImageCtx* mock_image_ctx;
+  C_SaferCond finished_cond;
+  Context *on_finish = &finished_cond;
+  MockShutDownCryptoRequest* mock_shutdown_crypto_request;
+  Context* shutdown_object_dispatch_context;
+  Context* shutdown_image_dispatch_context;
+
+  void SetUp() override {
+    TestMockFixture::SetUp();
+
+    librbd::ImageCtx *ictx;
+    ASSERT_EQ(0, open_image(m_image_name, &ictx));
+    mock_image_ctx = new MockTestImageCtx(*ictx);
+    mock_image_ctx->crypto = new MockCryptoInterface();
+    mock_shutdown_crypto_request = MockShutDownCryptoRequest::create(
+          mock_image_ctx, on_finish);
+  }
+
+  void TearDown() override {
+    delete mock_image_ctx;
+    TestMockFixture::TearDown();
+  }
+
+  void expect_crypto_object_layer_exists_check(bool exists) {
+    EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists(
+            io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+  }
+
+  void expect_crypto_image_layer_exists_check(bool exists) {
+    EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, exists(
+            io::IMAGE_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+  }
+
+  void expect_shutdown_crypto_object_dispatch() {
+    EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, shut_down_dispatch(
+            io::OBJECT_DISPATCH_LAYER_CRYPTO, _)).WillOnce(
+                    WithArgs<1>(Invoke([this](Context* ctx) {
+                      shutdown_object_dispatch_context = ctx;
+    })));
+  }
+
+  void expect_shutdown_crypto_image_dispatch() {
+    EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, shut_down_dispatch(
+            io::IMAGE_DISPATCH_LAYER_CRYPTO, _)).WillOnce(
+                    WithArgs<1>(Invoke([this](Context* ctx) {
+                      shutdown_image_dispatch_context = ctx;
+    })));
+  }
+};
+
+TEST_F(TestMockShutDownCryptoRequest, NoCryptoObjectDispatch) {
+  expect_crypto_object_layer_exists_check(false);
+  mock_shutdown_crypto_request->send();
+  ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(nullptr, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockShutDownCryptoRequest, FailShutdownObjectDispatch) {
+  expect_crypto_object_layer_exists_check(true);
+  expect_shutdown_crypto_object_dispatch();
+  mock_shutdown_crypto_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  shutdown_object_dispatch_context->complete(-EIO);
+  ASSERT_EQ(-EIO, finished_cond.wait());
+  ASSERT_NE(nullptr, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockShutDownCryptoRequest, NoCryptoImageDispatch) {
+  expect_crypto_object_layer_exists_check(true);
+  expect_shutdown_crypto_object_dispatch();
+  mock_shutdown_crypto_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_crypto_image_layer_exists_check(false);
+  shutdown_object_dispatch_context->complete(0);
+  ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(nullptr, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockShutDownCryptoRequest, FailShutdownImageDispatch) {
+  expect_crypto_object_layer_exists_check(true);
+  expect_shutdown_crypto_object_dispatch();
+  mock_shutdown_crypto_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_crypto_image_layer_exists_check(true);
+  expect_shutdown_crypto_image_dispatch();
+  shutdown_object_dispatch_context->complete(0);
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  shutdown_image_dispatch_context->complete(-EIO);
+  ASSERT_EQ(-EIO, finished_cond.wait());
+  ASSERT_NE(nullptr, mock_image_ctx->crypto);
+}
+
+TEST_F(TestMockShutDownCryptoRequest, Success) {
+  expect_crypto_object_layer_exists_check(true);
+  expect_shutdown_crypto_object_dispatch();
+  mock_shutdown_crypto_request->send();
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  expect_crypto_image_layer_exists_check(true);
+  expect_shutdown_crypto_image_dispatch();
+  shutdown_object_dispatch_context->complete(0);
+  ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+  shutdown_image_dispatch_context->complete(0);
+  ASSERT_EQ(0, finished_cond.wait());
+  ASSERT_EQ(nullptr, mock_image_ctx->crypto);
+}
+
+} // namespace crypto
+} // namespace librbd
index 80307962eb4fd5c2f4de7556a3e83f535dc2df8e..521ecff0c8f32e36cf9c2f1f0e6ad534b8e77c08 100644 (file)
@@ -318,6 +318,8 @@ struct MockImageCtx {
 
   ZTracer::Endpoint trace_endpoint;
 
+  crypto::CryptoInterface* crypto = nullptr;
+
   uint64_t sparse_read_threshold_bytes;
   uint32_t discard_granularity_bytes;
   int mirroring_replay_delay;
index 1263fba95b824437ed1205c02f65b6986c8b404d..027ebde627cdecb7d63fe9ae40f31ea6234d8d58 100644 (file)
@@ -15,6 +15,8 @@ struct MockCryptoInterface : CryptoInterface {
 
   MOCK_METHOD2(encrypt, int(ceph::bufferlist*, uint64_t));
   MOCK_METHOD2(decrypt, int(ceph::bufferlist*, uint64_t));
+  MOCK_CONST_METHOD0(get_key, const unsigned char*());
+  MOCK_CONST_METHOD0(get_key_length, int());
 
   uint64_t get_block_size() const override {
     return 4096;
index 2f0198b5a136fa3505e002df07cbfd058a64a880..51a738862cf9fb927eae40b42644517e96950abe 100644 (file)
@@ -33,6 +33,8 @@ public:
   MOCK_CONST_METHOD4(update_context, int(MockCryptoContext*,
                                          const unsigned char*, unsigned char*,
                                          uint32_t));
+  MOCK_CONST_METHOD0(get_key, const unsigned char*());
+  MOCK_CONST_METHOD0(get_key_length, int());
 };
 
 } // namespace crypto
index 751ee825dab1186026c7cc29f32348396963ce00..1caf99eb068b52a8469efa294b6e9712c13d06a2 100644 (file)
@@ -14,9 +14,8 @@ namespace crypto {
 struct MockEncryptionFormat : EncryptionFormat<MockImageCtx> {
 
   MOCK_METHOD2(format, void(MockImageCtx* ictx, Context* on_finish));
-  MOCK_METHOD3(load, void(MockImageCtx* ictx,
-                          ceph::ref_t<CryptoInterface>* result_crypto,
-                          Context* on_finish));
+  MOCK_METHOD2(load, void(MockImageCtx* ictx, Context* on_finish));
+  MOCK_METHOD0(get_crypto, ceph::ref_t<CryptoInterface>());
 };
 
 } // namespace crypto
index 7a6b020ac77c22c5cf156f2ff0ae4ea7633f65fe..4dbd2e2442a8d979d0afcd21489af55e4e4fcb5c 100644 (file)
@@ -2141,10 +2141,21 @@ TEST_F(TestLibRBD, TestEncryptionLUKS1)
 #else
   ASSERT_EQ(0, rbd_encryption_format(
           image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts)));
-  ASSERT_EQ(0, rbd_encryption_load(
+  ASSERT_EQ(-EEXIST, rbd_encryption_load(
           image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts)));
 
   test_io(image);
+
+  bool passed;
+  write_test_data(image, "test", 0, 4, 0, &passed);
+  ASSERT_TRUE(passed);
+  ASSERT_EQ(0, rbd_close(image));
+
+  ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+  ASSERT_EQ(0, rbd_encryption_load(
+          image, RBD_ENCRYPTION_FORMAT_LUKS1, &opts, sizeof(opts)));
+  read_test_data(image, "test", 0, 4, 0, &passed);
+  ASSERT_TRUE(passed);
 #endif
 
   ASSERT_EQ(0, rbd_close(image));
@@ -2182,10 +2193,21 @@ TEST_F(TestLibRBD, TestEncryptionLUKS2)
 #else
   ASSERT_EQ(0, rbd_encryption_format(
           image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts)));
-  ASSERT_EQ(0, rbd_encryption_load(
+  ASSERT_EQ(-EEXIST, rbd_encryption_load(
           image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts)));
 
   test_io(image);
+
+  bool passed;
+  write_test_data(image, "test", 0, 4, 0, &passed);
+  ASSERT_TRUE(passed);
+  ASSERT_EQ(0, rbd_close(image));
+
+  ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+  ASSERT_EQ(0, rbd_encryption_load(
+          image, RBD_ENCRYPTION_FORMAT_LUKS2, &opts, sizeof(opts)));
+  read_test_data(image, "test", 0, 4, 0, &passed);
+  ASSERT_TRUE(passed);
 #endif
 
   ASSERT_EQ(0, rbd_close(image));