• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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