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