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

Autocrypt parse on display
This commit is contained in:
Vincent Breitmoser 2017-08-31 13:40:25 +02:00 committed by GitHub
commit 99786e0884
20 changed files with 953 additions and 97 deletions

View file

@ -4,7 +4,9 @@ package com.fsck.k9.mail.internet;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import android.support.annotation.NonNull; 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.MessagingException;
import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Multipart;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.Base64InputStream;
import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
import org.apache.james.mime4j.util.MimeUtil; import org.apache.james.mime4j.util.MimeUtil;
@ -953,6 +954,23 @@ public class MimeUtility {
return null; 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) { public static Part findFirstPartByMimeType(Part part, String mimeType) {
if (part.getBody() instanceof Multipart) { if (part.getBody() instanceof Multipart) {
Multipart multipart = (Multipart)part.getBody(); Multipart multipart = (Multipart)part.getBody();

View file

@ -14,6 +14,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import com.fsck.k9.autocrypt.AutocryptOperations;
import com.fsck.k9.ui.crypto.OpenPgpApiFactory; import com.fsck.k9.ui.crypto.OpenPgpApiFactory;
import timber.log.Timber; import timber.log.Timber;
@ -270,7 +271,8 @@ public class MessageLoaderHelper {
messageCryptoHelper = retainCryptoHelperFragment.getData(); messageCryptoHelper = retainCryptoHelperFragment.getData();
} }
if (messageCryptoHelper == null || messageCryptoHelper.isConfiguredForOutdatedCryptoProvider()) { if (messageCryptoHelper == null || messageCryptoHelper.isConfiguredForOutdatedCryptoProvider()) {
messageCryptoHelper = new MessageCryptoHelper(context, new OpenPgpApiFactory()); messageCryptoHelper = new MessageCryptoHelper(
context, new OpenPgpApiFactory(), AutocryptOperations.getInstance());
retainCryptoHelperFragment.setData(messageCryptoHelper); retainCryptoHelperFragment.setData(messageCryptoHelper);
} }
messageCryptoHelper.asyncStartOrResumeProcessingMessage( messageCryptoHelper.asyncStartOrResumeProcessingMessage(

View file

@ -0,0 +1,31 @@
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;
}
}

View file

@ -0,0 +1,97 @@
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;
}
}

View file

@ -0,0 +1,52 @@
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;
}
}

View file

