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

challenge-response.mddocs/

0

# Challenge-Response Operations

1

2

HMAC-SHA1 and Yubico challenge-response authentication for secure authentication workflows, supporting both variable and fixed-length responses with configurable user interaction requirements.

3

4

## Capabilities

5

6

### Challenge-Response Authentication

7

8

Perform challenge-response operations using pre-configured YubiKey slots.

9

10

```python { .api }

11

# Base YubiKey class method

12

def challenge(challenge, mode='HMAC', slot=1, variable=True, may_block=True):

13

"""

14

Issue challenge to YubiKey and return response (base class method).

15

16

Requires YubiKey 2.2+ with challenge-response configuration in specified slot.

17

18

Parameters:

19

- challenge (bytes): Challenge data to send to YubiKey

20

- HMAC mode: Up to 64 bytes

21

- OTP mode: Exactly 6 bytes

22

- mode (str): Challenge mode

23

- 'HMAC': HMAC-SHA1 challenge-response

24

- 'OTP': Yubico challenge-response

25

- slot (int): Configuration slot number (1 or 2)

26

- variable (bool): Variable length response (HMAC mode only)

27

- True: Response length matches challenge length

28

- False: Fixed 20-byte response

29

- may_block (bool): Allow operations requiring user interaction

30

- True: May wait for button press if configured

31

- False: Fail immediately if button press required

32

33

Returns:

34

bytes: Response data

35

- HMAC mode: 20 bytes (fixed) or variable length

36

- OTP mode: 16 bytes

37

38

Raises:

39

YubiKeyVersionError: Device doesn't support challenge-response

40

YubiKeyTimeout: Operation timed out waiting for user interaction

41

InputError: Challenge data is invalid for specified mode

42

YubiKeyError: Challenge-response operation failed

43

"""

44

45

# USB HID implementation method (same signature)

46

def challenge_response(challenge, mode='HMAC', slot=1, variable=True, may_block=True):

47

"""

48

Issue challenge to YubiKey and return response (USB HID implementation).

49

50

Same functionality as challenge() method but available on USB HID implementations.

51

Both methods can be used interchangeably.

52

53

Parameters: Same as challenge() method

54

Returns: Same as challenge() method

55

Raises: Same as challenge() method

56

"""

57

```

58

59

### HMAC-SHA1 Challenge-Response

60

61

HMAC-SHA1 provides cryptographically strong challenge-response authentication.

62

63

Usage example:

64

65

```python

66

import yubico

67

import hashlib

68

import hmac

69

import os

70

71

# Connect to YubiKey

72

yk = yubico.find_yubikey()

73

74

# Generate random challenge

75

challenge = os.urandom(32)

76

77

try:

78

# Perform HMAC challenge-response (works with both challenge() and challenge_response())

79

response = yk.challenge(

80

challenge=challenge,

81

mode='HMAC',

82

slot=1,

83

variable=True

84

)

85

86

print(f"Challenge: {challenge.hex()}")

87

print(f"Response: {response.hex()}")

88

print(f"Response length: {len(response)} bytes")

89

90

except yubico.yubikey_base.YubiKeyVersionError:

91

print("YubiKey doesn't support challenge-response")

92

except yubico.yubico_exception.InputError as e:

93

print(f"Invalid challenge data: {e.reason}")

94

```

95

96

### Yubico Challenge-Response

97

98

Yubico challenge-response uses a proprietary algorithm for compatibility with legacy systems.

99

100

Usage example:

101

102

```python

103

import yubico

104

105

yk = yubico.find_yubikey()

106

107

# Yubico challenge-response requires exactly 6 bytes

108

challenge = b"123456"

109

110

try:

111

response = yk.challenge(

112

challenge=challenge,

113

mode='OTP',

114

slot=2,

115

may_block=True

116

)

117

118

print(f"Challenge: {challenge.hex()}")

119

print(f"Response: {response.hex()}")

120

print(f"Response length: {len(response)} bytes") # Always 16 bytes

121

122

except yubico.yubikey_base.YubiKeyVersionError:

123

print("YubiKey doesn't support Yubico challenge-response")

124

except yubico.yubico_exception.InputError:

125

print("Challenge must be exactly 6 bytes for OTP mode")

126

```

127

128

### Variable vs Fixed Length Responses

129

130

HMAC mode supports both variable and fixed-length responses.

131

132

```python

133

import yubico

134

135

yk = yubico.find_yubikey()

136

challenge = b"test challenge data"

137

138

# Variable length response (matches challenge length)

139

var_response = yk.challenge_response(

140

challenge=challenge,

141

mode='HMAC',

142

slot=1,

143

variable=True

144

)

145

146

# Fixed length response (always 20 bytes)

147

fixed_response = yk.challenge_response(

148

challenge=challenge,

149

mode='HMAC',

150

slot=1,

151

variable=False

152

)

153

154

print(f"Challenge length: {len(challenge)}")

155

print(f"Variable response length: {len(var_response)}")

156

print(f"Fixed response length: {len(fixed_response)}")

157

```

158

159

### Button Press Requirements

160

161

Some YubiKey configurations require user button press for challenge-response.

162

163

```python

164

import yubico

165

166

yk = yubico.find_yubikey()

167

challenge = b"authentication challenge"

168

169

# Allow blocking for button press

170

try:

171

response = yk.challenge_response(

172

challenge=challenge,

173

mode='HMAC',

174

slot=1,

175

may_block=True

176

)

177

print("Challenge-response successful")

178

179

except yubico.yubikey_base.YubiKeyTimeout:

180

print("Timed out waiting for button press")

181

182

# Fail immediately if button press required

183

try:

184

response = yk.challenge_response(

185

challenge=challenge,

186

mode='HMAC',

187

slot=1,

188

may_block=False

189

)

190

print("Challenge-response successful (no button press required)")

191

192

except yubico.yubikey_base.YubiKeyTimeout:

193

print("Button press required but may_block=False")

194

```

