[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[minion-cvs] Start writing unit tests for directory voting formats. ...



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

Modified Files:
	DirFormats.py 
Log Message:
Start writing unit tests for directory voting formats. Fix many bugs and conformance issues. Keen!

Index: DirFormats.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/directory/DirFormats.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- DirFormats.py	7 Dec 2004 01:44:31 -0000	1.2
+++ DirFormats.py	13 Dec 2004 06:01:59 -0000	1.3
@@ -10,19 +10,21 @@
 
 import mixminion
 import mixminion.ServerInfo
+
 from mixminion.Common import formatBase64, formatDate, floorDiv, LOG, \
-     previousMidnight
+     MixError, previousMidnight
+
 from mixminion.Config import ConfigError
 from mixminion.Crypto import pk_sign, sha1, pk_encode_public_key
 
-def generateDirectory(identity, status,
+def _generateDirectory(identity, status,
                       servers, goodServerNames,
                       voters, validAfter,
                       clientVersions, serverVersions):
 
     assert status in ("vote", "consensus")
-    va = formatDate(validAfter)
-    vu = formatDate(validAfter+24*60*60+5)
+    va = formatDate(previousMidnight(validAfter))
+    vu = formatDate(previousMidnight(validAfter)+24*60*60+5)
     rec = goodServerNames[:]
     rec.sort()
     rec = ", ".join(rec)
@@ -30,7 +32,8 @@
     voters.sort()
     for keyid, urlbase in voters:
         v.append("Voting-Server: %s %s\n"
-                 % (formatBase64(keyid), urlbase))
+                 % (keyid, urlbase))
+    servers = sortServerList(servers)
 
     cvers = ", ".join(sortVersionList(clientVersions))
     svers = ", ".join(sortVersionList(serverVersions))
@@ -49,6 +52,37 @@
     signature = getDirectorySignature(unsigned, identity)
     return signature+unsigned
 
+def generateVoteDirectory(identity, servers, goodServerNames,
+                          voters, validAfter, clientVersions, serverVersions,
+                          validatedDigests=None):
+    valid = []
+    for server in servers:
+        try:
+            s = mixminion.ServerInfo.ServerInfo(
+                string=str(server), validatedDigests=validatedDigests,
+                _keepContents=1)
+        except ConfigError,e:
+            LOG.warn("Rejecting malformed serverinfo: %s",e)
+        else:
+            valid.append(s)
+
+    val = _generateDirectory(identity, 'vote', valid, goodServerNames,
+                             voters, validAfter,
+                             clientVersions, serverVersions)
+
+    try:
+        directory = mixminion.ServerInfo.SignedDirectory(
+            string=val, validatedDigests=validatedDigests)
+    except ConfigError,e:
+        raise MixError("Generated a vote directory that we cannot parse: %s"%e)
+
+    try:
+        checkVoteDirectory(voters, validAfter, directory)
+    except BadVote, e:
+        raise MixError("Generated unacceptable vote directory: %s"%e)
+
+    return val
+
 def generateConsensusDirectory(identity, voters, validAfter, directories,
                                validatedDigests=None):
     # directories is (source, stringable) list
@@ -165,14 +199,14 @@
     sig = sigs[0]
 
     ident = sig['Signed-Directory']['Directory-Identity']
-    keyid = sha1(pk_encode_public_key(ident))
+    keyid = mixminion.Crypto.pk_fingerprint(ident)
 
     # Do we recognize the signing key?
     for k,_ in voters:
         if k == keyid:
             break
     else:
-        raise BadVote("Unkown identity key (%s)"%formatBase64(keyid))
+        raise BadVote("Unknown identity key (%s)"%keyid)
 
     # Is the signature valid?
     if not sig.checkSignature():
@@ -188,29 +222,27 @@
         raise BadVote("Not marked as vote")
 
     # Do we agree about the voters?
-    dVoters = directory.dirInfo.voters[:]
-    dVoters.sort()
-    if dVoters != directory.dirInfo.voters:
-        raise BadVote("Votes not sorted")
+    if not _listIsSorted(directory.dirInfo.voters):
+        raise BadVote("Voters not sorted")
 
     vkeys = {}
-    for k,u in dVoters:
+    for k,u in directory.dirInfo.voters:
         vkeys[k]=u
     mykeys = {}
     for k,u in voters: mykeys[k]=u
 
-    for k,u in dVoters:
+    for k,u in directory.dirInfo.voters:
         try:
             if mykeys[k] != u:
                 raise BadVote("Mismatched URL for voter %s (%s vs %s)"%(
                     formatBase64(k), u, mykeys[k]))
         except KeyError:
-            raise BadVote("Unkown voter %s at %s"%(formatBase64(k),u))
+            raise BadVote("Unkown voter %s at %s"%(k,u))
     for k, u in voters:
         if not vkeys.has_key(k):
-            raise BadVote("Missing voter %s at %s"%(formatBase64(k),u))
+            raise BadVote("Missing voter %s at %s"%(k,u))
 
-    assert dVoters == voters
+    assert directory.dirInfo.voters == voters
 
     # Are the dates right?
     va = directory['Directory-Info']['Valid-After']
@@ -221,6 +253,14 @@
         raise BadVote("Validity span is not 1 day long (ends at %s)"%
                       formatDate(vu))
 
+    # Is everything sorted right?
+    for vs in ['MixminionClient', 'MixminionServer']:
+        versions = directory['Recommended-Software'][vs]
+        if not versionListIsSorted(versions):
+            raise BadVote("%s:%s is not in correct sorted order"%(vs,versions))
+    if not serverListIsSorted(directory.getAllServers()):
+        raise BadVote("Server descriptors are not in correct sorted order")
+
 def getDirectorySignature(directory, pkey):
     digest = mixminion.ServerInfo._getMultisignedDirectoryDigest(directory)
     signature = pk_sign(digest, pkey)
@@ -231,25 +271,46 @@
             "Directory-Digest: %s\nDirectory-Signature: %s\n")%(
         encKey,encDigest,encSig)
 
