ddp_utils.crypto¶
import ddp_utils.crypto
Provide cryptographic keys, authenticated encryption, signatures, and hashes.
AES helpers emit versioned ddp2 AES-256-GCM tokens. Fernet support is
available when cryptography is installed. Weak MD5 and SHA-1 hashes are
rejected for security use unless the caller explicitly opts into a legacy
non-security checksum.
Examples
Encrypt and authenticate a text value:
from ddp_utils.crypto import decrypt_str, encrypt, generate_key
key = generate_key()
token = encrypt("secret data", key)
assert decrypt_str(token, key) == "secret data"
Sign and verify an application message:
from ddp_utils.crypto import sign, verify
signature = sign("payload", key)
assert verify("payload", signature, key)
- ddp_utils.crypto.generate_key(length: int = 32) bytes¶
Generate cryptographically secure random key bytes.
Parameters
Name
Type
Description
length
int
Number of random bytes. The default produces an AES-256 key.
Returns
Type
Description
bytes
Random key bytes from the operating-system entropy source.
Raises
Exception
Description
ValueError
lengthis negative.Examples
Generate a key accepted by
encrypt():key = generate_key() assert len(key) == 32
- ddp_utils.crypto.generate_token(length: int = 32) str¶
Generate a URL-safe random text token.
Parameters
Name
Type
Description
length
int
Number of random bytes encoded into the token.
Returns
Type
Description
str
URL-safe Base64 text without a fixed character length.
Raises
Exception
Description
ValueError
lengthis negative.Examples
Create a session correlation token:
correlation_id = generate_token(24)
- ddp_utils.crypto.key_to_str(key: bytes) str¶
Encode raw key bytes as URL-safe Base64 text.
Parameters
Name
Type
Description
key
bytes
Raw key bytes.
Returns
Type
Description
str
ASCII Base64 representation suitable for text configuration storage.
Examples
Serialize a generated key:
stored = key_to_str(generate_key())
- ddp_utils.crypto.key_from_str(key_str: str) bytes¶
Decode URL-safe Base64 text into raw key bytes.
Parameters
Name
Type
Description
key_str
str
ASCII Base64 representation produced by
key_to_str().Returns
Type
Description
bytes
Decoded key bytes.
Raises
Exception
Description
UnicodeEncodeError
key_strcontains non-ASCII characters.Examples
Round-trip a generated key:
key = generate_key() assert key_from_str(key_to_str(key)) == key
- ddp_utils.crypto.derive_key(password: str, salt: bytes | None = None, length: int = 32) tuple[bytes, bytes]¶
Derive key bytes from a password with PBKDF2-HMAC-SHA256.
Parameters
Name
Type
Description
password
str
Unicode password encoded as UTF-8.
salt
bytes | None
Existing salt for deterministic re-derivation. A secure 16-byte salt is generated when omitted.
length
int
Desired derived-key length in bytes.
Returns
Type
Description
tuple[bytes, bytes]
Pair containing the derived key and the salt that must be retained.
Examples
Derive and later reproduce an encryption key:
key, salt = derive_key("correct horse battery staple") same_key, _ = derive_key("correct horse battery staple", salt) assert same_key == key
- ddp_utils.crypto.encrypt(plaintext: str | bytes, key: bytes) str¶
Encrypt and authenticate text or bytes with AES-256-GCM.
Parameters
Name
Type
Description
plaintext
str | bytes
UTF-8 text or raw bytes to encrypt.
key
bytes
Exact 32-byte AES-256 key.
Returns
Type
Description
str
Versioned
ddp2.<base64url>token containing the random nonce, ciphertext, and authentication tag.Raises
Exception
Description
TypeError
plaintextis not text or bytes, orkeyis not bytes.ValueError
keyis not exactly 32 bytes.Examples
Encrypt binary application data:
key = generate_key() token = encrypt(b"private payload", key)
- ddp_utils.crypto.decrypt(token: str, key: bytes) bytes¶
Decrypt and authenticate a current
ddp2AES-256-GCM token.Parameters
Name
Type
Description
token
str
Versioned token produced by
encrypt().key
bytes
Exact 32-byte key used for encryption.
Returns
Type
Description
bytes
Original plaintext bytes.
Raises
Exception
Description
TypeError
tokenis not text orkeyis not bytes.ValueError
The key size, token version, encoding, length, or authentication tag is invalid.
Examples
Recover an encrypted payload:
key = generate_key() assert decrypt(encrypt("secret", key), key) == b"secret"
- ddp_utils.crypto.decrypt_str(token: str, key: bytes, encoding: str = 'utf-8') str¶
Decrypt a token and decode its plaintext bytes as text.
Parameters
Name
Type
Description
token
str
Versioned token produced by
encrypt().key
bytes
Exact 32-byte encryption key.
encoding
str
Codec used to decode the plaintext.
Returns
Type
Description
str
Decoded plaintext string.
Raises
Exception
Description
ValueError
Token validation or authentication fails.
UnicodeDecodeError
Plaintext is invalid for
encoding.Examples
Recover Unicode text:
key = generate_key() assert decrypt_str(encrypt("hello", key), key) == "hello"
- ddp_utils.crypto.fernet_key() bytes¶
Generate a Base64-encoded key accepted by Fernet.
Returns
Type
Description
bytes
Fernet-compatible key bytes.
Raises
Exception
Description
ImportError
The optional
cryptographypackage is unavailable.Examples
Generate a key for the Fernet helpers:
key = fernet_key()
- ddp_utils.crypto.fernet_encrypt(plaintext: str | bytes, key: bytes) bytes¶
Encrypt text or bytes into an authenticated Fernet token.
Parameters
Name
Type
Description
plaintext
str | bytes
UTF-8 text or bytes to encrypt.
key
bytes
Base64-encoded Fernet key.
Returns
Type
Description
bytes
Fernet token bytes containing its creation timestamp.
Raises
Exception
Description
ImportError
The optional
cryptographypackage is unavailable.Examples
Encrypt a short secret:
key = fernet_key() token = fernet_encrypt("secret", key)
- ddp_utils.crypto.fernet_decrypt(token: bytes, key: bytes) bytes¶
Authenticate and decrypt a Fernet token.
Parameters
Name
Type
Description
token
bytes
Token produced by
fernet_encrypt().key
bytes
Base64-encoded Fernet key.
Returns
Type
Description
bytes
Original plaintext bytes.
Raises
Exception
Description
ImportError
The optional
cryptographypackage is unavailable.Examples
Round-trip Fernet-protected data:
key = fernet_key() assert fernet_decrypt(fernet_encrypt("secret", key), key) == b"secret"
- ddp_utils.crypto.sign(message: str | bytes, key: bytes, algorithm: str = 'sha256') str¶
Calculate a hexadecimal HMAC signature for a message.
Parameters
Name
Type
Description
message
str | bytes
UTF-8 text or raw message bytes.
key
bytes
Secret HMAC key bytes.
algorithm
str
Digest name accepted by
hmac.Returns
Type
Description
str
Lowercase hexadecimal signature.
Examples
Sign an API payload:
signature = sign("payload", b"shared-secret")
- ddp_utils.crypto.verify(message: str | bytes, signature: str, key: bytes, algorithm: str = 'sha256') bool¶
Verify an HMAC signature using constant-time comparison.
Parameters
Name
Type
Description
message
str | bytes
UTF-8 text or raw message bytes.
signature
str
Expected hexadecimal signature.
key
bytes
Secret HMAC key bytes.
algorithm
str
Digest name used when signing.
Returns
Type
Description
bool
Truewhen the computed signature matches; otherwiseFalse.Examples
Reject a modified message:
signature = sign("original", b"shared-secret") assert not verify("modified", signature, b"shared-secret")
- ddp_utils.crypto.hash_string(value: str | bytes, algorithm: str = 'sha256', *, usedforsecurity: bool = True) str¶
Hash text or bytes under the configured weak-algorithm policy.
Parameters
Name
Type
Description
value
str | bytes
UTF-8 text or raw bytes.
algorithm
str
Digest name accepted by
hashlib.new().usedforsecurity
bool
Reject MD5 and SHA-1 when
True.Returns
Type
Description
str
Lowercase hexadecimal digest.
Raises
Exception
Description
TypeError
usedforsecurityis not a boolean.ValueError
A weak or unknown digest violates the requested policy.
Examples
Calculate a SHA-256 content identifier:
digest = hash_string("payload")
Allow an MD5 legacy checksum explicitly:
legacy = hash_string("payload", "md5", usedforsecurity=False)
- ddp_utils.crypto.hash_file(path: str | Path, algorithm: str = 'sha256', chunk_size: int = 65536, *, usedforsecurity: bool = True) str¶
Hash a file incrementally without loading it entirely into memory.
Parameters
Name
Type
Description
path
str | Path
File to read.
algorithm
str
Digest name accepted by
hashlib.new().chunk_size
int
Maximum bytes read per iteration.
usedforsecurity
bool
Reject MD5 and SHA-1 when
True.Returns
Type
Description
str
Lowercase hexadecimal digest.
Raises
Exception
Description
OSError
The file cannot be opened or read.
ValueError
A weak or unknown digest violates the requested policy.
Examples
Hash a downloaded artifact:
digest = hash_file("report.pdf")
- ddp_utils.crypto.encrypt_file(src: str | Path, dst: str | Path, key: bytes) None¶
Encrypt an entire file into a versioned AES-256-GCM token file.
Parameters
Name
Type
Description
src
str | Path
Plaintext source file.
dst
str | Path
Destination receiving the ASCII token.
key
bytes
Exact 32-byte AES-256 key.
Raises
Exception
Description
OSError
Source reading or destination writing fails.
TypeError
keyis not bytes.ValueError
keyis not exactly 32 bytes.Examples
Encrypt a report for storage:
key = generate_key() encrypt_file("report.pdf", "report.pdf.ddp", key)
- ddp_utils.crypto.decrypt_file(src: str | Path, dst: str | Path, key: bytes) None¶
Decrypt a token file produced by
encrypt_file().Parameters
Name
Type
Description
src
str | Path
ASCII token source file.
dst
str | Path
Destination receiving plaintext bytes.
key
bytes
Exact 32-byte AES-256 key.
Raises
Exception
Description
OSError
Source reading or destination writing fails.
UnicodeDecodeError
The source is not an ASCII token file.
ValueError
Token validation or authentication fails.
Examples
Restore an encrypted report:
decrypt_file("report.pdf.ddp", "report.pdf", key)
- ddp_utils.crypto.mask_secret(value: str, visible: int = 4) str¶
Mask a secret while preserving a configurable leading prefix.
Parameters
Name
Type
Description
value
str
Secret text to mask.
visible
int
Number of leading characters to retain.
Returns
Type
Description
str
A same-length string whose hidden characters are replaced by
*. Values no longer thanvisibleare masked completely.Examples
Preserve only a diagnostic prefix:
masked = mask_secret("sk-abc123xyz789", visible=6) assert masked == "sk-abc*********"