diff options
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 21 | ||||
| -rw-r--r-- | core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java | 94 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 5 |
3 files changed, 92 insertions, 28 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0588a9d6224e..bc28ff177a7e 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -121,6 +121,10 @@ public class PackageParser { private static final int MAX_PACKAGES_PER_APK = 5; + public static final int APK_SIGNING_UNKNOWN = 0; + public static final int APK_SIGNING_V1 = 1; + public static final int APK_SIGNING_V2 = 2; + // TODO: switch outError users to PackageParserException // TODO: refactor "codePath" to "apkPath" @@ -1058,12 +1062,24 @@ public class PackageParser { return pkg; } + public static int getApkSigningVersion(Package pkg) { + try { + if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) { + return APK_SIGNING_V2; + } + return APK_SIGNING_V1; + } catch (IOException e) { + } + return APK_SIGNING_UNKNOWN; + } + /** * Collect certificates from all the APKs described in the given package, * populating {@link Package#mSignatures}. Also asserts that all APK * contents are signed correctly and consistently. */ - public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException { + public static void collectCertificates(Package pkg, int parseFlags) + throws PackageParserException { collectCertificatesInternal(pkg, parseFlags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { @@ -1074,7 +1090,8 @@ public class PackageParser { } } - private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException { + private static void collectCertificatesInternal(Package pkg, int parseFlags) + throws PackageParserException { pkg.mCertificates = null; pkg.mSignatures = null; pkg.mSigningKeys = null; diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 728f72309439..60c727037379 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -75,6 +75,36 @@ public class ApkSignatureSchemeV2Verifier { public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2; /** + * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 + * signature. The signature will not be verified. + */ + public static boolean hasSignature(String apkFile) throws IOException { + try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { + long fileSize = apk.length(); + if (fileSize > Integer.MAX_VALUE) { + return false; + } + MappedByteBuffer apkContents = + apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize); + // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order. + apkContents.order(ByteOrder.LITTLE_ENDIAN); + + final int centralDirOffset = + (int) getCentralDirOffset(apkContents, getEocdOffset(apkContents)); + // Find the APK Signing Block. + int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset); + ByteBuffer apkSigningBlock = + sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset); + + // Find the APK Signature Scheme v2 Block inside the APK Signing Block. + findApkSignatureSchemeV2Block(apkSigningBlock); + return true; + } catch (SignatureNotFoundException e) { + } + return false; + } + + /** * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates * associated with each signer. * @@ -130,31 +160,8 @@ public class ApkSignatureSchemeV2Verifier { // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order. apkContents.order(ByteOrder.LITTLE_ENDIAN); - // Find the offset of ZIP End of Central Directory (EoCD) - int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents); - if (eocdOffset == -1) { - throw new SignatureNotFoundException( - "Not an APK file: ZIP End of Central Directory record not found"); - } - if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) { - throw new SignatureNotFoundException("ZIP64 APK not supported"); - } - ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity()); - - // Look up the offset of ZIP Central Directory. - long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); - if (centralDirOffsetLong >= eocdOffset) { - throw new SignatureNotFoundException( - "ZIP Central Directory offset out of range: " + centralDirOffsetLong - + ". ZIP End of Central Directory offset: " + eocdOffset); - } - long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); - if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) { - throw new SignatureNotFoundException( - "ZIP Central Directory is not immediately followed by End of Central" - + " Directory"); - } - int centralDirOffset = (int) centralDirOffsetLong; + final int eocdOffset = getEocdOffset(apkContents); + final int centralDirOffset = (int) getCentralDirOffset(apkContents, eocdOffset); // Find the APK Signing Block. int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset); @@ -499,6 +506,43 @@ public class ApkSignatureSchemeV2Verifier { return result; } + /** + * Finds the offset of ZIP End of Central Directory (EoCD). + * + * @throws SignatureNotFoundException If the EoCD could not be found + */ + private static int getEocdOffset(ByteBuffer apkContents) throws SignatureNotFoundException { + int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents); + if (eocdOffset == -1) { + throw new SignatureNotFoundException( + "Not an APK file: ZIP End of Central Directory record not found"); + } + return eocdOffset; + } + + private static long getCentralDirOffset(ByteBuffer apkContents, int eocdOffset) + throws SignatureNotFoundException { + if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) { + throw new SignatureNotFoundException("ZIP64 APK not supported"); + } + ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity()); + + // Look up the offset of ZIP Central Directory. + long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd); + if (centralDirOffsetLong >= eocdOffset) { + throw new SignatureNotFoundException( + "ZIP Central Directory offset out of range: " + centralDirOffsetLong + + ". ZIP End of Central Directory offset: " + eocdOffset); + } + long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd); + if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) { + throw new SignatureNotFoundException( + "ZIP Central Directory is not immediately followed by End of Central" + + " Directory"); + } + return centralDirOffsetLong; + } + private static final int getChunkCount(int inputSizeBytes) { return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 310ad5353e32..6844cdd410ea 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2604,7 +2604,6 @@ final class Settings { if (pkg.volumeUuid != null) { serializer.attribute(null, "volumeUuid", pkg.volumeUuid); } - if (pkg.parentPackageName != null) { serializer.attribute(null, "parentPackageName", pkg.parentPackageName); } @@ -4316,6 +4315,10 @@ final class Settings { } pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName); pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println(); + final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg); + if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) { + pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion); + } pw.print(prefix); pw.print(" applicationInfo="); pw.println(ps.pkg.applicationInfo.toString()); pw.print(prefix); pw.print(" flags="); printFlags(pw, ps.pkg.applicationInfo.flags, |