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
```