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 }