forked from kjur/jsrsasign
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrsa.js
210 lines (181 loc) · 5.48 KB
/
rsa.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
*/
// Depends on jsbn.js and rng.js
// Version 1.1: support utf-8 encoding in pkcs1pad2
// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
return new BigInteger(str,r);
}
function linebrk(s,n) {
var ret = "";
var i = 0;
while(i + n < s.length) {
ret += s.substring(i,i+n) + "\n";
i += n;
}
return ret + s.substring(i,s.length);
}
function byte2Hex(b) {
if(b < 0x10)
return "0" + b.toString(16);
else
return b.toString(16);
}
// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
if(n < s.length + 11) { // TODO: fix for utf-8
alert("Message too long for RSA");
return null;
}
var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if(c < 128) { // encode using utf-8
ba[--n] = c;
}
else if((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
}
else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while(n > 2) { // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}
// PKCS#1 (OAEP) mask generation function
function oaep_mgf1_arr(seed, len, hash)
{
var mask = '', i = 0;
while (mask.length < len)
{
mask += hash(String.fromCharCode.apply(String, seed.concat([
(i & 0xff000000) >> 24,
(i & 0x00ff0000) >> 16,
(i & 0x0000ff00) >> 8,
i & 0x000000ff])));
i += 1;
}
return mask;
}
/**
* PKCS#1 (OAEP) pad input string s to n bytes, and return a bigint
* @name oaep_pad
* @param s raw string of message
* @param n key length of RSA key
* @param hash JavaScript function to calculate raw hash value from raw string or algorithm name (ex. "SHA1")
* @param hashLen byte length of resulted hash value (ex. 20 for SHA1)
* @return {BigInteger} BigInteger object of resulted PKCS#1 OAEP padded message
* @description
* This function calculates OAEP padded message from original message.<br/>
* NOTE: Since jsrsasign 6.2.0, 'hash' argument can accept an algorithm name such as "sha1".
* @example
* oaep_pad("aaa", 128) → big integer object // SHA-1 by default
* oaep_pad("aaa", 128, function(s) {...}, 20);
* oaep_pad("aaa", 128, "sha1");
*/
function oaep_pad(s, n, hash, hashLen) {
var MD = KJUR.crypto.MessageDigest;
var Util = KJUR.crypto.Util;
var algName = null;
if (!hash) hash = "sha1";
if (typeof hash === "string") {
algName = MD.getCanonicalAlgName(hash);
hashLen = MD.getHashLength(algName);
hash = function(s) { return hextorstr(Util.hashString(s, algName)); };
}
if (s.length + 2 * hashLen + 2 > n) {
throw "Message too long for RSA";
}
var PS = '', i;
for (i = 0; i < n - s.length - 2 * hashLen - 2; i += 1) {
PS += '\x00';
}
var DB = hash('') + PS + '\x01' + s;
var seed = new Array(hashLen);
new SecureRandom().nextBytes(seed);
var dbMask = oaep_mgf1_arr(seed, DB.length, hash);
var maskedDB = [];
for (i = 0; i < DB.length; i += 1) {
maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
}
var seedMask = oaep_mgf1_arr(maskedDB, seed.length, hash);
var maskedSeed = [0];
for (i = 0; i < seed.length; i += 1) {
maskedSeed[i + 1] = seed[i] ^ seedMask.charCodeAt(i);
}
return new BigInteger(maskedSeed.concat(maskedDB));
}
// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
// Set the public key fields N and e from hex strings
function RSASetPublic(N, E) {
this.isPublic = true;
this.isPrivate = false;
if (typeof N !== "string") {
this.n = N;
this.e = E;
} else if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
} else {
throw "Invalid RSA public key";
}
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}
// Return the PKCS#1 OAEP RSA encryption of "text" as an even-length hex string
function RSAEncryptOAEP(text, hash, hashLen) {
var m = oaep_pad(text, (this.n.bitLength() + 7) >> 3, hash, hashLen);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}
// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
// var h = this.encrypt(text);
// if(h) return hex2b64(h); else return null;
//}
// protected
RSAKey.prototype.doPublic = RSADoPublic;
// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
RSAKey.prototype.encryptOAEP = RSAEncryptOAEP;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
RSAKey.prototype.type = "RSA";