Support sending Autocrypt-Gossip headers

This commit is contained in:
Vincent Breitmoser 2017-10-26 20:30:28 +02:00
parent e4a70576a6
commit a293294f22
5 changed files with 134 additions and 17 deletions

View file

@ -0,0 +1,64 @@
package com.fsck.k9.autocrypt;
import java.util.Arrays;
import android.support.annotation.NonNull;
class AutocryptGossipHeader {
static final String AUTOCRYPT_GOSSIP_HEADER = "Autocrypt-Gossip";
private static final String AUTOCRYPT_PARAM_ADDR = "addr";
private static final String AUTOCRYPT_PARAM_KEY_DATA = "keydata";
@NonNull
private final byte[] keyData;
@NonNull
private final String addr;
AutocryptGossipHeader(@NonNull String addr, @NonNull byte[] keyData) {
this.addr = addr;
this.keyData = keyData;
}
String toRawHeaderString() {
StringBuilder builder = new StringBuilder();
builder.append(AutocryptGossipHeader.AUTOCRYPT_GOSSIP_HEADER).append(": ");
builder.append(AutocryptGossipHeader.AUTOCRYPT_PARAM_ADDR).append('=').append(addr).append("; ");
builder.append(AutocryptGossipHeader.AUTOCRYPT_PARAM_KEY_DATA).append('=');
builder.append(AutocryptHeader.createFoldedBase64KeyData(keyData));
return builder.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AutocryptGossipHeader that = (AutocryptGossipHeader) o;
if (!Arrays.equals(keyData, that.keyData)) {
return false;
}
if (!addr.equals(that.addr)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Arrays.hashCode(keyData);
result = 31 * result + addr.hashCode();
return result;
}
}

View file

@ -16,13 +16,23 @@ public class AutocryptOpenPgpApiInteractor {
private AutocryptOpenPgpApiInteractor() { } private AutocryptOpenPgpApiInteractor() { }
public byte[] getKeyMaterialFromApi(OpenPgpApi openPgpApi, long keyId, String minimizeForUserId) { public byte[] getKeyMaterialForKeyId(OpenPgpApi openPgpApi, long keyId, String minimizeForUserId) {
Intent retreiveKeyIntent = new Intent(OpenPgpApi.ACTION_GET_KEY); Intent retrieveKeyIntent = new Intent(OpenPgpApi.ACTION_GET_KEY);
retreiveKeyIntent.putExtra(OpenPgpApi.EXTRA_KEY_ID, keyId); retrieveKeyIntent.putExtra(OpenPgpApi.EXTRA_KEY_ID, keyId);
retreiveKeyIntent.putExtra(OpenPgpApi.EXTRA_MINIMIZE, true); return getKeyMaterialFromApi(openPgpApi, retrieveKeyIntent, minimizeForUserId);
retreiveKeyIntent.putExtra(OpenPgpApi.EXTRA_MINIMIZE_USER_ID, minimizeForUserId); }
public byte[] getKeyMaterialForUserId(OpenPgpApi openPgpApi, String userId) {
Intent retrieveKeyIntent = new Intent(OpenPgpApi.ACTION_GET_KEY);
retrieveKeyIntent.putExtra(OpenPgpApi.EXTRA_USER_ID, userId);
return getKeyMaterialFromApi(openPgpApi, retrieveKeyIntent, userId);
}
private byte[] getKeyMaterialFromApi(OpenPgpApi openPgpApi, Intent retrieveKeyIntent, String userId) {
retrieveKeyIntent.putExtra(OpenPgpApi.EXTRA_MINIMIZE, true);
retrieveKeyIntent.putExtra(OpenPgpApi.EXTRA_MINIMIZE_USER_ID, userId);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
Intent result = openPgpApi.executeApi(retreiveKeyIntent, (InputStream) null, baos); Intent result = openPgpApi.executeApi(retrieveKeyIntent, (InputStream) null, baos);
if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) == if (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR) ==
OpenPgpApi.RESULT_CODE_SUCCESS) { OpenPgpApi.RESULT_CODE_SUCCESS) {

View file

@ -7,6 +7,7 @@ import java.util.Date;
import android.content.Intent; import android.content.Intent;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.internet.MimeBodyPart;
import org.openintents.openpgp.AutocryptPeerUpdate; import org.openintents.openpgp.AutocryptPeerUpdate;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
@ -60,4 +61,10 @@ public class AutocryptOperations {
message.addRawHeader(AutocryptHeader.AUTOCRYPT_HEADER, rawAutocryptHeader); message.addRawHeader(AutocryptHeader.AUTOCRYPT_HEADER, rawAutocryptHeader);
} }
public void addAutocryptGossipHeaderToPart(MimeBodyPart part, byte[] keyData, String autocryptAddress) {
AutocryptGossipHeader autocryptGossipHeader = new AutocryptGossipHeader(autocryptAddress, keyData);
String rawAutocryptHeader = autocryptGossipHeader.toRawHeaderString();
part.addRawHeader(AutocryptGossipHeader.AUTOCRYPT_GOSSIP_HEADER, rawAutocryptHeader);
}
} }

