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.database;
18 
19 import androidx.room.Dao;
20 import androidx.room.Insert;
21 import androidx.room.Query;
22 import androidx.room.Transaction;
23 import androidx.room.Update;
24 
25 import java.time.Instant;
26 import java.util.List;
27 
28 /**
29  * DAO accessor for ProvisionedKey entities in RKPD database. This class helps us to execute queries
30  * over the database for remotely provisioned keys.
31  */
32 @Dao
33 public abstract class ProvisionedKeyDao {
34 
35     /**
36      * Insert keys to database.
37      */
38     @Insert
insertKeys(List<ProvisionedKey> keys)39     public abstract void insertKeys(List<ProvisionedKey> keys);
40 
41     /**
42      * Update specified keys in the database.
43      */
44     @Update
updateKey(ProvisionedKey key)45     public abstract void updateKey(ProvisionedKey key);
46 
47     /**
48      * Gets all the keys in the database.
49      */
50     @Query("SELECT * FROM provisioned_keys")
getAllKeys()51     public abstract List<ProvisionedKey> getAllKeys();
52 
53     /**
54      * Deletes a specific key from the database.
55      */
56     @Query("DELETE from provisioned_keys WHERE key_blob = :keyBlob")
deleteKey(byte[] keyBlob)57     public abstract void deleteKey(byte[] keyBlob);
58 
59     /**
60      * Delete all the provisioned keys.
61      */
62     @Query("DELETE FROM provisioned_keys")
deleteAllKeys()63     public abstract void deleteAllKeys();
64 
65     /**
66      * Delete all expiring keys provided by given Instant.
67      */
68     @Query("DELETE FROM provisioned_keys WHERE expiration_time < :expiryTime")
deleteExpiringKeys(Instant expiryTime)69     public abstract void deleteExpiringKeys(Instant expiryTime);
70 
71     /**
72      * Get a count of provisioned keys for a specific IRPC that are expiring at a given Instant.
73      */
74     @Query("SELECT COUNT(*) FROM provisioned_keys"
75             + " WHERE expiration_time < :expiryTime AND irpc_hal = :irpcHal")
getTotalExpiringKeysForIrpc(String irpcHal, Instant expiryTime)76     public abstract int getTotalExpiringKeysForIrpc(String irpcHal, Instant expiryTime);
77 
78     /**
79      * Get provisioned keys that can be assigned to clients, factoring in an expiration time to
80      * ensure that we do not return stale keys.
81      *
82      * @param minExpiry Any keys that expire previous to this time will not be considered, as they
83      *                  are too stale.
84      */
85     @Query("SELECT * FROM provisioned_keys"
86             + " WHERE client_uid IS NULL AND irpc_hal = :irpcHal AND expiration_time >= :minExpiry"
87             + " LIMIT 1")
getUnassignedKeyForIrpc(String irpcHal, Instant minExpiry)88     abstract ProvisionedKey getUnassignedKeyForIrpc(String irpcHal, Instant minExpiry);
89 
90     /**
91      * Gets total number of keys that can be assigned for a specific IRPC.
92      */
93     @Query("SELECT COUNT(*) FROM provisioned_keys WHERE client_uid IS NULL AND irpc_hal = :irpcHal")
getTotalUnassignedKeysForIrpc(String irpcHal)94     public abstract int getTotalUnassignedKeysForIrpc(String irpcHal);
95 
96     /**
97      * Gets total keys attested for a specific IRPC.
98      */
99     @Query("SELECT COUNT(*) FROM provisioned_keys WHERE irpc_hal = :irpcHal")
getTotalKeysForIrpc(String irpcHal)100     public abstract int getTotalKeysForIrpc(String irpcHal);
101 
102     /**
103      * Get key for given client and IRPC.
104      */
105     @Query("SELECT * FROM provisioned_keys"
106             + " WHERE client_uid = :clientUid AND irpc_hal = :irpcHal AND key_id = :keyId")
getKeyForClientAndIrpc(String irpcHal, int clientUid, int keyId)107     public abstract ProvisionedKey getKeyForClientAndIrpc(String irpcHal, int clientUid, int keyId);
108 
109     /**
110      * Stores the upgraded key blob.
111      */
112     @Query("UPDATE provisioned_keys SET key_blob = :newKeyBlob"
113             + " WHERE key_blob = :oldKeyBlob AND client_uid = :clientUid")
upgradeKeyBlob(int clientUid, byte[] oldKeyBlob, byte[] newKeyBlob)114     public abstract int upgradeKeyBlob(int clientUid, byte[] oldKeyBlob, byte[] newKeyBlob);
115 
116     /**
117      * This transaction first looks to see if a caller already has a key assigned, and if so
118      * returns that. If not, the caller is then assigned a key from the available pool of keys.
119      * If a key was assigned (either by this method or a previous call to this method), then the
120      * assigned key is returned. If no keys are available, this method returns null.
121      *
122      * @param irpcHal   The HAL for which we need to assign a key
123      * @param minExpiry The minimum expiration time allowed for an assigned key. Any keys that
124      *                  expire before minExpiry will not be assigned.
125      * @param clientUid Uid for RKPD's client that needs to set up the key for its own client.
126      * @param keyId     Client provided identifier to set up the key with.
127      * @return the key that has been assigned to the given (irpcHal, clientUid, keyId) tuple,
128      * else null if no keys are available to be assigned.
129      */
130     @Transaction
getOrAssignKey(String irpcHal, Instant minExpiry, int clientUid, int keyId)131     public ProvisionedKey getOrAssignKey(String irpcHal, Instant minExpiry, int clientUid,
132             int keyId) {
133         ProvisionedKey existingKey = getKeyForClientAndIrpc(irpcHal, clientUid, keyId);
134         if (existingKey != null) {
135             return existingKey;
136         }
137 
138         ProvisionedKey availableKey = getUnassignedKeyForIrpc(irpcHal, minExpiry);
139         if (availableKey == null) {
140             return null;
141         }
142         availableKey.clientUid = clientUid;
143         availableKey.keyId = keyId;
144         updateKey(availableKey);
145         return availableKey;
146     }
147 }
148