Task: The ISAAC cipher




Go: package main

import "fmt"

const (
    msg = "a Top Secret secret"
    key = "this is my secret key"
)

func main() {
    var z state
    z.seed(key)
    fmt.Println("Message: ", msg)
    fmt.Println("Key    : ", key)
    fmt.Println("XOR    : ", z.vernam(msg))
}

type state struct {
    aa, bb, cc uint32
    mm         [256]uint32
    randrsl    [256]uint32
    randcnt    int
}

func (z *state) isaac() {
    z.cc++
    z.bb += z.cc
    for i, x := range z.mm {
        switch i % 4 {
        case 0:
            z.aa = z.aa ^ z.aa<<13
        case 1:
            z.aa = z.aa ^ z.aa>>6
        case 2:
            z.aa = z.aa ^ z.aa<<2
        case 3:
            z.aa = z.aa ^ z.aa>>16
        }
        z.aa += z.mm[(i+128)%256]
        y := z.mm[x>>2%256] + z.aa + z.bb
        z.mm[i] = y
        z.bb = z.mm[y>>10%256] + x
        z.randrsl[i] = z.bb
    }
}

func (z *state) randInit() {
    const gold = uint32(0x9e3779b9)
    a := [8]uint32{gold, gold, gold, gold, gold, gold, gold, gold}
    mix1 := func(i int, v uint32) {
        a[i] ^= v
        a[(i+3)%8] += a[i]
        a[(i+1)%8] += a[(i+2)%8]
    }
    mix := func() {
        mix1(0, a[1]<<11)
        mix1(1, a[2]>>2)
        mix1(2, a[3]<<8)
        mix1(3, a[4]>>16)
        mix1(4, a[5]<<10)
        mix1(5, a[6]>>4)
        mix1(6, a[7]<<8)
        mix1(7, a[0]>>9)
    }
    for i := 0; i < 4; i++ {
        mix()
    }
    for i := 0; i < 256; i += 8 {
        for j, rj := range z.randrsl[i : i+8] {
            a[j] += rj
        }
        mix()
        for j, aj := range a {
            z.mm[i+j] = aj
        }
    }
    for i := 0; i < 256; i += 8 {
        for j, mj := range z.mm[i : i+8] {
            a[j] += mj
        }
        mix()
        for j, aj := range a {
            z.mm[i+j] = aj
        }
    }
    z.isaac()
}

func (z *state) seed(seed string) {
    for i, r := range seed {
        if i == 256 {
            break
        }
        z.randrsl[i] = uint32(r)
    }
    z.randInit()
}

func (z *state) random() (r uint32) {
    r = z.randrsl[z.randcnt]
    z.randcnt++
    if z.randcnt == 256 {
        z.isaac()
        z.randcnt = 0
    }
    return
}

func (z *state) randA() byte {
    return byte(z.random()%95 + 32)
}

func (z *state) vernam(msg string) string {
    b := []byte(msg)
    for i := range b {
        b[i] ^= z.randA()
    }
    return fmt.Sprintf("%X", b)
}




Python: import random
import collections

INT_MASK = 0xFFFFFFFF       

class IsaacRandom(random.Random):
    

    def seed(self, seed=None):
        
        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) 
        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')
            
            
            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):
        
        
        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: 
                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):
        
        if self.rand_count == 256:
            self._generate()
        result = self.rand_result[self.rand_count]
        self.rand_count += 1
        return result
        
    def getrandbits(self, k):
        
        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)    
        return result
    
    def random(self):
        
        
        return self.getrandbits(53) * (2**-53)
    
    def rand_char(self):
        
        return self.next_int() % 95 + 32
    
    def vernam(self, msg):
        
        return bytes((self.rand_char() & 0xFF) ^ x for x in msg)
    
    
    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):
        
        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)