or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

curves.mdecdh.mdeddsa.mdencoding.mdindex.mdkeys-signatures.mdmathematical-functions.md

encoding.mddocs/

0

# Encoding & Decoding Utilities

1

2

Comprehensive encoding and decoding utilities for digital signatures, cryptographic keys, and ASN.1 structures. The ecdsa library provides multiple encoding formats to ensure compatibility with different systems and standards including DER, PEM, SSH, and raw binary formats.

3

4

## Capabilities

5

6

### Signature Encoding Functions

7

8

Functions to encode ECDSA signature pairs (r, s) into various standard formats.

9

10

```python { .api }

11

def sigencode_string(r, s, order):

12

"""

13

Encode signature as raw concatenated byte string.

14

15

Parameters:

16

- r: int, signature r component

17

- s: int, signature s component

18

- order: int, curve order for length calculation

19

20

Returns:

21

bytes, concatenated r||s as fixed-length byte string

22

"""

23

24

def sigencode_strings(r, s, order):

25

"""

26

Encode signature components as separate byte strings.

27

28

Parameters:

29

- r: int, signature r component

30

- s: int, signature s component

31

- order: int, curve order for length calculation

32

33

Returns:

34

tuple[bytes, bytes], (r_bytes, s_bytes) as separate strings

35

"""

36

37

def sigencode_der(r, s, order):

38

"""

39

Encode signature in DER ASN.1 format.

40

41

Parameters:

42

- r: int, signature r component

43

- s: int, signature s component

44

- order: int, curve order (unused but kept for API consistency)

45

46

Returns:

47

bytes, DER-encoded ASN.1 SEQUENCE containing r and s INTEGERs

48

"""

49

50

def sigencode_string_canonize(r, s, order):

51

"""

52

Encode signature as canonical raw byte string (low-s form).

53

54

Parameters:

55

- r: int, signature r component

56

- s: int, signature s component

57

- order: int, curve order for canonicalization

58

59

Returns:

60

bytes, canonical concatenated r||s with s in low form

61

"""

62

63

def sigencode_strings_canonize(r, s, order):

64

"""

65

Encode signature components as canonical separate byte strings.

66

67

Parameters:

68

- r: int, signature r component

69

- s: int, signature s component

70

- order: int, curve order for canonicalization

71

72

Returns:

73

tuple[bytes, bytes], canonical (r_bytes, s_bytes) with low-s

74

"""

75

76

def sigencode_der_canonize(r, s, order):

77

"""

78

Encode signature in canonical DER format (low-s form).

79

80

Parameters:

81

- r: int, signature r component

82

- s: int, signature s component

83

- order: int, curve order for canonicalization

84

85

Returns:

86

bytes, canonical DER-encoded signature with low-s

87

"""

88

```

89

90

### Signature Decoding Functions

91

92

Functions to decode signatures from various formats back to (r, s) integer pairs.

93

94

```python { .api }

95

def sigdecode_string(signature, order):

96

"""

97

Decode signature from raw concatenated byte string.

98

99

Parameters:

100

- signature: bytes, concatenated r||s byte string

101

- order: int, curve order for length calculation

102

103

Returns:

104

tuple[int, int], (r, s) signature components

105

106

Raises:

107

ValueError: if signature length is incorrect

108

"""

109

110

def sigdecode_strings(rs_strings, order):

111

"""

112

Decode signature from separate byte string tuple.

113

114

Parameters:

115

- rs_strings: tuple[bytes, bytes], (r_bytes, s_bytes)

116

- order: int, curve order for validation

117

118

Returns:

119

tuple[int, int], (r, s) signature components

120

"""

121

122

def sigdecode_der(sig_der, order):

123

"""

124

Decode signature from DER ASN.1 format.

125

126

Parameters:

127

- sig_der: bytes, DER-encoded ASN.1 signature

128

- order: int, curve order for validation

129

130

Returns:

131

tuple[int, int], (r, s) signature components

132

133

Raises:

134

UnexpectedDER: if DER structure is invalid

135

"""

136

```

