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

sign.mddocs/

0

# Digital Signatures

1

2

Digital signature functions provide message authentication, integrity, and non-repudiation using Ed25519 elliptic curve cryptography. Signatures prove that a message was signed by the holder of a private key without revealing the private key.

3

4

## Key Pair Generation

5

6

### Generate Random Key Pair

7

8

```javascript { .api }

9

/**

10

* Generate a random Ed25519 key pair for signing

11

* @returns Object with publicKey and privateKey properties

12

*/

13

function crypto_sign_keypair(): {

14

publicKey: Uint8Array; // 32 bytes - can be shared publicly

15

privateKey: Uint8Array; // 64 bytes - keep secret

16

};

17

```

18

19

### Generate Key Pair from Seed

20

21

```javascript { .api }

22

/**

23

* Generate deterministic Ed25519 key pair from seed

24

* @param seed - 32-byte seed for reproducible key generation

25

* @returns Object with publicKey and privateKey properties

26

*/

27

function crypto_sign_seed_keypair(seed: Uint8Array): {

28

publicKey: Uint8Array;

29

privateKey: Uint8Array;

30

};

31

```

32

33

## Digital Signatures

34

35

### Attached Signatures

36

37

Attached signatures include the message within the signed data.

38

39

```javascript { .api }

40

/**

41

* Sign a message (signature includes the message)

42

* @param message - Data to sign

43

* @param privateKey - 64-byte private signing key

44

* @returns Uint8Array - Signed message (message + signature)

45

*/

46

function crypto_sign(

47

message: Uint8Array,

48

privateKey: Uint8Array

49

): Uint8Array;

50

51

/**

52

* Verify and extract message from signed data

53

* @param signedMessage - Signed message from crypto_sign

54

* @param publicKey - 32-byte public verification key

55

* @returns Uint8Array - Original message if signature is valid

56

* @throws Error if signature verification fails

57

*/

58

function crypto_sign_open(

59

signedMessage: Uint8Array,

60

publicKey: Uint8Array

61

): Uint8Array;

62

```

63

64

### Detached Signatures (Recommended)

65

66

Detached signatures are separate from the message, allowing independent storage.

67

68

```javascript { .api }

69

/**

70

* Create detached signature for a message

71

* @param message - Data to sign

72

* @param privateKey - 64-byte private signing key

73

* @returns Uint8Array - 64-byte signature

74

*/

75

function crypto_sign_detached(

76

message: Uint8Array,

77

privateKey: Uint8Array

78

): Uint8Array;

79

80

/**

81

* Verify detached signature for a message

82

* @param signature - 64-byte signature to verify

83

* @param message - Original message data

84

* @param publicKey - 32-byte public verification key

85

* @returns boolean - True if signature is valid

86

*/

87

function crypto_sign_verify_detached(

88

signature: Uint8Array,

89

message: Uint8Array,

90

publicKey: Uint8Array

91

): boolean;

92

```

93

94

## Streaming Signatures

95

96

For large messages that don't fit in memory, use streaming signature operations.

97

98

### Initialize Streaming

99

100

```javascript { .api }

101

/**

102

* Initialize streaming signature state

103

* @returns Uint8Array - State object for streaming operations

104

*/

105

function crypto_sign_init(): Uint8Array;

106

```

107

108

### Update with Data

109

110

```javascript { .api }

111

/**

112

* Add data chunk to streaming signature

113

* @param state_address - State from crypto_sign_init

114

* @param message_chunk - Data chunk to add to signature

115

*/

116

function crypto_sign_update(

117

state_address: any,

118

message_chunk: Uint8Array

119

): void;

120

```

121

122

### Finalize Signature

123

124

```javascript { .api }

125

/**

126

* Finalize streaming signature and create signature

127

* @param state_address - State from init/update operations

128

* @param privateKey - 64-byte private signing key

129

* @returns Uint8Array - 64-byte signature

130

*/

131

function crypto_sign_final_create(

132

state_address: any,

133

privateKey: Uint8Array

134

): Uint8Array;

135

136

/**

137

* Finalize streaming signature and verify existing signature

138

* @param state_address - State from init/update operations

139

* @param signature - 64-byte signature to verify

140

* @param publicKey - 32-byte public verification key

141

* @returns boolean - True if signature is valid

142

*/

143

function crypto_sign_final_verify(

144

state_address: any,

145

signature: Uint8Array,

146

publicKey: Uint8Array

147

): boolean;

148

```

