Merge "CTS: fsv_sig install tests" into rvc-dev
diff --git a/hostsidetests/appsecurity/Android.bp b/hostsidetests/appsecurity/Android.bp
index 0c069d0..3cc61fe 100644
--- a/hostsidetests/appsecurity/Android.bp
+++ b/hostsidetests/appsecurity/Android.bp
@@ -50,6 +50,18 @@
"CtsCorruptApkTests_Unaligned_R",
],
+ data: [
+ ":CtsApkVerityTestApp",
+ ":CtsApkVerityTestAppFsvSig",
+ ":CtsApkVerityTestAppDm",
+ ":CtsApkVerityTestAppDmFsvSig",
+ ":CtsApkVerityTestAppSplit",
+ ":CtsApkVerityTestAppSplitFsvSig",
+ ":CtsApkVerityTestAppSplitDm",
+ ":CtsApkVerityTestAppSplitDmFsvSig",
+ ":CtsApkVerityTestApp2",
+ ":CtsApkVerityTestApp2FsvSig",
+ ],
}
filegroup {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
new file mode 100644
index 0000000..a21cee3
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.AppModeFull;
+import com.android.compatibility.common.util.CddTest;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public final class ApkVerityInstallTest extends BaseAppSecurityTest {
+
+ private static final String PACKAGE_NAME = "android.appsecurity.cts.apkveritytestapp";
+
+ private static final String BASE_APK = "CtsApkVerityTestApp.apk";
+ private static final String BASE_APK_DM = "CtsApkVerityTestApp.dm";
+ private static final String SPLIT_APK = "CtsApkVerityTestAppSplit.apk";
+ private static final String SPLIT_APK_DM = "CtsApkVerityTestAppSplit.dm";
+ private static final String BAD_BASE_APK = "CtsApkVerityTestApp2.apk";
+ private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2.dm";
+ private static final String FSV_SIG_SUFFIX = ".fsv_sig";
+ private static final String APK_VERITY_STANDARD_MODE = "2";
+
+ private static final HashMap<String, String> ORIGINAL_TO_INSTALL_NAME = new HashMap<>() {{
+ put(BASE_APK, "base.apk");
+ put(BASE_APK_DM, "base.dm");
+ put(SPLIT_APK, "split_feature_x.apk");
+ put(SPLIT_APK_DM, "split_feature_x.dm");
+ }};
+
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException {
+ ITestDevice device = getDevice();
+ String apkVerityMode = device.getProperty("ro.apk_verity.mode");
+ assumeTrue(device.getLaunchApiLevel() >= 30
+ || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+ }
+
+ @CddTest(requirement="9.10/C-0-3")
+ @Test
+ public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK);
+ }
+
+ @CddTest(requirement="9.10/C-0-3")
+ @Test
+ public void testInstallBaseWithWrongSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BAD_BASE_APK)
+ .addFile(BAD_BASE_APK + FSV_SIG_SUFFIX)
+ .runExpectingFailure();
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallBaseWithSplit()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK, SPLIT_APK);
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .addFile(BASE_APK_DM)
+ .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK, BASE_APK_DM);
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .addFile(BASE_APK_DM)
+ .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK_DM)
+ .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK, BASE_APK_DM, SPLIT_APK, SPLIT_APK_DM);
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallSplitOnly()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK);
+
+ new InstallMultiple()
+ .inheritFrom(PACKAGE_NAME)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK, SPLIT_APK);
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallSplitOnlyMissingSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK);
+
+ new InstallMultiple()
+ .inheritFrom(PACKAGE_NAME)
+ .addFile(SPLIT_APK)
+ .runExpectingFailure();
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallSplitOnlyWithoutBaseSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .run();
+
+ new InstallMultiple()
+ .inheritFrom(PACKAGE_NAME)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(SPLIT_APK);
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallOnlyBaseHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .addFile(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallOnlyDmHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK_DM)
+ .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallOnlySplitHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @CddTest(requirement="9.10/C-0-3,C-1-1")
+ @Test
+ public void testInstallBaseWithFsvSigThenSplitWithout()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(BASE_APK);
+
+ new InstallMultiple()
+ .addFile(SPLIT_APK)
+ .runExpectingFailure();
+ }
+
+ void verifyFsverityInstall(String... files) throws DeviceNotAvailableException {
+ DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME);
+ options.setTestClassName(PACKAGE_NAME + ".InstalledFilesCheck");
+ options.setTestMethodName("testFilesHaveFsverity");
+ options.addInstrumentationArg("Number",
+ Integer.toString(files.length));
+ for (int i = 0; i < files.length; ++i) {
+ String installName = ORIGINAL_TO_INSTALL_NAME.get(files[i]);
+ if (installName == null) {
+ fail("Install name is not defined for " + files[i]);
+ }
+ options.addInstrumentationArg("File" + i, installName);
+ }
+ runDeviceTests(options);
+ }
+
+ private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+ InstallMultiple() {
+ super(getDevice(), getBuild(), getAbi());
+ }
+
+ @Override
+ protected String deriveRemoteName(String originalName, int index) {
+ return originalName;
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index 8acda6d..23f1840 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -46,7 +46,7 @@
private final IAbi mAbi;
private final List<String> mArgs = new ArrayList<>();
- private final List<File> mApks = new ArrayList<>();
+ private final List<File> mFiles = new ArrayList<>();
private final List<String> mSplits = new ArrayList<>();
private boolean mUseNaturalAbi;
@@ -63,8 +63,12 @@
}
T addApk(String apk) throws FileNotFoundException {
+ return addFile(apk);
+ }
+
+ T addFile(String file) throws FileNotFoundException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
- mApks.add(buildHelper.getTestFile(apk));
+ mFiles.add(buildHelper.getTestFile(file));
return (T) this;
}
@@ -114,6 +118,10 @@
return (T) this;
}
+ protected String deriveRemoteName(String originalName, int index) {
+ return index + "_" + originalName;
+ }
+
void run() throws DeviceNotAvailableException {
run(true, null);
}
@@ -157,17 +165,18 @@
// Push our files into session. Ideally we'd use stdin streaming,
// but ddmlib doesn't support it yet.
- for (int i = 0; i < mApks.size(); i++) {
- final File apk = mApks.get(i);
- final String remotePath = "/data/local/tmp/" + i + "_" + apk.getName();
- if (!device.pushFile(apk, remotePath)) {
- throw new IllegalStateException("Failed to push " + apk);
+ for (int i = 0; i < mFiles.size(); i++) {
+ final File file = mFiles.get(i);
+ final String remoteName = deriveRemoteName(file.getName(), i);
+ final String remotePath = "/data/local/tmp/" + remoteName;
+ if (!device.pushFile(file, remotePath)) {
+ throw new IllegalStateException("Failed to push " + file);
}
cmd.setLength(0);
cmd.append("pm install-write");
cmd.append(' ').append(sessionId);
- cmd.append(' ').append(i + "_" + apk.getName());
+ cmd.append(' ').append(remoteName);
cmd.append(' ').append(remotePath);
result = device.executeShellCommand(cmd.toString());
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp
new file mode 100644
index 0000000..aca31e7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/Android.bp
@@ -0,0 +1,69 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsApkVerityTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ jni_libs: [
+ "libCtsApkVerityTestAppJni",
+ ],
+ compile_multilib: "both",
+ libs: [
+ "junit",
+ ],
+ static_libs: [
+ "androidx.test.runner",
+ "ctstestrunner-axt",
+ ],
+ dex_preopt: {
+ enabled: false,
+ },
+ use_embedded_native_libs: true,
+ sdk_version: "current",
+ certificate: ":cts-testkey1",
+}
+
+android_test_helper_app {
+ name: "CtsApkVerityTestAppSplit",
+ manifest: "feature_split/AndroidManifest.xml",
+ srcs: ["feature_split/src/**/*.java"],
+ aaptflags: [
+ "--custom-package android.appsecurity.cts.apkveritytestapp.feature_x",
+ "--package-id 0x80",
+ ],
+ dex_preopt: {
+ enabled: false,
+ },
+ sdk_version: "current",
+ certificate: ":cts-testkey1",
+}
+
+cc_library_shared {
+ name: "libCtsApkVerityTestAppJni",
+ srcs: ["jni/**/*.cpp"],
+ shared_libs: [
+ "libnativehelper_compat_libc++",
+ "liblog",
+ ],
+ static_libs: [
+ "libbase_ndk",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ stl: "c++_static",
+ sdk_version: "current",
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..5331c28
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appsecurity.cts.apkveritytestapp">
+ <application>
+ <activity android:name=".DummyActivity"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.appsecurity.cts.apkveritytestapp"
+ android:label="CTS test app of apk verity">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>/>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml
new file mode 100644
index 0000000..bec126d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appsecurity.cts.apkveritytestapp"
+ android:isFeatureSplit="true"
+ split="feature_x">
+ <application>
+ <activity android:name=".feature_x.DummyActivity"/>
+ </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java
new file mode 100644
index 0000000..ab0faf7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/feature_split/src/android/appsecurity.cts.apkveritytestapp/feature_x/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts.apkveritytestapp.feature_x;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp
new file mode 100644
index 0000000..abc04d9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/jni/android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ApkVerityTestApp"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <android/log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_android_appsecurity_cts_apkveritytestapp_InstalledFilesCheck_hasFsverityNative(
+ JNIEnv *env, jobject /*thiz*/, jstring filePath) {
+ ScopedUtfChars path(env, filePath);
+
+ struct statx out = {};
+ if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+ ALOGE("statx failed at %s", path.c_str());
+ return JNI_FALSE;
+ }
+
+ // Sanity check.
+ if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+ ALOGE("STATX_ATTR_VERITY not supported by kernel");
+ return JNI_FALSE;
+ }
+
+ return (out.stx_attributes & STATX_ATTR_VERITY) != 0 ? JNI_TRUE : JNI_FALSE;
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java
new file mode 100644
index 0000000..ff4f298
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts.apkveritytestapp;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java
new file mode 100644
index 0000000..c69f20b
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/src/android/appsecurity/cts/apkveritytestapp/InstalledFilesCheck.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts.apkveritytestapp;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+
+public class InstalledFilesCheck {
+ private static final String TAG = InstalledFilesCheck.class.getSimpleName();
+ private static final String PACKAGE_NAME = InstalledFilesCheck.class.getPackage().getName();
+
+ static {
+ System.loadLibrary("CtsApkVerityTestAppJni");
+ }
+
+ private static native boolean hasFsverityNative(@NonNull String path);
+
+ @Test
+ public void testFilesHaveFsverity() {
+ for (String path : getInterestedFiles()) {
+ assertTrue("Expect file installed: " + path, new File(path).exists());
+ assertTrue("Expect to have fs-verity: " + path, hasFsverityNative(path));
+ }
+ }
+
+ private ArrayList<String> getInterestedFiles() {
+ Context context = InstrumentationRegistry.getContext();
+ Path dirname = Paths.get(context.getApplicationInfo().sourceDir).getParent();
+ ArrayList<String> output = new ArrayList<>();
+
+ android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+ assertNotEquals(testArgs, null);
+ int number = Integer.valueOf(testArgs.getString("Number"));
+ assertTrue(number > 0);
+
+ for (int i = 0; i < number; ++i) {
+ String basename = testArgs.getString("File" + Integer.toString(i));
+ String filename = dirname.resolve(basename).toString();
+ Log.d(TAG, "Adding interested file " + filename);
+ output.add(filename);
+ }
+ return output;
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
new file mode 100644
index 0000000..3fc6f86
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/Android.bp
@@ -0,0 +1,89 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+filegroup {
+ name: "CtsApkVerityTestAppDm",
+ srcs: ["CtsApkVerityTestApp.dm"],
+}
+
+filegroup {
+ name: "CtsApkVerityTestAppSplitDm",
+ srcs: ["CtsApkVerityTestAppSplit.dm"],
+}
+
+filegroup {
+ name: "fsverity-debug-key",
+ srcs: ["fsverity-debug-key.pem"],
+}
+
+filegroup {
+ name: "fsverity-debug-cert",
+ srcs: ["fsverity-debug.x509.pem"],
+}
+
+genrule_defaults {
+ name: "cts_apk_verity_sig_gen_default",
+ tools: ["fsverity"],
+ tool_files: [
+ ":fsverity-debug-key",
+ ":fsverity-debug-cert",
+ ],
+ cmd: "$(location fsverity) sign $(in) $(out) " +
+ "--key=$(location :fsverity-debug-key) " +
+ "--cert=$(location :fsverity-debug-cert) " +
+ "> /dev/null",
+}
+
+genrule {
+ name: "CtsApkVerityTestAppFsvSig",
+ defaults: ["cts_apk_verity_sig_gen_default"],
+ srcs: [":CtsApkVerityTestApp"],
+ out: ["CtsApkVerityTestApp.apk.fsv_sig"],
+}
+
+genrule {
+ name: "CtsApkVerityTestAppDmFsvSig",
+ defaults: ["cts_apk_verity_sig_gen_default"],
+ srcs: [":CtsApkVerityTestAppDm"],
+ out: ["CtsApkVerityTestApp.dm.fsv_sig"],
+}
+
+genrule {
+ name: "CtsApkVerityTestAppSplitFsvSig",
+ defaults: ["cts_apk_verity_sig_gen_default"],
+ srcs: [":CtsApkVerityTestAppSplit"],
+ out: ["CtsApkVerityTestAppSplit.apk.fsv_sig"],
+}
+
+genrule {
+ name: "CtsApkVerityTestAppSplitDmFsvSig",
+ defaults: ["cts_apk_verity_sig_gen_default"],
+ srcs: [":CtsApkVerityTestAppSplitDm"],
+ out: ["CtsApkVerityTestAppSplit.dm.fsv_sig"],
+}
+
+// Create another app with mismatched .fsv_sig
+genrule {
+ name: "CtsApkVerityTestApp2",
+ srcs: [":CtsApkVerityTestApp"],
+ out: ["CtsApkVerityTestApp2.apk"],
+ cmd: "cp -f $(in) $(out)"
+}
+
+genrule {
+ name: "CtsApkVerityTestApp2FsvSig",
+ srcs: [":CtsApkVerityTestAppSplitFsvSig"],
+ out: ["CtsApkVerityTestApp2.apk.fsv_sig"],
+ cmd: "cp -f $(in) $(out)"
+}
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm
new file mode 100644
index 0000000..e53a861
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestApp.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm
new file mode 100644
index 0000000..75396f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/CtsApkVerityTestAppSplit.dm
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md
new file mode 100644
index 0000000..a0be4f9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/README.md
@@ -0,0 +1,7 @@
+How to generate dex metadata (.dm) with profile for testing
+===========================================================
+```
+adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
+adb pull /data/local/tmp/primary.prof
+zip foo.dm primary.prof
+```
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem
new file mode 100644
index 0000000..a3b4019
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug-key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+Ay3LwFyjYVrcHTlmGYYR6HVN6my098ymjg==
+-----END PRIVATE KEY-----
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der
new file mode 100644
index 0000000..fbee6f1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.der
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem
new file mode 100644
index 0000000..d4fbea5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestApp/testdata/fsverity-debug.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIUMwFudVZlac6hU7ClbIJXRUiXOIUwDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMB4XDTIwMDQwMzIxMzEwM1oXDTIwMDUwMzIxMzEwM1owgZQxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp
+biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD
+VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4DLcnCcpDlhm7xq6TCOQ
+XNw7CAItEiLaaWkIXA5yOSLfPSBl77OWFt1thEXaZCSKmyVYy2gnSJ7dDYSworZ3
+M/Nrb47J73/yM2oO5FUrt8j9Vx/zwzJaG7nhtBZIZHEf4Fg6/Yw50+5ny7kjllCr
+5qcFd4v7B7BJs7DQKOiXeWYe4GfEn7We7hfVENcEU0x02A3gwxMM8ZLi1qdHqdOo
+FvXrdbRVLQ/lGEjdsngjczoCv2nZeC49IykksNiiM4atmMMWaik8d/Sd5R9z/s2k
+bou8prEpEuP9PyXgi7ASGrtdvMBw4PY2NKVqgXlYhVuQcCreYLi7Dwu8A/VKOJbX
+XayXrbQ8FjOdEHTz3tVpctv7Js1E/W7QDxp3isnG3XuR4bfLo3NjTmEXcSNGrrAv
+vawvUbqCMr5VJEzKnaGS0A9vMg1aEmqTwPTEgmUVRmahyAVn4uVgQDGIqc4XNTvL
+3NZ/C8AcNlHQxbHjg6se8tTQe+U05aMTuL2eS8jfxLFxonD7SqpCZckBqXAsYLXl
+efLw9GpNy3bOFZavFVC4wZqliEW6HfPJW6ub5qYVkwS6Wp9IWYJRWzE5M3Kg4Ez/
+56O/nzSNu0MEWz5eDcimPZawnbmjxBi7D3lGIidgSFJfKc4SJaz5wR0fybKX+3Ys
+hLDfNNqhbTRh4ZD/Awa5ra8CAwEAAaNTMFEwHQYDVR0OBBYEFFJpY9e3fqe+38d5
+M8XP2u4zCiGWMB8GA1UdIwQYMBaAFFJpY9e3fqe+38d5M8XP2u4zCiGWMA8GA1Ud
+EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBADcNIAiaUtZbOPLZLgv8sUHk
+QdBuKmNrQKvS91HSWIkQ0pK/5jcemAkTI3geF+cO3lBCiJRYWyF3d09onHvJXtid
+ASoT89W+BxJSIiPXOhcpLST7RqLVqgiyg0nATCzHa4euTKHFwTNll3pIG06QTcPg
+mXxTG5wJqztAq3E3chZewrWuN7leVvaWE2jeI3x3IpaH+Gyul3LOBVC70FMIwfTt
+/HEFwawI+7t1aQU9Mk7VUSFX4nsoN7ZhicUu+WF5nuZpAIlJvM03HGTlr8/IbPIg
+yXiqx8//mgiC/eO/Pwy1kgPJ7Mfq5Fy3eMRIwUDqEMOgtcVAz/s6QdEKzkI0byPK
+LNs/eSI5lJ8qq8E4xtSIH4vSginvfYX+S6VQvIf1VqSkKh3NMTCrQ1768h+qLtyx
+yIdfSTjq1YD4IwQv7NInkVd0TdJf03BpmS0Q6wbici/6QZmK96et/BrnbsW6lXpB
+qBr2L9Xnr0p9jnJO+xu0HYJXsg1xZ6kE/SKnE8EPQIjuqDBtcO9kYBwF0jGj/srk
+2KK4SRxyhbdumJ7bNk4Y4R6h3c6LubUmNGudizahnxBm3sfQk275ppig0qANDl10
+wnKknFYY54g/AzTC2deXrIJXFw+Mb7XZbEUmFtU8gZT8r6vv3tpMmLD2mL8FyuDP
+k0bCIojaAS2Ufxxc1SuH
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
new file mode 100644
index 0000000..968675c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ApkVerityTestAppPrebuilt/README.md
@@ -0,0 +1,28 @@
+How the test works
+==================
+ApkVerityTestApp is a test helper app to be installed with fs-verity signature
+file (.fsv\_sig). In order for this CTS test to run on a release build across
+vendors, the signature needs to be verified against a release certificate loaded
+to kernel.
+
+How to modify the test helper app
+=================================
+Modifying the test helper app will also require to sign the apk with a local debug
+key. You will also need to point the test to use your local build.
+
+How to load debug key
+---------------------
+On debuggable build, it can be done by:
+
+```
+adb root
+adb shell 'mini-keyctl padd asymmetric fsv-play .fs-verity' < fsverity-debug.x509.der
+```
+
+On user build, the keyring is closed and doesn't accept extra key. A workaround
+is to copy the .der file to /system/etc/security/fsverity. Upon reboot, the
+certificate will be loaded to kernel as usual.
+
+How to use the app built locally
+--------------------------------
+TODO: provide instruction once the test switches to prebuilt and release signature.