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