Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f013c08b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "V4 signature check refactors."

parents 36f539e5 f4f1218d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ public class V4Signature {
     *
     * @param fileSize - size of the signed file (APK)
     */
    public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
    public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
            SigningInfo signingInfo) {
        final int size =
                4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
+8 −9
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;

import android.util.ArrayMap;
@@ -213,11 +212,9 @@ public class ApkSignatureSchemeV2Verifier {
                    verityDigest, apk.length(), signatureInfo);
        }

        byte[] digest = pickBestDigestForV4(contentDigests);

        return new VerifiedSigner(
                signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
                verityRootHash, digest);
                verityRootHash, contentDigests);
    }

    private static X509Certificate[] verifySigner(
@@ -339,8 +336,7 @@ public class ApkSignatureSchemeV2Verifier {
            } catch (CertificateException e) {
                throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
            }
            certificate = new VerbatimX509Certificate(
                    certificate, encodedCert);
            certificate = new VerbatimX509Certificate(certificate, encodedCert);
            certs.add(certificate);
        }

@@ -434,12 +430,15 @@ public class ApkSignatureSchemeV2Verifier {
        public final X509Certificate[][] certs;

        public final byte[] verityRootHash;
        public final byte[] digest;
        // Algorithm -> digest map of signed digests in the signature.
        // All these are verified if requested.
        public final Map<Integer, byte[]> contentDigests;

        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash,
                Map<Integer, byte[]> contentDigests) {
            this.certs = certs;
            this.verityRootHash = verityRootHash;
            this.digest = digest;
            this.contentDigests = contentDigests;
        }

    }
+18 −15
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;

import android.os.Build;
@@ -161,7 +160,7 @@ public class ApkSignatureSchemeV3Verifier {
            boolean doVerifyIntegrity) throws SecurityException, IOException {
        int signerCount = 0;
        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
        VerifiedSigner result = null;
        Pair<X509Certificate[], VerifiedProofOfRotation> result = null;
        CertificateFactory certFactory;
        try {
            certFactory = CertificateFactory.getInstance("X.509");
@@ -206,18 +205,17 @@ public class ApkSignatureSchemeV3Verifier {
            ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
        }

        byte[] verityRootHash = null;
        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
            byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
            result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
            verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
                    verityDigest, apk.length(), signatureInfo);
        }

        result.digest = pickBestDigestForV4(contentDigests);

        return result;
        return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
    }

    private static VerifiedSigner verifySigner(
    private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner(
            ByteBuffer signerBlock,
            Map<Integer, byte[]> contentDigests,
            CertificateFactory certFactory)
@@ -349,8 +347,7 @@ public class ApkSignatureSchemeV3Verifier {
            } catch (CertificateException e) {
                throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
            }
            certificate = new VerbatimX509Certificate(
                    certificate, encodedCert);
            certificate = new VerbatimX509Certificate(certificate, encodedCert);
            certs.add(certificate);
        }

@@ -382,8 +379,9 @@ public class ApkSignatureSchemeV3Verifier {

    private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;

    private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
            List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
    private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes(
            ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)
            throws IOException {
        X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
        VerifiedProofOfRotation por = null;

@@ -421,7 +419,7 @@ public class ApkSignatureSchemeV3Verifier {
                    break;
            }
        }
        return new VerifiedSigner(certChain, por);
        return Pair.create(certChain, por);
    }

    private static VerifiedProofOfRotation verifyProofOfRotationStruct(
@@ -570,12 +568,17 @@ public class ApkSignatureSchemeV3Verifier {
        public final X509Certificate[] certs;
        public final VerifiedProofOfRotation por;

        public byte[] verityRootHash;
        public byte[] digest;
        public final byte[] verityRootHash;
        // Algorithm -> digest map of signed digests in the signature.
        // All these are verified if requested.
        public final Map<Integer, byte[]> contentDigests;

        public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
        public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por,
                byte[] verityRootHash, Map<Integer, byte[]> contentDigests) {
            this.certs = certs;
            this.por = por;
            this.verityRootHash = verityRootHash;
            this.contentDigests = contentDigests;
        }

    }
+30 −7
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package android.util.apk;

import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;

import android.os.incremental.IncrementalManager;
import android.os.incremental.V4Signature;
import android.util.ArrayMap;
import android.util.Pair;

import java.io.ByteArrayInputStream;
@@ -42,6 +44,7 @@ import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Map;

/**
 * APK Signature Scheme v4 verifier.
@@ -79,13 +82,20 @@ public class ApkSignatureSchemeV4Verifier {
            throw new SignatureNotFoundException("Failed to read V4 signature.", e);
        }

        final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
        // Verify signed data and extract certificates and apk digest.
        final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo,
                signingInfo);
        final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);

        return verifySigner(signingInfo, signedData);
        // Populate digests enforced by IncFS driver.
        Map<Integer, byte[]> contentDigests = new ArrayMap<>();
        contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
                hashingInfo.rawRootHash);

        return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests);
    }

    private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
    private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo,
            final byte[] signedData) throws SecurityException {
        if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
            throw new SecurityException("No supported signatures found");
@@ -145,21 +155,34 @@ public class ApkSignatureSchemeV4Verifier {
                    "Public key mismatch between certificate and signature record");
        }

        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
        return Pair.create(certificate, signingInfo.apkDigest);
    }

    private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException {
        if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) {
            return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
        }
        throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm);
    }

    /**
     * Verified APK Signature Scheme v4 signer, including V3 digest.
     * Verified APK Signature Scheme v4 signer, including V2/V3 digest.
     *
     * @hide for internal use only.
     */
    public static class VerifiedSigner {
        public final Certificate[] certs;
        public byte[] apkDigest;
        public final byte[] apkDigest;

        // Algorithm -> digest map of signed digests in the signature.
        // These are continuously enforced by the IncFS driver.
        public final Map<Integer, byte[]> contentDigests;

        public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
        public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
                Map<Integer, byte[]> contentDigests) {
            this.certs = certs;
            this.apkDigest = apkDigest;
            this.contentDigests = contentDigests;
        }

    }
+14 −6
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;

@@ -184,21 +185,21 @@ public class ApkSignatureVerifier {
            Signature[] signerSigs = convertToSignatures(signerCerts);

            if (verifyFull) {
                byte[] nonstreamingDigest = null;
                Certificate[][] nonstreamingCerts = null;
                Map<Integer, byte[]> nonstreamingDigests;
                Certificate[][] nonstreamingCerts;

                try {
                    // v4 is an add-on and requires v2 or v3 signature to validate against its
                    // certificate and digest
                    ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
                    nonstreamingDigest = v3Signer.digest;
                    nonstreamingDigests = v3Signer.contentDigests;
                    nonstreamingCerts = new Certificate[][]{v3Signer.certs};
                } catch (SignatureNotFoundException e) {
                    try {
                        ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
                                ApkSignatureSchemeV2Verifier.verify(apkPath, false);
                        nonstreamingDigest = v2Signer.digest;
                        nonstreamingDigests = v2Signer.contentDigests;
                        nonstreamingCerts = v2Signer.certs;
                    } catch (SignatureNotFoundException ee) {
                        throw new SecurityException(
@@ -220,8 +221,15 @@ public class ApkSignatureVerifier {
                    }
                }

                if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
                boolean found = false;
                for (byte[] nonstreamingDigest : nonstreamingDigests.values()) {
                    if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
                            vSigner.apkDigest.length)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                }
            }
Loading