1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.appdigest; 18 19 import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES; 20 21 import static androidx.core.appdigest.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256; 22 import static androidx.core.appdigest.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; 23 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_MD5; 24 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256; 25 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA1; 26 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA256; 27 import static androidx.core.appdigest.Checksum.TYPE_WHOLE_SHA512; 28 import static androidx.core.appdigest.Checksums.TRUST_ALL; 29 import static androidx.core.appdigest.Checksums.TRUST_NONE; 30 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertFalse; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertNull; 35 import static org.junit.Assert.assertTrue; 36 37 import android.Manifest; 38 import android.app.PendingIntent; 39 import android.app.UiAutomation; 40 import android.content.BroadcastReceiver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.pm.ApplicationInfo; 45 import android.content.pm.PackageInfo; 46 import android.content.pm.PackageInstaller; 47 import android.content.pm.PackageManager; 48 import android.content.pm.Signature; 49 import android.os.Build; 50 import android.os.ParcelFileDescriptor; 51 52 import androidx.annotation.RequiresApi; 53 import androidx.concurrent.futures.ResolvableFuture; 54 import androidx.test.core.app.ApplicationProvider; 55 import androidx.test.ext.junit.runners.AndroidJUnit4; 56 import androidx.test.filters.LargeTest; 57 import androidx.test.filters.SdkSuppress; 58 import androidx.test.filters.SmallTest; 59 import androidx.test.platform.app.InstrumentationRegistry; 60 61 import org.jspecify.annotations.NonNull; 62 import org.jspecify.annotations.Nullable; 63 import org.junit.After; 64 import org.junit.Assert; 65 import org.junit.Assume; 66 import org.junit.Before; 67 import org.junit.BeforeClass; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 71 import java.io.ByteArrayInputStream; 72 import java.io.ByteArrayOutputStream; 73 import java.io.DataInputStream; 74 import java.io.DataOutputStream; 75 import java.io.EOFException; 76 import java.io.IOException; 77 import java.io.InputStream; 78 import java.io.OutputStream; 79 import java.security.cert.Certificate; 80 import java.security.cert.CertificateFactory; 81 import java.security.cert.X509Certificate; 82 import java.util.ArrayList; 83 import java.util.Arrays; 84 import java.util.Comparator; 85 import java.util.List; 86 import java.util.concurrent.Executor; 87 import java.util.concurrent.Executors; 88 import java.util.function.Predicate; 89 90 @SuppressWarnings("deprecation") /* WHOLE_MD5, WHOLE_SHA1 */ 91 @RunWith(AndroidJUnit4.class) 92 public class ChecksumsTest { 93 private static final String INSTALLER_PACKAGE_NAME = "androidx.core.appdigest.test"; 94 private static final String V2V3_PACKAGE_NAME = "androidx.core.appdigest.test"; 95 private static final String V4_PACKAGE_NAME = "com.example.helloworld"; 96 private static final String FIXED_PACKAGE_NAME = "android.appsecurity.cts.tinyapp"; 97 98 private static final String TEST_V4_APK = "HelloWorld5.apk"; 99 private static final String TEST_V4_SPLIT0 = "HelloWorld5_hdpi-v4.apk"; 100 private static final String TEST_V4_SPLIT1 = "HelloWorld5_mdpi-v4.apk"; 101 private static final String TEST_V4_SPLIT2 = "HelloWorld5_xhdpi-v4.apk"; 102 private static final String TEST_V4_SPLIT3 = "HelloWorld5_xxhdpi-v4.apk"; 103 private static final String TEST_V4_SPLIT4 = "HelloWorld5_xxxhdpi-v4.apk"; 104 105 private static final String TEST_FIXED_APK = "CtsPkgInstallTinyAppV2V3V4.apk"; 106 private static final String TEST_FIXED_APK_DIGESTS_FILE = 107 "CtsPkgInstallTinyAppV2V3V4.digests"; 108 private static final String TEST_FIXED_APK_DIGESTS_SIGNATURE = 109 "CtsPkgInstallTinyAppV2V3V4.digests.signature"; 110 private static final String TEST_CERTIFICATE = "test-cert.x509.pem"; 111 private static final String TEST_FIXED_APK_V1 = "CtsPkgInstallTinyAppV1.apk"; 112 private static final String TEST_FIXED_APK_V2_SHA512 = 113 "CtsPkgInstallTinyAppV2V3V4-Sha512withEC.apk"; 114 private static final String TEST_FIXED_APK_VERITY = "CtsPkgInstallTinyAppV2V3V4-Verity.apk"; 115 116 private static final String TEST_FIXED_APK_MD5 = "c19868da017dc01467169f8ea7c5bc57"; 117 private static final String TEST_FIXED_APK_V2_SHA256 = 118 "1eec9e86e322b8d7e48e255fc3f2df2dbc91036e63982ff9850597c6a37bbeb3"; 119 private static final String TEST_FIXED_APK_SHA256 = 120 "91aa30c1ce8d0474052f71cb8210691d41f534989c5521e27e794ec4f754c5ef"; 121 private static final String TEST_FIXED_APK_SHA512 = 122 "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af525357" 123 + "11e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018"; 124 125 private static final byte[] NO_SIGNATURE = null; 126 127 private static final int ALL_CHECKSUMS = 128 TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256 129 | TYPE_WHOLE_SHA512 130 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; 131 private static final char[] HEX_LOWER_CASE_DIGITS = 132 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 133 private Context mContext; 134 private Executor mExecutor; 135 136 @BeforeClass onBeforeClass()137 public static void onBeforeClass() throws Exception { 138 uninstallPackageSilently(V4_PACKAGE_NAME); 139 uninstallPackageSilently(FIXED_PACKAGE_NAME); 140 } 141 getChecksums(@onNull Context context, @NonNull Executor executor, @NonNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)142 private static Checksum[] getChecksums(@NonNull Context context, @NonNull Executor executor, 143 @NonNull String packageName, boolean includeSplits, @Checksum.Type int required, 144 @NonNull List<Certificate> trustedInstallers) throws Exception { 145 Checksum[] checksums = Checksums.getChecksums(context, packageName, includeSplits, 146 required, trustedInstallers, executor).get(); 147 148 Arrays.sort(checksums, new Comparator<Checksum>() { 149 @Override 150 public int compare(Checksum lhs, Checksum rhs) { 151 final String lhsSplit = lhs.getSplitName(); 152 final String rhsSplit = rhs.getSplitName(); 153 if ((lhsSplit == rhsSplit) || (lhsSplit != null && lhsSplit.equals(rhsSplit))) { 154 return Integer.signum(lhs.getType() - rhs.getType()); 155 } 156 if (lhsSplit == null) { 157 return -1; 158 } 159 if (rhsSplit == null) { 160 return +1; 161 } 162 return lhsSplit.compareTo(rhsSplit); 163 } 164 }); 165 166 return checksums; 167 } 168 getFileChecksums(@onNull Context context, @NonNull Executor executor, @NonNull String filePath, @Checksum.Type int required, @Nullable String installerPackageName, @NonNull List<Certificate> trustedInstallers)169 private static Checksum[] getFileChecksums(@NonNull Context context, @NonNull Executor executor, 170 @NonNull String filePath, @Checksum.Type int required, 171 @Nullable String installerPackageName, 172 @NonNull List<Certificate> trustedInstallers) throws Exception { 173 Checksum[] checksums = Checksums.getFileChecksums(context, filePath, 174 required, installerPackageName, trustedInstallers, executor).get(); 175 176 Arrays.sort(checksums, new Comparator<Checksum>() { 177 @Override 178 public int compare(Checksum lhs, Checksum rhs) { 179 return Integer.signum(lhs.getType() - rhs.getType()); 180 } 181 }); 182 183 return checksums; 184 } 185 getResourceAsStream(String name)186 public static InputStream getResourceAsStream(String name) { 187 return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); 188 } 189 readFullStream(InputStream inputStream)190 private static String readFullStream(InputStream inputStream) throws IOException { 191 ByteArrayOutputStream result = new ByteArrayOutputStream(); 192 writeFullStream(inputStream, result, -1); 193 return result.toString("UTF-8"); 194 } 195 readAllBytes(InputStream inputStream)196 private static byte[] readAllBytes(InputStream inputStream) throws IOException { 197 ByteArrayOutputStream result = new ByteArrayOutputStream(); 198 writeFullStream(inputStream, result, -1); 199 return result.toByteArray(); 200 } 201 writeFullStream(InputStream inputStream, OutputStream outputStream, long expected)202 private static void writeFullStream(InputStream inputStream, OutputStream outputStream, 203 long expected) 204 throws IOException { 205 byte[] buffer = new byte[1024]; 206 long total = 0; 207 int length; 208 while ((length = inputStream.read(buffer)) != -1) { 209 outputStream.write(buffer, 0, length); 210 total += length; 211 } 212 if (expected > 0) { 213 Assert.assertEquals(expected, total); 214 } 215 } 216 executeShellCommand(String command)217 private static String executeShellCommand(String command) throws IOException { 218 if (Build.VERSION.SDK_INT >= 29) { 219 return InstallerApi29.executeShellCommand(command); 220 } 221 return ""; 222 } 223 uninstallPackageSilently(String packageName)224 private static void uninstallPackageSilently(String packageName) throws IOException { 225 executeShellCommand("pm uninstall " + packageName); 226 } 227 bytesToHexString(byte[] array)228 private static @NonNull String bytesToHexString(byte[] array) { 229 int offset = 0; 230 int length = array.length; 231 232 char[] digits = HEX_LOWER_CASE_DIGITS; 233 char[] buf = new char[length * 2]; 234 235 int bufIndex = 0; 236 for (int i = offset; i < offset + length; i++) { 237 byte b = array[i]; 238 buf[bufIndex++] = digits[(b >>> 4) & 0x0F]; 239 buf[bufIndex++] = digits[b & 0x0F]; 240 } 241 242 return new String(buf); 243 } 244 hexStringToBytes(String s)245 private static byte @NonNull [] hexStringToBytes(String s) { 246 int len = s.length(); 247 byte[] data = new byte[len / 2]; 248 for (int i = 0; i < len; i += 2) { 249 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 250 + Character.digit(s.charAt(i + 1), 16)); 251 } 252 return data; 253 } 254 255 @Before onBefore()256 public void onBefore() throws Exception { 257 // b/331664452 258 Assume.assumeFalse(Build.VERSION.SDK_INT >= 35); 259 mContext = ApplicationProvider.getApplicationContext(); 260 mExecutor = Executors.newCachedThreadPool(); 261 } 262 263 @After onAfter()264 public void onAfter() throws Exception { 265 uninstallPackageSilently(V4_PACKAGE_NAME); 266 assertFalse(isAppInstalled(V4_PACKAGE_NAME)); 267 uninstallPackageSilently(FIXED_PACKAGE_NAME); 268 assertFalse(isAppInstalled(FIXED_PACKAGE_NAME)); 269 } 270 271 @SmallTest 272 @Test 273 @SdkSuppress(maxSdkVersion = 33) // b/262909049: Failing on SDK 34 // b/262909049: Failing on SDK 34 testDefaultChecksums()274 public void testDefaultChecksums() throws Exception { 275 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 276 return; // b/262909049: Do not run this test on pre-release Android U. 277 } 278 279 Checksum[] checksums = getChecksums(V2V3_PACKAGE_NAME, true, 0, TRUST_NONE); 280 assertNotNull(checksums); 281 if (Build.VERSION.SDK_INT >= 31) { 282 assertEquals(1, checksums.length); 283 assertEquals(checksums[0].getType(), 284 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 285 } else { 286 assertEquals(0, checksums.length); 287 } 288 } 289 290 @SmallTest 291 @Test testDefaultFileChecksums()292 public void testDefaultFileChecksums() throws Exception { 293 Checksum[] checksums = getFileChecksums(V2V3_PACKAGE_NAME, 0, TRUST_NONE); 294 assertNotNull(checksums); 295 assertEquals(0, checksums.length); 296 } 297 298 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 299 @LargeTest 300 @Test testSplitsDefaultChecksums()301 public void testSplitsDefaultChecksums() throws Exception { 302 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 303 return; // b/262909049: Do not run this test on pre-release Android U. 304 } 305 306 installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2, 307 TEST_V4_SPLIT3, TEST_V4_SPLIT4}); 308 assertTrue(isAppInstalled(V4_PACKAGE_NAME)); 309 310 Checksum[] checksums = getChecksums(V4_PACKAGE_NAME, true, 0, TRUST_NONE); 311 assertNotNull(checksums); 312 if (Build.VERSION.SDK_INT >= 31) { 313 assertEquals(checksums.length, 6); 314 // v2/v3 signature use 1M merkle tree. 315 assertEquals(null, checksums[0].getSplitName()); 316 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[0].getType()); 317 assertEquals("config.hdpi", checksums[1].getSplitName()); 318 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[1].getType()); 319 assertEquals("config.mdpi", checksums[2].getSplitName()); 320 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[2].getType()); 321 assertEquals("config.xhdpi", checksums[3].getSplitName()); 322 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[3].getType()); 323 assertEquals("config.xxhdpi", checksums[4].getSplitName()); 324 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[4].getType()); 325 assertEquals("config.xxxhdpi", checksums[5].getSplitName()); 326 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType()); 327 } else { 328 assertEquals(0, checksums.length); 329 } 330 } 331 332 @SdkSuppress(minSdkVersion = 29) 333 @LargeTest 334 @Test testSplitsDefaultFileChecksums()335 public void testSplitsDefaultFileChecksums() throws Exception { 336 installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2, 337 TEST_V4_SPLIT3, TEST_V4_SPLIT4}); 338 assertTrue(isAppInstalled(V4_PACKAGE_NAME)); 339 340 Checksum[] checksums = getFileChecksums(V4_PACKAGE_NAME, 0, TRUST_NONE); 341 assertNotNull(checksums); 342 assertEquals(0, checksums.length); 343 } 344 345 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 346 @LargeTest 347 @Test testSplitsSha256Checksums()348 public void testSplitsSha256Checksums() throws Exception { 349 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 350 return; // b/262909049: Do not run this test on pre-release Android U. 351 } 352 353 installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2, 354 TEST_V4_SPLIT3, TEST_V4_SPLIT4}); 355 assertTrue(isAppInstalled(V4_PACKAGE_NAME)); 356 357 Checksum[] checksums = getChecksums(V4_PACKAGE_NAME, true, TYPE_WHOLE_SHA256, 358 TRUST_NONE); 359 assertNotNull(checksums); 360 if (Build.VERSION.SDK_INT >= 31) { 361 assertEquals(checksums.length, 12); 362 // v2/v3 signature use 1M merkle tree. 363 assertEquals(null, checksums[0].getSplitName()); 364 assertEquals(TYPE_WHOLE_SHA256, checksums[0].getType()); 365 assertEquals(bytesToHexString(checksums[0].getValue()), 366 "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc"); 367 assertEquals(null, checksums[1].getSplitName()); 368 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[1].getType()); 369 assertEquals(checksums[2].getSplitName(), "config.hdpi"); 370 assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA256); 371 assertEquals(bytesToHexString(checksums[2].getValue()), 372 "336a47c278f6b6c22abffefa6a62971fd0bd718d6947143e6ed1f6f6126a8196"); 373 assertEquals("config.hdpi", checksums[3].getSplitName()); 374 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[3].getType()); 375 assertEquals(checksums[4].getSplitName(), "config.mdpi"); 376 assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256); 377 assertEquals(bytesToHexString(checksums[4].getValue()), 378 "17fe9f85e6f29a7354932002c8bc4cb829e1f4acf7f30626bd298c810bb13215"); 379 assertEquals("config.mdpi", checksums[5].getSplitName()); 380 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType()); 381 assertEquals(checksums[6].getSplitName(), "config.xhdpi"); 382 assertEquals(checksums[6].getType(), TYPE_WHOLE_SHA256); 383 assertEquals(bytesToHexString(checksums[6].getValue()), 384 "71a0b0ac5970def7ad80071c909be1e446174a9b39ea5cbf3004db05f87bcc4b"); 385 assertEquals("config.xhdpi", checksums[7].getSplitName()); 386 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[7].getType()); 387 assertEquals(checksums[8].getSplitName(), "config.xxhdpi"); 388 assertEquals(checksums[8].getType(), TYPE_WHOLE_SHA256); 389 assertEquals(bytesToHexString(checksums[8].getValue()), 390 "cf6eaee309cf906df5519b9a449ab136841cec62857e283fb4fd20dcd2ea14aa"); 391 assertEquals("config.xxhdpi", checksums[9].getSplitName()); 392 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[9].getType()); 393 assertEquals(checksums[10].getSplitName(), "config.xxxhdpi"); 394 assertEquals(checksums[10].getType(), TYPE_WHOLE_SHA256); 395 assertEquals(bytesToHexString(checksums[10].getValue()), 396 "e7c51a01794d33e13d005b62e5ae96a39215bc588e0a2ef8f6161e1e360a17cc"); 397 assertEquals("config.xxxhdpi", checksums[11].getSplitName()); 398 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[11].getType()); 399 } else { 400 assertEquals(6, checksums.length); 401 assertEquals(checksums[0].getSplitName(), null); 402 assertEquals(checksums[0].getType(), TYPE_WHOLE_SHA256); 403 assertEquals(bytesToHexString(checksums[0].getValue()), 404 "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc"); 405 assertEquals(checksums[1].getSplitName(), "config.hdpi"); 406 assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256); 407 assertEquals(bytesToHexString(checksums[1].getValue()), 408 "336a47c278f6b6c22abffefa6a62971fd0bd718d6947143e6ed1f6f6126a8196"); 409 assertEquals(checksums[2].getSplitName(), "config.mdpi"); 410 assertEquals(checksums[2].getType(), TYPE_WHOLE_SHA256); 411 assertEquals(bytesToHexString(checksums[2].getValue()), 412 "17fe9f85e6f29a7354932002c8bc4cb829e1f4acf7f30626bd298c810bb13215"); 413 assertEquals(checksums[3].getSplitName(), "config.xhdpi"); 414 assertEquals(checksums[3].getType(), TYPE_WHOLE_SHA256); 415 assertEquals(bytesToHexString(checksums[3].getValue()), 416 "71a0b0ac5970def7ad80071c909be1e446174a9b39ea5cbf3004db05f87bcc4b"); 417 assertEquals(checksums[4].getSplitName(), "config.xxhdpi"); 418 assertEquals(checksums[4].getType(), TYPE_WHOLE_SHA256); 419 assertEquals(bytesToHexString(checksums[4].getValue()), 420 "cf6eaee309cf906df5519b9a449ab136841cec62857e283fb4fd20dcd2ea14aa"); 421 assertEquals(checksums[5].getSplitName(), "config.xxxhdpi"); 422 assertEquals(checksums[5].getType(), TYPE_WHOLE_SHA256); 423 assertEquals(bytesToHexString(checksums[5].getValue()), 424 "e7c51a01794d33e13d005b62e5ae96a39215bc588e0a2ef8f6161e1e360a17cc"); 425 } 426 } 427 428 @SdkSuppress(minSdkVersion = 29) 429 @LargeTest 430 @Test testSplitsSha256FileChecksums()431 public void testSplitsSha256FileChecksums() throws Exception { 432 installSplits(new String[]{TEST_V4_APK, TEST_V4_SPLIT0, TEST_V4_SPLIT1, TEST_V4_SPLIT2, 433 TEST_V4_SPLIT3, TEST_V4_SPLIT4}); 434 assertTrue(isAppInstalled(V4_PACKAGE_NAME)); 435 436 Checksum[] checksums = getFileChecksums(V4_PACKAGE_NAME, TYPE_WHOLE_SHA256, 437 TRUST_NONE); 438 assertNotNull(checksums); 439 assertEquals(1, checksums.length); 440 assertEquals(checksums[0].getSplitName(), null); 441 assertEquals(checksums[0].getType(), TYPE_WHOLE_SHA256); 442 assertEquals(bytesToHexString(checksums[0].getValue()), 443 "ce4ad41be1191ab3cdfef09ab6fb3c5d057e15cb3553661b393f770d9149f1cc"); 444 } 445 446 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 447 @LargeTest 448 @Test testFixedDefaultChecksums()449 public void testFixedDefaultChecksums() throws Exception { 450 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 451 return; // b/262909049: Do not run this test on pre-release Android U. 452 } 453 454 installPackage(TEST_FIXED_APK); 455 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 456 457 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, 458 TRUST_NONE); 459 assertNotNull(checksums); 460 if (Build.VERSION.SDK_INT >= 31) { 461 assertEquals(1, checksums.length); 462 // v2/v3 signature use 1M merkle tree. 463 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[0].getType()); 464 assertEquals(TEST_FIXED_APK_V2_SHA256, bytesToHexString(checksums[0].getValue())); 465 assertNull(checksums[0].getInstallerCertificate()); 466 } else { 467 assertEquals(0, checksums.length); 468 } 469 } 470 471 @SdkSuppress(minSdkVersion = 29) 472 @LargeTest 473 @Test testFixedDefaultFileChecksums()474 public void testFixedDefaultFileChecksums() throws Exception { 475 installPackage(TEST_FIXED_APK); 476 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 477 478 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE); 479 assertNotNull(checksums); 480 assertEquals(0, checksums.length); 481 } 482 483 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 484 @LargeTest 485 @Test testFixedV1DefaultChecksums()486 public void testFixedV1DefaultChecksums() throws Exception { 487 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 488 return; // b/262909049: Do not run this test on pre-release Android U. 489 } 490 491 installPackage(TEST_FIXED_APK_V1); 492 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 493 494 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, 495 TRUST_NONE); 496 assertNotNull(checksums); 497 assertEquals(0, checksums.length); 498 } 499 500 @SdkSuppress(minSdkVersion = 29) 501 @LargeTest 502 @Test testFixedV1DefaultFileChecksums()503 public void testFixedV1DefaultFileChecksums() throws Exception { 504 installPackage(TEST_FIXED_APK_V1); 505 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 506 507 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE); 508 assertNotNull(checksums); 509 assertEquals(0, checksums.length); 510 } 511 512 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 513 @LargeTest 514 @Test testFixedSha512DefaultChecksums()515 public void testFixedSha512DefaultChecksums() throws Exception { 516 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 517 return; // b/262909049: Do not run this test on pre-release Android U. 518 } 519 520 installPackage(TEST_FIXED_APK_V2_SHA512); 521 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 522 523 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, 524 TRUST_NONE); 525 assertNotNull(checksums); 526 if (Build.VERSION.SDK_INT >= 31) { 527 assertEquals(1, checksums.length); 528 // v2/v3 signature use 1M merkle tree. 529 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, checksums[0].getType()); 530 assertEquals(bytesToHexString(checksums[0].getValue()), 531 "6b866e8a54a3e358dfc20007960fb96123845f6c6d6c45f5fddf88150d71677f" 532 + "4c3081a58921c88651f7376118aca312cf764b391cdfb8a18c6710f9f27916a0"); 533 assertNull(checksums[0].getInstallerCertificate()); 534 } else { 535 assertEquals(0, checksums.length); 536 } 537 } 538 539 @SdkSuppress(minSdkVersion = 29) 540 @LargeTest 541 @Test testFixedSha512DefaultFileChecksums()542 public void testFixedSha512DefaultFileChecksums() throws Exception { 543 installPackage(TEST_FIXED_APK_V2_SHA512); 544 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 545 546 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE); 547 assertNotNull(checksums); 548 assertEquals(0, checksums.length); 549 } 550 551 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 552 @LargeTest 553 @Test testFixedVerityDefaultChecksums()554 public void testFixedVerityDefaultChecksums() throws Exception { 555 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 556 return; // b/262909049: Do not run this test on pre-release Android U. 557 } 558 559 installPackage(TEST_FIXED_APK_VERITY); 560 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 561 562 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, 563 TRUST_NONE); 564 assertNotNull(checksums); 565 // No usable hashes as verity-in-v2-signature does not cover the whole file. 566 assertEquals(0, checksums.length); 567 } 568 569 @SdkSuppress(minSdkVersion = 29) 570 @LargeTest 571 @Test testFixedVerityDefaultFileChecksums()572 public void testFixedVerityDefaultFileChecksums() throws Exception { 573 installPackage(TEST_FIXED_APK_VERITY); 574 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 575 576 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE); 577 assertNotNull(checksums); 578 // No usable hashes as verity-in-v2-signature does not cover the whole file. 579 assertEquals(0, checksums.length); 580 } 581 582 @LargeTest 583 @Test testAllChecksums()584 public void testAllChecksums() throws Exception { 585 Checksum[] checksums = getChecksums(V2V3_PACKAGE_NAME, true, ALL_CHECKSUMS, 586 TRUST_NONE); 587 assertNotNull(checksums); 588 if (Build.VERSION.SDK_INT >= 31) { 589 assertEquals(checksums.length, 7); 590 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 591 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 592 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 593 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 594 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 595 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, checksums[5].getType()); 596 assertEquals(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, checksums[6].getType()); 597 } else { 598 assertEquals(5, checksums.length); 599 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 600 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 601 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 602 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 603 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 604 } 605 } 606 607 @LargeTest 608 @Test testAllFileChecksums()609 public void testAllFileChecksums() throws Exception { 610 Checksum[] checksums = getFileChecksums(V2V3_PACKAGE_NAME, ALL_CHECKSUMS, 611 TRUST_NONE); 612 assertNotNull(checksums); 613 assertEquals(5, checksums.length); 614 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 615 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 616 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 617 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 618 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 619 } 620 621 @SdkSuppress(minSdkVersion = 29) 622 @LargeTest 623 @Test testFixedAllChecksums()624 public void testFixedAllChecksums() throws Exception { 625 installPackage(TEST_FIXED_APK); 626 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 627 628 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, 629 TRUST_NONE); 630 validateFixedAllChecksums(checksums); 631 } 632 633 @SdkSuppress(minSdkVersion = 29) 634 @LargeTest 635 @Test testFixedAllFileChecksums()636 public void testFixedAllFileChecksums() throws Exception { 637 installPackage(TEST_FIXED_APK); 638 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 639 640 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, ALL_CHECKSUMS, 641 TRUST_NONE); 642 validateFixedAllChecksumsFallback(checksums); 643 } 644 645 @SdkSuppress(minSdkVersion = 29) 646 @LargeTest 647 @Test testFixedAllChecksumsDirectExecutor()648 public void testFixedAllChecksumsDirectExecutor() throws Exception { 649 installPackage(TEST_FIXED_APK); 650 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 651 652 Checksum[] checksums = getChecksums(mContext, new Executor() { 653 @Override 654 public void execute(Runnable command) { 655 command.run(); 656 } 657 }, FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE); 658 validateFixedAllChecksums(checksums); 659 } 660 661 @SdkSuppress(minSdkVersion = 29) 662 @LargeTest 663 @Test 664 @SuppressWarnings("deprecation") testFixedAllFileChecksumsDirectExecutor()665 public void testFixedAllFileChecksumsDirectExecutor() throws Exception { 666 installPackage(TEST_FIXED_APK); 667 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 668 669 final String apk = 670 mContext.getPackageManager().getApplicationInfo(FIXED_PACKAGE_NAME, 0).sourceDir; 671 Checksum[] checksums = getFileChecksums(mContext, new Executor() { 672 @Override 673 public void execute(Runnable command) { 674 command.run(); 675 } 676 }, apk, ALL_CHECKSUMS, INSTALLER_PACKAGE_NAME, TRUST_NONE); 677 validateFixedAllChecksumsFallback(checksums); 678 } 679 680 @SdkSuppress(minSdkVersion = 29) 681 @LargeTest 682 @Test testFixedAllChecksumsSingleThread()683 public void testFixedAllChecksumsSingleThread() throws Exception { 684 installPackage(TEST_FIXED_APK); 685 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 686 687 Checksum[] checksums = getChecksums(mContext, Executors.newSingleThreadExecutor(), 688 FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, TRUST_NONE); 689 validateFixedAllChecksums(checksums); 690 } 691 692 @SdkSuppress(minSdkVersion = 29) 693 @LargeTest 694 @Test 695 @SuppressWarnings("deprecation") testFixedAllFileChecksumsSingleThread()696 public void testFixedAllFileChecksumsSingleThread() throws Exception { 697 installPackage(TEST_FIXED_APK); 698 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 699 700 final String apk = 701 mContext.getPackageManager().getApplicationInfo(FIXED_PACKAGE_NAME, 0).sourceDir; 702 Checksum[] checksums = getFileChecksums(mContext, Executors.newSingleThreadExecutor(), 703 apk, ALL_CHECKSUMS, INSTALLER_PACKAGE_NAME, TRUST_NONE); 704 validateFixedAllChecksumsFallback(checksums); 705 } 706 707 @SdkSuppress(minSdkVersion = 29) 708 @LargeTest 709 @Test testFixedV1AllChecksums()710 public void testFixedV1AllChecksums() throws Exception { 711 installPackage(TEST_FIXED_APK_V1); 712 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 713 714 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, ALL_CHECKSUMS, 715 TRUST_NONE); 716 assertNotNull(checksums); 717 assertEquals(5, checksums.length); 718 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 719 if (Build.VERSION.SDK_INT < 33) { 720 assertEquals("1e8f831ef35257ca30d11668520aaafc6da243e853531caabc3b7867986f8886", 721 bytesToHexString(checksums[0].getValue())); 722 } else { 723 assertEquals("0b9bd6ef683e0c4e8940aba6460382b33e607c0fcf487f3dc6a44b715615d166", 724 bytesToHexString(checksums[0].getValue())); 725 } 726 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 727 assertEquals(bytesToHexString(checksums[1].getValue()), "78e51e8c51e4adc6870cd71389e0f3db"); 728 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 729 assertEquals("f6654505f2274fd9bfc098b660cdfdc2e4da6d53", 730 bytesToHexString(checksums[2].getValue())); 731 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 732 assertEquals("43755d36ec944494f6275ee92662aca95079b3aa6639f2d35208c5af15adff78", 733 bytesToHexString(checksums[3].getValue())); 734 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 735 assertEquals("030fc815a4957c163af2bc6f30dd5b48ac09c94c25a824a514609e1476f91421" 736 + "e2c8b6baa16ef54014ad6c5b90c37b26b0f5c8aeb01b63a1db2eca133091c8d1", 737 bytesToHexString(checksums[4].getValue())); 738 } 739 740 @SdkSuppress(minSdkVersion = 29) 741 @LargeTest 742 @Test testFixedV1AllFileChecksums()743 public void testFixedV1AllFileChecksums() throws Exception { 744 installPackage(TEST_FIXED_APK_V1); 745 assertTrue(isAppInstalled(FIXED_PACKAGE_NAME)); 746 747 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, ALL_CHECKSUMS, 748 TRUST_NONE); 749 assertNotNull(checksums); 750 assertEquals(5, checksums.length); 751 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 752 assertEquals("1e8f831ef35257ca30d11668520aaafc6da243e853531caabc3b7867986f8886", 753 bytesToHexString(checksums[0].getValue())); 754 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 755 assertEquals(bytesToHexString(checksums[1].getValue()), "78e51e8c51e4adc6870cd71389e0f3db"); 756 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 757 assertEquals("f6654505f2274fd9bfc098b660cdfdc2e4da6d53", 758 bytesToHexString(checksums[2].getValue())); 759 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 760 assertEquals("43755d36ec944494f6275ee92662aca95079b3aa6639f2d35208c5af15adff78", 761 bytesToHexString(checksums[3].getValue())); 762 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 763 assertEquals("030fc815a4957c163af2bc6f30dd5b48ac09c94c25a824a514609e1476f91421" 764 + "e2c8b6baa16ef54014ad6c5b90c37b26b0f5c8aeb01b63a1db2eca133091c8d1", 765 bytesToHexString(checksums[4].getValue())); 766 } 767 768 @SdkSuppress(minSdkVersion = 31) 769 @SmallTest 770 @Test testReadWriteChecksums()771 public void testReadWriteChecksums() throws Exception { 772 InstallerApi31.checkStoredChecksums(TEST_FIXED_APK_DIGESTS_FILE); 773 InstallerApi31.checkWrittenChecksums(TEST_FIXED_APK_DIGESTS_FILE); 774 } 775 776 @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 777 @LargeTest 778 @Test testInstallerChecksumsTrustNone()779 public void testInstallerChecksumsTrustNone() throws Exception { 780 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 781 return; // b/262909049: Do not run this test on pre-release Android U. 782 } 783 784 installApkWithChecksums(NO_SIGNATURE); 785 786 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_NONE); 787 assertNotNull(checksums); 788 assertEquals(1, checksums.length); 789 assertEquals(checksums[0].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 790 assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_V2_SHA256); 791 assertNull(checksums[0].getInstallerPackageName()); 792 assertNull(checksums[0].getInstallerCertificate()); 793 } 794 795 @SdkSuppress(minSdkVersion = 31) 796 @LargeTest 797 @Test testInstallerFileChecksumsTrustNone()798 public void testInstallerFileChecksumsTrustNone() throws Exception { 799 installApkWithChecksums(NO_SIGNATURE); 800 801 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 0, TRUST_NONE); 802 assertNotNull(checksums); 803 assertEquals(0, checksums.length); 804 } 805 806 @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 807 @LargeTest 808 @Test testInstallerChecksumsTrustAll()809 public void testInstallerChecksumsTrustAll() throws Exception { 810 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 811 return; // b/262909049: Do not run this test on pre-release Android U. 812 } 813 814 installApkWithChecksums(NO_SIGNATURE); 815 816 final Certificate certificate = InstallerApi31.getInstallerCertificate(mContext); 817 818 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL); 819 820 assertNotNull(checksums); 821 // installer provided. 822 assertEquals(3, checksums.length); 823 assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5); 824 assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5); 825 assertEquals(checksums[0].getSplitName(), null); 826 assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 827 assertEquals(checksums[0].getInstallerCertificate(), certificate); 828 assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256); 829 assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256); 830 assertEquals(checksums[1].getSplitName(), null); 831 assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 832 assertEquals(checksums[1].getInstallerCertificate(), certificate); 833 assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 834 assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256); 835 assertEquals(checksums[2].getSplitName(), null); 836 assertNull(checksums[2].getInstallerPackageName()); 837 assertNull(checksums[2].getInstallerCertificate()); 838 } 839 840 @SdkSuppress(minSdkVersion = 31) 841 @LargeTest 842 @Test testInstallerFileChecksumsTrustAll()843 public void testInstallerFileChecksumsTrustAll() throws Exception { 844 installApkWithChecksums(NO_SIGNATURE); 845 846 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 847 TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA256, TRUST_ALL); 848 849 assertNotNull(checksums); 850 assertEquals(2, checksums.length); 851 assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5); 852 assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5); 853 assertEquals(checksums[0].getSplitName(), null); 854 assertNull(checksums[0].getInstallerPackageName()); 855 assertNull(checksums[0].getInstallerCertificate()); 856 assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256); 857 assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256); 858 assertEquals(checksums[1].getSplitName(), null); 859 assertNull(checksums[1].getInstallerPackageName()); 860 assertNull(checksums[1].getInstallerCertificate()); 861 /* Uncomment when access bug is fixed. 862 final Certificate certificate = InstallerApi31.getInstallerCertificate(mContext); 863 assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 864 assertEquals(checksums[0].getInstallerCertificate(), certificate); 865 assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 866 assertEquals(checksums[1].getInstallerCertificate(), certificate); 867 assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 868 assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256); 869 assertEquals(checksums[2].getSplitName(), null); 870 assertEquals(checksums[2].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 871 assertEquals(checksums[2].getInstallerCertificate(), certificate); 872 */ 873 } 874 875 @SdkSuppress(minSdkVersion = 31, maxSdkVersion = 33) // b/262909049: Failing on SDK 34 876 @LargeTest 877 @Test testInstallerSignedChecksums()878 public void testInstallerSignedChecksums() throws Exception { 879 if (Build.VERSION.SDK_INT == 33 && !"REL".equals(Build.VERSION.CODENAME)) { 880 return; // b/262909049: Do not run this test on pre-release Android U. 881 } 882 883 final byte[] signature = InstallerApi31.readSignature(); 884 final Certificate certificate = InstallerApi31.readCertificate(); 885 886 installApkWithChecksums(signature); 887 888 Checksum[] checksums = getChecksums(FIXED_PACKAGE_NAME, true, 0, TRUST_ALL); 889 890 assertNotNull(checksums); 891 assertEquals(3, checksums.length); 892 assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5); 893 assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5); 894 assertEquals(checksums[0].getSplitName(), null); 895 assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 896 assertEquals(checksums[0].getInstallerCertificate(), certificate); 897 assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256); 898 assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256); 899 assertEquals(checksums[1].getSplitName(), null); 900 assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 901 assertEquals(checksums[1].getInstallerCertificate(), certificate); 902 assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 903 assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256); 904 assertEquals(checksums[2].getSplitName(), null); 905 assertNull(checksums[2].getInstallerPackageName()); 906 assertNull(checksums[2].getInstallerCertificate()); 907 } 908 909 @SdkSuppress(minSdkVersion = 31) 910 @LargeTest 911 @Test testInstallerSignedFileChecksums()912 public void testInstallerSignedFileChecksums() throws Exception { 913 final byte[] signature = InstallerApi31.readSignature(); 914 915 installApkWithChecksums(signature); 916 917 Checksum[] checksums = getFileChecksums(FIXED_PACKAGE_NAME, 918 TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA256, TRUST_ALL); 919 920 assertNotNull(checksums); 921 assertEquals(2, checksums.length); 922 assertEquals(checksums[0].getType(), TYPE_WHOLE_MD5); 923 assertEquals(bytesToHexString(checksums[0].getValue()), TEST_FIXED_APK_MD5); 924 assertEquals(checksums[0].getSplitName(), null); 925 assertNull(checksums[0].getInstallerPackageName()); 926 assertNull(checksums[0].getInstallerCertificate()); 927 assertEquals(checksums[1].getType(), TYPE_WHOLE_SHA256); 928 assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_SHA256); 929 assertEquals(checksums[1].getSplitName(), null); 930 assertNull(checksums[1].getInstallerPackageName()); 931 assertNull(checksums[1].getInstallerCertificate()); 932 /* Uncomment when access bug is fixed. 933 final Certificate certificate = InstallerApi31.readCertificate(); 934 assertEquals(checksums[0].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 935 assertEquals(checksums[0].getInstallerCertificate(), certificate); 936 assertEquals(checksums[1].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 937 assertEquals(checksums[1].getInstallerCertificate(), certificate); 938 assertEquals(checksums[2].getType(), TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 939 assertEquals(bytesToHexString(checksums[2].getValue()), TEST_FIXED_APK_V2_SHA256); 940 assertEquals(checksums[2].getSplitName(), null); 941 assertEquals(checksums[2].getInstallerPackageName(), INSTALLER_PACKAGE_NAME); 942 assertEquals(checksums[2].getInstallerCertificate(), certificate); 943 */ 944 } 945 validateFixedAllChecksums(Checksum[] checksums)946 private void validateFixedAllChecksums(Checksum[] checksums) { 947 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 948 validateFixedAllChecksumsFallback(checksums); 949 return; 950 } 951 assertNotNull(checksums); 952 assertEquals(7, checksums.length); 953 assertEquals(checksums[0].getType(), 954 android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256); 955 if (Build.VERSION.SDK_INT < 33) { 956 assertEquals(bytesToHexString(checksums[0].getValue()), 957 "90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a"); 958 } else { 959 assertEquals(bytesToHexString(checksums[0].getValue()), 960 "759626c33083fbf43215cb5b17156977d963d4c6850c0cb4e73162a665db560b"); 961 } 962 assertEquals(checksums[1].getType(), android.content.pm.Checksum.TYPE_WHOLE_MD5); 963 assertEquals(bytesToHexString(checksums[1].getValue()), TEST_FIXED_APK_MD5); 964 assertEquals(checksums[2].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA1); 965 assertEquals(bytesToHexString(checksums[2].getValue()), 966 "331eef6bc57671de28cbd7e32089d047285ade6a"); 967 assertEquals(checksums[3].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA256); 968 assertEquals(bytesToHexString(checksums[3].getValue()), TEST_FIXED_APK_SHA256); 969 assertEquals(checksums[4].getType(), android.content.pm.Checksum.TYPE_WHOLE_SHA512); 970 assertEquals(bytesToHexString(checksums[4].getValue()), 971 "b59467fe578ebc81974ab3aaa1e0d2a76fef3e4ea7212a6f2885cec1af5253571" 972 + "1e2e94496224cae3eba8dc992144ade321540ebd458ec5b9e6a4cc51170e018"); 973 assertEquals(checksums[5].getType(), 974 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); 975 assertEquals(bytesToHexString(checksums[5].getValue()), TEST_FIXED_APK_V2_SHA256); 976 assertEquals(checksums[6].getType(), 977 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512); 978 assertEquals(bytesToHexString(checksums[6].getValue()), 979 "ef80a8630283f60108e8557c924307d0ccdfb6bbbf2c0176bd49af342f43bc84" 980 + "5f2888afcb71524196dda0d6dd16a6a3292bb75b431b8ff74fb60d796e882f80"); 981 } 982 validateFixedAllChecksumsFallback(Checksum[] checksums)983 private void validateFixedAllChecksumsFallback(Checksum[] checksums) { 984 assertNotNull(checksums); 985 assertEquals(5, checksums.length); 986 assertEquals(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, checksums[0].getType()); 987 assertEquals("90553b8d221ab1b900b242a93e4cc659ace3a2ff1d5c62e502488b385854e66a", 988 bytesToHexString(checksums[0].getValue())); 989 assertEquals(TYPE_WHOLE_MD5, checksums[1].getType()); 990 assertEquals(TEST_FIXED_APK_MD5, bytesToHexString(checksums[1].getValue())); 991 assertEquals(TYPE_WHOLE_SHA1, checksums[2].getType()); 992 assertEquals("331eef6bc57671de28cbd7e32089d047285ade6a", 993 bytesToHexString(checksums[2].getValue())); 994 assertEquals(TYPE_WHOLE_SHA256, checksums[3].getType()); 995 assertEquals(TEST_FIXED_APK_SHA256, bytesToHexString(checksums[3].getValue())); 996 assertEquals(TYPE_WHOLE_SHA512, checksums[4].getType()); 997 assertEquals(TEST_FIXED_APK_SHA512, bytesToHexString(checksums[4].getValue())); 998 } 999 getChecksums(@onNull String packageName, boolean includeSplits, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)1000 private Checksum[] getChecksums(@NonNull String packageName, boolean includeSplits, 1001 @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers) 1002 throws Exception { 1003 return getChecksums(mContext, mExecutor, packageName, includeSplits, required, 1004 trustedInstallers); 1005 } 1006 1007 @SuppressWarnings("deprecation") getFileChecksums(@onNull String packageName, @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers)1008 private Checksum[] getFileChecksums(@NonNull String packageName, 1009 @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers) 1010 throws Exception { 1011 final ApplicationInfo applicationInfo = 1012 mContext.getPackageManager().getApplicationInfo(packageName, 0); 1013 if (applicationInfo == null) { 1014 throw new PackageManager.NameNotFoundException(packageName); 1015 } 1016 1017 return getFileChecksums(mContext, mExecutor, applicationInfo.sourceDir, required, 1018 INSTALLER_PACKAGE_NAME, trustedInstallers); 1019 } 1020 installPackage(String baseName)1021 private void installPackage(String baseName) throws Exception { 1022 installSplits(new String[]{baseName}); 1023 } 1024 installSplits(String[] names)1025 void installSplits(String[] names) throws Exception { 1026 if (Build.VERSION.SDK_INT >= 29) { 1027 new InstallerApi29(mContext).installSplits(names); 1028 } 1029 } 1030 installApkWithChecksums(byte[] signature)1031 void installApkWithChecksums(byte[] signature) throws Exception { 1032 if (Build.VERSION.SDK_INT >= 31) { 1033 new InstallerApi31(mContext).installApkWithChecksums(TEST_FIXED_APK, 1034 signature); 1035 } 1036 } 1037 isAppInstalled(String packageName)1038 private boolean isAppInstalled(String packageName) throws IOException { 1039 if (Build.VERSION.SDK_INT >= 29) { 1040 return InstallerApi29.isAppInstalled(packageName); 1041 } 1042 return false; 1043 } 1044 1045 @RequiresApi(29) 1046 static class InstallerApi29 { 1047 protected Context mContext; 1048 InstallerApi29(Context context)1049 InstallerApi29(Context context) { 1050 mContext = context; 1051 } 1052 writeFileToSession(PackageInstaller.Session session, String name, String apk)1053 static void writeFileToSession(PackageInstaller.Session session, String name, 1054 String apk) throws IOException { 1055 try (OutputStream os = session.openWrite(name, 0, -1); 1056 InputStream is = getResourceAsStream(apk)) { 1057 assertNotNull(name, is); 1058 writeFullStream(is, os, -1); 1059 } 1060 } 1061 getUiAutomation()1062 static UiAutomation getUiAutomation() { 1063 return InstrumentationRegistry.getInstrumentation().getUiAutomation(); 1064 } 1065 executeShellCommand(String command)1066 static String executeShellCommand(String command) throws IOException { 1067 final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command); 1068 try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) { 1069 return readFullStream(inputStream); 1070 } 1071 } 1072 isAppInstalled(final String packageName)1073 static boolean isAppInstalled(final String packageName) throws IOException { 1074 final String commandResult = executeShellCommand("pm list packages"); 1075 final int prefixLength = "package:".length(); 1076 return Arrays.stream(commandResult.split("\\r?\\n")) 1077 .anyMatch(new Predicate<String>() { 1078 @Override 1079 public boolean test(String line) { 1080 return line.substring(prefixLength).equals(packageName); 1081 } 1082 }); 1083 } 1084 installSplits(String[] names)1085 void installSplits(String[] names) throws Exception { 1086 getUiAutomation().adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES); 1087 try { 1088 final PackageInstaller installer = 1089 mContext.getPackageManager().getPackageInstaller(); 1090 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 1091 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 1092 1093 final int sessionId = installer.createSession(params); 1094 PackageInstaller.Session session = installer.openSession(sessionId); 1095 1096 for (String name : names) { 1097 writeFileToSession(session, name, name); 1098 } 1099 1100 commitSession(session); 1101 } finally { 1102 getUiAutomation().dropShellPermissionIdentity(); 1103 } 1104 } 1105 1106 @SuppressWarnings("deprecation") commitSession(PackageInstaller.Session session)1107 void commitSession(PackageInstaller.Session session) throws Exception { 1108 final ResolvableFuture<Intent> result = ResolvableFuture.create(); 1109 1110 // Create a single-use broadcast receiver 1111 BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { 1112 @Override 1113 public void onReceive(Context context, Intent intent) { 1114 context.unregisterReceiver(this); 1115 result.set(intent); 1116 } 1117 }; 1118 1119 // Create a matching intent-filter and register the receiver 1120 final int resultId = result.hashCode(); 1121 final String action = "androidx.core.appdigest.COMMIT_COMPLETE." + resultId; 1122 IntentFilter intentFilter = new IntentFilter(); 1123 intentFilter.addAction(action); 1124 mContext.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED); 1125 1126 Intent intent = new Intent(action); 1127 intent.setPackage(mContext.getPackageName()); 1128 PendingIntent sender = PendingIntent.getBroadcast(mContext, resultId, intent, 1129 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT 1130 | PendingIntent.FLAG_MUTABLE); 1131 1132 session.commit(sender.getIntentSender()); 1133 1134 Intent commitResult = result.get(); 1135 final int status = commitResult.getIntExtra(PackageInstaller.EXTRA_STATUS, 1136 PackageInstaller.STATUS_FAILURE); 1137 assertEquals(commitResult.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + " OR " 1138 + commitResult.getExtras().get(Intent.EXTRA_INTENT), 1139 PackageInstaller.STATUS_SUCCESS, status); 1140 } 1141 } 1142 1143 @RequiresApi(31) 1144 static class InstallerApi31 extends InstallerApi29 { 1145 InstallerApi31(Context context) { 1146 super(context); 1147 } 1148 1149 private static final android.content.pm.Checksum[] TEST_FIXED_APK_DIGESTS = 1150 new android.content.pm.Checksum[]{ 1151 new android.content.pm.Checksum( 1152 android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, 1153 hexStringToBytes(TEST_FIXED_APK_V2_SHA256)), 1154 new android.content.pm.Checksum( 1155 android.content.pm.Checksum.TYPE_WHOLE_SHA256, 1156 hexStringToBytes(TEST_FIXED_APK_SHA256)), 1157 new android.content.pm.Checksum(android.content.pm.Checksum.TYPE_WHOLE_MD5, 1158 hexStringToBytes(TEST_FIXED_APK_MD5))}; 1159 1160 static android.content.pm.@NonNull Checksum readFromStream(@NonNull DataInputStream dis) 1161 throws IOException { 1162 final int type = dis.readInt(); 1163 1164 final byte[] valueBytes = new byte[dis.readInt()]; 1165 dis.read(valueBytes); 1166 return new android.content.pm.Checksum(type, valueBytes); 1167 } 1168 1169 private static void writeToStream(@NonNull DataOutputStream dos, 1170 android.content.pm.@NonNull Checksum checksum) throws IOException { 1171 dos.writeInt(checksum.getType()); 1172 1173 final byte[] valueBytes = checksum.getValue(); 1174 dos.writeInt(valueBytes.length); 1175 dos.write(valueBytes); 1176 } 1177 1178 static void checkStoredChecksums(String fileName) throws Exception { 1179 android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS; 1180 // Read checksums from file and confirm they are the same as hardcoded. 1181 ArrayList<android.content.pm.Checksum> storedChecksumsList = new ArrayList<>(); 1182 try (InputStream is = getResourceAsStream(fileName); 1183 DataInputStream dis = new DataInputStream(is)) { 1184 for (int i = 0; i < 100; ++i) { 1185 try { 1186 storedChecksumsList.add(readFromStream(dis)); 1187 } catch (EOFException e) { 1188 break; 1189 } 1190 } 1191 } 1192 final android.content.pm.Checksum[] storedChecksums = storedChecksumsList.toArray( 1193 new android.content.pm.Checksum[storedChecksumsList.size()]); 1194 1195 final String message = fileName + " needs to be updated: "; 1196 Assert.assertEquals(message, storedChecksums.length, checksums.length); 1197 for (int i = 0, size = storedChecksums.length; i < size; ++i) { 1198 Assert.assertEquals(message, storedChecksums[i].getType(), checksums[i].getType()); 1199 Assert.assertArrayEquals(message, storedChecksums[i].getValue(), 1200 checksums[i].getValue()); 1201 } 1202 } 1203 1204 static void checkWrittenChecksums(String fileName) throws Exception { 1205 android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS; 1206 // Write checksums and confirm that the file stays the same. 1207 try (ByteArrayOutputStream os = new ByteArrayOutputStream(); 1208 DataOutputStream dos = new DataOutputStream(os)) { 1209 for (android.content.pm.Checksum checksum : checksums) { 1210 writeToStream(dos, checksum); 1211 } 1212 final byte[] fileBytes = readAllBytes(getResourceAsStream(fileName)); 1213 final byte[] localBytes = os.toByteArray(); 1214 Assert.assertArrayEquals(fileBytes, localBytes); 1215 } 1216 } 1217 1218 static List<Certificate> convertSignaturesToCertificates(Signature[] signatures) 1219 throws Exception { 1220 final CertificateFactory cf = CertificateFactory.getInstance("X.509"); 1221 ArrayList<Certificate> certs = new ArrayList<>(signatures.length); 1222 for (Signature signature : signatures) { 1223 try (InputStream is = new ByteArrayInputStream(signature.toByteArray())) { 1224 final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); 1225 certs.add(cert); 1226 } 1227 } 1228 return certs; 1229 } 1230 1231 static byte[] readSignature() throws IOException { 1232 return readAllBytes(getResourceAsStream(TEST_FIXED_APK_DIGESTS_SIGNATURE)); 1233 } 1234 1235 static Certificate readCertificate() throws Exception { 1236 try (InputStream is = getResourceAsStream(TEST_CERTIFICATE)) { 1237 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 1238 return certFactory.generateCertificate(is); 1239 } 1240 } 1241 1242 @SuppressWarnings("deprecation") 1243 static Certificate getInstallerCertificate(Context context) throws Exception { 1244 PackageManager pm = context.getPackageManager(); 1245 PackageInfo installerPackageInfo = pm.getPackageInfo(INSTALLER_PACKAGE_NAME, 1246 GET_SIGNING_CERTIFICATES); 1247 final List<Certificate> signatures = convertSignaturesToCertificates( 1248 installerPackageInfo.signingInfo.getApkContentsSigners()); 1249 return signatures.get(0); 1250 } 1251 1252 void installApkWithChecksums(String apk, byte[] signature) throws Exception { 1253 android.content.pm.Checksum[] checksums = TEST_FIXED_APK_DIGESTS; 1254 getUiAutomation().adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES); 1255 try { 1256 final PackageInstaller installer = 1257 mContext.getPackageManager().getPackageInstaller(); 1258 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 1259 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 1260 1261 final int sessionId = installer.createSession(params); 1262 PackageInstaller.Session session = installer.openSession(sessionId); 1263 writeFileToSession(session, "file", apk); 1264 session.setChecksums("file", Arrays.asList(checksums), signature); 1265 1266 commitSession(session); 1267 } finally { 1268 getUiAutomation().dropShellPermissionIdentity(); 1269 } 1270 } 1271 } 1272 } 1273