1 /*
2 * Copyright (c) 2022 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 #include "store_factory.h"
16
17 #include <cerrno>
18 #include <cstdio>
19 #include <gtest/gtest.h>
20 #include <sys/types.h>
21 #include <vector>
22
23 #include "backup_manager.h"
24 #include "file_ex.h"
25 #include "store_manager.h"
26 #include "store_util.h"
27 #include "sys/stat.h"
28 #include "types.h"
29 namespace {
30 using namespace testing::ext;
31 using namespace OHOS::DistributedKv;
32
33 static StoreId storeId = { "single_test" };
34 static AppId appId = { "rekey" };
35 static Options options = {
36 .encrypt = true,
37 .securityLevel = S1,
38 .area = EL1,
39 .kvStoreType = SINGLE_VERSION,
40 .baseDir = "/data/service/el1/public/database/rekey",
41 };
42
43 class StoreFactoryTest : public testing::Test {
44 public:
45 using DBStore = DistributedDB::KvStoreNbDelegate;
46 using DBManager = DistributedDB::KvStoreDelegateManager;
47 using DBOption = DistributedDB::KvStoreNbDelegate::Option;
48 using DBPassword = SecurityManager::DBPassword;
49 using DBStatus = DistributedDB::DBStatus;
50
51 static constexpr int OUTDATED_TIME = (24 * 500);
52 static constexpr int NOT_OUTDATED_TIME = (24 * 50);
53
54 static void SetUpTestCase(void);
55 static void TearDownTestCase(void);
56 void SetUp();
57 void TearDown();
58
59 std::chrono::system_clock::time_point GetDate(const std::string &name, const std::string &path);
60 bool ChangeKeyDate(const std::string &name, const std::string &path, int duration);
61 bool MoveToRekeyPath(Options options, StoreId storeId);
62 std::shared_ptr<StoreFactoryTest::DBManager> GetDBManager(const std::string &path, const AppId &appId);
63 DBOption GetOption(const Options &options, const DBPassword &dbPassword);
64 DBStatus ChangeKVStoreDate(const std::string &storeId, std::shared_ptr<DBManager> dbManager,
65 const Options &options, DBPassword &dbPassword, int time);
66 bool ModifyDate(int time);
67 static void DeleteKVStore();
68 };
69
SetUpTestCase(void)70 void StoreFactoryTest::SetUpTestCase(void)
71 {
72 }
73
TearDownTestCase(void)74 void StoreFactoryTest::TearDownTestCase(void)
75 {
76 }
77
SetUp(void)78 void StoreFactoryTest::SetUp(void)
79 {
80 std::string baseDir = "/data/service/el1/public/database/rekey";
81 mkdir(baseDir.c_str(), (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH));
82 }
83
TearDown(void)84 void StoreFactoryTest::TearDown(void)
85 {
86 DeleteKVStore();
87 (void)remove("/data/service/el1/public/database/rekey");
88 }
89
DeleteKVStore()90 void StoreFactoryTest::DeleteKVStore()
91 {
92 StoreManager::GetInstance().Delete(appId, storeId, options.baseDir);
93 }
94
GetDate(const std::string & name,const std::string & path)95 std::chrono::system_clock::time_point StoreFactoryTest::GetDate(const std::string &name, const std::string &path)
96 {
97 std::chrono::system_clock::time_point timePoint;
98 auto keyPath = path + "/key/" + name + ".key";
99 if (!OHOS::FileExists(keyPath)) {
100 return timePoint;
101 }
102
103 std::vector<char> content;
104 auto loaded = OHOS::LoadBufferFromFile(keyPath, content);
105 if (!loaded) {
106 return timePoint;
107 }
108 constexpr uint32_t DATE_FILE_OFFSET = 1;
109 constexpr uint32_t DATE_FILE_LENGTH = sizeof(time_t) / sizeof(uint8_t);
110 std::vector<uint8_t> date;
111 date.assign(content.begin() + DATE_FILE_OFFSET, content.begin() + DATE_FILE_LENGTH + DATE_FILE_OFFSET);
112 timePoint = std::chrono::system_clock::from_time_t(*reinterpret_cast<time_t *>(const_cast<uint8_t *>(&date[0])));
113 return timePoint;
114 }
115
ChangeKeyDate(const std::string & name,const std::string & path,int duration)116 bool StoreFactoryTest::ChangeKeyDate(const std::string &name, const std::string &path, int duration)
117 {
118 auto keyPath = path + "/key/" + name + ".key";
119 if (!OHOS::FileExists(keyPath)) {
120 return false;
121 }
122
123 std::vector<char> content;
124 auto loaded = OHOS::LoadBufferFromFile(keyPath, content);
125 if (!loaded) {
126 return false;
127 }
128 auto time = std::chrono::system_clock::to_time_t(
129 std::chrono::system_clock::system_clock::now() - std::chrono::hours(duration));
130 std::vector<char> date(reinterpret_cast<uint8_t *>(&time), reinterpret_cast<uint8_t *>(&time) + sizeof(time));
131 std::copy(date.begin(), date.end(), ++content.begin());
132
133 auto saved = OHOS::SaveBufferToFile(keyPath, content);
134 return saved;
135 }
136
MoveToRekeyPath(Options options,StoreId storeId)137 bool StoreFactoryTest::MoveToRekeyPath(Options options, StoreId storeId)
138 {
139 std::string keyFileName = options.baseDir + "/key/" + storeId.storeId + ".key";
140 std::string rekeyFileName = options.baseDir + "/rekey/key/" + storeId.storeId + ".new.key";
141 bool result = StoreUtil::Rename(keyFileName, rekeyFileName);
142 if (!result) {
143 return false;
144 }
145 result = StoreUtil::Remove(keyFileName);
146 return result;
147 }
148
GetDBManager(const std::string & path,const AppId & appId)149 std::shared_ptr<StoreFactoryTest::DBManager> StoreFactoryTest::GetDBManager(const std::string &path, const AppId &appId)
150 {
151 std::string fullPath = path + "/kvdb";
152 StoreUtil::InitPath(fullPath);
153 std::shared_ptr<DBManager> dbManager = std::make_shared<DBManager>(appId.appId, "default");
154 dbManager->SetKvStoreConfig({ fullPath });
155 BackupManager::GetInstance().Init(path);
156 return dbManager;
157 }
158
GetOption(const Options & options,const DBPassword & dbPassword)159 StoreFactoryTest::DBOption StoreFactoryTest::GetOption(const Options &options, const DBPassword &dbPassword)
160 {
161 DBOption dbOption;
162 dbOption.syncDualTupleMode = true; // tuple of (appid+storeid)
163 dbOption.createIfNecessary = options.createIfMissing;
164 dbOption.isNeedRmCorruptedDb = options.rebuild;
165 dbOption.isMemoryDb = (!options.persistent);
166 dbOption.isEncryptedDb = options.encrypt;
167 if (options.encrypt) {
168 dbOption.cipher = DistributedDB::CipherType::AES_256_GCM;
169 dbOption.passwd = dbPassword.password;
170 }
171
172 dbOption.conflictResolvePolicy = options.kvStoreType == KvStoreType::SINGLE_VERSION
173 ? DistributedDB::LAST_WIN
174 : DistributedDB::DEVICE_COLLABORATION;
175
176 dbOption.schema = options.schema;
177 dbOption.createDirByStoreIdOnly = true;
178 dbOption.secOption = StoreUtil::GetDBSecurity(options.securityLevel);
179 return dbOption;
180 }
181
ChangeKVStoreDate(const std::string & storeId,std::shared_ptr<DBManager> dbManager,const Options & options,DBPassword & dbPassword,int time)182 StoreFactoryTest::DBStatus StoreFactoryTest::ChangeKVStoreDate(const std::string &storeId,
183 std::shared_ptr<DBManager> dbManager, const Options &options, DBPassword &dbPassword, int time)
184 {
185 DBStatus status;
186 const auto dbOption = GetOption(options, dbPassword);
187 DBStore *store = nullptr;
188 dbManager->GetKvStore(storeId, dbOption, [&status, &store](auto dbStatus, auto *dbStore) {
189 status = dbStatus;
190 store = dbStore;
191 });
192 if (!ChangeKeyDate(storeId, options.baseDir, time)) {
193 std::cout << "failed" << std::endl;
194 }
195 dbPassword = SecurityManager::GetInstance().GetDBPassword(storeId, options.baseDir, false);
196 auto dbStatus = store->Rekey(dbPassword.password);
197 dbManager->CloseKvStore(store);
198 return dbStatus;
199 }
200
ModifyDate(int time)201 bool StoreFactoryTest::ModifyDate(int time)
202 {
203 auto dbPassword = SecurityManager::GetInstance().GetDBPassword(storeId, options.baseDir, false);
204 auto dbManager = GetDBManager(options.baseDir, appId);
205 auto dbstatus = ChangeKVStoreDate(storeId, dbManager, options, dbPassword, time);
206 return StoreUtil::ConvertStatus(dbstatus) == SUCCESS;
207 }
208
209 /**
210 * @tc.name: Rekey
211 * @tc.desc: test rekey function
212 * @tc.type: FUNC
213 * @tc.require:
214 * @tc.author: Cui Renjie
215 */
216 HWTEST_F(StoreFactoryTest, Rekey, TestSize.Level1)
217 {
218 Status status = DB_ERROR;
219 options.autoRekey = true;
220 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
221 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
222 ASSERT_EQ(status, SUCCESS);
223
224 ASSERT_TRUE(ModifyDate(OUTDATED_TIME));
225
226 auto oldKeyTime = GetDate(storeId, options.baseDir);
227 ASSERT_FALSE(std::chrono::system_clock::now() - oldKeyTime < std::chrono::seconds(2));
228
229 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
230 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
231 ASSERT_EQ(status, SUCCESS);
232
233 auto newKeyTime = GetDate(storeId, options.baseDir);
234 ASSERT_TRUE(std::chrono::system_clock::now() - newKeyTime < std::chrono::seconds(2));
235 }
236
237 /**
238 * @tc.name: RekeyNotOutdated
239 * @tc.desc: try to rekey kvstore with not outdated password
240 * @tc.type: FUNC
241 * @tc.require:
242 * @tc.author: Cui Renjie
243 */
244 HWTEST_F(StoreFactoryTest, RekeyNotOutdated, TestSize.Level1)
245 {
246 Status status = DB_ERROR;
247 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
248 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
249 ASSERT_EQ(status, SUCCESS);
250
251 ASSERT_TRUE(ModifyDate(NOT_OUTDATED_TIME));
252 auto oldKeyTime = GetDate(storeId, options.baseDir);
253
254 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
255 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
256 ASSERT_EQ(status, SUCCESS);
257
258 auto newKeyTime = GetDate(storeId, options.baseDir);
259 ASSERT_EQ(oldKeyTime, newKeyTime);
260 }
261
262 /**
263 * @tc.name: RekeyInterrupted0
264 * @tc.desc: mock the situation that open store after rekey was interrupted last time,
265 * which caused key file lost but rekey key file exist.
266 * @tc.type: FUNC
267 * @tc.require:
268 * @tc.author: Cui Renjie
269 */
270 HWTEST_F(StoreFactoryTest, RekeyInterruptedWhileChangeKeyFile, TestSize.Level1)
271 {
272 Status status = DB_ERROR;
273 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
274 ASSERT_EQ(status, SUCCESS);
275 auto oldKeyTime = GetDate(storeId, options.baseDir);
276
277 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
278 ASSERT_EQ(status, SUCCESS);
279 ASSERT_TRUE(MoveToRekeyPath(options, storeId));
280
281 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
282 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
283 ASSERT_EQ(status, SUCCESS);
284 std::string keyFileName = options.baseDir + "/key/" + storeId.storeId + ".key";
285 auto isKeyExist = StoreUtil::IsFileExist(keyFileName);
286 ASSERT_TRUE(isKeyExist);
287
288 auto newKeyTime = GetDate(storeId, options.baseDir);
289 ASSERT_TRUE(newKeyTime - oldKeyTime < std::chrono::seconds(2));
290 }
291
292 /**
293 * @tc.name: RekeyInterrupted1
294 * @tc.desc: mock the situation that open store after rekey was interrupted last time,
295 * which caused key file not changed but rekey key file exist.
296 * @tc.type: FUNC
297 * @tc.require:
298 * @tc.author: Cui Renjie
299 */
300 HWTEST_F(StoreFactoryTest, RekeyInterruptedBeforeChangeKeyFile, TestSize.Level1)
301 {
302 Status status = DB_ERROR;
303 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
304 ASSERT_EQ(status, SUCCESS);
305 auto oldKeyTime = GetDate(storeId, options.baseDir);
306
307 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
308 ASSERT_EQ(status, SUCCESS);
309 ASSERT_EQ(MoveToRekeyPath(options, storeId), true);
310
311 StoreId newStoreId = { "newStore" };
312 StoreManager::GetInstance().GetKVStore(appId, newStoreId, options, status);
313
314 std::string keyFileName = options.baseDir + "/key/" + storeId.storeId + ".key";
315 std::string mockKeyFileName = options.baseDir + "/key/" + newStoreId.storeId + ".key";
316 StoreUtil::Rename(mockKeyFileName, keyFileName);
317 StoreUtil::Remove(mockKeyFileName);
318 auto isKeyExist = StoreUtil::IsFileExist(mockKeyFileName);
319 ASSERT_FALSE(isKeyExist);
320 isKeyExist = StoreUtil::IsFileExist(keyFileName);
321 ASSERT_TRUE(isKeyExist);
322
323 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
324 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
325 ASSERT_EQ(status, SUCCESS);
326
327 isKeyExist = StoreUtil::IsFileExist(keyFileName);
328 ASSERT_TRUE(isKeyExist);
329
330 auto newKeyTime = GetDate(storeId, options.baseDir);
331 ASSERT_TRUE(newKeyTime - oldKeyTime < std::chrono::seconds(2));
332 }
333
334 /**
335 * @tc.name: RekeyNoPwdFile
336 * @tc.desc: try to open kvstore and execute RekeyRecover() without key and rekey key files.
337 * @tc.type: FUNC
338 * @tc.require:
339 * @tc.author: Cui Renjie
340 */
341 HWTEST_F(StoreFactoryTest, RekeyNoPwdFile, TestSize.Level1)
342 {
343 Status status = DB_ERROR;
344 options.autoRekey = false;
345 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
346 ASSERT_EQ(status, SUCCESS);
347
348 status = StoreManager::GetInstance().CloseKVStore(appId, storeId);
349 ASSERT_EQ(status, SUCCESS);
350 std::string keyFileName = options.baseDir + "/key/" + storeId.storeId + ".key";
351 StoreUtil::Remove(keyFileName);
352
353 auto isKeyExist = StoreUtil::IsFileExist(keyFileName);
354 ASSERT_EQ(isKeyExist, false);
355
356 StoreManager::GetInstance().GetKVStore(appId, storeId, options, status);
357 ASSERT_EQ(status, SUCCESS);
358
359 isKeyExist = StoreUtil::IsFileExist(keyFileName);
360 ASSERT_EQ(isKeyExist, true);
361 }
362 }