• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{openssl_err, openssl_err_or, ossl};
2 use alloc::boxed::Box;
3 use alloc::vec::Vec;
4 use core::cmp::min;
5 use kmr_common::{
6     crypto, crypto::OpaqueOr, explicit, km_err, vec_try, vec_try_with_capacity, Error,
7     FallibleAllocExt,
8 };
9 use openssl::symm::{Cipher, Crypter};
10 
11 /// [`crypto::Aes`] implementation based on BoringSSL.
12 pub struct BoringAes;
13 
14 impl crypto::Aes for BoringAes {
begin( &self, key: OpaqueOr<crypto::aes::Key>, mode: crypto::aes::CipherMode, dir: crypto::SymmetricOperation, ) -> Result<Box<dyn crypto::EmittingOperation>, Error>15     fn begin(
16         &self,
17         key: OpaqueOr<crypto::aes::Key>,
18         mode: crypto::aes::CipherMode,
19         dir: crypto::SymmetricOperation,
20     ) -> Result<Box<dyn crypto::EmittingOperation>, Error> {
21         let key = explicit!(key)?;
22         let dir_mode = match dir {
23             crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt,
24             crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt,
25         };
26         let crypter = match mode {
27             crypto::aes::CipherMode::EcbNoPadding | crypto::aes::CipherMode::EcbPkcs7Padding => {
28                 let (cipher, key) = match &key {
29                     crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ecb(), &k[..]),
30                     crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ecb(), &k[..]),
31                     crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ecb(), &k[..]),
32                 };
33                 let mut crypter = Crypter::new(cipher, dir_mode, key, None)
34                     .map_err(openssl_err!("failed to create ECB Crypter"))?;
35                 if let crypto::aes::CipherMode::EcbPkcs7Padding = mode {
36                     crypter.pad(true);
37                 } else {
38                     crypter.pad(false);
39                 }
40                 crypter
41             }
42 
43             crypto::aes::CipherMode::CbcNoPadding { nonce: n }
44             | crypto::aes::CipherMode::CbcPkcs7Padding { nonce: n } => {
45                 let (cipher, key) = match &key {
46                     crypto::aes::Key::Aes128(k) => (Cipher::aes_128_cbc(), &k[..]),
47                     crypto::aes::Key::Aes192(k) => (Cipher::aes_192_cbc(), &k[..]),
48                     crypto::aes::Key::Aes256(k) => (Cipher::aes_256_cbc(), &k[..]),
49                 };
50                 let mut crypter = Crypter::new(cipher, dir_mode, key, Some(&n[..]))
51                     .map_err(openssl_err!("failed to create CBC Crypter"))?;
52                 if let crypto::aes::CipherMode::CbcPkcs7Padding { nonce: _ } = mode {
53                     crypter.pad(true);
54                 } else {
55                     crypter.pad(false);
56                 }
57                 crypter
58             }
59 
60             crypto::aes::CipherMode::Ctr { nonce: n } => {
61                 let (cipher, key) = match &key {
62                     crypto::aes::Key::Aes128(k) => (Cipher::aes_128_ctr(), &k[..]),
63                     crypto::aes::Key::Aes192(k) => (Cipher::aes_192_ctr(), &k[..]),
64                     crypto::aes::Key::Aes256(k) => (Cipher::aes_256_ctr(), &k[..]),
65                 };
66                 Crypter::new(cipher, dir_mode, key, Some(&n[..]))
67                     .map_err(openssl_err!("failed to create CTR Crypter"))?
68             }
69         };
70 
71         Ok(Box::new(BoringAesOperation { crypter }))
72     }
73 
begin_aead( &self, key: OpaqueOr<crypto::aes::Key>, mode: crypto::aes::GcmMode, dir: crypto::SymmetricOperation, ) -> Result<Box<dyn crypto::AadOperation>, Error>74     fn begin_aead(
75         &self,
76         key: OpaqueOr<crypto::aes::Key>,
77         mode: crypto::aes::GcmMode,
78         dir: crypto::SymmetricOperation,
79     ) -> Result<Box<dyn crypto::AadOperation>, Error> {
80         let key = explicit!(key)?;
81         let dir_mode = match dir {
82             crypto::SymmetricOperation::Encrypt => openssl::symm::Mode::Encrypt,
83             crypto::SymmetricOperation::Decrypt => openssl::symm::Mode::Decrypt,
84         };
85         let crypter = match mode {
86             crypto::aes::GcmMode::GcmTag12 { nonce: n }
87             | crypto::aes::GcmMode::GcmTag13 { nonce: n }
88             | crypto::aes::GcmMode::GcmTag14 { nonce: n }
89             | crypto::aes::GcmMode::GcmTag15 { nonce: n }
90             | crypto::aes::GcmMode::GcmTag16 { nonce: n } => {
91                 let (cipher, key) = match &key {
92                     crypto::aes::Key::Aes128(k) => (Cipher::aes_128_gcm(), &k[..]),
93                     crypto::aes::Key::Aes192(k) => (Cipher::aes_192_gcm(), &k[..]),
94                     crypto::aes::Key::Aes256(k) => (Cipher::aes_256_gcm(), &k[..]),
95                 };
96                 Crypter::new(cipher, dir_mode, key, Some(&n[..])).map_err(openssl_err!(
97                     "failed to create GCM Crypter for {:?} {:?}",
98                     mode,
99                     dir
100                 ))?
101             }
102         };
103 
104         Ok(match dir {
105             crypto::SymmetricOperation::Encrypt => Box::new({
106                 BoringAesGcmEncryptOperation { mode, inner: BoringAesOperation { crypter } }
107             }),
108             crypto::SymmetricOperation::Decrypt => Box::new(BoringAesGcmDecryptOperation {
109                 crypter,
110                 decrypt_tag_len: mode.tag_len(),
111                 pending_input_tail: vec_try_with_capacity!(mode.tag_len())?,
112             }),
113         })
114     }
115 }
116 
117 /// [`crypto::AesOperation`] implementation based on BoringSSL.
118 pub struct BoringAesOperation {
119     crypter: openssl::symm::Crypter,
120 }
121 
122 impl crypto::EmittingOperation for BoringAesOperation {
update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>123     fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
124         let mut output = vec_try![0; data.len() + crypto::aes::BLOCK_SIZE]?;
125         let out_len = self
126             .crypter
127             .update(data, &mut output)
128             .map_err(openssl_err!("update {} bytes from input failed", data.len()))?;
129         output.truncate(out_len);
130         Ok(output)
131     }
132 
finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>133     fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> {
134         let mut output = vec_try![0; crypto::aes::BLOCK_SIZE]?;
135         let out_len = ossl!(self.crypter.finalize(&mut output))?;
136         output.truncate(out_len);
137         Ok(output)
138     }
139 }
140 
141 /// [`crypto::AesGcmEncryptOperation`] implementation based on BoringSSL.
142 pub struct BoringAesGcmEncryptOperation {
143     mode: crypto::aes::GcmMode,
144     inner: BoringAesOperation,
145 }
146 
147 impl crypto::AadOperation for BoringAesGcmEncryptOperation {
update_aad(&mut self, aad: &[u8]) -> Result<(), Error>148     fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> {
149         ossl!(self.inner.crypter.aad_update(aad))
150     }
151 }
152 
153 impl crypto::EmittingOperation for BoringAesGcmEncryptOperation {
update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>154     fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
155         self.inner.update(data)
156     }
157 
finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>158     fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> {
159         let mut output = vec_try![0; crypto::aes::BLOCK_SIZE + self.mode.tag_len()]?;
160         let offset = self
161             .inner
162             .crypter
163             .finalize(&mut output)
164             .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?;
165 
166         self.inner
167             .crypter
168             .get_tag(&mut output[offset..offset + self.mode.tag_len()])
169             .map_err(openssl_err!("failed to get tag of len {}", self.mode.tag_len()))?;
170         output.truncate(offset + self.mode.tag_len());
171         Ok(output)
172     }
173 }
174 
175 /// [`crypto::AesGcmDecryptOperation`] implementation based on BoringSSL.
176 pub struct BoringAesGcmDecryptOperation {
177     crypter: openssl::symm::Crypter,
178 
179     // Size of a final tag when decrypting.
180     decrypt_tag_len: usize,
181 
182     // For decryption, the last `decrypt_tag_len` bytes of input must be fed in separately.
183     // However, the overall size of the input data is not known in advance, so we need to hold up to
184     // `decrypt_tag_len` bytes on input in reserve until `finish()`.
185     pending_input_tail: Vec<u8>, // Capacity = decrypt_tag_len
186 }
187 
188 impl crypto::AadOperation for BoringAesGcmDecryptOperation {
update_aad(&mut self, aad: &[u8]) -> Result<(), Error>189     fn update_aad(&mut self, aad: &[u8]) -> Result<(), Error> {
190         ossl!(self.crypter.aad_update(aad))
191     }
192 }
193 
194 impl crypto::EmittingOperation for BoringAesGcmDecryptOperation {
update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error>195     fn update(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
196         // The current input is the (self.pending_input_tail || data) combination.
197         let combined_len = self.pending_input_tail.len() + data.len();
198         if combined_len <= self.decrypt_tag_len {
199             // Adding on this data is still not enough for more than just a tag,
200             // so save off the input data for next time and return.
201             self.pending_input_tail.try_extend_from_slice(data)?;
202             return Ok(Vec::new());
203         }
204 
205         // At this point the combination (self.pending_input_tail || data) includes enough data to both:
206         // - feed some into the cipher
207         // - still keep a full self.decrypt_tag_len worth of data still pending.
208         let cipherable_len = combined_len - self.decrypt_tag_len;
209         let cipherable_from_pending = min(cipherable_len, self.pending_input_tail.len());
210         let cipherable_from_data = cipherable_len - cipherable_from_pending;
211 
212         let mut output = vec_try![0; data.len()]?;
213         let mut offset = 0;
214         if cipherable_from_pending > 0 {
215             offset = self
216                 .crypter
217                 .update(&self.pending_input_tail[..cipherable_from_pending], &mut output)
218                 .map_err(openssl_err!(
219                     "update {} bytes from pending failed",
220                     cipherable_from_pending
221                 ))?;
222         }
223         if cipherable_from_data > 0 {
224             let out_len = self
225                 .crypter
226                 .update(&data[..cipherable_from_data], &mut output[offset..])
227                 .map_err(openssl_err!("update {} bytes from input failed", cipherable_from_data))?;
228             offset += out_len;
229         }
230         output.truncate(offset);
231 
232         // Reset `self.pending_input_tail` to the unused data.
233         let leftover_pending = self.pending_input_tail.len() - cipherable_from_pending;
234         self.pending_input_tail.resize(self.decrypt_tag_len, 0);
235         self.pending_input_tail.copy_within(cipherable_from_pending.., 0);
236         self.pending_input_tail[leftover_pending..].copy_from_slice(&data[cipherable_from_data..]);
237 
238         Ok(output)
239     }
240 
finish(mut self: Box<Self>) -> Result<Vec<u8>, Error>241     fn finish(mut self: Box<Self>) -> Result<Vec<u8>, Error> {
242         // Need to feed in the entire tag before completion.
243         if self.pending_input_tail.len() != self.decrypt_tag_len {
244             return Err(km_err!(
245                 InvalidTag,
246                 "only {} bytes of pending data, need {}",
247                 self.pending_input_tail.len(),
248                 self.decrypt_tag_len
249             ));
250         }
251         self.crypter.set_tag(&self.pending_input_tail).map_err(openssl_err!(
252             "failed to set {} bytes of tag",
253             self.pending_input_tail.len()
254         ))?;
255 
256         // Feeding in just the tag should not result in any output data.
257         let mut output = Vec::new();
258         let out_len = self
259             .crypter
260             .finalize(&mut output)
261             .map_err(openssl_err_or!(VerificationFailed, "failed to finalize"))?;
262         if out_len != 0 {
263             return Err(km_err!(
264                 UnknownError,
265                 "finalizing AES-GCM tag produced {} bytes of data!",
266                 out_len
267             ));
268         }
269         Ok(output)
270     }
271 }
272