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 try { 69 // Go through the service just to avoid exposing the vendor controlled system property 70 // to all apps. 71 return mService.isApkVeritySupported(); 72 } catch (RemoteException e) { 73 throw e.rethrowFromSystemServer(); 74 } 75 } 76 77 /** 78 * Enables fs-verity to the owned file under the calling app's private directory. It always uses 79 * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt. 80 * 81 * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be 82 * writable by the app and not already have fs-verity enabled, and the file must not currently 83 * be open for writing by any process. To check whether the device supports fs-verity, use 84 * {@link #isApkVeritySupported()}. 85 * 86 * <p>It takes O(file size) time to build the underlying data structure for continuous 87 * verification. The operation is atomic, i.e. it's either enabled or not, even in case of 88 * power failure during or after the call. 89 * 90 * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to 91 * perform a signature check by itself before using the file. The signature is often delivered 92 * as a separate file and stored next to the targeting file in the filesystem. The public key of 93 * the signer (normally the same app developer) can be put in the APK, and the app can use the 94 * public key to verify the signature to the file's actual fs-verity digest (from {@link 95 * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the 96 * framework. App developers may choose to use common practices like JCA for the signing and 97 * verification, or their own preferred approach. 98 * 99 * @param file The file to enable fs-verity. It must represent an absolute path. 100 * @throws IllegalArgumentException If the provided file is not an absolute path. 101 * @throws IOException If the operation failed. 102 * 103 * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> 104 * @hide 105 */ 106 @FlaggedApi(Flags.FLAG_FSVERITY_API) 107 @SuppressLint("StreamFiles") 108 @SystemApi setupFsVerity(@onNull File file)109 public void setupFsVerity(@NonNull File file) throws IOException { 110 if (!file.isAbsolute()) { 111 // fs-verity is to be enabled by installd, which enforces the validation to the 112 // (untrusted) file path passed from here. To make this less error prone, installd 113 // accepts only absolute path. When a relative path is provided, we fail with an 114 // explicit exception to help developers understand the requirement to use an absolute 115 // path. 116 throw new IllegalArgumentException("Expect an absolute path"); 117 } 118 IFsveritySetupAuthToken authToken; 119 // fs-verity setup requires no writable fd to the file. Make sure it's closed before 120 // continue. 121 try (var authFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)) { 122 authToken = mService.createAuthToken(authFd); 123 } catch (RemoteException e) { 124 throw e.rethrowFromSystemServer(); 125 } 126 127 try { 128 int errno = mService.setupFsverity(authToken, file.getPath(), 129 mContext.getPackageName()); 130 if (errno != 0) { 131 new ErrnoException("setupFsVerity", errno).rethrowAsIOException(); 132 } 133 } catch (RemoteException e) { 134 throw e.rethrowFromSystemServer(); 135 } 136 } 137 138 /** 139 * Returns the fs-verity digest for the owned file under the calling app's private directory, or 140 * null when the file does not have fs-verity enabled (including when fs-verity is not supported 141 * on older devices). 142 * 143 * @param file The file to measure the fs-verity digest. 144 * @return The fs-verity digest in byte[], null if none. 145 * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> 146 * @hide 147 */ 148 @FlaggedApi(Flags.FLAG_FSVERITY_API) 149 @SuppressLint("StreamFiles") 150 @SystemApi getFsVerityDigest(@onNull File file)151 public @Nullable byte[] getFsVerityDigest(@NonNull File file) throws IOException { 152 return VerityUtils.getFsverityDigest(file.getPath()); 153 } 154 155 /** 156 * Returns whether the given certificate can be used to prove app's install source. Always 157 * return false if the feature is not supported. 158 * 159 * <p>A store can use this API to decide if a signature file needs to be downloaded. Also, if a 160 * store has shipped different certificates before (e.g. with stronger and weaker key), it can 161 * also use this API to download the best signature on the running device. 162 * 163 * @return whether the certificate is trusted in the system 164 * @deprecated The feature is no longer supported, and this API now always returns false. 165 */ 166 @RequiresPermission(anyOf = { 167 android.Manifest.permission.INSTALL_PACKAGES, 168 android.Manifest.permission.REQUEST_INSTALL_PACKAGES 169 }) 170 @Deprecated isAppSourceCertificateTrusted(@onNull X509Certificate certificate)171 public boolean isAppSourceCertificateTrusted(@NonNull X509Certificate certificate) 172 throws CertificateEncodingException { 173 try { 174 return mService.isAppSourceCertificateTrusted( 175 certificate.getEncoded(), mContext.getOpPackageName()); 176 } catch (RemoteException e) { 177 throw e.rethrowFromSystemServer(); 178 } 179 } 180 } 181