137

138

### Number and String Conversion Utilities

139

140

Low-level utilities for converting between integers and byte strings with proper length handling.

141

142

```python { .api }

143

def number_to_string(num, order):

144

"""

145

Convert integer to fixed-length byte string based on curve order.

146

147

Parameters:

148

- num: int, number to convert

149

- order: int, curve order determining output length

150

151

Returns:

152

bytes, big-endian byte string with length based on order

153

"""

154

155

def number_to_string_crop(num, order):

156

"""

157

Convert integer to minimal-length byte string.

158

159

Parameters:

160

- num: int, number to convert

161

- order: int, curve order for validation

162

163

Returns:

164

bytes, minimal big-endian byte string (no leading zeros)

165

"""

166

167

def string_to_number(string):

168

"""

169

Convert byte string to integer.

170

171

Parameters:

172

- string: bytes, big-endian byte string

173

174

Returns:

175

int, converted number

176

"""

177

178

def string_to_number_fixedlen(string, order):

179

"""

180

Convert byte string to integer with length validation.

181

182

Parameters:

183

- string: bytes, big-endian byte string

184

- order: int, curve order for length validation

185

186

Returns:

187

int, converted number

188

189

Raises:

190

ValueError: if string length doesn't match expected length

191

"""

192

```

193

194

### DER ASN.1 Encoding Functions

195

196

Functions for encoding data structures in Distinguished Encoding Rules (DER) format.

197

198

```python { .api }

199

def encode_sequence(*encoded_pieces):

200

"""

201

Encode ASN.1 SEQUENCE containing multiple elements.

202

203

Parameters:

204

- encoded_pieces: bytes, already DER-encoded elements

205

206

Returns:

207

bytes, DER-encoded SEQUENCE

208

"""

209

210

def encode_integer(r):

211

"""

212

Encode integer as ASN.1 INTEGER.

213

214

Parameters:

215

- r: int, integer to encode

216

217

Returns:

218

bytes, DER-encoded INTEGER

219

"""

220

221

def encode_octet_string(s):

222

"""

223

Encode byte string as ASN.1 OCTET STRING.

224

225

Parameters:

226

- s: bytes, byte string to encode

227

228

Returns:

229

bytes, DER-encoded OCTET STRING

230

"""

231

232

def encode_bitstring(s, unused=0):

233

"""

234

Encode byte string as ASN.1 BIT STRING.

235

236

Parameters:

237

- s: bytes, byte string to encode

238

- unused: int, number of unused bits in last byte (0-7)

239

240

Returns:

241

bytes, DER-encoded BIT STRING

242

"""

243

244

def encode_oid(first, second, *pieces):

245

"""

246

Encode ASN.1 OBJECT IDENTIFIER from components.

247

248

Parameters:

249

- first: int, first OID component

250

- second: int, second OID component

251

- pieces: int, additional OID components

252

253

Returns:

254

bytes, DER-encoded OBJECT IDENTIFIER

255

"""

256

257

def encode_constructed(tag, value):

258

"""

259

Encode constructed ASN.1 type with custom tag.

260

261

Parameters:

262

- tag: int, ASN.1 tag number

263

- value: bytes, content to encode

264

265

Returns:

266

bytes, DER-encoded constructed type

267

"""

268

269

def encode_implicit(tag, value, cls="context-specific"):

270

"""

271

Encode implicit ASN.1 tag.

272

273

Parameters:

274

- tag: int, tag number

275

- value: bytes, content to encode

276

- cls: str, tag class ("context-specific", "application", etc.)

277

278

Returns:

279

bytes, DER-encoded implicit tag

280

"""

281

```

282

283

### DER ASN.1 Decoding Functions

284

285

Functions for decoding DER-encoded ASN.1 structures.

286

