node-firebird 2.3.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +544 -6
- package/lib/callback.d.ts +20 -0
- package/lib/callback.js +6 -16
- package/lib/firebird.msg.json +1370 -1370
- package/lib/gdscodes.d.ts +1519 -1520
- package/lib/gdscodes.js +5 -5
- package/lib/ieee754-decimal.d.ts +38 -0
- package/lib/ieee754-decimal.js +71 -118
- package/lib/index.d.ts +37 -338
- package/lib/index.js +97 -86
- package/lib/messages.d.ts +6 -0
- package/lib/messages.js +135 -162
- package/lib/pool.d.ts +23 -0
- package/lib/pool.js +34 -47
- package/lib/srp.d.ts +51 -0
- package/lib/srp.js +62 -72
- package/lib/types.d.ts +375 -0
- package/lib/types.js +22 -0
- package/lib/unix-crypt.d.ts +1 -0
- package/lib/unix-crypt.js +126 -152
- package/lib/utils.d.ts +16 -0
- package/lib/utils.js +24 -48
- package/lib/wire/connection.d.ts +100 -0
- package/lib/wire/connection.js +1029 -1009
- package/lib/wire/const.d.ts +567 -0
- package/lib/wire/const.js +620 -599
- package/lib/wire/database.d.ts +57 -0
- package/lib/wire/database.js +111 -108
- package/lib/wire/eventConnection.d.ts +16 -0
- package/lib/wire/eventConnection.js +29 -31
- package/lib/wire/fbEventManager.d.ts +45 -0
- package/lib/wire/fbEventManager.js +38 -57
- package/lib/wire/serialize.d.ts +97 -0
- package/lib/wire/serialize.js +42 -141
- package/lib/wire/service.d.ts +66 -0
- package/lib/wire/service.js +374 -401
- package/lib/wire/socket.d.ts +59 -0
- package/lib/wire/socket.js +79 -40
- package/lib/wire/statement.d.ts +25 -0
- package/lib/wire/statement.js +13 -9
- package/lib/wire/transaction.d.ts +21 -0
- package/lib/wire/transaction.js +33 -61
- package/lib/wire/xsqlvar.d.ts +175 -0
- package/lib/wire/xsqlvar.js +169 -269
- package/package.json +12 -4
- package/src/callback.ts +55 -0
- package/{poc/node_modules/node-firebird/lib/gdscodes.js → src/gdscodes.ts} +3 -3
- package/{poc/node_modules/node-firebird/lib/ieee754-decimal.js → src/ieee754-decimal.ts} +7 -14
- package/src/index.ts +157 -0
- package/{poc/node_modules/node-firebird/lib/messages.js → src/messages.ts} +163 -162
- package/src/pool.ts +209 -0
- package/{poc/node_modules/node-firebird/lib/srp.js → src/srp.ts} +74 -33
- package/src/types.ts +419 -0
- package/{poc/node_modules/node-firebird/lib/unix-crypt.js → src/unix-crypt.ts} +6 -12
- package/{poc/node_modules/node-firebird/lib/utils.js → src/utils.ts} +11 -20
- package/{poc/node_modules/node-firebird/lib/wire/connection.js → src/wire/connection.ts} +611 -129
- package/{poc/node_modules/node-firebird/lib/wire/const.js → src/wire/const.ts} +96 -9
- package/{poc/node_modules/node-firebird/lib/wire/database.js → src/wire/database.ts} +110 -44
- package/{poc/node_modules/node-firebird/lib/wire/eventConnection.js → src/wire/eventConnection.ts} +29 -14
- package/{poc/node_modules/node-firebird/lib/wire/fbEventManager.js → src/wire/fbEventManager.ts} +39 -30
- package/{poc/node_modules/node-firebird/lib/wire/serialize.js → src/wire/serialize.ts} +78 -59
- package/{poc/node_modules/node-firebird/lib/wire/service.js → src/wire/service.ts} +90 -88
- package/src/wire/socket.ts +262 -0
- package/src/wire/statement.ts +71 -0
- package/{poc/node_modules/node-firebird/lib/wire/transaction.js → src/wire/transaction.ts} +25 -19
- package/{poc/node_modules/node-firebird/lib/wire/xsqlvar.js → src/wire/xsqlvar.ts} +244 -143
- package/.eslintrc.json +0 -12
- package/.github/workflows/codeql.yml +0 -76
- package/.github/workflows/node.js.yml +0 -95
- package/BIGINT_MIGRATION.md +0 -374
- package/CI_DEBUGGING_GUIDE.md +0 -148
- package/ENCRYPTION_CALLBACK.md +0 -152
- package/FIREBIRD_LOG_FEATURE.md +0 -145
- package/MINIMAL_CHANGES_SUMMARY.md +0 -136
- package/PR_SUMMARY.md +0 -96
- package/ROADMAP.md +0 -223
- package/SRP_PROTOCOL.md +0 -482
- package/poc/README.md +0 -160
- package/poc/helpers.js +0 -59
- package/poc/node_modules/.package-lock.json +0 -14
- package/poc/node_modules/node-firebird/.eslintrc.json +0 -12
- package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +0 -76
- package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +0 -95
- package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +0 -374
- package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +0 -148
- package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +0 -152
- package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +0 -145
- package/poc/node_modules/node-firebird/LICENSE +0 -373
- package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +0 -136
- package/poc/node_modules/node-firebird/PR_SUMMARY.md +0 -96
- package/poc/node_modules/node-firebird/README.md +0 -794
- package/poc/node_modules/node-firebird/ROADMAP.md +0 -223
- package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +0 -482
- package/poc/node_modules/node-firebird/lib/callback.js +0 -38
- package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +0 -1524
- package/poc/node_modules/node-firebird/lib/index.d.ts +0 -316
- package/poc/node_modules/node-firebird/lib/index.js +0 -128
- package/poc/node_modules/node-firebird/lib/pool.js +0 -108
- package/poc/node_modules/node-firebird/lib/wire/socket.js +0 -175
- package/poc/node_modules/node-firebird/lib/wire/statement.js +0 -48
- package/poc/node_modules/node-firebird/package.json +0 -38
- package/poc/node_modules/node-firebird/vitest.config.js +0 -24
- package/poc/package-lock.json +0 -21
- package/poc/package.json +0 -12
- package/poc/reproduce-fixed.js +0 -150
- package/poc/reproduce.js +0 -133
- package/vitest.config.js +0 -27
- /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg +0 -0
- /package/{poc/node_modules/node-firebird/lib → src}/firebird.msg.json +0 -0
package/src/pool.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/***************************************
|
|
2
|
+
*
|
|
3
|
+
* Simple Pooling
|
|
4
|
+
*
|
|
5
|
+
***************************************/
|
|
6
|
+
|
|
7
|
+
import type { Callback } from './callback';
|
|
8
|
+
|
|
9
|
+
type AttachFn = (options: any, callback: Callback) => void;
|
|
10
|
+
|
|
11
|
+
class Pool {
|
|
12
|
+
attach: AttachFn;
|
|
13
|
+
internaldb: any[];
|
|
14
|
+
pooldb: any[];
|
|
15
|
+
dbinuse: number;
|
|
16
|
+
_creating: number;
|
|
17
|
+
max: number;
|
|
18
|
+
pending: Callback[];
|
|
19
|
+
options: any;
|
|
20
|
+
_destroyed: boolean;
|
|
21
|
+
|
|
22
|
+
constructor(attach: AttachFn, max: number, options: any) {
|
|
23
|
+
this.attach = attach;
|
|
24
|
+
this.internaldb = []; // connections created by the pool (for destroy)
|
|
25
|
+
this.pooldb = []; // connections available in the pool (idle)
|
|
26
|
+
this.dbinuse = 0; // connections currently handed out to callers
|
|
27
|
+
this._creating = 0; // connections currently being created (attach in flight)
|
|
28
|
+
this.max = max || 4;
|
|
29
|
+
this.pending = []; // callbacks waiting for a free slot
|
|
30
|
+
this.options = options;
|
|
31
|
+
this._destroyed = false; // true after destroy() — prevents further use
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get(callback: Callback): this {
|
|
35
|
+
// [Fix 2] Reject immediately if the pool has already been destroyed.
|
|
36
|
+
if (this._destroyed) {
|
|
37
|
+
callback(new Error('Pool has been destroyed'), null);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
var self = this;
|
|
41
|
+
self.pending.push(callback);
|
|
42
|
+
self.check();
|
|
43
|
+
return self;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
check(): this {
|
|
47
|
+
var self = this;
|
|
48
|
+
|
|
49
|
+
// [Fix 2] Do not serve requests on a destroyed pool.
|
|
50
|
+
if (self._destroyed) return self;
|
|
51
|
+
|
|
52
|
+
if ((self.dbinuse + self._creating) >= self.max)
|
|
53
|
+
return self;
|
|
54
|
+
|
|
55
|
+
var cb = self.pending.shift();
|
|
56
|
+
if (!cb)
|
|
57
|
+
return self;
|
|
58
|
+
if (self.pooldb.length) {
|
|
59
|
+
var db = self.pooldb.shift();
|
|
60
|
+
// Discard connections that have been closed or destroyed while idle
|
|
61
|
+
if (db.connection && (db.connection._isClosed || db.connection._isDetach || !db.connection._socket || db.connection._socket.destroyed)) {
|
|
62
|
+
var idx = self.internaldb.indexOf(db);
|
|
63
|
+
if (idx !== -1) self.internaldb.splice(idx, 1);
|
|
64
|
+
self.pending.unshift(cb);
|
|
65
|
+
setImmediate(function () { self.check(); });
|
|
66
|
+
return self;
|
|
67
|
+
}
|
|
68
|
+
// Idle connection available — hand it out immediately.
|
|
69
|
+
self.dbinuse++;
|
|
70
|
+
cb(null, db);
|
|
71
|
+
} else {
|
|
72
|
+
// No idle connection — create a new one via attach().
|
|
73
|
+
self._creating++;
|
|
74
|
+
|
|
75
|
+
var timedOut = false;
|
|
76
|
+
var timer: NodeJS.Timeout | null = null;
|
|
77
|
+
|
|
78
|
+
// [Fix 1] Optional per-attach timeout.
|
|
79
|
+
// If attach() does not call back within connectTimeout ms (e.g. because
|
|
80
|
+
// the server accepted TCP but stalled on the Firebird wire protocol), we
|
|
81
|
+
// free the slot and notify the caller. Any connection that arrives late
|
|
82
|
+
// is discarded in the attach() callback below.
|
|
83
|
+
if (self.options.connectTimeout > 0) {
|
|
84
|
+
timer = setTimeout(function () {
|
|
85
|
+
timedOut = true;
|
|
86
|
+
self._creating--;
|
|
87
|
+
cb(new Error(
|
|
88
|
+
'Connection timeout after ' + self.options.connectTimeout + 'ms'
|
|
89
|
+
), null);
|
|
90
|
+
// Free the slot so the next pending request can be served.
|
|
91
|
+
setImmediate(function () { self.check(); });
|
|
92
|
+
}, self.options.connectTimeout);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.attach(self.options, function (err, db) {
|
|
96
|
+
|
|
97
|
+
// [Fix 1] Timeout already fired — discard this late connection.
|
|
98
|
+
// Without this guard the socket would stay open until the OS-level
|
|
99
|
+
// TCP timeout (potentially minutes), leaking a file descriptor.
|
|
100
|
+
if (timedOut) {
|
|
101
|
+
if (db) {
|
|
102
|
+
try {
|
|
103
|
+
// _pooled = false forces a real op_detach / socket close
|
|
104
|
+
// instead of a silent pool-return emit.
|
|
105
|
+
if (db.connection) db.connection._pooled = false;
|
|
106
|
+
db.detach();
|
|
107
|
+
} catch (e) { /* ignore cleanup errors */ }
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (timer) clearTimeout(timer);
|
|
113
|
+
self._creating--;
|
|
114
|
+
|
|
115
|
+
// [Fix 3] Pool was destroyed while attach() was in flight.
|
|
116
|
+
if (self._destroyed) {
|
|
117
|
+
if (db) {
|
|
118
|
+
try {
|
|
119
|
+
if (db.connection) db.connection._pooled = false;
|
|
120
|
+
db.detach();
|
|
121
|
+
} catch (e) { /* ignore cleanup errors */ }
|
|
122
|
+
}
|
|
123
|
+
cb(new Error('Pool has been destroyed'), null);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!err) {
|
|
128
|
+
self.dbinuse++;
|
|
129
|
+
self.internaldb.push(db);
|
|
130
|
+
db.on('detach', function () {
|
|
131
|
+
// also in pool (could be a twice call to detach)
|
|
132
|
+
if (self.pooldb.indexOf(db) !== -1 || self.internaldb.indexOf(db) === -1)
|
|
133
|
+
return;
|
|
134
|
+
// if not usable don't put it back in the pool
|
|
135
|
+
if (db.connection._isClosed || db.connection._isDetach || db.connection._pooled === false)
|
|
136
|
+
self.internaldb.splice(self.internaldb.indexOf(db), 1);
|
|
137
|
+
else
|
|
138
|
+
self.pooldb.push(db);
|
|
139
|
+
|
|
140
|
+
self.dbinuse--;
|
|
141
|
+
self.check();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
cb(err, db);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
setImmediate(function() {
|
|
149
|
+
self.check();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return self;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
destroy(callback?: (err?: any) => void): void {
|
|
156
|
+
var self = this;
|
|
157
|
+
self._destroyed = true;
|
|
158
|
+
|
|
159
|
+
// [Fix 4] Drain pending callbacks so callers are not left hanging.
|
|
160
|
+
// This is critical when destroy() is called as a recovery measure after
|
|
161
|
+
// a timeout: without draining, every concurrent pool.get() that had not
|
|
162
|
+
// yet received a slot would hang until the process exits.
|
|
163
|
+
var draining = self.pending.splice(0);
|
|
164
|
+
draining.forEach(function (cb) {
|
|
165
|
+
try { cb(new Error('Pool is being destroyed'), null); } catch (e) { /* ignore */ }
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
var connectionCount = this.internaldb.length;
|
|
169
|
+
|
|
170
|
+
if (connectionCount === 0 && callback) {
|
|
171
|
+
callback();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function detachCallback(err?: any) {
|
|
175
|
+
if (err) {
|
|
176
|
+
if (callback) {
|
|
177
|
+
callback(err);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
connectionCount--;
|
|
183
|
+
if (connectionCount === 0 && callback) {
|
|
184
|
+
callback();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.internaldb.forEach(function(db) {
|
|
189
|
+
if (db.connection._pooled === false) {
|
|
190
|
+
detachCallback();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// check if the db is not free into the pool otherwise user should manual detach it
|
|
194
|
+
var _db_in_pool = self.pooldb.indexOf(db);
|
|
195
|
+
if (_db_in_pool !== -1) {
|
|
196
|
+
self.pooldb.splice(_db_in_pool, 1);
|
|
197
|
+
db.connection._pooled = false;
|
|
198
|
+
db.detach(detachCallback);
|
|
199
|
+
} else {
|
|
200
|
+
// [Fix 5] Connection is currently in use (dbinuse > 0).
|
|
201
|
+
// The caller is responsible for releasing it via detach().
|
|
202
|
+
// Count it down here so the destroy() callback is not blocked forever.
|
|
203
|
+
detachCallback();
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export = Pool;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
export interface KeyPair {
|
|
4
|
+
public: bigint;
|
|
5
|
+
private: bigint;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ClientProof {
|
|
9
|
+
clientSessionKey: bigint;
|
|
10
|
+
authData: bigint;
|
|
11
|
+
}
|
|
2
12
|
|
|
3
13
|
const SRP_KEY_SIZE = 128,
|
|
4
14
|
SRP_KEY_MAX = BigInt('340282366920938463463374607431768211456'), // 1 << SRP_KEY_SIZE
|
|
@@ -18,13 +28,25 @@ const PRIME = {
|
|
|
18
28
|
k: BigInt('1277432915985975349439481660349303019122249719989')
|
|
19
29
|
};
|
|
20
30
|
|
|
31
|
+
const kCache: Record<string, bigint> = {};
|
|
32
|
+
function getK(hashAlgo: string): bigint {
|
|
33
|
+
if (!kCache[hashAlgo]) {
|
|
34
|
+
kCache[hashAlgo] = BigInt('0x' + getHash(hashAlgo, pad(PRIME.N), pad(PRIME.g)));
|
|
35
|
+
}
|
|
36
|
+
return kCache[hashAlgo];
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
/**
|
|
22
40
|
* Generate a client key pair.
|
|
23
41
|
*
|
|
24
42
|
* @param a BigInt Client private key.
|
|
25
43
|
* @returns {{private: BigInt, public: BigInt}}
|
|
26
44
|
*/
|
|
27
|
-
|
|
45
|
+
export function clientSeed(a: bigint = toBigInt(crypto.randomBytes(SRP_KEY_SIZE)) % PRIME.N): KeyPair {
|
|
46
|
+
// a must be in [0, N): clientSession() reduces the exponent (a + ux) mod N
|
|
47
|
+
// to match the Firebird engine, but A = g^a is computed from the raw a.
|
|
48
|
+
// When random a >= N (~10% of 1024-bit values), the proof diverges from
|
|
49
|
+
// the server's session key, causing sporadic auth failures.
|
|
28
50
|
var A = modPow(PRIME.g, a, PRIME.N);
|
|
29
51
|
|
|
30
52
|
dump('a', a);
|
|
@@ -45,21 +67,29 @@ exports.clientSeed = function(a = toBigInt(crypto.randomBytes(SRP_KEY_SIZE))) {
|
|
|
45
67
|
* @param b BigInt Server private key.
|
|
46
68
|
* @returns {{private: BigInt, public: BigInt}}
|
|
47
69
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
export function serverSeed(user: string, password: string, salt: Buffer, b?: bigint | string, hashAlgo: string = 'sha1'): KeyPair {
|
|
71
|
+
if (typeof b === 'string') {
|
|
72
|
+
hashAlgo = b;
|
|
73
|
+
b = undefined;
|
|
74
|
+
}
|
|
75
|
+
const bKey: bigint = (b === undefined || b === null)
|
|
76
|
+
? toBigInt(crypto.randomBytes(SRP_KEY_SIZE))
|
|
77
|
+
: b as bigint;
|
|
78
|
+
var v = getVerifier(user, password, salt, 'sha1');
|
|
79
|
+
var gb = modPow(PRIME.g, bKey, PRIME.N);
|
|
80
|
+
var k = getK('sha1');
|
|
81
|
+
var kv = (k * v) % PRIME.N;
|
|
52
82
|
var B = (kv + gb) % PRIME.N;
|
|
53
83
|
|
|
54
84
|
dump('v', v);
|
|
55
|
-
dump('b',
|
|
56
|
-
dump('gb',
|
|
85
|
+
dump('b', bKey);
|
|
86
|
+
dump('gb', bKey);
|
|
57
87
|
dump('kv', v);
|
|
58
88
|
dump('B', B);
|
|
59
89
|
|
|
60
90
|
return {
|
|
61
91
|
public: B,
|
|
62
|
-
private:
|
|
92
|
+
private: bKey
|
|
63
93
|
};
|
|
64
94
|
}
|
|
65
95
|
|
|
@@ -74,9 +104,9 @@ exports.serverSeed = function(user, password, salt, b = toBigInt(crypto.randomBy
|
|
|
74
104
|
* @param b BigInt Server private key.
|
|
75
105
|
* @returns {BigInt}
|
|
76
106
|
*/
|
|
77
|
-
|
|
78
|
-
var u = getScramble(A, B);
|
|
79
|
-
var v = getVerifier(user, password, salt);
|
|
107
|
+
export function serverSession(user: string, password: string, salt: Buffer, A: bigint, B: bigint, b: bigint, hashAlgo: string = 'sha1'): bigint {
|
|
108
|
+
var u = getScramble(A, B, 'sha1');
|
|
109
|
+
var v = getVerifier(user, password, salt, 'sha1');
|
|
80
110
|
var vu = modPow(v, u, PRIME.N);
|
|
81
111
|
var Avu = (A * vu) % PRIME.N;
|
|
82
112
|
var sessionSecret = modPow(Avu, b, PRIME.N);
|
|
@@ -91,8 +121,8 @@ exports.serverSession = function(user, password, salt, A, B, b) {
|
|
|
91
121
|
/**
|
|
92
122
|
* M = H(H(N) xor H(g), H(I), s, A, B, K)
|
|
93
123
|
*/
|
|
94
|
-
|
|
95
|
-
var K = clientSession(user, password, salt, A, B, a);
|
|
124
|
+
export function clientProof(user: string, password: string, salt: Buffer, A: bigint, B: bigint, a: bigint, hashAlgo: string = 'sha1'): ClientProof {
|
|
125
|
+
var K = clientSession(user, password, salt, A, B, a, 'sha1');
|
|
96
126
|
var n1, n2;
|
|
97
127
|
|
|
98
128
|
n1 = toBigInt(getHash('sha1', toBuffer(PRIME.N)));
|
|
@@ -118,14 +148,20 @@ exports.clientProof = function(user, password, salt, A, B, a, hashAlgo) {
|
|
|
118
148
|
/**
|
|
119
149
|
* Pad hex string.
|
|
120
150
|
*/
|
|
121
|
-
function hexPad(hex) {
|
|
151
|
+
export function hexPad(hex: string): string {
|
|
122
152
|
if (hex.length % 2 !== 0) {
|
|
123
153
|
hex = '0' + hex;
|
|
124
154
|
}
|
|
125
155
|
|
|
126
156
|
return hex;
|
|
127
157
|
}
|
|
128
|
-
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The SRP prime modulus N (1024-bit, same value used by Firebird).
|
|
161
|
+
* Exported so tests and tooling can assert key-size invariants without
|
|
162
|
+
* duplicating the constant.
|
|
163
|
+
*/
|
|
164
|
+
export const PRIME_N: bigint = PRIME.N;
|
|
129
165
|
|
|
130
166
|
/**
|
|
131
167
|
* Pad key with SRP_KEY_SIZE.
|
|
@@ -133,11 +169,15 @@ exports.hexPad = hexPad;
|
|
|
133
169
|
* @param n BigInt Key to pad.
|
|
134
170
|
* @returns Buffer
|
|
135
171
|
*/
|
|
136
|
-
function pad(n) {
|
|
172
|
+
function pad(n: bigint): Buffer {
|
|
137
173
|
var buff = Buffer.from(hexPad(n.toString(16)), 'hex');
|
|
138
174
|
|
|
139
175
|
if (buff.length > SRP_KEY_SIZE) {
|
|
140
176
|
buff = buff.slice(buff.length - SRP_KEY_SIZE, buff.length);
|
|
177
|
+
} else if (buff.length < SRP_KEY_SIZE) {
|
|
178
|
+
var padded = Buffer.alloc(SRP_KEY_SIZE);
|
|
179
|
+
buff.copy(padded, SRP_KEY_SIZE - buff.length);
|
|
180
|
+
buff = padded;
|
|
141
181
|
}
|
|
142
182
|
|
|
143
183
|
return buff;
|
|
@@ -150,8 +190,8 @@ function pad(n) {
|
|
|
150
190
|
* @param B BigInt Server public key.
|
|
151
191
|
* @returns {BigInt}
|
|
152
192
|
*/
|
|
153
|
-
function getScramble(A, B) {
|
|
154
|
-
return BigInt('0x' + getHash(
|
|
193
|
+
function getScramble(A: bigint, B: bigint, hashAlgo: string = 'sha1'): bigint {
|
|
194
|
+
return BigInt('0x' + getHash(hashAlgo, pad(A), pad(B)));
|
|
155
195
|
}
|
|
156
196
|
|
|
157
197
|
/**
|
|
@@ -169,11 +209,12 @@ function getScramble(A, B) {
|
|
|
169
209
|
* @param B BigInt Server public key.
|
|
170
210
|
* @param a BigInt Client private key.
|
|
171
211
|
*/
|
|
172
|
-
function clientSession(user, password, salt, A, B, a) {
|
|
173
|
-
var u = getScramble(A, B);
|
|
174
|
-
var x = getUserHash(user, salt, password);
|
|
212
|
+
function clientSession(user: string, password: string, salt: Buffer, A: bigint, B: bigint, a: bigint, hashAlgo: string = 'sha1'): bigint {
|
|
213
|
+
var u = getScramble(A, B, 'sha1');
|
|
214
|
+
var x = getUserHash(user, salt, password, 'sha1');
|
|
175
215
|
var gx = modPow(PRIME.g, x, PRIME.N);
|
|
176
|
-
var
|
|
216
|
+
var k = getK('sha1');
|
|
217
|
+
var kgx = (k * gx) % PRIME.N;
|
|
177
218
|
var diff = (B - kgx) % PRIME.N;
|
|
178
219
|
|
|
179
220
|
if (diff < 0n) {
|
|
@@ -210,9 +251,9 @@ function clientSession(user, password, salt, A, B, a) {
|
|
|
210
251
|
* @param password string Connection password.
|
|
211
252
|
* @returns {BigInt}
|
|
212
253
|
*/
|
|
213
|
-
function getUserHash(user, salt, password) {
|
|
214
|
-
var hash1 = getHash(
|
|
215
|
-
var hash2 = getHash(
|
|
254
|
+
function getUserHash(user: string, salt: Buffer, password: string, hashAlgo: string = 'sha1'): bigint {
|
|
255
|
+
var hash1 = getHash(hashAlgo, user.toUpperCase(), ':', password);
|
|
256
|
+
var hash2 = getHash(hashAlgo, salt, toBuffer(hash1));
|
|
216
257
|
|
|
217
258
|
return toBigInt(hash2);
|
|
218
259
|
}
|
|
@@ -225,8 +266,8 @@ function getUserHash(user, salt, password) {
|
|
|
225
266
|
* @param salt BigInt Connection salt.
|
|
226
267
|
* @returns {BigInt}
|
|
227
268
|
*/
|
|
228
|
-
function getVerifier(user, password, salt) {
|
|
229
|
-
return modPow(PRIME.g, getUserHash(user, salt, password), PRIME.N);
|
|
269
|
+
function getVerifier(user: string, password: string, salt: Buffer, hashAlgo: string = 'sha1'): bigint {
|
|
270
|
+
return modPow(PRIME.g, getUserHash(user, salt, password, hashAlgo), PRIME.N);
|
|
230
271
|
}
|
|
231
272
|
|
|
232
273
|
/**
|
|
@@ -236,7 +277,7 @@ function getVerifier(user, password, salt) {
|
|
|
236
277
|
* @param data any[] Data to hash.
|
|
237
278
|
* @returns {string}
|
|
238
279
|
*/
|
|
239
|
-
function getHash(algo, ...data) {
|
|
280
|
+
function getHash(algo: string, ...data: (string | Buffer)[]): string {
|
|
240
281
|
var hash = crypto.createHash(algo);
|
|
241
282
|
|
|
242
283
|
for (var d of data) {
|
|
@@ -252,7 +293,7 @@ function getHash(algo, ...data) {
|
|
|
252
293
|
* @param bigInt
|
|
253
294
|
* @returns {*}
|
|
254
295
|
*/
|
|
255
|
-
function toBuffer(bigInt) {
|
|
296
|
+
function toBuffer(bigInt: bigint | string): Buffer {
|
|
256
297
|
return Buffer.from(typeof bigInt === 'bigint' ? hexPad(bigInt.toString(16)) : bigInt, 'hex');
|
|
257
298
|
}
|
|
258
299
|
|
|
@@ -262,7 +303,7 @@ function toBuffer(bigInt) {
|
|
|
262
303
|
* @param hex
|
|
263
304
|
* @returns {BigInt}
|
|
264
305
|
*/
|
|
265
|
-
function toBigInt(hex) {
|
|
306
|
+
function toBigInt(hex: Buffer | string): bigint {
|
|
266
307
|
return BigInt('0x' + (Buffer.isBuffer(hex) ? hex.toString('hex') : hex));
|
|
267
308
|
}
|
|
268
309
|
|
|
@@ -272,7 +313,7 @@ function toBigInt(hex) {
|
|
|
272
313
|
* @param key
|
|
273
314
|
* @param value
|
|
274
315
|
*/
|
|
275
|
-
function dump(key, value) {
|
|
316
|
+
function dump(key: string, value: any): void {
|
|
276
317
|
if (DEBUG) {
|
|
277
318
|
if (typeof value === 'bigint') {
|
|
278
319
|
value = value.toString(16);
|
|
@@ -285,7 +326,7 @@ function dump(key, value) {
|
|
|
285
326
|
/**
|
|
286
327
|
* Calculates (base ^ exp) % mod using native BigInt.
|
|
287
328
|
*/
|
|
288
|
-
function modPow(base, exp, mod) {
|
|
329
|
+
function modPow(base: bigint, exp: bigint, mod: bigint): bigint {
|
|
289
330
|
let result = 1n;
|
|
290
331
|
base = base % mod;
|
|
291
332
|
while (exp > 0n) {
|