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

Commit 5f761245 authored by Victor Hsieh's avatar Victor Hsieh
Browse files

Skip priv app full apk verification if has verify

When ro.apk_verity.mode is on, full apk verification is only skipped if
the apk already has verity enabled in the file system, and if the apk
contains the Merkle tree root hash we need.

Since the configuration in the file system is duplicated from the apk
(including the offset and size of Signing Block and the Merkle tree),
in order to prevent offline attacker from changing it, we need to
measure the observed configuration and make sure it matches the kernel's
view.

Test: observed package manager's requeset to installd (only) for updated
      priv apps.
Bug: 30972906

Change-Id: I33531a3f6148232b777ea8bfd02f13700649e317
parent d93a7959
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4740,7 +4740,7 @@ public abstract class PackageManager {

            PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
            if ((flags & GET_SIGNATURES) != 0) {
                PackageParser.collectCertificates(pkg, 0);
                PackageParser.collectCertificates(pkg, false /* skipVerify */);
            }
            PackageUserState state = new PackageUserState();
            return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
+9 −8
Original line number Diff line number Diff line
@@ -1504,9 +1504,9 @@ public class PackageParser {
     * populating {@link Package#mSigningDetails}. Also asserts that all APK
     * contents are signed correctly and consistently.
     */
    public static void collectCertificates(Package pkg, @ParseFlags int parseFlags)
    public static void collectCertificates(Package pkg, boolean skipVerify)
            throws PackageParserException {
        collectCertificatesInternal(pkg, parseFlags);
        collectCertificatesInternal(pkg, skipVerify);
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            Package childPkg = pkg.childPackages.get(i);
@@ -1514,17 +1514,17 @@ public class PackageParser {
        }
    }

    private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags)
    private static void collectCertificatesInternal(Package pkg, boolean skipVerify)
            throws PackageParserException {
        pkg.mSigningDetails = SigningDetails.UNKNOWN;

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
        try {
            collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
            collectCertificates(pkg, new File(pkg.baseCodePath), skipVerify);

            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
                for (int i = 0; i < pkg.splitCodePaths.length; i++) {
                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), skipVerify);
                }
            }
        } finally {
@@ -1532,7 +1532,7 @@ public class PackageParser {
        }
    }

    private static void collectCertificates(Package pkg, File apkFile, @ParseFlags int parseFlags)
    private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

@@ -1542,7 +1542,7 @@ public class PackageParser {
            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
        }
        SigningDetails verified;
        if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) {
        if (skipVerify) {
            // systemDir APKs are already trusted, save time by not verifying
            verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
                        apkPath, minSignatureScheme);
@@ -1622,9 +1622,10 @@ public class PackageParser {
            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                // TODO: factor signature related items out of Package object
                final Package tempPkg = new Package((String) null);
                final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                try {
                    collectCertificates(tempPkg, apkFile, flags);
                    collectCertificates(tempPkg, apkFile, skipVerify);
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
+14 −0
Original line number Diff line number Diff line
@@ -412,6 +412,20 @@ public class ApkSignatureSchemeV2Verifier {
        }
    }

    static byte[] generateFsverityRootHash(String apkPath)
            throws IOException, SignatureNotFoundException, DigestException,
                   NoSuchAlgorithmException {
        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
            SignatureInfo signatureInfo = findSignature(apk);
            VerifiedSigner vSigner = verify(apk, false);
            if (vSigner.verityRootHash == null) {
                return null;
            }
            return ApkVerityBuilder.generateFsverityRootHash(
                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
        }
    }

    private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
        switch (sigAlgorithm) {
            case SIGNATURE_RSA_PSS_WITH_SHA256:
+14 −0
Original line number Diff line number Diff line
@@ -523,6 +523,20 @@ public class ApkSignatureSchemeV3Verifier {
        }
    }

    static byte[] generateFsverityRootHash(String apkPath)
            throws NoSuchAlgorithmException, DigestException, IOException,
                   SignatureNotFoundException {
        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
            SignatureInfo signatureInfo = findSignature(apk);
            VerifiedSigner vSigner = verify(apk, false);
            if (vSigner.verityRootHash == null) {
                return null;
            }
            return ApkVerityBuilder.generateFsverityRootHash(
                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
        }
    }

    private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
        switch (sigAlgorithm) {
            case SIGNATURE_RSA_PSS_WITH_SHA256:
+21 −0
Original line number Diff line number Diff line
@@ -426,6 +426,27 @@ public class ApkSignatureVerifier {
        return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
    }

    /**
     * Generates the FSVerity root hash from FSVerity header, extensions and Merkle tree root hash
     * in Signing Block.
     *
     * @return FSverity root hash
     */
    public static byte[] generateFsverityRootHash(String apkPath)
            throws NoSuchAlgorithmException, DigestException, IOException {
        // first try v3
        try {
            return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
        } catch (SignatureNotFoundException e) {
            // try older version
        }
        try {
            return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
        } catch (SignatureNotFoundException e) {
            return null;
        }
    }

    /**
     * Result of a successful APK verification operation.
     */
Loading