View file

@ -20,6 +20,7 @@ import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.BoundaryGenerator; import com.fsck.k9.mail.BoundaryGenerator;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.fsck.k9.mail.internet.BinaryTempFileBody;
@ -51,6 +52,7 @@ public class PgpMessageBuilder extends MessageBuilder {
private OpenPgpApi openPgpApi; private OpenPgpApi openPgpApi;
private MimeMessage currentProcessedMimeMessage; private MimeMessage currentProcessedMimeMessage;
private MimeBodyPart messageContentBodyPart;
private ComposeCryptoStatus cryptoStatus; private ComposeCryptoStatus cryptoStatus;
@ -106,7 +108,7 @@ public class PgpMessageBuilder extends MessageBuilder {
} }
Address address = currentProcessedMimeMessage.getFrom()[0]; Address address = currentProcessedMimeMessage.getFrom()[0];
byte[] keyData = autocryptOpenPgpApiInteractor.getKeyMaterialFromApi( byte[] keyData = autocryptOpenPgpApiInteractor.getKeyMaterialForKeyId(
openPgpApi, openPgpKeyId, address.getAddress()); openPgpApi, openPgpKeyId, address.getAddress());
if (keyData != null) { if (keyData != null) {
autocryptOperations.addAutocryptHeaderToMessage( autocryptOperations.addAutocryptHeaderToMessage(
@ -145,12 +147,16 @@ public class PgpMessageBuilder extends MessageBuilder {
throw new MessagingException("Must have recipients to build message!"); throw new MessagingException("Must have recipients to build message!");
} }
if (messageContentBodyPart == null) {
messageContentBodyPart = createBodyPartFromMessageContent(shouldEncrypt);
}
if (pgpApiIntent == null) { if (pgpApiIntent == null) {
pgpApiIntent = buildOpenPgpApiIntent(shouldSign, shouldEncrypt, isPgpInlineMode); pgpApiIntent = buildOpenPgpApiIntent(shouldSign, shouldEncrypt, isPgpInlineMode);
} }
PendingIntent returnedPendingIntent = launchOpenPgpApiIntent( PendingIntent returnedPendingIntent = launchOpenPgpApiIntent(pgpApiIntent, messageContentBodyPart,
pgpApiIntent, shouldEncrypt || isPgpInlineMode, shouldEncrypt || !isPgpInlineMode, isPgpInlineMode); shouldEncrypt || isPgpInlineMode, shouldEncrypt || !isPgpInlineMode, isPgpInlineMode);
if (returnedPendingIntent != null) { if (returnedPendingIntent != null) {
queueMessageBuildPendingIntent(returnedPendingIntent, REQUEST_USER_INTERACTION); queueMessageBuildPendingIntent(returnedPendingIntent, REQUEST_USER_INTERACTION);
return; return;
@ -162,6 +168,42 @@ public class PgpMessageBuilder extends MessageBuilder {
} }
} }
private MimeBodyPart createBodyPartFromMessageContent(boolean shouldEncrypt) throws MessagingException {
MimeBodyPart bodyPart = currentProcessedMimeMessage.toBodyPart();
String[] contentType = currentProcessedMimeMessage.getHeader(MimeHeader.HEADER_CONTENT_TYPE);
if (contentType.length > 0) {
bodyPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType[0]);
}
addGossipHeadersToBodyPart(shouldEncrypt, bodyPart);
return bodyPart;
}
private void addGossipHeadersToBodyPart(boolean shouldEncrypt, MimeBodyPart bodyPart) {
if (!shouldEncrypt) {
return;
}
Address[] toRecipients = currentProcessedMimeMessage.getRecipients(RecipientType.TO);
Address[] ccRecipients = currentProcessedMimeMessage.getRecipients(RecipientType.CC);
if (toRecipients.length + ccRecipients.length > 1) {
addAutocryptGossipHeadersToPart(bodyPart, toRecipients);
addAutocryptGossipHeadersToPart(bodyPart, ccRecipients);
}
}
private void addAutocryptGossipHeadersToPart(MimeBodyPart bodyPart, Address[] recipients) {
for (Address address : recipients) {
byte[] keyMaterial = autocryptOpenPgpApiInteractor.getKeyMaterialForUserId(openPgpApi, address.getAddress());
if (keyMaterial == null) {
Timber.e("Failed fetching gossip key material for address %s", address.getAddress());
continue;
}
autocryptOperations.addAutocryptGossipHeaderToPart(bodyPart, keyMaterial, address.getAddress());
}
}
@NonNull @NonNull
private Intent buildOpenPgpApiIntent(boolean shouldSign, boolean shouldEncrypt, boolean isPgpInlineMode) { private Intent buildOpenPgpApiIntent(boolean shouldSign, boolean shouldEncrypt, boolean isPgpInlineMode) {
Intent pgpApiIntent; Intent pgpApiIntent;
@ -193,14 +235,8 @@ public class PgpMessageBuilder extends MessageBuilder {
return pgpApiIntent; return pgpApiIntent;
} }
private PendingIntent launchOpenPgpApiIntent(@NonNull Intent openPgpIntent, private PendingIntent launchOpenPgpApiIntent(@NonNull Intent openPgpIntent, MimeBodyPart bodyPart,
boolean captureOutputPart, boolean capturedOutputPartIs7Bit, boolean writeBodyContentOnly) throws MessagingException { boolean captureOutputPart, boolean capturedOutputPartIs7Bit, boolean writeBodyContentOnly) throws MessagingException {
final MimeBodyPart bodyPart = currentProcessedMimeMessage.toBodyPart();
String[] contentType = currentProcessedMimeMessage.getHeader(MimeHeader.HEADER_CONTENT_TYPE);
if (contentType.length > 0) {
bodyPart.setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType[0]);
}
OpenPgpDataSource dataSource = createOpenPgpDataSourceFromBodyPart(bodyPart, writeBodyContentOnly); OpenPgpDataSource dataSource = createOpenPgpDataSourceFromBodyPart(bodyPart, writeBodyContentOnly);
BinaryTempFileBody pgpResultTempBody = null; BinaryTempFileBody pgpResultTempBody = null;

View file

@ -80,7 +80,7 @@ public class PgpMessageBuilderTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
when(autocryptOpenPgpApiInteractor.getKeyMaterialFromApi(openPgpApi, TEST_KEY_ID, SENDER_EMAIL)) when(autocryptOpenPgpApiInteractor.getKeyMaterialForKeyId(openPgpApi, TEST_KEY_ID, SENDER_EMAIL))
.thenReturn(AUTOCRYPT_KEY_MATERIAL); .thenReturn(AUTOCRYPT_KEY_MATERIAL);
} }