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

Commit 1d85df2d authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Signature verification for checksums.

Bug: 160605420
Test: atest ChecksumsTest
Change-Id: Ia1cca0c678256defc2d3b781eb75494b9b17496d
parent 3bf626d1
Loading
Loading
Loading
Loading
+34 −4
Original line number Original line Diff line number Diff line
@@ -23,6 +23,9 @@ import android.os.Parcelable;


import com.android.internal.util.DataClass;
import com.android.internal.util.DataClass;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;


@@ -122,6 +125,33 @@ public final class Checksum implements Parcelable {
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {}
    public @interface Type {}


    /**
     * Serialize checksum to the stream in binary format.
     * @hide
     */
    public static void writeToStream(@NonNull DataOutputStream dos, @NonNull Checksum checksum)
            throws IOException {
        dos.writeInt(checksum.getType());

        final byte[] valueBytes = checksum.getValue();
        dos.writeInt(valueBytes.length);
        dos.write(valueBytes);
    }

    /**
     * Deserialize checksum previously stored in
     * {@link #writeToStream(DataOutputStream, Checksum)}.
     * @hide
     */
    public static @NonNull Checksum readFromStream(@NonNull DataInputStream dis)
            throws IOException {
        final int type = dis.readInt();

        final byte[] valueBytes = new byte[dis.readInt()];
        dis.read(valueBytes);
        return new Checksum(type, valueBytes);
    }

    /**
    /**
     * Checksum type.
     * Checksum type.
     */
     */
@@ -133,7 +163,7 @@ public final class Checksum implements Parcelable {






    // Code below generated by codegen v1.0.15.
    // Code below generated by codegen v1.0.22.
    //
    //
    // DO NOT MODIFY!
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    // CHECKSTYLE:OFF Generated code
@@ -233,10 +263,10 @@ public final class Checksum implements Parcelable {
    };
    };


    @DataClass.Generated(
    @DataClass.Generated(
            time = 1601955017774L,
            time = 1611601571576L,
            codegenVersion = "1.0.15",
            codegenVersion = "1.0.22",
            sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
            sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
            inputSignatures = "public static final  int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
            inputSignatures = "public static final  int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static  void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
    @Deprecated
    @Deprecated
    private void __metadata() {}
    private void __metadata() {}


+11 −3
Original line number Original line Diff line number Diff line
@@ -1253,9 +1253,17 @@ public class PackageInstaller {
         *                  {@link #openWrite}
         *                  {@link #openWrite}
         * @param checksums installer intends to make available via
         * @param checksums installer intends to make available via
         *                  {@link PackageManager#requestChecksums}.
         *                  {@link PackageManager#requestChecksums}.
         * @param signature PKCS#7 detached signature bytes over serialized checksums to enable
         * @param signature DER PKCS#7 detached signature bytes over binary serialized checksums
         *                  fs-verity for the checksums or null if fs-verity should not be enabled.
         *                  to enable integrity checking for the checksums or null for no integrity
         *                  @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a>
         *                  checking. {@link PackageManager#requestChecksums} will return
         *                  the certificate used to create signature.
         *                  Binary format for checksums:
         *                  <pre>{@code DataOutputStream dos;
         *                  dos.writeInt(checksum.getType());
         *                  dos.writeInt(checksum.getValue().length);
         *                  dos.write(checksum.getValue());}</pre>
         *                  If using <b>openssl cms</b>, make sure to specify -binary -nosmimecap.
         *                  @see <a href="https://www.openssl.org/docs/man1.0.2/man1/cms.html">openssl cms</a>
         * @throws SecurityException if called after the session has been
         * @throws SecurityException if called after the session has been
         *                           committed or abandoned.
         *                           committed or abandoned.
         * @throws IllegalStateException if checksums for this file have already been added.
         * @throws IllegalStateException if checksums for this file have already been added.
+160 −41
Original line number Original line Diff line number Diff line
@@ -63,8 +63,10 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.security.VerityUtils;
import com.android.server.security.VerityUtils;


import java.io.BufferedInputStream;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.IOException;
@@ -72,17 +74,24 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.security.DigestException;
import java.security.DigestException;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.Set;


import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;

/**
/**
 * Provides checksums for APK.
 * Provides checksums for APK.
 */
 */
@@ -90,6 +99,7 @@ public class ApkChecksums {
    static final String TAG = "ApkChecksums";
    static final String TAG = "ApkChecksums";


    private static final String DIGESTS_FILE_EXTENSION = ".digests";
    private static final String DIGESTS_FILE_EXTENSION = ".digests";
    private static final String DIGESTS_SIGNATURE_FILE_EXTENSION = ".signature";


    // MessageDigest algorithms.
    // MessageDigest algorithms.
    static final String ALGO_MD5 = "MD5";
    static final String ALGO_MD5 = "MD5";
@@ -97,6 +107,8 @@ public class ApkChecksums {
    static final String ALGO_SHA256 = "SHA256";
    static final String ALGO_SHA256 = "SHA256";
    static final String ALGO_SHA512 = "SHA512";
    static final String ALGO_SHA512 = "SHA512";


    private static final Certificate[] EMPTY_CERTIFICATE_ARRAY = {};

    /**
    /**
     * Check back in 1 second after we detected we needed to wait for the APK to be fully available.
     * Check back in 1 second after we detected we needed to wait for the APK to be fully available.
     */
     */
@@ -166,6 +178,21 @@ public class ApkChecksums {
                + DIGESTS_FILE_EXTENSION;
                + DIGESTS_FILE_EXTENSION;
    }
    }


    /**
     * Return the signature path associated with the given digests path.
     * (appends '.signature' to the end)
     */
    public static String buildSignaturePathForDigests(String digestsPath) {
        return digestsPath + DIGESTS_SIGNATURE_FILE_EXTENSION;
    }

    /** Returns true if the given file looks like containing digests or digests' signature. */
    public static boolean isDigestOrDigestSignatureFile(File file) {
        final String name = file.getName();
        return name.endsWith(DIGESTS_FILE_EXTENSION) || name.endsWith(
                DIGESTS_SIGNATURE_FILE_EXTENSION);
    }

    /**
    /**
     * Search for the digests file associated with the given target file.
     * Search for the digests file associated with the given target file.
     * If it exists, the method returns the digests file; otherwise it returns null.
     * If it exists, the method returns the digests file; otherwise it returns null.
@@ -176,41 +203,91 @@ public class ApkChecksums {
        return digestsFile.exists() ? digestsFile : null;
        return digestsFile.exists() ? digestsFile : null;
    }
    }


    /**
     * Search for the signature file associated with the given digests file.
     * If it exists, the method returns the signature file; otherwise it returns null.
     */
    public static File findSignatureForDigests(File digestsFile) {
        String signaturePath = buildSignaturePathForDigests(digestsFile.getAbsolutePath());
        File signatureFile = new File(signaturePath);
        return signatureFile.exists() ? signatureFile : null;
    }

    /**
    /**
     * Serialize checksums to the stream in binary format.
     * Serialize checksums to the stream in binary format.
     */
     */
    public static void writeChecksums(OutputStream os, Checksum[] checksums)
    public static void writeChecksums(OutputStream os, Checksum[] checksums)
            throws IOException, CertificateException {
            throws IOException {
        try (DataOutputStream dos = new DataOutputStream(os)) {
        try (DataOutputStream dos = new DataOutputStream(os)) {
            dos.writeInt(checksums.length);
            for (Checksum checksum : checksums) {
            for (Checksum checksum : checksums) {
                dos.writeInt(checksum.getType());
                Checksum.writeToStream(dos, checksum);

                final byte[] valueBytes = checksum.getValue();
                dos.writeInt(valueBytes.length);
                dos.write(valueBytes);
            }
            }
        }
        }
    }
    }


    private static Checksum[] readChecksums(File file) throws IOException {
        try (InputStream is = new FileInputStream(file)) {
            return readChecksums(is);
        }
    }

    /**
    /**
     * Deserialize array of checksums previously stored in
     * Deserialize array of checksums previously stored in
     * {@link #writeChecksums(OutputStream, Checksum[])}.
     * {@link #writeChecksums(OutputStream, Checksum[])}.
     */
     */
    private static Checksum[] readChecksums(File file) throws IOException {
    public static Checksum[] readChecksums(InputStream is) throws IOException {
        try (InputStream is = new FileInputStream(file);
        try (DataInputStream dis = new DataInputStream(is)) {
             DataInputStream dis = new DataInputStream(is)) {
            ArrayList<Checksum> checksums = new ArrayList<>();
            final int size = dis.readInt();
            try {
            Checksum[] checksums = new Checksum[size];
                // 100 is an arbitrary very big number. We should stop at EOF.
            for (int i = 0; i < size; ++i) {
                for (int i = 0; i < 100; ++i) {
                final int type = dis.readInt();
                    checksums.add(Checksum.readFromStream(dis));
                }
            } catch (EOFException e) {
                // expected
            }
            return checksums.toArray(new Checksum[checksums.size()]);
        }
    }


                final byte[] valueBytes = new byte[dis.readInt()];
    /**
                dis.read(valueBytes);
     * Verifies signature over binary serialized checksums.
                checksums[i] = new Checksum(type, valueBytes);
     * @param checksums array of checksums
     * @param signature detached PKCS7 signature in DER format
     * @return all certificates that passed verification
     * @throws SignatureException if verification fails
     */
    public static @NonNull Certificate[] verifySignature(Checksum[] checksums, byte[] signature)
            throws NoSuchAlgorithmException, IOException, SignatureException {
        final byte[] blob;
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            writeChecksums(os, checksums);
            blob = os.toByteArray();
        }
        }
            return checksums;

        PKCS7 pkcs7 = new PKCS7(signature);

        final Certificate[] certs = pkcs7.getCertificates();
        if (certs == null || certs.length == 0) {
            throw new SignatureException("Signature missing certificates");
        }

        final SignerInfo[] signerInfos = pkcs7.verify(blob);
        if (signerInfos == null || signerInfos.length == 0) {
            throw new SignatureException("Verification failed");
        }
        }

        ArrayList<Certificate> certificates = new ArrayList<>(signerInfos.length);
        for (SignerInfo signerInfo : signerInfos) {
            ArrayList<X509Certificate> chain = signerInfo.getCertificateChain(pkcs7);
            if (chain == null) {
                throw new SignatureException(
                        "Verification passed, but certification chain is empty.");
            }
            certificates.addAll(chain);
        }

        return certificates.toArray(new Certificate[certificates.size()]);
    }
    }


    /**
    /**
@@ -335,6 +412,8 @@ public class ApkChecksums {
            }
            }
        }
        }


        // Note: this compares installer and system digests internally and
        // has to be called right after all system digests are populated.
        getInstallerChecksums(split, file, types, installerPackageName, trustedInstallers,
        getInstallerChecksums(split, file, types, installerPackageName, trustedInstallers,
                checksums, injector);
                checksums, injector);
    }
    }
@@ -352,11 +431,32 @@ public class ApkChecksums {
            return;
            return;
        }
        }


        final File digestsFile = new File(buildDigestsPathForApk(file.getAbsolutePath()));
        final File digestsFile = findDigestsForFile(file);
        if (!digestsFile.exists()) {
        if (digestsFile == null) {
            return;
            return;
        }
        }
        final File signatureFile = findSignatureForDigests(digestsFile);

        try {
            final Checksum[] digests = readChecksums(digestsFile);
            final Signature[] certs;
            final Signature[] pastCerts;

            if (signatureFile != null) {
                final Certificate[] certificates = verifySignature(digests,
                        Files.readAllBytes(signatureFile.toPath()));
                if (certificates == null || certificates.length == 0) {
                    Slog.e(TAG, "Error validating signature");
                    return;
                }

                certs = new Signature[certificates.length];
                for (int i = 0, size = certificates.length; i < size; i++) {
                    certs[i] = new Signature(certificates[i].getEncoded());
                }


                pastCerts = null;
            } else {
                final AndroidPackage installer = injector.getPackageManagerInternal().getPackage(
                final AndroidPackage installer = injector.getPackageManagerInternal().getPackage(
                        installerPackageName);
                        installerPackageName);
                if (installer == null) {
                if (installer == null) {
@@ -365,18 +465,18 @@ public class ApkChecksums {
                }
                }


                // Obtaining array of certificates used for signing the installer package.
                // Obtaining array of certificates used for signing the installer package.
        final Signature[] certs = installer.getSigningDetails().signatures;
                certs = installer.getSigningDetails().signatures;
        final Signature[] pastCerts = installer.getSigningDetails().pastSigningCertificates;
                pastCerts = installer.getSigningDetails().pastSigningCertificates;
            }
            if (certs == null || certs.length == 0 || certs[0] == null) {
            if (certs == null || certs.length == 0 || certs[0] == null) {
            Slog.e(TAG, "Can't obtain calling installer package's certificates.");
                Slog.e(TAG, "Can't obtain certificates.");
                return;
                return;
            }
            }
        // According to V2/V3 signing schema, the first certificate corresponds to the public key

        // in the signing block.
            // According to V2/V3 signing schema, the first certificate corresponds to the public
            // key in the signing block.
            byte[] trustedCertBytes = certs[0].toByteArray();
            byte[] trustedCertBytes = certs[0].toByteArray();


        try {
            final Checksum[] digests = readChecksums(digestsFile);
            final Set<Signature> trusted = convertToSet(trustedInstallers);
            final Set<Signature> trusted = convertToSet(trustedInstallers);


            if (trusted != null && !trusted.isEmpty()) {
            if (trusted != null && !trusted.isEmpty()) {
@@ -391,6 +491,16 @@ public class ApkChecksums {
                trustedCertBytes = trustedCert.toByteArray();
                trustedCertBytes = trustedCert.toByteArray();
            }
            }


            // Compare OS-enforced digests.
            for (Checksum digest : digests) {
                final ApkChecksum system = checksums.get(digest.getType());
                if (system != null && !Arrays.equals(system.getValue(), digest.getValue())) {
                    throw new InvalidParameterException("System digest " + digest.getType()
                            + " mismatch, can't bind installer-provided digests to the APK.");
                }
            }

            // Append missing digests.
            for (Checksum digest : digests) {
            for (Checksum digest : digests) {
                if (isRequired(digest.getType(), types, checksums)) {
                if (isRequired(digest.getType(), types, checksums)) {
                    checksums.put(digest.getType(),
                    checksums.put(digest.getType(),
@@ -398,7 +508,16 @@ public class ApkChecksums {
                }
                }
            }
            }
        } catch (IOException e) {
        } catch (IOException e) {
            Slog.e(TAG, "Error reading .digests", e);
            Slog.e(TAG, "Error reading .digests or .signature", e);
        } catch (NoSuchAlgorithmException | SignatureException | InvalidParameterException e) {
            Slog.e(TAG, "Error validating digests. Invalid digests will be removed", e);
            try {
                Files.deleteIfExists(digestsFile.toPath());
                if (signatureFile != null) {
                    Files.deleteIfExists(signatureFile.toPath());
                }
            } catch (IOException ignored) {
            }
        } catch (CertificateEncodingException e) {
        } catch (CertificateEncodingException e) {
            Slog.e(TAG, "Error encoding trustedInstallers", e);
            Slog.e(TAG, "Error encoding trustedInstallers", e);
        }
        }
+26 −18
Original line number Original line Diff line number Diff line
@@ -27,7 +27,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_CREAT;
@@ -59,7 +59,6 @@ import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.Checksum;
import android.content.pm.Checksum;
import android.content.pm.DataLoaderManager;
import android.content.pm.DataLoaderManager;
@@ -160,7 +159,9 @@ import java.io.FileDescriptor;
import java.io.FileFilter;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
@@ -249,7 +250,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
    private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
    private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
    private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
    private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
    private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
    private static final ApkChecksum[] EMPTY_FILE_CHECKSUM_ARRAY = {};


    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
    private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
    private static final String APEX_FILE_EXTENSION = ".apex";
    private static final String APEX_FILE_EXTENSION = ".apex";
@@ -805,6 +805,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
            if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
            if (DexMetadataHelper.isDexMetadataFile(file)) return false;
            if (DexMetadataHelper.isDexMetadataFile(file)) return false;
            if (VerityUtils.isFsveritySignatureFile(file)) return false;
            if (VerityUtils.isFsveritySignatureFile(file)) return false;
            if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false;
            return true;
            return true;
        }
        }
    };
    };
@@ -1294,13 +1295,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }
        }


        if (signature != null && signature.length != 0) {
        if (signature != null && signature.length != 0) {
            final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
            try {
            final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
                Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
            if (!standardMode || legacyMode) {
            } catch (IOException | NoSuchAlgorithmException | SignatureException e) {
                Slog.e(TAG,
                throw new IllegalArgumentException("Can't verify signature", e);
                        "Can't enforce checksum's signature: Apk-Verity is disabled or in legacy "
                                + "mode.");
                signature = null;
            }
            }
        }
        }


@@ -3093,30 +3091,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
        final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
        final File targetDigestsFile = new File(stageDir, targetDigestsPath);
        final File targetDigestsFile = new File(stageDir, targetDigestsPath);
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            // Storing and staging checksums.
            ApkChecksums.writeChecksums(os, checksums);
            ApkChecksums.writeChecksums(os, checksums);

            final byte[] signature = perFileChecksum.getSignature();
            if (signature != null && signature.length > 0) {
                Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
            }

            // Storing and staging checksums.
            storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
            storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
                    os.toByteArray());
                    os.toByteArray());
            stageFileLocked(targetDigestsFile, targetDigestsFile);
            stageFileLocked(targetDigestsFile, targetDigestsFile);


            final byte[] signature = perFileChecksum.getSignature();
            // Storing and staging signature.
            if (signature == null || signature.length == 0) {
            if (signature == null || signature.length == 0) {
                return;
                return;
            }
            }


            // Storing and staging signature.
            final String targetDigestsSignaturePath = ApkChecksums.buildSignaturePathForDigests(
            final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath(
                    targetDigestsPath);
                    targetDigestsPath);
            final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
            final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
            storeBytesToInstallationFile(targetDigestsSignaturePath,
            storeBytesToInstallationFile(targetDigestsSignaturePath,
                    targetDigestsSignatureFile.getAbsolutePath(), signature);
                    targetDigestsSignatureFile.getAbsolutePath(), signature);
            stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
            stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
        } catch (CertificateException e) {
            throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                    "Failed to encode certificate for " + mPackageName, e);
        } catch (IOException e) {
        } catch (IOException e) {
            throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
            throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                    "Failed to store digests for " + mPackageName, e);
                    "Failed to store digests for " + mPackageName, e);
        } catch (NoSuchAlgorithmException | SignatureException e) {
            throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "Failed to verify digests' signature for " + mPackageName, e);
        }
        }
    }
    }


@@ -3160,6 +3163,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
        final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
        if (digestsFile != null) {
        if (digestsFile != null) {
            mResolvedInheritedFiles.add(digestsFile);
            mResolvedInheritedFiles.add(digestsFile);

            final File signatureFile = ApkChecksums.findSignatureForDigests(digestsFile);
            if (signatureFile != null) {
                mResolvedInheritedFiles.add(signatureFile);
            }
        }
        }
    }
    }