@ -11,10 +11,8 @@ import android.content.Intent;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import timber.log.Timber;
import com.fsck.k9.Globals; import com.fsck.k9.Globals;
import com.fsck.k9.K9;
import com.fsck.k9.activity.compose.ComposeCryptoStatus; import com.fsck.k9.activity.compose.ComposeCryptoStatus;
import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.BodyPart;
@ -36,6 +34,7 @@ import org.apache.james.mime4j.util.MimeUtil;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
import timber.log.Timber;
public class PgpMessageBuilder extends MessageBuilder { public class PgpMessageBuilder extends MessageBuilder {
@ -154,7 +153,7 @@ public class PgpMessageBuilder extends MessageBuilder {
throw new MessagingException("encryption is enabled, but no recipient specified!"); throw new MessagingException("encryption is enabled, but no recipient specified!");
} }
pgpApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, encryptRecipientAddresses); pgpApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, encryptRecipientAddresses);
pgpApiIntent.putExtra(OpenPgpApi.EXTRA_ENCRYPT_OPPORTUNISTIC, cryptoStatus.isEncryptionOpportunistic()); pgpApiIntent.putExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, cryptoStatus.isEncryptionOpportunistic());
} }
} else { } else {
pgpApiIntent = new Intent(isPgpInlineMode ? OpenPgpApi.ACTION_SIGN : OpenPgpApi.ACTION_DETACHED_SIGN); pgpApiIntent = new Intent(isPgpInlineMode ? OpenPgpApi.ACTION_SIGN : OpenPgpApi.ACTION_DETACHED_SIGN);

View file

@ -18,6 +18,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread; import android.support.annotation.WorkerThread;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.autocrypt.AutocryptOperations;
import com.fsck.k9.crypto.MessageDecryptVerifier; import com.fsck.k9.crypto.MessageDecryptVerifier;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Body; import com.fsck.k9.mail.Body;
@ -45,6 +46,7 @@ import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation; import org.openintents.openpgp.util.OpenPgpApi.CancelableBackgroundOperation;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
@ -62,8 +64,9 @@ public class MessageCryptoHelper {
private final Context context; private final Context context;
private final String openPgpProviderPackage; private final String openPgpProviderPackage;
private final AutocryptOperations autocryptOperations;
private final Object callbackLock = new Object(); private final Object callbackLock = new Object();
private final Deque<CryptoPart> partsToDecryptOrVerify = new ArrayDeque<>(); private final Deque<CryptoPart> partsToProcess = new ArrayDeque<>();
@Nullable @Nullable
private MessageCryptoCallback callback; private MessageCryptoCallback callback;
@ -78,7 +81,7 @@ public class MessageCryptoHelper {
private CryptoPart currentCryptoPart; private CryptoPart currentCryptoPart;
private Intent currentCryptoResult; private Intent currentCryptoResult;
private Intent userInteractionResultIntent; private Intent userInteractionResultIntent;
private boolean secondPassStarted; private State state;
private CancelableBackgroundOperation cancelableBackgroundOperation; private CancelableBackgroundOperation cancelableBackgroundOperation;
private boolean isCancelled; private boolean isCancelled;
@ -87,13 +90,15 @@ public class MessageCryptoHelper {
private OpenPgpApiFactory openPgpApiFactory; private OpenPgpApiFactory openPgpApiFactory;
public MessageCryptoHelper(Context context, OpenPgpApiFactory openPgpApiFactory) { public MessageCryptoHelper(Context context, OpenPgpApiFactory openPgpApiFactory,
AutocryptOperations autocryptOperations) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
if (!K9.isOpenPgpProviderConfigured()) { if (!K9.isOpenPgpProviderConfigured()) {
throw new IllegalStateException("MessageCryptoHelper must only be called with a openpgp provider!"); throw new IllegalStateException("MessageCryptoHelper must only be called with a OpenPGP provider!");
} }
this.autocryptOperations = autocryptOperations;
this.openPgpApiFactory = openPgpApiFactory; this.openPgpApiFactory = openPgpApiFactory;
openPgpProviderPackage = K9.getOpenPgpProvider(); openPgpProviderPackage = K9.getOpenPgpProvider();
} }
@ -110,28 +115,37 @@ public class MessageCryptoHelper {
} }
this.messageAnnotations = new MessageCryptoAnnotations(); this.messageAnnotations = new MessageCryptoAnnotations();
this.state = State.START;
this.currentMessage = message; this.currentMessage = message;
this.cachedDecryptionResult = cachedDecryptionResult; this.cachedDecryptionResult = cachedDecryptionResult;
this.callback = callback; this.callback = callback;
runFirstPass(); nextStep();
} }
private void runFirstPass() { private void findPartsForEncryptionPass() {
List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage); List<Part> encryptedParts = MessageDecryptVerifier.findEncryptedParts(currentMessage);
processFoundEncryptedParts(encryptedParts); processFoundEncryptedParts(encryptedParts);
decryptOrVerifyNextPart();
} }
private void runSecondPass() { private void findPartsForSignaturePass() {
List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations); List<Part> signedParts = MessageDecryptVerifier.findSignedParts(currentMessage, messageAnnotations);
processFoundSignedParts(signedParts); processFoundSignedParts(signedParts);
List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage); List<Part> inlineParts = MessageDecryptVerifier.findPgpInlineParts(currentMessage);
addFoundInlinePgpParts(inlineParts); processFoundInlinePgpParts(inlineParts);
}
decryptOrVerifyNextPart(); private void findPartsForAutocryptPass() {
boolean otherCryptoPerformed = !messageAnnotations.isEmpty();
if (otherCryptoPerformed) {
return;
}
if (autocryptOperations.hasAutocryptHeader(currentMessage)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PLAIN_AUTOCRYPT, currentMessage);
partsToProcess.add(cryptoPart);
}
} }
private void processFoundEncryptedParts(List<Part> foundParts) { private void processFoundEncryptedParts(List<Part> foundParts) {
@ -142,7 +156,7 @@ public class MessageCryptoHelper {
} }
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) { if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_ENCRYPTED, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
continue; continue;
} }
addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart()); addErrorAnnotation(part, CryptoError.ENCRYPTED_BUT_UNSUPPORTED, MessageHelper.createEmptyPart());
@ -158,7 +172,7 @@ public class MessageCryptoHelper {
} }
if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) { if (MessageDecryptVerifier.isPgpMimeEncryptedOrSignedPart(part)) {
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_SIGNED, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
continue; continue;
} }
MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part); MimeBodyPart replacementPart = getMultipartSignedContentPartIfAvailable(part);
@ -171,7 +185,7 @@ public class MessageCryptoHelper {
messageAnnotations.put(part, annotation); messageAnnotations.put(part, annotation);
} }
private void addFoundInlinePgpParts(List<Part> foundParts) { private void processFoundInlinePgpParts(List<Part> foundParts) {
for (Part part : foundParts) { for (Part part : foundParts) {
if (!currentMessage.getFlags().contains(Flag.X_DOWNLOADED_FULL)) { if (!currentMessage.getFlags().contains(Flag.X_DOWNLOADED_FULL)) {
if (MessageDecryptVerifier.isPartPgpInlineEncrypted(part)) { if (MessageDecryptVerifier.isPartPgpInlineEncrypted(part)) {
@ -184,29 +198,34 @@ public class MessageCryptoHelper {
} }
CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part); CryptoPart cryptoPart = new CryptoPart(CryptoPartType.PGP_INLINE, part);
partsToDecryptOrVerify.add(cryptoPart); partsToProcess.add(cryptoPart);
} }
} }
private void decryptOrVerifyNextPart() { private void nextStep() {
if (isCancelled) { if (isCancelled) {
return; return;
} }
if (partsToDecryptOrVerify.isEmpty()) { while (state != State.FINISHED && partsToProcess.isEmpty()) {
runSecondPassOrReturnResultToFragment(); findPartsForNextPass();
}
if (state == State.FINISHED) {
callbackReturnResult();
return; return;
} }
CryptoPart cryptoPart = partsToDecryptOrVerify.peekFirst();
startDecryptingOrVerifyingPart(cryptoPart);
}
private void startDecryptingOrVerifyingPart(CryptoPart cryptoPart) {
if (!isBoundToCryptoProviderService()) { if (!isBoundToCryptoProviderService()) {
connectToCryptoProviderService(); connectToCryptoProviderService();
return;
}
currentCryptoPart = partsToProcess.peekFirst();
if (currentCryptoPart.type == CryptoPartType.PLAIN_AUTOCRYPT) {
processAutocryptHeaderForCurrentPart();
} else { } else {
decryptOrVerifyPart(cryptoPart); decryptOrVerifyCurrentPart();
} }
} }
@ -222,7 +241,7 @@ public class MessageCryptoHelper {
public void onBound(IOpenPgpService2 service) { public void onBound(IOpenPgpService2 service) {
openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service); openPgpApi = openPgpApiFactory.createOpenPgpApi(context, service);
decryptOrVerifyNextPart(); nextStep();
} }
@Override @Override
@ -234,24 +253,26 @@ public class MessageCryptoHelper {
openPgpServiceConnection.bindToService(); openPgpServiceConnection.bindToService();
} }
private void decryptOrVerifyPart(CryptoPart cryptoPart) { private void decryptOrVerifyCurrentPart() {
currentCryptoPart = cryptoPart; Intent apiIntent = userInteractionResultIntent;
Intent decryptIntent = userInteractionResultIntent;
userInteractionResultIntent = null; userInteractionResultIntent = null;
if (decryptIntent == null) { if (apiIntent == null) {
decryptIntent = getDecryptionIntent(); apiIntent = getDecryptVerifyIntent();
} }
decryptVerify(decryptIntent); decryptVerify(apiIntent);
} }
@NonNull @NonNull
private Intent getDecryptionIntent() { private Intent getDecryptVerifyIntent() {
Intent decryptIntent = new Intent(OpenPgpApi.ACTION_DECRYPT_VERIFY); Intent decryptIntent = new Intent(OpenPgpApi.ACTION_DECRYPT_VERIFY);
Address[] from = currentMessage.getFrom(); Address[] from = currentMessage.getFrom();
if (from.length > 0) { if (from.length > 0) {
decryptIntent.putExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS, from[0].getAddress()); decryptIntent.putExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS, from[0].getAddress());
// we add this here independently of the autocrypt peer update, to allow picking up signing keys as gossip
decryptIntent.putExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID, from[0].getAddress());
} }
autocryptOperations.addAutocryptPeerUpdateToIntentIfPresent(currentMessage, decryptIntent);
decryptIntent.putExtra(OpenPgpApi.EXTRA_SUPPORT_OVERRIDE_CRYPTO_WARNING, true); decryptIntent.putExtra(OpenPgpApi.EXTRA_SUPPORT_OVERRIDE_CRYPTO_WARNING, true);
decryptIntent.putExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT, cachedDecryptionResult); decryptIntent.putExtra(OpenPgpApi.EXTRA_DECRYPTION_RESULT, cachedDecryptionResult);
@ -259,22 +280,24 @@ public class MessageCryptoHelper {
return decryptIntent; return decryptIntent;
} }
private void decryptVerify(Intent intent) { private void decryptVerify(Intent apiIntent) {
try { try {
CryptoPartType cryptoPartType = currentCryptoPart.type; CryptoPartType cryptoPartType = currentCryptoPart.type;
switch (cryptoPartType) { switch (cryptoPartType) {
case PGP_SIGNED: { case PGP_SIGNED: {
callAsyncDetachedVerify(intent); callAsyncDetachedVerify(apiIntent);
return; return;
} }
case PGP_ENCRYPTED: { case PGP_ENCRYPTED: {
callAsyncDecrypt(intent); callAsyncDecrypt(apiIntent);
return; return;
} }
case PGP_INLINE: { case PGP_INLINE: {
callAsyncInlineOperation(intent); callAsyncInlineOperation(apiIntent);
return; return;
} }
case PLAIN_AUTOCRYPT:
throw new IllegalStateException("This part type must have been handled previously!");
} }
throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType); throw new IllegalStateException("Unknown crypto part type: " + cryptoPartType);
@ -285,6 +308,23 @@ public class MessageCryptoHelper {
} }
} }
private void processAutocryptHeaderForCurrentPart() {
Intent intent = new Intent(OpenPgpApi.ACTION_UPDATE_AUTOCRYPT_PEER);
boolean hasInlineKeyData = autocryptOperations.addAutocryptPeerUpdateToIntentIfPresent(
(Message) currentCryptoPart.part, intent);
if (hasInlineKeyData) {
Timber.d("Passing autocrypt data from plain mail to OpenPGP API");
// We don't care about the result here, so we just call this fire-and-forget wait to minimize delay
openPgpApi.executeApiAsync(intent, null, null, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
Timber.d("Autocrypt update OK!");
}
});
}
onCryptoFinished();
}
private void callAsyncInlineOperation(Intent intent) throws IOException { private void callAsyncInlineOperation(Intent intent) throws IOException {
OpenPgpDataSource dataSource = getDataSourceForEncryptedOrInlineData(); OpenPgpDataSource dataSource = getDataSourceForEncryptedOrInlineData();
OpenPgpDataSink<MimeBodyPart> dataSink = getDataSinkForDecryptedInlineData(); OpenPgpDataSink<MimeBodyPart> dataSink = getDataSinkForDecryptedInlineData();
@ -534,7 +574,7 @@ public class MessageCryptoHelper {
} }
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
userInteractionResultIntent = data; userInteractionResultIntent = data;
decryptOrVerifyNextPart(); nextStep();
} else { } else {
onCryptoOperationCanceled(); onCryptoOperationCanceled();
} }
@ -586,31 +626,55 @@ public class MessageCryptoHelper {
} }
private void onCryptoFinished() { private void onCryptoFinished() {
boolean currentPartIsFirstInQueue = partsToDecryptOrVerify.peekFirst() == currentCryptoPart; boolean currentPartIsFirstInQueue = partsToProcess.peekFirst() == currentCryptoPart;
if (!currentPartIsFirstInQueue) { if (!currentPartIsFirstInQueue) {
throw new IllegalStateException( throw new IllegalStateException(
"Trying to remove part from queue that is not the currently processed one!"); "Trying to remove part from queue that is not the currently processed one!");
} }
if (currentCryptoPart != null) { if (currentCryptoPart != null) {
partsToDecryptOrVerify.removeFirst(); partsToProcess.removeFirst();
currentCryptoPart = null; currentCryptoPart = null;
} else { } else {
Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!"); Timber.e(new Throwable(), "Got to onCryptoFinished() with no part in processing!");
} }
decryptOrVerifyNextPart(); nextStep();
} }
private void runSecondPassOrReturnResultToFragment() { private void findPartsForNextPass() {
if (secondPassStarted) { switch (state) {
callbackReturnResult(); case START: {
state = State.ENCRYPTION;
findPartsForEncryptionPass();
return; return;
} }
secondPassStarted = true;
runSecondPass(); case ENCRYPTION: {
state = State.SIGNATURES;
findPartsForSignaturePass();
return;
}
case SIGNATURES: {
state = State.AUTOCRYPT;
findPartsForAutocryptPass();
return;
}
case AUTOCRYPT: {
state = State.FINISHED;
return;
}
default: {
throw new IllegalStateException("unhandled state");
}
}
} }
private void cleanupAfterProcessingFinished() { private void cleanupAfterProcessingFinished() {
partsToDecryptOrVerify.clear(); partsToProcess.clear();
openPgpApi = null; openPgpApi = null;
if (openPgpServiceConnection != null) { if (openPgpServiceConnection != null) {
openPgpServiceConnection.unbindFromService(); openPgpServiceConnection.unbindFromService();
@ -699,7 +763,8 @@ public class MessageCryptoHelper {
private enum CryptoPartType { private enum CryptoPartType {
PGP_INLINE, PGP_INLINE,
PGP_ENCRYPTED, PGP_ENCRYPTED,
PGP_SIGNED PGP_SIGNED,
PLAIN_AUTOCRYPT
} }
@Nullable @Nullable
@ -729,4 +794,8 @@ public class MessageCryptoHelper {
return NO_REPLACEMENT_PART; return NO_REPLACEMENT_PART;
} }
} }
private enum State {
START, ENCRYPTION, SIGNATURES, AUTOCRYPT, FINISHED
}
} }

View file

