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
30namespace OHOS {
31namespace Global {
32namespace I18n {
33namespace {
34    const int32_t BASE64_ENCODE_PACKET_LEN = 3;
35    const int32_t BASE64_ENCODE_LEN_OF_EACH_GROUP_DATA = 4;
36}
37
38const int SignatureVerifier::HASH_BUFFER_SIZE = 4096;
39const int SignatureVerifier::MIN_SIZE = 2;
40const int SignatureVerifier::VERSION_SIZE = 4;
41
42
43std::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
67int 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
87bool 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
118bool 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
157bool 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
188bool 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
224std::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
233void 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
243int 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
267std::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}