nicer MessageCryptoHelper structure, and handle plain autocrypt asynchronously

This commit is contained in:
Vincent Breitmoser 2017-08-21 20:30:32 +02:00
parent a7aaefe404
commit 2e7c6cf5e8
2 changed files with 75 additions and 72 deletions

View file

@ -1,7 +1,6 @@
package com.fsck.k9.crypto; package com.fsck.k9.crypto;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -12,7 +11,6 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.MimeMessage;
import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.MimeUtility;
import okio.ByteString; import okio.ByteString;
import org.openintents.openpgp.AutocryptPeerUpdate; import org.openintents.openpgp.AutocryptPeerUpdate;
@ -59,14 +57,6 @@ public class AutocryptOperations {
return true; return true;
} }
public void processCleartextMessage(OpenPgpApi openPgpApi, Message currentMessage) {
Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
boolean hasInlineKeyData = addAutocryptPeerUpdateToIntentIfPresent(currentMessage, intent);
if (hasInlineKeyData) {
openPgpApi.executeApi(intent, (InputStream) null, null);
}
}
@Nullable @Nullable
@VisibleForTesting @VisibleForTesting
AutocryptHeader getValidAutocryptHeader(Message currentMessage) { AutocryptHeader getValidAutocryptHeader(Message currentMessage) {

View file

@ -17,10 +17,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import com.fsck.k9.crypto.AutocryptOperations;
import timber.log.Timber;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.crypto.AutocryptOperations;
import com.fsck.k9.crypto.MessageDecryptVerifier; import com.fsck.k9.crypto.MessageDecryptVerifier;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Body;
@ -48,6 +46,7 @@ import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation; import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
@ -67,7 +66,7 @@ public class MessageCryptoHelper {
private final String openPgpProviderPackage; private final String openPgpProviderPackage;
private final AutocryptOperations autocryptOperations; private final AutocryptOperations autocryptOperations;
private final Object callbackLock = new Object(); private final Object callbackLock = new Object();
private final Deque<CryptoPart> partsToDecryptOrVerify = new ArrayDeque<>(); private final Deque<CryptoPart> partsToProcess = new ArrayDeque<>();
@Nullable @Nullable
private MessageCryptoCallback callback; private MessageCryptoCallback callback;
@ -82,6 +81,7 @@ public class MessageCryptoHelper {
private CryptoPart currentCryptoPart; private CryptoPart currentCryptoPart;
private Intent currentCryptoResult; private Intent currentCryptoResult;
private Intent userInteractionResultIntent; private Intent userInteractionResultIntent;
private boolean firstPassStarted;
private boolean secondPassStarted; private boolean secondPassStarted;
private boolean thirdPassStarted; private boolean thirdPassStarted;
private CancelableBackgroundOperation cancelableBackgroundOperation; private CancelableBackgroundOperation cancelableBackgroundOperation;
@ -120,17 +120,17 @@ public class MessageCryptoHelper {
this.cachedDecryptionResult = cachedDecryptionResult; this.cachedDecryptionResult = cachedDecryptionResult;
this.callback = callback; this.callback = callback;
runFirstPass(); nextStep();
} }
private void runFirstPass() { private void findPartsForFirstPass() {
firstPassStarted = true;
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage); List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
processFoundEncryptedParts(encryptedParts); processFoundEncryptedParts(encryptedParts);
decryptOrVerifyNextPart();
} }
private void runSecondPass() { private void findPartsForSecondPass() {
secondPassStarted = true; secondPassStarted = true;
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
@ -138,23 +138,18 @@ public class MessageCryptoHelper {
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
addFoundInlinePgpParts(inlineParts); addFoundInlinePgpParts(inlineParts);
decryptOrVerifyNextPart();
} }
private void runThirdPass() { private void findPartsForThirdPass() {
thirdPassStarted = true; thirdPassStarted = true;
if (messageAnnotations.isEmpty()) { boolean noOtherCryptoPerformed = messageAnnotations.isEmpty();
if (autocryptOperations.hasAutocryptHeader(currentMessage)) { if (noOtherCryptoPerformed && autocryptOperations.hasAutocryptHeader(currentMessage)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
} }
} }
decryptOrVerifyNextPart();
}
private void processFoundEncryptedParts(List<Part> foundParts) { private void processFoundEncryptedParts(List<Part> foundParts) {
for (Part part : foundParts) { for (Part part : foundParts) {
if (!MessageHelper.isCompletePartAvailable(part)) { if (!MessageHelper.isCompletePartAvailable(part)) {
@ -163,7 +158,7 @@ public class MessageCryptoHelper {
} }
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) { if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
continue; continue;
} }
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart()); addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
@ -179,7 +174,7 @@ public class MessageCryptoHelper {
} }
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) { if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
continue; continue;
} }
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part); MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
@ -205,29 +200,34 @@ public class MessageCryptoHelper {
} }
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
} }
} }
private void decryptOrVerifyNextPart() { private void nextStep() {
if (isCancelled) { if (isCancelled) {
return; return;
} }
if (partsToDecryptOrVerify.isEmpty()) { while (partsToProcess.isEmpty()) {
runNextPassOrReturnResultToFragment(); boolean hadNextPass = findPartsForNextPass();
if (!hadNextPass) {
callbackReturnResult();
return;
}
}
if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService();
return; return;
} }
CryptoPart cryptoPart = partsToDecryptOrVerify.peekFirst(); currentCryptoPart = partsToProcess.peekFirst();
startDecryptingOrVerifyingPart(cryptoPart); if (currentCryptoPart.type == CryptoPartType.PLAIN_AUTOCRYPT) {
} processAutocryptHeaderForCurrentPart();
private void startDecryptingOrVerifyingPart(CryptoPart cryptoPart) {
if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService();
} else { } else {
decryptOrVerifyPart(cryptoPart); decryptOrVerifyCurrentPart();
} }
} }
@ -243,7 +243,7 @@ public class MessageCryptoHelper {
public void onBound(IOpenPgpService2 service) { public void onBound(IOpenPgpService2 service) {
openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service); openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service);
decryptOrVerifyNextPart(); nextStep();
} }
@Override @Override
@ -255,14 +255,13 @@ public class MessageCryptoHelper {
openPgpServiceConnection.bindToService(); openPgpServiceConnection.bindToService();
} }
private void decryptOrVerifyPart(CryptoPart cryptoPart) { private void decryptOrVerifyCurrentPart() {
currentCryptoPart = cryptoPart; Intent apiIntent = userInteractionResultIntent;
Intent decryptIntent = userInteractionResultIntent;
userInteractionResultIntent = null; userInteractionResultIntent = null;
if (decryptIntent == null) { if (apiIntent == null) {
decryptIntent = getDecryptVerifyIntent(); apiIntent = getDecryptVerifyIntent();
} }
decryptVerify(decryptIntent); decryptVerify(apiIntent);
} }
@NonNull @NonNull
@ -283,26 +282,24 @@ public class MessageCryptoHelper {
return decryptIntent; return decryptIntent;
} }
private void decryptVerify(Intent intent) { private void decryptVerify(Intent apiIntent) {
try { try {
CryptoPartType cryptoPartType = currentCryptoPart.type; CryptoPartType cryptoPartType = currentCryptoPart.type;
switch (cryptoPartType) { switch (cryptoPartType) {
case PGP_SIGNED: { case PGP_SIGNED: {
callAsyncDetachedVerify(intent); callAsyncDetachedVerify(apiIntent);
return; return;
} }
case PGP_ENCRYPTED: { case PGP_ENCRYPTED: {
callAsyncDecrypt(intent); callAsyncDecrypt(apiIntent);
return; return;
} }
case PGP_INLINE: { case PGP_INLINE: {
callAsyncInlineOperation(intent); callAsyncInlineOperation(apiIntent);
return;
}
case PLAIN_AUTOCRYPT: {
callAsyncParseAutocryptHeaderOperation();
return; return;
} }
case PLAIN_AUTOCRYPT:
throw new IllegalStateException("This part type must have been handled previously!");
} }
throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType); throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType);
@ -313,9 +310,20 @@ public class MessageCryptoHelper {
} }
} }
private void callAsyncParseAutocryptHeaderOperation() { private void processAutocryptHeaderForCurrentPart() {
// TODO make actually async (then callback to onCryptoFinished) Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
autocryptOperations.processCleartextMessage(openPgpApi, (Message) currentCryptoPart.part); boolean hasInlineKeyData = autocryptOperations.addAutocryptPeerUpdateToIntentIfPresent(
(Message) currentCryptoPart.part, intent);
if (hasInlineKeyData) {
Timber.d("Passing autocrypt data from plain mail to OpenPGP API");
// We don't care about the result here, so we just call this fire-and-forget wait to minimize delay
openPgpApi.executeApiAsync(intent, null, null, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
Timber.d("Autocrypt update OK!");
}
});
}
onCryptoFinished(); onCryptoFinished();
} }
@ -568,7 +576,7 @@ public class MessageCryptoHelper {
} }
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
userInteractionResultIntent = data; userInteractionResultIntent = data;
decryptOrVerifyNextPart(); nextStep();
} else { } else {
onCryptoOperationCanceled(); onCryptoOperationCanceled();
} }
@ -620,34 +628,39 @@ public class MessageCryptoHelper {
} }
private void onCryptoFinished() { private void onCryptoFinished() {
boolean currentPartIsFirstInQueue = partsToDecryptOrVerify.peekFirst() == currentCryptoPart; boolean currentPartIsFirstInQueue = partsToProcess.peekFirst() == currentCryptoPart;
if (!currentPartIsFirstInQueue) { if (!currentPartIsFirstInQueue) {
throw new IllegalStateException( throw new IllegalStateException(
"Trying to remove part from queue that is not the currently processed one!"); "Trying to remove part from queue that is not the currently processed one!");
} }
if (currentCryptoPart != null) { if (currentCryptoPart != null) {
partsToDecryptOrVerify.removeFirst(); partsToProcess.removeFirst();
currentCryptoPart = null; currentCryptoPart = null;
} else { } else {
Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!"); Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!");
} }
decryptOrVerifyNextPart(); nextStep();
} }
private void runNextPassOrReturnResultToFragment() { private boolean findPartsForNextPass() {
if (!firstPassStarted) {
findPartsForFirstPass();
return true;
}
if (!secondPassStarted) { if (!secondPassStarted) {
runSecondPass(); findPartsForSecondPass();
return; return true;
} }
if (!thirdPassStarted) { if (!thirdPassStarted) {
runThirdPass(); findPartsForThirdPass();
return; return true;
} }
callbackReturnResult();
return false;
} }
private void cleanupAfterProcessingFinished() { private void cleanupAfterProcessingFinished() {
partsToDecryptOrVerify.clear(); partsToProcess.clear();
openPgpApi = null; openPgpApi = null;
if (openPgpServiceConnection != null) { if (openPgpServiceConnection != null) {
openPgpServiceConnection.unbindFromService(); openPgpServiceConnection.unbindFromService();