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