Loading core/java/android/content/pm/PackageManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -3157,6 +3157,23 @@ public abstract class PackageManager { public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE = "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE"; /** * Extra field name for the Merkle tree root hash of a package. * <p>Passed to a package verifier both prior to verification and as a result * of verification. * <p>The value of the extra is a specially formatted list: * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N} * <p>The extra must include an entry for every APK within an installation. If * a hash is not physically present, a hash value of {@code 0} will be used. * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block * size. See the description of the * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a> * for more details. * @hide */ public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH"; /** * Extra field name for the ID of a intent filter pending verification. * Passed to an intent filter verifier and is used to call back to Loading core/java/android/os/incremental/V4Signature.java +21 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os.incremental; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.ParcelFileDescriptor; import java.io.ByteArrayInputStream; Loading Loading @@ -45,8 +47,8 @@ public class V4Signature { public static class HashingInfo { public final int hashAlgorithm; // only 1 == SHA256 supported public final byte log2BlockSize; // only 12 (block size 4096) supported now public final byte[] salt; // used exactly as in fs-verity, 32 bytes max public final byte[] rawRootHash; // salted digest of the first Merkle tree page @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) { this.hashAlgorithm = hashAlgorithm; Loading @@ -58,7 +60,8 @@ public class V4Signature { /** * Constructs HashingInfo from byte array. */ public static HashingInfo fromByteArray(byte[] bytes) throws IOException { @NonNull public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); final int hashAlgorithm = buffer.getInt(); final byte log2BlockSize = buffer.get(); Loading Loading @@ -106,8 +109,18 @@ public class V4Signature { } public final int version; // Always 2 for now. public final byte[] hashingInfo; public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later. /** * Raw byte array containing the IncFS hashing data. * @see HashingInfo#fromByteArray(byte[]) */ @Nullable public final byte[] hashingInfo; /** * Raw byte array containing the V4 signature data. * <p>Passed as-is to the kernel. Can be retrieved later. * @see SigningInfo#fromByteArray(byte[]) */ @Nullable public final byte[] signingInfo; /** * Construct a V4Signature from .idsig file. Loading @@ -121,7 +134,8 @@ public class V4Signature { /** * Construct a V4Signature from a byte array. */ public static V4Signature readFrom(byte[] bytes) throws IOException { @NonNull public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException { try (InputStream stream = new ByteArrayInputStream(bytes)) { return readFrom(stream); } Loading Loading @@ -169,7 +183,7 @@ public class V4Signature { return this.version == SUPPORTED_VERSION; } private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) { private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) { this.version = version; this.hashingInfo = hashingInfo; this.signingInfo = signingInfo; Loading services/core/java/com/android/server/pm/PackageManagerService.java +46 −8 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.incremental.IncrementalManager.isIncrementalPath; Loading Loading @@ -1820,10 +1821,12 @@ public class PackageManagerService extends IPackageManager.Stub state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, user); PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType, user); } else { broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, user); PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType, user); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); state.setVerifierResponse(Binder.getCallingUid(), Loading Loading @@ -1899,7 +1902,7 @@ public class PackageManagerService extends IPackageManager.Stub if (state.isInstallAllowed()) { broadcastPackageVerified(verificationId, originUri, response.code, args.getUser()); response.code, null, args.mDataLoaderType, args.getUser()); } else { params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); Loading Loading @@ -13589,12 +13592,17 @@ public class PackageManagerService extends IPackageManager.Stub } private void broadcastPackageVerified(int verificationId, Uri packageUri, int verificationCode, UserHandle user) { int verificationCode, @Nullable String rootHashString, int dataLoaderType, UserHandle user) { final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode); if (rootHashString != null) { intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString); } intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); mContext.sendBroadcastAsUser(intent, user, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT); Loading Loading @@ -14966,8 +14974,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationState.setRequiredVerifierUid(requiredUid); final int installerUid = verificationInfo == null ? -1 : verificationInfo.installerUid; if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) { final boolean isVerificationEnabled = isVerificationEnabled( pkgLite, verifierUser.getIdentifier(), installFlags, installerUid); final boolean isV4Signed = (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4); final boolean isIncrementalInstall = (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL); // NOTE: We purposefully skip verification for only incremental installs when there's // a v4 signature block. Otherwise, proceed with verification as usual. if (!origin.existing && isVerificationEnabled && (!isIncrementalInstall || !isV4Signed)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); Loading Loading @@ -16584,7 +16601,29 @@ public class PackageManagerService extends IPackageManager.Stub } executePostCommitSteps(commitRequest); } finally { if (!success) { if (success) { for (InstallRequest request : requests) { final InstallArgs args = request.args; if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) { continue; } if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) { continue; } // For incremental installs, we bypass the verifier prior to install. Now // that we know the package is valid, send a notice to the verifier with // the root hash of the base.apk. final String baseCodePath = request.installResult.pkg.getBaseCodePath(); final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths(); final Uri originUri = Uri.fromFile(args.origin.resolvedFile); final int verificationId = mPendingVerificationToken++; final String rootHashString = PackageManagerServiceUtils .buildVerificationRootHashString(baseCodePath, splitCodePaths); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, rootHashString, args.mDataLoaderType, args.getUser()); } } else { for (ScanResult result : preparedScans.values()) { if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(), false)) { Loading Loading @@ -16926,7 +16965,6 @@ public class PackageManagerService extends IPackageManager.Stub if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { parsedPackage.setSigningDetails(args.signingDetails); } else { // TODO(b/136132412): skip for Incremental installation parsedPackage.setSigningDetails( ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */)); } services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +71 −4 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; Loading @@ -40,7 +39,6 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.pm.parsing.ParsingPackageUtils; import android.os.Build; import android.os.Debug; import android.os.Environment; Loading @@ -50,6 +48,9 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManagerInternal; import android.os.incremental.IncrementalManager; import android.os.incremental.V4Signature; import android.os.incremental.V4Signature.HashingInfo; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; Loading @@ -62,6 +63,7 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.HexDump; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; Loading Loading @@ -94,8 +96,6 @@ import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; Loading Loading @@ -943,4 +943,71 @@ public class PackageManagerServiceUtils { Os.chmod(currentDir.getAbsolutePath(), mode); } } /** * Returns a string that's compatible with the verification root hash extra. * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH */ @NonNull public static String buildVerificationRootHashString(@NonNull String baseFilename, @Nullable String[] splitFilenameArray) { final StringBuilder sb = new StringBuilder(); final String baseFilePath = baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1); sb.append(baseFilePath).append(":"); final byte[] baseRootHash = getRootHash(baseFilename); if (baseRootHash == null) { sb.append("0"); } else { sb.append(HexDump.toHexString(baseRootHash)); } if (splitFilenameArray == null || splitFilenameArray.length == 0) { return sb.toString(); } for (int i = splitFilenameArray.length - 1; i >= 0; i--) { final String splitFilename = splitFilenameArray[i]; final String splitFilePath = splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1); final byte[] splitRootHash = getRootHash(splitFilename); sb.append(";").append(splitFilePath).append(":"); if (splitRootHash == null) { sb.append("0"); } else { sb.append(HexDump.toHexString(splitRootHash)); } } return sb.toString(); } /** * Returns the root has for the given file. * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated. * <p>NOTE: This currently only works on files stored on the incremental file system. The * eventual goal is that this hash [among others] can be retrieved for any file. */ @Nullable private static byte[] getRootHash(String filename) { try { final byte[] baseFileSignature = IncrementalManager.unsafeGetFileSignature(filename); if (baseFileSignature == null) { throw new IOException("File signature not present"); } final V4Signature signature = V4Signature.readFrom(baseFileSignature); if (signature.hashingInfo == null) { throw new IOException("Hashing info not present"); } final HashingInfo hashInfo = HashingInfo.fromByteArray(signature.hashingInfo); if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) { throw new IOException("Root has not present"); } return hashInfo.rawRootHash; } catch (IOException ignore) { Slog.e(TAG, "ERROR: could not load root hash from incremental install"); } return null; } } Loading
core/java/android/content/pm/PackageManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -3157,6 +3157,23 @@ public abstract class PackageManager { public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE = "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE"; /** * Extra field name for the Merkle tree root hash of a package. * <p>Passed to a package verifier both prior to verification and as a result * of verification. * <p>The value of the extra is a specially formatted list: * {@code filename1:HASH_1;filename2:HASH_2;...;filenameN:HASH_N} * <p>The extra must include an entry for every APK within an installation. If * a hash is not physically present, a hash value of {@code 0} will be used. * <p>The root hash is generated using SHA-256, no salt with a 4096 byte block * size. See the description of the * <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#merkle-tree">fs-verity merkle-tree</a> * for more details. * @hide */ public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.EXTRA_VERIFICATION_ROOT_HASH"; /** * Extra field name for the ID of a intent filter pending verification. * Passed to an intent filter verifier and is used to call back to Loading
core/java/android/os/incremental/V4Signature.java +21 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os.incremental; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.ParcelFileDescriptor; import java.io.ByteArrayInputStream; Loading Loading @@ -45,8 +47,8 @@ public class V4Signature { public static class HashingInfo { public final int hashAlgorithm; // only 1 == SHA256 supported public final byte log2BlockSize; // only 12 (block size 4096) supported now public final byte[] salt; // used exactly as in fs-verity, 32 bytes max public final byte[] rawRootHash; // salted digest of the first Merkle tree page @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) { this.hashAlgorithm = hashAlgorithm; Loading @@ -58,7 +60,8 @@ public class V4Signature { /** * Constructs HashingInfo from byte array. */ public static HashingInfo fromByteArray(byte[] bytes) throws IOException { @NonNull public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); final int hashAlgorithm = buffer.getInt(); final byte log2BlockSize = buffer.get(); Loading Loading @@ -106,8 +109,18 @@ public class V4Signature { } public final int version; // Always 2 for now. public final byte[] hashingInfo; public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later. /** * Raw byte array containing the IncFS hashing data. * @see HashingInfo#fromByteArray(byte[]) */ @Nullable public final byte[] hashingInfo; /** * Raw byte array containing the V4 signature data. * <p>Passed as-is to the kernel. Can be retrieved later. * @see SigningInfo#fromByteArray(byte[]) */ @Nullable public final byte[] signingInfo; /** * Construct a V4Signature from .idsig file. Loading @@ -121,7 +134,8 @@ public class V4Signature { /** * Construct a V4Signature from a byte array. */ public static V4Signature readFrom(byte[] bytes) throws IOException { @NonNull public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException { try (InputStream stream = new ByteArrayInputStream(bytes)) { return readFrom(stream); } Loading Loading @@ -169,7 +183,7 @@ public class V4Signature { return this.version == SUPPORTED_VERSION; } private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) { private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) { this.version = version; this.hashingInfo = hashingInfo; this.signingInfo = signingInfo; Loading
services/core/java/com/android/server/pm/PackageManagerService.java +46 −8 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.incremental.IncrementalManager.isIncrementalPath; Loading Loading @@ -1820,10 +1821,12 @@ public class PackageManagerService extends IPackageManager.Stub state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, user); PackageManager.VERIFICATION_ALLOW, null, args.mDataLoaderType, user); } else { broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, user); PackageManager.VERIFICATION_REJECT, null, args.mDataLoaderType, user); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); state.setVerifierResponse(Binder.getCallingUid(), Loading Loading @@ -1899,7 +1902,7 @@ public class PackageManagerService extends IPackageManager.Stub if (state.isInstallAllowed()) { broadcastPackageVerified(verificationId, originUri, response.code, args.getUser()); response.code, null, args.mDataLoaderType, args.getUser()); } else { params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); Loading Loading @@ -13589,12 +13592,17 @@ public class PackageManagerService extends IPackageManager.Stub } private void broadcastPackageVerified(int verificationId, Uri packageUri, int verificationCode, UserHandle user) { int verificationCode, @Nullable String rootHashString, int dataLoaderType, UserHandle user) { final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId); intent.putExtra(PackageManager.EXTRA_VERIFICATION_RESULT, verificationCode); if (rootHashString != null) { intent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString); } intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); mContext.sendBroadcastAsUser(intent, user, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT); Loading Loading @@ -14966,8 +14974,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationState.setRequiredVerifierUid(requiredUid); final int installerUid = verificationInfo == null ? -1 : verificationInfo.installerUid; if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) { final boolean isVerificationEnabled = isVerificationEnabled( pkgLite, verifierUser.getIdentifier(), installFlags, installerUid); final boolean isV4Signed = (mArgs.signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4); final boolean isIncrementalInstall = (mArgs.mDataLoaderType == DataLoaderType.INCREMENTAL); // NOTE: We purposefully skip verification for only incremental installs when there's // a v4 signature block. Otherwise, proceed with verification as usual. if (!origin.existing && isVerificationEnabled && (!isIncrementalInstall || !isV4Signed)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); Loading Loading @@ -16584,7 +16601,29 @@ public class PackageManagerService extends IPackageManager.Stub } executePostCommitSteps(commitRequest); } finally { if (!success) { if (success) { for (InstallRequest request : requests) { final InstallArgs args = request.args; if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) { continue; } if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) { continue; } // For incremental installs, we bypass the verifier prior to install. Now // that we know the package is valid, send a notice to the verifier with // the root hash of the base.apk. final String baseCodePath = request.installResult.pkg.getBaseCodePath(); final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths(); final Uri originUri = Uri.fromFile(args.origin.resolvedFile); final int verificationId = mPendingVerificationToken++; final String rootHashString = PackageManagerServiceUtils .buildVerificationRootHashString(baseCodePath, splitCodePaths); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, rootHashString, args.mDataLoaderType, args.getUser()); } } else { for (ScanResult result : preparedScans.values()) { if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(), false)) { Loading Loading @@ -16926,7 +16965,6 @@ public class PackageManagerService extends IPackageManager.Stub if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { parsedPackage.setSigningDetails(args.signingDetails); } else { // TODO(b/136132412): skip for Incremental installation parsedPackage.setSigningDetails( ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */)); }
services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +71 −4 Original line number Diff line number Diff line Loading @@ -32,7 +32,6 @@ import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; Loading @@ -40,7 +39,6 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.pm.parsing.ParsingPackageUtils; import android.os.Build; import android.os.Debug; import android.os.Environment; Loading @@ -50,6 +48,9 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManagerInternal; import android.os.incremental.IncrementalManager; import android.os.incremental.V4Signature; import android.os.incremental.V4Signature.HashingInfo; import android.service.pm.PackageServiceDumpProto; import android.system.ErrnoException; import android.system.Os; Loading @@ -62,6 +63,7 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.HexDump; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; Loading Loading @@ -94,8 +96,6 @@ import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; Loading Loading @@ -943,4 +943,71 @@ public class PackageManagerServiceUtils { Os.chmod(currentDir.getAbsolutePath(), mode); } } /** * Returns a string that's compatible with the verification root hash extra. * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH */ @NonNull public static String buildVerificationRootHashString(@NonNull String baseFilename, @Nullable String[] splitFilenameArray) { final StringBuilder sb = new StringBuilder(); final String baseFilePath = baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1); sb.append(baseFilePath).append(":"); final byte[] baseRootHash = getRootHash(baseFilename); if (baseRootHash == null) { sb.append("0"); } else { sb.append(HexDump.toHexString(baseRootHash)); } if (splitFilenameArray == null || splitFilenameArray.length == 0) { return sb.toString(); } for (int i = splitFilenameArray.length - 1; i >= 0; i--) { final String splitFilename = splitFilenameArray[i]; final String splitFilePath = splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1); final byte[] splitRootHash = getRootHash(splitFilename); sb.append(";").append(splitFilePath).append(":"); if (splitRootHash == null) { sb.append("0"); } else { sb.append(HexDump.toHexString(splitRootHash)); } } return sb.toString(); } /** * Returns the root has for the given file. * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated. * <p>NOTE: This currently only works on files stored on the incremental file system. The * eventual goal is that this hash [among others] can be retrieved for any file. */ @Nullable private static byte[] getRootHash(String filename) { try { final byte[] baseFileSignature = IncrementalManager.unsafeGetFileSignature(filename); if (baseFileSignature == null) { throw new IOException("File signature not present"); } final V4Signature signature = V4Signature.readFrom(baseFileSignature); if (signature.hashingInfo == null) { throw new IOException("Hashing info not present"); } final HashingInfo hashInfo = HashingInfo.fromByteArray(signature.hashingInfo); if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) { throw new IOException("Root has not present"); } return hashInfo.rawRootHash; } catch (IOException ignore) { Slog.e(TAG, "ERROR: could not load root hash from incremental install"); } return null; } }