From 712acf4481dabb4bd5b4afa9d0d8880ff1da685d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 29 Jan 2015 19:01:44 +0100 Subject: [PATCH] early support for detached signatures --- ...t.java => MessageDecryptVerifyerTest.java} | 10 +- .../k9/crypto/MessageDecryptVerifyer.java | 106 +++++++++++ .../com/fsck/k9/crypto/MessageDecryptor.java | 51 ----- .../k9/mailstore/DecryptStreamParser.java | 34 +--- .../k9/mailstore/LocalMessageExtractor.java | 31 ++- .../k9/mailstore/OpenPgpResultBodyPart.java | 38 ++++ .../ui/messageview/MessageViewFragment.java | 179 +++++++++++------- 7 files changed, 289 insertions(+), 160 deletions(-) rename k9mail/src/androidTest/java/com/fsck/k9/crypto/{MessageDecryptorTest.java => MessageDecryptVerifyerTest.java} (86%) create mode 100644 k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifyer.java delete mode 100644 k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptor.java create mode 100644 k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultBodyPart.java diff --git a/k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptorTest.java b/k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptVerifyerTest.java similarity index 86% rename from k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptorTest.java rename to k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptVerifyerTest.java index 17782d318..4f298f723 100644 --- a/k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptorTest.java +++ b/k9mail/src/androidTest/java/com/fsck/k9/crypto/MessageDecryptVerifyerTest.java @@ -19,13 +19,13 @@ import static junit.framework.Assert.assertSame; @RunWith(AndroidJUnit4.class) -public class MessageDecryptorTest { +public class MessageDecryptVerifyerTest { @Test public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception { MimeMessage emptyMessage = new MimeMessage(); - List encryptedParts = MessageDecryptor.findEncryptedParts(emptyMessage); + List encryptedParts = MessageDecryptVerifyer.findEncryptedParts(emptyMessage); assertEquals(0, encryptedParts.size()); } @@ -34,7 +34,7 @@ public class MessageDecryptorTest { MimeMessage message = new MimeMessage(); message.setBody(new TextBody("message text")); - List encryptedParts = MessageDecryptor.findEncryptedParts(message); + List encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message); assertEquals(0, encryptedParts.size()); } @@ -45,7 +45,7 @@ public class MessageDecryptorTest { mulitpartEncrypted.setSubType("encrypted"); MimeMessageHelper.setBody(message, mulitpartEncrypted); - List encryptedParts = MessageDecryptor.findEncryptedParts(message); + List encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message); assertEquals(1, encryptedParts.size()); assertSame(message, encryptedParts.get(0)); } @@ -70,7 +70,7 @@ public class MessageDecryptorTest { MimeBodyPart bodyPartThree = new MimeBodyPart(mulitpartEncryptedThree); multipartMixed.addBodyPart(bodyPartThree); - List encryptedParts = MessageDecryptor.findEncryptedParts(message); + List encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message); assertEquals(2, encryptedParts.size()); assertSame(bodyPartOne, encryptedParts.get(0)); assertSame(bodyPartThree, encryptedParts.get(1)); diff --git a/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifyer.java b/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifyer.java new file mode 100644 index 000000000..92575e007 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifyer.java @@ -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 findEncryptedParts(Part startPart) { + List encryptedParts = new ArrayList(); + Stack partsToCheck = new Stack(); + 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 findSignedParts(Part startPart) { + List signedParts = new ArrayList(); + Stack partsToCheck = new Stack(); + 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()); + } + +} diff --git a/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptor.java b/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptor.java deleted file mode 100644 index 54a75570c..000000000 --- a/k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptor.java +++ /dev/null @@ -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 findEncryptedParts(Part startPart) { - List encryptedParts = new ArrayList(); - Stack partsToCheck = new Stack(); - 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; - } -} diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/DecryptStreamParser.java b/k9mail/src/main/java/com/fsck/k9/mailstore/DecryptStreamParser.java index 13cd6c87d..34b29b84e 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/DecryptStreamParser.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/DecryptStreamParser.java @@ -24,13 +24,11 @@ import org.apache.james.mime4j.parser.MimeStreamParser; import org.apache.james.mime4j.stream.BodyDescriptor; import org.apache.james.mime4j.stream.Field; import org.apache.james.mime4j.stream.MimeConfig; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpSignatureResult; public class DecryptStreamParser { - public static DecryptedBodyPart parse(InputStream inputStream) throws MessagingException, IOException { - DecryptedBodyPart decryptedRootPart = new DecryptedBodyPart(); + public static OpenPgpResultBodyPart parse(InputStream inputStream) throws MessagingException, IOException { + OpenPgpResultBodyPart decryptedRootPart = new OpenPgpResultBodyPart(true); MimeConfig parserConfig = new MimeConfig(); parserConfig.setMaxHeaderLen(-1); @@ -66,10 +64,10 @@ public class DecryptStreamParser { private static class PartBuilder implements ContentHandler { - private final DecryptedBodyPart decryptedRootPart; + private final OpenPgpResultBodyPart decryptedRootPart; private final Stack stack = new Stack(); - public PartBuilder(DecryptedBodyPart decryptedRootPart) throws MessagingException { + public PartBuilder(OpenPgpResultBodyPart decryptedRootPart) throws MessagingException { 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; - } - } } diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java index ffda014de..ad17bcc2f 100644 --- a/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/LocalMessageExtractor.java @@ -3,11 +3,9 @@ package com.fsck.k9.mailstore; import android.app.PendingIntent; import android.content.Context; import android.net.Uri; -import android.util.Log; -import com.fsck.k9.K9; 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.Body; 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.MimeUtility; 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.provider.AttachmentProvider; import org.openintents.openpgp.OpenPgpError; @@ -443,7 +440,7 @@ public class LocalMessageExtractor { // TODO fill from part OpenPgpSignatureResult pgpResult = getSignatureResultForPart(part); OpenPgpError pgpError = null; - boolean wasEncrypted = false; + boolean wasEncrypted = getPartWasEncrypted(part); PendingIntent pendingIntent = null; containers.add(new MessageViewContainer( @@ -454,6 +451,10 @@ public class LocalMessageExtractor { return new MessageViewInfo(containers, message); } + private static boolean getPartWasEncrypted(Part part) { + return (part instanceof OpenPgpResultBodyPart) && ((OpenPgpResultBodyPart) part).wasEncrypted(); + } + public static List getCryptPieces(Part part) throws MessagingException { ArrayList parts = new ArrayList(); if (!getCryptSubPieces(part, parts)) { @@ -477,6 +478,9 @@ public class LocalMessageExtractor { parts.add(part); return true; } + } else if (MessageDecryptVerifyer.isPgpMimeSignedPart(part)) { + parts.add(part); + return true; } else if (isPgpMimeDecryptedPart(part)) { parts.add(multi.getBodyPart(2)); return true; @@ -488,14 +492,21 @@ public class LocalMessageExtractor { public static boolean isPgpMimeDecryptedPart (Part part) { Body body = part.getBody(); return (body instanceof Multipart) - && MessageDecryptor.isPgpMimeEncryptedPart(part) - && ((Multipart) part.getBody()).getBodyParts().size() == 3; + && MessageDecryptVerifyer.isPgpMimeEncryptedPart(part) + && ((Multipart) part.getBody()).getCount() == 3; } private static OpenPgpSignatureResult getSignatureResultForPart(Part part) { - if (part instanceof DecryptedBodyPart) { - DecryptedBodyPart decryptedBodyPart = (DecryptedBodyPart) part; - return decryptedBodyPart.getSignatureResult(); + if (part instanceof OpenPgpResultBodyPart) { + OpenPgpResultBodyPart openPgpResultBodyPart = (OpenPgpResultBodyPart) part; + 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; diff --git a/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultBodyPart.java b/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultBodyPart.java new file mode 100644 index 000000000..555966f90 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/mailstore/OpenPgpResultBodyPart.java @@ -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; + } +} diff --git a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java index 3deb3ca4a..1145de324 100644 --- a/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java +++ b/k9mail/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.java @@ -47,7 +47,7 @@ import com.fsck.k9.activity.MessageList; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.controller.MessagingController; 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.PgpData; 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.mailstore.AttachmentViewInfo; 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.MessageViewInfo; import com.fsck.k9.ui.message.DecodeMessageLoader; @@ -139,10 +139,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF private LoaderCallbacks decodeMessageLoaderCallback = new DecodeMessageLoaderCallback(); private MessageViewInfo messageViewInfo; private AttachmentViewInfo currentAttachmentViewInfo; - private Deque partsToDecrypt; + private Deque partsToDecryptOrVerify; private OpenPgpApi openPgpApi; - private Part currentlyDecryptingPart; - private Intent currentDecryptingResult; + private Part currentlyDecrypringOrVerifyingPart; + private Intent currentCryptoResult; @Override @@ -257,38 +257,42 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF if (message.isBodyMissing()) { startDownloadingMessageBody(message); } else { - decryptMessagePartsIfNecessary(message); + decryptOrVerifyMessagePartsIfNecessary(message); } } - private void decryptMessagePartsIfNecessary(LocalMessage message) { - List encryptedParts = MessageDecryptor.findEncryptedParts(message); - if (!encryptedParts.isEmpty()) { - partsToDecrypt = new ArrayDeque(encryptedParts); - decryptNextPartOrStartExtractingTextAndAttachments(); + private void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) { + List encryptedParts = MessageDecryptVerifyer.findEncryptedParts(message); + List signedParts = MessageDecryptVerifyer.findSignedParts(message); + if (!encryptedParts.isEmpty() || !signedParts.isEmpty()) { + partsToDecryptOrVerify = new ArrayDeque(); + partsToDecryptOrVerify.addAll(encryptedParts); + partsToDecryptOrVerify.addAll(signedParts); + decryptOrVerifyNextPartOrStartExtractingTextAndAttachments(); } else { startExtractingTextAndAttachments(message); } } - private void decryptNextPartOrStartExtractingTextAndAttachments() { - if (partsToDecrypt.isEmpty()) { - startExtractingTextAndAttachments(mMessage); + private void decryptOrVerifyNextPartOrStartExtractingTextAndAttachments() { + if (!partsToDecryptOrVerify.isEmpty()) { + + Part part = partsToDecryptOrVerify.peekFirst(); + if (MessageDecryptVerifyer.isPgpMimePart(part)) { + startDecryptingOrVerifyingPart(part); + } else { + partsToDecryptOrVerify.removeFirst(); + decryptOrVerifyNextPartOrStartExtractingTextAndAttachments(); + } + return; } - Part part = partsToDecrypt.peekFirst(); - if (MessageDecryptor.isPgpMimeEncryptedPart(part)) { - startDecryptingPart(part); - } else { - // Note: We currently only support PGP/MIME multipart/encrypted parts + startExtractingTextAndAttachments(mMessage); - partsToDecrypt.removeFirst(); - decryptNextPartOrStartExtractingTextAndAttachments(); - } } - private void startDecryptingPart(Part part) { + private void startDecryptingOrVerifyingPart(Part part) { Multipart multipart = (Multipart) part.getBody(); if (multipart == null) { 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()) { connectToCryptoProviderService(); } else { - decryptPart(part); + decryptOrVerifyPart(part); } } @@ -313,7 +317,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF public void onBound(IOpenPgpService service) { openPgpApi = new OpenPgpApi(getContext(), service); - decryptNextPartOrStartExtractingTextAndAttachments(); + decryptOrVerifyNextPartOrStartExtractingTextAndAttachments(); } @Override @@ -323,8 +327,8 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF }).bindToService(); } - private void decryptPart(Part part) { - currentlyDecryptingPart = part; + private void decryptOrVerifyPart(Part part) { + currentlyDecrypringOrVerifyingPart = part; decryptVerify(new Intent()); } @@ -336,24 +340,66 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF intent.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, accountName); try { - final CountDownLatch latch = new CountDownLatch(1); - PipedInputStream pipedInputStream = getPipedInputStreamForEncryptedData(); - PipedOutputStream decryptedOutputStream = getPipedOutputStreamForDecryptedData(latch); + PipedInputStream pipedInputStream; + 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() { @Override public void onReturn(Intent result) { - Log.d(K9.LOG_TAG, "on result!"); - currentDecryptingResult = result; - latch.countDown(); + currentCryptoResult = result; + + if (latch != null) { + Log.d(K9.LOG_TAG, "on result!"); + latch.countDown(); + return; + } + + onCryptoConverge(null); } }); } catch (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 { PipedInputStream pipedInputStream = new PipedInputStream(); @@ -362,7 +408,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF @Override public void run() { try { - Multipart multipartEncryptedMultipart = (Multipart) currentlyDecryptingPart.getBody(); + Multipart multipartEncryptedMultipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody(); BodyPart encryptionPayloadPart = multipartEncryptedMultipart.getBodyPart(1); Body encryptionPayloadBody = encryptionPayloadPart.getBody(); encryptionPayloadBody.writeTo(out); @@ -378,10 +424,10 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF private PipedOutputStream getPipedOutputStreamForDecryptedData(final CountDownLatch latch) throws IOException { PipedOutputStream decryptedOutputStream = new PipedOutputStream(); final PipedInputStream decryptedInputStream = new PipedInputStream(decryptedOutputStream); - new AsyncTask() { + new AsyncTask() { @Override - protected DecryptedBodyPart doInBackground(Void... params) { - DecryptedBodyPart decryptedPart = null; + protected OpenPgpResultBodyPart doInBackground(Void... params) { + OpenPgpResultBodyPart decryptedPart = null; try { decryptedPart = DecryptStreamParser.parse(decryptedInputStream); @@ -396,21 +442,21 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF } @Override - protected void onPostExecute(DecryptedBodyPart decryptedPart) { - onDecryptionConverge(decryptedPart); + protected void onPostExecute(OpenPgpResultBodyPart decryptedPart) { + onCryptoConverge(decryptedPart); } }.execute(); return decryptedOutputStream; } - private void onDecryptionConverge (DecryptedBodyPart decryptedPart) { + private void onCryptoConverge(OpenPgpResultBodyPart openPgpResultBodyPart) { try { - if (currentDecryptingResult == null) { + if (currentCryptoResult == null) { Log.e(K9.LOG_TAG, "Internal error: we should have a result here!"); 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) { Log.d(K9.LOG_TAG, "OpenPGP API decryptVerify result code: " + resultCode); } @@ -421,7 +467,7 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF break; } case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: { - PendingIntent pendingIntent = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + PendingIntent pendingIntent = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_INTENT); if (pendingIntent == null) { throw new AssertionError("Expecting PendingIntent on USER_INTERACTION_REQUIRED!"); } @@ -435,62 +481,68 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF break; } case OpenPgpApi.RESULT_CODE_ERROR: { - OpenPgpError error = currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + OpenPgpError error = currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_ERROR); if (K9.DEBUG) { Log.w(K9.LOG_TAG, "OpenPGP API error: " + error.getMessage()); } - onDecryptionFailed(error); + onCryptoFailed(error); break; } case OpenPgpApi.RESULT_CODE_SUCCESS: { + if (openPgpResultBodyPart == null) { + openPgpResultBodyPart = new OpenPgpResultBodyPart(false); + } OpenPgpSignatureResult signatureResult = - currentDecryptingResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); - decryptedPart.setSignatureResult(signatureResult); + currentCryptoResult.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); + openPgpResultBodyPart.setSignatureResult(signatureResult); - onDecryptionSuccess(decryptedPart); + onCryptoSuccess(openPgpResultBodyPart); 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 { - currentDecryptingResult = null; + currentCryptoResult = null; } } public void handleCryptoResult(int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { - decryptNextPartOrStartExtractingTextAndAttachments(); + decryptOrVerifyNextPartOrStartExtractingTextAndAttachments(); } else { //FIXME: don't pass null - onDecryptionFailed(null); + onCryptoFailed(null); } } - private void onDecryptionSuccess(DecryptedBodyPart decryptedPart) { - addDecryptedPartToMessage(decryptedPart); - onDecryptionFinished(); + private void onCryptoSuccess(OpenPgpResultBodyPart decryptedPart) { + addOpenPgpResultPartToMessage(decryptedPart); + onCryptoFinished(); } - private void addDecryptedPartToMessage(DecryptedBodyPart decryptedPart) { - Multipart multipart = (Multipart) currentlyDecryptingPart.getBody(); + private void addOpenPgpResultPartToMessage(OpenPgpResultBodyPart decryptedPart) { + Multipart multipart = (Multipart) currentlyDecrypringOrVerifyingPart.getBody(); multipart.addBodyPart(decryptedPart); } - private void onDecryptionFailed(OpenPgpError error) { + private void onCryptoFailed(OpenPgpError error) { try { - DecryptedBodyPart errorPart = new DecryptedBodyPart(); + OpenPgpResultBodyPart errorPart = new OpenPgpResultBodyPart(false); errorPart.setError(error); - addDecryptedPartToMessage(errorPart); + addOpenPgpResultPartToMessage(errorPart); } catch (MessagingException e) { Log.e(K9.LOG_TAG, "This shouldn't happen", e); } - onDecryptionFinished(); + onCryptoFinished(); } - private void onDecryptionFinished() { - partsToDecrypt.removeFirst(); - decryptNextPartOrStartExtractingTextAndAttachments(); + private void onCryptoFinished() { + partsToDecryptOrVerify.removeFirst(); + decryptOrVerifyNextPartOrStartExtractingTextAndAttachments(); } private void onLoadMessageFromDatabaseFailed() { @@ -527,7 +579,6 @@ public class MessageViewFragment extends Fragment implements ConfirmationDialogF } private void onDecodeMessageFinished(MessageViewInfo messageContainer) { - //TODO: handle decryption and signature verification this.messageViewInfo = messageContainer; showMessage(messageContainer); }