diff --git a/Cargo.lock b/Cargo.lock
index d6ce4d0a6824f189d235de3dcc85e77eb4041263..6b87192615ebc40417f6ce7ade2c515238489f79 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,12 +8,54 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bcrypt-pbkdf"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2"
+dependencies = [
+ "blowfish",
+ "pbkdf2",
+ "sha2",
+]
+
 [[package]]
 name = "bitflags"
 version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "blowfish"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
+dependencies = [
+ "byteorder",
+ "cipher",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
 [[package]]
 name = "cbitset"
 version = "0.2.0"
@@ -32,6 +74,31 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "crt0"
 version = "0.1.0"
@@ -44,10 +111,41 @@ version = "0.1.0"
 name = "crtn"
 version = "0.1.0"
 
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
 [[package]]
 name = "dlmalloc"
 version = "0.2.4"
 
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
 [[package]]
 name = "goblin"
 version = "0.7.1"
@@ -59,6 +157,24 @@ dependencies = [
  "scroll",
 ]
 
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 name = "ld_so"
 version = "0.1.0"
@@ -75,6 +191,16 @@ version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
 [[package]]
 name = "memchr"
 version = "2.7.1"
@@ -99,6 +225,28 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core 0.6.4",
+ "subtle",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+ "sha2",
+]
+
 [[package]]
 name = "plain"
 version = "0.2.3"
@@ -140,8 +288,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 dependencies = [
  "rand_chacha",
- "rand_core",
+ "rand_core 0.5.1",
  "rand_hc",
+ "rand_pcg",
 ]
 
 [[package]]
@@ -151,7 +300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.5.1",
 ]
 
 [[package]]
@@ -160,13 +309,19 @@ version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
 [[package]]
 name = "rand_hc"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
 dependencies = [
- "rand_core",
+ "rand_core 0.5.1",
 ]
 
 [[package]]
@@ -176,17 +331,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a404fd88e0f817fc1c3351b9ba2207ffa65038cdde464405308a5f5d254835fe"
 dependencies = [
  "libc",
- "rand_core",
+ "rand_core 0.5.1",
  "winapi",
 ]
 
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
 [[package]]
 name = "rand_xorshift"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
 dependencies = [
- "rand_core",
+ "rand_core 0.5.1",
 ]
 
 [[package]]
