Fix up logic to match Autocrypt 1.0

This commit is contained in:
Vincent Breitmoser
2018-01-08 12:38:57 +01:00
parent 5a2631841d
commit 7b268b11ed
11 changed files with 515 additions and 321 deletions

View File

@@ -0,0 +1,106 @@
package org.sufficientlysecure.keychain.remote;
import java.io.IOException;
import java.util.Date;
import android.content.Context;
import org.openintents.openpgp.AutocryptPeerUpdate;
import org.openintents.openpgp.AutocryptPeerUpdate.PreferEncrypt;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
import org.sufficientlysecure.keychain.util.Log;
class AutocryptInteractor {
private AutocryptPeerDataAccessObject autocryptPeerDao;
private KeyWritableRepository keyWritableRepository;
public static AutocryptInteractor getInstance(Context context, AutocryptPeerDataAccessObject autocryptPeerentityDao) {
KeyWritableRepository keyWritableRepository = KeyWritableRepository.create(context);
return new AutocryptInteractor(autocryptPeerentityDao, keyWritableRepository);
}
private AutocryptInteractor(AutocryptPeerDataAccessObject autocryptPeerDao,
KeyWritableRepository keyWritableRepository) {
this.autocryptPeerDao = autocryptPeerDao;
this.keyWritableRepository = keyWritableRepository;
}
void updateAutocryptPeerState(String autocryptPeerId, AutocryptPeerUpdate autocryptPeerUpdate)
throws PgpGeneralException, IOException {
if (autocryptPeerUpdate == null) {
return;
}
Long newMasterKeyId;
if (autocryptPeerUpdate.hasKeyData()) {
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(autocryptPeerUpdate.getKeyData());
if (uncachedKeyRing.isSecret()) {
Log.e(Constants.TAG, "Found secret key in autocrypt id! - Ignoring");
return;
}
// this will merge if the key already exists - no worries!
keyWritableRepository.savePublicKeyRing(uncachedKeyRing);
newMasterKeyId = uncachedKeyRing.getMasterKeyId();
} else {
newMasterKeyId = null;
}
Date effectiveDate = autocryptPeerUpdate.getEffectiveDate();
autocryptPeerDao.updateLastSeen(autocryptPeerId, effectiveDate);
if (newMasterKeyId == null) {
return;
}
Date lastSeenKey = autocryptPeerDao.getLastSeenKey(autocryptPeerId);
if (lastSeenKey == null || effectiveDate.after(lastSeenKey)) {
boolean isMutual = autocryptPeerUpdate.getPreferEncrypt() == PreferEncrypt.MUTUAL;
autocryptPeerDao.updateKey(autocryptPeerId, effectiveDate, newMasterKeyId, isMutual);
}
}
void updateAutocryptPeerGossipState(String autocryptPeerId, AutocryptPeerUpdate autocryptPeerUpdate)
throws PgpGeneralException, IOException {
if (autocryptPeerUpdate == null) {
return;
}
Long newMasterKeyId;
if (autocryptPeerUpdate.hasKeyData()) {
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(autocryptPeerUpdate.getKeyData());
if (uncachedKeyRing.isSecret()) {
Log.e(Constants.TAG, "Found secret key in autocrypt id! - Ignoring");
return;
}
// this will merge if the key already exists - no worries!
keyWritableRepository.savePublicKeyRing(uncachedKeyRing);
newMasterKeyId = uncachedKeyRing.getMasterKeyId();
} else {
newMasterKeyId = null;
}
Date lastSeen = autocryptPeerDao.getLastSeen(autocryptPeerId);
Date effectiveDate = autocryptPeerUpdate.getEffectiveDate();
if (newMasterKeyId == null) {
return;
}
Date lastSeenKey = autocryptPeerDao.getLastSeenKey(autocryptPeerId);
if (lastSeenKey != null && effectiveDate.before(lastSeenKey)) {
return;
}
if (lastSeen == null || effectiveDate.after(lastSeen)) {
autocryptPeerDao.updateKeyGossip(autocryptPeerId, effectiveDate, newMasterKeyId);
}
}
}

