Bug 1479665: add GCM ciphers to SrtpFlow. r=bwc
☠☠ backed out by c1933b9ea68d ☠ ☠
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Thu, 13 Sep 2018 17:38:50 +0000
changeset 436206 1a7771d373ff7fbc3e22e1a27a4c5fa8e0eb879f
parent 436205 c8eec40da8fd776ef48f185378ab8c2a54fcaadd
child 436207 9293d3493e0c3eb13a5cd4f56d6de5ae2ef77afd
push id69255
push usernohlmeier@mozilla.com
push dateThu, 13 Sep 2018 20:31:02 +0000
treeherderautoland@1a7771d373ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbwc
bugs1479665
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1479665: add GCM ciphers to SrtpFlow. r=bwc add GCM ciphers to SrtpFlow Differential Revision: https://phabricator.services.mozilla.com/D5731
media/mtransport/SrtpFlow.cpp
media/mtransport/SrtpFlow.h
media/mtransport/test/transport_unittests.cpp
media/mtransport/transportlayersrtp.cpp
media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
--- a/media/mtransport/SrtpFlow.cpp
+++ b/media/mtransport/SrtpFlow.cpp
@@ -21,42 +21,65 @@ MOZ_MTLOG_MODULE("mtransport")
 bool SrtpFlow::initialized;  // Static
 
 SrtpFlow::~SrtpFlow() {
   if (session_) {
     srtp_dealloc(session_);
   }
 }
 
