Security Advisories (2)
CVE-2026-41564 (2026-04-23)

CryptX versions before 0.088 for Perl do not reseed the Crypt::PK PRNG state after forking. The Crypt::PK::RSA, Crypt::PK::DSA, Crypt::PK::DH, Crypt::PK::ECC, Crypt::PK::Ed25519 and Crypt::PK::X25519 modules seed a per-object PRNG state in their constructors and reuse it without fork detection. A Crypt::PK::* object created before `fork()` shares byte-identical PRNG state with every child process, and any randomized operation they perform can produce identical output, including key generation. Two ECDSA or DSA signatures from different processes are enough to recover the signing private key through nonce-reuse key recovery. This affects preforking services such as the Starman web server, where a Crypt::PK::* object loaded at startup is inherited by every worker process.

CVE-2026-41565 (2026-05-28)

CryptX versions before 0.088_001 for Perl have a stack buffer overflow in four AEAD decrypt_verify helpers. The gcm_decrypt_verify, ccm_decrypt_verify, chacha20poly1305_decrypt_verify and eax_decrypt_verify XS routines copied the caller-supplied authentication tag into a fixed 144-byte stack buffer (MAXBLOCKSIZE) without checking the supplied length. A longer tag overwrites the stack past the buffer. Version 0.088 added the clamp to gcm_decrypt_verify, and 0.088_001 added it to the other three. Any caller of an affected helper that forwards an attacker-controlled tag longer than the buffer can trigger the overflow.

NAME

Crypt::KeyDerivation - PBKDF1, PBKDF2, HKDF, Bcrypt, Scrypt, Argon2 key derivation functions

SYNOPSIS

use Crypt::KeyDerivation ':all';

### PBKDF1/2
$derived_key1 = pbkdf1($password, $salt, $iteration_count, $hash_name, $len);
$derived_key2 = pbkdf1_openssl($password, $salt, $iteration_count, $hash_name, $len);
$derived_key3 = pbkdf2($password, $salt, $iteration_count, $hash_name, $len);

### HKDF & co.
$derived_key4 = hkdf($keying_material, $salt, $hash_name, $len, $info);
$prk  = hkdf_extract($keying_material, $salt, $hash_name);
$okm1 = hkdf_expand($prk, $hash_name, $len, $info);

### bcrypt / scrypt / argon2
$derived_key4 = bcrypt_pbkdf($password, $salt, $rounds, $hash_name, $len);
$derived_key5 = scrypt_pbkdf($password, $salt, $N, $r, $p, $len);
$derived_key6 = argon2_pbkdf($type, $password, $salt, $t_cost, $m_factor, $parallelism, $len, $secret, $ad);

DESCRIPTION

Provides an interface to key derivation functions:

While primarily designed for key derivation, the functions PBKDF2, Bcrypt, Scrypt and Argon2 are also widely used for password hashing. In that use case the derived key serves as the stored password hash.

FUNCTIONS

pbkdf1

BEWARE: if you are not sure, do not use pbkdf1 but rather choose pbkdf2.

$derived_key = pbkdf1($password, $salt, $iteration_count, $hash_name, $len);
#or
$derived_key = pbkdf1($password, $salt, $iteration_count, $hash_name);
#or
$derived_key = pbkdf1($password, $salt, $iteration_count);
#or
$derived_key = pbkdf1($password, $salt);

# $password ......... input keying material  (password)
# $salt ............. salt/nonce (expected length: 8)
# $iteration_count .. optional, DEFAULT: 5000
# $hash_name ........ optional, DEFAULT: 'SHA256'
# $len .............. optional, derived key len, DEFAULT: 32

pbkdf1_openssl

Since: CryptX-0.088

OpenSSL-compatible variant of PBKDF1 (implements EVP_BytesToKey). Unlike strict pbkdf1, the output length is not limited to the hash size -- it can be arbitrarily long by chaining hash blocks. OpenSSL defaults: MD5 hash, iteration_count=1.

$derived_key = pbkdf1_openssl($password, $salt, $iteration_count, $hash_name, $len);
#or
$derived_key = pbkdf1_openssl($password, $salt, $iteration_count, $hash_name);
#or
$derived_key = pbkdf1_openssl($password, $salt, $iteration_count);
#or
$derived_key = pbkdf1_openssl($password, $salt);

# $password ......... input keying material (password)
# $salt ............. salt/nonce (expected length: 8)
# $iteration_count .. optional, DEFAULT: 5000
# $hash_name ........ optional, DEFAULT: 'SHA256'
# $len .............. optional, derived key len, DEFAULT: 32

pbkdf2

