or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

challenge-response.mdconfiguration.mddevice-discovery.mddevice-interface.mdexceptions.mdindex.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

Helper functions for data processing, CRC validation, hex dumping, ModHex encoding/decoding, and cryptographic operations commonly needed in YubiKey applications.

3

4

## Capabilities

5

6

### CRC Validation

7

8

Functions for calculating and validating ISO13239 CRC checksums used in YubiKey data integrity verification.

9

10

```python { .api }

11

def crc16(data):

12

"""

13

Calculate ISO13239 CRC checksum of input data.

14

15

Used internally by YubiKey protocol for data integrity verification.

16

Useful for custom protocol implementations and data validation.

17

18

Parameters:

19

- data (bytes): Input data to calculate checksum for

20

21

Returns:

22

int: 16-bit CRC checksum value

23

"""

24

25

def validate_crc16(data):

26

"""

27

Validate CRC16 checksum of data.

28

29

Verifies that the last 2 bytes of the input data contain a valid

30

CRC16 checksum for the preceding data.

31

32

Parameters:

33

- data (bytes): Data with CRC16 checksum appended

34

35

Returns:

36

bool: True if CRC checksum is valid, False otherwise

37

38

Raises:

39

InputError: If data is too short to contain CRC

40

"""

41

```

42

43

CRC validation example:

44

45

```python

46

import yubico.yubico_util as util

47

48

# Calculate CRC for data

49

data = b"Hello, YubiKey!"

50

crc_value = util.crc16(data)

51

print(f"CRC16 of '{data.decode()}': 0x{crc_value:04x}")

52

53

# Create data with CRC appended

54

data_with_crc = data + crc_value.to_bytes(2, 'little')

55

56

# Validate CRC

57

is_valid = util.validate_crc16(data_with_crc)

58

print(f"CRC validation: {'PASS' if is_valid else 'FAIL'}")

59

60

# Test with corrupted data

61

corrupted_data = data + b'\x00\x00' # Wrong CRC

62

is_valid = util.validate_crc16(corrupted_data)

63

print(f"Corrupted data validation: {'PASS' if is_valid else 'FAIL'}")

64

```

65

66

### Hex Dump Utilities

67

68

Functions for creating human-readable hex dumps of binary data for debugging and analysis.

69

70

```python { .api }

71

def hexdump(src, length=8, colorize=False):

72

"""

73

Create formatted hexadecimal dump of binary data.

74

75

Produces output similar to the Unix hexdump utility, showing both

76

hex values and ASCII representation for debugging and analysis.

77

78

Parameters:

79

- src (bytes): Source data to dump

80

- length (int): Number of bytes per line (default: 8)

81

- colorize (bool): Enable colored output (default: False)

82

83

Returns:

84

str: Formatted hex dump string

85

"""

86

```

87

88

Hex dump example:

89

90

```python

91

import yubico.yubico_util as util

92

93

# Create sample data

94

data = b"YubiKey challenge-response data with binary\x00\x01\x02\x03"

95

96

# Basic hex dump

97

print("Basic hex dump:")

98

print(util.hexdump(data))

99

100

# Custom width

101

print("\nWide hex dump (16 bytes per line):")

102

print(util.hexdump(data, length=16))

103

104

# Colorized output (if terminal supports it)

105

print("\nColorized hex dump:")

106

print(util.hexdump(data, colorize=True))

107

108

# Dump YubiKey response data

109

import yubico

110

try:

111

yk = yubico.find_yubikey()

112

response = yk.challenge_response(b"test challenge", slot=1)

113

print("\nYubiKey response hex dump:")

114

print(util.hexdump(response))

115

except yubico.yubico_exception.YubicoError:

116

print("YubiKey not available for response dump")

117

```

118

119

### ModHex Encoding

120

121

Functions for working with ModHex (Modified Hexadecimal) encoding used by YubiKey OTP output.

122

123

