password_encryptor/
lib.rs1mod error;
2
3use error::{Error, Result};
4use hmac::{Hmac, Mac};
5use sha2::Sha512;
6
7
8#[derive(Clone)]
9pub struct EncryptionData<'a> {
10 pub content: &'a str,
11 pub salt: &'a str,
12}
13
14#[derive(Clone)]
15pub struct PasswordEncryptor {
16 key: Vec<u8>,
17 encryption_prefix: Option<String>,
18}
19
20impl PasswordEncryptor {
21 pub fn new(key: Vec<u8>, encryption_prefix: Option<String>) -> Self {
22 Self {
23 key,
24 encryption_prefix,
25 }
26 }
27}
28
29impl PasswordEncryptor {
30 fn encrypt_into_base64url(
31 &self,
32 key: &[u8],
33 encryption_data: &EncryptionData,
34 ) -> Result<String> {
35 let EncryptionData { content, salt } = encryption_data;
36
37 let mut hmac_sha512 =
38 Hmac::<Sha512>::new_from_slice(key).map_err(|_| Error::KeyFailHmac)?;
39
40 hmac_sha512.update(content.as_bytes());
41 hmac_sha512.update(salt.as_bytes());
42
43 let hmac_result = hmac_sha512.finalize();
44 let result_bytes = hmac_result.into_bytes();
45
46 let result = base64_url::encode(&result_bytes);
47
48 Ok(result)
49 }
50
51 pub fn encrypt_password(&self, encryption_data: &EncryptionData) -> Result<String> {
52 let encrypted = self.encrypt_into_base64url(&self.key, encryption_data)?;
53 let final_prefix = self.encryption_prefix.clone().unwrap_or("".to_string());
54 Ok(format!("{final_prefix}{encrypted}"))
55 }
56
57 pub fn validate_password(
58 &self,
59 encryption_data: &EncryptionData,
60 encrypted_password: &str,
61 ) -> Result<()> {
62 let inner_encrypted_password = self.encrypt_password(encryption_data)?;
63 if inner_encrypted_password == encrypted_password {
64 Ok(())
65 } else {
66 Err(Error::PasswordsDontMatch)
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_successful_encryption() {
77 let encryptor = PasswordEncryptor::new(vec![1,2,3], None);
78 let data = EncryptionData {
79 content: "password123",
80 salt: "salt",
81 };
82
83 let encrypted = encryptor.encrypt_password(&data).unwrap();
84 assert!(!encrypted.is_empty())
85 }
86
87 #[test]
88 fn test_validation_success() {
89 let encryptor = PasswordEncryptor::new(vec![1,2,3], None);
90 let data = EncryptionData {
91 content: "password123",
92 salt: "salt",
93 };
94
95 let encrypted = encryptor.encrypt_password(&data).unwrap();
96 assert!(encryptor.validate_password(&data, &encrypted).is_ok());
97 }
98
99 #[test]
100 fn test_validation_failure() {
101 let encryptor = PasswordEncryptor::new(vec![1,2,3], Some("prefix_".to_string()));
102 let data = EncryptionData {
103 content: "password123",
104 salt: "salt",
105 };
106
107 assert!(encryptor
108 .validate_password(&data, "wrong_password")
109 .is_err());
110 }
111
112 #[test]
113 fn test_password_mismatch_error() {
114 let encryptor = PasswordEncryptor::new(vec![1,2,3], Some("prefix_".to_string()));
115 let data = EncryptionData {
116 content: "password123",
117 salt: "salt",
118 };
119
120 let wrong_data = EncryptionData {
121 content: "wrong_password",
122 salt: "salt",
123 };
124
125 let encrypted = encryptor.encrypt_password(&data).unwrap();
126 match encryptor.validate_password(&wrong_data, &encrypted) {
127 Err(Error::PasswordsDontMatch) => (),
128 _ => panic!("Expected PasswordsDontMatch error"),
129 }
130 }
131}