-
-
Save satnami/886caf8d55e0c9615c1f to your computer and use it in GitHub Desktop.
AES encrypt and decrypt
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
CmdUtils.CreateCommand({ | |
name: "AES encrypt", | |
takes: {plaintext: noun_arb_text}, | |
modifiers: {key: noun_arb_text}, | |
icon: "chrome://ubiquity/skin/icons/sum.png", | |
description: "Encrypt the selected text", | |
execute: function( directObj, modifiers) | |
{ | |
if (modifiers.key.text.length <= 0) | |
displayMessage("No key has been enter selected."); | |
else | |
{ | |
if (directObj.text && modifiers.key.text) | |
CmdUtils.setSelection( AESEncryptCtr(directObj.text, modifiers.key.text, 256) ); | |
} | |
}, | |
preview: function(pBlock, directObj, modifiers) | |
{ | |
if (modifiers.key.text.length > 0) | |
{ | |
if (directObj.text && modifiers.key.text) | |
pBlock.innerHTML = "Encrypted text is: " + | |
AESEncryptCtr(directObj.text, modifiers.key.text, 256); | |
else | |
{ | |
var msg = 'AES encrypt: "<i>${plaintext}</i>" with key: "<i>${key}</i>"'; | |
pBlock.innerHTML = CmdUtils.renderTemplate( msg, {plaintext: this.plaintext.text, key: this.modifiers.key.text} ); | |
} | |
} | |
else | |
pBlock.innerHTML = "Enter or select the text you would like to encrypt, then type KEY then your key."; | |
} | |
}); | |
CmdUtils.CreateCommand({ | |
name: "AES decrypt", | |
takes: {plaintext: noun_arb_text}, | |
modifiers: {key: noun_arb_text}, | |
icon: "chrome://ubiquity/skin/icons/sum.png", | |
description: "Decrypt the selected text", | |
execute: function( directObj, modifiers) | |
{ | |
if (modifiers.key.text.length <= 0) | |
displayMessage("No key has been enter selected."); | |
else | |
{ | |
if (directObj.text && modifiers.key.text) | |
CmdUtils.setSelection( AESDecryptCtr(directObj.text, modifiers.key.text, 256) ); | |
} | |
}, | |
preview: function(pBlock, directObj, modifiers) | |
{ | |
if (modifiers.key.text.length > 0) | |
{ | |
if (directObj.text && modifiers.key.text) | |
pBlock.innerHTML = "Encrypted text is: " + | |
AESDecryptCtr(directObj.text, modifiers.key.text, 256); | |
else | |
{ | |
var msg = 'AES Decrypt: "<i>${plaintext}</i>" with key: "<i>${key}</i>"'; | |
pBlock.innerHTML = CmdUtils.renderTemplate( msg, {plaintext: this.plaintext.text, key: this.modifiers.key.text} ); | |
} | |
} | |
else | |
pBlock.innerHTML = "Enter or select the text you would like to decrypt, then type KEY then your key."; | |
} | |
}); | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
/* | |
* AES Cipher function: encrypt 'input' with Rijndael algorithm | |
* | |
* takes byte-array 'input' (16 bytes) | |
* 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) | |
* | |
* applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage | |
* | |
* returns byte-array encrypted value (16 bytes) | |
*/ | |
function Cipher(input, w) { // main Cipher function [§5.1] | |
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) | |
var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys | |
var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] | |
for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; | |
state = AddRoundKey(state, w, 0, Nb); | |
for (var round=1; round<Nr; round++) { | |
state = SubBytes(state, Nb); | |
state = ShiftRows(state, Nb); | |
state = MixColumns(state, Nb); | |
state = AddRoundKey(state, w, round, Nb); | |
} | |
state = SubBytes(state, Nb); | |
state = ShiftRows(state, Nb); | |
state = AddRoundKey(state, w, Nr, Nb); | |
var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4] | |
for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)]; | |
return output; | |
} | |
function SubBytes(s, Nb) { // apply SBox to state S [§5.1.1] | |
for (var r=0; r<4; r++) { | |
for (var c=0; c<Nb; c++) s[r][c] = Sbox[s[r][c]]; | |
} | |
return s; | |
} | |
function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.2] | |
var t = new Array(4); | |
for (var r=1; r<4; r++) { | |
for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy | |
for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back | |
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): | |
return s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf | |
} | |
function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3] | |
for (var c=0; c<4; c++) { | |
var a = new Array(4); // 'a' is a copy of the current column from 's' | |
var b = new Array(4); // 'b' is a•{02} in GF(2^8) | |
for (var i=0; i<4; i++) { | |
a[i] = s[i][c]; | |
b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1; | |
} | |
// a[n] ^ b[n] is a•{03} in GF(2^8) | |
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3 | |
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3 | |
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3 | |
s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3 | |
} | |
return s; | |
} | |
function AddRoundKey(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4] | |
for (var r=0; r<4; r++) { | |
for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r]; | |
} | |
return state; | |
} | |
function KeyExpansion(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2] | |
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) | |
var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys | |
var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys | |
var w = new Array(Nb*(Nr+1)); | |
var temp = new Array(4); | |
for (var i=0; i<Nk; i++) { | |
var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]]; | |
w[i] = r; | |
} | |
for (var i=Nk; i<(Nb*(Nr+1)); i++) { | |
w[i] = new Array(4); | |
for (var t=0; t<4; t++) temp[t] = w[i-1][t]; | |
if (i % Nk == 0) { | |
temp = SubWord(RotWord(temp)); | |
for (var t=0; t<4; t++) temp[t] ^= Rcon[i/Nk][t]; | |
} else if (Nk > 6 && i%Nk == 4) { | |
temp = SubWord(temp); | |
} | |
for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; | |
} | |
return w; | |
} | |
function SubWord(w) { // apply SBox to 4-byte word w | |
for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; | |
return w; | |
} | |
function RotWord(w) { // rotate 4-byte word w left by one byte | |
var tmp = w[0]; | |
for (var i=0; i<3; i++) w[i] = w[i+1]; | |
w[3] = tmp; | |
return w; | |
} | |
// Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] | |
var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, | |
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, | |
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, | |
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, | |
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, | |
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, | |
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, | |
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, | |
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, | |
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, | |
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, | |
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, | |
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, | |
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, | |
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, | |
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; | |
// Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] | |
var Rcon = [ [0x00, 0x00, 0x00, 0x00], | |
[0x01, 0x00, 0x00, 0x00], | |
[0x02, 0x00, 0x00, 0x00], | |
[0x04, 0x00, 0x00, 0x00], | |
[0x08, 0x00, 0x00, 0x00], | |
[0x10, 0x00, 0x00, 0x00], | |
[0x20, 0x00, 0x00, 0x00], | |
[0x40, 0x00, 0x00, 0x00], | |
[0x80, 0x00, 0x00, 0x00], | |
[0x1b, 0x00, 0x00, 0x00], | |
[0x36, 0x00, 0x00, 0x00] ]; | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
/** | |
* Encrypt a text using AES encryption in Counter mode of operation | |
* - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf | |
* | |
* Unicode multi-byte character safe | |
* | |
* @param plaintext source text to be encrypted | |
* @param password the password to use to generate a key | |
* @param nBits number of bits to be used in the key (128, 192, or 256) | |
* @return encrypted text | |
*/ | |
function AESEncryptCtr(plaintext, password, nBits) { | |
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES | |
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys | |
plaintext = plaintext.encodeUTF8(); | |
password = password.encodeUTF8(); | |
//var t = new Date(); // timer | |
// use AES itself to encrypt password to get cipher key (using plain password as source for key | |
// expansion) - gives us well encrypted key | |
var nBytes = nBits/8; // no bytes in key | |
var pwBytes = new Array(nBytes); | |
for (var i=0; i<nBytes; i++) { | |
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); | |
} | |
var key = Cipher(pwBytes, KeyExpansion(pwBytes)); // gives us 16-byte key | |
key = key.concat(key.slice(0, nBytes-32)); // expand key to 16/24/32 bytes long | |
// initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes, | |
// block counter in 2nd 8 bytes | |
var counterBlock = new Array(blockSize); | |
var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970 | |
var nonceSec = Math.floor(nonce/1000); | |
var nonceMs = nonce%1000; | |
// encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes | |
for (var i=0; i<4; i++) counterBlock[i] = (nonceSec >>> i*8) & 0xff; | |
for (var i=0; i<4; i++) counterBlock[i+4] = nonceMs & 0xff; | |
// and convert it to a string to go on the front of the ciphertext | |
var ctrTxt = ''; | |
for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]); | |
// generate key schedule - an expansion of the key into distinct Key Rounds for each round | |
var keySchedule = KeyExpansion(key); | |
var blockCount = Math.ceil(plaintext.length/blockSize); | |
var ciphertxt = new Array(blockCount); // ciphertext as array of strings | |
for (var b=0; b<blockCount; b++) { | |
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) | |
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) | |
for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff; | |
for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) | |
var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- | |
// block size is reduced on final block | |
var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1; | |
var cipherChar = new Array(blockLength); | |
for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char -- | |
cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i); | |
cipherChar[i] = String.fromCharCode(cipherChar[i]); | |
} | |
ciphertxt[b] = cipherChar.join(''); | |
} | |
// Array.join is more efficient than repeated string concatenation | |
var ciphertext = ctrTxt + ciphertxt.join(''); | |
ciphertext = ciphertext.encodeBase64(); // encode in base64 | |
//alert((new Date()) - t); | |
return ciphertext; | |
} | |
/** | |
* Decrypt a text encrypted by AES in counter mode of operation | |
* | |
* @param ciphertext source text to be encrypted | |
* @param password the password to use to generate a key | |
* @param nBits number of bits to be used in the key (128, 192, or 256) | |
* @return decrypted text | |
*/ | |
function AESDecryptCtr(ciphertext, password, nBits) { | |
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES | |
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys | |
ciphertext = ciphertext.decodeBase64(); | |
password = password.encodeUTF8(); | |
//var t = new Date(); // timer | |
// use AES to encrypt password (mirroring encrypt routine) | |
var nBytes = nBits/8; // no bytes in key | |
var pwBytes = new Array(nBytes); | |
for (var i=0; i<nBytes; i++) { | |
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); | |
} | |
var key = Cipher(pwBytes, KeyExpansion(pwBytes)); | |
key = key.concat(key.slice(0, nBytes-32)); // expand key to 16/24/32 bytes long | |
// recover nonce from 1st 8 bytes of ciphertext | |
var counterBlock = new Array(8); | |
ctrTxt = ciphertext.slice(0, 8); | |
for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i); | |
// generate key schedule | |
var keySchedule = KeyExpansion(key); | |
// separate ciphertext into blocks (skipping past initial 8 bytes) | |
var nBlocks = Math.ceil((ciphertext.length-8) / blockSize); | |
var ct = new Array(nBlocks); | |
for (var b=0; b<nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize); | |
ciphertext = ct; // ciphertext is now array of block-length strings | |
// plaintext will get generated block-by-block into array of block-length strings | |
var plaintxt = new Array(ciphertext.length); | |
for (var b=0; b<nBlocks; b++) { | |
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) | |
for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff; | |
for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff; | |
var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block | |
var plaintxtByte = new Array(ciphertext[b].length); | |
for (var i=0; i<ciphertext[b].length; i++) { | |
// -- xor plaintxt with ciphered counter byte-by-byte -- | |
plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i); | |
plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]); | |
} | |
plaintxt[b] = plaintxtByte.join(''); | |
} | |
// join array of blocks into single plaintext string | |
var plaintext = plaintxt.join(''); | |
plaintext = plaintext.decodeUTF8(); // decode from UTF8 back to Unicode multi-byte chars | |
//alert((new Date()) - t); | |
return plaintext; | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
/** | |
* Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] | |
* (instance method extending String object). As per RFC 4648, no newlines are added. | |
* | |
* @param utf8encode optional parameter, if set to true Unicode string is encoded to UTF8 before | |
* conversion to base64; otherwise string is assumed to be 8-bit characters | |
* @return base64-encoded string | |
*/ | |
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |
String.prototype.encodeBase64 = function(utf8encode) { // http://tools.ietf.org/html/rfc4648 | |
utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode; | |
var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded; | |
plain = utf8encode ? this.encodeUTF8() : this; | |
c = plain.length % 3; // pad string to length of multiple of 3 | |
if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } } | |
// note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars | |
for (c=0; c<plain.length; c+=3) { // pack three octets into four hexets | |
o1 = plain.charCodeAt(c); | |
o2 = plain.charCodeAt(c+1); | |
o3 = plain.charCodeAt(c+2); | |
bits = o1<<16 | o2<<8 | o3; | |
h1 = bits>>18 & 0x3f; | |
h2 = bits>>12 & 0x3f; | |
h3 = bits>>6 & 0x3f; | |
h4 = bits & 0x3f; | |
// use hextets to index into b64 string | |
e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); | |
} | |
coded = e.join(''); // join() is far faster than repeated string concatenation | |
// replace 'A's from padded nulls with '='s | |
coded = coded.slice(0, coded.length-pad.length) + pad; | |
return coded; | |
} | |
/** | |
* Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] | |
* (instance method extending String object). As per RFC 4648, newlines are not catered for. | |
* | |
* @param utf8decode optional parameter, if set to true UTF8 string is decoded back to Unicode | |
* after conversion from base64 | |
* @return decoded string | |
*/ | |
String.prototype.decodeBase64 = function(utf8decode) { | |
utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode; | |
var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded; | |
coded = utf8decode ? this.decodeUTF8() : this; | |
for (var c=0; c<coded.length; c+=4) { // unpack four hexets into three octets | |
h1 = b64.indexOf(coded.charAt(c)); | |
h2 = b64.indexOf(coded.charAt(c+1)); | |
h3 = b64.indexOf(coded.charAt(c+2)); | |
h4 = b64.indexOf(coded.charAt(c+3)); | |
bits = h1<<18 | h2<<12 | h3<<6 | h4; | |
o1 = bits>>>16 & 0xff; | |
o2 = bits>>>8 & 0xff; | |
o3 = bits & 0xff; | |
d[c/4] = String.fromCharCode(o1, o2, o3); | |
// check for padding | |
if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2); | |
if (h3 == 0x40) d[c/4] = String.fromCharCode(o1); | |
} | |
plain = d.join(''); // join() is far faster than repeated string concatenation | |
return utf8decode ? plain.decodeUTF8() : plain; | |
} | |
/** | |
* Encode multi-byte Unicode string into utf-8 multiple single-byte characters | |
* (BMP / basic multilingual plane only) (instance method extending String object). | |
* | |
* Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars | |
* | |
* @return encoded string | |
*/ | |
String.prototype.encodeUTF8 = function() { | |
// use regular expressions & String.replace callback function for better efficiency | |
// than procedural approaches | |
var str = this.replace( | |
/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz | |
function(c) { | |
var cc = c.charCodeAt(0); | |
return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } | |
); | |
str = str.replace( | |
/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz | |
function(c) { | |
var cc = c.charCodeAt(0); | |
return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } | |
); | |
return str; | |
} | |
/** | |
* Decode utf-8 encoded string back into multi-byte Unicode characters | |
* (instance method extending String object). | |
* | |
* @return decoded string | |
*/ | |
String.prototype.decodeUTF8 = function() { | |
var str = this.replace( | |
/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars | |
function(c) { // (note parentheses for precence) | |
var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; | |
return String.fromCharCode(cc); } | |
); | |
str = str.replace( | |
/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars | |
function(c) { // (note parentheses for precence) | |
var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | ( c.charCodeAt(2)&0x3f); | |
return String.fromCharCode(cc); } | |
); | |
return str; | |
} | |
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
/* | |
CREDIT THE ENCRYPTION CODE TO: © 2005–2008 Chris Veness | |
http://www.movable-type.co.uk/scripts/aes.html | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment