summaryrefslogtreecommitdiff
path: root/deskutils/cycle/files/p_rotor.py
diff options
context:
space:
mode:
Diffstat (limited to 'deskutils/cycle/files/p_rotor.py')
-rw-r--r--deskutils/cycle/files/p_rotor.py227
1 files changed, 227 insertions, 0 deletions
diff --git a/deskutils/cycle/files/p_rotor.py b/deskutils/cycle/files/p_rotor.py
new file mode 100644
index 000000000000..ed0467761cfa
--- /dev/null
+++ b/deskutils/cycle/files/p_rotor.py
@@ -0,0 +1,227 @@
+"""This module was written by Gerd Woetzel:
+ http://mail.python.org/pipermail/python-list/2005-January/261304.html
+
+
+ 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
+