Crypt::...ivation(3)

πŸŽ€ Crypt::KeyDerivation(3) πŸŽ€

okay so bestie. this is Crypt::KeyDerivation, from the User Contributed Perl Documentation set, section 3 of the manual. it's the key-derivation girlie. she makes keys out of passwords. she's iconic, she's lowkey the backbone of like half the auth code you've ever touched, and she has opinions about zero-length output. buckle up πŸ’…


✨ NAME ✨

Crypt::KeyDerivation β€” PBKDF1, PBKDF2, HKDF, Bcrypt, Scrypt, Argon2 key derivation functions

like, the whole roster. the entire cast. no cap 🫢


🌸 SYNOPSIS 🌸

here's the whole moodboard, verbatim, don't touch it:

use Crypt::KeyDerivation ':all';

my $password = 'secret';
my $salt = '12345678';
my $iteration_count = 5000;
my $hash_name = 'SHA256';
my $len = 32;
my $keying_material = 'input keying material';
my $info = 'context';
my $rounds = 16;
my $N = 1024;
my $r = 8;
my $p = 1;
my $type = 'argon2id';
my $t_cost = 3;
my $m_factor = 65536;
my $parallelism = 1;
my $secret = '';
my $ad = '';

### PBKDF1/2
my $pbkdf1_key = pbkdf1($password, $salt, $iteration_count, $hash_name, $len);
my $openssl_pbkdf1_key = pbkdf1_openssl($password, $salt, $iteration_count, $hash_name, $len);
my $pbkdf2_key = pbkdf2($password, $salt, $iteration_count, $hash_name, $len);

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

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

look at her go. literally every one of these is a little key factory πŸ”₯


πŸ¦‹ DESCRIPTION πŸ¦‹

so what does she actually do. she gives you an interface to key derivation functions. plural. the whole squad:

each one with the receipt. respect the RFCs, bestie πŸ“œβœ¨


πŸ’œ the password-hashing of it all

ik they're called key derivation functions, that's literally the design brief. but listen β€” PBKDF2, Bcrypt, Scrypt and Argon2 are also widely used for password hashing. and in that whole situation? the derived key is the stored password hash. that's the whole trick. main character behaviour honestly πŸ‘‘


🌈 the zero-length output situation (important, bestie, read this one)

a few things you need to know about what comes out of these functions:

  • all functions return raw bytes. not hex, not base64, not a cute little string. bytes. periodt.
  • passing an output length of 0 returns an empty string in this wrapper API.

and here's the part people get wrong πŸ™ˆ β€” argument validation still happens first. so even though you asked for a zero-length output, she still checks your work:

  • required password / input-keying-material arguments reject "undef"
  • invalid hash names are still rejected
  • invalid Argon2 type names are still rejected
  • malformed optional scalar arguments still return "undef" where applicable

like she's not gonna let you slide just because you asked for nothing. standards πŸ’…

okay so this next bit is literally the disclaimer, don't skip it: this zero-length behaviour is a Crypt::KeyDerivation wrapper policy. the underlying libtomcrypt functions do not all behave the same way β€” some accept zero-length outputs, while others reject them with algorithm-specific checks. so if you're calling libtomcrypt directly, do not assume the wrapper behaviour. different girl, different rules. fr fr 😭

🧚 EXPORT 🧚

nothing is exported by default. she's not gonna dump her whole closet on you uninvited.

you can export selected functions, like a curated little capsule wardrobe:

use Crypt::KeyDerivation qw(
  pbkdf1 pbkdf1_openssl pbkdf2
  hkdf hkdf_expand hkdf_extract
  bcrypt_pbkdf scrypt_pbkdf argon2_pbkdf
);

or all of them at once, if you're that kind of girlie:

use Crypt::KeyDerivation ':all';

go off πŸ’–


part 1 of 2, bestie β€” there's more coming, don't go anywhere 🌸

✨ FUNCTIONS ✨

πŸ’– pbkdf1

BEWARE: if you are not sure, do not use pbkdf1 β€” choose pbkdf2 instead. Like, seriously, bestie, this is not the one to vibe-check your way through.

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

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

In strict PKCS #5 v1 mode, the derived key length must not exceed the selected hash output size. For example, SHA1 allows at most 20 bytes. That is a hard limit, no cap.

The underlying algorithm uses only the first 8 bytes of $salt. Shorter salts are rejected; longer salts are accepted but truncated to 8 bytes. So yes, it is literally giving β€œwe only need the first 8 bytes” energy.


πŸ’… 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. Iconic behaviour, honestly.

Important: this function implements the OpenSSL-compatible algorithm, but its default parameters do not match the historical openssl enc defaults. OpenSSL traditionally uses MD5 and iteration_count=1, while this wrapper defaults to SHA256 and 5000. If you need output compatible with the traditional OpenSSL default behaviour, pass both values explicitly:

