• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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