or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dns-constants.mddns-exceptions.mddns-messages.mddns-names.mddns-queries.mddns-records.mddns-resolution.mddns-updates.mddns-utilities.mddns-zones.mddnssec.mdindex.mdtsig.md

tsig.mddocs/

0

# TSIG Authentication

1

2

Transaction Signature (TSIG) authentication functionality for securing DNS messages with cryptographic signatures. Provides key management, signature generation, and verification for authenticated DNS communication.

3

4

## Capabilities

5

6

### TSIG Signing

7

8

Sign DNS messages with TSIG authentication.

9

10

```python { .api }

11

def sign(wire, keyring, keyname, fudge=300, original_id=None, tsig_error=0,

12

other_data=b'', algorithm='HMAC-MD5.SIG-ALG.REG.INT'):

13

"""

14

Sign a DNS message with TSIG.

15

16

Args:

17

wire (bytes): Wire format DNS message to sign

18

keyring (dict): TSIG keyring containing keys

19

keyname (str or dns.name.Name): Name of key to use

20

fudge (int): Time fudge factor in seconds (default 300)

21

original_id (int): Original message ID for error responses

22

tsig_error (int): TSIG error code

23

other_data (bytes): Additional data for error responses

24

algorithm (str): TSIG algorithm name

25

26

Returns:

27

bytes: Signed wire format message with TSIG record

28

"""

29

30

def validate(wire, keyring, request_mac, now=None, request_fudge=None):

31

"""

32

Validate TSIG signature on a DNS message.

33

34

Args:

35

wire (bytes): Wire format DNS message with TSIG

36

keyring (dict): TSIG keyring containing keys

37

request_mac (bytes): MAC from original request

38

now (float): Current time (default system time)

39

request_fudge (int): Fudge from original request

40

41

Returns:

42

tuple: (validated_wire, tsig_ctx) where validated_wire is the

43

message without TSIG and tsig_ctx is context for multi-message

44

45

Raises:

46

dns.tsig.BadSignature: If signature validation fails

47

dns.tsig.BadTime: If time is outside fudge window

48

dns.tsig.BadKey: If key is not found or invalid

49

"""

50

```

51

52

### Key Management

53

54

Manage TSIG keys and keyrings for authentication.

55

56

```python { .api }

57

def from_text(textring):

58

"""

59

Create a TSIG keyring from a dictionary of text key data.

60

61

Args:

62

textring (dict): Dictionary mapping key names to base64-encoded keys

63

{key_name: base64_key_data, ...}

64

65

Returns:

66

dict: TSIG keyring ready for use

67

68

Example:

69

keyring = dns.tsigkeyring.from_text({

70

'key1.example.com.': 'base64encodedkeydata',

71

'key2.example.com.': 'anotherkeyinbase64'

72

})

73

"""

74

75

def to_text(keyring):

76

"""

77

Convert a TSIG keyring to text format.

78

79

Args:

80

keyring (dict): TSIG keyring

81

82

Returns:

83

dict: Dictionary with base64-encoded key values

84

"""

85

```

86

87

### TSIG Algorithms

88

89

Supported TSIG algorithms and algorithm management.

90

91

```python { .api }

92

# Standard TSIG algorithms

93

HMAC_MD5 = 'HMAC-MD5.SIG-ALG.REG.INT'

94

HMAC_SHA1 = 'hmac-sha1'

95

HMAC_SHA224 = 'hmac-sha224'

96

HMAC_SHA256 = 'hmac-sha256'

97

HMAC_SHA384 = 'hmac-sha384'

98

HMAC_SHA512 = 'hmac-sha512'

99

100

# Default algorithm

101

default_algorithm = HMAC_MD5

102

103

def algorithm_from_text(text):

104

"""

105

Convert algorithm name to canonical form.

106

107

Args:

108

text (str): Algorithm name

109

110

Returns:

111

dns.name.Name: Canonical algorithm name

112

"""

113

114

def algorithm_to_text(algorithm):

115

"""

116

Convert algorithm to text format.

117

118

Args:

119

algorithm (dns.name.Name): Algorithm name

120

121

Returns:

122

str: Text representation of algorithm

123

"""

124

```

125

126

### TSIG Context

127

128

Multi-message TSIG context for zone transfers and other multi-message operations.

129

130

