<div dir="ltr"><div>Hi,</div><div><br></div><div>first of all, beware: this is a very long post :-)</div><div><br></div><div>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 (<a href="http://forums.asterisk.org/viewtopic.php?f=1&t=85275">http://forums.asterisk.org/viewtopic.php?f=1&t=85275</a>) so I hope this post will at the very least help other Asterisk developers address this novel feature.</div>
<div><br></div><div>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.</div>
<div><br></div><div>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.</div>
<div><br></div><div> 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.</div>
<div> </div><div> 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.</div>
<div> </div><div> 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 <a href="http://www.webrtc.org/interop">http://www.webrtc.org/interop</a> (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.</div>
<div> </div><div> 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.</div>
<div> </div><div> 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. </div>
<div> </div><div> 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.</div>
<div> </div><div> 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 (<a href="https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.media/RxQe9WrY2Ao">https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.media/RxQe9WrY2Ao</a>), but until this gets fixed this probably needs being taken care of.</div>
<div><br></div><div> </div><div>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 <a href="https://groups.google.com/forum/?fromgroups=#!topic/discuss-webrtc/PLbYI6we7hY">https://groups.google.com/forum/?fromgroups=#!topic/discuss-webrtc/PLbYI6we7hY</a>) 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.</div>
<div><br></div><div>I guess that's all: if you managed to get this far, the least thing I can do is praise your patience!</div><div>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.</div>
<div><br></div><div>Lorenzo</div><div><br></div><div><br></div><div>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.</div>
<div><br></div></div>