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

aead.mddocs/

0

# Authenticated Encryption with Additional Data (AEAD)

1

2

Modern authenticated encryption schemes using ChaCha20-Poly1305 and XChaCha20-Poly1305 for secure encryption with additional data authentication.

3

4

## Capabilities

5

6

### XChaCha20-Poly1305-IETF AEAD

7

8

Extended nonce authenticated encryption suitable for high-volume applications.

9

10

```javascript { .api }

11

/**

12

* Generate random key for XChaCha20-Poly1305-IETF AEAD

13

* @param k - Output buffer for key (must be KEYBYTES long)

14

*/

15

function crypto_aead_xchacha20poly1305_ietf_keygen(k: Buffer): void;

16

17

/**

18

* Encrypt message with XChaCha20-Poly1305-IETF AEAD

19

* @param c - Output buffer for ciphertext (must be m.length + ABYTES)

20

* @param m - Message buffer to encrypt

21

* @param ad - Additional data buffer to authenticate (can be null)

22

* @param nsec - Must always be null (reserved parameter)

23

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

24

* @param k - Key buffer (must be KEYBYTES long)

25

* @returns Number of bytes written to ciphertext buffer

26

* @throws Error if encryption fails or parameters invalid

27

*/

28

function crypto_aead_xchacha20poly1305_ietf_encrypt(

29

c: Buffer,

30

m: Buffer,

31

ad: Buffer | null,

32

nsec: null,

33

npub: Buffer,

34

k: Buffer

35

): number;

36

37

/**

38

* Decrypt and verify with XChaCha20-Poly1305-IETF AEAD

39

* @param m - Output buffer for plaintext (must be c.length - ABYTES)

40

* @param nsec - Must always be null (reserved parameter)

41

* @param c - Ciphertext buffer to decrypt

42

* @param ad - Additional data buffer that was authenticated (can be null)

43

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

44

* @param k - Key buffer (must be KEYBYTES long)

45

* @returns Number of bytes written to plaintext buffer

46

* @throws Error if decryption fails or authentication invalid

47

*/

48

function crypto_aead_xchacha20poly1305_ietf_decrypt(

49

m: Buffer,

50

nsec: null,

51

c: Buffer,

52

ad: Buffer | null,

53

npub: Buffer,

54

k: Buffer

55

): number;

56

```

57

58

**Usage Example:**

59

60

```javascript

61

const sodium = require('sodium-native');

62

63

// Generate key and nonce

64

const key = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES);

65

const nonce = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

66

sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key);

67

sodium.randombytes_buf(nonce);

68

69

// Encrypt with additional data

70

const message = Buffer.from('Secret message');

71

const additionalData = Buffer.from('header info');

72

const ciphertext = Buffer.alloc(message.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);

73

74

const cipherLen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(

75

ciphertext, message, additionalData, null, nonce, key

76

);

77

78

// Decrypt and verify

79

const plaintext = Buffer.alloc(ciphertext.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);

80

const plainLen = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(

81

plaintext, null, ciphertext, additionalData, nonce, key

82

);

83

84

console.log('Decrypted:', plaintext.subarray(0, plainLen).toString());

85

```

86

87

### XChaCha20-Poly1305-IETF Detached Mode

88

89

Encrypt with authentication tag stored separately from ciphertext.

90

91

```javascript { .api }

92

/**

93

* Encrypt with XChaCha20-Poly1305-IETF AEAD (detached mode)

94

* @param c - Output buffer for ciphertext (must be same length as message)

95

* @param mac - Output buffer for authentication tag (must be ABYTES long)

96

* @param m - Message buffer to encrypt

97

* @param ad - Additional data buffer to authenticate (can be null)

98

* @param nsec - Must always be null (reserved parameter)

99

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

100

* @param k - Key buffer (must be KEYBYTES long)

101

* @returns Number of bytes written to ciphertext buffer

102

* @throws Error if encryption fails

103

*/

104

function crypto_aead_xchacha20poly1305_ietf_encrypt_detached(

105

c: Buffer,

106

mac: Buffer,

107

m: Buffer,

108

ad: Buffer | null,

109

nsec: null,

110

npub: Buffer,

111

k: Buffer

112

): number;

113

114

/**

115

* Decrypt with XChaCha20-Poly1305-IETF AEAD (detached mode)

116

* @param m - Output buffer for plaintext (must be same length as ciphertext)

117

* @param nsec - Must always be null (reserved parameter)

118

* @param c - Ciphertext buffer to decrypt

119

* @param mac - Authentication tag buffer (must be ABYTES long)

120

* @param ad - Additional data buffer that was authenticated (can be null)

121

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

122

* @param k - Key buffer (must be KEYBYTES long)

123

* @throws Error if decryption fails or authentication invalid

124

*/

125

function crypto_aead_xchacha20poly1305_ietf_decrypt_detached(

126

m: Buffer,

127

nsec: null,

128

c: Buffer,

129

mac: Buffer,

130

ad: Buffer | null,

131

npub: Buffer,

132

k: Buffer

133

): void;

134

```

