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

Commit 780d2bb7 authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Android (Google) Code Review
Browse files

Merge "v4 signing schema parsing and verification."

parents 223c27ef 036864b6
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -61,7 +61,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ApkParseUtils;
import android.content.pm.parsing.ApkParseUtils;
import android.content.pm.parsing.PackageImpl;
import android.content.pm.parsing.PackageImpl;
import android.content.pm.parsing.PackageInfoUtils;
import android.content.pm.parsing.ParsedPackage;
import android.content.pm.parsing.ParsedPackage;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.DefaultSplitAssetLoader;
@@ -5967,12 +5966,14 @@ public class PackageParser {
        @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
        @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
                SigningDetails.SignatureSchemeVersion.JAR,
                SigningDetails.SignatureSchemeVersion.JAR,
                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3})
                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4})
        public @interface SignatureSchemeVersion {
        public @interface SignatureSchemeVersion {
            int UNKNOWN = 0;
            int UNKNOWN = 0;
            int JAR = 1;
            int JAR = 1;
            int SIGNING_BLOCK_V2 = 2;
            int SIGNING_BLOCK_V2 = 2;
            int SIGNING_BLOCK_V3 = 3;
            int SIGNING_BLOCK_V3 = 3;
            int SIGNING_BLOCK_V4 = 4;
        }
        }


        @Nullable
        @Nullable
+53 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.util.apk;

import android.os.incremental.IncrementalManager;

import java.io.File;
import java.security.cert.Certificate;

import sun.security.pkcs.PKCS7;
import sun.security.pkcs.ParsingException;

/**
 * APK Signature Scheme v4 verifier.
 *
 * @hide for internal use only.
 */
public class ApkSignatureSchemeV4Verifier {

    /**
     * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates
     * associated with each signer.
     */
    public static Certificate[] extractCertificates(String apkFile)
            throws SignatureNotFoundException, SecurityException {
        final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature(
                new File(apkFile).getAbsolutePath());
        if (rawSignature == null || rawSignature.length == 0) {
            throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS.");
        }

        try {
            PKCS7 pkcs7 = new PKCS7(rawSignature);
            return pkcs7.getCertificates();
        } catch (ParsingException e) {
            throw new SecurityException("Failed to parse signature and extract certificates", e);
        }
    }
}
+78 −1
Original line number Original line Diff line number Diff line
@@ -92,6 +92,24 @@ public class ApkSignatureVerifier {
            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
            throws PackageParserException {
            throws PackageParserException {


        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
            // V3 and before are older than the requested minimum signing version
            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "No signature found in package of version " + minSignatureSchemeVersion
                            + " or newer for package " + apkPath);
        }

        // first try v4
        try {
            return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull);
        } catch (SignatureNotFoundException e) {
            // not signed with v4, try older if allowed
            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                        "No APK Signature Scheme v4 signature in package " + apkPath, e);
            }
        }

        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
            // V3 and before are older than the requested minimum signing version
            // V3 and before are older than the requested minimum signing version
            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
@@ -99,7 +117,13 @@ public class ApkSignatureVerifier {
                            + " or newer for package " + apkPath);
                            + " or newer for package " + apkPath);
        }
        }


        // first try v3
        return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
    }

    private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath,
            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
            throws PackageParserException {
        // try v3
        try {
        try {
            return verifyV3Signature(apkPath, verifyFull);
            return verifyV3Signature(apkPath, verifyFull);
        } catch (SignatureNotFoundException e) {
        } catch (SignatureNotFoundException e) {
@@ -141,6 +165,59 @@ public class ApkSignatureVerifier {
        return verifyV1Signature(apkPath, verifyFull);
        return verifyV1Signature(apkPath, verifyFull);
    }
    }


    /**
     * Verifies the provided APK using V4 schema.
     *
     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
     * @return the certificates associated with each signer.
     * @throws SignatureNotFoundException if there are no V4 signatures in the APK
     * @throws PackageParserException     if there was a problem collecting certificates
     */
    private static PackageParser.SigningDetails verifyV4Signature(String apkPath,
            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
            throws SignatureNotFoundException, PackageParserException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
        try {
            Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath);
            Certificate[][] signerCerts = new Certificate[][]{certs};
            Signature[] signerSigs = convertToSignatures(signerCerts);

            if (verifyFull) {
                // v4 is an add-on and requires v2/v3 signature to validate against its certificates
                final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures(
                        apkPath, minSignatureSchemeVersion, false);
                if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) {
                    throw new SecurityException(
                            "V4 signing block can only be verified along with V2 and above.");
                }
                if (nonstreaming.signatures.length == 0
                        || nonstreaming.signatures.length != signerSigs.length) {
                    throw new SecurityException("Invalid number of signatures in "
                            + nonstreaming.signatureSchemeVersion);
                }

                for (int i = 0, size = signerSigs.length; i < size; ++i) {
                    if (!nonstreaming.signatures[i].equals(signerSigs[i])) {
                        throw new SecurityException("V4 signature certificate does not match "
                                + nonstreaming.signatureSchemeVersion);
                    }
                }
            }

            return new PackageParser.SigningDetails(signerSigs,
                    SignatureSchemeVersion.SIGNING_BLOCK_V4);
        } catch (SignatureNotFoundException e) {
            throw e;
        } catch (Exception e) {
            // APK Signature Scheme v4 signature found but did not verify
            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "Failed to collect certificates from " + apkPath
                            + " using APK Signature Scheme v4", e);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

    /**
    /**
     * Verifies the provided APK using V3 schema.
     * Verifies the provided APK using V3 schema.
     *
     *