@ -0,0 +1,118 @@
package com.fsck.k9.autocrypt;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.mail.internet.MimeMessage;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk = 21)
@SuppressWarnings("WeakerAccess")
public class AutocryptHeaderParserTest {
AutocryptHeaderParser autocryptHeaderParser = AutocryptHeaderParser.getInstance();
@Before
public void setUp() throws Exception {
BinaryTempFileBody.setTempDirectory(RuntimeEnvironment.application.getCacheDir());
}
// Test cases taken from: https://github.com/mailencrypt/autocrypt/tree/master/src/tests/data
@Test
public void getValidAutocryptHeader__withNoHeader__shouldReturnNull() throws Exception {
MimeMessage message = parseFromResource("autocrypt/no_autocrypt.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNull(autocryptHeader);
}
@Test
public void getValidAutocryptHeader__withBrokenBase64__shouldReturnNull() throws Exception {
MimeMessage message = parseFromResource("autocrypt/rsa2048-broken-base64.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNull(autocryptHeader);
}
@Test
public void getValidAutocryptHeader__withSimpleAutocrypt() throws Exception {
MimeMessage message = parseFromResource("autocrypt/rsa2048-simple.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNotNull(autocryptHeader);
assertEquals("alice@testsuite.autocrypt.org", autocryptHeader.addr);
assertEquals(0, autocryptHeader.parameters.size());
assertEquals(1225, autocryptHeader.keyData.length);
}
@Test
public void getValidAutocryptHeader__withExplicitType() throws Exception {
MimeMessage message = parseFromResource("autocrypt/rsa2048-explicit-type.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNotNull(autocryptHeader);
assertEquals("alice@testsuite.autocrypt.org", autocryptHeader.addr);
assertEquals(0, autocryptHeader.parameters.size());
}
@Test
public void getValidAutocryptHeader__withUnknownType__shouldReturnNull() throws Exception {
MimeMessage message = parseFromResource("autocrypt/unknown-type.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNull(autocryptHeader);
}
@Test
public void getValidAutocryptHeader__withUnknownCriticalHeader__shouldReturnNull() throws Exception {
MimeMessage message = parseFromResource("autocrypt/rsa2048-unknown-critical.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNull(autocryptHeader);
}
@Test
public void getValidAutocryptHeader__withUnknownNonCriticalHeader() throws Exception {
MimeMessage message = parseFromResource("autocrypt/rsa2048-unknown-non-critical.eml");
AutocryptHeader autocryptHeader = autocryptHeaderParser.getValidAutocryptHeader(message);
assertNotNull(autocryptHeader);
assertEquals("alice@testsuite.autocrypt.org", autocryptHeader.addr);
assertEquals(1, autocryptHeader.parameters.size());
assertEquals("ignore", autocryptHeader.parameters.get("_monkey"));
}
private MimeMessage parseFromResource(String resourceName) throws IOException, MessagingException {
InputStream inputStream = readFromResourceFile(resourceName);
return MimeMessage.parseMimeMessage(inputStream, false);
}
private InputStream readFromResourceFile(String name) throws FileNotFoundException {
return new FileInputStream(RuntimeEnvironment.application.getPackageResourcePath() + "/src/test/resources/" + name);
}
}

View file

@ -277,7 +277,7 @@ public class PgpMessageBuilderTest {
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_SIGN_KEY_ID); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_SIGN_KEY_ID);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID }); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID });
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_ENCRYPT_OPPORTUNISTIC, false); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses()); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses());
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue()); assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue());
@ -330,7 +330,7 @@ public class PgpMessageBuilderTest {
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_SIGN_KEY_ID); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, TEST_SIGN_KEY_ID);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID }); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { TEST_SELF_ENCRYPT_KEY_ID });
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_ENCRYPT_OPPORTUNISTIC, false); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_OPPORTUNISTIC_ENCRYPTION, false);
expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses()); expectedApiIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, cryptoStatus.getRecipientAddresses());
assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue()); assertIntentEqualsActionAndExtras(expectedApiIntent, capturedApiIntent.getValue());

View file

@ -1,6 +1,7 @@
package com.fsck.k9.ui.crypto; package com.fsck.k9.ui.crypto;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -8,6 +9,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.autocrypt.AutocryptOperations;
import com.fsck.k9.mail.Address; 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;
@ -27,6 +29,7 @@ import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.OpenPgpDecryptionResult; import org.openintents.openpgp.OpenPgpDecryptionResult;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpSinkResultCallback;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSink;
import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource; import org.openintents.openpgp.util.OpenPgpApi.OpenPgpDataSource;
@ -38,6 +41,8 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -54,17 +59,21 @@ public class MessageCryptoHelperTest {
private Intent capturedApiIntent; private Intent capturedApiIntent;
private IOpenPgpSinkResultCallback capturedCallback; private IOpenPgpSinkResultCallback capturedCallback;
private MessageCryptoCallback messageCryptoCallback; private MessageCryptoCallback messageCryptoCallback;
private AutocryptOperations autocryptOperations;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
openPgpApi = mock(OpenPgpApi.class); openPgpApi = mock(OpenPgpApi.class);
autocryptOperations = mock(AutocryptOperations.class);
K9.setOpenPgpProvider("org.example.dummy"); K9.setOpenPgpProvider("org.example.dummy");
OpenPgpApiFactory openPgpApiFactory = mock(OpenPgpApiFactory.class); OpenPgpApiFactory openPgpApiFactory = mock(OpenPgpApiFactory.class);
when(openPgpApiFactory.createOpenPgpApi(any(Context.class), any(IOpenPgpService2.class))).thenReturn(openPgpApi); when(openPgpApiFactory.createOpenPgpApi(any(Context.class), any(IOpenPgpService2.class))).thenReturn(openPgpApi);
messageCryptoHelper = new MessageCryptoHelper(RuntimeEnvironment.application, openPgpApiFactory);
messageCryptoHelper = new MessageCryptoHelper(RuntimeEnvironment.application, openPgpApiFactory,
autocryptOperations);
messageCryptoCallback = mock(MessageCryptoCallback.class); messageCryptoCallback = mock(MessageCryptoCallback.class);
} }
@ -82,6 +91,35 @@ public class MessageCryptoHelperTest {
MessageCryptoAnnotations annotations = captor.getValue(); MessageCryptoAnnotations annotations = captor.getValue();
assertTrue(annotations.isEmpty()); assertTrue(annotations.isEmpty());
verifyNoMoreInteractions(messageCryptoCallback); verifyNoMoreInteractions(messageCryptoCallback);
verify(autocryptOperations).hasAutocryptHeader(message);
verifyNoMoreInteractions(autocryptOperations);
}
@Test
public void textPlain_withAutocrypt() throws Exception {
MimeMessage message = new MimeMessage();
message.setUid("msguid");
message.setHeader("Content-Type", "text/plain");
when(autocryptOperations.hasAutocryptHeader(message)).thenReturn(true);
when(autocryptOperations.addAutocryptPeerUpdateToIntentIfPresent(same(message), any(Intent.class))).thenReturn(true);
MessageCryptoCallback messageCryptoCallback = mock(MessageCryptoCallback.class);
messageCryptoHelper.asyncStartOrResumeProcessingMessage(message, messageCryptoCallback, null);
ArgumentCaptor<MessageCryptoAnnotations> captor = ArgumentCaptor.forClass(MessageCryptoAnnotations.class);
verify(messageCryptoCallback).onCryptoOperationsFinished(captor.capture());
MessageCryptoAnnotations annotations = captor.getValue();
assertTrue(annotations.isEmpty());
verifyNoMoreInteractions(messageCryptoCallback);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(autocryptOperations).addAutocryptPeerUpdateToIntentIfPresent(same(message), intentCaptor.capture());
verify(openPgpApi).executeApiAsync(same(intentCaptor.getValue()), same((InputStream) null),
same((OutputStream) null), any(IOpenPgpCallback.class));
} }
@Test @Test
@ -161,6 +199,9 @@ public class MessageCryptoHelperTest {
assertEquals(OpenPgpApi.ACTION_DECRYPT_VERIFY, capturedApiIntent.getAction()); assertEquals(OpenPgpApi.ACTION_DECRYPT_VERIFY, capturedApiIntent.getAction());
assertEquals("test@example.org", capturedApiIntent.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS)); assertEquals("test@example.org", capturedApiIntent.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS));
verify(autocryptOperations).addAutocryptPeerUpdateToIntentIfPresent(message, capturedApiIntent);
verifyNoMoreInteractions(autocryptOperations);
} }
@Test @Test
@ -201,6 +242,8 @@ public class MessageCryptoHelperTest {
assertEquals("test@example.org", capturedApiIntent.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS)); assertEquals("test@example.org", capturedApiIntent.getStringExtra(OpenPgpApi.EXTRA_SENDER_ADDRESS));
assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.OPENPGP_OK, decryptedPart, assertPartAnnotationHasState(message, messageCryptoCallback, CryptoError.OPENPGP_OK, decryptedPart,
decryptionResult, signatureResult, pendingIntent); decryptionResult, signatureResult, pendingIntent);
verify(autocryptOperations).addAutocryptPeerUpdateToIntentIfPresent(message, capturedApiIntent);
verifyNoMoreInteractions(autocryptOperations);
} }
private void processEncryptedMessageAndCaptureMocks(Message message, Body encryptedBody, OutputStream outputStream) private void processEncryptedMessageAndCaptureMocks(Message message, Body encryptedBody, OutputStream outputStream)

View file

@ -0,0 +1,11 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: INBOME with invalid type attribute
Date: Sat, 17 Dec 2016 10:51:48 +0100
Message-ID: <unknown-type@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This message contains no INBOME header
An agent capable of INBOME level 0 should not try to parse it as a header and crash.

View file