149

150

## Key Management

151

152

### Extract Public Key from Private Key

153

154

```javascript { .api }

155

/**

156

* Extract public key from Ed25519 private key

157

* @param privateKey - 64-byte Ed25519 private key

158

* @returns Uint8Array - 32-byte public key

159

*/

160

function crypto_sign_ed25519_sk_to_pk(privateKey: Uint8Array): Uint8Array;

161

162

/**

163

* Extract seed from Ed25519 private key

164

* @param privateKey - 64-byte Ed25519 private key

165

* @returns Uint8Array - 32-byte seed

166

*/

167

function crypto_sign_ed25519_sk_to_seed(privateKey: Uint8Array): Uint8Array;

168

```

169

170

### Key Conversion

171

172

Convert between Ed25519 signing keys and Curve25519 encryption keys.

173

174

```javascript { .api }

175

/**

176

* Convert Ed25519 public key to Curve25519 public key

177

* @param edPk - 32-byte Ed25519 public key

178

* @returns Uint8Array - 32-byte Curve25519 public key

179

*/

180

function crypto_sign_ed25519_pk_to_curve25519(edPk: Uint8Array): Uint8Array;

181

182

/**

183

* Convert Ed25519 private key to Curve25519 private key

184

* @param edSk - 64-byte Ed25519 private key

185

* @returns Uint8Array - 32-byte Curve25519 private key

186

*/

187

function crypto_sign_ed25519_sk_to_curve25519(edSk: Uint8Array): Uint8Array;

188

```

189

190

## Constants

191

192

```javascript { .api }

193

const crypto_sign_BYTES: number; // 64 (signature length)

194

const crypto_sign_PUBLICKEYBYTES: number; // 32 (public key length)

195

const crypto_sign_SECRETKEYBYTES: number; // 64 (private key length)

196

const crypto_sign_SEEDBYTES: number; // 32 (seed length)

197

const crypto_sign_MESSAGEBYTES_MAX: number; // Maximum message size

198

199

// Ed25519-specific constants (same values)

200

const crypto_sign_ed25519_BYTES: number; // 64

201

const crypto_sign_ed25519_PUBLICKEYBYTES: number; // 32

202

const crypto_sign_ed25519_SECRETKEYBYTES: number; // 64

203

const crypto_sign_ed25519_SEEDBYTES: number; // 32

204

const crypto_sign_ed25519_MESSAGEBYTES_MAX: number;

205

```

206

207

## Usage Examples

208

209

### Basic Digital Signatures

210

211

```javascript

212

import _sodium from 'libsodium-wrappers-sumo';

213

await _sodium.ready;

214

const sodium = _sodium;

215

216

// Generate signing key pair

217

const { publicKey, privateKey } = sodium.crypto_sign_keypair();

218

219

// Message to sign

220

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

221

222

// Create detached signature

223

const signature = sodium.crypto_sign_detached(message, privateKey);

224

225

// Verify signature

226

const isValid = sodium.crypto_sign_verify_detached(signature, message, publicKey);

227

228

console.log('Signature valid:', isValid); // true

229

console.log('Signature length:', signature.length); // 64 bytes

230

console.log('Public key length:', publicKey.length); // 32 bytes

231

```

232

233

### Attached Signatures

234

235

```javascript

236

// Sign message with attached signature

237

const signedMessage = sodium.crypto_sign(message, privateKey);

238

239

console.log('Original message length:', message.length);

240

console.log('Signed message length:', signedMessage.length); // +64 bytes

241

242

// Verify and extract original message

243

const verifiedMessage = sodium.crypto_sign_open(signedMessage, publicKey);

244

245

console.log('Messages match:',

246

sodium.memcmp(message, verifiedMessage)); // true

247

```

248

249

### Deterministic Key Generation

250

251

