Merge "Add API to expose signing certificate proof-of-rotation."
diff --git a/api/current.txt b/api/current.txt
index 9667b901..ac6d80a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10873,7 +10873,8 @@
     field public android.content.pm.ServiceInfo[] services;
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
-    field public android.content.pm.Signature[] signatures;
+    field public deprecated android.content.pm.Signature[] signatures;
+    field public android.content.pm.Signature[][] signingCertificateHistory;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
     field public deprecated int versionCode;
@@ -11076,6 +11077,8 @@
     method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public boolean hasSigningCertificate(java.lang.String, byte[], int);
+    method public boolean hasSigningCertificate(int, byte[], int);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
@@ -11101,6 +11104,8 @@
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void updateInstantAppCookie(byte[]);
     method public abstract void verifyPendingInstall(int, int);
+    field public static final int CERT_INPUT_RAW_X509 = 0; // 0x0
+    field public static final int CERT_INPUT_SHA256 = 1; // 0x1
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
@@ -11219,7 +11224,8 @@
     field public static final int GET_RESOLVED_FILTER = 64; // 0x40
     field public static final int GET_SERVICES = 4; // 0x4
     field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
-    field public static final int GET_SIGNATURES = 64; // 0x40
+    field public static final deprecated int GET_SIGNATURES = 64; // 0x40
+    field public static final int GET_SIGNING_CERTIFICATES = 134217728; // 0x8000000
     field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 49d5224..cc68c05 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -699,6 +699,26 @@
     }
 
     @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        try {
+            return mPM.hasSigningCertificate(packageName, certificate, type);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        try {
+            return mPM.hasUidSigningCertificate(uid, certificate, type);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public String[] getPackagesForUid(int uid) {
         try {
             return mPM.getPackagesForUid(uid);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cce6b84..379bff4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -656,4 +656,8 @@
     void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
 
     CharSequence getHarmfulAppWarning(String packageName, int userId);
+
+    boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags);
+
+    boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
 }
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 5a91e94..13ec4fd 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -246,9 +246,44 @@
      * equivalent to being signed with certificates B and A. This means that
      * in case multiple signatures are reported you cannot assume the one at
      * the first position to be the same across updates.
+     *
+     * <strong>Deprecated</strong> This has been replaced by the
+     * {@link PackageInfo#signingCertificateHistory} field, which takes into
+     * account signing certificate rotation.  For backwards compatibility in
+     * the event of signing certificate rotation, this will return the oldest
+     * reported signing certificate, so that an application will appear to
+     * callers as though no rotation occurred.
+     *
+     * @deprecated use {@code signingCertificateHistory} instead
      */
+    @Deprecated
     public Signature[] signatures;
-    
+
+    /**
+     * Array of all signatures arrays read from the package file, potentially
+     * including past signing certificates no longer used after signing
+     * certificate rotation.  Though signing certificate rotation is only
+     * available for apps with a single signing certificate, this provides an
+     * array of arrays so that packages signed with multiple signing
+     * certificates can still return all signers.  This is only filled in if
+     * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
+     *
+     * A package must be singed with at least one certificate, which is at
+     * position zero in the array.  An application may be signed by multiple
+     * certificates, which would be in the array at position zero in an
+     * indeterminate order.  A package may also have a history of certificates
+     * due to signing certificate rotation.  In this case, the array will be
+     * populated by a series of single-entry arrays corresponding to a signing
+     * certificate of the package.
+     *
+     * <strong>Note:</strong> Signature ordering is not guaranteed to be
+     * stable which means that a package signed with certificates A and B is
+     * equivalent to being signed with certificates B and A. This means that
+     * in case multiple signatures are reported you cannot assume the one at
+     * the first position will be the same across updates.
+     */
+    public Signature[][] signingCertificateHistory;
+
     /**
      * Application specified preferred configuration
      * {@link android.R.styleable#AndroidManifestUsesConfiguration
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b81267a..67c9584 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -133,6 +133,7 @@
             GET_SERVICES,
             GET_SHARED_LIBRARY_FILES,
             GET_SIGNATURES,
+            GET_SIGNING_CERTIFICATES,
             GET_URI_PERMISSION_PATTERNS,
             MATCH_UNINSTALLED_PACKAGES,
             MATCH_DISABLED_COMPONENTS,
@@ -272,7 +273,10 @@
     /**
      * {@link PackageInfo} flag: return information about the
      * signatures included in the package.
+     *
+     * @deprecated use {@code GET_SIGNING_CERTIFICATES} instead
      */
+    @Deprecated
     public static final int GET_SIGNATURES          = 0x00000040;
 
     /**
@@ -488,6 +492,14 @@
     public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
 
     /**
+     * {@link PackageInfo} flag: return the signing certificates associated with
+     * this package.  Each entry is a signing certificate that the package
+     * has proven it is authorized to use, usually a past signing certificate from
+     * which it has rotated.
+     */
+    public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
+
+    /**
      * Internal flag used to indicate that a system component has done their
      * homework and verified that they correctly handle packages and components
      * that come and go over time. In particular:
@@ -3781,7 +3793,7 @@
     public abstract int getInstantAppCookieMaxBytes();
 
     /**
-     * @deprecated
+     * deprecated
      * @hide
      */
     public abstract int getInstantAppCookieMaxSize();
@@ -5914,4 +5926,60 @@
     public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
         throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
     }
+
+    /** @hide */
+    @IntDef(prefix = { "CERT_INPUT_" }, value = {
+            CERT_INPUT_RAW_X509,
+            CERT_INPUT_SHA256
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CertificateInputType {}
+
+    /**
+     * Certificate input bytes: the input bytes represent an encoded X.509 Certificate which could
+     * be generated using an {@code CertificateFactory}
+     */
+    public static final int CERT_INPUT_RAW_X509 = 0;
+
+    /**
+     * Certificate input bytes: the input bytes represent the SHA256 output of an encoded X.509
+     * Certificate.
+     */
+    public static final int CERT_INPUT_SHA256 = 1;
+
+    /**
+     * Searches the set of signing certificates by which the given package has proven to have been
+     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported.
+     *
+     * @param packageName package whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
+
+    /**
+     * Searches the set of signing certificates by which the given uid has proven to have been
+     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported.
+     *
+     * @param uid package whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4efd081..5b5ccf5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -801,13 +801,40 @@
                 }
             }
         }
+        // deprecated method of getting signing certificates
         if ((flags&PackageManager.GET_SIGNATURES) != 0) {
-            if (p.mSigningDetails.hasSignatures()) {
+            if (p.mSigningDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = p.mSigningDetails.pastSigningCertificates[0];
+            } else if (p.mSigningDetails.hasSignatures()) {
+                // otherwise keep old behavior
                 int numberOfSigs = p.mSigningDetails.signatures.length;
                 pi.signatures = new Signature[numberOfSigs];
                 System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
             }
         }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (p.mSigningDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Convert each
+                // entry to an array
+                int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
+                pi.signingCertificateHistory = new Signature[numberOfSigs][];
+                for (int i = 0; i < numberOfSigs; i++) {
+                    pi.signingCertificateHistory[i] =
+                            new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
+                }
+            } else if (p.mSigningDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = p.mSigningDetails.signatures.length;
+                pi.signingCertificateHistory = new Signature[1][numberOfSigs];
+                System.arraycopy(p.mSigningDetails.signatures, 0,
+                        pi.signingCertificateHistory[0], 0, numberOfSigs);
+            }
+        }
         return pi;
     }
 
@@ -5759,6 +5786,11 @@
             return signatures != null && signatures.length > 0;
         }
 
+        /** Returns true if the signing details have past signing certificates. */
+        public boolean hasPastSigningCertificates() {
+            return pastSigningCertificates != null && pastSigningCertificates.length > 0;
+        }
+
         /** Returns true if the signatures in this and other match exactly. */
         public boolean signaturesMatchExactly(SigningDetails other) {
             return Signature.areExactMatch(this.signatures, other.signatures);
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index e2e9d53..a5e3818 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -105,7 +105,7 @@
      * @param data The data.
      * @return The digest or null if an error occurs.
      */
-    public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+    public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) {
         MessageDigest messageDigest;
         try {
             messageDigest = MessageDigest.getInstance("SHA256");
@@ -116,6 +116,15 @@
 
         messageDigest.update(data);
 
-        return ByteStringUtils.toHexString(messageDigest.digest());
+        return messageDigest.digest();
+    }
+
+    /**
+     * Computes the SHA256 digest of some data.
+     * @param data The data.
+     * @return The digest or null if an error occurs.
+     */
+    public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+        return ByteStringUtils.toHexString(computeSha256DigestBytes(data));
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index da1bdc7..42b6946 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,8 @@
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -108,6 +110,8 @@
 import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
 import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasCertificate;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasSha256Certificate;
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
@@ -5457,6 +5461,73 @@
         }
     }
 
+    @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+
+        synchronized (mPackages) {
+            final PackageParser.Package p = mPackages.get(packageName);
+            if (p == null || p.mExtras == null) {
+                return false;
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            final PackageSetting ps = (PackageSetting) p.mExtras;
+            if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                return false;
+            }
+            switch (type) {
+                case CERT_INPUT_RAW_X509:
+                    return signingDetailsHasCertificate(certificate, p.mSigningDetails);
+                case CERT_INPUT_SHA256:
+                    return signingDetailsHasSha256Certificate(certificate, p.mSigningDetails);
+                default:
+                    return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean hasUidSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        // Map to base uids.
+        uid = UserHandle.getAppId(uid);
+        // reader
+        synchronized (mPackages) {
+            final PackageParser.SigningDetails signingDetails;
+            final Object obj = mSettings.getUserIdLPr(uid);
+            if (obj != null) {
+                if (obj instanceof SharedUserSetting) {
+                    final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+                    if (isCallerInstantApp) {
+                        return false;
+                    }
+                    signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
+                } else if (obj instanceof PackageSetting) {
+                    final PackageSetting ps = (PackageSetting) obj;
+                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                        return false;
+                    }
+                    signingDetails = ps.signatures.mSigningDetails;
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+            switch (type) {
+                case CERT_INPUT_RAW_X509:
+                    return signingDetailsHasCertificate(certificate, signingDetails);
+                case CERT_INPUT_SHA256:
+                    return signingDetailsHasSha256Certificate(certificate, signingDetails);
+                default:
+                    return false;
+            }
+        }
+    }
+
     /**
      * This method should typically only be used when granting or revoking
      * permissions, since the app may immediately restart after this call.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index cfc12de..021c4b8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -54,6 +54,7 @@
 import android.system.Os;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.PackageUtils;
 import android.util.Slog;
 import android.util.jar.StrictJarFile;
 import android.util.proto.ProtoOutputStream;
@@ -74,6 +75,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
@@ -576,6 +579,69 @@
         return true;
     }
 
+
+    /**
+     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
+     * {@code SigningDetails} with multiple signing certificates.
+     * @param certificate certificate to check for membership
+     * @param signingDetails signing certificates record whose members are to be searched
+     * @return true if {@code certificate} is in {@code signingDetails}
+     */
+    public static boolean signingDetailsHasCertificate(
+            byte[] certificate, PackageParser.SigningDetails signingDetails) {
+        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+            return false;
+        }
+        Signature signature = new Signature(certificate);
+        if (signingDetails.hasPastSigningCertificates()) {
+            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+                if (signingDetails.pastSigningCertificates[i].equals(signature)) {
+                    return true;
+                }
+            }
+        } else {
+            // no signing history, just check the current signer
+            if (signingDetails.signatures.length == 1
+                    && signingDetails.signatures[0].equals(signature)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
+     * {@code SigningDetails} with multiple signing certificaes.
+     * @param sha256Certificate certificate to check for membership
+     * @param signingDetails signing certificates record whose members are to be searched
+     * @return true if {@code certificate} is in {@code signingDetails}
+     */
+    public static boolean signingDetailsHasSha256Certificate(
+            byte[] sha256Certificate, PackageParser.SigningDetails signingDetails ) {
+        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+            return false;
+        }
+        if (signingDetails.hasPastSigningCertificates()) {
+            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+                byte[] digest = PackageUtils.computeSha256DigestBytes(
+                        signingDetails.pastSigningCertificates[i].toByteArray());
+                if (Arrays.equals(sha256Certificate, digest)) {
+                    return true;
+                }
+            }
+        } else {
+            // no signing history, just check the current signer
+            if (signingDetails.signatures.length == 1) {
+                byte[] digest = PackageUtils.computeSha256DigestBytes(
+                        signingDetails.signatures[0].toByteArray());
+                if (Arrays.equals(sha256Certificate, digest)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
         return disabledPs != null && disabledPs.isPrivileged() &&
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 41cde17..1ddc52c 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1196,4 +1196,17 @@
     public CharSequence getHarmfulAppWarning(String packageName) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        throw new UnsupportedOperationException();
+    }
+
 }