Skip to content

Commit aa7064c

Browse files
authored
Merge pull request #30 from simbo1905/fix-client-hexlength-entropy
Fix client ephemeral key entropy bug
2 parents 8a27820 + 4aeaea2 commit aa7064c

File tree

6 files changed

+118
-51
lines changed

6 files changed

+118
-51
lines changed

.jshintrc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"esversion": 11,
3+
"browser": true,
4+
"node": true,
5+
"strict": false,
6+
"undef": false,
7+
"unused": false,
8+
"predef": [
9+
"BigInteger",
10+
"randomStrings",
11+
"SHA256",
12+
"globalThis",
13+
"module",
14+
"exports",
15+
"require",
16+
"console",
17+
"CryptoJS",
18+
"random16byteHex",
19+
"define",
20+
"self"
21+
],
22+
"maxerr": 200,
23+
"evil": true,
24+
"expr": true,
25+
"funcscope": false,
26+
"iterator": false,
27+
"lastsemic": true,
28+
"laxbreak": false,
29+
"laxcomma": false,
30+
"loopfunc": false,
31+
"multistr": false,
32+
"onecase": false,
33+
"proto": false,
34+
"regexdash": false,
35+
"scripturl": false,
36+
"smarttabs": false,
37+
"shadow": true,
38+
"sub": false,
39+
"supernew": false,
40+
"validthis": false
41+
}

browser.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,20 +161,16 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
161161
};
162162

163163
// public helper
164-
/* jshint ignore:start */
165164
SRP6JavascriptClientSession.prototype.fromHex = function(s) {
166165
"use strict";
167166
return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat
168167
};
169-
/* jshint ignore:end */
170168

171169
// public helper to hide BigInteger from the linter
172-
/* jshint ignore:start */
173170
SRP6JavascriptClientSession.prototype.BigInteger = function(string, radix) {
174171
"use strict";
175172
return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat
176173
};
177-
/* jshint ignore:end */
178174

179175

180176
// public getter of the current workflow state.
@@ -224,9 +220,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
224220
"use strict";
225221
var s = null;
226222

227-
/* jshint ignore:start */
228223
s = randomStrings.hex(32); // 16 bytes
229-
/* jshint ignore:end */
230224

231225
// if you invoke without passing the string parameter the '+' operator uses 'undefined' so no nullpointer risk here
232226
var ss = this.H((new Date())+':'+opionalServerSalt+':'+s);
@@ -299,7 +293,6 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
299293
//console.log("SRP6JavascriptClientSession.prototype.computeU");
300294
this.check(Astr, "Astr");
301295
this.check(Bstr, "Bstr");
302-
/* jshint ignore:start */
303296
var output = this.H(Astr+Bstr);
304297
//console.log("js raw u:"+output);
305298
var u = new BigInteger(""+output,16);
@@ -308,16 +301,13 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
308301
throw new Error("SRP6Exception bad shared public value 'u' as u==0");
309302
}
310303
return u;
311-
/* jshint ignore:end */
312304
};
313305

314306
SRP6JavascriptClientSession.prototype.random16byteHex = function() {
315307
"use strict";
316308

317309
var r1 = null;
318-
/* jshint ignore:start */
319310
r1 = random16byteHex.random();
320-
/* jshint ignore:end */
321311
return r1;
322312
};
323313

@@ -413,9 +403,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
413403

414404
var ZERO = null;
415405

416-
/* jshint ignore:start */
417406
ZERO = BigInteger.ZERO;
418-
/* jshint ignore:end */
419407

420408
if (this.B.mod(this.N()).equals(ZERO)) {
421409
throw new Error("SRP6Exception bad server public value 'B' as B == 0 (mod N)");

client-exports.js

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
119119

120120
//console.log("js hash:"+hash)
121121
//console.log("js x before modN "+this.fromHex(hash));
122-
this.x = this.fromHex(hash).mod(this.N());
122+
this.x = this.fromHex(hash).mod(this.N);
123123
return this.x;
124124
};
125125

@@ -149,32 +149,34 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
149149
this.check(B, "B");
150150

151151
var exp = u.multiply(x).add(a);
152-
var tmp = this.g().modPow(x, this.N()).multiply(k);
153-
return B.subtract(tmp).modPow(exp, this.N());
152+
var tmp = this.g.modPow(x, this.N).multiply(k);
153+
return B.subtract(tmp).modPow(exp, this.N);
154154
};
155155
}
156156

157157
// public helper
158158
SRP6JavascriptClientSession.prototype.toHex = function(n) {
159159
"use strict";
160+
if (n === null || n === undefined || typeof n.toString !== 'function') {
161+
throw new Error("Invalid parameter for hex conversion: " + typeof n);
162+
}
160163
return n.toString(16);
161164
};
162165

