1/* 2 * Copyright (c) 2024-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#include <climits> 16#include <cstdlib> 17#include <regex> 18#include <unordered_map> 19#include <vector> 20#include <algorithm> 21 22#include "securec.h" 23#include "hap_signer_block_utils.h" 24#include "signature_info.h" 25#include "options.h" 26#include "openssl/pem.h" 27#include "pkcs7_data.h" 28#include "hap_utils.h" 29#include "string_utils.h" 30#include "verify_code_signature.h" 31#include "param_constants.h" 32#include "file_utils.h" 33#include "nlohmann/json.hpp" 34#include "verify_hap.h" 35 36using namespace nlohmann; 37namespace OHOS { 38namespace SignatureTools { 39const int32_t VerifyHap::HEX_PRINT_LENGTH = 3; 40const int32_t VerifyHap::DIGEST_BLOCK_LEN_OFFSET = 8; 41const int32_t VerifyHap::DIGEST_ALGORITHM_OFFSET = 12; 42const int32_t VerifyHap::DIGEST_LEN_OFFSET = 16; 43const int32_t VerifyHap::DIGEST_OFFSET_IN_CONTENT = 20; 44const std::string VerifyHap::HAP_APP_PATTERN = "[^]*.hap$"; 45const std::string VerifyHap::HQF_APP_PATTERN = "[^]*.hqf$"; 46const std::string VerifyHap::HSP_APP_PATTERN = "[^]*.hsp$"; 47const std::string VerifyHap::APP_APP_PATTERN = "[^]*.app$"; 48static constexpr int ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH = 12; 49 50VerifyHap::VerifyHap() : isPrintCert(true) 51{ 52} 53 54VerifyHap::VerifyHap(bool printCert) 55{ 56 isPrintCert = printCert; 57} 58 59void VerifyHap::setIsPrintCert(bool printCert) 60{ 61 isPrintCert = printCert; 62} 63 64bool VerifyHap::HapOutPutPkcs7(PKCS7* p7, const std::string& outPutPath) 65{ 66 std::string p7bContent = StringUtils::Pkcs7ToString(p7); 67 if (p7bContent.empty()) { 68 SIGNATURE_TOOLS_LOGE("p7b to string failed!\n"); 69 return false; 70 } 71 if (FileUtils::Write(p7bContent, outPutPath) < 0) { 72 SIGNATURE_TOOLS_LOGE("p7b write to file falied!\n"); 73 return false; 74 } 75 return true; 76} 77 78bool VerifyHap::outputOptionalBlocks(const std::string& outputProfileFile, const std::string& outputProofFile, 79 const std::string& outputPropertyFile, 80 const std::vector<OptionalBlock>& optionBlocks) 81{ 82 for (auto& optionBlock : optionBlocks) { 83 if (optionBlock.optionalType == HapUtils::HAP_PROFILE_BLOCK_ID) { 84 if (!writeOptionalBytesToFile(optionBlock, outputProfileFile)) { 85 return false; 86 } 87 } else if (optionBlock.optionalType == HapUtils::HAP_PROPERTY_BLOCK_ID) { 88 if (!writeOptionalBytesToFile(optionBlock, outputPropertyFile)) { 89 return false; 90 } 91 } else if (optionBlock.optionalType == HapUtils::HAP_PROOF_OF_ROTATION_BLOCK_ID) { 92 if (!writeOptionalBytesToFile(optionBlock, outputProofFile)) { 93 return false; 94 } 95 } else { 96 SIGNATURE_TOOLS_LOGE("Unsupported Block Id: %d", optionBlock.optionalType); 97 return false; 98 } 99 } 100 return true; 101} 102bool VerifyHap::writeOptionalBytesToFile(const OptionalBlock& optionalBlock, const std::string& path) 103{ 104 if (path.empty()) { 105 return true; 106 } 107 std::string optionBlockString(optionalBlock.optionalBlockValue.GetBufferPtr(), 108 optionalBlock.optionalBlockValue.GetCapacity()); 109 if (FileUtils::Write(optionBlockString, path) < 0) { 110 PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "write optional bytes to file:" + path + " falied!"); 111 return false; 112 } 113 return true; 114} 115 116bool VerifyHap::HapOutPutCertChain(std::vector<X509*>& certs, const std::string& outPutPath) 117{ 118 if (isPrintCert) { 119 if (!PrintCertChainToCmd(certs)) { 120 SIGNATURE_TOOLS_LOGE("print cert chain to cmd failed\n"); 121 return false; 122 } 123 } 124 VerifyHapOpensslUtils::GetOpensslErrorMessage(); 125 SIGNATURE_TOOLS_LOGD("outPutPath = %s", outPutPath.c_str()); 126 std::vector<std::string> certStr; 127 for (auto& cert : certs) { 128 certStr.emplace_back(StringUtils::SubjectToString(cert)); 129 certStr.emplace_back(StringUtils::x509CertToString(cert)); 130 } 131 std::string outPutCertChainContent; 132 for (auto& certstr : certStr) { 133 outPutCertChainContent += certstr; 134 } 135 if (FileUtils::Write(outPutCertChainContent, outPutPath) < 0) { 136 SIGNATURE_TOOLS_LOGE("certChain write to file falied!\n"); 137 return false; 138 } 139 return true; 140} 141 142int32_t VerifyHap::Verify(const std::string& filePath, Options* options) 143{ 144 SIGNATURE_TOOLS_LOGD("Start Verify"); 145 std::string standardFilePath; 146 if (!CheckFilePath(filePath, standardFilePath)) { 147 SIGNATURE_TOOLS_LOGE("Check file path%s failed", filePath.c_str()); 148 return IO_ERROR; 149 } 150 RandomAccessFile hapFile; 151 if (!hapFile.Init(standardFilePath)) { 152 SIGNATURE_TOOLS_LOGE("%s init failed", standardFilePath.c_str()); 153 return ZIP_ERROR; 154 } 155 int32_t resultCode = Verify(hapFile, options, filePath); 156 if (resultCode != RET_OK) { 157 PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, standardFilePath + " verify failed"); 158 } 159 return resultCode; 160} 161 162bool VerifyHap::CheckFilePath(const std::string& filePath, std::string& standardFilePath) 163{ 164 char path[PATH_MAX] = { 0x00 }; 165 if (filePath.size() > PATH_MAX || realpath(filePath.c_str(), path) == nullptr) { 166 PrintErrorNumberMsg("IO_ERROR", IO_ERROR, 167 filePath + " does not exist or is over " + std::to_string(PATH_MAX) + " chars"); 168 return false; 169 } 170 standardFilePath = std::string(path); 171 std::string standardFilePathTmp = std::string(path); 172 std::transform(standardFilePathTmp.begin(), standardFilePathTmp.end(), standardFilePathTmp.begin(), 173 [](unsigned char c) { return std::tolower(c); }); 174 bool ret = (!std::regex_match(standardFilePathTmp, std::regex(HAP_APP_PATTERN)) && 175 !std::regex_match(standardFilePathTmp, std::regex(HSP_APP_PATTERN)) && 176 !std::regex_match(standardFilePathTmp, std::regex(APP_APP_PATTERN)) && 177 !std::regex_match(standardFilePathTmp, std::regex(HQF_APP_PATTERN))); 178 if (ret) { 179 PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR, 180 "only support format is [hap, hqf, hsp, app]"); 181 return false; 182 } 183 return true; 184} 185 186int32_t VerifyHap::Verify(RandomAccessFile& hapFile, Options* options, const std::string& filePath) 187{ 188 SignatureInfo hapSignInfo; 189 if (!HapSignerBlockUtils::FindHapSignature(hapFile, hapSignInfo)) { 190 return ZIP_ERROR; 191 } 192 193 if (CheckCodeSign(filePath, hapSignInfo.optionBlocks) == false) { 194 SIGNATURE_TOOLS_LOGE("check coode sign failed\n"); 195 return VERIFY_ERROR; 196 } 197 198 Pkcs7Context pkcs7Context; 199 if (!VerifyAppPkcs7(pkcs7Context, hapSignInfo.hapSignatureBlock)) { 200 return PARSE_ERROR; 201 } 202 203 if (!GetDigestAndAlgorithm(pkcs7Context)) { 204 SIGNATURE_TOOLS_LOGE("Get digest failed"); 205 return PARSE_ERROR; 206 } 207 208 STACK_OF(X509_CRL)* x509Crl = nullptr; 209 if (!VerifyHapOpensslUtils::GetCrlStack(pkcs7Context.p7, x509Crl)) { 210 SIGNATURE_TOOLS_LOGE("Get Crl stack failed"); 211 return PARSE_ERROR; 212 } 213 214 if (!VerifyCertOpensslUtils::VerifyCrl(pkcs7Context.certChain[0], x509Crl, pkcs7Context)) { 215 SIGNATURE_TOOLS_LOGE("Verify Crl stack failed"); 216 return VERIFY_ERROR; 217 } 218 219 if (!HapSignerBlockUtils::VerifyHapIntegrity(pkcs7Context, hapFile, hapSignInfo)) { 220 SIGNATURE_TOOLS_LOGE("Verify Integrity failed"); 221 return VERIFY_ERROR; 222 } 223 if (!HapOutPutCertChain(pkcs7Context.certChain[0], 224 options->GetString(Options::OUT_CERT_CHAIN))) { 225 SIGNATURE_TOOLS_LOGE("out put cert chain failed"); 226 return IO_ERROR; 227 } 228 229 if (!outputOptionalBlocks(options->GetString(ParamConstants::PARAM_VERIFY_PROFILE_FILE), 230 options->GetString(ParamConstants::PARAM_VERIFY_PROOF_FILE), 231 options->GetString(ParamConstants::PARAM_VERIFY_PROPERTY_FILE), 232 hapSignInfo.optionBlocks)) { 233 SIGNATURE_TOOLS_LOGE("output Optional Blocks failed"); 234 return IO_ERROR; 235 } 236 return RET_OK; 237} 238 239bool VerifyHap::CheckCodeSign(const std::string& hapFilePath, 240 const std::vector<OptionalBlock>& optionalBlocks)const 241{ 242 std::unordered_map<int, ByteBuffer> map; 243 for (const OptionalBlock& block : optionalBlocks) { 244 map.emplace(block.optionalType, block.optionalBlockValue); 245 } 246 bool codeSignFlag = map.find(HapUtils::HAP_PROPERTY_BLOCK_ID) != map.end() && 247 map[HapUtils::HAP_PROPERTY_BLOCK_ID].GetCapacity() > 0; 248 if (codeSignFlag) { 249 ByteBuffer propertyBlockArray = map[HapUtils::HAP_PROPERTY_BLOCK_ID]; 250 std::vector<std::string> fileNameArray = StringUtils::SplitString(hapFilePath, '.'); 251 if (fileNameArray.size() < ParamConstants::FILE_NAME_MIN_LENGTH) { 252 PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "ZIP64 format not supported."); 253 return false; 254 } 255 256 if (propertyBlockArray.GetCapacity() < ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH) 257 return false; 258 uint32_t blockType; 259 propertyBlockArray.GetUInt32(OFFSET_ZERO, blockType); 260 uint32_t blockLength; 261 propertyBlockArray.GetUInt32(OFFSET_FOUR, blockLength); 262 uint32_t blockOffset; 263 propertyBlockArray.GetUInt32(OFFSET_EIGHT, blockOffset); 264 265 if (blockType != HapUtils::HAP_CODE_SIGN_BLOCK_ID) { 266 PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "code sign data not exist in hap " + hapFilePath); 267 return false; 268 } 269 auto ite = map.find(HapUtils::HAP_PROFILE_BLOCK_ID); 270 if (ite == map.end()) 271 return false; 272 ByteBuffer profileArray = ite->second; 273 std::string profileArray_(profileArray.GetBufferPtr(), profileArray.GetCapacity()); 274 std::string profileContent; 275 if (GetProfileContent(profileArray_, profileContent) < 0) { 276 SIGNATURE_TOOLS_LOGE("get profile content failed, file: %s", hapFilePath.c_str()); 277 return false; 278 } 279 std::string suffix = fileNameArray[fileNameArray.size() - 1]; 280 bool isCodeSign = VerifyCodeSignature::VerifyHap(hapFilePath, blockOffset, blockLength, 281 suffix, profileContent); 282 if (!isCodeSign) { 283 SIGNATURE_TOOLS_LOGE("verify codesign failed, file: %s", hapFilePath.c_str()); 284 return false; 285 } 286 SIGNATURE_TOOLS_LOGI("verify codesign success."); 287 return true; 288 } 289 SIGNATURE_TOOLS_LOGI("can not find codesign block."); 290 return true; 291} 292 293int VerifyHap::GetProfileContent(const std::string profile, std::string& ret) 294{ 295 json obj = json::parse(profile, nullptr, false); 296 if (!obj.is_discarded() && obj.is_structured()) { 297 ret = profile; 298 return 0; 299 } 300 PKCS7Data p7Data; 301 if (p7Data.Parse(profile) < 0) { 302 ret = profile; 303 return -1; 304 } 305 if (p7Data.Verify() < 0) { 306 PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR, 307 "Verify profile pkcs7 failed! Profile is invalid"); 308 ret = profile; 309 return -1; 310 } 311 if (p7Data.GetContent(ret) < 0) { 312 PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR, 313 "Check profile failed, signed profile content is not byte array"); 314 ret = profile; 315 return -1; 316 } 317 return 0; 318} 319 320bool VerifyHap::VerifyAppPkcs7(Pkcs7Context& pkcs7Context, const ByteBuffer& hapSignatureBlock) 321{ 322 const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(hapSignatureBlock.GetBufferPtr()); 323 uint32_t pkcs7Len = static_cast<unsigned int>(hapSignatureBlock.GetCapacity()); 324 if (!VerifyHapOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) { 325 SIGNATURE_TOOLS_LOGE("parse pkcs7 failed"); 326 return false; 327 } 328 if (!VerifyHapOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) { 329 SIGNATURE_TOOLS_LOGE("GetCertChains from pkcs7 failed"); 330 return false; 331 } 332 if (!VerifyHapOpensslUtils::VerifyPkcs7(pkcs7Context)) { 333 SIGNATURE_TOOLS_LOGE("verify signature failed"); 334 return false; 335 } 336 return true; 337} 338 339bool VerifyHap::GetDigestAndAlgorithm(Pkcs7Context& digest) 340{ 341 /* 342 * contentinfo format: 343 * int: version 344 * int: block number 345 * digest blocks: 346 * each digest block format: 347 * int: length of sizeof(digestblock) - 4 348 * int: Algorithm ID 349 * int: length of digest 350 * byte[]: digest 351 */ 352 /* length of sizeof(digestblock - 4) */ 353 int32_t digestBlockLen; 354 if (!digest.content.GetInt32(DIGEST_BLOCK_LEN_OFFSET, digestBlockLen)) { 355 SIGNATURE_TOOLS_LOGE("get digestBlockLen failed"); 356 return false; 357 } 358 /* Algorithm ID */ 359 if (!digest.content.GetInt32(DIGEST_ALGORITHM_OFFSET, digest.digestAlgorithm)) { 360 SIGNATURE_TOOLS_LOGE("get digestAlgorithm failed"); 361 return false; 362 } 363 /* length of digest */ 364 int32_t digestlen; 365 if (!digest.content.GetInt32(DIGEST_LEN_OFFSET, digestlen)) { 366 SIGNATURE_TOOLS_LOGE("get digestlen failed"); 367 return false; 368 } 369 int32_t sum = sizeof(digestlen) + sizeof(digest.digestAlgorithm) + digestlen; 370 if (sum != digestBlockLen) { 371 SIGNATURE_TOOLS_LOGE("digestBlockLen: %d is not equal to sum: %d", 372 digestBlockLen, sum); 373 return false; 374 } 375 /* set position to the digest start point */ 376 digest.content.SetPosition(DIGEST_OFFSET_IN_CONTENT); 377 /* set limit to the digest end point */ 378 digest.content.SetLimit(DIGEST_OFFSET_IN_CONTENT + digestlen); 379 digest.content.Slice(); 380 return true; 381} 382 383int32_t VerifyHap::WriteVerifyOutput(Pkcs7Context& pkcs7Context, std::vector<int8_t>& profile, Options* options) 384{ 385 if (pkcs7Context.certChain.size() > 0) { 386 bool flag = VerifyHap::HapOutPutCertChain(pkcs7Context.certChain[0], 387 options->GetString(Options::OUT_CERT_CHAIN)); 388 if (!flag) { 389 SIGNATURE_TOOLS_LOGE("out put cert chain failed"); 390 return IO_ERROR; 391 } 392 } 393 if (pkcs7Context.p7 == nullptr) { 394 std::string p7bContent(profile.begin(), profile.end()); 395 bool writeFlag = FileUtils::Write(p7bContent, options->GetString(Options::OUT_PROFILE)) < 0; 396 if (writeFlag) { 397 SIGNATURE_TOOLS_LOGE("p7b write to file falied!\n"); 398 return IO_ERROR; 399 } 400 return RET_OK; 401 } 402 bool pkcs7flag = VerifyHap::HapOutPutPkcs7(pkcs7Context.p7, options->GetString(Options::OUT_PROFILE)); 403 if (!pkcs7flag) { 404 SIGNATURE_TOOLS_LOGE("out put p7b failed"); 405 return IO_ERROR; 406 } 407 return RET_OK; 408} 409 410bool VerifyHap::PrintCertChainToCmd(std::vector<X509*>& certChain) 411{ 412 BIO* outFd = BIO_new_fp(stdout, BIO_NOCLOSE); 413 if (!outFd) { 414 PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "The stdout stream may have errors"); 415 return false; 416 } 417 uint64_t format = XN_FLAG_SEP_COMMA_PLUS; // Print according to RFC2253 418 uint64_t content = X509_FLAG_NO_EXTENSIONS | X509_FLAG_NO_ATTRIBUTES | X509_FLAG_NO_HEADER | X509_FLAG_NO_SIGDUMP; 419 int num = 0; 420 for (auto& cert : certChain) { 421 PrintMsg("+++++++++++++++++++++++++++++++++certificate #" + std::to_string(num) + 422 "+++++++++++++++++++++++++++++++++++++"); 423 if (!X509_print_ex(outFd, cert, format, content)) { 424 VerifyHapOpensslUtils::GetOpensslErrorMessage(); 425 SIGNATURE_TOOLS_LOGE("print x509 cert to cmd failed"); 426 BIO_free(outFd); 427 return false; 428 } 429 ++num; 430 } 431 BIO_free(outFd); 432 return true; 433} 434} // namespace SignatureTools 435} // namespace OHOS