diff --git a/hapsigntool/hap_sign_tool/src/main/java/com/ohos/hapsigntool/HapSignTool.java b/hapsigntool/hap_sign_tool/src/main/java/com/ohos/hapsigntool/HapSignTool.java index 3d23079464ece5aa9bcb9a9b163e81a030ea3d5d..638d323e1e64703765302a28f1e251515b4a11ed 100644 --- a/hapsigntool/hap_sign_tool/src/main/java/com/ohos/hapsigntool/HapSignTool.java +++ b/hapsigntool/hap_sign_tool/src/main/java/com/ohos/hapsigntool/HapSignTool.java @@ -333,7 +333,10 @@ public final class HapSignTool { private static boolean runVerifyApp(Options params, ServiceApi api) { params.required(Options.IN_FILE, Options.OUT_CERT_CHAIN, Options.OUT_PROFILE); - FileUtils.validFileType(params.getString(Options.IN_FILE), "hap"); + String inForm = params.getString(Options.IN_FORM, "zip"); + if (!informList.contains(inForm)) { + CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "inForm params must is " + informList); + } FileUtils.validFileType(params.getString(Options.OUT_CERT_CHAIN), "cer"); FileUtils.validFileType(params.getString(Options.OUT_PROFILE), "p7b"); return api.verifyHap(params); diff --git a/hapsigntool/hap_sign_tool/src/main/resources/help.txt b/hapsigntool/hap_sign_tool/src/main/resources/help.txt index b1909942472633d1bbe0b0fadfd0c12f9700cd7e..c1e75cfcdc139bd9a63e5776fc24b273891877cc 100644 --- a/hapsigntool/hap_sign_tool/src/main/resources/help.txt +++ b/hapsigntool/hap_sign_tool/src/main/resources/help.txt @@ -169,13 +169,13 @@ USAGE: [options] -appCertFile : application signature certificate file, required fields on localSign mode, optional fields on remoteSign mode; -profileFile : signed Provision Profile file, p7b format, required fields; -profileSigned : indicates whether the profile file has a signature.The options are as follows: 1:yes; 0:no; default value:1. optional fields; - -inFile : input original application package file, hap or bin format, required fields; + -inFile : input original application package file, .hap, .bin, and .elf format, required fields; -signAlg : signature algorithm, required fields, including SHA256withRSA/SHA384withRSA/SHA256withECDSA/SHA384withECDSA; -keystoreFile : keystore file, if signature mode is localSign, required fields on localSign mode, JKS or P12 format; -keystorePwd : keystore password, optional fields on localSign mode; -outFile : output the signed Provision Profile file, required fields; -extCfgFile : Extend Profile, optional fields; - -inForm : enter the format of the original file, the format is .zip or .bin; + -inForm : Enter the format of the original file. The supported file formats include .zip, .bin, and .elf.; -compatibleVersion : min compatible api version for running app, required fields while input original application package file format is hap; -signServer : remote signer plugin, required fields on remoteSign mode; -signerPlugin : remote sign service url, required fields on remoteSign mode; @@ -193,6 +193,7 @@ USAGE: [options] -outCertChain : signed certificate chain file, required fields; -outProfile : profile file in application package, required fields; -extCfgFile : Extend Profile, optional fields; + -inForm : Enter the format of the original file. The supported file formats include .zip, .bin, and .elf.; EXAMPLE: verify-app -inFile "D:\OH\app1-signed.hap" -outCertChain "outCertChain.cer" -outProfile "outprofile.p7b" diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/api/SignToolServiceImpl.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/api/SignToolServiceImpl.java index d97dc144dbc7cab2185e43a8565bfd84db7a1d8e..331ba8b3d36963b4e6367a7cbc0f176ddcb79209 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/api/SignToolServiceImpl.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/api/SignToolServiceImpl.java @@ -23,12 +23,14 @@ import com.ohos.hapsigntool.error.VerifyException; import com.ohos.hapsigntool.hap.provider.LocalJKSSignProvider; import com.ohos.hapsigntool.hap.provider.RemoteSignProvider; import com.ohos.hapsigntool.hap.provider.SignProvider; +import com.ohos.hapsigntool.hap.verify.VerifyElf; import com.ohos.hapsigntool.hap.verify.VerifyHap; import com.ohos.hapsigntool.profile.ProfileSignTool; import com.ohos.hapsigntool.profile.VerifyHelper; import com.ohos.hapsigntool.profile.model.VerificationResult; import com.ohos.hapsigntool.utils.CertUtils; import com.ohos.hapsigntool.utils.FileUtils; +import com.ohos.hapsigntool.utils.ParamConstants; import com.ohos.hapsigntool.utils.ProfileUtils; import com.ohos.hapsigntool.utils.StringUtils; @@ -324,8 +326,13 @@ public class SignToolServiceImpl implements ServiceApi { @Override public boolean verifyHap(Options options) { - VerifyHap hapVerify = new VerifyHap(); - return hapVerify.verify(options); + if ("zip".equals(options.getOrDefault(ParamConstants.PARAM_IN_FORM, "zip"))) { + VerifyHap hapVerify = new VerifyHap(); + return hapVerify.verify(options); + } else { + VerifyElf verifyElf = new VerifyElf(); + return verifyElf.verify(options); + } } /** diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/ElfSignBlock.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/ElfSignBlock.java index e399244b4a26d3f6762f753a489459a65d7ec0d4..959f9ed87a911beca73a3e3caf6c108590f68535 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/ElfSignBlock.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/ElfSignBlock.java @@ -185,6 +185,7 @@ public class ElfSignBlock { throw new VerifyCodeSignException("Invalid fs-verify descriptor with signature length of ElfSignBlock"); } byte[] fsdArray = new byte[FsVerityDescriptor.DESCRIPTOR_SIZE]; + bf.get(fsdArray); FsVerityDescriptor fsd = FsVerityDescriptor.fromByteArray(fsdArray); if (inFsdLength != fsd.getSignSize() + FsVerityDescriptor.DESCRIPTOR_SIZE) { throw new VerifyCodeSignException("Invalid sign size of ElfSignBlock"); diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java index 98cdd8964e9783ffa0d705c63f091e2aac93e801..5531b7f837b0b55054bc7a1db9291d2e94f38916 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java @@ -160,7 +160,7 @@ public class FsVerityDescriptor { builder.setVersion(inFsVersion).setHashAlgorithm(inFsHashAlgorithm).setLog2BlockSize(inLog2BlockSize); byte inSaltSize = bf.get(); int inSignSize = bf.getInt(); - int inDataSize = bf.getInt(); + long inDataSize = bf.getLong(); byte[] inRootHash = new byte[FsVerityDescriptor.ROOT_HASH_FILED_SIZE]; bf.get(inRootHash); builder.setSaltSize(inSaltSize).setSignSize(inSignSize).setFileSize(inDataSize).setRawRootHash(inRootHash); diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java index 9fbf0553407e6a8a13fd84e190b577f5bcbdbb78..38b341af27cea2365b79c89f5f9f0c24ec46081b 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java @@ -116,14 +116,16 @@ public class VerifyCodeSignature { * @param offset start position of code sign block based on the start of the elf file * @param length byte size of code sign block * @param fileFormat elf or hqf or hsp, etc. + * @param profileContent profile json string * @return true if signature verify succeed and false otherwise * @throws IOException If an input or output exception occurred * @throws VerifyCodeSignException parsing result invalid * @throws FsVerityDigestException if fs-verity digest generation failed * @throws CMSException if signature verify failed + * @throws ProfileException if verify profile failed */ - public static boolean verifyElf(File file, long offset, long length, String fileFormat) - throws IOException, VerifyCodeSignException, FsVerityDigestException, CMSException { + public static boolean verifyElf(File file, long offset, long length, String fileFormat, String profileContent) + throws IOException, VerifyCodeSignException, FsVerityDigestException, CMSException, ProfileException { if (!CodeSigning.SUPPORT_BIN_FILE_FORM.contains(fileFormat)) { LOGGER.info("Not elf file, skip code signing verify"); return true; @@ -144,6 +146,10 @@ public class VerifyCodeSignature { verifySingleFile(signedElf, elfSignBlock.getDataSize(), elfSignBlock.getSignature(), elfSignBlock.getTreeOffset(), merkleTree); } + if (profileContent != null) { + Pair pairResult = HapUtils.parseAppIdentifier(profileContent); + checkOwnerID(elfSignBlock.getSignature(), pairResult.getFirst(), pairResult.getSecond()); + } return true; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/ElfBlockData.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/ElfBlockData.java new file mode 100644 index 0000000000000000000000000000000000000000..b8cdd1ad6ed0b53ae902d9485f8235f7533ac6dc --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/ElfBlockData.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.hapsigntool.hap.entity; + +/** + * Elf block info + * + * @since 2023/11/20 + */ +public class ElfBlockData { + int blockNum; + int blockStart; + + public ElfBlockData(int blockNum, int blockStart) { + this.blockNum = blockNum; + this.blockStart = blockStart; + } + + public int getBlockNum() { + return blockNum; + } + + public void setBlockNum(int blockNum) { + this.blockNum = blockNum; + } + + public int getBlockStart() { + return blockStart; + } + + public void setBlockStart(int blockStart) { + this.blockStart = blockStart; + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwBlockHead.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwBlockHead.java index 8a270554b812a10e7d5501885db894a6333bd0e1..c790e885625a4e9d09e75bd74bd77a96849f5899 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwBlockHead.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwBlockHead.java @@ -24,9 +24,15 @@ import java.nio.ByteOrder; * @since 2023/11/07 */ public class HwBlockHead { - private static final int BLOCK_LEN = 8; // bin block length is 8 byte + /** + * bin file sign block length is 8 byte + */ + public static final int BLOCK_LEN = 8; - private static final int ELF_BLOCK_LEN = 12; // elf block length is 12 byte + /** + * elf file sign block length is 12 byte + */ + public static final int ELF_BLOCK_LEN = 12; private static final int BIT_SIZE = 8; diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwSignHead.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwSignHead.java index 0010344c927ed9dbf85d413939d0629074dce365..46ce01fe83e56c0f74f4367349864afdf3f538c9 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwSignHead.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/HwSignHead.java @@ -32,11 +32,20 @@ public class HwSignHead { */ public static final int SIGN_HEAD_LEN = 32; - private static final char[] MAGIC = "hw signed app ".toCharArray(); // 16Bytes-Magic + /** + * sign hap magic string 16Bytes-Magic + */ + public static final char[] MAGIC = "hw signed app ".toCharArray(); - private static final char[] ELF_MAGIC = "elf sign block ".toCharArray(); // 16Bytes-Magic + /** + * sign elf magic string 16Bytes-Magic + */ + public static final char[] ELF_MAGIC = "elf sign block ".toCharArray(); - private static final char[] VERSION = "1000".toCharArray(); // 4-Bytes, version is 1.0.0.0 + /** + * sign block version 4-Bytes, version is 1.0.0.0 + */ + public static final char[] VERSION = "1000".toCharArray(); private static final int NUM_OF_BLOCK = 2; // number of sub-block diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/SigningBlock.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/SigningBlock.java index 0794f0d781f781bdeafbf385b88bb149c2f34728..663dd56ee4a81f39bc2fa96a21ee394c43de0cea 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/SigningBlock.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/entity/SigningBlock.java @@ -25,6 +25,14 @@ public class SigningBlock { private int length; private byte[] value; + private int offset; + + /** + * Init Signing Block type and value + * + * @param type signing type + * @param value signing value + */ public SigningBlock(int type, byte[] value) { super(); this.type = type; @@ -32,6 +40,21 @@ public class SigningBlock { this.value = value; } + /** + * Init Signing Block type and value + * + * @param type signing type + * @param value signing value + * @param offset signing block offset + */ + public SigningBlock(int type, byte[] value, int offset) { + super(); + this.type = type; + this.length = value.length; + this.value = value; + this.offset = offset; + } + public int getType() { return type; } @@ -43,4 +66,8 @@ public class SigningBlock { public byte[] getValue() { return value; } + + public int getOffset() { + return offset; + } } \ No newline at end of file diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignBin.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignBin.java index 522bf44fc6d4fbc381453b2c755faa4196b3c0c1..670eea6c335fa89964638c1aced1a3217cebd910 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignBin.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignBin.java @@ -60,6 +60,10 @@ public class SignBin { public static boolean sign(SignerConfig signerConfig, Map signParams) { boolean result = false; /* 1. Make block head, write to output file. */ + String codesign = signParams.get(ParamConstants.PARAM_SIGN_CODE); + if (ParamConstants.ProfileSignFlag.ENABLE_SIGN_CODE.getSignFlag().equals(codesign)) { + LOGGER.warn("can not sign bin with codesign"); + } String inputFile = signParams.get(ParamConstants.PARAM_BASIC_INPUT_FILE); String outputFile = signParams.get(ParamConstants.PARAM_BASIC_OUTPUT_FILE); String profileFile = signParams.get(ParamConstants.PARAM_BASIC_PROFILE); diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java index 17212f672b42b35553c4634eb91809b24087d148..cb95b491e8196d524380e1decabf35c101b22ba4 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java @@ -30,32 +30,40 @@ import com.ohos.hapsigntool.utils.FileUtils; import com.ohos.hapsigntool.utils.ParamConstants; import com.ohos.hapsigntool.utils.ParamProcessUtil; import com.ohos.hapsigntool.utils.StringUtils; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.DataOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; /** - * LiteOS bin file Signature signer. + * elf file Signature signer. * - * @since 2021/12/21 + * @since 2023/11/21 */ public class SignElf { + /** + * codesign sign block type + */ + public static final char CODESIGN_BLOCK_TYPE = 3; + private static final Logger LOGGER = LogManager.getLogger(SignElf.class); private static final String CODESIGN_OFF = "0"; - private static final char CODESIGN_BLOCK_TYPE = 3; - private static int blockNum = 0; + private static final int PAGE_SIZE = 4096; + + private static final int FILE_BUFFER_BLOCK = 16384; + /** * Constructor of Method */ @@ -63,27 +71,32 @@ public class SignElf { } /** - * Sign the bin file. + * Sign the elf file. * - * @param signerConfig Config of the bin file to be signed. - * @param signParams The input parameters of sign bin. + * @param signerConfig Config of the elf file to be signed. + * @param signParams The input parameters of sign elf. * @return true if sign successfully; false otherwise. */ public static boolean sign(SignerConfig signerConfig, Map signParams) { boolean isSuccess = false; /* 1. Make block head, write to output file. */ String inputFile = signParams.get(ParamConstants.PARAM_BASIC_INPUT_FILE); + String tmpFile = alignFileBy4kBytes(inputFile); + if (tmpFile == null) { + LOGGER.error("copy input File failed"); + return isSuccess; + } String outputFile = signParams.get(ParamConstants.PARAM_BASIC_OUTPUT_FILE); String profileSigned = signParams.get(ParamConstants.PARAM_BASIC_PROFILE_SIGNED); - if (!writeBlockDataToFile(signerConfig, inputFile, outputFile, profileSigned, signParams)) { - LOGGER.error("The block head data made failed."); + if (!writeBlockDataToFile(signerConfig, tmpFile, outputFile, profileSigned, signParams)) { + LOGGER.error("The block head data made failed.`"); ParamProcessUtil.delDir(new File(outputFile)); - return false; + return isSuccess; } LOGGER.info("The block head data made success."); /* 2. Make sign data, and write to output file */ - if (!writeSignHeadDataToOutputFile(inputFile, outputFile, blockNum)) { + if (!writeSignHeadDataToOutputFile(tmpFile, outputFile, blockNum)) { LOGGER.error("The sign head data made failed."); ParamProcessUtil.delDir(new File(outputFile)); } else { @@ -92,6 +105,38 @@ public class SignElf { return isSuccess; } + private static String alignFileBy4kBytes(String inputFile) { + String tmp = "tmpFile" + new Date().getTime(); + File tmpFile = new File(tmp); + try { + tmpFile.createNewFile(); + } catch (IOException e) { + LOGGER.error("create tmp file Failed"); + return null; + } + try (FileOutputStream output = new FileOutputStream(tmpFile); + FileInputStream input = new FileInputStream(inputFile)) { + byte[] buffer = new byte[FILE_BUFFER_BLOCK]; + int read; + while ((read = input.read(buffer)) != FileUtils.FILE_END) { + output.write(buffer, 0, read); + } + + long addLength = PAGE_SIZE - (tmpFile.length() % PAGE_SIZE); + if (isLongOverflowInteger(addLength)) { + LOGGER.error("File alignment error"); + return null; + } + byte[] bytes = new byte[(int) addLength]; + java.util.Arrays.fill(bytes, (byte) 0); + FileUtils.writeByteToOutFile(bytes, tmp); + } catch (IOException e) { + LOGGER.error("copy inFile Failed"); + return null; + } + return tmp; + } + private static boolean writeBlockDataToFile(SignerConfig signerConfig, String inputFile, String outputFile, String profileSigned, Map signParams) { try { @@ -101,14 +146,14 @@ public class SignElf { long binFileLen = FileUtils.getFileLen(inputFile); if (binFileLen == -1) { - LOGGER.error("file length is invalid, bin file len: " + binFileLen); + LOGGER.error("file length is invalid, elf file len: " + binFileLen); throw new IOException(); } // 1. generate sign data if (!StringUtils.isEmpty(signParams.get(ParamConstants.PARAM_BASIC_PROFILE))) { signDataList.add(generateProfileSignByte(profileFile, profileSigned)); } - blockNum = signDataList.size(); + blockNum = signDataList.size() + 1; // other sign block num + codesign block 1 SignBlockData codeSign = generateCodeSignByte(signerConfig, signParams, inputFile, blockNum, binFileLen); if (codeSign != null) { signDataList.add(0, codeSign); @@ -160,7 +205,7 @@ public class SignElf { } } } catch (IOException e) { - LOGGER.error("writeSignedBin failed.", e); + LOGGER.error("writeSignedElf failed.", e); return false; } return true; diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/HapVerify.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/HapVerify.java index db6eec7d04ccc2bb5f4613025f9776878afcda33..a889fc9c67586920213c49af039563eb53fb7d38 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/HapVerify.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/HapVerify.java @@ -93,6 +93,15 @@ public class HapVerify { private boolean isPrintCert; + /** + * Init Zip HapVerify + * + * @param beforeApkSigningBlock beforeApkSigningBlock + * @param signatureSchemeBlock signatureSchemeBlock + * @param centralDirectoryBlock centralDirectoryBlock + * @param eocd eocd + * @param optionalBlocks optionalBlocks + */ public HapVerify( ZipDataInput beforeApkSigningBlock, ByteBuffer signatureSchemeBlock, @@ -106,6 +115,12 @@ public class HapVerify { this.optionalBlocks = optionalBlocks; } + /** + * init HapVerify + */ + public HapVerify() { + } + /** * Verify hap signature. * @@ -115,6 +130,16 @@ public class HapVerify { return parserSigner(signatureSchemeBlock); } + /** + * Verify elf signature. + * + * @param profile profile byte + * @return verify result. + */ + public VerifyResult verifyElfProfile(byte[] profile) { + return parserSigner(ByteBuffer.wrap(profile), false); + } + public void setIsPrintCert(boolean isPrintCert) { this.isPrintCert = isPrintCert; } @@ -197,6 +222,10 @@ public class HapVerify { } private VerifyResult parserSigner(ByteBuffer signer) { + return parserSigner(signer, true); + } + + private VerifyResult parserSigner(ByteBuffer signer, boolean verifyContent) { byte[] signingBlock = new byte[signer.remaining()]; signer.get(signingBlock); try { @@ -204,7 +233,9 @@ public class HapVerify { List certificates = getCertChain(cmsSignedData); List crlList = getCrlList(cmsSignedData); verifyCRLs(crlList, certificates); - checkContentDigest(cmsSignedData); + if (verifyContent) { + checkContentDigest(cmsSignedData); + } List signerInfos = getSignerInformations(cmsSignedData); VerifyResult result = new VerifyResult(true, VerifyResult.RET_SUCCESS, "Verify success"); result.setCrls(crlList); @@ -214,7 +245,7 @@ public class HapVerify { result.setOptionalBlocks(optionalBlocks); return result; } catch (VerifyHapException e) { - LOGGER.error("Verify Hap error!", e); + LOGGER.error("Verify profile error!", e); return new VerifyResult(false, VerifyResult.RET_UNKNOWN_ERROR, e.getMessage()); } } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyElf.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyElf.java new file mode 100644 index 0000000000000000000000000000000000000000..1a1fc80f736f96e236a115287ced1244cd90e884 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyElf.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ohos.hapsigntool.hap.verify; + +import com.ohos.hapsigntool.api.model.Options; +import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; +import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException; +import com.ohos.hapsigntool.codesigning.sign.VerifyCodeSignature; +import com.ohos.hapsigntool.hap.entity.ElfBlockData; +import com.ohos.hapsigntool.hap.entity.HwBlockHead; +import com.ohos.hapsigntool.hap.entity.HwSignHead; +import com.ohos.hapsigntool.hap.entity.SignatureBlockTypes; +import com.ohos.hapsigntool.hap.entity.SigningBlock; +import com.ohos.hapsigntool.hap.exception.ProfileException; +import com.ohos.hapsigntool.hap.sign.SignElf; +import com.ohos.hapsigntool.utils.FileUtils; +import com.ohos.hapsigntool.utils.ParamConstants; +import com.ohos.hapsigntool.utils.StringUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class of verify ELF. + * + * @since 2023/11/23 + */ +public class VerifyElf { + private static final Logger LOGGER = LogManager.getLogger(VerifyElf.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private static String getProfileContent(byte[] profile) throws ProfileException { + try { + CMSSignedData cmsSignedData = new CMSSignedData(profile); + if (!VerifyUtils.verifyCmsSignedData(cmsSignedData)) { + throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid"); + } + Object contentObj = cmsSignedData.getSignedContent().getContent(); + if (!(contentObj instanceof byte[])) { + throw new ProfileException("Check profile failed, signed profile content is not byte array!"); + } + return new String((byte[]) contentObj, StandardCharsets.UTF_8); + } catch (CMSException e) { + return new String(profile, StandardCharsets.UTF_8); + } + } + + + /** + * Check whether parameters are valid + * + * @param options input parameters used to verify ELF. + * @return true, if all parameters are valid. + */ + public boolean checkParams(Options options) { + if (!options.containsKey(ParamConstants.PARAM_VERIFY_CERTCHAIN_FILE)) { + LOGGER.error("Missing parameter: {}", ParamConstants.PARAM_VERIFY_CERTCHAIN_FILE); + return false; + } + if (!options.containsKey(ParamConstants.PARAM_VERIFY_PROFILE_FILE)) { + LOGGER.error("Missing parameter: {}", ParamConstants.PARAM_VERIFY_PROFILE_FILE); + return false; + } + if (!options.containsKey(ParamConstants.PARAM_VERIFY_PROOF_FILE)) { + LOGGER.warn("Missing parameter: {}", ParamConstants.PARAM_VERIFY_PROOF_FILE); + } + return true; + } + + /** + * verify elf file. + * + * @param options input parameters used to verify elf. + * @return true, if verify successfully. + */ + public boolean verify(Options options) { + VerifyResult verifyResult; + try { + if (!checkParams(options)) { + LOGGER.error("Check params failed!"); + throw new IOException(); + } + String filePath = options.getString(ParamConstants.PARAM_BASIC_INPUT_FILE); + if (StringUtils.isEmpty(filePath)) { + LOGGER.error("Not found verify file path!"); + throw new IOException(); + } + File signedFile = new File(filePath); + if (!checkSignFile(signedFile)) { + LOGGER.error("Check input signature ELF false!"); + throw new IOException(); + } + verifyResult = verifyElf(filePath); + if (!verifyResult.isVerified()) { + LOGGER.error("verify: {}", verifyResult.getMessage()); + throw new IOException(); + } + String outputCertPath = options.getString(ParamConstants.PARAM_VERIFY_CERTCHAIN_FILE); + if (verifyResult.getCertificates() != null) { + writeCertificate(outputCertPath, verifyResult.getCertificates()); + } + } catch (IOException e) { + LOGGER.error("Write certificate chain error", e); + return false; + } + + String outputProfileFile = options.getString(ParamConstants.PARAM_VERIFY_PROFILE_FILE); + try { + outputOptionalBlocks(outputProfileFile, verifyResult); + } catch (IOException e) { + LOGGER.error("Output optional blocks error", e); + return false; + } + + LOGGER.info("verify: {}", verifyResult.getMessage()); + return true; + } + + private void writeCertificate(String destFile, List certificates) throws IOException { + try (JcaPEMWriter writer = new JcaPEMWriter(new FileWriter(destFile))) { + for (final X509Certificate cert : certificates) { + writer.write(cert.getSubjectDN().toString() + System.lineSeparator()); + writer.writeObject(cert); + } + LOGGER.info("Write certificate chain success!"); + } + } + + private void outputOptionalBlocks(String outputProfileFile, VerifyResult verifyResult) throws IOException { + byte[] profile = verifyResult.getProfile(); + if (profile != null) { + writeOptionalBytesToFile(profile, outputProfileFile); + } + } + + private void writeOptionalBytesToFile(byte[] data, String outputFile) throws IOException { + if (outputFile == null || outputFile.isEmpty()) { + return; + } + try (OutputStream out = Files.newOutputStream(Paths.get(outputFile))) { + out.write(data); + out.flush(); + } + } + + private boolean checkSignFile(File signedFile) { + try { + FileUtils.isValidFile(signedFile); + } catch (IOException e) { + LOGGER.error("signedFile is invalid.", e); + return false; + } + return true; + } + + /** + * Verify elf file. + * + * @param binFile path of elf file. + * @return true, if verify successfully. + */ + public VerifyResult verifyElf(String binFile) { + VerifyResult result = new VerifyResult(true, VerifyResult.RET_SUCCESS, "verify signature success"); + File bin = new File(binFile); + try { + byte[] bytes = FileUtils.readFile(bin); + ElfBlockData elfSignBlockData = getElfSignBlockData(bytes); + String profileJson; + byte[] profileByte; + Map signBlock = getSignBlock(bytes, elfSignBlockData); + if (signBlock.containsKey(SignatureBlockTypes.PROFILE_NOSIGNED_BLOCK)) { + profileByte = signBlock.get(SignatureBlockTypes.PROFILE_NOSIGNED_BLOCK).getValue(); + profileJson = new String(profileByte, StandardCharsets.UTF_8); + result.setProfile(profileByte); + LOGGER.warn("profile is not signed"); + } else if (signBlock.containsKey(SignatureBlockTypes.PROFILE_SIGNED_BLOCK)) { + // verify signed profile + SigningBlock profileSign = signBlock.get(SignatureBlockTypes.PROFILE_SIGNED_BLOCK); + profileByte = profileSign.getValue(); + profileJson = getProfileContent(profileByte); + result = new HapVerify().verifyElfProfile(profileSign.getValue()); + result.setProfile(profileByte); + LOGGER.info("verify profile success"); + } else { + LOGGER.warn("can not found profile sign block"); + profileJson = null; + } + + if (signBlock.containsKey(SignElf.CODESIGN_BLOCK_TYPE)) { + // verify codesign + SigningBlock codesign = signBlock.get(SignElf.CODESIGN_BLOCK_TYPE); + if (!VerifyCodeSignature.verifyElf(bin, codesign.getOffset(), codesign.getLength(), + "elf", profileJson)) { + String errMsg = "Verify codesign error!"; + result = new VerifyResult(false, VerifyResult.RET_IO_ERROR, errMsg); + } + LOGGER.info("verify codesign success"); + } else { + LOGGER.warn("can not found code sign block"); + } + } catch (IOException e) { + LOGGER.error("Verify file has IO error!", e); + result = new VerifyResult(false, VerifyResult.RET_IO_ERROR, e.getMessage()); + } catch (FsVerityDigestException | VerifyCodeSignException e) { + LOGGER.error("Verify codesign error!", e); + result = new VerifyResult(false, VerifyResult.RET_IO_ERROR, e.getMessage()); + } catch (CMSException | ProfileException e) { + LOGGER.error("Verify profile error!", e); + result = new VerifyResult(false, VerifyResult.RET_IO_ERROR, e.getMessage()); + } + return result; + } + + private ElfBlockData getElfSignBlockData(byte[] bytes) throws IOException { + int offset = bytes.length - HwSignHead.SIGN_HEAD_LEN; + byte[] magicByte = readByteArrayOffset(bytes, offset, HwSignHead.ELF_MAGIC.length); + offset += HwSignHead.ELF_MAGIC.length; + byte[] versionByte = readByteArrayOffset(bytes, offset, HwSignHead.VERSION.length); + offset += HwSignHead.VERSION.length; + for (int i = 0; i < HwSignHead.ELF_MAGIC.length; i++) { + if (HwSignHead.ELF_MAGIC[i] != magicByte[i]) { + throw new IOException("elf magic verify failed"); + } + } + for (int i = 0; i < HwSignHead.VERSION.length; i++) { + if (HwSignHead.VERSION[i] != versionByte[i]) { + throw new IOException("elf sign version verify failed"); + } + } + int intByteLength = 4; + byte[] blockSizeByte = readByteArrayOffset(bytes, offset, intByteLength); + offset += intByteLength; + byte[] blockNumByte = readByteArrayOffset(bytes, offset, intByteLength); + ByteBuffer blockNumBf = ByteBuffer.wrap(blockNumByte).order(ByteOrder.LITTLE_ENDIAN); + int blockNum = blockNumBf.getInt(); + + ByteBuffer blockSizeBf = ByteBuffer.wrap(blockSizeByte).order(ByteOrder.LITTLE_ENDIAN); + int blockSize = blockSizeBf.getInt(); + + int blockStart = bytes.length - HwSignHead.SIGN_HEAD_LEN - blockSize; + return new ElfBlockData(blockNum, blockStart); + } + + private Map getSignBlock(byte[] bytes, ElfBlockData elfBlockData) throws ProfileException { + int offset = elfBlockData.getBlockStart(); + + Map blockMap = new HashMap<>(); + for (int i = 0; i < elfBlockData.getBlockNum(); i++) { + byte[] blockByte = readByteArrayOffset(bytes, offset, HwBlockHead.ELF_BLOCK_LEN); + ByteBuffer blockBuffer = ByteBuffer.wrap(blockByte).order(ByteOrder.LITTLE_ENDIAN); + char type = blockBuffer.getChar(); + char tag = blockBuffer.getChar(); + int length = blockBuffer.getInt(); + int blockOffset = blockBuffer.getInt(); + byte[] value = readByteArrayOffset(bytes, elfBlockData.getBlockStart() + blockOffset, length); + blockMap.put(type, new SigningBlock(type, value, elfBlockData.getBlockStart() + blockOffset)); + offset += HwBlockHead.ELF_BLOCK_LEN; + } + return blockMap; + } + + private byte[] readByteArrayOffset(byte[] bytes, int offset, int length) { + byte[] output = new byte[length]; + System.arraycopy(bytes, offset, output, 0, length); + return output; + } +} \ No newline at end of file diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java index fdc8f45a9d4f4c2c697a9fb9521e1ed85d6b4911..33b6fd9128324223d66f909ea005424c0ba0475c 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java @@ -43,7 +43,6 @@ import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.util.Arrays; import java.io.File; -import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; @@ -51,6 +50,8 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -151,7 +152,9 @@ public class VerifyHap { throw new IOException(); } String outputCertPath = options.getString(ParamConstants.PARAM_VERIFY_CERTCHAIN_FILE); - writeCertificate(outputCertPath, verifyResult.getCertificates()); + if (verifyResult.getCertificates() != null) { + writeCertificate(outputCertPath, verifyResult.getCertificates()); + } } catch (IOException e) { LOGGER.error("Write certificate chain error", e); return false; @@ -202,13 +205,17 @@ public class VerifyHap { } } } + byte[] profile = verifyResult.getProfile(); + if (profile != null) { + writeOptionalBytesToFile(profile, outputProfileFile); + } } private void writeOptionalBytesToFile(byte[] data, String outputFile) throws IOException { if (outputFile == null || outputFile.isEmpty()) { return; } - try (OutputStream out = new FileOutputStream(outputFile)) { + try (OutputStream out = Files.newOutputStream(Paths.get(outputFile))) { out.write(data); out.flush(); } @@ -224,31 +231,6 @@ public class VerifyHap { return true; } - /** - * Verify signature of hap. - * - * @param hapFilePath path of hap file - * @param outCertPath path to output certificate file - * @param outProvisionFile path to output provision file - * @return verify result - */ - public VerifyResult verifyHap(String hapFilePath, String outCertPath, String outProvisionFile) { - VerifyResult verifyResult = verifyHap(hapFilePath); - if (!verifyResult.isVerified()) { - return verifyResult; - } - List certificates = verifyResult.getCertificates(); - try { - writeCertificate(outCertPath, certificates); - outputOptionalBlocks(outProvisionFile, null, null, verifyResult); - } catch (IOException e) { - LOGGER.error("Write certificate chain or profile error", e); - verifyResult.setIsResult(false); - return verifyResult; - } - return verifyResult; - } - /** * Verify hap file. * @@ -274,7 +256,7 @@ public class VerifyHap { List optionalBlocks = blockPair.getSecond(); Collections.reverse(optionalBlocks); if (!checkCodeSign(hapFilePath, optionalBlocks)) { - String errMsg = "ZIP64 code sign data error"; + String errMsg = "code sign verify failed"; return new VerifyResult(false, VerifyResult.RET_CODESIGN_DATA_ERROR, errMsg); } HapVerify verifyEngine = getHapVerify(hapFile, zipInfo, hapSigningBlockAndOffsetInFile, @@ -340,9 +322,10 @@ public class VerifyHap { .collect(Collectors.toMap(SigningBlock::getType, SigningBlock::getValue)); byte[] propertyBlockArray = map.get(HapUtils.HAP_PROPERTY_BLOCK_ID); if (propertyBlockArray != null && propertyBlockArray.length > 0) { + LOGGER.info("trying verify codesign block"); String[] fileNameArray = hapFilePath.split("\\."); if (fileNameArray.length < ParamConstants.FILE_NAME_MIN_LENGTH) { - LOGGER.error("ZIP46 format not supported"); + LOGGER.error("ZIP64 format not supported"); return false; } ByteBuffer byteBuffer = ByteBuffer.wrap(propertyBlockArray); @@ -364,8 +347,10 @@ public class VerifyHap { LOGGER.error("Verify Hap has no code sign data error!"); return false; } + LOGGER.info("verify codesign success"); return true; } + LOGGER.info("can not find codesign block"); return true; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyResult.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyResult.java index 0ad0ec25fdbcb5ada91afa9deda5432b83ce5cbb..ba032e84625eca6fdf6e1c75f217f20462f8ddec 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyResult.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyResult.java @@ -117,6 +117,8 @@ public class VerifyResult { private int signBlockVersion; + private byte[] profile; + /** * Empty constructor */ @@ -207,4 +209,12 @@ public class VerifyResult { public void setSignBlockVersion(int signBlockVersion) { this.signBlockVersion = signBlockVersion; } + + public byte[] getProfile() { + return profile; + } + + public void setProfile(byte[] profile) { + this.profile = profile; + } } diff --git a/tools/commands.config b/tools/commands.config index 11ee56b64c5a39896c4b1f938404600aabce5fcc..9625f383279ea2a607793465cee5f3c5fb25aafd 100644 --- a/tools/commands.config +++ b/tools/commands.config @@ -100,7 +100,11 @@ 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -profileFile "profile.json" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_pass.jks" -outFile "app1-signedcode.hap" -keyPwd "123456" -keystorePwd "123456" -profileSigned "0"', 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -profileFile "profile.json" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_pass.jks" -outFile "app1-unsignedcode.hap" -keyPwd "123456" -keystorePwd "123456" -profileSigned "0" -signcode "0"', 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -profileFile "profile.json" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_pass.jks" -outFile "app1-signedcode.hap" -keyPwd "123456" -keystorePwd "123456" -profileSigned "0" -signcode "1"', - 'verify-app -inFile "app1-signedcode.hap" -outCertChain "app-sign-srv-ca1.cer" -outProfile "app1-profile.p7b"' + 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -profileFile "app1-profile.p7b" -inFile "test/elf_unittest" -keystoreFile "ohtest_pass.jks" -outFile "output-elf-codesign-profile" -keyPwd "123456" -keystorePwd "123456" -profileSigned "1" -signcode "1" -inForm elf', + 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -inFile "test/elf_unittest" -keystoreFile "ohtest_pass.jks" -outFile "output-elf-codesign" -keyPwd "123456" -keystorePwd "123456" -signcode "1" -inForm elf', + 'verify-app -inFile "app1-signedcode.hap" -outCertChain "app-sign-srv-ca1.cer" -outProfile "app1-profile.p7b"', + 'verify-app -inFile "output-elf-codesign-profile" -outCertChain "app-sign-srv-ca1.cer" -outProfile "app1-profile.p7b" -inForm elf', + 'verify-app -inFile "output-elf-codesign" -outCertChain "app-sign-srv-ca1.cer" -outProfile "app1-profile.p7b" -inForm elf' ], 'case-assert-false': [ 'generate-keypair -keyPwd 123456 -keyAlg ECC -keySize NIST-P-384 -keystoreFile "ohtest.jks" -keystorePwd 123456 -extCfgFile "111.txt"', @@ -466,6 +470,7 @@ 'sign-app -keyAlias "oh-app1-key-v1" -keyPwd "123456" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app2.pem" -profileFile "app1-profile.p7b" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_nopass.jks" -outFile "app1-signedcode.hap" -inForm -profileSigned -extCfgFile ', 'sign-app -keyAlias "oh-app1-key-v1" -keyPwd "123456" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app2.pem" -profileFile "app1-profile.p7b" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_nopass.jks" -outFile "app1-unsignedcode.hap" -inForm -profileSigned -extCfgFile -signcode "0"', 'sign-app -keyAlias "oh-app1-key-v1" -keyPwd "123456" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app2.pem" -profileFile "app1-profile.p7b" -inFile "test/app1-unsigned.hap" -keystoreFile "ohtest_nopass.jks" -outFile "app1-signedcode.hap" -inForm -profileSigned -extCfgFile -signcode "1"', + 'sign-app -keyAlias "oh-app1-key-v1" -signAlg "SHA256withECDSA" -mode "localSign" -appCertFile "app1.pem" -profileFile "profile.json" -inFile "test/elf_unittest" -keystoreFile "ohtest_pass.jks" -outFile "output-elf-codesign-profile" -keyPwd "123456" -keystorePwd "123456" -profileSigned "0" -signcode "1"', 'verify-app -inFile "app1-signedcode.hap" -outCertChain "" -outProfile "app1-profile.p7b"', 'verify-app -inFile "app1-signedcode.hap" -outCertChain "app1.cer" -outProfile ""', 'verify-app -inFile "app1-signedcode.hap" -outCertChain "" -outProfile ""', diff --git a/tools/test/elf_unittest.elf b/tools/test/elf_unittest.elf new file mode 100644 index 0000000000000000000000000000000000000000..027e26783f0aba1317762d73d0a764ff523490db --- /dev/null +++ b/tools/test/elf_unittest.elf @@ -0,0 +1 @@ +elf test file \ No newline at end of file