CRYPT(3)

๐Ÿ’– CRYPT(3) ๐Ÿ’– โ€” Library Functions Manual

okay bestie, sit down, hydrate, because we're talking about crypt, crypt_r, crypt_rn, and crypt_ra โ€” the passphrase hashing girlies from the Crypt Library (libcrypt, -lcrypt). this is the manual page, part one, and it's lowkey a lot but we're gonna get through it together. ๐Ÿซถ


โœจ NAME โœจ

crypt, crypt_r, crypt_rn, crypt_ra โ€” passphrase hashing ๐Ÿ’…


๐ŸŒธ LIBRARY ๐ŸŒธ

Crypt Library โ€” that's libcrypt, and you link it with -lcrypt. periodt.


๐ŸŽ€ SYNOPSIS ๐ŸŽ€

#include <crypt.h>

char *
crypt(const char *phrase, const char *setting);

char *
crypt_r(const char *phrase, const char *setting,
    struct crypt_data *data);

char *
crypt_rn(const char *phrase, const char *setting,
    struct crypt_data *data, int size);

char *
crypt_ra(const char *phrase, const char *setting, void **data,
    int *size);

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

so. the way that crypt, crypt_r, crypt_rn, and crypt_ra work is: they irreversibly "hash" a phrase so you can store it in the system password database โ€” that's shadow(5), iykyk โ€” using a cryptographic "hashing method." the thing you get out of that is called a "hashed passphrase" or just a "hash" for short. all the hashing methods themselves live in crypt(5). go look, they're cute.

setting is the main character here โ€” it picks which hashing method gets used, and it also hands over a bunch of parameters to whichever method you chose. most importantly it carries a random "salt", which is the whole reason no two stored hashes come out identical even when the phrase strings are literally the same. no cap. โœจ

the data argument to crypt_r is a structure of type struct crypt_data. at minimum it has these fields:

struct crypt_data {
    char output[CRYPT_OUTPUT_SIZE];
    char setting[CRYPT_OUTPUT_SIZE];
    char input[CRYPT_MAX_PASSPHRASE_SIZE];
    char initialized;
};