@ -0,0 +1,35 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: an INBOME RSA test
Autocrypt: addr=alice@testsuite.autocrypt.org; keydata=
m!@#$%hVF+ABCADu17FBUgA3mCemeKbNaBTyWe3VGxjbu7fUyHgdLK7i3tnd7IRtxQy/AEN2t6Vq
0/xeZEAKYRInsHI/HjvmhqPeWFzipk71jRQ02WUY1pZytFjYNIrTdMk4eLYdC1N0go83PU33V4R8
fc2fWHD8N5JPsDH2xOB6WNWkMPxgMbtGIa0QTx7TINhDif4/1/VcrX3wz1gZ6xYI+sujbC54iBZo
qbEfu4SFVvp53d+a1plxBzuZ/X6nqJqcysiS7ORMieBvU6W/mVeiAxwN4qcAI5s+rGmRnP8ltONK
/P1ScH6lmELgqm8Z/M0wdiYgywme/bdEQOg3s0S/8nCIFmwUchN7ABEBAAG0HWFsaWNlQHRlc3Rz
dWl0ZS5hdXRvY3J5cHQub3JniQFOBBMBCAA4FiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+AC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuvxTPNmTvX/k4wf+JJZ0M0rZeAXbnxdR6HDU
ZYL734Z8x/HRpz3vzK4VQQJ4oIbUQPwydZmAlTlglQY48IWWOdJnYvn2pIhlTM/T8q9ZfmOyp6i1
jxFCPT+2ma4DjNOqYFhfnULE/MYc6xeVaBcwGj7yvAW7YY7156/wDo6+9TCd/a9mzOFCGS0yQoRa
K3uDajA+G/SmbC8t/3X8+5sapvi9Ru0HNkIzaj1jhH+kW6628E7nkf9aN9LodXHfs1UtfuLqM8VG
Ysk9474x9QxbsrJ4YvXeFwM9zAs+Pvj4lnpH/0WOU8jJc3uarluGH58kTHM5/5+p0TeMpOHX7OEw
JndsBOV9gFc6FMx4hLkBDQRYVRfiAQgA3ad+Aat4UY8xvQQutLYb8e417XZN1zVmKypyReB0l0Zf
HA6Qc7uxnJQ7dzIEZAxdnjvTYJaCFrOCBXAyPHpShVMpKqQP+kBBY/WiC3BSUALR3xqp7k5/sjLD
+K4dAacXEc7nyXP5o5+oqBXEH8Ls5X440c9A3EdsvVlncvSW5ILLItlFHmQd6f1ynnjK+FQwYJRJ
ypDuqJpYkA1vn7+XxeQShpX105rM3C8tJUxRAP3QFimenn4Zm2BDhQpCneuBt239rkXOAXsR0PnJ
fV8eNAEsE8IIqnPoSlBme5DZAri69+joYmTeSKGuj4aoxzDlx1AQigwpMISLciTXLnJypwARAQAB
iQE2BBgBCAAgFiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+ICGwwACgkQuvxTPNmTvX8dOwf9
H72BGoYJkuuFrbQ6F/mH7gG9z3ytQHRD2Z0ja+3O7YnJBpotHFFjF7yHGj0FtmQR0Q7KnhkJ/3mv
fkvuaH3Gcjli/E7VASastuFDFkGANLmGZVGQQ2iTYFG1aejjtGb01vcaPrgE9WDueMB+Pn6/QbDc
5SWCrVWrRFZKrwbAGw35GySoFYpxXyCNsk6q6Db56plllPZjrYj7axF0yN536D1ntEVFDOdKZq8x
Tb9P/4Tq9NKRLE4+aO6qCqEOz+V1OeOvYLw58BfnzXY8rXF93D/86YLyilv6p5WGaS/cRhIzr+Xq
+qBLD/vW+dh72e8MvcduX3tXV3Vkg0mkGekdOw==
Date: Sat, 17 Dec 2016 10:07:48 +0100
Message-ID: <rsa2048-simple@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This contains an INBOME header with invalid characters in the base64 key
data.
This header should be ignored by all user agents.

View file

@ -0,0 +1,40 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: inbome rsa2048 with correct type
Autocrypt: addr=alice@testsuite.autocrypt.org; type=1; keydata=
mQENBFhVF+ABCADu17FBUgA3mCemeKbNaBTyWe3VGxjbu7fUyHgdLK7i3tnd7IRtxQy/AEN2t6Vq
0/xeZEAKYRInsHI/HjvmhqPeWFzipk71jRQ02WUY1pZytFjYNIrTdMk4eLYdC1N0go83PU33V4R8
fc2fWHD8N5JPsDH2xOB6WNWkMPxgMbtGIa0QTx7TINhDif4/1/VcrX3wz1gZ6xYI+sujbC54iBZo
qbEfu4SFVvp53d+a1plxBzuZ/X6nqJqcysiS7ORMieBvU6W/mVeiAxwN4qcAI5s+rGmRnP8ltONK
/P1ScH6lmELgqm8Z/M0wdiYgywme/bdEQOg3s0S/8nCIFmwUchN7ABEBAAG0HWFsaWNlQHRlc3Rz
dWl0ZS5hdXRvY3J5cHQub3JniQFOBBMBCAA4FiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+AC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuvxTPNmTvX/k4wf+JJZ0M0rZeAXbnxdR6HDU
ZYL734Z8x/HRpz3vzK4VQQJ4oIbUQPwydZmAlTlglQY48IWWOdJnYvn2pIhlTM/T8q9ZfmOyp6i1
jxFCPT+2ma4DjNOqYFhfnULE/MYc6xeVaBcwGj7yvAW7YY7156/wDo6+9TCd/a9mzOFCGS0yQoRa
K3uDajA+G/SmbC8t/3X8+5sapvi9Ru0HNkIzaj1jhH+kW6628E7nkf9aN9LodXHfs1UtfuLqM8VG
Ysk9474x9QxbsrJ4YvXeFwM9zAs+Pvj4lnpH/0WOU8jJc3uarluGH58kTHM5/5+p0TeMpOHX7OEw
JndsBOV9gFc6FMx4hLkBDQRYVRfiAQgA3ad+Aat4UY8xvQQutLYb8e417XZN1zVmKypyReB0l0Zf
HA6Qc7uxnJQ7dzIEZAxdnjvTYJaCFrOCBXAyPHpShVMpKqQP+kBBY/WiC3BSUALR3xqp7k5/sjLD
+K4dAacXEc7nyXP5o5+oqBXEH8Ls5X440c9A3EdsvVlncvSW5ILLItlFHmQd6f1ynnjK+FQwYJRJ
ypDuqJpYkA1vn7+XxeQShpX105rM3C8tJUxRAP3QFimenn4Zm2BDhQpCneuBt239rkXOAXsR0PnJ
fV8eNAEsE8IIqnPoSlBme5DZAri69+joYmTeSKGuj4aoxzDlx1AQigwpMISLciTXLnJypwARAQAB
iQE2BBgBCAAgFiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+ICGwwACgkQuvxTPNmTvX8dOwf9
H72BGoYJkuuFrbQ6F/mH7gG9z3ytQHRD2Z0ja+3O7YnJBpotHFFjF7yHGj0FtmQR0Q7KnhkJ/3mv
fkvuaH3Gcjli/E7VASastuFDFkGANLmGZVGQQ2iTYFG1aejjtGb01vcaPrgE9WDueMB+Pn6/QbDc
5SWCrVWrRFZKrwbAGw35GySoFYpxXyCNsk6q6Db56plllPZjrYj7axF0yN536D1ntEVFDOdKZq8x
Tb9P/4Tq9NKRLE4+aO6qCqEOz+V1OeOvYLw58BfnzXY8rXF93D/86YLyilv6p5WGaS/cRhIzr+Xq
+qBLD/vW+dh72e8MvcduX3tXV3Vkg0mkGekdOw==
Date: Sat, 17 Dec 2016 10:49:02 +0100
Message-ID: <rsa2048-explicit-type@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This message contains the standard INBOME header, but includes an
explicit "type=p" attribute. This is the default value of type, so it
should not be necessary to include this attribute.
"p" signifies that the key value is a specialized subset of OpenPGP.
This should be accepted by any agent capable of INBOME level 0.
--dkg

View file

