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


Java: import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Random;


public class IsaacRandom extends Random {
	
	private static final long serialVersionUID = 1L;
	
	private final int[] randResult = new int[256];    
	private int valuesUsed;                           
	
	
	private final int[] mm = new int[256];
	private int aa, bb, cc;
	
	public IsaacRandom() {
		super(0);
		init(null);
	}
	
	public IsaacRandom(int[] seed) {
		super(0);
		setSeed(seed);
	}
	
	public IsaacRandom(String seed) {
		super(0);
		setSeed(seed);
	}
	
	private void generateMoreResults() {
		cc++;
		bb += cc;
		
		for (int i=0; i<256; i++) {
			int x = mm[i];
			switch (i&3) {
			case 0:
				aa = aa^(aa<<13);
				break;
			case 1:
				aa = aa^(aa>>>6);
				break;
			case 2:
				aa = aa^(aa<<2);
				break;
			case 3:
				aa = aa^(aa>>>16);
				break;
			}
			aa = mm[i^128] + aa;
			int y = mm[i] = mm[(x>>>2) & 0xFF] + aa + bb;
			randResult[i] = bb = mm[(y>>>10) & 0xFF] + x;
		}
		
		valuesUsed = 0;
	}
	
	private static void mix(int[] s) {
		   s[0]^=s[1]<<11;  s[3]+=s[0]; s[1]+=s[2];
		   s[1]^=s[2]>>>2;  s[4]+=s[1]; s[2]+=s[3];
		   s[2]^=s[3]<<8;   s[5]+=s[2]; s[3]+=s[4];
		   s[3]^=s[4]>>>16; s[6]+=s[3]; s[4]+=s[5];
		   s[4]^=s[5]<<10;  s[7]+=s[4]; s[5]+=s[6];
		   s[5]^=s[6]>>>4;  s[0]+=s[5]; s[6]+=s[7];
		   s[6]^=s[7]<<8;   s[1]+=s[6]; s[7]+=s[0];
		   s[7]^=s[0]>>>9;  s[2]+=s[7]; s[0]+=s[1];
	}
	
	private void init(int[] seed) {
		if (seed != null && seed.length != 256) {
			seed = Arrays.copyOf(seed, 256);
		}
		aa = bb = cc = 0;
		int[] initState = new int[8];
		Arrays.fill(initState, 0x9e3779b9);	
		
		for (int i=0; i<4; i++) {
			mix(initState);
		}
		
		for (int i=0; i<256; i+=8) {
			if (seed != null) {
				for (int j=0; j<8; j++) {
					initState[j] += seed[i+j];
				}
			}
			mix(initState);
			for (int j=0; j<8; j++) {
				mm[i+j] = initState[j];
			}
		}
		
		if (seed != null) {
			for (int i=0; i<256; i+=8) {
				for (int j=0; j<8; j++) {
					initState[j] += mm[i+j];
				}
				
				mix(initState);
				
				for (int j=0; j<8; j++) {
					mm[i+j] = initState[j];
				}
			}
		}
		
		valuesUsed = 256;	
	}
	
	@Override
	protected int next(int bits) {
		if (valuesUsed == 256) {
			generateMoreResults();
			assert(valuesUsed == 0);
		}
		int value = randResult[valuesUsed];
		valuesUsed++;
		return value >>> (32-bits);
	}
	
	@Override
	public synchronized void setSeed(long seed) {
		super.setSeed(0);
		if (mm == null) {
			
			
			
			return;
		}
		int[] arraySeed = new int[256];
		arraySeed[0] = (int) (seed & 0xFFFFFFFF);
		arraySeed[1] = (int) (seed >>> 32);
		init(arraySeed);
	}
	
	public synchronized void setSeed(int[] seed) {
		super.setSeed(0);
		init(seed);
	}
	
	public synchronized void setSeed(String seed) {
		super.setSeed(0);
		char[] charSeed = seed.toCharArray();
		int[] intSeed = new int[charSeed.length];
		for (int i=0; i<charSeed.length; i++) {
			intSeed[i] = charSeed[i];
		}
		init(intSeed);
	}
	
	public int randomChar() {
		long unsignedNext = nextInt() & 0xFFFFFFFFL;	
		return (int) (unsignedNext % 95 + 32);		    
	}
	
	public enum CipherMode { ENCIPHER, DECIPHER, NONE };
	
	public byte[] vernamCipher(byte[] input) {
		byte[] result = new byte[input.length];
		for (int i=0; i<input.length; i++) {
			result[i] = (byte) (randomChar() ^ input[i]);
		}
		return result;
	}
	
	private static byte caesarShift(CipherMode mode, byte ch, int shift, byte modulo, byte start) {
		if (mode == CipherMode.DECIPHER) {
			shift = -shift;
		}
		int n = (ch-start) + shift;
		n %= modulo;
		if (n<0) {
			n += modulo;
		}
		return (byte) (start + n);
	}
	
	public byte[] caesarCipher(CipherMode mode, byte[] input, byte modulo, byte start) {
		byte[] result = new byte[input.length];
		for (int i=0; i<input.length; i++) {
			result[i] = caesarShift(mode, input[i], randomChar(), modulo, start);
		}
		return result;
	}
	
	private static String toHexString(byte[] input) {
		
		StringBuilder sb = new StringBuilder(input.length*2);
		for (byte b : input) {
			sb.append(String.format("%02X", b));
		}
		return sb.toString();
	}
	
	public static void main(String[] args) {
		final byte MOD = 95;
		final byte START = 32;
		
		String secret = "a Top Secret secret";
		String key = "this is my secret key";
		
		IsaacRandom random = new IsaacRandom(key);
		byte[] vernamResult;
		byte[] caesarResult;
		String vernamDecrypted;
		String caesarDecrypted;
		try {
			vernamResult = random.vernamCipher(secret.getBytes("ASCII"));
			caesarResult = random.caesarCipher(CipherMode.ENCIPHER, secret.getBytes("ASCII"), MOD, START);
			random.setSeed(key);
			vernamDecrypted = new String(random.vernamCipher(vernamResult), "ASCII");
			caesarDecrypted = new String(random.caesarCipher(CipherMode.DECIPHER, caesarResult, MOD, START), "ASCII");
		} catch (UnsupportedEncodingException e) {
			throw new InternalError("JVM isn't conforming - ASCII encoding isn't available");
		}
		System.out.printf("Message: %s\n", secret);
		System.out.printf("Key    : %s\n", key);
		System.out.printf("XOR    : %s\n", toHexString(vernamResult));
		System.out.printf("XOR dcr: %s\n", vernamDecrypted);
		System.out.printf("MOD    : %s\n", toHexString(caesarResult));
		System.out.printf("MOD dcr: %s\n", caesarDecrypted);
	}
}