or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aead.mdauth.mdbox.mdhash.mdindex.mdkey-derivation.mdsecretbox.mdsign.mdstreaming.mdutilities.md
tile.json

secretbox.mddocs/

0

# Secret-Key Encryption (SecretBox)

1

2

Secret-key encryption functions provide fast symmetric encryption with authentication using shared secrets. The "secretbox" functions combine stream ciphers with polynomial authentication for both confidentiality and integrity.

3

4

## Supported Algorithms

5

6

- **XSalsa20Poly1305**: Original NaCl-compatible algorithm (default)

7

- **XChaCha20Poly1305**: Extended nonce variant for better security margins

8

9

## Generic SecretBox (XSalsa20Poly1305)

10

11

The default secretbox implementation using XSalsa20 stream cipher with Poly1305 authentication.

12

13

### Key Generation

14

15

```javascript { .api }

16

/**

17

* Generate a random key for secret-key encryption

18

* @returns Uint8Array - 32-byte encryption key

19

*/

20

function crypto_secretbox_keygen(): Uint8Array;

21

```

22

23

### Encryption

24

25

```javascript { .api }

26

/**

27

* Encrypt and authenticate data using secret-key cryptography

28

* @param message - Plaintext data to encrypt

29

* @param nonce - 24-byte nonce (must be unique per key)

30

* @param key - 32-byte encryption key

31

* @returns Uint8Array - Encrypted data with authentication tag

32

* @throws Error on invalid parameters

33

*/

34

function crypto_secretbox_easy(

35

message: Uint8Array,

36

nonce: Uint8Array,

37

key: Uint8Array

38

): Uint8Array;

39

```

40

41

### Decryption

42

43

```javascript { .api }

44

/**

45

* Decrypt and verify data using secret-key cryptography

46

* @param ciphertext - Encrypted data with authentication tag

47

* @param nonce - 24-byte nonce used for encryption

48

* @param key - 32-byte decryption key

49

* @returns Uint8Array - Decrypted plaintext

50

* @throws Error if authentication fails or wrong key

51

*/

52

function crypto_secretbox_open_easy(

53

ciphertext: Uint8Array,

54

nonce: Uint8Array,

55

key: Uint8Array

56

): Uint8Array;

57

```

58

59

### Detached Operations

60

61

```javascript { .api }

62

/**

63

* Encrypt with separate authentication tag

64

* @param message - Plaintext to encrypt

65

* @param nonce - 24-byte nonce

66

* @param key - 32-byte encryption key

67

* @returns Object with cipher and mac properties

68

*/

69

function crypto_secretbox_detached(

70

message: Uint8Array,

71

nonce: Uint8Array,

72

key: Uint8Array

73

): { cipher: Uint8Array; mac: Uint8Array };

74

75

/**

76

* Decrypt with separate authentication tag

77

* @param ciphertext - Encrypted data (without tag)

78

* @param mac - 16-byte authentication tag

79

* @param nonce - 24-byte nonce

80

* @param key - 32-byte decryption key

81

* @returns Uint8Array - Decrypted plaintext

82

* @throws Error if authentication fails

83

*/

84

function crypto_secretbox_open_detached(

85

ciphertext: Uint8Array,

86

mac: Uint8Array,

87

nonce: Uint8Array,

88

key: Uint8Array

89

): Uint8Array;

90

```

91

92

### Constants

93

94

```javascript { .api }

95

const crypto_secretbox_KEYBYTES: number; // 32 (key length)

96

const crypto_secretbox_NONCEBYTES: number; // 24 (nonce length)

97

const crypto_secretbox_MACBYTES: number; // 16 (authentication tag length)

98

const crypto_secretbox_MESSAGEBYTES_MAX: number; // Maximum message size

99

```

100

101

## XChaCha20Poly1305 Variant

102

103

The XChaCha20Poly1305 variant provides the same API but with extended nonce space for better security.

104

105

### Key Generation

106

107

```javascript { .api }

108

/**

109

* Generate key for XChaCha20Poly1305 (compatible with generic)

110

* @returns Uint8Array - 32-byte encryption key

111

*/

112

// Uses same crypto_secretbox_keygen() function

113

```

114

115

### Encryption and Decryption

116

117

```javascript { .api }

118

/**

119

* Encrypt using XChaCha20-Poly1305

120

* @param message - Plaintext to encrypt

121

* @param nonce - 24-byte nonce (can be random with XChaCha20)

122

* @param key - 32-byte encryption key

123

* @returns Uint8Array - Encrypted data with tag

124

*/

125

// Note: XChaCha20 functions have same signatures as generic secretbox

126

// The library automatically selects the appropriate algorithm

127

```

128

129

### XChaCha20 Constants

130

131

