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

[minion-cvs] HACKING: add note on magic comments



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv10387/lib/mixminion

Modified Files:
	BuildMessage.py Common.py Crypto.py HashLog.py MMTPClient.py 
	MMTPServer.py Packet.py PacketHandler.py Queue.py benchmark.py 
	test.py 
Log Message:
HACKING: add note on magic comments

Packet/PacketHandler/BuildMessage/test: 
	Implement Reply Block format recently added to spec.

Common:
	Improve documentation on secure deleting

MMTPServer/client:
	Add stubs for keyid checking.  Now blocking on spec for how to
	hash a public key.

tls.c:
	Fix mm_TLSSock_check -> mm_TLSSock_Check.

Everywhere else:
	- Fix code style according to PEP-0008 guidelines
	- Improve doc style to be closer to PEP-0257 guidelines
	- General cleanups and removal of dead code.


Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- BuildMessage.py	24 Jun 2002 20:28:19 -0000	1.6
+++ BuildMessage.py	25 Jun 2002 11:41:07 -0000	1.7
@@ -5,62 +5,56 @@
 
    Code to construct messages and reply blocks."""
 
+import operator
 from mixminion.Packet import *
 from mixminion.Common import MixError
 import mixminion.Crypto as Crypto
 import mixminion.Modules as Modules
-import operator
 
 __all__ = [ 'buildForwardMessage', 'buildReplyBlock', 'buildReplyMessage',
             'buildStatelessReplyBlock' ]
 
 def buildForwardMessage(payload, exitType, exitInfo, path1, path2):
-    """buildForwardMessage(payload, exitType, exitInfo, path1, path2) ->str
-
-       Constructs a forward message.
+    """Construct a forward message.
             payload: The payload to deliver.
             exitType: The routing type for the final node
             exitType: The routing info for the final node
             path1: Sequence of ServerInfo objects for the first leg of the path
             path1: Sequence of ServerInfo objects for the 2nd leg of the path
-        """
+    """
     return _buildMessage(payload, exitType, exitInfo, path1, path2)
 
 def buildReplyMessage(payload, path1, replyBlock):
-    """buildReplyMessage(payload, path1, replyBlock) ->str
-
-       Builds a message using a reply block.  'path1' is a sequence of
-       ServerInfo for the nodes on the first leg of the path."""
+    """Build a message using a reply block.  'path1' is a sequence of
+       ServerInfo for the nodes on the first leg of the path.
+    """
     return _buildMessage(payload, None, None,
-                         path1=path1,
-                         reply=replyBlock)
-
-def buildReplyBlock(path, exitType, exitInfo, secretPRNG=None):
-    """buildReplyBlock(path, exitType, exitInfo, secretPRNG=None) 
-                                                  -> (Reply block, secret list)
+                         path1=path1, path2=replyBlock)
 
-       Returns a newly-constructed reply block and a list of secrets used
+def buildReplyBlock(path, exitType, exitInfo, expiryTime=0, secretPRNG=None):
+    """Return a newly-constructed reply block and a list of secrets used
        to make it.
        
               path: A list of ServerInfo
               exitType: Routing type to use for the final node
               exitInfo: Routing info for the final node
+              expiryTime: The time at which this block should expirt.
               secretPRNG: A PRNG to use for generating secrets.  If not
                  provided, uses an AES counter-mode stream seeded from our
                  entropy source.
        """
-    if secretPRNG == None:
+    if secretPRNG is None:
         secretPRNG = Crypto.AESCounterPRNG()
     secrets = [ secretPRNG.getBytes(SECRET_LEN) for _ in path ]
     header = _buildHeader(path, secrets, exitType, exitInfo, 
                           paddingPRNG=Crypto.AESCounterPRNG())
-    return ReplyBlock(header, path[0]), secrets
+    return ReplyBlock(header, expiryTime,
+                      Modules.SWAP_FWD_TYPE,
+                      path[0].getRoutingInfo().pack()), secrets
 
 # Maybe we shouldn't even allow this to be called with userKey==None.
-def buildStatelessReplyBlock(path, user, userKey, email=0):
-    """buildStatelessReplyBlock(path, user, userKey, email=0) -> ReplyBlock
-
-       Constructs a 'stateless' reply block that does not require the
+def buildStatelessReplyBlock(path, user, userKey, email=0, expiryTime=0):
+    """Construct a 'stateless' reply block that does not require the
        reply-message recipient to remember a list of secrets.
        Instead, all secrets are generated from an AES counter-mode
        stream, and the seed for the stream is stored in the 'tag'
@@ -78,6 +72,7 @@
                   userKey: an AES key to encrypt the seed, or None.
                   email: If true, delivers via SMTP; else delivers via LOCAL.
        """
+    #XXXX Out of sync with the spec.
     if email and userKey:
         raise MixError("Requested EMail delivery without password-protection")
 
@@ -95,15 +90,12 @@
         exitInfo = LocalInfo(user, "RTRN"+tag).pack()
 
     prng = Crypto.AESCounterPRNG(seed)
-    return buildReplyBlock(path, exitType, exitInfo, prng)[0]
+    return buildReplyBlock(path, exitType, exitInfo, expiryTime, prng)[0]
 
 #----------------------------------------------------------------------
 def _buildMessage(payload, exitType, exitInfo,
-                  path1, path2=None, reply=None, paddingPRNG=None, paranoia=0):
-    """_buildMessage(payload, exitType, exitInfo, path1, path2=None,
-                     reply=None, paddingPRNG=None, paranoia=0) -> str
-    
-    Helper method to create a message.
+                  path1, path2, paddingPRNG=None, paranoia=0):
+    """Helper method to create a message.
 
     The following fields must be set:
        payload: the intended exit payload.
@@ -113,11 +105,11 @@
           on the first leg of the path.
 
     The following fields must be set for a forward message:
-       path2: a sequence of ServerInfo objects, one for each of the nodes
-          on the second leg of the path.
-
-    The following fields must be set for a reply message:
-       reply: a ReplyBlock object
+       path2: EITHER
+             a sequence of ServerInfo objects, one for each of the nodes
+             on the second leg of the path.
+         OR
+             a replyBlock object.
 
     The following fields are optional:
        paddingPRNG: A pseudo-random number generator used to pad the headers
@@ -127,13 +119,15 @@
          header secrets too.  Otherwise, we read all of our header secrets
          from the true entropy source. 
     """
-    assert path2 or reply
-    assert not (path2 and reply)
+    reply = None
+    if isinstance(path2, ReplyBlock):
+        reply = path2
+        path2 = None
 
     ### SETUP CODE: let's handle all the variant cases.
 
     # Set up the random number generators.
-    if paddingPRNG == None:
+    if paddingPRNG is None:
         paddingPRNG = Crypto.AESCounterPRNG()
     if paranoia:
         nHops = len(path1)
@@ -144,8 +138,10 @@
 
     # Determine exit routing for path1 and path2.
     if reply:
-        path1exitinfo = reply.addr.getRoutingInfo().pack()
+        path1exittype = reply.routingType
+        path1exitinfo = reply.routingInfo
     else:
+        path1exittype = Modules.SWAP_FWD_TYPE
         path1exitinfo = path2[0].getRoutingInfo().pack()
 
     # Pad the payload, as needed.
@@ -166,21 +162,20 @@
         header2 = reply.header
 
     # Construct header1.
