[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Fwd: end-to-end encryption? SSL? GnuPG?
There was a greasemonkey script --- maybe the one that showcased the
vulnerability that allowed greasemonkey to execute local control over
user files (?.3) - that did basic encrypt/decrypt of pgp on web pages;
an example of a greasemonkey encryption scheme for gmail,
"using a system similar to AES" - Mark Langenhoven ---
http://mark.langenhoven.com
---see attached greasemonkey script.
Regards,
H
// ==UserScript==
// @name Gmail Encrypt Text
// @author Mark Langenhoven
// @namespace http://mark.langenhoven.com/
// @description Allows the user to encrypt their email conversations
// @include http*://*mail.google.com/*mail/*
// @date 2006/06/04
// @version 1.03
// ==/UserScript==
// ------------------------------------------------------------------------
// Copyright (c) 2006, Mark Langenhoven
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
// 1.03 2006/06/01 When using formatted text, the first line is discarded
// 1.02 2006/06/01 Inserted equalsInt from the BigInt scripts.
// Corrected for some gmail website changes which prevented some
// people from decrypting their texts.
// 1.01 2006/04/03 Minor bugfixes in RSA decryption form
// 1.00 2006/03/19 Original version
//
// This script encrypts your email conversation using a system similar to AES
// and the key to this is encrypted using a public key system similar to RSA.
//
// For more details see http://www.langenhoven.com/code/emailencrypt
// I don't claim to be a security guru. I am simply trying to implement
// what looked to me to be a relatively secure scheme.
//
// Some "BigInt" functionality came from the bigint.js library created
// by Leemon Baird. http://www.leemon.com/crypto/BigInt.html
//
// ------------------------------------------------------------------------
//We have to grab the text in this event listener because it does not exist
//at the time the script is normally called
document.addEventListener('click',function(event) {
var msg;
//Grab the text
if (event.target.id=='encrypt' || event.target.id=='decrypt') {
if (document.getElementsByTagName("iframe").length>0) {
var grabContent=document.getElementsByTagName("iframe")[0].contentDocument.getElementsByTagName("body")[0].cloneNode(true);
msg=grabContent.innerHTML;
} else {
msg=document.getElementsByName('msgbody')[0].value;
}
//Encrypt or decrypt the text
if (event.target.id=='encrypt') {
msg = RSAencryptText(msg);
}
if (event.target.id=='decrypt') {
msg = RSAdecryptText(msg);
}
//update the text
if (document.getElementsByTagName("iframe").length>0) {
document.getElementsByTagName("iframe")[0].contentDocument.getElementsByTagName("body")[0].innerHTML=msg;
} else {
document.getElementsByName('msgbody')[0].value=msg;
}
}
}, true);
// --- bigint initialisations -------------------
//globals
bpe=0; //bits stored per array element
mask=0; //AND this with an array element to chop it down to bpe bits
radix=mask+1; //equals 2^bpe. A single 1 bit to the left of the last bit of mask.
//the digits for converting to different bases
digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\'\"+-';
//initialize the global variables
for (bpe=0; (1<<(bpe+1)) > (1<<bpe); bpe++); //bpe=number of bits in the mantissa on this platform
bpe>>=1; //bpe=number of bits in one element of the array representing the bigInt
mask=(1<<bpe)-1; //AND the mask with an integer to get its bpe least-significant bits
radix=mask+1; //2^bpe. a single 1 bit to the left of the first bit of mask
one=int2bigInt(1,1,1); //constant used in powMod_()
//the following global variables are scratchpad memory to
//reduce dynamic memory allocation in the inner loop
t=new Array(0);
ss=t; //used in mult_()
s0=t; //used in multMod_(), squareMod_()
s1=t; //used in powMod_(), multMod_(), squareMod_()
s2=t; //used in powMod_(), multMod_()
s3=t; //used in powMod_()
s4=t; s5=t; //used in mod_()
s6=t; //used in bigInt2str()
s7=t; //used in powMod_()
T=t; //used in GCD_()
sa=t; //used in mont_()
mr_x1=t; mr_r=t; mr_a=t; //used in millerRabin()
eg_v=t; eg_u=t; eg_A=t; eg_B=t; eg_C=t; eg_D=t; //used in eGCD_(), inverseMod_()
md_q1=t; md_q2=t; md_q3=t; md_r=t; md_r1=t; md_r2=t; md_tt=t; //used in mod_()
primes=t; pows=t; s_i=t; s_i2=t; s_R=t; s_rm=t; s_q=t; s_n1=t;
s_a=t; s_r2=t; s_n=t; s_b=t; s_d=t; s_x1=t; s_x2=t, s_aa=t; //used in randTruePrime_()
//end of bigint initialisations
//--- aes initialisations -----------------------
// S-Box substitution table
var S_enc = new Array(
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);
// inverse S-Box for decryptions
var S_dec = new Array(
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);
// convert two-dimensional indicies to one-dim array indices
var I00 = 0;
var I01 = 1;
var I02 = 2;
var I03 = 3;
var I10 = 4;
var I11 = 5;
var I12 = 6;
var I13 = 7;
var I20 = 8;
var I21 = 9;
var I22 = 10;
var I23 = 11;
var I30 = 12;
var I31 = 13;
var I32 = 14;
var I33 = 15;
//--- end of AES inits --------------------------
init();
function mypow(base, exp)
{
//I didn't find a regular exponentiation function in bigInt
//so I had to put together this homegrown version
//This function is missing some error checks, etc, but
//it works for this purpose
//Special checks
if (exp < 1) {
return str2bigInt('1','10',0);
}
if (exp == 1){
return base;
}
var orig = base;
var origexp = exp;
//Precalculate some values to cut down on the number of loops
var val2 = mult(base, base);
var val4 = mult(val2, val2);
var val16 = mult(val4, val4);
var myval = val2;
var expcount = 2;
while (1>0)
{
//prevent long processes from killing the system
if (exp > 512) {
alert("Exponent count " + expcount + " -> original " + origexp);
}
if (expcount * 2 <= origexp)
{
myval = mult(myval, myval);
expcount = expcount * 2;
} else {
var diff = origexp - expcount;
if (diff>=16) {
myval = mult(myval,val16);
expcount = expcount + 16;
continue;
}
if (diff>=4) {
myval = mult(myval,val4);
expcount = expcount + 4;
continue;
}
if (diff>=2) {
myval = mult(myval,val2);
expcount = expcount + 2;
continue;
}
if (diff==1) {
myval = mult(myval,orig);
expcount = expcount + 1;
continue;
}
if (diff<1) {
return myval;
}
}
}
}
function getpnum(ptext,n)
{
//convert the plain text into a number
var bb = '10'; //bigInt base
var thenum = str2bigInt('0',bb,0);
var hexbase = str2bigInt('16',bb,0);
var cval, expval;
//Grab "n" number of chars from the string to encrypt
while (ptext.length < n) {
ptext = ptext.concat('0');
}
for (i=0,n;i<n;i++)
{
expval = n - (i + 1);
//Adjust the ASCII codes down so that 0 has
//a value of 2
var ccode = ptext.charCodeAt(i);
//0 has an ASCII value of 48
//9 = 57, A = 65, F = 70
if (ccode >=48 && ccode <= 57) {
ccode = ccode - 48;
} else {
ccode = ccode - 55;
}
cval = int2bigInt(ccode,0);
var power = mypow(hexbase, expval );
cval = mult(cval, power);
thenum = add(thenum, cval);
}
return thenum;
} //getpnum
function pad(ustr, n) {
//Pad the ustr to make sure it is "n" chars long
//this will catch numbers with leading zeroes
while (ustr.length < n) {
ustr = '0' + ustr;
}
return ustr;
} //pad
function makeKey()
{
//Create a random AES key. It must be 32 Hex chars long
var key = '';
for (i=0,33;i<33;i++)
{
var randomnumber=Math.floor(Math.random()*16);
if (randomnumber <= 9 ) {
key = key + randomnumber;
} else if (randomnumber == 10) {
key = key + 'A';
} else if (randomnumber == 11) {
key = key + 'B';
} else if (randomnumber == 12) {
key = key + 'C';
} else if (randomnumber == 13) {
key = key + 'D';
} else if (randomnumber == 14) {
key = key + 'E';
} else {
key = key + 'F';
}
}
return key;
}
function RSAencryptText(plainmsg) {
//This function is called by the button click and
//prepares all the pieces of text for the RSA encryption process
var bb = '10'; //bigint base;
//Generate a key for the AES encryption process
var AESkey = makeKey();
//GM_log("Plain AES key " + AESkey);
//Encrypt the email
var aes_text = aes_encrypt(AESkey, plainmsg);
//Encrypt the key using RSA
var plaintext = AESkey;
//Grab the public key
var keyspot = document.getElementById("pubkeytext");
var key = keyspot.value;
var keys = key.split(":");
var keymax = keys.length;
keymax = keymax - 1;
var keyE = keys[keymax];
keymax = keymax - 1;
var keyN = keys[keymax];
//make the keys "big"
var bigkeyN = int2bigInt(keyN,20);
var bigkeyE = int2bigInt(keyE,20);
//Encrypt the text
var etext = '';
while (plaintext.length > 0)
{
var plainnum = getpnum(plaintext,5);
//Check to see if this number is too large to encrypt
if (greater(plainnum, bigkeyN)) {
alert("This text cannot be encrypted using keys this small");
return;
}
var ciphernum = str2bigInt('0',bb,0);
ciphernum = powMod(plainnum, bigkeyE, bigkeyN);
if (etext == '') {
etext = bigInt2str(ciphernum,bb);
} else {
etext = etext.concat(';', bigInt2str(ciphernum,bb));
}
//Cut off the first 5 chars of the string
plaintext = plaintext.slice(5);
}
//GM_log("RSA key " + etext);
//Combine the RSA encrypted key with the AES encryption
etext = etext + ':' + aes_text;
//Mark the text as encrypted so that we know were to start and end with the
//decryption process
etext = "--- Start of mailencrypt --- " + etext + " --- End of mailencrypt ---";
//Return the encrypted text
//GM_log("Fully encrypted email " + etext);
return etext;
} //RSAencryptext
function RSAdecryptText(encryptmsg) {
//This is called by the decrypt button and gets all the pieces
//of the process together to call the RSA decryption process
var bb = '10'; //bigint base;
//Find the place where the text is stored
var plaintext = encryptmsg;
//GM_log("Decrypting " + plaintext);
//Grab the private key
var keyspot = document.getElementById("privkeytext");
var key = keyspot.value;
//Split this key into the various components
var keys = key.split(":");
var keymax = keys.length;
//The last value is the D component
keymax = keymax - 1;
var keyD = keys[keymax];
//Second last part is the N component
keymax = keymax - 1;
var keyN = keys[keymax];
//Make the keys big
var bigkeyN = str2bigInt(keyN,bb,0);
var bigkeyD = str2bigInt(keyD,bb,0);
// Strip out all the "> " indendation characters from the message
// Only if we are dealing with a plaintext message
var beforepos = 1;
beforepos = plaintext.indexOf("<div>"); //check for html
if (beforepos <= 0 ) {
beforepos = 1;
while ( beforepos > 0 ) {
beforepos = plaintext.indexOf(">");
if ( beforepos > 0 ) {
var afterpos = beforepos + 1;
plaintext = plaintext.slice(0,beforepos) + plaintext.slice(afterpos);
}
}
}
//find the encrypted part of the email
var beforepos = plaintext.indexOf("--- Start of mailencrypt ---");
var afterpos = plaintext.indexOf(" --- End of mailencrypt ---");
if ( beforepos > 0 ) {
var beforetext = plaintext.slice(0,beforepos);
} else {
var beforetext = "";
}
if ( afterpos >0 ) {
var aftertext = plaintext.slice(afterpos + 27);
} else {
var aftertext = "";
}
plaintext = plaintext.slice(beforepos + 29, afterpos);
//GM_log("plaintext " + plaintext);
//split the encrypted email into the key and the AES encrypted portion
var etexts = plaintext.split(":");
//the first text is the key which we need to decrypt using RSA
var ptexts = etexts[0].split(";");
var i = 0;
var dtext = '';
var tmptext = '';
while ( i < ptexts.length )
{
//Turn the ciphertext number into a plaintext number
var ciphernum = str2bigInt(ptexts[i],bb,0);
if (greater(ciphernum, bigkeyN)) {
alert("This text cannot be decrypted using keys this small");
return;
}
var plainnum = powMod(ciphernum, bigkeyD, bigkeyN);
tmptext = bigInt2str(plainnum,'16'); //use base 16 to convert to hex
tmptext = pad(tmptext,5);
if (dtext == '') {
dtext = tmptext;
} else {
dtext = dtext.concat(tmptext);
}
i = i + 1;
}
//chop off the padding at the end of the string
dtext = dtext.slice(0,dtext.length-3);
//Now decrypt the email body using AES
//GM_log("text to decrypt " + etexts[1]);
var finaltxt = aes_decrypt(dtext, etexts[1]);
//Place the other parts of the text back around it again
finaltxt = beforetext + finaltxt + aftertext;
return finaltxt;
//GM_log("Decrypted email " + finaltxt);
} //RSAdecrypttext
function putButtons() {
//Place the buttons on the screen
//Find the Send button
var sendrow;
if (document.getElementsByTagName("frame").length>0) {
var framea = document.getElementsByTagName("frame")[0];
if (framea.contentDocument.getElementsByTagName("iframe").length>0) {
var iframea = framea.contentDocument.getElementsByTagName("iframe")[0];
sendrow =iframea.contentDocument.getElementById("st_compose");
if (!sendrow) {
//for replies + forwards the send button is placed differently
var elemname;
for (var k=0; k<=9; k++) {
elemname = "st_" + k;
if (iframea.contentDocument.getElementById(elemname)) {
sendrow = iframea.contentDocument.getElementById(elemname);
k = 999;
}
}
}
}
} else {
sendrow = document.getElementById("st_compose");
if (!sendrow) {
var elemname;
for (var k=0; k<=9; k++) {
elemname = "st_" + k;
if (document.getElementById(elemname)) {
sendrow = document.getElementById(elemname);
k = 999;
}
}
}
}
if (!sendrow) {
window.setTimeout(putButtons, 1000);
return;
}
//Make sure we have not already added the buttons to this tag
for (var k=0;k<sendrow.childNodes.length; k++) {
knode = sendrow.childNodes[k];
if (knode.id == "encryptdiv") {
window.setTimeout(putButtons, 1000);
return;
}
}
//Create a DIV element to visually separate our buttons
//from the rest of the screen
var divTag = document.createElement("div");
divTag.setAttribute('id', 'encryptdiv');
// divTag.setAttribute('style', 'background-color:wheat');
divTag.setAttribute('style', '-moz-border-radius: 10px; background: wheat; margin: 10px 7px 0 0; padding: 3px;');
divTag.setAttribute('align', 'left');
sendrow.appendChild(divTag);
var spacetxt = document.createTextNode(" ");
divTag.appendChild(spacetxt);
//Add the encryption button
var EncryptButton = document.createElement("button");
EncryptButton.setAttribute('id', 'encrypt');
//Click events are handled by the event listener created earlier
// EncryptButton.addEventListener("click", RSAencryptText, true);
EncryptButton.innerHTML='<font color="blue">Encrypt</font>';
divTag.appendChild(EncryptButton);
//Add the public key
var pubkeylabel = document.createTextNode(" Public Key: ");
divTag.appendChild(pubkeylabel);
var pubkeytext = document.createElement("input");
pubkeytext.setAttribute('id', 'pubkeytext');
divTag.appendChild(pubkeytext);
divTag.appendChild(spacetxt);
//Add the decryption button
var DecryptButton = document.createElement("button");
DecryptButton.setAttribute('id', 'decrypt');
// DecryptButton.addEventListener("click", RSAdecryptText, true);
DecryptButton.innerHTML='<font color="blue">Decrypt</font>';
divTag.appendChild(DecryptButton);
//Add the private key
var privkeylabel = document.createTextNode(" Private Key: ");
divTag.appendChild(privkeylabel);
var privkeytext = document.createElement("input");
privkeytext.setAttribute('id', 'privkeytext');
privkeytext.setAttribute('type', 'password');
divTag.appendChild(privkeytext);
//go back and recreate the buttons after any form inputs
window.setTimeout(putButtons, 5000);
} //putButtons
function init() {
//GM_log("running the encryption script");
putButtons();
}//init
//Exit before you execute the other routines
return;
//--- BigInt routines culled from bigint.js -------------------------
//Many functions and comments were stripped from the original file to cut down
//on load size. Please see the file bigint.js in the same location where you
//originally found this script for full attribution
//return a copy of x with at least n elements, adding leading zeros if needed
function expand(x,n) {
var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
copy_(ans,x);
return ans;
}
//return x*y for bigInts x and y. This is faster when y<x.
function mult(x,y) {
var ans=expand(x,x.length+y.length);
mult_(ans,y);
return trim(ans,1);
}
//return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.
function powMod(x,y,n) {
var ans=expand(x,n.length);
powMod_(ans,trim(y,2),trim(n,2),0); //this should work without the trim, but doesn't
return trim(ans,1);
}
//return (x+y) for bigInts x and y.
function add(x,y) {
var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1));
add_(ans,y);
return trim(ans,1);
}
//return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse
function inverseModInt_(x,n) {
var a=1,b=0,t;
for (;;) {
if (x==1) return a;
if (x==0) return 0;
b-=a*Math.floor(n/x);
n%=x;
if (n==1) return b; //to avoid negatives, change this b to n-b, and each -= to +=
if (n==0) return 0;
a-=b*Math.floor(x/n);
x%=n;
}
}
//is bigInt x negative?
function negative(x) {
return ((x[x.length-1]>>(bpe-1))&1);
}
//is (x << (shift*bpe)) > y?
function greaterShift(x,y,shift) {
var kx=x.length, ky=y.length;
k=((kx+shift)<ky) ? (kx+shift) : ky;
for (i=ky-1-shift; i<kx && i>=0; i++)
if (x[i]>0)
return 1; //if there are nonzeros in x to the left of the first column of y, then x is bigger
for (i=kx-1+shift; i<ky; i++)
if (y[i]>0)
return 0; //if there are nonzeros in y to the left of the first column of x, then x is not bigger
for (i=k-1; i>=shift; i--)
if (x[i-shift]>y[i]) return 1;
else if (x[i-shift]<y[i]) return 0;
return 0;
}
//is x > y? (x and y both nonnegative)
function greater(x,y) {
var i;
var k=(x.length<y.length) ? x.length : y.length;
for (i=x.length;i<y.length;i++)
if (y[i])
return 0; //y has more digits
for (i=y.length;i<x.length;i++)
if (x[i])
return 1; //x has more digits
for (i=k-1;i>=0;i--)
if (x[i]>y[i])
return 1;
else if (x[i]<y[i])
return 0;
return 0;
}
function divide_(x,y,q,r) {
var kx, ky;
var i,j,y1,y2,c,a,b;
copy_(r,x);
for (ky=y.length;y[ky-1]==0;ky--); //kx,ky is number of elements in x,y, not including leading zeros
for (kx=r.length;r[kx-1]==0 && kx>ky;kx--);
//normalize: ensure the most significant element of y has its highest bit set
b=y[ky-1];
for (a=0; b; a++)
b>>=1;
a=bpe-a; //a is how many bits to shift so that the high order bit of y is leftmost in its array element
leftShift_(y,a); //multiply both by 1<<a now, then divide_ both by that at the end
leftShift_(r,a);
copyInt_(q,0); // q=0
while (!greaterShift(y,r,kx-ky)) { // while (leftShift_(y,kx-ky) <= r) {
subShift_(r,y,kx-ky); // r=r-leftShift_(y,kx-ky)
q[kx-ky]++; // q[kx-ky]++;
} // }
for (i=kx-1; i>=ky; i--) {
if (r[i]==y[ky-1])
q[i-ky]=mask;
else
q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]);
for (;;) {
y2=(ky>1 ? y[ky-2] : 0)*q[i-ky];
c=y2>>bpe;
y2=y2 & mask;
y1=c+q[i-ky]*y[ky-1];
c=y1>>bpe;
y1=y1 & mask;
if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i])
q[i-ky]--;
else
break;
}
linCombShift_(r,y,-q[i-ky],i-ky); //r=r-q[i-ky]*leftShift_(y,i-ky)
if (negative(r)) {
addShift_(r,y,i-ky); //r=r+leftShift_(y,i-ky)
q[i-ky]--;
}
}
rightShift_(y,a); //undo the normalization step
rightShift_(r,a); //undo the normalization step
}
//return x mod n for bigInt x and integer n.
function modInt(x,n) {
var i,c=0;
for (i=x.length-1; i>=0; i--)
c=(c*radix+x[i])%n;
return c;
}
function int2bigInt(t,bits,minSize) {
var i,k;
k=Math.ceil(bits/bpe)+1;
k=minSize>k ? minSize : k;
buff=new Array(k);
copyInt_(buff,t);
return buff;
}
function str2bigInt(s,base,minSize) {
var d, i, j, x, y, kk;
var k=s.length;
if (base==-1) { //comma-separated list of array elements in decimal
x=new Array(0);
for (;;) {
y=new Array(x.length+1);
for (i=0;i<x.length;i++)
y[i+1]=x[i];
y[0]=parseInt(s,10);
x=y;
d=s.indexOf(',',0);
if (d<1)
break;
s=s.substring(d+1);
if (s.length==0)
break;
}
if (x.length<minSize) {
y=new Array(minSize);
copy_(y,x);
return y;
}
return x;
}
x=int2bigInt(0,base*k,0);
for (i=0;i<k;i++) {
d=digitsStr.indexOf(s.substring(i,i+1),0);
if (base<=36 && d>=36) //convert lowercase to uppercase if base<=36
d-=26;
if (d<base && d>=0) { //ignore illegal characters
multInt_(x,base);
addInt_(x,d);
}
}
for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros
k=minSize>k+1 ? minSize : k+1;
y=new Array(k);
kk=k<x.length ? k : x.length;
for (i=0;i<kk;i++)
y[i]=x[i];
for (;i<k;i++)
y[i]=0;
return y;
}
//is the bigInt x equal to zero?
function isZero(x) {
var i;
for (i=0;i<x.length;i++)
if (x[i])
return 0;
return 1;
}
//convert a bigInt into a string in a given base, from base 2 up to base 95.
//Base -1 prints the contents of the array representing the number.
function bigInt2str(x,base) {
var i,t,s="";
if (s6.length!=x.length)
s6=dup(x);
else
copy_(s6,x);
if (base==-1) { //return the list of array contents
for (i=x.length-1;i>0;i--)
s+=x[i]+',';
s+=x[0];
}
else { //return it in the given base
while (!isZero(s6)) {
t=divInt_(s6,base); //t=s6 % base; s6=floor(s6/base);
s=digitsStr.substring(t,t+1)+s;
}
}
if (s.length==0)
s="0";
return s;
}
//returns a duplicate of bigInt x
function dup(x) {
var i;
buff=new Array(x.length);
copy_(buff,x);
return buff;
}
//do x=y on bigInts x and y.
function copy_(x,y) {
var i;
var k=x.length<y.length ? x.length : y.length;
for (i=0;i<k;i++)
x[i]=y[i];
for (i=k;i<x.length;i++)
x[i]=0;
}
//do x=y on bigInt x and integer y.
function copyInt_(x,n) {
var i,c;
for (c=n,i=0;i<x.length;i++) {
x[i]=c & mask;
c>>=bpe;
}
}
//do x=x+n where x is a bigInt and n is an integer.
//x must be large enough to hold the result.
function addInt_(x,n) {
var i,k,c,b;
x[0]+=n;
k=x.length;
c=0;
for (i=0;i<k;i++) {
c+=x[i];
b=0;
if (c<0) {
b=-(c>>bpe);
c+=b*radix;
}
x[i]=c & mask;
c=(c>>bpe)-b;
if (!c) return; //stop carrying as soon as the carry_ is zero
}
}
//right shift bigInt x by n bits. 0 <= n < bpe.
function rightShift_(x,n) {
var i;
var k=Math.floor(n/bpe);
if (k) {
for (i=0;i<x.length-k;i++) //right shift x by k elements
x[i]=x[i+k];
for (;i<x.length;i++)
x[i]=0;
n%=bpe;
}
for (i=0;i<x.length-1;i++) {
x[i]=mask & ((x[i+1]<<(bpe-n)) | (x[i]>>n));
}
x[i]>>=n;
}
//left shift bigInt x by n bits.
function leftShift_(x,n) {
var i;
var k=Math.floor(n/bpe);
if (k) {
for (i=x.length; i>=k; i--) //left shift x by k elements
x[i]=x[i-k];
for (;i>=0;i--)
x[i]=0;
n%=bpe;
}
if (!n)
return;
for (i=x.length-1;i>0;i--) {
x[i]=mask & ((x[i]<<n) | (x[i-1]>>(bpe-n)));
}
x[i]=mask & (x[i]<<n);
}
//do x=x*n where x is a bigInt and n is an integer.
//x must be large enough to hold the result.
function multInt_(x,n) {
var i,k,c,b;
if (!n)
return;
k=x.length;
c=0;
for (i=0;i<k;i++) {
c+=x[i]*n;
b=0;
if (c<0) {
b=-(c>>bpe);
c+=b*radix;
}
x[i]=c & mask;
c=(c>>bpe)-b;
}
}
//do x=floor(x/n) for bigInt x and integer n, and return the remainder
function divInt_(x,n) {
var i,r=0,s;
for (i=x.length-1;i>=0;i--) {
s=r*radix+x[i];
x[i]=Math.floor(s/n);
r=s%n;
}
return r;
}
//do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys.
//x must be large enough to hold the answer.
function linCombShift_(x,y,b,ys) {
var i,c,k,kk;
k=x.length<ys+y.length ? x.length : ys+y.length;
kk=x.length;
for (c=0,i=ys;i<k;i++) {
c+=x[i]+b*y[i-ys];
x[i]=c & mask;
c>>=bpe;
}
for (i=k;c && i<kk;i++) {
c+=x[i];
x[i]=c & mask;
c>>=bpe;
}
}
//do x=x-(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.
//x must be large enough to hold the answer.
function subShift_(x,y,ys) {
var i,c,k,kk;
k=x.length<ys+y.length ? x.length : ys+y.length;
kk=x.length;
for (c=0,i=ys;i<k;i++) {
c+=x[i]-y[i-ys];
x[i]=c & mask;
c>>=bpe;
}
for (i=k;c && i<kk;i++) {
c+=x[i];
x[i]=c & mask;
c>>=bpe;
}
}
//do x=x+y for bigInts x and y.
//x must be large enough to hold the answer.
function add_(x,y) {
var i,c,k,kk;
k=x.length<y.length ? x.length : y.length;
for (c=0,i=0;i<k;i++) {
c+=x[i]+y[i];
x[i]=c & mask;
c>>=bpe;
}
for (i=k;c && i<x.length;i++) {
c+=x[i];
x[i]=c & mask;
c>>=bpe;
}
}
//do x=x*y for bigInts x and y. This is faster when y<x.
function mult_(x,y) {
var i;
if (ss.length!=2*x.length)
ss=new Array(2*x.length);
copyInt_(ss,0);
for (i=0;i<y.length;i++)
if (y[i])
linCombShift_(ss,x,y[i],i); //ss=1*ss+y[i]*(x<<(i*bpe))
copy_(x,ss);
}
//do x=x mod n for bigInts x and n.
function mod_(x,n) {
if (s4.length!=x.length)
s4=dup(x);
else
copy_(s4,x);
if (s5.length!=x.length)
s5=dup(x);
divide_(s4,n,s5,x); //x = remainder of s4 / n
}
//do x=x*y mod n for bigInts x,y,n.
//for greater speed, let y<x.
function multMod_(x,y,n) {
var i;
if (s0.length!=2*x.length)
s0=new Array(2*x.length);
copyInt_(s0,0);
for (i=0;i<y.length;i++)
if (y[i])
linCombShift_(s0,x,y[i],i); //s0=1*s0+y[i]*(x<<(i*bpe))
mod_(s0,n);
copy_(x,s0);
}
//return x with exactly k leading zero elements
function trim(x,k) {
var i,y;
for (i=x.length; i>0 && !x[i-1]; i--);
y=new Array(i+k);
copy_(y,x);
return y;
}
//is bigint x equal to integer y?
//y must have less than bpe bits
function equalsInt(x,y) {
var i;
if (x[0]!=y)
return 0;
for (i=1;i<x.length;i++)
if (x[i])
return 0;
return 1;
}
//do x=x**y mod n, where x,y,n are bigInts and ** is exponentiation. 0**0=1.
//this is faster when n is odd. x usually needs to have as many elements as n.
function powMod_(x,y,n) {
var k1,k2,kn,np;
if(s7.length!=n.length)
s7=dup(n);
//for even modulus, use a simple square-and-multiply algorithm,
//rather than using the more complex Montgomery algorithm.
if ((n[0]&1)==0) {
copy_(s7,x);
copyInt_(x,1);
while(!equalsInt(y,0)) {
if (y[0]&1)
multMod_(x,s7,n);
divInt_(y,2);
squareMod_(s7,n);
}
return;
}
//calculate np from n for the Montgomery multiplications
copyInt_(s7,0);
for (kn=n.length;kn>0 && !n[kn-1];kn--);
np=radix-inverseModInt_(modInt(n,radix),radix);
s7[kn]=1;
multMod_(x ,s7,n); // x = x * 2**(kn*bp) mod n
if (s3.length!=x.length)
s3=dup(x);
else
copy_(s3,x);
for (k1=y.length-1;k1>0 & !y[k1]; k1--); //k1=first nonzero element of y
if (y[k1]==0) { //anything to the 0th power is 1
copyInt_(x,1);
return;
}
for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1); //k2=position of first 1 bit in y[k1]
for (;;) {
if (!(k2>>=1)) { //look at next bit of y
k1--;
if (k1<0) {
mont_(x,one,n,np);
return;
}
k2=1<<(bpe-1);
}
mont_(x,x,n,np);
if (k2 & y[k1]) //if next bit is a 1
mont_(x,s3,n,np);
}
}
function sub_(x,y) {
var i,c,k,kk;
k=x.length<y.length ? x.length : y.length;
for (c=0,i=0;i<k;i++) {
c+=x[i]-y[i];
x[i]=c & mask;
c>>=bpe;
}
for (i=k;c && i<x.length;i++) {
c+=x[i];
x[i]=c & mask;
c>>=bpe;
}
}
function mont_(x,y,n,np) {
var i,j,c,ui,t;
var kn=n.length;
var ky=y.length;
if (sa.length!=kn)
sa=new Array(kn);
for (;kn>0 && n[kn-1]==0;kn--); //ignore leading zeros of n
//this function sometimes gives wrong answers when the next line is uncommented
//for (;ky>0 && y[ky-1]==0;ky--); //ignore leading zeros of y
copyInt_(sa,0);
//the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large keys
for (i=0; i<kn; i++) {
t=sa[0]+x[i]*y[0];
ui=((t & mask) * np) & mask; //the inner "& mask" is needed on Macintosh MSIE, but not windows MSIE
c=(t+ui*n[0]) >> bpe;
t=x[i];
//do sa=(sa+x[i]*y+ui*n)/b where b=2**bpe
for (j=1;j<ky;j++) {
c+=sa[j]+t*y[j]+ui*n[j];
sa[j-1]=c & mask;
c>>=bpe;
}
for (;j<kn;j++) {
c+=sa[j]+ui*n[j];
sa[j-1]=c & mask;
c>>=bpe;
}
sa[j-1]=c & mask;
}
if (!greater(n,sa))
sub_(sa,n);
copy_(x,sa);
}
//--- end of BigInt -------------------------------------------------
//--- AES routines --------------------------------------------------
// convert a 8-bit value to a string
function cvt_hex8( val )
{
var vh = (val>>>4)&0x0f;
return vh.toString(16) + (val&0x0f).toString(16);
}
// convert a 32-bit value to a 8-char hex string
function cvt_hex32( val )
{
var str="";
var i;
var v;
for( i=7; i>=0; i-- )
{
v = (val>>>(i*4))&0x0f;
str += v.toString(16);
}
return str;
}
// convert a two-digit hex value to a number
function cvt_byte( str )
{
// get the first hex digit
var val1 = str.charCodeAt(0);
// do some error checking
if ( val1 >= 48 && val1 <= 57 )
// have a valid digit 0-9
val1 -= 48;
else if ( val1 >= 65 && val1 <= 70 )
// have a valid digit A-F
val1 -= 55;
else if ( val1 >= 97 && val1 <= 102 )
// have a valid digit A-F
val1 -= 87;
else
{
// not 0-9 or A-F, complain
window.alert( str.charAt(1)+" is not a valid hex digit" );
return -1;
}
// get the second hex digit
var val2 = str.charCodeAt(1);
// do some error checking
if ( val2 >= 48 && val2 <= 57 )
// have a valid digit 0-9
val2 -= 48;
else if ( val2 >= 65 && val2 <= 70 )
// have a valid digit A-F
val2 -= 55;
else if ( val2 >= 97 && val2 <= 102 )
// have a valid digit A-F
val2 -= 87;
else
{
// not 0-9 or A-F, complain
window.alert( str.charAt(2)+" is not a valid hex digit" );
return -1;
}
// all is ok, return the value
return val1*16 + val2;
}
// add a byte to the output
function accumulate_byte( label, val )
{
accumulated_output_info += label + cvt_hex8(val) + "\n";
}
// add a word to the output
function accumulate_wordarray( label, ary )
{
var i, j;
accumulated_output_info += label + " ";
// process the four elements in this word
for( j=0; j<4; j++ )
accumulated_output_info += " " + cvt_hex8( ary[j] );
// mark the end of the word
accumulated_output_info += "\n";
}
// add an array to the output
function accumulate_array( label, ary )
{
var i, j;
var spacer="";
// build a set of spaces of equal length to the label
while( spacer.length < label.length )
spacer += " ";
// build the table
for( i=0; i<16; i+= 4 )
{
// add label/spaces
if ( i== 0 )
accumulated_output_info += label + " ";
else
accumulated_output_info += spacer + " ";
// process the four elements in this "row"
for( j=0; j<4; j++ )
accumulated_output_info += " " + cvt_hex8( ary[i+j] );
// mark the end of this row
accumulated_output_info += "\n";
}
}
// conversion function for non-constant subscripts
// assume subscript range 0..3
function I(x,y)
{ return (x*4) + y; }
// remove spaces from input
function remove_spaces( instr )
{
var i;
var outstr="";
for( i=0; i<instr.length; i++ )
{
if ( instr.charAt(i) != " " )
{
if (instr.charCodeAt(i) < 9 || instr.charCodeAt(i) > 13 )
// not a space, include it
outstr += instr.charAt(i);
}
}
return outstr;
}
// get the message to encrypt/decrypt or the key
// return as a 16-byte array
function ascTo16(str)
{
var dbyte = new Array(16);
var i;
for( i=0; i<16; i++ )
{
dbyte[i] = str.charCodeAt(i);
}
return dbyte;
}
function hexTo16(str)
{
var dbyte = new Array(16);
var i;
str = remove_spaces(str);
for( i=0; i<16; i++ )
{
// isolate and convert this substring
dbyte[i] = cvt_byte( str.substr(i*2,2) );
if( dbyte[i] < 0 )
{
// have an error
dbyte[0] = -1;
return dbyte;
}
} // for i
return dbyte;
}
function get_value( lbl, str, isASCII )
{
var dbyte = new Array(16);
var i;
var val; // one hex digit
if ( isASCII )
{
// ??? pad with spaces/nulls if < 16 chars ???
// check length of data
if ( str.length != 16 )
{
window.alert( lbl + " length wrong: Is " + str.length +
"characters, but must be 128 bits (16 ASCII characters)");
dbyte[0] = -1;
return dbyte;
}
// have ASCII data
for( i=0; i<16; i++ )
{
dbyte[i] = str.charCodeAt(i);
}
}
else
{
// have hex data - remove any spaces they used, then convert
str = remove_spaces(str);
// check length of data
if ( str.length != 32 )
{
window.alert( lbl + " length wrong: Is " + str.length +
" hex digits, but must be 128 bits (32 hex digits)");
dbyte[0] = -1;
return dbyte;
}
for( i=0; i<16; i++ )
{
// isolate and convert this substring
dbyte[i] = cvt_byte( str.substr(i*2,2) );
if( dbyte[i] < 0 )
{
// have an error
dbyte[0] = -1;
return dbyte;
}
} // for i
} // if isASCII
// return successful conversion
return dbyte;
} // get_value
//do the AES GF(2**8) multiplication
// do this by the shift-and-"add" approach
function aes_mul( a, b )
{
var res = 0;
while( a > 0 )
{
if ( a&1 )
res = res ^ b; // "add" to the result
a >>>= 1; // shift a to get next higher-order bit
b <<= 1; // shift multiplier also
}
// now reduce it modulo x**8 + x**4 + x**3 + x + 1
var hbit = 0x10000; // bit to test if we need to take action
var modulus = 0x11b00; // modulus - XOR by this to change value
while( hbit >= 0x100 )
{
if ( res & hbit ) // if the high-order bit is set
res ^= modulus; // XOR with the modulus
// prepare for the next loop
hbit >>= 1;
modulus >>= 1;
}
return res;
}
// apply the S-box substitution to the key expansion
function SubWord( word_ary )
{
var i;
for( i=0; i<16; i++ )
word_ary[i] = S_enc[ word_ary[i] ];
return word_ary;
}
// rotate the bytes in a word
function RotWord( word_ary )
{
return new Array( word_ary[1], word_ary[2], word_ary[3], word_ary[0] );
}
// calculate the first item Rcon[i] = { x^(i-1), 0, 0, 0 }
// note we only return the first item
function Rcon( exp )
{
var val = 2;
var result = 1;
// remember to calculate x^(exp-1)
exp--;
// process the exponent using normal shift and multiply
while ( exp > 0 )
{
if ( exp & 1 )
result = aes_mul( result, val );
// square the value
val = aes_mul( val, val );
// move to the next bit
exp >>= 1;
}
return result;
}
// round key generation
// return a byte array with the expanded key information
function key_expand( key )
{
var temp = new Array(4);
var i, j;
var w = new Array( 4*11 );
// copy initial key stuff
for( i=0; i<16; i++ )
{
w[i] = key[i];
}
accumulate_wordarray( "w[0] = ", w.slice(0,4) );
accumulate_wordarray( "w[1] = ", w.slice(4,8) );
accumulate_wordarray( "w[2] = ", w.slice(8,12) );
accumulate_wordarray( "w[3] = ", w.slice(12,16) );
// generate rest of key schedule using 32-bit words
i = 4;
while ( i < 44 ) // blocksize * ( rounds + 1 )
{
// copy word W[i-1] to temp
for( j=0; j<4; j++ )
temp[j] = w[(i-1)*4+j];
if ( i % 4 == 0)
{
// temp = SubWord(RotWord(temp)) ^ Rcon[i/4];
temp = RotWord( temp );
accumulate_wordarray( "RotWord()=", temp );
temp = SubWord( temp );
accumulate_wordarray( "SubWord()=", temp );
temp[0] ^= Rcon( i>>>2 );
accumulate_wordarray( " ^ Rcon()=", temp );
}
// word = word ^ temp
for( j=0; j<4; j++ )
w[i*4+j] = w[(i-4)*4+j] ^ temp[j];
accumulate_wordarray( "w["+i+"] = ", w.slice( i*4, i*4+4 ) );
i++;
}
return w;
}
// do S-Box substitution
function SubBytes(state, Sbox)
{
var i;
for( i=0; i<16; i++ )
state[i] = Sbox[ state[i] ];
return state;
}
// shift each row as appropriate
function ShiftRows(state)
{
var t0, t1, t2, t3;
// top row (row 0) isn't shifted
// next row (row 1) rotated left 1 place
t0 = state[I10];
t1 = state[I11];
t2 = state[I12];
t3 = state[I13];
state[I10] = t1;
state[I11] = t2;
state[I12] = t3;
state[I13] = t0;
// next row (row 2) rotated left 2 places
t0 = state[I20];
t1 = state[I21];
t2 = state[I22];
t3 = state[I23];
state[I20] = t2;
state[I21] = t3;
state[I22] = t0;
state[I23] = t1;
// bottom row (row 3) rotated left 3 places
t0 = state[I30];
t1 = state[I31];
t2 = state[I32];
t3 = state[I33];
state[I30] = t3;
state[I31] = t0;
state[I32] = t1;
state[I33] = t2;
return state;
}
// inverset shift each row as appropriate
function InvShiftRows(state)
{
var t0, t1, t2, t3;
// top row (row 0) isn't shifted
// next row (row 1) rotated left 1 place
t0 = state[I10];
t1 = state[I11];
t2 = state[I12];
t3 = state[I13];
state[I10] = t3;
state[I11] = t0;
state[I12] = t1;
state[I13] = t2;
// next row (row 2) rotated left 2 places
t0 = state[I20];
t1 = state[I21];
t2 = state[I22];
t3 = state[I23];
state[I20] = t2;
state[I21] = t3;
state[I22] = t0;
state[I23] = t1;
// bottom row (row 3) rotated left 3 places
t0 = state[I30];
t1 = state[I31];
t2 = state[I32];
t3 = state[I33];
state[I30] = t1;
state[I31] = t2;
state[I32] = t3;
state[I33] = t0;
return state;
}
// process column info
function MixColumns(state)
{
var col;
var c0, c1, c2, c3;
for( col=0; col<4; col++ )
{
c0 = state[I(0,col)];
c1 = state[I(1,col)];
c2 = state[I(2,col)];
c3 = state[I(3,col)];
// do mixing, and put back into array
state[I(0,col)] = aes_mul(2,c0) ^ aes_mul(3,c1) ^ c2 ^ c3;
state[I(1,col)] = c0 ^ aes_mul(2,c1) ^ aes_mul(3,c2) ^ c3;
state[I(2,col)] = c0 ^ c1 ^ aes_mul(2,c2) ^ aes_mul(3,c3);
state[I(3,col)] = aes_mul(3,c0) ^ c1 ^ c2 ^ aes_mul(2,c3);
}
return state;
}
// inverse process column info
function InvMixColumns(state)
{
var col;
var c0, c1, c2, c3;
for( col=0; col<4; col++ )
{
c0 = state[I(0,col)];
c1 = state[I(1,col)];
c2 = state[I(2,col)];
c3 = state[I(3,col)];
// do inverse mixing, and put back into array
state[I(0,col)] = aes_mul(0x0e,c0) ^ aes_mul(0x0b,c1)
^ aes_mul(0x0d,c2) ^ aes_mul(0x09,c3);
state[I(1,col)] = aes_mul(0x09,c0) ^ aes_mul(0x0e,c1)
^ aes_mul(0x0b,c2) ^ aes_mul(0x0d,c3);
state[I(2,col)] = aes_mul(0x0d,c0) ^ aes_mul(0x09,c1)
^ aes_mul(0x0e,c2) ^ aes_mul(0x0b,c3);
state[I(3,col)] = aes_mul(0x0b,c0) ^ aes_mul(0x0d,c1)
^ aes_mul(0x09,c2) ^ aes_mul(0x0e,c3);
}
return state;
}
// insert subkey information
function AddRoundKey( state, w, base )
{
var col;
for( col=0; col<4; col++ )
{
state[I(0,col)] ^= w[base+col*4];
state[I(1,col)] ^= w[base+col*4+1];
state[I(2,col)] ^= w[base+col*4+2];
state[I(3,col)] ^= w[base+col*4+3];
}
return state;
}
// return a transposed array
function transpose( msg )
{
var row, col;
var state = new Array( 16 );
for( row=0; row<4; row++ )
for( col=0; col<4; col++ )
state[I(row,col)] = msg[I(col,row)];
return state;
}
// final AES state
var AES_output = new Array(16);
// format AES output
function format_AES_output(what, how)
{
var i;
var bits;
var str="";
// what type of data do we have to work with?
if ( how == "asc")
{
// convert each set of bits back to ASCII
for( i=0; i<16; i++ )
str += String.fromCharCode( what[i] );
}
else
{
// output hexdecimal data
str = cvt_hex8( AES_output[0] );
for( i=1; i<16; i++ )
{
str += " " + cvt_hex8( what[i] );
}
}
return str;
}
// do AES encrytion
function aes_encrypt(key, plainmsg)
{
var w = new Array( 44 ); // subkey information
var state = new Array( 16 ); // working state
var round;
var finalstr = "";
var msg = "";
//Grab the text to encrypt
var longmsg = plainmsg;
//GM_log("AES Unencrypted " + longmsg);
//check the key
if ( key[0] < 0 )
{
alert("There is a problem with the key");
return;
}
// expand the key
key = hexTo16(key);
w = key_expand( key );
while (longmsg.length > 0 )
{
msg = longmsg.slice(0,16);
longmsg = longmsg.slice(16);
while (msg.length < 16) {
msg += " ";
}
msg = ascTo16(msg);
// problems??
if ( msg[0] < 0 )
{
alert("There is a problem with the message");
return;
}
// initial state = message in columns (transposed from what we input)
state = transpose( msg );
state = AddRoundKey(state, w, 0);
for( round=1; round<10; round++ )
{
state = SubBytes(state, S_enc);
state = ShiftRows(state);
state = MixColumns(state);
// note here the spec uses 32-bit words, we are using bytes, so an extra *4
state = AddRoundKey(state, w, round*4*4);
}
SubBytes(state, S_enc);
ShiftRows(state);
AddRoundKey(state, w, 10*4*4);
// process output
AES_output = transpose( state );
var tmpstr = format_AES_output(AES_output, "hex");
if (finalstr == "") {
finalstr = tmpstr;
} else {
finalstr = finalstr + "$" + tmpstr;
}
}
//GM_log("AES Encrypted " + finalstr);
return finalstr;
}//aes_encrypt
// do AES decryption
function aes_decrypt(key, bodytext)
{
var w = new Array( 44 ); // subkey information
var state = new Array( 16 ); // working state
var round;
var finalstr = "";
var msg = "";
// get the message from the user
if (bodytext == '') {
var longmsg = document.getElementById(loc).contentWindow.document.body.innerHTML;
} else {
var longmsg = bodytext;
}
//check the key
if ( key[0] < 0 )
{
alert("There is a problem with the key");
return;
}
// expand the key
key = hexTo16(key);
w = key_expand( key );
var multmsg = longmsg.split("$");
var i = 0;
while (i < multmsg.length)
{
msg = hexTo16(multmsg[i]);
// problems??
if ( msg[0] < 0 )
{
alert("There is a problem with the message")
return;
}
// initial state = message
state = transpose( msg );
state = AddRoundKey(state, w, 10*4*4);
for( round=9; round>=1; round-- )
{
state = InvShiftRows(state);
state = SubBytes(state, S_dec);
// note here the spec uses 32-bit words, we are using bytes, so an extra *4
state = AddRoundKey(state, w, round*4*4);
state = InvMixColumns(state);
}
InvShiftRows(state);
SubBytes(state, S_dec);
AddRoundKey(state, w, 0);
// process output
AES_output = transpose( state );
var tmpstr = format_AES_output(AES_output, "asc");
if (finalstr == "") {
finalstr = tmpstr;
} else {
finalstr = finalstr + tmpstr;
}
i += 1;
}
//GM_log("AES Decrypted " + finalstr);
return finalstr;
}//aes_decrypt
//--- end of AES routines -------------------------------------------
//This space intentionally left blank
//