410 lines
No EOL
10 KiB
JavaScript
410 lines
No EOL
10 KiB
JavaScript
/*!
|
|
* Crypto-JS 2.5.4 BlockModes.js
|
|
* contribution from Simon Greatrix
|
|
*/
|
|
|
|
(function (C) {
|
|
|
|
// Create pad namespace
|
|
var C_pad = C.pad = {};
|
|
|
|
// Calculate the number of padding bytes required.
|
|
function _requiredPadding(cipher, message) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
var reqd = blockSizeInBytes - message.length % blockSizeInBytes;
|
|
return reqd;
|
|
}
|
|
|
|
// Remove padding when the final byte gives the number of padding bytes.
|
|
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?");
|
|
}
|
|
}
|
|
};
|
|
|
|
// No-operation padding, used for stream ciphers
|
|
C_pad.NoPadding = {
|
|
pad: function (cipher, message) { },
|
|
unpad: function (cipher, message) { }
|
|
};
|
|
|
|
// Zero Padding.
|
|
//
|
|
// If the message is not an exact number of blocks, the final block is
|
|
// completed with 0x00 bytes. There is no unpadding.
|
|
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();
|
|
}
|
|
}
|
|
};
|
|
|
|
// ISO/IEC 7816-4 padding.
|
|
//
|
|
// Pads the plain text with an 0x80 byte followed by as many 0x00
|
|
// bytes are required to complete the block.
|
|
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?");
|
|
}
|
|
};
|
|
|
|
// ANSI X.923 padding
|
|
//
|
|
// The final block is padded with zeros except for the last byte of the
|
|
// last block which contains the number of padding bytes.
|
|
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);
|
|
}
|
|
};
|
|
|
|
// ISO 10126
|
|
//
|
|
// The final block is padded with random bytes except for the last
|
|
// byte of the last block which contains the number of padding bytes.
|
|
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);
|
|
}
|
|
};
|
|
|
|
// PKCS7 padding
|
|
//
|
|
// PKCS7 is described in RFC 5652. Padding is in whole bytes. The
|
|
// value of each added byte is the number of bytes that are added,
|
|
// i.e. N bytes, each of value N are added.
|
|
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]);
|
|
}
|
|
};
|
|
|
|
// Create mode namespace
|
|
var C_mode = C.mode = {};
|
|
|
|
/**
|
|
* Mode base "class".
|
|
*/
|
|
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);
|
|
},
|
|
|
|
// Default padding
|
|
_padding: C_pad.iso7816
|
|
};
|
|
|
|
|
|
/**
|
|
* Electronic Code Book mode.
|
|
*
|
|
* ECB applies the cipher directly against each block of the input.
|
|
*
|
|
* ECB does not require an initialization vector.
|
|
*/
|
|
var ECB = C_mode.ECB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var ECB_prototype = ECB.prototype = new Mode;
|
|
|
|
// Concrete steps for Mode template
|
|
ECB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
// Encrypt each block
|
|
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;
|
|
// Decrypt each block
|
|
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
|
cipher._decryptblock(c, offset);
|
|
}
|
|
};
|
|
|
|
// ECB never uses an IV
|
|
ECB_prototype.fixOptions = function (options) {
|
|
options.iv = [];
|
|
};
|
|
|
|
|
|
/**
|
|
* Cipher block chaining
|
|
*
|
|
* The first block is XORed with the IV. Subsequent blocks are XOR with the
|
|
* previous cipher output.
|
|
*/
|
|
var CBC = C_mode.CBC = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CBC_prototype = CBC.prototype = new Mode;
|
|
|
|
// Concrete steps for Mode template
|
|
CBC_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
|
|
// Encrypt each block
|
|
for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
|
|
if (offset == 0) {
|
|
// XOR first block using IV
|
|
for (var i = 0; i < blockSizeInBytes; i++)
|
|
m[i] ^= iv[i];
|
|
} else {
|
|
// XOR this block using previous crypted block
|
|
for (var i = 0; i < blockSizeInBytes; i++)
|
|
m[offset + i] ^= m[offset + i - blockSizeInBytes];
|
|
}
|
|
// Encrypt block
|
|
cipher._encryptblock(m, offset);
|
|
}
|
|
};
|
|
CBC_prototype._doDecrypt = function (cipher, c, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4;
|
|
|
|
// At the start, the previously crypted block is the IV
|
|
var prevCryptedBlock = iv;
|
|
|
|
// Decrypt each block
|
|
for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
|
|
// Save this crypted block
|
|
var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);
|
|
// Decrypt block
|
|
cipher._decryptblock(c, offset);
|
|
// XOR decrypted block using previous crypted block
|
|
for (var i = 0; i < blockSizeInBytes; i++) {
|
|
c[offset + i] ^= prevCryptedBlock[i];
|
|
}
|
|
prevCryptedBlock = thisCryptedBlock;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Cipher feed back
|
|
*
|
|
* The cipher output is XORed with the plain text to produce the cipher output,
|
|
* which is then fed back into the cipher to produce a bit pattern to XOR the
|
|
* next block with.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var CFB = C_mode.CFB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CFB_prototype = CFB.prototype = new Mode;
|
|
|
|
// Override padding
|
|
CFB_prototype._padding = C_pad.NoPadding;
|
|
|
|
// Concrete steps for Mode template
|
|
CFB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
var blockSizeInBytes = cipher._blocksize * 4,
|
|
keystream = iv.slice(0);
|
|
|
|
// Encrypt each byte
|
|
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);
|
|
|
|
// Encrypt each byte
|
|
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;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Output feed back
|
|
*
|
|
* The cipher repeatedly encrypts its own output. The output is XORed with the
|
|
* plain text to produce the cipher text.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var OFB = C_mode.OFB = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var OFB_prototype = OFB.prototype = new Mode;
|
|
|
|
// Override padding
|
|
OFB_prototype._padding = C_pad.NoPadding;
|
|
|
|
// Concrete steps for Mode template
|
|
OFB_prototype._doEncrypt = function (cipher, m, iv) {
|
|
|
|
var blockSizeInBytes = cipher._blocksize * 4,
|
|
keystream = iv.slice(0);
|
|
|
|
// Encrypt each byte
|
|
for (var i = 0; i < m.length; i++) {
|
|
|
|
// Generate keystream
|
|
if (i % blockSizeInBytes == 0)
|
|
cipher._encryptblock(keystream, 0);
|
|
|
|
// Encrypt byte
|
|
m[i] ^= keystream[i % blockSizeInBytes];
|
|
|
|
}
|
|
};
|
|
OFB_prototype._doDecrypt = OFB_prototype._doEncrypt;
|
|
|
|
/**
|
|
* Counter
|
|
* @author Gergely Risko
|
|
*
|
|
* After every block the last 4 bytes of the IV is increased by one
|
|
* with carry and that IV is used for the next block.
|
|
*
|
|
* This is a stream cipher mode and does not require padding.
|
|
*/
|
|
var CTR = C_mode.CTR = function () {
|
|
// Call parent constructor
|
|
Mode.apply(this, arguments);
|
|
};
|
|
|
|
// Inherit from Mode
|
|
var CTR_prototype = CTR.prototype = new Mode;
|
|
|
|
// Override padding
|
|
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; ) {
|
|
// do not lose iv
|
|
var keystream = counter.slice(0);
|
|
|
|
// Generate keystream for next block
|
|
cipher._encryptblock(keystream, 0);
|
|
|
|
// XOR keystream with block
|
|
for (var j = 0; i < m.length && j < blockSizeInBytes; j++, i++) {
|
|
m[i] ^= keystream[j];
|
|
}
|
|
|
|
// Increase counter
|
|
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); |