[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Tweaks to make server need less babysitting; patch for ...
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv11825/lib/mixminion
Modified Files:
BuildMessage.py ClientMain.py Config.py Crypto.py HashLog.py
MMTPClient.py MMTPServer.py Modules.py Packet.py
PacketHandler.py Queue.py ServerInfo.py ServerMain.py test.py
Log Message:
Tweaks to make server need less babysitting; patch for attack; work on client.
PacketHandler.py, BuildMessage.py, Crypto.py, test.py:
Patch for George's attack of august 15
ClientMain.py:
Untested beginnings of directory store
Config.py:
Check config files for bogus characters
Crypyo.py:
Make RSA public keys pickleable
HashLog.py:
Remove debugging output
MMTPClient.py, BuildMessage.py, MMTPServer.py, Packet.py, Modules.py,
ServerInfo.py:
Change version numbers from "1.0" to "0.1" so we can obsolete this
version of the code on a real 1.0 release.
Queues.py:
Add note about the Cottrell algorithm not being the one I implemented
now.
ServerMain.py:
Clean up hash logs on exit.
Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- BuildMessage.py 31 Aug 2002 04:12:36 -0000 1.12
+++ BuildMessage.py 10 Sep 2002 14:45:29 -0000 1.13
@@ -276,6 +276,11 @@
key = Crypto.lioness_keys_from_payload(payload)
header2 = Crypto.lioness_encrypt(header2, key)
+ # Encrypt payload with a hash of header2. Now tagging either will make
+ # both unrecoverable.
+ key = Crypto.lioness_keys_from_header(header2)
+ payload = Crypto.lioness_encrypt(payload, key)
+
# Copy secrets1 so we don't reverse the original.
secrets1 = secrets1[:]
Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- ClientMain.py 31 Aug 2002 04:12:36 -0000 1.1
+++ ClientMain.py 10 Sep 2002 14:45:30 -0000 1.2
@@ -10,6 +10,21 @@
XXXX at least one configuration works.
"""
+
+# The client needs to store:
+# - config
+# - keys for pending SURBs
+# - server directory
+# (Have dir of files from which to reconstruct a shelf of cached
+# info.)
+# (Don't *name* files in dir; or at least, don't make their names
+# magic. Files can be: ServerInfos, ServerDirectories, or 'fake'
+# directories. Each server can have any number of virtual or
+# official tags. Users should use the CLI to add/remove entries from
+# dir.)
+# - Per-systemm directory location is a neat idea, but individual users
+# must check signature. That's a way better idea for later.
+
import os
import getopt
import sys
@@ -17,11 +32,101 @@
import bisect
import mixminion.Crypto
-from mixminion.Common import getLog, floorDiv
+from mixminion.Common import getLog, floorDiv, createPrivateDir
import mixminion.Config
import mixminion.BuildMessage
import mixminion.MMTPClient
import mixminion.Modules
+
+class DirectoryCache:
+ """Holds a set of directories and serverinfo objects persistently.
+
+ FFFF This should actually cache the nickname and liveness information
+ FFFF rather than parsing and reparsing it each time. Of course, we'll
+ FFFF want to re-do it entirely once we have directory support, so it
+ FFFF doesn't matter so much right now.
+ """
+ def __init__(self, dirname):
+ createPrivateDir(dirname)
+ self.dirname = dirname
+ self.servers = None
+
+ def load(self, forceReload=0):
+ if not (self.servers is None or forceReload):
+ return
+ now = time.time()
+ self.servers = {}
+ self.allServers = []
+ self.highest_num = -1
+ for fn in os.listdir(self.dirname):
+ if not fn.startswith("si"):
+ continue
+ n = int(fn[2:])
+ if n > self.highest_num:
+ self.highest_num = n
+ info = ServerInfo(fname=os.path.join(self.dirname, fn),
+ assumeValid=1)
+ nickname = info['Server']['Nickname']
+ if info['Server']['Valid-Until'] < now:
+ getLog().info("Removing expired descriptor for %s",
+ nickname)
+ os.unlink(os.path.join(dirname, fn))
+ continue
+ self.allServers.append(info)
+ if self.servers.has_key(nickname):
+ self.servers[nickname].append(info)
+ else:
+ self.servers[nickname] = info
+
+ def getCurrentServer(nickname, when=None):
+ if when is None:
+ when = time.time()
+ for info in self.servers[nickname]:
+ #XXXX fail on DNE
+ server = info['Server']
+ if server['Valid-After'] <= now <= server['Valid-Until']:
+ return info
+ #XXXX fail on DNE
+ return None
+
+ def importServerInfo(self, fname, force=1):
+ self.load()
+ f = open(fname)
+ contents = f.read()
+ f.close()
+ info = ServerInfo(string=contents, assumeValid=0)
+ now = time.time()
+ if info['Server']['Valid-Until'] < now:
+ getLog().error("Not importing descriptor %s: already expired",
+ fname)
+ return 0
+ nickname = info['Server']['Nickname']
+ identity_pk = info['Server']['Identity'].get_public_key()
+ if self.servers.has_key(nickname):
+ other = self.servers[nickname][0]
+ if other['Server']['Identity'].get_public_key() != identity_pk:
+ getLog().error("Possible spoofing: that's not the public key I remember for %s", nickname)
+ if not force:
+ getLog().error("I'm not going to import it.")
+ return 0
+ else:
+ getLog().error("... importing anyway.")
+
+ self.servers[nickname].append(info)
+ else:
+ self.servers[nickname] = info
+
+ self.allServers.append(info)
+
+ self.highest_num += 1
+ fname_new = "si%d" % self.highest_num
+ f = os.fdopen(os.open(os.path.join(self.dirname, fname_name),
+ os.O_CREAT|os.O_EXCL, 0600),
+ 'w')
+ f.write(contents)
+ f.close()
+
+
def sendTestMessage(servers1, servers2):
assert len(servers1)
Index: Config.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Config.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- Config.py 31 Aug 2002 04:12:36 -0000 1.14
+++ Config.py 10 Sep 2002 14:45:30 -0000 1.15
@@ -59,6 +59,11 @@
import mixminion.Packet
import mixminion.Crypto
+# String with all characters 0..255; used for str.translate
+_ALLCHARS = "".join(map(chr, range(256)))
+# String with all printing ascii characters.
+_GOODCHARS = "".join(map(chr, range(0x07,0x0e)+range(0x20,0x80)))
+
class ConfigError(MixError):
"""Thrown when an error is found in a configuration file."""
pass
@@ -213,11 +218,10 @@
raise ConfigError("No match found for command %r" %cmd)
-_allChars = "".join(map(chr, range(256)))
def _parseBase64(s,_hexmode=0):
"""Validation function. Converts a base-64 encoded config value into
its original. Raises ConfigError on failure."""
- s = s.translate(_allChars, " \t\v\n")
+ s = s.translate(_ALLCHARS, " \t\v\n")
try:
if _hexmode:
return binascii.a2b_hex(s)
@@ -327,6 +331,10 @@
curSection = None
lineno = 0
lastKey = None
+
+ badchars = contents.translate(_ALLCHARS, _GOODCHARS)
+ if badchars:
+ raise ConfigError("Invalid characters in file: %r", badchars)
fileLines = contents.split("\n")
if fileLines[-1] == '':
Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- Crypto.py 29 Aug 2002 03:30:21 -0000 1.19
+++ Crypto.py 10 Sep 2002 14:45:30 -0000 1.20
@@ -11,6 +11,7 @@
import os
import sys
import stat
+import copy_reg
from types import StringType
import mixminion._minionlib as _ml
@@ -47,7 +48,7 @@
except:
raise MixFatalError("Error initializing entropy source")
openssl_seed(40)
-
+
def sha1(s):
"""Return the SHA1 hash of its argument"""
return _ml.sha1(s)
@@ -219,6 +220,14 @@
f.close()
return rsa
+def _pickle_rsa(rsa):
+ return _ml.rsa_make_public_key, rsa.get_public_key()
+
+# Register this function to make RSA keys pickleable. Note that we only
+# pickle the public part of an RSA key; for long-term storage of private
+# keys, you should use PEM so we can support encryption.
+copy_reg.pickle(_ml.RSA, _pickle_rsa, _ml.rsa_make_public_key)
+
#----------------------------------------------------------------------
# OAEP Functionality
#
@@ -328,6 +337,9 @@
# Used to LIONESS-encrypt the header at the swap point.
HIDE_HEADER_MODE = "HIDE HEADER"
+# Used to LIONESS-encrypt the payload at the swap point.
+HIDE_PAYLOAD_MODE = "HIDE PAYLOAD"
+
# Used to remember whether we've seen a secret before
REPLAY_PREVENTION_MODE = "REPLAY PREVENTION"
@@ -365,6 +377,12 @@
at the swap point.'''
digest = sha1(payload)
return Keyset(digest).getLionessKeys(HIDE_HEADER_MODE)
+
+def lioness_keys_from_header(header2):
+ '''Given the off-header, returns the LIONESS keys to encrypt the payload
+ at the swap point.'''
+ digest = sha1(header2)
+ return Keyset(digest).getLionessKeys(HIDE_PAYLOAD_MODE)
#---------------------------------------------------------------------
# Random number generators
Index: HashLog.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/HashLog.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- HashLog.py 31 Aug 2002 04:12:36 -0000 1.10
+++ HashLog.py 10 Sep 2002 14:45:30 -0000 1.11
@@ -13,6 +13,9 @@
# FFFF Mechanism to force a different default db module.
+# FFFF Journaling for dbs that don't recover from catastrophic failure during
+# FFFF writes.
+
class HashLog:
"""A HashLog is a file containing a list of message digests that we've
already processed.
@@ -40,7 +43,6 @@
'keyid'."""
parent = os.path.split(filename)[0]
createPrivateDir(parent)
- print filename
self.log = anydbm.open(filename, 'c')
if isinstance(self.log, dumbdbm._Database):
getLog().warn("Warning: logging packet digests to a flat file.")
Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- MMTPClient.py 25 Aug 2002 05:58:02 -0000 1.8
+++ MMTPClient.py 10 Sep 2002 14:45:30 -0000 1.9
@@ -48,10 +48,12 @@
####
# Protocol negotiation
- # For now, we only support 1.0
- self.tls.write("MMTP 1.0\r\n")
- inp = self.tls.read(len("PROTOCOL 1.0\r\n"))
- if inp != "MMTP 1.0\r\n":
+ # For now, we only support 1.0, but we call it 0.1 so we can
+ # change our mind between now and a release candidate, and so we
+ # can obsolete betas come release time.
+ self.tls.write("MMTP 0.1\r\n")
+ inp = self.tls.read(len("MMTP 0.1\r\n"))
+ if inp != "MMTP 0.1\r\n":
raise MixProtocolError("Protocol negotiation failed")
def sendPacket(self, packet):
Index: MMTPServer.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPServer.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- MMTPServer.py 31 Aug 2002 04:12:36 -0000 1.16
+++ MMTPServer.py 10 Sep 2002 14:45:30 -0000 1.17
@@ -397,7 +397,7 @@
pass
#----------------------------------------------------------------------
-PROTOCOL_STRING = "MMTP 1.0\r\n"
+PROTOCOL_STRING = "MMTP 0.1\r\n"
PROTOCOL_RE = re.compile("MMTP ([^\s\r\n]+)\r\n")
SEND_CONTROL = "SEND\r\n"
JUNK_CONTROL = "JUNK\r\n"
@@ -434,7 +434,7 @@
warn("Bad protocol list. Closing connection.")
self.shutdown(err=1)
protocols = m.group(1).split(",")
- if "1.0" not in protocols:
+ if "0.1" not in protocols:
warn("Unsupported protocol list. Closing connection.")
self.shutdown(err=1); return
else:
Index: Modules.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Modules.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- Modules.py 31 Aug 2002 04:12:36 -0000 1.11
+++ Modules.py 10 Sep 2002 14:45:30 -0000 1.12
@@ -366,7 +366,7 @@
def getServerInfoBlock(self):
return """\
[Delivery/MBOX]
- Version: 1.0
+ Version: 0.1
"""
def getName(self):
Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- Packet.py 21 Aug 2002 19:09:48 -0000 1.8
+++ Packet.py 10 Sep 2002 14:45:30 -0000 1.9
@@ -246,8 +246,9 @@
if magic != 'SURB':
raise ParseError("Misformatted reply block")
- if major != 0x01 or minor != 0x00:
- raise ParseError("Unrecognized version on reply block")
+ if major != 0x00 or minor != 0x01:
+ raise ParseError("Unrecognized version on reply block %s.%s",
+ major,minor)
ri = s[MIN_RB_LEN:]
if len(ri) != rlen:
@@ -269,7 +270,7 @@
def pack(self):
"""Returns the external representation of this reply block"""
return struct.pack(RB_UNPACK_PATTERN,
- "SURB", 0x01, 0x00, self.timestamp,
+ "SURB", 0x00, 0x01, self.timestamp,
self.header, len(self.routingInfo),
self.routingType)+self.routingInfo
Index: PacketHandler.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/PacketHandler.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- PacketHandler.py 31 Aug 2002 04:12:36 -0000 1.7
+++ PacketHandler.py 10 Sep 2002 14:45:30 -0000 1.8
@@ -42,6 +42,16 @@
self.privatekey = (privatekey, )
self.hashlog = (hashlog, )
+ def syncLogs(self):
+ """Sync all this PacketHandler's hashlogs."""
+ for h in self.hashlog:
+ h.sync()
+
+ def close(self):
+ """Close all this PacketHandler's hashlogs."""
+ for h in self.hashlog:
+ h.close()
+
def processMessage(self, msg):
"""Given a 32K mixminion message, processes it completely.
@@ -156,11 +166,16 @@
header2 = Crypto.lioness_decrypt(msg.header2,
keys.getLionessKeys(Crypto.HEADER_ENCRYPT_MODE))
- # If we're the swap node, decrypt header2 with a hash of the
- # payload, and swap the headers.
+ # If we're the swap node, (1) decrypt the payload with a hash of
+ # header2... (2) decrypt header2 with a hash of the payload...
+ # (3) and swap the headers.
if rt == Modules.SWAP_FWD_TYPE:
+ hkey = Crypto.lioness_keys_from_header(header2)
+ payload = Crypto.lioness_decrypt(payload, hkey)
+
hkey = Crypto.lioness_keys_from_payload(payload)
header2 = Crypto.lioness_decrypt(header2, hkey)
+
header1, header2 = header2, header1
# Build the address object for the next hop
Index: Queue.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Queue.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- Queue.py 29 Aug 2002 03:30:21 -0000 1.16
+++ Queue.py 10 Sep 2002 14:45:30 -0000 1.17
@@ -382,6 +382,9 @@
self.sendRate = 1.0 - retainRate
def getBatch(self):
+ # XXXX This is not the real cottrell algorithm. Once somebody
+ # XXXX has explained to me what is going on here, I will implement
+ # XXXX the real one. -NM
pool = self.count()
if pool <= self.threshold:
return []
Index: ServerInfo.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerInfo.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- ServerInfo.py 29 Aug 2002 03:30:21 -0000 1.13
+++ ServerInfo.py 10 Sep 2002 14:45:31 -0000 1.14
@@ -15,7 +15,7 @@
import base64
import socket
-from mixminion.Common import createPrivateDir
+from mixminion.Common import createPrivateDir, getLog
from mixminion.Modules import SWAP_FWD_TYPE, FWD_TYPE
from mixminion.Packet import IPV4Info
import mixminion.Config
@@ -85,8 +85,9 @@
####
# Check 'Server' section.
server = sections['Server']
- if server['Descriptor-Version'] != '1.0':
- raise ConfigError("Unrecognized descriptor version")
+ if server['Descriptor-Version'] != '0.1':
+ raise ConfigError("Unrecognized descriptor version %r",
+ server['Descriptor-Version'])
if len(server['Nickname']) > MAX_NICKNAME:
raise ConfigError("Nickname too long")
identityKey = server['Identity']
@@ -245,6 +246,7 @@
nickname = socket.gethostname()
if not nickname or nickname.lower().startswith("localhost"):
nickname = config['Incoming/MMTP'].get('IP', "<Unknown host>")
+ getLog().warn("No nickname given: defaulting to %r", nickname)
contact = config['Server']['Contact-Email']
comments = config['Server']['Comments']
if not validAt:
@@ -280,7 +282,7 @@
info = """\
[Server]
- Descriptor-Version: 1.0
+ Descriptor-Version: 0.1
IP: %(IP)s
Nickname: %(Nickname)s
Identity: %(Identity)s
@@ -299,10 +301,10 @@
if config["Incoming/MMTP"].get("Enabled", 0):
info += """\
[Incoming/MMTP]
- Version: 1.0
+ Version: 0.1
Port: %(Port)s
Key-Digest: %(KeyID)s
- Protocols: 1.0
+ Protocols: 0.1
""" % fields
for k,v in config.getSectionItems("Incoming/MMTP"):
if k not in ("Allow", "Deny"):
@@ -312,8 +314,8 @@
if config["Outgoing/MMTP"].get("Enabled", 0):
info += """\
[Outgoing/MMTP]
- Version: 1.0
- Protocols: 1.0
+ Version: 0.1
+ Protocols: 0.1
"""
for k,v in config.getSectionItems("Outgoing/MMTP"):
if k not in ("Allow", "Deny"):
Index: ServerMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerMain.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- ServerMain.py 31 Aug 2002 04:12:36 -0000 1.8
+++ ServerMain.py 10 Sep 2002 14:45:31 -0000 1.9
@@ -439,6 +439,7 @@
self.mmtpServer.process(1)
self.incomingQueue.sendReadyMessages()
+ self.packetHandler.syncLogs()
getLog().trace("Mix interval elapsed")
self.mixPool.mix()
self.outgoingQueue.sendReadyMessages()
@@ -454,6 +455,10 @@
self.moduleManager.cleanQueues()
nextShred = now + 6000
+ def close(self):
+ """Release all resources; close all files."""
+ self.packetHandler.close()
+
#----------------------------------------------------------------------
def usageAndExit(cmd):
@@ -510,7 +515,7 @@
except:
getLog().fatal_exc(sys.exc_info(),"Exception while running server")
getLog().info("Server shutting down")
-
+ server.close()
sys.exit(0)
Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -d -r1.26 -r1.27
--- test.py 31 Aug 2002 04:12:36 -0000 1.26
+++ test.py 10 Sep 2002 14:45:31 -0000 1.27
@@ -383,6 +383,12 @@
decoded = pk_decode_private_key(encoded)
eq(msg, pk_decrypt(pk_encrypt(msg, pub512),decoded))
+ # Test pickling
+ init_crypto()
+ s = cPickle.dumps(k512)
+ self.assertEquals(cPickle.loads(s).get_public_key(),
+ k512.get_public_key())
+
def test_trng(self):
# Make sure that the true rng is at least superficially ok.
self.assertNotEquals(trng(40), trng(40))
@@ -632,7 +638,7 @@
self.assertEquals(inf.pack(), ri)
def test_replyblock(self):
- r = ("SURB\x01\x00"+"\x00\x00\x00\x00"+("Z"*2048)+"\x00\x0A"+"\x00\x01"
+ r = ("SURB\x00\x01"+"\x00\x00\x00\x00"+("Z"*2048)+"\x00\x0A"+"\x00\x01"
+("F"*10))
rb = parseReplyBlock(r)
self.assertEquals(rb.timestamp, 0)
@@ -913,6 +919,9 @@
payload = lioness_decrypt(payload, pkey)
if path is secrets1:
+ swapkey = mixminion.Crypto.lioness_keys_from_header(head2)
+ payload = lioness_decrypt(payload, swapkey)
+
swapkey = mixminion.Crypto.lioness_keys_from_payload(payload)
head2 = lioness_decrypt(head2, swapkey)
@@ -933,6 +942,10 @@
pkey = ks.getLionessKeys(PAYLOAD_ENCRYPT_MODE)
head2 = lioness_decrypt(head2, hkey)
payload = lioness_decrypt(payload, pkey)
+
+ swapkey = mixminion.Crypto.lioness_keys_from_header(head2)
+ payload = lioness_decrypt(payload, swapkey)
+
swapkey = mixminion.Crypto.lioness_keys_from_payload(payload)
head2 = lioness_decrypt(head2, swapkey)
@@ -961,6 +974,7 @@
ks = Keyset(s)
p = lioness_decrypt(p,ks.getLionessKeys(PAYLOAD_ENCRYPT_MODE))
h2 = lioness_decrypt(h2,ks.getLionessKeys(HEADER_ENCRYPT_MODE))
+ p = lioness_decrypt(p,mixminion.Crypto.lioness_keys_from_header(h2))
h2 = lioness_decrypt(h2,mixminion.Crypto.lioness_keys_from_payload(p))
sec = self.do_header_test(h2, *header_info_2)
@@ -2120,6 +2134,7 @@
PublicKeyLifetime: 10 days
EncryptPrivateKey: no
Mode: relay
+Nickname: fred-the-bunny
"""
_IDENTITY_KEY = None
@@ -2150,7 +2165,7 @@
d)
info = mixminion.ServerInfo.ServerInfo(string=inf)
eq = self.assertEquals
- eq(info['Server']['Descriptor-Version'], "1.0")
+ eq(info['Server']['Descriptor-Version'], "0.1")
eq(info['Server']['IP'], "192.168.0.1")
eq(info['Server']['Nickname'], "The Server")
self.failUnless(0 <= time.time()-info['Server']['Published'] <= 120)
@@ -2162,11 +2177,11 @@
eq(info['Server']['Comments'],
"This is a test of the emergency broadcast system")
- eq(info['Incoming/MMTP']['Version'], "1.0")
+ eq(info['Incoming/MMTP']['Version'], "0.1")
eq(info['Incoming/MMTP']['Port'], 48099)
- eq(info['Incoming/MMTP']['Protocols'], "1.0")
- eq(info['Outgoing/MMTP']['Version'], "1.0")
- eq(info['Outgoing/MMTP']['Protocols'], "1.0")
+ eq(info['Incoming/MMTP']['Protocols'], "0.1")
+ eq(info['Outgoing/MMTP']['Version'], "0.1")
+ eq(info['Outgoing/MMTP']['Protocols'], "0.1")
eq(info['Incoming/MMTP']['Allow'], [("192.168.0.16", "255.255.255.255",
1,1024),
("0.0.0.0", "0.0.0.0",
@@ -2174,7 +2189,7 @@
eq(info['Incoming/MMTP']['Deny'], [("192.168.0.16", "255.255.255.255",
0,65535),
])
- eq(info['Delivery/MBOX']['Version'], "1.0")
+ eq(info['Delivery/MBOX']['Version'], "0.1")
# Now make sure everything was saved properly
keydir = os.path.join(d, "key_key1")
@@ -2402,6 +2417,7 @@
PublicKeyLifetime: 10 days
IdentityKeyBits: 2048
EncryptPrivateKey: no
+Nickname: mac-the-knife
"""
_FAKE_HOME = None