0
# Utility Functions
1
2
Cryptographic utilities including secure random number generation and constant-time comparison for security-critical operations. These functions provide essential building blocks for secure cryptographic implementations.
3
4
## Capabilities
5
6
### Secure Random Generation
7
8
Generate cryptographically secure random bytes using the operating system's entropy source.
9
10
```python { .api }
11
def rand_bytes(length: int) -> bytes:
12
"""
13
Generate cryptographically secure random bytes.
14
15
Parameters:
16
- length: int - Number of random bytes to generate
17
18
Returns:
19
Random bytes of specified length
20
21
Note:
22
Uses OS entropy sources (CryptGenRandom on Windows, /dev/urandom on Unix)
23
"""
24
```
25
26
### Constant-Time Comparison
27
28
Compare byte strings in constant time to prevent timing attacks in security-critical code.
29
30
```python { .api }
31
def constant_compare(a: bytes, b: bytes) -> bool:
32
"""
33
Compare two byte strings in constant time.
34
35
Parameters:
36
- a: bytes - First byte string
37
- b: bytes - Second byte string
38
39
Returns:
40
True if byte strings are equal, False otherwise
41
42
Raises:
43
TypeError if either parameter is not bytes
44
45
Note:
46
Comparison time is independent of where strings differ, preventing timing attacks
47
"""
48
```
49
50
## Usage Examples
51
52
### Secure Random Generation
53
54
```python
55
from oscrypto.util import rand_bytes
56
57
# Generate random keys of various sizes
58
aes_128_key = rand_bytes(16) # 128-bit AES key
59
aes_256_key = rand_bytes(32) # 256-bit AES key
60
random_salt = rand_bytes(16) # Salt for key derivation
61
session_id = rand_bytes(32) # Session identifier
62
63
print(f"AES-128 key: {aes_128_key.hex()}")
64
print(f"AES-256 key: {aes_256_key.hex()}")
65
print(f"Salt: {random_salt.hex()}")
66
print(f"Session ID: {session_id.hex()}")
67
68
# Generate random initialization vectors
69
aes_iv = rand_bytes(16) # AES block size
70
des_iv = rand_bytes(8) # DES block size
71
72
print(f"AES IV: {aes_iv.hex()}")
73
print(f"DES IV: {des_iv.hex()}")
74
```
75
76
### Constant-Time Comparison
77
78
```python
79
from oscrypto.util import constant_compare
80
import time
81
82
def timing_attack_demo():
83
"""Demonstrate why constant-time comparison is important."""
84
85
# Create two similar strings that differ at different positions
86
target = b"secret_authentication_token_12345678"
87
88
# Attack string differs at position 0
89
attack1 = b"xxxxxx_authentication_token_12345678"
90
91
# Attack string differs at position 30
92
attack2 = b"secret_authentication_token_1234xxxx"
93
94
# Measure time for regular comparison (vulnerable to timing attacks)
95
start = time.perf_counter()
96
for _ in range(100000):
97
result = target == attack1
98
time1 = time.perf_counter() - start
99
100
start = time.perf_counter()
101
for _ in range(100000):
102
result = target == attack2
103
time2 = time.perf_counter() - start
104
105
print("Regular comparison timing:")
106
print(f" Early difference: {time1:.6f} seconds")
107
print(f" Late difference: {time2:.6f} seconds")
108
print(f" Timing difference: {abs(time1 - time2):.6f} seconds")
109
110
# Measure time for constant-time comparison (secure)
111
start = time.perf_counter()
112
for _ in range(100000):
113
result = constant_compare(target, attack1)
114
time1 = time.perf_counter() - start
115
116
start = time.perf_counter()
117
for _ in range(100000):
118
result = constant_compare(target, attack2)
119
time2 = time.perf_counter() - start
120
121
print("\nConstant-time comparison:")
122
print(f" Early difference: {time1:.6f} seconds")
123
print(f" Late difference: {time2:.6f} seconds")
124
print(f" Timing difference: {abs(time1 - time2):.6f} seconds")
125
126
# Run timing demonstration
127
timing_attack_demo()
128
```
129
130
### Secure Token Generation
131
132
```python
133
from oscrypto.util import rand_bytes, constant_compare
134
import base64
135
import hashlib
136
137
class SecureTokenManager:
138
"""Example secure token management using oscrypto utilities."""
139
140
def __init__(self):
141
# Generate master key for token signing
142
self.master_key = rand_bytes(32)
143
144
def generate_token(self, user_id: str, expires_at: int) -> str:
145
"""Generate a secure token for a user."""
146
# Create token payload
147
payload = f"{user_id}:{expires_at}".encode('utf-8')
148
149
# Generate random nonce
150
nonce = rand_bytes(16)
151
152
# Create HMAC signature
153
hmac_key = hashlib.pbkdf2_hmac('sha256', self.master_key, nonce, 10000)
154
signature = hashlib.hmac.new(hmac_key, payload, hashlib.sha256).digest()
155
156
# Combine nonce + payload + signature
157
token_data = nonce + payload + signature
158
159
# Encode as base64
160
return base64.urlsafe_b64encode(token_data).decode('ascii')
161
162
def verify_token(self, token: str, user_id: str, expires_at: int) -> bool:
163
"""Verify a token in constant time."""
164
try:
165
# Decode token
166
token_data = base64.urlsafe_b64decode(token.encode('ascii'))
167
168
# Extract components
169
nonce = token_data[:16]
170
payload = token_data[16:-32]
171
signature = token_data[-32:]
172
173
# Verify payload matches expected values
174
expected_payload = f"{user_id}:{expires_at}".encode('utf-8')
175
if not constant_compare(payload, expected_payload):
176
return False
177
178
# Recreate signature
179
hmac_key = hashlib.pbkdf2_hmac('sha256', self.master_key, nonce, 10000)
180
expected_signature = hashlib.hmac.new(hmac_key, payload, hashlib.sha256).digest()
181
182
# Constant-time signature verification
183
return constant_compare(signature, expected_signature)
184
185
except Exception:
186
return False
187
188
# Example usage
189
token_manager = SecureTokenManager()
190
191
# Generate token
192
user_id = "user123"
193
expires_at = 1735689600 # Timestamp
194
token = token_manager.generate_token(user_id, expires_at)
195
print(f"Generated token: {token}")
196
197
# Verify valid token
198
is_valid = token_manager.verify_token(token, user_id, expires_at)
199
print(f"Token valid: {is_valid}")
200
201
# Verify invalid token
202
is_valid = token_manager.verify_token(token + "x", user_id, expires_at)
203
print(f"Modified token valid: {is_valid}")
204
```
205
206
### Cryptographic Key Generation
207
208
```python
209
from oscrypto.util import rand_bytes
210
from oscrypto.kdf import pbkdf2
211
import base64
212
213
def generate_keypair_seed() -> str:
214
"""Generate a high-entropy seed for deterministic key generation."""
215
# Generate 256 bits of entropy
216
seed = rand_bytes(32)
217
218
# Encode as base64 for storage/transmission
219
return base64.b64encode(seed).decode('ascii')
220
221
def derive_multiple_keys(master_secret: bytes, key_count: int = 3) -> dict:
222
"""Derive multiple keys from a master secret."""
223
keys = {}
224
225
for i in range(key_count):
226
# Use different salt for each key
227
salt = f"key_derivation_{i}".encode('utf-8') + rand_bytes(8)
228
229
# Derive 32-byte key
230
key = pbkdf2('sha256', master_secret, salt, 100000, 32)
231
keys[f"key_{i}"] = key
232
233
return keys
234
235
def secure_key_splitting(secret: bytes, threshold: int, shares: int) -> list:
236
"""Simple secret sharing using XOR (for demonstration)."""
237
if threshold != shares:
238
raise ValueError("This example only supports threshold == shares")
239
240
# Generate random shares
241
share_list = []
242
running_xor = secret
243
244
for i in range(shares - 1):
245
share = rand_bytes(len(secret))
246
share_list.append(share)
247
248
# XOR with running total
249
running_xor = bytes(a ^ b for a, b in zip(running_xor, share))
250
251
# Last share is the XOR of all previous shares with the secret
252
share_list.append(running_xor)
253
254
return share_list
255
256
def reconstruct_secret(shares: list) -> bytes:
257
"""Reconstruct secret from XOR shares."""
258
result = shares[0]
259
for share in shares[1:]:
260
result = bytes(a ^ b for a, b in zip(result, share))
261
return result
262
263
# Example usage
264
print("Generating master seed...")
265
seed_b64 = generate_keypair_seed()
266
print(f"Seed (base64): {seed_b64}")
267
268
# Convert back to bytes
269
master_secret = base64.b64decode(seed_b64)
270
271
print("\nDeriving multiple keys...")
272
keys = derive_multiple_keys(master_secret, 3)
273
for name, key in keys.items():
274
print(f"{name}: {key[:8].hex()}...")
275
276
print("\nSecret sharing example...")
277
secret = b"This is a very secret message!"
278
shares = secure_key_splitting(secret, 3, 3)
279
print(f"Split secret into {len(shares)} shares")
280
281
# Reconstruct
282
reconstructed = reconstruct_secret(shares)
283
print(f"Reconstructed: {reconstructed}")
284
print(f"Match: {secret == reconstructed}")
285
```
286
287
### Secure Random Testing
288
289
```python
290
from oscrypto.util import rand_bytes
291
import collections
292
293
def test_randomness_quality(samples: int = 10000, byte_length: int = 16):
294
"""Basic statistical tests for random number quality."""
295
296
print(f"Testing randomness with {samples} samples of {byte_length} bytes each")
297
298
# Collect random samples
299
byte_counts = collections.defaultdict(int)
300
bit_counts = [0, 0] # [0-bits, 1-bits]
301
302
for _ in range(samples):
303
random_bytes = rand_bytes(byte_length)
304
305
# Count byte values
306
for byte_val in random_bytes:
307
byte_counts[byte_val] += 1
308
309
# Count bits
310
for byte_val in random_bytes:
311
for bit_pos in range(8):
312
bit_counts[(byte_val >> bit_pos) & 1] += 1
313
314
# Analyze results
315
total_bytes = samples * byte_length
316
total_bits = total_bytes * 8
317
318
print(f"\nBit distribution:")
319
print(f" 0-bits: {bit_counts[0]} ({bit_counts[0]/total_bits*100:.2f}%)")
320
print(f" 1-bits: {bit_counts[1]} ({bit_counts[1]/total_bits*100:.2f}%)")
321
print(f" Balance: {abs(bit_counts[0] - bit_counts[1])} difference")
322
323
# Check for repeated sequences
324
sequences = set()
325
duplicates = 0
326
327
for _ in range(1000):
328
seq = rand_bytes(16)
329
if seq in sequences:
330
duplicates += 1
331
sequences.add(seq)
332
333
print(f"\nSequence uniqueness (1000 samples):")
334
print(f" Duplicates: {duplicates}")
335
print(f" Unique: {1000 - duplicates}")
336
337
# Byte value distribution
338
expected_per_value = total_bytes / 256
339
max_deviation = max(abs(count - expected_per_value) for count in byte_counts.values())
340
341
print(f"\nByte value distribution:")
342
print(f" Expected per value: {expected_per_value:.1f}")
343
print(f" Max deviation: {max_deviation:.1f}")
344
345
# Run randomness tests
346
test_randomness_quality()
347
```