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

[minion-cvs] More end-to-end testing



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

Modified Files:
	BuildMessage.py Crypto.py Packet.py test.py 
Log Message:
More end-to-end testing

BuildMessage:
	Move compression to BuildMessage.py
	Debugging
	More documentation

Crypto:
	Remove extraneous import

Packet:
	Fix thinko w.r.t. OAEP_OVERHEAD vs ENC_FWD_OVERHEAD on efwd messages

test:
	More tests for end-to-end


Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- BuildMessage.py	13 Oct 2002 01:34:44 -0000	1.14
+++ BuildMessage.py	14 Oct 2002 03:03:42 -0000	1.15
@@ -5,16 +5,17 @@
 
    Code to construct messages and reply blocks."""
 
+import zlib
 import operator
 from mixminion.Packet import *
-from mixminion.Common import MixError
+from mixminion.Common import MixError, MixFatalError, getLog
 import mixminion.Crypto as Crypto
 import mixminion.Modules as Modules
 
 __all__ = [ 'Address', 
            'buildForwardMessage', 'buildEncryptedMessage', 'buildReplyMessage',
            'buildStatelessReplyBlock', 'buildReplyBlock', 'decodePayload',
-	   'decodeForwardPayload', 'decodeEncryptedPayload', 
+	   'decodeForwardPayload', 'decodeEncryptedForwardPayload', 
 	   'decodeReplyPayload', 'decodeStatelessReplyPayload' ]
 
 def buildForwardMessage(payload, exitType, exitInfo, path1, path2, 
@@ -44,21 +45,21 @@
     if paddingPRNG is None: paddingPRNG = Crypto.AESCounterPRNG()
     if secretRNG is None: secretRNG = paddingPRNG
 
-    payload = _encodePayload(payload, OAEP_OVERHEAD, paddingPRNG)
+    payload = _encodePayload(payload, ENC_FWD_OVERHEAD, paddingPRNG)
 
     sessionKey = secretRNG.getBytes(SECRET_LEN)
-    payload = sessionKey+payLoad
+    payload = sessionKey+payload
     rsaDataLen = key.get_modulus_bytes()-OAEP_OVERHEAD
     rsaPart = payload[:rsaDataLen]
     lionessPart = payload[rsaDataLen:]
     # XXXX DOC
     while 1:
-	encrypted = mixminion.Crypto.pk_encrypt(rsaPart, key)
+	encrypted = Crypto.pk_encrypt(rsaPart, key)
 	if not (ord(encrypted[0]) & 0x80):
 	    break
     #XXXX doc mode 'End-to-end encrypt'
-    k = mixminion.Crypto.Keyset(sessionKey).getLionessKeys("End-to-end encrypt")
-    lionessPart = mixminion.Crypto.lioness_encrypt(lionessPart, k)
+    k = Crypto.Keyset(sessionKey).getLionessKeys("End-to-end encrypt")
+    lionessPart = Crypto.lioness_encrypt(lionessPart, k)
     payload = encrypted + lionessPart
     tag = payload[:TAG_LEN]
     payload = payload[TAG_LEN:]
@@ -180,7 +181,7 @@
 		pass
 
     if key is not None:
-	p = decodeEncryptedPayload(payload, tag, key)
+	p = decodeEncryptedForwardPayload(payload, tag, key)
 	if p is not None:
 	    return p
 	
@@ -190,7 +191,7 @@
     "XXXX"
     return _decodePayload(payload)
 
-def decodeEncryptedPayload(payload, tag, key):
+def decodeEncryptedForwardPayload(payload, tag, key):
     "XXXX"
     msg = tag+payload
     try:
@@ -220,10 +221,7 @@
     prng = Crypto.AESCounterPRNG(seed)
     secrets = [ prng.getBytes(SECRET_LEN) for _ in xrange(17) ]
 			 
-    return decodeReplyBlock(payload, secrets, check=1)
-	
-	
-    
+    return decodeReplyPayload(payload, secrets, check=1)
 
 #----------------------------------------------------------------------
 def _buildMessage(payload, exitType, exitInfo,
@@ -421,11 +419,19 @@
 
     return Message(header1, header2, payload).pack()
 
+#----------------------------------------------------------------------
+# Payload-related helpers
+
 def _encodePayload(payload, overhead, paddingPRNG):
-    """XXXX
+    """Helper: compress a payload, pad it, and add extra fields (size and hash)
+              payload: the initial payload
+	      overhead: number of bytes to omit from result
+	                (0 or ENC_FWD_OVERHEAD)
+              paddingPRNG: generator for padding.
+
+       BUG: This should eventually support K-of-N.
     """
