1 /* 2 * Copyright (c) 2023-2023 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 package com.ohos.hapsigntool.hap.sign; 17 18 import com.ohos.hapsigntool.codesigning.exception.CodeSignException; 19 import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; 20 import com.ohos.hapsigntool.codesigning.sign.CodeSigning; 21 import com.ohos.hapsigntool.hap.config.SignerConfig; 22 import com.ohos.hapsigntool.hap.entity.BlockHead; 23 import com.ohos.hapsigntool.hap.entity.SignHead; 24 import com.ohos.hapsigntool.hap.entity.SignBlockData; 25 import com.ohos.hapsigntool.hap.entity.SignatureBlockTags; 26 import com.ohos.hapsigntool.hap.entity.SignatureBlockTypes; 27 import com.ohos.hapsigntool.error.HapFormatException; 28 import com.ohos.hapsigntool.error.ProfileException; 29 import com.ohos.hapsigntool.utils.FileUtils; 30 import com.ohos.hapsigntool.entity.ParamConstants; 31 import com.ohos.hapsigntool.utils.LogUtils; 32 import com.ohos.hapsigntool.utils.ParamProcessUtil; 33 import com.ohos.hapsigntool.utils.StringUtils; 34 35 import java.io.DataOutputStream; 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.Date; 42 import java.util.List; 43 import java.util.Map; 44 45 /** 46 * elf file Signature signer. 47 * 48 * @since 2023/11/21 49 */ 50 public class SignElf { 51 /** 52 * codesign sign block type 53 */ 54 public static final char CODESIGN_BLOCK_TYPE = 3; 55 56 private static final LogUtils LOGGER = new LogUtils(SignElf.class); 57 58 private static final String CODESIGN_OFF = "0"; 59 60 private static int blockNum = 0; 61 62 private static final int PAGE_SIZE = 4096; 63 64 private static final int FILE_BUFFER_BLOCK = 16384; 65 66 /** 67 * Constructor of Method 68 */ SignElf()69 private SignElf() { 70 } 71 72 /** 73 * Sign the elf file. 74 * 75 * @param signerConfig Config of the elf file to be signed. 76 * @param signParams The input parameters of sign elf. 77 * @return true if sign successfully; false otherwise. 78 */ sign(SignerConfig signerConfig, Map<String, String> signParams)79 public static boolean sign(SignerConfig signerConfig, Map<String, String> signParams) { 80 boolean isSuccess = false; 81 /* 1. Make block head, write to output file. */ 82 String inputFile = signParams.get(ParamConstants.PARAM_BASIC_INPUT_FILE); 83 String tmpFile = alignFileBy4kBytes(inputFile); 84 if (tmpFile == null) { 85 LOGGER.error("copy input File failed"); 86 return isSuccess; 87 } 88 String outputFile = signParams.get(ParamConstants.PARAM_BASIC_OUTPUT_FILE); 89 String profileSigned = signParams.get(ParamConstants.PARAM_BASIC_PROFILE_SIGNED); 90 if (!writeBlockDataToFile(signerConfig, tmpFile, outputFile, profileSigned, signParams)) { 91 LOGGER.error("The block head data made failed.`"); 92 ParamProcessUtil.delDir(new File(outputFile)); 93 return isSuccess; 94 } 95 LOGGER.info("The block head data made success."); 96 97 /* 2. Make sign data, and write to output file */ 98 if (!writeSignHeadDataToOutputFile(tmpFile, outputFile, blockNum)) { 99 LOGGER.error("The sign head data made failed."); 100 ParamProcessUtil.delDir(new File(outputFile)); 101 } else { 102 isSuccess = true; 103 } 104 return isSuccess; 105 } 106 alignFileBy4kBytes(String inputFile)107 private static String alignFileBy4kBytes(String inputFile) { 108 String tmp = "tmpFile" + new Date().getTime(); 109 File tmpFile = new File(tmp); 110 try { 111 tmpFile.createNewFile(); 112 } catch (IOException e) { 113 LOGGER.error("create tmp file Failed"); 114 return null; 115 } 116 try (FileOutputStream output = new FileOutputStream(tmpFile); 117 FileInputStream input = new FileInputStream(inputFile)) { 118 byte[] buffer = new byte[FILE_BUFFER_BLOCK]; 119 int read; 120 while ((read = input.read(buffer)) != FileUtils.FILE_END) { 121 output.write(buffer, 0, read); 122 } 123 124 long addLength = PAGE_SIZE - (tmpFile.length() % PAGE_SIZE); 125 if (isLongOverflowInteger(addLength)) { 126 LOGGER.error("File alignment error"); 127 return null; 128 } 129 byte[] bytes = new byte[(int) addLength]; 130 java.util.Arrays.fill(bytes, (byte) 0); 131 FileUtils.writeByteToOutFile(bytes, tmp); 132 } catch (IOException e) { 133 LOGGER.error("copy inFile Failed"); 134 return null; 135 } 136 return tmp; 137 } 138 writeBlockDataToFile(SignerConfig signerConfig, String inputFile, String outputFile, String profileSigned, Map<String, String> signParams)139 private static boolean writeBlockDataToFile(SignerConfig signerConfig, 140 String inputFile, String outputFile, String profileSigned, Map<String, String> signParams) { 141 try { 142 String profileFile = signParams.get(ParamConstants.PARAM_BASIC_PROFILE); 143 144 List<SignBlockData> signDataList = new ArrayList<>(); 145 146 long binFileLen = FileUtils.getFileLen(inputFile); 147 if (binFileLen == -1) { 148 LOGGER.error("file length is invalid, elf file len: " + binFileLen); 149 throw new IOException(); 150 } 151 // 1. generate sign data 152 if (!StringUtils.isEmpty(signParams.get(ParamConstants.PARAM_BASIC_PROFILE))) { 153 signDataList.add(generateProfileSignByte(profileFile, profileSigned)); 154 } 155 blockNum = signDataList.size() + 1; // other sign block num + codesign block 1 156 SignBlockData codeSign = generateCodeSignByte(signerConfig, signParams, inputFile, blockNum, binFileLen); 157 if (codeSign != null) { 158 signDataList.add(0, codeSign); 159 } 160 blockNum = signDataList.size(); 161 // 2. use sign data generate offset and sign block head 162 generateSignBlockHead(signDataList); 163 164 return writeSignedElf(inputFile, signDataList, outputFile); 165 } catch (IOException e) { 166 LOGGER.error("writeBlockDataToFile failed.", e); 167 return false; 168 } catch (FsVerityDigestException | CodeSignException | HapFormatException | ProfileException e) { 169 LOGGER.error("codesign failed.", e); 170 return false; 171 } 172 } 173 writeSignedElf(String inputFile, List<SignBlockData> signBlockList, String outputFile)174 private static boolean writeSignedElf(String inputFile, List<SignBlockData> signBlockList, String outputFile) { 175 try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile); 176 DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream)) { 177 // 1. write the input file to the output file. 178 if (!FileUtils.writeFileToDos(inputFile, dataOutputStream)) { 179 LOGGER.error("Failed to write information of input file: " + inputFile 180 + " to outputFile: " + outputFile); 181 throw new IOException(); 182 } 183 184 // 2. write block head to the output file. 185 for (SignBlockData signBlockData : signBlockList) { 186 if (!FileUtils.writeByteToDos(signBlockData.getBlockHead(), dataOutputStream)) { 187 LOGGER.error("Failed to write Block Head to output file: " + outputFile); 188 throw new IOException(); 189 } 190 } 191 192 // 3. write block data to the output file. 193 for (SignBlockData signBlockData : signBlockList) { 194 boolean isSuccess; 195 if (signBlockData.isByte()) { 196 isSuccess = FileUtils.writeByteToDos(signBlockData.getSignData(), dataOutputStream); 197 } else { 198 isSuccess = FileUtils.writeFileToDos(signBlockData.getSignFile(), dataOutputStream); 199 } 200 201 if (!isSuccess) { 202 LOGGER.error("Failed to write Block Data to output file: " + outputFile); 203 throw new IOException(); 204 } 205 } 206 } catch (IOException e) { 207 LOGGER.error("writeSignedElf failed.", e); 208 return false; 209 } 210 return true; 211 } 212 generateSignBlockHead(List<SignBlockData> signDataList)213 private static void generateSignBlockHead(List<SignBlockData> signDataList) 214 throws IOException { 215 long offset = (long) BlockHead.getElfBlockLen() * signDataList.size(); 216 217 for (int i = 0; i < signDataList.size(); i++) { 218 SignBlockData signBlockData = signDataList.get(i); 219 220 signBlockData.setBlockHead(BlockHead.getBlockHeadLittleEndian(signBlockData.getType(), 221 SignatureBlockTags.DEFAULT, (int) signBlockData.getLen(), (int) offset)); 222 offset += signBlockData.getLen(); 223 if (isLongOverflowInteger(offset)) { 224 LOGGER.error("The sign block " + i + "offset is overflow integer, offset: " + offset); 225 throw new IOException(); 226 } 227 } 228 } 229 generateProfileSignByte(String profileFile, String profileSigned)230 private static SignBlockData generateProfileSignByte(String profileFile, String profileSigned) throws IOException { 231 long profileDataLen = FileUtils.getFileLen(profileFile); 232 233 if (profileDataLen == -1 || isLongOverflowShort(profileDataLen)) { 234 LOGGER.error("file length is invalid, profileDataLen: " + profileDataLen); 235 throw new IOException(); 236 } 237 238 char isSigned = SignatureBlockTypes.getProfileBlockTypes(profileSigned); 239 return new SignBlockData(profileFile, isSigned); 240 } 241 generateCodeSignByte(SignerConfig signerConfig, Map<String, String> signParams, String inputFile, int blockNum, long binFileLen)242 private static SignBlockData generateCodeSignByte(SignerConfig signerConfig, Map<String, String> signParams, 243 String inputFile, int blockNum, long binFileLen) throws IOException, 244 FsVerityDigestException, CodeSignException, HapFormatException, ProfileException { 245 if (CODESIGN_OFF.equals(signParams.get(ParamConstants.PARAM_SIGN_CODE))) { 246 return null; 247 } 248 CodeSigning codeSigning = new CodeSigning(signerConfig); 249 long offset = binFileLen + (long) BlockHead.getElfBlockLen() * blockNum; 250 String profileContent = signParams.get(ParamConstants.PARAM_PROFILE_JSON_CONTENT); 251 byte[] codesignData = codeSigning.getElfCodeSignBlock(new File(inputFile), offset, 252 signParams.get(ParamConstants.PARAM_IN_FORM), profileContent); 253 return new SignBlockData(codesignData, CODESIGN_BLOCK_TYPE); 254 } 255 writeSignHeadDataToOutputFile(String inputFile, String outputFile, int blockNum)256 private static boolean writeSignHeadDataToOutputFile(String inputFile, String outputFile, int blockNum) { 257 long size = FileUtils.getFileLen(outputFile) - FileUtils.getFileLen(inputFile); 258 if (isLongOverflowInteger(size)) { 259 LOGGER.error("File size is overflow integer range."); 260 return false; 261 } 262 SignHead signHeadData = new SignHead(); 263 byte[] signHeadByte = signHeadData.getSignHeadLittleEndian((int) size, blockNum); 264 if (signHeadByte == null) { 265 LOGGER.error("Failed to get sign head data."); 266 return false; 267 } 268 return FileUtils.writeByteToOutFile(signHeadByte, outputFile); 269 } 270 isLongOverflowInteger(long num)271 private static boolean isLongOverflowInteger(long num) { 272 return (num - (num & 0xffffffffL)) != 0; 273 } 274 isLongOverflowShort(long num)275 private static boolean isLongOverflowShort(long num) { 276 return (num - (num & 0xffffL)) != 0; 277 } 278 } 279