@ -0,0 +1,34 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Autocrypt-Bot <bot@autocrypt.org>
Subject: an INBOME RSA test
INBOME: to=alice@testsuite.autocrypt.org; keydata=
mQENBFhVF+ABCADu17FBUgA3mCemeKbNaBTyWe3VGxjbu7fUyHgdLK7i3tnd7IRtxQy/AEN2t6Vq
0/xeZEAKYRInsHI/HjvmhqPeWFzipk71jRQ02WUY1pZytFjYNIrTdMk4eLYdC1N0go83PU33V4R8
fc2fWHD8N5JPsDH2xOB6WNWkMPxgMbtGIa0QTx7TINhDif4/1/VcrX3wz1gZ6xYI+sujbC54iBZo
qbEfu4SFVvp53d+a1plxBzuZ/X6nqJqcysiS7ORMieBvU6W/mVeiAxwN4qcAI5s+rGmRnP8ltONK
/P1ScH6lmELgqm8Z/M0wdiYgywme/bdEQOg3s0S/8nCIFmwUchN7ABEBAAG0HWFsaWNlQHRlc3Rz
dWl0ZS5hdXRvY3J5cHQub3JniQFOBBMBCAA4FiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+AC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuvxTPNmTvX/k4wf+JJZ0M0rZeAXbnxdR6HDU
ZYL734Z8x/HRpz3vzK4VQQJ4oIbUQPwydZmAlTlglQY48IWWOdJnYvn2pIhlTM/T8q9ZfmOyp6i1
jxFCPT+2ma4DjNOqYFhfnULE/MYc6xeVaBcwGj7yvAW7YY7156/wDo6+9TCd/a9mzOFCGS0yQoRa
K3uDajA+G/SmbC8t/3X8+5sapvi9Ru0HNkIzaj1jhH+kW6628E7nkf9aN9LodXHfs1UtfuLqM8VG
Ysk9474x9QxbsrJ4YvXeFwM9zAs+Pvj4lnpH/0WOU8jJc3uarluGH58kTHM5/5+p0TeMpOHX7OEw
JndsBOV9gFc6FMx4hLkBDQRYVRfiAQgA3ad+Aat4UY8xvQQutLYb8e417XZN1zVmKypyReB0l0Zf
HA6Qc7uxnJQ7dzIEZAxdnjvTYJaCFrOCBXAyPHpShVMpKqQP+kBBY/WiC3BSUALR3xqp7k5/sjLD
+K4dAacXEc7nyXP5o5+oqBXEH8Ls5X440c9A3EdsvVlncvSW5ILLItlFHmQd6f1ynnjK+FQwYJRJ
ypDuqJpYkA1vn7+XxeQShpX105rM3C8tJUxRAP3QFimenn4Zm2BDhQpCneuBt239rkXOAXsR0PnJ
fV8eNAEsE8IIqnPoSlBme5DZAri69+joYmTeSKGuj4aoxzDlx1AQigwpMISLciTXLnJypwARAQAB
iQE2BBgBCAAgFiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+ICGwwACgkQuvxTPNmTvX8dOwf9
H72BGoYJkuuFrbQ6F/mH7gG9z3ytQHRD2Z0ja+3O7YnJBpotHFFjF7yHGj0FtmQR0Q7KnhkJ/3mv
fkvuaH3Gcjli/E7VASastuFDFkGANLmGZVGQQ2iTYFG1aejjtGb01vcaPrgE9WDueMB+Pn6/QbDc
5SWCrVWrRFZKrwbAGw35GySoFYpxXyCNsk6q6Db56plllPZjrYj7axF0yN536D1ntEVFDOdKZq8x
Tb9P/4Tq9NKRLE4+aO6qCqEOz+V1OeOvYLw58BfnzXY8rXF93D/86YLyilv6p5WGaS/cRhIzr+Xq
+qBLD/vW+dh72e8MvcduX3tXV3Vkg0mkGekdOw==
Date: Sat, 17 Dec 2016 10:07:48 +0100
Message-ID: <rsa2048-simple-to-bot@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This contains a default, minimal INBOME header using an RSA 2048 key.
This should be importable and valid by any agent supporting INBOME level 0.

View file

@ -0,0 +1,34 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: an INBOME RSA test
Autocrypt: addr=alice@testsuite.autocrypt.org; keydata=
mQENBFhVF+ABCADu17FBUgA3mCemeKbNaBTyWe3VGxjbu7fUyHgdLK7i3tnd7IRtxQy/AEN2t6Vq
0/xeZEAKYRInsHI/HjvmhqPeWFzipk71jRQ02WUY1pZytFjYNIrTdMk4eLYdC1N0go83PU33V4R8
fc2fWHD8N5JPsDH2xOB6WNWkMPxgMbtGIa0QTx7TINhDif4/1/VcrX3wz1gZ6xYI+sujbC54iBZo
qbEfu4SFVvp53d+a1plxBzuZ/X6nqJqcysiS7ORMieBvU6W/mVeiAxwN4qcAI5s+rGmRnP8ltONK
/P1ScH6lmELgqm8Z/M0wdiYgywme/bdEQOg3s0S/8nCIFmwUchN7ABEBAAG0HWFsaWNlQHRlc3Rz
dWl0ZS5hdXRvY3J5cHQub3JniQFOBBMBCAA4FiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+AC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuvxTPNmTvX/k4wf+JJZ0M0rZeAXbnxdR6HDU
ZYL734Z8x/HRpz3vzK4VQQJ4oIbUQPwydZmAlTlglQY48IWWOdJnYvn2pIhlTM/T8q9ZfmOyp6i1
jxFCPT+2ma4DjNOqYFhfnULE/MYc6xeVaBcwGj7yvAW7YY7156/wDo6+9TCd/a9mzOFCGS0yQoRa
K3uDajA+G/SmbC8t/3X8+5sapvi9Ru0HNkIzaj1jhH+kW6628E7nkf9aN9LodXHfs1UtfuLqM8VG
Ysk9474x9QxbsrJ4YvXeFwM9zAs+Pvj4lnpH/0WOU8jJc3uarluGH58kTHM5/5+p0TeMpOHX7OEw
JndsBOV9gFc6FMx4hLkBDQRYVRfiAQgA3ad+Aat4UY8xvQQutLYb8e417XZN1zVmKypyReB0l0Zf
HA6Qc7uxnJQ7dzIEZAxdnjvTYJaCFrOCBXAyPHpShVMpKqQP+kBBY/WiC3BSUALR3xqp7k5/sjLD
+K4dAacXEc7nyXP5o5+oqBXEH8Ls5X440c9A3EdsvVlncvSW5ILLItlFHmQd6f1ynnjK+FQwYJRJ
ypDuqJpYkA1vn7+XxeQShpX105rM3C8tJUxRAP3QFimenn4Zm2BDhQpCneuBt239rkXOAXsR0PnJ
fV8eNAEsE8IIqnPoSlBme5DZAri69+joYmTeSKGuj4aoxzDlx1AQigwpMISLciTXLnJypwARAQAB
iQE2BBgBCAAgFiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+ICGwwACgkQuvxTPNmTvX8dOwf9
H72BGoYJkuuFrbQ6F/mH7gG9z3ytQHRD2Z0ja+3O7YnJBpotHFFjF7yHGj0FtmQR0Q7KnhkJ/3mv
fkvuaH3Gcjli/E7VASastuFDFkGANLmGZVGQQ2iTYFG1aejjtGb01vcaPrgE9WDueMB+Pn6/QbDc
5SWCrVWrRFZKrwbAGw35GySoFYpxXyCNsk6q6Db56plllPZjrYj7axF0yN536D1ntEVFDOdKZq8x
Tb9P/4Tq9NKRLE4+aO6qCqEOz+V1OeOvYLw58BfnzXY8rXF93D/86YLyilv6p5WGaS/cRhIzr+Xq
+qBLD/vW+dh72e8MvcduX3tXV3Vkg0mkGekdOw==
Date: Sat, 17 Dec 2016 10:07:48 +0100
Message-ID: <rsa2048-simple@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This contains a default, minimal INBOME header using an RSA 2048 key.
This should be importable and valid by any agent supporting INBOME level 0.

View file

@ -0,0 +1,35 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: another inbome with rsa2048 but with an unknown critical attribute
Autocrypt: addr=alice@testsuite.autocrypt.org; danger=do-not-use; keydata=
mQENBFhVGA8BCADK+qTRkAfax0LtJ6RiyxzuAFyIohBTwvtcOM2sd/tRmWq1eyNif5AGDnc1+b6X
zJ6l3BXiYM/8qXU/F04UA5BP05SgIqXjqT5I13blrydjKtUbZFchK7lJU7cyDbar+TH70DZURSQm
MusCj0+fdx6hx8y4LSOM68rjwVeq7JXAPU78QQsYgMrbtkf5mZWUquDdb7tEoxU+PcNifvtvuHF2
ILv09a4Fi8thJG4i/3LxMFtmMLIiZWLfk5KpXAKrOy436e1LCm3vesALcihPNppb803dgBqpvvEE
9W7sg5NUy3P8+fTEuvI8HYYd+lEvYe2ojm4HVTts4YFHmzaGVzHLABEBAAG0HVRoaXMgc2hvdWxk
IG5ldmVyIGJlIGltcG9ydGVkiQFOBBMBCAA4FiEE0uuMX0KSMgVBfC/25MZusLe5gWMFAlhVGA8C
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ5MZusLe5gWN9Hwf+PpLCCV7TiGc1nqIxLMTs
O84PVLSQZB642/QhLoMYXQ5iqty5H2FqGuK+uWLCnM+yIMDkcJC3ayWfa06fs3JOipVKlMh8hHnU
6/FHJB+3eZrc4lhh5B67Vi8Xg43pTP+I9ct/PlbHvD9kYw+DpcmCz0XILhaUP0R1oQ6M5KI49uLg
LAdNczcEtcw3A/hZ5ZTUe3o3gav0XDBXFCgGjkI+CaMjKb/HjgNM9YsrGxUxH1RFMYqTfrmCklHD
EboQc1Qtzi5rIwzVR3zSryve9KHH75TCfDApghwUBKSLNh374hjTFj5v5kPAxG3njX6EOqHS/UVX
Mn5aEVn0n6S1y+DJZLkBDQRYVRgRAQgAzQgD/CluB1wuBeI8qaqmIxG8epHCPstQ4kee6FuFWi3F
Lqtyk1R9tB4UL40gpEkpzB+qYms/zs9SeicuNcXoXA4bMcNGDFz3mZ1d9qG2izgC19e9p50oXiMY
cr8GM1Qcb77dmxlk829cBpr+X7NDKJy9VMGsqNYukgFDnNIzty0oMdCLSzpqi3UtXtCGYDqIiltU
aT8XdMAvddr6Scgpkz3wrqi/bVagc+q4IdKL0r8iL7o3EnTf/5Dc2XUaCFJLCa3Rk6oat5kTWjan
sp/K5k/VzSDcESji8n6xl0OzD2okhmX8iJZg1hhyI8hNmtW3boe51Hkkdlj+wC8Y2Fgh4QARAQAB
iQE2BBgBCAAgFiEE0uuMX0KSMgVBfC/25MZusLe5gWMFAlhVGBECGwwACgkQ5MZusLe5gWOn/Qf/
aeV7CqZW/YN4/LhXjJG7i+iDJYv/9Lr12dvgjO/sOlmDPHkEzXPMLKalm0biMPN7E1woQzcKt7Qy
eF/CRcVKK1TM6wdClOj2jErnWyx85/uZfnG9QRD41rhInk891A8LGebPZ6DJeJR/uwzMniEgNnKN
AMuGy95ckwlM3AfwzsKPTUUFnBAmSwWfMLRxjZPNefeo1Ic8mMRAT3d5sfDUx/4wm8tyiNLuOSkm
Ej6ONYpESD2sJGMo3ZY96pkzir7ZH++4mH6PwZg1ZT2nO+0PtaB9DHRGfBrzH85d4aLFZD9txx3p
ewabrNpYI/cJu9hUTaTM7wZaG5kmfStwihKYUg==
Date: Sat, 17 Dec 2016 10:32:49 +0100
Message-ID: <rsa2048-unknown-critical@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This message contains an INBOME header with RSA 2048, but with an
unknown critical attribute. Agents that are compatible with INBOME
level 0 should ignore this header because of the unknown critical
attribute.

