Skip to main content

relibc/header/crypt/
mod.rs

1//! `crypt.h` implementation.
2//!
3//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
4
5use ::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/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
42#[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/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
66#[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}