relibc/header/crypt/
mod.rs1use ::scrypt::password_hash::{Salt, SaltString};
6use alloc::{
7 ffi::CString,
8 string::{String, ToString},
9};
10use core::ptr;
11use rand::{Rng, SeedableRng, rngs::SmallRng};
12
13use crate::{
14 c_str::CStr,
15 header::{errno::EINVAL, stdlib::rand},
16 platform::{
17 self,
18 types::{c_char, c_int},
19 },
20};
21
22mod argon2;
23mod blowfish;
24mod md5;
25mod pbkdf2;
26mod scrypt;
27mod sha;
28
29use self::{
30 argon2::crypt_argon2,
31 blowfish::crypt_blowfish,
32 md5::crypt_md5,
33 pbkdf2::crypt_pbkdf2,
34 scrypt::crypt_scrypt,
35 sha::{
36 ShaType::{Sha256, Sha512},
37 crypt_sha,
38 },
39};
40
41#[repr(C)]
43pub struct crypt_data {
44 initialized: c_int,
45 buff: [c_char; 256],
46}
47
48impl crypt_data {
49 #[allow(clippy::new_without_default)]
50 pub fn new() -> Self {
51 crypt_data {
52 initialized: 1,
53 buff: [0; 256],
54 }
55 }
56}
57
58fn gen_salt() -> Option<String> {
59 let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 });
60 let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
61 rng.fill_bytes(&mut bytes);
62 Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string())
63}
64
65#[unsafe(no_mangle)]
67pub unsafe extern "C" fn crypt_r(
68 key: *const c_char,
69 setting: *const c_char,
70 data: *mut crypt_data,
71) -> *mut c_char {
72 if unsafe { (*data).initialized } == 0 {
73 unsafe { *data = crypt_data::new() };
74 }
75
76 let key = unsafe { CStr::from_ptr(key) }.to_bytes();
77 let setting = match unsafe { CStr::from_ptr(setting) }.to_str() {
78 Ok(s) => s,
79 Err(_) => {
80 platform::ERRNO.set(EINVAL);
81 return ptr::null_mut();
82 }
83 };
84
85 let encoded = if setting.starts_with('$') {
86 if setting.starts_with("$1$") {
87 crypt_md5(key, setting)
88 } else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') {
89 crypt_blowfish(key, setting)
90 } else if setting.starts_with("$5$") {
91 crypt_sha(key, setting, Sha256)
92 } else if setting.starts_with("$6$") {
93 crypt_sha(key, setting, Sha512)
94 } else if setting.starts_with("$7$") {
95 crypt_scrypt(key, setting)
96 } else if setting.starts_with("$8$") {
97 crypt_pbkdf2(key, setting)
98 } else if setting.starts_with("$argon2") {
99 crypt_argon2(key, setting)
100 } else {
101 platform::ERRNO.set(EINVAL);
102 return ptr::null_mut();
103 }
104 } else {
105 None
106 };
107
108 if let Some(inner) = encoded {
109 let len = inner.len();
110 if let Ok(ret) = CString::new(inner) {
111 let ret_ptr = ret.into_raw();
112 unsafe {
113 let dst = (*data).buff.as_mut_ptr();
114 ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len);
115 }
116 ret_ptr.cast()
117 } else {
118 ptr::null_mut()
119 }
120 } else {
121 ptr::null_mut()
122 }
123}