[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Halftested] Add support to MMTP for connection padding,...



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv9624/lib/mixminion

Modified Files:
	MMTPClient.py test.py 
Log Message:
[Halftested] Add support to MMTP for connection padding, key renegotiation,
protocol negotiation.  Bump protocol version to 0.2, since older servers don't
receive padding correctly.


Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- MMTPClient.py	16 Dec 2002 02:40:11 -0000	1.14
+++ MMTPClient.py	12 Jan 2003 04:27:19 -0000	1.15
@@ -19,7 +19,7 @@
 
 import socket
 import mixminion._minionlib as _ml
-from mixminion.Crypto import sha1
+from mixminion.Crypto import sha1, getCommonPRNG
 from mixminion.Common import MixProtocolError, LOG
 
 class BlockingClientConnection:
@@ -34,6 +34,8 @@
     # context: a TLSContext object; used to create connections.
     # sock: a TCP socket, open to the server.
     # tls: a TLS socket, wrapping sock.
+    #DOCDOC protocol
+    PROTOCOL_VERSIONS = ['0.2', '0.1']
     def __init__(self, targetIP, targetPort, targetKeyID):
         """Open a new connection."""
         self.targetIP = targetIP
@@ -72,31 +74,53 @@
         # change our mind between now and a release candidate, and so we
         # can obsolete betas come release time.
         LOG.debug("Negotiatiating MMTP protocol")
-        self.tls.write("MMTP 0.1\r\n")
-        inp = self.tls.read(len("MMTP 0.1\r\n"))
-        if inp != "MMTP 0.1\r\n":
+        self.tls.write("MMTP %s\r\n" % ",".join(self.PROTOCOL_VERSIONS))
+        # This is ugly, but we have no choice if we want to read up to the
+        # first newline.
+        # we don't really want 100; we just want up to the newline.
+        inp = self.tls.read(100) 
+        while "\n" not in inp and len(inp) < 100:
+            inp += self.tls.read(100)
+        self.protocol = None
+        for p in self.PROTOCOL_VERSIONS:
+            if inp == 'MMTP %s\r\n'%p:
+                self.protocol = p
+                break
+        if not self.protocol:
             raise MixProtocolError("Protocol negotiation failed")
-        LOG.debug("MMTP protocol negotated: version 0.1")
+        LOG.debug("MMTP protocol negotated: version %s", self.protocol)
 
-    def sendPacket(self, packet):
+    def renegotiate(self):
+        self.tls.renegotiate()
+        self.tls.do_handshake()
+
+    def sendPacket(self, packet,
+                   control="SEND\r\n", serverControl="RECEIVED\r\n",
+                   hashExtra="SEND",serverHashExtra="RECEIVED"):
         """Send a single packet to a server."""
         assert len(packet) == 1<<15
         LOG.debug("Sending packet")
         ##
         # We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
-        self.tls.write("SEND\r\n")
+        self.tls.write(control)
         self.tls.write(packet)
-        self.tls.write(sha1(packet+"SEND"))
+        self.tls.write(sha1(packet+hashExtra))
         LOG.debug("Packet sent; waiting for ACK")
 
         # And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
-        inp = self.tls.read(len("RECEIVED\r\n")+20)
-        if inp != "RECEIVED\r\n"+sha1(packet+"RECEIVED"):
+        inp = self.tls.read(len(serverControl)+20)
+        if inp != serverControl+sha1(packet+serverHashExtra):
             raise MixProtocolError("Bad ACK received")
         LOG.debug("ACK received; packet successfully delivered")
 