```python { .api }

124

def modhex_decode(data):

125

"""

126

Decode ModHex string to regular hexadecimal.

127

128

ModHex uses the characters 'cbdefghijklnrtuv' instead of '0123456789abcdef'

129

to avoid keyboard layout issues and ensure reliable character input.

130

131

Parameters:

132

- data (str): ModHex encoded string

133

134

Returns:

135

str: Regular hexadecimal string

136

137

Raises:

138

InputError: If input contains invalid ModHex characters

139

"""

140

```

141

142

ModHex example:

143

144

```python

145

import yubico.yubico_util as util

146

147

# ModHex characters: cbdefghijklnrtuv (instead of 0123456789abcdef)

148

modhex_data = "vvhhhbbbdvcv" # Example ModHex string

149

150

try:

151

hex_data = util.modhex_decode(modhex_data)

152

print(f"ModHex: {modhex_data}")

153

print(f"Hex: {hex_data}")

154

155

# Convert to bytes

156

binary_data = bytes.fromhex(hex_data)

157

print(f"Binary: {binary_data}")

158

159

except yubico.yubico_exception.InputError as e:

160

print(f"ModHex decode error: {e.reason}")

161

162

# Working with YubiKey OTP output

163

yubikey_otp = "cccccccfhcbelrhifnjrrddcgrburluurftrgfdrdifj"

164

print(f"\nYubiKey OTP: {yubikey_otp}")

165

166

# First 12 characters are usually the public ID in ModHex

167

public_id_modhex = yubikey_otp[:12]

168

otp_modhex = yubikey_otp[12:]

169

170

try:

171

public_id_hex = util.modhex_decode(public_id_modhex)

172

otp_hex = util.modhex_decode(otp_modhex)

173

174

print(f"Public ID (ModHex): {public_id_modhex}")

175

print(f"Public ID (Hex): {public_id_hex}")

176

print(f"OTP (ModHex): {otp_modhex}")

177

print(f"OTP (Hex): {otp_hex}")

178

179

except yubico.yubico_exception.InputError as e:

180

print(f"OTP decode error: {e.reason}")

181

```

182

183

### HOTP Operations

184

185

Functions for HOTP (HMAC-based One-Time Password) truncation according to RFC 4226.

186

187

```python { .api }

188

def hotp_truncate(hmac_result, length=6):

189

"""

190

Perform HOTP truncation operation per RFC 4226.

191

192

Truncates HMAC-SHA1 result to generate numeric OTP codes.

193

Used in OATH-HOTP implementations and OTP validation.

194

195

Parameters:

196

- hmac_result (bytes): 20-byte HMAC-SHA1 result

197

- length (int): Desired OTP code length (typically 6 or 8)

198

199

Returns:

200

int: Truncated numeric OTP code

201

202

Raises:

203

InputError: If HMAC result length is invalid or length is out of range

204

"""

205

```

206

207

HOTP truncation example:

208

209

```python

210

import yubico.yubico_util as util

211

import hmac

212

import hashlib

213

214

# Example HOTP calculation

215

secret = b"12345678901234567890" # 20-byte secret

216

counter = 1234 # HOTP counter value

217

218

# Calculate HMAC-SHA1

219

counter_bytes = counter.to_bytes(8, 'big')

220

hmac_result = hmac.new(secret, counter_bytes, hashlib.sha1).digest()

221

222

print(f"Secret: {secret.hex()}")

223

print(f"Counter: {counter}")

224

print(f"HMAC-SHA1: {hmac_result.hex()}")

225

226

# Truncate to 6-digit OTP

227

try:

228

otp_6 = util.hotp_truncate(hmac_result, length=6)

229

print(f"6-digit OTP: {otp_6:06d}")

230

231

# Truncate to 8-digit OTP

232

otp_8 = util.hotp_truncate(hmac_result, length=8)

233

print(f"8-digit OTP: {otp_8:08d}")

234

235

except yubico.yubico_exception.InputError as e:

236

print(f"HOTP truncation error: {e.reason}")

237

238

# Generate sequence of HOTP codes

239

print("\nHOTP sequence:")

240

for i in range(5):

241

counter_bytes = (counter + i).to_bytes(8, 'big')

242

hmac_result = hmac.new(secret, counter_bytes, hashlib.sha1).digest()

243

otp = util.hotp_truncate(hmac_result, length=6)

244

print(f"Counter {counter + i}: {otp:06d}")

245

```

