diff options
Diffstat (limited to '')
6 files changed, 120 insertions, 60 deletions
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java index 71fbd40db..100ad1c43 100644 --- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java @@ -9,6 +9,7 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.List; import javax.crypto.BadPaddingException; @@ -42,7 +43,7 @@ public class XmppAxolotlMessage { private byte[] ciphertext = null; private byte[] authtagPlusInnerKey = null; private byte[] iv = null; - private final SparseArray<XmppAxolotlSession.AxolotlKey> keys; + private final List<XmppAxolotlSession.AxolotlKey> keys; private final Jid from; private final int sourceDeviceId; @@ -110,7 +111,7 @@ public class XmppAxolotlMessage { throw new IllegalArgumentException("invalid source id"); } List<Element> keyElements = header.getChildren(); - this.keys = new SparseArray<>(); + this.keys = new ArrayList<>(); for (Element keyElement : keyElements) { switch (keyElement.getName()) { case KEYTAG: @@ -118,7 +119,7 @@ public class XmppAxolotlMessage { Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID)); byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT); boolean isPreKey = keyElement.getAttributeAsBoolean("prekey"); - this.keys.put(recipientId, new XmppAxolotlSession.AxolotlKey(key, isPreKey)); + this.keys.add(new XmppAxolotlSession.AxolotlKey(recipientId, key, isPreKey)); } catch (NumberFormatException e) { throw new IllegalArgumentException("invalid remote id"); } @@ -143,7 +144,7 @@ public class XmppAxolotlMessage { XmppAxolotlMessage(Jid from, int sourceDeviceId) { this.from = from; this.sourceDeviceId = sourceDeviceId; - this.keys = new SparseArray<>(); + this.keys = new ArrayList<>(); this.iv = generateIv(); this.innerKey = generateKey(); } @@ -198,15 +199,15 @@ public class XmppAxolotlMessage { private static byte[] getPaddedBytes(String plaintext) { int plainLength = plaintext.getBytes().length; - int pad = Math.max(64,(plainLength / 32 + 1) * 32) - plainLength; + int pad = Math.max(64, (plainLength / 32 + 1) * 32) - plainLength; SecureRandom random = new SecureRandom(); int left = random.nextInt(pad); int right = pad - left; StringBuilder builder = new StringBuilder(plaintext); - for(int i = 0; i < left; ++i) { - builder.insert(0,random.nextBoolean() ? "\t" : " "); + for (int i = 0; i < left; ++i) { + builder.insert(0, random.nextBoolean() ? "\t" : " "); } - for(int i = 0; i < right; ++i) { + for (int i = 0; i < right; ++i) { builder.append(random.nextBoolean() ? "\t" : " "); } return builder.toString().getBytes(); @@ -232,7 +233,7 @@ public class XmppAxolotlMessage { key = session.processSending(innerKey, ignoreSessionTrust); } if (key != null) { - keys.put(session.getRemoteAddress().getDeviceId(), key); + keys.add(key); } } @@ -248,13 +249,13 @@ public class XmppAxolotlMessage { Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX); Element headerElement = encryptionElement.addChild(HEADER); headerElement.setAttribute(SOURCEID, sourceDeviceId); - for(int i = 0; i < keys.size(); ++i) { + for (XmppAxolotlSession.AxolotlKey key : keys) { Element keyElement = new Element(KEYTAG); - keyElement.setAttribute(REMOTEID, keys.keyAt(i)); - if (keys.valueAt(i).prekey) { + keyElement.setAttribute(REMOTEID, key.deviceId); + if (key.prekey) { keyElement.setAttribute("prekey", "true"); } - keyElement.setContent(Base64.encodeToString(keys.valueAt(i).key, Base64.NO_WRAP)); + keyElement.setContent(Base64.encodeToString(key.key, Base64.NO_WRAP)); headerElement.addChild(keyElement); } headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP)); @@ -266,11 +267,16 @@ public class XmppAxolotlMessage { } private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException { - XmppAxolotlSession.AxolotlKey encryptedKey = keys.get(sourceDeviceId); - if (encryptedKey == null) { + ArrayList<XmppAxolotlSession.AxolotlKey> possibleKeys = new ArrayList<>(); + for (XmppAxolotlSession.AxolotlKey key : keys) { + if (key.deviceId == sourceDeviceId) { + possibleKeys.add(key); + } + } + if (possibleKeys.size() == 0) { throw new NotEncryptedForThisDeviceException(); } - return session.processReceiving(encryptedKey); + return session.processReceiving(possibleKeys); } XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException { @@ -311,4 +317,4 @@ public class XmppAxolotlMessage { } return plaintextMessage; } -} +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java index cbfdaf28e..71e20676e 100644 --- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java +++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java @@ -2,6 +2,7 @@ package de.pixart.messenger.crypto.axolotl; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; import org.whispersystems.libsignal.DuplicateMessageException; import org.whispersystems.libsignal.IdentityKey; @@ -19,6 +20,10 @@ import org.whispersystems.libsignal.protocol.PreKeySignalMessage; import org.whispersystems.libsignal.protocol.SignalMessage; import org.whispersystems.libsignal.util.guava.Optional; +import java.util.Iterator; +import java.util.List; + +import de.pixart.messenger.Config; import de.pixart.messenger.entities.Account; import de.pixart.messenger.utils.CryptoHelper; @@ -79,35 +84,56 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> { } @Nullable - byte[] processReceiving(AxolotlKey encryptedKey) throws CryptoFailedException { - byte[] plaintext; + byte[] processReceiving(List<AxolotlKey> possibleKeys) throws CryptoFailedException { + byte[] plaintext = null; FingerprintStatus status = getTrust(); if (!status.isCompromised()) { - try { - if (encryptedKey.prekey) { - PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(encryptedKey.key); - Optional<Integer> optionalPreKeyId = preKeySignalMessage.getPreKeyId(); - IdentityKey identityKey = preKeySignalMessage.getIdentityKey(); - if (!optionalPreKeyId.isPresent()) { - throw new CryptoFailedException("PreKeyWhisperMessage did not contain a PreKeyId"); + Iterator<AxolotlKey> iterator = possibleKeys.iterator(); + while (iterator.hasNext()) { + AxolotlKey encryptedKey = iterator.next(); + try { + if (encryptedKey.prekey) { + PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(encryptedKey.key); + Optional<Integer> optionalPreKeyId = preKeySignalMessage.getPreKeyId(); + IdentityKey identityKey = preKeySignalMessage.getIdentityKey(); + if (!optionalPreKeyId.isPresent()) { + if (iterator.hasNext()) { + continue; + } + throw new CryptoFailedException("PreKeyWhisperMessage did not contain a PreKeyId"); + } + preKeyId = optionalPreKeyId.get(); + if (this.identityKey != null && !this.identityKey.equals(identityKey)) { + if (iterator.hasNext()) { + continue; + } + throw new CryptoFailedException("Received PreKeyWhisperMessage but preexisting identity key changed."); + } + this.identityKey = identityKey; + plaintext = cipher.decrypt(preKeySignalMessage); + } else { + SignalMessage signalMessage = new SignalMessage(encryptedKey.key); + try { + plaintext = cipher.decrypt(signalMessage); + } catch (InvalidMessageException | NoSessionException e) { + if (iterator.hasNext()) { + Log.w(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring crypto exception because possible keys left to try", e); + continue; + } + throw new BrokenSessionException(this.remoteAddress, e); + } + preKeyId = null; //better safe than sorry because we use that to do special after prekey handling } - preKeyId = optionalPreKeyId.get(); - if (this.identityKey != null && !this.identityKey.equals(identityKey)) { - throw new CryptoFailedException("Received PreKeyWhisperMessage but preexisting identity key changed."); + } catch (InvalidVersionException | InvalidKeyException | LegacyMessageException | InvalidMessageException | DuplicateMessageException | InvalidKeyIdException | UntrustedIdentityException e) { + if (iterator.hasNext()) { + Log.w(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring crypto exception because possible keys left to try", e); + continue; } - this.identityKey = identityKey; - plaintext = cipher.decrypt(preKeySignalMessage); - } else { - SignalMessage signalMessage = new SignalMessage(encryptedKey.key); - try { - plaintext = cipher.decrypt(signalMessage); - } catch (InvalidMessageException | NoSessionException e) { - throw new BrokenSessionException(this.remoteAddress, e); - } - preKeyId = null; //better safe than sorry because we use that to do special after prekey handling + throw new CryptoFailedException("Error decrypting SignalMessage", e); + } + if (iterator.hasNext()) { + break; } - } catch (InvalidVersionException | InvalidKeyException | LegacyMessageException | InvalidMessageException | DuplicateMessageException | InvalidKeyIdException | UntrustedIdentityException e) { - throw new CryptoFailedException("Error decrypting SignalMessage", e); } if (!status.isActive()) { setTrust(status.toActive()); @@ -125,7 +151,7 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> { if (ignoreSessionTrust || status.isTrustedAndActive()) { try { CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage); - return new AxolotlKey(ciphertextMessage.serialize(), ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE); + return new AxolotlKey(getRemoteAddress().getDeviceId(), ciphertextMessage.serialize(), ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE); } catch (UntrustedIdentityException e) { return null; } @@ -148,8 +174,10 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> { public final byte[] key; public final boolean prekey; + public final int deviceId; - public AxolotlKey(byte[] key, boolean prekey) { + public AxolotlKey(int deviceId, byte[] key, boolean prekey) { + this.deviceId = deviceId; this.key = key; this.prekey = prekey; } diff --git a/src/main/java/de/pixart/messenger/services/AvatarService.java b/src/main/java/de/pixart/messenger/services/AvatarService.java index edc0a2e67..23e2da4d6 100644 --- a/src/main/java/de/pixart/messenger/services/AvatarService.java +++ b/src/main/java/de/pixart/messenger/services/AvatarService.java @@ -10,8 +10,11 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.Nullable; +import android.support.v4.content.res.ResourcesCompat; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; @@ -131,10 +134,11 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { } private void drawIcon(Canvas canvas, Paint paint) { - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inSampleSize = 2; - Resources resources = mXmppConnectionService.getResources(); - Bitmap icon = BitmapFactory.decodeResource(resources, R.drawable.ic_launcher, opts); + final Resources resources = mXmppConnectionService.getResources(); + final Bitmap icon = getRoundLauncherIcon(resources); + if (icon == null) { + return; + } paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); int iconSize = Math.round(canvas.getHeight() / 2.6f); @@ -145,6 +149,25 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { canvas.drawBitmap(icon, null, rect, paint); } + private static Bitmap getRoundLauncherIcon(Resources resources) { + + final Drawable drawable = ResourcesCompat.getDrawable(resources, R.drawable.ic_launcher, null); + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + } + public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) { Contact c = user.getContact(); if (c != null && (c.getProfilePhoto() != null || c.getAvatarFilename() != null || user.getAvatar() == null)) { diff --git a/src/main/java/de/pixart/messenger/services/ShortcutService.java b/src/main/java/de/pixart/messenger/services/ShortcutService.java index 8051c8849..fa4853665 100644 --- a/src/main/java/de/pixart/messenger/services/ShortcutService.java +++ b/src/main/java/de/pixart/messenger/services/ShortcutService.java @@ -127,9 +127,9 @@ public class ShortcutService { } @NonNull - public Intent createShortcut(Contact contact) { + public Intent createShortcut(Contact contact, boolean legacy) { Intent intent; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !legacy) { ShortcutInfo shortcut = getShortcutInfo(contact); ShortcutManager shortcutManager = xmppConnectionService.getSystemService(ShortcutManager.class); intent = shortcutManager.createShortcutResultIntent(shortcut); @@ -141,10 +141,9 @@ public class ShortcutService { @NonNull private Intent createShortcutResultIntent(Contact contact) { - Intent intent; AvatarService avatarService = xmppConnectionService.getAvatarService(); Bitmap icon = avatarService.getRoundedShortcutWithIcon(contact); - intent = new Intent(); + Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, contact.getDisplayName()); intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, getShortcutIntent(contact)); diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index c2bc94263..4df6391da 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -1790,13 +1790,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } }; - if (account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) { - if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) { - conversation.setNextCounterpart(null); - callback.onPresenceSelected(); - } else { - activity.selectPresence(conversation, callback); - } + if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) { + conversation.setNextCounterpart(null); + callback.onPresenceSelected(); + } else { + activity.selectPresence(conversation, callback); } } diff --git a/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java b/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java index ef5c296e0..68ad0cb86 100644 --- a/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShortcutActivity.java @@ -1,5 +1,6 @@ package de.pixart.messenger.ui; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -7,7 +8,9 @@ import android.support.v7.app.ActionBar; import android.view.View; import android.view.inputmethod.InputMethodManager; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; @@ -16,6 +19,8 @@ import de.pixart.messenger.entities.ListItem; public class ShortcutActivity extends AbstractSearchableListItemActivity { + private static final List<String> BLACKLISTED_ACTIVITIES = Arrays.asList("com.teslacoilsw.launcher.ChooseActionIntentActivity"); + @Override protected void refreshUiReal() { @@ -25,15 +30,16 @@ public class ShortcutActivity extends AbstractSearchableListItemActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getListView().setOnItemClickListener((parent, view, position, id) -> { + final ComponentName callingActivity = getCallingActivity(); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); ListItem listItem = getListItems().get(position); - Intent shortcut = xmppConnectionService.getShortcutService().createShortcut(((Contact) listItem)); + final boolean legacy = BLACKLISTED_ACTIVITIES.contains(callingActivity == null ? null : callingActivity.getClassName()); + Intent shortcut = xmppConnectionService.getShortcutService().createShortcut(((Contact) listItem), legacy); setResult(RESULT_OK, shortcut); finish(); }); - binding.fab.setVisibility(View.GONE); } @Override |