Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58d7ef66 authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by android-build-merger
Browse files

Merge "Use all certs for computing package signing sha256" into oc-mr1-dev

am: ee580764

Change-Id: Ie766ecd41d2ac07dd3608fa4ed69edc3c575058f
parents 383551da ee580764
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -188,7 +188,16 @@ public class PackageInfo implements Parcelable {

    /**
     * Array of all signatures read from the package file. This is only filled
     * in if the flag {@link PackageManager#GET_SIGNATURES} was set.
     * in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
     * must be singed with at least one certificate which is at position zero.
     * The package can be signed with additional certificates which appear as
     * subsequent entries.
     *
     * <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 to be the same across updates.
     */
    public Signature[] signatures;
    
+74 −12
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ import com.android.internal.util.XmlUtils;

import libcore.io.IoUtils;

import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

@@ -2824,14 +2825,14 @@ public class PackageParser {
                com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
        final int version = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
        String certSha256 = sa.getNonResourceString(com.android.internal.R.styleable
        String certSha256Digest = sa.getNonResourceString(com.android.internal.R.styleable
                .AndroidManifestUsesStaticLibrary_certDigest);
        sa.recycle();

        // Since an APK providing a static shared lib can only provide the lib - fail if malformed
        if (lname == null || version < 0 || certSha256 == null) {
        if (lname == null || version < 0 || certSha256Digest == null) {
            outError[0] = "Bad uses-static-library declaration name: " + lname + " version: "
                    + version + " certDigest" + certSha256;
                    + version + " certDigest" + certSha256Digest;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            XmlUtils.skipCurrentTag(parser);
            return false;
@@ -2848,16 +2849,73 @@ public class PackageParser {
        lname = lname.intern();
        // We allow ":" delimiters in the SHA declaration as this is the format
        // emitted by the certtool making it easy for developers to copy/paste.
        certSha256 = certSha256.replace(":", "").toLowerCase();
        certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();

        // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
        String[] additionalCertSha256Digests = EmptyArray.STRING;
        if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.O) {
            additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
            if (additionalCertSha256Digests == null) {
                return false;
            }
        } else {
            XmlUtils.skipCurrentTag(parser);
        }

        final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
        certSha256Digests[0] = certSha256Digest;
        System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
                1, additionalCertSha256Digests.length);

        pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
        pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt(
                pkg.usesStaticLibrariesVersions, version, true);
        pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String.class,
                pkg.usesStaticLibrariesCertDigests, certSha256, true);
        pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
                pkg.usesStaticLibrariesCertDigests, certSha256Digests, true);

        return true;
    }

    private String[] parseAdditionalCertificates(Resources resources, XmlResourceParser parser,
            String[] outError) throws XmlPullParserException, IOException {
        String[] certSha256Digests = EmptyArray.STRING;

        int outerDepth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            final String nodeName = parser.getName();
            if (nodeName.equals("additional-certificate")) {
                final TypedArray sa = resources.obtainAttributes(parser, com.android.internal.
                        R.styleable.AndroidManifestAdditionalCertificate);
                String certSha256Digest = sa.getNonResourceString(com.android.internal.
                        R.styleable.AndroidManifestAdditionalCertificate_certDigest);
                sa.recycle();

                if (TextUtils.isEmpty(certSha256Digest)) {
                    outError[0] = "Bad additional-certificate declaration with empty"
                            + " certDigest:" + certSha256Digest;
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    XmlUtils.skipCurrentTag(parser);
                    sa.recycle();
                    return null;
                }

        return true;
                // We allow ":" delimiters in the SHA declaration as this is the format
                // emitted by the certtool making it easy for developers to copy/paste.
                certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
                certSha256Digests = ArrayUtils.appendElement(String.class,
                        certSha256Digests, certSha256Digest);
            } else {
                XmlUtils.skipCurrentTag(parser);
            }
        }

        return certSha256Digests;
    }

    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