246

247

248

### Python 2/3 Compatibility

249

250

Utility functions for handling differences between Python 2 and Python 3 byte/string operations.

251

252

```python { .api }

253

def ord_byte(byte):

254

"""

255

Convert byte to integer value (Python 2/3 compatible).

256

257

Handles the difference between Python 2 (where bytes are strings)

258

and Python 3 (where bytes are integers when indexed).

259

260

Parameters:

261

- byte: Single byte (str in Python 2, int in Python 3)

262

263

Returns:

264

int: Integer value of the byte

265

"""

266

267

def chr_byte(number):

268

"""

269

Convert integer to single-byte string (Python 2/3 compatible).

270

271

Handles the difference between Python 2 chr() and Python 3 bytes()

272

for creating single-byte strings.

273

274

Parameters:

275

- number (int): Integer value (0-255)

276

277

Returns:

278

bytes: Single-byte string

279

"""

280

```

281

282

Compatibility function example:

283

284

```python

285

import yubico.yubico_util as util

286

287

# Working with binary data across Python versions

288

data = b"binary data example"

289

290

print("Processing bytes:")

291

for i, byte in enumerate(data):

292

# Use ord_byte for consistent behavior

293

byte_value = util.ord_byte(byte)

294

print(f" Byte {i}: 0x{byte_value:02x} ({byte_value})")

295

296

# Creating binary data

297

print("\nCreating bytes:")

298

binary_data = b""

299

for value in [0x48, 0x65, 0x6c, 0x6c, 0x6f]: # "Hello"

300

binary_data += util.chr_byte(value)

301

302

print(f"Created: {binary_data} ({binary_data.decode()})")

303

304

# Practical usage in YubiKey data processing

305

def process_yubikey_data(response_data):

306

"""Process YubiKey response data in a Python 2/3 compatible way."""

307

processed = []

308

309

for i in range(len(response_data)):

310

byte_val = util.ord_byte(response_data[i])

311

312

# Apply some processing (example: XOR with 0x5C)

313

processed_byte = byte_val ^ 0x5C

314

processed.append(util.chr_byte(processed_byte))

315

316

return b"".join(processed)

317

318

# Example usage

319

sample_response = b"YubiKey response data"

320

processed = process_yubikey_data(sample_response)

321

print(f"\nOriginal: {sample_response.hex()}")

322

print(f"Processed: {processed.hex()}")

323

```

324

325

326

## Usage Best Practices

327

328

### Error Handling

329

330

Always handle potential errors when using utility functions:

331

332

```python

333

import yubico.yubico_util as util

334

import yubico.yubico_exception

335

336

def safe_modhex_decode(modhex_str):

337

"""Safely decode ModHex with error handling."""

338

try:

339

return util.modhex_decode(modhex_str)

340

except yubico.yubico_exception.InputError as e:

341

print(f"ModHex decode failed: {e.reason}")

342

return None

343

344

def safe_crc_validate(data):

345

"""Safely validate CRC with error handling."""

346

try:

347

return util.validate_crc16(data)

348

except yubico.yubico_exception.InputError as e:

349

print(f"CRC validation failed: {e.reason}")

350

return False

351

```

352

353

### Performance Considerations

354

355

Some utility functions are computationally intensive:

356

357

```python

358

import yubico.yubico_util as util

359

import time

360

361

# CRC calculation performance

362

large_data = b"x" * 10000

363

start_time = time.time()

364

crc_result = util.crc16(large_data)

365

end_time = time.time()

366

367

print(f"CRC16 calculation time for {len(large_data)} bytes: {(end_time - start_time)*1000:.2f} ms")

368

369

# Optimize for repeated operations

370

def batch_crc_validation(data_list):

371

"""Efficiently validate CRC for multiple data blocks."""

372

results = []

373

for data in data_list:

374

try:

375

results.append(util.validate_crc16(data))

376

except yubico.yubico_exception.InputError:

377

results.append(False)

378

return results

379

```