endif()
endif()
+# libcryptsetup is only available on linux
+if(WITH_RBD AND LINUX)
+ find_package(libcryptsetup REQUIRED)
+ set(HAVE_LIBCRYPTSETUP ${LIBCRYPTSETUP_FOUND})
+ if(${LIBCRYPTSETUP_VERSION} VERSION_LESS 2.0.5)
+ set(LIBCRYPTSETUP_LEGACY_DATA_ALIGNMENT TRUE)
+ endif()
+endif()
+
include(CMakeDependentOption)
CMAKE_DEPENDENT_OPTION(WITH_ZBD "Enable libzbd bluestore backend" OFF
BuildRequires: leveldb-devel > 1.2
BuildRequires: libaio-devel
BuildRequires: libblkid-devel >= 2.17
+BuildRequires: cryptsetup-devel
BuildRequires: libcurl-devel
BuildRequires: libcap-ng-devel
BuildRequires: fmt-devel >= 5.2.1
--- /dev/null
+# - Find libcryptsetup
+# Sets the following:
+#
+# LIBCRYPTSETUP_INCLUDE_DIR
+# LIBCRYPTSETUP_LIBRARIES
+# LIBCRYPTSETUP_VERSION
+# LIBCRYPTSETUP_FOUND
+
+pkg_search_module(PC_libcryptsetup libcryptsetup)
+
+find_path(LIBCRYPTSETUP_INCLUDE_DIR
+ NAMES libcryptsetup.h
+ PATHS ${PC_libcryptsetup_INCLUDE_DIRS})
+
+find_library(LIBCRYPTSETUP_LIBRARIES
+ NAMES libcryptsetup.so
+ PATHS ${PC_libcryptsetup_LIBRARY_DIRS})
+
+set(LIBCRYPTSETUP_VERSION ${PC_libcryptsetup_VERSION})
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(libcryptsetup
+ REQUIRED_VARS
+ LIBCRYPTSETUP_INCLUDE_DIR
+ LIBCRYPTSETUP_LIBRARIES
+ VERSION_VAR LIBCRYPTSETUP_VERSION)
+
+mark_as_advanced(
+ LIBCRYPTSETUP_LIBRARIES
+ LIBCRYPTSETUP_INCLUDE_DIR
+ LIBCRYPTSETUP_VERSION)
libblkid-dev (>= 2.17),
# Crimson libc-ares-dev,
# Crimson libcrypto++-dev,
+ libcryptsetup-dev,
libcap-ng-dev,
libcunit1-dev,
libcurl4-openssl-dev,
/* Define if PWL-SSD is enabled */
#cmakedefine WITH_RBD_SSD_CACHE
+/* Define if libcryptsetup version < 2.0.5 */
+#cmakedefine LIBCRYPTSETUP_LEGACY_DATA_ALIGNMENT
+
/* Shared library extension, such as .so, .dll or .dylib */
#cmakedefine CMAKE_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@"
list(APPEND librbd_internal_srcs ../common/EventTrace.cc)
endif()
+if(LINUX AND HAVE_LIBCRYPTSETUP)
+ list(APPEND librbd_internal_srcs
+ crypto/luks/Header.cc
+ crypto/luks/FormatRequest.cc
+ crypto/luks/LoadRequest.cc)
+endif()
+
if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
set(librbd_internal_srcs
${librbd_internal_srcs}
osdc rbd_types
OpenSSL::SSL)
target_include_directories(rbd_internal PRIVATE ${OPENSSL_INCLUDE_DIR})
+if(LINUX AND HAVE_LIBCRYPTSETUP)
+ target_include_directories(rbd_internal PRIVATE ${LIBCRYPTSETUP_INCLUDE_DIR})
+ target_link_libraries(rbd_internal PRIVATE ${LIBCRYPTSETUP_LIBRARIES})
+endif()
if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
target_link_libraries(rbd_internal
template <typename T>
BlockCrypto<T>::BlockCrypto(CephContext* cct, DataCryptor<T>* data_cryptor,
- uint64_t block_size)
+ uint64_t block_size, uint64_t data_offset)
: m_cct(cct), m_data_cryptor(data_cryptor), m_block_size(block_size),
- m_iv_size(data_cryptor->get_iv_size()) {
+ m_data_offset(data_offset), m_iv_size(data_cryptor->get_iv_size()) {
ceph_assert(isp2(block_size));
ceph_assert((block_size % data_cryptor->get_block_size()) == 0);
}
} // namespace crypto
} // namespace librbd
+
+template class librbd::crypto::BlockCrypto<EVP_CIPHER_CTX>;
#include "include/Context.h"
#include "librbd/crypto/CryptoInterface.h"
-#include "librbd/crypto/DataCryptor.h"
+#include "librbd/crypto/openssl/DataCryptor.h"
namespace librbd {
namespace crypto {
class BlockCrypto : public CryptoInterface {
public:
+ static BlockCrypto* create(CephContext* cct, DataCryptor<T>* data_cryptor,
+ uint32_t block_size, uint64_t data_offset) {
+ return new BlockCrypto(cct, data_cryptor, block_size, data_offset);
+ }
BlockCrypto(CephContext* cct, DataCryptor<T>* data_cryptor,
- uint64_t block_size);
+ uint64_t block_size, uint64_t data_offset);
int encrypt(ceph::bufferlist* data, uint64_t image_offset) override;
int decrypt(ceph::bufferlist* data, uint64_t image_offset) override;
return m_block_size;
}
+ uint64_t get_data_offset() const override {
+ return m_data_offset;
+ }
+
private:
CephContext* m_cct;
DataCryptor<T>* m_data_cryptor;
uint64_t m_block_size;
+ uint64_t m_data_offset;
uint32_t m_iv_size;
int crypt(ceph::bufferlist* data, uint64_t image_offset, CipherMode mode);
} // namespace crypto
} // namespace librbd
+extern template class librbd::crypto::BlockCrypto<EVP_CIPHER_CTX>;
+
#endif //CEPH_LIBRBD_CRYPTO_BLOCK_CRYPTO_H
virtual int encrypt(ceph::bufferlist* data, uint64_t image_offset) = 0;
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;
inline std::pair<uint64_t, uint64_t> get_pre_and_post_align(
uint64_t off, uint64_t len) {
CIPHER_MODE_DEC,
};
+enum DiskEncryptionFormat {
+ DISK_ENCRYPTION_FORMAT_LUKS1,
+ DISK_ENCRYPTION_FORMAT_LUKS2,
+};
+
+enum CipherAlgorithm {
+ CIPHER_ALGORITHM_AES128,
+ CIPHER_ALGORITHM_AES256,
+};
+
} // namespace crypto
} // namespace librbd
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "FormatRequest.h"
+
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/Utils.h"
+#include "librbd/crypto/luks/Header.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ObjectDispatcherInterface.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::crypto::luks::FormatRequest: " << this \
+ << " " << __func__ << ": "
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using librbd::util::create_context_callback;
+
+template <typename I>
+FormatRequest<I>::FormatRequest(
+ I* image_ctx, DiskEncryptionFormat type, CipherAlgorithm cipher,
+ std::string&& passphrase, Context* on_finish,
+ bool insecure_fast_mode) : m_image_ctx(image_ctx), m_type(type),
+ m_cipher(cipher), m_on_finish(on_finish),
+ m_insecure_fast_mode(insecure_fast_mode),
+ m_header(image_ctx->cct),
+ m_passphrase(std::move(passphrase)) {
+}
+
+template <typename I>
+void FormatRequest<I>::send() {
+ if (m_image_ctx->io_object_dispatcher->exists(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO)) {
+ finish(-EEXIST);
+ return;
+ }
+
+ const char* type;
+ size_t sector_size;
+ switch(m_type) {
+ case DISK_ENCRYPTION_FORMAT_LUKS1:
+ type = CRYPT_LUKS1;
+ sector_size = 512;
+ break;
+ case DISK_ENCRYPTION_FORMAT_LUKS2:
+ type = CRYPT_LUKS2;
+ sector_size = 4096;
+ break;
+ default:
+ lderr(m_image_ctx->cct) << "unsupported disk encryption type: " << m_type
+ << dendl;
+ finish(-EINVAL);
+ return;
+ }
+
+ const char* alg;
+ size_t key_size;
+ switch (m_cipher) {
+ case CIPHER_ALGORITHM_AES128:
+ alg = "aes";
+ key_size = 32;
+ break;
+ case CIPHER_ALGORITHM_AES256:
+ alg = "aes";
+ key_size = 64;
+ break;
+ default:
+ lderr(m_image_ctx->cct) << "unsupported cipher algorithm: " << m_cipher
+ << dendl;
+ finish(-EINVAL);
+ return;
+ }
+
+ // setup interface with libcryptsetup
+ auto r = m_header.init();
+ if (r < 0) {
+ finish(r);
+ return;
+ }
+
+ // format (create LUKS header)
+ r = m_header.format(type, alg, key_size, "xts-plain64", sector_size,
+ m_image_ctx->get_object_size(), m_insecure_fast_mode);
+ if (r != 0) {
+ finish(r);
+ return;
+ }
+
+ // add keyslot (volume key encrypted with passphrase)
+ r = m_header.add_keyslot(m_passphrase.c_str(), m_passphrase.size());
+ if (r != 0) {
+ finish(r);
+ return;
+ }
+
+ // read header from libcryptsetup interface
+ ceph::bufferlist bl;
+ r = m_header.read(&bl);
+ if (r < 0) {
+ finish(r);
+ return;
+ }
+
+ // write header to offset 0 of the image
+ 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);
+
+ ZTracer::Trace trace;
+ auto req = io::ImageDispatchSpec::create_write(
+ *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp,
+ {{0, bl.length()}}, std::move(bl),
+ m_image_ctx->get_data_io_context(), 0, trace);
+ req->send();
+}
+
+template <typename I>
+void FormatRequest<I>::handle_write_header(int r) {
+ if (r < 0) {
+ lderr(m_image_ctx->cct) << "error writing header to image: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ finish(0);
+}
+
+template <typename I>
+void FormatRequest<I>::finish(int r) {
+ explicit_bzero(&m_passphrase[0], m_passphrase.size());
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
+
+template class librbd::crypto::luks::FormatRequest<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_LUKS_FORMAT_REQUEST_H
+#define CEPH_LIBRBD_CRYPTO_LUKS_FORMAT_REQUEST_H
+
+#include "librbd/ImageCtx.h"
+#include "librbd/crypto/Types.h"
+#include "librbd/crypto/luks/Header.h"
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace crypto {
+namespace luks {
+
+template <typename I>
+class FormatRequest {
+public:
+ static FormatRequest* create(
+ I* image_ctx, DiskEncryptionFormat type, CipherAlgorithm cipher,
+ std::string&& passphrase, Context* on_finish,
+ bool insecure_fast_mode) {
+ return new FormatRequest(image_ctx, type, cipher, std::move(passphrase),
+ on_finish, insecure_fast_mode);
+ }
+
+ FormatRequest(I* image_ctx, DiskEncryptionFormat type,
+ CipherAlgorithm cipher, std::string&& passphrase,
+ Context* on_finish, bool insecure_fast_mode);
+ void send();
+ void finish(int r);
+
+private:
+ I* m_image_ctx;
+
+ DiskEncryptionFormat m_type;
+ CipherAlgorithm m_cipher;
+ Context* m_on_finish;
+ bool m_insecure_fast_mode;
+ Header m_header;
+ std::string m_passphrase;
+
+ void handle_write_header(int r);
+};
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
+
+extern template class librbd::crypto::luks::FormatRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_CRYPTO_LUKS_FORMAT_REQUEST_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "Header.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include "common/dout.h"
+#include "common/errno.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::crypto::luks::Header: " << this << " " \
+ << __func__ << ": "
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+Header::Header(CephContext* cct) : m_cct(cct), m_fd(-1), m_cd(nullptr) {
+}
+
+Header::~Header() {
+ if (m_fd != -1) {
+ close(m_fd);
+ m_fd = -1;
+ }
+ if (m_cd != nullptr) {
+ crypt_free(m_cd);
+ m_cd = nullptr;
+ }
+}
+
+void Header::libcryptsetup_log_wrapper(int level, const char* msg, void* header) {
+ ((Header*)header)->libcryptsetup_log(level, msg);
+}
+
+void Header::libcryptsetup_log(int level, const char* msg) {
+ switch (level) {
+ case CRYPT_LOG_NORMAL:
+ ldout(m_cct, 5) << "[libcryptsetup] " << msg << dendl;
+ break;
+ case CRYPT_LOG_ERROR:
+ lderr(m_cct) << "[libcryptsetup] " << msg << dendl;
+ break;
+ case CRYPT_LOG_VERBOSE:
+ ldout(m_cct, 10) << "[libcryptsetup] " << msg << dendl;
+ break;
+ case CRYPT_LOG_DEBUG:
+ ldout(m_cct, 20) << "[libcryptsetup] " << msg << dendl;
+ break;
+ }
+}
+
+int Header::init() {
+ // create anonymous file
+ m_fd = syscall(SYS_memfd_create, "LibcryptsetupInterface", 0);
+ if (m_fd == -1) {
+ lderr(m_cct) << "error creating anonymous file: " << cpp_strerror(-errno)
+ << dendl;
+ return -errno;
+ }
+ std::string path =
+ "/proc/" + std::to_string(getpid()) + "/fd/" + std::to_string(m_fd);
+
+ if (m_cct->_conf->subsys.should_gather<dout_subsys, 20>()) {
+ crypt_set_debug_level(CRYPT_DEBUG_ALL);
+ }
+
+ // init libcryptsetup handle
+ auto r = crypt_init(&m_cd, path.c_str());
+ if (r != 0) {
+ lderr(m_cct) << "crypt_init failed: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ // redirect logging
+ crypt_set_log_callback(m_cd, &libcryptsetup_log_wrapper, this);
+
+ return 0;
+}
+
+int Header::write(const ceph::bufferlist& bl) {
+ ceph_assert(m_fd != -1);
+
+ auto r = bl.write_fd(m_fd);
+ if (r != 0) {
+ lderr(m_cct) << "error writing header: " << cpp_strerror(r) << dendl;
+ }
+ return r;
+}
+
+ssize_t Header::read(ceph::bufferlist* bl) {
+ ceph_assert(m_fd != -1);
+
+ // get current header size
+ struct stat st;
+ ssize_t r = fstat(m_fd, &st);
+ if (r < 0) {
+ r = -errno;
+ lderr(m_cct) << "failed to stat anonymous file: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ r = bl->read_fd(m_fd, st.st_size);
+ if (r < 0) {
+ lderr(m_cct) << "error reading header: " << cpp_strerror(r) << dendl;
+ }
+ 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) {
+ ceph_assert(m_cd != nullptr);
+
+ // required for passing libcryptsetup device size check
+ if (ftruncate(m_fd, 4096) != 0) {
+ lderr(m_cct) << "failed to truncate anonymous file: "
+ << cpp_strerror(-errno) << dendl;
+ return -errno;
+ }
+
+ struct crypt_params_luks1 luks1params;
+ struct crypt_params_luks2 luks2params;
+
+#ifdef LIBCRYPTSETUP_LEGACY_DATA_ALIGNMENT
+ size_t converted_data_alignment = data_alignment / sector_size;
+#else
+ size_t converted_data_alignment = data_alignment / 512;
+#endif
+
+
+ void* params = nullptr;
+ if (strcmp(type, CRYPT_LUKS1) == 0) {
+ memset(&luks1params, 0, sizeof(luks1params));
+ luks1params.data_alignment = converted_data_alignment;
+ params = &luks1params;
+ } else if (strcmp(type, CRYPT_LUKS2) == 0) {
+ memset(&luks2params, 0, sizeof(luks2params));
+ luks2params.data_alignment = converted_data_alignment;
+ luks2params.sector_size = sector_size;
+ params = &luks2params;
+ }
+
+ // this mode should be used for testing only
+ if (insecure_fast_mode) {
+ struct crypt_pbkdf_type pbkdf;
+ memset(&pbkdf, 0, sizeof(pbkdf));
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.flags = CRYPT_PBKDF_NO_BENCHMARK;
+ pbkdf.hash = "sha256";
+ pbkdf.iterations = 1000;
+ pbkdf.time_ms = 1;
+ auto r = crypt_set_pbkdf_type(m_cd, &pbkdf);
+ if (r != 0) {
+ lderr(m_cct) << "crypt_set_pbkdf_type failed: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+ }
+
+ auto r = crypt_format(
+ m_cd, type, alg, cipher_mode, NULL, NULL, key_size, params);
+ if (r != 0) {
+ lderr(m_cct) << "crypt_format failed: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+int Header::add_keyslot(const char* passphrase, size_t passphrase_size) {
+ ceph_assert(m_cd != nullptr);
+
+ auto r = crypt_keyslot_add_by_volume_key(
+ m_cd, CRYPT_ANY_SLOT, NULL, 0, passphrase, passphrase_size);
+ if (r != 0) {
+ lderr(m_cct) << "crypt_keyslot_add_by_volume_key failed: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+int Header::load() {
+ ceph_assert(m_cd != nullptr);
+
+ // libcryptsetup checks if device size matches the header and keyslots size
+ // in LUKS2, 2 X 4MB header + 128MB keyslots
+ if (ftruncate(m_fd, 136 * 1024 * 1024) != 0) {
+ lderr(m_cct) << "failed to truncate anonymous file: "
+ << cpp_strerror(-errno) << dendl;
+ return -errno;
+ }
+
+ auto r = crypt_load(m_cd, CRYPT_LUKS, NULL);
+ if (r != 0) {
+ lderr(m_cct) << "crypt_load failed: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+int Header::read_volume_key(const char* passphrase, size_t passphrase_size,
+ char* volume_key, size_t* volume_key_size) {
+ ceph_assert(m_cd != nullptr);
+
+ auto r = crypt_volume_key_get(
+ m_cd, CRYPT_ANY_SLOT, volume_key, volume_key_size, passphrase,
+ passphrase_size);
+ if (r != 0) {
+ lderr(m_cct) << "crypt_volume_key_get failed: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+int Header::get_sector_size() {
+ ceph_assert(m_cd != nullptr);
+ return crypt_get_sector_size(m_cd);
+}
+
+uint64_t Header::get_data_offset() {
+ ceph_assert(m_cd != nullptr);
+ return crypt_get_data_offset(m_cd) << 9;
+}
+
+const char* Header::get_cipher() {
+ ceph_assert(m_cd != nullptr);
+ return crypt_get_cipher(m_cd);
+}
+
+const char* Header::get_cipher_mode() {
+ ceph_assert(m_cd != nullptr);
+ return crypt_get_cipher_mode(m_cd);
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
--- /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_LUKS_HEADER_H
+#define CEPH_LIBRBD_CRYPTO_LUKS_HEADER_H
+
+#include <libcryptsetup.h>
+#include "common/ceph_context.h"
+#include "include/buffer.h"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+class Header {
+public:
+ Header(CephContext* cct);
+ ~Header();
+ int init();
+
+ 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,
+ uint32_t data_alignment, bool insecure_fast_mode);
+ int add_keyslot(const char* passphrase, size_t passphrase_size);
+ int load();
+ int read_volume_key(const char* passphrase, size_t passphrase_size,
+ char* volume_key, size_t* volume_key_size);
+
+ int get_sector_size();
+ uint64_t get_data_offset();
+ const char* get_cipher();
+ const char* get_cipher_mode();
+
+private:
+ void libcryptsetup_log(int level, const char* msg);
+ static void libcryptsetup_log_wrapper(int level, const char* msg,
+ void* header);
+
+ CephContext* m_cct;
+ int m_fd;
+ struct crypt_device *m_cd;
+};
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_LUKS_HEADER_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "LoadRequest.h"
+
+#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/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
+#include "librbd/io/ObjectDispatcherInterface.h"
+#include "librbd/io/ReadResult.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::crypto::luks::LoadRequest: " << this \
+ << " " << __func__ << ": "
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using librbd::util::create_context_callback;
+
+template <typename I>
+LoadRequest<I>::LoadRequest(
+ I* image_ctx, std::string&& passphrase,
+ ceph::ref_t<CryptoInterface>* result_crypto,
+ Context* on_finish) : m_image_ctx(image_ctx), m_on_finish(on_finish),
+ m_result_crypto(result_crypto),
+ m_initial_read_size(DEFAULT_INITIAL_READ_SIZE),
+ m_header(image_ctx->cct), m_offset(0),
+ m_passphrase(std::move(passphrase)) {
+}
+
+template <typename I>
+void LoadRequest<I>::set_initial_read_size(uint64_t read_size) {
+ m_initial_read_size = read_size;
+}
+
+template <typename I>
+void LoadRequest<I>::send() {
+ if (m_image_ctx->io_object_dispatcher->exists(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO)) {
+ finish(-EEXIST);
+ return;
+ }
+
+ // setup interface with libcryptsetup
+ auto r = m_header.init();
+ if (r < 0) {
+ finish(r);
+ return;
+ }
+
+ auto ctx = create_context_callback<
+ LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this);
+ read(m_initial_read_size, ctx);
+}
+
+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);
+ ZTracer::Trace trace;
+ auto req = io::ImageDispatchSpec::create_read(
+ *m_image_ctx, io::IMAGE_DISPATCH_LAYER_API_START, aio_comp,
+ {{m_offset, length}}, io::ReadResult{&m_bl},
+ m_image_ctx->get_data_io_context(), 0, 0, trace);
+ req->send();
+}
+
+template <typename I>
+bool LoadRequest<I>::handle_read(int r) {
+ if (r < 0) {
+ lderr(m_image_ctx->cct) << "error reading from image: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return false;
+ }
+
+ // write header to libcryptsetup interface
+ r = m_header.write(m_bl);
+ if (r < 0) {
+ finish(r);
+ return false;
+ }
+
+ m_offset += m_bl.length();
+ m_bl.clear();
+ return true;
+}
+
+template <typename I>
+void LoadRequest<I>::handle_read_header(int r) {
+ if (!handle_read(r)) {
+ return;
+ }
+
+ // parse header via libcryptsetup
+ r = m_header.load();
+ if (r != 0) {
+ if (m_offset < MAXIMUM_HEADER_SIZE) {
+ // perhaps we did not feed the entire header to libcryptsetup, retry
+ auto ctx = create_context_callback<
+ LoadRequest<I>, &LoadRequest<I>::handle_read_header>(this);
+ read(MAXIMUM_HEADER_SIZE, ctx);
+ return;
+ }
+
+ finish(r);
+ return;
+ }
+
+ auto cipher = m_header.get_cipher();
+ if (strcmp(cipher, "aes") != 0) {
+ lderr(m_image_ctx->cct) << "unsupported cipher: " << cipher << dendl;
+ finish(-ENOTSUP);
+ return;
+ }
+
+ auto cipher_mode = m_header.get_cipher_mode();
+ if (strcmp(cipher_mode, "xts-plain64") != 0) {
+ lderr(m_image_ctx->cct) << "unsupported cipher mode: " << cipher_mode
+ << dendl;
+ finish(-ENOTSUP);
+ return;
+ }
+
+ read_volume_key();
+ return;
+}
+
+template <typename I>
+void LoadRequest<I>::handle_read_keyslots(int r) {
+ if (!handle_read(r)) {
+ return;
+ }
+
+ read_volume_key();
+}
+
+template <typename I>
+void LoadRequest<I>::read_volume_key() {
+ char volume_key[64];
+ size_t volume_key_size = sizeof(volume_key);
+
+ auto r = m_header.read_volume_key(
+ m_passphrase.c_str(), m_passphrase.size(),
+ reinterpret_cast<char*>(volume_key), &volume_key_size);
+ if (r != 0) {
+ auto keyslots_end_offset = m_header.get_data_offset();
+ if (m_offset < keyslots_end_offset) {
+ // perhaps we did not feed the the necessary keyslot, retry
+ auto ctx = create_context_callback<
+ LoadRequest<I>, &LoadRequest<I>::handle_read_keyslots>(this);
+ read(keyslots_end_offset, ctx);
+ return;
+ }
+
+ finish(r);
+ 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);
+}
+
+template <typename I>
+void LoadRequest<I>::finish(int r) {
+ explicit_bzero(&m_passphrase[0], m_passphrase.size());
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
+
+template class librbd::crypto::luks::LoadRequest<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_LUKS_LOAD_REQUEST_H
+#define CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H
+
+#include "librbd/ImageCtx.h"
+#include "librbd/crypto/CryptoInterface.h"
+#include "librbd/crypto/luks/Header.h"
+
+namespace librbd {
+
+class ImageCtx;
+
+namespace crypto {
+namespace luks {
+
+// max header size in LUKS1/2 (excl. keyslots) is 4MB
+const uint64_t MAXIMUM_HEADER_SIZE = 4 * 1024 * 1024;
+// default header size in LUKS2 2 X 16KB + 1 X 256KB keyslot
+const uint64_t DEFAULT_INITIAL_READ_SIZE = 288 * 1024;
+
+template <typename I>
+class LoadRequest {
+public:
+ static LoadRequest* create(
+ I* image_ctx, std::string&& passphrase,
+ ceph::ref_t<CryptoInterface>* result_crypto, Context* on_finish) {
+ return new LoadRequest(image_ctx, std::move(passphrase), result_crypto,
+ on_finish);
+ }
+
+ LoadRequest(I* image_ctx, std::string&& passphrase,
+ ceph::ref_t<CryptoInterface>* result_crypto,
+ Context* on_finish);
+ void send();
+ void finish(int r);
+ void set_initial_read_size(uint64_t read_size);
+
+private:
+ I* m_image_ctx;
+ Context* m_on_finish;
+ ceph::bufferlist m_bl;
+ ceph::ref_t<CryptoInterface>* m_result_crypto;
+ uint64_t m_initial_read_size;
+ Header m_header;
+ uint64_t m_offset;
+ std::string m_passphrase;
+
+ void read(uint64_t end_offset, Context* on_finish);
+ bool handle_read(int r);
+ void handle_read_header(int r);
+ void handle_read_keyslots(int r);
+ void read_volume_key();
+};
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
+
+extern template class librbd::crypto::luks::LoadRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_CRYPTO_LUKS_LOAD_REQUEST_H
ceph_assert(result.second);
}
+ bool exists(DispatchLayer dispatch_layer) override {
+ std::unique_lock locker{m_lock};
+ return m_dispatches.find(dispatch_layer) != m_dispatches.end();
+ }
+
void shut_down_dispatch(DispatchLayer dispatch_layer,
Context* on_finish) override {
auto cct = m_image_ctx->cct;
virtual void shut_down(Context* on_finish) = 0;
virtual void register_dispatch(Dispatch* dispatch) = 0;
+ virtual bool exists(DispatchLayer dispatch_layer) = 0;
virtual void shut_down_dispatch(DispatchLayer dispatch_layer,
Context* on_finish) = 0;
cache/pwl/test_WriteLogMap.cc)
endif(WITH_RBD_RWL)
+if(LINUX AND HAVE_LIBCRYPTSETUP)
+ list(APPEND unittest_librbd_srcs
+ crypto/luks/test_mock_FormatRequest.cc
+ crypto/luks/test_mock_LoadRequest.cc)
+endif()
+
add_executable(unittest_librbd
${unittest_librbd_srcs}
$<TARGET_OBJECTS:common_texttable_obj>)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.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/luks/FormatRequest.cc"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockCryptoLuksFormatRequest : public TestMockFixture {
+ typedef FormatRequest<librbd::MockImageCtx> MockFormatRequest;
+
+ const size_t OBJECT_SIZE = 4 * 1024 * 1024;
+ const char* passphrase_cstr = "password";
+ std::string passphrase = passphrase_cstr;
+
+ MockImageCtx* mock_image_ctx;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ io::AioCompletion* aio_comp;
+ ceph::bufferlist header_bl;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockImageCtx(*ictx);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ TestMockFixture::TearDown();
+ }
+
+ void expect_get_object_size() {
+ EXPECT_CALL(*mock_image_ctx, get_object_size()).WillOnce(Return(
+ OBJECT_SIZE));
+ }
+
+ void expect_crypto_layer_exists_check(bool exists = false) {
+ EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+ }
+
+ void expect_image_write() {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this](io::ImageDispatchSpec* spec) {
+ auto* write = boost::get<io::ImageDispatchSpec::Write>(
+ &spec->request);
+ ASSERT_TRUE(write != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(0, spec->image_extents[0].first);
+ ASSERT_GT(spec->image_extents[0].second, 0);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ aio_comp = spec->aio_comp;
+ header_bl = write->bl;
+ }));
+ }
+
+ void complete_aio(int r) {
+ if (r < 0) {
+ aio_comp->fail(r);
+ } else {
+ aio_comp->set_request_count(1);
+ aio_comp->add_request();
+ aio_comp->complete_request(r);
+ }
+ }
+
+ void verify_header(size_t expected_key_length,
+ uint64_t expected_sector_size) {
+ Header header(mock_image_ctx->cct);
+
+ ASSERT_EQ(0, header.init());
+ ASSERT_EQ(0, header.write(header_bl));
+ ASSERT_EQ(0, header.load());
+
+ ASSERT_EQ(expected_sector_size, header.get_sector_size());
+ ASSERT_EQ(0, header.get_data_offset() % OBJECT_SIZE);
+
+ char volume_key[64];
+ size_t volume_key_size = sizeof(volume_key);
+ ASSERT_EQ(0, header.read_volume_key(
+ passphrase_cstr, strlen(passphrase_cstr),
+ reinterpret_cast<char*>(volume_key), &volume_key_size));
+ }
+};
+
+TEST_F(TestMockCryptoLuksFormatRequest, LUKS1) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, DiskEncryptionFormat::DISK_ENCRYPTION_FORMAT_LUKS1,
+ CipherAlgorithm::CIPHER_ALGORITHM_AES128, std::move(passphrase),
+ on_finish, true);
+ expect_crypto_layer_exists_check();
+ expect_get_object_size();
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(32, 512));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, AES128) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, DiskEncryptionFormat::DISK_ENCRYPTION_FORMAT_LUKS2,
+ CipherAlgorithm::CIPHER_ALGORITHM_AES128, std::move(passphrase),
+ on_finish, true);
+ expect_crypto_layer_exists_check();
+ expect_get_object_size();
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(32, 4096));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, AES256) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, DiskEncryptionFormat::DISK_ENCRYPTION_FORMAT_LUKS2,
+ CipherAlgorithm::CIPHER_ALGORITHM_AES256, std::move(passphrase),
+ on_finish, true);
+ expect_crypto_layer_exists_check();
+ expect_get_object_size();
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(0);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NO_FATAL_FAILURE(verify_header(62, 4096));
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, CryptoAlreadyLoaded) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, DiskEncryptionFormat::DISK_ENCRYPTION_FORMAT_LUKS2,
+ CipherAlgorithm::CIPHER_ALGORITHM_AES256, std::move(passphrase),
+ on_finish, true);
+ expect_crypto_layer_exists_check(true);
+ mock_format_request->send();
+ ASSERT_EQ(-EEXIST, finished_cond.wait());
+}
+
+TEST_F(TestMockCryptoLuksFormatRequest, WriteFail) {
+ auto mock_format_request = MockFormatRequest::create(
+ mock_image_ctx, DiskEncryptionFormat::DISK_ENCRYPTION_FORMAT_LUKS2,
+ CipherAlgorithm::CIPHER_ALGORITHM_AES256, std::move(passphrase),
+ on_finish, true);
+ expect_crypto_layer_exists_check();
+ expect_get_object_size();
+ expect_image_write();
+ mock_format_request->send();
+ ASSERT_EQ(ETIMEDOUT, finished_cond.wait_for(0));
+ complete_aio(-123);
+ ASSERT_EQ(-123, finished_cond.wait());
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.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/luks/LoadRequest.cc"
+
+namespace librbd {
+namespace crypto {
+namespace luks {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+struct TestMockCryptoLuksLoadRequest : public TestMockFixture {
+ typedef LoadRequest<librbd::MockImageCtx> MockLoadRequest;
+
+ const size_t OBJECT_SIZE = 4 * 1024 * 1024;
+ const char* passphrase_cstr = "password";
+ std::string passphrase = passphrase_cstr;
+
+ MockImageCtx* mock_image_ctx;
+ ceph::ref_t<CryptoInterface> crypto;
+ MockLoadRequest* mock_load_request;
+ C_SaferCond finished_cond;
+ Context *on_finish = &finished_cond;
+ Context* image_read_request;
+ ceph::bufferlist header_bl;
+ uint64_t data_offset;
+
+ void SetUp() override {
+ TestMockFixture::SetUp();
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ mock_image_ctx = new MockImageCtx(*ictx);
+ crypto = nullptr;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, std::move(passphrase), &crypto, on_finish);
+ }
+
+ void TearDown() override {
+ delete mock_image_ctx;
+ if (crypto != nullptr) {
+ crypto = nullptr;
+ }
+ TestMockFixture::TearDown();
+ }
+
+ // returns data offset in bytes
+ void generate_header(const char* type, const char* alg, size_t key_size,
+ const char* cipher_mode, uint32_t sector_size) {
+ 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.add_keyslot(passphrase_cstr, strlen(passphrase_cstr)));
+ ASSERT_LE(0, header.read(&header_bl));
+
+ data_offset = header.get_data_offset();
+ }
+
+ void expect_crypto_layer_exists_check(bool exists = false) {
+ EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, exists(
+ io::OBJECT_DISPATCH_LAYER_CRYPTO)).WillOnce(Return(exists));
+ }
+
+ void expect_image_read(uint64_t offset, uint64_t length) {
+ EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, send(_))
+ .WillOnce(Invoke([this, offset,
+ length](io::ImageDispatchSpec* spec) {
+ auto* read = boost::get<io::ImageDispatchSpec::Read>(
+ &spec->request);
+ ASSERT_TRUE(read != nullptr);
+
+ ASSERT_EQ(1, spec->image_extents.size());
+ ASSERT_EQ(offset, spec->image_extents[0].first);
+ ASSERT_EQ(length, spec->image_extents[0].second);
+
+ spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE;
+ auto aio_comp = spec->aio_comp;
+ aio_comp->set_request_count(1);
+ aio_comp->read_result = std::move(read->read_result);
+ aio_comp->read_result.set_image_extents(spec->image_extents);
+ auto ctx = new io::ReadResult::C_ImageReadRequest(
+ aio_comp, 0, spec->image_extents);
+ if (header_bl.length() < offset + length) {
+ header_bl.append_zero(offset + length - header_bl.length());
+ }
+ ctx->bl.substr_of(header_bl, offset, length);
+ image_read_request = ctx;
+ }));
+ }
+};
+
+TEST_F(TestMockCryptoLuksLoadRequest, AES128) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, AES256) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, LUKS1) {
+ generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedAlgorithm) {
+ generate_header(CRYPT_LUKS2, "twofish", 32, "xts-plain64", 4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+ ASSERT_EQ(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedCipherMode) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "cbc-essiv:sha256", 4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-ENOTSUP, finished_cond.wait());
+ ASSERT_EQ(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, HeaderBiggerThanInitialRead) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096);
+ mock_load_request->set_initial_read_size(4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, 4096);
+ mock_load_request->send();
+
+ expect_image_read(4096, MAXIMUM_HEADER_SIZE - 4096);
+ image_read_request->complete(4096); // complete initial read
+
+ image_read_request->complete(MAXIMUM_HEADER_SIZE - 4096);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, KeyslotsBiggerThanInitialRead) {
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096);
+ mock_load_request->set_initial_read_size(16384);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, 16384);
+ mock_load_request->send();
+
+ expect_image_read(16384, data_offset - 16384);
+ image_read_request->complete(16384); // complete initial read
+
+ image_read_request->complete(data_offset - 16384);
+ ASSERT_EQ(0, finished_cond.wait());
+ ASSERT_NE(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, WrongPassphrase) {
+ delete mock_load_request;
+ mock_load_request = MockLoadRequest::create(
+ mock_image_ctx, "wrong", &crypto, on_finish);
+
+ generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096);
+ expect_crypto_layer_exists_check();
+ expect_image_read(0, DEFAULT_INITIAL_READ_SIZE);
+ mock_load_request->send();
+
+ // crypt_volume_key_get will fail, we will retry reading more
+ expect_image_read(DEFAULT_INITIAL_READ_SIZE,
+ data_offset - DEFAULT_INITIAL_READ_SIZE);
+ image_read_request->complete(DEFAULT_INITIAL_READ_SIZE);
+
+ image_read_request->complete(data_offset - DEFAULT_INITIAL_READ_SIZE);
+ ASSERT_EQ(-EPERM, finished_cond.wait());
+ ASSERT_EQ(crypto, nullptr);
+}
+
+TEST_F(TestMockCryptoLuksLoadRequest, CryptoAlreadyLoaded) {
+ generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096);
+ expect_crypto_layer_exists_check(true);
+ mock_load_request->send();
+ ASSERT_EQ(-EEXIST, finished_cond.wait());
+ ASSERT_EQ(crypto, nullptr);
+}
+
+} // namespace luks
+} // namespace crypto
+} // namespace librbd
struct TestMockBlockCrypto : public TestFixture {
MockDataCryptor cryptor;
- BlockCrypto<MockCryptoContext>* bc;
+ ceph::ref_t<BlockCrypto<MockCryptoContext>> bc;
int cryptor_block_size = 2;
int cryptor_iv_size = 16;
int block_size = 4;
+ int data_offset = 0;
ExpectationSet* expectation_set;
void SetUp() override {
cryptor.block_size = cryptor_block_size;
bc = new BlockCrypto<MockCryptoContext>(
reinterpret_cast<CephContext*>(m_ioctx.cct()), &cryptor,
- block_size);
+ block_size, data_offset);
expectation_set = new ExpectationSet();
}
uint64_t get_block_size() const override {
return 4096;
}
+
+ uint64_t get_data_offset() const override {
+ return 4 * 1024 * 1024;
+ }
};
} // namespace crypto
MOCK_METHOD1(shut_down, void(Context*));
MOCK_METHOD1(register_dispatch, void(ImageDispatchInterface*));
+ MOCK_METHOD1(exists, bool(ImageDispatchLayer));
MOCK_METHOD2(shut_down_dispatch, void(ImageDispatchLayer, Context*));
MOCK_METHOD1(invalidate_cache, void(Context *));
MOCK_METHOD1(shut_down, void(Context*));
MOCK_METHOD1(register_dispatch, void(ObjectDispatchInterface*));
+ MOCK_METHOD1(exists, bool(ObjectDispatchLayer));
MOCK_METHOD2(shut_down_dispatch, void(ObjectDispatchLayer, Context*));
MOCK_METHOD2(flush, void(FlushSource, Context*));