SCALARs and strings

crypt#

One-way passwd(5)-style hashing of a plaintext string with a salt.

crypt is a thin wrapper over the system C library’s crypt(3) function. It turns PLAINTEXT and SALT into a short string — a digest — and returns it. The same PLAINTEXT and SALT always produce the same digest; small changes in either produce a completely different digest. There is no inverse function: you cannot recover PLAINTEXT from a digest.

Warning

crypt is not a general-purpose encryption function, despite the name. It cannot be undone. It is also not a modern password hash on most traditional configurations — the historical Unix scheme considers only the first eight bytes of PLAINTEXT and produces a 13-byte digest over a 12-bit salt, which is unsuitable for protecting passwords against contemporary attackers. What crypt is good for is verifying a user-supplied string against an already-stored digest produced by the same system’s crypt. For anything else — encryption, key derivation, or hashing large data — use a real cryptographic library. See See also below.

Synopsis#

crypt PLAINTEXT, SALT

Both arguments are strings. The return value is a string digest.

What you get back#

A string whose exact shape depends on the host’s crypt(3) implementation. Traditionally it is 13 bytes: two bytes of salt followed by 11 bytes from the set [./0-9A-Za-z]. Modern glibc, musl, and BSD libcs accept extended salts that select stronger schemes (MD5, SHA-256, SHA-512, bcrypt, yescrypt) and return longer digests in the $id$salt$hash passwd-file format.

Treat the digest as opaque. Do not parse it, do not assume a length, and do not assume which bytes of SALT the implementation consumed. The correct way to verify a plaintext against an existing digest is to pass the digest itself back in as the salt:

if (crypt($plain, $digest) eq $digest) { ... }

The salt used to create the digest is embedded in the digest, so this re-runs the same scheme with the same parameters on the candidate plaintext. Code written this way works with the classical DES scheme and with every modern extension a given crypt(3) exposes, without having to know which one produced the stored digest.

Global state it touches#

crypt does not read or write any Perl special variable. On Unicode input it may emit a fatal Wide character in crypt error (see Edge cases).

Examples#

Verify a password against a stored passwd-style digest:

my $stored = (getpwuid($<))[1];       # encrypted field from passwd

print "Password: ";
chomp(my $word = <STDIN>);

if (crypt($word, $stored) eq $stored) {
    print "ok\n";
} else {
    die "Sorry...\n";
}

Create a fresh digest for a new password using a random two-character salt from the classical alphabet. The set is only a recommendation; the exact characters accepted in a salt are whatever the host crypt(3) allows, and Perl imposes no restriction of its own:

my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
my $salt  = $chars[rand 64] . $chars[rand 64];
my $hash  = crypt($plaintext, $salt);

Request a specific modern scheme by using an extended salt (glibc and compatible libcs). The $id$ prefix selects the algorithm — $1$ is MD5-crypt, $5$ is SHA-256-crypt, $6$ is SHA-512-crypt, $2b$ is bcrypt where supported:

my $hash = crypt($plaintext, '$6$rounds=100000$' . $random_salt);

Re-verify using the stored digest as the salt — scheme-agnostic:

my $candidate = crypt($plaintext, $stored_digest);
my $match     = $candidate eq $stored_digest;

Constant-time comparison of the two digests is still the caller’s responsibility; plain eq leaks timing information. If the threat model cares, compare byte-by-byte with a loop that does not short-circuit, or use a comparison routine from a crypto module.

Edge cases#

  • First-8-byte truncation on classical DES. On traditional Unix crypt(3), only the first eight bytes of PLAINTEXT affect the digest: crypt("password123", $s) and crypt("password456", $s) collide. Extended schemes ($1$, $5$, $6$, $2b$, …) do not have this limit, but the classical scheme is what you get from an unprefixed two-byte salt.

  • Salt-length assumptions are wrong. Do not assume substr($digest,0,2) is the salt. Extended digests carry the salt in a different position and length. Re-use the full digest as the salt argument (crypt($plain, $digest)) and let crypt(3) parse it.

  • Unicode input. If PLAINTEXT or SALT contains characters above codepoint 255, Perl attempts to downgrade a copy of the string to bytes before calling crypt(3). If every character fits in a byte, the downgrade succeeds silently. If any character does not, crypt dies with Wide character in crypt. Encode explicitly (e.g. Encode::encode_utf8) if your plaintext might contain wide characters.

  • Unsupported scheme returns failure. Passing a $id$ prefix the host crypt(3) does not understand typically returns undef or a short string starting with *0 or *1. Always check the return shape before using it.

  • crypt(3) may be absent or restricted. On a small number of hosts the library function has been removed or replaced with a stub for export-control reasons. crypt in Perl is only as capable as the underlying libc call; if the libc call is a stub, so is this.

  • Not suitable for hashing large data. crypt ignores most of its input on the classical scheme and is slow per call on the stretched schemes. It is the wrong tool for checksumming files or building content-addressable storage.

Differences from upstream#

Fully compatible with upstream Perl 5.42. pperl dispatches to the host crypt(3) the same way perl5 does, so the accepted salt formats and the exact digest shape match whatever the system library produces — including the availability of $1$/$5$/$6$/ $2b$ extended schemes.

See also#

  • Digest::MD5 — MD5-128 over arbitrary byte strings; use for fingerprinting data, not for password storage

  • Digest::SHA — SHA-1/2 family digests; use for integrity checks and as a building block for HMAC and key derivation

  • Crypt::Bcrypt, Crypt::Argon2, Crypt::Passphrase — CPAN modules for modern password hashing; prefer these over crypt for any new password-verification code

  • Crypt::Eksblowfish, Crypt::ScryptKDF — CPAN modules for bcrypt and scrypt-based key derivation

  • getpwuid — fetch the encrypted-password field from the local passwd database; its seventh field is the digest you pass as SALT when verifying

  • chomp — remove the trailing newline from a password read with <STDIN> before passing it to crypt