1use aes_gcm::aead::array::Array;
2use aes_gcm::aead::{Aead as _, Generate, KeyInit, Nonce};
3use aes_gcm::Aes256Gcm;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum CryptoError {
8 #[error("encryption failed")]
9 Seal,
10 #[error("decryption failed (tampered or wrong key)")]
11 Open,
12}
13
14pub trait Aead: Send + Sync {
15 fn seal(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError>;
16 fn open(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError>;
17}
18
19pub struct Aes256GcmAead {
21 cipher: Aes256Gcm,
22}
23
24impl Aes256GcmAead {
25 pub fn new(key: &[u8; 32]) -> Self {
26 Self {
27 cipher: Aes256Gcm::new(key.into()),
28 }
29 }
30}
31
32impl Aead for Aes256GcmAead {
33 fn seal(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
34 let nonce = Nonce::<Aes256Gcm>::generate();
35 let ciphertext = self
36 .cipher
37 .encrypt(&nonce, plaintext)
38 .map_err(|_| CryptoError::Seal)?;
39 let mut out = Vec::with_capacity(nonce.len() + ciphertext.len());
40 out.extend_from_slice(&nonce);
41 out.extend_from_slice(&ciphertext);
42 Ok(out)
43 }
44
45 fn open(&self, blob: &[u8]) -> Result<Vec<u8>, CryptoError> {
46 if blob.len() < 12 {
47 return Err(CryptoError::Open);
48 }
49 let (nonce, ciphertext) = blob.split_at(12);
50 let nonce = Array::try_from(nonce).map_err(|_| CryptoError::Open)?;
51 self.cipher
52 .decrypt(&nonce, ciphertext)
53 .map_err(|_| CryptoError::Open)
54 }
55}
56
57pub trait MasterKeyProvider: Send + Sync {
58 fn current_key(&self) -> [u8; 32];
59}
60
61pub struct StaticMasterKeyProvider {
65 key: [u8; 32],
66}
67
68impl StaticMasterKeyProvider {
69 pub fn from_hex(hex_key: &str) -> Result<Self, CryptoError> {
70 let bytes = hex::decode(hex_key.trim()).map_err(|_| CryptoError::Seal)?;
71 let key: [u8; 32] = bytes.try_into().map_err(|_| CryptoError::Seal)?;
72 Ok(Self { key })
73 }
74
75 pub fn from_env(var: &str) -> Result<Self, CryptoError> {
76 let value = std::env::var(var).map_err(|_| CryptoError::Seal)?;
77 if let Ok(contents) = std::fs::read_to_string(&value) {
78 Self::from_hex(&contents)
79 } else {
80 Self::from_hex(&value)
81 }
82 }
83}
84
85impl MasterKeyProvider for StaticMasterKeyProvider {
86 fn current_key(&self) -> [u8; 32] {
87 self.key
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 fn aead() -> Aes256GcmAead {
96 Aes256GcmAead::new(&[7u8; 32])
97 }
98
99 #[test]
100 fn round_trip() {
101 let aead = aead();
102 let plaintext = b"super secret value";
103 let sealed = aead.seal(plaintext).unwrap();
104 assert_eq!(aead.open(&sealed).unwrap(), plaintext);
105 }
106
107 #[test]
108 fn tamper_detection() {
109 let aead = aead();
110 let mut sealed = aead.seal(b"super secret value").unwrap();
111 let last = sealed.len() - 1;
112 sealed[last] ^= 0xFF;
113 assert!(aead.open(&sealed).is_err());
114 }
115
116 #[test]
117 fn wrong_key_fails() {
118 let sealed = aead().seal(b"super secret value").unwrap();
119 let other = Aes256GcmAead::new(&[9u8; 32]);
120 assert!(other.open(&sealed).is_err());
121 }
122}