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