-    # FFFF Do fragmentation here.
-    
+    assert overhead in (0, ENC_FWD_OVERHEAD)
     payload = compressData(payload)
 
     length = len(payload)
@@ -438,7 +444,7 @@
     return SingletonPayload(length, Crypto.sha1(payload), payload).pack()
 
 def _getRandomTag(rng):
-    "XXXX"
+    "Helper: Return a 20-byte string with the MSB of byte 0 set to 0."
     b = ord(rng.getBytes(1)) & 0x7f
     return chr(b) + rng.getBytes(TAG_LEN-1)
 
@@ -449,11 +455,63 @@
 
     if not payload.isSingleton():
 	raise MixError("Message fragments not yet supported")
-    #if payload.hash != Crypto.sha1(payload.data):
-    #    raise MixError("Hash doesn't match")
 
     return uncompressData(payload.getContents())
 
 def _checkPayload(payload):
+    'Return true iff the hash on the given payload seems valid'
     return payload[2:22] == Crypto.sha1(payload[22:])
 
+#----------------------------------------------------------------------
+# COMPRESSION FOR PAYLOADS
+
+_ZLIB_LIBRARY_OK = 0
+
+def compressData(payload):
+    """Given a string 'payload', compress it with zlib as specified in the
+       remailer spec."""
+    if not _ZLIB_LIBRARY_OK:
+	_validateZlib()
+    # Don't change any of these options; they're all mandated.  
+    zobj = zlib.compressobj(zlib.Z_BEST_COMPRESSION, zlib.DEFLATED,
+			    zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 
+			    zlib.Z_DEFAULT_STRATEGY)
+    s1 = zobj.compress(payload)
+    s2 = zobj.flush()
+    return s1 + s2
+
+def uncompressData(payload):
+    """Uncompress a string 'payload'; raise ParseError if it is not valid
+       compressed data."""
+    try:
+	return zlib.decompress(payload)
+    except zlib.error, _:
+	raise ParseError("Error in compressed data")
+
+def _validateZlib():
+    """Internal function:  Make sure that zlib is a recognized version, and 
+       that it compresses things as expected.  (This check is important, 
+       because using a zlib version that compressed differently from zlib1.1.4
+       would make senders partitionable by payload compression.)
+    """
+    global _ZLIB_LIBRARY_OK
+    ver = getattr(zlib, "ZLIB_VERSION")
+    if ver and ver < "1.1.2":
+	raise MixFatalError("Zlib version %s is not supported"%ver)
+    if ver in ("1.1.2", "1.1.3", "1.1.4"):
+	_ZLIB_LIBRARY_OK = 1
+	return
+
+    getLog().warn("Unrecognized zlib version: %r. Spot-checking output", ver)
+    # This test is inadequate, but it _might_ catch future incompatible
+    # changes.
+    _ZLIB_LIBRARY_OK = 0.5
+    good = 'x\xda\xed\xc6A\x11\x00 \x08\x00\xb0l\xd4\xf0\x87\x02\xf6o'+\
+	   '`\x0e\xef\xb6\xd7r\xed\x88S=7\xcd\xcc\xcc\xcc\xcc\xcc\xcc'+\
+	   '\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xbe\xdd\x03q'+\
+	   '\x8d\n\x93'
+    if compressData("aZbAAcdefg"*1000) == good:
+	_ZLIB_LIBRARY_OK = 1
+    else:
+	_ZLIB_LIBRARY_OK = 0
+	raise MixFatalError("Zlib output not as exected.")

Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- Crypto.py	13 Oct 2002 01:34:44 -0000	1.21
+++ Crypto.py	14 Oct 2002 03:03:42 -0000	1.22
@@ -12,7 +12,6 @@
 import sys
 import stat
 import copy_reg
