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