1 /*
2  * Copyright (c) 2022 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 "pkg_verify_util.h"
17 #include <ctime>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include "dump.h"
21 #include "openssl_util.h"
22 #include "pkcs7_signed_data.h"
23 #include "pkg_algo_sign.h"
24 #include "pkg_algorithm.h"
25 #include "pkg_manager_impl.h"
26 #include "pkg_utils.h"
27 #include "securec.h"
28 #include "zip_pkg_parse.h"
29 
30 namespace Hpackage {
31 namespace {
32 constexpr uint32_t ZIP_EOCD_FIXED_PART_LEN = 22;
33 constexpr uint32_t PKG_FOOTER_SIZE = 6;
34 constexpr uint32_t PKG_HASH_CONTENT_LEN = SHA256_DIGEST_LENGTH;
35 }
36 
VerifySourceDigest(std::vector<uint8_t> & signature,std::vector<uint8_t> & sourceDigest,const std::string & keyPath) const37 int32_t PkgVerifyUtil::VerifySourceDigest(std::vector<uint8_t> &signature, std::vector<uint8_t> &sourceDigest,
38     const std::string & keyPath) const
39 {
40     std::vector<std::vector<uint8_t>> sigs;
41     Pkcs7SignedData pkcs7;
42     SignAlgorithm::SignAlgorithmPtr signAlgorithm = PkgAlgorithmFactory::GetVerifyAlgorithm(
43         keyPath, PKG_DIGEST_TYPE_SHA256);
44     int32_t ret = pkcs7.ReadSig(signature.data(), signature.size(), sigs);
45     if (ret != PKCS7_SUCCESS) {
46         UPDATER_LAST_WORD("pkcs7", ret);
47         return ret;
48     }
49     for (auto &sig : sigs) {
50         if (signAlgorithm->VerifyDigest(sourceDigest, sig) == 0) {
51             return PKG_SUCCESS;
52         }
53     }
54     return PKG_VERIFY_FAIL;
55 }
56 
VerifyAccPackageSign(const PkgStreamPtr pkgStream,const std::string & keyPath) const57 int32_t PkgVerifyUtil::VerifyAccPackageSign(const PkgStreamPtr pkgStream, const std::string &keyPath) const
58 {
59     if (pkgStream == nullptr) {
60         UPDATER_LAST_WORD(PKG_INVALID_PARAM);
61         return PKG_INVALID_PARAM;
62     }
63     size_t signatureSize = 0;
64     std::vector<uint8_t> signature;
65     uint16_t commentTotalLenAll = 0;
66     if (GetSignature(pkgStream, signatureSize, signature, commentTotalLenAll) != PKG_SUCCESS) {
67         PKG_LOGE("get package signature fail!");
68         UPDATER_LAST_WORD(PKG_INVALID_SIGNATURE);
69         return PKG_INVALID_SIGNATURE;
70     }
71     size_t srcDataLen = pkgStream->GetFileLength() - commentTotalLenAll -2;
72     size_t readLen = 0;
73     std::vector<uint8_t> sourceDigest;
74     PkgBuffer digest(srcDataLen);
75     pkgStream->Read(digest, 0, srcDataLen, readLen);
76     sourceDigest.assign(digest.buffer, digest.buffer + readLen);
77     return VerifySourceDigest(signature, sourceDigest, keyPath);
78 }
79 
VerifySign(std::vector<uint8_t> & signData,std::vector<uint8_t> & digest) const80 int32_t PkgVerifyUtil::VerifySign(std::vector<uint8_t> &signData, std::vector<uint8_t> &digest) const
81 {
82     std::vector<uint8_t> hash;
83     int32_t ret = Pkcs7verify(signData, hash);
84     if (ret != PKG_SUCCESS) {
85         PKG_LOGE("pkcs7 verify fail!");
86         UPDATER_LAST_WORD(ret);
87         return ret;
88     }
89     size_t hashLen = hash.size();
90     if ((hashLen != digest.size()) || memcmp(hash.data(), digest.data(), hashLen) != EOK) {
91         PKG_LOGE("Failed to memcmp data.");
92         UPDATER_LAST_WORD(PKG_INVALID_DIGEST);
93         return PKG_INVALID_DIGEST;
94     }
95     return PKG_SUCCESS;
96 }
97 
VerifyPackageSign(const PkgStreamPtr pkgStream,const std::string & path) const98 int32_t PkgVerifyUtil::VerifyPackageSign(const PkgStreamPtr pkgStream, const std::string &path) const
99 {
100     if (pkgStream == nullptr) {
101         UPDATER_LAST_WORD(PKG_INVALID_PARAM);
102         return PKG_INVALID_PARAM;
103     }
104     size_t signatureSize = 0;
105     std::vector<uint8_t> signature;
106     uint16_t commentTotalLenAll = 0;
107     if (GetSignature(pkgStream, signatureSize, signature, commentTotalLenAll) != PKG_SUCCESS) {
108         PKG_LOGE("get package signature fail!");
109         UPDATER_LAST_WORD(PKG_INVALID_SIGNATURE);
110         return PKG_INVALID_SIGNATURE;
111     }
112 
113     std::vector<uint8_t> hash;
114     int32_t ret = Pkcs7verify(signature, hash);
115     if (ret != PKG_SUCCESS) {
116         PKG_LOGE("pkcs7 verify fail!");
117         UPDATER_LAST_WORD(ret);
118         return ret;
119     }
120     size_t srcDataLen = pkgStream->GetFileLength() - commentTotalLenAll - 2;
121 
122     ret = HashCheck(pkgStream, srcDataLen, hash, path);
123     if (ret != PKG_SUCCESS) {
124         srcDataLen = pkgStream->GetFileLength() - signatureSize - ZIP_EOCD_FIXED_PART_LEN;
125         ret = HashCheck(pkgStream, srcDataLen, hash, path);
126     }
127     PKG_LOGI("verify package signature %s", ret == PKG_SUCCESS ? "successfull" : "failed");
128     return ret;
129 }
130 
GetSignature(const PkgStreamPtr pkgStream,size_t & signatureSize,std::vector<uint8_t> & signature,uint16_t & commentTotalLenAll) const131 int32_t PkgVerifyUtil::GetSignature(const PkgStreamPtr pkgStream, size_t &signatureSize,
132     std::vector<uint8_t> &signature, uint16_t &commentTotalLenAll) const
133 {
134     size_t signatureStart = 0;
135     int32_t ret = ParsePackage(pkgStream, signatureStart, signatureSize, commentTotalLenAll);
136     if (ret != PKG_SUCCESS || signatureSize < PKG_FOOTER_SIZE) {
137         PKG_LOGE("Parse package failed.");
138         UPDATER_LAST_WORD(-1);
139         return -1;
140     }
141 
142     size_t signDataLen = signatureSize - PKG_FOOTER_SIZE;
143     PkgBuffer signData(signDataLen);
144     size_t readLen = 0;
145     ret = pkgStream->Read(signData, signatureStart, signDataLen, readLen);
146     if (ret != PKG_SUCCESS) {
147         PKG_LOGE("read signature failed %s", pkgStream->GetFileName().c_str());
148         UPDATER_LAST_WORD(ret);
149         return ret;
150     }
151     signature.assign(signData.buffer, signData.buffer + readLen);
152 
153     size_t fileLen = pkgStream->GetFileLength();
154     if (fileLen < (signatureSize + ZIP_EOCD_FIXED_PART_LEN)) {
155         PKG_LOGE("Invalid fileLen[%zu] and signature size[%zu]", fileLen, signatureSize);
156         UPDATER_LAST_WORD(PKG_INVALID_PARAM, fileLen, signatureSize);
157         return PKG_INVALID_PARAM;
158     }
159 
160     return PKG_SUCCESS;
161 }
162 
ParsePackage(const PkgStreamPtr pkgStream,size_t & signatureStart,size_t & signatureSize,uint16_t & commentTotalLenAll) const163 int32_t PkgVerifyUtil::ParsePackage(const PkgStreamPtr pkgStream, size_t &signatureStart,
164     size_t &signatureSize, uint16_t &commentTotalLenAll) const
165 {
166     ZipPkgParse zipParse;
167     PkgSignComment pkgSignComment {};
168     int32_t ret = zipParse.ParseZipPkg(pkgStream, pkgSignComment);
169     if (ret != PKG_SUCCESS) {
170         PKG_LOGE("Parse zip package signature failed.");
171         UPDATER_LAST_WORD(ret);
172         return ret;
173     }
174     signatureStart = pkgStream->GetFileLength() - pkgSignComment.signCommentAppendLen;
175     signatureSize = pkgSignComment.signCommentAppendLen;
176     commentTotalLenAll = pkgSignComment.signCommentTotalLen;
177 
178     return PKG_SUCCESS;
179 }
180 
Pkcs7verify(std::vector<uint8_t> & signature,std::vector<uint8_t> & hash) const181 int32_t PkgVerifyUtil::Pkcs7verify(std::vector<uint8_t> &signature, std::vector<uint8_t> &hash) const
182 {
183     Pkcs7SignedData pkcs7;
184 
185     return pkcs7.GetHashFromSignBlock(signature.data(), signature.size(), hash);
186 }
187 
HashCheck(const PkgStreamPtr srcData,const size_t dataLen,const std::vector<uint8_t> & hash,const std::string & path) const188 int32_t PkgVerifyUtil::HashCheck(const PkgStreamPtr srcData, const size_t dataLen,
189     const std::vector<uint8_t> &hash, const std::string &path) const
190 {
191     Updater::UPDATER_INIT_RECORD;
192     struct stat statInfo {};
193     std::string fileInfo = "valid info";
194     if (stat(path.c_str(), &statInfo) != 0) {
195         PKG_LOGE("get file info error");
196     } else {
197         fileInfo = "pkg size is " + std::to_string(statInfo.st_size) +
198             " , pkg last change time is " + ctime(&statInfo.st_mtime);
199         PKG_LOGI(fileInfo.c_str());
200     }
201     if (srcData == nullptr || dataLen == 0) {
202         UPDATER_LAST_WORD(PKG_INVALID_PARAM);
203         return PKG_INVALID_PARAM;
204     }
205 
206     size_t digestLen = hash.size();
207     if (digestLen != PKG_HASH_CONTENT_LEN) {
208         PKG_LOGE("calc pkg sha256 digest failed.");
209         UPDATER_LAST_WORD(PKG_INVALID_PARAM, fileInfo);
210         return PKG_INVALID_PARAM;
211     }
212     std::vector<uint8_t> sourceDigest(digestLen);
213     int32_t ret = CalcSha256Digest(srcData, dataLen, sourceDigest);
214     if (ret != PKG_SUCCESS) {
215         PKG_LOGE("calc pkg sha256 digest failed.");
216         UPDATER_LAST_WORD(ret, fileInfo);
217         return ret;
218     }
219 
220     if (memcmp(hash.data(), sourceDigest.data(), digestLen) != EOK) {
221         PKG_LOGW("Failed to memcmp data.");
222         UPDATER_LAST_WORD(PKG_INVALID_DIGEST, fileInfo);
223         return PKG_INVALID_DIGEST;
224     }
225 
226     return PKG_SUCCESS;
227 }
228 } // namespace Hpackage
229