-import zlib
 from types import StringType
 
 import mixminion._minionlib as _ml

Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- Packet.py	13 Oct 2002 01:34:44 -0000	1.11
+++ Packet.py	14 Oct 2002 03:03:42 -0000	1.12
@@ -14,10 +14,8 @@
             'parseReplyBlock', 'ENC_SUBHEADER_LEN', 'HEADER_LEN',
             'PAYLOAD_LEN', 'MAJOR_NO', 'MINOR_NO', 'SECRET_LEN', 'TAG_LEN',
 	    'SINGLETON_PAYLOAD_OVERHEAD', 'OAEP_OVERHEAD',
-	    'FRAGMENT_PAYLOAD_OVERHEAD',
-	    'compressData', 'uncompressData']
+	    'FRAGMENT_PAYLOAD_OVERHEAD', 'ENC_FWD_OVERHEAD']
 
-import zlib
 import struct
 from socket import inet_ntoa, inet_aton
 from mixminion.Common import MixError, floorDiv
@@ -243,13 +241,16 @@
 FRAGMENT_MESSAGEID_LEN = 20
 MAX_N_FRAGMENTS = 0x7ffff
 
+#XXXX DOCDOC
 SINGLETON_PAYLOAD_OVERHEAD = 2 + DIGEST_LEN
 FRAGMENT_PAYLOAD_OVERHEAD = 2 + DIGEST_LEN + FRAGMENT_MESSAGEID_LEN + 4
 OAEP_OVERHEAD = 42
+#XXXX DOC DOC  and e2e note is off by 4.
+ENC_FWD_OVERHEAD = OAEP_OVERHEAD - TAG_LEN + SECRET_LEN
 
 def parsePayload(payload):
     "XXXX"
-    if len(payload) not in (PAYLOAD_LEN, PAYLOAD_LEN-OAEP_OVERHEAD):
+    if len(payload) not in (PAYLOAD_LEN, PAYLOAD_LEN-ENC_FWD_OVERHEAD):
 	raise ParseError("Payload has bad length")
     bit0 = ord(payload[0]) & 0x80
     if bit0:
@@ -298,7 +299,7 @@
 	assert 0 <= self.size <= len(self.data)
 	assert len(self.hash) == DIGEST_LEN
 	assert (PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - len(self.data)) in \
-	       (0, OAEP_OVERHEAD)
+	       (0, ENC_FWD_OVERHEAD)
 	header = struct.pack(SINGLETON_UNPACK_PATTERN, self.size, self.hash)
 	return "%s%s" % (header, self.data)
 
@@ -322,30 +323,13 @@
 	assert len(self.msgID) == FRAGMENT_MESSAGEID_LEN
 	assert len(self.data) < self.msgLen < 0x100000000L
 	assert (PAYLOAD_LEN - FRAGMENT_PAYLOAD_OVERHEAD - len(self.data)) in \
-	       (0, OAEP_OVERHEAD)
+	       (0, ENC_FWD_OVERHEAD)
 	idx = self.index | 0x8000
 	header = struct.pack(FRAGMENT_UNPACK_PATTERN, idx, self.hash,
 			     self.msgID, self.msgLen)
 	return "%s%s" % (header, self.data)
 
 #----------------------------------------------------------------------
-# COMPRESSION FOR PAYLOADS
-
-# FFFF Check for zlib acceptability.  Check for correct parameters in zlib
-# FFFF module
-
-def compressData(payload):
-    "XXXX"
-    return zlib.compress(payload, 9)
-
-def uncompressData(payload):
-    "XXXX"
-    try:
-	return zlib.decompress(payload)
-    except zlib.error, _:
-	raise ParseError("Error in compressed data")
-
-#----------------------------------------------------------------------
 # REPLY BLOCKS
 
 RB_UNPACK_PATTERN = "!4sBBL%dsHH%ss" % (HEADER_LEN, SECRET_LEN)
@@ -477,4 +461,3 @@
     def pack(self):
         """Return the external representation of this routing info."""
 	return self.tag + self.user
