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

Commit 99786e08 authored by Vincent Breitmoser's avatar Vincent Breitmoser Committed by GitHub
Browse files

Merge pull request #2644 from k9mail/autocrypt-update-on-display

Autocrypt parse on display
parents 0466b985 a41e75f5
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -4,7 +4,9 @@ package com.fsck.k9.mail.internet;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

import android.support.annotation.NonNull;
@@ -15,7 +17,6 @@ import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part;

import org.apache.james.mime4j.codec.Base64InputStream;
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
import org.apache.james.mime4j.util.MimeUtil;
@@ -953,6 +954,23 @@ public class MimeUtility {
        return null;
    }

    public static Map<String,String> getAllHeaderParameters(String headerValue) {
        Map<String,String> result = new HashMap<>();

        headerValue = headerValue.replaceAll("\r|\n", "");
        String[] parts = headerValue.split(";");
        for (String part : parts) {
            String[] partParts = part.split("=", 2);
            if (partParts.length == 2) {
                String parameterName = partParts[0].trim().toLowerCase(Locale.US);
                String parameterValue = partParts[1].trim();
                result.put(parameterName, parameterValue);
            }
        }
        return result;
    }


    public static Part findFirstPartByMimeType(Part part, String mimeType) {
        if (part.getBody() instanceof Multipart) {
            Multipart multipart = (Multipart)part.getBody();
+3 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;

import com.fsck.k9.autocrypt.AutocryptOperations;
import com.fsck.k9.ui.crypto.OpenPgpApiFactory;
import timber.log.Timber;

@@ -270,7 +271,8 @@ public class MessageLoaderHelper {
            messageCryptoHelper = retainCryptoHelperFragment.getData();
        }
        if (messageCryptoHelper == null || messageCryptoHelper.isConfiguredForOutdatedCryptoProvider()) {
            messageCryptoHelper = new MessageCryptoHelper(context, new OpenPgpApiFactory());
            messageCryptoHelper = new MessageCryptoHelper(
                    context, new OpenPgpApiFactory(), AutocryptOperations.getInstance());
            retainCryptoHelperFragment.setData(messageCryptoHelper);
        }
        messageCryptoHelper.asyncStartOrResumeProcessingMessage(
+31 −0
Original line number Diff line number Diff line
package com.fsck.k9.autocrypt;


import java.util.Map;


class AutocryptHeader {
    static final String AUTOCRYPT_HEADER = "Autocrypt";

    static final String AUTOCRYPT_PARAM_TO = "addr";
    static final String AUTOCRYPT_PARAM_KEY_DATA = "keydata";

    static final String AUTOCRYPT_PARAM_TYPE = "type";
    static final String AUTOCRYPT_TYPE_1 = "1";

    static final String AUTOCRYPT_PARAM_PREFER_ENCRYPT = "prefer-encrypt";
    static final String AUTOCRYPT_PREFER_ENCRYPT_MUTUAL = "mutual";


    final byte[] keyData;
    final String addr;
    final Map<String,String> parameters;
    final boolean isPreferEncryptMutual;

    AutocryptHeader(Map<String, String> parameters, String addr, byte[] keyData, boolean isPreferEncryptMutual) {
        this.parameters = parameters;
        this.addr = addr;
        this.keyData = keyData;
        this.isPreferEncryptMutual = isPreferEncryptMutual;
    }
}
+97 −0
Original line number Diff line number Diff line
package com.fsck.k9.autocrypt;


import java.util.ArrayList;
import java.util.Map;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.MimeUtility;
import okio.ByteString;
import timber.log.Timber;


class AutocryptHeaderParser {
    private static final AutocryptHeaderParser INSTANCE = new AutocryptHeaderParser();


    public static AutocryptHeaderParser getInstance() {
        return INSTANCE;
    }

    private AutocryptHeaderParser() { }


    @Nullable
    AutocryptHeader getValidAutocryptHeader(Message currentMessage) {
        String[] headers = currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER);
        ArrayList<AutocryptHeader> autocryptHeaders = parseAllAutocryptHeaders(headers);

        boolean isSingleValidHeader = autocryptHeaders.size() == 1;
        return isSingleValidHeader ? autocryptHeaders.get(0) : null;
    }

    @Nullable
    private AutocryptHeader parseAutocryptHeader(String headerValue) {
        Map<String,String> parameters = MimeUtility.getAllHeaderParameters(headerValue);

        String type = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TYPE);
        if (type != null && !type.equals(AutocryptHeader.AUTOCRYPT_TYPE_1)) {
            Timber.e("autocrypt: unsupported type parameter %s", type);
            return null;
        }

        String base64KeyData = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_KEY_DATA);
        if (base64KeyData == null) {
            Timber.e("autocrypt: missing key parameter");
            return null;
        }

        ByteString byteString = ByteString.decodeBase64(base64KeyData);
        if (byteString == null) {
            Timber.e("autocrypt: error parsing base64 data");
            return null;
        }

        String to = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_TO);
        if (to == null) {
            Timber.e("autocrypt: no to header!");
            return null;
        }

        boolean isPreferEncryptMutual = false;
        String preferEncrypt = parameters.remove(AutocryptHeader.AUTOCRYPT_PARAM_PREFER_ENCRYPT);
        if (AutocryptHeader.AUTOCRYPT_PREFER_ENCRYPT_MUTUAL.equalsIgnoreCase(preferEncrypt)) {
            isPreferEncryptMutual = true;
        }

        if (hasCriticalParameters(parameters)) {
            return null;
        }

        return new AutocryptHeader(parameters, to, byteString.toByteArray(), isPreferEncryptMutual);
    }

    private boolean hasCriticalParameters(Map<String, String> parameters) {
        for (String parameterName : parameters.keySet()) {
            if (parameterName != null && !parameterName.startsWith("_")) {
                return true;
            }
        }
        return false;
    }

    @NonNull
    private ArrayList<AutocryptHeader> parseAllAutocryptHeaders(String[] headers) {
        ArrayList<AutocryptHeader> autocryptHeaders = new ArrayList<>();
        for (String header : headers) {
            AutocryptHeader autocryptHeader = parseAutocryptHeader(header);
            if (autocryptHeader != null) {
                autocryptHeaders.add(autocryptHeader);
            }
        }
        return autocryptHeaders;
    }
}
+52 −0
Original line number Diff line number Diff line
package com.fsck.k9.autocrypt;


