• 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.exception.VerifyCodeSignException;
19 import com.ohos.hapsigntool.entity.Pair;
20 
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.Locale;
28 
29 /**
30  * SoInfoSegment consists of a header part:
31  * <p>
32  * u32 magic: magic number
33  * <p>
34  * u32 length: byte size of SoInfoSegment
35  * <p>
36  * u32 section num: the amount of file being signed
37  * <p>
38  * Followed by an area containing the offset and size of each file being signed with its signed info:
39  * <p>
40  * u32 file name offset: position of file name based on the start of SoInfoSegment
41  * <p>
42  * u32 file name size : byte size of file name string
43  * <p>
44  * u32 sign info offset : position of signed info based on the start of SoInfoSegment
45  * <p>
46  * u32 sign size: byte size of signed info
47  * <p>
48  * Ends with the file name and signed info content:
49  * <p>
50  * file name List : file name of each signed file
51  * <p>
52  * sign info List : signed info of each file
53  * <p>
54  *
55  * @since 2023/09/08
56  */
57 public class NativeLibInfoSegment {
58     private static final int MAGIC_LENGTH_SECNUM_BYTES = 12;
59 
60     private static final int SIGNED_FILE_POS_SIZE = 16;
61 
62     // lower 4 bytes of the MD5 result of string "native lib info segment" (0ED2 E720)
63     private static final int MAGIC_NUM = (0x0ED2 << 16) + 0xE720;
64 
65     private static final int ALIGNMENT_FOR_SIGNINFO = 4;
66 
67     private int magic;
68 
69     private int segmentSize;
70 
71     private int sectionNum;
72 
73     private List<Pair<String, SignInfo>> soInfoList = new ArrayList<>();
74 
75     private List<SignedFilePos> signedFilePosList;
76 
77     private List<String> fileNameList;
78 
79     private List<SignInfo> signInfoList;
80 
81     private byte[] zeroPadding;
82 
83     private int fileNameListBlockSize;
84 
85     private int signInfoListBlockSize;
86 
87     /**
88      * Constructor for SoInfoSegment
89      *
90      * @param builder Builder
91      */
NativeLibInfoSegment(Builder builder)92     private NativeLibInfoSegment(Builder builder) {
93         this.magic = builder.magic;
94         this.segmentSize = builder.segmentSize;
95         this.sectionNum = builder.sectionNum;
96         this.signedFilePosList = builder.signedFilePosList;
97         this.fileNameList = builder.fileNameList;
98         this.signInfoList = builder.signInfoList;
99         this.zeroPadding = builder.zeroPadding;
100     }
101 
102     /**
103      * set soInfoList, generate fileNameList and soInfoList
104      *
105      * @param soInfoList list of file and its signed info
106      */
setSoInfoList(List<Pair<String, SignInfo>> soInfoList)107     public void setSoInfoList(List<Pair<String, SignInfo>> soInfoList) {
108         this.soInfoList = soInfoList;
109         // Once map is set, update length, sectionNum as well
110         this.sectionNum = soInfoList.size();
111         // generate file name list and sign info list
112         generateList();
113     }
114 
getSectionNum()115     public int getSectionNum() {
116         return sectionNum;
117     }
118 
getFileNameList()119     public List<String> getFileNameList() {
120         return fileNameList;
121     }
122 
getSignInfoList()123     public List<SignInfo> getSignInfoList() {
124         return signInfoList;
125     }
126 
127     // generate List based on current so
generateList()128     private void generateList() {
129         // empty all before generate list
130         this.fileNameList.clear();
131         this.signInfoList.clear();
132         this.signedFilePosList.clear();
133         int fileNameOffset = 0;
134         int signInfoOffset = 0;
135         for (Pair<String, SignInfo> soInfo : soInfoList) {
136             String fileName = soInfo.getFirst();
137             SignInfo signInfo = soInfo.getSecond();
138             int fileNameSizeInBytes = fileName.getBytes(StandardCharsets.UTF_8).length;
139             int signInfoSizeInBytes = signInfo.toByteArray().length;
140             this.fileNameList.add(fileName);
141             this.signInfoList.add(signInfo);
142             this.signedFilePosList.add(
143                 new SignedFilePos(fileNameOffset, fileNameSizeInBytes, signInfoOffset, signInfoSizeInBytes));
144             // increase fileNameOffset and signInfoOffset
145             fileNameOffset += fileNameSizeInBytes;
146             signInfoOffset += signInfoSizeInBytes;
147         }
148         this.fileNameListBlockSize = fileNameOffset;
149         this.signInfoListBlockSize = signInfoOffset;
150         // alignment for signInfo
151         this.zeroPadding = new byte[(ALIGNMENT_FOR_SIGNINFO - this.fileNameListBlockSize % ALIGNMENT_FOR_SIGNINFO)
152             % ALIGNMENT_FOR_SIGNINFO];
153         // after fileNameList and signInfoList is generated, update segment size
154         this.segmentSize = this.size();
155         // adjust file name and sign info offset base on segment start
156         int fileNameOffsetBase = MAGIC_LENGTH_SECNUM_BYTES + signedFilePosList.size() * SIGNED_FILE_POS_SIZE;
157         int signInfoOffsetBase = fileNameOffsetBase + this.fileNameListBlockSize;
158         for (SignedFilePos pos : this.signedFilePosList) {
159             pos.increaseFileNameOffset(fileNameOffsetBase);
160             pos.increaseSignInfoOffset(signInfoOffsetBase + this.zeroPadding.length);
161         }
162     }
163 
164     /**
165      * Returns byte size of SoInfoSegment
166      *
167      * @return byte size of SoInfoSegment
168      */
size()169     public int size() {
170         int blockSize = MAGIC_LENGTH_SECNUM_BYTES;
171         blockSize += signedFilePosList.size() * SIGNED_FILE_POS_SIZE;
172         blockSize += this.fileNameListBlockSize + this.zeroPadding.length + this.signInfoListBlockSize;
173         return blockSize;
174     }
175 
176     /**
177      * Converts SoInfoSegment to a newly created byte array
178      *
179      * @return Byte array representation of SoInfoSegment
180      */
toByteArray()181     public byte[] toByteArray() {
182         ByteBuffer bf = ByteBuffer.allocate(this.size()).order(ByteOrder.LITTLE_ENDIAN);
183         bf.putInt(magic);
184         bf.putInt(segmentSize);
185         bf.putInt(sectionNum);
186         for (SignedFilePos offsetAndSize : this.signedFilePosList) {
187             bf.putInt(offsetAndSize.getFileNameOffset());
188             bf.putInt(offsetAndSize.getFileNameSize());
189             bf.putInt(offsetAndSize.getSignInfoOffset());
190             bf.putInt(offsetAndSize.getSignInfoSize());
191         }
192         for (String fileName : fileNameList) {
193             bf.put(fileName.getBytes(StandardCharsets.UTF_8));
194         }
195         bf.put(this.zeroPadding);
196         for (SignInfo signInfo : signInfoList) {
197             bf.put(signInfo.toByteArray());
198         }
199         return bf.array();
200     }
201 
202     /**
203      * Init the SoInfoSegment by a byte array
204      *
205      * @param bytes Byte array representation of a SoInfoSegment object
206      * @return a newly created NativeLibInfoSegment object
207      * @throws VerifyCodeSignException parsing result invalid
208      */
fromByteArray(byte[] bytes)209     public static NativeLibInfoSegment fromByteArray(byte[] bytes) throws VerifyCodeSignException {
210         ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN);
211         bf.put(bytes);
212         bf.rewind();
213         int inMagic = bf.getInt();
214         if (inMagic != MAGIC_NUM) {
215             throw new VerifyCodeSignException("Invalid magic number of NativeLibInfoSegment");
216         }
217         int inSegmentSize = bf.getInt();
218         if (inSegmentSize < 0) {
219             throw new VerifyCodeSignException("Invalid segmentSize of NativeLibInfoSegment");
220         }
221         int inSectionNum = bf.getInt();
222         if (inSectionNum < 0) {
223             throw new VerifyCodeSignException("Invalid sectionNum of NativeLibInfoSegment");
224         }
225         List<SignedFilePos> inSignedFilePosList = new ArrayList<>();
226         for (int i = 0; i < inSectionNum; i++) {
227             byte[] entry = new byte[SIGNED_FILE_POS_SIZE];
228             bf.get(entry);
229             inSignedFilePosList.add(SignedFilePos.fromByteArray(entry));
230         }
231         // parse file name list
232         List<String> inFileNameList = new ArrayList<>();
233         int fileNameListSize = 0;
234         for (SignedFilePos pos : inSignedFilePosList) {
235             byte[] fileNameBuffer = new byte[pos.getFileNameSize()];
236             fileNameListSize += pos.getFileNameSize();
237             bf.position(pos.getFileNameOffset());
238             bf.get(fileNameBuffer);
239             inFileNameList.add(new String(fileNameBuffer, StandardCharsets.UTF_8));
240         }
241         // parse zeroPadding
242         byte[] inZeroPadding = new byte[(ALIGNMENT_FOR_SIGNINFO - fileNameListSize % ALIGNMENT_FOR_SIGNINFO)
243             % ALIGNMENT_FOR_SIGNINFO];
244         bf.get(inZeroPadding);
245         // parse sign info list
246         List<SignInfo> inSignInfoList = new ArrayList<>();
247         for (SignedFilePos pos : inSignedFilePosList) {
248             if (pos.getSignInfoOffset() % ALIGNMENT_FOR_SIGNINFO != 0) {
249                 throw new VerifyCodeSignException("SignInfo not aligned in NativeLibInfoSegment");
250             }
251             byte[] signInfoBuffer = new byte[pos.getSignInfoSize()];
252             bf.position(pos.getSignInfoOffset());
253             bf.get(signInfoBuffer);
254             inSignInfoList.add(SignInfo.fromByteArray(signInfoBuffer));
255         }
256         return new Builder().setMagic(inMagic).setSegmentSize(inSegmentSize).setSectionNum(inSectionNum)
257             .setSignedFilePosList(inSignedFilePosList).setFileNameList(inFileNameList)
258             .setSignInfoList(inSignInfoList).setZeroPadding(inZeroPadding).build();
259     }
260 
261     /**
262      * Return a string representation of the object
263      *
264      * @return string representation of the object
265      */
toString()266     public String toString() {
267         return String.format(Locale.ROOT,
268             "SoInfoSegment: magic[%d], length[%d], secNum[%d], signedFileEntryList[%s], fileNameList[%s], "
269                 + "zeroPadding[%s], signInfoList[%s]", this.magic, this.segmentSize, this.sectionNum,
270             Arrays.toString(this.signedFilePosList.toArray()), Arrays.toString(this.fileNameList.toArray()),
271             Arrays.toString(this.zeroPadding), Arrays.toString(this.signInfoList.toArray()));
272     }
273 
274     /**
275      * Builder of NativeLibInfoSegment class
276      */
277     public static class Builder {
278         private int magic = MAGIC_NUM;
279 
280         private int segmentSize;
281 
282         private int sectionNum;
283 
284         private List<SignedFilePos> signedFilePosList = new ArrayList<>();
285 
286         private List<String> fileNameList = new ArrayList<>();
287 
288         private List<SignInfo> signInfoList = new ArrayList<>();
289 
290         private byte[] zeroPadding = new byte[0];
291 
setMagic(int magic)292         public Builder setMagic(int magic) {
293             this.magic = magic;
294             return this;
295         }
296 
setSegmentSize(int segmentSize)297         public Builder setSegmentSize(int segmentSize) {
298             this.segmentSize = segmentSize;
299             return this;
300         }
301 
setSectionNum(int sectionNum)302         public Builder setSectionNum(int sectionNum) {
303             this.sectionNum = sectionNum;
304             return this;
305         }
306 
setSignedFilePosList(List<SignedFilePos> signedFilePosList)307         public Builder setSignedFilePosList(List<SignedFilePos> signedFilePosList) {
308             this.signedFilePosList = signedFilePosList;
309             return this;
310         }
311 
setFileNameList(List<String> fileNameList)312         public Builder setFileNameList(List<String> fileNameList) {
313             this.fileNameList = fileNameList;
314             return this;
315         }
316 
setSignInfoList(List<SignInfo> signInfoList)317         public Builder setSignInfoList(List<SignInfo> signInfoList) {
318             this.signInfoList = signInfoList;
319             return this;
320         }
321 
setZeroPadding(byte[] zeroPadding)322         public Builder setZeroPadding(byte[] zeroPadding) {
323             this.zeroPadding = zeroPadding;
324             return this;
325         }
326 
327         /**
328          * Create a NativeLibInfoSegment object
329          *
330          * @return a NativeLibInfoSegment object
331          */
build()332         public NativeLibInfoSegment build() {
333             return new NativeLibInfoSegment(this);
334         }
335     }
336 }
337