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