Add DMCryptEngine 96/96096/11
authorSeok Hong <seok85.hong@samsung.com>
Tue, 1 Nov 2016 05:56:37 +0000 (14:56 +0900)
committerSungbae Yoo <sungbae.yoo@samsung.com>
Fri, 11 Nov 2016 09:51:10 +0000 (18:51 +0900)
TODO(seok85.hong)
 - support the fast-encryption for dmcrypt engine

Change-Id: I5e634a1d2ae67c2b4e75a949ba8ad3ce3a10c77d
Signed-off-by: Seok Hong <seok85.hong@samsung.com>
server/engine/dmcrypt-engine.cpp
server/engine/dmcrypt-engine.h
tests/CMakeLists.txt
tests/dmcrypt-engine.cpp

index 1c1ffc6..fe1a751 100644 (file)
@@ -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.
  *  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
index f148de0..6348ff9 100644 (file)
@@ -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::stringgetSource()
+       const std::string &getSource()
        {
                return source;
        }
 
-       const std::stringgetDestination()
+       const std::string &getDestination()
        {
                return destination;
        }
 
        typedef std::vector<unsigned char> data;
 
-       void mount(const datakey);
+       void mount(const data &key);
        void umount();
 
-       void encrypt(const datakey);
-       void decrypt(const datakey);
+       void encrypt(const data &key);
+       void decrypt(const data &key);
 
 private:
        std::string source, destination;
index 5c85168..958f61d 100755 (executable)
@@ -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
 )
 
index 86f542b..aaffbc0 100644 (file)
  *  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::Exceptione) {
+       } 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())) {}
+}