extract reading of public key data from ContentProvider

This commit is contained in:
Vincent Breitmoser 2018-06-19 15:33:41 +02:00
parent 6585e7113d
commit 77c89cfa98
7 changed files with 62 additions and 86 deletions

View File

@ -0,0 +1,12 @@
package org.sufficientlysecure.keychain.model;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.KeyRingsPublicModel;
@AutoValue
public abstract class KeyRingPublic implements KeyRingsPublicModel {
public static final Factory<KeyRingPublic> FACTORY = new Factory<>(AutoValue_KeyRingPublic::new);
public static final Mapper<KeyRingPublic> MAPPER = new Mapper<>(FACTORY);
}

View File

@ -23,12 +23,15 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import android.arch.persistence.db.SupportSQLiteDatabase;
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;
import org.sufficientlysecure.keychain.model.KeyRingPublic;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
@ -36,7 +39,6 @@ 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.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import timber.log.Timber;
@ -54,6 +56,8 @@ public class KeyRepository {
final ContentResolver contentResolver;
final LocalPublicKeyStorage mLocalPublicKeyStorage;
final LocalSecretKeyStorage localSecretKeyStorage;
final SupportSQLiteDatabase db;
OperationLog mLog;
int mIndent;
@ -61,18 +65,23 @@ public class KeyRepository {
ContentResolver contentResolver = context.getContentResolver();
LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context);
LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(context);
SupportSQLiteDatabase db = new KeychainDatabase(context).getWritableDatabase();
return new KeyRepository(contentResolver, localPublicKeyStorage, localSecretKeyStorage);
return new KeyRepository(contentResolver, db, localPublicKeyStorage, localSecretKeyStorage);
}
private KeyRepository(ContentResolver contentResolver, LocalPublicKeyStorage localPublicKeyStorage,
private KeyRepository(ContentResolver contentResolver, SupportSQLiteDatabase db,
LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage) {
this(contentResolver, localPublicKeyStorage, localSecretKeyStorage, new OperationLog(), 0);
this(contentResolver, db, localPublicKeyStorage, localSecretKeyStorage, new OperationLog(), 0);
}
KeyRepository(ContentResolver contentResolver, LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage, OperationLog log, int indent) {
KeyRepository(ContentResolver contentResolver, SupportSQLiteDatabase db,
LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage,
OperationLog log, int indent) {
this.contentResolver = contentResolver;
this.db = db;
mLocalPublicKeyStorage = localPublicKeyStorage;
this.localSecretKeyStorage = localSecretKeyStorage;
mIndent = indent;
@ -107,10 +116,6 @@ public class KeyRepository {
return result;
}
Object getGenericDataOrNull(Uri uri, String column, int type) throws NotFoundException {
return getGenericData(uri, new String[]{column}, new int[]{type}, null).get(column);
}
Object getGenericData(Uri uri, String column, int type, String selection)
throws NotFoundException {
return getGenericData(uri, new String[]{column}, new int[]{type}, selection).get(column);
@ -282,23 +287,20 @@ public class KeyRepository {
}
public final byte[] loadPublicKeyRingData(long masterKeyId) throws NotFoundException {
byte[] data = (byte[]) getGenericDataOrNull(KeyRingData.buildPublicKeyRingUri(masterKeyId),
KeyRingData.KEY_RING_DATA, FIELD_TYPE_BLOB);
if (data == null) {
try {
data = mLocalPublicKeyStorage.readPublicKey(masterKeyId);
} catch (IOException e) {
Timber.e(e, "Error reading public key from storage!");
throw new NotFoundException();
SqlDelightQuery query = KeyRingPublic.FACTORY.selectByMasterKeyId(masterKeyId);
try (Cursor cursor = db.query(query)) {
if (cursor.moveToFirst()) {
KeyRingPublic keyRingPublic = KeyRingPublic.MAPPER.map(cursor);
byte[] keyRingData = keyRingPublic.key_ring_data();
if (keyRingData == null) {
keyRingData = mLocalPublicKeyStorage.readPublicKey(masterKeyId);
}
return keyRingData;
}
} catch (IOException e) {
Timber.e(e, "Error reading public key from storage!");
}
if (data == null) {
throw new NotFoundException();
}
return data;
throw new NotFoundException();
}
public final byte[] loadSecretKeyRingData(long masterKeyId) throws NotFoundException {

View File

@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
@ -90,23 +91,25 @@ public class KeyWritableRepository extends KeyRepository {
LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(context);
DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context);
AutocryptPeerDao autocryptPeerDao = AutocryptPeerDao.getInstance(context);
return new KeyWritableRepository(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager,
autocryptPeerDao);
SupportSQLiteDatabase db = new KeychainDatabase(context).getWritableDatabase();
return new KeyWritableRepository(context, db,
localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, autocryptPeerDao);
}
@VisibleForTesting
KeyWritableRepository(Context context,
LocalPublicKeyStorage localPublicKeyStorage,
SupportSQLiteDatabase db, LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage,
DatabaseNotifyManager databaseNotifyManager, AutocryptPeerDao autocryptPeerDao) {
this(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, new OperationLog(), 0,
this(context, db, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, new OperationLog(), 0,
autocryptPeerDao);
}
private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage,
private KeyWritableRepository(Context context, SupportSQLiteDatabase db,
LocalPublicKeyStorage localPublicKeyStorage,
LocalSecretKeyStorage localSecretKeyStorage, DatabaseNotifyManager databaseNotifyManager,
OperationLog log, int indent, AutocryptPeerDao autocryptPeerDao) {
super(context.getContentResolver(), localPublicKeyStorage, localSecretKeyStorage, log, indent);
super(context.getContentResolver(), db, localPublicKeyStorage, localSecretKeyStorage, log, indent);
this.context = context;
this.databaseNotifyManager = databaseNotifyManager;

View File

@ -186,22 +186,9 @@ public class KeychainContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
public static final String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.key_ring_data";
public static final String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.key_ring_data";
public static Uri buildPublicKeyRingUri() {
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
public static Uri buildPublicKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_PUBLIC).build();
}
public static Uri buildPublicKeyRingUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_PUBLIC).build();
}
}
public static class Keys implements KeysColumns, BaseColumns {

View File

@ -57,7 +57,6 @@ import static android.database.DatabaseUtils.dumpCursorToString;
public class KeychainProvider extends ContentProvider implements SimpleContentResolverInterface {
private static final int KEY_RINGS_UNIFIED = 101;
private static final int KEY_RINGS_PUBLIC = 102;
private static final int KEY_RINGS_USER_IDS = 104;
private static final int KEY_RING_UNIFIED = 200;
@ -90,17 +89,12 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
*
* <pre>
* key_rings/unified
* key_rings/public
* key_rings/secret
* key_rings/user_ids
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ "/" + KeychainContract.PATH_UNIFIED,
KEY_RINGS_UNIFIED);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ "/" + KeychainContract.PATH_PUBLIC,
KEY_RINGS_PUBLIC);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ "/" + KeychainContract.PATH_USER_IDS,
KEY_RINGS_USER_IDS);
@ -133,12 +127,8 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
* key_rings/_/keys
* key_rings/_/user_ids
* key_rings/_/linked_ids
* key_rings/_/linked_ids/_
* key_rings/_/linked_ids/_/certs
* key_rings/_/public
* key_rings/_/secret
* key_rings/_/certs
* key_rings/_/certs/_/_
* </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
@ -190,9 +180,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
public String getType(@NonNull Uri uri) {
final int match = mUriMatcher.match(uri);
switch (match) {
case KEY_RING_PUBLIC:
return KeyRings.CONTENT_ITEM_TYPE;
case KEY_RING_KEYS:
return Keys.CONTENT_TYPE;
@ -537,24 +524,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
break;
}
case KEY_RINGS_PUBLIC:
case KEY_RING_PUBLIC: {
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_PUBLIC + ".oid AS _id");
projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
qb.setProjectionMap(projectionMap);
qb.setTables(Tables.KEY_RINGS_PUBLIC);
if(match == KEY_RING_PUBLIC) {
qb.appendWhere(KeyRings.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
}
break;
}
default: {
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
}

View File

@ -24,7 +24,6 @@ import java.util.ArrayList;
import android.annotation.TargetApi;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
@ -40,9 +39,8 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
@ -67,12 +65,14 @@ public class SafeSlingerActivity extends BaseActivity
private ArrayList<ParcelableKeyRing> mKeyList;
private HkpKeyserverAddress mKeyserver;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
private KeyRepository keyRepository;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
keyRepository = KeyRepository.create(this);
mMasterKeyId = getIntent().getLongExtra(EXTRA_MASTER_KEY_ID, 0);
NumberPicker picker = findViewById(R.id.safe_slinger_picker);
@ -104,10 +104,8 @@ public class SafeSlingerActivity extends BaseActivity
}
private void startExchange(long masterKeyId, int number) {
// retrieve public key blob and start SafeSlinger
Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(masterKeyId);
try {
byte[] keyBlob = KeyRepository.create(this).getCachedPublicKeyRing(uri).getEncoded();
byte[] keyBlob = keyRepository.loadPublicKeyRingData(masterKeyId);
Intent slingerIntent = new Intent(this, ExchangeActivity.class);
@ -115,8 +113,8 @@ public class SafeSlingerActivity extends BaseActivity
slingerIntent.putExtra(ExchangeConfig.extra.USER_DATA, keyBlob);
slingerIntent.putExtra(ExchangeConfig.extra.HOST_NAME, Constants.SAFESLINGER_SERVER);
startActivityForResult(slingerIntent, REQUEST_CODE_SAFE_SLINGER);
} catch (PgpKeyNotFoundException e) {
Timber.e(e, "personal key not found");
} catch (NotFoundException e) {
Timber.e(e, "key for transfer not found");
}
}

View File

@ -1,4 +1,9 @@
CREATE TABLE IF NOT EXISTS keyrings_public (
master_key_id INTEGER PRIMARY KEY,
key_ring_data BLOB
);
master_key_id INTEGER NOT NULL PRIMARY KEY,
key_ring_data BLOB NULL
);
selectByMasterKeyId:
SELECT *
FROM keyrings_public
WHERE master_key_id = ?;