• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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.utils;
17 
18 import com.ohos.hapsigntool.hap.entity.Pair;
19 import com.ohos.hapsigntool.hap.entity.SigningBlock;
20 import com.ohos.hapsigntool.hap.exception.SignatureNotFoundException;
21 import com.ohos.hapsigntool.hap.sign.ContentDigestAlgorithm;
22 import com.ohos.hapsigntool.hap.sign.SignHap;
23 import com.ohos.hapsigntool.zip.MessageDigestZipDataOutput;
24 import com.ohos.hapsigntool.zip.ZipDataInput;
25 import com.ohos.hapsigntool.zip.ZipDataOutput;
26 import com.ohos.hapsigntool.zip.ZipFileInfo;
27 
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.bouncycastle.util.Arrays;
31 
32 import java.io.ByteArrayOutputStream;
33 import java.io.FileInputStream;
34 import java.io.IOException;
35 import java.nio.BufferUnderflowException;
36 import java.nio.ByteBuffer;
37 import java.nio.ByteOrder;
38 import java.security.DigestException;
39 import java.security.MessageDigest;
40 import java.security.NoSuchAlgorithmException;
41 import java.util.Collections;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47 
48 /**
49  * Hap util, parse hap, find signature block.
50  *
51  * @since 2021/12/20
52  */
53 public class HapUtils {
54     private static final Logger LOGGER = LogManager.getLogger(HapUtils.class);
55 
56     /**
57      * ID of hap signature blocks of version 1
58      */
59     public static final int HAP_SIGNATURE_SCHEME_V1_BLOCK_ID = 0x20000000;
60 
61     /**
62      * ID of hap proof of rotation block
63      */
64     public static final int HAP_PROOF_OF_ROTATION_BLOCK_ID = 0x20000001;
65 
66     /**
67      * ID of profile block
68      */
69     public static final int HAP_PROFILE_BLOCK_ID = 0x20000002;
70 
71     /**
72      * ID of property block
73      */
74     public static final int HAP_PROPERTY_BLOCK_ID = 0x20000003;
75 
76     /**
77      * ID of property block
78      */
79     public static final int HAP_CODE_SIGN_BLOCK_ID = 0x30000001;
80 
81     /**
82      * The size of data block used to get digest
83      */
84 
85     public static final int CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024;
86 
87     /**
88      * Content version
89      */
90     public static final int CONTENT_VERSION = 2;
91 
92     /**
93      * bit size
94      */
95     public static final int BIT_SIZE = 8;
96 
97     /**
98      * half bit size
99      */
100     public static final int HALF_BIT_SIZE = 4;
101 
102     /**
103      * int size
104      */
105     public static final int INT_SIZE = 4;
106 
107     /**
108      * block number
109      */
110     public static final int BLOCK_NUMBER = 1;
111 
112     /**
113      * hap sign schema v2 signature block version
114      */
115     public static final int HAP_SIGN_SCHEME_V2_BLOCK_VERSION = 2;
116 
117     /**
118      * hap sign schema v3 signature block version
119      */
120     public static final int HAP_SIGN_SCHEME_V3_BLOCK_VERSION = 3;
121 
122     /**
123      * The value of lower 8-bytes of old magic word
124      */
125     public static final long HAP_SIG_BLOCK_MAGIC_LO_V2 = 0x2067695320504148L;
126 
127     /**
128      * The value of higher 8-bytes of old magic word
129      */
130     public static final long HAP_SIG_BLOCK_MAGIC_HI_V2 = 0x3234206b636f6c42L;
131 
132     /**
133      * The value of lower 8 bytes of magic word
134      */
135     public static final long HAP_SIG_BLOCK_MAGIC_LO_V3 = 0x676973207061683cL;
136 
137     /**
138      * The value of higher 8 bytes of magic word
139      */
140     public static final long HAP_SIG_BLOCK_MAGIC_HI_V3 = 0x3e6b636f6c62206eL;
141 
142     /**
143      * Size of hap signature block header
144      */
145     public static final int HAP_SIG_BLOCK_HEADER_SIZE = 32;
146 
147     /**
148      * The min size of hap signature block
149      */
150     public static final int HAP_SIG_BLOCK_MIN_SIZE = HAP_SIG_BLOCK_HEADER_SIZE;
151 
152     /**
153      * The set of IDs of optional blocks in hap signature block.
154      */
155     private static final Set<Integer> HAP_SIGNATURE_OPTIONAL_BLOCK_IDS ;
156 
157     /**
158      * Minimum api version for hap sign schema v3.
159      */
160     private static final int MIN_COMPATIBLE_VERSION_FOR_SCHEMA_V3 = 8;
161 
162     /**
163      * Magic word of hap signature block v2
164      */
165     private static final byte[] HAP_SIGNING_BLOCK_MAGIC_V2 =
166             new byte[] {0x48, 0x41, 0x50, 0x20, 0x53, 0x69, 0x67, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32};
167 
168     /**
169      * Magic word of hap signature block
170      */
171     private static final byte[] HAP_SIGNING_BLOCK_MAGIC_V3 =
172             new byte[] {0x3c, 0x68, 0x61, 0x70, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3e};
173 
174     private static final byte ZIP_FIRST_LEVEL_CHUNK_PREFIX = 0x5a;
175     private static final byte ZIP_SECOND_LEVEL_CHUNK_PREFIX = (byte) 0xa5;
176     private static final int DIGEST_PRIFIX_LENGTH = 5;
177     private static final int BUFFER_LENGTH = 4096;
178     private static final char[] HEX_CHAR_ARRAY = "0123456789ABCDEF".toCharArray();
179 
180     /**
181      * The set of IDs of optional blocks in hap signature block.
182      */
183     static {
184         Set<Integer> blockIds = new HashSet<Integer>();
185         blockIds.add(HAP_PROOF_OF_ROTATION_BLOCK_ID);
186         blockIds.add(HAP_PROFILE_BLOCK_ID);
187         blockIds.add(HAP_PROPERTY_BLOCK_ID);
188         HAP_SIGNATURE_OPTIONAL_BLOCK_IDS = Collections.unmodifiableSet(blockIds);
189     }
190 
HapUtils()191     private HapUtils() {
192     }
193 
194     /**
195      * Get HAP_SIGNATURE_OPTIONAL_BLOCK_IDS
196      *
197      * @return HAP_SIGNATURE_OPTIONAL_BLOCK_IDS
198      */
getHapSignatureOptionalBlockIds()199     public static Set<Integer> getHapSignatureOptionalBlockIds() {
200         return HAP_SIGNATURE_OPTIONAL_BLOCK_IDS;
201     }
202 
203     /**
204      * Get HAP_SIGNING_BLOCK_MAGIC
205      *
206      * @param compatibleVersion compatible api version
207      * @return HAP_SIGNING_BLOCK_MAGIC
208      */
getHapSigningBlockMagic(int compatibleVersion)209     public static byte[] getHapSigningBlockMagic(int compatibleVersion) {
210         if (compatibleVersion >= MIN_COMPATIBLE_VERSION_FOR_SCHEMA_V3) {
211             return HAP_SIGNING_BLOCK_MAGIC_V3.clone();
212         }
213         return HAP_SIGNING_BLOCK_MAGIC_V2.clone();
214     }
215 
216     /**
217      * Get version number of hap signature block
218      *
219      * @param compatibleVersion compatible api version
220      * @return magic to number
221      */
getHapSigningBlockVersion(int compatibleVersion)222     public static int getHapSigningBlockVersion(int compatibleVersion) {
223         if (compatibleVersion >= MIN_COMPATIBLE_VERSION_FOR_SCHEMA_V3) {
224             return HAP_SIGN_SCHEME_V3_BLOCK_VERSION;
225         }
226         return HAP_SIGN_SCHEME_V2_BLOCK_VERSION;
227     }
228 
229     /**
230      * Read data from hap file.
231      *
232      * @param file input file path.
233      * @return true, if read successfully.
234      * @throws IOException on error.
235      */
readFileToByte(String file)236     public static byte[] readFileToByte(String file) throws IOException {
237         try (FileInputStream in = new FileInputStream(file);
238              ByteArrayOutputStream out = new ByteArrayOutputStream(in.available());) {
239             byte[] buf = new byte[BUFFER_LENGTH];
240             int len = 0;
241             while ((len = in.read(buf)) != -1) {
242                 out.write(buf, 0, len);
243             }
244             return out.toByteArray();
245         }
246     }
247 
getChunkCount(ZipDataInput[] contents)248     private static long getChunkCount(ZipDataInput[] contents) {
249         long chunkCount = 0L;
250         for (ZipDataInput content : contents) {
251             chunkCount += ((content.size() + CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES - 1)
252                     / CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
253         }
254         return chunkCount;
255     }
256 
257     /**
258      * compute digests of contents
259      *
260      * @param digestAlgorithms algorithm of digest
261      * @param zipData content used to get digest
262      * @param optionalBlocks list of optional blocks used to get digest
263      * @return digests
264      * @throws DigestException digest error
265      * @throws IOException if an IO error occurs when compute hap file digest
266      */
computeDigests( Set<ContentDigestAlgorithm> digestAlgorithms, ZipDataInput[] zipData, List<SigningBlock> optionalBlocks)267     public static Map<ContentDigestAlgorithm, byte[]> computeDigests(
268             Set<ContentDigestAlgorithm> digestAlgorithms, ZipDataInput[] zipData, List<SigningBlock> optionalBlocks)
269             throws DigestException, IOException {
270         long chunkCountLong = getChunkCount(zipData);
271         if (chunkCountLong > Integer.MAX_VALUE) {
272             throw new DigestException("Input too long: " + chunkCountLong + " chunks");
273         }
274         int chunkCount = (int) chunkCountLong;
275         ContentDigestAlgorithm[] contentDigestAlgorithms = digestAlgorithms.toArray(
276                 new ContentDigestAlgorithm[digestAlgorithms.size()]);
277         MessageDigest[] messageDigests = new MessageDigest[contentDigestAlgorithms.length];
278         int[] digestOutputSizes = new int[contentDigestAlgorithms.length];
279         byte[][] digestOfChunks = new byte[contentDigestAlgorithms.length][];
280         initComputeItem(chunkCount, contentDigestAlgorithms, messageDigests, digestOutputSizes, digestOfChunks);
281         int chunkIndex = 0;
282         byte[] chunkContentPrefix = new byte[DIGEST_PRIFIX_LENGTH];
283         chunkContentPrefix[0] = ZIP_SECOND_LEVEL_CHUNK_PREFIX;
284         byte[] buf = new byte[CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES];
285         ZipDataOutput digests = new MessageDigestZipDataOutput(messageDigests);
286         for (ZipDataInput content : zipData) {
287             long offset = 0L;
288             long remaining = content.size();
289             while (remaining > 0) {
290                 int chunkSize = (int) Math.min(buf.length, remaining);
291                 setUInt32ToByteArrayWithLittleEngian(chunkSize, chunkContentPrefix, 1);
292                 for (int i = 0; i < contentDigestAlgorithms.length; i++) {
293                     messageDigests[i].update(chunkContentPrefix);
294                 }
295                 try {
296                     content.copyTo(offset, chunkSize, digests);
297                 } catch (IOException e) {
298                     throw new IOException("Failed to read chunk #" + chunkIndex, e);
299                 }
300 
301                 getDigests(contentDigestAlgorithms, digestOutputSizes, messageDigests, digestOfChunks, chunkIndex);
302                 offset += chunkSize;
303                 remaining -= chunkSize;
304                 chunkIndex++;
305             }
306         }
307         return getContentDigestAlgorithmMap(optionalBlocks, contentDigestAlgorithms, messageDigests, digestOfChunks);
308     }
309 
getDigests(ContentDigestAlgorithm[] contentDigestAlgorithms, int[] digestOutputSizes, MessageDigest[] messageDigests, byte[][] digestOfChunks, int chunkIndex)310     private static void getDigests(ContentDigestAlgorithm[] contentDigestAlgorithms, int[] digestOutputSizes,
311         MessageDigest[] messageDigests, byte[][] digestOfChunks, int chunkIndex) throws DigestException {
312         for (int i = 0; i < contentDigestAlgorithms.length; i++) {
313             int expectedDigestSizeBytes = digestOutputSizes[i];
314             int actualDigestSizeBytes = messageDigests[i].digest(digestOfChunks[i],
315                     chunkIndex * expectedDigestSizeBytes + DIGEST_PRIFIX_LENGTH, expectedDigestSizeBytes);
316             if (actualDigestSizeBytes != expectedDigestSizeBytes) {
317                 throw new DigestException("Unexpected output size of " + messageDigests[i].getAlgorithm()
318                         + " digest: " + actualDigestSizeBytes);
319             }
320         }
321     }
322 
initComputeItem(int chunkCount, ContentDigestAlgorithm[] contentDigestAlgorithms, MessageDigest[] messageDigests, int[] digestOutputSizes, byte[][] digestOfChunks)323     private static void initComputeItem(int chunkCount, ContentDigestAlgorithm[] contentDigestAlgorithms,
324                                         MessageDigest[] messageDigests, int[] digestOutputSizes,
325                                         byte[][] digestOfChunks) throws DigestException {
326         try {
327             for (int i = 0; i < contentDigestAlgorithms.length; i++) {
328                 int digestOutputSizeBytes = contentDigestAlgorithms[i].getDigestOutputByteSize();
329                 byte[] concatenationOfChunkCountAndChunkDigests =
330                         new byte[DIGEST_PRIFIX_LENGTH + chunkCount * digestOutputSizeBytes];
331                 concatenationOfChunkCountAndChunkDigests[0] = ZIP_FIRST_LEVEL_CHUNK_PREFIX;
332                 setUInt32ToByteArrayWithLittleEngian(chunkCount, concatenationOfChunkCountAndChunkDigests, 1);
333                 digestOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
334                 messageDigests[i] = MessageDigest.getInstance(contentDigestAlgorithms[i].getDigestAlgorithm());
335                 digestOutputSizes[i] = contentDigestAlgorithms[i].getDigestOutputByteSize();
336             }
337         } catch (NoSuchAlgorithmException e) {
338             throw new DigestException("Digest algorithm not supported", e);
339         }
340     }
341 
getContentDigestAlgorithmMap(List<SigningBlock> optionalBlocks, ContentDigestAlgorithm[] contentDigestAlgorithms, MessageDigest[] messageDigests, byte[][] digestOfChunks)342     private static Map<ContentDigestAlgorithm, byte[]> getContentDigestAlgorithmMap(List<SigningBlock> optionalBlocks,
343         ContentDigestAlgorithm[] contentDigestAlgorithms, MessageDigest[] messageDigests, byte[][] digestOfChunks) {
344         Map<ContentDigestAlgorithm, byte[]> result = new HashMap<>(contentDigestAlgorithms.length);
345         for (int i = 0; i < contentDigestAlgorithms.length; i++) {
346             messageDigests[i].update(digestOfChunks[i]);
347             for (SigningBlock signingBlock : optionalBlocks) {
348                 messageDigests[i].update(signingBlock.getValue());
349             }
350             result.put(contentDigestAlgorithms[i], messageDigests[i].digest());
351         }
352         return result;
353     }
354 
setUInt32ToByteArrayWithLittleEngian(int value, byte[] result, int offset)355     private static void setUInt32ToByteArrayWithLittleEngian(int value, byte[] result, int offset) {
356         for (int i = 0; i < INT_SIZE; i++) {
357             result[offset + i] = (byte) ((value >> (BIT_SIZE * i)) & 0xff);
358         }
359     }
360 
361     /**
362      * Slice buffer to target size.
363      *
364      * @param source input data buffer
365      * @param targetSize target buffer's size
366      * @return target buffer of target size
367      */
sliceBuffer(ByteBuffer source, int targetSize)368     public static ByteBuffer sliceBuffer(ByteBuffer source, int targetSize) {
369         int limit = source.limit();
370         int position = source.position();
371         int targetLimit = position + targetSize;
372         if ((targetLimit < position) || (targetLimit > limit)) {
373             LOGGER.error("targetSize: " + targetSize);
374             throw new BufferUnderflowException();
375         }
376         try {
377             source.limit(targetLimit);
378             ByteBuffer target = source.slice();
379             target.order(source.order());
380             return target;
381         } finally {
382             source.position(targetLimit);
383             source.limit(limit);
384         }
385     }
386 
sliceBuffer(ByteBuffer source, int startPos, int endPos)387     private static ByteBuffer sliceBuffer(ByteBuffer source, int startPos, int endPos) {
388         int capacity = source.capacity();
389         if (startPos < 0 || endPos < startPos || endPos > capacity) {
390             throw new IllegalArgumentException(
391                     "startPos: " + startPos + ", endPos: " + endPos + ", capacity: " + capacity);
392         }
393         int limit = source.limit();
394         int position = source.position();
395         try {
396             source.position(0);
397             source.limit(endPos);
398             source.position(startPos);
399             ByteBuffer target = source.slice();
400             target.order(source.order());
401             return target;
402         } finally {
403             source.limit(limit);
404             source.position(position);
405         }
406     }
407 
408     /**
409      * Slice buffer from startPos to endPos, and then reverse it.
410      *
411      * @param hapSigningBlock input buffer used to slice.
412      * @param startPos start position of slice buffer.
413      * @param endPos end position of slice buffer.
414      * @return new buffer.
415      */
reverseSliceBuffer(ByteBuffer hapSigningBlock, int startPos, int endPos)416     public static ByteBuffer reverseSliceBuffer(ByteBuffer hapSigningBlock, int startPos, int endPos) {
417         ByteBuffer header = HapUtils.sliceBuffer(hapSigningBlock, startPos, endPos);
418         byte[] signatureBlockBytes = new byte[header.capacity()];
419         header.get(signatureBlockBytes, 0, signatureBlockBytes.length);
420         return ByteBuffer.wrap(Arrays.reverse(signatureBlockBytes));
421     }
422 
423     /**
424      * Check whether buffer is little endian.
425      *
426      * @param buffer ByteBuffer used to check
427      */
checkBufferLittleEndian(ByteBuffer buffer)428     public static void checkBufferLittleEndian(ByteBuffer buffer) {
429         if (buffer.order() == ByteOrder.LITTLE_ENDIAN) {
430             return;
431         }
432         throw new IllegalArgumentException("ByteBuffer is not little endian");
433     }
434 
435     /**
436      * TLV encode list of pairs
437      *
438      * @param pairList input list of pairs
439      * @return byte array after encoding
440      */
encodeListOfPairsToByteArray(List<Pair<Integer, byte[]>> pairList)441     public static byte[] encodeListOfPairsToByteArray(List<Pair<Integer, byte[]>> pairList) {
442         int encodeSize = 0;
443         encodeSize += INT_SIZE + INT_SIZE;
444         for (Pair<Integer, byte[]> pair : pairList) {
445             encodeSize += INT_SIZE + INT_SIZE + INT_SIZE + pair.getSecond().length;
446         }
447         ByteBuffer encodeBytes = ByteBuffer.allocate(encodeSize);
448         encodeBytes.order(ByteOrder.LITTLE_ENDIAN);
449         encodeBytes.putInt(CONTENT_VERSION); // version
450         encodeBytes.putInt(BLOCK_NUMBER); // block number
451         for (Pair<Integer, byte[]> pair : pairList) {
452             byte[] second = pair.getSecond();
453             encodeBytes.putInt(INT_SIZE + INT_SIZE + second.length);
454             encodeBytes.putInt(pair.getFirst());
455             encodeBytes.putInt(second.length);
456             encodeBytes.put(second);
457         }
458         return encodeBytes.array();
459     }
460 
461     /**
462      * Translate value to Hex string.
463      *
464      * @param value input byte array.
465      * @param separator symbol insert between two bytes.
466      * @return a hex-values string.
467      */
toHex(byte[] value, String separator)468     public static String toHex(byte[] value, String separator) {
469         StringBuilder sb = new StringBuilder(value.length + value.length);
470         String useSeparator = separator == null ? "" : separator;
471         int len = value.length;
472         for (int i = 0; i < len; i++) {
473             int hi = (value[i] & 0xff) >>> HALF_BIT_SIZE;
474             int lo = value[i] & 0x0f;
475             sb.append(HEX_CHAR_ARRAY[hi]).append(HEX_CHAR_ARRAY[lo]);
476             if (i != len - 1) {
477                 sb.append(useSeparator);
478             }
479         }
480         return sb.toString();
481     }
482 
483     /**
484      * find signing block from hap file
485      *
486      * @param hap ZipDataInput object of zip file
487      * @param zipInfo ZipFileInfo object of hap file
488      * @return pair of offset of signing block and data of signing block
489      * @throws SignatureNotFoundException No signing block is found
490      * @throws IOException file operation error
491      */
findHapSigningBlock(ZipDataInput hap, ZipFileInfo zipInfo)492     public static HapSignBlockInfo findHapSigningBlock(ZipDataInput hap, ZipFileInfo zipInfo)
493             throws SignatureNotFoundException, IOException {
494         long centralDirectoryStartOffset = zipInfo.getCentralDirectoryOffset();
495         long centralDirectorySize = zipInfo.getCentralDirectorySize();
496         long eocdOffset = zipInfo.getEocdOffset();
497         long centralDirectoryEndOffset = centralDirectoryStartOffset + centralDirectorySize;
498         if (eocdOffset != centralDirectoryEndOffset) {
499             throw new SignatureNotFoundException("ZIP Central Directory is not immediately followed by End of Central"
500                     + "Directory. CD end: " + centralDirectoryEndOffset + ", EoCD start: " + eocdOffset);
501         }
502         if (centralDirectoryStartOffset < HAP_SIG_BLOCK_MIN_SIZE) {
503             throw new SignatureNotFoundException("Hap too small for Hap Signing Block. ZIP Central Directory offset: "
504                     + centralDirectoryStartOffset);
505         }
506         long hapSigningBlockHeaderOffset = centralDirectoryStartOffset - HAP_SIG_BLOCK_HEADER_SIZE;
507         ByteBuffer hapSigningBlockHeader = hap.createByteBuffer(hapSigningBlockHeaderOffset, HAP_SIG_BLOCK_HEADER_SIZE);
508         hapSigningBlockHeader.order(ByteOrder.LITTLE_ENDIAN);
509         int blockCount = hapSigningBlockHeader.getInt();
510         long hapSigBlockSize = hapSigningBlockHeader.getLong();
511         long hapSignBlockMagicLo = hapSigningBlockHeader.getLong();
512         long hapSignBlockMagicHi = hapSigningBlockHeader.getLong();
513         int version = hapSigningBlockHeader.getInt();
514         long hapSigningBlockOffset = verifySignBlock(hapSigBlockSize,
515                 hapSignBlockMagicLo, hapSignBlockMagicHi, version, centralDirectoryStartOffset);
516         ByteBuffer hapSigningBlockByteBuffer = hap.createByteBuffer(hapSigningBlockOffset, (int) hapSigBlockSize)
517                 .order(ByteOrder.LITTLE_ENDIAN);
518         LOGGER.info("Find Hap Signing Block success, version: {}, block count: {}", version, blockCount);
519         return new HapSignBlockInfo(hapSigningBlockOffset, version, hapSigningBlockByteBuffer);
520     }
521 
verifySignBlock(long hapSigBlockSize, long hapSignBlockMagicLo, long hapSignBlockMagicHi, int version, long centralDirectoryStartOffset)522     private static long verifySignBlock(long hapSigBlockSize, long hapSignBlockMagicLo,
523         long hapSignBlockMagicHi, int version, long centralDirectoryStartOffset) throws SignatureNotFoundException {
524         if (!isVersionAndMagicNumValid(version, hapSignBlockMagicLo, hapSignBlockMagicHi)) {
525             throw new SignatureNotFoundException("No Hap Signing Block before ZIP Central Directory");
526         }
527         if ((hapSigBlockSize < HAP_SIG_BLOCK_HEADER_SIZE)
528                 || (hapSigBlockSize > Integer.MAX_VALUE - SignHap.getBlockSize())) {
529             throw new SignatureNotFoundException("Hap Signing Block size out of range: " + hapSigBlockSize);
530         }
531         int totalSize = (int) hapSigBlockSize;
532         long hapSigningBlockOffset = centralDirectoryStartOffset - totalSize;
533         if (hapSigningBlockOffset < 0) {
534             throw new SignatureNotFoundException("Hap Signing Block offset out of range: " + hapSigningBlockOffset);
535         }
536         return hapSigningBlockOffset;
537     }
538 
isVersionAndMagicNumValid(int version, long hapSignBlockMagicLo, long hapSignBlockMagicHi)539     private static boolean isVersionAndMagicNumValid(int version, long hapSignBlockMagicLo, long hapSignBlockMagicHi) {
540         if (version < HAP_SIGN_SCHEME_V3_BLOCK_VERSION) {
541             return hapSignBlockMagicLo == HAP_SIG_BLOCK_MAGIC_LO_V2 && hapSignBlockMagicHi == HAP_SIG_BLOCK_MAGIC_HI_V2;
542         }
543         return hapSignBlockMagicLo == HAP_SIG_BLOCK_MAGIC_LO_V3 && hapSignBlockMagicHi == HAP_SIG_BLOCK_MAGIC_HI_V3;
544     }
545 
546     /**
547      * Hap sign block info
548      */
549     public static class HapSignBlockInfo {
550         private final long offset;
551         private final int version;
552         private final ByteBuffer content;
553 
HapSignBlockInfo(long offset, int version, ByteBuffer content)554         public HapSignBlockInfo(long offset, int version, ByteBuffer content) {
555             this.offset = offset;
556             this.version = version;
557             this.content = content;
558         }
559 
getVersion()560         public int getVersion() {
561             return version;
562         }
563 
getContent()564         public ByteBuffer getContent() {
565             return content;
566         }
567 
getOffset()568         public long getOffset() {
569             return offset;
570         }
571     }
572 }