-def sortVersionList(versionList):
-    """DOCDOC"""
-    lst = []
-    for v in versionList:
-        try:
-            t = mixminion.parse_version_string(v)
-            lst.append((t,v))
-        except ValueError:
-            lst.append(((sys.maxint,sys.maxint),v))
-    lst.sort()
-    return [ v for _,v in lst ]
+def _versionOrdering(v):
+    try:
+        return mixminion.parse_version_string(v)
+    except ValueError:
+        return (sys.maxint, sys.maxint)
+
+def _serverOrdering(s):
+    return ( s.getNickname().lower(), s['Server']['Valid-After'],
+             s.getDigest() )
 
 def sortServerList(servers):
-    lst = []
-    for s in servers:
-        lst.append( (s.getNickname().lower(), s['Server']['Valid-After'],
-                     s.getDigest(), s) )
-    lst.sort()
-    return [ s for _, _, _, s in lst ]
+    return _sortedBy(servers, _serverOrdering)
+
+def sortVersionList(versions):
+    return _sortedBy(versions, _versionOrdering)
+
+def serverListIsSorted(servers):
+    return _listIsSorted(servers, _serverOrdering)
+
+def versionListIsSorted(versions):
+    assert _listIsSorted([4,9,16])
+    assert not _listIsSorted([4,91,16])
+    assert _listIsSorted([16,9,4], lambda x:-x)
+    return _listIsSorted(versions, _versionOrdering)
+
+def _sortedBy(lst, keyFn):
+    lst2 = [ (keyFn(item), item) for item in lst ]
+    lst2.sort()
+    return [ item for _, item in lst2 ]
+
+def _listIsSorted(lst, keyFn=None):
+    if keyFn is None:
+        lst2 = lst[:]
+        lst2.sort()
+    else:
+        lst2 = _sortedBy(lst, keyFn)
+    for a,b in zip(lst,lst2):
+        if a is not b:
+            return 0
+    return 1
 
 def commonElements(lists, threshold):
     counts = {}