1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-23 18:02:15 -05:00

early support for detached signatures

This commit is contained in:
Vincent Breitmoser 2015-01-29 19:01:44 +01:00
parent 38d3564c57
commit 712acf4481
7 changed files with 289 additions and 160 deletions

View File

@ -19,13 +19,13 @@ import static junit.framework.Assert.assertSame;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class MessageDecryptorTest { public class MessageDecryptVerifyerTest {
@Test @Test
public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception { public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
MimeMessage emptyMessage = new MimeMessage(); MimeMessage emptyMessage = new MimeMessage();
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(emptyMessage); List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(emptyMessage);
assertEquals(0, encryptedParts.size()); assertEquals(0, encryptedParts.size());
} }
@ -34,7 +34,7 @@ public class MessageDecryptorTest {
MimeMessage message = new MimeMessage(); MimeMessage message = new MimeMessage();
message.setBody(new TextBody("message text")); message.setBody(new TextBody("message text"));
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message); List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
assertEquals(0, encryptedParts.size()); assertEquals(0, encryptedParts.size());
} }
@ -45,7 +45,7 @@ public class MessageDecryptorTest {
mulitpartEncrypted.setSubType("encrypted"); mulitpartEncrypted.setSubType("encrypted");
MimeMessageHelper.setBody(message, mulitpartEncrypted); MimeMessageHelper.setBody(message, mulitpartEncrypted);
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message); List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
assertEquals(1, encryptedParts.size()); assertEquals(1, encryptedParts.size());
assertSame(message, encryptedParts.get(0)); assertSame(message, encryptedParts.get(0));
} }
@ -70,7 +70,7 @@ public class MessageDecryptorTest {
MimeBodyPart bodyPartThree = new MimeBodyPart(mulitpartEncryptedThree); MimeBodyPart bodyPartThree = new MimeBodyPart(mulitpartEncryptedThree);
multipartMixed.addBodyPart(bodyPartThree); multipartMixed.addBodyPart(bodyPartThree);
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message); List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
assertEquals(2, encryptedParts.size()); assertEquals(2, encryptedParts.size());
assertSame(bodyPartOne, encryptedParts.get(0)); assertSame(bodyPartOne, encryptedParts.get(0));
assertSame(bodyPartThree, encryptedParts.get(1)); assertSame(bodyPartThree, encryptedParts.get(1));

View File

@ -0,0 +1,106 @@
package com.fsck.k9.crypto;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;
public class MessageDecryptVerifyer {
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
private static final String MULTIPART_SIGNED = "multipart/signed";
private static final String PROTOCOL_PARAMETER = "protocol";
private static final String APPLICATION_PGP_ENCRYPTED = "application/pgp-encrypted";
private static final String APPLICATION_PGP_SIGNATURE = "application/pgp-signature";
public static List<Part> findEncryptedParts(Part startPart) {
List<Part> encryptedParts = new ArrayList<Part>();
Stack<Part> partsToCheck = new Stack<Part>();
partsToCheck.push(startPart);
while (!partsToCheck.isEmpty()) {
Part part = partsToCheck.pop();
String mimeType = part.getMimeType();
Body body = part.getBody();
if (MULTIPART_ENCRYPTED.equals(mimeType)) {
encryptedParts.add(part);
} else if (body instanceof Multipart) {
Multipart multipart = (Multipart) body;
for (int i = multipart.getCount() - 1; i >= 0; i--) {
BodyPart bodyPart = multipart.getBodyPart(i);
partsToCheck.push(bodyPart);
}
}
}
return encryptedParts;
}
public static List<Part> findSignedParts(Part startPart) {
List<Part> signedParts = new ArrayList<Part>();
Stack<Part> partsToCheck = new Stack<Part>();
partsToCheck.push(startPart);
while (!partsToCheck.isEmpty()) {
Part part = partsToCheck.pop();
String mimeType = part.getMimeType();
Body body = part.getBody();
if (MULTIPART_SIGNED.equals(mimeType)) {
signedParts.add(part);
} else if (body instanceof Multipart) {
Multipart multipart = (Multipart) body;
for (int i = multipart.getCount() - 1; i >= 0; i--) {
BodyPart bodyPart = multipart.getBodyPart(i);
partsToCheck.push(bodyPart);
}
}
}
return signedParts;
}
public static byte[] getSignatureData(Part part) throws IOException, MessagingException {
if (MULTIPART_SIGNED.equals(part.getMimeType())) {
Body body = part.getBody();
if (body instanceof Multipart) {
Multipart multi = (Multipart) body;
BodyPart signatureBody = multi.getBodyPart(1);
if (APPLICATION_PGP_SIGNATURE.equals(signatureBody.getMimeType())) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
signatureBody.getBody().writeTo(bos);
return bos.toByteArray();
}
}
}
return null;
}
public static boolean isPgpMimePart(Part part) {
return isPgpMimeSignedPart(part) || isPgpMimeEncryptedPart(part);
}
public static boolean isPgpMimeSignedPart(Part part) {
return MULTIPART_SIGNED.equals(part.getMimeType());
}
public static boolean isPgpMimeEncryptedPart(Part part) {
//FIXME: Doesn't work right now because LocalMessage.getContentType() doesn't load headers from database
// String contentType = part.getContentType();
// String protocol = MimeUtility.getHeaderParameter(contentType, PROTOCOL_PARAMETER);
// return APPLICATION_PGP_ENCRYPTED.equals(protocol);
return MULTIPART_ENCRYPTED.equals(part.getMimeType());
}
}

