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/progress-bar.h"
33 #include "../server/progress-vconf-backend.h"
34 #include "../server/engine/dmcrypt-engine.cpp"
36 #define TEST_USERDATA_NAME "userdata"
37 #define TEST_USERDATA_PATH "/opt/usr"
41 static std::string test_real_rawfile; // i.e. "/tmp/fileXXXXXX"
42 static std::string test_real_blkdev; // i.e. "/dev/loop0"
43 static std::string test_real_mntpoint;
45 // ode::DMCryptEngine gvTest("/dev/mmcblk0p25", "/opt/usr");
47 ode::VConfBackend vconfBackend(VCONFKEY_ODE_ENCRYPT_PROGRESS);
48 ode::ProgressBar progressBar(std::bind(&ode::VConfBackend::update, &vconfBackend, std::placeholders::_1));
52 ////////////////////////////////////////////////////////////////////////////////////////
54 ////////////////////////////////////////////////////////////////////////////////////////
56 // Test file size: 10485760 + 20480*512
57 // Expected Secor size: 20480 (10485760 = 20480 * 512)
58 TESTCASE(DMCryptTestInit)
60 test_real_rawfile = tmpnam(nullptr);
61 test_real_mntpoint = test_real_rawfile + "ABCD";
64 std::ofstream rawfile;
65 rawfile.open(test_real_rawfile);
66 for (int c = 0; c < 10485760; c++) {
71 // prepare loopback device
72 const char *base_lo_str = "/dev/loop";
73 char buff[11] = {0, };
74 FILE *fp = popen("/usr/sbin/losetup -f", "r");
76 TEST_FAIL("Can't Open loop-control");
78 if (fgets(buff, 11, fp) == nullptr)
79 TEST_FAIL("Can't Open loop-control");
83 if (strncmp(buff, base_lo_str, strlen(base_lo_str)) != 0)
84 TEST_FAIL("Can't get loopnum");
86 test_real_blkdev = buff;
87 test_real_blkdev.erase(std::remove(test_real_blkdev.begin(), test_real_blkdev.end(), '\n'), test_real_blkdev.end());
89 // set loopback device
90 std::string cmd = "/usr/sbin/losetup ";
91 cmd += test_real_blkdev;
93 cmd += test_real_rawfile;
94 if (system(cmd.c_str())) {}
96 // make filesystem(ext4) and write test file
97 cmd = "/usr/sbin/mkfs.ext4 -j -b 4096 ";
98 // cmd = "/usr/sbin/mkfs.ext4 -j -b 2048 ";
99 // cmd = "/usr/sbin/mkfs.ext4 -j -b 1024 "; // has problem now
100 cmd += test_real_blkdev; // "/dev/loop0"
101 if (system(cmd.c_str())) {}
103 cmd = "/usr/bin/mkdir ";
104 cmd += test_real_mntpoint;
105 if (system(cmd.c_str())) {}
107 cmd = "/usr/bin/mount ";
108 cmd += test_real_rawfile;
110 cmd += test_real_mntpoint;
111 if (system(cmd.c_str())) {}
113 cmd = "echo DEF | cat > "; // filename: ABC, file: DEF
114 cmd += test_real_mntpoint;
116 if (system(cmd.c_str())) {}
118 cmd = "/usr/bin/umount ";
119 cmd += test_real_mntpoint;
120 if (system(cmd.c_str())) {}
123 ////////////////////////////////////////////////////////////////////////////////////////
124 // TEST DMCryptEngine STATIC Functions
125 ////////////////////////////////////////////////////////////////////////////////////////
127 TESTCASE(DMCryptConvertKeyToHexAsciiSingle16byte)
130 const ode::DMCryptEngine::data master_key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
131 ode::DMCryptEngine::data master_key_ascii(512);
132 ode::DMCryptEngine::data answer = {0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37,
133 0x30, 0x38, 0x30, 0x39, 0x30, 0x41, 0x30, 0x42, 0x30, 0x43, 0x30, 0x44, 0x30, 0x45, 0x30, 0x46
135 ode::convertKeyToHexASCII(master_key, master_key_ascii);
138 for (unsigned int i = 0; i < master_key.size() * 2; i++) {
139 if (master_key_ascii[i] != answer[i]) {
140 printf("[%2d] %X != %X\n", i, master_key_ascii[i], answer[i]);
141 TEST_FAIL("outcome isn't match with expected answer");
144 } catch (runtime::Exception &e) {
149 TESTCASE(DMCryptConvertKeyToHexAsciiDouble16byte)
152 const ode::DMCryptEngine::data master_key = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F};
153 ode::DMCryptEngine::data master_key_ascii(512);
154 ode::DMCryptEngine::data answer = {0x46, 0x30, 0x45, 0x31, 0x44, 0x32, 0x43, 0x33, 0x42, 0x34, 0x41, 0x35, 0x39, 0x36, 0x38, 0x37,
155 0x37, 0x38, 0x36, 0x39, 0x35, 0x41, 0x34, 0x42, 0x33, 0x43, 0x32, 0x44, 0x31, 0x45, 0x30, 0x46
157 ode::convertKeyToHexASCII(master_key, master_key_ascii);
160 for (unsigned int i = 0; i < master_key.size() * 2; i++)
161 if (master_key_ascii[i] != answer[i])
162 TEST_FAIL("outcome isn't match with expected answer");
163 } catch (runtime::Exception &e) {
168 TESTCASE(DMCryptBlkDevCryptInfoWithDummyFile)
171 const std::string test_crypto_type_name = "test-crypto-type-name";
173 ode::CryptInfo cryptInfo;
174 cryptInfo.init(test_real_blkdev, test_crypto_type_name);
175 if (test_crypto_type_name.compare(cryptInfo.getCryptoTypeName()))
176 TEST_FAIL("CryptoTypeName doesn't match");
178 // we espect the sector size (20480) with our temporary created file
179 long test_fs_size = 20480;
180 if (test_fs_size != cryptInfo.getFileSystemSize())
181 TEST_FAIL("FileSystemSize doesn't match");
183 } catch (runtime::Exception &e) {
188 TESTCASE(DMCryptCreateRemoveCryptoBlkDevWithTempkey)
191 ode::CryptInfo cryptInfo;
192 cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
193 const std::string keystring = "01020304050607080910111213141516";
194 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
195 const std::string crypto_blkdev = ode::createCryptoBlkDev(test_real_blkdev, "testdata", key32bit, cryptInfo.getFileSystemSize(), cryptInfo.getCryptoTypeName());
196 // check crypto device status using cryptsetup
198 FILE *fp = popen("cryptsetup status testdata", "r");
200 TEST_FAIL("Can't get cryptsetup status");
202 std::vector<std::string> answer = {
203 "/dev/mapper/testdata is active.",
205 " cipher: aes-cbc-essiv:sha256",
206 " keysize: 256 bits",
207 std::string(" device: " + test_real_blkdev),
208 std::string(" loop: " + test_real_rawfile),
209 " offset: 0 sectors",
210 " size: 20480 sectors",
214 char buff[128] = {0, };
215 for (auto &a : answer) {
216 if (fgets(buff, 128, fp) == nullptr) {
218 TEST_FAIL("Can't read cryptsetup status from buffer");
221 std::string ret = buff;
222 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
224 if (ret.compare(a)) {
226 std::cout << "expected: " << a << "\n";
227 std::cout << "real : " << ret << "\n";
228 TEST_FAIL("Unexpected result from cryptsetup status");
234 // remove crypto device
235 ode::destroyCryptoBlkDev("testdata");
236 // check crypto device status using cryptsetup
238 FILE *fp = popen("cryptsetup status testdata", "r");
240 TEST_FAIL("Can't get cryptsetup status");
242 std::vector<std::string> answer = {
243 "/dev/mapper/testdata is inactive.",
246 char buff[128] = {0, };
247 for (auto &a : answer) {
248 if (fgets(buff, 128, fp) == nullptr) {
250 TEST_FAIL("Can't read cryptsetup status from buffer");
253 std::string ret = buff;
254 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
256 if (ret.compare(a)) {
258 std::cout << "expected: " << a << "\n";
259 std::cout << "real : " << ret << "\n";
260 TEST_FAIL("Unexpected result from cryptsetup status");
265 } catch (runtime::Exception &e) {
270 TESTCASE(DMCryptSanitizeKey)
273 // case1. small size than minimum byte (32) for dmcrypt
274 // -- expected: cause exception
276 const std::string keystring = "SMALLER_THAN_32BYTES______";
277 const ode::DMCryptEngine::data keySmall32bit(keystring.begin(), keystring.end());
279 ode::sanitizeKey(keySmall32bit);
281 TEST_FAIL("small key should be cause exception");
282 } catch (runtime::Exception &e) {
283 // expected result: it's good
287 // -- expected: returned key length should be 32 byte.
288 // -- expected: returned key data is same with 32 elements from first of big key
290 const std::string keystring = "01020304050607080910111213141516";
291 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
292 const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(key32bit);
295 TEST_EXPECT(sanitized_key.size(), key32bit.size());
298 for (unsigned i = 0; i < sanitized_key.size(); i++) {
299 TEST_EXPECT(sanitized_key[i], key32bit[i]);
302 // case3. big size than minimum byte (32) for dmcrypt
303 // -- expected: returned key length should be 32 byte.
304 // -- expected: returned key data is same with 32 elements from first of big key
306 const std::string keystring = "01020304050607080910111213141516_MAKES_BIG";
307 const ode::DMCryptEngine::data keyBig(keystring.begin(), keystring.end());
308 const ode::DMCryptEngine::data sanitized_key = ode::sanitizeKey(keyBig);
311 TEST_EXPECT(sanitized_key.size(), (unsigned)DMCRYPT_KEY_MIN_LEN_BYTE);
314 for (unsigned i = 0; i < sanitized_key.size(); i++) {
315 TEST_EXPECT(sanitized_key[i], keyBig[i]);
319 } catch (runtime::Exception &e) {
324 ////////////////////////////////////////////////////////////////////////////////////////
325 // TEST DMCryptEngine
326 ////////////////////////////////////////////////////////////////////////////////////////
328 TESTCASE(DMCryptGetPathTest)
331 ode::DMCryptEngine engine("/dev/mmcblk0p1", TEST_USERDATA_PATH, progressBar);
332 if (engine.getSource() != "/dev/mmcblk0p1") {
333 throw runtime::Exception("Source doen't match");
335 if (engine.getDestination() != "/opt/usr") {
336 throw runtime::Exception("Destination doen't match");
338 } catch (runtime::Exception &e) {
343 TESTCASE(DMCryptEncryptAndDecrypt)
346 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
347 const std::string keystring = "01020304050607080910111213141516";
348 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
350 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progressBar);
351 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
353 // check the encryption result of test_real_blkdev(/dev/loop0)
354 // at this time, if we mount /dev/loop0 forcely, we can't mount them...
356 int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
357 if ((ret != -1) || (runtime::Error::lastErrorCode() != EINVAL)) {
358 // we expected ret value is EINVAL (mount failure in man page of mount)
359 std::cout << "expected: EINVAL" << "\n";
360 std::cout << "real : " << runtime::GetSystemErrorMessage() << "\n";
361 TEST_FAIL("Unexpected result from cryptsetup status");
365 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
367 // check the decryption result of test_Real_Blkdev(/dev/loop0)
368 // at this time, if we mount /dev/loop0 forcely, we can mount them,
369 // and, we can read test file successfully (file name: ABC, body:DEF)
371 int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
373 TEST_FAIL("Can't mount decyprted target");
375 runtime::File isgood(test_real_mntpoint + "/ABC");
376 if (isgood.exists() == false) {
377 TEST_FAIL("Can't read test file (file name: ABC, body:DEF)");
379 ::umount(test_real_mntpoint.c_str());
381 } catch (runtime::Exception &e) {
386 TESTCASE(DMCryptEncryptMountUnmountDecrypt)
389 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
390 const std::string keystring = "01020304050607080910111213141516";
391 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
393 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progressBar);
394 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
395 engine.mount(key32bit, 0);
397 // we should find test file (file name: ABC, body: DEF) in mount-point
398 std::string cmd = "cat " + test_real_mntpoint + "/ABC";
399 FILE *fp = popen(cmd.c_str(), "r");
401 TEST_FAIL("Can't get test file body");
403 std::vector<std::string> answer = {
407 char buff[128] = {0, };
408 for (auto &a : answer) {
409 if (fgets(buff, 128, fp) == nullptr) {
411 TEST_FAIL("Can't read test file body from buffer");
414 std::string ret = buff;
415 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
417 if (ret.compare(a)) {
419 std::cout << "expected: " << a << "\n";
420 std::cout << "real : " << ret << "\n";
421 TEST_FAIL("Unexpected result from test file body");
427 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
429 } catch (runtime::Exception &e) {
434 TESTCASE(DMCryptFastEncryptMountUnmountFastDecrypt)
437 // ode::cryptInfo.init(test_real_blkdev, "aes-cbc-essiv:sha256");
438 const std::string keystring = "01020304050607080910111213141516";
439 const ode::DMCryptEngine::data key32bit(keystring.begin(), keystring.end());
441 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progressBar);
442 engine.encrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
443 engine.mount(key32bit, 0);
445 // we should find test file (file name: ABC, body: DEF) in mount-point
446 std::string cmd = "cat " + test_real_mntpoint + "/ABC";
447 FILE *fp = popen(cmd.c_str(), "r");
449 TEST_FAIL("Can't get test file body");
451 std::vector<std::string> answer = {
455 char buff[128] = {0, };
456 for (auto &a : answer) {
457 if (fgets(buff, 128, fp) == nullptr) {
459 TEST_FAIL("Can't read test file body from buffer");
462 std::string ret = buff;
463 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
465 if (ret.compare(a)) {
467 std::cout << "expected: " << a << "\n";
468 std::cout << "real : " << ret << "\n";
469 TEST_FAIL("Unexpected result from test file body");
475 engine.decrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
477 } catch (runtime::Exception &e) {
482 ////////////////////////////////////////////////////////////////////////////////////////
483 // TEST Deinitializer
484 ////////////////////////////////////////////////////////////////////////////////////////
486 TESTCASE(DMCryptTestDeInit)
488 std::string cmd = "/usr/sbin/losetup -d ";
489 cmd += test_real_blkdev;
490 if (system(cmd.c_str())) {}
492 if (::unlink(test_real_rawfile.c_str()) == -1)
493 ::unlink(test_real_rawfile.c_str());
495 cmd = "/usr/bin/rm -rf ";
496 cmd += test_real_mntpoint;
497 if (system(cmd.c_str())) {}