webrtc_dtls/crypto/
crypto_ccm.rs

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