```javascript { .api }

132

const crypto_secretbox_xchacha20poly1305_KEYBYTES: number; // 32

133

const crypto_secretbox_xchacha20poly1305_NONCEBYTES: number; // 24

134

const crypto_secretbox_xchacha20poly1305_MACBYTES: number; // 16

135

const crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX: number; // Large value

136

```

137

138

### XSalsa20 Legacy Constants

139

140

```javascript { .api }

141

const crypto_secretbox_xsalsa20poly1305_KEYBYTES: number; // 32

142

const crypto_secretbox_xsalsa20poly1305_NONCEBYTES: number; // 24

143

const crypto_secretbox_xsalsa20poly1305_MACBYTES: number; // 16

144

const crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX: number; // Large value

145

```

146

147

## Usage Examples

148

149

### Basic Secret-Key Encryption

150

151

```javascript

152

import _sodium from 'libsodium-wrappers-sumo';

153

await _sodium.ready;

154

const sodium = _sodium;

155

156

// Generate encryption key

157

const key = sodium.crypto_secretbox_keygen();

158

159

// Message to encrypt

160

const message = sodium.from_string('This is a secret message');

161

162

// Generate nonce (must be unique per key)

163

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

164

165

// Encrypt

166

const ciphertext = sodium.crypto_secretbox_easy(message, nonce, key);

167

168

// Decrypt

169

const plaintext = sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);

170

171

console.log(sodium.to_string(plaintext)); // "This is a secret message"

172

console.log('Ciphertext length:', ciphertext.length); // Original length + 16 (MAC)

173

```

174

175

### Detached Authentication for Separate Storage

176

177

```javascript

178

const key = sodium.crypto_secretbox_keygen();

179

const message = sodium.from_string('Message with separate MAC');

180

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

181

182

// Encrypt with separate MAC

183

const { cipher, mac } = sodium.crypto_secretbox_detached(message, nonce, key);

184

185

// Store ciphertext and MAC separately

186

console.log('Cipher:', sodium.to_hex(cipher));

187

console.log('MAC:', sodium.to_hex(mac));

188

console.log('Nonce:', sodium.to_hex(nonce));

189

190

// Decrypt with separate MAC

191

const plaintext = sodium.crypto_secretbox_open_detached(cipher, mac, nonce, key);

192

console.log('Decrypted:', sodium.to_string(plaintext));

193

```

194

195

### File Encryption System

196

197

```javascript

198

class FileEncryption {

199

constructor() {

200

this.key = sodium.crypto_secretbox_keygen();

201

}

202

203

encrypt(data) {

204

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

205

const ciphertext = sodium.crypto_secretbox_easy(data, nonce, this.key);

206

207

// Prepend nonce to ciphertext for storage

208

const encrypted = new Uint8Array(nonce.length + ciphertext.length);

209

encrypted.set(nonce);

210

encrypted.set(ciphertext, nonce.length);

211

212

return encrypted;

213

}

214

215

decrypt(encrypted) {

216

// Extract nonce and ciphertext

217

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

218

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

219

220

return sodium.crypto_secretbox_open_easy(ciphertext, nonce, this.key);

221

}

222

223

// Export key for storage (should be encrypted with password)

224

exportKey() {

225

return sodium.to_base64(this.key);

226

}

227

228

// Import key from storage

229

importKey(base64Key) {

230

this.key = sodium.from_base64(base64Key);

231

}

232

}

233

234

// Usage

235

const fileEnc = new FileEncryption();

236

const fileData = sodium.from_string('File contents to encrypt');

237

238

const encrypted = fileEnc.encrypt(fileData);

239

const decrypted = fileEnc.decrypt(encrypted);

240

241

console.log('Original equals decrypted:',

242

sodium.memcmp(fileData, decrypted)); // true

243

```

244

245

### Streaming Encryption for Large Files

246

247