163166
// public helper
164-
/* jshint ignore:start */
165167
SRP6JavascriptClientSession.prototype.fromHex = function(s) {
166168
"use strict";
169+
if (s === null || s === undefined || typeof s !== 'string') {
170+
throw new Error("Invalid hex string for BigInteger conversion: " + typeof s);
171+
}
167172
return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat
168173
};
169-
/* jshint ignore:end */
170174

171175
// public helper to hide BigInteger from the linter
172-
/* jshint ignore:start */
173176
SRP6JavascriptClientSession.prototype.BigInteger = function(string, radix) {
174177
"use strict";
175178
return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat
176179
};
177-
/* jshint ignore:end */
178180

179181

180182
// public getter of the current workflow state.
@@ -224,9 +226,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
224226
"use strict";
225227
var s = null;
226228

227-
/* jshint ignore:start */
228229
s = randomStrings.hex(32); // 16 bytes
229-
/* jshint ignore:end */
230230

231231
// if you invoke without passing the string parameter the '+' operator uses 'undefined' so no nullpointer risk here
232232
var ss = this.H((new Date())+':'+opionalServerSalt+':'+s);
@@ -249,7 +249,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
249249
// no need to check the parameters as generateX will do this
250250
var x = this.generateX(salt, identity, password);
251251
//console.log("js x: "+this.toHex(x));
252-
this.v = this.g().modPow(x, this.N());
252+
this.v = this.g.modPow(x, this.N);
253253
//console.log("js v: "+this.toHex(this.v));
254254
return this.toHex(this.v);
255255
};
@@ -299,7 +299,6 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
299299
//console.log("SRP6JavascriptClientSession.prototype.computeU");
300300
this.check(Astr, "Astr");
301301
this.check(Bstr, "Bstr");
302-
/* jshint ignore:start */
303302
var output = this.H(Astr+Bstr);
304303
//console.log("js raw u:"+output);
305304
var u = new BigInteger(""+output,16);
@@ -308,16 +307,13 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
308307
throw new Error("SRP6Exception bad shared public value 'u' as u==0");
309308
}
310309
return u;
311-
/* jshint ignore:end */
312310
};
313311

314312
SRP6JavascriptClientSession.prototype.random16byteHex = function() {
315313
"use strict";
316314

317315
var r1 = null;
318-
/* jshint ignore:start */
319316
r1 = random16byteHex.random();
320-
/* jshint ignore:end */
321317
return r1;
322318
};
323319