$derived_key = pbkdf2($password, $salt, $iteration_count, $hash_name, $len);
#or
$derived_key = pbkdf2($password, $salt, $iteration_count, $hash_name);
#or
$derived_key = pbkdf2($password, $salt, $iteration_count);
#or
$derived_key = pbkdf2($password, $salt);

# $password ......... input keying material (password)
# $salt ............. salt/nonce
# $iteration_count .. optional, DEFAULT: 5000
# $hash_name ........ optional, DEFAULT: 'SHA256'
# $len .............. optional, derived key len, DEFAULT: 32

hkdf

$okm2 = hkdf($password, $salt, $hash_name, $len, $info);
#or
$okm2 = hkdf($password, $salt, $hash_name, $len);
#or
$okm2 = hkdf($password, $salt, $hash_name);
#or
$okm2 = hkdf($password, $salt);

# $password ... input keying material (password)
# $salt ....... salt/nonce, if undef defaults to HashLen zero octets
# $hash_name .. optional, DEFAULT: 'SHA256'
# $len ........ optional, derived key len, DEFAULT: 32
# $info ....... optional context and application specific information, DEFAULT: ''

hkdf_extract

$prk  = hkdf_extract($password, $salt, $hash_name);
#or
$prk  = hkdf_extract($password, $salt, $hash_name);

# $password ... input keying material (password)
# $salt ....... salt/nonce, if undef defaults to HashLen zero octets
# $hash_name .. optional, DEFAULT: 'SHA256'

hkdf_expand

$okm = hkdf_expand($pseudokey, $hash_name, $len, $info);
#or
$okm = hkdf_expand($pseudokey, $hash_name, $len);
#or
$okm = hkdf_expand($pseudokey, $hash_name);
#or
$okm = hkdf_expand($pseudokey);

# $pseudokey .. input keying material
# $hash_name .. optional, DEFAULT: 'SHA256'
# $len ........ optional, derived key len, DEFAULT: 32
# $info ....... optional context and application specific information, DEFAULT: ''

bcrypt_pbkdf

bcrypt-based key derivation as defined by the OpenBSD project.

Since: CryptX-0.088

$derived_key = bcrypt_pbkdf($password, $salt, $rounds, $hash_name, $len);
#or
$derived_key = bcrypt_pbkdf($password, $salt, $rounds, $hash_name);
#or
$derived_key = bcrypt_pbkdf($password, $salt, $rounds);
#or
$derived_key = bcrypt_pbkdf($password, $salt);

# $password ... input keying material (password)
# $salt ....... salt/nonce
# $rounds ..... optional, number of rounds, DEFAULT: 16
# $hash_name .. optional, DEFAULT: 'SHA512'
# $len ........ optional, derived key len, DEFAULT: 32

scrypt_pbkdf

scrypt key derivation according to https://tools.ietf.org/html/rfc7914.

Since: CryptX-0.088

$derived_key = scrypt_pbkdf($password, $salt, $N, $r, $p, $len);
#or
$derived_key = scrypt_pbkdf($password, $salt, $N, $r, $p);
#or
$derived_key = scrypt_pbkdf($password, $salt, $N);
#or
$derived_key = scrypt_pbkdf($password, $salt);

# $password ... input keying material (password)
# $salt ....... salt/nonce
# $N .......... optional, CPU/memory cost parameter (power of 2), DEFAULT: 1024
# $r .......... optional, block size, DEFAULT: 8
# $p .......... optional, parallelization parameter, DEFAULT: 1
# $len ........ optional, derived key len, DEFAULT: 32

argon2_pbkdf

Argon2 key derivation according to https://tools.ietf.org/html/rfc9106.

Since: CryptX-0.088

$derived_key = argon2_pbkdf($type, $password, $salt, $t_cost, $m_factor, $parallelism, $len, $secret, $ad);
#or
$derived_key = argon2_pbkdf($type, $password, $salt, $t_cost, $m_factor, $parallelism, $len);
#or
$derived_key = argon2_pbkdf($type, $password, $salt, $t_cost, $m_factor, $parallelism);
#or
$derived_key = argon2_pbkdf($type, $password, $salt);

# $type        ... one of 'argon2d', 'argon2i', 'argon2id'
# $password    ... input keying material (password)
# $salt        ... salt/nonce
# $t_cost      ... optional, time cost (number of iterations), DEFAULT: 3
# $m_factor    ... optional, memory cost in kibibytes, DEFAULT: 65536
# $parallelism ... optional, degree of parallelism, DEFAULT: 1
# $len         ... optional, derived key len, DEFAULT: 32
# $secret      ... optional, secret value, DEFAULT: ''
# $ad          ... optional, associated data, DEFAULT: ''