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