1use std::path::Path;
2
3use aes_gcm::{
4 Aes256Gcm, Nonce,
5 aead::{Aead, AeadCore, KeyInit},
6};
7use rand::rngs::OsRng;
8
9use crate::{
10 error::{CryptoError, SourceError},
11 format::Format,
12 key::SecretKey,
13 value::ConfigValue,
14};
15
16pub trait Encryptor: Send + Sync {
18 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError>;
21
22 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError>;
25}
26
27pub struct Aes256GcmEncryptor {
29 cipher: Aes256Gcm,
30}
31
32impl From<&SecretKey> for Aes256GcmEncryptor {
33 fn from(key: &SecretKey) -> Self {
34 Self {
35 cipher: Aes256Gcm::new(key.as_bytes().into()),
36 }
37 }
38}
39
40const NONCE_LEN: usize = 12;
41
42impl Encryptor for Aes256GcmEncryptor {
43 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
44 let nonce = Aes256Gcm::generate_nonce(OsRng);
45 let ct = self
46 .cipher
47 .encrypt(&nonce, plaintext)
48 .map_err(|_| CryptoError::Encrypt)?;
49
50 let mut out = Vec::with_capacity(NONCE_LEN + ct.len());
51 out.extend_from_slice(&nonce);
52 out.extend_from_slice(&ct);
53 Ok(out)
54 }
55
56 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
57 if ciphertext.len() < NONCE_LEN {
58 return Err(CryptoError::InvalidFormat);
59 }
60 let (nonce_bytes, ct) = ciphertext.split_at(NONCE_LEN);
61 let nonce = Nonce::from_slice(nonce_bytes);
62 self.cipher
63 .decrypt(nonce, ct)
64 .map_err(|_| CryptoError::Decrypt)
65 }
66}
67
68pub fn encrypt_fields(
76 value: &mut ConfigValue,
77 field_names: &[&str],
78 encryptor: &dyn Encryptor,
79) -> Result<(), CryptoError> {
80 let ConfigValue::Table(table) = value else {
81 return Ok(());
82 };
83 for (key, val) in table.iter_mut() {
84 if field_names.contains(&key.as_str()) {
85 let ConfigValue::String(s) = val else {
86 return Err(CryptoError::NotAString(key.clone()));
87 };
88 let encrypted = encryptor.encrypt(s.as_bytes())?;
89 *val = ConfigValue::String(hex::encode(encrypted));
90 } else if matches!(val, ConfigValue::Table(_)) {
91 encrypt_fields(val, field_names, encryptor)?;
92 }
93 }
94 Ok(())
95}
96
97pub fn decrypt_fields(
102 value: &mut ConfigValue,
103 field_names: &[&str],
104 encryptor: &dyn Encryptor,
105) -> Result<(), CryptoError> {
106 let ConfigValue::Table(table) = value else {
107 return Ok(());
108 };
109 for (key, val) in table.iter_mut() {
110 if field_names.contains(&key.as_str()) {
111 let ConfigValue::String(s) = val else {
112 return Err(CryptoError::NotAString(key.clone()));
113 };
114 let bytes = hex::decode(s.as_str()).map_err(|_| CryptoError::InvalidFormat)?;
115 let plain = encryptor.decrypt(&bytes)?;
116 *val = ConfigValue::String(String::from_utf8(plain).map_err(|_| CryptoError::Decrypt)?);
117 } else if matches!(val, ConfigValue::Table(_)) {
118 decrypt_fields(val, field_names, encryptor)?;
119 }
120 }
121 Ok(())
122}
123
124pub struct EncryptedFormat<'a, F: Format> {
128 inner: F,
129 encryptor: &'a dyn Encryptor,
130}
131
132impl<'a, F: Format> EncryptedFormat<'a, F> {
133 #[must_use]
134 pub fn new(inner: F, encryptor: &'a dyn Encryptor) -> Self {
135 Self { inner, encryptor }
136 }
137}
138
139impl<F: Format> Format for EncryptedFormat<'_, F> {
140 fn name(&self) -> &'static str {
141 self.inner.name()
142 }
143
144 fn parse(&self, path: &Path, bytes: &[u8]) -> Result<ConfigValue, SourceError> {
145 let decrypted = self.encryptor.decrypt(bytes)?;
146 self.inner.parse(path, &decrypted)
147 }
148
149 fn serialize(&self, value: &ConfigValue) -> Result<Vec<u8>, SourceError> {
150 let bytes = self.inner.serialize(value)?;
151 Ok(self.encryptor.encrypt(&bytes)?)
152 }
153}