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