-    header1 = _buildHeader(path1,secrets1,Modules.SWAP_FWD_TYPE,path1exitinfo,
+    header1 = _buildHeader(path1,secrets1,path1exittype,path1exitinfo,
                            paddingPRNG)
 
     return _constructMessage(secrets1, secrets2, header1, header2, payload)
 
 def _buildHeader(path,secrets,exitType,exitInfo,paddingPRNG):
-    """_buildHeader(path, secrets, exitType, exitInfo, paddingPRNG) -> str
-
-       Helper method to construct a single header.
+    """Helper method to construct a single header.
            path: A sequence of serverinfo objects.
            secrets: A list of 16-byte strings to use as master-secrets for
                each of the subeaders.
            exitType: The routing for the last node in the header
            exitInfo: The routing info for the last node in the header
-           paddingPRNG: A pseudo-random number generator to generate padding"""
+           paddingPRNG: A pseudo-random number generator to generate padding
+    """
 
     assert len(path) == len(secrets)
     if len(path) * ENC_SUBHEADER_LEN > HEADER_LEN:
@@ -189,10 +184,10 @@
     # Construct a list 'routing' of exitType, exitInfo.  
     routing = [ (Modules.FWD_TYPE, node.getRoutingInfo().pack()) for
                 node in path[1:] ]
-    routing.append( (exitType, exitInfo) )
+    routing.append((exitType, exitInfo))
     
     # sizes[i] is size, in blocks, of subheaders for i.
-    sizes =[ getTotalBlocksForRoutingInfoLen(len(info)) for t, info in routing]
+    sizes =[ getTotalBlocksForRoutingInfoLen(len(ri)) for _, ri in routing]
     
     # totalSize is number total number of blocks.
     totalSize = reduce(operator.add, sizes)
@@ -207,7 +202,7 @@
     #   junkSeen[i]==the junk that node i will see, before it does any
     #                encryption.   Note that junkSeen[0]=="", because node 0
     #                sees no junk.
-    junkSeen = [ "" ]
+    junkSeen = [""]
     for secret, headerKey, size in zip(secrets, headerKeys, sizes):
         # Here we're calculating the junk that node i+1 will see.
         #
@@ -256,10 +251,10 @@
     """Helper method: Builds a message, given both headers, all known
        secrets, and the padded payload.
 
-       If using a reply block, secrets2 should be null."""
+       If using a reply block, secrets2 should be null.
+    """
     assert len(payload) == PAYLOAD_LEN
     assert len(header1) == len(header2) == HEADER_LEN
-
     
     if secrets2:
         # (Copy secrets2 so we don't reverse the original)

Index: Common.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Common.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- Common.py	24 Jun 2002 20:28:19 -0000	1.4
+++ Common.py	25 Jun 2002 11:41:08 -0000	1.5
@@ -9,7 +9,10 @@
             'installSignalHandlers', 'secureDelete', 'secureRename',
             'ceilDiv', 'floorDiv', 'log', 'debug' ]
 
-import os, signal, sys
+import os
+import signal
+import sys
+from types import StringType
 
 class MixError(Exception):
     """Base exception class for all Mixminion errors"""
@@ -38,11 +41,12 @@
 # Python 3.0 is off in the distant future, but I like to plan ahead.
 
 def floorDiv(a,b):
-    "Computes floor(a / b). See comments for portability notes."
+    "Compute floor(a / b). See comments for portability notes."
     return divmod(a,b)[0]
 
+
 def ceilDiv(a,b):
-    "Computes ceil(a / b). See comments for portability notes."
+    "Compute ceil(a / b). See comments for portability notes."
     return divmod(a-1,b)[0]+1
 
 #----------------------------------------------------------------------
@@ -52,12 +56,20 @@
 _SHRED_CMD = "/usr/bin/shred"
 
 def secureDelete(fnames, blocking=0):
-    """ Given a list of filenames, removes the contents of all of those files,
-        from the disk, 'securely'.  If blocking=1, does not return until the
-        remove is complete.  If blocking=0, returns immediately, and returns
-        the PID of the process removing the files.  (Returns None if this
-        process unlinked the files itself) XXXX Clarify this."""
-    if type(fnames) == type(""):
+    """Given a list of filenames, removes the contents of all of those
+       files, from the disk, 'securely'.  If blocking=1, does not
+       return until the remove is complete.  If blocking=0, returns
+       immediately, and returns the PID of the process removing the
+       files.  (Returns None if this process unlinked the files
+       itself) XXXX Clarify this.
+
+       XXXX Securely deleting files only does so much good.  Metadata on
+       XXXX the file system, such as atime and dtime, can still be used
+       XXXX to reconstruct information about message timings.  To be
+       XXXX really safe, we should use a loopback device and shred _that_
+       XXXX from time to time.
+    """
+    if isinstance(fnames, StringType):
         fnames = [fnames]
     if blocking:
         mode = os.P_WAIT
@@ -77,8 +89,6 @@
 # XXXX Placeholder for a real logging mechanism
 def log(s):
     print s
-def debug(s):
-    print s
 
 #----------------------------------------------------------------------
 # Signal handling
@@ -90,17 +100,19 @@
 terminateHooks = []
 
 def onReset(fn):
-    """Given a 0-argument function fn, causes fn to be invoked when
+    """Given a 0-argument function fn, cause fn to be invoked when
        this process next receives a SIGHUP."""
     resetHooks.append(fn)
 
+
 def onTerminate(fn):
-    """Given a 0-argument function fn, causes fn to be invoked when
+    """Given a 0-argument function fn, cause fn to be invoked when
        this process next receives a SIGTERM."""
     terminateHooks.append(fn)
 
+
 def waitForChildren():
-    """Waits until all subprocesses have finished.  Useful for testing.""" 
+    """Wait until all subprocesses have finished.  Useful for testing.""" 
     while 1:
         try:
             # FFFF This won't work on Windows.  What to do?
@@ -108,12 +120,13 @@
         except:
             break
 
+
 def _sigChldHandler(signal_num, _):
     '''(Signal handler for SIGCHLD)'''
     # Because of the peculiarities of Python's signal handling logic, I
     # believe we need to re-register ourself.
-    signal.signal(signal.SIGCHLD, _sigChldHandler)
-
+    signal.signal(signal_num, _sigChldHandler)
+    
     while 1:
         try:
             # This waitpid call won't work on Windows.  What to do?
@@ -126,7 +139,8 @@
     #outcome, core, sig = status & 0xff00, status & 0x0080, status & 0x7f
     # FFFF Log if outcome wasn't as expected.
 
-def _sigHandler(signal_num, frame):
+
+def _sigHandler(signal_num, _):
     '''(Signal handler for SIGTERM and SIGHUP)'''
     signal.signal(signal_num, _sigHandler)
     if signal_num == signal.SIGTERM:
@@ -137,8 +151,9 @@
         for hook in resetHooks:
             hook()
 
+
 def installSignalHandlers(child=1,hup=1,term=1):
-    '''Registers signal handlers for this process.  If 'child', registers
+    '''Register signal handlers for this process.  If 'child', registers
        a handler for SIGCHLD.  If 'hup', registers a handler for SIGHUP.
        If 'term', registes a handler for SIGTERM.'''
     if child:

Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- Crypto.py	24 Jun 2002 20:28:19 -0000	1.7
+++ Crypto.py	25 Jun 2002 11:41:08 -0000	1.8
@@ -8,6 +8,8 @@
    the functions in mixminion.Crypto, and not call _minionlib's crypto
    functionality themselves."""
 
+from types import StringType
+
 import mixminion._minionlib as _ml
 from mixminion.Common import MixError, MixFatalError, floorDiv, ceilDiv
 
@@ -56,13 +58,13 @@
     """Given a string s and a 16-byte key key, computes the AES counter-mode
        encryption of s using k.  The counter begins at idx.
     """
-    if type(key) == type(""):
+    if isinstance(key, StringType):
         key = _ml.aes_key(key)
     return _ml.aes_ctr128_crypt(key,s,idx)
 
 def prng(key,count,idx=0):
     """Returns the bytestream 0x00000000...., encrypted in counter mode."""
-    if type(key) == type(""):
+    if isinstance(key, StringType):
         key = _ml.aes_key(key)
     return _ml.aes_ctr128_crypt(key,"",idx,count)
 
@@ -71,8 +73,8 @@
        s using the LIONESS super-pseudorandom permutation.
     """
 
-    assert len(key1)==len(key3)==DIGEST_LEN
-    assert len(key2)==len(key4)==DIGEST_LEN
+    assert len(key1) == len(key3) == DIGEST_LEN
+    assert len(key2) == len(key4) == DIGEST_LEN
     assert len(s) > DIGEST_LEN
 
     left = s[:DIGEST_LEN]
@@ -152,15 +154,11 @@
     return _ml.rsa_make_public_key(long(n),long(e))
 
 def pk_encode_private_key(key):
-    """pk_encode_private_key(rsa)->str
-
-       Creates an ASN1 representation of a keypair for external storage."""
+    """Creates an ASN1 representation of a keypair for external storage."""
     return key.encode_key(0)
 
 def pk_decode_private_key(s):
-    """pk_encode_private_key(str)->rsa
-
-       Reads an ASN1 representation of a keypair from external storage."""
+    """Reads an ASN1 representation of a keypair from external storage."""
     return _ml.rsa_decode_key(s,0)
 
 #----------------------------------------------------------------------
@@ -202,8 +200,8 @@
 
        If rng is None, uses the general purpose RNG.  The parameter may
        be any length.  len(data) must be <= bytes-42.  '''
-    if rng==None:
-        rng=getCommonPRNG()
+    if rng is None:
+        rng = getCommonPRNG()
     bytes = bytes-1
     mLen = len(data)
     paddingLen = bytes-mLen-2*DIGEST_LEN-1
@@ -242,7 +240,7 @@
             pass
         else:
             raise CryptoError("Decoding error")
-    if m == None:
+    if m is None:
         raise CryptoError("Decoding error")
     return m
 
@@ -291,12 +289,12 @@
     def get(self, mode, bytes=AES_KEY_LEN):
         """Creates a new key from the master secret, using the first <bytes>
            bytes of SHA1(master||mode)."""
-        assert 0<bytes<=DIGEST_LEN
+        assert 0 < bytes <= DIGEST_LEN
         return sha1(self.master+mode)[:bytes]
     def getLionessKeys(self, mode):
         """Returns a set of 4 lioness keys, as described in the Mixminion
            specification."""
-        z19="\x00"*19
+        z19 = "\x00"*19
         key1 = sha1(self.master+mode)
         key2 = _ml.strxor(sha1(self.master+mode), z19+"\x01")
         key3 = _ml.strxor(sha1(self.master+mode), z19+"\x02")
@@ -322,8 +320,8 @@
     def __init__(self, chunksize):
         """Initializes a RNG.  Bytes will be fetched from _prng by 'chunkSize'
            bytes at a time."""
-        self.bytes=""
-        self.chunksize=chunksize
+        self.bytes = ""
+        self.chunksize = chunksize
     def getBytes(self, n):
         """Returns a string of 'n' random bytes."""
 
@@ -333,11 +331,11 @@
             nMore = n+self.chunksize-len(self.bytes)
             morebytes = self._prng(nMore)
             res = self.bytes+morebytes[:n-len(self.bytes)]
-            self.bytes=morebytes[n-len(self.bytes):]
+            self.bytes = morebytes[n-len(self.bytes):]
             return res
         else:
             res = self.bytes[:n]
-            self.bytes=self.bytes[n:]
+            self.bytes = self.bytes[n:]
             return res
 
     def getInt(self, max):
@@ -387,14 +385,14 @@
            is specified, gets one from the true random number generator."""
         RNG.__init__(self, 16*1024)
         self.counter = 0
-        if seed == None:
+        if seed is None:
             seed = trng(AES_KEY_LEN)
         self.key = aes_key(seed)
         
     def _prng(self, n):
         """Implementation: uses the AES counter stream to generate entropy."""
         c = self.counter
-        self.counter+=n
+        self.counter += n
         # On python2.0, we overflow and wrap around.
         if (self.counter < c) or (self.counter >> 32):
             raise MixFatalError("Exhausted period of PRNG.")
@@ -404,7 +402,7 @@
 def getCommonPRNG():
     '''Returns a general-use AESCounterPRNG, initializing it if necessary.'''
     global _theSharedPRNG
-    if _theSharedPRNG == None:
+    if _theSharedPRNG is None:
         _theSharedPRNG = AESCounterPRNG()
     return _theSharedPRNG
 

Index: HashLog.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/HashLog.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- HashLog.py	24 Jun 2002 20:28:19 -0000	1.4
+++ HashLog.py	25 Jun 2002 11:41:08 -0000	1.5
@@ -30,9 +30,7 @@
        The base HashLog implementation assumes an 8-bit-clean database that
        maps strings to strings."""
     def __init__(self, filename, keyid):
-        """HashLog(filename, keyid) -> hashlog
-
-           Creates a new HashLog to store data in 'filename' for the key
+        """Create a new HashLog to store data in 'filename' for the key
            'keyid'."""
         self.log = anydbm.open(filename, 'c')
         #FFFF Warn if we're using dumbdbm
@@ -43,9 +41,7 @@
             self.log["KEYID"] = keyid
 
     def seenHash(self, hash):
-        """seenHash(hash) -> bool
-
-           Returns true iff 'hash' has been logged before."""
+        """Return true iff 'hash' has been logged before."""
         try:
             _ = self.log[hash]
             return 1
@@ -53,22 +49,16 @@
             return 0
 
     def logHash(self, hash):
-        """logHash(hash)
-
-           Inserts 'hash' into the database."""
+        """Insert 'hash' into the database."""
         self.log[hash] = "1"
 
     def sync(self):
-        """sync()
-
-           Flushes changes to this log to the filesystem."""
+        """Flushes changes to this log to the filesystem."""
         if hasattr(self.log, "sync"):
             self.log.sync()
 
     def close(self):
-        """close()
-
-           Closes this log."""
+        """Closes this log."""
         self.sync()
         self.log.close()
 

Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- MMTPClient.py	24 Jun 2002 20:28:19 -0000	1.1
+++ MMTPClient.py	25 Jun 2002 11:41:08 -0000	1.2
@@ -36,32 +36,34 @@
         self.sock.setblocking(1)
         self.sock.connect((self.targetIP,self.targetPort))
         
-        self.ssl = self.context.sock(self.sock.fileno())
+        self.tls = self.context.sock(self.sock.fileno())
         #XXXX session resumption
-        self.ssl.connect()
-        # XXXX CHECK KEY XXXX rsa = ssl.get_peer_cert_pk()
+        self.tls.connect()
+        peer_pk = self.tls.get_peer_cert_pk()
+        # XXXX Check the key ... how exactly is this to be hashed?
+        
         ####
         # Protocol negotiation
 
-        self.ssl.write("PROTOCOL 1.0\n")
-        inp = self.ssl.read(len("PROTOCOL 1.0\n"))
+        self.tls.write("PROTOCOL 1.0\n")
+        inp = self.tls.read(len("PROTOCOL 1.0\n"))
         if inp != "PROTOCOL 1.0\n":
             raise MixProtocolError("Protocol negotiation failed")
         
     def sendPacket(self, packet):
         """Send a single packet to a server."""
         assert len(packet) == 1<<15
-        self.ssl.write("SEND\n")
-        self.ssl.write(packet)
-        self.ssl.write(sha1(packet+"SEND"))
+        self.tls.write("SEND\n")
+        self.tls.write(packet)
+        self.tls.write(sha1(packet+"SEND"))
         
-        inp = self.ssl.read(len("RECEIVED\n")+20)
+        inp = self.tls.read(len("RECEIVED\n")+20)
         if inp != "RECEIVED\n"+sha1(packet+"RECEIVED"):
             raise MixProtocolError("Bad ACK received")
 
     def shutdown(self):
         """Close this connection."""
-        self.ssl.shutdown()
+        self.tls.shutdown()
         self.sock.close()
 
 def sendMessages(targetIP, targetPort, targetKeyID, packetList):
@@ -71,11 +73,3 @@
     for p in packetList:
         con.sendPacket(p)
     con.shutdown()
-
-# ----------------------------------------------------------------------
-# Old defunct testing code.  Will remove.
-
-## if __name__=='__main__':
-##     msg = "helloxxx"*4096
-##     assert len(msg) == (1<<15)
-##     sendMessages("127.0.0.1",9001,None,[msg])

Index: MMTPServer.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPServer.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- MMTPServer.py	24 Jun 2002 20:28:19 -0000	1.1
+++ MMTPServer.py	25 Jun 2002 11:41:08 -0000	1.2
@@ -12,7 +12,9 @@
    XXXX As yet unsupported are: Session resumption, key renegotiation,
    XXXX checking KeyID."""
 
-import socket, select, re
+import socket
+import select
+import re
 import mixminion._minionlib as _ml
 from types import StringType
 from mixminion.Common import MixError, MixFatalError, log
@@ -23,6 +25,7 @@
             'MMTPClientConnection' ]
 
 def debug(s):
+    '''placeholder; all calls should go away.'''
     #print s
     pass
 
@@ -37,8 +40,6 @@
         """Create a new AsyncServer with no readers or writers."""
         self.writers = {}
         self.readers = {}
-        ## Defunct code for poll-based implementation.
-        #self.p = select.poll()
 
     def process(self, timeout):
         """If any relevant file descriptors become available within
@@ -49,23 +50,6 @@
         debug("%s readers, %s writers" % (len(self.readers),
                                           len(self.writers)))
         
-### Defunct code for for poll-based implementation
-#          res = self.p.poll(timeout*1000)
-#          for fd, event in res:
-#              if event == select.POLLIN:
-#                  print "Got a read on", fd
-#                  self.readers[fd].handleRead()
-#              elif event == select.POLLOUT:
-#                  print "Got a write on", fd 
-#                  self.writers[fd].handleWrite()
-#              elif event == select.POLLNVAL:
-#                  #XXXX Should never happen
-#                  print "Bad FD: ",fd, "unregistered."
-#                  self.p.unregister(fd)
-#              else:
-#                  # XXXX Should never happen
-#                  print "????", fd,event
-
         readfds = self.readers.keys()
         writefds = self.writers.keys()
         readfds, writefds, exfds = select.select(readfds, writefds,[], timeout)
@@ -84,7 +68,6 @@
         """Register a connection as a reader.  The connection's 'handleRead'
            method will be called whenever data is available for reading."""
         fd = reader.fileno()
-        #self.p.register(fd, select.POLLIN)
         self.readers[fd] = reader
         if self.writers.has_key(fd):
             del self.writers[fd]
@@ -94,7 +77,6 @@
            method will be called whenever the buffer is free for writing.
         """
         fd = writer.fileno()
-        #self.p.register(fd, select.POLLOUT)
         self.writers[fd] = writer
         if self.readers.has_key(fd):
             del self.readers[fd]
@@ -215,7 +197,8 @@
             self.__state = self.__connectFn
 
     def isShutdown(self):
-        return self.__state == None
+        """Returns true iff this connection is finished shutting down"""
+        return self.__state is None
 
     def register(self, server):
         self.__server = server
@@ -258,18 +241,20 @@
         self.__server.registerWriter(self)
 
     def __acceptFn(self):
-        # may throw wantread, wantwrite.
-        self.__con.accept()
+        """Hook to implement server-side handshake."""
+        self.__con.accept() #may throw want*
         self.__server.unregister(self)
         self.finished()
 
     def __connectFn(self):
-        self.__con.connect()
+        """Hook to implement client-side handshake."""
+        self.__con.connect() #may throw want*
         self.__server.unregister(self)
         self.finished()
 
     def __shutdownFn(self):
-        r = self.__con.shutdown()
+        """Hook to implement shutdown."""
+        r = self.__con.shutdown() #may throw want*
         if r == 1:
             debug("Got a 1 on shutdown")
             self.__server.unregister(self)
@@ -280,14 +265,15 @@
             debug("Got a 0 on shutdown")
 
     def __readFn(self):
+        """Hook to implement read"""
         while 1:
-            r = self.__con.read(1024)
+            r = self.__con.read(1024) #may throw want*
             if r == 0:
                 debug("read returned 0.")
                 self.shutdown()
                 return
             else:
-                assert type(r) == StringType
+                assert isinstance(r, StringType)
                 debug("read got %s bytes" % len(r))
                 self.__inbuf.append(r)
                 self.__inbuflen += len(r)
@@ -295,30 +281,28 @@
                     break
 
         if self.__terminator and len(self.__inbuf) > 1:
-            self.__inbuf = [ "".join(self.__inbuf) ]
+            self.__inbuf = ["".join(self.__inbuf)]
 
         if self.__maxReadLen and self.__inbuflen > self.__maxReadLen:
             debug("Read got too much.")
             self.shutdown(err=1)
             return
          
-        if (self.__terminator and self.__inbuf[0].find(self.__terminator)>-1):
+        if self.__terminator and self.__inbuf[0].find(self.__terminator) > -1:
             debug("read found terminator")
             self.__server.unregister(self)
             self.finished()
 
-        if (self.__expectReadLen and 
-            (self.__inbuflen >= self.__expectReadLen)):
-            
+        if self.__expectReadLen and (self.__inbuflen >= self.__expectReadLen):
             debug("read got enough.")
             self.__server.unregister(self)
             self.finished()
 
     def __writeFn(self):
+        """Hook to implement write"""
         out = self.__outbuf
         while len(out):
-            # may throw
-            r = self.__con.write(out)
+            r = self.__con.write(out) # may throw
 
             if r == 0:
                 self.shutdown() #XXXX
@@ -337,22 +321,31 @@
         self.__handleAll()
 
     def __handleAll(self):
-          try:
-              while self.__state is not None:
-                  self.__state()
-          except _ml.TLSWantWrite:
-              self.__server.registerWriter(self)
-          except _ml.TLSWantRead:
-              self.__server.registerReader(self)
-          except _ml.TLSError:
-              if self.__state != self.__shutdownFn:
-                  debug("Unexpected error: closing connection.")
-                  self.shutdown(1)
-              else:
-                  debug("Error while shutting down: closing connection.")
-                  self.__server.unregister(self)
-          else:
-              self.__server.unregister(self)
+        """Underlying implementation of TLS connection: traverses as
+           many states as possible until some operation blocks on
+           reading or writing, or until the current __state becomes
+           None.
+        """
+        try:
+            # We have a while loop here so that, upon entering a new
+            # state, we immediately see if we can go anywhere with it
+            # without blocking.
+            while self.__state is not None:
+                self.__state()
+        except _ml.TLSWantWrite:
+            self.__server.registerWriter(self)
+        except _ml.TLSWantRead:
+            self.__server.registerReader(self)
+        except _ml.TLSError:
+            if self.__state != self.__shutdownFn:
+                debug("Unexpected error: closing connection.")
+                self.shutdown(1)
+            else:
+                debug("Error while shutting down: closing connection.")
+                self.__server.unregister(self)
+        else:
+            # We are in no state at all.
+            self.__server.unregister(self)
               
     def finished(self):
         """Called whenever a connect, accept, read, or write is finished."""
@@ -364,6 +357,7 @@
 
     def shutdown(self, err=0):
         """Begin a shutdown on this connection"""
+        
         self.__state = self.__shutdownFn
         #self.__server.registerWriter(self)
         
@@ -373,6 +367,9 @@
     def getInput(self):
         """Returns the current contents of the input buffer."""
         return "".join(self.__inbuf)
+
+    def getPeerPK(self):
+        return self.__con.get_peer_cert_pk()
     
 #----------------------------------------------------------------------
 # XXXX Need to support future protos.
@@ -452,11 +449,12 @@
 #----------------------------------------------------------------------
         
 class MMTPClientConnection(SimpleTLSConnection):
-    def __init__(self, context, ip, port, keyId, messageList,
+    def __init__(self, context, ip, port, keyID, messageList,
                  sentCallback=None):
         debug("CLIENT CON")
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.setblocking(0)
+        self.keyID = keyID
         try:
             sock.connect((ip, port))
         except socket.error:
@@ -471,14 +469,18 @@
         self.sentCallback = sentCallback
 
     def __setupFinished(self):
-        '''Called when we're done with the client side negotations.
-           Begins sending the protocol string.'''
+        """Called when we're done with the client side negotations.
+           Begins sending the protocol string.
+        """
+        peer_pk = self.getPeerPK()
+        # Check this!
         self.beginWrite(PROTOCOL_STRING)
         self.finished = self.__sentProtocol
 
     def __sentProtocol(self):    
-        '''Called when we're done sending the protocol string.  Begins
-           reading the server's response.'''
+        """Called when we're done sending the protocol string.  Begins
+           reading the server's response.
+        """
         self.expectRead(len(PROTOCOL_STRING), len(PROTOCOL_STRING))
         self.finished = self.__receivedProtocol
 
@@ -493,6 +495,7 @@
         self.beginNextMessage()
 
     def beginNextMessage(self):
+        """Start writing a message to the connection."""
         if not self.messageList:
             self.shutdown(0)
             return
@@ -533,46 +536,3 @@
 
        self.beginNextMessage()
 
-# ----------------------------------------------------------------------
-# Old defunct testing code.  Will remove 
-
-## if __name__=='__main__':
-##   import sys
-##   if len(sys.argv) == 1:
-##     d = "/home/nickm/src/ssl_sandbox/"
-##     for f in (d+"server.cert",d+"server.pk",d+"dh"):
-##         assert os.path.exists(f)
-##     context = _ml.TLSContext_new(d+"server.cert",d+"server.pk",d+"dh")
-
-##     _server = AsyncServer()
-##     def receiveMessage(pkt):
-##         print "Received packet beginning with %r" % pkt[:16]
-##     def conFactory(con,context=context,receiveMessage=receiveMessage):
-##         tls = context.sock(con)
-##         con.setblocking(0)
-##         return MMTPServerConnection(con, tls, receiveMessage)
-
-##     listener = ListenConnection("127.0.0.1", 9002, 5, conFactory)
-##     listener.register(_server)
-##     try:
-##         while 1:
-##             print "."
-##             _server.process(10)
-##     finally:
-##         listener.shutdown()
-##   else:
-##     context = _ml.TLSContext_new()
-##     _server = AsyncServer()
-##     def sentMessage():
-##         print "Done sending a message"
-##     msg = "helloxxx"*4096
-##     clientDone = 0
-##     def onSend():
-##         global clientDone
-##         clientDone = 1
-##     sender = MMTPClientConnection(context, "127.0.0.1", 9002, None,
-##                                   [msg], onSend)
-##     sender.register(_server)
-##     while 1 and not clientDone:
-##         print "."
-##         _server.process(10)

Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- Packet.py	24 Jun 2002 20:28:19 -0000	1.2
+++ Packet.py	25 Jun 2002 11:41:08 -0000	1.3
@@ -9,7 +9,8 @@
             'parseMessage', 'parseHeader', 'parseSubheader',
             'getTotalBlocksForRoutingInfoLen', 'ReplyBlock',
             'IPV4Info', 'SMTPInfo', 'LocalInfo', 'parseIPV4Info',
-            'parseSMTPInfo', 'parseLocalInfo', 'ENC_SUBHEADER_LEN',
+            'parseSMTPInfo', 'parseLocalInfo', 'ReplyBlock',
+            'parseReplyBlock', 'ENC_SUBHEADER_LEN',
             'HEADER_LEN', 'PAYLOAD_LEN', 'MAJOR_NO', 'MINOR_NO',
             'SECRET_LEN']
 
@@ -49,9 +50,7 @@
     pass
 
 def parseMessage(s):
-    """parseMessage(s) -> Message
-
-       Given a 32K string, returns a Message object that breaks it into
+    """Given a 32K string, returns a Message object that breaks it into
        two headers and a payload."""
     if len(s) != MESSAGE_LEN:
         raise ParseError("Bad message length")
@@ -65,21 +64,17 @@
 
        Fields: header1, header2, payload"""
     def __init__(self, header1, header2, payload):
-        """Message(header1, header2, payload) -> msg
-
-           Creates a new Message object from three strings."""
+        """Create a new Message object from three strings."""
         self.header1 = header1
         self.header2 = header2
         self.payload = payload
 
     def pack(self):
-        """Returns the 32K string value of this message."""
+        """Return the 32K string value of this message."""
         return "".join([self.header1,self.header2,self.payload])
 
 def parseHeader(s):
-    """parseHeader(s) -> Header
-
-       Converts a 2K string into a Header object"""
+    """Convert a 2K string into a Header object"""
     if len(s) != HEADER_LEN:
         raise ParseError("Bad header length")
 
@@ -88,6 +83,7 @@
 class Header:
     """Represents a 2K Mixminion header"""
     def __init__(self, contents):
+        """Initialize a new header from its contents"""
         self.contents = contents
 
     def __getitem__(self, i):
@@ -99,16 +95,17 @@
                              (i+1)*ENC_SUBHEADER_LEN]
 
     def __getslice__(self, i, j):
-        """header[i] -> str
+        """header[i:j] -> str
 
            Returns a slice of the i-j'th subheaders of this header."""
         if j > 16: j = 16
-        if i < 0: i=16+i
-        if j < 0: j=16+j
+        if i < 0: i += 16
+        if j < 0: j += 16 
         return self.contents[i*ENC_SUBHEADER_LEN:
                              j*ENC_SUBHEADER_LEN]
 
     def __len__(self):
+        """Return the number of subheaders in this header (always 16)"""
         return 16
 
 # A subheader begins with: a major byte, a minor byte, SECRET_LEN secret
@@ -117,9 +114,7 @@
 SH_UNPACK_PATTERN = "!BB%ds%dsHH" % (SECRET_LEN, DIGEST_LEN)
 
 def parseSubheader(s):
-    """parseSubheader(s) -> Subheader
-
-       Converts a decoded Mixminion subheader into a Subheader object"""
+    """Convert a decoded Mixminion subheader into a Subheader object"""
     if len(s) < MIN_SUBHEADER_LEN:
         raise ParseError("Header too short")
     if len(s) > MAX_SUBHEADER_LEN:
@@ -136,7 +131,7 @@
     return Subheader(major,minor,secret,digest,rt,ri,rlen)
 
 def getTotalBlocksForRoutingInfoLen(bytes):
-    """Returns the number of subheraders that will be needed for a hop
+    """Return the number of subheaders that will be needed for a hop
        whose routinginfo is (bytes) long."""
     if bytes <= MAX_ROUTING_INFO_LEN:
         return 1
@@ -157,11 +152,12 @@
 
     def __init__(self, major, minor, secret, digest, routingtype,
                  routinginfo, routinglen=None):
+        """Initialize a new subheader"""
         self.major = major
         self.minor = minor
         self.secret = secret
         self.digest = digest
-        if routinglen == None:
+        if routinglen is None:
             self.routinglen = len(routinginfo)
         else:
             self.routinglen = routinglen
@@ -175,25 +171,25 @@
                 "routinglen=%(routinglen)r)")% self.__dict__
 
     def setRoutingInfo(self, info):
-        """Changes the routinginfo, and the routinglength to correspond."""
+        """Change the routinginfo, and the routinglength to correspond."""
         self.routinginfo = info
         self.routinglen = len(info)
 
     def isExtended(self):
-        """Returns true iff the routinginfo is too long to fit in a single
+        """Return true iff the routinginfo is too long to fit in a single
            subheader."""
         return self.routinglen > MAX_ROUTING_INFO_LEN
 
     def getNExtraBlocks(self):
-        """Returns the number of extra blocks that will be needed to fit
+        """Return the number of extra blocks that will be needed to fit
            the routinginfo."""
         return getTotalBlocksForRoutingInfoLen(self.routinglen)-1
 
     def appendExtraBlocks(self, data):
-        """appendExtraBlocks(str)
-
-           Given additional (decoded) blocks of routing info, adds them
-           to the routinginfo of this object."""
+        """Given a string containing additional (decoded) blocks of
+           routing info, add them to the routinginfo of this
+           object.
+        """
         nBlocks = self.getNExtraBlocks()
         assert len(data) == nBlocks * ENC_SUBHEADER_LEN
         raw = [self.routinginfo]
@@ -203,7 +199,7 @@
         self.routinginfo = ("".join(raw))[:self.routinglen]
 
     def pack(self):
-        """Returns the (unencrypted) string representation of this Subhead.
+        """Return the (unencrypted) string representation of this Subhead.
 
            Does not include extra blocks"""
         assert self.routinglen == len(self.routinginfo)
@@ -216,9 +212,7 @@
                            self.routinglen, self.routingtype)+info
 
     def getExtraBlocks(self):
