tisura 0.0.1-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 ADDED
@@ -0,0 +1,95 @@
1
+ # Sentinel SDK
2
+
3
+ This project contains the code for [tisura](https://www.npmjs.com/package/tisura) npm package.
4
+ It provides modules for parsing network packets, proving statements, managing connections, and handling TLS operations.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install tisura
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Basic Setup
15
+
16
+ ```typescript
17
+ import { Tisura } from 'tisura';
18
+
19
+ const client = new Tisura({
20
+ dbHost: "localhost",
21
+ dbName: "your_database",
22
+ dbUser: "your_username",
23
+ dbPassword: "your_password",
24
+ dbPort: 5432,
25
+ tlsVersion: "1.3"
26
+ });
27
+ ```
28
+
29
+ ### Parsing Network Packets
30
+
31
+ ```typescript
32
+ // Get and parse packets from database
33
+ const parseClient = client.parse;
34
+ const packets = await parseClient.getPackets();
35
+ const appData = await parseClient.getAppData();
36
+ ```
37
+
38
+ ### Creating TLS connection
39
+
40
+ ```typescript
41
+ // Get and parse packets from database
42
+ const tlsClient = client.tls;
43
+ await tlsClient.init();
44
+ const tlsConn = await tlsClient.TlsConnection();
45
+ ```
46
+
47
+ ### Available Modules
48
+
49
+ The SDK consists of several modules:
50
+
51
+ - **Parse**: Network packet parsing and analysis
52
+ - **Prove**: Statement proving and verification (coming soon)
53
+ - **Connect**: Connection management (coming soon)
54
+ - **TLS**: TLS operations with WASM implementation
55
+
56
+ ### Module-specific Imports
57
+
58
+ You can import specific modules directly:
59
+
60
+ ```typescript
61
+ import { ParseClient } from 'tisura/parse';
62
+ // import { ProveClient } from 'tisura/prove'; // Coming soon
63
+ // import { ConnectClient } from 'tisura/connect'; // Coming soon
64
+ import { TLSClient } from 'tisura/tls';
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ The SDK requires the following configuration parameters:
70
+
71
+ ```typescript
72
+ interface TisuraConfig {
73
+ dbHost?: string; // Database host
74
+ dbName?: string; // Database name
75
+ dbUser?: string; // Database username
76
+ dbPassword?: string; // Database password
77
+ dbPort?: number; // Database port
78
+ tlsVersion?: string; // TLS version ("1.2" or "1.3")
79
+ }
80
+ ```
81
+
82
+ ## Development
83
+
84
+ ### Install, build and run tests
85
+
86
+ ```bash
87
+ # Install dependencies
88
+ npm install
89
+
90
+ # Build the package
91
+ npm run build
92
+
93
+ # Run tests
94
+ npm test
95
+ ```
@@ -0,0 +1,6 @@
1
+ import { TisuraBase, TisuraConfig } from "./config";
2
+ import { ParseClient } from "./parse";
3
+ export declare class Tisura extends TisuraBase {
4
+ parse: ParseClient;
5
+ constructor(config: TisuraConfig);
6
+ }
package/dist/client.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Tisura = void 0;
4
+ const config_1 = require("./config");
5
+ const parse_1 = require("./parse");
6
+ // import { ProveClient } from "./prove";
7
+ // import { ConnectClient } from "./connect";
8
+ // import { TLSClient } from "./tls";
9
+ class Tisura extends config_1.TisuraBase {
10
+ parse;
11
+ // public prove: ProveClient;
12
+ // public connect: ConnectClient;
13
+ // public tls: TLSClient;
14
+ constructor(config) {
15
+ super(config);
16
+ this.parse = new parse_1.ParseClient(this.config);
17
+ // this.prove = new ProveClient(this.config);
18
+ // this.connect = new ConnectClient(this.config);
19
+ // this.tls = new TLSClient(this.config);
20
+ }
21
+ }
22
+ exports.Tisura = Tisura;
@@ -0,0 +1,14 @@
1
+ import { Pool } from "pg";
2
+ export interface TisuraConfig {
3
+ dbHost?: string;
4
+ dbName?: string;
5
+ dbUser?: string;
6
+ dbPassword?: string;
7
+ dbPort?: number;
8
+ tlsVersion?: string;
9
+ }
10
+ export declare class TisuraBase {
11
+ protected config: TisuraConfig;
12
+ constructor(config: TisuraConfig);
13
+ protected getPool(): Pool;
14
+ }
package/dist/config.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TisuraBase = void 0;
4
+ const pg_1 = require("pg");
5
+ class TisuraBase {
6
+ config;
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ getPool() {
11
+ return new pg_1.Pool({
12
+ host: this.config.dbHost,
13
+ database: this.config.dbName,
14
+ user: this.config.dbUser,
15
+ password: this.config.dbPassword,
16
+ port: this.config.dbPort,
17
+ });
18
+ }
19
+ }
20
+ exports.TisuraBase = TisuraBase;
@@ -0,0 +1 @@
1
+ export declare function connectToServer(url: string): void;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectToServer = connectToServer;
4
+ function connectToServer(url) {
5
+ console.log(`Connecting to ${url}...`);
6
+ }
@@ -0,0 +1,6 @@
1
+ import { Pool } from "pg";
2
+ import { PacketInfo } from "./types";
3
+ /**
4
+ * Reads rows from the "captures" table and returns an array of PacketInfo.
5
+ */
6
+ export declare function readPacketsFromDb(pool: Pool): Promise<PacketInfo[]>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readPacketsFromDb = readPacketsFromDb;
4
+ /**
5
+ * Reads rows from the "captures" table and returns an array of PacketInfo.
6
+ */
7
+ async function readPacketsFromDb(pool) {
8
+ try {
9
+ // Query the database
10
+ const result = await pool.query(`
11
+ SELECT
12
+ id,
13
+ timestamp,
14
+ source_ip,
15
+ source_port,
16
+ destination_ip,
17
+ destination_port,
18
+ protocol,
19
+ length,
20
+ payload
21
+ FROM captures
22
+ ORDER BY id;
23
+ `);
24
+ // Map each row to a PacketInfo object
25
+ const packets = result.rows.map((row) => {
26
+ // In Node.js, 'timestamp' column often comes as a Date object.
27
+ // Python's row[1].timestamp() returned float seconds from epoch.
28
+ // We'll replicate that by dividing Date.getTime() by 1000.
29
+ const epochSeconds = row.timestamp.getTime() / 1000;
30
+ return {
31
+ timestamp: epochSeconds,
32
+ sourceIp: row.source_ip,
33
+ sourcePort: row.source_port,
34
+ destinationIp: row.destination_ip,
35
+ destinationPort: row.destination_port,
36
+ protocol: row.protocol,
37
+ length: row.length,
38
+ // 'payload' from Postgres may be a Buffer in Node. Convert to Uint8Array.
39
+ payload: new Uint8Array(row.payload),
40
+ };
41
+ });
42
+ return packets;
43
+ }
44
+ finally {
45
+ // Close the pool to free up resources
46
+ await pool.end();
47
+ }
48
+ }
@@ -0,0 +1,7 @@
1
+ import { TisuraBase, TisuraConfig } from "../config";
2
+ import { PacketInfo, ApplicationData } from "./types";
3
+ export declare class ParseClient extends TisuraBase {
4
+ constructor(config: TisuraConfig);
5
+ getPackets(): Promise<PacketInfo[]>;
6
+ getAppData(): Promise<ApplicationData[]>;
7
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseClient = void 0;
4
+ const config_1 = require("../config");
5
+ const parser_1 = require("./parser");
6
+ const db_1 = require("./db");
7
+ class ParseClient extends config_1.TisuraBase {
8
+ constructor(config) {
9
+ super(config);
10
+ }
11
+ async getPackets() {
12
+ const pool = this.getPool();
13
+ const packets = await (0, db_1.readPacketsFromDb)(pool);
14
+ return packets;
15
+ }
16
+ async getAppData() {
17
+ const packets = await this.getPackets();
18
+ const parser = new parser_1.Parser(packets, this.config.tlsVersion);
19
+ const appData = parser.run();
20
+ return appData;
21
+ }
22
+ }
23
+ exports.ParseClient = ParseClient;
@@ -0,0 +1,55 @@
1
+ import { PacketInfo, Stream, TLSRecord, ApplicationData } from "./types";
2
+ export declare class Parser {
3
+ private packets;
4
+ private tlsVersion?;
5
+ streams: Stream[];
6
+ records: TLSRecord[];
7
+ appData: ApplicationData[];
8
+ /**
9
+ * In TLS 1.2, we track a client sequence number for Application Data records
10
+ */
11
+ clientSeqNum: number;
12
+ constructor(packets: PacketInfo[], tlsVersion?: string);
13
+ /**
14
+ * Run the parser, orchestrating the steps:
15
+ * 1) Reassemble TCP streams and parse out TLS records
16
+ * 2) Combine all records into a single list (ordered by time)
17
+ * 3) Filter to find TLS Application Data
18
+ */
19
+ run(): ApplicationData[];
20
+ /**
21
+ * Reassemble TCP packets into streams and parse TLS records simultaneously
22
+ */
23
+ private reassembleAndParse;
24
+ /**
25
+ * Combine all parsed records from the streams into one flat list (sorted by time).
26
+ */
27
+ private combineStreams;
28
+ /**
29
+ * Extract TLS parameters (e.g., random, cipher_suite) from a Handshake record
30
+ * if it's ClientHello or ServerHello.
31
+ *
32
+ * - If ClientHello, returns [clientRandom, null]
33
+ * - If ServerHello, returns [serverRandom, cipherSuiteNumber]
34
+ */
35
+ private extractTlsParams;
36
+ /**
37
+ * Filter out only TLS Application Data records (0x17).
38
+ * Includes logic for TLS 1.2 vs. TLS 1.3 as per the Python version.
39
+ */
40
+ private filterAppData;
41
+ /**
42
+ * Get a directed stream ID from a given packet.
43
+ * Returns a tuple: ((srcIp, srcPort), (dstIp, dstPort))
44
+ * or null if not TCP.
45
+ */
46
+ private getDirectedStreamId;
47
+ /**
48
+ * Convert a StreamID to a string for use as a Map key.
49
+ */
50
+ private streamIdToString;
51
+ /**
52
+ * Concatenate two Uint8Arrays
53
+ */
54
+ private concatUint8Arrays;
55
+ }
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Parser = void 0;
4
+ const HEADER_LENGTH = 5;
5
+ class Parser {
6
+ packets;
7
+ tlsVersion;
8
+ // Internal state
9
+ streams = [];
10
+ records = [];
11
+ appData = [];
12
+ /**
13
+ * In TLS 1.2, we track a client sequence number for Application Data records
14
+ */
15
+ clientSeqNum = 0;
16
+ constructor(packets, tlsVersion) {
17
+ this.packets = packets;
18
+ this.tlsVersion = tlsVersion;
19
+ }
20
+ /**
21
+ * Run the parser, orchestrating the steps:
22
+ * 1) Reassemble TCP streams and parse out TLS records
23
+ * 2) Combine all records into a single list (ordered by time)
24
+ * 3) Filter to find TLS Application Data
25
+ */
26
+ run() {
27
+ this.streams = this.reassembleAndParse();
28
+ this.records = this.combineStreams();
29
+ this.appData = this.filterAppData();
30
+ return this.appData;
31
+ }
32
+ /**
33
+ * Reassemble TCP packets into streams and parse TLS records simultaneously
34
+ */
35
+ reassembleAndParse() {
36
+ // Use a Map keyed by the stream ID stringified.
37
+ // (Alternatively, you could store by the actual tuple, but strings are simpler.)
38
+ const partialRecords = new Map();
39
+ // Map of streamIDString -> Stream
40
+ const streamsMap = new Map();
41
+ for (const pkt of this.packets) {
42
+ if (pkt.protocol !== "TCP") {
43
+ continue;
44
+ }
45
+ const streamId = this.getDirectedStreamId(pkt);
46
+ if (!streamId) {
47
+ continue;
48
+ }
49
+ const streamIdKey = this.streamIdToString(streamId);
50
+ if (!partialRecords.has(streamIdKey)) {
51
+ partialRecords.set(streamIdKey, {
52
+ data: new Uint8Array(0),
53
+ firstTime: null,
54
+ lastTime: null,
55
+ neededLength: null,
56
+ });
57
+ }
58
+ if (!streamsMap.has(streamIdKey)) {
59
+ streamsMap.set(streamIdKey, { records: [], streamId });
60
+ }
61
+ const partial = partialRecords.get(streamIdKey);
62
+ const pktTime = pkt.timestamp;
63
+ // Append new data
64
+ partial.data = this.concatUint8Arrays(partial.data, pkt.payload);
65
+ partial.lastTime = pktTime;
66
+ if (partial.firstTime === null) {
67
+ partial.firstTime = pktTime;
68
+ }
69
+ // Process complete records
70
+ // We need at least 5 bytes to parse the record header (TLS).
71
+ while (partial.data.length >= HEADER_LENGTH) {
72
+ // If we haven't identified the record length yet
73
+ if (partial.neededLength === null) {
74
+ const contentType = partial.data[0];
75
+ // The total TLS record length is in bytes 3-4 (big-endian).
76
+ const length = (partial.data[3] << 8) | partial.data[4];
77
+ partial.neededLength = HEADER_LENGTH + length; // header (5) + payload
78
+ }
79
+ // If we have a complete record
80
+ if (partial.data.length >= partial.neededLength) {
81
+ const recordData = partial.data.slice(0, partial.neededLength);
82
+ const contentType = recordData[0];
83
+ const record = {
84
+ fullRecord: recordData,
85
+ contentType,
86
+ streamId,
87
+ firstPacketTime: partial.firstTime,
88
+ lastPacketTime: partial.lastTime,
89
+ };
90
+ if (contentType === 0x16) {
91
+ // Handshake
92
+ const handshakeType = recordData.length > 5 ? recordData[5] : null;
93
+ console.log(`[PARSER] Found handshake type: ${handshakeType}`);
94
+ if (handshakeType === 2) {
95
+ // ServerHello
96
+ console.log(`[PARSER] ServerHello in stream ${this.streamIdToString(streamId)}`);
97
+ }
98
+ }
99
+ console.log(`[PARSER] New record: type=0x${contentType.toString(16).padStart(2, "0")}, stream=${this.streamIdToString(streamId)}`);
100
+ console.log(`[PARSER] Time span: ${partial.firstTime} -> ${partial.lastTime}`);
101
+ // Add record to the corresponding Stream
102
+ const streamObj = streamsMap.get(streamIdKey);
103
+ streamObj.records.push(record);
104
+ // Remove this record's data
105
+ partial.data = partial.data.slice(partial.neededLength);
106
+ // Reset neededLength
107
+ partial.neededLength = null;
108
+ // Reset first_time to the last_time for the next record
109
+ partial.firstTime = partial.lastTime;
110
+ }
111
+ else {
112
+ // Need more data for this record
113
+ break;
114
+ }
115
+ }
116
+ }
117
+ // Return an array of Streams
118
+ return Array.from(streamsMap.values());
119
+ }
120
+ /**
121
+ * Combine all parsed records from the streams into one flat list (sorted by time).
122
+ */
123
+ combineStreams() {
124
+ const combinedRecords = this.streams.flatMap((stream) => stream.records);
125
+ // Sort by last_packet_time
126
+ combinedRecords.sort((a, b) => a.lastPacketTime - b.lastPacketTime);
127
+ return combinedRecords;
128
+ }
129
+ /**
130
+ * Extract TLS parameters (e.g., random, cipher_suite) from a Handshake record
131
+ * if it's ClientHello or ServerHello.
132
+ *
133
+ * - If ClientHello, returns [clientRandom, null]
134
+ * - If ServerHello, returns [serverRandom, cipherSuiteNumber]
135
+ */
136
+ extractTlsParams(handshakeRecord) {
137
+ // Handshake layer starts after the first 5 bytes (TLS header)
138
+ const hsData = handshakeRecord.slice(5);
139
+ const hsType = hsData[0];
140
+ const hsLength = (hsData[1] << 16) | (hsData[2] << 8) | hsData[3];
141
+ const body = hsData.slice(4, 4 + hsLength);
142
+ // Typically 32 bytes of random data
143
+ const randomSize = 32;
144
+ const random = body.slice(2, 2 + randomSize);
145
+ if (hsType === 0x01) {
146
+ // ClientHello
147
+ return [random, null];
148
+ }
149
+ else if (hsType === 0x02) {
150
+ // ServerHello
151
+ const sessionIdLength = body[34];
152
+ const start = 35 + sessionIdLength;
153
+ const end = start + 2;
154
+ const cipherSuite = (body[start] << 8) | body[start + 1];
155
+ return [random, cipherSuite];
156
+ }
157
+ // If not recognized, return defaults
158
+ return [new Uint8Array(0), null];
159
+ }
160
+ /**
161
+ * Filter out only TLS Application Data records (0x17).
162
+ * Includes logic for TLS 1.2 vs. TLS 1.3 as per the Python version.
163
+ */
164
+ filterAppData() {
165
+ console.log(`[PARSER] Filtering ${this.records.length} TLS records for application data`);
166
+ const appData = [];
167
+ // ------------------------------------
168
+ // COMMON STATE
169
+ // ------------------------------------
170
+ let clientPort = null;
171
+ let clientToServerStreamId = null;
172
+ let tlsParams = {
173
+ clientRandom: new Uint8Array(0),
174
+ serverRandom: new Uint8Array(0),
175
+ cipherSuite: 0,
176
+ };
177
+ // TLS 1.2 tracking
178
+ let clientCcsSent = false;
179
+ let saveClientAppData = false;
180
+ // TLS 1.3 tracking
181
+ let clientAppDataAfterKeyUpdate = false;
182
+ this.clientSeqNum = 0;
183
+ for (let i = 0; i < this.records.length; i++) {
184
+ const record = this.records[i];
185
+ let isClient = false;
186
+ if (clientPort !== null) {
187
+ // If the source port matches the known client port
188
+ isClient = record.streamId[0][1] === clientPort;
189
+ }
190
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191
+ // TLS 1.2 LOGIC
192
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193
+ if (this.tlsVersion === "1.2") {
194
+ if (record.contentType === 0x16) {
195
+ // Handshake
196
+ const payload = record.fullRecord.slice(5);
197
+ if (payload.length === 0) {
198
+ continue;
199
+ }
200
+ const hsType = payload[0];
201
+ if (hsType === 0x01) {
202
+ // ClientHello
203
+ clientPort = record.streamId[0][1];
204
+ clientToServerStreamId = record.streamId;
205
+ saveClientAppData = false;
206
+ console.log(`[TLS 1.2] Found ClientHello from client port ${clientPort} at record #${i}`);
207
+ const [clientRandom, _] = this.extractTlsParams(record.fullRecord);
208
+ tlsParams = {
209
+ clientRandom,
210
+ serverRandom: new Uint8Array(0),
211
+ cipherSuite: 0,
212
+ };
213
+ }
214
+ else if (hsType === 0x02) {
215
+ // ServerHello
216
+ const [serverRandom, cipherSuite] = this.extractTlsParams(record.fullRecord);
217
+ tlsParams = {
218
+ clientRandom: tlsParams.clientRandom,
219
+ serverRandom,
220
+ cipherSuite: cipherSuite || 0,
221
+ };
222
+ }
223
+ else {
224
+ // Possibly an encrypted handshake message (e.g. Finished)
225
+ if (isClient && clientCcsSent && !saveClientAppData) {
226
+ // The next handshake record from client after CCS is the client Finished
227
+ saveClientAppData = true;
228
+ clientCcsSent = false;
229
+ this.clientSeqNum = 0;
230
+ console.log(`[TLS 1.2] Detected Client Finished (encrypted) at record #${i}. Now capturing client app data.`);
231
+ }
232
+ }
233
+ }
234
+ else if (record.contentType === 0x14) {
235
+ // ChangeCipherSpec (TLS 1.2)
236
+ if (isClient) {
237
+ clientCcsSent = true;
238
+ console.log(`[TLS 1.2] Client CCS at record #${i} => waiting for client Finished.`);
239
+ }
240
+ }
241
+ else if (record.contentType === 0x17) {
242
+ // Application Data
243
+ if (isClient && saveClientAppData) {
244
+ console.log(`[TLS 1.2] Capturing client app data at record #${i}`);
245
+ // client→server app data
246
+ this.clientSeqNum += 1;
247
+ appData.push({
248
+ tlsRecord: record,
249
+ seqNum: this.clientSeqNum,
250
+ tlsParams,
251
+ });
252
+ }
253
+ }
254
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
255
+ // TLS 1.3 LOGIC
256
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257
+ }
258
+ else if (this.tlsVersion == "1.3") {
259
+ if (record.contentType === 0x16) {
260
+ // Handshake
261
+ const payload = record.fullRecord.slice(5);
262
+ if (payload.length === 0)
263
+ continue;
264
+ const hsType = payload[0];
265
+ if (hsType === 0x01) {
266
+ // ClientHello
267
+ clientPort = record.streamId[0][1];
268
+ clientToServerStreamId = record.streamId;
269
+ }
270
+ }
271
+ else if (record.contentType === 0x14) {
272
+ // In TLS 1.3, 0x14 can be KeyUpdate or a dummy CCS
273
+ const payload = record.fullRecord.slice(5);
274
+ if (isClient && payload.length > 0 && payload[0] === 0x01) {
275
+ // Suppose this indicates a KeyUpdate from client
276
+ clientAppDataAfterKeyUpdate = true;
277
+ saveClientAppData = false;
278
+ this.clientSeqNum = 0;
279
+ }
280
+ }
281
+ else if (record.contentType === 0x17) {
282
+ // Application Data in TLS 1.3
283
+ const isClientToServer = clientToServerStreamId &&
284
+ record.streamId[0][0] === clientToServerStreamId[0][0] &&
285
+ record.streamId[0][1] === clientToServerStreamId[0][1] &&
286
+ record.streamId[1][0] === clientToServerStreamId[1][0] &&
287
+ record.streamId[1][1] === clientToServerStreamId[1][1];
288
+ if (isClientToServer && clientAppDataAfterKeyUpdate) {
289
+ if (saveClientAppData) {
290
+ appData.push({
291
+ tlsRecord: record,
292
+ seqNum: this.clientSeqNum,
293
+ tlsParams: {
294
+ clientRandom: new Uint8Array(0),
295
+ serverRandom: new Uint8Array(0),
296
+ cipherSuite: 0,
297
+ },
298
+ });
299
+ this.clientSeqNum++;
300
+ }
301
+ else {
302
+ saveClientAppData = true;
303
+ continue;
304
+ }
305
+ }
306
+ }
307
+ }
308
+ else {
309
+ console.log("[PARSER] Error: `tlsVersion` should be either 1.2 or 1.3.");
310
+ }
311
+ }
312
+ // Warn if we never identified a client port
313
+ if (clientPort === null) {
314
+ console.log("[PARSER] Warning: Could not identify client port");
315
+ }
316
+ return appData;
317
+ }
318
+ /**
319
+ * Get a directed stream ID from a given packet.
320
+ * Returns a tuple: ((srcIp, srcPort), (dstIp, dstPort))
321
+ * or null if not TCP.
322
+ */
323
+ getDirectedStreamId(pkt) {
324
+ if (pkt.protocol === "TCP") {
325
+ const src = [pkt.sourceIp, pkt.sourcePort];
326
+ const dst = [pkt.destinationIp, pkt.destinationPort];
327
+ return [src, dst];
328
+ }
329
+ return null;
330
+ }
331
+ /**
332
+ * Convert a StreamID to a string for use as a Map key.
333
+ */
334
+ streamIdToString(streamId) {
335
+ const [[srcIp, srcPort], [dstIp, dstPort]] = streamId;
336
+ return `${srcIp}:${srcPort}->${dstIp}:${dstPort}`;
337
+ }
338
+ /**
339
+ * Concatenate two Uint8Arrays
340
+ */
341
+ concatUint8Arrays(a, b) {
342
+ const c = new Uint8Array(a.length + b.length);
343
+ c.set(a, 0);
344
+ c.set(b, a.length);
345
+ return c;
346
+ }
347
+ }
348
+ exports.Parser = Parser;