diff --git a/core/tests/coretests/res/raw/install_app1_cert5 b/core/tests/coretests/res/raw/install_app1_cert5 new file mode 100644 index 0000000000000..138b6113ea6b9 Binary files /dev/null and b/core/tests/coretests/res/raw/install_app1_cert5 differ diff --git a/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6 b/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6 new file mode 100644 index 0000000000000..2da2436d9b16a Binary files /dev/null and b/core/tests/coretests/res/raw/install_app1_cert5_rotated_cert6 differ diff --git a/core/tests/coretests/res/raw/install_app1_cert6 b/core/tests/coretests/res/raw/install_app1_cert6 new file mode 100644 index 0000000000000..256e03a2de549 Binary files /dev/null and b/core/tests/coretests/res/raw/install_app1_cert6 differ diff --git a/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6 b/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6 new file mode 100644 index 0000000000000..30bb6478d18de Binary files /dev/null and b/core/tests/coretests/res/raw/install_app2_cert5_rotated_cert6 differ diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index ed2436a269d90..79cb1f9f85f9d 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -1830,27 +1830,35 @@ public class PackageManagerTests extends AndroidTestCase { * The following series of tests are related to upgrading apps with * different certificates. */ - private int APP1_UNSIGNED = R.raw.install_app1_unsigned; + private static final int APP1_UNSIGNED = R.raw.install_app1_unsigned; - private int APP1_CERT1 = R.raw.install_app1_cert1; + private static final int APP1_CERT1 = R.raw.install_app1_cert1; - private int APP1_CERT2 = R.raw.install_app1_cert2; + private static final int APP1_CERT2 = R.raw.install_app1_cert2; - private int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2; + private static final int APP1_CERT1_CERT2 = R.raw.install_app1_cert1_cert2; - private int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4; + private static final int APP1_CERT3_CERT4 = R.raw.install_app1_cert3_cert4; - private int APP1_CERT3 = R.raw.install_app1_cert3; + private static final int APP1_CERT3 = R.raw.install_app1_cert3; - private int APP2_UNSIGNED = R.raw.install_app2_unsigned; + private static final int APP1_CERT5 = R.raw.install_app1_cert5; - private int APP2_CERT1 = R.raw.install_app2_cert1; + private static final int APP1_CERT5_ROTATED_CERT6 = R.raw.install_app1_cert5_rotated_cert6; - private int APP2_CERT2 = R.raw.install_app2_cert2; + private static final int APP1_CERT6 = R.raw.install_app1_cert6; - private int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2; + private static final int APP2_UNSIGNED = R.raw.install_app2_unsigned; - private int APP2_CERT3 = R.raw.install_app2_cert3; + private static final int APP2_CERT1 = R.raw.install_app2_cert1; + + private static final int APP2_CERT2 = R.raw.install_app2_cert2; + + private static final int APP2_CERT1_CERT2 = R.raw.install_app2_cert1_cert2; + + private static final int APP2_CERT3 = R.raw.install_app2_cert3; + + private static final int APP2_CERT5_ROTATED_CERT6 = R.raw.install_app2_cert5_rotated_cert6; private InstallParams replaceCerts(int apk1, int apk2, boolean cleanUp, boolean fail, int retCode) throws Exception { @@ -2472,6 +2480,26 @@ public class PackageManagerTests extends AndroidTestCase { } } + @LargeTest + public void testCheckSignaturesRotatedAgainstOriginal() throws Exception { + // checkSignatures should be backwards compatible with pre-rotation behavior; this test + // verifies that an app signed with a rotated key results in a signature match with an app + // signed with the original key in the lineage. + int apk1 = APP1_CERT5; + int apk2 = APP2_CERT5_ROTATED_CERT6; + checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH); + } + + @LargeTest + public void testCheckSignaturesRotatedAgainstRotated() throws Exception { + // checkSignatures should be successful when both apps have been signed with the same + // rotated key since the initial signature comparison between the two apps should + // return a match. + int apk1 = APP1_CERT5_ROTATED_CERT6; + int apk2 = APP2_CERT5_ROTATED_CERT6; + checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH); + } + @LargeTest public void testInstallNoCertificates() throws Exception { int apk1 = APP1_UNSIGNED; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d2481b758e828..e4b4bfd7e1881 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5941,8 +5941,25 @@ public class PackageManagerService extends IPackageManager.Stub || shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - return compareSignatures(p1.getSigningDetails().signatures, - p2.getSigningDetails().signatures); + SigningDetails p1SigningDetails = p1.getSigningDetails(); + SigningDetails p2SigningDetails = p2.getSigningDetails(); + int result = compareSignatures(p1SigningDetails.signatures, + p2SigningDetails.signatures); + // To support backwards compatibility with clients of this API expecting pre-key + // rotation results if either of the packages has a signing lineage the oldest signer + // in the lineage is used for signature verification. + if (result != PackageManager.SIGNATURE_MATCH && ( + p1SigningDetails.hasPastSigningCertificates() + || p2SigningDetails.hasPastSigningCertificates())) { + Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates() + ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]} + : p1SigningDetails.signatures; + Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates() + ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]} + : p2SigningDetails.signatures; + result = compareSignatures(p1Signatures, p2Signatures); + } + return result; } }