+unsigned int SrtpFlow::KeySize(int cipher_suite) {
+  srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
+  return srtp_profile_get_master_key_length(profile);
+}
+
+unsigned int SrtpFlow::SaltSize(int cipher_suite) {
+  srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
+  return srtp_profile_get_master_salt_length(profile);
+}
+
 RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite,
                                            bool inbound,
                                            const void *key,
                                            size_t key_len) {
   nsresult res = Init();
   if (!NS_SUCCEEDED(res))
     return nullptr;
 
   RefPtr<SrtpFlow> flow = new SrtpFlow();
 
   if (!key) {
     MOZ_MTLOG(ML_ERROR, "Null SRTP key specified");
     return nullptr;
   }
 
-  if (key_len != SRTP_TOTAL_KEY_LENGTH) {
+  if ((key_len > SRTP_MAX_KEY_LENGTH) ||
+      (key_len < SRTP_MIN_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 kDtlsSrtpAeadAes256Gcm:
+      MOZ_MTLOG(ML_DEBUG,
+                  "Setting SRTP cipher suite SRTP_AEAD_AES_256_GCM");
+      srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
+      srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
+      break;
+    case kDtlsSrtpAeadAes128Gcm:
+      MOZ_MTLOG(ML_DEBUG,
+                  "Setting SRTP cipher suite SRTP_AEAD_AES_128_GCM");
+      srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
+      srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
+      break;
     case kDtlsSrtpAes128CmHmacSha1_80:
       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 kDtlsSrtpAes128CmHmacSha1_32:
       MOZ_MTLOG(ML_DEBUG,
--- a/media/mtransport/SrtpFlow.h
+++ b/media/mtransport/SrtpFlow.h
@@ -8,29 +8,37 @@
 #define srtpflow_h__
 
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 #include "srtp.h"
 
 namespace mozilla {
 
-#define SRTP_MASTER_KEY_LENGTH 16
-#define SRTP_MASTER_SALT_LENGTH 14
-#define SRTP_TOTAL_KEY_LENGTH (SRTP_MASTER_KEY_LENGTH + SRTP_MASTER_SALT_LENGTH)
+#define SRTP_ICM_MASTER_KEY_LENGTH 16
+#define SRTP_ICM_MASTER_SALT_LENGTH 14
+#define SRTP_ICM_MAX_MASTER_LEGNTH (SRTP_ICM_MASTER_KEY_LENGTH + SRTP_ICM_MASTER_SALT_LENGTH)
+#define SRTP_GCM_MASTER_KEY_LENGTH 32
+#define SRTP_GCM_MASTER_SALT_LENGTH 12
+#define SRTP_GCM_MAX_MASTER_LEGNTH (SRTP_GCM_MASTER_KEY_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH)
+
+#define SRTP_MIN_KEY_LENGTH SRTP_ICM_MAX_MASTER_LEGNTH 
+#define SRTP_MAX_KEY_LENGTH SRTP_GCM_MAX_MASTER_LEGNTH 
 
 // SRTCP requires an auth tag *plus* a 4-byte index-plus-'E'-bit value (see
 // RFC 3711)
 #define SRTP_MAX_EXPANSION (SRTP_MAX_TRAILER_LEN+4)
 
 
 class SrtpFlow {
   ~SrtpFlow();
  public:
 
+  static unsigned int KeySize(int cipher_suite);
+  static unsigned int SaltSize(int cipher_suite);
 
   static RefPtr<SrtpFlow> Create(int cipher_suite,
                                           bool inbound,
                                           const void *key,
                                           size_t key_len);
 
   nsresult ProtectRtp(void *in, int in_len,
                       int max_len, int *out_len);
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -532,16 +532,18 @@ class TransportTestPeer : public sigslot
 
       mask <<= 1;
     }
   }
 
   void SetupSrtp() {
     // this mimics the setup we do elsewhere
     std::vector<uint16_t> srtp_ciphers;
+    srtp_ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
+    srtp_ciphers.push_back(kDtlsSrtpAeadAes128Gcm);
     srtp_ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
     srtp_ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);
 
     SetSrtpCiphers(srtp_ciphers);
  }
 
   void SetSrtpCiphers(std::vector<uint16_t>& srtp_ciphers) {
     ASSERT_TRUE(NS_SUCCEEDED(dtls_->SetSrtpCiphers(srtp_ciphers)));
@@ -1012,17 +1014,17 @@ TEST_F(TransportTest, TestConnectSrtp) {
   SetupSrtp();
   SetDtlsPeer();
   DisableChaCha(p2_);
   ConnectSocket();
 
   ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite());
 
   // SRTP is on
-  ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
+  ASSERT_EQ(kDtlsSrtpAeadAes256Gcm, p1_->srtpCipher());
 }
 
 
 TEST_F(TransportTest, TestConnectDestroyFlowsMainThread) {
   SetDtlsPeer();
   ConnectSocket();
   DestroyPeerFlows();
 }
@@ -1404,16 +1406,32 @@ TEST_F(TransportTest, OnlyClientSendsSrt
   SetDtlsPeer();
   // This means that the server won't semd the extension as well.  The server
   // (p1) thinks that everything is OK.  The client (p2) notices the problem
   // after connecting and aborts.
   ConnectSocketExpectState(TransportLayer::TS_CLOSED,
                            TransportLayer::TS_ERROR);
 }
 
+TEST_F(TransportTest, TestSrtpFallback) {
+  std::vector<uint16_t> setA;
+  setA.push_back(kDtlsSrtpAeadAes256Gcm);
+  setA.push_back(kDtlsSrtpAes128CmHmacSha1_80);
+  std::vector<uint16_t> setB;
+  setB.push_back(kDtlsSrtpAes128CmHmacSha1_80);
+
+  p1_->SetSrtpCiphers(setA);
+  p2_->SetSrtpCiphers(setB);
+  SetDtlsPeer();
+  ConnectSocket();
+
+  ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
+  ASSERT_EQ(kDtlsSrtpAes128CmHmacSha1_80, p1_->srtpCipher());
+}
+
 // NSS doesn't support DHE suites on the server end.
 // This checks to see if we barf when that's the only option available.
 TEST_F(TransportTest, TestDheOnlyFails) {
   SetDtlsPeer();
 
   // p2_ is the client
   // setting this on p1_ (the server) causes NSS to assert
   ConfigureOneCipher(p2_, TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
--- a/media/mtransport/transportlayersrtp.cpp
+++ b/media/mtransport/transportlayersrtp.cpp
@@ -154,60 +154,60 @@ TransportLayerSrtp::StateChange(Transpor
     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;
     }
 
+    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_TOTAL_KEY_LENGTH * 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_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 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, SRTP_TOTAL_KEY_LENGTH);
+      SrtpFlow::Create(cipher_suite, false, write_key, master_key_size);
     mRecvSrtp =
-      SrtpFlow::Create(cipher_suite, true, read_key, SRTP_TOTAL_KEY_LENGTH);
+      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!");
   }
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
@@ -178,17 +178,17 @@ class TransportInfo {
   }
 
   void Init(bool client) {
     UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback);
     UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls);
     UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls));
 
     std::vector<uint16_t> ciphers;
-    ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
+    ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
     dtls->SetSrtpCiphers(ciphers);
     dtls->SetIdentity(DtlsIdentity::Generate());
     dtls->SetRole(client ? TransportLayerDtls::CLIENT :
       TransportLayerDtls::SERVER);
     dtls->SetVerificationAllowAll();
 
     ASSERT_EQ(NS_OK, loopback->Init());
     ASSERT_EQ(NS_OK, dtls->Init());
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -620,16 +620,18 @@ PeerConnectionMedia::UpdateTransportFlow
                                      fingerprint.fingerprint.size());
     if (NS_FAILED(rv)) {
       CSFLogError(LOGTAG, "Could not set fingerprint");
       return rv;
     }
   }
 
   std::vector<uint16_t> srtpCiphers;
+  srtpCiphers.push_back(kDtlsSrtpAeadAes256Gcm);
+  srtpCiphers.push_back(kDtlsSrtpAeadAes128Gcm);
   srtpCiphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
   srtpCiphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);
 
   rv = dtls->SetSrtpCiphers(srtpCiphers);
   if (NS_FAILED(rv)) {
     CSFLogError(LOGTAG, "Couldn't set SRTP ciphers");
     return rv;
   }