forked from rachanon/stdbWeb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
6.9 KiB
251 lines
6.9 KiB
/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ |
|
*/ |
|
// Depends on rsa.js and jsbn2.js |
|
|
|
// Version 1.1: support utf-8 decoding in pkcs1unpad2 |
|
|
|
// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext |
|
function pkcs1unpad2(d,n) { |
|
var b = d.toByteArray(); |
|
var i = 0; |
|
while(i < b.length && b[i] == 0) ++i; |
|
if(b.length-i != n-1 || b[i] != 2) |
|
return null; |
|
++i; |
|
while(b[i] != 0) |
|
if(++i >= b.length) return null; |
|
var ret = ""; |
|
while(++i < b.length) { |
|
var c = b[i] & 255; |
|
if(c < 128) { // utf-8 decode |
|
ret += String.fromCharCode(c); |
|
} |
|
else if((c > 191) && (c < 224)) { |
|
ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63)); |
|
++i; |
|
} |
|
else { |
|
ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63)); |
|
i += 2; |
|
} |
|
} |
|
return ret; |
|
} |
|
|
|
// PKCS#1 (OAEP) mask generation function |
|
function oaep_mgf1_str(seed, len, hash) |
|
{ |
|
var mask = '', i = 0; |
|
|
|
while (mask.length < len) |
|
{ |
|
mask += hash(seed + String.fromCharCode.apply(String, [ |
|
(i & 0xff000000) >> 24, |
|
(i & 0x00ff0000) >> 16, |
|
(i & 0x0000ff00) >> 8, |
|
i & 0x000000ff])); |
|
i += 1; |
|
} |
|
|
|
return mask; |
|
} |
|
|
|
// Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext |
|
function oaep_unpad(d, n, hash, hashLen) |
|
{ |
|
if (!hash) |
|
{ |
|
hash = rstr_sha1; |
|
hashLen = 20; |
|
} |
|
|
|
d = d.toByteArray(); |
|
|
|
var i; |
|
|
|
for (i = 0; i < d.length; i += 1) |
|
{ |
|
d[i] &= 0xff; |
|
} |
|
|
|
while (d.length < n) |
|
{ |
|
d.unshift(0); |
|
} |
|
|
|
d = String.fromCharCode.apply(String, d); |
|
|
|
if (d.length < 2 * hashLen + 2) |
|
{ |
|
throw "Cipher too short"; |
|
} |
|
|
|
var maskedSeed = d.substr(1, hashLen) |
|
var maskedDB = d.substr(hashLen + 1); |
|
|
|
var seedMask = oaep_mgf1_str(maskedDB, hashLen, hash); |
|
var seed = [], i; |
|
|
|
for (i = 0; i < maskedSeed.length; i += 1) |
|
{ |
|
seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i); |
|
} |
|
|
|
var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed), |
|
d.length - hashLen, hash); |
|
|
|
var DB = []; |
|
|
|
for (i = 0; i < maskedDB.length; i += 1) |
|
{ |
|
DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i); |
|
} |
|
|
|
DB = String.fromCharCode.apply(String, DB); |
|
|
|
if (DB.substr(0, hashLen) !== hash('')) |
|
{ |
|
throw "Hash mismatch"; |
|
} |
|
|
|
DB = DB.substr(hashLen); |
|
|
|
var first_one = DB.indexOf('\x01'); |
|
var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1; |
|
|
|
if (last_zero + 1 != first_one) |
|
{ |
|
throw "Malformed data"; |
|
} |
|
|
|
return DB.substr(first_one + 1); |
|
} |
|
|
|
// Set the private key fields N, e, and d from hex strings |
|
function RSASetPrivate(N,E,D) { |
|
this.isPrivate = true; |
|
if (typeof N !== "string") |
|
{ |
|
this.n = N; |
|
this.e = E; |
|
this.d = D; |
|
} |
|
else if(N != null && E != null && N.length > 0 && E.length > 0) { |
|
this.n = parseBigInt(N,16); |
|
this.e = parseInt(E,16); |
|
this.d = parseBigInt(D,16); |
|
} |
|
else |
|
alert("Invalid RSA private key"); |
|
} |
|
|
|
// Set the private key fields N, e, d and CRT params from hex strings |
|
function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { |
|
this.isPrivate = true; |
|
if (N == null) throw "RSASetPrivateEx N == null"; |
|
if (E == null) throw "RSASetPrivateEx E == null"; |
|
if (N.length == 0) throw "RSASetPrivateEx N.length == 0"; |
|
if (E.length == 0) throw "RSASetPrivateEx E.length == 0"; |
|
|
|
if (N != null && E != null && N.length > 0 && E.length > 0) { |
|
this.n = parseBigInt(N,16); |
|
this.e = parseInt(E,16); |
|
this.d = parseBigInt(D,16); |
|
this.p = parseBigInt(P,16); |
|
this.q = parseBigInt(Q,16); |
|
this.dmp1 = parseBigInt(DP,16); |
|
this.dmq1 = parseBigInt(DQ,16); |
|
this.coeff = parseBigInt(C,16); |
|
} else { |
|
alert("Invalid RSA private key in RSASetPrivateEx"); |
|
} |
|
} |
|
|
|
// Generate a new random private key B bits long, using public expt E |
|
function RSAGenerate(B,E) { |
|
var rng = new SecureRandom(); |
|
var qs = B>>1; |
|
this.e = parseInt(E,16); |
|
var ee = new BigInteger(E,16); |
|
for(;;) { |
|
for(;;) { |
|
this.p = new BigInteger(B-qs,1,rng); |
|
if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; |
|
} |
|
for(;;) { |
|
this.q = new BigInteger(qs,1,rng); |
|
if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; |
|
} |
|
if(this.p.compareTo(this.q) <= 0) { |
|
var t = this.p; |
|
this.p = this.q; |
|
this.q = t; |
|
} |
|
var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1 |
|
var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1 |
|
var phi = p1.multiply(q1); |
|
if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { |
|
this.n = this.p.multiply(this.q); // this.n = p * q |
|
this.d = ee.modInverse(phi); // this.d = |
|
this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1) |
|
this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1) |
|
this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p |
|
break; |
|
} |
|
} |
|
this.isPrivate = true; |
|
} |
|
|
|
// Perform raw private operation on "x": return x^d (mod n) |
|
function RSADoPrivate(x) { |
|
if(this.p == null || this.q == null) |
|
return x.modPow(this.d, this.n); |
|
|
|
// TODO: re-calculate any missing CRT params |
|
var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp? |
|
var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq? |
|
|
|
while(xp.compareTo(xq) < 0) |
|
xp = xp.add(this.p); |
|
// NOTE: |
|
// xp.subtract(xq) => cp -cq |
|
// xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h |
|
// xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M |
|
return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); |
|
} |
|
|
|
// Return the PKCS#1 RSA decryption of "ctext". |
|
// "ctext" is an even-length hex string and the output is a plain string. |
|
function RSADecrypt(ctext) { |
|
var c = parseBigInt(ctext, 16); |
|
var m = this.doPrivate(c); |
|
if(m == null) return null; |
|
return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); |
|
} |
|
|
|
// Return the PKCS#1 OAEP RSA decryption of "ctext". |
|
// "ctext" is an even-length hex string and the output is a plain string. |
|
function RSADecryptOAEP(ctext, hash, hashLen) { |
|
var c = parseBigInt(ctext, 16); |
|
var m = this.doPrivate(c); |
|
if(m == null) return null; |
|
return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash, hashLen); |
|
} |
|
|
|
// Return the PKCS#1 RSA decryption of "ctext". |
|
// "ctext" is a Base64-encoded string and the output is a plain string. |
|
//function RSAB64Decrypt(ctext) { |
|
// var h = b64tohex(ctext); |
|
// if(h) return this.decrypt(h); else return null; |
|
//} |
|
|
|
// protected |
|
RSAKey.prototype.doPrivate = RSADoPrivate; |
|
|
|
// public |
|
RSAKey.prototype.setPrivate = RSASetPrivate; |
|
RSAKey.prototype.setPrivateEx = RSASetPrivateEx; |
|
RSAKey.prototype.generate = RSAGenerate; |
|
RSAKey.prototype.decrypt = RSADecrypt; |
|
RSAKey.prototype.decryptOAEP = RSADecryptOAEP; |
|
//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
|
|
|