cache/rwl/InitRequest.cc
cache/rwl/ShutdownRequest.cc
cache/WriteAroundObjectDispatch.cc
+ crypto/BlockCrypto.cc
+ crypto/CryptoContextPool.cc
crypto/CryptoObjectDispatch.cc
+ crypto/openssl/DataCryptor.cc
deep_copy/ImageCopyRequest.cc
deep_copy/MetadataCopyRequest.cc
deep_copy/ObjectCopyRequest.cc
endif()
target_link_libraries(rbd_internal PRIVATE
osdc rbd_types)
+target_include_directories(rbd_internal PRIVATE ${OPENSSL_INCLUDE_DIR})
if(WITH_RBD_RWL)
target_link_libraries(rbd_internal
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/BlockCrypto.h"
+#include <alloca.h>
+#include "include/byteorder.h"
+#include "include/ceph_assert.h"
+
+namespace librbd {
+namespace crypto {
+
+template <typename T>
+BlockCrypto<T>::BlockCrypto(CephContext* cct, DataCryptor<T>* data_cryptor,
+ uint32_t block_size)
+ : m_cct(cct), m_data_cryptor(data_cryptor), m_block_size(block_size),
+ m_iv_size(data_cryptor->get_iv_size()) {
+ ceph_assert((block_size % data_cryptor->get_block_size()) == 0);
+}
+
+template <typename T>
+int BlockCrypto<T>::crypt(ceph::bufferlist* data, uint64_t image_offset,
+ CipherMode mode) {
+ if (image_offset % m_block_size != 0) {
+ lderr(m_cct) << "image offset: " << image_offset
+ << " not aligned to block size: " << m_block_size << dendl;
+ return -EINVAL;
+ }
+ if (data->length() % m_block_size != 0) {
+ lderr(m_cct) << "data length: " << data->length()
+ << " not aligned to block size: " << m_block_size << dendl;
+ return -EINVAL;
+ }
+
+ unsigned char* iv = (unsigned char*)alloca(m_iv_size);
+ memset(iv, 0, m_iv_size);
+
+ bufferlist src = *data;
+ data->clear();
+
+ auto ctx = m_data_cryptor->get_context(mode);
+ if (ctx == nullptr) {
+ lderr(m_cct) << "unable to get crypt context" << dendl;
+ return -EIO;
+ }
+ auto block_offset = image_offset / m_block_size;
+ auto appender = data->get_contiguous_appender(src.length());
+ unsigned char* out_buf_ptr = nullptr;
+ uint32_t remaining_block_bytes = 0;
+ for (auto buf = src.buffers().begin(); buf != src.buffers().end(); ++buf) {
+ auto in_buf_ptr = reinterpret_cast<const unsigned char*>(buf->c_str());
+ auto remaining_buf_bytes = buf->length();
+ while (remaining_buf_bytes > 0) {
+ if (remaining_block_bytes == 0) {
+ auto block_offset_le = init_le64(block_offset);
+ memcpy(iv, &block_offset_le, sizeof(block_offset_le));
+ auto r = m_data_cryptor->init_context(ctx, iv, m_iv_size);
+ if (r != 0) {
+ lderr(m_cct) << "unable to init cipher's IV" << dendl;
+ return r;
+ }
+
+ out_buf_ptr = reinterpret_cast<unsigned char*>(
+ appender.get_pos_add(m_block_size));
+ remaining_block_bytes = m_block_size;
+ ++block_offset;
+ }
+
+ auto crypto_input_length = std::min(remaining_buf_bytes,
+ remaining_block_bytes);
+ auto crypto_output_length = m_data_cryptor->update_context(
+ ctx, in_buf_ptr, out_buf_ptr, crypto_input_length);
+ if (crypto_output_length < 0) {
+ lderr(m_cct) << "crypt update failed" << dendl;
+ return crypto_output_length;
+ }
+
+ out_buf_ptr += crypto_output_length;
+ in_buf_ptr += crypto_input_length;
+ remaining_buf_bytes -= crypto_input_length;
+ remaining_block_bytes -= crypto_input_length;
+ }
+ }
+
+ m_data_cryptor->return_context(ctx, mode);
+
+ return 0;
+}
+
+template <typename T>
+int BlockCrypto<T>::encrypt(ceph::bufferlist* data, uint64_t image_offset) {
+ return crypt(data, image_offset, CipherMode::CIPHER_MODE_ENC);
+}
+
+template <typename T>
+int BlockCrypto<T>::decrypt(ceph::bufferlist* data, uint64_t image_offset) {
+ return crypt(data, image_offset, CipherMode::CIPHER_MODE_DEC);
+}
+
+} // 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_BLOCK_CRYPTO_H
+#define CEPH_LIBRBD_CRYPTO_BLOCK_CRYPTO_H
+
+#include "include/Context.h"
+#include "librbd/crypto/CryptoInterface.h"
+#include "librbd/crypto/DataCryptor.h"
+
+namespace librbd {
+namespace crypto {
+
+template <typename T>
+class BlockCrypto : public CryptoInterface {
+
+public:
+ BlockCrypto(CephContext* cct, DataCryptor<T>* data_cryptor,
+ uint32_t block_size);
+
+ int encrypt(ceph::bufferlist* data, uint64_t image_offset) override;
+ int decrypt(ceph::bufferlist* data, uint64_t image_offset) override;
+
+ uint32_t get_block_size() const {
+ return m_block_size;
+ }
+
+private:
+ CephContext* m_cct;
+ DataCryptor<T>* m_data_cryptor;
+ uint32_t m_block_size;
+ uint32_t m_iv_size;
+
+ int crypt(ceph::bufferlist* data, uint64_t image_offset, CipherMode mode);
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif //CEPH_LIBRBD_CRYPTO_BLOCK_CRYPTO_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/CryptoContextPool.h"
+
+namespace librbd {
+namespace crypto {
+
+template <typename T>
+CryptoContextPool<T>::CryptoContextPool(DataCryptor<T>* data_cryptor,
+ uint32_t pool_size)
+ : m_data_cryptor(data_cryptor), m_encrypt_contexts(pool_size),
+ m_decrypt_contexts(pool_size) {
+}
+
+template <typename T>
+CryptoContextPool<T>::~CryptoContextPool() {
+ T* ctx;
+ while (m_encrypt_contexts.pop(ctx)) {
+ m_data_cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+ }
+ while (m_decrypt_contexts.pop(ctx)) {
+ m_data_cryptor->return_context(ctx, CipherMode::CIPHER_MODE_DEC);
+ }
+}
+
+template <typename T>
+T* CryptoContextPool<T>::get_context(CipherMode mode) {
+ T* ctx;
+ if (!get_contexts(mode).pop(ctx)) {
+ ctx = m_data_cryptor->get_context(mode);
+ }
+ return ctx;
+}
+
+template <typename T>
+void CryptoContextPool<T>::return_context(T* ctx, CipherMode mode) {
+ if (!get_contexts(mode).push(ctx)) {
+ m_data_cryptor->return_context(ctx, mode);
+ }
+}
+
+} // 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_CRYPTO_CONTEXT_POOL_H
+#define CEPH_LIBRBD_CRYPTO_CRYPTO_CONTEXT_POOL_H
+
+#include "librbd/crypto/DataCryptor.h"
+#include "common/allocator.h"
+#include "include/ceph_assert.h"
+#include <boost/lockfree/queue.hpp>
+
+namespace librbd {
+namespace crypto {
+
+template <typename T>
+class CryptoContextPool : public DataCryptor<T> {
+
+public:
+ CryptoContextPool(DataCryptor<T>* data_cryptor, uint32_t pool_size);
+ ~CryptoContextPool();
+
+ T* get_context(CipherMode mode) override;
+ void return_context(T* ctx, CipherMode mode) override;
+
+ inline uint32_t get_block_size() const override {
+ return m_data_cryptor->get_block_size();
+ }
+ inline uint32_t get_iv_size() const override {
+ return m_data_cryptor->get_iv_size();
+ }
+ 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);
+ }
+ inline int update_context(T* ctx, const unsigned char* in,
+ unsigned char* out,
+ uint32_t len) const override {
+ return m_data_cryptor->update_context(ctx, in, out, len);
+ }
+
+ typedef boost::lockfree::queue<
+ T*,
+ boost::lockfree::allocator<ceph::allocator<void>>> ContextQueue;
+
+private:
+ DataCryptor<T>* m_data_cryptor;
+ ContextQueue m_encrypt_contexts;
+ ContextQueue m_decrypt_contexts;
+
+ inline ContextQueue& get_contexts(CipherMode mode) {
+ switch(mode) {
+ case CIPHER_MODE_ENC:
+ return m_encrypt_contexts;
+ case CIPHER_MODE_DEC:
+ return m_decrypt_contexts;
+ default:
+ ceph_assert(false);
+ }
+ }
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_CRYPTO_CONTEXT_POOL_H
class CryptoInterface : public RefCountedObject {
public:
- virtual void encrypt(ceph::bufferlist&& data,
- uint64_t image_offset) const = 0;
- virtual void decrypt(ceph::bufferlist&& data,
- uint64_t image_offset) const = 0;
+ virtual int encrypt(ceph::bufferlist* data, uint64_t image_offset) = 0;
+ virtual int decrypt(ceph::bufferlist* data, uint64_t image_offset) = 0;
};
void finish(int r) override {
ldout(image_ctx->cct, 20) << "r=" << r << dendl;
if (r > 0) {
- crypto->decrypt(
- std::move(*read_data),
+ auto crypto_ret = crypto->decrypt(
+ read_data,
Striper::get_file_offset(
image_ctx->cct, &image_ctx->layout, object_no,
object_off));
+ if (crypto_ret != 0) {
+ ceph_assert(crypto_ret < 0);
+ r = crypto_ret;
+ }
}
onfinish->complete(r);
}
<< object_off << "~" << data.length() << dendl;
ceph_assert(m_crypto != nullptr);
- m_crypto->encrypt(
- std::move(data),
+ auto r = m_crypto->encrypt(
+ &data,
Striper::get_file_offset(
m_image_ctx->cct, &m_image_ctx->layout, object_no,
object_off));
*dispatch_result = io::DISPATCH_RESULT_CONTINUE;
- on_dispatched->complete(0);
+ on_dispatched->complete(r);
return true;
}
uint64_t image_offset = Striper::get_file_offset(
m_image_ctx->cct, &m_image_ctx->layout, object_no, object_off);
- m_crypto->encrypt(std::move(cmp_data), image_offset);
- m_crypto->encrypt(std::move(write_data), image_offset);
+ auto r = m_crypto->encrypt(&cmp_data, image_offset);
+ if (r == 0) {
+ r = m_crypto->encrypt(&write_data, image_offset);
+ }
*dispatch_result = io::DISPATCH_RESULT_CONTINUE;
- on_dispatched->complete(0);
+ on_dispatched->complete(r);
return true;
}
--- /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_DATA_CRYPTOR_H
+#define CEPH_LIBRBD_CRYPTO_DATA_CRYPTOR_H
+
+#include "include/int_types.h"
+#include "librbd/crypto/Types.h"
+
+namespace librbd {
+namespace crypto {
+
+template <typename T>
+class DataCryptor {
+
+public:
+
+ virtual ~DataCryptor() = default;
+
+ virtual uint32_t get_block_size() const = 0;
+ virtual uint32_t get_iv_size() const = 0;
+ virtual T* get_context(CipherMode mode) = 0;
+ virtual void return_context(T* ctx, CipherMode mode) = 0;
+
+ virtual int init_context(T* ctx, const unsigned char* iv,
+ uint32_t iv_length) const = 0;
+ virtual int update_context(T* ctx, const unsigned char* in,
+ unsigned char* out, uint32_t len) const = 0;
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_DATA_CRYPTOR_H
--- /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_TYPES_H
+#define CEPH_LIBRBD_CRYPTO_TYPES_H
+
+namespace librbd {
+namespace crypto {
+
+enum CipherMode {
+ CIPHER_MODE_ENC,
+ CIPHER_MODE_DEC,
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_DATA_CRYPTOR_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/crypto/openssl/DataCryptor.h"
+#include <openssl/err.h>
+#include <string.h>
+#include "include/ceph_assert.h"
+
+namespace librbd {
+namespace crypto {
+namespace openssl {
+
+int DataCryptor::init(const char* cipher_name, const unsigned char* key,
+ uint16_t key_length) {
+ m_key = nullptr;
+ if (cipher_name == nullptr) {
+ lderr(m_cct) << "missing cipher name" << dendl;
+ return -EINVAL;
+ }
+ if (key == nullptr) {
+ lderr(m_cct) << "missing key" << dendl;
+ return -EINVAL;
+ }
+
+ m_cipher = EVP_get_cipherbyname(cipher_name);
+ if (m_cipher == nullptr) {
+ lderr(m_cct) << "EVP_get_cipherbyname failed. Cipher name: " << cipher_name
+ << dendl;
+ log_errors();
+ return -EINVAL;
+ }
+
+ auto expected_key_length = EVP_CIPHER_key_length(m_cipher);
+ if (expected_key_length != key_length) {
+ lderr(m_cct) << "cipher " << cipher_name << " expects key of "
+ << expected_key_length << " bytes. got: " << key_length
+ << dendl;
+ return -EINVAL;
+ }
+
+ m_key = new unsigned char[key_length];
+ memcpy(m_key, key, key_length);
+ m_iv_size = static_cast<uint32_t>(EVP_CIPHER_iv_length(m_cipher));
+ return 0;
+}
+
+DataCryptor::~DataCryptor() {
+ if (m_key != nullptr) {
+ explicit_bzero(m_key, EVP_CIPHER_key_length(m_cipher));
+ delete m_key;
+ m_key = nullptr;
+ }
+}
+
+uint32_t DataCryptor::get_block_size() const {
+ return EVP_CIPHER_block_size(m_cipher);
+}
+
+uint32_t DataCryptor::get_iv_size() const {
+ return m_iv_size;
+}
+
+EVP_CIPHER_CTX* DataCryptor::get_context(CipherMode mode) {
+ int enc;
+ switch(mode) {
+ case CIPHER_MODE_ENC:
+ enc = 1;
+ break;
+ case CIPHER_MODE_DEC:
+ enc = 0;
+ break;
+ default:
+ lderr(m_cct) << "Invalid CipherMode:" << mode << dendl;
+ return nullptr;
+ }
+
+ auto ctx = EVP_CIPHER_CTX_new();
+ if (ctx == nullptr) {
+ lderr(m_cct) << "EVP_CIPHER_CTX_new failed" << dendl;
+ log_errors();
+ return nullptr;
+ }
+
+ if (1 != EVP_CipherInit_ex(ctx, m_cipher, nullptr, m_key, nullptr, enc)) {
+ lderr(m_cct) << "EVP_CipherInit_ex failed" << dendl;
+ log_errors();
+ return nullptr;
+ }
+
+ return ctx;
+}
+
+void DataCryptor::return_context(EVP_CIPHER_CTX* ctx, CipherMode mode) {
+ if (ctx != nullptr) {
+ EVP_CIPHER_CTX_free(ctx);
+ }
+}
+
+int DataCryptor::init_context(EVP_CIPHER_CTX* ctx, const unsigned char* iv,
+ uint32_t iv_length) const {
+ if (iv_length != m_iv_size) {
+ lderr(m_cct) << "cipher expects IV of " << m_iv_size << " bytes. got: "
+ << iv_length << dendl;
+ return -EINVAL;
+ }
+ if (1 != EVP_CipherInit_ex(ctx, nullptr, nullptr, nullptr, iv, -1)) {
+ lderr(m_cct) << "EVP_CipherInit_ex failed" << dendl;
+ log_errors();
+ return -EIO;
+ }
+ return 0;
+}
+
+int DataCryptor::update_context(EVP_CIPHER_CTX* ctx, const unsigned char* in,
+ unsigned char* out, uint32_t len) const {
+ int out_length;
+ if (1 != EVP_CipherUpdate(ctx, out, &out_length, in, len)) {
+ lderr(m_cct) << "EVP_CipherUpdate failed. len=" << len << dendl;
+ log_errors();
+ return -EIO;
+ }
+ return out_length;
+}
+
+void DataCryptor::log_errors() const {
+ while (true) {
+ auto error = ERR_get_error();
+ if (error == 0) {
+ break;
+ }
+ lderr(m_cct) << "OpenSSL error: " << error << dendl;
+ }
+}
+
+} // namespace openssl
+} // 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_OPENSSL_DATA_CRYPTOR_H
+#define CEPH_LIBRBD_CRYPTO_OPENSSL_DATA_CRYPTOR_H
+
+#include "librbd/crypto/DataCryptor.h"
+#include "include/Context.h"
+#include <openssl/evp.h>
+
+namespace librbd {
+namespace crypto {
+namespace openssl {
+
+class DataCryptor : public crypto::DataCryptor<EVP_CIPHER_CTX> {
+
+public:
+ DataCryptor(CephContext* cct) : m_cct(cct) {};
+ ~DataCryptor();
+
+ int init(const char* cipher_name, const unsigned char* key,
+ uint16_t key_length);
+ uint32_t get_block_size() const override;
+ uint32_t get_iv_size() 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,
+ uint32_t iv_length) const override;
+ int update_context(EVP_CIPHER_CTX* ctx, const unsigned char* in,
+ unsigned char* out, uint32_t len) const override;
+
+private:
+ CephContext* m_cct;
+ unsigned char* m_key = nullptr;
+ const EVP_CIPHER* m_cipher;
+ uint32_t m_iv_size;
+
+ void log_errors() const;
+};
+
+} // namespace openssl
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_CRYPTO_OPENSSL_DATA_CRYPTOR_H
test_mock_Watcher.cc
cache/test_mock_WriteAroundObjectDispatch.cc
cache/test_mock_ParentCacheObjectDispatch.cc
+ crypto/test_mock_BlockCrypto.cc
+ crypto/test_mock_CryptoContextPool.cc
crypto/test_mock_CryptoObjectDispatch.cc
+ crypto/openssl/test_DataCryptor.cc
deep_copy/test_mock_ImageCopyRequest.cc
deep_copy/test_mock_MetadataCopyRequest.cc
deep_copy/test_mock_ObjectCopyRequest.cc
--- /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_fixture.h"
+#include "librbd/crypto/openssl/DataCryptor.h"
+
+namespace librbd {
+namespace crypto {
+namespace openssl {
+
+const char* TEST_CIPHER_NAME = "aes-256-xts";
+const unsigned char TEST_KEY[64] = {1};
+const unsigned char TEST_IV[16] = {2};
+const unsigned char TEST_IV_2[16] = {3};
+const unsigned char TEST_DATA[4096] = {4};
+
+struct TestDataCryptor : public TestFixture {
+ DataCryptor *cryptor;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+ cryptor = new DataCryptor(reinterpret_cast<CephContext*>(m_ioctx.cct()));
+ ASSERT_EQ(0,
+ cryptor->init(TEST_CIPHER_NAME, TEST_KEY, sizeof(TEST_KEY)));
+ }
+
+ void TearDown() override {
+ delete cryptor;
+ Test::TearDown();
+ }
+};
+
+TEST_F(TestDataCryptor, InvalidCipherName) {
+ EXPECT_EQ(-EINVAL, cryptor->init(nullptr, TEST_KEY, sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init("", TEST_KEY, sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init("Invalid", TEST_KEY, sizeof(TEST_KEY)));
+}
+
+TEST_F(TestDataCryptor, InvalidKey) {
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, nullptr, 0));
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, nullptr,
+ sizeof(TEST_KEY)));
+ EXPECT_EQ(-EINVAL, cryptor->init(TEST_CIPHER_NAME, TEST_KEY, 1));
+}
+
+TEST_F(TestDataCryptor, GetContextInvalidMode) {
+ EXPECT_EQ(nullptr, cryptor->get_context(static_cast<CipherMode>(-1)));
+}
+
+TEST_F(TestDataCryptor, ReturnNullContext) {
+ cryptor->return_context(nullptr, static_cast<CipherMode>(-1));
+}
+
+TEST_F(TestDataCryptor, ReturnContextInvalidMode) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_DEC);
+ ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->return_context(ctx, static_cast<CipherMode>(-1));
+}
+
+TEST_F(TestDataCryptor, EncryptDecrypt) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+ cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV));
+
+ unsigned char out[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+ ctx = cryptor->get_context(CipherMode::CIPHER_MODE_DEC);
+ ASSERT_NE(ctx, nullptr);
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV)));
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, out, out, sizeof(TEST_DATA)));
+ ASSERT_EQ(0, memcmp(out, TEST_DATA, sizeof(TEST_DATA)));
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_DEC);
+}
+
+TEST_F(TestDataCryptor, ReuseContext) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV, sizeof(TEST_IV)));
+ unsigned char out[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+
+ ASSERT_EQ(0, cryptor->init_context(ctx, TEST_IV_2, sizeof(TEST_IV_2)));
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx, TEST_DATA, out, sizeof(TEST_DATA)));
+
+ auto ctx2 = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx2, nullptr);
+
+ ASSERT_EQ(0, cryptor->init_context(ctx2, TEST_IV_2, sizeof(TEST_IV_2)));
+ unsigned char out2[sizeof(TEST_DATA)];
+ ASSERT_EQ(sizeof(TEST_DATA),
+ cryptor->update_context(ctx2, TEST_DATA, out2, sizeof(TEST_DATA)));
+
+ ASSERT_EQ(0, memcmp(out, out2, sizeof(TEST_DATA)));
+
+ cryptor->return_context(ctx, CipherMode::CIPHER_MODE_ENC);
+ cryptor->return_context(ctx2, CipherMode::CIPHER_MODE_ENC);
+}
+
+TEST_F(TestDataCryptor, InvalidIVLength) {
+ auto ctx = cryptor->get_context(CipherMode::CIPHER_MODE_ENC);
+ ASSERT_NE(ctx, nullptr);
+
+ ASSERT_EQ(-EINVAL, cryptor->init_context(ctx, TEST_IV, 1));
+}
+
+} // namespace openssl
+} // 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_fixture.h"
+#include "librbd/crypto/BlockCrypto.h"
+#include "test/librbd/mock/crypto/MockDataCryptor.h"
+
+#include "librbd/crypto/BlockCrypto.cc"
+template class librbd::crypto::BlockCrypto<
+ librbd::crypto::MockCryptoContext>;
+
+using ::testing::ExpectationSet;
+using ::testing::internal::ExpectationBase;
+using ::testing::Return;
+using ::testing::_;
+
+namespace librbd {
+namespace crypto {
+
+MATCHER_P(CompareArrayToString, s, "") {
+ return (memcmp(arg, s.c_str(), s.length()) == 0);
+}
+
+struct TestMockBlockCrypto : public TestFixture {
+ MockDataCryptor cryptor;
+ BlockCrypto<MockCryptoContext>* bc;
+ int cryptor_block_size = 2;
+ int cryptor_iv_size = 16;
+ int block_size = 4;
+ ExpectationSet* expectation_set;
+
+ void SetUp() override {
+ TestFixture::SetUp();
+
+ cryptor.block_size = cryptor_block_size;
+ bc = new BlockCrypto<MockCryptoContext>(
+ reinterpret_cast<CephContext*>(m_ioctx.cct()), &cryptor,
+ block_size);
+ expectation_set = new ExpectationSet();
+ }
+
+ void TearDown() override {
+ delete expectation_set;
+ TestFixture::TearDown();
+ }
+
+ void expect_get_context(CipherMode mode) {
+ _set_last_expectation(
+ EXPECT_CALL(cryptor, get_context(mode))
+ .After(*expectation_set).WillOnce(Return(
+ new MockCryptoContext())));
+ }
+
+ void expect_init_context(const std::string& iv) {
+ _set_last_expectation(
+ EXPECT_CALL(cryptor, init_context(_, CompareArrayToString(iv),
+ cryptor_iv_size))
+ .After(*expectation_set));
+ }
+
+ void expect_update_context(const std::string& in_str, int out_ret) {
+ _set_last_expectation(
+ EXPECT_CALL(cryptor, update_context(_,
+ CompareArrayToString(in_str),
+ _, in_str.length()))
+ .After(*expectation_set).WillOnce(Return(out_ret)));
+ }
+
+ void _set_last_expectation(ExpectationBase& expectation) {
+ delete expectation_set;
+ expectation_set = new ExpectationSet(expectation);
+ }
+};
+
+TEST_F(TestMockBlockCrypto, Encrypt) {
+ uint32_t image_offset = 0x1234 * block_size;
+
+ ceph::bufferlist data1;
+ data1.append("123");
+ ceph::bufferlist data2;
+ data2.append("456");
+ ceph::bufferlist data3;
+ data3.append("78");
+
+ // bufferlist buffers: "123", "456", "78"
+ ceph::bufferlist data;
+ data.claim_append(data1);
+ data.claim_append(data2);
+ data.claim_append(data3);
+
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ expect_init_context(std::string("\x34\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16));
+ expect_update_context("123", 0);
+ expect_update_context("4", 4);
+ expect_init_context(std::string("\x35\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16));
+ expect_update_context("56", 0);
+ expect_update_context("78", 4);
+ EXPECT_CALL(cryptor, return_context(_, CipherMode::CIPHER_MODE_ENC));
+
+ ASSERT_EQ(0, bc->encrypt(&data, image_offset));
+
+ ASSERT_EQ(data.length(), 8);
+ ASSERT_TRUE(data.is_aligned(block_size));
+}
+
+TEST_F(TestMockBlockCrypto, UnalignedImageOffset) {
+ ceph::bufferlist data;
+ data.append("1234");
+ ASSERT_EQ(-EINVAL, bc->encrypt(&data, 2));
+}
+
+TEST_F(TestMockBlockCrypto, UnalignedDataLength) {
+ ceph::bufferlist data;
+ data.append("123");
+ ASSERT_EQ(-EINVAL, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockBlockCrypto, GetContextError) {
+ ceph::bufferlist data;
+ data.append("1234");
+ EXPECT_CALL(cryptor, get_context(CipherMode::CIPHER_MODE_ENC)).WillOnce(
+ Return(nullptr));
+ ASSERT_EQ(-EIO, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockBlockCrypto, InitContextError) {
+ ceph::bufferlist data;
+ data.append("1234");
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ EXPECT_CALL(cryptor, init_context(_, _, _)).WillOnce(Return(-123));
+ ASSERT_EQ(-123, bc->encrypt(&data, 0));
+}
+
+TEST_F(TestMockBlockCrypto, UpdateContextError) {
+ ceph::bufferlist data;
+ data.append("1234");
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ EXPECT_CALL(cryptor, init_context(_, _, _));
+ EXPECT_CALL(cryptor, update_context(_, _, _, _)).WillOnce(Return(-123));
+ ASSERT_EQ(-123, bc->encrypt(&data, 0));
+}
+
+} // 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 "gtest/gtest.h"
+#include "librbd/crypto/CryptoContextPool.h"
+#include "test/librbd/mock/crypto/MockDataCryptor.h"
+
+#include "librbd/crypto/CryptoContextPool.cc"
+template class librbd::crypto::CryptoContextPool<
+ librbd::crypto::MockCryptoContext>;
+
+using ::testing::Return;
+
+namespace librbd {
+namespace crypto {
+
+struct TestMockCryptoContextPool : public ::testing::Test {
+ MockDataCryptor cryptor;
+
+ void expect_get_context(CipherMode mode) {
+ EXPECT_CALL(cryptor, get_context(mode)).WillOnce(Return(
+ new MockCryptoContext()));
+ }
+
+ void expect_return_context(MockCryptoContext* ctx, CipherMode mode) {
+ delete ctx;
+ EXPECT_CALL(cryptor, return_context(ctx, mode));
+ }
+};
+
+TEST_F(TestMockCryptoContextPool, Test) {
+ CryptoContextPool<MockCryptoContext> pool(&cryptor, 1);
+
+ expect_get_context(CipherMode::CIPHER_MODE_ENC);
+ auto enc_ctx = pool.get_context(CipherMode::CIPHER_MODE_ENC);
+
+ expect_get_context(CipherMode::CIPHER_MODE_DEC);
+ auto dec_ctx1 = pool.get_context(CipherMode::CIPHER_MODE_DEC);
+ expect_get_context(CipherMode::CIPHER_MODE_DEC);
+ auto dec_ctx2 = pool.get_context(CipherMode::CIPHER_MODE_DEC);
+ pool.return_context(dec_ctx1, CipherMode::CIPHER_MODE_DEC);
+ expect_return_context(dec_ctx2, CipherMode::CIPHER_MODE_DEC);
+ pool.return_context(dec_ctx2, CipherMode::CIPHER_MODE_DEC);
+
+ pool.return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+ ASSERT_EQ(enc_ctx, pool.get_context(CipherMode::CIPHER_MODE_ENC));
+ pool.return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+
+ expect_return_context(enc_ctx, CipherMode::CIPHER_MODE_ENC);
+ expect_return_context(dec_ctx1, CipherMode::CIPHER_MODE_DEC);
+}
+
+} // namespace crypto
+} // namespace librbd
struct MockCryptoInterface : CryptoInterface {
- MOCK_CONST_METHOD2(encrypt, void(ceph::bufferlist&&, uint64_t));
- MOCK_CONST_METHOD2(decrypt, void(ceph::bufferlist&&, uint64_t));
+ MOCK_METHOD2(encrypt, int(ceph::bufferlist*, uint64_t));
+ MOCK_METHOD2(decrypt, int(ceph::bufferlist*, uint64_t));
};
} // namespace crypto
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H
+#define CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H
+
+#include "gmock/gmock.h"
+#include "librbd/crypto/DataCryptor.h"
+
+namespace librbd {
+namespace crypto {
+
+struct MockCryptoContext {};
+
+class MockDataCryptor : public DataCryptor<MockCryptoContext> {
+
+public:
+ uint32_t block_size = 16;
+ uint32_t iv_size = 16;
+
+ uint32_t get_block_size() const override {
+ return block_size;
+ }
+
+ uint32_t get_iv_size() const override {
+ return iv_size;
+ }
+
+ MOCK_METHOD1(get_context, MockCryptoContext*(CipherMode));
+ MOCK_METHOD2(return_context, void(MockCryptoContext*, CipherMode));
+ MOCK_CONST_METHOD3(init_context, int(MockCryptoContext*,
+ const unsigned char*, uint32_t));
+ MOCK_CONST_METHOD4(update_context, int(MockCryptoContext*,
+ const unsigned char*, unsigned char*,
+ uint32_t));
+};
+
+} // namespace crypto
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_DATA_CRYPTOR_H