```javascript

252

// Generate reproducible keys from seed

253

const seed = sodium.randombytes_buf(sodium.crypto_sign_SEEDBYTES);

254

const keyPair1 = sodium.crypto_sign_seed_keypair(seed);

255

const keyPair2 = sodium.crypto_sign_seed_keypair(seed);

256

257

// Keys are identical

258

console.log('Public keys match:',

259

sodium.memcmp(keyPair1.publicKey, keyPair2.publicKey)); // true

260

261

console.log('Private keys match:',

262

sodium.memcmp(keyPair1.privateKey, keyPair2.privateKey)); // true

263

```

264

265

### Streaming Signatures for Large Data

266

267

```javascript

268

// Sign large data in chunks

269

function signLargeData(chunks, privateKey) {

270

const state = sodium.crypto_sign_init();

271

272

for (const chunk of chunks) {

273

sodium.crypto_sign_update(state, chunk);

274

}

275

276

return sodium.crypto_sign_final_create(state, privateKey);

277

}

278

279

// Verify large data in chunks

280

function verifyLargeData(chunks, signature, publicKey) {

281

const state = sodium.crypto_sign_init();

282

283

for (const chunk of chunks) {

284

sodium.crypto_sign_update(state, chunk);

285

}

286

287

return sodium.crypto_sign_final_verify(state, signature, publicKey);

288

}

289

290

// Usage with large file

291

const { publicKey, privateKey } = sodium.crypto_sign_keypair();

292

const largeData = new Uint8Array(10 * 1024 * 1024); // 10MB

293

sodium.randombytes_buf_into(largeData);

294

295

// Split into chunks

296

const chunkSize = 64 * 1024; // 64KB chunks

297

const chunks = [];

298

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

299

chunks.push(largeData.subarray(i, i + chunkSize));

300

}

301

302

console.time('Sign large data');

303

const signature = signLargeData(chunks, privateKey);

304

console.timeEnd('Sign large data');

305

306

console.time('Verify large data');

307

const isValid = verifyLargeData(chunks, signature, publicKey);

308

console.timeEnd('Verify large data');

309

310

console.log('Large data signature valid:', isValid); // true

311

```

312

313

### Document Signing System

314

315

```javascript

316

class DocumentSigner {

317

constructor() {

318

const { publicKey, privateKey } = sodium.crypto_sign_keypair();

319

this.publicKey = publicKey;

320

this.privateKey = privateKey;

321

}

322

323

signDocument(document) {

324

const documentBytes = sodium.from_string(JSON.stringify(document));

325

const signature = sodium.crypto_sign_detached(documentBytes, this.privateKey);

326

327

return {

328

document,

329

signature: sodium.to_base64(signature),

330

publicKey: sodium.to_hex(this.publicKey),

331

timestamp: new Date().toISOString()

332

};

333

}

334

335

verifyDocument(signedDoc) {

336

try {

337

const documentBytes = sodium.from_string(JSON.stringify(signedDoc.document));

338

const signature = sodium.from_base64(signedDoc.signature);

339

const publicKey = sodium.from_hex(signedDoc.publicKey);

340

341

return sodium.crypto_sign_verify_detached(signature, documentBytes, publicKey);

342

} catch (e) {

343

return false;

344

}

345

}

346

347

getPublicKeyHex() {

348

return sodium.to_hex(this.publicKey);

349

}

350

}

351

352

// Usage

353

const signer = new DocumentSigner();

354

const document = {

355

title: 'Important Contract',

356

content: 'Contract terms and conditions...',

357

amount: 10000

358

};

359

360

const signedDoc = signer.signDocument(document);

361

console.log('Signed document:', signedDoc);

362

363

const isValid = signer.verifyDocument(signedDoc);

364

console.log('Document signature valid:', isValid); // true

365

366

// Try to tamper with document

367

const tamperedDoc = { ...signedDoc };

368

tamperedDoc.document.amount = 50000;

369

370

const isTamperedValid = signer.verifyDocument(tamperedDoc);

371

console.log('Tampered document valid:', isTamperedValid); // false

372

```

373

374

### Multi-Signature Validation

375

376

