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.rkpdapp.utils;
18 
19 import android.util.Log;
20 
21 import com.android.rkpdapp.database.ProvisionedKeyDao;
22 
23 import java.time.Instant;
24 
25 /**
26  * Utility class to process and maintain the key count.
27  */
28 public class StatsProcessor {
29     public static final double LIMIT_SCALER = .4;
30 
31     private static final String TAG = "RkpdKeyPoolStats";
32 
StatsProcessor()33     private StatsProcessor() {}
34 
35     /**
36      * Returns the minimum unassigned keys required to trigger a round of remote key provisioning.
37      */
calcMinUnassignedToTriggerProvisioning(int extraSignedKeysAvailable)38     public static int calcMinUnassignedToTriggerProvisioning(int extraSignedKeysAvailable) {
39         return (int) Math.ceil(LIMIT_SCALER * extraSignedKeysAvailable);
40     }
41 
42     /**
43      * Creates a PoolStats. Takes an {@code ProvisionedKeyDao} and calculates different
44      * pieces of status to inform the caller if any action needs to be taken to re-provision the
45      * pool and what action is needed in terms of keys to generate.
46      *
47      * @param keyDao class to get current status of RKPD database.
48      * @param extraSignedKeysAvailable how many extra attested keys should ideally be available
49      *                                     for assignment.
50      * @param irpcHal IRPC HAL for which we need to calculate pool status.
51      * @return the PoolStats object describing higher level info about the state of the key pool.
52      */
processPool(ProvisionedKeyDao keyDao, String irpcHal, int extraSignedKeysAvailable, Instant expirationTime)53     public static PoolStats processPool(ProvisionedKeyDao keyDao, String irpcHal,
54             int extraSignedKeysAvailable, Instant expirationTime) {
55         PoolStats stats = new PoolStats();
56         int totalKeys = keyDao.getTotalKeysForIrpc(irpcHal);
57         int unassignedKeys = keyDao.getTotalUnassignedKeysForIrpc(irpcHal);
58         int expiringKeys = keyDao.getTotalExpiringKeysForIrpc(irpcHal, expirationTime);
59         stats.keysUnassigned = unassignedKeys;
60         stats.keysInUse = totalKeys - unassignedKeys;
61         // Need to generate the total number of keys in use along with the "slack" of extra signed
62         // keys so that we always have extra keys when other clients decide to call us.
63         stats.idealTotalSignedKeys = stats.keysInUse + extraSignedKeysAvailable;
64         // If nothing is expiring, and the amount of available unassigned keys is sufficient,
65         // then do nothing. Otherwise, generate the complete amount of idealTotalSignedKeys.
66         //
67         // It will reduce network usage if the app just provisions an entire new batch in one go,
68         // rather than consistently grabbing just a few at a time as the expiration dates become
69         // misaligned.
70         boolean provisioningNeeded =
71                 (unassignedKeys - expiringKeys)
72                 <= calcMinUnassignedToTriggerProvisioning(extraSignedKeysAvailable);
73         if (!provisioningNeeded) {
74             Log.i(TAG, "Sufficient keys are available, no CSR needed.");
75             stats.keysToGenerate = 0;
76         } else {
77             stats.keysToGenerate = stats.idealTotalSignedKeys;
78         }
79         Log.i(TAG, stats.toString());
80         return stats;
81     }
82 
83     /**
84      * Actual stats for KeyPool
85      */
86     public static class PoolStats {
87         public int keysInUse;
88         public int idealTotalSignedKeys;
89         public int keysToGenerate;
90         public int keysUnassigned;
91 
92         @Override
toString()93         public String toString() {
94             return "PoolStats{"
95                     + "keysInUse=" + keysInUse
96                     + ", idealTotalSignedKeys=" + idealTotalSignedKeys
97                     + ", keysToGenerate=" + keysToGenerate
98                     + ", keysUnassigned=" + keysUnassigned
99                     + '}';
100         }
101     }
102 }
103