The ISAAC cipher: Difference between revisions
Content added Content deleted
(Adding Java) |
(Adding Python) |
||
Line 2,042: | Line 2,042: | ||
(bye)</lang> |
(bye)</lang> |
||
=={{header|Python}}== |
|||
{{works with|Python|3.0 or later}} |
|||
Python doesn't have integer overflow (integers are handled as bignums if they don't fit into a machine word), so we need to emulate it manually by masking off the high bits after each addition and left shift. |
|||
This implementation extends the Random class of the built-in random module, so it automatically inherits methods for generating several distributions, as well as support for shuffling and sampling collections. |
|||
<lang Python>import random |
|||
import collections |
|||
INT_MASK = 0xFFFFFFFF # we use this to emulate 32-bit overflow semantics by masking off higher bits after operations |
|||
class IsaacRandom(random.Random): |
|||
""" |
|||
Random number generator using the ISAAC algorithm. |
|||
""" |
|||
def seed(self, seed=None): |
|||
""" |
|||
Initialize internal state. |
|||
The seed, if given, can be a string, an integer, or an iterable that contains |
|||
integers only. If no seed is given, a fixed default state is set up; unlike |
|||
our superclass, this class will not attempt to randomize the seed from outside sources. |
|||
""" |
|||
def mix(): |
|||
init_state[0] ^= ((init_state[1]<<11)&INT_MASK); init_state[3] += init_state[0]; init_state[3] &= INT_MASK; init_state[1] += init_state[2]; init_state[1] &= INT_MASK |
|||
init_state[1] ^= (init_state[2]>>2) ; init_state[4] += init_state[1]; init_state[4] &= INT_MASK; init_state[2] += init_state[3]; init_state[2] &= INT_MASK |
|||
init_state[2] ^= ((init_state[3]<<8 )&INT_MASK); init_state[5] += init_state[2]; init_state[5] &= INT_MASK; init_state[3] += init_state[4]; init_state[3] &= INT_MASK |
|||
init_state[3] ^= (init_state[4]>>16) ; init_state[6] += init_state[3]; init_state[6] &= INT_MASK; init_state[4] += init_state[5]; init_state[4] &= INT_MASK |
|||
init_state[4] ^= ((init_state[5]<<10)&INT_MASK); init_state[7] += init_state[4]; init_state[7] &= INT_MASK; init_state[5] += init_state[6]; init_state[5] &= INT_MASK |
|||
init_state[5] ^= (init_state[6]>>4 ) ; init_state[0] += init_state[5]; init_state[0] &= INT_MASK; init_state[6] += init_state[7]; init_state[6] &= INT_MASK |
|||
init_state[6] ^= ((init_state[7]<<8 )&INT_MASK); init_state[1] += init_state[6]; init_state[1] &= INT_MASK; init_state[7] += init_state[0]; init_state[7] &= INT_MASK |
|||
init_state[7] ^= (init_state[0]>>9 ) ; init_state[2] += init_state[7]; init_state[2] &= INT_MASK; init_state[0] += init_state[1]; init_state[0] &= INT_MASK |
|||
super().seed(0) # give a chance for the superclass to reset its state - the actual seed given to it doesn't matter |
|||
if seed is not None: |
|||
if isinstance(seed, str): |
|||
seed = [ord(x) for x in seed] |
|||
elif isinstance(seed, collections.Iterable): |
|||
seed = [x & INT_MASK for x in seed] |
|||
elif isinstance(seed, int): |
|||
val = abs(seed) |
|||
seed = [] |
|||
while val: |
|||
seed.append(val & INT_MASK) |
|||
val >>= 32 |
|||
else: |
|||
raise TypeError('Seed must be string, integer or iterable of integer') |
|||
# make sure the seed list is exactly 256 elements long |
|||
if len(seed)>256: |
|||
del seed[256:] |
|||
elif len(seed)<256: |
|||
seed.extend([0]*(256-len(seed))) |
|||
self.aa = self.bb = self.cc = 0 |
|||
self.mm = [] |
|||
init_state = [0x9e3779b9]*8 |
|||
for _ in range(4): |
|||
mix() |
|||
for i in range(0, 256, 8): |
|||
if seed is not None: |
|||
for j in range(8): |
|||
init_state[j] += seed[i+j] |
|||
init_state[j] &= INT_MASK |
|||
mix() |
|||
self.mm += init_state |
|||
if seed is not None: |
|||
for i in range(0, 256, 8): |
|||
for j in range(8): |
|||
init_state[j] += self.mm[i+j] |
|||
init_state[j] &= INT_MASK |
|||
mix() |
|||
for j in range(8): |
|||
self.mm[i+j] = init_state[j] |
|||
self.rand_count = 256 |
|||
self.rand_result = [0]*256 |
|||
def getstate(self): |
|||
return super().getstate(), self.aa, self.bb, self.cc, self.mm, self.rand_count, self.rand_result |
|||
def setstate(self, state): |
|||
super().setstate(state[0]) |
|||
_, self.aa, self.bb, self.cc, self.mm, self.rand_count, self.rand_result = state |
|||
def _generate(self): |
|||
# Generate 256 random 32-bit values and save them in an internal field. |
|||
# The actual random functions will dish out these values to callers. |
|||
self.cc = (self.cc + 1) & INT_MASK |
|||
self.bb = (self.bb + self.cc) & INT_MASK |
|||
for i in range(256): |
|||
x = self.mm[i] |
|||
mod = i & 3 |
|||
if mod==0: |
|||
self.aa ^= ((self.aa << 13) & INT_MASK) |
|||
elif mod==1: |
|||
self.aa ^= (self.aa >> 6) |
|||
elif mod==2: |
|||
self.aa ^= ((self.aa << 2) & INT_MASK) |
|||
else: # mod == 3 |
|||
self.aa ^= (self.aa >> 16) |
|||
self.aa = (self.mm[i^128] + self.aa) & INT_MASK |
|||
y = self.mm[i] = (self.mm[(x>>2) & 0xFF] + self.aa + self.bb) & INT_MASK |
|||
self.rand_result[i] = self.bb = (self.mm[(y>>10) & 0xFF] + x) & INT_MASK |
|||
self.rand_count = 0 |
|||
def next_int(self): |
|||
"""Return a random integer between 0 (inclusive) and 2**32 (exclusive).""" |
|||
if self.rand_count == 256: |
|||
self._generate() |
|||
result = self.rand_result[self.rand_count] |
|||
self.rand_count += 1 |
|||
return result |
|||
def getrandbits(self, k): |
|||
"""Return a random integer between 0 (inclusive) and 2**k (exclusive).""" |
|||
result = 0 |
|||
ints_needed = (k+31)//32 |
|||
ints_used = 0 |
|||
while ints_used < ints_needed: |
|||
if self.rand_count == 256: |
|||
self._generate() |
|||
ints_to_take = min(256-self.rand_count, ints_needed) |
|||
for val in self.rand_result[self.rand_count : self.rand_count+ints_to_take]: |
|||
result = (result << 32) | val |
|||
self.rand_count += ints_to_take |
|||
ints_used += ints_to_take |
|||
result &= ((1<<k)-1) # mask off extra bits, if any |
|||
return result |
|||
def random(self): |
|||
"""Return a random float between 0 (inclusive) and 1 (exclusive).""" |
|||
# A double stores 53 significant bits, so scale a 53-bit integer into the [0..1) range. |
|||
return self.getrandbits(53) * (2**-53) |
|||
def rand_char(self): |
|||
"""Return a random integer from the printable ASCII range [32..126].""" |
|||
return self.next_int() % 95 + 32 |
|||
def vernam(self, msg): |
|||
""" |
|||
Encrypt/decrypt the given bytes object with the XOR algorithm, using the current generator state. |
|||
To decrypt an encrypted string, restore the state of the generator to the state it had |
|||
during encryption, then call this method with the encrypted string. |
|||
""" |
|||
return bytes((self.rand_char() & 0xFF) ^ x for x in msg) |
|||
# Constants for selecting Caesar operation modes. |
|||
ENCIPHER = 'encipher' |
|||
DECIPHER = 'decipher' |
|||
@staticmethod |
|||
def _caesar(ciphermode, ch, shift, modulo, start): |
|||
if ciphermode == IsaacRandom.DECIPHER: |
|||
shift = -shift |
|||
n = ((ch-start)+shift) % modulo |
|||
if n<0: |
|||
n += modulo |
|||
return start+n |
|||
def caesar(self, ciphermode, msg, modulo, start): |
|||
""" |
|||
Encrypt/decrypt a string using the Caesar algorithm. |
|||
For decryption to work, the generator must be in the same state it was during encryption, |
|||
and the same modulo and start parameters must be used. |
|||
ciphermode must be one of IsaacRandom.ENCIPHER or IsaacRandom.DECIPHER. |
|||
""" |
|||
return bytes(self._caesar(ciphermode, ch, self.rand_char(), modulo, start) for ch in msg) |
|||
if __name__=='__main__': |
|||
import binascii |
|||
def hexify(b): |
|||
return binascii.hexlify(b).decode('ascii').upper() |
|||
MOD = 95 |
|||
START = 32 |
|||
msg = 'a Top Secret secret' |
|||
key = 'this is my secret key' |
|||
isaac_random = IsaacRandom(key) |
|||
vernam_encoded = isaac_random.vernam(msg.encode('ascii')) |
|||
caesar_encoded = isaac_random.caesar(IsaacRandom.ENCIPHER, msg.encode('ascii'), MOD, START) |
|||
isaac_random.seed(key) |
|||
vernam_decoded = isaac_random.vernam(vernam_encoded).decode('ascii') |
|||
caesar_decoded = isaac_random.caesar(IsaacRandom.DECIPHER, caesar_encoded, MOD, START).decode('ascii') |
|||
print('Message:', msg) |
|||
print('Key :', key) |
|||
print('XOR :', hexify(vernam_encoded)) |
|||
print('XOR dcr:', vernam_decoded) |
|||
print('MOD :', hexify(caesar_encoded)) |
|||
print('MOD dcr:', caesar_decoded) |
|||
</lang> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |