diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/test/librbd/crypto/luks/test_mock_LoadRequest.cc | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/librbd/crypto/luks/test_mock_LoadRequest.cc')
-rw-r--r-- | src/test/librbd/crypto/luks/test_mock_LoadRequest.cc | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc new file mode 100644 index 000000000..5fb566a07 --- /dev/null +++ b/src/test/librbd/crypto/luks/test_mock_LoadRequest.cc @@ -0,0 +1,333 @@ +// -*- 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; + std::unique_ptr<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; + std::string detected_format_name; + + void SetUp() override { + TestMockFixture::SetUp(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + mock_image_ctx = new MockImageCtx(*ictx); + mock_load_request = MockLoadRequest::create( + mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, std::move(passphrase), + &crypto, &detected_format_name, on_finish); + detected_format_name = ""; + } + + void TearDown() override { + delete mock_image_ctx; + 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, + bool magic_switched) { + Header header(mock_image_ctx->cct); + + ASSERT_EQ(0, header.init()); + 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_LT(0, header.read(&header_bl)); + if (magic_switched) { + ASSERT_EQ(0, Magic::replace_magic(mock_image_ctx->cct, header_bl)); + } + + data_offset = header.get_data_offset(); + } + + 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; + })); + } + + void expect_get_image_size(uint64_t size) { + EXPECT_CALL(*mock_image_ctx, get_image_size(_)).WillOnce( + Return(size)); + } + + void expect_get_stripe_period(uint64_t period) { + EXPECT_CALL(*mock_image_ctx, get_stripe_period()).WillOnce( + Return(period)); + } +}; + +TEST_F(TestMockCryptoLuksLoadRequest, AES128) { + generate_header(CRYPT_LUKS2, "aes", 32, "xts-plain64", 4096, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, AES256) { + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, LUKS1) { + delete mock_load_request; + mock_load_request = MockLoadRequest::create( + mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, {passphrase_cstr}, &crypto, + &detected_format_name, on_finish); + generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS1", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, LUKS1ViaLUKS) { + delete mock_load_request; + mock_load_request = MockLoadRequest::create( + mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS, {passphrase_cstr}, &crypto, + &detected_format_name, on_finish); + generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS1", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, UnknownFormat) { + header_bl.append_zero(MAXIMUM_HEADER_SIZE); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + mock_load_request->send(); + + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + + ASSERT_EQ(-EINVAL, finished_cond.wait()); + ASSERT_EQ(crypto.get(), nullptr); + ASSERT_EQ("<unknown>", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, WrongFormat) { + generate_header(CRYPT_LUKS1, "aes", 32, "xts-plain64", 512, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + mock_load_request->send(); + + expect_image_read(DEFAULT_INITIAL_READ_SIZE, + MAXIMUM_HEADER_SIZE - DEFAULT_INITIAL_READ_SIZE); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + image_read_request->complete(MAXIMUM_HEADER_SIZE - DEFAULT_INITIAL_READ_SIZE); + + ASSERT_EQ(-EINVAL, finished_cond.wait()); + ASSERT_EQ(crypto.get(), nullptr); + ASSERT_EQ("LUKS", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedAlgorithm) { + generate_header(CRYPT_LUKS2, "twofish", 32, "xts-plain64", 4096, false); + 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.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, UnsupportedCipherMode) { + generate_header(CRYPT_LUKS2, "aes", 32, "cbc-essiv:sha256", 4096, false); + 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.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, BadSize) { + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE - 1); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(-EINVAL, finished_cond.wait()); + ASSERT_EQ(crypto.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, BadStripePattern) { + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE * 3); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(-EINVAL, finished_cond.wait()); + ASSERT_EQ(crypto.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, HeaderBiggerThanInitialRead) { + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + mock_load_request->set_initial_read_size(4096); + expect_image_read(0, 4096); + mock_load_request->send(); + + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + 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.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, LUKS1FormattedClone) { + mock_image_ctx->parent = mock_image_ctx; + delete mock_load_request; + mock_load_request = MockLoadRequest::create( + mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS1, {passphrase_cstr}, &crypto, + &detected_format_name, on_finish); + generate_header(CRYPT_LUKS1, "aes", 64, "xts-plain64", 512, true); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS1", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, LUKS2FormattedClone) { + mock_image_ctx->parent = mock_image_ctx; + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, true); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + mock_load_request->send(); + image_read_request->complete(DEFAULT_INITIAL_READ_SIZE); + ASSERT_EQ(0, finished_cond.wait()); + ASSERT_NE(crypto.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, KeyslotsBiggerThanInitialRead) { + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + mock_load_request->set_initial_read_size(16384); + expect_image_read(0, 16384); + mock_load_request->send(); + + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_SIZE); + 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.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +TEST_F(TestMockCryptoLuksLoadRequest, WrongPassphrase) { + delete mock_load_request; + mock_load_request = MockLoadRequest::create( + mock_image_ctx, RBD_ENCRYPTION_FORMAT_LUKS2, "wrong", &crypto, + &detected_format_name, on_finish); + + generate_header(CRYPT_LUKS2, "aes", 64, "xts-plain64", 4096, false); + expect_image_read(0, DEFAULT_INITIAL_READ_SIZE); + expect_get_image_size(OBJECT_SIZE << 5); + expect_get_stripe_period(OBJECT_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.get(), nullptr); + ASSERT_EQ("LUKS2", detected_format_name); +} + +} // namespace luks +} // namespace crypto +} // namespace librbd |