View File

@@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.remote;
import java.security.AccessControlException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -40,9 +39,11 @@ 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.AutocryptPeerStateResult;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@@ -230,81 +231,70 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
}
db.execSQL("CREATE TEMPORARY TABLE " + TEMP_TABLE_QUERIED_ADDRESSES + " (" + TEMP_TABLE_COLUMN_ADDRES + " TEXT);");
if (projection == null) {
throw new IllegalArgumentException("Please provide a projection!");
}
db.execSQL("CREATE TEMPORARY TABLE " + TEMP_TABLE_QUERIED_ADDRESSES + " (" +
TEMP_TABLE_COLUMN_ADDRES + " TEXT NOT NULL PRIMARY KEY, " +
AutocryptStatus.UID_KEY_STATUS + " INT, " +
AutocryptStatus.UID_ADDRESS + " TEXT, " +
AutocryptStatus.UID_MASTER_KEY_ID + " TEXT, " +
AutocryptStatus.AUTOCRYPT_KEY_STATUS + " INT, " +
AutocryptStatus.AUTOCRYPT_PEER_STATE + " INT, " +
AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID + " INT" +
");");
ContentValues cv = new ContentValues();
for (String address : selectionArgs) {
cv.put(TEMP_TABLE_COLUMN_ADDRES, address);
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, null, cv);
}
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(AutocryptStatus._ID, "email AS _id");
projectionMap.put(AutocryptStatus.ADDRESS, // this is actually the queried address
TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES + " AS " + AutocryptStatus.ADDRESS);
long unixSeconds = System.currentTimeMillis() / 1000;
db.execSQL("REPLACE INTO " + TEMP_TABLE_QUERIED_ADDRESSES +
"(" + TEMP_TABLE_COLUMN_ADDRES + ", " + AutocryptStatus.UID_KEY_STATUS + ", " + AutocryptStatus.UID_ADDRESS + ")" +
" SELECT " + TEMP_TABLE_COLUMN_ADDRES + ", " +
"CASE ( MIN (certs_user_id." + Certs.VERIFIED + " ) ) "
// remap to keep this provider contract independent from our internal representation
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
+ " WHEN NULL THEN NULL"
+ " END AS " + AutocryptStatus.UID_KEY_STATUS +
", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID +
" FROM " + TEMP_TABLE_QUERIED_ADDRESSES
+ " JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.USER_PACKETS + "." + UserPackets.USER_ID + " IS NOT NULL"
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.EMAIL + " LIKE " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
+ ")"
+ " JOIN " + Tables.CERTS + " AS certs_user_id ON ("
+ Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = certs_user_id." + Certs.MASTER_KEY_ID
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = certs_user_id." + Certs.RANK
+ ")"
+ " WHERE (EXISTS (SELECT * FROM " + Tables.KEYS + " WHERE "
+ Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND " + Tables.KEYS + "." + Keys.IS_REVOKED + " = 0"
+ " AND NOT " + "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY
+ " < " + unixSeconds + ")"
+ ")) OR " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IS NULL"
+ " GROUP BY " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
);
projectionMap.put(AutocryptStatus.UID_ADDRESS,
Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + AutocryptStatus.UID_ADDRESS);
// we take the minimum (>0) here, where "1" is "verified by known secret key", "2" is "self-certified"
projectionMap.put(AutocryptStatus.UID_KEY_STATUS, "CASE ( MIN (certs_user_id." + Certs.VERIFIED + " ) ) "
// remap to keep this provider contract independent from our internal representation
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
+ " WHEN NULL THEN NULL"
+ " END AS " + AutocryptStatus.UID_KEY_STATUS);
projectionMap.put(AutocryptStatus.UID_MASTER_KEY_ID,
Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + AutocryptStatus.UID_MASTER_KEY_ID);
projectionMap.put(AutocryptStatus.UID_CANDIDATES,
"COUNT(DISTINCT " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID +
") AS " + AutocryptStatus.UID_CANDIDATES);
projectionMap.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, "CASE ( MIN (certs_autocrypt_peer." + Certs.VERIFIED + " ) ) "
// remap to keep this provider contract independent from our internal representation
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
+ " WHEN NULL THEN NULL"
+ " END AS " + AutocryptStatus.AUTOCRYPT_KEY_STATUS);
projectionMap.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID,
Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + " AS " + AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID);
projectionMap.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, Tables.API_AUTOCRYPT_PEERS + "." +
ApiAutocryptPeer.STATE + " AS " + AutocryptStatus.AUTOCRYPT_LAST_SEEN_KEY);
projectionMap.put(AutocryptStatus.AUTOCRYPT_LAST_SEEN, Tables.API_AUTOCRYPT_PEERS + "." +
ApiAutocryptPeer.LAST_SEEN + " AS " + AutocryptStatus.AUTOCRYPT_LAST_SEEN);
projectionMap.put(AutocryptStatus.AUTOCRYPT_LAST_SEEN_KEY, Tables.API_AUTOCRYPT_PEERS + "." +
ApiAutocryptPeer.LAST_SEEN_KEY + " AS " + AutocryptStatus.AUTOCRYPT_LAST_SEEN_KEY);
qb.setProjectionMap(projectionMap);
if (projection == null) {
throw new IllegalArgumentException("Please provide a projection!");
AutocryptPeerDataAccessObject autocryptPeerDao =
new AutocryptPeerDataAccessObject(getContext(), callingPackageName);
for (String autocryptId : selectionArgs) {
AutocryptPeerStateResult autocryptState = autocryptPeerDao.getAutocryptState(autocryptId);
cv.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, getPeerStateValue(autocryptState.autocryptState));
if (autocryptState.masterKeyId != null) {
cv.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID, autocryptState.masterKeyId);
cv.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, autocryptState.isVerified ?
KeychainExternalContract.KEY_STATUS_VERIFIED : KeychainExternalContract.KEY_STATUS_UNVERIFIED);
}
System.err.println(cv.toString());
db.update(TEMP_TABLE_QUERIED_ADDRESSES, cv,
TEMP_TABLE_COLUMN_ADDRES + "=?", new String [] { autocryptId });
}
qb.setTables(
TEMP_TABLE_QUERIED_ADDRESSES
+ " LEFT JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.USER_PACKETS + "." + UserPackets.USER_ID + " IS NOT NULL"
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.EMAIL + " LIKE " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
+ ")"
+ " LEFT JOIN " + Tables.CERTS + " AS certs_user_id ON ("
+ Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = certs_user_id." + Certs.MASTER_KEY_ID
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = certs_user_id." + Certs.RANK
+ ")"
+ " LEFT JOIN " + Tables.API_AUTOCRYPT_PEERS + " ON ("
+ Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.IDENTIFIER + " LIKE queried_addresses.address"
+ " AND " + Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = \"" + callingPackageName + "\""
+ ")"
+ " LEFT JOIN " + Tables.CERTS + " AS certs_autocrypt_peer ON ("
+ Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + " = certs_autocrypt_peer." + Certs.MASTER_KEY_ID
+ ")"
);
// in case there are multiple verifying certificates
groupBy = TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES;
// can't have an expired master key for the uid candidate
qb.appendWhere("(EXISTS (SELECT * FROM " + Tables.KEYS + " WHERE "
+ Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND " + Tables.KEYS + "." + Keys.IS_REVOKED + " = 0"
+ " AND NOT " + "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY
+ " < " + new Date().getTime() / 1000 + ")"
+ ")) OR " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IS NULL");
qb.setTables(TEMP_TABLE_QUERIED_ADDRESSES);
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = AutocryptStatus.ADDRESS;
@@ -355,6 +345,17 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
return cursor;
}
private int getPeerStateValue(AutocryptState autocryptState) {
switch (autocryptState) {
case DISABLE: return AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
case DISCOURAGED_OLD: return AutocryptStatus.AUTOCRYPT_PEER_DISCOURAGED_OLD;
case DISCOURAGED_GOSSIP: return AutocryptStatus.AUTOCRYPT_PEER_GOSSIP;
case AVAILABLE: return AutocryptStatus.AUTOCRYPT_PEER_AVAILABLE;
case MUTUAL: return AutocryptStatus.AUTOCRYPT_PEER_MUTUAL;
}
throw new IllegalStateException("Unhandled case!");
}
private void checkIfPackageBelongsToCaller(Context context, String requestedPackageName) {
int callerUid = Binder.getCallingUid();
String[] callerPackageNames = context.getPackageManager().getPackagesForUid(callerUid);

View File

@@ -42,7 +42,6 @@ import android.support.annotation.Nullable;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.openintents.openpgp.AutocryptPeerUpdate;
import org.openintents.openpgp.AutocryptPeerUpdate.PreferEncrypt;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpError;
@@ -65,17 +64,14 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncryptData;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.SecurityProblem;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.KeyRepository;
import org.sufficientlysecure.keychain.provider.KeyWritableRepository;
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;
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.AutocryptState;
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResult;
import org.sufficientlysecure.keychain.remote.OpenPgpServiceKeyIdExtractor.KeyIdResultStatus;
import org.sufficientlysecure.keychain.service.BackupKeyringParcel;
@@ -203,21 +199,9 @@ public class OpenPgpService extends Service {
private Intent encryptAndSignImpl(Intent data, InputStream inputStream,
OutputStream outputStream, boolean sign, boolean isQueryAutocryptStatus) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME);
if (originalFilename == null) {
originalFilename = "";
}
PgpSignEncryptData.Builder pgpData = PgpSignEncryptData.builder()
.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(null);
boolean enableCompression = data.getBooleanExtra(OpenPgpApi.EXTRA_ENABLE_COMPRESSION, true);
if (!enableCompression) {
pgpData.setCompressionAlgorithm(OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
}
if (sign) {
Intent signKeyIdIntent = getSignKeyMasterId(data);
// NOTE: Fallback to return account settings (Old API)
@@ -240,13 +224,25 @@ public class OpenPgpService extends Service {
KeyIdResult keyIdResult = mKeyIdExtractor.returnKeyIdsFromIntent(data, false,
mApiPermissionHelper.getCurrentCallingPackage());
boolean isOpportunistic = data.getBooleanExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
KeyIdResultStatus keyIdResultStatus = keyIdResult.getStatus();
if (isQueryAutocryptStatus) {
return getAutocryptStatusResult(keyIdResult);
}
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
pgpData.setEnableAsciiArmorOutput(asciiArmor);
boolean enableCompression = data.getBooleanExtra(OpenPgpApi.EXTRA_ENABLE_COMPRESSION, true);
pgpData.setCompressionAlgorithm(enableCompression ? OpenKeychainCompressionAlgorithmTags.USE_DEFAULT :
OpenKeychainCompressionAlgorithmTags.UNCOMPRESSED);
String originalFilename = data.getStringExtra(OpenPgpApi.EXTRA_ORIGINAL_FILENAME);
if (originalFilename == null) {
originalFilename = "";
}
if (keyIdResult.hasKeySelectionPendingIntent()) {
boolean isOpportunistic = data.getBooleanExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
if ((keyIdResultStatus == KeyIdResultStatus.MISSING || keyIdResultStatus == KeyIdResultStatus.NO_KEYS ||
keyIdResultStatus == KeyIdResultStatus.NO_KEYS_ERROR) && isOpportunistic) {
return createErrorResultIntent(OpenPgpError.OPPORTUNISTIC_MISSING_KEYS,
@@ -309,8 +305,8 @@ public class OpenPgpService extends Service {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
result.putExtra(OpenPgpApi.RESULT_KEYS_CONFIRMED, keyIdResult.isAllKeysConfirmed());
AutocryptState combinedAutocryptState = keyIdResult.getCombinedAutocryptState();
if (combinedAutocryptState == null) {
int combinedAutocryptState = keyIdResult.getAutocryptRecommendation();
if (combinedAutocryptState == AutocryptStatus.AUTOCRYPT_PEER_DISABLED) {
switch (keyIdResult.getStatus()) {
case NO_KEYS:
case NO_KEYS_ERROR:
@@ -334,18 +330,17 @@ public class OpenPgpService extends Service {
}
switch (combinedAutocryptState) {
case EXTERNAL:
case SELECTED:
case GOSSIP:
case RESET: {
case AutocryptStatus.AUTOCRYPT_PEER_DISCOURAGED_OLD:
case AutocryptStatus.AUTOCRYPT_PEER_GOSSIP: {
result.putExtra(OpenPgpApi.RESULT_AUTOCRYPT_STATUS, OpenPgpApi.AUTOCRYPT_STATUS_DISCOURAGE);
break;
}
case AVAILABLE: {
case AutocryptStatus.AUTOCRYPT_PEER_AVAILABLE_EXTERNAL:
case AutocryptStatus.AUTOCRYPT_PEER_AVAILABLE: {
result.putExtra(OpenPgpApi.RESULT_AUTOCRYPT_STATUS, OpenPgpApi.AUTOCRYPT_STATUS_AVAILABLE);
break;
}
case MUTUAL: {
case AutocryptStatus.AUTOCRYPT_PEER_MUTUAL: {
result.putExtra(OpenPgpApi.RESULT_AUTOCRYPT_STATUS, OpenPgpApi.AUTOCRYPT_STATUS_MUTUAL);
break;
}
@@ -387,9 +382,7 @@ public class OpenPgpService extends Service {
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
String senderAddress = data.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS);
AutocryptPeerDataAccessObject autocryptPeerentityDao = new AutocryptPeerDataAccessObject(
getBaseContext(), mApiPermissionHelper.getCurrentCallingPackage());
updateAutocryptPeerStateFromIntent(data, autocryptPeerentityDao, false);
updateAutocryptPeerImpl(data);
PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(this, mKeyRepository, progressable);
@@ -476,63 +469,6 @@ public class OpenPgpService extends Service {
mApiPendingIntentFactory.createSecurityProblemIntent(packageName, securityProblem, supportOverride));
}
private String updateAutocryptPeerStateFromIntent(Intent data, AutocryptPeerDataAccessObject autocryptPeerDao,
boolean isGossip)
throws PgpGeneralException, IOException {
String autocryptPeerId = data.getStringExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID);
AutocryptPeerUpdate autocryptPeerUpdate = data.getParcelableExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE);
return updateAutocryptPeerState(autocryptPeerId, autocryptPeerUpdate, autocryptPeerDao, isGossip);
}
private String updateAutocryptPeerState(String autocryptPeerId, AutocryptPeerUpdate autocryptPeerUpdate,
AutocryptPeerDataAccessObject autocryptPeerDao, boolean isGossip)
throws PgpGeneralException, IOException {
if (autocryptPeerUpdate == null) {
return null;
}
Long newMasterKeyId;
if (autocryptPeerUpdate.hasKeyData()) {
UncachedKeyRing uncachedKeyRing = UncachedKeyRing.decodeFromData(autocryptPeerUpdate.getKeyData());
if (uncachedKeyRing.isSecret()) {
Timber.e("Found secret key in autocrypt id! - Ignoring");
return null;
}
// this will merge if the key already exists - no worries!
KeyWritableRepository.create(this).savePublicKeyRing(uncachedKeyRing);
newMasterKeyId = uncachedKeyRing.getMasterKeyId();
} else {
newMasterKeyId = null;
}
Date lastSeen = autocryptPeerDao.getLastSeen(autocryptPeerId);
Date effectiveDate = autocryptPeerUpdate.getEffectiveDate();
if (newMasterKeyId == null) {
if (!isGossip && effectiveDate.after(lastSeen)) {
autocryptPeerDao.updateToResetState(autocryptPeerId, effectiveDate);
}
return autocryptPeerId;
}
Date lastSeenKey = autocryptPeerDao.getLastSeenKey(autocryptPeerId);
if (lastSeenKey != null && effectiveDate.before(lastSeenKey)) {
return autocryptPeerId;
}
if (lastSeen == null || effectiveDate.after(lastSeen)) {
if (isGossip) {
autocryptPeerDao.updateToGossipState(autocryptPeerId, effectiveDate, newMasterKeyId);
} else if (autocryptPeerUpdate.getPreferEncrypt() == PreferEncrypt.MUTUAL) {
autocryptPeerDao.updateToMutualState(autocryptPeerId, effectiveDate, newMasterKeyId);
} else {
autocryptPeerDao.updateToAvailableState(autocryptPeerId, effectiveDate, newMasterKeyId);
}
}
return autocryptPeerId;
}
private void processDecryptionResultForResultIntent(int targetApiVersion, Intent result,
OpenPgpDecryptionResult decryptionResult) {
if (targetApiVersion < API_VERSION_WITH_DECRYPTION_RESULT) {
@@ -627,7 +563,7 @@ public class OpenPgpService extends Service {
}
private OpenPgpSignatureResult processAutocryptPeerInfoToSignatureResult(OpenPgpSignatureResult signatureResult,
String autocryptPeerentity) {
String autocryptPeerId) {
boolean hasValidSignature =
signatureResult.getResult() == OpenPgpSignatureResult.RESULT_VALID_KEY_CONFIRMED ||
signatureResult.getResult() == OpenPgpSignatureResult.RESULT_VALID_KEY_UNCONFIRMED;
@@ -637,11 +573,17 @@ public class OpenPgpService extends Service {
AutocryptPeerDataAccessObject autocryptPeerentityDao = new AutocryptPeerDataAccessObject(getBaseContext(),
mApiPermissionHelper.getCurrentCallingPackage());
Long autocryptPeerMasterKeyId = autocryptPeerentityDao.getMasterKeyIdForAutocryptPeer(autocryptPeerentity);
Long autocryptPeerMasterKeyId = autocryptPeerentityDao.getMasterKeyIdForAutocryptPeer(autocryptPeerId);
long masterKeyId = signatureResult.getKeyId();
if (autocryptPeerMasterKeyId == null) {
autocryptPeerentityDao.updateToGossipState(autocryptPeerentity, new Date(), masterKeyId);
// TODO
// Date now = new Date();
// Date effectiveTime = signatureResult.getSignatureTimestamp();
// if (effectiveTime.after(now)) {
// effectiveTime = now;
// }
// autocryptPeerentityDao.updateKeyGossip(autocryptPeerId, effectiveTime, masterKeyId);
return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.NEW);
} else if (masterKeyId == autocryptPeerMasterKeyId) {
return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.OK);
@@ -811,22 +753,25 @@ public class OpenPgpService extends Service {
private Intent updateAutocryptPeerImpl(Intent data) {
try {
AutocryptPeerDataAccessObject autocryptPeerentityDao = new AutocryptPeerDataAccessObject(getBaseContext(),
AutocryptPeerDataAccessObject autocryptPeerDao = new AutocryptPeerDataAccessObject(getBaseContext(),
mApiPermissionHelper.getCurrentCallingPackage());
AutocryptInteractor autocryptInteractor = AutocryptInteractor.getInstance(getBaseContext(), autocryptPeerDao);
if (data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID) &&
data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE)) {
updateAutocryptPeerStateFromIntent(data, autocryptPeerentityDao, false);
} else if (data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_GOSSIP_UPDATES)) {
String autocryptPeerId = data.getStringExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID);
AutocryptPeerUpdate autocryptPeerUpdate = data.getParcelableExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE);
autocryptInteractor.updateAutocryptPeerState(autocryptPeerId, autocryptPeerUpdate);
}
if (data.hasExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_GOSSIP_UPDATES)) {
Bundle updates = data.getBundleExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_GOSSIP_UPDATES);
for (String address : updates.keySet()) {
Log.d(Constants.TAG, "Updating gossip state: " + address);
Timber.d(Constants.TAG, "Updating gossip state: " + address);
AutocryptPeerUpdate update = updates.getParcelable(address);
updateAutocryptPeerState(address, update, autocryptPeerentityDao, true);
autocryptInteractor.updateAutocryptPeerGossipState(address, update);
}
} else {
throw new IllegalArgumentException("need to specify both autocrypt_peer_id and" +
"autocrypt_peer_update, or autocrypt_peer_gossip_updates!");
}
Intent result = new Intent();

View File

@@ -31,7 +31,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeer;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@@ -82,7 +81,7 @@ class OpenPgpServiceKeyIdExtractor {
for (long keyId : data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS_SELECTED)) {
encryptKeyIds.add(keyId);
}
result = createKeysOkResult(encryptKeyIds, false, null);
result = createKeysOkResult(encryptKeyIds, false, AutocryptStatus.AUTOCRYPT_PEER_DISABLED);
} else if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS) || askIfNoUserIdsProvided) {
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
result = returnKeyIdsFromEmails(data, userIds, callingPackageName);
@@ -109,7 +108,7 @@ class OpenPgpServiceKeyIdExtractor {
HashSet<Long> keyIds = new HashSet<>();
ArrayList<String> missingEmails = new ArrayList<>();
ArrayList<String> duplicateEmails = new ArrayList<>();
AutocryptState combinedAutocryptState = null;
int combinedAutocryptState = AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
if (hasAddresses) {
HashMap<String, AddressQueryResult> userIdEntries = getStatusMapForQueriedAddresses(
@@ -129,18 +128,15 @@ class OpenPgpServiceKeyIdExtractor {
anyKeyNotVerified = true;
}
if (combinedAutocryptState == null) {
combinedAutocryptState = addressQueryResult.autocryptState;
} else {
combinedAutocryptState = combinedAutocryptState.combineWith(addressQueryResult.autocryptState);
}
combinedAutocryptState = combineAutocryptState(
combinedAutocryptState, addressQueryResult.autocryptState);
continue;
}
if (addressQueryResult.uidMasterKeyId != null) {
keyIds.add(addressQueryResult.uidMasterKeyId);
combinedAutocryptState = AutocryptState.EXTERNAL;
combinedAutocryptState = AutocryptStatus.AUTOCRYPT_PEER_AVAILABLE_EXTERNAL;
if (addressQueryResult.uidHasMultipleCandidates) {
duplicateEmails.add(queriedAddress);
@@ -178,6 +174,10 @@ class OpenPgpServiceKeyIdExtractor {
return createKeysOkResult(keyIds, allKeysConfirmed, combinedAutocryptState);
}
private int combineAutocryptState(int first, int second) {
return first < second ? first : second;
}
/** This method queries the KeychainExternalProvider for all addresses given in encryptionUserIds.
* It returns a map with one UserIdStatus per queried address. If multiple key candidates exist,
* the one with the highest verification status is selected. If two candidates with the same
@@ -207,7 +207,7 @@ class OpenPgpServiceKeyIdExtractor {
AddressQueryResult status = new AddressQueryResult(
uidMasterKeyId, uidKeyStatus, uidHasMultipleCandidates, autocryptMasterKeyId,
autocryptKeyStatus, AutocryptState.fromDbValue(autocryptPeerStatus));
autocryptKeyStatus, autocryptPeerStatus);
keyRows.put(queryAddress, status);
}
@@ -223,10 +223,10 @@ class OpenPgpServiceKeyIdExtractor {
private boolean uidHasMultipleCandidates;
private final Long autocryptMasterKeyId;
private final int autocryptKeyStatus;
private final AutocryptState autocryptState;
private final int autocryptState;
AddressQueryResult(Long uidMasterKeyId, int uidKeyStatus, boolean uidHasMultipleCandidates, Long autocryptMasterKeyId,
int autocryptKeyStatus, AutocryptState autocryptState) {
int autocryptKeyStatus, int autocryptState) {
this.uidMasterKeyId = uidMasterKeyId;
this.uidKeyStatus = uidKeyStatus;
this.uidHasMultipleCandidates = uidHasMultipleCandidates;
@@ -236,56 +236,13 @@ class OpenPgpServiceKeyIdExtractor {
}
}
enum AutocryptState {
EXTERNAL, RESET, GOSSIP, SELECTED, AVAILABLE, MUTUAL;
static AutocryptState fromDbValue(int state) {
switch (state) {
case ApiAutocryptPeer.RESET:
return RESET;
case ApiAutocryptPeer.AVAILABLE:
return AVAILABLE;
case ApiAutocryptPeer.SELECTED:
return SELECTED;
case ApiAutocryptPeer.GOSSIP:
return GOSSIP;
case ApiAutocryptPeer.MUTUAL:
return MUTUAL;
default:
throw new IllegalStateException();
}
}
public AutocryptState combineWith(AutocryptState other) {
if (this == EXTERNAL || other == EXTERNAL) {
return EXTERNAL;
}
if (this == RESET || other == RESET) {
return RESET;
}
if (this == GOSSIP || other == GOSSIP) {
return GOSSIP;
}
if (this == SELECTED || other == SELECTED) {
return SELECTED;
}
if (this == AVAILABLE || other == AVAILABLE) {
return AVAILABLE;
}
if (this == MUTUAL && other == MUTUAL) {
return MUTUAL;
}
throw new IllegalStateException("Bug: autocrypt states can't be combined!");
}
}
static class KeyIdResult {
private final PendingIntent mKeySelectionPendingIntent;
private final HashSet<Long> mUserKeyIds;
private final HashSet<Long> mExplicitKeyIds;
private final KeyIdResultStatus mStatus;
private final boolean mAllKeysConfirmed;
private final AutocryptState mCombinedAutocryptState;
private final int mCombinedAutocryptState;
private KeyIdResult(PendingIntent keySelectionPendingIntent, KeyIdResultStatus keyIdResultStatus) {
mKeySelectionPendingIntent = keySelectionPendingIntent;
@@ -293,11 +250,11 @@ class OpenPgpServiceKeyIdExtractor {
mAllKeysConfirmed = false;
mStatus = keyIdResultStatus;
mExplicitKeyIds = null;
mCombinedAutocryptState = null;
mCombinedAutocryptState = AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
}
private KeyIdResult(HashSet<Long> keyIds, boolean allKeysConfirmed, KeyIdResultStatus keyIdResultStatus,
AutocryptState combinedAutocryptState) {
int combinedAutocryptState) {
mKeySelectionPendingIntent = null;
mUserKeyIds = keyIds;
mAllKeysConfirmed = allKeysConfirmed;
@@ -355,7 +312,7 @@ class OpenPgpServiceKeyIdExtractor {
return mStatus;
}
public AutocryptState getCombinedAutocryptState() {
int getAutocryptRecommendation() {
return mCombinedAutocryptState;
}
}
@@ -365,7 +322,7 @@ class OpenPgpServiceKeyIdExtractor {
}
private KeyIdResult createKeysOkResult(HashSet<Long> encryptKeyIds, boolean allKeysConfirmed,
AutocryptState combinedAutocryptState) {
int combinedAutocryptState) {
return new KeyIdResult(encryptKeyIds, allKeysConfirmed, KeyIdResultStatus.OK, combinedAutocryptState);
}

View File

@@ -120,8 +120,8 @@ class RemoteDeduplicatePresenter implements LoaderCallbacks<List<KeyInfo>> {
return;
}
long masterKeyId = keyInfoData.get(selectedItem).getMasterKeyId();
autocryptPeerDao.updateToSelectedState(duplicateAddress, masterKeyId);
// long masterKeyId = keyInfoData.get(selectedItem).getMasterKeyId();
// autocryptPeerDao.updateToSelectedState(duplicateAddress, masterKeyId);
view.finish();
}