Skip to content
File

Blob: firmware/vendor/str0m-rust-crypto/src/srtp.rs

rust323 lines
1//! SRTP cipher implementations using RustCrypto.
2 
3use aes_gcm::aead::generic_array::GenericArray;
4use aes_gcm::aead::{Aead, KeyInit, Payload};
5use aes_gcm::{Aes128Gcm, Aes256Gcm, Nonce};
6use ctr::cipher::{KeyIvInit, StreamCipher};
7 
8use str0m_proto::crypto::CryptoError;
9use str0m_proto::crypto::{AeadAes128Gcm, AeadAes128GcmCipher};
10use str0m_proto::crypto::{AeadAes256Gcm, AeadAes256GcmCipher, Aes128CmSha1_80Cipher};
11use str0m_proto::crypto::{SrtpProvider, SupportedAeadAes128Gcm};
12use str0m_proto::crypto::{SupportedAeadAes256Gcm, SupportedAes128CmSha1_80};
13 
14// Type alias for AES-128 in CTR mode
15type Aes128Ctr = ctr::Ctr128BE<aes::Aes128>;
16 
17// ============================================================================
18// AES-128-CM-SHA1-80 Cipher
19// ============================================================================
20 
21struct RustCryptoAes128CmSha1_80Cipher {
22 key: [u8; 16],
23}
24 
25impl std::fmt::Debug for RustCryptoAes128CmSha1_80Cipher {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("RustCryptoAes128CmSha1_80Cipher").finish()
28 }
29}
30 
31impl Aes128CmSha1_80Cipher for RustCryptoAes128CmSha1_80Cipher {
32 fn encrypt(
33 &mut self,
34 iv: &[u8; 16],
35 input: &[u8],
36 output: &mut [u8],
37 ) -> Result<(), CryptoError> {
38 // AES-128 Counter Mode (CTR)
39 let mut cipher = Aes128Ctr::new(&self.key.into(), iv.into());
40 
41 // Copy input to output
42 output[..input.len()].copy_from_slice(input);
43 
44 // Apply CTR mode encryption (XOR with keystream)
45 cipher.apply_keystream(&mut output[..input.len()]);
46 
47 Ok(())
48 }
49 
50 fn decrypt(
51 &mut self,
52 iv: &[u8; 16],
53 input: &[u8],
54 output: &mut [u8],
55 ) -> Result<(), CryptoError> {
56 // AES-CTR is symmetric (same operation for encrypt and decrypt)
57 self.encrypt(iv, input, output)
58 }
59}
60 
61// ============================================================================
62// AEAD-AES-128-GCM Cipher
63// ============================================================================
64 
65struct RustCryptoAeadAes128GcmCipher {
66 cipher: Aes128Gcm,
67}
68 
69impl std::fmt::Debug for RustCryptoAeadAes128GcmCipher {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.debug_struct("RustCryptoAeadAes128GcmCipher").finish()
72 }
73}
74 
75impl AeadAes128GcmCipher for RustCryptoAeadAes128GcmCipher {
76 fn encrypt(
77 &mut self,
78 iv: &[u8; AeadAes128Gcm::IV_LEN],
79 aad: &[u8],
80 input: &[u8],
81 output: &mut [u8],
82 ) -> Result<(), CryptoError> {
83 assert!(
84 aad.len() >= 12,
85 "Associated data length MUST be at least 12 octets"
86 );
87 
88 let nonce = Nonce::from_slice(iv);
89 let payload = Payload { msg: input, aad };
90 
91 let ciphertext = self
92 .cipher
93 .encrypt(nonce, payload)
94 .map_err(|e| CryptoError::Other(format!("AES-GCM encrypt failed: {:?}", e)))?;
95 
96 output[..ciphertext.len()].copy_from_slice(&ciphertext);
97 Ok(())
98 }
99 
100 fn decrypt(
101 &mut self,
102 iv: &[u8; AeadAes128Gcm::IV_LEN],
103 aads: &[&[u8]],
104 input: &[u8],
105 output: &mut [u8],
106 ) -> Result<usize, CryptoError> {
107 assert!(input.len() >= AeadAes128Gcm::TAG_LEN);
108 
109 let nonce = Nonce::from_slice(iv);
110 
111 // Concatenate AAD slices if needed
112 let aad_vec: Vec<u8>;
113 let aad = if aads.len() == 1 {
114 aads[0]
115 } else {
116 aad_vec = aads.concat();
117 &aad_vec
118 };
119 
120 let payload = Payload { msg: input, aad };
121 
122 let plaintext = self
123 .cipher
124 .decrypt(nonce, payload)
125 .map_err(|e| CryptoError::Other(format!("AES-GCM decrypt failed: {:?}", e)))?;
126 
127 output[..plaintext.len()].copy_from_slice(&plaintext);
128 Ok(plaintext.len())
129 }
130}
131 
132// ============================================================================
133// AEAD-AES-256-GCM Cipher
134// ============================================================================
135 
136struct RustCryptoAeadAes256GcmCipher {
137 cipher: Aes256Gcm,
138}
139 
140impl std::fmt::Debug for RustCryptoAeadAes256GcmCipher {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 f.debug_struct("RustCryptoAeadAes256GcmCipher").finish()
143 }
144}
145 
146impl AeadAes256GcmCipher for RustCryptoAeadAes256GcmCipher {
147 fn encrypt(
148 &mut self,
149 iv: &[u8; AeadAes256Gcm::IV_LEN],
150 aad: &[u8],
151 input: &[u8],
152 output: &mut [u8],
153 ) -> Result<(), CryptoError> {
154 assert!(
155 aad.len() >= 12,
156 "Associated data length MUST be at least 12 octets"
157 );
158 
159 let nonce = Nonce::from_slice(iv);
160 let payload = Payload { msg: input, aad };
161 
162 let ciphertext = self
163 .cipher
164 .encrypt(nonce, payload)
165 .map_err(|e| CryptoError::Other(format!("AES-GCM encrypt failed: {:?}", e)))?;
166 
167 output[..ciphertext.len()].copy_from_slice(&ciphertext);
168 Ok(())
169 }
170 
171 fn decrypt(
172 &mut self,
173 iv: &[u8; AeadAes256Gcm::IV_LEN],
174 aads: &[&[u8]],
175 input: &[u8],
176 output: &mut [u8],
177 ) -> Result<usize, CryptoError> {
178 assert!(input.len() >= AeadAes256Gcm::TAG_LEN);
179 
180 let nonce = Nonce::from_slice(iv);
181 
182 // Concatenate AAD slices if needed
183 let aad_vec: Vec<u8>;
184 let aad = if aads.len() == 1 {
185 aads[0]
186 } else {
187 aad_vec = aads.concat();
188 &aad_vec
189 };
190 
191 let payload = Payload { msg: input, aad };
192 
193 let plaintext = self
194 .cipher
195 .decrypt(nonce, payload)
196 .map_err(|e| CryptoError::Other(format!("AES-GCM decrypt failed: {:?}", e)))?;
197 
198 output[..plaintext.len()].copy_from_slice(&plaintext);
199 Ok(plaintext.len())
200 }
201}
202 
203// ============================================================================
204// SRTP Profile Support Implementations
205// ============================================================================
206 
207struct RustCryptoSupportedAes128CmSha1_80;
208 
209impl std::fmt::Debug for RustCryptoSupportedAes128CmSha1_80 {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 f.debug_struct("RustCryptoSupportedAes128CmSha1_80")
212 .finish()
213 }
214}
215 
216impl SupportedAes128CmSha1_80 for RustCryptoSupportedAes128CmSha1_80 {
217 fn create_cipher(&self, key: [u8; 16], _encrypt: bool) -> Box<dyn Aes128CmSha1_80Cipher> {
218 Box::new(RustCryptoAes128CmSha1_80Cipher { key })
219 }
220}
221 
222struct RustCryptoSupportedAeadAes128Gcm;
223 
224impl std::fmt::Debug for RustCryptoSupportedAeadAes128Gcm {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 f.debug_struct("RustCryptoSupportedAeadAes128Gcm").finish()
227 }
228}
229 
230impl SupportedAeadAes128Gcm for RustCryptoSupportedAeadAes128Gcm {
231 fn create_cipher(&self, key: [u8; 16], _encrypt: bool) -> Box<dyn AeadAes128GcmCipher> {
232 let cipher = Aes128Gcm::new(GenericArray::from_slice(&key));
233 Box::new(RustCryptoAeadAes128GcmCipher { cipher })
234 }
235}
236 
237struct RustCryptoSupportedAeadAes256Gcm;
238 
239impl std::fmt::Debug for RustCryptoSupportedAeadAes256Gcm {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 f.debug_struct("RustCryptoSupportedAeadAes256Gcm").finish()
242 }
243}
244 
245impl SupportedAeadAes256Gcm for RustCryptoSupportedAeadAes256Gcm {
246 fn create_cipher(&self, key: [u8; 32], _encrypt: bool) -> Box<dyn AeadAes256GcmCipher> {
247 let cipher = Aes256Gcm::new(GenericArray::from_slice(&key));
248 Box::new(RustCryptoAeadAes256GcmCipher { cipher })
249 }
250}
251 
252// ============================================================================
253// SRTP Provider Implementation
254// ============================================================================
255 
256pub(super) struct RustCryptoSrtpProvider;
257 
258impl std::fmt::Debug for RustCryptoSrtpProvider {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.debug_struct("RustCryptoSrtpProvider").finish()
261 }
262}
263 
264impl SrtpProvider for RustCryptoSrtpProvider {
265 fn aes_128_cm_sha1_80(&self) -> &'static dyn SupportedAes128CmSha1_80 {
266 &RustCryptoSupportedAes128CmSha1_80
267 }
268 
269 fn aead_aes_128_gcm(&self) -> &'static dyn SupportedAeadAes128Gcm {
270 &RustCryptoSupportedAeadAes128Gcm
271 }
272 
273 fn aead_aes_256_gcm(&self) -> &'static dyn SupportedAeadAes256Gcm {
274 &RustCryptoSupportedAeadAes256Gcm
275 }
276 
277 fn srtp_aes_128_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]) {
278 // Use aes crate for ECB mode
279 use aes::Aes128;
280 use aes::cipher::{BlockEncrypt, KeyInit};
281 
282 let cipher = Aes128::new(GenericArray::from_slice(key));
283 
284 // Input is 16 bytes, output needs to be 32 bytes (with PKCS7 padding)
285 // For a 16-byte input, PKCS7 adds a full block of padding (16 bytes of 0x10)
286 assert!(input.len() == 16);
287 assert!(output.len() >= 32);
288 
289 // First block: encrypt the input
290 let mut block1 = *GenericArray::from_slice(&input[0..16]);
291 cipher.encrypt_block(&mut block1);
292 output[0..16].copy_from_slice(&block1);
293 
294 // Second block: PKCS7 padding (16 bytes of 0x10)
295 let mut block2 = GenericArray::from([0x10u8; 16]);
296 cipher.encrypt_block(&mut block2);
297 output[16..32].copy_from_slice(&block2);
298 }
299 
300 fn srtp_aes_256_ecb_round(&self, key: &[u8], input: &[u8], output: &mut [u8]) {
301 // Use aes crate for ECB mode
302 use aes::Aes256;
303 use aes::cipher::{BlockEncrypt, KeyInit};
304 
305 let cipher = Aes256::new(GenericArray::from_slice(key));
306 
307 // Input is 16 bytes, output needs to be 32 bytes (with PKCS7 padding)
308 // For a 16-byte input, PKCS7 adds a full block of padding (16 bytes of 0x10)
309 assert!(input.len() == 16);
310 assert!(output.len() >= 32);
311 
312 // First block: encrypt the input
313 let mut block1 = *GenericArray::from_slice(&input[0..16]);
314 cipher.encrypt_block(&mut block1);
315 output[0..16].copy_from_slice(&block1);
316 
317 // Second block: PKCS7 padding (16 bytes of 0x10)
318 let mut block2 = GenericArray::from([0x10u8; 16]);
319 cipher.encrypt_block(&mut block2);
320 output[16..32].copy_from_slice(&block2);
321 }
322}