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.File;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; 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.HashSet;
import java.util.Set; 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.SmackException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.SubscribeListener; import org.jivesoftware.smack.roster.SubscribeListener;
import org.jivesoftware.smack.tcp.XMPPTCPConnection; 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_ibb.JingleIBBTransportManager;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager; 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.jid.FullJid;
import org.jxmpp.stringprep.XmppStringprepException; import org.jxmpp.stringprep.XmppStringprepException;
@ -26,7 +42,10 @@ public class Client {
protected final Path root; protected final Path root;
protected final Set<FullJid> remotes = new HashSet<>(); 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); File dir = new File(directory);
if (!dir.exists()) { if (!dir.exists()) {
throw new IllegalArgumentException("Directory " + directory + " does not exist!"); throw new IllegalArgumentException("Directory " + directory + " does not exist!");
@ -37,10 +56,29 @@ public class Client {
Roster.getInstanceFor(connection).addSubscribeListener((from, subscribeRequest) -> SubscribeListener.SubscribeAnswer.Approve); Roster.getInstanceFor(connection).addSubscribeListener((from, subscribeRequest) -> SubscribeListener.SubscribeAnswer.Approve);
JingleS5BTransportManager.getInstanceFor(connection); JingleS5BTransportManager.getInstanceFor(connection);
JingleIBBTransportManager.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(); ((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) { public void addRemote(FullJid remote) {

View file

@ -1,9 +1,18 @@
package de.vanitasvitae.sync_client; package de.vanitasvitae.sync_client;
import java.io.IOException; 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.List;
import java.util.Scanner; 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.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
@ -11,6 +20,10 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry; 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.CommandLine;
import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.CommandLineParser;
@ -41,11 +54,17 @@ public class Main {
return options; 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); 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); return new Master(username, password, dir);
} }
@ -95,6 +114,7 @@ public class Main {
} }
Roster roster = Roster.getInstanceFor(client.connection); Roster roster = Roster.getInstanceFor(client.connection);
OmemoManager omemoManager = OmemoManager.getInstanceFor(client.connection);
switch (split[0]) { switch (split[0]) {
case "/add": case "/add":
@ -146,6 +166,52 @@ public class Main {
} }
break; 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": case "/quit":
((AbstractXMPPConnection) client.connection).disconnect(new Presence(Presence.Type.unavailable, "Shutdown", 100, Presence.Mode.away)); ((AbstractXMPPConnection) client.connection).disconnect(new Presence(Presence.Type.unavailable, "Shutdown", 100, Presence.Mode.away));
break outerloop; break outerloop;

View file

@ -17,12 +17,23 @@ import java.nio.file.WatchEvent;
import java.nio.file.WatchKey; import java.nio.file.WatchKey;
import java.nio.file.WatchService; import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes; 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 java.util.HashMap;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.Async; 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; import org.jxmpp.jid.FullJid;
@ -31,18 +42,23 @@ public class Master extends Client {
private final WatchService fileWatcher; private final WatchService fileWatcher;
private final HashMap<WatchKey, Path> fileKeys; private final HashMap<WatchKey, Path> fileKeys;
private boolean trace; 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); super(username, password, directory);
fileWatcher = FileSystems.getDefault().newWatchService(); fileWatcher = FileSystems.getDefault().newWatchService();
fileKeys = new HashMap<>(); fileKeys = new HashMap<>();
registerFileWatcher(); registerFileWatcher();
jftm = JingleFileTransferManager.getInstanceFor(connection); jftm = JingleFileTransferManager.getInstanceFor(connection);
jetm = JetManager.getInstanceFor(connection);
jftm.addIncomingFileOfferListener(offer -> { jftm.addIncomingFileOfferListener(offer -> {
try { try {
offer.accept(connection, new File(root.toFile(), offer.getFile().getName())); 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(); e.printStackTrace();
} }
}); });
@ -126,14 +142,6 @@ public class Master extends Client {
{ {
WatchKey key = dir.register(fileWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); WatchKey key = dir.register(fileWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) { 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(); File[] files = dir.toFile().listFiles();
for (File f : files) { for (File f : files) {
if (f.isFile()) { if (f.isFile()) {
@ -181,7 +189,12 @@ public class Master extends Client {
throw new AssertionError("Illegal path! " + fileName); throw new AssertionError("Illegal path! " + fileName);
} }
for (FullJid recipient : remotes) { 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; package de.vanitasvitae.sync_client;
import java.io.File; 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.SmackException;
import org.jivesoftware.smack.XMPPException; 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; import org.jxmpp.stringprep.XmppStringprepException;
public class Slave extends Client { 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); super(username, password, dir);
JingleFileTransferManager jingleFileTransferManager = JingleFileTransferManager.getInstanceFor(connection); JingleFileTransferManager jingleFileTransferManager = JingleFileTransferManager.getInstanceFor(connection);
jingleFileTransferManager.addIncomingFileOfferListener(offer -> { jingleFileTransferManager.addIncomingFileOfferListener(offer -> {
@ -18,7 +32,7 @@ public class Slave extends Client {
File target = new File(root.toFile(), offer.getFile().getName()); File target = new File(root.toFile(), offer.getFile().getName());
target.getParentFile().mkdirs(); target.getParentFile().mkdirs();
offer.accept(connection, target); 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(); e.printStackTrace();
} }
}); });