-    # FFFF we need a sendJunkPacket method.
-
+    def sendJunkPacket(self, packet):
+        if self.protocol == '0.1':
+            LOG.debug("Not sending junk to a v0.1 server")
+            return
+        self.sendPacket(packet,
+                        control="JUNK\r\n", serverControl="RECEIVED\r\n",
+                        hashExtra="JUNK", serverHashExtra="RECEIVED JUNK")
+        
     def shutdown(self):
         """Close this connection."""
         LOG.debug("Shutting down connection to %s:%s",
@@ -108,11 +132,29 @@
         LOG.debug("Connection closed")
 
 def sendMessages(targetIP, targetPort, targetKeyID, packetList):
-    """Sends a list of messages to a server."""
+    """Sends a list of messages to a server.
+        DOCDOC arguments
+        DOCDOC "JUNK", "RENEGOTIATE"
+    """
+    # Generate junk before opening connection to avoid timing attacks
+    packets = []
+    for p in packetList:
+        if p == 'JUNK':
+            packets.append(("JUNK", getCommonPRNG().getBytes(1<<15)))
+        elif p == 'RENEGOTIATE':
+            packets.append(("RENEGOTIATE", None))
+        else:
+            packets.append(("MSG", p))
+    
     con = BlockingClientConnection(targetIP, targetPort, targetKeyID)
     try:
         con.connect()
-        for p in packetList:
-            con.sendPacket(p)
+        for t,p in packets:
+            if t == "JUNK":
+                con.sendJunkPacket(p)
+            elif t == "RENEGOTIATE":
+                con.renegotiate()
+            else:
+                con.sendPacket(p)
     finally:
         con.shutdown()

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.71
retrieving revision 1.72
diff -u -d -r1.71 -r1.72
--- test.py	10 Jan 2003 20:12:05 -0000	1.71
+++ test.py	12 Jan 2003 04:27:19 -0000	1.72
@@ -2352,7 +2352,6 @@
         # (Fails less than once in 2 **30 tests.)
         self.assert_(messageLens[0] <= 30)
         self.assert_(messageLens[-1] >= 30)
-        #print messageLens
 
         bcmq.removeAll()
         bcmq.cleanQueue()
@@ -2644,7 +2643,6 @@
             server.process(0.1)
         t.join()
 
-
     def _testNonblockingTransmission(self):
         server, listener, messagesIn, keyid = _getMMTPServer()
         self.listener = listener
@@ -2654,7 +2652,8 @@
         messages = ["helloxxx"*4096, "helloyyy"*4096]
         async = mixminion.server.MMTPServer.AsyncServer()
         clientcon = mixminion.server.MMTPServer.MMTPClientConnection(
-           _getTLSContext(0), "127.0.0.1", TEST_PORT, keyid, messages[:],
+           _getTLSContext(0), "127.0.0.1", TEST_PORT, keyid,
+           messages[:]+["JUNK","RENEGOTIATE","JUNK"],
            [None, None], None)
         clientcon.register(async)
         def clientThread(clientcon=clientcon, async=async):
@@ -2710,43 +2709,33 @@
         self.listener = listener
         self.server = server
 
-        # This is a little tricky.  We want to test connection timeouts, so we
-        # concoct a fake list object that blocks before returning its second
-        # element so that we can make MMTPClient.sendMessages pause for a
-        # while.
-        class SlowMessageList:
-            def __init__(self):
-                self.pausing = 50
-            def __getitem__(self, i):
-                if i == 0:
-                    return "helloxxx"*4096
-                elif i == 1:
-                    # We use a counter here so that we can make the thread
-                    # holding this list end quickly when we want it to.
-                    while self.pausing > 0:
-                        time.sleep(0.1)
-                        self.pausing -= 0.1
-                    return "helloyyy"*4096
-                else:
-                    raise IndexError
-
         # This function wraps MMTPClient.sendMessages, but catches exceptions.
         # Since we're going to run this function in a thread, we pass the
         # exception back through a list argument.
-        def sendAndCaptureException(lst, *args):
+        def sendSlowlyAndCaptureException(exlst, pausing, targetIP, targetPort,
+                                          targetKeyID, msgFast, msgSlow):
             try:
-                mixminion.MMTPClient.sendMessages(*args)
+                con = mixminion.MMTPClient.BlockingClientConnection(
+                    targetIP,targetPort,targetKeyID)
+                con.connect()
+                con.sendPacket(msgFast)
+                while pausing[0] > 0:
+                    time.sleep(.1)
+                    pausing[0] -= .1
+                con.sendPacket(msgSlow)
+                con.close()
             except:
-                lst.append(sys.exc_info())
+                exlst.append(sys.exc_info())
 
         # Manually set the server's timeout threshold to 600 msec.
         server._timeout = 0.6
         server.process(0.1)
         excList = []
-        msgList = SlowMessageList()
+        pausing = [10]
         t = threading.Thread(None,
-              sendAndCaptureException,
-              args=(excList, "127.0.0.1", TEST_PORT, keyid, msgList))
+              sendSlowlyAndCaptureException,
+              args=(excList, pausing, "127.0.0.1", TEST_PORT, keyid,
+                    "helloxxx"*4096, "helloyyy"*4096))
         t.start()
         timedOut = 0 # flag: have we really timed out?
         try:
