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