Cross-platform cryptographic library providing TLS sockets, asymmetric/symmetric encryption, and key operations using OS-native crypto libraries
—
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.
Generate cryptographically secure random bytes using the operating system's entropy source.
def rand_bytes(length: int) -> bytes:
"""
Generate cryptographically secure random bytes.
Parameters:
- length: int - Number of random bytes to generate
Returns:
Random bytes of specified length
Note:
Uses OS entropy sources (CryptGenRandom on Windows, /dev/urandom on Unix)
"""Compare byte strings in constant time to prevent timing attacks in security-critical code.
def constant_compare(a: bytes, b: bytes) -> bool:
"""
Compare two byte strings in constant time.
Parameters:
- a: bytes - First byte string
- b: bytes - Second byte string
Returns:
True if byte strings are equal, False otherwise
Raises:
TypeError if either parameter is not bytes
Note:
Comparison time is independent of where strings differ, preventing timing attacks
"""from oscrypto.util import rand_bytes
# Generate random keys of various sizes
aes_128_key = rand_bytes(16) # 128-bit AES key
aes_256_key = rand_bytes(32) # 256-bit AES key
random_salt = rand_bytes(16) # Salt for key derivation
session_id = rand_bytes(32) # Session identifier
print(f"AES-128 key: {aes_128_key.hex()}")
print(f"AES-256 key: {aes_256_key.hex()}")
print(f"Salt: {random_salt.hex()}")
print(f"Session ID: {session_id.hex()}")
# Generate random initialization vectors
aes_iv = rand_bytes(16) # AES block size
des_iv = rand_bytes(8) # DES block size
print(f"AES IV: {aes_iv.hex()}")
print(f"DES IV: {des_iv.hex()}")from oscrypto.util import constant_compare
import time
def timing_attack_demo():
"""Demonstrate why constant-time comparison is important."""
# Create two similar strings that differ at different positions
target = b"secret_authentication_token_12345678"
# Attack string differs at position 0
attack1 = b"xxxxxx_authentication_token_12345678"
# Attack string differs at position 30
attack2 = b"secret_authentication_token_1234xxxx"
# Measure time for regular comparison (vulnerable to timing attacks)
start = time.perf_counter()
for _ in range(100000):
result = target == attack1
time1 = time.perf_counter() - start
start = time.perf_counter()
for _ in range(100000):
result = target == attack2
time2 = time.perf_counter() - start
print("Regular comparison timing:")
print(f" Early difference: {time1:.6f} seconds")
print(f" Late difference: {time2:.6f} seconds")
print(f" Timing difference: {abs(time1 - time2):.6f} seconds")
# Measure time for constant-time comparison (secure)
start = time.perf_counter()
for _ in range(100000):
result = constant_compare(target, attack1)
time1 = time.perf_counter() - start
start = time.perf_counter()
for _ in range(100000):
result = constant_compare(target, attack2)
time2 = time.perf_counter() - start
print("\nConstant-time comparison:")
print(f" Early difference: {time1:.6f} seconds")
print(f" Late difference: {time2:.6f} seconds")
print(f" Timing difference: {abs(time1 - time2):.6f} seconds")
# Run timing demonstration
timing_attack_demo()from oscrypto.util import rand_bytes, constant_compare
import base64
import hashlib
class SecureTokenManager:
"""Example secure token management using oscrypto utilities."""
def __init__(self):
# Generate master key for token signing
self.master_key = rand_bytes(32)
def generate_token(self, user_id: str, expires_at: int) -> str:
"""Generate a secure token for a user."""
# Create token payload
payload = f"{user_id}:{expires_at}".encode('utf-8')
# Generate random nonce
nonce = rand_bytes(16)
# Create HMAC signature
hmac_key = hashlib.pbkdf2_hmac('sha256', self.master_key, nonce, 10000)
signature = hashlib.hmac.new(hmac_key, payload, hashlib.sha256).digest()
# Combine nonce + payload + signature
token_data = nonce + payload + signature
# Encode as base64
return base64.urlsafe_b64encode(token_data).decode('ascii')
def verify_token(self, token: str, user_id: str, expires_at: int) -> bool:
"""Verify a token in constant time."""
try:
# Decode token
token_data = base64.urlsafe_b64decode(token.encode('ascii'))
# Extract components
nonce = token_data[:16]
payload = token_data[16:-32]
signature = token_data[-32:]
# Verify payload matches expected values
expected_payload = f"{user_id}:{expires_at}".encode('utf-8')
if not constant_compare(payload, expected_payload):
return False
# Recreate signature
hmac_key = hashlib.pbkdf2_hmac('sha256', self.master_key, nonce, 10000)
expected_signature = hashlib.hmac.new(hmac_key, payload, hashlib.sha256).digest()
# Constant-time signature verification
return constant_compare(signature, expected_signature)
except Exception:
return False
# Example usage
token_manager = SecureTokenManager()
# Generate token
user_id = "user123"
expires_at = 1735689600 # Timestamp
token = token_manager.generate_token(user_id, expires_at)
print(f"Generated token: {token}")
# Verify valid token
is_valid = token_manager.verify_token(token, user_id, expires_at)
print(f"Token valid: {is_valid}")
# Verify invalid token
is_valid = token_manager.verify_token(token + "x", user_id, expires_at)
print(f"Modified token valid: {is_valid}")from oscrypto.util import rand_bytes
from oscrypto.kdf import pbkdf2
import base64
def generate_keypair_seed() -> str:
"""Generate a high-entropy seed for deterministic key generation."""
# Generate 256 bits of entropy
seed = rand_bytes(32)
# Encode as base64 for storage/transmission
return base64.b64encode(seed).decode('ascii')
def derive_multiple_keys(master_secret: bytes, key_count: int = 3) -> dict:
"""Derive multiple keys from a master secret."""
keys = {}
for i in range(key_count):
# Use different salt for each key
salt = f"key_derivation_{i}".encode('utf-8') + rand_bytes(8)
# Derive 32-byte key
key = pbkdf2('sha256', master_secret, salt, 100000, 32)
keys[f"key_{i}"] = key
return keys
def secure_key_splitting(secret: bytes, threshold: int, shares: int) -> list:
"""Simple secret sharing using XOR (for demonstration)."""
if threshold != shares:
raise ValueError("This example only supports threshold == shares")
# Generate random shares
share_list = []
running_xor = secret
for i in range(shares - 1):
share = rand_bytes(len(secret))
share_list.append(share)
# XOR with running total
running_xor = bytes(a ^ b for a, b in zip(running_xor, share))
# Last share is the XOR of all previous shares with the secret
share_list.append(running_xor)
return share_list
def reconstruct_secret(shares: list) -> bytes:
"""Reconstruct secret from XOR shares."""
result = shares[0]
for share in shares[1:]:
result = bytes(a ^ b for a, b in zip(result, share))
return result
# Example usage
print("Generating master seed...")
seed_b64 = generate_keypair_seed()
print(f"Seed (base64): {seed_b64}")
# Convert back to bytes
master_secret = base64.b64decode(seed_b64)
print("\nDeriving multiple keys...")
keys = derive_multiple_keys(master_secret, 3)
for name, key in keys.items():
print(f"{name}: {key[:8].hex()}...")
print("\nSecret sharing example...")
secret = b"This is a very secret message!"
shares = secure_key_splitting(secret, 3, 3)
print(f"Split secret into {len(shares)} shares")
# Reconstruct
reconstructed = reconstruct_secret(shares)
print(f"Reconstructed: {reconstructed}")
print(f"Match: {secret == reconstructed}")from oscrypto.util import rand_bytes
import collections
def test_randomness_quality(samples: int = 10000, byte_length: int = 16):
"""Basic statistical tests for random number quality."""
print(f"Testing randomness with {samples} samples of {byte_length} bytes each")
# Collect random samples
byte_counts = collections.defaultdict(int)
bit_counts = [0, 0] # [0-bits, 1-bits]
for _ in range(samples):
random_bytes = rand_bytes(byte_length)
# Count byte values
for byte_val in random_bytes:
byte_counts[byte_val] += 1
# Count bits
for byte_val in random_bytes:
for bit_pos in range(8):
bit_counts[(byte_val >> bit_pos) & 1] += 1
# Analyze results
total_bytes = samples * byte_length
total_bits = total_bytes * 8
print(f"\nBit distribution:")
print(f" 0-bits: {bit_counts[0]} ({bit_counts[0]/total_bits*100:.2f}%)")
print(f" 1-bits: {bit_counts[1]} ({bit_counts[1]/total_bits*100:.2f}%)")
print(f" Balance: {abs(bit_counts[0] - bit_counts[1])} difference")
# Check for repeated sequences
sequences = set()
duplicates = 0
for _ in range(1000):
seq = rand_bytes(16)
if seq in sequences:
duplicates += 1
sequences.add(seq)
print(f"\nSequence uniqueness (1000 samples):")
print(f" Duplicates: {duplicates}")
print(f" Unique: {1000 - duplicates}")
# Byte value distribution
expected_per_value = total_bytes / 256
max_deviation = max(abs(count - expected_per_value) for count in byte_counts.values())
print(f"\nByte value distribution:")
print(f" Expected per value: {expected_per_value:.1f}")
print(f" Max deviation: {max_deviation:.1f}")
# Run randomness tests
test_randomness_quality()Install with Tessl CLI
npx tessl i tessl/pypi-oscrypto