Loading services/backup/java/com/android/server/backup/BackupManagerService.java +1 −72 Original line number Diff line number Diff line Loading @@ -7617,77 +7617,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // ----- Restore handling ----- // new style: we only store the SHA-1 hashes of each sig, not the full block static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { if (target == null) { return false; } // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system // partition will be signed with the device's platform certificate, so on // different phones the same system app will have different signatures.) if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 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 (MORE_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) { 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])); } // 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; } } // match is false when no on-device sig matched one of the stored ones if (!match) { return false; } } return true; } static byte[] hashSignature(Signature sig) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(sig.toByteArray()); return digest.digest(); } catch (NoSuchAlgorithmException e) { Slog.w(TAG, "No SHA-256 algorithm found!"); } return null; } // Old style: directly match the stored vs on device signature blocks static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { if (target == null) { Loading Loading @@ -8200,7 +8129,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { Slog.w(TAG, "Signature mismatch restoring " + packageName); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Signature mismatch"); Loading services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +5 −22 Original line number Diff line number Diff line Loading @@ -205,7 +205,7 @@ public class PackageManagerBackupAgent extends BackupAgent { PackageManager.GET_SIGNATURES); homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); homeVersion = homeInfo.versionCode; homeSigHashes = hashSignatureArray(homeInfo.signatures); homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures); } catch (NameNotFoundException e) { Slog.w(TAG, "Can't access preferred home info"); // proceed as though there were no preferred home set Loading @@ -222,7 +222,7 @@ public class PackageManagerBackupAgent extends BackupAgent { final boolean needHomeBackup = (homeVersion != mStoredHomeVersion) || !Objects.equals(home, mStoredHomeComponent) || (home != null && !BackupManagerService.signaturesMatch(mStoredHomeSigHashes, homeInfo)); && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo)); if (needHomeBackup) { if (DEBUG) { Slog.i(TAG, "Home preference changed; backing up new state " + home); Loading Loading @@ -309,7 +309,7 @@ public class PackageManagerBackupAgent extends BackupAgent { outputBuffer.reset(); outputBufferStream.writeInt(info.versionCode); writeSignatureHashArray(outputBufferStream, hashSignatureArray(info.signatures)); BackupUtils.hashSignatureArray(info.signatures)); if (DEBUG) { Slog.v(TAG, "+ writing metadata for " + packName Loading Loading @@ -432,18 +432,6 @@ public class PackageManagerBackupAgent extends BackupAgent { mRestoredSignatures = sigMap; } private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length); for (Signature s : sigs) { hashes.add(BackupManagerService.hashSignature(s)); } return hashes; } private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes) throws IOException { // the number of entries in the array Loading Loading @@ -492,13 +480,8 @@ public class PackageManagerBackupAgent extends BackupAgent { } if (nonHashFound) { ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.size()); for (int i = 0; i < sigs.size(); i++) { Signature s = new Signature(sigs.get(i)); hashes.add(BackupManagerService.hashSignature(s)); } sigs = hashes; // Replace with the hashes. sigs = BackupUtils.hashSignatureArray(sigs); } return sigs; Loading services/core/java/com/android/server/backup/BackupUtils.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.backup; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.Signature; import android.util.Slog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class BackupUtils { private static final String TAG = "BackupUtils"; private static final boolean DEBUG = false; // STOPSHIP if true public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { if (target == null) { return false; } // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system // partition will be signed with the device's platform certificate, so on // different phones the same system app will have different signatures.) if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 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) { 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])); } // 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; } } // match is false when no on-device sig matched one of the stored ones if (!match) { return false; } } return true; } public static byte[] hashSignature(byte[] signature) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(signature); return digest.digest(); } catch (NoSuchAlgorithmException e) { Slog.w(TAG, "No SHA-256 algorithm found!"); } return null; } public static byte[] hashSignature(Signature signature) { return hashSignature(signature.toByteArray()); } public static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<>(sigs.length); for (Signature s : sigs) { hashes.add(hashSignature(s)); } return hashes; } public static ArrayList<byte[]> hashSignatureArray(List<byte[]> sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<>(sigs.size()); for (byte[] s : sigs) { hashes.add(hashSignature(s)); } return hashes; } } services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.backup; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; 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 com.android.server.backup.BackupUtils; import java.util.ArrayList; import java.util.Arrays; @SmallTest public class BackupUtilsTest extends AndroidTestCase { 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 PackageInfo genPackage(String... signatures) { final PackageInfo pi = new PackageInfo(); pi.packageName = "package"; pi.applicationInfo = new ApplicationInfo(); pi.signatures = genSignatures(signatures); return pi; } 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())); PackageInfo pi; // 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"))); } public void testHashSignature() { final byte[] sig1 = "abc".getBytes(); final byte[] sig2 = "def".getBytes(); final byte[] hash1a = BackupUtils.hashSignature(sig1); final byte[] hash1b = BackupUtils.hashSignature(new Signature(sig1)); final byte[] hash2a = BackupUtils.hashSignature(sig2); final byte[] hash2b = BackupUtils.hashSignature(new Signature(sig2)); assertEquals(32, hash1a.length); MoreAsserts.assertEquals(hash1a, hash1b); assertEquals(32, hash2a.length); MoreAsserts.assertEquals(hash2a, hash2b); assertFalse(Arrays.equals(hash1a, hash2a)); final ArrayList<byte[]> listA = BackupUtils.hashSignatureArray(Arrays.asList( "abc".getBytes(), "def".getBytes())); final ArrayList<byte[]> listB = BackupUtils.hashSignatureArray(new Signature[]{ new Signature("abc".getBytes()), new Signature("def".getBytes())}); assertEquals(2, listA.size()); assertEquals(2, listB.size()); MoreAsserts.assertEquals(hash1a, listA.get(0)); MoreAsserts.assertEquals(hash1a, listB.get(0)); MoreAsserts.assertEquals(hash2a, listA.get(1)); MoreAsserts.assertEquals(hash2a, listB.get(1)); } } Loading
services/backup/java/com/android/server/backup/BackupManagerService.java +1 −72 Original line number Diff line number Diff line Loading @@ -7617,77 +7617,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // ----- Restore handling ----- // new style: we only store the SHA-1 hashes of each sig, not the full block static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { if (target == null) { return false; } // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system // partition will be signed with the device's platform certificate, so on // different phones the same system app will have different signatures.) if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 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 (MORE_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) { 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])); } // 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; } } // match is false when no on-device sig matched one of the stored ones if (!match) { return false; } } return true; } static byte[] hashSignature(Signature sig) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(sig.toByteArray()); return digest.digest(); } catch (NoSuchAlgorithmException e) { Slog.w(TAG, "No SHA-256 algorithm found!"); } return null; } // Old style: directly match the stored vs on device signature blocks static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { if (target == null) { Loading Loading @@ -8200,7 +8129,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName); if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) { Slog.w(TAG, "Signature mismatch restoring " + packageName); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, "Signature mismatch"); Loading
services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +5 −22 Original line number Diff line number Diff line Loading @@ -205,7 +205,7 @@ public class PackageManagerBackupAgent extends BackupAgent { PackageManager.GET_SIGNATURES); homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); homeVersion = homeInfo.versionCode; homeSigHashes = hashSignatureArray(homeInfo.signatures); homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures); } catch (NameNotFoundException e) { Slog.w(TAG, "Can't access preferred home info"); // proceed as though there were no preferred home set Loading @@ -222,7 +222,7 @@ public class PackageManagerBackupAgent extends BackupAgent { final boolean needHomeBackup = (homeVersion != mStoredHomeVersion) || !Objects.equals(home, mStoredHomeComponent) || (home != null && !BackupManagerService.signaturesMatch(mStoredHomeSigHashes, homeInfo)); && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo)); if (needHomeBackup) { if (DEBUG) { Slog.i(TAG, "Home preference changed; backing up new state " + home); Loading Loading @@ -309,7 +309,7 @@ public class PackageManagerBackupAgent extends BackupAgent { outputBuffer.reset(); outputBufferStream.writeInt(info.versionCode); writeSignatureHashArray(outputBufferStream, hashSignatureArray(info.signatures)); BackupUtils.hashSignatureArray(info.signatures)); if (DEBUG) { Slog.v(TAG, "+ writing metadata for " + packName Loading Loading @@ -432,18 +432,6 @@ public class PackageManagerBackupAgent extends BackupAgent { mRestoredSignatures = sigMap; } private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length); for (Signature s : sigs) { hashes.add(BackupManagerService.hashSignature(s)); } return hashes; } private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes) throws IOException { // the number of entries in the array Loading Loading @@ -492,13 +480,8 @@ public class PackageManagerBackupAgent extends BackupAgent { } if (nonHashFound) { ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.size()); for (int i = 0; i < sigs.size(); i++) { Signature s = new Signature(sigs.get(i)); hashes.add(BackupManagerService.hashSignature(s)); } sigs = hashes; // Replace with the hashes. sigs = BackupUtils.hashSignatureArray(sigs); } return sigs; Loading
services/core/java/com/android/server/backup/BackupUtils.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.backup; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.Signature; import android.util.Slog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class BackupUtils { private static final String TAG = "BackupUtils"; private static final boolean DEBUG = false; // STOPSHIP if true public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { if (target == null) { return false; } // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system // partition will be signed with the device's platform certificate, so on // different phones the same system app will have different signatures.) if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check"); 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) { 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])); } // 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; } } // match is false when no on-device sig matched one of the stored ones if (!match) { return false; } } return true; } public static byte[] hashSignature(byte[] signature) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); digest.update(signature); return digest.digest(); } catch (NoSuchAlgorithmException e) { Slog.w(TAG, "No SHA-256 algorithm found!"); } return null; } public static byte[] hashSignature(Signature signature) { return hashSignature(signature.toByteArray()); } public static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<>(sigs.length); for (Signature s : sigs) { hashes.add(hashSignature(s)); } return hashes; } public static ArrayList<byte[]> hashSignatureArray(List<byte[]> sigs) { if (sigs == null) { return null; } ArrayList<byte[]> hashes = new ArrayList<>(sigs.size()); for (byte[] s : sigs) { hashes.add(hashSignature(s)); } return hashes; } }
services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.backup; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; 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 com.android.server.backup.BackupUtils; import java.util.ArrayList; import java.util.Arrays; @SmallTest public class BackupUtilsTest extends AndroidTestCase { 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 PackageInfo genPackage(String... signatures) { final PackageInfo pi = new PackageInfo(); pi.packageName = "package"; pi.applicationInfo = new ApplicationInfo(); pi.signatures = genSignatures(signatures); return pi; } 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())); PackageInfo pi; // 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"))); } public void testHashSignature() { final byte[] sig1 = "abc".getBytes(); final byte[] sig2 = "def".getBytes(); final byte[] hash1a = BackupUtils.hashSignature(sig1); final byte[] hash1b = BackupUtils.hashSignature(new Signature(sig1)); final byte[] hash2a = BackupUtils.hashSignature(sig2); final byte[] hash2b = BackupUtils.hashSignature(new Signature(sig2)); assertEquals(32, hash1a.length); MoreAsserts.assertEquals(hash1a, hash1b); assertEquals(32, hash2a.length); MoreAsserts.assertEquals(hash2a, hash2b); assertFalse(Arrays.equals(hash1a, hash2a)); final ArrayList<byte[]> listA = BackupUtils.hashSignatureArray(Arrays.asList( "abc".getBytes(), "def".getBytes())); final ArrayList<byte[]> listB = BackupUtils.hashSignatureArray(new Signature[]{ new Signature("abc".getBytes()), new Signature("def".getBytes())}); assertEquals(2, listA.size()); assertEquals(2, listB.size()); MoreAsserts.assertEquals(hash1a, listA.get(0)); MoreAsserts.assertEquals(hash1a, listB.get(0)); MoreAsserts.assertEquals(hash2a, listA.get(1)); MoreAsserts.assertEquals(hash2a, listB.get(1)); } }