135

136

### ChaCha20-Poly1305-IETF AEAD

137

138

Standard IETF ChaCha20-Poly1305 authenticated encryption.

139

140

```javascript { .api }

141

/**

142

* Generate random key for ChaCha20-Poly1305-IETF AEAD

143

* @param k - Output buffer for key (must be KEYBYTES long)

144

*/

145

function crypto_aead_chacha20poly1305_ietf_keygen(k: Buffer): void;

146

147

/**

148

* Encrypt message with ChaCha20-Poly1305-IETF AEAD

149

* @param c - Output buffer for ciphertext (must be m.length + ABYTES)

150

* @param m - Message buffer to encrypt

151

* @param ad - Additional data buffer to authenticate (can be null)

152

* @param nsec - Must always be null (reserved parameter)

153

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

154

* @param k - Key buffer (must be KEYBYTES long)

155

* @returns Number of bytes written to ciphertext buffer

156

* @throws Error if encryption fails or parameters invalid

157

*/

158

function crypto_aead_chacha20poly1305_ietf_encrypt(

159

c: Buffer,

160

m: Buffer,

161

ad: Buffer | null,

162

nsec: null,

163

npub: Buffer,

164

k: Buffer

165

): number;

166

167

/**

168

* Decrypt and verify with ChaCha20-Poly1305-IETF AEAD

169

* @param m - Output buffer for plaintext (must be c.length - ABYTES)

170

* @param nsec - Must always be null (reserved parameter)

171

* @param c - Ciphertext buffer to decrypt

172

* @param ad - Additional data buffer that was authenticated (can be null)

173

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

174

* @param k - Key buffer (must be KEYBYTES long)

175

* @returns Number of bytes written to plaintext buffer

176

* @throws Error if decryption fails or authentication invalid

177

*/

178

function crypto_aead_chacha20poly1305_ietf_decrypt(

179

m: Buffer,

180

nsec: null,

181

c: Buffer,

182

ad: Buffer | null,

183

npub: Buffer,

184

k: Buffer

185

): number;

186

```

187

188

### ChaCha20-Poly1305-IETF Detached Mode

189

190

```javascript { .api }

191

/**

192

* Encrypt with ChaCha20-Poly1305-IETF AEAD (detached mode)

193

* @param c - Output buffer for ciphertext (must be same length as message)

194

* @param mac - Output buffer for authentication tag (must be ABYTES long)

195

* @param m - Message buffer to encrypt

196

* @param ad - Additional data buffer to authenticate (can be null)

197

* @param nsec - Must always be null (reserved parameter)

198

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

199

* @param k - Key buffer (must be KEYBYTES long)

200

* @returns Number of bytes written to ciphertext buffer

201

* @throws Error if encryption fails

202

*/

203

function crypto_aead_chacha20poly1305_ietf_encrypt_detached(

204

c: Buffer,

205

mac: Buffer,

206

m: Buffer,

207

ad: Buffer | null,

208

nsec: null,

209

npub: Buffer,

210

k: Buffer

211

): number;

212

213

/**

214

* Decrypt with ChaCha20-Poly1305-IETF AEAD (detached mode)

215

* @param m - Output buffer for plaintext (must be same length as ciphertext)

216

* @param nsec - Must always be null (reserved parameter)

217

* @param c - Ciphertext buffer to decrypt

218

* @param mac - Authentication tag buffer (must be ABYTES long)

219

* @param ad - Additional data buffer that was authenticated (can be null)

220

* @param npub - Nonce/public number buffer (must be NPUBBYTES long)

221

* @param k - Key buffer (must be KEYBYTES long)

222

* @throws Error if decryption fails or authentication invalid

223

*/

224

function crypto_aead_chacha20poly1305_ietf_decrypt_detached(

225

m: Buffer,

226

nsec: null,

227

c: Buffer,

228

mac: Buffer,

229

ad: Buffer | null,

230

npub: Buffer,

231

k: Buffer

232

): void;

233

```

