|
| 1 | +#[cfg(feature = "alloc")] |
| 2 | +use alloc::boxed::Box; |
| 3 | + |
| 4 | +use super::{DecryptBufferAdapter, EncryptBufferAdapter}; |
| 5 | + |
| 6 | +use aead::AeadInOut; |
| 7 | +use aes::{Aes128, Aes256}; |
| 8 | +use ccm::{ |
| 9 | + Ccm, |
| 10 | + consts::{U8, U12, U16}, |
| 11 | +}; |
| 12 | +use crypto_common::{KeyInit, KeySizeUser}; |
| 13 | +use paste::paste; |
| 14 | +use rustls::crypto::cipher::{ |
| 15 | + self, AeadKey, InboundOpaqueMessage, InboundPlainMessage, MessageDecrypter, MessageEncrypter, |
| 16 | + OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, Tls13AeadAlgorithm, |
| 17 | + UnsupportedOperationError, |
| 18 | +}; |
| 19 | +use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; |
| 20 | + |
| 21 | +#[cfg(feature = "tls12")] |
| 22 | +use { |
| 23 | + aead::AeadCore, |
| 24 | + crypto_common::typenum::Unsigned, |
| 25 | + rustls::crypto::cipher::{KeyBlockShape, Tls12AeadAlgorithm}, |
| 26 | +}; |
| 27 | + |
| 28 | +/// AES-128-CCM with 16-byte tag and 12-byte nonce (RFC 5116 / RFC 6655). |
| 29 | +pub type Aes128Ccm = Ccm<Aes128, U16, U12>; |
| 30 | +/// AES-256-CCM with 16-byte tag and 12-byte nonce. |
| 31 | +pub type Aes256Ccm = Ccm<Aes256, U16, U12>; |
| 32 | +/// AES-128-CCM with 8-byte tag and 12-byte nonce (CCM-8). |
| 33 | +pub type Aes128Ccm8 = Ccm<Aes128, U8, U12>; |
| 34 | +/// AES-256-CCM with 8-byte tag and 12-byte nonce (CCM-8). |
| 35 | +pub type Aes256Ccm8 = Ccm<Aes256, U8, U12>; |
| 36 | + |
| 37 | +#[cfg(feature = "tls12")] |
| 38 | +const TLS12_CCM_EXPLICIT_NONCE_LEN: usize = 8; |
| 39 | + |
| 40 | +#[cfg(feature = "tls12")] |
| 41 | +const TLS12_CCM_OVERHEAD: usize = TLS12_CCM_EXPLICIT_NONCE_LEN + 16; |
| 42 | + |
| 43 | +#[cfg(feature = "tls12")] |
| 44 | +const TLS12_CCM_8_OVERHEAD: usize = TLS12_CCM_EXPLICIT_NONCE_LEN + 8; |
| 45 | + |
| 46 | +macro_rules! impl_ccm_tls13 { |
| 47 | + ($name: ident, $aead: ty, $overhead: expr) => { |
| 48 | + paste! { |
| 49 | + pub struct [<Tls13 $name>]; |
| 50 | + |
| 51 | + impl Tls13AeadAlgorithm for [<Tls13 $name>] { |
| 52 | + fn encrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box<dyn MessageEncrypter> { |
| 53 | + Box::new([<Tls13Cipher $name>]( |
| 54 | + $aead::new_from_slice(key.as_ref()).unwrap(), |
| 55 | + iv, |
| 56 | + )) |
| 57 | + } |
| 58 | + |
| 59 | + fn decrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box<dyn MessageDecrypter> { |
| 60 | + Box::new([<Tls13Cipher $name>]( |
| 61 | + $aead::new_from_slice(key.as_ref()).unwrap(), |
| 62 | + iv, |
| 63 | + )) |
| 64 | + } |
| 65 | + |
| 66 | + fn key_len(&self) -> usize { |
| 67 | + $aead::key_size() |
| 68 | + } |
| 69 | + |
| 70 | + fn extract_keys( |
| 71 | + &self, |
| 72 | + _key: AeadKey, |
| 73 | + _iv: cipher::Iv, |
| 74 | + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { |
| 75 | + // rustls::ConnectionTrafficSecrets has no CCM variants. |
| 76 | + Err(UnsupportedOperationError) |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + struct [<Tls13Cipher $name>]($aead, cipher::Iv); |
| 81 | + |
| 82 | + impl MessageEncrypter for [<Tls13Cipher $name>] { |
| 83 | + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result<OutboundOpaqueMessage, rustls::Error> { |
| 84 | + let total_len = self.encrypted_payload_len(m.payload.len()); |
| 85 | + let mut payload = PrefixedPayload::with_capacity(total_len); |
| 86 | + |
| 87 | + let nonce = cipher::Nonce::new(&self.1, seq).0; |
| 88 | + let aad = cipher::make_tls13_aad(total_len); |
| 89 | + payload.extend_from_chunks(&m.payload); |
| 90 | + payload.extend_from_slice(&m.typ.to_array()); |
| 91 | + |
| 92 | + self.0 |
| 93 | + .encrypt_in_place(&nonce.into(), &aad, &mut EncryptBufferAdapter(&mut payload)) |
| 94 | + .map_err(|_| rustls::Error::EncryptError) |
| 95 | + .map(|_| OutboundOpaqueMessage::new( |
| 96 | + ContentType::ApplicationData, |
| 97 | + ProtocolVersion::TLSv1_2, |
| 98 | + payload, |
| 99 | + )) |
| 100 | + } |
| 101 | + |
| 102 | + fn encrypted_payload_len(&self, payload_len: usize) -> usize { |
| 103 | + payload_len + 1 + $overhead |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + impl MessageDecrypter for [<Tls13Cipher $name>] { |
| 108 | + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result<InboundPlainMessage<'a>, rustls::Error> { |
| 109 | + let payload = &mut m.payload; |
| 110 | + let nonce = cipher::Nonce::new(&self.1, seq).0; |
| 111 | + let aad = cipher::make_tls13_aad(payload.len()); |
| 112 | + |
| 113 | + self.0 |
| 114 | + .decrypt_in_place(&nonce.into(), &aad, &mut DecryptBufferAdapter(payload)) |
| 115 | + .map_err(|_| rustls::Error::DecryptError)?; |
| 116 | + |
| 117 | + m.into_tls13_unpadded_message() |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + }; |
| 122 | +} |
| 123 | + |
| 124 | +#[cfg(feature = "tls12")] |
| 125 | +macro_rules! impl_ccm_tls12 { |
| 126 | + ($name: ident, $aead: ty, $nonce: expr, $overhead: expr) => { |
| 127 | + paste! { |
| 128 | + #[cfg(feature = "tls12")] |
| 129 | + pub struct [<Tls12 $name>]; |
| 130 | + |
| 131 | + #[cfg(feature = "tls12")] |
| 132 | + impl Tls12AeadAlgorithm for [<Tls12 $name>] { |
| 133 | + fn encrypter(&self, key: AeadKey, write_iv: &[u8], explicit: &[u8]) -> Box<dyn MessageEncrypter> { |
| 134 | + Box::new([<Tls12Cipher $name Encrypter>]( |
| 135 | + $aead::new_from_slice(key.as_ref()).unwrap(), |
| 136 | + { |
| 137 | + let mut iv: [u8; 12] = [0; 12]; |
| 138 | + iv[..4].copy_from_slice(write_iv); |
| 139 | + iv[4..].copy_from_slice(explicit); |
| 140 | + iv |
| 141 | + }, |
| 142 | + )) |
| 143 | + } |
| 144 | + |
| 145 | + fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box<dyn MessageDecrypter> { |
| 146 | + Box::new([<Tls12Cipher $name Decrypter>]( |
| 147 | + $aead::new_from_slice(dec_key.as_ref()).unwrap(), |
| 148 | + dec_iv.try_into().unwrap(), |
| 149 | + )) |
| 150 | + } |
| 151 | + |
| 152 | + fn key_block_shape(&self) -> KeyBlockShape { |
| 153 | + KeyBlockShape { |
| 154 | + enc_key_len: $aead::key_size(), |
| 155 | + fixed_iv_len: 4, |
| 156 | + explicit_nonce_len: 8, |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + fn extract_keys( |
| 161 | + &self, |
| 162 | + _key: AeadKey, |
| 163 | + _iv: &[u8], |
| 164 | + _explicit: &[u8], |
| 165 | + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { |
| 166 | + // rustls::ConnectionTrafficSecrets has no CCM variants. |
| 167 | + Err(UnsupportedOperationError) |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + #[cfg(feature = "tls12")] |
| 172 | + struct [<Tls12Cipher $name Encrypter>]($aead, [u8; 12]); |
| 173 | + |
| 174 | + #[cfg(feature = "tls12")] |
| 175 | + impl MessageEncrypter for [<Tls12Cipher $name Encrypter>] { |
| 176 | + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result<OutboundOpaqueMessage, rustls::Error> { |
| 177 | + let total_len = self.encrypted_payload_len(m.payload.len()); |
| 178 | + let mut payload = PrefixedPayload::with_capacity(total_len); |
| 179 | + |
| 180 | + let nonce = cipher::Nonce::new(&self.1.into(), seq).0; |
| 181 | + let aad = cipher::make_tls12_aad(seq, m.typ, m.version, m.payload.len()); |
| 182 | + payload.extend_from_slice(&nonce.as_ref()[4..]); // explicit |
| 183 | + payload.extend_from_chunks(&m.payload); |
| 184 | + |
| 185 | + self.0 |
| 186 | + .encrypt_inout_detached(&nonce.into(), &aad, (&mut payload.as_mut()[$nonce..]).into()) |
| 187 | + .map(|tag| payload.extend(tag.as_ref() as &[u8])) |
| 188 | + .map_err(|_| rustls::Error::EncryptError) |
| 189 | + .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) |
| 190 | + } |
| 191 | + |
| 192 | + fn encrypted_payload_len(&self, payload_len: usize) -> usize { |
| 193 | + payload_len + $nonce + <$aead as AeadCore>::TagSize::USIZE |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + #[cfg(feature = "tls12")] |
| 198 | + struct [<Tls12Cipher $name Decrypter>]($aead, [u8; 4]); |
| 199 | + |
| 200 | + #[cfg(feature = "tls12")] |
| 201 | + impl MessageDecrypter for [<Tls12Cipher $name Decrypter>] { |
| 202 | + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result<InboundPlainMessage<'a>, rustls::Error> { |
| 203 | + type TagSize = <$aead as AeadCore>::TagSize; |
| 204 | + |
| 205 | + let payload = &m.payload; |
| 206 | + |
| 207 | + if payload.len() < $overhead { |
| 208 | + return Err(rustls::Error::DecryptError); |
| 209 | + } |
| 210 | + |
| 211 | + let nonce: aead::Nonce<$aead> = { |
| 212 | + let mut nonce = [0u8; 12]; |
| 213 | + nonce[..4].copy_from_slice(&self.1); // dec_iv |
| 214 | + nonce[4..].copy_from_slice(&payload[..$nonce]); |
| 215 | + nonce.into() |
| 216 | + }; |
| 217 | + |
| 218 | + let aad = cipher::make_tls12_aad(seq, m.typ, m.version, payload.len() - $overhead); |
| 219 | + |
| 220 | + let payload = &mut m.payload; |
| 221 | + let tag_pos = { |
| 222 | + let payload = &mut payload[$nonce..]; |
| 223 | + let tag_pos = payload.len() - TagSize::to_usize(); |
| 224 | + let (msg, tag) = payload.split_at_mut(tag_pos); |
| 225 | + |
| 226 | + let tag = ccm::Tag::<TagSize>::try_from(&*tag) |
| 227 | + .map_err(|_| rustls::Error::DecryptError)?; |
| 228 | + self.0 |
| 229 | + .decrypt_inout_detached(&nonce, &aad, msg.into(), &tag) |
| 230 | + .map_err(|_| rustls::Error::DecryptError)?; |
| 231 | + tag_pos |
| 232 | + }; |
| 233 | + |
| 234 | + // Defer truncation until after successful decrypt so a failure cannot |
| 235 | + // leave the buffer shifted relative to the wire layout. |
| 236 | + payload.rotate_left($nonce); |
| 237 | + payload.truncate(tag_pos); |
| 238 | + Ok(m.into_plain_message()) |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + }; |
| 243 | +} |
| 244 | + |
| 245 | +impl_ccm_tls13! {Aes128Ccm, Aes128Ccm, 16} |
| 246 | +impl_ccm_tls13! {Aes128Ccm8, Aes128Ccm8, 8} |
| 247 | + |
| 248 | +#[cfg(feature = "tls12")] |
| 249 | +impl_ccm_tls12! {Aes128Ccm, Aes128Ccm, TLS12_CCM_EXPLICIT_NONCE_LEN, TLS12_CCM_OVERHEAD} |
| 250 | + |
| 251 | +#[cfg(feature = "tls12")] |
| 252 | +impl_ccm_tls12! {Aes256Ccm, Aes256Ccm, TLS12_CCM_EXPLICIT_NONCE_LEN, TLS12_CCM_OVERHEAD} |
| 253 | + |
| 254 | +#[cfg(feature = "tls12")] |
| 255 | +impl_ccm_tls12! {Aes128Ccm8, Aes128Ccm8, TLS12_CCM_EXPLICIT_NONCE_LEN, TLS12_CCM_8_OVERHEAD} |
| 256 | + |
| 257 | +#[cfg(feature = "tls12")] |
| 258 | +impl_ccm_tls12! {Aes256Ccm8, Aes256Ccm8, TLS12_CCM_EXPLICIT_NONCE_LEN, TLS12_CCM_8_OVERHEAD} |
0 commit comments