once crypt_r returns successfully, the hashed passphrase is sitting pretty in output. apps are encouraged (but like, not required, it's not that deep) to use the input and setting fields to stash the strings they're gonna pass as phrase and setting to crypt_r. why? because then it's way easier to wipe all the sensitive data once you don't need it anymore. slay. ๐Ÿ”ฅ

โš ๏ธ the initialized field must be set to zero before the very first time a struct crypt_data object is used in a call to crypt_r(). we recommend zeroing the entire object โ€” not just initialized, and not just the documented fields โ€” before first use. (obviously do this before you store anything in setting and input, bestie.)

the data argument to crypt_rn should also point at a struct crypt_data object, and size should be the size of that object, cast to int. here's the thing though โ€” when you're using crypt_rn, the entire data object (except for the input and setting fields) must be zeroed before its first use. that is not a recommendation like it is for crypt_r. it's the rules. otherwise, the fields do exactly the same jobs they do for crypt_r. ๐Ÿ’€

on the first call to crypt_ra, data should be the address of a void * variable that's set to NULL, and size should be the address of an int variable set to zero. crypt_ra will then allocate and initialize a struct crypt_data object for you using malloc(3), and write its address and size into the variables that data and size point at. you can reuse those in later calls, iconic. once the app is done hashing passphrases, it should deallocate that struct crypt_data object with free(3). ๐Ÿงš


โญ RETURN VALUES โญ

when everything goes right, crypt, crypt_r, crypt_rn, and crypt_ra return a pointer to a string that encodes both the hashed passphrase and the settings used to encode it. that string is directly usable as setting in other calls to crypt, crypt_r, crypt_rn, and crypt_ra, and as prefix in calls to crypt_gensalt, crypt_gensalt_rn, and crypt_gensalt_ra. it will be entirely printable ASCII, and it will not contain whitespace or the characters ':', ';', '*', '!', or '\'. head to crypt(5) for more detail on the format of hashed passphrases. ๐Ÿ’œ

crypt puts its result in a static storage area, which gets overwritten by subsequent calls to crypt. so it is not safe to call crypt from multiple threads at the same time. also, don't use the pointer it returned as an argument for another call to crypt โ€” some implementations, including earlier releases of libxcrypt, may overwrite the underlying static output buffer before computing the hash. rude, honestly. ๐Ÿ˜ญ

crypt_r, crypt_rn, and crypt_ra put their result in the output field of their data argument. it is safe to call them from multiple threads simultaneously, as long as you use a separate data object for each thread. and same energy as before: don't use the returned pointer as an argument for another call to crypt_r, crypt_rn, and crypt_ra that reuses the same data object for subsequent calls, because some implementations, including earlier releases of libxcrypt, may overwrite the output field of the passed data argument before computing the hash. also, calling crypt_ra with the phrase and/or setting parameters located inside a passed data object that's smaller than the size of struct crypt_data? not recommended. some implementations, including earlier releases of libxcrypt, may not preserve the passed literals if the data object needs to be altered. ๐Ÿ’…

when there's an error, crypt_r, crypt_rn, and crypt_ra write an invalid hash to the output field of their data argument, and crypt writes an invalid hash to its static storage area. that string will be shorter than 13 characters, will begin with a '*', and will not compare equal to setting.

on error, crypt_rn and crypt_ra return a null pointer. crypt_r and crypt may also return a null pointer, or they may return a pointer to that invalid hash โ€” it depends on how libcrypt was configured. (the option to return the invalid hash exists for compatibility with old applications that assume crypt can't return a null pointer. see "PORTABILITY NOTES" below. ๐Ÿ‘€)

all four functions set errno when they fail. when they succeed, the value of errno is unspecified and you must not rely on it. periodt.


๐ŸŒˆ ERRORS ๐ŸŒˆ

  • EINVAL โ€” setting is invalid, or it's requesting a hashing method that isn't supported. ๐Ÿ™ˆ
  • ERANGE โ€” phrase is too long (more than CRYPT_MAX_PASSPHRASE_SIZE characters; some hashing methods may have lower limits).
    • crypt_rn only: size is too small for the hashing method requested by setting.
  • ENOMEM โ€” failed to allocate internal scratch memory.
    • crypt_ra only: failed to allocate memory for data.
  • ENOSYS or EOPNOTSUPP โ€” hashing passphrases is not supported at all on this installation, or the hashing method requested by setting is not supported. these error codes are not used by this version of libcrypt, but you may run into them on other systems. โญ

๐Ÿ’œ PORTABILITY NOTES ๐Ÿ’œ

crypt is included in POSIX, but crypt_r, crypt_rn, and crypt_ra are not part of any standard. sorry bestie, that's just how it is.

POSIX permits crypt to use a single storage area for all callers, even when threads are in the picture. this library uses per-thread storage areas, but portable code should still avoid calling crypt from multiple threads simultaneously. lowkey just be careful. ๐ŸŒธ

POSIX doesn't specify any hashing methods, and it doesn't require hashed passphrases to be portable between systems. in practice, hashed passphrases are portable as long as both systems support the hashing method that was used. however, the set of supported hashing methods varies a lot from system to system. ๐Ÿ˜ญ

the behavior of crypt on errors isn't well standardized. some implementations literally can't fail (except by crashing the program ๐Ÿ’€), others return a null pointer or a fixed string. most implementations don't set errno, but some do. POSIX specifies returning a null pointer and setting errno, but it only defines one possible error โ€” ENOSYS โ€” for the case where crypt isn't supported at all. some older applications are not prepared to handle null pointers coming back from crypt. the behavior described above for this implementation โ€” setting errno and returning an invalid hash that's different from setting โ€” was chosen to make those applications fail closed when an error occurs. it's giving safety. ๐Ÿ”ฅ

because of historical restrictions on exporting cryptographic software from the USA, crypt is an optional POSIX component. so applications should be prepared for crypt to not be available, or to always fail at runtime (with errno set to ENOSYS). ๐Ÿ™ˆ

POSIX specifies that crypt is declared in <unistd.h>, but only if the macro _XOPEN_CRYPT is defined and has a value greater than or equal to zero. since libcrypt does not provide <unistd.h>, it declares crypt, crypt_r, crypt_rn, and crypt_ra in <crypt.h> instead. ๐Ÿ’…

on a minority of systems (notably recent versions of Solaris), crypt uses a thread-specific static storage buffer, which makes it safe to call from multiple threads simultaneously โ€” but it does not stop each call within a thread from overwriting the results of the previous one. if an application needs to call crypt from parallel threads independently, it is strongly advised to allocate thread-specific struct crypt_data objects from within that program itself, and to use those objects with one of the re-entrant interfaces provided through crypt_r, crypt_rn, or crypt_ra. do it, it's the move. ๐Ÿงš


๐Ÿ“ BUGS ๐Ÿ“

some implementations of crypt, when they hit an error, return an invalid hash that's stored in a read-only location or only initialized once โ€” which means it is only safe to erase the buffer pointed to by the crypt return value if an error did not occur. ๐Ÿ’€

struct crypt_data can be quite large (32kB in this implementation of libcrypt; over 128kB in some other implementations). that is large enough that it may be unwise to allocate it on the stack. ๐Ÿ˜ญ

some recently designed hashing methods need even more scratch memory, but the crypt_r interface makes it impossible to change the size of struct crypt_data without breaking binary compatibility. the crypt_rn interface could accommodate larger allocations for specific hashing methods, but the caller of crypt_rn has no way of knowing how much memory to allocate. crypt_ra does the allocation itself, but it can only make a single call to malloc(3). ๐Ÿ™ˆ


okay that's part one bestie, don't go anywhere โ€” there's more coming ๐Ÿ’–

โœจ ATTRIBUTES โœจ

ok so if you're wondering what all those words even mean, bestie, go peek at attributes(7) ๐Ÿ’–

Here's the tea on thread safety, laid out flat since we're not doing tables in here:

  • crypt โ€” Thread safety: MT-Safe* ๐Ÿซถ
  • crypt_r, crypt_rn, crypt_ra โ€” Thread safety: MT-Safe ๐Ÿ‘‘
* In this implementation. Do NOT rely on this to be portable behavior. Go read the โ€œPORTABILITY NOTESโ€ section, fr fr. ๐Ÿ’€

๐ŸŒธ HISTORY ๐ŸŒธ

storytime, gather round ๐ŸŽ€

A rotor-based crypt function showed up way back in Version 6 AT&T UNIX. iconic behavior, honestly.

Then the โ€œtraditionalโ€ DES-based crypt made its debut in Version 7 AT&T UNIX. a glow-up era โœจ

crypt_r? That one originates with the GNU C Library. There's also a crypt_r function hanging around on HP-UX and MKS Toolkit, but heads up โ€” the prototypes and semantics differ. so like, same name, different vibe, do not get it twisted ๐Ÿ™ˆ

crypt_rn and crypt_ra originate with the Openwall project. slay ๐Ÿ’…


๐Ÿ‘‘ SEE ALSO ๐Ÿ‘‘

go read these next, no cap:


and that's the whole page, bestie!! ๐Ÿฆ‹ you made it to the end, which is honestly main character energy ๐ŸŒˆ

this one comes to you from the Openwall Project, dated October 11, 2017, and yeah it's the CRYPT(3) page ๐Ÿ’œ go forth and hash responsibly, mwah ๐Ÿ’‹

๐Ÿ“ฆ where this page comes from
  • fedora 43: libxcrypt-devel 4.5.2-1.fc43 ยท LGPL-2.1-or-later AND BSD-3-Clause AND BSD-2-Clause AND BSD-2-Clause-FreeBSD AND 0BSD AND CC0-1.0 AND LicenseRef-Fedora-Public-Domain
  • fedora 43: libxcrypt-devel 4.5.2-1.fc43 ยท LGPL-2.1-or-later AND BSD-3-Clause AND BSD-2-Clause AND BSD-2-Clause-FreeBSD AND 0BSD AND CC0-1.0 AND LicenseRef-Fedora-Public-Domain
  • fedora 44: libxcrypt-devel 4.5.2-3.fc44 ยท LGPL-2.1-or-later AND BSD-3-Clause AND BSD-2-Clause AND BSD-2-Clause-FreeBSD AND 0BSD AND CC0-1.0 AND LicenseRef-Fedora-Public-Domain
  • fedora 44: libxcrypt-devel 4.5.2-3.fc44 ยท LGPL-2.1-or-later AND BSD-3-Clause AND BSD-2-Clause AND BSD-2-Clause-FreeBSD AND 0BSD AND CC0-1.0 AND LicenseRef-Fedora-Public-Domain
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms