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