```python { .api }

131

class HMACTSig:

132

"""

133

HMAC-based TSIG context for multi-message authentication.

134

135

Used for zone transfers and other operations that span multiple

136

DNS messages, maintaining authentication state across messages.

137

"""

138

139

def __init__(self, keyring, keyname, algorithm=default_algorithm):

140

"""

141

Initialize TSIG context.

142

143

Args:

144

keyring (dict): TSIG keyring

145

keyname (str or dns.name.Name): Key name to use

146

algorithm (str): TSIG algorithm

147

"""

148

149

def sign(self, wire, fudge=300):

150

"""

151

Sign a message using this TSIG context.

152

153

Args:

154

wire (bytes): Wire format message

155

fudge (int): Time fudge factor

156

157

Returns:

158

bytes: Signed message with TSIG

159

"""

160

161

def validate(self, wire):

162

"""

163

Validate a message using this TSIG context.

164

165

Args:

166

wire (bytes): Wire format message with TSIG

167

168

Returns:

169

bytes: Validated message without TSIG

170

"""

171

```

172

173

### TSIG Record Data

174

175

TSIG resource record data structure and operations.

176

177

```python { .api }

178

class TSIG(dns.rdata.Rdata):

179

"""

180

TSIG resource record data.

181

182

Attributes:

183

algorithm (dns.name.Name): TSIG algorithm name

184

time_signed (int): Time message was signed (seconds since epoch)

185

fudge (int): Time fudge factor in seconds

186

mac (bytes): Message authentication code

187

original_id (int): Original message ID

188

error (int): TSIG error code

189

other (bytes): Other data (for error responses)

190

"""

191

192

def __init__(self, rdclass, rdtype, algorithm, time_signed, fudge, mac,

193

original_id, error, other):

194

"""Initialize TSIG record data."""

195

196

def to_text(self, origin=None, relativize=True):

197

"""Convert TSIG to text format."""

198

199

def to_wire(self, file, compress=None, origin=None):

200

"""Convert TSIG to wire format."""

201

```

202

203

## Usage Examples

204

205

### Basic TSIG Authentication

206

207

```python

208

import dns.message

209

import dns.query

210

import dns.tsigkeyring

211

import dns.name

212

213

# Create TSIG keyring

214

keyring = dns.tsigkeyring.from_text({

215

'mykey.example.com.': 'base64-encoded-shared-secret'

216

})

217

218

# Create query with TSIG

219

qname = dns.name.from_text('secure.example.com.')

220

query = dns.message.make_query(qname, dns.rdatatype.A)

221

query.use_tsig(keyring, 'mykey.example.com.')

222

223

# Send authenticated query

224

response = dns.query.udp(query, '192.0.2.1')

225

226

# Check TSIG authentication

227

if response.had_tsig():

228

if response.tsig_error() == 0:

229

print("TSIG authentication successful")

230

231

# Process authenticated response

232

for rrset in response.answer:

233

print(f"Authenticated answer: {rrset}")

234

else:

235

print(f"TSIG authentication failed: {response.tsig_error()}")

236

else:

237

print("No TSIG in response")

238

```

239

240

### TSIG with Different Algorithms

241

242

```python

243

import dns.tsigkeyring

244

import dns.tsig

245

import dns.message

246

247

# Create keyring with SHA-256 key

248

keyring = dns.tsigkeyring.from_text({

249

'sha256-key.example.com.': 'base64-sha256-key-data'

250

})

251

252

# Create message with SHA-256 TSIG

253

query = dns.message.make_query('test.example.com.', dns.rdatatype.A)

254

query.use_tsig(keyring, 'sha256-key.example.com.', algorithm=dns.tsig.HMAC_SHA256)

255

256

# Sign and send

257

response = dns.query.tcp(query, '192.0.2.1')

258

259

# Different algorithms for different security levels

260

algorithms = [

261

(dns.tsig.HMAC_SHA256, 'SHA-256 (recommended)'),

262

(dns.tsig.HMAC_SHA512, 'SHA-512 (high security)'),

263

(dns.tsig.HMAC_SHA1, 'SHA-1 (legacy compatibility)'),

264

(dns.tsig.HMAC_MD5, 'MD5 (legacy only)')

265

]

266

267

for alg, description in algorithms:

268

print(f"Algorithm: {alg} - {description}")

269

```

270

271

### Multi-Message TSIG (Zone Transfers)

272

273

