Add crypto Erase to eStorageD.
The goal is to erase the keys that are used to decrypt the drive. After
the keys are erased it will not be possible to decrypt the drive, even
if the password can be recalled. The data is forever inaccessible.
Testing:
$ systemctl stop emmc.service
$ /eStoraged -b /dev/mmcblk0 &
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume FormatLuks ays 3 1 2 3 xyz.openbmc_project.Inventory.Item.Volume.FilesystemType.ext4
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Lock
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Erase s xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.CryptoErase
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Unlock ay 3 1 2 3
Call failed: The operation failed internally.
Signed-off-by: John Edward Broadbent <jebr@google.com>
Change-Id: I3221e82a92c1b555e2379b19c9e1d5b6e4b02f9b
diff --git a/src/test/erase/crypto_test.cpp b/src/test/erase/crypto_test.cpp
new file mode 100644
index 0000000..917b23b
--- /dev/null
+++ b/src/test/erase/crypto_test.cpp
@@ -0,0 +1,146 @@
+
+#include "cryptErase.hpp"
+#include "cryptsetupInterface.hpp"
+#include "estoraged.hpp"
+#include "estoraged_test.hpp"
+
+#include <unistd.h>
+
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <exception>
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace estoraged_test
+{
+
+using estoraged::CryptErase;
+using estoraged::Cryptsetup;
+using estoraged::CryptsetupInterface;
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound;
+using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class cryptoEraseTest : public testing::Test
+{
+ public:
+ static constexpr char testFileName[] = "testfile";
+ std::ofstream testFile;
+
+ void SetUp() override
+ {
+ /* Create an empty file that we'll pretend is a 'storage device'. */
+ testFile.open(testFileName,
+ std::ios::out | std::ios::binary | std::ios::trunc);
+ testFile.close();
+ if (testFile.fail())
+ {
+ throw std::runtime_error("Failed to open test file");
+ }
+ testFile.close();
+ }
+};
+
+TEST_F(cryptoEraseTest, EraseCryptPass)
+{
+ std::unique_ptr<MockCryptsetupInterface> mockCryptIface =
+ std::make_unique<MockCryptsetupInterface>();
+
+ EXPECT_CALL(*mockCryptIface, cryptLoad(_, StrEq(CRYPT_LUKS2), nullptr))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotMax(StrEq(CRYPT_LUKS2)))
+ .WillOnce(Return(1));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotStatus(_, 0))
+ .WillOnce(Return(CRYPT_SLOT_ACTIVE_LAST));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeyslotDestroy(_, 0)).Times(1);
+
+ CryptErase myCryptErase =
+ CryptErase(testFileName, std::move(mockCryptIface));
+ EXPECT_NO_THROW(myCryptErase.doErase());
+}
+
+TEST_F(cryptoEraseTest, EraseCrypMaxSlotFails)
+{
+ std::unique_ptr<MockCryptsetupInterface> mockCryptIface =
+ std::make_unique<MockCryptsetupInterface>();
+
+ EXPECT_CALL(*mockCryptIface, cryptLoad(_, StrEq(CRYPT_LUKS2), nullptr))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotMax(StrEq(CRYPT_LUKS2)))
+ .WillOnce(Return(-1));
+
+ CryptErase myCryptErase =
+ CryptErase(testFileName, std::move(mockCryptIface));
+ EXPECT_THROW(myCryptErase.doErase(), ResourceNotFound);
+}
+
+TEST_F(cryptoEraseTest, EraseCrypMaxSlotZero)
+{
+ std::unique_ptr<MockCryptsetupInterface> mockCryptIface =
+ std::make_unique<MockCryptsetupInterface>();
+
+ EXPECT_CALL(*mockCryptIface, cryptLoad(_, StrEq(CRYPT_LUKS2), nullptr))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotMax(StrEq(CRYPT_LUKS2)))
+ .WillOnce(Return(0));
+
+ CryptErase myCryptErase =
+ CryptErase(testFileName, std::move(mockCryptIface));
+ EXPECT_THROW(myCryptErase.doErase(), ResourceNotFound);
+}
+
+TEST_F(cryptoEraseTest, EraseCrypOnlyInvalid)
+{
+ std::unique_ptr<MockCryptsetupInterface> mockCryptIface =
+ std::make_unique<MockCryptsetupInterface>();
+
+ EXPECT_CALL(*mockCryptIface, cryptLoad(_, StrEq(CRYPT_LUKS2), nullptr))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotMax(StrEq(CRYPT_LUKS2)))
+ .WillOnce(Return(32));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotStatus(_, _))
+ .WillRepeatedly(Return(CRYPT_SLOT_INVALID));
+
+ CryptErase myCryptErase =
+ CryptErase(testFileName, std::move(mockCryptIface));
+ EXPECT_NO_THROW(myCryptErase.doErase());
+}
+
+TEST_F(cryptoEraseTest, EraseCrypDestoryFails)
+{
+ std::unique_ptr<MockCryptsetupInterface> mockCryptIface =
+ std::make_unique<MockCryptsetupInterface>();
+
+ EXPECT_CALL(*mockCryptIface, cryptLoad(_, StrEq(CRYPT_LUKS2), nullptr))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotMax(StrEq(CRYPT_LUKS2)))
+ .WillOnce(Return(1));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeySlotStatus(_, 0))
+ .WillOnce(Return(CRYPT_SLOT_ACTIVE));
+
+ EXPECT_CALL(*mockCryptIface, cryptKeyslotDestroy(_, 0))
+ .WillOnce(Return(-1));
+
+ CryptErase myCryptErase =
+ CryptErase(testFileName, std::move(mockCryptIface));
+ EXPECT_THROW(myCryptErase.doErase(), InternalFailure);
+}
+
+} // namespace estoraged_test