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