```javascript

377

class MultiSigner {

378

constructor(requiredSignatures = 2) {

379

this.requiredSignatures = requiredSignatures;

380

this.signers = [];

381

}

382

383

addSigner(name) {

384

const { publicKey, privateKey } = sodium.crypto_sign_keypair();

385

this.signers.push({

386

name,

387

publicKey,

388

privateKey: privateKey // In practice, each signer keeps their own private key

389

});

390

return sodium.to_hex(publicKey);

391

}

392

393

signMessage(message, signerIndex) {

394

if (signerIndex >= this.signers.length) {

395

throw new Error('Invalid signer index');

396

}

397

398

const messageBytes = sodium.from_string(message);

399

const signature = sodium.crypto_sign_detached(

400

messageBytes,

401

this.signers[signerIndex].privateKey

402

);

403

404

return {

405

signerName: this.signers[signerIndex].name,

406

publicKey: sodium.to_hex(this.signers[signerIndex].publicKey),

407

signature: sodium.to_base64(signature)

408

};

409

}

410

411

verifyMultiSignature(message, signatures) {

412

if (signatures.length < this.requiredSignatures) {

413

return false;

414

}

415

416

const messageBytes = sodium.from_string(message);

417

let validSignatures = 0;

418

419

for (const sig of signatures) {

420

try {

421

const publicKey = sodium.from_hex(sig.publicKey);

422

const signature = sodium.from_base64(sig.signature);

423

424

if (sodium.crypto_sign_verify_detached(signature, messageBytes, publicKey)) {

425

validSignatures++;

426

}

427

} catch (e) {

428

// Invalid signature format

429

continue;

430

}

431

}

432

433

return validSignatures >= this.requiredSignatures;

434

}

435

}

436

437

// Usage

438

const multiSigner = new MultiSigner(2); // Require 2 signatures

439

440

const alice = multiSigner.addSigner('Alice');

441

const bob = multiSigner.addSigner('Bob');

442

const charlie = multiSigner.addSigner('Charlie');

443

444

const message = 'Execute transaction: transfer $1000';

445

446

// Multiple parties sign

447

const signatures = [

448

multiSigner.signMessage(message, 0), // Alice

449

multiSigner.signMessage(message, 1), // Bob

450

];

451

452

const isValid = multiSigner.verifyMultiSignature(message, signatures);

453

console.log('Multi-signature valid:', isValid); // true

454

```

455

456

### Key Conversion for Hybrid Cryptography

457

458

```javascript

459

// Convert signing keys for use with encryption

460

const { publicKey: signPublicKey, privateKey: signPrivateKey } = sodium.crypto_sign_keypair();

461

462

// Convert to encryption keys

463

const encryptPublicKey = sodium.crypto_sign_ed25519_pk_to_curve25519(signPublicKey);

464

const encryptPrivateKey = sodium.crypto_sign_ed25519_sk_to_curve25519(signPrivateKey);

465

466

console.log('Sign public key:', sodium.to_hex(signPublicKey));

467

console.log('Encrypt public key:', sodium.to_hex(encryptPublicKey));

468

469

// Now you can use the same identity for both signing and encryption

470

const message = sodium.from_string('Signed and encrypted message');

471

472

// Sign with Ed25519

473

const signature = sodium.crypto_sign_detached(message, signPrivateKey);

474

475

// Encrypt with Curve25519 (need recipient's key)

476

const recipientKeys = sodium.crypto_box_keypair();

477

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

478

const ciphertext = sodium.crypto_box_easy(message, nonce, recipientKeys.publicKey, encryptPrivateKey);

479

480

console.log('Message signed and encrypted with same identity');

481

```

482

483

## Security Considerations

484

485

- **Private Key Security**: Keep private keys absolutely secret - signatures prove key possession

486

- **Message Integrity**: Signatures protect against message tampering

487

- **Non-Repudiation**: Valid signatures prove the signer cannot deny signing

488

- **Public Key Authentication**: Verify public key authenticity through trusted channels

489

- **Replay Protection**: Include timestamps or sequence numbers in signed messages

490

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

491

492

## Algorithm Details

493

494

Ed25519 provides:

495

- **128-bit security level**: Equivalent to 3072-bit RSA

496

- **Fast verification**: Optimized for signature verification

497

- **Small signatures**: Only 64 bytes per signature

498

- **Deterministic**: Same message and key always produce same signature

499

- **Side-channel resistant**: Designed to prevent timing and power analysis attacks

500

501

Digital signatures are essential for authentication, integrity, and non-repudiation in modern cryptographic systems.