extract autocrypt_peers from KeychainProvider into AutocryptPeerDao

This commit is contained in:
Vincent Breitmoser
2018-06-18 23:03:09 +02:00
parent bae90f1b23
commit e144a402b5
20 changed files with 546 additions and 650 deletions

View File

@@ -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 messages 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 messages effective date is more recent than peers[from-addr].last_seen then set peers[from-addr].last_seen to the messages 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 mails 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 messages 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<AutocryptRecommendationResult> determineAutocryptRecommendations(String... autocryptIds) {
List<AutocryptRecommendationResult> 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
}
}

View File

@@ -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<String, String> 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<AutocryptRecommendationResult> autocryptStates =
autocryptPeerDao.determineAutocryptRecommendations(peerIds);
autocryptInteractor.determineAutocryptRecommendations(peerIds);
fillTempTableWithAutocryptRecommendations(db, autocryptStates);
}

View File

@@ -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)) {

View File

@@ -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<List<KeyInfo>> {
private final int loaderId;
private AutocryptPeerDataAccessObject autocryptPeerDao;
private AutocryptInteractor autocryptInteractor;
private String duplicateAddress;
private RemoteDeduplicateView view;
@@ -73,7 +72,7 @@ class RemoteDeduplicatePresenter implements LoaderCallbacks<List<KeyInfo>> {
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<List<KeyInfo>> {
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();
}