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.internal.apk.v3;
18 
19 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsLengthPrefixedElement;
20 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeAsSequenceOfLengthPrefixedElements;
21 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
22 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
23 
24 import com.android.apksig.apk.ApkFormatException;
25 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
26 import com.android.apksig.internal.apk.SignatureAlgorithm;
27 import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate;
28 import com.android.apksig.internal.util.X509CertificateUtils;
29 
30 import java.io.IOException;
31 import java.nio.BufferUnderflowException;
32 import java.nio.ByteBuffer;
33 import java.nio.ByteOrder;
34 import java.security.InvalidAlgorithmParameterException;
35 import java.security.InvalidKeyException;
36 import java.security.NoSuchAlgorithmException;
37 import java.security.PublicKey;
38 import java.security.Signature;
39 import java.security.SignatureException;
40 import java.security.cert.CertificateEncodingException;
41 import java.security.cert.CertificateException;
42 import java.security.cert.X509Certificate;
43 import java.security.spec.AlgorithmParameterSpec;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.HashSet;
47 import java.util.List;
48 
49 /**
50  * APK Signer Lineage.
51  *
52  * <p>The signer lineage contains a history of signing certificates with each ancestor attesting to
53  * the validity of its descendant.  Each additional descendant represents a new identity that can be
54  * used to sign an APK, and each generation has accompanying attributes which represent how the
55  * APK would like to view the older signing certificates, specifically how they should be trusted in
56  * certain situations.
57  *
58  * <p> Its primary use is to enable APK Signing Certificate Rotation.  The Android platform verifies
59  * the APK Signer Lineage, and if the current signing certificate for the APK is in the Signer
60  * Lineage, and the Lineage contains the certificate the platform associates with the APK, it will
61  * allow upgrades to the new certificate.
62  *
63  * @see <a href="https://source.android.com/security/apksigning/index.html">Application Signing</a>
64  */
65 public class V3SigningCertificateLineage {
66 
67     private final static int FIRST_VERSION = 1;
68     private final static int CURRENT_VERSION = FIRST_VERSION;
69 
70     /**
71      * Deserializes the binary representation of an {@link V3SigningCertificateLineage}. Also
72      * verifies that the structure is well-formed, e.g. that the signature for each node is from its
73      * parent.
74      */
readSigningCertificateLineage(ByteBuffer inputBytes)75     public static List<SigningCertificateNode> readSigningCertificateLineage(ByteBuffer inputBytes)
76             throws IOException {
77         List<SigningCertificateNode> result = new ArrayList<>();
78         int nodeCount = 0;
79         if (inputBytes == null || !inputBytes.hasRemaining()) {
80             return null;
81         }
82 
83         ApkSigningBlockUtils.checkByteOrderLittleEndian(inputBytes);
84 
85         // FORMAT (little endian):
86         // * uint32: version code
87         // * sequence of length-prefixed (uint32): nodes
88         //   * length-prefixed bytes: signed data
89         //     * length-prefixed bytes: certificate
90         //     * uint32: signature algorithm id
91         //   * uint32: flags
92         //   * uint32: signature algorithm id (used by to sign next cert in lineage)
93         //   * length-prefixed bytes: signature over above signed data
94 
95         X509Certificate lastCert = null;
96         int lastSigAlgorithmId = 0;
97 
98         try {
99             int version = inputBytes.getInt();
100             if (version != CURRENT_VERSION) {
101                 // we only have one version to worry about right now, so just check it
102                 throw new IllegalArgumentException("Encoded SigningCertificateLineage has a version"
103                         + " different than any of which we are aware");
104             }
105             HashSet<X509Certificate> certHistorySet = new HashSet<>();
106             while (inputBytes.hasRemaining()) {
107                 nodeCount++;
108                 ByteBuffer nodeBytes = getLengthPrefixedSlice(inputBytes);
109                 ByteBuffer signedData = getLengthPrefixedSlice(nodeBytes);
110                 int flags = nodeBytes.getInt();
111                 int sigAlgorithmId = nodeBytes.getInt();
112                 SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(lastSigAlgorithmId);
113                 byte[] signature = readLengthPrefixedByteArray(nodeBytes);
114 
115                 if (lastCert != null) {
116                     // Use previous level cert to verify current level
117                     String jcaSignatureAlgorithm =
118                             sigAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
119                     AlgorithmParameterSpec jcaSignatureAlgorithmParams =
120                             sigAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
121                     PublicKey publicKey = lastCert.getPublicKey();
122                     Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
123                     sig.initVerify(publicKey);
124                     if (jcaSignatureAlgorithmParams != null) {
125                         sig.setParameter(jcaSignatureAlgorithmParams);
126                     }
127                     sig.update(signedData);
128                     if (!sig.verify(signature)) {
129                         throw new SecurityException("Unable to verify signature of certificate #"
130                                 + nodeCount + " using " + jcaSignatureAlgorithm + " when verifying"
131                                 + " V3SigningCertificateLineage object");
132                     }
133                 }
134 
135                 signedData.rewind();
136                 byte[] encodedCert = readLengthPrefixedByteArray(signedData);
137                 int signedSigAlgorithm = signedData.getInt();
138                 if (lastCert != null && lastSigAlgorithmId != signedSigAlgorithm) {
139                     throw new SecurityException("Signing algorithm ID mismatch for certificate #"
140                             + nodeBytes + " when verifying V3SigningCertificateLineage object");
141                 }
142                 lastCert = X509CertificateUtils.generateCertificate(encodedCert);
143                 lastCert = new GuaranteedEncodedFormX509Certificate(lastCert, encodedCert);
144                 if (certHistorySet.contains(lastCert)) {
145                     throw new SecurityException("Encountered duplicate entries in "
146                             + "SigningCertificateLineage at certificate #" + nodeCount + ".  All "
147                             + "signing certificates should be unique");
148                 }
149                 certHistorySet.add(lastCert);
150                 lastSigAlgorithmId = sigAlgorithmId;
151                 result.add(new SigningCertificateNode(
152                         lastCert, SignatureAlgorithm.findById(signedSigAlgorithm),
153                         SignatureAlgorithm.findById(sigAlgorithmId), signature, flags));
154             }
155         } catch(ApkFormatException | BufferUnderflowException e){
156             throw new IOException("Failed to parse V3SigningCertificateLineage object", e);
157         } catch(NoSuchAlgorithmException | InvalidKeyException
158                 | InvalidAlgorithmParameterException | SignatureException e){
159             throw new SecurityException(
160                     "Failed to verify signature over signed data for certificate #" + nodeCount
161                             + " when parsing V3SigningCertificateLineage object", e);
162         } catch(CertificateException e){
163             throw new SecurityException("Failed to decode certificate #" + nodeCount
164                     + " when parsing V3SigningCertificateLineage object", e);
165         }
166         return result;
167     }
168 
169     /**
170      * encode the in-memory representation of this {@code V3SigningCertificateLineage}
171      */
encodeSigningCertificateLineage( List<SigningCertificateNode> signingCertificateLineage)172     public static byte[] encodeSigningCertificateLineage(
173             List<SigningCertificateNode> signingCertificateLineage) {
174         // FORMAT (little endian):
175         // * version code
176         // * sequence of length-prefixed (uint32): nodes
177         //   * length-prefixed bytes: signed data
178         //     * length-prefixed bytes: certificate
179         //     * uint32: signature algorithm id
180         //   * uint32: flags
181         //   * uint32: signature algorithm id (used by to sign next cert in lineage)
182 
183         List<byte[]> nodes = new ArrayList<>();
184         for (SigningCertificateNode node : signingCertificateLineage) {
185             nodes.add(encodeSigningCertificateNode(node));
186         }
187         byte [] encodedSigningCertificateLineage = encodeAsSequenceOfLengthPrefixedElements(nodes);
188 
189         // add the version code (uint32) on top of the encoded nodes
190         int payloadSize = 4 + encodedSigningCertificateLineage.length;
191         ByteBuffer encodedWithVersion = ByteBuffer.allocate(payloadSize);
192         encodedWithVersion.order(ByteOrder.LITTLE_ENDIAN);
193         encodedWithVersion.putInt(CURRENT_VERSION);
194         encodedWithVersion.put(encodedSigningCertificateLineage);
195         return encodedWithVersion.array();
196     }
197 
encodeSigningCertificateNode(SigningCertificateNode node)198     public static byte[] encodeSigningCertificateNode(SigningCertificateNode node) {
199         // FORMAT (little endian):
200         // * length-prefixed bytes: signed data
201         //   * length-prefixed bytes: certificate
202         //   * uint32: signature algorithm id
203         // * uint32: flags
204         // * uint32: signature algorithm id (used by to sign next cert in lineage)
205         // * length-prefixed bytes: signature over signed data
206         int parentSigAlgorithmId = 0;
207         if (node.parentSigAlgorithm != null) {
208             parentSigAlgorithmId = node.parentSigAlgorithm.getId();
209         }
210         int sigAlgorithmId = 0;
211         if (node.sigAlgorithm != null) {
212             sigAlgorithmId = node.sigAlgorithm.getId();
213         }
214         byte[] prefixedSignedData = encodeSignedData(node.signingCert, parentSigAlgorithmId);
215         byte[] prefixedSignature = encodeAsLengthPrefixedElement(node.signature);
216         int payloadSize = prefixedSignedData.length + 4 + 4 + prefixedSignature.length;
217         ByteBuffer result = ByteBuffer.allocate(payloadSize);
218         result.order(ByteOrder.LITTLE_ENDIAN);
219         result.put(prefixedSignedData);
220         result.putInt(node.flags);
221         result.putInt(sigAlgorithmId);
222         result.put(prefixedSignature);
223         return result.array();
224     }
225 
encodeSignedData(X509Certificate certificate, int flags)226     public static byte[] encodeSignedData(X509Certificate certificate, int flags) {
227         try {
228             byte[] prefixedCertificate = encodeAsLengthPrefixedElement(certificate.getEncoded());
229             int payloadSize = 4 + prefixedCertificate.length;
230             ByteBuffer result = ByteBuffer.allocate(payloadSize);
231             result.order(ByteOrder.LITTLE_ENDIAN);
232             result.put(prefixedCertificate);
233             result.putInt(flags);
234             return encodeAsLengthPrefixedElement(result.array());
235         } catch (CertificateEncodingException e) {
236             throw new RuntimeException(
237                     "Failed to encode V3SigningCertificateLineage certificate", e);
238         }
239     }
240 
241     /**
242      * Represents one signing certificate in the {@link V3SigningCertificateLineage}, which
243      * generally means it is/was used at some point to sign the same APK of the others in the
244      * lineage.
245      */
246     public static class SigningCertificateNode {
247 
SigningCertificateNode( X509Certificate signingCert, SignatureAlgorithm parentSigAlgorithm, SignatureAlgorithm sigAlgorithm, byte[] signature, int flags)248         public SigningCertificateNode(
249                 X509Certificate signingCert,
250                 SignatureAlgorithm parentSigAlgorithm,
251                 SignatureAlgorithm sigAlgorithm,
252                 byte[] signature,
253                 int flags) {
254             this.signingCert = signingCert;
255             this.parentSigAlgorithm = parentSigAlgorithm;
256             this.sigAlgorithm = sigAlgorithm;
257             this.signature = signature;
258             this.flags = flags;
259         }
260 
261         @Override
equals(Object o)262         public boolean equals(Object o) {
263             if (this == o) return true;
264             if (!(o instanceof SigningCertificateNode)) return false;
265 
266             SigningCertificateNode that = (SigningCertificateNode) o;
267             if (!signingCert.equals(that.signingCert)) return false;
268             if (parentSigAlgorithm != that.parentSigAlgorithm) return false;
269             if (sigAlgorithm != that.sigAlgorithm) return false;
270             if (!Arrays.equals(signature, that.signature)) return false;
271             if (flags != that.flags) return false;
272 
273             // we made it
274             return true;
275         }
276 
277         /**
278          * the signing cert for this node.  This is part of the data signed by the parent node.
279          */
280         public final X509Certificate signingCert;
281 
282         /**
283          * the algorithm used by the this node's parent to bless this data.  Its ID value is part of
284          * the data signed by the parent node. {@code null} for first node.
285          */
286         public final SignatureAlgorithm parentSigAlgorithm;
287 
288         /**
289          * the algorithm used by the this nodeto bless the next node's data.  Its ID value is part
290          * of the signed data of the next node. {@code null} for the last node.
291          */
292         public SignatureAlgorithm sigAlgorithm;
293 
294         /**
295          * signature over the signed data (above).  The signature is from this node's parent
296          * signing certificate, which should correspond to the signing certificate used to sign an
297          * APK before rotating to this one, and is formed using {@code signatureAlgorithm}.
298          */
299         public final byte[] signature;
300 
301         /**
302          * the flags detailing how the platform should treat this signing cert
303          */
304         public int flags;
305     }
306 }
307