• 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.apk.ApkUtils.computeSha256DigestBytes;
21 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERITY_PADDING_BLOCK_ID;
22 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2;
23 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3;
24 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME;
25 
26 import com.android.apksig.apk.ApkFormatException;
27 import com.android.apksig.apk.ApkUtils;
28 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
29 import com.android.apksig.internal.apk.ContentDigestAlgorithm;
30 import com.android.apksig.internal.apk.SignatureAlgorithm;
31 import com.android.apksig.internal.apk.stamp.V2SourceStampSigner;
32 import com.android.apksig.internal.apk.v1.DigestAlgorithm;
33 import com.android.apksig.internal.apk.v1.V1SchemeConstants;
34 import com.android.apksig.internal.apk.v1.V1SchemeSigner;
35 import com.android.apksig.internal.apk.v1.V1SchemeVerifier;
36 import com.android.apksig.internal.apk.v2.V2SchemeSigner;
37 import com.android.apksig.internal.apk.v3.V3SchemeSigner;
38 import com.android.apksig.internal.apk.v4.V4SchemeSigner;
39 import com.android.apksig.internal.apk.v4.V4Signature;
40 import com.android.apksig.internal.jar.ManifestParser;
41 import com.android.apksig.internal.util.AndroidSdkVersion;
42 import com.android.apksig.internal.util.Pair;
43 import com.android.apksig.internal.util.TeeDataSink;
44 import com.android.apksig.util.DataSink;
45 import com.android.apksig.util.DataSinks;
46 import com.android.apksig.util.DataSource;
47 import com.android.apksig.util.RunnablesExecutor;
48 
49 import java.io.ByteArrayOutputStream;
50 import java.io.File;
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.nio.ByteBuffer;
54 import java.security.InvalidKeyException;
55 import java.security.MessageDigest;
56 import java.security.NoSuchAlgorithmException;
57 import java.security.PrivateKey;
58 import java.security.PublicKey;
59 import java.security.SignatureException;
60 import java.security.cert.CertificateEncodingException;
61 import java.security.cert.CertificateException;
62 import java.security.cert.X509Certificate;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Collection;
66 import java.util.Collections;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Set;
72 
73 /**
74  * Default implementation of {@link ApkSignerEngine}.
75  *
76  * <p>Use {@link Builder} to obtain instances of this engine.
77  */
78 public class DefaultApkSignerEngine implements ApkSignerEngine {
79 
80     // IMPLEMENTATION NOTE: This engine generates a signed APK as follows:
81     // 1. The engine asks its client to output input JAR entries which are not part of JAR
82     //    signature.
83     // 2. If JAR signing (v1 signing) is enabled, the engine inspects the output JAR entries to
84     //    compute their digests, to be placed into output META-INF/MANIFEST.MF. It also inspects
85     //    the contents of input and output META-INF/MANIFEST.MF to borrow the main section of the
86     //    file. It does not care about individual (i.e., JAR entry-specific) sections. It then
87     //    emits the v1 signature (a set of JAR entries) and asks the client to output them.
88     // 3. If APK Signature Scheme v2 (v2 signing) is enabled, the engine emits an APK Signing Block
89     //    from outputZipSections() and asks its client to insert this block into the output.
90     // 4. If APK Signature Scheme v3 (v3 signing) is enabled, the engine includes it in the APK
91     //    Signing BLock output from outputZipSections() and asks its client to insert this block
92     //    into the output.  If both v2 and v3 signing is enabled, they are both added to the APK
93     //    Signing Block before asking the client to insert it into the output.
94 
95     private final boolean mV1SigningEnabled;
96     private final boolean mV2SigningEnabled;
97     private final boolean mV3SigningEnabled;
98     private final boolean mVerityEnabled;
99     private final boolean mDebuggableApkPermitted;
100     private final boolean mOtherSignersSignaturesPreserved;
101     private final String mCreatedBy;
102     private final List<SignerConfig> mSignerConfigs;
103     private final SignerConfig mSourceStampSignerConfig;
104     private final SigningCertificateLineage mSourceStampSigningCertificateLineage;
105     private final int mMinSdkVersion;
106     private final SigningCertificateLineage mSigningCertificateLineage;
107 
108     private List<byte[]> mPreservedV2Signers = Collections.emptyList();
109     private List<Pair<byte[], Integer>> mPreservedSignatureBlocks = Collections.emptyList();
110 
111     private List<V1SchemeSigner.SignerConfig> mV1SignerConfigs = Collections.emptyList();
112     private DigestAlgorithm mV1ContentDigestAlgorithm;
113 
114     private boolean mClosed;
115 
116     private boolean mV1SignaturePending;
117 
118     /** Names of JAR entries which this engine is expected to output as part of v1 signing. */
119     private Set<String> mSignatureExpectedOutputJarEntryNames = Collections.emptySet();
120 
121     /** Requests for digests of output JAR entries. */
122     private final Map<String, GetJarEntryDataDigestRequest> mOutputJarEntryDigestRequests =
123             new HashMap<>();
124 
125     /** Digests of output JAR entries. */
126     private final Map<String, byte[]> mOutputJarEntryDigests = new HashMap<>();
127 
128     /** Data of JAR entries emitted by this engine as v1 signature. */
129     private final Map<String, byte[]> mEmittedSignatureJarEntryData = new HashMap<>();
130 
131     /** Requests for data of output JAR entries which comprise the v1 signature. */
132     private final Map<String, GetJarEntryDataRequest> mOutputSignatureJarEntryDataRequests =
133             new HashMap<>();
134     /**
135      * Request to obtain the data of MANIFEST.MF or {@code null} if the request hasn't been issued.
136      */
137     private GetJarEntryDataRequest mInputJarManifestEntryDataRequest;
138 
139     /**
140      * Request to obtain the data of AndroidManifest.xml or {@code null} if the request hasn't been
141      * issued.
142      */
143     private GetJarEntryDataRequest mOutputAndroidManifestEntryDataRequest;
144 
145     /**
146      * Whether the package being signed is marked as {@code android:debuggable} or {@code null} if
147      * this is not yet known.
148      */
149     private Boolean mDebuggable;
150 
151     /**
152      * Request to output the emitted v1 signature or {@code null} if the request hasn't been issued.
153      */
154     private OutputJarSignatureRequestImpl mAddV1SignatureRequest;
155 
156     private boolean mV2SignaturePending;
157     private boolean mV3SignaturePending;
158 
159     /**
160      * Request to output the emitted v2 and/or v3 signature(s) {@code null} if the request hasn't
161      * been issued.
162      */
163     private OutputApkSigningBlockRequestImpl mAddSigningBlockRequest;
164 
165     private RunnablesExecutor mExecutor = RunnablesExecutor.MULTI_THREADED;
166 
167     /**
168      * A Set of block IDs to be discarded when requesting to preserve the original signatures.
169      */
170     private static final Set<Integer> DISCARDED_SIGNATURE_BLOCK_IDS;
171     static {
172         DISCARDED_SIGNATURE_BLOCK_IDS = new HashSet<>(3);
173         // The verity padding block is recomputed on an
174         // ApkSigningBlockUtils.ANDROID_COMMON_PAGE_ALIGNMENT_BYTES boundary.
175         DISCARDED_SIGNATURE_BLOCK_IDS.add(VERITY_PADDING_BLOCK_ID);
176         // The source stamp block is not currently preserved; appending a new signature scheme
177         // block will invalidate the previous source stamp.
178         DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V1_SOURCE_STAMP_BLOCK_ID);
179         DISCARDED_SIGNATURE_BLOCK_IDS.add(Constants.V2_SOURCE_STAMP_BLOCK_ID);
180     }
181 
DefaultApkSignerEngine( List<SignerConfig> signerConfigs, SignerConfig sourceStampSignerConfig, SigningCertificateLineage sourceStampSigningCertificateLineage, int minSdkVersion, boolean v1SigningEnabled, boolean v2SigningEnabled, boolean v3SigningEnabled, boolean verityEnabled, boolean debuggableApkPermitted, boolean otherSignersSignaturesPreserved, String createdBy, SigningCertificateLineage signingCertificateLineage)182     private DefaultApkSignerEngine(
183             List<SignerConfig> signerConfigs,
184             SignerConfig sourceStampSignerConfig,
185             SigningCertificateLineage sourceStampSigningCertificateLineage,
186             int minSdkVersion,
187             boolean v1SigningEnabled,
188             boolean v2SigningEnabled,
189             boolean v3SigningEnabled,
190             boolean verityEnabled,
191             boolean debuggableApkPermitted,
192             boolean otherSignersSignaturesPreserved,
193             String createdBy,
194             SigningCertificateLineage signingCertificateLineage)
195             throws InvalidKeyException {
196         if (signerConfigs.isEmpty()) {
197             throw new IllegalArgumentException("At least one signer config must be provided");
198         }
199 
200         mV1SigningEnabled = v1SigningEnabled;
201         mV2SigningEnabled = v2SigningEnabled;
202         mV3SigningEnabled = v3SigningEnabled;
203         mVerityEnabled = verityEnabled;
204         mV1SignaturePending = v1SigningEnabled;
205         mV2SignaturePending = v2SigningEnabled;
206         mV3SignaturePending = v3SigningEnabled;
207         mDebuggableApkPermitted = debuggableApkPermitted;
208         mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved;
209         mCreatedBy = createdBy;
210         mSignerConfigs = signerConfigs;
211         mSourceStampSignerConfig = sourceStampSignerConfig;
212         mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage;
213         mMinSdkVersion = minSdkVersion;
214         mSigningCertificateLineage = signingCertificateLineage;
215 
216         if (v1SigningEnabled) {
217             if (v3SigningEnabled) {
218 
219                 // v3 signing only supports single signers, of which the oldest (first) will be the
220                 // one to use for v1 and v2 signing
221                 SignerConfig oldestConfig = signerConfigs.get(0);
222 
223                 // in the event of signing certificate changes, make sure we have the oldest in the
224                 // signing history to sign with v1
225                 if (signingCertificateLineage != null) {
226                     SigningCertificateLineage subLineage =
227                             signingCertificateLineage.getSubLineage(
228                                     oldestConfig.mCertificates.get(0));
229                     if (subLineage.size() != 1) {
230                         throw new IllegalArgumentException(
231                                 "v1 signing enabled but the oldest signer in the"
232                                     + " SigningCertificateLineage is missing.  Please provide the"
233                                     + " oldest signer to enable v1 signing");
234                     }
235                 }
236                 createV1SignerConfigs(Collections.singletonList(oldestConfig), minSdkVersion);
237             } else {
238                 createV1SignerConfigs(signerConfigs, minSdkVersion);
239             }
240         }
241     }
242 
createV1SignerConfigs(List<SignerConfig> signerConfigs, int minSdkVersion)243     private void createV1SignerConfigs(List<SignerConfig> signerConfigs, int minSdkVersion)
244             throws InvalidKeyException {
245         mV1SignerConfigs = new ArrayList<>(signerConfigs.size());
246         Map<String, Integer> v1SignerNameToSignerIndex = new HashMap<>(signerConfigs.size());
247         DigestAlgorithm v1ContentDigestAlgorithm = null;
248         for (int i = 0; i < signerConfigs.size(); i++) {
249             SignerConfig signerConfig = signerConfigs.get(i);
250             List<X509Certificate> certificates = signerConfig.getCertificates();
251             PublicKey publicKey = certificates.get(0).getPublicKey();
252 
253             String v1SignerName = V1SchemeSigner.getSafeSignerName(signerConfig.getName());
254             // Check whether the signer's name is unique among all v1 signers
255             Integer indexOfOtherSignerWithSameName = v1SignerNameToSignerIndex.put(v1SignerName, i);
256             if (indexOfOtherSignerWithSameName != null) {
257                 throw new IllegalArgumentException(
258                         "Signers #"
259                                 + (indexOfOtherSignerWithSameName + 1)
260                                 + " and #"
261                                 + (i + 1)
262                                 + " have the same name: "
263                                 + v1SignerName
264                                 + ". v1 signer names must be unique");
265             }
266 
267             DigestAlgorithm v1SignatureDigestAlgorithm =
268                     V1SchemeSigner.getSuggestedSignatureDigestAlgorithm(publicKey, minSdkVersion);
269             V1SchemeSigner.SignerConfig v1SignerConfig = new V1SchemeSigner.SignerConfig();
270             v1SignerConfig.name = v1SignerName;
271             v1SignerConfig.privateKey = signerConfig.getPrivateKey();
272             v1SignerConfig.certificates = certificates;
273             v1SignerConfig.signatureDigestAlgorithm = v1SignatureDigestAlgorithm;
274             v1SignerConfig.deterministicDsaSigning = signerConfig.getDeterministicDsaSigning();
275             // For digesting contents of APK entries and of MANIFEST.MF, pick the algorithm
276             // of comparable strength to the digest algorithm used for computing the signature.
277             // When there are multiple signers, pick the strongest digest algorithm out of their
278             // signature digest algorithms. This avoids reducing the digest strength used by any
279             // of the signers to protect APK contents.
280             if (v1ContentDigestAlgorithm == null) {
281                 v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm;
282             } else {
283                 if (DigestAlgorithm.BY_STRENGTH_COMPARATOR.compare(
284                                 v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm)
285                         > 0) {
286                     v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm;
287                 }
288             }
289             mV1SignerConfigs.add(v1SignerConfig);
290         }
291         mV1ContentDigestAlgorithm = v1ContentDigestAlgorithm;
292         mSignatureExpectedOutputJarEntryNames =
293                 V1SchemeSigner.getOutputEntryNames(mV1SignerConfigs);
294     }
295 
createV2SignerConfigs( boolean apkSigningBlockPaddingSupported)296     private List<ApkSigningBlockUtils.SignerConfig> createV2SignerConfigs(
297             boolean apkSigningBlockPaddingSupported) throws InvalidKeyException {
298         if (mV3SigningEnabled) {
299 
300             // v3 signing only supports single signers, of which the oldest (first) will be the one
301             // to use for v1 and v2 signing
302             List<ApkSigningBlockUtils.SignerConfig> signerConfig = new ArrayList<>();
303 
304             SignerConfig oldestConfig = mSignerConfigs.get(0);
305 
306             // first make sure that if we have signing certificate history that the oldest signer
307             // corresponds to the oldest ancestor
308             if (mSigningCertificateLineage != null) {
309                 SigningCertificateLineage subLineage =
310                         mSigningCertificateLineage.getSubLineage(oldestConfig.mCertificates.get(0));
311                 if (subLineage.size() != 1) {
312                     throw new IllegalArgumentException(
313                             "v2 signing enabled but the oldest signer in"
314                                     + " the SigningCertificateLineage is missing.  Please provide"
315                                     + " the oldest signer to enable v2 signing.");
316                 }
317             }
318             signerConfig.add(
319                     createSigningBlockSignerConfig(
320                             mSignerConfigs.get(0),
321                             apkSigningBlockPaddingSupported,
322                             ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2));
323             return signerConfig;
324         } else {
325             return createSigningBlockSignerConfigs(
326                     apkSigningBlockPaddingSupported,
327                     ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
328         }
329     }
330 
processV3Configs( List<ApkSigningBlockUtils.SignerConfig> rawConfigs)331     private List<ApkSigningBlockUtils.SignerConfig> processV3Configs(
332             List<ApkSigningBlockUtils.SignerConfig> rawConfigs) throws InvalidKeyException {
333         List<ApkSigningBlockUtils.SignerConfig> processedConfigs = new ArrayList<>();
334 
335         // we have our configs, now touch them up to appropriately cover all SDK levels since APK
336         // signature scheme v3 was introduced
337         int currentMinSdk = Integer.MAX_VALUE;
338         for (int i = rawConfigs.size() - 1; i >= 0; i--) {
339             ApkSigningBlockUtils.SignerConfig config = rawConfigs.get(i);
340             if (config.signatureAlgorithms == null) {
341                 // no valid algorithm was found for this signer, and we haven't yet covered all
342                 // platform versions, something's wrong
343                 String keyAlgorithm = config.certificates.get(0).getPublicKey().getAlgorithm();
344                 throw new InvalidKeyException(
345                         "Unsupported key algorithm "
346                                 + keyAlgorithm
347                                 + " is "
348                                 + "not supported for APK Signature Scheme v3 signing");
349             }
350             if (i == rawConfigs.size() - 1) {
351                 // first go through the loop, config should support all future platform versions.
352                 // this assumes we don't deprecate support for signers in the future.  If we do,
353                 // this needs to change
354                 config.maxSdkVersion = Integer.MAX_VALUE;
355             } else {
356                 // otherwise, we only want to use this signer up to the minimum platform version
357                 // on which a newer one is acceptable
358                 config.maxSdkVersion = currentMinSdk - 1;
359             }
360             config.minSdkVersion = getMinSdkFromV3SignatureAlgorithms(config.signatureAlgorithms);
361             if (mSigningCertificateLineage != null) {
362                 config.mSigningCertificateLineage =
363                         mSigningCertificateLineage.getSubLineage(config.certificates.get(0));
364             }
365             // we know that this config will be used, so add it to our result, order doesn't matter
366             // at this point (and likely only one will be needed
367             processedConfigs.add(config);
368             currentMinSdk = config.minSdkVersion;
369             if (currentMinSdk <= mMinSdkVersion || currentMinSdk <= AndroidSdkVersion.P) {
370                 // this satisfies all we need, stop here
371                 break;
372             }
373         }
374         if (currentMinSdk > AndroidSdkVersion.P && currentMinSdk > mMinSdkVersion) {
375             // we can't cover all desired SDK versions, abort
376             throw new InvalidKeyException(
377                     "Provided key algorithms not supported on all desired "
378                             + "Android SDK versions");
379         }
380 
381         return processedConfigs;
382     }
383 
createV3SignerConfigs( boolean apkSigningBlockPaddingSupported)384     private List<ApkSigningBlockUtils.SignerConfig> createV3SignerConfigs(
385             boolean apkSigningBlockPaddingSupported) throws InvalidKeyException {
386         return processV3Configs(createSigningBlockSignerConfigs(apkSigningBlockPaddingSupported,
387                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3));
388     }
389 
createV4SignerConfig()390     private ApkSigningBlockUtils.SignerConfig createV4SignerConfig() throws InvalidKeyException {
391         List<ApkSigningBlockUtils.SignerConfig> configs = createSigningBlockSignerConfigs(true,
392                 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4);
393         if (configs.size() != 1) {
394             // V4 only uses signer config to connect back to v3. Use the same filtering logic.
395             configs = processV3Configs(configs);
396         }
397         if (configs.size() != 1) {
398             throw new InvalidKeyException("Only accepting one signer config for V4 Signature.");
399         }
400         return configs.get(0);
401     }
402 
createSourceStampSignerConfig()403     private ApkSigningBlockUtils.SignerConfig createSourceStampSignerConfig()
404             throws InvalidKeyException {
405         ApkSigningBlockUtils.SignerConfig config = createSigningBlockSignerConfig(
406                 mSourceStampSignerConfig,
407                 /* apkSigningBlockPaddingSupported= */ false,
408                 ApkSigningBlockUtils.VERSION_SOURCE_STAMP);
409         if (mSourceStampSigningCertificateLineage != null) {
410             config.mSigningCertificateLineage = mSourceStampSigningCertificateLineage.getSubLineage(
411                     config.certificates.get(0));
412         }
413         return config;
414     }
415 
getMinSdkFromV3SignatureAlgorithms(List<SignatureAlgorithm> algorithms)416     private int getMinSdkFromV3SignatureAlgorithms(List<SignatureAlgorithm> algorithms) {
417         int min = Integer.MAX_VALUE;
418         for (SignatureAlgorithm algorithm : algorithms) {
419             int current = algorithm.getMinSdkVersion();
420             if (current < min) {
421                 if (current <= mMinSdkVersion || current <= AndroidSdkVersion.P) {
422                     // this algorithm satisfies all of our needs, no need to keep looking
423                     return current;
424                 } else {
425                     min = current;
426                 }
427             }
428         }
429         return min;
430     }
431 
createSigningBlockSignerConfigs( boolean apkSigningBlockPaddingSupported, int schemeId)432     private List<ApkSigningBlockUtils.SignerConfig> createSigningBlockSignerConfigs(
433             boolean apkSigningBlockPaddingSupported, int schemeId) throws InvalidKeyException {
434         List<ApkSigningBlockUtils.SignerConfig> signerConfigs =
435                 new ArrayList<>(mSignerConfigs.size());
436         for (int i = 0; i < mSignerConfigs.size(); i++) {
437             SignerConfig signerConfig = mSignerConfigs.get(i);
438             signerConfigs.add(
439                     createSigningBlockSignerConfig(
440                             signerConfig, apkSigningBlockPaddingSupported, schemeId));
441         }
442         return signerConfigs;
443     }
444 
createSigningBlockSignerConfig( SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId)445     private ApkSigningBlockUtils.SignerConfig createSigningBlockSignerConfig(
446             SignerConfig signerConfig, boolean apkSigningBlockPaddingSupported, int schemeId)
447             throws InvalidKeyException {
448         List<X509Certificate> certificates = signerConfig.getCertificates();
449         PublicKey publicKey = certificates.get(0).getPublicKey();
450 
451         ApkSigningBlockUtils.SignerConfig newSignerConfig = new ApkSigningBlockUtils.SignerConfig();
452         newSignerConfig.privateKey = signerConfig.getPrivateKey();
453         newSignerConfig.certificates = certificates;
454 
455         switch (schemeId) {
456             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
457                 newSignerConfig.signatureAlgorithms =
458                         V2SchemeSigner.getSuggestedSignatureAlgorithms(
459                                 publicKey,
460                                 mMinSdkVersion,
461                                 apkSigningBlockPaddingSupported && mVerityEnabled,
462                                 signerConfig.getDeterministicDsaSigning());
463                 break;
464             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
465                 try {
466                     newSignerConfig.signatureAlgorithms =
467                             V3SchemeSigner.getSuggestedSignatureAlgorithms(
468                                     publicKey,
469                                     mMinSdkVersion,
470                                     apkSigningBlockPaddingSupported && mVerityEnabled,
471                                     signerConfig.getDeterministicDsaSigning());
472                 } catch (InvalidKeyException e) {
473 
474                     // It is possible for a signer used for v1/v2 signing to not be allowed for use
475                     // with v3 signing.  This is ok as long as there exists a more recent v3 signer
476                     // that covers all supported platform versions.  Populate signatureAlgorithm
477                     // with null, it will be cleaned-up in a later step.
478                     newSignerConfig.signatureAlgorithms = null;
479                 }
480                 break;
481             case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4:
482                 try {
483                     newSignerConfig.signatureAlgorithms =
484                             V4SchemeSigner.getSuggestedSignatureAlgorithms(
485                                     publicKey, mMinSdkVersion, apkSigningBlockPaddingSupported,
486                                     signerConfig.getDeterministicDsaSigning());
487                 } catch (InvalidKeyException e) {
488                     // V4 is an optional signing schema, ok to proceed without.
489                     newSignerConfig.signatureAlgorithms = null;
490                 }
491                 break;
492             case ApkSigningBlockUtils.VERSION_SOURCE_STAMP:
493                 newSignerConfig.signatureAlgorithms =
494                         Collections.singletonList(
495                                 SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256);
496                 break;
497             default:
498                 throw new IllegalArgumentException("Unknown APK Signature Scheme ID requested");
499         }
500         return newSignerConfig;
501     }
502 
isDebuggable(String entryName)503     private boolean isDebuggable(String entryName) {
504         return mDebuggableApkPermitted
505                 || !ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName);
506     }
507 
508     /**
509      * Initializes DefaultApkSignerEngine with the existing MANIFEST.MF. This reads existing digests
510      * from the MANIFEST.MF file (they are assumed correct) and stores them for the final signature
511      * without recalculation. This step has a significant performance benefit in case of incremental
512      * build.
513      *
514      * <p>This method extracts and stored computed digest for every entry that it would compute it
515      * for in the {@link #outputJarEntry(String)} method
516      *
517      * @param manifestBytes raw representation of MANIFEST.MF file
518      * @param entryNames a set of expected entries names
519      * @return set of entry names which were processed by the engine during the initialization, a
520      *     subset of entryNames
521      */
522     @Override
523     @SuppressWarnings("AndroidJdkLibsChecker")
initWith(byte[] manifestBytes, Set<String> entryNames)524     public Set<String> initWith(byte[] manifestBytes, Set<String> entryNames) {
525         V1SchemeVerifier.Result result = new V1SchemeVerifier.Result();
526         Pair<ManifestParser.Section, Map<String, ManifestParser.Section>> sections =
527                 V1SchemeVerifier.parseManifest(manifestBytes, entryNames, result);
528         String alg = V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm);
529         for (Map.Entry<String, ManifestParser.Section> entry : sections.getSecond().entrySet()) {
530             String entryName = entry.getKey();
531             if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entry.getKey())
532                     && isDebuggable(entryName)) {
533 
534                 V1SchemeVerifier.NamedDigest extractedDigest = null;
535                 Collection<V1SchemeVerifier.NamedDigest> digestsToVerify =
536                         V1SchemeVerifier.getDigestsToVerify(
537                                 entry.getValue(), "-Digest", mMinSdkVersion, Integer.MAX_VALUE);
538                 for (V1SchemeVerifier.NamedDigest digestToVerify : digestsToVerify) {
539                     if (digestToVerify.jcaDigestAlgorithm.equals(alg)) {
540                         extractedDigest = digestToVerify;
541                         break;
542                     }
543                 }
544                 if (extractedDigest != null) {
545                     mOutputJarEntryDigests.put(entryName, extractedDigest.digest);
546                 }
547             }
548         }
549         return mOutputJarEntryDigests.keySet();
550     }
551 
552     @Override
setExecutor(RunnablesExecutor executor)553     public void setExecutor(RunnablesExecutor executor) {
554         mExecutor = executor;
555     }
556 
557     @Override
inputApkSigningBlock(DataSource apkSigningBlock)558     public void inputApkSigningBlock(DataSource apkSigningBlock) {
559         checkNotClosed();
560 
561         if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) {
562             return;
563         }
564 
565         if (mOtherSignersSignaturesPreserved) {
566             boolean schemeSignatureBlockPreserved = false;
567             mPreservedSignatureBlocks = new ArrayList<>();
568             try {
569                 List<Pair<byte[], Integer>> signatureBlocks =
570                         ApkSigningBlockUtils.getApkSignatureBlocks(apkSigningBlock);
571                 for (Pair<byte[], Integer> signatureBlock : signatureBlocks) {
572                     if (signatureBlock.getSecond() == Constants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
573                         // If a V2 signature block is found and the engine is configured to use V2
574                         // then save any of the previous signers that are not part of the current
575                         // signing request.
576                         if (mV2SigningEnabled) {
577                             List<Pair<List<X509Certificate>, byte[]>> v2Signers =
578                                     ApkSigningBlockUtils.getApkSignatureBlockSigners(
579                                             signatureBlock.getFirst());
580                             mPreservedV2Signers = new ArrayList<>(v2Signers.size());
581                             for (Pair<List<X509Certificate>, byte[]> v2Signer : v2Signers) {
582                                 if (!isConfiguredWithSigner(v2Signer.getFirst())) {
583                                     mPreservedV2Signers.add(v2Signer.getSecond());
584                                     schemeSignatureBlockPreserved = true;
585                                 }
586                             }
587                         } else {
588                             // else V2 signing is not enabled; save the entire signature block to be
589                             // added to the final APK signing block.
590                             mPreservedSignatureBlocks.add(signatureBlock);
591                             schemeSignatureBlockPreserved = true;
592                         }
593                     } else if (signatureBlock.getSecond()
594                             == Constants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID) {
595                         // Preserving other signers in the presence of a V3 signature block is only
596                         // supported if the engine is configured to resign the APK with the V3
597                         // signature scheme, and the V3 signer in the signature block is the same
598                         // as the engine is configured to use.
599                         if (!mV3SigningEnabled) {
600                             throw new IllegalStateException(
601                                     "Preserving an existing V3 signature is not supported");
602                         }
603                         List<Pair<List<X509Certificate>, byte[]>> v3Signers =
604                                 ApkSigningBlockUtils.getApkSignatureBlockSigners(
605                                         signatureBlock.getFirst());
606                         if (v3Signers.size() > 1) {
607                             throw new IllegalArgumentException(
608                                     "The provided APK signing block contains " + v3Signers.size()
609                                             + " V3 signers; the V3 signature scheme only supports"
610                                             + " one signer");
611                         }
612                         // If there is only a single V3 signer then ensure it is the signer
613                         // configured to sign the APK.
614                         if (v3Signers.size() == 1
615                                 && !isConfiguredWithSigner(v3Signers.get(0).getFirst())) {
616                             throw new IllegalStateException(
617                                     "The V3 signature scheme only supports one signer; a request "
618                                             + "was made to preserve the existing V3 signature, "
619                                             + "but the engine is configured to sign with a "
620                                             + "different signer");
621                         }
622                     } else if (!DISCARDED_SIGNATURE_BLOCK_IDS.contains(
623                             signatureBlock.getSecond())) {
624                         mPreservedSignatureBlocks.add(signatureBlock);
625                     }
626                 }
627             } catch (ApkFormatException | CertificateException | IOException e) {
628                 throw new IllegalArgumentException("Unable to parse the provided signing block", e);
629             }
630             // Signature scheme V3+ only support a single signer; if the engine is configured to
631             // sign with V3+ then ensure no scheme signature blocks have been preserved.
632             if (mV3SigningEnabled && schemeSignatureBlockPreserved) {
633                 throw new IllegalStateException(
634                         "Signature scheme V3+ only supports a single signer and cannot be "
635                                 + "appended to the existing signature scheme blocks");
636             }
637             return;
638         }
639     }
640 
641     /**
642      * Returns whether the engine is configured to sign the APK with a signer using the specified
643      * {@code signerCerts}.
644      */
isConfiguredWithSigner(List<X509Certificate> signerCerts)645     private boolean isConfiguredWithSigner(List<X509Certificate> signerCerts) {
646         for (SignerConfig signerConfig : mSignerConfigs) {
647             if (signerCerts.containsAll(signerConfig.getCertificates())) {
648                 return true;
649             }
650         }
651         return false;
652     }
653 
654     @Override
inputJarEntry(String entryName)655     public InputJarEntryInstructions inputJarEntry(String entryName) {
656         checkNotClosed();
657 
658         InputJarEntryInstructions.OutputPolicy outputPolicy =
659                 getInputJarEntryOutputPolicy(entryName);
660         switch (outputPolicy) {
661             case SKIP:
662                 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.SKIP);
663             case OUTPUT:
664                 return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.OUTPUT);
665             case OUTPUT_BY_ENGINE:
666                 if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) {
667                     // We copy the main section of the JAR manifest from input to output. Thus, this
668                     // invalidates v1 signature and we need to see the entry's data.
669                     mInputJarManifestEntryDataRequest = new GetJarEntryDataRequest(entryName);
670                     return new InputJarEntryInstructions(
671                             InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE,
672                             mInputJarManifestEntryDataRequest);
673                 }
674                 return new InputJarEntryInstructions(
675                         InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE);
676             default:
677                 throw new RuntimeException("Unsupported output policy: " + outputPolicy);
678         }
679     }
680 
681     @Override
outputJarEntry(String entryName)682     public InspectJarEntryRequest outputJarEntry(String entryName) {
683         checkNotClosed();
684         invalidateV2Signature();
685 
686         if (!isDebuggable(entryName)) {
687             forgetOutputApkDebuggableStatus();
688         }
689 
690         if (!mV1SigningEnabled) {
691             // No need to inspect JAR entries when v1 signing is not enabled.
692             if (!isDebuggable(entryName)) {
693                 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to
694                 // check whether it declares that the APK is debuggable
695                 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName);
696                 return mOutputAndroidManifestEntryDataRequest;
697             }
698             return null;
699         }
700         // v1 signing is enabled
701 
702         if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) {
703             // This entry is covered by v1 signature. We thus need to inspect the entry's data to
704             // compute its digest(s) for v1 signature.
705 
706             // TODO: Handle the case where other signer's v1 signatures are present and need to be
707             // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries
708             // covered by v1 signature.
709             invalidateV1Signature();
710             GetJarEntryDataDigestRequest dataDigestRequest =
711                     new GetJarEntryDataDigestRequest(
712                             entryName,
713                             V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm));
714             mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest);
715             mOutputJarEntryDigests.remove(entryName);
716 
717             if ((!mDebuggableApkPermitted)
718                     && (ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME.equals(entryName))) {
719                 // To reject debuggable APKs we need to inspect the APK's AndroidManifest.xml to
720                 // check whether it declares that the APK is debuggable
721                 mOutputAndroidManifestEntryDataRequest = new GetJarEntryDataRequest(entryName);
722                 return new CompoundInspectJarEntryRequest(
723                         entryName, mOutputAndroidManifestEntryDataRequest, dataDigestRequest);
724             }
725 
726             return dataDigestRequest;
727         }
728 
729         if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
730             // This entry is part of v1 signature generated by this engine. We need to check whether
731             // the entry's data is as output by the engine.
732             invalidateV1Signature();
733             GetJarEntryDataRequest dataRequest;
734             if (V1SchemeConstants.MANIFEST_ENTRY_NAME.equals(entryName)) {
735                 dataRequest = new GetJarEntryDataRequest(entryName);
736                 mInputJarManifestEntryDataRequest = dataRequest;
737             } else {
738                 // If this entry is part of v1 signature which has been emitted by this engine,
739                 // check whether the output entry's data matches what the engine emitted.
740                 dataRequest =
741                         (mEmittedSignatureJarEntryData.containsKey(entryName))
742                                 ? new GetJarEntryDataRequest(entryName)
743                                 : null;
744             }
745 
746             if (dataRequest != null) {
747                 mOutputSignatureJarEntryDataRequests.put(entryName, dataRequest);
748             }
749             return dataRequest;
750         }
751 
752         // This entry is not covered by v1 signature and isn't part of v1 signature.
753         return null;
754     }
755 
756     @Override
inputJarEntryRemoved(String entryName)757     public InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) {
758         checkNotClosed();
759         return getInputJarEntryOutputPolicy(entryName);
760     }
761 
762     @Override
outputJarEntryRemoved(String entryName)763     public void outputJarEntryRemoved(String entryName) {
764         checkNotClosed();
765         invalidateV2Signature();
766         if (!mV1SigningEnabled) {
767             return;
768         }
769 
770         if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) {
771             // This entry is covered by v1 signature.
772             invalidateV1Signature();
773             mOutputJarEntryDigests.remove(entryName);
774             mOutputJarEntryDigestRequests.remove(entryName);
775             mOutputSignatureJarEntryDataRequests.remove(entryName);
776             return;
777         }
778 
779         if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
780             // This entry is part of the v1 signature generated by this engine.
781             invalidateV1Signature();
782             return;
783         }
784     }
785 
786     @Override
outputJarEntries()787     public OutputJarSignatureRequest outputJarEntries()
788             throws ApkFormatException, InvalidKeyException, SignatureException,
789                     NoSuchAlgorithmException {
790         checkNotClosed();
791 
792         if (!mV1SignaturePending) {
793             return null;
794         }
795 
796         if ((mInputJarManifestEntryDataRequest != null)
797                 && (!mInputJarManifestEntryDataRequest.isDone())) {
798             throw new IllegalStateException(
799                     "Still waiting to inspect input APK's "
800                             + mInputJarManifestEntryDataRequest.getEntryName());
801         }
802 
803         for (GetJarEntryDataDigestRequest digestRequest : mOutputJarEntryDigestRequests.values()) {
804             String entryName = digestRequest.getEntryName();
805             if (!digestRequest.isDone()) {
806                 throw new IllegalStateException(
807                         "Still waiting to inspect output APK's " + entryName);
808             }
809             mOutputJarEntryDigests.put(entryName, digestRequest.getDigest());
810         }
811         if (isEligibleForSourceStamp()) {
812             MessageDigest messageDigest =
813                     MessageDigest.getInstance(
814                             V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm));
815             messageDigest.update(generateSourceStampCertificateDigest());
816             mOutputJarEntryDigests.put(
817                     SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, messageDigest.digest());
818         }
819         mOutputJarEntryDigestRequests.clear();
820 
821         for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) {
822             if (!dataRequest.isDone()) {
823                 throw new IllegalStateException(
824                         "Still waiting to inspect output APK's " + dataRequest.getEntryName());
825             }
826         }
827 
828         List<Integer> apkSigningSchemeIds = new ArrayList<>();
829         if (mV2SigningEnabled) {
830             apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
831         }
832         if (mV3SigningEnabled) {
833             apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
834         }
835         byte[] inputJarManifest =
836                 (mInputJarManifestEntryDataRequest != null)
837                         ? mInputJarManifestEntryDataRequest.getData()
838                         : null;
839         if (isEligibleForSourceStamp()) {
840             inputJarManifest =
841                     V1SchemeSigner.generateManifestFile(
842                                     mV1ContentDigestAlgorithm,
843                                     mOutputJarEntryDigests,
844                                     inputJarManifest)
845                             .contents;
846         }
847 
848         // Check whether the most recently used signature (if present) is still fine.
849         checkOutputApkNotDebuggableIfDebuggableMustBeRejected();
850         List<Pair<String, byte[]>> signatureZipEntries;
851         if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) {
852             try {
853                 signatureZipEntries =
854                         V1SchemeSigner.sign(
855                                 mV1SignerConfigs,
856                                 mV1ContentDigestAlgorithm,
857                                 mOutputJarEntryDigests,
858                                 apkSigningSchemeIds,
859                                 inputJarManifest,
860                                 mCreatedBy);
861             } catch (CertificateException e) {
862                 throw new SignatureException("Failed to generate v1 signature", e);
863             }
864         } else {
865             V1SchemeSigner.OutputManifestFile newManifest =
866                     V1SchemeSigner.generateManifestFile(
867                             mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest);
868             byte[] emittedSignatureManifest =
869                     mEmittedSignatureJarEntryData.get(V1SchemeConstants.MANIFEST_ENTRY_NAME);
870             if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) {
871                 // Emitted v1 signature is no longer valid.
872                 try {
873                     signatureZipEntries =
874                             V1SchemeSigner.signManifest(
875                                     mV1SignerConfigs,
876                                     mV1ContentDigestAlgorithm,
877                                     apkSigningSchemeIds,
878                                     mCreatedBy,
879                                     newManifest);
880                 } catch (CertificateException e) {
881                     throw new SignatureException("Failed to generate v1 signature", e);
882                 }
883             } else {
884                 // Emitted v1 signature is still valid. Check whether the signature is there in the
885                 // output.
886                 signatureZipEntries = new ArrayList<>();
887                 for (Map.Entry<String, byte[]> expectedOutputEntry :
888                         mEmittedSignatureJarEntryData.entrySet()) {
889                     String entryName = expectedOutputEntry.getKey();
890                     byte[] expectedData = expectedOutputEntry.getValue();
891                     GetJarEntryDataRequest actualDataRequest =
892                             mOutputSignatureJarEntryDataRequests.get(entryName);
893                     if (actualDataRequest == null) {
894                         // This signature entry hasn't been output.
895                         signatureZipEntries.add(Pair.of(entryName, expectedData));
896                         continue;
897                     }
898                     byte[] actualData = actualDataRequest.getData();
899                     if (!Arrays.equals(expectedData, actualData)) {
900                         signatureZipEntries.add(Pair.of(entryName, expectedData));
901                     }
902                 }
903                 if (signatureZipEntries.isEmpty()) {
904                     // v1 signature in the output is valid
905                     return null;
906                 }
907                 // v1 signature in the output is not valid.
908             }
909         }
910 
911         if (signatureZipEntries.isEmpty()) {
912             // v1 signature in the output is valid
913             mV1SignaturePending = false;
914             return null;
915         }
916 
917         List<OutputJarSignatureRequest.JarEntry> sigEntries =
918                 new ArrayList<>(signatureZipEntries.size());
919         for (Pair<String, byte[]> entry : signatureZipEntries) {
920             String entryName = entry.getFirst();
921             byte[] entryData = entry.getSecond();
922             sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData));
923             mEmittedSignatureJarEntryData.put(entryName, entryData);
924         }
925         mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries);
926         return mAddV1SignatureRequest;
927     }
928 
929     @Deprecated
930     @Override
outputZipSections( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)931     public OutputApkSigningBlockRequest outputZipSections(
932             DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)
933             throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
934         return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, false);
935     }
936 
937     @Override
outputZipSections2( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)938     public OutputApkSigningBlockRequest2 outputZipSections2(
939             DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd)
940             throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
941         return outputZipSectionsInternal(zipEntries, zipCentralDirectory, zipEocd, true);
942     }
943 
outputZipSectionsInternal( DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd, boolean apkSigningBlockPaddingSupported)944     private OutputApkSigningBlockRequestImpl outputZipSectionsInternal(
945             DataSource zipEntries,
946             DataSource zipCentralDirectory,
947             DataSource zipEocd,
948             boolean apkSigningBlockPaddingSupported)
949             throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
950         checkNotClosed();
951         checkV1SigningDoneIfEnabled();
952         if (!mV2SigningEnabled && !mV3SigningEnabled && !isEligibleForSourceStamp()) {
953             return null;
954         }
955         checkOutputApkNotDebuggableIfDebuggableMustBeRejected();
956 
957         // adjust to proper padding
958         Pair<DataSource, Integer> paddingPair =
959                 ApkSigningBlockUtils.generateApkSigningBlockPadding(
960                         zipEntries, apkSigningBlockPaddingSupported);
961         DataSource beforeCentralDir = paddingPair.getFirst();
962         int padSizeBeforeApkSigningBlock = paddingPair.getSecond();
963         DataSource eocd = ApkSigningBlockUtils.copyWithModifiedCDOffset(beforeCentralDir, zipEocd);
964 
965         List<Pair<byte[], Integer>> signingSchemeBlocks = new ArrayList<>();
966         ApkSigningBlockUtils.SigningSchemeBlockAndDigests v2SigningSchemeBlockAndDigests = null;
967         ApkSigningBlockUtils.SigningSchemeBlockAndDigests v3SigningSchemeBlockAndDigests = null;
968         // If the engine is configured to preserve previous signature blocks and any were found in
969         // the existing APK signing block then add them to the list to be used to generate the
970         // new APK signing block.
971         if (mOtherSignersSignaturesPreserved && mPreservedSignatureBlocks != null
972                 && !mPreservedSignatureBlocks.isEmpty()) {
973             signingSchemeBlocks.addAll(mPreservedSignatureBlocks);
974         }
975 
976         // create APK Signature Scheme V2 Signature if requested
977         if (mV2SigningEnabled) {
978             invalidateV2Signature();
979             List<ApkSigningBlockUtils.SignerConfig> v2SignerConfigs =
980                     createV2SignerConfigs(apkSigningBlockPaddingSupported);
981             v2SigningSchemeBlockAndDigests =
982                     V2SchemeSigner.generateApkSignatureSchemeV2Block(
983                             mExecutor,
984                             beforeCentralDir,
985                             zipCentralDirectory,
986                             eocd,
987                             v2SignerConfigs,
988                             mV3SigningEnabled,
989                             mOtherSignersSignaturesPreserved ? mPreservedV2Signers : null);
990             signingSchemeBlocks.add(v2SigningSchemeBlockAndDigests.signingSchemeBlock);
991         }
992         if (mV3SigningEnabled) {
993             invalidateV3Signature();
994             List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs =
995                     createV3SignerConfigs(apkSigningBlockPaddingSupported);
996             v3SigningSchemeBlockAndDigests =
997                     V3SchemeSigner.generateApkSignatureSchemeV3Block(
998                             mExecutor,
999                             beforeCentralDir,
1000                             zipCentralDirectory,
1001                             eocd,
1002                             v3SignerConfigs);
1003             signingSchemeBlocks.add(v3SigningSchemeBlockAndDigests.signingSchemeBlock);
1004         }
1005         if (isEligibleForSourceStamp()) {
1006             ApkSigningBlockUtils.SignerConfig sourceStampSignerConfig =
1007                     createSourceStampSignerConfig();
1008             Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeDigestInfos =
1009                     new HashMap<>();
1010             if (mV3SigningEnabled) {
1011                 signatureSchemeDigestInfos.put(
1012                         VERSION_APK_SIGNATURE_SCHEME_V3, v3SigningSchemeBlockAndDigests.digestInfo);
1013             }
1014             if (mV2SigningEnabled) {
1015                 signatureSchemeDigestInfos.put(
1016                         VERSION_APK_SIGNATURE_SCHEME_V2, v2SigningSchemeBlockAndDigests.digestInfo);
1017             }
1018             if (mV1SigningEnabled) {
1019                 Map<ContentDigestAlgorithm, byte[]> v1SigningSchemeDigests = new HashMap<>();
1020                 try {
1021                     // Jar signing related variables must have been already populated at this point
1022                     // if V1 signing is enabled since it is happening before computations on the APK
1023                     // signing block (V2/V3/V4/SourceStamp signing).
1024                     byte[] inputJarManifest =
1025                             (mInputJarManifestEntryDataRequest != null)
1026                                     ? mInputJarManifestEntryDataRequest.getData()
1027                                     : null;
1028                     byte[] jarManifest =
1029                             V1SchemeSigner.generateManifestFile(
1030                                             mV1ContentDigestAlgorithm,
1031                                             mOutputJarEntryDigests,
1032                                             inputJarManifest)
1033                                     .contents;
1034                     // The digest of the jar manifest does not need to be computed in chunks due to
1035                     // the small size of the manifest.
1036                     v1SigningSchemeDigests.put(
1037                             ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(jarManifest));
1038                 } catch (ApkFormatException e) {
1039                     throw new RuntimeException("Failed to generate manifest file", e);
1040                 }
1041                 signatureSchemeDigestInfos.put(
1042                         VERSION_JAR_SIGNATURE_SCHEME, v1SigningSchemeDigests);
1043             }
1044             signingSchemeBlocks.add(
1045                     V2SourceStampSigner.generateSourceStampBlock(
1046                             sourceStampSignerConfig, signatureSchemeDigestInfos));
1047         }
1048 
1049         // create APK Signing Block with v2 and/or v3 and/or SourceStamp blocks
1050         byte[] apkSigningBlock = ApkSigningBlockUtils.generateApkSigningBlock(signingSchemeBlocks);
1051 
1052         mAddSigningBlockRequest =
1053                 new OutputApkSigningBlockRequestImpl(apkSigningBlock, padSizeBeforeApkSigningBlock);
1054         return mAddSigningBlockRequest;
1055     }
1056 
1057     @Override
outputDone()1058     public void outputDone() {
1059         checkNotClosed();
1060         checkV1SigningDoneIfEnabled();
1061         checkSigningBlockDoneIfEnabled();
1062     }
1063 
1064     @Override
signV4(DataSource dataSource, File outputFile, boolean ignoreFailures)1065     public void signV4(DataSource dataSource, File outputFile, boolean ignoreFailures)
1066             throws SignatureException {
1067         if (outputFile == null) {
1068             if (ignoreFailures) {
1069                 return;
1070             }
1071             throw new SignatureException("Missing V4 output file.");
1072         }
1073         try {
1074             ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig();
1075             V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig, outputFile);
1076         } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) {
1077             if (ignoreFailures) {
1078                 return;
1079             }
1080             throw new SignatureException("V4 signing failed", e);
1081         }
1082     }
1083 
1084     /** For external use only to generate V4 & tree separately. */
produceV4Signature(DataSource dataSource, OutputStream sigOutput)1085     public byte[] produceV4Signature(DataSource dataSource, OutputStream sigOutput)
1086             throws SignatureException {
1087         if (sigOutput == null) {
1088             throw new SignatureException("Missing V4 output streams.");
1089         }
1090         try {
1091             ApkSigningBlockUtils.SignerConfig v4SignerConfig = createV4SignerConfig();
1092             Pair<V4Signature, byte[]> pair =
1093                     V4SchemeSigner.generateV4Signature(dataSource, v4SignerConfig);
1094             pair.getFirst().writeTo(sigOutput);
1095             return pair.getSecond();
1096         } catch (InvalidKeyException | IOException | NoSuchAlgorithmException e) {
1097             throw new SignatureException("V4 signing failed", e);
1098         }
1099     }
1100 
1101     @Override
isEligibleForSourceStamp()1102     public boolean isEligibleForSourceStamp() {
1103         return mSourceStampSignerConfig != null
1104                 && (mV2SigningEnabled || mV3SigningEnabled || mV1SigningEnabled);
1105     }
1106 
1107     @Override
generateSourceStampCertificateDigest()1108     public byte[] generateSourceStampCertificateDigest() throws SignatureException {
1109         if (mSourceStampSignerConfig.getCertificates().isEmpty()) {
1110             throw new SignatureException("No certificates configured for stamp");
1111         }
1112         try {
1113             return computeSha256DigestBytes(
1114                     mSourceStampSignerConfig.getCertificates().get(0).getEncoded());
1115         } catch (CertificateEncodingException e) {
1116             throw new SignatureException("Failed to encode source stamp certificate", e);
1117         }
1118     }
1119 
1120     @Override
close()1121     public void close() {
1122         mClosed = true;
1123 
1124         mAddV1SignatureRequest = null;
1125         mInputJarManifestEntryDataRequest = null;
1126         mOutputAndroidManifestEntryDataRequest = null;
1127         mDebuggable = null;
1128         mOutputJarEntryDigestRequests.clear();
1129         mOutputJarEntryDigests.clear();
1130         mEmittedSignatureJarEntryData.clear();
1131         mOutputSignatureJarEntryDataRequests.clear();
1132 
1133         mAddSigningBlockRequest = null;
1134     }
1135 
invalidateV1Signature()1136     private void invalidateV1Signature() {
1137         if (mV1SigningEnabled) {
1138             mV1SignaturePending = true;
1139         }
1140         invalidateV2Signature();
1141     }
1142 
invalidateV2Signature()1143     private void invalidateV2Signature() {
1144         if (mV2SigningEnabled) {
1145             mV2SignaturePending = true;
1146             mAddSigningBlockRequest = null;
1147         }
1148     }
1149 
invalidateV3Signature()1150     private void invalidateV3Signature() {
1151         if (mV3SigningEnabled) {
1152             mV3SignaturePending = true;
1153             mAddSigningBlockRequest = null;
1154         }
1155     }
1156 
checkNotClosed()1157     private void checkNotClosed() {
1158         if (mClosed) {
1159             throw new IllegalStateException("Engine closed");
1160         }
1161     }
1162 
checkV1SigningDoneIfEnabled()1163     private void checkV1SigningDoneIfEnabled() {
1164         if (!mV1SignaturePending) {
1165             return;
1166         }
1167 
1168         if (mAddV1SignatureRequest == null) {
1169             throw new IllegalStateException(
1170                     "v1 signature (JAR signature) not yet generated. Skipped outputJarEntries()?");
1171         }
1172         if (!mAddV1SignatureRequest.isDone()) {
1173             throw new IllegalStateException(
1174                     "v1 signature (JAR signature) addition requested by outputJarEntries() hasn't"
1175                             + " been fulfilled");
1176         }
1177         for (Map.Entry<String, byte[]> expectedOutputEntry :
1178                 mEmittedSignatureJarEntryData.entrySet()) {
1179             String entryName = expectedOutputEntry.getKey();
1180             byte[] expectedData = expectedOutputEntry.getValue();
1181             GetJarEntryDataRequest actualDataRequest =
1182                     mOutputSignatureJarEntryDataRequests.get(entryName);
1183             if (actualDataRequest == null) {
1184                 throw new IllegalStateException(
1185                         "APK entry "
1186                                 + entryName
1187                                 + " not yet output despite this having been"
1188                                 + " requested");
1189             } else if (!actualDataRequest.isDone()) {
1190                 throw new IllegalStateException(
1191                         "Still waiting to inspect output APK's " + entryName);
1192             }
1193             byte[] actualData = actualDataRequest.getData();
1194             if (!Arrays.equals(expectedData, actualData)) {
1195                 throw new IllegalStateException(
1196                         "Output APK entry " + entryName + " data differs from what was requested");
1197             }
1198         }
1199         mV1SignaturePending = false;
1200     }
1201 
checkSigningBlockDoneIfEnabled()1202     private void checkSigningBlockDoneIfEnabled() {
1203         if (!mV2SignaturePending && !mV3SignaturePending) {
1204             return;
1205         }
1206         if (mAddSigningBlockRequest == null) {
1207             throw new IllegalStateException(
1208                     "Signed APK Signing BLock not yet generated. Skipped outputZipSections()?");
1209         }
1210         if (!mAddSigningBlockRequest.isDone()) {
1211             throw new IllegalStateException(
1212                     "APK Signing Block addition of signature(s) requested by"
1213                             + " outputZipSections() hasn't been fulfilled yet");
1214         }
1215         mAddSigningBlockRequest = null;
1216         mV2SignaturePending = false;
1217         mV3SignaturePending = false;
1218     }
1219 
checkOutputApkNotDebuggableIfDebuggableMustBeRejected()1220     private void checkOutputApkNotDebuggableIfDebuggableMustBeRejected() throws SignatureException {
1221         if (mDebuggableApkPermitted) {
1222             return;
1223         }
1224 
1225         try {
1226             if (isOutputApkDebuggable()) {
1227                 throw new SignatureException(
1228                         "APK is debuggable (see android:debuggable attribute) and this engine is"
1229                                 + " configured to refuse to sign debuggable APKs");
1230             }
1231         } catch (ApkFormatException e) {
1232             throw new SignatureException("Failed to determine whether the APK is debuggable", e);
1233         }
1234     }
1235 
1236     /**
1237      * Returns whether the output APK is debuggable according to its {@code android:debuggable}
1238      * declaration.
1239      */
isOutputApkDebuggable()1240     private boolean isOutputApkDebuggable() throws ApkFormatException {
1241         if (mDebuggable != null) {
1242             return mDebuggable;
1243         }
1244 
1245         if (mOutputAndroidManifestEntryDataRequest == null) {
1246             throw new IllegalStateException(
1247                     "Cannot determine debuggable status of output APK because "
1248                             + ApkUtils.ANDROID_MANIFEST_ZIP_ENTRY_NAME
1249                             + " entry contents have not yet been requested");
1250         }
1251 
1252         if (!mOutputAndroidManifestEntryDataRequest.isDone()) {
1253             throw new IllegalStateException(
1254                     "Still waiting to inspect output APK's "
1255                             + mOutputAndroidManifestEntryDataRequest.getEntryName());
1256         }
1257         mDebuggable =
1258                 ApkUtils.getDebuggableFromBinaryAndroidManifest(
1259                         ByteBuffer.wrap(mOutputAndroidManifestEntryDataRequest.getData()));
1260         return mDebuggable;
1261     }
1262 
forgetOutputApkDebuggableStatus()1263     private void forgetOutputApkDebuggableStatus() {
1264         mDebuggable = null;
1265     }
1266 
1267     /** Returns the output policy for the provided input JAR entry. */
getInputJarEntryOutputPolicy(String entryName)1268     private InputJarEntryInstructions.OutputPolicy getInputJarEntryOutputPolicy(String entryName) {
1269         if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
1270             return InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE;
1271         }
1272         if ((mOtherSignersSignaturesPreserved)
1273                 || (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName))) {
1274             return InputJarEntryInstructions.OutputPolicy.OUTPUT;
1275         }
1276         return InputJarEntryInstructions.OutputPolicy.SKIP;
1277     }
1278 
1279     private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest {
1280         private final List<JarEntry> mAdditionalJarEntries;
1281         private volatile boolean mDone;
1282 
OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries)1283         private OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries) {
1284             mAdditionalJarEntries =
1285                     Collections.unmodifiableList(new ArrayList<>(additionalZipEntries));
1286         }
1287 
1288         @Override
getAdditionalJarEntries()1289         public List<JarEntry> getAdditionalJarEntries() {
1290             return mAdditionalJarEntries;
1291         }
1292 
1293         @Override
done()1294         public void done() {
1295             mDone = true;
1296         }
1297 
isDone()1298         private boolean isDone() {
1299             return mDone;
1300         }
1301     }
1302 
1303     @SuppressWarnings("deprecation")
1304     private static class OutputApkSigningBlockRequestImpl
1305             implements OutputApkSigningBlockRequest, OutputApkSigningBlockRequest2 {
1306         private final byte[] mApkSigningBlock;
1307         private final int mPaddingBeforeApkSigningBlock;
1308         private volatile boolean mDone;
1309 
OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore)1310         private OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock, int paddingBefore) {
1311             mApkSigningBlock = apkSigingBlock.clone();
1312             mPaddingBeforeApkSigningBlock = paddingBefore;
1313         }
1314 
1315         @Override
getApkSigningBlock()1316         public byte[] getApkSigningBlock() {
1317             return mApkSigningBlock.clone();
1318         }
1319 
1320         @Override
done()1321         public void done() {
1322             mDone = true;
1323         }
1324 
isDone()1325         private boolean isDone() {
1326             return mDone;
1327         }
1328 
1329         @Override
getPaddingSizeBeforeApkSigningBlock()1330         public int getPaddingSizeBeforeApkSigningBlock() {
1331             return mPaddingBeforeApkSigningBlock;
1332         }
1333     }
1334 
1335     /** JAR entry inspection request which obtain the entry's uncompressed data. */
1336     private static class GetJarEntryDataRequest implements InspectJarEntryRequest {
1337         private final String mEntryName;
1338         private final Object mLock = new Object();
1339 
1340         private boolean mDone;
1341         private DataSink mDataSink;
1342         private ByteArrayOutputStream mDataSinkBuf;
1343 
GetJarEntryDataRequest(String entryName)1344         private GetJarEntryDataRequest(String entryName) {
1345             mEntryName = entryName;
1346         }
1347 
1348         @Override
getEntryName()1349         public String getEntryName() {
1350             return mEntryName;
1351         }
1352 
1353         @Override
getDataSink()1354         public DataSink getDataSink() {
1355             synchronized (mLock) {
1356                 checkNotDone();
1357                 if (mDataSinkBuf == null) {
1358                     mDataSinkBuf = new ByteArrayOutputStream();
1359                 }
1360                 if (mDataSink == null) {
1361                     mDataSink = DataSinks.asDataSink(mDataSinkBuf);
1362                 }
1363                 return mDataSink;
1364             }
1365         }
1366 
1367         @Override
done()1368         public void done() {
1369             synchronized (mLock) {
1370                 if (mDone) {
1371                     return;
1372                 }
1373                 mDone = true;
1374             }
1375         }
1376 
isDone()1377         private boolean isDone() {
1378             synchronized (mLock) {
1379                 return mDone;
1380             }
1381         }
1382 
checkNotDone()1383         private void checkNotDone() throws IllegalStateException {
1384             synchronized (mLock) {
1385                 if (mDone) {
1386                     throw new IllegalStateException("Already done");
1387                 }
1388             }
1389         }
1390 
getData()1391         private byte[] getData() {
1392             synchronized (mLock) {
1393                 if (!mDone) {
1394                     throw new IllegalStateException("Not yet done");
1395                 }
1396                 return (mDataSinkBuf != null) ? mDataSinkBuf.toByteArray() : new byte[0];
1397             }
1398         }
1399     }
1400 
1401     /** JAR entry inspection request which obtains the digest of the entry's uncompressed data. */
1402     private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest {
1403         private final String mEntryName;
1404         private final String mJcaDigestAlgorithm;
1405         private final Object mLock = new Object();
1406 
1407         private boolean mDone;
1408         private DataSink mDataSink;
1409         private MessageDigest mMessageDigest;
1410         private byte[] mDigest;
1411 
GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm)1412         private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) {
1413             mEntryName = entryName;
1414             mJcaDigestAlgorithm = jcaDigestAlgorithm;
1415         }
1416 
1417         @Override
getEntryName()1418         public String getEntryName() {
1419             return mEntryName;
1420         }
1421 
1422         @Override
getDataSink()1423         public DataSink getDataSink() {
1424             synchronized (mLock) {
1425                 checkNotDone();
1426                 if (mDataSink == null) {
1427                     mDataSink = DataSinks.asDataSink(getMessageDigest());
1428                 }
1429                 return mDataSink;
1430             }
1431         }
1432 
getMessageDigest()1433         private MessageDigest getMessageDigest() {
1434             synchronized (mLock) {
1435                 if (mMessageDigest == null) {
1436                     try {
1437                         mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm);
1438                     } catch (NoSuchAlgorithmException e) {
1439                         throw new RuntimeException(
1440                                 mJcaDigestAlgorithm + " MessageDigest not available", e);
1441                     }
1442                 }
1443                 return mMessageDigest;
1444             }
1445         }
1446 
1447         @Override
done()1448         public void done() {
1449             synchronized (mLock) {
1450                 if (mDone) {
1451                     return;
1452                 }
1453                 mDone = true;
1454                 mDigest = getMessageDigest().digest();
1455                 mMessageDigest = null;
1456                 mDataSink = null;
1457             }
1458         }
1459 
isDone()1460         private boolean isDone() {
1461             synchronized (mLock) {
1462                 return mDone;
1463             }
1464         }
1465 
checkNotDone()1466         private void checkNotDone() throws IllegalStateException {
1467             synchronized (mLock) {
1468                 if (mDone) {
1469                     throw new IllegalStateException("Already done");
1470                 }
1471             }
1472         }
1473 
getDigest()1474         private byte[] getDigest() {
1475             synchronized (mLock) {
1476                 if (!mDone) {
1477                     throw new IllegalStateException("Not yet done");
1478                 }
1479                 return mDigest.clone();
1480             }
1481         }
1482     }
1483 
1484     /** JAR entry inspection request which transparently satisfies multiple such requests. */
1485     private static class CompoundInspectJarEntryRequest implements InspectJarEntryRequest {
1486         private final String mEntryName;
1487         private final InspectJarEntryRequest[] mRequests;
1488         private final Object mLock = new Object();
1489 
1490         private DataSink mSink;
1491 
CompoundInspectJarEntryRequest( String entryName, InspectJarEntryRequest... requests)1492         private CompoundInspectJarEntryRequest(
1493                 String entryName, InspectJarEntryRequest... requests) {
1494             mEntryName = entryName;
1495             mRequests = requests;
1496         }
1497 
1498         @Override
getEntryName()1499         public String getEntryName() {
1500             return mEntryName;
1501         }
1502 
1503         @Override
getDataSink()1504         public DataSink getDataSink() {
1505             synchronized (mLock) {
1506                 if (mSink == null) {
1507                     DataSink[] sinks = new DataSink[mRequests.length];
1508                     for (int i = 0; i < sinks.length; i++) {
1509                         sinks[i] = mRequests[i].getDataSink();
1510                     }
1511                     mSink = new TeeDataSink(sinks);
1512                 }
1513                 return mSink;
1514             }
1515         }
1516 
1517         @Override
done()1518         public void done() {
1519             for (InspectJarEntryRequest request : mRequests) {
1520                 request.done();
1521             }
1522         }
1523     }
1524 
1525     /**
1526      * Configuration of a signer.
1527      *
1528      * <p>Use {@link Builder} to obtain configuration instances.
1529      */
1530     public static class SignerConfig {
1531         private final String mName;
1532         private final PrivateKey mPrivateKey;
1533         private final List<X509Certificate> mCertificates;
1534         private final boolean mDeterministicDsaSigning;
1535 
SignerConfig( String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1536         private SignerConfig(
1537                 String name, PrivateKey privateKey, List<X509Certificate> certificates,
1538                 boolean deterministicDsaSigning) {
1539             mName = name;
1540             mPrivateKey = privateKey;
1541             mCertificates = Collections.unmodifiableList(new ArrayList<>(certificates));
1542             mDeterministicDsaSigning = deterministicDsaSigning;
1543         }
1544 
1545         /** Returns the name of this signer. */
getName()1546         public String getName() {
1547             return mName;
1548         }
1549 
1550         /** Returns the signing key of this signer. */
getPrivateKey()1551         public PrivateKey getPrivateKey() {
1552             return mPrivateKey;
1553         }
1554 
1555         /**
1556          * Returns the certificate(s) of this signer. The first certificate's public key corresponds
1557          * to this signer's private key.
1558          */
getCertificates()1559         public List<X509Certificate> getCertificates() {
1560             return mCertificates;
1561         }
1562 
1563         /**
1564          * If this signer is a DSA signer, whether or not the signing is done deterministically.
1565          */
getDeterministicDsaSigning()1566         public boolean getDeterministicDsaSigning() {
1567             return mDeterministicDsaSigning;
1568         }
1569 
1570         /** Builder of {@link SignerConfig} instances. */
1571         public static class Builder {
1572             private final String mName;
1573             private final PrivateKey mPrivateKey;
1574             private final List<X509Certificate> mCertificates;
1575             private final boolean mDeterministicDsaSigning;
1576 
1577             /**
1578              * Constructs a new {@code Builder}.
1579              *
1580              * @param name signer's name. The name is reflected in the name of files comprising the
1581              *     JAR signature of the APK.
1582              * @param privateKey signing key
1583              * @param certificates list of one or more X.509 certificates. The subject public key of
1584              *     the first certificate must correspond to the {@code privateKey}.
1585              */
Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates)1586             public Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates) {
1587                 this(name, privateKey, certificates, false);
1588             }
1589 
1590             /**
1591              * Constructs a new {@code Builder}.
1592              *
1593              * @param name signer's name. The name is reflected in the name of files comprising the
1594              *     JAR signature of the APK.
1595              * @param privateKey signing key
1596              * @param certificates list of one or more X.509 certificates. The subject public key of
1597              *     the first certificate must correspond to the {@code privateKey}.
1598              * @param deterministicDsaSigning When signing using DSA, whether or not the
1599              * deterministic signing algorithm variant (RFC6979) should be used.
1600              */
Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates, boolean deterministicDsaSigning)1601             public Builder(String name, PrivateKey privateKey, List<X509Certificate> certificates,
1602                     boolean deterministicDsaSigning) {
1603                 if (name.isEmpty()) {
1604                     throw new IllegalArgumentException("Empty name");
1605                 }
1606                 mName = name;
1607                 mPrivateKey = privateKey;
1608                 mCertificates = new ArrayList<>(certificates);
1609                 mDeterministicDsaSigning = deterministicDsaSigning;
1610             }
1611 
1612             /**
1613              * Returns a new {@code SignerConfig} instance configured based on the configuration of
1614              * this builder.
1615              */
build()1616             public SignerConfig build() {
1617                 return new SignerConfig(mName, mPrivateKey, mCertificates,
1618                         mDeterministicDsaSigning);
1619             }
1620         }
1621     }
1622 
1623     /** Builder of {@link DefaultApkSignerEngine} instances. */
1624     public static class Builder {
1625         private List<SignerConfig> mSignerConfigs;
1626         private SignerConfig mStampSignerConfig;
1627         private SigningCertificateLineage mSourceStampSigningCertificateLineage;
1628         private final int mMinSdkVersion;
1629 
1630         private boolean mV1SigningEnabled = true;
1631         private boolean mV2SigningEnabled = true;
1632         private boolean mV3SigningEnabled = true;
1633         private boolean mVerityEnabled = false;
1634         private boolean mDebuggableApkPermitted = true;
1635         private boolean mOtherSignersSignaturesPreserved;
1636         private String mCreatedBy = "1.0 (Android)";
1637 
1638         private SigningCertificateLineage mSigningCertificateLineage;
1639 
1640         // APK Signature Scheme v3 only supports a single signing certificate, so to move to v3
1641         // signing by default, but not require prior clients to update to explicitly disable v3
1642         // signing for multiple signers, we modify the mV3SigningEnabled depending on the provided
1643         // inputs (multiple signers and mSigningCertificateLineage in particular).  Maintain two
1644         // extra variables to record whether or not mV3SigningEnabled has been set directly by a
1645         // client and so should override the default behavior.
1646         private boolean mV3SigningExplicitlyDisabled = false;
1647         private boolean mV3SigningExplicitlyEnabled = false;
1648 
1649         /**
1650          * Constructs a new {@code Builder}.
1651          *
1652          * @param signerConfigs information about signers with which the APK will be signed. At
1653          *     least one signer configuration must be provided.
1654          * @param minSdkVersion API Level of the oldest Android platform on which the APK is
1655          *     supposed to be installed. See {@code minSdkVersion} attribute in the APK's {@code
1656          *     AndroidManifest.xml}. The higher the version, the stronger signing features will be
1657          *     enabled.
1658          */
Builder(List<SignerConfig> signerConfigs, int minSdkVersion)1659         public Builder(List<SignerConfig> signerConfigs, int minSdkVersion) {
1660             if (signerConfigs.isEmpty()) {
1661                 throw new IllegalArgumentException("At least one signer config must be provided");
1662             }
1663             if (signerConfigs.size() > 1) {
1664                 // APK Signature Scheme v3 only supports single signer, unless a
1665                 // SigningCertificateLineage is provided, in which case this will be reset to true,
1666                 // since we don't yet have a v4 scheme about which to worry
1667                 mV3SigningEnabled = false;
1668             }
1669             mSignerConfigs = new ArrayList<>(signerConfigs);
1670             mMinSdkVersion = minSdkVersion;
1671         }
1672 
1673         /**
1674          * Returns a new {@code DefaultApkSignerEngine} instance configured based on the
1675          * configuration of this builder.
1676          */
build()1677         public DefaultApkSignerEngine build() throws InvalidKeyException {
1678 
1679             if (mV3SigningExplicitlyDisabled && mV3SigningExplicitlyEnabled) {
1680                 throw new IllegalStateException(
1681                         "Builder configured to both enable and disable APK "
1682                                 + "Signature Scheme v3 signing");
1683             }
1684             if (mV3SigningExplicitlyDisabled) {
1685                 mV3SigningEnabled = false;
1686             } else if (mV3SigningExplicitlyEnabled) {
1687                 mV3SigningEnabled = true;
1688             }
1689 
1690             // make sure our signers are appropriately setup
1691             if (mSigningCertificateLineage != null) {
1692                 try {
1693                     mSignerConfigs = mSigningCertificateLineage.sortSignerConfigs(mSignerConfigs);
1694                     if (!mV3SigningEnabled && mSignerConfigs.size() > 1) {
1695 
1696                         // this is a strange situation: we've provided a valid rotation history, but
1697                         // are only signing with v1/v2.  blow up, since we don't know for sure with
1698                         // which signer the user intended to sign
1699                         throw new IllegalStateException(
1700                                 "Provided multiple signers which are part of the"
1701                                         + " SigningCertificateLineage, but not signing with APK"
1702                                         + " Signature Scheme v3");
1703                     }
1704                 } catch (IllegalArgumentException e) {
1705                     throw new IllegalStateException(
1706                             "Provided signer configs do not match the "
1707                                     + "provided SigningCertificateLineage",
1708                             e);
1709                 }
1710             } else if (mV3SigningEnabled && mSignerConfigs.size() > 1) {
1711                 throw new IllegalStateException(
1712                         "Multiple signing certificates provided for use with APK Signature Scheme"
1713                                 + " v3 without an accompanying SigningCertificateLineage");
1714             }
1715 
1716             return new DefaultApkSignerEngine(
1717                     mSignerConfigs,
1718                     mStampSignerConfig,
1719                     mSourceStampSigningCertificateLineage,
1720                     mMinSdkVersion,
1721                     mV1SigningEnabled,
1722                     mV2SigningEnabled,
1723                     mV3SigningEnabled,
1724                     mVerityEnabled,
1725                     mDebuggableApkPermitted,
1726                     mOtherSignersSignaturesPreserved,
1727                     mCreatedBy,
1728                     mSigningCertificateLineage);
1729         }
1730 
1731         /** Sets the signer configuration for the SourceStamp to be embedded in the APK. */
setStampSignerConfig(SignerConfig stampSignerConfig)1732         public Builder setStampSignerConfig(SignerConfig stampSignerConfig) {
1733             mStampSignerConfig = stampSignerConfig;
1734             return this;
1735         }
1736 
1737         /**
1738          * Sets the source stamp {@link SigningCertificateLineage}. This structure provides proof of
1739          * signing certificate rotation for certificates previously used to sign source stamps.
1740          */
setSourceStampSigningCertificateLineage( SigningCertificateLineage sourceStampSigningCertificateLineage)1741         public Builder setSourceStampSigningCertificateLineage(
1742                 SigningCertificateLineage sourceStampSigningCertificateLineage) {
1743             mSourceStampSigningCertificateLineage = sourceStampSigningCertificateLineage;
1744             return this;
1745         }
1746 
1747         /**
1748          * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme).
1749          *
1750          * <p>By default, the APK will be signed using this scheme.
1751          */
setV1SigningEnabled(boolean enabled)1752         public Builder setV1SigningEnabled(boolean enabled) {
1753             mV1SigningEnabled = enabled;
1754             return this;
1755         }
1756 
1757         /**
1758          * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature
1759          * scheme).
1760          *
1761          * <p>By default, the APK will be signed using this scheme.
1762          */
setV2SigningEnabled(boolean enabled)1763         public Builder setV2SigningEnabled(boolean enabled) {
1764             mV2SigningEnabled = enabled;
1765             return this;
1766         }
1767 
1768         /**
1769          * Sets whether the APK should be signed using APK Signature Scheme v3 (aka v3 signature
1770          * scheme).
1771          *
1772          * <p>By default, the APK will be signed using this scheme.
1773          */
setV3SigningEnabled(boolean enabled)1774         public Builder setV3SigningEnabled(boolean enabled) {
1775             mV3SigningEnabled = enabled;
1776             if (enabled) {
1777                 mV3SigningExplicitlyEnabled = true;
1778             } else {
1779                 mV3SigningExplicitlyDisabled = true;
1780             }
1781             return this;
1782         }
1783 
1784         /**
1785          * Sets whether the APK should be signed using the verity signature algorithm in the v2 and
1786          * v3 signature blocks.
1787          *
1788          * <p>By default, the APK will be signed using the verity signature algorithm for the v2 and
1789          * v3 signature schemes.
1790          */
setVerityEnabled(boolean enabled)1791         public Builder setVerityEnabled(boolean enabled) {
1792             mVerityEnabled = enabled;
1793             return this;
1794         }
1795 
1796         /**
1797          * Sets whether the APK should be signed even if it is marked as debuggable ({@code
1798          * android:debuggable="true"} in its {@code AndroidManifest.xml}). For backward
1799          * compatibility reasons, the default value of this setting is {@code true}.
1800          *
1801          * <p>It is dangerous to sign debuggable APKs with production/release keys because Android
1802          * platform loosens security checks for such APKs. For example, arbitrary unauthorized code
1803          * may be executed in the context of such an app by anybody with ADB shell access.
1804          */
setDebuggableApkPermitted(boolean permitted)1805         public Builder setDebuggableApkPermitted(boolean permitted) {
1806             mDebuggableApkPermitted = permitted;
1807             return this;
1808         }
1809 
1810         /**
1811          * Sets whether signatures produced by signers other than the ones configured in this engine
1812          * should be copied from the input APK to the output APK.
1813          *
1814          * <p>By default, signatures of other signers are omitted from the output APK.
1815          */
setOtherSignersSignaturesPreserved(boolean preserved)1816         public Builder setOtherSignersSignaturesPreserved(boolean preserved) {
1817             mOtherSignersSignaturesPreserved = preserved;
1818             return this;
1819         }
1820 
1821         /** Sets the value of the {@code Created-By} field in JAR signature files. */
setCreatedBy(String createdBy)1822         public Builder setCreatedBy(String createdBy) {
1823             if (createdBy == null) {
1824                 throw new NullPointerException();
1825             }
1826             mCreatedBy = createdBy;
1827             return this;
1828         }
1829 
1830         /**
1831          * Sets the {@link SigningCertificateLineage} to use with the v3 signature scheme. This
1832          * structure provides proof of signing certificate rotation linking {@link SignerConfig}
1833          * objects to previous ones.
1834          */
setSigningCertificateLineage( SigningCertificateLineage signingCertificateLineage)1835         public Builder setSigningCertificateLineage(
1836                 SigningCertificateLineage signingCertificateLineage) {
1837             if (signingCertificateLineage != null) {
1838                 mV3SigningEnabled = true;
1839                 mSigningCertificateLineage = signingCertificateLineage;
1840             }
1841             return this;
1842         }
1843     }
1844 }
1845