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