From e144a402b5e0e53bb64c468a2d68f3605fd36f11 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 18 Jun 2018 23:03:09 +0200 Subject: [PATCH] extract autocrypt_peers from KeychainProvider into AutocryptPeerDao --- .../keychain/model/AutocryptPeer.java | 61 ++++ .../keychain/model/CustomColumnAdapters.java | 25 ++ .../provider/ApiDataAccessObject.java | 8 +- .../keychain/provider/AutocryptPeerDao.java | 162 +++++++++ .../AutocryptPeerDataAccessObject.java | 328 ------------------ .../provider/KeyWritableRepository.java | 16 +- .../keychain/provider/KeychainContract.java | 64 ---- .../keychain/provider/KeychainDatabase.java | 35 +- .../provider/KeychainExternalContract.java | 1 - .../keychain/provider/KeychainProvider.java | 131 +------ .../keychain/remote/AutocryptInteractor.java | 135 ++++++- .../remote/KeychainExternalProvider.java | 25 +- .../keychain/remote/OpenPgpService.java | 16 +- .../ui/dialog/RemoteDeduplicatePresenter.java | 13 +- .../ui/keyview/loader/IdentityDao.java | 60 ++-- .../presenter/IdentitiesPresenter.java | 8 +- .../keychain/AutocryptPeers.sq | 64 ++++ .../org/sufficientlysecure/keychain/Certs.sq | 13 + .../keychain/provider/InteropTest.java | 3 +- .../remote/KeychainExternalProviderTest.java | 28 +- 20 files changed, 546 insertions(+), 650 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/AutocryptPeer.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDao.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java create mode 100644 OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/AutocryptPeers.sq create mode 100644 OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Certs.sq diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/AutocryptPeer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/AutocryptPeer.java new file mode 100644 index 000000000..b6dcc32f8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/AutocryptPeer.java @@ -0,0 +1,61 @@ +package org.sufficientlysecure.keychain.model; + + +import com.google.auto.value.AutoValue; +import com.squareup.sqldelight.RowMapper; +import org.sufficientlysecure.keychain.AutocryptPeersModel; + + +@AutoValue +public abstract class AutocryptPeer implements AutocryptPeersModel { + + public enum GossipOrigin { + GOSSIP_HEADER, SIGNATURE, DEDUP + } + + public static final Factory FACTORY = new Factory(AutoValue_AutocryptPeer::new, + CustomColumnAdapters.DATE_ADAPTER, CustomColumnAdapters.DATE_ADAPTER, CustomColumnAdapters.DATE_ADAPTER, + CustomColumnAdapters.GOSSIP_ORIGIN_ADAPTER); + + public static final RowMapper PEER_MAPPER = FACTORY.selectByIdentifiersMapper(); + public static final RowMapper KEY_STATUS_MAPPER = + FACTORY.selectAutocryptKeyStatusMapper(AutoValue_AutocryptPeer_AutocryptKeyStatus::new); + + @AutoValue + public static abstract class AutocryptKeyStatus implements SelectAutocryptKeyStatusModel { + public boolean hasGossipKey() { + return autocryptPeer().gossip_master_key_id() != null; + } + + public boolean isGossipKeyRevoked() { + Long revokedInt = gossip_key_is_revoked_int(); + return revokedInt != null && revokedInt != 0; + } + + public boolean isGossipKeyExpired() { + return gossip_key_is_expired_int() != 0; + } + + public boolean isGossipKeyVerified() { + return gossip_key_is_verified_int() != 0; + } + + public boolean hasKey() { + return autocryptPeer().master_key_id() != null; + } + + public boolean isKeyRevoked() { + Long revokedInt = key_is_revoked_int(); + return revokedInt != null && revokedInt != 0; + } + + public boolean isKeyExpired() { + return key_is_expired_int() != 0; + } + + public boolean isKeyVerified() { + return key_is_verified_int() != 0; + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java index 2bc69aa1a..e6b9d0ee7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java @@ -6,6 +6,8 @@ import java.util.Date; import android.support.annotation.NonNull; import com.squareup.sqldelight.ColumnAdapter; +import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; + public final class CustomColumnAdapters { @@ -25,4 +27,27 @@ public final class CustomColumnAdapters { return value.getTime() / 1000; } }; + + static final ColumnAdapter GOSSIP_ORIGIN_ADAPTER = new ColumnAdapter() { + @NonNull + @Override + public GossipOrigin decode(Long databaseValue) { + switch (databaseValue.intValue()) { + case 0: return GossipOrigin.GOSSIP_HEADER; + case 10: return GossipOrigin.SIGNATURE; + case 20: return GossipOrigin.DEDUP; + default: throw new IllegalArgumentException("Unhandled database value!"); + } + } + + @Override + public Long encode(@NonNull GossipOrigin value) { + switch (value) { + case GOSSIP_HEADER: return 0L; + case SIGNATURE: return 10L; + case DEDUP: return 20L; + default: throw new IllegalArgumentException("Unhandled database value!"); + } + } + }; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java index 936519cf8..22f1b9cc8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java @@ -54,8 +54,12 @@ public class ApiDataAccessObject { } public byte[] getApiAppCertificate(String packageName) { - Cursor cursor = db.query(ApiApp.FACTORY.getCertificate(packageName)); - return ApiApp.FACTORY.getCertificateMapper().map(cursor); + try (Cursor cursor = db.query(ApiApp.FACTORY.getCertificate(packageName))) { + if (cursor.moveToFirst()) { + return ApiApp.FACTORY.getCertificateMapper().map(cursor); + } + return null; + } } public void insertApiApp(ApiApp apiApp) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDao.java new file mode 100644 index 000000000..bdf79f6a7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDao.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 Schürmann & Breitmoser GbR + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.provider; + + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import android.arch.persistence.db.SupportSQLiteDatabase; +import android.content.Context; +import android.database.Cursor; +import android.support.annotation.Nullable; + +import com.squareup.sqldelight.SqlDelightQuery; +import org.sufficientlysecure.keychain.AutocryptPeersModel.DeleteByIdentifier; +import org.sufficientlysecure.keychain.AutocryptPeersModel.DeleteByMasterKeyId; +import org.sufficientlysecure.keychain.AutocryptPeersModel.InsertPeer; +import org.sufficientlysecure.keychain.AutocryptPeersModel.UpdateGossipKey; +import org.sufficientlysecure.keychain.AutocryptPeersModel.UpdateKey; +import org.sufficientlysecure.keychain.AutocryptPeersModel.UpdateLastSeen; +import org.sufficientlysecure.keychain.model.AutocryptPeer; +import org.sufficientlysecure.keychain.model.AutocryptPeer.AutocryptKeyStatus; +import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; + + +public class AutocryptPeerDao { + private final SupportSQLiteDatabase db; + private final DatabaseNotifyManager databaseNotifyManager; + + public static AutocryptPeerDao getInstance(Context context) { + KeychainDatabase keychainDatabase = new KeychainDatabase(context); + DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context); + + return new AutocryptPeerDao(keychainDatabase.getWritableDatabase(), databaseNotifyManager); + } + + private AutocryptPeerDao(SupportSQLiteDatabase writableDatabase, DatabaseNotifyManager databaseNotifyManager) { + this.db = writableDatabase; + this.databaseNotifyManager = databaseNotifyManager; + } + + public Long getMasterKeyIdForAutocryptPeer(String autocryptId) { + SqlDelightQuery query = AutocryptPeer.FACTORY.selectMasterKeyIdByIdentifier(autocryptId); + try (Cursor cursor = db.query(query)) { + if (cursor.moveToFirst()) { + return AutocryptPeer.FACTORY.selectMasterKeyIdByIdentifierMapper().map(cursor); + } + } + return null; + } + + @Nullable + public AutocryptPeer getAutocryptPeer(String packageName, String autocryptId) { + List autocryptPeers = getAutocryptPeers(packageName, autocryptId); + if (!autocryptPeers.isEmpty()) { + return autocryptPeers.get(0); + } + return null; + } + + public List getAutocryptPeers(String packageName, String... autocryptId) { + ArrayList result = new ArrayList<>(autocryptId.length); + SqlDelightQuery query = AutocryptPeer.FACTORY.selectByIdentifiers(packageName, autocryptId); + try (Cursor cursor = db.query(query)) { + if (cursor.moveToNext()) { + AutocryptPeer autocryptPeer = AutocryptPeer.PEER_MAPPER.map(cursor); + result.add(autocryptPeer); + } + } + return result; + } + + public List getAutocryptKeyStatus(String packageName, String[] autocryptIds) { + ArrayList result = new ArrayList<>(autocryptIds.length); + SqlDelightQuery query = AutocryptPeer.FACTORY.selectAutocryptKeyStatus(packageName, autocryptIds, System.currentTimeMillis()); + try (Cursor cursor = db.query(query)) { + if (cursor.moveToNext()) { + AutocryptKeyStatus autocryptPeer = AutocryptPeer.KEY_STATUS_MAPPER.map(cursor); + result.add(autocryptPeer); + } + } + return result; + } + + public void insertOrUpdateLastSeen(String packageName, String autocryptId, Date date) { + UpdateLastSeen updateStatement = new UpdateLastSeen(db, AutocryptPeer.FACTORY); + updateStatement.bind(packageName, autocryptId, date); + int updated = updateStatement.executeUpdateDelete(); + + if (updated == 0) { + InsertPeer insertStatement = new InsertPeer(db, AutocryptPeer.FACTORY); + insertStatement.bind(packageName, autocryptId, date); + insertStatement.executeInsert(); + } + } + + public void updateKey(String packageName, String autocryptId, Date effectiveDate, long masterKeyId, + boolean isMutual) { + UpdateKey updateStatement = new UpdateKey(db, AutocryptPeer.FACTORY); + updateStatement.bind(packageName, autocryptId, effectiveDate, masterKeyId, isMutual); + int rowsUpdated = updateStatement.executeUpdateDelete(); + if (rowsUpdated == 0) { + throw new IllegalStateException("No rows updated! Was this peer inserted before the update?"); + } + databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId); + } + + public void updateKeyGossip(String packageName, String autocryptId, Date effectiveDate, long masterKeyId, + GossipOrigin origin) { + UpdateGossipKey updateStatement = new UpdateGossipKey(db, AutocryptPeer.FACTORY); + updateStatement.bind(packageName, autocryptId, effectiveDate, masterKeyId, origin); + int rowsUpdated = updateStatement.executeUpdateDelete(); + if (rowsUpdated == 0) { + throw new IllegalStateException("No rows updated! Was this peer inserted before the update?"); + } + databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId); + } + + public List getAutocryptPeersForKey(long masterKeyId) { + ArrayList result = new ArrayList<>(); + SqlDelightQuery query = AutocryptPeer.FACTORY.selectByMasterKeyId(masterKeyId); + try (Cursor cursor = db.query(query)) { + if (cursor.moveToNext()) { + AutocryptPeer autocryptPeer = AutocryptPeer.PEER_MAPPER.map(cursor); + result.add(autocryptPeer); + } + } + return result; + } + + public void deleteByIdentifier(String packageName, String autocryptId) { + Long masterKeyId = getMasterKeyIdForAutocryptPeer(autocryptId); + DeleteByIdentifier deleteStatement = new DeleteByIdentifier(db); + deleteStatement.bind(packageName, autocryptId); + deleteStatement.execute(); + if (masterKeyId != null) { + databaseNotifyManager.notifyAutocryptDelete(autocryptId, masterKeyId); + } + } + + public void deleteByMasterKeyId(long masterKeyId) { + DeleteByMasterKeyId deleteStatement = new DeleteByMasterKeyId(db); + deleteStatement.bind(masterKeyId); + deleteStatement.execute(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java deleted file mode 100644 index d7d4707dd..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.provider; - - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.support.annotation.Nullable; -import android.text.format.DateUtils; - -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer; - - -public class AutocryptPeerDataAccessObject { - private static final long AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS = 35 * DateUtils.DAY_IN_MILLIS; - - private static final String[] PROJECTION_LAST_SEEN = { ApiAutocryptPeer.LAST_SEEN }; - private static final String[] PROJECTION_LAST_SEEN_KEY = { ApiAutocryptPeer.LAST_SEEN_KEY }; - private static final String[] PROJECTION_GOSSIP_LAST_SEEN_KEY = { ApiAutocryptPeer.GOSSIP_LAST_SEEN_KEY }; - private static final String[] PROJECTION_MASTER_KEY_ID = { ApiAutocryptPeer.MASTER_KEY_ID }; - - private static final String[] PROJECTION_AUTOCRYPT_QUERY = { - ApiAutocryptPeer.IDENTIFIER, - ApiAutocryptPeer.LAST_SEEN, - ApiAutocryptPeer.MASTER_KEY_ID, - ApiAutocryptPeer.LAST_SEEN_KEY, - ApiAutocryptPeer.IS_MUTUAL, - ApiAutocryptPeer.KEY_IS_REVOKED, - ApiAutocryptPeer.KEY_IS_EXPIRED, - ApiAutocryptPeer.KEY_IS_VERIFIED, - ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID, - ApiAutocryptPeer.GOSSIP_LAST_SEEN_KEY, - ApiAutocryptPeer.GOSSIP_KEY_IS_REVOKED, - ApiAutocryptPeer.GOSSIP_KEY_IS_EXPIRED, - ApiAutocryptPeer.GOSSIP_KEY_IS_VERIFIED, - }; - private static final int INDEX_IDENTIFIER = 0; - private static final int INDEX_LAST_SEEN = 1; - private static final int INDEX_MASTER_KEY_ID = 2; - private static final int INDEX_LAST_SEEN_KEY = 3; - private static final int INDEX_STATE = 4; - private static final int INDEX_KEY_IS_REVOKED = 5; - private static final int INDEX_KEY_IS_EXPIRED = 6; - private static final int INDEX_KEY_IS_VERIFIED = 7; - private static final int INDEX_GOSSIP_MASTER_KEY_ID = 8; - private static final int INDEX_GOSSIP_LAST_SEEN_KEY = 9; - private static final int INDEX_GOSSIP_KEY_IS_REVOKED = 10; - private static final int INDEX_GOSSIP_KEY_IS_EXPIRED = 11; - private static final int INDEX_GOSSIP_KEY_IS_VERIFIED = 12; - - private final SimpleContentResolverInterface queryInterface; - private final String packageName; - private final DatabaseNotifyManager databaseNotifyManager; - - public AutocryptPeerDataAccessObject(Context context, String packageName) { - this.packageName = packageName; - this.databaseNotifyManager = DatabaseNotifyManager.create(context); - - final ContentResolver contentResolver = context.getContentResolver(); - queryInterface = new SimpleContentResolverInterface() { - @Override - public Cursor query(Uri contentUri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder); - } - - @Override - public Uri insert(Uri contentUri, ContentValues values) { - return contentResolver.insert(contentUri, values); - } - - @Override - public int update(Uri contentUri, ContentValues values, String where, String[] selectionArgs) { - return contentResolver.update(contentUri, values, where, selectionArgs); - } - - @Override - public int delete(Uri contentUri, String where, String[] selectionArgs) { - return contentResolver.delete(contentUri, where, selectionArgs); - } - }; - } - - public AutocryptPeerDataAccessObject(SimpleContentResolverInterface queryInterface, String packageName, - DatabaseNotifyManager databaseNotifyManager) { - this.queryInterface = queryInterface; - this.packageName = packageName; - this.databaseNotifyManager = databaseNotifyManager; - } - - public Long getMasterKeyIdForAutocryptPeer(String autocryptId) { - Cursor cursor = queryInterface.query( - ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), - PROJECTION_MASTER_KEY_ID, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - return cursor.getLong(0); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return null; - } - - public Date getLastSeen(String autocryptId) { - Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), - PROJECTION_LAST_SEEN, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - long lastUpdated = cursor.getLong(0); - return new Date(lastUpdated); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - public Date getLastSeenKey(String autocryptId) { - Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), - PROJECTION_LAST_SEEN_KEY, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - long lastUpdated = cursor.getLong(0); - return new Date(lastUpdated); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - public Date getLastSeenGossip(String autocryptId) { - Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), - PROJECTION_GOSSIP_LAST_SEEN_KEY, null, null, null); - - try { - if (cursor != null && cursor.moveToFirst()) { - long lastUpdated = cursor.getLong(0); - return new Date(lastUpdated); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - public void updateLastSeen(String autocryptId, Date date) { - ContentValues cv = new ContentValues(); - cv.put(ApiAutocryptPeer.LAST_SEEN, date.getTime()); - queryInterface - .update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); - } - - public void updateKey(String autocryptId, Date effectiveDate, long masterKeyId, boolean isMutual) { - ContentValues cv = new ContentValues(); - cv.put(ApiAutocryptPeer.MASTER_KEY_ID, masterKeyId); - cv.put(ApiAutocryptPeer.LAST_SEEN_KEY, effectiveDate.getTime()); - cv.put(ApiAutocryptPeer.IS_MUTUAL, isMutual ? 1 : 0); - queryInterface - .update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); - databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId); - } - - public void updateKeyGossipFromAutocrypt(String autocryptId, Date effectiveDate, long masterKeyId) { - updateKeyGossip(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.GOSSIP_ORIGIN_AUTOCRYPT); - } - - public void updateKeyGossipFromSignature(String autocryptId, Date effectiveDate, long masterKeyId) { - updateKeyGossip(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.GOSSIP_ORIGIN_SIGNATURE); - } - - public void updateKeyGossipFromDedup(String autocryptId, Date effectiveDate, long masterKeyId) { - updateKeyGossip(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.GOSSIP_ORIGIN_DEDUP); - } - - private void updateKeyGossip(String autocryptId, Date effectiveDate, long masterKeyId, int origin) { - ContentValues cv = new ContentValues(); - cv.put(ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID, masterKeyId); - cv.put(ApiAutocryptPeer.GOSSIP_LAST_SEEN_KEY, effectiveDate.getTime()); - cv.put(ApiAutocryptPeer.GOSSIP_ORIGIN, origin); - queryInterface - .update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); - databaseNotifyManager.notifyAutocryptUpdate(autocryptId, masterKeyId); - } - - public void delete(String autocryptId) { - Long masterKeyId = getMasterKeyIdForAutocryptPeer(autocryptId); - queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null); - databaseNotifyManager.notifyAutocryptDelete(autocryptId, masterKeyId); - } - - public List determineAutocryptRecommendations(String... autocryptIds) { - List result = new ArrayList<>(autocryptIds.length); - - Cursor cursor = queryAutocryptPeerData(autocryptIds); - try { - while (cursor.moveToNext()) { - AutocryptRecommendationResult peerResult = determineAutocryptRecommendation(cursor); - result.add(peerResult); - } - } finally { - cursor.close(); - } - - return result; - } - - /** Determines Autocrypt "ui-recommendation", according to spec. - * See https://autocrypt.org/level1.html#recommendations-for-single-recipient-messages - */ - private AutocryptRecommendationResult determineAutocryptRecommendation(Cursor cursor) { - String peerId = cursor.getString(INDEX_IDENTIFIER); - - AutocryptRecommendationResult keyRecommendation = determineAutocryptKeyRecommendation(peerId, cursor); - if (keyRecommendation != null) return keyRecommendation; - - AutocryptRecommendationResult gossipRecommendation = determineAutocryptGossipRecommendation(peerId, cursor); - if (gossipRecommendation != null) return gossipRecommendation; - - return new AutocryptRecommendationResult(peerId, AutocryptState.DISABLE, null, false); - } - - @Nullable - private AutocryptRecommendationResult determineAutocryptKeyRecommendation(String peerId, Cursor cursor) { - boolean hasKey = !cursor.isNull(INDEX_MASTER_KEY_ID); - boolean isRevoked = cursor.getInt(INDEX_KEY_IS_REVOKED) != 0; - boolean isExpired = cursor.getInt(INDEX_KEY_IS_EXPIRED) != 0; - if (!hasKey || isRevoked || isExpired) { - return null; - } - - long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - long lastSeen = cursor.getLong(INDEX_LAST_SEEN); - long lastSeenKey = cursor.getLong(INDEX_LAST_SEEN_KEY); - boolean isVerified = cursor.getInt(INDEX_KEY_IS_VERIFIED) != 0; - if (lastSeenKey < (lastSeen - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) { - return new AutocryptRecommendationResult(peerId, AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified); - } - - boolean isMutual = cursor.getInt(INDEX_STATE) != 0; - if (isMutual) { - return new AutocryptRecommendationResult(peerId, AutocryptState.MUTUAL, masterKeyId, isVerified); - } else { - return new AutocryptRecommendationResult(peerId, AutocryptState.AVAILABLE, masterKeyId, isVerified); - } - } - - @Nullable - private AutocryptRecommendationResult determineAutocryptGossipRecommendation(String peerId, Cursor cursor) { - boolean gossipHasKey = !cursor.isNull(INDEX_GOSSIP_MASTER_KEY_ID); - boolean gossipIsRevoked = cursor.getInt(INDEX_GOSSIP_KEY_IS_REVOKED) != 0; - boolean gossipIsExpired = cursor.getInt(INDEX_GOSSIP_KEY_IS_EXPIRED) != 0; - boolean isVerified = cursor.getInt(INDEX_GOSSIP_KEY_IS_VERIFIED) != 0; - - if (!gossipHasKey || gossipIsRevoked || gossipIsExpired) { - return null; - } - - long masterKeyId = cursor.getLong(INDEX_GOSSIP_MASTER_KEY_ID); - return new AutocryptRecommendationResult(peerId, AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified); - } - - private Cursor queryAutocryptPeerData(String[] autocryptIds) { - StringBuilder selection = new StringBuilder(ApiAutocryptPeer.IDENTIFIER + " IN (?"); - for (int i = 1; i < autocryptIds.length; i++) { - selection.append(",?"); - } - selection.append(")"); - - return queryInterface.query(ApiAutocryptPeer.buildByPackageName(packageName), - PROJECTION_AUTOCRYPT_QUERY, selection.toString(), autocryptIds, null); - } - - public static class AutocryptRecommendationResult { - public final String peerId; - public final Long masterKeyId; - public final AutocryptState autocryptState; - public final boolean isVerified; - - AutocryptRecommendationResult(String peerId, AutocryptState autocryptState, Long masterKeyId, - boolean isVerified) { - this.peerId = peerId; - this.autocryptState = autocryptState; - this.masterKeyId = masterKeyId; - this.isVerified = isVerified; - } - - } - - public enum AutocryptState { - DISABLE, DISCOURAGED_OLD, DISCOURAGED_GOSSIP, AVAILABLE, MUTUAL - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index d2e1af3e4..a4eed8d49 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -55,7 +55,6 @@ import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; import org.sufficientlysecure.keychain.pgp.WrappedSignature; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -84,29 +83,34 @@ public class KeyWritableRepository extends KeyRepository { private final Context context; private final DatabaseNotifyManager databaseNotifyManager; + private AutocryptPeerDao autocryptPeerDao; public static KeyWritableRepository create(Context context) { LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context); LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(context); DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context); - return new KeyWritableRepository(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager); + AutocryptPeerDao autocryptPeerDao = AutocryptPeerDao.getInstance(context); + return new KeyWritableRepository(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, + autocryptPeerDao); } @VisibleForTesting KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, LocalSecretKeyStorage localSecretKeyStorage, - DatabaseNotifyManager databaseNotifyManager) { - this(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, new OperationLog(), 0); + DatabaseNotifyManager databaseNotifyManager, AutocryptPeerDao autocryptPeerDao) { + this(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, new OperationLog(), 0, + autocryptPeerDao); } private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, LocalSecretKeyStorage localSecretKeyStorage, DatabaseNotifyManager databaseNotifyManager, - OperationLog log, int indent) { + OperationLog log, int indent, AutocryptPeerDao autocryptPeerDao) { super(context.getContentResolver(), localPublicKeyStorage, localSecretKeyStorage, log, indent); this.context = context; this.databaseNotifyManager = databaseNotifyManager; + this.autocryptPeerDao = autocryptPeerDao; } private LongSparseArray getTrustedMasterKeys() { @@ -585,7 +589,7 @@ public class KeyWritableRepository extends KeyRepository { Timber.e(e, "Could not delete file!"); return false; } - contentResolver.delete(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId),null, null); + autocryptPeerDao.deleteByMasterKeyId(masterKeyId); int deletedRows = contentResolver.delete(KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null); databaseNotifyManager.notifyKeyChange(masterKeyId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index a44da1370..e41e8ed39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -51,13 +51,6 @@ public class KeychainContract { String EXPIRY = "expiry"; } - interface UpdatedKeysColumns { - String MASTER_KEY_ID = "master_key_id"; // not a database id - String LAST_UPDATED = "last_updated"; // time since epoch in seconds - String SEEN_ON_KEYSERVERS = "seen_on_keyservers"; - String FINGERPRINT = "fingerprint"; - } - interface KeySignaturesColumns { String MASTER_KEY_ID = "master_key_id"; // not a database id String SIGNER_KEY_ID = "signer_key_id"; @@ -86,11 +79,6 @@ public class KeychainContract { String DATA = "data"; } - interface ApiAppsColumns { - String PACKAGE_NAME = "package_name"; - String PACKAGE_CERTIFICATE = "package_signature"; - } - interface ApiAppsAllowedKeysColumns { String KEY_ID = "key_id"; // not a database id String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name @@ -100,20 +88,6 @@ public class KeychainContract { String IDENTIFIER = "identifier"; } - interface ApiAutocryptPeerColumns { - String PACKAGE_NAME = "package_name"; - String IDENTIFIER = "identifier"; - String LAST_SEEN = "last_seen"; - - String MASTER_KEY_ID = "master_key_id"; - String LAST_SEEN_KEY = "last_seen_key"; - String IS_MUTUAL = "is_mutual"; - - String GOSSIP_MASTER_KEY_ID = "gossip_master_key_id"; - String GOSSIP_LAST_SEEN_KEY = "gossip_last_seen_key"; - String GOSSIP_ORIGIN = "gossip_origin"; - } - public static final String CONTENT_AUTHORITY = Constants.PROVIDER_AUTHORITY; private static final Uri BASE_CONTENT_URI_INTERNAL = Uri @@ -121,8 +95,6 @@ public class KeychainContract { public static final String BASE_KEY_RINGS = "key_rings"; - public static final String BASE_UPDATED_KEYS = "updated_keys"; - public static final String BASE_KEY_SIGNATURES = "key_signatures"; public static final String PATH_UNIFIED = "unified"; @@ -141,11 +113,6 @@ public class KeychainContract { public static final String PATH_KEYS = "keys"; public static final String PATH_CERTS = "certs"; - public static final String PATH_BY_PACKAGE_NAME = "by_package_name"; - public static final String PATH_BY_KEY_ID = "by_key_id"; - - public static final String BASE_AUTOCRYPT_PEERS = "autocrypt_peers"; - public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns { public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; public static final String IS_REVOKED = KeysColumns.IS_REVOKED; @@ -310,37 +277,6 @@ public class KeychainContract { } - public static class ApiAutocryptPeer implements ApiAutocryptPeerColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_AUTOCRYPT_PEERS).build(); - public static final String KEY_IS_REVOKED = "key_is_revoked"; - public static final String KEY_IS_EXPIRED = "key_is_expired"; - public static final String KEY_IS_VERIFIED = "key_is_verified"; - public static final String GOSSIP_KEY_IS_REVOKED = "gossip_key_is_revoked"; - public static final String GOSSIP_KEY_IS_EXPIRED = "gossip_key_is_expired"; - public static final String GOSSIP_KEY_IS_VERIFIED = "gossip_key_is_verified"; - - public static final int GOSSIP_ORIGIN_AUTOCRYPT = 0; - public static final int GOSSIP_ORIGIN_SIGNATURE = 10; - public static final int GOSSIP_ORIGIN_DEDUP = 20; - - public static Uri buildByKeyUri(Uri uri) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(uri.getPathSegments().get(1)).build(); - } - - public static Uri buildByPackageName(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).build(); - } - - public static Uri buildByPackageNameAndAutocryptId(String packageName, String autocryptPeer) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).appendPath(autocryptPeer).build(); - } - - public static Uri buildByMasterKeyId(long masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(Long.toString(masterKeyId)).build(); - } - } - public static class Certs implements CertsColumns, BaseColumns { public static final String USER_ID = UserPacketsColumns.USER_ID; public static final String NAME = UserPacketsColumns.NAME; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 81861e9e8..cd925c115 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -30,21 +30,21 @@ import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration; import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory; import android.content.Context; import android.database.Cursor; +import android.database.SQLException; import android.database.sqlite.SQLiteException; import android.provider.BaseColumns; +import org.sufficientlysecure.keychain.AutocryptPeersModel; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.KeyMetadataModel; import org.sufficientlysecure.keychain.KeyRingsPublicModel; import org.sufficientlysecure.keychain.model.ApiApp; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAllowedKeysColumns; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeerColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignaturesColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.util.Preferences; import timber.log.Timber; @@ -60,20 +60,18 @@ import timber.log.Timber; */ public class KeychainDatabase { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 27; + private static final int DATABASE_VERSION = 28; private final SupportSQLiteOpenHelper supportSQLiteOpenHelper; private Context context; public interface Tables { String KEY_RINGS_PUBLIC = "keyrings_public"; String KEYS = "keys"; - String UPDATED_KEYS = "updated_keys"; String KEY_SIGNATURES = "key_signatures"; String USER_PACKETS = "user_packets"; String CERTS = "certs"; String API_ALLOWED_KEYS = "api_allowed_keys"; String OVERRIDDEN_WARNINGS = "overridden_warnings"; - String API_AUTOCRYPT_PEERS = "api_autocrypt_peers"; } private static final String CREATE_KEYS = @@ -151,23 +149,6 @@ public class KeychainDatabase { + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; - private static final String CREATE_API_AUTOCRYPT_PEERS = - "CREATE TABLE IF NOT EXISTS " + Tables.API_AUTOCRYPT_PEERS + " (" - + ApiAutocryptPeerColumns.PACKAGE_NAME + " TEXT NOT NULL, " - + ApiAutocryptPeerColumns.IDENTIFIER + " TEXT NOT NULL, " - + ApiAutocryptPeerColumns.LAST_SEEN + " INTEGER, " - + ApiAutocryptPeerColumns.LAST_SEEN_KEY + " INTEGER NULL, " - + ApiAutocryptPeerColumns.IS_MUTUAL + " INTEGER NULL, " - + ApiAutocryptPeerColumns.MASTER_KEY_ID + " INTEGER NULL, " - + ApiAutocryptPeerColumns.GOSSIP_MASTER_KEY_ID + " INTEGER NULL, " - + ApiAutocryptPeerColumns.GOSSIP_LAST_SEEN_KEY + " INTEGER NULL, " - + ApiAutocryptPeerColumns.GOSSIP_ORIGIN + " INTEGER NULL, " - + "PRIMARY KEY(" + ApiAutocryptPeerColumns.PACKAGE_NAME + ", " - + ApiAutocryptPeerColumns.IDENTIFIER + "), " - + "FOREIGN KEY(" + ApiAutocryptPeerColumns.PACKAGE_NAME + ") REFERENCES " - + "api_apps (package_signature) ON DELETE CASCADE" - + ")"; - private static final String CREATE_API_APPS_ALLOWED_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ALLOWED_KEYS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -237,7 +218,7 @@ public class KeychainDatabase { db.execSQL(CREATE_KEY_SIGNATURES); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); db.execSQL(CREATE_OVERRIDDEN_WARNINGS); - db.execSQL(CREATE_API_AUTOCRYPT_PEERS); + db.execSQL(AutocryptPeersModel.CREATE_TABLE); db.execSQL(ApiApp.CREATE_TABLE); db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");"); @@ -466,6 +447,10 @@ public class KeychainDatabase { case 26: { migrateUpdatedKeysToKeyMetadataTable(db); } + + case 27: { + renameApiAutocryptPeersTable(db); + } } } @@ -488,6 +473,10 @@ public class KeychainDatabase { db.execSQL("UPDATE key_metadata SET last_updated = last_updated * 1000;"); } + private void renameApiAutocryptPeersTable(SupportSQLiteDatabase db) { + db.execSQL("ALTER TABLE api_autocrypt_peers RENAME TO autocrypt_peers;"); + } + public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { // Downgrade is ok for the debug version, makes it easier to work with branches if (Constants.DEBUG) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java index e19332d92..4802b667e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java @@ -22,7 +22,6 @@ import android.net.Uri; import android.provider.BaseColumns; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer; public class KeychainExternalContract { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 805dd8036..6a02f5702 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -38,8 +38,8 @@ import android.support.annotation.NonNull; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.model.AutocryptPeer; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -74,10 +74,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe private static final int KEY_RINGS_FIND_BY_USER_ID = 402; private static final int KEY_RINGS_FILTER_BY_SIGNER = 403; - private static final int AUTOCRYPT_PEERS_BY_MASTER_KEY_ID = 601; - private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME = 602; - private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID = 603; - private static final int KEY_SIGNATURES = 700; protected UriMatcher mUriMatcher; @@ -173,21 +169,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe + KeychainContract.PATH_CERTS + "/*/*", KEY_RING_CERTS_SPECIFIC); - /* - * Trust Identity access - * - *
-         * trust_ids/by_key_id/_
-         *
-         * 
- */ - matcher.addURI(authority, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" + - KeychainContract.PATH_BY_KEY_ID + "/*", AUTOCRYPT_PEERS_BY_MASTER_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" + - KeychainContract.PATH_BY_PACKAGE_NAME + "/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME); - matcher.addURI(authority, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" + - KeychainContract.PATH_BY_PACKAGE_NAME + "/*/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_SIGNATURES, KEY_SIGNATURES); @@ -309,7 +290,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY + " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED); projectionMap.put(KeyRings.API_KNOWN_TO_PACKAGE_NAMES, - "GROUP_CONCAT(DISTINCT aTI." + ApiAutocryptPeer.PACKAGE_NAME + ") AS " + "GROUP_CONCAT(DISTINCT aTI." + AutocryptPeer.PACKAGE_NAME + ") AS " + KeyRings.API_KNOWN_TO_PACKAGE_NAMES); qb.setProjectionMap(projectionMap); @@ -390,8 +371,8 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe + " >= " + new Date().getTime() / 1000 + " )" + ")" : "") + (plist.contains(KeyRings.API_KNOWN_TO_PACKAGE_NAMES) ? - " LEFT JOIN " + Tables.API_AUTOCRYPT_PEERS + " AS aTI ON (" - +"aTI." + Keys.MASTER_KEY_ID + " LEFT JOIN " + AutocryptPeer.TABLE_NAME + " AS aTI ON (" + +"aTI." + AutocryptPeer.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + ")" : "") ); @@ -639,76 +620,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe break; } - case AUTOCRYPT_PEERS_BY_MASTER_KEY_ID: - case AUTOCRYPT_PEERS_BY_PACKAGE_NAME: - case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: { - long unixSeconds = new Date().getTime() / 1000; - - HashMap projectionMap = new HashMap<>(); - projectionMap.put(ApiAutocryptPeer._ID, Tables.API_AUTOCRYPT_PEERS + ".oid AS " + ApiAutocryptPeer._ID); - projectionMap.put(ApiAutocryptPeer.IDENTIFIER, ApiAutocryptPeer.IDENTIFIER); - projectionMap.put(ApiAutocryptPeer.PACKAGE_NAME, ApiAutocryptPeer.PACKAGE_NAME); - projectionMap.put(ApiAutocryptPeer.LAST_SEEN, ApiAutocryptPeer.LAST_SEEN); - projectionMap.put(ApiAutocryptPeer.MASTER_KEY_ID, Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID); - projectionMap.put(ApiAutocryptPeer.IS_MUTUAL, ApiAutocryptPeer.IS_MUTUAL); - projectionMap.put(ApiAutocryptPeer.LAST_SEEN_KEY, ApiAutocryptPeer.LAST_SEEN_KEY); - projectionMap.put(ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID, ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID); - projectionMap.put(ApiAutocryptPeer.GOSSIP_LAST_SEEN_KEY, ApiAutocryptPeer.GOSSIP_LAST_SEEN_KEY); - projectionMap.put(ApiAutocryptPeer.GOSSIP_ORIGIN, ApiAutocryptPeer.GOSSIP_ORIGIN); - projectionMap.put(ApiAutocryptPeer.KEY_IS_REVOKED, "ac_key." + Keys.IS_REVOKED + " AS " + ApiAutocryptPeer.KEY_IS_REVOKED); - projectionMap.put(ApiAutocryptPeer.KEY_IS_EXPIRED, "(CASE" + - " WHEN ac_key." + Keys.EXPIRY + " IS NULL THEN 0" + - " WHEN ac_key." + Keys.EXPIRY + " > " + unixSeconds + " THEN 0" + - " ELSE 1 END) AS " + ApiAutocryptPeer.KEY_IS_EXPIRED); - projectionMap.put(ApiAutocryptPeer.KEY_IS_VERIFIED, "EXISTS (SELECT * FROM " + Tables.CERTS + - " WHERE " + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + - Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + - " AND " + Certs.VERIFIED + " = " + Certs.VERIFIED_SECRET + - ") AS " + ApiAutocryptPeer.KEY_IS_VERIFIED); - projectionMap.put(ApiAutocryptPeer.GOSSIP_KEY_IS_REVOKED, - "gossip_key." + Keys.IS_REVOKED + " AS " + ApiAutocryptPeer.GOSSIP_KEY_IS_REVOKED); - projectionMap.put(ApiAutocryptPeer.GOSSIP_KEY_IS_EXPIRED, "(CASE" + - " WHEN gossip_key." + Keys.EXPIRY + " IS NULL THEN 0" + - " WHEN gossip_key." + Keys.EXPIRY + " > " + unixSeconds + " THEN 0" + - " ELSE 1 END) AS " + ApiAutocryptPeer.GOSSIP_KEY_IS_EXPIRED); - projectionMap.put(ApiAutocryptPeer.GOSSIP_KEY_IS_VERIFIED, "EXISTS (SELECT * FROM " + Tables.CERTS + - " WHERE " + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + - Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID + - " AND " + Certs.VERIFIED + " = " + Certs.VERIFIED_SECRET + - ") AS " + ApiAutocryptPeer.GOSSIP_KEY_IS_VERIFIED); - qb.setProjectionMap(projectionMap); - - qb.setTables(Tables.API_AUTOCRYPT_PEERS + - " LEFT JOIN " + Tables.KEYS + " AS ac_key" + - " ON (ac_key." + Keys.MASTER_KEY_ID + " = " + Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + - " AND ac_key." + Keys.RANK + " = 0)" + - " LEFT JOIN " + Tables.KEYS + " AS gossip_key" + - " ON (gossip_key." + Keys.MASTER_KEY_ID + " = " + ApiAutocryptPeer.GOSSIP_MASTER_KEY_ID + - " AND gossip_key." + Keys.RANK + " = 0)" - ); - - if (match == AUTOCRYPT_PEERS_BY_MASTER_KEY_ID) { - long masterKeyId = Long.parseLong(uri.getLastPathSegment()); - - qb.appendWhere(Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + " = "); - qb.appendWhere(Long.toString(masterKeyId)); - } else if (match == AUTOCRYPT_PEERS_BY_PACKAGE_NAME) { - String packageName = uri.getPathSegments().get(2); - - qb.appendWhere(Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(packageName); - } else { // AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID - String packageName = uri.getPathSegments().get(2); - String autocryptPeer = uri.getPathSegments().get(3); - - selection = Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = ? AND " + - Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.IDENTIFIER + " = ?"; - selectionArgs = new String[] { packageName, autocryptPeer }; - } - - break; - } - default: { throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); } @@ -846,25 +757,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe break; } - case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: { - String packageName = uri.getPathSegments().get(2); - String autocryptPeer = uri.getPathSegments().get(3); - - String selection = ApiAutocryptPeer.PACKAGE_NAME + " = ? AND " + ApiAutocryptPeer.IDENTIFIER + " = ?"; - selectionArgs = new String[] { packageName, autocryptPeer }; - - count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs); - break; - } - - case AUTOCRYPT_PEERS_BY_MASTER_KEY_ID: - String selection = ApiAutocryptPeer.MASTER_KEY_ID + " = " + uri.getLastPathSegment(); - if (!TextUtils.isEmpty(additionalSelection)) { - selection += " AND (" + additionalSelection + ")"; - } - count = db.delete(Tables.API_AUTOCRYPT_PEERS, selection, selectionArgs); - break; - default: { throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -900,21 +792,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe count = db.update(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values, actualSelection, selectionArgs); break; } - case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: { - String packageName = uri.getPathSegments().get(2); - String identifier = uri.getLastPathSegment(); - values.put(ApiAutocryptPeer.PACKAGE_NAME, packageName); - values.put(ApiAutocryptPeer.IDENTIFIER, identifier); - - int updated = db.update(Tables.API_AUTOCRYPT_PEERS, SQLiteDatabase.CONFLICT_IGNORE, values, - ApiAutocryptPeer.PACKAGE_NAME + "=? AND " + ApiAutocryptPeer.IDENTIFIER + "=?", - new String[] { packageName, identifier }); - if (updated == 0) { - db.insert(Tables.API_AUTOCRYPT_PEERS, SQLiteDatabase.CONFLICT_FAIL,values); - } - - break; - } default: { throw new UnsupportedOperationException("Unknown uri: " + uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java index c981f5df5..d280f6a11 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java @@ -2,52 +2,64 @@ package org.sufficientlysecure.keychain.remote; import java.io.IOException; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import android.content.Context; import android.support.annotation.Nullable; +import android.text.format.DateUtils; import org.openintents.openpgp.AutocryptPeerUpdate; import org.openintents.openpgp.AutocryptPeerUpdate.PreferEncrypt; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.model.AutocryptPeer; +import org.sufficientlysecure.keychain.model.AutocryptPeer.AutocryptKeyStatus; +import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import timber.log.Timber; -class AutocryptInteractor { +public class AutocryptInteractor { + private static final long AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS = 35 * DateUtils.DAY_IN_MILLIS; - private AutocryptPeerDataAccessObject autocryptPeerDao; + private AutocryptPeerDao autocryptPeerDao; private KeyWritableRepository keyWritableRepository; - public static AutocryptInteractor getInstance(Context context, AutocryptPeerDataAccessObject autocryptPeerentityDao) { + private final String packageName; + + public static AutocryptInteractor getInstance(Context context, String packageName) { + AutocryptPeerDao autocryptPeerDao = AutocryptPeerDao.getInstance(context); KeyWritableRepository keyWritableRepository = KeyWritableRepository.create(context); - return new AutocryptInteractor(autocryptPeerentityDao, keyWritableRepository); + return new AutocryptInteractor(autocryptPeerDao, keyWritableRepository, packageName); } - private AutocryptInteractor(AutocryptPeerDataAccessObject autocryptPeerDao, - KeyWritableRepository keyWritableRepository) { + private AutocryptInteractor(AutocryptPeerDao autocryptPeerDao, + KeyWritableRepository keyWritableRepository, String packageName) { this.autocryptPeerDao = autocryptPeerDao; this.keyWritableRepository = keyWritableRepository; + this.packageName = packageName; } void updateAutocryptPeerState(String autocryptPeerId, AutocryptPeerUpdate autocryptPeerUpdate) { + AutocryptPeer currentAutocryptPeer = autocryptPeerDao.getAutocryptPeer(packageName, autocryptPeerId); Date effectiveDate = autocryptPeerUpdate.getEffectiveDate(); // 1. If the message’s effective date is older than the peers[from-addr].autocrypt_timestamp value, then no changes are required, and the update process terminates. - Date lastSeenAutocrypt = autocryptPeerDao.getLastSeenKey(autocryptPeerId); - if (lastSeenAutocrypt != null && effectiveDate.compareTo(lastSeenAutocrypt) <= 0) { + Date lastSeenKey = currentAutocryptPeer != null ? currentAutocryptPeer.last_seen_key() : null; + if (lastSeenKey != null && effectiveDate.compareTo(lastSeenKey) <= 0) { return; } // 2. If the message’s effective date is more recent than peers[from-addr].last_seen then set peers[from-addr].last_seen to the message’s effective date. - Date lastSeen = autocryptPeerDao.getLastSeen(autocryptPeerId); + Date lastSeen = currentAutocryptPeer != null ? currentAutocryptPeer.last_seen() : null; if (lastSeen == null || effectiveDate.after(lastSeen)) { - autocryptPeerDao.updateLastSeen(autocryptPeerId, effectiveDate); + autocryptPeerDao.insertOrUpdateLastSeen(packageName, autocryptPeerId, effectiveDate); } // 3. If the Autocrypt header is unavailable, no further changes are required and the update process terminates. @@ -66,17 +78,18 @@ class AutocryptInteractor { // 6. Set peers[from-addr].prefer_encrypt to the corresponding prefer-encrypt value of the Autocrypt header. boolean isMutual = autocryptPeerUpdate.getPreferEncrypt() == PreferEncrypt.MUTUAL; - autocryptPeerDao.updateKey(autocryptPeerId, effectiveDate, newMasterKeyId, isMutual); + autocryptPeerDao.updateKey(packageName, autocryptPeerId, effectiveDate, newMasterKeyId, isMutual); } void updateAutocryptPeerGossipState(String autocryptPeerId, AutocryptPeerUpdate autocryptPeerUpdate) { + AutocryptPeer currentAutocryptPeer = autocryptPeerDao.getAutocryptPeer(packageName, autocryptPeerId); Date effectiveDate = autocryptPeerUpdate.getEffectiveDate(); // 1. If gossip-addr does not match any recipient in the mail’s To or Cc header, the update process terminates (i.e., header is ignored). // -> This should be taken care of in the mail client that sends us this data! // 2. If peers[gossip-addr].gossip_timestamp is more recent than the message’s effective date, then the update process terminates. - Date lastSeenGossip = autocryptPeerDao.getLastSeenGossip(autocryptPeerId); + Date lastSeenGossip = currentAutocryptPeer.gossip_last_seen_key(); if (lastSeenGossip != null && lastSeenGossip.after(effectiveDate)) { return; } @@ -94,7 +107,8 @@ class AutocryptInteractor { // 4. Set peers[gossip-addr].gossip_key to the value of the keydata attribute. Long newMasterKeyId = saveKeyringResult.savedMasterKeyId; - autocryptPeerDao.updateKeyGossipFromAutocrypt(autocryptPeerId, effectiveDate, newMasterKeyId); + autocryptPeerDao.updateKeyGossip(packageName, autocryptPeerId, effectiveDate, newMasterKeyId, + GossipOrigin.GOSSIP_HEADER); } @Nullable @@ -131,4 +145,97 @@ class AutocryptInteractor { } return uncachedKeyRing; } + + public List determineAutocryptRecommendations(String... autocryptIds) { + List result = new ArrayList<>(autocryptIds.length); + + for (AutocryptKeyStatus autocryptKeyStatus : autocryptPeerDao.getAutocryptKeyStatus(packageName, autocryptIds)) { + AutocryptRecommendationResult peerResult = determineAutocryptRecommendation(autocryptKeyStatus); + result.add(peerResult); + } + + return result; + } + + /** Determines Autocrypt "ui-recommendation", according to spec. + * See https://autocrypt.org/level1.html#recommendations-for-single-recipient-messages + */ + private AutocryptRecommendationResult determineAutocryptRecommendation(AutocryptKeyStatus autocryptKeyStatus) { + AutocryptRecommendationResult keyRecommendation = determineAutocryptKeyRecommendation(autocryptKeyStatus); + if (keyRecommendation != null) return keyRecommendation; + + AutocryptRecommendationResult gossipRecommendation = determineAutocryptGossipRecommendation(autocryptKeyStatus); + if (gossipRecommendation != null) return gossipRecommendation; + + return new AutocryptRecommendationResult(autocryptKeyStatus.autocryptPeer().identifier(), AutocryptState.DISABLE, null, false); + } + + @Nullable + private AutocryptRecommendationResult determineAutocryptKeyRecommendation(AutocryptKeyStatus autocryptKeyStatus) { + boolean hasKey = autocryptKeyStatus.hasKey(); + boolean isRevoked = autocryptKeyStatus.isKeyRevoked(); + boolean isExpired = autocryptKeyStatus.isKeyExpired(); + if (!hasKey || isRevoked || isExpired) { + return null; + } + + AutocryptPeer autocryptPeer = autocryptKeyStatus.autocryptPeer(); + long masterKeyId = autocryptPeer.master_key_id(); + Date lastSeen = autocryptPeer.last_seen(); + Date lastSeenKey = autocryptPeer.last_seen_key(); + boolean isVerified = autocryptKeyStatus.isKeyVerified(); + if (lastSeenKey.getTime() < (lastSeen.getTime() - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) { + return new AutocryptRecommendationResult(autocryptPeer.identifier(), AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified); + } + + boolean isMutual = autocryptPeer.is_mutual(); + if (isMutual) { + return new AutocryptRecommendationResult(autocryptPeer.identifier(), AutocryptState.MUTUAL, masterKeyId, isVerified); + } else { + return new AutocryptRecommendationResult(autocryptPeer.identifier(), AutocryptState.AVAILABLE, masterKeyId, isVerified); + } + } + + @Nullable + private AutocryptRecommendationResult determineAutocryptGossipRecommendation(AutocryptKeyStatus autocryptKeyStatus) { + boolean gossipHasKey = autocryptKeyStatus.hasGossipKey(); + boolean gossipIsRevoked = autocryptKeyStatus.isGossipKeyRevoked(); + boolean gossipIsExpired = autocryptKeyStatus.isGossipKeyExpired(); + boolean isVerified = autocryptKeyStatus.isGossipKeyVerified(); + + if (!gossipHasKey || gossipIsRevoked || gossipIsExpired) { + return null; + } + + Long masterKeyId = autocryptKeyStatus.autocryptPeer().gossip_master_key_id(); + return new AutocryptRecommendationResult(autocryptKeyStatus.autocryptPeer().identifier(), AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified); + } + + public void updateKeyGossipFromSignature(String autocryptId, Date effectiveDate, long masterKeyId) { + autocryptPeerDao.updateKeyGossip(packageName, autocryptId, effectiveDate, masterKeyId, GossipOrigin.SIGNATURE); + } + + public void updateKeyGossipFromDedup(String autocryptId, long masterKeyId) { + autocryptPeerDao.updateKeyGossip(packageName, autocryptId, new Date(), masterKeyId, GossipOrigin.DEDUP); + } + + public static class AutocryptRecommendationResult { + public final String peerId; + public final Long masterKeyId; + public final AutocryptState autocryptState; + public final boolean isVerified; + + AutocryptRecommendationResult(String peerId, AutocryptState autocryptState, Long masterKeyId, + boolean isVerified) { + this.peerId = peerId; + this.autocryptState = autocryptState; + this.masterKeyId = masterKeyId; + this.isVerified = isVerified; + } + + } + + public enum AutocryptState { + DISABLE, DISCOURAGED_OLD, DISCOURAGED_GOSSIP, AVAILABLE, MUTUAL + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java index 855babaaf..2d146fe77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java @@ -39,10 +39,6 @@ import android.text.TextUtils; import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState; -import org.sufficientlysecure.keychain.provider.DatabaseNotifyManager; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; @@ -52,8 +48,9 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainExternalContract; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus; -import org.sufficientlysecure.keychain.provider.KeychainProvider; import org.sufficientlysecure.keychain.provider.SimpleContentResolverInterface; +import org.sufficientlysecure.keychain.remote.AutocryptInteractor.AutocryptRecommendationResult; +import org.sufficientlysecure.keychain.remote.AutocryptInteractor.AutocryptState; import timber.log.Timber; @@ -69,8 +66,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC private UriMatcher uriMatcher; private ApiPermissionHelper apiPermissionHelper; - private KeychainProvider internalKeychainProvider; - private DatabaseNotifyManager databaseNotifyManager; /** @@ -107,10 +102,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC throw new NullPointerException("Context can't be null during onCreate!"); } - internalKeychainProvider = new KeychainProvider(); - internalKeychainProvider.attachInfo(context, null); apiPermissionHelper = new ApiPermissionHelper(context, new ApiDataAccessObject(getContext())); - databaseNotifyManager = DatabaseNotifyManager.create(context); return true; } @@ -267,10 +259,9 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC throw new UnsupportedOperationException("Cannot wildcard-query autocrypt results!"); } if (!isWildcardSelector && queriesAutocryptResult) { - AutocryptPeerDataAccessObject autocryptPeerDao = - new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName, - databaseNotifyManager); - fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs); + AutocryptInteractor autocryptInteractor = + AutocryptInteractor.getInstance(getContext(), callingPackageName); + fillTempTableWithAutocryptRecommendations(db, autocryptInteractor, selectionArgs); } HashMap projectionMap = new HashMap<>(); @@ -310,7 +301,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC } qb.setStrict(true); - String query = qb.buildQuery(projection, null, null, groupBy, null, orderBy); + String query = qb.buildQuery(projection, null, groupBy, null, orderBy, null); Cursor cursor = db.query(query); if (cursor != null) { // Tell the cursor what uri to watch, so it knows when its source data changes @@ -327,9 +318,9 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC } private void fillTempTableWithAutocryptRecommendations(SupportSQLiteDatabase db, - AutocryptPeerDataAccessObject autocryptPeerDao, String[] peerIds) { + AutocryptInteractor autocryptInteractor, String[] peerIds) { List autocryptStates = - autocryptPeerDao.determineAutocryptRecommendations(peerIds); + autocryptInteractor.determineAutocryptRecommendations(peerIds); fillTempTableWithAutocryptRecommendations(db, autocryptStates); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 8406c297b..2a7fe0874 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -68,10 +68,9 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.SecurityProblem; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus; import org.sufficientlysecure.keychain.provider.OverriddenWarningsRepository; @@ -585,8 +584,8 @@ public class OpenPgpService extends Service { return signatureResult; } - AutocryptPeerDataAccessObject autocryptPeerentityDao = new AutocryptPeerDataAccessObject(getBaseContext(), - mApiPermissionHelper.getCurrentCallingPackage()); + AutocryptPeerDao autocryptPeerentityDao = + AutocryptPeerDao.getInstance(getBaseContext()); Long autocryptPeerMasterKeyId = autocryptPeerentityDao.getMasterKeyIdForAutocryptPeer(autocryptPeerId); long masterKeyId = signatureResult.getKeyId(); @@ -596,7 +595,9 @@ public class OpenPgpService extends Service { if (effectiveTime.after(now)) { effectiveTime = now; } - autocryptPeerentityDao.updateKeyGossipFromSignature(autocryptPeerId, effectiveTime, masterKeyId); + AutocryptInteractor autocryptInteractor = + AutocryptInteractor.getInstance(this, mApiPermissionHelper.getCurrentCallingPackage()); + autocryptInteractor.updateKeyGossipFromSignature(autocryptPeerId, effectiveTime, masterKeyId); return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.NEW); } else if (masterKeyId == autocryptPeerMasterKeyId) { return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.OK); @@ -860,9 +861,8 @@ public class OpenPgpService extends Service { private Intent updateAutocryptPeerImpl(Intent data) { try { - AutocryptPeerDataAccessObject autocryptPeerDao = new AutocryptPeerDataAccessObject(getBaseContext(), - mApiPermissionHelper.getCurrentCallingPackage()); - AutocryptInteractor autocryptInteractor = AutocryptInteractor.getInstance(getBaseContext(), autocryptPeerDao); + AutocryptInteractor autocryptInteractor = AutocryptInteractor.getInstance( + getBaseContext(), mApiPermissionHelper.getCurrentCallingPackage()); if (data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID) && data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicatePresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicatePresenter.java index ffda9a81d..a3ecadbe1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicatePresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/dialog/RemoteDeduplicatePresenter.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.remote.ui.dialog; -import java.util.Date; import java.util.List; import android.content.Context; @@ -31,10 +30,10 @@ import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeyInfo; import org.sufficientlysecure.keychain.livedata.KeyInfoInteractor.KeySelector; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.remote.AutocryptInteractor; import timber.log.Timber; @@ -44,7 +43,7 @@ class RemoteDeduplicatePresenter implements LoaderCallbacks> { private final int loaderId; - private AutocryptPeerDataAccessObject autocryptPeerDao; + private AutocryptInteractor autocryptInteractor; private String duplicateAddress; private RemoteDeduplicateView view; @@ -73,7 +72,7 @@ class RemoteDeduplicatePresenter implements LoaderCallbacks> { return; } - autocryptPeerDao = new AutocryptPeerDataAccessObject(context, packageName); + this.autocryptInteractor = AutocryptInteractor.getInstance(context, packageName); this.duplicateAddress = duplicateAddress; view.setAddressText(duplicateAddress); @@ -121,8 +120,8 @@ class RemoteDeduplicatePresenter implements LoaderCallbacks> { return; } - long masterKeyId = keyInfoData.get(selectedItem).getMasterKeyId(); - autocryptPeerDao.updateKeyGossipFromDedup(duplicateAddress, new Date(), masterKeyId); + long masterKeyId = keyInfoData.get(selectedItem).getMasterKeyId(); + autocryptInteractor.updateKeyGossipFromDedup(duplicateAddress, masterKeyId); view.finish(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java index 8a3458a51..fe6609e5f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java @@ -36,7 +36,8 @@ import com.google.auto.value.AutoValue; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.UriAttribute; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer; +import org.sufficientlysecure.keychain.model.AutocryptPeer; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.PackageIconGetter; @@ -71,30 +72,26 @@ public class IdentityDao { private static final String USER_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; - private static final String[] AUTOCRYPT_PEER_PROJECTION = new String[] { - ApiAutocryptPeer._ID, - ApiAutocryptPeer.PACKAGE_NAME, - ApiAutocryptPeer.IDENTIFIER, - }; - private static final int INDEX_PACKAGE_NAME = 1; - private static final int INDEX_IDENTIFIER = 2; - private final ContentResolver contentResolver; private final PackageIconGetter packageIconGetter; private final PackageManager packageManager; + private final AutocryptPeerDao autocryptPeerDao; static IdentityDao getInstance(Context context) { ContentResolver contentResolver = context.getContentResolver(); PackageManager packageManager = context.getPackageManager(); PackageIconGetter iconGetter = PackageIconGetter.getInstance(context); - return new IdentityDao(contentResolver, packageManager, iconGetter); + AutocryptPeerDao autocryptPeerDao = AutocryptPeerDao.getInstance(context); + return new IdentityDao(contentResolver, packageManager, iconGetter, autocryptPeerDao); } - private IdentityDao(ContentResolver contentResolver, PackageManager packageManager, PackageIconGetter iconGetter) { + private IdentityDao(ContentResolver contentResolver, PackageManager packageManager, PackageIconGetter iconGetter, + AutocryptPeerDao autocryptPeerDao) { this.packageManager = packageManager; this.contentResolver = contentResolver; this.packageIconGetter = iconGetter; + this.autocryptPeerDao = autocryptPeerDao; } List getIdentityInfos(long masterKeyId, boolean showLinkedIds) { @@ -110,35 +107,24 @@ public class IdentityDao { } private void correlateOrAddAutocryptPeers(ArrayList identities, long masterKeyId) { - Cursor cursor = contentResolver.query(ApiAutocryptPeer.buildByMasterKeyId(masterKeyId), - AUTOCRYPT_PEER_PROJECTION, null, null, null); - if (cursor == null) { - Timber.e("Error loading Autocrypt peers"); - return; - } + for (AutocryptPeer autocryptPeer : autocryptPeerDao.getAutocryptPeersForKey(masterKeyId)) { + String packageName = autocryptPeer.package_name(); + String autocryptId = autocryptPeer.identifier(); - try { - while (cursor.moveToNext()) { - String packageName = cursor.getString(INDEX_PACKAGE_NAME); - String autocryptPeer = cursor.getString(INDEX_IDENTIFIER); + Drawable drawable = packageIconGetter.getDrawableForPackageName(packageName); + Intent autocryptPeerIntent = getAutocryptPeerActivityIntentIfResolvable(packageName, autocryptId); - Drawable drawable = packageIconGetter.getDrawableForPackageName(packageName); - Intent autocryptPeerIntent = getAutocryptPeerActivityIntentIfResolvable(packageName, autocryptPeer); - - UserIdInfo associatedUserIdInfo = findUserIdMatchingAutocryptPeer(identities, autocryptPeer); - if (associatedUserIdInfo != null) { - int position = identities.indexOf(associatedUserIdInfo); - AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo - .create(associatedUserIdInfo, autocryptPeer, packageName, drawable, autocryptPeerIntent); - identities.set(position, autocryptPeerInfo); - } else { - AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo - .create(autocryptPeer, packageName, drawable, autocryptPeerIntent); - identities.add(autocryptPeerInfo); - } + UserIdInfo associatedUserIdInfo = findUserIdMatchingAutocryptPeer(identities, autocryptId); + if (associatedUserIdInfo != null) { + int position = identities.indexOf(associatedUserIdInfo); + AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo + .create(associatedUserIdInfo, autocryptId, packageName, drawable, autocryptPeerIntent); + identities.set(position, autocryptPeerInfo); + } else { + AutocryptPeerInfo autocryptPeerInfo = AutocryptPeerInfo + .create(autocryptId, packageName, drawable, autocryptPeerIntent); + identities.add(autocryptPeerInfo); } - } finally { - cursor.close(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/IdentitiesPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/IdentitiesPresenter.java index 28cd69b5b..478d0be5f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/IdentitiesPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/IdentitiesPresenter.java @@ -29,7 +29,7 @@ import android.net.Uri; import android.support.annotation.Nullable; import android.view.View; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter; @@ -56,12 +56,14 @@ public class IdentitiesPresenter implements Observer> { private final long masterKeyId; private final boolean isSecret; private final boolean showLinkedIds; + private AutocryptPeerDao autocryptPeerDao; public IdentitiesPresenter(Context context, IdentitiesMvpView view, ViewKeyMvpView viewKeyMvpView, long masterKeyId, boolean isSecret) { this.context = context; this.view = view; this.viewKeyMvpView = viewKeyMvpView; + this.autocryptPeerDao = AutocryptPeerDao.getInstance(context); this.masterKeyId = masterKeyId; this.isSecret = isSecret; @@ -146,9 +148,7 @@ public class IdentitiesPresenter implements Observer> { return; } - AutocryptPeerDataAccessObject autocryptPeerDao = - new AutocryptPeerDataAccessObject(context, info.getPackageName()); - autocryptPeerDao.delete(info.getIdentity()); + autocryptPeerDao.deleteByIdentifier(info.getPackageName(), info.getIdentity()); } public LiveData> getLiveDataInstance() { diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/AutocryptPeers.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/AutocryptPeers.sq new file mode 100644 index 000000000..df4506516 --- /dev/null +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/AutocryptPeers.sq @@ -0,0 +1,64 @@ +import java.util.Date; +import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; + +CREATE TABLE IF NOT EXISTS autocrypt_peers ( + package_name TEXT NOT NULL, + identifier TEXT NOT NULL, + last_seen INTEGER AS Date NULL, + last_seen_key INTEGER AS Date NULL, + is_mutual INTEGER AS Boolean NOT NULL DEFAULT 0, + master_key_id INTEGER NULL, + gossip_master_key_id INTEGER NULL, + gossip_last_seen_key INTEGER AS Date NULL, + gossip_origin INTEGER AS GossipOrigin NULL, + PRIMARY KEY(package_name, identifier), + FOREIGN KEY(package_name) REFERENCES api_apps (package_name) ON DELETE CASCADE +); + +selectByIdentifiers: +SELECT * + FROM autocrypt_peers + WHERE package_name = ? AND identifier IN ?; + +selectByMasterKeyId: +SELECT * + FROM autocrypt_peers + WHERE master_key_id = ?; + +selectMasterKeyIdByIdentifier: +SELECT master_key_id + FROM autocrypt_peers + WHERE identifier = ?; + +deleteByIdentifier: +DELETE FROM autocrypt_peers + WHERE package_name = ? AND identifier = ?; + +deleteByMasterKeyId: +DELETE FROM autocrypt_peers + WHERE master_key_id = ?; + +updateLastSeen: +UPDATE autocrypt_peers SET last_seen = ?3 WHERE package_name = ?1 AND identifier = ?2; + +updateKey: +UPDATE autocrypt_peers SET last_seen_key = ?3, master_key_id = ?4, is_mutual = ?5 WHERE package_name = ?1 AND identifier = ?2; + +updateGossipKey: +UPDATE autocrypt_peers SET gossip_last_seen_key = ?3, gossip_master_key_id = ?4, gossip_origin = ?5 WHERE package_name = ?1 AND identifier = ?2; + +insertPeer: +INSERT INTO autocrypt_peers (package_name, identifier, last_seen) VALUES (?, ?, ?); + +selectAutocryptKeyStatus: +SELECT autocryptPeer.*, + (CASE WHEN ac_key.expiry IS NULL THEN 0 WHEN ac_key.expiry > ?3 THEN 0 ELSE 1 END) AS key_is_expired_int, + (CASE WHEN gossip_key.expiry IS NULL THEN 0 WHEN gossip_key.expiry > ?3 THEN 0 ELSE 1 END) AS gossip_key_is_expired_int, + ac_key.is_revoked AS key_is_revoked_int, + gossip_key.is_revoked AS gossip_key_is_revoked_int, + EXISTS (SELECT * FROM certs WHERE certs.master_key_id = autocryptPeer.master_key_id AND verified = 1 ) AS key_is_verified_int, + EXISTS (SELECT * FROM certs WHERE certs.master_key_id = autocryptPeer.gossip_master_key_id AND verified = 1 ) AS gossip_key_is_verified_int + FROM autocrypt_peers AS autocryptPeer + LEFT JOIN keys AS ac_key ON (ac_key.master_key_id = autocryptPeer.master_key_id AND ac_key.rank = 0) + LEFT JOIN keys AS gossip_key ON (gossip_key.master_key_id = gossip_master_key_id AND gossip_key.rank = 0) + WHERE package_name = ?1 AND identifier IN ?2; \ No newline at end of file diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Certs.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Certs.sq new file mode 100644 index 000000000..1553509aa --- /dev/null +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Certs.sq @@ -0,0 +1,13 @@ +-- TODO implement. this is only here for reference in SQLDelight +CREATE TABLE IF NOT EXISTS certs( + master_key_id INTEGER, + rank INTEGER, + key_id_certifier INTEGER, + type INTEGER, + verified INTEGER, + creation INTEGER, + data BLOB, + PRIMARY KEY(master_key_id, rank, key_id_certifier), + FOREIGN KEY(master_key_id) REFERENCES keyrings_public(master_key_id) ON DELETE CASCADE + -- FOREIGN KEY(master_key_id, rank) REFERENCES user_packets(master_key_id, rank) ON DELETE CASCADE +); \ No newline at end of file diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java index 14288f7bb..6101b4a02 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java @@ -246,7 +246,8 @@ public class InteropTest { KeyWritableRepository helper = new KeyWritableRepository(RuntimeEnvironment.application, LocalPublicKeyStorage.getInstance(RuntimeEnvironment.application), LocalSecretKeyStorage.getInstance(RuntimeEnvironment.application), - DatabaseNotifyManager.create(RuntimeEnvironment.application)) { + DatabaseNotifyManager.create(RuntimeEnvironment.application), + AutocryptPeerDao.getInstance(RuntimeEnvironment.application)) { @Override public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException { diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java index 2d045a7b9..6c0b9b4f2 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java @@ -19,12 +19,13 @@ import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowPackageManager; import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.model.ApiApp; +import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin; import org.sufficientlysecure.keychain.operations.CertifyOperation; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDao; import org.sufficientlysecure.keychain.provider.KeyRepositorySaveTest; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.provider.KeychainExternalContract; @@ -63,7 +64,7 @@ public class KeychainExternalProviderTest { ContentResolver contentResolver = RuntimeEnvironment.application.getContentResolver(); ApiPermissionHelper apiPermissionHelper; ApiDataAccessObject apiDao; - AutocryptPeerDataAccessObject autocryptPeerDao; + AutocryptPeerDao autocryptPeerDao; @Before @@ -81,9 +82,9 @@ public class KeychainExternalProviderTest { apiDao = new ApiDataAccessObject(RuntimeEnvironment.application); apiPermissionHelper = new ApiPermissionHelper(RuntimeEnvironment.application, apiDao); - autocryptPeerDao = new AutocryptPeerDataAccessObject(RuntimeEnvironment.application, PACKAGE_NAME); + autocryptPeerDao = AutocryptPeerDao.getInstance(RuntimeEnvironment.application); - apiDao.insertApiApp(new ApiApp(PACKAGE_NAME, PACKAGE_SIGNATURE)); + apiDao.insertApiApp(ApiApp.create(PACKAGE_NAME, PACKAGE_SIGNATURE)); } @Test(expected = AccessControlException.class) @@ -100,7 +101,7 @@ public class KeychainExternalProviderTest { @Test(expected = AccessControlException.class) public void testPermission__withWrongPackageCert() throws Exception { apiDao.deleteApiApp(PACKAGE_NAME); - apiDao.insertApiApp(new ApiApp(PACKAGE_NAME, new byte[] { 1, 2, 4 })); + apiDao.insertApiApp(ApiApp.create(PACKAGE_NAME, new byte[] { 1, 2, 4 })); contentResolver.query( EmailStatus.CONTENT_URI, @@ -208,7 +209,8 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.updateKey(AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, false); + autocryptPeerDao.insertOrUpdateLastSeen(PACKAGE_NAME, "tid", new Date()); + autocryptPeerDao.updateKey(PACKAGE_NAME, AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, false); Cursor cursor = contentResolver.query( AutocryptStatus.CONTENT_URI, new String[] { @@ -235,7 +237,8 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.updateKey(AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, true); + autocryptPeerDao.insertOrUpdateLastSeen(PACKAGE_NAME, "tid", new Date()); + autocryptPeerDao.updateKey(PACKAGE_NAME, AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, true); Cursor cursor = contentResolver.query( AutocryptStatus.CONTENT_URI, new String[] { @@ -262,7 +265,8 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.updateKey("tid", new Date(), KEY_ID_PUBLIC, false); + autocryptPeerDao.insertOrUpdateLastSeen(PACKAGE_NAME, "tid", new Date()); + autocryptPeerDao.updateKey(PACKAGE_NAME, AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, false); certifyKey(KEY_ID_SECRET, KEY_ID_PUBLIC, USER_ID_1); Cursor cursor = contentResolver.query( @@ -306,8 +310,9 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.updateKeyGossipFromAutocrypt("tid", new Date(), KEY_ID_PUBLIC); - autocryptPeerDao.delete("tid"); + autocryptPeerDao.insertOrUpdateLastSeen(PACKAGE_NAME, "tid", new Date()); + autocryptPeerDao.updateKeyGossip(PACKAGE_NAME, "tid", new Date(), KEY_ID_PUBLIC, GossipOrigin.GOSSIP_HEADER); + autocryptPeerDao.deleteByIdentifier(PACKAGE_NAME, "tid"); Cursor cursor = contentResolver.query( AutocryptStatus.CONTENT_URI, new String[] { @@ -331,7 +336,8 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.updateKeyGossipFromAutocrypt(AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC); + autocryptPeerDao.insertOrUpdateLastSeen(PACKAGE_NAME, "tid", new Date()); + autocryptPeerDao.updateKeyGossip(PACKAGE_NAME, AUTOCRYPT_PEER, new Date(), KEY_ID_PUBLIC, GossipOrigin.GOSSIP_HEADER); certifyKey(KEY_ID_SECRET, KEY_ID_PUBLIC, USER_ID_1); Cursor cursor = contentResolver.query(