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

streaming.mddocs/

0

# Streaming Operations

1

2

Streaming functions provide encryption and stream generation for large data sets, real-time communication, and custom protocols. These functions process data in chunks rather than requiring the entire message in memory.

3

4

## Secret Stream (XChaCha20-Poly1305)

5

6

Secret stream provides streaming authenticated encryption with rekeying support, perfect for encrypting large files or real-time data streams.

7

8

### Key Generation

9

10

```javascript { .api }

11

/**

12

* Generate a random key for secret stream operations

13

* @returns Uint8Array - 32-byte stream key

14

*/

15

function crypto_secretstream_xchacha20poly1305_keygen(): Uint8Array;

16

```

17

18

### Stream Initialization

19

20

#### Push (Encryption) Stream

21

22

```javascript { .api }

23

/**

24

* Initialize stream for encryption (push)

25

* @param key - 32-byte stream key

26

* @returns Object with state and header

27

*/

28

function crypto_secretstream_xchacha20poly1305_init_push(key: Uint8Array): {

29

state: Uint8Array; // State object for streaming

30

header: Uint8Array; // 24-byte header to send with stream

31

};

32

```

33

34

#### Pull (Decryption) Stream

35

36

```javascript { .api }

37

/**

38

* Initialize stream for decryption (pull)

39

* @param header - 24-byte header from encryption stream

40

* @param key - 32-byte stream key

41

* @returns Uint8Array - State object for streaming

42

*/

43

function crypto_secretstream_xchacha20poly1305_init_pull(

44

header: Uint8Array,

45

key: Uint8Array

46

): Uint8Array;

47

```

48

49

### Stream Operations

50

51

#### Push (Encrypt) Data

52

53

```javascript { .api }

54

/**

55

* Encrypt data chunk in stream

56

* @param state_address - State from init_push

57

* @param message_chunk - Data chunk to encrypt

58

* @param ad - Additional authenticated data (optional)

59

* @param tag - Stream tag (MESSAGE, PUSH, REKEY, or FINAL)

60

* @returns Uint8Array - Encrypted chunk with authentication

61

*/

62

function crypto_secretstream_xchacha20poly1305_push(

63

state_address: any,

64

message_chunk: Uint8Array,

65

ad: Uint8Array | null,

66

tag: number

67

): Uint8Array;

68

```

69

70

#### Pull (Decrypt) Data

71

72

```javascript { .api }

73

/**

74

* Decrypt data chunk from stream

75

* @param state_address - State from init_pull

76

* @param cipher - Encrypted chunk

77

* @param ad - Additional authenticated data (optional)

78

* @returns Object with message_chunk and tag

79

*/

80

function crypto_secretstream_xchacha20poly1305_pull(

81

state_address: any,

82

cipher: Uint8Array,

83

ad: Uint8Array | null

84

): {

85

message_chunk: Uint8Array;

86

tag: number;

87

};

88

```

89

90

#### Rekeying

91

92

```javascript { .api }

93

/**

94

* Rekey the stream for forward secrecy

95

* @param state_address - Stream state to rekey

96

*/

97

function crypto_secretstream_xchacha20poly1305_rekey(state_address: any): void;

98

```

99

100

### Stream Tags

101

102

```javascript { .api }

103

const crypto_secretstream_xchacha20poly1305_TAG_MESSAGE: number; // 0 - Normal message

104

const crypto_secretstream_xchacha20poly1305_TAG_PUSH: number; // 1 - End of chunk

105

const crypto_secretstream_xchacha20poly1305_TAG_REKEY: number; // 2 - Rekey after this

106

const crypto_secretstream_xchacha20poly1305_TAG_FINAL: number; // 3 - End of stream

107

```

108

109

### Secret Stream Constants

110

111

```javascript { .api }

112

const crypto_secretstream_xchacha20poly1305_KEYBYTES: number; // 32

113

const crypto_secretstream_xchacha20poly1305_HEADERBYTES: number; // 24

114

const crypto_secretstream_xchacha20poly1305_ABYTES: number; // 17

115

const crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX: number; // Large value

116

```