-        """getExtraBlocks() -> [ str, ...]
-
-           Returns a list of (unencrypted) blocks of extra routing info."""
+        """Return a list of (unencrypted) blocks of extra routing info."""
         if not self.isExtended():
             return []
         else:
@@ -234,17 +228,51 @@
             return result
 
 
-#FFFF We don't have an interchange format for these yet, so there's no way
-#FFFF to parse or unparse 'em.
+RB_UNPACK_PATTERN = "!4sBBL%ssHH" % (HEADER_LEN)
+MIN_RB_LEN = 14+HEADER_LEN
+
+def parseReplyBlock(s):
+    """Return a new ReplyBlock object for an encoded reply block"""
+    if len(s) < MIN_RB_LEN:
+        raise ParseError("Reply block too short")
+
+    try:
+        magic, major, minor, timestamp, header, rlen, rt = \
+               struct.unpack(RB_UNPACK_PATTERN, s[:MIN_RB_LEN])
+    except struct.error:
+        raise ParseError("Misformatted reply block")
+
+    if magic != 'SURB':
+        raise ParseError("Misformatted reply block")
+
+    if major != 0x01 or minor != 0x00:
+        raise ParseError("Unrecognized version on reply block")
+
+    ri = s[MIN_RB_LEN:]
+    if len(ri) != rlen:
+        raise ParseError("Misformatted reply block")
+
+    return ReplyBlock(header, timestamp, rt, ri)
+
 class ReplyBlock:
     """A mixminion reply block, including the address of the first hop
-       on the path, and a ServerInfo for the server."""
-    def __init__(self, header, addr):
+       on the path, and the RoutingType and RoutingInfo for the server."""
+    def __init__(self, header, useBy, rt, ri):
+        """Construct a new Reply Block."""
         self.header = header
-        self.addr = addr
+        self.timestamp = useBy
+        self.routingType = rt
+        self.routingInfo = ri
+
+    def pack(self):
+        """Returns the external representation of this reply block"""
+        return struct.pack(RB_UNPACK_PATTERN,
+                           "SURB", 0x01, 0x00, self.timestamp,
+                           self.header, len(self.routingInfo),
+                           self.routingType)+self.routingInfo
 
 def _packIP(s):
-    """Helper method: converts a dotted-decimal IPv4 address into a
+    """Helper method: convert a dotted-decimal IPv4 address into a
        four-byte encoding.  Raises ParseError if the input is not a
        dotted quad of 0..255"""
     addr = s.split(".")
