• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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