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; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import java.util.concurrent.CountDownLatch; 28 import java.util.concurrent.Executor; 29 import java.util.concurrent.Executors; 30 import java.util.concurrent.TimeUnit; 31 32 /** 33 * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner 34 * app. There are two cases where Keystore should use this class. 35 * 36 * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the 37 * RemoteProvisioner app check if the state of the attestation key pool is getting low enough 38 * to warrant provisioning more attestation certificates early. 39 * 40 * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of 41 * attestation key pairs and cannot provide one for the given application. Keystore can then 42 * make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another 43 * attestation certificate chain provisioned. 44 * 45 * In most cases, the proper usage of (1) should preclude the need for (2). 46 * 47 * @hide 48 */ 49 public class GenerateRkpKey { 50 private static final String TAG = "GenerateRkpKey"; 51 52 private static final int NOTIFY_EMPTY = 0; 53 private static final int NOTIFY_KEY_GENERATED = 1; 54 private static final int TIMEOUT_MS = 1000; 55 56 private IGenerateRkpKeyService mBinder; 57 private Context mContext; 58 private CountDownLatch mCountDownLatch; 59 60 private ServiceConnection mConnection = new ServiceConnection() { 61 @Override 62 public void onServiceConnected(ComponentName className, IBinder service) { 63 mBinder = IGenerateRkpKeyService.Stub.asInterface(service); 64 mCountDownLatch.countDown(); 65 } 66 67 @Override public void onBindingDied(ComponentName className) { 68 mCountDownLatch.countDown(); 69 } 70 71 @Override 72 public void onServiceDisconnected(ComponentName className) { 73 mBinder = null; 74 } 75 }; 76 77 /** 78 * Constructor which takes a Context object. 79 */ GenerateRkpKey(Context context)80 public GenerateRkpKey(Context context) { 81 mContext = context; 82 } 83 bindAndSendCommand(int command, int securityLevel)84 private void bindAndSendCommand(int command, int securityLevel) throws RemoteException { 85 Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); 86 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 87 if (comp == null) { 88 // On a system that does not use RKP, the RemoteProvisioner app won't be installed. 89 return; 90 } 91 intent.setComponent(comp); 92 mCountDownLatch = new CountDownLatch(1); 93 Executor executor = Executors.newCachedThreadPool(); 94 if (!mContext.bindService(intent, Context.BIND_AUTO_CREATE, executor, mConnection)) { 95 throw new RemoteException("Failed to bind to GenerateRkpKeyService"); 96 } 97 try { 98 mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 99 } catch (InterruptedException e) { 100 Log.e(TAG, "Interrupted: ", e); 101 } 102 if (mBinder != null) { 103 switch (command) { 104 case NOTIFY_EMPTY: 105 mBinder.generateKey(securityLevel); 106 break; 107 case NOTIFY_KEY_GENERATED: 108 mBinder.notifyKeyGenerated(securityLevel); 109 break; 110 default: 111 Log.e(TAG, "Invalid case for command"); 112 } 113 } else { 114 Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService."); 115 } 116 mContext.unbindService(mConnection); 117 } 118 119 /** 120 * Fulfills the use case of (2) described in the class documentation. Blocks until the 121 * RemoteProvisioner application can get new attestation keys signed by the server. 122 */ notifyEmpty(int securityLevel)123 public void notifyEmpty(int securityLevel) throws RemoteException { 124 bindAndSendCommand(NOTIFY_EMPTY, securityLevel); 125 } 126 127 /** 128 * Fulfills the use case of (1) described in the class documentation. Non blocking call. 129 */ notifyKeyGenerated(int securityLevel)130 public void notifyKeyGenerated(int securityLevel) throws RemoteException { 131 bindAndSendCommand(NOTIFY_KEY_GENERATED, securityLevel); 132 } 133 } 134