Bug 1455647 - Part 3: Move SRTP into a TransportLayer. r?drno draft
authorByron Campen [:bwc] <docfaraday@gmail.com>
Mon, 23 Apr 2018 13:14:30 -0500
changeset 806512 4c9d2ddc79745326f6fbcfb9d230a51e0417f06b
parent 806511 1f569a18b1ced688d2de0a3ba8d477ecafe50558
child 806513 4bf6a535209e872df3612b9e5c003b61b36ca88a
push id112901
push userbcampen@mozilla.com
push dateSun, 10 Jun 2018 18:18:51 +0000
reviewersdrno
bugs1455647
milestone62.0a1
Bug 1455647 - Part 3: Move SRTP into a TransportLayer. r?drno MozReview-Commit-ID: LWZFPVDYZKb
media/mtransport/SrtpFlow.cpp
media/mtransport/SrtpFlow.h
media/mtransport/build/moz.build
media/mtransport/common.build
media/mtransport/test/sctp_unittest.cpp
media/mtransport/transportlayersrtp.cpp
media/mtransport/transportlayersrtp.h
media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
media/webrtc/signaling/src/mediapipeline/SrtpFlow.h
media/webrtc/signaling/src/mediapipeline/moz.build
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
rename from media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
rename to media/mtransport/SrtpFlow.cpp
--- a/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
+++ b/media/mtransport/SrtpFlow.cpp
@@ -8,26 +8,21 @@
 #include "SrtpFlow.h"
 
 #include "srtp.h"
 #include "ssl.h"
 #include "sslproto.h"
 
 #include "mozilla/RefPtr.h"
 
