167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman/* 267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Copyright (C) 2018 The Android Open Source Project 367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Licensed under the Apache License, Version 2.0 (the "License"); 567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * you may not use this file except in compliance with the License. 667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * You may obtain a copy of the License at 767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * http://www.apache.org/licenses/LICENSE-2.0 967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 1067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Unless required by applicable law or agreed to in writing, software 1167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * distributed under the License is distributed on an "AS IS" BASIS, 1267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * See the License for the specific language governing permissions and 1467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * limitations under the License. 1567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 1667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 1767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanpackage android.util.apk; 1867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 194acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsiehimport android.util.ArrayMap; 2067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport android.util.Pair; 2167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 2267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.io.FileDescriptor; 2367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.io.IOException; 2467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.io.RandomAccessFile; 2567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.nio.BufferUnderflowException; 2667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.nio.ByteBuffer; 2767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.nio.ByteOrder; 2867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.DigestException; 2967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.MessageDigest; 3067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.NoSuchAlgorithmException; 3167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.spec.AlgorithmParameterSpec; 3267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.spec.MGF1ParameterSpec; 3367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.security.spec.PSSParameterSpec; 344acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsiehimport java.util.Arrays; 3567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanimport java.util.Map; 3667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 3767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman/** 3867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Utility class for an APK Signature Scheme using the APK Signing Block. 3967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 4067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @hide for internal use only. 4167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 4267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashmanfinal class ApkSigningBlockUtils { 4367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 4467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private ApkSigningBlockUtils() { 4567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 4667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 4767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman /** 4867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Returns the APK Signature Scheme block contained in the provided APK file and the 4967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * additional information relevant for verifying the block against the file. 5067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 5167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs 5267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * identifying the appropriate block to find, e.g. the APK Signature Scheme v2 5367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * block ID. 5467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 5567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @throws SignatureNotFoundException if the APK is not signed using this scheme. 5667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @throws IOException if an I/O error occurs while reading the APK file. 5767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 5867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static SignatureInfo findSignature(RandomAccessFile apk, int blockId) 5967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws IOException, SignatureNotFoundException { 6067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Find the ZIP End of Central Directory (EoCD) record. 6167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman Pair<ByteBuffer, Long> eocdAndOffsetInFile = getEocd(apk); 6267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer eocd = eocdAndOffsetInFile.first; 6367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long eocdOffset = eocdAndOffsetInFile.second; 6467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) { 6567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException("ZIP64 APK not supported"); 6667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 6767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 6867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Find the APK Signing Block. The block immediately precedes the Central Directory. 6967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long centralDirOffset = getCentralDirOffset(eocd, eocdOffset); 7067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman Pair<ByteBuffer, Long> apkSigningBlockAndOffsetInFile = 7167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman findApkSigningBlock(apk, centralDirOffset); 7267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer apkSigningBlock = apkSigningBlockAndOffsetInFile.first; 7367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long apkSigningBlockOffset = apkSigningBlockAndOffsetInFile.second; 7467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 7567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Find the APK Signature Scheme Block inside the APK Signing Block. 7667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer apkSignatureSchemeBlock = findApkSignatureSchemeBlock(apkSigningBlock, 7767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman blockId); 7867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 7967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return new SignatureInfo( 8067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apkSignatureSchemeBlock, 8167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apkSigningBlockOffset, 8267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman centralDirOffset, 8367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman eocdOffset, 8467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman eocd); 8567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 8667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 8767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static void verifyIntegrity( 8867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman Map<Integer, byte[]> expectedDigests, 894acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh RandomAccessFile apk, 904acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh SignatureInfo signatureInfo) throws SecurityException { 9167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (expectedDigests.isEmpty()) { 9267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SecurityException("No digests provided"); 9367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 9467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 958d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh boolean neverVerified = true; 968d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh 974acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh Map<Integer, byte[]> expected1MbChunkDigests = new ArrayMap<>(); 984acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) { 994acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA256, 1004acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA256)); 1014acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 1024acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) { 1034acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA512, 1044acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA512)); 1054acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 1068d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh if (!expected1MbChunkDigests.isEmpty()) { 1074acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh try { 1084acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh verifyIntegrityFor1MbChunkBasedAlgorithm(expected1MbChunkDigests, apk.getFD(), 1094acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh signatureInfo); 1108d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh neverVerified = false; 1114acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } catch (IOException e) { 1124acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh throw new SecurityException("Cannot get FD", e); 1134acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 1148d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh } 1158d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh 1168d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh if (expectedDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { 1178d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh verifyIntegrityForVerityBasedAlgorithm( 1188d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh expectedDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256), apk, signatureInfo); 1198d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh neverVerified = false; 1208d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh } 1218d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh 1228d1553b9b1be06100ce4f4cc4c8c5088b48995a2Victor Hsieh if (neverVerified) { 1234acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh throw new SecurityException("No known digest exists for integrity check"); 1244acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 1254acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 1264acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh 1274acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh private static void verifyIntegrityFor1MbChunkBasedAlgorithm( 1284acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh Map<Integer, byte[]> expectedDigests, 1294acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh FileDescriptor apkFileDescriptor, 1304acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh SignatureInfo signatureInfo) throws SecurityException { 13167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // We need to verify the integrity of the following three sections of the file: 13267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 1. Everything up to the start of the APK Signing Block. 13367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 2. ZIP Central Directory. 13467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 3. ZIP End of Central Directory (EoCD). 13567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Each of these sections is represented as a separate DataSource instance below. 13667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 13767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // To handle large APKs, these sections are read in 1 MB chunks using memory-mapped I/O to 13867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // avoid wasting physical memory. In most APK verification scenarios, the contents of the 13967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // APK are already there in the OS's page cache and thus mmap does not use additional 14067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // physical memory. 14167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman DataSource beforeApkSigningBlock = 1424acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh new MemoryMappedFileDataSource(apkFileDescriptor, 0, 1434acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh signatureInfo.apkSigningBlockOffset); 14467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman DataSource centralDir = 14567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman new MemoryMappedFileDataSource( 1464acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh apkFileDescriptor, signatureInfo.centralDirOffset, 1474acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh signatureInfo.eocdOffset - signatureInfo.centralDirOffset); 14867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 14967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // For the purposes of integrity verification, ZIP End of Central Directory's field Start of 15067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Central Directory must be considered to point to the offset of the APK Signing Block. 1514acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh ByteBuffer eocdBuf = signatureInfo.eocd.duplicate(); 15267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman eocdBuf.order(ByteOrder.LITTLE_ENDIAN); 1534acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset); 15467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman DataSource eocd = new ByteBufferDataSource(eocdBuf); 15567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 15667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int[] digestAlgorithms = new int[expectedDigests.size()]; 15767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithmCount = 0; 15867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int digestAlgorithm : expectedDigests.keySet()) { 15967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman digestAlgorithms[digestAlgorithmCount] = digestAlgorithm; 16067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman digestAlgorithmCount++; 16167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 16267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[][] actualDigests; 16367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 16467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman actualDigests = 1654acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh computeContentDigestsPer1MbChunk( 16667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman digestAlgorithms, 16767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); 16867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } catch (DigestException e) { 16967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SecurityException("Failed to compute digest(s) of contents", e); 17067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 17167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < digestAlgorithms.length; i++) { 17267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm = digestAlgorithms[i]; 17367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] expectedDigest = expectedDigests.get(digestAlgorithm); 17467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] actualDigest = actualDigests[i]; 17567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (!MessageDigest.isEqual(expectedDigest, actualDigest)) { 17667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SecurityException( 17767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) 17867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + " digest of contents did not verify"); 17967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 18067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 18167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 18267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 1834acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh private static byte[][] computeContentDigestsPer1MbChunk( 18467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int[] digestAlgorithms, 18567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman DataSource[] contents) throws DigestException { 18667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // For each digest algorithm the result is computed as follows: 18767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 1. Each segment of contents is split into consecutive chunks of 1 MB in size. 18867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // The final chunk will be shorter iff the length of segment is not a multiple of 1 MB. 18967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // No chunks are produced for empty (zero length) segments. 19067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's 19167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // length in bytes (uint32 little-endian) and the chunk's contents. 19267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of 19367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // chunks (uint32 little-endian) and the concatenation of digests of chunks of all 19467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // segments in-order. 19567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 19667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long totalChunkCountLong = 0; 19767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (DataSource input : contents) { 19867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman totalChunkCountLong += getChunkCount(input.size()); 19967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 20067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (totalChunkCountLong >= Integer.MAX_VALUE / 1024) { 20167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new DigestException("Too many chunks: " + totalChunkCountLong); 20267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 20367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int totalChunkCount = (int) totalChunkCountLong; 20467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 20567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[][] digestsOfChunks = new byte[digestAlgorithms.length][]; 20667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < digestAlgorithms.length; i++) { 20767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm = digestAlgorithms[i]; 20867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); 20967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] concatenationOfChunkCountAndChunkDigests = 21067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman new byte[5 + totalChunkCount * digestOutputSizeBytes]; 21167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman concatenationOfChunkCountAndChunkDigests[0] = 0x5a; 21267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman setUnsignedInt32LittleEndian( 21367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman totalChunkCount, 21467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman concatenationOfChunkCountAndChunkDigests, 21567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 1); 21667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests; 21767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 21867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 21967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] chunkContentPrefix = new byte[5]; 22067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman chunkContentPrefix[0] = (byte) 0xa5; 22167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int chunkIndex = 0; 22267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman MessageDigest[] mds = new MessageDigest[digestAlgorithms.length]; 22367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < digestAlgorithms.length; i++) { 22467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman String jcaAlgorithmName = 22567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithms[i]); 22667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 22767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman mds[i] = MessageDigest.getInstance(jcaAlgorithmName); 22867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } catch (NoSuchAlgorithmException e) { 22967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); 23067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 23167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 23267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // TODO: Compute digests of chunks in parallel when beneficial. This requires some research 23367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // into how to parallelize (if at all) based on the capabilities of the hardware on which 23467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // this code is running and based on the size of input. 23567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman DataDigester digester = new MultipleDigestDataDigester(mds); 23667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int dataSourceIndex = 0; 23767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (DataSource input : contents) { 23867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long inputOffset = 0; 23967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long inputRemaining = input.size(); 24067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman while (inputRemaining > 0) { 24167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int chunkSize = (int) Math.min(inputRemaining, CHUNK_SIZE_BYTES); 24267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1); 24367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < mds.length; i++) { 24467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman mds[i].update(chunkContentPrefix); 24567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 24667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 24767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman input.feedIntoDataDigester(digester, inputOffset, chunkSize); 24867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } catch (IOException e) { 24967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new DigestException( 25067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Failed to digest chunk #" + chunkIndex + " of section #" 25167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + dataSourceIndex, 25267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman e); 25367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 25467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < digestAlgorithms.length; i++) { 25567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm = digestAlgorithms[i]; 25667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i]; 25767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int expectedDigestSizeBytes = 25867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm); 25967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman MessageDigest md = mds[i]; 26067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int actualDigestSizeBytes = 26167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman md.digest( 26267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman concatenationOfChunkCountAndChunkDigests, 26367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 5 + chunkIndex * expectedDigestSizeBytes, 26467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman expectedDigestSizeBytes); 26567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (actualDigestSizeBytes != expectedDigestSizeBytes) { 26667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new RuntimeException( 26767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unexpected output size of " + md.getAlgorithm() + " digest: " 26867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + actualDigestSizeBytes); 26967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 27067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 27167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman inputOffset += chunkSize; 27267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman inputRemaining -= chunkSize; 27367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman chunkIndex++; 27467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 27567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman dataSourceIndex++; 27667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 27767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 27867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[][] result = new byte[digestAlgorithms.length][]; 27967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (int i = 0; i < digestAlgorithms.length; i++) { 28067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm = digestAlgorithms[i]; 28167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] input = digestsOfChunks[i]; 28267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm); 28367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman MessageDigest md; 28467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 28567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman md = MessageDigest.getInstance(jcaAlgorithmName); 28667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } catch (NoSuchAlgorithmException e) { 28767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new RuntimeException(jcaAlgorithmName + " digest not supported", e); 28867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 28967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] output = md.digest(input); 29067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result[i] = output; 29167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 29267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return result; 29367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 29467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 2954ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh /** 2964ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * Return the verity digest only if the length of digest content looks correct. 2974ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * When verity digest is generated, the last incomplete 4k chunk is padded with 0s before 2984ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * hashing. This means two almost identical APKs with different number of 0 at the end will have 2994ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * the same verity digest. To avoid this problem, the length of the source content (excluding 3004ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * Signing Block) is appended to the verity digest, and the digest is returned only if the 3014ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh * length is consistent to the current APK. 3024ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh */ 3034ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh static byte[] parseVerityDigestAndVerifySourceLength( 3044ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException { 3054ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh // FORMAT: 3064ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh // OFFSET DATA TYPE DESCRIPTION 3074ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh // * @+0 bytes uint8[32] Merkle tree root hash of SHA-256 3084ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh // * @+32 bytes int64 Length of source data 3094ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh int kRootHashSize = 32; 3104ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh int kSourceLengthSize = 8; 3114ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh 3124ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh if (data.length != kRootHashSize + kSourceLengthSize) { 3134ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh throw new SecurityException("Verity digest size is wrong: " + data.length); 3144ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh } 3154ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); 3164ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh buffer.position(kRootHashSize); 3174ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh long expectedSourceLength = buffer.getLong(); 3184ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh 3194ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh long signingBlockSize = signatureInfo.centralDirOffset 3204ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh - signatureInfo.apkSigningBlockOffset; 3214ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh if (expectedSourceLength != fileSize - signingBlockSize) { 3224ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh throw new SecurityException("APK content size did not verify"); 3234ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh } 3244ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh 3254ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh return Arrays.copyOfRange(data, 0, kRootHashSize); 3264ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh } 3274ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh 3284acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh private static void verifyIntegrityForVerityBasedAlgorithm( 3294ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh byte[] expectedDigest, 3304acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh RandomAccessFile apk, 3314acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh SignatureInfo signatureInfo) throws SecurityException { 3324acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh try { 3334ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest, 3344ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh apk.length(), signatureInfo); 3354acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk, 3364acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh signatureInfo, new ByteBufferFactory() { 3374acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh @Override 3384acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh public ByteBuffer create(int capacity) { 3394acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh return ByteBuffer.allocate(capacity); 3404acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 3414acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh }); 3424acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh if (!Arrays.equals(expectedRootHash, verity.rootHash)) { 3434acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh throw new SecurityException("APK verity digest of contents did not verify"); 3444acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 3454acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } catch (DigestException | IOException | NoSuchAlgorithmException e) { 3464acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh throw new SecurityException("Error during verification", e); 3474acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 3484acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 3494acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh 35067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman /** 35107bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * Generates the fsverity header and hash tree to be used by kernel for the given apk. This 35207bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * method does not check whether the root hash exists in the Signing Block or not. 35307bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * 35407bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * <p>The output is stored in the {@link ByteBuffer} created by the given {@link 35507bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * ByteBufferFactory}. 35607bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * 35707bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh * @return the root hash of the generated hash tree. 35807bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh */ 35907bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory, 36007bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh SignatureInfo signatureInfo) 36107bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh throws IOException, SignatureNotFoundException, SecurityException, DigestException, 36207bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh NoSuchAlgorithmException { 36307bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { 36407bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk, 36507bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh signatureInfo, bufferFactory); 36607bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh return result.rootHash; 36707bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh } 36807bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh } 36907bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh 37007bc80c51b1c099bd1ac5f3056ae739778753af1Victor Hsieh /** 37167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. 37267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 37367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @throws IOException if an I/O error occurs while reading the file. 37467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * @throws SignatureNotFoundException if the EoCD could not be found. 37567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 37667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static Pair<ByteBuffer, Long> getEocd(RandomAccessFile apk) 37767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws IOException, SignatureNotFoundException { 37867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman Pair<ByteBuffer, Long> eocdAndOffsetInFile = 37967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ZipUtils.findZipEndOfCentralDirectoryRecord(apk); 38067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (eocdAndOffsetInFile == null) { 38167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 38267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Not an APK file: ZIP End of Central Directory record not found"); 38367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 38467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return eocdAndOffsetInFile; 38567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 38667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 38767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static long getCentralDirOffset(ByteBuffer eocd, long eocdOffset) 38867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws SignatureNotFoundException { 38967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Look up the offset of ZIP Central Directory. 39067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); 39167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (centralDirOffset > eocdOffset) { 39267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 39367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "ZIP Central Directory offset out of range: " + centralDirOffset 39467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + ". ZIP End of Central Directory offset: " + eocdOffset); 39567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 39667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long centralDirSize = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); 39767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (centralDirOffset + centralDirSize != eocdOffset) { 39867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 39967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "ZIP Central Directory is not immediately followed by End of Central" 40067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + " Directory"); 40167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 40267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return centralDirOffset; 40367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 40467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 40567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static long getChunkCount(long inputSizeBytes) { 40667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES; 40767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 40867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 40967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static final int CHUNK_SIZE_BYTES = 1024 * 1024; 41067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 41167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_RSA_PSS_WITH_SHA256 = 0x0101; 41267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_RSA_PSS_WITH_SHA512 = 0x0102; 41367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0103; 41467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 = 0x0104; 41567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; 41667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; 41767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; 4184ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0421; 4194ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423; 4204ba1eeaa0e0468131da08a5c5f461361cac79ff1Victor Hsieh static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425; 42167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 42267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; 42367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; 4244acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; 42567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 42667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { 42767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); 42867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2); 42967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2); 43067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 43167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 43267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) { 43367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (digestAlgorithm1) { 43467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA256: 43567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (digestAlgorithm2) { 43667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA256: 43767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return 0; 43867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA512: 4394acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 44067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return -1; 44167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 44267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 44367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown digestAlgorithm2: " + digestAlgorithm2); 44467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 44567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA512: 44667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (digestAlgorithm2) { 44767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA256: 4484acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 44967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return 1; 45067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA512: 45167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return 0; 45267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 45367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 45467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown digestAlgorithm2: " + digestAlgorithm2); 45567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 4564acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 4574acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh switch (digestAlgorithm2) { 4584acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_CHUNKED_SHA512: 4594acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh return -1; 4604acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 4614acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh return 0; 4624acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_CHUNKED_SHA256: 4634acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh return 1; 4644acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh default: 4654acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh throw new IllegalArgumentException( 4664acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh "Unknown digestAlgorithm2: " + digestAlgorithm2); 4674acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh } 46867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 46967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1); 47067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 47167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 47267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 47367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) { 47467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (sigAlgorithm) { 47567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA256: 47667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: 47767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA256: 47867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_DSA_WITH_SHA256: 47967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return CONTENT_DIGEST_CHUNKED_SHA256; 48067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA512: 48167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: 48267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA512: 48367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return CONTENT_DIGEST_CHUNKED_SHA512; 4844acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: 4854acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_ECDSA_WITH_SHA256: 4864acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_DSA_WITH_SHA256: 4874acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh return CONTENT_DIGEST_VERITY_CHUNKED_SHA256; 48867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 48967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 49067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown signature algorithm: 0x" 49167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + Long.toHexString(sigAlgorithm & 0xffffffff)); 49267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 49367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 49467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 49567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) { 49667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (digestAlgorithm) { 49767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA256: 4984acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 49967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return "SHA-256"; 50067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA512: 50167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return "SHA-512"; 50267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 50367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 50467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown content digest algorthm: " + digestAlgorithm); 50567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 50667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 50767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 50867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) { 50967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (digestAlgorithm) { 51067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA256: 5114acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: 51267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return 256 / 8; 51367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case CONTENT_DIGEST_CHUNKED_SHA512: 51467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return 512 / 8; 51567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 51667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 51767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown content digest algorthm: " + digestAlgorithm); 51867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 51967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 52067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 52167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) { 52267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (sigAlgorithm) { 52367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA256: 52467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA512: 52567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: 52667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: 5274acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: 52867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return "RSA"; 52967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA256: 53067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA512: 5314acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_ECDSA_WITH_SHA256: 53267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return "EC"; 53367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_DSA_WITH_SHA256: 5344acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_DSA_WITH_SHA256: 53567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return "DSA"; 53667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 53767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 53867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown signature algorithm: 0x" 53967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + Long.toHexString(sigAlgorithm & 0xffffffff)); 54067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 54167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 54267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 54367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static Pair<String, ? extends AlgorithmParameterSpec> 54467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) { 54567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman switch (sigAlgorithm) { 54667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA256: 54767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create( 54867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "SHA256withRSA/PSS", 54967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman new PSSParameterSpec( 55067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1)); 55167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PSS_WITH_SHA512: 55267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create( 55367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "SHA512withRSA/PSS", 55467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman new PSSParameterSpec( 55567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)); 55667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: 5574acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: 55867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create("SHA256withRSA", null); 55967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: 56067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create("SHA512withRSA", null); 56167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA256: 5624acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_ECDSA_WITH_SHA256: 56367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create("SHA256withECDSA", null); 56467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_ECDSA_WITH_SHA512: 56567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create("SHA512withECDSA", null); 56667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman case SIGNATURE_DSA_WITH_SHA256: 5674acad4c01406c50902733e7a70503c06f0d54dbbVictor Hsieh case SIGNATURE_VERITY_DSA_WITH_SHA256: 56867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create("SHA256withDSA", null); 56967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman default: 57067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException( 57167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Unknown signature algorithm: 0x" 57267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + Long.toHexString(sigAlgorithm & 0xffffffff)); 57367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 57467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 57567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 57667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman /** 57767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Returns new byte buffer whose content is a shared subsequence of this buffer's content 57867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * between the specified start (inclusive) and end (exclusive) positions. As opposed to 57967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source 58067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * buffer's byte order. 58167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 58267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) { 58367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (start < 0) { 58467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("start: " + start); 58567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 58667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (end < start) { 58767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("end < start: " + end + " < " + start); 58867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 58967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int capacity = source.capacity(); 59067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (end > source.capacity()) { 59167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity); 59267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 59367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int originalLimit = source.limit(); 59467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int originalPosition = source.position(); 59567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 59667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.position(0); 59767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.limit(end); 59867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.position(start); 59967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer result = source.slice(); 60067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result.order(source.order()); 60167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return result; 60267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } finally { 60367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.position(0); 60467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.limit(originalLimit); 60567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.position(originalPosition); 60667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 60767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 60867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 60967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman /** 61067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * Relative <em>get</em> method for reading {@code size} number of bytes from the current 61167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * position of this buffer. 61267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * 61367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * <p>This method reads the next {@code size} bytes at this buffer's current position, 61467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to 61567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * {@code size}, byte order set to this buffer's byte order; and then increments the position by 61667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * {@code size}. 61767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 61867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static ByteBuffer getByteBuffer(ByteBuffer source, int size) 61967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws BufferUnderflowException { 62067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (size < 0) { 62167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("size: " + size); 62267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 62367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int originalLimit = source.limit(); 62467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int position = source.position(); 62567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int limit = position + size; 62667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if ((limit < position) || (limit > originalLimit)) { 62767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new BufferUnderflowException(); 62867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 62967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.limit(limit); 63067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman try { 63167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer result = source.slice(); 63267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result.order(source.order()); 63367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.position(limit); 63467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return result; 63567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } finally { 63667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman source.limit(originalLimit); 63767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 63867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 63967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 64067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException { 64167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (source.remaining() < 4) { 64267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IOException( 64367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Remaining buffer too short to contain length of length-prefixed field." 64467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + " Remaining: " + source.remaining()); 64567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 64667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int len = source.getInt(); 64767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (len < 0) { 64867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("Negative length"); 64967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } else if (len > source.remaining()) { 65067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IOException("Length-prefixed field longer than remaining buffer." 65167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + " Field length: " + len + ", remaining: " + source.remaining()); 65267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 65367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return getByteBuffer(source, len); 65467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 65567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 65667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException { 65767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int len = buf.getInt(); 65867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (len < 0) { 65967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IOException("Negative length"); 66067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } else if (len > buf.remaining()) { 66167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IOException("Underflow while reading length-prefixed value. Length: " + len 66267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + ", available: " + buf.remaining()); 66367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 66467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman byte[] result = new byte[len]; 66567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman buf.get(result); 66667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return result; 66767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 66867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 66967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) { 67067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result[offset] = (byte) (value & 0xff); 67167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result[offset + 1] = (byte) ((value >>> 8) & 0xff); 67267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result[offset + 2] = (byte) ((value >>> 16) & 0xff); 67367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman result[offset + 3] = (byte) ((value >>> 24) & 0xff); 67467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 67567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 67667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L; 67767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L; 67867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static final int APK_SIG_BLOCK_MIN_SIZE = 32; 67967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 68067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static Pair<ByteBuffer, Long> findApkSigningBlock( 68167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman RandomAccessFile apk, long centralDirOffset) 68267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws IOException, SignatureNotFoundException { 68367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // FORMAT: 68467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // OFFSET DATA TYPE DESCRIPTION 68567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @+0 bytes uint64: size in bytes (excluding this field) 68667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @+8 bytes payload 68767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @-24 bytes uint64: size in bytes (same as the one above) 68867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @-16 bytes uint128: magic 68967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 69067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) { 69167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 69267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK too small for APK Signing Block. ZIP Central Directory offset: " 69367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + centralDirOffset); 69467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 69567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Read the magic and offset in file from the footer section of the block: 69667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * uint64: size of block 69767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * 16 bytes: magic 69867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer footer = ByteBuffer.allocate(24); 69967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman footer.order(ByteOrder.LITTLE_ENDIAN); 70067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apk.seek(centralDirOffset - footer.capacity()); 70167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apk.readFully(footer.array(), footer.arrayOffset(), footer.capacity()); 70267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO) 70367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) { 70467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 70567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "No APK Signing Block before ZIP Central Directory"); 70667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 70767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // Read and compare size fields 70867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long apkSigBlockSizeInFooter = footer.getLong(0); 70967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if ((apkSigBlockSizeInFooter < footer.capacity()) 71067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) { 71167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 71267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK Signing Block size out of range: " + apkSigBlockSizeInFooter); 71367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 71467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int totalSize = (int) (apkSigBlockSizeInFooter + 8); 71567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long apkSigBlockOffset = centralDirOffset - totalSize; 71667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (apkSigBlockOffset < 0) { 71767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 71867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK Signing Block offset out of range: " + apkSigBlockOffset); 71967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 72067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer apkSigBlock = ByteBuffer.allocate(totalSize); 72167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apkSigBlock.order(ByteOrder.LITTLE_ENDIAN); 72267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apk.seek(apkSigBlockOffset); 72367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman apk.readFully(apkSigBlock.array(), apkSigBlock.arrayOffset(), apkSigBlock.capacity()); 72467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long apkSigBlockSizeInHeader = apkSigBlock.getLong(0); 72567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) { 72667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 72767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK Signing Block sizes in header and footer do not match: " 72867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter); 72967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 73067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return Pair.create(apkSigBlock, apkSigBlockOffset); 73167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 73267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 73367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman static ByteBuffer findApkSignatureSchemeBlock(ByteBuffer apkSigningBlock, int blockId) 73467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throws SignatureNotFoundException { 73567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman checkByteOrderLittleEndian(apkSigningBlock); 73667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // FORMAT: 73767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // OFFSET DATA TYPE DESCRIPTION 73867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @+0 bytes uint64: size in bytes (excluding this field) 73967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @+8 bytes pairs 74067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @-24 bytes uint64: size in bytes (same as the one above) 74167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman // * @-16 bytes uint128: magic 74267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24); 74367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 74467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int entryCount = 0; 74567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman while (pairs.hasRemaining()) { 74667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman entryCount++; 74767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (pairs.remaining() < 8) { 74867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 74967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "Insufficient data to read size of APK Signing Block entry #" + entryCount); 75067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 75167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman long lenLong = pairs.getLong(); 75267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) { 75367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 75467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK Signing Block entry #" + entryCount 75567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + " size out of range: " + lenLong); 75667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 75767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int len = (int) lenLong; 75867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int nextEntryPos = pairs.position() + len; 75967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (len > pairs.remaining()) { 76067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 76167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "APK Signing Block entry #" + entryCount + " size out of range: " + len 76267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman + ", available: " + pairs.remaining()); 76367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 76467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman int id = pairs.getInt(); 76567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (id == blockId) { 76667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman return getByteBuffer(pairs, len - 4); 76767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 76867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman pairs.position(nextEntryPos); 76967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 77067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 77167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new SignatureNotFoundException( 77267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman "No block with ID " + blockId + " in APK Signing Block."); 77367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 77467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 77567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static void checkByteOrderLittleEndian(ByteBuffer buffer) { 77667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman if (buffer.order() != ByteOrder.LITTLE_ENDIAN) { 77767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman throw new IllegalArgumentException("ByteBuffer byte order must be little endian"); 77867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 77967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 78067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 78167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman /** 78267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman * {@link DataDigester} that updates multiple {@link MessageDigest}s whenever data is fed. 78367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman */ 78467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private static class MultipleDigestDataDigester implements DataDigester { 78567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman private final MessageDigest[] mMds; 78667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 78767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman MultipleDigestDataDigester(MessageDigest[] mds) { 78867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman mMds = mds; 78967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 79067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 79167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman @Override 79267096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman public void consume(ByteBuffer buffer) { 79367096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman buffer = buffer.slice(); 79467096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman for (MessageDigest md : mMds) { 79567096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman buffer.position(0); 79667096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman md.update(buffer); 79767096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 79867096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 79967096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman } 80067096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman 80167096e08a72beea85979a3aa9fc5b376b2c2b5adDaniel Cashman} 802