/*
- * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License
*/
-#include <klay/filesystem.h>
+#include <linux/dm-ioctl.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <fcntl.h>
+
#include <klay/audit/logger.h>
+#include <klay/error.h>
+#include <klay/exception.h>
+#include <klay/filesystem.h>
+
#include "dmcrypt-engine.h"
namespace ode {
-DMCryptEngine::DMCryptEngine(const std::string& src, const std::string& dest) :
+class BlkDevCryptInfo {
+public:
+ void init(const std::string &src, const std::string &crypto_name)
+ {
+ crypto_type_name = crypto_name;
+ fs_size = getBlkdevSize(src);
+ }
+
+ long getFileSystemSize()
+ {
+ return fs_size;
+ }
+
+ std::string getCryptoTypeName() const
+ {
+ return crypto_type_name;
+ }
+
+private:
+ long getBlkdevSize(const std::string &src)
+ {
+ int fd = open(src.c_str(), O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ long fs_size = 0;
+ if ((ioctl(fd, BLKGETSIZE, &fs_size)) == -1)
+ fs_size = 0;
+ close(fd);
+
+ return fs_size;
+ }
+
+private:
+ // TODO(seok85.hong): support fast-encryption
+ long fs_size;
+ std::string crypto_type_name;
+} real_blkdev_crypto_info;
+
+static void convertKeyToHexASCII(const DMCryptEngine::data &key,
+ DMCryptEngine::data &key_ascii)
+{
+ unsigned int i, a;
+ unsigned char nibble;
+
+ for (i = 0, a = 0; i < key.size(); i++, a += 2) {
+ // For each byte, write out two ascii hex digits
+ nibble = (key[i] >> 4) & 0xf;
+ key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+ nibble = key[i] & 0xf;
+ key_ascii[a + 1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+ }
+
+ // Add the null termination
+ key_ascii[a] = '\0';
+}
+
+DMCryptEngine::DMCryptEngine(const std::string &src, const std::string &dest) :
source(src), destination(dest)
{
+ // 1. Get Real Block device(src)'s infomation
+ real_blkdev_crypto_info.init(src, "aes-cbc-essiv:sha256");
}
DMCryptEngine::~DMCryptEngine()
{
}
-void DMCryptEngine::mount(const DMCryptEngine::data& key)
+static void ioctlInit(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
{
- //TODO
+ memset(io, 0, dataSize);
+ io->data_size = dataSize;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags;
+ if (name) {
+ memset(io->name, 0, sizeof(io->name));
+ strncpy(io->name, name, sizeof(io->name) - 1);
+ }
}
-void DMCryptEngine::umount()
+#define DM_CRYPT_BUF_SIZE 4096
+#define DM_LABEL "userdata"
+
+static const std::string createCryptoBlkDev(const std::string &real_blkdev, const std::string &mount_name, const DMCryptEngine::data &key)
{
- //TODO
+ int fd = -1;
+
+ std::string crypto_blkdev;
+ ode::DMCryptEngine::data key_ascii(512);
+
+ /*
+ * dm_buffer |-------------------------------------------------| 4096
+ * dm_io |----------| size of dm_ioctl
+ * dm_ts |--------------| size of dm_Target_spec
+ * crypt_params |---------------------|
+ */
+ char dm_buffer[DM_CRYPT_BUF_SIZE]; // first: for dm_io, dm_ts
+ struct dm_ioctl *dm_io;
+ struct dm_target_spec *dm_ts;
+ unsigned int dm_minor;
+
+ char *crypt_params; // protocol for dm-crypt
+
+ // Open dm control IOCTL
+ if ((fd = open("/dev/mapper/control", O_RDWR)) < 0)
+ throw runtime::Exception("Cannot open device-mapper");
+
+ /*
+ * dm_buffer |-------------------------------------------------| 4096
+ * dm_io |----------| size of dm_ioctl
+ */
+ dm_io = (struct dm_ioctl *)dm_buffer;
+
+ // Clean-up dm_ioctl
+ // Create Device (mount_name)
+ ioctlInit(dm_io, DM_CRYPT_BUF_SIZE, mount_name.c_str(), 0);
+ if (ioctl(fd, DM_DEV_CREATE, dm_io)) {
+ close(fd);
+ throw runtime::Exception("Cannot create dm-crypt device");
+ }
+
+ // Clean-up dm_ioctl
+ // Get the device status, in particular, the mount_name of it's device file
+ ioctlInit(dm_io, DM_CRYPT_BUF_SIZE, mount_name.c_str(), 0);
+ if (ioctl(fd, DM_DEV_STATUS, dm_io)) {
+ close(fd);
+ throw runtime::Exception("Cannot retrieve dm-crypt device status");
+ }
+
+ // Store created device into crypto_blkdev
+ dm_minor = (dm_io->dev & 0xff) | ((dm_io->dev >> 12) & 0xfff00);
+ crypto_blkdev = "/dev/dm-" + std::to_string(dm_minor);
+
+ // Load the mapping table for this device
+ dm_ts = (struct dm_target_spec *)&dm_buffer[sizeof(struct dm_ioctl)];
+
+ /*
+ * dm_buffer |-------------------------------------------------| 4096
+ * dm_io |----------| size of dm_ioctl
+ * dm_ts |--------------| size of dm_Target_spec
+ */
+
+ // Force clean-up whole dm_buffer
+ ioctlInit(dm_io, 4096, mount_name.c_str(), 0);
+ dm_io->target_count = 1;
+ dm_ts->status = 0;
+ dm_ts->sector_start = 0;
+ dm_ts->length = real_blkdev_crypto_info.getFileSystemSize();
+ strcpy(dm_ts->target_type, "crypt");
+
+ // Prepare crypt_params
+ /*
+ * dm_buffer |-------------------------------------------------| 4096
+ * dm_io |----------| size of dm_ioctl
+ * dm_ts |--------------| size of dm_Target_spec
+ * crypt_params |---------------------|
+ */
+
+ crypt_params = dm_buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+
+ ode::convertKeyToHexASCII(key, key_ascii);
+
+ // Store crypt_params
+ sprintf(crypt_params, "%s %s 0 %s 0", real_blkdev_crypto_info.getCryptoTypeName().c_str(), key_ascii.data(), real_blkdev.c_str());
+
+ crypt_params += strlen(crypt_params) + 1;
+ // Align to an 8 byte boundary
+ crypt_params = (char *)(((unsigned long)crypt_params + 7) & ~8);
+ dm_ts->next = crypt_params - dm_buffer;
+
+ // Table load
+ if (ioctl(fd, DM_TABLE_LOAD, dm_io) < 0) {
+ close(fd);
+ throw runtime::Exception("Cannot load dm-crypt mapping table.");
+ }
+
+ // Force clean-up whole dm_buffer
+ // Resume this device to activate it
+ ioctlInit(dm_io, 4096, mount_name.c_str(), 0);
+ if (ioctl(fd, DM_DEV_SUSPEND, dm_io)) {
+ close(fd);
+ throw runtime::Exception("Cannot resume the dm-crypt device");
+ }
+
+ // If fd is <0 from a failed open call,
+ // it's safe to just ignore the close error
+ if (fd >= 0)
+ close(fd);
+
+ return crypto_blkdev;
+}
+
+static void destroyCryptoBlkDev(const std::string &mount_name)
+{
+ int fd;
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+
+ if ((fd = open("/dev/mapper/control", O_RDWR)) < 0)
+ throw runtime::Exception("Cannot open device-mapper");
+
+ io = (struct dm_ioctl *)buffer;
+
+ ioctlInit(io, DM_CRYPT_BUF_SIZE, mount_name.c_str(), 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ close(fd);
+ throw runtime::Exception("Cannot remove dm-crypt device");
+ }
+
+ if (fd >= 0)
+ close(fd);
}
-void DMCryptEngine::encrypt(const DMCryptEngine::data& key)
+#define DMCRYPT_KEY_MIN_LEN_BYTE (32)
+#define DMCRYPT_KEY_MIN_LEN_BIT (DMCRYPT_KEY_MIN_LEN_BYTE*8)
+
+static ode::DMCryptEngine::data sanitizeKey(const ode::DMCryptEngine::data &key)
{
- //TODO
+ if (key.size() < DMCRYPT_KEY_MIN_LEN_BYTE)
+ throw runtime::Exception("Key length isn't enough to be used for dmcrypt");
+ else if (key.size() > DMCRYPT_KEY_MIN_LEN_BYTE)
+ return ode::DMCryptEngine::data(key.begin(), key.begin() + DMCRYPT_KEY_MIN_LEN_BYTE);
+ else
+ return key;
}
+void DMCryptEngine::mount(const DMCryptEngine::data &key)
+{
+ DMCryptEngine::data sanitized_key = sanitizeKey(key);
+ // create crypto type device mapping layer to mount the encrypted partition.
+ const std::string crypto_blkdev = createCryptoBlkDev(source, DM_LABEL, sanitized_key);
+
+ if (::mount(crypto_blkdev.c_str(), destination.c_str(), "ext4", 0, 0) < 0)
+ throw runtime::Exception(runtime::GetSystemErrorMessage());
+}
-void DMCryptEngine::decrypt(const DMCryptEngine::data& key)
+void DMCryptEngine::umount()
{
- //TODO
+ if (::umount(destination.c_str()))
+ throw runtime::Exception(runtime::GetSystemErrorMessage());
+
+ destroyCryptoBlkDev(DM_LABEL);
}
+#define CRYPT_INPLACE_BUFSIZE 4096
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / 512)
+
+static void encryptInPlace(const std::string &dest_blkdev,
+ const std::string &src_blkdev,
+ const long src_blkdev_size)
+{
+ // TODO(seok85.hong): support fast-encryption
+
+ char buff[CRYPT_INPLACE_BUFSIZE];
+
+ off64_t numblocks = src_blkdev_size / CRYPT_SECTORS_PER_BUFSIZE;
+ off64_t remainder = src_blkdev_size % CRYPT_SECTORS_PER_BUFSIZE;
+
+ runtime::File dest(dest_blkdev, O_WRONLY);
+ runtime::File src(src_blkdev, O_RDONLY);
+
+ for (off64_t n = 0; n < (numblocks + remainder); n++) {
+ src.read(buff, CRYPT_INPLACE_BUFSIZE);
+ dest.write(buff, CRYPT_INPLACE_BUFSIZE);
+ }
+}
+
+void DMCryptEngine::encrypt(const DMCryptEngine::data &key)
+{
+ DMCryptEngine::data sanitized_key = sanitizeKey(key);
+
+ // create crypto type device mapping layer to mount the plain partition
+ // should be encrypted here.
+ const std::string crypto_blkdev = createCryptoBlkDev(source, DM_LABEL, sanitized_key);
+
+ // We always do In-place encryption
+ encryptInPlace(crypto_blkdev, source, real_blkdev_crypto_info.getFileSystemSize());
+
+ // remove crypto type device mapper
+ destroyCryptoBlkDev(DM_LABEL);
+}
+
+void DMCryptEngine::decrypt(const DMCryptEngine::data &key)
+{
+ DMCryptEngine::data sanitized_key = sanitizeKey(key);
+
+ // create crypto type device mapping layer to mount the plain partition
+ // should be encrypted here.
+ const std::string crypto_blkdev = createCryptoBlkDev(source, DM_LABEL, sanitized_key);
+
+ // We always do In-place encryption
+ encryptInPlace(source, crypto_blkdev, real_blkdev_crypto_info.getFileSystemSize());
+
+ // remove crypto type device mapper
+ destroyCryptoBlkDev(DM_LABEL);
+}
} // namespace ode
* limitations under the License
*/
+#include <unistd.h>
+#include <linux/loop.h>
+
+#include <cstdio>
+#include <cstdlib>
#include <string>
+#include <fstream>
+#include <vector>
+#include <algorithm>
+
+#include <klay/error.h>
#include <klay/exception.h>
#include <klay/testbench.h>
+#include <klay/process.h>
+
+#include "../server/engine/dmcrypt-engine.cpp"
+
+#define TEST_USERDATA_NAME "userdata"
+#define TEST_USERDATA_PATH "/opt/usr"
+
+namespace {
+
+static std::string test_real_rawfile; // i.e. "/tmp/fileXXXXXX"
+static std::string test_real_blkdev; // i.e. "/dev/loop0"
+static std::string test_real_mntpoint;
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// TEST Initializer
+////////////////////////////////////////////////////////////////////////////////////////
+
+// Test file size: 10485760 + 20480*512
+// Expected Secor size: 20480 (10485760 = 20480 * 512)
+TESTCASE(DMCryptTestInit)
+{
+ test_real_rawfile = tmpnam(nullptr);
+ test_real_mntpoint = test_real_rawfile + "ABCD";
+
+ // prepare rawfile
+ std::ofstream rawfile;
+ rawfile.open(test_real_rawfile);
+ for (int c = 0; c < 10485760; c++) {
+ rawfile << "0";
+ }
+ rawfile.close();
+
+ // prepare loopback device
+ const char *base_lo_str = "/dev/loop";
+ char buff[11] = {0, };
+ FILE *fp = popen("/usr/sbin/losetup -f", "r");
+ if (!fp)
+ TEST_FAIL("Can't Open loop-control");
+
+ if (fgets(buff, 11, fp) == nullptr)
+ TEST_FAIL("Can't Open loop-control");
+
+ pclose(fp);
+
+ if (strncmp(buff, base_lo_str, strlen(base_lo_str)) != 0)
+ TEST_FAIL("Can't get loopnum");
+
+ test_real_blkdev = buff;
+ test_real_blkdev.erase(std::remove(test_real_blkdev.begin(), test_real_blkdev.end(), '\n'), test_real_blkdev.end());
+
+ // set loopback device
+ std::string cmd = "/usr/sbin/losetup ";
+ cmd += test_real_blkdev;
+ cmd += " ";
+ cmd += test_real_rawfile;
+ if (system(cmd.c_str())) {}
+
+ // make filesystem(ext4) and write test file
+ cmd = "/usr/sbin/mkfs.ext4 -j ";
+ cmd += test_real_blkdev; // "/dev/loop0"
+ if (system(cmd.c_str())) {}
+
+ cmd = "/usr/bin/mkdir ";
+ cmd += test_real_mntpoint;
+ if (system(cmd.c_str())) {}
+
+ cmd = "/usr/bin/mount ";
+ cmd += test_real_rawfile;
+ cmd += " ";
+ cmd += test_real_mntpoint;
+ if (system(cmd.c_str())) {}
+
+ cmd = "echo DEF | cat > "; // filename: ABC, file: DEF
+ cmd += test_real_mntpoint;
+ cmd += "/ABC";
+ if (system(cmd.c_str())) {}
+
+ cmd = "/usr/bin/umount ";
+ cmd += test_real_mntpoint;
+ if (system(cmd.c_str())) {}
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// TEST DMCryptEngine STATIC Functions
+////////////////////////////////////////////////////////////////////////////////////////
+
+TESTCASE(DMCryptConvertKeyToHexAsciiSingle16byte)
+{
+ try {
+ const ode::DMCryptEngine::data master_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
+ ode::DMCryptEngine::data master_key_ascii(512);
+ ode::DMCryptEngine::data answer = {0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37,
+ 0x30, 0x38, 0x30, 0x39, 0x30, 0x41, 0x30, 0x42, 0x30, 0x43, 0x30, 0x44, 0x30, 0x45, 0x30, 0x46
+ };
+ ode::convertKeyToHexASCII(master_key, master_key_ascii);
+
+ // check
+ for (unsigned int i = 0; i < master_key.size() * 2; i++) {
+ if (master_key_ascii[i] != answer[i]) {
+ printf("[%2d] %X != %X\n", i, master_key_ascii[i], answer[i]);
+ TEST_FAIL("outcome isn't match with expected answer");
+ }
+ }
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+TESTCASE(DMCryptConvertKeyToHexAsciiDouble16byte)
+{
+ try {
+ const ode::DMCryptEngine::data master_key = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F};
+ ode::DMCryptEngine::data master_key_ascii(512);
+ ode::DMCryptEngine::data answer = {0x46, 0x30, 0x45, 0x31, 0x44, 0x32, 0x43, 0x33, 0x42, 0x34, 0x41, 0x35, 0x39, 0x36, 0x38, 0x37,
+ 0x37, 0x38, 0x36, 0x39, 0x35, 0x41, 0x34, 0x42, 0x33, 0x43, 0x32, 0x44, 0x31, 0x45, 0x30, 0x46
+ };
+ ode::convertKeyToHexASCII(master_key, master_key_ascii);
+
+ // check
+ for (unsigned int i = 0; i < master_key.size() * 2; i++)
+ if (master_key_ascii[i] != answer[i])
+ TEST_FAIL("outcome isn't match with expected answer");
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+TESTCASE(DMCryptBlkDevCryptInfoWithDummyFile)
+{
+ try {
+ const std::string test_crypto_type_name = "test-crypto-type-name";
+
+ ode::real_blkdev_crypto_info.init(test_real_blkdev, test_crypto_type_name);
+ if (test_crypto_type_name.compare(ode::real_blkdev_crypto_info.getCryptoTypeName()))
+ TEST_FAIL("CryptoTypeName doesn't match");
-#include "../server/engine/dmcrypt-engine.h"
+ // we espect the sector size (20480) with our temporary created file
+ long test_fs_size = 20480;
+ if (test_fs_size != ode::real_blkdev_crypto_info.getFileSystemSize())
+ TEST_FAIL("FileSystemSize doesn't match");
+ //
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+TESTCASE(DMCryptCreateRemoveCryptoBlkDevWithTempkey)
+{
+ try {
+ ode::real_blkdev_crypto_info.init(test_real_blkdev, "aes-cbc-essiv:sha256");
+ const std::string keystring = "01020304050607080910111213141516";
+ const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
+ const std::string crypto_blkdev = ode::createCryptoBlkDev(test_real_blkdev, "testdata", key32bit);
+ // check crypto device status using cryptsetup
+ {
+ FILE *fp = popen("cryptsetup status testdata", "r");
+ if (!fp)
+ TEST_FAIL("Can't get cryptsetup status");
+
+ std::vector<std::string> answer = {
+ "/dev/mapper/testdata is active.",
+ " type: n/a",
+ " cipher: aes-cbc-essiv:sha256",
+ " keysize: 256 bits",
+ std::string(" device: " + test_real_blkdev),
+ std::string(" loop: " + test_real_rawfile),
+ " offset: 0 sectors",
+ " size: 20480 sectors",
+ " mode: read/write",
+ };
+
+ char buff[128] = {0, };
+ for (auto &a : answer) {
+ if (fgets(buff, 128, fp) == nullptr) {
+ pclose(fp);
+ TEST_FAIL("Can't read cryptsetup status from buffer");
+ }
-#define TEST_PATH "/opt/usr"
+ std::string ret = buff;
+ ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
+
+ if (ret.compare(a)) {
+ pclose(fp);
+ std::cout << "expected: " << a << "\n";
+ std::cout << "real : " << ret << "\n";
+ TEST_FAIL("Unexpected result from cryptsetup status");
+ }
+ }
+ pclose(fp);
+ }
+
+ // remove crypto device
+ ode::destroyCryptoBlkDev("testdata");
+ // check crypto device status using cryptsetup
+ {
+ FILE *fp = popen("cryptsetup status testdata", "r");
+ if (!fp)
+ TEST_FAIL("Can't get cryptsetup status");
+
+ std::vector<std::string> answer = {
+ "/dev/mapper/testdata is inactive.",
+ };
+
+ char buff[128] = {0, };
+ for (auto &a : answer) {
+ if (fgets(buff, 128, fp) == nullptr) {
+ pclose(fp);
+ TEST_FAIL("Can't read cryptsetup status from buffer");
+ }
+
+ std::string ret = buff;
+ ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
+
+ if (ret.compare(a)) {
+ pclose(fp);
+ std::cout << "expected: " << a << "\n";
+ std::cout << "real : " << ret << "\n";
+ TEST_FAIL("Unexpected result from cryptsetup status");
+ }
+ }
+ pclose(fp);
+ }
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+TESTCASE(DMCryptSanitizeKey)
+{
+ try {
+ // case1. small size than minimum byte (32) for dmcrypt
+ // -- expected: cause exception
+ {
+ const std::string keystring = "SMALLER_THAN_32BYTES______";
+ const ode::DMCryptEngine::data keySmall32bit(keystring.begin(), keystring.end());
+ try {
+ ode::sanitizeKey(keySmall32bit);
+ //
+ TEST_FAIL("small key should be cause exception");
+ } catch (runtime::Exception &e) {
+ // expected result: it's good
+ }
+ }
+ // case2. same size
+ // -- expected: returned key length should be 32 byte.
+ // -- expected: returned key data is same with 32 elements from first of big key
+ {
+ const std::string keystring = "01020304050607080910111213141516";
+ const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
+ const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(key32bit);
+
+ // check size
+ TEST_EXPECT(sanitized_key.size(), key32bit.size());
+
+ // check data
+ for (unsigned i = 0; i < sanitized_key.size(); i++) {
+ TEST_EXPECT(sanitized_key[i], key32bit[i]);
+ }
+ }
+ // case3. big size than minimum byte (32) for dmcrypt
+ // -- expected: returned key length should be 32 byte.
+ // -- expected: returned key data is same with 32 elements from first of big key
+ {
+ const std::string keystring = "01020304050607080910111213141516_MAKES_BIG";
+ const ode::DMCryptEngine::data keyBig(keystring.begin(), keystring.end());
+ const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(keyBig);
+
+ // check size
+ TEST_EXPECT(sanitized_key.size(), (unsigned)DMCRYPT_KEY_MIN_LEN_BYTE);
+
+ // check data
+ for (unsigned i = 0; i < sanitized_key.size(); i++) {
+ TEST_EXPECT(sanitized_key[i], keyBig[i]);
+ }
+ }
+ //
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// TEST DMCryptEngine
+////////////////////////////////////////////////////////////////////////////////////////
TESTCASE(DMCryptGetPathTest)
{
try {
- ode::DMCryptEngine engine("/dev/mmcblkp0", "/opt/usr");
- if (engine.getDestination() != "/dev/mmcblkp0") {
+ ode::DMCryptEngine engine("/dev/mmcblk0p1", TEST_USERDATA_PATH);
+ if (engine.getSource() != "/dev/mmcblk0p1") {
throw runtime::Exception("Source doen't match");
}
if (engine.getDestination() != "/opt/usr") {
throw runtime::Exception("Destination doen't match");
}
- } catch (runtime::Exception& e) {
+ } catch (runtime::Exception &e) {
TEST_FAIL(e.what());
}
}
+
+TESTCASE(DMCryptEncryptAndDecrypt)
+{
+ try {
+ ode::real_blkdev_crypto_info.init(test_real_blkdev, "aes-cbc-essiv:sha256");
+ const std::string keystring = "01020304050607080910111213141516";
+ const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
+
+ ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint);
+ engine.encrypt(key32bit);
+
+ // check the encryption result of test_real_blkdev(/dev/loop0)
+ // at this time, if we mount /dev/loop0 forcely, we can't mount them...
+ {
+ int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
+ if ((ret != -1) || (runtime::Error::lastErrorCode() != EINVAL)) {
+ // we expected ret value is EINVAL (mount failure in man page of mount)
+ std::cout << "expected: EINVAL" << "\n";
+ std::cout << "real : " << runtime::GetSystemErrorMessage() << "\n";
+ TEST_FAIL("Unexpected result from cryptsetup status");
+ }
+ }
+ // decyprt
+ engine.decrypt(key32bit);
+
+ // check the decryption result of test_Real_Blkdev(/dev/loop0)
+ // at this time, if we mount /dev/loop0 forcely, we can mount them,
+ // and, we can read test file successfully (file name: ABC, body:DEF)
+ {
+ int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
+ if (ret == -1) {
+ TEST_FAIL("Can't mount decyprted target");
+ }
+ runtime::File isgood(test_real_mntpoint + "/ABC");
+ if (isgood.exists() == false) {
+ TEST_FAIL("Can't read test file (file name: ABC, body:DEF)");
+ }
+ ::umount(test_real_mntpoint.c_str());
+ }
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+TESTCASE(DMCryptEncryptMountUnmountDecrypt)
+{
+ try {
+ ode::real_blkdev_crypto_info.init(test_real_blkdev, "aes-cbc-essiv:sha256");
+ const std::string keystring = "01020304050607080910111213141516";
+ const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
+
+ ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint);
+ engine.encrypt(key32bit);
+ engine.mount(key32bit);
+ {
+ // we should find test file (file name: ABC, body: DEF) in mount-point
+ std::string cmd = "cat " + test_real_mntpoint + "/ABC";
+ FILE *fp = popen(cmd.c_str(), "r");
+ if (!fp)
+ TEST_FAIL("Can't get test file body");
+
+ std::vector<std::string> answer = {
+ "DEF",
+ };
+
+ char buff[128] = {0, };
+ for (auto &a : answer) {
+ if (fgets(buff, 128, fp) == nullptr) {
+ pclose(fp);
+ TEST_FAIL("Can't read test file body from buffer");
+ }
+
+ std::string ret = buff;
+ ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
+
+ if (ret.compare(a)) {
+ pclose(fp);
+ std::cout << "expected: " << a << "\n";
+ std::cout << "real : " << ret << "\n";
+ TEST_FAIL("Unexpected result from test file body");
+ }
+ }
+ pclose(fp);
+ }
+ engine.umount();
+ engine.decrypt(key32bit);
+ //
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+// TODO(seok85.hong): is numblocks + remainder less than fast-encryption enabled count?
+
+// When we try to dcrypt using wrong key, our encrypted partition will be broken.
+// so, we should do this test at last test case
+TESTCASE(DMCryptEncryptButDecryptWithWrongKey)
+{
+ try {
+ ode::real_blkdev_crypto_info.init(test_real_blkdev, "aes-cbc-essiv:sha256");
+ const std::string keystring = "01020304050607080910111213141516";
+ const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
+ const std::string wrongkeystring = "SIZE_IS_SAME_BUT_WRONG_DECKEY___";
+ const ode::DMCryptEngine::data wrongkey32bit(wrongkeystring.begin(), wrongkeystring.end());
+
+ ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint);
+ engine.encrypt(key32bit);
+
+ // check the encryption result of test_real_blkdev(/dev/loop0)
+ // at this time, if we mount /dev/loop0 forcely, we can't mount them...
+ {
+ int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
+ if ((ret != -1) || (runtime::Error::lastErrorCode() != EINVAL)) {
+ // we expected ret value is EINVAL (mount failure in man page of mount)
+ std::cout << "expected: EINVAL" << "\n";
+ std::cout << "real : " << runtime::GetSystemErrorMessage() << "\n";
+ TEST_FAIL("Unexpected result from cryptsetup status");
+ }
+ }
+ // decrypt with WRONG KEY
+ engine.decrypt(wrongkey32bit);
+
+ // check the decryption result of test_Real_Blkdev(/dev/loop0)
+ // at this time, if we mount /dev/loop0 forcely, we can't mount them...
+ {
+ int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
+ if ((ret != -1) || (runtime::Error::lastErrorCode() != EINVAL)) {
+ // we expected ret value is EINVAL (mount failure in man page of mount)
+ std::cout << "expected: EINVAL" << "\n";
+ std::cout << "real : " << runtime::GetSystemErrorMessage() << "\n";
+ TEST_FAIL("Unexpected result from cryptsetup status");
+ }
+ }
+ } catch (runtime::Exception &e) {
+ TEST_FAIL(e.what());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// TEST Deinitializer
+////////////////////////////////////////////////////////////////////////////////////////
+
+TESTCASE(DMCryptTestDeInit)
+{
+ std::string cmd = "/usr/sbin/losetup -d ";
+ cmd += test_real_blkdev;
+ if (system(cmd.c_str())) {}
+
+ if (::unlink(test_real_rawfile.c_str()) == -1)
+ ::unlink(test_real_rawfile.c_str());
+
+ cmd = "/usr/bin/rm -rf ";
+ cmd += test_real_mntpoint;
+ if (system(cmd.c_str())) {}
+}