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