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