// To see how the S-boxes are generated, see gen_sbox.pl
var sbox = [6,14,10,0,7,13,9,1,3,15,11,2,8,12,5,4];
var rsbox = [3,7,11,8,15,14,0,4,12,6,2,10,13,5,1,9];

function numToHexString(b, bits) {
	var out = '';
	for (var i=0; i<bits; i += 4) {
		var n = (b >> (bits - 4)) & 0xF;
		if (n < 10) {
			out += String.fromCharCode(48 + n);
		} else {
			out += String.fromCharCode(55 + n);
		}
		b <<= 4;
	}
	return out;
}

function hexStringToNum(str) {
	var n = 0;
	str = str.toUpperCase();

	for (var i=0; i < str.length; i++) {
		n <<= 4;
		var c = str.charCodeAt(i);
		if (c >= 48 && c <= 57) {
			n |= c - 48;
		} else if (c >= 65 && c <= 70) {
			n |= c - 55;
		} else {
			// Unknown character
			n >>= 4;
			continue;
		}
	}
	return n;
}

function permute(x) {
	var y = 0;

	for (var i=0; i < 4; i++) {
		y <<= 4;
		y |= sbox[x >> 12 & 0xF];
		x <<= 4;
	}
	return y;
}

function unpermute(y) {
	var x = 0;
	for (var i=0; i < 4; i++) {
		x <<= 4;
		x |= rsbox[y >> 12 & 0xF];
		y <<= 4;
	}
	return x;
}

var ekey = 0;

function encrypt_block(block) {
	for (var i=0; i < 16; i++) {
		block = permute(ekey ^ block);
	}
	return block;
}

function decrypt_block(block) {
	for (var i=0; i < 16; i++) {
		block = unpermute(block) ^ ekey;
	}
	return block;
}

// Encrypt ptext with key k in CBC mode. IV is the last two bytes of the MD5.
function encrypt(k, ptext) {
	ekey = k
	var ctext = hex_md5(plaintext.value).toUpperCase();
	iv = hexStringToNum(ctext.substring(28));

	for (var i=0; i < ptext.length; i += 2) {
		var block = ptext.charCodeAt(i) << 8 | ptext.charCodeAt(i+1);
		block = block ^ iv;
		block = encrypt_block(block);
		ctext += numToHexString(block, 16);
		iv = block;
	}
	return ctext;
}

function decrypt(k, ctext) {
	ekey = k
	var ptext = '';
	var c;
	var md5 = ctext.substr(0,32).toLowerCase();
	var iv = hexStringToNum(md5.substring(28));
	var next_iv;
	ctext = ctext.substring(32);

	for (var i=0; i < ctext.length; i += 4) {
		var block = hexStringToNum(ctext.substr(i, 4));
		next_iv = block;
		block = decrypt_block(block) ^ iv;
		c = block >> 8 & 0xFF;
		if (c != 0) ptext += String.fromCharCode(c);
		c = block & 0xFF;
		if (c != 0) ptext += String.fromCharCode(c);
		iv = next_iv;
	}

	if (hex_md5(ptext) != md5) {
		return null;
	} else {
		return ptext;
	}
}
