• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 <climits>
17 #include <cstdlib>
18 #include <cstring>
19 #include <fcntl.h>
20 #include <filesystem>
21 #include <fstream>
22 #include <openssl/pem.h>
23 #include <openssl/rsa.h>
24 #include <openssl/sha.h>
25 #include <sstream>
26 #include <unistd.h>
27 #include "i18n_hilog.h"
28 #include "securec.h"
29 #include "utils.h"
30 #include "signature_verifier.h"
31 
32 namespace OHOS {
33 namespace Global {
34 namespace I18n {
35 const int32_t SignatureVerifier::BASE64_ENCODE_PACKET_LEN = 3;
36 const int32_t SignatureVerifier::BASE64_ENCODE_LEN_OF_EACH_GROUP_DATA = 4;
37 const size_t SignatureVerifier::HASH_BUFFER_SIZE = 4096;
38 const std::string SignatureVerifier::PREFIX_NAME = "Name: ";
39 const size_t SignatureVerifier::MIN_SIZE = 2;
40 
41 // verify certificate file
VerifyCertFile(const std::string & certPath,const std::string & verifyPath,const std::string & pubkeyPath,const std::string & manifestPath)42 bool SignatureVerifier::VerifyCertFile(const std::string& certPath, const std::string& verifyPath,
43     const std::string& pubkeyPath, const std::string& manifestPath)
44 {
45     if (!VerifyFileSign(pubkeyPath, certPath, verifyPath)) {
46         HILOG_ERROR_I18N("SignatureVerifier::VerifyCertFile: Verify file sign failed.");
47         return false;
48     }
49     std::ifstream file(verifyPath);
50     if (!file.is_open()) {
51         HILOG_ERROR_I18N("SignatureVerifier::VerifyCertFile: Verify file open failed.");
52         return false;
53     }
54     std::string line;
55     std::getline(file, line);
56     file.close();
57     std::vector<std::string> strs;
58     Split(line, ":", strs);
59     if (strs.size() < MIN_SIZE) {
60         HILOG_ERROR_I18N("SignatureVerifier::VerifyCertFile: Parse digest failed.");
61         return false;
62     }
63     std::string sha256Digest = strs[1];
64     sha256Digest = trim(sha256Digest);
65     std::string manifestDigest = CalcFileSha256Digest(manifestPath);
66     if (sha256Digest.compare(manifestDigest) != 0) {
67         HILOG_ERROR_I18N("SignatureVerifier::VerifyCertFile: Manifest digest verify failed.");
68         return false;
69     }
70     return true;
71 }
72 
73 // verify cert file sign
VerifyFileSign(const std::string & pubkeyPath,const std::string & signPath,const std::string & digestPath)74 bool SignatureVerifier::VerifyFileSign(const std::string& pubkeyPath, const std::string& signPath,
75     const std::string& digestPath)
76 {
77     std::string validPubkeyPath = GetAbsoluteFilePath(pubkeyPath);
78     if (validPubkeyPath.empty()) {
79         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: Check pubkey file failed.");
80         return false;
81     }
82     std::string validSignPath = GetAbsoluteFilePath(signPath);
83     if (validSignPath.empty()) {
84         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: Check sign file failed.");
85         return false;
86     }
87     std::string validDigestPath = GetAbsoluteFilePath(digestPath);
88     if (validDigestPath.empty()) {
89         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: Check digest file failed.");
90         return false;
91     }
92 
93     std::string signStr = GetFileStream(validSignPath);
94     std::string digestStr = GetFileStream(validDigestPath);
95     if (signStr.empty() || digestStr.empty()) {
96         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: sign (%{public}s), digest (%{public}s).",
97             signStr.c_str(), digestStr.c_str());
98         return false;
99     }
100     RSA* pubkey = RSA_new();
101     if (pubkey == nullptr) {
102         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: Create RSA failed.");
103         return false;
104     }
105     BIO* bio = BIO_new_file(validPubkeyPath.c_str(), "r");
106     if (bio == nullptr) {
107         RSA_free(pubkey);
108         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: Create BIO failed.");
109         return false;
110     }
111     if (PEM_read_bio_RSA_PUBKEY(bio, &pubkey, nullptr, nullptr) == nullptr) {
112         RSA_free(pubkey);
113         BIO_free(bio);
114         HILOG_ERROR_I18N("SignatureVerifier::VerifyFileSign: PEM read bio RSA pubkey failed.");
115         return false;
116     }
117     bool verify = VerifyRsa(pubkey, digestStr, signStr);
118     RSA_free(pubkey);
119     BIO_free(bio);
120     return verify;
121 }
122 
VerifyUpdateFile(const std::string & filePath,const std::string & manifestPath,std::vector<std::string> & filesList)123 bool SignatureVerifier::VerifyUpdateFile(const std::string& filePath, const std::string& manifestPath,
124     std::vector<std::string>& filesList)
125 {
126     char* result = realpath(manifestPath.c_str(), nullptr);
127     if (result == nullptr) {
128         HILOG_ERROR_I18N("SignatureVerifier::VerifyUpdateFile: realpath error: %{public}s.", strerror(errno));
129         return false;
130     }
131     std::ifstream file(result);
132     free(result);
133     result = nullptr;
134     if (!file.is_open()) {
135         HILOG_ERROR_I18N("SignatureVerifier::VerifyUpdateFile: Open manifest failed.");
136         return false;
137     }
138     std::string line;
139     while (std::getline(file, line)) {
140         if (line.find(PREFIX_NAME) == std::string::npos) {
141             continue;
142         }
143         std::string nextLine;
144         std::getline(file, nextLine);
145         std::vector<std::string> strs;
146         Split(nextLine, ":", strs);
147         if (strs.size() < MIN_SIZE) {
148             HILOG_ERROR_I18N("SignatureVerifier::VerifyUpdateFile: Parse digest failed.");
149             return false;
150         }
151         std::string sha256Digest = strs[1];
152         sha256Digest = trim(sha256Digest);
153         if (sha256Digest.empty()) {
154             HILOG_ERROR_I18N("SignatureVerifier::VerifyUpdateFile: Digest is empty.");
155             return false;
156         }
157         std::string fileName = line.substr(PREFIX_NAME.size());
158         filesList.push_back(fileName);
159         std::string absFilePath = filePath + trim(fileName);
160         absFilePath = GetAbsoluteFilePath(absFilePath);
161         if (absFilePath.empty()) {
162             HILOG_ERROR_I18N("VerifyUpdateFile: File %{public}s is not exist.", fileName.c_str());
163             return false;
164         }
165         std::string fileDigest = CalcFileSha256Digest(absFilePath);
166         if (fileDigest.compare(sha256Digest) != 0) {
167             HILOG_ERROR_I18N("VerifyUpdateFile: verify %{public}s failed.", fileDigest.c_str());
168             return false;
169         }
170     }
171     return true;
172 }
173 
VerifyRsa(RSA * pubkey,const std::string & digest,const std::string & sign)174 bool SignatureVerifier::VerifyRsa(RSA* pubkey, const std::string& digest, const std::string& sign)
175 {
176     EVP_PKEY* evpKey = EVP_PKEY_new();
177     if (evpKey == nullptr) {
178         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: Create EVP_PKEY failed.");
179         return false;
180     }
181     if (EVP_PKEY_set1_RSA(evpKey, pubkey) != 1) {
182         EVP_PKEY_free(evpKey);
183         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: EVP_PKEY set pubkey failed.");
184         return false;
185     }
186     EVP_MD_CTX* ctx = EVP_MD_CTX_new();
187     if (ctx == nullptr) {
188         EVP_PKEY_free(evpKey);
189         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: Create EVP_MD_CTX failed.");
190         return false;
191     }
192     EVP_MD_CTX_init(ctx);
193     if (EVP_VerifyInit_ex(ctx, EVP_sha256(), nullptr) != 1) {
194         EVP_PKEY_free(evpKey);
195         EVP_MD_CTX_free(ctx);
196         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: Verify init failed.");
197         return false;
198     }
199     if (EVP_VerifyUpdate(ctx, digest.c_str(), digest.size()) != 1) {
200         EVP_PKEY_free(evpKey);
201         EVP_MD_CTX_free(ctx);
202         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: Verify update failed.");
203         return false;
204     }
205     char* signArr = const_cast<char*>(sign.c_str());
206     if (EVP_VerifyFinal(ctx, reinterpret_cast<unsigned char *>(signArr), sign.size(), evpKey) != 1) {
207         EVP_PKEY_free(evpKey);
208         EVP_MD_CTX_free(ctx);
209         HILOG_ERROR_I18N("SignatureVerifier::VerifyRsa: Verify final failed.");
210         return false;
211     }
212     EVP_PKEY_free(evpKey);
213     EVP_MD_CTX_free(ctx);
214     return true;
215 }
216 
CalcFileSha256Digest(const std::string & path)217 std::string SignatureVerifier::CalcFileSha256Digest(const std::string& path)
218 {
219     std::string dist;
220     unsigned char res[SHA256_DIGEST_LENGTH] = {0};
221     if (!CalcFileShaOriginal(path, res)) {
222         HILOG_ERROR_I18N("SignatureVerifier::CalcFileSha256Digest: Calc file sha original failed.");
223         return dist;
224     }
225     CalcBase64(res, SHA256_DIGEST_LENGTH, dist);
226     return dist;
227 }
228 
CalcBase64(uint8_t * input,uint32_t inputLen,std::string & encodedStr)229 void SignatureVerifier::CalcBase64(uint8_t* input, uint32_t inputLen, std::string& encodedStr)
230 {
231     size_t base64Len = static_cast<size_t>(ceil(static_cast<long double>(inputLen) / BASE64_ENCODE_PACKET_LEN) *
232         BASE64_ENCODE_LEN_OF_EACH_GROUP_DATA + 1);
233     std::unique_ptr<unsigned char[]> base64Str = std::make_unique<unsigned char[]>(base64Len);
234     int encodeLen = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64Str.get()), input, inputLen);
235     size_t outLen = static_cast<size_t>(encodeLen);
236     encodedStr = std::string(reinterpret_cast<char*>(base64Str.get()), outLen);
237 }
238 
CalcFileShaOriginal(const std::string & filePath,unsigned char * hash)239 bool SignatureVerifier::CalcFileShaOriginal(const std::string& filePath, unsigned char* hash)
240 {
241     if (filePath.empty() || hash == nullptr || !IsLegalPath(filePath)) {
242         HILOG_ERROR_I18N("SignatureVerifier::CalcFileShaOriginal: Invalid input parameter.");
243         return false;
244     }
245     char* resolvedPath = realpath(filePath.c_str(), nullptr);
246     if (resolvedPath == nullptr) {
247         HILOG_ERROR_I18N("SignatureVerifier::CalcFileShaOriginal: Input parameter filePath invalid.");
248         return false;
249     }
250     FILE* fp = fopen(resolvedPath, "rb");
251     free(resolvedPath);
252     if (fp == nullptr) {
253         HILOG_ERROR_I18N("SignatureVerifier::CalcFileShaOriginal: Close file failed.");
254         return false;
255     }
256     size_t n;
257     char buffer[HASH_BUFFER_SIZE] = {0};
258     SHA256_CTX ctx;
259     SHA256_Init(&ctx);
260     while ((n = fread(buffer, 1, sizeof(buffer), fp))) {
261         SHA256_Update(&ctx, reinterpret_cast<unsigned char*>(buffer), n);
262     }
263     SHA256_Final(hash, &ctx);
264     if (fclose(fp) == -1) {
265         HILOG_ERROR_I18N("SignatureVerifier::CalcFileShaOriginal: Close file failed.");
266         return false;
267     }
268     return true;
269 }
270 
271 // load file content
GetFileStream(const std::string & filePath)272 std::string SignatureVerifier::GetFileStream(const std::string& filePath)
273 {
274     if (filePath.length() > PATH_MAX) {
275         HILOG_ERROR_I18N("SignatureVerifier::GetFileStream: File path length is too large.");
276         return "";
277     }
278     char* result = realpath(filePath.c_str(), nullptr);
279     if (result == nullptr) {
280         HILOG_ERROR_I18N("SignatureVerifier::GetFileStream: Get real path failed.");
281         return "";
282     }
283     std::ifstream file(result, std::ios::in | std::ios::binary);
284     free(result);
285     result = nullptr;
286     if (!file.is_open()) {
287         HILOG_ERROR_I18N("SignatureVerifier::GetFileStream: Open file failed.");
288         return "";
289     }
290     std::stringstream inFile;
291     inFile << file.rdbuf();
292     return inFile.str();
293 }
294 } // namespace I18n
295 } // namespace Global
296 } // namespace OHOS