sync15/
enc_payload.rs
1use crate::error;
6use crate::key_bundle::KeyBundle;
7use lazy_static::lazy_static;
8use serde::{Deserialize, Serialize};
9
10#[derive(Deserialize, Serialize, Clone, Debug)]
13pub struct EncryptedPayload {
14 #[serde(rename = "IV")]
15 pub iv: String,
16 pub hmac: String,
17 pub ciphertext: String,
18}
19
20impl EncryptedPayload {
21 #[inline]
22 pub fn serialized_len(&self) -> usize {
23 (*EMPTY_ENCRYPTED_PAYLOAD_SIZE) + self.ciphertext.len() + self.hmac.len() + self.iv.len()
24 }
25
26 pub fn decrypt(&self, key: &KeyBundle) -> error::Result<String> {
27 key.decrypt(&self.ciphertext, &self.iv, &self.hmac)
28 }
29
30 pub fn decrypt_into<T>(&self, key: &KeyBundle) -> error::Result<T>
31 where
32 for<'a> T: Deserialize<'a>,
33 {
34 Ok(serde_json::from_str(&self.decrypt(key)?)?)
35 }
36
37 pub fn from_cleartext(key: &KeyBundle, cleartext: String) -> error::Result<Self> {
38 let (enc_base64, iv_base64, hmac_base16) =
39 key.encrypt_bytes_rand_iv(cleartext.as_bytes())?;
40 Ok(EncryptedPayload {
41 iv: iv_base64,
42 hmac: hmac_base16,
43 ciphertext: enc_base64,
44 })
45 }
46
47 pub fn from_cleartext_payload<T: Serialize>(
48 key: &KeyBundle,
49 cleartext_payload: &T,
50 ) -> error::Result<Self> {
51 Self::from_cleartext(key, serde_json::to_string(cleartext_payload)?)
52 }
53}
54
55lazy_static! {
58 static ref EMPTY_ENCRYPTED_PAYLOAD_SIZE: usize = serde_json::to_string(
60 &EncryptedPayload { iv: "".into(), hmac: "".into(), ciphertext: "".into() }
61 ).unwrap().len();
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use serde_json::json;
68
69 #[derive(Serialize, Deserialize, Debug)]
70 struct TestStruct {
71 id: String,
72 age: u32,
73 meta: String,
74 }
75
76 #[test]
77 fn test_roundtrip_crypt_record() {
78 let key = KeyBundle::new_random().unwrap();
79 let payload_json = json!({ "id": "aaaaaaaaaaaa", "age": 105, "meta": "data" });
80 let payload =
81 EncryptedPayload::from_cleartext(&key, serde_json::to_string(&payload_json).unwrap())
82 .unwrap();
83
84 let record = payload.decrypt_into::<TestStruct>(&key).unwrap();
85 assert_eq!(record.id, "aaaaaaaaaaaa");
86 assert_eq!(record.age, 105);
87 assert_eq!(record.meta, "data");
88
89 let val_rec = serde_json::to_string(&serde_json::to_value(&payload).unwrap()).unwrap();
91 assert_eq!(payload.serialized_len(), val_rec.len());
92 }
93
94 #[test]
95 fn test_record_bad_hmac() {
96 let key1 = KeyBundle::new_random().unwrap();
97 let json = json!({ "id": "aaaaaaaaaaaa", "deleted": true, });
98
99 let payload =
100 EncryptedPayload::from_cleartext(&key1, serde_json::to_string(&json).unwrap()).unwrap();
101
102 let key2 = KeyBundle::new_random().unwrap();
103 let e = payload
104 .decrypt(&key2)
105 .expect_err("Should fail because wrong keybundle");
106
107 assert!(matches!(e, error::Error::CryptoError(_)));
109 }
110}