@@ -259,7 +287,7 @@
     return struct.pack("!BBBB", *addr)
 
 def _unpackIP(s):
-    """Helper method: convers a four-byte IPv4 address into a dotted quad."""
+    """Helper method: convert a four-byte IPv4 address into a dotted quad."""
     if len(s) != 4: raise ParseError("Malformed IP")
     return ".".join(map(str, struct.unpack("!BBBB", s)))
 
@@ -268,9 +296,7 @@
 IPV4_PAT = "!4sH%ds" % DIGEST_LEN
 
 def parseIPV4Info(s):
-    """parseIP4VInfo(s) -> IPV4Info
-
-       Converts routing info for an IPV4 address into an IPV4Info object,
+    """Converts routing info for an IPV4 address into an IPV4Info object,
        suitable for use by FWD or SWAP_FWD modules."""
     if len(s) != 4+2+DIGEST_LEN:
         raise ParseError("IPV4 information with wrong length")
@@ -287,17 +313,19 @@
        Fields: ip (a dotted quad), port (an int from 0..65535), and keyinfo
        (a digest)."""
     def __init__(self, ip, port, keyinfo):
-        assert 0<=port<=65535
+        """Construct a new IPV4Info"""
+        assert 0 <= port <= 65535
         self.ip = ip
         self.port = port
         self.keyinfo = keyinfo
 
     def pack(self):
