7fecdedd0b8f8c3b7aade1c2d25d99fa97689259
[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/engine/dmcrypt-engine.cpp"
33
34 #define TEST_USERDATA_NAME  "userdata"
35 #define TEST_USERDATA_PATH      "/opt/usr"
36
37 namespace {
38
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;
42
43 // ode::DMCryptEngine gvTest("/dev/mmcblk0p25", "/opt/usr");
44
45 ode::ProgressBar progress([](int v) {});
46
47 }
48
49 ////////////////////////////////////////////////////////////////////////////////////////
50 // TEST Initializer
51 ////////////////////////////////////////////////////////////////////////////////////////
52
53 // Test file size: 10485760 + 20480*512
54 // Expected Secor size: 20480 (10485760 = 20480 * 512)
55 TESTCASE(DMCryptTestInit)
56 {
57         test_real_rawfile = tmpnam(nullptr);
58         test_real_mntpoint = test_real_rawfile + "ABCD";
59
60         // prepare rawfile
61         std::ofstream rawfile;
62         rawfile.open(test_real_rawfile);
63         for (int c = 0; c < 10485760; c++) {
64                 rawfile << "0";
65         }
66         rawfile.close();
67
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");
72         if (!fp)
73                 TEST_FAIL("Can't Open loop-control");
74
75         if (fgets(buff, 11, fp) == nullptr)
76                 TEST_FAIL("Can't Open loop-control");
77
78         pclose(fp);
79
80         if (strncmp(buff, base_lo_str, strlen(base_lo_str)) != 0)
81                 TEST_FAIL("Can't get loopnum");
82
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());
85
86         // set loopback device
87         std::string cmd = "/usr/sbin/losetup ";
88         cmd += test_real_blkdev;
89         cmd += " ";
90         cmd += test_real_rawfile;
91         if (system(cmd.c_str())) {}
92
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())) {}
99
100         cmd = "/usr/bin/mkdir ";
101         cmd += test_real_mntpoint;
102         if (system(cmd.c_str())) {}
103
104         cmd = "/usr/bin/mount ";
105         cmd += test_real_rawfile;
106         cmd += " ";
107         cmd += test_real_mntpoint;
108         if (system(cmd.c_str())) {}
109
110         cmd = "echo DEF | cat > ";      // filename: ABC, file: DEF
111         cmd += test_real_mntpoint;
112         cmd += "/ABC";
113         if (system(cmd.c_str())) {}
114
115         cmd = "/usr/bin/umount ";
116         cmd += test_real_mntpoint;
117         if (system(cmd.c_str())) {}
118 }
119
120 ////////////////////////////////////////////////////////////////////////////////////////
121 // TEST DMCryptEngine STATIC Functions
122 ////////////////////////////////////////////////////////////////////////////////////////
123
124 TESTCASE(DMCryptConvertKeyToHexAsciiSingle16byte)
125 {
126         try {
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
131                                                                                   };
132                 ode::convertKeyToHexASCII(master_key, master_key_ascii);
133
134                 // check
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");
139                         }
140                 }
141         } catch (runtime::Exception &e) {
142                 TEST_FAIL(e.what());
143         }
144 }
145
146 TESTCASE(DMCryptConvertKeyToHexAsciiDouble16byte)
147 {
148         try {
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
153                                                                                   };
154                 ode::convertKeyToHexASCII(master_key, master_key_ascii);
155
156                 // check
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) {
161                 TEST_FAIL(e.what());
162         }
163 }
164
165 TESTCASE(DMCryptBlkDevCryptInfoWithDummyFile)
166 {
167         try {
168                 const std::string test_crypto_type_name = "test-crypto-type-name";
169
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");
174
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");
179                 //
180         } catch (runtime::Exception &e) {
181                 TEST_FAIL(e.what());
182         }
183 }
184
185 TESTCASE(DMCryptCreateRemoveCryptoBlkDevWithTempkey)
186 {
187         try {
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
194                 {
195                         FILE *fp = popen("cryptsetup status testdata", "r");
196                         if (!fp)
197                                 TEST_FAIL("Can't get cryptsetup status");
198
199                         std::vector<std::string> answer = {
200                                 "/dev/mapper/testdata is active.",
201                                 "  type:    n/a",
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",
208                                 "  mode:    read/write",
209                         };
210
211                         char buff[128] = {0, };
212                         for (auto &a : answer) {
213                                 if (fgets(buff, 128, fp) == nullptr) {
214                                         pclose(fp);
215                                         TEST_FAIL("Can't read cryptsetup status from buffer");
216                                 }
217
218                                 std::string ret = buff;
219                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
220
221                                 if (ret.compare(a)) {
222                                         pclose(fp);
223                                         std::cout << "expected: " << a << "\n";
224                                         std::cout << "real    : " << ret << "\n";
225                                         TEST_FAIL("Unexpected result from cryptsetup status");
226                                 }
227                         }
228                         pclose(fp);
229                 }
230
231                 // remove crypto device
232                 ode::destroyCryptoBlkDev("testdata");
233                 // check crypto device status using cryptsetup
234                 {
235                         FILE *fp = popen("cryptsetup status testdata", "r");
236                         if (!fp)
237                                 TEST_FAIL("Can't get cryptsetup status");
238
239                         std::vector<std::string> answer = {
240                                 "/dev/mapper/testdata is inactive.",
241                         };
242
243                         char buff[128] = {0, };
244                         for (auto &a : answer) {
245                                 if (fgets(buff, 128, fp) == nullptr) {
246                                         pclose(fp);
247                                         TEST_FAIL("Can't read cryptsetup status from buffer");
248                                 }
249
250                                 std::string ret = buff;
251                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
252
253                                 if (ret.compare(a)) {
254                                         pclose(fp);
255                                         std::cout << "expected: " << a << "\n";
256                                         std::cout << "real    : " << ret << "\n";
257                                         TEST_FAIL("Unexpected result from cryptsetup status");
258                                 }
259                         }
260                         pclose(fp);
261                 }
262         } catch (runtime::Exception &e) {
263                 TEST_FAIL(e.what());
264         }
265 }
266
267 TESTCASE(DMCryptSanitizeKey)
268 {
269         try {
270                 // case1. small size than minimum byte (32) for dmcrypt
271                 // -- expected: cause exception
272                 {
273                         const std::string keystring = "SMALLER_THAN_32BYTES______";
274                         const ode::DMCryptEngine::data keySmall32bit(keystring.begin(), keystring.end());
275                         try {
276                                 ode::sanitizeKey(keySmall32bit);
277                                 //
278                                 TEST_FAIL("small key should be cause exception");
279                         } catch (runtime::Exception &e) {
280                                 // expected result: it's good
281                         }
282                 }
283                 // case2. same size
284                 // -- expected: returned key length should be 32 byte.
285                 // -- expected: returned key data is same with 32 elements from first of big key
286                 {
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);
290
291                         // check size
292                         TEST_EXPECT(sanitized_key.size(), key32bit.size());
293
294                         // check data
295                         for (unsigned i = 0; i < sanitized_key.size(); i++) {
296                                 TEST_EXPECT(sanitized_key[i], key32bit[i]);
297                         }
298                 }
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
302                 {
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);
306
307                         // check size
308                         TEST_EXPECT(sanitized_key.size(), (unsigned)DMCRYPT_KEY_MIN_LEN_BYTE);
309
310                         // check data
311                         for (unsigned i = 0; i < sanitized_key.size(); i++) {
312                                 TEST_EXPECT(sanitized_key[i], keyBig[i]);
313                         }
314                 }
315                 //
316         } catch (runtime::Exception &e) {
317                 TEST_FAIL(e.what());
318         }
319 }
320
321 ////////////////////////////////////////////////////////////////////////////////////////
322 // TEST DMCryptEngine
323 ////////////////////////////////////////////////////////////////////////////////////////
324
325 TESTCASE(DMCryptGetPathTest)
326 {
327         try {
328                 ode::DMCryptEngine engine("/dev/mmcblk0p1", TEST_USERDATA_PATH, progress);
329                 if (engine.getSource() != "/dev/mmcblk0p1") {
330                         throw runtime::Exception("Source doen't match");
331                 }
332                 if (engine.getDestination() != "/opt/usr") {
333                         throw runtime::Exception("Destination doen't match");
334                 }
335         } catch (runtime::Exception &e) {
336                 TEST_FAIL(e.what());
337         }
338 }
339
340 TESTCASE(DMCryptEncryptAndDecrypt)
341 {
342         try {
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());
346
347                 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progress);
348                 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
349
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...
352                 {
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");
359                         }
360                 }
361                 // decyprt
362                 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
363
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)
367                 {
368                         int ret = ::mount(test_real_blkdev.c_str(), test_real_mntpoint.c_str(), "ext4", 0, 0);
369                         if (ret == -1) {
370                                 TEST_FAIL("Can't mount decyprted target");
371                         }
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)");
375                         }
376                         ::umount(test_real_mntpoint.c_str());
377                 }
378         } catch (runtime::Exception &e) {
379                 TEST_FAIL(e.what());
380         }
381 }
382
383 TESTCASE(DMCryptEncryptMountUnmountDecrypt)
384 {
385         try {
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());
389
390                 ode::DMCryptEngine engine(test_real_blkdev, test_real_mntpoint, progress);
391                 engine.encrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
392                 engine.mount(key32bit, 0);
393                 {
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");
397                         if (!fp)
398                                 TEST_FAIL("Can't get test file body");
399
400                         std::vector<std::string> answer = {
401                                 "DEF",
402                         };
403
404                         char buff[128] = {0, };
405                         for (auto &a : answer) {
406                                 if (fgets(buff, 128, fp) == nullptr) {
407                                         pclose(fp);
408                                         TEST_FAIL("Can't read test file body from buffer");
409                                 }
410
411                                 std::string ret = buff;
412                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
413
414                                 if (ret.compare(a)) {
415                                         pclose(fp);
416                                         std::cout << "expected: " << a << "\n";
417                                         std::cout << "real    : " << ret << "\n";
418                                         TEST_FAIL("Unexpected result from test file body");
419                                 }
420                         }
421                         pclose(fp);
422                 }
423                 engine.umount();
424                 engine.decrypt(key32bit, OPTION_INCLUDE_UNUSED_REGION);
425                 //
426         } catch (runtime::Exception &e) {
427                 TEST_FAIL(e.what());
428         }
429 }
430
431 TESTCASE(DMCryptFastEncryptMountUnmountFastDecrypt)
432 {
433         try {
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());
437
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);
441                 {
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");
445                         if (!fp)
446                                 TEST_FAIL("Can't get test file body");
447
448                         std::vector<std::string> answer = {
449                                 "DEF",
450                         };
451
452                         char buff[128] = {0, };
453                         for (auto &a : answer) {
454                                 if (fgets(buff, 128, fp) == nullptr) {
455                                         pclose(fp);
456                                         TEST_FAIL("Can't read test file body from buffer");
457                                 }
458
459                                 std::string ret = buff;
460                                 ret.erase(std::remove(ret.begin(), ret.end(), '\n'), ret.end());
461
462                                 if (ret.compare(a)) {
463                                         pclose(fp);
464                                         std::cout << "expected: " << a << "\n";
465                                         std::cout << "real    : " << ret << "\n";
466                                         TEST_FAIL("Unexpected result from test file body");
467                                 }
468                         }
469                         pclose(fp);
470                 }
471                 engine.umount();
472                 engine.decrypt(key32bit, (~OPTION_INCLUDE_UNUSED_REGION)); // disable fast-encryption
473                 //
474         } catch (runtime::Exception &e) {
475                 TEST_FAIL(e.what());
476         }
477 }
478
479 ////////////////////////////////////////////////////////////////////////////////////////
480 // TEST Deinitializer
481 ////////////////////////////////////////////////////////////////////////////////////////
482
483 TESTCASE(DMCryptTestDeInit)
484 {
485         std::string cmd = "/usr/sbin/losetup -d ";
486         cmd += test_real_blkdev;
487         if (system(cmd.c_str())) {}
488
489         if (::unlink(test_real_rawfile.c_str()) == -1)
490                 ::unlink(test_real_rawfile.c_str());
491
492         cmd = "/usr/bin/rm -rf ";
493         cmd += test_real_mntpoint;
494         if (system(cmd.c_str())) {}
495 }