6c926d49b46ad72a799840b9314bc7af3f81ca4a
[platform/core/security/ode.git] / tests / dmcrypt-engine.cpp
1 /*
2  *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16
17 #include <unistd.h>
18 #include <linux/loop.h>
19
20 #include <cstdio>
21 #include <cstdlib>
22 #include <string>
23 #include <fstream>
24 #include <vector>
25 #include <algorithm>
26
27 #include <klay/error.h>
28 #include <klay/exception.h>
29 #include <klay/testbench.h>
30 #include <klay/process.h>
31
32 #include "../server/progress-bar.h"
33 #include "../server/progress-vconf-backend.h"
34 #include "../server/engine/dmcrypt-engine.cpp"
35
36 #define TEST_USERDATA_NAME  "userdata"
37 #define TEST_USERDATA_PATH      "/opt/usr"
38
39 namespace {
40
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;
44
45 // ode::DMCryptEngine gvTest("/dev/mmcblk0p25", "/opt/usr");
46
47 ode::VConfBackend vconfBackend(VCONFKEY_ODE_ENCRYPT_PROGRESS);
48 ode::ProgressBar progressBar(std::bind(&ode::VConfBackend::update, &vconfBackend, std::placeholders::_1));
49
50 }
51
52 ////////////////////////////////////////////////////////////////////////////////////////
53 // TEST Initializer
54 ////////////////////////////////////////////////////////////////////////////////////////
55
56 // Test file size: 10485760 + 20480*512
57 // Expected Secor size: 20480 (10485760 = 20480 * 512)
58 TESTCASE(DMCryptTestInit)
59 {
60         test_real_rawfile = tmpnam(nullptr);
61         test_real_mntpoint = test_real_rawfile + "ABCD";
62
63         // prepare rawfile
64         std::ofstream rawfile;
65         rawfile.open(test_real_rawfile);
66         for (int c = 0; c < 10485760; c++) {
67                 rawfile << "0";
68         }
69         rawfile.close();
70
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");
75         if (!fp)
76                 TEST_FAIL("Can't Open loop-control");
77
78         if (fgets(buff, 11, fp) == nullptr)
79                 TEST_FAIL("Can't Open loop-control");
80
81         pclose(fp);
82
83         if (strncmp(buff, base_lo_str, strlen(base_lo_str)) != 0)
84                 TEST_FAIL("Can't get loopnum");
85
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());
88
89         // set loopback device
90         std::string cmd = "/usr/sbin/losetup ";
91         cmd += test_real_blkdev;
92         cmd += " ";
93         cmd += test_real_rawfile;
94         if (system(cmd.c_str())) {}
95
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())) {}
102
103         cmd = "/usr/bin/mkdir ";
104         cmd += test_real_mntpoint;
105         if (system(cmd.c_str())) {}
106
107         cmd = "/usr/bin/mount ";
108         cmd += test_real_rawfile;
109         cmd += " ";
110         cmd += test_real_mntpoint;
111         if (system(cmd.c_str())) {}
112
113         cmd = "echo DEF | cat > ";      // filename: ABC, file: DEF
114         cmd += test_real_mntpoint;
115         cmd += "/ABC";
116         if (system(cmd.c_str())) {}
117
118         cmd = "/usr/bin/umount ";
119         cmd += test_real_mntpoint;
120         if (system(cmd.c_str())) {}
121 }
122
123 ////////////////////////////////////////////////////////////////////////////////////////
124 // TEST DMCryptEngine STATIC Functions
125 ////////////////////////////////////////////////////////////////////////////////////////
126
127 TESTCASE(DMCryptConvertKeyToHexAsciiSingle16byte)
128 {
129         try {
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
134                                                                                   };
135                 ode::convertKeyToHexASCII(master_key, master_key_ascii);
136
137                 // check
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");
142                         }
143                 }
144         } catch (runtime::Exception &e) {
145                 TEST_FAIL(e.what());
146         }
147 }
148
149 TESTCASE(DMCryptConvertKeyToHexAsciiDouble16byte)
150 {
151         try {
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
156                                                                                   };
157                 ode::convertKeyToHexASCII(master_key, master_key_ascii);
158
159                 // check
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) {
164                 TEST_FAIL(e.what());
165         }
166 }
167
168 TESTCASE(DMCryptBlkDevCryptInfoWithDummyFile)
169 {
170         try {
171                 const std::string test_crypto_type_name = "test-crypto-type-name";
172
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");
177
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");
182                 //
183         } catch (runtime::Exception &e) {
184                 TEST_FAIL(e.what());
185         }
186 }
187
188 TESTCASE(DMCryptCreateRemoveCryptoBlkDevWithTempkey)
189 {
190         try {
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
197                 {
198                         FILE *fp = popen("cryptsetup status testdata", "r");
199                         if (!fp)
200                                 TEST_FAIL("Can't get cryptsetup status");
201
202                         std::vector<std::string> answer = {
203                                 "/dev/mapper/testdata is active.",
204                                 "  type:    n/a",
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",
211                                 "  mode:    read/write",
212                         };
213
214                         char buff[128] = {0, };
215                         for (auto &a : answer) {
216                                 if (fgets(buff, 128, fp) == nullptr) {
217                                         pclose(fp);
218                                         TEST_FAIL("Can't read cryptsetup status from buffer");
219                                 }
220
221                                 std::string ret = buff;
222                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
223
224                                 if (ret.compare(a)) {
225                                         pclose(fp);
226                                         std::cout << "expected: " << a << "\n";
227                                         std::cout << "real    : " << ret << "\n";
228                                         TEST_FAIL("Unexpected result from cryptsetup status");
229                                 }
230                         }
231                         pclose(fp);
232                 }
233
234                 // remove crypto device
235                 ode::destroyCryptoBlkDev("testdata");
236                 // check crypto device status using cryptsetup
237                 {
238                         FILE *fp = popen("cryptsetup status testdata", "r");
239                         if (!fp)
240                                 TEST_FAIL("Can't get cryptsetup status");
241
242                         std::vector<std::string> answer = {
243                                 "/dev/mapper/testdata is inactive.",
244                         };
245
246                         char buff[128] = {0, };
247                         for (auto &a : answer) {
248                                 if (fgets(buff, 128, fp) == nullptr) {
249                                         pclose(fp);
250                                         TEST_FAIL("Can't read cryptsetup status from buffer");
251                                 }
252
253                                 std::string ret = buff;
254                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
255
256                                 if (ret.compare(a)) {
257                                         pclose(fp);
258                                         std::cout << "expected: " << a << "\n";
259                                         std::cout << "real    : " << ret << "\n";
260                                         TEST_FAIL("Unexpected result from cryptsetup status");
261                                 }
262                         }
263                         pclose(fp);
264                 }
265         } catch (runtime::Exception &e) {
266                 TEST_FAIL(e.what());
267         }
268 }
269
270 TESTCASE(DMCryptSanitizeKey)
271 {
272         try {
273                 // case1. small size than minimum byte (32) for dmcrypt
274                 // -- expected: cause exception
275                 {
276                         const std::string keystring = "SMALLER_THAN_32BYTES______";
277                         const ode::DMCryptEngine::data keySmall32bit(keystring.begin(), keystring.end());
278                         try {
279                                 ode::sanitizeKey(keySmall32bit);
280                                 //
281                                 TEST_FAIL("small key should be cause exception");
282                         } catch (runtime::Exception &e) {
283                                 // expected result: it's good
284                         }
285                 }
286                 // case2. same size
287                 // -- expected: returned key length should be 32 byte.
288                 // -- expected: returned key data is same with 32 elements from first of big key
289                 {
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);
293
294                         // check size
295                         TEST_EXPECT(sanitized_key.size(), key32bit.size());
296
297                         // check data
298                         for (unsigned i = 0; i < sanitized_key.size(); i++) {
299                                 TEST_EXPECT(sanitized_key[i], key32bit[i]);
300                         }
301                 }
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
305                 {
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);
309
310                         // check size
311                         TEST_EXPECT(sanitized_key.size(), (unsigned)DMCRYPT_KEY_MIN_LEN_BYTE);
312
313                         // check data
314                         for (unsigned i = 0; i < sanitized_key.size(); i++) {
315                                 TEST_EXPECT(sanitized_key[i], keyBig[i]);
316                         }
317                 }
318                 //
319         } catch (runtime::Exception &e) {
320                 TEST_FAIL(e.what());
321         }
322 }
323
324 ////////////////////////////////////////////////////////////////////////////////////////
325 // TEST DMCryptEngine
326 ////////////////////////////////////////////////////////////////////////////////////////
327
328 TESTCASE(DMCryptGetPathTest)
329 {
330         try {
331                 ode::DMCryptEngine engine("/dev/mmcblk0p1", TEST_USERDATA_PATH, progressBar);
332                 if (engine.getSource() != "/dev/mmcblk0p1") {
333                         throw runtime::Exception("Source doen't match");
334                 }
335                 if (engine.getDestination() != "/opt/usr") {
336                         throw runtime::Exception("Destination doen't match");
337                 }
338         } catch (runtime::Exception &e) {
339                 TEST_FAIL(e.what());
340         }
341 }
342
343 TESTCASE(DMCryptEncryptAndDecrypt)
344 {
345         try {
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());
349
350                 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progressBar);
351                 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
352
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...
355                 {
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");
362                         }
363                 }
364                 // decyprt
365                 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
366
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)
370                 {
371                         int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
372                         if (ret == -1) {
373                                 TEST_FAIL("Can't mount decyprted target");
374                         }
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)");
378                         }
379                         ::umount(test_real_mntpoint.c_str());
380                 }
381         } catch (runtime::Exception &e) {
382                 TEST_FAIL(e.what());
383         }
384 }
385
386 TESTCASE(DMCryptEncryptMountUnmountDecrypt)
387 {
388         try {
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());
392
393                 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progressBar);
394                 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
395                 engine.mount(key32bit, 0);
396                 {
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");
400                         if (!fp)
401                                 TEST_FAIL("Can't get test file body");
402
403                         std::vector<std::string> answer = {
404                                 "DEF",
405                         };
406
407                         char buff[128] = {0, };
408                         for (auto &a : answer) {
409                                 if (fgets(buff, 128, fp) == nullptr) {
410                                         pclose(fp);
411                                         TEST_FAIL("Can't read test file body from buffer");
412                                 }
413
414                                 std::string ret = buff;
415                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
416
417                                 if (ret.compare(a)) {
418                                         pclose(fp);
419                                         std::cout << "expected: " << a << "\n";
420                                         std::cout << "real    : " << ret << "\n";
421                                         TEST_FAIL("Unexpected result from test file body");
422                                 }
423                         }
424                         pclose(fp);
425                 }
426                 engine.umount();
427                 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
428                 //
429         } catch (runtime::Exception &e) {
430                 TEST_FAIL(e.what());
431         }
432 }
433
434 TESTCASE(DMCryptFastEncryptMountUnmountFastDecrypt)
435 {
436         try {
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());
440
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);
444                 {
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");
448                         if (!fp)
449                                 TEST_FAIL("Can't get test file body");
450
451                         std::vector<std::string> answer = {
452                                 "DEF",
453                         };
454
455                         char buff[128] = {0, };
456                         for (auto &a : answer) {
457                                 if (fgets(buff, 128, fp) == nullptr) {
458                                         pclose(fp);
459                                         TEST_FAIL("Can't read test file body from buffer");
460                                 }
461
462                                 std::string ret = buff;
463                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
464
465                                 if (ret.compare(a)) {
466                                         pclose(fp);
467                                         std::cout << "expected: " << a << "\n";
468                                         std::cout << "real    : " << ret << "\n";
469                                         TEST_FAIL("Unexpected result from test file body");
470                                 }
471                         }
472                         pclose(fp);
473                 }
474                 engine.umount();
475                 engine.decrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
476                 //
477         } catch (runtime::Exception &e) {
478                 TEST_FAIL(e.what());
479         }
480 }
481
482 ////////////////////////////////////////////////////////////////////////////////////////
483 // TEST Deinitializer
484 ////////////////////////////////////////////////////////////////////////////////////////
485
486 TESTCASE(DMCryptTestDeInit)
487 {
488         std::string cmd = "/usr/sbin/losetup -d ";
489         cmd += test_real_blkdev;
490         if (system(cmd.c_str())) {}
491
492         if (::unlink(test_real_rawfile.c_str()) == -1)
493                 ::unlink(test_real_rawfile.c_str());
494
495         cmd = "/usr/bin/rm -rf ";
496         cmd += test_real_mntpoint;
497         if (system(cmd.c_str())) {}
498 }