View file

@ -0,0 +1,36 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: inbome: rsa 2048 with unknown non-critical attribute
Autocrypt: addr=alice@testsuite.autocrypt.org; _monkey=ignore; keydata=
mQENBFhVF+ABCADu17FBUgA3mCemeKbNaBTyWe3VGxjbu7fUyHgdLK7i3tnd7IRtxQy/AEN2t6Vq
0/xeZEAKYRInsHI/HjvmhqPeWFzipk71jRQ02WUY1pZytFjYNIrTdMk4eLYdC1N0go83PU33V4R8
fc2fWHD8N5JPsDH2xOB6WNWkMPxgMbtGIa0QTx7TINhDif4/1/VcrX3wz1gZ6xYI+sujbC54iBZo
qbEfu4SFVvp53d+a1plxBzuZ/X6nqJqcysiS7ORMieBvU6W/mVeiAxwN4qcAI5s+rGmRnP8ltONK
/P1ScH6lmELgqm8Z/M0wdiYgywme/bdEQOg3s0S/8nCIFmwUchN7ABEBAAG0HWFsaWNlQHRlc3Rz
dWl0ZS5hdXRvY3J5cHQub3JniQFOBBMBCAA4FiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+AC
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuvxTPNmTvX/k4wf+JJZ0M0rZeAXbnxdR6HDU
ZYL734Z8x/HRpz3vzK4VQQJ4oIbUQPwydZmAlTlglQY48IWWOdJnYvn2pIhlTM/T8q9ZfmOyp6i1
jxFCPT+2ma4DjNOqYFhfnULE/MYc6xeVaBcwGj7yvAW7YY7156/wDo6+9TCd/a9mzOFCGS0yQoRa
K3uDajA+G/SmbC8t/3X8+5sapvi9Ru0HNkIzaj1jhH+kW6628E7nkf9aN9LodXHfs1UtfuLqM8VG
Ysk9474x9QxbsrJ4YvXeFwM9zAs+Pvj4lnpH/0WOU8jJc3uarluGH58kTHM5/5+p0TeMpOHX7OEw
JndsBOV9gFc6FMx4hLkBDQRYVRfiAQgA3ad+Aat4UY8xvQQutLYb8e417XZN1zVmKypyReB0l0Zf
HA6Qc7uxnJQ7dzIEZAxdnjvTYJaCFrOCBXAyPHpShVMpKqQP+kBBY/WiC3BSUALR3xqp7k5/sjLD
+K4dAacXEc7nyXP5o5+oqBXEH8Ls5X440c9A3EdsvVlncvSW5ILLItlFHmQd6f1ynnjK+FQwYJRJ
ypDuqJpYkA1vn7+XxeQShpX105rM3C8tJUxRAP3QFimenn4Zm2BDhQpCneuBt239rkXOAXsR0PnJ
fV8eNAEsE8IIqnPoSlBme5DZAri69+joYmTeSKGuj4aoxzDlx1AQigwpMISLciTXLnJypwARAQAB
iQE2BBgBCAAgFiEEfi47NkGai9tG9hBruvxTPNmTvX8FAlhVF+ICGwwACgkQuvxTPNmTvX8dOwf9
H72BGoYJkuuFrbQ6F/mH7gG9z3ytQHRD2Z0ja+3O7YnJBpotHFFjF7yHGj0FtmQR0Q7KnhkJ/3mv
fkvuaH3Gcjli/E7VASastuFDFkGANLmGZVGQQ2iTYFG1aejjtGb01vcaPrgE9WDueMB+Pn6/QbDc
5SWCrVWrRFZKrwbAGw35GySoFYpxXyCNsk6q6Db56plllPZjrYj7axF0yN536D1ntEVFDOdKZq8x
Tb9P/4Tq9NKRLE4+aO6qCqEOz+V1OeOvYLw58BfnzXY8rXF93D/86YLyilv6p5WGaS/cRhIzr+Xq
+qBLD/vW+dh72e8MvcduX3tXV3Vkg0mkGekdOw==
Date: Sat, 17 Dec 2016 10:34:30 +0100
Message-ID: <rsa2048-unknown-non-critical@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This message contains an INBOME header with RSA 2048, but with an
unknown non-critical attribute. Agents that are compatible with
INBOME level 0 should accept this header while ignoring the unknown
attribute.

View file

@ -0,0 +1,36 @@
From: Alice <alice@testsuite.autocrypt.org>
To: Bob <bob@testsuite.autocrypt.org>
Subject: INBOME with invalid type attribute
Autocrypt: addr=alice@testsuite.autocrypt.org; type=x; keydata=
mQENBFhVGA8BCADK+qTRkAfax0LtJ6RiyxzuAFyIohBTwvtcOM2sd/tRmWq1eyNif5AGDnc1+b6X
zJ6l3BXiYM/8qXU/F04UA5BP05SgIqXjqT5I13blrydjKtUbZFchK7lJU7cyDbar+TH70DZURSQm
MusCj0+fdx6hx8y4LSOM68rjwVeq7JXAPU78QQsYgMrbtkf5mZWUquDdb7tEoxU+PcNifvtvuHF2
ILv09a4Fi8thJG4i/3LxMFtmMLIiZWLfk5KpXAKrOy436e1LCm3vesALcihPNppb803dgBqpvvEE
9W7sg5NUy3P8+fTEuvI8HYYd+lEvYe2ojm4HVTts4YFHmzaGVzHLABEBAAG0HVRoaXMgc2hvdWxk
IG5ldmVyIGJlIGltcG9ydGVkiQFOBBMBCAA4FiEE0uuMX0KSMgVBfC/25MZusLe5gWMFAlhVGA8C
GwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ5MZusLe5gWN9Hwf+PpLCCV7TiGc1nqIxLMTs
O84PVLSQZB642/QhLoMYXQ5iqty5H2FqGuK+uWLCnM+yIMDkcJC3ayWfa06fs3JOipVKlMh8hHnU
6/FHJB+3eZrc4lhh5B67Vi8Xg43pTP+I9ct/PlbHvD9kYw+DpcmCz0XILhaUP0R1oQ6M5KI49uLg
LAdNczcEtcw3A/hZ5ZTUe3o3gav0XDBXFCgGjkI+CaMjKb/HjgNM9YsrGxUxH1RFMYqTfrmCklHD
EboQc1Qtzi5rIwzVR3zSryve9KHH75TCfDApghwUBKSLNh374hjTFj5v5kPAxG3njX6EOqHS/UVX
Mn5aEVn0n6S1y+DJZLkBDQRYVRgRAQgAzQgD/CluB1wuBeI8qaqmIxG8epHCPstQ4kee6FuFWi3F
Lqtyk1R9tB4UL40gpEkpzB+qYms/zs9SeicuNcXoXA4bMcNGDFz3mZ1d9qG2izgC19e9p50oXiMY
cr8GM1Qcb77dmxlk829cBpr+X7NDKJy9VMGsqNYukgFDnNIzty0oMdCLSzpqi3UtXtCGYDqIiltU
aT8XdMAvddr6Scgpkz3wrqi/bVagc+q4IdKL0r8iL7o3EnTf/5Dc2XUaCFJLCa3Rk6oat5kTWjan
sp/K5k/VzSDcESji8n6xl0OzD2okhmX8iJZg1hhyI8hNmtW3boe51Hkkdlj+wC8Y2Fgh4QARAQAB
iQE2BBgBCAAgFiEE0uuMX0KSMgVBfC/25MZusLe5gWMFAlhVGBECGwwACgkQ5MZusLe5gWOn/Qf/
aeV7CqZW/YN4/LhXjJG7i+iDJYv/9Lr12dvgjO/sOlmDPHkEzXPMLKalm0biMPN7E1woQzcKt7Qy
eF/CRcVKK1TM6wdClOj2jErnWyx85/uZfnG9QRD41rhInk891A8LGebPZ6DJeJR/uwzMniEgNnKN
AMuGy95ckwlM3AfwzsKPTUUFnBAmSwWfMLRxjZPNefeo1Ic8mMRAT3d5sfDUx/4wm8tyiNLuOSkm
Ej6ONYpESD2sJGMo3ZY96pkzir7ZH++4mH6PwZg1ZT2nO+0PtaB9DHRGfBrzH85d4aLFZD9txx3p
ewabrNpYI/cJu9hUTaTM7wZaG5kmfStwihKYUg==
Date: Sat, 17 Dec 2016 10:51:48 +0100
Message-ID: <unknown-type@testsuite.autocrypt.org>
MIME-Version: 1.0
Content-Type: text/plain
This message contains an INBOME header that claims to be of type "x",
which is not a specified type.
An agent capable of INBOME level 0 should reject this inbome header
because of this type. (it should not try to parse it as a header).