```python

274

import dns.query

275

import dns.tsig

276

import dns.tsigkeyring

277

import dns.name

278

279

# Setup TSIG for zone transfer

280

keyring = dns.tsigkeyring.from_text({

281

'xfer-key.example.com.': 'zone-transfer-key-data'

282

})

283

284

zone_name = dns.name.from_text('example.com.')

285

286

# Perform authenticated zone transfer

287

try:

288

# TSIG context is automatically managed by dns.query.xfr

289

messages = dns.query.xfr('192.0.2.1', zone_name,

290

keyring=keyring,

291

keyname='xfer-key.example.com.',

292

keyalgorithm=dns.tsig.HMAC_SHA256)

293

294

message_count = 0

295

for message in messages:

296

message_count += 1

297

298

# Verify each message has valid TSIG

299

if message.had_tsig():

300

if message.tsig_error() == 0:

301

print(f"Message {message_count}: TSIG valid")

302

else:

303

print(f"Message {message_count}: TSIG error {message.tsig_error()}")

304

break

305

else:

306

print(f"Message {message_count}: No TSIG (unexpected)")

307

break

308

309

# Process zone data

310

for rrset in message.answer:

311

if rrset.rdtype != dns.rdatatype.TSIG: # Skip TSIG records

312

print(f" {rrset.name} {rrset.rdtype}")

313

314

print(f"Zone transfer complete: {message_count} messages")

315

316

except dns.tsig.BadSignature:

317

print("TSIG signature validation failed")

318

except dns.tsig.BadTime:

319

print("TSIG time validation failed")

320

except dns.exception.DNSException as e:

321

print(f"Zone transfer failed: {e}")

322

```

323

324

### Dynamic Updates with TSIG

325

326

```python

327

import dns.update

328

import dns.tsigkeyring

329

import dns.query

330

import dns.tsig

331

332

# Create authenticated update

333

keyring = dns.tsigkeyring.from_text({

334

'update-key.example.com.': 'dynamic-update-key-data'

335

})

336

337

update = dns.update.Update('example.com.',

338

keyring=keyring,

339

keyname='update-key.example.com.',

340

keyalgorithm=dns.tsig.HMAC_SHA256)

341

342

# Add authenticated updates

343

update.add('dynamic.example.com.', 300, 'A', '192.0.2.100')

344

update.add('dynamic.example.com.', 300, 'TXT', 'Updated with TSIG authentication')

345

346

# Send authenticated update

347

response = dns.query.tcp(update, '192.0.2.1')

348

349

if response.rcode() == dns.rcode.NOERROR:

350

print("Authenticated update successful")

351

352

# Verify response authentication

353

if response.had_tsig() and response.tsig_error() == 0:

354

print("Response TSIG verification successful")

355

else:

356

print(f"Update failed: {dns.rcode.to_text(response.rcode())}")

357

```

358

359

### Manual TSIG Operations

360

361

```python

362

import dns.tsig

363

import dns.tsigkeyring

364

import dns.message

365

import time

366

367

# Create keyring and message

368

keyring = dns.tsigkeyring.from_text({

369

'manual-key.example.com.': 'manual-signing-key-data'

370

})

371

372

query = dns.message.make_query('manual.example.com.', dns.rdatatype.A)

373

374

# Manual TSIG signing

375

wire = query.to_wire()

376

signed_wire = dns.tsig.sign(wire, keyring, 'manual-key.example.com.',

377

fudge=300, algorithm=dns.tsig.HMAC_SHA256)

378

379

print(f"Original message size: {len(wire)} bytes")

380

print(f"Signed message size: {len(signed_wire)} bytes")

381

382

# Manual TSIG validation

383

try:

384

validated_wire, tsig_ctx = dns.tsig.validate(signed_wire, keyring, b'')

385

print("Manual TSIG validation successful")

386

print(f"Validated message size: {len(validated_wire)} bytes")

387

388

# Parse validated message

389

validated_msg = dns.message.from_wire(validated_wire)

390

print(f"Validated message ID: {validated_msg.id}")

391

392

except dns.tsig.BadSignature:

393

print("TSIG signature validation failed")

394

except dns.tsig.BadTime:

395

print("TSIG time validation failed")

396

```

397

398

### Key Generation and Management

399

400