@@ -330,13 +326,14 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
330326
* to the generated random number.
331327
* @param N The safe prime.
332328
*/
333-
SRP6JavascriptClientSession.prototype.randomA = function(N) {
329+
SRP6JavascriptClientSession.prototype.randomA = function() {
334330
"use strict";
335331

336-
//console.log("N:"+N);
332+
//console.log("N:"+this.N);
333+
337334

338335
// our ideal number of random bits to use for `a` as long as its bigger than 256 bits
339-
var hexLength = this.toHex(N).length;
336+
var hexLength = this.toHex(this.N).length;
340337

341338
var ZERO = this.BigInteger("0", 10);
342339
var ONE = this.BigInteger("1", 10);
@@ -366,7 +363,7 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
366363
// this protected against a buggy browser random number generated generating a constant value
367364
// we mod(N) to wrap to the range [0,N) then loop if we get 0 to give [1,N)
368365
// mod(N) is broken due to buggy library code so we workaround with modPow(1,N)
369-
r = (oneTimeBi.add(rBi)).modPow(ONE, N);
366+
r = (oneTimeBi.add(rBi)).modPow(ONE, this.N);
370367
}
371368

372369
//console.log("r:"+r);
@@ -413,11 +410,9 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
413410

414411
var ZERO = null;
415412

416-
/* jshint ignore:start */
417413
ZERO = BigInteger.ZERO;
418-
/* jshint ignore:end */
419414

420-
if (this.B.mod(this.N()).equals(ZERO)) {
415+
if (this.B.mod(this.N).equals(ZERO)) {
421416
throw new Error("SRP6Exception bad server public value 'B' as B == 0 (mod N)");
422417
}
423418

@@ -432,11 +427,11 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
432427

433428
//console.log("N:"+this.toHex(this.N).toString(16));
434429

435-
this.a = this.randomA(this.N);
430+
this.a = this.randomA();
436431

437432
//console.log("a:" + this.toHex(this.a));
438433

439-
this.A = this.g().modPow(this.a, this.N());
434+
this.A = this.g.modPow(this.a, this.N);
440435
//console.log("A:" + this.toHex(this.A));
441436
this.check(this.A, "A");
442437

@@ -531,13 +526,9 @@ function srpClientFactory (N_base10, g_base10, k_base16) {
531526

532527
SRP6JavascriptClientSessionSHA256.prototype = new SRP6JavascriptClientSession();
533528

534-
SRP6JavascriptClientSessionSHA256.prototype.N = function() {
535-
return new BigInteger(N_base10, 10);
536-
}
529+
SRP6JavascriptClientSessionSHA256.prototype.N = new BigInteger(N_base10, 10);
537530

538-
SRP6JavascriptClientSessionSHA256.prototype.g = function() {
539-
return new BigInteger(g_base10, 10);
540-
}
531+
SRP6JavascriptClientSessionSHA256.prototype.g = new BigInteger(g_base10, 10);
541532

542533
SRP6JavascriptClientSessionSHA256.prototype.H = function (x) {
543534
return SHA256(x).toString().toLowerCase();

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"test:e2e:headed": "HEADED=true npm run test:e2e",
1616
"test:e2e:debug": "DEBUG=true HEADED=true npm run test:e2e",
1717
"test:e2e:slow": "SLOW=true npm run test:e2e",
18-
"build": "npm run build-es && npm run build-server",
18+
"build": "npm run build-es && npm run build-server && npm run lint",
19+
"lint": "npx jshint client.mjs server.mjs browser.js",
1920
"build-legacy": "mkdir -p dist && npm run build-es && rollup -c rollup.config.js",
2021
"test:umd": "npm run build-legacy && npm run build-server && mocha e2e/tests/umd.e2e.test.js --timeout 10000",
2122
"test:umd:headed": "HEADED=true npm run test:umd",
@@ -54,6 +55,7 @@
5455
"express": "^5.1.0",
5556
"jasmine-node": "^1.14.5",
5657
"jsonfn": "^0.31.0",
58+
"jshint": "^2.13.6",
5759
"mocha": "^11.7.1",
5860
"puppeteer": "^24.15.0",
5961
"rollup": "^4.46.2",

server-exports.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function srpServerFactory (N_base10, g_base10, k_base16) {
8282
//return {I: this.I, v: this.toHex(this.v), s: this.toHex(this.salt), b: this.toHex(this.b)};
8383
this.I = obj.I;
8484
this.v = this.fromHex(obj.v);
85-
this.salt = this.fromHex(obj.salt);
85+
this.salt = this.fromHex(obj.s); // Note: stored as 's', not 'salt'
8686
this.b = this.fromHex(obj.b);
8787
this.B = this.g.modPow(this.b, this.N).add(this.v.multiply(this.k)).mod(this.N);
8888
this.state = this.STEP_1;
@@ -92,24 +92,26 @@ function srpServerFactory (N_base10, g_base10, k_base16) {
9292
// public helper
9393
SRP6JavascriptServerSession.prototype.toHex = function(n) {
9494
"use strict";
95+
if (n === null || n === undefined || typeof n.toString !== 'function') {
96+
throw new Error("Invalid parameter for hex conversion: " + typeof n);
97+
}
9598
return n.toString(16);
9699
};
97100

98101
// public helper
99-
/* jshint ignore:start */
100102
SRP6JavascriptServerSession.prototype.fromHex = function(s) {
101103
"use strict";
104+
if (s === null || s === undefined || typeof s !== 'string') {
105+
throw new Error("Invalid hex string for BigInteger conversion: " + typeof s);
106+
}
102107
return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat
103108
};
104-
/* jshint ignore:end */
105109

106110
// public helper to hide BigInteger from the linter
107-
/* jshint ignore:start */
108111
SRP6JavascriptServerSession.prototype.BigInteger = function(string, radix) {
109112
"use strict";
110113
return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat
111114
};
112-
/* jshint ignore:end */
113115

114116

115117
// public getter of the current workflow state.
@@ -209,7 +211,6 @@ function srpServerFactory (N_base10, g_base10, k_base16) {
209211
//console.log("SRP6JavascriptServerSession.prototype.computeU");
210212
this.check(Astr, "Astr");
211213
this.check(Bstr, "Bstr");
212-
/* jshint ignore:start */
213214
var output = this.H(Astr+Bstr);
214215
//console.log("js raw u:"+output);
215216
var u = new BigInteger(""+output,16);
@@ -218,16 +219,13 @@ function srpServerFactory (N_base10, g_base10, k_base16) {
218219
throw new Error("SRP6Exception bad shared public value 'u' as u==0");
219220
}
220221
return u;
221-
/* jshint ignore:end */
222222
};
223223

224224
SRP6JavascriptServerSession.prototype.random16byteHex = function() {
225225
"use strict";
226226

227227
var r1 = null;
228-
/* jshint ignore:start */
229228
r1 = random16byteHex.random();
230-
/* jshint ignore:end */
231229
return r1;
232230
};
233231

@@ -242,6 +240,7 @@ function srpServerFactory (N_base10, g_base10, k_base16) {
242240
SRP6JavascriptServerSession.prototype.randomB = function() {
243241
"use strict";
244242

243+
245244
// our ideal number of random bits to use for `a` as long as its bigger than 256 bits
246245
var hexLength = this.toHex(this.N).length;
247246

0 commit comments

Comments
 (0)