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}}==