View file

@ -0,0 +1,131 @@
/*
* Copyright (C) 2014-2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openintents.openpgp;
import java.util.Date;
import android.os.Parcel;
import android.os.Parcelable;
@SuppressWarnings("unused")
public class AutocryptPeerUpdate implements Parcelable {
/**
* Since there might be a case where new versions of the client using the library getting
* old versions of the protocol (and thus old versions of this class), we need a versioning
* system for the parcels sent between the clients and the providers.
*/
private static final int PARCELABLE_VERSION = 1;
private final byte[] keyData;
private final Date effectiveDate;
private final PreferEncrypt preferEncrypt;
private AutocryptPeerUpdate(byte[] keyData, Date effectiveDate, PreferEncrypt preferEncrypt) {
this.keyData = keyData;
this.effectiveDate = effectiveDate;
this.preferEncrypt = preferEncrypt;
}
private AutocryptPeerUpdate(Parcel source, int version) {
this.keyData = source.createByteArray();
this.effectiveDate = source.readInt() != 0 ? new Date(source.readLong()) : null;
this.preferEncrypt = PreferEncrypt.values()[source.readInt()];
}
public static AutocryptPeerUpdate create(byte[] keyData, Date timestamp, boolean isMutual) {
return new AutocryptPeerUpdate(keyData, timestamp, isMutual ? PreferEncrypt.MUTUAL : PreferEncrypt.NOPREFERENCE);
}
public byte[] getKeyData() {
return keyData;
}
public boolean hasKeyData() {
return keyData != null;
}
public Date getEffectiveDate() {
return effectiveDate;
}
public PreferEncrypt getPreferEncrypt() {
return preferEncrypt;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
/**
* NOTE: When adding fields in the process of updating this API, make sure to bump
* {@link #PARCELABLE_VERSION}.
*/
dest.writeInt(PARCELABLE_VERSION);
// Inject a placeholder that will store the parcel size from this point on
// (not including the size itself).
int sizePosition = dest.dataPosition();
dest.writeInt(0);
int startPosition = dest.dataPosition();
// version 1
dest.writeByteArray(keyData);
if (effectiveDate != null) {
dest.writeInt(1);
dest.writeLong(effectiveDate.getTime());
} else {
dest.writeInt(0);
}
dest.writeInt(preferEncrypt.ordinal());
// Go back and write the size
int parcelableSize = dest.dataPosition() - startPosition;
dest.setDataPosition(sizePosition);
dest.writeInt(parcelableSize);
dest.setDataPosition(startPosition + parcelableSize);
}
public static final Creator<AutocryptPeerUpdate> CREATOR = new Creator<AutocryptPeerUpdate>() {
public AutocryptPeerUpdate createFromParcel(final Parcel source) {
int version = source.readInt(); // parcelableVersion
int parcelableSize = source.readInt();
int startPosition = source.dataPosition();
AutocryptPeerUpdate vr = new AutocryptPeerUpdate(source, version);
// skip over all fields added in future versions of this parcel
source.setDataPosition(startPosition + parcelableSize);
return vr;
}
public AutocryptPeerUpdate[] newArray(final int size) {
return new AutocryptPeerUpdate[size];
}
};
public enum PreferEncrypt {
NOPREFERENCE, MUTUAL;
}
}

View file

