forkoff 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/E2EE-COMPLETE.md +290 -0
  2. package/README.md +11 -0
  3. package/dist/__tests__/crypto/e2e-integration.test.d.ts +17 -0
  4. package/dist/__tests__/crypto/e2e-integration.test.d.ts.map +1 -0
  5. package/dist/__tests__/crypto/e2e-integration.test.js +338 -0
  6. package/dist/__tests__/crypto/e2e-integration.test.js.map +1 -0
  7. package/dist/__tests__/crypto/e2eeManager.test.d.ts +2 -0
  8. package/dist/__tests__/crypto/e2eeManager.test.d.ts.map +1 -0
  9. package/dist/__tests__/crypto/e2eeManager.test.js +242 -0
  10. package/dist/__tests__/crypto/e2eeManager.test.js.map +1 -0
  11. package/dist/__tests__/crypto/encryption.test.d.ts +2 -0
  12. package/dist/__tests__/crypto/encryption.test.d.ts.map +1 -0
  13. package/dist/__tests__/crypto/encryption.test.js +116 -0
  14. package/dist/__tests__/crypto/encryption.test.js.map +1 -0
  15. package/dist/__tests__/crypto/keyExchange.test.d.ts +2 -0
  16. package/dist/__tests__/crypto/keyExchange.test.d.ts.map +1 -0
  17. package/dist/__tests__/crypto/keyExchange.test.js +84 -0
  18. package/dist/__tests__/crypto/keyExchange.test.js.map +1 -0
  19. package/dist/__tests__/crypto/keyGeneration.test.d.ts +2 -0
  20. package/dist/__tests__/crypto/keyGeneration.test.d.ts.map +1 -0
  21. package/dist/__tests__/crypto/keyGeneration.test.js +61 -0
  22. package/dist/__tests__/crypto/keyGeneration.test.js.map +1 -0
  23. package/dist/__tests__/crypto/keyStorage.test.d.ts +2 -0
  24. package/dist/__tests__/crypto/keyStorage.test.d.ts.map +1 -0
  25. package/dist/__tests__/crypto/keyStorage.test.js +133 -0
  26. package/dist/__tests__/crypto/keyStorage.test.js.map +1 -0
  27. package/dist/__tests__/crypto/websocketIntegration.test.d.ts +2 -0
  28. package/dist/__tests__/crypto/websocketIntegration.test.d.ts.map +1 -0
  29. package/dist/__tests__/crypto/websocketIntegration.test.js +259 -0
  30. package/dist/__tests__/crypto/websocketIntegration.test.js.map +1 -0
  31. package/dist/crypto/e2eeManager.d.ts +82 -0
  32. package/dist/crypto/e2eeManager.d.ts.map +1 -0
  33. package/dist/crypto/e2eeManager.js +270 -0
  34. package/dist/crypto/e2eeManager.js.map +1 -0
  35. package/dist/crypto/encryption.d.ts +19 -0
  36. package/dist/crypto/encryption.d.ts.map +1 -0
  37. package/dist/crypto/encryption.js +111 -0
  38. package/dist/crypto/encryption.js.map +1 -0
  39. package/dist/crypto/keyExchange.d.ts +24 -0
  40. package/dist/crypto/keyExchange.d.ts.map +1 -0
  41. package/dist/crypto/keyExchange.js +119 -0
  42. package/dist/crypto/keyExchange.js.map +1 -0
  43. package/dist/crypto/keyGeneration.d.ts +18 -0
  44. package/dist/crypto/keyGeneration.d.ts.map +1 -0
  45. package/dist/crypto/keyGeneration.js +99 -0
  46. package/dist/crypto/keyGeneration.js.map +1 -0
  47. package/dist/crypto/keyStorage.d.ts +39 -0
  48. package/dist/crypto/keyStorage.d.ts.map +1 -0
  49. package/dist/crypto/keyStorage.js +117 -0
  50. package/dist/crypto/keyStorage.js.map +1 -0
  51. package/dist/crypto/sessionPersistence.d.ts +33 -0
  52. package/dist/crypto/sessionPersistence.d.ts.map +1 -0
  53. package/dist/crypto/sessionPersistence.js +173 -0
  54. package/dist/crypto/sessionPersistence.js.map +1 -0
  55. package/dist/crypto/types.d.ts +35 -0
  56. package/dist/crypto/types.d.ts.map +1 -0
  57. package/dist/crypto/types.js +8 -0
  58. package/dist/crypto/types.js.map +1 -0
  59. package/dist/crypto/websocketE2EE.d.ts +47 -0
  60. package/dist/crypto/websocketE2EE.d.ts.map +1 -0
  61. package/dist/crypto/websocketE2EE.js +144 -0
  62. package/dist/crypto/websocketE2EE.js.map +1 -0
  63. package/dist/index.js +78 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/websocket.d.ts +26 -1
  66. package/dist/websocket.d.ts.map +1 -1
  67. package/dist/websocket.js +25 -1
  68. package/dist/websocket.js.map +1 -1
  69. package/jest.config.js +15 -0
  70. package/package.json +10 -3
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.E2EEManager = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const keyGeneration_1 = require("./keyGeneration");
9
+ const keyStorage_1 = require("./keyStorage");
10
+ const keyExchange_1 = require("./keyExchange");
11
+ const encryption_1 = require("./encryption");
12
+ const sessionPersistence_1 = require("./sessionPersistence");
13
+ /**
14
+ * E2EE Manager for CLI
15
+ * Orchestrates all end-to-end encryption operations
16
+ */
17
+ class E2EEManager {
18
+ constructor(deviceId, apiUrl, authToken) {
19
+ this.keyPair = null;
20
+ this.initialized = false;
21
+ // Track ephemeral keys for pending key exchanges
22
+ this.pendingKeyExchanges = new Map(); // deviceId -> ephemeralPrivateKey
23
+ // Track message counters for replay protection
24
+ this.outgoingCounters = new Map(); // deviceId -> counter
25
+ this.incomingCounters = new Map(); // deviceId -> last seen counter
26
+ this.deviceId = deviceId;
27
+ this.apiUrl = apiUrl;
28
+ this.authToken = authToken;
29
+ this.axiosInstance = axios_1.default.create({
30
+ headers: {
31
+ Authorization: `Bearer ${authToken}`,
32
+ },
33
+ });
34
+ }
35
+ /**
36
+ * Initializes E2EE manager
37
+ * - Loads or generates key pair
38
+ * - Uploads public key to backend
39
+ */
40
+ async initialize() {
41
+ // Try to load existing key pair
42
+ const existingPrivateKey = await (0, keyStorage_1.getPrivateKey)(this.deviceId);
43
+ if (existingPrivateKey) {
44
+ // Derive public key from existing private key
45
+ const publicKey = this.derivePublicKeyFromPrivateKey(existingPrivateKey);
46
+ this.keyPair = {
47
+ publicKey,
48
+ privateKey: existingPrivateKey,
49
+ };
50
+ }
51
+ else {
52
+ // Generate new key pair
53
+ this.keyPair = (0, keyGeneration_1.generateKeyPair)();
54
+ await (0, keyStorage_1.storePrivateKey)(this.deviceId, this.keyPair.privateKey);
55
+ }
56
+ // Upload public key to backend
57
+ await this.uploadPublicKey();
58
+ this.initialized = true;
59
+ }
60
+ /**
61
+ * Derives X25519 public key from private key
62
+ */
63
+ derivePublicKeyFromPrivateKey(privateKey) {
64
+ const crypto = require('crypto');
65
+ const privateKeyBytes = Buffer.from(privateKey, 'base64');
66
+ // Create private key object
67
+ const privateKeyObject = crypto.createPrivateKey({
68
+ key: Buffer.concat([
69
+ Buffer.from([
70
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65,
71
+ 0x6e, 0x04, 0x22, 0x04, 0x20,
72
+ ]),
73
+ privateKeyBytes,
74
+ ]),
75
+ format: 'der',
76
+ type: 'pkcs8',
77
+ });
78
+ // Derive public key
79
+ const publicKeyObject = crypto.createPublicKey(privateKeyObject);
80
+ const publicKeyDER = publicKeyObject.export({
81
+ type: 'spki',
82
+ format: 'der',
83
+ });
84
+ // Extract raw 32-byte public key
85
+ const rawPublicKey = publicKeyDER.slice(-32);
86
+ return rawPublicKey.toString('base64');
87
+ }
88
+ /**
89
+ * Uploads public key to backend
90
+ */
91
+ async uploadPublicKey() {
92
+ if (!this.keyPair) {
93
+ throw new Error('Key pair not initialized');
94
+ }
95
+ await this.axiosInstance.put(`${this.apiUrl}/devices/${this.deviceId}/public-key`, { publicKey: this.keyPair.publicKey });
96
+ }
97
+ /**
98
+ * Initiates key exchange with target device
99
+ * Returns payload to send via WebSocket
100
+ */
101
+ async initiateKeyExchange(targetDeviceId) {
102
+ if (!this.keyPair) {
103
+ throw new Error('E2EE Manager not initialized');
104
+ }
105
+ // Generate ephemeral key pair for this exchange
106
+ const ephemeralKeyPair = (0, keyGeneration_1.generateKeyPair)();
107
+ // Store ephemeral private key for when we receive ack
108
+ this.pendingKeyExchanges.set(targetDeviceId, ephemeralKeyPair.privateKey);
109
+ // Fetch target device's public key
110
+ const response = await this.axiosInstance.get(`${this.apiUrl}/devices/${targetDeviceId}/public-key`);
111
+ const targetPublicKey = response.data.publicKey;
112
+ // Compute session key using our ephemeral private key and their public key
113
+ const sessionKey = (0, keyExchange_1.performKeyExchange)(ephemeralKeyPair.privateKey, targetPublicKey);
114
+ // Store session key (in memory and on disk for reconnection resilience)
115
+ const sessionId = `session-${this.deviceId}-${targetDeviceId}-${Date.now()}`;
116
+ const sessionKeys = { encryptionKey: sessionKey, sessionId };
117
+ (0, keyStorage_1.storeSessionKey)(targetDeviceId, sessionKey, sessionId);
118
+ (0, sessionPersistence_1.persistSessionKey)(this.deviceId, targetDeviceId, sessionKeys); // Persist to disk
119
+ // Initialize counters
120
+ this.outgoingCounters.set(targetDeviceId, 0);
121
+ this.incomingCounters.set(targetDeviceId, -1); // Start at -1 so first message (counter: 0) is accepted
122
+ return {
123
+ senderDeviceId: this.deviceId,
124
+ ephemeralPublicKey: ephemeralKeyPair.publicKey,
125
+ };
126
+ }
127
+ /**
128
+ * Handles incoming key exchange init from sender
129
+ * Returns ack payload to send back via WebSocket
130
+ */
131
+ async handleKeyExchangeInit(senderDeviceId, senderEphemeralPublicKey) {
132
+ if (!this.keyPair) {
133
+ throw new Error('E2EE Manager not initialized');
134
+ }
135
+ // Generate our ephemeral key pair
136
+ const ephemeralKeyPair = (0, keyGeneration_1.generateKeyPair)();
137
+ // Compute session key using our ephemeral private key and their ephemeral public key
138
+ const sessionKey = (0, keyExchange_1.performKeyExchange)(ephemeralKeyPair.privateKey, senderEphemeralPublicKey);
139
+ // Store session key (in memory and on disk for reconnection resilience)
140
+ const sessionId = `session-${senderDeviceId}-${this.deviceId}-${Date.now()}`;
141
+ const sessionKeys = { encryptionKey: sessionKey, sessionId };
142
+ (0, keyStorage_1.storeSessionKey)(senderDeviceId, sessionKey, sessionId);
143
+ (0, sessionPersistence_1.persistSessionKey)(this.deviceId, senderDeviceId, sessionKeys); // Persist to disk
144
+ // Initialize counters
145
+ this.outgoingCounters.set(senderDeviceId, 0);
146
+ this.incomingCounters.set(senderDeviceId, -1); // Start at -1 so first message (counter: 0) is accepted
147
+ return {
148
+ recipientDeviceId: this.deviceId,
149
+ ephemeralPublicKey: ephemeralKeyPair.publicKey,
150
+ };
151
+ }
152
+ /**
153
+ * Handles incoming key exchange ack from recipient
154
+ * Completes the key exchange by deriving the final session key
155
+ */
156
+ async handleKeyExchangeAck(recipientDeviceId, recipientEphemeralPublicKey) {
157
+ const ephemeralPrivateKey = this.pendingKeyExchanges.get(recipientDeviceId);
158
+ if (!ephemeralPrivateKey) {
159
+ throw new Error('No pending key exchange for this device. Must call initiateKeyExchange first.');
160
+ }
161
+ // Compute session key using our ephemeral private key and their ephemeral public key
162
+ const sessionKey = (0, keyExchange_1.performKeyExchange)(ephemeralPrivateKey, recipientEphemeralPublicKey);
163
+ // Store session key (overwrites the one from init, in memory and on disk)
164
+ const sessionId = `session-${this.deviceId}-${recipientDeviceId}-${Date.now()}`;
165
+ const sessionKeys = { encryptionKey: sessionKey, sessionId };
166
+ (0, keyStorage_1.storeSessionKey)(recipientDeviceId, sessionKey, sessionId);
167
+ (0, sessionPersistence_1.persistSessionKey)(this.deviceId, recipientDeviceId, sessionKeys); // Persist to disk
168
+ // Clean up pending exchange
169
+ this.pendingKeyExchanges.delete(recipientDeviceId);
170
+ }
171
+ /**
172
+ * Attempts to restore a persisted session after reconnection
173
+ * Useful when IP changes cause WebSocket disconnection
174
+ */
175
+ async restorePersistedSession(targetDeviceId) {
176
+ const persistedKeys = (0, sessionPersistence_1.loadPersistedSessionKey)(this.deviceId, targetDeviceId);
177
+ if (!persistedKeys) {
178
+ return false;
179
+ }
180
+ // Restore session to memory
181
+ (0, keyStorage_1.storeSessionKey)(targetDeviceId, persistedKeys.encryptionKey, persistedKeys.sessionId);
182
+ // Reset counters (conservative approach - could be persisted too)
183
+ this.outgoingCounters.set(targetDeviceId, 0);
184
+ this.incomingCounters.set(targetDeviceId, -1);
185
+ console.log(`[E2EE] Restored persisted session for ${targetDeviceId}`);
186
+ return true;
187
+ }
188
+ /**
189
+ * Lists all devices with persisted sessions
190
+ * Useful for auto-reconnection after network changes
191
+ */
192
+ listPersistedDevices() {
193
+ return (0, sessionPersistence_1.listPersistedSessions)(this.deviceId);
194
+ }
195
+ /**
196
+ * Encrypts a message for a target device
197
+ */
198
+ encryptMessage(plaintext, targetDeviceId, sessionId) {
199
+ const sessionKeys = (0, keyStorage_1.getSessionKey)(targetDeviceId);
200
+ if (!sessionKeys) {
201
+ throw new Error(`No session key found for device ${targetDeviceId}. Must complete key exchange first.`);
202
+ }
203
+ // Encrypt message
204
+ const encryptedPayload = (0, encryption_1.encrypt)(plaintext, sessionKeys.encryptionKey);
205
+ // Get and increment counter
206
+ const counter = this.outgoingCounters.get(targetDeviceId) ?? 0;
207
+ this.outgoingCounters.set(targetDeviceId, counter + 1);
208
+ return {
209
+ senderDeviceId: this.deviceId,
210
+ recipientDeviceId: targetDeviceId,
211
+ sessionId,
212
+ payload: encryptedPayload,
213
+ messageCounter: counter,
214
+ timestamp: new Date().toISOString(),
215
+ };
216
+ }
217
+ /**
218
+ * Decrypts a message from a sender device
219
+ */
220
+ decryptMessage(encryptedMessage, senderDeviceId) {
221
+ const sessionKeys = (0, keyStorage_1.getSessionKey)(senderDeviceId);
222
+ if (!sessionKeys) {
223
+ throw new Error(`No session key found for device ${senderDeviceId}. Key exchange not completed.`);
224
+ }
225
+ // Replay protection: check message counter
226
+ const lastCounter = this.incomingCounters.get(senderDeviceId) ?? -1;
227
+ if (encryptedMessage.messageCounter <= lastCounter) {
228
+ throw new Error(`Invalid message counter. Possible replay attack. Expected > ${lastCounter}, got ${encryptedMessage.messageCounter}`);
229
+ }
230
+ // Update last seen counter
231
+ this.incomingCounters.set(senderDeviceId, encryptedMessage.messageCounter);
232
+ // Decrypt message
233
+ const plaintext = (0, encryption_1.decrypt)(encryptedMessage.payload, sessionKeys.encryptionKey);
234
+ return plaintext;
235
+ }
236
+ /**
237
+ * Checks if a session key exists for a device
238
+ */
239
+ hasSessionKey(deviceId) {
240
+ return (0, keyStorage_1.getSessionKey)(deviceId) !== null;
241
+ }
242
+ /**
243
+ * Checks if manager is initialized
244
+ */
245
+ isInitialized() {
246
+ return this.initialized;
247
+ }
248
+ /**
249
+ * Cleans up all session keys and pending exchanges
250
+ * @param deletePersisted - Whether to delete persisted sessions from disk (default: false)
251
+ */
252
+ cleanup(deletePersisted = false) {
253
+ (0, keyStorage_1.clearSessionKeys)();
254
+ this.pendingKeyExchanges.clear();
255
+ this.outgoingCounters.clear();
256
+ this.incomingCounters.clear();
257
+ // Optionally delete persisted sessions
258
+ if (deletePersisted) {
259
+ (0, sessionPersistence_1.deleteAllPersistedSessions)(this.deviceId);
260
+ }
261
+ }
262
+ /**
263
+ * Removes a specific persisted session
264
+ */
265
+ removePersistedSession(targetDeviceId) {
266
+ (0, sessionPersistence_1.deletePersistedSession)(this.deviceId, targetDeviceId);
267
+ }
268
+ }
269
+ exports.E2EEManager = E2EEManager;
270
+ //# sourceMappingURL=e2eeManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2eeManager.js","sourceRoot":"","sources":["../../src/crypto/e2eeManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { EncryptedPayload } from './types';
2
+ /**
3
+ * Encrypts plaintext using AES-256-GCM
4
+ *
5
+ * @param plaintext - Text to encrypt
6
+ * @param key - 32-byte encryption key (AES-256)
7
+ * @returns EncryptedPayload with Base64-encoded ciphertext, nonce, and authTag
8
+ */
9
+ export declare function encrypt(plaintext: string, key: Uint8Array): EncryptedPayload;
10
+ /**
11
+ * Decrypts ciphertext using AES-256-GCM
12
+ *
13
+ * @param payload - EncryptedPayload with Base64-encoded ciphertext, nonce, and authTag
14
+ * @param key - 32-byte encryption key (AES-256)
15
+ * @returns Decrypted plaintext
16
+ * @throws Error if decryption fails (wrong key, tampered data, etc.)
17
+ */
18
+ export declare function decrypt(payload: EncryptedPayload, key: Uint8Array): string;
19
+ //# sourceMappingURL=encryption.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../src/crypto/encryption.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAM3C;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,gBAAgB,CA6B5E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,UAAU,GACd,MAAM,CAqCR"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.encrypt = encrypt;
37
+ exports.decrypt = decrypt;
38
+ const crypto = __importStar(require("crypto"));
39
+ const ALGORITHM = 'aes-256-gcm';
40
+ const NONCE_LENGTH = 12; // 96 bits for AES-GCM
41
+ const AUTH_TAG_LENGTH = 16; // 128 bits for AES-GCM
42
+ /**
43
+ * Encrypts plaintext using AES-256-GCM
44
+ *
45
+ * @param plaintext - Text to encrypt
46
+ * @param key - 32-byte encryption key (AES-256)
47
+ * @returns EncryptedPayload with Base64-encoded ciphertext, nonce, and authTag
48
+ */
49
+ function encrypt(plaintext, key) {
50
+ if (key.length !== 32) {
51
+ throw new Error('Encryption key must be 32 bytes (256 bits)');
52
+ }
53
+ // Generate random nonce (12 bytes)
54
+ const nonce = crypto.randomBytes(NONCE_LENGTH);
55
+ // Create cipher
56
+ const cipher = crypto.createCipheriv(ALGORITHM, key, nonce);
57
+ // Encrypt
58
+ const ciphertext = Buffer.concat([
59
+ cipher.update(plaintext, 'utf8'),
60
+ cipher.final(),
61
+ ]);
62
+ // Get authentication tag
63
+ const authTag = cipher.getAuthTag();
64
+ if (authTag.length !== AUTH_TAG_LENGTH) {
65
+ throw new Error('Auth tag must be 16 bytes');
66
+ }
67
+ return {
68
+ ciphertext: ciphertext.toString('base64'),
69
+ nonce: nonce.toString('base64'),
70
+ authTag: authTag.toString('base64'),
71
+ };
72
+ }
73
+ /**
74
+ * Decrypts ciphertext using AES-256-GCM
75
+ *
76
+ * @param payload - EncryptedPayload with Base64-encoded ciphertext, nonce, and authTag
77
+ * @param key - 32-byte encryption key (AES-256)
78
+ * @returns Decrypted plaintext
79
+ * @throws Error if decryption fails (wrong key, tampered data, etc.)
80
+ */
81
+ function decrypt(payload, key) {
82
+ if (key.length !== 32) {
83
+ throw new Error('Decryption key must be 32 bytes (256 bits)');
84
+ }
85
+ // Decode from Base64
86
+ const ciphertext = Buffer.from(payload.ciphertext, 'base64');
87
+ const nonce = Buffer.from(payload.nonce, 'base64');
88
+ const authTag = Buffer.from(payload.authTag, 'base64');
89
+ if (nonce.length !== NONCE_LENGTH) {
90
+ throw new Error('Nonce must be 12 bytes');
91
+ }
92
+ if (authTag.length !== AUTH_TAG_LENGTH) {
93
+ throw new Error('Auth tag must be 16 bytes');
94
+ }
95
+ try {
96
+ // Create decipher
97
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, nonce);
98
+ // Set auth tag for verification
99
+ decipher.setAuthTag(authTag);
100
+ // Decrypt
101
+ const plaintext = Buffer.concat([
102
+ decipher.update(ciphertext),
103
+ decipher.final(),
104
+ ]);
105
+ return plaintext.toString('utf8');
106
+ }
107
+ catch (error) {
108
+ throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'unknown error'}`);
109
+ }
110
+ }
111
+ //# sourceMappingURL=encryption.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/crypto/encryption.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Computes X25519 shared secret using ECDH
3
+ *
4
+ * @param privateKey - Base64-encoded X25519 private key (32 bytes)
5
+ * @param publicKey - Base64-encoded X25519 public key (32 bytes)
6
+ * @returns Shared secret (32 bytes)
7
+ */
8
+ export declare function computeSharedSecret(privateKey: string, publicKey: string): Uint8Array;
9
+ /**
10
+ * Derives a session encryption key from a shared secret using HKDF
11
+ *
12
+ * @param sharedSecret - Shared secret from X25519 key exchange
13
+ * @returns Derived session key (32 bytes for AES-256)
14
+ */
15
+ export declare function deriveSessionKey(sharedSecret: Uint8Array): Uint8Array;
16
+ /**
17
+ * Performs complete key exchange: computes shared secret and derives session key
18
+ *
19
+ * @param myPrivateKey - My Base64-encoded X25519 private key
20
+ * @param theirPublicKey - Their Base64-encoded X25519 public key
21
+ * @returns Derived session encryption key (32 bytes for AES-256-GCM)
22
+ */
23
+ export declare function performKeyExchange(myPrivateKey: string, theirPublicKey: string): Uint8Array;
24
+ //# sourceMappingURL=keyExchange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyExchange.d.ts","sourceRoot":"","sources":["../../src/crypto/keyExchange.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,UAAU,CA8CZ;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,UAAU,GAAG,UAAU,CAoBrE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,GACrB,UAAU,CAIZ"}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.computeSharedSecret = computeSharedSecret;
37
+ exports.deriveSessionKey = deriveSessionKey;
38
+ exports.performKeyExchange = performKeyExchange;
39
+ const crypto = __importStar(require("crypto"));
40
+ /**
41
+ * Computes X25519 shared secret using ECDH
42
+ *
43
+ * @param privateKey - Base64-encoded X25519 private key (32 bytes)
44
+ * @param publicKey - Base64-encoded X25519 public key (32 bytes)
45
+ * @returns Shared secret (32 bytes)
46
+ */
47
+ function computeSharedSecret(privateKey, publicKey) {
48
+ const privateKeyBytes = Buffer.from(privateKey, 'base64');
49
+ const publicKeyBytes = Buffer.from(publicKey, 'base64');
50
+ if (privateKeyBytes.length !== 32) {
51
+ throw new Error('Private key must be 32 bytes');
52
+ }
53
+ if (publicKeyBytes.length !== 32) {
54
+ throw new Error('Public key must be 32 bytes');
55
+ }
56
+ // Create X25519 private key object from raw bytes
57
+ const privateKeyObject = crypto.createPrivateKey({
58
+ key: Buffer.concat([
59
+ // PKCS#8 header for X25519
60
+ Buffer.from([
61
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e,
62
+ 0x04, 0x22, 0x04, 0x20,
63
+ ]),
64
+ privateKeyBytes,
65
+ ]),
66
+ format: 'der',
67
+ type: 'pkcs8',
68
+ });
69
+ // Create X25519 public key object from raw bytes
70
+ const publicKeyObject = crypto.createPublicKey({
71
+ key: Buffer.concat([
72
+ // SPKI header for X25519
73
+ Buffer.from([
74
+ 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00,
75
+ ]),
76
+ publicKeyBytes,
77
+ ]),
78
+ format: 'der',
79
+ type: 'spki',
80
+ });
81
+ // Perform ECDH to compute shared secret
82
+ const sharedSecret = crypto.diffieHellman({
83
+ privateKey: privateKeyObject,
84
+ publicKey: publicKeyObject,
85
+ });
86
+ return new Uint8Array(sharedSecret);
87
+ }
88
+ /**
89
+ * Derives a session encryption key from a shared secret using HKDF
90
+ *
91
+ * @param sharedSecret - Shared secret from X25519 key exchange
92
+ * @returns Derived session key (32 bytes for AES-256)
93
+ */
94
+ function deriveSessionKey(sharedSecret) {
95
+ if (sharedSecret.length !== 32) {
96
+ throw new Error('Shared secret must be 32 bytes');
97
+ }
98
+ // Use HKDF-SHA256 to derive session key
99
+ // Info string provides domain separation
100
+ const info = Buffer.from('forkoff-e2ee-session-key-v1', 'utf8');
101
+ const salt = Buffer.alloc(0); // Empty salt (optional for HKDF)
102
+ // Use synchronous HKDF (blocking but fast for 32-byte output)
103
+ const derivedKey = crypto.hkdfSync('sha256', Buffer.from(sharedSecret), salt, info, 32 // Output length: 32 bytes for AES-256
104
+ );
105
+ return new Uint8Array(derivedKey);
106
+ }
107
+ /**
108
+ * Performs complete key exchange: computes shared secret and derives session key
109
+ *
110
+ * @param myPrivateKey - My Base64-encoded X25519 private key
111
+ * @param theirPublicKey - Their Base64-encoded X25519 public key
112
+ * @returns Derived session encryption key (32 bytes for AES-256-GCM)
113
+ */
114
+ function performKeyExchange(myPrivateKey, theirPublicKey) {
115
+ const sharedSecret = computeSharedSecret(myPrivateKey, theirPublicKey);
116
+ const sessionKey = deriveSessionKey(sharedSecret);
117
+ return sessionKey;
118
+ }
119
+ //# sourceMappingURL=keyExchange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyExchange.js","sourceRoot":"","sources":["../../src/crypto/keyExchange.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { E2EEKeyPair } from './types';
2
+ /**
3
+ * Generates a random X25519 key pair for E2EE
4
+ * Uses Node.js crypto module
5
+ *
6
+ * @returns E2EEKeyPair with Base64-encoded public and private keys (32 bytes each)
7
+ */
8
+ export declare function generateKeyPair(): E2EEKeyPair;
9
+ /**
10
+ * Generates a deterministic X25519 key pair from a seed
11
+ * Useful for testing and key derivation
12
+ *
13
+ * @param seed - 32-byte buffer to use as the private key seed
14
+ * @returns E2EEKeyPair with Base64-encoded public and private keys
15
+ * @throws Error if seed is not 32 bytes
16
+ */
17
+ export declare function generateKeyPairFromSeed(seed: Buffer): E2EEKeyPair;
18
+ //# sourceMappingURL=keyGeneration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyGeneration.d.ts","sourceRoot":"","sources":["../../src/crypto/keyGeneration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAsB7C;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA6BjE"}