aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Groover <mpgroover@google.com>2021-10-07 17:29:58 -0700
committerMichael Groover <mpgroover@google.com>2021-10-08 17:49:11 -0700
commitc5bd203c87b7fa76572176a44437d66fa28f918e (patch)
tree685fb5c8697c8ea2a88455a7d8ce7040eb317e09
parentf25dde795d091848ca28677289322644bd75be52 (diff)
downloadapksig-c5bd203c87b7fa76572176a44437d66fa28f918e.tar.gz
Add V4 signing tests to verify V3.1 support
Android T introduced the V3.1 signature scheme to allow APK version targeting for key rotation; this scheme allows a single APK to support rotation on SDK version X for X >= Android T, while all previous releases will only see and use the original signing key. This requires the V4 signature to be updated to include both the rotated signing key when installed on a device running X+ as well as the original signing key for devices running a version < X. This commit adds two new tests to ensure the V4 signature contains the expected signer both when the V3.1 signature scheme is used as well as when only the V3.0 scheme is used for rotation. The V3.1 test is currently marked with @Ignore and should be re-enabled once the V4 scheme supports V3.1. Bug: 202011194 Test: gradlew test Change-Id: I4a4c43553c9e1e72c7b6d97c6b716ad57728666a
-rw-r--r--src/main/java/com/android/apksig/ApkVerifier.java6
-rw-r--r--src/test/java/com/android/apksig/ApkSignerTest.java83
2 files changed, 88 insertions, 1 deletions
diff --git a/src/main/java/com/android/apksig/ApkVerifier.java b/src/main/java/com/android/apksig/ApkVerifier.java
index 6661e50..fb8648c 100644
--- a/src/main/java/com/android/apksig/ApkVerifier.java
+++ b/src/main/java/com/android/apksig/ApkVerifier.java
@@ -1172,7 +1172,11 @@ public class ApkVerifier {
return mV31SchemeSigners;
}
- private List<V4SchemeSignerInfo> getV4SchemeSigners() {
+ /**
+ * Returns information about APK Signature Scheme v4 signers associated with the APK's
+ * signature.
+ */
+ public List<V4SchemeSignerInfo> getV4SchemeSigners() {
return mV4SchemeSigners;
}
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index 30c5766..f213c61 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -51,6 +51,7 @@ import com.android.apksig.zip.ZipFormatException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -1802,6 +1803,67 @@ public class ApkSignerTest {
assertTrue(result.getV3SchemeSigners().get(0).getMaxSdkVersion() >= AndroidSdkVersion.S);
}
+ @Test
+ public void testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner()
+ throws Exception {
+ // To support SDK version targeting in the v3.1 signature scheme, apksig added a
+ // rotation-min-sdk-version option to allow the caller to specify the level from which
+ // the rotated signer should be used. A value less than T should result in a single
+ // rotated signer in the V3 block (along with the corresponding lineage), and the V4
+ // signature should use this signer.
+ List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
+ Arrays.asList(
+ getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
+ getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
+ SigningCertificateLineage lineage =
+ Resources.toSigningCertificateLineage(
+ ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
+
+ File signedApk = sign("original.apk",
+ new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(true)
+ .setV3SigningEnabled(true)
+ .setV4SigningEnabled(true)
+ .setMinSdkVersionForRotation(AndroidSdkVersion.P)
+ .setSigningCertificateLineage(lineage));
+ ApkVerifier.Result result = verify(signedApk, null);
+
+ assertResultContainsV4Signers(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
+ }
+
+ @Test
+ @Ignore("TODO(b/202011194): Re-enable once V4 supports V3.1")
+ public void testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey() throws Exception {
+ // When an APK is signed with a rotated key and the rotation-min-sdk-version X is set to T+,
+ // a V3.1 block will be signed with the rotated signing key targeting X and later, and
+ // a V3.0 block will be signed with the original signing key targeting P - X-1. The
+ // V4 signature should contain both the original signing key and the rotated signing
+ // key; this ensures if an APK is installed on a device running an SDK version less than X,
+ // the V4 signature will be verified using the original signing key which will be the only
+ // signing key visible to the platform.
+ List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
+ Arrays.asList(
+ getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
+ getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
+ SigningCertificateLineage lineage =
+ Resources.toSigningCertificateLineage(
+ ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
+
+ File signedApk = sign("original.apk",
+ new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(true)
+ .setV3SigningEnabled(true)
+ .setV4SigningEnabled(true)
+ .setMinSdkVersionForRotation(AndroidSdkVersion.P)
+ .setSigningCertificateLineage(lineage));
+ ApkVerifier.Result result = verify(signedApk, null);
+
+ assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
+ SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
+ }
+
/**
* Asserts the provided {@code signedApk} contains a signature block with the expected
* {@code byte[]} value and block ID as specified in the {@code expectedBlock}.
@@ -1911,6 +1973,27 @@ public class ApkSignerTest {
}
/**
+ * Asserts the provided verification {@code result} contains the expected V4 {@code signers}.
+ */
+ private static void assertResultContainsV4Signers(ApkVerifier.Result result, String... signers)
+ throws Exception {
+ assertTrue(result.isVerified());
+ assertTrue(result.isVerifiedUsingV4Scheme());
+ List<X509Certificate> expectedSigners = new ArrayList<>();
+ for (String signer : signers) {
+ ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer);
+ expectedSigners.addAll(signerConfig.getCertificates());
+ }
+ List<X509Certificate> v4Signers = new ArrayList<>();
+ for (ApkVerifier.Result.V4SchemeSignerInfo signer : result.getV4SchemeSigners()) {
+ v4Signers.addAll(signer.getCertificates());
+ }
+ assertTrue("Expected V4 signers: " + getAllSubjectNamesFrom(expectedSigners)
+ + ", actual V4 signers: " + getAllSubjectNamesFrom(v4Signers),
+ v4Signers.containsAll(expectedSigners));
+ }
+
+ /**
* Asserts the provided {@code result} contains the expected {@code signer} targeting
* {@code minSdkVersion} as the minimum version for rotation.
*/