Loading core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +11 −11 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WIT import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; Loading @@ -35,7 +38,6 @@ import android.util.ArrayMap; import android.util.Pair; import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; Loading Loading @@ -139,7 +141,7 @@ public class ApkSignatureSchemeV2Verifier { private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk.getFD(), signatureInfo, verifyIntegrity); return verify(apk, signatureInfo, verifyIntegrity); } /** Loading @@ -162,9 +164,9 @@ public class ApkSignatureSchemeV2Verifier { * against the APK file. */ private static X509Certificate[][] verify( FileDescriptor apkFileDescriptor, RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException { boolean doVerifyIntegrity) throws SecurityException, IOException { int signerCount = 0; Map<Integer, byte[]> contentDigests = new ArrayMap<>(); List<X509Certificate[]> signerCerts = new ArrayList<>(); Loading Loading @@ -202,13 +204,7 @@ public class ApkSignatureSchemeV2Verifier { } if (doVerifyIntegrity) { ApkSigningBlockUtils.verifyIntegrity( contentDigests, apkFileDescriptor, signatureInfo.apkSigningBlockOffset, signatureInfo.centralDirOffset, signatureInfo.eocdOffset, signatureInfo.eocd); ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } return signerCerts.toArray(new X509Certificate[signerCerts.size()][]); Loading Loading @@ -386,6 +382,7 @@ public class ApkSignatureSchemeV2Verifier { } return; } private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: Loading @@ -395,6 +392,9 @@ public class ApkSignatureSchemeV2Verifier { case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return true; default: return false; Loading core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +9 −10 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WIT import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; Loading @@ -36,7 +39,6 @@ import android.util.ArrayMap; import android.util.Pair; import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; Loading Loading @@ -136,7 +138,7 @@ public class ApkSignatureSchemeV3Verifier { private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk.getFD(), signatureInfo, verifyIntegrity); return verify(apk, signatureInfo, verifyIntegrity); } /** Loading @@ -159,7 +161,7 @@ public class ApkSignatureSchemeV3Verifier { * against the APK file. */ private static VerifiedSigner verify( FileDescriptor apkFileDescriptor, RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException { int signerCount = 0; Loading Loading @@ -206,13 +208,7 @@ public class ApkSignatureSchemeV3Verifier { } if (doVerifyIntegrity) { ApkSigningBlockUtils.verifyIntegrity( contentDigests, apkFileDescriptor, signatureInfo.apkSigningBlockOffset, signatureInfo.centralDirOffset, signatureInfo.eocdOffset, signatureInfo.eocd); ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } return result; Loading Loading @@ -512,6 +508,9 @@ public class ApkSignatureSchemeV3Verifier { case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return true; default: return false; Loading core/java/android/util/apk/ApkSigningBlockUtils.java +91 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.util.apk; import android.util.ArrayMap; import android.util.Pair; import java.io.FileDescriptor; Loading @@ -30,6 +31,7 @@ import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; import java.util.Arrays; import java.util.Map; /** Loading Loading @@ -84,16 +86,41 @@ final class ApkSigningBlockUtils { static void verifyIntegrity( Map<Integer, byte[]> expectedDigests, FileDescriptor apkFileDescriptor, long apkSigningBlockOffset, long centralDirOffset, long eocdOffset, ByteBuffer eocdBuf) throws SecurityException { RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException { if (expectedDigests.isEmpty()) { throw new SecurityException("No digests provided"); } Map<Integer, byte[]> expected1MbChunkDigests = new ArrayMap<>(); if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) { expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA256, expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA256)); } if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) { expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA512, expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA512)); } if (expectedDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { verifyIntegrityForVerityBasedAlgorithm( expectedDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256), apk, signatureInfo); } else if (!expected1MbChunkDigests.isEmpty()) { try { verifyIntegrityFor1MbChunkBasedAlgorithm(expected1MbChunkDigests, apk.getFD(), signatureInfo); } catch (IOException e) { throw new SecurityException("Cannot get FD", e); } } else { throw new SecurityException("No known digest exists for integrity check"); } } private static void verifyIntegrityFor1MbChunkBasedAlgorithm( Map<Integer, byte[]> expectedDigests, FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws SecurityException { // We need to verify the integrity of the following three sections of the file: // 1. Everything up to the start of the APK Signing Block. // 2. ZIP Central Directory. Loading @@ -105,16 +132,18 @@ final class ApkSigningBlockUtils { // APK are already there in the OS's page cache and thus mmap does not use additional // physical memory. DataSource beforeApkSigningBlock = new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset); new MemoryMappedFileDataSource(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = new MemoryMappedFileDataSource( apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset); apkFileDescriptor, signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); // For the purposes of integrity verification, ZIP End of Central Directory's field Start of // Central Directory must be considered to point to the offset of the APK Signing Block. eocdBuf = eocdBuf.duplicate(); ByteBuffer eocdBuf = signatureInfo.eocd.duplicate(); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset); DataSource eocd = new ByteBufferDataSource(eocdBuf); int[] digestAlgorithms = new int[expectedDigests.size()]; Loading @@ -126,7 +155,7 @@ final class ApkSigningBlockUtils { byte[][] actualDigests; try { actualDigests = computeContentDigests( computeContentDigestsPer1MbChunk( digestAlgorithms, new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); } catch (DigestException e) { Loading @@ -144,7 +173,7 @@ final class ApkSigningBlockUtils { } } private static byte[][] computeContentDigests( private static byte[][] computeContentDigestsPer1MbChunk( int[] digestAlgorithms, DataSource[] contents) throws DigestException { // For each digest algorithm the result is computed as follows: Loading Loading @@ -256,6 +285,26 @@ final class ApkSigningBlockUtils { return result; } private static void verifyIntegrityForVerityBasedAlgorithm( byte[] expectedRootHash, RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException { try { ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk, signatureInfo, new ByteBufferFactory() { @Override public ByteBuffer create(int capacity) { return ByteBuffer.allocate(capacity); } }); if (!Arrays.equals(expectedRootHash, verity.rootHash)) { throw new SecurityException("APK verity digest of contents did not verify"); } } catch (DigestException | IOException | NoSuchAlgorithmException e) { throw new SecurityException("Error during verification", e); } } /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * Loading Loading @@ -304,9 +353,13 @@ final class ApkSigningBlockUtils { static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0401; static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0403; static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0405; static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); Loading @@ -321,6 +374,7 @@ final class ApkSigningBlockUtils { case CONTENT_DIGEST_CHUNKED_SHA256: return 0; case CONTENT_DIGEST_CHUNKED_SHA512: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return -1; default: throw new IllegalArgumentException( Loading @@ -329,6 +383,7 @@ final class ApkSigningBlockUtils { case CONTENT_DIGEST_CHUNKED_SHA512: switch (digestAlgorithm2) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 1; case CONTENT_DIGEST_CHUNKED_SHA512: return 0; Loading @@ -336,6 +391,18 @@ final class ApkSigningBlockUtils { throw new IllegalArgumentException( "Unknown digestAlgorithm2: " + digestAlgorithm2); } case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: switch (digestAlgorithm2) { case CONTENT_DIGEST_CHUNKED_SHA512: return -1; case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 0; case CONTENT_DIGEST_CHUNKED_SHA256: return 1; default: throw new IllegalArgumentException( "Unknown digestAlgorithm2: " + digestAlgorithm2); } default: throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1); } Loading @@ -352,6 +419,10 @@ final class ApkSigningBlockUtils { case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: case SIGNATURE_ECDSA_WITH_SHA512: return CONTENT_DIGEST_CHUNKED_SHA512; case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return CONTENT_DIGEST_VERITY_CHUNKED_SHA256; default: throw new IllegalArgumentException( "Unknown signature algorithm: 0x" Loading @@ -362,6 +433,7 @@ final class ApkSigningBlockUtils { static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) { switch (digestAlgorithm) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return "SHA-256"; case CONTENT_DIGEST_CHUNKED_SHA512: return "SHA-512"; Loading @@ -374,6 +446,7 @@ final class ApkSigningBlockUtils { private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) { switch (digestAlgorithm) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 256 / 8; case CONTENT_DIGEST_CHUNKED_SHA512: return 512 / 8; Loading @@ -389,11 +462,14 @@ final class ApkSigningBlockUtils { case SIGNATURE_RSA_PSS_WITH_SHA512: case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: return "RSA"; case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: return "EC"; case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return "DSA"; default: throw new IllegalArgumentException( Loading @@ -416,14 +492,17 @@ final class ApkSigningBlockUtils { new PSSParameterSpec( "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)); case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: return Pair.create("SHA256withRSA", null); case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: return Pair.create("SHA512withRSA", null); case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: return Pair.create("SHA256withECDSA", null); case SIGNATURE_ECDSA_WITH_SHA512: return Pair.create("SHA512withECDSA", null); case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return Pair.create("SHA256withDSA", null); default: throw new IllegalArgumentException( Loading Loading
core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +11 −11 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WIT import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; Loading @@ -35,7 +38,6 @@ import android.util.ArrayMap; import android.util.Pair; import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; Loading Loading @@ -139,7 +141,7 @@ public class ApkSignatureSchemeV2Verifier { private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk.getFD(), signatureInfo, verifyIntegrity); return verify(apk, signatureInfo, verifyIntegrity); } /** Loading @@ -162,9 +164,9 @@ public class ApkSignatureSchemeV2Verifier { * against the APK file. */ private static X509Certificate[][] verify( FileDescriptor apkFileDescriptor, RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException { boolean doVerifyIntegrity) throws SecurityException, IOException { int signerCount = 0; Map<Integer, byte[]> contentDigests = new ArrayMap<>(); List<X509Certificate[]> signerCerts = new ArrayList<>(); Loading Loading @@ -202,13 +204,7 @@ public class ApkSignatureSchemeV2Verifier { } if (doVerifyIntegrity) { ApkSigningBlockUtils.verifyIntegrity( contentDigests, apkFileDescriptor, signatureInfo.apkSigningBlockOffset, signatureInfo.centralDirOffset, signatureInfo.eocdOffset, signatureInfo.eocd); ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } return signerCerts.toArray(new X509Certificate[signerCerts.size()][]); Loading Loading @@ -386,6 +382,7 @@ public class ApkSignatureSchemeV2Verifier { } return; } private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: Loading @@ -395,6 +392,9 @@ public class ApkSignatureSchemeV2Verifier { case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return true; default: return false; Loading
core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +9 −10 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WIT import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; Loading @@ -36,7 +39,6 @@ import android.util.ArrayMap; import android.util.Pair; import java.io.ByteArrayInputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; Loading Loading @@ -136,7 +138,7 @@ public class ApkSignatureSchemeV3Verifier { private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk.getFD(), signatureInfo, verifyIntegrity); return verify(apk, signatureInfo, verifyIntegrity); } /** Loading @@ -159,7 +161,7 @@ public class ApkSignatureSchemeV3Verifier { * against the APK file. */ private static VerifiedSigner verify( FileDescriptor apkFileDescriptor, RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException { int signerCount = 0; Loading Loading @@ -206,13 +208,7 @@ public class ApkSignatureSchemeV3Verifier { } if (doVerifyIntegrity) { ApkSigningBlockUtils.verifyIntegrity( contentDigests, apkFileDescriptor, signatureInfo.apkSigningBlockOffset, signatureInfo.centralDirOffset, signatureInfo.eocdOffset, signatureInfo.eocd); ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } return result; Loading Loading @@ -512,6 +508,9 @@ public class ApkSignatureSchemeV3Verifier { case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return true; default: return false; Loading
core/java/android/util/apk/ApkSigningBlockUtils.java +91 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.util.apk; import android.util.ArrayMap; import android.util.Pair; import java.io.FileDescriptor; Loading @@ -30,6 +31,7 @@ import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; import java.util.Arrays; import java.util.Map; /** Loading Loading @@ -84,16 +86,41 @@ final class ApkSigningBlockUtils { static void verifyIntegrity( Map<Integer, byte[]> expectedDigests, FileDescriptor apkFileDescriptor, long apkSigningBlockOffset, long centralDirOffset, long eocdOffset, ByteBuffer eocdBuf) throws SecurityException { RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException { if (expectedDigests.isEmpty()) { throw new SecurityException("No digests provided"); } Map<Integer, byte[]> expected1MbChunkDigests = new ArrayMap<>(); if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) { expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA256, expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA256)); } if (expectedDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) { expected1MbChunkDigests.put(CONTENT_DIGEST_CHUNKED_SHA512, expectedDigests.get(CONTENT_DIGEST_CHUNKED_SHA512)); } if (expectedDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { verifyIntegrityForVerityBasedAlgorithm( expectedDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256), apk, signatureInfo); } else if (!expected1MbChunkDigests.isEmpty()) { try { verifyIntegrityFor1MbChunkBasedAlgorithm(expected1MbChunkDigests, apk.getFD(), signatureInfo); } catch (IOException e) { throw new SecurityException("Cannot get FD", e); } } else { throw new SecurityException("No known digest exists for integrity check"); } } private static void verifyIntegrityFor1MbChunkBasedAlgorithm( Map<Integer, byte[]> expectedDigests, FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws SecurityException { // We need to verify the integrity of the following three sections of the file: // 1. Everything up to the start of the APK Signing Block. // 2. ZIP Central Directory. Loading @@ -105,16 +132,18 @@ final class ApkSigningBlockUtils { // APK are already there in the OS's page cache and thus mmap does not use additional // physical memory. DataSource beforeApkSigningBlock = new MemoryMappedFileDataSource(apkFileDescriptor, 0, apkSigningBlockOffset); new MemoryMappedFileDataSource(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = new MemoryMappedFileDataSource( apkFileDescriptor, centralDirOffset, eocdOffset - centralDirOffset); apkFileDescriptor, signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); // For the purposes of integrity verification, ZIP End of Central Directory's field Start of // Central Directory must be considered to point to the offset of the APK Signing Block. eocdBuf = eocdBuf.duplicate(); ByteBuffer eocdBuf = signatureInfo.eocd.duplicate(); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, apkSigningBlockOffset); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset); DataSource eocd = new ByteBufferDataSource(eocdBuf); int[] digestAlgorithms = new int[expectedDigests.size()]; Loading @@ -126,7 +155,7 @@ final class ApkSigningBlockUtils { byte[][] actualDigests; try { actualDigests = computeContentDigests( computeContentDigestsPer1MbChunk( digestAlgorithms, new DataSource[] {beforeApkSigningBlock, centralDir, eocd}); } catch (DigestException e) { Loading @@ -144,7 +173,7 @@ final class ApkSigningBlockUtils { } } private static byte[][] computeContentDigests( private static byte[][] computeContentDigestsPer1MbChunk( int[] digestAlgorithms, DataSource[] contents) throws DigestException { // For each digest algorithm the result is computed as follows: Loading Loading @@ -256,6 +285,26 @@ final class ApkSigningBlockUtils { return result; } private static void verifyIntegrityForVerityBasedAlgorithm( byte[] expectedRootHash, RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException { try { ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk, signatureInfo, new ByteBufferFactory() { @Override public ByteBuffer create(int capacity) { return ByteBuffer.allocate(capacity); } }); if (!Arrays.equals(expectedRootHash, verity.rootHash)) { throw new SecurityException("APK verity digest of contents did not verify"); } } catch (DigestException | IOException | NoSuchAlgorithmException e) { throw new SecurityException("Error during verification", e); } } /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * Loading Loading @@ -304,9 +353,13 @@ final class ApkSigningBlockUtils { static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201; static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202; static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301; static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0401; static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0403; static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0405; static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) { int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1); Loading @@ -321,6 +374,7 @@ final class ApkSigningBlockUtils { case CONTENT_DIGEST_CHUNKED_SHA256: return 0; case CONTENT_DIGEST_CHUNKED_SHA512: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return -1; default: throw new IllegalArgumentException( Loading @@ -329,6 +383,7 @@ final class ApkSigningBlockUtils { case CONTENT_DIGEST_CHUNKED_SHA512: switch (digestAlgorithm2) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 1; case CONTENT_DIGEST_CHUNKED_SHA512: return 0; Loading @@ -336,6 +391,18 @@ final class ApkSigningBlockUtils { throw new IllegalArgumentException( "Unknown digestAlgorithm2: " + digestAlgorithm2); } case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: switch (digestAlgorithm2) { case CONTENT_DIGEST_CHUNKED_SHA512: return -1; case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 0; case CONTENT_DIGEST_CHUNKED_SHA256: return 1; default: throw new IllegalArgumentException( "Unknown digestAlgorithm2: " + digestAlgorithm2); } default: throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1); } Loading @@ -352,6 +419,10 @@ final class ApkSigningBlockUtils { case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: case SIGNATURE_ECDSA_WITH_SHA512: return CONTENT_DIGEST_CHUNKED_SHA512; case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return CONTENT_DIGEST_VERITY_CHUNKED_SHA256; default: throw new IllegalArgumentException( "Unknown signature algorithm: 0x" Loading @@ -362,6 +433,7 @@ final class ApkSigningBlockUtils { static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) { switch (digestAlgorithm) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return "SHA-256"; case CONTENT_DIGEST_CHUNKED_SHA512: return "SHA-512"; Loading @@ -374,6 +446,7 @@ final class ApkSigningBlockUtils { private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) { switch (digestAlgorithm) { case CONTENT_DIGEST_CHUNKED_SHA256: case CONTENT_DIGEST_VERITY_CHUNKED_SHA256: return 256 / 8; case CONTENT_DIGEST_CHUNKED_SHA512: return 512 / 8; Loading @@ -389,11 +462,14 @@ final class ApkSigningBlockUtils { case SIGNATURE_RSA_PSS_WITH_SHA512: case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: return "RSA"; case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_ECDSA_WITH_SHA512: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: return "EC"; case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return "DSA"; default: throw new IllegalArgumentException( Loading @@ -416,14 +492,17 @@ final class ApkSigningBlockUtils { new PSSParameterSpec( "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1)); case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: return Pair.create("SHA256withRSA", null); case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: return Pair.create("SHA512withRSA", null); case SIGNATURE_ECDSA_WITH_SHA256: case SIGNATURE_VERITY_ECDSA_WITH_SHA256: return Pair.create("SHA256withECDSA", null); case SIGNATURE_ECDSA_WITH_SHA512: return Pair.create("SHA512withECDSA", null); case SIGNATURE_DSA_WITH_SHA256: case SIGNATURE_VERITY_DSA_WITH_SHA256: return Pair.create("SHA256withDSA", null); default: throw new IllegalArgumentException( Loading