Add support for changing the TLS elliptic curve set.

This CL is almost identical to http://chromium-review.googlesource.com/c/611150

Bug: webrtc:8213
Change-Id: I21a8a0041a73b3171ed66b687dc47a579d45fe19
Reviewed-on: https://chromium-review.googlesource.com/653205
Commit-Queue: Diogo Real <diogor@google.com>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Emad Omara <emadomara@webrtc.org>
Reviewed-by: Zeke Chin <tkchin@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Original-Commit-Position: refs/heads/master@{#19755}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 7bd1f1bb5bc747af7050b22b288eebd930b9e528
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 6a2b904..e23b705 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -195,12 +195,15 @@
     std::string hostname;
     // List of protocols to be used in the TLS ALPN extension.
     std::vector<std::string> tls_alpn_protocols;
+    // List of elliptic curves to be used in the TLS elliptic curves extension.
+    std::vector<std::string> tls_elliptic_curves;
 
     bool operator==(const IceServer& o) const {
       return uri == o.uri && urls == o.urls && username == o.username &&
              password == o.password && tls_cert_policy == o.tls_cert_policy &&
              hostname == o.hostname &&
-             tls_alpn_protocols == o.tls_alpn_protocols;
+             tls_alpn_protocols == o.tls_alpn_protocols &&
+             tls_elliptic_curves == o.tls_elliptic_curves;
     }
     bool operator!=(const IceServer& o) const { return !(*this == o); }
   };
diff --git a/p2p/base/basicpacketsocketfactory.cc b/p2p/base/basicpacketsocketfactory.cc
index fe9eb4b..b0f464b 100644
--- a/p2p/base/basicpacketsocketfactory.cc
+++ b/p2p/base/basicpacketsocketfactory.cc
@@ -159,6 +159,7 @@
     }
 
     ssl_adapter->SetAlpnProtocols(tcp_options.tls_alpn_protocols);
+    ssl_adapter->SetEllipticCurves(tcp_options.tls_elliptic_curves);
 
     socket = ssl_adapter;
 
diff --git a/p2p/base/packetsocketfactory.h b/p2p/base/packetsocketfactory.h
index 34f568c..8ae0bd3 100644
--- a/p2p/base/packetsocketfactory.h
+++ b/p2p/base/packetsocketfactory.h
@@ -20,6 +20,7 @@
 struct PacketSocketTcpOptions {
   int opts;
   std::vector<std::string> tls_alpn_protocols;
+  std::vector<std::string> tls_elliptic_curves;
 };
 
 class AsyncPacketSocket;
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 0a53609..f47751f 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -536,7 +536,7 @@
     return TurnPort::Create(
         &main_, socket_factory, MakeNetwork(addr), 0, 0, username_, password_,
         ProtocolAddress(server_addr, int_proto), kRelayCredentials, 0,
-        std::string(), std::vector<std::string>());
+        std::string(), std::vector<std::string>(), std::vector<std::string>());
   }
   RelayPort* CreateGturnPort(const SocketAddress& addr,
                              ProtocolType int_proto, ProtocolType ext_proto) {
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index eef4305..63d7c71 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -192,6 +192,7 @@
   int priority = 0;
   TlsCertPolicy tls_cert_policy = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
   std::vector<std::string> tls_alpn_protocols;
+  std::vector<std::string> tls_elliptic_curves;
 };
 
 class PortAllocatorSession : public sigslot::has_slots<> {
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc
index 68dae82..71a0311 100644
--- a/p2p/base/turnport.cc
+++ b/p2p/base/turnport.cc
@@ -222,7 +222,8 @@
                    const RelayCredentials& credentials,
                    int server_priority,
                    const std::string& origin,
-                   const std::vector<std::string>& tls_alpn_protocols)
+                   const std::vector<std::string>& tls_alpn_protocols,
+                   const std::vector<std::string>& tls_elliptic_curves)
     : Port(thread,
            RELAY_PORT_TYPE,
            factory,
@@ -233,6 +234,7 @@
            password),
       server_address_(server_address),
       tls_alpn_protocols_(tls_alpn_protocols),
+      tls_elliptic_curves_(tls_elliptic_curves),
       credentials_(credentials),
       socket_(NULL),
       resolver_(NULL),
@@ -341,6 +343,7 @@
     rtc::PacketSocketTcpOptions tcp_options;
     tcp_options.opts = opts;
     tcp_options.tls_alpn_protocols = tls_alpn_protocols_;
