#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")))
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
template <typename> class PluginRegistry;
namespace asio { struct ContextWQ; }
+ namespace crypto { class CryptoInterface; }
namespace exclusive_lock { struct Policy; }
namespace io {
class AioCompletion;
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,
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);
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;
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);
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) {
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;
}
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
#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
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;
}
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:
#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
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;
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;
}
}
I* m_image_ctx;
std::unique_ptr<EncryptionFormat<I>> m_format;
Context* m_on_finish;
- ceph::ref_t<crypto::CryptoInterface> m_crypto;
};
} // namespace crypto
--- /dev/null
+// -*- 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>;
--- /dev/null
+// -*- 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
--- /dev/null
+// -*- 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);
--- /dev/null
+// -*- 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
// 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"
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();
}
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>
#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"
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) {
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) {
}
// 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);
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);
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(
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;
}
#include "include/rbd/librbd.hpp"
#include "librbd/ImageCtx.h"
+#include "librbd/crypto/CryptoInterface.h"
#include "librbd/crypto/luks/Header.h"
namespace librbd {
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);
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;
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: "
}
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;
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);
#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"
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,
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>
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) {
if (error == 0) {
break;
}
- lderr(m_cct) << "OpenSSL error: " << error << dendl;
+ lderr(m_cct) << "OpenSSL error: " << ERR_error_string(error, nullptr)
+ << dendl;
}
}
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,
// 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()),
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
Context *on_finish = &finished_cond;
io::AioCompletion* aio_comp;
ceph::bufferlist header_bl;
+ ceph::ref_t<CryptoInterface> crypto;
void SetUp() override {
TestMockFixture::SetUp();
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();
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();
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();
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();
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();
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));
#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();
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));
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
// 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"
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();
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));
}
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
--- /dev/null
+// -*- 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
ZTracer::Endpoint trace_endpoint;
+ crypto::CryptoInterface* crypto = nullptr;
+
uint64_t sparse_read_threshold_bytes;
uint32_t discard_granularity_bytes;
int mirroring_replay_delay;
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;
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
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
#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));
#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));