(function (C) {
var C_pad = C.pad = {};
function _requiredPadding(cipher, message) {
var blockSizeInBytes = cipher._blocksize * 4;
var reqd = blockSizeInBytes - message.length % blockSizeInBytes;
return reqd;
}
var _unpadLength = function (cipher, message, alg, padding) {
var pad = message.pop();
if (pad == 0) {
throw new Error("Invalid zero-length padding specified for " + alg
+ ". Wrong cipher specification or key used?");
}
var maxPad = cipher._blocksize * 4;
if (pad > maxPad) {
throw new Error("Invalid padding length of " + pad
+ " specified for " + alg
+ ". Wrong cipher specification or key used?");
}
for (var i = 1; i < pad; i++) {
var b = message.pop();
if (padding != undefined && padding != b) {
throw new Error("Invalid padding byte of 0x" + b.toString(16)
+ " specified for " + alg
+ ". Wrong cipher specification or key used?");
}
}
};
C_pad.NoPadding = {
pad: function (cipher, message) { },
unpad: function (cipher, message) { }
};
C_pad.ZeroPadding = {
pad: function (cipher, message) {
var blockSizeInBytes = cipher._blocksize * 4;
var reqd = message.length % blockSizeInBytes;
if (reqd != 0) {
for (reqd = blockSizeInBytes - reqd; reqd > 0; reqd--) {
message.push(0x00);
}
}
},
unpad: function (cipher, message) {
while (message[message.length - 1] == 0) {
message.pop();
}
}
};
C_pad.iso7816 = {
pad: function (cipher, message) {
var reqd = _requiredPadding(cipher, message);
message.push(0x80);
for (; reqd > 1; reqd--) {
message.push(0x00);
}
},
unpad: function (cipher, message) {
var padLength;
for (padLength = cipher._blocksize * 4; padLength > 0; padLength--) {
var b = message.pop();
if (b == 0x80) return;
if (b != 0x00) {
throw new Error("ISO-7816 padding byte must be 0, not 0x" + b.toString(16) + ". Wrong cipher specification or key used?");
}
}
throw new Error("ISO-7816 padded beyond cipher block size. Wrong cipher specification or key used?");
}
};
C_pad.ansix923 = {
pad: function (cipher, message) {
var reqd = _requiredPadding(cipher, message);
for (var i = 1; i < reqd; i++) {
message.push(0x00);
}
message.push(reqd);
},
unpad: function (cipher, message) {
_unpadLength(cipher, message, "ANSI X.923", 0);
}
};
C_pad.iso10126 = {
pad: function (cipher, message) {
var reqd = _requiredPadding(cipher, message);
for (var i = 1; i < reqd; i++) {
message.push(Math.floor(Math.random() * 256));
}
message.push(reqd);
},
unpad: function (cipher, message) {
_unpadLength(cipher, message, "ISO 10126", undefined);
}
};
C_pad.pkcs7 = {
pad: function (cipher, message) {
var reqd = _requiredPadding(cipher, message);
for (var i = 0; i < reqd; i++) {
message.push(reqd);
}
},
unpad: function (cipher, message) {
_unpadLength(cipher, message, "PKCS 7", message[message.length - 1]);
}
};
var C_mode = C.mode = {};
var Mode = C_mode.Mode = function (padding) {
if (padding) {
this._padding = padding;
}
};
Mode.prototype = {
encrypt: function (cipher, m, iv) {
this._padding.pad(cipher, m);
this._doEncrypt(cipher, m, iv);
},
decrypt: function (cipher, m, iv) {
this._doDecrypt(cipher, m, iv);
this._padding.unpad(cipher, m);
},
_padding: C_pad.iso7816
};
var ECB = C_mode.ECB = function () {
Mode.apply(this, arguments);
};
var ECB_prototype = ECB.prototype = new Mode;
ECB_prototype._doEncrypt = function (cipher, m, iv) {
var blockSizeInBytes = cipher._blocksize * 4;
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
cipher._encryptblock(m, offset);
}
};
ECB_prototype._doDecrypt = function (cipher, c, iv) {
var blockSizeInBytes = cipher._blocksize * 4;
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
cipher._decryptblock(c, offset);
}
};
ECB_prototype.fixOptions = function (options) {
options.iv = [];
};
var CBC = C_mode.CBC = function () {
Mode.apply(this, arguments);
};
var CBC_prototype = CBC.prototype = new Mode;
CBC_prototype._doEncrypt = function (cipher, m, iv) {
var blockSizeInBytes = cipher._blocksize * 4;
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
if (offset == 0) {
for (var i = 0; i < blockSizeInBytes; i++)
m[i] ^= iv[i];
} else {
for (var i = 0; i < blockSizeInBytes; i++)
m[offset + i] ^= m[offset + i - blockSizeInBytes];
}
cipher._encryptblock(m, offset);
}
};
CBC_prototype._doDecrypt = function (cipher, c, iv) {
var blockSizeInBytes = cipher._blocksize * 4;
var prevCryptedBlock = iv;
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);
cipher._decryptblock(c, offset);
for (var i = 0; i < blockSizeInBytes; i++) {
c[offset + i] ^= prevCryptedBlock[i];
}
prevCryptedBlock = thisCryptedBlock;
}
};
var CFB = C_mode.CFB = function () {
Mode.apply(this, arguments);
};
var CFB_prototype = CFB.prototype = new Mode;
CFB_prototype._padding = C_pad.NoPadding;
CFB_prototype._doEncrypt = function (cipher, m, iv) {
var blockSizeInBytes = cipher._blocksize * 4,
keystream = iv.slice(0);
for (var i = 0; i < m.length; i++) {
var j = i % blockSizeInBytes;
if (j == 0) cipher._encryptblock(keystream, 0);
m[i] ^= keystream[j];
keystream[j] = m[i];
}
};
CFB_prototype._doDecrypt = function (cipher, c, iv) {
var blockSizeInBytes = cipher._blocksize * 4,
keystream = iv.slice(0);
for (var i = 0; i < c.length; i++) {
var j = i % blockSizeInBytes;
if (j == 0) cipher._encryptblock(keystream, 0);
var b = c[i];
c[i] ^= keystream[j];
keystream[j] = b;
}
};
var OFB = C_mode.OFB = function () {
Mode.apply(this, arguments);
};
var OFB_prototype = OFB.prototype = new Mode;
OFB_prototype._padding = C_pad.NoPadding;
OFB_prototype._doEncrypt = function (cipher, m, iv) {
var blockSizeInBytes = cipher._blocksize * 4,
keystream = iv.slice(0);
for (var i = 0; i < m.length; i++) {
if (i % blockSizeInBytes == 0)
cipher._encryptblock(keystream, 0);
m[i] ^= keystream[i % blockSizeInBytes];
}
};
OFB_prototype._doDecrypt = OFB_prototype._doEncrypt;
var CTR = C_mode.CTR = function () {
Mode.apply(this, arguments);
};
var CTR_prototype = CTR.prototype = new Mode;
CTR_prototype._padding = C_pad.NoPadding;
CTR_prototype._doEncrypt = function (cipher, m, iv) {
var blockSizeInBytes = cipher._blocksize * 4;
var counter = iv.slice(0);
for (var i = 0; i < m.length; ) {
var keystream = counter.slice(0);
cipher._encryptblock(keystream, 0);
for (var j = 0; i < m.length && j < blockSizeInBytes; j++, i++) {
m[i] ^= keystream[j];
}
if (++(counter[blockSizeInBytes - 1]) == 256) {
counter[blockSizeInBytes - 1] = 0;
if (++(counter[blockSizeInBytes - 2]) == 256) {
counter[blockSizeInBytes - 2] = 0;
if (++(counter[blockSizeInBytes - 3]) == 256) {
counter[blockSizeInBytes - 3] = 0;
++(counter[blockSizeInBytes - 4]);
}
}
}
}
};
CTR_prototype._doDecrypt = CTR_prototype._doEncrypt;
})(Crypto);