2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
18 #include <linux/loop.h>
27 #include <klay/error.h>
28 #include <klay/exception.h>
29 #include <klay/testbench.h>
30 #include <klay/process.h>
32 #include "../server/engine/dmcrypt-engine.cpp"
34 #define TEST_USERDATA_NAME "userdata"
35 #define TEST_USERDATA_PATH "/opt/usr"
39 static std::string test_real_rawfile; // i.e. "/tmp/fileXXXXXX"
40 static std::string test_real_blkdev; // i.e. "/dev/loop0"
41 static std::string test_real_mntpoint;
43 // ode::DMCryptEngine gvTest("/dev/mmcblk0p25", "/opt/usr");
45 ode::ProgressBar progress([](int v) {});
49 ////////////////////////////////////////////////////////////////////////////////////////
51 ////////////////////////////////////////////////////////////////////////////////////////
53 // Test file size: 10485760 + 20480*512
54 // Expected Secor size: 20480 (10485760 = 20480 * 512)
55 TESTCASE(DMCryptTestInit)
57 test_real_rawfile = tmpnam(nullptr);
58 test_real_mntpoint = test_real_rawfile + "ABCD";
61 std::ofstream rawfile;
62 rawfile.open(test_real_rawfile);
63 for (int c = 0; c < 10485760; c++) {
68 // prepare loopback device
69 const char *base_lo_str = "/dev/loop";
70 char buff[11] = {0, };
71 FILE *fp = popen("/usr/sbin/losetup -f", "r");
73 TEST_FAIL("Can't Open loop-control");
75 if (fgets(buff, 11, fp) == nullptr)
76 TEST_FAIL("Can't Open loop-control");
80 if (strncmp(buff, base_lo_str, strlen(base_lo_str)) != 0)
81 TEST_FAIL("Can't get loopnum");
83 test_real_blkdev = buff;
84 test_real_blkdev.erase(std::remove(test_real_blkdev.begin(), test_real_blkdev.end(), '\n'), test_real_blkdev.end());
86 // set loopback device
87 std::string cmd = "/usr/sbin/losetup ";
88 cmd += test_real_blkdev;
90 cmd += test_real_rawfile;
91 if (system(cmd.c_str())) {}
93 // make filesystem(ext4) and write test file
94 cmd = "/usr/sbin/mkfs.ext4 -j -b 4096 ";
95 // cmd = "/usr/sbin/mkfs.ext4 -j -b 2048 ";
96 // cmd = "/usr/sbin/mkfs.ext4 -j -b 1024 "; // has problem now
97 cmd += test_real_blkdev; // "/dev/loop0"
98 if (system(cmd.c_str())) {}
100 cmd = "/usr/bin/mkdir ";
101 cmd += test_real_mntpoint;
102 if (system(cmd.c_str())) {}
104 cmd = "/usr/bin/mount ";
105 cmd += test_real_rawfile;
107 cmd += test_real_mntpoint;
108 if (system(cmd.c_str())) {}
110 cmd = "echo DEF | cat > "; // filename: ABC, file: DEF
111 cmd += test_real_mntpoint;
113 if (system(cmd.c_str())) {}
115 cmd = "/usr/bin/umount ";
116 cmd += test_real_mntpoint;
117 if (system(cmd.c_str())) {}
120 ////////////////////////////////////////////////////////////////////////////////////////
121 // TEST DMCryptEngine STATIC Functions
122 ////////////////////////////////////////////////////////////////////////////////////////
124 TESTCASE(DMCryptConvertKeyToHexAsciiSingle16byte)
127 const ode::DMCryptEngine::data master_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
128 ode::DMCryptEngine::data master_key_ascii(512);
129 ode::DMCryptEngine::data answer = {0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37,
130 0x30, 0x38, 0x30, 0x39, 0x30, 0x41, 0x30, 0x42, 0x30, 0x43, 0x30, 0x44, 0x30, 0x45, 0x30, 0x46
132 ode::convertKeyToHexASCII(master_key, master_key_ascii);
135 for (unsigned int i = 0; i < master_key.size() * 2; i++) {
136 if (master_key_ascii[i] != answer[i]) {
137 printf("[%2d] %X != %X\n", i, master_key_ascii[i], answer[i]);
138 TEST_FAIL("outcome isn't match with expected answer");
141 } catch (runtime::Exception &e) {
146 TESTCASE(DMCryptConvertKeyToHexAsciiDouble16byte)
149 const ode::DMCryptEngine::data master_key = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F};
150 ode::DMCryptEngine::data master_key_ascii(512);
151 ode::DMCryptEngine::data answer = {0x46, 0x30, 0x45, 0x31, 0x44, 0x32, 0x43, 0x33, 0x42, 0x34, 0x41, 0x35, 0x39, 0x36, 0x38, 0x37,
152 0x37, 0x38, 0x36, 0x39, 0x35, 0x41, 0x34, 0x42, 0x33, 0x43, 0x32, 0x44, 0x31, 0x45, 0x30, 0x46
154 ode::convertKeyToHexASCII(master_key, master_key_ascii);
157 for (unsigned int i = 0; i < master_key.size() * 2; i++)
158 if (master_key_ascii[i] != answer[i])
159 TEST_FAIL("outcome isn't match with expected answer");
160 } catch (runtime::Exception &e) {
165 TESTCASE(DMCryptBlkDevCryptInfoWithDummyFile)
168 const std::string test_crypto_type_name = "test-crypto-type-name";
170 ode::CryptInfo cryptInfo;
171 cryptInfo.init(test_real_blkdev, test_crypto_type_name);
172 if (test_crypto_type_name.compare(cryptInfo.getCryptoTypeName()))
173 TEST_FAIL("CryptoTypeName doesn't match");
175 // we espect the sector size (20480) with our temporary created file
176 long test_fs_size = 20480;
177 if (test_fs_size != cryptInfo.getFileSystemSize())
178 TEST_FAIL("FileSystemSize doesn't match");
180 } catch (runtime::Exception &e) {
185 TESTCASE(DMCryptCreateRemoveCryptoBlkDevWithTempkey)
188 ode::CryptInfo cryptInfo;
189 cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
190 const std::string keystring = "01020304050607080910111213141516";
191 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
192 const std::string crypto_blkdev = ode::createCryptoBlkDev(test_real_blkdev, "testdata", key32bit, cryptInfo.getFileSystemSize(), cryptInfo.getCryptoTypeName());
193 // check crypto device status using cryptsetup
195 FILE *fp = popen("cryptsetup status testdata", "r");
197 TEST_FAIL("Can't get cryptsetup status");
199 std::vector<std::string> answer = {
200 "/dev/mapper/testdata is active.",
202 " cipher: aes-cbc-essiv:sha256",
203 " keysize: 256 bits",
204 std::string(" device: " + test_real_blkdev),
205 std::string(" loop: " + test_real_rawfile),
206 " offset: 0 sectors",
207 " size: 20480 sectors",
211 char buff[128] = {0, };
212 for (auto &a : answer) {
213 if (fgets(buff, 128, fp) == nullptr) {
215 TEST_FAIL("Can't read cryptsetup status from buffer");
218 std::string ret = buff;
219 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
221 if (ret.compare(a)) {
223 std::cout << "expected: " << a << "\n";
224 std::cout << "real : " << ret << "\n";
225 TEST_FAIL("Unexpected result from cryptsetup status");
231 // remove crypto device
232 ode::destroyCryptoBlkDev("testdata");
233 // check crypto device status using cryptsetup
235 FILE *fp = popen("cryptsetup status testdata", "r");
237 TEST_FAIL("Can't get cryptsetup status");
239 std::vector<std::string> answer = {
240 "/dev/mapper/testdata is inactive.",
243 char buff[128] = {0, };
244 for (auto &a : answer) {
245 if (fgets(buff, 128, fp) == nullptr) {
247 TEST_FAIL("Can't read cryptsetup status from buffer");
250 std::string ret = buff;
251 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
253 if (ret.compare(a)) {
255 std::cout << "expected: " << a << "\n";
256 std::cout << "real : " << ret << "\n";
257 TEST_FAIL("Unexpected result from cryptsetup status");
262 } catch (runtime::Exception &e) {
267 TESTCASE(DMCryptSanitizeKey)
270 // case1. small size than minimum byte (32) for dmcrypt
271 // -- expected: cause exception
273 const std::string keystring = "SMALLER_THAN_32BYTES______";
274 const ode::DMCryptEngine::data keySmall32bit(keystring.begin(), keystring.end());
276 ode::sanitizeKey(keySmall32bit);
278 TEST_FAIL("small key should be cause exception");
279 } catch (runtime::Exception &e) {
280 // expected result: it's good
284 // -- expected: returned key length should be 32 byte.
285 // -- expected: returned key data is same with 32 elements from first of big key
287 const std::string keystring = "01020304050607080910111213141516";
288 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
289 const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(key32bit);
292 TEST_EXPECT(sanitized_key.size(), key32bit.size());
295 for (unsigned i = 0; i < sanitized_key.size(); i++) {
296 TEST_EXPECT(sanitized_key[i], key32bit[i]);
299 // case3. big size than minimum byte (32) for dmcrypt
300 // -- expected: returned key length should be 32 byte.
301 // -- expected: returned key data is same with 32 elements from first of big key
303 const std::string keystring = "01020304050607080910111213141516_MAKES_BIG";
304 const ode::DMCryptEngine::data keyBig(keystring.begin(), keystring.end());
305 const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(keyBig);
308 TEST_EXPECT(sanitized_key.size(), (unsigned)DMCRYPT_KEY_MIN_LEN_BYTE);
311 for (unsigned i = 0; i < sanitized_key.size(); i++) {
312 TEST_EXPECT(sanitized_key[i], keyBig[i]);
316 } catch (runtime::Exception &e) {
321 ////////////////////////////////////////////////////////////////////////////////////////
322 // TEST DMCryptEngine
323 ////////////////////////////////////////////////////////////////////////////////////////
325 TESTCASE(DMCryptGetPathTest)
328 ode::DMCryptEngine engine("/dev/mmcblk0p1", TEST_USERDATA_PATH, progress);
329 if (engine.getSource() != "/dev/mmcblk0p1") {
330 throw runtime::Exception("Source doen't match");
332 if (engine.getDestination() != "/opt/usr") {
333 throw runtime::Exception("Destination doen't match");
335 } catch (runtime::Exception &e) {
340 TESTCASE(DMCryptEncryptAndDecrypt)
343 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
344 const std::string keystring = "01020304050607080910111213141516";
345 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
347 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progress);
348 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
350 // check the encryption result of test_real_blkdev(/dev/loop0)
351 // at this time, if we mount /dev/loop0 forcely, we can't mount them...
353 int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
354 if ((ret != -1) || (runtime::Error::lastErrorCode() != EINVAL)) {
355 // we expected ret value is EINVAL (mount failure in man page of mount)
356 std::cout << "expected: EINVAL" << "\n";
357 std::cout << "real : " << runtime::GetSystemErrorMessage() << "\n";
358 TEST_FAIL("Unexpected result from cryptsetup status");
362 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
364 // check the decryption result of test_Real_Blkdev(/dev/loop0)
365 // at this time, if we mount /dev/loop0 forcely, we can mount them,
366 // and, we can read test file successfully (file name: ABC, body:DEF)
368 int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
370 TEST_FAIL("Can't mount decyprted target");
372 runtime::File isgood(test_real_mntpoint + "/ABC");
373 if (isgood.exists() == false) {
374 TEST_FAIL("Can't read test file (file name: ABC, body:DEF)");
376 ::umount(test_real_mntpoint.c_str());
378 } catch (runtime::Exception &e) {
383 TESTCASE(DMCryptEncryptMountUnmountDecrypt)
386 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
387 const std::string keystring = "01020304050607080910111213141516";
388 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
390 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progress);
391 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
392 engine.mount(key32bit, 0);
394 // we should find test file (file name: ABC, body: DEF) in mount-point
395 std::string cmd = "cat " + test_real_mntpoint + "/ABC";
396 FILE *fp = popen(cmd.c_str(), "r");
398 TEST_FAIL("Can't get test file body");
400 std::vector<std::string> answer = {
404 char buff[128] = {0, };
405 for (auto &a : answer) {
406 if (fgets(buff, 128, fp) == nullptr) {
408 TEST_FAIL("Can't read test file body from buffer");
411 std::string ret = buff;
412 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
414 if (ret.compare(a)) {
416 std::cout << "expected: " << a << "\n";
417 std::cout << "real : " << ret << "\n";
418 TEST_FAIL("Unexpected result from test file body");
424 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
426 } catch (runtime::Exception &e) {
431 TESTCASE(DMCryptFastEncryptMountUnmountFastDecrypt)
434 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
435 const std::string keystring = "01020304050607080910111213141516";
436 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
438 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progress);
439 engine.encrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
440 engine.mount(key32bit, 0);
442 // we should find test file (file name: ABC, body: DEF) in mount-point
443 std::string cmd = "cat " + test_real_mntpoint + "/ABC";
444 FILE *fp = popen(cmd.c_str(), "r");
446 TEST_FAIL("Can't get test file body");
448 std::vector<std::string> answer = {
452 char buff[128] = {0, };
453 for (auto &a : answer) {
454 if (fgets(buff, 128, fp) == nullptr) {
456 TEST_FAIL("Can't read test file body from buffer");
459 std::string ret = buff;
460 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
462 if (ret.compare(a)) {
464 std::cout << "expected: " << a << "\n";
465 std::cout << "real : " << ret << "\n";
466 TEST_FAIL("Unexpected result from test file body");
472 engine.decrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
474 } catch (runtime::Exception &e) {
479 ////////////////////////////////////////////////////////////////////////////////////////
480 // TEST Deinitializer
481 ////////////////////////////////////////////////////////////////////////////////////////
483 TESTCASE(DMCryptTestDeInit)
485 std::string cmd = "/usr/sbin/losetup -d ";
486 cmd += test_real_blkdev;
487 if (system(cmd.c_str())) {}
489 if (::unlink(test_real_rawfile.c_str()) == -1)
490 ::unlink(test_real_rawfile.c_str());
492 cmd = "/usr/bin/rm -rf ";
493 cmd += test_real_mntpoint;
494 if (system(cmd.c_str())) {}