or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-interface.mdcore-authentication.mddevice-models.mddjango-integration.mdemail-devices.mdhotp-devices.mdindex.mdoath-algorithms.mdstatic-tokens.mdtotp-devices.md

oath-algorithms.mddocs/

0

# OATH Algorithms

1

2

Low-level implementations of HOTP and TOTP algorithms following RFC 4226 and RFC 6238. These functions provide the cryptographic foundation for OTP generation and can be used directly or for custom device implementations.

3

4

## Capabilities

5

6

### HOTP Algorithm

7

8

HMAC-based one-time password algorithm (RFC 4226).

9

10

```python { .api }

11

def hotp(key, counter, digits=6):

12

"""

13

HOTP algorithm implementation (RFC 4226).

14

15

Parameters:

16

- key: bytes - Secret key for HMAC

17

- counter: int - Counter value

18

- digits: int - Number of digits in output (default: 6)

19

20

Returns:

21

int - HOTP value

22

"""

23

```

24

25

### TOTP Algorithm

26

27

Time-based one-time password algorithm (RFC 6238).

28

29

```python { .api }

30

def totp(key, step=30, t0=0, digits=6, drift=0):

31

"""

32

TOTP algorithm implementation (RFC 6238).

33

34

Parameters:

35

- key: bytes - Secret key for HMAC

36

- step: int - Time step in seconds (default: 30)

37

- t0: int - Unix time to start counting (default: 0)

38

- digits: int - Number of digits in output (default: 6)

39

- drift: int - Drift compensation in time steps (default: 0)

40

41

Returns:

42

int - TOTP value

43

"""

44

```

45

46

### TOTP Class

47

48

Advanced TOTP interface with intermediate access and verification capabilities.

49

50

```python { .api }

51

class TOTP:

52

"""TOTP interface with intermediate access."""

53

54

def __init__(self, key, step=30, t0=0, digits=6, drift=0):

55

"""

56

Initialize TOTP instance.

57

58

Parameters:

59

- key: bytes - Secret key

60

- step: int - Time step in seconds

61

- t0: int - Start time (Unix timestamp)

62

- digits: int - Number of digits

63

- drift: int - Drift value in time steps

64

"""

65

66

@property

67

def time(self):

68

"""Current time (settable for testing)."""

69

70

@time.setter

71

def time(self, value):

72

"""Set current time."""

73

74

def token(self) -> int:

75

"""Compute TOTP token for current time."""

76

77

def t(self) -> int:

78

"""Compute current time step."""

79

80

def verify(self, token, tolerance=0, min_t=None) -> int:

81

"""

82

Verify token with tolerance.

83

84

Parameters:

85

- token: int - Token to verify

86

- tolerance: int - Time step tolerance

87

- min_t: int or None - Minimum time step to accept

88

89

Returns:

90

int - Time step if valid, None if invalid

91

"""

92

```

93

94

## Usage Examples

95

96

### Basic HOTP Usage

97

98

```python

99

from django_otp.oath import hotp

100

import secrets

101

102

# Generate secret key

103

key = secrets.token_bytes(20) # 160-bit key

104

105

# Generate HOTP tokens

106

counter = 0

107

token1 = hotp(key, counter) # First token

108

token2 = hotp(key, counter + 1) # Second token

109

110

print(f"Token 1: {token1:06d}") # Zero-padded 6 digits

111

print(f"Token 2: {token2:06d}")

112

113

# Generate 8-digit tokens

114

token_8 = hotp(key, counter, digits=8)

115

print(f"8-digit token: {token_8:08d}")

116

```

117

118

### Basic TOTP Usage

119

120

```python

121

from django_otp.oath import totp

122

import time

123

import secrets

124

125

# Generate secret key

126

key = secrets.token_bytes(20)

127

128

# Generate current TOTP token

129

current_token = totp(key)

130

print(f"Current token: {current_token:06d}")

131

132

# Generate token for specific time

133

specific_time = int(time.time()) - 30 # 30 seconds ago

134

past_token = totp(key, t0=specific_time)

135

136

# Generate token with different step size

137

token_60s = totp(key, step=60) # 60-second intervals

138

print(f"60s step token: {token_60s:06d}")

139

```

140

141

### Advanced TOTP Usage

142

143

```python

144

from django_otp.oath import TOTP

145

import time

146

147

# Create TOTP instance

148

key = b'secret_key_here'

149

totp_instance = TOTP(key, step=30, digits=6)

150

151

# Get current token

152

current_token = totp_instance.token()

153

print(f"Current token: {current_token:06d}")

154

155

# Get current time step

156

current_t = totp_instance.t()

157

print(f"Current time step: {current_t}")

158

159

# Verify token with tolerance

160

test_token = current_token

161

result = totp_instance.verify(test_token, tolerance=1)

162

if result is not None:

163

print(f"Token verified at time step: {result}")

164

else:

165

print("Token verification failed")

166

167

# Test with historical tokens (prevent replay)

168

min_acceptable_t = current_t - 5 # Don't accept tokens older than 5 steps

169

result = totp_instance.verify(test_token, tolerance=1, min_t=min_acceptable_t)

170

```

