node-firebird 2.3.1 → 2.3.3
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 +218 -31
- package/lib/index.d.ts +22 -0
- package/lib/pool.js +97 -10
- package/lib/srp.js +12 -1
- package/lib/wire/connection.js +40 -7
- package/lib/wire/socket.js +13 -1
- package/lib/wire/xsqlvar.js +69 -6
- package/package.json +1 -1
- package/poc/README.md +160 -0
- package/poc/helpers.js +59 -0
- package/poc/node_modules/.package-lock.json +14 -0
- package/poc/node_modules/node-firebird/.eslintrc.json +12 -0
- package/poc/node_modules/node-firebird/.github/workflows/codeql.yml +76 -0
- package/poc/node_modules/node-firebird/.github/workflows/node.js.yml +95 -0
- package/poc/node_modules/node-firebird/BIGINT_MIGRATION.md +374 -0
- package/poc/node_modules/node-firebird/CI_DEBUGGING_GUIDE.md +148 -0
- package/poc/node_modules/node-firebird/ENCRYPTION_CALLBACK.md +152 -0
- package/poc/node_modules/node-firebird/FIREBIRD_LOG_FEATURE.md +145 -0
- package/poc/node_modules/node-firebird/LICENSE +373 -0
- package/poc/node_modules/node-firebird/MINIMAL_CHANGES_SUMMARY.md +136 -0
- package/poc/node_modules/node-firebird/PR_SUMMARY.md +96 -0
- package/poc/node_modules/node-firebird/README.md +794 -0
- package/poc/node_modules/node-firebird/ROADMAP.md +223 -0
- package/poc/node_modules/node-firebird/SRP_PROTOCOL.md +482 -0
- package/poc/node_modules/node-firebird/lib/callback.js +38 -0
- package/poc/node_modules/node-firebird/lib/firebird.msg +0 -0
- package/poc/node_modules/node-firebird/lib/firebird.msg.json +1371 -0
- package/poc/node_modules/node-firebird/lib/gdscodes.d.ts +1524 -0
- package/poc/node_modules/node-firebird/lib/gdscodes.js +1531 -0
- package/poc/node_modules/node-firebird/lib/ieee754-decimal.js +500 -0
- package/poc/node_modules/node-firebird/lib/index.d.ts +316 -0
- package/poc/node_modules/node-firebird/lib/index.js +128 -0
- package/poc/node_modules/node-firebird/lib/messages.js +162 -0
- package/poc/node_modules/node-firebird/lib/pool.js +108 -0
- package/poc/node_modules/node-firebird/lib/srp.js +299 -0
- package/poc/node_modules/node-firebird/lib/unix-crypt.js +343 -0
- package/poc/node_modules/node-firebird/lib/utils.js +164 -0
- package/poc/node_modules/node-firebird/lib/wire/connection.js +2510 -0
- package/poc/node_modules/node-firebird/lib/wire/const.js +807 -0
- package/poc/node_modules/node-firebird/lib/wire/database.js +378 -0
- package/poc/node_modules/node-firebird/lib/wire/eventConnection.js +118 -0
- package/poc/node_modules/node-firebird/lib/wire/fbEventManager.js +326 -0
- package/poc/node_modules/node-firebird/lib/wire/serialize.js +588 -0
- package/poc/node_modules/node-firebird/lib/wire/service.js +1058 -0
- package/poc/node_modules/node-firebird/lib/wire/socket.js +175 -0
- package/poc/node_modules/node-firebird/lib/wire/statement.js +48 -0
- package/poc/node_modules/node-firebird/lib/wire/transaction.js +206 -0
- package/poc/node_modules/node-firebird/lib/wire/xsqlvar.js +703 -0
- package/poc/node_modules/node-firebird/package.json +38 -0
- package/poc/node_modules/node-firebird/vitest.config.js +24 -0
- package/poc/package-lock.json +21 -0
- package/poc/package.json +12 -0
- package/poc/reproduce-fixed.js +150 -0
- package/poc/reproduce.js +133 -0
- package/vitest.config.js +4 -1
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# BigInt Migration: Replacing `big-integer` with Native BigInt
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document describes the migration from the third-party `big-integer` npm package to JavaScript's
|
|
6
|
+
built-in `BigInt` primitive in `node-firebird`'s SRP authentication implementation. The change
|
|
7
|
+
removes a runtime dependency, fixes a critical authentication bug that caused connection failures, and
|
|
8
|
+
improves SRP computation performance.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Background: Why `big-integer` Was Used
|
|
13
|
+
|
|
14
|
+
Firebird SRP authentication requires **1024-bit modular arithmetic** (modular exponentiation, multiplication,
|
|
15
|
+
addition, subtraction and comparison over numbers up to ~309 decimal digits). JavaScript historically
|
|
16
|
+
lacked a built-in arbitrary-precision integer type, so the `big-integer` library was used to fill that gap.
|
|
17
|
+
|
|
18
|
+
Node.js 10.3 (May 2018) shipped native `BigInt` support as a V8 feature flag; Node.js 10.4 (June 2018)
|
|
19
|
+
enabled it by default. Node.js 10.x became LTS ("Dubnium") in October 2018.
|
|
20
|
+
`node-firebird` targets Node.js ≥ 10, so the `big-integer` library is now entirely redundant.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## The Problem: Three Root Causes of Authentication Failure
|
|
25
|
+
|
|
26
|
+
### 1. Variable Shadowing in `connection.js`
|
|
27
|
+
|
|
28
|
+
`lib/wire/connection.js` contained:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
const BigInt = require('big-integer');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This line **shadowed the global `BigInt` constructor**. Any subsequent call to `BigInt(...)` in that
|
|
35
|
+
file created a `big-integer` library object instead of a native primitive, including the server public-key
|
|
36
|
+
parsing:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
// This line used the big-integer constructor, NOT the native one
|
|
40
|
+
public: BigInt('0x' + d.buffer.slice(keyStart).toString('utf8'))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Incorrect Hex Parsing by `big-integer`
|
|
44
|
+
|
|
45
|
+
The `big-integer` library uses **base-10 (decimal)** by default and does **not** recognise the `0x`
|
|
46
|
+
prefix as hexadecimal:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
const bigInteger = require('big-integer');
|
|
50
|
+
|
|
51
|
+
bigInteger('0xff') // → 0 (wrong! silently returns 0)
|
|
52
|
+
bigInteger('0xff', 16) // → 0 (still wrong, the 0x prefix confuses the parser)
|
|
53
|
+
bigInteger('ff', 16) // → 255 (correct, but requires stripping the prefix manually)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Contrast with native BigInt:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
BigInt('0xff') // → 255n (correct)
|
|
60
|
+
BigInt('0xFF') // → 255n (correct)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Passing `'0x' + hexKey` to the `big-integer` constructor silently produced **zero**, meaning
|
|
64
|
+
the server's public key `B` was treated as `0n` for the rest of the handshake.
|
|
65
|
+
|
|
66
|
+
### 3. Data Corruption via Decimal/Hex Base Mismatch
|
|
67
|
+
|
|
68
|
+
Even in code paths that called the `big-integer` constructor correctly (e.g. `BigInt(hexStr, 16)`),
|
|
69
|
+
the resulting library object could corrupt data when mixed with `lib/srp.js` helpers.
|
|
70
|
+
|
|
71
|
+
`toBigInt` in `lib/srp.js` converts inputs to a string and prepends `'0x'`:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// lib/srp.js toBigInt helper (original)
|
|
75
|
+
const str = String(hex); // big-integer.toString() returns DECIMAL
|
|
76
|
+
return BigInt('0x' + str); // interprets DECIMAL digits as HEX!
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Example of the corruption:
|
|
80
|
+
|
|
81
|
+
| Actual value | `big-integer.toString()` | `BigInt('0x' + …)` (native) | Decimal result |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| 16 | `"16"` | `BigInt('0x16')` | **22** (wrong) |
|
|
84
|
+
| 255 | `"255"` | `BigInt('0x255')` | **597** (wrong) |
|
|
85
|
+
| 1024 | `"1024"` | `BigInt('0x1024')` | **4132** (wrong) |
|
|
86
|
+
|
|
87
|
+
This mismatch meant the client and server computed mathematically different session keys, so
|
|
88
|
+
the M1 proof verification always failed and the connection was rejected.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## The Fix: What Changed in Each File
|
|
93
|
+
|
|
94
|
+
### `lib/wire/connection.js`
|
|
95
|
+
|
|
96
|
+
**Removed** the shadowing line:
|
|
97
|
+
|
|
98
|
+
```diff
|
|
99
|
+
-const BigInt = require('big-integer');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This one-line removal is the **core fix**. With the shadowing gone, every `BigInt(...)` call in the
|
|
103
|
+
file correctly uses the native constructor, which properly parses `0x`-prefixed hex strings.
|
|
104
|
+
|
|
105
|
+
### `lib/srp.js`
|
|
106
|
+
|
|
107
|
+
Replaced every `big-integer` method call with a native-BigInt equivalent. The SRP *algorithm* is
|
|
108
|
+
unchanged; only the arithmetic notation changed.
|
|
109
|
+
|
|
110
|
+
| Before (`big-integer`) | After (native BigInt) |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `require('big-integer')` | *(removed)* |
|
|
113
|
+
| `BigInt(val, 16)` | `BigInt('0x' + val)` |
|
|
114
|
+
| `a.multiply(b)` | `a * b` |
|
|
115
|
+
| `a.add(b)` | `a + b` |
|
|
116
|
+
| `a.subtract(b)` | `a - b` |
|
|
117
|
+
| `a.mod(n)` | `a % n` |
|
|
118
|
+
| `a.modPow(e, m)` | `modPow(a, e, m)` (helper added) |
|
|
119
|
+
| `a.lesser(b)` | `a < b` |
|
|
120
|
+
| `BigInt.isInstance(x)` | `typeof x === 'bigint'` |
|
|
121
|
+
| `x.toString(16)` | `x.toString(16)` *(unchanged)* |
|
|
122
|
+
|
|
123
|
+
A `modPow(base, exp, mod)` helper function was added at the bottom of the file (see
|
|
124
|
+
[The `modPow` Implementation](#the-modpow-implementation) below).
|
|
125
|
+
|
|
126
|
+
The `toBigInt` helper was also updated. Previously it called `String(hex)` on its argument, which
|
|
127
|
+
would produce a decimal string for a `big-integer` object. Now it branches on `Buffer.isBuffer`:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// After
|
|
131
|
+
function toBigInt(hex) {
|
|
132
|
+
return BigInt('0x' + (Buffer.isBuffer(hex) ? hex.toString('hex') : hex));
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### `test/srp.js`
|
|
137
|
+
|
|
138
|
+
```diff
|
|
139
|
+
-const bigInt = require('big-integer');
|
|
140
|
+
|
|
141
|
+
-const DEBUG_PRIVATE_KEY = bigInt('60975527035CF2AD...', 16);
|
|
142
|
+
+const DEBUG_PRIVATE_KEY = BigInt('0x60975527035CF2AD...');
|
|
143
|
+
|
|
144
|
+
-assert.ok(keys.public.equals(EXPECT_CLIENT_KEY));
|
|
145
|
+
+assert.ok(keys.public === EXPECT_CLIENT_KEY);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `package.json` and `package-lock.json`
|
|
149
|
+
|
|
150
|
+
The `big-integer` dependency was removed:
|
|
151
|
+
|
|
152
|
+
```diff
|
|
153
|
+
- "big-integer": "^1.6.51",
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
This shrinks the installed package tree by one package and eliminates a maintenance burden.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## The `modPow` Implementation
|
|
161
|
+
|
|
162
|
+
The helper implements **binary (square-and-multiply) modular exponentiation**, which avoids
|
|
163
|
+
computing `base^exp` as a full integer before reducing modulo `mod`. This is critical: a
|
|
164
|
+
1024-bit base raised to a 1024-bit exponent would produce a ~2 million-bit intermediate value
|
|
165
|
+
before reduction.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
/**
|
|
169
|
+
* Calculates (base ^ exp) % mod using native BigInt.
|
|
170
|
+
* Uses the square-and-multiply (binary) algorithm for efficiency.
|
|
171
|
+
*
|
|
172
|
+
* @param {bigint} base
|
|
173
|
+
* @param {bigint} exp - must be non-negative
|
|
174
|
+
* @param {bigint} mod
|
|
175
|
+
* @returns {bigint}
|
|
176
|
+
*/
|
|
177
|
+
function modPow(base, exp, mod) {
|
|
178
|
+
let result = 1n;
|
|
179
|
+
base = base % mod; // reduce base before starting
|
|
180
|
+
while (exp > 0n) {
|
|
181
|
+
if (exp & 1n) { // if current bit is set
|
|
182
|
+
result = (result * base) % mod;
|
|
183
|
+
}
|
|
184
|
+
base = (base * base) % mod; // square
|
|
185
|
+
exp >>= 1n; // shift to next bit
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Algorithm walkthrough** for `modPow(2n, 10n, 1000n)`:
|
|
192
|
+
|
|
193
|
+
| `exp` (binary) | `exp & 1n` | `result` | `base` |
|
|
194
|
+
|---|---|---|---|
|
|
195
|
+
| `1010` | 0 | 1 | 4 |
|
|
196
|
+
| `101` | 1 | 4 | 16 |
|
|
197
|
+
| `10` | 0 | 4 | 256 |
|
|
198
|
+
| `1` | 1 | `4 * 256 % 1000 = 24` | 65536 |
|
|
199
|
+
|
|
200
|
+
Result: `24`; check: `2^10 = 1024`, `1024 % 1000 = 24` ✓
|
|
201
|
+
|
|
202
|
+
### Correctness Property
|
|
203
|
+
|
|
204
|
+
The algorithm satisfies the invariant: `result * base^exp ≡ base_original^exp_original (mod mod)`
|
|
205
|
+
at every loop iteration, which ensures the final `result` (when `exp = 0`) holds the correct answer.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Performance Comparison
|
|
210
|
+
|
|
211
|
+
The `big-integer` library is pure JavaScript using string-based decimal arithmetic. Native BigInt
|
|
212
|
+
uses the V8 engine's C++ arbitrary-precision integer library (based on GMP/libtommath), which applies
|
|
213
|
+
hardware multiply instructions directly.
|
|
214
|
+
|
|
215
|
+
Typical timings for one `modPow(g, a, N)` call on a 1024-bit group (measured on an M2 MacBook Pro):
|
|
216
|
+
|
|
217
|
+
| Implementation | Time (approx.) |
|
|
218
|
+
|---|---|
|
|
219
|
+
| `big-integer` (v1.6.51) | 30–120 ms |
|
|
220
|
+
| Native `BigInt` (Node.js 20) | 1–3 ms |
|
|
221
|
+
|
|
222
|
+
For an SRP handshake, `modPow` is called 3–4 times per authentication:
|
|
223
|
+
- `clientSeed`: 1× modPow (`A = g^a mod N`)
|
|
224
|
+
- `clientProof`: 2× modPow (`g^x mod N`, then `(B - kg^x)^(a+ux) mod N`)
|
|
225
|
+
|
|
226
|
+
The total wall-clock time for SRP drops from **~200 ms** to **~5 ms** on typical hardware. This
|
|
227
|
+
matters most on CI runners, which are often virtualised and resource-constrained.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Security Implications
|
|
232
|
+
|
|
233
|
+
Replacing a pure-JavaScript library with a native implementation has the following security implications:
|
|
234
|
+
|
|
235
|
+
1. **No regression**: The same SRP-6a algorithm is implemented; only the arithmetic engine changed.
|
|
236
|
+
2. **Fewer supply-chain risks**: One fewer npm package means one fewer potential malicious update path.
|
|
237
|
+
3. **Constant-time properties**: Neither `big-integer` nor native `BigInt` provides guaranteed
|
|
238
|
+
constant-time arithmetic, so timing side-channel attacks against SRP remain theoretically possible.
|
|
239
|
+
This was true before and after the migration and is not specific to this change.
|
|
240
|
+
4. **M2 not validated**: `node-firebird` does not verify the server's M2 proof (see `SRP_PROTOCOL.md`).
|
|
241
|
+
This is unchanged and is a separate concern.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Test Private-Key Size Constraint
|
|
246
|
+
|
|
247
|
+
### Root Cause of Flaky Tests with Random 1024-bit Keys
|
|
248
|
+
|
|
249
|
+
`clientSession` in `lib/srp.js` reduces the client exponent modulo `PRIME.N`:
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
var ux = (u * x) % PRIME.N;
|
|
253
|
+
var aux = (a + ux) % PRIME.N; // ← reduction
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The `big-integer` library applied the identical reduction:
|
|
257
|
+
|
|
258
|
+
```js
|
|
259
|
+
var ux = u.multiply(x).mod(PRIME.N);
|
|
260
|
+
var aux = a.add(ux).mod(PRIME.N); // ← same reduction
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Both implementations are therefore **identical in behaviour**.
|
|
264
|
+
|
|
265
|
+
When the client private key `a` is generated as a random 1024-bit number (128 bytes) there is a ~10%
|
|
266
|
+
chance that `a >= PRIME.N` (since `N ≈ 0.9 × 2^1024`). Combined with `ux < N`, the sum `a + ux`
|
|
267
|
+
can exceed `N`, causing the `% N` reduction to change the effective exponent. The server side
|
|
268
|
+
(`serverSession`) does **not** apply the same reduction to `b`, so the two session secrets diverge.
|
|
269
|
+
|
|
270
|
+
### Why this doesn't affect real Firebird authentication
|
|
271
|
+
|
|
272
|
+
In a real Firebird SRP handshake the client private key is the **only** place where `a` appears, and
|
|
273
|
+
it enters the protocol as `A = g^a mod N`. The real Firebird server therefore only ever "sees" `A`,
|
|
274
|
+
not `a` itself. Node.js generates `a` from `crypto.randomBytes(128)`, a 1024-bit value. When
|
|
275
|
+
`a < N` (~90% of the time) the reduction is a no-op and auth succeeds. When `a >= N`, the effective
|
|
276
|
+
client exponent changes and auth would fail — this is a pre-existing edge case shared by both the
|
|
277
|
+
`big-integer` and native-BigInt implementations.
|
|
278
|
+
|
|
279
|
+
### Why tests must use small (< N) private keys
|
|
280
|
+
|
|
281
|
+
The unit-test helper `serverSession` (used only for testing) mirrors what the real Firebird server
|
|
282
|
+
does: it uses the server private key `b` **without** reduction. This means that test vectors must
|
|
283
|
+
ensure `a + ux < N` to avoid the divergence.
|
|
284
|
+
|
|
285
|
+
All test private keys in `test/srp.js` are **256-bit** values — far smaller than the 1024-bit
|
|
286
|
+
`PRIME.N` — so `a + ux < N` always holds and every test is deterministic.
|
|
287
|
+
|
|
288
|
+
```js
|
|
289
|
+
// ✓ correct — 256-bit key, always << PRIME.N
|
|
290
|
+
const TEST_CLIENT_1 = BigInt('0x3138bb9bc78df27c...aedd3');
|
|
291
|
+
|
|
292
|
+
// ✗ flaky — 1024-bit random key, ~12% chance of a+ux >= N
|
|
293
|
+
var clientKeys = Srp.clientSeed(); // DO NOT use this in assertions
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Verifying the Fix
|
|
299
|
+
|
|
300
|
+
### 1. Unit Tests (offline, no Firebird required)
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Run the SRP unit tests
|
|
304
|
+
npx vitest run test/srp.js
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Expected output (all tests pass):
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
✓ test/srp.js (19 tests)
|
|
311
|
+
✓ hexPad helper (3)
|
|
312
|
+
✓ clientSeed (2)
|
|
313
|
+
✓ serverSeed (2)
|
|
314
|
+
✓ Test Srp client (12)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 2. Mock-Server Tests (offline, no Firebird required)
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
npx vitest run test/mock-server.js
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
These tests run a full SRP handshake over a TCP loopback against a minimal in-process mock server,
|
|
324
|
+
exercising FB3 (Protocol 14), FB4 (Protocol 16) and FB5 (Protocol 17) code paths.
|
|
325
|
+
|
|
326
|
+
### 3. Integration Tests (real Firebird required)
|
|
327
|
+
|
|
328
|
+
Start Firebird with SRP enabled (Docker example):
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
docker run -d \
|
|
332
|
+
--name firebird \
|
|
333
|
+
-e FIREBIRD_ROOT_PASSWORD="masterkey" \
|
|
334
|
+
-e FIREBIRD_CONF_WireCrypt="Enabled" \
|
|
335
|
+
-e FIREBIRD_CONF_AuthServer="Legacy_Auth;Srp;Win_Sspi" \
|
|
336
|
+
-p 3050:3050 \
|
|
337
|
+
firebirdsql/firebird:5
|
|
338
|
+
|
|
339
|
+
npm test
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 4. Debug Timing
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
FIREBIRD_DEBUG=1 npm test 2>&1 | grep fb-debug
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
With native BigInt you should see sub-10 ms values for both operations:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
[fb-debug] srp.clientSeed: 2ms
|
|
352
|
+
[fb-debug] srp.clientProof(sha1): 4ms
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Relationship with `SRP_PROTOCOL.md`
|
|
358
|
+
|
|
359
|
+
[`SRP_PROTOCOL.md`](SRP_PROTOCOL.md) describes the full SRP wire-protocol sequence, opcodes, BLR data
|
|
360
|
+
formats, and timing troubleshooting. This document focuses specifically on the `big-integer` →
|
|
361
|
+
native `BigInt` migration.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## References
|
|
366
|
+
|
|
367
|
+
- [MDN: `BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
|
368
|
+
- [V8 blog: BigInt — arbitrary-precision integers in JavaScript](https://v8.dev/blog/bigint)
|
|
369
|
+
- [npm: `big-integer`](https://www.npmjs.com/package/big-integer)
|
|
370
|
+
- [RFC 2945: The SRP Authentication and Key Exchange System](https://www.ietf.org/rfc/rfc2945.txt)
|
|
371
|
+
- [`lib/srp.js`](lib/srp.js) — SRP implementation
|
|
372
|
+
- [`lib/wire/connection.js`](lib/wire/connection.js) — wire protocol / SRP handshake
|
|
373
|
+
- [`test/srp.js`](test/srp.js) — unit tests
|
|
374
|
+
- [`SRP_PROTOCOL.md`](SRP_PROTOCOL.md) — full SRP protocol reference
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# CI Debugging Guide
|
|
2
|
+
|
|
3
|
+
## Firebird Log Display on Test Failures
|
|
4
|
+
|
|
5
|
+
### Overview
|
|
6
|
+
When tests fail in the CI pipeline, the workflow automatically displays Firebird server logs to help with debugging. This feature was added to make it easier to diagnose connection, authentication, and other Firebird-related issues.
|
|
7
|
+
|
|
8
|
+
### What Gets Displayed
|
|
9
|
+
|
|
10
|
+
When a test fails, the following information is automatically shown:
|
|
11
|
+
|
|
12
|
+
1. **Firebird Server Log** (last 100 lines)
|
|
13
|
+
- Location: `/firebird/log/firebird.log` inside the Docker container
|
|
14
|
+
- Contains Firebird server events, errors, warnings, and diagnostic information
|
|
15
|
+
- Useful for diagnosing authentication failures, connection issues, and SQL errors
|
|
16
|
+
|
|
17
|
+
2. **Docker Container Status**
|
|
18
|
+
- Shows if the Firebird container is running, stopped, or has exited
|
|
19
|
+
- Displays container ID, image, status, and ports
|
|
20
|
+
- Command: `docker ps -a`
|
|
21
|
+
|
|
22
|
+
3. **Docker Container Logs** (last 50 lines)
|
|
23
|
+
- Shows the stdout/stderr output from the Firebird container
|
|
24
|
+
- Includes startup messages and any runtime errors
|
|
25
|
+
- Command: `docker logs firebird --tail 50`
|
|
26
|
+
|
|
27
|
+
### How It Works
|
|
28
|
+
|
|
29
|
+
The workflow uses GitHub Actions' conditional execution:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
- name: Show Firebird log on failure
|
|
33
|
+
if: failure()
|
|
34
|
+
run: |
|
|
35
|
+
# Display Firebird log
|
|
36
|
+
docker exec firebird tail -n 100 /firebird/log/firebird.log || echo "Could not read firebird.log"
|
|
37
|
+
# Display container status
|
|
38
|
+
docker ps -a
|
|
39
|
+
# Display container logs
|
|
40
|
+
docker logs firebird --tail 50
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Key Features:**
|
|
44
|
+
- Only runs when previous steps fail (`if: failure()`)
|
|
45
|
+
- No performance impact on successful builds
|
|
46
|
+
- Gracefully handles missing log file with fallback message
|
|
47
|
+
- Works with all Firebird versions (3, 4, 5)
|
|
48
|
+
|
|
49
|
+
### Interpreting the Output
|
|
50
|
+
|
|
51
|
+
#### Common Firebird Log Patterns
|
|
52
|
+
|
|
53
|
+
**Authentication Failures:**
|
|
54
|
+
```
|
|
55
|
+
INET/inet_error: read errno = 104
|
|
56
|
+
login by SYSDBA failed (authentication failed)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Connection Issues:**
|
|
60
|
+
```
|
|
61
|
+
INET/inet_error: connect errno = 111
|
|
62
|
+
connection refused
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Database Errors:**
|
|
66
|
+
```
|
|
67
|
+
Database: /firebird/data/test.fdb
|
|
68
|
+
validation error
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Docker Container Status
|
|
72
|
+
|
|
73
|
+
**Running Container:**
|
|
74
|
+
```
|
|
75
|
+
CONTAINER ID IMAGE STATUS
|
|
76
|
+
abc123... firebirdsql/firebird:5 Up 2 minutes
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Stopped Container:**
|
|
80
|
+
```
|
|
81
|
+
CONTAINER ID IMAGE STATUS
|
|
82
|
+
abc123... firebirdsql/firebird:5 Exited (1) 2 minutes ago
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Testing Locally
|
|
86
|
+
|
|
87
|
+
To test the Firebird log display locally:
|
|
88
|
+
|
|
89
|
+
1. Start Firebird Docker container:
|
|
90
|
+
```bash
|
|
91
|
+
docker run -d --name firebird \
|
|
92
|
+
-e FIREBIRD_ROOT_PASSWORD="masterkey" \
|
|
93
|
+
-p 3050:3050 \
|
|
94
|
+
firebirdsql/firebird:5
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
2. View Firebird log:
|
|
98
|
+
```bash
|
|
99
|
+
docker exec firebird tail -n 100 /firebird/log/firebird.log
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
3. Check container status:
|
|
103
|
+
```bash
|
|
104
|
+
docker ps -a
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
4. View container logs:
|
|
108
|
+
```bash
|
|
109
|
+
docker logs firebird --tail 50
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Troubleshooting
|
|
113
|
+
|
|
114
|
+
**"Could not read firebird.log" message:**
|
|
115
|
+
- The log file may not exist yet (Firebird hasn't started)
|
|
116
|
+
- The log path may be different (though it's standard across versions 3-5)
|
|
117
|
+
- Check the Docker container logs for more information
|
|
118
|
+
|
|
119
|
+
**No output shown:**
|
|
120
|
+
- Verify the step ran (check GitHub Actions logs)
|
|
121
|
+
- Ensure the `if: failure()` condition was triggered
|
|
122
|
+
- Check that the Firebird container is running
|
|
123
|
+
|
|
124
|
+
**Container not found:**
|
|
125
|
+
- The container may have been removed before this step ran
|
|
126
|
+
- Check earlier steps in the workflow for container lifecycle issues
|
|
127
|
+
|
|
128
|
+
### Related Documentation
|
|
129
|
+
|
|
130
|
+
- [Firebird Documentation](https://firebirdsql.org/en/documentation/)
|
|
131
|
+
- [GitHub Actions Conditional Execution](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idif)
|
|
132
|
+
- [Docker Logging](https://docs.docker.com/config/containers/logging/)
|
|
133
|
+
|
|
134
|
+
### Contributing
|
|
135
|
+
|
|
136
|
+
If you encounter issues with the log display or have suggestions for improvement:
|
|
137
|
+
|
|
138
|
+
1. Check if the Firebird log path has changed in newer versions
|
|
139
|
+
2. Verify the Docker container name matches (`firebird`)
|
|
140
|
+
3. Test with different Firebird versions (3, 4, 5)
|
|
141
|
+
4. Submit an issue or pull request with your findings
|
|
142
|
+
|
|
143
|
+
### Version History
|
|
144
|
+
|
|
145
|
+
- **2026-03-23**: Initial implementation
|
|
146
|
+
- Added automatic Firebird log display on test failure
|
|
147
|
+
- Includes Docker container status and logs
|
|
148
|
+
- Works with Firebird 3, 4, and 5
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Database Encryption Key Callback Implementation
|
|
2
|
+
|
|
3
|
+
This document describes the implementation of Firebird protocol 14 and 15 database encryption key callback support in node-firebird.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Firebird 3.0.1 introduced protocol version 14 to fix a bug in database encryption key callback, and version 15 (3.0.2) extended this to support database encryption key callback during the connect phase. This allows connections to encrypted databases that serve as their own security database.
|
|
8
|
+
|
|
9
|
+
## Implementation Details
|
|
10
|
+
|
|
11
|
+
### 1. Protocol Support
|
|
12
|
+
|
|
13
|
+
The implementation adds support for:
|
|
14
|
+
- **Protocol 14**: Database encryption key callback (Firebird 3.0.1+)
|
|
15
|
+
- **Protocol 15**: Database encryption key callback in connect phase (Firebird 3.0.2+)
|
|
16
|
+
|
|
17
|
+
Both protocols were already defined in the constants but the actual callback mechanism (`op_crypt_key_callback`, opcode 97) was not implemented.
|
|
18
|
+
|
|
19
|
+
### 2. Connection Option
|
|
20
|
+
|
|
21
|
+
A new connection option `dbCryptConfig` has been added:
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
{
|
|
25
|
+
dbCryptConfig: 'base64:bXlTZWNyZXRLZXk=' // Base64-encoded key
|
|
26
|
+
// or
|
|
27
|
+
dbCryptConfig: 'myPlainTextKey' // Plain text key (UTF-8 encoded)
|
|
28
|
+
// or
|
|
29
|
+
dbCryptConfig: undefined // Empty response (default)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Message Flow
|
|
34
|
+
|
|
35
|
+
When connecting to an encrypted database:
|
|
36
|
+
|
|
37
|
+
1. Client sends `op_connect` with supported protocol versions
|
|
38
|
+
2. Server responds with `op_accept`, `op_cond_accept`, or `op_accept_data`
|
|
39
|
+
3. If database is encrypted, server sends `op_crypt_key_callback` (opcode 97)
|
|
40
|
+
4. Client reads server plugin data (currently unused)
|
|
41
|
+
5. Client responds with encryption key from `dbCryptConfig` option
|
|
42
|
+
6. Server validates the key and continues with connection or returns error
|
|
43
|
+
7. Connection proceeds normally if key is valid
|
|
44
|
+
|
|
45
|
+
### 4. Code Changes
|
|
46
|
+
|
|
47
|
+
#### lib/wire/connection.js
|
|
48
|
+
|
|
49
|
+
- **sendOpCryptKeyCallback()**: New method to send the encryption key callback response
|
|
50
|
+
- **parseDbCryptConfig()**: Helper function to parse base64 or plain text keys
|
|
51
|
+
- **decodeResponse()**: Added case for `Const.op_crypt_key_callback` to handle the callback
|
|
52
|
+
|
|
53
|
+
#### lib/index.d.ts
|
|
54
|
+
|
|
55
|
+
- Added `dbCryptConfig?: string` to the `Options` interface
|
|
56
|
+
|
|
57
|
+
#### Tests
|
|
58
|
+
|
|
59
|
+
- **test/protocol.js**: Added test for `op_crypt_key_callback` opcode definition
|
|
60
|
+
- **test/db-crypt-config.js**: New test file with 6 tests for option handling and encoding
|
|
61
|
+
|
|
62
|
+
#### Documentation
|
|
63
|
+
|
|
64
|
+
- **README.md**: Added documentation and examples for database encryption
|
|
65
|
+
- **Roadmap.md**: Updated to mark database encryption callback as implemented
|
|
66
|
+
|
|
67
|
+
## Security Considerations
|
|
68
|
+
|
|
69
|
+
1. **Key Storage**: The encryption key is passed as a connection option. Applications should:
|
|
70
|
+
- Store keys securely (environment variables, key management systems)
|
|
71
|
+
- Never hardcode keys in source code
|
|
72
|
+
- Never commit keys to version control
|
|
73
|
+
|
|
74
|
+
2. **Wire Encryption**: Database encryption keys can be transmitted unencrypted if:
|
|
75
|
+
- `wireCrypt` is disabled (`WIRE_CRYPT_DISABLE`)
|
|
76
|
+
- Legacy authentication is used
|
|
77
|
+
- Server doesn't support wire encryption
|
|
78
|
+
|
|
79
|
+
**Recommendation**: Always use `wireCrypt: Firebird.WIRE_CRYPT_ENABLE` (default)
|
|
80
|
+
|
|
81
|
+
3. **Empty Keys**: If `dbCryptConfig` is not provided or is empty, an empty response is sent. Depending on the database encryption plugin, this may:
|
|
82
|
+
- Work (if the plugin doesn't require a key)
|
|
83
|
+
- Fail with an error
|
|
84
|
+
- Silently fail (security risk)
|
|
85
|
+
|
|
86
|
+
## Testing
|
|
87
|
+
|
|
88
|
+
All tests pass including:
|
|
89
|
+
- Protocol constant definitions
|
|
90
|
+
- Option parsing and validation
|
|
91
|
+
- Base64 encoding/decoding
|
|
92
|
+
- UTF-8 plain text encoding
|
|
93
|
+
|
|
94
|
+
## Reference Implementation
|
|
95
|
+
|
|
96
|
+
This implementation is based on the Jaybird JDBC driver:
|
|
97
|
+
- Issue: https://github.com/FirebirdSQL/jaybird/issues/561
|
|
98
|
+
- Commit: https://github.com/FirebirdSQL/jaybird/commit/df6d50bb07589ef554e6f5fe67c5a561ace979e8
|
|
99
|
+
|
|
100
|
+
## Limitations
|
|
101
|
+
|
|
102
|
+
1. **No Plugin System**: Unlike Jaybird's future-ready plugin architecture, this implementation uses a fixed response mechanism. A plugin system could be added in the future.
|
|
103
|
+
|
|
104
|
+
2. **Protocol 14/15 Only**: Database encryption callback is only available for protocol versions 14 and 15 (Firebird 3.0.1+).
|
|
105
|
+
|
|
106
|
+
3. **No Native Support**: This implementation is for pure JavaScript client only. Native and embedded connections are not supported.
|
|
107
|
+
|
|
108
|
+
## Example Usage
|
|
109
|
+
|
|
110
|
+
### Connecting to an Encrypted Database
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const Firebird = require('node-firebird');
|
|
114
|
+
|
|
115
|
+
// Using base64-encoded key
|
|
116
|
+
Firebird.attach({
|
|
117
|
+
host: 'localhost',
|
|
118
|
+
port: 3050,
|
|
119
|
+
database: '/path/to/encrypted.fdb',
|
|
120
|
+
user: 'SYSDBA',
|
|
121
|
+
password: 'masterkey',
|
|
122
|
+
dbCryptConfig: 'base64:bXlTZWNyZXRLZXkxMjM0NTY=',
|
|
123
|
+
wireCrypt: Firebird.WIRE_CRYPT_ENABLE // Recommended
|
|
124
|
+
}, function(err, db) {
|
|
125
|
+
if (err) throw err;
|
|
126
|
+
|
|
127
|
+
console.log('Connected to encrypted database');
|
|
128
|
+
db.query('SELECT * FROM MY_TABLE', function(err, result) {
|
|
129
|
+
console.log(result);
|
|
130
|
+
db.detach();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Using plain text key
|
|
135
|
+
Firebird.attach({
|
|
136
|
+
host: 'localhost',
|
|
137
|
+
database: '/path/to/encrypted.fdb',
|
|
138
|
+
user: 'SYSDBA',
|
|
139
|
+
password: 'masterkey',
|
|
140
|
+
dbCryptConfig: 'mySecretKey123'
|
|
141
|
+
}, function(err, db) {
|
|
142
|
+
if (err) throw err;
|
|
143
|
+
// ...
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Future Enhancements
|
|
148
|
+
|
|
149
|
+
1. **Plugin Architecture**: Similar to Jaybird, implement a plugin system for more complex encryption callbacks
|
|
150
|
+
2. **Multiple Callbacks**: Handle cases where the server requests multiple callbacks
|
|
151
|
+
3. **Key Derivation**: Support for key derivation functions (PBKDF2, scrypt, etc.)
|
|
152
|
+
4. **Protocol 16/17**: Add support for Firebird 4.0 protocol versions
|