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 android.appsecurity.cts; 18 19 import static org.junit.Assert.fail; 20 import static org.junit.Assume.assumeTrue; 21 22 import android.platform.test.annotations.AppModeFull; 23 24 import com.android.compatibility.common.util.CddTest; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.testtype.junit4.DeviceParameterizedRunner; 28 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; 29 30 import org.junit.After; 31 import org.junit.Before; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 import java.util.HashMap; 36 37 import junitparams.Parameters; 38 39 @RunWith(DeviceParameterizedRunner.class) 40 @AppModeFull 41 public final class ApkVerityInstallTest extends BaseAppSecurityTest { 42 43 private static final String PACKAGE_NAME = "android.appsecurity.cts.apkveritytestapp"; 44 45 private static final String BASE_APK = "CtsApkVerityTestAppPrebuilt.apk"; 46 private static final String BASE_APK_DM = "CtsApkVerityTestAppPrebuilt.dm"; 47 private static final String SPLIT_APK = "CtsApkVerityTestAppSplitPrebuilt.apk"; 48 private static final String SPLIT_APK_DM = "CtsApkVerityTestAppSplitPrebuilt.dm"; 49 private static final String BAD_BASE_APK = "CtsApkVerityTestApp2Prebuilt.apk"; 50 private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2Prebuilt.dm"; 51 private static final String FSV_SIG_SUFFIX = ".fsv_sig"; 52 private static final String ID_SIG_SUFFIX = ".idsig"; 53 private static final String APK_VERITY_STANDARD_MODE = "2"; 54 55 private static final boolean INCREMENTAL = true; 56 private static final boolean NON_INCREMENTAL = false; 57 58 private static final boolean SUPPORTED = true; 59 private static final boolean UNSUPPORTED = false; 60 61 private static final HashMap<String, String> ORIGINAL_TO_INSTALL_NAME = new HashMap<>() {{ 62 put(BASE_APK, "base.apk"); 63 put(BASE_APK_DM, "base.dm"); 64 put(SPLIT_APK, "split_feature_x.apk"); 65 put(SPLIT_APK_DM, "split_feature_x.dm"); 66 }}; 67 68 private boolean mDmRequireFsVerity; 69 installSingle()70 private static final Object[] installSingle() { 71 // Non-Incremental and Incremental. 72 return new Boolean[][]{{NON_INCREMENTAL}, {INCREMENTAL}}; 73 } 74 installAndUpdate()75 private static final Object[] installAndUpdate() { 76 // Non-Incremental -> Non-Incremental: supported 77 // Incremental -> Non-Incremental: supported 78 // Incremental -> Incremental: supported 79 // Non-Incremental -> Incremental: unsupported 80 return new Boolean[][]{ 81 {NON_INCREMENTAL, NON_INCREMENTAL, SUPPORTED}, 82 {INCREMENTAL, NON_INCREMENTAL, SUPPORTED}, 83 {INCREMENTAL, INCREMENTAL, SUPPORTED}, 84 {NON_INCREMENTAL, INCREMENTAL, UNSUPPORTED} 85 }; 86 } 87 88 private int mLaunchApiLevel; 89 @Before setUp()90 public void setUp() throws DeviceNotAvailableException { 91 ITestDevice device = getDevice(); 92 String apkVerityMode = device.getProperty("ro.apk_verity.mode"); 93 mLaunchApiLevel = device.getLaunchApiLevel(); 94 assumeTrue(mLaunchApiLevel >= 30 || APK_VERITY_STANDARD_MODE.equals(apkVerityMode)); 95 mDmRequireFsVerity = "true".equals(device.getProperty("pm.dexopt.dm.require_fsverity")); 96 assumeSecurityModelCompat(); 97 } 98 99 @After tearDown()100 public void tearDown() throws DeviceNotAvailableException { 101 getDevice().uninstallPackage(PACKAGE_NAME); 102 } 103 104 @CddTest(requirement = "9.10/C-0-3") 105 @Test 106 @Parameters(method = "installSingle") testInstallBase(boolean incremental)107 public void testInstallBase(boolean incremental) throws Exception { 108 assumePreconditions(incremental); 109 new InstallMultiple(incremental) 110 .addFile(BASE_APK) 111 .addFile(BASE_APK + FSV_SIG_SUFFIX) 112 .run(); 113 verifyFsverityInstall(incremental, BASE_APK); 114 } 115 116 @CddTest(requirement = "9.10/C-0-3") 117 @Test 118 @Parameters(method = "installSingle") testInstallBaseWithWrongSignature(boolean incremental)119 public void testInstallBaseWithWrongSignature(boolean incremental) throws Exception { 120 assumePreconditions(incremental); 121 InstallMultiple install = new InstallMultiple(incremental) 122 .addFile(BAD_BASE_APK) 123 .addFile(BAD_BASE_APK + FSV_SIG_SUFFIX); 124 125 // S with IncFsV1 silently skips fs-verity signatures. 126 boolean expectingSuccess = incremental && !isIncrementalDeliveryV2Feature(); 127 install.run(expectingSuccess); 128 } 129 130 @CddTest(requirement = "9.10/C-0-3,C-0-5") 131 @Test 132 @Parameters(method = "installSingle") testInstallBaseWithSplit(boolean incremental)133 public void testInstallBaseWithSplit(boolean incremental) throws Exception { 134 assumePreconditions(incremental); 135 new InstallMultiple(incremental) 136 .addFile(BASE_APK) 137 .addFile(BASE_APK + FSV_SIG_SUFFIX) 138 .addFile(SPLIT_APK) 139 .addFile(SPLIT_APK + FSV_SIG_SUFFIX) 140 .run(); 141 verifyFsverityInstall(incremental, BASE_APK, SPLIT_APK); 142 } 143 144 @CddTest(requirement = "9.10/C-0-3,C-0-5") 145 @Test 146 @Parameters(method = "installSingle") testInstallBaseWithDm(boolean incremental)147 public void testInstallBaseWithDm(boolean incremental) throws Exception { 148 assumePreconditions(incremental); 149 new InstallMultiple(incremental) 150 .addFile(BASE_APK) 151 .addFile(BASE_APK + FSV_SIG_SUFFIX) 152 .addFile(BASE_APK_DM) 153 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 154 .run(); 155 verifyFsverityInstall(incremental, BASE_APK, BASE_APK_DM); 156 } 157 158 @CddTest(requirement = "9.10/C-0-3,C-0-5") 159 @Test 160 @Parameters(method = "installSingle") testInstallEverything(boolean incremental)161 public void testInstallEverything(boolean incremental) throws Exception { 162 assumePreconditions(incremental); 163 new InstallMultiple(incremental) 164 .addFile(BASE_APK) 165 .addFile(BASE_APK + FSV_SIG_SUFFIX) 166 .addFile(BASE_APK_DM) 167 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 168 .addFile(SPLIT_APK) 169 .addFile(SPLIT_APK + FSV_SIG_SUFFIX) 170 .addFile(SPLIT_APK_DM) 171 .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX) 172 .run(); 173 verifyFsverityInstall(incremental, BASE_APK, BASE_APK_DM, SPLIT_APK, SPLIT_APK_DM); 174 } 175 176 @CddTest(requirement = "9.10/C-0-3,C-0-5") 177 @Test 178 @Parameters(method = "installAndUpdate") testInstallSplitOnly(boolean installIncremental, boolean updateIncremental, boolean isSupported)179 public void testInstallSplitOnly(boolean installIncremental, boolean updateIncremental, 180 boolean isSupported) throws Exception { 181 assumePreconditions(installIncremental || updateIncremental); 182 new InstallMultiple(installIncremental) 183 .addFile(BASE_APK) 184 .addFile(BASE_APK + FSV_SIG_SUFFIX) 185 .run(); 186 verifyFsverityInstall(installIncremental, BASE_APK); 187 188 new InstallMultiple(updateIncremental) 189 .inheritFrom(PACKAGE_NAME) 190 .addFile(SPLIT_APK) 191 .addFile(SPLIT_APK + FSV_SIG_SUFFIX) 192 .run(isSupported); 193 if (isSupported) { 194 verifyFsverityInstall(updateIncremental, BASE_APK, SPLIT_APK); 195 } 196 } 197 198 @CddTest(requirement = "9.10/C-0-3,C-0-5") 199 @Test 200 @Parameters(method = "installAndUpdate") testInstallSplitOnlyMissingSignature(boolean installIncremental, boolean updateIncremental, boolean isSupported)201 public void testInstallSplitOnlyMissingSignature(boolean installIncremental, 202 boolean updateIncremental, boolean isSupported) throws Exception { 203 assumePreconditions(installIncremental || updateIncremental); 204 new InstallMultiple(installIncremental) 205 .addFile(BASE_APK) 206 .addFile(BASE_APK + FSV_SIG_SUFFIX) 207 .run(); 208 verifyFsverityInstall(installIncremental, BASE_APK); 209 210 InstallMultiple install = new InstallMultiple(updateIncremental) 211 .inheritFrom(PACKAGE_NAME) 212 .addFile(SPLIT_APK); 213 214 // S with IncFsV1 silently skips fs-verity signatures. 215 boolean expectingSuccess = 216 isSupported && installIncremental && !isIncrementalDeliveryV2Feature(); 217 install.run(expectingSuccess); 218 } 219 220 @CddTest(requirement = "9.10/C-0-3,C-0-5") 221 @Test 222 @Parameters(method = "installAndUpdate") testInstallSplitOnlyWithoutBaseSignature(boolean installIncremental, boolean updateIncremental, boolean isSupported)223 public void testInstallSplitOnlyWithoutBaseSignature(boolean installIncremental, 224 boolean updateIncremental, boolean isSupported) throws Exception { 225 assumePreconditions(installIncremental || updateIncremental); 226 new InstallMultiple(installIncremental) 227 .addFile(BASE_APK) 228 .run(); 229 230 new InstallMultiple(updateIncremental) 231 .inheritFrom(PACKAGE_NAME) 232 .addFile(SPLIT_APK) 233 .addFile(SPLIT_APK + FSV_SIG_SUFFIX) 234 .run(isSupported); 235 if (isSupported) { 236 verifyFsverityInstall(updateIncremental, SPLIT_APK); 237 } 238 } 239 240 @CddTest(requirement = "9.10/C-0-3,C-0-5") 241 @Test 242 @Parameters(method = "installAndUpdate") testInstallSplitAndSignatureForBase(boolean installIncremental, boolean updateIncremental, boolean isSupported)243 public void testInstallSplitAndSignatureForBase(boolean installIncremental, 244 boolean updateIncremental, boolean isSupported) throws Exception { 245 assumePreconditions(installIncremental || updateIncremental); 246 new InstallMultiple(installIncremental) 247 .addFile(BASE_APK) 248 .run(); 249 250 new InstallMultiple(updateIncremental) 251 .inheritFrom(PACKAGE_NAME) 252 .addFile(BASE_APK) 253 .addFile(BASE_APK + FSV_SIG_SUFFIX) 254 .addFile(SPLIT_APK) 255 .addFile(SPLIT_APK + FSV_SIG_SUFFIX) 256 .run(isSupported); 257 if (isSupported) { 258 verifyFsverityInstall(updateIncremental, BASE_APK); 259 } 260 } 261 262 @CddTest(requirement = "9.10/C-0-3,C-0-5") 263 @Test 264 @Parameters(method = "installAndUpdate") testUpdateBaseWithSignature(boolean installIncremental, boolean updateIncremental, boolean isSupported)265 public void testUpdateBaseWithSignature(boolean installIncremental, boolean updateIncremental, 266 boolean isSupported) throws Exception { 267 assumePreconditions(installIncremental || updateIncremental); 268 new InstallMultiple(installIncremental) 269 .addFile(BASE_APK) 270 .addFile(BASE_APK + FSV_SIG_SUFFIX) 271 .run(); 272 verifyFsverityInstall(installIncremental, BASE_APK); 273 274 new InstallMultiple(updateIncremental) 275 .inheritFrom(PACKAGE_NAME) 276 .addFile(BASE_APK) 277 .addFile(BASE_APK + FSV_SIG_SUFFIX) 278 .run(isSupported); 279 verifyFsverityInstall(updateIncremental, BASE_APK); 280 } 281 282 @CddTest(requirement = "9.10/C-0-3,C-0-5") 283 @Test 284 @Parameters(method = "installSingle") testInstallBaseWithFsvSigAndSplitWithout(boolean incremental)285 public void testInstallBaseWithFsvSigAndSplitWithout(boolean incremental) throws Exception { 286 assumePreconditions(incremental); 287 new InstallMultiple(incremental) 288 .addFile(BASE_APK) 289 .addFile(BASE_APK + FSV_SIG_SUFFIX) 290 .addFile(BASE_APK_DM) 291 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 292 .addFile(SPLIT_APK) 293 .addFile(SPLIT_APK_DM) 294 .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX) 295 .runExpectingFailure(); 296 } 297 298 @CddTest(requirement = "9.10/C-0-3,C-0-5") 299 @Test 300 @Parameters(method = "installSingle") testInstallDmWithFsvSig(boolean incremental)301 public void testInstallDmWithFsvSig(boolean incremental) throws Exception { 302 assumePreconditions(incremental); 303 new InstallMultiple(incremental) 304 .addFile(BASE_APK) 305 .addFile(BASE_APK_DM) 306 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 307 .addFile(SPLIT_APK) 308 .addFile(SPLIT_APK_DM) 309 .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX) 310 .run(); 311 verifyFsverityInstall(incremental, BASE_APK_DM, SPLIT_APK_DM); 312 } 313 314 @CddTest(requirement = "9.10/C-0-3,C-0-5") 315 @Test 316 @Parameters(method = "installSingle") testInstallDmWithMissingFsvSig(boolean incremental)317 public void testInstallDmWithMissingFsvSig(boolean incremental) throws Exception { 318 assumePreconditions(incremental); 319 InstallMultiple installer = new InstallMultiple(incremental) 320 .addFile(BASE_APK) 321 .addFile(BASE_APK_DM) 322 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 323 .addFile(SPLIT_APK) 324 .addFile(SPLIT_APK_DM); 325 if (mDmRequireFsVerity) { 326 installer.runExpectingFailure(); 327 } else { 328 installer.run(); 329 verifyFsverityInstall(incremental, BASE_APK_DM); 330 } 331 } 332 333 @CddTest(requirement = "9.10/C-0-3,C-0-5") 334 @Test 335 @Parameters(method = "installSingle") testInstallSplitWithFsvSigAndBaseWithout(boolean incremental)336 public void testInstallSplitWithFsvSigAndBaseWithout(boolean incremental) throws Exception { 337 assumePreconditions(incremental); 338 InstallMultiple installer = new InstallMultiple(incremental) 339 .addFile(BASE_APK) 340 .addFile(BASE_APK_DM) 341 .addFile(BASE_APK_DM + FSV_SIG_SUFFIX) 342 .addFile(SPLIT_APK) 343 .addFile(SPLIT_APK_DM) 344 .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX); 345 if (mDmRequireFsVerity) { 346 installer.runExpectingFailure(); 347 } else { 348 installer.run(); 349 verifyFsverityInstall(incremental, BASE_APK_DM, SPLIT_APK_DM); 350 } 351 } 352 353 @CddTest(requirement = "9.10/C-0-3,C-0-5") 354 @Test 355 @Parameters(method = "installAndUpdate") testInstallBaseWithFsvSigThenSplitWithout(boolean installIncremental, boolean updateIncremental, boolean isSupported)356 public void testInstallBaseWithFsvSigThenSplitWithout(boolean installIncremental, 357 boolean updateIncremental, boolean isSupported) throws Exception { 358 assumePreconditions(installIncremental || updateIncremental); 359 new InstallMultiple(installIncremental) 360 .addFile(BASE_APK) 361 .addFile(BASE_APK + FSV_SIG_SUFFIX) 362 .run(); 363 verifyFsverityInstall(installIncremental, BASE_APK); 364 365 new InstallMultiple(updateIncremental) 366 .addFile(SPLIT_APK) 367 .runExpectingFailure(); 368 } 369 370 @Test testInstallBaseIncrementally()371 public void testInstallBaseIncrementally() throws Exception { 372 assumeTrue(hasIncrementalDeliveryFeature()); 373 new InstallMultiple(/*incremental=*/true) 374 .addFile(BASE_APK) 375 .run(); 376 } 377 378 @Test testInstallBaseIncrementallyWithFsvSig()379 public void testInstallBaseIncrementallyWithFsvSig() throws Exception { 380 assumeTrue(isIncrementalDeliveryV2Feature()); 381 new InstallMultiple(/*incremental=*/true) 382 .addFile(BASE_APK) 383 .addFile(BASE_APK + FSV_SIG_SUFFIX) 384 .run(); 385 verifyFsverityInstall(true, BASE_APK); 386 } 387 388 @Test testInstallBaseIncrementallyWithFsvSigAndIdSig()389 public void testInstallBaseIncrementallyWithFsvSigAndIdSig() throws Exception { 390 assumeTrue(isIncrementalDeliveryV2Feature()); 391 new InstallMultiple(/*incremental=*/true) 392 .addFile(BASE_APK) 393 .pushFile(BASE_APK + ID_SIG_SUFFIX) 394 .addFile(BASE_APK + FSV_SIG_SUFFIX) 395 .run(); 396 verifyFsverityInstall(true, BASE_APK); 397 } 398 399 @Test testInstallBaseIncrementallyWithIdSigAndWrongFsvSig()400 public void testInstallBaseIncrementallyWithIdSigAndWrongFsvSig() throws Exception { 401 assumeTrue(isIncrementalDeliveryV2Feature()); 402 new InstallMultiple(/*incremental=*/true) 403 .addFile(BASE_APK) 404 .pushFile(BASE_APK + ID_SIG_SUFFIX) 405 .renameAndAddFile(BAD_BASE_APK + FSV_SIG_SUFFIX, BASE_APK + FSV_SIG_SUFFIX) 406 .runExpectingFailure(); 407 } 408 409 @Test testInstallBaseIncrementallyWithWrongIdSigAndFsvSig()410 public void testInstallBaseIncrementallyWithWrongIdSigAndFsvSig() throws Exception { 411 assumeTrue(isIncrementalDeliveryV2Feature()); 412 new InstallMultiple(/*incremental=*/true) 413 .addFile(BASE_APK) 414 .renameAndPushFile(BAD_BASE_APK + ID_SIG_SUFFIX, BASE_APK + ID_SIG_SUFFIX) 415 .addFile(BASE_APK + FSV_SIG_SUFFIX) 416 .runExpectingFailure(); 417 } 418 assumePreconditions(boolean requiresIncremental)419 private void assumePreconditions(boolean requiresIncremental) throws Exception { 420 if (requiresIncremental) { 421 assumeTrue(hasIncrementalDeliveryFeature()); 422 } 423 } 424 hasIncrementalDeliveryFeature()425 private boolean hasIncrementalDeliveryFeature() throws Exception { 426 return "true\n".equals(getDevice().executeShellCommand( 427 "pm has-feature android.software.incremental_delivery")); 428 } 429 isIncrementalDeliveryV2Feature()430 private boolean isIncrementalDeliveryV2Feature() throws Exception { 431 return "true\n".equals(getDevice().executeShellCommand( 432 "pm has-feature android.software.incremental_delivery 2")); 433 } 434 assumeSecurityModelCompat()435 private void assumeSecurityModelCompat() throws DeviceNotAvailableException { 436 // This feature name check only applies to devices that first shipped with 437 // SC or later. 438 if (mLaunchApiLevel >= 31) { 439 assumeTrue("Skipping test: FEATURE_SECURITY_MODEL_COMPATIBLE missing.", 440 getDevice().hasFeature("feature:android.hardware.security.model.compatible")); 441 } 442 } 443 verifyFsverityInstall(boolean incremental, String... files)444 void verifyFsverityInstall(boolean incremental, String... files) throws Exception { 445 if (incremental && !isIncrementalDeliveryV2Feature()) { 446 return; 447 } 448 449 DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME); 450 options.setTestClassName(PACKAGE_NAME + ".InstalledFilesCheck"); 451 options.setTestMethodName("testFilesHaveFsverity"); 452 options.addInstrumentationArg("Number", 453 Integer.toString(files.length)); 454 for (int i = 0; i < files.length; ++i) { 455 String installName = ORIGINAL_TO_INSTALL_NAME.get(files[i]); 456 if (installName == null) { 457 fail("Install name is not defined for " + files[i]); 458 } 459 options.addInstrumentationArg("File" + i, installName); 460 } 461 runDeviceTests(options); 462 } 463 464 private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> { InstallMultiple(boolean incremental)465 InstallMultiple(boolean incremental) throws Exception { 466 super(getDevice(), getBuild(), getAbi()); 467 if (incremental) { 468 useIncremental(); 469 } 470 } 471 472 @Override deriveRemoteName(String originalName, int index)473 protected String deriveRemoteName(String originalName, int index) { 474 return originalName; 475 } 476 } 477 } 478