• 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.codesigning.datastructure;
17 
18 import com.ohos.hapsigntool.codesigning.sign.CodeSigning;
19 
20 import java.nio.ByteBuffer;
21 import java.nio.ByteOrder;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Map;
28 
29 /**
30  * code sign block is a chunk of bytes attached to hap package or file.
31  * It consists of two headers:
32  * 1) code sign block header
33  * 2) segment header
34  * three segments:
35  * 1) fs-verity info segment
36  * 2) hap info segment
37  * 3) so info segment
38  * one zero padding area in order to align merkle tree raw bytes
39  * 1) zero padding
40  * and one area storing merkle tree bytes:
41  * 1) merkle tree raw bytes
42  * <p>
43  * After signing a hap, call toByteArray() method to generate a block of bytes.
44  *
45  * @since 2023/09/08
46  */
47 public class CodeSignBlock {
48     /**
49      * page size in bytes
50      */
51     public static final long PAGE_SIZE_4K = 4096L;
52 
53     /**
54      * Segment header count, including fs-verity info, hap info, so info segment
55      */
56     public static final int SEGMENT_HEADER_COUNT = 3;
57 
58     private CodeSignBlockHeader codeSignBlockHeader;
59 
60     private final List<SegmentHeader> segmentHeaderList;
61 
62     private FsVerityInfoSegment fsVerityInfoSegment;
63 
64     private HapInfoSegment hapInfoSegment;
65 
66     private NativeLibInfoSegment nativeLibInfoSegment;
67 
68     private byte[] zeroPadding;
69 
70     private final Map<String, byte[]> merkleTreeMap;
71 
CodeSignBlock()72     public CodeSignBlock() {
73         this.codeSignBlockHeader = new CodeSignBlockHeader.Builder().build();
74         this.segmentHeaderList = new ArrayList<>();
75         this.fsVerityInfoSegment = new FsVerityInfoSegment();
76         this.hapInfoSegment = new HapInfoSegment();
77         this.nativeLibInfoSegment = new NativeLibInfoSegment.Builder().build();
78         this.merkleTreeMap = new HashMap<>();
79     }
80 
81     /**
82      * Add one merkle tree into merkleTreeMap
83      *
84      * @param key        file name
85      * @param merkleTree merkle tree raw bytes
86      */
addOneMerkleTree(String key, byte[] merkleTree)87     public void addOneMerkleTree(String key, byte[] merkleTree) {
88         if (merkleTree == null) {
89             this.merkleTreeMap.put(key, new byte[0]);
90         } else {
91             this.merkleTreeMap.put(key, merkleTree);
92         }
93     }
94 
95     /**
96      * Get one merkle tree from merkleTreeMap by file name
97      *
98      * @param key file name
99      * @return merkle tree bytes
100      */
getOneMerkleTreeByFileName(String key)101     public byte[] getOneMerkleTreeByFileName(String key) {
102         return this.merkleTreeMap.get(key);
103     }
104 
105     /**
106      * set code sign block flag
107      */
setCodeSignBlockFlag()108     public void setCodeSignBlockFlag() {
109         int flags = CodeSignBlockHeader.FLAG_MERKLE_TREE_INLINED;
110         if (this.nativeLibInfoSegment.getSectionNum() != 0) {
111             flags += CodeSignBlockHeader.FLAG_NATIVE_LIB_INCLUDED;
112         }
113         this.codeSignBlockHeader.setFlags(flags);
114     }
115 
116     /**
117      * set segmentNum defined in code sign block header, equals length of segmentHeaderList
118      */
setSegmentNum()119     public void setSegmentNum() {
120         this.codeSignBlockHeader.setSegmentNum(segmentHeaderList.size());
121     }
122 
123     /**
124      * add one segment to segmentHeaderList
125      *
126      * @param sh segment header
127      */
addToSegmentList(SegmentHeader sh)128     public void addToSegmentList(SegmentHeader sh) {
129         this.segmentHeaderList.add(sh);
130     }
131 
getSegmentHeaderList()132     public List<SegmentHeader> getSegmentHeaderList() {
133         return segmentHeaderList;
134     }
135 
136     /**
137      * set segment header list
138      */
setSegmentHeaders()139     public void setSegmentHeaders() {
140         // fs-verity info segment
141         segmentHeaderList.add(new SegmentHeader(SegmentHeader.CSB_FSVERITY_INFO_SEG, this.fsVerityInfoSegment.size()));
142         // hap info segment
143         segmentHeaderList.add(new SegmentHeader(SegmentHeader.CSB_HAP_META_SEG, this.hapInfoSegment.size()));
144         // native lib info segment
145         segmentHeaderList.add(
146             new SegmentHeader(SegmentHeader.CSB_NATIVE_LIB_INFO_SEG, this.nativeLibInfoSegment.size()));
147     }
148 
getCodeSignBlockHeader()149     public CodeSignBlockHeader getCodeSignBlockHeader() {
150         return codeSignBlockHeader;
151     }
152 
setCodeSignBlockHeader(CodeSignBlockHeader csbHeader)153     public void setCodeSignBlockHeader(CodeSignBlockHeader csbHeader) {
154         this.codeSignBlockHeader = csbHeader;
155     }
156 
setFsVerityInfoSegment(FsVerityInfoSegment fsVeritySeg)157     public void setFsVerityInfoSegment(FsVerityInfoSegment fsVeritySeg) {
158         this.fsVerityInfoSegment = fsVeritySeg;
159     }
160 
getFsVerityInfoSegment()161     public FsVerityInfoSegment getFsVerityInfoSegment() {
162         return fsVerityInfoSegment;
163     }
164 
getHapInfoSegment()165     public HapInfoSegment getHapInfoSegment() {
166         return hapInfoSegment;
167     }
168 
setHapInfoSegment(HapInfoSegment hapSeg)169     public void setHapInfoSegment(HapInfoSegment hapSeg) {
170         this.hapInfoSegment = hapSeg;
171     }
172 
getSoInfoSegment()173     public NativeLibInfoSegment getSoInfoSegment() {
174         return nativeLibInfoSegment;
175     }
176 
setSoInfoSegment(NativeLibInfoSegment soSeg)177     public void setSoInfoSegment(NativeLibInfoSegment soSeg) {
178         this.nativeLibInfoSegment = soSeg;
179     }
180 
181     /**
182      * Convert code sign block object to a newly created byte array
183      *
184      * @return Byte array representation of a CodeSignBlock object
185      */
toByteArray()186     public byte[] toByteArray() {
187         ByteBuffer bf = ByteBuffer.allocate(this.codeSignBlockHeader.getBlockSize()).order(ByteOrder.LITTLE_ENDIAN);
188         bf.put(this.codeSignBlockHeader.toByteArray());
189         for (SegmentHeader sh : this.segmentHeaderList) {
190             bf.put(sh.toByteArray());
191         }
192         bf.put(this.zeroPadding);
193         // Hap merkle tree
194         if (this.hapInfoSegment.getSignInfo().getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED) != null) {
195             bf.put(merkleTreeMap.get("Hap"));
196         }
197         bf.put(this.fsVerityInfoSegment.toByteArray());
198         bf.put(this.hapInfoSegment.toByteArray());
199         bf.put(this.nativeLibInfoSegment.toByteArray());
200         return bf.array();
201     }
202 
203     /**
204      * SegmentOffset is the position of each segment defined in segmentHeaderList,
205      * based on the start position of code sign block
206      */
computeSegmentOffset()207     public void computeSegmentOffset() {
208         // 1) the first segment is placed after merkle tree
209         int segmentOffset = CodeSignBlockHeader.size()
210             + this.segmentHeaderList.size() * SegmentHeader.SEGMENT_HEADER_LENGTH + this.zeroPadding.length
211             + this.getOneMerkleTreeByFileName(CodeSigning.HAP_SIGNATURE_ENTRY_NAME).length;
212         for (SegmentHeader sh : segmentHeaderList) {
213             sh.setSegmentOffset(segmentOffset);
214             segmentOffset += sh.getSegmentSize();
215         }
216     }
217 
218     /**
219      * Compute the offset to store merkle tree raw bytes based on file start
220      *
221      * @param codeSignBlockOffset offset to store code sign block based on file start
222      * @return offset to store merkle tree based on the file start, it includes the codeSignBlockOffset
223      */
computeMerkleTreeOffset(long codeSignBlockOffset)224     public long computeMerkleTreeOffset(long codeSignBlockOffset) {
225         long sizeWithoutMerkleTree = CodeSignBlockHeader.size()
226             + SEGMENT_HEADER_COUNT * SegmentHeader.SEGMENT_HEADER_LENGTH;
227         // add code sign block offset while computing align position for merkle tree
228         long residual = (codeSignBlockOffset + sizeWithoutMerkleTree) % PAGE_SIZE_4K;
229         if (residual == 0) {
230             this.zeroPadding = new byte[0];
231         } else {
232             this.zeroPadding = new byte[(int) (PAGE_SIZE_4K - residual)];
233         }
234         return codeSignBlockOffset + sizeWithoutMerkleTree + zeroPadding.length;
235     }
236 
237     /**
238      * Convert CodeSignBlock to bytes
239      *
240      * @param fsvTreeOffset merkle tree offset
241      * @return byte array representing the code sign block
242      */
generateCodeSignBlockByte(long fsvTreeOffset)243     public byte[] generateCodeSignBlockByte(long fsvTreeOffset) {
244         // 1) compute overall block size without merkle tree
245         long csbSize = CodeSignBlockHeader.size()
246             + (long) this.segmentHeaderList.size() * SegmentHeader.SEGMENT_HEADER_LENGTH + this.zeroPadding.length
247             + this.getOneMerkleTreeByFileName(CodeSigning.HAP_SIGNATURE_ENTRY_NAME).length
248             + this.fsVerityInfoSegment.size() + this.hapInfoSegment.size() + this.nativeLibInfoSegment.size();
249         Extension ext = this.hapInfoSegment.getSignInfo().getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED);
250         if (ext instanceof MerkleTreeExtension) {
251             MerkleTreeExtension merkleTreeExtension = (MerkleTreeExtension) ext;
252             merkleTreeExtension.setMerkleTreeOffset(fsvTreeOffset);
253         }
254         this.codeSignBlockHeader.setBlockSize(csbSize);
255         // 2) generate byte array of complete code sign block
256         return toByteArray();
257     }
258 
259     /**
260      * Return a string representation of the object
261      *
262      * @return string representation of the object
263      */
toString()264     public String toString() {
265         return String.format(Locale.ROOT,
266             "CodeSignBlockHeader[%s], SegmentHeaders[%s], FsVeritySeg[%s], HapInfoSeg[%s], SoInfoSeg[%s]",
267             this.codeSignBlockHeader, Arrays.toString(this.segmentHeaderList.toArray()), this.fsVerityInfoSegment,
268             this.hapInfoSegment, this.nativeLibInfoSegment);
269     }
270 }
271