-

Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -d -r1.29 -r1.30
--- test.py	13 Oct 2002 01:34:44 -0000	1.29
+++ test.py	14 Oct 2002 03:03:42 -0000	1.30
@@ -202,7 +202,7 @@
         _ml.openssl_seed("")
 
     def test_oaep(self):
-        import Crypto
+        import mixminion.Crypto as Crypto
         _add = Crypto._add_oaep_padding
         _check = Crypto._check_oaep_padding
         for add,check in ((_ml.add_oaep_padding, _ml.check_oaep_padding),
@@ -647,7 +647,7 @@
 	contents = ("payload"*(4*1024))[:28*1024 - 22]
 	hash = "HASH"*5
 	singleton_payload_1 = "\x00\xff"+hash+contents
-	singleton_payload_2 = singleton_payload_1[:-42] #OAEP overhead
+	singleton_payload_2 = singleton_payload_1[:-38] #efwd overhead
 	p1 = parsePayload(singleton_payload_1)
 	p2 = parsePayload(singleton_payload_2)
 	self.failUnless(p1.isSingleton() and p2.isSingleton())
@@ -656,7 +656,7 @@
 	self.assertEquals(p1.hash,hash)
 	self.assertEquals(p2.hash,hash)
 	self.assertEquals(p1.data,contents)
-	self.assertEquals(p2.data,contents[:-42])
+	self.assertEquals(p2.data,contents[:-38])
 	self.assertEquals(p1.getContents(), contents[:255])
 	self.assertEquals(p2.getContents(), contents[:255])
 	self.assertEquals(p1.pack(),singleton_payload_1)
@@ -665,7 +665,7 @@
 	self.assertEquals(singleton_payload_1,
 			  SingletonPayload(255, hash, contents).pack())
 	self.assertEquals(singleton_payload_2,
-			  SingletonPayload(255, hash, contents[:-42]).pack())
+			  SingletonPayload(255, hash, contents[:-38]).pack())
 
 	# Impossible payload lengths
 	self.failUnlessRaises(ParseError,parsePayload,singleton_payload_1+"a")
@@ -680,7 +680,7 @@
 	assert len(msgID) == 20
 	contents = contents[:28*1024 - 46]
 	frag_payload_1 = "\x80\x02"+hash+msgID+"\x00\x01\x00\x00"+contents
-	frag_payload_2 = frag_payload_1[:-42] # oaep overhead
+	frag_payload_2 = frag_payload_1[:-38] # efwd overhead
 	p1 = parsePayload(frag_payload_1)
 	p2 = parsePayload(frag_payload_2)
 	self.failUnless(not p1.isSingleton() and not p2.isSingleton())
@@ -693,14 +693,14 @@
 	self.assertEquals(p1.msgLen,64*1024)
 	self.assertEquals(p2.msgLen,64*1024)
 	self.assertEquals(p1.data,contents)
-	self.assertEquals(p2.data,contents[:-42])
+	self.assertEquals(p2.data,contents[:-38])
 	self.assertEquals(p1.pack(),frag_payload_1)
 	self.assertEquals(p2.pack(),frag_payload_2)
 
 	self.assertEquals(frag_payload_1,
 		  FragmentPayload(2,hash,msgID,64*1024,contents).pack())
 	self.assertEquals(frag_payload_2,
-		  FragmentPayload(2,hash,msgID,64*1024,contents[:-42]).pack())
+		  FragmentPayload(2,hash,msgID,64*1024,contents[:-38]).pack())
 
 	# Impossible payload lengths
 	self.failUnlessRaises(ParseError,parsePayload,frag_payload_1+"a")
@@ -710,9 +710,9 @@
 	# Impossible message sizes
 	min_payload_1 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xD3"+contents
 	bad_payload_1 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xD2"+contents
-	min_payload_2 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xA9"+contents[:-42]
-	bad_payload_2 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xA8"+contents[:-42]
-	min_payload_3 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xD2"+contents[:-42]
+	min_payload_2 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xAD"+contents[:-38]
+	bad_payload_2 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xAC"+contents[:-38]
+	min_payload_3 = "\x80\x02"+hash+msgID+"\x00\x00\x6F\xD2"+contents[:-38]
 	parsePayload(min_payload_1)
 	parsePayload(min_payload_2)
 	parsePayload(min_payload_3)
