Allow restoring of apps that rotated key
Restoring of apps that rotated key wouldn't be possible due to explicit signature matching. Amend signature matching strategies to take into account apps that have rotated key. Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java Test: m -j RunFrameworksServicesRoboTests Test: runtest -p com.android.server.backup frameworks-services Bug: 64686581 Bug: 34345052 Change-Id: I91b5ae0afb6f2714ceae02b4d4dc202d6cd4fe4e
This commit is contained in:
@@ -539,4 +539,16 @@ public abstract class PackageManagerInternal {
|
||||
/** Updates the flags for the given permission. */
|
||||
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
|
||||
@NonNull String packageName, int flagMask, int flagValues, int userId);
|
||||
|
||||
/**
|
||||
* Returns true if it's still safe to restore data backed up from this app's version
|
||||
* that was signed with restoringFromSigHash.
|
||||
*/
|
||||
public abstract boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName);
|
||||
|
||||
/**
|
||||
* Returns true if it's still safe to restore data backed up from this app's version
|
||||
* that was signed with restoringFromSig.
|
||||
*/
|
||||
public abstract boolean isDataRestoreSafe(Signature restoringFromSig, String packageName);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
@@ -30,6 +31,7 @@ import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.utils.AppBackupUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -235,7 +237,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
|
||||
if (home != null) {
|
||||
try {
|
||||
homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
|
||||
PackageManager.GET_SIGNATURES);
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
|
||||
homeVersion = homeInfo.getLongVersionCode();
|
||||
homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
|
||||
@@ -252,10 +254,11 @@ public class PackageManagerBackupAgent extends BackupAgent {
|
||||
// 2. the home app [or absence] we now use differs from the prior state,
|
||||
// OR 3. it looks like we use the same home app + version as before, but
|
||||
// the signatures don't match so we treat them as different apps.
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
|
||||
|| !Objects.equals(home, mStoredHomeComponent)
|
||||
|| (home != null
|
||||
&& !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo));
|
||||
&& !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo, pmi));
|
||||
if (needHomeBackup) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Home preference changed; backing up new state " + home);
|
||||
|
||||
@@ -36,11 +36,13 @@ import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupRestoreTask;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
@@ -207,8 +209,11 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks, info, signatures);
|
||||
mBackupManagerService.getPackageManager(), allowApks, info, signatures,
|
||||
pmi);
|
||||
mManifestSignatures.put(info.packageName, signatures);
|
||||
mPackagePolicies.put(pkg, restorePolicy);
|
||||
mPackageInstallers.put(pkg, info.installerPackageName);
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -47,6 +48,7 @@ import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
@@ -470,9 +472,11 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks,
|
||||
info, signatures);
|
||||
info, signatures, pmi);
|
||||
mManifestSignatures.put(info.packageName, signatures);
|
||||
mPackagePolicies.put(pkg, restorePolicy);
|
||||
mPackageInstallers.put(pkg, info.installerPackageName);
|
||||
|
||||
@@ -42,6 +42,7 @@ import android.app.backup.RestoreDescription;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
@@ -56,6 +57,7 @@ import android.util.Slog;
|
||||
import com.android.internal.backup.IBackupTransport;
|
||||
import com.android.server.AppWidgetBackupBridge;
|
||||
import com.android.server.EventLogTags;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupRestoreTask;
|
||||
import com.android.server.backup.BackupUtils;
|
||||
import com.android.server.backup.PackageManagerBackupAgent;
|
||||
@@ -497,7 +499,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
|
||||
try {
|
||||
mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
|
||||
pkgName, PackageManager.GET_SIGNATURES);
|
||||
pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
} catch (NameNotFoundException e) {
|
||||
// Whoops, we thought we could restore this package but it
|
||||
// turns out not to be present. Skip it.
|
||||
@@ -612,7 +614,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
}
|
||||
|
||||
Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
|
||||
if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage, pmi)) {
|
||||
Slog.w(TAG, "Signature mismatch restoring " + packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
|
||||
BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH, mCurrentPackage,
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.backup.BackupTransport;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Process;
|
||||
import android.util.Slog;
|
||||
@@ -37,6 +38,9 @@ import com.android.server.backup.transport.TransportClient;
|
||||
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
|
||||
*/
|
||||
public class AppBackupUtils {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Returns whether app is eligible for backup.
|
||||
*
|
||||
@@ -88,7 +92,8 @@ public class AppBackupUtils {
|
||||
public static boolean appIsRunningAndEligibleForBackupWithTransport(
|
||||
@Nullable TransportClient transportClient, String packageName, PackageManager pm) {
|
||||
try {
|
||||
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
|
||||
PackageInfo packageInfo = pm.getPackageInfo(packageName,
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
|
||||
if (!appIsEligibleForBackup(applicationInfo, pm)
|
||||
|| appIsStopped(applicationInfo)
|
||||
@@ -165,12 +170,18 @@ public class AppBackupUtils {
|
||||
*
|
||||
* <ul>
|
||||
* <li>Source and target have at least one signature each
|
||||
* <li>Target contains all signatures in source
|
||||
* <li>Target contains all signatures in source, and nothing more
|
||||
* </ul>
|
||||
*
|
||||
* or if both source and target have exactly one signature, and they don't match, we check
|
||||
* if the app was ever signed with source signature (i.e. app has rotated key)
|
||||
* Note: key rotation is only supported for apps ever signed with one key, and those apps will
|
||||
* not be allowed to be signed by more certificates in the future
|
||||
*
|
||||
* Note that if {@param target} is null we return false.
|
||||
*/
|
||||
public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
|
||||
public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target,
|
||||
PackageManagerInternal pmi) {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -187,33 +198,52 @@ public class AppBackupUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
Signature[] deviceSigs = target.signatures;
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs);
|
||||
}
|
||||
|
||||
// Don't allow unsigned apps on either end
|
||||
if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) {
|
||||
if (ArrayUtils.isEmpty(storedSigs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Signatures can be added over time, so the target-device apk needs to contain all the
|
||||
// source-device apk signatures, but not necessarily the other way around.
|
||||
int nStored = storedSigs.length;
|
||||
int nDevice = deviceSigs.length;
|
||||
Signature[][] deviceHistorySigs = target.signingCertificateHistory;
|
||||
if (ArrayUtils.isEmpty(deviceHistorySigs)) {
|
||||
Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
|
||||
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (storedSigs[i].equals(deviceSigs[j])) {
|
||||
match = true;
|
||||
break;
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceHistorySigs);
|
||||
}
|
||||
|
||||
final int nStored = storedSigs.length;
|
||||
if (nStored == 1) {
|
||||
// if the app is only signed with one sig, it's possible it has rotated its key
|
||||
// (the checks with signing history are delegated to PackageManager)
|
||||
// TODO: address the case that app has declared restoreAnyVersion and is restoring
|
||||
// from higher version to lower after having rotated the key (i.e. higher version has
|
||||
// different sig than lower version that we want to restore to)
|
||||
return pmi.isDataRestoreSafe(storedSigs[0], target.packageName);
|
||||
} else {
|
||||
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
|
||||
// a comprehensive 1-to-1 signatures check
|
||||
// since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
|
||||
Signature[] deviceSigs = deviceHistorySigs[0];
|
||||
int nDevice = deviceSigs.length;
|
||||
|
||||
// ensure that each stored sig matches an on-device sig
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (storedSigs[i].equals(deviceSigs[j])) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
// we have found a match for all stored sigs
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.Session;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -37,6 +38,7 @@ import android.os.Process;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.restore.RestoreDeleteObserver;
|
||||
import com.android.server.backup.restore.RestorePolicy;
|
||||
@@ -142,9 +144,8 @@ public class RestoreUtils {
|
||||
uninstall = true;
|
||||
} else {
|
||||
try {
|
||||
PackageInfo pkg = packageManager.getPackageInfo(
|
||||
info.packageName,
|
||||
PackageManager.GET_SIGNATURES);
|
||||
PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
|
||||
== 0) {
|
||||
Slog.w(TAG, "Restore stream contains apk of package "
|
||||
@@ -154,7 +155,9 @@ public class RestoreUtils {
|
||||
} else {
|
||||
// So far so good -- do the signatures match the manifest?
|
||||
Signature[] sigs = manifestSignatures.get(info.packageName);
|
||||
if (AppBackupUtils.signaturesMatch(sigs, pkg)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
if (AppBackupUtils.signaturesMatch(sigs, pkg, pmi)) {
|
||||
// If this is a system-uid app without a declared backup agent,
|
||||
// don't restore any of the file data.
|
||||
if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.app.backup.IBackupManagerMonitor;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
@@ -385,7 +386,8 @@ public class TarBackupReader {
|
||||
* @return a restore policy constant.
|
||||
*/
|
||||
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
|
||||
boolean allowApks, FileMetadata info, Signature[] signatures) {
|
||||
boolean allowApks, FileMetadata info, Signature[] signatures,
|
||||
PackageManagerInternal pmi) {
|
||||
if (signatures == null) {
|
||||
return RestorePolicy.IGNORE;
|
||||
}
|
||||
@@ -395,7 +397,7 @@ public class TarBackupReader {
|
||||
// Okay, got the manifest info we need...
|
||||
try {
|
||||
PackageInfo pkgInfo = packageManager.getPackageInfo(
|
||||
info.packageName, PackageManager.GET_SIGNATURES);
|
||||
info.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
// Fall through to IGNORE if the app explicitly disallows backup
|
||||
final int flags = pkgInfo.applicationInfo.flags;
|
||||
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
|
||||
@@ -411,7 +413,7 @@ public class TarBackupReader {
|
||||
// such packages are signed with the platform cert instead of
|
||||
// the app developer's cert, so they're different on every
|
||||
// device.
|
||||
if (AppBackupUtils.signaturesMatch(signatures, pkgInfo)) {
|
||||
if (AppBackupUtils.signaturesMatch(signatures, pkgInfo, pmi)) {
|
||||
if ((pkgInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
|
||||
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
|
||||
|
||||
@@ -18,9 +18,12 @@ package com.android.server.backup;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,9 +33,10 @@ import java.util.List;
|
||||
public class BackupUtils {
|
||||
private static final String TAG = "BackupUtils";
|
||||
|
||||
private static final boolean DEBUG = false; // STOPSHIP if true
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
|
||||
public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target,
|
||||
PackageManagerInternal pmi) {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -47,48 +51,54 @@ public class BackupUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow unsigned apps, but not signed on one device and unsigned on the other
|
||||
// !!! TODO: is this the right policy?
|
||||
Signature[] deviceSigs = target.signatures;
|
||||
if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
|
||||
+ " device=" + deviceSigs);
|
||||
if ((storedSigHashes == null || storedSigHashes.size() == 0)
|
||||
&& (deviceSigs == null || deviceSigs.length == 0)) {
|
||||
return true;
|
||||
}
|
||||
if (storedSigHashes == null || deviceSigs == null) {
|
||||
// Don't allow unsigned apps on either end
|
||||
if (ArrayUtils.isEmpty(storedSigHashes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// !!! TODO: this demands that every stored signature match one
|
||||
// that is present on device, and does not demand the converse.
|
||||
// Is this this right policy?
|
||||
final int nStored = storedSigHashes.size();
|
||||
final int nDevice = deviceSigs.length;
|
||||
|
||||
// hash each on-device signature
|
||||
ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
|
||||
for (int i = 0; i < nDevice; i++) {
|
||||
deviceHashes.add(hashSignature(deviceSigs[i]));
|
||||
Signature[][] deviceHistorySigs = target.signingCertificateHistory;
|
||||
if (ArrayUtils.isEmpty(deviceHistorySigs)) {
|
||||
Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
|
||||
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
// now ensure that each stored sig (hash) matches an on-device sig (hash)
|
||||
for (int n = 0; n < nStored; n++) {
|
||||
boolean match = false;
|
||||
final byte[] storedHash = storedSigHashes.get(n);
|
||||
for (int i = 0; i < nDevice; i++) {
|
||||
if (Arrays.equals(storedHash, deviceHashes.get(i))) {
|
||||
match = true;
|
||||
break;
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
|
||||
+ " device=" + deviceHistorySigs);
|
||||
}
|
||||
|
||||
final int nStored = storedSigHashes.size();
|
||||
if (nStored == 1) {
|
||||
// if the app is only signed with one sig, it's possible it has rotated its key
|
||||
// the checks with signing history are delegated to PackageManager
|
||||
// TODO: address the case that app has declared restoreAnyVersion and is restoring
|
||||
// from higher version to lower after having rotated the key (i.e. higher version has
|
||||
// different sig than lower version that we want to restore to)
|
||||
return pmi.isDataRestoreSafe(storedSigHashes.get(0), target.packageName);
|
||||
} else {
|
||||
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
|
||||
// a comprehensive 1-to-1 signatures check
|
||||
// since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
|
||||
ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
|
||||
int nDevice = deviceHashes.size();
|
||||
|
||||
// ensure that each stored sig matches an on-device sig
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (Arrays.equals(storedSigHashes.get(i), deviceHashes.get(j))) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// match is false when no on-device sig matched one of the stored ones
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
// we have found a match for all stored sigs
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] hashSignature(byte[] signature) {
|
||||
|
||||
@@ -176,6 +176,7 @@ import android.content.pm.PackageParser.PackageLite;
|
||||
import android.content.pm.PackageParser.PackageParserException;
|
||||
import android.content.pm.PackageParser.ParseFlags;
|
||||
import android.content.pm.PackageParser.ServiceIntentInfo;
|
||||
import android.content.pm.PackageParser.SigningDetails;
|
||||
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
|
||||
import android.content.pm.PackageStats;
|
||||
import android.content.pm.PackageUserState;
|
||||
@@ -23319,6 +23320,39 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
permName, packageName, flagMask, flagValues, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
|
||||
SigningDetails sd = getSigningDetails(packageName);
|
||||
if (sd == null) {
|
||||
return false;
|
||||
}
|
||||
return sd.hasSha256Certificate(restoringFromSigHash,
|
||||
SigningDetails.CertCapabilities.INSTALLED_DATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) {
|
||||
SigningDetails sd = getSigningDetails(packageName);
|
||||
if (sd == null) {
|
||||
return false;
|
||||
}
|
||||
return sd.hasCertificate(restoringFromSig,
|
||||
SigningDetails.CertCapabilities.INSTALLED_DATA);
|
||||
}
|
||||
|
||||
private SigningDetails getSigningDetails(String packageName) {
|
||||
synchronized (mPackages) {
|
||||
if (packageName == null) {
|
||||
return null;
|
||||
}
|
||||
PackageParser.Package p = mPackages.get(packageName);
|
||||
if (p == null) {
|
||||
return null;
|
||||
}
|
||||
return p.mSigningDetails;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
|
||||
return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
|
||||
|
||||
@@ -18,10 +18,12 @@ package com.android.server.pm;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupUtils;
|
||||
|
||||
import libcore.util.HexEncoding;
|
||||
@@ -136,7 +138,8 @@ class ShortcutPackageInfo {
|
||||
|
||||
//@DisabledReason
|
||||
public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) {
|
||||
if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage, pmi)) {
|
||||
Slog.w(TAG, "Can't restore: Package signature mismatch");
|
||||
return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
|
||||
}
|
||||
|
||||
@@ -3125,7 +3125,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
try {
|
||||
return mIPackageManager.getPackageInfo(
|
||||
packageName, PACKAGE_MATCH_FLAGS
|
||||
| (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
|
||||
| (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0),
|
||||
userId);
|
||||
} catch (RemoteException e) {
|
||||
// Shouldn't happen.
|
||||
Slog.wtf(TAG, "RemoteException", e);
|
||||
|
||||
@@ -18,9 +18,14 @@ package com.android.server.backup.utils;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Process;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -30,6 +35,7 @@ import android.support.test.runner.AndroidJUnit4;
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
import com.android.server.backup.testutils.PackageManagerStub;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -45,7 +51,14 @@ public class AppBackupUtilsTest {
|
||||
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
|
||||
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
|
||||
|
||||
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
|
||||
private PackageManagerStub mPackageManagerStub;
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mPackageManagerStub = new PackageManagerStub();
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appIsEligibleForBackup_backupNotAllowed_returnsFalse() throws Exception {
|
||||
@@ -358,7 +371,8 @@ public class AppBackupUtilsTest {
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -369,7 +383,8 @@ public class AppBackupUtilsTest {
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -378,10 +393,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[] {SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -390,10 +406,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[] {SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -404,11 +421,11 @@ public class AppBackupUtilsTest {
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[0];
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
|
||||
packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -418,11 +435,11 @@ public class AppBackupUtilsTest {
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = null;
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
|
||||
packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -431,10 +448,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = null;
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -443,10 +461,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[0];
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -458,11 +477,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature3Copy = new Signature(SIGNATURE_3.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature3Copy, signature1Copy, signature2Copy}, packageInfo);
|
||||
new Signature[] {signature3Copy, signature1Copy, signature2Copy}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -473,11 +495,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature2Copy, signature1Copy}, packageInfo);
|
||||
new Signature[]{signature2Copy, signature1Copy}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -488,11 +513,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{signature1Copy, signature2Copy};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{signature1Copy, signature2Copy}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo);
|
||||
new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -503,11 +531,76 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo);
|
||||
new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know signature1Copy is in history, and we want to assume it has
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know signature1Copy is in history, but we want to assume it does not have
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BA
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@@ -37,6 +40,7 @@ import android.app.backup.IBackupManagerMonitor;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
@@ -79,6 +83,7 @@ public class TarBackupReaderTest {
|
||||
|
||||
@Mock private BytesReadListener mBytesReadListenerMock;
|
||||
@Mock private IBackupManagerMonitor mBackupManagerMonitorMock;
|
||||
@Mock private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
|
||||
private Context mContext;
|
||||
@@ -139,7 +144,8 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
fileMetadata);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
|
||||
@@ -152,7 +158,8 @@ public class TarBackupReaderTest {
|
||||
signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
fileMetadata);
|
||||
restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
|
||||
@@ -214,7 +221,8 @@ public class TarBackupReaderTest {
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, new FileMetadata(), null /* signatures */);
|
||||
true /* allowApks */, new FileMetadata(), null /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
verifyZeroInteractions(mBackupManagerMonitorMock);
|
||||
@@ -234,7 +242,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */);
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -258,7 +267,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */);
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -283,7 +293,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -307,7 +318,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -333,7 +345,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -358,11 +371,11 @@ public class TarBackupReaderTest {
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_2};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_2}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -383,16 +396,19 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |=
|
||||
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = "backup.agent";
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -412,16 +428,19 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |=
|
||||
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -444,17 +463,20 @@ public class TarBackupReaderTest {
|
||||
info.version = 1;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 2;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, info, signatures);
|
||||
false /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -479,17 +501,20 @@ public class TarBackupReaderTest {
|
||||
info.hasApk = true;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 1;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, signatures);
|
||||
true /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
verifyNoMoreInteractions(mBackupManagerMonitorMock);
|
||||
@@ -510,17 +535,20 @@ public class TarBackupReaderTest {
|
||||
info.version = 2;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 1;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, info, signatures);
|
||||
false /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
|
||||
@@ -15,73 +15,298 @@
|
||||
*/
|
||||
package com.android.server.pm.backup;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser.Package;
|
||||
import android.content.pm.Signature;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.backup.BackupUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
public class BackupUtilsTest extends AndroidTestCase {
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BackupUtilsTest {
|
||||
|
||||
private Signature[] genSignatures(String... signatures) {
|
||||
final Signature[] sigs = new Signature[signatures.length];
|
||||
for (int i = 0; i < signatures.length; i++){
|
||||
sigs[i] = new Signature(signatures[i].getBytes());
|
||||
}
|
||||
return sigs;
|
||||
private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
|
||||
private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
|
||||
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
|
||||
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
|
||||
private static final byte[] SIGNATURE_HASH_1 = BackupUtils.hashSignature(SIGNATURE_1);
|
||||
private static final byte[] SIGNATURE_HASH_2 = BackupUtils.hashSignature(SIGNATURE_2);
|
||||
private static final byte[] SIGNATURE_HASH_3 = BackupUtils.hashSignature(SIGNATURE_3);
|
||||
private static final byte[] SIGNATURE_HASH_4 = BackupUtils.hashSignature(SIGNATURE_4);
|
||||
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
private PackageInfo genPackage(String... signatures) {
|
||||
final PackageInfo pi = new PackageInfo();
|
||||
pi.packageName = "package";
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.signatures = genSignatures(signatures);
|
||||
@Test
|
||||
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, null,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
return pi;
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
public void testSignaturesMatch() {
|
||||
final ArrayList<byte[]> stored1 = BackupUtils.hashSignatureArray(Arrays.asList(
|
||||
"abc".getBytes()));
|
||||
final ArrayList<byte[]> stored2 = BackupUtils.hashSignatureArray(Arrays.asList(
|
||||
"abc".getBytes(), "def".getBytes()));
|
||||
@Test
|
||||
public void signaturesMatch_systemApplication_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
|
||||
PackageInfo pi;
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
// False for null package.
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, null));
|
||||
|
||||
// If it's a system app, signatures don't matter.
|
||||
pi = genPackage("xyz");
|
||||
pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, pi));
|
||||
|
||||
// Non system apps.
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc")));
|
||||
|
||||
// Superset is okay.
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc", "xyz")));
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "abc")));
|
||||
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz")));
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "def")));
|
||||
|
||||
assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("def", "abc")));
|
||||
assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("x", "def", "abc", "y")));
|
||||
|
||||
// Subset is not okay.
|
||||
assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("abc")));
|
||||
assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("def")));
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_3);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1, SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_3);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_4);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know SIGNATURE_1 is in history, and we want to assume it has
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know SIGNATURE_1 is in history, but we want to assume it does not have
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashSignature() {
|
||||
final byte[] sig1 = "abc".getBytes();
|
||||
final byte[] sig2 = "def".getBytes();
|
||||
@@ -115,4 +340,10 @@ public class BackupUtilsTest extends AndroidTestCase {
|
||||
MoreAsserts.assertEquals(hash2a, listA.get(1));
|
||||
MoreAsserts.assertEquals(hash2a, listB.get(1));
|
||||
}
|
||||
|
||||
private static Signature generateSignature(byte i) {
|
||||
byte[] signatureBytes = new byte[256];
|
||||
signatureBytes[0] = i;
|
||||
return new Signature(signatureBytes);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user