+    tcp_options.tls_elliptic_curves = tls_elliptic_curves_;
     socket_ = socket_factory()->CreateClientTcpSocket(
         rtc::SocketAddress(Network()->GetBestIP(), 0), server_address_.address,
         proxy(), user_agent(), tcp_options);
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index 46200a9..4d9a811 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -70,10 +70,11 @@
                           const RelayCredentials& credentials,
                           int server_priority,
                           const std::string& origin,
-                          const std::vector<std::string>& tls_alpn_protocols) {
+                          const std::vector<std::string>& tls_alpn_protocols,
+                          const std::vector<std::string>& tls_elliptic_curves) {
     return new TurnPort(thread, factory, network, min_port, max_port, username,
                         password, server_address, credentials, server_priority,
-                        origin, tls_alpn_protocols);
+                        origin, tls_alpn_protocols, tls_elliptic_curves);
   }
 
   virtual ~TurnPort();
@@ -100,6 +101,10 @@
     return tls_alpn_protocols_;
   }
 
+  virtual std::vector<std::string> GetTlsEllipticCurves() const {
+    return tls_elliptic_curves_;
+  }
+
   virtual void PrepareAddress();
   virtual Connection* CreateConnection(
       const Candidate& c, PortInterface::CandidateOrigin origin);
@@ -192,7 +197,8 @@
            const RelayCredentials& credentials,
            int server_priority,
            const std::string& origin,
-           const std::vector<std::string>& alpn_protocols);
+           const std::vector<std::string>& tls_alpn_protocols,
+           const std::vector<std::string>& tls_elliptic_curves);
 
  private:
   enum {
@@ -273,6 +279,7 @@
   ProtocolAddress server_address_;
   TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
   std::vector<std::string> tls_alpn_protocols_;
+  std::vector<std::string> tls_elliptic_curves_;
   RelayCredentials credentials_;
   AttemptedServerSet attempted_server_addresses_;
 
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index 546c21e..a8e2937 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -263,7 +263,8 @@
     RelayCredentials credentials(username, password);
     turn_port_.reset(TurnPort::Create(
         &main_, &socket_factory_, network, 0, 0, kIceUfrag1, kIcePwd1,
-        server_address, credentials, 0, origin, std::vector<std::string>()));
+        server_address, credentials, 0, origin, std::vector<std::string>(),
+        std::vector<std::string>()));
     // This TURN port will be the controlling.
     turn_port_->SetIceRole(ICEROLE_CONTROLLING);
     ConnectSignals();
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index fe2dfb6..992aee6 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -1445,7 +1445,7 @@
           session_->allocator()->min_port(), session_->allocator()->max_port(),
           session_->username(), session_->password(), *relay_port,
           config.credentials, config.priority, session_->allocator()->origin(),
-          config.tls_alpn_protocols);
+          config.tls_alpn_protocols, config.tls_elliptic_curves);
     }
     RTC_DCHECK(port != NULL);
     port->SetTlsCertPolicy(config.tls_cert_policy);
diff --git a/pc/iceserverparsing.cc b/pc/iceserverparsing.cc
index 00e9f25..9b802fc 100644
--- a/pc/iceserverparsing.cc
+++ b/pc/iceserverparsing.cc
@@ -258,6 +258,7 @@
             cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK;
       }
       config.tls_alpn_protocols = server.tls_alpn_protocols;
+      config.tls_elliptic_curves = server.tls_elliptic_curves;
 
       turn_servers->push_back(config);
       break;
diff --git a/rtc_base/openssladapter.cc b/rtc_base/openssladapter.cc
index 9164258..d944fa0 100644
--- a/rtc_base/openssladapter.cc
+++ b/rtc_base/openssladapter.cc
@@ -31,6 +31,7 @@
 #include "webrtc/rtc_base/openssl.h"
 #include "webrtc/rtc_base/safe_conversions.h"
 #include "webrtc/rtc_base/sslroots.h"
+#include "webrtc/rtc_base/stringencode.h"
 #include "webrtc/rtc_base/stringutils.h"
 #include "webrtc/rtc_base/thread.h"
 
@@ -311,6 +312,10 @@
   alpn_protocols_ = protos;
 }
 