195

196

### Multi-Slot Challenge-Response

197

198

YubiKeys can have different challenge-response configurations in each slot.

199

200

```python

201

import yubico

202

203

yk = yubico.find_yubikey()

204

challenge = b"test challenge"

205

206

# Test both slots

207

for slot in [1, 2]:

208

try:

209

response = yk.challenge_response(challenge, slot=slot)

210

print(f"Slot {slot} response: {response.hex()}")

211

except yubico.yubikey_base.YubiKeyError:

212

print(f"Slot {slot} not configured for challenge-response")

213

```

214

215

### Authentication Workflow Example

216

217

Complete authentication workflow using challenge-response.

218

219

```python

220

import yubico

221

import hashlib

222

import hmac

223

import os

224

import time

225

226

def yubikey_authenticate(user_id, expected_secret=None):

227

"""

228

Authenticate user using YubiKey challenge-response.

229

230

Parameters:

231

- user_id (str): User identifier

232

- expected_secret (bytes): Known secret for verification (demo only)

233

234

Returns:

235

bool: Authentication success

236

"""

237

try:

238

# Connect to YubiKey

239

yk = yubico.find_yubikey()

240

241

# Generate random challenge

242

challenge = os.urandom(20)

243

244

# Get YubiKey response

245

yk_response = yk.challenge_response(

246

challenge=challenge,

247

mode='HMAC',

248

slot=1,

249

variable=False # Fixed 20-byte response

250

)

251

252

# For demo: verify against known secret

253

if expected_secret:

254

expected_response = hmac.new(

255

expected_secret,

256

challenge,

257

hashlib.sha1

258

).digest()

259

260

if hmac.compare_digest(yk_response, expected_response):

261

print(f"Authentication successful for user: {user_id}")

262

return True

263

else:

264

print(f"Authentication failed for user: {user_id}")

265

return False

266

267

# In real applications, verify response against stored hash

268

print(f"Challenge-response completed for user: {user_id}")

269

print(f"Response: {yk_response.hex()}")

270

return True

271

272

except yubico.yubico_exception.YubicoError as e:

273

print(f"YubiKey authentication failed: {e.reason}")

274

return False

275

276

# Example usage

277

secret = os.urandom(20) # In practice, store this securely

278

success = yubikey_authenticate("alice", secret)

279

```

280

281

### Performance Considerations

282

283

Challenge-response operations have different performance characteristics.

284

285

```python

286

import yubico

287

import time

288

289

yk = yubico.find_yubikey()

290

challenge = b"performance test"

291

292

# Measure response time

293

start_time = time.time()

294

response = yk.challenge_response(challenge, mode='HMAC', slot=1)

295

end_time = time.time()

296

297

print(f"Challenge-response time: {(end_time - start_time)*1000:.2f} ms")

298

299

# Test multiple operations

300

times = []

301

for i in range(10):

302

start = time.time()

303

yk.challenge_response(f"test{i}".encode(), mode='HMAC', slot=1)

304

end = time.time()

305

times.append((end - start) * 1000)

306

307

avg_time = sum(times) / len(times)

308

print(f"Average response time over 10 operations: {avg_time:.2f} ms")

309

```

310

311

### Error Handling

312

313

Comprehensive error handling for challenge-response operations.

314

315

```python

316

import yubico

317

318

def safe_challenge_response(yk, challenge, **kwargs):

319

"""

320

Perform challenge-response with comprehensive error handling.

321

"""

322

try:

323

return yk.challenge_response(challenge, **kwargs)

324

325

except yubico.yubikey_base.YubiKeyVersionError:

326

print("ERROR: YubiKey doesn't support challenge-response")

327

print("Required: YubiKey 2.2+ with challenge-response configuration")

328

329

except yubico.yubikey_base.YubiKeyTimeout:

330

print("ERROR: Operation timed out")

331

print("This may indicate button press is required")

332

333

except yubico.yubico_exception.InputError as e:

334

print(f"ERROR: Invalid challenge data: {e.reason}")

335

print("HMAC mode: up to 64 bytes")

336

print("OTP mode: exactly 6 bytes")

337

338

except yubico.yubikey_base.YubiKeyError as e:

339

print(f"ERROR: Challenge-response failed: {e.reason}")

340

print("Check that the specified slot is configured for challenge-response")

341

342

return None

343

344

# Usage

345

yk = yubico.find_yubikey()

346

challenge = b"test challenge"

347

response = safe_challenge_response(yk, challenge, mode='HMAC', slot=1)

348

349

if response:

350

print(f"Success: {response.hex()}")

351

else:

352

print("Challenge-response failed")

353

```

354

355

## Configuration Requirements

356

357

Before using challenge-response, the YubiKey must be configured appropriately:

358

359

```python

360

import yubico

361

from yubico.yubikey_config import YubiKeyConfig

362

import os

363

364

# Configure YubiKey for HMAC challenge-response

365

yk = yubico.find_yubikey()

366

cfg = yk.init_config()

367

368

# Set secret key (20 bytes for HMAC)

369

secret = os.urandom(20)

370

cfg.mode_challenge_response(

371

secret=secret,

372

type='HMAC',

373

variable=True,

374

require_button=False # Set to True if button press required

375

)

376

377

# Write configuration to slot 1

378

yk.write_config(cfg, slot=1)

379

print("YubiKey configured for challenge-response")

380

381

# Test the configuration

382

test_challenge = b"configuration test"

383

response = yk.challenge_response(test_challenge, slot=1)

384

print(f"Test response: {response.hex()}")

385

```