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