node-firebird 2.8.0 → 2.8.1

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.
Files changed (3) hide show
  1. package/lib/srp.js +17 -4
  2. package/package.json +1 -1
  3. package/src/srp.ts +18 -5
package/lib/srp.js CHANGED
@@ -117,12 +117,16 @@ function clientProof(user, password, salt, A, B, a, hashAlgo = 'sha1') {
117
117
  dump('n2', n2);
118
118
  n1 = modPow(n1, n2, PRIME.N);
119
119
  n2 = toBigInt(getHash('sha1', user));
120
- var M = toBigInt(getHash(hashAlgo, toBuffer(n1), toBuffer(n2), salt, toBuffer(A), toBuffer(B), toBuffer(K)));
120
+ // K is hashed as the raw fixed-length digest, exactly like the server's
121
+ // digest.process(sessionKey) over a 20-byte UCharBuffer. Converting it
122
+ // through bigint dropped a leading zero byte (~0.4% of connections) and
123
+ // broke the proof (issue #421).
124
+ var M = toBigInt(getHash(hashAlgo, toBuffer(n1), toBuffer(n2), salt, toBuffer(A), toBuffer(B), K));
121
125
  dump('n1-2', n1);
122
126
  dump('n2-2', n2);
123
127
  dump('proof:M', M);
124
128
  return {
125
- clientSessionKey: K,
129
+ clientSessionKey: toBigInt(K),
126
130
  authData: M,
127
131
  };
128
132
  }
@@ -162,12 +166,19 @@ function pad(n) {
162
166
  /**
163
167
  * Scramble keys.
164
168
  *
169
+ * The server hashes the minimal (stripped) magnitude bytes of A and B
170
+ * (RemotePassword::computeScramble → processStrippedInt in Firebird's
171
+ * srp.cpp, identical in 3.0 through master) — NOT the 128-byte padded
172
+ * form, which the engine only uses for k = H(N, pad(g)). Padding here
173
+ * made u diverge whenever A or B had a leading zero byte (~0.8% of
174
+ * connections), failing the proof (issue #421).
175
+ *
165
176
  * @param A BigInt Client public key.
166
177
  * @param B BigInt Server public key.
167
178
  * @returns {BigInt}
168
179
  */
169
180
  function getScramble(A, B, hashAlgo = 'sha1') {
170
- return BigInt('0x' + getHash(hashAlgo, pad(A), pad(B)));
181
+ return BigInt('0x' + getHash(hashAlgo, toBuffer(A), toBuffer(B)));
171
182
  }
172
183
  /**
173
184
  * Client session secret.
@@ -183,6 +194,8 @@ function getScramble(A, B, hashAlgo = 'sha1') {
183
194
  * @param A BigInt Client public key.
184
195
  * @param B BigInt Server public key.
185
196
  * @param a BigInt Client private key.
197
+ * @returns Buffer The raw session-key digest (fixed length, may start
198
+ * with a zero byte — significant for the proof).
186
199
  */
187
200
  function clientSession(user, password, salt, A, B, a, hashAlgo = 'sha1') {
188
201
  var u = getScramble(A, B, 'sha1');
@@ -200,7 +213,7 @@ function clientSession(user, password, salt, A, B, a, hashAlgo = 'sha1') {
200
213
  var ux = (u * x) % PRIME.N;
201
214
  var aux = (a + ux) % PRIME.N;
202
215
  var sessionSecret = modPow(diff, aux, PRIME.N);
203
- var K = toBigInt(getHash('sha1', toBuffer(sessionSecret)));
216
+ var K = Buffer.from(getHash('sha1', toBuffer(sessionSecret)), 'hex');
204
217
  dump('B', B);
205
218
  dump('u', u);
206
219
  dump('x', x);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
package/src/srp.ts CHANGED
@@ -133,14 +133,18 @@ export function clientProof(user: string, password: string, salt: Buffer, A: big
133
133
 
134
134
  n1 = modPow(n1, n2, PRIME.N);
135
135
  n2 = toBigInt(getHash('sha1', user));
136
- var M = toBigInt(getHash(hashAlgo, toBuffer(n1), toBuffer(n2), salt, toBuffer(A), toBuffer(B), toBuffer(K)));
136
+ // K is hashed as the raw fixed-length digest, exactly like the server's
137
+ // digest.process(sessionKey) over a 20-byte UCharBuffer. Converting it
138
+ // through bigint dropped a leading zero byte (~0.4% of connections) and
139
+ // broke the proof (issue #421).
140
+ var M = toBigInt(getHash(hashAlgo, toBuffer(n1), toBuffer(n2), salt, toBuffer(A), toBuffer(B), K));
137
141
 
138
142
  dump('n1-2', n1);
139
143
  dump('n2-2', n2);
140
144
  dump('proof:M', M);
141
145
 
142
146
  return {
143
- clientSessionKey: K,
147
+ clientSessionKey: toBigInt(K),
144
148
  authData: M,
145
149
  };
146
150
  }
@@ -186,12 +190,19 @@ function pad(n: bigint): Buffer {
186
190
  /**
187
191
  * Scramble keys.
188
192
  *
193
+ * The server hashes the minimal (stripped) magnitude bytes of A and B
194
+ * (RemotePassword::computeScramble → processStrippedInt in Firebird's
195
+ * srp.cpp, identical in 3.0 through master) — NOT the 128-byte padded
196
+ * form, which the engine only uses for k = H(N, pad(g)). Padding here
197
+ * made u diverge whenever A or B had a leading zero byte (~0.8% of
198
+ * connections), failing the proof (issue #421).
199
+ *
189
200
  * @param A BigInt Client public key.
190
201
  * @param B BigInt Server public key.
191
202
  * @returns {BigInt}
192
203
  */
193
204
  function getScramble(A: bigint, B: bigint, hashAlgo: string = 'sha1'): bigint {
194
- return BigInt('0x' + getHash(hashAlgo, pad(A), pad(B)));
205
+ return BigInt('0x' + getHash(hashAlgo, toBuffer(A), toBuffer(B)));
195
206
  }
196
207
 
197
208
  /**
@@ -208,8 +219,10 @@ function getScramble(A: bigint, B: bigint, hashAlgo: string = 'sha1'): bigint {
208
219
  * @param A BigInt Client public key.
209
220
  * @param B BigInt Server public key.
210
221
  * @param a BigInt Client private key.
222
+ * @returns Buffer The raw session-key digest (fixed length, may start
223
+ * with a zero byte — significant for the proof).
211
224
  */
212
- function clientSession(user: string, password: string, salt: Buffer, A: bigint, B: bigint, a: bigint, hashAlgo: string = 'sha1'): bigint {
225
+ function clientSession(user: string, password: string, salt: Buffer, A: bigint, B: bigint, a: bigint, hashAlgo: string = 'sha1'): Buffer {
213
226
  var u = getScramble(A, B, 'sha1');
214
227
  var x = getUserHash(user, salt, password, 'sha1');
215
228
  var gx = modPow(PRIME.g, x, PRIME.N);
@@ -227,7 +240,7 @@ function clientSession(user: string, password: string, salt: Buffer, A: bigint,
227
240
  var ux = (u * x) % PRIME.N;
228
241
  var aux = (a + ux) % PRIME.N;
229
242
  var sessionSecret = modPow(diff, aux, PRIME.N);
230
- var K = toBigInt(getHash('sha1', toBuffer(sessionSecret)));
243
+ var K = Buffer.from(getHash('sha1', toBuffer(sessionSecret)), 'hex');
231
244
 
232
245
  dump('B', B);
233
246
  dump('u', u);