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