aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Schneppe <christian.schneppe@pix-art.de>2020-01-24 20:36:29 +0100
committerChristian Schneppe <christian.schneppe@pix-art.de>2020-01-24 20:36:29 +0100
commitb9962f7b50a03a9d40ecb426be1bd95ce4b7b9bd (patch)
tree2714b0ad523162ab0cb39ae753743d9847e1c143
parenta917c5403f89c8c6351bc379fc91e981139dd25e (diff)
omemo changes: use 12 byte IV, no longer accept auth tag appended to payload
-rw-r--r--src/main/java/de/pixart/messenger/Config.java3
-rw-r--r--src/main/java/de/pixart/messenger/crypto/OtrService.java2
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java390
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/OutdatedSenderException.java8
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java209
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java4
-rw-r--r--src/main/java/de/pixart/messenger/entities/MucOptions.java4
-rw-r--r--src/main/java/de/pixart/messenger/parser/MessageParser.java3
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java6
10 files changed, 306 insertions, 327 deletions
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java
index de423634a..daf25028a 100644
--- a/src/main/java/de/pixart/messenger/Config.java
+++ b/src/main/java/de/pixart/messenger/Config.java
@@ -110,6 +110,7 @@ public final class Config {
public static final boolean REMOVE_BROKEN_DEVICES = false;
public static final boolean OMEMO_PADDING = false;
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
+ public static final boolean TWELVE_BYTE_IV = true;
public static final int MAX_DISPLAY_MESSAGE_CHARS = 4096;
public static final int MAX_STORAGE_MESSAGE_CHARS = 2 * 1024 * 1024; //2MB
@@ -140,7 +141,7 @@ public final class Config {
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY * 30;
public static final int MAM_MAX_MESSAGES = 750;
- public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE;
+ public static final ChatState DEFAULT_CHAT_STATE = ChatState.ACTIVE;
public static final int TYPING_TIMEOUT = 5;
public static final int EXPIRY_INTERVAL = 30 * 60 * 1000; // 30 minutes
diff --git a/src/main/java/de/pixart/messenger/crypto/OtrService.java b/src/main/java/de/pixart/messenger/crypto/OtrService.java
index cf41ec26f..b6e6507f5 100644
--- a/src/main/java/de/pixart/messenger/crypto/OtrService.java
+++ b/src/main/java/de/pixart/messenger/crypto/OtrService.java
@@ -188,7 +188,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost {
try {
Jid jid = OtrJidHelper.fromSessionID(session);
Conversation conversation = mXmppConnectionService.find(account, jid);
- if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
if (mXmppConnectionService.sendChatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
index 186629853..3bc0c2901 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -80,16 +80,37 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
private final Map<Jid, Boolean> fetchDeviceListStatus = new HashMap<>();
private final HashMap<Jid, List<OnDeviceIdsFetched>> fetchDeviceIdsMap = new HashMap<>();
private final SerialSingleThreadExecutor executor;
+ private final Set<SignalProtocolAddress> healingAttempts = new HashSet<>();
+ private final HashSet<Integer> cleanedOwnDeviceIds = new HashSet<>();
+ private final Set<Integer> PREVIOUSLY_REMOVED_FROM_ANNOUNCEMENT = new HashSet<>();
private int numPublishTriesOnEmptyPep = 0;
private boolean pepBroken = false;
- private final Set<SignalProtocolAddress> healingAttempts = new HashSet<>();
private int lastDeviceListNotificationHash = 0;
- private final HashSet<Integer> cleanedOwnDeviceIds = new HashSet<>();
private Set<XmppAxolotlSession> postponedSessions = new HashSet<>(); //sessions stored here will receive after mam catchup treatment
private Set<SignalProtocolAddress> postponedHealing = new HashSet<>(); //addresses stored here will need a healing notification after mam catchup
-
private AtomicBoolean changeAccessMode = new AtomicBoolean(false);
+ public AxolotlService(Account account, XmppConnectionService connectionService) {
+ if (account == null || connectionService == null) {
+ throw new IllegalArgumentException("account and service cannot be null");
+ }
+ if (Security.getProvider("BC") == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ this.mXmppConnectionService = connectionService;
+ this.account = account;
+ this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
+ this.deviceIds = new HashMap<>();
+ this.messageCache = new HashMap<>();
+ this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
+ this.fetchStatusMap = new FetchStatusMap();
+ this.executor = new SerialSingleThreadExecutor("Axolotl");
+ }
+
+ public static String getLogprefix(Account account) {
+ return LOGPREFIX + " (" + account.getJid().asBareJid().toString() + "): ";
+ }
+
@Override
public void onAdvancedStreamFeaturesAvailable(Account account) {
if (Config.supportOmemo()
@@ -146,172 +167,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return false;
}
- private static class AxolotlAddressMap<T> {
- protected Map<String, Map<Integer, T>> map;
- protected final Object MAP_LOCK = new Object();
-
- public AxolotlAddressMap() {
- this.map = new HashMap<>();
- }
-
- public void put(SignalProtocolAddress address, T value) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- if (devices == null) {
- devices = new HashMap<>();
- map.put(address.getName(), devices);
- }
- devices.put(address.getDeviceId(), value);
- }
- }
-
- public T get(SignalProtocolAddress address) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- if (devices == null) {
- return null;
- }
- return devices.get(address.getDeviceId());
- }
- }
-
- public Map<Integer, T> getAll(String name) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(name);
- if (devices == null) {
- return new HashMap<>();
- }
- return devices;
- }
- }
-
- public boolean hasAny(SignalProtocolAddress address) {
- synchronized (MAP_LOCK) {
- Map<Integer, T> devices = map.get(address.getName());
- return devices != null && !devices.isEmpty();
- }
- }
-
- public void clear() {
- map.clear();
- }
-
- }
-
- private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
- private final XmppConnectionService xmppConnectionService;
- private final Account account;
-
- public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) {
- super();
- this.xmppConnectionService = service;
- this.account = account;
- this.fillMap(store);
- }
-
- public Set<Jid> findCounterpartsForSourceId(Integer sid) {
- Set<Jid> candidates = new HashSet<>();
- synchronized (MAP_LOCK) {
- for (Map.Entry<String, Map<Integer, XmppAxolotlSession>> entry : map.entrySet()) {
- String key = entry.getKey();
- if (entry.getValue().containsKey(sid)) {
- candidates.add(Jid.of(key));
- }
- }
- }
- return candidates;
- }
-
- private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) {
- for (Integer deviceId : deviceIds) {
- SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(bareJid, deviceId);
- IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey();
- if (Config.X509_VERIFICATION) {
- X509Certificate certificate = store.getFingerprintCertificate(CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize()));
- if (certificate != null) {
- Bundle information = CryptoHelper.extractCertificateInformation(certificate);
- try {
- final String cn = information.getString("subject_cn");
- final Jid jid = Jid.of(bareJid);
- Log.d(Config.LOGTAG, "setting common name for " + jid + " to " + cn);
- account.getRoster().getContact(jid).setCommonName(cn);
- } catch (final IllegalArgumentException ignored) {
- //ignored
- }
- }
- }
- this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey));
- }
- }
-
- private void fillMap(SQLiteAxolotlStore store) {
- List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().asBareJid().toString());
- putDevicesForJid(account.getJid().asBareJid().toString(), deviceIds, store);
- for (String address : store.getKnownAddresses()) {
- deviceIds = store.getSubDeviceSessions(address);
- putDevicesForJid(address, deviceIds, store);
- }
- }
-
- @Override
- public void put(SignalProtocolAddress address, XmppAxolotlSession value) {
- super.put(address, value);
- value.setNotFresh();
- }
-
- public void put(XmppAxolotlSession session) {
- this.put(session.getRemoteAddress(), session);
- }
- }
-
- public enum FetchStatus {
- PENDING,
- SUCCESS,
- SUCCESS_VERIFIED,
- TIMEOUT,
- SUCCESS_TRUSTED,
- ERROR
- }
-
- private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
-
- public void clearErrorFor(Jid jid) {
- synchronized (MAP_LOCK) {
- Map<Integer, FetchStatus> devices = this.map.get(jid.asBareJid().toString());
- if (devices == null) {
- return;
- }
- for (Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) {
- if (entry.getValue() == FetchStatus.ERROR) {
- Log.d(Config.LOGTAG, "resetting error for " + jid.asBareJid() + "(" + entry.getKey() + ")");
- entry.setValue(FetchStatus.TIMEOUT);
- }
- }
- }
- }
- }
-
- public static String getLogprefix(Account account) {
- return LOGPREFIX + " (" + account.getJid().asBareJid().toString() + "): ";
- }
-
- public AxolotlService(Account account, XmppConnectionService connectionService) {
- if (account == null || connectionService == null) {
- throw new IllegalArgumentException("account and service cannot be null");
- }
- if (Security.getProvider("BC") == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- this.mXmppConnectionService = connectionService;
- this.account = account;
- this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
- this.deviceIds = new HashMap<>();
- this.messageCache = new HashMap<>();
- this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account);
- this.fetchStatusMap = new FetchStatusMap();
- this.executor = new SerialSingleThreadExecutor("Axolotl");
- }
-
public String getOwnFingerprint() {
return CryptoHelper.bytesToHex(axolotlStore.getIdentityKeyPair().getPublicKey().serialize());
}
@@ -360,7 +215,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return s;
}
-
public Collection<XmppAxolotlSession> findSessionsForContact(Contact contact) {
SignalProtocolAddress contactAddress = getAddressForJid(contact.getJid());
ArrayList<XmppAxolotlSession> s = new ArrayList<>(this.sessions.getAll(contactAddress.getName()).values());
@@ -925,8 +779,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
- private final Set<Integer> PREVIOUSLY_REMOVED_FROM_ANNOUNCEMENT = new HashSet<>();
-
private void finishBuildingSessionsFromPEP(final SignalProtocolAddress address) {
SignalProtocolAddress ownAddress = new SignalProtocolAddress(account.getJid().asBareJid().toString(), 0);
Map<Integer, FetchStatus> own = fetchStatusMap.getAll(ownAddress.getName());
@@ -964,14 +816,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return !hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty());
}
- public interface OnDeviceIdsFetched {
- void fetched(Jid jid, Set<Integer> deviceIds);
- }
-
- public interface OnMultipleDeviceIdFetched {
- void fetched();
- }
-
public void fetchDeviceIds(final Jid jid) {
fetchDeviceIds(jid, null);
}
@@ -1048,12 +892,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
- interface OnSessionBuildFromPep {
- void onSessionBuildSuccessful();
-
- void onSessionBuildFailed();
- }
-
private void buildSessionFromPEP(final SignalProtocolAddress address) {
buildSessionFromPEP(address, null);
}
@@ -1124,18 +962,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
callback.onSessionBuildSuccessful();
}
}
- } catch (UntrustedIdentityException e) {
- Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
- + e.getClass().getName() + ", " + e.getMessage());
- fetchStatusMap.put(address, FetchStatus.ERROR);
- finishBuildingSessionsFromPEP(address);
- if (oneOfOurs && cleanedOwnDeviceIds.add(address.getDeviceId())) {
- removeFromDeviceAnnouncement(address.getDeviceId());
- }
- if (callback != null) {
- callback.onSessionBuildFailed();
- }
- } catch (InvalidKeyException e) {
+ } catch (UntrustedIdentityException | InvalidKeyException e) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
+ e.getClass().getName() + ", " + e.getMessage());
fetchStatusMap.put(address, FetchStatus.ERROR);
@@ -1412,7 +1239,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
private XmppAxolotlSession getReceivingSession(SignalProtocolAddress senderAddress) {
XmppAxolotlSession session = sessions.get(senderAddress);
if (session == null) {
- //Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message);
session = recreateUncachedSession(senderAddress);
if (session == null) {
session = new XmppAxolotlSession(account, axolotlStore, senderAddress);
@@ -1421,7 +1247,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return session;
}
- public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message, boolean postponePreKeyMessageHandling) throws NotEncryptedForThisDeviceException, BrokenSessionException {
+ public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message, boolean postponePreKeyMessageHandling) throws NotEncryptedForThisDeviceException, BrokenSessionException, OutdatedSenderException {
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
XmppAxolotlSession session = getReceivingSession(message);
@@ -1440,6 +1266,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
} catch (final BrokenSessionException e) {
throw e;
+ } catch (final OutdatedSenderException e) {
+ Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage());
+ throw e;
} catch (CryptoFailedException e) {
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message from " + message.getFrom(), e);
}
@@ -1545,7 +1374,6 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
-
public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message, final boolean postponePreKeyMessageHandling) {
final XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage;
final XmppAxolotlSession session = getReceivingSession(message);
@@ -1577,4 +1405,164 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
}
+
+ public enum FetchStatus {
+ PENDING,
+ SUCCESS,
+ SUCCESS_VERIFIED,
+ TIMEOUT,
+ SUCCESS_TRUSTED,
+ ERROR
+ }
+
+ public interface OnDeviceIdsFetched {
+ void fetched(Jid jid, Set<Integer> deviceIds);
+ }
+
+
+ public interface OnMultipleDeviceIdFetched {
+ void fetched();
+ }
+
+ interface OnSessionBuildFromPep {
+ void onSessionBuildSuccessful();
+
+ void onSessionBuildFailed();
+ }
+
+ private static class AxolotlAddressMap<T> {
+ protected final Object MAP_LOCK = new Object();
+ protected Map<String, Map<Integer, T>> map;
+
+ public AxolotlAddressMap() {
+ this.map = new HashMap<>();
+ }
+
+ public void put(SignalProtocolAddress address, T value) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ if (devices == null) {
+ devices = new HashMap<>();
+ map.put(address.getName(), devices);
+ }
+ devices.put(address.getDeviceId(), value);
+ }
+ }
+
+ public T get(SignalProtocolAddress address) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ if (devices == null) {
+ return null;
+ }
+ return devices.get(address.getDeviceId());
+ }
+ }
+
+ public Map<Integer, T> getAll(String name) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(name);
+ if (devices == null) {
+ return new HashMap<>();
+ }
+ return devices;
+ }
+ }
+
+ public boolean hasAny(SignalProtocolAddress address) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, T> devices = map.get(address.getName());
+ return devices != null && !devices.isEmpty();
+ }
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ }
+
+ private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
+ private final XmppConnectionService xmppConnectionService;
+ private final Account account;
+
+ public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) {
+ super();
+ this.xmppConnectionService = service;
+ this.account = account;
+ this.fillMap(store);
+ }
+
+ public Set<Jid> findCounterpartsForSourceId(Integer sid) {
+ Set<Jid> candidates = new HashSet<>();
+ synchronized (MAP_LOCK) {
+ for (Map.Entry<String, Map<Integer, XmppAxolotlSession>> entry : map.entrySet()) {
+ String key = entry.getKey();
+ if (entry.getValue().containsKey(sid)) {
+ candidates.add(Jid.of(key));
+ }
+ }
+ }
+ return candidates;
+ }
+
+ private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) {
+ for (Integer deviceId : deviceIds) {
+ SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(bareJid, deviceId);
+ IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey();
+ if (Config.X509_VERIFICATION) {
+ X509Certificate certificate = store.getFingerprintCertificate(CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize()));
+ if (certificate != null) {
+ Bundle information = CryptoHelper.extractCertificateInformation(certificate);
+ try {
+ final String cn = information.getString("subject_cn");
+ final Jid jid = Jid.of(bareJid);
+ Log.d(Config.LOGTAG, "setting common name for " + jid + " to " + cn);
+ account.getRoster().getContact(jid).setCommonName(cn);
+ } catch (final IllegalArgumentException ignored) {
+ //ignored
+ }
+ }
+ }
+ this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey));
+ }
+ }
+
+ private void fillMap(SQLiteAxolotlStore store) {
+ List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().asBareJid().toString());
+ putDevicesForJid(account.getJid().asBareJid().toString(), deviceIds, store);
+ for (String address : store.getKnownAddresses()) {
+ deviceIds = store.getSubDeviceSessions(address);
+ putDevicesForJid(address, deviceIds, store);
+ }
+ }
+
+ @Override
+ public void put(SignalProtocolAddress address, XmppAxolotlSession value) {
+ super.put(address, value);
+ value.setNotFresh();
+ }
+
+ public void put(XmppAxolotlSession session) {
+ this.put(session.getRemoteAddress(), session);
+ }
+ }
+
+ private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> {
+
+ public void clearErrorFor(Jid jid) {
+ synchronized (MAP_LOCK) {
+ Map<Integer, FetchStatus> devices = this.map.get(jid.asBareJid().toString());
+ if (devices == null) {
+ return;
+ }
+ for (Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) {
+ if (entry.getValue() == FetchStatus.ERROR) {
+ Log.d(Config.LOGTAG, "resetting error for " + jid.asBareJid() + "(" + entry.getKey() + ")");
+ entry.setValue(FetchStatus.TIMEOUT);
+ }
+ }
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/OutdatedSenderException.java b/src/main/java/de/pixart/messenger/crypto/axolotl/OutdatedSenderException.java
new file mode 100644
index 000000000..905a11b84
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/OutdatedSenderException.java
@@ -0,0 +1,8 @@
+package de.pixart.messenger.crypto.axolotl;
+
+public class OutdatedSenderException extends CryptoFailedException {
+
+ public OutdatedSenderException(final String msg) {
+ super(msg);
+ }
+} \ No newline at end of file
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 cfa6a275e..6fbf8c3dc 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -37,69 +37,13 @@ public class XmppAxolotlMessage {
private static final String KEYTYPE = "AES";
private static final String CIPHERMODE = "AES/GCM/NoPadding";
private static final String PROVIDER = "BC";
-
+ private final List<XmppAxolotlSession.AxolotlKey> keys;
+ private final Jid from;
+ private final int sourceDeviceId;
private byte[] innerKey;
private byte[] ciphertext = null;
private byte[] authtagPlusInnerKey = null;
private byte[] iv = null;
- private final List<XmppAxolotlSession.AxolotlKey> keys;
- private final Jid from;
- private final int sourceDeviceId;
-
- public static class XmppAxolotlPlaintextMessage {
- private final String plaintext;
- private final String fingerprint;
-
- XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
- this.plaintext = plaintext;
- this.fingerprint = fingerprint;
- }
-
- public String getPlaintext() {
- return plaintext;
- }
-
-
- public String getFingerprint() {
- return fingerprint;
- }
- }
-
- public static class XmppAxolotlKeyTransportMessage {
- private final String fingerprint;
- private final byte[] key;
- private final byte[] iv;
-
- XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
- this.fingerprint = fingerprint;
- this.key = key;
- this.iv = iv;
- }
-
- public String getFingerprint() {
- return fingerprint;
- }
-
- public byte[] getKey() {
- return key;
- }
-
- public byte[] getIv() {
- return iv;
- }
- }
-
- public static int parseSourceId(final Element axolotlMessage) throws IllegalArgumentException {
- final Element header = axolotlMessage.findChild(HEADER);
- if (header == null) {
- throw new IllegalArgumentException("No header found");
- }
- try {
- return Integer.parseInt(header.getAttribute(SOURCEID));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid source id");
- }
- }
private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
this.from = from;
@@ -148,6 +92,18 @@ public class XmppAxolotlMessage {
this.innerKey = generateKey();
}
+ public static int parseSourceId(final Element axolotlMessage) throws IllegalArgumentException {
+ final Element header = axolotlMessage.findChild(HEADER);
+ if (header == null) {
+ throw new IllegalArgumentException("No header found");
+ }
+ try {
+ return Integer.parseInt(header.getAttribute(SOURCEID));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid source id");
+ }
+ }
+
public static XmppAxolotlMessage fromElement(Element element, Jid from) {
return new XmppAxolotlMessage(element, from);
}
@@ -164,12 +120,28 @@ public class XmppAxolotlMessage {
}
private static byte[] generateIv() {
- SecureRandom random = new SecureRandom();
- byte[] iv = new byte[16];
+ final SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[Config.TWELVE_BYTE_IV ? 12 : 16];
random.nextBytes(iv);
return iv;
}
+ private static byte[] getPaddedBytes(String plaintext) {
+ int plainLength = plaintext.getBytes().length;
+ 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 < right; ++i) {
+ builder.append(random.nextBoolean() ? "\t" : " ");
+ }
+ return builder.toString().getBytes();
+ }
+
public boolean hasPayload() {
return ciphertext != null;
}
@@ -189,39 +161,13 @@ public class XmppAxolotlMessage {
System.arraycopy(this.innerKey, 0, authtagPlusInnerKey, 0, this.innerKey.length);
this.ciphertext = ciphertext;
}
- } catch (NoSuchAlgorithmException e) {
- throw new CryptoFailedException(e);
- } catch (NoSuchPaddingException e) {
- throw new CryptoFailedException(e);
- } catch (InvalidKeyException e) {
- throw new CryptoFailedException(e);
- } catch (IllegalBlockSizeException e) {
- throw new CryptoFailedException(e);
- } catch (BadPaddingException e) {
- throw new CryptoFailedException(e);
- } catch (NoSuchProviderException e) {
- throw new CryptoFailedException(e);
- } catch (InvalidAlgorithmParameterException e) {
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
throw new CryptoFailedException(e);
}
}
- private static byte[] getPaddedBytes(String plaintext) {
- int plainLength = plaintext.getBytes().length;
- 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 < right; ++i) {
- builder.append(random.nextBoolean() ? "\t" : " ");
- }
- return builder.toString().getBytes();
- }
-
public Jid getFrom() {
return this.from;
}
@@ -297,19 +243,19 @@ public class XmppAxolotlMessage {
byte[] key = unpackKey(session, sourceDeviceId);
if (key != null) {
try {
- if (key.length >= 32) {
- int authtaglength = key.length - 16;
- Log.d(Config.LOGTAG, "found auth tag as part of omemo key");
- byte[] newCipherText = new byte[key.length - 16 + ciphertext.length];
- byte[] newKey = new byte[16];
- System.arraycopy(ciphertext, 0, newCipherText, 0, ciphertext.length);
- System.arraycopy(key, 16, newCipherText, ciphertext.length, authtaglength);
- System.arraycopy(key, 0, newKey, 0, newKey.length);
- ciphertext = newCipherText;
- key = newKey;
+ if (key.length < 32) {
+ throw new OutdatedSenderException("Key did not contain auth tag. Sender needs to update their OMEMO client");
}
-
- Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ final int authTagLength = key.length - 16;
+ byte[] newCipherText = new byte[key.length - 16 + ciphertext.length];
+ byte[] newKey = new byte[16];
+ System.arraycopy(ciphertext, 0, newCipherText, 0, ciphertext.length);
+ System.arraycopy(key, 16, newCipherText, ciphertext.length, authTagLength);
+ System.arraycopy(key, 0, newKey, 0, newKey.length);
+ ciphertext = newCipherText;
+ key = newKey;
+
+ final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
@@ -318,22 +264,55 @@ public class XmppAxolotlMessage {
String plaintext = new String(cipher.doFinal(ciphertext));
plaintextMessage = new XmppAxolotlPlaintextMessage(Config.OMEMO_PADDING ? plaintext.trim() : plaintext, session.getFingerprint());
- } catch (NoSuchAlgorithmException e) {
- throw new CryptoFailedException(e);
- } catch (NoSuchPaddingException e) {
- throw new CryptoFailedException(e);
- } catch (InvalidKeyException e) {
- throw new CryptoFailedException(e);
- } catch (InvalidAlgorithmParameterException e) {
- throw new CryptoFailedException(e);
- } catch (IllegalBlockSizeException e) {
- throw new CryptoFailedException(e);
- } catch (BadPaddingException e) {
- throw new CryptoFailedException(e);
- } catch (NoSuchProviderException e) {
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+ | InvalidAlgorithmParameterException | IllegalBlockSizeException
+ | BadPaddingException | NoSuchProviderException e) {
throw new CryptoFailedException(e);
}
}
return plaintextMessage;
}
+
+ public static class XmppAxolotlPlaintextMessage {
+ private final String plaintext;
+ private final String fingerprint;
+
+ XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
+ this.plaintext = plaintext;
+ this.fingerprint = fingerprint;
+ }
+
+ public String getPlaintext() {
+ return plaintext;
+ }
+
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+ }
+
+ public static class XmppAxolotlKeyTransportMessage {
+ private final String fingerprint;
+ private final byte[] key;
+ private final byte[] iv;
+
+ XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
+ this.fingerprint = fingerprint;
+ this.key = key;
+ this.iv = iv;
+ }
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+
+ public byte[] getKey() {
+ return key;
+ }
+
+ public byte[] getIv() {
+ return iv;
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index 13bfecfca..0635a4553 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -89,8 +89,8 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private transient MucOptions mucOptions = null;
private byte[] symmetricKey;
private boolean messagesLeftOnServer = true;
- private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
- private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
+ private ChatState mOutgoingChatState = Config.DEFAULT_CHAT_STATE;
+ private ChatState mIncomingChatState = Config.DEFAULT_CHAT_STATE;
private String mLastReceivedOtrMessageId = null;
private String mFirstMamReference = null;
diff --git a/src/main/java/de/pixart/messenger/entities/MucOptions.java b/src/main/java/de/pixart/messenger/entities/MucOptions.java
index 40a0be58d..c4e711400 100644
--- a/src/main/java/de/pixart/messenger/entities/MucOptions.java
+++ b/src/main/java/de/pixart/messenger/entities/MucOptions.java
@@ -96,7 +96,7 @@ public class MucOptions {
public void resetChatState() {
synchronized (users) {
for (User user : users) {
- user.chatState = Config.DEFAULT_CHATSTATE;
+ user.chatState = Config.DEFAULT_CHAT_STATE;
}
}
}
@@ -747,7 +747,7 @@ public class MucOptions {
private long pgpKeyId = 0;
private Avatar avatar;
private MucOptions options;
- private ChatState chatState = Config.DEFAULT_CHATSTATE;
+ private ChatState chatState = Config.DEFAULT_CHAT_STATE;
public User(MucOptions options, Jid fullJid) {
this.options = options;
diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java
index 8dadbd364..5fd701f89 100644
--- a/src/main/java/de/pixart/messenger/parser/MessageParser.java
+++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java
@@ -26,6 +26,7 @@ import de.pixart.messenger.crypto.OtrService;
import de.pixart.messenger.crypto.axolotl.AxolotlService;
import de.pixart.messenger.crypto.axolotl.BrokenSessionException;
import de.pixart.messenger.crypto.axolotl.NotEncryptedForThisDeviceException;
+import de.pixart.messenger.crypto.axolotl.OutdatedSenderException;
import de.pixart.messenger.crypto.axolotl.XmppAxolotlMessage;
import de.pixart.messenger.entities.Account;
import de.pixart.messenger.entities.Bookmark;
@@ -235,6 +236,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
} catch (NotEncryptedForThisDeviceException e) {
return new Message(conversation, "", Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE, status);
+ } catch (OutdatedSenderException e) {
+ return new Message(conversation, "", Message.ENCRYPTION_AXOLOTL_FAILED, status);
}
if (plaintextMessage != null) {
Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index 5f4abb833..bd981774c 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -1753,7 +1753,7 @@ public class XmppConnectionService extends Service {
if (delay) {
mMessageGenerator.addDelay(packet, message.getTimeSent());
}
- if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
if (this.sendChatStates()) {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
@@ -2809,7 +2809,7 @@ public class XmppConnectionService extends Service {
if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.getMucOptions().resetChatState();
} else {
- conversation.setIncomingChatState(Config.DEFAULT_CHATSTATE);
+ conversation.setIncomingChatState(Config.DEFAULT_CHAT_STATE);
}
}
for (Account account : getAccounts()) {
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index b2a1c8969..2e8e50ecb 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -2020,7 +2020,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
Toast.makeText(getActivity(), activity.getString(R.string.user_has_left_conference, counterpart.getResource()), Toast.LENGTH_SHORT).show();
return;
}
- if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
activity.xmppConnectionService.sendChatState(conversation);
}
this.binding.textinput.setText("");
@@ -2158,7 +2158,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private void updateChatState(final Conversation conversation, final String msg) {
- ChatState state = msg.length() == 0 ? Config.DEFAULT_CHATSTATE : ChatState.PAUSED;
+ ChatState state = msg.length() == 0 ? Config.DEFAULT_CHAT_STATE : ChatState.PAUSED;
Account.State status = conversation.getAccount().getStatus();
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(state)) {
activity.xmppConnectionService.sendChatState(conversation);
@@ -2967,7 +2967,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return;
}
Account.State status = conversation.getAccount().getStatus();
- if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
+ if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHAT_STATE)) {
service.sendChatState(conversation);
}
runOnUiThread(() -> {