From 90f7b84817c5358221e2282d73a6d41b21f04814 Mon Sep 17 00:00:00 2001 From: Seok Hong Date: Tue, 1 Nov 2016 14:56:37 +0900 Subject: [PATCH] Add DMCryptEngine TODO(seok85.hong) - support the fast-encryption for dmcrypt engine Change-Id: I5e634a1d2ae67c2b4e75a949ba8ad3ce3a10c77d Signed-off-by: Seok Hong --- server/engine/dmcrypt-engine.cpp | 300 +++++++++++++++++++- server/engine/dmcrypt-engine.h | 22 +- tests/CMakeLists.txt | 1 - tests/dmcrypt-engine.cpp | 457 ++++++++++++++++++++++++++++++- 4 files changed, 752 insertions(+), 28 deletions(-) diff --git a/server/engine/dmcrypt-engine.cpp b/server/engine/dmcrypt-engine.cpp index 1c1ffc6..fe1a751 100644 --- a/server/engine/dmcrypt-engine.cpp +++ b/server/engine/dmcrypt-engine.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -13,44 +13,322 @@ * See the License for the specific language governing permissions and * limitations under the License */ -#include +#include +#include +#include +#include + #include +#include +#include +#include + #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 diff --git a/server/engine/dmcrypt-engine.h b/server/engine/dmcrypt-engine.h index f148de0..6348ff9 100644 --- a/server/engine/dmcrypt-engine.h +++ b/server/engine/dmcrypt-engine.h @@ -1,5 +1,5 @@ /* - * 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. @@ -24,31 +24,31 @@ namespace ode { class DMCryptEngine final { public: - DMCryptEngine(const std::string& src, const std::string& dest); - DMCryptEngine(const DMCryptEngine&) = delete; - DMCryptEngine(DMCryptEngine&&) = delete; + DMCryptEngine(const std::string &src, const std::string &dest); + DMCryptEngine(const DMCryptEngine &) = delete; + DMCryptEngine(DMCryptEngine &&) = delete; ~DMCryptEngine(); - DMCryptEngine& operator=(const DMCryptEngine&) = delete; - DMCryptEngine& operator=(DMCryptEngine&&) = delete; + DMCryptEngine &operator=(const DMCryptEngine &) = delete; + DMCryptEngine &operator=(DMCryptEngine &&) = delete; - const std::string& getSource() + const std::string &getSource() { return source; } - const std::string& getDestination() + const std::string &getDestination() { return destination; } typedef std::vector data; - void mount(const data& key); + void mount(const data &key); void umount(); - void encrypt(const data& key); - void decrypt(const data& key); + void encrypt(const data &key); + void decrypt(const data &key); private: std::string source, destination; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5c85168..958f61d 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,7 +20,6 @@ SET(TEST_SRC main.cpp dmcrypt-engine.cpp ecryptfs-engine.cpp ../server/engine/ext4-engine.cpp - ../server/engine/dmcrypt-engine.cpp ../server/engine/ecryptfs-engine.cpp ) diff --git a/tests/dmcrypt-engine.cpp b/tests/dmcrypt-engine.cpp index 86f542b..aaffbc0 100644 --- a/tests/dmcrypt-engine.cpp +++ b/tests/dmcrypt-engine.cpp @@ -14,25 +14,472 @@ * limitations under the License */ +#include +#include + +#include +#include #include +#include +#include +#include + +#include #include #include +#include + +#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 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 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 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())) {} +} -- 2.34.1