+void OpenSSLAdapter::SetEllipticCurves(const std::vector<std::string>& curves) {
+  elliptic_curves_ = curves;
+}
+
 void OpenSSLAdapter::SetMode(SSLMode mode) {
   RTC_DCHECK(!ssl_ctx_);
   RTC_DCHECK(state_ == SSL_NONE);
@@ -445,6 +450,10 @@
     }
   }
 
+  if (!elliptic_curves_.empty()) {
+    SSL_set1_curves_list(ssl_, rtc::join(elliptic_curves_, ':').c_str());
+  }
+
   // Now that the initial config is done, transfer ownership of |bio| to the
   // SSL object. If ContinueSSL() fails, the bio will be freed in Cleanup().
   SSL_set_bio(ssl_, bio, bio);
diff --git a/rtc_base/openssladapter.h b/rtc_base/openssladapter.h
index 9c6c344..6409c76 100644
--- a/rtc_base/openssladapter.h
+++ b/rtc_base/openssladapter.h
@@ -40,6 +40,7 @@
 
   void SetIgnoreBadCert(bool ignore) override;
   void SetAlpnProtocols(const std::vector<std::string>& protos) override;
+  void SetEllipticCurves(const std::vector<std::string>& curves) override;
 
   void SetMode(SSLMode mode) override;
   void SetIdentity(SSLIdentity* identity) override;
@@ -136,6 +137,8 @@
   bool ignore_bad_cert_;
   // List of protocols to be used in the TLS ALPN extension.
   std::vector<std::string> alpn_protocols_;
+  // List of elliptic curves to be used in the TLS elliptic curves extension.
+  std::vector<std::string> elliptic_curves_;
 
   bool custom_verification_succeeded_;
 };
diff --git a/rtc_base/ssladapter.h b/rtc_base/ssladapter.h
index b30e176..922f42e 100644
--- a/rtc_base/ssladapter.h
+++ b/rtc_base/ssladapter.h
@@ -48,7 +48,9 @@
   // TODO(juberti): Remove the opportunistic encryption mechanism in
   // BasicPacketSocketFactory that uses this function.
   virtual void SetIgnoreBadCert(bool ignore) = 0;
+
   virtual void SetAlpnProtocols(const std::vector<std::string>& protos) = 0;
+  virtual void SetEllipticCurves(const std::vector<std::string>& curves) = 0;
 
   // Do DTLS or TLS (default is TLS, if unspecified)
   virtual void SetMode(SSLMode mode) = 0;
diff --git a/rtc_base/ssladapter_unittest.cc b/rtc_base/ssladapter_unittest.cc
index 5c61f6a..8e5d322 100644
--- a/rtc_base/ssladapter_unittest.cc
+++ b/rtc_base/ssladapter_unittest.cc
@@ -64,6 +64,10 @@
     ssl_adapter_->SetAlpnProtocols(protos);
   }
 
+  void SetEllipticCurves(const std::vector<std::string>& curves) {
+    ssl_adapter_->SetEllipticCurves(curves);
+  }
+
   rtc::SocketAddress GetAddress() const {
     return ssl_adapter_->GetLocalAddress();
   }
@@ -290,6 +294,10 @@
     client_->SetAlpnProtocols(protos);
   }
 
+  void SetEllipticCurves(const std::vector<std::string>& curves) {
+    client_->SetEllipticCurves(curves);
+  }
+
   void TestHandshake(bool expect_success) {
     int rv;
 
@@ -450,6 +458,14 @@
   TestTransfer("Hello, world!");
 }
 
+// Test transfer with TLS Elliptic curves set to "X25519:P-256:P-384:P-521"
+TEST_F(SSLAdapterTestTLS_ECDSA, TestTLSEllipticCurves) {
+  std::vector<std::string> elliptic_curves{"X25519", "P-256", "P-384", "P-521"};
+  SetEllipticCurves(elliptic_curves);
+  TestHandshake(true);
+  TestTransfer("Hello, world!");
+}
+
 // Basic tests: DTLS
 
 // Test that handshake works, using RSA
diff --git a/rtc_base/stringencode.cc b/rtc_base/stringencode.cc
index 93470b6..a4d594d 100644
--- a/rtc_base/stringencode.cc
+++ b/rtc_base/stringencode.cc
@@ -645,6 +645,28 @@
   return true;
 }
 
