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