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