• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 import static org.junit.Assume.assumeNoException;
23 
24 import com.android.apksig.ApkVerifier.Issue;
25 import com.android.apksig.ApkVerifier.IssueWithParams;
26 import com.android.apksig.ApkVerifier.Result.SourceStampInfo.SourceStampVerificationStatus;
27 import com.android.apksig.apk.ApkFormatException;
28 import com.android.apksig.internal.util.AndroidSdkVersion;
29 import com.android.apksig.internal.util.HexEncoding;
30 import com.android.apksig.internal.util.Resources;
31 import com.android.apksig.util.DataSources;
32 
33 import org.junit.Assume;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.junit.runners.JUnit4;
37 
38 import java.io.IOException;
39 import java.lang.reflect.Field;
40 import java.lang.reflect.Modifier;
41 import java.nio.ByteBuffer;
42 import java.security.InvalidKeyException;
43 import java.security.MessageDigest;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.PublicKey;
46 import java.security.Security;
47 import java.security.Signature;
48 import java.security.cert.CertificateFactory;
49 import java.security.cert.X509Certificate;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.Locale;
53 import java.util.Objects;
54 import java.util.stream.Collectors;
55 import java.util.stream.Stream;
56 
57 @RunWith(JUnit4.class)
58 public class ApkVerifierTest {
59 
60     private static final String[] DSA_KEY_NAMES = {"1024", "2048", "3072"};
61     private static final String[] DSA_KEY_NAMES_1024_AND_SMALLER = {"1024"};
62     private static final String[] DSA_KEY_NAMES_2048_AND_LARGER = {"2048", "3072"};
63     private static final String[] EC_KEY_NAMES = {"p256", "p384", "p521"};
64     private static final String[] RSA_KEY_NAMES = {"1024", "2048", "3072", "4096", "8192", "16384"};
65     private static final String[] RSA_KEY_NAMES_2048_AND_LARGER = {
66             "2048", "3072", "4096", "8192", "16384"
67     };
68 
69     private static final String RSA_2048_CERT_SHA256_DIGEST =
70             "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8";
71     private static final String EC_P256_CERT_SHA256_DIGEST =
72             "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599";
73 
74     @Test
testOriginalAccepted()75     public void testOriginalAccepted() throws Exception {
76         // APK signed with v1 and v2 schemes. Obtained by building
77         // cts/hostsidetests/appsecurity/test-apps/tinyapp.
78         // This APK is used as a basis for many of the other tests here. Hence, we check that this
79         // APK verifies.
80         assertVerified(verify("original.apk"));
81     }
82 
83     @Test
testV1OneSignerMD5withRSAAccepted()84     public void testV1OneSignerMD5withRSAAccepted() throws Exception {
85         // APK signed with v1 scheme only, one signer
86         assertVerifiedForEach(
87                 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
88         assertVerifiedForEach(
89                 "v1-only-with-rsa-pkcs1-md5-1.2.840.113549.1.1.4-%s.apk", RSA_KEY_NAMES);
90     }
91 
92     @Test
testV1OneSignerSHA1withRSAAccepted()93     public void testV1OneSignerSHA1withRSAAccepted() throws Exception {
94         // APK signed with v1 scheme only, one signer
95         assertVerifiedForEach(
96                 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
97         assertVerifiedForEach(
98                 "v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.5-%s.apk", RSA_KEY_NAMES);
99     }
100 
101     @Test
testV1OneSignerSHA224withRSAAccepted()102     public void testV1OneSignerSHA224withRSAAccepted() throws Exception {
103         // APK signed with v1 scheme only, one signer
104         assertVerifiedForEach(
105                 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
106         assertVerifiedForEach(
107                 "v1-only-with-rsa-pkcs1-sha224-1.2.840.113549.1.1.14-%s.apk", RSA_KEY_NAMES);
108     }
109 
110     @Test
testV1OneSignerSHA256withRSAAccepted()111     public void testV1OneSignerSHA256withRSAAccepted() throws Exception {
112         // APK signed with v1 scheme only, one signer
113         assertVerifiedForEach(
114                 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
115         assertVerifiedForEach(
116                 "v1-only-with-rsa-pkcs1-sha256-1.2.840.113549.1.1.11-%s.apk", RSA_KEY_NAMES);
117     }
118 
119     @Test
testV1OneSignerSHA384withRSAAccepted()120     public void testV1OneSignerSHA384withRSAAccepted() throws Exception {
121         // APK signed with v1 scheme only, one signer
122         assertVerifiedForEach(
123                 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
124         assertVerifiedForEach(
125                 "v1-only-with-rsa-pkcs1-sha384-1.2.840.113549.1.1.12-%s.apk", RSA_KEY_NAMES);
126     }
127 
128     @Test
testV1OneSignerSHA512withRSAVerifies()129     public void testV1OneSignerSHA512withRSAVerifies() throws Exception {
130         // APK signed with v1 scheme only, one signer
131         assertVerifiedForEach(
132                 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.1-%s.apk", RSA_KEY_NAMES);
133         assertVerifiedForEach(
134                 "v1-only-with-rsa-pkcs1-sha512-1.2.840.113549.1.1.13-%s.apk", RSA_KEY_NAMES);
135     }
136 
137     @Test
testV1OneSignerSHA1withECDSAAccepted()138     public void testV1OneSignerSHA1withECDSAAccepted() throws Exception {
139         // APK signed with v1 scheme only, one signer
140         assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
141         assertVerifiedForEach("v1-only-with-ecdsa-sha1-1.2.840.10045.4.1-%s.apk", EC_KEY_NAMES);
142     }
143 
144     @Test
testV1OneSignerSHA224withECDSAAccepted()145     public void testV1OneSignerSHA224withECDSAAccepted() throws Exception {
146         // APK signed with v1 scheme only, one signer
147         assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
148         assertVerifiedForEach("v1-only-with-ecdsa-sha224-1.2.840.10045.4.3.1-%s.apk", EC_KEY_NAMES);
149     }
150 
151     @Test
testV1OneSignerSHA256withECDSAAccepted()152     public void testV1OneSignerSHA256withECDSAAccepted() throws Exception {
153         // APK signed with v1 scheme only, one signer
154         assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
155         assertVerifiedForEach("v1-only-with-ecdsa-sha256-1.2.840.10045.4.3.2-%s.apk", EC_KEY_NAMES);
156     }
157 
158     @Test
testV1OneSignerSHA384withECDSAAccepted()159     public void testV1OneSignerSHA384withECDSAAccepted() throws Exception {
160         // APK signed with v1 scheme only, one signer
161         assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
162         assertVerifiedForEach("v1-only-with-ecdsa-sha384-1.2.840.10045.4.3.3-%s.apk", EC_KEY_NAMES);
163     }
164 
165     @Test
testV1OneSignerSHA512withECDSAAccepted()166     public void testV1OneSignerSHA512withECDSAAccepted() throws Exception {
167         // APK signed with v1 scheme only, one signer
168         assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.2.1-%s.apk", EC_KEY_NAMES);
169         assertVerifiedForEach("v1-only-with-ecdsa-sha512-1.2.840.10045.4.3.4-%s.apk", EC_KEY_NAMES);
170     }
171 
172     @Test
testV1OneSignerSHA1withDSAAccepted()173     public void testV1OneSignerSHA1withDSAAccepted() throws Exception {
174         // APK signed with v1 scheme only, one signer
175         // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to
176         // verify DSA signatures with keys too long for the SHA-1 digest.
177         assertVerifiedForEach(
178                 "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER);
179         assertVerifiedForEach(
180                 "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER);
181     }
182 
183     @Test
testV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest()184     public void testV1OneSignerSHA1withDSAAcceptedWithKeysTooLongForDigest() throws Exception {
185         // APK signed with v1 scheme only, one signer
186 
187         // OpenJDK's default implementation of Signature.SHA1withDSA refuses to verify signatures
188         // created with keys too long for the digest used. Android Package Manager does not reject
189         // such signatures. We thus skip this test if Signature.SHA1withDSA exhibits this issue.
190         PublicKey publicKey =
191                 Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey();
192         Signature s = Signature.getInstance("SHA1withDSA");
193         try {
194             s.initVerify(publicKey);
195         } catch (InvalidKeyException e) {
196             assumeNoException(e);
197         }
198 
199         assertVerifiedForEach(
200                 "v1-only-with-dsa-sha1-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER);
201         assertVerifiedForEach(
202                 "v1-only-with-dsa-sha1-1.2.840.10040.4.3-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER);
203     }
204 
205     @Test
testV1OneSignerSHA224withDSAAccepted()206     public void testV1OneSignerSHA224withDSAAccepted() throws Exception {
207         // APK signed with v1 scheme only, one signer
208         // NOTE: This test is split into two because JCA Providers shipping with OpenJDK refuse to
209         // verify DSA signatures with keys too long for the SHA-224 digest.
210         assertVerifiedForEach(
211                 "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_1024_AND_SMALLER);
212         assertVerifiedForEach(
213                 "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk",
214                 DSA_KEY_NAMES_1024_AND_SMALLER);
215     }
216 
217     @Test
testV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest()218     public void testV1OneSignerSHA224withDSAAcceptedWithKeysTooLongForDigest() throws Exception {
219         // APK signed with v1 scheme only, one signer
220 
221         // OpenJDK's default implementation of Signature.SHA224withDSA refuses to verify signatures
222         // created with keys too long for the digest used. Android Package Manager does not reject
223         // such signatures. We thus skip this test if Signature.SHA224withDSA exhibits this issue.
224         PublicKey publicKey =
225                 Resources.toCertificate(getClass(), "dsa-2048.x509.pem").getPublicKey();
226         Signature s = Signature.getInstance("SHA224withDSA");
227         try {
228             s.initVerify(publicKey);
229         } catch (InvalidKeyException e) {
230             assumeNoException(e);
231         }
232         assertVerifiedForEach(
233                 "v1-only-with-dsa-sha224-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES_2048_AND_LARGER);
234         assertVerifiedForEach(
235                 "v1-only-with-dsa-sha224-2.16.840.1.101.3.4.3.1-%s.apk",
236                 DSA_KEY_NAMES_2048_AND_LARGER);
237     }
238 
239     @Test
testV1OneSignerSHA256withDSAAccepted()240     public void testV1OneSignerSHA256withDSAAccepted() throws Exception {
241         // APK signed with v1 scheme only, one signer
242         assertVerifiedForEach("v1-only-with-dsa-sha256-1.2.840.10040.4.1-%s.apk", DSA_KEY_NAMES);
243         assertVerifiedForEach(
244                 "v1-only-with-dsa-sha256-2.16.840.1.101.3.4.3.2-%s.apk", DSA_KEY_NAMES);
245     }
246 
247     @Test
testV2StrippedRejected()248     public void testV2StrippedRejected() throws Exception {
249         // APK signed with v1 and v2 schemes, but v2 signature was stripped from the file (by using
250         // zipalign).
251         // This should fail because the v1 signature indicates that the APK was supposed to be
252         // signed with v2 scheme as well, making the platform's anti-stripping protections reject
253         // the APK.
254         assertVerificationFailure("v2-stripped.apk", Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED);
255 
256         // Similar to above, but the X-Android-APK-Signed anti-stripping header in v1 signature
257         // lists unknown signature schemes in addition to APK Signature Scheme v2. Unknown schemes
258         // should be ignored.
259         assertVerificationFailure(
260                 "v2-stripped-with-ignorable-signing-schemes.apk",
261                 Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED);
262     }
263 
264     @Test
testV3StrippedRejected()265     public void testV3StrippedRejected() throws Exception {
266         // APK signed with v2 and v3 schemes, but v3 signature was stripped from the file by
267         // modifying the v3 block ID to be the verity padding block ID. Without the stripping
268         // protection this modification ignores the v3 signing scheme block.
269         assertVerificationFailure("v3-stripped.apk", Issue.V2_SIG_MISSING_APK_SIG_REFERENCED);
270     }
271 
272     @Test
testSignaturesIgnoredForMaxSDK()273     public void testSignaturesIgnoredForMaxSDK() throws Exception {
274         // The V2 signature scheme was introduced in N, and V3 was introduced in P. This test
275         // verifies a max SDK of pre-P ignores the V3 signature and a max SDK of pre-N ignores both
276         // the V2 and V3 signatures.
277         assertVerified(
278                 verifyForMaxSdkVersion(
279                         "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.O));
280         assertVerified(
281                 verifyForMaxSdkVersion(
282                         "v1v2v3-with-rsa-2048-lineage-3-signers.apk", AndroidSdkVersion.M));
283     }
284 
285     @Test
testV2OneSignerOneSignatureAccepted()286     public void testV2OneSignerOneSignatureAccepted() throws Exception {
287         // APK signed with v2 scheme only, one signer, one signature
288         assertVerifiedForEachForMinSdkVersion(
289                 "v2-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.N);
290         assertVerifiedForEachForMinSdkVersion(
291                 "v2-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N);
292         assertVerifiedForEachForMinSdkVersion(
293                 "v2-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N);
294         // RSA-PSS signatures tested in a separate test below
295 
296         // DSA with SHA-512 is not supported by Android platform and thus APK Signature Scheme v2
297         // does not support that either
298         // assertInstallSucceedsForEach("v2-only-with-dsa-sha512-%s.apk", DSA_KEY_NAMES);
299         assertVerifiedForEachForMinSdkVersion(
300                 "v2-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.N);
301         assertVerifiedForEachForMinSdkVersion(
302                 "v2-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N);
303     }
304 
305     @Test
testV3OneSignerOneSignatureAccepted()306     public void testV3OneSignerOneSignatureAccepted() throws Exception {
307         // APK signed with v3 scheme only, one signer, one signature
308         assertVerifiedForEachForMinSdkVersion(
309                 "v3-only-with-dsa-sha256-%s.apk", DSA_KEY_NAMES, AndroidSdkVersion.P);
310         assertVerifiedForEachForMinSdkVersion(
311                 "v3-only-with-ecdsa-sha256-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P);
312         assertVerifiedForEachForMinSdkVersion(
313                 "v3-only-with-rsa-pkcs1-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P);
314 
315         assertVerifiedForEachForMinSdkVersion(
316                 "v3-only-with-ecdsa-sha512-%s.apk", EC_KEY_NAMES, AndroidSdkVersion.P);
317         assertVerifiedForEachForMinSdkVersion(
318                 "v3-only-with-rsa-pkcs1-sha512-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.P);
319     }
320 
321     @Test
testV2OneSignerOneRsaPssSignatureAccepted()322     public void testV2OneSignerOneRsaPssSignatureAccepted() throws Exception {
323         assumeThatRsaPssAvailable();
324         // APK signed with v2 scheme only, one signer, one signature
325         assertVerifiedForEachForMinSdkVersion(
326                 "v2-only-with-rsa-pss-sha256-%s.apk", RSA_KEY_NAMES, AndroidSdkVersion.N);
327         assertVerifiedForEachForMinSdkVersion(
328                 "v2-only-with-rsa-pss-sha512-%s.apk",
329                 RSA_KEY_NAMES_2048_AND_LARGER, // 1024-bit key is too short for PSS with SHA-512
330                 AndroidSdkVersion.N);
331     }
332 
333     @Test
testV2SignatureDoesNotMatchSignedDataRejected()334     public void testV2SignatureDoesNotMatchSignedDataRejected() throws Exception {
335         // APK signed with v2 scheme only, but the signature over signed-data does not verify
336 
337         // Bitflip in certificate field inside signed-data. Based on
338         // v2-only-with-dsa-sha256-1024.apk.
339         assertVerificationFailure(
340                 "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk",
341                 Issue.V2_SIG_DID_NOT_VERIFY);
342 
343         // Signature claims to be RSA PKCS#1 v1.5 with SHA-256, but is actually using SHA-512.
344         // Based on v2-only-with-rsa-pkcs1-sha256-2048.apk.
345         assertVerificationFailure(
346                 "v2-only-with-rsa-pkcs1-sha256-2048-sig-does-not-verify.apk",
347                 Issue.V2_SIG_VERIFY_EXCEPTION);
348 
349         // Bitflip in the ECDSA signature. Based on v2-only-with-ecdsa-sha256-p256.apk.
350         assertVerificationFailure(
351                 "v2-only-with-ecdsa-sha256-p256-sig-does-not-verify.apk",
352                 Issue.V2_SIG_DID_NOT_VERIFY);
353     }
354 
355     @Test
testV3SignatureDoesNotMatchSignedDataRejected()356     public void testV3SignatureDoesNotMatchSignedDataRejected() throws Exception {
357         // APK signed with v3 scheme only, but the signature over signed-data does not verify
358 
359         // Bitflip in DSA signature. Based on v3-only-with-dsa-sha256-2048.apk.
360         assertVerificationFailure(
361                 "v3-only-with-dsa-sha256-2048-sig-does-not-verify.apk",
362                 Issue.V3_SIG_DID_NOT_VERIFY);
363 
364         // Bitflip in signed data. Based on v3-only-with-rsa-pkcs1-sha256-3072.apk
365         assertVerificationFailure(
366                 "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk",
367                 Issue.V3_SIG_DID_NOT_VERIFY);
368 
369         // Based on v3-only-with-ecdsa-sha512-p521 with the signature ID changed to be ECDSA with
370         // SHA-256.
371         assertVerificationFailure(
372                 "v3-only-with-ecdsa-sha512-p521-sig-does-not-verify.apk",
373                 Issue.V3_SIG_DID_NOT_VERIFY);
374     }
375 
376     @Test
testV2RsaPssSignatureDoesNotMatchSignedDataRejected()377     public void testV2RsaPssSignatureDoesNotMatchSignedDataRejected() throws Exception {
378         assumeThatRsaPssAvailable();
379 
380         // APK signed with v2 scheme only, but the signature over signed-data does not verify.
381 
382         // Signature claims to be RSA PSS with SHA-256 and 32 bytes of salt, but is actually using 0
383         // bytes of salt. Based on v2-only-with-rsa-pkcs1-sha256-2048.apk. Obtained by modifying APK
384         // signer to use the wrong amount of salt.
385         assertVerificationFailure(
386                 "v2-only-with-rsa-pss-sha256-2048-sig-does-not-verify.apk",
387                 Issue.V2_SIG_DID_NOT_VERIFY);
388     }
389 
390     @Test
testV2ContentDigestMismatchRejected()391     public void testV2ContentDigestMismatchRejected() throws Exception {
392         // APK signed with v2 scheme only, but the digest of contents does not match the digest
393         // stored in signed-data
394         ApkVerifier.Issue error = Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY;
395 
396         // Based on v2-only-with-rsa-pkcs1-sha512-4096.apk. Obtained by modifying APK signer to
397         // flip the leftmost bit in content digest before signing signed-data.
398         assertVerificationFailure("v2-only-with-rsa-pkcs1-sha512-4096-digest-mismatch.apk", error);
399 
400         // Based on v2-only-with-ecdsa-sha256-p256.apk. Obtained by modifying APK signer to flip the
401         // leftmost bit in content digest before signing signed-data.
402         assertVerificationFailure("v2-only-with-ecdsa-sha256-p256-digest-mismatch.apk", error);
403     }
404 
405     @Test
testV3ContentDigestMismatchRejected()406     public void testV3ContentDigestMismatchRejected() throws Exception {
407         // APK signed with v3 scheme only, but the digest of contents does not match the digest
408         // stored in signed-data.
409 
410         // Based on v3-only-with-rsa-pkcs1-sha512-8192. Obtained by flipping a bit in the local
411         // file header of the APK.
412         assertVerificationFailure(
413                 "v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk",
414                 Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY);
415 
416         // Based on v3-only-with-dsa-sha256-3072.apk. Obtained by modifying APK signer to flip the
417         // leftmost bit in content digest before signing signed-data.
418         assertVerificationFailure(
419                 "v3-only-with-dsa-sha256-3072-digest-mismatch.apk",
420                 Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY);
421     }
422 
423     @Test
testNoApkSignatureSchemeBlockRejected()424     public void testNoApkSignatureSchemeBlockRejected() throws Exception {
425         // APK signed with v2 scheme only, but the rules for verifying APK Signature Scheme v2
426         // signatures say that this APK must not be verified using APK Signature Scheme v2.
427 
428         // Obtained from v2-only-with-rsa-pkcs1-sha512-4096.apk by flipping a bit in the magic
429         // field in the footer of APK Signing Block. This makes the APK Signing Block disappear.
430         assertVerificationFailure(
431                 "v2-only-wrong-apk-sig-block-magic.apk", Issue.JAR_SIG_NO_MANIFEST);
432 
433         // Obtained by modifying APK signer to insert "GARBAGE" between ZIP Central Directory and
434         // End of Central Directory. The APK is otherwise fine and is signed with APK Signature
435         // Scheme v2. Based on v2-only-with-rsa-pkcs1-sha256.apk.
436         assertVerificationFailure(
437                 "v2-only-garbage-between-cd-and-eocd.apk", Issue.JAR_SIG_NO_MANIFEST);
438 
439         // Obtained by modifying the size in APK Signature Block header. Based on
440         // v2-only-with-ecdsa-sha512-p521.apk.
441         assertVerificationFailure(
442                 "v2-only-apk-sig-block-size-mismatch.apk", Issue.JAR_SIG_NO_MANIFEST);
443 
444         // Obtained by modifying the ID under which APK Signature Scheme v2 Block is stored in
445         // APK Signing Block and by modifying the APK signer to not insert anti-stripping
446         // protections into JAR Signature. The APK should appear as having no APK Signature Scheme
447         // v2 Block and should thus successfully verify using JAR Signature Scheme.
448         assertVerified(verify("v1-with-apk-sig-block-but-without-apk-sig-scheme-v2-block.apk"));
449     }
450 
451     @Test
testNoV3ApkSignatureSchemeBlockRejected()452     public void testNoV3ApkSignatureSchemeBlockRejected() throws Exception {
453         // Obtained from v3-only-with-ecdsa-sha512-p384.apk by flipping a bit in the magic field
454         // in the footer of the APK Signing Block.
455         assertVerificationFailure(
456                 "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk",
457                 Issue.JAR_SIG_NO_MANIFEST);
458 
459         // Obtained from v3-only-with-rsa-pkcs1-sha512-4096.apk by modifying the size in the APK
460         // Signature Block header and footer.
461         assertVerificationFailure(
462                 "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
463                 Issue.JAR_SIG_NO_MANIFEST);
464     }
465 
466     @Test(expected = ApkFormatException.class)
testTruncatedZipCentralDirectoryRejected()467     public void testTruncatedZipCentralDirectoryRejected() throws Exception {
468         // Obtained by modifying APK signer to truncate the ZIP Central Directory by one byte. The
469         // APK is otherwise fine and is signed with APK Signature Scheme v2. Based on
470         // v2-only-with-rsa-pkcs1-sha256.apk
471         verify("v2-only-truncated-cd.apk");
472     }
473 
474     @Test
testV2UnknownPairIgnoredInApkSigningBlock()475     public void testV2UnknownPairIgnoredInApkSigningBlock() throws Exception {
476         // Obtained by modifying APK signer to emit an unknown ID-value pair into APK Signing Block
477         // before the ID-value pair containing the APK Signature Scheme v2 Block. The unknown
478         // ID-value should be ignored.
479         assertVerified(
480                 verifyForMinSdkVersion(
481                         "v2-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.N));
482     }
483 
484     @Test
testV3UnknownPairIgnoredInApkSigningBlock()485     public void testV3UnknownPairIgnoredInApkSigningBlock() throws Exception {
486         // Obtained by modifying APK signer to emit an unknown ID value pair into APK Signing Block
487         // before the ID value pair containing the APK Signature Scheme v3 Block. The unknown
488         // ID value should be ignored.
489         assertVerified(
490                 verifyForMinSdkVersion(
491                         "v3-only-unknown-pair-in-apk-sig-block.apk", AndroidSdkVersion.P));
492     }
493 
494     @Test
testV2UnknownSignatureAlgorithmsIgnored()495     public void testV2UnknownSignatureAlgorithmsIgnored() throws Exception {
496         // APK is signed with a known signature algorithm and with a couple of unknown ones.
497         // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to
498         // known ones.
499         assertVerified(
500                 verifyForMinSdkVersion(
501                         "v2-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.N));
502     }
503 
504     @Test
testV3UnknownSignatureAlgorithmsIgnored()505     public void testV3UnknownSignatureAlgorithmsIgnored() throws Exception {
506         // APK is signed with a known signature algorithm and a couple of unknown ones.
507         // Obtained by modifying APK signer to use "unknown" signature algorithms in addition to
508         // known ones.
509         assertVerified(
510                 verifyForMinSdkVersion(
511                         "v3-only-with-ignorable-unsupported-sig-algs.apk", AndroidSdkVersion.P));
512     }
513 
514     @Test
testV3WithOnlyUnknownSignatureAlgorithmsRejected()515     public void testV3WithOnlyUnknownSignatureAlgorithmsRejected() throws Exception {
516         // APK is only signed with an unknown signature algorithm. Obtained by modifying APK
517         // signer's ID for a known signature algorithm.
518         assertVerificationFailure(
519                 "v3-only-no-supported-sig-algs.apk", Issue.V3_SIG_NO_SUPPORTED_SIGNATURES);
520     }
521 
522     @Test
testV2UnknownAdditionalAttributeIgnored()523     public void testV2UnknownAdditionalAttributeIgnored() throws Exception {
524         // APK's v2 signature contains an unknown additional attribute, but is otherwise fine.
525         // Obtained by modifying APK signer to output an additional attribute with ID 0x01020304
526         // and value 0x05060708.
527         assertVerified(
528                 verifyForMinSdkVersion("v2-only-unknown-additional-attr.apk", AndroidSdkVersion.N));
529     }
530 
531     @Test
testV3UnknownAdditionalAttributeIgnored()532     public void testV3UnknownAdditionalAttributeIgnored() throws Exception {
533         // APK's v3 signature contains unknown additional attributes before and after the lineage.
534         // Obtained by modifying APK signer to output additional attributes with IDs 0x11223344
535         // and 0x99aabbcc with values 0x55667788 and 0xddeeff00
536         assertVerified(
537                 verifyForMinSdkVersion("v3-only-unknown-additional-attr.apk", AndroidSdkVersion.P));
538 
539         // APK's v2 and v3 signatures contain unknown additional attributes before and after the
540         // anti-stripping and lineage attributes.
541         assertVerified(
542                 verifyForMinSdkVersion("v2v3-unknown-additional-attr.apk", AndroidSdkVersion.P));
543     }
544 
545     @Test
testV2MismatchBetweenSignaturesAndDigestsBlockRejected()546     public void testV2MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception {
547         // APK is signed with a single signature algorithm, but the digests block claims that it is
548         // signed with two different signature algorithms. Obtained by modifying APK Signer to
549         // emit an additional digest record with signature algorithm 0x12345678.
550         assertVerificationFailure(
551                 "v2-only-signatures-and-digests-block-mismatch.apk",
552                 Issue.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS);
553     }
554 
555     @Test
testV3MismatchBetweenSignaturesAndDigestsBlockRejected()556     public void testV3MismatchBetweenSignaturesAndDigestsBlockRejected() throws Exception {
557         // APK is signed with a single signature algorithm, but the digests block claims that it is
558         // signed with two different signature algorithms. Obtained by modifying APK Signer to
559         // emit an additional digest record with signature algorithm 0x11223344.
560         assertVerificationFailure(
561                 "v3-only-signatures-and-digests-block-mismatch.apk",
562                 Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS);
563     }
564 
565     @Test
testV2MismatchBetweenPublicKeyAndCertificateRejected()566     public void testV2MismatchBetweenPublicKeyAndCertificateRejected() throws Exception {
567         // APK is signed with v2 only. The public key field does not match the public key in the
568         // leaf certificate. Obtained by modifying APK signer to write out a modified leaf
569         // certificate where the RSA modulus has a bitflip.
570         assertVerificationFailure(
571                 "v2-only-cert-and-public-key-mismatch.apk",
572                 Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD);
573     }
574 
575     @Test
testV3MismatchBetweenPublicKeyAndCertificateRejected()576     public void testV3MismatchBetweenPublicKeyAndCertificateRejected() throws Exception {
577         // APK is signed with v3 only. The public key field does not match the public key in the
578         // leaf certificate. Obtained by modifying APK signer to write out a modified leaf
579         // certificate where the RSA modulus has a bitflip.
580         assertVerificationFailure(
581                 "v3-only-cert-and-public-key-mismatch.apk",
582                 Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD);
583     }
584 
585     @Test
testV2SignerBlockWithNoCertificatesRejected()586     public void testV2SignerBlockWithNoCertificatesRejected() throws Exception {
587         // APK is signed with v2 only. There are no certificates listed in the signer block.
588         // Obtained by modifying APK signer to output no certificates.
589         assertVerificationFailure("v2-only-no-certs-in-sig.apk", Issue.V2_SIG_NO_CERTIFICATES);
590     }
591 
592     @Test
testV3SignerBlockWithNoCertificatesRejected()593     public void testV3SignerBlockWithNoCertificatesRejected() throws Exception {
594         // APK is signed with v3 only. There are no certificates listed in the signer block.
595         // Obtained by modifying APK signer to output no certificates.
596         assertVerificationFailure("v3-only-no-certs-in-sig.apk", Issue.V3_SIG_NO_CERTIFICATES);
597     }
598 
599     @Test
testTwoSignersAccepted()600     public void testTwoSignersAccepted() throws Exception {
601         // APK signed by two different signers
602         assertVerified(verify("two-signers.apk"));
603         assertVerified(verify("v1-only-two-signers.apk"));
604         assertVerified(verifyForMinSdkVersion("v2-only-two-signers.apk", AndroidSdkVersion.N));
605     }
606 
607     @Test
testV2TwoSignersRejectedWhenOneBroken()608     public void testV2TwoSignersRejectedWhenOneBroken() throws Exception {
609         // Bitflip in the ECDSA signature of second signer. Based on two-signers.apk.
610         // This asserts that breakage in any signer leads to rejection of the APK.
611         assertVerificationFailure(
612                 "two-signers-second-signer-v2-broken.apk", Issue.V2_SIG_DID_NOT_VERIFY);
613     }
614 
615     @Test
testV2TwoSignersRejectedWhenOneWithoutSignatures()616     public void testV2TwoSignersRejectedWhenOneWithoutSignatures() throws Exception {
617         // APK v2-signed by two different signers. However, there are no signatures for the second
618         // signer.
619         assertVerificationFailure(
620                 "v2-only-two-signers-second-signer-no-sig.apk", Issue.V2_SIG_NO_SIGNATURES);
621     }
622 
623     @Test
testV2TwoSignersRejectedWhenOneWithoutSupportedSignatures()624     public void testV2TwoSignersRejectedWhenOneWithoutSupportedSignatures() throws Exception {
625         // APK v2-signed by two different signers. However, there are no supported signatures for
626         // the second signer.
627         assertVerificationFailure(
628                 "v2-only-two-signers-second-signer-no-supported-sig.apk",
629                 Issue.V2_SIG_NO_SUPPORTED_SIGNATURES);
630     }
631 
632     @Test
testCorrectCertUsedFromPkcs7SignedDataCertsSet()633     public void testCorrectCertUsedFromPkcs7SignedDataCertsSet() throws Exception {
634         // Obtained by prepending the rsa-1024 certificate to the PKCS#7 SignedData certificates set
635         // of v1-only-with-rsa-pkcs1-sha1-1.2.840.113549.1.1.1-2048.apk META-INF/CERT.RSA. The certs
636         // (in the order of appearance in the file) are thus: rsa-1024, rsa-2048. The package's
637         // signing cert is rsa-2048.
638         ApkVerifier.Result result = verify("v1-only-pkcs7-cert-bag-first-cert-not-used.apk");
639         assertVerified(result);
640         List<X509Certificate> signingCerts = result.getSignerCertificates();
641         assertEquals(1, signingCerts.size());
642         assertEquals(
643                 "fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8",
644                 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded())));
645     }
646 
647     @Test
testV1SchemeSignatureCertNotReencoded()648     public void testV1SchemeSignatureCertNotReencoded() throws Exception {
649         // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the
650         // original encoded form of signing certificates, bad things happen, such as rejection of
651         // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that
652         // PackageManager started re-encoding signing certs into DER. This normally produces exactly
653         // the original form because X.509 certificates are supposed to be DER-encoded. However, a
654         // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For
655         // such apps, re-encoding into DER changes the serialized form of the certificate, creating
656         // a mismatch with the serialized form stored in the PackageManager database, leading to the
657         // rejection of updates for the app.
658         //
659         // v1-only-with-rsa-1024-cert-not-der.apk cert's signature is not DER-encoded. It is
660         // BER-encoded, with length encoded as two bytes instead of just one.
661         // v1-only-with-rsa-1024-cert-not-der.apk META-INF/CERT.RSA was obtained from
662         // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure.
663         ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der.apk");
664 
665         // On JDK 8u131 and newer, when the default (SUN) X.509 CertificateFactory implementation is
666         // used, PKCS #7 signature verification fails because the certificate is not DER-encoded.
667         // This contrived block of code disables this test in this scenario.
668         if (!result.isVerified()) {
669             List<ApkVerifier.Result.V1SchemeSignerInfo> signers = result.getV1SchemeSigners();
670             if (signers.size() > 0) {
671                 ApkVerifier.Result.V1SchemeSignerInfo signer = signers.get(0);
672                 for (IssueWithParams issue : signer.getErrors()) {
673                     if (issue.getIssue() == Issue.JAR_SIG_PARSE_EXCEPTION) {
674                         CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
675                         if ("SUN".equals(certFactory.getProvider().getName())) {
676                             Throwable exception = (Throwable) issue.getParams()[1];
677                             Throwable e = exception;
678                             while (e != null) {
679                                 String msg = e.getMessage();
680                                 e = e.getCause();
681                                 if ((msg != null)
682                                         && (msg.contains("Redundant length bytes found"))) {
683                                     Assume.assumeNoException(exception);
684                                 }
685                             }
686                         }
687                         break;
688                     }
689                 }
690             }
691         }
692 
693         assertVerified(result);
694         List<X509Certificate> signingCerts = result.getSignerCertificates();
695         assertEquals(1, signingCerts.size());
696         assertEquals(
697                 "c5d4535a7e1c8111687a8374b2198da6f5ff8d811a7a25aa99ef060669342fa9",
698                 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded())));
699     }
700 
701     @Test
testV1SchemeSignatureCertNotReencoded2()702     public void testV1SchemeSignatureCertNotReencoded2() throws Exception {
703         // Regression test for b/30148997 and b/18228011. When PackageManager does not preserve the
704         // original encoded form of signing certificates, bad things happen, such as rejection of
705         // completely valid updates to apps. The issue in b/30148997 and b/18228011 was that
706         // PackageManager started re-encoding signing certs into DER. This normally produces exactly
707         // the original form because X.509 certificates are supposed to be DER-encoded. However, a
708         // small fraction of Android apps uses X.509 certificates which are not DER-encoded. For
709         // such apps, re-encoding into DER changes the serialized form of the certificate, creating
710         // a mismatch with the serialized form stored in the PackageManager database, leading to the
711         // rejection of updates for the app.
712         //
713         // v1-only-with-rsa-1024-cert-not-der2.apk cert's signature is not DER-encoded. It is
714         // BER-encoded, with the BIT STRING value containing an extraneous leading 0x00 byte.
715         // v1-only-with-rsa-1024-cert-not-der2.apk META-INF/CERT.RSA was obtained from
716         // v1-only-with-rsa-1024.apk META-INF/CERT.RSA by manually modifying the ASN.1 structure.
717         ApkVerifier.Result result = verify("v1-only-with-rsa-1024-cert-not-der2.apk");
718         assertVerified(result);
719         List<X509Certificate> signingCerts = result.getSignerCertificates();
720         assertEquals(1, signingCerts.size());
721         assertEquals(
722                 "da3da398de674541313deed77218ce94798531ea5131bb9b1bb4063ba4548cfb",
723                 HexEncoding.encode(sha256(signingCerts.get(0).getEncoded())));
724     }
725 
726     @Test
testMaxSizedZipEocdCommentAccepted()727     public void testMaxSizedZipEocdCommentAccepted() throws Exception {
728         // Obtained by modifying apksigner to produce a max-sized (0xffff bytes long) ZIP End of
729         // Central Directory comment, and signing the original.apk using the modified apksigner.
730         assertVerified(verify("v1-only-max-sized-eocd-comment.apk"));
731         assertVerified(
732                 verifyForMinSdkVersion("v2-only-max-sized-eocd-comment.apk", AndroidSdkVersion.N));
733     }
734 
735     @Test
testEmptyApk()736     public void testEmptyApk() throws Exception {
737         // Unsigned empty ZIP archive
738         try {
739             verifyForMinSdkVersion("empty-unsigned.apk", 1);
740             fail("ApkFormatException should've been thrown");
741         } catch (ApkFormatException expected) {
742         }
743 
744         // JAR-signed empty ZIP archive
745         try {
746             verifyForMinSdkVersion("v1-only-empty.apk", 18);
747             fail("ApkFormatException should've been thrown");
748         } catch (ApkFormatException expected) {
749         }
750 
751         // APK Signature Scheme v2 signed empty ZIP archive
752         try {
753             verifyForMinSdkVersion("v2-only-empty.apk", AndroidSdkVersion.N);
754             fail("ApkFormatException should've been thrown");
755         } catch (ApkFormatException expected) {
756         }
757 
758         // APK Signature Scheme v3 signed empty ZIP archive
759         try {
760             verifyForMinSdkVersion("v3-only-empty.apk", AndroidSdkVersion.P);
761             fail("ApkFormatException should've been thrown");
762         } catch (ApkFormatException expected) {
763         }
764     }
765 
766     @Test
testTargetSandboxVersion2AndHigher()767     public void testTargetSandboxVersion2AndHigher() throws Exception {
768         // This APK (and its variants below) use minSdkVersion 18, meaning it needs to be signed
769         // with v1 and v2 schemes
770 
771         // This APK is signed with v1 and v2 schemes and thus should verify
772         assertVerified(verify("targetSandboxVersion-2.apk"));
773 
774         // v1 signature is needed only if minSdkVersion is lower than 24
775         assertVerificationFailure(
776                 verify("v2-only-targetSandboxVersion-2.apk"), Issue.JAR_SIG_NO_MANIFEST);
777         assertVerified(verifyForMinSdkVersion("v2-only-targetSandboxVersion-2.apk", 24));
778 
779         // v2 signature is required
780         assertVerificationFailure(
781                 verify("v1-only-targetSandboxVersion-2.apk"),
782                 Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION);
783         assertVerificationFailure(
784                 verify("unsigned-targetSandboxVersion-2.apk"),
785                 Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION);
786 
787         // minSdkVersion 28, meaning v1 signature not needed
788         assertVerified(verify("v2-only-targetSandboxVersion-3.apk"));
789     }
790 
791     @Test
testTargetSdkMinSchemeVersionNotMet()792     public void testTargetSdkMinSchemeVersionNotMet() throws Exception {
793         // Android 11 / SDK version 30 requires apps targeting this SDK version or higher must be
794         // signed with at least the V2 signature scheme. This test verifies if an app is targeting
795         // this SDK version and is only signed with a V1 signature then the verifier reports the
796         // platform will not accept it.
797         assertVerificationFailure(verify("v1-ec-p256-targetSdk-30.apk"),
798                 Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET);
799     }
800 
801     @Test
testTargetSdkMinSchemeVersionMet()802     public void testTargetSdkMinSchemeVersionMet() throws Exception {
803         // This test verifies if an app is signed with the minimum required signature scheme version
804         // for the target SDK version then the verifier reports the platform will accept it.
805         assertVerified(verify("v2-ec-p256-targetSdk-30.apk"));
806 
807         // If an app is only signed with a signature scheme higher than the required version for the
808         // target SDK the verifier should also report that the platform will accept it.
809         assertVerified(verify("v3-ec-p256-targetSdk-30.apk"));
810     }
811 
812     @Test
testTargetSdkMinSchemeVersionNotMetMaxLessThanTarget()813     public void testTargetSdkMinSchemeVersionNotMetMaxLessThanTarget() throws Exception {
814         // If the minimum signature scheme for the target SDK version is not met but the maximum
815         // SDK version is less than the target then the verifier should report that the platform
816         // will accept it since the specified max SDK version does not know about the minimum
817         // signature scheme requirement.
818         verifyForMaxSdkVersion("v1-ec-p256-targetSdk-30.apk", 29);
819     }
820 
821     @Test
testTargetSdkNoUsesSdkElement()822     public void testTargetSdkNoUsesSdkElement() throws Exception {
823         // The target SDK minimum signature scheme version check will attempt to obtain the
824         // targetSdkVersion attribute value from the uses-sdk element in the AndroidManifest. If
825         // the targetSdkVersion is not specified then the verifier should behave the same as the
826         // platform; the minSdkVersion should be used when available and when neither the minimum or
827         // target SDK are specified a default value of 1 should be used. This test verifies that the
828         // verifier does not fail when the uses-sdk element is not specified.
829         verify("v1-only-no-uses-sdk.apk");
830     }
831 
832     @Test
testV1MultipleDigestAlgsInManifestAndSignatureFile()833     public void testV1MultipleDigestAlgsInManifestAndSignatureFile() throws Exception {
834         // MANIFEST.MF contains SHA-1 and SHA-256 digests for each entry, .SF contains only SHA-1
835         // digests. This file was obtained by:
836         //   jarsigner -sigalg SHA256withRSA -digestalg SHA-256 ... <file> ...
837         //   jarsigner -sigalg SHA1withRSA -digestalg SHA1 ... <same file> ...
838         assertVerified(verify("v1-sha1-sha256-manifest-and-sha1-sf.apk"));
839 
840         // MANIFEST.MF and .SF contain SHA-1 and SHA-256 digests for each entry. This file was
841         // obtained by modifying apksigner to output multiple digests.
842         assertVerified(verify("v1-sha1-sha256-manifest-and-sf.apk"));
843 
844         // One of the digests is wrong in either MANIFEST.MF or .SF. These files were obtained by
845         // modifying apksigner to output multiple digests and to flip a bit to create a wrong
846         // digest.
847 
848         // SHA-1 digests in MANIFEST.MF are wrong, but SHA-256 digests are OK.
849         // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18
850         // and higher.
851         assertVerificationFailure(
852                 verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk"),
853                 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
854         assertVerificationFailure(
855                 verifyForMaxSdkVersion(
856                         "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 17),
857                 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
858         assertVerified(
859                 verifyForMinSdkVersion(
860                         "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-manifest.apk", 18));
861 
862         // SHA-1 digests in .SF are wrong, but SHA-256 digests are OK.
863         // The APK will fail to verify on API Level 17 and lower, but will verify on API Level 18
864         // and higher.
865         assertVerificationFailure(
866                 verify("v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk"),
867                 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
868         assertVerificationFailure(
869                 verifyForMaxSdkVersion(
870                         "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 17),
871                 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
872         assertVerified(
873                 verifyForMinSdkVersion(
874                         "v1-sha1-sha256-manifest-and-sf-with-sha1-wrong-in-sf.apk", 18));
875 
876         // SHA-256 digests in MANIFEST.MF are wrong, but SHA-1 digests are OK.
877         // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17
878         // and lower.
879         assertVerificationFailure(
880                 verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk"),
881                 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
882         assertVerificationFailure(
883                 verifyForMinSdkVersion(
884                         "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 18),
885                 Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY);
886         assertVerified(
887                 verifyForMaxSdkVersion(
888                         "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-manifest.apk", 17));
889 
890         // SHA-256 digests in .SF are wrong, but SHA-1 digests are OK.
891         // The APK will fail to verify on API Level 18 and higher, but will verify on API Level 17
892         // and lower.
893         assertVerificationFailure(
894                 verify("v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk"),
895                 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
896         assertVerificationFailure(
897                 verifyForMinSdkVersion(
898                         "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 18),
899                 Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY);
900         assertVerified(
901                 verifyForMaxSdkVersion(
902                         "v1-sha1-sha256-manifest-and-sf-with-sha256-wrong-in-sf.apk", 17));
903     }
904 
905     @Test
testV1WithUnsupportedCharacterInZipEntryName()906     public void testV1WithUnsupportedCharacterInZipEntryName() throws Exception {
907         // Android Package Manager does not support ZIP entry names containing CR or LF
908         assertVerificationFailure(
909                 verify("v1-only-with-cr-in-entry-name.apk"),
910                 Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION);
911         assertVerificationFailure(
912                 verify("v1-only-with-lf-in-entry-name.apk"),
913                 Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION);
914     }
915 
916     @Test
testWeirdZipCompressionMethod()917     public void testWeirdZipCompressionMethod() throws Exception {
918         // Any ZIP compression method other than STORED is treated as DEFLATED by Android.
919         // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry,
920         // but the entry is actually Deflate-compressed.
921         assertVerified(verify("weird-compression-method.apk"));
922     }
923 
924     @Test
testZipCompressionMethodMismatchBetweenLfhAndCd()925     public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception {
926         // Android Package Manager ignores compressionMethod field in Local File Header and always
927         // uses the compressionMethod from Central Directory instead.
928         // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header
929         // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed.
930         assertVerified(verify("mismatched-compression-method.apk"));
931     }
932 
933     @Test
testV1SignedAttrs()934     public void testV1SignedAttrs() throws Exception {
935         String apk = "v1-only-with-signed-attrs.apk";
936         assertVerificationFailure(
937                 verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2),
938                 Issue.JAR_SIG_VERIFY_EXCEPTION);
939         assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT));
940 
941         apk = "v1-only-with-signed-attrs-signerInfo1-good-signerInfo2-good.apk";
942         assertVerificationFailure(
943                 verifyForMinSdkVersion(apk, AndroidSdkVersion.JELLY_BEAN_MR2),
944                 Issue.JAR_SIG_VERIFY_EXCEPTION);
945         assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.KITKAT));
946     }
947 
948     @Test
testV1SignedAttrsNotInDerOrder()949     public void testV1SignedAttrsNotInDerOrder() throws Exception {
950         // Android does not re-order SignedAttributes despite it being a SET OF. Pre-N, Android
951         // treated them as SEQUENCE OF, meaning no re-ordering is necessary. From N onwards, it
952         // treats them as SET OF, but does not re-encode into SET OF during verification if all
953         // attributes parsed fine.
954         assertVerified(verify("v1-only-with-signed-attrs-wrong-order.apk"));
955         assertVerified(
956                 verify("v1-only-with-signed-attrs-signerInfo1-wrong-order-signerInfo2-good.apk"));
957     }
958 
959     @Test
testV1SignedAttrsMissingContentType()960     public void testV1SignedAttrsMissingContentType() throws Exception {
961         // SignedAttributes must contain ContentType. Pre-N, Android ignores this requirement.
962         // Android N onwards rejects such APKs.
963         String apk = "v1-only-with-signed-attrs-missing-content-type.apk";
964         assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1));
965         assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION);
966         // Assert that this issue fails verification of the entire signature block, rather than
967         // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but
968         // verification does not get there.
969         apk = "v1-only-with-signed-attrs-signerInfo1-missing-content-type-signerInfo2-good.apk";
970         assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1));
971         assertVerificationFailure(verify(apk), Issue.JAR_SIG_VERIFY_EXCEPTION);
972     }
973 
974     @Test
testV1SignedAttrsWrongContentType()975     public void testV1SignedAttrsWrongContentType() throws Exception {
976         // ContentType of SignedAttributes must equal SignedData.encapContentInfo.eContentType.
977         // Pre-N, Android ignores this requirement.
978         // From N onwards, Android rejects such SignerInfos.
979         String apk = "v1-only-with-signed-attrs-wrong-content-type.apk";
980         assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1));
981         assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY);
982         // First SignerInfo does not verify on Android N and newer, but verification moves on to the
983         // second SignerInfo, which verifies.
984         apk = "v1-only-with-signed-attrs-signerInfo1-wrong-content-type-signerInfo2-good.apk";
985         assertVerified(verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1));
986         assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N));
987         // Although the APK's signature verifies on pre-N and N+, we reject such APKs because the
988         // APK's verification results in different verified SignerInfos (and thus potentially
989         // different signing certs) between pre-N and N+.
990         assertVerificationFailure(verify(apk), Issue.JAR_SIG_DID_NOT_VERIFY);
991     }
992 
993     @Test
testV1SignedAttrsMissingDigest()994     public void testV1SignedAttrsMissingDigest() throws Exception {
995         // Content digest must be present in SignedAttributes
996         String apk = "v1-only-with-signed-attrs-missing-digest.apk";
997         assertVerificationFailure(
998                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1),
999                 Issue.JAR_SIG_VERIFY_EXCEPTION);
1000         assertVerificationFailure(
1001                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION);
1002         // Assert that this issue fails verification of the entire signature block, rather than
1003         // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but
1004         // verification does not get there.
1005         apk = "v1-only-with-signed-attrs-signerInfo1-missing-digest-signerInfo2-good.apk";
1006         assertVerificationFailure(
1007                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1),
1008                 Issue.JAR_SIG_VERIFY_EXCEPTION);
1009         assertVerificationFailure(
1010                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_VERIFY_EXCEPTION);
1011     }
1012 
1013     @Test
testV1SignedAttrsMultipleGoodDigests()1014     public void testV1SignedAttrsMultipleGoodDigests() throws Exception {
1015         // Only one content digest must be present in SignedAttributes
1016         String apk = "v1-only-with-signed-attrs-multiple-good-digests.apk";
1017         assertVerificationFailure(
1018                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1),
1019                 Issue.JAR_SIG_PARSE_EXCEPTION);
1020         assertVerificationFailure(
1021                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION);
1022         // Assert that this issue fails verification of the entire signature block, rather than
1023         // skipping the broken SignerInfo. The second signer info SignerInfo verifies fine, but
1024         // verification does not get there.
1025         apk = "v1-only-with-signed-attrs-signerInfo1-multiple-good-digests-signerInfo2-good.apk";
1026         assertVerificationFailure(
1027                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1),
1028                 Issue.JAR_SIG_PARSE_EXCEPTION);
1029         assertVerificationFailure(
1030                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_PARSE_EXCEPTION);
1031     }
1032 
1033     @Test
testV1SignedAttrsWrongDigest()1034     public void testV1SignedAttrsWrongDigest() throws Exception {
1035         // Content digest in SignedAttributes does not match the contents
1036         String apk = "v1-only-with-signed-attrs-wrong-digest.apk";
1037         assertVerificationFailure(
1038                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY);
1039         assertVerificationFailure(
1040                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY);
1041         // First SignerInfo does not verify, but Android N and newer moves on to the second
1042         // SignerInfo, which verifies.
1043         apk = "v1-only-with-signed-attrs-signerInfo1-wrong-digest-signerInfo2-good.apk";
1044         assertVerificationFailure(
1045                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY);
1046         assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N));
1047     }
1048 
1049     @Test
testV1SignedAttrsWrongSignature()1050     public void testV1SignedAttrsWrongSignature() throws Exception {
1051         // Signature over SignedAttributes does not verify
1052         String apk = "v1-only-with-signed-attrs-wrong-signature.apk";
1053         assertVerificationFailure(
1054                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY);
1055         assertVerificationFailure(
1056                 verifyForMinSdkVersion(apk, AndroidSdkVersion.N), Issue.JAR_SIG_DID_NOT_VERIFY);
1057         // First SignerInfo does not verify, but Android N and newer moves on to the second
1058         // SignerInfo, which verifies.
1059         apk = "v1-only-with-signed-attrs-signerInfo1-wrong-signature-signerInfo2-good.apk";
1060         assertVerificationFailure(
1061                 verifyForMaxSdkVersion(apk, AndroidSdkVersion.N - 1), Issue.JAR_SIG_DID_NOT_VERIFY);
1062         assertVerified(verifyForMinSdkVersion(apk, AndroidSdkVersion.N));
1063     }
1064 
1065     @Test
testSourceStampBlock_correctSignature()1066     public void testSourceStampBlock_correctSignature() throws Exception {
1067         ApkVerifier.Result verificationResult = verify("valid-stamp.apk");
1068         // Verifies the signature of the APK.
1069         assertVerified(verificationResult);
1070         // Verifies the signature of source stamp.
1071         assertTrue(verificationResult.isSourceStampVerified());
1072     }
1073 
1074     @Test
verifySourceStamp_correctSignature()1075     public void verifySourceStamp_correctSignature() throws Exception {
1076         ApkVerifier.Result verificationResult = verifySourceStamp("valid-stamp.apk");
1077         // Since the API is only verifying the source stamp the result itself should be marked as
1078         // verified.
1079         assertVerified(verificationResult);
1080         assertSourceStampVerificationStatus(verificationResult,
1081                 SourceStampVerificationStatus.STAMP_VERIFIED);
1082 
1083         // The source stamp can also be verified by platform version; confirm the verification works
1084         // using just the max signature scheme version supported by that platform version.
1085         verificationResult = verifySourceStamp("valid-stamp.apk", 18, 18);
1086         assertVerified(verificationResult);
1087         assertSourceStampVerificationStatus(verificationResult,
1088                 SourceStampVerificationStatus.STAMP_VERIFIED);
1089 
1090         verificationResult = verifySourceStamp("valid-stamp.apk", 24, 24);
1091         assertVerified(verificationResult);
1092         assertSourceStampVerificationStatus(verificationResult,
1093                 SourceStampVerificationStatus.STAMP_VERIFIED);
1094 
1095         verificationResult = verifySourceStamp("valid-stamp.apk", 28, 28);
1096         assertVerified(verificationResult);
1097         assertSourceStampVerificationStatus(verificationResult,
1098                 SourceStampVerificationStatus.STAMP_VERIFIED);
1099     }
1100 
1101     @Test
testSourceStampBlock_signatureMissing()1102     public void testSourceStampBlock_signatureMissing() throws Exception {
1103         ApkVerifier.Result verificationResult = verify("stamp-without-block.apk");
1104         // A broken stamp should not block a signing scheme verified APK.
1105         assertVerified(verificationResult);
1106         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING);
1107     }
1108 
1109     @Test
verifySourceStamp_signatureMissing()1110     public void verifySourceStamp_signatureMissing() throws Exception {
1111         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-without-block.apk");
1112         assertSourceStampVerificationStatus(verificationResult,
1113                 SourceStampVerificationStatus.STAMP_NOT_VERIFIED);
1114         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_SIG_MISSING);
1115     }
1116 
1117     @Test
testSourceStampBlock_certificateMismatch()1118     public void testSourceStampBlock_certificateMismatch() throws Exception {
1119         ApkVerifier.Result verificationResult = verify("stamp-certificate-mismatch.apk");
1120         // A broken stamp should not block a signing scheme verified APK.
1121         assertVerified(verificationResult);
1122         assertSourceStampVerificationFailure(
1123                 verificationResult,
1124                 Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK);
1125     }
1126 
1127     @Test
verifySourceStamp_certificateMismatch()1128     public void verifySourceStamp_certificateMismatch() throws Exception {
1129         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-certificate-mismatch.apk");
1130         assertSourceStampVerificationStatus(verificationResult,
1131                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1132         assertSourceStampVerificationFailure(
1133                 verificationResult,
1134                 Issue.SOURCE_STAMP_CERTIFICATE_MISMATCH_BETWEEN_SIGNATURE_BLOCK_AND_APK);
1135     }
1136 
1137     @Test
testSourceStampBlock_v1OnlySignatureValidStamp()1138     public void testSourceStampBlock_v1OnlySignatureValidStamp() throws Exception {
1139         ApkVerifier.Result verificationResult = verify("v1-only-with-stamp.apk");
1140         assertVerified(verificationResult);
1141         assertTrue(verificationResult.isSourceStampVerified());
1142     }
1143 
1144     @Test
verifySourceStamp_v1OnlySignatureValidStamp()1145     public void verifySourceStamp_v1OnlySignatureValidStamp() throws Exception {
1146         ApkVerifier.Result verificationResult = verifySourceStamp("v1-only-with-stamp.apk");
1147         assertVerified(verificationResult);
1148         assertSourceStampVerificationStatus(verificationResult,
1149                 SourceStampVerificationStatus.STAMP_VERIFIED);
1150 
1151         // Confirm that the source stamp verification succeeds when specifying platform versions
1152         // that supported later signature scheme versions.
1153         verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 28, 28);
1154         assertVerified(verificationResult);
1155         assertSourceStampVerificationStatus(verificationResult,
1156                 SourceStampVerificationStatus.STAMP_VERIFIED);
1157 
1158         verificationResult = verifySourceStamp("v1-only-with-stamp.apk", 24, 24);
1159         assertVerified(verificationResult);
1160         assertSourceStampVerificationStatus(verificationResult,
1161                 SourceStampVerificationStatus.STAMP_VERIFIED);
1162     }
1163 
1164     @Test
testSourceStampBlock_v2OnlySignatureValidStamp()1165     public void testSourceStampBlock_v2OnlySignatureValidStamp() throws Exception {
1166         ApkVerifier.Result verificationResult = verify("v2-only-with-stamp.apk");
1167         assertVerified(verificationResult);
1168         assertTrue(verificationResult.isSourceStampVerified());
1169     }
1170 
1171     @Test
verifySourceStamp_v2OnlySignatureValidStamp()1172     public void verifySourceStamp_v2OnlySignatureValidStamp() throws Exception {
1173         ApkVerifier.Result verificationResult = verifySourceStamp("v2-only-with-stamp.apk");
1174         assertVerified(verificationResult);
1175         assertSourceStampVerificationStatus(verificationResult,
1176                 SourceStampVerificationStatus.STAMP_VERIFIED);
1177 
1178         // Confirm that the source stamp verification succeeds when specifying a platform version
1179         // that supports a later signature scheme version.
1180         verificationResult = verifySourceStamp("v2-only-with-stamp.apk", 28, 28);
1181         assertVerified(verificationResult);
1182         assertSourceStampVerificationStatus(verificationResult,
1183                 SourceStampVerificationStatus.STAMP_VERIFIED);
1184     }
1185 
1186     @Test
testSourceStampBlock_v3OnlySignatureValidStamp()1187     public void testSourceStampBlock_v3OnlySignatureValidStamp() throws Exception {
1188         ApkVerifier.Result verificationResult = verify("v3-only-with-stamp.apk");
1189         assertVerified(verificationResult);
1190         assertTrue(verificationResult.isSourceStampVerified());
1191     }
1192 
1193     @Test
verifySourceStamp_v3OnlySignatureValidStamp()1194     public void verifySourceStamp_v3OnlySignatureValidStamp() throws Exception {
1195         ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk");
1196         assertVerified(verificationResult);
1197         assertSourceStampVerificationStatus(verificationResult,
1198                 SourceStampVerificationStatus.STAMP_VERIFIED);
1199     }
1200 
1201     @Test
testSourceStampBlock_apkHashMismatch_v1SignatureScheme()1202     public void testSourceStampBlock_apkHashMismatch_v1SignatureScheme() throws Exception {
1203         ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v1.apk");
1204         // A broken stamp should not block a signing scheme verified APK.
1205         assertVerified(verificationResult);
1206         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1207     }
1208 
1209     @Test
verifySourceStamp_apkHashMismatch_v1SignatureScheme()1210     public void verifySourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception {
1211         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v1.apk");
1212         assertSourceStampVerificationStatus(verificationResult,
1213                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1214         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1215     }
1216 
1217     @Test
testSourceStampBlock_apkHashMismatch_v2SignatureScheme()1218     public void testSourceStampBlock_apkHashMismatch_v2SignatureScheme() throws Exception {
1219         ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v2.apk");
1220         // A broken stamp should not block a signing scheme verified APK.
1221         assertVerified(verificationResult);
1222         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1223     }
1224 
1225     @Test
verifySourceStamp_apkHashMismatch_v2SignatureScheme()1226     public void verifySourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception {
1227         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v2.apk");
1228         assertSourceStampVerificationStatus(verificationResult,
1229                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1230         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1231     }
1232 
1233     @Test
testSourceStampBlock_apkHashMismatch_v3SignatureScheme()1234     public void testSourceStampBlock_apkHashMismatch_v3SignatureScheme() throws Exception {
1235         ApkVerifier.Result verificationResult = verify("stamp-apk-hash-mismatch-v3.apk");
1236         // A broken stamp should not block a signing scheme verified APK.
1237         assertVerified(verificationResult);
1238         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1239     }
1240 
1241     @Test
verifySourceStamp_apkHashMismatch_v3SignatureScheme()1242     public void verifySourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception {
1243         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-apk-hash-mismatch-v3.apk");
1244         assertSourceStampVerificationStatus(verificationResult,
1245                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1246         assertSourceStampVerificationFailure(verificationResult, Issue.SOURCE_STAMP_DID_NOT_VERIFY);
1247     }
1248 
1249     @Test
testSourceStampBlock_malformedSignature()1250     public void testSourceStampBlock_malformedSignature() throws Exception {
1251         ApkVerifier.Result verificationResult = verify("stamp-malformed-signature.apk");
1252         // A broken stamp should not block a signing scheme verified APK.
1253         assertVerified(verificationResult);
1254         assertSourceStampVerificationFailure(
1255                 verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE);
1256     }
1257 
1258     @Test
verifySourceStamp_malformedSignature()1259     public void verifySourceStamp_malformedSignature() throws Exception {
1260         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-malformed-signature.apk");
1261         assertSourceStampVerificationStatus(verificationResult,
1262                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1263         assertSourceStampVerificationFailure(
1264                 verificationResult, Issue.SOURCE_STAMP_MALFORMED_SIGNATURE);
1265     }
1266 
1267     @Test
verifySourceStamp_expectedDigestMatchesActual()1268     public void verifySourceStamp_expectedDigestMatchesActual() throws Exception {
1269         // The ApkVerifier provides an API to specify the expected certificate digest; this test
1270         // verifies that the test runs through to completion when the actual digest matches the
1271         // provided value.
1272         ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk",
1273                 RSA_2048_CERT_SHA256_DIGEST);
1274         assertVerified(verificationResult);
1275         assertSourceStampVerificationStatus(verificationResult,
1276                 SourceStampVerificationStatus.STAMP_VERIFIED);
1277     }
1278 
1279     @Test
verifySourceStamp_expectedDigestMismatch()1280     public void verifySourceStamp_expectedDigestMismatch() throws Exception {
1281         // If the caller requests source stamp verification with an expected cert digest that does
1282         // not match the actual digest in the APK the verifier should report the mismatch.
1283         ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk",
1284                 EC_P256_CERT_SHA256_DIGEST);
1285         assertSourceStampVerificationStatus(verificationResult,
1286                 SourceStampVerificationStatus.CERT_DIGEST_MISMATCH);
1287         assertSourceStampVerificationFailure(verificationResult,
1288                 Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH);
1289     }
1290 
1291     @Test
verifySourceStamp_validStampLineage()1292     public void verifySourceStamp_validStampLineage() throws Exception {
1293         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-valid.apk");
1294         assertVerified(verificationResult);
1295         assertSourceStampVerificationStatus(verificationResult,
1296                 SourceStampVerificationStatus.STAMP_VERIFIED);
1297     }
1298 
1299     @Test
verifySourceStamp_invalidStampLineage()1300     public void verifySourceStamp_invalidStampLineage() throws Exception {
1301         ApkVerifier.Result verificationResult = verifySourceStamp("stamp-lineage-invalid.apk");
1302         assertSourceStampVerificationStatus(verificationResult,
1303                 SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
1304         assertSourceStampVerificationFailure(verificationResult,
1305                 Issue.SOURCE_STAMP_POR_CERT_MISMATCH);
1306     }
1307 
1308     @Test
apkVerificationIssueAdapter_verifyAllBaseIssuesMapped()1309     public void apkVerificationIssueAdapter_verifyAllBaseIssuesMapped() throws Exception {
1310         Field[] fields = ApkVerificationIssue.class.getFields();
1311         StringBuilder msg = new StringBuilder();
1312         for (Field field : fields) {
1313             // All public static int fields in the ApkVerificationIssue class should be issue IDs;
1314             // if any are added that are not intended as IDs a filter set should be applied to this
1315             // test.
1316             if (Modifier.isStatic(field.getModifiers()) && field.getType() == int.class) {
1317                 if (!ApkVerifier.ApkVerificationIssueAdapter
1318                         .sVerificationIssueIdToIssue.containsKey(field.get(null))) {
1319                     if (msg.length() > 0) {
1320                         msg.append('\n');
1321                     }
1322                     msg.append(
1323                             "A mapping is required from ApkVerificationIssue." + field.getName()
1324                                     + " to an ApkVerifier.Issue in ApkVerificationIssueAdapter");
1325                 }
1326             }
1327         }
1328         if (msg.length() > 0) {
1329             fail(msg.toString());
1330         }
1331     }
1332 
verify(String apkFilenameInResources)1333     private ApkVerifier.Result verify(String apkFilenameInResources)
1334             throws IOException, ApkFormatException, NoSuchAlgorithmException {
1335         return verify(apkFilenameInResources, null, null);
1336     }
1337 
verifyForMinSdkVersion( String apkFilenameInResources, int minSdkVersion)1338     private ApkVerifier.Result verifyForMinSdkVersion(
1339             String apkFilenameInResources, int minSdkVersion)
1340             throws IOException, ApkFormatException, NoSuchAlgorithmException {
1341         return verify(apkFilenameInResources, minSdkVersion, null);
1342     }
1343 
verifyForMaxSdkVersion( String apkFilenameInResources, int maxSdkVersion)1344     private ApkVerifier.Result verifyForMaxSdkVersion(
1345             String apkFilenameInResources, int maxSdkVersion)
1346             throws IOException, ApkFormatException, NoSuchAlgorithmException {
1347         return verify(apkFilenameInResources, null, maxSdkVersion);
1348     }
1349 
verify( String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)1350     private ApkVerifier.Result verify(
1351             String apkFilenameInResources,
1352             Integer minSdkVersionOverride,
1353             Integer maxSdkVersionOverride)
1354             throws IOException, ApkFormatException, NoSuchAlgorithmException {
1355         byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources);
1356 
1357         ApkVerifier.Builder builder =
1358                 new ApkVerifier.Builder(DataSources.asDataSource(ByteBuffer.wrap(apkBytes)));
1359         if (minSdkVersionOverride != null) {
1360             builder.setMinCheckedPlatformVersion(minSdkVersionOverride);
1361         }
1362         if (maxSdkVersionOverride != null) {
1363             builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride);
1364         }
1365         return builder.build().verify();
1366     }
1367 
verifySourceStamp(String apkFilenameInResources)1368     private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources) throws Exception {
1369         return verifySourceStamp(apkFilenameInResources, null, null, null);
1370     }
1371 
verifySourceStamp(String apkFilenameInResources, String expectedCertDigest)1372     private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources,
1373             String expectedCertDigest) throws Exception {
1374         return verifySourceStamp(apkFilenameInResources, expectedCertDigest, null, null);
1375     }
1376 
verifySourceStamp(String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)1377     private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources,
1378             Integer minSdkVersionOverride, Integer maxSdkVersionOverride) throws Exception {
1379         return verifySourceStamp(apkFilenameInResources, null, minSdkVersionOverride,
1380                 maxSdkVersionOverride);
1381     }
1382 
verifySourceStamp(String apkFilenameInResources, String expectedCertDigest, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)1383     private ApkVerifier.Result verifySourceStamp(String apkFilenameInResources,
1384             String expectedCertDigest, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)
1385             throws Exception {
1386         byte[] apkBytes = Resources.toByteArray(getClass(), apkFilenameInResources);
1387         ApkVerifier.Builder builder = new ApkVerifier.Builder(
1388                 DataSources.asDataSource(ByteBuffer.wrap(apkBytes)));
1389         if (minSdkVersionOverride != null) {
1390             builder.setMinCheckedPlatformVersion(minSdkVersionOverride);
1391         }
1392         if (maxSdkVersionOverride != null) {
1393             builder.setMaxCheckedPlatformVersion(maxSdkVersionOverride);
1394         }
1395         return builder.build().verifySourceStamp(expectedCertDigest);
1396     }
1397 
assertVerified(ApkVerifier.Result result)1398     static void assertVerified(ApkVerifier.Result result) {
1399         assertVerified(result, "APK");
1400     }
1401 
assertVerified(ApkVerifier.Result result, String apkId)1402     static void assertVerified(ApkVerifier.Result result, String apkId) {
1403         if (result.isVerified()) {
1404             return;
1405         }
1406 
1407         StringBuilder msg = new StringBuilder();
1408         for (IssueWithParams issue : result.getErrors()) {
1409             if (msg.length() > 0) {
1410                 msg.append('\n');
1411             }
1412             msg.append(issue);
1413         }
1414         for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) {
1415             String signerName = signer.getName();
1416             for (IssueWithParams issue : signer.getErrors()) {
1417                 if (msg.length() > 0) {
1418                     msg.append('\n');
1419                 }
1420                 msg.append("JAR signer ")
1421                         .append(signerName)
1422                         .append(": ")
1423                         .append(issue.getIssue())
1424                         .append(": ")
1425                         .append(issue);
1426             }
1427         }
1428         for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) {
1429             String signerName = "signer #" + (signer.getIndex() + 1);
1430             for (IssueWithParams issue : signer.getErrors()) {
1431                 if (msg.length() > 0) {
1432                     msg.append('\n');
1433                 }
1434                 msg.append("APK Signature Scheme v2 signer ")
1435                         .append(signerName)
1436                         .append(": ")
1437                         .append(issue.getIssue())
1438                         .append(": ")
1439                         .append(issue);
1440             }
1441         }
1442         for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) {
1443             String signerName = "signer #" + (signer.getIndex() + 1);
1444             for (IssueWithParams issue : signer.getErrors()) {
1445                 if (msg.length() > 0) {
1446                     msg.append('\n');
1447                 }
1448                 msg.append("APK Signature Scheme v3 signer ")
1449                         .append(signerName)
1450                         .append(": ")
1451                         .append(issue.getIssue())
1452                         .append(": ")
1453                         .append(issue);
1454             }
1455         }
1456 
1457         fail(apkId + " did not verify: " + msg);
1458     }
1459 
assertVerified( String apkFilenameInResources, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)1460     private void assertVerified(
1461             String apkFilenameInResources,
1462             Integer minSdkVersionOverride,
1463             Integer maxSdkVersionOverride)
1464             throws Exception {
1465         assertVerified(
1466                 verify(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride),
1467                 apkFilenameInResources);
1468     }
1469 
assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)1470     static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) {
1471         if (result.isVerified()) {
1472             fail("APK verification succeeded instead of failing with " + expectedIssue);
1473             return;
1474         }
1475 
1476         StringBuilder msg = new StringBuilder();
1477         for (IssueWithParams issue : result.getErrors()) {
1478             if (expectedIssue.equals(issue.getIssue())) {
1479                 return;
1480             }
1481             if (msg.length() > 0) {
1482                 msg.append('\n');
1483             }
1484             msg.append(issue);
1485         }
1486         for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) {
1487             String signerName = signer.getName();
1488             for (ApkVerifier.IssueWithParams issue : signer.getErrors()) {
1489                 if (expectedIssue.equals(issue.getIssue())) {
1490                     return;
1491                 }
1492                 if (msg.length() > 0) {
1493                     msg.append('\n');
1494                 }
1495                 msg.append("JAR signer ")
1496                         .append(signerName)
1497                         .append(": ")
1498                         .append(issue.getIssue())
1499                         .append(" ")
1500                         .append(issue);
1501             }
1502         }
1503         for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) {
1504             String signerName = "signer #" + (signer.getIndex() + 1);
1505             for (IssueWithParams issue : signer.getErrors()) {
1506                 if (expectedIssue.equals(issue.getIssue())) {
1507                     return;
1508                 }
1509                 if (msg.length() > 0) {
1510                     msg.append('\n');
1511                 }
1512                 msg.append("APK Signature Scheme v2 signer ")
1513                         .append(signerName)
1514                         .append(": ")
1515                         .append(issue);
1516             }
1517         }
1518         for (ApkVerifier.Result.V3SchemeSignerInfo signer : result.getV3SchemeSigners()) {
1519             String signerName = "signer #" + (signer.getIndex() + 1);
1520             for (IssueWithParams issue : signer.getErrors()) {
1521                 if (expectedIssue.equals(issue.getIssue())) {
1522                     return;
1523                 }
1524                 if (msg.length() > 0) {
1525                     msg.append('\n');
1526                 }
1527                 msg.append("APK Signature Scheme v3 signer ")
1528                         .append(signerName)
1529                         .append(": ")
1530                         .append(issue);
1531             }
1532         }
1533 
1534         fail(
1535                 "APK failed verification for the wrong reason"
1536                         + ". Expected: "
1537                         + expectedIssue
1538                         + ", actual: "
1539                         + msg);
1540     }
1541 
assertSourceStampVerificationFailure( ApkVerifier.Result result, Issue expectedIssue)1542     private static void assertSourceStampVerificationFailure(
1543             ApkVerifier.Result result, Issue expectedIssue) {
1544         if (result.isSourceStampVerified()) {
1545             fail(
1546                     "APK source stamp verification succeeded instead of failing with "
1547                             + expectedIssue);
1548             return;
1549         }
1550 
1551         StringBuilder msg = new StringBuilder();
1552         List<IssueWithParams> resultIssueWithParams =
1553                 Stream.of(result.getErrors(), result.getWarnings())
1554                         .filter(Objects::nonNull)
1555                         .flatMap(Collection::stream)
1556                         .collect(Collectors.toList());
1557         for (IssueWithParams issue : resultIssueWithParams) {
1558             if (expectedIssue.equals(issue.getIssue())) {
1559                 return;
1560             }
1561             if (msg.length() > 0) {
1562                 msg.append('\n');
1563             }
1564             msg.append(issue);
1565         }
1566 
1567         ApkVerifier.Result.SourceStampInfo signer = result.getSourceStampInfo();
1568         if (signer != null) {
1569             List<IssueWithParams> sourceStampIssueWithParams =
1570                     Stream.of(signer.getErrors(), signer.getWarnings())
1571                             .filter(Objects::nonNull)
1572                             .flatMap(Collection::stream)
1573                             .collect(Collectors.toList());
1574             for (IssueWithParams issue : sourceStampIssueWithParams) {
1575                 if (expectedIssue.equals(issue.getIssue())) {
1576                     return;
1577                 }
1578                 if (msg.length() > 0) {
1579                     msg.append('\n');
1580                 }
1581                 msg.append("APK SourceStamp signer").append(": ").append(issue);
1582             }
1583         }
1584 
1585         fail(
1586                 "APK source stamp failed verification for the wrong reason"
1587                         + ". Expected: "
1588                         + expectedIssue
1589                         + ", actual: "
1590                         + msg);
1591     }
1592 
assertSourceStampVerificationStatus(ApkVerifier.Result result, SourceStampVerificationStatus verificationStatus)1593     private static void assertSourceStampVerificationStatus(ApkVerifier.Result result,
1594             SourceStampVerificationStatus verificationStatus) throws Exception {
1595         assertEquals(verificationStatus,
1596                 result.getSourceStampInfo().getSourceStampVerificationStatus());
1597     }
1598 
assertVerificationFailure( String apkFilenameInResources, ApkVerifier.Issue expectedIssue)1599     private void assertVerificationFailure(
1600             String apkFilenameInResources, ApkVerifier.Issue expectedIssue) throws Exception {
1601         assertVerificationFailure(verify(apkFilenameInResources), expectedIssue);
1602     }
1603 
assertVerifiedForEach(String apkFilenamePatternInResources, String[] args)1604     private void assertVerifiedForEach(String apkFilenamePatternInResources, String[] args)
1605             throws Exception {
1606         assertVerifiedForEach(apkFilenamePatternInResources, args, null, null);
1607     }
1608 
assertVerifiedForEach( String apkFilenamePatternInResources, String[] args, Integer minSdkVersionOverride, Integer maxSdkVersionOverride)1609     private void assertVerifiedForEach(
1610             String apkFilenamePatternInResources,
1611             String[] args,
1612             Integer minSdkVersionOverride,
1613             Integer maxSdkVersionOverride)
1614             throws Exception {
1615         for (String arg : args) {
1616             String apkFilenameInResources =
1617                     String.format(Locale.US, apkFilenamePatternInResources, arg);
1618             assertVerified(apkFilenameInResources, minSdkVersionOverride, maxSdkVersionOverride);
1619         }
1620     }
1621 
assertVerifiedForEachForMinSdkVersion( String apkFilenameInResources, String[] args, int minSdkVersion)1622     private void assertVerifiedForEachForMinSdkVersion(
1623             String apkFilenameInResources, String[] args, int minSdkVersion) throws Exception {
1624         assertVerifiedForEach(apkFilenameInResources, args, minSdkVersion, null);
1625     }
1626 
sha256(byte[] msg)1627     private static byte[] sha256(byte[] msg) {
1628         try {
1629             return MessageDigest.getInstance("SHA-256").digest(msg);
1630         } catch (NoSuchAlgorithmException e) {
1631             throw new RuntimeException("Failed to create SHA-256 MessageDigest", e);
1632         }
1633     }
1634 
assumeThatRsaPssAvailable()1635     private static void assumeThatRsaPssAvailable() {
1636         Assume.assumeTrue(Security.getProviders("Signature.SHA256withRSA/PSS") != null);
1637     }
1638 }
1639