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