@@ -5820,7 +5878,7 @@ public class PackageParser {
        public ArrayList<String> usesLibraries = null;
        public ArrayList<String> usesStaticLibraries = null;
        public int[] usesStaticLibrariesVersions = null;
        public String[] usesStaticLibrariesCertDigests = null;
        public String[][] usesStaticLibrariesCertDigests = null;
        public ArrayList<String> usesOptionalLibraries = null;
        public String[] usesLibraryFiles = null;

@@ -6318,8 +6376,10 @@ public class PackageParser {
                internStringArrayList(usesStaticLibraries);
                usesStaticLibrariesVersions = new int[libCount];
                dest.readIntArray(usesStaticLibrariesVersions);
                usesStaticLibrariesCertDigests = new String[libCount];
                dest.readStringArray(usesStaticLibrariesCertDigests);
                usesStaticLibrariesCertDigests = new String[libCount][];
                for (int i = 0; i < libCount; i++) {
                    usesStaticLibrariesCertDigests[i] = dest.createStringArray();
                }
            }

            preferredActivityFilters = new ArrayList<>();
@@ -6465,7 +6525,9 @@ public class PackageParser {
                dest.writeInt(usesStaticLibraries.size());
                dest.writeStringList(usesStaticLibraries);
                dest.writeIntArray(usesStaticLibrariesVersions);
                dest.writeStringArray(usesStaticLibrariesCertDigests);
                for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) {
                    dest.writeStringArray(usesStaticLibrariesCertDigest);
                }
            }

            dest.writeParcelableList(preferredActivityFilters, flags);
