OMEMO encrypted File transfers

This commit is contained in:
vanitasvitae 2017-08-15 17:44:02 +02:00
parent 9f53052954
commit fcd0a7ce8c
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
4 changed files with 152 additions and 21 deletions

View File

@ -2,20 +2,36 @@ package de.vanitasvitae.sync_client;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashSet;
import java.util.Set;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.SubscribeListener;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
import org.jivesoftware.smackx.jet.JetManager;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager;
import org.jivesoftware.smackx.omemo.OmemoConfiguration;
import org.jivesoftware.smackx.omemo.OmemoManager;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
import org.jivesoftware.smackx.omemo.signal.SignalOmemoService;
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
import org.jxmpp.jid.FullJid;
import org.jxmpp.stringprep.XmppStringprepException;
@ -26,7 +42,10 @@ public class Client {
protected final Path root;
protected final Set<FullJid> remotes = new HashSet<>();
public Client(String username, String password, String directory) throws XmppStringprepException {
public Client(String username, String password, String directory)
throws XmppStringprepException, CorruptedOmemoKeyException, NoSuchAlgorithmException, UnsupportedEncodingException,
InvalidKeyException, InterruptedException, XMPPException.XMPPErrorException, NoSuchPaddingException, BadPaddingException,
InvalidAlgorithmParameterException, NoSuchProviderException, IllegalBlockSizeException, SmackException {
File dir = new File(directory);
if (!dir.exists()) {
throw new IllegalArgumentException("Directory " + directory + " does not exist!");
@ -37,10 +56,29 @@ public class Client {
Roster.getInstanceFor(connection).addSubscribeListener((from, subscribeRequest) -> SubscribeListener.SubscribeAnswer.Approve);
JingleS5BTransportManager.getInstanceFor(connection);
JingleIBBTransportManager.getInstanceFor(connection);
JetManager jetm = JetManager.getInstanceFor(connection);
SignalOmemoService.acknowledgeLicense();
SignalOmemoService.setup();
String userHome = System.getProperty("user.home");
File storePath;
if (userHome != null) {
File f = new File(userHome);
storePath = new File(f, ".config/omemo_store");
} else {
storePath = new File("omemo_store");
}
OmemoConfiguration.setFileBasedOmemoStoreDefaultPath(storePath);
}
public void login() throws InterruptedException, IOException, SmackException, XMPPException {
public void login() throws InterruptedException, IOException, SmackException, XMPPException, CorruptedOmemoKeyException {
((XMPPTCPConnection) connection).connect().login();
OmemoManager omemoManager = OmemoManager.getInstanceFor(connection);
omemoManager.initialize();
connection.setReplyTimeout(10000);
JetManager.getInstanceFor(connection).registerEncryptionMethod(OmemoManager.getInstanceFor(connection));
JetManager.registerEncryptionMethodProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider());
}
public void addRemote(FullJid remote) {

View File

@ -1,9 +1,18 @@
package de.vanitasvitae.sync_client;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
@ -11,6 +20,10 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
import org.jivesoftware.smackx.omemo.OmemoManager;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@ -41,11 +54,17 @@ public class Main {
return options;
}
public static Slave slave(String username, String password, String dir) throws IOException, InterruptedException, XMPPException, SmackException {
public static Slave slave(String username, String password, String dir) throws IOException, InterruptedException,
XMPPException, SmackException, NoSuchPaddingException, CorruptedOmemoKeyException, InvalidKeyException,
NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException,
InvalidAlgorithmParameterException {
return new Slave(username, password, dir);
}
public static Master master(String username, String password, String dir) throws IOException, InterruptedException, XMPPException, SmackException {
public static Master master(String username, String password, String dir) throws IOException, InterruptedException,
XMPPException, SmackException, CorruptedOmemoKeyException, InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException {
return new Master(username, password, dir);
}
@ -95,6 +114,7 @@ public class Main {
}
Roster roster = Roster.getInstanceFor(client.connection);
OmemoManager omemoManager = OmemoManager.getInstanceFor(client.connection);
switch (split[0]) {
case "/add":
@ -146,6 +166,52 @@ public class Main {
}
break;
case "/trust":
if (split.length != 2) {
System.out.println("Usage: /trust user@server.tld");
continue outerloop;
}
BareJid jid = getJid(split[1]);
omemoManager.requestDeviceListUpdateFor(jid);
omemoManager.requestDeviceListUpdateFor(client.connection.getUser().asBareJid());
HashMap<OmemoDevice, OmemoFingerprint> theirFPs = omemoManager.getActiveFingerprints(jid);
HashMap<OmemoDevice, OmemoFingerprint> ourFPs = omemoManager.getActiveFingerprints(client.connection.getUser().asBareJid());
System.out.println("Their fingerprints: <deviceId>: <fingerprint>: <trusted>");
for (OmemoDevice d : theirFPs.keySet()) {
OmemoFingerprint f = theirFPs.get(d);
System.out.println(d.getDeviceId() + ": " + f + ": " + omemoManager.isTrustedOmemoIdentity(d, f));
System.out.println("Trust? (y/n)");
String a = scanner.nextLine();
if (a.isEmpty()) {
continue;
} else if (a.toLowerCase().equals("y")) {
omemoManager.trustOmemoIdentity(d, f);
System.out.println("Trusted.");
} else {
omemoManager.distrustOmemoIdentity(d, f);
System.out.println("Distrusted.");
}
}
System.out.println("Our fingerprints: <deviceId>: <fingerprint>: <trusted>");
for (OmemoDevice d : ourFPs.keySet()) {
OmemoFingerprint f = ourFPs.get(d);
System.out.println(d.getDeviceId() + ": " + f + ": " + omemoManager.isTrustedOmemoIdentity(d, f));
System.out.println("Trust? (y/n)");
String a = scanner.nextLine();
if (a.isEmpty()) {
continue;
} else if (a.toLowerCase().equals("y")) {
omemoManager.trustOmemoIdentity(d, f);
System.out.println("Trusted.");
} else {
omemoManager.distrustOmemoIdentity(d, f);
System.out.println("Distrusted.");
}
}
break;
case "/quit":
((AbstractXMPPConnection) client.connection).disconnect(new Presence(Presence.Type.unavailable, "Shutdown", 100, Presence.Mode.away));
break outerloop;

View File

@ -17,12 +17,23 @@ import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashMap;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smackx.jft.JingleFileTransferManager;
import org.jivesoftware.smackx.jet.JetManager;
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
import org.jivesoftware.smackx.omemo.OmemoManager;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jxmpp.jid.FullJid;
@ -31,18 +42,23 @@ public class Master extends Client {
private final WatchService fileWatcher;
private final HashMap<WatchKey, Path> fileKeys;
private boolean trace;
private JingleFileTransferManager jftm;
private final JingleFileTransferManager jftm;
private final JetManager jetm;
public Master(String username, String password, String directory) throws IOException, InterruptedException, SmackException, XMPPException {
public Master(String username, String password, String directory) throws IOException, InterruptedException,
SmackException, XMPPException, CorruptedOmemoKeyException, InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException,
NoSuchProviderException, InvalidKeyException {
super(username, password, directory);
fileWatcher = FileSystems.getDefault().newWatchService();
fileKeys = new HashMap<>();
registerFileWatcher();
jftm = JingleFileTransferManager.getInstanceFor(connection);
jetm = JetManager.getInstanceFor(connection);
jftm.addIncomingFileOfferListener(offer -> {
try {
offer.accept(connection, new File(root.toFile(), offer.getFile().getName()));
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NoResponseException | SmackException.NotConnectedException e) {
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NoResponseException | SmackException.NotConnectedException | IOException e) {
e.printStackTrace();
}
});
@ -126,14 +142,6 @@ public class Master extends Client {
{
WatchKey key = dir.register(fileWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) {
Path prev = fileKeys.get(key);
if (prev == null) {
System.out.format("register: %s\n", dir);
} else {
if (!dir.equals(prev)) {
System.out.format("update: %s -> %s\n", prev, dir);
}
}
File[] files = dir.toFile().listFiles();
for (File f : files) {
if (f.isFile()) {
@ -181,7 +189,12 @@ public class Master extends Client {
throw new AssertionError("Illegal path! " + fileName);
}
for (FullJid recipient : remotes) {
jftm.sendFile(file, fileName, recipient);
try {
jetm.sendEncryptedFile(file, fileName, recipient, OmemoManager.getInstanceFor(connection));
} catch (Exception e) {
System.out.println("Could not send file: " + e);
}
//jftm.sendFile(file, fileName, recipient);
}
}
}

View File

@ -1,16 +1,30 @@
package de.vanitasvitae.sync_client;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jft.JingleFileTransferManager;
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jxmpp.stringprep.XmppStringprepException;
public class Slave extends Client {
public Slave(String username, String password, String dir) throws XmppStringprepException {
public Slave(String username, String password, String dir) throws XmppStringprepException,
InvalidAlgorithmParameterException, NoSuchAlgorithmException, UnsupportedEncodingException,
InvalidKeyException, InterruptedException, XMPPException.XMPPErrorException, NoSuchPaddingException,
BadPaddingException, CorruptedOmemoKeyException, NoSuchProviderException, IllegalBlockSizeException,
SmackException {
super(username, password, dir);
JingleFileTransferManager jingleFileTransferManager = JingleFileTransferManager.getInstanceFor(connection);
jingleFileTransferManager.addIncomingFileOfferListener(offer -> {
@ -18,7 +32,7 @@ public class Slave extends Client {
File target = new File(root.toFile(), offer.getFile().getName());
target.getParentFile().mkdirs();
offer.accept(connection, target);
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NoResponseException | SmackException.NotConnectedException e) {
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NoResponseException | SmackException.NotConnectedException | IOException e) {
e.printStackTrace();
}
});