1 // AES-CCM (Counter with CBC-MAC) 2 // Alternative to GCM mode. 3 // Available in OpenSSL as of TLS 1.3 (2018), but disabled by default. 4 // Two AES computations per block, thus expected to be somewhat slower than AES-GCM. 5 // RFC 6655 year 2012 https://tools.ietf.org/html/rfc6655 6 // Much lower adoption, probably because it came after GCM and offer no significant benefit. 7 8 // https://github.com/RustCrypto/AEADs 9 // https://docs.rs/ccm/0.3.0/ccm/ Or https://crates.io/crates/aes-ccm? 10 11 use rand::Rng; 12 13 use std::io::Cursor; 14 15 use super::*; 16 use crate::content::*; 17 use crate::error::*; 18 use crate::record_layer::record_layer_header::*; 19 20 use aes::Aes128; 21 use ccm::aead::{generic_array::GenericArray, AeadInPlace, NewAead}; 22 use ccm::{ 23 consts::{U12, U16, U8}, 24 Ccm, 25 }; 26 27 const CRYPTO_CCM_8_TAG_LENGTH: usize = 8; 28 const CRYPTO_CCM_TAG_LENGTH: usize = 16; 29 const CRYPTO_CCM_NONCE_LENGTH: usize = 12; 30 31 type AesCcm8 = Ccm<Aes128, U8, U12>; 32 type AesCcm = Ccm<Aes128, U16, U12>; 33 34 #[derive(Clone)] 35 pub enum CryptoCcmTagLen { 36 CryptoCcm8TagLength, 37 CryptoCcmTagLength, 38 } 39 40 enum CryptoCcmType { 41 CryptoCcm8(AesCcm8), 42 CryptoCcm(AesCcm), 43 } 44 45 // State needed to handle encrypted input/output 46 pub struct CryptoCcm { 47 local_ccm: CryptoCcmType, 48 remote_ccm: CryptoCcmType, 49 local_write_iv: Vec<u8>, 50 remote_write_iv: Vec<u8>, 51 // used by clone() 52 local_write_key: Vec<u8>, 53 remote_write_key: Vec<u8>, 54 } 55 56 impl Clone for CryptoCcm { clone(&self) -> Self57 fn clone(&self) -> Self { 58 match self.local_ccm { 59 CryptoCcmType::CryptoCcm(_) => Self::new( 60 &CryptoCcmTagLen::CryptoCcmTagLength, 61 &self.local_write_key, 62 &self.local_write_iv, 63 &self.remote_write_key, 64 &self.remote_write_iv, 65 ), 66 CryptoCcmType::CryptoCcm8(_) => Self::new( 67 &CryptoCcmTagLen::CryptoCcm8TagLength, 68 &self.local_write_key, 69 &self.local_write_iv, 70 &self.remote_write_key, 71 &self.remote_write_iv, 72 ), 73 } 74 } 75 } 76 77 impl CryptoCcm { new( tag_len: &CryptoCcmTagLen, local_key: &[u8], local_write_iv: &[u8], remote_key: &[u8], remote_write_iv: &[u8], ) -> Self78 pub fn new( 79 tag_len: &CryptoCcmTagLen, 80 local_key: &[u8], 81 local_write_iv: &[u8], 82 remote_key: &[u8], 83 remote_write_iv: &[u8], 84 ) -> Self { 85 let key = GenericArray::from_slice(local_key); 86 let local_ccm = match tag_len { 87 CryptoCcmTagLen::CryptoCcmTagLength => CryptoCcmType::CryptoCcm(AesCcm::new(key)), 88 CryptoCcmTagLen::CryptoCcm8TagLength => CryptoCcmType::CryptoCcm8(AesCcm8::new(key)), 89 }; 90 91 let key = GenericArray::from_slice(remote_key); 92 let remote_ccm = match tag_len { 93 CryptoCcmTagLen::CryptoCcmTagLength => CryptoCcmType::CryptoCcm(AesCcm::new(key)), 94 CryptoCcmTagLen::CryptoCcm8TagLength => CryptoCcmType::CryptoCcm8(AesCcm8::new(key)), 95 }; 96 97 CryptoCcm { 98 local_ccm, 99 local_write_key: local_key.to_vec(), 100 local_write_iv: local_write_iv.to_vec(), 101 remote_ccm, 102 remote_write_key: remote_key.to_vec(), 103 remote_write_iv: remote_write_iv.to_vec(), 104 } 105 } 106 encrypt(&self, pkt_rlh: &RecordLayerHeader, raw: &[u8]) -> Result<Vec<u8>>107 pub fn encrypt(&self, pkt_rlh: &RecordLayerHeader, raw: &[u8]) -> Result<Vec<u8>> { 108 let payload = &raw[RECORD_LAYER_HEADER_SIZE..]; 109 let raw = &raw[..RECORD_LAYER_HEADER_SIZE]; 110 111 let mut nonce = vec![0u8; CRYPTO_CCM_NONCE_LENGTH]; 112 nonce[..4].copy_from_slice(&self.local_write_iv[..4]); 113 rand::thread_rng().fill(&mut nonce[4..]); 114 let nonce = GenericArray::from_slice(&nonce); 115 116 let additional_data = generate_aead_additional_data(pkt_rlh, payload.len()); 117 118 let mut buffer: Vec<u8> = Vec::new(); 119 buffer.extend_from_slice(payload); 120 121 match &self.local_ccm { 122 CryptoCcmType::CryptoCcm(ccm) => { 123 ccm.encrypt_in_place(nonce, &additional_data, &mut buffer) 124 .map_err(|e| Error::Other(e.to_string()))?; 125 } 126 CryptoCcmType::CryptoCcm8(ccm8) => { 127 ccm8.encrypt_in_place(nonce, &additional_data, &mut buffer) 128 .map_err(|e| Error::Other(e.to_string()))?; 129 } 130 } 131 132 let mut r = Vec::with_capacity(raw.len() + nonce.len() + buffer.len()); 133 134 r.extend_from_slice(raw); 135 r.extend_from_slice(&nonce[4..]); 136 r.extend_from_slice(&buffer); 137 138 // Update recordLayer size to include explicit nonce 139 let r_len = (r.len() - RECORD_LAYER_HEADER_SIZE) as u16; 140 r[RECORD_LAYER_HEADER_SIZE - 2..RECORD_LAYER_HEADER_SIZE] 141 .copy_from_slice(&r_len.to_be_bytes()); 142 143 Ok(r) 144 } 145 decrypt(&self, r: &[u8]) -> Result<Vec<u8>>146 pub fn decrypt(&self, r: &[u8]) -> Result<Vec<u8>> { 147 let mut reader = Cursor::new(r); 148 let h = RecordLayerHeader::unmarshal(&mut reader)?; 149 if h.content_type == ContentType::ChangeCipherSpec { 150 // Nothing to encrypt with ChangeCipherSpec 151 return Ok(r.to_vec()); 152 } 153 154 if r.len() <= (RECORD_LAYER_HEADER_SIZE + 8) { 155 return Err(Error::ErrNotEnoughRoomForNonce); 156 } 157 158 let mut nonce = vec![]; 159 nonce.extend_from_slice(&self.remote_write_iv[..4]); 160 nonce.extend_from_slice(&r[RECORD_LAYER_HEADER_SIZE..RECORD_LAYER_HEADER_SIZE + 8]); 161 let nonce = GenericArray::from_slice(&nonce); 162 163 let out = &r[RECORD_LAYER_HEADER_SIZE + 8..]; 164 165 let mut buffer: Vec<u8> = Vec::new(); 166 buffer.extend_from_slice(out); 167 168 match &self.remote_ccm { 169 CryptoCcmType::CryptoCcm(ccm) => { 170 let additional_data = 171 generate_aead_additional_data(&h, out.len() - CRYPTO_CCM_TAG_LENGTH); 172 ccm.decrypt_in_place(nonce, &additional_data, &mut buffer) 173 .map_err(|e| Error::Other(e.to_string()))?; 174 } 175 CryptoCcmType::CryptoCcm8(ccm8) => { 176 let additional_data = 177 generate_aead_additional_data(&h, out.len() - CRYPTO_CCM_8_TAG_LENGTH); 178 ccm8.decrypt_in_place(nonce, &additional_data, &mut buffer) 179 .map_err(|e| Error::Other(e.to_string()))?; 180 } 181 } 182 183 let mut d = Vec::with_capacity(RECORD_LAYER_HEADER_SIZE + buffer.len()); 184 d.extend_from_slice(&r[..RECORD_LAYER_HEADER_SIZE]); 185 d.extend_from_slice(&buffer); 186 187 Ok(d) 188 } 189 } 190