287

```python { .api }

288

def remove_sequence(string):

289

"""

290

Decode ASN.1 SEQUENCE and return content.

291

292

Parameters:

293

- string: bytes, DER-encoded data starting with SEQUENCE

294

295

Returns:

296

tuple[bytes, bytes], (sequence_content, remaining_data)

297

298

Raises:

299

UnexpectedDER: if not a valid SEQUENCE

300

"""

301

302

def remove_integer(string):

303

"""

304

Decode ASN.1 INTEGER and return value.

305

306

Parameters:

307

- string: bytes, DER-encoded data starting with INTEGER

308

309

Returns:

310

tuple[int, bytes], (integer_value, remaining_data)

311

312

Raises:

313

UnexpectedDER: if not a valid INTEGER

314

"""

315

316

def remove_octet_string(string):

317

"""

318

Decode ASN.1 OCTET STRING and return content.

319

320

Parameters:

321

- string: bytes, DER-encoded data starting with OCTET STRING

322

323

Returns:

324

tuple[bytes, bytes], (octet_string_content, remaining_data)

325

"""

326

327

def remove_bitstring(string, expect_unused=0):

328

"""

329

Decode ASN.1 BIT STRING and return content.

330

331

Parameters:

332

- string: bytes, DER-encoded data starting with BIT STRING

333

- expect_unused: int, expected number of unused bits

334

335

Returns:

336

tuple[bytes, bytes], (bitstring_content, remaining_data)

337

338

Raises:

339

UnexpectedDER: if unused bits don't match expected

340

"""

341

342

def remove_object(string):

343

"""

344

Decode ASN.1 OBJECT IDENTIFIER and return OID tuple.

345

346

Parameters:

347

- string: bytes, DER-encoded data starting with OBJECT IDENTIFIER

348

349

Returns:

350

tuple[tuple[int, ...], bytes], (oid_tuple, remaining_data)

351

"""

352

353

def remove_constructed(string):

354

"""

355

Decode constructed ASN.1 type.

356

357

Parameters:

358

- string: bytes, DER-encoded constructed type

359

360

Returns:

361

tuple[int, bytes, bytes], (tag, content, remaining_data)

362

"""

363

```

364

365

### DER/PEM Conversion Utilities

366

367

Functions for converting between DER binary format and PEM text format.

368

369

```python { .api }

370

def encode_length(l):

371

"""

372

Encode ASN.1 length field.

373

374

Parameters:

375

- l: int, length to encode

376

377

Returns:

378

bytes, DER-encoded length

379

"""

380

381

def read_length(string):

382

"""

383

Decode ASN.1 length field.

384

385

Parameters:

386

- string: bytes, DER data starting with length field

387

388

Returns:

389

tuple[int, bytes], (length, remaining_data)

390

"""

391

392

def is_sequence(string):

393

"""

394

Check if data starts with ASN.1 SEQUENCE.

395

396

Parameters:

397

- string: bytes, DER-encoded data

398

399

Returns:

400

bool, True if starts with SEQUENCE tag

401

"""

402

403

def topem(der, name):

404

"""

405

Convert DER to PEM format.

406

407

Parameters:

408

- der: bytes, DER-encoded data

409

- name: str, PEM label (e.g., "PRIVATE KEY", "PUBLIC KEY")

410

411

Returns:

412

str, PEM-formatted string with headers and base64 encoding

413

"""

414

415

def unpem(pem):

416

"""

417

Convert PEM to DER format.

418

419

Parameters:

420

- pem: str or bytes, PEM-formatted data

421

422

Returns:

423

bytes, DER-encoded data (base64 decoded, headers removed)

424

"""

425

```

426

427

### Object Identifiers and Constants

428

429

Standard ASN.1 Object Identifiers used in elliptic curve cryptography.

430

431