117

118

## Stream Ciphers

119

120

Stream cipher functions generate pseudorandom streams for XOR-based encryption or random number generation.

121

122

### ChaCha20

123

124

#### Key Generation

125

126

```javascript { .api }

127

/**

128

* Generate key for ChaCha20 stream cipher

129

* @returns Uint8Array - 32-byte stream key

130

*/

131

function crypto_stream_chacha20_keygen(): Uint8Array;

132

```

133

134

#### Stream Generation

135

136

```javascript { .api }

137

/**

138

* Generate ChaCha20 keystream

139

* @param outLength - Length of stream to generate

140

* @param key - 32-byte stream key

141

* @param nonce - 8-byte nonce

142

* @returns Uint8Array - Generated keystream

143

*/

144

function crypto_stream_chacha20(

145

outLength: number,

146

key: Uint8Array,

147

nonce: Uint8Array

148

): Uint8Array;

149

```

150

151

#### XOR Operations

152

153

```javascript { .api }

154

/**

155

* Encrypt/decrypt data using ChaCha20 stream XOR

156

* @param input_message - Data to encrypt/decrypt

157

* @param nonce - 8-byte nonce

158

* @param key - 32-byte stream key

159

* @returns Uint8Array - XOR result

160

*/

161

function crypto_stream_chacha20_xor(

162

input_message: Uint8Array,

163

nonce: Uint8Array,

164

key: Uint8Array

165

): Uint8Array;

166

167

/**

168

* ChaCha20 XOR with initial counter

169

* @param input_message - Data to encrypt/decrypt

170

* @param nonce - 8-byte nonce

171

* @param nonce_increment - Initial counter value

172

* @param key - 32-byte stream key

173

* @returns Uint8Array - XOR result

174

*/

175

function crypto_stream_chacha20_xor_ic(

176

input_message: Uint8Array,

177

nonce: Uint8Array,

178

nonce_increment: number,

179

key: Uint8Array

180

): Uint8Array;

181

```

182

183

### ChaCha20 IETF

184

185

#### XOR Operations

186

187

```javascript { .api }

188

/**

189

* ChaCha20 IETF variant XOR (12-byte nonce)

190

* @param input_message - Data to encrypt/decrypt

191

* @param nonce - 12-byte nonce

192

* @param key - 32-byte stream key

193

* @returns Uint8Array - XOR result

194

*/

195

function crypto_stream_chacha20_ietf_xor(

196

input_message: Uint8Array,

197

nonce: Uint8Array,

198

key: Uint8Array

199

): Uint8Array;

200

201

/**

202

* ChaCha20 IETF XOR with initial counter

203

* @param input_message - Data to encrypt/decrypt

204

* @param nonce - 12-byte nonce

205

* @param nonce_increment - Initial counter value

206

* @param key - 32-byte stream key

207

* @returns Uint8Array - XOR result

208

*/

209

function crypto_stream_chacha20_ietf_xor_ic(

210

input_message: Uint8Array,

211

nonce: Uint8Array,

212

nonce_increment: number,

213

key: Uint8Array

214

): Uint8Array;

215

```

216

217

### XChaCha20

218

219

Extended nonce variant of ChaCha20 for better security.

220

221

#### Key Generation

222

223

```javascript { .api }

224

/**

225

* Generate key for XChaCha20 stream cipher

226

* @returns Uint8Array - 32-byte stream key

227

*/

228

function crypto_stream_xchacha20_keygen(): Uint8Array;

229

```

230

231

#### XOR Operations

232

233

