• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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