Loading core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +376 −188 File changed.Preview size limit exceeded, changes collapsed. Show changes core/java/android/util/apk/ZipUtils.java +113 −14 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package android.util.apk; import android.util.Pair; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * Assorted ZIP format helpers. * * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances except that the byte * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte * order of these buffers is little-endian. */ abstract class ZipUtils { Loading @@ -35,9 +39,101 @@ abstract class ZipUtils { private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20; private static final int ZIP64_EOCD_LOCATOR_SIZE = 20; private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50; private static final int ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER = 0x504b0607; private static final int UINT32_MAX_VALUE = 0xffff; private static final int UINT16_MAX_VALUE = 0xffff; /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(RandomAccessFile zip) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. long fileSize = zip.length(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { return null; } // Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus // the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily // reading more data. Pair<ByteBuffer, Long> result = findZipEndOfCentralDirectoryRecord(zip, 0); if (result != null) { return result; } // EoCD does not start where we expected it to. Perhaps it contains a non-empty comment // field. Expand the search. The maximum size of the comment field in EoCD is 65535 because // the comment length field is an unsigned 16-bit number. return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE); } /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @param maxCommentSize maximum accepted size (in bytes) of EoCD comment field. The permitted * value is from 0 to 65535 inclusive. The smaller the value, the faster this method * locates the record, provided its comment field is no longer than this value. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ private static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord( RandomAccessFile zip, int maxCommentSize) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) { throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize); } long fileSize = zip.length(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { // No space for EoCD record in the file. return null; } // Lower maxCommentSize if the file is too small. maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE); ByteBuffer buf = ByteBuffer.allocate(ZIP_EOCD_REC_MIN_SIZE + maxCommentSize); buf.order(ByteOrder.LITTLE_ENDIAN); long bufOffsetInFile = fileSize - buf.capacity(); zip.seek(bufOffsetInFile); zip.readFully(buf.array(), buf.arrayOffset(), buf.capacity()); int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf); if (eocdOffsetInBuf == -1) { // No EoCD record found in the buffer return null; } // EoCD found buf.position(eocdOffsetInBuf); ByteBuffer eocd = buf.slice(); eocd.order(ByteOrder.LITTLE_ENDIAN); return Pair.create(eocd, bufOffsetInFile + eocdOffsetInBuf); } /** * Returns the position at which ZIP End of Central Directory record starts in the provided Loading @@ -45,7 +141,7 @@ abstract class ZipUtils { * * <p>NOTE: Byte order of {@code zipContents} must be little-endian. */ public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { assertByteOrderLittleEndian(zipContents); // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. Loading @@ -56,14 +152,13 @@ abstract class ZipUtils { // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 32-bit number. // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. int archiveSize = zipContents.capacity(); if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) { System.out.println("File size smaller than EOCD min size"); return -1; } int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT32_MAX_VALUE); int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE); int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE; for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength; expectedCommentLength++) { Loading @@ -82,24 +177,28 @@ abstract class ZipUtils { } /** * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory * Returns {@code true} if the provided file contains a ZIP64 End of Central Directory * Locator. * * <p>NOTE: Byte order of {@code zipContents} must be little-endian. * @param zipEndOfCentralDirectoryPosition offset of the ZIP End of Central Directory record * in the file. * * @throws IOException if an I/O error occurs while reading the file. */ public static final boolean isZip64EndOfCentralDirectoryLocatorPresent( ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) { assertByteOrderLittleEndian(zipContents); RandomAccessFile zip, long zipEndOfCentralDirectoryPosition) throws IOException { // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central // Directory Record. int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE; long locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE; if (locatorPosition < 0) { return false; } return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG; zip.seek(locatorPosition); // RandomAccessFile.readInt assumes big-endian byte order, but ZIP format uses // little-endian. return zip.readInt() == ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER; } /** Loading Loading
core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +376 −188 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/java/android/util/apk/ZipUtils.java +113 −14 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package android.util.apk; import android.util.Pair; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * Assorted ZIP format helpers. * * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances except that the byte * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte * order of these buffers is little-endian. */ abstract class ZipUtils { Loading @@ -35,9 +39,101 @@ abstract class ZipUtils { private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20; private static final int ZIP64_EOCD_LOCATOR_SIZE = 20; private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50; private static final int ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER = 0x504b0607; private static final int UINT32_MAX_VALUE = 0xffff; private static final int UINT16_MAX_VALUE = 0xffff; /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(RandomAccessFile zip) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. long fileSize = zip.length(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { return null; } // Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus // the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily // reading more data. Pair<ByteBuffer, Long> result = findZipEndOfCentralDirectoryRecord(zip, 0); if (result != null) { return result; } // EoCD does not start where we expected it to. Perhaps it contains a non-empty comment // field. Expand the search. The maximum size of the comment field in EoCD is 65535 because // the comment length field is an unsigned 16-bit number. return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE); } /** * Returns the ZIP End of Central Directory record of the provided ZIP file. * * @param maxCommentSize maximum accepted size (in bytes) of EoCD comment field. The permitted * value is from 0 to 65535 inclusive. The smaller the value, the faster this method * locates the record, provided its comment field is no longer than this value. * * @return contents of the ZIP End of Central Directory record and the record's offset in the * file or {@code null} if the file does not contain the record. * * @throws IOException if an I/O error occurs while reading the file. */ private static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord( RandomAccessFile zip, int maxCommentSize) throws IOException { // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. // The record can be identified by its 4-byte signature/magic which is located at the very // beginning of the record. A complication is that the record is variable-length because of // the comment field. // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) { throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize); } long fileSize = zip.length(); if (fileSize < ZIP_EOCD_REC_MIN_SIZE) { // No space for EoCD record in the file. return null; } // Lower maxCommentSize if the file is too small. maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE); ByteBuffer buf = ByteBuffer.allocate(ZIP_EOCD_REC_MIN_SIZE + maxCommentSize); buf.order(ByteOrder.LITTLE_ENDIAN); long bufOffsetInFile = fileSize - buf.capacity(); zip.seek(bufOffsetInFile); zip.readFully(buf.array(), buf.arrayOffset(), buf.capacity()); int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf); if (eocdOffsetInBuf == -1) { // No EoCD record found in the buffer return null; } // EoCD found buf.position(eocdOffsetInBuf); ByteBuffer eocd = buf.slice(); eocd.order(ByteOrder.LITTLE_ENDIAN); return Pair.create(eocd, bufOffsetInFile + eocdOffsetInBuf); } /** * Returns the position at which ZIP End of Central Directory record starts in the provided Loading @@ -45,7 +141,7 @@ abstract class ZipUtils { * * <p>NOTE: Byte order of {@code zipContents} must be little-endian. */ public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { assertByteOrderLittleEndian(zipContents); // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. Loading @@ -56,14 +152,13 @@ abstract class ZipUtils { // end of the buffer for the EOCD record signature. Whenever we find a signature, we check // the candidate record's comment length is such that the remainder of the record takes up // exactly the remaining bytes in the buffer. The search is bounded because the maximum // size of the comment field is 65535 bytes because the field is an unsigned 32-bit number. // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. int archiveSize = zipContents.capacity(); if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) { System.out.println("File size smaller than EOCD min size"); return -1; } int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT32_MAX_VALUE); int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE); int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE; for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength; expectedCommentLength++) { Loading @@ -82,24 +177,28 @@ abstract class ZipUtils { } /** * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory * Returns {@code true} if the provided file contains a ZIP64 End of Central Directory * Locator. * * <p>NOTE: Byte order of {@code zipContents} must be little-endian. * @param zipEndOfCentralDirectoryPosition offset of the ZIP End of Central Directory record * in the file. * * @throws IOException if an I/O error occurs while reading the file. */ public static final boolean isZip64EndOfCentralDirectoryLocatorPresent( ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) { assertByteOrderLittleEndian(zipContents); RandomAccessFile zip, long zipEndOfCentralDirectoryPosition) throws IOException { // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central // Directory Record. int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE; long locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE; if (locatorPosition < 0) { return false; } return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG; zip.seek(locatorPosition); // RandomAccessFile.readInt assumes big-endian byte order, but ZIP format uses // little-endian. return zip.readInt() == ZIP64_EOCD_LOCATOR_SIG_REVERSE_BYTE_ORDER; } /** Loading