Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5e837cc authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #2036 from k9mail/encrypted-multipart-alternative

Handle encrypted parts in multipart/alternative
parents d51b90f3 76b66472
Loading
Loading
Loading
Loading
+57 −10
Original line number Diff line number Diff line
@@ -35,9 +35,9 @@ public class MessageDecryptVerifier {
    // APPLICATION/PGP is a special case which occurs from mutt. see http://www.mutt.org/doc/PGP-Notes.txt
    private static final String APPLICATION_PGP = "application/pgp";

    public static final String PGP_INLINE_START_MARKER = "-----BEGIN PGP MESSAGE-----";
    public static final String PGP_INLINE_SIGNED_START_MARKER = "-----BEGIN PGP SIGNED MESSAGE-----";
    public static final int TEXT_LENGTH_FOR_INLINE_CHECK = 36;
    private static final String PGP_INLINE_START_MARKER = "-----BEGIN PGP MESSAGE-----";
    private static final String PGP_INLINE_SIGNED_START_MARKER = "-----BEGIN PGP SIGNED MESSAGE-----";
    private static final int TEXT_LENGTH_FOR_INLINE_CHECK = 36;


    public static Part findPrimaryEncryptedOrSignedPart(Part part, List<Part> outputExtraParts) {
@@ -45,16 +45,63 @@ public class MessageDecryptVerifier {
            return part;
        }

        Part foundPart;

        foundPart = findPrimaryPartInAlternative(part);
        if (foundPart != null) {
            return foundPart;
        }

        foundPart = findPrimaryPartInMixed(part, outputExtraParts);
        if (foundPart != null) {
            return foundPart;
        }

        return null;
    }

    @Nullable
    private static Part findPrimaryPartInMixed(Part part, List<Part> outputExtraParts) {
        Body body = part.getBody();
        if (part.isMimeType("multipart/mixed") && body instanceof Multipart) {

        boolean isMultipartMixed = part.isMimeType("multipart/mixed") && body instanceof Multipart;
        if (!isMultipartMixed) {
            return null;
        }

        Multipart multipart = (Multipart) body;
        if (multipart.getCount() == 0) {
            return null;
        }
        
        BodyPart firstBodyPart = multipart.getBodyPart(0);

        Part foundPart;
        if (isPartEncryptedOrSigned(firstBodyPart)) {
                if (outputExtraParts != null) {
            foundPart = firstBodyPart;
        } else {
            foundPart = findPrimaryPartInAlternative(firstBodyPart);
        }

        if (foundPart != null && outputExtraParts != null) {
            for (int i = 1; i < multipart.getCount(); i++) {
                outputExtraParts.add(multipart.getBodyPart(i));
            }
        }

        return foundPart;
    }

    private static Part findPrimaryPartInAlternative(Part part) {
        Body body = part.getBody();
        if (part.isMimeType("multipart/alternative") && body instanceof Multipart) {
            Multipart multipart = (Multipart) body;
            if (multipart.getCount() == 0) {
                return null;
            }
            
            BodyPart firstBodyPart = multipart.getBodyPart(0);
            if (isPartPgpInlineEncryptedOrSigned(firstBodyPart)) {
                return firstBodyPart;
            }
        }
+5 −0
Original line number Diff line number Diff line
@@ -9,8 +9,10 @@ import java.util.List;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;

import com.fsck.k9.Globals;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.helper.HtmlConverter;
import com.fsck.k9.helper.HtmlSanitizer;
@@ -78,6 +80,9 @@ public class MessageViewInfoExtractor {
            cryptoResultAnnotation = cryptoMessageParts.contentCryptoAnnotation;
            extraParts = cryptoMessageParts.extraParts;
        } else {
            if (!annotations.isEmpty()) {
                Log.e(K9.LOG_TAG, "Got message annotations but no crypto root part!");
            }
            rootPart = message;
            cryptoResultAnnotation = null;
            extraParts = null;
+4 −0
Original line number Diff line number Diff line
@@ -28,6 +28,10 @@ public class MessageCryptoAnnotations {
        return annotations.containsKey(part);
    }

    public boolean isEmpty() {
        return annotations.isEmpty();
    }

    public Part findKeyForAnnotationWithReplacementPart(Part part) {
        for (HashMap.Entry<Part, CryptoResultAnnotation> entry : annotations.entrySet()) {
            if (part == entry.getValue().getReplacementData()) {
+122 −7
Original line number Diff line number Diff line
package com.fsck.k9.crypto;


import java.util.ArrayList;
import java.util.List;

import com.fsck.k9.mail.BodyPart;
@@ -20,20 +21,129 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;


@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 21)
public class MessageDecryptVerifierTest {

    public static final String MIME_TYPE_MULTIPART_ENCRYPTED = "multipart/encrypted";
    private static final String MIME_TYPE_MULTIPART_ENCRYPTED = "multipart/encrypted";
    private MessageCryptoAnnotations messageCryptoAnnotations = mock(MessageCryptoAnnotations.class);
    public static final String PROTCOL_PGP_ENCRYPTED = "application/pgp-encrypted";
    private static final String PROTCOL_PGP_ENCRYPTED = "application/pgp-encrypted";
    private static final String PGP_INLINE_DATA = "" +
            "-----BEGIN PGP MESSAGE-----\n" +
            "Header: Value\n" +
            "\n" +
            "base64base64base64base64\n" +
            "-----END PGP MESSAGE-----\n";


    @Test
    public void findPrimaryCryptoPart_withSimplePgpInline() throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        Message message = new MimeMessage();
        MimeMessageHelper.setBody(message, new TextBody(PGP_INLINE_DATA));

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertSame(message, cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withMultipartAlternativeContainingPgpInline() throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        BodyPart pgpInlinePart = bodypart("text/plain", PGP_INLINE_DATA);
        Message message = messageFromBody(
                multipart("alternative",
                        pgpInlinePart,
                        bodypart("text/html")
                )
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertSame(pgpInlinePart, cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withMultipartMixedContainingPgpInline() throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        BodyPart pgpInlinePart = bodypart("text/plain", PGP_INLINE_DATA);
        Message message = messageFromBody(
                multipart("mixed",
                        pgpInlinePart,
                        bodypart("application/octet-stream")
                )
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertSame(pgpInlinePart, cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withMultipartMixedContainingMultipartAlternativeContainingPgpInline()
            throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        BodyPart pgpInlinePart = bodypart("text/plain", PGP_INLINE_DATA);
        Message message = messageFromBody(
                multipart("mixed",
                        multipart("alternative",
                            pgpInlinePart,
                            bodypart("text/html")
                        ),
                        bodypart("application/octet-stream")
                )
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertSame(pgpInlinePart, cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withEmptyMultipartAlternative_shouldReturnNull() throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        Message message = messageFromBody(
                multipart("alternative")
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertNull(cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withEmptyMultipartMixed_shouldReturnNull() throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        Message message = messageFromBody(
                multipart("mixed")
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertNull(cryptoPart);
    }

    @Test
    public void findPrimaryCryptoPart_withEmptyMultipartAlternativeInsideMultipartMixed_shouldReturnNull()
            throws Exception {
        List<Part> outputExtraParts = new ArrayList<>();
        Message message = messageFromBody(
                multipart("mixed",
                        multipart("alternative")
                )
        );

        Part cryptoPart = MessageDecryptVerifier.findPrimaryEncryptedOrSignedPart(message, outputExtraParts);

        assertNull(cryptoPart);
    }

    @Test
    public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception {
@@ -373,6 +483,11 @@ public class MessageDecryptVerifierTest {
        return new MimeBodyPart(null, type);
    }

    BodyPart bodypart(String type, String text) throws MessagingException {
        TextBody textBody = new TextBody(text);
        return new MimeBodyPart(textBody, type);
    }

    public static Part getPart(Part searchRootPart, int... indexes) {
        Part part = searchRootPart;
        for (int index : indexes) {