```javascript { .api }

234

/**

235

* XChaCha20 XOR encryption/decryption

236

* @param input_message - Data to encrypt/decrypt

237

* @param nonce - 24-byte nonce

238

* @param key - 32-byte stream key

239

* @returns Uint8Array - XOR result

240

*/

241

function crypto_stream_xchacha20_xor(

242

input_message: Uint8Array,

243

nonce: Uint8Array,

244

key: Uint8Array

245

): Uint8Array;

246

247

/**

248

* XChaCha20 XOR with initial counter

249

* @param input_message - Data to encrypt/decrypt

250

* @param nonce - 24-byte nonce

251

* @param nonce_increment - Initial counter value

252

* @param key - 32-byte stream key

253

* @returns Uint8Array - XOR result

254

*/

255

function crypto_stream_xchacha20_xor_ic(

256

input_message: Uint8Array,

257

nonce: Uint8Array,

258

nonce_increment: number,

259

key: Uint8Array

260

): Uint8Array;

261

```

262

263

### Generic Stream

264

265

Default stream cipher functions.

266

267

```javascript { .api }

268

/**

269

* Generate key for generic stream cipher

270

* @returns Uint8Array - Stream key

271

*/

272

function crypto_stream_keygen(): Uint8Array;

273

```

274

275

### Stream Cipher Constants

276

277

```javascript { .api }

278

// ChaCha20

279

const crypto_stream_chacha20_KEYBYTES: number; // 32

280

const crypto_stream_chacha20_NONCEBYTES: number; // 8

281

const crypto_stream_chacha20_MESSAGEBYTES_MAX: number; // Large value

282

283

// ChaCha20 IETF

284

const crypto_stream_chacha20_ietf_KEYBYTES: number; // 32

285

const crypto_stream_chacha20_ietf_NONCEBYTES: number; // 12

286

const crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX: number; // Large value

287

288

// XChaCha20

289

const crypto_stream_xchacha20_KEYBYTES: number; // 32

290

const crypto_stream_xchacha20_NONCEBYTES: number; // 24

291

const crypto_stream_xchacha20_MESSAGEBYTES_MAX: number; // Large value

292

293

// Generic stream

294

const crypto_stream_KEYBYTES: number; // 32

295

const crypto_stream_NONCEBYTES: number; // 24

296

const crypto_stream_MESSAGEBYTES_MAX: number; // Large value

297

```

298

299

## Usage Examples

300

301

### Basic Secret Stream

302

303

```javascript

304

import _sodium from 'libsodium-wrappers-sumo';

305

await _sodium.ready;

306

const sodium = _sodium;

307

308

// Generate stream key

309

const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();

310

311

// Initialize encryption stream

312

const { state: pushState, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(key);

313

314

// Initialize decryption stream

315

const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, key);

316

317

// Encrypt data chunks

318

const chunk1 = sodium.from_string('First chunk of data');

319

const chunk2 = sodium.from_string('Second chunk of data');

320

const chunk3 = sodium.from_string('Final chunk of data');

321

322

const encrypted1 = sodium.crypto_secretstream_xchacha20poly1305_push(

323

pushState, chunk1, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE

324

);

325

326

const encrypted2 = sodium.crypto_secretstream_xchacha20poly1305_push(

327

pushState, chunk2, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE

328

);

329

330

const encrypted3 = sodium.crypto_secretstream_xchacha20poly1305_push(

331

pushState, chunk3, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL

332

);

333

334

// Decrypt data chunks

335

const { message_chunk: decrypted1, tag: tag1 } = sodium.crypto_secretstream_xchacha20poly1305_pull(

336

pullState, encrypted1, null

337

);

338

339

const { message_chunk: decrypted2, tag: tag2 } = sodium.crypto_secretstream_xchacha20poly1305_pull(

340

pullState, encrypted2, null

341

);

342

343

const { message_chunk: decrypted3, tag: tag3 } = sodium.crypto_secretstream_xchacha20poly1305_pull(

344

pullState, encrypted3, null

345

);

346

347

console.log('Decrypted chunk 1:', sodium.to_string(decrypted1));

348

console.log('Decrypted chunk 2:', sodium.to_string(decrypted2));

349

console.log('Decrypted chunk 3:', sodium.to_string(decrypted3));

350

console.log('Final tag:', tag3 === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL); // true

351

```

352

353

### File Streaming Encryption

354

355