+std::string join(const std::vector<std::string>& source, char delimiter) {
+  if (source.size() == 0) {
+    return std::string();
+  }
+  // Find length of the string to be returned to pre-allocate memory.
+  size_t source_string_length = 0;
+  for (size_t i = 0; i < source.size(); ++i) {
+    source_string_length += source[i].length();
+  }
+
+  // Build the joined string.
+  std::string joined_string;
+  joined_string.reserve(source_string_length + source.size() - 1);
+  for (size_t i = 0; i < source.size(); ++i) {
+    if (i != 0) {
+      joined_string += delimiter;
+    }
+    joined_string += source[i];
+  }
+  return joined_string;
+}
+
 size_t split(const std::string& source, char delimiter,
              std::vector<std::string>* fields) {
   RTC_DCHECK(fields);
diff --git a/rtc_base/stringencode.h b/rtc_base/stringencode.h
index 5296a9c..a4452c2 100644
--- a/rtc_base/stringencode.h
+++ b/rtc_base/stringencode.h
@@ -136,6 +136,10 @@
   return s_transform(source, url_decode);
 }
 
+// Joins the source vector of strings into a single string, with each
+// field in source being separated by delimiter. No trailing delimiter is added.
+std::string join(const std::vector<std::string>& source, char delimiter);
+
 // Splits the source string into multiple fields separated by delimiter,
 // with duplicates of delimiter creating empty fields.
 size_t split(const std::string& source, char delimiter,
diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java
index 8322410..c362d92 100644
--- a/sdk/android/api/org/webrtc/PeerConnection.java
+++ b/sdk/android/api/org/webrtc/PeerConnection.java
@@ -113,6 +113,10 @@
     // List of protocols to be used in the TLS ALPN extension.
     public final List<String> tlsAlpnProtocols;
 
+    // List of elliptic curves to be used in the TLS elliptic curves extension.
+    // Only curve names supported by OpenSSL should be used (eg. "P-256","X25519").
+    public final List<String> tlsEllipticCurves;
+
     /** Convenience constructor for STUN servers. */
     @Deprecated
     public IceServer(String uri) {
@@ -132,22 +136,23 @@
     @Deprecated
     public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
         String hostname) {
-      this(uri, username, password, tlsCertPolicy, hostname, null);
+      this(uri, username, password, tlsCertPolicy, hostname, null, null);
     }
 
     private IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
-        String hostname, List<String> tlsAlpnProtocols) {
+        String hostname, List<String> tlsAlpnProtocols, List<String> tlsEllipticCurves) {
       this.uri = uri;
       this.username = username;
       this.password = password;
       this.tlsCertPolicy = tlsCertPolicy;
       this.hostname = hostname;
       this.tlsAlpnProtocols = tlsAlpnProtocols;
+      this.tlsEllipticCurves = tlsEllipticCurves;
     }
 
     public String toString() {
       return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "] [" + hostname
-          + "] [" + tlsAlpnProtocols + "]";
+          + "] [" + tlsAlpnProtocols + "] [" + tlsEllipticCurves + "]";
     }
 
     public static Builder builder(String uri) {
@@ -161,6 +166,7 @@
       private TlsCertPolicy tlsCertPolicy = TlsCertPolicy.TLS_CERT_POLICY_SECURE;
       private String hostname = "";
       private List<String> tlsAlpnProtocols;
+      private List<String> tlsEllipticCurves;
 
       private Builder(String uri) {
         this.uri = uri;
@@ -191,8 +197,14 @@
         return this;
       }
 
+      public Builder setTlsEllipticCurves(List<String> tlsEllipticCurves) {
+        this.tlsEllipticCurves = tlsEllipticCurves;
+        return this;
+      }
+
       public IceServer createIceServer() {
-        return new IceServer(uri, username, password, tlsCertPolicy, hostname, tlsAlpnProtocols);
+        return new IceServer(
+            uri, username, password, tlsCertPolicy, hostname, tlsAlpnProtocols, tlsEllipticCurves);
       }
     }
   }
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 799f67d..eb9892a 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -364,6 +364,8 @@
         GetFieldID(jni, j_ice_server_class, "hostname", "Ljava/lang/String;");
     jfieldID j_ice_server_tls_alpn_protocols_id = GetFieldID(
         jni, j_ice_server_class, "tlsAlpnProtocols", "Ljava/util/List;");
