blob: a474053afcd65f8f533cd87f445cbd7cca8ddeef [file] [log] [blame]
Alexander Afanasyev181a8b92013-02-28 13:28:53 -08001// Depends on rsa.js and jsbn2.js
2
3// Version 1.1: support utf-8 decoding in pkcs1unpad2
4
5// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
6function pkcs1unpad2(d,n) {
7 var b = d.toByteArray();
8 var i = 0;
9 while(i < b.length && b[i] == 0) ++i;
10 if(b.length-i != n-1 || b[i] != 2)
11 return null;
12 ++i;
13 while(b[i] != 0)
14 if(++i >= b.length) return null;
15 var ret = "";
16 while(++i < b.length) {
17 var c = b[i] & 255;
18 if(c < 128) { // utf-8 decode
19 ret += String.fromCharCode(c);
20 }
21 else if((c > 191) && (c < 224)) {
22 ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63));
23 ++i;
24 }
25 else {
26 ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63));
27 i += 2;
28 }
29 }
30 return ret;
31}
32
33// Set the private key fields N, e, and d from hex strings
34function RSASetPrivate(N,E,D) {
35 if(N != null && E != null && N.length > 0 && E.length > 0) {
36 this.n = parseBigInt(N,16);
37 this.e = parseInt(E,16);
38 this.d = parseBigInt(D,16);
39 }
40 else
41 alert("Invalid RSA private key");
42}
43
44// Set the private key fields N, e, d and CRT params from hex strings
45function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) {
46 if(N != null && E != null && N.length > 0 && E.length > 0) {
47 this.n = parseBigInt(N,16);
48 this.e = parseInt(E,16);
49 this.d = parseBigInt(D,16);
50 this.p = parseBigInt(P,16);
51 this.q = parseBigInt(Q,16);
52 this.dmp1 = parseBigInt(DP,16);
53 this.dmq1 = parseBigInt(DQ,16);
54 this.coeff = parseBigInt(C,16);
55 }
56 else
57 alert("Invalid RSA private key");
58}
59
60/**
61 * Generate a new random private key B bits long, using public expt E
62 */
63function RSAGenerate(B,E) {
64 var rng = new SecureRandom();
65 var qs = B>>1;
66 this.e = parseInt(E,16);
67 var ee = new BigInteger(E,16);
68 for(;;) {
69 for(;;) {
70 this.p = new BigInteger(B-qs,1,rng);
71 if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;
72 }
73 for(;;) {
74 this.q = new BigInteger(qs,1,rng);
75 if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;
76 }
77 if(this.p.compareTo(this.q) <= 0) {
78 var t = this.p;
79 this.p = this.q;
80 this.q = t;
81 }
82 var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1
83 var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1
84 var phi = p1.multiply(q1);
85 if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
86 this.n = this.p.multiply(this.q); // this.n = p * q
87 this.d = ee.modInverse(phi); // this.d =
88 this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1)
89 this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1)
90 this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p
91 break;
92 }
93 }
94}
95
96/**
97 * Perform raw private operation on "x": return x^d (mod n)
98 * @return x^d (mod n)
99 */
100function RSADoPrivate(x) {
101 if(this.p == null || this.q == null)
102 return x.modPow(this.d, this.n);
103
104 // TODO: re-calculate any missing CRT params
105 var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp?
106 var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq?
107
108 while(xp.compareTo(xq) < 0)
109 xp = xp.add(this.p);
110 // NOTE:
111 // xp.subtract(xq) => cp -cq
112 // xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h
113 // xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M
114 return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
115}
116
117// Return the PKCS#1 RSA decryption of "ctext".
118// "ctext" is an even-length hex string and the output is a plain string.
119function RSADecrypt(ctext) {
120 var c = parseBigInt(ctext, 16);
121 var m = this.doPrivate(c);
122 if(m == null) return null;
123 return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
124}
125
126// Return the PKCS#1 RSA decryption of "ctext".
127// "ctext" is a Base64-encoded string and the output is a plain string.
128//function RSAB64Decrypt(ctext) {
129// var h = b64tohex(ctext);
130// if(h) return this.decrypt(h); else return null;
131//}
132
133// protected
134RSAKey.prototype.doPrivate = RSADoPrivate;
135
136// public
137RSAKey.prototype.setPrivate = RSASetPrivate;
138RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
139RSAKey.prototype.generate = RSAGenerate;
140RSAKey.prototype.decrypt = RSADecrypt;
141//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;