my $derived_key = pbkdf1_openssl($password, $salt, 1, 'MD5', $len);

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

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

The underlying algorithm uses only the first 8 bytes of $salt. Shorter salts are rejected; longer salts are accepted but truncated to 8 bytes. Same salty little rule as above, bestie.


🫢 pbkdf2

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

# $password ......... [binary string] input keying material (password)
# $salt ............. [binary string] salt/nonce (any length; longer is better)
# $iteration_count .. [integer] optional, DEFAULT: 5000
# $hash_name ........ [string]  optional, DEFAULT: 'SHA256'
# $len .............. [integer] optional, derived key len in bytes, DEFAULT: 32

This is the one you probably want. Salt can be any length, and longer is better β€” she said what she said. πŸ’…


🌸 hkdf

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

# $password ... [binary string] input keying material
# $salt ....... [binary string | undef] salt; if undef defaults to HashLen zero octets
# $hash_name .. [string]  optional, DEFAULT: 'SHA256'
# $len ........ [integer] optional, derived key len in bytes, DEFAULT: 32
# $info ....... [binary string] optional context/application info, DEFAULT: ''

Use hkdf for one-shot extract+expand. For multi-step workflows, use hkdf_extract followed by hkdf_expand. It is giving β€œchoose your own adventure,” but cryptographic.

The input keying material / pseudokey arguments must be string or stringifiable scalars. Optional $salt and $info may be undef.


πŸ¦‹ hkdf_extract

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

# $password ... [binary string] input keying material
# $salt ....... [binary string | undef] salt; if undef defaults to HashLen zero octets
# $hash_name .. [string]  optional, DEFAULT: 'SHA256'

Returns the pseudorandom key (PRK). Its length is the digest size of the selected hash and it is intended to be passed to hkdf_expand. So like, this is the pre-game, not the whole party.


πŸŽ€ hkdf_expand

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

# $pseudokey .. [binary string] input keying material (normally from hkdf_extract)
# $hash_name .. [string]  optional, DEFAULT: 'SHA256'
# $len ........ [integer] optional, derived key len in bytes, DEFAULT: 32
# $info ....... [binary string] optional context/application info, DEFAULT: ''

$pseudokey is normally the PRK returned by hkdf_extract. Main character energy: it expands the vibe into your final output keying material.


πŸ“ bcrypt_pbkdf

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

Since: CryptX-0.088

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

# $password ... [binary string] input keying material (password)
# $salt ....... [binary string] salt/nonce
# $rounds ..... [integer] optional, number of rounds, DEFAULT: 16
# $hash_name .. [string]  optional, DEFAULT: 'SHA512'
# $len ........ [integer] optional, derived key len in bytes, DEFAULT: 32

Larger $rounds values increase CPU cost linearly. So if you crank it, your CPU is gonna feel it, no cap.


🌈 scrypt_pbkdf

scrypt key derivation according to <https://www.rfc-editor.org/rfc/rfc7914>.

Since: CryptX-0.088

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

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

Use only power-of-two values for $N. Larger $N, $r, and $p increase resource usage substantially; invalid combinations croak. So do not just freestyle the numbers, bestie β€” that is not the move.


πŸ‘‘ argon2_pbkdf

Argon2 key derivation according to <https://www.rfc-editor.org/rfc/rfc9106>.

Since: CryptX-0.088

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

# $type        ... [string]  one of 'argon2d', 'argon2i', 'argon2id'
# $password    ... [binary string] input keying material (password)
# $salt        ... [binary string] salt/nonce (recommended: at least 16 bytes)
# $t_cost      ... [integer] optional, time cost (number of iterations), DEFAULT: 3
# $m_factor    ... [integer] optional, memory cost in kibibytes (1 KiB = 1024 B), DEFAULT: 65536 (= 64 MiB)
# $parallelism ... [integer] optional, degree of parallelism, DEFAULT: 1
# $len         ... [integer] optional, derived key len in bytes, DEFAULT: 32
# $secret      ... [binary string] optional, secret value, DEFAULT: ''
# $ad          ... [binary string] optional, associated data, DEFAULT: ''

Increasing $t_cost, $m_factor, or $parallelism increases work and memory requirements. Invalid combinations croak. Optional $secret and $ad may be undef; otherwise they must be string or stringifiable scalars. Basically: the settings are not decorative, they are the whole point. ✨


πŸ’œ SEE ALSO


And that is the whole page, bestie. This little masterpiece came from perl v5.42.0, dated 2026-05-11 β€” screenshot it, cite it, live your truth. πŸ«ΆπŸ’–

πŸ“¦ where this page comes from