+    jfieldID j_ice_server_tls_elliptic_curves_id = GetFieldID(
+        jni, j_ice_server_class, "tlsEllipticCurves", "Ljava/util/List;");
     jstring uri = reinterpret_cast<jstring>(
         GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
     jstring username = reinterpret_cast<jstring>(
@@ -376,6 +378,8 @@
         GetObjectField(jni, j_ice_server, j_ice_server_hostname_id));
     jobject tls_alpn_protocols = GetNullableObjectField(
         jni, j_ice_server, j_ice_server_tls_alpn_protocols_id);
+    jobject tls_elliptic_curves = GetNullableObjectField(
+        jni, j_ice_server, j_ice_server_tls_elliptic_curves_id);
     PeerConnectionInterface::IceServer server;
     server.uri = JavaToStdString(jni, uri);
     server.username = JavaToStdString(jni, username);
@@ -383,6 +387,8 @@
     server.tls_cert_policy = tls_cert_policy;
     server.hostname = JavaToStdString(jni, hostname);
     server.tls_alpn_protocols = JavaToStdVectorStrings(jni, tls_alpn_protocols);
+    server.tls_elliptic_curves =
+        JavaToStdVectorStrings(jni, tls_elliptic_curves);
     ice_servers->push_back(server);
   }
 }
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
index a28e237..eeb1177 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
@@ -20,6 +20,7 @@
 @synthesize tlsCertPolicy = _tlsCertPolicy;
 @synthesize hostname = _hostname;
 @synthesize tlsAlpnProtocols = _tlsAlpnProtocols;
+@synthesize tlsEllipticCurves = _tlsEllipticCurves;
 
 - (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings {
   return [self initWithURLStrings:urlStrings
@@ -57,7 +58,7 @@
                        credential:credential
                     tlsCertPolicy:tlsCertPolicy
                          hostname:hostname
-                 tlsAlpnProtocols:[NSMutableArray new]];
+                 tlsAlpnProtocols:[NSArray array]];
 }
 
 - (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
@@ -66,6 +67,22 @@
                      tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
                           hostname:(NSString *)hostname
                   tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols {
+  return [self initWithURLStrings:urlStrings
+                         username:username
+                       credential:credential
+                    tlsCertPolicy:tlsCertPolicy
+                         hostname:hostname
+                 tlsAlpnProtocols:tlsAlpnProtocols
+                tlsEllipticCurves:[NSArray array]];
+}
+
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+                          username:(NSString *)username
+                        credential:(NSString *)credential
+                     tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
+                          hostname:(NSString *)hostname
+                  tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols
+                 tlsEllipticCurves:(NSArray<NSString *> *)tlsEllipticCurves {
   NSParameterAssert(urlStrings.count);
   if (self = [super init]) {
     _urlStrings = [[NSArray alloc] initWithArray:urlStrings copyItems:YES];
@@ -74,18 +91,20 @@
     _tlsCertPolicy = tlsCertPolicy;
     _hostname = [hostname copy];
     _tlsAlpnProtocols = [[NSArray alloc] initWithArray:tlsAlpnProtocols copyItems:YES];
+    _tlsEllipticCurves = [[NSArray alloc] initWithArray:tlsEllipticCurves copyItems:YES];
   }
   return self;
 }
 
 - (NSString *)description {
-  return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@\n%@\n%@",
+  return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@\n%@\n%@\n%@",
                                     _urlStrings,
                                     _username,
                                     _credential,
                                     [self stringForTlsCertPolicy:_tlsCertPolicy],
                                     _hostname,
-                                    _tlsAlpnProtocols];
+                                    _tlsAlpnProtocols,
+                                    _tlsEllipticCurves];
 }
 
 #pragma mark - Private
@@ -110,6 +129,10 @@
     iceServer.tls_alpn_protocols.push_back(proto.stdString);
   }];
 