@@ -793,7 +793,6 @@
     pk1 = pk_generate()
     pk2 = pk_generate()
     pk3 = pk_generate()
-
     
 class FakeServerInfo:
     """Represents a Mixminion server, and the information needed to send
@@ -823,6 +822,51 @@
         self.server2 = FakeServerInfo("127.0.0.2", 3, self.pk2, "Z"*20)
         self.server3 = FakeServerInfo("127.0.0.3", 5, self.pk3, "Q"*20)
 
+    def test_compression(self):
+	p = AESCounterPRNG()
+	longMsg = p.getBytes(100)*2 + str(dir(mixminion.Crypto))
+                     
+	# Make sure compression is reversible.
+	for m in ("", "a", "\000", "xyzzy"*10, ("glossy glossolalia.."*2)[32],
+		  longMsg):
+	    c = BuildMessage.compressData(m)
+	    self.assertEquals(m, BuildMessage.uncompressData(c))
+	
+	self.failUnlessRaises(ParseError, BuildMessage.uncompressData, "3")
+
+	for _ in xrange(20):
+	    for _ in xrange(20):
+		m = p.getBytes(p.getInt(1000))
+		try:
+		    BuildMessage.uncompressData(m)
+		except ParseError, _:
+		    pass
+	#FFFF Find a decent test vector.
+
+    def test_payload_helpers(self):
+	"test helpers for payload encoding"
+	p = AESCounterPRNG()
+	for i in xrange(10):
+	    t = BuildMessage._getRandomTag(p)
+	    self.assertEquals(20, len(t))
+	    self.assertEquals(0, ord(t[0])&0x80)
+
+	b = p.getBytes(28*1024)
+	self.assert_(not BuildMessage._checkPayload(b))
+
+	for m in (p.getBytes(3000), p.getBytes(10000), "", "q", "blznerty"):
+	    for ov in 0, 42-20+16: # encrypted forward overhead
+		pld = BuildMessage._encodePayload(m,ov,p)
+		self.assertEquals(28*1024, len(pld)+ov)
+		comp = BuildMessage.compressData(m)
+		self.assert_(pld[22:].startswith(comp))
+		self.assertEquals(sha1(pld[22:]),pld[2:22])
+		self.assertEquals(len(comp), ord(pld[0])*256+ord(pld[1]))
+		self.assertEquals(0, ord(pld[0])&0x80)
+		self.assertEquals(m, BuildMessage._decodePayload(pld))
+
+	# XXXX Test failing cases
+
     def test_buildheader_1hop(self):
         bhead = BuildMessage._buildHeader
 
@@ -884,6 +928,7 @@
             subh = mixminion.Packet.parseSubheader(pk_decrypt(head[:128], pk))
             if secret:
                 self.assertEquals(subh.secret, secret)
+                retsecrets.append(secret)
             else:
                 secret = subh.secret
                 retsecrets.append(secret)
@@ -901,7 +946,7 @@
 	    else:
 		ext = 20
 		if ri:
-		    tag = ri[:20]
+		    tag = subh.routinginfo[:20]
             if not subh.isExtended():
                 if ri:
 		    self.assertEquals(subh.routinginfo[ext:], ri)
@@ -1079,6 +1124,7 @@
 
     def test_build_fwd_message(self):
         bfm = BuildMessage.buildForwardMessage
+        befm = BuildMessage.buildEncryptedForwardMessage
         payload = "Hello"
 
         m = bfm(payload, 500, "Goodbye",
@@ -1109,6 +1155,28 @@
                                ("Goodbye",) ),
                              "Hello")
 
+	# Encrypted forward message
+	rsa1, rsa2 = pk_generate(1024), pk_generate(2048)
+	for rsakey in rsa1,rsa2:
+	    m = befm(payload, 500, "Phello", 
+		     [self.server1, self.server2],
+		     [self.server3, self.server2],
+		     rsakey)
+	    def decoder(p,t,key=rsakey):
+		return BuildMessage.decodeEncryptedForwardPayload(p,t,key)
+
+	    self.do_message_test(m,
+				 ( (self.pk1, self.pk2), None,
+				   (FWD_TYPE, SWAP_FWD_TYPE),
+				   (self.server2.getRoutingInfo().pack(),
+				    self.server3.getRoutingInfo().pack()) ),
+				 ( (self.pk3, self.pk2), None,
+				   (FWD_TYPE, 500),
+				   (self.server2.getRoutingInfo().pack(),
+				    "Phello") ),
+				 payload,
+				 decoder=decoder)
+
     def test_buildreply(self):
         brb = BuildMessage.buildReplyBlock
         bsrb = BuildMessage.buildStatelessReplyBlock
@@ -1135,35 +1203,53 @@
                 [self.server3, self.server1],
                 reply)
 
-	#XXXX Explain this thing
 	def decoder(p,t,secrets=secrets):
-	    return BuildMessage.decodeReplyPayload(p,secrets[-1:])
+	    return BuildMessage.decodeReplyPayload(p,secrets)
 
         self.do_message_test(m,
                              ((self.pk3, self.pk1), None,
                               (FWD_TYPE,SWAP_FWD_TYPE),
                               (self.server1.getRoutingInfo().pack(),
                                self.server3.getRoutingInfo().pack())),
-                             (pks_1, hsecrets, # stop after first pk???????XXXX
+                             (pks_1, hsecrets,
                               (FWD_TYPE,FWD_TYPE,FWD_TYPE,FWD_TYPE,SMTP_TYPE),
                               infos+("no-such-user@invalid",)),
                              "Information?",
 			     decoder=decoder)
-
         ## Stateless replies
         reply = bsrb([self.server3, self.server1, self.server2,
                       self.server1, self.server3], MBOX_TYPE,
-                     "fred", "Galaxy Far Away.", 0)
+                     "fred", "Galaxy Far Away", 0)
 
         sec,(loc,) = self.do_header_test(reply.header, pks_1, None,
                             (FWD_TYPE,FWD_TYPE,FWD_TYPE,FWD_TYPE,MBOX_TYPE),
                             infos+(None,))
-	if 0: #XXXXX
-	    s = "fred\x00RTRN"
-	    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.assertEquals(loc[20:], "fred")
+
+	seed = loc[:20]
+	prng = AESCounterPRNG(sha1(seed+"Galaxy Far AwayGenerate")[:16])
+	sec.reverse()
+	self.assertEquals(sec, [ prng.getBytes(16) for _ in range(len(sec)) ])
+
+        m = brm("Information?  What's the world come to?",
+                [self.server3, self.server1],
+                reply)
+
+	def decoder2(p,t):
+	    return BuildMessage.decodeStatelessReplyPayload(p,t,
+							    "Galaxy Far Away")
+        self.do_message_test(m,
+                             ((self.pk3, self.pk1), None,
+                              (FWD_TYPE,SWAP_FWD_TYPE),
+                              (self.server1.getRoutingInfo().pack(),
+                               self.server3.getRoutingInfo().pack())),
+                             (pks_1, None,
+                              (FWD_TYPE,FWD_TYPE,FWD_TYPE,FWD_TYPE,MBOX_TYPE),
+                              infos+("fred",)),
+                             "Information?  What's the world come to?",
+			     decoder=decoder2)
+	
 
 #----------------------------------------------------------------------
 # Having tested BuildMessage without using PacketHandler, we can now use
@@ -1221,7 +1307,7 @@
 		if decoder is None:
 		    p = BuildMessage.decodeForwardPayload(p)
 		else:
-		    p = decoder(p, res[1][1])
+		    p = decoder(p, res[1][1][:20])
                 self.assert_(p.startswith(payload))
                 break
 
@@ -2627,6 +2713,10 @@
     suite = unittest.TestSuite()
     loader = unittest.TestLoader()
     tc = loader.loadTestsFromTestCase
+
+    if 0:
+	suite.addTest(tc(BuildMessageTests))
+	return suite
 
     suite.addTest(tc(ClientMainTests))
     suite.addTest(tc(ServerMainTests))