• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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 "openssl_crypto.h"
28 #include "storage_service_log.h"
29 #include "string_ex.h"
30 #include "utils/file_utils.h"
31 #include "utils/string_utils.h"
32 
33 namespace {
34 const std::string PATH_LATEST_BACKUP = "/latest_bak";
35 const std::string PATH_KEY_VERSION = "/version_";
36 const std::string PATH_KEY_TEMP = "/temp";
37 
38 #ifndef F2FS_IOCTL_MAGIC
39 #define F2FS_IOCTL_MAGIC 0xf5
40 #endif
41 
42 #ifndef F2FS_IOC_SEC_TRIM_FILE
43     struct F2fsSectrimRange {
44         uint64_t start;
45         uint64_t len;
46         uint64_t flags;
47     };
48     using F2fsSectrim = F2fsSectrimRange;
49 #define F2FS_IOC_SEC_TRIM_FILE _IOW(F2FS_IOCTL_MAGIC, 20, F2fsSectrim)
50 #define F2FS_TRIM_FILE_DISCARD 0x1
51 #define F2FS_TRIM_FILE_ZEROOUT 0x2
52 #endif
53 #ifndef F2FS_IOC_SET_PIN_FILE
54 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, set)
55 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, set)
56 #endif
57 }
58 
59 namespace OHOS {
60 namespace StorageDaemon {
BaseKey(const std::string & dir,uint8_t keyLen)61 BaseKey::BaseKey(const std::string &dir, uint8_t keyLen) : dir_(dir), keyLen_(keyLen)
62 {
63 }
64 
InitKey()65 bool BaseKey::InitKey()
66 {
67     LOGD("enter");
68     if (keyInfo_.version == FSCRYPT_INVALID || keyInfo_.version > KeyCtrlGetFscryptVersion(MNT_DATA.c_str())) {
69         LOGE("invalid version %{public}u", keyInfo_.version);
70         return false;
71     }
72     if (!keyInfo_.key.IsEmpty()) {
73         LOGE("key is not empty");
74         return false;
75     }
76     if (!GenerateKeyBlob(keyInfo_.key, keyLen_)) {
77         LOGE("GenerateKeyBlob raw key failed");
78         return false;
79     }
80     return true;
81 }
82 
GenerateKeyBlob(KeyBlob & blob,const uint32_t size)83 bool BaseKey::GenerateKeyBlob(KeyBlob &blob, const uint32_t size)
84 {
85     blob = HuksMaster::GenerateRandomKey(size);
86     return !blob.IsEmpty();
87 }
88 
SaveKeyBlob(const KeyBlob & blob,const std::string & path)89 bool BaseKey::SaveKeyBlob(const KeyBlob &blob, const std::string &path)
90 {
91     if (blob.IsEmpty()) {
92         return false;
93     }
94     LOGD("enter %{public}s, size=%{public}d", path.c_str(), blob.size);
95     return WriteFileSync(path.c_str(), blob.data.get(), blob.size);
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 
172 #ifdef USER_CRYPTO_MIGRATE_KEY
StoreKey(const UserAuth & auth,bool needGenerateShield)173 bool BaseKey::StoreKey(const UserAuth &auth, bool needGenerateShield)
174 #else
175 bool BaseKey::StoreKey(const UserAuth &auth)
176 #endif
177 {
178     LOGD("enter");
179     auto pathTemp = dir_ + PATH_KEY_TEMP;
180 #ifdef USER_CRYPTO_MIGRATE_KEY
181     if (DoStoreKey(auth, needGenerateShield)) {
182 #else
183     if (DoStoreKey(auth)) {
184 #endif
185         // rename keypath/temp/ to keypath/version_xx/
186         auto candidate = GetNextCandidateDir();
187         LOGD("rename %{public}s to %{public}s", pathTemp.c_str(), candidate.c_str());
188         if (rename(pathTemp.c_str(), candidate.c_str()) == 0) {
189             SyncKeyDir();
190             return true;
191         }
192         LOGE("rename fail return %{public}d, cleanup the temp dir", errno);
193     } else {
194         LOGE("DoStoreKey fail, cleanup the temp dir");
195     }
196     OHOS::ForceRemoveDirectory(pathTemp);
197     SyncKeyDir();
198     return false;
199 }
200 
201 // All key files are saved under keypath/temp/ in this function.
202 #ifdef USER_CRYPTO_MIGRATE_KEY
203 bool BaseKey::DoStoreKey(const UserAuth &auth, bool needGenerateShield)
204 #else
205 bool BaseKey::DoStoreKey(const UserAuth &auth)
206 #endif
207 {
208     auto pathTemp = dir_ + PATH_KEY_TEMP;
209     MkDirRecurse(pathTemp, S_IRWXU);
210     const std::string NEED_UPDATE_PATH = pathTemp + SUFFIX_NEED_UPDATE;
211     if (!CheckAndUpdateVersion()) {
212         return false;
213     }
214     if ((auth.secret.IsEmpty()) && (!LoadAndSaveShield(auth, pathTemp, needGenerateShield))) {
215         return false;
216     }
217     if (!GenerateAndSaveKeyBlob(keyContext_.secDiscard, pathTemp + PATH_SECDISC, CRYPTO_KEY_SECDISC_SIZE)) {
218         LOGE("GenerateAndSaveKeyBlob sec_discard failed");
219         return false;
220     }
221     if (!Encrypt(auth)) {
222         return false;
223     }
224     if (!SaveKeyBlob(keyContext_.encrypted, pathTemp + PATH_ENCRYPTED)) {
225         return false;
226     }
227     if (!SaveStringToFile(NEED_UPDATE_PATH, KeyEncryptTypeToString(keyEncryptType_))) {
228         LOGE("make file fali");
229         return false;
230     }
231     keyContext_.encrypted.Clear();
232     LOGD("finish");
233     return true;
234 }
235 
236 bool BaseKey::CheckAndUpdateVersion()
237 {
238     auto pathVersion = dir_ + PATH_FSCRYPT_VER;
239     std::string version;
240     if (OHOS::LoadStringFromFile(pathVersion, version)) {
241         if (version != std::to_string(keyInfo_.version)) {
242             LOGE("version already exist %{public}s, not expected %{public}d", version.c_str(), keyInfo_.version);
243             return false;
244         }
245     } else if (SaveStringToFileSync(pathVersion, std::to_string(keyInfo_.version)) == false) {
246         LOGE("save version failed, errno:%{public}d", errno);
247         return false;
248     }
249     ChMod(pathVersion, S_IREAD | S_IWRITE);
250     return true;
251 }
252 
253 bool BaseKey::LoadAndSaveShield(const UserAuth &auth, const std::string &pathTemp, bool needGenerateShield)
254 {
255 #ifdef USER_CRYPTO_MIGRATE_KEY
256         if (needGenerateShield) {
257             if (!HuksMaster::GetInstance().GenerateKey(auth, keyContext_.shield)) {
258                 LOGE("GenerateKey of shield failed");
259                 return false;
260             }
261         } else {
262             if (!LoadKeyBlob(keyContext_.shield, dir_ + PATH_LATEST + PATH_SHIELD)) {
263                 keyContext_.encrypted.Clear();
264                 return false;
265             }
266         }
267 #else
268         if (!HuksMaster::GetInstance().GenerateKey(auth, keyContext_.shield)) {
269             LOGE("GenerateKey of shield failed");
270             return false;
271         }
272 #endif
273         if (!SaveKeyBlob(keyContext_.shield, pathTemp + PATH_SHIELD)) {
274             return false;
275         }
276     return true;
277 }
278 
279 // update the latest and do cleanups.
280 bool BaseKey::UpdateKey(const std::string &keypath)
281 {
282     LOGD("enter");
283     auto candidate = keypath.empty() ? GetCandidateDir() : keypath;
284     if (candidate.empty()) {
285         LOGE("no candidate dir");
286         return false;
287     }
288 
289     // backup the latest
290     std::string pathLatest = dir_ + PATH_LATEST;
291     std::string pathLatestBak = dir_ + PATH_LATEST_BACKUP;
292     bool hasLatest = IsDir(dir_ + PATH_LATEST);
293     if (hasLatest) {
294         OHOS::ForceRemoveDirectory(pathLatestBak);
295         if (rename(pathLatest.c_str(),
296                    pathLatestBak.c_str()) != 0) {
297             LOGE("backup the latest fail errno:%{public}d", errno);
298         }
299         LOGD("backup the latest success");
300     }
301 
302     // rename {candidate} to latest
303     OHOS::ForceRemoveDirectory(dir_ + PATH_LATEST);
304     if (rename(candidate.c_str(), pathLatest.c_str()) != 0) {
305         LOGE("rename candidate to latest fail return %{public}d", errno);
306         if (hasLatest) {
307             // revert from the backup
308             if (rename(pathLatestBak.c_str(),
309                        pathLatest.c_str()) != 0) {
310                 LOGE("restore the latest_backup fail errno:%{public}d", errno);
311             } else {
312                 LOGI("restore the latest_backup success");
313             }
314         }
315         SyncKeyDir();
316         return false;
317     }
318     LOGD("rename candidate %{public}s to latest success", candidate.c_str());
319 
320     // cleanup backup and other versions
321     std::vector<std::string> files;
322     GetSubDirs(dir_, files);
323     for (const auto &it: files) {
324         if (it != PATH_LATEST.substr(1)) {
325             OHOS::ForceRemoveDirectory(dir_ + "/" + it);
326         }
327     }
328 
329     SyncKeyDir();
330     return true;
331 }
332 
333 bool BaseKey::Encrypt(const UserAuth &auth)
334 {
335     LOGD("enter");
336     bool ret;
337     if (!auth.secret.IsEmpty()) {
338         LOGI("Enhanced encrypt start");
339         ret = OpensslCrypto::AESEncrypt(auth.secret, keyInfo_.key, keyContext_);
340         keyEncryptType_ = KeyEncryptType::KEY_CRYPT_OPENSSL;
341     } else {
342         LOGI("Huks encrypt start");
343         ret = HuksMaster::GetInstance().EncryptKey(keyContext_, auth, keyInfo_);
344         keyEncryptType_ = KeyEncryptType::KEY_CRYPT_HUKS;
345     }
346     keyContext_.shield.Clear();
347     keyContext_.secDiscard.Clear();
348     keyContext_.nonce.Clear();
349     keyContext_.aad.Clear();
350     LOGD("finish");
351     return ret;
352 }
353 
354 bool BaseKey::RestoreKey(const UserAuth &auth)
355 {
356     LOGD("enter");
357     auto candidate = GetCandidateDir();
358     if (candidate.empty()) {
359         // no candidate dir, just restore from the latest
360         return DoRestoreKey(auth, dir_ + PATH_LATEST);
361     }
362 
363     if (DoRestoreKey(auth, candidate)) {
364         // update the latest with the candidate
365         UpdateKey();
366         return true;
367     }
368 
369     LOGE("DoRestoreKey with %{public}s failed", candidate.c_str());
370     // try to restore from other versions
371     std::vector<std::string> files;
372     GetSubDirs(dir_, files);
373     std::sort(files.begin(), files.end(), [&](const std::string &a, const std::string &b) {
374         if (a.length() != b.length() ||
375             a.length() < PATH_KEY_VERSION.length() ||
376             b.length() < PATH_KEY_VERSION.length()) {
377             return a.length() > b.length();
378         }
379         // make sure a.length() >= PATH_KEY_VERSION.length() && b.length() >= PATH_KEY_VERSION.length()
380         return std::stoi(a.substr(PATH_KEY_VERSION.size() - 1)) > std::stoi(b.substr(PATH_KEY_VERSION.size() - 1));
381     });
382     for (const auto &it: files) {
383         if (it != candidate) {
384             if (DoRestoreKey(auth, dir_ + "/" + it)) {
385                 UpdateKey(it);
386                 return true;
387             }
388         }
389     }
390     return false;
391 }
392 
393 bool BaseKey::DoRestoreKey(const UserAuth &auth, const std::string &path)
394 {
395     LOGD("enter, path = %{public}s", path.c_str());
396     const std::string NEED_UPDATE_PATH = dir_ + PATH_LATEST + SUFFIX_NEED_UPDATE;
397     if (!auth.secret.IsEmpty() && FileExists(NEED_UPDATE_PATH)) {
398         keyEncryptType_ = KeyEncryptType::KEY_CRYPT_OPENSSL;
399         LOGI("set keyEncryptType_ as KEY_CRYPT_OPENSSL success");
400     } else if (auth.secret.IsEmpty() || (!auth.secret.IsEmpty() && !FileExists(NEED_UPDATE_PATH))) {
401         keyEncryptType_ = KeyEncryptType::KEY_CRYPT_HUKS;
402         LOGI("set keyEncryptType_ as KEY_CRYPT_HUKS success");
403     }
404     auto ver = KeyCtrlLoadVersion(dir_.c_str());
405     if (ver == FSCRYPT_INVALID || ver != keyInfo_.version) {
406         LOGE("RestoreKey fail. bad version loaded %{public}u not expected %{public}u", ver, keyInfo_.version);
407         return false;
408     }
409     if (!LoadKeyBlob(keyContext_.encrypted, path + PATH_ENCRYPTED)) {
410         return false;
411     }
412     if (keyEncryptType_ == KeyEncryptType::KEY_CRYPT_HUKS) {
413         if (!LoadKeyBlob(keyContext_.shield, path + PATH_SHIELD)) {
414             keyContext_.encrypted.Clear();
415             return false;
416         }
417     }
418     if (!LoadKeyBlob(keyContext_.secDiscard, path + PATH_SECDISC, CRYPTO_KEY_SECDISC_SIZE)) {
419         keyContext_.encrypted.Clear();
420         keyContext_.shield.Clear();
421         return false;
422     }
423     return Decrypt(auth);
424 }
425 
426 bool BaseKey::Decrypt(const UserAuth &auth)
427 {
428     bool ret = false;
429     switch (keyEncryptType_) {
430         case KeyEncryptType::KEY_CRYPT_OPENSSL:
431             LOGI("Enhanced decrypt key start");
432             ret = OpensslCrypto::AESDecrypt(auth.secret, keyContext_, keyInfo_.key);
433             break;
434         case KeyEncryptType::KEY_CRYPT_HUKS:
435             LOGI("Huks decrypt key start");
436             ret = HuksMaster::GetInstance().DecryptKey(keyContext_, auth, keyInfo_);
437             break;
438     }
439     keyContext_.encrypted.Clear();
440     keyContext_.shield.Clear();
441     keyContext_.secDiscard.Clear();
442     keyContext_.nonce.Clear();
443     keyContext_.aad.Clear();
444     return ret;
445 }
446 
447 bool BaseKey::ClearKey(const std::string &mnt)
448 {
449     LOGD("enter, dir_ = %{public}s", dir_.c_str());
450     InactiveKey(USER_DESTROY, mnt);
451     keyInfo_.key.Clear();
452     WipingActionDir(dir_);
453     return OHOS::ForceRemoveDirectory(dir_);
454     // use F2FS_IOC_SEC_TRIM_FILE
455 }
456 
457 void BaseKey::WipingActionDir(std::string &path)
458 {
459     std::vector<std::string> fileList;
460     LOGI("WipingActionDir path.c_str() is %{public}s", path.c_str());
461     OpenSubFile(path.c_str(), fileList);
462     for (const auto &it: fileList) {
463         int fd = open(it.c_str(), O_WRONLY | O_CLOEXEC);
464         if (fd < 0) {
465             LOGE("open %{public}s failed, errno %{public}u", it.c_str(), errno);
466             return;
467         }
468         uint32_t  set = 1;
469         int ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
470         if (ret != 0) {
471             LOGE("F2FS_IOC_SET_PIN_FILE ioctl is %{public}u, errno = %{public}u", ret, errno);
472         }
473         struct F2fsSectrimRange trimRange;
474         trimRange.start = 0;
475         trimRange.len = -1;
476         trimRange.flags = F2FS_TRIM_FILE_DISCARD | F2FS_TRIM_FILE_ZEROOUT;
477         ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &trimRange);
478         if (ret != 0 && errno == EOPNOTSUPP) {
479             trimRange.flags = F2FS_TRIM_FILE_ZEROOUT;
480             ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &trimRange);
481             if (ret != 0) {
482                 LOGE("F2FS_IOC_SEC_TRIM_FILE ioctl is %{public}u, errno = %{public}u", ret, errno);
483             }
484         }
485         set = 0;
486         ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
487         if (ret != 0) {
488             LOGE("F2FS_IOC_SET_PIN_FILE ioctl is %{public}u", ret);
489         }
490         LOGI("WipingActionDir success");
491         close(fd);
492     }
493 }
494 
495 void BaseKey::SyncKeyDir() const
496 {
497     int fd = open(dir_.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
498     if (fd < 0) {
499         LOGE("open %{private}s failed, errno %{public}d", dir_.c_str(), errno);
500         sync();
501         return;
502     }
503     if (syncfs(fd) != 0) {
504         LOGE("syncfs %{private}s failed, errno %{public}d", dir_.c_str(), errno);
505         sync();
506     }
507     (void)close(fd);
508 }
509 
510 bool BaseKey::UpgradeKeys()
511 {
512     std::vector<std::string> versions;
513     GetSubDirs(dir_, versions);
514 
515     for (const auto &it : versions) {
516         std::string shieldPath = dir_ + "/" + it + PATH_SHIELD;
517         LOGI("Upgrade of %{public}s", shieldPath.c_str());
518         LoadKeyBlob(keyContext_.shield, shieldPath);
519         if (HuksMaster::GetInstance().UpgradeKey(keyContext_)) {
520             LOGI("success upgrade of %{public}s", shieldPath.c_str());
521             SaveKeyBlob(keyContext_.shield, shieldPath);
522             SyncKeyDir();
523         }
524     }
525     return true;
526 }
527 
528 std::string BaseKey::KeyEncryptTypeToString(KeyEncryptType keyEncryptType_) const
529 {
530     switch (keyEncryptType_) {
531         case KeyEncryptType::KEY_CRYPT_OPENSSL:
532             return "KEY_CRYPT_OPENSSL";
533         case KeyEncryptType::KEY_CRYPT_HUKS:
534             return "KEY_CRYPT_HUKS";
535     }
536 }
537 } // namespace StorageDaemon
538 } // namespace OHOS
539