diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java index c5ca3ad75..c4f58da94 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java @@ -6,10 +6,17 @@ import android.util.Log; import androidx.annotation.StringRes; +import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.io.BaseEncoding; + import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback; +import org.openintents.openpgp.util.OpenPgpUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -18,6 +25,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -29,268 +37,258 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.UiCallback; +import eu.siacs.conversations.utils.AsciiArmor; public class PgpEngine { - private final OpenPgpApi api; - private final XmppConnectionService mXmppConnectionService; + private final OpenPgpApi api; + private final XmppConnectionService mXmppConnectionService; - public PgpEngine(OpenPgpApi api, XmppConnectionService service) { - this.api = api; - this.mXmppConnectionService = service; - } + public PgpEngine(OpenPgpApi api, XmppConnectionService service) { + this.api = api; + this.mXmppConnectionService = service; + } - private static void logError(Account account, OpenPgpError error) { - if (error != null) { - error.describeContents(); - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error '" + error.getMessage() + "' code=" + error.getErrorId()+" class="+error.getClass().getName()); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error with no message"); - } - } + private static void logError(Account account, OpenPgpError error) { + if (error != null) { + error.describeContents(); + Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error '" + error.getMessage() + "' code=" + error.getErrorId() + " class=" + error.getClass().getName()); + } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error with no message"); + } + } - public void encrypt(final Message message, final UiCallback callback) { - Intent params = new Intent(); - params.setAction(OpenPgpApi.ACTION_ENCRYPT); - final Conversation conversation = (Conversation) message.getConversation(); - if (conversation.getMode() == Conversation.MODE_SINGLE) { - long[] keys = { - conversation.getContact().getPgpKeyId(), - conversation.getAccount().getPgpId() - }; - params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys); - } else { - params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds()); - } + public void encrypt(final Message message, final UiCallback callback) { + Intent params = new Intent(); + params.setAction(OpenPgpApi.ACTION_ENCRYPT); + final Conversation conversation = (Conversation) message.getConversation(); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + long[] keys = { + conversation.getContact().getPgpKeyId(), + conversation.getAccount().getPgpId() + }; + params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys); + } else { + params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, conversation.getMucOptions().getPgpKeyIds()); + } - if (!message.needsUploading()) { - params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - String body; - if (message.hasFileOnRemoteHost()) { - body = message.getFileParams().url.toString(); - } else { - body = message.getBody(); - } - InputStream is = new ByteArrayInputStream(body.getBytes()); - final OutputStream os = new ByteArrayOutputStream(); - api.executeApiAsync(params, is, os, result -> { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - try { - os.flush(); - StringBuilder encryptedMessageBody = new StringBuilder(); - String[] lines = os.toString().split("\n"); - for (int i = 2; i < lines.length - 1; ++i) { - if (!lines[i].contains("Version")) { - encryptedMessageBody.append(lines[i].trim()); - } - } - message.setEncryptedBody(encryptedMessageBody.toString()); - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - mXmppConnectionService.sendMessage(message); - callback.success(message); - } catch (IOException e) { - callback.error(R.string.openpgp_error, message); - } + if (!message.needsUploading()) { + params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + String body; + if (message.hasFileOnRemoteHost()) { + body = message.getFileParams().url.toString(); + } else { + body = message.getBody(); + } + InputStream is = new ByteArrayInputStream(body.getBytes()); + final OutputStream os = new ByteArrayOutputStream(); + api.executeApiAsync(params, is, os, result -> { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + try { + os.flush(); + final ArrayList encryptedMessageBody = new ArrayList<>(); + final String[] lines = os.toString().split("\n"); + for (int i = 2; i < lines.length - 1; ++i) { + if (!lines[i].contains("Version")) { + encryptedMessageBody.add(lines[i].trim()); + } + } + message.setEncryptedBody(Joiner.on('\n').join(encryptedMessageBody)); + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + mXmppConnectionService.sendMessage(message); + callback.success(message); + } catch (IOException e) { + callback.error(R.string.openpgp_error, message); + } - break; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message); - break; - case OpenPgpApi.RESULT_CODE_ERROR: - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - String errorMessage = error != null ? error.getMessage() : null; - @StringRes final int res; - if (errorMessage != null && errorMessage.startsWith("Bad key for encryption")) { - res = R.string.bad_key_for_encryption; - } else { - res = R.string.openpgp_error; - } - logError(conversation.getAccount(), error); - callback.error(res, message); - break; - } - }); - } else { - try { - DownloadableFile inputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, true); - DownloadableFile outputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, false); - outputFile.getParentFile().mkdirs(); - outputFile.createNewFile(); - final InputStream is = new FileInputStream(inputFile); - final OutputStream os = new FileOutputStream(outputFile); - api.executeApiAsync(params, is, os, result -> { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - try { - os.flush(); - } catch (IOException ignored) { - //ignored - } - FileBackend.close(os); - mXmppConnectionService.sendMessage(message); - callback.success(message); - break; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message); - break; - case OpenPgpApi.RESULT_CODE_ERROR: - logError(conversation.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); - callback.error(R.string.openpgp_error, message); - break; - } - }); - } catch (final IOException e) { - callback.error(R.string.openpgp_error, message); - } - } - } + break; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message); + break; + case OpenPgpApi.RESULT_CODE_ERROR: + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + String errorMessage = error != null ? error.getMessage() : null; + @StringRes final int res; + if (errorMessage != null && errorMessage.startsWith("Bad key for encryption")) { + res = R.string.bad_key_for_encryption; + } else { + res = R.string.openpgp_error; + } + logError(conversation.getAccount(), error); + callback.error(res, message); + break; + } + }); + } else { + try { + DownloadableFile inputFile = this.mXmppConnectionService + .getFileBackend().getFile(message, true); + DownloadableFile outputFile = this.mXmppConnectionService + .getFileBackend().getFile(message, false); + outputFile.getParentFile().mkdirs(); + outputFile.createNewFile(); + final InputStream is = new FileInputStream(inputFile); + final OutputStream os = new FileOutputStream(outputFile); + api.executeApiAsync(params, is, os, result -> { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + try { + os.flush(); + } catch (IOException ignored) { + //ignored + } + FileBackend.close(os); + mXmppConnectionService.sendMessage(message); + callback.success(message); + break; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message); + break; + case OpenPgpApi.RESULT_CODE_ERROR: + logError(conversation.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error, message); + break; + } + }); + } catch (final IOException e) { + callback.error(R.string.openpgp_error, message); + } + } + } - public long fetchKeyId(Account account, String status, String signature) { - if ((signature == null) || (api == null)) { - return 0; - } - if (status == null) { - status = ""; - } - final StringBuilder pgpSig = new StringBuilder(); - pgpSig.append("-----BEGIN PGP SIGNED MESSAGE-----"); - pgpSig.append('\n'); - pgpSig.append('\n'); - pgpSig.append(status); - pgpSig.append('\n'); - pgpSig.append("-----BEGIN PGP SIGNATURE-----"); - pgpSig.append('\n'); - pgpSig.append('\n'); - pgpSig.append(signature.replace("\n", "").trim()); - pgpSig.append('\n'); - pgpSig.append("-----END PGP SIGNATURE-----"); - Intent params = new Intent(); - params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); - params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - InputStream is = new ByteArrayInputStream(pgpSig.toString().getBytes()); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - Intent result = api.executeApi(params, is, os); - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, - OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - OpenPgpSignatureResult sigResult = result - .getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); - if (sigResult != null) { - return sigResult.getKeyId(); - } else { - return 0; - } - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - return 0; - case OpenPgpApi.RESULT_CODE_ERROR: - logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); - return 0; - } - return 0; - } + public long fetchKeyId(final Account account, final String status, final String signature) { + if (signature == null || api == null) { + return 0; + } + final Intent params = new Intent(); + params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); + try { + params.putExtra(OpenPgpApi.RESULT_DETACHED_SIGNATURE, AsciiArmor.decode(signature)); + } catch (final IllegalArgumentException e) { + Log.d(Config.LOGTAG, "unable to parse signature", e); + return 0; + } + final InputStream is = new ByteArrayInputStream(Strings.nullToEmpty(status).getBytes()); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final Intent result = api.executeApi(params, is, os); + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, + OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + final OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE); + //TODO unsure that sigResult.getResult() is either 1, 2 or 3 + if (sigResult != null) { + return sigResult.getKeyId(); + } else { + return 0; + } + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + return 0; + case OpenPgpApi.RESULT_CODE_ERROR: + logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + return 0; + } + return 0; + } - public void chooseKey(final Account account, final UiCallback callback) { - Intent p = new Intent(); - p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID); - api.executeApiAsync(p, null, null, result -> { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - callback.success(account); - return; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), account); - return; - case OpenPgpApi.RESULT_CODE_ERROR: - logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); - callback.error(R.string.openpgp_error, account); - } - }); - } + public void chooseKey(final Account account, final UiCallback callback) { + Intent p = new Intent(); + p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID); + api.executeApiAsync(p, null, null, result -> { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + callback.success(account); + return; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), account); + return; + case OpenPgpApi.RESULT_CODE_ERROR: + logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error, account); + } + }); + } - public void generateSignature(Intent intent, final Account account, String status, final UiCallback callback) { - if (account.getPgpId() == 0) { - return; - } - Intent params = intent == null ? new Intent() : intent; - params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN); - params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId()); - InputStream is = new ByteArrayInputStream(status.getBytes()); - final OutputStream os = new ByteArrayOutputStream(); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": signing status message \"" + status + "\""); - api.executeApiAsync(params, is, os, result -> { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - StringBuilder signatureBuilder = new StringBuilder(); - try { - os.flush(); - String[] lines = os.toString().split("\n"); - boolean sig = false; - for (String line : lines) { - if (sig) { - if (line.contains("END PGP SIGNATURE")) { - sig = false; - } else { - if (!line.contains("Version")) { - signatureBuilder.append(line.trim()); - } - } - } - if (line.contains("BEGIN PGP SIGNATURE")) { - sig = true; - } - } - } catch (IOException e) { - callback.error(R.string.openpgp_error, null); - return; - } - callback.success(signatureBuilder.toString()); - return; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), status); - return; - case OpenPgpApi.RESULT_CODE_ERROR: - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - if (error != null && "signing subkey not found!".equals(error.getMessage())) { - callback.error(0, null); - } else { - logError(account, error); - callback.error(R.string.unable_to_connect_to_keychain, null); - } - } - }); - } + public void generateSignature(Intent intent, final Account account, String status, final UiCallback callback) { + if (account.getPgpId() == 0) { + return; + } + Intent params = intent == null ? new Intent() : intent; + params.setAction(OpenPgpApi.ACTION_CLEARTEXT_SIGN); + params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId()); + InputStream is = new ByteArrayInputStream(status.getBytes()); + final OutputStream os = new ByteArrayOutputStream(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": signing status message \"" + status + "\""); + api.executeApiAsync(params, is, os, result -> { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + final ArrayList signature = new ArrayList<>(); + try { + os.flush(); + boolean sig = false; + for (final String line : Splitter.on('\n').split(os.toString())) { + if (sig) { + if (line.contains("END PGP SIGNATURE")) { + sig = false; + } else { + if (!line.contains("Version")) { + signature.add(line.trim()); + } + } + } + if (line.contains("BEGIN PGP SIGNATURE")) { + sig = true; + } + } + } catch (IOException e) { + callback.error(R.string.openpgp_error, null); + return; + } + callback.success(Joiner.on('\n').join(signature)); + return; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), status); + return; + case OpenPgpApi.RESULT_CODE_ERROR: + OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); + if (error != null && "signing subkey not found!".equals(error.getMessage())) { + callback.error(0, null); + } else { + logError(account, error); + callback.error(R.string.unable_to_connect_to_keychain, null); + } + } + }); + } - public void hasKey(final Contact contact, final UiCallback callback) { - Intent params = new Intent(); - params.setAction(OpenPgpApi.ACTION_GET_KEY); - params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); - api.executeApiAsync(params, null, null, new IOpenPgpCallback() { + public void hasKey(final Contact contact, final UiCallback callback) { + Intent params = new Intent(); + params.setAction(OpenPgpApi.ACTION_GET_KEY); + params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); + api.executeApiAsync(params, null, null, new IOpenPgpCallback() { - @Override - public void onReturn(Intent result) { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - callback.success(contact); - return; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), contact); - return; - case OpenPgpApi.RESULT_CODE_ERROR: - logError(contact.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); - callback.error(R.string.openpgp_error, contact); - } - } - }); - } + @Override + public void onReturn(Intent result) { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + callback.success(contact); + return; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), contact); + return; + case OpenPgpApi.RESULT_CODE_ERROR: + logError(contact.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error, contact); + } + } + }); + } - public PendingIntent getIntentForKey(long pgpKeyId) { - Intent params = new Intent(); - params.setAction(OpenPgpApi.ACTION_GET_KEY); - params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId); - Intent result = api.executeApi(params, null, null); - return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); - } + public PendingIntent getIntentForKey(long pgpKeyId) { + Intent params = new Intent(); + params.setAction(OpenPgpApi.ACTION_GET_KEY); + params.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyId); + Intent result = api.executeApi(params, null, null); + return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + } } diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index d0ec7f6bc..723a3fee8 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -2,6 +2,8 @@ package eu.siacs.conversations.parser; import android.util.Log; +import org.openintents.openpgp.util.OpenPgpUtils; + import java.util.ArrayList; import java.util.List; @@ -17,6 +19,7 @@ import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; @@ -313,9 +316,10 @@ public class PresenceParser extends AbstractParser implements PgpEngine pgp = mXmppConnectionService.getPgpEngine(); Element x = packet.findChild("x", "jabber:x:signed"); if (pgp != null && x != null) { - Element status = packet.findChild("status"); - String msg = status != null ? status.getContent() : ""; - if (contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent()))) { + final String status = packet.findChildContent("status"); + final long keyId = pgp.fetchKeyId(account, status, x.getContent()); + if (keyId != 0 && contact.setPgpKeyId(keyId)) { + Log.d(Config.LOGTAG,account.getJid().asBareJid()+": found OpenPGP key id for "+contact.getJid()+" "+OpenPgpUtils.convertKeyIdToHex(keyId)); mXmppConnectionService.syncRoster(account); } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index ff193f0c6..af57d7cc9 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -52,6 +52,8 @@ import androidx.appcompat.app.AlertDialog.Builder; import androidx.appcompat.app.AppCompatDelegate; import androidx.databinding.DataBindingUtil; +import com.google.common.base.Strings; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -573,13 +575,7 @@ public abstract class XmppActivity extends ActionBarActivity { if (account.getPgpId() == 0) { choosePgpSignId(account); } else { - String status = null; - if (manuallyChangePresence()) { - status = account.getPresenceStatusMessage(); - } - if (status == null) { - status = ""; - } + final String status = Strings.nullToEmpty(account.getPresenceStatusMessage()); xmppConnectionService.getPgpEngine().generateSignature(intent, account, status, new UiCallback() { @Override diff --git a/src/main/java/eu/siacs/conversations/utils/AsciiArmor.java b/src/main/java/eu/siacs/conversations/utils/AsciiArmor.java new file mode 100644 index 000000000..beb224169 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/utils/AsciiArmor.java @@ -0,0 +1,29 @@ +package eu.siacs.conversations.utils; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.io.BaseEncoding; + +import java.util.List; + +public class AsciiArmor { + + public static byte[] decode(final String input) { + final List lines = Splitter.on('\n').splitToList(Strings.nullToEmpty(input).trim()); + if (lines.size() == 1) { + final String line = lines.get(0); + final String cleaned = line.substring(0, line.lastIndexOf("=")); + return BaseEncoding.base64().decode(cleaned); + } + final String withoutChecksum; + if (Iterables.getLast(lines).charAt(0) == '=') { + withoutChecksum = Joiner.on("").join(lines.subList(0, lines.size() - 1)); + } else { + withoutChecksum = Joiner.on("").join(lines); + } + return BaseEncoding.base64().decode(withoutChecksum); + } + +}