[asterisk-dev] DTLS-SRTP support and WebRTC: a report

Lorenzo Miniero lminiero at gmail.com
Fri May 24 05:51:33 CDT 2013


Hi,

first of all, beware: this is a very long post :-)

I recently worked on getting the existing DTLS-SRTP support in Asterisk to
work with the available WebRTC implementations in Chrome and Firefox.
You'll be glad to know that, apart from a few tests and experimentations,
there was not a lot that needed to be done (great work, guys!).
Nevertheless, there are some relevant aspects that need to be considered,
so I thought I'd share what I did here, in order to also better understand
how I could provide a patch that wouldn't mess up the way the Asterisk code
is organized right now. In fact, right now the patch I have is quite messy
(I did several different tests in different directions until I could get
something working) and would probably be more harmful than helpful :-)
Anyway, the only related discussion I could find about DTLS support in
Asterisk dates back to a few months ago (
http://forums.asterisk.org/viewtopic.php?f=1&t=85275) so I hope this post
will at the very least help other Asterisk developers address this novel
feature.

That said, as I said the existng DTLS code base was already well written,
so I focused on what was missing to get it to work with both Chrome and
Firefox. I was not using the existing websockets implementation in Asterisk
to interoperate with browsers, but a simple web application that acted as a
signalling gateway between the two: this web app basically acts as a SIP
client for Asterisk, manipulating incoming and outgoing SDP in order to
keep both browsers and Asterisk happy, so I guess the same considerations
I'll describe in this mail should apply to the websockets version as well
(a few of them could be done in JavaScript, for instance). Besides, I only
tested calls generated by the browsers towards Asterisk but, for reasons
I'll mention later, the same considerations *should* also apply when
changing the perspective.

Anyway, long story short (too late, I guess!), here's a list of points I
found out. They're related to both user (configuration, signalling
adaptation) and developer (what had to be changed/added in the Asterisk
code) perspective.

    1. First of all, from a configuration point of view, 'dtlsverify' needs
to be set to 'no'. In fact, all certificates used in WebRTC calls from
browsers are apparently generated on the fly or, anyway, self certificates,
and so cannot be verified. Besides, I also just tested
'ALL:NULL:eNULL:aNULL' as the 'dtlschipher': not sure whether or not this
can be safely narrowed down. Not really a technical issue or anything that
needs to be addressed in the code, but I thought it was important to point
out, if only to notify this in related documentation files.

    2. Asterisk only enforces DTLS-SRTP when it receives
UDP/TLS/RTP/SAVP(F) as the protocol to be used in the SDP, but both Chrome
and Firefox negotiate RTP/SAVPF. According to RFC 5764, Asterisk is doing
the right thing: anyway, UDP/TLS/RTP/SAVP(F) is a SHALL and not a MUST, and
besides both browsers don't seem to "digest" it very well. This means that
this needs to be addressed: I did it in the signalling gateway, others may
do this somewhere else (e.g., directly in JavaScript after producing an
offer and before passing an answer). Probably not needed in chan_sip.c as
well, even though I'm not sure how other DTLS-SRTP implementations behave
in that sense.

    3. Even when forced, through the proper "DtlsSrtpKeyAgreement":true
constraint, to negotiate DTLS-SRTP, Chrome also adds SDES crypto attributes
as a fallback. As far as I've understood from the chan_sip.c code, this
could cause Asterisk to try and setup SRTP using both SDES (first) and DTLS
(then), with potentially unpredictable results. I have not tested this, as
in my signalling gateway I made sure that only the fingerprint attribute
was left in the code, but this may be worth looking at. What was definitely
needed in the Chrome case, though, was to add a fake crypto line in the
reply anyway (e.g., a=crypto:1 AES_CM_128_HMAC_SHA1_80
inline:BAADBAADBAADBAADBAADBAADBAADBAADBAADBAAD), as was explained in
http://www.webrtc.org/interop (note: this doesn't seem to be on that page
anymore, so this may have been fixed, but older Chrome versions may still
be affected). Again, I did this in the signalling gateway and not Asterisk,
so probably not very important.

    4. Neither Chrome nor Asterisk provide any 'setup' attribute when
negotiating the DTLS fingerprint. According to RFC 5763, this is a MUST,
and in fact Asterisk negotiates it instead: nevertheless, both browsers
ignore it and don't negotiate the role there, but use a different approach.
Specifically, Chrome uses ICE roles to determine passive/active DTLS
parties (ICE-CONTROLLED is active), while Firefox assumes that the caller
is going to take the DTLS passive role (DTLS server) and the callee the
active one (DTLS client). As both approaches basically lead to the same
thing, they are able to interact with each other. In order to get this
working in Asterisk as well, I made sure that Asterisk was configured as
AST_RTP_DTLS_SETUP_ACTIVE for the incoming calls, by adding a setup:passive
attribute in the signalling gateway to have it comply with RFC 5763: in
fact, as I anticipated I so far only tested calls from browsers to
Asterisk. Nevertheless, using a similar approach for outgoing calls (i.e.,
making sure Asterisk is configured as passive when negotiating DTLS in a
call it generates) should do the trick for those scenarios as well.

    5. An important aspect I needed to take care of was the right sequence