@@ -216,14 +380,18 @@ dependencies = [
 name = "relibc"
 version = "0.2.5"
 dependencies = [
+ "base64ct",
+ "bcrypt-pbkdf",
  "bitflags",
  "cbitset",
  "cc",
  "dlmalloc",
  "goblin",
  "libc",
+ "md-5",
  "memchr",
  "memoffset",
+ "pbkdf2",
  "plain",
  "posix-regex",
  "rand",
@@ -233,9 +401,21 @@ dependencies = [
  "redox-path",
  "redox_syscall",
  "sc",
+ "scrypt",
+ "sha-crypt",
+ "sha2",
  "unicode-width",
 ]
 
+[[package]]
+name = "salsa20"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
+dependencies = [
+ "cipher",
+]
+
 [[package]]
 name = "sc"
 version = "0.2.7"
@@ -262,6 +442,45 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "scrypt"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
+dependencies = [
+ "password-hash",
+ "pbkdf2",
+ "salsa20",
+ "sha2",
+]
+
+[[package]]
+name = "sha-crypt"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88e79009728d8311d42d754f2f319a975f9e38f156fd5e422d2451486c78b286"
+dependencies = [
+ "base64ct",
+ "sha2",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
 [[package]]
 name = "syn"
 version = "2.0.48"
@@ -273,6 +492,12 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
@@ -285,6 +510,12 @@ version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
diff --git a/Cargo.toml b/Cargo.toml
index 3e8e98e16c9309e16349669c1a71b9ee5760645f..4e029d5d457f29066980ffab790f5780d19f7e02 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ memoffset = "0.9"
 posix-regex = { path = "posix-regex", features = ["no_std"] }
 
 # TODO: For some reason, rand_jitter hasn't been updated to use the latest rand_core
-rand = { version = "0.7", default-features = false }
+rand = { version = "0.7", default-features = false, features = ["small_rng"] }
 rand_xorshift = "0.2"
 rand_jitter = "0.3"
 
@@ -36,6 +36,13 @@ memchr = { version = "2.2.0", default-features = false }
 plain = "0.2"
 unicode-width = "0.1"
 __libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true }
+md5-crypto = { package = "md-5", version = "0.10.6", default-features = false}
+sha-crypt = { version = "0.5", default-features = false }
+base64ct = { version = "1.6", default-features = false, features = ["alloc"] }
+bcrypt-pbkdf = { version = "0.10", default-features = false, features = ["alloc"] }
+scrypt = { version = "0.11", default-features = false, features = ["simple"]}
+pbkdf2 = { version = "0.12", features = ["sha2"]}
+sha2 = { version = "0.10", default-features = false }
 
 [dependencies.goblin]
 version = "0.7"
diff --git a/src/header/crypt/blowfish.rs b/src/header/crypt/blowfish.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3d24325d90882a7d642fc68d2c20554594f549db
--- /dev/null
+++ b/src/header/crypt/blowfish.rs
@@ -0,0 +1,90 @@
+use crate::platform::types::{c_uchar, c_uint};
+use alloc::{string::String, vec::Vec};
+use base64ct::{Base64Bcrypt, Encoding};
+use bcrypt_pbkdf::bcrypt_pbkdf;
+use core::str;
+
+const MIN_COST: u32 = 4;
+const MAX_COST: u32 = 31;
+const BHASH_WORDS: usize = 8;
+const BHASH_OUTPUT_SIZE: usize = BHASH_WORDS * 4;
+
+/// Inspired by https://github.com/Keats/rust-bcrypt/blob/87fc59e917bcb6cf3f3752fc7f2b4c659d415597/src/lib.rs#L135
+fn split_with_prefix(hash: &str) -> Option<(&str, &str, c_uint)> {
+    let valid_prefixes = ["2y", "2b", "2a", "2x"];
+
+    // Should be [prefix, cost, hash]
+    let raw_parts: Vec<_> = hash.split('$').skip(1).collect();
+    if raw_parts.len() != 3 {
+        return None;
+    }
+
+    let prefix = raw_parts[0];
+    let setting = raw_parts[2];
+
+    if !valid_prefixes.contains(&prefix) {
+        return None;
+    }
+
+    raw_parts[1]
+        .parse::<c_uint>()
+        .ok()
+        .map(|cost| (prefix, setting, cost))
+}
+
+/// Performs Blowfish key derivation on a given password with a specific setting.
+///
+/// # Parameters
+/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
+/// * `setting`: The settings for the Blowfish key derivation. It must be a string slice (`&str`)
+///   and should follow the format `$<prefix>$<cost>$<setting>`, where `<prefix>` is a string that
+///   indicates the type of the hash (e.g., "$2a$"), `<cost>` is a decimal number representing
+///   the cost factor for the Blowfish operation, and `<setting>` is a base64-encoded string
+///   representing the salt to be used for the Blowfish function.
+///
+/// # Returns
+/// * `Option<String>`: Returns `Some(String)` if the Blowfish operation was successful, where the
+///   returned string is the result of the Blowfish operation formatted according to the Modular
+///   Crypt Format (MCF). If the Blowfish operation failed, it returns `None`.
+///
+/// # Errors
+/// * If the cost factor is outside the range `[MIN_COST, MAX_COST]`.
+///
+/// # Example
+/// ```
+/// let password = "correctbatteryhorsestapler";
+/// let setting = "$2y$12$L6Bc/AlTQHyd9liGgGEZyO";
+/// let result = crypt_blowfish(password, setting);
+/// assert!(result.is_some());
+///```
+///
+/// # Note
+/// The `crypt_blowfish` function uses the Blowfish block cipher for hashing.
+/// The output of the Blowfish operation is base64-encoded using the BCrypt variant of base64.
+pub fn crypt_blowfish(passw: &str, setting: &str) -> Option<String> {
+    if let Some((prefix, setting, cost)) = split_with_prefix(setting) {
+        if !(MIN_COST..=MAX_COST).contains(&cost) {
+            return None;
+        }
+        // Passwords need to be null terminated
+        let mut vec = Vec::with_capacity(passw.len() + 1);
+        vec.extend_from_slice(passw.as_bytes());
+        vec.push(0);
+
+        // We only consider the first 72 chars; truncate if necessary.
+        let passw_t = if vec.len() > 72 { &vec[..72] } else { &vec };
+        let passw: &[c_uchar] = &passw_t;
+        let setting = setting.as_bytes();
+        let mut output = vec![0; BHASH_OUTPUT_SIZE + 1];
+
+        bcrypt_pbkdf(passw, setting, cost, &mut output).ok()?;
+        Some(format!(
+            "${}${}${}",
+            prefix,
+            cost,
+            Base64Bcrypt::encode_string(&output),
+        ))
+    } else {
+        None
+    }
+}
diff --git a/src/header/crypt/cbindgen.toml b/src/header/crypt/cbindgen.toml
new file mode 100644
index 0000000000000000000000000000000000000000..bdcf5ebda142d88bf5b02188808d43672ed74ef1
--- /dev/null
+++ b/src/header/crypt/cbindgen.toml
@@ -0,0 +1,9 @@
+include_guard = "_RELIBC_CRYPT_H"
+language = "C"
+style = "Type"
+no_includes = true
+cpp_compat = true
+
+
+[enum]
+prefix_with_name = true
\ No newline at end of file
diff --git a/src/header/crypt/md5.rs b/src/header/crypt/md5.rs
new file mode 100644
index 0000000000000000000000000000000000000000..25cfacff37c2f5f1bcfd059bd7add81b0628bd23
--- /dev/null
+++ b/src/header/crypt/md5.rs
@@ -0,0 +1,146 @@
+use crate::platform::types::c_uchar;
+use alloc::string::String;
+use base64ct::{Base64ShaCrypt, Encoding};
+use core::str;
+use md5_crypto::{Digest, Md5};
+
+// Block size for MD5
+const BLOCK_SIZE: usize = 16;
+// PWD part length of the password string
+const PW_SIZE_MD5: usize = 22;
+// Maximum length of a setting
+const SALT_MAX: usize = 8;
+// Inverse encoding map for MD5.
+const MAP_MD5: [c_uchar; BLOCK_SIZE] = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11];
+
+const KEY_MAX: usize = 30000;
+
+fn encode_md5(source: &[c_uchar]) -> Option<[c_uchar; PW_SIZE_MD5]> {
+    let mut transposed = [0; BLOCK_SIZE];
+    for (i, &ti) in MAP_MD5.iter().enumerate() {
+        transposed[i] = source[ti as usize];
+    }
+    let mut buf = [0; PW_SIZE_MD5];
+    Base64ShaCrypt::encode(&transposed, &mut buf).ok()?;
+    Some(buf)
+}
+
+/// Function taken from PR: https://github.com/RustCrypto/password-hashes/pull/351
+/// This won't be needed once the PR is merged
+fn inner_md5(passw: &str, setting: &str) -> Option<String> {
+    let mut digest_b = Md5::default();
+    digest_b.update(passw);
+    digest_b.update(setting);
+    digest_b.update(passw);
+    let hash_b = digest_b.finalize();
+
+    let mut digest_a = Md5::default();
+    digest_a.update(passw);
+    digest_a.update("$1$");
+    digest_a.update(setting);
+
+    let mut pw_len = passw.len();
+    let rounds = pw_len / BLOCK_SIZE;
+    for _ in 0..rounds {
+        digest_a.update(hash_b);
+    }
+
+    // leftover passw
+    digest_a.update(&hash_b[..(pw_len - rounds * BLOCK_SIZE)]);
+
+    while pw_len > 0 {
+        match pw_len & 1 {
+            0 => digest_a.update(&passw[..1]),
+            1 => digest_a.update([0u8]),
+            _ => unreachable!(),
+        }
+        pw_len >>= 1;
+    }
+
+    let mut hash_a = digest_a.finalize();
+
+    // Repeatedly run the collected hash value through MD5 to burn
+    // CPU cycles
+    for i in 0..1000_usize {
+        // new hasher
+        let mut hasher = Md5::default();
+
+        // Add key or last result
+        if (i & 1) != 0 {
+            hasher.update(passw);
+        } else {
+            hasher.update(hash_a);
+        }
+
+        // Add setting for numbers not divisible by 3
+        if i % 3 != 0 {
+            hasher.update(setting);
+        }
+
+        // Add key for numbers not divisible by 7
+        if i % 7 != 0 {
+            hasher.update(passw);
+        }
+
+        // Add key or last result
+        if (i & 1) != 0 {
+            hasher.update(hash_a);
+        } else {
+            hasher.update(passw);
+        }
+
+        // digest_c.clone_from_slice(&hasher.finalize());
+        hash_a = hasher.finalize();
+    }
+    encode_md5(hash_a.as_slice())
+        .map(|encstr| format!("$1${}${}", setting, str::from_utf8(&encstr).unwrap()))
+}
+
+/// Performs MD5 hashing on a given password with a specific setting.
+///
+/// # Parameters
+/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
+/// * `setting`: The settings for the MD5 hashing. It must be a string slice (`&str`)
+///   and should start with "$1$". The rest of the string should represent the salt
+///   to be used for the MD5 hashing.
+///
+/// # Returns
+/// * `Option<String>`: Returns `Some(String)` if the MD5 operation was successful, where the
+///   returned string is the result of the MD5 operation formatted according to the Modular
+///   Crypt Format (MCF). If the MD5 operation failed, it returns `None`.
+///
+/// # Errors
+/// * If the `passw` length exceeds `KEY_MAX`.
+/// * If the `setting` does not start with "$1$".
+///
+/// # Example
+/// ```
+/// let password = "my_password";
+/// let setting = "$1$saltstring";
+/// let result = crypt_md5(password, setting);
+/// assert!(result.is_some());
+/// ```
+///
+/// # Note
+/// The `crypt_md5` function uses the MD5 hashing algorithm for hashing.
+/// The output of the MD5 operation is base64-encoded using the BCrypt variant of base64.
+pub fn crypt_md5(passw: &str, setting: &str) -> Option<String> {
+    /* reject large keys */
+    if passw.len() > KEY_MAX {
+        return None;
+    }
+
+    if &setting[0..3] != "$1$" {
+        return None;
+    }
+
+    let cursor = 3;
+    let slen = cursor
+        + setting[cursor..cursor + SALT_MAX]
+            .chars()
+            .take_while(|c| *c != '$')
+            .count();
+    let setting = &setting[cursor..slen];
+
+    inner_md5(passw, setting)
+}
diff --git a/src/header/crypt/mod.rs b/src/header/crypt/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aa5cfac0cdbccb64334f43c7c4501603aaa9e1fb
--- /dev/null
+++ b/src/header/crypt/mod.rs
@@ -0,0 +1,101 @@
+use ::scrypt::password_hash::{Salt, SaltString};
+use alloc::{
+    ffi::CString,
+    string::{String, ToString},
+};
+use core::ptr;
+use rand::{rngs::SmallRng, RngCore, SeedableRng};
+
+use crate::{
+    c_str::CStr,
+    header::{errno::EINVAL, stdlib::rand},
+    platform::{self, types::*},
+};
+
+mod blowfish;
+mod md5;
+mod pbkdf2;
+mod scrypt;
+mod sha;
+
+use self::{
+    blowfish::crypt_blowfish,
+    md5::crypt_md5,
+    pbkdf2::crypt_pbkdf2,
+    scrypt::crypt_scrypt,
+    sha::{crypt_sha, ShaType::*},
+};
+
+#[repr(C)]
+pub struct crypt_data {
+    initialized: c_int,
+    buff: [c_char; 256],
+}
+
+impl crypt_data {
+    pub fn new() -> Self {
+        crypt_data {
+            initialized: 1,
+            buff: [0; 256],
+        }
+    }
+}
+
+fn gen_salt() -> Option<String> {
+    let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 });
+    let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
+    rng.fill_bytes(&mut bytes);
+    Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn crypt_r(
+    key: *const c_char,
+    setting: *const c_char,
+    data: *mut crypt_data,
+) -> *mut c_char {
+    if (*data).initialized == 0 {
+        *data = crypt_data::new();
+    }
+
+    let key = CStr::from_ptr(key).to_str().expect("key must be utf-8");
+    let setting = CStr::from_ptr(setting)
+        .to_str()
+        .expect("setting must be utf-8");
+    let setting_bytes = setting.as_bytes();
+
+    let encoded = if setting_bytes[0] == b'$' && setting_bytes[1] != 0 && setting_bytes[2] != 0 {
+        if setting_bytes[1] == b'1' && setting_bytes[2] == b'$' {
+            crypt_md5(key, setting)
+        } else if setting_bytes[1] == b'2' && setting_bytes[3] == b'$' {
+            crypt_blowfish(key, setting)
+        } else if setting_bytes[1] == b'5' && setting_bytes[2] == b'$' {
+            crypt_sha(key, setting, Sha256)
+        } else if setting_bytes[1] == b'6' && setting_bytes[2] == b'$' {
+            crypt_sha(key, setting, Sha512)
+        } else if setting_bytes[1] == b'7' && setting_bytes[2] == b'$' {
+            crypt_scrypt(key, setting)
+        } else if setting_bytes[1] == b'8' && setting_bytes[2] == b'$' {
+            crypt_pbkdf2(key, setting)
+        } else {
+            platform::errno = EINVAL;
+            return ptr::null_mut();
+        }
+    } else {
+        None
+    };
+
+    if let Some(inner) = encoded {
+        let len = inner.len();
+        if let Ok(ret) = CString::new(inner) {
+            let ret_ptr = ret.into_raw();
+            let dst = (*data).buff.as_mut_ptr();
+            ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len);
+            ret_ptr.cast()
+        } else {
+            ptr::null_mut()
+        }
+    } else {
+        ptr::null_mut()
+    }
+}
diff --git a/src/header/crypt/pbkdf2.rs b/src/header/crypt/pbkdf2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..43e0e24d536e02c9a301ef570a1ead4e3badf88c
--- /dev/null
+++ b/src/header/crypt/pbkdf2.rs
@@ -0,0 +1,64 @@
+use super::gen_salt;
+use alloc::string::{String, ToString};
+use base64ct::{Base64Bcrypt, Encoding};
+use core::{str, u32};
+use pbkdf2::pbkdf2_hmac;
+use sha2::Sha256;
+
+/// Performs PBKDF2 key derivation on a given password with a specific setting.
+///
+/// # Parameters
+/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
+/// * `setting`: The settings for the PBKDF2 key derivation. It must be a string slice (`&str`)
+///   and should follow the format `$<iter>$<salt>`. The `<iter>` part should be a hexadecimal
+///   number representing the iteration count for the PBKDF2 function. The `<salt>` part should
+///   be a base64-encoded string representing the salt to be used for the PBKDF2 function.
+///
+/// # Returns
+/// * `Option<String>`: Returns `Some(String)` if the PBKDF2 operation was successful, where the
+///   returned string is the result of the PBKDF2 operation formatted according to the Modular
+///   Crypt Format (MCF). If the PBKDF2 operation failed, it returns `None`.
+///
+/// # Errors
+/// * If the `setting` does not contain a '$' character.
+/// * If the `setting` contains another '$' character after the first one.
+/// * If the `<salt>` part of the `setting` is empty.
+/// * If the `<iter>` part of the `setting` cannot be converted into a `u32` integer.
+///
+/// # Example
+/// ```
+/// let password = "my_password";
+/// let setting = "$8$3e8$salt";
+/// let result = crypt_pbkdf2(password, setting);
+/// assert!(result.is_some());
+/// ```
+///
+/// # Note
+/// The `crypt_pbkdf2` function uses the SHA256 hashing algorithm for the PBKDF2 operation.
+/// The output of the PBKDF2 operation is base64-encoded using the BCrypt variant of base64.
+pub fn crypt_pbkdf2(passw: &str, setting: &str) -> Option<String> {
+    if let Some((iter_str, salt)) = &setting[3..].split_once('$') {
+        if salt.contains('$') {
+            return None;
+        }
+
+        let actual_salt = if salt.len() > 0 {
+            salt.to_string()
+        } else {
+            gen_salt()?
+        };
+
+        let iter = u32::from_str_radix(iter_str, 16).ok()?;
+        let mut buffer = [0u8; 32];
+        pbkdf2_hmac::<Sha256>(passw.as_bytes(), actual_salt.as_bytes(), iter, &mut buffer);
+
+        Some(format!(
+            "$8${}${}${}",
+            iter_str,
+            salt,
+            Base64Bcrypt::encode_string(&buffer)
+        ))
+    } else {
+        None
+    }
+}
diff --git a/src/header/crypt/scrypt.rs b/src/header/crypt/scrypt.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e18df8a23416b71a5ecb55625519e6b5dd95c239
--- /dev/null
+++ b/src/header/crypt/scrypt.rs
@@ -0,0 +1,115 @@
+use super::gen_salt;
+use crate::platform::types::*;
+use alloc::string::{String, ToString};
+use base64ct::{Base64Bcrypt, Encoding};
+use core::{str, u32};
+use scrypt::{scrypt, Params};
+
+/// Map for encoding and decoding
+#[inline(always)]
+fn to_digit(c: char, radix: u32) -> Option<u32> {
+    match c {
+        '.' => Some(0),
+        '/' => Some(1),
+        _ => c.to_digit(radix).map(|d| d + 2),
+    }
+}
+
+/// Decodes a 5 character lengt str value to c_uint
+///
+/// # Arguments
+///
+/// * `value` - A string slice that represents a u32 value in base64
+///
+/// # Returns
+///
+/// * `Option<c_uint>` - Returns the decoded c_uint value if successful, otherwise None
+fn dencode_uint(value: &str) -> Option<c_uint> {
+    if value.len() != 5 {
+        return None;
+    }
+
+    let result = value
+        .chars()
+        .enumerate()
+        .try_fold(0 as c_uint, |acc, (i, c)| {
+            acc.checked_add((to_digit(c, 30)? as c_uint) << (i * 6))
+        });
+
+    Some(result?)
+}
+
+/// Reads settings for password encryption
+///
+/// # Arguments
+///
+/// * `setting` - A string slice that represents the settings
+///
+/// # Returns
+///
+/// * `Option<(c_uchar, c_uint, c_uint, String)>` - Returns a tuple containing the settings if successful, otherwise None
+fn read_setting(setting: &str) -> Option<(c_uchar, c_uint, c_uint, String)> {
+    let nlog2 = to_digit(setting.chars().next()?, 30)? as c_uchar;
+    let r = dencode_uint(&setting[1..6])?;
+    let p = dencode_uint(&setting[6..11])?;
+
+    let salt = &setting[11..];
+    let actual_salt = if salt.len() > 0 {
+        salt.to_string()
+    } else {
+        gen_salt()?
+    };
+
+    Some((nlog2, r, p, actual_salt))
+}
+
+/// Performs Scrypt key derivation on a given password with a specific setting.
+///
+/// # Parameters
+/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
+/// * `setting`: The settings for the Scrypt key derivation. It must be a string slice (`&str`)
+///   and should follow the format `$<Nlog2>$<r>$<p>$<salt>`. The `<Nlog2>` part should be a decimal
+///   number representing the logarithm base 2 of the CPU/memory cost factor N for Scrypt. The `<r>`
+///   part should be a decimal number representing the block size r. The `<p>` part should be a decimal
+///   number representing the parallelization factor p. The `<salt>` part should be a base64-encoded
+///   string representing the salt to be used for the Scrypt function.
+///
+/// # Returns
+/// * `Option<String>`: Returns `Some(String)` if the Scrypt operation was successful, where the
+///   returned string is the result of the Scrypt operation formatted according to the Modular
+///   Crypt Format (MCF). If the Scrypt operation failed, it returns `None`.
+///
+/// # Errors
+/// * If the `setting` length is less than 14 characters.
+/// * If the `scrypt` function fails to perform the Scrypt operation.
+///
+/// # Example
+/// ```
+/// let password = "my_password";
+/// let setting = "$7$C6..../....SodiumChloride";
+/// let result = crypt_scrypt(password, setting);
+/// assert!(result.is_some());
+/// ```
+///
+/// # Note
+/// The `crypt_scrypt` function uses the Scrypt key derivation function for hashing.
+/// The output of the Scrypt operation is base64-encoded using the BCrypt variant of base64.
+pub fn crypt_scrypt(passw: &str, setting: &str) -> Option<String> {
+    if setting.len() < 14 {
+        return None;
+    }
+
+    let (nlog2, r, p, salt) = read_setting(&setting[3..])?;
+
+    let params = Params::new(nlog2, r, p, 32).ok()?;
+    let mut output = [0u8; 32];
+
+    scrypt(passw.as_bytes(), salt.as_bytes(), &params, &mut output).ok()?;
+
+    Some(format!(
+        "$7${}${}${}",
+        &setting[3..14],
+        salt,
+        Base64Bcrypt::encode_string(&output)
+    ))
+}
diff --git a/src/header/crypt/sha.rs b/src/header/crypt/sha.rs
new file mode 100644
index 0000000000000000000000000000000000000000..366ccc87fc35e413c32ab7e5269f199a57607698
--- /dev/null
+++ b/src/header/crypt/sha.rs
@@ -0,0 +1,141 @@
+use alloc::string::{String, ToString};
+
+use sha_crypt::{
+    sha256_crypt_b64, sha512_crypt_b64, Sha256Params, Sha512Params, ROUNDS_DEFAULT, ROUNDS_MAX,
+    ROUNDS_MIN,
+};
+
+use crate::platform::types::*;
+
+// key limit is not part of the original design, added for DoS protection.
+// rounds limit has been lowered (versus the reference/spec), also for DoS
+// protection. runtime is O(klen^2 + klen*rounds)
+const KEY_MAX: usize = 256;
+const SALT_MAX: usize = 16;
+const RSTRING: &str = "rounds=";
+
+pub enum ShaType {
+    Sha256,
+    Sha512,
+}
+
+/// Performs SHA hashing on a given password with a specific setting.
+///
+/// # Parameters
+/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
+/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`)
+///   and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt
+///   to be used for the SHA hashing.
+/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`.
+///
+/// # Returns
+/// * `Option<String>`: Returns `Some(String)` if the SHA operation was successful, where the
+///   returned string is the result of the SHA operation formatted according to the Modular
+///   Crypt Format (MCF). If the SHA operation failed, it returns `None`.
+///
+/// # Errors
+/// * If the `passw` length exceeds `KEY_MAX`.
+/// * If the `setting` does not start with "$5$" or "$6$".
+/// * If the `setting` does not contain a '$' character.
+/// * If the `setting` contains another '$' character after the first one.
+/// * If the `setting` contains invalid characters.
+/// * If the `setting` contains an invalid number of rounds.
+/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password.
+///
+/// # Example
+/// ```
+/// let password = "my_password";
+/// let setting = "$5$rounds=1400$anotherlongsaltstringg";
+/// let result = crypt_sha(password, setting, ShaType::Sha256);
+/// assert!(result.is_some());
+/// ```
+///
+/// # Note
+/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing.
+/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64.
+pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option<String> {
+    let mut cursor = 3;
+    let rounds;
+
+    /* reject large keys */
+    if passw.len() > KEY_MAX {
+        return None;
+    }
+
+    // SHA256
+    // setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional)
+    // SHA512
+    // setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional)
+    let param = match cipher {
+        ShaType::Sha256 => "$5$",
+        ShaType::Sha512 => "$6$",
+    };
+
+    if &setting[0..3] != param {
+        return None;
+    }
+
+    let has_round;
+    // 7 is len("rounds=")
+    if &setting[cursor..cursor + 7] == RSTRING {
+        cursor += 7;
+        has_round = true;
+        if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') {
+            if let Ok(u) = setting[cursor..cursor + c_end].parse::<c_ulong>() {
+                cursor += c_end + 1;
+                rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong);
+            } else {
+                return None;
+            }
+        } else {
+            return None;
+        }
+    } else {
+        has_round = false;
+        rounds = ROUNDS_DEFAULT as c_ulong;
+    }
+
+    let mut slen = cursor;
+
+    for i in 0..SALT_MAX.min(setting.len() - cursor) {
+        let idx = cursor + i;
+
+        if &setting[idx..idx + 1] == "$" {
+            break;
+        }
+
+        // reject characters that interfere with /etc/shadow parsing
+        if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" {
+            return None;
+        }
+        slen += 1;
+    }
+
+    let setting = &setting[cursor..slen];
+
+    if let Ok(enc) = match cipher {
+        ShaType::Sha256 => {
+            let params = Sha256Params::new(rounds as usize)
+                .unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap());
+            sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
+        }
+        ShaType::Sha512 => {
+            let params = Sha512Params::new(rounds as usize)
+                .unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap());
+            sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
+        }
+    } {
+        let (r_slice, rn_slice) = if has_round {
+            (RSTRING, rounds.to_string() + "$")
+        } else {
+            ("", String::new())
+        };
+
+        Some(format!(
+            "{}{}{}{}${}",
+            param, r_slice, rn_slice, setting, enc
+        ))
+    } else {
+        None
+    }
+}
diff --git a/src/header/mod.rs b/src/header/mod.rs
index 14ff2f56b9b4eb13dc4babe197424e70dbad0b07..c820e41a4843b648d20b4e607285b911dad0e749 100644
--- a/src/header/mod.rs
+++ b/src/header/mod.rs
@@ -4,6 +4,7 @@ pub mod arpa_inet;
 pub mod assert;
 pub mod bits_pthread;
 pub mod bits_sched;
