extract database access from CachedPublicKeyRing

This commit is contained in:
Vincent Breitmoser
2018-06-26 10:24:19 +02:00
parent aa640f3227
commit 31830a8c86
40 changed files with 328 additions and 730 deletions

View File

@@ -41,6 +41,15 @@ class AbstractDao {
return result;
}
<T> T mapSingleRow(SupportSQLiteQuery query, Mapper<T> mapper) {
try (Cursor cursor = getReadableDb().query(query)) {
if (cursor.moveToNext()) {
return mapper.map(cursor);
}
}
return null;
}
interface Mapper<T> {
T map(Cursor cursor);
}

View File

@@ -18,16 +18,11 @@
package org.sufficientlysecure.keychain.provider;
import android.net.Uri;
import org.sufficientlysecure.keychain.model.CustomColumnAdapters;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import timber.log.Timber;
/** This implementation of KeyRing provides a cached view of PublicKeyRing
@@ -47,234 +42,64 @@ import timber.log.Timber;
*
*/
public class CachedPublicKeyRing extends KeyRing {
private UnifiedKeyInfo unifiedKeyInfo;
final KeyRepository mKeyRepository;
final Uri mUri;
public CachedPublicKeyRing(KeyRepository keyRepository, Uri uri) {
mKeyRepository = keyRepository;
mUri = uri;
public CachedPublicKeyRing(UnifiedKeyInfo unifiedKeyInfo) {
this.unifiedKeyInfo = unifiedKeyInfo;
}
@Override
public long getMasterKeyId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID, KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data;
} catch (KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public long getMasterKeyId() {
return unifiedKeyInfo.master_key_id();
}
/**
* Find the master key id related to a given query. The id will either be extracted from the
* query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
*/
public long extractOrGetMasterKeyId() throws PgpKeyNotFoundException {
// try extracting from the uri first
String firstSegment = mUri.getPathSegments().get(1);
if (!"find".equals(firstSegment)) try {
return Long.parseLong(firstSegment);
} catch (NumberFormatException e) {
// didn't work? oh well.
Timber.d("Couldn't get masterKeyId from URI, querying...");
}
return getMasterKeyId();
public byte[] getFingerprint() {
return unifiedKeyInfo.fingerprint();
}
public byte[] getFingerprint() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.FINGERPRINT, KeyRepository.FIELD_TYPE_BLOB);
return (byte[]) data;
} catch (KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public long getCreationTime() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.CREATION, KeyRepository.FIELD_TYPE_INTEGER);
return (long) data;
} catch (KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public long getCreationTime() {
return unifiedKeyInfo.creation();
}
@Override
public String getPrimaryUserId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.USER_ID,
KeyRepository.FIELD_TYPE_STRING);
return (String) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public String getPrimaryUserId() {
return unifiedKeyInfo.user_id();
}
public String getPrimaryUserIdWithFallback() throws PgpKeyNotFoundException {
public String getPrimaryUserIdWithFallback() {
return getPrimaryUserId();
}
public String getName() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.NAME,
KeyRepository.FIELD_TYPE_STRING);
return (String) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public String getEmail() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.EMAIL,
KeyRepository.FIELD_TYPE_STRING);
return (String) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public String getComment() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.COMMENT,
KeyRepository.FIELD_TYPE_STRING);
return (String) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
@Override
public boolean isRevoked() {
return unifiedKeyInfo.is_revoked();
}
@Override
public boolean isRevoked() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.IS_REVOKED,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public boolean canCertify() {
return unifiedKeyInfo.can_certify();
}
@Override
public boolean canCertify() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.HAS_CERTIFY_SECRET,
KeyRepository.FIELD_TYPE_NULL);
return !((Boolean) data);
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public long getEncryptId() {
return unifiedKeyInfo.has_encrypt_key_int();
}
@Override
public long getEncryptId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.HAS_ENCRYPT,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public boolean hasEncrypt() {
return unifiedKeyInfo.has_encrypt_key();
}
public long getAuthenticationId() {
return unifiedKeyInfo.has_auth_key_int();
}
@Override
public boolean hasEncrypt() throws PgpKeyNotFoundException {
return getEncryptId() != 0;
public VerificationStatus getVerified() {
return unifiedKeyInfo.verified();
}
/** Returns the key id which should be used for signing.
*
* This method returns keys which are actually available (ie. secret available, and not stripped,
* revoked, or expired), hence only works on keyrings where a secret key is available!
*
*/
public long getSecretSignId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.HAS_SIGN_SECRET,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
/** Returns the key id which should be used for authentication.
*
* This method returns keys which are actually available (ie. secret available, and not stripped,
* revoked, or expired), hence only works on keyrings where a secret key is available!
*
*/
public long getSecretAuthenticationId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.HAS_AUTHENTICATE_SECRET,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public long getAuthenticationId() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeyRings.HAS_AUTHENTICATE,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
@Override
public VerificationStatus getVerified() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.VERIFIED,
KeyRepository.FIELD_TYPE_INTEGER);
return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode((Long) data);
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public boolean hasAnySecret() throws PgpKeyNotFoundException {
try {
Object data = mKeyRepository.getGenericData(mUri,
KeychainContract.KeyRings.HAS_ANY_SECRET,
KeyRepository.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
}
public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException {
SecretKeyType secretKeyType = mKeyRepository.getSecretKeyType(keyId);
if (secretKeyType == null) {
throw new NotFoundException();
}
return secretKeyType;
}
public byte[] getEncoded() throws PgpKeyNotFoundException {
try {
return mKeyRepository.loadPublicKeyRingData(getMasterKeyId());
} catch(KeyWritableRepository.NotFoundException e) {
throw new PgpKeyNotFoundException(e);
}
public boolean hasAnySecret() {
return unifiedKeyInfo.has_any_secret();
}
}

View File

@@ -21,13 +21,11 @@ package org.sufficientlysecure.keychain.provider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.squareup.sqldelight.SqlDelightQuery;
import org.bouncycastle.bcpg.ArmoredOutputStream;
@@ -43,21 +41,10 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStat
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import timber.log.Timber;
public class KeyRepository extends AbstractDao {
// If we ever switch to api level 11, we can ditch this whole mess!
public static final int FIELD_TYPE_NULL = 1;
// this is called integer to stay coherent with the constants in Cursor (api level 11)
public static final int FIELD_TYPE_INTEGER = 2;
public static final int FIELD_TYPE_FLOAT = 3;
public static final int FIELD_TYPE_STRING = 4;
public static final int FIELD_TYPE_BLOB = 5;
final ContentResolver contentResolver;
final LocalPublicKeyStorage mLocalPublicKeyStorage;
final LocalSecretKeyStorage localSecretKeyStorage;
@@ -115,72 +102,13 @@ public class KeyRepository extends AbstractDao {
mLog = new OperationLog();
}
Object getGenericData(Uri uri, String column, int type) throws NotFoundException {
Object result = getGenericData(uri, new String[]{column}, new int[]{type}, null).get(column);
if (result == null) {
// replace with getUnifiedKeyInfo
public CachedPublicKeyRing getCachedPublicKeyRing(long masterKeyId) throws NotFoundException {
UnifiedKeyInfo unifiedKeyInfo = getUnifiedKeyInfo(masterKeyId);
if (unifiedKeyInfo == null) {
throw new NotFoundException();
}
return result;
}
private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types)
throws NotFoundException {
return getGenericData(uri, proj, types, null);
}
private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types, String selection)
throws NotFoundException {
Cursor cursor = contentResolver.query(uri, proj, selection, null, null);
try {
HashMap<String, Object> result = new HashMap<>(proj.length);
if (cursor != null && cursor.moveToFirst()) {
int pos = 0;
for (String p : proj) {
switch (types[pos]) {
case FIELD_TYPE_NULL:
result.put(p, cursor.isNull(pos));
break;
case FIELD_TYPE_INTEGER:
result.put(p, cursor.getLong(pos));
break;
case FIELD_TYPE_FLOAT:
result.put(p, cursor.getFloat(pos));
break;
case FIELD_TYPE_STRING:
result.put(p, cursor.getString(pos));
break;
case FIELD_TYPE_BLOB:
result.put(p, cursor.getBlob(pos));
break;
}
pos += 1;
}
} else {
// If no data was found, throw an appropriate exception
throw new NotFoundException();
}
return result;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types)
throws NotFoundException {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {
long masterKeyId = new CachedPublicKeyRing(this, queryUri).extractOrGetMasterKeyId();
return getCachedPublicKeyRing(masterKeyId);
}
public CachedPublicKeyRing getCachedPublicKeyRing(long id) {
return new CachedPublicKeyRing(this, KeyRings.buildUnifiedKeyRingUri(id));
return new CachedPublicKeyRing(unifiedKeyInfo);
}
public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long masterKeyId) throws NotFoundException {
@@ -211,11 +139,7 @@ public class KeyRepository extends AbstractDao {
}
public List<Long> getMasterKeyIdsBySigner(List<Long> signerMasterKeyIds) {
long[] signerKeyIds = new long[signerMasterKeyIds.size()];
int i = 0;
for (Long signerKeyId : signerMasterKeyIds) {
signerKeyIds[i++] = signerKeyId;
}
long[] signerKeyIds = getLongListAsArray(signerMasterKeyIds);
SqlDelightQuery query = SubKey.FACTORY.selectMasterKeyIdsBySigner(signerKeyIds);
return mapAllRows(query, KeyRingPublic.FACTORY.selectAllMasterKeyIdsMapper()::map);
}
@@ -240,6 +164,11 @@ public class KeyRepository extends AbstractDao {
}
}
public List<UnifiedKeyInfo> getUnifiedKeyInfo(long... masterKeyIds) {
SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoByMasterKeyIds(masterKeyIds);
return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map);
}
public List<UnifiedKeyInfo> getUnifiedKeyInfosByMailAddress(String mailAddress) {
SqlDelightQuery query = SubKey.FACTORY.selectUnifiedKeyInfoSearchMailAddress('%' + mailAddress + '%');
return mapAllRows(query, SubKey.UNIFIED_KEY_INFO_MAPPER::map);
@@ -275,17 +204,17 @@ public class KeyRepository extends AbstractDao {
return mapAllRows(query, SubKey.SUBKEY_MAPPER::map);
}
public SecretKeyType getSecretKeyType(long keyId) {
public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException {
SqlDelightQuery query = SubKey.FACTORY.selectSecretKeyType(keyId);
try (Cursor cursor = getReadableDb().query(query)) {
if (cursor.moveToFirst()) {
return SubKey.SKT_MAPPER.map(cursor);
}
return null;
throw new NotFoundException();
}
}
private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException, PgpGeneralException {
private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
@@ -295,15 +224,13 @@ public class KeyRepository extends AbstractDao {
return bos.toByteArray();
}
public String getPublicKeyRingAsArmoredString(long masterKeyId)
throws NotFoundException, IOException, PgpGeneralException {
public String getPublicKeyRingAsArmoredString(long masterKeyId) throws NotFoundException, IOException {
byte[] data = loadPublicKeyRingData(masterKeyId);
byte[] armoredData = getKeyRingAsArmoredData(data);
return new String(armoredData);
}
public byte[] getSecretKeyRingAsArmoredData(long masterKeyId)
throws NotFoundException, IOException, PgpGeneralException {
public byte[] getSecretKeyRingAsArmoredData(long masterKeyId) throws NotFoundException, IOException {
byte[] data = loadSecretKeyRingData(masterKeyId);
return getKeyRingAsArmoredData(data);
}
@@ -338,6 +265,24 @@ public class KeyRepository extends AbstractDao {
}
}
public long getSecretSignId(long masterKeyId) throws NotFoundException {
SqlDelightQuery query = SubKey.FACTORY.selectEffectiveSignKeyIdByMasterKeyId(masterKeyId);
Long signKeyId = mapSingleRow(query, SubKey.FACTORY.selectEffectiveSignKeyIdByMasterKeyIdMapper()::map);
if (signKeyId == null) {
throw new NotFoundException();
}
return signKeyId;
}
public Long getSecretAuthenticationId(long masterKeyId) throws NotFoundException {
SqlDelightQuery query = SubKey.FACTORY.selectEffectiveAuthKeyIdByMasterKeyId(masterKeyId);
Long authKeyId = mapSingleRow(query, SubKey.FACTORY.selectEffectiveAuthKeyIdByMasterKeyIdMapper()::map);
if (authKeyId == null) {
throw new NotFoundException();
}
return authKeyId;
}
public static class NotFoundException extends Exception {
public NotFoundException() {
}
@@ -346,4 +291,13 @@ public class KeyRepository extends AbstractDao {
super(name);
}
}
private long[] getLongListAsArray(List<Long> longList) {
long[] longs = new long[longList.size()];
int i = 0;
for (Long aLong : longList) {
longs[i++] = aLong;
}
return longs;
}
}

View File

@@ -29,7 +29,6 @@ import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.support.annotation.NonNull;
@@ -40,6 +39,7 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.KeyRingsPublicModel.DeleteByMasterKeyId;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.model.CustomColumnAdapters;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
@@ -60,7 +60,6 @@ import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
@@ -99,8 +98,7 @@ public class KeyWritableRepository extends KeyRepository {
localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, autocryptPeerDao);
}
@VisibleForTesting
KeyWritableRepository(Context context,
private KeyWritableRepository(Context context,
KeychainDatabase database, LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage,
DatabaseNotifyManager databaseNotifyManager, AutocryptPeerDao autocryptPeerDao) {
@@ -120,40 +118,22 @@ public class KeyWritableRepository extends KeyRepository {
}
private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() {
Cursor cursor = contentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {
KeyRings.MASTER_KEY_ID,
// we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED
}, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<>();
try {
LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<>();
if (cursor == null) {
return result;
}
while (cursor.moveToNext()) {
try {
long masterKeyId = cursor.getLong(0);
long verified = cursor.getLong(2);
byte[] blob = loadPublicKeyRingData(masterKeyId);
VerificationStatus verificationStatus = CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified);
if (blob != null) {
result.put(masterKeyId, new CanonicalizedPublicKeyRing(blob, verificationStatus).getPublicKey());
}
} catch (NotFoundException e) {
throw new IllegalStateException("Error reading secret key data, this should not happen!", e);
List<UnifiedKeyInfo> unifiedKeyInfoWithSecret = getAllUnifiedKeyInfoWithSecret();
for (UnifiedKeyInfo unifiedKeyInfo : unifiedKeyInfoWithSecret) {
try {
byte[] blob = loadPublicKeyRingData(unifiedKeyInfo.master_key_id());
if (blob != null) {
result.put(unifiedKeyInfo.master_key_id(),
new CanonicalizedPublicKeyRing(blob, unifiedKeyInfo.verified()).getPublicKey());
}
}
return result;
} finally {
if (cursor != null) {
cursor.close();
} catch (NotFoundException e) {
throw new IllegalStateException("Error reading secret key data, this should not happen!", e);
}
}
return result;
}
// bits, in order: CESA. make SURE these are correct, we will get bad log entries otherwise!!

View File

@@ -99,9 +99,6 @@ public class KeychainContract {
public static final String PATH_UNIFIED = "unified";
public static final String PATH_FIND = "find";
public static final String PATH_BY_SUBKEY = "subkey";
public static final String PATH_PUBLIC = "public";
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_KEYS = "keys";
@@ -132,16 +129,6 @@ public class KeychainContract {
public static Uri buildGenericKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
}
public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
.appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
.appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build();
}
}
public static class KeyRingData implements KeyRingsColumns, BaseColumns {

View File

@@ -32,7 +32,9 @@ import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteException;
import android.support.annotation.VisibleForTesting;
import org.sufficientlysecure.keychain.ApiAllowedKeysModel;
import org.sufficientlysecure.keychain.ApiAppsModel;
import org.sufficientlysecure.keychain.AutocryptPeersModel;
import org.sufficientlysecure.keychain.CertsModel;
@@ -66,12 +68,15 @@ public class KeychainDatabase {
private static KeychainDatabase sInstance;
public static KeychainDatabase getInstance(Context context) {
if (sInstance == null) {
sInstance = new KeychainDatabase(context.getApplicationContext());
}
return sInstance;
}
@VisibleForTesting
public static void resetSingleton() {
sInstance = null;
}
public interface Tables {
String KEY_RINGS_PUBLIC = "keyrings_public";
String KEYS = "keys";
@@ -133,7 +138,7 @@ public class KeychainDatabase {
db.execSQL(ApiAppsModel.CREATE_TABLE);
db.execSQL(OverriddenWarningsModel.CREATE_TABLE);
db.execSQL(AutocryptPeersModel.CREATE_TABLE);
db.execSQL(ApiAppsModel.CREATE_TABLE);
db.execSQL(ApiAllowedKeysModel.CREATE_TABLE);
db.execSQL(KeysModel.UNIFIEDKEYVIEW);
db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");");
@@ -460,8 +465,8 @@ public class KeychainDatabase {
// DANGEROUS, use in test code ONLY!
public void clearDatabase() {
getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC);
getWritableDatabase().execSQL("delete from " + Tables.API_ALLOWED_KEYS);
getWritableDatabase().execSQL("delete from " + KeyRingsPublicModel.TABLE_NAME);
getWritableDatabase().execSQL("delete from " + ApiAllowedKeysModel.TABLE_NAME);
getWritableDatabase().execSQL("delete from api_apps");
}

View File

@@ -61,8 +61,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
private static final int KEY_RING_PUBLIC = 203;
private static final int KEY_RING_CERTS = 205;
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
private static final int KEY_SIGNATURES = 700;
protected UriMatcher mUriMatcher;
@@ -88,16 +86,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
+ "/" + KeychainContract.PATH_UNIFIED,
KEY_RINGS_UNIFIED);
/*
* find by criteria other than master key id
*
* key_rings/find/subkey/_
*
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
+ KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*",
KEY_RINGS_FIND_BY_SUBKEY);
/*
* list key_ring specifics
*
@@ -174,8 +162,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
switch (match) {
case KEY_RING_UNIFIED:
case KEY_RINGS_UNIFIED:
case KEY_RINGS_FIND_BY_SUBKEY: {
case KEY_RINGS_UNIFIED: {
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
@@ -318,28 +305,8 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
// in case there are multiple verifying certificates
groupBy = Tables.KEYS + "." + Keys.MASTER_KEY_ID;
switch(match) {
case KEY_RING_UNIFIED: {
qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
}
case KEY_RINGS_FIND_BY_SUBKEY: {
try {
String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
qb.appendWhere(" AND EXISTS ("
+ " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
+ " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ ")");
} catch(NumberFormatException e) {
Timber.e(e, "Malformed find by subkey query!");
qb.appendWhere(" AND 0");
}
break;
}
}
qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC";