@trust-proto/auth-node 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/dist/index.js ADDED
@@ -0,0 +1,1283 @@
1
+ // src/liberion-auth.ts
2
+ import { v1 as uuid, validate } from "uuid";
3
+ import WebSocket2, { WebSocketServer } from "ws";
4
+ import https from "https";
5
+ import http from "http";
6
+ import { encode as encode2, decode as decode2 } from "@msgpack/msgpack";
7
+
8
+ // src/adapters/logger.ts
9
+ var NoOpLogger = class {
10
+ info() {
11
+ }
12
+ warn() {
13
+ }
14
+ error() {
15
+ }
16
+ };
17
+ var ConsoleLogger = class {
18
+ prefix;
19
+ constructor(prefix = "[LiberionAuth]") {
20
+ this.prefix = prefix;
21
+ }
22
+ info(message, meta) {
23
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
24
+ if (meta && Object.keys(meta).length > 0) {
25
+ console.log(`${timestamp} ${this.prefix} INFO: ${message}`, meta);
26
+ } else {
27
+ console.log(`${timestamp} ${this.prefix} INFO: ${message}`);
28
+ }
29
+ }
30
+ warn(message, meta) {
31
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
32
+ if (meta && Object.keys(meta).length > 0) {
33
+ console.warn(`${timestamp} ${this.prefix} WARN: ${message}`, meta);
34
+ } else {
35
+ console.warn(`${timestamp} ${this.prefix} WARN: ${message}`);
36
+ }
37
+ }
38
+ error(message, meta) {
39
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
40
+ if (meta && Object.keys(meta).length > 0) {
41
+ console.error(`${timestamp} ${this.prefix} ERROR: ${message}`, meta);
42
+ } else {
43
+ console.error(`${timestamp} ${this.prefix} ERROR: ${message}`);
44
+ }
45
+ }
46
+ };
47
+ function createWinstonAdapter(winston) {
48
+ return {
49
+ info: (msg, meta) => winston.info(msg, meta),
50
+ warn: (msg, meta) => winston.warn(msg, meta),
51
+ error: (msg, meta) => winston.error(msg, meta)
52
+ };
53
+ }
54
+ function shortenAddress(address) {
55
+ if (!address || address.length < 10) return address ?? "";
56
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
57
+ }
58
+
59
+ // src/crypto/aes.ts
60
+ import crypto from "crypto";
61
+ var AES_CIPHER = "aes-256-cbc";
62
+ function encryptBuffer(buffer, key) {
63
+ const hashedKey = crypto.createHash("sha256").update(key).digest("hex").substring(0, 32);
64
+ const iv = crypto.randomBytes(16);
65
+ const cipher = crypto.createCipheriv(
66
+ AES_CIPHER,
67
+ Buffer.from(hashedKey),
68
+ iv
69
+ );
70
+ const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
71
+ return Buffer.concat([iv, encrypted]);
72
+ }
73
+ function decryptBuffer(data, key) {
74
+ const hashedKey = crypto.createHash("sha256").update(key).digest("hex").substring(0, 32);
75
+ const iv = data.subarray(0, 16);
76
+ const encryptedData = data.subarray(16);
77
+ const decipher = crypto.createDecipheriv(AES_CIPHER, hashedKey, iv);
78
+ return Buffer.concat([decipher.update(encryptedData), decipher.final()]);
79
+ }
80
+
81
+ // src/crypto/signature.ts
82
+ import { ml_dsa87 } from "@noble/post-quantum/ml-dsa.js";
83
+ function normalizeToBytes(data) {
84
+ if (data instanceof Uint8Array) {
85
+ return data;
86
+ }
87
+ if (Buffer.isBuffer(data)) {
88
+ return new Uint8Array(data);
89
+ }
90
+ if (data instanceof ArrayBuffer) {
91
+ return new Uint8Array(data);
92
+ }
93
+ if (typeof data === "string") {
94
+ if (data.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(data)) {
95
+ return new Uint8Array(Buffer.from(data, "hex"));
96
+ }
97
+ return new Uint8Array(Buffer.from(data, "utf8"));
98
+ }
99
+ throw new Error(`Invalid data type: ${typeof data}`);
100
+ }
101
+ function normalizeSignature(signature) {
102
+ if (signature instanceof Uint8Array) {
103
+ return signature;
104
+ }
105
+ if (Buffer.isBuffer(signature)) {
106
+ return new Uint8Array(signature);
107
+ }
108
+ if (signature instanceof ArrayBuffer) {
109
+ return new Uint8Array(signature);
110
+ }
111
+ if (typeof signature === "string") {
112
+ return new Uint8Array(Buffer.from(signature, "hex"));
113
+ }
114
+ throw new Error(`Invalid signature format: ${typeof signature}`);
115
+ }
116
+ function initUserCrypto(userToken, logger = new NoOpLogger()) {
117
+ logger.info("[initUserCrypto] Initializing user crypto with ML-DSA-87");
118
+ if (!userToken || !userToken.publicKeySign) {
119
+ throw new Error("Invalid user token: publicKeySign not found");
120
+ }
121
+ const dsaPublicKey = new Uint8Array(
122
+ Buffer.from(userToken.publicKeySign, "base64")
123
+ );
124
+ if (dsaPublicKey.length !== 2592) {
125
+ throw new Error(
126
+ `Invalid ML-DSA-87 public key size: expected 2592 bytes, got ${dsaPublicKey.length}`
127
+ );
128
+ }
129
+ let kemPublicKey = null;
130
+ if (userToken.publicKeyEncrypt) {
131
+ try {
132
+ const kemBuffer = Buffer.from(userToken.publicKeyEncrypt, "base64");
133
+ if (kemBuffer.length !== 1568) {
134
+ logger.warn(
135
+ `[initUserCrypto] Invalid ML-KEM-1024 public key size: expected 1568, got ${kemBuffer.length}`
136
+ );
137
+ } else {
138
+ kemPublicKey = new Uint8Array(kemBuffer);
139
+ }
140
+ } catch (ex) {
141
+ const error = ex;
142
+ logger.warn(
143
+ `[initUserCrypto] Failed to parse publicKeyEncrypt: ${error.message}`
144
+ );
145
+ }
146
+ }
147
+ const verifySignature = (msg, sig) => {
148
+ try {
149
+ const msgBytes = normalizeToBytes(msg);
150
+ const sigBytes = normalizeSignature(sig);
151
+ const isValid = ml_dsa87.verify(sigBytes, msgBytes, dsaPublicKey);
152
+ logger.info("[initUserCrypto.verifySignature] Verification result", {
153
+ isValid,
154
+ msgLength: msgBytes.length,
155
+ sigLength: sigBytes.length
156
+ });
157
+ return isValid;
158
+ } catch (ex) {
159
+ const error = ex;
160
+ logger.error(
161
+ `[initUserCrypto.verifySignature] Error: ${error.message}`
162
+ );
163
+ return false;
164
+ }
165
+ };
166
+ logger.info("[initUserCrypto] Crypto initialized successfully", {
167
+ algorithm: "ML-DSA-87 + ML-KEM-1024",
168
+ dsaPublicKeySize: dsaPublicKey.length,
169
+ kemPublicKeySize: kemPublicKey?.length ?? 0,
170
+ hasEncryption: !!kemPublicKey
171
+ });
172
+ return {
173
+ dsaPublicKey,
174
+ kemPublicKey,
175
+ algorithm: "ML-DSA-87",
176
+ userToken,
177
+ verifySignature
178
+ };
179
+ }
180
+ function checkSignature(crypto2, data, signature, logger = new NoOpLogger()) {
181
+ logger.info("[checkSignature] Checking ML-DSA-87 signature");
182
+ if (!crypto2 || !crypto2.verifySignature) {
183
+ throw new Error("Invalid crypto instance: missing verifySignature method");
184
+ }
185
+ if (!data || !signature) {
186
+ throw new Error("Missing data or signature");
187
+ }
188
+ const msgBytes = normalizeToBytes(data);
189
+ const sigBytes = normalizeSignature(signature);
190
+ logger.info("[checkSignature] Data and signature normalized", {
191
+ dataLength: msgBytes.length,
192
+ signatureLength: sigBytes.length,
193
+ algorithm: crypto2.algorithm
194
+ });
195
+ if (sigBytes.length < 4500 || sigBytes.length > 4700) {
196
+ logger.warn("[checkSignature] Unexpected signature size", {
197
+ expected: "~4595 bytes (ML-DSA-87)",
198
+ actual: sigBytes.length
199
+ });
200
+ }
201
+ const isValid = crypto2.verifySignature(msgBytes, sigBytes);
202
+ if (!isValid) {
203
+ throw new Error("Invalid signature");
204
+ }
205
+ logger.info("[checkSignature] ML-DSA-87 signature verified successfully");
206
+ return true;
207
+ }
208
+
209
+ // src/blockchain/token-provider.ts
210
+ import { Contract, JsonRpcProvider } from "ethers";
211
+
212
+ // src/blockchain/constants.ts
213
+ var PRODUCTION_CONFIG = {
214
+ RPC_ENDPOINT: "https://bc.liberion.com/rpc",
215
+ USER_CONTRACT_ADDRESS: "0xB0b23F79Bf023C97f0235BC0ffC5a8258C03fef9",
216
+ IPFS_GATEWAY: "https://secure.liberion.com",
217
+ TRUST_GATE_URL: "wss://gate.liberion.com"
218
+ };
219
+ var DEVELOPMENT_CONFIG = {
220
+ RPC_ENDPOINT: "https://bc.liberion.dev/rpc",
221
+ USER_CONTRACT_ADDRESS: "0xb584861f5760E9B04B7439c777246c5B82FE6Fff",
222
+ IPFS_GATEWAY: "https://secure.liberion.dev",
223
+ TRUST_GATE_URL: "wss://gate.liberion.dev"
224
+ };
225
+ function getNetworkConfig(env = "production") {
226
+ return env === "development" ? DEVELOPMENT_CONFIG : PRODUCTION_CONFIG;
227
+ }
228
+ var USER_CONTRACT_ABI = [
229
+ {
230
+ inputs: [
231
+ {
232
+ internalType: "address",
233
+ name: "tokenId",
234
+ type: "address"
235
+ }
236
+ ],
237
+ name: "tokenURI",
238
+ outputs: [
239
+ {
240
+ internalType: "string",
241
+ name: "",
242
+ type: "string"
243
+ }
244
+ ],
245
+ stateMutability: "view",
246
+ type: "function"
247
+ }
248
+ ];
249
+
250
+ // src/blockchain/token-provider.ts
251
+ var tokenCache = /* @__PURE__ */ new Map();
252
+ var CACHE_TTL = 15 * 60 * 1e3;
253
+ function extractIpfsHash(tokenURI) {
254
+ if (!tokenURI) {
255
+ throw new Error("tokenURI is empty");
256
+ }
257
+ if (tokenURI.startsWith("ipfs://")) {
258
+ return tokenURI.replace("ipfs://", "");
259
+ }
260
+ if (tokenURI.includes("/ipfs/")) {
261
+ const parts = tokenURI.split("/ipfs/");
262
+ return parts[1];
263
+ }
264
+ if (tokenURI.includes("liberion.com/")) {
265
+ const parts = tokenURI.split("/");
266
+ return parts[parts.length - 1];
267
+ }
268
+ return tokenURI;
269
+ }
270
+ async function fetchFromIPFS(ipfsHash, ipfsGateway, logger) {
271
+ const cached = tokenCache.get(ipfsHash);
272
+ if (cached && cached.expiry > Date.now()) {
273
+ logger.info("[fetchFromIPFS] Cache hit", { ipfsHash });
274
+ return cached.data;
275
+ }
276
+ const url = `${ipfsGateway}/${ipfsHash}`;
277
+ logger.info("[fetchFromIPFS] Fetching from IPFS", { url });
278
+ const response = await fetch(url, {
279
+ headers: {
280
+ Accept: "application/json"
281
+ }
282
+ });
283
+ if (!response.ok) {
284
+ throw new Error(`IPFS fetch failed: ${response.status} ${response.statusText}`);
285
+ }
286
+ const data = await response.json();
287
+ tokenCache.set(ipfsHash, {
288
+ data,
289
+ expiry: Date.now() + CACHE_TTL
290
+ });
291
+ logger.info("[fetchFromIPFS] Token fetched and cached", { ipfsHash });
292
+ return data;
293
+ }
294
+ async function getTokenFromIPFS(address, logger = new NoOpLogger(), networkConfig) {
295
+ const config = networkConfig ?? getNetworkConfig();
296
+ logger.info("[getTokenFromIPFS] Getting SNFT token from IPFS", { address: shortenAddress(address) });
297
+ const provider = new JsonRpcProvider(config.RPC_ENDPOINT);
298
+ const contract = new Contract(
299
+ config.USER_CONTRACT_ADDRESS,
300
+ USER_CONTRACT_ABI,
301
+ provider
302
+ );
303
+ const tokenURI = await contract.tokenURI(address);
304
+ logger.info("[getTokenFromIPFS] Token URI retrieved", {
305
+ address: shortenAddress(address),
306
+ tokenURI
307
+ });
308
+ const ipfsHash = extractIpfsHash(tokenURI);
309
+ logger.info("[getTokenFromIPFS] IPFS hash extracted", {
310
+ address: shortenAddress(address),
311
+ ipfsHash
312
+ });
313
+ const userToken = await fetchFromIPFS(ipfsHash, config.IPFS_GATEWAY, logger);
314
+ if (!userToken.publicKeySign || !userToken.publicKeyEncrypt) {
315
+ throw new Error("Invalid SNFT token structure: missing public keys");
316
+ }
317
+ logger.info("[getTokenFromIPFS] SNFT token fetched successfully", {
318
+ address: shortenAddress(address),
319
+ hasPublicKeySign: !!userToken.publicKeySign,
320
+ hasPublicKeyEncrypt: !!userToken.publicKeyEncrypt,
321
+ hasAssets: !!userToken.assets
322
+ });
323
+ return userToken;
324
+ }
325
+ function clearTokenCache() {
326
+ tokenCache.clear();
327
+ }
328
+
329
+ // src/protocol/trust-gate-client.ts
330
+ import WebSocket from "ws";
331
+ import { encode, decode } from "@msgpack/msgpack";
332
+ var TrustGateClient = class {
333
+ address;
334
+ timeout;
335
+ ws = null;
336
+ pendingRequests = /* @__PURE__ */ new Map();
337
+ requestId = 0;
338
+ connectionTimeout = null;
339
+ logger;
340
+ constructor(options) {
341
+ this.address = options.address;
342
+ this.timeout = options.timeout ?? 1e4;
343
+ this.logger = options.logger ?? new NoOpLogger();
344
+ }
345
+ /**
346
+ * Opens WebSocket connection
347
+ */
348
+ async open() {
349
+ this.logger.info(`[TrustGateClient] Attempting to connect to ${this.address}`);
350
+ return new Promise((resolve, reject) => {
351
+ try {
352
+ this.ws = new WebSocket(this.address);
353
+ this.ws.on("open", () => {
354
+ if (this.connectionTimeout) {
355
+ clearTimeout(this.connectionTimeout);
356
+ }
357
+ this.logger.info(`[TrustGateClient] Connected to ${this.address}`);
358
+ resolve();
359
+ });
360
+ this.ws.on("message", (data) => {
361
+ try {
362
+ const message = decode(data);
363
+ this.handleMessage(message);
364
+ } catch (ex) {
365
+ const error = ex;
366
+ this.logger.error(
367
+ `[TrustGateClient] Failed to decode message: ${error.message}`
368
+ );
369
+ }
370
+ });
371
+ this.ws.on("close", (code) => {
372
+ this.logger.info(
373
+ `[TrustGateClient] Connection closed with code ${code}`
374
+ );
375
+ this.pendingRequests.forEach((request, id) => {
376
+ request.reject(new Error(`Connection closed with code ${code}`));
377
+ this.pendingRequests.delete(id);
378
+ });
379
+ });
380
+ this.ws.on("error", (error) => {
381
+ if (this.connectionTimeout) {
382
+ clearTimeout(this.connectionTimeout);
383
+ }
384
+ this.logger.error("[TrustGateClient] Connection error", {
385
+ message: error.message,
386
+ code: error.code,
387
+ address: this.address
388
+ });
389
+ const enhancedError = new Error(
390
+ `Failed to connect to Trust Gate Server at ${this.address}: ${error.message}`
391
+ );
392
+ enhancedError.code = error.code;
393
+ reject(enhancedError);
394
+ });
395
+ this.connectionTimeout = setTimeout(() => {
396
+ if (this.ws && this.ws.readyState !== WebSocket.OPEN) {
397
+ this.ws.terminate();
398
+ this.logger.error("[TrustGateClient] Connection timeout", {
399
+ address: this.address,
400
+ timeout: this.timeout
401
+ });
402
+ reject(
403
+ new Error(
404
+ `Connection timeout: Trust Gate Server at ${this.address} did not respond within ${this.timeout}ms`
405
+ )
406
+ );
407
+ }
408
+ }, this.timeout);
409
+ } catch (ex) {
410
+ reject(ex);
411
+ }
412
+ });
413
+ }
414
+ /**
415
+ * Sends message and waits for response
416
+ */
417
+ send(data) {
418
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
419
+ return Promise.reject(new Error("WebSocket is not open"));
420
+ }
421
+ return new Promise((resolve, reject) => {
422
+ const requestId = ++this.requestId;
423
+ const timeout = setTimeout(() => {
424
+ this.pendingRequests.delete(requestId);
425
+ reject(new Error("Request timeout"));
426
+ }, 3e4);
427
+ this.pendingRequests.set(requestId, {
428
+ resolve,
429
+ reject,
430
+ timeout
431
+ });
432
+ const message = encode({ ...data, _requestId: requestId });
433
+ this.ws.send(message);
434
+ });
435
+ }
436
+ /**
437
+ * Handles incoming message
438
+ */
439
+ handleMessage(message) {
440
+ const requestId = message._requestId;
441
+ if (!requestId) {
442
+ this.logger.warn(
443
+ "[TrustGateClient] Received message without requestId",
444
+ message
445
+ );
446
+ return;
447
+ }
448
+ const pending = this.pendingRequests.get(requestId);
449
+ if (!pending) {
450
+ this.logger.warn(
451
+ `[TrustGateClient] No pending request for id ${requestId}`
452
+ );
453
+ return;
454
+ }
455
+ clearTimeout(pending.timeout);
456
+ this.pendingRequests.delete(requestId);
457
+ if (message._ === "error") {
458
+ pending.reject(
459
+ new Error(message.message || "Unknown error")
460
+ );
461
+ } else {
462
+ pending.resolve(message);
463
+ }
464
+ }
465
+ /**
466
+ * Closes WebSocket connection
467
+ */
468
+ close() {
469
+ if (this.connectionTimeout) {
470
+ clearTimeout(this.connectionTimeout);
471
+ }
472
+ if (this.ws) {
473
+ this.ws.close();
474
+ this.ws = null;
475
+ }
476
+ }
477
+ /**
478
+ * Creates authorization task via Trust Gate Server
479
+ */
480
+ async createTask(params) {
481
+ const response = await this.send({
482
+ _: "task_init",
483
+ projectId: params.projectId,
484
+ scenarioId: params.scenarioId,
485
+ clientKey: params.clientKey
486
+ });
487
+ if (response.status !== "ok") {
488
+ throw new Error(response.message || "Task creation failed");
489
+ }
490
+ return {
491
+ linkWeb: response.linkWeb,
492
+ taskId: response.taskId
493
+ };
494
+ }
495
+ };
496
+
497
+ // src/protocol/constants.ts
498
+ var COMMAND_ACTIVATE = "activate";
499
+ var COMMAND_READY = "ready";
500
+ var COMMAND_AUTH = "auth";
501
+ var COMMAND_AUTH_RESULT = "auth_result";
502
+ var COMMAND_AUTH_INIT = "auth_init";
503
+ var COMMAND_ACTIVATED = "activated";
504
+ var COMMAND_AUTH_DECLINED = "declined";
505
+ var COMMAND_AUTH_TIMEOUT = "timeout";
506
+ var COMMAND_RECONNECT = "reconnect";
507
+ var COMMAND_CONNECTION_FAILED = "connection_failed";
508
+ var COMMAND_ERROR = "error";
509
+ var COMMAND_HEALTH = "health";
510
+ var SOCKET_PING_TIMEOUT = 3e4;
511
+ var AUTHORIZATION_TIME_FRAME = 10 * 60 * 1e3;
512
+ var DEFAULT_PORT = 31313;
513
+ var STATUS = {
514
+ OK: "ok",
515
+ FAILED: "error"
516
+ };
517
+ var DECLINE_REASON = {
518
+ USER: "user_declined",
519
+ TIMEOUT: "timeout",
520
+ ERROR: "error",
521
+ UNKNOWN: "unknown"
522
+ };
523
+
524
+ // src/liberion-auth.ts
525
+ var LiberionAuth = class {
526
+ logger;
527
+ projectId;
528
+ secretCode;
529
+ networkConfig;
530
+ sessions = {};
531
+ interval = null;
532
+ // Callbacks
533
+ onHello;
534
+ onSuccess;
535
+ onDecline;
536
+ // Static command exports for compatibility
537
+ static COMMAND_AUTH = COMMAND_AUTH;
538
+ static COMMAND_READY = COMMAND_READY;
539
+ constructor(config) {
540
+ this.logger = config.logger ?? (config.debug ? new ConsoleLogger() : new NoOpLogger());
541
+ if (!validate(config.projectId)) {
542
+ this.logger.error(
543
+ "[LiberionAuth] Invalid projectId (should be UUID string)"
544
+ );
545
+ throw new Error("Invalid projectId: must be a valid UUID");
546
+ }
547
+ this.projectId = config.projectId;
548
+ this.secretCode = config.secretCode;
549
+ this.networkConfig = getNetworkConfig(config.environment);
550
+ this.onHello = config.onHello;
551
+ this.onSuccess = config.onSuccess;
552
+ this.onDecline = config.onDecline;
553
+ const port = config.port ?? DEFAULT_PORT;
554
+ const server = config.ssl ? https.createServer(config.ssl) : http.createServer();
555
+ this.logger.info(
556
+ `[LiberionAuth] Starting ws${config.ssl ? "s" : ""} server on port ${port}`
557
+ );
558
+ const wsServer = new WebSocketServer({ server });
559
+ server.listen(port);
560
+ wsServer.on("connection", (socket) => {
561
+ const ws = socket;
562
+ this.newClient(ws);
563
+ ws.on("message", (data) => {
564
+ this.request(ws, data);
565
+ });
566
+ ws.on("close", (code) => {
567
+ this.handleClose(ws, code);
568
+ });
569
+ ws.isAlive = true;
570
+ ws.on("pong", () => {
571
+ ws.isAlive = true;
572
+ });
573
+ });
574
+ this.interval = setInterval(() => {
575
+ wsServer.clients.forEach((socket) => {
576
+ const ws = socket;
577
+ if (ws.isAlive === false) {
578
+ return ws.terminate();
579
+ }
580
+ ws.isAlive = false;
581
+ ws.ping();
582
+ });
583
+ }, SOCKET_PING_TIMEOUT);
584
+ wsServer.on("close", () => {
585
+ if (this.interval) {
586
+ clearInterval(this.interval);
587
+ }
588
+ });
589
+ }
590
+ encode(data) {
591
+ return Buffer.from(encode2(data));
592
+ }
593
+ decode(data) {
594
+ return decode2(data);
595
+ }
596
+ async request(client, data) {
597
+ try {
598
+ const response = await this.readMessage(client, data);
599
+ if (response) {
600
+ this.send(client, response);
601
+ }
602
+ } catch (error) {
603
+ const err = error;
604
+ this.logger.error(`[LiberionAuth] Request error: ${err.message}`);
605
+ this.errorResponse(client, err.message);
606
+ this.finalizeSession(client.clientId);
607
+ }
608
+ }
609
+ send(client, data) {
610
+ if (client.readyState === WebSocket2.OPEN) {
611
+ client.send(this.encode(data));
612
+ }
613
+ }
614
+ async readMessage(client, rawData) {
615
+ let data;
616
+ try {
617
+ data = this.decode(rawData);
618
+ } catch (error) {
619
+ return this.errorResponse(client, "Invalid message format");
620
+ }
621
+ const command = data._;
622
+ switch (command) {
623
+ case COMMAND_AUTH_INIT:
624
+ return this.responseInit(client, data);
625
+ case COMMAND_ACTIVATE:
626
+ return this.responseActivate(
627
+ client,
628
+ data.data,
629
+ data._requestId
630
+ );
631
+ case COMMAND_AUTH:
632
+ return this.responseAuth(client, data);
633
+ case COMMAND_AUTH_DECLINED:
634
+ return this.responseDeclined(client, data);
635
+ case COMMAND_HEALTH:
636
+ return { status: STATUS.OK };
637
+ case COMMAND_RECONNECT:
638
+ return this.responseReconnect(client, data);
639
+ default:
640
+ return this.errorResponse(client, `Unknown command: ${command}`);
641
+ }
642
+ }
643
+ newClient(client) {
644
+ const clientId = uuid();
645
+ const sessionId = uuid();
646
+ this.sessions[clientId] = {
647
+ client,
648
+ sessionId,
649
+ address: null,
650
+ clientSessionId: null,
651
+ isBrowserSession: false
652
+ };
653
+ client.clientId = clientId;
654
+ this.logger.info("[LiberionAuth] New client connected", {
655
+ clientId,
656
+ activeSessions: Object.keys(this.sessions).length
657
+ });
658
+ setTimeout(() => {
659
+ if (this.sessions[clientId]) {
660
+ this.finalizeSession(clientId, true);
661
+ }
662
+ }, AUTHORIZATION_TIME_FRAME);
663
+ }
664
+ handleClose(client, code) {
665
+ const isNormal = code === 1e3 || code === 1001;
666
+ client.isAlive = false;
667
+ const session = this.sessions[client.clientId];
668
+ if (!session) {
669
+ this.logger.info("[LiberionAuth] Socket closed, session already cleaned", {
670
+ clientId: client.clientId,
671
+ code
672
+ });
673
+ return;
674
+ }
675
+ const shouldPreserve = session.isBrowserSession && !session.authResult;
676
+ this.logger.info("[LiberionAuth] WebSocket closed", {
677
+ clientId: client.clientId,
678
+ code,
679
+ isNormal,
680
+ shouldPreserve
681
+ });
682
+ if (shouldPreserve) {
683
+ if (!isNormal) {
684
+ this.logger.warn("[LiberionAuth] Browser abnormal close, session preserved", {
685
+ clientId: client.clientId,
686
+ code
687
+ });
688
+ }
689
+ this.logger.info("[LiberionAuth] Browser session preserved for reconnect", {
690
+ clientId: client.clientId,
691
+ sessionId: session.sessionId
692
+ });
693
+ return;
694
+ }
695
+ if (!isNormal) {
696
+ this.responseFailed(client, { _: COMMAND_CONNECTION_FAILED });
697
+ }
698
+ delete this.sessions[client.clientId];
699
+ }
700
+ async responseInit(client, data) {
701
+ const session = this.sessions[client.clientId];
702
+ this.logger.info("[LiberionAuth] Browser requesting QR code", {
703
+ clientId: client.clientId,
704
+ sessionId: session.sessionId
705
+ });
706
+ const token = encryptBuffer(
707
+ Buffer.from(session.sessionId),
708
+ this.secretCode
709
+ );
710
+ let trustGateClient = null;
711
+ try {
712
+ this.logger.info("[LiberionAuth] Connecting to Trust Gate Server", {
713
+ url: this.networkConfig.TRUST_GATE_URL
714
+ });
715
+ trustGateClient = new TrustGateClient({
716
+ address: this.networkConfig.TRUST_GATE_URL,
717
+ logger: this.logger
718
+ });
719
+ await trustGateClient.open();
720
+ const result = await trustGateClient.createTask({
721
+ projectId: this.projectId,
722
+ clientKey: token.toString("base64")
723
+ });
724
+ this.logger.info("[LiberionAuth] Task created successfully", {
725
+ linkWeb: result.linkWeb,
726
+ taskId: result.taskId
727
+ });
728
+ session.isBrowserSession = true;
729
+ return {
730
+ _: data._,
731
+ linkWeb: result.linkWeb,
732
+ sessionId: session.sessionId
733
+ };
734
+ } catch (ex) {
735
+ const error = ex;
736
+ this.logger.error("[LiberionAuth] responseInit failed", {
737
+ message: error.message
738
+ });
739
+ let userMessage;
740
+ if (error.message.includes("timeout")) {
741
+ userMessage = "Authorization service timeout. Please try again.";
742
+ } else if (error.message.includes("Failed to connect")) {
743
+ userMessage = "Authorization service unavailable. Please try again later.";
744
+ } else {
745
+ userMessage = "Authorization service error. Please contact support.";
746
+ }
747
+ throw new Error(userMessage);
748
+ } finally {
749
+ trustGateClient?.close();
750
+ }
751
+ }
752
+ async responseActivate(client, encryptedData, requestId) {
753
+ const decrypted = decryptBuffer(Buffer.from(encryptedData), this.secretCode);
754
+ const { address, sessionId } = this.decode(decrypted);
755
+ const session = this.findSession("sessionId", sessionId);
756
+ if (!session) {
757
+ this.logger.error("[LiberionAuth] Session not found for activation", {
758
+ address: shortenAddress(address),
759
+ sessionId
760
+ });
761
+ return this.errorResponse(client, "Session not found", requestId);
762
+ }
763
+ if (session.address) {
764
+ this.logger.warn("[LiberionAuth] Session already activated", {
765
+ existingAddress: shortenAddress(session.address),
766
+ newAddress: shortenAddress(address)
767
+ });
768
+ return this.errorResponse(client, "Session already activated", requestId);
769
+ }
770
+ this.logger.info("[LiberionAuth] Activating session", {
771
+ address: shortenAddress(address),
772
+ sessionId
773
+ });
774
+ session.address = address;
775
+ session.clientSessionId = uuid();
776
+ const isRegistered = this.onHello ? await this.onHello(address) : false;
777
+ const responseData = encryptBuffer(
778
+ this.encode({ clientSessionId: session.clientSessionId, isRegistered }),
779
+ this.secretCode
780
+ );
781
+ this.finalResponse(client, {
782
+ _: COMMAND_READY,
783
+ data: responseData,
784
+ _requestId: requestId
785
+ });
786
+ const browserClient = session.client;
787
+ if (browserClient?.readyState === WebSocket2.OPEN) {
788
+ this.send(browserClient, { _: COMMAND_ACTIVATED });
789
+ this.logger.info("[LiberionAuth] ACTIVATED sent to browser", { address: shortenAddress(address) });
790
+ }
791
+ return void 0;
792
+ }
793
+ async responseAuth(client, data) {
794
+ const { address, payload, signature, fields } = data;
795
+ if (!address || !payload || !signature || !fields) {
796
+ return this.errorResponse(client, "Missing required parameters");
797
+ }
798
+ let session = null;
799
+ try {
800
+ const decryptedPayload = decryptBuffer(
801
+ Buffer.from(payload),
802
+ this.secretCode
803
+ );
804
+ const { clientSessionId } = this.decode(decryptedPayload);
805
+ if (!clientSessionId || !validate(clientSessionId)) {
806
+ throw new Error("Invalid clientSessionId");
807
+ }
808
+ session = this.findSession("clientSessionId", clientSessionId);
809
+ if (!session) {
810
+ return this.errorResponse(client, "Session not found");
811
+ }
812
+ const userToken = await getTokenFromIPFS(address, this.logger, this.networkConfig);
813
+ const crypto2 = initUserCrypto(userToken, this.logger);
814
+ checkSignature(crypto2, payload, signature, this.logger);
815
+ const result = this.onSuccess ? await this.onSuccess({ address, fields }) : { token: void 0 };
816
+ this.logger.info("[LiberionAuth] Auth successful", {
817
+ address: shortenAddress(address),
818
+ hasToken: !!result.token
819
+ });
820
+ this.finalResponse(client, {
821
+ _: result.error ? STATUS.FAILED : COMMAND_AUTH,
822
+ message: result.error || "welcome"
823
+ });
824
+ if (result.token) {
825
+ session.authResult = { token: result.token };
826
+ }
827
+ const browserClient = session.client;
828
+ if (browserClient?.readyState === WebSocket2.OPEN) {
829
+ this.finalResponse(browserClient, {
830
+ _: result.error ? STATUS.FAILED : COMMAND_AUTH_RESULT,
831
+ message: result.error || "welcome",
832
+ payload: { token: result.token }
833
+ });
834
+ }
835
+ return void 0;
836
+ } catch (ex) {
837
+ const error = ex;
838
+ this.logger.error(`[LiberionAuth] Auth failed: ${error.message}`);
839
+ if (session) {
840
+ const browserClient = session.client;
841
+ if (browserClient?.readyState === WebSocket2.OPEN) {
842
+ this.errorResponse(browserClient, error.message);
843
+ }
844
+ }
845
+ return this.errorResponse(client, error.message);
846
+ }
847
+ }
848
+ responseDeclined(client, data) {
849
+ const { payload } = data;
850
+ let clientSessionId;
851
+ let browserSession = null;
852
+ if (payload) {
853
+ try {
854
+ const decryptedPayload = decryptBuffer(
855
+ Buffer.from(payload),
856
+ this.secretCode
857
+ );
858
+ const decoded = this.decode(decryptedPayload);
859
+ clientSessionId = decoded.clientSessionId;
860
+ if (clientSessionId && validate(clientSessionId)) {
861
+ browserSession = this.findSession("clientSessionId", clientSessionId);
862
+ }
863
+ } catch (ex) {
864
+ this.logger.error("[LiberionAuth] Failed to decrypt decline payload", {
865
+ error: ex.message
866
+ });
867
+ }
868
+ }
869
+ if (browserSession) {
870
+ this.logger.info("[LiberionAuth] Wallet declined, notifying browser", {
871
+ clientSessionId,
872
+ address: shortenAddress(browserSession.address)
873
+ });
874
+ const browserClient = browserSession.client;
875
+ if (browserClient?.readyState === WebSocket2.OPEN) {
876
+ this.send(browserClient, {
877
+ _: COMMAND_AUTH_DECLINED,
878
+ message: "Authorization declined"
879
+ });
880
+ } else {
881
+ browserSession.declineResult = { message: "Authorization declined" };
882
+ this.logger.info("[LiberionAuth] Browser offline, decline stored", {
883
+ clientSessionId
884
+ });
885
+ }
886
+ if (this.onDecline) {
887
+ this.onDecline({
888
+ address: browserSession.address,
889
+ reason: DECLINE_REASON.USER,
890
+ message: "Authorization declined",
891
+ declinedBy: "wallet",
892
+ sessionId: browserSession.sessionId
893
+ }).catch((ex) => {
894
+ this.logger.error(`[LiberionAuth] onDecline error: ${ex.message}`);
895
+ });
896
+ }
897
+ this.finalResponse(client, {
898
+ _: COMMAND_AUTH_DECLINED,
899
+ message: "Authorization declined"
900
+ });
901
+ return void 0;
902
+ }
903
+ this.logger.error("[LiberionAuth] Browser session not found for decline", {
904
+ clientSessionId,
905
+ hasPayload: !!payload
906
+ });
907
+ return this.responseFailed(client, data);
908
+ }
909
+ responseReconnect(client, data) {
910
+ const sessionId = data.sessionId;
911
+ if (!sessionId) {
912
+ return this.errorResponse(client, "sessionId required");
913
+ }
914
+ const existingSession = this.findSession("sessionId", sessionId);
915
+ if (!existingSession) {
916
+ return this.errorResponse(client, "session_not_found");
917
+ }
918
+ const oldClientId = Object.keys(this.sessions).find(
919
+ (k) => this.sessions[k] === existingSession
920
+ );
921
+ this.logger.info("[LiberionAuth] Reconnecting session", {
922
+ sessionId,
923
+ oldClientId,
924
+ newClientId: client.clientId,
925
+ hasAuthResult: !!existingSession.authResult
926
+ });
927
+ existingSession.client = client;
928
+ if (oldClientId) {
929
+ delete this.sessions[oldClientId];
930
+ }
931
+ this.sessions[client.clientId] = existingSession;
932
+ if (existingSession.authResult) {
933
+ this.send(client, {
934
+ _: COMMAND_AUTH_RESULT,
935
+ message: "welcome",
936
+ payload: existingSession.authResult
937
+ });
938
+ }
939
+ if (existingSession.declineResult) {
940
+ this.send(client, {
941
+ _: COMMAND_AUTH_DECLINED,
942
+ message: existingSession.declineResult.message
943
+ });
944
+ this.logger.info("[LiberionAuth] Sent stored decline", { sessionId });
945
+ }
946
+ let status;
947
+ if (existingSession.authResult) {
948
+ status = "completed";
949
+ } else if (existingSession.declineResult) {
950
+ status = "declined";
951
+ } else if (existingSession.address) {
952
+ status = "activated";
953
+ } else {
954
+ status = "waiting";
955
+ }
956
+ return {
957
+ _: COMMAND_RECONNECT,
958
+ status
959
+ };
960
+ }
961
+ responseFailed(client, data) {
962
+ const session = this.sessions[client.clientId];
963
+ if (!session) {
964
+ return this.errorResponse(client, "Session not found");
965
+ }
966
+ const command = data._;
967
+ const message = data.message || "Authorization declined";
968
+ let reason;
969
+ switch (command) {
970
+ case COMMAND_AUTH_DECLINED:
971
+ reason = DECLINE_REASON.USER;
972
+ break;
973
+ case COMMAND_AUTH_TIMEOUT:
974
+ reason = DECLINE_REASON.TIMEOUT;
975
+ break;
976
+ case COMMAND_CONNECTION_FAILED:
977
+ case COMMAND_ERROR:
978
+ reason = DECLINE_REASON.ERROR;
979
+ break;
980
+ default:
981
+ reason = DECLINE_REASON.UNKNOWN;
982
+ }
983
+ const isBrowser = session.client === client && session.isBrowserSession;
984
+ this.logger.info("[LiberionAuth] Authorization declined", {
985
+ sessionId: session.sessionId,
986
+ address: shortenAddress(session.address),
987
+ reason,
988
+ declinedBy: isBrowser ? "browser" : "wallet"
989
+ });
990
+ if (this.onDecline) {
991
+ this.onDecline({
992
+ address: session.address,
993
+ reason,
994
+ message,
995
+ declinedBy: isBrowser ? "browser" : "wallet",
996
+ sessionId: session.sessionId
997
+ }).catch((ex) => {
998
+ this.logger.error(`[LiberionAuth] onDecline error: ${ex.message}`);
999
+ });
1000
+ }
1001
+ this.finalResponse(client, { _: command, message });
1002
+ return void 0;
1003
+ }
1004
+ errorResponse(client, error, requestId) {
1005
+ this.logger.error("[LiberionAuth] Sending error", {
1006
+ clientId: client.clientId,
1007
+ error
1008
+ });
1009
+ this.finalResponse(client, {
1010
+ _: COMMAND_ERROR,
1011
+ message: error,
1012
+ ...requestId && { _requestId: requestId }
1013
+ });
1014
+ return false;
1015
+ }
1016
+ finalResponse(client, data) {
1017
+ if (client.readyState === WebSocket2.OPEN) {
1018
+ this.send(client, data);
1019
+ }
1020
+ this.closeClient(client);
1021
+ }
1022
+ finalizeSession(clientId, isTimeout = false) {
1023
+ const session = this.sessions[clientId];
1024
+ if (!session) return;
1025
+ if (isTimeout) {
1026
+ this.responseFailed(session.client, {
1027
+ _: COMMAND_AUTH_TIMEOUT,
1028
+ message: "Authorization timeout"
1029
+ });
1030
+ } else {
1031
+ this.closeClient(session.client);
1032
+ }
1033
+ }
1034
+ closeClient(client) {
1035
+ delete this.sessions[client.clientId];
1036
+ client?.terminate();
1037
+ }
1038
+ findSession(paramName, searchValue) {
1039
+ for (const key of Object.keys(this.sessions)) {
1040
+ if (this.sessions[key][paramName] === searchValue) {
1041
+ return this.sessions[key];
1042
+ }
1043
+ }
1044
+ return null;
1045
+ }
1046
+ };
1047
+
1048
+ // src/crypto/pq-crypto.ts
1049
+ import { ml_kem1024 } from "@noble/post-quantum/ml-kem.js";
1050
+ import { ml_dsa87 as ml_dsa872 } from "@noble/post-quantum/ml-dsa.js";
1051
+ import { webcrypto } from "crypto";
1052
+ import sodium from "libsodium-wrappers";
1053
+ var { subtle } = webcrypto;
1054
+ var PROTOCOL_AAD = new TextEncoder().encode("PQv1");
1055
+ var PQCrypto = class {
1056
+ logger;
1057
+ // Trust Gate keys (generated from seed)
1058
+ // Note: kemPrivateKey kept for potential future decryption support
1059
+ _kemPrivateKey = null;
1060
+ kemPublicKey = null;
1061
+ dsaPrivateKey = null;
1062
+ dsaPublicKey = null;
1063
+ // Peer (User/Wallet) public keys
1064
+ peerKEMPublicKey = null;
1065
+ peerDSAPublicKey = null;
1066
+ constructor(logger) {
1067
+ this.logger = logger ?? new NoOpLogger();
1068
+ }
1069
+ /**
1070
+ * Generate Trust Gate session keys from cryptographic seed
1071
+ * @param seed - Random seed (at least 96 bytes: 64 for ML-KEM + 32 for ML-DSA)
1072
+ */
1073
+ async generateKeysFromSeed(seed) {
1074
+ try {
1075
+ if (seed.length < 96) {
1076
+ throw new Error(
1077
+ `Seed too short: expected at least 96 bytes (64 for KEM + 32 for DSA), got ${seed.length}`
1078
+ );
1079
+ }
1080
+ const kemSeed = seed.slice(0, 64);
1081
+ const kemKeys = ml_kem1024.keygen(kemSeed);
1082
+ this.kemPublicKey = kemKeys.publicKey;
1083
+ this._kemPrivateKey = kemKeys.secretKey;
1084
+ const dsaSeed = seed.slice(64, 96);
1085
+ const dsaKeys = ml_dsa872.keygen(dsaSeed);
1086
+ this.dsaPublicKey = dsaKeys.publicKey;
1087
+ this.dsaPrivateKey = dsaKeys.secretKey;
1088
+ this.logger.info("[PQCrypto] Keys generated successfully", {
1089
+ kemPublicKeySize: this.kemPublicKey.length,
1090
+ dsaPublicKeySize: this.dsaPublicKey.length
1091
+ });
1092
+ } catch (ex) {
1093
+ const error = ex;
1094
+ this.logger.error(`[PQCrypto] Failed to generate keys: ${error.message}`);
1095
+ throw new Error(`Failed to generate PQ keys: ${error.message}`);
1096
+ }
1097
+ }
1098
+ /**
1099
+ * Import peer's (user) public keys for encryption and verification
1100
+ * @param kemPublicKey - ML-KEM-1024 public key (1568 bytes)
1101
+ * @param dsaPublicKey - ML-DSA-87 public key (2592 bytes)
1102
+ */
1103
+ importPeerPublicKeys(kemPublicKey, dsaPublicKey) {
1104
+ try {
1105
+ this.peerKEMPublicKey = new Uint8Array(kemPublicKey);
1106
+ this.peerDSAPublicKey = new Uint8Array(dsaPublicKey);
1107
+ if (this.peerKEMPublicKey.length !== 1568) {
1108
+ throw new Error(
1109
+ `Invalid ML-KEM-1024 public key size: expected 1568, got ${this.peerKEMPublicKey.length}`
1110
+ );
1111
+ }
1112
+ if (this.peerDSAPublicKey.length !== 2592) {
1113
+ throw new Error(
1114
+ `Invalid ML-DSA-87 public key size: expected 2592, got ${this.peerDSAPublicKey.length}`
1115
+ );
1116
+ }
1117
+ this.logger.info("[PQCrypto] Peer public keys imported", {
1118
+ kemKeySize: this.peerKEMPublicKey.length,
1119
+ dsaKeySize: this.peerDSAPublicKey.length
1120
+ });
1121
+ } catch (ex) {
1122
+ const error = ex;
1123
+ this.logger.error(`[PQCrypto] Failed to import peer keys: ${error.message}`);
1124
+ throw new Error(`Failed to import peer public keys: ${error.message}`);
1125
+ }
1126
+ }
1127
+ /**
1128
+ * Hybrid encryption: ML-KEM + XChaCha20-Poly1305 + ML-DSA signature
1129
+ * Matches client format: version + header + sigLen + signature + cipherText + nonce + ct
1130
+ * @param message - Data to encrypt
1131
+ * @returns Encrypted package in client-compatible format
1132
+ */
1133
+ async encrypt(message) {
1134
+ try {
1135
+ await sodium.ready;
1136
+ if (!this.peerKEMPublicKey) {
1137
+ throw new Error("Peer KEM public key not imported");
1138
+ }
1139
+ if (!this.dsaPrivateKey) {
1140
+ throw new Error("DSA private key not generated");
1141
+ }
1142
+ const messageBytes = message instanceof Uint8Array ? message : new Uint8Array(message);
1143
+ this.logger.info("[PQCrypto] Starting hybrid encryption", {
1144
+ messageSize: messageBytes.length
1145
+ });
1146
+ const { cipherText: kemCiphertext, sharedSecret } = ml_kem1024.encapsulate(
1147
+ this.peerKEMPublicKey
1148
+ );
1149
+ this.logger.info("[PQCrypto] ML-KEM encapsulation complete", {
1150
+ ciphertextSize: kemCiphertext.length,
1151
+ // 1568 bytes
1152
+ sharedSecretSize: sharedSecret.length
1153
+ // 32 bytes
1154
+ });
1155
+ const secretKeyRaw = await subtle.deriveBits(
1156
+ {
1157
+ name: "HKDF",
1158
+ hash: "SHA-256",
1159
+ salt: PROTOCOL_AAD,
1160
+ info: PROTOCOL_AAD
1161
+ },
1162
+ await subtle.importKey("raw", sharedSecret, { name: "HKDF" }, false, [
1163
+ "deriveBits"
1164
+ ]),
1165
+ 256
1166
+ );
1167
+ sharedSecret.fill(0);
1168
+ const versionBuf = new Uint8Array([1]);
1169
+ const header = new Uint8Array(4);
1170
+ new DataView(header.buffer).setUint32(0, kemCiphertext.length, false);
1171
+ const aad = new Uint8Array(PROTOCOL_AAD.length + 1 + 4);
1172
+ aad.set(PROTOCOL_AAD, 0);
1173
+ aad.set(versionBuf, PROTOCOL_AAD.length);
1174
+ aad.set(header, PROTOCOL_AAD.length + 1);
1175
+ const keyBytes = new Uint8Array(secretKeyRaw);
1176
+ const nonce = webcrypto.getRandomValues(
1177
+ new Uint8Array(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)
1178
+ );
1179
+ const ct = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
1180
+ messageBytes,
1181
+ aad,
1182
+ null,
1183
+ nonce,
1184
+ keyBytes
1185
+ );
1186
+ this.logger.info("[PQCrypto] XChaCha20-Poly1305 encryption complete", {
1187
+ nonceSize: nonce.length,
1188
+ // 24 bytes
1189
+ encryptedSize: ct.length
1190
+ // message + 16 bytes tag
1191
+ });
1192
+ const signature = ml_dsa872.sign(kemCiphertext, this.dsaPrivateKey);
1193
+ this.logger.info("[PQCrypto] ML-DSA signature created", {
1194
+ signatureSize: signature.length
1195
+ // ~4627 bytes
1196
+ });
1197
+ const sigLenBuf = new Uint8Array(4);
1198
+ new DataView(sigLenBuf.buffer).setUint32(0, signature.length, false);
1199
+ const result = new Uint8Array(
1200
+ 1 + 4 + 4 + signature.length + kemCiphertext.length + nonce.length + ct.length
1201
+ );
1202
+ let offset = 0;
1203
+ result.set(versionBuf, offset);
1204
+ offset += 1;
1205
+ result.set(header, offset);
1206
+ offset += 4;
1207
+ result.set(sigLenBuf, offset);
1208
+ offset += 4;
1209
+ result.set(signature, offset);
1210
+ offset += signature.length;
1211
+ result.set(kemCiphertext, offset);
1212
+ offset += kemCiphertext.length;
1213
+ result.set(nonce, offset);
1214
+ offset += nonce.length;
1215
+ result.set(ct, offset);
1216
+ keyBytes.fill(0);
1217
+ new Uint8Array(secretKeyRaw).fill(0);
1218
+ this.logger.info("[PQCrypto] Hybrid encryption complete", {
1219
+ totalSize: result.length,
1220
+ breakdown: {
1221
+ version: 1,
1222
+ header: 4,
1223
+ sigLen: 4,
1224
+ signature: signature.length,
1225
+ kemCiphertext: kemCiphertext.length,
1226
+ nonce: nonce.length,
1227
+ ct: ct.length
1228
+ }
1229
+ });
1230
+ return Buffer.from(result);
1231
+ } catch (ex) {
1232
+ const error = ex;
1233
+ this.logger.error(`[PQCrypto] Encryption failed: ${error.message}`);
1234
+ throw new Error(`PQ encryption failed: ${error.message}`);
1235
+ }
1236
+ }
1237
+ /**
1238
+ * Check if keys have been generated
1239
+ */
1240
+ hasKeys() {
1241
+ return !!(this.kemPublicKey && this.dsaPublicKey && this._kemPrivateKey);
1242
+ }
1243
+ /**
1244
+ * Export Trust Gate public keys (for sending to client)
1245
+ * @returns Object with publicKeyEncrypt and publicKeySign as Buffers
1246
+ */
1247
+ exportKeys() {
1248
+ if (!this.kemPublicKey || !this.dsaPublicKey) {
1249
+ throw new Error("Keys not generated yet");
1250
+ }
1251
+ return {
1252
+ publicKeyEncrypt: Buffer.from(this.kemPublicKey),
1253
+ publicKeySign: Buffer.from(this.dsaPublicKey)
1254
+ };
1255
+ }
1256
+ };
1257
+ export {
1258
+ COMMAND_ACTIVATED,
1259
+ COMMAND_AUTH,
1260
+ COMMAND_AUTH_DECLINED,
1261
+ COMMAND_AUTH_RESULT,
1262
+ COMMAND_AUTH_TIMEOUT,
1263
+ COMMAND_ERROR,
1264
+ COMMAND_READY,
1265
+ ConsoleLogger,
1266
+ DECLINE_REASON,
1267
+ DEFAULT_PORT,
1268
+ LiberionAuth,
1269
+ NoOpLogger,
1270
+ PQCrypto,
1271
+ STATUS,
1272
+ USER_CONTRACT_ABI,
1273
+ checkSignature,
1274
+ clearTokenCache,
1275
+ createWinstonAdapter,
1276
+ decryptBuffer,
1277
+ encryptBuffer,
1278
+ extractIpfsHash,
1279
+ getNetworkConfig,
1280
+ getTokenFromIPFS,
1281
+ initUserCrypto,
1282
+ shortenAddress
1283
+ };