RSA Encoding in JavaScript
<html>
<head>
<!--
CryptoMX Tools
Copyright (C) 2004 - 2006 Derek Buitehuis
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-->
<title>RSA Encoding</title>
</head>
<body>
<script language="JavaScript"><!--
var dg=''
function makeArray(n) {
for (var i=1; i<=n; i++) {
this[i]=0
}
return this
}
function rc4(key, text) {
var i, x, y, t, x2;
status("rc4")
s=makeArray(0);
for (i=0; i<256; i++) {
s[i]=i
}
y=0
for (x=0; x<256; x++) {
y=(key.charCodeAt(x % key.length) + s[x] + y) % 256
t=s[x]; s[x]=s[y]; s[y]=t
}
x=0; y=0;
var z=""
for (x=0; x<text.length; x++) {
x2=x % 256
y=( s[x2] + y) % 256
t=s[x2]; s[x2]=s[y]; s[y]=t
z+= String.fromCharCode((text.charCodeAt(x) ^ s[(s[x2] + s[y]) % 256]))
}
return z
}
function badd(a,b) { // binary add
var r=''
var c=0
while(a || b) {
c=chop(a)+chop(b)+c
a=a.slice(0,-1); b=b.slice(0,-1)
if(c & 1) {
r="1"+r
} else {
r="0"+r
}
c>>=1
}
if(c) {r="1"+r}
return r
}
function chop(a) {
if(a.length) {
return parseInt(a.charAt(a.length-1))
} else {
return 0
}
}
function bsub(a,b) { // binary subtract
var r=''
var c=0
while(a) {
c=chop(a)-chop(b)-c
a=a.slice(0,-1); b=b.slice(0,-1)
if(c==0) {
r="0"+r
}
if(c == 1) {
r="1"+r
c=0
}
if(c == -1) {
r="1"+r
c=1
}
if(c==-2) {
r="0"+r
c=1
}
}
if(b || c) {return ''}
return bnorm(r)
}
function bnorm(r) { // trim off leading 0s
var i=r.indexOf('1')
if(i == -1) {
return '0'
} else {
return r.substr(i)
}
}
function bmul(a,b) { // binary multiply
var r=''; var p=''
while(a) {
if(chop(a) == '1') {
r=badd(r,b+p)
}
a=a.slice(0,-1)
p+='0'
}
return r;
}
function bmod(a,m) { // binary modulo
return bdiv(a,m).mod
}
function bdiv(a,m) { // binary divide & modulo
// this.q = quotient this.mod=remainder
var lm=m.length, al=a.length
var p='',d
this.q=''
for(n=0; n<al; n++) {
p=p+a.charAt(n);
if(p.length<lm || (d=bsub(p,m)) == '') {
this.q+='0'
} else {
if(this.q.charAt(0)=='0') {
this.q='1'
} else {
this.q+="1"
}
p=d
}
}
this.mod=bnorm(p)
return this
}
function bmodexp(x,y,m) { // binary modular exponentiation
var r='1'
status("bmodexp "+x+" "+y+" "+m)
while(y) {
if(chop(y) == 1) {
r=bmod(bmul(r,x),m)
}
y=y.slice(0,y.length-1)
x=bmod(bmul(x,x),m)
}
return bnorm(r)
}
function modexp(x,y,m) { // modular exponentiation
// convert packed bits (text) into strings of 0s and 1s
return b2t(bmodexp(t2b(x),t2b(y),t2b(m)))
}
function i2b(i) { // convert integer to binary
var r=''
while(i) {
if(i & 1) { r="1"+r} else {r="0"+r}
i>>=1;
}
return r? r:'0'
}
function t2b(s) {
var r=''
if(s=='') {return '0'}
while(s.length) {
var i=s.charCodeAt(0)
s=s.substr(1)
for(n=0; n<8; n++) {
r=((i & 1)? '1':'0') + r
i>>=1;
}
}
return bnorm(r)
}
function b2t(b) {
var r=''; var v=0; var m=1
while(b.length) {
v|=chop(b)*m
b=b.slice(0,-1)
m<<=1
if(m==256 || b=='') {
r+=String.fromCharCode(v)
v=0; m=1
}
}
return r
}
b64s='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"'
function textToBase64(t) {
status("t 2 b64")
var r=''; var m=0; var a=0; var tl=t.length-1; var c
for(n=0; n<=tl; n++) {
c=t.charCodeAt(n)
r+=b64s.charAt((c << m | a) & 63)
a = c >> (6-m)
m+=2
if(m==6 || n==tl) {
r+=b64s.charAt(a)
if((n%45)==44) {r+="\n"}
m=0
a=0
}
}
return r
}
function base64ToText(t) {
status("b64 2 t")
var r=''; var m=0; var a=0; var c
for(n=0; n<t.length; n++) {
c=b64s.indexOf(t.charAt(n))
if(c >= 0) {
if(m) {
r+=String.fromCharCode((c << (8-m))&255 | a)
}
a = c >> m
m+=2
if(m==8) { m=0 }
}
}
return r
}
function rand(n) { return Math.floor(Math.random() * n) }
function rstring(s,l) {
var r=""
var sl=s.length
while(l-->0) {
r+=s.charAt(rand(sl))
}
//status("rstring "+r)
return r
}
function key2(k) {
var l=k.length
var kl=l
var r=''
while(l--) {
r+=k.charAt((l*3)%kl)
}
return r
}
function rsaEncrypt(keylen,key,mod,text) {
// I read that rc4 with keys larger than 256 bytes doesn't significantly
// increase the level of rc4 encryption because it's sbuffer is 256 bytes
// makes sense to me, but what do i know...
status("encrypt")
if(text.length >= keylen) {
var sessionkey=rc4(rstring(text,keylen),rstring(text,keylen))
// session key must be less than mod, so mod it
sessionkey=b2t(bmod(t2b(sessionkey),t2b(mod)))
alert("sessionkey="+sessionkey)
// return the rsa encoded key and the encrypted text
// i'm double encrypting because it would seem to me to
// lessen known-plaintext attacks, but what do i know
return modexp(sessionkey,key,mod) +
rc4(key2(sessionkey),rc4(sessionkey,text))
} else {
// don't need a session key
return modexp(text,key,mod)
}
}
function rsaDecrypt(keylen,key,mod,text) {
status("decrypt")
if(text.length <= keylen) {
return modexp(text,key,mod)
} else {
// sessionkey is first keylen bytes
var sessionkey=text.substr(0,keylen)
text=text.substr(keylen)
// un-rsa the session key
sessionkey=modexp(sessionkey,key,mod)
alert("sessionkey="+sessionkey)
// double decrypt the text
return rc4(sessionkey,rc4(key2(sessionkey,text),text))
}
}
function trim2(d) { return d.substr(0,d.lastIndexOf('1')+1) }
function bgcd(u,v) { // return greatest common divisor
// algorythm from http://algo.inria.fr/banderier/Seminar/Vallee/index.html
var d, t
while(1) {
d=bsub(v,u)
//alert(v+" - "+u+" = "+d)
if(d=='0') {return u}
if(d) {
if(d.substr(-1)=='0') {
v=d.substr(0,d.lastIndexOf('1')+1) // v=(v-u)/2^val2(v-u)
} else v=d
} else {
t=v; v=u; u=t // swap u and v
}
}
}
function isPrime(p) {
var n,p1,p12,t
p1=bsub(p,'1')
t=p1.length-p1.lastIndexOf('1')
p12=trim2(p1)
for(n=0; n<2; n+=mrtest(p,p1,p12,t)) {
if(n<0) return 0
}
return 1
}
function mrtest(p,p1,p12,t) {
// Miller-Rabin test from forum.swathmore.edu/dr.math/
var n,a,u
a='1'+rstring('01',Math.floor(p.length/2)) // random a
//alert("mrtest "+p+", "+p1+", "+a+"-"+p12)
u=bmodexp(a,p12,p)
if(u=='1') {return 1}
for(n=0;n<t;n++) {
u=bmod(bmul(u,u),p)
//dg+=u+" "
if(u=='1') return -100
if(u==p1) return 1
}
return -100
}
pfactors='11100011001110101111000110001101'
// this number is 3*5*7*11*13*17*19*23*29*31*37
function prime(bits) {
// return a prime number of bits length
var p='1'+rstring('001',bits-2)+'1'
while( ! isPrime(p)) {
p=badd(p,'10'); // add 2
}
alert("p is "+p)
return p
}
function genkey(bits) {
q=prime(bits)
do {
p=q
q=prime(bits)
} while(bgcd(p,q)!='1')
p1q1=bmul(bsub(p,'1'),bsub(q,'1'))
// now we need a d, e, and an n so that:
// p1q1*n-1=de -> bmod(bsub(bmul(d,e),'1'),p1q1)='0'
// or more specifically an n so that d & p1q1 are rel prime and factor e
n='1'+rstring('001',Math.floor(bits/3)+2)
alert('n is '+n)
factorMe=badd(bmul(p1q1,n),'1')
alert('factor is '+factorMe)
//e=bgcd(factorMe,p1q1)
//alert('bgcd='+e)
e='1'
// is this always 1?
//r=bdiv(factorMe,e)
//alert('r='+r.q+" "+r.mod)
//if(r.mod != '0') {alert('Mod Error!')}
//factorMe=r.q
d=bgcd(factorMe,'11100011001110101111000110001101')
alert('d='+d)
if(d == '1' && e == '1') {alert('Factoring failed '+factorMe+' p='+p+' q='+q)}
e=bmul(e,d)
r=bdiv(factorMe,d)
d=r.q
if(r.mod != '0') {alert('Mod Error 2!')}
this.mod=b2t(bmul(p,q))
this.pub=b2t(e)
this.priv=b2t(d)
}
function status(a) { }//alert(a)}
// -->
</script>
<h3>RSA-type:</h3>
Note that long keys will likely be slow. <p>
<form method="POST" name="RSA">
<div align="center">
<table border="0">
<tr>
<td align="right">keylength:</td>
<td><input type="text" size="4" name="keylen" value="2"> How many bytes?</td>
<td> </td>
</tr>
<tr>
<td align="right">key:</td>
<td><input type="text" size="60" name="key" value="10001"></td>
<td>Binary</td>
</tr>
<tr>
<td>
<p align="right">modulo:</p>
</td>
<td><input type="text" size="60" name="mod" value="110010100001"></td>
<td>Binary</td>
</tr>
<tr>
<td align="right">text:</td>
<td><textarea name="text" rows="6" cols="60"></textarea></td>
<td>For sample values you can use:<br>
Public Key: 10001<br>
Modulo: 110010100001<br>
Private Key: 101011000001</td>
</tr>
<tr>
<td align="right">
<script language="JavaScript"><!--
function RSAencryptDemo() {
document.RSA.text.value=textToBase64(
rsaEncrypt(
parseInt(document.RSA.keylen.value),
b2t(document.RSA.key.value),
b2t(document.RSA.mod.value),
document.RSA.text.value))
document.RSA.key.value="101011000001"
}
function RSAdecryptDemo() {
document.RSA.text.value=rsaDecrypt(
parseInt(document.RSA.keylen.value),
b2t(document.RSA.key.value),
b2t(document.RSA.mod.value),
base64ToText(document.RSA.text.value))
document.RSA.key.value="10001"
}
// --></script>
</td>
<td>
<p align="center"><input type="button" name="B1" value="Encrypt" onclick="RSAencryptDemo()">
<input type="button" name="B2" value="Decrypt" onclick="RSAdecryptDemo()">
</p>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Related examples in the same category