-static const char* sfLogTag = "SrtpFlow";
-#ifdef LOGTAG
-#undef LOGTAG
-#endif
-#define LOGTAG sfLogTag
-
 using namespace mozilla;
 
 namespace mozilla {
 
+MOZ_MTLOG_MODULE("mtransport")
 bool SrtpFlow::initialized;  // Static
 
 SrtpFlow::~SrtpFlow() {
   if (session_) {
     srtp_dealloc(session_);
   }
 }
 
@@ -37,97 +32,97 @@ RefPtr<SrtpFlow> SrtpFlow::Create(int ci
                                            size_t key_len) {
   nsresult res = Init();
   if (!NS_SUCCEEDED(res))
     return nullptr;
 
   RefPtr<SrtpFlow> flow = new SrtpFlow();
 
   if (!key) {
-    CSFLogError(LOGTAG, "Null SRTP key specified");
+    MOZ_MTLOG(ML_ERROR, "Null SRTP key specified");
     return nullptr;
   }
 
   if (key_len != SRTP_TOTAL_KEY_LENGTH) {
-    CSFLogError(LOGTAG, "Invalid SRTP key length");
+    MOZ_MTLOG(ML_ERROR, "Invalid SRTP key length");
     return nullptr;
   }
 
   srtp_policy_t policy;
   memset(&policy, 0, sizeof(srtp_policy_t));
 
   // Note that we set the same cipher suite for RTP and RTCP
   // since any flow can only have one cipher suite with DTLS-SRTP
   switch (cipher_suite) {
     case SRTP_AES128_CM_HMAC_SHA1_80:
-      CSFLogDebug(LOGTAG,
+      MOZ_MTLOG(ML_DEBUG,
                   "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80");
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
       break;
     case SRTP_AES128_CM_HMAC_SHA1_32:
-      CSFLogDebug(LOGTAG,
+      MOZ_MTLOG(ML_DEBUG,
                   "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32");
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
       srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // 80-bit per RFC 5764
       break;                                                   // S 4.1.2.
     default:
-      CSFLogError(LOGTAG, "Request to set unknown SRTP cipher suite");
+      MOZ_MTLOG(ML_ERROR, "Request to set unknown SRTP cipher suite");
       return nullptr;
   }
   // This key is copied into the srtp_t object, so we don't
   // need to keep it.
   policy.key = const_cast<unsigned char *>(
       static_cast<const unsigned char *>(key));
   policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound;
   policy.ssrc.value = 0;
   policy.ekt = nullptr;
   policy.window_size = 1024;   // Use the Chrome value.  Needs to be revisited.  Default is 128
   policy.allow_repeat_tx = 1;  // Use Chrome value; needed for NACK mode to work
   policy.next = nullptr;
 
   // Now make the session
   srtp_err_status_t r = srtp_create(&flow->session_, &policy);
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error creating srtp session");
+    MOZ_MTLOG(ML_ERROR, "Error creating srtp session");
     return nullptr;
   }
 
   return flow;
 }
 
 
 nsresult SrtpFlow::CheckInputs(bool protect, void *in, int in_len,
                                int max_len, int *out_len) {
   MOZ_ASSERT(in);
   if (!in) {
-    CSFLogError(LOGTAG, "NULL input value");
+    MOZ_MTLOG(ML_ERROR, "NULL input value");
     return NS_ERROR_NULL_POINTER;
   }
 
   if (in_len < 0) {
-    CSFLogError(LOGTAG, "Input length is negative");
+    MOZ_MTLOG(ML_ERROR, "Input length is negative");
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (max_len < 0) {
-    CSFLogError(LOGTAG, "Max output length is negative");
+    MOZ_MTLOG(ML_ERROR, "Max output length is negative");
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (protect) {
     if ((max_len < SRTP_MAX_EXPANSION) ||
         ((max_len - SRTP_MAX_EXPANSION) < in_len)) {
-      CSFLogError(LOGTAG, "Output too short");
+      MOZ_MTLOG(ML_ERROR, "Output too short");
       return NS_ERROR_ILLEGAL_VALUE;
     }
   }
   else {
     if (in_len > max_len) {
-      CSFLogError(LOGTAG, "Output too short");
+      MOZ_MTLOG(ML_ERROR, "Output too short");
       return NS_ERROR_ILLEGAL_VALUE;
     }
   }
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::ProtectRtp(void *in, int in_len,
@@ -135,117 +130,117 @@ nsresult SrtpFlow::ProtectRtp(void *in, 
   nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_protect(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error protecting SRTP packet");
+    MOZ_MTLOG(ML_ERROR, "Error protecting SRTP packet");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
 
-  CSFLogDebug(LOGTAG, "Successfully protected an SRTP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully protected an SRTP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::UnprotectRtp(void *in, int in_len,
                                 int max_len, int *out_len) {
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_unprotect(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error unprotecting SRTP packet error=%d", (int)r);
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully unprotected an SRTP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::ProtectRtcp(void *in, int in_len,
                                int max_len, int *out_len) {
   nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_protect_rtcp(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error protecting SRTCP packet");
+    MOZ_MTLOG(ML_ERROR, "Error protecting SRTCP packet");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully protected an SRTCP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully protected an SRTCP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 nsresult SrtpFlow::UnprotectRtcp(void *in, int in_len,
                                  int max_len, int *out_len) {
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len);
 
   if (r != srtp_err_status_ok) {
-    CSFLogError(LOGTAG, "Error unprotecting SRTCP packet error=%d", (int)r);
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
-  CSFLogDebug(LOGTAG, "Successfully unprotected an SRTCP packet of len %d",
-              *out_len);
+  MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTCP packet of len "
+                      << *out_len);
 
   return NS_OK;
 }
 
 // Statics
 void SrtpFlow::srtp_event_handler(srtp_event_data_t *data) {
   // TODO(ekr@rtfm.com): Implement this
   MOZ_CRASH();
 }
 
 nsresult SrtpFlow::Init() {
   if (!initialized) {
     srtp_err_status_t r = srtp_init();
     if (r != srtp_err_status_ok) {
-      CSFLogError(LOGTAG, "Could not initialize SRTP");
+      MOZ_MTLOG(ML_ERROR, "Could not initialize SRTP");
       MOZ_ASSERT(PR_FALSE);
       return NS_ERROR_FAILURE;
     }
 
     r = srtp_install_event_handler(&SrtpFlow::srtp_event_handler);
     if (r != srtp_err_status_ok) {
-      CSFLogError(LOGTAG, "Could not install SRTP event handler");
+      MOZ_MTLOG(ML_ERROR, "Could not install SRTP event handler");
       MOZ_ASSERT(PR_FALSE);
       return NS_ERROR_FAILURE;
     }
 
     initialized = true;
   }
 
   return NS_OK;
rename from media/webrtc/signaling/src/mediapipeline/SrtpFlow.h
rename to media/mtransport/SrtpFlow.h
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -12,23 +12,25 @@ EXPORTS.mtransport += [
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../nricestunaddr.h',
     '../rlogconnector.h',
     '../runnable_utils.h',
     '../sigslot.h',
     '../simpletokenbucket.h',
+    '../SrtpFlow.h',
     '../stun_socket_filter.h',
     '../transportflow.h',
     '../transportlayer.h',
     '../transportlayerdtls.h',
     '../transportlayerice.h',
     '../transportlayerlog.h',
     '../transportlayerloopback.h',
+    '../transportlayersrtp.h',
 ]
 
 include('../common.build')
 
 # Add libFuzzer configuration directives
 include('/tools/fuzzing/libfuzzer-config.mozbuild')
 
 # These files cannot be built in unified mode because of the redefinition of
--- a/media/mtransport/common.build
+++ b/media/mtransport/common.build
@@ -12,24 +12,26 @@ mtransport_lcppsrcs = [
     'nricectxhandler.cpp',
     'nricemediastream.cpp',
     'nriceresolver.cpp',
     'nriceresolverfake.cpp',
     'nricestunaddr.cpp',
     'nrinterfaceprioritizer.cpp',
     'rlogconnector.cpp',
     'simpletokenbucket.cpp',
+    'SrtpFlow.cpp',
     'stun_socket_filter.cpp',
     'test_nr_socket.cpp',
     'transportflow.cpp',
     'transportlayer.cpp',
     'transportlayerdtls.cpp',
     'transportlayerice.cpp',
     'transportlayerlog.cpp',
     'transportlayerloopback.cpp',
+    'transportlayersrtp.cpp',
 ]
 
 mtransport_cppsrcs = [
     '/media/mtransport/%s' % s for s in sorted(mtransport_lcppsrcs)
 ]
 
 LOCAL_INCLUDES += [
     '/media/mtransport/',
@@ -42,16 +44,18 @@ LOCAL_INCLUDES += [
     '/media/mtransport/third_party/nrappkit/src/event',
     '/media/mtransport/third_party/nrappkit/src/log',
     '/media/mtransport/third_party/nrappkit/src/plugin',
     '/media/mtransport/third_party/nrappkit/src/port/generic/include',
     '/media/mtransport/third_party/nrappkit/src/registry',
     '/media/mtransport/third_party/nrappkit/src/share',
     '/media/mtransport/third_party/nrappkit/src/stats',
     '/media/mtransport/third_party/nrappkit/src/util/libekr',
+    '/netwerk/srtp/src/crypto/include',
+    '/netwerk/srtp/src/include',
 ]
 
 if CONFIG['OS_TARGET'] in ['Darwin', 'DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
     if CONFIG['OS_TARGET'] == 'Darwin':
         DEFINES['DARWIN'] = True
     else:
         DEFINES['BSD'] = True
     LOCAL_INCLUDES += [
--- a/media/mtransport/test/sctp_unittest.cpp
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -153,16 +153,17 @@ class TransportTestPeer : public sigslot
 
     std::cerr << "Calling usrsctp_connect()" << std::endl;
     r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
         &remote_addr_), sizeof(remote_addr_));
     ASSERT_GE(0, r);
   }
 
   void Disconnect_s() {
+    disconnect_all();
     if (flow_) {
       flow_ = nullptr;
     }
   }
 
   void Disconnect() {
     loopback_->Disconnect();
   }
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayersrtp.cpp
@@ -0,0 +1,267 @@
+/* -*- 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"
+#include "transportlayerdtls.h"
+#include "srtp.h"
+#include "databuffer.h"
+#include "nsAutoPtr.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;
+}
+
+static bool
+IsRtp(const unsigned char* aData, size_t aLen)
+{
+  if (aLen < 2)
+    return false;
+
+  // Check if this is a RTCP packet. Logic based on the types listed in
+  // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
+
+  // Anything outside this range is RTP.
+  if ((aData[1] < 192) || (aData[1] > 207))
+    return true;
+
+  if (aData[1] == 192) // FIR
+    return false;
+
+  if (aData[1] == 193) // NACK, but could also be RTP. This makes us sad
+    return true;      // but it's how webrtc.org behaves.
+
+  if (aData[1] == 194)
+    return true;
+
+  if (aData[1] == 195) // IJ.
+    return false;
+
+  if ((aData[1] > 195) && (aData[1] < 200)) // the > 195 is redundant
+    return true;
+
+  if ((aData[1] >= 200) && (aData[1] <= 207)) // SR, RR, SDES, BYE,
+    return false;                           // APP, RTPFB, PSFB, XR
+
+  MOZ_ASSERT(false); // Not reached, belt and suspenders.
+  return true;
+}
+
+TransportResult
+TransportLayerSrtp::SendPacket(const unsigned char* data, size_t len)
+{
+  if (len < 4) {
+    MOZ_ASSERT(false);
+    return TE_ERROR;
+  }
+
+  // Make copy and add some room to expand.
+  nsAutoPtr<DataBuffer> buf(
+    new DataBuffer(data, len, len + SRTP_MAX_EXPANSION));
+
+  int out_len;
+  nsresult res;
+  if (IsRtp(data, len)) {
+    MOZ_MTLOG(ML_INFO, "Attempting to protect RTP...");
+    res = mSendSrtp->ProtectRtp(
+      buf->data(), buf->len(), buf->capacity(), &out_len);
+  } else {
+    MOZ_MTLOG(ML_INFO, "Attempting to protect RTCP...");
+    res = mSendSrtp->ProtectRtcp(
+      buf->data(), buf->len(), buf->capacity(), &out_len);
+  }
+
+  if (NS_FAILED(res)) {
+    MOZ_MTLOG(ML_ERROR,
+                "Error protecting RTP/RTCP len=" << len
+                << "[" << std::hex
+                << buf->data()[0] << " "
+                << buf->data()[1] << " "
+                << buf->data()[2] << " "
+                << buf->data()[3]
+                << "]");
+    return TE_ERROR;
+  }
+
+  // paranoia; don't have uninitialized bytes included in data->len()
+  buf->SetLength(out_len);
+
+  TransportResult bytes = downward_->SendPacket(buf->data(), buf->len());
+  if (bytes == static_cast<int>(buf->len())) {
+    // Whole packet was written, but the encrypted length might be different.
+    // Don't confuse the caller.
+    return len;
+  }
+
+  if (bytes == TE_WOULDBLOCK) {
+    return TE_WOULDBLOCK;
+  }
+
+  return TE_ERROR;
+}
+
+void
+TransportLayerSrtp::StateChange(TransportLayer* layer, State state)
+{
+  if (state == TS_OPEN) {
+    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_ERROR, "Failed to negotiate DTLS-SRTP. This is an error");
+      TL_SET_STATE(TS_ERROR);
+      return;
+    }
+
+    // SRTP Key Exporter as per RFC 5764 S 4.2
+    unsigned char srtp_block[SRTP_TOTAL_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_TOTAL_KEY_LENGTH];
+    unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
+    int offset = 0;
+    memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
+    offset += SRTP_MASTER_KEY_LENGTH;
+    memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
+    offset += SRTP_MASTER_KEY_LENGTH;
+    memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
+           srtp_block + offset,
+           SRTP_MASTER_SALT_LENGTH);
+    offset += SRTP_MASTER_SALT_LENGTH;
+    memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
+           srtp_block + offset,
+           SRTP_MASTER_SALT_LENGTH);
+    offset += SRTP_MASTER_SALT_LENGTH;
+    MOZ_ASSERT(offset == sizeof(srtp_block));
+
+    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, SRTP_TOTAL_KEY_LENGTH);
+    mRecvSrtp =
+      SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
+    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,
+                                   const unsigned char *data,
+                                   size_t len)
+{
+  if (state() != TS_OPEN) {
+    return;
+  }
+
+  if (len < 4) {
+    return;
+  }
+
+  // not RTP/RTCP per RFC 7983
+  if (data[0] <= 127 || data[0] >= 192) {
+    return;
+  }
+
+  // Make a copy rather than cast away constness
+  auto innerData = MakeUnique<unsigned char[]>(len);
+  memcpy(innerData.get(), data, len);
+  int outLen;
+  nsresult res;
+
+  if (IsRtp(innerData.get(), len)) {
+    MOZ_MTLOG(ML_INFO, "Attempting to unprotect RTP...");
+    res = mRecvSrtp->UnprotectRtp(innerData.get(), len, len, &outLen);
+  } else {
+    MOZ_MTLOG(ML_INFO, "Attempting to unprotect RTCP...");
+    res = mRecvSrtp->UnprotectRtcp(innerData.get(), len, len, &outLen);
+  }
+
+  if (NS_SUCCEEDED(res)) {
+    SignalPacketReceived(this, innerData.get(), outLen);
+  } else {
+    MOZ_MTLOG(ML_ERROR,
+                "Error unprotecting RTP/RTCP len=" << len
+                << "[" << std::hex
+                << innerData[0] << " "
+                << innerData[1] << " "
+                << innerData[2] << " "
+                << innerData[3]
+                << "]");
+  }
+}
+
+} // namespace mozilla
+
+
new file mode 100644
--- /dev/null
+++ b/media/mtransport/transportlayersrtp.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#ifndef transportlayersrtp_h__
+#define transportlayersrtp_h__
+
+#include <string>
+
+#include "transportlayer.h"
+#include "mozilla/RefPtr.h"
+#include "SrtpFlow.h"
+
+namespace mozilla {
+
+class TransportLayerDtls;
+
+class TransportLayerSrtp final : public TransportLayer {
+  public:
+    explicit TransportLayerSrtp(TransportLayerDtls& dtls);
+    virtual ~TransportLayerSrtp() {};
+
+    // Transport layer overrides.
+    void WasInserted() override;
+    TransportResult SendPacket(const unsigned char *data, size_t len) override;
+
+    // Signals
+    void StateChange(TransportLayer *layer, State state);
+    void PacketReceived(TransportLayer* layer, const unsigned char *data,
+                        size_t len);
+
+    TRANSPORT_LAYER_ID("srtp")
+
+  private:
+    bool Setup();
+    DISALLOW_COPY_ASSIGN(TransportLayerSrtp);
+    RefPtr<SrtpFlow> mSendSrtp;
+    RefPtr<SrtpFlow> mRecvSrtp;
+};
+
+
+}  // close namespace
+#endif
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
@@ -19,16 +19,17 @@
 #include "MediaPipeline.h"
 #include "MediaPipelineFilter.h"
 #include "MediaStreamGraph.h"
 #include "MediaStreamListener.h"
 #include "MediaStreamTrack.h"
 #include "transportflow.h"
 #include "transportlayerloopback.h"
 #include "transportlayerdtls.h"
+#include "transportlayersrtp.h"
 #include "mozilla/SyncRunnable.h"
 #include "mtransport_test_utils.h"
 #include "SharedBuffer.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 using namespace mozilla;
@@ -164,97 +165,69 @@ public:
       }
     }
 };
 
 class TransportInfo {
  public:
   TransportInfo() :
     flow_(nullptr),
-    loopback_(nullptr),
-    dtls_(nullptr) {}
+    loopback_(nullptr) {}
 
   static void InitAndConnect(TransportInfo &client, TransportInfo &server) {
     client.Init(true);
     server.Init(false);
-    client.PushLayers();
-    server.PushLayers();
     client.Connect(&server);
     server.Connect(&client);
   }
 
   void Init(bool client) {
-    nsresult res;
-
-    flow_ = new TransportFlow();
-    loopback_ = new TransportLayerLoopback();
-    dtls_ = new TransportLayerDtls();
-
-    res = loopback_->Init();
-    if (res != NS_OK) {
-      FreeLayers();
-    }
-    ASSERT_EQ((nsresult)NS_OK, res);
+    UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback);
+    UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls);
+    UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls));
 
     std::vector<uint16_t> ciphers;
     ciphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
-    dtls_->SetSrtpCiphers(ciphers);
-    dtls_->SetIdentity(DtlsIdentity::Generate());
-    dtls_->SetRole(client ? TransportLayerDtls::CLIENT :
+    dtls->SetSrtpCiphers(ciphers);
+    dtls->SetIdentity(DtlsIdentity::Generate());
+    dtls->SetRole(client ? TransportLayerDtls::CLIENT :
       TransportLayerDtls::SERVER);
-    dtls_->SetVerificationAllowAll();
-  }
+    dtls->SetVerificationAllowAll();
 
-  void PushLayers() {
-    if (NS_FAILED(loopback_->Init())) {
-      delete loopback_;
-      loopback_ = nullptr;
-    }
+    ASSERT_EQ(NS_OK, loopback->Init());
+    ASSERT_EQ(NS_OK, dtls->Init());
+    ASSERT_EQ(NS_OK, srtp->Init());
 
-    if (NS_FAILED(dtls_->Init())) {
-      delete dtls_;
-      dtls_ = nullptr;
-    }
+    dtls->Chain(loopback.get());
+    srtp->Chain(loopback.get());
 
-    ASSERT_TRUE(loopback_);
-    ASSERT_TRUE(dtls_);
-
-    dtls_->Chain(loopback_);
+    flow_ = new TransportFlow();
+    loopback_ = loopback.release();
     flow_->PushLayer(loopback_);
-    flow_->PushLayer(dtls_);
+    flow_->PushLayer(dtls.release());
+    flow_->PushLayer(srtp.release());
   }
 
   void Connect(TransportInfo* peer) {
     MOZ_ASSERT(loopback_);
     MOZ_ASSERT(peer->loopback_);
 
     loopback_->Connect(peer->loopback_);
   }
 
-  // Free the memory allocated at the beginning of Init
-  // if failure occurs before layers setup.
-  void FreeLayers() {
-    delete loopback_;
-    loopback_ = nullptr;
-    delete dtls_;
-    dtls_ = nullptr;
-  }
-
   void Shutdown() {
     if (loopback_) {
       loopback_->Disconnect();
     }
     loopback_ = nullptr;
-    dtls_ = nullptr;
     flow_ = nullptr;
   }
 
   RefPtr<TransportFlow> flow_;
   TransportLayerLoopback *loopback_;
-  TransportLayerDtls *dtls_;
 };
 
 class TestAgent {
  public:
   TestAgent() :
       audio_config_(109, "opus", 48000, 960, 2, 64000, false),
       audio_conduit_(mozilla::AudioSessionConduit::Create()),
       audio_pipeline_(),
@@ -369,19 +342,19 @@ class TestAgentSend : public TestAgent {
     }
 
     RefPtr<MediaPipelineTransmit> audio_pipeline =
       new mozilla::MediaPipelineTransmit(
         test_pc,
         nullptr,
         test_utils->sts_target(),
         false,
-        audio_stream_track_.get(),
         audio_conduit_);
 
+    audio_pipeline->SetTrack(audio_stream_track_.get());
     audio_pipeline->Start();
 
     audio_pipeline_ = audio_pipeline;
 
     RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_);
     RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_);
 
     if (use_bundle_) {
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -670,33 +670,29 @@ protected:
   UniquePtr<AudioPacketizer<int16_t, int16_t>> mPacketizer;
   // A buffer to hold a single packet of audio.
   UniquePtr<int16_t[]> mPacket;
   nsTArray<int16_t> mInterleavedAudio;
   AlignedShortBuffer mOutputAudio;
   UniquePtr<AudioConverter> mAudioConverter;
 };
 
-static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
-
 MediaPipeline::MediaPipeline(const std::string& aPc,
                              DirectionType aDirection,
                              nsCOMPtr<nsIEventTarget> aMainThread,
                              nsCOMPtr<nsIEventTarget> aStsThread,
                              RefPtr<MediaSessionConduit> aConduit)
   : mDirection(aDirection)
   , mLevel(0)
   , mConduit(aConduit)
   , mRtp(nullptr, RTP)
   , mRtcp(nullptr, RTCP)
   , mMainThread(aMainThread)
   , mStsThread(aStsThread)
-  , mTransport(new PipelineTransport(this)) // PipelineTransport() will access
-                                            // this->mStsThread; moved here
-                                            // for safety
+  , mTransport(new PipelineTransport(aStsThread))
   , mRtpPacketsSent(0)
   , mRtcpPacketsSent(0)
   , mRtpPacketsReceived(0)
   , mRtcpPacketsReceived(0)
   , mRtpBytesSent(0)
   , mRtpBytesReceived(0)
   , mPc(aPc)
   , mRtpParser(webrtc::RtpHeaderParser::Create())
@@ -916,106 +912,33 @@ MediaPipeline::TransportReady_s(Transpor
   }
 
   CSFLogInfo(LOGTAG,
              "Transport ready for pipeline %p flow %s: %s",
              this,
              mDescription.c_str(),
              ToString(aInfo.mType));
 
-  // TODO(bcampen@mozilla.com): Should we disconnect from the flow on failure?
-  nsresult res;
-
-  // Now instantiate the SRTP objects
-  TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(
-    aInfo.mTransport->GetLayer(TransportLayerDtls::ID()));
-  MOZ_ASSERT(dtls); // DTLS is mandatory
-
-  uint16_t cipher_suite;
-  res = dtls->GetSrtpCipher(&cipher_suite);
-  if (NS_FAILED(res)) {
-    CSFLogError(LOGTAG, "Failed to negotiate DTLS-SRTP. This is an error");
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    return res;
-  }
-
-  // SRTP Key Exporter as per RFC 5764 S 4.2
-  unsigned char srtp_block[SRTP_TOTAL_KEY_LENGTH * 2];
-  res = dtls->ExportKeyingMaterial(
-    kDTLSExporterLabel, false, "", srtp_block, sizeof(srtp_block));
-  if (NS_FAILED(res)) {
-    CSFLogError(LOGTAG, "Failed to compute DTLS-SRTP keys. This is an error");
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    MOZ_CRASH(); // TODO: Remove once we have enough field experience to
-                 // know it doesn't happen. bug 798797. Note that the
-                 // code after this never executes.
-    return res;
-  }
-
-  // Slice and dice as per RFC 5764 S 4.2
-  unsigned char client_write_key[SRTP_TOTAL_KEY_LENGTH];
-  unsigned char server_write_key[SRTP_TOTAL_KEY_LENGTH];
-  int offset = 0;
-  memcpy(client_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
-  offset += SRTP_MASTER_KEY_LENGTH;
-  memcpy(server_write_key, srtp_block + offset, SRTP_MASTER_KEY_LENGTH);
-  offset += SRTP_MASTER_KEY_LENGTH;
-  memcpy(client_write_key + SRTP_MASTER_KEY_LENGTH,
-         srtp_block + offset,
-         SRTP_MASTER_SALT_LENGTH);
-  offset += SRTP_MASTER_SALT_LENGTH;
-  memcpy(server_write_key + SRTP_MASTER_KEY_LENGTH,
-         srtp_block + offset,
-         SRTP_MASTER_SALT_LENGTH);
-  offset += SRTP_MASTER_SALT_LENGTH;
-  MOZ_ASSERT(offset == sizeof(srtp_block));
-
-  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(!aInfo.mSendSrtp && !aInfo.mRecvSrtp);
-  aInfo.mSendSrtp =
-    SrtpFlow::Create(cipher_suite, false, write_key, SRTP_TOTAL_KEY_LENGTH);
-  aInfo.mRecvSrtp =
-    SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
-  if (!aInfo.mSendSrtp || !aInfo.mRecvSrtp) {
-    CSFLogError(
-      LOGTAG, "Couldn't create SRTP flow for %s", ToString(aInfo.mType));
-    aInfo.mState = StateType::MP_CLOSED;
-    UpdateRtcpMuxState(aInfo);
-    return NS_ERROR_FAILURE;
-  }
-
   if (mDirection == DirectionType::RECEIVE) {
     CSFLogInfo(LOGTAG,
                "Listening for %s packets received on %p",
                ToString(aInfo.mType),
-               dtls->downward());
+               aInfo.mSrtp);
 
     switch (aInfo.mType) {
       case RTP:
-        dtls->downward()->SignalPacketReceived.connect(
+        aInfo.mSrtp->SignalPacketReceived.connect(
           this, &MediaPipeline::RtpPacketReceived);
         break;
       case RTCP:
-        dtls->downward()->SignalPacketReceived.connect(
+        aInfo.mSrtp->SignalPacketReceived.connect(
           this, &MediaPipeline::RtcpPacketReceived);
         break;
       case MUX:
-        dtls->downward()->SignalPacketReceived.connect(
+        aInfo.mSrtp->SignalPacketReceived.connect(
           this, &MediaPipeline::PacketReceived);
         break;
       default:
         MOZ_CRASH();
     }
   }
 
   aInfo.mState = StateType::MP_OPEN;
@@ -1045,20 +968,16 @@ MediaPipeline::TransportFailed_s(Transpo
 }
 
 void
 MediaPipeline::UpdateRtcpMuxState(TransportInfo& aInfo)
 {
   if (aInfo.mType == MUX) {
     if (aInfo.mTransport == mRtcp.mTransport) {
       mRtcp.mState = aInfo.mState;
-      if (!mRtcp.mSendSrtp) {
-        mRtcp.mSendSrtp = aInfo.mSendSrtp;
-        mRtcp.mRecvSrtp = aInfo.mRecvSrtp;
-      }
     }
   }
 }
 
 nsresult
 MediaPipeline::SendPacket(TransportLayer* aLayer, const void* aData, int aLen)
 {
   ASSERT_ON_THREAD(mStsThread);
@@ -1161,24 +1080,21 @@ MediaPipeline::RtpPacketReceived(Transpo
     return;
   }
 
   if (mRtp.mState != StateType::MP_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; pipeline not open");
     return;
   }
 
-  if (mRtp.mDtls->state() != TransportLayer::TS_OPEN) {
+  if (mRtp.mSrtp->state() != TransportLayer::TS_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; transport not open");
     return;
   }
 
-  // This should never happen.
-  MOZ_ASSERT(mRtp.mRecvSrtp);
-
   if (!aLen) {
     return;
   }
 
   // Filter out everything but RTP/RTCP
   if (aData[0] < 128 || aData[0] > 191) {
     return;
   }
@@ -1227,51 +1143,28 @@ MediaPipeline::RtpPacketReceived(Transpo
       } else {
         csrcInfo->second.SetTimestamp(now);
       }
     }
   }
 
   mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtp, false, aData, aLen);
 
-  // Make a copy rather than cast away constness
-  auto innerData = MakeUnique<unsigned char[]>(aLen);
-  memcpy(innerData.get(), aData, aLen);
-  int outLen = 0;
-  nsresult res =
-    mRtp.mRecvSrtp->UnprotectRtp(innerData.get(), aLen, aLen, &outLen);
-  if (!NS_SUCCEEDED(res)) {
-    char tmp[16];
-
-    SprintfLiteral(tmp,
-                   "%.2x %.2x %.2x %.2x",
-                   innerData[0],
-                   innerData[1],
-                   innerData[2],
-                   innerData[3]);
-
-    CSFLogError(LOGTAG,
-                "Error unprotecting RTP in %s len= %zu [%s]",
-                mDescription.c_str(),
-                aLen,
-                tmp);
-    return;
-  }
   CSFLogDebug(LOGTAG, "%s received RTP packet.", mDescription.c_str());
-  IncrementRtpPacketsReceived(outLen);
+  IncrementRtpPacketsReceived(aLen);
   OnRtpPacketReceived();
 
   RtpLogger::LogPacket(
-    innerData.get(), outLen, true, true, header.headerLength, mDescription);
+    aData, aLen, true, true, header.headerLength, mDescription);
 
   mPacketDumper->Dump(
-    mLevel, dom::mozPacketDumpType::Rtp, false, innerData.get(), outLen);
+    mLevel, dom::mozPacketDumpType::Rtp, false, aData, aLen);
 
   (void)mConduit->ReceivedRTPPacket(
-    innerData.get(), outLen, header.ssrc); // Ignore error codes
+    aData, aLen, header.ssrc); // Ignore error codes
 }
 
 void
 MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer,
                                   const unsigned char* aData,
                                   size_t aLen)
 {
   if (!mTransport->Pipeline()) {
@@ -1284,17 +1177,17 @@ MediaPipeline::RtcpPacketReceived(Transp
     return;
   }
 
   if (mRtcp.mState != StateType::MP_OPEN) {
     CSFLogDebug(LOGTAG, "Discarding incoming packet; pipeline not open");
     return;
   }
 
-  if (mRtcp.mDtls->state() != TransportLayer::TS_OPEN) {
+  if (mRtcp.mSrtp->state() != TransportLayer::TS_OPEN) {
     CSFLogError(LOGTAG, "Discarding incoming packet; transport not open");
     return;
   }
 
   if (!aLen) {
     return;
   }
 
@@ -1308,38 +1201,24 @@ MediaPipeline::RtcpPacketReceived(Transp
   // TODO bug 1279153: remove SR check for reduced size RTCP
   if (mFilter && !mFilter->FilterSenderReport(aData, aLen)) {
     CSFLogWarn(LOGTAG, "Dropping incoming RTCP packet; filtered out");
     return;
   }
 
   mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtcp, false, aData, aLen);
 
-  // Make a copy rather than cast away constness
-  auto innerData = MakeUnique<unsigned char[]>(aLen);
-  memcpy(innerData.get(), aData, aLen);
-  int outLen;
-
-  nsresult res =
-    mRtcp.mRecvSrtp->UnprotectRtcp(innerData.get(), aLen, aLen, &outLen);
-
-  if (!NS_SUCCEEDED(res))
-    return;
-
   CSFLogDebug(LOGTAG, "%s received RTCP packet.", mDescription.c_str());
   IncrementRtcpPacketsReceived();
 
-  RtpLogger::LogPacket(innerData.get(), outLen, true, false, 0, mDescription);
+  RtpLogger::LogPacket(aData, aLen, true, false, 0, mDescription);
 
   mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false, aData, aLen);
 
-  MOZ_ASSERT(mRtcp.mRecvSrtp); // This should never happen
-
-  (void)mConduit->ReceivedRTCPPacket(innerData.get(),
-                                     outLen); // Ignore error codes
+  (void)mConduit->ReceivedRTCPPacket(aData, aLen); // Ignore error codes
 }
 
 bool
 MediaPipeline::IsRtp(const unsigned char* aData, size_t aLen) const
 {
   if (aLen < 2)
     return false;
 
@@ -1509,35 +1388,32 @@ protected:
   RefPtr<PipelineListener> mListener;
 };
 
 MediaPipelineTransmit::MediaPipelineTransmit(
   const std::string& aPc,
   nsCOMPtr<nsIEventTarget> aMainThread,
   nsCOMPtr<nsIEventTarget> aStsThread,
   bool aIsVideo,
-  dom::MediaStreamTrack* aDomTrack,
   RefPtr<MediaSessionConduit> aConduit)
   : MediaPipeline(aPc,
                   DirectionType::TRANSMIT,
                   aMainThread,
                   aStsThread,
                   aConduit)
   , mIsVideo(aIsVideo)
   , mListener(new PipelineListener(aConduit))
   , mFeeder(aIsVideo ? MakeAndAddRef<VideoFrameFeeder>(mListener)
                      : nullptr) // For video we send frames to an
                                 // async VideoFrameConverter that
                                 // calls back to a VideoFrameFeeder
                                 // that feeds I420 frames to
                                 // VideoConduit.
-  , mDomTrack(aDomTrack)
   , mTransmitting(false)
 {
-  SetDescription();
   if (!IsVideo()) {
     mAudioProcessing = MakeAndAddRef<AudioProxyThread>(
       static_cast<AudioSessionConduit*>(aConduit.get()));
     mListener->SetAudioProxy(mAudioProcessing);
   } else { // Video
     mConverter = MakeAndAddRef<VideoFrameConverter>();
     mConverter->AddListener(mFeeder);
     mListener->SetVideoFrameConverter(mConverter);
@@ -1709,17 +1585,17 @@ MediaPipelineTransmit::TransportReady_s(
   if (&aInfo == &mRtp) {
     mListener->SetActive(true);
   }
 
   return NS_OK;
 }
 
 nsresult
-MediaPipelineTransmit::ReplaceTrack(RefPtr<MediaStreamTrack>& aDomTrack)
+MediaPipelineTransmit::SetTrack(MediaStreamTrack* aDomTrack)
 {
   // MainThread, checked in calls we make
   if (aDomTrack) {
     nsString nsTrackId;
     aDomTrack->GetId(nsTrackId);
     std::string track_id(NS_ConvertUTF16toUTF8(nsTrackId).get());
     CSFLogDebug(
       LOGTAG,
@@ -1740,62 +1616,61 @@ MediaPipelineTransmit::ReplaceTrack(RefP
   }
   return NS_OK;
 }
 
 nsresult
 MediaPipeline::ConnectTransport_s(TransportInfo& aInfo)
 {
   MOZ_ASSERT(aInfo.mTransport);
-  MOZ_ASSERT(aInfo.mDtls);
+  MOZ_ASSERT(aInfo.mSrtp);
   ASSERT_ON_THREAD(mStsThread);
 
   // Look to see if the transport is ready
-  if (aInfo.mDtls->state() == TransportLayer::TS_OPEN) {
+  if (aInfo.mSrtp->state() == TransportLayer::TS_OPEN) {
     nsresult res = TransportReady_s(aInfo);
     if (NS_FAILED(res)) {
       CSFLogError(LOGTAG,
                   "Error calling TransportReady(); res=%u in %s",
                   static_cast<uint32_t>(res),
                   __FUNCTION__);
       return res;
     }
-  } else if (aInfo.mDtls->state() == TransportLayer::TS_ERROR) {
+  } else if (aInfo.mSrtp->state() == TransportLayer::TS_ERROR) {
     CSFLogError(
       LOGTAG, "%s transport is already in error state", ToString(aInfo.mType));
     TransportFailed_s(aInfo);
     return NS_ERROR_FAILURE;
   }
 
-  aInfo.mDtls->SignalStateChange.connect(this, &MediaPipeline::StateChange);
+  aInfo.mSrtp->SignalStateChange.connect(this, &MediaPipeline::StateChange);
 
   return NS_OK;
 }
 
 MediaPipeline::TransportInfo*
 MediaPipeline::GetTransportInfo_s(TransportLayer* aLayer)
 {
   ASSERT_ON_THREAD(mStsThread);
-  if (aLayer == mRtp.mDtls) {
+  if (aLayer == mRtp.mSrtp) {
     return &mRtp;
   }
 
-  if (aLayer == mRtcp.mDtls) {
+  if (aLayer == mRtcp.mSrtp) {
     return &mRtcp;
   }
 
   return nullptr;
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtpPacket(const uint8_t* aData, size_t aLen)
 {
-
-  nsAutoPtr<DataBuffer> buf(
-    new DataBuffer(aData, aLen, aLen + SRTP_MAX_EXPANSION));
+  // Might be nice to avoid this copy.
+  nsAutoPtr<DataBuffer> buf(new DataBuffer(aData, aLen));
 
   RUN_ON_THREAD(
     mStsThread,
     WrapRunnable(RefPtr<MediaPipeline::PipelineTransport>(this),
                  &MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s,
                  buf,
                  true),
     NS_DISPATCH_NORMAL);
@@ -1803,108 +1678,75 @@ MediaPipeline::PipelineTransport::SendRt
   return NS_OK;
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s(
   nsAutoPtr<DataBuffer> aData,
   bool aIsRtp)
 {
-
   ASSERT_ON_THREAD(mStsThread);
   if (!mPipeline) {
     return NS_OK; // Detached
   }
   TransportInfo& transport = aIsRtp ? mPipeline->mRtp : mPipeline->mRtcp;
 
-  if (!transport.mSendSrtp) {
-    CSFLogDebug(LOGTAG, "Couldn't write RTP/RTCP packet; SRTP not set up yet");
+  if (transport.mSrtp->state() != TransportLayer::TS_OPEN) {
+    // SRTP not ready yet.
     return NS_OK;
   }
 
   MOZ_ASSERT(transport.mTransport);
   NS_ENSURE_TRUE(transport.mTransport, NS_ERROR_NULL_POINTER);
 
-  // libsrtp enciphers in place, so we need a big enough buffer.
-  MOZ_ASSERT(aData->capacity() >= aData->len() + SRTP_MAX_EXPANSION);
-
   if (RtpLogger::IsPacketLoggingOn()) {
     int headerLen = 12;
     webrtc::RTPHeader header;
     if (mPipeline->mRtpParser &&
         mPipeline->mRtpParser->Parse(aData->data(), aData->len(), &header)) {
       headerLen = header.headerLength;
     }
     RtpLogger::LogPacket(aData->data(),
                          aData->len(),
                          false,
                          aIsRtp,
                          headerLen,
                          mPipeline->mDescription);
   }
 
-  int out_len;
-  nsresult res;
   if (aIsRtp) {
     mPipeline->mPacketDumper->Dump(mPipeline->Level(),
                                     dom::mozPacketDumpType::Rtp,
                                     true,
                                     aData->data(),
                                     aData->len());
-
-    res = transport.mSendSrtp->ProtectRtp(
-      aData->data(), aData->len(), aData->capacity(), &out_len);
+    mPipeline->IncrementRtpPacketsSent(aData->len());
   } else {
     mPipeline->mPacketDumper->Dump(mPipeline->Level(),
                                     dom::mozPacketDumpType::Rtcp,
                                     true,
                                     aData->data(),
                                     aData->len());
-
-    res = transport.mSendSrtp->ProtectRtcp(
-      aData->data(), aData->len(), aData->capacity(), &out_len);
+    mPipeline->IncrementRtcpPacketsSent();
   }
-  if (!NS_SUCCEEDED(res)) {
-    return res;
-  }
-
-  // paranoia; don't have uninitialized bytes included in data->len()
-  aData->SetLength(out_len);
 
   CSFLogDebug(LOGTAG,
               "%s sending %s packet",
               mPipeline->mDescription.c_str(),
               (aIsRtp ? "RTP" : "RTCP"));
-  if (aIsRtp) {
-    mPipeline->mPacketDumper->Dump(mPipeline->Level(),
-                                    dom::mozPacketDumpType::Srtp,
-                                    true,
-                                    aData->data(),
-                                    out_len);
 
-    mPipeline->IncrementRtpPacketsSent(out_len);
-  } else {
-    mPipeline->mPacketDumper->Dump(mPipeline->Level(),
-                                    dom::mozPacketDumpType::Srtcp,
-                                    true,
-                                    aData->data(),
-                                    out_len);
-
-    mPipeline->IncrementRtcpPacketsSent();
-  }
-  return mPipeline->SendPacket(transport.mDtls->downward(), aData->data(), out_len);
+  return mPipeline->SendPacket(transport.mSrtp, aData->data(), aData->len());
 }
 
 nsresult
 MediaPipeline::PipelineTransport::SendRtcpPacket(const uint8_t* aData,
                                                  size_t aLen)
 {
-
-  nsAutoPtr<DataBuffer> buf(
-    new DataBuffer(aData, aLen, aLen + SRTP_MAX_EXPANSION));
+  // Might be nice to avoid this copy.
+  nsAutoPtr<DataBuffer> buf(new DataBuffer(aData, aLen));
 
   RUN_ON_THREAD(
     mStsThread,
     WrapRunnable(RefPtr<MediaPipeline::PipelineTransport>(this),
                  &MediaPipeline::PipelineTransport::SendRtpRtcpPacket_s,
                  buf,
                  false),
     NS_DISPATCH_NORMAL);
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -180,19 +180,19 @@ public:
 
   typedef enum { RTP, RTCP, MUX, MAX_RTP_TYPE } RtpType;
 
   // Separate class to allow ref counting
   class PipelineTransport : public TransportInterface
   {
   public:
     // Implement the TransportInterface functions
-    explicit PipelineTransport(MediaPipeline* aPipeline)
-      : mPipeline(aPipeline)
-      , mStsThread(aPipeline->mStsThread)
+    explicit PipelineTransport(nsIEventTarget* aStsThread)
+      : mPipeline(nullptr)
+      , mStsThread(aStsThread)
     {
     }
 
     void Attach(MediaPipeline* pipeline) { mPipeline = pipeline; }
     void Detach() { mPipeline = nullptr; }
     MediaPipeline* Pipeline() const { return mPipeline; }
 
     virtual nsresult SendRtpPacket(const uint8_t* aData, size_t aLen) override;
@@ -210,35 +210,31 @@ protected:
   virtual ~MediaPipeline();
   nsresult AttachTransport_s();
   friend class PipelineTransport;
 
   struct TransportInfo
   {
     TransportInfo(RefPtr<TransportFlow> aFlow, RtpType aType)
       : mTransport(aFlow)
-      , mDtls(mTransport ? mTransport->GetLayer("dtls") : nullptr)
+      , mSrtp(mTransport ? mTransport->GetLayer("srtp") : nullptr)
       , mState(StateType::MP_CONNECTING)
       , mType(aType)
     {
     }
 
     void Detach()
     {
       mTransport = nullptr;
-      mDtls = nullptr;
-      mSendSrtp = nullptr;
-      mRecvSrtp = nullptr;
+      mSrtp = nullptr;
     }
 
     RefPtr<TransportFlow> mTransport;
-    TransportLayer* mDtls;
+    TransportLayer* mSrtp;
     StateType mState;
-    RefPtr<SrtpFlow> mSendSrtp;
-    RefPtr<SrtpFlow> mRecvSrtp;
     RtpType mType;
   };
 
   // The transport is down
   virtual nsresult TransportFailed_s(TransportInfo& aInfo);
   // The transport is ready
   virtual nsresult TransportReady_s(TransportInfo& aInfo);
   void UpdateRtcpMuxState(TransportInfo& aInfo);
@@ -324,17 +320,16 @@ private:
 class MediaPipelineTransmit : public MediaPipeline
 {
 public:
   // Set aRtcpTransport to nullptr to use rtcp-mux
   MediaPipelineTransmit(const std::string& aPc,
                         nsCOMPtr<nsIEventTarget> aMainThread,
                         nsCOMPtr<nsIEventTarget> aStsThread,
                         bool aIsVideo,
-                        dom::MediaStreamTrack* aDomTrack,
                         RefPtr<MediaSessionConduit> aConduit);
 
   void Start() override;
   void Stop() override;
 
   // written and used from MainThread
   bool IsVideo() const override;
 
@@ -350,17 +345,17 @@ public:
 
   // Override MediaPipeline::TransportReady.
   nsresult TransportReady_s(TransportInfo& aInfo) override;
 
   // Replace a track with a different one
   // In non-compliance with the likely final spec, allow the new
   // track to be part of a different stream (since we don't support
   // multiple tracks of a type in a stream yet).  bug 1056650
-  virtual nsresult ReplaceTrack(RefPtr<dom::MediaStreamTrack>& aDomTrack);
+  virtual nsresult SetTrack(dom::MediaStreamTrack* aDomTrack);
 
   // Separate classes to allow ref counting
   class PipelineListener;
   class VideoFrameFeeder;
 
 protected:
   ~MediaPipelineTransmit();
 
--- a/media/webrtc/signaling/src/mediapipeline/moz.build
+++ b/media/webrtc/signaling/src/mediapipeline/moz.build
@@ -18,12 +18,11 @@ LOCAL_INCLUDES += [
     '/netwerk/srtp/src/crypto/include',
     '/netwerk/srtp/src/include',
 ]
 
 UNIFIED_SOURCES += [
     'MediaPipeline.cpp',
     'MediaPipelineFilter.cpp',
     'RtpLogger.cpp',
-    'SrtpFlow.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -14,16 +14,17 @@
 #include "nricemediastream.h"
 #include "MediaPipelineFilter.h"
 #include "MediaPipeline.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionMedia.h"
 #include "runnable_utils.h"
 #include "transportlayerice.h"
 #include "transportlayerdtls.h"
+#include "transportlayersrtp.h"
 #include "signaling/src/jsep/JsepSession.h"
 #include "signaling/src/jsep/JsepTransport.h"
 
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIURI.h"
 #include "nsIScriptSecurityManager.h"
@@ -525,26 +526,30 @@ PeerConnectionMedia::UpdateTransportFlow
 // Accessing the PCMedia should be safe here because we shouldn't
 // have enqueued this function unless it was still active and
 // the ICE data is destroyed on the STS.
 static void
 FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,
                         RefPtr<TransportFlow> aFlow, size_t aLevel,
                         bool aIsRtcp,
                         TransportLayerIce* aIceLayer,
-                        TransportLayerDtls* aDtlsLayer)
+                        TransportLayerDtls* aDtlsLayer,
+                        TransportLayerSrtp* aSrtpLayer)
 {
   aIceLayer->SetParameters(aPCMedia->ice_media_stream(aLevel),
                            aIsRtcp ? 2 : 1);
   // TODO(bug 854518): Process errors.
   (void)aIceLayer->Init();
   (void)aDtlsLayer->Init();
+  (void)aSrtpLayer->Init();
   aDtlsLayer->Chain(aIceLayer);
+  aSrtpLayer->Chain(aIceLayer);
   aFlow->PushLayer(aIceLayer);
   aFlow->PushLayer(aDtlsLayer);
+  aFlow->PushLayer(aSrtpLayer);
 }
 
 static void
 AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,
                             RefPtr<TransportFlow> aFlow,
                             size_t aLevel,
                             bool aIsRtcp)
 {
@@ -594,16 +599,17 @@ PeerConnectionMedia::UpdateTransportFlow
 
   std::ostringstream osId;
   osId << mParentHandle << ":" << aLevel << "," << (aIsRtcp ? "rtcp" : "rtp");
   flow = new TransportFlow(osId.str());
 
   // The media streams are made on STS so we need to defer setup.
   auto ice = MakeUnique<TransportLayerIce>();
   auto dtls = MakeUnique<TransportLayerDtls>();
+  auto srtp = MakeUnique<TransportLayerSrtp>(*dtls);
   dtls->SetRole(aTransport.mDtls->GetRole() ==
                         JsepDtlsTransport::kJsepDtlsClient
                     ? TransportLayerDtls::CLIENT
                     : TransportLayerDtls::SERVER);
 
   RefPtr<DtlsIdentity> pcid = mParent->Identity();
   if (!pcid) {
     CSFLogError(LOGTAG, "Failed to get DTLS identity.");
@@ -648,17 +654,17 @@ PeerConnectionMedia::UpdateTransportFlow
   if (NS_FAILED(rv)) {
     CSFLogError(LOGTAG, "Couldn't set ALPN");
     return rv;
   }
 
   RefPtr<PeerConnectionMedia> pcMedia(this);
   rv = GetSTSThread()->Dispatch(
       WrapRunnableNM(FinalizeTransportFlow_s, pcMedia, flow, aLevel, aIsRtcp,
-                     ice.release(), dtls.release()),
+                     ice.release(), dtls.release(), srtp.release()),
       NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
     CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s");
     return rv;
   }
 
   AddTransportFlow(aLevel, aIsRtcp, flow);
 
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -64,18 +64,19 @@ TransceiverImpl::TransceiverImpl(
 
   mConduit->SetPCHandle(mPCHandle);
 
   mTransmitPipeline = new MediaPipelineTransmit(
       mPCHandle,
       mMainThread.get(),
       mStsThread.get(),
       IsVideo(),
-      mSendTrack,
       mConduit);
+
+  mTransmitPipeline->SetTrack(mSendTrack);
 }
 
 TransceiverImpl::~TransceiverImpl() = default;
 
 NS_IMPL_ISUPPORTS0(TransceiverImpl)
 
 void
 TransceiverImpl::InitAudio()
@@ -153,17 +154,17 @@ TransceiverImpl::UpdateSendTrack(dom::Me
 {
   if (mJsepTransceiver->IsStopped()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   MOZ_MTLOG(ML_DEBUG, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
                       "(" << aSendTrack << ")");
   mSendTrack = aSendTrack;
-  return mTransmitPipeline->ReplaceTrack(mSendTrack);
+  return mTransmitPipeline->SetTrack(mSendTrack);
 }
 
 nsresult
 TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager)
 {
   if (!mJsepTransceiver->HasLevel()) {
     return NS_OK;
   }