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 this class with the prefix {@code RESULT_}. 83 * The result is {@link #RESULT_SUCCESS} when at least one verifier has passed its checks. The 84 * token may be used in calls to other parts of the system. 85 * 86 * <p>It's expected that a verifier will be able to decode and understand the passed values, 87 * otherwise fail to verify. {@code attestation} should contain some type data to prevent parse 88 * errors. 89 * 90 * <p>The values put into the {@code requirements} Bundle depend on the {@code 91 * localBindingType} used. 92 * 93 * @param profile the attestation profile which defines the security requirements which 94 * must be met by the environment described by {@code attestation} 95 * @param localBindingType the type of the local binding data; see constants in this class with 96 * the prefix {@code TYPE_} 97 * @param requirements a {@link Bundle} containing locally-known data which must match 98 * {@code attestation} 99 * @param attestation attestation data which describes a remote computing environment 100 * @param executor {@code callback} will be executed on this executor 101 * @param callback will be called with the results of the verification 102 * @see AttestationVerificationService 103 */ 104 @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<@VerificationResult Integer, VerificationToken> callback)105 public void verifyAttestation( 106 @NonNull AttestationProfile profile, 107 @LocalBindingType int localBindingType, 108 @NonNull Bundle requirements, 109 @NonNull byte[] attestation, 110 @NonNull @CallbackExecutor Executor executor, 111 @NonNull BiConsumer<@VerificationResult Integer, VerificationToken> callback) { 112 try { 113 AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>(); 114 resultCallback.thenAccept(result -> { 115 Log.d(TAG, "verifyAttestation result: " + result.resultCode + " / " + result.token); 116 executor.execute(() -> { 117 callback.accept(result.resultCode, result.token); 118 }); 119 }); 120 121 mService.verifyAttestation(profile, localBindingType, requirements, attestation, 122 resultCallback); 123 124 } catch (RemoteException e) { 125 throw e.rethrowFromSystemServer(); 126 } 127 } 128 129 /** 130 * Verifies that {@code token} is a valid token, returning the result contained in valid 131 * tokens. 132 * 133 * <p>This verifies that the token was issued by the platform and thus the system verified 134 * attestation data against the specified {@code profile}, {@code localBindingType}, and {@code 135 * requirements}. The value returned by this method is the same as the one originally returned 136 * when the token was generated. Callers of this method should not trust the provider of the 137 * token to also specify the profile, local binding type, or requirements, but instead have 138 * their own security requirements about these arguments. 139 * 140 * <p>This method, in contrast to {@code verifyAttestation}, executes synchronously and only 141 * checks that a previous verification succeeded. This allows callers to pass the token to 142 * others, including system APIs, without those components needing to re-verify the attestation 143 * data, an operation which can take several seconds. 144 * 145 * <p>When {@code maximumAge} is not specified (null), this method verifies the token was 146 * generated in the past hour. Otherwise, it verifies the token was generated between now and 147 * {@code maximumAge} ago. The maximum value of {@code maximumAge} is one hour; specifying a 148 * duration greater than one hour will result in an {@link IllegalArgumentException}. 149 * 150 * @param profile the attestation profile which must be in the token 151 * @param localBindingType the local binding type which must be in the token 152 * @param requirements the requirements which must be in the token 153 * @param token the token to be verified 154 * @param maximumAge the maximum age to accept for the token 155 */ 156 @RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE) 157 @CheckResult 158 @VerificationResult verifyToken( @onNull AttestationProfile profile, @LocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull VerificationToken token, @Nullable Duration maximumAge)159 public int verifyToken( 160 @NonNull AttestationProfile profile, 161 @LocalBindingType int localBindingType, 162 @NonNull Bundle requirements, 163 @NonNull VerificationToken token, 164 @Nullable Duration maximumAge) { 165 Duration usedMaximumAge; 166 if (maximumAge == null) { 167 usedMaximumAge = MAX_TOKEN_AGE; 168 } else { 169 if (maximumAge.compareTo(MAX_TOKEN_AGE) > 0) { 170 throw new IllegalArgumentException( 171 "maximumAge cannot be greater than " + MAX_TOKEN_AGE + "; was " 172 + maximumAge); 173 } 174 usedMaximumAge = maximumAge; 175 } 176 177 try { 178 AndroidFuture<Integer> resultCallback = new AndroidFuture<>(); 179 resultCallback.orTimeout(5, TimeUnit.SECONDS); 180 181 mService.verifyToken(token, new ParcelDuration(usedMaximumAge), resultCallback); 182 return resultCallback.get(); // block on result callback 183 } catch (RemoteException e) { 184 throw e.rethrowFromSystemServer(); 185 } catch (Throwable t) { 186 throw new RuntimeException("Error verifying token.", t); 187 } 188 } 189 190 /** @hide */ AttestationVerificationManager( @onNull Context context, @NonNull IAttestationVerificationManagerService service)191 public AttestationVerificationManager( 192 @NonNull Context context, 193 @NonNull IAttestationVerificationManagerService service) { 194 this.mContext = context; 195 this.mService = service; 196 } 197 198 /** @hide */ 199 @IntDef( 200 prefix = {"PROFILE_"}, 201 value = { 202 PROFILE_UNKNOWN, 203 PROFILE_APP_DEFINED, 204 PROFILE_SELF_TRUSTED, 205 PROFILE_PEER_DEVICE, 206 }) 207 @Retention(RetentionPolicy.SOURCE) 208 public @interface AttestationProfileId { 209 } 210 211 /** 212 * The profile is unknown because it is a profile unknown to this version of the SDK. 213 */ 214 public static final int PROFILE_UNKNOWN = 0; 215 216 /** The profile is defined by an app. */ 217 public static final int PROFILE_APP_DEFINED = 1; 218 219 /** 220 * A system-defined profile which verifies that the attesting environment can create an 221 * attestation with the same root certificate as the verifying device with a matching 222 * attestation challenge. 223 * 224 * This profile is intended to be used only for testing. 225 */ 226 public static final int PROFILE_SELF_TRUSTED = 2; 227 228 /** 229 * A system-defined profile which verifies that the attesting environment is similar to the 230 * current device in terms of security model and security configuration. This category is fairly 231 * broad and most securely configured Android devices should qualify, along with a variety of 232 * non-Android devices. 233 */ 234 public static final int PROFILE_PEER_DEVICE = 3; 235 236 /** @hide */ 237 @IntDef( 238 prefix = {"TYPE_"}, 239 value = { 240 TYPE_UNKNOWN, 241 TYPE_APP_DEFINED, 242 TYPE_PUBLIC_KEY, 243 TYPE_CHALLENGE, 244 }) 245 @Retention(RetentionPolicy.SOURCE) 246 public @interface LocalBindingType { 247 } 248 249 /** 250 * The type of the local binding data is unknown because it is a type unknown to this version of 251 * the SDK. 252 */ 253 public static final int TYPE_UNKNOWN = 0; 254 255 /** 256 * A local binding type for app-defined profiles which use local binding data which does not 257 * match any of the existing system-defined types. 258 */ 259 public static final int TYPE_APP_DEFINED = 1; 260 261 /** 262 * A local binding type where the attestation is bound to a public key negotiated and 263 * authenticated to a public key. 264 * 265 * <p>When using this type, the {@code requirements} bundle contains values for: 266 * <ul> 267 * <li>{@link #PARAM_PUBLIC_KEY} 268 * <li>{@link #PARAM_ID}: identifying the remote environment, optional 269 * </ul> 270 */ 271 public static final int TYPE_PUBLIC_KEY = 2; 272 273 /** 274 * A local binding type where the attestation is bound to a challenge. 275 * 276 * <p>When using this type, the {@code requirements} bundle contains values for: 277 * <ul> 278 * <li>{@link #PARAM_CHALLENGE}: containing the challenge 279 * </ul> 280 */ 281 public static final int TYPE_CHALLENGE = 3; 282 283 /** @hide */ 284 @IntDef( 285 prefix = {"RESULT_"}, 286 value = { 287 RESULT_UNKNOWN, 288 RESULT_SUCCESS, 289 RESULT_FAILURE, 290 }) 291 @Retention(RetentionPolicy.SOURCE) 292 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) 293 public @interface VerificationResult { 294 } 295 296 /** The result of the verification is unknown because it has a value unknown to this SDK. */ 297 public static final int RESULT_UNKNOWN = 0; 298 299 /** The result of the verification was successful. */ 300 public static final int RESULT_SUCCESS = 1; 301 302 /** 303 * The result of the attestation verification was failure. The attestation could not be 304 * verified. 305 */ 306 public static final int RESULT_FAILURE = 2; 307 308 /** 309 * Requirements bundle parameter key for a public key, a byte array. 310 * 311 * <p>This should contain the encoded key bytes according to the ASN.1 type 312 * {@code SubjectPublicKeyInfo} defined in the X.509 standard, the same as a call to {@link 313 * java.security.spec.X509EncodedKeySpec#getEncoded()} would produce. 314 * 315 * @see Bundle#putByteArray(String, byte[]) 316 */ 317 public static final String PARAM_PUBLIC_KEY = "localbinding.public_key"; 318 319 /** Requirements bundle parameter key for an ID, String. */ 320 public static final String PARAM_ID = "localbinding.id"; 321 322 /** Requirements bundle parameter for a challenge. */ 323 public static final String PARAM_CHALLENGE = "localbinding.challenge"; 324 325 /** @hide */ localBindingTypeToString(@ocalBindingType int localBindingType)326 public static String localBindingTypeToString(@LocalBindingType int localBindingType) { 327 final String text; 328 switch (localBindingType) { 329 case TYPE_UNKNOWN: 330 text = "UNKNOWN"; 331 break; 332 333 case TYPE_APP_DEFINED: 334 text = "APP_DEFINED"; 335 break; 336 337 case TYPE_PUBLIC_KEY: 338 text = "PUBLIC_KEY"; 339 break; 340 341 case TYPE_CHALLENGE: 342 text = "CHALLENGE"; 343 break; 344 345 default: 346 return Integer.toString(localBindingType); 347 } 348 return text + "(" + localBindingType + ")"; 349 } 350 351 /** @hide */ verificationResultCodeToString(@erificationResult int resultCode)352 public static String verificationResultCodeToString(@VerificationResult int resultCode) { 353 final String text; 354 switch (resultCode) { 355 case RESULT_UNKNOWN: 356 text = "UNKNOWN"; 357 break; 358 359 case RESULT_SUCCESS: 360 text = "SUCCESS"; 361 break; 362 363 case RESULT_FAILURE: 364 text = "FAILURE"; 365 break; 366 367 default: 368 return Integer.toString(resultCode); 369 } 370 return text + "(" + resultCode + ")"; 371 } 372 } 373