+pub mod crypt;
 pub mod ctype;
 pub mod dirent;
 #[path = "dl-tls/mod.rs"]
diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs
index 1cb6236a3488af76b60b1a4c9cea5270c12c9a84..06ec98966471a98092ac99aa8b1dc363a7d77d76 100644
--- a/src/header/unistd/mod.rs
+++ b/src/header/unistd/mod.rs
@@ -10,8 +10,11 @@ use core::{
 use crate::{
     c_str::CStr,
     header::{
-        errno, fcntl, limits, stdlib::getenv, sys_ioctl, sys_resource, sys_time, sys_utsname,
-        termios, time::timespec,
+        crypt::{crypt_data, crypt_r},
+        errno, fcntl, limits,
+        stdlib::getenv,
+        sys_ioctl, sys_resource, sys_time, sys_utsname, termios,
+        time::timespec,
     },
     platform::{self, types::*, Pal, Sys},
 };
@@ -119,9 +122,10 @@ pub extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t
     unimplemented!();
 }
 
-// #[no_mangle]
-pub extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char {
+    let mut data = crypt_data::new();
+    crypt_r(key, salt, &mut data as *mut _)
 }
 
 #[no_mangle]
diff --git a/tests/Makefile b/tests/Makefile
index 3f6c43b305ba99165043520f9e9de50f5f3ac6bd..21ed06bbb11de3861031383c2cf685487f7eb00c 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -6,6 +6,12 @@ EXPECT_NAMES=\
 	assert \
 	constructor \
 	ctype \
