0
# Key Exchange
1
2
Diffie-Hellman key exchange using X25519 elliptic curve cryptography for establishing shared secrets between parties.
3
4
## Capabilities
5
6
### Key Pair Generation
7
8
Generate X25519 key pairs for key exchange operations.
9
10
```javascript { .api }
11
/**
12
* Generate random X25519 key pair for key exchange
13
* @param pk - Output buffer for public key (must be PUBLICKEYBYTES long)
14
* @param sk - Output buffer for secret key (must be SECRETKEYBYTES long)
15
* @throws Error if buffer sizes are incorrect or generation fails
16
*/
17
function crypto_kx_keypair(pk: Buffer, sk: Buffer): void;
18
19
/**
20
* Generate X25519 key pair from seed
21
* @param pk - Output buffer for public key (must be PUBLICKEYBYTES long)
22
* @param sk - Output buffer for secret key (must be SECRETKEYBYTES long)
23
* @param seed - Seed buffer (must be SEEDBYTES long)
24
* @throws Error if buffer sizes are incorrect or generation fails
25
*/
26
function crypto_kx_seed_keypair(pk: Buffer, sk: Buffer, seed: Buffer): void;
27
```
28
29
**Usage Example:**
30
31
```javascript
32
const sodium = require('sodium-native');
33
34
// Generate key pairs for Alice and Bob
35
const alicePk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
36
const aliceSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
37
sodium.crypto_kx_keypair(alicePk, aliceSk);
38
39
const bobPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
40
const bobSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
41
sodium.crypto_kx_keypair(bobPk, bobSk);
42
```
43
44
### Client Session Key Derivation
45
46
Derive session keys from the client's perspective.
47
48
```javascript { .api }
49
/**
50
* Derive client session keys for secure communication
51
* @param rx - Output buffer for receiving key (can be null if not needed)
52
* @param tx - Output buffer for transmitting key (can be null if not needed)
53
* @param clientPk - Client's public key (must be PUBLICKEYBYTES long)
54
* @param clientSk - Client's secret key (must be SECRETKEYBYTES long)
55
* @param serverPk - Server's public key (must be PUBLICKEYBYTES long)
56
* @throws Error if both rx and tx are null, or if key derivation fails
57
*/
58
function crypto_kx_client_session_keys(
59
rx: Buffer | null,
60
tx: Buffer | null,
61
clientPk: Buffer,
62
clientSk: Buffer,
63
serverPk: Buffer
64
): void;
65
```
66
67
### Server Session Key Derivation
68
69
Derive session keys from the server's perspective.
70
71
```javascript { .api }
72
/**
73
* Derive server session keys for secure communication
74
* @param rx - Output buffer for receiving key (can be null if not needed)
75
* @param tx - Output buffer for transmitting key (can be null if not needed)
76
* @param serverPk - Server's public key (must be PUBLICKEYBYTES long)
77
* @param serverSk - Server's secret key (must be SECRETKEYBYTES long)
78
* @param clientPk - Client's public key (must be PUBLICKEYBYTES long)
79
* @throws Error if both rx and tx are null, or if key derivation fails
80
*/
81
function crypto_kx_server_session_keys(
82
rx: Buffer | null,
83
tx: Buffer | null,
84
serverPk: Buffer,
85
serverSk: Buffer,
86
clientPk: Buffer
87
): void;
88
```
89
90
**Usage Example:**
91
92
```javascript
93
const sodium = require('sodium-native');
94
95
// After key pair generation...
96
97
// Client derives session keys
98
const clientRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
99
const clientTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
100
101
sodium.crypto_kx_client_session_keys(
102
clientRx, // Key for receiving data from server
103
clientTx, // Key for transmitting data to server
104
alicePk, // Client's public key
105
aliceSk, // Client's secret key
106
bobPk // Server's public key
107
);
108
109
// Server derives session keys
110
const serverRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
111
const serverTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
112
113
sodium.crypto_kx_server_session_keys(
114
serverRx, // Key for receiving data from client
115
serverTx, // Key for transmitting data to client
116
bobPk, // Server's public key
117
bobSk, // Server's secret key
118
alicePk // Client's public key
119
);
120
121
// Now clientTx === serverRx and serverTx === clientRx
122
console.log('Keys match:', clientTx.equals(serverRx) && serverTx.equals(clientRx));
123
```
124
125
## Constants
126
127
```javascript { .api }
128
// Public key size in bytes
129
const crypto_kx_PUBLICKEYBYTES: number;
130
131
// Secret key size in bytes
132
const crypto_kx_SECRETKEYBYTES: number;
133
134
// Seed size for deterministic key generation
135
const crypto_kx_SEEDBYTES: number;
136
137
// Session key size in bytes
138
const crypto_kx_SESSIONKEYBYTES: number;
139
```
140
141
## Security Considerations
142
143
- **Key Reuse**: X25519 key pairs can be reused for multiple key exchanges with different parties.
144
- **Forward Secrecy**: For forward secrecy, generate ephemeral key pairs for each session.
145
- **Key Validation**: Always validate public keys received from other parties.
146
- **Session Keys**: Use derived session keys immediately and securely delete them after use.
147
148
## Common Patterns
149
150
### Secure Communication Protocol
151
152
```javascript
153
const sodium = require('sodium-native');
154
155
class SecureChannel {
156
constructor(role = 'client') {
157
this.role = role;
158
159
// Generate long-term identity key pair
160
this.identityPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
161
this.identitySk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
162
sodium.crypto_kx_keypair(this.identityPk, this.identitySk);
163
164
this.sessionKeys = null;
165
}
166
167
// Generate ephemeral key pair for this session
168
generateEphemeralKeys() {
169
this.ephemeralPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
170
this.ephemeralSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
171
sodium.crypto_kx_keypair(this.ephemeralPk, this.ephemeralSk);
172
173
return this.ephemeralPk;
174
}
175
176
// Establish secure session with peer
177
establishSession(peerPublicKey) {
178
if (!this.ephemeralPk || !this.ephemeralSk) {
179
throw new Error('Must generate ephemeral keys first');
180
}
181
182
const rx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
183
const tx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
184
185
if (this.role === 'client') {
186
sodium.crypto_kx_client_session_keys(
187
rx, tx,
188
this.ephemeralPk,
189
this.ephemeralSk,
190
peerPublicKey
191
);
192
} else {
193
sodium.crypto_kx_server_session_keys(
194
rx, tx,
195
this.ephemeralPk,
196
this.ephemeralSk,
197
peerPublicKey
198
);
199
}
200
201
this.sessionKeys = { rx, tx };
202
203
// Clean up ephemeral secret key
204
sodium.sodium_memzero(this.ephemeralSk);
205
206
return this.sessionKeys;
207
}
208
209
// Encrypt message using session key
210
encrypt(message) {
211
if (!this.sessionKeys) {
212
throw new Error('Session not established');
213
}
214
215
const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES);
216
sodium.randombytes_buf(nonce);
217
218
const ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES);
219
sodium.crypto_secretbox_easy(ciphertext, message, nonce, this.sessionKeys.tx);
220
221
return { ciphertext, nonce };
222
}
223
224
// Decrypt message using session key
225
decrypt(ciphertext, nonce) {
226
if (!this.sessionKeys) {
227
throw new Error('Session not established');
228
}
229
230
const plaintext = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES);
231
232
if (sodium.crypto_secretbox_open_easy(plaintext, ciphertext, nonce, this.sessionKeys.rx)) {
233
return plaintext;
234
}
235
236
return null;
237
}
238
}
239
240
// Usage
241
const client = new SecureChannel('client');
242
const server = new SecureChannel('server');
243
244
// Key exchange
245
const clientEphemeralPk = client.generateEphemeralKeys();
246
const serverEphemeralPk = server.generateEphemeralKeys();
247
248
client.establishSession(serverEphemeralPk);
249
server.establishSession(clientEphemeralPk);
250
251
// Secure communication
252
const message = Buffer.from('Hello from client!');
253
const encrypted = client.encrypt(message);
254
const decrypted = server.decrypt(encrypted.ciphertext, encrypted.nonce);
255
256
console.log('Decrypted:', decrypted.toString());
257
```
258
259
### Multi-party Key Exchange
260
261
```javascript
262
const sodium = require('sodium-native');
263
264
class MultiPartyKeyExchange {
265
constructor(participantId) {
266
this.participantId = participantId;
267
this.publicKey = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
268
this.secretKey = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
269
sodium.crypto_kx_keypair(this.publicKey, this.secretKey);
270
271
this.sharedKeys = new Map();
272
}
273
274
// Establish pairwise shared keys with other participants
275
establishPairwiseKeys(participants) {
276
for (const [id, publicKey] of participants) {
277
if (id === this.participantId) continue;
278
279
const sharedKey = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
280
281
// Use consistent role assignment based on ID comparison
282
if (this.participantId < id) {
283
// Act as client
284
sodium.crypto_kx_client_session_keys(
285
sharedKey, null,
286
this.publicKey,
287
this.secretKey,
288
publicKey
289
);
290
} else {
291
// Act as server
292
sodium.crypto_kx_server_session_keys(
293
sharedKey, null,
294
this.publicKey,
295
this.secretKey,
296
publicKey
297
);
298
}
299
300
this.sharedKeys.set(id, sharedKey);
301
}
302
}
303
304
// Get shared key with specific participant
305
getSharedKey(participantId) {
306
return this.sharedKeys.get(participantId);
307
}
308
309
// Encrypt message for specific participant
310
encryptFor(participantId, message) {
311
const sharedKey = this.sharedKeys.get(participantId);
312
if (!sharedKey) {
313
throw new Error(`No shared key with participant ${participantId}`);
314
}
315
316
const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES);
317
sodium.randombytes_buf(nonce);
318
319
const ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES);
320
sodium.crypto_secretbox_easy(ciphertext, message, nonce, sharedKey);
321
322
return { ciphertext, nonce };
323
}
324
}
325
```
326
327
### Perfect Forward Secrecy Protocol
328
329
```javascript
330
const sodium = require('sodium-native');
331
332
class PFSProtocol {
333
constructor() {
334
// Long-term identity keys
335
this.identityPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
336
this.identitySk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
337
sodium.crypto_kx_keypair(this.identityPk, this.identitySk);
338
339
this.sessions = new Map();
340
}
341
342
// Create new session with forward secrecy
343
createSession(sessionId, peerIdentityPk) {
344
// Generate ephemeral keys for this session
345
const ephemeralPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES);
346
const ephemeralSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES);
347
sodium.crypto_kx_keypair(ephemeralPk, ephemeralSk);
348
349
const session = {
350
ephemeralPk,
351
ephemeralSk,
352
peerIdentityPk,
353
peerEphemeralPk: null,
354
sessionKeys: null,
355
messageCounter: 0
356
};
357
358
this.sessions.set(sessionId, session);
359
return ephemeralPk;
360
}
361
362
// Complete session establishment
363
completeSession(sessionId, peerEphemeralPk) {
364
const session = this.sessions.get(sessionId);
365
if (!session) {
366
throw new Error('Session not found');
367
}
368
369
session.peerEphemeralPk = peerEphemeralPk;
370
371
// Derive session keys using ephemeral keys
372
const rx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
373
const tx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES);
374
375
sodium.crypto_kx_client_session_keys(
376
rx, tx,
377
session.ephemeralPk,
378
session.ephemeralSk,
379
peerEphemeralPk
380
);
381
382
session.sessionKeys = { rx, tx };
383
384
// Immediately destroy ephemeral secret keys
385
sodium.sodium_memzero(session.ephemeralSk);
386
}
387
388
// Destroy session and all keys
389
destroySession(sessionId) {
390
const session = this.sessions.get(sessionId);
391
if (session) {
392
if (session.sessionKeys) {
393
sodium.sodium_memzero(session.sessionKeys.rx);
394
sodium.sodium_memzero(session.sessionKeys.tx);
395
}
396
this.sessions.delete(sessionId);
397
}
398
}
399
}
400
```