@WorkerThread public MessageViewInfo extractMessageForView(Message message, @Nullable MessageCryptoAnnotations cryptoAnnotations, boolean openPgpProviderConfigured) throws MessagingException { ArrayList<Part> extraParts = new ArrayList<>(); Part cryptoContentPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, extraParts); if (cryptoContentPart == null) { if (cryptoAnnotations != null && !cryptoAnnotations.isEmpty()) { Timber.e("Got crypto message cryptoContentAnnotations but no crypto root part!"); } MessageViewInfo messageViewInfo = extractSimpleMessageForView(message, message); return messageViewInfo.withSubject(message.getSubject(), false); } boolean isOpenPgpEncrypted = (MessageCryptoStructureDetector.isPartMultipartEncrypted(cryptoContentPart) && MessageCryptoStructureDetector.isMultipartEncryptedOpenPgpProtocol(cryptoContentPart)) || MessageCryptoStructureDetector.isPartPgpInlineEncrypted(cryptoContentPart); if (!openPgpProviderConfigured && isOpenPgpEncrypted) { CryptoResultAnnotation noProviderAnnotation = CryptoResultAnnotation.createErrorAnnotation( CryptoError.OPENPGP_ENCRYPTED_NO_PROVIDER, null); return MessageViewInfo.createWithErrorState(message, false) .withCryptoData(noProviderAnnotation, null, null); } MessageViewInfo messageViewInfo = getMessageContent(message, cryptoAnnotations, extraParts, cryptoContentPart); messageViewInfo = extractSubject(messageViewInfo); return messageViewInfo; }
private boolean messageIsEncrypted(Message localMessage) { List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(localMessage); return !encryptedParts.isEmpty(); } }
private void findPartsForMultipartSignaturePass() { List<Part> signedParts = MessageCryptoStructureDetector .findMultipartSignedParts(currentMessage, messageAnnotations); for (Part part : signedParts) { if (!processSignedOnly) { boolean isEncapsulatedSignature = messageAnnotations.findKeyForAnnotationWithReplacementPart(part) != null; if (!isEncapsulatedSignature) { continue; } } if (!MessageHelper.isCompletePartAvailable(part)) { MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part); addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, replacementPart); continue; } if (MessageCryptoStructureDetector.isMultipartSignedOpenPgpProtocol(part)) { CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part); partsToProcess.add(cryptoPart); continue; } MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part); addErrorAnnotation(part, CryptoError.SIGNED_BUT_UNSUPPORTED, replacementPart); } }
private static boolean isPartEncryptedOrSigned(Part part) { return isPartMultipartEncrypted(part) || isPartMultipartSigned(part) || isPartPgpInlineEncryptedOrSigned(part); }
private void findPartsForPgpInlinePass() { List<Part> inlineParts = MessageCryptoStructureDetector.findPgpInlineParts(currentMessage); for (Part part : inlineParts) { if (!processSignedOnly && !MessageCryptoStructureDetector.isPartPgpInlineEncrypted(part)) { continue; } if (!currentMessage.getFlags().contains(Flag.X_DOWNLOADED_FULL)) { if (MessageCryptoStructureDetector.isPartPgpInlineEncrypted(part)) { addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, NO_REPLACEMENT_PART); } else { addErrorAnnotation(part, CryptoError.OPENPGP_SIGNED_BUT_INCOMPLETE, NO_REPLACEMENT_PART); } continue; } CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part); partsToProcess.add(cryptoPart); } }
private void findPartsForMultipartEncryptionPass() { List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(currentMessage); for (Part part : encryptedParts) { if (!MessageHelper.isCompletePartAvailable(part)) { addErrorAnnotation(part, CryptoError.OPENPGP_ENCRYPTED_BUT_INCOMPLETE, MessageHelper.createEmptyPart()); continue; } if (MessageCryptoStructureDetector.isMultipartEncryptedOpenPgpProtocol(part)) { CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part); partsToProcess.add(cryptoPart); continue; } addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart()); } }
@Test public void isPgpInlineMethods__withEncryptedDataAndLeadingGarbage__shouldReturnFalse() throws Exception { String pgpInlineData = "garbage!" + "-----BEGIN PGP MESSAGE-----\n" + "Header: Value\n" + "\n" + "base64base64base64base64\n" + "-----END PGP MESSAGE-----\n"; MimeMessage message = new MimeMessage(); message.setBody(new TextBody(pgpInlineData)); assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncryptedOrSigned(message)); assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message)); }
private boolean containsInlinePgpEncryptedText(Message message) { Part textPart = textPartFinder.findFirstTextPart(message); return MessageCryptoStructureDetector.isPartPgpInlineEncrypted(textPart); }
@Test public void findSigned__withMissingSignature__shouldReturnEmpty() throws Exception { Message message = messageFromBody( multipart("signed", "protocol=\"application/pgp-signature\"", bodypart("text/plain") ) ); List<Part> signedParts = MessageCryptoStructureDetector .findMultipartSignedParts(message, messageCryptoAnnotations); assertTrue(signedParts.isEmpty()); }
@Test public void findPrimaryCryptoPart_withEmptyMultipartMixed_shouldReturnNull() throws Exception { List<Part> outputExtraParts = new ArrayList<>(); Message message = messageFromBody( multipart("mixed") ); Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts); assertNull(cryptoPart); }
public static List<Part> findPgpInlineParts(Part startPart) { List<Part> inlineParts = new ArrayList<>(); Stack<Part> partsToCheck = new Stack<>(); partsToCheck.push(startPart); while (!partsToCheck.isEmpty()) { Part part = partsToCheck.pop(); Body body = part.getBody(); if (isPartPgpInlineEncryptedOrSigned(part)) { inlineParts.add(part); continue; } 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 inlineParts; }
private boolean messageHasPgpInlineParts(Message localMessage) { List<Part> inlineParts = MessageCryptoStructureDetector.findPgpInlineParts(localMessage); return !inlineParts.isEmpty(); } }
@Test public void isPgpInlineMethods__withEncryptedDataAndLeadingWhitespace__shouldReturnTrue() throws Exception { String pgpInlineData = "\n \n \n" + "-----BEGIN PGP MESSAGE-----\n" + "Header: Value\n" + "\n" + "base64base64base64base64\n" + "-----END PGP MESSAGE-----\n"; MimeMessage message = new MimeMessage(); message.setBody(new TextBody(pgpInlineData)); assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncryptedOrSigned(message)); assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message)); }
@Test public void isPgpInlineMethods__withPgpInlineData__shouldReturnTrue() throws Exception { String pgpInlineData = "-----BEGIN PGP MESSAGE-----\n" + "Header: Value\n" + "\n" + "base64base64base64base64\n" + "-----END PGP MESSAGE-----\n"; MimeMessage message = new MimeMessage(); message.setBody(new TextBody(pgpInlineData)); assertTrue(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message)); }
@Test public void findSigned__withSimpleMultipartSigned__shouldReturnRoot() throws Exception { Message message = messageFromBody( multipart("signed", "protocol=\"application/pgp-signature\"", bodypart("text/plain"), bodypart("application/pgp-signature") ) ); List<Part> signedParts = MessageCryptoStructureDetector .findMultipartSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); }
@Test public void findPrimaryCryptoPart_withEmptyMultipartAlternative_shouldReturnNull() throws Exception { List<Part> outputExtraParts = new ArrayList<>(); Message message = messageFromBody( multipart("alternative") ); Part cryptoPart = MessageCryptoStructureDetector.findPrimaryEncryptedOrSignedPart(message, outputExtraParts); assertNull(cryptoPart); }
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; } } return null; }
@Test public void findEncryptedPartsShouldReturnEmptyListForEmptyMessage() throws Exception { MimeMessage emptyMessage = new MimeMessage(); List<Part> encryptedParts = MessageCryptoStructureDetector.findMultipartEncryptedParts(emptyMessage); assertEquals(0, encryptedParts.size()); }
@Test public void isPartPgpInlineEncrypted__withSignedData__shouldReturnFalse() throws Exception { String pgpInlineData = "-----BEGIN PGP SIGNED MESSAGE-----\n" + "Header: Value\n" + "\n" + "-----BEGIN PGP SIGNATURE-----\n" + "Header: Value\n" + "\n" + "base64base64base64base64\n" + "-----END PGP SIGNED MESSAGE-----\n"; MimeMessage message = new MimeMessage(); message.setBody(new TextBody(pgpInlineData)); assertFalse(MessageCryptoStructureDetector.isPartPgpInlineEncrypted(message)); }
@Test public void findSigned__withNoProtocolAndNoBody__shouldReturnRoot() throws Exception { Message message = messageFromBody( multipart("signed", bodypart("text/plain"), bodypart("application/pgp-signature") ) ); List<Part> signedParts = MessageCryptoStructureDetector .findMultipartSignedParts(message, messageCryptoAnnotations); assertEquals(1, signedParts.size()); assertSame(message, signedParts.get(0)); }