1 /* 2 * Copyright (C) 2022 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 com.android.server.companion.securechannel; 18 19 import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE; 20 import static android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE; 21 import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; 22 23 import android.annotation.NonNull; 24 import android.content.Context; 25 import android.os.Bundle; 26 import android.security.attestationverification.AttestationProfile; 27 import android.security.attestationverification.AttestationVerificationManager; 28 import android.security.attestationverification.VerificationToken; 29 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.TimeUnit; 32 import java.util.concurrent.atomic.AtomicInteger; 33 import java.util.function.BiConsumer; 34 35 /** 36 * Helper class to perform attestation verification synchronously. 37 */ 38 public class AttestationVerifier { 39 private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds 40 private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system"; 41 42 private final Context mContext; 43 AttestationVerifier(Context context)44 AttestationVerifier(Context context) { 45 this.mContext = context; 46 } 47 48 /** 49 * Synchronously verify remote attestation as a suitable peer device on current thread. 50 * 51 * The peer device must be owned by the Android system and be protected with appropriate 52 * public key that this device can verify as attestation challenge. 53 * 54 * @param remoteAttestation the full certificate chain containing attestation extension. 55 * @param attestationChallenge attestation challenge for authentication. 56 * @return true if attestation is successfully verified; false otherwise. 57 */ 58 @NonNull verifyAttestation( @onNull byte[] remoteAttestation, @NonNull byte[] attestationChallenge )59 public int verifyAttestation( 60 @NonNull byte[] remoteAttestation, 61 @NonNull byte[] attestationChallenge 62 ) throws SecureChannelException { 63 Bundle requirements = new Bundle(); 64 requirements.putByteArray(PARAM_CHALLENGE, attestationChallenge); 65 requirements.putBoolean(PARAM_OWNED_BY_SYSTEM, true); // Custom parameter for CDM 66 67 // Synchronously execute attestation verification. 68 AtomicInteger verificationResult = new AtomicInteger(0); 69 CountDownLatch verificationFinished = new CountDownLatch(1); 70 BiConsumer<Integer, VerificationToken> onVerificationResult = (result, token) -> { 71 verificationResult.set(result); 72 verificationFinished.countDown(); 73 }; 74 75 mContext.getSystemService(AttestationVerificationManager.class).verifyAttestation( 76 new AttestationProfile(PROFILE_PEER_DEVICE), 77 /* localBindingType */ TYPE_CHALLENGE, 78 requirements, 79 remoteAttestation, 80 Runnable::run, 81 onVerificationResult 82 ); 83 84 boolean finished; 85 try { 86 finished = verificationFinished.await( 87 ATTESTATION_VERIFICATION_TIMEOUT_SECONDS, 88 TimeUnit.SECONDS 89 ); 90 } catch (InterruptedException e) { 91 throw new SecureChannelException("Attestation verification was interrupted", e); 92 } 93 94 if (!finished) { 95 throw new SecureChannelException("Attestation verification timed out."); 96 } 97 98 return verificationResult.get(); 99 } 100 } 101