import java.util.Date;

import android.content.Intent;

import com.fsck.k9.mail.Message;
import org.openintents.openpgp.AutocryptPeerUpdate;
import org.openintents.openpgp.util.OpenPgpApi;


public class AutocryptOperations {
    private final AutocryptHeaderParser autocryptHeaderParser;


    public static AutocryptOperations getInstance() {
        AutocryptHeaderParser autocryptHeaderParser = AutocryptHeaderParser.getInstance();
        return new AutocryptOperations(autocryptHeaderParser);
    }


    private AutocryptOperations(AutocryptHeaderParser autocryptHeaderParser) {
        this.autocryptHeaderParser = autocryptHeaderParser;
    }

    public boolean addAutocryptPeerUpdateToIntentIfPresent(Message currentMessage, Intent intent) {
        AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(currentMessage);
        if (autocryptHeader == null) {
            return false;
        }

        String messageFromAddress = currentMessage.getFrom()[0].getAddress();
        if (!autocryptHeader.addr.equalsIgnoreCase(messageFromAddress)) {
            return false;
        }

        Date messageDate = currentMessage.getSentDate();
        Date internalDate = currentMessage.getInternalDate();
        Date effectiveDate = messageDate.before(internalDate) ? messageDate : internalDate;

        AutocryptPeerUpdate data = AutocryptPeerUpdate.create(
                autocryptHeader.keyData, effectiveDate, autocryptHeader.isPreferEncryptMutual);
        intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, messageFromAddress);
        intent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_UPDATE, data);
        return true;
    }

    public boolean hasAutocryptHeader(Message currentMessage) {
        return currentMessage.getHeader(AutocryptHeader.AUTOCRYPT_HEADER).length > 0;
    }
}
Loading