1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! # rust binding for POSIX crypt library (libcrypt)
//! 
//! ## Example
//! ```
//! // Import Crypt struct and Encryptions enum.
//! use libcrypt_rs::{Crypt, Encryptions};
//! 
//! fn main() {
//! 	// Create new instance of Crypt object.
//! 	let mut engine = Crypt::new();
//! 
//! 	// Generate new encryption salt for sha256crypt encryption algorithm.
//! 	engine.gen_salt(Encryptions::Sha256).expect("Salt generation failed");
//! 	
//! 	// Encrypt "1234" string.
//! 	engine.encrypt("1234".to_string()).expect("Encryption failed");
//! 	
//! 	// Encrypted data is stored in engine.encrypted. Let's print it to stdout.
//! 	println!("Encrypted data: '{}'", engine.encrypted);
//! }
//! ```

#[cfg(test)]
mod tests;

use std::ffi::{CString, NulError, CStr};

pub mod encryptions;
pub use encryptions::Encryptions;
pub mod raw;

/// Encryption struct.
pub struct Crypt {
	/// Stores encrypted data.
	pub encrypted: String,
	salt: Option<CString>
}

impl Crypt {
	/// Create new instance of Crypt object.
	pub fn new() -> Self {
		Crypt {
			encrypted: String::new(),
			salt: None
		}
	}

	/// Set custom salt for encryption.
	/// ```
	/// use libcrypt_rs::Crypt;
	/// 
	/// let mut engine = Crypt::new();
	/// engine.set_salt("$1$N1TAWHQs".to_string()).expect("Setting custom salt failed");
	/// 
	/// println!("{}", engine.get_salt().unwrap());
	pub fn set_salt(&mut self, salt: String) -> Result<(), NulError> {
		self.salt = Some(CString::new(salt)?);

		Ok(())
	}

	/// Generate salt.
	/// ```
	/// use libcrypt_rs::{Crypt, Encryptions};
	/// 
	/// let mut engine = Crypt::new();
	/// engine.gen_salt(Encryptions::Sha256).expect("Salt generation failed");
	/// 
	/// println!("{}", engine.get_salt().unwrap());
	/// ```
	pub fn gen_salt(&mut self, encryption: Encryptions) -> Result<(), String> {
		let decoded = encryption.decode();

		self.salt = Some(unsafe {
			let enc = match CString::new(decoded) {
				Ok(o) => o,
				Err(e) => return Err(format!("Failed to create C string: {e}"))
			};
			CString::from(
				CStr::from_ptr(raw::crypt_gensalt(enc.as_ptr(), 15, std::ptr::null(), 0))
			)
		});
		
		Ok(())
	}

	/// Encrypt data. Encrypted data can be accessed in the `encrypted` field of Crypt struct.
	/// ```
	/// use libcrypt_rs::{Crypt, Encryptions};
	/// 
	/// let mut engine = Crypt::new();
	/// engine.gen_salt(Encryptions::Sha256).expect("Salt generation failed");
	/// 
	/// engine.encrypt("example_phrase".to_string()).expect("Encryption failed");
	/// 
	/// println!("Encrypted data: {}", engine.encrypted);
	pub fn encrypt(&mut self, encrypt: String) -> Result<(), String> {
		if let None = self.salt {
			return Err("Salt hasn't been set".to_string());
		}

		let enc = match CString::new(encrypt) {
			Ok(o) => o,
			Err(e) => return Err(format!("Failed to allocate C string: {e}"))
		};
		let ptr = unsafe {
			raw::crypt(enc.clone().as_ptr(), self.salt.clone().unwrap().as_ptr())
		};

		self.encrypted = unsafe {
			match CStr::from_ptr(ptr).to_str() {
				Err(e) => return Err(format!("Failed to encode C string: {e}")),
				Ok(o) => o.to_string(),
			}
		};
		
		Ok(())
	}

	/// `salt` is a private field of Crypt struct, using this function.
	pub fn get_salt(&self) -> Result<String, String> {
		match self.salt.clone().unwrap().to_str() {
			Err(e) => return Err(format!("Failed to encode C string: {e}")),
			Ok(o) => Ok(o.to_string())
		}
	}
}