+        """Return the routing info for this address"""
         assert len(self.keyinfo) == DIGEST_LEN
         return struct.pack(IPV4_PAT, _packIP(self.ip), self.port, self.keyinfo)
 
 def parseSMTPInfo(s):
-    """Converts the encoding of an SMTP routinginfo into an SMTPInfo object."""
+    """Convert the encoding of an SMTP routinginfo into an SMTPInfo object."""
     lst = s.split("\000",1)
     if len(lst) == 1:
         return SMTPInfo(s,None)
@@ -313,13 +341,14 @@
         self.tag = tag
 
     def pack(self):
+        """Returns the wire representation of this SMTPInfo"""
         if self.tag != None:
             return self.email+"\000"+self.tag
         else:
             return self.email
 
 def parseLocalInfo(s):
-    """Converts the encoding of an LOCAL routinginfo into an LocalInfo
+    """Convert the encoding of an LOCAL routinginfo into an LocalInfo
        object."""
     lst = s.split("\000",1)
     if len(lst) == 1:
@@ -337,6 +366,7 @@
         self.tag = tag
 
     def pack(self):
+        """Return the external representation of this routing info."""
         if self.tag:
             return self.user+"\000"+self.tag
         else:

Index: PacketHandler.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/PacketHandler.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- PacketHandler.py	24 Jun 2002 20:28:19 -0000	1.2
+++ PacketHandler.py	25 Jun 2002 11:41:08 -0000	1.3
@@ -22,9 +22,7 @@
        an exist handler."""
     
     def __init__(self, privatekey, hashlog):
