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 }