1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.apksig;
18 
19 import static com.android.apksig.ApkVerifier.Result.V3SchemeSignerInfo;
20 import static com.android.apksig.ApkVerifierTest.assertVerificationWarning;
21 import static com.android.apksig.SigningCertificateLineage.SignerCapabilities;
22 import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSigners;
23 import static com.android.apksig.SigningCertificateLineageTest.assertLineageContainsExpectedSignersWithCapabilities;
24 import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
25 import static com.android.apksig.apk.ApkUtils.findZipSections;
26 import static com.android.apksig.internal.util.Resources.EC_P256_2_SIGNER_RESOURCE_NAME;
27 import static com.android.apksig.internal.util.Resources.EC_P256_SIGNER_RESOURCE_NAME;
28 import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS;
29 import static com.android.apksig.internal.util.Resources.FIRST_RSA_2048_SIGNER_RESOURCE_NAME;
30 import static com.android.apksig.internal.util.Resources.FIRST_RSA_4096_SIGNER_RESOURCE_NAME;
31 import static com.android.apksig.internal.util.Resources.LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME;
32 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME;
33 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME;
34 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME;
35 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME;
36 import static com.android.apksig.internal.util.Resources.LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME;
37 import static com.android.apksig.internal.util.Resources.SECOND_RSA_2048_SIGNER_RESOURCE_NAME;
38 // BEGIN-AOSP
39 import static com.android.apksig.internal.util.Resources.TEST_GCP_KEY_RING;
40 // END-AOSP
41 import static com.android.apksig.internal.util.Resources.THIRD_RSA_2048_SIGNER_RESOURCE_NAME;
42 
43 import static org.junit.Assert.assertArrayEquals;
44 import static org.junit.Assert.assertEquals;
45 import static org.junit.Assert.assertFalse;
46 import static org.junit.Assert.assertNotNull;
47 import static org.junit.Assert.assertNull;
48 import static org.junit.Assert.assertThrows;
49 import static org.junit.Assert.assertTrue;
50 import static org.junit.Assert.fail;
51 import static org.junit.Assume.assumeNoException;
52 import static org.junit.Assume.assumeTrue;
53 
54 import com.android.apksig.ApkVerifier.Issue;
55 import com.android.apksig.apk.ApkFormatException;
56 import com.android.apksig.apk.ApkUtils;
57 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
58 import com.android.apksig.internal.apk.SignatureInfo;
59 import com.android.apksig.internal.apk.stamp.SourceStampConstants;
60 import com.android.apksig.internal.apk.v1.V1SchemeVerifier;
61 import com.android.apksig.internal.apk.v2.V2SchemeConstants;
62 import com.android.apksig.internal.apk.v3.V3SchemeConstants;
63 import com.android.apksig.internal.asn1.Asn1BerParser;
64 import com.android.apksig.internal.util.AndroidSdkVersion;
65 import com.android.apksig.internal.util.Pair;
66 import com.android.apksig.internal.util.Resources;
67 import com.android.apksig.internal.x509.RSAPublicKey;
68 import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
69 import com.android.apksig.internal.zip.CentralDirectoryRecord;
70 import com.android.apksig.internal.zip.LocalFileRecord;
71 // BEGIN-AOSP
72 import com.android.apksig.kms.aws.AwsSignerConfigGenerator;
73 import com.android.apksig.kms.aws.KeyAliasClient;
74 import com.android.apksig.kms.gcp.GcpSignerConfigGenerator;
75 import com.android.apksig.kms.gcp.KeyRingClient;
76 // END-AOSP
77 import com.android.apksig.util.DataSource;
78 import com.android.apksig.util.DataSources;
79 import com.android.apksig.zip.ZipFormatException;
80 
81 import org.bouncycastle.jce.provider.BouncyCastleProvider;
82 import org.junit.Rule;
83 import org.junit.Test;
84 import org.junit.rules.TemporaryFolder;
85 import org.junit.runner.RunWith;
86 import org.junit.runners.JUnit4;
87 
88 import java.io.File;
89 import java.io.FileInputStream;
90 import java.io.IOException;
91 import java.io.RandomAccessFile;
92 import java.math.BigInteger;
93 import java.nio.ByteBuffer;
94 import java.nio.file.Files;
95 import java.nio.file.Paths;
96 import java.security.MessageDigest;
97 import java.security.NoSuchAlgorithmException;
98 import java.security.PrivateKey;
99 import java.security.Security;
100 import java.security.Signature;
101 import java.security.SignatureException;
102 import java.security.cert.X509Certificate;
103 import java.util.ArrayList;
104 import java.util.Arrays;
105 import java.util.Collection;
106 import java.util.Collections;
107 import java.util.HashSet;
108 import java.util.List;
109 import java.util.Set;
110 import java.util.zip.ZipEntry;
111 import java.util.zip.ZipInputStream;
112 
113 @RunWith(JUnit4.class)
114 public class ApkSignerTest {
115 
116     /**
117      * Whether to preserve, as files, outputs of failed tests. This is useful for investigating test
118      * failures.
119      */
120     private static final boolean KEEP_FAILING_OUTPUT_AS_FILES = false;
121 
122     private static final SignerCapabilities DEFAULT_CAPABILITIES =
123             new SignerCapabilities.Builder().build();
124     private static final SignerCapabilities NO_CAPABILITIES = new SignerCapabilities.Builder(
125             0).build();
126 
127     // These are the ID and value of an extra signature block within the APK signing block that
128     // can be preserved through the setOtherSignersSignaturesPreserved API.
129     private final int EXTRA_BLOCK_ID = 0x7e57c0de;
130     private final byte[] EXTRA_BLOCK_VALUE = {0, 1, 2, 3, 4, 5, 6, 7};
131 
132     @Rule
133     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
134 
main(String[] params)135     public static void main(String[] params) throws Exception {
136         File outDir = (params.length > 0) ? new File(params[0]) : new File(".");
137         generateGoldenFiles(outDir);
138     }
139 
generateGoldenFiles(File outDir)140     private static void generateGoldenFiles(File outDir) throws Exception {
141         System.out.println(
142                 "Generating golden files "
143                         + ApkSignerTest.class.getSimpleName()
144                         + " into "
145                         + outDir);
146         if (!outDir.exists()) {
147             if (!outDir.mkdirs()) {
148                 throw new IOException("Failed to create directory: " + outDir);
149             }
150         }
151         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
152                 Collections.singletonList(
153                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
154         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
155                 Arrays.asList(
156                         rsa2048SignerConfig.get(0),
157                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
158         SigningCertificateLineage lineage =
159                 Resources.toSigningCertificateLineage(
160                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
161 
162         signGolden(
163                 "golden-unaligned-in.apk",
164                 new File(outDir, "golden-unaligned-out.apk"),
165                 new ApkSigner.Builder(rsa2048SignerConfig));
166         signGolden(
167                 "golden-legacy-aligned-in.apk",
168                 new File(outDir, "golden-legacy-aligned-out.apk"),
169                 new ApkSigner.Builder(rsa2048SignerConfig));
170         signGolden(
171                 "golden-aligned-in.apk",
172                 new File(outDir, "golden-aligned-out.apk"),
173                 new ApkSigner.Builder(rsa2048SignerConfig));
174 
175         signGolden(
176                 "golden-unaligned-in.apk",
177                 new File(outDir, "golden-unaligned-v1-out.apk"),
178                 new ApkSigner.Builder(rsa2048SignerConfig)
179                         .setV1SigningEnabled(true)
180                         .setV2SigningEnabled(false)
181                         .setV3SigningEnabled(false)
182                         .setV4SigningEnabled(false));
183         signGolden(
184                 "golden-legacy-aligned-in.apk",
185                 new File(outDir, "golden-legacy-aligned-v1-out.apk"),
186                 new ApkSigner.Builder(rsa2048SignerConfig)
187                         .setV1SigningEnabled(true)
188                         .setV2SigningEnabled(false)
189                         .setV3SigningEnabled(false)
190                         .setV4SigningEnabled(false));
191         signGolden(
192                 "golden-aligned-in.apk",
193                 new File(outDir, "golden-aligned-v1-out.apk"),
194                 new ApkSigner.Builder(rsa2048SignerConfig)
195                         .setV1SigningEnabled(true)
196                         .setV2SigningEnabled(false)
197                         .setV3SigningEnabled(false)
198                         .setV4SigningEnabled(false));
199 
200         signGolden(
201                 "golden-unaligned-in.apk",
202                 new File(outDir, "golden-unaligned-v2-out.apk"),
203                 new ApkSigner.Builder(rsa2048SignerConfig)
204                         .setV1SigningEnabled(false)
205                         .setV2SigningEnabled(true)
206                         .setV3SigningEnabled(false));
207         signGolden(
208                 "golden-legacy-aligned-in.apk",
209                 new File(outDir, "golden-legacy-aligned-v2-out.apk"),
210                 new ApkSigner.Builder(rsa2048SignerConfig)
211                         .setV1SigningEnabled(false)
212                         .setV2SigningEnabled(true)
213                         .setV3SigningEnabled(false));
214         signGolden(
215                 "golden-aligned-in.apk",
216                 new File(outDir, "golden-aligned-v2-out.apk"),
217                 new ApkSigner.Builder(rsa2048SignerConfig)
218                         .setV1SigningEnabled(false)
219                         .setV2SigningEnabled(true)
220                         .setV3SigningEnabled(false));
221 
222         signGolden(
223                 "golden-unaligned-in.apk",
224                 new File(outDir, "golden-unaligned-v3-out.apk"),
225                 new ApkSigner.Builder(rsa2048SignerConfig)
226                         .setV1SigningEnabled(false)
227                         .setV2SigningEnabled(false)
228                         .setV3SigningEnabled(true));
229         signGolden(
230                 "golden-legacy-aligned-in.apk",
231                 new File(outDir, "golden-legacy-aligned-v3-out.apk"),
232                 new ApkSigner.Builder(rsa2048SignerConfig)
233                         .setV1SigningEnabled(false)
234                         .setV2SigningEnabled(false)
235                         .setV3SigningEnabled(true));
236         signGolden(
237                 "golden-aligned-in.apk",
238                 new File(outDir, "golden-aligned-v3-out.apk"),
239                 new ApkSigner.Builder(rsa2048SignerConfig)
240                         .setV1SigningEnabled(false)
241                         .setV2SigningEnabled(false)
242                         .setV3SigningEnabled(true));
243 
244         signGolden(
245                 "golden-unaligned-in.apk",
246                 new File(outDir, "golden-unaligned-v3-lineage-out.apk"),
247                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
248                         .setV1SigningEnabled(false)
249                         .setV2SigningEnabled(false)
250                         .setV3SigningEnabled(true)
251                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
252                         .setSigningCertificateLineage(lineage));
253 
254         signGolden(
255                 "golden-legacy-aligned-in.apk",
256                 new File(outDir, "golden-legacy-aligned-v3-lineage-out.apk"),
257                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
258                         .setV1SigningEnabled(false)
259                         .setV2SigningEnabled(false)
260                         .setV3SigningEnabled(true)
261                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
262                         .setSigningCertificateLineage(lineage));
263         signGolden(
264                 "golden-aligned-in.apk",
265                 new File(outDir, "golden-aligned-v3-lineage-out.apk"),
266                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
267                         .setV1SigningEnabled(false)
268                         .setV2SigningEnabled(false)
269                         .setV3SigningEnabled(true)
270                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
271                         .setSigningCertificateLineage(lineage));
272 
273         signGolden(
274                 "golden-unaligned-in.apk",
275                 new File(outDir, "golden-unaligned-v1v2-out.apk"),
276                 new ApkSigner.Builder(rsa2048SignerConfig)
277                         .setV1SigningEnabled(true)
278                         .setV2SigningEnabled(true)
279                         .setV3SigningEnabled(false));
280         signGolden(
281                 "golden-legacy-aligned-in.apk",
282                 new File(outDir, "golden-legacy-aligned-v1v2-out.apk"),
283                 new ApkSigner.Builder(rsa2048SignerConfig)
284                         .setV1SigningEnabled(true)
285                         .setV2SigningEnabled(true)
286                         .setV3SigningEnabled(false));
287         signGolden(
288                 "golden-aligned-in.apk",
289                 new File(outDir, "golden-aligned-v1v2-out.apk"),
290                 new ApkSigner.Builder(rsa2048SignerConfig)
291                         .setV1SigningEnabled(true)
292                         .setV2SigningEnabled(true)
293                         .setV3SigningEnabled(false));
294 
295         signGolden(
296                 "golden-unaligned-in.apk",
297                 new File(outDir, "golden-unaligned-v2v3-out.apk"),
298                 new ApkSigner.Builder(rsa2048SignerConfig)
299                         .setV1SigningEnabled(false)
300                         .setV2SigningEnabled(true)
301                         .setV3SigningEnabled(true));
302         signGolden(
303                 "golden-legacy-aligned-in.apk",
304                 new File(outDir, "golden-legacy-aligned-v2v3-out.apk"),
305                 new ApkSigner.Builder(rsa2048SignerConfig)
306                         .setV1SigningEnabled(false)
307                         .setV2SigningEnabled(true)
308                         .setV3SigningEnabled(true));
309         signGolden(
310                 "golden-aligned-in.apk",
311                 new File(outDir, "golden-aligned-v2v3-out.apk"),
312                 new ApkSigner.Builder(rsa2048SignerConfig)
313                         .setV1SigningEnabled(false)
314                         .setV2SigningEnabled(true)
315                         .setV3SigningEnabled(true));
316         signGolden(
317                 "golden-unaligned-in.apk",
318                 new File(outDir, "golden-unaligned-v2v3-lineage-out.apk"),
319                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
320                         .setV1SigningEnabled(false)
321                         .setV2SigningEnabled(true)
322                         .setV3SigningEnabled(true)
323                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
324                         .setSigningCertificateLineage(lineage));
325         signGolden(
326                 "golden-legacy-aligned-in.apk",
327                 new File(outDir, "golden-legacy-aligned-v2v3-lineage-out.apk"),
328                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
329                         .setV1SigningEnabled(false)
330                         .setV2SigningEnabled(true)
331                         .setV3SigningEnabled(true)
332                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
333                         .setSigningCertificateLineage(lineage));
334         signGolden(
335                 "golden-aligned-in.apk",
336                 new File(outDir, "golden-aligned-v2v3-lineage-out.apk"),
337                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
338                         .setV1SigningEnabled(false)
339                         .setV2SigningEnabled(true)
340                         .setV3SigningEnabled(true)
341                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
342                         .setSigningCertificateLineage(lineage));
343 
344         signGolden(
345                 "golden-unaligned-in.apk",
346                 new File(outDir, "golden-unaligned-v1v2v3-out.apk"),
347                 new ApkSigner.Builder(rsa2048SignerConfig)
348                         .setV1SigningEnabled(true)
349                         .setV2SigningEnabled(true)
350                         .setV3SigningEnabled(true));
351         signGolden(
352                 "golden-legacy-aligned-in.apk",
353                 new File(outDir, "golden-legacy-aligned-v1v2v3-out.apk"),
354                 new ApkSigner.Builder(rsa2048SignerConfig)
355                         .setV1SigningEnabled(true)
356                         .setV2SigningEnabled(true)
357                         .setV3SigningEnabled(true));
358         signGolden(
359                 "golden-aligned-in.apk",
360                 new File(outDir, "golden-aligned-v1v2v3-out.apk"),
361                 new ApkSigner.Builder(rsa2048SignerConfig)
362                         .setV1SigningEnabled(true)
363                         .setV2SigningEnabled(true)
364                         .setV3SigningEnabled(true));
365         signGolden(
366                 "golden-unaligned-in.apk",
367                 new File(outDir, "golden-unaligned-v1v2v3-lineage-out.apk"),
368                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
369                         .setV1SigningEnabled(true)
370                         .setV2SigningEnabled(true)
371                         .setV3SigningEnabled(true)
372                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
373                         .setSigningCertificateLineage(lineage));
374         signGolden(
375                 "golden-legacy-aligned-in.apk",
376                 new File(outDir, "golden-legacy-aligned-v1v2v3-lineage-out.apk"),
377                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
378                         .setV1SigningEnabled(true)
379                         .setV2SigningEnabled(true)
380                         .setV3SigningEnabled(true)
381                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
382                         .setSigningCertificateLineage(lineage));
383         signGolden(
384                 "golden-aligned-in.apk",
385                 new File(outDir, "golden-aligned-v1v2v3-lineage-out.apk"),
386                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
387                         .setV1SigningEnabled(true)
388                         .setV2SigningEnabled(true)
389                         .setV3SigningEnabled(true)
390                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
391                         .setSigningCertificateLineage(lineage));
392 
393         signGolden(
394                 "original.apk",
395                 new File(outDir, "golden-rsa-out.apk"),
396                 new ApkSigner.Builder(rsa2048SignerConfig));
397         signGolden(
398                 "original.apk",
399                 new File(outDir, "golden-rsa-minSdkVersion-1-out.apk"),
400                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(1));
401         signGolden(
402                 "original.apk",
403                 new File(outDir, "golden-rsa-minSdkVersion-18-out.apk"),
404                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(18));
405         signGolden(
406                 "original.apk",
407                 new File(outDir, "golden-rsa-minSdkVersion-24-out.apk"),
408                 new ApkSigner.Builder(rsa2048SignerConfig).setMinSdkVersion(24));
409         signGolden(
410                 "original.apk",
411                 new File(outDir, "golden-rsa-verity-out.apk"),
412                 new ApkSigner.Builder(rsa2048SignerConfig)
413                         .setV1SigningEnabled(true)
414                         .setV2SigningEnabled(true)
415                         .setV3SigningEnabled(true)
416                         .setVerityEnabled(true));
417         signGolden(
418                 "original.apk",
419                 new File(outDir, "golden-file-size-aligned.apk"),
420                 new ApkSigner.Builder(rsa2048SignerConfig)
421                         .setAlignFileSize(true));
422         signGolden(
423                 "pinsapp-unsigned.apk",
424                 new File(outDir, "golden-pinsapp-signed.apk"),
425                 new ApkSigner.Builder(rsa2048SignerConfig)
426                         .setV1SigningEnabled(true)
427                         .setV2SigningEnabled(true)
428                         .setV3SigningEnabled(true)
429                         .setVerityEnabled(true));
430     }
431 
signGolden( String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)432     private static void signGolden(
433             String inResourceName, File outFile, ApkSigner.Builder apkSignerBuilder)
434             throws Exception {
435         DataSource in =
436                 DataSources.asDataSource(
437                         ByteBuffer.wrap(Resources.toByteArray(ApkSigner.class, inResourceName)));
438         apkSignerBuilder.setInputApk(in).setOutputApk(outFile);
439 
440         File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig");
441         apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig);
442         apkSignerBuilder.setV4ErrorReportingEnabled(true);
443 
444         apkSignerBuilder.build().sign();
445     }
446 
447     @Test
testAlignmentPreserved_Golden()448     public void testAlignmentPreserved_Golden() throws Exception {
449         // Regression tests for preserving (mis)alignment of ZIP Local File Header data
450         // NOTE: Expected output files can be re-generated by running the "main" method.
451 
452         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
453                 Collections.singletonList(
454                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
455         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
456                 Arrays.asList(
457                         rsa2048SignerConfig.get(0),
458                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
459         SigningCertificateLineage lineage =
460                 Resources.toSigningCertificateLineage(
461                         getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
462         // Uncompressed entries in this input file are not aligned -- the file was created using
463         // the jar utility. temp4.txt entry was then manually added into the archive. This entry's
464         // ZIP Local File Header "extra" field declares that the entry's data must be aligned to
465         // 4 kB boundary, but the data isn't actually aligned in the file.
466         assertGolden(
467                 "golden-unaligned-in.apk",
468                 "golden-unaligned-out.apk",
469                 new ApkSigner.Builder(rsa2048SignerConfig)
470                         .setAlignmentPreserved(true));
471         assertGolden(
472                 "golden-unaligned-in.apk",
473                 "golden-unaligned-v1-out.apk",
474                 new ApkSigner.Builder(rsa2048SignerConfig)
475                         .setV1SigningEnabled(true)
476                         .setV2SigningEnabled(false)
477                         .setV3SigningEnabled(false)
478                         .setV4SigningEnabled(false)
479                         .setAlignmentPreserved(true));
480         assertGolden(
481                 "golden-unaligned-in.apk",
482                 "golden-unaligned-v2-out.apk",
483                 new ApkSigner.Builder(rsa2048SignerConfig)
484                         .setV1SigningEnabled(false)
485                         .setV2SigningEnabled(true)
486                         .setV3SigningEnabled(false)
487                         .setAlignmentPreserved(true));
488         assertGolden(
489                 "golden-unaligned-in.apk",
490                 "golden-unaligned-v3-out.apk",
491                 new ApkSigner.Builder(rsa2048SignerConfig)
492                         .setV1SigningEnabled(false)
493                         .setV2SigningEnabled(false)
494                         .setV3SigningEnabled(true)
495                         .setAlignmentPreserved(true));
496         assertGolden(
497                 "golden-unaligned-in.apk",
498                 "golden-unaligned-v3-lineage-out.apk",
499                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
500                         .setV1SigningEnabled(false)
501                         .setV2SigningEnabled(false)
502                         .setV3SigningEnabled(true)
503                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
504                         .setSigningCertificateLineage(lineage)
505                         .setAlignmentPreserved(true));
506         assertGolden(
507                 "golden-unaligned-in.apk",
508                 "golden-unaligned-v1v2-out.apk",
509                 new ApkSigner.Builder(rsa2048SignerConfig)
510                         .setV1SigningEnabled(true)
511                         .setV2SigningEnabled(true)
512                         .setV3SigningEnabled(false)
513                         .setAlignmentPreserved(true));
514         assertGolden(
515                 "golden-unaligned-in.apk",
516                 "golden-unaligned-v2v3-out.apk",
517                 new ApkSigner.Builder(rsa2048SignerConfig)
518                         .setV1SigningEnabled(false)
519                         .setV2SigningEnabled(true)
520                         .setV3SigningEnabled(true)
521                         .setAlignmentPreserved(true));
522         assertGolden(
523                 "golden-unaligned-in.apk",
524                 "golden-unaligned-v2v3-lineage-out.apk",
525                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
526                         .setV1SigningEnabled(false)
527                         .setV2SigningEnabled(true)
528                         .setV3SigningEnabled(true)
529                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
530                         .setSigningCertificateLineage(lineage)
531                         .setAlignmentPreserved(true));
532         assertGolden(
533                 "golden-unaligned-in.apk",
534                 "golden-unaligned-v1v2v3-out.apk",
535                 new ApkSigner.Builder(rsa2048SignerConfig)
536                         .setV1SigningEnabled(true)
537                         .setV2SigningEnabled(true)
538                         .setV3SigningEnabled(true)
539                         .setAlignmentPreserved(true));
540         assertGolden(
541                 "golden-unaligned-in.apk",
542                 "golden-unaligned-v1v2v3-lineage-out.apk",
543                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
544                         .setV1SigningEnabled(true)
545                         .setV2SigningEnabled(true)
546                         .setV3SigningEnabled(true)
547                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
548                         .setSigningCertificateLineage(lineage)
549                         .setAlignmentPreserved(true));
550 
551         // Uncompressed entries in this input file are aligned by zero-padding the "extra" field, as
552         // performed by zipalign at the time of writing. This padding technique produces ZIP
553         // archives whose "extra" field are not compliant with APPNOTE.TXT. Hence, this technique
554         // was deprecated.
555         assertGolden(
556                 "golden-legacy-aligned-in.apk",
557                 "golden-legacy-aligned-out.apk",
558                 new ApkSigner.Builder(rsa2048SignerConfig)
559                         .setLibraryPageAlignmentBytes(4096)
560                         .setAlignmentPreserved(true));
561         assertGolden(
562                 "golden-legacy-aligned-in.apk",
563                 "golden-legacy-aligned-v1-out.apk",
564                 new ApkSigner.Builder(rsa2048SignerConfig)
565                         .setV1SigningEnabled(true)
566                         .setV2SigningEnabled(false)
567                         .setV3SigningEnabled(false)
568                         .setV4SigningEnabled(false)
569                         .setLibraryPageAlignmentBytes(4096)
570                         .setAlignmentPreserved(true));
571         assertGolden(
572                 "golden-legacy-aligned-in.apk",
573                 "golden-legacy-aligned-v2-out.apk",
574                 new ApkSigner.Builder(rsa2048SignerConfig)
575                         .setV1SigningEnabled(false)
576                         .setV2SigningEnabled(true)
577                         .setV3SigningEnabled(false)
578                         .setLibraryPageAlignmentBytes(4096)
579                         .setAlignmentPreserved(true));
580         assertGolden(
581                 "golden-legacy-aligned-in.apk",
582                 "golden-legacy-aligned-v3-out.apk",
583                 new ApkSigner.Builder(rsa2048SignerConfig)
584                         .setV1SigningEnabled(false)
585                         .setV2SigningEnabled(false)
586                         .setV3SigningEnabled(true)
587                         .setLibraryPageAlignmentBytes(4096)
588                         .setAlignmentPreserved(true));
589         assertGolden(
590                 "golden-legacy-aligned-in.apk",
591                 "golden-legacy-aligned-v3-lineage-out.apk",
592                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
593                         .setV1SigningEnabled(false)
594                         .setV2SigningEnabled(false)
595                         .setV3SigningEnabled(true)
596                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
597                         .setSigningCertificateLineage(lineage)
598                         .setLibraryPageAlignmentBytes(4096)
599                         .setAlignmentPreserved(true));
600         assertGolden(
601                 "golden-legacy-aligned-in.apk",
602                 "golden-legacy-aligned-v1v2-out.apk",
603                 new ApkSigner.Builder(rsa2048SignerConfig)
604                         .setV1SigningEnabled(true)
605                         .setV2SigningEnabled(true)
606                         .setV3SigningEnabled(false)
607                         .setLibraryPageAlignmentBytes(4096)
608                         .setAlignmentPreserved(true));
609         assertGolden(
610                 "golden-legacy-aligned-in.apk",
611                 "golden-legacy-aligned-v2v3-out.apk",
612                 new ApkSigner.Builder(rsa2048SignerConfig)
613                         .setV1SigningEnabled(false)
614                         .setV2SigningEnabled(true)
615                         .setV3SigningEnabled(true)
616                         .setLibraryPageAlignmentBytes(4096)
617                         .setAlignmentPreserved(true));
618         assertGolden(
619                 "golden-legacy-aligned-in.apk",
620                 "golden-legacy-aligned-v2v3-lineage-out.apk",
621                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
622                         .setV1SigningEnabled(false)
623                         .setV2SigningEnabled(true)
624                         .setV3SigningEnabled(true)
625                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
626                         .setSigningCertificateLineage(lineage)
627                         .setLibraryPageAlignmentBytes(4096)
628                         .setAlignmentPreserved(true));
629         assertGolden(
630                 "golden-legacy-aligned-in.apk",
631                 "golden-legacy-aligned-v1v2v3-out.apk",
632                 new ApkSigner.Builder(rsa2048SignerConfig)
633                         .setV1SigningEnabled(true)
634                         .setV2SigningEnabled(true)
635                         .setV3SigningEnabled(true)
636                         .setLibraryPageAlignmentBytes(4096)
637                         .setAlignmentPreserved(true));
638         assertGolden(
639                 "golden-legacy-aligned-in.apk",
640                 "golden-legacy-aligned-v1v2v3-lineage-out.apk",
641                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
642                         .setV1SigningEnabled(true)
643                         .setV2SigningEnabled(true)
644                         .setV3SigningEnabled(true)
645                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
646                         .setSigningCertificateLineage(lineage)
647                         .setLibraryPageAlignmentBytes(4096)
648                         .setAlignmentPreserved(true));
649 
650         // Uncompressed entries in this input file are aligned by padding the "extra" field, as
651         // generated by signapk and apksigner. This padding technique produces "extra" fields which
652         // are compliant with APPNOTE.TXT.
653         assertGolden(
654                 "golden-aligned-in.apk",
655                 "golden-aligned-out.apk",
656                 new ApkSigner.Builder(rsa2048SignerConfig)
657                         .setAlignmentPreserved(true));
658         assertGolden(
659                 "golden-aligned-in.apk",
660                 "golden-aligned-v1-out.apk",
661                 new ApkSigner.Builder(rsa2048SignerConfig)
662                         .setV1SigningEnabled(true)
663                         .setV2SigningEnabled(false)
664                         .setV3SigningEnabled(false)
665                         .setV4SigningEnabled(false)
666                         .setAlignmentPreserved(true));
667         assertGolden(
668                 "golden-aligned-in.apk",
669                 "golden-aligned-v2-out.apk",
670                 new ApkSigner.Builder(rsa2048SignerConfig)
671                         .setV1SigningEnabled(false)
672                         .setV2SigningEnabled(true)
673                         .setV3SigningEnabled(false)
674                         .setAlignmentPreserved(true));
675         assertGolden(
676                 "golden-aligned-in.apk",
677                 "golden-aligned-v3-out.apk",
678                 new ApkSigner.Builder(rsa2048SignerConfig)
679                         .setV1SigningEnabled(false)
680                         .setV2SigningEnabled(false)
681                         .setV3SigningEnabled(true)
682                         .setAlignmentPreserved(true));
683         assertGolden(
684                 "golden-aligned-in.apk",
685                 "golden-aligned-v3-lineage-out.apk",
686                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
687                         .setV1SigningEnabled(false)
688                         .setV2SigningEnabled(false)
689                         .setV3SigningEnabled(true)
690                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
691                         .setSigningCertificateLineage(lineage)
692                         .setAlignmentPreserved(true));
693         assertGolden(
694                 "golden-aligned-in.apk",
695                 "golden-aligned-v1v2-out.apk",
696                 new ApkSigner.Builder(rsa2048SignerConfig)
697                         .setV1SigningEnabled(true)
698                         .setV2SigningEnabled(true)
699                         .setV3SigningEnabled(false)
700                         .setAlignmentPreserved(true));
701         assertGolden(
702                 "golden-aligned-in.apk",
703                 "golden-aligned-v2v3-out.apk",
704                 new ApkSigner.Builder(rsa2048SignerConfig)
705                         .setV1SigningEnabled(false)
706                         .setV2SigningEnabled(true)
707                         .setV3SigningEnabled(true)
708                         .setAlignmentPreserved(true));
709         assertGolden(
710                 "golden-aligned-in.apk",
711                 "golden-aligned-v2v3-lineage-out.apk",
712                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
713                         .setV1SigningEnabled(false)
714                         .setV2SigningEnabled(true)
715                         .setV3SigningEnabled(true)
716                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
717                         .setSigningCertificateLineage(lineage)
718                         .setAlignmentPreserved(true));
719         assertGolden(
720                 "golden-aligned-in.apk",
721                 "golden-aligned-v1v2v3-out.apk",
722                 new ApkSigner.Builder(rsa2048SignerConfig)
723                         .setV1SigningEnabled(true)
724                         .setV2SigningEnabled(true)
725                         .setV3SigningEnabled(true)
726                         .setAlignmentPreserved(true));
727         assertGolden(
728                 "golden-aligned-in.apk",
729                 "golden-aligned-v1v2v3-lineage-out.apk",
730                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
731                         .setV1SigningEnabled(true)
732                         .setV2SigningEnabled(true)
733                         .setV3SigningEnabled(true)
734                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
735                         .setSigningCertificateLineage(lineage)
736                         .setAlignmentPreserved(true));
737     }
738 
739     // BEGIN-AOSP
740     @Test
testAws_Golden()741     public void testAws_Golden() throws Exception {
742         try (KeyAliasClient client = new KeyAliasClient()) {
743             client.listKeyAliases();
744         } catch (Exception e) {
745             assumeNoException("Test cannot run without access to test data in AWS", e);
746         }
747         final List<ApkSigner.SignerConfig> rsa2048SignerConfig =
748                 Collections.singletonList(
749                         AwsSignerConfigGenerator.getSignerConfigFromResources(
750                                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false));
751 
752         assertGolden(
753                 "golden-aligned-in.apk",
754                 "golden-aligned-out.apk",
755                 new ApkSigner.Builder(rsa2048SignerConfig).setAlignmentPreserved(true));
756         assertGolden(
757                 "golden-aligned-in.apk",
758                 "golden-aligned-v1-out.apk",
759                 new ApkSigner.Builder(rsa2048SignerConfig)
760                         .setV1SigningEnabled(true)
761                         .setV2SigningEnabled(false)
762                         .setV3SigningEnabled(false)
763                         .setV4SigningEnabled(false)
764                         .setAlignmentPreserved(true));
765         assertGolden(
766                 "golden-aligned-in.apk",
767                 "golden-aligned-v2-out.apk",
768                 new ApkSigner.Builder(rsa2048SignerConfig)
769                         .setV1SigningEnabled(false)
770                         .setV2SigningEnabled(true)
771                         .setV3SigningEnabled(false)
772                         .setAlignmentPreserved(true));
773         assertGolden(
774                 "golden-aligned-in.apk",
775                 "golden-aligned-v3-out.apk",
776                 new ApkSigner.Builder(rsa2048SignerConfig)
777                         .setV1SigningEnabled(false)
778                         .setV2SigningEnabled(false)
779                         .setV3SigningEnabled(true)
780                         .setAlignmentPreserved(true));
781         assertGolden(
782                 "golden-aligned-in.apk",
783                 "golden-aligned-v1v2-out.apk",
784                 new ApkSigner.Builder(rsa2048SignerConfig)
785                         .setV1SigningEnabled(true)
786                         .setV2SigningEnabled(true)
787                         .setV3SigningEnabled(false)
788                         .setAlignmentPreserved(true));
789         assertGolden(
790                 "golden-aligned-in.apk",
791                 "golden-aligned-v2v3-out.apk",
792                 new ApkSigner.Builder(rsa2048SignerConfig)
793                         .setV1SigningEnabled(false)
794                         .setV2SigningEnabled(true)
795                         .setV3SigningEnabled(true)
796                         .setAlignmentPreserved(true));
797         assertGolden(
798                 "golden-aligned-in.apk",
799                 "golden-aligned-v1v2v3-out.apk",
800                 new ApkSigner.Builder(rsa2048SignerConfig)
801                         .setV1SigningEnabled(true)
802                         .setV2SigningEnabled(true)
803                         .setV3SigningEnabled(true)
804                         .setAlignmentPreserved(true));
805 
806         // In the first set of lineage tests, the lineage starts with an AWS KMS key config, and
807         // then
808         // rotates to JCA.
809         final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithAwsAtStartOfLineage =
810                 Arrays.asList(
811                         rsa2048SignerConfig.get(0),
812                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
813         final SigningCertificateLineage lineage =
814                 Resources.toSigningCertificateLineage(
815                         getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
816 
817         assertGolden(
818                 "golden-aligned-in.apk",
819                 "golden-aligned-v3-lineage-out.apk",
820                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage)
821                         .setV1SigningEnabled(false)
822                         .setV2SigningEnabled(false)
823                         .setV3SigningEnabled(true)
824                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
825                         .setSigningCertificateLineage(lineage)
826                         .setAlignmentPreserved(true));
827         assertGolden(
828                 "golden-aligned-in.apk",
829                 "golden-aligned-v2v3-lineage-out.apk",
830                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage)
831                         .setV1SigningEnabled(false)
832                         .setV2SigningEnabled(true)
833                         .setV3SigningEnabled(true)
834                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
835                         .setSigningCertificateLineage(lineage)
836                         .setAlignmentPreserved(true));
837         assertGolden(
838                 "golden-aligned-in.apk",
839                 "golden-aligned-v1v2v3-lineage-out.apk",
840                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtStartOfLineage)
841                         .setV1SigningEnabled(true)
842                         .setV2SigningEnabled(true)
843                         .setV3SigningEnabled(true)
844                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
845                         .setSigningCertificateLineage(lineage)
846                         .setAlignmentPreserved(true));
847 
848         // In the this set of lineage tests, the lineage starts with a local JCA key config, and
849         // then rotates to AWS KMS.
850         final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithAwsAtEndOfLineage =
851                 Arrays.asList(
852                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
853                         AwsSignerConfigGenerator.getSignerConfigFromResources(
854                                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false));
855 
856         assertGolden(
857                 "golden-aligned-in.apk",
858                 "golden-aligned-v3-lineage-out.apk",
859                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage)
860                         .setV1SigningEnabled(false)
861                         .setV2SigningEnabled(false)
862                         .setV3SigningEnabled(true)
863                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
864                         .setSigningCertificateLineage(lineage)
865                         .setAlignmentPreserved(true));
866 
867         assertGolden(
868                 "golden-aligned-in.apk",
869                 "golden-aligned-v2v3-lineage-out.apk",
870                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage)
871                         .setV1SigningEnabled(false)
872                         .setV2SigningEnabled(true)
873                         .setV3SigningEnabled(true)
874                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
875                         .setSigningCertificateLineage(lineage)
876                         .setAlignmentPreserved(true));
877 
878         assertGolden(
879                 "golden-aligned-in.apk",
880                 "golden-aligned-v1v2v3-lineage-out.apk",
881                 new ApkSigner.Builder(rsa2048SignerConfigWithAwsAtEndOfLineage)
882                         .setV1SigningEnabled(true)
883                         .setV2SigningEnabled(true)
884                         .setV3SigningEnabled(true)
885                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
886                         .setSigningCertificateLineage(lineage)
887                         .setAlignmentPreserved(true));
888     }
889     // END-AOSP
890 
891     // BEGIN-AOSP
892     @Test
testGcp_Golden()893     public void testGcp_Golden() throws Exception {
894         try (KeyRingClient keyRingClient = new KeyRingClient(TEST_GCP_KEY_RING)) {
895             keyRingClient.getKeyRing();
896         } catch (Exception e) {
897             assumeNoException("Test cannot run without access to test data in GCP", e);
898         }
899         final List<ApkSigner.SignerConfig> rsa2048SignerConfig =
900                 Collections.singletonList(
901                         GcpSignerConfigGenerator.getSignerConfigFromResources(
902                                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false));
903 
904         assertGolden(
905                 "golden-aligned-in.apk",
906                 "golden-aligned-out.apk",
907                 new ApkSigner.Builder(rsa2048SignerConfig).setAlignmentPreserved(true));
908         assertGolden(
909                 "golden-aligned-in.apk",
910                 "golden-aligned-v1-out.apk",
911                 new ApkSigner.Builder(rsa2048SignerConfig)
912                         .setV1SigningEnabled(true)
913                         .setV2SigningEnabled(false)
914                         .setV3SigningEnabled(false)
915                         .setV4SigningEnabled(false)
916                         .setAlignmentPreserved(true));
917         assertGolden(
918                 "golden-aligned-in.apk",
919                 "golden-aligned-v2-out.apk",
920                 new ApkSigner.Builder(rsa2048SignerConfig)
921                         .setV1SigningEnabled(false)
922                         .setV2SigningEnabled(true)
923                         .setV3SigningEnabled(false)
924                         .setAlignmentPreserved(true));
925         assertGolden(
926                 "golden-aligned-in.apk",
927                 "golden-aligned-v3-out.apk",
928                 new ApkSigner.Builder(rsa2048SignerConfig)
929                         .setV1SigningEnabled(false)
930                         .setV2SigningEnabled(false)
931                         .setV3SigningEnabled(true)
932                         .setAlignmentPreserved(true));
933         assertGolden(
934                 "golden-aligned-in.apk",
935                 "golden-aligned-v1v2-out.apk",
936                 new ApkSigner.Builder(rsa2048SignerConfig)
937                         .setV1SigningEnabled(true)
938                         .setV2SigningEnabled(true)
939                         .setV3SigningEnabled(false)
940                         .setAlignmentPreserved(true));
941         assertGolden(
942                 "golden-aligned-in.apk",
943                 "golden-aligned-v2v3-out.apk",
944                 new ApkSigner.Builder(rsa2048SignerConfig)
945                         .setV1SigningEnabled(false)
946                         .setV2SigningEnabled(true)
947                         .setV3SigningEnabled(true)
948                         .setAlignmentPreserved(true));
949         assertGolden(
950                 "golden-aligned-in.apk",
951                 "golden-aligned-v1v2v3-out.apk",
952                 new ApkSigner.Builder(rsa2048SignerConfig)
953                         .setV1SigningEnabled(true)
954                         .setV2SigningEnabled(true)
955                         .setV3SigningEnabled(true)
956                         .setAlignmentPreserved(true));
957 
958         // In the first set of lineage tests, the lineage starts with a GCP KMS key config, and then
959         // rotates to JCA.
960         final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithGcpAtStartOfLineage =
961                 Arrays.asList(
962                         rsa2048SignerConfig.get(0),
963                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
964         final SigningCertificateLineage lineage =
965                 Resources.toSigningCertificateLineage(
966                         getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
967 
968         assertGolden(
969                 "golden-aligned-in.apk",
970                 "golden-aligned-v3-lineage-out.apk",
971                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage)
972                         .setV1SigningEnabled(false)
973                         .setV2SigningEnabled(false)
974                         .setV3SigningEnabled(true)
975                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
976                         .setSigningCertificateLineage(lineage)
977                         .setAlignmentPreserved(true));
978         assertGolden(
979                 "golden-aligned-in.apk",
980                 "golden-aligned-v2v3-lineage-out.apk",
981                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage)
982                         .setV1SigningEnabled(false)
983                         .setV2SigningEnabled(true)
984                         .setV3SigningEnabled(true)
985                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
986                         .setSigningCertificateLineage(lineage)
987                         .setAlignmentPreserved(true));
988         assertGolden(
989                 "golden-aligned-in.apk",
990                 "golden-aligned-v1v2v3-lineage-out.apk",
991                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtStartOfLineage)
992                         .setV1SigningEnabled(true)
993                         .setV2SigningEnabled(true)
994                         .setV3SigningEnabled(true)
995                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
996                         .setSigningCertificateLineage(lineage)
997                         .setAlignmentPreserved(true));
998 
999         // In the this set of lineage tests, the lineage starts with a local JCA key config, and
1000         // then rotates to GCP KMS.
1001         final List<ApkSigner.SignerConfig> rsa2048SignerConfigWithGcpAtEndOfLineage =
1002                 Arrays.asList(
1003                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
1004                         GcpSignerConfigGenerator.getSignerConfigFromResources(
1005                                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false));
1006 
1007         assertGolden(
1008                 "golden-aligned-in.apk",
1009                 "golden-aligned-v3-lineage-out.apk",
1010                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage)
1011                         .setV1SigningEnabled(false)
1012                         .setV2SigningEnabled(false)
1013                         .setV3SigningEnabled(true)
1014                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
1015                         .setSigningCertificateLineage(lineage)
1016                         .setAlignmentPreserved(true));
1017 
1018         assertGolden(
1019                 "golden-aligned-in.apk",
1020                 "golden-aligned-v2v3-lineage-out.apk",
1021                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage)
1022                         .setV1SigningEnabled(false)
1023                         .setV2SigningEnabled(true)
1024                         .setV3SigningEnabled(true)
1025                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
1026                         .setSigningCertificateLineage(lineage)
1027                         .setAlignmentPreserved(true));
1028 
1029         assertGolden(
1030                 "golden-aligned-in.apk",
1031                 "golden-aligned-v1v2v3-lineage-out.apk",
1032                 new ApkSigner.Builder(rsa2048SignerConfigWithGcpAtEndOfLineage)
1033                         .setV1SigningEnabled(true)
1034                         .setV2SigningEnabled(true)
1035                         .setV3SigningEnabled(true)
1036                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
1037                         .setSigningCertificateLineage(lineage)
1038                         .setAlignmentPreserved(true));
1039     }
1040     // END-AOSP
1041 
1042     @Test
testMinSdkVersion_Golden()1043     public void testMinSdkVersion_Golden() throws Exception {
1044         // Regression tests for minSdkVersion-based signature/digest algorithm selection
1045         // NOTE: Expected output files can be re-generated by running the "main" method.
1046 
1047         List<ApkSigner.SignerConfig> rsaSignerConfig =
1048                 Collections.singletonList(
1049                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1050         assertGolden(
1051                 "original.apk",
1052                 "golden-rsa-out.apk",
1053                 new ApkSigner.Builder(rsaSignerConfig).setAlignmentPreserved(true));
1054         assertGolden(
1055                 "original.apk",
1056                 "golden-rsa-minSdkVersion-1-out.apk",
1057                 new ApkSigner.Builder(rsaSignerConfig)
1058                         .setMinSdkVersion(1)
1059                         .setAlignmentPreserved(true));
1060         assertGolden(
1061                 "original.apk",
1062                 "golden-rsa-minSdkVersion-18-out.apk",
1063                 new ApkSigner.Builder(rsaSignerConfig)
1064                         .setMinSdkVersion(18)
1065                         .setAlignmentPreserved(true));
1066         assertGolden(
1067                 "original.apk",
1068                 "golden-rsa-minSdkVersion-24-out.apk",
1069                 new ApkSigner.Builder(rsaSignerConfig).setMinSdkVersion(24)
1070                         .setAlignmentPreserved(true));
1071 
1072         // TODO: Add tests for DSA and ECDSA. This is non-trivial because the default
1073         // implementations of these signature algorithms are non-deterministic which means output
1074         // files always differ from golden files.
1075     }
1076 
1077     @Test
testVerityEnabled_Golden()1078     public void testVerityEnabled_Golden() throws Exception {
1079         List<ApkSigner.SignerConfig> rsaSignerConfig =
1080                 Collections.singletonList(
1081                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1082 
1083         assertGolden(
1084                 "original.apk",
1085                 "golden-rsa-verity-out.apk",
1086                 new ApkSigner.Builder(rsaSignerConfig)
1087                         .setV1SigningEnabled(true)
1088                         .setV2SigningEnabled(true)
1089                         .setV3SigningEnabled(true)
1090                         .setVerityEnabled(true)
1091                         .setAlignmentPreserved(true));
1092     }
1093 
1094     @Test
testAlignFileSize_Golden()1095     public void testAlignFileSize_Golden() throws Exception {
1096         List<ApkSigner.SignerConfig> rsaSignerConfig =
1097                 Collections.singletonList(
1098                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1099         String goldenOutput = "golden-file-size-aligned.apk";
1100         assertGolden(
1101                 "original.apk",
1102                 goldenOutput,
1103                 new ApkSigner.Builder(rsaSignerConfig)
1104                         .setAlignFileSize(true)
1105                         .setAlignmentPreserved(true));
1106         assertTrue(Resources.toByteArray(getClass(), goldenOutput).length % 4096 == 0);
1107     }
1108 
1109     @Test
testRsaSignedVerifies()1110     public void testRsaSignedVerifies() throws Exception {
1111         List<ApkSigner.SignerConfig> signers =
1112                 Collections.singletonList(
1113                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1114         String in = "original.apk";
1115 
1116         // Sign so that the APK is guaranteed to verify on API Level 1+
1117         File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1));
1118         assertVerified(verifyForMinSdkVersion(out, 1));
1119 
1120         // Sign so that the APK is guaranteed to verify on API Level 18+
1121         out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18));
1122         assertVerified(verifyForMinSdkVersion(out, 18));
1123         // Does not verify on API Level 17 because RSA with SHA-256 not supported
1124         assertVerificationFailure(
1125                 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
1126     }
1127 
1128     @Test
testDsaSignedVerifies()1129     public void testDsaSignedVerifies() throws Exception {
1130         List<ApkSigner.SignerConfig> signers =
1131                 Collections.singletonList(getDefaultSignerConfigFromResources("dsa-1024"));
1132         String in = "original.apk";
1133 
1134         // Sign so that the APK is guaranteed to verify on API Level 1+
1135         File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1));
1136         assertVerified(verifyForMinSdkVersion(out, 1));
1137 
1138         // Sign so that the APK is guaranteed to verify on API Level 21+
1139         out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21));
1140         assertVerified(verifyForMinSdkVersion(out, 21));
1141         // Does not verify on API Level 20 because DSA with SHA-256 not supported
1142         assertVerificationFailure(
1143                 verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
1144     }
1145 
1146     @Test
testDeterministicDsaSignedVerifies()1147     public void testDeterministicDsaSignedVerifies() throws Exception {
1148         Security.addProvider(new BouncyCastleProvider());
1149         try {
1150             // TODO(b/319494004) see if external/bouncycastle can support this algorithm
1151             assumeSHA1withDetDSAIsSupported();
1152             List<ApkSigner.SignerConfig> signers =
1153                     Collections.singletonList(
1154                             getDeterministicDsaSignerConfigFromResources("dsa-2048"));
1155             String in = "original.apk";
1156 
1157             // Sign so that the APK is guaranteed to verify on API Level 1+
1158             File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(1));
1159             assertVerified(verifyForMinSdkVersion(out, 1));
1160 
1161             // Sign so that the APK is guaranteed to verify on API Level 21+
1162             out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(21));
1163             assertVerified(verifyForMinSdkVersion(out, 21));
1164             // Does not verify on API Level 20 because DSA with SHA-256 not supported
1165             assertVerificationFailure(
1166                     verifyForMinSdkVersion(out, 20), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
1167         } finally {
1168             Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
1169         }
1170     }
1171 
1172     @Test
testDeterministicDsaSigningIsDeterministic()1173     public void testDeterministicDsaSigningIsDeterministic() throws Exception {
1174         Security.addProvider(new BouncyCastleProvider());
1175         try {
1176             // TODO(b/319494004) see if external/bouncycastle can support this algorithm
1177             assumeSHA1withDetDSAIsSupported();
1178             List<ApkSigner.SignerConfig> signers =
1179                     Collections.singletonList(
1180                             getDeterministicDsaSignerConfigFromResources("dsa-2048"));
1181             String in = "original.apk";
1182 
1183             ApkSigner.Builder apkSignerBuilder = new ApkSigner.Builder(signers).setMinSdkVersion(1);
1184             File first = sign(in, apkSignerBuilder);
1185             File second = sign(in, apkSignerBuilder);
1186 
1187             assertFileContentsEqual(first, second);
1188         } finally {
1189             Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
1190         }
1191     }
1192 
assumeSHA1withDetDSAIsSupported()1193     private void assumeSHA1withDetDSAIsSupported() {
1194         try {
1195             Signature.getInstance("SHA1withDetDSA");
1196         } catch (NoSuchAlgorithmException e) {
1197             assumeNoException(
1198                     "We should be running with a provider that supports SHA1withDetDSA",
1199                     e);
1200         }
1201     }
1202 
1203     @Test
testEcSignedVerifies()1204     public void testEcSignedVerifies() throws Exception {
1205         List<ApkSigner.SignerConfig> signers =
1206                 Collections.singletonList(
1207                         getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
1208         String in = "original.apk";
1209 
1210         // NOTE: EC APK signatures are not supported prior to API Level 18
1211         // Sign so that the APK is guaranteed to verify on API Level 18+
1212         File out = sign(in, new ApkSigner.Builder(signers).setMinSdkVersion(18));
1213         assertVerified(verifyForMinSdkVersion(out, 18));
1214         // Does not verify on API Level 17 because EC not supported
1215         assertVerificationFailure(
1216                 verifyForMinSdkVersion(out, 17), Issue.JAR_SIG_UNSUPPORTED_SIG_ALG);
1217     }
1218 
1219     @Test
testV1SigningRejectsInvalidZipEntryNames()1220     public void testV1SigningRejectsInvalidZipEntryNames() throws Exception {
1221         // ZIP/JAR entry name cannot contain CR, LF, or NUL characters when the APK is being
1222         // JAR-signed.
1223         List<ApkSigner.SignerConfig> signers =
1224                 Collections.singletonList(
1225                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1226 
1227         assertThrows(
1228                 ApkFormatException.class,
1229                 () ->
1230                         sign(
1231                                 "v1-only-with-cr-in-entry-name.apk",
1232                                 new ApkSigner.Builder(signers).setV1SigningEnabled(true)));
1233         assertThrows(
1234                 ApkFormatException.class,
1235                 () ->
1236                         sign(
1237                                 "v1-only-with-lf-in-entry-name.apk",
1238                                 new ApkSigner.Builder(signers).setV1SigningEnabled(true)));
1239         assertThrows(
1240                 ApkFormatException.class,
1241                 () ->
1242                         sign(
1243                                 "v1-only-with-nul-in-entry-name.apk",
1244                                 new ApkSigner.Builder(signers).setV1SigningEnabled(true)));
1245     }
1246 
1247     @Test
testV1SigningAllowedWithMaximumNumberOfSigners()1248     public void testV1SigningAllowedWithMaximumNumberOfSigners() throws Exception {
1249         // The APK Signature Scheme v1 supports a maximum of 10 signers; this test verifies a
1250         // signing config with the maximum number of signers is allowed to sign the APK.
1251         List<ApkSigner.SignerConfig> signers = List.of(
1252                 getDefaultSignerConfigFromResources("dsa-1024"),
1253                 getDefaultSignerConfigFromResources("dsa-2048"),
1254                 getDefaultSignerConfigFromResources("dsa-3072"),
1255                 getDefaultSignerConfigFromResources("rsa-1024"),
1256                 getDefaultSignerConfigFromResources("rsa-2048"),
1257                 getDefaultSignerConfigFromResources("rsa-3072"),
1258                 getDefaultSignerConfigFromResources("rsa-4096"),
1259                 getDefaultSignerConfigFromResources("rsa-8192"),
1260                 getDefaultSignerConfigFromResources("ec-p256"),
1261                 getDefaultSignerConfigFromResources("ec-p384")
1262         );
1263         sign("original.apk",
1264                 new ApkSigner.Builder(signers)
1265                         .setV1SigningEnabled(true)
1266                         .setV2SigningEnabled(false)
1267                         .setV3SigningEnabled(false)
1268                         .setV4SigningEnabled(false));
1269     }
1270 
1271     @Test
testV1SigningRejectedWithMoreThanMaximumNumberOfSigners()1272     public void testV1SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception {
1273         // This test ensures a v1 signing config with more than the maximum supported number
1274         // of signers will fail to sign.
1275         List<ApkSigner.SignerConfig> signers = List.of(
1276                 getDefaultSignerConfigFromResources("dsa-1024"),
1277                 getDefaultSignerConfigFromResources("dsa-2048"),
1278                 getDefaultSignerConfigFromResources("dsa-3072"),
1279                 getDefaultSignerConfigFromResources("rsa-1024"),
1280                 getDefaultSignerConfigFromResources("rsa-2048"),
1281                 getDefaultSignerConfigFromResources("rsa-3072"),
1282                 getDefaultSignerConfigFromResources("rsa-4096"),
1283                 getDefaultSignerConfigFromResources("rsa-8192"),
1284                 getDefaultSignerConfigFromResources("ec-p256"),
1285                 getDefaultSignerConfigFromResources("ec-p384"),
1286                 getDefaultSignerConfigFromResources("ec-p521")
1287         );
1288         assertThrows(IllegalArgumentException.class, () ->
1289             sign("original.apk",
1290                     new ApkSigner.Builder(signers)
1291                             .setV1SigningEnabled(true)
1292                             .setV2SigningEnabled(false)
1293                             .setV3SigningEnabled(false)
1294                             .setV4SigningEnabled(false)));
1295     }
1296 
1297     @Test
testV2SigningAllowedWithMaximumNumberOfSigners()1298     public void testV2SigningAllowedWithMaximumNumberOfSigners() throws Exception {
1299         // The APK Signature Scheme v2 supports a maximum of 10 signers; this test verifies a
1300         // signing config with the maximum number of signers is allowed to sign the APK.
1301         List<ApkSigner.SignerConfig> signers = List.of(
1302                 getDefaultSignerConfigFromResources("dsa-1024"),
1303                 getDefaultSignerConfigFromResources("dsa-2048"),
1304                 getDefaultSignerConfigFromResources("dsa-3072"),
1305                 getDefaultSignerConfigFromResources("rsa-1024"),
1306                 getDefaultSignerConfigFromResources("rsa-2048"),
1307                 getDefaultSignerConfigFromResources("rsa-3072"),
1308                 getDefaultSignerConfigFromResources("rsa-4096"),
1309                 getDefaultSignerConfigFromResources("rsa-8192"),
1310                 getDefaultSignerConfigFromResources("ec-p256"),
1311                 getDefaultSignerConfigFromResources("ec-p384")
1312         );
1313         sign("original.apk",
1314                 new ApkSigner.Builder(signers)
1315                         .setV1SigningEnabled(false)
1316                         .setV2SigningEnabled(true)
1317                         .setV3SigningEnabled(false)
1318                         .setV4SigningEnabled(false));
1319     }
1320 
1321     @Test
testV2SigningRejectedWithMoreThanMaximumNumberOfSigners()1322     public void testV2SigningRejectedWithMoreThanMaximumNumberOfSigners() throws Exception {
1323         // This test ensures a v2 signing config with more than the maximum supported number
1324         // of signers will fail to sign.
1325         List<ApkSigner.SignerConfig> signers = List.of(
1326                 getDefaultSignerConfigFromResources("dsa-1024"),
1327                 getDefaultSignerConfigFromResources("dsa-2048"),
1328                 getDefaultSignerConfigFromResources("dsa-3072"),
1329                 getDefaultSignerConfigFromResources("rsa-1024"),
1330                 getDefaultSignerConfigFromResources("rsa-2048"),
1331                 getDefaultSignerConfigFromResources("rsa-3072"),
1332                 getDefaultSignerConfigFromResources("rsa-4096"),
1333                 getDefaultSignerConfigFromResources("rsa-8192"),
1334                 getDefaultSignerConfigFromResources("ec-p256"),
1335                 getDefaultSignerConfigFromResources("ec-p384"),
1336                 getDefaultSignerConfigFromResources("ec-p521")
1337         );
1338         assertThrows(IllegalArgumentException.class, () ->
1339                 sign("original.apk",
1340                         new ApkSigner.Builder(signers)
1341                                 .setV1SigningEnabled(false)
1342                                 .setV2SigningEnabled(true)
1343                                 .setV3SigningEnabled(false)
1344                                 .setV4SigningEnabled(false)));
1345     }
1346 
1347     @Test
testWeirdZipCompressionMethod()1348     public void testWeirdZipCompressionMethod() throws Exception {
1349         // Any ZIP compression method other than STORED is treated as DEFLATED by Android.
1350         // This APK declares compression method 21 (neither STORED nor DEFLATED) for CERT.RSA entry,
1351         // but the entry is actually Deflate-compressed.
1352         List<ApkSigner.SignerConfig> signers =
1353                 Collections.singletonList(
1354                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1355         sign("weird-compression-method.apk", new ApkSigner.Builder(signers));
1356     }
1357 
1358     @Test
testZipCompressionMethodMismatchBetweenLfhAndCd()1359     public void testZipCompressionMethodMismatchBetweenLfhAndCd() throws Exception {
1360         // Android Package Manager ignores compressionMethod field in Local File Header and always
1361         // uses the compressionMethod from Central Directory instead.
1362         // In this APK, compression method of CERT.RSA is declared as STORED in Local File Header
1363         // and as DEFLATED in Central Directory. The entry is actually Deflate-compressed.
1364         List<ApkSigner.SignerConfig> signers =
1365                 Collections.singletonList(
1366                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1367         sign("mismatched-compression-method.apk", new ApkSigner.Builder(signers));
1368     }
1369 
1370     @Test
testDebuggableApk()1371     public void testDebuggableApk() throws Exception {
1372         // APK which uses a boolean value "true" in its android:debuggable
1373         final String debuggableBooleanApk = "debuggable-boolean.apk";
1374         List<ApkSigner.SignerConfig> signers =
1375                 Collections.singletonList(
1376                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1377         // Signing debuggable APKs is permitted by default
1378         sign(debuggableBooleanApk, new ApkSigner.Builder(signers));
1379         // Signing debuggable APK succeeds when explicitly requested
1380         sign(debuggableBooleanApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true));
1381 
1382         // Signing debuggable APK fails when requested
1383         assertThrows(
1384                 SignatureException.class,
1385                 () ->
1386                         sign(
1387                                 debuggableBooleanApk,
1388                                 new ApkSigner.Builder(signers).setDebuggableApkPermitted(false)));
1389 
1390         // APK which uses a reference value, pointing to boolean "false", in its android:debuggable
1391         final String debuggableResourceApk = "debuggable-resource.apk";
1392         // When we permit signing regardless of whether the APK is debuggable, the value of
1393         // android:debuggable should be ignored.
1394         sign(debuggableResourceApk, new ApkSigner.Builder(signers).setDebuggableApkPermitted(true));
1395 
1396         // When we disallow signing debuggable APKs, APKs with android:debuggable being a resource
1397         // reference must be rejected, because there's no easy way to establish whether the resolved
1398         // boolean value is the same for all resource configurations.
1399         assertThrows(
1400                 SignatureException.class,
1401                 () ->
1402                         sign(
1403                                 debuggableResourceApk,
1404                                 new ApkSigner.Builder(signers).setDebuggableApkPermitted(false)));
1405     }
1406 
1407     @Test
testV3SigningWithSignersNotInLineageFails()1408     public void testV3SigningWithSignersNotInLineageFails() throws Exception {
1409         // APKs signed with the v3 scheme after a key rotation must specify the lineage containing
1410         // the proof of rotation. This test verifies that the signing will fail if the provided
1411         // signers are not in the specified lineage.
1412         List<ApkSigner.SignerConfig> signers =
1413                 Arrays.asList(
1414                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
1415                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
1416         SigningCertificateLineage lineage =
1417                 Resources.toSigningCertificateLineage(getClass(), "rsa-1024-lineage-2-signers");
1418         assertThrows(
1419                 IllegalStateException.class,
1420                 () ->
1421                         sign(
1422                                 "original.apk",
1423                                 new ApkSigner.Builder(signers)
1424                                         .setSigningCertificateLineage(lineage)));
1425     }
1426 
1427     @Test
testSigningWithLineageRequiresOldestSignerForV1AndV2()1428     public void testSigningWithLineageRequiresOldestSignerForV1AndV2() throws Exception {
1429         // After a key rotation the oldest signer must still be specified for v1 and v2 signing.
1430         // The lineage contains the proof of rotation and will be used to determine the oldest
1431         // signer.
1432         ApkSigner.SignerConfig firstSigner =
1433                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1434         ApkSigner.SignerConfig secondSigner =
1435                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1436         ApkSigner.SignerConfig thirdSigner =
1437                 getDefaultSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
1438         SigningCertificateLineage lineage =
1439                 Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-3-signers");
1440 
1441         // Verifies that the v1 signing scheme requires the oldest signer after a key rotation.
1442         List<ApkSigner.SignerConfig> signers = Collections.singletonList(thirdSigner);
1443         try {
1444             sign(
1445                     "original.apk",
1446                     new ApkSigner.Builder(signers)
1447                             .setV1SigningEnabled(true)
1448                             .setV2SigningEnabled(false)
1449                             .setV3SigningEnabled(true)
1450                             .setSigningCertificateLineage(lineage));
1451             fail(
1452                     "The signing should have failed due to the oldest signer in the lineage not"
1453                             + " being provided for v1 signing");
1454         } catch (IllegalArgumentException expected) {
1455         }
1456 
1457         // Verifies that the v2 signing scheme requires the oldest signer after a key rotation.
1458         try {
1459             sign(
1460                     "original.apk",
1461                     new ApkSigner.Builder(signers)
1462                             .setV1SigningEnabled(false)
1463                             .setV2SigningEnabled(true)
1464                             .setV3SigningEnabled(true)
1465                             .setSigningCertificateLineage(lineage));
1466             fail(
1467                     "The signing should have failed due to the oldest signer in the lineage not"
1468                             + " being provided for v2 signing");
1469         } catch (IllegalArgumentException expected) {
1470         }
1471 
1472         // Verifies that when only the v3 signing scheme is requested the oldest signer does not
1473         // need to be provided.
1474         sign(
1475                 "original.apk",
1476                 new ApkSigner.Builder(signers)
1477                         .setV1SigningEnabled(false)
1478                         .setV2SigningEnabled(false)
1479                         .setV3SigningEnabled(true)
1480                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
1481                         .setSigningCertificateLineage(lineage));
1482 
1483         // Verifies that an intermediate signer in the lineage is not sufficient to satisfy the
1484         // requirement that the oldest signer be provided for v1 and v2 signing.
1485         signers = Arrays.asList(secondSigner, thirdSigner);
1486         try {
1487             sign(
1488                     "original.apk",
1489                     new ApkSigner.Builder(signers)
1490                             .setV1SigningEnabled(true)
1491                             .setV2SigningEnabled(true)
1492                             .setV3SigningEnabled(true)
1493                             .setSigningCertificateLineage(lineage));
1494             fail(
1495                     "The signing should have failed due to the oldest signer in the lineage not"
1496                             + " being provided for v1/v2 signing");
1497         } catch (IllegalArgumentException expected) {
1498         }
1499 
1500         // Verifies that the signing is successful when the oldest and newest signers are provided
1501         // and that intermediate signers are not required.
1502         signers = Arrays.asList(firstSigner, thirdSigner);
1503         sign(
1504                 "original.apk",
1505                 new ApkSigner.Builder(signers)
1506                         .setV1SigningEnabled(true)
1507                         .setV2SigningEnabled(true)
1508                         .setV3SigningEnabled(true)
1509                         .setSigningCertificateLineage(lineage));
1510     }
1511 
1512     @Test
testV3SigningWithMultipleSignersAndNoLineageFails()1513     public void testV3SigningWithMultipleSignersAndNoLineageFails() throws Exception {
1514         // The v3 signing scheme does not support multiple signers; if multiple signers are provided
1515         // it is assumed these signers are part of the lineage. This test verifies v3 signing
1516         // fails if multiple signers are provided without a lineage.
1517         ApkSigner.SignerConfig firstSigner =
1518                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1519         ApkSigner.SignerConfig secondSigner =
1520                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1521         List<ApkSigner.SignerConfig> signers = Arrays.asList(firstSigner, secondSigner);
1522         assertThrows(
1523                 IllegalStateException.class,
1524                 () ->
1525                         sign(
1526                                 "original.apk",
1527                                 new ApkSigner.Builder(signers)
1528                                         .setV1SigningEnabled(true)
1529                                         .setV2SigningEnabled(true)
1530                                         .setV3SigningEnabled(true)));
1531     }
1532 
1533     @Test
testLineageCanBeReadAfterV3Signing()1534     public void testLineageCanBeReadAfterV3Signing() throws Exception {
1535         SigningCertificateLineage.SignerConfig firstSigner =
1536                 Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1537         SigningCertificateLineage.SignerConfig secondSigner =
1538                 Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1539         SigningCertificateLineage lineage =
1540                 new SigningCertificateLineage.Builder(firstSigner, secondSigner).build();
1541         List<ApkSigner.SignerConfig> signerConfigs =
1542                 Arrays.asList(
1543                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
1544                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
1545         File out =
1546                 sign(
1547                         "original.apk",
1548                         new ApkSigner.Builder(signerConfigs)
1549                                 .setV3SigningEnabled(true)
1550                                 .setSigningCertificateLineage(lineage));
1551         SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkFile(out);
1552         assertTrue(
1553                 "The first signer was not in the lineage from the signed APK",
1554                 lineageFromApk.isSignerInLineage((firstSigner)));
1555         assertTrue(
1556                 "The second signer was not in the lineage from the signed APK",
1557                 lineageFromApk.isSignerInLineage((secondSigner)));
1558     }
1559 
1560     @Test
testPublicKeyHasPositiveModulusAfterSigning()1561     public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception {
1562         // The V2 and V3 signature schemes include the public key from the certificate in the
1563         // signing block. If a certificate with an RSAPublicKey is improperly encoded with a
1564         // negative modulus this was previously written to the signing block as is and failed on
1565         // device verification since on device the public key in the certificate was reencoded with
1566         // the correct encoding for the modulus. This test uses an improperly encoded certificate to
1567         // sign an APK and verifies that the public key in the signing block is corrected with a
1568         // positive modulus to allow on device installs / updates.
1569         List<ApkSigner.SignerConfig> signersList =
1570                 Collections.singletonList(
1571                         getDefaultSignerConfigFromResources(
1572                                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
1573                                 FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS));
1574         File signedApk =
1575                 sign(
1576                         "original.apk",
1577                         new ApkSigner.Builder(signersList)
1578                                 .setV1SigningEnabled(true)
1579                                 .setV2SigningEnabled(true)
1580                                 .setV3SigningEnabled(true));
1581         RSAPublicKey v2PublicKey =
1582                 getRSAPublicKeyFromSigningBlock(
1583                         signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
1584         assertTrue(
1585                 "The modulus in the public key in the V2 signing block must not be negative",
1586                 v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
1587         RSAPublicKey v3PublicKey =
1588                 getRSAPublicKeyFromSigningBlock(
1589                         signedApk, ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
1590         assertTrue(
1591                 "The modulus in the public key in the V3 signing block must not be negative",
1592                 v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
1593     }
1594 
1595     @Test
testV4State_disableV2V3EnableV4_fails()1596     public void testV4State_disableV2V3EnableV4_fails() throws Exception {
1597         ApkSigner.SignerConfig signer =
1598                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1599 
1600         assertThrows(
1601                 IllegalStateException.class,
1602                 () ->
1603                         sign(
1604                                 "original.apk",
1605                                 new ApkSigner.Builder(Collections.singletonList(signer))
1606                                         .setV1SigningEnabled(true)
1607                                         .setV2SigningEnabled(false)
1608                                         .setV3SigningEnabled(false)
1609                                         .setV4SigningEnabled(true)));
1610     }
1611 
1612     @Test
testSignApk_stampFile()1613     public void testSignApk_stampFile() throws Exception {
1614         List<ApkSigner.SignerConfig> signers =
1615                 Collections.singletonList(
1616                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1617         ApkSigner.SignerConfig sourceStampSigner =
1618                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1619         MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
1620         messageDigest.update(sourceStampSigner.getCertificates().get(0).getEncoded());
1621         byte[] expectedStampCertificateDigest = messageDigest.digest();
1622 
1623         File signedApkFile =
1624                 sign(
1625                         "original.apk",
1626                         new ApkSigner.Builder(signers)
1627                                 .setV1SigningEnabled(true)
1628                                 .setSourceStampSignerConfig(sourceStampSigner));
1629 
1630         try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) {
1631             DataSource signedApk = DataSources.asDataSource(f, 0, f.length());
1632 
1633             ApkUtils.ZipSections zipSections = findZipSections(signedApk);
1634             List<CentralDirectoryRecord> cdRecords =
1635                     V1SchemeVerifier.parseZipCentralDirectory(signedApk, zipSections);
1636             CentralDirectoryRecord stampCdRecord = null;
1637             for (CentralDirectoryRecord cdRecord : cdRecords) {
1638                 if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
1639                     stampCdRecord = cdRecord;
1640                     break;
1641                 }
1642             }
1643             assertNotNull(stampCdRecord);
1644             byte[] actualStampCertificateDigest =
1645                     LocalFileRecord.getUncompressedData(
1646                             signedApk, stampCdRecord, zipSections.getZipCentralDirectoryOffset());
1647             assertArrayEquals(expectedStampCertificateDigest, actualStampCertificateDigest);
1648         }
1649     }
1650 
1651     @Test
testSignApk_existingStampFile_sameSourceStamp()1652     public void testSignApk_existingStampFile_sameSourceStamp() throws Exception {
1653         List<ApkSigner.SignerConfig> signers =
1654                 Collections.singletonList(
1655                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1656         ApkSigner.SignerConfig sourceStampSigner =
1657                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1658 
1659         File signedApk =
1660                 sign(
1661                         "original-with-stamp-file.apk",
1662                         new ApkSigner.Builder(signers)
1663                                 .setV1SigningEnabled(true)
1664                                 .setV2SigningEnabled(true)
1665                                 .setV3SigningEnabled(true)
1666                                 .setSourceStampSignerConfig(sourceStampSigner));
1667 
1668         ApkVerifier.Result sourceStampVerificationResult =
1669                 verify(signedApk, /* minSdkVersionOverride= */ null);
1670         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1671     }
1672 
1673     @Test
testSignApk_existingStampFile_differentSourceStamp()1674     public void testSignApk_existingStampFile_differentSourceStamp() throws Exception {
1675         List<ApkSigner.SignerConfig> signers =
1676                 Collections.singletonList(
1677                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1678         ApkSigner.SignerConfig sourceStampSigner =
1679                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1680 
1681         Exception exception =
1682                 assertThrows(
1683                         ApkFormatException.class,
1684                         () ->
1685                                 sign(
1686                                         "original-with-stamp-file.apk",
1687                                         new ApkSigner.Builder(signers)
1688                                                 .setV1SigningEnabled(true)
1689                                                 .setV2SigningEnabled(true)
1690                                                 .setV3SigningEnabled(true)
1691                                                 .setSourceStampSignerConfig(sourceStampSigner)));
1692         assertEquals(
1693                 String.format(
1694                         "Cannot generate SourceStamp. APK contains an existing entry with the"
1695                                 + " name: %s, and it is different than the provided source stamp"
1696                                 + " certificate",
1697                         SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME),
1698                 exception.getMessage());
1699     }
1700 
1701     @Test
testSignApk_existingStampFile_differentSourceStamp_forceOverwrite()1702     public void testSignApk_existingStampFile_differentSourceStamp_forceOverwrite()
1703             throws Exception {
1704         List<ApkSigner.SignerConfig> signers =
1705                 Collections.singletonList(
1706                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1707         ApkSigner.SignerConfig sourceStampSigner =
1708                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1709 
1710         File signedApk =
1711                 sign(
1712                         "original-with-stamp-file.apk",
1713                         new ApkSigner.Builder(signers)
1714                                 .setV1SigningEnabled(true)
1715                                 .setV2SigningEnabled(true)
1716                                 .setV3SigningEnabled(true)
1717                                 .setForceSourceStampOverwrite(true)
1718                                 .setSourceStampSignerConfig(sourceStampSigner));
1719 
1720         ApkVerifier.Result sourceStampVerificationResult =
1721                 verify(signedApk, /* minSdkVersionOverride= */ null);
1722         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1723     }
1724 
1725     @Test
testSignApk_stampBlock_noStampGenerated()1726     public void testSignApk_stampBlock_noStampGenerated() throws Exception {
1727         List<ApkSigner.SignerConfig> signersList =
1728                 Collections.singletonList(
1729                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1730 
1731         File signedApkFile =
1732                 sign(
1733                         "original.apk",
1734                         new ApkSigner.Builder(signersList)
1735                                 .setV1SigningEnabled(true)
1736                                 .setV2SigningEnabled(true)
1737                                 .setV3SigningEnabled(true));
1738 
1739         try (RandomAccessFile f = new RandomAccessFile(signedApkFile, "r")) {
1740             DataSource signedApk = DataSources.asDataSource(f, 0, f.length());
1741 
1742             ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(signedApk);
1743             ApkSigningBlockUtils.Result result =
1744                     new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_SOURCE_STAMP);
1745             assertThrows(
1746                     ApkSigningBlockUtils.SignatureNotFoundException.class,
1747                     () ->
1748                             ApkSigningBlockUtils.findSignature(
1749                                     signedApk,
1750                                     zipSections,
1751                                     ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
1752                                     result));
1753         }
1754     }
1755 
1756     @Test
testSignApk_stampBlock_whenV1SignaturePresent()1757     public void testSignApk_stampBlock_whenV1SignaturePresent() throws Exception {
1758         List<ApkSigner.SignerConfig> signersList =
1759                 Collections.singletonList(
1760                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1761         ApkSigner.SignerConfig sourceStampSigner =
1762                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1763 
1764         File signedApk =
1765                 sign(
1766                         "original.apk",
1767                         new ApkSigner.Builder(signersList)
1768                                 .setV1SigningEnabled(true)
1769                                 .setV2SigningEnabled(false)
1770                                 .setV3SigningEnabled(false)
1771                                 .setV4SigningEnabled(false)
1772                                 .setSourceStampSignerConfig(sourceStampSigner));
1773 
1774         ApkVerifier.Result sourceStampVerificationResult =
1775                 verify(signedApk, /* minSdkVersionOverride= */ null);
1776         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1777     }
1778 
1779     @Test
testSignApk_stampBlock_whenV2SignaturePresent()1780     public void testSignApk_stampBlock_whenV2SignaturePresent() throws Exception {
1781         List<ApkSigner.SignerConfig> signersList =
1782                 Collections.singletonList(
1783                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1784         ApkSigner.SignerConfig sourceStampSigner =
1785                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1786 
1787         File signedApk =
1788                 sign(
1789                         "original.apk",
1790                         new ApkSigner.Builder(signersList)
1791                                 .setV1SigningEnabled(false)
1792                                 .setV2SigningEnabled(true)
1793                                 .setV3SigningEnabled(false)
1794                                 .setSourceStampSignerConfig(sourceStampSigner));
1795 
1796         ApkVerifier.Result sourceStampVerificationResult =
1797                 verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N);
1798         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1799     }
1800 
1801     @Test
testSignApk_stampBlock_whenV3SignaturePresent()1802     public void testSignApk_stampBlock_whenV3SignaturePresent() throws Exception {
1803         List<ApkSigner.SignerConfig> signersList =
1804                 Collections.singletonList(
1805                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1806         ApkSigner.SignerConfig sourceStampSigner =
1807                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1808 
1809         File signedApk =
1810                 sign(
1811                         "original.apk",
1812                         new ApkSigner.Builder(signersList)
1813                                 .setV1SigningEnabled(false)
1814                                 .setV2SigningEnabled(false)
1815                                 .setV3SigningEnabled(true)
1816                                 .setSourceStampSignerConfig(sourceStampSigner));
1817 
1818         ApkVerifier.Result sourceStampVerificationResult =
1819                 verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N);
1820         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1821     }
1822 
1823     @Test
testSignApk_stampBlock_withStampLineage()1824     public void testSignApk_stampBlock_withStampLineage() throws Exception {
1825         List<ApkSigner.SignerConfig> signersList =
1826                 Collections.singletonList(
1827                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1828         ApkSigner.SignerConfig sourceStampSigner =
1829                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
1830         SigningCertificateLineage sourceStampLineage =
1831                 Resources.toSigningCertificateLineage(
1832                         getClass(), LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
1833 
1834         File signedApk =
1835                 sign(
1836                         "original.apk",
1837                         new ApkSigner.Builder(signersList)
1838                                 .setV1SigningEnabled(true)
1839                                 .setV2SigningEnabled(true)
1840                                 .setV3SigningEnabled(true)
1841                                 .setSourceStampSignerConfig(sourceStampSigner)
1842                                 .setSourceStampSigningCertificateLineage(sourceStampLineage));
1843 
1844         ApkVerifier.Result sourceStampVerificationResult =
1845                 verify(signedApk, /* minSdkVersion= */ null);
1846         assertSourceStampVerified(signedApk, sourceStampVerificationResult);
1847     }
1848 
1849     @Test
testSignApk_Pinlist()1850     public void testSignApk_Pinlist() throws Exception {
1851         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
1852             Collections.singletonList(
1853                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1854         assertGolden(
1855             "pinsapp-unsigned.apk",
1856             "golden-pinsapp-signed.apk",
1857             new ApkSigner.Builder(rsa2048SignerConfig)
1858                 .setV1SigningEnabled(true)
1859                 .setV2SigningEnabled(true)
1860                 .setV3SigningEnabled(true)
1861                 .setVerityEnabled(true)
1862                 .setAlignmentPreserved(true));
1863         assertTrue("pinlist.meta file must be in the signed APK.",
1864             resourceZipFileContains("golden-pinsapp-signed.apk", "pinlist.meta"));
1865     }
1866 
1867     @Test
testOtherSignersSignaturesPreserved_extraSigBlock_signatureAppended()1868     public void testOtherSignersSignaturesPreserved_extraSigBlock_signatureAppended()
1869             throws Exception {
1870         // The DefaultApkSignerEngine contains support to append a signature to an existing
1871         // signing block; any existing signature blocks within the APK signing block should be
1872         // left intact except for the original verity padding block (since this is regenerated) and
1873         // the source stamp. This test verifies that an extra signature block is still in
1874         // the APK signing block after appending a V2 signature.
1875         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
1876                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
1877 
1878         File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk",
1879                 new ApkSigner.Builder(ecP256SignerConfig)
1880                 .setV1SigningEnabled(false)
1881                 .setV2SigningEnabled(true)
1882                 .setV3SigningEnabled(false)
1883                 .setV4SigningEnabled(false)
1884                 .setOtherSignersSignaturesPreserved(true));
1885 
1886         ApkVerifier.Result result = verify(signedApk, null);
1887         assertVerified(result);
1888         assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
1889                 EC_P256_SIGNER_RESOURCE_NAME);
1890         assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID));
1891     }
1892 
1893     @Test
testOtherSignersSignaturesPreserved_v1Only_signatureAppended()1894     public void testOtherSignersSignaturesPreserved_v1Only_signatureAppended() throws Exception {
1895         // This test verifies appending an additional V1 signature to an existing V1 signer behaves
1896         // similar to jarsigner where the APK is then verified as signed by both signers.
1897         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
1898                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
1899 
1900         File signedApk = sign("v1-only-with-rsa-2048.apk",
1901                 new ApkSigner.Builder(ecP256SignerConfig)
1902                         .setV1SigningEnabled(true)
1903                         .setV2SigningEnabled(false)
1904                         .setV3SigningEnabled(false)
1905                         .setV4SigningEnabled(false)
1906                         .setOtherSignersSignaturesPreserved(true));
1907 
1908         ApkVerifier.Result result = verify(signedApk, null);
1909         assertVerified(result);
1910         assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
1911                 EC_P256_SIGNER_RESOURCE_NAME);
1912     }
1913 
1914     @Test
testOtherSignersSignaturesPreserved_v3OnlyDifferentSigner_throwsException()1915     public void testOtherSignersSignaturesPreserved_v3OnlyDifferentSigner_throwsException()
1916             throws Exception {
1917         // The V3 Signature Scheme only supports a single signer; if an attempt is made to append
1918         // a different signer to a V3 signature then an exception should be thrown.
1919         // The APK used for this test is signed with the ec-p256 signer so use the rsa-2048 to
1920         // attempt to append a different signature.
1921         List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList(
1922                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1923 
1924         assertThrows(IllegalStateException.class, () ->
1925                 sign("v3-only-with-stamp.apk",
1926                     new ApkSigner.Builder(rsa2048SignerConfig)
1927                             .setV1SigningEnabled(false)
1928                             .setV2SigningEnabled(false)
1929                             .setV3SigningEnabled(true)
1930                             .setV4SigningEnabled(false)
1931                             .setOtherSignersSignaturesPreserved(true))
1932         );
1933     }
1934 
1935     @Test
testOtherSignersSignaturesPreserved_v2OnlyAppendV2V3SameSigner_signatureAppended()1936     public void testOtherSignersSignaturesPreserved_v2OnlyAppendV2V3SameSigner_signatureAppended()
1937           throws Exception {
1938         // A V2 and V3 signature can be appended to an existing V2 signature if the same signer is
1939         // used to resign the APK; this could be used in a case where an APK was previously signed
1940         // with just the V2 signature scheme along with additional non-APK signing scheme signature
1941         // blocks and the signer wanted to preserve those existing blocks.
1942         List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList(
1943                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1944 
1945         File signedApk = sign("v2-rsa-2048-with-extra-sig-block.apk",
1946                 new ApkSigner.Builder(rsa2048SignerConfig)
1947                         .setV1SigningEnabled(false)
1948                         .setV2SigningEnabled(true)
1949                         .setV3SigningEnabled(true)
1950                         .setV4SigningEnabled(false)
1951                         .setOtherSignersSignaturesPreserved(true));
1952 
1953         ApkVerifier.Result result = verify(signedApk, null);
1954         assertVerified(result);
1955         assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
1956         assertSigningBlockContains(signedApk, Pair.of(EXTRA_BLOCK_VALUE, EXTRA_BLOCK_ID));
1957     }
1958 
1959     @Test
testOtherSignersSignaturesPreserved_v2OnlyAppendV3SameSigner_throwsException()1960     public void testOtherSignersSignaturesPreserved_v2OnlyAppendV3SameSigner_throwsException()
1961             throws Exception {
1962         // A V3 only signature cannot be appended to an existing V2 signature, even when using the
1963         // same signer, since the V2 signature would then not contain the stripping protection for
1964         // the V3 signature. If the same signer is being used then the signer should be configured
1965         // to resign using the V2 signature scheme as well as the V3 signature scheme.
1966         List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList(
1967                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1968 
1969         assertThrows(IllegalStateException.class, () ->
1970                 sign("v2-rsa-2048-with-extra-sig-block.apk",
1971                     new ApkSigner.Builder(rsa2048SignerConfig)
1972                             .setV1SigningEnabled(false)
1973                             .setV2SigningEnabled(false)
1974                             .setV3SigningEnabled(true)
1975                             .setV4SigningEnabled(false)
1976                             .setOtherSignersSignaturesPreserved(true)));
1977     }
1978 
1979     @Test
testOtherSignersSignaturesPreserved_v1v2IndividuallySign_signaturesAppended()1980     public void testOtherSignersSignaturesPreserved_v1v2IndividuallySign_signaturesAppended()
1981             throws Exception {
1982         // One of the primary requirements for appending signatures is when an APK has already
1983         // released with two signers; with the minimum signature scheme v2 requirement for target
1984         // SDK version 30+ each signer must be able to append their signature to the existing
1985         // signature block. This test verifies an APK with appended signatures verifies as expected
1986         // after a series of appending V1 and V2 signatures.
1987         List<ApkSigner.SignerConfig> rsa2048SignerConfig = Collections.singletonList(
1988                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
1989         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
1990                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
1991 
1992         // When two parties are signing an APK the first must sign with both V1 and V2; this will
1993         // write the stripping-protection attribute to the V1 signature.
1994         File signedApk = sign("original.apk",
1995                 new ApkSigner.Builder(rsa2048SignerConfig)
1996                         .setV1SigningEnabled(true)
1997                         .setV2SigningEnabled(true)
1998                         .setV3SigningEnabled(false)
1999                         .setV4SigningEnabled(false));
2000 
2001         // The second party can then append their signature with both the V1 and V2 signature; this
2002         // will invalidate the V2 signature of the initial signer since the APK itself will be
2003         // modified with this signers V1 / jar signature.
2004         signedApk = sign(signedApk,
2005                 new ApkSigner.Builder(ecP256SignerConfig)
2006                         .setV1SigningEnabled(true)
2007                         .setV2SigningEnabled(true)
2008                         .setV3SigningEnabled(false)
2009                         .setV4SigningEnabled(false)
2010                         .setOtherSignersSignaturesPreserved(true));
2011 
2012         // The first party will then need to resign with just the V2 signature after its previous
2013         // signature was invalidated by the V1 signature of the second signer; however since this
2014         // signature is appended its previous V2 signature should be removed from the signature
2015         // block and replaced with this new signature while preserving the V2 signature of the
2016         // other signer.
2017         signedApk = sign(signedApk,
2018                 new ApkSigner.Builder(rsa2048SignerConfig)
2019                         .setV1SigningEnabled(false)
2020                         .setV2SigningEnabled(true)
2021                         .setV3SigningEnabled(false)
2022                         .setV4SigningEnabled(false)
2023                         .setOtherSignersSignaturesPreserved(true));
2024 
2025         ApkVerifier.Result result = verify(signedApk, null);
2026         assertVerified(result);
2027         assertResultContainsSigners(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2028                 EC_P256_SIGNER_RESOURCE_NAME);
2029     }
2030 
2031     @Test
testSetMinSdkVersionForRotation_lessThanT_noV31Block()2032     public void testSetMinSdkVersionForRotation_lessThanT_noV31Block() throws Exception {
2033         // The V3.1 signing block is intended to allow APK signing key rotation to target T+, but
2034         // a minimum SDK version can be explicitly set for rotation; if it is less than T than
2035         // the rotated key will be included in the V3.0 block.
2036         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2037                 Arrays.asList(
2038                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2039                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2040         SigningCertificateLineage lineage =
2041                 Resources.toSigningCertificateLineage(
2042                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2043 
2044         File signedApkMinRotationP = sign("original.apk",
2045                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2046                         .setV1SigningEnabled(true)
2047                         .setV2SigningEnabled(true)
2048                         .setV3SigningEnabled(true)
2049                         .setV4SigningEnabled(false)
2050                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
2051                         .setSigningCertificateLineage(lineage));
2052         ApkVerifier.Result resultMinRotationP = verify(signedApkMinRotationP, null);
2053         // The V3.1 signature scheme was introduced in T; specifying an older SDK version as the
2054         // minimum for rotation should cause the APK to still be signed with rotation in the V3.0
2055         // signing block.
2056         File signedApkMinRotationS = sign("original.apk",
2057                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2058                         .setV1SigningEnabled(true)
2059                         .setV2SigningEnabled(true)
2060                         .setV3SigningEnabled(true)
2061                         .setV4SigningEnabled(false)
2062                         .setMinSdkVersionForRotation(AndroidSdkVersion.S)
2063                         .setSigningCertificateLineage(lineage));
2064         ApkVerifier.Result resultMinRotationS = verify(signedApkMinRotationS, null);
2065 
2066         assertVerified(resultMinRotationP);
2067         assertFalse(resultMinRotationP.isVerifiedUsingV31Scheme());
2068         assertEquals(1, resultMinRotationP.getV3SchemeSigners().size());
2069         assertResultContainsSigners(resultMinRotationP, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2070                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2071         assertVerified(resultMinRotationS);
2072         assertFalse(resultMinRotationS.isVerifiedUsingV31Scheme());
2073         // While rotation is targeting S, signer blocks targeting specific SDK versions have not
2074         // been tested in previous platform releases; ensure only a single signer block with the
2075         // rotated key is in the V3 block.
2076         assertEquals(1, resultMinRotationS.getV3SchemeSigners().size());
2077         assertResultContainsSigners(resultMinRotationS, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2078                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2079     }
2080 
2081     @Test
testSetMinSdkVersionForRotation_TAndLater_v31Block()2082     public void testSetMinSdkVersionForRotation_TAndLater_v31Block() throws Exception {
2083         // When T or later is specified as the minimum SDK version for rotation, then a new V3.1
2084         // signing block should be created with the new rotated key, and the V3.0 signing block
2085         // should still be signed with the original key.
2086         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2087             Arrays.asList(
2088                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2089                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2090         SigningCertificateLineage lineage =
2091             Resources.toSigningCertificateLineage(
2092                 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2093 
2094         File signedApkMinRotationT = sign("original.apk",
2095             new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2096                 .setV1SigningEnabled(true)
2097                 .setV2SigningEnabled(true)
2098                 .setV3SigningEnabled(true)
2099                 .setV4SigningEnabled(false)
2100                 .setMinSdkVersionForRotation(AndroidSdkVersion.T)
2101                 .setSigningCertificateLineage(lineage));
2102         ApkVerifier.Result resultMinRotationT = verify(signedApkMinRotationT, null);
2103         // The API level for a release after T is not yet defined, so for now treat it as T + 1.
2104         File signedApkMinRotationU = sign("original.apk",
2105             new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2106                 .setV1SigningEnabled(true)
2107                 .setV2SigningEnabled(true)
2108                 .setV3SigningEnabled(true)
2109                 .setV4SigningEnabled(false)
2110                 .setMinSdkVersionForRotation(AndroidSdkVersion.T + 1)
2111                 .setSigningCertificateLineage(lineage));
2112         ApkVerifier.Result resultMinRotationU = verify(signedApkMinRotationU, null);
2113 
2114         assertVerified(resultMinRotationT);
2115         assertTrue(resultMinRotationT.isVerifiedUsingV31Scheme());
2116         assertResultContainsSigners(resultMinRotationT, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2117             SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2118         assertV31SignerTargetsMinApiLevel(resultMinRotationT, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2119             AndroidSdkVersion.T);
2120         assertVerified(resultMinRotationU);
2121         assertTrue(resultMinRotationU.isVerifiedUsingV31Scheme());
2122         assertResultContainsSigners(resultMinRotationU, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2123             SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2124         assertV31SignerTargetsMinApiLevel(resultMinRotationU, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2125             AndroidSdkVersion.T + 1);
2126     }
2127 
2128     @Test
testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails()2129     public void testSetMinSdkVersionForRotation_targetTNoOriginalSigner_fails() throws Exception {
2130         // Similar to the V1 and V2 signatures schemes, if an app is targeting P or later with
2131         // rotation targeting T, the original signer must be provided so that it can be used in the
2132         // V3.0 signing block; if it is not provided the signer should throw an Exception.
2133         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List
2134             .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2135         SigningCertificateLineage lineage =
2136             Resources.toSigningCertificateLineage(
2137                 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2138 
2139         assertThrows(IllegalArgumentException.class, () ->
2140             sign("original.apk",
2141                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2142                     .setV1SigningEnabled(false)
2143                     .setV2SigningEnabled(false)
2144                     .setV3SigningEnabled(true)
2145                     .setV4SigningEnabled(false)
2146                     .setMinSdkVersion(28)
2147                     .setMinSdkVersionForRotation(AndroidSdkVersion.T)
2148                     .setSigningCertificateLineage(lineage)));
2149     }
2150 
2151     @Test
testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block()2152     public void testSetMinSdkVersionForRotation_targetTAndApkMinSdkT_onlySignsV3Block()
2153             throws Exception {
2154         // A V3.1 signing block should only exist alongside a V3.0 signing block; if an APK's
2155         // min SDK version is greater than or equal to the SDK version for rotation then the
2156         // original signer should not be required, and the rotated signing key should be in
2157         // a V3.0 signing block.
2158         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage = List
2159             .of(getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2160         SigningCertificateLineage lineage =
2161             Resources.toSigningCertificateLineage(
2162                 ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2163 
2164         File signedApk = sign("original.apk",
2165             new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2166                 .setV1SigningEnabled(false)
2167                 .setV2SigningEnabled(false)
2168                 .setV3SigningEnabled(true)
2169                 .setV4SigningEnabled(false)
2170                 .setMinSdkVersion(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)
2171                 .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)
2172                 .setSigningCertificateLineage(lineage));
2173         ApkVerifier.Result result = verifyForMinSdkVersion(signedApk,
2174             V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT);
2175 
2176         assertVerified(result);
2177         assertFalse(result.isVerifiedUsingV31Scheme());
2178         assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2179             SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2180     }
2181 
2182     @Test
testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings()2183     public void testSetMinSdkVersionForRotation_targetTWithSourceStamp_noWarnings()
2184             throws Exception {
2185         // Source stamp verification will report a warning if a stamp signature is not found for any
2186         // of the APK Signature Schemes used to sign the APK. This test verifies an APK signed with
2187         // a rotated key in the v3.1 block and a source stamp successfully verifies, including the
2188         // source stamp, without any warnings.
2189         ApkSigner.SignerConfig rsa2048OriginalSignerConfig = getDefaultSignerConfigFromResources(
2190                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2191         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2192                 Arrays.asList(
2193                         rsa2048OriginalSignerConfig,
2194                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2195         SigningCertificateLineage lineage =
2196                 Resources.toSigningCertificateLineage(
2197                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2198 
2199         File signedApk = sign("original.apk",
2200                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2201                         .setV1SigningEnabled(true)
2202                         .setV2SigningEnabled(true)
2203                         .setV3SigningEnabled(true)
2204                         .setV4SigningEnabled(false)
2205                         .setMinSdkVersionForRotation(AndroidSdkVersion.T)
2206                         .setSigningCertificateLineage(lineage)
2207                         .setSourceStampSignerConfig(rsa2048OriginalSignerConfig));
2208         ApkVerifier.Result result = verify(signedApk, null);
2209 
2210         assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2211                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2212         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2213                 AndroidSdkVersion.T);
2214         assertSourceStampVerified(signedApk, result);
2215     }
2216 
2217     @Test
testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34()2218     public void testSetRotationTargetsDevRelease_target34_v30SignerTargetsAtLeast34()
2219             throws Exception {
2220         // During development of a new platform release the new platform will use the SDK version
2221         // of the previously released platform, so in order to test rotation on a new platform
2222         // release it must target the SDK version of the previous platform. However an APK signed
2223         // with the v3.1 signature scheme and targeting rotation on the previous platform release X
2224         // would still use rotation if that APK were installed on a device running release version
2225         // X. To support targeting rotation on the main branch, the v3.1 signature scheme supports
2226         // a rotation-targets-dev-release attribute; this allows the APK to use the v3.1 signer
2227         // block on a development platform with SDK version X while a release platform X will
2228         // skip this signer block when it sees this additional attribute. To ensure that the APK
2229         // will still target the released platform X, the v3.0 signer must have a maxSdkVersion
2230         // of at least X for the signer.
2231         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2232                 Arrays.asList(
2233                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2234                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2235         SigningCertificateLineage lineage =
2236                 Resources.toSigningCertificateLineage(
2237                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2238         int rotationMinSdkVersion = 10000;
2239 
2240         File signedApk = sign("original.apk",
2241                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2242                         .setV1SigningEnabled(true)
2243                         .setV2SigningEnabled(true)
2244                         .setV3SigningEnabled(true)
2245                         .setV4SigningEnabled(false)
2246                         .setMinSdkVersionForRotation(rotationMinSdkVersion)
2247                         .setSigningCertificateLineage(lineage)
2248                         .setRotationTargetsDevRelease(true));
2249         ApkVerifier.Result result = verify(signedApk, null);
2250 
2251         assertVerified(result);
2252         assertTrue(result.isVerifiedUsingV31Scheme());
2253         assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease());
2254         assertResultContainsSigners(result, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2255                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2256         assertTrue(result.getV3SchemeSigners().get(0).getMaxSdkVersion() >= rotationMinSdkVersion);
2257     }
2258 
2259     @Test
testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired()2260     public void testV3_rotationMinSdkVersionLessThanTV3Only_origSignerNotRequired()
2261             throws Exception {
2262         // The v3.1 signature scheme allows a rotation-min-sdk-version be specified to target T+
2263         // for rotation; however if this value is less than the expected SDK version of T, then
2264         // apksig should just use the rotated signing key in the v3.0 block. An APK that targets
2265         // P+ that wants to use rotation in the v3.0 signing block should only need to provide
2266         // the rotated signing key and lineage; this test ensures this behavior when the
2267         // rotation-min-sdk-version is set to a value > P and < T.
2268         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2269                 Arrays.asList(
2270                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2271         SigningCertificateLineage lineage =
2272                 Resources.toSigningCertificateLineage(
2273                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2274 
2275         File signedApkRotationOnQ = sign("original.apk",
2276                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2277                         .setV1SigningEnabled(false)
2278                         .setV2SigningEnabled(false)
2279                         .setV3SigningEnabled(true)
2280                         .setV4SigningEnabled(false)
2281                         .setMinSdkVersion(AndroidSdkVersion.P)
2282                         .setMinSdkVersionForRotation(AndroidSdkVersion.Q)
2283                         .setSigningCertificateLineage(lineage));
2284         ApkVerifier.Result resultRotationOnQ = verify(signedApkRotationOnQ, AndroidSdkVersion.P);
2285 
2286         assertVerified(resultRotationOnQ);
2287         assertEquals(1, resultRotationOnQ.getV3SchemeSigners().size());
2288         assertFalse(resultRotationOnQ.isVerifiedUsingV31Scheme());
2289         assertResultContainsSigners(resultRotationOnQ, true, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2290                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2291     }
2292 
2293     @Test
testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent()2294     public void testV31_rotationMinSdkVersionEqualsMinSdkVersion_v3SignerPresent()
2295             throws Exception {
2296         // The SDK version for Sv2 (32) is used as the minSdkVersion for the V3.1 signature
2297         // scheme to allow rotation to target the T development platform; this will be updated
2298         // to the real SDK version of T once its SDK is finalized. This test verifies if a
2299         // package has Sv2 as its minSdkVersion, the signing can complete as expected with the
2300         // v3 block signed by the original signer and targeting just Sv2, and the v3.1 block
2301         // signed by the rotated signer and targeting the dev release of Sv2 and all later releases.
2302         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2303                 Arrays.asList(
2304                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2305                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2306         SigningCertificateLineage lineage =
2307                 Resources.toSigningCertificateLineage(
2308                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2309 
2310         File signedApk = sign("original-minSdk32.apk",
2311                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2312                         .setV1SigningEnabled(true)
2313                         .setV2SigningEnabled(true)
2314                         .setV3SigningEnabled(true)
2315                         .setV4SigningEnabled(false)
2316                         .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT)
2317                         .setSigningCertificateLineage(lineage));
2318         ApkVerifier.Result result = verify(signedApk, null);
2319 
2320         assertVerified(result);
2321         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2322     }
2323 
2324     @Test
testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds()2325     public void testV31_rotationMinSdkVersionTWithoutLineage_v30VerificationSucceeds()
2326             throws Exception {
2327         // apksig allows setting a rotation-min-sdk-version without providing a rotated signing
2328         // key / lineage; however in the absence of rotation, the rotation-min-sdk-version should
2329         // be a no-op, and the stripping protection attribute should not be written to the v3.0
2330         // signer.
2331         List<ApkSigner.SignerConfig> rsa2048SignerConfig =
2332                 Collections.singletonList(
2333                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
2334 
2335         File signedApk = sign("original.apk",
2336                 new ApkSigner.Builder(rsa2048SignerConfig)
2337                         .setV1SigningEnabled(true)
2338                         .setV2SigningEnabled(true)
2339                         .setV3SigningEnabled(true)
2340                         .setV4SigningEnabled(false)
2341                         .setMinSdkVersionForRotation(V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT));
2342         ApkVerifier.Result result = verify(signedApk, null);
2343 
2344         assertVerified(result);
2345         assertFalse(result.isVerifiedUsingV31Scheme());
2346         assertTrue(result.isVerifiedUsingV3Scheme());
2347     }
2348 
2349     @Test
testV31_rotationMinSdkVersionDefault_rotationTargetsT()2350     public void testV31_rotationMinSdkVersionDefault_rotationTargetsT() throws Exception {
2351         // The v3.1 signature scheme was introduced in T to allow developers to target T+ for
2352         // rotation due to known issues with rotation on previous platform releases. This test
2353         // verifies an APK signed with a rotated signing key defaults to the original signing
2354         // key used in the v3 signing block for pre-T devices, and the rotated signing key used
2355         // in the v3.1 signing block for T+ devices.
2356         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2357                 Arrays.asList(
2358                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2359                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2360         SigningCertificateLineage lineage =
2361                 Resources.toSigningCertificateLineage(
2362                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2363 
2364         File signedApk = sign("original.apk",
2365                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2366                         .setV1SigningEnabled(true)
2367                         .setV2SigningEnabled(true)
2368                         .setV3SigningEnabled(true)
2369                         .setV4SigningEnabled(false)
2370                         .setSigningCertificateLineage(lineage));
2371         ApkVerifier.Result result = verify(signedApk, null);
2372 
2373         assertVerified(result);
2374         assertTrue(result.isVerifiedUsingV3Scheme());
2375         assertTrue(result.isVerifiedUsingV31Scheme());
2376         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2377         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2378                 AndroidSdkVersion.T);
2379     }
2380 
2381     @Test
testV31_rotationMinSdkVersionP_rotationTargetsP()2382     public void testV31_rotationMinSdkVersionP_rotationTargetsP() throws Exception {
2383         // While the V3.1 signature scheme will target T by default, a package that has
2384         // previously rotated can provide a rotation-min-sdk-version less than T to continue
2385         // using the rotated signing key in the v3.0 block.
2386         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2387                 Arrays.asList(
2388                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2389                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2390         SigningCertificateLineage lineage =
2391                 Resources.toSigningCertificateLineage(
2392                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2393 
2394         File signedApk = sign("original.apk",
2395                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2396                         .setV1SigningEnabled(true)
2397                         .setV2SigningEnabled(true)
2398                         .setV3SigningEnabled(true)
2399                         .setV4SigningEnabled(false)
2400                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
2401                         .setSigningCertificateLineage(lineage));
2402         ApkVerifier.Result result = verify(signedApk, null);
2403 
2404         assertVerified(result);
2405         assertTrue(result.isVerifiedUsingV3Scheme());
2406         assertFalse(result.isVerifiedUsingV31Scheme());
2407     }
2408 
2409     @Test
testV31_rotationMinSdkVersionDevRelease_rotationTargetsDevRelease()2410     public void testV31_rotationMinSdkVersionDevRelease_rotationTargetsDevRelease()
2411             throws Exception {
2412         // The V3.1 signature scheme can be used to target rotation for a development release;
2413         // a development release uses the SDK version of the previously finalized release until
2414         // its own SDK is finalized. This test verifies if the rotation-min-sdk-version is set to
2415         // the current development release, then the resulting APK should target the previously
2416         // finalized release and the rotation-targets-dev-release attribute should be set for
2417         // the signer.
2418         // If the development release is less than the first release that supports V3.1, then
2419         // a development release is not currently supported.
2420         assumeTrue(V3SchemeConstants.DEV_RELEASE >= V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT);
2421         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
2422                 Arrays.asList(
2423                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
2424                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
2425         SigningCertificateLineage lineage =
2426                 Resources.toSigningCertificateLineage(
2427                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2428 
2429         File signedApk = sign("original.apk",
2430                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
2431                         .setV1SigningEnabled(true)
2432                         .setV2SigningEnabled(true)
2433                         .setV3SigningEnabled(true)
2434                         .setV4SigningEnabled(false)
2435                         .setMinSdkVersionForRotation(V3SchemeConstants.DEV_RELEASE)
2436                         .setSigningCertificateLineage(lineage));
2437         ApkVerifier.Result result = verify(signedApk, null);
2438 
2439         assertVerified(result);
2440         assertVerificationWarning(result, null);
2441         assertTrue(result.isVerifiedUsingV3Scheme());
2442         assertTrue(result.isVerifiedUsingV31Scheme());
2443         assertEquals(V3SchemeConstants.PROD_RELEASE,
2444                 result.getV31SchemeSigners().get(0).getMinSdkVersion());
2445         assertTrue(result.getV31SchemeSigners().get(0).getRotationTargetsDevRelease());
2446         // The maxSdkVersion for the V3 signer should overlap with the minSdkVersion for the V3.1
2447         // signer.
2448         assertEquals(V3SchemeConstants.PROD_RELEASE,
2449                 result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2450     }
2451 
2452 
2453     @Test
testV31_oneTargetedSigningConfigT_targetsT()2454     public void testV31_oneTargetedSigningConfigT_targetsT() throws Exception {
2455         // The V3.1 signature scheme supports targeting a signing config for devices running
2456         // T+. This test verifies a single signing config targeting T+ is written to the v3.1
2457         // block, and the original signer is used for pre-T devices in the v3.0 block. This
2458         // is functionally equivalent to calling setMinSdkVersionForRotation(AndroidSdkVersion.T).
2459         SigningCertificateLineage lineage =
2460                 Resources.toSigningCertificateLineage(
2461                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2462         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2463                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2464         ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources(
2465                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage);
2466         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner);
2467 
2468         File signedApk = sign("original.apk",
2469                 new ApkSigner.Builder(signerConfigs)
2470                         .setV1SigningEnabled(true)
2471                         .setV2SigningEnabled(true)
2472                         .setV3SigningEnabled(true)
2473                         .setV4SigningEnabled(false));
2474         ApkVerifier.Result result = verify(signedApk, null);
2475 
2476         assertVerified(result);
2477         assertVerificationWarning(result, null);
2478         assertTrue(result.isVerifiedUsingV3Scheme());
2479         assertTrue(result.isVerifiedUsingV31Scheme());
2480         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2481         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2482                 AndroidSdkVersion.T);
2483         assertEquals(1, result.getV31SchemeSigners().size());
2484         assertLineageContainsExpectedSigners(
2485                 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(),
2486                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2487     }
2488 
2489     @Test
testV31_oneTargetedSigningConfig10000_targets10000()2490     public void testV31_oneTargetedSigningConfig10000_targets10000() throws Exception {
2491         // When a signing config targets a later release, the V3.0 signature should be used for all
2492         // platform releases prior to the targeted release. This test verifies a signing config
2493         // targeting SDK 10000 has a V3.0 block that targets through SDK 9999.
2494         SigningCertificateLineage lineage =
2495                 Resources.toSigningCertificateLineage(
2496                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2497         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2498                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2499         ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources(
2500                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000, lineage);
2501         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner);
2502 
2503         File signedApk = sign("original.apk",
2504                 new ApkSigner.Builder(signerConfigs)
2505                         .setV1SigningEnabled(true)
2506                         .setV2SigningEnabled(true)
2507                         .setV3SigningEnabled(true)
2508                         .setV4SigningEnabled(false));
2509         ApkVerifier.Result result = verify(signedApk, null);
2510 
2511         assertVerified(result);
2512         assertVerificationWarning(result, null);
2513         assertTrue(result.isVerifiedUsingV3Scheme());
2514         assertTrue(result.isVerifiedUsingV31Scheme());
2515         assertEquals(9999, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2516         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME, 10000);
2517         assertEquals(1, result.getV31SchemeSigners().size());
2518         assertLineageContainsExpectedSigners(
2519                 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(),
2520                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2521     }
2522 
2523 
2524     @Test
test31_twoTargetedSigningConfigs_twoV31Signers()2525     public void test31_twoTargetedSigningConfigs_twoV31Signers() throws Exception {
2526         // This test verifies multiple signing configs targeting T+ can be added to the V3.1
2527         // signing block.
2528         SigningCertificateLineage lineageTargetT =
2529                 Resources.toSigningCertificateLineage(
2530                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2531         SigningCertificateLineage lineageTargetU =
2532                 Resources.toSigningCertificateLineage(
2533                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2534         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2535                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2536         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
2537                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
2538         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
2539                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU);
2540         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT,
2541                 signerTargetU);
2542 
2543         File signedApk = sign("original.apk",
2544                 new ApkSigner.Builder(signerConfigs)
2545                         .setV1SigningEnabled(true)
2546                         .setV2SigningEnabled(true)
2547                         .setV3SigningEnabled(true)
2548                         .setV4SigningEnabled(false));
2549         ApkVerifier.Result result = verify(signedApk, null);
2550 
2551         assertVerified(result);
2552         assertVerificationWarning(result, null);
2553         assertTrue(result.isVerifiedUsingV3Scheme());
2554         assertTrue(result.isVerifiedUsingV31Scheme());
2555         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2556         assertEquals(2, result.getV31SchemeSigners().size());
2557         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2558                 AndroidSdkVersion.T);
2559         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2560                 AndroidSdkVersion.U);
2561         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
2562                         AndroidSdkVersion.T).getSigningCertificateLineage(),
2563                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2564         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
2565                         AndroidSdkVersion.U).getSigningCertificateLineage(),
2566                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2567                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
2568     }
2569 
2570     @Test
test31_threeTargetedSigningConfigs_threeV31Signers()2571     public void test31_threeTargetedSigningConfigs_threeV31Signers() throws Exception {
2572         // This test verifies multiple signing configs targeting T+ with modified capabilities
2573         // can be added to the V3.1 signing block.
2574         SigningCertificateLineage lineageTargetT =
2575                 Resources.toSigningCertificateLineage(
2576                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2577         SigningCertificateLineage lineageTargetU =
2578                 Resources.toSigningCertificateLineage(
2579                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2580         SigningCertificateLineage lineageTarget10000 =
2581                 Resources.toSigningCertificateLineage(
2582                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_1_NO_CAPS_RESOURCE_NAME);
2583         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2584                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2585         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
2586                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
2587         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
2588                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U,
2589                 lineageTargetU);
2590         ApkSigner.SignerConfig signerTarget10000 = getDefaultSignerConfigFromResources(
2591                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, 10000,
2592                 lineageTarget10000);
2593         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT,
2594                 signerTargetU, signerTarget10000);
2595 
2596         File signedApk = sign("original.apk",
2597                 new ApkSigner.Builder(signerConfigs)
2598                         .setV1SigningEnabled(true)
2599                         .setV2SigningEnabled(true)
2600                         .setV3SigningEnabled(true)
2601                         .setV4SigningEnabled(false));
2602         ApkVerifier.Result result = verify(signedApk, null);
2603 
2604         assertVerified(result);
2605         assertVerificationWarning(result, null);
2606         assertTrue(result.isVerifiedUsingV3Scheme());
2607         assertTrue(result.isVerifiedUsingV31Scheme());
2608         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2609         assertEquals(3, result.getV31SchemeSigners().size());
2610         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2611                 AndroidSdkVersion.T);
2612         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2613                 AndroidSdkVersion.U);
2614         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, 10000);
2615         assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result,
2616                         AndroidSdkVersion.T).getSigningCertificateLineage(),
2617                 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2618                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME},
2619                 new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
2620         assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result,
2621                         AndroidSdkVersion.U).getSigningCertificateLineage(),
2622                 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2623                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME},
2624                 new SignerCapabilities[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES,
2625                         DEFAULT_CAPABILITIES});
2626         assertLineageContainsExpectedSignersWithCapabilities(getV31SignerTargetingSdkVersion(result,
2627                         10000).getSigningCertificateLineage(),
2628                 new String[]{FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
2629                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME},
2630                 new SignerCapabilities[]{NO_CAPABILITIES, DEFAULT_CAPABILITIES,
2631                         DEFAULT_CAPABILITIES});
2632     }
2633 
2634     @Test
testV31_oneTargetedSigningConfigP_targetsP()2635     public void testV31_oneTargetedSigningConfigP_targetsP() throws Exception {
2636         // A single signing config can be specified targeting < T; this test verifies a single
2637         // config targeting P is written to the V3.0 signing block
2638         SigningCertificateLineage lineage =
2639                 Resources.toSigningCertificateLineage(
2640                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2641         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2642                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2643         ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources(
2644                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineage);
2645         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner);
2646 
2647         File signedApk = sign("original.apk",
2648                 new ApkSigner.Builder(signerConfigs)
2649                         .setV1SigningEnabled(true)
2650                         .setV2SigningEnabled(true)
2651                         .setV3SigningEnabled(true)
2652                         .setV4SigningEnabled(false));
2653         ApkVerifier.Result result = verify(signedApk, null);
2654 
2655         assertVerified(result);
2656         assertVerificationWarning(result, null);
2657         assertTrue(result.isVerifiedUsingV3Scheme());
2658         assertFalse(result.isVerifiedUsingV31Scheme());
2659         assertEquals(1, result.getV3SchemeSigners().size());
2660         assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion());
2661         assertLineageContainsExpectedSigners(
2662                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
2663                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2664     }
2665 
2666     @Test
testV31_oneTargetedSigningConfigS_targetsP()2667     public void testV31_oneTargetedSigningConfigS_targetsP() throws Exception {
2668         // A single signing config can be specified targeting < T, but the V3.0 signature scheme
2669         // does not have verified SDK targeting. If a signing config is specified to target < T and
2670         // > P, the targeted SDK version should be set to P to ensure it applies on all platform
2671         // releases that support the V3.0 signature scheme.
2672         SigningCertificateLineage lineage =
2673                 Resources.toSigningCertificateLineage(
2674                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2675         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2676                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2677         ApkSigner.SignerConfig targetedSigner = getDefaultSignerConfigFromResources(
2678                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineage);
2679         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, targetedSigner);
2680 
2681         File signedApk = sign("original.apk",
2682                 new ApkSigner.Builder(signerConfigs)
2683                         .setV1SigningEnabled(true)
2684                         .setV2SigningEnabled(true)
2685                         .setV3SigningEnabled(true)
2686                         .setV4SigningEnabled(false));
2687         ApkVerifier.Result result = verify(signedApk, null);
2688 
2689         assertVerified(result);
2690         assertVerificationWarning(result, null);
2691         assertTrue(result.isVerifiedUsingV3Scheme());
2692         assertFalse(result.isVerifiedUsingV31Scheme());
2693         assertEquals(1, result.getV3SchemeSigners().size());
2694         assertEquals(AndroidSdkVersion.P, result.getV3SchemeSigners().get(0).getMinSdkVersion());
2695         assertLineageContainsExpectedSigners(
2696                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
2697                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2698     }
2699 
2700     @Test
testV31_twoTargetedSigningConfigsTargetT_throwsException()2701     public void testV31_twoTargetedSigningConfigsTargetT_throwsException() throws Exception {
2702         // The V3.1 signature scheme does not support multiple targeted signers targeting the same
2703         // SDK version; this test ensures an Exception is thrown if the caller specifies multiple
2704         // signers targeting the same release.
2705         SigningCertificateLineage lineageTargetT =
2706                 Resources.toSigningCertificateLineage(
2707                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2708         SigningCertificateLineage secondLineageTargetT =
2709                 Resources.toSigningCertificateLineage(
2710                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2711         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2712                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2713         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
2714                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
2715         ApkSigner.SignerConfig secondSignerTargetT = getDefaultSignerConfigFromResources(
2716                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T,
2717                 secondLineageTargetT);
2718         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT,
2719                 secondSignerTargetT);
2720 
2721         assertThrows(IllegalStateException.class, () -> sign("original.apk",
2722                 new ApkSigner.Builder(signerConfigs)
2723                         .setV1SigningEnabled(true)
2724                         .setV2SigningEnabled(true)
2725                         .setV3SigningEnabled(true)
2726                         .setV4SigningEnabled(false)));
2727     }
2728 
2729     @Test
testV31_oneTargetedSignerUAndDefaultRotationMinSdkVersion_multipleV31Signers()2730     public void testV31_oneTargetedSignerUAndDefaultRotationMinSdkVersion_multipleV31Signers()
2731             throws Exception {
2732         // SDK targeted signing configs can be specified alongside the rotation-min-sdk-version
2733         // for the initial rotation. This test verifies when the initial rotation is specified with
2734         // the default value for rotation-min-sdk-version and a separate signing config targeting U,
2735         // the two signing configs are written as separate V3.1 signatures.
2736         SigningCertificateLineage lineage =
2737                 Resources.toSigningCertificateLineage(
2738                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2739         SigningCertificateLineage lineageTargetU =
2740                 Resources.toSigningCertificateLineage(
2741                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2742         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2743                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2744         ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources(
2745                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2746         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
2747                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U,
2748                 lineageTargetU);
2749         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner,
2750                 signerTargetU);
2751 
2752         File signedApk = sign("original.apk",
2753                 new ApkSigner.Builder(signerConfigs)
2754                         .setV1SigningEnabled(true)
2755                         .setV2SigningEnabled(true)
2756                         .setV3SigningEnabled(true)
2757                         .setV4SigningEnabled(false)
2758                         .setSigningCertificateLineage(lineage));
2759         ApkVerifier.Result result = verify(signedApk, null);
2760 
2761         assertVerified(result);
2762         assertVerificationWarning(result, null);
2763         assertTrue(result.isVerifiedUsingV3Scheme());
2764         assertTrue(result.isVerifiedUsingV31Scheme());
2765         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2766         assertEquals(2, result.getV31SchemeSigners().size());
2767         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2768                 AndroidSdkVersion.T);
2769         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2770                 AndroidSdkVersion.U);
2771         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
2772                         AndroidSdkVersion.T).getSigningCertificateLineage(),
2773                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2774         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
2775                         AndroidSdkVersion.U).getSigningCertificateLineage(),
2776                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2777                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
2778     }
2779 
2780     @Test
testV31_oneTargetedSignerSAndRotationMinSdkVersionP_throwsException()2781     public void testV31_oneTargetedSignerSAndRotationMinSdkVersionP_throwsException()
2782             throws Exception {
2783         // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T
2784         // will target P. If a signing config targets < T and the rotation-min-sdk-version targets
2785         // < T, then an exception should be thrown to prevent both signers from targeting P.
2786         SigningCertificateLineage lineage =
2787                 Resources.toSigningCertificateLineage(
2788                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2789         SigningCertificateLineage lineageTargetS =
2790                 Resources.toSigningCertificateLineage(
2791                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2792         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
2793                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2794         ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources(
2795                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2796         ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources(
2797                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S,
2798                 lineageTargetS);
2799         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner,
2800                 signerTargetS);
2801 
2802         assertThrows(
2803                 IllegalStateException.class,
2804                 () ->
2805                         sign(
2806                                 "original.apk",
2807                                 new ApkSigner.Builder(signerConfigs)
2808                                         .setV1SigningEnabled(true)
2809                                         .setV2SigningEnabled(true)
2810                                         .setV3SigningEnabled(true)
2811                                         .setV4SigningEnabled(false)
2812                                         .setSigningCertificateLineage(lineage)
2813                                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)));
2814     }
2815 
2816     @Test
testV31_twoTargetedSignerPAndS_throwsException()2817     public void testV31_twoTargetedSignerPAndS_throwsException() throws Exception {
2818         // Since the v3.0 does not have verified targeted signing configs, any targeted SDK < T
2819         // will target P. If two signing configs target < T, then an exception should be thrown to
2820         // prevent both signers from targeting P.
2821         SigningCertificateLineage lineageTargetP =
2822                 Resources.toSigningCertificateLineage(
2823                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2824         SigningCertificateLineage lineageTargetS =
2825                 Resources.toSigningCertificateLineage(
2826                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2827         ApkSigner.SignerConfig originalSigner =
2828                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2829         ApkSigner.SignerConfig signerTargetP =
2830                 getDefaultSignerConfigFromResources(
2831                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2832                         false,
2833                         AndroidSdkVersion.P,
2834                         lineageTargetP);
2835         ApkSigner.SignerConfig signerTargetS = getDefaultSignerConfigFromResources(
2836                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.S, lineageTargetS);
2837         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetP,
2838                 signerTargetS);
2839 
2840         assertThrows(IllegalStateException.class, () -> sign("original.apk",
2841                 new ApkSigner.Builder(signerConfigs)
2842                         .setV1SigningEnabled(true)
2843                         .setV2SigningEnabled(true)
2844                         .setV3SigningEnabled(true)
2845                         .setV4SigningEnabled(false)));
2846     }
2847 
2848     @Test
testV31_oneTargetedSignerTAndRotationMinSdkVersionP_rotationInV3andV31()2849     public void testV31_oneTargetedSignerTAndRotationMinSdkVersionP_rotationInV3andV31()
2850             throws Exception {
2851         // An initial rotation could target P with a separate signing config targeting T+; this
2852         // test verifies a rotation-min-sdk-version < T and a signing config targeting T results
2853         // in the initial rotation being written to the V3 signing block and the targeted signing
2854         // config written to the V3.1 block.
2855         SigningCertificateLineage lineage =
2856                 Resources.toSigningCertificateLineage(
2857                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2858         SigningCertificateLineage lineageTargetT =
2859                 Resources.toSigningCertificateLineage(
2860                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2861         ApkSigner.SignerConfig originalSigner =
2862                 getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
2863         ApkSigner.SignerConfig rotatedSigner =
2864                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2865         ApkSigner.SignerConfig signerTargetT =
2866                 getDefaultSignerConfigFromResources(
2867                         THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2868                         false,
2869                         AndroidSdkVersion.T,
2870                         lineageTargetT);
2871         List<ApkSigner.SignerConfig> signerConfigs =
2872                 Arrays.asList(originalSigner, rotatedSigner, signerTargetT);
2873 
2874         File signedApk =
2875                 sign(
2876                         "original.apk",
2877                         new ApkSigner.Builder(signerConfigs)
2878                                 .setV1SigningEnabled(true)
2879                                 .setV2SigningEnabled(true)
2880                                 .setV3SigningEnabled(true)
2881                                 .setV4SigningEnabled(false)
2882                                 .setSigningCertificateLineage(lineage)
2883                                 .setMinSdkVersionForRotation(AndroidSdkVersion.P));
2884         ApkVerifier.Result result = verify(signedApk, null);
2885 
2886         assertVerified(result);
2887         assertVerificationWarning(result, null);
2888         assertTrue(result.isVerifiedUsingV3Scheme());
2889         assertTrue(result.isVerifiedUsingV31Scheme());
2890         assertEquals(AndroidSdkVersion.Sv2, result.getV3SchemeSigners().get(0).getMaxSdkVersion());
2891         assertEquals(1, result.getV31SchemeSigners().size());
2892         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2893                 AndroidSdkVersion.T);
2894         assertLineageContainsExpectedSigners(
2895                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
2896                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2897         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
2898                         AndroidSdkVersion.T).getSigningCertificateLineage(),
2899                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2900                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
2901     }
2902 
2903     @Test
testV31_oneTargetedSignerTApkMinSdkT_oneV3Signer()2904     public void testV31_oneTargetedSignerTApkMinSdkT_oneV3Signer()
2905             throws Exception {
2906         // The V3.1 signature scheme was introduced in SDK version 33; an APK with 33 as its
2907         // minSdkVersion can only be installed on devices with v3.1 support. However the V3.1
2908         // signature scheme should only be used if there's a separate signing config in the V3.0
2909         // block. This test verifies a single signing config targeting an APK's minSdkVersion of
2910         // 33 is written to the V3.0 block.
2911         SigningCertificateLineage lineageTargetT =
2912                 Resources.toSigningCertificateLineage(
2913                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2914         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
2915                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T,
2916                 lineageTargetT);
2917         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT);
2918 
2919         File signedApk = sign("original-minSdk33.apk",
2920                 new ApkSigner.Builder(signerConfigs)
2921                         .setV1SigningEnabled(false)
2922                         .setV2SigningEnabled(false)
2923                         .setV3SigningEnabled(true)
2924                         .setV4SigningEnabled(false));
2925         ApkVerifier.Result result = verify(signedApk, null);
2926 
2927         assertVerified(result);
2928         assertVerificationWarning(result, null);
2929         assertTrue(result.isVerifiedUsingV3Scheme());
2930         assertFalse(result.isVerifiedUsingV31Scheme());
2931         assertLineageContainsExpectedSigners(
2932                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
2933                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
2934     }
2935 
2936     @Test
testV31_oneTargetedSignerTApkMinSdkSv2_throwsException()2937     public void testV31_oneTargetedSignerTApkMinSdkSv2_throwsException()
2938             throws Exception {
2939         // When a signing config targeting T+ is specified for an APK with a minSdkVersion < T,
2940         // the original signer (or another config targeting the minSdkVersion), must be specified
2941         // to ensure the APK can be installed on all supported platform releases. If a signer is
2942         // not provided for the minimum SDK version, then an Exception should be thrown.
2943         SigningCertificateLineage lineageTargetT =
2944                 Resources.toSigningCertificateLineage(
2945                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2946         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
2947                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T,
2948                 lineageTargetT);
2949         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT);
2950 
2951         assertThrows(IllegalArgumentException.class, () -> sign("original-minSdk32.apk",
2952                 new ApkSigner.Builder(signerConfigs)
2953                         .setV1SigningEnabled(false)
2954                         .setV2SigningEnabled(false)
2955                         .setV3SigningEnabled(true)
2956                         .setV4SigningEnabled(false)));
2957     }
2958 
2959     @Test
testV31_twoTargetedSignersSv2AndTApkMinSdkSv2_v3AndV31Signed()2960     public void testV31_twoTargetedSignersSv2AndTApkMinSdkSv2_v3AndV31Signed()
2961             throws Exception {
2962         // V3.0 does not support verified SDK targeting, so a signing config targeting SDK > P and
2963         // < T will be applied to P in the V3.0 signing block. If an app's minSdkVersion > P, then
2964         // the app should still successfully sign and verify with one of the signers targeting the
2965         // APK's minSdkVersion.
2966         SigningCertificateLineage lineageTargetSv2 =
2967                 Resources.toSigningCertificateLineage(
2968                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
2969         SigningCertificateLineage lineageTargetT =
2970                 Resources.toSigningCertificateLineage(
2971                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
2972         ApkSigner.SignerConfig signerTargetSv2 =
2973                 getDefaultSignerConfigFromResources(
2974                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
2975                         false,
2976                         AndroidSdkVersion.Sv2,
2977                         lineageTargetSv2);
2978         ApkSigner.SignerConfig signerTargetT =
2979                 getDefaultSignerConfigFromResources(
2980                         THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
2981                         false,
2982                         AndroidSdkVersion.T,
2983                         lineageTargetT);
2984         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetSv2, signerTargetT);
2985 
2986         File signedApk =
2987                 sign(
2988                         "original-minSdk32.apk",
2989                         new ApkSigner.Builder(signerConfigs)
2990                                 .setV1SigningEnabled(false)
2991                                 .setV2SigningEnabled(false)
2992                                 .setV3SigningEnabled(true)
2993                                 .setV4SigningEnabled(false));
2994         ApkVerifier.Result result = verify(signedApk, null);
2995 
2996         assertVerified(result);
2997         assertVerificationWarning(result, null);
2998         assertTrue(result.isVerifiedUsingV3Scheme());
2999         assertTrue(result.isVerifiedUsingV31Scheme());
3000         assertEquals(1, result.getV31SchemeSigners().size());
3001         assertV31SignerTargetsMinApiLevel(
3002                 result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.T);
3003         assertLineageContainsExpectedSigners(
3004                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
3005                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3006                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3007         assertLineageContainsExpectedSigners(
3008                 getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.T)
3009                         .getSigningCertificateLineage(),
3010                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3011                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3012                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
3013     }
3014 
3015     @Test
testV31_twoTargetedSignersTAndUApkMinSdkT_v3AndV31Signed()3016     public void testV31_twoTargetedSignersTAndUApkMinSdkT_v3AndV31Signed()
3017             throws Exception {
3018         // A V3.0 block is always required before a V3.1 block can be written to the APK's signing
3019         // block. If an APK targets T (the first release with support for V3.1), and has two
3020         // targeted signers, the signer targeting T should be written to the V3.0 block and the
3021         // signer targeting a later release should be written to the V3.1 block.
3022         SigningCertificateLineage lineageTargetT =
3023                 Resources.toSigningCertificateLineage(
3024                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3025         SigningCertificateLineage lineageTargetU =
3026                 Resources.toSigningCertificateLineage(
3027                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
3028         ApkSigner.SignerConfig signerTargetT =
3029                 getDefaultSignerConfigFromResources(
3030                         SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3031                         false,
3032                         AndroidSdkVersion.T,
3033                         lineageTargetT);
3034         ApkSigner.SignerConfig signerTargetU =
3035                 getDefaultSignerConfigFromResources(
3036                         THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
3037                         false,
3038                         AndroidSdkVersion.U,
3039                         lineageTargetU);
3040         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU);
3041 
3042         File signedApk =
3043                 sign(
3044                         "original-minSdk33.apk",
3045                         new ApkSigner.Builder(signerConfigs)
3046                                 .setV1SigningEnabled(false)
3047                                 .setV2SigningEnabled(false)
3048                                 .setV3SigningEnabled(true)
3049                                 .setV4SigningEnabled(false));
3050         ApkVerifier.Result result = verify(signedApk, null);
3051 
3052         assertVerified(result);
3053         assertVerificationWarning(result, null);
3054         assertTrue(result.isVerifiedUsingV3Scheme());
3055         assertTrue(result.isVerifiedUsingV31Scheme());
3056         assertEquals(1, result.getV3SchemeSigners().size());
3057         assertEquals(1, result.getV31SchemeSigners().size());
3058         assertV31SignerTargetsMinApiLevel(
3059                 result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME, AndroidSdkVersion.U);
3060         assertLineageContainsExpectedSigners(
3061                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
3062                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3063                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3064         assertLineageContainsExpectedSigners(
3065                 getV31SignerTargetingSdkVersion(result, AndroidSdkVersion.U)
3066                         .getSigningCertificateLineage(),
3067                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3068                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3069                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
3070     }
3071 
3072     @Test
testV31_twoTargetedSignersTAndUWithTruncatedLineage_v3AndV31Signed()3073     public void testV31_twoTargetedSignersTAndUWithTruncatedLineage_v3AndV31Signed()
3074             throws Exception {
3075         // The V3.1 signature scheme allows different lineages to be specified for each targeted
3076         // signing config as long as all the lineages can be merged to form a common lineage. A
3077         // signing lineage with signers A -> B -> C could be truncated to only signer C in a
3078         // targeted signing config.
3079         SigningCertificateLineage lineage =
3080                 Resources.toSigningCertificateLineage(
3081                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
3082         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3083                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage);
3084         // Manually instantiate this signer instance to make use of the Builder's setMinSdkVersion.
3085         ApkSigner.SignerConfig signerTargetU =
3086                 new ApkSigner.SignerConfig.Builder(
3087                                 signerTargetT.getName(),
3088                                 signerTargetT.getKeyConfig(),
3089                                 signerTargetT.getCertificates())
3090                         .setMinSdkVersion(AndroidSdkVersion.U)
3091                         .build();
3092         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU);
3093 
3094         File signedApk = sign("original-minSdk33.apk",
3095                 new ApkSigner.Builder(signerConfigs)
3096                         .setV1SigningEnabled(false)
3097                         .setV2SigningEnabled(false)
3098                         .setV3SigningEnabled(true)
3099                         .setV4SigningEnabled(false));
3100         ApkVerifier.Result result = verify(signedApk, null);
3101 
3102         assertVerified(result);
3103         assertVerificationWarning(result, null);
3104         assertTrue(result.isVerifiedUsingV3Scheme());
3105         assertTrue(result.isVerifiedUsingV31Scheme());
3106         assertEquals(1, result.getV3SchemeSigners().size());
3107         assertEquals(1, result.getV31SchemeSigners().size());
3108         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
3109                 AndroidSdkVersion.U);
3110         assertLineageContainsExpectedSigners(
3111                 result.getV3SchemeSigners().get(0).getSigningCertificateLineage(),
3112                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3113                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
3114         assertNull(getV31SignerTargetingSdkVersion(result,
3115                 AndroidSdkVersion.U).getSigningCertificateLineage());
3116     }
3117 
3118     @Test
testV31_twoTargetedSignersTAndUWithSignerNotInLineage_throwsException()3119     public void testV31_twoTargetedSignersTAndUWithSignerNotInLineage_throwsException()
3120             throws Exception {
3121         // While the V3.1 signature scheme allows a targeted signing config to omit a lineage,
3122         // this can only be used if a previous targeted signer has specified a lineage that
3123         // includes the new signer without a lineage. If an independent signer is specified
3124         // that is not in the common lineage, an Exception should be thrown.
3125         SigningCertificateLineage lineage =
3126                 Resources.toSigningCertificateLineage(
3127                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3128         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3129                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineage);
3130         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
3131                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, null);
3132         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetT, signerTargetU);
3133 
3134         assertThrows(IllegalStateException.class, () -> sign("original-minSdk33.apk",
3135                 new ApkSigner.Builder(signerConfigs)
3136                         .setV1SigningEnabled(false)
3137                         .setV2SigningEnabled(false)
3138                         .setV3SigningEnabled(true)
3139                         .setV4SigningEnabled(false)));
3140     }
3141 
3142     @Test
testV31_twoTargetedSignersSeparateLineages_throwsException()3143     public void testV31_twoTargetedSignersSeparateLineages_throwsException() throws Exception {
3144         // When multiple SDK targeted signers are specified, the lineage for each signer must
3145         // be part of a common lineage; if any of the targeted signers has a lineage that diverges
3146         // from the common lineage, then an Exception should be thrown.
3147         SigningCertificateLineage lineageTargetT =
3148                 Resources.toSigningCertificateLineage(
3149                         ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME);
3150         SigningCertificateLineage lineageTargetU =
3151                 Resources.toSigningCertificateLineage(
3152                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
3153         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
3154                 EC_P256_SIGNER_RESOURCE_NAME);
3155         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3156                 EC_P256_2_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
3157         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
3158                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU);
3159         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT,
3160                 signerTargetU);
3161 
3162         assertThrows(IllegalStateException.class, () -> sign("original.apk",
3163                 new ApkSigner.Builder(signerConfigs)
3164                         .setV1SigningEnabled(false)
3165                         .setV2SigningEnabled(false)
3166                         .setV3SigningEnabled(true)
3167                         .setV4SigningEnabled(false)));
3168     }
3169 
3170     @Test
testV31_targetedSignerTAndRotationMinSdkVersionPSeparateLineages_throwsException()3171     public void testV31_targetedSignerTAndRotationMinSdkVersionPSeparateLineages_throwsException()
3172             throws Exception {
3173         // When one or more SDK targeted signers are specified with the initial rotation using
3174         // rotation-min-sdk-version, the lineage for each signer must be part of a common lineage;
3175         // if any of the targeted signers has a lineage that diverges from the common lineage,
3176         // then an Exception should be thrown.
3177         SigningCertificateLineage lineage =
3178                 Resources.toSigningCertificateLineage(
3179                         ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME);
3180         SigningCertificateLineage lineageTargetT =
3181                 Resources.toSigningCertificateLineage(
3182                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
3183         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
3184                 EC_P256_SIGNER_RESOURCE_NAME);
3185         ApkSigner.SignerConfig rotatedSigner = getDefaultSignerConfigFromResources(
3186                 EC_P256_2_SIGNER_RESOURCE_NAME);
3187         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3188                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetT);
3189         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, rotatedSigner,
3190                 signerTargetT);
3191 
3192         assertThrows(IllegalStateException.class, () -> sign("original.apk",
3193                 new ApkSigner.Builder(signerConfigs)
3194                         .setV1SigningEnabled(false)
3195                         .setV2SigningEnabled(false)
3196                         .setV3SigningEnabled(true)
3197                         .setV4SigningEnabled(false)
3198                         .setSigningCertificateLineage(lineage)
3199                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)));
3200     }
3201 
3202     @Test
testV31_targetedSignerWithSignerNotInLineage_throwsException()3203     public void testV31_targetedSignerWithSignerNotInLineage_throwsException()
3204             throws Exception {
3205         // When a targeted signer is created with a lineage, the signer must be in the provided
3206         // lineage otherwise an Exception should be thrown.
3207         SigningCertificateLineage lineageTargetT =
3208                 Resources.toSigningCertificateLineage(
3209                         ApkSignerTest.class, LINEAGE_EC_P256_2_SIGNERS_RESOURCE_NAME);
3210 
3211         assertThrows(IllegalArgumentException.class, () ->
3212                 getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false,
3213                         AndroidSdkVersion.T, lineageTargetT));
3214     }
3215 
3216     @Test
testV31_targetedSignerTCertNotLastInLineage_truncatesLineage()3217     public void testV31_targetedSignerTCertNotLastInLineage_truncatesLineage() throws Exception {
3218         // Previously when a rotation signing config was provided with a lineage that did not
3219         // contain the signer as the last node, the lineage was truncated to the signer's position.
3220         // This test verifies a targeted signing config specified with a lineage containing signers
3221         // later than the current signer will be truncated to the provided signer.
3222         SigningCertificateLineage lineageTargetT =
3223                 Resources.toSigningCertificateLineage(
3224                         ApkSignerTest.class, LINEAGE_RSA_2048_3_SIGNERS_RESOURCE_NAME);
3225         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3226                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
3227         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
3228                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
3229         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT);
3230 
3231         File signedApk = sign("original.apk",
3232                 new ApkSigner.Builder(signerConfigs)
3233                         .setV1SigningEnabled(true)
3234                         .setV2SigningEnabled(true)
3235                         .setV3SigningEnabled(true)
3236                         .setV4SigningEnabled(false));
3237         ApkVerifier.Result result = verify(signedApk, null);
3238 
3239         assertVerified(result);
3240         assertVerificationWarning(result, null);
3241         assertTrue(result.isVerifiedUsingV3Scheme());
3242         assertTrue(result.isVerifiedUsingV31Scheme());
3243         assertEquals(1, result.getV3SchemeSigners().size());
3244         assertEquals(1, result.getV31SchemeSigners().size());
3245         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3246                 AndroidSdkVersion.T);
3247         assertLineageContainsExpectedSigners(
3248                 result.getV31SchemeSigners().get(0).getSigningCertificateLineage(),
3249                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3250     }
3251 
3252     @Test
testV31_targetedSignerTAndUSubLineages_signsWithExpectedLineages()3253     public void testV31_targetedSignerTAndUSubLineages_signsWithExpectedLineages()
3254             throws Exception {
3255         // Since the V3.1 signature scheme supports targeted signing configs with separate lineages
3256         // as long as the lineages can be merged into a common lineage, this test verifies two
3257         // targeted signing configs with lineages A -> B and B -> C can be used to sign an APK
3258         // and that each signer from a verification has the expected lineage.
3259         SigningCertificateLineage lineageTargetT =
3260                 Resources.toSigningCertificateLineage(
3261                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3262         SigningCertificateLineage lineageTargetU =
3263                 Resources.toSigningCertificateLineage(
3264                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_2_3_RESOURCE_NAME);
3265         ApkSigner.SignerConfig signerTargetT = getDefaultSignerConfigFromResources(
3266                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.T, lineageTargetT);
3267         ApkSigner.SignerConfig signerTargetU = getDefaultSignerConfigFromResources(
3268                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.U, lineageTargetU);
3269         ApkSigner.SignerConfig originalSigner = getDefaultSignerConfigFromResources(
3270                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
3271         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(originalSigner, signerTargetT,
3272                 signerTargetU);
3273 
3274         File signedApk = sign("original.apk",
3275                 new ApkSigner.Builder(signerConfigs)
3276                         .setV1SigningEnabled(true)
3277                         .setV2SigningEnabled(true)
3278                         .setV3SigningEnabled(true)
3279                         .setV4SigningEnabled(false));
3280         ApkVerifier.Result result = verify(signedApk, null);
3281 
3282         assertVerified(result);
3283         assertVerificationWarning(result, null);
3284         assertTrue(result.isVerifiedUsingV3Scheme());
3285         assertTrue(result.isVerifiedUsingV31Scheme());
3286         assertEquals(1, result.getV3SchemeSigners().size());
3287         assertEquals(2, result.getV31SchemeSigners().size());
3288         assertV31SignerTargetsMinApiLevel(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3289                 AndroidSdkVersion.T);
3290         assertV31SignerTargetsMinApiLevel(result, THIRD_RSA_2048_SIGNER_RESOURCE_NAME,
3291                 AndroidSdkVersion.U);
3292         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
3293                         AndroidSdkVersion.T).getSigningCertificateLineage(),
3294                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3295         assertLineageContainsExpectedSigners(getV31SignerTargetingSdkVersion(result,
3296                         AndroidSdkVersion.U).getSigningCertificateLineage(),
3297                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
3298         assertLineageContainsExpectedSigners(result.getSigningCertificateLineage(),
3299                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME,
3300                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
3301     }
3302 
3303     @Test
testV31_targetedSignerPNoOriginalSigner_throwsException()3304     public void testV31_targetedSignerPNoOriginalSigner_throwsException() throws Exception {
3305         // Targeted signing configs can only target Android P and later since this was the initial
3306         // release that added support for V3. This test verifies if a signing config with a lineage
3307         // targeting P is provided without an original signer, an Exception is thrown to indicate
3308         // the original signer is required for the V1 and V2 signature schemes.
3309         SigningCertificateLineage lineageTargetP =
3310                 Resources.toSigningCertificateLineage(
3311                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3312         ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources(
3313                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, lineageTargetP);
3314         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetP);
3315 
3316         assertThrows(IllegalArgumentException.class, () -> sign("original.apk",
3317                 new ApkSigner.Builder(signerConfigs)
3318                         .setV1SigningEnabled(true)
3319                         .setV2SigningEnabled(true)
3320                         .setV3SigningEnabled(true)
3321                         .setV4SigningEnabled(false)));
3322     }
3323 
3324     @Test
testV31_targetedSignerPOriginalSigner_signed()3325     public void testV31_targetedSignerPOriginalSigner_signed() throws Exception {
3326         // While SDK targeted signing configs are intended to target later platform releases for
3327         // rotation, it is possible for a signer to target P with the original signing key. Without
3328         // a lineage, the signer will treat this as the original signing key and can use it to sign
3329         // the V1 and V2 blocks as well.
3330         ApkSigner.SignerConfig signerTargetP = getDefaultSignerConfigFromResources(
3331                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, false, AndroidSdkVersion.P, null);
3332         List<ApkSigner.SignerConfig> signerConfigs = Arrays.asList(signerTargetP);
3333 
3334         File signedApk = sign("original.apk",
3335                 new ApkSigner.Builder(signerConfigs)
3336                         .setV1SigningEnabled(true)
3337                         .setV2SigningEnabled(true)
3338                         .setV3SigningEnabled(true)
3339                         .setV4SigningEnabled(false));
3340         ApkVerifier.Result result = verify(signedApk, null);
3341 
3342         assertVerified(result);
3343         assertVerificationWarning(result, null);
3344         assertTrue(result.isVerifiedUsingV3Scheme());
3345         assertFalse(result.isVerifiedUsingV31Scheme());
3346     }
3347 
3348     @Test
testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner()3349     public void testV4_rotationMinSdkVersionLessThanT_signatureOnlyHasRotatedSigner()
3350             throws Exception {
3351         // To support SDK version targeting in the v3.1 signature scheme, apksig added a
3352         // rotation-min-sdk-version option to allow the caller to specify the level from which
3353         // the rotated signer should be used. A value less than T should result in a single
3354         // rotated signer in the V3 block (along with the corresponding lineage), and the V4
3355         // signature should use this signer.
3356         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
3357                 Arrays.asList(
3358                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
3359                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
3360         SigningCertificateLineage lineage =
3361                 Resources.toSigningCertificateLineage(
3362                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3363 
3364         File signedApk = sign("original.apk",
3365                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
3366                         .setV1SigningEnabled(true)
3367                         .setV2SigningEnabled(true)
3368                         .setV3SigningEnabled(true)
3369                         .setV4SigningEnabled(true)
3370                         .setMinSdkVersionForRotation(AndroidSdkVersion.P)
3371                         .setSigningCertificateLineage(lineage));
3372         ApkVerifier.Result result = verify(signedApk, null);
3373 
3374         assertResultContainsV4Signers(result, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3375     }
3376 
3377     @Test
testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey()3378     public void testV4_rotationMinSdkVersionT_signatureHasOrigAndRotatedKey() throws Exception {
3379         // When an APK is signed with a rotated key and the rotation-min-sdk-version X is set to T+,
3380         // a V3.1 block will be signed with the rotated signing key targeting X and later, and
3381         // a V3.0 block will be signed with the original signing key targeting P - X-1. The
3382         // V4 signature should contain both the original signing key and the rotated signing
3383         // key; this ensures if an APK is installed on a device running an SDK version less than X,
3384         // the V4 signature will be verified using the original signing key which will be the only
3385         // signing key visible to the platform.
3386         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
3387                 Arrays.asList(
3388                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
3389                         getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
3390         SigningCertificateLineage lineage =
3391                 Resources.toSigningCertificateLineage(
3392                         ApkSignerTest.class, LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME);
3393 
3394         File signedApk = sign("original.apk",
3395                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
3396                         .setV1SigningEnabled(true)
3397                         .setV2SigningEnabled(true)
3398                         .setV3SigningEnabled(true)
3399                         .setV4SigningEnabled(true)
3400                         .setMinSdkVersionForRotation(AndroidSdkVersion.T)
3401                         .setSigningCertificateLineage(lineage));
3402         ApkVerifier.Result result = verify(signedApk, null);
3403 
3404         assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3405                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
3406     }
3407 
3408     @Test
testV41_rotationWithDifferentDigestAlgos_v41UsesCorrectDigest()3409     public void testV41_rotationWithDifferentDigestAlgos_v41UsesCorrectDigest() throws Exception {
3410         // When signing an APK, the digest algorithm is determined by the number of bits in the
3411         // signing key to ensure the digest is not weaker than the key. If an original signing key
3412         // meets the requirements for the CHUNKED_SHA256 digest and the rotated signing key
3413         // meets the requirements for CHUNKED_SHA512, then the v3.0 and v3.1 signing blocks will
3414         // use different digests. The v4.1 signature must use the content digest from the v3.1
3415         // block since that's the digest that will be used to verify the v4.1 signature on all
3416         // platform versions that support the v3.1 signer.
3417         List<ApkSigner.SignerConfig> rsa2048SignerConfigWithLineage =
3418                 Arrays.asList(
3419                         getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME),
3420                         getDefaultSignerConfigFromResources(FIRST_RSA_4096_SIGNER_RESOURCE_NAME));
3421         SigningCertificateLineage lineage =
3422                 Resources.toSigningCertificateLineage(
3423                         ApkSignerTest.class, LINEAGE_RSA_2048_TO_RSA_4096_RESOURCE_NAME);
3424 
3425         File signedApk = sign("original.apk",
3426                 new ApkSigner.Builder(rsa2048SignerConfigWithLineage)
3427                         .setV1SigningEnabled(true)
3428                         .setV2SigningEnabled(true)
3429                         .setV3SigningEnabled(true)
3430                         .setV4SigningEnabled(true)
3431                         .setMinSdkVersionForRotation(AndroidSdkVersion.T)
3432                         .setSigningCertificateLineage(lineage));
3433         ApkVerifier.Result result = verify(signedApk, null);
3434 
3435         assertResultContainsV4Signers(result, FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
3436                 FIRST_RSA_4096_SIGNER_RESOURCE_NAME);
3437     }
3438 
3439     @Test
3440     public void
testSourceStampTimestamp_signWithSourceStampAndTimestampDefault_validTimestampValue()3441     testSourceStampTimestamp_signWithSourceStampAndTimestampDefault_validTimestampValue()
3442             throws Exception {
3443         // Source stamps should include a timestamp attribute with the epoch time the stamp block
3444         // was signed. This test verifies a standard signing with a source stamp includes a valid
3445         // value for the source stamp timestamp attribute by default.
3446         ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources(
3447                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
3448         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
3449                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
3450 
3451         File signedApk = sign("original.apk",
3452                 new ApkSigner.Builder(ecP256SignerConfig)
3453                         .setV1SigningEnabled(true)
3454                         .setV2SigningEnabled(true)
3455                         .setV3SigningEnabled(true)
3456                         .setV4SigningEnabled(false)
3457                         .setSourceStampSignerConfig(rsa2048SignerConfig));
3458         ApkVerifier.Result result = verify(signedApk, null);
3459 
3460         assertSourceStampVerified(signedApk, result);
3461         long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds();
3462         assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0);
3463     }
3464 
3465     @Test
3466     public void
testSourceStampTimestamp_signWithSourceStampAndTimestampEnabled_validTimestampValue()3467     testSourceStampTimestamp_signWithSourceStampAndTimestampEnabled_validTimestampValue()
3468             throws Exception {
3469         // Similar to above, this test verifies a valid timestamp value is written to the
3470         // attribute when the caller explicitly requests to enable the source stamp timestamp.
3471         ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources(
3472                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
3473         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
3474                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
3475 
3476         File signedApk = sign("original.apk",
3477                 new ApkSigner.Builder(ecP256SignerConfig)
3478                         .setV1SigningEnabled(true)
3479                         .setV2SigningEnabled(true)
3480                         .setV3SigningEnabled(true)
3481                         .setV4SigningEnabled(false)
3482                         .setSourceStampSignerConfig(rsa2048SignerConfig)
3483                         .setSourceStampTimestampEnabled(true));
3484         ApkVerifier.Result result = verify(signedApk, null);
3485 
3486         assertSourceStampVerified(signedApk, result);
3487         long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds();
3488         assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0);
3489     }
3490 
3491     @Test
3492     public void
testSourceStampTimestamp_signWithSourceStampAndTimestampDisabled_defaultTimestampValue()3493     testSourceStampTimestamp_signWithSourceStampAndTimestampDisabled_defaultTimestampValue()
3494             throws Exception {
3495         // While source stamps should include a timestamp attribute indicating the time at which
3496         // the stamp was signed, this can cause problems for reproducible builds. The
3497         // ApkSigner.Builder#setSourceStampTimestampEnabled API allows the caller to specify
3498         // whether the timestamp attribute should be written; this test verifies no timestamp is
3499         // written to the source stamp if this API is used to disable the timestamp.
3500         ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources(
3501                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
3502         List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
3503                 getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
3504 
3505         File signedApk = sign("original.apk",
3506                 new ApkSigner.Builder(ecP256SignerConfig)
3507                         .setV1SigningEnabled(true)
3508                         .setV2SigningEnabled(true)
3509                         .setV3SigningEnabled(true)
3510                         .setV4SigningEnabled(false)
3511                         .setSourceStampSignerConfig(rsa2048SignerConfig)
3512                         .setSourceStampTimestampEnabled(false));
3513         ApkVerifier.Result result = verify(signedApk, null);
3514 
3515         assertSourceStampVerified(signedApk, result);
3516         long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds();
3517         assertEquals(0, timestamp);
3518     }
3519 
3520     /**
3521      * Asserts the provided {@code signedApk} contains a signature block with the expected
3522      * {@code byte[]} value and block ID as specified in the {@code expectedBlock}.
3523      */
assertSigningBlockContains(File signedApk, Pair<byte[], Integer> expectedBlock)3524     private static void assertSigningBlockContains(File signedApk,
3525             Pair<byte[], Integer> expectedBlock) throws Exception {
3526         try (RandomAccessFile apkFile = new RandomAccessFile(signedApk, "r")) {
3527             ApkUtils.ApkSigningBlock apkSigningBlock = ApkUtils.findApkSigningBlock(
3528                     DataSources.asDataSource(apkFile));
3529             List<Pair<byte[], Integer>> signatureBlocks =
3530                     ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock.getContents());
3531             for (Pair<byte[], Integer> signatureBlock : signatureBlocks) {
3532                 if (signatureBlock.getSecond().equals(expectedBlock.getSecond())) {
3533                     if (Arrays.equals(signatureBlock.getFirst(), expectedBlock.getFirst())) {
3534                         return;
3535                     }
3536                 }
3537             }
3538             fail(String.format(
3539                     "The APK signing block did not contain the expected block with ID %08x",
3540                     expectedBlock.getSecond()));
3541         }
3542     }
3543 
3544     /**
3545      * Asserts the provided verification {@code result} contains the expected {@code signers} for
3546      * each scheme that was used to verify the APK's signature.
3547      */
assertResultContainsSigners(ApkVerifier.Result result, String... signers)3548     static void assertResultContainsSigners(ApkVerifier.Result result, String... signers)
3549             throws Exception {
3550         assertResultContainsSigners(result, false, signers);
3551     }
3552 
3553     /**
3554      * Asserts the provided verification {@code result} contains the expected {@code signers} for
3555      * each scheme that was used to verify the APK's signature; if {@code rotationExpected} is set
3556      * to {@code true}, then the first element in {@code signers} is treated as the expected
3557      * original signer for any V1, V2, and V3 (where applicable) signatures, and the last element
3558      * is the rotated expected signer for V3+.
3559      */
assertResultContainsSigners(ApkVerifier.Result result, boolean rotationExpected, String... signers)3560     static void assertResultContainsSigners(ApkVerifier.Result result,
3561         boolean rotationExpected, String... signers) throws Exception {
3562         // A result must be successfully verified before verifying any of the result's signers.
3563         assertTrue(result.isVerified());
3564 
3565         List<X509Certificate> expectedSigners = new ArrayList<>();
3566         for (String signer : signers) {
3567             ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer);
3568             expectedSigners.addAll(signerConfig.getCertificates());
3569         }
3570         // If rotation is expected then the V1 and V2 signature should only be signed by the
3571         // original signer.
3572         List<X509Certificate> expectedV1Signers =
3573             rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners;
3574         List<X509Certificate> expectedV2Signers =
3575             rotationExpected ? List.of(expectedSigners.get(0)) : expectedSigners;
3576         // V3 only supports a single signer; if rotation is not expected or the V3.1 block contains
3577         // the rotated signing key then the expected V3.0 signer should be the original signer.
3578         List<X509Certificate> expectedV3Signers =
3579             !rotationExpected || result.isVerifiedUsingV31Scheme()
3580                 ? List.of(expectedSigners.get(0))
3581                 : List.of(expectedSigners.get(expectedSigners.size() - 1));
3582 
3583         if (result.isVerifiedUsingV1Scheme()) {
3584             Set<X509Certificate> v1Signers = new HashSet<>();
3585             for (ApkVerifier.Result.V1SchemeSignerInfo signer : result.getV1SchemeSigners()) {
3586                 v1Signers.add(signer.getCertificate());
3587             }
3588             assertTrue("Expected V1 signers: " + getAllSubjectNamesFrom(expectedV1Signers)
3589                             + ", actual V1 signers: " + getAllSubjectNamesFrom(v1Signers),
3590                     v1Signers.containsAll(expectedV1Signers));
3591         }
3592 
3593         if (result.isVerifiedUsingV2Scheme()) {
3594             Set<X509Certificate> v2Signers = new HashSet<>();
3595             for (ApkVerifier.Result.V2SchemeSignerInfo signer : result.getV2SchemeSigners()) {
3596                 v2Signers.add(signer.getCertificate());
3597             }
3598             assertTrue("Expected V2 signers: " + getAllSubjectNamesFrom(expectedV2Signers)
3599                             + ", actual V2 signers: " + getAllSubjectNamesFrom(v2Signers),
3600                     v2Signers.containsAll(expectedV2Signers));
3601         }
3602 
3603         if (result.isVerifiedUsingV3Scheme()) {
3604             Set<X509Certificate> v3Signers = new HashSet<>();
3605             for (V3SchemeSignerInfo signer : result.getV3SchemeSigners()) {
3606                 v3Signers.add(signer.getCertificate());
3607             }
3608             assertTrue("Expected V3 signers: " + getAllSubjectNamesFrom(expectedV3Signers)
3609                             + ", actual V3 signers: " + getAllSubjectNamesFrom(v3Signers),
3610                     v3Signers.containsAll(expectedV3Signers));
3611         }
3612 
3613         if (result.isVerifiedUsingV31Scheme()) {
3614             Set<X509Certificate> v31Signers = new HashSet<>();
3615             for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) {
3616                 v31Signers.add(signer.getCertificate());
3617             }
3618             // V3.1 only supports specifying signatures with a rotated signing key; if a V3.1
3619             // signing block was verified then ensure it contains the expected rotated signer.
3620             List<X509Certificate> expectedV31Signers = List
3621                 .of(expectedSigners.get(expectedSigners.size() - 1));
3622             assertTrue("Expected V3.1 signers: " + getAllSubjectNamesFrom(expectedV31Signers)
3623                     + ", actual V3.1 signers: " + getAllSubjectNamesFrom(v31Signers),
3624                 v31Signers.containsAll(expectedV31Signers));
3625         }
3626     }
3627 
3628     /**
3629      * Asserts the provided verification {@code result} contains the expected V4 {@code signers}.
3630      */
assertResultContainsV4Signers(ApkVerifier.Result result, String... signers)3631     private static void assertResultContainsV4Signers(ApkVerifier.Result result, String... signers)
3632             throws Exception {
3633         assertTrue(result.isVerified());
3634         assertTrue(result.isVerifiedUsingV4Scheme());
3635         List<X509Certificate> expectedSigners = new ArrayList<>();
3636         for (String signer : signers) {
3637             ApkSigner.SignerConfig signerConfig = getDefaultSignerConfigFromResources(signer);
3638             expectedSigners.addAll(signerConfig.getCertificates());
3639         }
3640         List<X509Certificate> v4Signers = new ArrayList<>();
3641         for (ApkVerifier.Result.V4SchemeSignerInfo signer : result.getV4SchemeSigners()) {
3642             v4Signers.addAll(signer.getCertificates());
3643         }
3644         assertTrue("Expected V4 signers: " + getAllSubjectNamesFrom(expectedSigners)
3645                         + ", actual V4 signers: " + getAllSubjectNamesFrom(v4Signers),
3646                 v4Signers.containsAll(expectedSigners));
3647     }
3648 
3649     /**
3650      * Asserts the provided {@code result} contains the expected {@code signer} targeting
3651      * {@code minSdkVersion} as the minimum version for rotation.
3652      */
assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer, int minSdkVersion)3653     static void assertV31SignerTargetsMinApiLevel(ApkVerifier.Result result, String signer,
3654         int minSdkVersion) throws Exception {
3655         assertTrue(result.isVerifiedUsingV31Scheme());
3656         ApkSigner.SignerConfig expectedSignerConfig = getDefaultSignerConfigFromResources(signer);
3657         StringBuilder errorMessage = new StringBuilder();
3658 
3659         boolean signerTargetsDevRelease = false;
3660         if (minSdkVersion == V3SchemeConstants.DEV_RELEASE) {
3661             minSdkVersion = V3SchemeConstants.PROD_RELEASE;
3662             signerTargetsDevRelease = true;
3663         }
3664 
3665         for (V3SchemeSignerInfo signerConfig : result.getV31SchemeSigners()) {
3666             if (signerConfig.getCertificates()
3667                 .containsAll(expectedSignerConfig.getCertificates())) {
3668                 // The V3.1 signature scheme allows the same signer to target multiple SDK versions
3669                 // with different capabilities in the lineage, so save the current error message
3670                 // in case no subsequent instances of this signer target the specified SDK version.
3671                 if (minSdkVersion != signerConfig.getMinSdkVersion()) {
3672                     if (errorMessage.length() > 0) {
3673                         errorMessage.append(System.getProperty("line.separator"));
3674                     }
3675                     errorMessage.append(
3676                             "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates())
3677                                     + ", is expected to target SDK version " + minSdkVersion
3678                                     + ", instead it is targeting "
3679                                     + signerConfig.getMinSdkVersion());
3680                 } else if (signerTargetsDevRelease
3681                         && !signerConfig.getRotationTargetsDevRelease()) {
3682                     if (errorMessage.length() > 0) {
3683                         errorMessage.append(System.getProperty("line.separator"));
3684                     }
3685                     errorMessage.append(
3686                             "The signer, " + getAllSubjectNamesFrom(signerConfig.getCertificates())
3687                                     + ", is targeting a development release, " + minSdkVersion
3688                                     + ", but the attribute to target a development release is not"
3689                                     + " set");
3690                 } else {
3691                     return;
3692                 }
3693             }
3694         }
3695         fail("Did not find the expected signer, " + getAllSubjectNamesFrom(
3696             expectedSignerConfig.getCertificates()) + ": " + errorMessage);
3697     }
3698 
3699     /**
3700      * Returns the V3.1 signer from the provided {@code result} targeting the specified {@code
3701      * targetSdkVersion}.
3702      */
getV31SignerTargetingSdkVersion(ApkVerifier.Result result, int targetSdkVersion)3703     private V3SchemeSignerInfo getV31SignerTargetingSdkVersion(ApkVerifier.Result result,
3704             int targetSdkVersion) throws Exception {
3705         boolean signerTargetsDevRelease = false;
3706         if (targetSdkVersion == V3SchemeConstants.DEV_RELEASE) {
3707             targetSdkVersion = V3SchemeConstants.PROD_RELEASE;
3708             signerTargetsDevRelease = true;
3709         }
3710         for (V3SchemeSignerInfo signer : result.getV31SchemeSigners()) {
3711             if (signer.getMinSdkVersion() == targetSdkVersion) {
3712                 // If a signer is targeting a development release and another signer is targeting
3713                 // the most recent production release, then both could be targeting the same SDK
3714                 // version.
3715                 if (signerTargetsDevRelease != signer.getRotationTargetsDevRelease()) {
3716                     continue;
3717                 }
3718                 return signer;
3719             }
3720         }
3721         fail("No V3.1 signer found targeting min SDK version " + targetSdkVersion
3722                 + ", dev release: " + signerTargetsDevRelease);
3723         return null;
3724     }
3725 
3726     /**
3727      * Returns a comma delimited {@code String} containing all of the Subject Names from the
3728      * provided {@code certificates}.
3729      */
getAllSubjectNamesFrom(Collection<X509Certificate> certificates)3730     private static String getAllSubjectNamesFrom(Collection<X509Certificate> certificates) {
3731         StringBuilder result = new StringBuilder();
3732         for (X509Certificate certificate : certificates) {
3733             if (result.length() > 0) {
3734                 result.append(", ");
3735             }
3736             result.append(certificate.getSubjectDN().getName());
3737         }
3738         return result.toString();
3739     }
3740 
resourceZipFileContains(String resourceName, String zipEntryName)3741     private static boolean resourceZipFileContains(String resourceName, String zipEntryName)
3742         throws IOException {
3743         ZipInputStream zip = new ZipInputStream(
3744             Resources.toInputStream(ApkSignerTest.class, resourceName));
3745         while (true) {
3746             ZipEntry entry = zip.getNextEntry();
3747             if (entry == null) {
3748                 break;
3749             }
3750 
3751             if (entry.getName().equals(zipEntryName)) {
3752                 return true;
3753             }
3754         }
3755 
3756         return false;
3757     }
3758 
getRSAPublicKeyFromSigningBlock(File apk, int signatureVersionId)3759     private RSAPublicKey getRSAPublicKeyFromSigningBlock(File apk, int signatureVersionId)
3760             throws Exception {
3761         int signatureVersionBlockId;
3762         switch (signatureVersionId) {
3763             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
3764                 signatureVersionBlockId = V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID;
3765                 break;
3766             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
3767                 signatureVersionBlockId = V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID;
3768                 break;
3769             default:
3770                 throw new Exception(
3771                         "Invalid signature version ID specified: " + signatureVersionId);
3772         }
3773         SignatureInfo signatureInfo =
3774                 getSignatureInfoFromApk(apk, signatureVersionId, signatureVersionBlockId);
3775         // FORMAT:
3776         // * length prefixed sequence of length prefixed signers
3777         //   * length-prefixed signed data
3778         //   * V3+ only - minSDK (uint32)
3779         //   * V3+ only - maxSDK (uint32)
3780         //   * length-prefixed sequence of length-prefixed signatures:
3781         //   * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
3782         ByteBuffer signers =
3783                 ApkSigningBlockUtils.getLengthPrefixedSlice(signatureInfo.signatureBlock);
3784         ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers);
3785         // Since all the data is read from the signer block the signedData and signatures are
3786         // discarded.
3787         ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
3788         // For V3+ signature version IDs discard the min / max SDKs as well
3789         if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) {
3790             signer.getInt();
3791             signer.getInt();
3792         }
3793         ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
3794         ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
3795         SubjectPublicKeyInfo subjectPublicKeyInfo =
3796                 Asn1BerParser.parse(publicKey, SubjectPublicKeyInfo.class);
3797         ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey;
3798         // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first
3799         // byte indicating the number of padding bits in the public key. Read this first byte to
3800         // allow parsing the rest of the RSAPublicKey as a sequence.
3801         subjectPublicKeyBuffer.get();
3802         return Asn1BerParser.parse(subjectPublicKeyBuffer, RSAPublicKey.class);
3803     }
3804 
getSignatureInfoFromApk( File apkFile, int signatureVersionId, int signatureVersionBlockId)3805     private static SignatureInfo getSignatureInfoFromApk(
3806             File apkFile, int signatureVersionId, int signatureVersionBlockId)
3807             throws IOException, ZipFormatException,
3808             ApkSigningBlockUtils.SignatureNotFoundException {
3809         try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) {
3810             DataSource apk = DataSources.asDataSource(f, 0, f.length());
3811             ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
3812             ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(
3813                     signatureVersionId);
3814             return ApkSigningBlockUtils.findSignature(apk, zipSections, signatureVersionBlockId,
3815                     result);
3816         }
3817     }
3818 
3819     /**
3820      * Asserts that signing the specified golden input file using the provided signing configuration
3821      * produces output identical to the specified golden output file.
3822      */
assertGolden( String inResourceName, String expectedOutResourceName, ApkSigner.Builder apkSignerBuilder)3823     private void assertGolden(
3824             String inResourceName,
3825             String expectedOutResourceName,
3826             ApkSigner.Builder apkSignerBuilder)
3827             throws Exception {
3828         // Sign the provided golden input
3829         File out = sign(inResourceName, apkSignerBuilder);
3830         assertVerified(verify(out, AndroidSdkVersion.P));
3831 
3832         // Assert that the output is identical to the provided golden output
3833         if (out.length() > Integer.MAX_VALUE) {
3834             throw new RuntimeException("Output too large: " + out.length() + " bytes");
3835         }
3836         byte[] outData = new byte[(int) out.length()];
3837         try (FileInputStream fis = new FileInputStream(out)) {
3838             fis.read(outData);
3839         }
3840         ByteBuffer actualOutBuf = ByteBuffer.wrap(outData);
3841 
3842         ByteBuffer expectedOutBuf =
3843                 ByteBuffer.wrap(Resources.toByteArray(getClass(), expectedOutResourceName));
3844 
3845         boolean identical = false;
3846         if (actualOutBuf.remaining() == expectedOutBuf.remaining()) {
3847             while (actualOutBuf.hasRemaining()) {
3848                 if (actualOutBuf.get() != expectedOutBuf.get()) {
3849                     break;
3850                 }
3851             }
3852             identical = !actualOutBuf.hasRemaining();
3853         }
3854 
3855         if (identical) {
3856             return;
3857         }
3858 
3859         if (KEEP_FAILING_OUTPUT_AS_FILES) {
3860             File tmp = File.createTempFile(getClass().getSimpleName(), ".apk");
3861             Files.copy(out.toPath(), tmp.toPath());
3862             fail(tmp + " differs from " + expectedOutResourceName);
3863         } else {
3864             fail("Output differs from " + expectedOutResourceName);
3865         }
3866     }
3867 
sign(File inApkFile, ApkSigner.Builder apkSignerBuilder)3868     private File sign(File inApkFile, ApkSigner.Builder apkSignerBuilder) throws Exception {
3869         try (RandomAccessFile apkFile = new RandomAccessFile(inApkFile, "r")) {
3870             DataSource in = DataSources.asDataSource(apkFile);
3871             return sign(in, apkSignerBuilder);
3872         }
3873     }
3874 
sign(String inResourceName, ApkSigner.Builder apkSignerBuilder)3875     private File sign(String inResourceName, ApkSigner.Builder apkSignerBuilder) throws Exception {
3876         DataSource in =
3877                 DataSources.asDataSource(
3878                         ByteBuffer.wrap(Resources.toByteArray(getClass(), inResourceName)));
3879         return sign(in, apkSignerBuilder);
3880     }
3881 
sign(DataSource in, ApkSigner.Builder apkSignerBuilder)3882     private File sign(DataSource in, ApkSigner.Builder apkSignerBuilder) throws Exception {
3883         File outFile = mTemporaryFolder.newFile();
3884         apkSignerBuilder.setInputApk(in).setOutputApk(outFile);
3885 
3886         File outFileIdSig = new File(outFile.getCanonicalPath() + ".idsig");
3887         apkSignerBuilder.setV4SignatureOutputFile(outFileIdSig);
3888         apkSignerBuilder.setV4ErrorReportingEnabled(true);
3889 
3890         apkSignerBuilder.build().sign();
3891         return outFile;
3892     }
3893 
verifyForMinSdkVersion(File apk, int minSdkVersion)3894     private static ApkVerifier.Result verifyForMinSdkVersion(File apk, int minSdkVersion)
3895             throws IOException, ApkFormatException, NoSuchAlgorithmException {
3896         return verify(apk, minSdkVersion);
3897     }
3898 
verify(File apk, Integer minSdkVersionOverride)3899     private static ApkVerifier.Result verify(File apk, Integer minSdkVersionOverride)
3900             throws IOException, ApkFormatException, NoSuchAlgorithmException {
3901         ApkVerifier.Builder builder = new ApkVerifier.Builder(apk);
3902         if (minSdkVersionOverride != null) {
3903             builder.setMinCheckedPlatformVersion(minSdkVersionOverride);
3904         }
3905         File idSig = new File(apk.getCanonicalPath() + ".idsig");
3906         if (idSig.exists()) {
3907             builder.setV4SignatureFile(idSig);
3908         }
3909         return builder.build().verify();
3910     }
3911 
assertVerified(ApkVerifier.Result result)3912     private static void assertVerified(ApkVerifier.Result result) {
3913         ApkVerifierTest.assertVerified(result);
3914     }
3915 
assertSourceStampVerified(File signedApk, ApkVerifier.Result result)3916     private static void assertSourceStampVerified(File signedApk, ApkVerifier.Result result)
3917             throws ApkSigningBlockUtils.SignatureNotFoundException,
3918                     IOException,
3919                     ZipFormatException {
3920         SignatureInfo signatureInfo =
3921                 getSignatureInfoFromApk(
3922                         signedApk,
3923                         ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
3924                         SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID);
3925         assertNotNull(signatureInfo.signatureBlock);
3926         assertTrue(result.isSourceStampVerified());
3927     }
3928 
assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue)3929     private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) {
3930         ApkVerifierTest.assertVerificationFailure(result, expectedIssue);
3931     }
3932 
assertFileContentsEqual(File first, File second)3933     private void assertFileContentsEqual(File first, File second) throws IOException {
3934         assertArrayEquals(Files.readAllBytes(Paths.get(first.getPath())),
3935                 Files.readAllBytes(Paths.get(second.getPath())));
3936     }
3937 
getSignerConfigsFromResources( String... signerNames)3938     private static List<ApkSigner.SignerConfig> getSignerConfigsFromResources(
3939             String... signerNames) throws Exception {
3940         List<ApkSigner.SignerConfig> signerConfigs = new ArrayList<>();
3941         for (String signerName : signerNames) {
3942             signerConfigs.add(getDefaultSignerConfigFromResources(signerName));
3943         }
3944         return signerConfigs;
3945     }
3946 
getDefaultSignerConfigFromResources( String keyNameInResources)3947     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
3948             String keyNameInResources) throws Exception {
3949         return getDefaultSignerConfigFromResources(keyNameInResources, false);
3950     }
3951 
getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning)3952     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
3953             String keyNameInResources, boolean deterministicDsaSigning) throws Exception {
3954         return getDefaultSignerConfigFromResources(
3955                 keyNameInResources, deterministicDsaSigning, 0, null);
3956     }
3957 
3958     /**
3959      * Returns a new {@link ApkSigner.SignerConfig} with the certificate and private key in
3960      * resources with the file prefix {@code keyNameInResources} targeting {@code targetSdkVersion}
3961      * with lineage {@code lineage} and using deterministic DSA signing when {@code
3962      * deterministicDsaSigning} is set to true.
3963      */
getDefaultSignerConfigFromResources( String keyNameInResources, boolean deterministicDsaSigning, int targetSdkVersion, SigningCertificateLineage lineage)3964     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
3965             String keyNameInResources,
3966             boolean deterministicDsaSigning,
3967             int targetSdkVersion,
3968             SigningCertificateLineage lineage)
3969             throws Exception {
3970         PrivateKey privateKey =
3971                 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8");
3972         List<X509Certificate> certs =
3973                 Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem");
3974         ApkSigner.SignerConfig.Builder signerConfigBuilder =
3975                 new ApkSigner.SignerConfig.Builder(
3976                         keyNameInResources,
3977                         new KeyConfig.Jca(privateKey),
3978                         certs,
3979                         deterministicDsaSigning);
3980         if (targetSdkVersion > 0) {
3981             signerConfigBuilder.setLineageForMinSdkVersion(lineage, targetSdkVersion);
3982         }
3983         return signerConfigBuilder.build();
3984     }
3985 
getDefaultSignerConfigFromResources( String keyNameInResources, String certNameInResources)3986     private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
3987             String keyNameInResources, String certNameInResources) throws Exception {
3988         PrivateKey privateKey =
3989                 Resources.toPrivateKey(ApkSignerTest.class, keyNameInResources + ".pk8");
3990         List<X509Certificate> certs =
3991                 Resources.toCertificateChain(ApkSignerTest.class, certNameInResources);
3992         return new ApkSigner.SignerConfig.Builder(
3993                         keyNameInResources, new KeyConfig.Jca(privateKey), certs)
3994                 .build();
3995     }
3996 
getDeterministicDsaSignerConfigFromResources( String keyNameInResources)3997     private static ApkSigner.SignerConfig getDeterministicDsaSignerConfigFromResources(
3998             String keyNameInResources) throws Exception {
3999         return getDefaultSignerConfigFromResources(keyNameInResources, true);
4000     }
4001 }
4002