diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index bb1b6f6b4d..3a9a6f5723 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -68,7 +68,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; -import android.os.OperationCanceledException; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -153,14 +152,21 @@ import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1SequenceParser; +import org.bouncycastle.asn1.ASN1StreamParser; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; +import org.bouncycastle.asn1.cms.ContentInfoParser; import org.bouncycastle.asn1.cms.Time; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cms.CMSAuthEnvelopedDataParser; import org.bouncycastle.cms.CMSEnvelopedDataParser; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessable; @@ -169,11 +175,13 @@ import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSTypedData; import org.bouncycastle.cms.PKIXRecipientId; import org.bouncycastle.cms.RecipientInformation; +import org.bouncycastle.cms.RecipientInformationStore; import org.bouncycastle.cms.SignerId; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.SignerInformationVerifier; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.cms.jcajce.JceKeyTransAuthEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransRecipient; import org.bouncycastle.operator.DefaultAlgorithmNameFinder; @@ -10603,17 +10611,46 @@ public class FragmentMessages extends FragmentBase if (input == null) throw new IllegalArgumentException("Encrypted message missing"); + // Do not trust only the smime-type MIME parameter: it is a hint and + // is not retained in EntityAttachment.type. Detect the actual CMS + // content type from the ContentInfo OID. + ASN1ObjectIdentifier contentType = getCmsContentType(input); + boolean authEnveloped = CMSObjectIdentifiers.authEnvelopedData.equals(contentType); + + if (!authEnveloped && !CMSObjectIdentifiers.envelopedData.equals(contentType)) + throw new CMSException("Unsupported encrypted CMS content type: " + contentType); + int count = -1; boolean decoded = false; Throwable last = null; while (!decoded) try (FileInputStream fis = new FileInputStream(input)) { - // Create parser - CMSEnvelopedDataParser envelopedData = new CMSEnvelopedDataParser(fis); + // AuthEnvelopedData uses an AEAD-capable parser and recipient. + RecipientInformationStore recipientStore; + AlgorithmIdentifier contentAlgorithm; + String contentAlgorithmOid; + String parserClass; + + JceKeyTransRecipient recipient; + if (authEnveloped) { + CMSAuthEnvelopedDataParser authData = new CMSAuthEnvelopedDataParser(fis); + recipientStore = authData.getRecipientInfos(); + contentAlgorithm = authData.getEncryptionAlgOID(); + contentAlgorithmOid = authData.getEncAlgOID(); + parserClass = authData.getClass().getName(); + recipient = new JceKeyTransAuthEnvelopedRecipient(privkey); + } else { + CMSEnvelopedDataParser envelopedData = new CMSEnvelopedDataParser(fis); + recipientStore = envelopedData.getRecipientInfos(); + contentAlgorithm = envelopedData.getContentEncryptionAlgorithm(); + contentAlgorithmOid = envelopedData.getEncryptionAlgOID(); + parserClass = envelopedData.getClass().getName(); + recipient = new JceKeyTransEnvelopedRecipient(privkey); + } // Get recipient info - JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient(privkey); - Collection recipients = envelopedData.getRecipientInfos().getRecipients(); // KeyTransRecipientInformation + Collection recipients = + recipientStore.getRecipients(); // KeyTransRecipientInformation EntityLog.log(context, "s/mime private key" + " algo=" + privkey.getAlgorithm() + @@ -10644,14 +10681,15 @@ public class FragmentMessages extends FragmentBase String malgo; try { DefaultAlgorithmNameFinder af = new DefaultAlgorithmNameFinder(); - malgo = af.getAlgorithmName(envelopedData.getContentEncryptionAlgorithm()); + malgo = af.getAlgorithmName(contentAlgorithm); } catch (Throwable ex) { Log.e(ex); - malgo = envelopedData.getEncryptionAlgOID(); + malgo = contentAlgorithmOid; } EntityLog.log(context, "s/mime message" + + " type=" + contentType + " algo=" + malgo + - " class=" + envelopedData.getClass()); + " class=" + parserClass); // Find recipient if (count < 0) { @@ -10938,6 +10976,20 @@ public class FragmentMessages extends FragmentBase Log.unexpectedError(getParentFragmentManager(), ex); } + private ASN1ObjectIdentifier getCmsContentType(File file) + throws IOException, CMSException { + try (InputStream is = + new BufferedInputStream(new FileInputStream(file))) { + ASN1Encodable object = new ASN1StreamParser(is).readObject(); + if (!(object instanceof ASN1SequenceParser)) + throw new CMSException("Invalid CMS ContentInfo"); + + ContentInfoParser contentInfo = + new ContentInfoParser((ASN1SequenceParser) object); + return contentInfo.getContentType(); + } + } + private void decodeMessage(Context context, InputStream is, EntityMessage message, Bundle args) throws MessagingException, IOException { int type = args.getInt("type"); String alias = args.getString("alias"); diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index 735c2f9359..28d9082c57 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -5271,6 +5271,10 @@ public class MessageHelper { if ("enveloped-data".equalsIgnoreCase(smimeType)) { getMessageParts(null, part, parts, EntityAttachment.SMIME_MESSAGE); return parts; + } else if ("authEnveloped-data".equalsIgnoreCase(smimeType) || + "auth-enveloped-data".equalsIgnoreCase(smimeType)) { + getMessageParts(null, part, parts, EntityAttachment.SMIME_MESSAGE); + return parts; } else if ("signed-data".equalsIgnoreCase(smimeType)) { getMessageParts(null, part, parts, EntityAttachment.SMIME_SIGNED_DATA); return parts;