Merge "Use all certs for computing package signing sha256" into oc-mr1-dev
am: ee580764ad
Change-Id: Ie766ecd41d2ac07dd3608fa4ed69edc3c575058f
This commit is contained in:
@@ -187,8 +187,17 @@ public class PackageInfo implements Parcelable {
|
||||
public static final int REQUESTED_PERMISSION_GRANTED = 1<<1;
|
||||
|
||||
/**
|
||||
* Array of all signatures read from the package file. This is only filled
|
||||
* in if the flag {@link PackageManager#GET_SIGNATURES} was set.
|
||||
* Array of all signatures read from the package file. This is only filled
|
||||
* 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;
|
||||
|
||||
|
||||
@@ -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,18 +2849,75 @@ 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);
|
||||
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
throws XmlPullParserException, IOException {
|
||||
TypedArray sa = res.obtainAttributes(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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -293,14 +293,35 @@ class InstantAppRegistry {
|
||||
if (currentCookieFile == null) {
|
||||
continue;
|
||||
}
|
||||
File expectedCookeFile = computeInstantCookieFile(pkg, userId);
|
||||
if (!currentCookieFile.equals(expectedCookeFile)) {
|
||||
Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
|
||||
+ " changed - dropping cookie");
|
||||
// Make sure a pending write for the old signed app is cancelled
|
||||
mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
|
||||
currentCookieFile.delete();
|
||||
|
||||
// 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 avoid
|
||||
// data loss.
|
||||
final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
|
||||
pkg.mSignatures);
|
||||
final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest(
|
||||
signaturesSha256Digests);
|
||||
|
||||
// We prefer a match based on all signatures
|
||||
if (currentCookieFile.equals(computeInstantCookieFile(pkg.packageName,
|
||||
signaturesSha256Digest, userId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For backwards compatibility we accept match based on first signature
|
||||
if (pkg.mSignatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
|
||||
pkg.packageName, signaturesSha256Digests[0], userId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sorry, you are out of luck - different signatures - nuke data
|
||||
Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
|
||||
+ " changed - dropping cookie");
|
||||
// Make sure a pending write for the old signed app is cancelled
|
||||
mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
|
||||
currentCookieFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,11 +989,11 @@ class InstantAppRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
private static @NonNull File computeInstantCookieFile(@NonNull PackageParser.Package pkg,
|
||||
@UserIdInt int userId) {
|
||||
File appDir = getInstantApplicationDir(pkg.packageName, userId);
|
||||
String cookieFile = INSTANT_APP_COOKIE_FILE_PREFIX + PackageUtils.computeSha256Digest(
|
||||
pkg.mSignatures[0].toByteArray()) + INSTANT_APP_COOKIE_FILE_SIFFIX;
|
||||
private static @NonNull File computeInstantCookieFile(@NonNull String packageName,
|
||||
@NonNull String sha256Digest, @UserIdInt int userId) {
|
||||
final File appDir = getInstantApplicationDir(packageName, userId);
|
||||
final String cookieFile = INSTANT_APP_COOKIE_FILE_PREFIX
|
||||
+ sha256Digest + INSTANT_APP_COOKIE_FILE_SIFFIX;
|
||||
return new File(appDir, cookieFile);
|
||||
}
|
||||
|
||||
@@ -1147,9 +1168,20 @@ class InstantAppRegistry {
|
||||
|
||||
public void schedulePersistLPw(@UserIdInt int userId, @NonNull PackageParser.Package pkg,
|
||||
@NonNull byte[] cookie) {
|
||||
File cookieFile = computeInstantCookieFile(pkg, userId);
|
||||
// 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 and delete the file derived via the legacy hash computation.
|
||||
File newCookieFile = computeInstantCookieFile(pkg.packageName,
|
||||
PackageUtils.computeSignaturesSha256Digest(pkg.mSignatures), userId);
|
||||
if (pkg.mSignatures.length > 0) {
|
||||
File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
|
||||
if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
|
||||
oldCookieFile.delete();
|
||||
}
|
||||
}
|
||||
cancelPendingPersistLPw(pkg, userId);
|
||||
addPendingPersistCookieLPw(userId, pkg, cookie, cookieFile);
|
||||
addPendingPersistCookieLPw(userId, pkg, cookie, newCookieFile);
|
||||
sendMessageDelayed(obtainMessage(userId, pkg),
|
||||
PERSIST_COOKIE_DELAY_MILLIS);
|
||||
}
|
||||
|
||||
@@ -10385,16 +10385,19 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
ArraySet<String> usesLibraryFiles = null;
|
||||
if (pkg.usesLibraries != null) {
|
||||
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
|
||||
null, null, pkg.packageName, changingLib, true, null);
|
||||
null, null, pkg.packageName, changingLib, true,
|
||||
pkg.applicationInfo.targetSdkVersion, null);
|
||||
}
|
||||
if (pkg.usesStaticLibraries != null) {
|
||||
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesStaticLibraries,
|
||||
pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
|
||||
pkg.packageName, changingLib, true, usesLibraryFiles);
|
||||
pkg.packageName, changingLib, true,
|
||||
pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
|
||||
}
|
||||
if (pkg.usesOptionalLibraries != null) {
|
||||
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
|
||||
null, null, pkg.packageName, changingLib, false, usesLibraryFiles);
|
||||
null, null, pkg.packageName, changingLib, false,
|
||||
pkg.applicationInfo.targetSdkVersion, usesLibraryFiles);
|
||||
}
|
||||
if (!ArrayUtils.isEmpty(usesLibraryFiles)) {
|
||||
pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
|
||||
@@ -10404,9 +10407,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
|
||||
@Nullable int[] requiredVersions, @Nullable String[] requiredCertDigests,
|
||||
@Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
|
||||
@NonNull String packageName, @Nullable PackageParser.Package changingLib,
|
||||
boolean required, @Nullable ArraySet<String> outUsedLibraries)
|
||||
boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
|
||||
throws PackageManagerException {
|
||||
final int libCount = requestedLibraries.size();
|
||||
for (int i = 0; i < libCount; i++) {
|
||||
@@ -10440,13 +10443,34 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
+ " library; failing!");
|
||||
}
|
||||
|
||||
String expectedCertDigest = requiredCertDigests[i];
|
||||
String libCertDigest = PackageUtils.computeCertSha256Digest(
|
||||
libPkg.mSignatures[0]);
|
||||
if (!libCertDigest.equalsIgnoreCase(expectedCertDigest)) {
|
||||
final String[] expectedCertDigests = requiredCertDigests[i];
|
||||
// For apps targeting O MR1 we require explicit enumeration of all certs.
|
||||
final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
|
||||
? PackageUtils.computeSignaturesSha256Digests(libPkg.mSignatures)
|
||||
: PackageUtils.computeSignaturesSha256Digests(
|
||||
new Signature[]{libPkg.mSignatures[0]});
|
||||
|
||||
// Take a shortcut if sizes don't match. Note that if an app doesn't
|
||||
// target O we don't parse the "additional-certificate" tags similarly
|
||||
// how we only consider all certs only for apps targeting O (see above).
|
||||
// Therefore, the size check is safe to make.
|
||||
if (expectedCertDigests.length != libCertDigests.length) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
|
||||
"Package " + packageName + " requires differently signed" +
|
||||
" static shared library; failing!");
|
||||
" static sDexLoadReporter.java:45.19hared library; failing!");
|
||||
}
|
||||
|
||||
// Use a predictable order as signature order may vary
|
||||
Arrays.sort(libCertDigests);
|
||||
Arrays.sort(expectedCertDigests);
|
||||
|
||||
final int certCount = libCertDigests.length;
|
||||
for (int j = 0; j < certCount; j++) {
|
||||
if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
|
||||
"Package " + packageName + " requires differently signed" +
|
||||
" static shared library; failing!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -467,7 +467,8 @@ public class PackageParserTest {
|
||||
pkg.staticSharedLibVersion = 100;
|
||||
pkg.usesStaticLibraries = new ArrayList<>();
|
||||
pkg.usesStaticLibraries.add("foo23");
|
||||
pkg.usesStaticLibrariesCertDigests = new String[] { "digest" };
|
||||
pkg.usesStaticLibrariesCertDigests = new String[1][];
|
||||
pkg.usesStaticLibrariesCertDigests[0] = new String[] { "digest" };
|
||||
pkg.usesStaticLibrariesVersions = new int[] { 100 };
|
||||
|
||||
pkg.libraryNames = new ArrayList<>();
|
||||
|
||||
Reference in New Issue
Block a user