1 /* 2 * Copyright (C) 2018 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.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; 20 21 import com.android.apksig.apk.ApkFormatException; 22 import com.android.apksig.apk.ApkUtils; 23 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 24 import com.android.apksig.internal.apk.SignatureAlgorithm; 25 import com.android.apksig.internal.apk.SignatureInfo; 26 import com.android.apksig.internal.apk.v3.V3SchemeConstants; 27 import com.android.apksig.internal.apk.v3.V3SchemeSigner; 28 import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage; 29 import com.android.apksig.internal.apk.v3.V3SigningCertificateLineage.SigningCertificateNode; 30 import com.android.apksig.internal.util.AndroidSdkVersion; 31 import com.android.apksig.internal.util.ByteBufferUtils; 32 import com.android.apksig.internal.util.Pair; 33 import com.android.apksig.internal.util.RandomAccessFileDataSink; 34 import com.android.apksig.util.DataSink; 35 import com.android.apksig.util.DataSource; 36 import com.android.apksig.util.DataSources; 37 import com.android.apksig.zip.ZipFormatException; 38 39 import java.io.File; 40 import java.io.IOException; 41 import java.io.RandomAccessFile; 42 import java.nio.ByteBuffer; 43 import java.nio.ByteOrder; 44 import java.security.InvalidKeyException; 45 import java.security.NoSuchAlgorithmException; 46 import java.security.PrivateKey; 47 import java.security.PublicKey; 48 import java.security.SignatureException; 49 import java.security.cert.CertificateEncodingException; 50 import java.security.cert.X509Certificate; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collections; 54 import java.util.List; 55 56 /** 57 * APK Signer Lineage. 58 * 59 * <p>The signer lineage contains a history of signing certificates with each ancestor attesting to 60 * the validity of its descendant. Each additional descendant represents a new identity that can be 61 * used to sign an APK, and each generation has accompanying attributes which represent how the 62 * APK would like to view the older signing certificates, specifically how they should be trusted in 63 * certain situations. 64 * 65 * <p> Its primary use is to enable APK Signing Certificate Rotation. The Android platform verifies 66 * the APK Signer Lineage, and if the current signing certificate for the APK is in the Signer 67 * Lineage, and the Lineage contains the certificate the platform associates with the APK, it will 68 * allow upgrades to the new certificate. 69 * 70 * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a> 71 */ 72 public class SigningCertificateLineage { 73 74 public final static int MAGIC = 0x3eff39d1; 75 76 private final static int FIRST_VERSION = 1; 77 78 private static final int CURRENT_VERSION = FIRST_VERSION; 79 80 /** accept data from already installed pkg with this cert */ 81 private static final int PAST_CERT_INSTALLED_DATA = 1; 82 83 /** accept sharedUserId with pkg with this cert */ 84 private static final int PAST_CERT_SHARED_USER_ID = 2; 85 86 /** grant SIGNATURE permissions to pkgs with this cert */ 87 private static final int PAST_CERT_PERMISSION = 4; 88 89 /** 90 * Enable updates back to this certificate. WARNING: this effectively removes any benefit of 91 * signing certificate changes, since a compromised key could retake control of an app even 92 * after change, and should only be used if there is a problem encountered when trying to ditch 93 * an older cert. 94 */ 95 private static final int PAST_CERT_ROLLBACK = 8; 96 97 /** 98 * Preserve authenticator module-based access in AccountManager gated by signing certificate. 99 */ 100 private static final int PAST_CERT_AUTH = 16; 101 102 private final int mMinSdkVersion; 103 104 /** 105 * The signing lineage is just a list of nodes, with the first being the original signing 106 * certificate and the most recent being the one with which the APK is to actually be signed. 107 */ 108 private final List<SigningCertificateNode> mSigningLineage; 109 SigningCertificateLineage(int minSdkVersion, List<SigningCertificateNode> list)110 private SigningCertificateLineage(int minSdkVersion, List<SigningCertificateNode> list) { 111 mMinSdkVersion = minSdkVersion; 112 mSigningLineage = list; 113 } 114 createSigningLineage( int minSdkVersion, SignerConfig parent, SignerCapabilities parentCapabilities, SignerConfig child, SignerCapabilities childCapabilities)115 private static SigningCertificateLineage createSigningLineage( 116 int minSdkVersion, SignerConfig parent, SignerCapabilities parentCapabilities, 117 SignerConfig child, SignerCapabilities childCapabilities) 118 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 119 SignatureException { 120 SigningCertificateLineage signingCertificateLineage = 121 new SigningCertificateLineage(minSdkVersion, new ArrayList<>()); 122 signingCertificateLineage = 123 signingCertificateLineage.spawnFirstDescendant(parent, parentCapabilities); 124 return signingCertificateLineage.spawnDescendant(parent, child, childCapabilities); 125 } 126 readFromBytes(byte[] lineageBytes)127 public static SigningCertificateLineage readFromBytes(byte[] lineageBytes) 128 throws IOException { 129 return readFromDataSource(DataSources.asDataSource(ByteBuffer.wrap(lineageBytes))); 130 } 131 readFromFile(File file)132 public static SigningCertificateLineage readFromFile(File file) 133 throws IOException { 134 if (file == null) { 135 throw new NullPointerException("file == null"); 136 } 137 RandomAccessFile inputFile = new RandomAccessFile(file, "r"); 138 return readFromDataSource(DataSources.asDataSource(inputFile)); 139 } 140 readFromDataSource(DataSource dataSource)141 public static SigningCertificateLineage readFromDataSource(DataSource dataSource) 142 throws IOException { 143 if (dataSource == null) { 144 throw new NullPointerException("dataSource == null"); 145 } 146 ByteBuffer inBuff = dataSource.getByteBuffer(0, (int) dataSource.size()); 147 inBuff.order(ByteOrder.LITTLE_ENDIAN); 148 return read(inBuff); 149 } 150 151 /** 152 * Extracts a Signing Certificate Lineage from a v3 signer proof-of-rotation attribute. 153 * 154 * <note> 155 * this may not give a complete representation of an APK's signing certificate history, 156 * since the APK may have multiple signers corresponding to different platform versions. 157 * Use <code> readFromApkFile</code> to handle this case. 158 * </note> 159 * @param attrValue 160 */ readFromV3AttributeValue(byte[] attrValue)161 public static SigningCertificateLineage readFromV3AttributeValue(byte[] attrValue) 162 throws IOException { 163 List<SigningCertificateNode> parsedLineage = 164 V3SigningCertificateLineage.readSigningCertificateLineage(ByteBuffer.wrap( 165 attrValue).order(ByteOrder.LITTLE_ENDIAN)); 166 int minSdkVersion = calculateMinSdkVersion(parsedLineage); 167 return new SigningCertificateLineage(minSdkVersion, parsedLineage); 168 } 169 170 /** 171 * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 172 * signature block of the provided APK File. 173 * 174 * @throws IllegalArgumentException if the provided APK does not contain a V3 signature block, 175 * or if the V3 signature block does not contain a valid lineage. 176 */ readFromApkFile(File apkFile)177 public static SigningCertificateLineage readFromApkFile(File apkFile) 178 throws IOException, ApkFormatException { 179 try (RandomAccessFile f = new RandomAccessFile(apkFile, "r")) { 180 DataSource apk = DataSources.asDataSource(f, 0, f.length()); 181 return readFromApkDataSource(apk); 182 } 183 } 184 185 /** 186 * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3 187 * signature block of the provided APK DataSource. 188 * 189 * @throws IllegalArgumentException if the provided APK does not contain a V3 signature block, 190 * or if the V3 signature block does not contain a valid lineage. 191 */ readFromApkDataSource(DataSource apk)192 public static SigningCertificateLineage readFromApkDataSource(DataSource apk) 193 throws IOException, ApkFormatException { 194 ApkUtils.ZipSections zipSections; 195 try { 196 zipSections = ApkUtils.findZipSections(apk); 197 } catch (ZipFormatException e) { 198 throw new ApkFormatException(e.getMessage()); 199 } 200 201 List<SignatureInfo> signatureInfoList = new ArrayList<>(); 202 try { 203 ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( 204 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V31); 205 signatureInfoList.add( 206 ApkSigningBlockUtils.findSignature(apk, zipSections, 207 V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID, result)); 208 } 209 catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { 210 // This could be expected if there's only a V3 signature block. 211 } 212 try { 213 ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result( 214 ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3); 215 signatureInfoList.add( 216 ApkSigningBlockUtils.findSignature(apk, zipSections, 217 V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result)); 218 } 219 catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) { 220 // This could be expected if the provided APK is not signed with the v3 signature scheme 221 } 222 if (signatureInfoList.isEmpty()) { 223 throw new IllegalArgumentException( 224 "The provided APK does not contain a valid V3 signature block."); 225 } 226 227 List<SigningCertificateLineage> lineages = new ArrayList<>(1); 228 for (SignatureInfo signatureInfo : signatureInfoList) { 229 // FORMAT: 230 // * length-prefixed sequence of length-prefixed signers: 231 // * length-prefixed signed data 232 // * minSDK 233 // * maxSDK 234 // * length-prefixed sequence of length-prefixed signatures 235 // * length-prefixed public key 236 ByteBuffer signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); 237 while (signers.hasRemaining()) { 238 ByteBuffer signer = getLengthPrefixedSlice(signers); 239 ByteBuffer signedData = getLengthPrefixedSlice(signer); 240 try { 241 SigningCertificateLineage lineage = readFromSignedData(signedData); 242 lineages.add(lineage); 243 } catch (IllegalArgumentException ignored) { 244 // The current signer block does not contain a valid lineage, but it is possible 245 // another block will. 246 } 247 } 248 } 249 250 SigningCertificateLineage result; 251 if (lineages.isEmpty()) { 252 throw new IllegalArgumentException( 253 "The provided APK does not contain a valid lineage."); 254 } else if (lineages.size() > 1) { 255 result = consolidateLineages(lineages); 256 } else { 257 result = lineages.get(0); 258 } 259 return result; 260 } 261 262 /** 263 * Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the provided 264 * signed data portion of a signer in a V3 signature block. 265 * 266 * @throws IllegalArgumentException if the provided signed data does not contain a valid 267 * lineage. 268 */ readFromSignedData(ByteBuffer signedData)269 public static SigningCertificateLineage readFromSignedData(ByteBuffer signedData) 270 throws IOException, ApkFormatException { 271 // FORMAT: 272 // * length-prefixed sequence of length-prefixed digests: 273 // * length-prefixed sequence of certificates: 274 // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). 275 // * uint-32: minSdkVersion 276 // * uint-32: maxSdkVersion 277 // * length-prefixed sequence of length-prefixed additional attributes: 278 // * uint32: ID 279 // * (length - 4) bytes: value 280 // * uint32: Proof-of-rotation ID: 0x3ba06f8c 281 // * length-prefixed proof-of-rotation structure 282 // consume the digests through the maxSdkVersion to reach the lineage in the attributes 283 getLengthPrefixedSlice(signedData); 284 getLengthPrefixedSlice(signedData); 285 signedData.getInt(); 286 signedData.getInt(); 287 // iterate over the additional attributes adding any lineages to the List 288 ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData); 289 List<SigningCertificateLineage> lineages = new ArrayList<>(1); 290 while (additionalAttributes.hasRemaining()) { 291 ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes); 292 int id = attribute.getInt(); 293 if (id == V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID) { 294 byte[] value = ByteBufferUtils.toByteArray(attribute); 295 SigningCertificateLineage lineage = readFromV3AttributeValue(value); 296 lineages.add(lineage); 297 } 298 } 299 SigningCertificateLineage result; 300 // There should only be a single attribute with the lineage, but if there are multiple then 301 // attempt to consolidate the lineages. 302 if (lineages.isEmpty()) { 303 throw new IllegalArgumentException("The signed data does not contain a valid lineage."); 304 } else if (lineages.size() > 1) { 305 result = consolidateLineages(lineages); 306 } else { 307 result = lineages.get(0); 308 } 309 return result; 310 } 311 getBytes()312 public byte[] getBytes() { 313 return write().array(); 314 } 315 writeToFile(File file)316 public void writeToFile(File file) throws IOException { 317 if (file == null) { 318 throw new NullPointerException("file == null"); 319 } 320 RandomAccessFile outputFile = new RandomAccessFile(file, "rw"); 321 writeToDataSink(new RandomAccessFileDataSink(outputFile)); 322 } 323 writeToDataSink(DataSink dataSink)324 public void writeToDataSink(DataSink dataSink) throws IOException { 325 if (dataSink == null) { 326 throw new NullPointerException("dataSink == null"); 327 } 328 dataSink.consume(write()); 329 } 330 331 /** 332 * Add a new signing certificate to the lineage. This effectively creates a signing certificate 333 * rotation event, forcing APKs which include this lineage to be signed by the new signer. The 334 * flags associated with the new signer are set to a default value. 335 * 336 * @param parent current signing certificate of the containing APK 337 * @param child new signing certificate which will sign the APK contents 338 */ spawnDescendant(SignerConfig parent, SignerConfig child)339 public SigningCertificateLineage spawnDescendant(SignerConfig parent, SignerConfig child) 340 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 341 SignatureException { 342 if (parent == null || child == null) { 343 throw new NullPointerException("can't add new descendant to lineage with null inputs"); 344 } 345 SignerCapabilities signerCapabilities = new SignerCapabilities.Builder().build(); 346 return spawnDescendant(parent, child, signerCapabilities); 347 } 348 349 /** 350 * Add a new signing certificate to the lineage. This effectively creates a signing certificate 351 * rotation event, forcing APKs which include this lineage to be signed by the new signer. 352 * 353 * @param parent current signing certificate of the containing APK 354 * @param child new signing certificate which will sign the APK contents 355 * @param childCapabilities flags 356 */ spawnDescendant( SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities)357 public SigningCertificateLineage spawnDescendant( 358 SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities) 359 throws CertificateEncodingException, InvalidKeyException, 360 NoSuchAlgorithmException, SignatureException { 361 if (parent == null) { 362 throw new NullPointerException("parent == null"); 363 } 364 if (child == null) { 365 throw new NullPointerException("child == null"); 366 } 367 if (childCapabilities == null) { 368 throw new NullPointerException("childCapabilities == null"); 369 } 370 if (mSigningLineage.isEmpty()) { 371 throw new IllegalArgumentException("Cannot spawn descendant signing certificate on an" 372 + " empty SigningCertificateLineage: no parent node"); 373 } 374 375 // make sure that the parent matches our newest generation (leaf node/sink) 376 SigningCertificateNode currentGeneration = mSigningLineage.get(mSigningLineage.size() - 1); 377 if (!Arrays.equals(currentGeneration.signingCert.getEncoded(), 378 parent.getCertificate().getEncoded())) { 379 throw new IllegalArgumentException("SignerConfig Certificate containing private key" 380 + " to sign the new SigningCertificateLineage record does not match the" 381 + " existing most recent record"); 382 } 383 384 // create data to be signed, including the algorithm we're going to use 385 SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(parent); 386 ByteBuffer prefixedSignedData = ByteBuffer.wrap( 387 V3SigningCertificateLineage.encodeSignedData( 388 child.getCertificate(), signatureAlgorithm.getId())); 389 prefixedSignedData.position(4); 390 ByteBuffer signedDataBuffer = ByteBuffer.allocate(prefixedSignedData.remaining()); 391 signedDataBuffer.put(prefixedSignedData); 392 byte[] signedData = signedDataBuffer.array(); 393 394 // create SignerConfig to do the signing 395 List<X509Certificate> certificates = new ArrayList<>(1); 396 certificates.add(parent.getCertificate()); 397 ApkSigningBlockUtils.SignerConfig newSignerConfig = 398 new ApkSigningBlockUtils.SignerConfig(); 399 newSignerConfig.privateKey = parent.getPrivateKey(); 400 newSignerConfig.certificates = certificates; 401 newSignerConfig.signatureAlgorithms = Collections.singletonList(signatureAlgorithm); 402 403 // sign it 404 List<Pair<Integer, byte[]>> signatures = 405 ApkSigningBlockUtils.generateSignaturesOverData(newSignerConfig, signedData); 406 407 // finally, add it to our lineage 408 SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(signatures.get(0).getFirst()); 409 byte[] signature = signatures.get(0).getSecond(); 410 currentGeneration.sigAlgorithm = sigAlgorithm; 411 SigningCertificateNode childNode = 412 new SigningCertificateNode( 413 child.getCertificate(), sigAlgorithm, null, 414 signature, childCapabilities.getFlags()); 415 List<SigningCertificateNode> lineageCopy = new ArrayList<>(mSigningLineage); 416 lineageCopy.add(childNode); 417 return new SigningCertificateLineage(mMinSdkVersion, lineageCopy); 418 } 419 420 /** 421 * The number of signing certificates in the lineage, including the current signer, which means 422 * this value can also be used to V2determine the number of signing certificate rotations by 423 * subtracting 1. 424 */ size()425 public int size() { 426 return mSigningLineage.size(); 427 } 428 getSignatureAlgorithm(SignerConfig parent)429 private SignatureAlgorithm getSignatureAlgorithm(SignerConfig parent) 430 throws InvalidKeyException { 431 PublicKey publicKey = parent.getCertificate().getPublicKey(); 432 433 // TODO switch to one signature algorithm selection, or add support for multiple algorithms 434 List<SignatureAlgorithm> algorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms( 435 publicKey, mMinSdkVersion, false /* verityEnabled */, 436 false /* deterministicDsaSigning */); 437 return algorithms.get(0); 438 } 439 spawnFirstDescendant( SignerConfig parent, SignerCapabilities signerCapabilities)440 private SigningCertificateLineage spawnFirstDescendant( 441 SignerConfig parent, SignerCapabilities signerCapabilities) { 442 if (!mSigningLineage.isEmpty()) { 443 throw new IllegalStateException("SigningCertificateLineage already has its first node"); 444 } 445 446 // check to make sure that the public key for the first node is acceptable for our minSdk 447 try { 448 getSignatureAlgorithm(parent); 449 } catch (InvalidKeyException e) { 450 throw new IllegalArgumentException("Algorithm associated with first signing certificate" 451 + " invalid on desired platform versions", e); 452 } 453 454 // create "fake" signed data (there will be no signature over it, since there is no parent 455 SigningCertificateNode firstNode = new SigningCertificateNode( 456 parent.getCertificate(), null, null, new byte[0], signerCapabilities.getFlags()); 457 return new SigningCertificateLineage(mMinSdkVersion, Collections.singletonList(firstNode)); 458 } 459 read(ByteBuffer inputByteBuffer)460 private static SigningCertificateLineage read(ByteBuffer inputByteBuffer) 461 throws IOException { 462 ApkSigningBlockUtils.checkByteOrderLittleEndian(inputByteBuffer); 463 if (inputByteBuffer.remaining() < 8) { 464 throw new IllegalArgumentException( 465 "Improper SigningCertificateLineage format: insufficient data for header."); 466 } 467 468 if (inputByteBuffer.getInt() != MAGIC) { 469 throw new IllegalArgumentException( 470 "Improper SigningCertificateLineage format: MAGIC header mismatch."); 471 } 472 return read(inputByteBuffer, inputByteBuffer.getInt()); 473 } 474 read(ByteBuffer inputByteBuffer, int version)475 private static SigningCertificateLineage read(ByteBuffer inputByteBuffer, int version) 476 throws IOException { 477 switch (version) { 478 case FIRST_VERSION: 479 try { 480 List<SigningCertificateNode> nodes = 481 V3SigningCertificateLineage.readSigningCertificateLineage( 482 getLengthPrefixedSlice(inputByteBuffer)); 483 int minSdkVersion = calculateMinSdkVersion(nodes); 484 return new SigningCertificateLineage(minSdkVersion, nodes); 485 } catch (ApkFormatException e) { 486 // unable to get a proper length-prefixed lineage slice 487 throw new IOException("Unable to read list of signing certificate nodes in " 488 + "SigningCertificateLineage", e); 489 } 490 default: 491 throw new IllegalArgumentException( 492 "Improper SigningCertificateLineage format: unrecognized version."); 493 } 494 } 495 calculateMinSdkVersion(List<SigningCertificateNode> nodes)496 private static int calculateMinSdkVersion(List<SigningCertificateNode> nodes) { 497 if (nodes == null) { 498 throw new IllegalArgumentException("Can't calculate minimum SDK version of null nodes"); 499 } 500 int minSdkVersion = AndroidSdkVersion.P; // lineage introduced in P 501 for (SigningCertificateNode node : nodes) { 502 if (node.sigAlgorithm != null) { 503 int nodeMinSdkVersion = node.sigAlgorithm.getMinSdkVersion(); 504 if (nodeMinSdkVersion > minSdkVersion) { 505 minSdkVersion = nodeMinSdkVersion; 506 } 507 } 508 } 509 return minSdkVersion; 510 } 511 write()512 private ByteBuffer write() { 513 byte[] encodedLineage = 514 V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); 515 int payloadSize = 4 + 4 + 4 + encodedLineage.length; 516 ByteBuffer result = ByteBuffer.allocate(payloadSize); 517 result.order(ByteOrder.LITTLE_ENDIAN); 518 result.putInt(MAGIC); 519 result.putInt(CURRENT_VERSION); 520 result.putInt(encodedLineage.length); 521 result.put(encodedLineage); 522 result.flip(); 523 return result; 524 } 525 encodeSigningCertificateLineage()526 public byte[] encodeSigningCertificateLineage() { 527 return V3SigningCertificateLineage.encodeSigningCertificateLineage(mSigningLineage); 528 } 529 sortSignerConfigs( List<DefaultApkSignerEngine.SignerConfig> signerConfigs)530 public List<DefaultApkSignerEngine.SignerConfig> sortSignerConfigs( 531 List<DefaultApkSignerEngine.SignerConfig> signerConfigs) { 532 if (signerConfigs == null) { 533 throw new NullPointerException("signerConfigs == null"); 534 } 535 536 // not the most elegant sort, but we expect signerConfigs to be quite small (1 or 2 signers 537 // in most cases) and likely already sorted, so not worth the overhead of doing anything 538 // fancier 539 List<DefaultApkSignerEngine.SignerConfig> sortedSignerConfigs = 540 new ArrayList<>(signerConfigs.size()); 541 for (int i = 0; i < mSigningLineage.size(); i++) { 542 for (int j = 0; j < signerConfigs.size(); j++) { 543 DefaultApkSignerEngine.SignerConfig config = signerConfigs.get(j); 544 if (mSigningLineage.get(i).signingCert.equals(config.getCertificates().get(0))) { 545 sortedSignerConfigs.add(config); 546 break; 547 } 548 } 549 } 550 if (sortedSignerConfigs.size() != signerConfigs.size()) { 551 throw new IllegalArgumentException("SignerConfigs supplied which are not present in the" 552 + " SigningCertificateLineage"); 553 } 554 return sortedSignerConfigs; 555 } 556 557 /** 558 * Returns the SignerCapabilities for the signer in the lineage that matches the provided 559 * config. 560 */ getSignerCapabilities(SignerConfig config)561 public SignerCapabilities getSignerCapabilities(SignerConfig config) { 562 if (config == null) { 563 throw new NullPointerException("config == null"); 564 } 565 566 X509Certificate cert = config.getCertificate(); 567 return getSignerCapabilities(cert); 568 } 569 570 /** 571 * Returns the SignerCapabilities for the signer in the lineage that matches the provided 572 * certificate. 573 */ getSignerCapabilities(X509Certificate cert)574 public SignerCapabilities getSignerCapabilities(X509Certificate cert) { 575 if (cert == null) { 576 throw new NullPointerException("cert == null"); 577 } 578 579 for (int i = 0; i < mSigningLineage.size(); i++) { 580 SigningCertificateNode lineageNode = mSigningLineage.get(i); 581 if (lineageNode.signingCert.equals(cert)) { 582 int flags = lineageNode.flags; 583 return new SignerCapabilities.Builder(flags).build(); 584 } 585 } 586 587 // the provided signer certificate was not found in the lineage 588 throw new IllegalArgumentException("Certificate (" + cert.getSubjectDN() 589 + ") not found in the SigningCertificateLineage"); 590 } 591 592 /** 593 * Updates the SignerCapabilities for the signer in the lineage that matches the provided 594 * config. Only those capabilities that have been modified through the setXX methods will be 595 * updated for the signer to prevent unset default values from being applied. 596 */ updateSignerCapabilities(SignerConfig config, SignerCapabilities capabilities)597 public void updateSignerCapabilities(SignerConfig config, SignerCapabilities capabilities) { 598 if (config == null) { 599 throw new NullPointerException("config == null"); 600 } 601 602 X509Certificate cert = config.getCertificate(); 603 for (int i = 0; i < mSigningLineage.size(); i++) { 604 SigningCertificateNode lineageNode = mSigningLineage.get(i); 605 if (lineageNode.signingCert.equals(cert)) { 606 int flags = lineageNode.flags; 607 SignerCapabilities newCapabilities = new SignerCapabilities.Builder( 608 flags).setCallerConfiguredCapabilities(capabilities).build(); 609 lineageNode.flags = newCapabilities.getFlags(); 610 return; 611 } 612 } 613 614 // the provided signer config was not found in the lineage 615 throw new IllegalArgumentException("Certificate (" + cert.getSubjectDN() 616 + ") not found in the SigningCertificateLineage"); 617 } 618 619 /** 620 * Returns a list containing all of the certificates in the lineage. 621 */ getCertificatesInLineage()622 public List<X509Certificate> getCertificatesInLineage() { 623 List<X509Certificate> certs = new ArrayList<>(); 624 for (int i = 0; i < mSigningLineage.size(); i++) { 625 X509Certificate cert = mSigningLineage.get(i).signingCert; 626 certs.add(cert); 627 } 628 return certs; 629 } 630 631 /** 632 * Returns {@code true} if the specified config is in the lineage. 633 */ isSignerInLineage(SignerConfig config)634 public boolean isSignerInLineage(SignerConfig config) { 635 if (config == null) { 636 throw new NullPointerException("config == null"); 637 } 638 639 X509Certificate cert = config.getCertificate(); 640 return isCertificateInLineage(cert); 641 } 642 643 /** 644 * Returns {@code true} if the specified certificate is in the lineage. 645 */ isCertificateInLineage(X509Certificate cert)646 public boolean isCertificateInLineage(X509Certificate cert) { 647 if (cert == null) { 648 throw new NullPointerException("cert == null"); 649 } 650 651 for (int i = 0; i < mSigningLineage.size(); i++) { 652 if (mSigningLineage.get(i).signingCert.equals(cert)) { 653 return true; 654 } 655 } 656 return false; 657 } 658 calculateDefaultFlags()659 private static int calculateDefaultFlags() { 660 return PAST_CERT_INSTALLED_DATA | PAST_CERT_PERMISSION 661 | PAST_CERT_SHARED_USER_ID | PAST_CERT_AUTH; 662 } 663 664 /** 665 * Returns a new SigingCertificateLineage which terminates at the node corresponding to the 666 * given certificate. This is useful in the event of rotating to a new signing algorithm that 667 * is only supported on some platform versions. It enables a v3 signature to be generated using 668 * this signing certificate and the shortened proof-of-rotation record from this sub lineage in 669 * conjunction with the appropriate SDK version values. 670 * 671 * @param x509Certificate the signing certificate for which to search 672 * @return A new SigningCertificateLineage if the given certificate is present. 673 * 674 * @throws IllegalArgumentException if the provided certificate is not in the lineage. 675 */ getSubLineage(X509Certificate x509Certificate)676 public SigningCertificateLineage getSubLineage(X509Certificate x509Certificate) { 677 if (x509Certificate == null) { 678 throw new NullPointerException("x509Certificate == null"); 679 } 680 for (int i = 0; i < mSigningLineage.size(); i++) { 681 if (mSigningLineage.get(i).signingCert.equals(x509Certificate)) { 682 return new SigningCertificateLineage( 683 mMinSdkVersion, new ArrayList<>(mSigningLineage.subList(0, i + 1))); 684 } 685 } 686 687 // looks like we didn't find the cert, 688 throw new IllegalArgumentException("Certificate not found in SigningCertificateLineage"); 689 } 690 691 /** 692 * Consolidates all of the lineages found in an APK into one lineage, which is the longest one. 693 * In so doing, it also checks that all of the smaller lineages are contained in the largest, 694 * and that they properly cover the desired platform ranges. 695 * 696 * An APK may contain multiple lineages, one for each signer, which correspond to different 697 * supported platform versions. In this event, the lineage(s) from the earlier platform 698 * version(s) need to be present in the most recent (longest) one to make sure that when a 699 * platform version changes. 700 * 701 * <note> This does not verify that the largest lineage corresponds to the most recent supported 702 * platform version. That check requires is performed during v3 verification. </note> 703 */ consolidateLineages( List<SigningCertificateLineage> lineages)704 public static SigningCertificateLineage consolidateLineages( 705 List<SigningCertificateLineage> lineages) { 706 if (lineages == null || lineages.isEmpty()) { 707 return null; 708 } 709 int largestIndex = 0; 710 int maxSize = 0; 711 712 // determine the longest chain 713 for (int i = 0; i < lineages.size(); i++) { 714 int curSize = lineages.get(i).size(); 715 if (curSize > maxSize) { 716 largestIndex = i; 717 maxSize = curSize; 718 } 719 } 720 721 List<SigningCertificateNode> largestList = lineages.get(largestIndex).mSigningLineage; 722 // make sure all other lineages fit into this one, with the same capabilities 723 for (int i = 0; i < lineages.size(); i++) { 724 if (i == largestIndex) { 725 continue; 726 } 727 List<SigningCertificateNode> underTest = lineages.get(i).mSigningLineage; 728 if (!underTest.equals(largestList.subList(0, underTest.size()))) { 729 throw new IllegalArgumentException("Inconsistent SigningCertificateLineages. " 730 + "Not all lineages are subsets of each other."); 731 } 732 } 733 734 // if we've made it this far, they all check out, so just return the largest 735 return lineages.get(largestIndex); 736 } 737 738 /** 739 * Representation of the capabilities the APK would like to grant to its old signing 740 * certificates. The {@code SigningCertificateLineage} provides two conceptual data structures. 741 * 1) proof of rotation - Evidence that other parties can trust an APK's current signing 742 * certificate if they trust an older one in this lineage 743 * 2) self-trust - certain capabilities may have been granted by an APK to other parties based 744 * on its own signing certificate. When it changes its signing certificate it may want to 745 * allow the other parties to retain those capabilities. 746 * {@code SignerCapabilties} provides a representation of the second structure. 747 * 748 * <p>Use {@link Builder} to obtain configuration instances. 749 */ 750 public static class SignerCapabilities { 751 private final int mFlags; 752 753 private final int mCallerConfiguredFlags; 754 SignerCapabilities(int flags)755 private SignerCapabilities(int flags) { 756 this(flags, 0); 757 } 758 SignerCapabilities(int flags, int callerConfiguredFlags)759 private SignerCapabilities(int flags, int callerConfiguredFlags) { 760 mFlags = flags; 761 mCallerConfiguredFlags = callerConfiguredFlags; 762 } 763 getFlags()764 private int getFlags() { 765 return mFlags; 766 } 767 768 /** 769 * Returns {@code true} if the capabilities of this object match those of the provided 770 * object. 771 */ equals(SignerCapabilities other)772 public boolean equals(SignerCapabilities other) { 773 return this.mFlags == other.mFlags; 774 } 775 776 /** 777 * Returns {@code true} if this object has the installed data capability. 778 */ hasInstalledData()779 public boolean hasInstalledData() { 780 return (mFlags & PAST_CERT_INSTALLED_DATA) != 0; 781 } 782 783 /** 784 * Returns {@code true} if this object has the shared UID capability. 785 */ hasSharedUid()786 public boolean hasSharedUid() { 787 return (mFlags & PAST_CERT_SHARED_USER_ID) != 0; 788 } 789 790 /** 791 * Returns {@code true} if this object has the permission capability. 792 */ hasPermission()793 public boolean hasPermission() { 794 return (mFlags & PAST_CERT_PERMISSION) != 0; 795 } 796 797 /** 798 * Returns {@code true} if this object has the rollback capability. 799 */ hasRollback()800 public boolean hasRollback() { 801 return (mFlags & PAST_CERT_ROLLBACK) != 0; 802 } 803 804 /** 805 * Returns {@code true} if this object has the auth capability. 806 */ hasAuth()807 public boolean hasAuth() { 808 return (mFlags & PAST_CERT_AUTH) != 0; 809 } 810 811 /** 812 * Builder of {@link SignerCapabilities} instances. 813 */ 814 public static class Builder { 815 private int mFlags; 816 817 private int mCallerConfiguredFlags; 818 819 /** 820 * Constructs a new {@code Builder}. 821 */ Builder()822 public Builder() { 823 mFlags = calculateDefaultFlags(); 824 } 825 826 /** 827 * Constructs a new {@code Builder} with the initial capabilities set to the provided 828 * flags. 829 */ Builder(int flags)830 public Builder(int flags) { 831 mFlags = flags; 832 } 833 834 /** 835 * Set the {@code PAST_CERT_INSTALLED_DATA} flag in this capabilities object. This flag 836 * is used by the platform to determine if installed data associated with previous 837 * signing certificate should be trusted. In particular, this capability is required to 838 * perform signing certificate rotation during an upgrade on-device. Without it, the 839 * platform will not permit the app data from the old signing certificate to 840 * propagate to the new version. Typically, this flag should be set to enable signing 841 * certificate rotation, and may be unset later when the app developer is satisfied that 842 * their install base is as migrated as it will be. 843 */ setInstalledData(boolean enabled)844 public Builder setInstalledData(boolean enabled) { 845 mCallerConfiguredFlags |= PAST_CERT_INSTALLED_DATA; 846 if (enabled) { 847 mFlags |= PAST_CERT_INSTALLED_DATA; 848 } else { 849 mFlags &= ~PAST_CERT_INSTALLED_DATA; 850 } 851 return this; 852 } 853 854 /** 855 * Set the {@code PAST_CERT_SHARED_USER_ID} flag in this capabilities object. This flag 856 * is used by the platform to determine if this app is willing to be sharedUid with 857 * other apps which are still signed with the associated signing certificate. This is 858 * useful in situations where sharedUserId apps would like to change their signing 859 * certificate, but can't guarantee the order of updates to those apps. 860 */ setSharedUid(boolean enabled)861 public Builder setSharedUid(boolean enabled) { 862 mCallerConfiguredFlags |= PAST_CERT_SHARED_USER_ID; 863 if (enabled) { 864 mFlags |= PAST_CERT_SHARED_USER_ID; 865 } else { 866 mFlags &= ~PAST_CERT_SHARED_USER_ID; 867 } 868 return this; 869 } 870 871 /** 872 * Set the {@code PAST_CERT_PERMISSION} flag in this capabilities object. This flag 873 * is used by the platform to determine if this app is willing to grant SIGNATURE 874 * permissions to apps signed with the associated signing certificate. Without this 875 * capability, an application signed with the older certificate will not be granted the 876 * SIGNATURE permissions defined by this app. In addition, if multiple apps define the 877 * same SIGNATURE permission, the second one the platform sees will not be installable 878 * if this capability is not set and the signing certificates differ. 879 */ setPermission(boolean enabled)880 public Builder setPermission(boolean enabled) { 881 mCallerConfiguredFlags |= PAST_CERT_PERMISSION; 882 if (enabled) { 883 mFlags |= PAST_CERT_PERMISSION; 884 } else { 885 mFlags &= ~PAST_CERT_PERMISSION; 886 } 887 return this; 888 } 889 890 /** 891 * Set the {@code PAST_CERT_ROLLBACK} flag in this capabilities object. This flag 892 * is used by the platform to determine if this app is willing to upgrade to a new 893 * version that is signed by one of its past signing certificates. 894 * 895 * <note> WARNING: this effectively removes any benefit of signing certificate changes, 896 * since a compromised key could retake control of an app even after change, and should 897 * only be used if there is a problem encountered when trying to ditch an older cert 898 * </note> 899 */ setRollback(boolean enabled)900 public Builder setRollback(boolean enabled) { 901 mCallerConfiguredFlags |= PAST_CERT_ROLLBACK; 902 if (enabled) { 903 mFlags |= PAST_CERT_ROLLBACK; 904 } else { 905 mFlags &= ~PAST_CERT_ROLLBACK; 906 } 907 return this; 908 } 909 910 /** 911 * Set the {@code PAST_CERT_AUTH} flag in this capabilities object. This flag 912 * is used by the platform to determine whether or not privileged access based on 913 * authenticator module signing certificates should be granted. 914 */ setAuth(boolean enabled)915 public Builder setAuth(boolean enabled) { 916 mCallerConfiguredFlags |= PAST_CERT_AUTH; 917 if (enabled) { 918 mFlags |= PAST_CERT_AUTH; 919 } else { 920 mFlags &= ~PAST_CERT_AUTH; 921 } 922 return this; 923 } 924 925 /** 926 * Applies the capabilities that were explicitly set in the provided capabilities object 927 * to this builder. Any values that were not set will not be applied to this builder 928 * to prevent unintentinoally setting a capability back to a default value. 929 */ setCallerConfiguredCapabilities(SignerCapabilities capabilities)930 public Builder setCallerConfiguredCapabilities(SignerCapabilities capabilities) { 931 // The mCallerConfiguredFlags should have a bit set for each capability that was 932 // set by a caller. If a capability was explicitly set then the corresponding bit 933 // in mCallerConfiguredFlags should be set. This allows the provided capabilities 934 // to take effect for those set by the caller while those that were not set will 935 // be cleared by the bitwise and and the initial value for the builder will remain. 936 mFlags = (mFlags & ~capabilities.mCallerConfiguredFlags) | 937 (capabilities.mFlags & capabilities.mCallerConfiguredFlags); 938 return this; 939 } 940 941 /** 942 * Returns a new {@code SignerConfig} instance configured based on the configuration of 943 * this builder. 944 */ build()945 public SignerCapabilities build() { 946 return new SignerCapabilities(mFlags, mCallerConfiguredFlags); 947 } 948 } 949 } 950 951 /** 952 * Configuration of a signer. Used to add a new entry to the {@link SigningCertificateLineage} 953 * 954 * <p>Use {@link Builder} to obtain configuration instances. 955 */ 956 public static class SignerConfig { 957 private final PrivateKey mPrivateKey; 958 private final X509Certificate mCertificate; 959 SignerConfig( PrivateKey privateKey, X509Certificate certificate)960 private SignerConfig( 961 PrivateKey privateKey, 962 X509Certificate certificate) { 963 mPrivateKey = privateKey; 964 mCertificate = certificate; 965 } 966 967 /** 968 * Returns the signing key of this signer. 969 */ getPrivateKey()970 public PrivateKey getPrivateKey() { 971 return mPrivateKey; 972 } 973 974 /** 975 * Returns the certificate(s) of this signer. The first certificate's public key corresponds 976 * to this signer's private key. 977 */ getCertificate()978 public X509Certificate getCertificate() { 979 return mCertificate; 980 } 981 982 /** 983 * Builder of {@link SignerConfig} instances. 984 */ 985 public static class Builder { 986 private final PrivateKey mPrivateKey; 987 private final X509Certificate mCertificate; 988 989 /** 990 * Constructs a new {@code Builder}. 991 * 992 * @param privateKey signing key 993 * @param certificate the X.509 certificate with a subject public key of the 994 * {@code privateKey}. 995 */ Builder( PrivateKey privateKey, X509Certificate certificate)996 public Builder( 997 PrivateKey privateKey, 998 X509Certificate certificate) { 999 mPrivateKey = privateKey; 1000 mCertificate = certificate; 1001 } 1002 1003 /** 1004 * Returns a new {@code SignerConfig} instance configured based on the configuration of 1005 * this builder. 1006 */ build()1007 public SignerConfig build() { 1008 return new SignerConfig( 1009 mPrivateKey, 1010 mCertificate); 1011 } 1012 } 1013 } 1014 1015 /** 1016 * Builder of {@link SigningCertificateLineage} instances. 1017 */ 1018 public static class Builder { 1019 private final SignerConfig mOriginalSignerConfig; 1020 private final SignerConfig mNewSignerConfig; 1021 private SignerCapabilities mOriginalCapabilities; 1022 private SignerCapabilities mNewCapabilities; 1023 private int mMinSdkVersion; 1024 /** 1025 * Constructs a new {@code Builder}. 1026 * 1027 * @param originalSignerConfig first signer in this lineage, parent of the next 1028 * @param newSignerConfig new signer in the lineage; the new signing key that the APK will 1029 * use 1030 */ Builder( SignerConfig originalSignerConfig, SignerConfig newSignerConfig)1031 public Builder( 1032 SignerConfig originalSignerConfig, 1033 SignerConfig newSignerConfig) { 1034 if (originalSignerConfig == null || newSignerConfig == null) { 1035 throw new NullPointerException("Can't pass null SignerConfigs when constructing a " 1036 + "new SigningCertificateLineage"); 1037 } 1038 mOriginalSignerConfig = originalSignerConfig; 1039 mNewSignerConfig = newSignerConfig; 1040 } 1041 1042 /** 1043 * Sets the minimum Android platform version (API Level) on which this lineage is expected 1044 * to validate. It is possible that newer signers in the lineage may not be recognized on 1045 * the given platform, but as long as an older signer is, the lineage can still be used to 1046 * sign an APK for the given platform. 1047 * 1048 * <note> By default, this value is set to the value for the 1049 * P release, since this structure was created for that release, and will also be set to 1050 * that value if a smaller one is specified. </note> 1051 */ setMinSdkVersion(int minSdkVersion)1052 public Builder setMinSdkVersion(int minSdkVersion) { 1053 mMinSdkVersion = minSdkVersion; 1054 return this; 1055 } 1056 1057 /** 1058 * Sets capabilities to give {@code mOriginalSignerConfig}. These capabilities allow an 1059 * older signing certificate to still be used in some situations on the platform even though 1060 * the APK is now being signed by a newer signing certificate. 1061 */ setOriginalCapabilities(SignerCapabilities signerCapabilities)1062 public Builder setOriginalCapabilities(SignerCapabilities signerCapabilities) { 1063 if (signerCapabilities == null) { 1064 throw new NullPointerException("signerCapabilities == null"); 1065 } 1066 mOriginalCapabilities = signerCapabilities; 1067 return this; 1068 } 1069 1070 /** 1071 * Sets capabilities to give {@code mNewSignerConfig}. These capabilities allow an 1072 * older signing certificate to still be used in some situations on the platform even though 1073 * the APK is now being signed by a newer signing certificate. By default, the new signer 1074 * will have all capabilities, so when first switching to a new signing certificate, these 1075 * capabilities have no effect, but they will act as the default level of trust when moving 1076 * to a new signing certificate. 1077 */ setNewCapabilities(SignerCapabilities signerCapabilities)1078 public Builder setNewCapabilities(SignerCapabilities signerCapabilities) { 1079 if (signerCapabilities == null) { 1080 throw new NullPointerException("signerCapabilities == null"); 1081 } 1082 mNewCapabilities = signerCapabilities; 1083 return this; 1084 } 1085 build()1086 public SigningCertificateLineage build() 1087 throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, 1088 SignatureException { 1089 if (mMinSdkVersion < AndroidSdkVersion.P) { 1090 mMinSdkVersion = AndroidSdkVersion.P; 1091 } 1092 1093 if (mOriginalCapabilities == null) { 1094 mOriginalCapabilities = new SignerCapabilities.Builder().build(); 1095 } 1096 1097 if (mNewCapabilities == null) { 1098 mNewCapabilities = new SignerCapabilities.Builder().build(); 1099 } 1100 1101 return createSigningLineage( 1102 mMinSdkVersion, mOriginalSignerConfig, mOriginalCapabilities, 1103 mNewSignerConfig, mNewCapabilities); 1104 } 1105 } 1106 } 1107