[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[minion-cvs] Do not mix tabs and spaces in our source files; use spa...
Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.mit.edu:/tmp/cvs-serv9270/lib/mixminion
Modified Files:
BuildMessage.py ClientMain.py Common.py Crypto.py
MMTPClient.py Main.py Packet.py ServerInfo.py benchmark.py
test.py testSupport.py
Log Message:
Do not mix tabs and spaces in our source files; use spaces only.
Index: BuildMessage.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/BuildMessage.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- BuildMessage.py 12 Dec 2002 19:56:46 -0000 1.22
+++ BuildMessage.py 16 Dec 2002 02:40:11 -0000 1.23
@@ -16,7 +16,7 @@
'buildReplyBlock', 'decodePayload' ]
def buildForwardMessage(payload, exitType, exitInfo, path1, path2,
- paddingPRNG=None):
+ paddingPRNG=None):
"""Construct a forward message.
payload: The payload to deliver. Must compress to under 28K-22b.
If it does not, MixError is raised.
@@ -24,19 +24,19 @@
exitInfo: The routing info for the final node, not including tag.
path1: Sequence of ServerInfo objects for the first leg of the path
path2: Sequence of ServerInfo objects for the 2nd leg of the path
- paddingPRNG: random number generator used to generate padding.
- If None, a new PRNG is initialized.
+ paddingPRNG: random number generator used to generate padding.
+ If None, a new PRNG is initialized.
Neither path1 nor path2 may be empty.
"""
if paddingPRNG is None:
- paddingPRNG = Crypto.getCommonPRNG()
+ paddingPRNG = Crypto.getCommonPRNG()
assert path1 and path2
LOG.debug("Encoding forward message for %s-byte payload",len(payload))
LOG.debug(" Using path %s/%s",
- [s.getNickname() for s in path1],
- [s.getNickname() for s in path2])
+ [s.getNickname() for s in path1],
+ [s.getNickname() for s in path2])
LOG.debug(" Delivering to %04x:%r", exitType, exitInfo)
# Compress, pad, and checksum the payload.
@@ -48,7 +48,7 @@
return _buildMessage(payload, exitType, exitInfo, path1, path2,paddingPRNG)
def buildEncryptedForwardMessage(payload, exitType, exitInfo, path1, path2,
- key, paddingPRNG=None, secretRNG=None):
+ key, paddingPRNG=None, secretRNG=None):
"""Construct a forward message encrypted with the public key of a
given user.
payload: The payload to deliver. Must compress to under 28K-60b.
@@ -57,19 +57,19 @@
exitInfo: The routing info for the final node, not including tag.
path1: Sequence of ServerInfo objects for the first leg of the path
path2: Sequence of ServerInfo objects for the 2nd leg of the path
- key: Public key of this message's recipient.
- paddingPRNG: random number generator used to generate padding.
- If None, a new PRNG is initialized.
+ key: Public key of this message's recipient.
+ paddingPRNG: random number generator used to generate padding.
+ If None, a new PRNG is initialized.
"""
if paddingPRNG is None:
- paddingPRNG = Crypto.getCommonPRNG()
+ paddingPRNG = Crypto.getCommonPRNG()
if secretRNG is None: secretRNG = paddingPRNG
LOG.debug("Encoding encrypted forward message for %s-byte payload",
- len(payload))
+ len(payload))
LOG.debug(" Using path %s/%s",
- [s.getNickname() for s in path1],
- [s.getNickname() for s in path2])
+ [s.getNickname() for s in path1],
+ [s.getNickname() for s in path2])
LOG.debug(" Delivering to %04x:%r", exitType, exitInfo)
# Compress, pad, and checksum the payload.
@@ -91,9 +91,9 @@
# we keep trying to encrypt until the MSBit of our encrypted value is
# zero.
while 1:
- encrypted = Crypto.pk_encrypt(rsaPart, key)
- if not (ord(encrypted[0]) & 0x80):
- break
+ encrypted = Crypto.pk_encrypt(rsaPart, key)
+ if not (ord(encrypted[0]) & 0x80):
+ break
# Lioness encryption.
k= Crypto.Keyset(sessionKey).getLionessKeys(Crypto.END_TO_END_ENCRYPT_MODE)
lionessPart = Crypto.lioness_encrypt(lionessPart, k)
@@ -114,10 +114,10 @@
ServerInfo for the nodes on the first leg of the path.
"""
if paddingPRNG is None:
- paddingPRNG = Crypto.getCommonPRNG()
+ paddingPRNG = Crypto.getCommonPRNG()
LOG.debug("Encoding reply message for %s-byte payload",
- len(payload))
+ len(payload))
LOG.debug(" Using path %s/??",[s.getNickname() for s in path1])
# Compress, pad, and checksum the payload.
@@ -127,14 +127,14 @@
# crossover note. (We use 'decrypt' so that the message recipient can
# simply use 'encrypt' to reverse _all_ the steps of the reply path.)
k = Crypto.Keyset(replyBlock.encryptionKey).getLionessKeys(
- Crypto.PAYLOAD_ENCRYPT_MODE)
+ Crypto.PAYLOAD_ENCRYPT_MODE)
payload = Crypto.lioness_decrypt(payload, k)
return _buildMessage(payload, None, None,
path1=path1, path2=replyBlock)
def _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime=0,
- secretPRNG=None, tag=None):
+ secretPRNG=None, tag=None):
"""Helper function: makes a reply block, given a tag and a PRNG to
generate secrets. Returns a 3-tuple containing (1) a
newly-constructed reply block, (2) a list of secrets used to
@@ -155,7 +155,7 @@
secretPRNG = Crypto.getCommonPRNG()
LOG.debug("Building reply block for path %s",
- [s.getNickname() for s in path])
+ [s.getNickname() for s in path])
LOG.debug(" Delivering to %04x:%r", exitType, exitInfo)
# The message is encrypted first by the end-to-end key, then by
@@ -168,7 +168,7 @@
# (This will go away when we deprecate 'stateful' reply blocks
if tag is None:
- tag = _getRandomTag(secretPRNG)
+ tag = _getRandomTag(secretPRNG)
header = _buildHeader(path, headerSecrets, exitType, tag+exitInfo,
paddingPRNG=Crypto.getCommonPRNG())
@@ -179,7 +179,7 @@
# Maybe we shouldn't even allow this to be called with userKey==None.
def buildReplyBlock(path, exitType, exitInfo, userKey,
- expiryTime=0, secretRNG=None):
+ expiryTime=0, secretRNG=None):
"""Construct a 'state-carrying' 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
@@ -188,7 +188,7 @@
info).
path: a list of ServerInfo objects
- exitType,exitInfo: The address to deliver the final message.
+ exitType,exitInfo: The address to deliver the final message.
userKey: a string used to encrypt the seed.
NOTE: We used to allow another kind of 'non-state-carrying' reply
@@ -196,7 +196,7 @@
determine
"""
if secretRNG is None:
- secretRNG = Crypto.getCommonPRNG()
+ secretRNG = Crypto.getCommonPRNG()
# We need to pick the seed to generate our keys. To make the decoding
# step a little faster, we find a seed such that H(seed|userKey|"Validate")
@@ -209,26 +209,26 @@
# XXXX anybody who sees multiple tags. We need to make sure that userKey
# XXXX is stored on disk, and isn't a password. This needs more thought.
while 1:
- seed = _getRandomTag(secretRNG)
- if Crypto.sha1(seed+userKey+"Validate")[-1] == '\x00':
- break
+ seed = _getRandomTag(secretRNG)
+ if Crypto.sha1(seed+userKey+"Validate")[-1] == '\x00':
+ break
prng = Crypto.AESCounterPRNG(Crypto.sha1(seed+userKey+"Generate")[:16])
return _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime, prng,
- seed)[0]
+ seed)[0]
#----------------------------------------------------------------------
# MESSAGE DECODING
def decodePayload(payload, tag, key=None,
- #storedKeys=None, # 'Stateful' reply blocks are disabled.
- userKey=None):
+ #storedKeys=None, # 'Stateful' reply blocks are disabled.
+ userKey=None):
"""Given a 28K payload and a 20-byte decoding tag, attempt to decode and
decompress the original message.
key: an RSA key to decode encrypted forward messages, or None
- userKey: our encryption key for reply blocks, or None.
+ userKey: our encryption key for reply blocks, or None.
If we can successfully decrypt the payload, we return it. If we
might be able to decrypt the payload given more/different keys,
@@ -238,12 +238,12 @@
# FFFF Allow callbacks?
if len(payload) != PAYLOAD_LEN or len(tag) != TAG_LEN:
- raise MixError("Wrong payload or tag length")
+ raise MixError("Wrong payload or tag length")
# If the payload already contains a valid checksum, it's a forward
# message.
if _checkPayload(payload):
- return _decodeForwardPayload(payload)
+ return _decodeForwardPayload(payload)
# ('Stateful' reply blocks are disabled.)
@@ -251,28 +251,28 @@
## # using those keys.
## if storedKeys is not None:
-## secrets = storedKeys.get(tag)
-## if secrets is not None:
-## del storedKeys[tag]
-## return _decodeReplyPayload(payload, secrets)
+## secrets = storedKeys.get(tag)
+## if secrets is not None:
+## del storedKeys[tag]
+## return _decodeReplyPayload(payload, secrets)
# If H(tag|userKey|"Validate") ends with 0, then the message _might_
# be a reply message using H(tag|userKey|"Generate") as the seed for
# its master secrets. (There's a 1-in-256 chance that it isn't.)
if userKey is not None:
- if Crypto.sha1(tag+userKey+"Validate")[-1] == '\x00':
- try:
- return _decodeStatelessReplyPayload(payload, tag, userKey)
- except MixError:
- pass
+ if Crypto.sha1(tag+userKey+"Validate")[-1] == '\x00':
+ try:
+ return _decodeStatelessReplyPayload(payload, tag, userKey)
+ except MixError:
+ pass
# If we have an RSA key, and none of the above steps get us a good
# payload, then we may as well try to decrypt the start of tag+key with
# our RSA key.
if key is not None:
- p = _decodeEncryptedForwardPayload(payload, tag, key)
- if p is not None:
- return p
+ p = _decodeEncryptedForwardPayload(payload, tag, key)
+ if p is not None:
+ return p
return None
@@ -285,8 +285,8 @@
"""Helper function: decode an encrypted forward payload. Return values
are the same as decodePayload.
payload: the payload to decode
- tag: the decoding tag
- key: the RSA key of the payload's recipient."""
+ tag: the decoding tag
+ key: the RSA key of the payload's recipient."""
assert len(tag) == TAG_LEN
assert len(payload) == PAYLOAD_LEN
@@ -295,13 +295,13 @@
# first N. Try decrypting...
msg = tag+payload
try:
- rsaPart = Crypto.pk_decrypt(msg[:key.get_modulus_bytes()], key)
+ rsaPart = Crypto.pk_decrypt(msg[:key.get_modulus_bytes()], key)
except Crypto.CryptoError:
- return None
+ return None
rest = msg[key.get_modulus_bytes():]
k = Crypto.Keyset(rsaPart[:SECRET_LEN]).getLionessKeys(
- Crypto.END_TO_END_ENCRYPT_MODE)
+ Crypto.END_TO_END_ENCRYPT_MODE)
rest = rsaPart[SECRET_LEN:] + Crypto.lioness_decrypt(rest, k)
# ... and then, check the checksum and continue.
@@ -316,10 +316,10 @@
# Reverse the 'decrypt' operations of the reply mixes, and the initial
# 'decrypt' of the originating user...
for sec in secrets:
- k = Crypto.Keyset(sec).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE)
- payload = Crypto.lioness_encrypt(payload, k)
- if check and _checkPayload(payload):
- break
+ k = Crypto.Keyset(sec).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE)
+ payload = Crypto.lioness_encrypt(payload, k)
+ if check and _checkPayload(payload):
+ break
# ... and then, check the checksum and continue.
return _decodePayloadImpl(payload)
@@ -368,16 +368,16 @@
reply = path2
path2 = None
else:
- if len(exitInfo) < TAG_LEN:
- raise MixError("Implausibly short exit info: %r"%exitInfo)
- if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE:
- raise MixError("Invalid exit type: %4x"%exitType)
+ if len(exitInfo) < TAG_LEN:
+ raise MixError("Implausibly short exit info: %r"%exitInfo)
+ if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE:
+ raise MixError("Invalid exit type: %4x"%exitType)
### SETUP CODE: let's handle all the variant cases.
# Set up the random number generators.
if paddingPRNG is None:
- paddingPRNG = Crypto.getCommonPRNG()
+ paddingPRNG = Crypto.getCommonPRNG()
if paranoia:
nHops = len(path1)
if path2: nHops += len(path2)
@@ -542,8 +542,8 @@
def _encodePayload(payload, overhead, paddingPRNG):
"""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)
+ 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.
@@ -557,7 +557,7 @@
# If the compressed payload doesn't fit in 28K, then we need to bail out.
if paddingLen < 0:
- raise MixError("Payload too long for singleton message")
+ raise MixError("Payload too long for singleton message")
# Otherwise, we pad the payload, and construct a new SingletonPayload,
# including this payload's size and checksum.
@@ -574,13 +574,13 @@
not encryption."""
# Is the hash ok?
if not _checkPayload(payload):
- raise MixError("Hash doesn't match")
+ raise MixError("Hash doesn't match")
# Parse the payload into its size, checksum, and body.
payload = parsePayload(payload)
if not payload.isSingleton():
- raise MixError("Message fragments not yet supported")
+ raise MixError("Message fragments not yet supported")
# Uncompress the body.
return uncompressData(payload.getContents())
@@ -600,14 +600,14 @@
"""Given a string 'payload', compress it with the 'deflate' method
as specified in the remailer spec and in RFC1951."""
if not _ZLIB_LIBRARY_OK:
- _validateZlib()
+ _validateZlib()
# Don't change any of these options; if different Mixminion clients
# compress their data differently, an adversary could distinguish
# messages generated by them.
zobj = zlib.compressobj(zlib.Z_BEST_COMPRESSION, zlib.DEFLATED,
- zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL,
- zlib.Z_DEFAULT_STRATEGY)
+ zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL,
+ zlib.Z_DEFAULT_STRATEGY)
s1 = zobj.compress(payload)
s2 = zobj.flush()
s = s1 + s2
@@ -627,20 +627,20 @@
# FFFF zero bytes down to fit in a single payload and use us to
# FFFF mailbomb people, hard.
if len(payload) < 6 or payload[0:2] != '\x78\xDA':
- raise ParseError("Invalid zlib header")
+ raise ParseError("Invalid zlib header")
try:
- # We can't just call zlib.decompress(payload), since we'll eventually
- # want to limit the output size.
- zobj = zlib.decompressobj(zlib.MAX_WBITS)
- # Decompress the payload.
- d = zobj.decompress(payload)
- # Get any leftovers, which shouldn't exist.
- nil = zobj.flush()
- if nil != '':
- raise ParseError("Error in compressed data")
- return d
+ # We can't just call zlib.decompress(payload), since we'll eventually
+ # want to limit the output size.
+ zobj = zlib.decompressobj(zlib.MAX_WBITS)
+ # Decompress the payload.
+ d = zobj.decompress(payload)
+ # Get any leftovers, which shouldn't exist.
+ nil = zobj.flush()
+ if nil != '':
+ raise ParseError("Error in compressed data")
+ return d
except zlib.error:
- raise ParseError("Error in compressed data")
+ raise ParseError("Error in compressed data")
def _validateZlib():
"""Internal function: Make sure that zlib is a recognized version, and
@@ -651,23 +651,23 @@
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)
+ raise MixFatalError("Zlib version %s is not supported"%ver)
_ZLIB_LIBRARY_OK = 0.5
if ver in ("1.1.2", "1.1.3", "1.1.4"):
- _ZLIB_LIBRARY_OK = 1
- return
+ _ZLIB_LIBRARY_OK = 1
+ return
LOG.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 = '\x78\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\x03'+\
- 'q\x8d\n\x93'
+ '`\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\x03'+\
+ 'q\x8d\n\x93'
if compressData("aZbAAcdefg"*1000) == good:
- _ZLIB_LIBRARY_OK = 1
+ _ZLIB_LIBRARY_OK = 1
else:
- _ZLIB_LIBRARY_OK = 0
- raise MixFatalError("Zlib output not as exected.")
+ _ZLIB_LIBRARY_OK = 0
+ raise MixFatalError("Zlib output not as exected.")
Index: ClientMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ClientMain.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- ClientMain.py 15 Dec 2002 03:45:30 -0000 1.16
+++ ClientMain.py 16 Dec 2002 02:40:11 -0000 1.17
@@ -62,141 +62,141 @@
# byFilename: a map from filename within self.directory to valid
# ServerInfo object.
def __init__(self, directory, now=None):
- """Create a new TrivialKeystore to access the descriptors stored in
- directory. Selects descriptors that are valid at the time 'now',
- or at the current time if 'now' is None."""
- self.directory = directory
- createPrivateDir(directory)
- self.byNickname = {}
- self.byFilename = {}
+ """Create a new TrivialKeystore to access the descriptors stored in
+ directory. Selects descriptors that are valid at the time 'now',
+ or at the current time if 'now' is None."""
+ self.directory = directory
+ createPrivateDir(directory)
+ self.byNickname = {}
+ self.byFilename = {}
- if now is None:
- now = time.time()
+ if now is None:
+ now = time.time()
- for f in os.listdir(self.directory):
- # Try to read a file: is it a server descriptor?
- p = os.path.join(self.directory, f)
- try:
- info = ServerInfo(fname=p, assumeValid=0)
- except ConfigError:
- LOG.warn("Invalid server descriptor %s", p)
- continue
+ for f in os.listdir(self.directory):
+ # Try to read a file: is it a server descriptor?
+ p = os.path.join(self.directory, f)
+ try:
+ info = ServerInfo(fname=p, assumeValid=0)
+ except ConfigError:
+ LOG.warn("Invalid server descriptor %s", p)
+ continue
- # Find its nickname and normalized filename
- serverSection = info['Server']
- nickname = serverSection['Nickname']
+ # Find its nickname and normalized filename
+ serverSection = info['Server']
+ nickname = serverSection['Nickname']
- if '.' in f:
- f = f[:f.rindex('.')]
+ if '.' in f:
+ f = f[:f.rindex('.')]
- # Skip the descriptor if it isn't valid yet...
- if now < serverSection['Valid-After']:
- LOG.info("Ignoring future decriptor %s", p)
- continue
- # ... or if it's expired ...
- if now >= serverSection['Valid-Until']:
- LOG.info("Ignoring expired decriptor %s", p)
- continue
- # ... or if it's going to expire within 3 hours (HACK!).
- if now + 3*60*60 >= serverSection['Valid-Until']:
- LOG.info("Ignoring soon-to-expire decriptor %s", p)
- continue
- # Only allow one server per nickname ...
- if self.byNickname.has_key(nickname):
- LOG.warn(
- "Ignoring descriptor %s with duplicate nickname %s",
- p, nickname)
- continue
- # ... and per normalized filename.
- if self.byFilename.has_key(f):
- LOG.warn(
- "Ignoring descriptor %s with duplicate prefix %s",
- p, f)
- continue
- LOG.info("Loaded server %s from %s", nickname, f)
- # Okay, it's good. Cache it.
- self.byNickname[nickname] = info
- self.byFilename[f] = info
+ # Skip the descriptor if it isn't valid yet...
+ if now < serverSection['Valid-After']:
+ LOG.info("Ignoring future decriptor %s", p)
+ continue
+ # ... or if it's expired ...
+ if now >= serverSection['Valid-Until']:
+ LOG.info("Ignoring expired decriptor %s", p)
+ continue
+ # ... or if it's going to expire within 3 hours (HACK!).
+ if now + 3*60*60 >= serverSection['Valid-Until']:
+ LOG.info("Ignoring soon-to-expire decriptor %s", p)
+ continue
+ # Only allow one server per nickname ...
+ if self.byNickname.has_key(nickname):
+ LOG.warn(
+ "Ignoring descriptor %s with duplicate nickname %s",
+ p, nickname)
+ continue
+ # ... and per normalized filename.
+ if self.byFilename.has_key(f):
+ LOG.warn(
+ "Ignoring descriptor %s with duplicate prefix %s",
+ p, f)
+ continue
+ LOG.info("Loaded server %s from %s", nickname, f)
+ # Okay, it's good. Cache it.
+ self.byNickname[nickname] = info
+ self.byFilename[f] = info
def getServerInfo(self, name):
- """Return a ServerInfo object corresponding to 'name'. If 'name' is
- a ServerInfo object, returns 'name'. Otherwise, checks server by
- nickname, then by filename within the keystore, then by filename
- on the file system. If no server is found, returns None."""
- if isinstance(name, ServerInfo):
- return name
- elif self.byNickname.has_key(name):
- return self.byNickname[name]
- elif self.byFilename.has_key(name):
- return self.byFilename[name]
- elif os.path.exists(name):
- try:
- return ServerInfo(fname=name, assumeValid=0)
- except OSError, e:
- raise MixError("Couldn't read descriptor %s: %s" %
- (name, e))
- except ConfigError, e:
- raise MixError("Couldn't parse descriptor %s: %s" %
- (name, e))
- else:
- return None
+ """Return a ServerInfo object corresponding to 'name'. If 'name' is
+ a ServerInfo object, returns 'name'. Otherwise, checks server by
+ nickname, then by filename within the keystore, then by filename
+ on the file system. If no server is found, returns None."""
+ if isinstance(name, ServerInfo):
+ return name
+ elif self.byNickname.has_key(name):
+ return self.byNickname[name]
+ elif self.byFilename.has_key(name):
+ return self.byFilename[name]
+ elif os.path.exists(name):
+ try:
+ return ServerInfo(fname=name, assumeValid=0)
+ except OSError, e:
+ raise MixError("Couldn't read descriptor %s: %s" %
+ (name, e))
+ except ConfigError, e:
+ raise MixError("Couldn't parse descriptor %s: %s" %
+ (name, e))
+ else:
+ return None
def getPath(self, serverList):
- """Given a sequence of strings of ServerInfo objects, resolves each
- one according to the rule of getServerInfo, and returns a list of
- ServerInfos. Raises MixError if any server can't be resolved."""
- path = []
- for s in serverList:
- if isinstance(s, ServerInfo):
- path.append(s)
- elif isinstance(s, types.StringType):
- server = self.getServerInfo(s)
- if server is not None:
- path.append(server)
- else:
- raise MixError("Couldn't find descriptor %s" % s)
- return path
+ """Given a sequence of strings of ServerInfo objects, resolves each
+ one according to the rule of getServerInfo, and returns a list of
+ ServerInfos. Raises MixError if any server can't be resolved."""
+ path = []
+ for s in serverList:
+ if isinstance(s, ServerInfo):
+ path.append(s)
+ elif isinstance(s, types.StringType):
+ server = self.getServerInfo(s)
+ if server is not None:
+ path.append(server)
+ else:
+ raise MixError("Couldn't find descriptor %s" % s)
+ return path
def listServers(self):
- """Returns a linewise listing of the current servers and their caps.
- stdout. This will go away or get refactored in future versions
- once we have real path selection and client-level modules."""
+ """Returns a linewise listing of the current servers and their caps.
+ stdout. This will go away or get refactored in future versions
+ once we have real path selection and client-level modules."""
lines = []
nicknames = self.byNickname.keys()
- nicknames.sort()
- longestnamelen = max(map(len, nicknames))
- fmtlen = min(longestnamelen, 20)
- format = "%"+str(fmtlen)+"s (expires %s): %s"
- for n in nicknames:
- caps = []
- si = self.byNickname[n]
- if si['Delivery/MBOX'].get('Version',None):
- caps.append("mbox")
- if si['Delivery/SMTP'].get('Version',None):
- caps.append("smtp")
- # XXXX This next check is highly bogus.
- if (si['Incoming/MMTP'].get('Version',None) and
- si['Outgoing/MMTP'].get('Version',None)):
- caps.append("relay")
- until = formatDate(si['Server']['Valid-Until'])
- line = format % (n, until, " ".join(caps))
- lines.append(line)
- return lines
+ nicknames.sort()
+ longestnamelen = max(map(len, nicknames))
+ fmtlen = min(longestnamelen, 20)
+ format = "%"+str(fmtlen)+"s (expires %s): %s"
+ for n in nicknames:
+ caps = []
+ si = self.byNickname[n]
+ if si['Delivery/MBOX'].get('Version',None):
+ caps.append("mbox")
+ if si['Delivery/SMTP'].get('Version',None):
+ caps.append("smtp")
+ # XXXX This next check is highly bogus.
+ if (si['Incoming/MMTP'].get('Version',None) and
+ si['Outgoing/MMTP'].get('Version',None)):
+ caps.append("relay")
+ until = formatDate(si['Server']['Valid-Until'])
+ line = format % (n, until, " ".join(caps))
+ lines.append(line)
+ return lines
def getRandomServers(self, prng, n):
- """Returns a list of n different servers, in random order, according
- to prng. Raises MixError if not enough exist.
+ """Returns a list of n different servers, in random order, according
+ to prng. Raises MixError if not enough exist.
- (This isn't actually used.)"""
- vals = self.byNickname.values()
- if len(vals) < n:
- raise MixError("Not enough servers (%s requested)", n)
- return prng.shuffle(vals, n)
+ (This isn't actually used.)"""
+ vals = self.byNickname.values()
+ if len(vals) < n:
+ raise MixError("Not enough servers (%s requested)", n)
+ return prng.shuffle(vals, n)
def installDefaultConfig(fname):
"""Create a default, 'fail-safe' configuration in a given file"""
LOG.warn("No configuration file found. Installing default file in %s",
- fname)
+ fname)
f = open(os.path.expanduser(fname), 'w')
f.write("""\
# This file contains your options for the mixminion client.
@@ -232,83 +232,83 @@
# keystore: A TrivialKeystore object
# prng: A pseudo-random number generator for padding and path selection
def __init__(self, conf):
- """Create a new MixminionClient with a given configuration"""
- self.config = conf
+ """Create a new MixminionClient with a given configuration"""
+ self.config = conf
- # Make directories
- userdir = os.path.expanduser(self.config['User']['UserDir'])
- createPrivateDir(userdir)
- #createPrivateDir(os.path.join(userdir, 'surbs'))
- createPrivateDir(os.path.join(userdir, 'servers'))
+ # Make directories
+ userdir = os.path.expanduser(self.config['User']['UserDir'])
+ createPrivateDir(userdir)
+ #createPrivateDir(os.path.join(userdir, 'surbs'))
+ createPrivateDir(os.path.join(userdir, 'servers'))
- # Get directory cache
- self.keystore = TrivialKeystore(
- os.path.join(userdir,"servers"))
+ # Get directory cache
+ self.keystore = TrivialKeystore(
+ os.path.join(userdir,"servers"))
- # Initialize PRNG
- self.prng = mixminion.Crypto.getCommonPRNG()
+ # Initialize PRNG
+ self.prng = mixminion.Crypto.getCommonPRNG()
def sendForwardMessage(self, address, payload, path1, path2):
- """Generate and send a forward message.
- address -- the results of a parseAddress call
- payload -- the contents of the message to send
- path1,path2 -- lists of servers or server names for the first and
- second legs of the path, respectively. These are processed
- as described in TrivialKeystore.getServerInfo"""
- message, firstHop = \
- self.generateForwardMessage(address, payload, path1, path2)
+ """Generate and send a forward message.
+ address -- the results of a parseAddress call
+ payload -- the contents of the message to send
+ path1,path2 -- lists of servers or server names for the first and
+ second legs of the path, respectively. These are processed
+ as described in TrivialKeystore.getServerInfo"""
+ message, firstHop = \
+ self.generateForwardMessage(address, payload, path1, path2)
- self.sendMessages([message], firstHop)
+ self.sendMessages([message], firstHop)
def generateForwardMessage(self, address, payload, path1, path2):
- """Generate a forward message, but do not send it. Returns
- a tuple of (the message body, a ServerInfo for the first hop.)
+ """Generate a forward message, but do not send it. Returns
+ a tuple of (the message body, a ServerInfo for the first hop.)
- address -- the results of a parseAddress call
- payload -- the contents of the message to send
- path1,path2 -- lists of servers or server names for the first and
- second legs of the path, respectively. These are processed
- as described in TrivialKeystore.getServerInfo"""
+ address -- the results of a parseAddress call
+ payload -- the contents of the message to send
+ path1,path2 -- lists of servers or server names for the first and
+ second legs of the path, respectively. These are processed
+ as described in TrivialKeystore.getServerInfo"""
if not path1:
- raise MixError("No servers in first leg of path")
- if not path2:
- raise MixError("No servers in second leg of path")
+ raise MixError("No servers in first leg of path")
+ if not path2:
+ raise MixError("No servers in second leg of path")
- servers1 = self.keystore.getPath(path1)
- servers2 = self.keystore.getPath(path2)
+ servers1 = self.keystore.getPath(path1)
+ servers2 = self.keystore.getPath(path2)
- routingType, routingInfo, lastHop = address.getRouting()
- if lastHop is None:
+ routingType, routingInfo, lastHop = address.getRouting()
+ if lastHop is None:
lastServer = servers2[-1]
- # FFFF This is only a temporary solution. It needs to get
- # FFFF rethought, or refactored into ServerInfo, or something.
- if routingType == SMTP_TYPE:
- ok = lastServer['Delivery/SMTP'].get('Version',None)
- if not ok:
- raise MixError("Last hop doesn't support SMTP")
- elif routingType == MBOX_TYPE:
- ok = lastServer['Delivery/MBOX'].get('Version',None)
- if not ok:
- raise MixError("Last hop doesn't support MBOX")
- else:
- servers2.append(self.keystore.getServerInfo(lastHop))
- msg = mixminion.BuildMessage.buildForwardMessage(
- payload, routingType, routingInfo, servers1, servers2,
- self.prng)
- return msg, servers1[0]
+ # FFFF This is only a temporary solution. It needs to get
+ # FFFF rethought, or refactored into ServerInfo, or something.
+ if routingType == SMTP_TYPE:
+ ok = lastServer['Delivery/SMTP'].get('Version',None)
+ if not ok:
+ raise MixError("Last hop doesn't support SMTP")
+ elif routingType == MBOX_TYPE:
+ ok = lastServer['Delivery/MBOX'].get('Version',None)
+ if not ok:
+ raise MixError("Last hop doesn't support MBOX")
+ else:
+ servers2.append(self.keystore.getServerInfo(lastHop))
+ msg = mixminion.BuildMessage.buildForwardMessage(
+ payload, routingType, routingInfo, servers1, servers2,
+ self.prng)
+ return msg, servers1[0]
def sendMessages(self, msgList, server):
- """Given a list of packets and a ServerInfo object, sends the
- packets to the server via MMTP"""
- con = mixminion.MMTPClient.BlockingClientConnection(server.getAddr(),
- server.getPort(),
- server.getKeyID())
- try:
- con.connect()
- for msg in msgList:
- con.sendPacket(msg)
- finally:
- con.shutdown()
+ """Given a list of packets and a ServerInfo object, sends the
+ packets to the server via MMTP"""
+ con = mixminion.MMTPClient.BlockingClientConnection(server.getAddr(),
+ server.getPort(),
+ server.getKeyID())
+ try:
+ con.connect()
+ for msg in msgList:
+ con.sendPacket(msg)
+ finally:
+ con.shutdown()
def parseAddress(s):
"""Parse and validate an address; takes a string, and returns an Address
@@ -317,43 +317,43 @@
Accepts strings of the format:
mbox:<mailboxname>@<server>
OR smtp:<email address>
- OR <email address> (smtp is implicit)
- OR drop
- OR 0x<routing type>:<routing info>
+ OR <email address> (smtp is implicit)
+ OR drop
+ OR 0x<routing type>:<routing info>
"""
# ???? Should this should get refactored into clientmodules, or someplace?
if s.lower() == 'drop':
- return Address(DROP_TYPE, None, None)
+ return Address(DROP_TYPE, None, None)
elif s.lower() == 'test':
- return Address(0xFFFE, "", None)
+ return Address(0xFFFE, "", None)
elif ':' not in s:
- if isSMTPMailbox(s):
- return Address(SMTP_TYPE, s, None)
- else:
- raise ParseError("Can't parse address %s"%s)
+ if isSMTPMailbox(s):
+ return Address(SMTP_TYPE, s, None)
+ else:
+ raise ParseError("Can't parse address %s"%s)
tp,val = s.split(':', 1)
tp = tp.lower()
if tp.startswith("0x"):
- try:
- tp = int(tp[2:], 16)
- except ValueError:
- raise ParseError("Invalid hexidecimal value %s"%tp)
- if not (0x0000 <= tp <= 0xFFFF):
- raise ParseError("Invalid type: 0x%04x"%tp)
- return Address(tp, val, None)
+ try:
+ tp = int(tp[2:], 16)
+ except ValueError:
+ raise ParseError("Invalid hexidecimal value %s"%tp)
+ if not (0x0000 <= tp <= 0xFFFF):
+ raise ParseError("Invalid type: 0x%04x"%tp)
+ return Address(tp, val, None)
elif tp == 'mbox':
- if "@" in val:
+ if "@" in val:
mbox, server = val.split("@",1)
return Address(MBOX_TYPE, parseMBOXInfo(mbox).pack(), server)
else:
- return Address(MBOX_TYPE, parseMBOXInfo(val).pack(), None)
+ return Address(MBOX_TYPE, parseMBOXInfo(val).pack(), None)
elif tp == 'smtp':
- # May raise ParseError
- return Address(SMTP_TYPE, parseSMTPInfo(val).pack(), None)
+ # May raise ParseError
+ return Address(SMTP_TYPE, parseSMTPInfo(val).pack(), None)
elif tp == 'test':
- return Address(0xFFFE, val, None)
+ return Address(0xFFFE, val, None)
else:
- raise ParseError("Unrecognized address type: %s"%s)
+ raise ParseError("Unrecognized address type: %s"%s)
class Address:
"""Represents the target address for a Mixminion message.
@@ -361,31 +361,31 @@
the last hop, and (optionally) a server to use as the last hop.
"""
def __init__(self, exitType, exitAddress, lastHop=None):
- self.exitType = exitType
- self.exitAddress = exitAddress
- self.lastHop = lastHop
+ self.exitType = exitType
+ self.exitAddress = exitAddress
+ self.lastHop = lastHop
def getRouting(self):
- return self.exitType, self.exitAddress, self.lastHop
+ return self.exitType, self.exitAddress, self.lastHop
def readConfigFile(configFile):
try:
- return ClientConfig(fname=configFile)
+ return ClientConfig(fname=configFile)
except (IOError, OSError), e:
- print >>sys.stderr, "Error reading configuration file %r:"%configFile
- print >>sys.stderr, " ", str(e)
- sys.exit(1)
+ print >>sys.stderr, "Error reading configuration file %r:"%configFile
+ print >>sys.stderr, " ", str(e)
+ sys.exit(1)
except ConfigError, e:
- print >>sys.stderr, "Error in configuration file %r"%configFile
- print >>sys.stderr, str(e)
- sys.exit(1)
+ print >>sys.stderr, "Error in configuration file %r"%configFile
+ print >>sys.stderr, str(e)
+ sys.exit(1)
return None #suppress pychecker warning
# NOTE: This isn't anything LIKE the final client interface. Many or all
# options will change between now and 1.0.0
def runClient(cmd, args):
options, args = getopt.getopt(args, "hvf:i:t:",
- ["help", "verbose", "config=", "input=",
- "path1=", "path2=", "to="])
+ ["help", "verbose", "config=", "input=",
+ "path1=", "path2=", "to="])
configFile = '~/.mixminionrc'
usage = 0
inFile = "-"
@@ -394,52 +394,52 @@
path2 = []
address = None
for opt,val in options:
- if opt in ('-h', '--help'):
- usage=1
- elif opt in ('-f', '--config'):
- configFile = val
- elif opt in ('-i', '--input'):
- inFile = val
- elif opt in ('-v', '--verbose'):
- verbose = 1
- elif opt == '--path1':
- path1.extend(val.split(","))
- elif opt == '--path2':
- path2.extend(val.split(","))
- elif opt in ('-t', '--to'):
- address = parseAddress(val)
+ if opt in ('-h', '--help'):
+ usage=1
+ elif opt in ('-f', '--config'):
+ configFile = val
+ elif opt in ('-i', '--input'):
+ inFile = val
+ elif opt in ('-v', '--verbose'):
+ verbose = 1
+ elif opt == '--path1':
+ path1.extend(val.split(","))
+ elif opt == '--path2':
+ path2.extend(val.split(","))
+ elif opt in ('-t', '--to'):
+ address = parseAddress(val)
if args:
- print >>sys.stderr, "Unexpected options."
- usage = 1
+ print >>sys.stderr, "Unexpected options."
+ usage = 1
if not path1:
- print >>sys.stderr, "First leg of path was not specified"
- usage = 1
+ print >>sys.stderr, "First leg of path was not specified"
+ usage = 1
if not path2:
- print >>sys.stderr, "Second leg of path was not specified"
- usage = 1
+ print >>sys.stderr, "Second leg of path was not specified"
+ usage = 1
if address is None:
- print >>sys.stderr, "No recipient specified"
- usage = 1
+ print >>sys.stderr, "No recipient specified"
+ usage = 1
if usage:
- print >>sys.stderr, """\
+ print >>sys.stderr, """\
Usage: %s [-h] [-v] [-f configfile] [-i inputfile]
[--path1=server1,server2,...]
[--path2=server1,server2,...] [-t <address>]"""%cmd
- sys.exit(1)
+ sys.exit(1)
if configFile is None:
- configFile = os.environ.get("MIXMINIONRC", None)
- if configFile is None:
- configFile = "~/.mixminionrc"
+ configFile = os.environ.get("MIXMINIONRC", None)
+ if configFile is None:
+ configFile = "~/.mixminionrc"
configFile = os.path.expanduser(configFile)
if not os.path.exists(configFile):
- installDefaultConfig(configFile)
+ installDefaultConfig(configFile)
config = readConfigFile(configFile)
LOG.configure(config)
if verbose:
- LOG.setMinSeverity("DEBUG")
+ LOG.setMinSeverity("DEBUG")
LOG.debug("Configuring client")
mixminion.Common.configureShredCommand(config)
@@ -448,9 +448,9 @@
client = MixminionClient(config)
if inFile == '-':
- f = sys.stdin
+ f = sys.stdin
else:
- f = open(inFile, 'r')
+ f = open(inFile, 'r')
payload = f.read()
f.close()
@@ -460,26 +460,26 @@
options, args = getopt.getopt(args, "hf:", ['help', 'config='])
configFile = None
for o,v in options:
- if o in ('-h', '--help'):
- print "Usage %s [--help] [--config=configFile]"
- sys.exit(1)
- elif o in ('-f', '--config'):
- configFile = v
+ if o in ('-h', '--help'):
+ print "Usage %s [--help] [--config=configFile]"
+ sys.exit(1)
+ elif o in ('-f', '--config'):
+ configFile = v
# XXXX duplicate code; refactor into separate method.
if configFile is None:
- configFile = os.environ.get("MIXMINIONRC", None)
- if configFile is None:
- configFile = "~/.mixminionrc"
+ configFile = os.environ.get("MIXMINIONRC", None)
+ if configFile is None:
+ configFile = "~/.mixminionrc"
configFile = os.path.expanduser(configFile)
if not os.path.exists(configFile):
- installDefaultConfig(configFile)
+ installDefaultConfig(configFile)
config = readConfigFile(configFile)
userdir = os.path.expanduser(config['User']['UserDir'])
createPrivateDir(os.path.join(userdir, 'servers'))
keystore = TrivialKeystore(os.path.join(userdir,"servers"))
-
+
for line in keystore.listServers():
- print line
+ print line
Index: Common.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Common.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- Common.py 16 Dec 2002 01:37:19 -0000 1.35
+++ Common.py 16 Dec 2002 02:40:11 -0000 1.36
@@ -6,11 +6,11 @@
Common functionality and utility code for Mixminion"""
__all__ = [ 'LOG', 'LogStream', 'MixError', 'MixFatalError',
- 'MixProtocolError', 'ceilDiv', 'checkPrivateDir',
- 'createPrivateDir', 'floorDiv', 'formatBase64', 'formatDate',
- 'formatTime', 'installSignalHandlers', 'isSMTPMailbox', 'mkgmtime',
- 'onReset', 'onTerminate', 'previousMidnight', 'secureDelete',
- 'stringContains', 'waitForChildren' ]
+ 'MixProtocolError', 'ceilDiv', 'checkPrivateDir',
+ 'createPrivateDir', 'floorDiv', 'formatBase64', 'formatDate',
+ 'formatTime', 'installSignalHandlers', 'isSMTPMailbox', 'mkgmtime',
+ 'onReset', 'onTerminate', 'previousMidnight', 'secureDelete',
+ 'stringContains', 'waitForChildren' ]
import base64
import calendar
@@ -80,15 +80,15 @@
# String containing all printing ascii characters, and all characters that
# may be used in an extended charset.
_P_ASCII_CHARS_HIGH = "\t\n\v\r"+"".join(map(chr, range(0x20, 0x7F)+
- range(0x80, 0xFF)))
+ range(0x80, 0xFF)))
def isPrintingAscii(s,allowISO=0):
"""Return true iff every character in s is a printing ascii character.
If allowISO is true, also permit characters between 0x80 and 0xFF."""
if allowISO:
- return len(s.translate(_ALLCHARS, _P_ASCII_CHARS_HIGH)) == 0
+ return len(s.translate(_ALLCHARS, _P_ASCII_CHARS_HIGH)) == 0
else:
- return len(s.translate(_ALLCHARS, _P_ASCII_CHARS)) == 0
+ return len(s.translate(_ALLCHARS, _P_ASCII_CHARS)) == 0
def stripSpace(s, space=" \t\v\n"):
"""Remove all whitespace from s."""
@@ -103,12 +103,12 @@
"""Create a directory, and all parent directories, checking permissions
as we go along. All superdirectories must be owned by root or us."""
if not os.path.exists(d):
- if nocreate:
- raise MixFatalError("Nonexistent directory %s" % d)
- try:
- os.makedirs(d, 0700)
- except OSError:
- raise MixFatalError("Unable to create directory %s" % d)
+ if nocreate:
+ raise MixFatalError("Nonexistent directory %s" % d)
+ try:
+ os.makedirs(d, 0700)
+ except OSError:
+ raise MixFatalError("Unable to create directory %s" % d)
checkPrivateDir(d)
@@ -120,41 +120,41 @@
me = os.getuid()
if not os.path.exists(d):
- raise MixFatalError("Directory %s does not exist" % d)
+ raise MixFatalError("Directory %s does not exist" % d)
if not os.path.isdir(d):
- raise MixFatalError("%s is not a directory" % d)
+ raise MixFatalError("%s is not a directory" % d)
st = os.stat(d)
# check permissions
if st[stat.ST_MODE] & 0777 != 0700:
- raise MixFatalError("Directory %s must be mode 0700" % d)
+ raise MixFatalError("Directory %s must be mode 0700" % d)
if st[stat.ST_UID] != me:
- raise MixFatalError("Directory %s has must have owner %s" %(d, me))
+ raise MixFatalError("Directory %s has must have owner %s" %(d, me))
if not recurse:
- return
+ return
# Check permissions on parents.
while 1:
- parent = os.path.split(d)[0]
- if parent == d:
- return
- d = parent
+ parent = os.path.split(d)[0]
+ if parent == d:
+ return
+ d = parent
- st = os.stat(d)
- mode = st[stat.ST_MODE]
- owner = st[stat.ST_UID]
- if owner not in (0, me):
- raise MixFatalError("Bad owner (uid=%s) on directory %s"
- % (owner, d))
- if (mode & 02) and not (mode & stat.S_ISVTX):
- raise MixFatalError("Bad mode (%o) on directory %s" %(mode, d))
+ st = os.stat(d)
+ mode = st[stat.ST_MODE]
+ owner = st[stat.ST_UID]
+ if owner not in (0, me):
+ raise MixFatalError("Bad owner (uid=%s) on directory %s"
+ % (owner, d))
+ if (mode & 02) and not (mode & stat.S_ISVTX):
+ raise MixFatalError("Bad mode (%o) on directory %s" %(mode, d))
- if (mode & 020) and not (mode & stat.S_ISVTX):
- # FFFF We may want to give an even stronger error here.
- LOG.warn("Iffy mode %o on directory %s (Writable by gid %s)",
- mode, d, st[stat.ST_GID])
+ if (mode & 020) and not (mode & stat.S_ISVTX):
+ # FFFF We may want to give an even stronger error here.
+ LOG.warn("Iffy mode %o on directory %s (Writable by gid %s)",
+ mode, d, st[stat.ST_GID])
#----------------------------------------------------------------------
# Secure filesystem operations.
@@ -181,7 +181,7 @@
if os.path.exists("/usr/bin/shred"):
cmd, opts = "/usr/bin/shred", ["-uz", "-n0"]
else:
- # Use built-in _overwriteFile
+ # Use built-in _overwriteFile
cmd, opts = None, None
_SHRED_CMD, _SHRED_OPTS = cmd, opts
@@ -198,21 +198,21 @@
global _BLKSIZE
global _NILSTR
if not _BLKSIZE:
- #???? this assumes that all filesystems we are using have the same
- #???? block size.
- if hasattr(os, 'statvfs'):
- _BLKSIZE = os.statvfs(f)[statvfs.F_BSIZE]
- else:
- _BLKSIZE = 8192 # ???? Safe guess?
- _NILSTR = '\x00' * _BLKSIZE
+ #???? this assumes that all filesystems we are using have the same
+ #???? block size.
+ if hasattr(os, 'statvfs'):
+ _BLKSIZE = os.statvfs(f)[statvfs.F_BSIZE]
+ else:
+ _BLKSIZE = 8192 # ???? Safe guess?
+ _NILSTR = '\x00' * _BLKSIZE
fd = os.open(f, os.O_WRONLY)
try:
- size = os.fstat(fd)[stat.ST_SIZE]
- blocks = ceilDiv(size, _BLKSIZE)
- for _ in xrange(blocks):
- os.write(fd, _NILSTR)
+ size = os.fstat(fd)[stat.ST_SIZE]
+ blocks = ceilDiv(size, _BLKSIZE)
+ for _ in xrange(blocks):
+ os.write(fd, _NILSTR)
finally:
- os.close(fd)
+ os.close(fd)
def secureDelete(fnames, blocking=0):
"""Given a list of filenames, removes the contents of all of those
@@ -256,7 +256,7 @@
if not _SHRED_CMD:
for f in fnames:
- _overwriteFile(f)
+ _overwriteFile(f)
os.unlink(f)
return None
@@ -267,8 +267,8 @@
# Some systems are unhappy when you call them with too many options.
for i in xrange(0, len(fnames), 250-len(_SHRED_OPTS)):
- files = fnames[i:i+250-len(_SHRED_OPTS)]
- os.spawnl(mode, _SHRED_CMD, _SHRED_CMD, *(_SHRED_OPTS+files))
+ files = fnames[i:i+250-len(_SHRED_OPTS)]
+ os.spawnl(mode, _SHRED_CMD, _SHRED_CMD, *(_SHRED_OPTS+files))
#----------------------------------------------------------------------
# Logging
@@ -287,30 +287,30 @@
# file -- a file object, or None if the file is closed.
# fname -- this log's associated filename
def __init__(self, fname):
- "Create a new FileLogHandler to append messages to fname"
+ "Create a new FileLogHandler to append messages to fname"
self.file = None
self.fname = fname
self.reset()
def reset(self):
- """Close and reopen our underlying file. This behavior is needed
- to implement log rotation."""
+ """Close and reopen our underlying file. This behavior is needed
+ to implement log rotation."""
if self.file is not None:
self.file.close()
- try:
- parent = os.path.split(self.fname)[0]
- if not os.path.exists(parent):
- createPrivateDir(parent)
- self.file = open(self.fname, 'a')
- except OSError:
- self.file = None
- raise MixError("Unable to open log file %r"%self.fname)
+ try:
+ parent = os.path.split(self.fname)[0]
+ if not os.path.exists(parent):
+ createPrivateDir(parent)
+ self.file = open(self.fname, 'a')
+ except OSError:
+ self.file = None
+ raise MixError("Unable to open log file %r"%self.fname)
def close(self):
- "Close the underlying file"
+ "Close the underlying file"
self.file.close()
def write(self, severity, message):
- """(Used by Log: write a message to this log handler.)"""
- if self.file is None:
- return
+ """(Used by Log: write a message to this log handler.)"""
+ if self.file is None:
+ return
print >> self.file, "%s [%s] %s" % (_logtime(), severity, message)
self.file.flush()
@@ -318,12 +318,12 @@
"""Helper class for logging: directs all log messages to a stderr-like
file object"""
def __init__(self, file):
- "Create a new _ConsoleLogHandler attached to a given file."""
+ "Create a new _ConsoleLogHandler attached to a given file."""
self.file = file
def reset(self): pass
def close(self): pass
def write(self, severity, message):
- """(Used by Log: write a message to this log handler.)"""
+ """(Used by Log: write a message to this log handler.)"""
print >> self.file, "%s [%s] %s" % (_logtime(), severity, message)
# Map from log severity name to numeric values
@@ -343,12 +343,12 @@
TRACE: hyperverbose mode; used for debugging fiddly
little details. This is a security risk.
DEBUG: very verbose mode; used for tracing connections
- and messages through the system. This is a security risk.
+ and messages through the system. This is a security risk.
INFO: non-critical events.
- WARN: recoverable errors
- ERROR: nonrecoverable errors that affect only a single
+ WARN: recoverable errors
+ ERROR: nonrecoverable errors that affect only a single
message or a connection.
- FATAL: nonrecoverable errors that affect the entire system.
+ FATAL: nonrecoverable errors that affect the entire system.
In practise, we instantiate only a single instance of this class,
accessed as mixminion.Common.LOG."""
@@ -356,14 +356,14 @@
# handlers: a list of logHandler objects.
# severity: a severity below which log messages are ignored.
def __init__(self, minSeverity):
- """Create a new Log object that ignores all message less severe than
- minSeverity, and sends its output to stderr."""
- self.configure(None)
- self.setMinSeverity(minSeverity)
+ """Create a new Log object that ignores all message less severe than
+ minSeverity, and sends its output to stderr."""
+ self.configure(None)
+ self.setMinSeverity(minSeverity)
def configure(self, config):
- """Set up this Log object based on a ServerConfig or ClientConfig
- object"""
+ """Set up this Log object based on a ServerConfig or ClientConfig
+ object"""
# XXXX001 Don't EchoLogMessages when NoDaemon==0.
self.handlers = []
if config == None or not config.has_section("Server"):
@@ -376,66 +376,66 @@
homedir = config['Server']['Homedir']
if homedir:
logfile = os.path.join(homedir, "log")
- self.addHandler(_ConsoleLogHandler(sys.stderr))
+ self.addHandler(_ConsoleLogHandler(sys.stderr))
if logfile:
- try:
- self.addHandler(_FileLogHandler(logfile))
- except MixError, e:
- self.error(str(e))
+ try:
+ self.addHandler(_FileLogHandler(logfile))
+ except MixError, e:
+ self.error(str(e))
if logfile and not (config['Server'].get('EchoMessages',0) and
- config['Server'].get('NoDaemon',0)):
- del self.handlers[0]
+ config['Server'].get('NoDaemon',0)):
+ del self.handlers[0]
def setMinSeverity(self, minSeverity):
- """Sets the minimum severity of messages to be logged.
- minSeverity -- the string representation of a severity level."""
+ """Sets the minimum severity of messages to be logged.
+ minSeverity -- the string representation of a severity level."""
self.severity = _SEVERITIES.get(minSeverity, 1)
def getMinSeverity(self):
- """Return a string representation of this log's minimum severity
- level."""
+ """Return a string representation of this log's minimum severity
+ level."""
for k,v in _SEVERITIES.items():
if v == self.severity:
return k
- return "INFO"
+ return "INFO"
def addHandler(self, handler):
- """Add a LogHandler object to the list of objects that receive
- messages from this log."""
+ """Add a LogHandler object to the list of objects that receive
+ messages from this log."""
self.handlers.append(handler)
def reset(self):
- """Flush and re-open all logs."""
+ """Flush and re-open all logs."""
for h in self.handlers:
- try:
- h.reset()
- except MixError, e:
- if len(self.handlers) > 1:
- self.error(str(e))
- else:
- print >>sys.stderr, "Unable to reset log system"
+ try:
+ h.reset()
+ except MixError, e:
+ if len(self.handlers) > 1:
+ self.error(str(e))
+ else:
+ print >>sys.stderr, "Unable to reset log system"
def close(self):
- """Close all logs"""
+ """Close all logs"""
for h in self.handlers:
h.close()
def log(self, severity, message, *args):
- """Send a message of a given severity to the log. If additional
- arguments are provided, write 'message % args'. """
- self._log(severity, message, args)
+ """Send a message of a given severity to the log. If additional
+ arguments are provided, write 'message % args'. """
+ self._log(severity, message, args)
def _log(self, severity, message, args):
- """Helper method: If we aren't ignoring messages of level 'severity',
- then send message%args to all the underlying log handlers."""
+ """Helper method: If we aren't ignoring messages of level 'severity',
+ then send message%args to all the underlying log handlers."""
# Enable this block to bail early in production versions
#if _SEVERITIES.get(severity, 100) < self.severity:
# return
- if args is None:
- m = message
- else:
- m = message % args
+ if args is None:
+ m = message
+ else:
+ m = message % args
# Enable this block to debug message formats.
if _SEVERITIES.get(severity, 100) < self.severity:
@@ -445,53 +445,53 @@
h.write(severity, m)
def trace(self, message, *args):
- "Write a trace (hyperverbose) message to the log"
+ "Write a trace (hyperverbose) message to the log"
self.log("TRACE", message, *args)
def debug(self, message, *args):
- "Write a debug (verbose) message to the log"
+ "Write a debug (verbose) message to the log"
self.log("DEBUG", message, *args)
def info(self, message, *args):
- "Write an info (non-error) message to the log"
+ "Write an info (non-error) message to the log"
self.log("INFO", message, *args)
def warn(self, message, *args):
- "Write a warn (recoverable error) message to the log"
+ "Write a warn (recoverable error) message to the log"
self.log("WARN", message, *args)
def error(self, message, *args):
- "Write an error (message loss error) message to the log"
+ "Write an error (message loss error) message to the log"
self.log("ERROR", message, *args)
def fatal(self, message, *args):
- "Write a fatal (unrecoverable system error) message to the log"
+ "Write a fatal (unrecoverable system error) message to the log"
self.log("FATAL", message, *args)
def log_exc(self, severity, (exclass, ex, tb), message=None, *args):
- """Write an exception and stack trace to the log. If message and
- args are provided, use them as an explanitory message; otherwise,
- introduce the message as "Unexpected exception".
+ """Write an exception and stack trace to the log. If message and
+ args are provided, use them as an explanitory message; otherwise,
+ introduce the message as "Unexpected exception".
- This should usually be called as
- LOG.log_exc('ERROR', sys.exc_info(), message, args...)
- """
- if message is not None:
- self.log(severity, message, *args)
- elif tb is not None:
- filename = tb.tb_frame.f_code.co_filename
- self.log(severity, "Unexpected exception in %s", filename)
- else:
- self.log(severity, "Unexpected exception")
+ This should usually be called as
+ LOG.log_exc('ERROR', sys.exc_info(), message, args...)
+ """
+ if message is not None:
+ self.log(severity, message, *args)
+ elif tb is not None:
+ filename = tb.tb_frame.f_code.co_filename
+ self.log(severity, "Unexpected exception in %s", filename)
+ else:
+ self.log(severity, "Unexpected exception")
- formatted = traceback.format_exception(exclass, ex, tb)
- formatted[1:] = [ " %s" % line for line in formatted[1:] ]
- indented = "".join(formatted)
- if indented.endswith('\n'):
- indented = indented[:-1]
- self._log(severity, indented, None)
+ formatted = traceback.format_exception(exclass, ex, tb)
+ formatted[1:] = [ " %s" % line for line in formatted[1:] ]
+ indented = "".join(formatted)
+ if indented.endswith('\n'):
+ indented = indented[:-1]
+ self._log(severity, indented, None)
def error_exc(self, (exclass, ex, tb), message=None, *args):
- "Same as log_exc, but logs an error message."
- self.log_exc("ERROR", (exclass, ex, tb), message, *args)
+ "Same as log_exc, but logs an error message."
+ self.log_exc("ERROR", (exclass, ex, tb), message, *args)
def fatal_exc(self, (exclass, ex, tb), message=None, *args):
- "Same as log_exc, but logs a fatal message."
- self.log_exc("FATAL", (exclass, ex, tb), message, *args)
+ "Same as log_exc, but logs a fatal message."
+ self.log_exc("FATAL", (exclass, ex, tb), message, *args)
# The global 'Log' instance for the mixminion client or server.
LOG = Log('WARN')
@@ -504,10 +504,10 @@
prints on the floor.
"""
def __init__(self, name, severity):
- self.name = name
- self.severity = severity
+ self.name = name
+ self.severity = severity
def write(self, s):
- LOG.log(self.severity, "->%s: %s", self.name, s)
+ LOG.log(self.severity, "->%s: %s", self.name, s)
def flush(self): pass
def close(self): pass
@@ -532,11 +532,11 @@
"""Given a time in seconds since the epoch, returns a time value in the
format used by server descriptors (YYYY/MM/DD HH:MM:SS) in GMT"""
if localtime:
- gmt = time.localtime(when)
+ gmt = time.localtime(when)
else:
- gmt = time.gmtime(when)
+ gmt = time.gmtime(when)
return "%04d/%02d/%02d %02d:%02d:%02d" % (
- gmt[0],gmt[1],gmt[2], gmt[3],gmt[4],gmt[5])
+ gmt[0],gmt[1],gmt[2], gmt[3],gmt[4],gmt[5])
def formatDate(when):
"""Given a time in seconds since the epoch, returns a date value in the
Index: Crypto.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Crypto.py,v
retrieving revision 1.27
retrieving revision 1.28
diff -u -d -r1.27 -r1.28
--- Crypto.py 12 Dec 2002 19:56:46 -0000 1.27
+++ Crypto.py 16 Dec 2002 02:40:11 -0000 1.28
@@ -23,7 +23,7 @@
'pk_decode_private_key', 'pk_decrypt', 'pk_encode_private_key',
'pk_encrypt', 'pk_from_modulus', 'pk_generate', 'pk_get_modulus',
'pk_sign', 'prng', 'sha1', 'strxor', 'trng',
- 'AES_KEY_LEN', 'DIGEST_LEN', 'HEADER_SECRET_MODE', 'PRNG_MODE',
+ 'AES_KEY_LEN', 'DIGEST_LEN', 'HEADER_SECRET_MODE', 'PRNG_MODE',
'RANDOM_JUNK_MODE', 'HEADER_ENCRYPT_MODE', 'APPLICATION_KEY_MODE',
'PAYLOAD_ENCRYPT_MODE', 'HIDE_HEADER_MODE' ]
@@ -44,7 +44,7 @@
# Try to read /dev/urandom
trng(1)
except MixFatalError:
- raise
+ raise
except:
raise MixFatalError("Error initializing entropy source")
openssl_seed(40)
@@ -94,12 +94,12 @@
# may look slow, but it contributes only .7% to the total time for
# LIONESS.
right = _ml.aes_ctr128_crypt(
- _ml.aes_key(_ml.sha1("".join((key1,left,key1)))[:AES_KEY_LEN]),
- right, 0)
+ _ml.aes_key(_ml.sha1("".join((key1,left,key1)))[:AES_KEY_LEN]),
+ right, 0)
left = _ml.strxor(left, _ml.sha1("".join((key2,right,key2))))
right = _ml.aes_ctr128_crypt(
- _ml.aes_key(_ml.sha1("".join((key3,left,key3)))[:AES_KEY_LEN]),
- right, 0)
+ _ml.aes_key(_ml.sha1("".join((key3,left,key3)))[:AES_KEY_LEN]),
+ right, 0)
left = _ml.strxor(left, _ml.sha1("".join((key4,right,key4))))
# You could write the above as:
@@ -134,12 +134,12 @@
# Equivalent-but-faster version:
left = _ml.strxor(left, _ml.sha1("".join((key4,right,key4))))
right = _ml.aes_ctr128_crypt(
- _ml.aes_key(_ml.sha1("".join((key3,left, key3)))[:AES_KEY_LEN]),
- right, 0)
+ _ml.aes_key(_ml.sha1("".join((key3,left, key3)))[:AES_KEY_LEN]),
+ right, 0)
left = _ml.strxor(left, _ml.sha1("".join((key2,right,key2))))
right = _ml.aes_ctr128_crypt(
- _ml.aes_key(_ml.sha1("".join((key1,left, key1)))[:AES_KEY_LEN]),
- right, 0)
+ _ml.aes_key(_ml.sha1("".join((key1,left, key1)))[:AES_KEY_LEN]),
+ right, 0)
return left + right
@@ -482,29 +482,29 @@
return res
def shuffle(self, lst, n=None):
- """Rearranges the elements of lst so that the first n elements
- are randomly chosen from lst. Returns the first n elements.
- (Other elements are still in lst, but may be in a nonrandom
- order.) If n is None, shuffles and returns the entire list"""
+ """Rearranges the elements of lst so that the first n elements
+ are randomly chosen from lst. Returns the first n elements.
+ (Other elements are still in lst, but may be in a nonrandom
+ order.) If n is None, shuffles and returns the entire list"""
size = len(lst)
- if n is None:
- n = size
- else:
- n = min(n, size)
+ if n is None:
+ n = size
+ else:
+ n = min(n, size)
- if n == size:
- series = xrange(n-1)
- else:
- series = xrange(n)
+ if n == size:
+ series = xrange(n-1)
+ else:
+ series = xrange(n)
# This permutation algorithm yields all permutation with equal
# probability (assuming a good rng); others do not.
- getInt = self.getInt
+ getInt = self.getInt
for i in series:
swap = i+getInt(size-i)
- lst[swap],lst[i] = lst[i],lst[swap]
+ lst[swap],lst[i] = lst[i],lst[swap]
- return lst[:n]
+ return lst[:n]
def getInt(self, max):
"""Returns a random integer i s.t. 0 <= i < max.
@@ -512,33 +512,33 @@
The value of max must be less than 2**30."""
# FFFF This implementation is about 2-4x as good as the last one, but
- # FFFF still could be better. It's faster than getFloat()*max.
+ # FFFF still could be better. It's faster than getFloat()*max.
- # FFFF (This code assumes that integers are at least 32 bits. Maybe
- # FFFF we could do better.)
+ # FFFF (This code assumes that integers are at least 32 bits. Maybe
+ # FFFF we could do better.)
assert 0 < max < 0x3fffffff
- _ord = ord
- while 1:
- # Get a random positive int between 0 and 0x7fffffff.
- b = self.getBytes(4)
- o = (((((((_ord(b[0])&0x7f)<<8) +
- _ord(b[1]))<<8) +
- _ord(b[2]))<<8) +
- _ord(b[3]))
- # Retry if we got a value that would fall in an incomplete
- # run of 'max' elements.
- if 0x7fffffff - max >= o:
- return o % max
+ _ord = ord
+ while 1:
+ # Get a random positive int between 0 and 0x7fffffff.
+ b = self.getBytes(4)
+ o = (((((((_ord(b[0])&0x7f)<<8) +
+ _ord(b[1]))<<8) +
+ _ord(b[2]))<<8) +
+ _ord(b[3]))
+ # Retry if we got a value that would fall in an incomplete
+ # run of 'max' elements.
+ if 0x7fffffff - max >= o:
+ return o % max
def getFloat(self):
- """Return a floating-point number between 0 and 1."""
- b = self.getBytes(4)
- _ord = ord
- o = ((((((_ord(b[0])&0x7f)<<8) + _ord(b[1]))<<8) +
- _ord(b[2]))<<8) + _ord(b[3])
- #return o / float(0x7fffffff)
- return o / 2147483647.0
+ """Return a floating-point number between 0 and 1."""
+ b = self.getBytes(4)
+ _ord = ord
+ o = ((((((_ord(b[0])&0x7f)<<8) + _ord(b[1]))<<8) +
+ _ord(b[2]))<<8) + _ord(b[3])
+ #return o / float(0x7fffffff)
+ return o / 2147483647.0
def _prng(self, n):
"""Abstract method: Must be overridden to return n bytes of fresh
@@ -601,42 +601,42 @@
if config is not None:
requestedFile = config['Host'].get('EntropySource', None)
else:
- requestedFile = None
+ requestedFile = None
# Build a list of candidates
- defaults = PLATFORM_TRNG_DEFAULTS.get(sys.platform,
- PLATFORM_TRNG_DEFAULTS['***'])
+ defaults = PLATFORM_TRNG_DEFAULTS.get(sys.platform,
+ PLATFORM_TRNG_DEFAULTS['***'])
files = [ requestedFile ] + defaults
# Now find the first of our candidates that exists and is a character
# device.
randFile = None
for file in files:
- if file is None:
- continue
+ if file is None:
+ continue
- verbose = 1#(file == requestedFile)
- if not os.path.exists(file):
- if verbose:
- LOG.error("No such file as %s", file)
- else:
- st = os.stat(file)
- if not (st[stat.ST_MODE] & stat.S_IFCHR):
- if verbose:
- LOG.error("Entropy source %s isn't a character device",
- file)
- else:
- randFile = file
- break
+ verbose = 1#(file == requestedFile)
+ if not os.path.exists(file):
+ if verbose:
+ LOG.error("No such file as %s", file)
+ else:
+ st = os.stat(file)
+ if not (st[stat.ST_MODE] & stat.S_IFCHR):
+ if verbose:
+ LOG.error("Entropy source %s isn't a character device",
+ file)
+ else:
+ randFile = file
+ break
if randFile is None and _TRNG_FILENAME is None:
LOG.fatal("No entropy source available")
raise MixFatalError("No entropy source available")
elif randFile is None:
LOG.warn("Falling back to previous entropy source %s",
- _TRNG_FILENAME)
+ _TRNG_FILENAME)
else:
- LOG.info("Setting entropy source to %r", randFile)
+ LOG.info("Setting entropy source to %r", randFile)
_TRNG_FILENAME = randFile
class _TrueRNG(RNG):
@@ -648,13 +648,13 @@
RNG.__init__(self,n)
def _prng(self,n):
"Returns n fresh bytes from our true RNG."
- if _TRNG_FILENAME is None:
- configure_trng(None)
+ if _TRNG_FILENAME is None:
+ configure_trng(None)
- f = open(_TRNG_FILENAME, 'rb')
- d = f.read(n)
- f.close()
- return d
+ f = open(_TRNG_FILENAME, 'rb')
+ d = f.read(n)
+ f.close()
+ return d
# Global _TrueRNG instance, for use by trng().
_theTrueRNG = _TrueRNG(1024)
Index: MMTPClient.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPClient.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- MMTPClient.py 12 Dec 2002 19:56:46 -0000 1.13
+++ MMTPClient.py 16 Dec 2002 02:40:11 -0000 1.14
@@ -40,26 +40,26 @@
self.targetPort = targetPort
self.targetKeyID = targetKeyID
self.context = _ml.TLSContext_new()
- self.tls = None
- self.sock = None
+ self.tls = None
+ self.sock = None
def connect(self):
"""Negotiate the handshake and protocol."""
- # Connect to the server
+ # Connect to the server
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setblocking(1)
- LOG.debug("Connecting to %s:%s", self.targetIP, self.targetPort)
+ LOG.debug("Connecting to %s:%s", self.targetIP, self.targetPort)
- # Do the TLS handshaking
+ # Do the TLS handshaking
self.sock.connect((self.targetIP,self.targetPort))
LOG.debug("Handshaking with %s:%s",self.targetIP, self.targetPort)
self.tls = self.context.sock(self.sock.fileno())
# FFFF session resumption
self.tls.connect()
- LOG.debug("Connected.")
+ LOG.debug("Connected.")
- # Check the public key of the server to prevent man-in-the-middle
- # attacks.
+ # Check the public key of the server to prevent man-in-the-middle
+ # attacks.
peer_pk = self.tls.get_peer_cert_pk()
keyID = sha1(peer_pk.encode_key(public=1))
if self.targetKeyID is not None and (keyID != self.targetKeyID):
@@ -69,43 +69,43 @@
####
# Protocol negotiation
# 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.
- LOG.debug("Negotiatiating MMTP protocol")
+ # change our mind between now and a release candidate, and so we
+ # can obsolete betas come release time.
+ LOG.debug("Negotiatiating MMTP protocol")
self.tls.write("MMTP 0.1\r\n")
inp = self.tls.read(len("MMTP 0.1\r\n"))
if inp != "MMTP 0.1\r\n":
raise MixProtocolError("Protocol negotiation failed")
- LOG.debug("MMTP protocol negotated: version 0.1")
+ LOG.debug("MMTP protocol negotated: version 0.1")
def sendPacket(self, packet):
"""Send a single packet to a server."""
assert len(packet) == 1<<15
- LOG.debug("Sending packet")
- ##
- # We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
+ LOG.debug("Sending packet")
+ ##
+ # We write: "SEND\r\n", 28KB of data, and sha1(packet|"SEND").
self.tls.write("SEND\r\n")
self.tls.write(packet)
self.tls.write(sha1(packet+"SEND"))
- LOG.debug("Packet sent; waiting for ACK")
+ LOG.debug("Packet sent; waiting for ACK")
- # And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
+ # And we expect, "RECEIVED\r\n", and sha1(packet|"RECEIVED")
inp = self.tls.read(len("RECEIVED\r\n")+20)
if inp != "RECEIVED\r\n"+sha1(packet+"RECEIVED"):
raise MixProtocolError("Bad ACK received")
- LOG.debug("ACK received; packet successfully delivered")
+ LOG.debug("ACK received; packet successfully delivered")
# FFFF we need a sendJunkPacket method.
def shutdown(self):
"""Close this connection."""
- LOG.debug("Shutting down connection to %s:%s",
- self.targetIP, self.targetPort)
- if self.tls is not None:
- self.tls.shutdown()
- if self.sock is not None:
- self.sock.close()
- LOG.debug("Connection closed")
+ LOG.debug("Shutting down connection to %s:%s",
+ self.targetIP, self.targetPort)
+ if self.tls is not None:
+ self.tls.shutdown()
+ if self.sock is not None:
+ self.sock.close()
+ LOG.debug("Connection closed")
def sendMessages(targetIP, targetPort, targetKeyID, packetList):
"""Sends a list of messages to a server."""
Index: Main.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Main.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- Main.py 15 Dec 2002 03:45:30 -0000 1.14
+++ Main.py 16 Dec 2002 02:40:11 -0000 1.15
@@ -34,14 +34,14 @@
def filesAreSame(f1, f2):
"Return true if f1 and f2 are exactly the same file."
if os.path.normpath(f1) == os.path.normpath(f2):
- return 1
+ return 1
try:
# FFFF what happens on systems that (shudder) lack inodes?
- ino1 = os.stat(f1)[stat.ST_INO]
- ino2 = os.stat(f2)[stat.ST_INO]
- return ino1 and ino1 > 0 and ino1 == ino2
+ ino1 = os.stat(f1)[stat.ST_INO]
+ ino2 = os.stat(f2)[stat.ST_INO]
+ return ino1 and ino1 > 0 and ino1 == ino2
except OSError:
- return 0
+ return 0
def correctPath(myself):
"Given a command (sys.argv[0]), fix sys.path so 'import mixminion' works"
@@ -52,15 +52,15 @@
# If we can import mixminion.Main, we bail out early: let's not mess
# with anything.
try:
- __import__('mixminion.Main')
- return
+ __import__('mixminion.Main')
+ return
except ImportError:
- pass
+ pass
orig_cmd = myself
# First, resolve all links.
while os.path.islink(myself):
- myself = os.readlink(myself)
+ myself = os.readlink(myself)
# Now, the module ought to be living in x/y/z/mixminon/Foo.py.
# The "x/y/z" is the part we're interested in.
@@ -68,38 +68,38 @@
parentdir, miniondir = os.path.split(mydir)
if not miniondir == 'mixminion':
sys.stderr.write(("Bad mixminion installation:\n"+
- " I resolved %s to %s, but expected to find ../mixminion/Main.py\n")%(
- orig_cmd, myself))
+ " I resolved %s to %s, but expected to find ../mixminion/Main.py\n")%(
+ orig_cmd, myself))
# Now we check whether there's already an entry in sys.path. If not,
# we add the directory we found.
parentdir = os.path.normpath(parentdir)
foundEntry = 0
for pathEntry in sys.path:
- # There are intimations on Python-dev that sys.path may eventually
- # contain non-strings.
- if not isinstance(pathEntry, types.StringType):
- continue
- if os.path.normpath(pathEntry) == parentdir:
- foundEntry = 1; break
+ # There are intimations on Python-dev that sys.path may eventually
+ # contain non-strings.
+ if not isinstance(pathEntry, types.StringType):
+ continue
+ if os.path.normpath(pathEntry) == parentdir:
+ foundEntry = 1; break
- ent = os.path.join(pathEntry, 'mixminion', 'Main.py')
- if os.path.exists(ent) and filesAreSame(pathEntry, myself):
- foundEntry = 1; break
+ ent = os.path.join(pathEntry, 'mixminion', 'Main.py')
+ if os.path.exists(ent) and filesAreSame(pathEntry, myself):
+ foundEntry = 1; break
if not foundEntry:
- sys.stderr.write("Adding %s to PYTHONPATH\n" % parentdir)
- sys.path[0:0] = [ parentdir ]
+ sys.stderr.write("Adding %s to PYTHONPATH\n" % parentdir)
+ sys.path[0:0] = [ parentdir ]
# Finally, we make sure it all works.
try:
# We use __import__ here instead of 'import' so that we can stay
# parseable by Python 1.1. You're welcome.
- __import__('mixminion.Main')
+ __import__('mixminion.Main')
except ImportError, e:
- sys.stderr.write(str(e)+"\n")
- sys.stderr.write("Unable to find correct path for mixminion.\n")
- sys.exit(1)
+ sys.stderr.write(str(e)+"\n")
+ sys.stderr.write("Unable to find correct path for mixminion.\n")
+ sys.exit(1)
# Global map from command name to 2-tuples of (module_name, function_name).
# The function 'main' below uses this map to pick which module to import,
@@ -140,12 +140,12 @@
# Check whether we have a recognized command.
if len(args) == 1 or not _COMMANDS.has_key(args[1]):
- # FFFF we could do better in generating a usage message here.
- cmds = _COMMANDS.keys()
- cmds.sort()
- sys.stderr.write("Usage: %s {%s} [arguments]\n" %(
- args[0], "|".join(cmds)))
- sys.exit(1)
+ # FFFF we could do better in generating a usage message here.
+ cmds = _COMMANDS.keys()
+ cmds.sort()
+ sys.stderr.write("Usage: %s {%s} [arguments]\n" %(
+ args[0], "|".join(cmds)))
+ sys.exit(1)
# Read the module and function.
command_module, command_fn = _COMMANDS[args[1]]
Index: Packet.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Packet.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- Packet.py 12 Dec 2002 19:56:46 -0000 1.21
+++ Packet.py 16 Dec 2002 02:40:11 -0000 1.22
@@ -17,10 +17,10 @@
'parseSMTPInfo', 'parseMBOXInfo', 'ReplyBlock',
'parseReplyBlock', 'ENC_SUBHEADER_LEN', 'HEADER_LEN',
'PAYLOAD_LEN', 'MAJOR_NO', 'MINOR_NO', 'SECRET_LEN', 'TAG_LEN',
- 'SINGLETON_PAYLOAD_OVERHEAD', 'OAEP_OVERHEAD',
- 'FRAGMENT_PAYLOAD_OVERHEAD', 'ENC_FWD_OVERHEAD',
- 'DROP_TYPE', 'FWD_TYPE', 'SWAP_FWD_TYPE',
- 'SMTP_TYPE', 'MBOX_TYPE', 'MIN_EXIT_TYPE'
+ 'SINGLETON_PAYLOAD_OVERHEAD', 'OAEP_OVERHEAD',
+ 'FRAGMENT_PAYLOAD_OVERHEAD', 'ENC_FWD_OVERHEAD',
+ 'DROP_TYPE', 'FWD_TYPE', 'SWAP_FWD_TYPE',
+ 'SMTP_TYPE', 'MBOX_TYPE', 'MIN_EXIT_TYPE'
]
import struct
@@ -159,7 +159,7 @@
if rlen < len(ri):
ri = ri[:rlen]
if rt >= MIN_EXIT_TYPE and rlen < 20:
- raise ParseError("Subheader missing tag")
+ raise ParseError("Subheader missing tag")
return Subheader(major,minor,secret,digest,rt,ri,rlen)
def getTotalBlocksForRoutingInfoLen(bytes):
@@ -203,18 +203,18 @@
"routinglen=%(routinglen)r)")% self.__dict__
def getExitAddress(self):
- """Return the part of the routingInfo that contains the delivery
- address. (Requires that routingType is an exit type.)"""
- assert self.routingtype >= MIN_EXIT_TYPE
- assert len(self.routinginfo) >= TAG_LEN
- return self.routinginfo[TAG_LEN:]
+ """Return the part of the routingInfo that contains the delivery
+ address. (Requires that routingType is an exit type.)"""
+ assert self.routingtype >= MIN_EXIT_TYPE
+ assert len(self.routinginfo) >= TAG_LEN
+ return self.routinginfo[TAG_LEN:]
def getTag(self):
- """Return the part of the routingInfo that contains the decoding
- tag. (Requires that routingType is an exit type.)"""
- assert self.routingtype >= MIN_EXIT_TYPE
- assert len(self.routinginfo) >= TAG_LEN
- return self.routinginfo[:TAG_LEN]
+ """Return the part of the routingInfo that contains the decoding
+ tag. (Requires that routingType is an exit type.)"""
+ assert self.routingtype >= MIN_EXIT_TYPE
+ assert len(self.routinginfo) >= TAG_LEN
+ return self.routinginfo[:TAG_LEN]
def setRoutingInfo(self, info):
"""Change the routinginfo, and the routinglength to correspond."""
@@ -295,25 +295,25 @@
FragmentPayload object. Raise ParseError on failure or data
corruption."""
if len(payload) not in (PAYLOAD_LEN, PAYLOAD_LEN-ENC_FWD_OVERHEAD):
- raise ParseError("Payload has bad length")
+ raise ParseError("Payload has bad length")
bit0 = ord(payload[0]) & 0x80
if bit0:
- # We have a fragment
- idx, hash, msgID, msgLen = struct.unpack(FRAGMENT_UNPACK_PATTERN,
- payload[:FRAGMENT_PAYLOAD_OVERHEAD])
- idx &= 0x7f
- contents = payload[FRAGMENT_PAYLOAD_OVERHEAD:]
- if msgLen <= len(contents):
- raise ParseError("Payload has an invalid size field")
- return FragmentPayload(idx,hash,msgID,msgLen,contents)
+ # We have a fragment
+ idx, hash, msgID, msgLen = struct.unpack(FRAGMENT_UNPACK_PATTERN,
+ payload[:FRAGMENT_PAYLOAD_OVERHEAD])
+ idx &= 0x7f
+ contents = payload[FRAGMENT_PAYLOAD_OVERHEAD:]
+ if msgLen <= len(contents):
+ raise ParseError("Payload has an invalid size field")
+ return FragmentPayload(idx,hash,msgID,msgLen,contents)
else:
- # We have a singleton
- size, hash = struct.unpack(SINGLETON_UNPACK_PATTERN,
- payload[:SINGLETON_PAYLOAD_OVERHEAD])
- contents = payload[SINGLETON_PAYLOAD_OVERHEAD:]
- if size > len(contents):
- raise ParseError("Payload has invalid size field")
- return SingletonPayload(size,hash,contents)
+ # We have a singleton
+ size, hash = struct.unpack(SINGLETON_UNPACK_PATTERN,
+ payload[:SINGLETON_PAYLOAD_OVERHEAD])
+ contents = payload[SINGLETON_PAYLOAD_OVERHEAD:]
+ if size > len(contents):
+ raise ParseError("Payload has invalid size field")
+ return SingletonPayload(size,hash,contents)
# A singleton payload starts with a 0 bit, 15 bits of size, and a 20-byte hash
SINGLETON_UNPACK_PATTERN = "!H%ds" % (DIGEST_LEN)
@@ -329,28 +329,28 @@
"""Represents the payload for a standalone mixminion message.
Fields: size, hash, data. (Note that data is padded.)"""
def __init__(self, size, hash, data):
- self.size = size
- self.hash = hash
- self.data = data
+ self.size = size
+ self.hash = hash
+ self.data = data
def isSingleton(self):
- """Returns true; this is a singleton payload."""
- return 1
+ """Returns true; this is a singleton payload."""
+ return 1
def getContents(self):
- """Returns the non-padding portion of this payload's data"""
- return self.data[:self.size]
+ """Returns the non-padding portion of this payload's data"""
+ return self.data[:self.size]
def pack(self):
- """Check for reasonable values of fields, and return a packed payload.
+ """Check for reasonable values of fields, and return a packed payload.
"""
- assert (0x8000 & self.size) == 0
- assert 0 <= self.size <= len(self.data)
- assert len(self.hash) == DIGEST_LEN
- assert (PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - len(self.data)) in \
- (0, ENC_FWD_OVERHEAD)
- header = struct.pack(SINGLETON_UNPACK_PATTERN, self.size, self.hash)
- return "%s%s" % (header, self.data)
+ assert (0x8000 & self.size) == 0
+ assert 0 <= self.size <= len(self.data)
+ assert len(self.hash) == DIGEST_LEN
+ assert (PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - len(self.data)) in \
+ (0, ENC_FWD_OVERHEAD)
+ header = struct.pack(SINGLETON_UNPACK_PATTERN, self.size, self.hash)
+ return "%s%s" % (header, self.data)
class FragmentPayload(_Payload):
"""Represents the fields of a decoded fragment payload.
@@ -359,28 +359,28 @@
or decode them.
"""
def __init__(self, index, hash, msgID, msgLen, data):
- self.index = index
- self.hash = hash
- self.msgID = msgID
- self.msgLen = msgLen
- self.data = data
+ self.index = index
+ self.hash = hash
+ self.msgID = msgID
+ self.msgLen = msgLen
+ self.data = data
def isSingleton(self):
- """Return false; not a singleton"""
- return 0
+ """Return false; not a singleton"""
+ return 0
def pack(self):
- """Returns the string value of this payload."""
- assert 0 <= self.index <= MAX_N_FRAGMENTS
- assert len(self.hash) == DIGEST_LEN
- 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, 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)
+ """Returns the string value of this payload."""
+ assert 0 <= self.index <= MAX_N_FRAGMENTS
+ assert len(self.hash) == DIGEST_LEN
+ 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, 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)
#----------------------------------------------------------------------
# REPLY BLOCKS
@@ -408,7 +408,7 @@
if major != 0x00 or minor != 0x01:
raise ParseError("Unrecognized version on reply block %s.%s",
- major,minor)
+ major,minor)
ri = s[MIN_RB_LEN:]
if len(ri) != rlen:
@@ -426,14 +426,14 @@
self.timestamp = useBy
self.routingType = rt
self.routingInfo = ri
- self.encryptionKey = key
+ self.encryptionKey = key
def pack(self):
"""Returns the external representation of this reply block"""
return struct.pack(RB_UNPACK_PATTERN,
"SURB", 0x00, 0x01, self.timestamp, self.header,
len(self.routingInfo), self.routingType,
- self.encryptionKey) + self.routingInfo
+ self.encryptionKey) + self.routingInfo
#----------------------------------------------------------------------
# Routing info
@@ -471,19 +471,19 @@
"""Return the routing info for this address"""
assert len(self.keyinfo) == DIGEST_LEN
return struct.pack(IPV4_PAT, inet_aton(self.ip),
- self.port, self.keyinfo)
+ self.port, self.keyinfo)
def __hash__(self):
- return hash(self.pack())
+ return hash(self.pack())
def __eq__(self, other):
- return (type(self) == type(other) and self.ip == other.ip and
- self.port == other.port and self.keyinfo == other.keyinfo)
+ return (type(self) == type(other) and self.ip == other.ip and
+ self.port == other.port and self.keyinfo == other.keyinfo)
def parseSMTPInfo(s):
"""Convert the encoding of an SMTP exitinfo into an SMTPInfo object."""
if not isSMTPMailbox(s):
- raise ParseError("Invalid rfc822 mailbox %r" % s)
+ raise ParseError("Invalid rfc822 mailbox %r" % s)
return SMTPInfo(s)
class SMTPInfo:
@@ -495,7 +495,7 @@
def pack(self):
"""Returns the wire representation of this SMTPInfo"""
- return self.email
+ return self.email
def parseMBOXInfo(s):
"""Convert the encoding of an MBOX exitinfo into an MBOXInfo address"""
@@ -512,4 +512,4 @@
def pack(self):
"""Return the external representation of this routing info."""
- return self.user
+ return self.user
Index: ServerInfo.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/ServerInfo.py,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- ServerInfo.py 12 Dec 2002 19:56:46 -0000 1.25
+++ ServerInfo.py 16 Dec 2002 02:40:11 -0000 1.26
@@ -38,121 +38,121 @@
"""A ServerInfo object holds a parsed server descriptor."""
_restrictFormat = 1
_syntax = {
- "Server" : { "__SECTION__": ("REQUIRE", None, None),
+ "Server" : { "__SECTION__": ("REQUIRE", None, None),
"Descriptor-Version": ("REQUIRE", None, None),
- "IP": ("REQUIRE", C._parseIP, None),
- "Nickname": ("REQUIRE", None, None),
- "Identity": ("REQUIRE", C._parsePublicKey, None),
- "Digest": ("REQUIRE", C._parseBase64, None),
- "Signature": ("REQUIRE", C._parseBase64, None),
- "Published": ("REQUIRE", C._parseTime, None),
- "Valid-After": ("REQUIRE", C._parseDate, None),
- "Valid-Until": ("REQUIRE", C._parseDate, None),
- "Contact": ("ALLOW", None, None),
- "Comments": ("ALLOW", None, None),
- "Packet-Key": ("REQUIRE", C._parsePublicKey, None),
- },
- "Incoming/MMTP" : {
- "Version": ("REQUIRE", None, None),
- "Port": ("REQUIRE", C._parseInt, None),
- "Key-Digest": ("REQUIRE", C._parseBase64, None),
- "Protocols": ("REQUIRE", None, None),
+ "IP": ("REQUIRE", C._parseIP, None),
+ "Nickname": ("REQUIRE", None, None),
+ "Identity": ("REQUIRE", C._parsePublicKey, None),
+ "Digest": ("REQUIRE", C._parseBase64, None),
+ "Signature": ("REQUIRE", C._parseBase64, None),
+ "Published": ("REQUIRE", C._parseTime, None),
+ "Valid-After": ("REQUIRE", C._parseDate, None),
+ "Valid-Until": ("REQUIRE", C._parseDate, None),
+ "Contact": ("ALLOW", None, None),
+ "Comments": ("ALLOW", None, None),
+ "Packet-Key": ("REQUIRE", C._parsePublicKey, None),
+ },
+ "Incoming/MMTP" : {
+ "Version": ("REQUIRE", None, None),
+ "Port": ("REQUIRE", C._parseInt, None),
+ "Key-Digest": ("REQUIRE", C._parseBase64, None),
+ "Protocols": ("REQUIRE", None, None),
"Allow": ("ALLOW*", C._parseAddressSet_allow, None),
"Deny": ("ALLOW*", C._parseAddressSet_deny, None),
- },
- "Outgoing/MMTP" : {
- "Version": ("REQUIRE", None, None),
- "Protocols": ("REQUIRE", None, None),
+ },
+ "Outgoing/MMTP" : {
+ "Version": ("REQUIRE", None, None),
+ "Protocols": ("REQUIRE", None, None),
"Allow": ("ALLOW*", C._parseAddressSet_allow, None),
"Deny": ("ALLOW*", C._parseAddressSet_deny, None),
- },
- "Delivery/MBOX" : {
- "Version": ("REQUIRE", None, None),
- },
- "Delivery/SMTP" : {
- "Version": ("REQUIRE", None, None),
- }
- }
+ },
+ "Delivery/MBOX" : {
+ "Version": ("REQUIRE", None, None),
+ },
+ "Delivery/SMTP" : {
+ "Version": ("REQUIRE", None, None),
+ }
+ }
def __init__(self, fname=None, string=None, assumeValid=0):
- mixminion.Config._ConfigFile.__init__(self, fname, string, assumeValid)
- LOG.trace("Reading server descriptor %s from %s",
- self['Server']['Nickname'],
- fname or "<string>")
+ mixminion.Config._ConfigFile.__init__(self, fname, string, assumeValid)
+ LOG.trace("Reading server descriptor %s from %s",
+ self['Server']['Nickname'],
+ fname or "<string>")
def validate(self, sections, entries, lines, contents):
- ####
- # Check 'Server' section.
- server = sections['Server']
- 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']
- identityBytes = identityKey.get_modulus_bytes()
- if not (MIN_IDENTITY_BYTES <= identityBytes <= MAX_IDENTITY_BYTES):
- raise ConfigError("Invalid length on identity key")
- if server['Valid-Until'] <= server['Valid-After']:
- raise ConfigError("Server is never valid")
- if server['Contact'] and len(server['Contact']) > MAX_CONTACT:
- raise ConfigError("Contact too long")
+ ####
+ # Check 'Server' section.
+ server = sections['Server']
+ 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']
+ identityBytes = identityKey.get_modulus_bytes()
+ if not (MIN_IDENTITY_BYTES <= identityBytes <= MAX_IDENTITY_BYTES):
+ raise ConfigError("Invalid length on identity key")
+ if server['Valid-Until'] <= server['Valid-After']:
+ raise ConfigError("Server is never valid")
+ if server['Contact'] and len(server['Contact']) > MAX_CONTACT:
+ raise ConfigError("Contact too long")
if server['Comments'] and len(server['Comments']) > MAX_COMMENTS:
- raise ConfigError("Comments too long")
- packetKeyBytes = server['Packet-Key'].get_modulus_bytes()
- if packetKeyBytes != PACKET_KEY_BYTES:
- raise ConfigError("Invalid length on packet key")
+ raise ConfigError("Comments too long")
+ packetKeyBytes = server['Packet-Key'].get_modulus_bytes()
+ if packetKeyBytes != PACKET_KEY_BYTES:
+ raise ConfigError("Invalid length on packet key")
- ####
- # Check Digest of file
- digest = getServerInfoDigest(contents)
- if digest != server['Digest']:
- raise ConfigError("Invalid digest")
+ ####
+ # Check Digest of file
+ digest = getServerInfoDigest(contents)
+ if digest != server['Digest']:
+ raise ConfigError("Invalid digest")
- # Check signature
- if digest != mixminion.Crypto.pk_check_signature(server['Signature'],
- identityKey):
- raise ConfigError("Invalid signature")
+ # Check signature
+ if digest != mixminion.Crypto.pk_check_signature(server['Signature'],
+ identityKey):
+ raise ConfigError("Invalid signature")
- ## Incoming/MMTP section
- inMMTP = sections['Incoming/MMTP']
- if inMMTP:
- if inMMTP['Version'] != '0.1':
- raise ConfigError("Unrecognized MMTP descriptor version %s"%
- inMMTP['Version'])
- if len(inMMTP['Key-Digest']) != DIGEST_LEN:
- raise ConfigError("Invalid key digest %s"%
- formatBase64(inMMTP['Key-Digest']))
+ ## Incoming/MMTP section
+ inMMTP = sections['Incoming/MMTP']
+ if inMMTP:
+ if inMMTP['Version'] != '0.1':
+ raise ConfigError("Unrecognized MMTP descriptor version %s"%
+ inMMTP['Version'])
+ if len(inMMTP['Key-Digest']) != DIGEST_LEN:
+ raise ConfigError("Invalid key digest %s"%
+ formatBase64(inMMTP['Key-Digest']))
- ## Outgoing/MMTP section
- outMMTP = sections['Outgoing/MMTP']
- if outMMTP:
- if outMMTP['Version'] != '0.1':
- raise ConfigError("Unrecognized MMTP descriptor version %s"%
- inMMTP['Version'])
+ ## Outgoing/MMTP section
+ outMMTP = sections['Outgoing/MMTP']
+ if outMMTP:
+ if outMMTP['Version'] != '0.1':
+ raise ConfigError("Unrecognized MMTP descriptor version %s"%
+ inMMTP['Version'])
- # FFFF When a better client module system exists, check the
- # FFFF module descriptors.
+ # FFFF When a better client module system exists, check the
+ # FFFF module descriptors.
def getNickname(self):
- """Returns this server's nickname"""
- return self['Server']['Nickname']
+ """Returns this server's nickname"""
+ return self['Server']['Nickname']
def getAddr(self):
- """Returns this server's IP address"""
- return self['Server']['IP']
+ """Returns this server's IP address"""
+ return self['Server']['IP']
def getPort(self):
- """Returns this server's IP port"""
- return self['Incoming/MMTP']['Port']
+ """Returns this server's IP port"""
+ return self['Incoming/MMTP']['Port']
def getPacketKey(self):
- """Returns the RSA key this server uses to decrypt messages"""
- return self['Server']['Packet-Key']
+ """Returns the RSA key this server uses to decrypt messages"""
+ return self['Server']['Packet-Key']
def getKeyID(self):
- """Returns a hash of this server's MMTP key"""
- return self['Incoming/MMTP']['Key-Digest']
+ """Returns a hash of this server's MMTP key"""
+ return self['Incoming/MMTP']['Key-Digest']
def getRoutingInfo(self):
"""Returns a mixminion.Packet.IPV4Info object for routing messages
@@ -184,11 +184,11 @@
signatureLine = None
infoLines = info.split("\n")
for lineNo in range(len(infoLines)):
- line = infoLines[lineNo]
- if line.startswith("Digest:") and digestLine is None:
- digestLine = lineNo
- elif line.startswith("Signature:") and signatureLine is None:
- signatureLine = lineNo
+ line = infoLines[lineNo]
+ if line.startswith("Digest:") and digestLine is None:
+ digestLine = lineNo
+ elif line.startswith("Signature:") and signatureLine is None:
+ signatureLine = lineNo
assert digestLine is not None and signatureLine is not None
@@ -199,7 +199,7 @@
digest = mixminion.Crypto.sha1(info)
if rsa is None:
- return digest
+ return digest
# If we got an RSA key, we need to add the digest and signature.
signature = mixminion.Crypto.pk_sign(digest,rsa)
Index: benchmark.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/benchmark.py,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- benchmark.py 12 Dec 2002 19:56:46 -0000 1.18
+++ benchmark.py 16 Dec 2002 02:40:11 -0000 1.19
@@ -168,11 +168,11 @@
L10 = [ "x" ] * 10
L1000 = [ "x" ] * 1000
print "aesprng.shuffle (10/10)", \
- timeit((lambda c=c,L=L10: c.shuffle(L)), 1000)
+ timeit((lambda c=c,L=L10: c.shuffle(L)), 1000)
print "aesprng.shuffle (1000/1000)", \
- timeit((lambda c=c,L=L1000: c.shuffle(L)), 30)
+ timeit((lambda c=c,L=L1000: c.shuffle(L)), 30)
print "aesprng.shuffle (10/1000)", \
- timeit((lambda c=c,L=L1000: c.shuffle(L,10)), 1000)
+ timeit((lambda c=c,L=L1000: c.shuffle(L,10)), 1000)
lkey = Keyset("keymaterial foo bar baz").getLionessKeys("T")
print "lioness E (1K)", timeit((
@@ -265,7 +265,7 @@
for (bits,it) in ((1536,15), (2048,10),(4096,10)):
t = time()
- print "[generating key...]"
+ print "[generating key...]"
rsa2 = pk_generate(bits)
t = time()-t
print "RSA genrate (%d bit)"%bits, timestr(t)
@@ -300,7 +300,7 @@
print "Testing hash log (%s entries)"%load
if load > 20000:
- print "This may take a few minutes..."
+ print "This may take a few minutes..."
h = HashLog(fname, "A")
hashes = [ prng.getBytes(20) for _ in xrange(load) ]
@@ -508,9 +508,9 @@
os.mkdir(dname)
for i in xrange(200):
- f = open(os.path.join(dname, str(i)), 'wb')
- f.write(s32K)
- f.close()
+ f = open(os.path.join(dname, str(i)), 'wb')
+ f.write(s32K)
+ f.close()
lst = [os.path.join(dname,str(i)) for i in range(100) ]
t1 = time()
secureDelete(lst)
@@ -524,7 +524,7 @@
lst = [ os.path.join(dname,str(i)) for i in range(100,200) ]
t1 = time()
for file in lst:
- secureDelete(file)
+ secureDelete(file)
t = time()-t1
print "secureDelete (1)", timestr(t/100)
@@ -540,36 +540,36 @@
keytxt="a"*16
key = _ml.aes_key(keytxt)
while 1:
- _ml.sha1(s20k)
- _ml.aes_ctr128_crypt(key,s20k,0)
- _ml.aes_ctr128_crypt(key,s20k,2000)
- _ml.aes_ctr128_crypt(key,"",2000,20000)
- _ml.aes_ctr128_crypt(key,"",0,20000)
- _ml.aes_ctr128_crypt(key,s20k,0,2000)
- try:
- _ml.aes_ctr128_crypt("abc",s20k,0,2000)
- except:
- pass
- _ml.strxor(s20k,s20k)
- try:
- _ml.strxor(s20k,keytxt)
- except:
- pass
- _ml.openssl_seed(s20k)
- r = _ml.add_oaep_padding("Hello",OAEP_PARAMETER,128)
- _ml.check_oaep_padding(r,OAEP_PARAMETER,128)
- try:
- _ml.check_oaep_padding("hello",OAEP_PARAMETER,128)
- except:
- pass
- try:
- _ml.add_oaep_padding(s20k,OAEP_PARAMETER,128)
- except:
- pass
- try:
- _ml.add_oaep_padding("a"*127,OAEP_PARAMETER,128)
- except:
- pass
+ _ml.sha1(s20k)
+ _ml.aes_ctr128_crypt(key,s20k,0)
+ _ml.aes_ctr128_crypt(key,s20k,2000)
+ _ml.aes_ctr128_crypt(key,"",2000,20000)
+ _ml.aes_ctr128_crypt(key,"",0,20000)
+ _ml.aes_ctr128_crypt(key,s20k,0,2000)
+ try:
+ _ml.aes_ctr128_crypt("abc",s20k,0,2000)
+ except:
+ pass
+ _ml.strxor(s20k,s20k)
+ try:
+ _ml.strxor(s20k,keytxt)
+ except:
+ pass
+ _ml.openssl_seed(s20k)
+ r = _ml.add_oaep_padding("Hello",OAEP_PARAMETER,128)
+ _ml.check_oaep_padding(r,OAEP_PARAMETER,128)
+ try:
+ _ml.check_oaep_padding("hello",OAEP_PARAMETER,128)
+ except:
+ pass
+ try:
+ _ml.add_oaep_padding(s20k,OAEP_PARAMETER,128)
+ except:
+ pass
+ try:
+ _ml.add_oaep_padding("a"*127,OAEP_PARAMETER,128)
+ except:
+ pass
def testLeaks2():
print "Trying to leak (rsa)"
Index: test.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/test.py,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -d -r1.48 -r1.49
--- test.py 15 Dec 2002 04:35:54 -0000 1.48
+++ test.py 16 Dec 2002 02:40:11 -0000 1.49
@@ -88,11 +88,11 @@
"""Helper function. Returns the first index i for which s1[i] != s2[i],
or -1 s1 == s2."""
if s1 == s2:
- return -1
+ return -1
last = min(len(s1), len(s2))
for i in xrange(last):
- if s1[i] != s2[i]:
- return i
+ if s1[i] != s2[i]:
+ return i
[...3879 lines suppressed...]
- m = re.search(r"^Digest: (\S+)\n", s, re.M)
- assert m
- ds.append(base64.decodestring(m.group(1)))
- else:
- ds.append(s['Server']['Digest'])
- return ds[0] == ds[1]
+ """s1 and s2 are either ServerInfo objects or strings containing server
+ descriptors. Returns 1 iff their digest fields match"""
+ ds = []
+ for s in s1, s2:
+ if type(s) == types.StringType:
+ m = re.search(r"^Digest: (\S+)\n", s, re.M)
+ assert m
+ ds.append(base64.decodestring(m.group(1)))
+ else:
+ ds.append(s['Server']['Digest'])
+ return ds[0] == ds[1]
#----------------------------------------------------------------------
def testSuite():
Index: testSupport.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/testSupport.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- testSupport.py 12 Dec 2002 19:56:46 -0000 1.11
+++ testSupport.py 16 Dec 2002 02:40:11 -0000 1.12
@@ -41,70 +41,70 @@
# all existing numbers.
# next -- the number of the next file.
def getConfigSyntax(self):
- return { 'Testing/DirectoryDump':
- { 'Location' : ('REQUIRE', None, None),
- 'UseQueue': ('REQUIRE', _parseBoolean, None) } }
+ return { 'Testing/DirectoryDump':
+ { 'Location' : ('REQUIRE', None, None),
+ 'UseQueue': ('REQUIRE', _parseBoolean, None) } }
def validateConfig(self, sections, entries, lines, contents):
- # loc = sections['Testing/DirectoryDump'].get('Location')
- pass
+ # loc = sections['Testing/DirectoryDump'].get('Location')
+ pass
def configure(self, config, manager):
- self.loc = config['Testing/DirectoryDump'].get('Location')
- if not self.loc:
- return
- self.useQueue = config['Testing/DirectoryDump']['UseQueue']
- manager.enableModule(self)
+ self.loc = config['Testing/DirectoryDump'].get('Location')
+ if not self.loc:
+ return
+ self.useQueue = config['Testing/DirectoryDump']['UseQueue']
+ manager.enableModule(self)
- if not os.path.exists(self.loc):
- createPrivateDir(self.loc)
+ if not os.path.exists(self.loc):
+ createPrivateDir(self.loc)
- max = -1
- for f in os.listdir(self.loc):
- if int(f) > max:
- max = int(f)
- self.next = max+1
+ max = -1
+ for f in os.listdir(self.loc):
+ if int(f) > max:
+ max = int(f)
+ self.next = max+1
def getServerInfoBlock(self):
- return ""
+ return ""
def getName(self):
- return "Testing_DirectoryDump"
+ return "Testing_DirectoryDump"
def getExitTypes(self):
- return [ 0xFFFE ]
+ return [ 0xFFFE ]
def createDeliveryQueue(self, queueDir):
- if self.useQueue:
- return SimpleModuleDeliveryQueue(self, queueDir)
- else:
- return ImmediateDeliveryQueue(self)
+ if self.useQueue:
+ return SimpleModuleDeliveryQueue(self, queueDir)
+ else:
+ return ImmediateDeliveryQueue(self)
def processMessage(self, message, tag, exitType, exitInfo):
- assert exitType == 0xFFFE
+ assert exitType == 0xFFFE
- if exitInfo == 'fail':
- return DELIVER_FAIL_RETRY
- elif exitInfo == 'FAIL!':
- return DELIVER_FAIL_NORETRY
+ if exitInfo == 'fail':
+ return DELIVER_FAIL_RETRY
+ elif exitInfo == 'FAIL!':
+ return DELIVER_FAIL_NORETRY
- LOG.debug("Delivering test message")
+ LOG.debug("Delivering test message")
- m = _escapeMessageForEmail(message, tag)
- if m is None:
- # Ordinarily, we'd drop corrupt messages, but this module is
- # meant for debugging.
- m = """\
+ m = _escapeMessageForEmail(message, tag)
+ if m is None:
+ # Ordinarily, we'd drop corrupt messages, but this module is
+ # meant for debugging.
+ m = """\
==========CORRUPT OR UNDECODABLE MESSAGE
Decoding handle: %s%s==========MESSAGE ENDS""" % (
base64.encodestring(tag),
base64.encodestring(message))
- f = open(os.path.join(self.loc, str(self.next)), 'w')
- self.next += 1
- f.write(m)
- f.close()
- return DELIVER_OK
+ f = open(os.path.join(self.loc, str(self.next)), 'w')
+ self.next += 1
+ f.write(m)
+ f.close()
+ return DELIVER_OK
#----------------------------------------------------------------------
# mix_mktemp: A secure, paranoid mktemp replacement. (May be overkill
@@ -127,12 +127,12 @@
global _MM_TESTING_TEMPDIR
global _MM_TESTING_TEMPDIR_COUNTER
if _MM_TESTING_TEMPDIR is None:
- # We haven't configured our temporary directory yet.
- import tempfile
+ # We haven't configured our temporary directory yet.
+ import tempfile
paranoia = _MM_TESTING_TEMPDIR_PARANOIA
- # If tempfile.mkdtemp exists, use it. This avoids warnings, and
- # is harder for people to exploit.
+ # If tempfile.mkdtemp exists, use it. This avoids warnings, and
+ # is harder for people to exploit.
if hasattr(tempfile, 'mkdtemp'):
try:
temp = tempfile.mkdtemp()
@@ -140,8 +140,8 @@
print "mkdtemp failure: %s" % e
sys.exit(1)
else:
- # Otherwise, pick a dirname, make sure it doesn't exist, and try to
- # create it.
+ # Otherwise, pick a dirname, make sure it doesn't exist, and try to
+ # create it.
temp = tempfile.mktemp()
if paranoia and os.path.exists(temp):
print "I think somebody's trying to exploit mktemp."
@@ -152,49 +152,49 @@
print "Something's up with mktemp: %s" % e
sys.exit(1)
- # The directory must exist....
- if not os.path.exists(temp):
- print "Couldn't create temp dir %r" %temp
- sys.exit(1)
- st = os.stat(temp)
- if paranoia:
- # And be writeable only by us...
- if st[stat.ST_MODE] & 077:
- print "Couldn't make temp dir %r with secure permissions" %temp
- sys.exit(1)
- # And be owned by us...
- if st[stat.ST_UID] != os.getuid():
- print "The wrong user owns temp dir %r"%temp
- sys.exit(1)
- parent = temp
- # And if, and all of its parents, must not be group-writeable
- # unless their sticky bit is set, and must not be owned by
- # anybody except us and root.
- while 1:
- p = os.path.split(parent)[0]
- if parent == p:
- break
- parent = p
- st = os.stat(parent)
- m = st[stat.ST_MODE]
- if m & 02 and not (m & stat.S_ISVTX):
- print "Directory %s has fishy permissions %o" %(parent,m)
- sys.exit(1)
- if st[stat.ST_UID] not in (0, os.getuid()):
- print "Directory %s has bad owner %s" % (parent,
- st[stat.ST_UID])
- sys.exit(1)
+ # The directory must exist....
+ if not os.path.exists(temp):
+ print "Couldn't create temp dir %r" %temp
+ sys.exit(1)
+ st = os.stat(temp)
+ if paranoia:
+ # And be writeable only by us...
+ if st[stat.ST_MODE] & 077:
+ print "Couldn't make temp dir %r with secure permissions" %temp
+ sys.exit(1)
+ # And be owned by us...
+ if st[stat.ST_UID] != os.getuid():
+ print "The wrong user owns temp dir %r"%temp
+ sys.exit(1)
+ parent = temp
+ # And if, and all of its parents, must not be group-writeable
+ # unless their sticky bit is set, and must not be owned by
+ # anybody except us and root.
+ while 1:
+ p = os.path.split(parent)[0]
+ if parent == p:
+ break
+ parent = p
+ st = os.stat(parent)
+ m = st[stat.ST_MODE]
+ if m & 02 and not (m & stat.S_ISVTX):
+ print "Directory %s has fishy permissions %o" %(parent,m)
+ sys.exit(1)
+ if st[stat.ST_UID] not in (0, os.getuid()):
+ print "Directory %s has bad owner %s" % (parent,
+ st[stat.ST_UID])
+ sys.exit(1)
- _MM_TESTING_TEMPDIR = temp
- if _MM_TESTING_TEMPDIR_REMOVE_ON_EXIT:
- import atexit
- atexit.register(deltree, temp)
+ _MM_TESTING_TEMPDIR = temp
+ if _MM_TESTING_TEMPDIR_REMOVE_ON_EXIT:
+ import atexit
+ atexit.register(deltree, temp)
# So now we have a temporary directory; return the name of a new
# file there.
_MM_TESTING_TEMPDIR_COUNTER += 1
return os.path.join(_MM_TESTING_TEMPDIR,
- "tmp%05d%s" % (_MM_TESTING_TEMPDIR_COUNTER,extra))
+ "tmp%05d%s" % (_MM_TESTING_TEMPDIR_COUNTER,extra))
_WAIT_FOR_KIDS = 1
def deltree(*dirs):
@@ -202,17 +202,17 @@
contents."""
global _WAIT_FOR_KIDS
if _WAIT_FOR_KIDS:
- print "Waiting for shred processes to finish."
- waitForChildren()
- _WAIT_FOR_KIDS = 0
+ print "Waiting for shred processes to finish."
+ waitForChildren()
+ _WAIT_FOR_KIDS = 0
for d in dirs:
if os.path.isdir(d):
for fn in os.listdir(d):
- loc = os.path.join(d,fn)
- if os.path.isdir(loc):
- deltree(loc)
- else:
- os.unlink(loc)
+ loc = os.path.join(d,fn)
+ if os.path.isdir(loc):
+ deltree(loc)
+ else:
+ os.unlink(loc)
os.rmdir(d)
elif os.path.exists(d):
os.unlink(d)
@@ -224,7 +224,7 @@
"""Temporarily suppress logging output"""
log = LOG
if hasattr(log, '_storedHandlers'):
- resumeLog()
+ resumeLog()
buf = cStringIO.StringIO()
h = mixminion.Common._ConsoleLogHandler(buf)
log._storedHandlers = log.handlers
@@ -232,7 +232,7 @@
log._testBuf = buf
log.handlers = []
if severity is not None:
- log.setMinSeverity(severity)
+ log.setMinSeverity(severity)
log.addHandler(h)
def resumeLog():
@@ -240,7 +240,7 @@
suspend."""
log = LOG
if not hasattr(log, '_storedHandlers'):
- return None
+ return None
buf = log._testBuf
del log._testBuf
log.handlers = log._storedHandlers
@@ -259,9 +259,9 @@
"""Temporarily replace <object.attribute> with value. When
undoReplacedAttributes() is called, the old value is restored."""
if hasattr(object, attribute):
- tup = (object, attribute, getattr(object, attribute))
+ tup = (object, attribute, getattr(object, attribute))
else:
- tup = (object, attribute)
+ tup = (object, attribute)
_REPLACED_OBJECT_STACK.append(tup)
setattr(object, attribute, value)
@@ -273,14 +273,14 @@
"""Helper object: callable stub that logs its invocations to _CALL_LOG
and delegates to an internal function."""
def __init__(self, name, fn=None):
- self.name = name
- self.fn = fn
+ self.name = name
+ self.fn = fn
def __call__(self, *args, **kwargs):
- _CALL_LOG.append((self.name, args, kwargs))
- if self.fn:
- return self.fn(*args, **kwargs)
- else:
- return None
+ _CALL_LOG.append((self.name, args, kwargs))
+ if self.fn:
+ return self.fn(*args, **kwargs)
+ else:
+ return None
def replaceFunction(object, attribute, fn=None):
"""Temporarily replace the function or method <object.attribute>.
@@ -305,12 +305,12 @@
r.reverse()
del _REPLACED_OBJECT_STACK[:]
for item in r:
- if len(item) == 2:
- o,a = item
- delattr(o,a)
- else:
- o,a,v = item
- setattr(o,a,v)
+ if len(item) == 2:
+ o,a = item
+ delattr(o,a)
+ else:
+ o,a,v = item
+ setattr(o,a,v)
#----------------------------------------------------------------------
# Long keypairs: stored here to avoid regenerating them every time we need