// RSA, a suite of routines for performing RSA public-key computations
// BarretMu, a class for performing Barrett modular reduction computations
// BigInt, a suite of routines for performing multiple-precision arithmetic
//
// Copyright 1998-2005 David Shapiro.
//
// You may use, re-use, abuse, copy, and modify this code to your liking, but
// please keep this header.
//
// Thanks!
//
// Dave Shapiro
// dave@ohdave.com
//
// Warning: Minimalist stripped-down code. Original version at http://www.ohdave.com/rsa/

function RSAKeyPair(ee,d,modulus)
{this.e=biFromHex(ee);this.m=biFromHex(modulus);this.chunkSize=2*biHighIndex(this.m);this.radix=16;this.barrett=new BM(this.m);}
function encryptedString(key,s)
{var a=new Array();var sl=s.length;var i=0;while(i<sl){a[i]=s.charCodeAt(i);i++;}
while(a.length%key.chunkSize!=0){a[i++]=0;}
var al=a.length;var result=[];var j,k,block;for(i=0;i<al;i+=key.chunkSize){block=new BI();j=0;for(k=i;k<i+key.chunkSize;++j){block.d[j]=a[k++];block.d[j]+=a[k++]<<8;}
var crypt=key.barrett.powMod(block,key.e);result.push(biToHex(crypt));}
return result.join(" ");}
function BM(m)
{this.modulus=biCopy(m);this.k=biHighIndex(this.modulus)+1;var b2k=new BI();b2k.d[2*this.k]=1;this.mu=biDivide(b2k,this.modulus);this.bkplus1=new BI();this.bkplus1.d[this.k+1]=1;this.modulo=BM_modulo;this.multiplyMod=BM_multiplyMod;this.powMod=BM_powMod;}
function BM_modulo(x)
{var q1=biDivideByRadixPower(x,this.k-1);var q2=biMultiply(q1,this.mu);var q3=biDivideByRadixPower(q2,this.k+1);var r1=biModuloByRadixPower(x,this.k+1);var r2term=biMultiply(q3,this.modulus);var r2=biModuloByRadixPower(r2term,this.k+1);var r=biSubtract(r1,r2);if(r.isNeg){r=biAdd(r,this.bkplus1);}
var rgtem=biCompare(r,this.modulus)>=0;while(rgtem){r=biSubtract(r,this.modulus);rgtem=biCompare(r,this.modulus)>=0;}
return r;}
function BM_multiplyMod(x,y)
{var xy=biMultiply(x,y);return this.modulo(xy);}
function BM_powMod(x,y)
{var result=new BI();result.d[0]=1;var a=x;var k=y;while(true){if((k.d[0]&1)!=0)result=this.multiplyMod(result,a);k=biShiftRight(k,1);if(k.d[0]==0&&biHighIndex(k)==0)break;a=this.multiplyMod(a,a);}
return result;}
var biRadixBits=16;var bitsPerDigit=biRadixBits;var bir=1<<16;var biHalfRadix=bir>>>1;var biRadixSquared=bir*bir;var maxDigitVal=bir-1;var maxDigits;var ZERO_ARRAY;var bigZero,bigOne;function setMaxDigits(value)
{maxDigits=value;ZERO_ARRAY=new Array(maxDigits);for(var iza=0;iza<ZERO_ARRAY.length;iza++)ZERO_ARRAY[iza]=0;bigZero=new BI();bigOne=new BI();bigOne.d[0]=1;}
setMaxDigits(20);var dpl10=15;function BI(flag)
{if(typeof flag=="boolean"&&flag==true){this.d=null;}
else{this.d=ZERO_ARRAY.slice(0);}
this.isNeg=false;}
function biCopy(bi)
{var result=new BI(true);result.d=bi.d.slice(0);result.isNeg=bi.isNeg;return result;}
function reverseStr(s)
{var result="";for(var i=s.length-1;i>-1;--i){result+=s.charAt(i);}
return result;}
var hexatrigesimalToChar=new Array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');var hexToChar=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];function digitToHex(n)
{var mask=0xf;var result="";for(i=0;i<4;++i){result+=hexToChar[n&mask];n>>>=4;}
return reverseStr(result);}
function biToHex(x)
{var result="";var n=biHighIndex(x);for(var i=biHighIndex(x);i>-1;--i){result+=digitToHex(x.d[i]);}
return result;}
function charToHex(c)
{var ZERO=48;var NINE=ZERO+9;var littleA=97;var littleZ=littleA+25;var bigA=65;var bigZ=65+25;var result;if(c>=ZERO&&c<=NINE){result=c-ZERO;}else if(c>=bigA&&c<=bigZ){result=10+c-bigA;}else if(c>=littleA&&c<=littleZ){result=10+c-littleA;}else{result=0;}
return result;}
function hexToDigit(s)
{var result=0;var sl=Math.min(s.length,4);for(var i=0;i<sl;++i){result<<=4;result|=charToHex(s.charCodeAt(i))}
return result;}
function biFromHex(s)
{var result=new BI();var sl=s.length;for(var i=sl,j=0;i>0;i-=4,++j){result.d[j]=hexToDigit(s.substr(Math.max(i-4,0),Math.min(i,4)));}
return result;}
function biDump(b)
{return(b.isNeg?"-":"")+b.d.join(" ");}
function biAdd(x,y)
{var result;if(x.isNeg!=y.isNeg){y.isNeg=!y.isNeg;result=biSubtract(x,y);y.isNeg=!y.isNeg;}
else{result=new BI();var c=0;var n;for(var i=0;i<x.digits.length;++i){n=x.digits[i]+y.digits[i]+c;result.digits[i]=n%biRadix;c=Number(n>=biRadix);}
result.isNeg=x.isNeg;}
return result;}
function biSubtract(x,y)
{var result;if(x.isNeg!=y.isNeg){y.isNeg=!y.isNeg;result=biAdd(x,y);y.isNeg=!y.isNeg;}else{result=new BI();var n,c;c=0;for(var i=0;i<x.d.length;++i){n=x.d[i]-y.d[i]+c;result.d[i]=n%bir;if(result.d[i]<0)result.d[i]+=bir;c=0-Number(n<0);}
if(c==-1){c=0;for(var i=0;i<x.d.length;++i){n=0-result.d[i]+c;result.d[i]=n%bir;if(result.d[i]<0)result.d[i]+=bir;c=0-Number(n<0);}
result.isNeg=!x.isNeg;}else{result.isNeg=x.isNeg;}}
return result;}
function biHighIndex(x)
{var result=x.d.length-1;while(result>0&&x.d[result]==0)--result;return result;}
function biNumBits(x)
{var n=biHighIndex(x);var d=x.d[n];var m=(n+1)*bitsPerDigit;var result;for(result=m;result>m-bitsPerDigit;--result){if((d&0x8000)!=0)break;d<<=1;}
return result;}
function biMultiply(x,y)
{var result=new BI();var c;var n=biHighIndex(x);var t=biHighIndex(y);var u,uv,k;for(var i=0;i<=t;++i){c=0;k=i;for(j=0;j<=n;++j,++k){uv=result.d[k]+x.d[j]*y.d[i]+c;result.d[k]=uv&maxDigitVal;c=uv>>>biRadixBits;}
result.d[i+n+1]=c;}
result.isNeg=x.isNeg!=y.isNeg;return result;}
function biMultiplyDigit(x,y)
{var n,c,uv;result=new BI();n=biHighIndex(x);c=0;for(var j=0;j<=n;++j){uv=result.d[j]+x.d[j]*y+c;result.d[j]=uv&maxDigitVal;c=uv>>>biRadixBits;}
result.d[1+n]=c;return result;}
function arrayCopy(src,srcStart,dest,destStart,n)
{var m=Math.min(srcStart+n,src.length);for(var i=srcStart,j=destStart;i<m;++i,++j){dest[j]=src[i];}}
var highBitMasks=new Array(0x0000,0x8000,0xC000,0xE000,0xF000,0xF800,0xFC00,0xFE00,0xFF00,0xFF80,0xFFC0,0xFFE0,0xFFF0,0xFFF8,0xFFFC,0xFFFE,0xFFFF);function biShiftLeft(x,n)
{var digitCount=Math.floor(n/bitsPerDigit);var result=new BI();arrayCopy(x.d,0,result.d,digitCount,result.d.length-digitCount);var bits=n%bitsPerDigit;var rightBits=bitsPerDigit-bits;for(var i=result.d.length-1,i1=i-1;i>0;--i,--i1){result.d[i]=((result.d[i]<<bits)&maxDigitVal)|((result.d[i1]&highBitMasks[bits])>>>(rightBits));}
result.d[0]=((result.d[i]<<bits)&maxDigitVal);result.isNeg=x.isNeg;return result;}
var lowBitMasks=new Array(0x0000,0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF);function biShiftRight(x,n)
{var digitCount=Math.floor(n/bitsPerDigit);var result=new BI();arrayCopy(x.d,digitCount,result.d,0,x.d.length-digitCount);var bits=n%bitsPerDigit;var leftBits=bitsPerDigit-bits;for(var i=0,i1=i+1;i<result.d.length-1;++i,++i1){result.d[i]=(result.d[i]>>>bits)|((result.d[i1]&lowBitMasks[bits])<<leftBits);}
result.d[result.d.length-1]>>>=bits;result.isNeg=x.isNeg;return result;}
function biMultiplyByRadixPower(x,n)
{var result=new BI();arrayCopy(x.d,0,result.d,n,result.d.length-n);return result;}
function biDivideByRadixPower(x,n)
{var result=new BI();arrayCopy(x.d,n,result.d,0,result.d.length-n);return result;}
function biModuloByRadixPower(x,n)
{var result=new BI();arrayCopy(x.d,0,result.d,0,n);return result;}
function biCompare(x,y)
{if(x.isNeg!=y.isNeg){return 1-2*Number(x.isNeg);}
for(var i=x.d.length-1;i>=0;--i){if(x.d[i]!=y.d[i]){if(x.isNeg){return 1-2*Number(x.d[i]>y.d[i]);}else{return 1-2*Number(x.d[i]<y.d[i]);}}}
return 0;}
function biDivideModulo(x,y)
{var nb=biNumBits(x);var tb=biNumBits(y);var origYIsNeg=y.isNeg;var q,r;if(nb<tb){if(x.isNeg){q=biCopy(bigOne);q.isNeg=!y.isNeg;x.isNeg=false;y.isNeg=false;r=biSubtract(y,x);x.isNeg=true;y.isNeg=origYIsNeg;}else{q=new BI();r=biCopy(x);}
return new Array(q,r);}
q=new BI();r=x;var t=Math.ceil(tb/bitsPerDigit)-1;var lambda=0;while(y.d[t]<biHalfRadix){y=biShiftLeft(y,1);++lambda;++tb;t=Math.ceil(tb/bitsPerDigit)-1;}
r=biShiftLeft(r,lambda);nb+=lambda;var n=Math.ceil(nb/bitsPerDigit)-1;var b=biMultiplyByRadixPower(y,n-t);while(biCompare(r,b)!=-1){++q.d[n-t];r=biSubtract(r,b);}
for(var i=n;i>t;--i){var ri=(i>=r.d.length)?0:r.d[i];var ri1=(i-1>=r.d.length)?0:r.d[i-1];var ri2=(i-2>=r.d.length)?0:r.d[i-2];var yt=(t>=y.d.length)?0:y.d[t];var yt1=(t-1>=y.d.length)?0:y.d[t-1];if(ri==yt){q.d[i-t-1]=maxDigitVal;}else{q.d[i-t-1]=Math.floor((ri*bir+ri1)/yt);}
var c1=q.d[i-t-1]*((yt*bir)+yt1);var c2=(ri*biRadixSquared)+((ri1*bir)+ri2);while(c1>c2){--q.d[i-t-1];c1=q.d[i-t-1]*((yt*bir)|yt1);c2=(ri*bir*bir)+((ri1*bir)+ri2);}
b=biMultiplyByRadixPower(y,i-t-1);r=biSubtract(r,biMultiplyDigit(b,q.d[i-t-1]));if(r.isNeg){r=biAdd(r,b);--q.d[i-t-1];}}
r=biShiftRight(r,lambda);q.isNeg=x.isNeg!=origYIsNeg;if(x.isNeg){if(origYIsNeg){q=biAdd(q,bigOne);}else{q=biSubtract(q,bigOne);}
y=biShiftRight(y,lambda);r=biSubtract(y,r);}
if(r.d[0]==0&&biHighIndex(r)==0)r.isNeg=false;return new Array(q,r);}
function biDivide(x,y)
{return biDivideModulo(x,y)[0];}