Search in sources :

Example 1 with MessageDigestSink

use of com.android.apksigner.core.internal.util.MessageDigestSink in project walle by Meituan-Dianping.

the class V2SchemeSigner method computeContentDigests.

static Map<ContentDigestAlgorithm, byte[]> computeContentDigests(Set<ContentDigestAlgorithm> digestAlgorithms, DataSource[] contents) throws IOException, DigestException {
    // For each digest algorithm the result is computed as follows:
    // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
    //    The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
    //    No chunks are produced for empty (zero length) segments.
    // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's
    //    length in bytes (uint32 little-endian) and the chunk's contents.
    // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of
    //    chunks (uint32 little-endian) and the concatenation of digests of chunks of all
    //    segments in-order.
    long chunkCountLong = 0;
    for (DataSource input : contents) {
        chunkCountLong += getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
    }
    if (chunkCountLong > Integer.MAX_VALUE) {
        throw new DigestException("Input too long: " + chunkCountLong + " chunks");
    }
    int chunkCount = (int) chunkCountLong;
    ContentDigestAlgorithm[] digestAlgorithmsArray = digestAlgorithms.toArray(new ContentDigestAlgorithm[digestAlgorithms.size()]);
    MessageDigest[] mds = new MessageDigest[digestAlgorithmsArray.length];
    byte[][] digestsOfChunks = new byte[digestAlgorithmsArray.length][];
    int[] digestOutputSizes = new int[digestAlgorithmsArray.length];
    for (int i = 0; i < digestAlgorithmsArray.length; i++) {
        ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
        int digestOutputSizeBytes = digestAlgorithm.getChunkDigestOutputSizeBytes();
        digestOutputSizes[i] = digestOutputSizeBytes;
        byte[] concatenationOfChunkCountAndChunkDigests = new byte[5 + chunkCount * digestOutputSizeBytes];
        concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
        setUnsignedInt32LittleEndian(chunkCount, concatenationOfChunkCountAndChunkDigests, 1);
        digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
        String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
        try {
            mds[i] = MessageDigest.getInstance(jcaAlgorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(jcaAlgorithm + " MessageDigest not supported", e);
        }
    }
    MessageDigestSink mdSink = new MessageDigestSink(mds);
    byte[] chunkContentPrefix = new byte[5];
    chunkContentPrefix[0] = (byte) 0xa5;
    int chunkIndex = 0;
    // digest to be more efficient because it's presented with all of its input in one go.
    for (DataSource input : contents) {
        long inputOffset = 0;
        long inputRemaining = input.size();
        while (inputRemaining > 0) {
            int chunkSize = (int) Math.min(inputRemaining, CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
            setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1);
            for (int i = 0; i < mds.length; i++) {
                mds[i].update(chunkContentPrefix);
            }
            try {
                input.feed(inputOffset, chunkSize, mdSink);
            } catch (IOException e) {
                throw new IOException("Failed to read chunk #" + chunkIndex, e);
            }
            for (int i = 0; i < digestAlgorithmsArray.length; i++) {
                MessageDigest md = mds[i];
                byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
                int expectedDigestSizeBytes = digestOutputSizes[i];
                int actualDigestSizeBytes = md.digest(concatenationOfChunkCountAndChunkDigests, 5 + chunkIndex * expectedDigestSizeBytes, expectedDigestSizeBytes);
                if (actualDigestSizeBytes != expectedDigestSizeBytes) {
                    throw new RuntimeException("Unexpected output size of " + md.getAlgorithm() + " digest: " + actualDigestSizeBytes);
                }
            }
            inputOffset += chunkSize;
            inputRemaining -= chunkSize;
            chunkIndex++;
        }
    }
    Map<ContentDigestAlgorithm, byte[]> result = new HashMap<>(digestAlgorithmsArray.length);
    for (int i = 0; i < digestAlgorithmsArray.length; i++) {
        ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
        byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
        MessageDigest md = mds[i];
        byte[] digest = md.digest(concatenationOfChunkCountAndChunkDigests);
        result.put(digestAlgorithm, digest);
    }
    return result;
}
Also used : HashMap(java.util.HashMap) MessageDigestSink(com.android.apksigner.core.internal.util.MessageDigestSink) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) IOException(java.io.IOException) DataSource(com.android.apksigner.core.util.DataSource) DigestException(java.security.DigestException) MessageDigest(java.security.MessageDigest)

Aggregations

MessageDigestSink (com.android.apksigner.core.internal.util.MessageDigestSink)1 DataSource (com.android.apksigner.core.util.DataSource)1 IOException (java.io.IOException)1 DigestException (java.security.DigestException)1 MessageDigest (java.security.MessageDigest)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 HashMap (java.util.HashMap)1