of steps to accomplish, namely: i) ICE completion, ii) DTLS handshake, iii)
RTP/RTCP packets flowing. This is what browsers expect, and what Asterisk
failed to comply to: the DTLS handshake, for instance, is by default
attempted when activating RTP (ast_rtp_activate in res_rtp_asterisk.c),
which happens before ICE even starts to work, thus resulting in the
handshake never starting at all. In order to fix this, I had to add, to
res_rtp_engine.c, an .on_ice_complete callback triggered by PJNATH. This
allowed the module to be aware when ICE completed for a specific RTP
instance, so that's where I added a new point to start the DTLS handshake
for both RTP and RTCP. I also added a 'dtlsdone' variable to the RTP
instance to keep track of when the DTLS handshake successfully completed:
this was needed to prevent Asterisk from sending RTP/RTCP packets before
time, as it seemed to confuse the browsers.

    6. About the previous point, one more thing I found out is that both
browsers expect DTLS to be used to set up both RTP *and* RTCP. This is
needed because, as browsers (Chrome at least) mux RTP and RTCP on the same
port, when talking to each other they just need a single DTLS handshake for
the purpose. Asterisk doesn't mux them instead, and as this was causing
calls to fail, I added some very ugly hacks to enforce this for RTCP as
well. This means that I basically had to copy all the DTLS-related
variables from ast_rtp to the ast_rtcp structure as well, and replicate the
calls that were already there for RTP for RTCP as well. This code in
particular is especially "ugly" to see right now, but it seems to work
fine, meaning that at least it was the right way to go.

    7. For Firefox in particular, I had to add some extra care. Firefox
currently rejects with a "401 Unauthorized" error message all connectivity
checks that arrive before it is aware of the peer credentials. Considering
how PJNATH works (failing as soon as a 401 is received), this resulted in
all candidate pairs to be disabled and the call to fail, because PJNATH
would start generating checks before Firefox, in its code, was able to
enforce the remote credentials. To fix this, the only thing I could think
of was to "hack" PJNATH in order to have it retry whenever it got a 401
until a success arrived eventually. This is probably not the best way to
take care of this, but it seems to work fine for now. Besides, I still also
had to start the DTLS handshake after some time anyway (about 500ms) even
when ICE was completed according to the PJNATH callback: in fact, in some
cases there were issues with the DTLS handshake getting lost in Firefox
(probably for the same reason as the 401). I reported this issue on the
Mozilla WebRTC group (
https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.media/RxQe9WrY2Ao),
but until this gets fixed this probably needs being taken care of.


All the above steps eventually lead to a successful negotiation of both
audio and video with browsers. There still are a few issues, though. When
using DTLS, for instance, while SRTP works fine, SRTCP packets that
Asterisk generates seem not to be properly decoded by the browsers. I
notified this issue to Chrome developers (last messages in
https://groups.google.com/forum/?fromgroups=#!topic/discuss-webrtc/PLbYI6we7hY)
as I'm not sure whether it is on the browsers or Asterisk side. SRTCP works
as expected when exploiting SDES-SRTP instead. Besides, Asterisk still
seems to have a few SRTP interoperability issues with Firefox, as each
second an unprotect error appears: this seems to eventually lead to a
growing delay in the frames sent by Firefox to Asterisk, but weirdly enough
not the other way around. Again, not sure who's at fault there.

I guess that's all: if you managed to get this far, the least thing I can
do is praise your patience!
Any feedback on this? If you have any question related to what I've done,
feel free to ask. I'd also be happy to help integrating this in a "safe"
patch to the Asterisk code base, so let me know if there's any way I can be
of help there.

Lorenzo


PS: a few months ago I also talked, on the #asterisk-dev IRC, about the
support I added for both Opus (transcoding) and VP8 (passthrough) in
Asterisk, codecs that are currently the default ones used in WebRTC. I
checked whether there was an interest in a patch for them, but at the time
there were some concerns about the copyright status of Opus that prevented
it to be considered for integration in Asterisk. Has this situation changed
in the meanwhile? I can open a separate thread for this if needed.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-dev/attachments/20130524/6a0b5827/attachment-0001.htm>


More information about the asterisk-dev mailing list