View File

@ -1,51 +0,0 @@
package com.fsck.k9.crypto;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeUtility;
public class MessageDecryptor {
private static final String MULTIPART_ENCRYPTED = "multipart/encrypted";
private static final String PROTOCOL_PARAMETER = "protocol";
private static final String APPLICATION_PGP_ENCRYPTED = "application/pgp-encrypted";
public static List<Part> findEncryptedParts(Part startPart) {
List<Part> encryptedParts = new ArrayList<Part>();
Stack<Part> partsToCheck = new Stack<Part>();
partsToCheck.push(startPart);
while (!partsToCheck.isEmpty()) {
Part part = partsToCheck.pop();
String mimeType = part.getMimeType();
Body body = part.getBody();
if (MULTIPART_ENCRYPTED.equals(mimeType)) {
encryptedParts.add(part);
} else if (body instanceof Multipart) {
Multipart multipart = (Multipart) body;
for (int i = multipart.getCount() - 1; i >= 0; i--) {
BodyPart bodyPart = multipart.getBodyPart(i);
partsToCheck.push(bodyPart);
}
}
}
return encryptedParts;
}
public static boolean isPgpMimeEncryptedPart(Part part) {
//FIXME: Doesn't work right now because LocalMessage.getContentType() doesn't load headers from database
// String contentType = part.getContentType();
// String protocol = MimeUtility.getHeaderParameter(contentType, PROTOCOL_PARAMETER);
// return APPLICATION_PGP_ENCRYPTED.equals(protocol);
return true;
}
}

View File