+  [_tlsEllipticCurves enumerateObjectsUsingBlock:^(NSString *curve, NSUInteger idx, BOOL *stop) {
+    iceServer.tls_elliptic_curves.push_back(curve.stdString);
+  }];
+
   [_urlStrings enumerateObjectsUsingBlock:^(NSString *url,
                                             NSUInteger idx,
                                             BOOL *stop) {
@@ -144,6 +167,11 @@
   for (auto const &proto : nativeServer.tls_alpn_protocols) {
     [tlsAlpnProtocols addObject:[NSString stringForStdString:proto]];
   }
+  NSMutableArray *tlsEllipticCurves =
+      [NSMutableArray arrayWithCapacity:nativeServer.tls_elliptic_curves.size()];
+  for (auto const &curve : nativeServer.tls_elliptic_curves) {
+    [tlsEllipticCurves addObject:[NSString stringForStdString:curve]];
+  }
   RTCTlsCertPolicy tlsCertPolicy;
 
   switch (nativeServer.tls_cert_policy) {
@@ -160,7 +188,8 @@
                        credential:credential
                     tlsCertPolicy:tlsCertPolicy
                          hostname:hostname
-                 tlsAlpnProtocols:tlsAlpnProtocols];
+                 tlsAlpnProtocols:tlsAlpnProtocols
+                tlsEllipticCurves:tlsEllipticCurves];
   return self;
 }
 
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h b/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
index e9baa8f..727da8a 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
@@ -46,6 +46,12 @@
 /** List of protocols to be used in the TLS ALPN extension. */
 @property(nonatomic, readonly) NSArray<NSString *> *tlsAlpnProtocols;
 
+/**
+  List elliptic curves to be used in the TLS elliptic curves extension.
+  Only curve names supported by OpenSSL should be used (eg. "P-256","X25519").
+  */
+@property(nonatomic, readonly) NSArray<NSString *> *tlsEllipticCurves;
+
 - (nonnull instancetype)init NS_UNAVAILABLE;
 
 /** Convenience initializer for a server with no authentication (e.g. STUN). */
@@ -87,7 +93,20 @@
                         credential:(nullable NSString *)credential
                      tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
                           hostname:(nullable NSString *)hostname
-                  tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols
+                  tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols;
+
+/**
+ * Initialize an RTCIceServer with its associated URLs, optional username,
+ * optional credential, TLS cert policy, hostname, ALPN protocols and
+ * elliptic curves.
+ */
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+                          username:(nullable NSString *)username
+                        credential:(nullable NSString *)credential
+                     tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
+                          hostname:(nullable NSString *)hostname
+                  tlsAlpnProtocols:(nullable NSArray<NSString *> *)tlsAlpnProtocols
+                 tlsEllipticCurves:(nullable NSArray<NSString *> *)tlsEllipticCurves
     NS_DESIGNATED_INITIALIZER;
 
 @end
diff --git a/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm b/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
index 9d42c07..669ede6 100644
--- a/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
+++ b/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
@@ -92,6 +92,24 @@
   EXPECT_EQ(2u, iceStruct.tls_alpn_protocols.size());
 }
 
+- (void)testTlsEllipticCurves {
+  RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ]
+                                                         username:@"username"
+                                                       credential:@"credential"
+                                                    tlsCertPolicy:RTCTlsCertPolicySecure
+                                                         hostname:@"hostname"
+                                                 tlsAlpnProtocols:@[ @"proto1", @"proto2" ]
+                                                tlsEllipticCurves:@[ @"curve1", @"curve2" ]];
+  webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer;
+  EXPECT_EQ(1u, iceStruct.urls.size());
+  EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front());
+  EXPECT_EQ("username", iceStruct.username);
+  EXPECT_EQ("credential", iceStruct.password);
+  EXPECT_EQ("hostname", iceStruct.hostname);
+  EXPECT_EQ(2u, iceStruct.tls_alpn_protocols.size());
+  EXPECT_EQ(2u, iceStruct.tls_elliptic_curves.size());
+}
+
 - (void)testInitFromNativeServer {
   webrtc::PeerConnectionInterface::IceServer nativeServer;
   nativeServer.username = "username";
@@ -100,6 +118,8 @@
   nativeServer.hostname = "hostname";
   nativeServer.tls_alpn_protocols.push_back("proto1");
   nativeServer.tls_alpn_protocols.push_back("proto2");
+  nativeServer.tls_elliptic_curves.push_back("curve1");
+  nativeServer.tls_elliptic_curves.push_back("curve2");
 
   RTCIceServer *iceServer =
       [[RTCIceServer alloc] initWithNativeServer:nativeServer];
@@ -110,6 +130,7 @@
   EXPECT_EQ("password", [NSString stdStringForString:iceServer.credential]);
   EXPECT_EQ("hostname", [NSString stdStringForString:iceServer.hostname]);
   EXPECT_EQ(2u, iceServer.tlsAlpnProtocols.count);
+  EXPECT_EQ(2u, iceServer.tlsEllipticCurves.count);
 }
 
 @end