rvaultlib/vaultfunc/
mod.rs1use std::io::Cursor;
2
3use ansible_vault::{decrypt_vault, encrypt_vault};
4use thiserror::Error;
5
6use crate::types::{TypedValue, ValueType};
7
8const ANSIBLE_VAULT_PREFIX: &str = "$ANSIBLE_VAULT;";
9
10#[derive(Debug, Error)]
11pub enum VaultError {
12 #[error("value type isn't supported for encryption: {0}")]
13 UnsupportedType(String),
14 #[error("error while encrypting value: {0}")]
15 Encrypt(String),
16 #[error("error while decrypting value: {0}")]
17 Decrypt(String),
18 #[error("value doesn't contain Ansible vault prefix")]
19 NoVaultPrefix,
20 #[error("decrypted value is not valid UTF-8: {0}")]
21 Utf8(#[from] std::string::FromUtf8Error),
22}
23
24pub fn encrypt(value: &TypedValue, password: &str) -> Result<String, VaultError> {
29 if let TypedValue::String(s) = value {
30 if s.contains("$ANSIBLE_VAULT") {
31 return Ok(s.clone());
32 }
33 }
34
35 let str_value = match value {
36 TypedValue::Bool(b) => format!("{}", b),
37 TypedValue::Number(f) => format!("{:.6}", f),
38 TypedValue::Integer(i) => format!("{}", i),
39 TypedValue::String(s) => s.clone(),
40 TypedValue::Null => return Err(VaultError::UnsupportedType("Null".to_string())),
41 };
42
43 encrypt_vault(Cursor::new(str_value.as_bytes()), password)
44 .map_err(|e| VaultError::Encrypt(e.to_string()))
45}
46
47pub fn decrypt(value: &str, password: &str) -> Result<(TypedValue, ValueType), VaultError> {
51 let index = value
52 .find(ANSIBLE_VAULT_PREFIX)
53 .ok_or(VaultError::NoVaultPrefix)?;
54 let vault_str = &value[index..];
55
56 let decrypted_bytes = decrypt_vault(Cursor::new(vault_str.as_bytes()), password)
57 .map_err(|e| VaultError::Decrypt(e.to_string()))?;
58
59 let decrypted = String::from_utf8(decrypted_bytes)?;
60 Ok(input_type(&decrypted))
61}
62
63pub fn input_type(value: &str) -> (TypedValue, ValueType) {
66 if let Ok(i) = value.parse::<i64>() {
67 return (TypedValue::Integer(i), ValueType::Integer);
68 }
69 if let Ok(b) = value.parse::<bool>() {
70 return (TypedValue::Bool(b), ValueType::Bool);
71 }
72 if let Ok(f) = value.parse::<f64>() {
73 return (TypedValue::Number(f), ValueType::Number);
74 }
75 (TypedValue::String(value.to_string()), ValueType::String)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 const PASSWORD: &str = "_strengGeheim_";
83
84 #[test]
85 fn test_encrypt_decrypt_int() {
86 let tests: &[i64] = &[42, -45, 0];
87 for &v in tests {
88 let encrypted = encrypt(&TypedValue::Integer(v), PASSWORD).expect("encrypt failed");
89 let (decrypted, vt) = decrypt(&encrypted, PASSWORD).expect("decrypt failed");
90 assert_eq!(vt, ValueType::Integer);
91 assert_eq!(decrypted, TypedValue::Integer(v));
92 }
93 }
94
95 #[test]
96 fn test_encrypt_decrypt_bool() {
97 for &v in &[true, false] {
98 let encrypted = encrypt(&TypedValue::Bool(v), PASSWORD).expect("encrypt failed");
99 let (decrypted, vt) = decrypt(&encrypted, PASSWORD).expect("decrypt failed");
100 assert_eq!(vt, ValueType::Bool);
101 assert_eq!(decrypted, TypedValue::Bool(v));
102 }
103 }
104
105 #[test]
106 fn test_encrypt_decrypt_float() {
107 let tests: &[f64] = &[42.78, -13.8, 0.0];
108 for &v in tests {
109 let encrypted = encrypt(&TypedValue::Number(v), PASSWORD).expect("encrypt failed");
110 let (decrypted, vt) = decrypt(&encrypted, PASSWORD).expect("decrypt failed");
111 assert_eq!(vt, ValueType::Number);
112 assert_eq!(decrypted, TypedValue::Number(v));
113 }
114 }
115
116 #[test]
117 fn test_encrypt_decrypt_string() {
118 let tests: &[&str] = &["Some words with äoß", "1234x", ""];
119 for &v in tests {
120 let encrypted =
121 encrypt(&TypedValue::String(v.to_string()), PASSWORD).expect("encrypt failed");
122 let (decrypted, vt) = decrypt(&encrypted, PASSWORD).expect("decrypt failed");
123 assert_eq!(vt, ValueType::String);
124 assert_eq!(decrypted, TypedValue::String(v.to_string()));
125 }
126 }
127
128 #[test]
132 fn test_decrypt_go_encrypted_bool() {
133 let vault_str = concat!(
135 "$ANSIBLE_VAULT;1.1;AES256\n",
136 "35643238653231643030326432343462386436643537343863643061386261613835353835336666\n",
137 "3731623866666563373637326266663963383266636465330a303437336534643232353963346535\n",
138 "35356238303435303932336234356337383663346235653831316330376666323739313964343162\n",
139 "6132343332646432300a643232643534323562353330643336626432666235623237643132326563\n",
140 "3832"
141 );
142 let (value, vt) = decrypt(vault_str, "test999").expect("decrypt failed");
143 assert_eq!(vt, ValueType::Bool);
144 assert_eq!(value, TypedValue::Bool(true));
145 }
146
147 #[test]
148 fn test_decrypt_go_encrypted_int() {
149 let vault_str = concat!(
151 "$ANSIBLE_VAULT;1.1;AES256\n",
152 "30613433363731303632376132343263336664313761613234346366653461613337323865393561\n",
153 "6534386436626431373864343862633533383734623731660a313433643032373166383164326534\n",
154 "63343264636166653865353965666639316361626232366266356337666162316234376232373930\n",
155 "3437306362383631310a663030373266373338383935303061653566613039326637666564353438\n",
156 "3461"
157 );
158 let (value, vt) = decrypt(vault_str, "test999").expect("decrypt failed");
159 assert_eq!(vt, ValueType::Integer);
160 assert_eq!(value, TypedValue::Integer(14));
161 }
162}