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