Cross-platform cryptographic library providing TLS sockets, asymmetric/symmetric encryption, and key operations using OS-native crypto libraries
—
Password-based key derivation functions including PBKDF1, PBKDF2, and PKCS#12 KDF. These functions derive cryptographic keys from passwords using salt and iteration counts for security against brute-force attacks.
Password-Based Key Derivation Function 2 (PBKDF2) as defined in RFC 2898, the most commonly used key derivation function.
def pbkdf2(hash_algorithm: str, password: bytes, salt: bytes, iterations: int, key_length: int) -> bytes:
"""
Derive a key using PBKDF2.
Parameters:
- hash_algorithm: str - Hash algorithm ('sha1', 'sha224', 'sha256', 'sha384', 'sha512')
- password: bytes - Password to derive key from
- salt: bytes - Salt value (should be at least 8 bytes, preferably 16+)
- iterations: int - Number of iterations (minimum 1000, recommended 10000+)
- key_length: int - Length of derived key in bytes
Returns:
Derived key bytes
"""
def pbkdf2_iteration_calculator(hash_algorithm: str, key_length: int, target_ms: int = 100, quiet: bool = False) -> int:
"""
Calculate PBKDF2 iterations for a target computation time.
Parameters:
- hash_algorithm: str - Hash algorithm to benchmark
- key_length: int - Length of key to derive
- target_ms: int - Target computation time in milliseconds
- quiet: bool - Suppress progress output
Returns:
Number of iterations that achieves approximately target_ms computation time
"""Password-Based Key Derivation Function 1 (PBKDF1) as defined in RFC 2898. Less secure than PBKDF2, included for compatibility.
def pbkdf1(hash_algorithm: str, password: bytes, salt: bytes, iterations: int, key_length: int) -> bytes:
"""
Derive a key using PBKDF1 (legacy, less secure than PBKDF2).
Parameters:
- hash_algorithm: str - Hash algorithm ('sha1', 'md5')
- password: bytes - Password to derive key from
- salt: bytes - Salt value (8 bytes)
- iterations: int - Number of iterations
- key_length: int - Length of derived key (limited by hash output size)
Returns:
Derived key bytes
Note:
PBKDF1 is deprecated. Use PBKDF2 for new applications.
"""PKCS#12 key derivation function used specifically for PKCS#12 files and some legacy applications.
def pkcs12_kdf(hash_algorithm: str, password: bytes, salt: bytes, iterations: int, key_length: int, id_: int) -> bytes:
"""
Derive a key using PKCS#12 KDF.
Parameters:
- hash_algorithm: str - Hash algorithm ('sha1', 'sha224', 'sha256', 'sha384', 'sha512')
- password: bytes - Password to derive key from
- salt: bytes - Salt value
- iterations: int - Number of iterations
- key_length: int - Length of derived key in bytes
- id_: int - Purpose ID (1=key material, 2=IV, 3=MAC key)
Returns:
Derived key bytes
"""from oscrypto.kdf import pbkdf2, pbkdf2_iteration_calculator
from oscrypto.util import rand_bytes
import os
# Generate secure random salt
salt = rand_bytes(16)
password = b"user_password_123"
# Calculate appropriate iteration count for ~100ms computation time
iterations = pbkdf2_iteration_calculator('sha256', 32, target_ms=100)
print(f"Using {iterations} iterations for ~100ms computation time")
# Derive AES-256 key (32 bytes)
key = pbkdf2('sha256', password, salt, iterations, 32)
print(f"Derived {len(key)} byte key")
print(f"Key: {key.hex()}")from oscrypto.kdf import pbkdf2
from oscrypto.util import rand_bytes, constant_compare
import hashlib
def hash_password(password: str) -> tuple:
"""Hash a password securely for storage."""
# Convert password to bytes
password_bytes = password.encode('utf-8')
# Generate random salt
salt = rand_bytes(16)
# Use strong iteration count
iterations = 100000
# Derive key and hash it for storage
key = pbkdf2('sha256', password_bytes, salt, iterations, 32)
return salt, iterations, key
def verify_password(password: str, stored_salt: bytes, stored_iterations: int, stored_key: bytes) -> bool:
"""Verify a password against stored hash."""
password_bytes = password.encode('utf-8')
# Derive key with same parameters
derived_key = pbkdf2('sha256', password_bytes, stored_salt, stored_iterations, 32)
# Use constant-time comparison
return constant_compare(derived_key, stored_key)
# Example usage
password = "my_secure_password"
# Store password
salt, iterations, key = hash_password(password)
print(f"Stored salt: {salt.hex()}")
print(f"Iterations: {iterations}")
# Verify password
is_valid = verify_password(password, salt, iterations, key)
print(f"Password valid: {is_valid}")
# Wrong password
is_valid = verify_password("wrong_password", salt, iterations, key)
print(f"Wrong password valid: {is_valid}")from oscrypto.kdf import pbkdf2
from oscrypto.symmetric import aes_cbc_pkcs7_encrypt, aes_cbc_pkcs7_decrypt
from oscrypto.util import rand_bytes
def encrypt_with_password(data: bytes, password: str) -> tuple:
"""Encrypt data using password-derived key."""
# Convert password to bytes
password_bytes = password.encode('utf-8')
# Generate random salt for key derivation
salt = rand_bytes(16)
# Derive AES-256 key
key = pbkdf2('sha256', password_bytes, salt, 100000, 32)
# Encrypt data
ciphertext = aes_cbc_pkcs7_encrypt(key, data)
return salt, ciphertext
def decrypt_with_password(salt: bytes, ciphertext: bytes, password: str) -> bytes:
"""Decrypt data using password-derived key."""
password_bytes = password.encode('utf-8')
# Derive the same key
key = pbkdf2('sha256', password_bytes, salt, 100000, 32)
# Decrypt data
return aes_cbc_pkcs7_decrypt(key, ciphertext)
# Example usage
plaintext = b"Secret document contents"
password = "encryption_password_123"
# Encrypt
salt, ciphertext = encrypt_with_password(plaintext, password)
print(f"Encrypted {len(plaintext)} bytes to {len(ciphertext)} bytes")
# Decrypt
decrypted = decrypt_with_password(salt, ciphertext, password)
print(f"Decrypted: {decrypted}")
assert decrypted == plaintextfrom oscrypto.kdf import pbkdf2
from oscrypto.util import rand_bytes
password = b"test_password"
salt = rand_bytes(16)
iterations = 10000
key_length = 32
# Test different hash algorithms
algorithms = ['sha1', 'sha256', 'sha384', 'sha512']
for algorithm in algorithms:
key = pbkdf2(algorithm, password, salt, iterations, key_length)
print(f"{algorithm.upper()}: {key[:8].hex()}...")from oscrypto.kdf import pbkdf2_iteration_calculator
import time
# Test different target computation times
targets = [50, 100, 200, 500] # milliseconds
for target_ms in targets:
iterations = pbkdf2_iteration_calculator('sha256', 32, target_ms, quiet=True)
# Verify actual timing
start = time.time()
pbkdf2('sha256', b'test', b'salt1234', iterations, 32)
actual_ms = (time.time() - start) * 1000
print(f"Target: {target_ms}ms, Iterations: {iterations}, Actual: {actual_ms:.1f}ms")Install with Tessl CLI
npx tessl i tessl/pypi-oscrypto