//! `crypt.h` implementation. //! //! Non-POSIX, see . use ::scrypt::password_hash::{Salt, SaltString}; use alloc::{ ffi::CString, string::{String, ToString}, }; use core::ptr; use rand::{Rng, SeedableRng, rngs::SmallRng}; use crate::{ c_str::CStr, header::{errno::EINVAL, stdlib::rand}, platform::{ self, types::{c_char, c_int}, }, }; mod argon2; mod blowfish; mod md5; mod pbkdf2; mod scrypt; mod sha; use self::{ argon2::crypt_argon2, blowfish::crypt_blowfish, md5::crypt_md5, pbkdf2::crypt_pbkdf2, scrypt::crypt_scrypt, sha::{ ShaType::{Sha256, Sha512}, crypt_sha, }, }; /// See . #[repr(C)] pub struct crypt_data { initialized: c_int, buff: [c_char; 256], } impl crypt_data { #[allow(clippy::new_without_default)] pub fn new() -> Self { crypt_data { initialized: 1, buff: [0; 256], } } } fn gen_salt() -> Option { 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()) } /// See . #[unsafe(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 unsafe { (*data).initialized } == 0 { unsafe { *data = crypt_data::new() }; } let key = unsafe { CStr::from_ptr(key) }.to_bytes(); let setting = match unsafe { CStr::from_ptr(setting) }.to_str() { Ok(s) => s, Err(_) => { platform::ERRNO.set(EINVAL); return ptr::null_mut(); } }; let encoded = if setting.starts_with('$') { if setting.starts_with("$1$") { crypt_md5(key, setting) } else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') { crypt_blowfish(key, setting) } else if setting.starts_with("$5$") { crypt_sha(key, setting, Sha256) } else if setting.starts_with("$6$") { crypt_sha(key, setting, Sha512) } else if setting.starts_with("$7$") { crypt_scrypt(key, setting) } else if setting.starts_with("$8$") { crypt_pbkdf2(key, setting) } else if setting.starts_with("$argon2") { crypt_argon2(key, setting) } else { platform::ERRNO.set(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(); unsafe { 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() } }