@ -38,7 +38,6 @@ import org.openintents.openpgp.util.ParcelFileDescriptorUtil.DataSinkTransferThr
import org.openintents.openpgp.util.ParcelFileDescriptorUtil.DataSourceTransferThread; import org.openintents.openpgp.util.ParcelFileDescriptorUtil.DataSourceTransferThread;
@SuppressWarnings("unused")
public class OpenPgpApi { public class OpenPgpApi {
public static final String TAG = "OpenPgp API"; public static final String TAG = "OpenPgp API";
@ -48,7 +47,7 @@ public class OpenPgpApi {
/** /**
* see CHANGELOG.md * see CHANGELOG.md
*/ */
public static final int API_VERSION = 10; public static final int API_VERSION = 12;
/** /**
* General extras * General extras
@ -63,16 +62,16 @@ public class OpenPgpApi {
* PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED) * PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED)
*/ */
/**
* This action performs no operation, but can be used to check if the App has permission
* to access the API in general, returning a user interaction PendingIntent otherwise.
* This can be used to trigger the permission dialog explicitly.
*
* This action uses no extras.
*/
public static final String ACTION_CHECK_PERMISSION = "org.openintents.openpgp.action.CHECK_PERMISSION"; public static final String ACTION_CHECK_PERMISSION = "org.openintents.openpgp.action.CHECK_PERMISSION";
/** @Deprecated
* DEPRECATED
* Same as ACTION_CLEARTEXT_SIGN
* <p/>
* optional extras:
* boolean EXTRA_REQUEST_ASCII_ARMOR (DEPRECATED: this makes no sense here)
* char[] EXTRA_PASSPHRASE (key passphrase)
*/
public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN"; public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN";
/** /**
@ -81,10 +80,10 @@ public class OpenPgpApi {
* cleartext signatures per RFC 4880 before the text is actually signed: * cleartext signatures per RFC 4880 before the text is actually signed:
* - end cleartext with newline * - end cleartext with newline
* - remove whitespaces on line endings * - remove whitespaces on line endings
* <p/> *
* required extras: * required extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key) * long EXTRA_SIGN_KEY_ID (key id of signing key)
* <p/> *
* optional extras: * optional extras:
* char[] EXTRA_PASSPHRASE (key passphrase) * char[] EXTRA_PASSPHRASE (key passphrase)
*/ */
@ -94,49 +93,50 @@ public class OpenPgpApi {
* Sign text or binary data resulting in a detached signature. * Sign text or binary data resulting in a detached signature.
* No OutputStream necessary for ACTION_DETACHED_SIGN (No magic pre-processing like in ACTION_CLEARTEXT_SIGN)! * No OutputStream necessary for ACTION_DETACHED_SIGN (No magic pre-processing like in ACTION_CLEARTEXT_SIGN)!
* The detached signature is returned separately in RESULT_DETACHED_SIGNATURE. * The detached signature is returned separately in RESULT_DETACHED_SIGNATURE.
* <p/> *
* required extras: * required extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key) * long EXTRA_SIGN_KEY_ID (key id of signing key)
* <p/> *
* optional extras: * optional extras:
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for detached signature) * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for detached signature)
* char[] EXTRA_PASSPHRASE (key passphrase) * char[] EXTRA_PASSPHRASE (key passphrase)
* <p/> *
* returned extras: * returned extras:
* byte[] RESULT_DETACHED_SIGNATURE * byte[] RESULT_DETACHED_SIGNATURE
* String RESULT_SIGNATURE_MICALG (contains the name of the used signature algorithm as a string)
*/ */
public static final String ACTION_DETACHED_SIGN = "org.openintents.openpgp.action.DETACHED_SIGN"; public static final String ACTION_DETACHED_SIGN = "org.openintents.openpgp.action.DETACHED_SIGN";
/** /**
* Encrypt * Encrypt
* <p/> *
* required extras: * required extras:
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
* or * or
* long[] EXTRA_KEY_IDS * long[] EXTRA_KEY_IDS
* <p/> *
* optional extras: * optional extras:
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output) * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
* char[] EXTRA_PASSPHRASE (key passphrase) * char[] EXTRA_PASSPHRASE (key passphrase)
* String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata) * String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
* boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default is true) * boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
*/ */
public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT"; public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT";
/** /**
* Sign and encrypt * Sign and encrypt
* <p/> *
* required extras: * required extras:
* String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT)
* or * or
* long[] EXTRA_KEY_IDS * long[] EXTRA_KEY_IDS
* <p/> *
* optional extras: * optional extras:
* long EXTRA_SIGN_KEY_ID (key id of signing key) * long EXTRA_SIGN_KEY_ID (key id of signing key)
* boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output) * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for output)
* char[] EXTRA_PASSPHRASE (key passphrase) * char[] EXTRA_PASSPHRASE (key passphrase)
* String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata) * String EXTRA_ORIGINAL_FILENAME (original filename to be encrypted as metadata)
* boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default is true) * boolean EXTRA_ENABLE_COMPRESSION (enable ZLIB compression, default ist true)
*/ */
public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT"; public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT";
@ -144,15 +144,15 @@ public class OpenPgpApi {
* Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted, * Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted,
* and also signed-only input. * and also signed-only input.
* OutputStream is optional, e.g., for verifying detached signatures! * OutputStream is optional, e.g., for verifying detached signatures!
* <p/> *
* If OpenPgpSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_KEY_MISSING * If OpenPgpSignatureResult.getResult() == OpenPgpSignatureResult.RESULT_KEY_MISSING
* in addition a PendingIntent is returned via RESULT_INTENT to download missing keys. * in addition a PendingIntent is returned via RESULT_INTENT to download missing keys.
* On all other status, in addition a PendingIntent is returned via RESULT_INTENT to open * On all other status, in addition a PendingIntent is returned via RESULT_INTENT to open
* the key view in OpenKeychain. * the key view in OpenKeychain.
* <p/> *
* optional extras: * optional extras:
* byte[] EXTRA_DETACHED_SIGNATURE (detached signature) * byte[] EXTRA_DETACHED_SIGNATURE (detached signature)
* <p/> *
* returned extras: * returned extras:
* OpenPgpSignatureResult RESULT_SIGNATURE * OpenPgpSignatureResult RESULT_SIGNATURE
* OpenPgpDecryptionResult RESULT_DECRYPTION * OpenPgpDecryptionResult RESULT_DECRYPTION
@ -163,9 +163,9 @@ public class OpenPgpApi {
/** /**
* Decrypts the header of an encrypted file to retrieve metadata such as original filename. * Decrypts the header of an encrypted file to retrieve metadata such as original filename.
* <p/> *
* This does not decrypt the actual content of the file. * This does not decrypt the actual content of the file.
* <p/> *
* returned extras: * returned extras:
* OpenPgpDecryptMetadata RESULT_METADATA * OpenPgpDecryptMetadata RESULT_METADATA
* String RESULT_CHARSET (charset which was specified in the headers of ascii armored input, if any) * String RESULT_CHARSET (charset which was specified in the headers of ascii armored input, if any)
@ -174,10 +174,10 @@ public class OpenPgpApi {
/** /**
* Select key id for signing * Select key id for signing
* <p/> *
* optional extras: * optional extras:
* String EXTRA_USER_ID * String EXTRA_USER_ID
* <p/> *
* returned extras: * returned extras:
* long EXTRA_SIGN_KEY_ID * long EXTRA_SIGN_KEY_ID
*/ */
@ -185,10 +185,10 @@ public class OpenPgpApi {
/** /**
* Get key ids based on given user ids (=emails) * Get key ids based on given user ids (=emails)
* <p/> *
* required extras: * required extras:
* String[] EXTRA_USER_IDS * String[] EXTRA_USER_IDS
* <p/> *
* returned extras: * returned extras:
* long[] RESULT_KEY_IDS * long[] RESULT_KEY_IDS
*/ */
@ -197,26 +197,43 @@ public class OpenPgpApi {
/** /**
* This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key * This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key
* corresponding to the given key id in its database. * corresponding to the given key id in its database.
* <p/> *
* It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key. * It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key.
* The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver. * The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver.
* <p/> *
* If an Output stream has been defined the whole public key is returned. * If an Output stream has been defined the whole public key is returned.
* required extras: * required extras:
* long EXTRA_KEY_ID * long EXTRA_KEY_ID
* <p/> *
* optional extras: * optional extras:
* String EXTRA_REQUEST_ASCII_ARMOR (request that the returned key is encoded in ASCII Armor) * String EXTRA_REQUEST_ASCII_ARMOR (request that the returned key is encoded in ASCII Armor)
*
*/ */
public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY"; public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY";
/**
* Backup all keys given by EXTRA_KEY_IDS and if requested their secret parts.
* The encrypted backup will be written to the OutputStream.
* The client app has no access to the backup code used to encrypt the backup!
* This operation always requires user interaction with RESULT_CODE_USER_INTERACTION_REQUIRED!
*
* required extras:
* long[] EXTRA_KEY_IDS (keys that should be included in the backup)
* boolean EXTRA_BACKUP_SECRET (also backup secret keys)
*/
public static final String ACTION_BACKUP = "org.openintents.openpgp.action.BACKUP";
/**
* Update the status of some Autocrypt peer, identified by their peer id.
*
* required extras:
* String EXTRA_AUTOCRYPT_PEER_ID (autocrypt peer id to update)
* AutocryptPeerUpdate EXTRA_AUTOCRYPT_PEER_UPDATE (actual peer update)
*/
public static final String ACTION_UPDATE_AUTOCRYPT_PEER = "org.openintents.openpgp.action.UPDATE_AUTOCRYPT_PEER";
/* Intent extras */ /* Intent extras */
public static final String EXTRA_API_VERSION = "api_version"; public static final String EXTRA_API_VERSION = "api_version";
// DEPRECATED!!!
public static final String EXTRA_ACCOUNT_NAME = "account_name";
// ACTION_DETACHED_SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY // ACTION_DETACHED_SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
// request ASCII Armor for output // request ASCII Armor for output
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53) // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
@ -226,23 +243,37 @@ public class OpenPgpApi {
public static final String RESULT_DETACHED_SIGNATURE = "detached_signature"; public static final String RESULT_DETACHED_SIGNATURE = "detached_signature";
public static final String RESULT_SIGNATURE_MICALG = "signature_micalg"; public static final String RESULT_SIGNATURE_MICALG = "signature_micalg";
// ENCRYPT, SIGN_AND_ENCRYPT // ENCRYPT, SIGN_AND_ENCRYPT, QUERY_AUTOCRYPT_STATUS
public static final String EXTRA_USER_IDS = "user_ids"; public static final String EXTRA_USER_IDS = "user_ids";
public static final String EXTRA_KEY_IDS = "key_ids"; public static final String EXTRA_KEY_IDS = "key_ids";
public static final String EXTRA_KEY_IDS_SELECTED = "key_ids_selected";
public static final String EXTRA_SIGN_KEY_ID = "sign_key_id"; public static final String EXTRA_SIGN_KEY_ID = "sign_key_id";
public static final String RESULT_KEYS_CONFIRMED = "keys_confirmed";
public static final String RESULT_AUTOCRYPT_STATUS = "autocrypt_status";
public static final int AUTOCRYPT_STATUS_UNAVAILABLE = 0;
public static final int AUTOCRYPT_STATUS_DISCOURAGE = 1;
public static final int AUTOCRYPT_STATUS_AVAILABLE = 2;
public static final int AUTOCRYPT_STATUS_MUTUAL = 3;
// optional extras: // optional extras:
public static final String EXTRA_PASSPHRASE = "passphrase"; public static final String EXTRA_PASSPHRASE = "passphrase";
public static final String EXTRA_ORIGINAL_FILENAME = "original_filename"; public static final String EXTRA_ORIGINAL_FILENAME = "original_filename";
public static final String EXTRA_ENABLE_COMPRESSION = "enable_compression"; public static final String EXTRA_ENABLE_COMPRESSION = "enable_compression";
public static final String EXTRA_ENCRYPT_OPPORTUNISTIC = "opportunistic"; public static final String EXTRA_OPPORTUNISTIC_ENCRYPTION = "opportunistic";
// GET_SIGN_KEY_ID // GET_SIGN_KEY_ID
public static final String EXTRA_USER_ID = "user_id"; public static final String EXTRA_USER_ID = "user_id";
// GET_KEY // GET_KEY
public static final String EXTRA_KEY_ID = "key_id"; public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_MINIMIZE = "minimize";
public static final String EXTRA_MINIMIZE_USER_ID = "minimize_user_id";
public static final String RESULT_KEY_IDS = "key_ids"; public static final String RESULT_KEY_IDS = "key_ids";
// BACKUP
public static final String EXTRA_BACKUP_SECRET = "backup_secret";
/* Service Intent returns */ /* Service Intent returns */
public static final String RESULT_CODE = "result_code"; public static final String RESULT_CODE = "result_code";
@ -258,10 +289,10 @@ public class OpenPgpApi {
public static final String RESULT_INTENT = "intent"; public static final String RESULT_INTENT = "intent";
// DECRYPT_VERIFY // DECRYPT_VERIFY
public static final String EXTRA_DECRYPTION_RESULT = "decryption_result";
public static final String EXTRA_DETACHED_SIGNATURE = "detached_signature"; public static final String EXTRA_DETACHED_SIGNATURE = "detached_signature";
public static final String EXTRA_PROGRESS_MESSENGER = "progress_messenger"; public static final String EXTRA_PROGRESS_MESSENGER = "progress_messenger";
public static final String EXTRA_DATA_LENGTH = "data_length"; public static final String EXTRA_DATA_LENGTH = "data_length";
public static final String EXTRA_DECRYPTION_RESULT = "decryption_result";
public static final String EXTRA_SENDER_ADDRESS = "sender_address"; public static final String EXTRA_SENDER_ADDRESS = "sender_address";
public static final String EXTRA_SUPPORT_OVERRIDE_CRYPTO_WARNING = "support_override_crpto_warning"; public static final String EXTRA_SUPPORT_OVERRIDE_CRYPTO_WARNING = "support_override_crpto_warning";
public static final String RESULT_SIGNATURE = "signature"; public static final String RESULT_SIGNATURE = "signature";
@ -272,7 +303,11 @@ public class OpenPgpApi {
// This will be the charset which was specified in the headers of ascii armored input, if any // This will be the charset which was specified in the headers of ascii armored input, if any
public static final String RESULT_CHARSET = "charset"; public static final String RESULT_CHARSET = "charset";
// INTERNAL, should not be used // UPDATE_AUTOCRYPT_PEER
public static final String EXTRA_AUTOCRYPT_PEER_ID = "autocrypt_peer_id";
public static final String EXTRA_AUTOCRYPT_PEER_UPDATE = "autocrypt_peer_update";
// INTERNAL, must not be used
public static final String EXTRA_CALL_UUID1 = "call_uuid1"; public static final String EXTRA_CALL_UUID1 = "call_uuid1";
public static final String EXTRA_CALL_UUID2 = "call_uuid2"; public static final String EXTRA_CALL_UUID2 = "call_uuid2";