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