Loading k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifier.java +11 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,10 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.CryptoResultAnnotation; import com.fsck.k9.ui.crypto.MessageCryptoAnnotations; import static com.fsck.k9.mail.internet.MimeUtility.isSameMimeType; Loading Loading @@ -83,13 +86,20 @@ public class MessageDecryptVerifier { return encryptedParts; } public static List<Part> findSignedParts(Part startPart) { public static List<Part> findSignedParts(Part startPart, MessageCryptoAnnotations messageCryptoAnnotations) { List<Part> signedParts = new ArrayList<>(); Stack<Part> partsToCheck = new Stack<>(); partsToCheck.push(startPart); while (!partsToCheck.isEmpty()) { Part part = partsToCheck.pop(); if (messageCryptoAnnotations.has(part)) { CryptoResultAnnotation resultAnnotation = messageCryptoAnnotations.get(part); MimeBodyPart replacementData = resultAnnotation.getReplacementData(); if (replacementData != null) { part = replacementData; } } Body body = part.getBody(); if (isPartMultipartSigned(part)) { Loading k9mail/src/main/java/com/fsck/k9/mailstore/CryptoResultAnnotation.java +42 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ public final class CryptoResultAnnotation { private final OpenPgpError openPgpError; private final PendingIntent openPgpPendingIntent; private final CryptoResultAnnotation encapsulatedResult; private CryptoResultAnnotation(@NonNull CryptoError errorType, MimeBodyPart replacementData, OpenPgpDecryptionResult openPgpDecryptionResult, OpenPgpSignatureResult openPgpSignatureResult, Loading @@ -32,6 +34,24 @@ public final class CryptoResultAnnotation { this.openPgpSignatureResult = openPgpSignatureResult; this.openPgpPendingIntent = openPgpPendingIntent; this.openPgpError = openPgpError; this.encapsulatedResult = null; } private CryptoResultAnnotation(CryptoResultAnnotation annotation, CryptoResultAnnotation encapsulatedResult) { if (annotation.encapsulatedResult != null) { throw new AssertionError("cannot replace an encapsulated result, this is a bug!"); } this.errorType = annotation.errorType; this.replacementData = annotation.replacementData; this.openPgpDecryptionResult = annotation.openPgpDecryptionResult; this.openPgpSignatureResult = annotation.openPgpSignatureResult; this.openPgpPendingIntent = annotation.openPgpPendingIntent; this.openPgpError = annotation.openPgpError; this.encapsulatedResult = encapsulatedResult; } Loading @@ -52,6 +72,15 @@ public final class CryptoResultAnnotation { return new CryptoResultAnnotation(CryptoError.OPENPGP_API_RETURNED_ERROR, null, null, null, null, error); } public boolean isOpenPgpResult() { return openPgpDecryptionResult != null && openPgpSignatureResult != null; } public boolean hasSignatureResult() { return openPgpSignatureResult != null && openPgpSignatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE; } @Nullable public OpenPgpDecryptionResult getOpenPgpDecryptionResult() { return openPgpDecryptionResult; Loading Loading @@ -86,6 +115,19 @@ public final class CryptoResultAnnotation { return replacementData; } @NonNull public CryptoResultAnnotation withEncapsulatedResult(CryptoResultAnnotation resultAnnotation) { return new CryptoResultAnnotation(this, resultAnnotation); } public boolean hasEncapsulatedResult() { return encapsulatedResult != null; } public CryptoResultAnnotation getEncapsulatedResult() { return encapsulatedResult; } public enum CryptoError { NONE, Loading k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java +11 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ package com.fsck.k9.ui.crypto; import java.util.HashMap; import android.support.annotation.VisibleForTesting; import com.fsck.k9.mail.Part; import com.fsck.k9.mailstore.CryptoResultAnnotation; Loading @@ -25,4 +27,13 @@ public class MessageCryptoAnnotations { public boolean has(Part part) { return annotations.containsKey(part); } public Part findKeyForAnnotationWithReplacementPart(Part part) { for (HashMap.Entry<Part, CryptoResultAnnotation> entry : annotations.entrySet()) { if (part == entry.getValue().getReplacementData()) { return entry.getKey(); } } return null; } } k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java +40 −9 Original line number Diff line number Diff line Loading @@ -68,6 +68,8 @@ public class MessageCryptoHelper { private MessageCryptoAnnotations messageAnnotations; private Intent userInteractionResultIntent; private LocalMessage currentMessage; private boolean secondPassStarted; public MessageCryptoHelper(Activity activity, Account account, MessageCryptoCallback callback) { Loading @@ -75,8 +77,6 @@ public class MessageCryptoHelper { this.activity = activity; this.callback = callback; this.account = account; this.messageAnnotations = new MessageCryptoAnnotations(); } public void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) { Loading @@ -85,14 +85,23 @@ public class MessageCryptoHelper { return; } List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message); processFoundEncryptedParts(encryptedParts, MessageHelper.createEmptyPart()); this.messageAnnotations = new MessageCryptoAnnotations(); this.currentMessage = message; runFirstPass(); } List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); processFoundSignedParts(signedParts); private void runFirstPass() { List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage); processFoundEncryptedParts(encryptedParts, MessageHelper.createEmptyPart()); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(message); decryptOrVerifyNextPart(); } private void runSecondPass() { List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations); processFoundSignedParts(signedParts); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage); addFoundInlinePgpParts(inlineParts); decryptOrVerifyNextPart(); Loading Loading @@ -144,7 +153,7 @@ public class MessageCryptoHelper { private void decryptOrVerifyNextPart() { if (partsToDecryptOrVerify.isEmpty()) { returnResultToFragment(); runSecondPassOrReturnResultToFragment(); return; } Loading Loading @@ -465,6 +474,17 @@ public class MessageCryptoHelper { onCryptoFinished(); } private void propagateEncapsulatedSignedPart(CryptoResultAnnotation resultAnnotation, Part part) { Part encapsulatingPart = messageAnnotations.findKeyForAnnotationWithReplacementPart(part); CryptoResultAnnotation encapsulatingPartAnnotation = messageAnnotations.get(encapsulatingPart); if (encapsulatingPart != null && resultAnnotation.hasSignatureResult()) { CryptoResultAnnotation replacementAnnotation = encapsulatingPartAnnotation.withEncapsulatedResult(resultAnnotation); messageAnnotations.put(encapsulatingPart, replacementAnnotation); } } private void onCryptoFailed(OpenPgpError error) { CryptoResultAnnotation errorPart = CryptoResultAnnotation.createOpenPgpErrorAnnotation(error); addCryptoResultAnnotationToMessage(errorPart); Loading @@ -474,6 +494,8 @@ public class MessageCryptoHelper { private void addCryptoResultAnnotationToMessage(CryptoResultAnnotation resultAnnotation) { Part part = currentCryptoPart.part; messageAnnotations.put(part, resultAnnotation); propagateEncapsulatedSignedPart(resultAnnotation, part); } private void onCryptoFinished() { Loading @@ -481,6 +503,15 @@ public class MessageCryptoHelper { decryptOrVerifyNextPart(); } private void runSecondPassOrReturnResultToFragment() { if (secondPassStarted) { callback.onCryptoOperationsFinished(messageAnnotations); return; } secondPassStarted = true; runSecondPass(); } private void returnResultToFragment() { callback.onCryptoOperationsFinished(messageAnnotations); } Loading k9mail/src/test/java/com/fsck/k9/crypto/MessageDecryptVerifierTest.java +9 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.ui.crypto.MessageCryptoAnnotations; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; Loading @@ -21,12 +22,15 @@ import org.robolectric.annotation.Config; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertSame; import static org.mockito.Mockito.mock; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 21) public class MessageDecryptVerifierTest { private MessageCryptoAnnotations messageCryptoAnnotations = mock(MessageCryptoAnnotations.class); @Test public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception { MimeMessage emptyMessage = new MimeMessage(); Loading Loading @@ -189,7 +193,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); Loading @@ -207,7 +211,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); Loading @@ -224,7 +228,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 0), signedParts.get(0)); Loading @@ -242,7 +246,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 0), signedParts.get(0)); Loading @@ -260,7 +264,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 1), signedParts.get(0)); Loading Loading
k9mail/src/main/java/com/fsck/k9/crypto/MessageDecryptVerifier.java +11 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,10 @@ import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Part; import com.fsck.k9.mail.internet.MessageExtractor; import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mailstore.CryptoResultAnnotation; import com.fsck.k9.ui.crypto.MessageCryptoAnnotations; import static com.fsck.k9.mail.internet.MimeUtility.isSameMimeType; Loading Loading @@ -83,13 +86,20 @@ public class MessageDecryptVerifier { return encryptedParts; } public static List<Part> findSignedParts(Part startPart) { public static List<Part> findSignedParts(Part startPart, MessageCryptoAnnotations messageCryptoAnnotations) { List<Part> signedParts = new ArrayList<>(); Stack<Part> partsToCheck = new Stack<>(); partsToCheck.push(startPart); while (!partsToCheck.isEmpty()) { Part part = partsToCheck.pop(); if (messageCryptoAnnotations.has(part)) { CryptoResultAnnotation resultAnnotation = messageCryptoAnnotations.get(part); MimeBodyPart replacementData = resultAnnotation.getReplacementData(); if (replacementData != null) { part = replacementData; } } Body body = part.getBody(); if (isPartMultipartSigned(part)) { Loading
k9mail/src/main/java/com/fsck/k9/mailstore/CryptoResultAnnotation.java +42 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ public final class CryptoResultAnnotation { private final OpenPgpError openPgpError; private final PendingIntent openPgpPendingIntent; private final CryptoResultAnnotation encapsulatedResult; private CryptoResultAnnotation(@NonNull CryptoError errorType, MimeBodyPart replacementData, OpenPgpDecryptionResult openPgpDecryptionResult, OpenPgpSignatureResult openPgpSignatureResult, Loading @@ -32,6 +34,24 @@ public final class CryptoResultAnnotation { this.openPgpSignatureResult = openPgpSignatureResult; this.openPgpPendingIntent = openPgpPendingIntent; this.openPgpError = openPgpError; this.encapsulatedResult = null; } private CryptoResultAnnotation(CryptoResultAnnotation annotation, CryptoResultAnnotation encapsulatedResult) { if (annotation.encapsulatedResult != null) { throw new AssertionError("cannot replace an encapsulated result, this is a bug!"); } this.errorType = annotation.errorType; this.replacementData = annotation.replacementData; this.openPgpDecryptionResult = annotation.openPgpDecryptionResult; this.openPgpSignatureResult = annotation.openPgpSignatureResult; this.openPgpPendingIntent = annotation.openPgpPendingIntent; this.openPgpError = annotation.openPgpError; this.encapsulatedResult = encapsulatedResult; } Loading @@ -52,6 +72,15 @@ public final class CryptoResultAnnotation { return new CryptoResultAnnotation(CryptoError.OPENPGP_API_RETURNED_ERROR, null, null, null, null, error); } public boolean isOpenPgpResult() { return openPgpDecryptionResult != null && openPgpSignatureResult != null; } public boolean hasSignatureResult() { return openPgpSignatureResult != null && openPgpSignatureResult.getResult() != OpenPgpSignatureResult.RESULT_NO_SIGNATURE; } @Nullable public OpenPgpDecryptionResult getOpenPgpDecryptionResult() { return openPgpDecryptionResult; Loading Loading @@ -86,6 +115,19 @@ public final class CryptoResultAnnotation { return replacementData; } @NonNull public CryptoResultAnnotation withEncapsulatedResult(CryptoResultAnnotation resultAnnotation) { return new CryptoResultAnnotation(this, resultAnnotation); } public boolean hasEncapsulatedResult() { return encapsulatedResult != null; } public CryptoResultAnnotation getEncapsulatedResult() { return encapsulatedResult; } public enum CryptoError { NONE, Loading
k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoAnnotations.java +11 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ package com.fsck.k9.ui.crypto; import java.util.HashMap; import android.support.annotation.VisibleForTesting; import com.fsck.k9.mail.Part; import com.fsck.k9.mailstore.CryptoResultAnnotation; Loading @@ -25,4 +27,13 @@ public class MessageCryptoAnnotations { public boolean has(Part part) { return annotations.containsKey(part); } public Part findKeyForAnnotationWithReplacementPart(Part part) { for (HashMap.Entry<Part, CryptoResultAnnotation> entry : annotations.entrySet()) { if (part == entry.getValue().getReplacementData()) { return entry.getKey(); } } return null; } }
k9mail/src/main/java/com/fsck/k9/ui/crypto/MessageCryptoHelper.java +40 −9 Original line number Diff line number Diff line Loading @@ -68,6 +68,8 @@ public class MessageCryptoHelper { private MessageCryptoAnnotations messageAnnotations; private Intent userInteractionResultIntent; private LocalMessage currentMessage; private boolean secondPassStarted; public MessageCryptoHelper(Activity activity, Account account, MessageCryptoCallback callback) { Loading @@ -75,8 +77,6 @@ public class MessageCryptoHelper { this.activity = activity; this.callback = callback; this.account = account; this.messageAnnotations = new MessageCryptoAnnotations(); } public void decryptOrVerifyMessagePartsIfNecessary(LocalMessage message) { Loading @@ -85,14 +85,23 @@ public class MessageCryptoHelper { return; } List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(message); processFoundEncryptedParts(encryptedParts, MessageHelper.createEmptyPart()); this.messageAnnotations = new MessageCryptoAnnotations(); this.currentMessage = message; runFirstPass(); } List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); processFoundSignedParts(signedParts); private void runFirstPass() { List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage); processFoundEncryptedParts(encryptedParts, MessageHelper.createEmptyPart()); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(message); decryptOrVerifyNextPart(); } private void runSecondPass() { List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations); processFoundSignedParts(signedParts); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage); addFoundInlinePgpParts(inlineParts); decryptOrVerifyNextPart(); Loading Loading @@ -144,7 +153,7 @@ public class MessageCryptoHelper { private void decryptOrVerifyNextPart() { if (partsToDecryptOrVerify.isEmpty()) { returnResultToFragment(); runSecondPassOrReturnResultToFragment(); return; } Loading Loading @@ -465,6 +474,17 @@ public class MessageCryptoHelper { onCryptoFinished(); } private void propagateEncapsulatedSignedPart(CryptoResultAnnotation resultAnnotation, Part part) { Part encapsulatingPart = messageAnnotations.findKeyForAnnotationWithReplacementPart(part); CryptoResultAnnotation encapsulatingPartAnnotation = messageAnnotations.get(encapsulatingPart); if (encapsulatingPart != null && resultAnnotation.hasSignatureResult()) { CryptoResultAnnotation replacementAnnotation = encapsulatingPartAnnotation.withEncapsulatedResult(resultAnnotation); messageAnnotations.put(encapsulatingPart, replacementAnnotation); } } private void onCryptoFailed(OpenPgpError error) { CryptoResultAnnotation errorPart = CryptoResultAnnotation.createOpenPgpErrorAnnotation(error); addCryptoResultAnnotationToMessage(errorPart); Loading @@ -474,6 +494,8 @@ public class MessageCryptoHelper { private void addCryptoResultAnnotationToMessage(CryptoResultAnnotation resultAnnotation) { Part part = currentCryptoPart.part; messageAnnotations.put(part, resultAnnotation); propagateEncapsulatedSignedPart(resultAnnotation, part); } private void onCryptoFinished() { Loading @@ -481,6 +503,15 @@ public class MessageCryptoHelper { decryptOrVerifyNextPart(); } private void runSecondPassOrReturnResultToFragment() { if (secondPassStarted) { callback.onCryptoOperationsFinished(messageAnnotations); return; } secondPassStarted = true; runSecondPass(); } private void returnResultToFragment() { callback.onCryptoOperationsFinished(messageAnnotations); } Loading
k9mail/src/test/java/com/fsck/k9/crypto/MessageDecryptVerifierTest.java +9 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessageHelper; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.ui.crypto.MessageCryptoAnnotations; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; Loading @@ -21,12 +22,15 @@ import org.robolectric.annotation.Config; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertSame; import static org.mockito.Mockito.mock; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 21) public class MessageDecryptVerifierTest { private MessageCryptoAnnotations messageCryptoAnnotations = mock(MessageCryptoAnnotations.class); @Test public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception { MimeMessage emptyMessage = new MimeMessage(); Loading Loading @@ -189,7 +193,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); Loading @@ -207,7 +211,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); Loading @@ -224,7 +228,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 0), signedParts.get(0)); Loading @@ -242,7 +246,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 0), signedParts.get(0)); Loading @@ -260,7 +264,7 @@ public class MessageDecryptVerifierTest { ) ); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(getPart(message, 1), signedParts.get(0)); Loading