-        """PacketHandler(privatekey, hashlog)
-
-           Constructs a new packet handler, given a private key object for
+        """Constructs a new packet handler, given a private key object for
            header encryption, and a hashlog object to prevent replays.
 
            A sequence of private keys may be provided, if you'd like the
@@ -32,16 +30,17 @@
            though: this slows down the packet handler a lot.
         """
         # ???? Any way to support multiple keys in protocol?
-        if type(privatekey) in (type(()), type([])):
+        try:
+            # Check whether we have a key or a tuple of keys.
+            _ = privatekey[0]
             self.privatekey = privatekey
-        else:
+        except:
             self.privatekey = (privatekey, )
+
         self.hashlog = hashlog
 
     def processMessage(self, msg):
-        """ph.processMessage(msg)
-
-           Given a 32K mixminion message, processes it completely.
+        """Given a 32K mixminion message, processes it completely.
 
            Returns one of:
                     None [if the mesesage should be dropped.
@@ -64,17 +63,17 @@
         msg = Packet.parseMessage(msg)
         header1 = Packet.parseHeader(msg.header1)
 
-        # Try to decrypt the first subheader.
-        enc_subh = header1[0]
+        # Try to decrypt the first subheader.  Try each private key in
+        # order.  Only fail if all private keys fail.
         subh = None
-        err = None
+        e = None
         for pk in self.privatekey:
             try:
-                subh = Crypto.pk_decrypt(enc_subh, pk)
-            except Crypto.CryptoError, e:
-                err = e
+                subh = Crypto.pk_decrypt(header1[0], pk)
+            except Crypto.CryptoError, err:
+                e = err
         if not subh:
-            raise err
+            raise e
         subh = Packet.parseSubheader(subh)
 
         # Check the version: can we read it?
@@ -82,8 +81,7 @@
             raise ContentError("Invalid protocol version")
 
         # Check the digest: is it correct?
-        digest = Crypto.sha1(header1[1:])
-        if digest != subh.digest:
+        if subh.digest != Crypto.sha1(header1[1:]):
             raise ContentError("Invalid digest")
 
         # Get ready to generate message keys.

Index: Queue.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Queue.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- Queue.py	24 Jun 2002 20:28:19 -0000	1.1
+++ Queue.py	25 Jun 2002 11:41:08 -0000	1.2
@@ -6,7 +6,10 @@
    Facility for a fairly secure, directory-based, unordered queue.
    """
 
-import os, base64, time, stat
+import os
+import base64
+import time
+import stat
 
 from mixminion.Common import MixError, MixFatalError, secureDelete
 from mixminion.Crypto import AESCounterPRNG
@@ -97,7 +100,7 @@
         messages = [fn for fn in os.listdir(self.dir) if fn.startswith("msg_")]
 
         n = len(messages)
-        if count == None:
+        if count is None:
             count = n
         else:
             count = min(count, n)

Index: benchmark.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/benchmark.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- benchmark.py	24 Jun 2002 20:28:19 -0000	1.6
+++ benchmark.py	25 Jun 2002 11:41:08 -0000	1.7
@@ -35,7 +35,7 @@
     nones = [None]*iters
     if ov:
         overhead = loop_overhead.get(iters, None)
-        if overhead == None:
+        if overhead is None:
             overhead = loop_overhead[iters] = timeit_((
                 lambda:(lambda:None)()), iters, 0)
     else:
@@ -94,12 +94,12 @@
 
 def cryptoTiming():
     print "#==================== CRYPTO ======================="
