• 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.PageInfoException;
19 import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException;
20 import com.ohos.hapsigntool.utils.LogUtils;
21 
22 import java.nio.ByteBuffer;
23 import java.nio.ByteOrder;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.Locale;
28 
29 /**
30  * Sign info represents information after signing a file, including signature, merkle tree.
31  * Structure:
32  * <p>
33  * 1) u32 saltSize: byte size of salt
34  * <p>
35  * 2) u32 sigSize: byte size of signature
36  * <p>
37  * 3) u32 flags: reserved flags
38  * <p>
39  * 4) u64 dataSize: byte size of data being signed
40  * <p>
41  * 5) u8[32] salt: salt used in signing
42  * <p>
43  * 6) u32 extensionNum: number of extension
44  * <p>
45  * 7) u32 extensionOffset
46  * <p>
47  * 8) u8[] signature: signature of the data
48  * <p>
49  * MerkleTree is represented as an extension of the sign info.
50  * Its structure is defined in MerkleTreeExtension.java
51  *
52  * @since 2023/09/08
53  */
54 public class SignInfo {
55     /**
56      * merkle tree extension is included in sign info
57      */
58     public static final int FLAG_MERKLE_TREE_INCLUDED = 0x1;
59 
60     /**
61      * maximum of extension number
62      */
63     public static final int MAX_EXTENSION_NUM = 2;
64 
65     private static final LogUtils LOGGER = new LogUtils(SignInfo.class);
66 
67     /**
68      * sign info structure without signature in bytes, refer to toByteArray() method
69      */
70     private static final int SIGN_INFO_SIZE_WITHOUT_SIGNATURE = 60;
71 
72     private static final int SALT_BUFFER_LENGTH = 32;
73 
74     private static final int SIGNATURE_ALIGNMENT = 4;
75 
76     private int saltSize;
77 
78     private int sigSize;
79 
80     private int flags;
81 
82     private long dataSize;
83 
84     private byte[] salt;
85 
86     private int extensionNum;
87 
88     private int extensionOffset;
89 
90     private byte[] signature;
91 
92     private byte[] zeroPadding;
93 
94     // temporary, use list instead
95     private List<Extension> extensionList = new ArrayList<>();
96 
97     /**
98      * Constructor for SignInfo
99      *
100      * @param saltSize byte size of salt
101      * @param flags    reserved flags
102      * @param dataSize byte size of data being signed
103      * @param salt     salt in byte array representation
104      * @param sig      signature after signing the data in byte array representation
105      */
SignInfo(int saltSize, int flags, long dataSize, byte[] salt, byte[] sig)106     public SignInfo(int saltSize, int flags, long dataSize, byte[] salt, byte[] sig) {
107         this.saltSize = saltSize;
108         this.flags = flags;
109         this.dataSize = dataSize;
110         if (salt == null) {
111             this.salt = new byte[SALT_BUFFER_LENGTH];
112         } else {
113             this.salt = salt;
114         }
115         this.signature = sig;
116         this.sigSize = sig == null ? 0 : sig.length;
117         // align for extension after signature
118         this.zeroPadding = new byte[(SIGNATURE_ALIGNMENT - (this.sigSize % SIGNATURE_ALIGNMENT)) % SIGNATURE_ALIGNMENT];
119         this.extensionOffset = SIGN_INFO_SIZE_WITHOUT_SIGNATURE + sigSize + this.zeroPadding.length;
120     }
121 
122     /**
123      * Constructor by a SignInfoBuilder
124      *
125      * @param builder SignInfoBuilder
126      */
SignInfo(SignInfoBuilder builder)127     private SignInfo(SignInfoBuilder builder) {
128         this.saltSize = builder.saltSize;
129         this.sigSize = builder.sigSize;
130         this.flags = builder.flags;
131         this.dataSize = builder.dataSize;
132         this.salt = builder.salt;
133         this.extensionNum = builder.extensionNum;
134         this.extensionOffset = builder.extensionOffset;
135         this.signature = builder.signature;
136         this.zeroPadding = builder.zeroPadding;
137         this.extensionList = builder.extensionList;
138     }
139 
140     /**
141      * Add one Extension into SignInfo Object
142      *
143      * @param extension Extension object
144      */
addExtension(Extension extension)145     public void addExtension(Extension extension) {
146         this.extensionList.add(extension);
147         this.extensionNum = this.extensionList.size();
148     }
149 
150     /**
151      * Get Extension from SignInfo based on extension type
152      *
153      * @param type extension type
154      * @return Extension object
155      */
getExtensionByType(int type)156     public Extension getExtensionByType(int type) {
157         for (Extension ext : this.extensionList) {
158             if (ext.isType(type)) {
159                 return ext;
160             }
161         }
162         return null;
163     }
164 
165     /**
166      * Returns extensionNum
167      *
168      * @return extensionNum
169      */
getExtensionNum()170     public int getExtensionNum() {
171         return extensionNum;
172     }
173 
getSignature()174     public byte[] getSignature() {
175         return signature;
176     }
177 
getDataSize()178     public long getDataSize() {
179         return dataSize;
180     }
181 
182     /**
183      * Returns byte size of SignInfo object
184      *
185      * @return byte size of SignInfo object
186      */
size()187     public int size() {
188         int blockSize = SIGN_INFO_SIZE_WITHOUT_SIGNATURE + this.signature.length + this.zeroPadding.length;
189         for (Extension ext : this.extensionList) {
190             blockSize += ext.size();
191         }
192         return blockSize;
193     }
194 
195     /**
196      * Converts SignInfo to a newly created byte array
197      *
198      * @return Byte array representation of SignInfo
199      */
toByteArray()200     public byte[] toByteArray() {
201         ByteBuffer bf = ByteBuffer.allocate(this.size()).order(ByteOrder.LITTLE_ENDIAN);
202         bf.putInt(this.saltSize);
203         bf.putInt(this.sigSize);
204         bf.putInt(this.flags);
205         bf.putLong(this.dataSize);
206         bf.put(this.salt);
207         bf.putInt(this.extensionNum);
208         bf.putInt(this.extensionOffset);
209         bf.put(this.signature);
210         bf.put(this.zeroPadding);
211         // put extension
212         for (Extension ext : this.extensionList) {
213             bf.put(ext.toByteArray());
214         }
215         return bf.array();
216     }
217 
218     /**
219      * Init the SignInfo by a byte array
220      *
221      * @param bytes Byte array representation of a SignInfo object
222      * @return a newly created SignInfo object
223      * @throws VerifyCodeSignException parsing result invalid
224      */
fromByteArray(byte[] bytes)225     public static SignInfo fromByteArray(byte[] bytes) throws VerifyCodeSignException {
226         ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN);
227         bf.put(bytes);
228         bf.rewind();
229         int inSaltSize = bf.getInt();
230         if (inSaltSize < 0) {
231             throw new VerifyCodeSignException("Invalid saltSize of SignInfo");
232         }
233         int inSigSize = bf.getInt();
234         if (inSigSize < 0) {
235             throw new VerifyCodeSignException("Invalid sigSize of SignInfo");
236         }
237         int inFlags = bf.getInt();
238         if (inFlags != 0 && inFlags != FLAG_MERKLE_TREE_INCLUDED) {
239             throw new VerifyCodeSignException("Invalid flags of SignInfo");
240         }
241         long inDataSize = bf.getLong();
242         if (inDataSize < 0) {
243             throw new VerifyCodeSignException("Invalid dataSize of SignInfo");
244         }
245         byte[] inSalt = new byte[SALT_BUFFER_LENGTH];
246         bf.get(inSalt);
247         int inExtensionNum = bf.getInt();
248         if (inExtensionNum < 0 || inExtensionNum > MAX_EXTENSION_NUM) {
249             LOGGER.info("The signature information may be generated by an new tool, extensionNum {} of SignInfo",
250                 inExtensionNum);
251         }
252         int inExtensionOffset = bf.getInt();
253         if (inExtensionOffset < 0 || inExtensionOffset % 4 != 0) {
254             throw new VerifyCodeSignException("Invalid extensionOffset of SignInfo");
255         }
256         byte[] inSignature = new byte[inSigSize];
257         bf.get(inSignature);
258         byte[] inZeroPadding = new byte[(SIGNATURE_ALIGNMENT - (inSigSize % SIGNATURE_ALIGNMENT))
259             % SIGNATURE_ALIGNMENT];
260         bf.get(inZeroPadding);
261         // parse merkle tree extension
262         List<Extension> inExtensionList = parseExtensionList(bf, inExtensionNum, inDataSize);
263         return new SignInfoBuilder().setSaltSize(inSaltSize)
264             .setSigSize(inSigSize)
265             .setFlags(inFlags)
266             .setDataSize(inDataSize)
267             .setSalt(inSalt)
268             .setExtensionNum(inExtensionNum)
269             .setExtensionOffset(inExtensionOffset)
270             .setSignature(inSignature)
271             .setZeroPadding(inZeroPadding)
272             .setExtensionList(inExtensionList)
273             .build();
274     }
275 
parseExtensionList(ByteBuffer bf, int inExtensionNum, long inDataSize)276     private static List<Extension> parseExtensionList(ByteBuffer bf, int inExtensionNum, long inDataSize)
277         throws VerifyCodeSignException {
278         List<Extension> inExtensionList = new ArrayList<>();
279         for (int i = 0; i < inExtensionNum; i++) {
280             int extensionType = bf.getInt();
281             if (extensionType == MerkleTreeExtension.MERKLE_TREE_INLINED) {
282                 // parse merkle tree extension
283                 int extensionSize = bf.getInt();
284                 if (extensionSize != (MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE)) {
285                     throw new VerifyCodeSignException("Invalid MerkleTree extensionSize of SignInfo");
286                 }
287                 byte[] merkleTreeExtension = new byte[extensionSize];
288                 bf.get(merkleTreeExtension);
289                 inExtensionList.add(MerkleTreeExtension.fromByteArray(merkleTreeExtension));
290             } else if (extensionType == PageInfoExtension.PAGE_INFO_INLINED) {
291                 // parse page info extension
292                 int extensionSize = bf.getInt();
293                 if (extensionSize < (PageInfoExtension.PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN)) {
294                     throw new VerifyCodeSignException("Invalid PageInfo extensionSize of SignInfo");
295                 }
296                 byte[] extensionBytes = new byte[extensionSize];
297                 bf.get(extensionBytes);
298                 PageInfoExtension pageInfoExtension = PageInfoExtension.fromByteArray(extensionBytes);
299                 try {
300                     PageInfoExtension.valid(pageInfoExtension, inDataSize);
301                 } catch (PageInfoException e) {
302                     throw new VerifyCodeSignException(e.getMessage());
303                 }
304                 inExtensionList.add(pageInfoExtension);
305             } else {
306                 LOGGER.info("Invalid extensionType {} of SignInfo", extensionType);
307             }
308         }
309         return inExtensionList;
310     }
311 
312     /**
313      * Return a string representation of the object
314      *
315      * @return string representation of the object
316      */
toString()317     public String toString() {
318         String str = String.format(Locale.ROOT, "SignInfo: saltSize[%d], sigSize[%d],"
319                 + "flags[%d], dataSize[%d], salt[%s], zeroPad[%s], extNum[%d], extOffset[%d]",
320             this.saltSize, this.sigSize, this.flags, this.dataSize, Arrays.toString(this.salt),
321             Arrays.toString(this.zeroPadding), this.extensionNum, this.extensionOffset);
322         if (this.getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED) != null) {
323             str += String.format(Locale.ROOT, "SignInfo.merkleTreeExtension[%s]",
324                 this.getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED).toString());
325         }
326         return str;
327     }
328 
329     /**
330      * Builder of SignInfo object
331      */
332     public static class SignInfoBuilder {
333         private int saltSize;
334 
335         private int sigSize;
336 
337         private int flags;
338 
339         private long dataSize;
340 
341         private byte[] salt;
342 
343         private int extensionNum;
344 
345         private int extensionOffset;
346 
347         private byte[] signature;
348 
349         private byte[] zeroPadding;
350 
351         // temporary, use list instead
352         private List<Extension> extensionList = new ArrayList<>();
353 
354         /**
355          * set saltSize
356          *
357          * @param saltSize saltSize
358          * @return SignInfoBuilder
359          */
setSaltSize(int saltSize)360         public SignInfoBuilder setSaltSize(int saltSize) {
361             this.saltSize = saltSize;
362             return this;
363         }
364 
365         /**
366          * set sigSize
367          *
368          * @param sigSize sigSize
369          * @return SignInfoBuilder
370          */
setSigSize(int sigSize)371         public SignInfoBuilder setSigSize(int sigSize) {
372             this.sigSize = sigSize;
373             return this;
374         }
375 
376         /**
377          * set flags
378          *
379          * @param flags flags
380          * @return SignInfoBuilder
381          */
setFlags(int flags)382         public SignInfoBuilder setFlags(int flags) {
383             this.flags = flags;
384             return this;
385         }
386 
387         /**
388          * set dataSize
389          *
390          * @param dataSize dataSize
391          * @return SignInfoBuilder
392          */
setDataSize(long dataSize)393         public SignInfoBuilder setDataSize(long dataSize) {
394             this.dataSize = dataSize;
395             return this;
396         }
397 
398         /**
399          * set salt
400          *
401          * @param salt salt
402          * @return SignInfoBuilder
403          */
setSalt(byte[] salt)404         public SignInfoBuilder setSalt(byte[] salt) {
405             this.salt = salt;
406             return this;
407         }
408 
409         /**
410          * set extensionNum
411          *
412          * @param extensionNum extensionNum
413          * @return SignInfoBuilder
414          */
setExtensionNum(int extensionNum)415         public SignInfoBuilder setExtensionNum(int extensionNum) {
416             this.extensionNum = extensionNum;
417             return this;
418         }
419 
420         /**
421          * set extensionOffset
422          *
423          * @param extensionOffset extensionOffset
424          * @return SignInfoBuilder
425          */
setExtensionOffset(int extensionOffset)426         public SignInfoBuilder setExtensionOffset(int extensionOffset) {
427             this.extensionOffset = extensionOffset;
428             return this;
429         }
430 
431         /**
432          * set signature
433          *
434          * @param signature signature
435          * @return SignInfoBuilder
436          */
setSignature(byte[] signature)437         public SignInfoBuilder setSignature(byte[] signature) {
438             this.signature = signature;
439             return this;
440         }
441 
442         /**
443          * set zeroPadding
444          *
445          * @param zeroPadding zeroPadding
446          * @return SignInfoBuilder
447          */
setZeroPadding(byte[] zeroPadding)448         public SignInfoBuilder setZeroPadding(byte[] zeroPadding) {
449             this.zeroPadding = zeroPadding;
450             return this;
451         }
452 
453         /**
454          * set extensionList
455          *
456          * @param extensionList extensionList
457          * @return SignInfoBuilder
458          */
setExtensionList(List<Extension> extensionList)459         public SignInfoBuilder setExtensionList(List<Extension> extensionList) {
460             this.extensionList = extensionList;
461             return this;
462         }
463 
464         /**
465          * return a SignInfo object
466          *
467          * @return SignInfo object
468          */
build()469         public SignInfo build() {
470             return new SignInfo(this);
471         }
472     }
473 }
474