xref: /webrtc/dtls/src/crypto/crypto_ccm.rs (revision ffe74184)
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