@@ -2764,14 +2753,14 @@
             self.assert_(timedOut)
         finally:
             logMessage = resumeLog()
-            # Did we log the timeout?
-            self.assert_(stringContains(logMessage, "timed out"))
+        # Did we log the timeout?
+        self.assert_(stringContains(logMessage, "timed out"))#XXXX
         # Was the one message we expected in fact transmitted?
         self.assertEquals([messagesIn[0]], ["helloxxx"*4096])
 
         # Now stop the transmitting thread.  It will notice that its
         # connection has been forcibly closed.
-        msgList.pausing = 0
+        pausing[0] = 0
         t.join()
         # Was an exception raised?
         self.assertEquals(1, len(excList))
@@ -4030,7 +4019,7 @@
             self.assert_(not os.path.exists(os.path.join(dir, "2")))
         finally:
             m = resumeLog()
-            self.assert_(m.endswith("Unable to deliver message\n"))
+        self.assert_(m.endswith("Unable to deliver message\n"))
 
         try:
             suspendLog()
@@ -4071,7 +4060,7 @@
             queue.sendReadyMessages()
         finally:
             m = resumeLog()
-            self.assert_(m.endswith("[ERROR] Unable to deliver message\n"))
+        self.assert_(m.endswith("[ERROR] Unable to deliver message\n"))
         # After delivery: 91 and 92 go through, 93 stays, and 94 gets dropped.
         self.assertEquals(1, queue.count())
         self.assertEquals(5, len(os.listdir(dir)))
@@ -4304,7 +4293,6 @@
             conf.getModuleManager().configure(conf)
         finally:
             resumeLog()
-            pass
 
         # Now, for each starting time, generate a server desciprtor.
         _EXAMPLE_DESCRIPTORS[nickname] = []
@@ -4413,7 +4401,7 @@
             self.assert_(ks.getServerInfo(si) is None)
         finally:
             s = resumeLog()
-            self.assert_(stringContains(s, "Server is not currently"))
+        self.assert_(stringContains(s, "Server is not currently"))
 
         ##
         # Now try out the directory.  This is tricky; we add the other
@@ -4860,7 +4848,7 @@
         ##  Test generateForwardMessage.
         # We replace 'buildForwardMessage' to make this easier to test.
         replaceFunction(mixminion.BuildMessage, "buildForwardMessage",
-                        lambda *a:"X")
+                        lambda *a, **k:"X")
         try:
             getCalls = getReplacedFunctionCallLog
             clearCalls = clearReplacedFunctionCallLog
@@ -4980,8 +4968,7 @@
     tc = loader.loadTestsFromTestCase
 
     if 0:
-        suite.addTest(tc(ClientMainTests))
-        suite.addTest(tc(ModuleTests))
+        suite.addTest(tc(MMTPTests))
         return suite
 
     suite.addTest(tc(MiscTests))