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