• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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