Skip to content

jam.utils

utils

Various utilities that help with authorization.

Modules:

Name Description
aes
await_maybe
basic_auth
config_maker
ed
otp_keys
rsa
salt_hash

Utilities for secure password hashing and verification. Uses PBKDF2-HMAC-SHA256 with salt and constant-time comparison.

symmetric
xchacha20poly1305
xor

Functions:

Name Description
basic_auth_decode

Decodes basic auth token to login and password.

basic_auth_encode

Encodes the login and password in a basic auth token.

check_password

Verifies a password by recalculating the hash and comparing it to the stored hash.

deserialize_hash

Splits a stored string into salt and hash.

generate_aes_key

Generate a new AES key.

generate_ecdsa_p384_keypair

Generate ECDSA P-384 key pair.

generate_ed25519_keypair

Generate Ed25519 key.

generate_otp_key

Generate generic OTP secret key.

generate_rsa_key_pair

RSA key generation utility.

generate_symmetric_key

Generate a N-byte symmetric key.

hash_password

Hashes a password with a salt using PBKDF2-HMAC-SHA256.

otp_key_from_string

Generate OTP-valid key from string.

serialize_hash

Combines salt and hash into a single string for database storage.

xor_my_data

Encrypts a string using a secret key with the XOR cipher.

basic_auth_decode

basic_auth_decode(data: str) -> tuple[str, str]

Decodes basic auth token to login and password.

Parameters:

Name Type Description Default
data str

Decoded data

required

Raises:

Type Description
ValueError

If incorrect format

Example

login, password = basic_auth_decode(header) print(login, password) admin qwerty1234

basic_auth_encode

basic_auth_encode(login: str, password: str) -> str

Encodes the login and password in a basic auth token.

Parameters:

Name Type Description Default
login str

Login

required
password str

Password

required
Example
>>> header = basic_auth_encode("admin", "qwerty1234")
>>> print(header)
YWRtaW46cXdlcnR5MTIzNA==

check_password

check_password(
    password: str,
    salt_hex: str,
    hash_hex: str,
    iterations: int = 100000,
) -> bool

Verifies a password by recalculating the hash and comparing it to the stored hash.

Parameters:

Name Type Description Default
password str

Password to verify.

required
salt_hex str

Hex representation of the salt.

required
hash_hex str

Hex representation of the stored hash.

required
iterations int

Number of PBKDF2 iterations, must match the hashing call.

100000

Returns:

Name Type Description
bool bool

True if the password is correct, False otherwise.

Example
>>> salt, hash_ = hash_password("my_password")
>>> check_password("my_password", salt, hash_)
True
>>> check_password("wrong_password", salt, hash_)
False

# Using custom iterations
>>> salt, hash_ = hash_password("my_password", iterations=150_000)
>>> check_password("my_password", salt, hash_, iterations=150_000)
True

deserialize_hash

deserialize_hash(data: str) -> tuple[str, str]

Splits a stored string into salt and hash.

Example
>>> salt, hash_ = deserialize_hash("abcdef1234$9876543210")
>>> isinstance(salt, str)
True
>>> isinstance(hash_, str)
True

generate_aes_key

generate_aes_key() -> bytes

Generate a new AES key.

generate_ecdsa_p384_keypair

generate_ecdsa_p384_keypair() -> dict[str, str]

Generate ECDSA P-384 key pair.

Returns:

Type Description
dict[str, str]

dict[str, str]: {'private': KEY, 'public': KEY}

generate_ed25519_keypair

generate_ed25519_keypair() -> dict[str, str]

Generate Ed25519 key.

Returns:

Type Description
dict[str, str]

dict[str, str]: {'private': KEY, 'public': KEY}

generate_otp_key

generate_otp_key(entropy_bits: int = 128) -> str

Generate generic OTP secret key.

Parameters:

Name Type Description Default
entropy_bits int

Entropy bits to key

128

Returns:

Type Description
str

str

generate_rsa_key_pair

generate_rsa_key_pair(
    key_size: int = 2048,
) -> dict[str, str]

RSA key generation utility.

Parameters:

Name Type Description Default
key_size int

Size of RSA key

2048

Returns:

Type Description
dict

with public and private keys in format:

{
    "public": "some_key",
    "private": "key"
}

generate_symmetric_key

generate_symmetric_key(n: int = 32) -> str

Generate a N-byte symmetric key.

Parameters:

Name Type Description Default
n int

Bytes for key

32

Returns:

Name Type Description
str str

Key

hash_password

hash_password(
    password: str,
    salt: bytes | None = None,
    iterations: int = 100000,
    salt_size: int = 16,
) -> tuple[str, str]

Hashes a password with a salt using PBKDF2-HMAC-SHA256.

Parameters:

Name Type Description Default
password str

Password to hash.

required
salt bytes | None

Salt. If None, a random salt is generated.

None
iterations int

Number of PBKDF2 iterations.

100000
salt_size int

Size of the random salt in bytes.

16

Returns:

Type Description
tuple[str, str]

tuple[str, str]: (hex_salt, hex_hash)

Example
>>> salt, hash_ = hash_password("my_password")
>>> isinstance(salt, str)
True
>>> isinstance(hash_, str)
True

# Using custom iterations and salt size
>>> salt, hash_ = hash_password("my_password", iterations=150_000, salt_size=24)

otp_key_from_string

otp_key_from_string(s: str) -> str

Generate OTP-valid key from string.

Parameters:

Name Type Description Default
s str

String for key

required

Returns:

Name Type Description
bytes str

OTP key

Example
>>> from jam.utils import otp_key_from_string
>>> user_email: str = "some.email@mail.com"
>>> key = otp_key_from_string(user_email)
>>> print(key)
'O54O6YRKTH3IPNEBIUMKMK3FZ35OF6Q5'

serialize_hash

serialize_hash(salt_hex: str, hash_hex: str) -> str

Combines salt and hash into a single string for database storage.

Example
>>> salt, hash_ = hash_password("my_password")
>>> serialized = serialize_hash(salt, hash_)
>>> isinstance(serialized, str)
True

xor_my_data

xor_my_data(data: str, key: str) -> str

Encrypts a string using a secret key with the XOR cipher.

This function performs an XOR operation on each byte of the input data with the corresponding byte of the key. If the key is shorter than the data, it wraps around and continues from the beginning of the key. If you need to decrypt data, use the same function with the same secret key. XOR is a symmetric operation.

Parameters:

Name Type Description Default
data str

The plain text string to be encrypted.

required
key str

The secret key used for encryption.

required

Returns:

Name Type Description
str str

The encrypted data represented as a hexadecimal string.

Example
>>> encrypted = xor_my_data("Hello, World!", "secretkey")
>>> print(encrypted)
<...encrypted hex string...>