+58 −22
Original line number Diff line number Diff line
@@ -18,12 +18,13 @@ package android.util;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * Helper functions applicable to packages.
@@ -36,32 +37,67 @@ public final class PackageUtils {
    }

    /**
     * Computes the SHA256 digest of the signing cert for a package.
     * @param packageManager The package manager.
     * @param packageName The package for which to generate the digest.
     * @param userId The user for which to generate the digest.
     * @return The digest or null if the package does not exist for this user.
     * Computes the SHA256 digests of a list of signatures. Items in the
     * resulting array of hashes correspond to the signatures in the
     * input array.
     * @param signatures The signatures.
     * @return The digest array.
     */
    public static @Nullable String computePackageCertSha256Digest(
            @NonNull PackageManager packageManager,
            @NonNull String packageName, int userId) {
        final PackageInfo packageInfo;
        try {
            packageInfo = packageManager.getPackageInfoAsUser(packageName,
                    PackageManager.GET_SIGNATURES, userId);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
    public static @NonNull String[] computeSignaturesSha256Digests(
            @NonNull Signature[] signatures) {
        final int signatureCount = signatures.length;
        final String[] digests = new String[signatureCount];
        for (int i = 0; i < signatureCount; i++) {
            digests[i] = computeSha256Digest(signatures[i].toByteArray());
        }
        return computeCertSha256Digest(packageInfo.signatures[0]);
        return digests;
    }
    /**
     * Computes a SHA256 digest of the signatures' SHA256 digests. First,
     * individual hashes for each signature is derived in a hexademical
     * form, then these strings are sorted based the natural ordering, and
     * finally a hash is derived from these strings' bytes.
     * @param signatures The signatures.
     * @return The digest.
     */
    public static @NonNull String computeSignaturesSha256Digest(
            @NonNull Signature[] signatures) {
        // Shortcut for optimization - most apps singed by a single cert
        if (signatures.length == 1) {
            return computeSha256Digest(signatures[0].toByteArray());
        }

        // Make sure these are sorted to handle reversed certificates
        final String[] sha256Digests = computeSignaturesSha256Digests(signatures);
        return computeSignaturesSha256Digest(sha256Digests);
    }

    /**
     * Computes the SHA256 digest of a cert.
     * @param signature The signature.
     * @return The digest or null if an error occurs.
     * Computes a SHA256 digest in of the signatures SHA256 digests. First,
     * the strings are sorted based the natural ordering, and then a hash is
     * derived from these strings' bytes.
     * @param sha256Digests Signature SHA256 hashes in hexademical form.
     * @return The digest.
     */
    public static @Nullable String computeCertSha256Digest(@NonNull Signature signature) {
        return computeSha256Digest(signature.toByteArray());
    public static @NonNull String computeSignaturesSha256Digest(
            @NonNull String[] sha256Digests) {
        // Shortcut for optimization - most apps singed by a single cert
        if (sha256Digests.length == 1) {
            return sha256Digests[0];
        }

        // Make sure these are sorted to handle reversed certificates
        Arrays.sort(sha256Digests);

        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        for (String sha256Digest : sha256Digests) {
            try {
                bytes.write(sha256Digest.getBytes());
            } catch (IOException e) {
                /* ignore - can't happen */
            }
        }
        return computeSha256Digest(bytes.toByteArray());
    }

    /**
+16 −0
Original line number Diff line number Diff line
@@ -1779,6 +1779,11 @@
         the library at build time while it offers apps to share code defined in such
         libraries. Hence, static libraries are strictly required.

         <p>On devices running O MR1 or higher, if the library is singed with multiple
         signing certificates you must to specify the SHA-256 hashes of the additional
         certificates via adding
         {@link #AndroidManifestAdditionalCertificate additional-certificate} tags.

         <p>This appears as a child tag of the
         {@link #AndroidManifestApplication application} tag. -->
    <declare-styleable name="AndroidManifestUsesStaticLibrary" parent="AndroidManifestApplication">
@@ -1790,6 +1795,17 @@
        <attr name="certDigest" format="string" />
    </declare-styleable>

    <!-- The <code>additional-certificate</code> specifies the SHA-256 digest of a static
         shared library's additional signing certificate. You need to use this tag if the
         library is singed with more than one certificate.

         <p>This appears as a child tag of the
         {@link #AndroidManifestUsesStaticLibrary uses-static-library} tag. -->
    <declare-styleable name="AndroidManifestAdditionalCertificate" parent="AndroidManifestUsesStaticLibrary">
        <!-- The SHA-256 digest of the library signing certificate. -->
        <attr name="certDigest" />
    </declare-styleable>

    <!-- The <code>supports-screens</code> specifies the screen dimensions an
         application supports.  By default a modern application supports all
         screen sizes and must explicitly disable certain screen sizes here;
+25 −5
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
@@ -30,6 +31,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
@@ -126,9 +128,18 @@ public final class AccountManagerBackupHelper {
            } catch (PackageManager.NameNotFoundException e) {
                return false;
            }
            String currentCertDigest = PackageUtils.computeCertSha256Digest(
                    packageInfo.signatures[0]);
            if (!certDigest.equals(currentCertDigest)) {

            // Before we used only the first signature to compute the SHA 256 but some
            // apps could be singed by multiple certs and the cert order is undefined.
            // We prefer the modern computation procedure where all certs are taken
            // into account but also allow the value from the old computation to allow
            // restoring backed up grants on an older platform version.
            final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
                    packageInfo.signatures);
            final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest(
                    signaturesSha256Digests);
            if (!certDigest.equals(signaturesSha256Digest) && (packageInfo.signatures.length <= 1
                    || !certDigest.equals(signaturesSha256Digests[0]))) {
                return false;
            }
            final int uid = packageInfo.applicationInfo.uid;
@@ -169,8 +180,17 @@ public final class AccountManagerBackupHelper {
                        }

                        for (String packageName : packageNames) {
                            String digest = PackageUtils.computePackageCertSha256Digest(
                                    packageManager, packageName, userId);
                            final PackageInfo packageInfo;
                            try {
                                packageInfo = packageManager.getPackageInfoAsUser(packageName,
                                        PackageManager.GET_SIGNATURES, userId);
                            } catch (PackageManager.NameNotFoundException e) {
                                Slog.i(TAG, "Skipping backup of account access grant for"
                                        + " non-existing package: " + packageName);
                                continue;
                            }
                            final String digest = PackageUtils.computeSignaturesSha256Digest(
                                    packageInfo.signatures);
                            if (digest != null) {
                                serializer.startTag(null, TAG_PERMISSION);
                                serializer.attribute(null, ATTR_ACCOUNT_SHA_256,
Loading