blob: 1862c7b5002d7e9c8985bdb506c52d30faaa743b [file] [log] [blame]
/*
* Copyright (C) 2014 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 com.android.build.gradle.integration.application;
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
import static com.android.builder.core.BuilderConstants.RELEASE;
import static com.android.builder.internal.packaging.ApkCreatorType.APK_FLINGER;
import static com.android.builder.internal.packaging.ApkCreatorType.APK_Z_FILE_CREATOR;
import static com.android.testutils.truth.PathSubject.assertThat;
import static com.android.tools.build.apkzlib.sign.SignatureAlgorithm.DSA;
import static com.android.tools.build.apkzlib.sign.SignatureAlgorithm.ECDSA;
import static com.android.tools.build.apkzlib.sign.SignatureAlgorithm.RSA;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.annotations.NonNull;
import com.android.apksig.ApkVerifier;
import com.android.apksig.ApkVerifier.IssueWithParams;
import com.android.build.gradle.integration.common.fixture.BaseGradleExecutor;
import com.android.build.gradle.integration.common.fixture.GradleBuildResult;
import com.android.build.gradle.integration.common.fixture.GradleTestProject;
import com.android.build.gradle.integration.common.fixture.ModelBuilder;
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp;
import com.android.build.gradle.integration.common.runner.FilterableParameterized;
import com.android.build.gradle.integration.common.utils.AndroidProjectUtils;
import com.android.build.gradle.integration.common.utils.GradleTestProjectUtils;
import com.android.build.gradle.integration.common.utils.SigningConfigHelper;
import com.android.build.gradle.integration.common.utils.TestFileUtils;
import com.android.build.gradle.integration.common.utils.VariantUtils;
import com.android.build.gradle.options.BooleanOption;
import com.android.build.gradle.options.OptionalBooleanOption;
import com.android.build.gradle.options.StringOption;
import com.android.builder.core.BuilderConstants;
import com.android.builder.internal.packaging.ApkCreatorType;
import com.android.builder.model.AndroidArtifact;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SyncIssue;
import com.android.builder.model.Variant;
import com.android.testutils.TestUtils;
import com.android.testutils.apk.Apk;
import com.android.tools.build.apkzlib.sign.DigestAlgorithm;
import com.google.common.io.Resources;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/** Integration test for all signing-related features. */
@RunWith(FilterableParameterized.class)
public class SigningTest {
public static final String STORE_PASSWORD = "store_password";
public static final String ALIAS_NAME = "alias_name";
public static final String KEY_PASSWORD = "key_password";
@Parameterized.Parameter() public String keystoreName;
@Parameterized.Parameter(1)
public String certEntryName;
@Parameterized.Parameter(2)
public int minSdkVersion;
@Parameterized.Parameter(3)
public ApkCreatorType apkCreatorType;
@Rule
public GradleTestProject project =
GradleTestProject.builder().fromTestApp(HelloWorldApp.noBuildFile()).create();
private File keystore;
@Parameterized.Parameters(name = "{0}, {3}")
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[] {"rsa_keystore.jks", "CERT.RSA", RSA.minSdkVersion, APK_FLINGER},
new Object[] {
"rsa_keystore.jks", "CERT.RSA", RSA.minSdkVersion, APK_Z_FILE_CREATOR
},
new Object[] {"dsa_keystore.jks", "CERT.DSA", DSA.minSdkVersion, APK_FLINGER},
new Object[] {
"dsa_keystore.jks", "CERT.DSA", DSA.minSdkVersion, APK_Z_FILE_CREATOR
},
new Object[] {"ec_keystore.jks", "CERT.EC", ECDSA.minSdkVersion, APK_FLINGER},
new Object[] {
"ec_keystore.jks", "CERT.EC", ECDSA.minSdkVersion, APK_Z_FILE_CREATOR
});
}
private static void createKeystoreFile(@NonNull String resourceName, @NonNull File keystore)
throws Exception {
byte[] keystoreBytes =
Resources.toByteArray(
Resources.getResource(SigningTest.class, "SigningTest/" + resourceName));
Files.write(keystore.toPath(), keystoreBytes);
}
@NonNull
public static ApkVerifier.Result assertApkSignaturesVerify(@NonNull Apk apk, int minSdkVersion)
throws Exception {
ApkVerifier.Builder builder =
new ApkVerifier.Builder(apk.getFile().toFile())
.setMinCheckedPlatformVersion(minSdkVersion);
File v4SignatureFile = new File(apk.getFile().toFile().getAbsolutePath() + ".idsig");
if (v4SignatureFile.exists()) {
builder.setV4SignatureFile(v4SignatureFile);
}
ApkVerifier.Result result = builder.build().verify();
if (result.isVerified()) {
return result;
}
List<IssueWithParams> errors = new ArrayList<>(result.getErrors());
for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) {
errors.addAll(signer.getErrors());
}
for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) {
errors.addAll(signer.getErrors());
}
throw new AssertionError(
"APK signatures failed to verify. " + errors.size() + " error(s): " + errors);
}
private static void assertApkSignaturesDoNotVerify(Apk apk, int minSdkVersion)
throws Exception {
ApkVerifier.Result result =
new ApkVerifier.Builder(apk.getFile().toFile())
.setMinCheckedPlatformVersion(minSdkVersion)
.build()
.verify();
if (result.isVerified()) {
fail("APK signatures unexpectedly verified");
}
}
@Before
public void setUp() throws Exception {
keystore = project.file("the.keystore");
createKeystoreFile(keystoreName, keystore);
GradleTestProjectUtils.setApkCreatorType(project, apkCreatorType);
TestFileUtils.appendToFile(
project.getBuildFile(),
""
+ "apply plugin: 'com.android.application'\n"
+ "\n"
+ "android {\n"
+ " compileSdkVersion "
+ GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ "\n"
+ " buildToolsVersion '"
+ GradleTestProject.DEFAULT_BUILD_TOOL_VERSION
+ "'\n"
+ "\n"
+ " defaultConfig {\n"
+ " minSdkVersion "
+ minSdkVersion
+ "\n"
+ " }\n"
+ "\n"
+ " signingConfigs {\n"
+ " customDebug {\n"
+ " storeFile file('the.keystore')\n"
+ " storePassword '"
+ STORE_PASSWORD
+ "'\n"
+ " keyAlias '"
+ ALIAS_NAME
+ "'\n"
+ " keyPassword '"
+ KEY_PASSWORD
+ "'\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " buildTypes {\n"
+ " debug {\n"
+ " signingConfig signingConfigs.customDebug\n"
+ " }\n"
+ "\n"
+ " customSigning {\n"
+ " initWith release\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " applicationVariants.all { variant ->\n"
+ " if (variant.buildType.name == \"customSigning\") {\n"
+ " variant.outputsAreSigned = true\n"
+ " // This usually means there is a task that generates the final outputs\n"
+ " // and variant.outputs*.outputFile is set to point to these files.\n"
+ " }\n"
+ " }\n"
+ "}"
+ "");
}
@Test
public void signingDsl() throws Exception {
GradleBuildResult result = project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
// Check that signing config is not written to disk when passed from the build script (bug
// 137210434)
assertThat(result.getTasks()).doesNotContain(":signingConfigWriterDebug");
assertThat(result.getTasks()).contains(":writeDebugSigningConfigVersions");
}
@Test
public void assembleWithInjectedSigningConfig() throws Exception {
// add prop args for signing override.
GradleBuildResult result =
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
.with(OptionalBooleanOption.SIGNING_V1_ENABLED, true)
.with(OptionalBooleanOption.SIGNING_V2_ENABLED, true)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleRelease");
Apk apk = project.getApk(GradleTestProject.ApkType.RELEASE_SIGNED);
// Check for signing file inside the archive.
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
// Check that signing config is not written to disk when passed from the IDE (bug 137210434)
assertThat(result.getTasks()).doesNotContain(":signingConfigWriterRelease");
assertThat(result.getTasks()).contains(":writeReleaseSigningConfigVersions");
}
@Test
public void checkCustomSigning() throws Exception {
Collection<Variant> variants =
project.model()
.ignoreSyncIssues(SyncIssue.SEVERITY_WARNING)
.fetchAndroidProjects()
.getOnlyModel()
.getVariants();
for (Variant variant : variants) {
// Release variant doesn't specify the signing config, so it should not be considered
// signed.
if (variant.getName().equals("release")) {
assertThat(variant.getMainArtifact().isSigned()).named(variant.getName()).isFalse();
}
// customSigning is identical to release, but overrides the signing check.
if (variant.getName().equals("customSigning")) {
assertThat(variant.getMainArtifact().isSigned()).named(variant.getName()).isTrue();
}
}
}
@Test
public void signingConfigsModel() throws Exception {
ModelBuilder modelBuilder = project.model();
AndroidProject androidProject =
modelBuilder
.ignoreSyncIssues(SyncIssue.SEVERITY_WARNING)
.fetchAndroidProjects()
.getOnlyModel();
Collection<SigningConfig> signingConfigs = androidProject.getSigningConfigs();
assertThat(signingConfigs.stream().map(SigningConfig::getName).collect(Collectors.toList()))
.containsExactly(BuilderConstants.DEBUG, "customDebug");
SigningConfig debugSigningConfig =
AndroidProjectUtils.getSigningConfig(androidProject, BuilderConstants.DEBUG);
new SigningConfigHelper(
debugSigningConfig,
BuilderConstants.DEBUG,
modelBuilder.getPreferencesRootDir())
.test();
SigningConfig mySigningConfig =
AndroidProjectUtils.getSigningConfig(androidProject, "customDebug");
new SigningConfigHelper(mySigningConfig, "customDebug", keystore)
.setStorePassword(STORE_PASSWORD)
.setKeyAlias(ALIAS_NAME)
.setKeyPassword(KEY_PASSWORD)
.test();
Variant debugVariant =
AndroidProjectUtils.getVariantByName(androidProject, BuilderConstants.DEBUG);
assertThat(debugVariant.getMainArtifact().getSigningConfigName()).isEqualTo("customDebug");
AndroidArtifact androidTestArtifact = VariantUtils.getAndroidTestArtifact(debugVariant);
assertThat(androidTestArtifact.getSigningConfigName()).isEqualTo("customDebug");
Variant releaseVariant = AndroidProjectUtils.getVariantByName(androidProject, RELEASE);
assertThat(releaseVariant.getMainArtifact().getSigningConfigName()).isNull();
}
@Test
public void signingReportTask() throws Exception {
// SigningReportTask is not compatble
project.executor()
.withConfigurationCaching(BaseGradleExecutor.ConfigurationCaching.OFF)
.run("signingReport");
}
@Test
public void ShaAlgorithmChange() throws Exception {
if (minSdkVersion < DigestAlgorithm.API_SHA_256_RSA_AND_ECDSA) {
project.execute("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).containsFileWithMatch("META-INF/CERT.SF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-256-Digest");
assertThat(apk).containsFileWithMatch("META-INF/MANIFEST.MF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-256-Digest");
TestFileUtils.searchRegexAndReplace(
project.getBuildFile(),
"minSdkVersion \\d+",
"minSdkVersion " + DigestAlgorithm.API_SHA_256_RSA_AND_ECDSA);
}
TestUtils.waitForFileSystemTick();
project.execute("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
if ((certEntryName.endsWith(RSA.keyAlgorithm))
|| (certEntryName.endsWith(ECDSA.keyAlgorithm))) {
assertThat(apk).containsFileWithMatch("META-INF/CERT.SF", "SHA-256-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-1-Digest");
assertThat(apk).containsFileWithMatch("META-INF/MANIFEST.MF", "SHA-256-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-1-Digest");
} else {
assertThat(apk).containsFileWithMatch("META-INF/CERT.SF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-256-Digest");
assertThat(apk).containsFileWithMatch("META-INF/MANIFEST.MF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-256-Digest");
}
TestFileUtils.searchRegexAndReplace(
project.getBuildFile(),
"minSdkVersion \\d+",
"minSdkVersion " + DigestAlgorithm.API_SHA_256_ALL_ALGORITHMS);
TestUtils.waitForFileSystemTick();
project.execute("assembleDebug");
apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).containsFileWithMatch("META-INF/CERT.SF", "SHA-256-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA1-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/CERT.SF", "SHA-1-Digest");
assertThat(apk).containsFileWithMatch("META-INF/MANIFEST.MF", "SHA-256-Digest");
assertThat(apk).containsFileWithoutContent("META-INF/MANIFEST.MF", "SHA-1-Digest");
}
@Test
public void signingSchemeToggle() throws Exception {
// Toggles not specified -- testing their default values
project.execute("clean", "assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
assertTrue(verificationResult.isVerifiedUsingV2Scheme());
// Specified: v1SigningEnabled false, v2SigningEnabled false
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
"customDebug {\nv1SigningEnabled false\nv2SigningEnabled false");
TestUtils.waitForFileSystemTick();
project.execute("clean", "assembleDebug");
apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).doesNotContainApkSigningBlock();
assertApkSignaturesDoNotVerify(apk, minSdkVersion);
// Specified: v1SigningEnabled true, v2SigningEnabled false
TestFileUtils.searchAndReplace(
project.getBuildFile(), "v1SigningEnabled false", "v1SigningEnabled true");
TestUtils.waitForFileSystemTick();
project.execute("clean", "assembleDebug");
apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).doesNotContainApkSigningBlock();
verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
assertFalse(verificationResult.isVerifiedUsingV2Scheme());
// Specified: v1SigningEnabled false, v2SigningEnabled true
TestFileUtils.searchAndReplace(
project.getBuildFile(), "v1SigningEnabled true", "v1SigningEnabled false");
TestFileUtils.searchAndReplace(
project.getBuildFile(), "v2SigningEnabled false", "v2SigningEnabled true");
TestUtils.waitForFileSystemTick();
project.execute("clean", "assembleDebug");
apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
// API Level 24 is the lowest level at which APKs don't have to be signed with v1 scheme
assertApkSignaturesDoNotVerify(apk, Math.min(23, minSdkVersion));
verificationResult = assertApkSignaturesVerify(apk, Math.max(24, minSdkVersion));
assertFalse(verificationResult.isVerifiedUsingV1Scheme());
assertTrue(verificationResult.isVerifiedUsingV2Scheme());
// Specified: v1SigningEnabled true, v2SigningEnabled true
TestFileUtils.searchAndReplace(
project.getBuildFile(), "v1SigningEnabled false", "v1SigningEnabled true");
TestUtils.waitForFileSystemTick();
project.execute("clean", "assembleDebug");
apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
assertTrue(verificationResult.isVerifiedUsingV2Scheme());
}
@Test
public void assembleWithInjectedV1ConfigOnly() throws Exception {
// add prop args for signing override.
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
.with(OptionalBooleanOption.SIGNING_V1_ENABLED, true)
.with(OptionalBooleanOption.SIGNING_V2_ENABLED, false)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleRelease");
Apk apk = project.getApk(GradleTestProject.ApkType.RELEASE_SIGNED);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
assertFalse(verificationResult.isVerifiedUsingV2Scheme());
}
@Test
public void assembleWithInjectedV1ConfigDependencyInfoDisabled() throws Exception {
// add prop args for signing override.
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
.with(OptionalBooleanOption.SIGNING_V1_ENABLED, true)
.with(OptionalBooleanOption.SIGNING_V2_ENABLED, false)
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleRelease");
Apk apk = project.getApk(GradleTestProject.ApkType.RELEASE_SIGNED);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).doesNotContainApkSigningBlock();
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertTrue(verificationResult.isVerifiedUsingV1Scheme());
assertFalse(verificationResult.isVerifiedUsingV2Scheme());
}
@Test
public void assembleWithInjectedV2ConfigOnly() throws Exception {
// add prop args for signing override.
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
.with(OptionalBooleanOption.SIGNING_V1_ENABLED, false)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.with(OptionalBooleanOption.SIGNING_V2_ENABLED, true)
.run("assembleRelease");
Apk apk = project.getApk(GradleTestProject.ApkType.RELEASE_SIGNED);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
// API Level 24 is the lowest level at which APKs don't have to be signed with v1 scheme
assertApkSignaturesDoNotVerify(apk, 23);
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 24));
assertFalse(verificationResult.isVerifiedUsingV1Scheme());
assertTrue(verificationResult.isVerifiedUsingV2Scheme());
}
@Test
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
public void failWithMissingKeyPassword() throws Exception {
TestFileUtils.searchAndReplace(
project.getBuildFile(), "keyPassword '" + KEY_PASSWORD + "'", "");
project.executeExpectingFailure("assembleDebug");
}
@Test
public void signingWithV3() throws Exception {
// v3 signing is not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " v1SigningEnabled false\n"
+ " v2SigningEnabled false\n"
+ " enableV3Signing true\n");
project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
// API Level 28 is the lowest level that supports v3 signing
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 28));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isTrue();
}
@Test
public void signingWithV4AndV2() throws Exception {
// v4 signing is not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " v1SigningEnabled false\n"
+ " v2SigningEnabled true\n"
+ " enableV3Signing false\n"
+ " enableV4Signing true\n");
project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
// API Level 28 is the lowest level that supports v4 signing
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 28));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV4Scheme()).isTrue();
}
@Test
public void signingWithV4AndV3() throws Exception {
// v4 and v3 signing are not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " v1SigningEnabled false\n"
+ " v2SigningEnabled false\n"
+ " enableV3Signing true\n"
+ " enableV4Signing true\n");
project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
// API Level 28 is the lowest level that supports v4 signing
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 28));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV4Scheme()).isTrue();
}
/**
* Test that injecting signing config data doesn't change which signature versions are enabled
* if signature versions aren't injected.
*
* This test is a corner case for v1 and v2 because the IDE currently always injects non-null
* values for both of them when it injects signing config info.
*
* But this is an important regression test for v3 and v4. Previously, if the IDE injected
* signing config info, then we wouldn't sign with v3 or v4 even if they were enabled via the
* DSL.
*/
@Test
public void signingWithDslVersionsWithInjectedSigningConfig() throws Exception {
// v4 and v3 signing are not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
// set enableV1Signing and enableV2Signing to false below because we're testing that
// they're not being overridden by the "injected" null values which would cause v1 and v2
// signing to be enabled by default.
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " enableV1Signing false\n"
+ " enableV2Signing false\n"
+ " enableV3Signing true\n"
+ " enableV4Signing true\n");
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, 24);
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV4Scheme()).isTrue();
}
@Test
public void enableSigningWithVariantApi() throws Exception {
// v4 and v3 signing are not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
TestFileUtils.appendToFile(
project.getBuildFile(),
"\n"
+ "androidComponents {\n"
+ " onVariants(selector().withName('debug'), {\n"
+ " signingConfig.enableV1Signing.set(true)\n"
+ " signingConfig.enableV2Signing.set(true)\n"
+ " signingConfig.enableV3Signing.set(true)\n"
+ " signingConfig.enableV4Signing.set(true)\n"
+ " })\n"
+ "}\n");
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 23));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV4Scheme()).isTrue();
}
@Test
public void injectedValuesOverrideVariantApi() throws Exception {
// v4 and v3 signing are not supported by apkzlib
Assume.assumeTrue(apkCreatorType == APK_FLINGER);
TestFileUtils.appendToFile(
project.getBuildFile(),
"\n"
+ "androidComponents {\n"
+ " onVariants(selector().withName('debug'), {\n"
+ " signingConfig.enableV1Signing.set(false)\n"
+ " signingConfig.enableV2Signing.set(false)\n"
+ " signingConfig.enableV3Signing.set(true)\n"
+ " signingConfig.enableV4Signing.set(true)\n"
+ " })\n"
+ "}\n");
project.executor()
.with(StringOption.IDE_SIGNING_STORE_FILE, keystore.getPath())
.with(StringOption.IDE_SIGNING_STORE_PASSWORD, STORE_PASSWORD)
.with(StringOption.IDE_SIGNING_KEY_ALIAS, ALIAS_NAME)
.with(StringOption.IDE_SIGNING_KEY_PASSWORD, KEY_PASSWORD)
.with(OptionalBooleanOption.SIGNING_V1_ENABLED, true)
.with(OptionalBooleanOption.SIGNING_V2_ENABLED, true)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 23));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV3Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV4Scheme()).isTrue();
}
@Test
public void signingWithEnableV1SigningFalse() throws Exception {
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " enableV1Signing false\n");
project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).doesNotContain("META-INF/" + certEntryName);
assertThat(apk).doesNotContain("META-INF/CERT.SF");
assertThat(apk).containsApkSigningBlock();
ApkVerifier.Result verificationResult =
assertApkSignaturesVerify(apk, Math.max(minSdkVersion, 24));
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isFalse();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isTrue();
}
@Test
public void signingWithEnableV2SigningFalse() throws Exception {
TestFileUtils.searchAndReplace(
project.getBuildFile(),
"customDebug {",
""
+ "customDebug {\n"
+ " enableV2Signing false\n");
project.executor().run("assembleDebug");
Apk apk = project.getApk(GradleTestProject.ApkType.DEBUG);
assertThat(apk).contains("META-INF/" + certEntryName);
assertThat(apk).contains("META-INF/CERT.SF");
assertThat(apk).doesNotContainApkSigningBlock();
ApkVerifier.Result verificationResult = assertApkSignaturesVerify(apk, minSdkVersion);
assertThat(verificationResult.isVerifiedUsingV1Scheme()).isTrue();
assertThat(verificationResult.isVerifiedUsingV2Scheme()).isFalse();
}
}