1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.security; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SuppressLint; 24 import android.annotation.SystemApi; 25 import android.annotation.SystemService; 26 import android.content.Context; 27 import android.os.IInstalld.IFsveritySetupAuthToken; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.system.ErrnoException; 31 32 import com.android.internal.security.VerityUtils; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.security.cert.CertificateEncodingException; 37 import java.security.cert.X509Certificate; 38 39 /** 40 * This class provides access to file integrity related operations. 41 */ 42 @SystemService(Context.FILE_INTEGRITY_SERVICE) 43 public final class FileIntegrityManager { 44 @NonNull private final IFileIntegrityService mService; 45 @NonNull private final Context mContext; 46 47 /** @hide */ FileIntegrityManager(@onNull Context context, @NonNull IFileIntegrityService service)48 public FileIntegrityManager(@NonNull Context context, @NonNull IFileIntegrityService service) { 49 mContext = context; 50 mService = service; 51 } 52 53 /** 54 * Returns whether fs-verity is supported on the device. fs-verity provides on-access 55 * verification, although the app APIs are only made available to apps in a later SDK version. 56 * Only when this method returns true, the other fs-verity APIs in the same class can succeed. 57 * 58 * <p>The app may not need this method and just call the other APIs normally and handle any 59 * failure. If some app feature really depends on fs-verity (e.g. protecting integrity of a 60 * large file download), an early check of support status may avoid any cost if it is to fail 61 * late. 62 * 63 * <p>Note: for historical reasons this is named {@code isApkVeritySupported()} instead of 64 * {@code isFsVeritySupported()}. It has also been available since API level 30, predating the 65 * other fs-verity APIs. 66 */ isApkVeritySupported()67 public boolean isApkVeritySupported() { 68 return VerityUtils.isFsVeritySupported(); 69 } 70 71 /** 72 * Enables fs-verity to the owned file under the calling app's private directory. It always uses 73 * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt. 74 * 75 * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be 76 * writable by the app and not already have fs-verity enabled, and the file must not currently 77 * be open for writing by any process. To check whether the device supports fs-verity, use 78 * {@link #isApkVeritySupported()}. 79 * 80 * <p>It takes O(file size) time to build the underlying data structure for continuous 81 * verification. The operation is atomic, i.e. it's either enabled or not, even in case of 82 * power failure during or after the call. 83 * 84 * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to 85 * perform a signature check by itself before using the file. The signature is often delivered 86 * as a separate file and stored next to the targeting file in the filesystem. The public key of 87 * the signer (normally the same app developer) can be put in the APK, and the app can use the 88 * public key to verify the signature to the file's actual fs-verity digest (from {@link 89 * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the 90 * framework. App developers may choose to use common practices like JCA for the signing and 91 * verification, or their own preferred approach. 92 * 93 * @param file The file to enable fs-verity. It must represent an absolute path. 94 * @throws IllegalArgumentException If the provided file is not an absolute path. 95 * @throws IOException If the operation failed. 96 * 97 * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> 98 * @hide 99 */ 100 @FlaggedApi(Flags.FLAG_FSVERITY_API) 101 @SuppressLint("StreamFiles") 102 @SystemApi setupFsVerity(@onNull File file)103 public void setupFsVerity(@NonNull File file) throws IOException { 104 if (!file.isAbsolute()) { 105 // fs-verity is to be enabled by installd, which enforces the validation to the 106 // (untrusted) file path passed from here. To make this less error prone, installd 107 // accepts only absolute path. When a relative path is provided, we fail with an 108 // explicit exception to help developers understand the requirement to use an absolute 109 // path. 110 throw new IllegalArgumentException("Expect an absolute path"); 111 } 112 IFsveritySetupAuthToken authToken; 113 // fs-verity setup requires no writable fd to the file. Make sure it's closed before 114 // continue. 115 try (var authFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)) { 116 authToken = mService.createAuthToken(authFd); 117 } catch (RemoteException e) { 118 throw e.rethrowFromSystemServer(); 119 } 120 121 try { 122 int errno = mService.setupFsverity(authToken, file.getPath(), 123 mContext.getPackageName()); 124 if (errno != 0) { 125 new ErrnoException("setupFsVerity", errno).rethrowAsIOException(); 126 } 127 } catch (RemoteException e) { 128 throw e.rethrowFromSystemServer(); 129 } 130 } 131 132 /** 133 * Returns the fs-verity digest for the owned file under the calling app's private directory, or 134 * null when the file does not have fs-verity enabled (including when fs-verity is not supported 135 * on older devices). 136 * 137 * @param file The file to measure the fs-verity digest. 138 * @return The fs-verity digest in byte[], null if none. 139 * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> 140 * @hide 141 */ 142 @FlaggedApi(Flags.FLAG_FSVERITY_API) 143 @SuppressLint("StreamFiles") 144 @SystemApi getFsVerityDigest(@onNull File file)145 public @Nullable byte[] getFsVerityDigest(@NonNull File file) throws IOException { 146 return VerityUtils.getFsverityDigest(file.getPath()); 147 } 148 149 /** 150 * Returns whether the given certificate can be used to prove app's install source. Always 151 * return false if the feature is not supported. 152 * 153 * <p>A store can use this API to decide if a signature file needs to be downloaded. Also, if a 154 * store has shipped different certificates before (e.g. with stronger and weaker key), it can 155 * also use this API to download the best signature on the running device. 156 * 157 * @return whether the certificate is trusted in the system 158 * @deprecated The feature is no longer supported, and this API now always returns false. 159 */ 160 @RequiresPermission(anyOf = { 161 android.Manifest.permission.INSTALL_PACKAGES, 162 android.Manifest.permission.REQUEST_INSTALL_PACKAGES 163 }) 164 @Deprecated isAppSourceCertificateTrusted(@onNull X509Certificate certificate)165 public boolean isAppSourceCertificateTrusted(@NonNull X509Certificate certificate) 166 throws CertificateEncodingException { 167 return false; 168 } 169 } 170