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