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