-    print "SHA1 (short)", timeit((lambda : sha1(short)), 100000)
-    print "SHA1 (64b)", timeit((lambda : sha1(s64b)), 100000)
-    print "SHA1 (2K)", timeit((lambda : sha1(s2K)), 10000)
-    print "SHA1 (8K)", timeit((lambda : sha1(s8K)), 10000)
-    print "SHA1 (28K)", timeit((lambda : sha1(s28K)), 1000)
-    print "SHA1 (32K)", timeit((lambda : sha1(s32K)), 1000)
+    print "SHA1 (short)", timeit((lambda: sha1(short)), 100000)
+    print "SHA1 (64b)", timeit((lambda: sha1(s64b)), 100000)
+    print "SHA1 (2K)", timeit((lambda: sha1(s2K)), 10000)
+    print "SHA1 (8K)", timeit((lambda: sha1(s8K)), 10000)
+    print "SHA1 (28K)", timeit((lambda: sha1(s28K)), 1000)
+    print "SHA1 (32K)", timeit((lambda: sha1(s32K)), 1000)
 
     shakey = "8charstr"*2
     print "Keyed SHA1 for lioness (28K, unoptimized)", timeit(
@@ -147,11 +147,11 @@
 
     c = AESCounterPRNG()
     print "aesprng.getInt (10)", \
-          timeit((lambda c=c : c.getInt(10)), 10000)
+          timeit((lambda c=c: c.getInt(10)), 10000)
     print "aesprng.getInt (1000)", \
-          timeit((lambda c=c : c.getInt(1000)), 10000)
+          timeit((lambda c=c: c.getInt(1000)), 10000)
     print "aesprng.getInt (513)", \
-          timeit((lambda c=c : c.getInt(513)), 10000)
+          timeit((lambda c=c: c.getInt(513)), 10000)
 
     lkey = Keyset("keymaterial foo bar baz").getLionessKeys("T")
     print "lioness E (1K)", timeit((
@@ -216,7 +216,9 @@
     print "Timing overhead: %s...%s" % (timestr(min(o)),timestr(max(o)))
 
 #----------------------------------------------------------------------
-import tempfile, os, stat
+import tempfile
+import os
+import stat
 
 def hashlogTiming():
     print "#==================== HASH LOGS ======================="
@@ -242,20 +244,20 @@
     for hash in hashes:
         h.logHash(hash)
     t = time()-t
-    print "Add entry (up to %s entries)" %load, timestr( t/float(load) )
+    print "Add entry (up to %s entries)" %load, timestr(t/float(load))
 
     t = time()
     for hash in hashes[0:1000]:
         h.seenHash(hash)
     t = time()-t
-    print "Check entry [hit] (%s entries)" %load, timestr( t/1000.0 )
+    print "Check entry [hit] (%s entries)" %load, timestr(t/1000.0)
 
     hashes =[ prng.getBytes(20) for _ in xrange(1000) ]
     t = time()
     for hash in hashes:
         h.seenHash(hash)
     t = time()-t
-    print "Check entry [miss] (%s entries)" %load, timestr( t/1000.0 )
+    print "Check entry [miss] (%s entries)" %load, timestr(t/1000.0)
 
     hashes =[ prng.getBytes(20) for _ in xrange(1000) ]
     t = time()
@@ -263,7 +265,7 @@
         h.seenHash(hash)
         h.logHash(hash)
     t = time()-t
-    print "Check entry [miss+add] (%s entries)" %load, timestr( t/1000.0 )
+    print "Check entry [miss+add] (%s entries)" %load, timestr(t/1000.0)
 
     h.close()
     size = 0
@@ -283,15 +285,16 @@
     print "#================= BUILD MESSAGE ====================="
     pk = pk_generate()
     payload = ("Junky qoph flags vext crwd zimb."*1024)[:22*1024]
-    serverinfo = [ ServerInfo("127.0.0.1", 48099, pk_get_modulus(pk),
-                              "x"*20) ] * 16
+    serverinfo = [ServerInfo("127.0.0.1", 48099, pk_get_modulus(pk),"x"*20)
+                  ] * 16
+                             
     def bh(np,it, serverinfo=serverinfo):
         ctr = AESCounterPRNG()
 
         tm = timeit_(
               lambda np=np,it=it,serverinfo=serverinfo,ctr=ctr:
                          _buildHeader(serverinfo[:np], ["Z"*16]*np,
-                                        99, "Hello", ctr), it )
+                                        99, "Hello", ctr), it)
 
         print "Build header (%s)" %(np), timestr(tm)
 
@@ -335,13 +338,13 @@
                                    [server, server], [server, server])
 
     print "Server process (no swap, no log)", timeit(
-        lambda sp=sp, m_noswap=m_noswap : sp.processMessage(m_noswap), 100)
+        lambda sp=sp, m_noswap=m_noswap: sp.processMessage(m_noswap), 100)
 
     m_swap = buildForwardMessage("Hello world", SMTP_TYPE, "f@invalid",
                                  [server], [server, server])
 
     print "Server process (swap, no log)", timeit(
-        lambda sp=sp, m_swap=m_swap : sp.processMessage(m_swap), 100)
+        lambda sp=sp, m_swap=m_swap: sp.processMessage(m_swap), 100)
 
 #----------------------------------------------------------------------
 def timeEfficiency():
@@ -414,7 +417,7 @@
                                    [server, server], [server, server])
 
     sp_ns = timeit_(
-        lambda sp=sp, m_noswap=m_noswap : sp.processMessage(m_noswap), 100)
+        lambda sp=sp, m_noswap=m_noswap: sp.processMessage(m_noswap), 100)
 
     expected = rsa_128b+sha1_hdr+sha1_key*5+aes_2k+lioness_28k+prng_128b
     expected += lioness_2k
@@ -435,7 +438,8 @@
 
 #----------------------------------------------------------------------
 
-from mixminion.Common import secureDelete, installSignalHandlers, waitForChildren
+from mixminion.Common import secureDelete, installSignalHandlers, \
+     waitForChildren
 
 def fileOpsTiming():
     print "#================= File ops ====================="

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- test.py	24 Jun 2002 20:28:19 -0000	1.7
+++ test.py	25 Jun 2002 11:41:08 -0000	1.8
@@ -274,7 +274,7 @@
         eq(msg, pk_decrypt(pk_encrypt(msg, k1024),k1024))
         eq(msg, pk_decrypt(pk_encrypt(msg, pub1024),k1024))
 
-        # Make sure that CH_OAEP(RSA( )) inverts pk_encrypt.
+        # Make sure that CH_OAEP(RSA()) inverts pk_encrypt.
         eq(msg, _ml.check_oaep_padding(
                     k512.crypt(pk_encrypt(msg,k512), 0, 0),
                     mixminion.Crypto.OAEP_PARAMETER, 64))
@@ -313,9 +313,9 @@
 
         left = plain[:20]
         right = plain[20:]
-        right = ctr_crypt( right, sha1(key1+left+key1)[:16] )
+        right = ctr_crypt(right, sha1(key1+left+key1)[:16])
         left  = strxor(left, sha1(key2+right+key2))
-        right = ctr_crypt( right, sha1(key3+left+key3)[:16] )
+        right = ctr_crypt(right, sha1(key3+left+key3)[:16])
         left  = strxor(left, sha1(key4+right+key4))
 
         key = (key1,key2,key3,key4)
@@ -331,9 +331,9 @@
         eq(s("aBar")[:16], k.get("Bar"))
 
         z19 = "\x00"*19
-        eq( (s("aBaz"),               x(s("aBaz"), z19+"\x01"),
-             x(s("aBaz"),z19+"\x02"), x(s("aBaz"), z19+"\x03") ),
-            k.getLionessKeys("Baz"))
+        eq((s("aBaz"),               x(s("aBaz"), z19+"\x01"),
+            x(s("aBaz"),z19+"\x02"), x(s("aBaz"), z19+"\x03")),
+           k.getLionessKeys("Baz"))
 
     def test_aesprng(self):
         # Make sure that AESCounterPRNG is really repeatable.
@@ -462,6 +462,7 @@
 
         self.failUnlessRaises(ParseError, parseIPV4Info, ri[:-1])
         self.failUnlessRaises(ParseError, parseIPV4Info, ri+"x")