171

172

### Custom Device Implementation

173

174

```python

175

from django_otp.oath import TOTP

176

from django_otp.models import Device

177

import binascii

178

179

class CustomTOTPDevice(Device):

180

"""Custom TOTP device using OATH algorithms directly."""

181

182

def __init__(self, *args, **kwargs):

183

super().__init__(*args, **kwargs)

184

self.secret_key = binascii.unhexlify(self.key)

185

self.totp = TOTP(self.secret_key, step=30, digits=6)

186

self.last_used_t = -1

187

188

def generate_token(self):

189

"""Generate current TOTP token."""

190

return f"{self.totp.token():06d}"

191

192

def verify_token(self, token):

193

"""Verify TOTP token with replay protection."""

194

try:

195

token_int = int(token)

196

except ValueError:

197

return False

198

199

# Verify with tolerance, preventing replay

200

t = self.totp.verify(

201

token_int,

202

tolerance=1,

203

min_t=self.last_used_t + 1

204

)

205

206

if t is not None:

207

self.last_used_t = t

208

self.save()

209

return True

210

211

return False

212

```

213

214

### Token Generation Utilities

215

216

```python

217

from django_otp.oath import hotp, totp

218

import qrcode

219

import io

220

import base64

221

222

def generate_hotp_sequence(key, start_counter, count=10):

223

"""Generate a sequence of HOTP tokens."""

224

tokens = []

225

for i in range(count):

226

token = hotp(key, start_counter + i)

227

tokens.append(f"{token:06d}")

228

return tokens

229

230

def generate_totp_window(key, window_size=5):

231

"""Generate TOTP tokens for current time window."""

232

import time

233

234

tokens = {}

235

current_time = int(time.time())

236

237

for offset in range(-window_size, window_size + 1):

238

t = current_time + (offset * 30)

239

token = totp(key, t0=t)

240

tokens[offset] = f"{token:06d}"

241

242

return tokens

243

244

# Usage

245

key = b'shared_secret_key'

246

247

# Generate HOTP sequence

248

hotp_tokens = generate_hotp_sequence(key, 0, 5)

249

print("HOTP sequence:", hotp_tokens)

250

251

# Generate TOTP window

252

totp_window = generate_totp_window(key, 2)

253

for offset, token in totp_window.items():

254

print(f"T{offset:+d}: {token}")

255

```

256

257

### Performance Testing

258

259

```python

260

from django_otp.oath import hotp, totp, TOTP

261

import time

262

263

def benchmark_algorithms():

264

"""Benchmark OATH algorithm performance."""

265

266

key = b'benchmark_secret_key_here_123'

267

iterations = 10000

268

269

# Benchmark HOTP

270

start = time.time()

271

for i in range(iterations):

272

hotp(key, i)

273

hotp_time = time.time() - start

274

275

# Benchmark TOTP

276

start = time.time()

277

for i in range(iterations):

278

totp(key)

279

totp_time = time.time() - start

280

281

# Benchmark TOTP class

282

totp_instance = TOTP(key)

283

start = time.time()

284

for i in range(iterations):

285

totp_instance.token()

286

totp_class_time = time.time() - start

287

288

print(f"HOTP: {hotp_time:.3f}s ({iterations/hotp_time:.0f} ops/sec)")

289

print(f"TOTP: {totp_time:.3f}s ({iterations/totp_time:.0f} ops/sec)")

290

print(f"TOTP Class: {totp_class_time:.3f}s ({iterations/totp_class_time:.0f} ops/sec)")

291

292

# Run benchmark

293

benchmark_algorithms()

294

```

295

296

### Algorithm Validation

297

298

```python

299

from django_otp.oath import hotp, totp

300

301

def validate_rfc_test_vectors():

302

"""Validate against RFC 4226/6238 test vectors."""

303

304

# RFC 4226 HOTP test vectors

305

rfc4226_key = b"12345678901234567890"

306

rfc4226_vectors = [

307

(0, 755224),

308

(1, 287082),

309

(2, 359152),

310

(3, 969429),

311

(4, 338314)

312

]

313

314

print("RFC 4226 HOTP validation:")

315

for counter, expected in rfc4226_vectors:

316

result = hotp(rfc4226_key, counter)

317

status = "PASS" if result == expected else "FAIL"

318

print(f"Counter {counter}: {result} (expected {expected}) - {status}")

319

320

# RFC 6238 TOTP test vectors (simplified)

321

rfc6238_key = b"12345678901234567890"

322

323

# Test vector for Unix time 59 (T0=0, step=30, so T=1)

324

test_time = 59

325

expected_totp = 94287082 # From RFC 6238

326

327

# Calculate T value: (59 - 0) // 30 = 1

328

t_value = test_time // 30

329

result = hotp(rfc6238_key, t_value) # TOTP is HOTP with T as counter

330

331

print(f"\nRFC 6238 TOTP validation:")

332

print(f"Time {test_time}: {result} (expected {expected_totp}) - {'PASS' if result == expected_totp else 'FAIL'}")

333

334

# Run validation

335

validate_rfc_test_vectors()

336

```