Merge "Signature verification for checksums."
This commit is contained in:
committed by
Android (Google) Code Review
commit
50b077d6f5
@@ -23,6 +23,9 @@ import android.os.Parcelable;
|
||||
|
||||
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.RetentionPolicy;
|
||||
|
||||
@@ -122,6 +125,33 @@ public final class Checksum implements Parcelable {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
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.
|
||||
*/
|
||||
@@ -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!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
@@ -233,10 +263,10 @@ public final class Checksum implements Parcelable {
|
||||
};
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1601955017774L,
|
||||
codegenVersion = "1.0.15",
|
||||
time = 1611601571576L,
|
||||
codegenVersion = "1.0.22",
|
||||
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
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -1253,9 +1253,17 @@ public class PackageInstaller {
|
||||
* {@link #openWrite}
|
||||
* @param checksums installer intends to make available via
|
||||
* {@link PackageManager#requestChecksums}.
|
||||
* @param signature PKCS#7 detached signature bytes over serialized checksums to enable
|
||||
* fs-verity for the checksums or null if fs-verity should not be enabled.
|
||||
* @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a>
|
||||
* @param signature DER PKCS#7 detached signature bytes over binary serialized checksums
|
||||
* to enable integrity checking for the checksums or null for no integrity
|
||||
* 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
|
||||
* committed or abandoned.
|
||||
* @throws IllegalStateException if checksums for this file have already been added.
|
||||
|
||||
@@ -63,8 +63,10 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.security.VerityUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -72,17 +74,24 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.security.DigestException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.security.pkcs.PKCS7;
|
||||
import sun.security.pkcs.SignerInfo;
|
||||
|
||||
/**
|
||||
* Provides checksums for APK.
|
||||
*/
|
||||
@@ -90,6 +99,7 @@ public class ApkChecksums {
|
||||
static final String TAG = "ApkChecksums";
|
||||
|
||||
private static final String DIGESTS_FILE_EXTENSION = ".digests";
|
||||
private static final String DIGESTS_SIGNATURE_FILE_EXTENSION = ".signature";
|
||||
|
||||
// MessageDigest algorithms.
|
||||
static final String ALGO_MD5 = "MD5";
|
||||
@@ -97,6 +107,8 @@ public class ApkChecksums {
|
||||
static final String ALGO_SHA256 = "SHA256";
|
||||
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.
|
||||
*/
|
||||
@@ -166,6 +178,21 @@ public class ApkChecksums {
|
||||
+ 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.
|
||||
* If it exists, the method returns the digests file; otherwise it returns null.
|
||||
@@ -176,43 +203,93 @@ public class ApkChecksums {
|
||||
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.
|
||||
*/
|
||||
public static void writeChecksums(OutputStream os, Checksum[] checksums)
|
||||
throws IOException, CertificateException {
|
||||
throws IOException {
|
||||
try (DataOutputStream dos = new DataOutputStream(os)) {
|
||||
dos.writeInt(checksums.length);
|
||||
for (Checksum checksum : checksums) {
|
||||
dos.writeInt(checksum.getType());
|
||||
|
||||
final byte[] valueBytes = checksum.getValue();
|
||||
dos.writeInt(valueBytes.length);
|
||||
dos.write(valueBytes);
|
||||
Checksum.writeToStream(dos, checksum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Checksum[] readChecksums(File file) throws IOException {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
return readChecksums(is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize array of checksums previously stored in
|
||||
* {@link #writeChecksums(OutputStream, Checksum[])}.
|
||||
*/
|
||||
private static Checksum[] readChecksums(File file) throws IOException {
|
||||
try (InputStream is = new FileInputStream(file);
|
||||
DataInputStream dis = new DataInputStream(is)) {
|
||||
final int size = dis.readInt();
|
||||
Checksum[] checksums = new Checksum[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final int type = dis.readInt();
|
||||
|
||||
final byte[] valueBytes = new byte[dis.readInt()];
|
||||
dis.read(valueBytes);
|
||||
checksums[i] = new Checksum(type, valueBytes);
|
||||
public static Checksum[] readChecksums(InputStream is) throws IOException {
|
||||
try (DataInputStream dis = new DataInputStream(is)) {
|
||||
ArrayList<Checksum> checksums = new ArrayList<>();
|
||||
try {
|
||||
// 100 is an arbitrary very big number. We should stop at EOF.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
checksums.add(Checksum.readFromStream(dis));
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
// expected
|
||||
}
|
||||
return checksums;
|
||||
return checksums.toArray(new Checksum[checksums.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies signature over binary serialized checksums.
|
||||
* @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();
|
||||
}
|
||||
|
||||
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()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch or calculate checksums for the collection of files.
|
||||
*
|
||||
@@ -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,
|
||||
checksums, injector);
|
||||
}
|
||||
@@ -352,31 +431,52 @@ public class ApkChecksums {
|
||||
return;
|
||||
}
|
||||
|
||||
final File digestsFile = new File(buildDigestsPathForApk(file.getAbsolutePath()));
|
||||
if (!digestsFile.exists()) {
|
||||
final File digestsFile = findDigestsForFile(file);
|
||||
if (digestsFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final AndroidPackage installer = injector.getPackageManagerInternal().getPackage(
|
||||
installerPackageName);
|
||||
if (installer == null) {
|
||||
Slog.e(TAG, "Installer package not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtaining array of certificates used for signing the installer package.
|
||||
final Signature[] certs = installer.getSigningDetails().signatures;
|
||||
final Signature[] pastCerts = installer.getSigningDetails().pastSigningCertificates;
|
||||
if (certs == null || certs.length == 0 || certs[0] == null) {
|
||||
Slog.e(TAG, "Can't obtain calling installer package's certificates.");
|
||||
return;
|
||||
}
|
||||
// According to V2/V3 signing schema, the first certificate corresponds to the public key
|
||||
// in the signing block.
|
||||
byte[] trustedCertBytes = certs[0].toByteArray();
|
||||
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(
|
||||
installerPackageName);
|
||||
if (installer == null) {
|
||||
Slog.e(TAG, "Installer package not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtaining array of certificates used for signing the installer package.
|
||||
certs = installer.getSigningDetails().signatures;
|
||||
pastCerts = installer.getSigningDetails().pastSigningCertificates;
|
||||
}
|
||||
if (certs == null || certs.length == 0 || certs[0] == null) {
|
||||
Slog.e(TAG, "Can't obtain certificates.");
|
||||
return;
|
||||
}
|
||||
|
||||
// According to V2/V3 signing schema, the first certificate corresponds to the public
|
||||
// key in the signing block.
|
||||
byte[] trustedCertBytes = certs[0].toByteArray();
|
||||
|
||||
final Set<Signature> trusted = convertToSet(trustedInstallers);
|
||||
|
||||
if (trusted != null && !trusted.isEmpty()) {
|
||||
@@ -391,6 +491,16 @@ public class ApkChecksums {
|
||||
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) {
|
||||
if (isRequired(digest.getType(), types, checksums)) {
|
||||
checksums.put(digest.getType(),
|
||||
@@ -398,7 +508,16 @@ public class ApkChecksums {
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
Slog.e(TAG, "Error encoding trustedInstallers", e);
|
||||
}
|
||||
|
||||
@@ -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_MEDIA_UNAVAILABLE;
|
||||
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_SUCCEEDED;
|
||||
import static android.system.OsConstants.O_CREAT;
|
||||
@@ -59,7 +59,6 @@ import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ApkChecksum;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.Checksum;
|
||||
import android.content.pm.DataLoaderManager;
|
||||
@@ -160,7 +159,9 @@ import java.io.FileDescriptor;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileOutputStream;
|
||||
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.Arrays;
|
||||
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 int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
|
||||
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 APEX_FILE_EXTENSION = ".apex";
|
||||
@@ -805,6 +805,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
|
||||
if (DexMetadataHelper.isDexMetadataFile(file)) return false;
|
||||
if (VerityUtils.isFsveritySignatureFile(file)) return false;
|
||||
if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1294,13 +1295,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
if (signature != null && signature.length != 0) {
|
||||
final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
|
||||
final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
|
||||
if (!standardMode || legacyMode) {
|
||||
Slog.e(TAG,
|
||||
"Can't enforce checksum's signature: Apk-Verity is disabled or in legacy "
|
||||
+ "mode.");
|
||||
signature = null;
|
||||
try {
|
||||
Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
|
||||
} catch (IOException | NoSuchAlgorithmException | SignatureException e) {
|
||||
throw new IllegalArgumentException("Can't verify signature", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3093,30 +3091,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
|
||||
final File targetDigestsFile = new File(stageDir, targetDigestsPath);
|
||||
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||
// Storing and staging 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(),
|
||||
os.toByteArray());
|
||||
stageFileLocked(targetDigestsFile, targetDigestsFile);
|
||||
|
||||
final byte[] signature = perFileChecksum.getSignature();
|
||||
// Storing and staging signature.
|
||||
if (signature == null || signature.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Storing and staging signature.
|
||||
final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath(
|
||||
final String targetDigestsSignaturePath = ApkChecksums.buildSignaturePathForDigests(
|
||||
targetDigestsPath);
|
||||
final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
|
||||
storeBytesToInstallationFile(targetDigestsSignaturePath,
|
||||
targetDigestsSignatureFile.getAbsolutePath(), signature);
|
||||
stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
|
||||
} catch (CertificateException e) {
|
||||
throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
|
||||
"Failed to encode certificate for " + mPackageName, e);
|
||||
} catch (IOException e) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
|
||||
"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);
|
||||
if (digestsFile != null) {
|
||||
mResolvedInheritedFiles.add(digestsFile);
|
||||
|
||||
final File signatureFile = ApkChecksums.findSignatureForDigests(digestsFile);
|
||||
if (signatureFile != null) {
|
||||
mResolvedInheritedFiles.add(signatureFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user