234

235

**Usage Example:**

236

237

```javascript

238

const sodium = require('sodium-native');

239

240

// Standard ChaCha20-Poly1305-IETF

241

const key = Buffer.alloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES);

242

const nonce = Buffer.alloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES);

243

sodium.crypto_aead_chacha20poly1305_ietf_keygen(key);

244

sodium.randombytes_buf(nonce);

245

246

const message = Buffer.from('Important data');

247

const header = Buffer.from('v1.0|json|gzip');

248

249

// Encrypt with header as additional data

250

const ciphertext = Buffer.alloc(message.length + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES);

251

const cipherLen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(

252

ciphertext, message, header, null, nonce, key

253

);

254

255

// Decrypt and authenticate header

256

const plaintext = Buffer.alloc(ciphertext.length - sodium.crypto_aead_chacha20poly1305_ietf_ABYTES);

257

const plainLen = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(

258

plaintext, null, ciphertext, header, nonce, key

259

);

260

```

261

262

## Constants

263

264

```javascript { .api }

265

// XChaCha20-Poly1305-IETF constants

266

const crypto_aead_xchacha20poly1305_ietf_ABYTES: number;

267

const crypto_aead_xchacha20poly1305_ietf_KEYBYTES: number;

268

const crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: number;

269

const crypto_aead_xchacha20poly1305_ietf_NSECBYTES: number;

270

const crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: number;

271

272

// ChaCha20-Poly1305-IETF constants

273

const crypto_aead_chacha20poly1305_ietf_ABYTES: number;

274

const crypto_aead_chacha20poly1305_ietf_KEYBYTES: number;

275

const crypto_aead_chacha20poly1305_ietf_NPUBBYTES: number;

276

const crypto_aead_chacha20poly1305_ietf_NSECBYTES: number;

277

const crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX: number;

278

```

279

280

## Security Considerations

281

282

- **Nonce Management**: Never reuse nonces with the same key. XChaCha20 allows larger nonces for easier management.

283

- **Additional Data**: Additional data is authenticated but not encrypted. Use for headers, metadata, etc.

284

- **Key Reuse**: Keys can be safely reused with different nonces, but consider key rotation for long-term use.

285

- **Message Limits**: Respect maximum message size limits for each variant.

286

287

## Common Patterns

288

289

### API Request/Response Encryption

290

291

```javascript

292

const sodium = require('sodium-native');

293

294

class APIEncryption {

295

constructor() {

296

this.key = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES);

297

sodium.crypto_aead_xchacha20poly1305_ietf_keygen(this.key);

298

}

299

300

encryptRequest(method, url, body) {

301

const nonce = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

302

sodium.randombytes_buf(nonce);

303

304

// Use HTTP method and URL as additional data

305

const additionalData = Buffer.from(`${method} ${url}`);

306

const messageBuffer = Buffer.from(JSON.stringify(body));

307

308

const ciphertext = Buffer.alloc(

309

messageBuffer.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

310

);

311

312

const cipherLen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(

313

ciphertext,

314

messageBuffer,

315

additionalData,

316

null,

317

nonce,

318

this.key

319

);

320

321

return {

322

nonce: nonce.toString('base64'),

323

ciphertext: ciphertext.subarray(0, cipherLen).toString('base64'),

324

method,

325

url

326

};

327

}

328

329

decryptRequest(encryptedRequest) {

330

const nonce = Buffer.from(encryptedRequest.nonce, 'base64');

331

const ciphertext = Buffer.from(encryptedRequest.ciphertext, 'base64');

332

const additionalData = Buffer.from(`${encryptedRequest.method} ${encryptedRequest.url}`);

333

334

const plaintext = Buffer.alloc(

335

ciphertext.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

336

);

337

338

const plainLen = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(

339

plaintext,

340

null,

341

ciphertext,

342

additionalData,

343

nonce,

344

this.key

345

);

346

347

return JSON.parse(plaintext.subarray(0, plainLen).toString());

348

}

349

}

350

```

351

352

### Database Field Encryption

353

354

