1 /** 2 * Copyright (C) 2020 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.remoteprovisioner.service; 18 19 import android.app.Service; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.security.IGenerateRkpKeyService; 26 import android.security.remoteprovisioning.AttestationPoolStatus; 27 import android.security.remoteprovisioning.IRemoteProvisioning; 28 import android.security.remoteprovisioning.ImplInfo; 29 import android.util.Log; 30 31 import com.android.remoteprovisioner.GeekResponse; 32 import com.android.remoteprovisioner.PeriodicProvisioner; 33 import com.android.remoteprovisioner.ProvisionerMetrics; 34 import com.android.remoteprovisioner.ProvisionerMetrics.StopWatch; 35 import com.android.remoteprovisioner.RemoteProvisioningException; 36 import com.android.remoteprovisioner.ServerInterface; 37 import com.android.remoteprovisioner.SettingsManager; 38 39 import java.util.concurrent.locks.ReentrantLock; 40 41 /** 42 * Provides the implementation for IGenerateRkpKeyService.aidl 43 */ 44 public class GenerateRkpKeyService extends Service { 45 private static final int KEY_GENERATION_PAUSE_MS = 1000; 46 47 private static final String SERVICE = "android.security.remoteprovisioning"; 48 private static final String TAG = "RemoteProvisioningService"; 49 50 private static final ReentrantLock sLock = new ReentrantLock(); 51 52 private enum Concurrency { 53 BLOCKING, 54 NON_BLOCKING 55 }; 56 57 @Override onCreate()58 public void onCreate() { 59 super.onCreate(); 60 } 61 62 @Override onBind(Intent intent)63 public IBinder onBind(Intent intent) { 64 return mBinder; 65 } 66 67 private final IGenerateRkpKeyService.Stub mBinder = new IGenerateRkpKeyService.Stub() { 68 @Override 69 public int generateKey(int securityLevel) { 70 Log.i(TAG, "generateKey ping for secLevel: " + securityLevel); 71 try (ProvisionerMetrics metrics = 72 ProvisionerMetrics.createOutOfKeysAttemptMetrics( 73 getApplicationContext(), securityLevel)) { 74 IRemoteProvisioning binder = 75 IRemoteProvisioning.Stub.asInterface(ServiceManager.getService(SERVICE)); 76 return checkAndFillPool(binder, securityLevel, metrics, Concurrency.BLOCKING); 77 } 78 } 79 80 @Override 81 public void notifyKeyGenerated(int securityLevel) { 82 Log.i(TAG, "Notify key generated ping for secLevel: " + securityLevel); 83 try (ProvisionerMetrics metrics = 84 ProvisionerMetrics.createKeyConsumedAttemptMetrics( 85 getApplicationContext(), securityLevel)) { 86 IRemoteProvisioning binder = 87 IRemoteProvisioning.Stub.asInterface(ServiceManager.getService(SERVICE)); 88 checkAndFillPool(binder, securityLevel, metrics, Concurrency.NON_BLOCKING); 89 } 90 } 91 92 private int checkAndFillPool(IRemoteProvisioning binder, int secLevel, 93 ProvisionerMetrics metrics, Concurrency concurrency) { 94 // No need to hammer the pool check with a ton of redundant requests. 95 if (concurrency == Concurrency.BLOCKING) { 96 Log.i(TAG, "Waiting on lock to check pool status."); 97 try (StopWatch ignored = metrics.startLockWait()) { 98 sLock.lock(); 99 } 100 } else if (!sLock.tryLock()) { 101 Log.i(TAG, "Exiting check; another process already started the check."); 102 metrics.setStatus(ProvisionerMetrics.Status.UNKNOWN); 103 return Status.OK; 104 } 105 try { 106 AttestationPoolStatus pool; 107 ImplInfo[] implInfos; 108 try (StopWatch ignored = metrics.startBinderWait()) { 109 pool = binder.getPoolStatus(System.currentTimeMillis(), secLevel); 110 implInfos = binder.getImplementationInfo(); 111 } 112 int curve = -1; 113 for (int i = 0; i < implInfos.length; i++) { 114 if (implInfos[i].secLevel == secLevel) { 115 curve = implInfos[i].supportedCurve; 116 break; 117 } 118 } 119 // If curve has not been set, the security level does not have an associated 120 // RKP instance. This should only be possible for StrongBox on S. 121 if (curve == -1) { 122 metrics.setStatus(ProvisionerMetrics.Status.NO_PROVISIONING_NEEDED); 123 } 124 125 Context context = getApplicationContext(); 126 int keysToProvision = 127 PeriodicProvisioner.generateNumKeysNeeded( 128 binder, 129 context, 130 SettingsManager.getExpirationTime(context).toEpochMilli(), 131 secLevel, 132 metrics); 133 if (keysToProvision != 0) { 134 Log.i(TAG, "All signed keys are currently in use, provisioning more."); 135 GeekResponse resp = ServerInterface.fetchGeek(context, metrics); 136 PeriodicProvisioner.batchProvision(binder, context, keysToProvision, secLevel, 137 resp.getGeekChain(curve), resp.getChallenge(), metrics); 138 metrics.setStatus(ProvisionerMetrics.Status.KEYS_SUCCESSFULLY_PROVISIONED); 139 } else { 140 metrics.setStatus(ProvisionerMetrics.Status.NO_PROVISIONING_NEEDED); 141 } 142 } catch (InterruptedException e) { 143 Log.e(TAG, "Provisioner thread interrupted.", e); 144 } catch (RemoteProvisioningException e) { 145 Log.e(TAG, "RemoteProvisioningException: ", e); 146 return e.getErrorCode(); 147 } catch (RemoteException e) { 148 Log.e(TAG, "Remote Exception: ", e); 149 return Status.INTERNAL_ERROR; 150 } finally { 151 sLock.unlock(); 152 } 153 return Status.OK; 154 } 155 }; 156 } 157