or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aead.mdauth.mdbox.mded25519.mdhash.mdindex.mdkdf.mdkx.mdmemory.mdpwhash.mdrandom.mdsecretbox.mdsecretstream.mdshorthash.mdsign.mdstream.md

kx.mddocs/

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

```