```python { .api }

432

oid_ecPublicKey: tuple # (1, 2, 840, 10045, 2, 1) - EC public key algorithm

433

encoded_oid_ecPublicKey: bytes # DER-encoded version of above

434

oid_ecDH: tuple # (1, 3, 132, 1, 12) - ECDH algorithm

435

oid_ecMQV: tuple # (1, 3, 132, 1, 13) - ECMQV algorithm

436

```

437

438

### Random Number Generation

439

440

Utilities for cryptographically secure random number generation.

441

442

```python { .api }

443

class PRNG:

444

"""Pseudorandom number generator for deterministic signatures."""

445

446

def randrange(order, entropy=None):

447

"""

448

Generate cryptographically secure random number in range [1, order-1].

449

450

Parameters:

451

- order: int, upper bound (exclusive)

452

- entropy: callable or None, entropy source (default: os.urandom)

453

454

Returns:

455

int, random number in specified range

456

"""

457

```

458

459

## Exception Classes

460

461

```python { .api }

462

class UnexpectedDER(Exception):

463

"""Raised when DER encoding or decoding encounters invalid structure."""

464

```

465

466

## Usage Examples

467

468

### Signature Encoding and Decoding

469

470

```python

471

from ecdsa import SigningKey, NIST256p

472

from ecdsa.util import sigencode_der, sigdecode_der, sigencode_string, sigdecode_string

473

474

# Generate key and sign data

475

sk = SigningKey.generate(curve=NIST256p)

476

vk = sk.verifying_key

477

message = b"Test message for encoding"

478

479

# Sign with different encodings

480

sig_raw = sk.sign(message, sigencode=sigencode_string)

481

sig_der = sk.sign(message, sigencode=sigencode_der)

482

483

print(f"Raw signature length: {len(sig_raw)} bytes")

484

print(f"DER signature length: {len(sig_der)} bytes")

485

486

# Verify with corresponding decodings

487

vk.verify(sig_raw, message, sigdecode=sigdecode_string)

488

vk.verify(sig_der, message, sigdecode=sigdecode_der)

489

490

# Decode signatures to (r, s) pairs

491

r_raw, s_raw = sigdecode_string(sig_raw, sk.curve.order)

492

r_der, s_der = sigdecode_der(sig_der, sk.curve.order)

493

494

print(f"Raw signature r: {r_raw}")

495

print(f"DER signature r: {r_der}")

496

print(f"Signatures match: {r_raw == r_der and s_raw == s_der}")

497

```

498

499

### Working with DER/ASN.1 Structures

500

501

```python

502

from ecdsa.der import encode_sequence, encode_integer, remove_sequence, remove_integer

503

504

# Create DER-encoded sequence containing two integers

505

r, s = 12345, 67890

506

der_r = encode_integer(r)

507

der_s = encode_integer(s)

508

der_sequence = encode_sequence(der_r, der_s)

509

510

print(f"DER sequence: {der_sequence.hex()}")

511

512

# Decode the sequence

513

sequence_content, remaining = remove_sequence(der_sequence)

514

decoded_r, content_after_r = remove_integer(sequence_content)

515

decoded_s, final_remaining = remove_integer(content_after_r)

516

517

print(f"Decoded r: {decoded_r}, s: {decoded_s}")

518

print(f"Original r: {r}, s: {s}")

519

print(f"Match: {decoded_r == r and decoded_s == s}")

520

```

521

522

### PEM/DER Conversion

523

524

```python

525

from ecdsa import SigningKey, NIST256p

526

from ecdsa.der import topem, unpem

527

528

# Generate key and export as DER

529

sk = SigningKey.generate(curve=NIST256p)

530

der_key = sk.to_der()

531

532

# Convert DER to PEM

533

pem_key = topem(der_key, "EC PRIVATE KEY")

534

print("PEM format:")

535

print(pem_key.decode())

536

537

# Convert PEM back to DER

538

der_from_pem = unpem(pem_key)

539

print(f"DER roundtrip successful: {der_key == der_from_pem}")

540

541

# Also works with public keys

542

vk = sk.verifying_key

543

der_pubkey = vk.to_der()

544

pem_pubkey = topem(der_pubkey, "PUBLIC KEY")

545

der_pubkey_roundtrip = unpem(pem_pubkey)

546

print(f"Public key roundtrip successful: {der_pubkey == der_pubkey_roundtrip}")

547

```