```javascript

356

class StreamingFileEncryption {

357

constructor() {

358

this.key = sodium.crypto_secretstream_xchacha20poly1305_keygen();

359

}

360

361

// Encrypt file in chunks

362

encryptFile(fileData, chunkSize = 4096) {

363

const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);

364

const encryptedChunks = [];

365

366

// Store header first

367

encryptedChunks.push({

368

type: 'header',

369

data: header

370

});

371

372

// Process file in chunks

373

for (let i = 0; i < fileData.length; i += chunkSize) {

374

const chunk = fileData.subarray(i, i + chunkSize);

375

const isLast = i + chunkSize >= fileData.length;

376

377

const tag = isLast

378

? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL

379

: sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;

380

381

const encrypted = sodium.crypto_secretstream_xchacha20poly1305_push(

382

state, chunk, null, tag

383

);

384

385

encryptedChunks.push({

386

type: 'chunk',

387

data: encrypted,

388

tag: tag

389

});

390

}

391

392

return encryptedChunks;

393

}

394

395

// Decrypt file from chunks

396

decryptFile(encryptedChunks) {

397

if (encryptedChunks[0].type !== 'header') {

398

throw new Error('First chunk must be header');

399

}

400

401

const header = encryptedChunks[0].data;

402

const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, this.key);

403

404

const decryptedChunks = [];

405

406

for (let i = 1; i < encryptedChunks.length; i++) {

407

const { message_chunk, tag } = sodium.crypto_secretstream_xchacha20poly1305_pull(

408

state, encryptedChunks[i].data, null

409

);

410

411

decryptedChunks.push(message_chunk);

412

413

// Check if this is the final chunk

414

if (tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {

415

break;

416

}

417

}

418

419

// Concatenate all chunks

420

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

421

const result = new Uint8Array(totalLength);

422

let offset = 0;

423

424

for (const chunk of decryptedChunks) {

425

result.set(chunk, offset);

426

offset += chunk.length;

427

}

428

429

return result;

430

}

431

}

432

433

// Usage

434

const fileEnc = new StreamingFileEncryption();

435

const largeFile = new Uint8Array(100000); // 100KB file

436

sodium.randombytes_buf_into(largeFile);

437

438

console.time('Encrypt large file');

439

const encryptedChunks = fileEnc.encryptFile(largeFile, 8192); // 8KB chunks

440

console.timeEnd('Encrypt large file');

441

442

console.log('Encrypted chunks:', encryptedChunks.length);

443

444

console.time('Decrypt large file');

445

const decryptedFile = fileEnc.decryptFile(encryptedChunks);

446

console.timeEnd('Decrypt large file');

447

448

console.log('Files match:', sodium.memcmp(largeFile, decryptedFile)); // true

449

```

450

451

### Real-Time Streaming with Rekeying

452

453

