From fcd0a7ce8ce8b8a51de5734e6af8260e2490e7c5 Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Tue, 15 Aug 2017 17:44:02 +0200 Subject: [PATCH] OMEMO encrypted File transfers --- .../de/vanitasvitae/sync_client/Client.java | 44 +++++++++++- .../de/vanitasvitae/sync_client/Main.java | 70 ++++++++++++++++++- .../de/vanitasvitae/sync_client/Master.java | 39 +++++++---- .../de/vanitasvitae/sync_client/Slave.java | 20 +++++- 4 files changed, 152 insertions(+), 21 deletions(-) diff --git a/src/main/java/de/vanitasvitae/sync_client/Client.java b/src/main/java/de/vanitasvitae/sync_client/Client.java index 43714d0..6612390 100644 --- a/src/main/java/de/vanitasvitae/sync_client/Client.java +++ b/src/main/java/de/vanitasvitae/sync_client/Client.java @@ -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 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) { diff --git a/src/main/java/de/vanitasvitae/sync_client/Main.java b/src/main/java/de/vanitasvitae/sync_client/Main.java index dbe5a30..f3afe09 100644 --- a/src/main/java/de/vanitasvitae/sync_client/Main.java +++ b/src/main/java/de/vanitasvitae/sync_client/Main.java @@ -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 theirFPs = omemoManager.getActiveFingerprints(jid); + HashMap ourFPs = omemoManager.getActiveFingerprints(client.connection.getUser().asBareJid()); + System.out.println("Their fingerprints: : : "); + 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: : : "); + 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; diff --git a/src/main/java/de/vanitasvitae/sync_client/Master.java b/src/main/java/de/vanitasvitae/sync_client/Master.java index 955112b..2ae231d 100644 --- a/src/main/java/de/vanitasvitae/sync_client/Master.java +++ b/src/main/java/de/vanitasvitae/sync_client/Master.java @@ -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 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); } } } diff --git a/src/main/java/de/vanitasvitae/sync_client/Slave.java b/src/main/java/de/vanitasvitae/sync_client/Slave.java index 5d6e396..b940553 100644 --- a/src/main/java/de/vanitasvitae/sync_client/Slave.java +++ b/src/main/java/de/vanitasvitae/sync_client/Slave.java @@ -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(); } });