Robin Becker <[EMAIL PROTECTED]> writes: >Paul Rubin wrote: >.....I'm also missing the rotor module and regret that something useful >was warned about and now removed with no plugin replacement.
Hm, yes. Here is a (rather slow) replacement: """This module is derived from Modules/rotormodule.c and translated into Python. I have appended the Copyright by Lance Ellinghouse below. The rotor module has been removed from the Python 2.4 distribution because the rotor module uses an insecure algorithm and is deprecated. ============================================================== Of course, this does still hold. However, I think this module might be used and adapted for demonstration purposes and might help some poor users who have encrypted (or obfuscated) some old stuff with the rotor module and have no access to older Python versions any more. Documentation can be found in Python Library Reference, Guido van Rossum, Fred L. Drake, Jr., editor, PythonLabs, Release 2.3.4 May 20, 2004 <http://www.python.org/doc/2.3.4/lib/module-rotor.html> ##################################################################### Copyright 1994 by Lance Ellinghouse, Cathedral City, California Republic, United States of America. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Lance Ellinghouse not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ##################################################################### """ class newrotor(object): def __init__(self, key, n_rotors=6): self.n_rotors = n_rotors self.setkey(key) def setkey(self, key): self.key = key self.rotors = None self.positions = [None, None] def encrypt(self, buf): # Reset (encrypt) positions and encryptmore self.positions[0] = None return self.cryptmore(buf, 0) def encryptmore(self, buf): return self.cryptmore(buf, 0) def decrypt(self, buf): # Reset decrypt positions and decryptmore self.positions[1] = None return self.cryptmore(buf, 1) def decryptmore(self, buf): return self.cryptmore(buf, 1) def cryptmore(self, buf, do_decrypt): size, nr, rotors, pos = self.get_rotors(do_decrypt) outbuf = [] append = outbuf.append for c in map(ord, buf): if do_decrypt: # Apply decrypt rotors and xor in reverse order for i in xrange(nr-1,-1,-1): c = pos[i] ^ rotors[i][c] else: # Apply xor and ecrypt rotors for i in xrange(nr): c = rotors[i][c ^ pos[i]] append(c) # Advance rotors, i.e. add the (fixed) rotor increments to the # variable rotor positions with carry. # Note: In the C-implementation, the result of the carry addition # was stored to an "unsigned char". Hence the next carry # is lost if pos[i] == size-1 and pnew >= size. # Masking with 0xff simulates this behavior. # pnew = 0 # (pnew >= size) works as "carry bit" for i in xrange(nr): pnew = ((pos[i] + (pnew >= size)) & 0xff) + rotors[i][size] pos[i] = pnew % size return ''.join(map(chr, outbuf)) def get_rotors(self, do_decrypt): # Return a tuple (size, nr, rotors, positions) where # - size is the rotor size (== 256, because of 8-bit bytes) # - nr is the number of rotors. # - rotors is a tuple of nr encrypt or nr decrypt rotors # for do_decrypt == 0 or do_decrypt == 1 respectively. # - postions is a list of nr "rotor positions". # # The rotors represent the static aspect of the rotor machine which # is initially computed from key and fixed during en/decryption. # A rotor is a random permutation of range(size) extended # by an "increment value" in range(size). # # The followng statements hold for a tuple of encrypt rotors E and # and the corresponding tuple of decrypt rotors D. # # D[i][E[i][j]] == j for i in range(nr) for j in range(size) # # E[i][D[i][j]] == j for i in range(nr) for j in range(size) # # This means that the corresponding rotors E[i] and D[i] are # inverse permutations. # The increments are equal for the corresponding encrypt and # decrypt rotors and have an odd value: # # D[i][size] == E[i][size] and E[i][size] == 1 mod 2 and # 0 < E[i][size] < size for i in range(nr) # # The position vector represents the dynamic aspect. # It changes after each en/decrypted character (the rotors # are "advanced"). The initial position vector is also computed # from the key # nr = self.n_rotors rotors = self.rotors positions = self.positions[do_decrypt] if positions is None: if rotors: positions = list(rotors[3]) else: # Generate identity permutation for 8-bit bytes plus an # (unused) increment value self.size = size = 256 id_rotor = range(size+1) # Generate nr "random" initial positions and "random" # en/decrypt rotors from id_rotor. # rand = random_func(self.key) E = [] D = [] positions = [] for i in xrange(nr): i = size positions.append(rand(i)) erotor = id_rotor[:] drotor = id_rotor[:] drotor[i] = erotor[i] = 1 + 2*rand(i/2) # increment while i > 1: r = rand(i) i -= 1 er = erotor[r] erotor[r] = erotor[i] erotor[i] = er drotor[er] = i drotor[erotor[0]] = 0 E.append(tuple(erotor)) D.append(tuple(drotor)) self.rotors = rotors = ( tuple(E), tuple(D), size, tuple(positions)) self.positions[do_decrypt] = positions return rotors[2], nr, rotors[do_decrypt], positions def random_func(key): # Generate a random number generator that is "seeded" from key. # This algorithm is copied from Python2.3 randommodule.c. # mask = 0xffff x=995 y=576 z=767 for c in map(ord, key): x = (((x<<3 | x>>13) + c) & mask) y = (((y<<3 | y>>13) ^ c) & mask) z = (((z<<3 | z>>13) - c) & mask) # Emulate (unintended?) cast to short maxpos = mask >> 1 mask += 1 if x > maxpos: x -= mask if y > maxpos: y -= mask if z > maxpos: z -= mask y |= 1 # avoid very bad seed, why not for x and z too? # Oh, dear, for compatibility, we must evaluate the first seed transition # the hard way, later it becomes much simpler x = 171 * (x % 177) - 2 * (x/177) y = 172 * (y % 176) - 35 * (y/176) z = 170 * (z % 178) - 63 * (z/178) if x < 0: x += 30269 if y < 0: y += 30307 if z < 0: z += 30323 # At least all values are > 0 by now but may be greater than expected .. def rand(n, seed=[(x, y, z)]): # Return a random number 0 <= r < n # x, y, z = seed[0] seed[0] = ((171*x) % 30269, (172*y) % 30307, (170*z) % 30323) return int((x/30269.0 + y/30307.0 + z/30323.0) * n) % n # Original code was like this: # from math import floor # val = x/30269.0 + y/30307.0 + z/30323.0 # val = val - floor(val) # if val >= 1.0: # val = 0.0 # n = int(val*n) % n return rand -- http://mail.python.org/mailman/listinfo/python-list