# HG changeset patch # User Olaf Wintermann # Date 1735212545 -3600 # Node ID 42d0d099492b068f5ab658a253916c2f145aae2e # Parent f3095cda599e994ffd118c99e842933aab1c21eb add incomplete otr code diff -r f3095cda599e -r 42d0d099492b src/main/java/de/unixwork/im/ConversationFrame.java --- a/src/main/java/de/unixwork/im/ConversationFrame.java Wed Dec 25 21:49:48 2024 +0100 +++ b/src/main/java/de/unixwork/im/ConversationFrame.java Thu Dec 26 12:29:05 2024 +0100 @@ -15,10 +15,11 @@ private JTextArea messageHistory; private JTextArea messageInput; private JButton sendButton; - private JButton topRightButton; + private JButton secureButton; private MessageSendListener messageSendListener; - private TopRightButtonListener topRightButtonListener; - + + boolean isSecure = false; + public ConversationFrame(String xid) { this.xid = xid; @@ -29,8 +30,8 @@ // Top panel with top-right button JPanel topPanel = new JPanel(new BorderLayout()); - topRightButton = new JButton("Insecure"); - topPanel.add(topRightButton, BorderLayout.EAST); + secureButton = new JButton("Insecure"); + topPanel.add(secureButton, BorderLayout.EAST); add(topPanel, BorderLayout.NORTH); // Split pane @@ -77,11 +78,13 @@ } }); - topRightButton.addActionListener(new ActionListener() { + secureButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (topRightButtonListener != null) { - topRightButtonListener.onTopRightButtonClicked(); + if(isSecure) { + + } else { + App.getInstance().getXmpp().startOTR(xid); } } }); @@ -119,11 +122,6 @@ this.messageSendListener = listener; } - // Method to set the top-right button listener - public void setTopRightButtonListener(TopRightButtonListener listener) { - this.topRightButtonListener = listener; - } - // Trigger the message send callback private void triggerMessageSend() { if (messageSendListener != null) { @@ -135,9 +133,4 @@ } } - // Interface for top-right button callback - public interface TopRightButtonListener { - void onTopRightButtonClicked(); - } - } diff -r f3095cda599e -r 42d0d099492b src/main/java/de/unixwork/im/OTR.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/unixwork/im/OTR.java Thu Dec 26 12:29:05 2024 +0100 @@ -0,0 +1,160 @@ +package de.unixwork.im; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.java.otr4j.OtrEngineHost; +import net.java.otr4j.OtrException; +import net.java.otr4j.OtrPolicy; +import net.java.otr4j.OtrPolicyImpl; +import net.java.otr4j.crypto.OtrCryptoEngineImpl; +import net.java.otr4j.crypto.OtrCryptoException; +import net.java.otr4j.session.FragmenterInstructions; +import net.java.otr4j.session.InstanceTag; +import net.java.otr4j.session.SessionID; + +public class OTR implements OtrEngineHost { + private final Xmpp xmpp; + + // Map to store key pairs for each session + private final Map keyPairCache = new HashMap<>(); + + public OTR(Xmpp xmpp) { + this.xmpp = xmpp; + } + + @Override + public void injectMessage(SessionID sid, String string) throws OtrException { + System.out.println("inject " + string); + xmpp.send(sid.getUserID(), string); + } + + @Override + public void unreadableMessageReceived(SessionID sid) throws OtrException { + // TODO: send error to App + } + + @Override + public void unencryptedMessageReceived(SessionID sid, String string) throws OtrException { + // TODO: send error to App + } + + @Override + public void showError(SessionID sid, String string) throws OtrException { + System.out.println("showError " + string); + } + + @Override + public void smpError(SessionID sid, int i, boolean bln) throws OtrException { + System.out.println("smpError"); + } + + @Override + public void smpAborted(SessionID sid) throws OtrException { + + } + + @Override + public void finishedSessionMessage(SessionID sid, String string) throws OtrException { + System.out.println("finishedSessionMessage: " + sid); + } + + @Override + public void requireEncryptedMessage(SessionID sid, String string) throws OtrException { + System.out.println("requireEncryptedMessage"); + } + + @Override + public OtrPolicy getSessionPolicy(SessionID sid) { + return new OtrPolicyImpl(OtrPolicy.ALLOW_V2 | OtrPolicy.ALLOW_V3 | OtrPolicy.OPPORTUNISTIC); + } + + @Override + public FragmenterInstructions getFragmenterInstructions(SessionID sid) { + return new FragmenterInstructions(4096, 131072); + } + + @Override + public KeyPair getLocalKeyPair(SessionID sid) throws OtrException { + // Check if a key pair already exists for the session + if (keyPairCache.containsKey(sid)) { + return keyPairCache.get(sid); + } + + // Generate a new key pair + KeyPair keyPair = generateKeyPair(); + + // Cache the key pair for this session + keyPairCache.put(sid, keyPair); + + return keyPair; + } + + // Helper method to generate a new key pair + private KeyPair generateKeyPair() { + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); // Use DSA for OTR + keyPairGenerator.initialize(1024); // OTR uses 1024-bit keys + return keyPairGenerator.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Error generating key pair", e); + } + } + + @Override + public byte[] getLocalFingerprintRaw(SessionID sid) { + // code from DummyClient: https://github.com/jitsi/otr4j/blob/master/src/test/java/net/java/otr4j/session/DummyClient.java + try { + return new OtrCryptoEngineImpl() + .getFingerprintRaw(getLocalKeyPair(sid) + .getPublic()); + } catch (OtrCryptoException e) { + e.printStackTrace(); + } catch (OtrException ex) { + Logger.getLogger(OTR.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + + @Override + public void askForSecret(SessionID sid, InstanceTag it, String string) { + System.out.println("askForSecret " + sid); + } + + @Override + public void verify(SessionID sid, String string, boolean bln) { + System.out.println("verify"); + } + + @Override + public void unverify(SessionID sid, String string) { + System.out.println("unverify"); + } + + @Override + public String getReplyForUnreadableMessage(SessionID sid) { + return "Message unreadable"; + } + + @Override + public String getFallbackMessage(SessionID sid) { + return "error"; + } + + @Override + public void messageFromAnotherInstanceReceived(SessionID sid) { + System.out.println("messageFromAnotherInstanceReceived"); + } + + @Override + public void multipleInstancesDetected(SessionID sid) { + System.out.println("multipleInstancesDeteced"); + } + +} diff -r f3095cda599e -r 42d0d099492b src/main/java/de/unixwork/im/Xmpp.java --- a/src/main/java/de/unixwork/im/Xmpp.java Wed Dec 25 21:49:48 2024 +0100 +++ b/src/main/java/de/unixwork/im/Xmpp.java Thu Dec 26 12:29:05 2024 +0100 @@ -8,6 +8,16 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; +import net.java.otr4j.OtrException; +import net.java.otr4j.OtrPolicy; +import net.java.otr4j.OtrPolicyImpl; +import net.java.otr4j.OtrSessionManager; +import net.java.otr4j.OtrSessionManagerImpl; +import net.java.otr4j.session.InstanceTag; +import net.java.otr4j.session.Session; +import net.java.otr4j.session.SessionID; +import net.java.otr4j.session.SessionImpl; +import net.java.otr4j.session.TLV; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; @@ -25,11 +35,40 @@ private XMPPTCPConnection connection = null; + private final OTR otr; + + private final OtrSessionManager otrSM; + // BlockingQueue for event-driven communication private final BlockingQueue eventQueue = new LinkedBlockingQueue<>(); public Xmpp(XMPPTCPConnectionConfiguration xmppConfig) { config = xmppConfig; + otr = new OTR(this); + otrSM = new OtrSessionManagerImpl(otr); + + } + + public OTR getOTR() { + return otr; + } + + public OtrSessionManager getOtrSM() { + return otrSM; + } + + public void startOTR(String xid) { + String account = config.getUsername() + "@" + config.getHostString() + "/IM5"; + SessionID sid = new SessionID(account, xid, "xmpp"); + + Session session = new SessionImpl(sid, otr); + String[] outgoingMessage; + try { + session.startSession(); + } catch (OtrException ex) { + Logger.getLogger(Xmpp.class.getName()).log(Level.SEVERE, null, ex); + } + } // Method to send a message (this will be called from another thread) @@ -42,12 +81,15 @@ } } + public void send(String to, String message) { + sendMessage(to, message, false); + } + private void connect() throws SmackException, IOException, XMPPException, InterruptedException { connection = new XMPPTCPConnection(config); connection.setUseStreamManagement(false); connection.connect(); connection.login(); - } public List getRosterItems() throws SmackException.NotLoggedInException, SmackException.NotConnectedException, InterruptedException { diff -r f3095cda599e -r 42d0d099492b src/main/java/de/unixwork/im/XmppSession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/unixwork/im/XmppSession.java Thu Dec 26 12:29:05 2024 +0100 @@ -0,0 +1,30 @@ +package de.unixwork.im; + +import net.java.otr4j.crypto.OtrCryptoEngine; +import net.java.otr4j.crypto.OtrCryptoEngineImpl; + +public class XmppSession { + private String xid; + private String resource; + + + private boolean isSecure = false; + + public XmppSession(String xid, String resource) { + this.xid = xid; + this.resource = resource; + } + + public void startOtr(Xmpp xmpp) { + + + } + + public void endOtr(Xmpp xmpp) { + + } + + public boolean isSecure() { + return isSecure; + } +}