1 /* 2 * Copyright (C) 2017 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 com.android.apksig; 18 19 import static com.android.apksig.ApkSignerTest.assertResultContainsSigners; 20 import static com.android.apksig.ApkSignerTest.assertV31SignerTargetsMinApiLevel; 21 import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V2; 22 import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V3; 23 import static com.android.apksig.Constants.VERSION_APK_SIGNATURE_SCHEME_V31; 24 import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME; 25 import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 import static org.junit.Assume.assumeNoException; 32 import static org.junit.Assume.assumeTrue; 33 34 import com.android.apksig.ApkVerifier.Issue; 35 import com.android.apksig.ApkVerifier.IssueWithParams; 36 import com.android.apksig.ApkVerifier.Result; 37 import com.android.apksig.ApkVerifier.Result.SourceStampInfo.SourceStampVerificationStatus; 38 import com.android.apksig.apk.ApkFormatException; 39 import com.android.apksig.apk.ApkUtils; 40 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 41 import com.android.apksig.internal.apk.ContentDigestAlgorithm; 42 import com.android.apksig.internal.apk.v3.V3SchemeConstants; 43 import com.android.apksig.internal.util.AndroidSdkVersion; 44 import com.android.apksig.internal.util.HexEncoding; 45 import com.android.apksig.internal.util.Resources; 46 import com.android.apksig.util.DataSource; 47 import com.android.apksig.util.DataSources; 48 49 import org.junit.Assume; 50 import org.junit.Rule; 51 import org.junit.Test; 52 import org.junit.rules.TemporaryFolder; 53 import org.junit.runner.RunWith; 54 import org.junit.runners.JUnit4; 55 56 import java.io.IOException; 57 import java.lang.reflect.Field; 58 import java.lang.reflect.Modifier; 59 import java.net.URISyntaxException; 60 import java.nio.ByteBuffer; 61 import java.nio.charset.StandardCharsets; 62 import java.security.InvalidKeyException; 63 import java.security.MessageDigest; 64 import java.security.NoSuchAlgorithmException; 65 import java.security.Provider; 66 import java.security.PublicKey; 67 import java.security.Security; 68 import java.security.Signature; 69 import java.security.cert.CertificateFactory; 70 import java.security.cert.X509Certificate; 71 import java.util.Arrays; 72 import java.util.Collection; 73 import java.util.Collections; 74 import java.util.HashMap; 75 import java.util.List; 76 import java.util.Locale; 77 import java.util.Map; 78 import java.util.Objects; 79 import java.util.stream.Collectors; 80 import java.util.stream.Stream; 81 82 @RunWith(JUnit4.class) 83 public class ApkVerifierTest { 84 @Rule 85 public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); 86 87 private static final String[] DSA_KEY_NAMES = {"1024", "2048", "3072"}; 88 private static final String[] DSA_KEY_NAMES_1024_AND_SMALLER = {"1024"}; 89 private static final String[] DSA_KEY_NAMES_2048_AND_LARGER = {"2048", "3072"}; 90 private static final String[] EC_KEY_NAMES = {"p256", "p384", "p521"}; 91 private static final String[] RSA_KEY_NAMES = {"1024", "2048", "3072", "4096", "8192", "16384"}; 92 private static final String[] RSA_KEY_NAMES_2048_AND_LARGER = { 93 "2048", "3072", "4096", "8192", "16384" 94 }; 95 96 private static final String RSA_2048_CERT_SHA256_DIGEST = 97 "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8"; 98 private static final String EC_P256_CERT_SHA256_DIGEST = 99 "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599"; 100 private static final String RSA_2048_CHUNKED_SHA256_DIGEST = 101 "0a457e6dd7cc8d4dde28a4dae843032de5fbe58123eedd0a31e7f958f23e1626"; 102 private static final String RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK = 103 "0a457e6dd7cc8d4dde28a4dae843032de5fbe58101eedd0a31e7f958f23e1626"; 104 105 @Test testOriginalAccepted()106 public void testOriginalAccepted() throws Exception { 107 // APK signed with v1 and v2 schemes. Obtained by building 108 // cts/hostsidetests/appsecurity/test-apps/tinyapp. 109 // This APK is used as a basis for many of the other tests here. Hence, we check that this 110 // APK verifies. 111 assertVerified(verify("original.apk")); 112 } 113 114 @Test testV1OneSignerMD5withRSAAccepted()115 public void testV1OneSignerMD5withRSAAccepted() throws Exception { 116 // APK signed with v1 scheme only, one signer 117 assertVerifiedForEach( 118 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 119 assertVerifiedForEach( 120 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-%s.apk", RSA_KEY_NAMES); 121 } 122 123 @Test testV1OneSignerSHA1withRSAAccepted()124 public void testV1OneSignerSHA1withRSAAccepted() throws Exception { 125 // APK signed with v1 scheme only, one signer 126 assertVerifiedForEach( 127 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 128 assertVerifiedForEach( 129 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-%s.apk", RSA_KEY_NAMES); 130 } 131 132 @Test testV1OneSignerSHA224withRSAAccepted()133 public void testV1OneSignerSHA224withRSAAccepted() throws Exception { 134 // APK signed with v1 scheme only, one signer 135 assertVerifiedForEach( 136 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 137 assertVerifiedForEach( 138 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-%s.apk", RSA_KEY_NAMES); 139 } 140 141 @Test testV1OneSignerSHA256withRSAAccepted()142 public void testV1OneSignerSHA256withRSAAccepted() throws Exception { 143 // APK signed with v1 scheme only, one signer 144 assertVerifiedForEach( 145 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 146 assertVerifiedForEach( 147 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-%s.apk", RSA_KEY_NAMES); 148 } 149 150 @Test testV1OneSignerSHA384withRSAAccepted()151 public void testV1OneSignerSHA384withRSAAccepted() throws Exception { 152 // APK signed with v1 scheme only, one signer 153 assertVerifiedForEach( 154 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 155 assertVerifiedForEach( 156 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-%s.apk", RSA_KEY_NAMES); 157 } 158 159 @Test testV1OneSignerSHA512withRSAVerifies()160 public void testV1OneSignerSHA512withRSAVerifies() throws Exception { 161 // APK signed with v1 scheme only, one signer 162 assertVerifiedForEach( 163 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES); 164 assertVerifiedForEach( 165 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-%s.apk", RSA_KEY_NAMES); 166 } 167 168 @Test testV1OneSignerSHA1withECDSAAccepted()169 public void testV1OneSignerSHA1withECDSAAccepted() throws Exception { 170 // APK signed with v1 scheme only, one signer 171 assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); 172 assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-%s.apk", EC_KEY_NAMES); 173 } 174 175 @Test testV1OneSignerSHA224withECDSAAccepted()176 public void testV1OneSignerSHA224withECDSAAccepted() throws Exception { 177 // APK signed with v1 scheme only, one signer 178 assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); 179 assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-%s.apk", EC_KEY_NAMES); 180 } 181 182 @Test testV1OneSignerSHA256withECDSAAccepted()183 public void testV1OneSignerSHA256withECDSAAccepted() throws Exception { 184 // APK signed with v1 scheme only, one signer 185 assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); 186 assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-%s.apk", EC_KEY_NAMES); 187 } 188 189 @Test testV1OneSignerSHA384withECDSAAccepted()190 public void testV1OneSignerSHA384withECDSAAccepted() throws Exception { 191 // APK signed with v1 scheme only, one signer 192 assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); 193 assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-%s.apk", EC_KEY_NAMES); 194 } 195 196 @Test testV1OneSignerSHA512withECDSAAccepted()197 public void testV1OneSignerSHA512withECDSAAccepted() throws Exception { 198 // APK signed with v1 scheme only, one signer 199 assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES); 200 assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-%s.apk", EC_KEY_NAMES); 201 } 202 203 @Test testV1OneSignerSHA1withDSAAccepted()204 public void testV1OneSignerSHA1withDSAAccepted() throws Exception { 205 // APK signed with v1 scheme only, one signer 206 // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to 207 // verify DSA signatures with keys too long for the SHA-1 digest. 208 assertVerifiedForEach( 209 "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); 210 assertVerifiedForEach( 211 "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); 212 } 213 214 @Test testV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest()215 public void testV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest() throws Exception { 216 // APK signed with v1 scheme only, one signer 217 218 // OpenJDK's default implementation of Signature.SHA1withDSA refuses to verify signatures 219 // created with keys too long for the digest used. Android Package Manager does not reject 220 // such signatures. We thus skip this test if Signature.SHA1withDSA exhibits this issue. 221 PublicKey publicKey = 222 Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey(); 223 Signature s = Signature.getInstance("SHA1withDSA"); 224 try { 225 s.initVerify(publicKey); 226 } catch (InvalidKeyException e) { 227 assumeNoException(e); 228 } 229 230 assertVerifiedForEach( 231 "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); 232 assertVerifiedForEach( 233 "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); 234 } 235 236 @Test testV1OneSignerSHA224withDSAAccepted()237 public void testV1OneSignerSHA224withDSAAccepted() throws Exception { 238 // APK signed with v1 scheme only, one signer 239 // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to 240 // verify DSA signatures with keys too long for the SHA-224 digest. 241 assertVerifiedForEach( 242 "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER); 243 assertVerifiedForEach( 244 "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", 245 DSA_KEY_NAMES_1024_AND_SMALLER); 246 } 247 248 @Test testV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest()249 public void testV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest() throws Exception { 250 // APK signed with v1 scheme only, one signer 251 252 // OpenJDK's default implementation of Signature.SHA224withDSA refuses to verify signatures 253 // created with keys too long for the digest used. Android Package Manager does not reject 254 // such signatures. We thus skip this test if Signature.SHA224withDSA exhibits this issue. 255 PublicKey publicKey = 256 Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey(); 257 Signature s = Signature.getInstance("SHA224withDSA"); 258 try { 259 s.initVerify(publicKey); 260 } catch (InvalidKeyException e) { 261 assumeNoException(e); 262 } 263 assertVerifiedForEach( 264 "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER); 265 assertVerifiedForEach( 266 "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk", 267 DSA_KEY_NAMES_2048_AND_LARGER); 268 } 269 270 @Test testV1OneSignerSHA256withDSAAccepted()271 public void testV1OneSignerSHA256withDSAAccepted() throws Exception { 272 // APK signed with v1 scheme only, one signer 273 assertVerifiedForEach("v1-only-with-dsa-sha256-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES); 274 assertVerifiedForEach( 275 "v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-%s.apk", DSA_KEY_NAMES); 276 } 277 278 @Test testV1MaxSupportedSignersAccepted()279 public void testV1MaxSupportedSignersAccepted() throws Exception { 280 // The APK Signature Scheme V1 supports a maximum of 10 signers; this test ensures an 281 // APK signed with that many signers successfully verifies. 282 assertVerified(verify("v1-only-10-signers.apk")); 283 } 284 285 @Test testV1MoreThanMaxSupportedSignersRejected()286 public void testV1MoreThanMaxSupportedSignersRejected() throws Exception { 287 // This test ensure an APK signed with more than the supported number of signers fails 288 // to verify. 289 assertVerificationFailure("v1-only-11-signers.apk", Issue.JAR_SIG_MAX_SIGNATURES_EXCEEDED); 290 } 291 292 @Test testV2StrippedRejected()293 public void testV2StrippedRejected() throws Exception { 294 // APK signed with v1 and v2 schemes, but v2 signature was stripped from the file (by using 295 // zipalign). 296 // This should fail because the v1 signature indicates that the APK was supposed to be 297 // signed with v2 scheme as well, making the platform's anti-stripping protections reject 298 // the APK. 299 assertVerificationFailure("v2-stripped.apk", Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED); 300 301 // Similar to above, but the X-Android-APK-Signed anti-stripping header in v1 signature 302 // lists unknown signature schemes in addition to APK Signature Scheme v2. Unknown schemes 303 // should be ignored. 304 assertVerificationFailure( 305 "v2-stripped-with-ignorable-signing-schemes.apk", 306 Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED); 307 } 308 309 @Test testV3StrippedRejected()310 public void testV3StrippedRejected() throws Exception { 311 // APK signed with v2 and v3 schemes, but v3 signature was stripped from the file by 312 // modifying the v3 block ID to be the verity padding block ID. Without the stripping 313 // protection this modification ignores the v3 signing scheme block. 314 assertVerificationFailure("v3-stripped.apk", Issue.V2_SIG_MISSING_APK_SIG_REFERENCED); 315 } 316 317 @Test testSignaturesIgnoredForMaxSDK()318 public void testSignaturesIgnoredForMaxSDK() throws Exception { 319 // The V2 signature scheme was introduced in N, and V3 was introduced in P. This test 320 // verifies a max SDK of pre-P ignores the V3 signature and a max SDK of pre-N ignores both 321 // the V2 and V3 signatures. 322 assertVerified( 323 verifyForMaxSdkVersion( 324 "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.O)); 325 assertVerified( 326 verifyForMaxSdkVersion( 327 "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.M)); 328 } 329 330 @Test testGetResultLineage()331 public void testGetResultLineage() throws Exception { 332 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 333 "v31-tgt-33-no-v3-attr.apk"))); 334 int sdkVersion = AndroidSdkVersion.O; 335 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 336 337 Result result = ApkVerifier.getSigningBlockResult( 338 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 339 340 assertTrue(ApkVerifier.getLineageFromResult( 341 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31).size() == 2); 342 assertEquals(ApkVerifier.getLineageFromResult( 343 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) 344 .getCertificatesInLineage().get(1), 345 result.getV31SchemeSigners().get(0).getCertificate()); 346 347 SigningCertificateLineageTest.assertLineageContainsExpectedSigners( 348 ApkVerifier.getLineageFromResult( 349 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31), 350 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 351 } 352 353 @Test testGetResultV3Lineage()354 public void testGetResultV3Lineage() throws Exception { 355 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 356 "v3-rsa-2048_2-tgt-dev-release.apk"))); 357 int sdkVersion = AndroidSdkVersion.N; 358 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 359 360 Result result = ApkVerifier.getSigningBlockResult( 361 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); 362 363 assertTrue(ApkVerifier.getLineageFromResult( 364 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3).size() == 2); 365 assertEquals(ApkVerifier.getLineageFromResult( 366 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3) 367 .getCertificatesInLineage().get(1), 368 result.getV3SchemeSigners().get(0).getCertificate()); 369 370 SigningCertificateLineageTest.assertLineageContainsExpectedSigners( 371 ApkVerifier.getLineageFromResult( 372 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3), 373 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 374 } 375 376 @Test testGetResultNoLineageApk()377 public void testGetResultNoLineageApk() throws Exception { 378 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 379 "v31-empty-lineage-no-v3.apk"))); 380 int sdkVersion = AndroidSdkVersion.N; 381 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 382 383 Result result = ApkVerifier.getSigningBlockResult( 384 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 385 386 assertTrue(result != null); 387 assertTrue(!ApkVerifier.containsLineageErrors(result)); 388 assertTrue(ApkVerifier.getLineageFromResult( 389 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) != null); 390 assertEquals(ApkVerifier.getLineageFromResult( 391 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) 392 .getCertificatesInLineage().get(0), 393 result.getV31SchemeSigners().get(0).getCertificate()); 394 } 395 396 @Test testGetResultNoV31Apk()397 public void testGetResultNoV31Apk() throws Exception { 398 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 399 "v3-rsa-2048_2-tgt-dev-release.apk"))); 400 int sdkVersion = AndroidSdkVersion.N; 401 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 402 403 Result result = ApkVerifier.getSigningBlockResult( 404 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 405 406 assertTrue(result.getV31SchemeSigners().isEmpty()); 407 } 408 409 @Test testGetResultFromV3BlockFromV31SignedApk()410 public void testGetResultFromV3BlockFromV31SignedApk() throws Exception { 411 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 412 "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); 413 int sdkVersion = AndroidSdkVersion.N; 414 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 415 416 Result result = 417 ApkVerifier.getSigningBlockResult( 418 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); 419 420 assertTrue(!result.getV3SchemeSigners().isEmpty()); 421 assertTrue(ApkVerifier.getLineageFromResult( 422 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3) 423 .getCertificatesInLineage() 424 .equals(Arrays.asList(result.getV3SchemeSigners().get(0).getCertificate()))); 425 } 426 427 @Test testGetResultContainsLineageErrors()428 public void testGetResultContainsLineageErrors() throws Exception { 429 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 430 "v31-2elem-incorrect-lineage.apk"))); 431 int sdkVersion = AndroidSdkVersion.P; 432 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 433 434 Result result = ApkVerifier.getSigningBlockResult( 435 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 436 437 assertTrue(result != null); 438 assertTrue(ApkVerifier.containsLineageErrors(result)); 439 assertTrue(ApkVerifier.getLineageFromResult( 440 result, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31) == null); 441 } 442 443 @Test testGetResultDigests()444 public void testGetResultDigests() throws Exception { 445 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 446 "v31-empty-lineage-no-v3.apk"))); 447 int sdkVersion = AndroidSdkVersion.N; 448 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 449 450 Result result = ApkVerifier.getSigningBlockResult( 451 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 452 453 Map<ContentDigestAlgorithm, byte[]> digests = 454 ApkVerifier.getContentDigestsFromResult( 455 result, VERSION_APK_SIGNATURE_SCHEME_V31); 456 457 assertTrue(digests.size() == 1); 458 assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); 459 assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( 460 ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); 461 } 462 463 @Test testGetV3ResultDigests()464 public void testGetV3ResultDigests() throws Exception { 465 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 466 "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); 467 int sdkVersion = AndroidSdkVersion.N; 468 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 469 470 Result result = ApkVerifier.getSigningBlockResult( 471 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V3); 472 473 Map<ContentDigestAlgorithm, byte[]> digests = 474 ApkVerifier.getContentDigestsFromResult( 475 result, VERSION_APK_SIGNATURE_SCHEME_V3); 476 477 assertTrue(digests.size() == 1); 478 assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); 479 assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( 480 ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); 481 } 482 483 @Test testGetV2ResultDigests()484 public void testGetV2ResultDigests() throws Exception { 485 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 486 "v31-rsa-2048_2-tgt-33-1-tgt-28.apk"))); 487 int sdkVersion = AndroidSdkVersion.N; 488 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 489 490 Result result =ApkVerifier.getSigningBlockResult( 491 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V2); 492 493 Map<ContentDigestAlgorithm, byte[]> digests = 494 ApkVerifier.getContentDigestsFromResult( 495 result, VERSION_APK_SIGNATURE_SCHEME_V2); 496 497 assertTrue(digests.size() == 1); 498 assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); 499 assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( 500 ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); 501 } 502 503 @Test testGetResultIncorrectDigests()504 public void testGetResultIncorrectDigests() throws Exception { 505 DataSource apk = DataSources.asDataSource(ByteBuffer.wrap(Resources.toByteArray(getClass(), 506 "v31-2elem-lineage-incorrect-digest.apk"))); 507 int sdkVersion = AndroidSdkVersion.S; 508 ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk); 509 510 Result result = ApkVerifier.getSigningBlockResult( 511 apk, zipSections, sdkVersion, VERSION_APK_SIGNATURE_SCHEME_V31); 512 513 Map<ContentDigestAlgorithm, byte[]> digests = 514 ApkVerifier.getContentDigestsFromResult( 515 result, VERSION_APK_SIGNATURE_SCHEME_V31); 516 517 assertTrue(digests.size() == 1); 518 assertTrue(digests.containsKey(ContentDigestAlgorithm.CHUNKED_SHA256)); 519 assertTrue(!RSA_2048_CHUNKED_SHA256_DIGEST.equalsIgnoreCase( 520 ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); 521 assertTrue(RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK.equalsIgnoreCase( 522 ApkSigningBlockUtils.toHex(digests.get(ContentDigestAlgorithm.CHUNKED_SHA256)))); 523 } 524 525 @Test testV2OneSignerOneSignatureAccepted()526 public void testV2OneSignerOneSignatureAccepted() throws Exception { 527 // APK signed with v2 scheme only, one signer, one signature 528 assertVerifiedForEachForMinSdkVersion( 529 "v2-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.N); 530 assertVerifiedForEachForMinSdkVersion( 531 "v2-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N); 532 assertVerifiedForEachForMinSdkVersion( 533 "v2-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); 534 // RSA-PSS signatures tested in a separate test below 535 536 // DSA with SHA-512 is not supported by Android platform and thus APK Signature Scheme v2 537 // does not support that either 538 // assertInstallSucceedsForEach("v2-only-with-dsa-sha512-%s.apk", DSA_KEY_NAMES); 539 assertVerifiedForEachForMinSdkVersion( 540 "v2-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N); 541 assertVerifiedForEachForMinSdkVersion( 542 "v2-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); 543 } 544 545 @Test testV3OneSignerOneSignatureAccepted()546 public void testV3OneSignerOneSignatureAccepted() throws Exception { 547 // APK signed with v3 scheme only, one signer, one signature 548 assertVerifiedForEachForMinSdkVersion( 549 "v3-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.P); 550 assertVerifiedForEachForMinSdkVersion( 551 "v3-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); 552 assertVerifiedForEachForMinSdkVersion( 553 "v3-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); 554 555 assertVerifiedForEachForMinSdkVersion( 556 "v3-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P); 557 assertVerifiedForEachForMinSdkVersion( 558 "v3-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P); 559 } 560 561 @Test testV2OneSignerOneRsaPssSignatureAccepted()562 public void testV2OneSignerOneRsaPssSignatureAccepted() throws Exception { 563 assumeThatRsaPssAvailable(); 564 // APK signed with v2 scheme only, one signer, one signature 565 assertVerifiedForEachForMinSdkVersion( 566 "v2-only-with-rsa-pss-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N); 567 assertVerifiedForEachForMinSdkVersion( 568 "v2-only-with-rsa-pss-sha512-%s.apk", 569 RSA_KEY_NAMES_2048_AND_LARGER, // 1024-bit key is too short for PSS with SHA-512 570 AndroidSdkVersion.N); 571 } 572 573 @Test testV2SignatureDoesNotMatchSignedDataRejected()574 public void testV2SignatureDoesNotMatchSignedDataRejected() throws Exception { 575 // APK signed with v2 scheme only, but the signature over signed-data does not verify 576 577 // Bitflip in certificate field inside signed-data. Based on 578 // v2-only-with-dsa-sha256-1024.apk. 579 assertVerificationFailure( 580 "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", 581 Issue.V2_SIG_DID_NOT_VERIFY); 582 583 // Signature claims to be RSA PKCS#1 v1.5 with SHA-256, but is actually using SHA-512. 584 // Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. 585 assertVerificationIssue( 586 verify("v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk"), 587 true, 588 Issue.V2_SIG_VERIFY_EXCEPTION, Issue.V2_SIG_DID_NOT_VERIFY); 589 590 // Bitflip in the ECDSA signature. Based on v2-only-with-ecdsa-sha256-p256.apk. 591 assertVerificationFailure( 592 "v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk", 593 Issue.V2_SIG_DID_NOT_VERIFY); 594 } 595 596 @Test testV3SignatureDoesNotMatchSignedDataRejected()597 public void testV3SignatureDoesNotMatchSignedDataRejected() throws Exception { 598 // APK signed with v3 scheme only, but the signature over signed-data does not verify 599 600 // Bitflip in DSA signature. Based on v3-only-with-dsa-sha256-2048.apk. 601 assertVerificationFailure( 602 "v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk", 603 Issue.V3_SIG_DID_NOT_VERIFY); 604 605 // Bitflip in signed data. Based on v3-only-with-rsa-pkcs1-sha256-3072.apk 606 assertVerificationFailure( 607 "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk", 608 Issue.V3_SIG_DID_NOT_VERIFY); 609 610 // Based on v3-only-with-ecdsa-sha512-p521 with the signature ID changed to be ECDSA with 611 // SHA-256. 612 assertVerificationFailure( 613 "v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk", 614 Issue.V3_SIG_DID_NOT_VERIFY); 615 } 616 617 @Test testV2RsaPssSignatureDoesNotMatchSignedDataRejected()618 public void testV2RsaPssSignatureDoesNotMatchSignedDataRejected() throws Exception { 619 assumeThatRsaPssAvailable(); 620 621 // APK signed with v2 scheme only, but the signature over signed-data does not verify. 622 623 // Signature claims to be RSA PSS with SHA-256 and 32 bytes of salt, but is actually using 0 624 // bytes of salt. Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. Obtained by modifying APK 625 // signer to use the wrong amount of salt. 626 assertVerificationFailure( 627 "v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk", 628 Issue.V2_SIG_DID_NOT_VERIFY); 629 } 630 631 @Test testV2ContentDigestMismatchRejected()632 public void testV2ContentDigestMismatchRejected() throws Exception { 633 // APK signed with v2 scheme only, but the digest of contents does not match the digest 634 // stored in signed-data 635 ApkVerifier.Issue error = Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY; 636 637 // Based on v2-only-with-rsa-pkcs1-sha512-4096.apk. Obtained by modifying APK signer to 638 // flip the leftmost bit in content digest before signing signed-data. 639 assertVerificationFailure("v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk", error); 640 641 // Based on v2-only-with-ecdsa-sha256-p256.apk. Obtained by modifying APK signer to flip the 642 // leftmost bit in content digest before signing signed-data. 643 assertVerificationFailure("v2-only-with-ecdsa-sha256-p256-digest-mismatch.apk", error); 644 } 645 646 @Test testV3ContentDigestMismatchRejected()647 public void testV3ContentDigestMismatchRejected() throws Exception { 648 // APK signed with v3 scheme only, but the digest of contents does not match the digest 649 // stored in signed-data. 650 651 // Based on v3-only-with-rsa-pkcs1-sha512-8192. Obtained by flipping a bit in the local 652 // file header of the APK. 653 assertVerificationFailure( 654 "v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", 655 Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); 656 657 // Based on v3-only-with-dsa-sha256-3072.apk. Obtained by modifying APK signer to flip the 658 // leftmost bit in content digest before signing signed-data. 659 assertVerificationFailure( 660 "v3-only-with-dsa-sha256-3072-digest-mismatch.apk", 661 Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY); 662 } 663 664 @Test testNoApkSignatureSchemeBlockRejected()665 public void testNoApkSignatureSchemeBlockRejected() throws Exception { 666 // APK signed with v2 scheme only, but the rules for verifying APK Signature Scheme v2 667 // signatures say that this APK must not be verified using APK Signature Scheme v2. 668 669 // Obtained from v2-only-with-rsa-pkcs1-sha512-4096.apk by flipping a bit in the magic 670 // field in the footer of APK Signing Block. This makes the APK Signing Block disappear. 671 assertVerificationFailure( 672 "v2-only-wrong-apk-sig-block-magic.apk", Issue.JAR_SIG_NO_MANIFEST); 673 674 // Obtained by modifying APK signer to insert "GARBAGE" between ZIP Central Directory and 675 // End of Central Directory. The APK is otherwise fine and is signed with APK Signature 676 // Scheme v2. Based on v2-only-with-rsa-pkcs1-sha256.apk. 677 assertVerificationFailure( 678 "v2-only-garbage-between-cd-and-eocd.apk", Issue.JAR_SIG_NO_MANIFEST); 679 680 // Obtained by modifying the size in APK Signature Block header. Based on 681 // v2-only-with-ecdsa-sha512-p521.apk. 682 assertVerificationFailure( 683 "v2-only-apk-sig-block-size-mismatch.apk", Issue.JAR_SIG_NO_MANIFEST); 684 685 // Obtained by modifying the ID under which APK Signature Scheme v2 Block is stored in 686 // APK Signing Block and by modifying the APK signer to not insert anti-stripping 687 // protections into JAR Signature. The APK should appear as having no APK Signature Scheme 688 // v2 Block and should thus successfully verify using JAR Signature Scheme. 689 assertVerified(verify("v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk")); 690 } 691 692 @Test testNoV3ApkSignatureSchemeBlockRejected()693 public void testNoV3ApkSignatureSchemeBlockRejected() throws Exception { 694 // Obtained from v3-only-with-ecdsa-sha512-p384.apk by flipping a bit in the magic field 695 // in the footer of the APK Signing Block. 696 assertVerificationFailure( 697 "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", 698 Issue.JAR_SIG_NO_MANIFEST); 699 700 // Obtained from v3-only-with-rsa-pkcs1-sha512-4096.apk by modifying the size in the APK 701 // Signature Block header and footer. 702 assertVerificationFailure( 703 "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", 704 Issue.JAR_SIG_NO_MANIFEST); 705 } 706 707 @Test(expected = ApkFormatException.class) testTruncatedZipCentralDirectoryRejected()708 public void testTruncatedZipCentralDirectoryRejected() throws Exception { 709 // Obtained by modifying APK signer to truncate the ZIP Central Directory by one byte. The 710 // APK is otherwise fine and is signed with APK Signature Scheme v2. Based on 711 // v2-only-with-rsa-pkcs1-sha256.apk 712 verify("v2-only-truncated-cd.apk"); 713 } 714 715 @Test testV2UnknownPairIgnoredInApkSigningBlock()716 public void testV2UnknownPairIgnoredInApkSigningBlock() throws Exception { 717 // Obtained by modifying APK signer to emit an unknown ID-value pair into APK Signing Block 718 // before the ID-value pair containing the APK Signature Scheme v2 Block. The unknown 719 // ID-value should be ignored. 720 assertVerified( 721 verifyForMinSdkVersion( 722 "v2-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.N)); 723 } 724 725 @Test testV3UnknownPairIgnoredInApkSigningBlock()726 public void testV3UnknownPairIgnoredInApkSigningBlock() throws Exception { 727 // Obtained by modifying APK signer to emit an unknown ID value pair into APK Signing Block 728 // before the ID value pair containing the APK Signature Scheme v3 Block. The unknown 729 // ID value should be ignored. 730 assertVerified( 731 verifyForMinSdkVersion( 732 "v3-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.P)); 733 } 734 735 @Test testV2UnknownSignatureAlgorithmsIgnored()736 public void testV2UnknownSignatureAlgorithmsIgnored() throws Exception { 737 // APK is signed with a known signature algorithm and with a couple of unknown ones. 738 // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to 739 // known ones. 740 assertVerified( 741 verifyForMinSdkVersion( 742 "v2-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.N)); 743 } 744 745 @Test testV3UnknownSignatureAlgorithmsIgnored()746 public void testV3UnknownSignatureAlgorithmsIgnored() throws Exception { 747 // APK is signed with a known signature algorithm and a couple of unknown ones. 748 // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to 749 // known ones. 750 assertVerified( 751 verifyForMinSdkVersion( 752 "v3-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.P)); 753 } 754 755 @Test testV3WithOnlyUnknownSignatureAlgorithmsRejected()756 public void testV3WithOnlyUnknownSignatureAlgorithmsRejected() throws Exception { 757 // APK is only signed with an unknown signature algorithm. Obtained by modifying APK 758 // signer's ID for a known signature algorithm. 759 assertVerificationFailure( 760 "v3-only-no-supported-sig-algs.apk", Issue.V3_SIG_NO_SUPPORTED_SIGNATURES); 761 } 762 763 @Test testV2UnknownAdditionalAttributeIgnored()764 public void testV2UnknownAdditionalAttributeIgnored() throws Exception { 765 // APK's v2 signature contains an unknown additional attribute, but is otherwise fine. 766 // Obtained by modifying APK signer to output an additional attribute with ID 0x01020304 767 // and value 0x05060708. 768 assertVerified( 769 verifyForMinSdkVersion("v2-only-unknown-additional-attr.apk", AndroidSdkVersion.N)); 770 } 771 772 @Test testV3UnknownAdditionalAttributeIgnored()773 public void testV3UnknownAdditionalAttributeIgnored() throws Exception { 774 // APK's v3 signature contains unknown additional attributes before and after the lineage. 775 // Obtained by modifying APK signer to output additional attributes with IDs 0x11223344 776 // and 0x99aabbcc with values 0x55667788 and 0xddeeff00 777 assertVerified( 778 verifyForMinSdkVersion("v3-only-unknown-additional-attr.apk", AndroidSdkVersion.P)); 779 780 // APK's v2 and v3 signatures contain unknown additional attributes before and after the 781 // anti-stripping and lineage attributes. 782 assertVerified( 783 verifyForMinSdkVersion("v2v3-unknown-additional-attr.apk", AndroidSdkVersion.P)); 784 } 785 786 @Test testV2MismatchBetweenSignaturesAndDigestsBlockRejected()787 public void testV2MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { 788 // APK is signed with a single signature algorithm, but the digests block claims that it is 789 // signed with two different signature algorithms. Obtained by modifying APK Signer to 790 // emit an additional digest record with signature algorithm 0x12345678. 791 assertVerificationFailure( 792 "v2-only-signatures-and-digests-block-mismatch.apk", 793 Issue.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS); 794 } 795 796 @Test testV3MismatchBetweenSignaturesAndDigestsBlockRejected()797 public void testV3MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception { 798 // APK is signed with a single signature algorithm, but the digests block claims that it is 799 // signed with two different signature algorithms. Obtained by modifying APK Signer to 800 // emit an additional digest record with signature algorithm 0x11223344. 801 assertVerificationFailure( 802 "v3-only-signatures-and-digests-block-mismatch.apk", 803 Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS); 804 } 805 806 @Test testV2MismatchBetweenPublicKeyAndCertificateRejected()807 public void testV2MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { 808 // APK is signed with v2 only. The public key field does not match the public key in the 809 // leaf certificate. Obtained by modifying APK signer to write out a modified leaf 810 // certificate where the RSA modulus has a bitflip. 811 assertVerificationFailure( 812 "v2-only-cert-and-public-key-mismatch.apk", 813 Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD); 814 } 815 816 @Test testV3MismatchBetweenPublicKeyAndCertificateRejected()817 public void testV3MismatchBetweenPublicKeyAndCertificateRejected() throws Exception { 818 // APK is signed with v3 only. The public key field does not match the public key in the 819 // leaf certificate. Obtained by modifying APK signer to write out a modified leaf 820 // certificate where the RSA modulus has a bitflip. 821 assertVerificationFailure( 822 "v3-only-cert-and-public-key-mismatch.apk", 823 Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD); 824 } 825 826 @Test testV2SignerBlockWithNoCertificatesRejected()827 public void testV2SignerBlockWithNoCertificatesRejected() throws Exception { 828 // APK is signed with v2 only. There are no certificates listed in the signer block. 829 // Obtained by modifying APK signer to output no certificates. 830 assertVerificationFailure("v2-only-no-certs-in-sig.apk", Issue.V2_SIG_NO_CERTIFICATES); 831 } 832 833 @Test testV3SignerBlockWithNoCertificatesRejected()834 public void testV3SignerBlockWithNoCertificatesRejected() throws Exception { 835 // APK is signed with v3 only. There are no certificates listed in the signer block. 836 // Obtained by modifying APK signer to output no certificates. 837 assertVerificationFailure("v3-only-no-certs-in-sig.apk", Issue.V3_SIG_NO_CERTIFICATES); 838 } 839 840 @Test testTwoSignersAccepted()841 public void testTwoSignersAccepted() throws Exception { 842 // APK signed by two different signers 843 assertVerified(verify("two-signers.apk")); 844 assertVerified(verify("v1-only-two-signers.apk")); 845 assertVerified(verifyForMinSdkVersion("v2-only-two-signers.apk", AndroidSdkVersion.N)); 846 } 847 848 @Test testV2TwoSignersRejectedWhenOneBroken()849 public void testV2TwoSignersRejectedWhenOneBroken() throws Exception { 850 // Bitflip in the ECDSA signature of second signer. Based on two-signers.apk. 851 // This asserts that breakage in any signer leads to rejection of the APK. 852 assertVerificationFailure( 853 "two-signers-second-signer-v2-broken.apk", Issue.V2_SIG_DID_NOT_VERIFY); 854 } 855 856 @Test testV2TwoSignersRejectedWhenOneWithoutSignatures()857 public void testV2TwoSignersRejectedWhenOneWithoutSignatures() throws Exception { 858 // APK v2-signed by two different signers. However, there are no signatures for the second 859 // signer. 860 assertVerificationFailure( 861 "v2-only-two-signers-second-signer-no-sig.apk", Issue.V2_SIG_NO_SIGNATURES); 862 } 863 864 @Test testV2TwoSignersRejectedWhenOneWithoutSupportedSignatures()865 public void testV2TwoSignersRejectedWhenOneWithoutSupportedSignatures() throws Exception { 866 // APK v2-signed by two different signers. However, there are no supported signatures for 867 // the second signer. 868 assertVerificationFailure( 869 "v2-only-two-signers-second-signer-no-supported-sig.apk", 870 Issue.V2_SIG_NO_SUPPORTED_SIGNATURES); 871 } 872 873 @Test testV2MaxSupportedSignersAccepted()874 public void testV2MaxSupportedSignersAccepted() throws Exception { 875 // The APK Signature Scheme v2 supports a maximum of 10 signers; this test ensures an 876 // APK signed with that many signers successfully verifies. 877 assertVerified(verifyForMinSdkVersion("v2-only-10-signers.apk", AndroidSdkVersion.N)); 878 } 879 880 @Test testV2MoreThanMaxSupportedSignersRejected()881 public void testV2MoreThanMaxSupportedSignersRejected() throws Exception { 882 // This test ensure an APK signed with more than the supported number of signers fails 883 // to verify. 884 assertVerificationFailure( 885 verifyForMinSdkVersion("v2-only-11-signers.apk", AndroidSdkVersion.N), 886 Issue.V2_SIG_MAX_SIGNATURES_EXCEEDED); 887 } 888 889 890 @Test testCorrectCertUsedFromPkcs7SignedDataCertsSet()891 public void testCorrectCertUsedFromPkcs7SignedDataCertsSet() throws Exception { 892 // Obtained by prepending the rsa-1024 certificate to the PKCS#7 SignedData certificates set 893 // of v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk META-INF/CERT.RSA. The certs 894 // (in the order of appearance in the file) are thus: rsa-1024, rsa-2048. The package's 895 // signing cert is rsa-2048. 896 ApkVerifier.Result result = verify("v1-only-pkcs7-cert-bag-first-cert-not-used.apk"); 897 assertVerified(result); 898 List<X509Certificate> signingCerts = result.getSignerCertificates(); 899 assertEquals(1, signingCerts.size()); 900 assertEquals( 901 "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8", 902 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); 903 } 904 905 @Test testV1SchemeSignatureCertNotReencoded()906 public void testV1SchemeSignatureCertNotReencoded() throws Exception { 907 // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the 908 // original encoded form of signing certificates, bad things happen, such as rejection of 909 // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that 910 // PackageManager started re-encoding signing certs into DER. This normally produces exactly 911 // the original form because X.509 certificates are supposed to be DER-encoded. However, a 912 // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For 913 // such apps, re-encoding into DER changes the serialized form of the certificate, creating 914 // a mismatch with the serialized form stored in the PackageManager database, leading to the 915 // rejection of updates for the app. 916 // 917 // v1-only-with-rsa-1024-cert-not-der.apk cert's signature is not DER-encoded. It is 918 // BER-encoded, with length encoded as two bytes instead of just one. 919 // v1-only-with-rsa-1024-cert-not-der.apk META-INF/CERT.RSA was obtained from 920 // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure. 921 ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der.apk"); 922 923 // On JDK 8u131 and newer, when the default (SUN) X.509 CertificateFactory implementation is 924 // used, PKCS #7 signature verification fails because the certificate is not DER-encoded. 925 // This contrived block of code disables this test in this scenario. 926 if (!result.isVerified()) { 927 List<ApkVerifier.Result.V1SchemeSignerInfo> signers = result.getV1SchemeSigners(); 928 if (signers.size() > 0) { 929 ApkVerifier.Result.V1SchemeSignerInfo signer = signers.get(0); 930 for (IssueWithParams issue : signer.getErrors()) { 931 if (issue.getIssue() == Issue.JAR_SIG_PARSE_EXCEPTION) { 932 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 933 if ("SUN".equals(certFactory.getProvider().getName())) { 934 Throwable exception = (Throwable) issue.getParams()[1]; 935 Throwable e = exception; 936 while (e != null) { 937 String msg = e.getMessage(); 938 e = e.getCause(); 939 if ((msg != null) 940 && (msg.contains("Redundant length bytes found"))) { 941 Assume.assumeNoException(exception); 942 } 943 } 944 } 945 break; 946 } 947 } 948 } 949 } 950 951 assertVerified(result); 952 List<X509Certificate> signingCerts = result.getSignerCertificates(); 953 assertEquals(1, signingCerts.size()); 954 assertEquals( 955 "c5d4535a7e1c8111687a8374b2198da6f5ff8d811a7a25aa99ef060669342fa9", 956 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); 957 } 958 959 @Test testV1SchemeSignatureCertNotReencoded2()960 public void testV1SchemeSignatureCertNotReencoded2() throws Exception { 961 // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the 962 // original encoded form of signing certificates, bad things happen, such as rejection of 963 // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that 964 // PackageManager started re-encoding signing certs into DER. This normally produces exactly 965 // the original form because X.509 certificates are supposed to be DER-encoded. However, a 966 // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For 967 // such apps, re-encoding into DER changes the serialized form of the certificate, creating 968 // a mismatch with the serialized form stored in the PackageManager database, leading to the 969 // rejection of updates for the app. 970 // 971 // v1-only-with-rsa-1024-cert-not-der2.apk cert's signature is not DER-encoded. It is 972 // BER-encoded, with the BIT STRING value containing an extraneous leading 0x00 byte. 973 // v1-only-with-rsa-1024-cert-not-der2.apk META-INF/CERT.RSA was obtained from 974 // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure. 975 ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der2.apk"); 976 assertVerified(result); 977 List<X509Certificate> signingCerts = result.getSignerCertificates(); 978 assertEquals(1, signingCerts.size()); 979 assertEquals( 980 "da3da398de674541313deed77218ce94798531ea5131bb9b1bb4063ba4548cfb", 981 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded()))); 982 } 983 984 @Test testMaxSizedZipEocdCommentAccepted()985 public void testMaxSizedZipEocdCommentAccepted() throws Exception { 986 // Obtained by modifying apksigner to produce a max-sized (0xffff bytes long) ZIP End of 987 // Central Directory comment, and signing the original.apk using the modified apksigner. 988 assertVerified(verify("v1-only-max-sized-eocd-comment.apk")); 989 assertVerified( 990 verifyForMinSdkVersion("v2-only-max-sized-eocd-comment.apk", AndroidSdkVersion.N)); 991 } 992 993 @Test testEmptyApk()994 public void testEmptyApk() throws Exception { 995 // Unsigned empty ZIP archive 996 try { 997 verifyForMinSdkVersion("empty-unsigned.apk", 1); 998 fail("ApkFormatException should've been thrown"); 999 } catch (ApkFormatException expected) { 1000 } 1001 1002 // JAR-signed empty ZIP archive 1003 try { 1004 verifyForMinSdkVersion("v1-only-empty.apk", 18); 1005 fail("ApkFormatException should've been thrown"); 1006 } catch (ApkFormatException expected) { 1007 } 1008 1009 // APK Signature Scheme v2 signed empty ZIP archive 1010 try { 1011 verifyForMinSdkVersion("v2-only-empty.apk", AndroidSdkVersion.N); 1012 fail("ApkFormatException should've been thrown"); 1013 } catch (ApkFormatException expected) { 1014 } 1015 1016 // APK Signature Scheme v3 signed empty ZIP archive 1017 try { 1018 verifyForMinSdkVersion("v3-only-empty.apk", AndroidSdkVersion.P); 1019 fail("ApkFormatException should've been thrown"); 1020 } catch (ApkFormatException expected) { 1021 } 1022 } 1023 1024 @Test testTargetSandboxVersion2AndHigher()1025 public void testTargetSandboxVersion2AndHigher() throws Exception { 1026 // This APK (and its variants below) use minSdkVersion 18, meaning it needs to be signed 1027 // with v1 and v2 schemes 1028 1029 // This APK is signed with v1 and v2 schemes and thus should verify 1030 assertVerified(verify("targetSandboxVersion-2.apk")); 1031 1032 // v1 signature is needed only if minSdkVersion is lower than 24 1033 assertVerificationFailure( 1034 verify("v2-only-targetSandboxVersion-2.apk"), Issue.JAR_SIG_NO_MANIFEST); 1035 assertVerified(verifyForMinSdkVersion("v2-only-targetSandboxVersion-2.apk", 24)); 1036 1037 // v2 signature is required 1038 assertVerificationFailure( 1039 verify("v1-only-targetSandboxVersion-2.apk"), 1040 Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION); 1041 assertVerificationFailure( 1042 verify("unsigned-targetSandboxVersion-2.apk"), 1043 Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION); 1044 1045 // minSdkVersion 28, meaning v1 signature not needed 1046 assertVerified(verify("v2-only-targetSandboxVersion-3.apk")); 1047 } 1048 1049 @Test testTargetSdkMinSchemeVersionNotMet()1050 public void testTargetSdkMinSchemeVersionNotMet() throws Exception { 1051 // Android 11 / SDK version 30 requires apps targeting this SDK version or higher must be 1052 // signed with at least the V2 signature scheme. This test verifies if an app is targeting 1053 // this SDK version and is only signed with a V1 signature then the verifier reports the 1054 // platform will not accept it. 1055 assertVerificationFailure(verify("v1-ec-p256-targetSdk-30.apk"), 1056 Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET); 1057 } 1058 1059 @Test testTargetSdkMinSchemeVersionMet()1060 public void testTargetSdkMinSchemeVersionMet() throws Exception { 1061 // This test verifies if an app is signed with the minimum required signature scheme version 1062 // for the target SDK version then the verifier reports the platform will accept it. 1063 assertVerified(verify("v2-ec-p256-targetSdk-30.apk")); 1064 1065 // If an app is only signed with a signature scheme higher than the required version for the 1066 // target SDK the verifier should also report that the platform will accept it. 1067 assertVerified(verify("v3-ec-p256-targetSdk-30.apk")); 1068 } 1069 1070 @Test testTargetSdkMinSchemeVersionNotMetMaxLessThanTarget()1071 public void testTargetSdkMinSchemeVersionNotMetMaxLessThanTarget() throws Exception { 1072 // If the minimum signature scheme for the target SDK version is not met but the maximum 1073 // SDK version is less than the target then the verifier should report that the platform 1074 // will accept it since the specified max SDK version does not know about the minimum 1075 // signature scheme requirement. 1076 verifyForMaxSdkVersion("v1-ec-p256-targetSdk-30.apk", 29); 1077 } 1078 1079 @Test testTargetSdkNoUsesSdkElement()1080 public void testTargetSdkNoUsesSdkElement() throws Exception { 1081 // The target SDK minimum signature scheme version check will attempt to obtain the 1082 // targetSdkVersion attribute value from the uses-sdk element in the AndroidManifest. If 1083 // the targetSdkVersion is not specified then the verifier should behave the same as the 1084 // platform; the minSdkVersion should be used when available and when neither the minimum or 1085 // target SDK are specified a default value of 1 should be used. This test verifies that the 1086 // verifier does not fail when the uses-sdk element is not specified. 1087 verify("v1-only-no-uses-sdk.apk"); 1088 } 1089 1090 @Test testV1MultipleDigestAlgsInManifestAndSignatureFile()1091 public void testV1MultipleDigestAlgsInManifestAndSignatureFile() throws Exception { 1092 // MANIFEST.MF contains SHA-1 and SHA-256 digests for each entry, .SF contains only SHA-1 1093 // digests. This file was obtained by: 1094 // jarsigner -sigalg SHA256withRSA -digestalg SHA-256 ... <file> ... 1095 // jarsigner -sigalg SHA1withRSA -digestalg SHA1 ... <same file> ... 1096 assertVerified(verify("v1-sha1-sha256-manifest-and-sha1-sf.apk")); 1097 1098 // MANIFEST.MF and .SF contain SHA-1 and SHA-256 digests for each entry. This file was 1099 // obtained by modifying apksigner to output multiple digests. 1100 assertVerified(verify("v1-sha1-sha256-manifest-and-sf.apk")); 1101 1102 // One of the digests is wrong in either MANIFEST.MF or .SF. These files were obtained by 1103 // modifying apksigner to output multiple digests and to flip a bit to create a wrong 1104 // digest. 1105 1106 // SHA-1 digests in MANIFEST.MF are wrong, but SHA-256 digests are OK. 1107 // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18 1108 // and higher. 1109 assertVerificationFailure( 1110 verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk"), 1111 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); 1112 assertVerificationFailure( 1113 verifyForMaxSdkVersion( 1114 "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 17), 1115 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); 1116 assertVerified( 1117 verifyForMinSdkVersion( 1118 "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 18)); 1119 1120 // SHA-1 digests in .SF are wrong, but SHA-256 digests are OK. 1121 // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18 1122 // and higher. 1123 assertVerificationFailure( 1124 verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk"), 1125 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); 1126 assertVerificationFailure( 1127 verifyForMaxSdkVersion( 1128 "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 17), 1129 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); 1130 assertVerified( 1131 verifyForMinSdkVersion( 1132 "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 18)); 1133 1134 // SHA-256 digests in MANIFEST.MF are wrong, but SHA-1 digests are OK. 1135 // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17 1136 // and lower. 1137 assertVerificationFailure( 1138 verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk"), 1139 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); 1140 assertVerificationFailure( 1141 verifyForMinSdkVersion( 1142 "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 18), 1143 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY); 1144 assertVerified( 1145 verifyForMaxSdkVersion( 1146 "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 17)); 1147 1148 // SHA-256 digests in .SF are wrong, but SHA-1 digests are OK. 1149 // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17 1150 // and lower. 1151 assertVerificationFailure( 1152 verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk"), 1153 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); 1154 assertVerificationFailure( 1155 verifyForMinSdkVersion( 1156 "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 18), 1157 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY); 1158 assertVerified( 1159 verifyForMaxSdkVersion( 1160 "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 17)); 1161 } 1162 1163 @Test testV1WithUnsupportedCharacterInZipEntryName()1164 public void testV1WithUnsupportedCharacterInZipEntryName() throws Exception { 1165 // Android Package Manager does not support ZIP entry names containing CR or LF 1166 assertVerificationFailure( 1167 verify("v1-only-with-cr-in-entry-name.apk"), 1168 Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION); 1169 assertVerificationFailure( 1170 verify("v1-only-with-lf-in-entry-name.apk"), 1171 Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION); 1172 } 1173 1174 @Test testWeirdZipCompressionMethod()1175 public void testWeirdZipCompressionMethod() throws Exception { 1176 // Any ZIP compression method other than STORED is treated as DEFLATED by Android. 1177 // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry, 1178 // but the entry is actually Deflate-compressed. 1179 assertVerified(verify("weird-compression-method.apk")); 1180 } 1181 1182 @Test testZipCompressionMethodMismatchBetweenLfhAndCd()1183 public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception { 1184 // Android Package Manager ignores compressionMethod field in Local File Header and always 1185 // uses the compressionMethod from Central Directory instead. 1186 // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header 1187 // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed. 1188 assertVerified(verify("mismatched-compression-method.apk")); 1189 } 1190 1191 @Test testV1SignedAttrs()1192 public void testV1SignedAttrs() throws Exception { 1193 String apk = "v1-only-with-signed-attrs.apk"; 1194 assertVerificationFailure( 1195 verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2), 1196 Issue.JAR_SIG_VERIFY_EXCEPTION); 1197 assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT)); 1198 1199 apk = "v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.apk"; 1200 assertVerificationFailure( 1201 verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2), 1202 Issue.JAR_SIG_VERIFY_EXCEPTION); 1203 assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT)); 1204 } 1205 1206 @Test testV1SignedAttrsNotInDerOrder()1207 public void testV1SignedAttrsNotInDerOrder() throws Exception { 1208 // Android does not re-order SignedAttributes despite it being a SET OF. Pre-N, Android 1209 // treated them as SEQUENCE OF, meaning no re-ordering is necessary. From N onwards, it 1210 // treats them as SET OF, but does not re-encode into SET OF during verification if all 1211 // attributes parsed fine. 1212 assertVerified(verify("v1-only-with-signed-attrs-wrong-order.apk")); 1213 assertVerified( 1214 verify("v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-good.apk")); 1215 } 1216 1217 @Test testV1SignedAttrsMissingContentType()1218 public void testV1SignedAttrsMissingContentType() throws Exception { 1219 // SignedAttributes must contain ContentType. Pre-N, Android ignores this requirement. 1220 // Android N onwards rejects such APKs. 1221 String apk = "v1-only-with-signed-attrs-missing-content-type.apk"; 1222 assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); 1223 assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION); 1224 // Assert that this issue fails verification of the entire signature block, rather than 1225 // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but 1226 // verification does not get there. 1227 apk = "v1-only-with-signed-attrs-signerInfo1-missing-content-type-signerInfo2-good.apk"; 1228 assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); 1229 assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION); 1230 } 1231 1232 @Test testV1SignedAttrsWrongContentType()1233 public void testV1SignedAttrsWrongContentType() throws Exception { 1234 // ContentType of SignedAttributes must equal SignedData.encapContentInfo.eContentType. 1235 // Pre-N, Android ignores this requirement. 1236 // From N onwards, Android rejects such SignerInfos. 1237 String apk = "v1-only-with-signed-attrs-wrong-content-type.apk"; 1238 assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); 1239 assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY); 1240 // First SignerInfo does not verify on Android N and newer, but verification moves on to the 1241 // second SignerInfo, which verifies. 1242 apk = "v1-only-with-signed-attrs-signerInfo1-wrong-content-type-signerInfo2-good.apk"; 1243 assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1)); 1244 assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); 1245 // Although the APK's signature verifies on pre-N and N+, we reject such APKs because the 1246 // APK's verification results in different verified SignerInfos (and thus potentially 1247 // different signing certs) between pre-N and N+. 1248 assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY); 1249 } 1250 1251 @Test testV1SignedAttrsMissingDigest()1252 public void testV1SignedAttrsMissingDigest() throws Exception { 1253 // Content digest must be present in SignedAttributes 1254 String apk = "v1-only-with-signed-attrs-missing-digest.apk"; 1255 assertVerificationFailure( 1256 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), 1257 Issue.JAR_SIG_VERIFY_EXCEPTION); 1258 assertVerificationFailure( 1259 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION); 1260 // Assert that this issue fails verification of the entire signature block, rather than 1261 // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but 1262 // verification does not get there. 1263 apk = "v1-only-with-signed-attrs-signerInfo1-missing-digest-signerInfo2-good.apk"; 1264 assertVerificationFailure( 1265 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), 1266 Issue.JAR_SIG_VERIFY_EXCEPTION); 1267 assertVerificationFailure( 1268 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION); 1269 } 1270 1271 @Test testV1SignedAttrsMultipleGoodDigests()1272 public void testV1SignedAttrsMultipleGoodDigests() throws Exception { 1273 // Only one content digest must be present in SignedAttributes 1274 String apk = "v1-only-with-signed-attrs-multiple-good-digests.apk"; 1275 assertVerificationFailure( 1276 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), 1277 Issue.JAR_SIG_PARSE_EXCEPTION); 1278 assertVerificationFailure( 1279 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION); 1280 // Assert that this issue fails verification of the entire signature block, rather than 1281 // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but 1282 // verification does not get there. 1283 apk = "v1-only-with-signed-attrs-signerInfo1-multiple-good-digests-signerInfo2-good.apk"; 1284 assertVerificationFailure( 1285 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), 1286 Issue.JAR_SIG_PARSE_EXCEPTION); 1287 assertVerificationFailure( 1288 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION); 1289 } 1290 1291 @Test testV1SignedAttrsWrongDigest()1292 public void testV1SignedAttrsWrongDigest() throws Exception { 1293 // Content digest in SignedAttributes does not match the contents 1294 String apk = "v1-only-with-signed-attrs-wrong-digest.apk"; 1295 assertVerificationFailure( 1296 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); 1297 assertVerificationFailure( 1298 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY); 1299 // First SignerInfo does not verify, but Android N and newer moves on to the second 1300 // SignerInfo, which verifies. 1301 apk = "v1-only-with-signed-attrs-signerInfo1-wrong-digest-signerInfo2-good.apk"; 1302 assertVerificationFailure( 1303 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); 1304 assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); 1305 } 1306 1307 @Test testV1SignedAttrsWrongSignature()1308 public void testV1SignedAttrsWrongSignature() throws Exception { 1309 // Signature over SignedAttributes does not verify 1310 String apk = "v1-only-with-signed-attrs-wrong-signature.apk"; 1311 assertVerificationFailure( 1312 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); 1313 assertVerificationFailure( 1314 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY); 1315 // First SignerInfo does not verify, but Android N and newer moves on to the second 1316 // SignerInfo, which verifies. 1317 apk = "v1-only-with-signed-attrs-signerInfo1-wrong-signature-signerInfo2-good.apk"; 1318 assertVerificationFailure( 1319 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY); 1320 assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N)); 1321 } 1322 1323 @Test testSourceStampBlock_correctSignature()1324 public void testSourceStampBlock_correctSignature() throws Exception { 1325 ApkVerifier.Result verificationResult = verify("valid-stamp.apk"); 1326 // Verifies the signature of the APK. 1327 assertVerified(verificationResult); 1328 // Verifies the signature of source stamp. 1329 assertTrue(verificationResult.isSourceStampVerified()); 1330 } 1331 1332 @Test verifySourceStamp_correctSignature()1333 public void verifySourceStamp_correctSignature() throws Exception { 1334 ApkVerifier.Result verificationResult = verifySourceStamp("valid-stamp.apk"); 1335 // Since the API is only verifying the source stamp the result itself should be marked as 1336 // verified. 1337 assertVerified(verificationResult); 1338 assertSourceStampVerificationStatus(verificationResult, 1339 SourceStampVerificationStatus.STAMP_VERIFIED); 1340 1341 // The source stamp can also be verified by platform version; confirm the verification works 1342 // using just the max signature scheme version supported by that platform version. 1343 verificationResult = verifySourceStamp("valid-stamp.apk", 18, 18); 1344 assertVerified(verificationResult); 1345 assertSourceStampVerificationStatus(verificationResult, 1346 SourceStampVerificationStatus.STAMP_VERIFIED); 1347 1348 verificationResult = verifySourceStamp("valid-stamp.apk", 24, 24); 1349 assertVerified(verificationResult); 1350 assertSourceStampVerificationStatus(verificationResult, 1351 SourceStampVerificationStatus.STAMP_VERIFIED); 1352 1353 verificationResult = verifySourceStamp("valid-stamp.apk", 28, 28); 1354 assertVerified(verificationResult); 1355 assertSourceStampVerificationStatus(verificationResult, 1356 SourceStampVerificationStatus.STAMP_VERIFIED); 1357 } 1358 1359 @Test testSourceStampBlock_signatureMissing()1360 public void testSourceStampBlock_signatureMissing() throws Exception { 1361 ApkVerifier.Result verificationResult = verify("stamp-without-block.apk"); 1362 // A broken stamp should not block a signing scheme verified APK. 1363 assertVerified(verificationResult); 1364 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING); 1365 } 1366 1367 @Test verifySourceStamp_signatureMissing()1368 public void verifySourceStamp_signatureMissing() throws Exception { 1369 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-without-block.apk"); 1370 assertSourceStampVerificationStatus(verificationResult, 1371 SourceStampVerificationStatus.STAMP_NOT_VERIFIED); 1372 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING); 1373 } 1374 1375 @Test testSourceStampBlock_certificateMismatch()1376 public void testSourceStampBlock_certificateMismatch() throws Exception { 1377 ApkVerifier.Result verificationResult = verify("stamp-certificate-mismatch.apk"); 1378 // A broken stamp should not block a signing scheme verified APK. 1379 assertVerified(verificationResult); 1380 assertSourceStampVerificationFailure( 1381 verificationResult, 1382 Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); 1383 } 1384 1385 @Test verifySourceStamp_certificateMismatch()1386 public void verifySourceStamp_certificateMismatch() throws Exception { 1387 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-certificate-mismatch.apk"); 1388 assertSourceStampVerificationStatus(verificationResult, 1389 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1390 assertSourceStampVerificationFailure( 1391 verificationResult, 1392 Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK); 1393 } 1394 1395 @Test testSourceStampBlock_v1OnlySignatureValidStamp()1396 public void testSourceStampBlock_v1OnlySignatureValidStamp() throws Exception { 1397 ApkVerifier.Result verificationResult = verify("v1-only-with-stamp.apk"); 1398 assertVerified(verificationResult); 1399 assertTrue(verificationResult.isSourceStampVerified()); 1400 } 1401 1402 @Test verifySourceStamp_v1OnlySignatureValidStamp()1403 public void verifySourceStamp_v1OnlySignatureValidStamp() throws Exception { 1404 ApkVerifier.Result verificationResult = verifySourceStamp("v1-only-with-stamp.apk"); 1405 assertVerified(verificationResult); 1406 assertSourceStampVerificationStatus(verificationResult, 1407 SourceStampVerificationStatus.STAMP_VERIFIED); 1408 1409 // Confirm that the source stamp verification succeeds when specifying platform versions 1410 // that supported later signature scheme versions. 1411 verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 28, 28); 1412 assertVerified(verificationResult); 1413 assertSourceStampVerificationStatus(verificationResult, 1414 SourceStampVerificationStatus.STAMP_VERIFIED); 1415 1416 verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 24, 24); 1417 assertVerified(verificationResult); 1418 assertSourceStampVerificationStatus(verificationResult, 1419 SourceStampVerificationStatus.STAMP_VERIFIED); 1420 } 1421 1422 @Test testSourceStampBlock_v2OnlySignatureValidStamp()1423 public void testSourceStampBlock_v2OnlySignatureValidStamp() throws Exception { 1424 ApkVerifier.Result verificationResult = verify("v2-only-with-stamp.apk"); 1425 assertVerified(verificationResult); 1426 assertTrue(verificationResult.isSourceStampVerified()); 1427 } 1428 1429 @Test verifySourceStamp_v2OnlySignatureValidStamp()1430 public void verifySourceStamp_v2OnlySignatureValidStamp() throws Exception { 1431 ApkVerifier.Result verificationResult = verifySourceStamp("v2-only-with-stamp.apk"); 1432 assertVerified(verificationResult); 1433 assertSourceStampVerificationStatus(verificationResult, 1434 SourceStampVerificationStatus.STAMP_VERIFIED); 1435 1436 // Confirm that the source stamp verification succeeds when specifying a platform version 1437 // that supports a later signature scheme version. 1438 verificationResult = verifySourceStamp("v2-only-with-stamp.apk", 28, 28); 1439 assertVerified(verificationResult); 1440 assertSourceStampVerificationStatus(verificationResult, 1441 SourceStampVerificationStatus.STAMP_VERIFIED); 1442 } 1443 1444 @Test testSourceStampBlock_v3OnlySignatureValidStamp()1445 public void testSourceStampBlock_v3OnlySignatureValidStamp() throws Exception { 1446 ApkVerifier.Result verificationResult = verify("v3-only-with-stamp.apk"); 1447 assertVerified(verificationResult); 1448 assertTrue(verificationResult.isSourceStampVerified()); 1449 } 1450 1451 @Test verifySourceStamp_v3OnlySignatureValidStamp()1452 public void verifySourceStamp_v3OnlySignatureValidStamp() throws Exception { 1453 ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk"); 1454 assertVerified(verificationResult); 1455 assertSourceStampVerificationStatus(verificationResult, 1456 SourceStampVerificationStatus.STAMP_VERIFIED); 1457 } 1458 1459 @Test testSourceStampBlock_apkHashMismatch_v1SignatureScheme()1460 public void testSourceStampBlock_apkHashMismatch_v1SignatureScheme() throws Exception { 1461 ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v1.apk"); 1462 // A broken stamp should not block a signing scheme verified APK. 1463 assertVerified(verificationResult); 1464 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1465 } 1466 1467 @Test verifySourceStamp_apkHashMismatch_v1SignatureScheme()1468 public void verifySourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception { 1469 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v1.apk"); 1470 assertSourceStampVerificationStatus(verificationResult, 1471 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1472 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1473 } 1474 1475 @Test testSourceStampBlock_apkHashMismatch_v2SignatureScheme()1476 public void testSourceStampBlock_apkHashMismatch_v2SignatureScheme() throws Exception { 1477 ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v2.apk"); 1478 // A broken stamp should not block a signing scheme verified APK. 1479 assertVerified(verificationResult); 1480 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1481 } 1482 1483 @Test verifySourceStamp_apkHashMismatch_v2SignatureScheme()1484 public void verifySourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception { 1485 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v2.apk"); 1486 assertSourceStampVerificationStatus(verificationResult, 1487 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1488 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1489 } 1490 1491 @Test testSourceStampBlock_apkHashMismatch_v3SignatureScheme()1492 public void testSourceStampBlock_apkHashMismatch_v3SignatureScheme() throws Exception { 1493 ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v3.apk"); 1494 // A broken stamp should not block a signing scheme verified APK. 1495 assertVerified(verificationResult); 1496 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1497 } 1498 1499 @Test verifySourceStamp_apkHashMismatch_v3SignatureScheme()1500 public void verifySourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception { 1501 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v3.apk"); 1502 assertSourceStampVerificationStatus(verificationResult, 1503 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1504 assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1505 } 1506 1507 @Test testSourceStampBlock_malformedSignature()1508 public void testSourceStampBlock_malformedSignature() throws Exception { 1509 ApkVerifier.Result verificationResult = verify("stamp-malformed-signature.apk"); 1510 // A broken stamp should not block a signing scheme verified APK. 1511 assertVerified(verificationResult); 1512 assertSourceStampVerificationFailure( 1513 verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); 1514 } 1515 1516 @Test verifySourceStamp_malformedSignature()1517 public void verifySourceStamp_malformedSignature() throws Exception { 1518 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-malformed-signature.apk"); 1519 assertSourceStampVerificationStatus(verificationResult, 1520 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1521 assertSourceStampVerificationFailure( 1522 verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE); 1523 } 1524 1525 @Test verifySourceStamp_expectedDigestMatchesActual()1526 public void verifySourceStamp_expectedDigestMatchesActual() throws Exception { 1527 // The ApkVerifier provides an API to specify the expected certificate digest; this test 1528 // verifies that the test runs through to completion when the actual digest matches the 1529 // provided value. 1530 ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", 1531 RSA_2048_CERT_SHA256_DIGEST); 1532 assertVerified(verificationResult); 1533 assertSourceStampVerificationStatus(verificationResult, 1534 SourceStampVerificationStatus.STAMP_VERIFIED); 1535 } 1536 1537 @Test verifySourceStamp_expectedDigestMismatch()1538 public void verifySourceStamp_expectedDigestMismatch() throws Exception { 1539 // If the caller requests source stamp verification with an expected cert digest that does 1540 // not match the actual digest in the APK the verifier should report the mismatch. 1541 ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", 1542 EC_P256_CERT_SHA256_DIGEST); 1543 assertSourceStampVerificationStatus(verificationResult, 1544 SourceStampVerificationStatus.CERT_DIGEST_MISMATCH); 1545 assertSourceStampVerificationFailure(verificationResult, 1546 Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH); 1547 } 1548 1549 @Test verifySourceStamp_validStampLineage()1550 public void verifySourceStamp_validStampLineage() throws Exception { 1551 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-valid.apk"); 1552 assertVerified(verificationResult); 1553 assertSourceStampVerificationStatus(verificationResult, 1554 SourceStampVerificationStatus.STAMP_VERIFIED); 1555 } 1556 1557 @Test verifySourceStamp_invalidStampLineage()1558 public void verifySourceStamp_invalidStampLineage() throws Exception { 1559 ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-invalid.apk"); 1560 assertSourceStampVerificationStatus(verificationResult, 1561 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1562 assertSourceStampVerificationFailure(verificationResult, 1563 Issue.SOURCE_STAMP_POR_CERT_MISMATCH); 1564 } 1565 1566 @Test verifySourceStamp_noTimestamp_returnsDefaultValue()1567 public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception { 1568 // A timestamp attribute was added to the source stamp, but verification of APKs that were 1569 // generated prior to the addition of the timestamp should still complete successfully, 1570 // returning a default value of 0 for the timestamp. 1571 ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk"); 1572 1573 assertTrue(verificationResult.isSourceStampVerified()); 1574 assertEquals( 1575 "A value of 0 should be returned for the timestamp when the attribute is not " 1576 + "present", 1577 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); 1578 } 1579 1580 @Test verifySourceStamp_validTimestamp_returnsExpectedValue()1581 public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception { 1582 // Once an APK is signed with a source stamp that contains a valid value for the timestamp 1583 // attribute, verification of the source stamp should result in the same value for the 1584 // timestamp returned to the verifier. 1585 ApkVerifier.Result verificationResult = verifySourceStamp( 1586 "stamp-valid-timestamp-value.apk"); 1587 1588 assertTrue(verificationResult.isSourceStampVerified()); 1589 assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); 1590 } 1591 1592 @Test verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue()1593 public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue() 1594 throws Exception { 1595 // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer 1596 // as a little-endian long; while a larger buffer will not result in an error, any 1597 // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a 1598 // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read 1599 // successfully. 1600 ApkVerifier.Result verificationResult = verifySourceStamp( 1601 "stamp-valid-timestamp-16-byte-buffer.apk"); 1602 1603 assertTrue(verificationResult.isSourceStampVerified()); 1604 assertEquals(1645126786, 1605 verificationResult.getSourceStampInfo().getTimestampEpochSeconds()); 1606 } 1607 1608 @Test verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails()1609 public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails() 1610 throws Exception { 1611 // If the source stamp timestamp attribute exists and is <= 0, then a warning should be 1612 // reported to notify the caller to the invalid attribute value. This test verifies a 1613 // a warning is reported when the timestamp attribute value is 0. 1614 ApkVerifier.Result verificationResult = verifySourceStamp( 1615 "stamp-invalid-timestamp-value-zero.apk"); 1616 1617 assertSourceStampVerificationStatus(verificationResult, 1618 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1619 assertSourceStampVerificationFailure(verificationResult, 1620 Issue.SOURCE_STAMP_INVALID_TIMESTAMP); 1621 } 1622 1623 @Test verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails()1624 public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails() 1625 throws Exception { 1626 // If the source stamp timestamp attribute exists and is <= 0, then a warning should be 1627 // reported to notify the caller to the invalid attribute value. This test verifies a 1628 // a warning is reported when the timestamp attribute value is < 0. 1629 ApkVerifier.Result verificationResult = verifySourceStamp( 1630 "stamp-invalid-timestamp-value-less-than-zero.apk"); 1631 1632 assertSourceStampVerificationStatus(verificationResult, 1633 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1634 assertSourceStampVerificationFailure(verificationResult, 1635 Issue.SOURCE_STAMP_INVALID_TIMESTAMP); 1636 } 1637 1638 @Test verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails()1639 public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails() 1640 throws Exception { 1641 // The source stamp's timestamp attribute value is expected to be written to the first 8 1642 // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp 1643 // value is not written as a little-endian long to the first 8 bytes of the buffer, then 1644 // an error should be reported for the timestamp attribute since the rest of the buffer will 1645 // be ignored. 1646 ApkVerifier.Result verificationResult = verifySourceStamp( 1647 "stamp-timestamp-in-last-8-of-16-byte-buffer.apk"); 1648 1649 assertSourceStampVerificationStatus(verificationResult, 1650 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1651 assertSourceStampVerificationFailure(verificationResult, 1652 Issue.SOURCE_STAMP_INVALID_TIMESTAMP); 1653 } 1654 1655 1656 @Test verifySourceStamp_intTimestampValue_verificationFails()1657 public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception { 1658 // Since the source stamp timestamp attribute value is a long, an attribute value with 1659 // insufficient space to hold a long value should result in a warning reported to the user. 1660 ApkVerifier.Result verificationResult = verifySourceStamp( 1661 "stamp-int-timestamp-value.apk"); 1662 1663 assertSourceStampVerificationStatus(verificationResult, 1664 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1665 assertSourceStampVerificationFailure(verificationResult, 1666 Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE); 1667 } 1668 1669 @Test verifySourceStamp_modifiedTimestampValue_verificationFails()1670 public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception { 1671 // The source stamp timestamp attribute is part of the block's signed data; this test 1672 // verifies if the value of the timestamp in the stamp block is modified then verification 1673 // of the source stamp should fail. 1674 ApkVerifier.Result verificationResult = verifySourceStamp( 1675 "stamp-valid-timestamp-value-modified.apk"); 1676 1677 assertSourceStampVerificationStatus(verificationResult, 1678 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED); 1679 assertSourceStampVerificationFailure(verificationResult, 1680 Issue.SOURCE_STAMP_DID_NOT_VERIFY); 1681 } 1682 1683 @Test apkVerificationIssueAdapter_verifyAllBaseIssuesMapped()1684 public void apkVerificationIssueAdapter_verifyAllBaseIssuesMapped() throws Exception { 1685 Field[] fields = ApkVerificationIssue.class.getFields(); 1686 StringBuilder msg = new StringBuilder(); 1687 for (Field field : fields) { 1688 // All public static int fields in the ApkVerificationIssue class should be issue IDs; 1689 // if any are added that are not intended as IDs a filter set should be applied to this 1690 // test. 1691 if (Modifier.isStatic(field.getModifiers()) && field.getType() == int.class) { 1692 if (!ApkVerifier.ApkVerificationIssueAdapter 1693 .sVerificationIssueIdToIssue.containsKey(field.get(null))) { 1694 if (msg.length() > 0) { 1695 msg.append('\n'); 1696 } 1697 msg.append( 1698 "A mapping is required from ApkVerificationIssue." + field.getName() 1699 + " to an ApkVerifier.Issue in ApkVerificationIssueAdapter"); 1700 } 1701 } 1702 } 1703 if (msg.length() > 0) { 1704 fail(msg.toString()); 1705 } 1706 } 1707 1708 @Test verifySignature_negativeModulusConscryptProvider()1709 public void verifySignature_negativeModulusConscryptProvider() throws Exception { 1710 Provider conscryptProvider = null; 1711 try { 1712 conscryptProvider = new org.conscrypt.OpenSSLProvider(); 1713 Security.insertProviderAt(conscryptProvider, 1); 1714 assertVerified(verify("v1v2v3-rsa-2048-negmod-in-cert.apk")); 1715 } catch (UnsatisfiedLinkError e) { 1716 // If the library for conscrypt is not available then skip this test. 1717 return; 1718 } finally { 1719 if (conscryptProvider != null) { 1720 Security.removeProvider(conscryptProvider.getName()); 1721 } 1722 } 1723 } 1724 1725 @Test verifyV31_rotationTarget34_containsExpectedSigners()1726 public void verifyV31_rotationTarget34_containsExpectedSigners() throws Exception { 1727 // This test verifies an APK targeting a specific SDK version for rotation properly reports 1728 // that version for the rotated signer in the v3.1 block, and all other signing blocks 1729 // use the original signing key. The target is set to 10000 to prevent test failures when 1730 // SDK version 34 is set as the development release. 1731 ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-10000-1-tgt-28.apk"); 1732 1733 assertVerified(result); 1734 assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1735 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1736 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); 1737 } 1738 1739 @Test verifyV31_missingStrippingAttr_warningReported()1740 public void verifyV31_missingStrippingAttr_warningReported() throws Exception { 1741 // The v3.1 signing block supports targeting SDK versions; to protect against these target 1742 // versions being modified the v3 signer contains a stripping protection attribute with the 1743 // SDK version on which rotation should be applied. This test verifies a warning is reported 1744 // when this attribute is not present in the v3 signer. 1745 ApkVerifier.Result result = verify("v31-tgt-33-no-v3-attr.apk"); 1746 1747 assertVerificationWarning(result, Issue.V31_ROTATION_MIN_SDK_ATTR_MISSING); 1748 } 1749 1750 @Test verifyV31_strippingAttrMismatch_errorReportedOnSupportedVersions()1751 public void verifyV31_strippingAttrMismatch_errorReportedOnSupportedVersions() 1752 throws Exception { 1753 // This test verifies if the stripping protection attribute does not properly match the 1754 // minimum SDK version on which rotation is supported then the APK should fail verification. 1755 ApkVerifier.Result result = verify("v31-tgt-34-v3-attr-value-33.apk"); 1756 assertVerificationFailure(result, Issue.V31_ROTATION_MIN_SDK_MISMATCH); 1757 1758 // SDK versions that do not support v3.1 should ignore the stripping protection attribute 1759 // and the v3.1 signing block. 1760 result = verifyForMaxSdkVersion("v31-tgt-34-v3-attr-value-33.apk", 1761 V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); 1762 assertVerified(result); 1763 } 1764 1765 @Test verifyV31_missingV31Block_errorReportedOnSupportedVersions()1766 public void verifyV31_missingV31Block_errorReportedOnSupportedVersions() throws Exception { 1767 // This test verifies if the stripping protection attribute contains a value for rotation 1768 // but a v3.1 signing block was not found then the APK should fail verification. 1769 ApkVerifier.Result result = verify("v31-block-stripped-v3-attr-value-33.apk"); 1770 assertVerificationFailure(result, Issue.V31_BLOCK_MISSING); 1771 1772 // SDK versions that do not support v3.1 should ignore the stripping protection attribute 1773 // and the v3.1 signing block. 1774 result = verifyForMaxSdkVersion("v31-block-stripped-v3-attr-value-33.apk", 1775 V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT - 1); 1776 assertVerified(result); 1777 } 1778 1779 @Test verifyV31_v31BlockWithoutV3Block_reportsError()1780 public void verifyV31_v31BlockWithoutV3Block_reportsError() throws Exception { 1781 // A v3.1 block must always exist alongside a v3.0 block; if an APK's minSdkVersion is the 1782 // same as the version supporting rotation then it should be written to a v3.0 block. 1783 ApkVerifier.Result result = verify("v31-tgt-33-no-v3-block.apk"); 1784 assertVerificationFailure(result, Issue.V31_BLOCK_FOUND_WITHOUT_V3_BLOCK); 1785 } 1786 1787 @Test verifyV31_rotationTargetsDevRelease_resultReportsDevReleaseFlag()1788 public void verifyV31_rotationTargetsDevRelease_resultReportsDevReleaseFlag() throws Exception { 1789 // Development releases use the SDK version of the previous release until the SDK is 1790 // finalized. In order to only target the development release and later, the v3.1 signature 1791 // scheme supports targeting development releases such that the SDK version X will install 1792 // on a device running X with the system property ro.build.version.codename set to a new 1793 // development codename (eg T); a release platform will have this set to "REL", and the 1794 // platform will ignore the v3.1 signer if the minSdkVersion is X and the codename is "REL". 1795 // The target is set to 10000 to prevent test failures when SDK version 34 is set as the 1796 // development release. 1797 ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-10000-dev-release.apk"); 1798 1799 assertVerified(result); 1800 assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000); 1801 assertEquals(1, result.getV31SchemeSigners().size()); 1802 assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease()); 1803 assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1804 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1805 } 1806 1807 @Test verifyV3_v3RotatedSignerTargetsDevRelease_warningReported()1808 public void verifyV3_v3RotatedSignerTargetsDevRelease_warningReported() throws Exception { 1809 // While a v3.1 signer can target a development release, v3.0 does not support the same 1810 // attribute since it is only intended for v3.1 with v3.0 using the original signer. This 1811 // test verifies a warning is reported if an APK has this flag set on a v3.0 signer since it 1812 // will be ignored by the platform. 1813 ApkVerifier.Result result = verify("v3-rsa-2048_2-tgt-dev-release.apk"); 1814 1815 assertVerificationWarning(result, Issue.V31_ROTATION_TARGETS_DEV_RELEASE_ATTR_ON_V3_SIGNER); 1816 } 1817 1818 @Test verifyV31_rotationTargets34_resultContainsExpectedLineage()1819 public void verifyV31_rotationTargets34_resultContainsExpectedLineage() throws Exception { 1820 // During verification of the v3.1 and v3.0 signing blocks, ApkVerifier will set the 1821 // signing certificate lineage in the Result object; this test verifies a null lineage from 1822 // a v3.0 signer does not overwrite a valid lineage from a v3.1 signer. 1823 ApkVerifier.Result result = verify("v31-rsa-2048_2-tgt-34-1-tgt-28.apk"); 1824 1825 assertNotNull(result.getSigningCertificateLineage()); 1826 SigningCertificateLineageTest.assertLineageContainsExpectedSigners( 1827 result.getSigningCertificateLineage(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME, 1828 SECOND_RSA_2048_SIGNER_RESOURCE_NAME); 1829 } 1830 1831 @Test verify31_minSdkVersionT_resultSuccessfullyVerified()1832 public void verify31_minSdkVersionT_resultSuccessfullyVerified() throws Exception { 1833 // When a min-sdk-version of 33 is explicitly specified, apksig will behave the same as a 1834 // device running this API level and only verify a v3.1 signature if it exists. This test 1835 // verifies this v3.1 signature is sufficient to report the APK as verified. 1836 ApkVerifier.Result result = verifyForMinSdkVersion("v31-rsa-2048_2-tgt-33-1-tgt-28.apk", 1837 33); 1838 1839 assertVerified(result); 1840 assertTrue(result.isVerifiedUsingV31Scheme()); 1841 } 1842 1843 @Test verify31_minSdkVersionTTargetSdk30_resultSuccessfullyVerified()1844 public void verify31_minSdkVersionTTargetSdk30_resultSuccessfullyVerified() throws Exception { 1845 // This test verifies when a min-sdk-version of 33 is specified and the APK targets API 1846 // level 30 or later, the v3.1 signature is sufficient to report the APK meets the 1847 // requirement of a minimum v2 signature. 1848 ApkVerifier.Result result = verifyForMinSdkVersion( 1849 "v31-ec-p256-2-tgt-33-1-tgt-28-targetSdk-30.apk", 33); 1850 1851 assertVerified(result); 1852 assertTrue(result.isVerifiedUsingV31Scheme()); 1853 } 1854 1855 @Test verify41_v41DigestMismatchedWithV31_reportsError()1856 public void verify41_v41DigestMismatchedWithV31_reportsError() throws Exception { 1857 // This test verifies a digest mismatch between the v4.1 signature and the v3.1 signature 1858 // is properly reported during v4 signature verification. 1859 ApkVerifier.Result result = verifyWithV4Signature("v41-digest-mismatched-with-v31.apk", 1860 "v41-digest-mismatched-with-v31.apk.idsig"); 1861 1862 assertVerificationFailure(result, Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH); 1863 } 1864 1865 @Test(expected = IOException.class) verify_largeFileSize_doesNotFailWithOOMError()1866 public void verify_largeFileSize_doesNotFailWithOOMError() throws Exception { 1867 // TODO(b/319479290) make the test run with a specific max heap size 1868 assumeTrue(Runtime.getRuntime().maxMemory() < 2016310387L); // 2gb 1869 // During V1 signature verification, each file needs to be uncompressed to calculate 1870 // its digest; the verifier uses the file size from the central directory record to 1871 // determine the size of the byte[] to allocate. If there is not sufficient memory 1872 // in the heap for the allocation, the verification should fail with an exception 1873 // instead of an OutOfMemoryError. This test uses an APK where the size of the 1874 // MANIFEST.MF is reported as 2016310387. 1875 verify("incorrect-manifest-size.apk"); 1876 } 1877 1878 @Test(expected = ApkFormatException.class) 1879 public void verify_invalidApk_throwsApkFormatException() throws Exception { 1880 // This is just some random bytes and thus an invalid manifest 1881 verify("invalid_manifest.apk"); 1882 } 1883 1884 @Test 1885 public void compareMatchingDigests() throws Exception { 1886 Map<ContentDigestAlgorithm, byte[]> firstDigest = new HashMap<>(); 1887 firstDigest.put(ContentDigestAlgorithm.SHA256, 1888 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1889 firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1890 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1891 1892 Map<ContentDigestAlgorithm, byte[]> secondDigest = new HashMap<>(); 1893 secondDigest.put(ContentDigestAlgorithm.SHA256, 1894 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1895 secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1896 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1897 1898 assertTrue(ApkVerifier.compareDigests(firstDigest, secondDigest)); 1899 } 1900 1901 @Test 1902 public void compareMatchingIntersectionDigests() throws Exception { 1903 Map<ContentDigestAlgorithm, byte[]> firstDigest = new HashMap<>(); 1904 firstDigest.put(ContentDigestAlgorithm.SHA256, 1905 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1906 firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1907 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1908 1909 Map<ContentDigestAlgorithm, byte[]> secondDigest = new HashMap<>(); 1910 secondDigest.put(ContentDigestAlgorithm.SHA256, 1911 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1912 secondDigest.put(ContentDigestAlgorithm.VERITY_CHUNKED_SHA256, 1913 RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK 1914 .getBytes(StandardCharsets.UTF_8)); 1915 1916 assertTrue(ApkVerifier.compareDigests(firstDigest, secondDigest)); 1917 } 1918 1919 @Test 1920 public void compareNoIntersectionDigests() throws Exception { 1921 Map<ContentDigestAlgorithm, byte[]> firstDigest = new HashMap<>(); 1922 firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1923 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1924 1925 Map<ContentDigestAlgorithm, byte[]> secondDigest = new HashMap<>(); 1926 secondDigest.put(ContentDigestAlgorithm.SHA256, 1927 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1928 1929 assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); 1930 } 1931 1932 @Test 1933 public void compareNotMatchingDigests() throws Exception { 1934 Map<ContentDigestAlgorithm, byte[]> firstDigest = new HashMap<>(); 1935 firstDigest.put(ContentDigestAlgorithm.SHA256, 1936 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1937 firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1938 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1939 1940 Map<ContentDigestAlgorithm, byte[]> secondDigest = new HashMap<>(); 1941 secondDigest.put(ContentDigestAlgorithm.SHA256, 1942 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1943 secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1944 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1945 1946 assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); 1947 } 1948 1949 @Test 1950 public void comparePartiallyNotMatchingDigests() throws Exception { 1951 Map<ContentDigestAlgorithm, byte[]> firstDigest = new HashMap<>(); 1952 firstDigest.put(ContentDigestAlgorithm.SHA256, 1953 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1954 firstDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1955 RSA_2048_CERT_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1956 1957 Map<ContentDigestAlgorithm, byte[]> secondDigest = new HashMap<>(); 1958 secondDigest.put(ContentDigestAlgorithm.SHA256, 1959 RSA_2048_CHUNKED_SHA256_DIGEST.getBytes(StandardCharsets.UTF_8)); 1960 secondDigest.put(ContentDigestAlgorithm.CHUNKED_SHA256, 1961 RSA_2048_CHUNKED_SHA256_DIGEST_FROM_INCORRECTLY_SIGNED_APK 1962 .getBytes(StandardCharsets.UTF_8)); 1963 1964 assertTrue(!ApkVerifier.compareDigests(firstDigest, secondDigest)); 1965 } 1966 1967 private ApkVerifier.Result verify(String apkFilenameInResources) 1968 throws IOException, ApkFormatException, NoSuchAlgorithmException { 1969 return verify(apkFilenameInResources, null, null); 1970 } 1971 1972 private ApkVerifier.Result verifyForMinSdkVersion( 1973 String apkFilenameInResources, int minSdkVersion) 1974 throws IOException, ApkFormatException, NoSuchAlgorithmException { 1975 return verify(apkFilenameInResources, minSdkVersion, null); 1976 } 1977 1978 private ApkVerifier.Result verifyForMaxSdkVersion( 1979 String apkFilenameInResources, int maxSdkVersion) 1980 throws IOException, ApkFormatException, NoSuchAlgorithmException { 1981 return verify(apkFilenameInResources, null, maxSdkVersion); 1982 } 1983 1984 private ApkVerifier.Result verify( 1985 String apkFilenameInResources, 1986 Integer minSdkVersionOverride, 1987 Integer maxSdkVersionOverride) 1988 throws IOException, ApkFormatException, NoSuchAlgorithmException { 1989 byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); 1990 1991 ApkVerifier.Builder builder = 1992 new ApkVerifier.Builder(DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); 1993 if (minSdkVersionOverride != null) { 1994 builder.setMinCheckedPlatformVersion(minSdkVersionOverride); 1995 } 1996 if (maxSdkVersionOverride != null) { 1997 builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride); 1998 } 1999 return builder.build().verify(); 2000 } 2001 2002 private ApkVerifier.Result verifyWithV4Signature( 2003 String apkFilenameInResources, 2004 String v4SignatureFile) 2005 throws IOException, ApkFormatException, NoSuchAlgorithmException, URISyntaxException { 2006 byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); 2007 2008 ApkVerifier.Builder builder = 2009 new ApkVerifier.Builder(DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); 2010 if (v4SignatureFile != null) { 2011 builder.setV4SignatureFile( 2012 Resources.toFile(getClass(), v4SignatureFile, mTemporaryFolder)); 2013 } 2014 return builder.build().verify(); 2015 } 2016 2017 private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources) throws Exception { 2018 return verifySourceStamp(apkFilenameInResources, null, null, null); 2019 } 2020 2021 private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, 2022 String expectedCertDigest) throws Exception { 2023 return verifySourceStamp(apkFilenameInResources, expectedCertDigest, null, null); 2024 } 2025 2026 private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, 2027 Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception { 2028 return verifySourceStamp(apkFilenameInResources, null, minSdkVersionOverride, 2029 maxSdkVersionOverride); 2030 } 2031 2032 private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources, 2033 String expectedCertDigest, Integer minSdkVersionOverride, Integer maxSdkVersionOverride) 2034 throws Exception { 2035 byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources); 2036 ApkVerifier.Builder builder = new ApkVerifier.Builder( 2037 DataSources.asDataSource(ByteBuffer.wrap(apkBytes))); 2038 if (minSdkVersionOverride != null) { 2039 builder.setMinCheckedPlatformVersion(minSdkVersionOverride); 2040 } 2041 if (maxSdkVersionOverride != null) { 2042 builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride); 2043 } 2044 return builder.build().verifySourceStamp(expectedCertDigest); 2045 } 2046 2047 static void assertVerified(ApkVerifier.Result result) { 2048 assertVerified(result, "APK"); 2049 } 2050 2051 static void assertVerified(ApkVerifier.Result result, String apkId) { 2052 if (result.isVerified()) { 2053 return; 2054 } 2055 2056 StringBuilder msg = new StringBuilder(); 2057 for (IssueWithParams issue : result.getErrors()) { 2058 if (msg.length() > 0) { 2059 msg.append('\n'); 2060 } 2061 msg.append(issue); 2062 } 2063 for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { 2064 String signerName = signer.getName(); 2065 for (IssueWithParams issue : signer.getErrors()) { 2066 if (msg.length() > 0) { 2067 msg.append('\n'); 2068 } 2069 msg.append("JAR signer ") 2070 .append(signerName) 2071 .append(": ") 2072 .append(issue.getIssue()) 2073 .append(": ") 2074 .append(issue); 2075 } 2076 } 2077 for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { 2078 String signerName = "signer #" + (signer.getIndex() + 1); 2079 for (IssueWithParams issue : signer.getErrors()) { 2080 if (msg.length() > 0) { 2081 msg.append('\n'); 2082 } 2083 msg.append("APK Signature Scheme v2 signer ") 2084 .append(signerName) 2085 .append(": ") 2086 .append(issue.getIssue()) 2087 .append(": ") 2088 .append(issue); 2089 } 2090 } 2091 for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { 2092 String signerName = "signer #" + (signer.getIndex() + 1); 2093 for (IssueWithParams issue : signer.getErrors()) { 2094 if (msg.length() > 0) { 2095 msg.append('\n'); 2096 } 2097 msg.append("APK Signature Scheme v3 signer ") 2098 .append(signerName) 2099 .append(": ") 2100 .append(issue.getIssue()) 2101 .append(": ") 2102 .append(issue); 2103 } 2104 } 2105 for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { 2106 String signerName = "signer #" + (signer.getIndex() + 1); 2107 for (IssueWithParams issue : signer.getErrors()) { 2108 if (msg.length() > 0) { 2109 msg.append('\n'); 2110 } 2111 msg.append("APK Signature Scheme v3.1 signer ") 2112 .append(signerName) 2113 .append(": ") 2114 .append(issue.getIssue()) 2115 .append(": ") 2116 .append(issue); 2117 } 2118 } 2119 2120 fail(apkId + " did not verify: " + msg); 2121 } 2122 assertVerified( String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)2123 private void assertVerified( 2124 String apkFilenameInResources, 2125 Integer minSdkVersionOverride, 2126 Integer maxSdkVersionOverride) 2127 throws Exception { 2128 assertVerified( 2129 verify(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride), 2130 apkFilenameInResources); 2131 } 2132 assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)2133 static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) { 2134 assertVerificationIssue(result, true, expectedIssue); 2135 } 2136 assertVerificationWarning(ApkVerifier.Result result, Issue expectedIssue)2137 static void assertVerificationWarning(ApkVerifier.Result result, Issue expectedIssue) { 2138 assertVerificationIssue(result, false, expectedIssue); 2139 } 2140 2141 /** 2142 * Asserts the provided {@code result} contains one of the {@code expectedIssues}; if {@code 2143 * verifyError} is set to {@code true} then the specified {@link Issue} will be expected as an 2144 * error, otherwise it will be expected as a warning. 2145 */ assertVerificationIssue(ApkVerifier.Result result, boolean verifyError, Issue... expectedIssues)2146 private static void assertVerificationIssue(ApkVerifier.Result result, boolean verifyError, 2147 Issue... expectedIssues) { 2148 List<Issue> expectedIssuesList = expectedIssues != null 2149 ? Arrays.asList(expectedIssues) : Collections.emptyList(); 2150 if (result.isVerified() && verifyError) { 2151 fail("APK verification succeeded instead of failing with " + expectedIssuesList); 2152 return; 2153 } 2154 2155 StringBuilder msg = new StringBuilder(); 2156 for (IssueWithParams issue : (verifyError ? result.getErrors() : result.getWarnings())) { 2157 if (expectedIssuesList.contains(issue.getIssue())) { 2158 return; 2159 } 2160 if (msg.length() > 0) { 2161 msg.append('\n'); 2162 } 2163 msg.append(issue); 2164 } 2165 for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) { 2166 String signerName = signer.getName(); 2167 for (ApkVerifier.IssueWithParams issue : (verifyError ? signer.getErrors() 2168 : signer.getWarnings())) { 2169 if (expectedIssuesList.contains(issue.getIssue())) { 2170 return; 2171 } 2172 if (msg.length() > 0) { 2173 msg.append('\n'); 2174 } 2175 msg.append("JAR signer ") 2176 .append(signerName) 2177 .append(": ") 2178 .append(issue.getIssue()) 2179 .append(" ") 2180 .append(issue); 2181 } 2182 } 2183 for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) { 2184 String signerName = "signer #" + (signer.getIndex() + 1); 2185 for (IssueWithParams issue : (verifyError ? signer.getErrors() 2186 : signer.getWarnings())) { 2187 if (expectedIssuesList.contains(issue.getIssue())) { 2188 return; 2189 } 2190 if (msg.length() > 0) { 2191 msg.append('\n'); 2192 } 2193 msg.append("APK Signature Scheme v2 signer ") 2194 .append(signerName) 2195 .append(": ") 2196 .append(issue); 2197 } 2198 } 2199 for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) { 2200 String signerName = "signer #" + (signer.getIndex() + 1); 2201 for (IssueWithParams issue : (verifyError ? signer.getErrors() 2202 : signer.getWarnings())) { 2203 if (expectedIssuesList.contains(issue.getIssue())) { 2204 return; 2205 } 2206 if (msg.length() > 0) { 2207 msg.append('\n'); 2208 } 2209 msg.append("APK Signature Scheme v3 signer ") 2210 .append(signerName) 2211 .append(": ") 2212 .append(issue); 2213 } 2214 } 2215 for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV31SchemeSigners()) { 2216 String signerName = "signer #" + (signer.getIndex() + 1); 2217 for (IssueWithParams issue : (verifyError ? signer.getErrors() 2218 : signer.getWarnings())) { 2219 if (expectedIssuesList.contains(issue.getIssue())) { 2220 return; 2221 } 2222 if (msg.length() > 0) { 2223 msg.append('\n'); 2224 } 2225 msg.append("APK Signature Scheme v3.1 signer ") 2226 .append(signerName) 2227 .append(": ") 2228 .append(issue); 2229 } 2230 } 2231 if ((expectedIssuesList.isEmpty() || expectedIssues[0] == null) && msg.length() == 0) { 2232 return; 2233 } 2234 2235 fail( 2236 "APK failed verification for the wrong reason" 2237 + ". Expected: " 2238 + expectedIssuesList 2239 + ", actual: " 2240 + msg); 2241 } 2242 assertSourceStampVerificationFailure( ApkVerifier.Result result, Issue expectedIssue)2243 private static void assertSourceStampVerificationFailure( 2244 ApkVerifier.Result result, Issue expectedIssue) { 2245 if (result.isSourceStampVerified()) { 2246 fail( 2247 "APK source stamp verification succeeded instead of failing with " 2248 + expectedIssue); 2249 return; 2250 } 2251 2252 StringBuilder msg = new StringBuilder(); 2253 List<IssueWithParams> resultIssueWithParams = 2254 Stream.of(result.getErrors(), result.getWarnings()) 2255 .filter(Objects::nonNull) 2256 .flatMap(Collection::stream) 2257 .collect(Collectors.toList()); 2258 for (IssueWithParams issue : resultIssueWithParams) { 2259 if (expectedIssue.equals(issue.getIssue())) { 2260 return; 2261 } 2262 if (msg.length() > 0) { 2263 msg.append('\n'); 2264 } 2265 msg.append(issue); 2266 } 2267 2268 ApkVerifier.Result.SourceStampInfo signer = result.getSourceStampInfo(); 2269 if (signer != null) { 2270 List<IssueWithParams> sourceStampIssueWithParams = 2271 Stream.of(signer.getErrors(), signer.getWarnings()) 2272 .filter(Objects::nonNull) 2273 .flatMap(Collection::stream) 2274 .collect(Collectors.toList()); 2275 for (IssueWithParams issue : sourceStampIssueWithParams) { 2276 if (expectedIssue.equals(issue.getIssue())) { 2277 return; 2278 } 2279 if (msg.length() > 0) { 2280 msg.append('\n'); 2281 } 2282 msg.append("APK SourceStamp signer").append(": ").append(issue); 2283 } 2284 } 2285 2286 fail( 2287 "APK source stamp failed verification for the wrong reason" 2288 + ". Expected: " 2289 + expectedIssue 2290 + ", actual: " 2291 + msg); 2292 } 2293 assertSourceStampVerificationStatus(ApkVerifier.Result result, SourceStampVerificationStatus verificationStatus)2294 private static void assertSourceStampVerificationStatus(ApkVerifier.Result result, 2295 SourceStampVerificationStatus verificationStatus) throws Exception { 2296 assertEquals(verificationStatus, 2297 result.getSourceStampInfo().getSourceStampVerificationStatus()); 2298 } 2299 assertVerificationFailure( String apkFilenameInResources, ApkVerifier.Issue expectedIssue)2300 private void assertVerificationFailure( 2301 String apkFilenameInResources, ApkVerifier.Issue expectedIssue) throws Exception { 2302 assertVerificationFailure(verify(apkFilenameInResources), expectedIssue); 2303 } 2304 assertVerifiedForEach(String apkFilenamePatternInResources, String[] args)2305 private void assertVerifiedForEach(String apkFilenamePatternInResources, String[] args) 2306 throws Exception { 2307 assertVerifiedForEach(apkFilenamePatternInResources, args, null, null); 2308 } 2309 assertVerifiedForEach( String apkFilenamePatternInResources, String[] args, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)2310 private void assertVerifiedForEach( 2311 String apkFilenamePatternInResources, 2312 String[] args, 2313 Integer minSdkVersionOverride, 2314 Integer maxSdkVersionOverride) 2315 throws Exception { 2316 for (String arg : args) { 2317 String apkFilenameInResources = 2318 String.format(Locale.US, apkFilenamePatternInResources, arg); 2319 assertVerified(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride); 2320 } 2321 } 2322 assertVerifiedForEachForMinSdkVersion( String apkFilenameInResources, String[] args, int minSdkVersion)2323 private void assertVerifiedForEachForMinSdkVersion( 2324 String apkFilenameInResources, String[] args, int minSdkVersion) throws Exception { 2325 assertVerifiedForEach(apkFilenameInResources, args, minSdkVersion, null); 2326 } 2327 sha256(byte[] msg)2328 private static byte[] sha256(byte[] msg) { 2329 try { 2330 return MessageDigest.getInstance("SHA-256").digest(msg); 2331 } catch (NoSuchAlgorithmException e) { 2332 throw new RuntimeException("Failed to create SHA-256 MessageDigest", e); 2333 } 2334 } 2335 assumeThatRsaPssAvailable()2336 private static void assumeThatRsaPssAvailable() { 2337 Assume.assumeTrue(Security.getProviders("Signature.SHA256withRSA/PSS") != null); 2338 } 2339 } 2340