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.encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes;
22 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodeCertificates;
23 import static com.android.apksig.internal.apk.ApkSigningBlockUtils.encodePublicKey;
24 
25 import com.android.apksig.SigningCertificateLineage;
26 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
27 import com.android.apksig.internal.apk.ApkSigningBlockUtils.SignerConfig;
28 import com.android.apksig.internal.apk.ContentDigestAlgorithm;
29 import com.android.apksig.internal.apk.SignatureAlgorithm;
30 import com.android.apksig.internal.util.Pair;
31 import com.android.apksig.util.DataSource;
32 import com.android.apksig.util.RunnablesExecutor;
33 import java.io.IOException;
34 import java.nio.ByteBuffer;
35 import java.nio.ByteOrder;
36 import java.security.InvalidKeyException;
37 import java.security.NoSuchAlgorithmException;
38 import java.security.PublicKey;
39 import java.security.SignatureException;
40 import java.security.cert.CertificateEncodingException;
41 import java.security.interfaces.ECKey;
42 import java.security.interfaces.RSAKey;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Map;
47 
48 /**
49  * APK Signature Scheme v3 signer.
50  *
51  * <p>APK Signature Scheme v3 builds upon APK Signature Scheme v3, and maintains all of the APK
52  * Signature Scheme v2 goals.
53  *
54  * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a>
55  *
56  * <p> The main contribution of APK Signature Scheme v3 is the introduction of the
57  * {@link SigningCertificateLineage}, which enables an APK to change its signing
58  * certificate as long as it can prove the new siging certificate was signed by the old.
59  */
60 public abstract class V3SchemeSigner {
61 
62     public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
63 
64     /** Hidden constructor to prevent instantiation. */
V3SchemeSigner()65     private V3SchemeSigner() {}
66 
67     /**
68      * Gets the APK Signature Scheme v3 signature algorithms to be used for signing an APK using the
69      * provided key.
70      *
71      * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see
72      *        AndroidManifest.xml minSdkVersion attribute).
73      *
74      * @throws InvalidKeyException if the provided key is not suitable for signing APKs using
75      *         APK Signature Scheme v3
76      */
getSuggestedSignatureAlgorithms( PublicKey signingKey, int minSdkVersion, boolean apkSigningBlockPaddingSupported)77     public static List<SignatureAlgorithm> getSuggestedSignatureAlgorithms(
78             PublicKey signingKey, int minSdkVersion, boolean apkSigningBlockPaddingSupported)
79             throws InvalidKeyException {
80         String keyAlgorithm = signingKey.getAlgorithm();
81         if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
82             // Use RSASSA-PKCS1-v1_5 signature scheme instead of RSASSA-PSS to guarantee
83             // deterministic signatures which make life easier for OTA updates (fewer files
84             // changed when deterministic signature schemes are used).
85 
86             // Pick a digest which is no weaker than the key.
87             int modulusLengthBits = ((RSAKey) signingKey).getModulus().bitLength();
88             if (modulusLengthBits <= 3072) {
89                 // 3072-bit RSA is roughly 128-bit strong, meaning SHA-256 is a good fit.
90                 List<SignatureAlgorithm> algorithms = new ArrayList<>();
91                 algorithms.add(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256);
92                 if (apkSigningBlockPaddingSupported) {
93                     algorithms.add(SignatureAlgorithm.VERITY_RSA_PKCS1_V1_5_WITH_SHA256);
94                 }
95                 return algorithms;
96             } else {
97                 // Keys longer than 3072 bit need to be paired with a stronger digest to avoid the
98                 // digest being the weak link. SHA-512 is the next strongest supported digest.
99                 return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA512);
100             }
101         } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
102             // DSA is supported only with SHA-256.
103             List<SignatureAlgorithm> algorithms = new ArrayList<>();
104             algorithms.add(SignatureAlgorithm.DSA_WITH_SHA256);
105             if (apkSigningBlockPaddingSupported) {
106                 algorithms.add(SignatureAlgorithm.VERITY_DSA_WITH_SHA256);
107             }
108             return algorithms;
109         } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
110             // Pick a digest which is no weaker than the key.
111             int keySizeBits = ((ECKey) signingKey).getParams().getOrder().bitLength();
112             if (keySizeBits <= 256) {
113                 // 256-bit Elliptic Curve is roughly 128-bit strong, meaning SHA-256 is a good fit.
114                 List<SignatureAlgorithm> algorithms = new ArrayList<>();
115                 algorithms.add(SignatureAlgorithm.ECDSA_WITH_SHA256);
116                 if (apkSigningBlockPaddingSupported) {
117                     algorithms.add(SignatureAlgorithm.VERITY_ECDSA_WITH_SHA256);
118                 }
119                 return algorithms;
120             } else {
121                 // Keys longer than 256 bit need to be paired with a stronger digest to avoid the
122                 // digest being the weak link. SHA-512 is the next strongest supported digest.
123                 return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA512);
124             }
125         } else {
126             throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
127         }
128     }
129 
generateApkSignatureSchemeV3Block( RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List<SignerConfig> signerConfigs)130     public static Pair<byte[], Integer> generateApkSignatureSchemeV3Block(
131             RunnablesExecutor executor,
132             DataSource beforeCentralDir,
133             DataSource centralDir,
134             DataSource eocd,
135             List<SignerConfig> signerConfigs)
136                     throws IOException, InvalidKeyException, NoSuchAlgorithmException,
137                             SignatureException {
138         Pair<List<SignerConfig>,
139                 Map<ContentDigestAlgorithm, byte[]>> digestInfo =
140                 ApkSigningBlockUtils.computeContentDigests(
141                         executor, beforeCentralDir, centralDir, eocd, signerConfigs);
142         return generateApkSignatureSchemeV3Block(digestInfo.getFirst(), digestInfo.getSecond());
143     }
144 
generateApkSignatureSchemeV3Block( List<SignerConfig> signerConfigs, Map<ContentDigestAlgorithm, byte[]> contentDigests)145     private static Pair<byte[], Integer> generateApkSignatureSchemeV3Block(
146             List<SignerConfig> signerConfigs,
147             Map<ContentDigestAlgorithm, byte[]> contentDigests)
148                     throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
149         // FORMAT:
150         // * length-prefixed sequence of length-prefixed signer blocks.
151 
152         List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size());
153         int signerNumber = 0;
154         for (SignerConfig signerConfig : signerConfigs) {
155             signerNumber++;
156             byte[] signerBlock;
157             try {
158                 signerBlock = generateSignerBlock(signerConfig, contentDigests);
159             } catch (InvalidKeyException e) {
160                 throw new InvalidKeyException("Signer #" + signerNumber + " failed", e);
161             } catch (SignatureException e) {
162                 throw new SignatureException("Signer #" + signerNumber + " failed", e);
163             }
164             signerBlocks.add(signerBlock);
165         }
166 
167         return Pair.of(encodeAsSequenceOfLengthPrefixedElements(
168                 new byte[][] {
169                     encodeAsSequenceOfLengthPrefixedElements(signerBlocks),
170                 }), APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
171     }
172 
generateSignerBlock( SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests)173     private static byte[] generateSignerBlock(
174             SignerConfig signerConfig,
175             Map<ContentDigestAlgorithm, byte[]> contentDigests)
176                     throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
177         if (signerConfig.certificates.isEmpty()) {
178             throw new SignatureException("No certificates configured for signer");
179         }
180         PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
181 
182         byte[] encodedPublicKey = encodePublicKey(publicKey);
183 
184         V3SignatureSchemeBlock.SignedData signedData = new V3SignatureSchemeBlock.SignedData();
185         try {
186             signedData.certificates = encodeCertificates(signerConfig.certificates);
187         } catch (CertificateEncodingException e) {
188             throw new SignatureException("Failed to encode certificates", e);
189         }
190 
191         List<Pair<Integer, byte[]>> digests =
192                 new ArrayList<>(signerConfig.signatureAlgorithms.size());
193         for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
194             ContentDigestAlgorithm contentDigestAlgorithm =
195                     signatureAlgorithm.getContentDigestAlgorithm();
196             byte[] contentDigest = contentDigests.get(contentDigestAlgorithm);
197             if (contentDigest == null) {
198                 throw new RuntimeException(
199                         contentDigestAlgorithm + " content digest for " + signatureAlgorithm
200                                 + " not computed");
201             }
202             digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest));
203         }
204         signedData.digests = digests;
205         signedData.minSdkVersion = signerConfig.minSdkVersion;
206         signedData.maxSdkVersion = signerConfig.maxSdkVersion;
207         signedData.additionalAttributes = generateAdditionalAttributes(signerConfig);
208 
209         V3SignatureSchemeBlock.Signer signer = new V3SignatureSchemeBlock.Signer();
210 
211         signer.signedData = encodeSignedData(signedData);
212 
213         signer.minSdkVersion = signerConfig.minSdkVersion;
214         signer.maxSdkVersion = signerConfig.maxSdkVersion;
215         signer.publicKey = encodedPublicKey;
216         signer.signatures =
217                 ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData);
218 
219 
220         return encodeSigner(signer);
221     }
222 
encodeSigner(V3SignatureSchemeBlock.Signer signer)223     private static byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) {
224         byte[] signedData = encodeAsLengthPrefixedElement(signer.signedData);
225         byte[] signatures =
226                 encodeAsLengthPrefixedElement(
227                         encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
228                                 signer.signatures));
229         byte[] publicKey = encodeAsLengthPrefixedElement(signer.publicKey);
230 
231         // FORMAT:
232         // * length-prefixed signed data
233         // * uint32: minSdkVersion
234         // * uint32: maxSdkVersion
235         // * length-prefixed sequence of length-prefixed signatures:
236         //   * uint32: signature algorithm ID
237         //   * length-prefixed bytes: signature of signed data
238         // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
239         int payloadSize =
240                 signedData.length
241                 + 4
242                 + 4
243                 + signatures.length
244                 + publicKey.length;
245 
246         ByteBuffer result = ByteBuffer.allocate(payloadSize);
247         result.order(ByteOrder.LITTLE_ENDIAN);
248         result.put(signedData);
249         result.putInt(signer.minSdkVersion);
250         result.putInt(signer.maxSdkVersion);
251         result.put(signatures);
252         result.put(publicKey);
253 
254         return result.array();
255     }
256 
encodeSignedData(V3SignatureSchemeBlock.SignedData signedData)257     private static byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) {
258         byte[] digests =
259                 encodeAsLengthPrefixedElement(
260                         encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
261                                 signedData.digests));
262         byte[] certs =
263                 encodeAsLengthPrefixedElement(
264                         encodeAsSequenceOfLengthPrefixedElements(signedData.certificates));
265         byte[] attributes = encodeAsLengthPrefixedElement(signedData.additionalAttributes);
266 
267         // FORMAT:
268         // * length-prefixed sequence of length-prefixed digests:
269         //   * uint32: signature algorithm ID
270         //   * length-prefixed bytes: digest of contents
271         // * length-prefixed sequence of certificates:
272         //   * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
273         // * uint-32: minSdkVersion
274         // * uint-32: maxSdkVersion
275         // * length-prefixed sequence of length-prefixed additional attributes:
276         //   * uint32: ID
277         //   * (length - 4) bytes: value
278         //   * uint32: Proof-of-rotation ID: 0x3ba06f8c
279         //   * length-prefixed roof-of-rotation structure
280         int payloadSize =
281                 digests.length
282                         + certs.length
283                         + 4
284                         + 4
285                         + attributes.length;
286 
287         ByteBuffer result = ByteBuffer.allocate(payloadSize);
288         result.order(ByteOrder.LITTLE_ENDIAN);
289         result.put(digests);
290         result.put(certs);
291         result.putInt(signedData.minSdkVersion);
292         result.putInt(signedData.maxSdkVersion);
293         result.put(attributes);
294 
295         return result.array();
296     }
297 
298     public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
299 
generateAdditionalAttributes(SignerConfig signerConfig)300     private static byte[] generateAdditionalAttributes(SignerConfig signerConfig) {
301         if (signerConfig.mSigningCertificateLineage == null) {
302             return new byte[0];
303         }
304         return signerConfig.mSigningCertificateLineage.generateV3SignerAttribute();
305     }
306 
307     private static final class V3SignatureSchemeBlock {
308         private static final class Signer {
309             public byte[] signedData;
310             public int minSdkVersion;
311             public int maxSdkVersion;
312             public List<Pair<Integer, byte[]>> signatures;
313             public byte[] publicKey;
314         }
315 
316         private static final class SignedData {
317             public List<Pair<Integer, byte[]>> digests;
318             public List<byte[]> certificates;
319             public int minSdkVersion;
320             public int maxSdkVersion;
321             public byte[] additionalAttributes;
322         }
323     }
324 
325 }
326