1 /*
2 * Copyright (c) 2021 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 "base_key.h"
16
17 #include <fcntl.h>
18 #include <fstream>
19 #include <iostream>
20 #include <string>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #include "directory_ex.h"
26 #include "file_ex.h"
27 #include "huks_master.h"
28 #include "storage_service_log.h"
29 #include "string_ex.h"
30 #include "utils/file_utils.h"
31
32 namespace {
33 const std::string PATH_LATEST_BACKUP = "/latest_bak";
34 const std::string PATH_KEY_VERSION = "/version_";
35 const std::string PATH_KEY_TEMP = "/temp";
36 }
37
38 namespace OHOS {
39 namespace StorageDaemon {
40 static bool g_isHuksMasterInit = false;
BaseKey(std::string dir,uint8_t keyLen)41 BaseKey::BaseKey(std::string dir, uint8_t keyLen) : dir_(dir), keyLen_(keyLen)
42 {
43 if (!g_isHuksMasterInit && (HuksMaster::Init() == 0)) {
44 g_isHuksMasterInit = true;
45 }
46 }
47
InitKey()48 bool BaseKey::InitKey()
49 {
50 LOGD("enter");
51 if (keyInfo_.version == FSCRYPT_INVALID || keyInfo_.version > KeyCtrl::GetFscryptVersion(MNT_DATA)) {
52 LOGE("invalid version %{public}u", keyInfo_.version);
53 return false;
54 }
55 if (!keyInfo_.key.IsEmpty()) {
56 LOGE("key is not empty");
57 return false;
58 }
59 if (!GenerateKeyBlob(keyInfo_.key, keyLen_)) {
60 LOGE("GenerateKeyBlob raw key failed");
61 return false;
62 }
63 return true;
64 }
65
GenerateKeyBlob(KeyBlob & blob,const uint32_t size)66 bool BaseKey::GenerateKeyBlob(KeyBlob &blob, const uint32_t size)
67 {
68 if (!blob.Alloc(size)) {
69 return false;
70 }
71 if (!HuksMaster::GenerateRandomKey(blob)) {
72 blob.Clear();
73 return false;
74 }
75 return true;
76 }
77
SaveKeyBlob(const KeyBlob & blob,const std::string & path)78 bool BaseKey::SaveKeyBlob(const KeyBlob &blob, const std::string &path)
79 {
80 if (blob.IsEmpty()) {
81 return false;
82 }
83 LOGD("enter %{public}s, size=%{public}d", path.c_str(), blob.size);
84 std::ofstream file(path, std::ios::binary);
85 if (file.fail()) {
86 LOGE("open %{public}s failed, errno %{public}d", path.c_str(), errno);
87 return false;
88 }
89 ChMod(path, S_IREAD | S_IWRITE);
90 if (file.write(reinterpret_cast<char *>(blob.data.get()), blob.size).fail()) {
91 LOGE("write %{public}s failed, errno %{public}d", path.c_str(), errno);
92 return false;
93 }
94 file.flush();
95 return true;
96 }
97
GenerateAndSaveKeyBlob(KeyBlob & blob,const std::string & path,const uint32_t size)98 bool BaseKey::GenerateAndSaveKeyBlob(KeyBlob &blob, const std::string &path, const uint32_t size)
99 {
100 if (!GenerateKeyBlob(blob, size)) {
101 return false;
102 }
103 return SaveKeyBlob(blob, path);
104 }
105
LoadKeyBlob(KeyBlob & blob,const std::string & path,const uint32_t size=0)106 bool BaseKey::LoadKeyBlob(KeyBlob &blob, const std::string &path, const uint32_t size = 0)
107 {
108 LOGD("enter %{public}s, size=%{public}d", path.c_str(), size);
109 std::ifstream file(path, std::ios::binary);
110 if (file.fail()) {
111 LOGE("open %{public}s failed, errno %{public}d", path.c_str(), errno);
112 return false;
113 }
114
115 file.seekg(0, std::ios::end);
116 uint32_t length = static_cast<uint32_t>(file.tellg());
117 // zero size means use the file length.
118 if ((size != 0) && (length != size)) {
119 LOGE("file:%{public}s size error, real len %{public}d not expected %{public}d", path.c_str(), length, size);
120 return false;
121 }
122 if (!blob.Alloc(length)) {
123 return false;
124 }
125
126 file.seekg(0, std::ios::beg);
127 if (file.read(reinterpret_cast<char *>(blob.data.get()), length).fail()) {
128 LOGE("read %{public}s failed, errno %{public}d", path.c_str(), errno);
129 return false;
130 }
131 return true;
132 }
133
GetCandidateVersion() const134 int BaseKey::GetCandidateVersion() const
135 {
136 auto prefix = PATH_KEY_VERSION.substr(1); // skip the first slash
137 std::vector<std::string> files;
138 GetSubDirs(dir_, files);
139 int candidate = -1;
140 for (const auto &it: files) {
141 if (it.rfind(prefix) == 0) {
142 std::string str = it.substr(prefix.length());
143 int ver;
144 if (IsNumericStr(str) && StrToInt(str, ver) && ver >= candidate) {
145 candidate = ver;
146 }
147 }
148 }
149 LOGD("candidate key version is %{public}d", candidate);
150 return candidate;
151 }
152
153 // Get last version_xx dir to load key files.
GetCandidateDir() const154 std::string BaseKey::GetCandidateDir() const
155 {
156 auto candidate = GetCandidateVersion();
157 // candidate is -1 means no version_xx dir.
158 if (candidate == -1) {
159 return "";
160 }
161
162 return dir_ + PATH_KEY_VERSION + std::to_string(candidate);
163 }
164
165 // Get next available version_xx dir to save key files.
GetNextCandidateDir() const166 std::string BaseKey::GetNextCandidateDir() const
167 {
168 auto candidate = GetCandidateVersion();
169 return dir_ + PATH_KEY_VERSION + std::to_string(candidate + 1);
170 }
171
StoreKey(const UserAuth & auth)172 bool BaseKey::StoreKey(const UserAuth &auth)
173 {
174 LOGD("enter");
175 auto pathTemp = dir_ + PATH_KEY_TEMP;
176 if (DoStoreKey(auth)) {
177 // rename keypath/temp/ to keypath/version_xx/
178 auto candidate = GetNextCandidateDir();
179 LOGD("rename %{public}s to %{public}s", pathTemp.c_str(), candidate.c_str());
180 if (rename(pathTemp.c_str(), candidate.c_str()) == 0) {
181 return true;
182 }
183 LOGE("rename fail return %{public}d, cleanup the temp dir", errno);
184 } else {
185 LOGE("DoStoreKey fail, cleanup the temp dir");
186 }
187 RemoveAlias(pathTemp);
188 OHOS::ForceRemoveDirectory(pathTemp);
189 return false;
190 }
191
192 // All key files are saved under keypath/temp/ in this function.
DoStoreKey(const UserAuth & auth)193 bool BaseKey::DoStoreKey(const UserAuth &auth)
194 {
195 auto pathTemp = dir_ + PATH_KEY_TEMP;
196 MkDirRecurse(pathTemp, S_IRWXU);
197
198 auto pathVersion = dir_ + PATH_FSCRYPT_VER;
199 std::string version;
200 if (OHOS::LoadStringFromFile(pathVersion, version) && version != std::to_string(keyInfo_.version)) {
201 LOGE("version already exist %{public}s, not expected %{public}d", version.c_str(), keyInfo_.version);
202 return false;
203 }
204 if (OHOS::SaveStringToFile(pathVersion, std::to_string(keyInfo_.version)) == false) {
205 LOGE("save version failed, errno:%{public}d", errno);
206 return false;
207 }
208 ChMod(pathVersion, S_IREAD | S_IWRITE);
209
210 if (!GenerateAndSaveKeyBlob(keyContext_.alias, pathTemp + PATH_ALIAS, CRYPTO_KEY_ALIAS_SIZE)) {
211 LOGE("GenerateAndSaveKeyBlob alias failed");
212 return false;
213 }
214 if (!HuksMaster::GenerateKey(keyContext_.alias)) {
215 LOGE("HuksMaster::GenerateKey failed");
216 return false;
217 }
218 if (!GenerateAndSaveKeyBlob(keyContext_.secDiscard, pathTemp + PATH_SECDISC, CRYPTO_KEY_SECDISC_SIZE)) {
219 LOGE("GenerateAndSaveKeyBlob sec_discard failed");
220 return false;
221 }
222 if (!EncryptKey(auth)) {
223 return false;
224 }
225 if (!SaveKeyBlob(keyContext_.encrypted, pathTemp + PATH_ENCRYPTED)) {
226 return false;
227 }
228 keyContext_.encrypted.Clear();
229 return true;
230 }
231
232 // update the latest and do cleanups.
UpdateKey(const std::string & keypath)233 bool BaseKey::UpdateKey(const std::string &keypath)
234 {
235 LOGD("enter");
236 auto candidate = keypath.empty() ? GetCandidateDir() : keypath;
237 if (candidate.empty()) {
238 LOGE("no candidate dir");
239 return false;
240 }
241
242 // backup the latest
243 std::string pathLatest = dir_ + PATH_LATEST;
244 std::string pathLatestBak = dir_ + PATH_LATEST_BACKUP;
245 bool hasLatest = IsDir(dir_ + PATH_LATEST);
246 if (hasLatest) {
247 OHOS::ForceRemoveDirectory(pathLatestBak);
248 if (rename(pathLatest.c_str(),
249 pathLatestBak.c_str()) != 0) {
250 LOGE("backup the latest fail errno:%{public}d", errno);
251 }
252 LOGD("backup the latest success");
253 }
254
255 // rename {candidate} to latest
256 OHOS::ForceRemoveDirectory(dir_ + PATH_LATEST);
257 if (rename(candidate.c_str(), pathLatest.c_str()) != 0) {
258 LOGE("rename candidate to latest fail return %{public}d", errno);
259 if (hasLatest) {
260 // revert from the backup
261 if (rename(pathLatestBak.c_str(),
262 pathLatest.c_str()) != 0) {
263 LOGE("restore the latest_backup fail errno:%{public}d", errno);
264 } else {
265 LOGI("restore the latest_backup success");
266 }
267 }
268 return false;
269 }
270 LOGD("rename candidate %{public}s to latest success", candidate.c_str());
271
272 // cleanup backup and other versions
273 std::vector<std::string> files;
274 GetSubDirs(dir_, files);
275 for (const auto &it: files) {
276 if (it != PATH_LATEST.substr(1)) {
277 RemoveAlias(dir_ + "/" + it);
278 OHOS::ForceRemoveDirectory(dir_ + "/" + it);
279 }
280 }
281
282 return true;
283 }
284
EncryptKey(const UserAuth & auth)285 bool BaseKey::EncryptKey(const UserAuth &auth)
286 {
287 auto ret = HuksMaster::EncryptKey(keyContext_, auth, keyInfo_);
288 keyContext_.alias.Clear();
289 keyContext_.secDiscard.Clear();
290 keyContext_.nonce.Clear();
291 keyContext_.aad.Clear();
292 return ret;
293 }
294
RestoreKey(const UserAuth & auth)295 bool BaseKey::RestoreKey(const UserAuth &auth)
296 {
297 LOGD("enter");
298 auto candidate = GetCandidateDir();
299 if (candidate.empty()) {
300 // no candidate dir, just restore from the latest
301 return DoRestoreKey(auth, dir_ + PATH_LATEST);
302 }
303
304 if (DoRestoreKey(auth, candidate)) {
305 // update the latest with the candidate
306 UpdateKey();
307 return true;
308 }
309
310 LOGE("DoRestoreKey with %{public}s failed", candidate.c_str());
311 // try to restore from other versions
312 std::vector<std::string> files;
313 GetSubDirs(dir_, files);
314 std::sort(files.begin(), files.end(), [&](const std::string &a, const std::string &b) {
315 if (a.length() != b.length() ||
316 a.length() < PATH_KEY_VERSION.length() ||
317 b.length() < PATH_KEY_VERSION.length()) {
318 return a.length() > b.length();
319 }
320 // make sure a.length() >= PATH_KEY_VERSION.length() && b.length() >= PATH_KEY_VERSION.length()
321 return std::stoi(a.substr(PATH_KEY_VERSION.size() - 1)) > std::stoi(b.substr(PATH_KEY_VERSION.size() - 1));
322 });
323 for (const auto &it: files) {
324 if (it != candidate) {
325 if (DoRestoreKey(auth, dir_ + "/" + it)) {
326 UpdateKey(it);
327 return true;
328 }
329 }
330 }
331 return false;
332 }
333
DoRestoreKey(const UserAuth & auth,const std::string & path)334 bool BaseKey::DoRestoreKey(const UserAuth &auth, const std::string &path)
335 {
336 LOGD("enter, path = %{public}s", path.c_str());
337 auto ver = KeyCtrl::LoadVersion(dir_);
338 if (ver == FSCRYPT_INVALID || ver != keyInfo_.version) {
339 LOGE("RestoreKey fail. bad version loaded %{public}u not expected %{public}u", ver, keyInfo_.version);
340 return false;
341 }
342
343 if (!LoadKeyBlob(keyContext_.encrypted, path + PATH_ENCRYPTED)) {
344 return false;
345 }
346 if (!LoadKeyBlob(keyContext_.alias, path + PATH_ALIAS, CRYPTO_KEY_ALIAS_SIZE)) {
347 keyContext_.alias.Clear();
348 return false;
349 }
350 if (!LoadKeyBlob(keyContext_.secDiscard, path + PATH_SECDISC, CRYPTO_KEY_SECDISC_SIZE)) {
351 keyContext_.encrypted.Clear();
352 keyContext_.alias.Clear();
353 return false;
354 }
355 return DecryptKey(auth);
356 }
357
DecryptKey(const UserAuth & auth)358 bool BaseKey::DecryptKey(const UserAuth &auth)
359 {
360 auto ret = HuksMaster::DecryptKey(keyContext_, auth, keyInfo_);
361 keyContext_.encrypted.Clear();
362 keyContext_.alias.Clear();
363 keyContext_.secDiscard.Clear();
364 keyContext_.nonce.Clear();
365 keyContext_.aad.Clear();
366 return ret;
367 }
368
RemoveAlias(const std::string & keypath)369 bool BaseKey::RemoveAlias(const std::string &keypath)
370 {
371 LOGD("enter, keypath = %{public}s", keypath.c_str());
372 KeyBlob alias {};
373 return LoadKeyBlob(alias, keypath + PATH_ALIAS, CRYPTO_KEY_ALIAS_SIZE) &&
374 HuksMaster::DeleteKey(alias);
375 }
376
ClearKey(const std::string & mnt)377 bool BaseKey::ClearKey(const std::string &mnt)
378 {
379 LOGD("enter, dir_ = %{public}s", dir_.c_str());
380 InactiveKey(mnt);
381 keyInfo_.key.Clear();
382
383 // remove all key alias of all versions
384 std::vector<std::string> files;
385 GetSubDirs(dir_, files);
386 for (const auto &it : files) {
387 RemoveAlias(dir_ + "/" + it);
388 }
389
390 return OHOS::ForceRemoveDirectory(dir_);
391 // use F2FS_IOC_SEC_TRIM_FILE
392 }
393 } // namespace StorageDaemon
394 } // namespace OHOS
395