Home | History | Annotate | Download | only in v3
      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 
     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     private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
     63 
     64     /** Hidden constructor to prevent instantiation. */
     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      */
     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 
    130     public static Pair<byte[], Integer> generateApkSignatureSchemeV3Block(
    131             DataSource beforeCentralDir,
    132             DataSource centralDir,
    133             DataSource eocd,
    134             List<SignerConfig> signerConfigs)
    135                     throws IOException, InvalidKeyException, NoSuchAlgorithmException,
    136                             SignatureException {
    137         Pair<List<SignerConfig>,
    138                 Map<ContentDigestAlgorithm, byte[]>> digestInfo =
    139                 ApkSigningBlockUtils.computeContentDigests(beforeCentralDir, centralDir, eocd,
    140                         signerConfigs);
    141         return generateApkSignatureSchemeV3Block(digestInfo.getFirst(), digestInfo.getSecond());
    142     }
    143 
    144     private static Pair<byte[], Integer> generateApkSignatureSchemeV3Block(
    145             List<SignerConfig> signerConfigs,
    146             Map<ContentDigestAlgorithm, byte[]> contentDigests)
    147                     throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
    148         // FORMAT:
    149         // * length-prefixed sequence of length-prefixed signer blocks.
    150 
    151         List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size());
    152         int signerNumber = 0;
    153         for (SignerConfig signerConfig : signerConfigs) {
    154             signerNumber++;
    155             byte[] signerBlock;
    156             try {
    157                 signerBlock = generateSignerBlock(signerConfig, contentDigests);
    158             } catch (InvalidKeyException e) {
    159                 throw new InvalidKeyException("Signer #" + signerNumber + " failed", e);
    160             } catch (SignatureException e) {
    161                 throw new SignatureException("Signer #" + signerNumber + " failed", e);
    162             }
    163             signerBlocks.add(signerBlock);
    164         }
    165 
    166         return Pair.of(encodeAsSequenceOfLengthPrefixedElements(
    167                 new byte[][] {
    168                     encodeAsSequenceOfLengthPrefixedElements(signerBlocks),
    169                 }), APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
    170     }
    171 
    172     private static byte[] generateSignerBlock(
    173             SignerConfig signerConfig,
    174             Map<ContentDigestAlgorithm, byte[]> contentDigests)
    175                     throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
    176         if (signerConfig.certificates.isEmpty()) {
    177             throw new SignatureException("No certificates configured for signer");
    178         }
    179         PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
    180 
    181         byte[] encodedPublicKey = encodePublicKey(publicKey);
    182 
    183         V3SignatureSchemeBlock.SignedData signedData = new V3SignatureSchemeBlock.SignedData();
    184         try {
    185             signedData.certificates = encodeCertificates(signerConfig.certificates);
    186         } catch (CertificateEncodingException e) {
    187             throw new SignatureException("Failed to encode certificates", e);
    188         }
    189 
    190         List<Pair<Integer, byte[]>> digests =
    191                 new ArrayList<>(signerConfig.signatureAlgorithms.size());
    192         for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
    193             ContentDigestAlgorithm contentDigestAlgorithm =
    194                     signatureAlgorithm.getContentDigestAlgorithm();
    195             byte[] contentDigest = contentDigests.get(contentDigestAlgorithm);
    196             if (contentDigest == null) {
    197                 throw new RuntimeException(
    198                         contentDigestAlgorithm + " content digest for " + signatureAlgorithm
    199                                 + " not computed");
    200             }
    201             digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest));
    202         }
    203         signedData.digests = digests;
    204         signedData.minSdkVersion = signerConfig.minSdkVersion;
    205         signedData.maxSdkVersion = signerConfig.maxSdkVersion;
    206         signedData.additionalAttributes = generateAdditionalAttributes(signerConfig);
    207 
    208         V3SignatureSchemeBlock.Signer signer = new V3SignatureSchemeBlock.Signer();
    209 
    210         signer.signedData = encodeSignedData(signedData);
    211 
    212         signer.minSdkVersion = signerConfig.minSdkVersion;
    213         signer.maxSdkVersion = signerConfig.maxSdkVersion;
    214         signer.publicKey = encodedPublicKey;
    215         signer.signatures =
    216                 ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData);
    217 
    218 
    219         return encodeSigner(signer);
    220     }
    221 
    222     private static byte[] encodeSigner(V3SignatureSchemeBlock.Signer signer) {
    223         byte[] signedData = encodeAsLengthPrefixedElement(signer.signedData);
    224         byte[] signatures =
    225                 encodeAsLengthPrefixedElement(
    226                         encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
    227                                 signer.signatures));
    228         byte[] publicKey = encodeAsLengthPrefixedElement(signer.publicKey);
    229 
    230         // FORMAT:
    231         // * length-prefixed signed data
    232         // * uint32: minSdkVersion
    233         // * uint32: maxSdkVersion
    234         // * length-prefixed sequence of length-prefixed signatures:
    235         //   * uint32: signature algorithm ID
    236         //   * length-prefixed bytes: signature of signed data
    237         // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
    238         int payloadSize =
    239                 signedData.length
    240                 + 4
    241                 + 4
    242                 + signatures.length
    243                 + publicKey.length;
    244 
    245         ByteBuffer result = ByteBuffer.allocate(payloadSize);
    246         result.order(ByteOrder.LITTLE_ENDIAN);
    247         result.put(signedData);
    248         result.putInt(signer.minSdkVersion);
    249         result.putInt(signer.maxSdkVersion);
    250         result.put(signatures);
    251         result.put(publicKey);
    252 
    253         return result.array();
    254     }
    255 
    256     private static byte[] encodeSignedData(V3SignatureSchemeBlock.SignedData signedData) {
    257         byte[] digests =
    258                 encodeAsLengthPrefixedElement(
    259                         encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
    260                                 signedData.digests));
    261         byte[] certs =
    262                 encodeAsLengthPrefixedElement(
    263                         encodeAsSequenceOfLengthPrefixedElements(signedData.certificates));
    264         byte[] attributes = encodeAsLengthPrefixedElement(signedData.additionalAttributes);
    265 
    266         // FORMAT:
    267         // * length-prefixed sequence of length-prefixed digests:
    268         //   * uint32: signature algorithm ID
    269         //   * length-prefixed bytes: digest of contents
    270         // * length-prefixed sequence of certificates:
    271         //   * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
    272         // * uint-32: minSdkVersion
    273         // * uint-32: maxSdkVersion
    274         // * length-prefixed sequence of length-prefixed additional attributes:
    275         //   * uint32: ID
    276         //   * (length - 4) bytes: value
    277         //   * uint32: Proof-of-rotation ID: 0x3ba06f8c
    278         //   * length-prefixed roof-of-rotation structure
    279         int payloadSize =
    280                 digests.length
    281                         + certs.length
    282                         + 4
    283                         + 4
    284                         + attributes.length;
    285 
    286         ByteBuffer result = ByteBuffer.allocate(payloadSize);
    287         result.order(ByteOrder.LITTLE_ENDIAN);
    288         result.put(digests);
    289         result.put(certs);
    290         result.putInt(signedData.minSdkVersion);
    291         result.putInt(signedData.maxSdkVersion);
    292         result.put(attributes);
    293 
    294         return result.array();
    295     }
    296 
    297     public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
    298 
    299     private static byte[] generateAdditionalAttributes(SignerConfig signerConfig) {
    300         if (signerConfig.mSigningCertificateLineage == null) {
    301             return new byte[0];
    302         }
    303         return signerConfig.mSigningCertificateLineage.generateV3SignerAttribute();
    304     }
    305 
    306     private static final class V3SignatureSchemeBlock {
    307         private static final class Signer {
    308             public byte[] signedData;
    309             public int minSdkVersion;
    310             public int maxSdkVersion;
    311             public List<Pair<Integer, byte[]>> signatures;
    312             public byte[] publicKey;
    313         }
    314 
    315         private static final class SignedData {
    316             public List<Pair<Integer, byte[]>> digests;
    317             public List<byte[]> certificates;
    318             public int minSdkVersion;
    319             public int maxSdkVersion;
    320             public byte[] additionalAttributes;
    321         }
    322     }
    323 
    324 }
    325