Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First draft of OMEMO #1256

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9c00af7
Playing around with Smack OMEMO
daniele-athome Sep 9, 2018
98e1016
Merge branch 'master' into feature/omemo
daniele-athome Nov 1, 2018
dade92c
Merge branch 'master' into feature/omemo
daniele-athome Apr 19, 2019
0353ef1
Merge branch master
daniele-athome Apr 19, 2019
77e371f
Merge branch master
daniele-athome Apr 19, 2019
e90be41
Merge remote-tracking branch 'origin/master' into feature/omemo
daniele-athome Apr 19, 2019
707a651
Merge branch master
daniele-athome Apr 21, 2019
c7b7111
Merge branch master
daniele-athome Apr 21, 2019
cdb15a3
Begin refactoring of Coder to accomodate for OMEMO needs
daniele-athome Apr 21, 2019
3ffc778
Comments
daniele-athome Apr 21, 2019
06f6d5b
Some tests with OMEMO send/receive
daniele-athome Apr 22, 2019
e31673c
Better handle decryption errors
daniele-athome Apr 23, 2019
9a92053
Handle encryption fallback in a proper manner
daniele-athome Apr 24, 2019
df0f3f0
Extract trust information for OMEMO
daniele-athome Apr 25, 2019
fbe15a8
Fix using OMEMO with yourself
daniele-athome Apr 29, 2019
e81cd19
Time to deprecated stuff from years ago
daniele-athome Apr 30, 2019
ba70eed
Fix processing encrypted chat state group message
daniele-athome May 2, 2019
216c9ac
Update Gradle tools
daniele-athome May 25, 2019
c423e8a
Working incoming OMEMO processing via some dirty workarounds
daniele-athome May 25, 2019
8a9e45b
Upgrade Smack to 4.3.4
daniele-athome Jun 8, 2019
dfd4e7f
Crash test scenario
daniele-athome Jun 9, 2019
9b94523
More or less working OMEMO between clients
daniele-athome Jun 9, 2019
192ef2e
Merge branch 'master' into feature/omemo
daniele-athome Jun 15, 2019
719eaff
Merge branch 'master' into feature/omemo
daniele-athome Jun 16, 2019
ba76ed9
Merge branch 'master' into feature/omemo
daniele-athome Jun 16, 2019
c8e92bb
Unused code
daniele-athome Jun 16, 2019
d97b0ab
Remove name from key UID (close #1179)
daniele-athome Jun 16, 2019
b1c6087
Merge branch 'master' into feature/omemo
daniele-athome Jun 16, 2019
70e134f
Merge branch 'master' into feature/omemo
daniele-athome Jul 10, 2019
691b4a3
Init instance only when sure
daniele-athome Aug 11, 2019
29b5f71
Merge branch 'master' into feature/omemo
daniele-athome Dec 17, 2019
bc245c8
Merge branch 'master' into feature/omemo
daniele-athome Mar 19, 2020
ec16b56
Merge branch 'master' into feature/omemo
daniele-athome Mar 19, 2020
5330d97
Handle disconnections during OMEMO stuff (#1260)
daniele-athome Mar 19, 2020
32167b2
Merge branch 'master' into feature/omemo
daniele-athome Mar 22, 2020
780ad2f
Cache contact security flags
daniele-athome Mar 22, 2020
035d47b
Revert ZXing to 3.3.3 to allow API level < 24
daniele-athome Mar 28, 2020
992fb68
Upgrade dependencies
daniele-athome Mar 28, 2020
a3667c2
Upgrade dependencies
daniele-athome Mar 28, 2020
dc27c67
Merge branch 'master' into feature/omemo
daniele-athome Mar 29, 2020
b25bef4
Merge branch 'master' into feature/omemo
daniele-athome Mar 29, 2020
498369c
Merge branch 'master' into feature/omemo
daniele-athome Mar 29, 2020
3da589c
Merge branch 'master' into feature/omemo
daniele-athome Apr 4, 2020
251c4e6
Merge branch 'master' into feature/omemo
daniele-athome Apr 4, 2020
ebfa4fc
Merge branch 'master' into feature/omemo
daniele-athome Apr 5, 2020
e4c6ac8
Merge branch 'master' into feature/omemo
daniele-athome Apr 21, 2020
3e135a8
Merge branch 'master' into feature/omemo
daniele-athome Apr 21, 2020
58fdd35
Preliminary porting to Smack 4.4
daniele-athome Apr 22, 2020
aa8c9b1
Preliminary porting to Smack 4.4
daniele-athome Apr 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ android {

dependencies {
api (project(':client-common-java')) {
exclude group: 'net.sf.kxml'
exclude group: 'xpp3', module: 'xpp3_min'
}

// support libraries
Expand All @@ -187,7 +187,12 @@ dependencies {
// network/protocol libraries
implementation "org.igniterealtime.smack:smack-tcp:$smackVersion"
implementation "org.igniterealtime.smack:smack-experimental:$smackVersion"
implementation "org.igniterealtime.smack:smack-android:$smackVersion"
implementation ("org.igniterealtime.smack:smack-android:$smackVersion") {
exclude group: 'xpp3', module: 'xpp3_min'
}
implementation "org.igniterealtime.smack:smack-omemo:$smackVersion"
implementation "org.igniterealtime.smack:smack-omemo-signal:$smackVersion"
implementation 'org.whispersystems:signal-protocol-java:2.8.1'
implementation 'info.guardianproject.netcipher:netcipher:1.2.1'
implementation 'com.squareup.okhttp3:okhttp:4.5.0'
implementation 'com.segment.backo:backo:1.0.0'
Expand Down
2 changes: 2 additions & 0 deletions app/libs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Temporary place for the libraries I couldn't automatically reference from Gradle.
This folder will disappear before release.
Binary file added app/libs/smack-omemo-4.3.4.jar
Binary file not shown.
Binary file added app/libs/smack-omemo-signal-4.3.4.jar
Binary file not shown.
9 changes: 9 additions & 0 deletions app/proguard.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@
-keep class org.bouncycastle.openpgp.** { *; }

# Smack Core classes should be figured out by Proguard
# FIXME not really!
-keep class org.jivesoftware.smack.initializer.** { *; }
-keep class org.jivesoftware.smack.packet.** { *; }
-keep class org.jivesoftware.smack.**.packet.** { *; }

# keep Smack IM
-keep class org.jivesoftware.smack.im.** { *; }
Expand Down Expand Up @@ -82,6 +85,9 @@
-keep class org.jivesoftware.smackx.vcardtemp.** { *; }
-keep class org.jivesoftware.smackx.xdata.** { *; }
-keep class org.jivesoftware.smackx.forward.** { *; }
-keep class org.jivesoftware.smackx.pubsub.** { *; }
-keep class org.jivesoftware.smackx.pep.** { *; }
-keep class org.jivesoftware.smackx.omemo.** { *; }

# keep other Smack utilities
-keep class org.jivesoftware.smack.**.java7.** { *; }
Expand Down Expand Up @@ -135,6 +141,9 @@
public *;
}

# WhisperSystems (libsignal and curve*)
-keep class org.whispersystems.** { *; }

# OkHttp
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,15 +478,15 @@ public void testSetup() {

@Test
public void testAutotrustedLevel() throws IOException, PGPException {
Keyring.setAutoTrustLevel(mContext, TEST_USERID, MyUsers.Keys.TRUST_VERIFIED);
Keyring.setAutoTrustLevel(mContext, TEST_USERID, Keyring.TRUST_VERIFIED);
assertQueryValues(MyUsers.Keys.getUri(TEST_USERID, Keyring.VALUE_AUTOTRUST),
MyUsers.Keys.JID, TEST_USERID,
MyUsers.Keys.FINGERPRINT, Keyring.VALUE_AUTOTRUST);

byte[] keydata = Base64.decode(TEST_KEYDATA, Base64.DEFAULT);
PGPPublicKeyRing originalKey = PGP.readPublicKeyring(keydata);
Keyring.setKey(mContext, TEST_USERID, keydata);
PGPPublicKeyRing publicKey = Keyring.getPublicKey(mContext, TEST_USERID, MyUsers.Keys.TRUST_VERIFIED);
PGPPublicKeyRing publicKey = Keyring.getPublicKey(mContext, TEST_USERID, Keyring.TRUST_VERIFIED);
assertNotNull(publicKey);
assertArrayEquals(publicKey.getEncoded(), originalKey.getEncoded());

Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/org/kontalk/MessagesController.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
import org.kontalk.provider.KontalkGroupCommands;
import org.kontalk.provider.MessagesProviderClient;
import org.kontalk.provider.MyMessages;
import org.kontalk.provider.MyUsers;
import org.kontalk.provider.UsersProvider;
import org.kontalk.service.DownloadService;
import org.kontalk.service.MediaService;
Expand Down Expand Up @@ -284,7 +283,7 @@ public boolean setTrustLevelAndRetryMessages(String jid, String fingerprint, int
throw new NullPointerException("fingerprint");

Keyring.setTrustLevel(mContext, jid, fingerprint, trustLevel);
if (trustLevel >= MyUsers.Keys.TRUST_IGNORED) {
if (trustLevel >= Keyring.TRUST_IGNORED) {
retryMessagesTo(jid);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,11 @@ public static void exportDefaultPersonalKey(Context ctx, OutputStream dest, Stri
// trusted keys
Map<String, Keyring.TrustedFingerprint> trustedKeys = Keyring.getTrustedKeys(ctx);

String displayName = m.getUserData(acc, DATA_NAME);

PersonalKeyExporter exp = new PersonalKeyExporter();
exp.save(privateKey, publicKey, dest, passphrase, exportPassphrase, bridgeCert, trustedKeys, acc.name);
exp.save(privateKey, publicKey, dest, passphrase, exportPassphrase,
bridgeCert, trustedKeys, acc.name, displayName);
}

public static byte[] getPrivateKeyExportData(Context ctx, String passphrase, String exportPassphrase)
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/org/kontalk/client/KontalkConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.sm.StreamManagementException;
import org.jivesoftware.smack.sm.predicates.ForMatchingPredicateOrAfterXStanzas;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.receipts.DeliveryReceipt;
import org.jivesoftware.smackx.receipts.DeliveryReceiptRequest;
import org.jxmpp.stringprep.XmppStringprepException;
Expand All @@ -66,6 +69,8 @@ public class KontalkConnection extends XMPPTCPConnection {
/** Packet reply timeout. */
public static final int DEFAULT_PACKET_TIMEOUT = 15000;

private DiscoverInfo mDiscoverInfoCache;

protected EndpointServer mServer;

/** Actually a copy of the same Smack map, but since we need access to the listeners... */
Expand Down Expand Up @@ -246,10 +251,41 @@ protected void processStanza(Stanza packet) throws InterruptedException {
}
}

@Override
protected void shutdown() {
purgeCaches();
super.shutdown();
}

@Override
public synchronized void instantShutdown() {
purgeCaches();
super.instantShutdown();
}

private synchronized void purgeCaches() {
mDiscoverInfoCache = null;
}

public EndpointServer getServer() {
return mServer;
}

public synchronized DiscoverInfo getDiscoverInfo() throws XMPPException.XMPPErrorException,
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
if (mDiscoverInfoCache == null) {
mDiscoverInfoCache = ServiceDiscoveryManager.getInstanceFor(this)
.discoverInfo(getXMPPServiceDomain());
}
return mDiscoverInfoCache;
}

public boolean supportsFeature(String feature) throws XMPPException.XMPPErrorException,
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
DiscoverInfo cache = getDiscoverInfo();
return cache.containsFeature(feature);
}

@Override
public StanzaListener addStanzaIdAcknowledgedListener(String id, StanzaListener listener) throws StreamManagementException.StreamManagementNotEnabledException {
AckMultiListener multi = mStanzaIdAcknowledgedListeners.get(id);
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/org/kontalk/client/SmackInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.kontalk.client;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

Expand All @@ -27,6 +28,9 @@
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smackx.iqregister.provider.RegistrationProvider;
import org.jivesoftware.smackx.iqversion.VersionManager;
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
import org.jivesoftware.smackx.omemo.signal.SignalOmemoService;
import org.jivesoftware.smackx.xdata.provider.DataFormProvider;
import org.minidns.dnsserverlookup.android21.AndroidUsingLinkProperties;

Expand All @@ -42,6 +46,8 @@
public class SmackInitializer {

private static boolean sInitialized;
// TODO not sure what to do with this for now
private static SignalOmemoService sOmemoService;

public static void initialize(Context context) {
if (!sInitialized) {
Expand Down Expand Up @@ -69,6 +75,22 @@ public static void initialize(Context context) {
// we want to manually handle roster stuff
Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);

// initialize omemo engine
SignalOmemoService.acknowledgeLicense();
try {
SignalOmemoService.setup();
sOmemoService = (SignalOmemoService) SignalOmemoService.getInstance();
sOmemoService.setOmemoStoreBackend(
new SignalCachingOmemoStore(
new SignalFileBasedOmemoStore(new File(context.getFilesDir(), "omemo"))
)
);
}
catch (Exception e) {
// this shouldn't happen, so we just crash for now
throw new RuntimeException("OMEMO engine failure", e);
}

sInitialized = true;
}
}
Expand Down
35 changes: 30 additions & 5 deletions app/src/main/java/org/kontalk/client/smack/SmackFuture.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2017-2018 Florian Schmaus
* Copyright 2017-2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,9 @@
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
Expand All @@ -29,10 +31,10 @@

import javax.net.SocketFactory;

import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.CallbackRecipient;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.ExceptionCallback;
import org.jivesoftware.smack.util.SuccessCallback;

Expand All @@ -50,6 +52,8 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,

private ExceptionCallback<E> exceptionCallback;

private Consumer<SmackFuture<V, E>> completionCallback;

@Override
public final synchronized boolean cancel(boolean mayInterruptIfRunning) {
if (isDone()) {
Expand Down Expand Up @@ -89,16 +93,21 @@ public CallbackRecipient<V, E> onError(ExceptionCallback<E> exceptionCallback) {
return this;
}

public void onCompletion(Consumer<SmackFuture<V, E>> completionCallback) {
this.completionCallback = completionCallback;
maybeInvokeCallbacks();
}

private V getOrThrowExecutionException() throws ExecutionException {
assert (result != null || exception != null || cancelled);
assert result != null || exception != null || cancelled;
if (result != null) {
return result;
}
if (exception != null) {
throw new ExecutionException(exception);
}

assert (cancelled);
assert cancelled;
throw new CancellationException();
}

Expand Down Expand Up @@ -150,11 +159,19 @@ public final synchronized V get(long timeout, TimeUnit unit)
return getOrThrowExecutionException();
}

public V getIfAvailable() {
return result;
}

protected final synchronized void maybeInvokeCallbacks() {
if (cancelled) {
return;
}

if ((result != null || exception != null) && completionCallback != null) {
completionCallback.accept(this);
}

if (result != null && successCallback != null) {
AbstractXMPPConnectionWrapper.asyncGo(new Runnable() {
@Override
Expand Down Expand Up @@ -294,7 +311,7 @@ public final synchronized void processStanza(Stanza stanza) {
* A simple version of InternalSmackFuture which implements isNonFatalException(E) as always returning
* <code>false</code> method.
*
* @param <V>
* @param <V> the return value of the future.
*/
public abstract static class SimpleInternalProcessStanzaSmackFuture<V, E extends Exception>
extends InternalProcessStanzaSmackFuture<V, E> {
Expand All @@ -310,4 +327,12 @@ public static <V, E extends Exception> SmackFuture<V, E> from(V result) {
return future;
}

public static boolean await(Collection<? extends SmackFuture<?, ?>> futures, long timeout, TimeUnit unit) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(futures.size());
for (SmackFuture<?, ?> future : futures) {
future.onCompletion(f -> latch.countDown());
}

return latch.await(timeout, unit);
}
}
Loading