extract database access from CachedPublicKeyRing
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!!
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user