summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/transport/transportlayersrtp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/transport/transportlayersrtp.cpp')
-rw-r--r--dom/media/webrtc/transport/transportlayersrtp.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/dom/media/webrtc/transport/transportlayersrtp.cpp b/dom/media/webrtc/transport/transportlayersrtp.cpp
new file mode 100644
index 0000000000..25830a2dde
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayersrtp.cpp
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "transportlayersrtp.h"
+#include "transportlayerdtls.h"
+
+#include "logging.h"
+#include "nsError.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
+
+TransportLayerSrtp::TransportLayerSrtp(TransportLayerDtls& dtls) {
+ // We need to connect to the dtls layer, not the ice layer, because even
+ // though the packets that DTLS decrypts don't flow through us, we do base our
+ // keying information on the keying information established by the DTLS layer.
+ dtls.SignalStateChange.connect(this, &TransportLayerSrtp::StateChange);
+
+ TL_SET_STATE(dtls.state());
+}
+
+void TransportLayerSrtp::WasInserted() {
+ // Connect to the lower layers
+ if (!Setup()) {
+ TL_SET_STATE(TS_ERROR);
+ }
+}
+
+bool TransportLayerSrtp::Setup() {
+ CheckThread();
+ if (!downward_) {
+ MOZ_MTLOG(ML_ERROR, "SRTP layer with nothing below. This is useless");
+ return false;
+ }
+
+ // downward_ is the TransportLayerIce
+ downward_->SignalPacketReceived.connect(this,
+ &TransportLayerSrtp::PacketReceived);
+
+ return true;
+}
+
+TransportResult TransportLayerSrtp::SendPacket(MediaPacket& packet) {
+ if (state() != TS_OPEN) {
+ return TE_ERROR;
+ }
+
+ if (packet.len() < 4) {
+ MOZ_ASSERT(false);
+ return TE_ERROR;
+ }
+
+ MOZ_ASSERT(packet.capacity() - packet.len() >= SRTP_MAX_EXPANSION);
+
+ int out_len;
+ nsresult res;
+ switch (packet.type()) {
+ case MediaPacket::RTP:
+ res = mSendSrtp->ProtectRtp(packet.data(), packet.len(),
+ packet.capacity(), &out_len);
+ packet.SetType(MediaPacket::SRTP);
+ break;
+ case MediaPacket::RTCP:
+ res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(),
+ packet.capacity(), &out_len);
+ packet.SetType(MediaPacket::SRTCP);
+ break;
+ default:
+ MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP");
+ }
+
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Error protecting "
+ << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP")
+ << " len=" << packet.len() << "[" << std::hex
+ << packet.data()[0] << " " << packet.data()[1] << " "
+ << packet.data()[2] << " " << packet.data()[3] << "]");
+ return TE_ERROR;
+ }
+
+ size_t unencrypted_len = packet.len();
+ packet.SetLength(out_len);
+
+ TransportResult bytes = downward_->SendPacket(packet);
+ if (bytes == out_len) {
+ // Whole packet was written, but the encrypted length might be different.
+ // Don't confuse the caller.
+ return unencrypted_len;
+ }
+
+ if (bytes == TE_WOULDBLOCK) {
+ return TE_WOULDBLOCK;
+ }
+
+ return TE_ERROR;
+}
+
+void TransportLayerSrtp::StateChange(TransportLayer* layer, State state) {
+ if (state == TS_OPEN && !mSendSrtp) {
+ TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(layer);
+ MOZ_ASSERT(dtls); // DTLS is mandatory
+
+ uint16_t cipher_suite;
+ nsresult res = dtls->GetSrtpCipher(&cipher_suite);
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_DEBUG, "DTLS-SRTP disabled");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ unsigned int key_size = SrtpFlow::KeySize(cipher_suite);
+ unsigned int salt_size = SrtpFlow::SaltSize(cipher_suite);
+ unsigned int master_key_size = key_size + salt_size;
+ MOZ_ASSERT(master_key_size <= SRTP_MAX_KEY_LENGTH);
+
+ // SRTP Key Exporter as per RFC 5764 S 4.2
+ unsigned char srtp_block[SRTP_MAX_KEY_LENGTH * 2];
+ res = dtls->ExportKeyingMaterial(kDTLSExporterLabel, false, "", srtp_block,
+ sizeof(srtp_block));
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_ERROR, "Failed to compute DTLS-SRTP keys. This is an error");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ // Slice and dice as per RFC 5764 S 4.2
+ unsigned char client_write_key[SRTP_MAX_KEY_LENGTH];
+ unsigned char server_write_key[SRTP_MAX_KEY_LENGTH];
+ unsigned int offset = 0;
+ memcpy(client_write_key, srtp_block + offset, key_size);
+ offset += key_size;
+ memcpy(server_write_key, srtp_block + offset, key_size);
+ offset += key_size;
+ memcpy(client_write_key + key_size, srtp_block + offset, salt_size);
+ offset += salt_size;
+ memcpy(server_write_key + key_size, srtp_block + offset, salt_size);
+ MOZ_ASSERT((offset + salt_size) == (2 * master_key_size));
+
+ unsigned char* write_key;
+ unsigned char* read_key;
+
+ if (dtls->role() == TransportLayerDtls::CLIENT) {
+ write_key = client_write_key;
+ read_key = server_write_key;
+ } else {
+ write_key = server_write_key;
+ read_key = client_write_key;
+ }
+
+ MOZ_ASSERT(!mSendSrtp && !mRecvSrtp);
+ mSendSrtp =
+ SrtpFlow::Create(cipher_suite, false, write_key, master_key_size);
+ mRecvSrtp = SrtpFlow::Create(cipher_suite, true, read_key, master_key_size);
+ if (!mSendSrtp || !mRecvSrtp) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow.");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ MOZ_MTLOG(ML_INFO, "Created SRTP flow!");
+ }
+
+ TL_SET_STATE(state);
+}
+
+void TransportLayerSrtp::PacketReceived(TransportLayer* layer,
+ MediaPacket& packet) {
+ if (state() != TS_OPEN) {
+ return;
+ }
+
+ if (!packet.data()) {
+ // Something ate this, probably the DTLS layer
+ return;
+ }
+
+ if (packet.type() != MediaPacket::SRTP &&
+ packet.type() != MediaPacket::SRTCP) {
+ return;
+ }
+
+ // We want to keep the encrypted packet around for packet dumping
+ packet.CopyDataToEncrypted();
+ int outLen;
+ nsresult res;
+
+ if (packet.type() == MediaPacket::SRTP) {
+ packet.SetType(MediaPacket::RTP);
+ res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(),
+ &outLen);
+ } else {
+ packet.SetType(MediaPacket::RTCP);
+ res = mRecvSrtp->UnprotectRtcp(packet.data(), packet.len(), packet.len(),
+ &outLen);
+ }
+
+ if (NS_SUCCEEDED(res)) {
+ packet.SetLength(outLen);
+ SignalPacketReceived(this, packet);
+ } else {
+ // TODO: What do we do wrt packet dumping here? Maybe signal an empty
+ // packet? Signal the still-encrypted packet?
+ MOZ_MTLOG(ML_ERROR,
+ "Error unprotecting "
+ << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP")
+ << " len=" << packet.len() << "[" << std::hex
+ << packet.data()[0] << " " << packet.data()[1] << " "
+ << packet.data()[2] << " " << packet.data()[3] << "]");
+ }
+}
+
+} // namespace mozilla