Support sending Autocrypt-Gossip headers
This commit is contained in:
parent
e4a70576a6
commit
a293294f22
5 changed files with 134 additions and 17 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue