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

length is 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

length is 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_str contains 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

plaintext is not text or bytes, or key is not bytes.

ValueError

key is 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 ddp2 AES-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

token is not text or key is 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 cryptography package 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 cryptography package 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 cryptography package 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

True when the computed signature matches; otherwise False.

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

usedforsecurity is 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

key is not bytes.

ValueError

key is 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 than visible are masked completely.

Examples

Preserve only a diagnostic prefix:

masked = mask_secret("sk-abc123xyz789", visible=6)
assert masked == "sk-abc*********"