• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.odp.module.common.data;
18 
19 import static com.android.odp.module.common.encryption.OdpEncryptionKeyContract.ENCRYPTION_KEY_TABLE;
20 
21 import android.annotation.NonNull;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.database.Cursor;
25 import android.database.sqlite.SQLiteDatabase;
26 import android.database.sqlite.SQLiteException;
27 
28 import com.android.federatedcompute.internal.util.LogUtil;
29 import com.android.odp.module.common.Clock;
30 import com.android.odp.module.common.MonotonicClock;
31 import com.android.odp.module.common.encryption.OdpEncryptionKey;
32 import com.android.odp.module.common.encryption.OdpEncryptionKeyContract.OdpEncryptionColumns;
33 
34 import com.google.common.annotations.VisibleForTesting;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /** DAO for accessing encryption key table that stores {@link OdpEncryptionKey}s. */
40 public class OdpEncryptionKeyDao {
41     private static final String TAG = OdpEncryptionKeyDao.class.getSimpleName();
42 
43     private final OdpSQLiteOpenHelper mDbHelper;
44 
45     private final Clock mClock;
46 
47     private static volatile OdpEncryptionKeyDao sSingletonInstance;
48 
OdpEncryptionKeyDao(OdpSQLiteOpenHelper dbHelper, Clock clock)49     private OdpEncryptionKeyDao(OdpSQLiteOpenHelper dbHelper, Clock clock) {
50         mDbHelper = dbHelper;
51         mClock = clock;
52     }
53 
54     /** Returns an instance of {@link OdpEncryptionKeyDao} given a context. */
55     @NonNull
getInstance(Context context, OdpSQLiteOpenHelper dbHelper)56     public static OdpEncryptionKeyDao getInstance(Context context, OdpSQLiteOpenHelper dbHelper) {
57         if (sSingletonInstance == null) {
58             synchronized (OdpEncryptionKeyDao.class) {
59                 if (sSingletonInstance == null) {
60                     sSingletonInstance =
61                             new OdpEncryptionKeyDao(dbHelper, MonotonicClock.getInstance());
62                 }
63             }
64         }
65         return sSingletonInstance;
66     }
67 
68     /**
69      * Insert a key to the encryption_key table.
70      *
71      * @param key the {@link OdpEncryptionKey} to insert into DB.
72      * @return Whether the key was inserted successfully.
73      */
insertEncryptionKey(OdpEncryptionKey key)74     public boolean insertEncryptionKey(OdpEncryptionKey key) {
75         SQLiteDatabase db = mDbHelper.safeGetWritableDatabase();
76         if (db == null) {
77             throw new SQLiteException(TAG + ": Failed to open database.");
78         }
79 
80         ContentValues values = new ContentValues();
81         values.put(OdpEncryptionColumns.KEY_IDENTIFIER, key.getKeyIdentifier());
82         values.put(OdpEncryptionColumns.PUBLIC_KEY, key.getPublicKey());
83         values.put(OdpEncryptionColumns.KEY_TYPE, key.getKeyType());
84         values.put(OdpEncryptionColumns.CREATION_TIME, key.getCreationTime());
85         values.put(OdpEncryptionColumns.EXPIRY_TIME, key.getExpiryTime());
86 
87         long insertedRowId =
88                 db.insertWithOnConflict(
89                         ENCRYPTION_KEY_TABLE, "", values, SQLiteDatabase.CONFLICT_REPLACE);
90         return insertedRowId != -1;
91     }
92 
93     /**
94      * Read from encryption key table given selection, order and limit conditions.
95      *
96      * @return a list of matching {@link OdpEncryptionKey}s.
97      */
98     @VisibleForTesting
readEncryptionKeysFromDatabase( String selection, String[] selectionArgs, String orderBy, int count)99     public List<OdpEncryptionKey> readEncryptionKeysFromDatabase(
100             String selection, String[] selectionArgs, String orderBy, int count) {
101         List<OdpEncryptionKey> keyList = new ArrayList<>();
102         SQLiteDatabase db = mDbHelper.safeGetReadableDatabase();
103         if (db == null) {
104             throw new SQLiteException(TAG + ": Failed to open database.");
105         }
106 
107         String[] selectColumns = {
108             OdpEncryptionColumns.KEY_IDENTIFIER,
109             OdpEncryptionColumns.PUBLIC_KEY,
110             OdpEncryptionColumns.KEY_TYPE,
111             OdpEncryptionColumns.CREATION_TIME,
112             OdpEncryptionColumns.EXPIRY_TIME
113         };
114 
115         Cursor cursor = null;
116         try {
117             cursor =
118                     db.query(
119                             ENCRYPTION_KEY_TABLE,
120                             selectColumns,
121                             selection,
122                             selectionArgs,
123                             /* groupBy= */ null,
124                             /* having= */ null,
125                             /* orderBy= */ orderBy,
126                             /* limit= */ String.valueOf(count));
127             while (cursor.moveToNext()) {
128                 OdpEncryptionKey.Builder encryptionKeyBuilder =
129                         new OdpEncryptionKey.Builder()
130                                 .setKeyIdentifier(
131                                         cursor.getString(
132                                                 cursor.getColumnIndexOrThrow(
133                                                         OdpEncryptionColumns.KEY_IDENTIFIER)))
134                                 .setPublicKey(
135                                         cursor.getString(
136                                                 cursor.getColumnIndexOrThrow(
137                                                         OdpEncryptionColumns.PUBLIC_KEY)))
138                                 .setKeyType(
139                                         cursor.getInt(
140                                                 cursor.getColumnIndexOrThrow(
141                                                         OdpEncryptionColumns.KEY_TYPE)))
142                                 .setCreationTime(
143                                         cursor.getLong(
144                                                 cursor.getColumnIndexOrThrow(
145                                                         OdpEncryptionColumns.CREATION_TIME)))
146                                 .setExpiryTime(
147                                         cursor.getLong(
148                                                 cursor.getColumnIndexOrThrow(
149                                                         OdpEncryptionColumns.EXPIRY_TIME)));
150                 keyList.add(encryptionKeyBuilder.build());
151             }
152         } finally {
153             if (cursor != null) {
154                 cursor.close();
155             }
156         }
157         return keyList;
158     }
159 
160     /**
161      * @return latest expired keys (order by expiry time).
162      */
getLatestExpiryNKeys(int count)163     public List<OdpEncryptionKey> getLatestExpiryNKeys(int count) {
164         String selection = OdpEncryptionColumns.EXPIRY_TIME + " > ?";
165         String[] selectionArgs = {String.valueOf(mClock.currentTimeMillis())};
166         // reverse order of expiry time
167         String orderBy = OdpEncryptionColumns.EXPIRY_TIME + " DESC";
168         return readEncryptionKeysFromDatabase(selection, selectionArgs, orderBy, count);
169     }
170 
171     /**
172      * Delete expired keys.
173      *
174      * @return number of keys deleted.
175      */
deleteExpiredKeys()176     public int deleteExpiredKeys() {
177         SQLiteDatabase db = mDbHelper.safeGetWritableDatabase();
178         if (db == null) {
179             throw new SQLiteException(TAG + ": Failed to open database.");
180         }
181         String whereClause = OdpEncryptionColumns.EXPIRY_TIME + " < ?";
182         String[] whereArgs = {String.valueOf(mClock.currentTimeMillis())};
183         int deletedRows = db.delete(ENCRYPTION_KEY_TABLE, whereClause, whereArgs);
184         LogUtil.d(TAG, "Deleted %s expired keys from database", deletedRows);
185         return deletedRows;
186     }
187 
188     /** Test only method to clear the database of all keys, independent of expiry time etc. */
189     @VisibleForTesting
deleteAllKeys()190     public int deleteAllKeys() {
191         SQLiteDatabase db = mDbHelper.safeGetWritableDatabase();
192         if (db == null) {
193             throw new SQLiteException(TAG + ": Failed to open database.");
194         }
195         int deletedRows =
196                 db.delete(ENCRYPTION_KEY_TABLE, /* whereClause= */ null, /* whereArgs= */ null);
197         LogUtil.d(TAG, "Force deleted %s keys from database", deletedRows);
198         return deletedRows;
199     }
200 }
201