+    
 
     def test_smtpinfolocalinfo(self):
         for _class, _parse, _key in ((SMTPInfo, parseSMTPInfo, 'email'),
@@ -486,6 +487,15 @@
             self.assertEquals(inf.tag, "xyzzy\x00plover")
             self.assertEquals(inf.pack(), ri)
 
+    def test_replyblock(self):
+        r = ("SURB\x01\x00"+"\x00\x00\x00\x00"+("Z"*2048)+"\x00\x0A"+"\x00\x01"
+             +("F"*10))
+        rb = parseReplyBlock(r)
+        self.assertEquals(rb.timestamp, 0)
+        self.assertEquals(rb.header, "Z"*2048)
+        self.assertEquals(rb.routingType, 1)
+        self.assertEquals(rb.routingInfo, "F"*10)
+        self.assertEquals(r, rb.pack())
 
 #----------------------------------------------------------------------
 from mixminion.HashLog import HashLog
@@ -635,8 +645,8 @@
             """
         retsecrets = []
         retinfo = []
-        if secrets == None:
-            secrets = [ None ] * len(pks)
+        if secrets is None:
+            secrets = [None] * len(pks)
         self.assertEquals(len(head), mixminion.Packet.HEADER_LEN)
         for pk, secret, rt, ri in zip(pks, secrets,rtypes,rinfo):
             subh = mixminion.Packet.parseSubheader(pk_decrypt(head[:128], pk))
@@ -683,9 +693,9 @@
     def test_extended_routinginfo(self):
         bhead = BuildMessage._buildHeader
 
-        secrets = ["9"*16 ]
+        secrets = ["9"*16]
         longStr = "Foo"*50
-        head = bhead([self.server1 ], secrets, 99, longStr, AESCounterPRNG())
+        head = bhead([self.server1], secrets, 99, longStr, AESCounterPRNG())
         pks = (self.pk1,)
         rtypes = (99,)
         rinfo = (longStr,)
@@ -826,20 +836,20 @@
                                (FWD_TYPE, 99),
                                (self.server2.getRoutingInfo().pack(),
                                 "Goodbye") ),
-                             "Hello" )
+                             "Hello")
 
         m = bfm(payload, 99, "Goodbye",
-                [self.server1, ],
-                [self.server3, ])
+                [self.server1,],
+                [self.server3,])
 
         self.do_message_test(m,
                              ( (self.pk1,), None,
-                               (SWAP_FWD_TYPE, ),
-                               ( self.server3.getRoutingInfo().pack(), ) ),
-                             ( (self.pk3, ), None,
+                               (SWAP_FWD_TYPE,),
+                               (self.server3.getRoutingInfo().pack(),) ),
+                             ( (self.pk3,), None,
                                (99,),
                                ("Goodbye",) ),
-                             "Hello" )
+                             "Hello")
 
     def test_buildreply(self):
         brb = BuildMessage.buildReplyBlock
@@ -848,8 +858,8 @@
 
         ## Stateful reply blocks.
         reply, secrets = \
-             brb([ self.server3, self.server1, self.server2,
-                   self.server1, self.server3 ],
+             brb([self.server3, self.server1, self.server2,
+                  self.server1, self.server3],
                  SMTP_TYPE,
                  SMTPInfo("no-such-user@invalid", None).pack())
         pks_1 = (self.pk3, self.pk1, self.pk2, self.pk1, self.pk3)
@@ -858,7 +868,7 @@
                  self.server1.getRoutingInfo().pack(),
                  self.server3.getRoutingInfo().pack())
 
-        self.assert_(reply.addr == self.server3)
+        self.assert_(reply.routingInfo == self.server3.getRoutingInfo().pack())
 
         m = brm("Information?",
                 [self.server3, self.server1],
@@ -877,8 +887,8 @@
                              "Information?")
 
         ## Stateless replies
-        reply = bsrb([ self.server3, self.server1, self.server2,
-                       self.server1, self.server3 ],
+        reply = bsrb([self.server3, self.server1, self.server2,
+                      self.server1, self.server3],
                      "fred", "Galaxy Far Away.", 0)
 
         sec,(loc,) = self.do_header_test(reply.header, pks_1, None,
@@ -888,11 +898,11 @@
         self.assert_(loc.startswith(s))
         seed = ctr_crypt(loc[len(s):], "Galaxy Far Away.")
         prng = AESCounterPRNG(seed)
-        self.assert_(sec == [ prng.getBytes(16) for _ in range(5)])
+        self.assert_(sec == [ prng.getBytes(16) for _ in range(5) ])
 
         ## Stateless reply, no user key (trusted server)
-        reply = bsrb([ self.server3, self.server1, self.server2,
-                       self.server1, self.server3 ],
+        reply = bsrb([self.server3, self.server1, self.server2,
+                      self.server1, self.server3],
                      "fred", None)
         sec,(loc,) = self.do_header_test(reply.header, pks_1, None,
                             (FWD_TYPE,FWD_TYPE,FWD_TYPE,FWD_TYPE,LOCAL_TYPE),
@@ -900,7 +910,7 @@
         self.assert_(loc.startswith(s))
         seed = loc[len(s):]
         prng = AESCounterPRNG(seed)
-        self.assert_(sec == [ prng.getBytes(16) for _ in range(5)])
+        self.assert_(sec == [ prng.getBytes(16) for _ in range(5) ])
 
 #----------------------------------------------------------------------
 # Having tested BuildMessage without using PacketHandler, we can now use
@@ -1020,7 +1030,7 @@
         server1X = ServerInfo("127.0.0.1", 1, pk_get_modulus(self.pk1), "X"*20)
         class _packable:
             def pack(self): return "x"*200
-        server1X.getRoutingInfo = lambda _packable=_packable : _packable()
+        server1X.getRoutingInfo = lambda _packable=_packable: _packable()
 
         m = bfm("Z", LOCAL_TYPE, "hello\000bye",
                 [self.server2, server1X, self.server3],
@@ -1044,9 +1054,9 @@
 
         # Even duplicate secrets need to go.
         prng = AESCounterPRNG(" "*16)
-        reply1,s = brb([self.server1], SMTP_TYPE, "fred@invalid",prng)
+        reply1,s = brb([self.server1], SMTP_TYPE, "fred@invalid",0,prng)
         prng = AESCounterPRNG(" "*16)
-        reply2,s = brb([self.server2], LOCAL_TYPE, "foo",prng)
+        reply2,s = brb([self.server2], LOCAL_TYPE, "foo",0,prng)
         m = brm("Y", [self.server3], reply1)
         m2 = brm("Y", [self.server3], reply2)
         q, (a,m) = self.sp3.processMessage(m)
@@ -1189,8 +1199,8 @@
         queue1 = Queue(self.d1, create=1)
         queue2 = Queue(self.d2, create=1)
 
-        handles = [ queue1.queueMessage("Sample message %s" % i)
-                    for i in range(100) ]
+        handles = [queue1.queueMessage("Sample message %s" % i) 
+                   for i in range(100)]
         hdict = {}
         for i in range(100): hdict[handles[i]] = i
         self.assertEquals(queue1.count(), 100)
@@ -1208,7 +1218,7 @@
 
         q2h = []
         for h in handles[:30]:
-            q2h.append( queue1.moveMessage(h, queue2) )
+            q2h.append(queue1.moveMessage(h, queue2))
 
         from string import atoi
         seen = {}
@@ -1295,7 +1305,7 @@
 class MMTPTests(unittest.TestCase):
     def testBlockingTransmission(self):
         server, listener, messagesIn = _getMMTPServer() 
-        messages = [ "helloxxx"*4096, "helloyyy"*4096 ]
+        messages = ["helloxxx"*4096, "helloyyy"*4096]
 
         server.process(0.1)
         t = threading.Thread(None,
@@ -1313,7 +1323,7 @@
     def testNonblockingTransmission(self):
         server, listener, messagesIn = _getMMTPServer() 
 
-        messages = [ "helloxxx"*4096, "helloyyy"*4096 ]
+        messages = ["helloxxx"*4096, "helloyyy"*4096]
         async = mixminion.MMTPServer.AsyncServer()
         clientcon = mixminion.MMTPServer.MMTPClientConnection(
             _getTLSContext(0), "127.0.0.1", TEST_PORT, None, messages[:], None)