```javascript

355

const sodium = require('sodium-native');

356

357

class FieldEncryption {

358

constructor(masterKey) {

359

this.masterKey = Buffer.from(masterKey);

360

}

361

362

// Derive field-specific key from master key

363

deriveFieldKey(tableName, fieldName) {

364

const context = Buffer.from(`${tableName}.${fieldName}`);

365

const fieldKey = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES);

366

367

// Simple key derivation (use proper KDF in production)

368

const combined = Buffer.concat([this.masterKey, context]);

369

sodium.crypto_generichash(fieldKey, combined);

370

371

return fieldKey;

372

}

373

374

encryptField(tableName, fieldName, value, recordId) {

375

const fieldKey = this.deriveFieldKey(tableName, fieldName);

376

const nonce = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

377

sodium.randombytes_buf(nonce);

378

379

// Use record ID as additional data for integrity

380

const additionalData = Buffer.from(recordId.toString());

381

const plaintext = Buffer.from(value);

382

383

const ciphertext = Buffer.alloc(

384

plaintext.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

385

);

386

387

const cipherLen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(

388

ciphertext,

389

plaintext,

390

additionalData,

391

null,

392

nonce,

393

fieldKey

394

);

395

396

// Return as base64 with nonce prepended

397

const encrypted = Buffer.concat([nonce, ciphertext.subarray(0, cipherLen)]);

398

return encrypted.toString('base64');

399

}

400

401

decryptField(tableName, fieldName, encryptedValue, recordId) {

402

const fieldKey = this.deriveFieldKey(tableName, fieldName);

403

const encrypted = Buffer.from(encryptedValue, 'base64');

404

405

const nonce = encrypted.subarray(0, sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

406

const ciphertext = encrypted.subarray(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

407

const additionalData = Buffer.from(recordId.toString());

408

409

const plaintext = Buffer.alloc(

410

ciphertext.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

411

);

412

413

const plainLen = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(

414

plaintext,

415

null,

416

ciphertext,

417

additionalData,

418

nonce,

419

fieldKey

420

);

421

422

return plaintext.subarray(0, plainLen).toString();

423

}

424

}

425

426

// Usage

427

const encryption = new FieldEncryption('master-key-32-bytes-long-exactly!');

428

429

// Encrypt sensitive field

430

const encryptedSSN = encryption.encryptField('users', 'ssn', '123-45-6789', 12345);

431

432

// Decrypt when needed

433

const originalSSN = encryption.decryptField('users', 'ssn', encryptedSSN, 12345);

434

```

435

436

### File Encryption with Compression

437

438

```javascript

439

const sodium = require('sodium-native');

440

const zlib = require('zlib');

441

442

class SecureFileStorage {

443

constructor() {

444

this.key = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES);

445

sodium.crypto_aead_xchacha20poly1305_ietf_keygen(this.key);

446

}

447

448

async encryptFile(filename, fileData) {

449

// Compress data first

450

const compressed = zlib.gzipSync(fileData);

451

452

const nonce = Buffer.alloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);

453

sodium.randombytes_buf(nonce);

454

455

// Use filename as additional data

456

const additionalData = Buffer.from(filename);

457

458

const ciphertext = Buffer.alloc(

459

compressed.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

460

);

461

462

const cipherLen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(

463

ciphertext,

464

compressed,

465

additionalData,

466

null,

467

nonce,

468

this.key

469

);

470

471

// Return file package with metadata

472

return {

473

filename,

474

nonce: nonce.toString('hex'),

475

ciphertext: ciphertext.subarray(0, cipherLen).toString('hex'),

476

compressed: true,

477

originalSize: fileData.length,

478

encryptedSize: cipherLen

479

};

480

}

481

482

async decryptFile(encryptedFile) {

483

const nonce = Buffer.from(encryptedFile.nonce, 'hex');

484

const ciphertext = Buffer.from(encryptedFile.ciphertext, 'hex');

485

const additionalData = Buffer.from(encryptedFile.filename);

486

487

const compressed = Buffer.alloc(

488

ciphertext.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES

489

);

490

491

const compressedLen = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(

492

compressed,

493

null,

494

ciphertext,

495

additionalData,

496

nonce,

497

this.key

498

);

499

500

// Decompress if compressed

501

if (encryptedFile.compressed) {

502

return zlib.gunzipSync(compressed.subarray(0, compressedLen));

503

}

504

505

return compressed.subarray(0, compressedLen);

506

}

507

}

508

```