@ -24,13 +24,11 @@ import org.apache.james.mime4j.parser.MimeStreamParser;
import org.apache.james.mime4j.stream.BodyDescriptor; import org.apache.james.mime4j.stream.BodyDescriptor;
import org.apache.james.mime4j.stream.Field; import org.apache.james.mime4j.stream.Field;
import org.apache.james.mime4j.stream.MimeConfig; import org.apache.james.mime4j.stream.MimeConfig;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
public class DecryptStreamParser { public class DecryptStreamParser {
public static DecryptedBodyPart parse(InputStream inputStream) throws MessagingException, IOException { public static OpenPgpResultBodyPart parse(InputStream inputStream) throws MessagingException, IOException {
DecryptedBodyPart decryptedRootPart = new DecryptedBodyPart(); OpenPgpResultBodyPart decryptedRootPart = new OpenPgpResultBodyPart(true);
MimeConfig parserConfig = new MimeConfig(); MimeConfig parserConfig = new MimeConfig();
parserConfig.setMaxHeaderLen(-1); parserConfig.setMaxHeaderLen(-1);
@ -66,10 +64,10 @@ public class DecryptStreamParser {
private static class PartBuilder implements ContentHandler { private static class PartBuilder implements ContentHandler {
private final DecryptedBodyPart decryptedRootPart; private final OpenPgpResultBodyPart decryptedRootPart;
private final Stack<Object> stack = new Stack<Object>(); private final Stack<Object> stack = new Stack<Object>();
public PartBuilder(DecryptedBodyPart decryptedRootPart) throws MessagingException { public PartBuilder(OpenPgpResultBodyPart decryptedRootPart) throws MessagingException {
this.decryptedRootPart = decryptedRootPart; this.decryptedRootPart = decryptedRootPart;
} }
@ -182,28 +180,4 @@ public class DecryptStreamParser {
} }
} }
public static class DecryptedBodyPart extends MimeBodyPart {
private OpenPgpSignatureResult signatureResult;
private OpenPgpError error;
public DecryptedBodyPart() throws MessagingException {
// Do nothing
}
public OpenPgpSignatureResult getSignatureResult() {
return signatureResult;
}
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
this.signatureResult = signatureResult;
}
public OpenPgpError getError() {
return error;
}
public void setError(OpenPgpError error) {
this.error = error;
}
}
} }

View File

@ -3,11 +3,9 @@ package com.fsck.k9.mailstore;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.util.Log;
import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.crypto.MessageDecryptor; import com.fsck.k9.crypto.MessageDecryptVerifyer;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.BodyPart;
@ -20,7 +18,6 @@ import com.fsck.k9.mail.internet.MessageExtractor;
import com.fsck.k9.mail.internet.MimeHeader; import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.internet.Viewable; import com.fsck.k9.mail.internet.Viewable;
import com.fsck.k9.mailstore.DecryptStreamParser.DecryptedBodyPart;
import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer; import com.fsck.k9.mailstore.MessageViewInfo.MessageViewContainer;
import com.fsck.k9.provider.AttachmentProvider; import com.fsck.k9.provider.AttachmentProvider;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
@ -443,7 +440,7 @@ public class LocalMessageExtractor {
// TODO fill from part // TODO fill from part
OpenPgpSignatureResult pgpResult = getSignatureResultForPart(part); OpenPgpSignatureResult pgpResult = getSignatureResultForPart(part);
OpenPgpError pgpError = null; OpenPgpError pgpError = null;
boolean wasEncrypted = false; boolean wasEncrypted = getPartWasEncrypted(part);
PendingIntent pendingIntent = null; PendingIntent pendingIntent = null;
containers.add(new MessageViewContainer( containers.add(new MessageViewContainer(
@ -454,6 +451,10 @@ public class LocalMessageExtractor {
return new MessageViewInfo(containers, message); return new MessageViewInfo(containers, message);
} }
private static boolean getPartWasEncrypted(Part part) {
return (part instanceof OpenPgpResultBodyPart) && ((OpenPgpResultBodyPart) part).wasEncrypted();
}
public static List<Part> getCryptPieces(Part part) throws MessagingException { public static List<Part> getCryptPieces(Part part) throws MessagingException {
ArrayList<Part> parts = new ArrayList<Part>(); ArrayList<Part> parts = new ArrayList<Part>();
if (!getCryptSubPieces(part, parts)) { if (!getCryptSubPieces(part, parts)) {
@ -477,6 +478,9 @@ public class LocalMessageExtractor {
parts.add(part); parts.add(part);
return true; return true;
} }
} else if (MessageDecryptVerifyer.isPgpMimeSignedPart(part)) {
parts.add(part);
return true;
} else if (isPgpMimeDecryptedPart(part)) { } else if (isPgpMimeDecryptedPart(part)) {
parts.add(multi.getBodyPart(2)); parts.add(multi.getBodyPart(2));
return true; return true;
@ -488,14 +492,21 @@ public class LocalMessageExtractor {
public static boolean isPgpMimeDecryptedPart (Part part) { public static boolean isPgpMimeDecryptedPart (Part part) {
Body body = part.getBody(); Body body = part.getBody();
return (body instanceof Multipart) return (body instanceof Multipart)
&& MessageDecryptor.isPgpMimeEncryptedPart(part) && MessageDecryptVerifyer.isPgpMimeEncryptedPart(part)
&& ((Multipart) part.getBody()).getBodyParts().size() == 3; && ((Multipart) part.getBody()).getCount() == 3;
} }
private static OpenPgpSignatureResult getSignatureResultForPart(Part part) { private static OpenPgpSignatureResult getSignatureResultForPart(Part part) {
if (part instanceof DecryptedBodyPart) { if (part instanceof OpenPgpResultBodyPart) {
DecryptedBodyPart decryptedBodyPart = (DecryptedBodyPart) part; OpenPgpResultBodyPart openPgpResultBodyPart = (OpenPgpResultBodyPart) part;
return decryptedBodyPart.getSignatureResult(); return openPgpResultBodyPart.getSignatureResult();
}
if (MessageDecryptVerifyer.isPgpMimeSignedPart(part)) {
Multipart multi = (Multipart) part.getBody();
if (multi.getCount() == 3 && multi.getBodyPart(2) instanceof OpenPgpResultBodyPart) {
OpenPgpResultBodyPart openPgpResultBodyPart = (OpenPgpResultBodyPart) multi.getBodyPart(2);
return openPgpResultBodyPart.getSignatureResult();
}
} }
return NO_SIGNATURE_RESULT; return NO_SIGNATURE_RESULT;

View File

@ -0,0 +1,38 @@
package com.fsck.k9.mailstore;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeBodyPart;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
public class OpenPgpResultBodyPart extends MimeBodyPart {
private boolean wasEncrypted;
private OpenPgpSignatureResult signatureResult;
private OpenPgpError error;
public OpenPgpResultBodyPart(boolean wasEncrypted) throws MessagingException {
this.wasEncrypted = wasEncrypted;
}
public OpenPgpSignatureResult getSignatureResult() {
return signatureResult;
}
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
this.signatureResult = signatureResult;
}
public OpenPgpError getError() {
return error;
}
public void setError(OpenPgpError error) {
this.error = error;
}
public boolean wasEncrypted() {
return wasEncrypted;
}
}

View File

@ -47,7 +47,7 @@ import com.fsck.k9.activity.MessageList;
import com.fsck.k9.activity.MessageReference; import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.crypto.MessageDecryptor; import com.fsck.k9.crypto.MessageDecryptVerifyer;
import com.fsck.k9.crypto.OpenPgpApiHelper; import com.fsck.k9.crypto.OpenPgpApiHelper;
import com.fsck.k9.crypto.PgpData; import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.fragment.ConfirmationDialogFragment; import com.fsck.k9.fragment.ConfirmationDialogFragment;
@ -64,7 +64,7 @@ import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
import com.fsck.k9.mailstore.AttachmentViewInfo; import com.fsck.k9.mailstore.AttachmentViewInfo;
import com.fsck.k9.mailstore.DecryptStreamParser; import com.fsck.k9.mailstore.DecryptStreamParser;
import com.fsck.k9.mailstore.DecryptStreamParser.DecryptedBodyPart; import com.fsck.k9.mailstore.OpenPgpResultBodyPart;
import com.fsck.k9.mailstore.LocalMessage; import com.fsck.k9.mailstore.LocalMessage;
import com.fsck.k9.mailstore.MessageViewInfo; import com.fsck.k9.mailstore.MessageViewInfo;
import com.fsck.k9.ui.message.DecodeMessageLoader; import com.fsck.k9.ui.message.DecodeMessageLoader;
@ -139,10 +139,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback(); private LoaderCallbacks<MessageViewInfo> decodeMessageLoaderCallback = new DecodeMessageLoaderCallback();
private MessageViewInfo messageViewInfo; private MessageViewInfo messageViewInfo;
private AttachmentViewInfo currentAttachmentViewInfo; private AttachmentViewInfo currentAttachmentViewInfo;
private Deque<Part> partsToDecrypt; private Deque<Part> partsToDecryptOrVerify;
private OpenPgpApi openPgpApi; private OpenPgpApi openPgpApi;
private Part currentlyDecryptingPart; private Part currentlyDecrypringOrVerifyingPart;
private Intent currentDecryptingResult; private Intent currentCryptoResult;
@Override @Override
@ -257,38 +257,42 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
if (message.isBodyMissing()) { if (message.isBodyMissing()) {
startDownloadingMessageBody(message); startDownloadingMessageBody(message);
} else { } else {
decryptMessagePartsIfNecessary(message); decryptOrVerifyMessagePartsIfNecessary(message);
} }
} }
private void decryptMessagePartsIfNecessary(LocalMessage message) { private void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) {
List<Part> encryptedParts = MessageDecryptor.findEncryptedParts(message); List<Part> encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message);
if (!encryptedParts.isEmpty()) { List<Part> signedParts = MessageDecryptVerifyer.findSignedParts(message);
partsToDecrypt = new ArrayDeque<Part>(encryptedParts); if (!encryptedParts.isEmpty() || !signedParts.isEmpty()) {
decryptNextPartOrStartExtractingTextAndAttachments(); partsToDecryptOrVerify = new ArrayDeque<Part>();
partsToDecryptOrVerify.addAll(encryptedParts);
partsToDecryptOrVerify.addAll(signedParts);
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
} else { } else {
startExtractingTextAndAttachments(message); startExtractingTextAndAttachments(message);
} }
} }
private void decryptNextPartOrStartExtractingTextAndAttachments() { private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() {
if (partsToDecrypt.isEmpty()) { if (!partsToDecryptOrVerify.isEmpty()) {
startExtractingTextAndAttachments(mMessage);
Part part = partsToDecryptOrVerify.peekFirst();
if (MessageDecryptVerifyer.isPgpMimePart(part)) {
startDecryptingOrVerifyingPart(part);
} else {
partsToDecryptOrVerify.removeFirst();
decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
}
return; return;
} }
Part part = partsToDecrypt.peekFirst(); startExtractingTextAndAttachments(mMessage);
if (MessageDecryptor.isPgpMimeEncryptedPart(part)) {
startDecryptingPart(part);
} else {
// Note: We currently only support PGP/MIME multipart/encrypted parts
partsToDecrypt.removeFirst();
decryptNextPartOrStartExtractingTextAndAttachments();
}
} }
private void startDecryptingPart(Part part) { private void startDecryptingOrVerifyingPart(Part part) {
Multipart multipart = (Multipart) part.getBody(); Multipart multipart = (Multipart) part.getBody();
if (multipart == null) { if (multipart == null) {
throw new RuntimeException("Downloading missing parts before decryption isn't supported yet"); throw new RuntimeException("Downloading missing parts before decryption isn't supported yet");
@ -297,7 +301,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
if (!isBoundToCryptoProviderService()) { if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService(); connectToCryptoProviderService();
} else { } else {
decryptPart(part); decryptOrVerifyPart(part);
} }
} }
@ -313,7 +317,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
public void onBound(IOpenPgpService service) { public void onBound(IOpenPgpService service) {
openPgpApi = new OpenPgpApi(getContext(), service); openPgpApi = new OpenPgpApi(getContext(), service);
decryptNextPartOrStartExtractingTextAndAttachments(); decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
} }
@Override @Override
@ -323,8 +327,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
}).bindToService(); }).bindToService();
} }
private void decryptPart(Part part) { private void decryptOrVerifyPart(Part part) {
currentlyDecryptingPart = part; currentlyDecrypringOrVerifyingPart = part;
decryptVerify(new Intent()); decryptVerify(new Intent());
} }
@ -336,24 +340,66 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName); intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName);
try { try {
final CountDownLatch latch = new CountDownLatch(1);
PipedInputStream pipedInputStream = getPipedInputStreamForEncryptedData(); PipedInputStream pipedInputStream;
PipedOutputStream decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch); PipedOutputStream decryptedOutputStream;
final CountDownLatch latch;
if (MessageDecryptVerifyer.isPgpMimeSignedPart(currentlyDecrypringOrVerifyingPart)) {
pipedInputStream = getPipedInputStreamForSignedData();
byte[] signatureData = MessageDecryptVerifyer.getSignatureData(currentlyDecrypringOrVerifyingPart);
intent.putExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE, signatureData);
decryptedOutputStream = null;
latch = null;
} else {
pipedInputStream = getPipedInputStreamForEncryptedData();
latch = new CountDownLatch(1);
decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch);
}
openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() { openPgpApi.executeApiAsync(intent, pipedInputStream, decryptedOutputStream, new IOpenPgpCallback() {
@Override @Override
public void onReturn(Intent result) { public void onReturn(Intent result) {
currentCryptoResult = result;
if (latch != null) {
Log.d(K9.LOG_TAG, "on result!"); Log.d(K9.LOG_TAG, "on result!");
currentDecryptingResult = result;
latch.countDown(); latch.countDown();
return;
}
onCryptoConverge(null);
} }
}); });
} catch (IOException e) { } catch (IOException e) {
Log.e(K9.LOG_TAG, "IOException", e); Log.e(K9.LOG_TAG, "IOException", e);
} catch (MessagingException e) {
Log.e(K9.LOG_TAG, "MessagingException", e);
} }
} }
private PipedInputStream getPipedInputStreamForSignedData() throws IOException {
PipedInputStream pipedInputStream = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(pipedInputStream);
new Thread(new Runnable() {
@Override
public void run() {
try {
Multipart multipartSignedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
BodyPart signatureBodyPart = multipartSignedMultipart.getBodyPart(0);
Log.d(K9.LOG_TAG, "signed data type: " + signatureBodyPart.getMimeType());
signatureBodyPart.writeTo(out);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Exception while writing message to crypto provider", e);
}
}
}).start();
return pipedInputStream;
}
private PipedInputStream getPipedInputStreamForEncryptedData() throws IOException { private PipedInputStream getPipedInputStreamForEncryptedData() throws IOException {
PipedInputStream pipedInputStream = new PipedInputStream(); PipedInputStream pipedInputStream = new PipedInputStream();
@ -362,7 +408,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
@Override @Override
public void run() { public void run() {
try { try {
Multipart multipartEncryptedMultipart = (Multipart) currentlyDecryptingPart.getBody(); Multipart multipartEncryptedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1); BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1);
Body encryptionPayloadBody = encryptionPayloadPart.getBody(); Body encryptionPayloadBody = encryptionPayloadPart.getBody();
encryptionPayloadBody.writeTo(out); encryptionPayloadBody.writeTo(out);
@ -378,10 +424,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException { private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException {
PipedOutputStream decryptedOutputStream = new PipedOutputStream(); PipedOutputStream decryptedOutputStream = new PipedOutputStream();
final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream); final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream);
new AsyncTask<Void, Void, DecryptedBodyPart>() { new AsyncTask<Void, Void, OpenPgpResultBodyPart>() {
@Override @Override
protected DecryptedBodyPart doInBackground(Void... params) { protected OpenPgpResultBodyPart doInBackground(Void... params) {
DecryptedBodyPart decryptedPart = null; OpenPgpResultBodyPart decryptedPart = null;
try { try {
decryptedPart = DecryptStreamParser.parse(decryptedInputStream); decryptedPart = DecryptStreamParser.parse(decryptedInputStream);
@ -396,21 +442,21 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
} }
@Override @Override
protected void onPostExecute(DecryptedBodyPart decryptedPart) { protected void onPostExecute(OpenPgpResultBodyPart decryptedPart) {
onDecryptionConverge(decryptedPart); onCryptoConverge(decryptedPart);
} }
}.execute(); }.execute();
return decryptedOutputStream; return decryptedOutputStream;
} }
private void onDecryptionConverge (DecryptedBodyPart decryptedPart) { private void onCryptoConverge(OpenPgpResultBodyPart openPgpResultBodyPart) {
try { try {
if (currentDecryptingResult == null) { if (currentCryptoResult == null) {
Log.e(K9.LOG_TAG, "Internal error: we should have a result here!"); Log.e(K9.LOG_TAG, "Internal error: we should have a result here!");
return; return;
} }
int resultCode = currentDecryptingResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE); int resultCode = currentCryptoResult.getIntExtra(OpenPgpApi.RESULT_CODE, INVALID_OPENPGP_RESULT_CODE);
if (K9.DEBUG) { if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode); Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode);
} }
@ -421,7 +467,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
break; break;
} }
case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: {
PendingIntent pendingIntent = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT); PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
if (pendingIntent == null) { if (pendingIntent == null) {
throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!"); throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!");
} }
@ -435,62 +481,68 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
break; break;
} }
case OpenPgpApi.RESULT_CODE_ERROR: { case OpenPgpApi.RESULT_CODE_ERROR: {
OpenPgpError error = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR); OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
if (K9.DEBUG) { if (K9.DEBUG) {
Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage()); Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage());
} }
onDecryptionFailed(error); onCryptoFailed(error);
break; break;
} }
case OpenPgpApi.RESULT_CODE_SUCCESS: { case OpenPgpApi.RESULT_CODE_SUCCESS: {
if (openPgpResultBodyPart == null) {
openPgpResultBodyPart = new OpenPgpResultBodyPart(false);
}
OpenPgpSignatureResult signatureResult = OpenPgpSignatureResult signatureResult =
currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
decryptedPart.setSignatureResult(signatureResult); openPgpResultBodyPart.setSignatureResult(signatureResult);
onDecryptionSuccess(decryptedPart); onCryptoSuccess(openPgpResultBodyPart);
break; break;
} }
} }
} catch (MessagingException e) {
// catching the empty OpenPgpResultBodyPart constructor above - this can't actually happen
Log.e(K9.LOG_TAG, "This shouldn't happen", e);
} finally { } finally {
currentDecryptingResult = null; currentCryptoResult = null;
} }
} }
public void handleCryptoResult(int resultCode, Intent data) { public void handleCryptoResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
decryptNextPartOrStartExtractingTextAndAttachments(); decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
} else { } else {
//FIXME: don't pass null //FIXME: don't pass null
onDecryptionFailed(null); onCryptoFailed(null);
} }
} }
private void onDecryptionSuccess(DecryptedBodyPart decryptedPart) { private void onCryptoSuccess(OpenPgpResultBodyPart decryptedPart) {
addDecryptedPartToMessage(decryptedPart); addOpenPgpResultPartToMessage(decryptedPart);
onDecryptionFinished(); onCryptoFinished();
} }
private void addDecryptedPartToMessage(DecryptedBodyPart decryptedPart) { private void addOpenPgpResultPartToMessage(OpenPgpResultBodyPart decryptedPart) {
Multipart multipart = (Multipart) currentlyDecryptingPart.getBody(); Multipart multipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody();
multipart.addBodyPart(decryptedPart); multipart.addBodyPart(decryptedPart);
} }
private void onDecryptionFailed(OpenPgpError error) { private void onCryptoFailed(OpenPgpError error) {
try { try {
DecryptedBodyPart errorPart = new DecryptedBodyPart(); OpenPgpResultBodyPart errorPart = new OpenPgpResultBodyPart(false);
errorPart.setError(error); errorPart.setError(error);
addDecryptedPartToMessage(errorPart); addOpenPgpResultPartToMessage(errorPart);
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "This shouldn't happen", e); Log.e(K9.LOG_TAG, "This shouldn't happen", e);
} }
onDecryptionFinished(); onCryptoFinished();
} }
private void onDecryptionFinished() { private void onCryptoFinished() {
partsToDecrypt.removeFirst(); partsToDecryptOrVerify.removeFirst();
decryptNextPartOrStartExtractingTextAndAttachments(); decryptOrVerifyNextPartOrStartExtractingTextAndAttachments();
} }
private void onLoadMessageFromDatabaseFailed() { private void onLoadMessageFromDatabaseFailed() {
@ -527,7 +579,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF
} }
private void onDecodeMessageFinished(MessageViewInfo messageContainer) { private void onDecodeMessageFinished(MessageViewInfo messageContainer) {
//TODO: handle decryption and signature verification
this.messageViewInfo = messageContainer; this.messageViewInfo = messageContainer;
showMessage(messageContainer); showMessage(messageContainer);
} }