```javascript

248

class StreamingEncryption {

249

constructor(key) {

250

this.key = key;

251

this.chunkSize = 4096; // 4KB chunks

252

}

253

254

encryptStream(data) {

255

const chunks = [];

256

const totalChunks = Math.ceil(data.length / this.chunkSize);

257

258

for (let i = 0; i < totalChunks; i++) {

259

const start = i * this.chunkSize;

260

const end = Math.min(start + this.chunkSize, data.length);

261

const chunk = data.subarray(start, end);

262

263

// Use chunk index as part of nonce (be careful in production!)

264

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

265

const encrypted = sodium.crypto_secretbox_easy(chunk, nonce, this.key);

266

267

chunks.push({

268

index: i,

269

nonce: nonce,

270

data: encrypted

271

});

272

}

273

274

return chunks;

275

}

276

277

decryptStream(chunks) {

278

// Sort chunks by index

279

chunks.sort((a, b) => a.index - b.index);

280

281

const decryptedChunks = [];

282

for (const chunk of chunks) {

283

const decrypted = sodium.crypto_secretbox_open_easy(

284

chunk.data, chunk.nonce, this.key

285

);

286

decryptedChunks.push(decrypted);

287

}

288

289

// Concatenate all chunks

290

const totalLength = decryptedChunks.reduce((sum, chunk) => sum + chunk.length, 0);

291

const result = new Uint8Array(totalLength);

292

let offset = 0;

293

294

for (const chunk of decryptedChunks) {

295

result.set(chunk, offset);

296

offset += chunk.length;

297

}

298

299

return result;

300

}

301

}

302

303

// Usage with large file

304

const key = sodium.crypto_secretbox_keygen();

305

const streaming = new StreamingEncryption(key);

306

307

const largeData = new Uint8Array(100000); // 100KB

308

sodium.randombytes_buf_into(largeData);

309

310

console.time('Stream Encrypt');

311

const encryptedChunks = streaming.encryptStream(largeData);

312

console.timeEnd('Stream Encrypt');

313

314

console.time('Stream Decrypt');

315

const decryptedData = streaming.decryptStream(encryptedChunks);

316

console.timeEnd('Stream Decrypt');

317

318

console.log('Streaming successful:',

319

sodium.memcmp(largeData, decryptedData)); // true

320

```

321

322

### Password-Based Encryption

323

324

```javascript

325

// Combine with key derivation for password-based encryption

326

async function encryptWithPassword(message, password) {

327

// Derive key from password using Argon2id

328

const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);

329

const key = sodium.crypto_pwhash(

330

sodium.crypto_secretbox_KEYBYTES,

331

sodium.from_string(password),

332

salt,

333

sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,

334

sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,

335

sodium.crypto_pwhash_ALG_ARGON2ID13

336

);

337

338

// Encrypt with derived key

339

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

340

const ciphertext = sodium.crypto_secretbox_easy(message, nonce, key);

341

342

// Return salt, nonce, and ciphertext

343

return {

344

salt: sodium.to_base64(salt),

345

nonce: sodium.to_base64(nonce),

346

ciphertext: sodium.to_base64(ciphertext)

347

};

348

}

349

350

async function decryptWithPassword(encryptedData, password) {

351

const salt = sodium.from_base64(encryptedData.salt);

352

const nonce = sodium.from_base64(encryptedData.nonce);

353

const ciphertext = sodium.from_base64(encryptedData.ciphertext);

354

355

// Derive same key from password

356

const key = sodium.crypto_pwhash(

357

sodium.crypto_secretbox_KEYBYTES,

358

sodium.from_string(password),

359

salt,

360

sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,

361

sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,

362

sodium.crypto_pwhash_ALG_ARGON2ID13

363

);

364

365

// Decrypt

366

return sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);

367

}

368

369

// Usage

370

const message = sodium.from_string('Password-protected message');

371

const encrypted = await encryptWithPassword(message, 'mypassword123');

372

const decrypted = await decryptWithPassword(encrypted, 'mypassword123');

373

374

console.log(sodium.to_string(decrypted)); // "Password-protected message"

375

```

376

377

### Performance Benchmarking

378

379

```javascript

380

function benchmarkSecretBox() {

381

const key = sodium.crypto_secretbox_keygen();

382

const message = new Uint8Array(1024 * 1024); // 1MB

383

sodium.randombytes_buf_into(message);

384

385

console.time('SecretBox Encrypt 1MB');

386

for (let i = 0; i < 10; i++) {

387

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

388

const ciphertext = sodium.crypto_secretbox_easy(message, nonce, key);

389

}

390

console.timeEnd('SecretBox Encrypt 1MB');

391

392

// Test decryption

393

const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);

394

const ciphertext = sodium.crypto_secretbox_easy(message, nonce, key);

395

396

console.time('SecretBox Decrypt 1MB');

397

for (let i = 0; i < 10; i++) {

398

const plaintext = sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);

399

}

400

console.timeEnd('SecretBox Decrypt 1MB');

401

}

402

403

benchmarkSecretBox();

404

```

405

406

## Security Considerations

407

408

- **Nonce Uniqueness**: Never reuse nonces with the same key - this breaks security completely

409

- **Key Management**: Keep encryption keys secret and use crypto_secretbox_keygen() for generation

410

- **Nonce Generation**: Use randombytes_buf() for nonce generation

411

- **Authentication**: All secretbox functions provide authenticated encryption

412

- **Key Rotation**: Consider periodic key rotation for long-term use

413

- **Memory Security**: Use memzero() to clear sensitive data from memory

414

415

## Algorithm Selection Guide

416

417

- **Generic secretbox**: Good default choice, NaCl-compatible

418

- **XChaCha20 variant**: Better security margins, safer for high-volume applications

419

- **Detached mode**: When you need to store/transmit MAC separately

420

- **Password-based**: Combine with crypto_pwhash for user password encryption

421

422

SecretBox provides excellent performance and security for symmetric encryption needs.