Skip to content

Commit 2ac65a4

Browse files
authored
Make SRTP AES_256_GCM actually work (#677)
* Revert "Revert "add AeadAes256Gcm support"" This reverts commit 4478244. * add back support for aes256 * use AES_256_CM_PRF for AES_256_GCM * formatting * clippy * add mapping for Aes128CmHmacSha1_32 * fix clippy/fmt
1 parent bb3b8a1 commit 2ac65a4

File tree

14 files changed

+383
-94
lines changed

14 files changed

+383
-94
lines changed

srtp/src/cipher/cipher_aead_aes_gcm.rs

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,54 @@
1+
use std::marker::PhantomData;
2+
3+
use aead::consts::{U12, U16};
4+
use aes::cipher::{BlockEncrypt, BlockSizeUser, Unsigned};
15
use aes_gcm::aead::generic_array::GenericArray;
26
use aes_gcm::aead::{Aead, Payload};
3-
use aes_gcm::{Aes128Gcm, KeyInit, Nonce};
7+
use aes_gcm::{AesGcm, KeyInit, Nonce};
48
use byteorder::{BigEndian, ByteOrder};
59
use bytes::{Bytes, BytesMut};
610
use util::marshal::*;
711

812
use super::Cipher;
913
use crate::error::{Error, Result};
1014
use crate::key_derivation::*;
15+
use crate::protection_profile::ProtectionProfile;
1116

1217
pub const CIPHER_AEAD_AES_GCM_AUTH_TAG_LEN: usize = 16;
1318

1419
const RTCP_ENCRYPTION_FLAG: u8 = 0x80;
1520

1621
/// AEAD Cipher based on AES.
17-
pub(crate) struct CipherAeadAesGcm {
18-
srtp_cipher: aes_gcm::Aes128Gcm,
19-
srtcp_cipher: aes_gcm::Aes128Gcm,
22+
pub(crate) struct CipherAeadAesGcm<AES, NonceSize = U12>
23+
where
24+
NonceSize: Unsigned,
25+
{
26+
profile: ProtectionProfile,
27+
srtp_cipher: aes_gcm::AesGcm<AES, NonceSize>,
28+
srtcp_cipher: aes_gcm::AesGcm<AES, NonceSize>,
2029
srtp_session_salt: Vec<u8>,
2130
srtcp_session_salt: Vec<u8>,
31+
_tag: PhantomData<AES>,
2232
}
2333

24-
impl Cipher for CipherAeadAesGcm {
25-
fn auth_tag_len(&self) -> usize {
26-
CIPHER_AEAD_AES_GCM_AUTH_TAG_LEN
34+
impl<AES, NS> Cipher for CipherAeadAesGcm<AES, NS>
35+
where
36+
NS: Unsigned,
37+
AES: BlockEncrypt + KeyInit + BlockSizeUser<BlockSize = U16> + 'static,
38+
AesGcm<AES, NS>: Aead,
39+
{
40+
fn rtp_auth_tag_len(&self) -> usize {
41+
self.profile.rtp_auth_tag_len()
42+
}
43+
44+
/// Get RTCP authenticated tag length.
45+
fn rtcp_auth_tag_len(&self) -> usize {
46+
self.profile.rtcp_auth_tag_len()
47+
}
48+
49+
/// Get AEAD auth key length of the cipher.
50+
fn aead_auth_tag_len(&self) -> usize {
51+
self.profile.aead_auth_tag_len()
2752
}
2853

2954
fn encrypt_rtp(
@@ -34,7 +59,7 @@ impl Cipher for CipherAeadAesGcm {
3459
) -> Result<Bytes> {
3560
// Grow the given buffer to fit the output.
3661
let header_len = header.marshal_size();
37-
let mut writer = BytesMut::with_capacity(payload.len() + self.auth_tag_len());
62+
let mut writer = BytesMut::with_capacity(payload.len() + self.aead_auth_tag_len());
3863

3964
// Copy header unencrypted.
4065
writer.extend_from_slice(&payload[..header_len]);
@@ -59,7 +84,7 @@ impl Cipher for CipherAeadAesGcm {
5984
header: &rtp::header::Header,
6085
roc: u32,
6186
) -> Result<Bytes> {
62-
if ciphertext.len() < self.auth_tag_len() {
87+
if ciphertext.len() < self.aead_auth_tag_len() {
6388
return Err(Error::ErrFailedToVerifyAuthTag);
6489
}
6590

@@ -101,7 +126,7 @@ impl Cipher for CipherAeadAesGcm {
101126
}
102127

103128
fn decrypt_rtcp(&mut self, encrypted: &[u8], srtcp_index: usize, ssrc: u32) -> Result<Bytes> {
104-
if encrypted.len() < self.auth_tag_len() + SRTCP_INDEX_SIZE {
129+
if encrypted.len() < self.aead_auth_tag_len() + SRTCP_INDEX_SIZE {
105130
return Err(Error::ErrFailedToVerifyAuthTag);
106131
}
107132

@@ -131,10 +156,31 @@ impl Cipher for CipherAeadAesGcm {
131156
}
132157
}
133158

134-
impl CipherAeadAesGcm {
159+
impl<AES, NS> CipherAeadAesGcm<AES, NS>
160+
where
161+
NS: Unsigned,
162+
AES: BlockEncrypt + KeyInit + BlockSizeUser<BlockSize = U16> + 'static,
163+
AesGcm<AES, NS>: Aead,
164+
{
135165
/// Create a new AEAD instance.
136-
pub(crate) fn new(master_key: &[u8], master_salt: &[u8]) -> Result<CipherAeadAesGcm> {
137-
let srtp_session_key = aes_cm_key_derivation(
166+
pub(crate) fn new(
167+
profile: ProtectionProfile,
168+
master_key: &[u8],
169+
master_salt: &[u8],
170+
) -> Result<CipherAeadAesGcm<AES>> {
171+
assert_eq!(profile.aead_auth_tag_len(), AES::block_size());
172+
assert_eq!(profile.key_len(), AES::key_size());
173+
assert_eq!(profile.salt_len(), master_salt.len());
174+
175+
type Kdf = fn(u8, &[u8], &[u8], usize, usize) -> Result<Vec<u8>>;
176+
let kdf: Kdf = match profile {
177+
ProtectionProfile::AeadAes128Gcm => aes_cm_key_derivation,
178+
// AES_256_GCM must use AES_256_CM_PRF as per https://datatracker.ietf.org/doc/html/rfc7714#section-11
179+
ProtectionProfile::AeadAes256Gcm => aes_256_cm_key_derivation,
180+
_ => unreachable!(),
181+
};
182+
183+
let srtp_session_key = kdf(
138184
LABEL_SRTP_ENCRYPTION,
139185
master_key,
140186
master_salt,
@@ -144,9 +190,9 @@ impl CipherAeadAesGcm {
144190

145191
let srtp_block = GenericArray::from_slice(&srtp_session_key);
146192

147-
let srtp_cipher = Aes128Gcm::new(srtp_block);
193+
let srtp_cipher = AesGcm::<AES, U12>::new(srtp_block);
148194

149-
let srtcp_session_key = aes_cm_key_derivation(
195+
let srtcp_session_key = kdf(
150196
LABEL_SRTCP_ENCRYPTION,
151197
master_key,
152198
master_salt,
@@ -156,29 +202,31 @@ impl CipherAeadAesGcm {
156202

157203
let srtcp_block = GenericArray::from_slice(&srtcp_session_key);
158204

159-
let srtcp_cipher = Aes128Gcm::new(srtcp_block);
205+
let srtcp_cipher = AesGcm::<AES, U12>::new(srtcp_block);
160206

161-
let srtp_session_salt = aes_cm_key_derivation(
207+
let srtp_session_salt = kdf(
162208
LABEL_SRTP_SALT,
163209
master_key,
164210
master_salt,
165211
0,
166-
master_key.len(),
212+
master_salt.len(),
167213
)?;
168214

169-
let srtcp_session_salt = aes_cm_key_derivation(
215+
let srtcp_session_salt = kdf(
170216
LABEL_SRTCP_SALT,
171217
master_key,
172218
master_salt,
173219
0,
174-
master_key.len(),
220+
master_salt.len(),
175221
)?;
176222

177223
Ok(CipherAeadAesGcm {
224+
profile,
178225
srtp_cipher,
179226
srtcp_cipher,
180227
srtp_session_salt,
181228
srtcp_session_salt,
229+
_tag: PhantomData,
182230
})
183231
}
184232

@@ -245,3 +293,52 @@ impl CipherAeadAesGcm {
245293
aad
246294
}
247295
}
296+
297+
#[cfg(test)]
298+
mod tests {
299+
use aes::{Aes128, Aes256};
300+
301+
use super::*;
302+
303+
#[test]
304+
fn test_aead_aes_gcm_128() {
305+
let profile = ProtectionProfile::AeadAes128Gcm;
306+
let master_key = vec![0u8; profile.key_len()];
307+
let master_salt = vec![0u8; 12];
308+
309+
let mut cipher =
310+
CipherAeadAesGcm::<Aes128>::new(profile, &master_key, &master_salt).unwrap();
311+
312+
let header = rtp::header::Header {
313+
ssrc: 0x12345678,
314+
..Default::default()
315+
};
316+
317+
let payload = vec![0u8; 100];
318+
let encrypted = cipher.encrypt_rtp(&payload, &header, 0).unwrap();
319+
320+
let decrypted = cipher.decrypt_rtp(&encrypted, &header, 0).unwrap();
321+
assert_eq!(&decrypted[..], &payload[..]);
322+
}
323+
324+
#[test]
325+
fn test_aead_aes_gcm_256() {
326+
let profile = ProtectionProfile::AeadAes256Gcm;
327+
let master_key = vec![0u8; profile.key_len()];
328+
let master_salt = vec![0u8; 12];
329+
330+
let mut cipher =
331+
CipherAeadAesGcm::<Aes256>::new(profile, &master_key, &master_salt).unwrap();
332+
333+
let header = rtp::header::Header {
334+
ssrc: 0x12345678,
335+
..Default::default()
336+
};
337+
338+
let payload = vec![0u8; 100];
339+
let encrypted = cipher.encrypt_rtp(&payload, &header, 0).unwrap();
340+
341+
let decrypted = cipher.decrypt_rtp(&encrypted, &header, 0).unwrap();
342+
assert_eq!(&decrypted[..], &payload[..]);
343+
}
344+
}

srtp/src/cipher/cipher_aes_cm_hmac_sha1/ctrcipher.rs

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use util::marshal::*;
88
use super::{Cipher, CipherInner};
99
use crate::error::{Error, Result};
1010
use crate::key_derivation::*;
11+
use crate::protection_profile::ProtectionProfile;
1112

1213
type Aes128Ctr = ctr::Ctr128BE<aes::Aes128>;
1314

@@ -18,8 +19,8 @@ pub(crate) struct CipherAesCmHmacSha1 {
1819
}
1920

2021
impl CipherAesCmHmacSha1 {
21-
pub fn new(master_key: &[u8], master_salt: &[u8]) -> Result<Self> {
22-
let inner = CipherInner::new(master_key, master_salt)?;
22+
pub fn new(profile: ProtectionProfile, master_key: &[u8], master_salt: &[u8]) -> Result<Self> {
23+
let inner = CipherInner::new(profile, master_key, master_salt)?;
2324

2425
let srtp_session_key = aes_cm_key_derivation(
2526
LABEL_SRTP_ENCRYPTION,
@@ -45,8 +46,19 @@ impl CipherAesCmHmacSha1 {
4546
}
4647

4748
impl Cipher for CipherAesCmHmacSha1 {
48-
fn auth_tag_len(&self) -> usize {
49-
self.inner.auth_tag_len()
49+
/// Get RTP authenticated tag length.
50+
fn rtp_auth_tag_len(&self) -> usize {
51+
self.inner.profile.rtp_auth_tag_len()
52+
}
53+
54+
/// Get RTCP authenticated tag length.
55+
fn rtcp_auth_tag_len(&self) -> usize {
56+
self.inner.profile.rtcp_auth_tag_len()
57+
}
58+
59+
/// Get AEAD auth key length of the cipher.
60+
fn aead_auth_tag_len(&self) -> usize {
61+
self.inner.profile.aead_auth_tag_len()
5062
}
5163

5264
fn get_rtcp_index(&self, input: &[u8]) -> usize {
@@ -59,7 +71,7 @@ impl Cipher for CipherAesCmHmacSha1 {
5971
header: &rtp::header::Header,
6072
roc: u32,
6173
) -> Result<Bytes> {
62-
let mut writer = Vec::with_capacity(plaintext.len() + self.auth_tag_len());
74+
let mut writer = Vec::with_capacity(plaintext.len() + self.rtp_auth_tag_len());
6375

6476
// Write the plaintext to the destination buffer.
6577
writer.extend_from_slice(plaintext);
@@ -77,7 +89,7 @@ impl Cipher for CipherAesCmHmacSha1 {
7789
stream.apply_keystream(&mut writer[header.marshal_size()..]);
7890

7991
// Generate the auth tag.
80-
let auth_tag = &self.inner.generate_srtp_auth_tag(&writer, roc)[..self.auth_tag_len()];
92+
let auth_tag = &self.inner.generate_srtp_auth_tag(&writer, roc)[..self.rtp_auth_tag_len()];
8193
writer.extend(auth_tag);
8294

8395
Ok(Bytes::from(writer))
@@ -90,19 +102,19 @@ impl Cipher for CipherAesCmHmacSha1 {
90102
roc: u32,
91103
) -> Result<Bytes> {
92104
let encrypted_len = encrypted.len();
93-
if encrypted_len < self.auth_tag_len() {
94-
return Err(Error::SrtpTooSmall(encrypted_len, self.auth_tag_len()));
105+
if encrypted_len < self.rtp_auth_tag_len() {
106+
return Err(Error::SrtpTooSmall(encrypted_len, self.rtp_auth_tag_len()));
95107
}
96108

97-
let mut writer = Vec::with_capacity(encrypted_len - self.auth_tag_len());
109+
let mut writer = Vec::with_capacity(encrypted_len - self.rtp_auth_tag_len());
98110

99111
// Split the auth tag and the cipher text into two parts.
100-
let actual_tag = &encrypted[encrypted_len - self.auth_tag_len()..];
101-
let cipher_text = &encrypted[..encrypted_len - self.auth_tag_len()];
112+
let actual_tag = &encrypted[encrypted_len - self.rtp_auth_tag_len()..];
113+
let cipher_text = &encrypted[..encrypted_len - self.rtp_auth_tag_len()];
102114

103115
// Generate the auth tag we expect to see from the ciphertext.
104116
let expected_tag =
105-
&self.inner.generate_srtp_auth_tag(cipher_text, roc)[..self.auth_tag_len()];
117+
&self.inner.generate_srtp_auth_tag(cipher_text, roc)[..self.rtp_auth_tag_len()];
106118

107119
// See if the auth tag actually matches.
108120
// We use a constant time comparison to prevent timing attacks.
@@ -132,7 +144,7 @@ impl Cipher for CipherAesCmHmacSha1 {
132144

133145
fn encrypt_rtcp(&mut self, decrypted: &[u8], srtcp_index: usize, ssrc: u32) -> Result<Bytes> {
134146
let mut writer =
135-
Vec::with_capacity(decrypted.len() + SRTCP_INDEX_SIZE + self.auth_tag_len());
147+
Vec::with_capacity(decrypted.len() + SRTCP_INDEX_SIZE + self.rtcp_auth_tag_len());
136148

137149
// Write the decrypted to the destination buffer.
138150
writer.extend_from_slice(decrypted);
@@ -155,22 +167,22 @@ impl Cipher for CipherAesCmHmacSha1 {
155167
writer.put_u32(srtcp_index as u32 | (1u32 << 31));
156168

157169
// Generate the auth tag.
158-
let auth_tag = &self.inner.generate_srtcp_auth_tag(&writer)[..self.auth_tag_len()];
170+
let auth_tag = &self.inner.generate_srtcp_auth_tag(&writer)[..self.rtcp_auth_tag_len()];
159171
writer.extend(auth_tag);
160172

161173
Ok(Bytes::from(writer))
162174
}
163175

164176
fn decrypt_rtcp(&mut self, encrypted: &[u8], srtcp_index: usize, ssrc: u32) -> Result<Bytes> {
165177
let encrypted_len = encrypted.len();
166-
if encrypted_len < self.auth_tag_len() + SRTCP_INDEX_SIZE {
178+
if encrypted_len < self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE {
167179
return Err(Error::SrtcpTooSmall(
168180
encrypted_len,
169-
self.auth_tag_len() + SRTCP_INDEX_SIZE,
181+
self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE,
170182
));
171183
}
172184

173-
let tail_offset = encrypted_len - (self.auth_tag_len() + SRTCP_INDEX_SIZE);
185+
let tail_offset = encrypted_len - (self.rtcp_auth_tag_len() + SRTCP_INDEX_SIZE);
174186
if tail_offset < 8 {
175187
return Err(Error::ErrTooShortRtcp);
176188
}
@@ -185,18 +197,19 @@ impl Cipher for CipherAesCmHmacSha1 {
185197
}
186198

187199
// Split the auth tag and the cipher text into two parts.
188-
let actual_tag = &encrypted[encrypted_len - self.auth_tag_len()..];
189-
if actual_tag.len() != self.auth_tag_len() {
200+
let actual_tag = &encrypted[encrypted_len - self.rtcp_auth_tag_len()..];
201+
if actual_tag.len() != self.rtcp_auth_tag_len() {
190202
return Err(Error::RtcpInvalidLengthAuthTag(
191203
actual_tag.len(),
192-
self.auth_tag_len(),
204+
self.rtcp_auth_tag_len(),
193205
));
194206
}
195207

196-
let cipher_text = &encrypted[..encrypted_len - self.auth_tag_len()];
208+
let cipher_text = &encrypted[..encrypted_len - self.rtcp_auth_tag_len()];
197209

198210
// Generate the auth tag we expect to see from the ciphertext.
199-
let expected_tag = &self.inner.generate_srtcp_auth_tag(cipher_text)[..self.auth_tag_len()];
211+
let expected_tag =
212+
&self.inner.generate_srtcp_auth_tag(cipher_text)[..self.rtcp_auth_tag_len()];
200213

201214
// See if the auth tag actually matches.
202215
// We use a constant time comparison to prevent timing attacks.

0 commit comments

Comments
 (0)