diff options
Diffstat (limited to '')
-rw-r--r-- | src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java | 390 |
1 files changed, 189 insertions, 201 deletions
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 |