1use core::marker::PhantomData;
4
5use aead::generic_array::ArrayLength;
6use aes_core::{Aes128, Aes256};
7use cbc::{Decryptor as CbcDec, Encryptor as CbcEnc};
8use cipher::{
9 block_padding::Pkcs7, BlockCipher, BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit,
10};
11use digest::{crypto_common::BlockSizeUser, Digest};
12use hmac::{Mac, SimpleHmac};
13use subtle::ConstantTimeEq;
14
15use super::{AesKey, AesType, NonceSize, TagSize};
16use crate::{
17 alg::AesTypes,
18 buffer::ResizeBuffer,
19 encrypt::{KeyAeadInPlace, KeyAeadMeta, KeyAeadParams},
20 error::Error,
21 generic_array::{
22 typenum::{consts, Unsigned},
23 GenericArray,
24 },
25};
26
27pub type A128CbcHs256 = AesCbcHmac<Aes128, sha2::Sha256>;
29
30impl AesType for A128CbcHs256 {
31 type KeySize = consts::U32;
32 const ALG_TYPE: AesTypes = AesTypes::A128CbcHs256;
33 const JWK_ALG: &'static str = "A128CBC-HS256";
34}
35
36pub type A256CbcHs512 = AesCbcHmac<Aes256, sha2::Sha512>;
38
39impl AesType for A256CbcHs512 {
40 type KeySize = consts::U64;
41 const ALG_TYPE: AesTypes = AesTypes::A256CbcHs512;
42 const JWK_ALG: &'static str = "A256CBC-HS512";
43}
44
45#[derive(Debug)]
47pub struct AesCbcHmac<C, D>(PhantomData<(C, D)>);
48
49impl<C, D> AesCbcHmac<C, D>
50where
51 C: BlockCipher,
52{
53 #[inline]
54 fn padding_length(len: usize) -> usize {
55 C::BlockSize::USIZE - (len % C::BlockSize::USIZE)
56 }
57}
58
59impl<C, D> KeyAeadMeta for AesKey<AesCbcHmac<C, D>>
60where
61 AesCbcHmac<C, D>: AesType,
62 C: BlockCipher + KeyInit,
63{
64 type NonceSize = C::BlockSize;
65 type TagSize = C::KeySize;
66}
67
68impl<C, D> KeyAeadInPlace for AesKey<AesCbcHmac<C, D>>
69where
70 AesCbcHmac<C, D>: AesType,
71 C: BlockCipher + KeyInit + BlockEncryptMut + BlockDecryptMut,
72 D: Digest + BlockSizeUser,
73 C::KeySize: core::ops::Shl<consts::B1>,
74 <C::KeySize as core::ops::Shl<consts::B1>>::Output: ArrayLength<u8>,
75{
76 fn encrypt_in_place(
77 &self,
78 buffer: &mut dyn ResizeBuffer,
79 nonce: &[u8],
80 aad: &[u8],
81 ) -> Result<usize, Error> {
82 if nonce.len() != NonceSize::<Self>::USIZE {
83 return Err(err_msg!(InvalidNonce));
84 }
85 if TagSize::<Self>::USIZE > D::OutputSize::USIZE {
87 return Err(err_msg!(
88 Encryption,
89 "AES-CBC-HMAC tag size exceeds maximum supported"
90 ));
91 }
92 if aad.len() as u64 > u64::MAX / 8 {
93 return Err(err_msg!(
94 Encryption,
95 "AES-CBC-HMAC AAD size exceeds maximum supported"
96 ));
97 }
98
99 let msg_len = buffer.as_ref().len();
100 let pad_len = AesCbcHmac::<C, D>::padding_length(msg_len);
101 buffer.buffer_extend(pad_len + TagSize::<Self>::USIZE)?;
102 let enc_key = GenericArray::from_slice(&self.0[C::KeySize::USIZE..]);
103 <CbcEnc<C> as KeyIvInit>::new(enc_key, GenericArray::from_slice(nonce))
104 .encrypt_padded_mut::<Pkcs7>(buffer.as_mut(), msg_len)
105 .map_err(|_| err_msg!(Encryption, "AES-CBC encryption error"))?;
106 let ctext_end = msg_len + pad_len;
107
108 let mut hmac = <SimpleHmac<D> as Mac>::new_from_slice(&self.0[..C::KeySize::USIZE])
109 .expect("Incompatible HMAC key length");
110 hmac.update(aad);
111 hmac.update(nonce.as_ref());
112 hmac.update(&buffer.as_ref()[..ctext_end]);
113 hmac.update(&((aad.len() as u64) * 8).to_be_bytes());
114 let mac = hmac.finalize().into_bytes();
115 buffer.as_mut()[ctext_end..(ctext_end + TagSize::<Self>::USIZE)]
116 .copy_from_slice(&mac[..TagSize::<Self>::USIZE]);
117
118 Ok(ctext_end)
119 }
120
121 fn decrypt_in_place(
122 &self,
123 buffer: &mut dyn ResizeBuffer,
124 nonce: &[u8],
125 aad: &[u8],
126 ) -> Result<(), Error> {
127 if nonce.len() != NonceSize::<Self>::USIZE {
128 return Err(err_msg!(InvalidNonce));
129 }
130 if aad.len() as u64 > u64::MAX / 8 {
131 return Err(err_msg!(
132 Encryption,
133 "AES-CBC-HMAC AAD size exceeds maximum supported"
134 ));
135 }
136 let buf_len = buffer.as_ref().len();
137 if buf_len < TagSize::<Self>::USIZE {
138 return Err(err_msg!(Encryption, "Invalid size for encrypted data"));
139 }
140 let ctext_end = buf_len - TagSize::<Self>::USIZE;
141 let tag = GenericArray::<u8, TagSize<Self>>::from_slice(&buffer.as_ref()[ctext_end..]);
142
143 let mut hmac = <SimpleHmac<D> as Mac>::new_from_slice(&self.0[..C::KeySize::USIZE])
144 .expect("Incompatible HMAC key length");
145 hmac.update(aad);
146 hmac.update(nonce.as_ref());
147 hmac.update(&buffer.as_ref()[..ctext_end]);
148 hmac.update(&((aad.len() as u64) * 8).to_be_bytes());
149 let mac = hmac.finalize().into_bytes();
150 let tag_match = tag.as_ref().ct_eq(&mac[..TagSize::<Self>::USIZE]);
151
152 let enc_key = GenericArray::from_slice(&self.0[C::KeySize::USIZE..]);
153 let dec_len = <CbcDec<C> as KeyIvInit>::new(enc_key, GenericArray::from_slice(nonce))
154 .decrypt_padded_mut::<Pkcs7>(&mut buffer.as_mut()[..ctext_end])
155 .map_err(|_| err_msg!(Encryption, "AES-CBC decryption error"))?
156 .len();
157 buffer.buffer_resize(dec_len)?;
158
159 if tag_match.unwrap_u8() != 1 {
160 Err(err_msg!(Encryption, "AEAD decryption error"))
161 } else {
162 Ok(())
163 }
164 }
165
166 fn aead_params(&self) -> KeyAeadParams {
167 KeyAeadParams {
168 nonce_length: NonceSize::<Self>::USIZE,
169 tag_length: TagSize::<Self>::USIZE,
170 }
171 }
172
173 fn aead_padding(&self, msg_len: usize) -> usize {
174 AesCbcHmac::<C, D>::padding_length(msg_len)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use base64::Engine;
181 use std::string::ToString;
182
183 use super::*;
184 use crate::buffer::SecretBytes;
185 use crate::repr::KeySecretBytes;
186
187 #[test]
188 fn encrypt_expected_cbc_128_hmac_256() {
189 let key_data = &hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
190 let input = b"A cipher system must not be required to be secret, and it must be able to fall into the hands of the enemy without inconvenience";
191 let nonce = &hex!("1af38c2dc2b96ffdd86694092341bc04");
192 let aad = b"The second principle of Auguste Kerckhoffs";
193 let key = AesKey::<A128CbcHs256>::from_secret_bytes(key_data).unwrap();
194 let mut buffer = SecretBytes::from_slice(input);
195 key.encrypt_in_place(&mut buffer, &nonce[..], &aad[..])
196 .unwrap();
197
198 assert_eq!(
199 buffer.as_hex().to_string(),
200 "c80edfa32ddf39d5ef00c0b468834279a2e46a1b8049f792f76bfe54b903a9c9\
201 a94ac9b47ad2655c5f10f9aef71427e2fc6f9b3f399a221489f16362c7032336\
202 09d45ac69864e3321cf82935ac4096c86e133314c54019e8ca7980dfa4b9cf1b\
203 384c486f3a54c51078158ee5d79de59fbd34d848b3d69550a67646344427ade5\
204 4b8851ffb598f7f80074b9473c82e2db652c3fa36b0a7c5b3219fab3a30bc1c4"
205 );
206 key.decrypt_in_place(&mut buffer, &nonce[..], &aad[..])
207 .unwrap();
208 assert_eq!(buffer, &input[..]);
209 }
210
211 #[test]
212 fn encrypt_expected_cbc_256_hmac_512() {
213 let key_data = &hex!(
214 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
215 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
216 );
217 let input = b"A cipher system must not be required to be secret, and it must be able to fall into the hands of the enemy without inconvenience";
218 let nonce = &hex!("1af38c2dc2b96ffdd86694092341bc04");
219 let aad = b"The second principle of Auguste Kerckhoffs";
220 let key = AesKey::<A256CbcHs512>::from_secret_bytes(key_data).unwrap();
221 let mut buffer = SecretBytes::from_slice(input);
222 key.encrypt_in_place(&mut buffer, &nonce[..], &aad[..])
223 .unwrap();
224
225 assert_eq!(
226 buffer.as_hex().to_string(),
227 "4affaaadb78c31c5da4b1b590d10ffbd3dd8d5d302423526912da037ecbcc7bd\
228 822c301dd67c373bccb584ad3e9279c2e6d12a1374b77f077553df829410446b\
229 36ebd97066296ae6427ea75c2e0846a11a09ccf5370dc80bfecbad28c73f09b3\
230 a3b75e662a2594410ae496b2e2e6609e31e6e02cc837f053d21f37ff4f51950b\
231 be2638d09dd7a4930930806d0703b1f64dd3b4c088a7f45c216839645b2012bf\
232 2e6269a8c56a816dbc1b267761955bc5"
233 );
234 key.decrypt_in_place(&mut buffer, &nonce[..], &aad[..])
235 .unwrap();
236 assert_eq!(buffer, &input[..]);
237 }
238
239 #[test]
240 fn encrypt_expected_ecdh_1pu_cbc_hmac() {
241 let key_data = &hex!(
242 "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0
243 dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0"
244 );
245 let nonce = &hex!("000102030405060708090a0b0c0d0e0f");
246 let protected = "{\"alg\":\"ECDH-1PU+A128KW\",\"enc\":\"A256CBC-HS512\",\
247 \"apu\":\"QWxpY2U\",\"apv\":\"Qm9iIGFuZCBDaGFybGll\",\"epk\":{\
248 \"kty\":\"OKP\",\"crv\":\"X25519\",\
249 \"x\":\"k9of_cpAajy0poW5gaixXGs9nHkwg1AFqUAFa39dyBc\"}}";
250 let aad = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(protected);
251 let input = b"Three is a magic number.";
252 let key = AesKey::<A256CbcHs512>::from_secret_bytes(key_data).unwrap();
253 let mut buffer = SecretBytes::from_slice(input);
254 let ct_len = key
255 .encrypt_in_place(&mut buffer, &nonce[..], aad.as_bytes())
256 .unwrap();
257 let ctext =
258 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&buffer.as_ref()[..ct_len]);
259 let tag =
260 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&buffer.as_ref()[ct_len..]);
261 assert_eq!(ctext, "Az2IWsISEMDJvyc5XRL-3-d-RgNBOGolCsxFFoUXFYw");
262 assert_eq!(tag, "HLb4fTlm8spGmij3RyOs2gJ4DpHM4hhVRwdF_hGb3WQ");
263 key.decrypt_in_place(&mut buffer, &nonce[..], aad.as_bytes())
264 .unwrap();
265 assert_eq!(buffer, &input[..]);
266 }
267}