```javascript

454

class SecureDataStream {

455

constructor(isEncryptor = true) {

456

this.key = sodium.crypto_secretstream_xchacha20poly1305_keygen();

457

this.isEncryptor = isEncryptor;

458

this.messageCount = 0;

459

this.rekeyInterval = 100; // Rekey every 100 messages

460

461

if (isEncryptor) {

462

const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);

463

this.state = state;

464

this.header = header;

465

} else {

466

this.state = null; // Will be set when header received

467

}

468

}

469

470

// Initialize decryptor with header

471

initDecryptor(header) {

472

if (this.isEncryptor) {

473

throw new Error('Cannot init decryptor on encryptor');

474

}

475

this.state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, this.key);

476

}

477

478

// Process outgoing message

479

encrypt(message, metadata = null) {

480

if (!this.isEncryptor || !this.state) {

481

throw new Error('Invalid encryptor state');

482

}

483

484

this.messageCount++;

485

let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;

486

487

// Rekey periodically for forward secrecy

488

if (this.messageCount % this.rekeyInterval === 0) {

489

tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY;

490

}

491

492

const encrypted = sodium.crypto_secretstream_xchacha20poly1305_push(

493

this.state, message, metadata, tag

494

);

495

496

// Perform rekeying after REKEY tag

497

if (tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY) {

498

sodium.crypto_secretstream_xchacha20poly1305_rekey(this.state);

499

console.log('Stream rekeyed at message', this.messageCount);

500

}

501

502

return encrypted;

503

}

504

505

// Process incoming message

506

decrypt(encrypted, expectedMetadata = null) {

507

if (this.isEncryptor || !this.state) {

508

throw new Error('Invalid decryptor state');

509

}

510

511

const { message_chunk, tag } = sodium.crypto_secretstream_xchacha20poly1305_pull(

512

this.state, encrypted, expectedMetadata

513

);

514

515

// Handle rekeying

516

if (tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY) {

517

sodium.crypto_secretstream_xchacha20poly1305_rekey(this.state);

518

console.log('Stream rekeyed by peer');

519

}

520

521

return { message: message_chunk, tag };

522

}

523

524

// Finalize stream

525

finalize(finalMessage = null) {

526

if (!this.isEncryptor) {

527

throw new Error('Only encryptor can finalize');

528

}

529

530

const message = finalMessage || new Uint8Array(0);

531

return sodium.crypto_secretstream_xchacha20poly1305_push(

532

this.state, message, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL

533

);

534

}

535

536

getHeader() {

537

return this.header;

538

}

539

}

540

541

// Simulate real-time communication with rekeying

542

const sender = new SecureDataStream(true);

543

const receiver = new SecureDataStream(false);

544

545

// Initialize receiver with sender's header

546

receiver.initDecryptor(sender.getHeader());

547

548

// Send many messages to trigger rekeying

549

for (let i = 1; i <= 150; i++) {

550

const message = sodium.from_string(`Message ${i}`);

551

const metadata = sodium.from_string(`meta${i}`);

552

553

// Encrypt

554

const encrypted = sender.encrypt(message, metadata);

555

556

// Decrypt

557

const { message: decrypted, tag } = receiver.decrypt(encrypted, metadata);

558

559

if (i % 50 === 0) { // Log every 50th message

560

console.log(`Message ${i}:`, sodium.to_string(decrypted));

561

}

562

}

563

564

// Finalize stream

565

const finalEncrypted = sender.finalize(sodium.from_string('Stream ended'));

566

const { message: finalDecrypted, tag: finalTag } = receiver.decrypt(finalEncrypted);

567

568

console.log('Final message:', sodium.to_string(finalDecrypted));

569

console.log('Stream finalized:', finalTag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL);

570

```

571

572

### Stream Cipher Usage

573

574

```javascript

575

// Basic stream cipher usage

576

const streamKey = sodium.crypto_stream_xchacha20_keygen();

577

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

578

579

const plaintext = sodium.from_string('Stream cipher encryption');

580

581

// Encrypt using XOR

582

const ciphertext = sodium.crypto_stream_xchacha20_xor(plaintext, nonce, streamKey);

583

console.log('Encrypted:', sodium.to_hex(ciphertext));

584

585

// Decrypt using XOR (same operation)

586

const decrypted = sodium.crypto_stream_xchacha20_xor(ciphertext, nonce, streamKey);

587

console.log('Decrypted:', sodium.to_string(decrypted)); // "Stream cipher encryption"

588

589

// Generate raw keystream

590

const keystream = sodium.crypto_stream_chacha20(100, streamKey.subarray(0, 32), nonce.subarray(0, 8));

591

console.log('Keystream sample:', sodium.to_hex(keystream.subarray(0, 16)));

592

```

593

594

### Custom Protocol with Stream Chunks

595

596

