1use bytes::Bytes;
2use bytesstr::BytesStr;
3use internal::{ws, IResult};
4use nom::branch::alt;
5use nom::bytes::complete::{tag, take_while1};
6use nom::character::complete::{char, digit1};
7use nom::combinator::{map, map_res, not, opt, peek};
8use nom::error::context;
9use nom::multi::{separated_list0, separated_list1};
10use nom::sequence::{preceded, separated_pair, terminated, tuple};
11use std::fmt;
12
13#[derive(Debug, Clone)]
17pub struct SrtpCrypto {
18 pub tag: u32,
20
21 pub suite: SrtpSuite,
23
24 pub keys: Vec<SrtpKeyingMaterial>,
26
27 pub params: Vec<SrtpSessionParam>,
29}
30
31impl SrtpCrypto {
32 pub fn parse<'i>(src: &Bytes, i: &'i str) -> IResult<&'i str, Self> {
33 context(
34 "parsing srtp-crypto attribute",
35 map(
36 ws((
37 number,
39 SrtpSuite::parse(src),
41 parse_srtp_key_params(src),
43 separated_list0(
45 take_while1(char::is_whitespace),
46 SrtpSessionParam::parse(src),
47 ),
48 )),
49 |(tag, suite, keys, params)| Self {
50 tag,
51 suite,
52 keys,
53 params,
54 },
55 ),
56 )(i)
57 }
58}
59
60impl fmt::Display for SrtpCrypto {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "{} {}", self.tag, self.suite)?;
63
64 if !self.keys.is_empty() {
65 write!(f, " ")?;
66 }
67
68 let mut keys = self.keys.iter().peekable();
69
70 while let Some(key) = keys.next() {
71 write!(f, "inline:{key}")?;
72
73 if keys.peek().is_some() {
74 write!(f, ";")?;
75 }
76 }
77
78 for param in &self.params {
79 write!(f, " {param}")?;
80 }
81
82 Ok(())
83 }
84}
85
86macro_rules! suite {
87 ($($suite:ident),* $(,)?) => {
88 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
89 #[allow(non_camel_case_types)]
90 pub enum SrtpSuite {
91 $($suite,)*
92 Ext(BytesStr),
93 }
94
95 impl SrtpSuite {
96 pub fn parse(src: &Bytes) -> impl Fn(&str) -> IResult<&str, Self> + '_ {
97 move |i| {
98 context(
99 "parsing srtp suite",
100 alt((
101 $(
102 map(tag(stringify!($suite)), |_| Self::$suite),
103 )*
104 map(take_while1(is_alphanumeric_or_underscore), move |suite| {
105 Self::Ext(BytesStr::from_parse(src, suite))
106 }),
107 )),
108 )(i)
109 }
110 }
111
112 pub fn as_str(&self) -> &str {
113 match self {
114 $(Self::$suite => stringify!($suite),)*
115 Self::Ext(ext) => ext,
116 }
117 }
118 }
119
120 impl fmt::Display for SrtpSuite {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(f, "{}", self.as_str())
123 }
124 }
125 };
126}
127
128suite! {
129 AES_CM_128_HMAC_SHA1_80,
130 AES_CM_128_HMAC_SHA1_32,
131 F8_128_HMAC_SHA1_80,
132 AES_192_CM_HMAC_SHA1_80,
133 AES_192_CM_HMAC_SHA1_32,
134 AES_256_CM_HMAC_SHA1_80,
135 AES_256_CM_HMAC_SHA1_32,
136 AEAD_AES_128_GCM,
137 AEAD_AES_256_GCM,
138}
139
140impl SrtpSuite {
141 pub fn key_and_salt_len(&self) -> Option<(usize, usize)> {
142 match self {
143 SrtpSuite::AES_CM_128_HMAC_SHA1_80
144 | SrtpSuite::AES_CM_128_HMAC_SHA1_32
145 | SrtpSuite::F8_128_HMAC_SHA1_80 => Some((16, 14)),
146 SrtpSuite::AES_192_CM_HMAC_SHA1_80 | SrtpSuite::AES_192_CM_HMAC_SHA1_32 => {
147 Some((24, 14))
148 }
149 SrtpSuite::AES_256_CM_HMAC_SHA1_80 | SrtpSuite::AES_256_CM_HMAC_SHA1_32 => {
150 Some((32, 14))
151 }
152 SrtpSuite::AEAD_AES_128_GCM => Some((16, 12)),
153 SrtpSuite::AEAD_AES_256_GCM => Some((32, 12)),
154 SrtpSuite::Ext(_) => None,
155 }
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub enum SrtpSessionParam {
162 Kdr(u32),
164 UnencryptedSrtp,
166 UnencryptedSrtcp,
168 UnauthenticatedSrtp,
170 FecOrder(SrtpFecOrder),
172 FecKey(Vec<SrtpKeyingMaterial>),
174 WindowSizeHint(u32),
176 Ext(BytesStr),
178}
179
180#[derive(Debug, Clone, PartialEq, Eq)]
182pub enum SrtpFecOrder {
183 FecSrtp,
186
187 SrtpFec,
190}
191
192impl SrtpSessionParam {
193 pub fn parse(src: &Bytes) -> impl Fn(&str) -> IResult<&str, Self> + '_ {
194 move |i| {
195 context(
196 "parsing srtp-session-param",
197 alt((
198 map(preceded(tag("KDR="), number), Self::Kdr),
199 map(tag("UNENCRYPTED_SRTP"), |_| Self::UnencryptedSrtp),
200 map(tag("UNENCRYPTED_SRTCP"), |_| Self::UnencryptedSrtcp),
201 map(tag("UNAUTHENTICATED_SRTP"), |_| Self::UnauthenticatedSrtp),
202 preceded(
203 tag("FEC_ORDER="),
204 alt((
205 map(tag("FEC_SRTP"), |_| Self::FecOrder(SrtpFecOrder::FecSrtp)),
206 map(tag("SRTP_FEC"), |_| Self::FecOrder(SrtpFecOrder::SrtpFec)),
207 )),
208 ),
209 map(
210 preceded(tag("FEC_KEY="), parse_srtp_key_params(src)),
211 Self::FecKey,
212 ),
213 map(preceded(tag("WSH="), number), Self::WindowSizeHint),
214 map(
215 preceded(peek(not(char('-'))), take_while1(is_visible_char)),
216 |ext| Self::Ext(BytesStr::from_parse(src, ext)),
217 ),
218 )),
219 )(i)
220 }
221 }
222}
223
224impl fmt::Display for SrtpSessionParam {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 SrtpSessionParam::Kdr(v) => write!(f, "KDR={v}"),
228 SrtpSessionParam::UnencryptedSrtp => write!(f, "UNENCRYPTED_SRTP"),
229 SrtpSessionParam::UnencryptedSrtcp => write!(f, "UNENCRYPTED_SRTCP"),
230 SrtpSessionParam::UnauthenticatedSrtp => write!(f, "UNAUTHENTICATED_SRTP"),
231 SrtpSessionParam::FecOrder(order) => {
232 let order = match order {
233 SrtpFecOrder::FecSrtp => "FEC_SRTP",
234 SrtpFecOrder::SrtpFec => "SRTP_FEC",
235 };
236
237 write!(f, "FEC_ORDER={order}")
238 }
239 SrtpSessionParam::FecKey(keys) => {
240 if keys.is_empty() {
241 return Ok(());
242 }
243
244 write!(f, "FEC_KEY=")?;
245
246 let mut keys = keys.iter().peekable();
247
248 while let Some(key) = keys.next() {
249 write!(f, "inline:{key}")?;
250
251 if keys.peek().is_some() {
252 write!(f, ";")?;
253 }
254 }
255
256 Ok(())
257 }
258 SrtpSessionParam::WindowSizeHint(v) => write!(f, "WSH={v}"),
259 SrtpSessionParam::Ext(ext) => write!(f, "{ext}"),
260 }
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
265pub struct SrtpKeyingMaterial {
266 pub key_and_salt: BytesStr,
268
269 pub lifetime: Option<u32>,
271
272 pub mki: Option<(u32, u32)>,
274}
275
276impl SrtpKeyingMaterial {
277 pub fn parse(src: &Bytes) -> impl Fn(&str) -> IResult<&str, Self> + '_ {
278 move |i| {
279 context(
280 "parsing keying material",
281 map(
282 tuple((
283 take_while1(is_base64_char),
285 opt(map(
287 terminated(
288 preceded(char('|'), tuple((opt(tag("2^")), number))),
289 peek(not(char(':'))),
291 ),
292 |(exp, n)| {
293 if exp.is_some() {
294 2u32.pow(n)
295 } else {
296 n
297 }
298 },
299 )),
300 opt(preceded(
302 char('|'),
303 separated_pair(number, char(':'), number),
304 )),
305 )),
306 |(key_and_salt, lifetime, mki)| Self {
307 key_and_salt: BytesStr::from_parse(src, key_and_salt),
308 lifetime,
309 mki,
310 },
311 ),
312 )(i)
313 }
314 }
315}
316
317impl fmt::Display for SrtpKeyingMaterial {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 write!(f, "{}", self.key_and_salt)?;
320
321 if let Some(lifetime) = self.lifetime {
322 if lifetime.is_power_of_two() {
323 write!(f, "|2^{}", lifetime.trailing_zeros())?;
324 } else {
325 write!(f, "|{lifetime}")?;
326 }
327 }
328
329 if let Some((mki, mki_length)) = self.mki {
330 write!(f, "|{mki}:{mki_length}")?;
331 }
332
333 Ok(())
334 }
335}
336
337fn parse_srtp_key_params(
338 src: &Bytes,
339) -> impl FnMut(&str) -> IResult<&str, Vec<SrtpKeyingMaterial>> + '_ {
340 move |i| {
341 separated_list1(
342 char(';'),
343 preceded(tag("inline:"), SrtpKeyingMaterial::parse(src)),
344 )(i)
345 }
346}
347
348fn is_alphanumeric_or_underscore(c: char) -> bool {
349 matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')
350}
351
352fn is_visible_char(c: char) -> bool {
353 matches!(c, '\u{21}'..='\u{7E}')
354}
355
356fn is_base64_char(c: char) -> bool {
357 c.is_ascii_alphanumeric() || matches!(c, '+' | '/' | '=')
358}
359
360fn number(i: &str) -> IResult<&str, u32> {
361 context("parsing number", map_res(digit1, str::parse))(i)
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn srtp_session_param_kdr() {
370 let i = BytesStr::from_static("KDR=5");
371 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
372
373 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
374
375 let SrtpSessionParam::Kdr(5) = param else {
376 panic!("expected Kdr got {param:?}")
377 };
378
379 assert_eq!(param.to_string(), "KDR=5");
380 }
381
382 #[test]
383 fn srtp_session_param_unencrypted_srtp() {
384 let i = BytesStr::from_static("UNENCRYPTED_SRTP");
385 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
386
387 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
388
389 let SrtpSessionParam::UnencryptedSrtp = param else {
390 panic!("expected UnencryptedSrtp got {param:?}")
391 };
392
393 assert_eq!(param.to_string(), "UNENCRYPTED_SRTP");
394 }
395
396 #[test]
397 fn srtp_session_param_unencrypted_srtcp() {
398 let i = BytesStr::from_static("UNENCRYPTED_SRTCP");
399 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
400
401 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
402
403 let SrtpSessionParam::UnencryptedSrtcp = param else {
404 panic!("expected UnencryptedSrtcp got {param:?}")
405 };
406
407 assert_eq!(param.to_string(), "UNENCRYPTED_SRTCP");
408 }
409
410 #[test]
411 fn srtp_session_param_unauthenticated_srtp() {
412 let i = BytesStr::from_static("UNAUTHENTICATED_SRTP");
413 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
414
415 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
416
417 let SrtpSessionParam::UnauthenticatedSrtp = param else {
418 panic!("expected UnauthenticatedSrtp got {param:?}")
419 };
420
421 assert_eq!(param.to_string(), "UNAUTHENTICATED_SRTP");
422 }
423
424 #[test]
425 fn srtp_session_param_fec_order1() {
426 let i = BytesStr::from_static("FEC_ORDER=SRTP_FEC");
427 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
428
429 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
430
431 let SrtpSessionParam::FecOrder(SrtpFecOrder::SrtpFec) = param else {
432 panic!("expected FecOrder(SrtpFecOrder::SrtpFec) got {param:?}")
433 };
434
435 assert_eq!(param.to_string(), "FEC_ORDER=SRTP_FEC");
436 }
437
438 #[test]
439 fn srtp_session_param_fec_order2() {
440 let i = BytesStr::from_static("FEC_ORDER=FEC_SRTP");
441 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
442
443 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
444
445 let SrtpSessionParam::FecOrder(SrtpFecOrder::FecSrtp) = param else {
446 panic!("expected FecOrder(SrtpFecOrder::FecSrtp) got {param:?}")
447 };
448
449 assert_eq!(param.to_string(), "FEC_ORDER=FEC_SRTP");
450 }
451
452 #[test]
453 fn srtp_session_param_fec_key1() {
454 let i = BytesStr::from_static(
455 "FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4",
456 );
457 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
458
459 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
460
461 let SrtpSessionParam::FecKey(key) = ¶m else {
462 panic!("expected FecKey(..) got {param:?}")
463 };
464
465 assert_eq!(key.len(), 1);
466
467 assert_eq!(
468 key[0].key_and_salt,
469 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"
470 );
471 assert_eq!(key[0].lifetime, Some(1048576));
472 assert_eq!(key[0].mki, Some((1, 4)));
473
474 assert_eq!(
475 param.to_string(),
476 "FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4"
477 );
478 }
479
480 #[test]
481 fn srtp_session_param_fec_key2() {
482 let i = BytesStr::from_static(
483 "FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4;inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^14|1:2",
484 );
485 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
486
487 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
488
489 let SrtpSessionParam::FecKey(key) = ¶m else {
490 panic!("expected FecKey(..) got {param:?}")
491 };
492
493 assert_eq!(key.len(), 2);
494
495 assert_eq!(
496 key[0].key_and_salt,
497 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"
498 );
499 assert_eq!(key[0].lifetime, Some(1048576));
500 assert_eq!(key[0].mki, Some((1, 4)));
501
502 assert_eq!(
503 key[1].key_and_salt,
504 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"
505 );
506 assert_eq!(key[1].lifetime, Some(16384));
507 assert_eq!(key[1].mki, Some((1, 2)));
508
509 assert_eq!(
510 param.to_string(),
511 "FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4;inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^14|1:2"
512 );
513 }
514
515 #[test]
516 fn srtp_session_param_window_size_hint() {
517 let i = BytesStr::from_static("WSH=5");
518 let (rem, param) = SrtpSessionParam::parse(i.as_ref())(&i).unwrap();
519
520 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
521
522 let SrtpSessionParam::WindowSizeHint(5) = param else {
523 panic!("expected WindowSizeHint got {param:?}")
524 };
525
526 assert_eq!(param.to_string(), "WSH=5");
527 }
528
529 #[test]
530 fn keying_material_missing_lifetime() {
531 let i = BytesStr::from_static("d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|1:4");
532 let (rem, key) = SrtpKeyingMaterial::parse(i.as_ref())(&i).unwrap();
533
534 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
535
536 assert_eq!(key.key_and_salt, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj");
537 assert_eq!(key.lifetime, None);
538 assert_eq!(key.mki, Some((1, 4)));
539
540 assert_eq!(
541 key.to_string(),
542 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|1:4"
543 );
544 }
545
546 #[test]
547 fn keying_material_missing_mki() {
548 let i = BytesStr::from_static("d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^10");
549 let (rem, key) = SrtpKeyingMaterial::parse(i.as_ref())(&i).unwrap();
550
551 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
552
553 assert_eq!(key.key_and_salt, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj");
554 assert_eq!(key.lifetime, Some(1024));
555 assert_eq!(key.mki, None);
556
557 assert_eq!(
558 key.to_string(),
559 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^10"
560 );
561 }
562
563 #[test]
564 fn keying_material_only_key_and_salt() {
565 let i = BytesStr::from_static("d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj");
566 let (rem, key) = SrtpKeyingMaterial::parse(i.as_ref())(&i).unwrap();
567
568 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
569
570 assert_eq!(key.key_and_salt, "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj");
571 assert_eq!(key.lifetime, None);
572 assert_eq!(key.mki, None);
573
574 assert_eq!(key.to_string(), "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj");
575 }
576
577 #[test]
578 fn parse_everything() {
579 let i = BytesStr::from_static(
580 "\
5811 AES_CM_128_HMAC_SHA1_80 inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4;inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^14|1:2 \
582KDR=100 \
583UNENCRYPTED_SRTP \
584UNENCRYPTED_SRTCP \
585UNAUTHENTICATED_SRTP \
586FEC_ORDER=FEC_SRTP \
587FEC_ORDER=SRTP_FEC \
588FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|1:4 \
589WSH=123",
590 );
591 let (rem, c) = SrtpCrypto::parse(i.as_ref(), &i).unwrap();
592 assert!(rem.is_empty(), "rem is not empty: {rem:?}");
593
594 assert_eq!(c.tag, 1);
595 assert_eq!(c.suite, SrtpSuite::AES_CM_128_HMAC_SHA1_80);
596
597 assert_eq!(c.keys.len(), 2);
598
599 assert_eq!(
600 c.keys[0].key_and_salt,
601 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"
602 );
603 assert_eq!(c.keys[0].lifetime, Some(1048576));
604 assert_eq!(c.keys[0].mki, Some((1, 4)));
605
606 assert_eq!(
607 c.keys[1].key_and_salt,
608 "d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj"
609 );
610 assert_eq!(c.keys[1].lifetime, Some(16384));
611 assert_eq!(c.keys[1].mki, Some((1, 2)));
612
613 assert_eq!(c.params[0], SrtpSessionParam::Kdr(100));
614 assert_eq!(c.params[1], SrtpSessionParam::UnencryptedSrtp);
615 assert_eq!(c.params[2], SrtpSessionParam::UnencryptedSrtcp);
616 assert_eq!(c.params[3], SrtpSessionParam::UnauthenticatedSrtp);
617 assert_eq!(
618 c.params[4],
619 SrtpSessionParam::FecOrder(SrtpFecOrder::FecSrtp)
620 );
621 assert_eq!(
622 c.params[5],
623 SrtpSessionParam::FecOrder(SrtpFecOrder::SrtpFec)
624 );
625 assert_eq!(c.params[7], SrtpSessionParam::WindowSizeHint(123));
626
627 assert_eq!(c.to_string(), "1 AES_CM_128_HMAC_SHA1_80 inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:4;inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^14|1:2 KDR=100 UNENCRYPTED_SRTP UNENCRYPTED_SRTCP UNAUTHENTICATED_SRTP FEC_ORDER=FEC_SRTP FEC_ORDER=SRTP_FEC FEC_KEY=inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|1:4 WSH=123");
628 }
629}