Created
July 15, 2013 20:44
-
-
Save lancecarlson/6003283 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
// Global Crypto object | |
var Crypto = exports.Crypto = {}; | |
// Crypto utilities | |
var util = Crypto.util = { | |
// Bit-wise rotate left | |
rotl: function (n, b) { | |
return (n << b) | (n >>> (32 - b)); | |
}, | |
// Bit-wise rotate right | |
rotr: function (n, b) { | |
return (n << (32 - b)) | (n >>> b); | |
}, | |
// Swap big-endian to little-endian and vice versa | |
endian: function (n) { | |
// If number given, swap endian | |
if (n.constructor == Number) { | |
return util.rotl(n, 8) & 0x00FF00FF | | |
util.rotl(n, 24) & 0xFF00FF00; | |
} | |
// Else, assume array and swap all items | |
for (var i = 0; i < n.length; i++) | |
n[i] = util.endian(n[i]); | |
return n; | |
}, | |
// Generate an array of any length of random bytes | |
randomBytes: function (n) { | |
for (var bytes = []; n > 0; n--) | |
bytes.push(Math.floor(Math.random() * 256)); | |
return bytes; | |
}, | |
// Convert a byte array to big-endian 32-bit words | |
bytesToWords: function (bytes) { | |
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) | |
words[b >>> 5] |= bytes[i] << (24 - b % 32); | |
return words; | |
}, | |
// Convert big-endian 32-bit words to a byte array | |
wordsToBytes: function (words) { | |
for (var bytes = [], b = 0; b < words.length * 32; b += 8) | |
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); | |
return bytes; | |
}, | |
// Convert a byte array to a hex string | |
bytesToHex: function (bytes) { | |
for (var hex = [], i = 0; i < bytes.length; i++) { | |
hex.push((bytes[i] >>> 4).toString(16)); | |
hex.push((bytes[i] & 0xF).toString(16)); | |
} | |
return hex.join(""); | |
}, | |
// Convert a hex string to a byte array | |
hexToBytes: function (hex) { | |
for (var bytes = [], c = 0; c < hex.length; c += 2) | |
bytes.push(parseInt(hex.substr(c, 2), 16)); | |
return bytes; | |
}, | |
// Convert a string to a base-64 string | |
stringToBase64: function (str, encoding) { | |
var bytes; | |
if ( typeof encoding != 'string') { | |
bytes = UTF8.stringToBytes(str); | |
} else if ( encoding.toLowerCase() == 'binary' ) { | |
bytes = Binary.stringToBytes(str); | |
} else if ( encoding.toLowerCase() == 'utf8' || encoding.toLowerCase() == 'utf-8' ) { | |
bytes = UTF8.stringToBytes(str); | |
} else { | |
bytes = UTF8.stringToBytes(str); | |
} | |
return this.bytesToBase64(bytes); | |
}, | |
// Convert a base-64 string to origin string | |
base64ToString: function (base64, encoding) { | |
var bytes, str; | |
bytes = this.base64ToBytes(base64); | |
if ( typeof encoding != 'string') { | |
str = UTF8.bytesToString(bytes); | |
} else if ( encoding.toLowerCase() == 'binary' ) { | |
str = Binary.bytesToString(bytes); | |
} else if ( encoding.toLowerCase() == 'utf8' || encoding.toLowerCase() == 'utf-8' ) { | |
str = UTF8.bytesToString(bytes); | |
} else { | |
str = UTF8.bytesToString(bytes); | |
} | |
return str; | |
}, | |
// Convert a byte array to a base-64 string | |
bytesToBase64: function (bytes) { | |
// Use browser-native function if it exists | |
if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes)); | |
for(var base64 = [], i = 0; i < bytes.length; i += 3) { | |
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; | |
for (var j = 0; j < 4; j++) { | |
if (i * 8 + j * 6 <= bytes.length * 8) | |
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); | |
else base64.push("="); | |
} | |
} | |
return base64.join(""); | |
}, | |
// Convert a base-64 string to a byte array | |
base64ToBytes: function (base64) { | |
// Use browser-native function if it exists | |
if (typeof atob == "function") return Binary.stringToBytes(atob(base64)); | |
// Remove non-base-64 characters | |
base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); | |
for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { | |
if (imod4 == 0) continue; | |
bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | | |
(base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); | |
} | |
return bytes; | |
} | |
}; | |
// Crypto character encodings | |
var charenc = Crypto.charenc = {}; | |
// UTF-8 encoding | |
var UTF8 = charenc.UTF8 = { | |
// Convert a string to a byte array | |
stringToBytes: function (str) { | |
return Binary.stringToBytes(unescape(encodeURIComponent(str))); | |
}, | |
// Convert a byte array to a string | |
bytesToString: function (bytes) { | |
return decodeURIComponent(escape(Binary.bytesToString(bytes))); | |
} | |
}; | |
// Binary encoding | |
var Binary = charenc.Binary = { | |
// Convert a string to a byte array | |
stringToBytes: function (str) { | |
for (var bytes = [], i = 0; i < str.length; i++) | |
bytes.push(str.charCodeAt(i) & 0xFF); | |
return bytes; | |
}, | |
// Convert a byte array to a string | |
bytesToString: function (bytes) { | |
for (var str = [], i = 0; i < bytes.length; i++) | |
str.push(String.fromCharCode(bytes[i])); | |
return str.join(""); | |
} | |
}; | |
// Shortcut | |
var util = Crypto.util; | |
// Convert n to unsigned 32-bit integer | |
util.u32 = function (n) { | |
return n >>> 0; | |
}; | |
// Unsigned 32-bit addition | |
util.add = function () { | |
var result = this.u32(arguments[0]); | |
for (var i = 1; i < arguments.length; i++) | |
result = this.u32(result + this.u32(arguments[i])); | |
return result; | |
}; | |
// Unsigned 32-bit multiplication | |
util.mult = function (m, n) { | |
return this.add((n & 0xFFFF0000) * m, | |
(n & 0x0000FFFF) * m); | |
}; | |
// Unsigned 32-bit greater than (>) comparison | |
util.gt = function (m, n) { | |
return this.u32(m) > this.u32(n); | |
}; | |
// Unsigned 32-bit less than (<) comparison | |
util.lt = function (m, n) { | |
return this.u32(m) < this.u32(n); | |
}; | |
// Create pad namespace | |
var C_pad = Crypto.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 (message) { | |
var pad = message.pop(); | |
for (var i = 1; i < pad; i++) { | |
message.pop(); | |
} | |
}; | |
// No-operation padding, used for stream ciphers | |
C_pad.NoPadding = { | |
pad : function (cipher,message) {}, | |
unpad : function (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 (message) {} | |
}; | |
// 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 (message) { | |
while (message.pop() != 0x80) {} | |
} | |
}; | |
// 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 : _unpadLength | |
}; | |
// 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 : _unpadLength | |
}; | |
// 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 : _unpadLength | |
}; | |
// Create mode namespace | |
var C_mode = Crypto.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(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; | |
for (var i = 0; i < m.length;) { | |
// do not lose iv | |
var keystream = iv.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 IV | |
if(++(iv[blockSizeInBytes-1]) == 256) { | |
iv[blockSizeInBytes-1] = 0; | |
if(++(iv[blockSizeInBytes-2]) == 256) { | |
iv[blockSizeInBytes-2] = 0; | |
if(++(iv[blockSizeInBytes-3]) == 256) { | |
iv[blockSizeInBytes-3] = 0; | |
++(iv[blockSizeInBytes-4]); | |
} | |
} | |
} | |
} | |
}; | |
CTR_prototype._doDecrypt = CTR_prototype._doEncrypt; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
log('test'); | |
var TEST = exports.TEST = function(test) { | |
return 'blah'; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Crypto = exports.Crypto = require('./lib/crypto').Crypto; | |
[ 'test' | |
].forEach( function (path) { | |
var m = require('./lib/' + path); | |
log('loaded module') | |
log(m) | |
for ( var i in m ) { | |
Crypto[i] = m[i]; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment