• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
20 import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V31_SUPPORT;
21 import static com.android.apksig.internal.apk.v3.V3SchemeConstants.MIN_SDK_WITH_V3_SUPPORT;
22 
23 import com.android.apksig.apk.ApkFormatException;
24 import com.android.apksig.apk.ApkSigningBlockNotFoundException;
25 import com.android.apksig.apk.ApkUtils;
26 import com.android.apksig.apk.MinSdkVersionException;
27 import com.android.apksig.internal.apk.v3.V3SchemeConstants;
28 import com.android.apksig.internal.util.ByteBufferDataSource;
29 import com.android.apksig.internal.zip.CentralDirectoryRecord;
30 import com.android.apksig.internal.zip.EocdRecord;
31 import com.android.apksig.internal.zip.LocalFileRecord;
32 import com.android.apksig.internal.zip.ZipUtils;
33 import com.android.apksig.util.DataSink;
34 import com.android.apksig.util.DataSinks;
35 import com.android.apksig.util.DataSource;
36 import com.android.apksig.util.DataSources;
37 import com.android.apksig.util.ReadableDataSink;
38 import com.android.apksig.zip.ZipFormatException;
39 
40 import java.io.Closeable;
41 import java.io.File;
42 import java.io.IOException;
43 import java.io.RandomAccessFile;
44 import java.nio.ByteBuffer;
45 import java.nio.ByteOrder;
46 import java.security.InvalidKeyException;
47 import java.security.NoSuchAlgorithmException;
48 import java.security.PrivateKey;
49 import java.security.SignatureException;
50 import java.security.cert.X509Certificate;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.HashMap;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.Set;
59 
60 /**
61  * APK signer.
62  *
63  * <p>The signer preserves as much of the input APK as possible. For example, it preserves the order
64  * of APK entries and preserves their contents, including compressed form and alignment of data.
65  *
66  * <p>Use {@link Builder} to obtain instances of this signer.
67  *
68  * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a>
69  */
70 public class ApkSigner {
71 
72     /**
73      * Extensible data block/field header ID used for storing information about alignment of
74      * uncompressed entries as well as for aligning the entries's data. See ZIP appnote.txt section
75      * 4.5 Extensible data fields.
76      */
77     private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID = (short) 0xd935;
78 
79     /**
80      * Minimum size (in bytes) of the extensible data block/field used for alignment of uncompressed
81      * entries.
82      */
83     private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES = 6;
84 
85     private static final short ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
86 
87     /** Name of the Android manifest ZIP entry in APKs. */
88     private static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml";
89 
90     private final List<SignerConfig> mSignerConfigs;
91     private final SignerConfig mSourceStampSignerConfig;
92     private final SigningCertificateLineage mSourceStampSigningCertificateLineage;
93     private final boolean mForceSourceStampOverwrite;
94     private final Integer mMinSdkVersion;
95     private final int mRotationMinSdkVersion;
96     private final boolean mRotationTargetsDevRelease;
97     private final boolean mV1SigningEnabled;
98     private final boolean mV2SigningEnabled;
99     private final boolean mV3SigningEnabled;
100     private final boolean mV4SigningEnabled;
101     private final boolean mVerityEnabled;
102     private final boolean mV4ErrorReportingEnabled;
103     private final boolean mDebuggableApkPermitted;
104     private final boolean mOtherSignersSignaturesPreserved;
105     private final String mCreatedBy;
106 
107     private final ApkSignerEngine mSignerEngine;
108 
109     private final File mInputApkFile;
110     private final DataSource mInputApkDataSource;
111 
112     private final File mOutputApkFile;
113     private final DataSink mOutputApkDataSink;
114     private final DataSource mOutputApkDataSource;
115 
116     private final File mOutputV4File;
117 
118     private final SigningCertificateLineage mSigningCertificateLineage;
119 
ApkSigner( List<SignerConfig> signerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, boolean forceSourceStampOverwrite, Integer minSdkVersion, int rotationMinSdkVersion, boolean rotationTargetsDevRelease, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean v4SigningEnabled, boolean verityEnabled, boolean v4ErrorReportingEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, String createdBy, ApkSignerEngine signerEngine, File inputApkFile, DataSource inputApkDataSource, File outputApkFile, DataSink outputApkDataSink, DataSource outputApkDataSource, File outputV4File, SigningCertificateLineage signingCertificateLineage)120     private ApkSigner(
121             List<SignerConfig> signerConfigs,
122             SignerConfig sourceStampSignerConfig,
123             SigningCertificateLineage sourceStampSigningCertificateLineage,
124             boolean forceSourceStampOverwrite,
125             Integer minSdkVersion,
126             int rotationMinSdkVersion,
127             boolean rotationTargetsDevRelease,
128             boolean v1SigningEnabled,
129             boolean v2SigningEnabled,
130             boolean v3SigningEnabled,
131             boolean v4SigningEnabled,
132             boolean verityEnabled,
133             boolean v4ErrorReportingEnabled,
134             boolean debuggableApkPermitted,
135             boolean otherSignersSignaturesPreserved,
136             String createdBy,
137             ApkSignerEngine signerEngine,
138             File inputApkFile,
139             DataSource inputApkDataSource,
140             File outputApkFile,
141             DataSink outputApkDataSink,
142             DataSource outputApkDataSource,
143             File outputV4File,
144             SigningCertificateLineage signingCertificateLineage) {
145 
146         mSignerConfigs = signerConfigs;
147         mSourceStampSignerConfig = sourceStampSignerConfig;
148         mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage;
149         mForceSourceStampOverwrite = forceSourceStampOverwrite;
150         mMinSdkVersion = minSdkVersion;
151         mRotationMinSdkVersion = rotationMinSdkVersion;
152         mRotationTargetsDevRelease = rotationTargetsDevRelease;
153         mV1SigningEnabled = v1SigningEnabled;
154         mV2SigningEnabled = v2SigningEnabled;
155         mV3SigningEnabled = v3SigningEnabled;
156         mV4SigningEnabled = v4SigningEnabled;
157         mVerityEnabled = verityEnabled;
158         mV4ErrorReportingEnabled = v4ErrorReportingEnabled;
159         mDebuggableApkPermitted = debuggableApkPermitted;
160         mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved;
161         mCreatedBy = createdBy;
162 
163         mSignerEngine = signerEngine;
164 
165         mInputApkFile = inputApkFile;
166         mInputApkDataSource = inputApkDataSource;
167 
168         mOutputApkFile = outputApkFile;
169         mOutputApkDataSink = outputApkDataSink;
170         mOutputApkDataSource = outputApkDataSource;
171 
172         mOutputV4File = outputV4File;
173 
174         mSigningCertificateLineage = signingCertificateLineage;
175     }
176 
177     /**
178      * Signs the input APK and outputs the resulting signed APK. The input APK is not modified.
179      *
180      * @throws IOException if an I/O error is encountered while reading or writing the APKs
181      * @throws ApkFormatException if the input APK is malformed
182      * @throws NoSuchAlgorithmException if the APK signatures cannot be produced or verified because
183      *     a required cryptographic algorithm implementation is missing
184      * @throws InvalidKeyException if a signature could not be generated because a signing key is
185      *     not suitable for generating the signature
186      * @throws SignatureException if an error occurred while generating or verifying a signature
187      * @throws IllegalStateException if this signer's configuration is missing required information
188      *     or if the signing engine is in an invalid state.
189      */
sign()190     public void sign()
191             throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException,
192                     SignatureException, IllegalStateException {
193         Closeable in = null;
194         DataSource inputApk;
195         try {
196             if (mInputApkDataSource != null) {
197                 inputApk = mInputApkDataSource;
198             } else if (mInputApkFile != null) {
199                 RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r");
200                 in = inputFile;
201                 inputApk = DataSources.asDataSource(inputFile);
202             } else {
203                 throw new IllegalStateException("Input APK not specified");
204             }
205 
206             Closeable out = null;
207             try {
208                 DataSink outputApkOut;
209                 DataSource outputApkIn;
210                 if (mOutputApkDataSink != null) {
211                     outputApkOut = mOutputApkDataSink;
212                     outputApkIn = mOutputApkDataSource;
213                 } else if (mOutputApkFile != null) {
214                     RandomAccessFile outputFile = new RandomAccessFile(mOutputApkFile, "rw");
215                     out = outputFile;
216                     outputFile.setLength(0);
217                     outputApkOut = DataSinks.asDataSink(outputFile);
218                     outputApkIn = DataSources.asDataSource(outputFile);
219                 } else {
220                     throw new IllegalStateException("Output APK not specified");
221                 }
222 
223                 sign(inputApk, outputApkOut, outputApkIn);
224             } finally {
225                 if (out != null) {
226                     out.close();
227                 }
228             }
229         } finally {
230             if (in != null) {
231                 in.close();
232             }
233         }
234     }
235 
sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn)236     private void sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn)
237             throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException,
238                     SignatureException {
239         // Step 1. Find input APK's main ZIP sections
240         ApkUtils.ZipSections inputZipSections;
241         try {
242             inputZipSections = ApkUtils.findZipSections(inputApk);
243         } catch (ZipFormatException e) {
244             throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
245         }
246         long inputApkSigningBlockOffset = -1;
247         DataSource inputApkSigningBlock = null;
248         try {
249             ApkUtils.ApkSigningBlock apkSigningBlockInfo =
250                     ApkUtils.findApkSigningBlock(inputApk, inputZipSections);
251             inputApkSigningBlockOffset = apkSigningBlockInfo.getStartOffset();
252             inputApkSigningBlock = apkSigningBlockInfo.getContents();
253         } catch (ApkSigningBlockNotFoundException e) {
254             // Input APK does not contain an APK Signing Block. That's OK. APKs are not required to
255             // contain this block. It's only needed if the APK is signed using APK Signature Scheme
256             // v2 and/or v3.
257         }
258         DataSource inputApkLfhSection =
259                 inputApk.slice(
260                         0,
261                         (inputApkSigningBlockOffset != -1)
262                                 ? inputApkSigningBlockOffset
263                                 : inputZipSections.getZipCentralDirectoryOffset());
264 
265         // Step 2. Parse the input APK's ZIP Central Directory
266         ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections);
267         List<CentralDirectoryRecord> inputCdRecords =
268                 parseZipCentralDirectory(inputCd, inputZipSections);
269 
270         List<Hints.PatternWithRange> pinPatterns =
271                 extractPinPatterns(inputCdRecords, inputApkLfhSection);
272         List<Hints.ByteRange> pinByteRanges = pinPatterns == null ? null : new ArrayList<>();
273 
274         // Step 3. Obtain a signer engine instance
275         ApkSignerEngine signerEngine;
276         if (mSignerEngine != null) {
277             // Use the provided signer engine
278             signerEngine = mSignerEngine;
279         } else {
280             // Construct a signer engine from the provided parameters
281             int minSdkVersion;
282             if (mMinSdkVersion != null) {
283                 // No need to extract minSdkVersion from the APK's AndroidManifest.xml
284                 minSdkVersion = mMinSdkVersion;
285             } else {
286                 // Need to extract minSdkVersion from the APK's AndroidManifest.xml
287                 minSdkVersion = getMinSdkVersionFromApk(inputCdRecords, inputApkLfhSection);
288             }
289             List<DefaultApkSignerEngine.SignerConfig> engineSignerConfigs =
290                     new ArrayList<>(mSignerConfigs.size());
291             for (SignerConfig signerConfig : mSignerConfigs) {
292                 engineSignerConfigs.add(
293                         new DefaultApkSignerEngine.SignerConfig.Builder(
294                                         signerConfig.getName(),
295                                         signerConfig.getPrivateKey(),
296                                         signerConfig.getCertificates(),
297                                         signerConfig.getDeterministicDsaSigning())
298                                 .build());
299             }
300             DefaultApkSignerEngine.Builder signerEngineBuilder =
301                     new DefaultApkSignerEngine.Builder(engineSignerConfigs, minSdkVersion)
302                             .setV1SigningEnabled(mV1SigningEnabled)
303                             .setV2SigningEnabled(mV2SigningEnabled)
304                             .setV3SigningEnabled(mV3SigningEnabled)
305                             .setVerityEnabled(mVerityEnabled)
306                             .setDebuggableApkPermitted(mDebuggableApkPermitted)
307                             .setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved)
308                             .setSigningCertificateLineage(mSigningCertificateLineage)
309                             .setMinSdkVersionForRotation(mRotationMinSdkVersion)
310                             .setRotationTargetsDevRelease(mRotationTargetsDevRelease);
311             if (mCreatedBy != null) {
312                 signerEngineBuilder.setCreatedBy(mCreatedBy);
313             }
314             if (mSourceStampSignerConfig != null) {
315                 signerEngineBuilder.setStampSignerConfig(
316                         new DefaultApkSignerEngine.SignerConfig.Builder(
317                                         mSourceStampSignerConfig.getName(),
318                                         mSourceStampSignerConfig.getPrivateKey(),
319                                         mSourceStampSignerConfig.getCertificates(),
320                                         mSourceStampSignerConfig.getDeterministicDsaSigning())
321                                 .build());
322             }
323             if (mSourceStampSigningCertificateLineage != null) {
324                 signerEngineBuilder.setSourceStampSigningCertificateLineage(
325                         mSourceStampSigningCertificateLineage);
326             }
327             signerEngine = signerEngineBuilder.build();
328         }
329 
330         // Step 4. Provide the signer engine with the input APK's APK Signing Block (if any)
331         if (inputApkSigningBlock != null) {
332             signerEngine.inputApkSigningBlock(inputApkSigningBlock);
333         }
334 
335         // Step 5. Iterate over input APK's entries and output the Local File Header + data of those
336         // entries which need to be output. Entries are iterated in the order in which their Local
337         // File Header records are stored in the file. This is to achieve better data locality in
338         // case Central Directory entries are in the wrong order.
339         List<CentralDirectoryRecord> inputCdRecordsSortedByLfhOffset =
340                 new ArrayList<>(inputCdRecords);
341         Collections.sort(
342                 inputCdRecordsSortedByLfhOffset,
343                 CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
344         int lastModifiedDateForNewEntries = -1;
345         int lastModifiedTimeForNewEntries = -1;
346         long inputOffset = 0;
347         long outputOffset = 0;
348         byte[] sourceStampCertificateDigest = null;
349         Map<String, CentralDirectoryRecord> outputCdRecordsByName =
350                 new HashMap<>(inputCdRecords.size());
351         for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) {
352             String entryName = inputCdRecord.getName();
353             if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) {
354                 continue; // We'll re-add below if needed.
355             }
356             if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(entryName)) {
357                 try {
358                     sourceStampCertificateDigest =
359                             LocalFileRecord.getUncompressedData(
360                                     inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
361                 } catch (ZipFormatException ex) {
362                     throw new ApkFormatException("Bad source stamp entry");
363                 }
364                 continue; // Existing source stamp is handled below as needed.
365             }
366             ApkSignerEngine.InputJarEntryInstructions entryInstructions =
367                     signerEngine.inputJarEntry(entryName);
368             boolean shouldOutput;
369             switch (entryInstructions.getOutputPolicy()) {
370                 case OUTPUT:
371                     shouldOutput = true;
372                     break;
373                 case OUTPUT_BY_ENGINE:
374                 case SKIP:
375                     shouldOutput = false;
376                     break;
377                 default:
378                     throw new RuntimeException(
379                             "Unknown output policy: " + entryInstructions.getOutputPolicy());
380             }
381 
382             long inputLocalFileHeaderStartOffset = inputCdRecord.getLocalFileHeaderOffset();
383             if (inputLocalFileHeaderStartOffset > inputOffset) {
384                 // Unprocessed data in input starting at inputOffset and ending and the start of
385                 // this record's LFH. We output this data verbatim because this signer is supposed
386                 // to preserve as much of input as possible.
387                 long chunkSize = inputLocalFileHeaderStartOffset - inputOffset;
388                 inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
389                 outputOffset += chunkSize;
390                 inputOffset = inputLocalFileHeaderStartOffset;
391             }
392             LocalFileRecord inputLocalFileRecord;
393             try {
394                 inputLocalFileRecord =
395                         LocalFileRecord.getRecord(
396                                 inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
397             } catch (ZipFormatException e) {
398                 throw new ApkFormatException("Malformed ZIP entry: " + inputCdRecord.getName(), e);
399             }
400             inputOffset += inputLocalFileRecord.getSize();
401 
402             ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
403                     entryInstructions.getInspectJarEntryRequest();
404             if (inspectEntryRequest != null) {
405                 fulfillInspectInputJarEntryRequest(
406                         inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
407             }
408 
409             if (shouldOutput) {
410                 // Find the max value of last modified, to be used for new entries added by the
411                 // signer.
412                 int lastModifiedDate = inputCdRecord.getLastModificationDate();
413                 int lastModifiedTime = inputCdRecord.getLastModificationTime();
414                 if ((lastModifiedDateForNewEntries == -1)
415                         || (lastModifiedDate > lastModifiedDateForNewEntries)
416                         || ((lastModifiedDate == lastModifiedDateForNewEntries)
417                                 && (lastModifiedTime > lastModifiedTimeForNewEntries))) {
418                     lastModifiedDateForNewEntries = lastModifiedDate;
419                     lastModifiedTimeForNewEntries = lastModifiedTime;
420                 }
421 
422                 inspectEntryRequest = signerEngine.outputJarEntry(entryName);
423                 if (inspectEntryRequest != null) {
424                     fulfillInspectInputJarEntryRequest(
425                             inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
426                 }
427 
428                 // Output entry's Local File Header + data
429                 long outputLocalFileHeaderOffset = outputOffset;
430                 OutputSizeAndDataOffset outputLfrResult =
431                         outputInputJarEntryLfhRecordPreservingDataAlignment(
432                                 inputApkLfhSection,
433                                 inputLocalFileRecord,
434                                 outputApkOut,
435                                 outputLocalFileHeaderOffset);
436                 outputOffset += outputLfrResult.outputBytes;
437                 long outputDataOffset =
438                         outputLocalFileHeaderOffset + outputLfrResult.dataOffsetBytes;
439 
440                 if (pinPatterns != null) {
441                     boolean pinFileHeader = false;
442                     for (Hints.PatternWithRange pinPattern : pinPatterns) {
443                         if (pinPattern.matcher(inputCdRecord.getName()).matches()) {
444                             Hints.ByteRange dataRange =
445                                     new Hints.ByteRange(outputDataOffset, outputOffset);
446                             Hints.ByteRange pinRange =
447                                     pinPattern.ClampToAbsoluteByteRange(dataRange);
448                             if (pinRange != null) {
449                                 pinFileHeader = true;
450                                 pinByteRanges.add(pinRange);
451                             }
452                         }
453                     }
454                     if (pinFileHeader) {
455                         pinByteRanges.add(
456                                 new Hints.ByteRange(outputLocalFileHeaderOffset, outputDataOffset));
457                     }
458                 }
459 
460                 // Enqueue entry's Central Directory record for output
461                 CentralDirectoryRecord outputCdRecord;
462                 if (outputLocalFileHeaderOffset == inputLocalFileRecord.getStartOffsetInArchive()) {
463                     outputCdRecord = inputCdRecord;
464                 } else {
465                     outputCdRecord =
466                             inputCdRecord.createWithModifiedLocalFileHeaderOffset(
467                                     outputLocalFileHeaderOffset);
468                 }
469                 outputCdRecordsByName.put(entryName, outputCdRecord);
470             }
471         }
472         long inputLfhSectionSize = inputApkLfhSection.size();
473         if (inputOffset < inputLfhSectionSize) {
474             // Unprocessed data in input starting at inputOffset and ending and the end of the input
475             // APK's LFH section. We output this data verbatim because this signer is supposed
476             // to preserve as much of input as possible.
477             long chunkSize = inputLfhSectionSize - inputOffset;
478             inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
479             outputOffset += chunkSize;
480             inputOffset = inputLfhSectionSize;
481         }
482 
483         // Step 6. Sort output APK's Central Directory records in the order in which they should
484         // appear in the output
485         List<CentralDirectoryRecord> outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10);
486         for (CentralDirectoryRecord inputCdRecord : inputCdRecords) {
487             String entryName = inputCdRecord.getName();
488             CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName);
489             if (outputCdRecord != null) {
490                 outputCdRecords.add(outputCdRecord);
491             }
492         }
493 
494         if (lastModifiedDateForNewEntries == -1) {
495             lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS)
496             lastModifiedTimeForNewEntries = 0;
497         }
498 
499         // Step 7. Generate and output SourceStamp certificate hash, if necessary. This may output
500         // more Local File Header + data entries and add to the list of output Central Directory
501         // records.
502         if (signerEngine.isEligibleForSourceStamp()) {
503             byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest();
504             if (mForceSourceStampOverwrite
505                     || sourceStampCertificateDigest == null
506                     || Arrays.equals(uncompressedData, sourceStampCertificateDigest)) {
507                 outputOffset +=
508                         outputDataToOutputApk(
509                                 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME,
510                                 uncompressedData,
511                                 outputOffset,
512                                 outputCdRecords,
513                                 lastModifiedTimeForNewEntries,
514                                 lastModifiedDateForNewEntries,
515                                 outputApkOut);
516             } else {
517                 throw new ApkFormatException(
518                         String.format(
519                                 "Cannot generate SourceStamp. APK contains an existing entry with"
520                                     + " the name: %s, and it is different than the provided source"
521                                     + " stamp certificate",
522                                 SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME));
523             }
524         }
525 
526         // Step 7.5. Generate pinlist.meta file if necessary.
527         // This has to be before the step 8 so that the file is signed.
528         if (pinByteRanges != null) {
529             // Covers JAR signature and zip central dir entry.
530             // The signature files don't have to be pinned, but pinning them isn't that wasteful
531             // since the total size is small.
532             pinByteRanges.add(new Hints.ByteRange(outputOffset, Long.MAX_VALUE));
533             String entryName = Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME;
534             byte[] uncompressedData = Hints.encodeByteRangeList(pinByteRanges);
535 
536             requestOutputEntryInspection(signerEngine, entryName, uncompressedData);
537             outputOffset +=
538                 outputDataToOutputApk(
539                     entryName,
540                     uncompressedData,
541                     outputOffset,
542                     outputCdRecords,
543                     lastModifiedTimeForNewEntries,
544                     lastModifiedDateForNewEntries,
545                     outputApkOut);
546         }
547 
548         // Step 8. Generate and output JAR signatures, if necessary. This may output more Local File
549         // Header + data entries and add to the list of output Central Directory records.
550         ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest =
551                 signerEngine.outputJarEntries();
552         if (outputJarSignatureRequest != null) {
553             for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry :
554                     outputJarSignatureRequest.getAdditionalJarEntries()) {
555                 String entryName = entry.getName();
556                 byte[] uncompressedData = entry.getData();
557 
558                 requestOutputEntryInspection(signerEngine, entryName, uncompressedData);
559                 outputOffset +=
560                         outputDataToOutputApk(
561                                 entryName,
562                                 uncompressedData,
563                                 outputOffset,
564                                 outputCdRecords,
565                                 lastModifiedTimeForNewEntries,
566                                 lastModifiedDateForNewEntries,
567                                 outputApkOut);
568             }
569             outputJarSignatureRequest.done();
570         }
571 
572         // Step 9. Construct output ZIP Central Directory in an in-memory buffer
573         long outputCentralDirSizeBytes = 0;
574         for (CentralDirectoryRecord record : outputCdRecords) {
575             outputCentralDirSizeBytes += record.getSize();
576         }
577         if (outputCentralDirSizeBytes > Integer.MAX_VALUE) {
578             throw new IOException(
579                     "Output ZIP Central Directory too large: "
580                             + outputCentralDirSizeBytes
581                             + " bytes");
582         }
583         ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes);
584         for (CentralDirectoryRecord record : outputCdRecords) {
585             record.copyTo(outputCentralDir);
586         }
587         outputCentralDir.flip();
588         DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir);
589         long outputCentralDirStartOffset = outputOffset;
590         int outputCentralDirRecordCount = outputCdRecords.size();
591 
592         // Step 10. Construct output ZIP End of Central Directory record in an in-memory buffer
593         ByteBuffer outputEocd =
594                 EocdRecord.createWithModifiedCentralDirectoryInfo(
595                         inputZipSections.getZipEndOfCentralDirectory(),
596                         outputCentralDirRecordCount,
597                         outputCentralDirDataSource.size(),
598                         outputCentralDirStartOffset);
599 
600         // Step 11. Generate and output APK Signature Scheme v2 and/or v3 signatures and/or
601         // SourceStamp signatures, if necessary.
602         // This may insert an APK Signing Block just before the output's ZIP Central Directory
603         ApkSignerEngine.OutputApkSigningBlockRequest2 outputApkSigningBlockRequest =
604                 signerEngine.outputZipSections2(
605                         outputApkIn,
606                         outputCentralDirDataSource,
607                         DataSources.asDataSource(outputEocd));
608 
609         if (outputApkSigningBlockRequest != null) {
610             int padding = outputApkSigningBlockRequest.getPaddingSizeBeforeApkSigningBlock();
611             outputApkOut.consume(ByteBuffer.allocate(padding));
612             byte[] outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock();
613             outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length);
614             ZipUtils.setZipEocdCentralDirectoryOffset(
615                     outputEocd,
616                     outputCentralDirStartOffset + padding + outputApkSigningBlock.length);
617             outputApkSigningBlockRequest.done();
618         }
619 
620         // Step 12. Output ZIP Central Directory and ZIP End of Central Directory
621         outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut);
622         outputApkOut.consume(outputEocd);
623         signerEngine.outputDone();
624 
625         // Step 13. Generate and output APK Signature Scheme v4 signatures, if necessary.
626         if (mV4SigningEnabled) {
627             signerEngine.signV4(outputApkIn, mOutputV4File, !mV4ErrorReportingEnabled);
628         }
629     }
630 
requestOutputEntryInspection( ApkSignerEngine signerEngine, String entryName, byte[] uncompressedData)631     private static void requestOutputEntryInspection(
632             ApkSignerEngine signerEngine,
633             String entryName,
634             byte[] uncompressedData)
635             throws IOException {
636         ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
637                 signerEngine.outputJarEntry(entryName);
638         if (inspectEntryRequest != null) {
639             inspectEntryRequest.getDataSink().consume(
640                     uncompressedData, 0, uncompressedData.length);
641             inspectEntryRequest.done();
642         }
643     }
644 
outputDataToOutputApk( String entryName, byte[] uncompressedData, long localFileHeaderOffset, List<CentralDirectoryRecord> outputCdRecords, int lastModifiedTimeForNewEntries, int lastModifiedDateForNewEntries, DataSink outputApkOut)645     private static long outputDataToOutputApk(
646             String entryName,
647             byte[] uncompressedData,
648             long localFileHeaderOffset,
649             List<CentralDirectoryRecord> outputCdRecords,
650             int lastModifiedTimeForNewEntries,
651             int lastModifiedDateForNewEntries,
652             DataSink outputApkOut)
653             throws IOException {
654         ZipUtils.DeflateResult deflateResult = ZipUtils.deflate(ByteBuffer.wrap(uncompressedData));
655         byte[] compressedData = deflateResult.output;
656         long uncompressedDataCrc32 = deflateResult.inputCrc32;
657         long numOfDataBytes =
658                 LocalFileRecord.outputRecordWithDeflateCompressedData(
659                         entryName,
660                         lastModifiedTimeForNewEntries,
661                         lastModifiedDateForNewEntries,
662                         compressedData,
663                         uncompressedDataCrc32,
664                         uncompressedData.length,
665                         outputApkOut);
666         outputCdRecords.add(
667                 CentralDirectoryRecord.createWithDeflateCompressedData(
668                         entryName,
669                         lastModifiedTimeForNewEntries,
670                         lastModifiedDateForNewEntries,
671                         uncompressedDataCrc32,
672                         compressedData.length,
673                         uncompressedData.length,
674                         localFileHeaderOffset));
675         return numOfDataBytes;
676     }
677 
fulfillInspectInputJarEntryRequest( DataSource lfhSection, LocalFileRecord localFileRecord, ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest)678     private static void fulfillInspectInputJarEntryRequest(
679             DataSource lfhSection,
680             LocalFileRecord localFileRecord,
681             ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest)
682             throws IOException, ApkFormatException {
683         try {
684             localFileRecord.outputUncompressedData(lfhSection, inspectEntryRequest.getDataSink());
685         } catch (ZipFormatException e) {
686             throw new ApkFormatException("Malformed ZIP entry: " + localFileRecord.getName(), e);
687         }
688         inspectEntryRequest.done();
689     }
690 
691     private static class OutputSizeAndDataOffset {
692         public long outputBytes;
693         public long dataOffsetBytes;
694 
OutputSizeAndDataOffset(long outputBytes, long dataOffsetBytes)695         public OutputSizeAndDataOffset(long outputBytes, long dataOffsetBytes) {
696             this.outputBytes = outputBytes;
697             this.dataOffsetBytes = dataOffsetBytes;
698         }
699     }
700 
outputInputJarEntryLfhRecordPreservingDataAlignment( DataSource inputLfhSection, LocalFileRecord inputRecord, DataSink outputLfhSection, long outputOffset)701     private static OutputSizeAndDataOffset outputInputJarEntryLfhRecordPreservingDataAlignment(
702             DataSource inputLfhSection,
703             LocalFileRecord inputRecord,
704             DataSink outputLfhSection,
705             long outputOffset)
706             throws IOException {
707         long inputOffset = inputRecord.getStartOffsetInArchive();
708         if (inputOffset == outputOffset) {
709             // This record's data will be aligned same as in the input APK.
710             return new OutputSizeAndDataOffset(
711                     inputRecord.outputRecord(inputLfhSection, outputLfhSection),
712                     inputRecord.getDataStartOffsetInRecord());
713         }
714         int dataAlignmentMultiple = getInputJarEntryDataAlignmentMultiple(inputRecord);
715         if ((dataAlignmentMultiple <= 1)
716                 || ((inputOffset % dataAlignmentMultiple)
717                         == (outputOffset % dataAlignmentMultiple))) {
718             // This record's data will be aligned same as in the input APK.
719             return new OutputSizeAndDataOffset(
720                     inputRecord.outputRecord(inputLfhSection, outputLfhSection),
721                     inputRecord.getDataStartOffsetInRecord());
722         }
723 
724         long inputDataStartOffset = inputOffset + inputRecord.getDataStartOffsetInRecord();
725         if ((inputDataStartOffset % dataAlignmentMultiple) != 0) {
726             // This record's data is not aligned in the input APK. No need to align it in the
727             // output.
728             return new OutputSizeAndDataOffset(
729                     inputRecord.outputRecord(inputLfhSection, outputLfhSection),
730                     inputRecord.getDataStartOffsetInRecord());
731         }
732 
733         // This record's data needs to be re-aligned in the output. This is achieved using the
734         // record's extra field.
735         ByteBuffer aligningExtra =
736                 createExtraFieldToAlignData(
737                         inputRecord.getExtra(),
738                         outputOffset + inputRecord.getExtraFieldStartOffsetInsideRecord(),
739                         dataAlignmentMultiple);
740         long dataOffset =
741                 (long) inputRecord.getDataStartOffsetInRecord()
742                         + aligningExtra.remaining()
743                         - inputRecord.getExtra().remaining();
744         return new OutputSizeAndDataOffset(
745                 inputRecord.outputRecordWithModifiedExtra(
746                         inputLfhSection, aligningExtra, outputLfhSection),
747                 dataOffset);
748     }
749 
getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry)750     private static int getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry) {
751         if (entry.isDataCompressed()) {
752             // Compressed entries don't need to be aligned
753             return 1;
754         }
755 
756         // Attempt to obtain the alignment multiple from the entry's extra field.
757         ByteBuffer extra = entry.getExtra();
758         if (extra.hasRemaining()) {
759             extra.order(ByteOrder.LITTLE_ENDIAN);
760             // FORMAT: sequence of fields. Each field consists of:
761             //   * uint16 ID
762             //   * uint16 size
763             //   * 'size' bytes: payload
764             while (extra.remaining() >= 4) {
765                 short headerId = extra.getShort();
766                 int dataSize = ZipUtils.getUnsignedInt16(extra);
767                 if (dataSize > extra.remaining()) {
768                     // Malformed field -- insufficient input remaining
769                     break;
770                 }
771                 if (headerId != ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID) {
772                     // Skip this field
773                     extra.position(extra.position() + dataSize);
774                     continue;
775                 }
776                 // This is APK alignment field.
777                 // FORMAT:
778                 //  * uint16 alignment multiple (in bytes)
779                 //  * remaining bytes -- padding to achieve alignment of data which starts after
780                 //    the extra field
781                 if (dataSize < 2) {
782                     // Malformed
783                     break;
784                 }
785                 return ZipUtils.getUnsignedInt16(extra);
786             }
787         }
788 
789         // Fall back to filename-based defaults
790         return (entry.getName().endsWith(".so")) ? ANDROID_COMMON_PAGE_ALIGNMENT_BYTES : 4;
791     }
792 
createExtraFieldToAlignData( ByteBuffer original, long extraStartOffset, int dataAlignmentMultiple)793     private static ByteBuffer createExtraFieldToAlignData(
794             ByteBuffer original, long extraStartOffset, int dataAlignmentMultiple) {
795         if (dataAlignmentMultiple <= 1) {
796             return original;
797         }
798 
799         // In the worst case scenario, we'll increase the output size by 6 + dataAlignment - 1.
800         ByteBuffer result = ByteBuffer.allocate(original.remaining() + 5 + dataAlignmentMultiple);
801         result.order(ByteOrder.LITTLE_ENDIAN);
802 
803         // Step 1. Output all extra fields other than the one which is to do with alignment
804         // FORMAT: sequence of fields. Each field consists of:
805         //   * uint16 ID
806         //   * uint16 size
807         //   * 'size' bytes: payload
808         while (original.remaining() >= 4) {
809             short headerId = original.getShort();
810             int dataSize = ZipUtils.getUnsignedInt16(original);
811             if (dataSize > original.remaining()) {
812                 // Malformed field -- insufficient input remaining
813                 break;
814             }
815             if (((headerId == 0) && (dataSize == 0))
816                     || (headerId == ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID)) {
817                 // Ignore the field if it has to do with the old APK data alignment method (filling
818                 // the extra field with 0x00 bytes) or the new APK data alignment method.
819                 original.position(original.position() + dataSize);
820                 continue;
821             }
822             // Copy this field (including header) to the output
823             original.position(original.position() - 4);
824             int originalLimit = original.limit();
825             original.limit(original.position() + 4 + dataSize);
826             result.put(original);
827             original.limit(originalLimit);
828         }
829 
830         // Step 2. Add alignment field
831         // FORMAT:
832         //  * uint16 extra header ID
833         //  * uint16 extra data size
834         //        Payload ('data size' bytes)
835         //      * uint16 alignment multiple (in bytes)
836         //      * remaining bytes -- padding to achieve alignment of data which starts after the
837         //        extra field
838         long dataMinStartOffset =
839                 extraStartOffset
840                         + result.position()
841                         + ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES;
842         int paddingSizeBytes =
843                 (dataAlignmentMultiple - ((int) (dataMinStartOffset % dataAlignmentMultiple)))
844                         % dataAlignmentMultiple;
845         result.putShort(ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID);
846         ZipUtils.putUnsignedInt16(result, 2 + paddingSizeBytes);
847         ZipUtils.putUnsignedInt16(result, dataAlignmentMultiple);
848         result.position(result.position() + paddingSizeBytes);
849         result.flip();
850 
851         return result;
852     }
853 
getZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections)854     private static ByteBuffer getZipCentralDirectory(
855             DataSource apk, ApkUtils.ZipSections apkSections)
856             throws IOException, ApkFormatException {
857         long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
858         if (cdSizeBytes > Integer.MAX_VALUE) {
859             throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes);
860         }
861         long cdOffset = apkSections.getZipCentralDirectoryOffset();
862         ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
863         cd.order(ByteOrder.LITTLE_ENDIAN);
864         return cd;
865     }
866 
parseZipCentralDirectory( ByteBuffer cd, ApkUtils.ZipSections apkSections)867     private static List<CentralDirectoryRecord> parseZipCentralDirectory(
868             ByteBuffer cd, ApkUtils.ZipSections apkSections) throws ApkFormatException {
869         long cdOffset = apkSections.getZipCentralDirectoryOffset();
870         int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
871         List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
872         Set<String> entryNames = new HashSet<>(expectedCdRecordCount);
873         for (int i = 0; i < expectedCdRecordCount; i++) {
874             CentralDirectoryRecord cdRecord;
875             int offsetInsideCd = cd.position();
876             try {
877                 cdRecord = CentralDirectoryRecord.getRecord(cd);
878             } catch (ZipFormatException e) {
879                 throw new ApkFormatException(
880                         "Malformed ZIP Central Directory record #"
881                                 + (i + 1)
882                                 + " at file offset "
883                                 + (cdOffset + offsetInsideCd),
884                         e);
885             }
886             String entryName = cdRecord.getName();
887             if (!entryNames.add(entryName)) {
888                 throw new ApkFormatException(
889                         "Multiple ZIP entries with the same name: " + entryName);
890             }
891             cdRecords.add(cdRecord);
892         }
893         if (cd.hasRemaining()) {
894             throw new ApkFormatException(
895                     "Unused space at the end of ZIP Central Directory: "
896                             + cd.remaining()
897                             + " bytes starting at file offset "
898                             + (cdOffset + cd.position()));
899         }
900 
901         return cdRecords;
902     }
903 
findCdRecord( List<CentralDirectoryRecord> cdRecords, String name)904     private static CentralDirectoryRecord findCdRecord(
905             List<CentralDirectoryRecord> cdRecords, String name) {
906         for (CentralDirectoryRecord cdRecord : cdRecords) {
907             if (name.equals(cdRecord.getName())) {
908                 return cdRecord;
909             }
910         }
911         return null;
912     }
913 
914     /**
915      * Returns the contents of the APK's {@code AndroidManifest.xml} or {@code null} if this entry
916      * is not present in the APK.
917      */
getAndroidManifestFromApk( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)918     static ByteBuffer getAndroidManifestFromApk(
919             List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)
920             throws IOException, ApkFormatException, ZipFormatException {
921         CentralDirectoryRecord androidManifestCdRecord =
922                 findCdRecord(cdRecords, ANDROID_MANIFEST_ZIP_ENTRY_NAME);
923         if (androidManifestCdRecord == null) {
924             throw new ApkFormatException("Missing " + ANDROID_MANIFEST_ZIP_ENTRY_NAME);
925         }
926 
927         return ByteBuffer.wrap(
928                 LocalFileRecord.getUncompressedData(
929                         lhfSection, androidManifestCdRecord, lhfSection.size()));
930     }
931 
932     /**
933      * Return list of pin patterns embedded in the pin pattern asset file. If no such file, return
934      * {@code null}.
935      */
extractPinPatterns( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)936     private static List<Hints.PatternWithRange> extractPinPatterns(
937             List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)
938             throws IOException, ApkFormatException {
939         CentralDirectoryRecord pinListCdRecord =
940                 findCdRecord(cdRecords, Hints.PIN_HINT_ASSET_ZIP_ENTRY_NAME);
941         List<Hints.PatternWithRange> pinPatterns = null;
942         if (pinListCdRecord != null) {
943             pinPatterns = new ArrayList<>();
944             byte[] patternBlob;
945             try {
946                 patternBlob =
947                         LocalFileRecord.getUncompressedData(
948                                 lhfSection, pinListCdRecord, lhfSection.size());
949             } catch (ZipFormatException ex) {
950                 throw new ApkFormatException("Bad " + pinListCdRecord);
951             }
952             pinPatterns = Hints.parsePinPatterns(patternBlob);
953         }
954         return pinPatterns;
955     }
956 
957     /**
958      * Returns the minimum Android version (API Level) supported by the provided APK. This is based
959      * on the {@code android:minSdkVersion} attributes of the APK's {@code AndroidManifest.xml}.
960      */
getMinSdkVersionFromApk( List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)961     private static int getMinSdkVersionFromApk(
962             List<CentralDirectoryRecord> cdRecords, DataSource lhfSection)
963             throws IOException, MinSdkVersionException {
964         ByteBuffer androidManifest;
965         try {
966             androidManifest = getAndroidManifestFromApk(cdRecords, lhfSection);
967         } catch (ZipFormatException | ApkFormatException e) {
968             throw new MinSdkVersionException(
969                     "Failed to determine APK's minimum supported Android platform version", e);
970         }
971         return ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest);
972     }
973 
974     /**
975      * Configuration of a signer.
976      *
977      * <p>Use {@link Builder} to obtain configuration instances.
978      */
979     public static class SignerConfig {
980         private final String mName;
981         private final PrivateKey mPrivateKey;
982         private final List<X509Certificate> mCertificates;
983         private boolean mDeterministicDsaSigning;
984 
SignerConfig( String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)985         private SignerConfig(
986                 String name,
987                 PrivateKey privateKey,
988                 List<X509Certificate> certificates,
989                 boolean deterministicDsaSigning) {
990             mName = name;
991             mPrivateKey = privateKey;
992             mCertificates = Collections.unmodifiableList(new ArrayList<>(certificates));
993             mDeterministicDsaSigning = deterministicDsaSigning;
994         }
995         /** Returns the name of this signer. */
getName()996         public String getName() {
997             return mName;
998         }
999 
1000         /** Returns the signing key of this signer. */
getPrivateKey()1001         public PrivateKey getPrivateKey() {
1002             return mPrivateKey;
1003         }
1004 
1005         /**
1006          * Returns the certificate(s) of this signer. The first certificate's public key corresponds
1007          * to this signer's private key.
1008          */
getCertificates()1009         public List<X509Certificate> getCertificates() {
1010             return mCertificates;
1011         }
1012 
1013 
1014         /**
1015          * If this signer is a DSA signer, whether or not the signing is done deterministically.
1016          */
getDeterministicDsaSigning()1017         public boolean getDeterministicDsaSigning() {
1018             return mDeterministicDsaSigning;
1019         }
1020 
1021         /** Builder of {@link SignerConfig} instances. */
1022         public static class Builder {
1023             private final String mName;
1024             private final PrivateKey mPrivateKey;
1025             private final List<X509Certificate> mCertificates;
1026             private final boolean mDeterministicDsaSigning;
1027 
1028             /**
1029              * Constructs a new {@code Builder}.
1030              *
1031              * @param name signer's name. The name is reflected in the name of files comprising the
1032              *     JAR signature of the APK.
1033              * @param privateKey signing key
1034              * @param certificates list of one or more X.509 certificates. The subject public key of
1035              *     the first certificate must correspond to the {@code privateKey}.
1036              */
Builder( String name, PrivateKey privateKey, List<X509Certificate> certificates)1037             public Builder(
1038                     String name,
1039                     PrivateKey privateKey,
1040                     List<X509Certificate> certificates) {
1041                 this(name, privateKey, certificates, false);
1042             }
1043 
1044             /**
1045              * Constructs a new {@code Builder}.
1046              *
1047              * @param name signer's name. The name is reflected in the name of files comprising the
1048              *     JAR signature of the APK.
1049              * @param privateKey signing key
1050              * @param certificates list of one or more X.509 certificates. The subject public key of
1051              *     the first certificate must correspond to the {@code privateKey}.
1052              * @param deterministicDsaSigning When signing using DSA, whether or not the
1053              *     deterministic variant (RFC6979) should be used.
1054              */
Builder( String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1055             public Builder(
1056                     String name,
1057                     PrivateKey privateKey,
1058                     List<X509Certificate> certificates,
1059                     boolean deterministicDsaSigning) {
1060                 if (name.isEmpty()) {
1061                     throw new IllegalArgumentException("Empty name");
1062                 }
1063                 mName = name;
1064                 mPrivateKey = privateKey;
1065                 mCertificates = new ArrayList<>(certificates);
1066                 mDeterministicDsaSigning = deterministicDsaSigning;
1067             }
1068 
1069             /**
1070              * Returns a new {@code SignerConfig} instance configured based on the configuration of
1071              * this builder.
1072              */
build()1073             public SignerConfig build() {
1074                 return new SignerConfig(mName, mPrivateKey, mCertificates,
1075                         mDeterministicDsaSigning);
1076             }
1077         }
1078     }
1079 
1080     /**
1081      * Builder of {@link ApkSigner} instances.
1082      *
1083      * <p>The builder requires the following information to construct a working {@code ApkSigner}:
1084      *
1085      * <ul>
1086      *   <li>Signer configs or {@link ApkSignerEngine} -- provided in the constructor,
1087      *   <li>APK to be signed -- see {@link #setInputApk(File) setInputApk} variants,
1088      *   <li>where to store the output signed APK -- see {@link #setOutputApk(File) setOutputApk}
1089      *       variants.
1090      * </ul>
1091      */
1092     public static class Builder {
1093         private final List<SignerConfig> mSignerConfigs;
1094         private SignerConfig mSourceStampSignerConfig;
1095         private SigningCertificateLineage mSourceStampSigningCertificateLineage;
1096         private boolean mForceSourceStampOverwrite = false;
1097         private boolean mV1SigningEnabled = true;
1098         private boolean mV2SigningEnabled = true;
1099         private boolean mV3SigningEnabled = true;
1100         private boolean mV4SigningEnabled = true;
1101         private boolean mVerityEnabled = false;
1102         private boolean mV4ErrorReportingEnabled = false;
1103         private boolean mDebuggableApkPermitted = true;
1104         private boolean mOtherSignersSignaturesPreserved;
1105         private String mCreatedBy;
1106         private Integer mMinSdkVersion;
1107         private int mRotationMinSdkVersion = V3SchemeConstants.DEFAULT_ROTATION_MIN_SDK_VERSION;
1108         private boolean mRotationTargetsDevRelease = false;
1109 
1110         private final ApkSignerEngine mSignerEngine;
1111 
1112         private File mInputApkFile;
1113         private DataSource mInputApkDataSource;
1114 
1115         private File mOutputApkFile;
1116         private DataSink mOutputApkDataSink;
1117         private DataSource mOutputApkDataSource;
1118 
1119         private File mOutputV4File;
1120 
1121         private SigningCertificateLineage mSigningCertificateLineage;
1122 
1123         // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3
1124         // signing by default, but not require prior clients to update to explicitly disable v3
1125         // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided
1126         // inputs (multiple signers and mSigningCertificateLineage in particular).  Maintain two
1127         // extra variables to record whether or not mV3SigningEnabled has been set directly by a
1128         // client and so should override the default behavior.
1129         private boolean mV3SigningExplicitlyDisabled = false;
1130         private boolean mV3SigningExplicitlyEnabled = false;
1131 
1132         /**
1133          * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided
1134          * signer configurations. The resulting signer may be further customized through this
1135          * builder's setters, such as {@link #setMinSdkVersion(int)}, {@link
1136          * #setV1SigningEnabled(boolean)}, {@link #setV2SigningEnabled(boolean)}, {@link
1137          * #setOtherSignersSignaturesPreserved(boolean)}, {@link #setCreatedBy(String)}.
1138          *
1139          * <p>{@link #Builder(ApkSignerEngine)} is an alternative for advanced use cases where more
1140          * control over low-level details of signing is desired.
1141          */
Builder(List<SignerConfig> signerConfigs)1142         public Builder(List<SignerConfig> signerConfigs) {
1143             if (signerConfigs.isEmpty()) {
1144                 throw new IllegalArgumentException("At least one signer config must be provided");
1145             }
1146             if (signerConfigs.size() > 1) {
1147                 // APK Signature Scheme v3 only supports single signer, unless a
1148                 // SigningCertificateLineage is provided, in which case this will be reset to true,
1149                 // since we don't yet have a v4 scheme about which to worry
1150                 mV3SigningEnabled = false;
1151             }
1152             mSignerConfigs = new ArrayList<>(signerConfigs);
1153             mSignerEngine = null;
1154         }
1155 
1156         /**
1157          * Constructs a new {@code Builder} for an {@code ApkSigner} which signs using the provided
1158          * signing engine. This is meant for advanced use cases where more control is needed over
1159          * the lower-level details of signing. For typical use cases, {@link #Builder(List)} is more
1160          * appropriate.
1161          */
Builder(ApkSignerEngine signerEngine)1162         public Builder(ApkSignerEngine signerEngine) {
1163             if (signerEngine == null) {
1164                 throw new NullPointerException("signerEngine == null");
1165             }
1166             mSignerEngine = signerEngine;
1167             mSignerConfigs = null;
1168         }
1169 
1170         /** Sets the signing configuration of the source stamp to be embedded in the APK. */
setSourceStampSignerConfig(SignerConfig sourceStampSignerConfig)1171         public Builder setSourceStampSignerConfig(SignerConfig sourceStampSignerConfig) {
1172             mSourceStampSignerConfig = sourceStampSignerConfig;
1173             return this;
1174         }
1175 
1176         /**
1177          * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of
1178          * signing certificate rotation for certificates previously used to sign source stamps.
1179          */
setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage)1180         public Builder setSourceStampSigningCertificateLineage(
1181                 SigningCertificateLineage sourceStampSigningCertificateLineage) {
1182             mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage;
1183             return this;
1184         }
1185 
1186         /**
1187          * Sets whether the APK should overwrite existing source stamp, if found.
1188          *
1189          * @param force {@code true} to require the APK to be overwrite existing source stamp
1190          */
setForceSourceStampOverwrite(boolean force)1191         public Builder setForceSourceStampOverwrite(boolean force) {
1192             mForceSourceStampOverwrite = force;
1193             return this;
1194         }
1195 
1196         /**
1197          * Sets the APK to be signed.
1198          *
1199          * @see #setInputApk(DataSource)
1200          */
setInputApk(File inputApk)1201         public Builder setInputApk(File inputApk) {
1202             if (inputApk == null) {
1203                 throw new NullPointerException("inputApk == null");
1204             }
1205             mInputApkFile = inputApk;
1206             mInputApkDataSource = null;
1207             return this;
1208         }
1209 
1210         /**
1211          * Sets the APK to be signed.
1212          *
1213          * @see #setInputApk(File)
1214          */
setInputApk(DataSource inputApk)1215         public Builder setInputApk(DataSource inputApk) {
1216             if (inputApk == null) {
1217                 throw new NullPointerException("inputApk == null");
1218             }
1219             mInputApkDataSource = inputApk;
1220             mInputApkFile = null;
1221             return this;
1222         }
1223 
1224         /**
1225          * Sets the location of the output (signed) APK. {@code ApkSigner} will create this file if
1226          * it doesn't exist.
1227          *
1228          * @see #setOutputApk(ReadableDataSink)
1229          * @see #setOutputApk(DataSink, DataSource)
1230          */
setOutputApk(File outputApk)1231         public Builder setOutputApk(File outputApk) {
1232             if (outputApk == null) {
1233                 throw new NullPointerException("outputApk == null");
1234             }
1235             mOutputApkFile = outputApk;
1236             mOutputApkDataSink = null;
1237             mOutputApkDataSource = null;
1238             return this;
1239         }
1240 
1241         /**
1242          * Sets the readable data sink which will receive the output (signed) APK. After signing,
1243          * the contents of the output APK will be available via the {@link DataSource} interface of
1244          * the sink.
1245          *
1246          * <p>This variant of {@code setOutputApk} is useful for avoiding writing the output APK to
1247          * a file. For example, an in-memory data sink, such as {@link
1248          * DataSinks#newInMemoryDataSink()}, could be used instead of a file.
1249          *
1250          * @see #setOutputApk(File)
1251          * @see #setOutputApk(DataSink, DataSource)
1252          */
setOutputApk(ReadableDataSink outputApk)1253         public Builder setOutputApk(ReadableDataSink outputApk) {
1254             if (outputApk == null) {
1255                 throw new NullPointerException("outputApk == null");
1256             }
1257             return setOutputApk(outputApk, outputApk);
1258         }
1259 
1260         /**
1261          * Sets the sink which will receive the output (signed) APK. Data received by the {@code
1262          * outputApkOut} sink must be visible through the {@code outputApkIn} data source.
1263          *
1264          * <p>This is an advanced variant of {@link #setOutputApk(ReadableDataSink)}, enabling the
1265          * sink and the source to be different objects.
1266          *
1267          * @see #setOutputApk(ReadableDataSink)
1268          * @see #setOutputApk(File)
1269          */
setOutputApk(DataSink outputApkOut, DataSource outputApkIn)1270         public Builder setOutputApk(DataSink outputApkOut, DataSource outputApkIn) {
1271             if (outputApkOut == null) {
1272                 throw new NullPointerException("outputApkOut == null");
1273             }
1274             if (outputApkIn == null) {
1275                 throw new NullPointerException("outputApkIn == null");
1276             }
1277             mOutputApkFile = null;
1278             mOutputApkDataSink = outputApkOut;
1279             mOutputApkDataSource = outputApkIn;
1280             return this;
1281         }
1282 
1283         /**
1284          * Sets the location of the V4 output file. {@code ApkSigner} will create this file if it
1285          * doesn't exist.
1286          */
setV4SignatureOutputFile(File v4SignatureOutputFile)1287         public Builder setV4SignatureOutputFile(File v4SignatureOutputFile) {
1288             if (v4SignatureOutputFile == null) {
1289                 throw new NullPointerException("v4HashRootOutputFile == null");
1290             }
1291             mOutputV4File = v4SignatureOutputFile;
1292             return this;
1293         }
1294 
1295         /**
1296          * Sets the minimum Android platform version (API Level) on which APK signatures produced by
1297          * the signer being built must verify. This method is useful for overriding the default
1298          * behavior where the minimum API Level is obtained from the {@code android:minSdkVersion}
1299          * attribute of the APK's {@code AndroidManifest.xml}.
1300          *
1301          * <p><em>Note:</em> This method may result in APK signatures which don't verify on some
1302          * Android platform versions supported by the APK.
1303          *
1304          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1305          * with an {@link ApkSignerEngine}.
1306          *
1307          * @throws IllegalStateException if this builder was initialized with an {@link
1308          *     ApkSignerEngine}
1309          */
setMinSdkVersion(int minSdkVersion)1310         public Builder setMinSdkVersion(int minSdkVersion) {
1311             checkInitializedWithoutEngine();
1312             mMinSdkVersion = minSdkVersion;
1313             return this;
1314         }
1315 
1316         /**
1317          * Sets the minimum Android platform version (API Level) for which an APK's rotated signing
1318          * key should be used to produce the APK's signature. The original signing key for the APK
1319          * will be used for all previous platform versions. If a rotated key with signing lineage is
1320          * not provided then this method is a noop. This method is useful for overriding the
1321          * default behavior where Android T is set as the minimum API level for rotation.
1322          *
1323          * <p><em>Note:</em>Specifying a {@code minSdkVersion} value <= 32 (Android Sv2) will result
1324          * in the original V3 signing block being used without platform targeting.
1325          *
1326          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1327          * with an {@link ApkSignerEngine}.
1328          *
1329          * @throws IllegalStateException if this builder was initialized with an {@link
1330          *     ApkSignerEngine}
1331          */
setMinSdkVersionForRotation(int minSdkVersion)1332         public Builder setMinSdkVersionForRotation(int minSdkVersion) {
1333             checkInitializedWithoutEngine();
1334             // If the provided SDK version does not support v3.1, then use the default SDK version
1335             // with rotation support.
1336             if (minSdkVersion < MIN_SDK_WITH_V31_SUPPORT) {
1337                 mRotationMinSdkVersion = MIN_SDK_WITH_V3_SUPPORT;
1338             } else {
1339                 mRotationMinSdkVersion = minSdkVersion;
1340             }
1341             return this;
1342         }
1343 
1344         /**
1345          * Sets whether the rotation-min-sdk-version is intended to target a development release;
1346          * this is primarily required after the T SDK is finalized, and an APK needs to target U
1347          * during its development cycle for rotation.
1348          *
1349          * <p>This is only required after the T SDK is finalized since S and earlier releases do
1350          * not know about the V3.1 block ID, but once T is released and work begins on U, U will
1351          * use the SDK version of T during development. Specifying a rotation-min-sdk-version of T's
1352          * SDK version along with setting {@code enabled} to true will allow an APK to use the
1353          * rotated key on a device running U while causing this to be bypassed for T.
1354          *
1355          * <p><em>Note:</em>If the rotation-min-sdk-version is less than or equal to 32 (Android
1356          * Sv2), then the rotated signing key will be used in the v3.0 signing block and this call
1357          * will be a noop.
1358          *
1359          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1360          * with an {@link ApkSignerEngine}.
1361          */
setRotationTargetsDevRelease(boolean enabled)1362         public Builder setRotationTargetsDevRelease(boolean enabled) {
1363             checkInitializedWithoutEngine();
1364             mRotationTargetsDevRelease = enabled;
1365             return this;
1366         }
1367 
1368         /**
1369          * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme).
1370          *
1371          * <p>By default, whether APK is signed using JAR signing is determined by {@code
1372          * ApkSigner}, based on the platform versions supported by the APK or specified using {@link
1373          * #setMinSdkVersion(int)}. Disabling JAR signing will result in APK signatures which don't
1374          * verify on Android Marshmallow (Android 6.0, API Level 23) and lower.
1375          *
1376          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1377          * with an {@link ApkSignerEngine}.
1378          *
1379          * @param enabled {@code true} to require the APK to be signed using JAR signing, {@code
1380          *     false} to require the APK to not be signed using JAR signing.
1381          * @throws IllegalStateException if this builder was initialized with an {@link
1382          *     ApkSignerEngine}
1383          * @see <a
1384          *     href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">JAR
1385          *     signing</a>
1386          */
setV1SigningEnabled(boolean enabled)1387         public Builder setV1SigningEnabled(boolean enabled) {
1388             checkInitializedWithoutEngine();
1389             mV1SigningEnabled = enabled;
1390             return this;
1391         }
1392 
1393         /**
1394          * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature
1395          * scheme).
1396          *
1397          * <p>By default, whether APK is signed using APK Signature Scheme v2 is determined by
1398          * {@code ApkSigner} based on the platform versions supported by the APK or specified using
1399          * {@link #setMinSdkVersion(int)}.
1400          *
1401          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1402          * with an {@link ApkSignerEngine}.
1403          *
1404          * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme
1405          *     v2, {@code false} to require the APK to not be signed using APK Signature Scheme v2.
1406          * @throws IllegalStateException if this builder was initialized with an {@link
1407          *     ApkSignerEngine}
1408          * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature
1409          *     Scheme v2</a>
1410          */
setV2SigningEnabled(boolean enabled)1411         public Builder setV2SigningEnabled(boolean enabled) {
1412             checkInitializedWithoutEngine();
1413             mV2SigningEnabled = enabled;
1414             return this;
1415         }
1416 
1417         /**
1418          * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature
1419          * scheme).
1420          *
1421          * <p>By default, whether APK is signed using APK Signature Scheme v3 is determined by
1422          * {@code ApkSigner} based on the platform versions supported by the APK or specified using
1423          * {@link #setMinSdkVersion(int)}.
1424          *
1425          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1426          * with an {@link ApkSignerEngine}.
1427          *
1428          * <p><em>Note:</em> APK Signature Scheme v3 only supports a single signing certificate, but
1429          * may take multiple signers mapping to different targeted platform versions.
1430          *
1431          * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme
1432          *     v3, {@code false} to require the APK to not be signed using APK Signature Scheme v3.
1433          * @throws IllegalStateException if this builder was initialized with an {@link
1434          *     ApkSignerEngine}
1435          */
setV3SigningEnabled(boolean enabled)1436         public Builder setV3SigningEnabled(boolean enabled) {
1437             checkInitializedWithoutEngine();
1438             mV3SigningEnabled = enabled;
1439             if (enabled) {
1440                 mV3SigningExplicitlyEnabled = true;
1441             } else {
1442                 mV3SigningExplicitlyDisabled = true;
1443             }
1444             return this;
1445         }
1446 
1447         /**
1448          * Sets whether the APK should be signed using APK Signature Scheme v4.
1449          *
1450          * <p>V4 signing requires that the APK be v2 or v3 signed.
1451          *
1452          * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme v2
1453          *     or v3 and generate an v4 signature file
1454          */
setV4SigningEnabled(boolean enabled)1455         public Builder setV4SigningEnabled(boolean enabled) {
1456             checkInitializedWithoutEngine();
1457             mV4SigningEnabled = enabled;
1458             mV4ErrorReportingEnabled = enabled;
1459             return this;
1460         }
1461 
1462         /**
1463          * Sets whether errors during v4 signing should be reported and halt the signing process.
1464          *
1465          * <p>Error reporting for v4 signing is disabled by default, but will be enabled if the
1466          * caller invokes {@link #setV4SigningEnabled} with a value of true. This method is useful
1467          * for tools that enable v4 signing by default but don't want to fail the signing process if
1468          * the user did not explicitly request the v4 signing.
1469          *
1470          * @param enabled {@code false} to prevent errors encountered during the V4 signing from
1471          *     halting the signing process
1472          */
setV4ErrorReportingEnabled(boolean enabled)1473         public Builder setV4ErrorReportingEnabled(boolean enabled) {
1474             checkInitializedWithoutEngine();
1475             mV4ErrorReportingEnabled = enabled;
1476             return this;
1477         }
1478 
1479         /**
1480          * Sets whether to enable the verity signature algorithm for the v2 and v3 signature
1481          * schemes.
1482          *
1483          * @param enabled {@code true} to enable the verity signature algorithm for inclusion in the
1484          *     v2 and v3 signature blocks.
1485          */
setVerityEnabled(boolean enabled)1486         public Builder setVerityEnabled(boolean enabled) {
1487             checkInitializedWithoutEngine();
1488             mVerityEnabled = enabled;
1489             return this;
1490         }
1491 
1492         /**
1493          * Sets whether the APK should be signed even if it is marked as debuggable ({@code
1494          * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward
1495          * compatibility reasons, the default value of this setting is {@code true}.
1496          *
1497          * <p>It is dangerous to sign debuggable APKs with production/release keys because Android
1498          * platform loosens security checks for such APKs. For example, arbitrary unauthorized code
1499          * may be executed in the context of such an app by anybody with ADB shell access.
1500          *
1501          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1502          * with an {@link ApkSignerEngine}.
1503          */
setDebuggableApkPermitted(boolean permitted)1504         public Builder setDebuggableApkPermitted(boolean permitted) {
1505             checkInitializedWithoutEngine();
1506             mDebuggableApkPermitted = permitted;
1507             return this;
1508         }
1509 
1510         /**
1511          * Sets whether signatures produced by signers other than the ones configured in this engine
1512          * should be copied from the input APK to the output APK.
1513          *
1514          * <p>By default, signatures of other signers are omitted from the output APK.
1515          *
1516          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1517          * with an {@link ApkSignerEngine}.
1518          *
1519          * @throws IllegalStateException if this builder was initialized with an {@link
1520          *     ApkSignerEngine}
1521          */
setOtherSignersSignaturesPreserved(boolean preserved)1522         public Builder setOtherSignersSignaturesPreserved(boolean preserved) {
1523             checkInitializedWithoutEngine();
1524             mOtherSignersSignaturesPreserved = preserved;
1525             return this;
1526         }
1527 
1528         /**
1529          * Sets the value of the {@code Created-By} field in JAR signature files.
1530          *
1531          * <p><em>Note:</em> This method may only be invoked when this builder is not initialized
1532          * with an {@link ApkSignerEngine}.
1533          *
1534          * @throws IllegalStateException if this builder was initialized with an {@link
1535          *     ApkSignerEngine}
1536          */
setCreatedBy(String createdBy)1537         public Builder setCreatedBy(String createdBy) {
1538             checkInitializedWithoutEngine();
1539             if (createdBy == null) {
1540                 throw new NullPointerException();
1541             }
1542             mCreatedBy = createdBy;
1543             return this;
1544         }
1545 
checkInitializedWithoutEngine()1546         private void checkInitializedWithoutEngine() {
1547             if (mSignerEngine != null) {
1548                 throw new IllegalStateException(
1549                         "Operation is not available when builder initialized with an engine");
1550             }
1551         }
1552 
1553         /**
1554          * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This
1555          * structure provides proof of signing certificate rotation linking {@link SignerConfig}
1556          * objects to previous ones.
1557          */
setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage)1558         public Builder setSigningCertificateLineage(
1559                 SigningCertificateLineage signingCertificateLineage) {
1560             if (signingCertificateLineage != null) {
1561                 mV3SigningEnabled = true;
1562                 mSigningCertificateLineage = signingCertificateLineage;
1563             }
1564             return this;
1565         }
1566 
1567         /**
1568          * Returns a new {@code ApkSigner} instance initialized according to the configuration of
1569          * this builder.
1570          */
build()1571         public ApkSigner build() {
1572             if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) {
1573                 throw new IllegalStateException(
1574                         "Builder configured to both enable and disable APK "
1575                                 + "Signature Scheme v3 signing");
1576             }
1577 
1578             if (mV3SigningExplicitlyDisabled) {
1579                 mV3SigningEnabled = false;
1580             }
1581 
1582             if (mV3SigningExplicitlyEnabled) {
1583                 mV3SigningEnabled = true;
1584             }
1585 
1586             // If V4 signing is not explicitly set, and V2/V3 signing is disabled, then V4 signing
1587             // must be disabled as well as it is dependent on V2/V3.
1588             if (mV4SigningEnabled && !mV2SigningEnabled && !mV3SigningEnabled) {
1589                 if (!mV4ErrorReportingEnabled) {
1590                     mV4SigningEnabled = false;
1591                 } else {
1592                     throw new IllegalStateException(
1593                             "APK Signature Scheme v4 signing requires at least "
1594                                     + "v2 or v3 signing to be enabled");
1595                 }
1596             }
1597 
1598             // TODO - if v3 signing is enabled, check provided signers and history to see if valid
1599 
1600             return new ApkSigner(
1601                     mSignerConfigs,
1602                     mSourceStampSignerConfig,
1603                     mSourceStampSigningCertificateLineage,
1604                     mForceSourceStampOverwrite,
1605                     mMinSdkVersion,
1606                     mRotationMinSdkVersion,
1607                     mRotationTargetsDevRelease,
1608                     mV1SigningEnabled,
1609                     mV2SigningEnabled,
1610                     mV3SigningEnabled,
1611                     mV4SigningEnabled,
1612                     mVerityEnabled,
1613                     mV4ErrorReportingEnabled,
1614                     mDebuggableApkPermitted,
1615                     mOtherSignersSignaturesPreserved,
1616                     mCreatedBy,
1617                     mSignerEngine,
1618                     mInputApkFile,
1619                     mInputApkDataSource,
1620                     mOutputApkFile,
1621                     mOutputApkDataSink,
1622                     mOutputApkDataSource,
1623                     mOutputV4File,
1624                     mSigningCertificateLineage);
1625         }
1626     }
1627 }
1628