Search in sources :

Example 1 with DataSink

use of com.android.apksig.util.DataSink in project apksig by venshine.

the class VerityTreeBuilder method generateVerityTree.

/**
 * Returns the byte buffer that contains the whole verity tree.
 *
 * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the
 * input file.  If the total size is larger than 4 KB, take this level as input and repeat the
 * same procedure, until the level is within 4 KB.  If salt is given, it will apply to each
 * digestion before the actual data.
 *
 * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt.
 *
 * The tree is currently stored only in memory and is never written out.  Nevertheless, it is
 * the actual verity tree format on disk, and is supposed to be re-generated on device.
 */
public ByteBuffer generateVerityTree(DataSource fileSource) throws IOException {
    int digestSize = mMd.getDigestLength();
    // Calculate the summed area table of level size. In other word, this is the offset
    // table of each level, plus the next non-existing level.
    int[] levelOffset = calculateLevelOffset(fileSource.size(), digestSize);
    ByteBuffer verityBuffer = ByteBuffer.allocate(levelOffset[levelOffset.length - 1]);
    // Generate the hash tree bottom-up.
    for (int i = levelOffset.length - 2; i >= 0; i--) {
        DataSink middleBufferSink = new ByteBufferSink(slice(verityBuffer, levelOffset[i], levelOffset[i + 1]));
        DataSource src;
        if (i == levelOffset.length - 2) {
            src = fileSource;
            digestDataByChunks(src, middleBufferSink);
        } else {
            src = DataSources.asDataSource(slice(verityBuffer.asReadOnlyBuffer(), levelOffset[i + 1], levelOffset[i + 2]));
            digestDataByChunks(src, middleBufferSink);
        }
        // If the output is not full chunk, pad with 0s.
        long totalOutput = divideRoundup(src.size(), CHUNK_SIZE) * digestSize;
        int incomplete = (int) (totalOutput % CHUNK_SIZE);
        if (incomplete > 0) {
            byte[] padding = new byte[CHUNK_SIZE - incomplete];
            middleBufferSink.consume(padding, 0, padding.length);
        }
    }
    return verityBuffer;
}
Also used : DataSink(com.android.apksig.util.DataSink) ByteBuffer(java.nio.ByteBuffer) DataSource(com.android.apksig.util.DataSource)

Example 2 with DataSink

use of com.android.apksig.util.DataSink in project apksig by venshine.

the class ApkSigner method sign.

/**
 * Signs the input APK and outputs the resulting signed APK. The input APK is not modified.
 *
 * @throws IOException if an I/O error is encountered while reading or writing the APKs
 * @throws ApkFormatException if the input APK is malformed
 * @throws NoSuchAlgorithmException if the APK signatures cannot be produced or verified because
 *     a required cryptographic algorithm implementation is missing
 * @throws InvalidKeyException if a signature could not be generated because a signing key is
 *     not suitable for generating the signature
 * @throws SignatureException if an error occurred while generating or verifying a signature
 * @throws IllegalStateException if this signer's configuration is missing required information
 *     or if the signing engine is in an invalid state.
 */
public void sign() throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, IllegalStateException {
    Closeable in = null;
    DataSource inputApk;
    try {
        if (mInputApkDataSource != null) {
            inputApk = mInputApkDataSource;
        } else if (mInputApkFile != null) {
            RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r");
            in = inputFile;
            inputApk = DataSources.asDataSource(inputFile);
        } else {
            throw new IllegalStateException("Input APK not specified");
        }
        Closeable out = null;
        try {
            DataSink outputApkOut;
            DataSource outputApkIn;
            if (mOutputApkDataSink != null) {
                outputApkOut = mOutputApkDataSink;
                outputApkIn = mOutputApkDataSource;
            } else if (mOutputApkFile != null) {
                RandomAccessFile outputFile = new RandomAccessFile(mOutputApkFile, "rw");
                out = outputFile;
                outputFile.setLength(0);
                outputApkOut = DataSinks.asDataSink(outputFile);
                outputApkIn = DataSources.asDataSource(outputFile);
            } else {
                throw new IllegalStateException("Output APK not specified");
            }
            sign(inputApk, outputApkOut, outputApkIn);
        } finally {
            if (out != null) {
                out.close();
            }
        }
    } finally {
        if (in != null) {
            in.close();
        }
    }
}
Also used : ReadableDataSink(com.android.apksig.util.ReadableDataSink) DataSink(com.android.apksig.util.DataSink) RandomAccessFile(java.io.RandomAccessFile) Closeable(java.io.Closeable) DataSource(com.android.apksig.util.DataSource) ByteBufferDataSource(com.android.apksig.internal.util.ByteBufferDataSource)

Example 3 with DataSink

use of com.android.apksig.util.DataSink in project apksig by venshine.

the class ApkSigningBlockUtils method computeOneMbChunkContentDigests.

static void computeOneMbChunkContentDigests(Set<ContentDigestAlgorithm> digestAlgorithms, DataSource[] contents, Map<ContentDigestAlgorithm, byte[]> outputContentDigests) throws IOException, NoSuchAlgorithmException, 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();
        mds[i] = MessageDigest.getInstance(jcaAlgorithm);
    }
    DataSink mdSink = DataSinks.asDataSink(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++;
        }
    }
    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);
        outputContentDigests.put(digestAlgorithm, digest);
    }
}
Also used : DataSink(com.android.apksig.util.DataSink) IOException(java.io.IOException) DataSource(com.android.apksig.util.DataSource) ChainedDataSource(com.android.apksig.internal.util.ChainedDataSource) ByteBufferDataSource(com.android.apksig.internal.util.ByteBufferDataSource) DigestException(java.security.DigestException) MessageDigest(java.security.MessageDigest)

Aggregations

DataSink (com.android.apksig.util.DataSink)3 DataSource (com.android.apksig.util.DataSource)3 ByteBufferDataSource (com.android.apksig.internal.util.ByteBufferDataSource)2 ChainedDataSource (com.android.apksig.internal.util.ChainedDataSource)1 ReadableDataSink (com.android.apksig.util.ReadableDataSink)1 Closeable (java.io.Closeable)1 IOException (java.io.IOException)1 RandomAccessFile (java.io.RandomAccessFile)1 ByteBuffer (java.nio.ByteBuffer)1 DigestException (java.security.DigestException)1 MessageDigest (java.security.MessageDigest)1