• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.attestationverification;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.CheckResult;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemService;
27 import android.content.Context;
28 import android.os.Bundle;
29 import android.os.ParcelDuration;
30 import android.os.RemoteException;
31 import android.util.Log;
32 
33 import com.android.internal.infra.AndroidFuture;
34 
35 import java.lang.annotation.ElementType;
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.lang.annotation.Target;
39 import java.time.Duration;
40 import java.util.concurrent.Executor;
41 import java.util.concurrent.TimeUnit;
42 import java.util.function.BiConsumer;
43 
44 /**
45  * Provides methods for verifying that attestations from remote compute environments meet minimum
46  * security requirements specified by attestation profiles.
47  *
48  * @hide
49  */
50 @SystemService(Context.ATTESTATION_VERIFICATION_SERVICE)
51 public class AttestationVerificationManager {
52 
53     private static final String TAG = "AVF";
54     private static final Duration MAX_TOKEN_AGE = Duration.ofHours(1);
55 
56     private final Context mContext;
57     private final IAttestationVerificationManagerService mService;
58 
59     /**
60      * Verifies that {@code attestation} describes a computing environment that meets the
61      * requirements of {@code profile}, {@code localBindingType}, and {@code requirements}.
62      *
63      * <p>This method verifies that at least one system-registered {@linkplain
64      * AttestationVerificationService attestation verifier} associated with {@code profile} and
65      * {@code localBindingType} has verified that {@code attestation} attests that the remote
66      * environment matching the local binding data (determined by {@code localBindingType}) in
67      * {@code requirements} meets the requirements of the profile.
68      *
69      * <p>For successful verification, the {@code requirements} bundle must contain locally-known
70      * data which must match {@code attestation}. The required data in the bundle is defined by the
71      * {@code localBindingType} (see documentation for the type). Verifiers will fail to verify the
72      * attestation if the bundle contains unsupported data.
73      *
74      * <p>The {@code localBindingType} specifies how {@code attestation} is bound to a local
75      * secure channel endpoint or similar connection with the target remote environment described by
76      * the attestation. The binding is expected to be related to a cryptographic protocol, and each
77      * binding type requires specific arguments to be present in the {@code requirements} bundle. It
78      * is this binding to something known locally that ensures an attestation is not only valid, but
79      * is also associated with a particular connection.
80      *
81      * <p>The {@code callback} is called with a result and {@link VerificationToken} (which may be
82      * null). The result is an integer (see constants in {@link VerificationResultFlags}).
83      *
84      * <p>It's expected that a verifier will be able to decode and understand the passed values,
85      * otherwise fail to verify. {@code attestation} should contain some type data to prevent parse
86      * errors.
87      *
88      * <p>The values put into the {@code requirements} Bundle depend on the {@code
89      * localBindingType} used.
90      *
91      * @param profile          the attestation profile which defines the security requirements which
92      *                         must be met by the environment described by {@code attestation}
93      * @param localBindingType the type of the local binding data; see constants in this class with
94      *                         the prefix {@code TYPE_}
95      * @param requirements     a {@link Bundle} containing locally-known data which must match
96      *                         {@code attestation}
97      * @param attestation      attestation data which describes a remote computing environment
98      * @param executor         {@code callback} will be executed on this executor
99      * @param callback         will be called with the results of the verification
100      * @see AttestationVerificationService
101      */
102     @RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE)
verifyAttestation( @onNull AttestationProfile profile, @LocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation, @NonNull @CallbackExecutor Executor executor, @NonNull BiConsumer<@VerificationResultFlags Integer, VerificationToken> callback)103     public void verifyAttestation(
104             @NonNull AttestationProfile profile,
105             @LocalBindingType int localBindingType,
106             @NonNull Bundle requirements,
107             @NonNull byte[] attestation,
108             @NonNull @CallbackExecutor Executor executor,
109             @NonNull BiConsumer<@VerificationResultFlags Integer, VerificationToken> callback) {
110         try {
111             AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>();
112             resultCallback.thenAccept(result -> {
113                 Log.d(TAG, "verifyAttestation result: " + result.resultCode + " / " + result.token);
114                 executor.execute(() -> {
115                     callback.accept(result.resultCode, result.token);
116                 });
117             });
118 
119             mService.verifyAttestation(profile, localBindingType, requirements, attestation,
120                     resultCallback);
121 
122         } catch (RemoteException e) {
123             throw e.rethrowFromSystemServer();
124         }
125     }
126 
127     /**
128      * Verifies that {@code token} is a valid token, returning the result contained in valid
129      * tokens.
130      *
131      * <p>This verifies that the token was issued by the platform and thus the system verified
132      * attestation data against the specified {@code profile}, {@code localBindingType}, and {@code
133      * requirements}. The value returned by this method is the same as the one originally returned
134      * when the token was generated. Callers of this method should not trust the provider of the
135      * token to also specify the profile, local binding type, or requirements, but instead have
136      * their own security requirements about these arguments.
137      *
138      * <p>This method, in contrast to {@code verifyAttestation}, executes synchronously and only
139      * checks that a previous verification succeeded. This allows callers to pass the token to
140      * others, including system APIs, without those components needing to re-verify the attestation
141      * data, an operation which can take several seconds.
142      *
143      * <p>When {@code maximumAge} is not specified (null), this method verifies the token was
144      * generated in the past hour. Otherwise, it verifies the token was generated between now and
145      * {@code maximumAge} ago. The maximum value of {@code maximumAge} is one hour; specifying a
146      * duration greater than one hour will result in an {@link IllegalArgumentException}.
147      *
148      * @param profile          the attestation profile which must be in the token
149      * @param localBindingType the local binding type which must be in the token
150      * @param requirements     the requirements which must be in the token
151      * @param token            the token to be verified
152      * @param maximumAge       the maximum age to accept for the token
153      */
154     @RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE)
155     @CheckResult
156     @VerificationResultFlags
verifyToken( @onNull AttestationProfile profile, @LocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull VerificationToken token, @Nullable Duration maximumAge)157     public int verifyToken(
158             @NonNull AttestationProfile profile,
159             @LocalBindingType int localBindingType,
160             @NonNull Bundle requirements,
161             @NonNull VerificationToken token,
162             @Nullable Duration maximumAge) {
163         Duration usedMaximumAge;
164         if (maximumAge == null) {
165             usedMaximumAge = MAX_TOKEN_AGE;
166         } else {
167             if (maximumAge.compareTo(MAX_TOKEN_AGE) > 0) {
168                 throw new IllegalArgumentException(
169                         "maximumAge cannot be greater than " + MAX_TOKEN_AGE + "; was "
170                                 + maximumAge);
171             }
172             usedMaximumAge = maximumAge;
173         }
174 
175         try {
176             AndroidFuture<Integer> resultCallback = new AndroidFuture<>();
177             resultCallback.orTimeout(5, TimeUnit.SECONDS);
178 
179             mService.verifyToken(token, new ParcelDuration(usedMaximumAge), resultCallback);
180             return resultCallback.get(); // block on result callback
181         } catch (RemoteException e) {
182             throw e.rethrowFromSystemServer();
183         } catch (Throwable t) {
184             throw new RuntimeException("Error verifying token.", t);
185         }
186     }
187 
188     /** @hide */
AttestationVerificationManager( @onNull Context context, @NonNull IAttestationVerificationManagerService service)189     public AttestationVerificationManager(
190             @NonNull Context context,
191             @NonNull IAttestationVerificationManagerService service) {
192         this.mContext = context;
193         this.mService = service;
194     }
195 
196     /** @hide */
197     @IntDef(
198             prefix = {"PROFILE_"},
199             value = {
200                     PROFILE_UNKNOWN,
201                     PROFILE_APP_DEFINED,
202                     PROFILE_SELF_TRUSTED,
203                     PROFILE_PEER_DEVICE,
204             })
205     @Retention(RetentionPolicy.SOURCE)
206     public @interface AttestationProfileId {
207     }
208 
209     /**
210      * The profile is unknown because it is a profile unknown to this version of the SDK.
211      */
212     public static final int PROFILE_UNKNOWN = 0;
213 
214     /** The profile is defined by an app. */
215     public static final int PROFILE_APP_DEFINED = 1;
216 
217     /**
218      * A system-defined profile which verifies that the attesting environment can create an
219      * attestation with the same root certificate as the verifying device with a matching
220      * attestation challenge.
221      *
222      * This profile is intended to be used only for testing.
223      */
224     public static final int PROFILE_SELF_TRUSTED = 2;
225 
226     /**
227      * A system-defined profile which verifies that the attesting environment is similar to the
228      * current device in terms of security model and security configuration. This category is fairly
229      * broad and most securely configured Android devices should qualify, along with a variety of
230      * non-Android devices.
231      */
232     public static final int PROFILE_PEER_DEVICE = 3;
233 
234     /** @hide */
235     @IntDef(
236             prefix = {"TYPE_"},
237             value = {
238                     TYPE_UNKNOWN,
239                     TYPE_APP_DEFINED,
240                     TYPE_PUBLIC_KEY,
241                     TYPE_CHALLENGE,
242             })
243     @Retention(RetentionPolicy.SOURCE)
244     public @interface LocalBindingType {
245     }
246 
247     /**
248      * The type of the local binding data is unknown because it is a type unknown to this version of
249      * the SDK.
250      */
251     public static final int TYPE_UNKNOWN = 0;
252 
253     /**
254      * A local binding type for app-defined profiles which use local binding data which does not
255      * match any of the existing system-defined types.
256      */
257     public static final int TYPE_APP_DEFINED = 1;
258 
259     /**
260      * A local binding type where the attestation is bound to a public key negotiated and
261      * authenticated to a public key.
262      *
263      * <p>When using this type, the {@code requirements} bundle contains values for:
264      * <ul>
265      *   <li>{@link #PARAM_PUBLIC_KEY}
266      *   <li>{@link #PARAM_ID}: identifying the remote environment, optional
267      * </ul>
268      */
269     public static final int TYPE_PUBLIC_KEY = 2;
270 
271     /**
272      * A local binding type where the attestation is bound to a challenge.
273      *
274      * <p>When using this type, the {@code requirements} bundle contains values for:
275      * <ul>
276      *   <li>{@link #PARAM_CHALLENGE}: containing the challenge
277      * </ul>
278      */
279     public static final int TYPE_CHALLENGE = 3;
280 
281     /**
282      * Verification result returned from {@link #verifyAttestation}.
283      *
284      * A value of {@code 0} indicates success. Otherwise, a bit flag is set from first failing stage
285      * below:
286      * <ol>
287      * <li> The received attestation's integrity (e.g. the certificate signatures) is validated.
288      * If this fails, {@link #FLAG_FAILURE_CERTS} will be returned with all other bits unset.
289      * <li> The local binding requirements are checked. If this fails,
290      * {@link #FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS} is returned with all other bits unset.
291      * <li> The profile requirements are checked. If this fails, a bit flag will be returned with
292      * some of the these bits set to indicate the type of profile requirement failure:
293      * {@link #FLAG_FAILURE_UNSUPPORTED_PROFILE}, {@link #FLAG_FAILURE_KEYSTORE_REQUIREMENTS},
294      * {@link #FLAG_FAILURE_BOOT_STATE}, and {@link #FLAG_FAILURE_PATCH_LEVEL_DIFF}.
295      * </ol>
296      *
297      * Note: The reason of the failure must be not be provided to the remote device.
298      *
299      * @hide
300      */
301     @IntDef(flag = true, prefix = {"FLAG_FAILURE_"},
302     value = {
303             FLAG_FAILURE_UNKNOWN,
304             FLAG_FAILURE_UNSUPPORTED_PROFILE,
305             FLAG_FAILURE_CERTS,
306             FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS,
307             FLAG_FAILURE_KEYSTORE_REQUIREMENTS,
308             FLAG_FAILURE_BOOT_STATE,
309             FLAG_FAILURE_PATCH_LEVEL_DIFF,
310     })
311     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
312     @Retention(RetentionPolicy.SOURCE)
313     public @interface VerificationResultFlags{}
314 
315     /** Flag: If there are unknown failures e.g. runtime exception. 0 = no, 1 = yes. */
316     public static final int FLAG_FAILURE_UNKNOWN = 1;
317 
318     /** Flag: If the AVF profile is supported. 0 = supported, 1 = not supported */
319     public static final int FLAG_FAILURE_UNSUPPORTED_PROFILE = 1 << 1;
320 
321     /**
322      * Flag: Result bit for certs verification e.g. loading, generating, parsing certs.
323      * 0 = success, 1 = failure
324      */
325     public static final int FLAG_FAILURE_CERTS = 1 << 2;
326 
327     /** Flag: Result bit for local binding requirements verification. 0 = success, 1 = failure. */
328     public static final int FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS = 1 << 3;
329 
330     /**
331      * Flag: Result bit for KeyStore requirements verification.
332      * 0 = success, 1 = failure.
333      */
334     public static final int FLAG_FAILURE_KEYSTORE_REQUIREMENTS = 1 << 4;
335 
336     /** Flag: Result bit for boot state verification. 0 = success, 1 = failure */
337     public static final int FLAG_FAILURE_BOOT_STATE = 1 << 5;
338 
339     /** Flag: Result bit for patch level diff checks. 0 = success, 1 = failure. */
340     public static final int FLAG_FAILURE_PATCH_LEVEL_DIFF = 1 << 6;
341 
342     /**
343      * Requirements bundle parameter key for a public key, a byte array.
344      *
345      * <p>This should contain the encoded key bytes according to the ASN.1 type
346      * {@code SubjectPublicKeyInfo} defined in the X.509 standard, the same as a call to {@link
347      * java.security.spec.X509EncodedKeySpec#getEncoded()} would produce.
348      *
349      * @see Bundle#putByteArray(String, byte[])
350      */
351     public static final String PARAM_PUBLIC_KEY = "localbinding.public_key";
352 
353     /** Requirements bundle parameter key for an ID, String. */
354     public static final String PARAM_ID = "localbinding.id";
355 
356     /** Requirements bundle parameter for a challenge. */
357     public static final String PARAM_CHALLENGE = "localbinding.challenge";
358 
359     /** Requirements bundle parameter for max patch level diff (int) for a peer device. **/
360     public static final String PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS =
361             "param_max_patch_level_diff_months";
362 
363     /** @hide */
localBindingTypeToString(@ocalBindingType int localBindingType)364     public static String localBindingTypeToString(@LocalBindingType int localBindingType) {
365         final String text;
366         switch (localBindingType) {
367             case TYPE_UNKNOWN:
368                 text = "UNKNOWN";
369                 break;
370 
371             case TYPE_APP_DEFINED:
372                 text = "APP_DEFINED";
373                 break;
374 
375             case TYPE_PUBLIC_KEY:
376                 text = "PUBLIC_KEY";
377                 break;
378 
379             case TYPE_CHALLENGE:
380                 text = "CHALLENGE";
381                 break;
382 
383             default:
384                 return Integer.toString(localBindingType);
385         }
386         return text + "(" + localBindingType + ")";
387     }
388 }
389