+	crypt/blowfish \
+	crypt/md5 \
+	crypt/pbkdf2 \
+	crypt/scrypt \
+	crypt/sha256 \
+	crypt/sha512 \
 	destructor \
 	dirent/scandir \
 	errno \
diff --git a/tests/crypt/blowfish.c b/tests/crypt/blowfish.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a5b62e34b8bcaa6411b024458bc114c184f067b
--- /dev/null
+++ b/tests/crypt/blowfish.c
@@ -0,0 +1,26 @@
+#include <assert.h>
+#include <crypt.h>
+#include <string.h>
+#include <unistd.h>
+
+int main()
+{
+    char *expected_output = "$2y$12$CJr4uRfziaGp4CWIBk0fB.I2tCOHYe3pomaWbC53/92p";
+    char *result = crypt("correctbatteryhorsestapler", "$2y$12$L6Bc/AlTQHyd9liGgGEZyO");
+    assert(strcmp(result, expected_output) == 0);
+
+    // Invalid salt for Blowfish
+    result = crypt("correctbatteryhorsestapler", "$2t$12$L6Bc/AlTQHyd9liGgGEZyO");
+    assert(result == NULL);
+    
+    expected_output = "$2a$4$IAwt7hxuME3DekssMMTWU.xnJub2Xn45xK/oP.wWt3UC"; 
+    result = crypt("password", "$2a$04$UuTkLRZZ6QofpDOlMz32Mu");
+    assert(strcmp(result, expected_output) == 0);
+    
+    // Invalid salt for Blowfish
+    result = "$2b$10$testtesttesttes";
+    result = crypt("correctbatteryhorsestapler", "$2y$12$L6Bc/AlTQHyd9liGgGEZyO");
+    assert(result != NULL);
+
+    return 0;
+}
diff --git a/tests/crypt/md5.c b/tests/crypt/md5.c
new file mode 100644
index 0000000000000000000000000000000000000000..4251d70f9dbde7b574fc5b6f89aa0a9f7e69c5ff
--- /dev/null
+++ b/tests/crypt/md5.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 1991-2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <crypt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int main () {
+    const char salt[] = "$1$saltstring";
+    char *cp;
+    int result = 0;
+
+    cp = crypt ("Hello world!", salt);
+
+    /* MD5 is disabled in FIPS mode.  */
+    if (cp) {
+        result |= strcmp ("$1$saltstri$YMyguxXMBpd2TEZ.vS/3q1", cp);
+        if (!result)
+            printf("Success!\n");
+    }
+
+    return result;
+}
\ No newline at end of file
diff --git a/tests/crypt/pbkdf2.c b/tests/crypt/pbkdf2.c
new file mode 100644
index 0000000000000000000000000000000000000000..f3e9a3cfbc1a9125c10d3b4a35464c9854635cce
--- /dev/null
+++ b/tests/crypt/pbkdf2.c
@@ -0,0 +1,25 @@
+#include <assert.h>
+#include <crypt.h>
+#include <string.h>
+#include <unistd.h>
+
+int main()
+{
+    char *expected_output = "$8$3e8$salt$ZyTZgT5Pyp4CKdom6q6zHg0QrrAQO4ptWYCpWz4gu16";
+    char *result = crypt("pleaseletmein", "$8$3e8$salt");
+    assert(strcmp(result, expected_output) == 0);
+
+    // No salt
+    result = crypt("pleaseletmein", "$8$3e8$");
+    assert(result != NULL);
+    
+    // Invalid encoded number for rounds
+    result = crypt("pleaseletmein", "$8$$salt");
+    assert(result == NULL);
+    
+    // Invalid encoded number for rounds
+    result = crypt("pleaseletmein", "$8$.$salt");
+    assert(result == NULL);
+
+    return 0;
+}
diff --git a/tests/crypt/scrypt.c b/tests/crypt/scrypt.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6f70778ce1be3d4520148f2fcc3a2da8616aa29
--- /dev/null
+++ b/tests/crypt/scrypt.c
@@ -0,0 +1,25 @@
+#include <assert.h>
+#include <crypt.h>
+#include <string.h>
+#include <unistd.h>
+
+int main()
+{
+    char *expected_output = "$7$C6..../....$SodiumChloride$aAM7wxp7ayfEF.ZLedy2490m85oOR228oZTB7jPbmdG";
+    char *result = crypt("pleaseletmein", "$7$C6..../....SodiumChloride");
+    assert(strcmp(result, expected_output) == 0);
+
+    // No salt
+    result = crypt("pleaseletmein", "$7$C6..../....");
+    assert(result != NULL);
+    
+    // Invalid encoded number for r
+    result = crypt("pleaseletmein", "$7$C6.../....SodiumChloride");
+    assert(result == NULL);
+    
+    // Invalid encoded number for p
+    result = crypt("pleaseletmein", "$7$C6..../...SodiumChloride");
+    assert(result == NULL);
+
+    return 0;
+}
diff --git a/tests/crypt/sha256.c b/tests/crypt/sha256.c
new file mode 100644
index 0000000000000000000000000000000000000000..6e9c3ffa472bd9be6644cf0e5dd107b7ee5ea131
--- /dev/null
+++ b/tests/crypt/sha256.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 1991-2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <crypt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static const struct
+{
+  const char *salt;
+  const char *input;
+  const char *expected;
+} tests[] =
+{
+  { "$5$saltstring", "Hello world!",
+    "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5" },
+  { "$5$rounds=10000$saltstringsaltstring", "Hello world!",
+    "$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2."
+    "opqey6IcA" },
+  { "$5$rounds=5000$toolongsaltstring", "This is just a test",
+    "$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8"
+    "mGRcvxa5" },
+  { "$5$rounds=1400$anotherlongsaltstring",
+    "a very much longer text to encrypt.  This one even stretches over more"
+    "than one line.",
+    "$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12"
+    "oP84Bnq1" },
+  { "$5$rounds=77777$short",
+    "we have a short salt string but not a short password",
+    "$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/" },
+  { "$5$rounds=123456$asaltof16chars..", "a short string",
+    "$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/"
+    "cZKmF/wJvD" },
+  { "$5$rounds=10$roundstoolow", "the minimum number is still observed",
+    "$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97"
+    "2bIC" },
+};
+
+const int ntests = sizeof(tests) / sizeof(tests[0]);
+
+int main (void) {
+  int result = 0;
+
+  for (int i = 0; i < ntests; ++i){
+      char *cp = crypt (tests[i].input, tests[i].salt);
+      if (strcmp (cp, tests[i].expected) != 0) {
+        printf ("test %d: expected \"%s\", got \"%s\"\n", i, tests[i].expected, cp);
+        result = 1;
+      }
+  }
+  return result;
+}
\ No newline at end of file
diff --git a/tests/crypt/sha512.c b/tests/crypt/sha512.c
new file mode 100644
index 0000000000000000000000000000000000000000..efab1e316221621cbcc7364b65eaaaf2d2742c4a
--- /dev/null
+++ b/tests/crypt/sha512.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 1991-2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <crypt.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static const struct
+{
+  const char *salt;
+  const char *input;
+  const char *expected;
+} tests[] =
+{
+  { "$6$saltstring", "Hello world!",
+    "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu"
+    "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" },
+  { "$6$rounds=10000$saltstringsaltstring", "Hello world!",
+    "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb"
+    "HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." },
+  { "$6$rounds=5000$toolongsaltstring", "This is just a test",
+    "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ"
+    "zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" },
+  { "$6$rounds=1400$anotherlongsaltstring",
+    "a very much longer text to encrypt.  This one even stretches over more"
+    "than one line.",
+    "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP"
+    "vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" },
+  { "$6$rounds=77777$short",
+    "we have a short salt string but not a short password",
+    "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g"
+    "ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0" },
+  { "$6$rounds=123456$asaltof16chars..", "a short string",
+    "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc"
+    "elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" },
+  { "$6$rounds=10$roundstoolow", "the minimum number is still observed",
+    "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x"
+    "hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." },
+};
+
+const int ntests = sizeof(tests) / sizeof(tests[0]);
+
+int main(void) {
+  int result = 0;
+  int i;
+
+  for (i = 0; i < ntests; ++i) {
+    char * cp = crypt(tests[i].input, tests[i].salt);
+
+    if (strcmp(cp, tests[i].expected) != 0) {
+      printf("test %d: expected \"%s\", got \"%s\"\n", i, tests[i].expected, cp);
+      result = 1;
+    }
+  }
+
+  return result;
+}
\ No newline at end of file
diff --git a/tests/expected/bins_static/crypt/blowfish.stderr b/tests/expected/bins_static/crypt/blowfish.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/blowfish.stdout b/tests/expected/bins_static/crypt/blowfish.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/md5.stderr b/tests/expected/bins_static/crypt/md5.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/md5.stdout b/tests/expected/bins_static/crypt/md5.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..f985b46aff02b2ec96fac19c214629e5a3dc7468
--- /dev/null
+++ b/tests/expected/bins_static/crypt/md5.stdout
@@ -0,0 +1 @@
+Success!
diff --git a/tests/expected/bins_static/crypt/pbkdf2.stderr b/tests/expected/bins_static/crypt/pbkdf2.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/pbkdf2.stdout b/tests/expected/bins_static/crypt/pbkdf2.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/scrypt.stderr b/tests/expected/bins_static/crypt/scrypt.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/scrypt.stdout b/tests/expected/bins_static/crypt/scrypt.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/sha256.stderr b/tests/expected/bins_static/crypt/sha256.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/sha256.stdout b/tests/expected/bins_static/crypt/sha256.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/sha512.stderr b/tests/expected/bins_static/crypt/sha512.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/expected/bins_static/crypt/sha512.stdout b/tests/expected/bins_static/crypt/sha512.stdout
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391