tink_aead/subtle/
encrypt_then_authenticate.rs1use super::IndCpaCipher;
20use tink_core::{utils::wrap_err, TinkError};
21
22pub struct EncryptThenAuthenticate {
28 ind_cpa_cipher: Box<dyn IndCpaCipher>,
29 mac: Box<dyn tink_core::Mac>,
30 tag_size: usize,
31}
32
33impl Clone for EncryptThenAuthenticate {
36 fn clone(&self) -> Self {
37 Self {
38 ind_cpa_cipher: self.ind_cpa_cipher.box_clone(),
39 mac: self.mac.box_clone(),
40 tag_size: self.tag_size,
41 }
42 }
43}
44
45const MIN_TAG_SIZE_IN_BYTES: usize = 10;
46
47impl EncryptThenAuthenticate {
48 pub fn new(
50 ind_cpa_cipher: Box<dyn IndCpaCipher>,
51 mac: Box<dyn tink_core::Mac>,
52 tag_size: usize,
53 ) -> Result<EncryptThenAuthenticate, TinkError> {
54 if tag_size < MIN_TAG_SIZE_IN_BYTES {
55 return Err("EncryptThenAuthenticate: tag size too small".into());
56 }
57 Ok(EncryptThenAuthenticate {
58 ind_cpa_cipher,
59 mac,
60 tag_size,
61 })
62 }
63}
64
65impl tink_core::Aead for EncryptThenAuthenticate {
66 fn encrypt(&self, plaintext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, TinkError> {
75 let mut ciphertext = self
76 .ind_cpa_cipher
77 .encrypt(plaintext)
78 .map_err(|e| wrap_err("EncryptThenAuthenticate", e))?;
79
80 let mut to_auth_data = Vec::with_capacity(additional_data.len() + ciphertext.len() + 8);
83 to_auth_data.extend_from_slice(additional_data);
84 to_auth_data.extend_from_slice(&ciphertext);
85 let aad_size_in_bits: u64 = (additional_data.len() as u64)
86 .checked_mul(8)
87 .ok_or_else(|| TinkError::new("EncryptThenAuthenticate: additional data too long"))?;
88 to_auth_data.extend_from_slice(&aad_size_in_bits.to_be_bytes());
89
90 let tag = self
91 .mac
92 .compute_mac(&to_auth_data)
93 .map_err(|e| wrap_err("EncryptThenAuthenticate", e))?;
94 if tag.len() != self.tag_size {
95 return Err("EncryptThenAuthenticate: invalid tag size".into());
96 }
97
98 ciphertext.extend_from_slice(&tag);
100 Ok(ciphertext)
101 }
102
103 fn decrypt(&self, ciphertext: &[u8], additional_data: &[u8]) -> Result<Vec<u8>, TinkError> {
106 if ciphertext.len() < self.tag_size {
107 return Err("EncryptThenAuthenticate: ciphertext too short".into());
108 }
109
110 let payload = &ciphertext[..(ciphertext.len() - self.tag_size)];
112
113 let mut to_auth_data = Vec::with_capacity(additional_data.len() + payload.len() + 8);
116 to_auth_data.extend_from_slice(additional_data);
117 to_auth_data.extend_from_slice(payload);
118 let aad_size_in_bits: u64 = (additional_data.len() as u64)
119 .checked_mul(8)
120 .ok_or_else(|| TinkError::new("EncryptThenAuthenticate: additional data too long"))?;
121 to_auth_data.extend_from_slice(&aad_size_in_bits.to_be_bytes());
122
123 self.mac
125 .verify_mac(
126 &ciphertext[(ciphertext.len() - self.tag_size)..],
127 &to_auth_data,
128 )
129 .map_err(|e| wrap_err("EncryptThenAuthenticate", e))?;
130
131 let plaintext = self
132 .ind_cpa_cipher
133 .decrypt(payload)
134 .map_err(|e| wrap_err("EncryptThenAuthenticate", e))?;
135
136 Ok(plaintext)
137 }
138}