```python

401

import dns.tsigkeyring

402

import base64

403

import secrets

404

405

# Generate random key material

406

def generate_tsig_key(algorithm='sha256', key_size=32):

407

"""Generate random TSIG key material."""

408

key_bytes = secrets.token_bytes(key_size)

409

key_b64 = base64.b64encode(key_bytes).decode()

410

return key_b64

411

412

# Generate keys for different algorithms

413

keys = {

414

'md5-key.example.com.': generate_tsig_key('md5', 16), # 128-bit

415

'sha1-key.example.com.': generate_tsig_key('sha1', 20), # 160-bit

416

'sha256-key.example.com.': generate_tsig_key('sha256', 32), # 256-bit

417

'sha512-key.example.com.': generate_tsig_key('sha512', 64) # 512-bit

418

}

419

420

# Create keyring

421

keyring = dns.tsigkeyring.from_text(keys)

422

423

# Export keyring for storage

424

exported_keys = dns.tsigkeyring.to_text(keyring)

425

print("Generated TSIG keys:")

426

for name, key in exported_keys.items():

427

print(f" {name}: {key[:20]}...") # Show first 20 chars

428

429

# Key rotation example

430

old_keyring = dns.tsigkeyring.from_text({

431

'old-key.example.com.': 'old-key-material'

432

})

433

434

new_keyring = dns.tsigkeyring.from_text({

435

'new-key.example.com.': 'new-key-material'

436

})

437

438

# Combined keyring for transition period

439

combined_keyring = {**old_keyring, **new_keyring}

440

```

441

442

### Error Handling and Diagnostics

443

444

```python

445

import dns.tsig

446

import dns.message

447

import dns.query

448

import dns.tsigkeyring

449

450

def diagnose_tsig_failure(response):

451

"""Diagnose TSIG authentication failures."""

452

453

if not response.had_tsig():

454

return "No TSIG record in response"

455

456

error_code = response.tsig_error()

457

error_messages = {

458

0: "No error",

459

16: "Bad signature (BADSIG)",

460

17: "Bad key (BADKEY)",

461

18: "Bad time (BADTIME)",

462

19: "Bad mode (BADMODE)",

463

20: "Bad name (BADNAME)",

464

21: "Bad algorithm (BADALG)",

465

22: "Bad truncation (BADTRUNC)"

466

}

467

468

error_msg = error_messages.get(error_code, f"Unknown error code {error_code}")

469

return f"TSIG error: {error_msg}"

470

471

# Example usage with error handling

472

try:

473

keyring = dns.tsigkeyring.from_text({

474

'test-key.example.com.': 'test-key-data'

475

})

476

477

query = dns.message.make_query('test.example.com.', dns.rdatatype.A)

478

query.use_tsig(keyring, 'test-key.example.com.')

479

480

response = dns.query.udp(query, '192.0.2.1')

481

482

if response.had_tsig():

483

if response.tsig_error() == 0:

484

print("TSIG authentication successful")

485

else:

486

print(diagnose_tsig_failure(response))

487

else:

488

print("Server does not support TSIG")

489

490

except dns.tsig.BadSignature as e:

491

print(f"TSIG signature error: {e}")

492

except dns.tsig.BadTime as e:

493

print(f"TSIG time error: {e}")

494

except dns.tsig.BadKey as e:

495

print(f"TSIG key error: {e}")

496

except dns.exception.DNSException as e:

497

print(f"DNS error: {e}")

498

```

499

500

## TSIG Error Codes

501

502

```python { .api }

503

# TSIG-specific error codes

504

NOERROR = 0 # No error

505

BADSIG = 16 # Bad signature

506

BADKEY = 17 # Bad key

507

BADTIME = 18 # Bad time

508

BADMODE = 19 # Bad mode

509

BADNAME = 20 # Bad name

510

BADALG = 21 # Bad algorithm

511

BADTRUNC = 22 # Bad truncation

512

```

513

514

## Integration Notes

515

516

TSIG authentication integrates with:

517

518

- **DNS Queries**: Use with `dns.query` functions for authenticated queries

519

- **Dynamic Updates**: Use with `dns.update` for secure zone updates

520

- **Zone Transfers**: Automatic TSIG handling in `dns.query.xfr`

521

- **Message Handling**: TSIG records in `dns.message` for manual operations

522

- **Key Management**: Secure key storage and rotation practices

523

524

## Exceptions

525

526

```python { .api }

527

class TSIGError(DNSException):

528

"""Base class for TSIG errors."""

529

530

class BadSignature(TSIGError):

531

"""TSIG signature verification failed."""

532

533

class BadTime(TSIGError):

534

"""TSIG time is outside acceptable window."""

535

536

class BadKey(TSIGError):

537

"""TSIG key is unknown or invalid."""

538

539

class BadAlgorithm(TSIGError):

540

"""TSIG algorithm is not supported."""

541

542

class BadMode(TSIGError):

543

"""TSIG mode is invalid."""

544

545

class BadName(TSIGError):

546

"""TSIG key name is invalid."""

547

548

class BadTruncation(TSIGError):

549

"""TSIG truncation is invalid."""

550

```