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.
- package/E2EE-COMPLETE.md +290 -0
- package/README.md +11 -0
- package/dist/__tests__/crypto/e2e-integration.test.d.ts +17 -0
- package/dist/__tests__/crypto/e2e-integration.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/e2e-integration.test.js +338 -0
- package/dist/__tests__/crypto/e2e-integration.test.js.map +1 -0
- package/dist/__tests__/crypto/e2eeManager.test.d.ts +2 -0
- package/dist/__tests__/crypto/e2eeManager.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/e2eeManager.test.js +242 -0
- package/dist/__tests__/crypto/e2eeManager.test.js.map +1 -0
- package/dist/__tests__/crypto/encryption.test.d.ts +2 -0
- package/dist/__tests__/crypto/encryption.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/encryption.test.js +116 -0
- package/dist/__tests__/crypto/encryption.test.js.map +1 -0
- package/dist/__tests__/crypto/keyExchange.test.d.ts +2 -0
- package/dist/__tests__/crypto/keyExchange.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/keyExchange.test.js +84 -0
- package/dist/__tests__/crypto/keyExchange.test.js.map +1 -0
- package/dist/__tests__/crypto/keyGeneration.test.d.ts +2 -0
- package/dist/__tests__/crypto/keyGeneration.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/keyGeneration.test.js +61 -0
- package/dist/__tests__/crypto/keyGeneration.test.js.map +1 -0
- package/dist/__tests__/crypto/keyStorage.test.d.ts +2 -0
- package/dist/__tests__/crypto/keyStorage.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/keyStorage.test.js +133 -0
- package/dist/__tests__/crypto/keyStorage.test.js.map +1 -0
- package/dist/__tests__/crypto/websocketIntegration.test.d.ts +2 -0
- package/dist/__tests__/crypto/websocketIntegration.test.d.ts.map +1 -0
- package/dist/__tests__/crypto/websocketIntegration.test.js +259 -0
- package/dist/__tests__/crypto/websocketIntegration.test.js.map +1 -0
- package/dist/crypto/e2eeManager.d.ts +82 -0
- package/dist/crypto/e2eeManager.d.ts.map +1 -0
- package/dist/crypto/e2eeManager.js +270 -0
- package/dist/crypto/e2eeManager.js.map +1 -0
- package/dist/crypto/encryption.d.ts +19 -0
- package/dist/crypto/encryption.d.ts.map +1 -0
- package/dist/crypto/encryption.js +111 -0
- package/dist/crypto/encryption.js.map +1 -0
- package/dist/crypto/keyExchange.d.ts +24 -0
- package/dist/crypto/keyExchange.d.ts.map +1 -0
- package/dist/crypto/keyExchange.js +119 -0
- package/dist/crypto/keyExchange.js.map +1 -0
- package/dist/crypto/keyGeneration.d.ts +18 -0
- package/dist/crypto/keyGeneration.d.ts.map +1 -0
- package/dist/crypto/keyGeneration.js +99 -0
- package/dist/crypto/keyGeneration.js.map +1 -0
- package/dist/crypto/keyStorage.d.ts +39 -0
- package/dist/crypto/keyStorage.d.ts.map +1 -0
- package/dist/crypto/keyStorage.js +117 -0
- package/dist/crypto/keyStorage.js.map +1 -0
- package/dist/crypto/sessionPersistence.d.ts +33 -0
- package/dist/crypto/sessionPersistence.d.ts.map +1 -0
- package/dist/crypto/sessionPersistence.js +173 -0
- package/dist/crypto/sessionPersistence.js.map +1 -0
- package/dist/crypto/types.d.ts +35 -0
- package/dist/crypto/types.d.ts.map +1 -0
- package/dist/crypto/types.js +8 -0
- package/dist/crypto/types.js.map +1 -0
- package/dist/crypto/websocketE2EE.d.ts +47 -0
- package/dist/crypto/websocketE2EE.d.ts.map +1 -0
- package/dist/crypto/websocketE2EE.js +144 -0
- package/dist/crypto/websocketE2EE.js.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -1
- package/dist/websocket.d.ts +26 -1
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +25 -1
- package/dist/websocket.js.map +1 -1
- package/jest.config.js +15 -0
- 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"}
|