1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <fstream>
22 #include <iostream>
23 #include <string>
24
25 #include "common.h"
26 #include "file_ex.h"
27 #include "rdb_errno.h"
28 #include "rdb_helper.h"
29 #include "rdb_open_callback.h"
30 #include "rdb_security_manager.h"
31 #include "sqlite_utils.h"
32
33 using namespace testing::ext;
34 using namespace OHOS::NativeRdb;
35 class RdbRekeyTest : public testing::Test {
36 public:
37 static void SetUpTestCase();
38 static void TearDownTestCase();
39 void SetUp() override;
40 void TearDown() override;
41
42 static std::string RemoveSuffix(const std::string &name);
43 static std::chrono::system_clock::time_point GetKeyFileDate(const std::string &dbName);
44 static bool ChangeKeyFileDate(const std::string &dbName, int rep);
45 static bool SaveNewKey(const std::string &dbName);
46 static RdbStoreConfig GetRdbConfig(const std::string &name);
47 static RdbStoreConfig GetRdbNotRekeyConfig(const std::string &name);
48 static void InsertData(std::shared_ptr<RdbStore> &store);
49 static void CheckQueryData(std::shared_ptr<RdbStore> &store);
50
51 static const std::string encryptedDatabaseName;
52 static const std::string encryptedDatabasePath;
53 static const std::string encryptedDatabaseKeyDir;
54 static const std::string encryptedDatabaseMockName;
55 static const std::string encryptedDatabaseMockPath;
56 static constexpr int HOURS_EXPIRED = (24 * 365) + 1;
57 static constexpr int HOURS_LONG_LONG_AGO = 30 * (24 * 365);
58 static constexpr int HOURS_NOT_EXPIRED = (24 * 30);
59 };
60
61 const std::string RdbRekeyTest::encryptedDatabaseName = "encrypted.db";
62 const std::string RdbRekeyTest::encryptedDatabasePath = RDB_TEST_PATH + encryptedDatabaseName;
63 const std::string RdbRekeyTest::encryptedDatabaseKeyDir = RDB_TEST_PATH + "key/";
64 const std::string RdbRekeyTest::encryptedDatabaseMockName = "encrypted_mock.db";
65 const std::string RdbRekeyTest::encryptedDatabaseMockPath = RDB_TEST_PATH + encryptedDatabaseMockName;
66
67 class RekeyTestOpenCallback : public RdbOpenCallback {
68 public:
69 int OnCreate(RdbStore &store) override;
70 int OnUpgrade(RdbStore &store, int oldVersion, int newVersion) override;
71 static const std::string createTableTest;
72 };
73
74 std::string const RekeyTestOpenCallback::createTableTest = "CREATE TABLE IF NOT EXISTS test "
75 "(id INTEGER PRIMARY KEY "
76 "AUTOINCREMENT, "
77 "name TEXT NOT NULL, age INTEGER, "
78 "salary "
79 "REAL, blobType BLOB)";
80
OnCreate(RdbStore & store)81 int RekeyTestOpenCallback::OnCreate(RdbStore &store)
82 {
83 return store.ExecuteSql(createTableTest);
84 }
85
OnUpgrade(RdbStore & store,int oldVersion,int newVersion)86 int RekeyTestOpenCallback::OnUpgrade(RdbStore &store, int oldVersion, int newVersion)
87 {
88 return E_OK;
89 }
90
SetUpTestCase()91 void RdbRekeyTest::SetUpTestCase()
92 {
93 }
94
TearDownTestCase()95 void RdbRekeyTest::TearDownTestCase()
96 {
97 }
98
SetUp()99 void RdbRekeyTest::SetUp()
100 {
101 RdbHelper::ClearCache();
102 RdbHelper::DeleteRdbStore(RdbRekeyTest::encryptedDatabasePath);
103 RdbStoreConfig config = GetRdbConfig(encryptedDatabasePath);
104 RekeyTestOpenCallback helper;
105 int errCode;
106 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
107 EXPECT_NE(store, nullptr);
108 InsertData(store);
109 store.reset();
110 RdbHelper::ClearCache();
111 }
112
TearDown()113 void RdbRekeyTest::TearDown()
114 {
115 RdbHelper::ClearCache();
116 RdbHelper::DeleteRdbStore(RdbRekeyTest::encryptedDatabasePath);
117 }
118
RemoveSuffix(const std::string & name)119 std::string RdbRekeyTest::RemoveSuffix(const std::string &name)
120 {
121 std::string suffix(".db");
122 auto pos = name.rfind(suffix);
123 if (pos == std::string::npos || pos < name.length() - suffix.length()) {
124 return name;
125 }
126 return { name, 0, pos };
127 }
128
GetKeyFileDate(const std::string & dbName)129 std::chrono::system_clock::time_point RdbRekeyTest::GetKeyFileDate(const std::string &dbName)
130 {
131 std::chrono::system_clock::time_point timePoint;
132 std::string name = RemoveSuffix(dbName);
133 auto keyPath = RDB_TEST_PATH + "key/" + name + ".pub_key";
134 if (!OHOS::FileExists(keyPath)) {
135 return timePoint;
136 }
137 std::vector<char> content;
138 auto loaded = OHOS::LoadBufferFromFile(keyPath, content);
139 if (!loaded) {
140 return timePoint;
141 }
142 auto iter = content.begin();
143 iter++;
144 constexpr uint32_t dateFileLength = sizeof(time_t) / sizeof(uint8_t);
145 std::vector<uint8_t> date;
146 date.assign(iter, iter + dateFileLength);
147 timePoint = std::chrono::system_clock::from_time_t(*reinterpret_cast<time_t *>(const_cast<uint8_t *>(&date[0])));
148 return timePoint;
149 }
150
ChangeKeyFileDate(const std::string & dbName,int rep)151 bool RdbRekeyTest::ChangeKeyFileDate(const std::string &dbName, int rep)
152 {
153 std::string name = RemoveSuffix(dbName);
154 auto keyPath = RDB_TEST_PATH + "key/" + name + ".pub_key";
155 if (!OHOS::FileExists(keyPath)) {
156 return false;
157 }
158 std::vector<char> content;
159 auto loaded = OHOS::LoadBufferFromFile(keyPath, content);
160 if (!loaded) {
161 return false;
162 }
163 auto time =
164 std::chrono::system_clock::to_time_t(std::chrono::system_clock::system_clock::now() - std::chrono::hours(rep));
165 std::vector<char> date(reinterpret_cast<uint8_t *>(&time), reinterpret_cast<uint8_t *>(&time) + sizeof(time));
166 std::copy(date.begin(), date.end(), ++content.begin());
167
168 auto saved = OHOS::SaveBufferToFile(keyPath, content);
169 return saved;
170 }
171
SaveNewKey(const string & dbName)172 bool RdbRekeyTest::SaveNewKey(const string &dbName)
173 {
174 std::string name = RemoveSuffix(dbName);
175 auto keyPath = RDB_TEST_PATH + "key/" + name + ".pub_key";
176 auto newKeyPath = RDB_TEST_PATH + "key/" + name + ".pub_key.new";
177 if (!OHOS::FileExists(keyPath)) {
178 return false;
179 }
180 std::vector<char> content;
181 auto loaded = OHOS::LoadBufferFromFile(keyPath, content);
182 if (!loaded) {
183 return false;
184 }
185 OHOS::SaveBufferToFile(newKeyPath, content);
186 content[content.size() - 1] = 'E';
187 return OHOS::SaveBufferToFile(keyPath, content);
188 }
189
GetRdbConfig(const std::string & name)190 RdbStoreConfig RdbRekeyTest::GetRdbConfig(const std::string &name)
191 {
192 RdbStoreConfig config(name);
193 config.SetEncryptStatus(true);
194 config.SetBundleName("com.example.test_rekey");
195 config.EnableRekey(true);
196 return config;
197 }
198
GetRdbNotRekeyConfig(const std::string & name)199 RdbStoreConfig RdbRekeyTest::GetRdbNotRekeyConfig(const std::string &name)
200 {
201 RdbStoreConfig config(name);
202 config.SetEncryptStatus(true);
203 config.SetBundleName("com.example.test_rekey");
204 config.EnableRekey(false);
205 return config;
206 }
207
InsertData(std::shared_ptr<RdbStore> & store)208 void RdbRekeyTest::InsertData(std::shared_ptr<RdbStore> &store)
209 {
210 int64_t id;
211 ValuesBucket values;
212 std::string name = "zhangsan";
213 int age = 18;
214 double salary = 100.5;
215 std::vector<uint8_t> blob{ 1, 2, 3 };
216 values.PutString("name", name);
217 values.PutInt("age", age);
218 values.PutDouble("salary", salary);
219 values.PutBlob("blobType", blob);
220 int insertRet = store->Insert(id, "test", values);
221 EXPECT_EQ(insertRet, E_OK);
222 }
223
CheckQueryData(std::shared_ptr<RdbStore> & store)224 void RdbRekeyTest::CheckQueryData(std::shared_ptr<RdbStore> &store)
225 {
226 std::shared_ptr<ResultSet> resultSet =
227 store->QuerySql("SELECT * FROM test WHERE name = ?", std::vector<std::string>{ "zhangsan" });
228 EXPECT_NE(resultSet, nullptr);
229 int result = resultSet->GoToFirstRow();
230 EXPECT_EQ(result, E_OK);
231 int columnIndex;
232 std::string strVal;
233 ColumnType columnType;
234 result = resultSet->GetColumnIndex("name", columnIndex);
235 EXPECT_EQ(result, E_OK);
236 result = resultSet->GetColumnType(columnIndex, columnType);
237 EXPECT_EQ(result, E_OK);
238 EXPECT_EQ(columnType, ColumnType::TYPE_STRING);
239 result = resultSet->GetString(columnIndex, strVal);
240 EXPECT_EQ(result, E_OK);
241 EXPECT_EQ("zhangsan", strVal);
242
243 result = resultSet->Close();
244 EXPECT_EQ(result, E_OK);
245 }
246
247 /**
248 * @tc.name: Rdb_Rekey_Test_001
249 * @tc.desc: test RdbStore rekey function
250 * @tc.type: FUNC
251 */
252 HWTEST_F(RdbRekeyTest, Rdb_Rekey_01, TestSize.Level1)
253 {
254 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
255 std::string newKeyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key.new";
256
257 bool isFileExists = OHOS::FileExists(keyPath);
258 ASSERT_TRUE(isFileExists);
259
260 bool isFileDateChanged = ChangeKeyFileDate(encryptedDatabaseName, RdbRekeyTest::HOURS_EXPIRED);
261 ASSERT_TRUE(isFileDateChanged);
262
263 auto changedDate = GetKeyFileDate(encryptedDatabaseName);
264 ASSERT_TRUE(std::chrono::system_clock::now() - changedDate > std::chrono::hours(RdbRekeyTest::HOURS_EXPIRED));
265
266 RdbStoreConfig config = GetRdbConfig(RdbRekeyTest::encryptedDatabasePath);
267 RekeyTestOpenCallback helper;
268 int errCode = E_OK;
269 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
270 ASSERT_NE(store, nullptr);
271 ASSERT_EQ(errCode, E_OK);
272
273 isFileExists = OHOS::FileExists(keyPath);
274 ASSERT_TRUE(isFileExists);
275 isFileExists = OHOS::FileExists(newKeyPath);
276 ASSERT_FALSE(isFileExists);
277
278 auto newDate = GetKeyFileDate(encryptedDatabaseName);
279 ASSERT_TRUE(std::chrono::system_clock::now() - newDate < std::chrono::seconds(2));
280 CheckQueryData(store);
281 }
282
283 /**
284 * @tc.name: Rdb_Rekey_Test_002
285 * @tc.desc: test RdbStore with not outdated password
286 * @tc.type: FUNC
287 */
288 HWTEST_F(RdbRekeyTest, Rdb_Rekey_02, TestSize.Level1)
289 {
290 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
291 bool isFileExists = OHOS::FileExists(keyPath);
292 ASSERT_TRUE(isFileExists);
293
294 bool isFileDateChanged = ChangeKeyFileDate(encryptedDatabaseName, RdbRekeyTest::HOURS_NOT_EXPIRED);
295 ASSERT_TRUE(isFileDateChanged);
296
297 auto changedDate = GetKeyFileDate(encryptedDatabaseName);
298 ASSERT_TRUE(std::chrono::system_clock::now() - changedDate > std::chrono::hours(RdbRekeyTest::HOURS_NOT_EXPIRED));
299
300 RdbStoreConfig config = GetRdbConfig(RdbRekeyTest::encryptedDatabasePath);
301 RekeyTestOpenCallback helper;
302 int errCode = E_OK;
303 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
304 ASSERT_NE(store, nullptr);
305 ASSERT_EQ(errCode, E_OK);
306 CheckQueryData(store);
307 }
308
309 /**
310 * @tc.name: Rdb_Rekey_Test_003
311 * @tc.desc: try to open store and execute RekeyRecover() without key and new key files.
312 * @tc.type: FUNC
313 */
314 HWTEST_F(RdbRekeyTest, Rdb_Rekey_03, TestSize.Level1)
315 {
316 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
317 std::string newKeyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key.new";
318
319 bool isFileExists = OHOS::FileExists(keyPath);
320 ASSERT_TRUE(isFileExists);
321
322 SqliteUtils::DeleteFile(keyPath);
323 isFileExists = OHOS::FileExists(keyPath);
324 ASSERT_FALSE(isFileExists);
325 isFileExists = OHOS::FileExists(newKeyPath);
326 ASSERT_FALSE(isFileExists);
327
328 RekeyTestOpenCallback helper;
329 int errCode = E_OK;
330 RdbStoreConfig config = GetRdbConfig(encryptedDatabasePath);
331 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
332 ASSERT_NE(store, nullptr);
333 ASSERT_EQ(errCode, E_OK);
334 store = nullptr;
335 store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
336 ASSERT_NE(store, nullptr);
337 ASSERT_EQ(errCode, E_OK);
338 }
339
340 /**
341 * @tc.name: Rdb_Rekey_Test_004
342 * @tc.desc: try to open store and modify create date to a future time.
343 * @tc.type: FUNC
344 */
345 HWTEST_F(RdbRekeyTest, Rdb_Rekey_04, TestSize.Level1)
346 {
347 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
348 std::string newKeyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key.new";
349
350 bool isFileExists = OHOS::FileExists(keyPath);
351 ASSERT_TRUE(isFileExists);
352
353 auto keyFileDate = GetKeyFileDate(encryptedDatabaseName);
354
355 bool isFileDateChanged = ChangeKeyFileDate(encryptedDatabaseName, -RdbRekeyTest::HOURS_EXPIRED);
356 ASSERT_TRUE(isFileDateChanged);
357
358 auto changedDate = GetKeyFileDate(encryptedDatabaseName);
359 ASSERT_GT(changedDate, keyFileDate);
360
361 RdbStoreConfig config = GetRdbConfig(RdbRekeyTest::encryptedDatabasePath);
362 RekeyTestOpenCallback helper;
363 int errCode;
364 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
365 ASSERT_NE(store, nullptr);
366
367 isFileExists = OHOS::FileExists(keyPath);
368 ASSERT_TRUE(isFileExists);
369 isFileExists = OHOS::FileExists(newKeyPath);
370 ASSERT_FALSE(isFileExists);
371
372 keyFileDate = GetKeyFileDate(encryptedDatabaseName);
373 ASSERT_EQ(changedDate, keyFileDate);
374
375 CheckQueryData(store);
376 }
377
378 /**
379 * @tc.name: Rdb_Rekey_RenameFailed_05
380 * @tc.desc: re key and rename failed the new key file.
381 * @tc.type: FUNC
382 */
383 HWTEST_F(RdbRekeyTest, Rdb_Rekey_RenameFailed_05, TestSize.Level1)
384 {
385 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
386 std::string newKeyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key.new";
387
388 bool isFileExists = OHOS::FileExists(keyPath);
389 ASSERT_TRUE(isFileExists);
390
391 auto keyFileDate = GetKeyFileDate(encryptedDatabaseName);
392
393 bool isFileDateChanged = ChangeKeyFileDate(encryptedDatabaseName, RdbRekeyTest::HOURS_LONG_LONG_AGO);
394 ASSERT_TRUE(isFileDateChanged);
395
396 auto changedDate = GetKeyFileDate(encryptedDatabaseName);
397 ASSERT_GT(keyFileDate, changedDate);
398
399 RdbStoreConfig config = GetRdbConfig(RdbRekeyTest::encryptedDatabasePath);
400 RekeyTestOpenCallback helper;
401 int errCode = E_OK;
402 for (int i = 0; i < 50; ++i) {
403 auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
404 ASSERT_NE(store, nullptr);
405 ASSERT_EQ(errCode, E_OK);
406 store = nullptr;
407 SaveNewKey(encryptedDatabaseName);
408 }
409
410 auto store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
411 CheckQueryData(store);
412 }
413
414 /**
415 * @tc.name: Rdb_Delete_Rekey_Test_06
416 * @tc.desc: test RdbStore rekey function
417 * @tc.type: FUNC
418 */
419 HWTEST_F(RdbRekeyTest, Rdb_Rekey_06, TestSize.Level1)
420 {
421 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
422 std::string newKeyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key.new";
423
424 bool isFileExists = OHOS::FileExists(keyPath);
425 ASSERT_TRUE(isFileExists);
426
427 bool isFileDateChanged = ChangeKeyFileDate(encryptedDatabaseName, RdbRekeyTest::HOURS_EXPIRED);
428 ASSERT_TRUE(isFileDateChanged);
429
430 auto changedDate = GetKeyFileDate(encryptedDatabaseName);
431 ASSERT_TRUE(std::chrono::system_clock::now() - changedDate > std::chrono::hours(RdbRekeyTest::HOURS_EXPIRED));
432
433 RdbStoreConfig config = GetRdbNotRekeyConfig(RdbRekeyTest::encryptedDatabasePath);
434 RekeyTestOpenCallback helper;
435 int errCode = E_OK;
436 std::shared_ptr<RdbStore> store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
437 ASSERT_NE(store, nullptr);
438 ASSERT_EQ(errCode, E_OK);
439
440 isFileExists = OHOS::FileExists(keyPath);
441 ASSERT_TRUE(isFileExists);
442 isFileExists = OHOS::FileExists(newKeyPath);
443 ASSERT_FALSE(isFileExists);
444
445 ASSERT_TRUE(std::chrono::system_clock::now() - changedDate > std::chrono::hours(RdbRekeyTest::HOURS_EXPIRED));
446 CheckQueryData(store);
447 }
448
449 /**
450 @tc.name: Rdb_Delete_Rekey_Test_07
451 @tc.desc: test deleting the key file of the encrypted database
452 @tc.type: FUNC
453 */
454 HWTEST_F(RdbRekeyTest, Rdb_Rekey_07, TestSize.Level1)
455 {
456 RdbStoreConfig config(RdbRekeyTest::encryptedDatabasePath);
457 config.SetSecurityLevel(SecurityLevel::S1);
458 config.SetAllowRebuild(true);
459 config.SetEncryptStatus(true);
460 config.SetBundleName("com.example.test_rekey");
461 RekeyTestOpenCallback helper;
462 int errCode = E_OK;
463 std::shared_ptr store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
464 ASSERT_NE(store, nullptr);
465 ASSERT_EQ(errCode, E_OK);
466 std::string keyPath = encryptedDatabaseKeyDir + RemoveSuffix(encryptedDatabaseName) + ".pub_key";
467 bool isFileExists = OHOS::FileExists(keyPath);
468 ASSERT_TRUE(isFileExists);
469 struct stat fileStat;
470 ino_t inodeNumber1 = -1;
471 if (stat(keyPath.c_str(), &fileStat) == 0) {
472 inodeNumber1 = fileStat.st_ino;
473 }
474 store = nullptr;
475 {
476 std::ofstream fsDb(encryptedDatabasePath, std::ios_base::binary | std::ios_base::out);
477 fsDb.seekp(64);
478 fsDb.write("hello", 5);
479 fsDb.close();
480 }
481 store = RdbHelper::GetRdbStore(config, 1, helper, errCode);
482 isFileExists = OHOS::FileExists(keyPath);
483 ASSERT_TRUE(isFileExists);
484 ino_t inodeNumber2 = -1;
485 if (stat(keyPath.c_str(), &fileStat) == 0) {
486 inodeNumber2 = fileStat.st_ino;
487 }
488 ASSERT_NE(inodeNumber1, inodeNumber2);
489 }