```javascript

597

class ChunkedProtocol {

598

constructor(chunkSize = 1024) {

599

this.chunkSize = chunkSize;

600

this.key = sodium.crypto_secretstream_xchacha20poly1305_keygen();

601

}

602

603

// Encode message into protocol chunks

604

encodeMessage(message, messageType = 'data') {

605

const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);

606

607

const chunks = [];

608

609

// Add protocol header

610

chunks.push({

611

type: 'protocol_header',

612

data: header

613

});

614

615

// Add message metadata

616

const metadata = {

617

messageType,

618

totalLength: message.length,

619

timestamp: Date.now()

620

};

621

622

const metadataBytes = sodium.from_string(JSON.stringify(metadata));

623

const encryptedMetadata = sodium.crypto_secretstream_xchacha20poly1305_push(

624

state, metadataBytes, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_PUSH

625

);

626

627

chunks.push({

628

type: 'metadata',

629

data: encryptedMetadata

630

});

631

632

// Add message chunks

633

for (let i = 0; i < message.length; i += this.chunkSize) {

634

const chunk = message.subarray(i, i + this.chunkSize);

635

const isLast = i + this.chunkSize >= message.length;

636

637

const tag = isLast

638

? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL

639

: sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;

640

641

const encrypted = sodium.crypto_secretstream_xchacha20poly1305_push(

642

state, chunk, null, tag

643

);

644

645

chunks.push({

646

type: 'data',

647

data: encrypted

648

});

649

}

650

651

return chunks;

652

}

653

654

// Decode protocol chunks back to message

655

decodeMessage(chunks) {

656

if (chunks[0].type !== 'protocol_header') {

657

throw new Error('Invalid protocol: missing header');

658

}

659

660

const header = chunks[0].data;

661

const state = sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, this.key);

662

663

// Decrypt metadata

664

if (chunks[1].type !== 'metadata') {

665

throw new Error('Invalid protocol: missing metadata');

666

}

667

668

const { message_chunk: metadataBytes } = sodium.crypto_secretstream_xchacha20poly1305_pull(

669

state, chunks[1].data, null

670

);

671

672

const metadata = JSON.parse(sodium.to_string(metadataBytes));

673

console.log('Message metadata:', metadata);

674

675

// Decrypt data chunks

676

const dataChunks = [];

677

for (let i = 2; i < chunks.length; i++) {

678

if (chunks[i].type === 'data') {

679

const { message_chunk, tag } = sodium.crypto_secretstream_xchacha20poly1305_pull(

680

state, chunks[i].data, null

681

);

682

683

dataChunks.push(message_chunk);

684

685

if (tag === sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {

686

break;

687

}

688

}

689

}

690

691

// Reconstruct complete message

692

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

693

const result = new Uint8Array(totalLength);

694

let offset = 0;

695

696

for (const chunk of dataChunks) {

697

result.set(chunk, offset);

698

offset += chunk.length;

699

}

700

701

return {

702

message: result,

703

metadata

704

};

705

}

706

}

707

708

// Usage

709

const protocol = new ChunkedProtocol(512); // 512-byte chunks

710

const message = sodium.from_string('This is a long message that will be split into multiple chunks for transmission over the network. Each chunk is encrypted separately for security.');

711

712

// Encode

713

const encodedChunks = protocol.encodeMessage(message, 'text_message');

714

console.log('Encoded into', encodedChunks.length, 'chunks');

715

716

// Decode

717

const { message: decodedMessage, metadata } = protocol.decodeMessage(encodedChunks);

718

console.log('Decoded message:', sodium.to_string(decodedMessage));

719

console.log('Messages match:', sodium.memcmp(message, decodedMessage)); // true

720

```

721

722

## Security Considerations

723

724

- **Stream Reuse**: Never reuse stream states or nonces

725

- **Rekeying**: Use periodic rekeying for forward secrecy in long streams

726

- **Tag Handling**: Properly handle stream tags to maintain protocol integrity

727

- **Memory Management**: Clear stream states after use

728

- **Authentication**: Secret streams provide authentication, stream ciphers do not

729

- **Nonce Management**: Use proper nonce generation for stream ciphers

730

731

## Algorithm Selection Guide

732

733

- **Secret Stream**: Use for authenticated streaming encryption with rekeying

734

- **XChaCha20**: Best stream cipher choice for new applications

735

- **ChaCha20 IETF**: Standards compliance, careful nonce management

736

- **ChaCha20**: Original variant, 8-byte nonces

737

- **Stream Rekeying**: Essential for long-running streams and forward secrecy

738

739

Streaming operations are perfect for large files, real-time communication, and memory-constrained environments.