548

549

### Number and String Conversions

550

551

```python

552

from ecdsa.util import number_to_string, string_to_number, number_to_string_crop

553

from ecdsa import NIST256p

554

555

# Work with curve order for proper length calculation

556

order = NIST256p.order

557

test_number = 0x1234567890abcdef

558

559

# Convert number to fixed-length string

560

fixed_bytes = number_to_string(test_number, order)

561

minimal_bytes = number_to_string_crop(test_number, order)

562

563

print(f"Original number: 0x{test_number:x}")

564

print(f"Fixed length: {len(fixed_bytes)} bytes - {fixed_bytes.hex()}")

565

print(f"Minimal length: {len(minimal_bytes)} bytes - {minimal_bytes.hex()}")

566

567

# Convert back to numbers

568

recovered_fixed = string_to_number(fixed_bytes)

569

recovered_minimal = string_to_number(minimal_bytes)

570

571

print(f"Recovered from fixed: 0x{recovered_fixed:x}")

572

print(f"Recovered from minimal: 0x{recovered_minimal:x}")

573

print(f"All match: {test_number == recovered_fixed == recovered_minimal}")

574

```

575

576

### Canonical Signatures (Low-S Form)

577

578

```python

579

from ecdsa import SigningKey, SECP256k1

580

from ecdsa.util import sigencode_der_canonize, sigdecode_der

581

582

# Bitcoin uses canonical signatures (low-s form)

583

sk = SigningKey.generate(curve=SECP256k1)

584

message = b"Bitcoin transaction data"

585

586

# Generate canonical signature

587

canonical_sig = sk.sign(message, sigencode=sigencode_der_canonize)

588

589

# Decode and verify s is in low form

590

r, s = sigdecode_der(canonical_sig, sk.curve.order)

591

print(f"Signature r: {r}")

592

print(f"Signature s: {s}")

593

print(f"s is canonical (low): {s <= sk.curve.order // 2}")

594

595

# Verify signature

596

vk = sk.verifying_key

597

vk.verify(canonical_sig, message, sigdecode=sigdecode_der)

598

print("Canonical signature verified successfully")

599

```

600

601

### Advanced DER Operations

602

603

```python

604

from ecdsa.der import (

605

encode_oid, remove_object, encode_octet_string, remove_octet_string,

606

encode_bitstring, remove_bitstring

607

)

608

609

# Work with Object Identifiers

610

secp256k1_oid = (1, 3, 132, 0, 10) # secp256k1 curve OID

611

encoded_oid = encode_oid(*secp256k1_oid)

612

decoded_oid, remaining = remove_object(encoded_oid)

613

614

print(f"Original OID: {secp256k1_oid}")

615

print(f"Decoded OID: {decoded_oid}")

616

print(f"OID match: {secp256k1_oid == decoded_oid}")

617

618

# Work with octet strings

619

test_data = b"Secret key material"

620

encoded_octets = encode_octet_string(test_data)

621

decoded_octets, remaining = remove_octet_string(encoded_octets)

622

623

print(f"Octet string roundtrip: {test_data == decoded_octets}")

624

625

# Work with bit strings (for public key encoding)

626

pubkey_data = b"\x04" + b"x" * 64 # Uncompressed public key format

627

encoded_bits = encode_bitstring(pubkey_data, unused=0)

628

decoded_bits, remaining = remove_bitstring(encoded_bits, expect_unused=0)

629

630

print(f"Bit string roundtrip: {pubkey_data == decoded_bits}")

631

```