diff --git a/daemon/contrib/src/opendht/rules.mak b/daemon/contrib/src/opendht/rules.mak
index c8633b24810b5dc359fa7c45a657683c73907d71..df031dc4b2a57c996d9fcd0f10b6454fd550fc78 100644
--- a/daemon/contrib/src/opendht/rules.mak
+++ b/daemon/contrib/src/opendht/rules.mak
@@ -1,5 +1,5 @@
 # OPENDHT
-OPENDHT_VERSION := 29f5d8c68ec363899373469c0af2ba86ceb75dac
+OPENDHT_VERSION := b5d93471747ad08254806843253e27e5ae9b9372
 OPENDHT_URL := https://github.com/savoirfairelinux/opendht/archive/$(OPENDHT_VERSION).tar.gz
 
 PKGS += opendht
@@ -8,7 +8,7 @@ PKGS_FOUND += opendht
 endif
 
 # Avoid building distro-provided dependencies in case opendht was built manually
-ifneq ($(call need_pkg,"gnutls >= 3.0.20"),)
+ifneq ($(call need_pkg,"gnutls >= 3.1"),)
 DEPS_opendht = gnutls $(DEPS_gnutls)
 endif
 
diff --git a/daemon/src/dring/account_const.h b/daemon/src/dring/account_const.h
index 59cd573e706588dcca5704c1a9393d8090fd05f8..e64435d430a6a69d82cd6f38ea977955a6d85e9a 100644
--- a/daemon/src/dring/account_const.h
+++ b/daemon/src/dring/account_const.h
@@ -189,8 +189,6 @@ constexpr static const char NEGOTIATION_TIMEOUT_SEC    [] = "TLS.negotiationTime
 namespace DHT {
 
 constexpr static const char PORT               [] = "DHT.port"                           ;
-constexpr static const char PRIVATE_PATH       [] = "DHT.privkeyPath"                    ;
-constexpr static const char CERT_PATH          [] = "DHT.certificatePath"                ;
 
 } //namespace DRing::Account::DHT
 
diff --git a/daemon/src/ice_transport.cpp b/daemon/src/ice_transport.cpp
index 6a3671c376065010cf6ad26959731871122ec97c..4e173cdafbbf89e92ce34fbae64a5385b499bb65 100644
--- a/daemon/src/ice_transport.cpp
+++ b/daemon/src/ice_transport.cpp
@@ -241,6 +241,14 @@ IceTransport::setSlaveSession()
     createIceSession(PJ_ICE_SESS_ROLE_CONTROLLED);
 }
 
+bool
+IceTransport::isInitiator() const
+{
+    if (isInitialized())
+        return pj_ice_strans_get_role(icest_.get()) == PJ_ICE_SESS_ROLE_CONTROLLING;
+    return initiator_session_;
+}
+
 bool
 IceTransport::start(const Attribute& rem_attrs,
                     const std::vector<IceCandidate>& rem_candidates)
diff --git a/daemon/src/ice_transport.h b/daemon/src/ice_transport.h
index 98a7eba494106b16cae87ea734851b2476c22fd1..8f5dda1dbe058dbd458826a7f94f016faef1b3f9 100644
--- a/daemon/src/ice_transport.h
+++ b/daemon/src/ice_transport.h
@@ -91,6 +91,11 @@ class IceTransport {
          */
         bool setSlaveSession();
 
+        /**
+         * Get current state
+         */
+        bool isInitiator() const;
+
         /**
          * Start tranport negociation between local candidates and given remote
          * to find the right candidate pair.
diff --git a/daemon/src/ringdht/Makefile.am b/daemon/src/ringdht/Makefile.am
index 21554a38cd2c0103841c8b9407aea82ac0d01c23..1ee66dc804589e072cc16a1071a9edae70f8729c 100644
--- a/daemon/src/ringdht/Makefile.am
+++ b/daemon/src/ringdht/Makefile.am
@@ -11,6 +11,8 @@ libringacc_la_SOURCES = \
         ringaccount.cpp \
         ringaccount.h \
         sip_transport_ice.cpp \
-        sip_transport_ice.h
+        sip_transport_ice.h \
+        sips_transport_ice.cpp \
+        sips_transport_ice.h
 
 endif
diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp
index 267c6207b00a77c784fb9c252e4eaf5b4e58b599..7581e55c24d150af28898ba8d27697838995659c 100644
--- a/daemon/src/ringdht/ringaccount.cpp
+++ b/daemon/src/ringdht/ringaccount.cpp
@@ -40,15 +40,14 @@
 #include "sip/sipcall.h"
 #include "sip/siptransport.h"
 
-#include "sip_transport_ice.h"
+#include "sips_transport_ice.h"
 #include "ice_transport.h"
 
-#include <opendht/securedht.h>
-
-#include "array_size.h"
-
 #include "client/signal.h"
 
+#include "upnp/upnp_control.h"
+#include "system_codec_container.h"
+
 #include "account_schema.h"
 #include "logger.h"
 #include "manager.h"
@@ -57,12 +56,13 @@
 #include "libav_utils.h"
 #endif
 #include "fileutils.h"
+#include "string_utils.h"
+#include "array_size.h"
 
 #include "config/yamlparser.h"
-#include <yaml-cpp/yaml.h>
 
-#include "upnp/upnp_control.h"
-#include "system_codec_container.h"
+#include <opendht/securedht.h>
+#include <yaml-cpp/yaml.h>
 
 #include <algorithm>
 #include <array>
@@ -80,7 +80,7 @@ static constexpr int ICE_NEGOTIATION_TIMEOUT {60};
 constexpr const char * const RingAccount::ACCOUNT_TYPE;
 
 RingAccount::RingAccount(const std::string& accountID, bool /* presenceEnabled */)
-    : SIPAccountBase(accountID), tlsSetting_(), via_addr_()
+    : SIPAccountBase(accountID), via_addr_()
 {
     fileutils::check_dir(fileutils::get_cache_dir().c_str());
     cachePath_ = fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID();
@@ -233,7 +233,8 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
 void
 RingAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target)
 {
-    RING_WARN("RingAccount::createOutgoingCall to: %s target: %s tlsListener: %d", to_id.c_str(), target.toString(true).c_str(), tlsListener_?1:0);
+    RING_WARN("RingAccount::createOutgoingCall to: %s target: %s",
+              to_id.c_str(), target.toString(true).c_str());
     call->initIceTransport(true);
     call->setIPToIP(true);
     call->setPeerNumber(getToUri(to_id+"@"+target.toString(true).c_str()));
@@ -368,9 +369,6 @@ void RingAccount::serialize(YAML::Emitter &out)
     out << YAML::BeginMap;
     SIPAccountBase::serialize(out);
     out << YAML::Key << Conf::DHT_PORT_KEY << YAML::Value << dhtPort_;
-    out << YAML::Key << Conf::DHT_PRIVKEY_PATH_KEY << YAML::Value << privkeyPath_;
-    out << YAML::Key << Conf::DHT_CERT_PATH_KEY << YAML::Value << certPath_;
-    out << YAML::Key << Conf::DHT_CA_CERT_PATH_KEY << YAML::Value << cacertPath_;
 
     // tls submap
     out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap;
@@ -389,22 +387,19 @@ void RingAccount::unserialize(const YAML::Node &node)
     parseValue(node, Conf::DHT_PORT_KEY, port);
     dhtPort_ = port ? port : DHT_DEFAULT_PORT;
     dhtPortUsed_ = dhtPort_;
-    parseValue(node, Conf::DHT_PRIVKEY_PATH_KEY, privkeyPath_);
-    parseValue(node, Conf::DHT_CERT_PATH_KEY, certPath_);
-    parseValue(node, Conf::DHT_CA_CERT_PATH_KEY, cacertPath_);
     checkIdentityPath();
 }
 
 void
 RingAccount::checkIdentityPath()
 {
-    if (not privkeyPath_.empty() and not dataPath_.empty())
+    if (not tlsPrivateKeyFile_.empty() and not tlsCertificateFile_.empty())
         return;
 
     const auto idPath = fileutils::get_data_dir()+DIR_SEPARATOR_STR+getAccountID();
-    privkeyPath_ = idPath + DIR_SEPARATOR_STR "dht.key";
-    certPath_ = idPath + DIR_SEPARATOR_STR "dht.crt";
-    cacertPath_ = idPath + DIR_SEPARATOR_STR "ca.crt";
+    tlsPrivateKeyFile_ = idPath + DIR_SEPARATOR_STR "dht.key";
+    tlsCertificateFile_ = idPath + DIR_SEPARATOR_STR "dht.crt";
+    tlsCaListFile_ = idPath + DIR_SEPARATOR_STR "ca.crt";
 }
 
 std::vector<uint8_t>
@@ -445,9 +440,9 @@ RingAccount::loadIdentity()
     dht::crypto::PrivateKey dht_key;
 
     try {
-        ca_cert = dht::crypto::Certificate(fileutils::loadFile(cacertPath_));
-        dht_cert = dht::crypto::Certificate(fileutils::loadFile(certPath_));
-        dht_key = dht::crypto::PrivateKey(fileutils::loadFile(privkeyPath_));
+        ca_cert = dht::crypto::Certificate(fileutils::loadFile(tlsCaListFile_));
+        dht_cert = dht::crypto::Certificate(fileutils::loadFile(tlsCertificateFile_));
+        dht_key = dht::crypto::PrivateKey(fileutils::loadFile(tlsPrivateKeyFile_));
     }
     catch (const std::exception& e) {
         RING_ERR("Error loading identity: %s", e.what());
@@ -463,11 +458,11 @@ RingAccount::loadIdentity()
         fileutils::check_dir(idPath_.c_str());
 
         saveIdentity(ca, idPath_ + DIR_SEPARATOR_STR "ca");
-        cacertPath_ = idPath_ + DIR_SEPARATOR_STR "ca.crt";
+        tlsCaListFile_ = idPath_ + DIR_SEPARATOR_STR "ca.crt";
 
         saveIdentity(id, idPath_ + DIR_SEPARATOR_STR "dht");
-        certPath_ = idPath_ + DIR_SEPARATOR_STR "dht.crt";
-        privkeyPath_ = idPath_ + DIR_SEPARATOR_STR "dht.key";
+        tlsCertificateFile_ = idPath_ + DIR_SEPARATOR_STR "dht.crt";
+        tlsPrivateKeyFile_ = idPath_ + DIR_SEPARATOR_STR "dht.key";
 
         return {ca.second, id};
     }
@@ -511,20 +506,13 @@ void RingAccount::setAccountDetails(const std::map<std::string, std::string> &de
     if (dhtPort_ == 0)
         dhtPort_ = DHT_DEFAULT_PORT;
     dhtPortUsed_ = dhtPort_;
-    parseString(details, Conf::CONFIG_DHT_PRIVKEY_PATH, privkeyPath_);
-    parseString(details, Conf::CONFIG_DHT_CERT_PATH, certPath_);
     checkIdentityPath();
 }
 
 std::map<std::string, std::string> RingAccount::getAccountDetails() const
 {
     std::map<std::string, std::string> a = SIPAccountBase::getAccountDetails();
-
-    std::stringstream dhtport;
-    dhtport << dhtPort_;
-    a[Conf::CONFIG_DHT_PORT] = dhtport.str();
-    a[Conf::CONFIG_DHT_PRIVKEY_PATH] = privkeyPath_;
-    a[Conf::CONFIG_DHT_CERT_PATH] = certPath_;
+    a[Conf::CONFIG_DHT_PORT] = ring::to_string(dhtPort_);
     return a;
 }
 
@@ -540,15 +528,70 @@ RingAccount::handleEvents()
         auto call = c->call.lock();
         if (not call) {
             RING_WARN("Removing deleted call from pending calls");
-            dht_.cancelListen(c->call_key, c->listen_key.get());
+            if (c->call_key != dht::InfoHash())
+                dht_.cancelListen(c->call_key, c->listen_key.get());
             c = pendingCalls_.erase(c);
             continue;
         }
         auto ice = c->ice.get();
         if (ice->isRunning()) {
-            call->setTransport(link_->sipTransportBroker->getIceTransport(c->ice, ICE_COMP_SIP_TRANSPORT));
+            regenerateCAList();
+            auto id = loadIdentity();
+            auto remote_h = c->id;
+            std::shared_ptr<gnutls_dh_params_int> dh;
+            {
+                std::unique_lock<std::mutex> l(dhParamsMtx_);
+                dhParamsCv_.wait(l, [&]() {
+                        return static_cast<bool>(dhParams_);
+                    });
+                dh = dhParams_;
+            }
+            tls::TlsParams tlsParams {
+                .ca_list = caListPath_,
+                .id = id.second,
+                .dh_params = dh,
+                .timeout = std::chrono::seconds(30),
+                .cert_check = [remote_h](unsigned status,
+                                         const gnutls_datum_t* cert_list,
+                                         unsigned cert_num) -> pj_status_t {
+                    RING_WARN("TLS certificate check for %s",
+                              remote_h.toString().c_str());
+
+                    if (status & GNUTLS_CERT_EXPIRED ||
+                        status & GNUTLS_CERT_NOT_ACTIVATED)
+                        return PJ_SSL_CERT_EVALIDITY_PERIOD;
+                    else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+                        return PJ_SSL_CERT_EUNTRUSTED;
+
+                    if (cert_num == 0)
+                        return PJ_SSL_CERT_EUNKNOWN;
+
+                    try {
+                        std::vector<uint8_t> crt_blob(cert_list[0].data,
+                                                      cert_list[0].data + cert_list[0].size);
+                        dht::crypto::Certificate crt(crt_blob);
+                        const auto tls_id = crt.getId();
+                        if (crt.getUID() != tls_id.toString()) {
+                            RING_WARN("Certificate UID must be the public key ID");
+                            return PJ_SSL_CERT_EUNTRUSTED;
+                        }
+
+                        if (tls_id != remote_h) {
+                            RING_WARN("Certificate public key (ID %s) doesn't match expectation (%s)",
+                                      tls_id.toString().c_str(),
+                                      remote_h.toString().c_str());
+                            return PJ_SSL_CERT_EUNTRUSTED;
+                        }
+                    } catch (const std::exception& e) {
+                        return PJ_SSL_CERT_EUNKNOWN;
+                    }
+                    return PJ_SUCCESS;
+                }
+            };
+            auto tr = link_->sipTransportBroker->getTlsIceTransport(c->ice, ICE_COMP_SIP_TRANSPORT, tlsParams);
+            call->setTransport(tr);
             call->setConnectionState(Call::PROGRESSING);
-            if (c->id == dht::InfoHash()) {
+            if (c->call_key == dht::InfoHash()) {
                 RING_WARN("ICE succeeded : moving incomming call to pending sip call");
                 auto in = c;
                 ++c;
@@ -561,7 +604,7 @@ RingAccount::handleEvents()
             }
         } else if (ice->isFailed() || now - c->start > std::chrono::seconds(ICE_NEGOTIATION_TIMEOUT)) {
             RING_WARN("ICE timeout : removing pending outgoing call");
-            if (c->id != dht::InfoHash())
+            if (c->call_key != dht::InfoHash())
                 dht_.cancelListen(c->call_key, c->listen_key.get());
             call->setConnectionState(Call::DISCONNECTED);
             Manager::instance().callFailure(*call);
@@ -606,6 +649,10 @@ void RingAccount::doRegister()
         return;
     }
 
+    if (not dhParams_) {
+        generateDhParams();
+    }
+
     /* if UPnP is enabled, then wait for IGD to complete registration */
     if ( upnpEnabled_ ) {
         auto shared = shared_from_this();
@@ -637,22 +684,10 @@ void RingAccount::doRegister_()
             case dht::Dht::Status::Connecting:
             case dht::Dht::Status::Connected:
                 setRegistrationState(status == dht::Dht::Status::Connected ? RegistrationState::REGISTERED : RegistrationState::TRYING);
-                /*if (!tlsListener_) {
-                    initTlsConfiguration();
-                    tlsListener_ = link_->sipTransport->getTlsListener(
-                        SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
-                        getTlsSetting());
-                    if (!tlsListener_) {
-                        setRegistrationState(RegistrationState::ERROR_GENERIC);
-                        RING_ERR("Error creating TLS listener.");
-                        return;
-                    }
-                }*/
                 break;
             case dht::Dht::Status::Disconnected:
             default:
                 setRegistrationState(status == dht::Dht::Status::Disconnected ? RegistrationState::UNREGISTERED : RegistrationState::ERROR_GENERIC);
-                tlsListener_.reset();
                 break;
             }
         });
@@ -723,13 +758,14 @@ void RingAccount::doRegister_()
                             RING_DBG("Ignoring non encrypted or bad type value %s.", v->toString().c_str());
                             continue;
                         }
-                        if (v->owner.getId() == this_.dht_.getId())
+                        auto remote_id = v->owner.getId();
+                        if (remote_id == this_.dht_.getId())
                             continue;
                         auto res = this_.treatedCalls_.insert(v->id);
                         this_.saveTreatedCalls();
                         if (!res.second)
                             continue;
-                        auto from = v->owner.getId().toString();
+                        auto from = remote_id.toString();
                         auto from_vid = v->id;
                         auto reply_vid = from_vid+1;
                         RING_WARN("Received incomming DHT call request from %s (vid %llx) !!", from.c_str(), from_vid);
@@ -748,7 +784,7 @@ void RingAccount::doRegister_()
 
                         this_.dht_.putEncrypted(
                             listenKey,
-                            v->owner.getId(),
+                            remote_id,
                             dht::Value {
                                 this_.ICE_ANNOUCEMENT_TYPE.id,
                                 ice->getLocalAttributesAndCandidates(),
@@ -772,7 +808,7 @@ void RingAccount::doRegister_()
                         call->initRecFilename(from);
                         {
                             std::lock_guard<std::mutex> lock(this_.callsMutex_);
-                            this_.pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, weak_call, {}, {}, {}});
+                            this_.pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, weak_call, {}, {}, remote_id});
                         }
                         return true;
                     } catch (const std::exception& e) {
@@ -809,7 +845,6 @@ void RingAccount::doUnregister(std::function<void(bool)> released_cb)
     saveNodes(dht_.exportNodes());
     saveValues(dht_.exportValues());
     dht_.join();
-    tlsListener_.reset();
     setRegistrationState(RegistrationState::UNREGISTERED);
     if (released_cb)
         released_cb(false);
@@ -888,7 +923,7 @@ RingAccount::regenerateCAList()
     }
     auto cas = getRegistredCAs();
     {
-        std::ifstream file(cacertPath_, std::ios::binary);
+        std::ifstream file(tlsCaListFile_, std::ios::binary);
         list << file.rdbuf();
     }
     for (const auto& ca : cas) {
@@ -969,25 +1004,39 @@ RingAccount::loadValues() const
     return values;
 }
 
-void RingAccount::initTlsConfiguration()
+void
+RingAccount::initTlsConfiguration()
 {
-    // TLS listener is unique and should be only modified through IP2IP_PROFILE
-    pjsip_tls_setting_default(&tlsSetting_);
     regenerateCAList();
 
-    pj_cstr(&tlsSetting_.ca_list_file, caListPath_.c_str());
-    pj_cstr(&tlsSetting_.cert_file, certPath_.c_str());
-    pj_cstr(&tlsSetting_.privkey_file, privkeyPath_.c_str());
-    pj_cstr(&tlsSetting_.password, "");
-    tlsSetting_.method = PJSIP_TLSV1_METHOD;
-    tlsSetting_.ciphers_num = 0;
-    tlsSetting_.ciphers = nullptr;
-    tlsSetting_.verify_server = false;
-    tlsSetting_.verify_client = false;
-    tlsSetting_.require_client_cert = false;
-    tlsSetting_.timeout.sec = 2;
-    tlsSetting_.qos_type = PJ_QOS_TYPE_BEST_EFFORT;
-    tlsSetting_.qos_ignore_error = PJ_TRUE;
+}
+
+void
+RingAccount::generateDhParams()
+{
+    auto shared = shared_from_this();
+    std::weak_ptr<RingAccount> shared_w = std::static_pointer_cast<RingAccount>(shared);
+    auto t = std::thread([shared_w](){
+        using namespace std::chrono;
+        auto bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, /* GNUTLS_SEC_PARAM_NORMAL */ GNUTLS_SEC_PARAM_HIGH);
+        RING_DBG("Generating DH params with %u bits", bits);
+        high_resolution_clock::time_point t1 = high_resolution_clock::now();
+        gnutls_dh_params_t new_params_;
+        gnutls_dh_params_init(&new_params_);
+        gnutls_dh_params_generate2(new_params_, bits);
+        high_resolution_clock::time_point t2 = high_resolution_clock::now();
+        duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
+        RING_WARN("Generated DH params with %u bits in %lfs", bits, time_span.count());
+        std::shared_ptr<gnutls_dh_params_int> sp(new_params_, gnutls_dh_params_deinit);
+        if (auto s = shared_w.lock()) {
+            {
+                std::lock_guard<std::mutex> l(s->dhParamsMtx_);
+                s->dhParams_ = std::move(sp);
+            }
+            s->dhParamsCv_.notify_all();
+        }
+    });
+    t.detach();
 }
 
 void RingAccount::loadConfig()
@@ -1018,8 +1067,8 @@ std::string RingAccount::getFromUri() const
 std::string RingAccount::getToUri(const std::string& to) const
 {
     const std::string transport {pjsip_transport_get_type_name(transportType_)};
-    return "<sip:" + to + ">";
-    //return "<sips:" + to + ";transport=" + transport + ">";
+    //return "<sip:" + to + ">";
+    return "<sips:" + to + ";transport=" + transport + ">";
 }
 
 pj_str_t
@@ -1029,53 +1078,20 @@ RingAccount::getContactHeader(pjsip_transport* t)
         t = transport_->get();
     if (!t) {
         RING_ERR("Transport not created yet");
-        pj_cstr(&contact_, "<sip:>");
+        pj_cstr(&contact_, "<sips:>");
         return contact_;
     }
 
     // FIXME: be sure that given transport is from SipIceTransport
-    auto ice = reinterpret_cast<SipIceTransport::TransportData*>(t)->self;
-
-    // The transport type must be specified, in our case START_OTHER refers to stun transport
-    /*pjsip_transport_type_e transportType = transportType_;
-
-    if (transportType == PJSIP_TRANSPORT_START_OTHER)
-        transportType = PJSIP_TRANSPORT_UDP;*/
-
-    // Else we determine this infor based on transport information
-    //std::string address = "ring.dht";
-    //pj_uint16_t port = getTlsListenerPort();
-
-    //link_->sipTransport->findLocalAddressFromTransport(t, transportType, hostname_, address, port);
-    auto address = ice->getLocalAddress();
-    /*if (addr) {
-        address = addr;
-        port =
-    }*/
-    /*auto ports = ice->getLocalPorts();
-    if (not ports.empty())
-        port = ports[0];*/
-/*
-#if HAVE_IPV6
-    // Enclose IPv6 address in square brackets
-    if (IpAddr::isIpv6(address)) {
-        address = IpAddr(address);//.toString(false, true);
-    }
-#endif
-*/
+    auto tlsTr = reinterpret_cast<tls::SipsIceTransport::TransportData*>(t)->self;
+    auto address = tlsTr->getLocalAddress();
     RING_WARN("getContactHeader %s@%s", username_.c_str(), address.toString(true).c_str());
     contact_.slen = pj_ansi_snprintf(contact_.ptr, PJSIP_MAX_URL_SIZE,
-                                     "<sip:%s%s%s>",
-                                     username_.c_str(),
-                                     (username_.empty() ? "" : "@"),
-                                     address.toString(true).c_str());    /*
-    contact_.slen = pj_ansi_snprintf(contact_.ptr, PJSIP_MAX_URL_SIZE,
-                                     "<sips:%s%s%s:%d;transport=%s>",
+                                     "<sips:%s%s%s;transport=%s>",
                                      username_.c_str(),
                                      (username_.empty() ? "" : "@"),
-                                     address.c_str(),
-                                     port,
-                                     pjsip_transport_get_type_name(transportType));*/
+                                     address.toString(true).c_str(),
+                                     pjsip_transport_get_type_name(transportType_));
     return contact_;
 }
 
diff --git a/daemon/src/ringdht/ringaccount.h b/daemon/src/ringdht/ringaccount.h
index 5faa843acdb058113a5d8ab066f9b2b36b426272..4c48d47be99093a6ebf13519d901409f037bf4f5 100644
--- a/daemon/src/ringdht/ringaccount.h
+++ b/daemon/src/ringdht/ringaccount.h
@@ -48,6 +48,7 @@
 #include <vector>
 #include <map>
 #include <chrono>
+#include <list>
 
 /**
  * @file sipaccount.h
@@ -63,10 +64,6 @@ namespace ring {
 
 namespace Conf {
     const char *const DHT_PORT_KEY = "dhtPort";
-    const char *const DHT_PRIVKEY_PATH_KEY = "dhtPrivkeyPath";
-    const char *const DHT_PRIVKEY_PASSWORD_KEY = "dhtPrivkeyPassword";
-    const char *const DHT_CERT_PATH_KEY = "dhtCertificatePath";
-    const char *const DHT_CA_CERT_PATH_KEY = "dhtCACertificatePath";
     const char *const DHT_VALUES_PATH_KEY = "dhtValuesPath";
 }
 
@@ -235,11 +232,11 @@ class RingAccount : public SIPAccountBase {
         newIncomingCall(const std::string& from = {});
 
         virtual bool isTlsEnabled() const {
-            return false;
+            return true;
         }
 
         virtual bool getSrtpEnabled() const {
-            return false;
+            return true;
         }
 
         virtual sip_utils::KeyExchangeProtocol getSrtpKeyExchange() const {
@@ -287,15 +284,6 @@ class RingAccount : public SIPAccountBase {
          */
         bool mapPortUPnP();
 
-        /**
-         * @return pjsip_tls_setting structure, filled from the configuration
-         * file, that can be used directly by PJSIP to initialize
-         * TLS transport.
-         */
-        pjsip_tls_setting * getTlsSetting() {
-            return &tlsSetting_;
-        }
-
         dht::DhtRunner dht_ {};
 
         struct PendingCall {
@@ -318,11 +306,7 @@ class RingAccount : public SIPAccountBase {
         std::set<dht::Value::Id> treatedCalls_ {};
         mutable std::mutex callsMutex_ {};
 
-        std::string cacertPath_ {};
-        std::string privkeyPath_ {};
-        std::string certPath_ {};
         std::string idPath_ {};
-
         std::string cachePath_ {};
         std::string dataPath_ {};
         std::string caPath_ {};
@@ -371,7 +355,10 @@ class RingAccount : public SIPAccountBase {
         /**
          * The TLS settings, used only if tls is chosen as a sip transport.
          */
-        pjsip_tls_setting tlsSetting_;
+        void generateDhParams();
+        std::shared_ptr<gnutls_dh_params_int> dhParams_;
+        std::mutex dhParamsMtx_;
+        std::condition_variable dhParamsCv_;
 
         /**
          * Optional: "received" parameter from VIA header
diff --git a/daemon/src/ringdht/sips_transport_ice.cpp b/daemon/src/ringdht/sips_transport_ice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac1fe466c06468505376073c5cc3d60d92b90b0a
--- /dev/null
+++ b/daemon/src/ringdht/sips_transport_ice.cpp
@@ -0,0 +1,1160 @@
+/*
+ *  Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#include "sips_transport_ice.h"
+#include "ice_transport.h"
+#include "logger.h"
+
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pj/compat/socket.h>
+#include <pj/lock.h>
+
+#include <algorithm>
+
+namespace ring { namespace tls {
+
+static constexpr int POOL_TP_INIT {512};
+static constexpr int POOL_TP_INC {512};
+static constexpr int TRANSPORT_INFO_LENGTH {64};
+static constexpr int GNUTLS_LOG_LEVEL {8};
+
+static void
+sockaddr_to_host_port(pj_pool_t* pool,
+                      pjsip_host_port* host_port,
+                      const pj_sockaddr* addr)
+{
+    host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4);
+    pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0);
+    host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
+    host_port->port = pj_sockaddr_get_port(addr);
+}
+
+static void tls_print_logs(int level, const char* msg)
+{
+    if (level < 3)
+        return;
+    RING_DBG("GnuTLS [%d]: %s", level, msg);
+}
+
+
+static pj_status_t tls_status_from_err(int err)
+{
+    pj_status_t status;
+
+    switch (err) {
+    case GNUTLS_E_SUCCESS:
+        status = PJ_SUCCESS;
+        break;
+    case GNUTLS_E_MEMORY_ERROR:
+        status = PJ_ENOMEM;
+        break;
+    case GNUTLS_E_LARGE_PACKET:
+        status = PJ_ETOOBIG;
+        break;
+    case GNUTLS_E_NO_CERTIFICATE_FOUND:
+        status = PJ_ENOTFOUND;
+        break;
+    case GNUTLS_E_SESSION_EOF:
+        status = PJ_EEOF;
+        break;
+    case GNUTLS_E_HANDSHAKE_TOO_LARGE:
+        status = PJ_ETOOBIG;
+        break;
+    case GNUTLS_E_EXPIRED:
+        status = PJ_EGONE;
+        break;
+    case GNUTLS_E_TIMEDOUT:
+        status = PJ_ETIMEDOUT;
+        break;
+    case GNUTLS_E_PREMATURE_TERMINATION:
+        status = PJ_ECANCELLED;
+        break;
+    case GNUTLS_E_INTERNAL_ERROR:
+    case GNUTLS_E_UNIMPLEMENTED_FEATURE:
+        status = PJ_EBUG;
+        break;
+    case GNUTLS_E_AGAIN:
+    case GNUTLS_E_INTERRUPTED:
+    case GNUTLS_E_REHANDSHAKE:
+        status = PJ_EPENDING;
+        break;
+    case GNUTLS_E_TOO_MANY_EMPTY_PACKETS:
+    case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS:
+    case GNUTLS_E_RECORD_LIMIT_REACHED:
+        status = PJ_ETOOMANY;
+        break;
+    case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
+    case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM:
+    case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE:
+    case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE:
+    case GNUTLS_E_X509_UNSUPPORTED_EXTENSION:
+    case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION:
+        status = PJ_ENOTSUP;
+        break;
+    case GNUTLS_E_INVALID_SESSION:
+    case GNUTLS_E_INVALID_REQUEST:
+    case GNUTLS_E_INVALID_PASSWORD:
+    case GNUTLS_E_ILLEGAL_PARAMETER:
+    case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION:
+    case GNUTLS_E_UNEXPECTED_PACKET:
+    case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
+    case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
+    case GNUTLS_E_UNWANTED_ALGORITHM:
+    case GNUTLS_E_USER_ERROR:
+        status = PJ_EINVAL;
+        break;
+    default:
+        status = PJ_EUNKNOWN;
+        break;
+    }
+
+    /* Not thread safe */
+    /*tls_last_error = err;
+    if (ssock)
+        ssock->last_err = err;*/
+    return status;
+}
+
+pj_status_t
+SipsIceTransport::tryHandshake()
+{
+    RING_DBG("SipsIceTransport::tryHandshake as %s",
+             (is_server_ ? "server" : "client"));
+    pj_status_t status;
+    int ret = gnutls_handshake(session_);
+    if (ret == GNUTLS_E_SUCCESS) {
+        /* System are GO */
+        RING_DBG("SipsIceTransport::tryHandshake : ESTABLISHED");
+        state_ = TlsConnectionState::ESTABLISHED;
+        status = PJ_SUCCESS;
+    } else if (!gnutls_error_is_fatal(ret)) {
+        /* Non fatal error, retry later (busy or again) */
+        RING_DBG("SipsIceTransport::tryHandshake : EPENDING");
+        status = PJ_EPENDING;
+    } else {
+        /* Fatal error invalidates session, no fallback */
+        RING_DBG("SipsIceTransport::tryHandshake : EINVAL");
+        status = PJ_EINVAL;
+    }
+    last_err_ = ret;
+    return status;
+}
+
+int
+SipsIceTransport::verifyCertificate()
+{
+    RING_DBG("SipsIceTransport::verifyCertificate");
+
+    unsigned int status;
+    int ret;
+
+    /* Support only x509 format */
+    ret = gnutls_certificate_type_get(session_) != GNUTLS_CRT_X509;
+    if (ret < 0)
+        return GNUTLS_E_CERTIFICATE_ERROR;
+
+    /* Store verification status */
+    ret = gnutls_certificate_verify_peers2(session_, &status);
+    if (ret < 0 || status & GNUTLS_CERT_SIGNATURE_FAILURE)
+        return GNUTLS_E_CERTIFICATE_ERROR;
+
+    unsigned int cert_list_size;
+    const gnutls_datum_t *cert_list;
+
+    cert_list = gnutls_certificate_get_peers(session_, &cert_list_size);
+    if (cert_list == NULL) {
+        return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+    if (param_.cert_check) {
+        pj_status_t check_ret = param_.cert_check(status, cert_list,
+                                                  cert_list_size);
+        if (check_ret != PJ_SUCCESS) {
+            return GNUTLS_E_CERTIFICATE_ERROR;
+        }
+    }
+
+    /* notify GnuTLS to continue handshake normally */
+    return GNUTLS_E_SUCCESS;
+}
+
+SipsIceTransport::SipsIceTransport(pjsip_endpoint* endpt,
+                                   const TlsParams& param,
+                                   const std::shared_ptr<ring::IceTransport>& ice,
+                                   int comp_id)
+    : pool_(nullptr, pj_pool_release)
+    , rxPool_(nullptr, pj_pool_release)
+    , trData_()
+    , ice_(ice)
+    , comp_id_(comp_id)
+    , param_(param)
+    , tlsThread_(
+        std::bind(&SipsIceTransport::setup, this),
+        std::bind(&SipsIceTransport::loop, this),
+        std::bind(&SipsIceTransport::clean, this))
+{
+    trData_.self = this;
+
+    if (not ice or not ice->isRunning())
+        throw std::logic_error("ICE transport must exist and negotiation completed");
+
+    RING_DBG("SipIceTransport@%p {tr=%p}", this, &trData_.base);
+    auto& base = trData_.base;
+
+    pool_.reset(pjsip_endpt_create_pool(endpt, "SipsIceTransport.pool",
+                                        POOL_TP_INIT, POOL_TP_INC));
+    if (not pool_) {
+        RING_ERR("Can't create PJSIP pool");
+        throw std::bad_alloc();
+    }
+    auto pool = pool_.get();
+
+    pj_ansi_snprintf(base.obj_name, PJ_MAX_OBJ_NAME, "SipsIceTransport");
+    base.endpt = endpt;
+    base.tpmgr = pjsip_endpt_get_tpmgr(endpt);
+    base.pool = pool;
+
+    if (pj_atomic_create(pool, 0, &base.ref_cnt) != PJ_SUCCESS)
+        throw std::runtime_error("Can't create PJSIP atomic.");
+
+    if (pj_lock_create_recursive_mutex(pool, "SipsIceTransport.mutex",
+                                       &base.lock) != PJ_SUCCESS)
+        throw std::runtime_error("Can't create PJSIP mutex.");
+
+    is_server_ = not ice->isInitiator();
+    local_ = ice->getLocalAddress(comp_id);
+    remote_ = ice->getRemoteAddress(comp_id);
+    pj_sockaddr_cp(&base.key.rem_addr, remote_.pjPtr());
+    base.key.type = PJSIP_TRANSPORT_TLS;
+    base.type_name = (char*)pjsip_transport_get_type_name((pjsip_transport_type_e)base.key.type);
+    base.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)base.key.type);
+    base.info = (char*) pj_pool_alloc(pool, TRANSPORT_INFO_LENGTH);
+
+    char print_addr[PJ_INET6_ADDRSTRLEN+10];
+    pj_ansi_snprintf(base.info, TRANSPORT_INFO_LENGTH, "%s to %s",
+                     base.type_name,
+                     pj_sockaddr_print(remote_.pjPtr(), print_addr,
+                                       sizeof(print_addr), 3));
+    base.addr_len = remote_.getLength();
+    base.dir = PJSIP_TP_DIR_NONE; ///is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING;
+    base.data = nullptr;
+
+    /* Set initial local address */
+    auto local = ice->getDefaultLocalAddress();
+    pj_sockaddr_cp(&base.local_addr, local.pjPtr());
+
+    sockaddr_to_host_port(pool, &base.local_name, &base.local_addr);
+    sockaddr_to_host_port(pool, &base.remote_name, remote_.pjPtr());
+
+    base.send_msg = [](pjsip_transport *transport,
+                       pjsip_tx_data *tdata,
+                       const pj_sockaddr_t *rem_addr, int addr_len,
+                       void *token, pjsip_transport_callback callback) -> pj_status_t {
+        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        return this_->send(tdata, rem_addr, addr_len, token, callback);
+    };
+    base.do_shutdown = [](pjsip_transport *transport) -> pj_status_t {
+        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        RING_WARN("SipsIceTransport@%p: shutdown", this_);
+        this_->reset();
+        return PJ_SUCCESS;
+    };
+    base.destroy = [](pjsip_transport *transport) -> pj_status_t {
+        auto& this_ = reinterpret_cast<TransportData*>(transport)->self;
+        RING_WARN("SipsIceTransport@%p: destroy", this_);
+        delete this_;
+        return PJ_SUCCESS;
+    };
+
+    /* Init rdata_ */
+    rxPool_.reset(pjsip_endpt_create_pool(base.endpt,
+                                          "SipsIceTransport.rtd%p",
+                                          PJSIP_POOL_RDATA_LEN,
+                                          PJSIP_POOL_RDATA_LEN));
+    if (not rxPool_) {
+        RING_ERR("Can't create PJSIP rx pool");
+        throw std::bad_alloc();
+    }
+    pj_bzero(&rdata_, sizeof(pjsip_rx_data));
+    rdata_.tp_info.pool = rxPool_.get();
+    rdata_.tp_info.transport = &base;
+    rdata_.tp_info.tp_data = this;
+    rdata_.tp_info.op_key.rdata = &rdata_;
+    pj_ioqueue_op_key_init(&rdata_.tp_info.op_key.op_key,
+                           sizeof(pj_ioqueue_op_key_t));
+    rdata_.pkt_info.src_addr = base.key.rem_addr;
+    rdata_.pkt_info.src_addr_len = sizeof(rdata_.pkt_info.src_addr);
+    auto rem_addr = &base.key.rem_addr;
+    pj_sockaddr_print(rem_addr, rdata_.pkt_info.src_name,
+                      sizeof(rdata_.pkt_info.src_name), 0);
+    rdata_.pkt_info.src_port = pj_sockaddr_get_port(rem_addr);
+    rdata_.pkt_info.len  = 0;
+    rdata_.pkt_info.zero = 0;
+
+    pj_bzero(&local_cert_info_, sizeof(pj_ssl_cert_info));
+    pj_bzero(&remote_cert_info_, sizeof(pj_ssl_cert_info));
+
+    /* Register error subsystem */
+    /*pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER +
+                                              PJ_ERRNO_SPACE_SIZE * 6,
+                                              PJ_ERRNO_SPACE_SIZE,
+                                              &tls_strerror);
+    pj_assert(status == PJ_SUCCESS);*/
+
+    /* Init GnuTLS library */
+    int ret = gnutls_global_init();
+    if (ret < 0)
+        throw std::runtime_error("Can't initialise GNUTLS : "
+                                 + std::string(gnutls_strerror(ret)));
+
+    gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
+    gnutls_global_set_log_function(tls_print_logs);
+
+    gnutls_priority_init(&priority_cache,
+                         "SECURE192:-VERS-TLS-ALL:+VERS-DTLS1.0:%SERVER_PRECEDENCE",
+                         nullptr);
+
+    if (pjsip_transport_register(base.tpmgr, &base) != PJ_SUCCESS)
+        throw std::runtime_error("Can't register PJSIP transport.");
+    is_registered_ = true;
+
+    ice_->setOnRecv(comp_id_, [this](uint8_t* buf, size_t len) {
+        {
+            std::lock_guard<std::mutex> l(inputBuffMtx_);
+            tlsInputBuff_.emplace_back(buf, buf+len);
+            canRead_ = true;
+            RING_DBG("Ice: got data at %lu",
+                     clock::now().time_since_epoch().count());
+        }
+        cv_.notify_all();
+        return len;
+    });
+
+    tlsThread_.start();
+}
+
+SipsIceTransport::~SipsIceTransport()
+{
+    RING_DBG("~SipsIceTransport");
+    reset();
+    ice_->setOnRecv(comp_id_, nullptr);
+    tlsThread_.join();
+
+    /* Free GnuTLS library */
+    gnutls_global_deinit();
+
+    pj_lock_destroy(trData_.base.lock);
+    pj_atomic_destroy(trData_.base.ref_cnt);
+}
+
+pj_status_t
+SipsIceTransport::startTlsSession()
+{
+    RING_DBG("SipsIceTransport::startTlsSession as %s",
+             (is_server_ ? "server" : "client"));
+    int ret;
+    ret = gnutls_init(&session_, (is_server_ ? GNUTLS_SERVER : GNUTLS_CLIENT) | GNUTLS_DATAGRAM);
+    if (ret != GNUTLS_E_SUCCESS) {
+        reset();
+        return tls_status_from_err(ret);
+    }
+
+    gnutls_session_set_ptr(session_, this);
+    gnutls_transport_set_ptr(session_, this);
+
+    gnutls_priority_set(session_, priority_cache);
+
+    /* Allocate credentials for handshaking and transmission */
+    ret = gnutls_certificate_allocate_credentials(&xcred_);
+    if (ret < 0) {
+        RING_ERR("Can't allocate credentials");
+        reset();
+        return PJ_ENOMEM;
+    }
+
+    if (is_server_)
+        gnutls_certificate_set_dh_params(xcred_, param_.dh_params.get());
+
+    gnutls_certificate_set_verify_function(xcred_, [](gnutls_session_t session) {
+        auto this_ = reinterpret_cast<SipsIceTransport*>(gnutls_session_get_ptr(session));
+        return this_->verifyCertificate();
+    });
+
+    if (not param_.ca_list.empty()) {
+        /* Load CA if one is specified. */
+        ret = gnutls_certificate_set_x509_trust_file(xcred_,
+                                                     param_.ca_list.c_str(),
+                                                     GNUTLS_X509_FMT_PEM);
+        if (ret < 0)
+            ret = gnutls_certificate_set_x509_trust_file(xcred_,
+                                                         param_.ca_list.c_str(),
+                                                         GNUTLS_X509_FMT_DER);
+        if (ret < 0)
+            throw std::runtime_error("Can't load CA.");
+        RING_WARN("Loaded %s", param_.ca_list.c_str());
+
+        if (param_.id.first) {
+            /* Load certificate, key and pass */
+            ret = gnutls_certificate_set_x509_key(xcred_,
+                                                  &param_.id.second->cert, 1,
+                                                  param_.id.first->x509_key);
+            if (ret < 0)
+                throw std::runtime_error("Can't load certificate : "
+                                         + std::string(gnutls_strerror(ret)));
+        }
+    }
+
+    /* Finally set credentials for this session */
+    ret = gnutls_credentials_set(session_, GNUTLS_CRD_CERTIFICATE, xcred_);
+    if (ret != GNUTLS_E_SUCCESS) {
+        reset();
+        return tls_status_from_err(ret);
+    }
+
+    if (is_server_) {
+        /* Require client certificate and valid cookie */
+        gnutls_certificate_server_set_request(session_, GNUTLS_CERT_REQUIRE);
+        gnutls_dtls_prestate_set(session_, &prestate_);
+    }
+    int mtu = 3200;
+    gnutls_dtls_set_mtu(session_, mtu);
+
+    gnutls_transport_set_push_function(session_, [](gnutls_transport_ptr_t t,
+                                                    const void* d ,
+                                                    size_t s) -> ssize_t {
+        auto this_ = reinterpret_cast<SipsIceTransport*>(t);
+        return this_->tlsSend(d, s);
+    });
+    gnutls_transport_set_pull_function(session_, [](gnutls_transport_ptr_t t,
+                                                    void* d,
+                                                    size_t s) -> ssize_t {
+        auto this_ = reinterpret_cast<SipsIceTransport*>(t);
+        return this_->tlsRecv(d, s);
+    });
+    gnutls_transport_set_pull_timeout_function(session_, [](gnutls_transport_ptr_t t,
+                                                            unsigned ms) -> int {
+        auto this_ = reinterpret_cast<SipsIceTransport*>(t);
+        return this_->waitForTlsData(ms);
+    });
+
+    // start handshake
+    handshakeStart_ = clock::now();
+    state_ = TlsConnectionState::HANDSHAKING;
+}
+
+void
+SipsIceTransport::certGetCn(const pj_str_t *gen_name, pj_str_t *cn)
+{
+    pj_str_t CN_sign = {(char*)"CN=", 3};
+    char *p, *q;
+
+    pj_bzero(cn, sizeof(cn));
+
+    p = pj_strstr(gen_name, &CN_sign);
+    if (!p)
+        return;
+
+    p += 3; /* shift pointer to value part */
+    pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
+    q = pj_strchr(cn, ',');
+    if (q)
+        cn->slen = q - p;
+}
+
+/* Get certificate info; in case the certificate info is already populated,
+ * this function will check if the contents need updating by inspecting the
+ * issuer and the serial number. */
+void
+SipsIceTransport::certGetInfo(pj_pool_t *pool, pj_ssl_cert_info *ci,
+                              gnutls_x509_crt_t cert)
+{
+    RING_DBG("SipsIceTransport::certGetInfo");
+    char buf[512] = { 0 };
+    size_t bufsize = sizeof(buf);
+    std::array<uint8_t, sizeof(ci->serial_no)> serial_no; /* should be >= sizeof(ci->serial_no) */
+    size_t serialsize = serial_no.size();
+    size_t len = sizeof(buf);
+    int i, ret, seq = 0;
+    pj_ssl_cert_name_type type;
+
+    pj_assert(pool && ci && cert);
+
+    /* Get issuer */
+    gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize);
+
+    /* Get serial no */
+    gnutls_x509_crt_get_serial(cert, serial_no.data(), &serialsize);
+
+    /* Check if the contents need to be updated */
+    if (not pj_strcmp2(&ci->issuer.info, buf) and
+        not std::memcmp(ci->serial_no, serial_no.data(), serialsize))
+        return;
+
+    /* Update cert info */
+    pj_bzero(ci, sizeof(pj_ssl_cert_info));
+
+    /* Version */
+    ci->version = gnutls_x509_crt_get_version(cert);
+
+    /* Issuer */
+    pj_strdup2(pool, &ci->issuer.info, buf);
+    certGetCn(&ci->issuer.info, &ci->issuer.cn);
+
+    /* Serial number */
+    //pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
+    std::copy(serial_no.cbegin(), serial_no.cend(), (uint8_t*)ci->serial_no);
+
+    /* Subject */
+    bufsize = sizeof(buf);
+    gnutls_x509_crt_get_dn(cert, buf, &bufsize);
+    pj_strdup2(pool, &ci->subject.info, buf);
+    certGetCn(&ci->subject.info, &ci->subject.cn);
+
+    /* Validity */
+    ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert);
+    ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert);
+    ci->validity.gmt = 0;
+
+    /* Subject Alternative Name extension */
+    if (ci->version >= 3) {
+        char out[256] = { 0 };
+        /* Get the number of all alternate names so that we can allocate
+         * the correct number of bytes in subj_alt_name */
+        while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len, NULL) != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+            seq++;
+
+        ci->subj_alt_name.entry = \
+            (decltype(ci->subj_alt_name.entry))pj_pool_calloc(pool, seq, sizeof(*ci->subj_alt_name.entry));
+        if (!ci->subj_alt_name.entry) {
+            last_err_ = GNUTLS_E_MEMORY_ERROR;
+            return;
+        }
+
+        /* Now populate the alternative names */
+        for (i = 0; i < seq; i++) {
+            len = sizeof(out) - 1;
+            ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out, &len, NULL);
+            switch (ret) {
+                case GNUTLS_SAN_IPADDRESS:
+                    type = PJ_SSL_CERT_NAME_IP;
+                    pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6() : pj_AF_INET(),
+                                  out, buf, sizeof(buf));
+                    break;
+                case GNUTLS_SAN_URI:
+                    type = PJ_SSL_CERT_NAME_URI;
+                    break;
+                case GNUTLS_SAN_RFC822NAME:
+                    type = PJ_SSL_CERT_NAME_RFC822;
+                    break;
+                case GNUTLS_SAN_DNSNAME:
+                    type = PJ_SSL_CERT_NAME_DNS;
+                    break;
+                default:
+                    type = PJ_SSL_CERT_NAME_UNKNOWN;
+                    break;
+            }
+
+            if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
+                ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
+                pj_strdup2(pool,
+                           &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+                           type == PJ_SSL_CERT_NAME_IP ? buf : out);
+                ci->subj_alt_name.cnt++;
+            }
+        }
+        /* TODO: if no DNS alt. names were found, we could check against
+         * the commonName as per RFC3280. */
+    }
+}
+
+
+/* Update local & remote certificates info. This function should be
+ * called after handshake or renegotiation successfully completed. */
+void
+SipsIceTransport::certUpdate()
+{
+    RING_DBG("SipsIceTransport::certUpdate");
+    gnutls_x509_crt_t cert = NULL;
+    const gnutls_datum_t *us;
+    const gnutls_datum_t *certs;
+    unsigned int certslen = 0;
+    int ret = GNUTLS_CERT_INVALID;
+
+
+    //pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED);
+
+    /* Get active local certificate */
+    us = gnutls_certificate_get_ours(session_);
+    if (!us)
+        goto us_out;
+
+    ret = gnutls_x509_crt_init(&cert);
+    if (ret < 0)
+        goto us_out;
+    ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER);
+    if (ret < 0)
+        ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM);
+    if (ret < 0)
+        goto us_out;
+
+    certGetInfo(pool_.get(), &local_cert_info_, cert);
+
+us_out:
+    last_err_ = ret;
+    if (cert)
+        gnutls_x509_crt_deinit(cert);
+    else
+        pj_bzero(&local_cert_info_, sizeof(pj_ssl_cert_info));
+
+    cert = NULL;
+
+    /* Get active remote certificate */
+    certs = gnutls_certificate_get_peers(session_, &certslen);
+    if (certs == NULL || certslen == 0)
+        goto peer_out;
+
+    ret = gnutls_x509_crt_init(&cert);
+    if (ret < 0)
+        goto peer_out;
+
+    ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
+    if (ret < 0)
+        ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
+    if (ret < 0)
+        goto peer_out;
+
+    certGetInfo(pool_.get(), &remote_cert_info_, cert);
+
+peer_out:
+    last_err_ = ret;
+    if (cert)
+        gnutls_x509_crt_deinit(cert);
+    else
+        pj_bzero(&remote_cert_info_, sizeof(pj_ssl_cert_info));
+}
+
+pj_status_t
+SipsIceTransport::getInfo(pj_ssl_sock_info *info)
+{
+    RING_DBG("SipsIceTransport::getInfo");
+    pj_bzero(info, sizeof(*info));
+
+    /* Established flag */
+    info->established = (state_ == TlsConnectionState::ESTABLISHED);
+
+    /* Protocol */
+    info->proto = PJ_SSL_SOCK_PROTO_DTLS1;
+
+    /* Local address */
+    pj_sockaddr_cp(&info->local_addr, local_.pjPtr());
+
+    if (info->established) {
+        int i;
+        gnutls_cipher_algorithm_t lookup;
+        gnutls_cipher_algorithm_t cipher;
+
+        /* Current cipher */
+        cipher = gnutls_cipher_get(session_);
+        for (i = 0; ; i++) {
+            unsigned char id[2];
+            const char *suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
+                                                         NULL, &lookup,
+                                                         NULL, NULL);
+            if (suite) {
+                if (lookup == cipher) {
+                    info->cipher = (pj_ssl_cipher) ((id[0] << 8) | id[1]);
+                    break;
+                }
+            } else
+                break;
+        }
+
+        /* Remote address */
+        pj_sockaddr_cp(&info->remote_addr, remote_.pjPtr());
+
+        /* Certificates info */
+        info->local_cert_info = &local_cert_info_;
+        info->remote_cert_info = &remote_cert_info_;
+
+        /* Verification status */
+        info->verify_status = PJ_SUCCESS;//verify_status_;
+    }
+
+    /* Last known GnuTLS error code */
+    info->last_native_err = last_err_;
+
+    return PJ_SUCCESS;
+}
+
+
+/* When handshake completed:
+ * - notify application
+ * - if handshake failed, reset SSL state
+ * - return PJ_FALSE when SSL socket instance is destroyed by application. */
+pj_bool_t
+SipsIceTransport::onHandshakeComplete(pj_status_t status)
+{
+    RING_DBG("SipsIceTransport::onHandshakeComplete %d", status);
+    pj_bool_t ret = PJ_TRUE;
+
+    /* Cancel handshake timer */
+    /*if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
+        pj_timer_heap_cancel(param_.timer_heap, &ssock->timer);
+        ssock->timer.id = TIMER_NONE;
+    }*/
+
+    /* Update certificates info on successful handshake */
+    if (status == PJ_SUCCESS)
+        certUpdate();
+
+    pj_ssl_sock_info ssl_info;
+    getInfo(&ssl_info);
+
+    auto state_cb = pjsip_tpmgr_get_state_cb(trData_.base.tpmgr);
+    pjsip_transport_state_info state_info;
+    pjsip_tls_state_info tls_info;
+
+    /* Init transport state info */
+    pj_bzero(&state_info, sizeof(state_info));
+    pj_bzero(&tls_info, sizeof(tls_info));
+    tls_info.ssl_sock_info = &ssl_info;
+    state_info.ext_info = &tls_info;
+    state_info.status = ssl_info.verify_status ? PJSIP_TLS_ECERTVERIF : PJ_SUCCESS;
+
+    /* Accepting */
+    if (is_server_) {
+        if (status != PJ_SUCCESS) {
+            /* Handshake failed in accepting, destroy our self silently. */
+
+            char errmsg[PJ_ERR_MSG_SIZE];
+            RING_WARN("Handshake failed in accepting %s: %s",
+                remote_.toString(true).c_str(),
+                pj_strerror(status, errmsg, sizeof(errmsg)).ptr);
+
+            /* Workaround for ticket #985 */
+/*#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || (defined(PJ_WIN64) && PJ_WIN64 != 0)
+            if (param_.timer_heap) {
+                pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT};
+
+                reset();
+
+                timer.id = TIMER_CLOSE;
+                pj_time_val_normalize(&interval);
+                if (pj_timer_heap_schedule(param_.timer_heap, &timer, &interval) != 0) {
+                    timer.id = TIMER_NONE;
+                    close();
+                }
+            } else
+#endif*/ /* PJ_WIN32 */
+            {
+                reset();
+            }
+
+            return PJ_FALSE;
+        }
+        /* Notify application the newly accepted SSL socket */
+        if (state_cb)
+            (*state_cb)(getTransportBase(), PJSIP_TP_STATE_CONNECTED,
+                        &state_info);
+    } else { /* Connecting */
+        /* On failure, reset SSL socket state first, as app may try to
+         * reconnect in the callback. */
+        if (status != PJ_SUCCESS) {
+            /* Server disconnected us, possibly due to negotiation failure */
+            reset();
+        }
+        if (state_cb)
+            (*state_cb)(getTransportBase(),
+                        (status != PJ_SUCCESS) ? PJSIP_TP_STATE_DISCONNECTED : PJSIP_TP_STATE_CONNECTED,
+                        &state_info);
+    }
+
+    return ret;
+}
+
+bool
+SipsIceTransport::setup()
+{
+    RING_WARN("Starting GnuTLS thread");
+    if (is_server_) {
+        gnutls_key_generate(&cookie_key_, GNUTLS_COOKIE_KEY_SIZE);
+        state_ = TlsConnectionState::COOKIE;
+    } else
+        startTlsSession();
+    return true;
+}
+
+void
+SipsIceTransport::loop()
+{
+    if (not ice_->isRunning()) {
+        reset();
+        return;
+    }
+    if (state_ == TlsConnectionState::COOKIE) {
+        {
+            std::unique_lock<std::mutex> l(inputBuffMtx_);
+            if (tlsInputBuff_.empty()) {
+                cv_.wait(l, [&](){
+                    return state_ != TlsConnectionState::COOKIE or not tlsInputBuff_.empty();
+                });
+            }
+            RING_DBG("Cookie: got data at %lu",
+                     clock::now().time_since_epoch().count());
+            if (state_ != TlsConnectionState::COOKIE)
+                return;
+            const auto& pck = tlsInputBuff_.front();
+            memset(&prestate_, 0, sizeof(prestate_));
+            int ret = gnutls_dtls_cookie_verify(&cookie_key_,
+                                                &trData_.base.key.rem_addr,
+                                                trData_.base.addr_len,
+                                                (char*)pck.data(), pck.size(),
+                                                &prestate_);
+            if (ret < 0) {
+                RING_DBG("gnutls_dtls_cookie_send");
+                gnutls_dtls_cookie_send(&cookie_key_,
+                                        &trData_.base.key.rem_addr,
+                                        trData_.base.addr_len, &prestate_, this,
+                                        [](gnutls_transport_ptr_t t,
+                                           const void* d ,
+                                           size_t s) -> ssize_t {
+                    auto this_ = reinterpret_cast<SipsIceTransport*>(t);
+                    return this_->tlsSend(d, s);
+                });
+                tlsInputBuff_.pop_front();
+                if (tlsInputBuff_.empty())
+                    canRead_ = false;
+                return;
+            }
+        }
+        startTlsSession();
+    }
+    if (state_ == TlsConnectionState::HANDSHAKING) {
+        if (clock::now() - handshakeStart_ > param_.timeout) {
+            onHandshakeComplete(PJ_ETIMEDOUT);
+            return;
+        }
+        int status = tryHandshake();
+        if (status != PJ_EPENDING)
+            onHandshakeComplete(status);
+    }
+    if (state_ == TlsConnectionState::ESTABLISHED) {
+        {
+            std::mutex flagsMtx_ {};
+            std::unique_lock<std::mutex> l(flagsMtx_);
+            cv_.wait(l, [&](){
+                return state_ != TlsConnectionState::ESTABLISHED or canRead_ or canWrite_;
+            });
+        }
+        if (state_ != TlsConnectionState::ESTABLISHED)
+            return;
+        while (canRead_) {
+            if (rdata_.pkt_info.len < 0)
+                rdata_.pkt_info.len = 0;
+            ssize_t decrypted_size = gnutls_record_recv(session_,
+                    (uint8_t*)rdata_.pkt_info.packet + rdata_.pkt_info.len,
+                    sizeof(rdata_.pkt_info.packet)-rdata_.pkt_info.len);
+            rdata_.pkt_info.len += decrypted_size;
+            RING_WARN("gnutls_record_recv : %d (tot %d)", decrypted_size,
+                      rdata_.pkt_info.len);
+            if (decrypted_size > 0/* || transport error */) {
+                rdata_.pkt_info.zero = 0;
+                pj_gettimeofday(&rdata_.pkt_info.timestamp);
+                auto eaten = pjsip_tpmgr_receive_packet(trData_.base.tpmgr,
+                                                        &rdata_);
+                auto rem = rdata_.pkt_info.len - eaten;
+                if (rem > 0 && rem != rdata_.pkt_info.len) {
+                    std::move(rdata_.pkt_info.packet + eaten,
+                                rdata_.pkt_info.packet + eaten + rem,
+                                rdata_.pkt_info.packet);
+                }
+                rdata_.pkt_info.len = rem;
+                pj_pool_reset(rdata_.tp_info.pool);
+            } else if (decrypted_size == 0) {
+                /* Nothing more to read */
+                reset();
+                break;//  PJ_TRUE;
+            } else if (decrypted_size == GNUTLS_E_AGAIN or
+                       decrypted_size == GNUTLS_E_INTERRUPTED) {
+                break;//  PJ_TRUE;
+            } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
+                /* Seems like we are renegotiating */
+                pj_status_t try_handshake_status = tryHandshake();
+
+                /* Not pending is either success or failed */
+                if (try_handshake_status != PJ_EPENDING) {
+                    if (!onHandshakeComplete(try_handshake_status)) {
+                        break;// PJ_FALSE;
+                    }
+                }
+
+                if (try_handshake_status != PJ_SUCCESS and
+                    try_handshake_status != PJ_EPENDING) {
+                    break;// PJ_FALSE;
+                }
+            } else if (!gnutls_error_is_fatal(decrypted_size)) {
+                /* non-fatal error, let's just continue */
+            } else {
+                reset();
+                break;// PJ_FALSE;
+            }
+        }
+        flushOutputBuff();
+    }
+}
+
+void
+SipsIceTransport::clean()
+{
+    RING_WARN("Ending GnuTLS thread");
+    tlsInputBuff_.clear();
+    canRead_ = false;
+
+    while (not outputBuff_.empty()) {
+        auto& f = outputBuff_.front();
+        f.tdata_op_key->tdata = nullptr;
+        if (f.tdata_op_key->callback)
+            f.tdata_op_key->callback(getTransportBase(),
+                                     f.tdata_op_key->token,
+                                     -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN));
+        outputBuff_.pop_front();
+    }
+    canWrite_ = false;
+    if (cookie_key_.data) {
+        gnutls_free(cookie_key_.data);
+        cookie_key_.data = nullptr;
+        cookie_key_.size = 0;
+    }
+
+    pjsip_transport_add_ref(getTransportBase());
+    close();
+    auto state_cb = pjsip_tpmgr_get_state_cb(trData_.base.tpmgr);
+    if (state_cb) {
+        pjsip_transport_state_info state_info;
+        pjsip_tls_state_info tls_info;
+
+        /* Init transport state info */
+        pj_bzero(&state_info, sizeof(state_info));
+        pj_bzero(&tls_info, sizeof(tls_info));
+        pj_ssl_sock_info ssl_info;
+        getInfo(&ssl_info);
+        tls_info.ssl_sock_info = &ssl_info;
+        state_info.ext_info = &tls_info;
+        state_info.status = PJ_SUCCESS;
+
+        (*state_cb)(getTransportBase(), PJSIP_TP_STATE_DISCONNECTED,
+                    &state_info);
+    }
+    if (trData_.base.is_shutdown or trData_.base.is_destroying) {
+        pjsip_transport_dec_ref(getTransportBase());
+        return;
+    }
+    pjsip_transport_shutdown(getTransportBase());
+    /* Now, it is ok to destroy the transport. */
+    pjsip_transport_dec_ref(getTransportBase());
+}
+
+IpAddr
+SipsIceTransport::getLocalAddress() const
+{
+    return ice_->getLocalAddress(comp_id_);
+}
+
+ssize_t
+SipsIceTransport::tlsSend(const void* d , size_t s)
+{
+    RING_DBG("SipsIceTransport::tlsSend %lu", s);
+    return ice_->send(comp_id_, (const uint8_t*)d, s);
+}
+
+ssize_t
+SipsIceTransport::tlsRecv(void* d , size_t s)
+{
+    std::lock_guard<std::mutex> l(inputBuffMtx_);
+    if (tlsInputBuff_.empty()) {
+        errno = EAGAIN;
+        return -1;
+    }
+    RING_DBG("SipsIceTransport::tlsRecv %lu at %lu",
+             s, clock::now().time_since_epoch().count());
+    const auto& front = tlsInputBuff_.front();
+    const auto n = std::min(front.size(), s);
+    std::copy_n(front.begin(), n, (uint8_t*)d);
+    tlsInputBuff_.pop_front();
+    if (tlsInputBuff_.empty())
+        canRead_ = false;
+    return n;
+}
+
+int
+SipsIceTransport::waitForTlsData(unsigned ms)
+{
+    RING_DBG("SipsIceTransport::waitForTlsData %u", ms);
+
+    std::unique_lock<std::mutex> l(inputBuffMtx_);
+    if (tlsInputBuff_.empty()) {
+        cv_.wait_for(l, std::chrono::milliseconds(ms), [&]() {
+            return state_ == TlsConnectionState::DISCONNECTED or not tlsInputBuff_.empty();
+        });
+    }
+    return tlsInputBuff_.empty() ? 0 : tlsInputBuff_.front().size();
+}
+
+pj_status_t
+SipsIceTransport::send(pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr,
+                      int addr_len, void *token,
+                      pjsip_transport_callback callback)
+{
+    // Sanity check
+    PJ_ASSERT_RETURN(tdata, PJ_EINVAL);
+
+    // Check that there's no pending operation associated with the tdata
+    PJ_ASSERT_RETURN(tdata->op_key.tdata == nullptr, PJSIP_EPENDINGTX);
+
+    // Check the address is supported
+    PJ_ASSERT_RETURN(rem_addr and
+                     (addr_len==sizeof(pj_sockaddr_in) or
+                      addr_len==sizeof(pj_sockaddr_in6)),
+                     PJ_EINVAL);
+
+    tdata->op_key.tdata = tdata;
+    tdata->op_key.token = token;
+    tdata->op_key.callback = callback;
+    {
+        std::lock_guard<std::mutex> l(outputBuffMtx_);
+        outputBuff_.emplace_back(DelayedTxData{&tdata->op_key, {}});
+        if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
+            auto& dtd = outputBuff_.back();
+            dtd.timeout = clock::now();
+            dtd.timeout += std::chrono::milliseconds(pjsip_cfg()->tsx.td);
+        }
+        canWrite_ = true;
+    }
+    cv_.notify_all();
+    return PJ_EPENDING;
+}
+
+pj_status_t
+SipsIceTransport::flushOutputBuff()
+{
+    if (state_ != TlsConnectionState::ESTABLISHED)
+        return PJ_EPENDING;
+
+    ssize_t status = PJ_SUCCESS;
+    while (true) {
+        DelayedTxData f;
+        {
+            std::lock_guard<std::mutex> l(outputBuffMtx_);
+            if (outputBuff_.empty()) {
+                canWrite_ = false;
+                break;
+            }
+            else {
+                f = outputBuff_.front();
+                outputBuff_.pop_front();
+            }
+        }
+        if (f.timeout != clock::time_point() && f.timeout < clock::now())
+            continue;
+        status = trySend(f.tdata_op_key);
+        f.tdata_op_key->tdata = nullptr;
+        if (f.tdata_op_key->callback)
+            f.tdata_op_key->callback(getTransportBase(),
+                                     f.tdata_op_key->token, status);
+        if (status < 0)
+            break;
+    }
+    return status > 0 ? PJ_SUCCESS : (pj_status_t)status;
+}
+
+ssize_t
+SipsIceTransport::trySend(pjsip_tx_data_op_key *pck)
+{
+    if (state_ != TlsConnectionState::ESTABLISHED)
+        return PJ_EPENDING;
+
+    const size_t max_tx_sz = gnutls_dtls_get_data_mtu(session_);
+
+    pjsip_tx_data *tdata = pck->tdata;
+    size_t size = tdata->buf.cur - tdata->buf.start;
+    size_t total_written = 0;
+    while (total_written < size) {
+        /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push
+         * callback to actually write the encrypted bytes into our output circular
+         * buffer. GnuTLS may refuse to "send" everything at once, but since we are
+         * not really sending now, we will just call it again now until it succeeds
+         * (or fails in a fatal way). */
+        auto tx_size = std::min(max_tx_sz, size - total_written);
+        int nwritten = gnutls_record_send(session_,
+                                          tdata->buf.start + total_written,
+                                          tx_size);
+        if (nwritten > 0) {
+            /* Good, some data was encrypted and written */
+            total_written += nwritten;
+        } else {
+            /* Normally we would have to retry record_send but our internal
+             * state has not changed, so we have to ask for more data first.
+             * We will just try again later, although this should never happen.
+             */
+            RING_ERR("gnutls_record_send : %s", gnutls_strerror(nwritten));
+            return tls_status_from_err(nwritten);
+        }
+    }
+    return total_written;
+}
+
+void
+SipsIceTransport::close()
+{
+    RING_WARN("SipsIceTransport::close");
+    state_ = TlsConnectionState::DISCONNECTED;
+    if (session_) {
+        gnutls_bye(session_, GNUTLS_SHUT_RDWR);
+        gnutls_deinit(session_);
+        session_ = nullptr;
+    }
+
+    if (xcred_) {
+        gnutls_certificate_free_credentials(xcred_);
+        xcred_ = nullptr;
+    }
+}
+
+void
+SipsIceTransport::reset()
+{
+    RING_WARN("SipsIceTransport::reset");
+    state_ = TlsConnectionState::DISCONNECTED;
+    tlsThread_.stop();
+    cv_.notify_all();
+}
+
+}} // namespace ring::tls
diff --git a/daemon/src/ringdht/sips_transport_ice.h b/daemon/src/ringdht/sips_transport_ice.h
new file mode 100644
index 0000000000000000000000000000000000000000..2f49028661cc962de248a4251917d111460a01c5
--- /dev/null
+++ b/daemon/src/ringdht/sips_transport_ice.h
@@ -0,0 +1,181 @@
+/*
+ *  Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#pragma once
+
+#include "ip_utils.h"
+#include "threadloop.h"
+
+#include <opendht/crypto.h>
+
+#include <pjsip.h>
+#include <pj/pool.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/dtls.h>
+#include <gnutls/abstract.h>
+
+#include <list>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+namespace ring {
+class IceTransport;
+} // namespace ring
+
+namespace ring { namespace tls {
+
+enum class TlsConnectionState {
+    DISCONNECTED,
+    COOKIE,
+    HANDSHAKING,
+    ESTABLISHED
+};
+
+struct TlsParams {
+    std::string ca_list;
+    dht::crypto::Identity id;
+    std::shared_ptr<gnutls_dh_params_int> dh_params;
+    std::chrono::steady_clock::duration timeout;
+    std::function<pj_status_t(unsigned status,
+                              const gnutls_datum_t* cert_list,
+                              unsigned cert_list_size)> cert_check;
+};
+
+struct SipsIceTransport
+{
+    using clock = std::chrono::steady_clock;
+    using TransportData = struct {
+        pjsip_transport base; // do not move, SHOULD be the fist member
+        SipsIceTransport* self {nullptr};
+    };
+    static_assert(std::is_standard_layout<TransportData>::value,
+                  "TranportData requires standard-layout");
+
+    SipsIceTransport(pjsip_endpoint* endpt, const TlsParams& param,
+                    const std::shared_ptr<IceTransport>& ice, int comp_id);
+    ~SipsIceTransport();
+
+    void reset();
+
+    IpAddr getLocalAddress() const;
+
+    std::shared_ptr<IceTransport> getIceTransport() const {
+        return ice_;
+    }
+
+    pjsip_transport* getTransportBase() {
+        return &trData_.base;
+    }
+
+private:
+    std::unique_ptr<pj_pool_t, decltype(pj_pool_release)&> pool_;
+    std::unique_ptr<pj_pool_t, decltype(pj_pool_release)&> rxPool_;
+
+    using DelayedTxData = struct {
+        pjsip_tx_data_op_key *tdata_op_key;
+        clock::time_point timeout;
+    };
+
+    TransportData trData_;
+    bool is_registered_ {false};
+    const std::shared_ptr<IceTransport> ice_;
+    const int comp_id_;
+
+    TlsParams param_;
+
+    bool is_server_ {false};
+    //bool has_pending_connect_ {false};
+    IpAddr local_ {};
+    IpAddr remote_ {};
+
+    ThreadLoop tlsThread_;
+    std::condition_variable_any cv_;
+    std::atomic<bool> canRead_ {false};
+    std::atomic<bool> canWrite_ {false};
+
+    // TODO
+    pj_status_t verify_status_;
+    int last_err_;
+
+    pj_ssl_cert_info local_cert_info_;
+    pj_ssl_cert_info remote_cert_info_;
+
+    std::atomic<TlsConnectionState> state_ {TlsConnectionState::DISCONNECTED};
+    clock::time_point handshakeStart_;
+
+    gnutls_session_t session_ {nullptr};
+    gnutls_certificate_credentials_t xcred_;
+    gnutls_priority_t priority_cache;
+    gnutls_datum_t cookie_key_;
+    gnutls_dtls_prestate_st prestate_;
+
+    /**
+     * To be called on a regular basis to receive packets
+     */
+    bool setup();
+    void loop();
+    void clean();
+
+    // SIP transport <-> GnuTLS
+    pj_status_t send(pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr,
+                     int addr_len, void *token,
+                     pjsip_transport_callback callback);
+    ssize_t trySend(pjsip_tx_data_op_key *tdata);
+    pj_status_t flushOutputBuff();
+    std::list<DelayedTxData> outputBuff_ {};
+    std::mutex outputBuffMtx_ {};
+    pjsip_rx_data rdata_;
+
+    // GnuTLS <-> ICE
+    ssize_t tlsSend(const void*, size_t);
+    ssize_t tlsRecv(void* d , size_t s);
+    int waitForTlsData(unsigned ms);
+    std::mutex inputBuffMtx_ {};
+    std::list<std::vector<uint8_t>> tlsInputBuff_ {};
+
+    pj_status_t startTlsSession();
+    pj_status_t tryHandshake();
+    void certGetCn(const pj_str_t *gen_name, pj_str_t *cn);
+    void certGetInfo(pj_pool_t *pool, pj_ssl_cert_info *ci,
+                     gnutls_x509_crt_t cert);
+    void certUpdate();
+    pj_bool_t onHandshakeComplete(pj_status_t status);
+    int verifyCertificate();
+    pj_status_t getInfo (pj_ssl_sock_info *info);
+
+    void close();
+};
+
+}} // namespace ring::tls
diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp
index 4d0ca0f85c56e9174f8dc6d3cb4b64bf1d3320d7..17d0a65849e59f8296c163fb60386f312d16243a 100644
--- a/daemon/src/sip/sipaccount.cpp
+++ b/daemon/src/sip/sipaccount.cpp
@@ -124,10 +124,6 @@ SIPAccount::SIPAccount(const std::string& accountID, bool presenceEnabled)
     , cred_()
     , tlsSetting_()
     , ciphers_(100)
-    , tlsCaListFile_()
-    , tlsCertificateFile_()
-    , tlsPrivateKeyFile_()
-    , tlsPassword_()
     , tlsMethod_("TLSv1")
     , tlsCiphers_()
     , tlsServerName_(0, 0)
@@ -397,12 +393,8 @@ void SIPAccount::serialize(YAML::Emitter &out)
     out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer_;
     out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate_;
     out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeoutSec_;
-    out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile_;
-    out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile_;
     out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers_;
     out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod_;
-    out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword_;
-    out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile_;
     out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName_;
     out << YAML::EndMap;
 
@@ -498,16 +490,12 @@ void SIPAccount::unserialize(const YAML::Node &node)
     const auto &tlsMap = node[Conf::TLS_KEY];
 
     parseValue(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable_);
-    parseValue(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile_);
-    parseValue(tlsMap, Conf::CALIST_KEY, tlsCaListFile_);
     parseValue(tlsMap, Conf::CIPHERS_KEY, tlsCiphers_);
 
     std::string tmpMethod(tlsMethod_);
     parseValue(tlsMap, Conf::METHOD_KEY, tmpMethod);
     validate(tlsMethod_, tmpMethod, VALID_TLS_METHODS);
 
-    parseValue(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword_);
-    parseValue(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile_);
     parseValue(tlsMap, Conf::SERVER_KEY, tlsServerName_);
     parseValue(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate_);
     parseValue(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient_);
@@ -567,12 +555,6 @@ void SIPAccount::setAccountDetails(const std::map<std::string, std::string> &det
 
     // TLS settings
     parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable_);
-    parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort_);
-    parseString(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile_);
-    parseString(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile_);
-
-    parseString(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile_);
-    parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword_);
     auto iter = details.find(Conf::CONFIG_TLS_METHOD);
     if (iter != details.end())
         validate(tlsMethod_, iter->second, VALID_TLS_METHODS);
@@ -673,10 +655,6 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() const
 
     // TLS listener is unique and parameters are modified through IP2IP_PROFILE
     a[Conf::CONFIG_TLS_ENABLE] = tlsEnable_ ? TRUE_STR : FALSE_STR;
-    a[Conf::CONFIG_TLS_CA_LIST_FILE] = tlsCaListFile_;
-    a[Conf::CONFIG_TLS_CERTIFICATE_FILE] = tlsCertificateFile_;
-    a[Conf::CONFIG_TLS_PRIVATE_KEY_FILE] = tlsPrivateKeyFile_;
-    a[Conf::CONFIG_TLS_PASSWORD] = tlsPassword_;
     a[Conf::CONFIG_TLS_METHOD] = tlsMethod_;
     a[Conf::CONFIG_TLS_CIPHERS] = tlsCiphers_;
     a[Conf::CONFIG_TLS_SERVER_NAME] = tlsServerName_;
diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h
index b2e8997cc0be93cc29b6414511269c477d2ab3ec..5eaf76911c2763db57ef17a5c6385fe3a9ec51a6 100644
--- a/daemon/src/sip/sipaccount.h
+++ b/daemon/src/sip/sipaccount.h
@@ -644,10 +644,6 @@ class SIPAccount : public SIPAccountBase {
         pj_uint16_t stunPort_ {PJ_STUN_PORT};
 
         bool tlsEnable_ {false};
-        std::string tlsCaListFile_;
-        std::string tlsCertificateFile_;
-        std::string tlsPrivateKeyFile_;
-        std::string tlsPassword_;
         std::string tlsMethod_;
         std::string tlsCiphers_;
         std::string tlsServerName_;
diff --git a/daemon/src/sip/sipaccountbase.cpp b/daemon/src/sip/sipaccountbase.cpp
index d283c70eb2150cf32dfb069d854a65f214d72064..d315f23dcb190b1881ca910a4620f06eba3df083 100644
--- a/daemon/src/sip/sipaccountbase.cpp
+++ b/daemon/src/sip/sipaccountbase.cpp
@@ -121,6 +121,10 @@ void SIPAccountBase::serialize(YAML::Emitter &out)
 void SIPAccountBase::serializeTls(YAML::Emitter &out)
 {
     out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort_;
+    out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile_;
+    out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile_;
+    out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword_;
+    out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile_;
 }
 
 void SIPAccountBase::unserialize(const YAML::Node &node)
@@ -171,6 +175,10 @@ void SIPAccountBase::unserialize(const YAML::Node &node)
     // get tls submap
     const auto &tlsMap = node[Conf::TLS_KEY];
     parseValue(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort_);
+    parseValue(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile_);
+    parseValue(tlsMap, Conf::CALIST_KEY, tlsCaListFile_);
+    parseValue(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword_);
+    parseValue(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile_);
 
     unserializeRange(node, Conf::AUDIO_PORT_MIN_KEY, Conf::AUDIO_PORT_MAX_KEY, audioPortRange_);
     unserializeRange(node, Conf::VIDEO_PORT_MIN_KEY, Conf::VIDEO_PORT_MAX_KEY, videoPortRange_);
@@ -207,6 +215,10 @@ void SIPAccountBase::setAccountDetails(const std::map<std::string, std::string>
 
     // TLS
     parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort_);
+    parseString(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile_);
+    parseString(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile_);
+    parseString(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile_);
+    parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword_);
 }
 
 std::map<std::string, std::string>
@@ -240,6 +252,10 @@ SIPAccountBase::getAccountDetails() const
     std::stringstream tlslistenerport;
     tlslistenerport << tlsListenerPort_;
     a[Conf::CONFIG_TLS_LISTENER_PORT] = tlslistenerport.str();
+    a[Conf::CONFIG_TLS_CA_LIST_FILE] = tlsCaListFile_;
+    a[Conf::CONFIG_TLS_CERTIFICATE_FILE] = tlsCertificateFile_;
+    a[Conf::CONFIG_TLS_PRIVATE_KEY_FILE] = tlsPrivateKeyFile_;
+    a[Conf::CONFIG_TLS_PASSWORD] = tlsPassword_;
     return a;
 }
 
diff --git a/daemon/src/sip/sipaccountbase.h b/daemon/src/sip/sipaccountbase.h
index d2796ba55f7b3e705e046985abccec2892b779e4..89a379e6d871990fcb825981a4a5fc3d125e2a0f 100644
--- a/daemon/src/sip/sipaccountbase.h
+++ b/daemon/src/sip/sipaccountbase.h
@@ -336,9 +336,13 @@ protected:
     pj_uint16_t publishedPort_ {DEFAULT_SIP_PORT};
 
     /**
-     * The global TLS listener port which can be configured through the IP2IP_PROFILE
+     * The TLS listener port
      */
     pj_uint16_t tlsListenerPort_ {DEFAULT_SIP_TLS_PORT};
+    std::string tlsCaListFile_;
+    std::string tlsCertificateFile_;
+    std::string tlsPrivateKeyFile_;
+    std::string tlsPassword_;
 
     /**
      * DTMF type used for this account SIPINFO or RTP
diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp
index 68c0a959ba6d24d4e9b35d36120efd28db267063..36aa772fe504d2da04a1dc539555c377d4f5b694 100644
--- a/daemon/src/sip/siptransport.cpp
+++ b/daemon/src/sip/siptransport.cpp
@@ -35,6 +35,7 @@
 #include "ip_utils.h"
 
 #include "ringdht/sip_transport_ice.h"
+#include "ringdht/sips_transport_ice.h"
 
 #include "array_size.h"
 #include "intrin.h"
@@ -163,14 +164,14 @@ SipTransport::removeStateListener(uintptr_t lid)
 }
 
 SipTransportBroker::SipTransportBroker(pjsip_endpoint *endpt,
-                                       pj_caching_pool& cp, pj_pool_t& pool)
-    : cp_(cp), pool_(pool), endpt_(endpt)
+                                       pj_caching_pool& cp, pj_pool_t& pool) :
+cp_(cp), pool_(pool), endpt_(endpt)
 {
-#if HAVE_DHT
+/*#if HAVE_DHT
     pjsip_transport_register_type(PJSIP_TRANSPORT_DATAGRAM, "ICE",
                                   pjsip_transport_get_default_port_for_type(PJSIP_TRANSPORT_UDP),
                                   &ice_pj_transport_type_);
-#endif
+#endif*/
     RING_DBG("SipTransportBroker@%p", this);
 }
 
@@ -412,15 +413,34 @@ std::shared_ptr<SipTransport>
 SipTransportBroker::getIceTransport(const std::shared_ptr<IceTransport> ice,
                                     unsigned comp_id)
 {
-    auto sip_ice_tr = std::unique_ptr<SipIceTransport>(new SipIceTransport(endpt_, pool_,
-                                                                           ice_pj_transport_type_,
-                                                                           ice, comp_id));
+    auto sip_ice_tr = std::unique_ptr<SipIceTransport>(
+        new SipIceTransport(endpt_, pool_, ice_pj_transport_type_, ice, comp_id));
+    auto tr = sip_ice_tr->getTransportBase();
+    auto sip_tr = std::make_shared<SipTransport>(tr);
+    sip_ice_tr.release(); // managed by PJSIP now
+
+    {
+        std::lock_guard<std::mutex> lock(transportMapMutex_);
+        // we do not check for key existance as we've just created it
+        // (member of new SipIceTransport instance)
+        transports_.emplace(std::make_pair(tr, sip_tr));
+    }
+    return sip_tr;
+}
+
+std::shared_ptr<SipTransport>
+SipTransportBroker::getTlsIceTransport(const std::shared_ptr<ring::IceTransport> ice,
+                                       unsigned comp_id,
+                                       const tls::TlsParams& params)
+{
+    auto sip_ice_tr = std::unique_ptr<tls::SipsIceTransport>(
+        new tls::SipsIceTransport(endpt_, params, ice, comp_id));
     auto tr = sip_ice_tr->getTransportBase();
     auto sip_tr = std::make_shared<SipTransport>(tr);
     sip_ice_tr.release(); // managed by PJSIP now
 
     {
-        std::unique_lock<std::mutex> lock(transportMapMutex_);
+        std::lock_guard<std::mutex> lock(transportMapMutex_);
         // we do not check for key existance as we've just created it
         // (member of new SipIceTransport instance)
         transports_.emplace(std::make_pair(tr, sip_tr));
diff --git a/daemon/src/sip/siptransport.h b/daemon/src/sip/siptransport.h
index 4cf382e57d4f41f0adbe11035aa8f7f38e7934d8..64232882c1eb26e31ff9e7d90c01b61d4b26249c 100644
--- a/daemon/src/sip/siptransport.h
+++ b/daemon/src/sip/siptransport.h
@@ -145,8 +145,10 @@ class SipTransport
 };
 
 class IpAddr;
-class SipIceTransport;
 class IceTransport;
+namespace tls {
+   struct TlsParams;
+};
 
 /**
  * Manages the transports and receive callbacks from PJSIP
@@ -160,13 +162,20 @@ public:
     std::shared_ptr<SipTransport> getUdpTransport(const SipTransportDescr&);
 
 #if HAVE_TLS
-    std::shared_ptr<TlsListener> getTlsListener(const SipTransportDescr&, const pjsip_tls_setting*);
+    std::shared_ptr<TlsListener>
+    getTlsListener(const SipTransportDescr&, const pjsip_tls_setting*);
 
-    std::shared_ptr<SipTransport> getTlsTransport(const std::shared_ptr<TlsListener>&, const IpAddr& remote);
+    std::shared_ptr<SipTransport>
+    getTlsTransport(const std::shared_ptr<TlsListener>&, const IpAddr& remote);
 #endif
 
 #if HAVE_DHT
-    std::shared_ptr<SipTransport> getIceTransport(const std::shared_ptr<IceTransport>, unsigned comp_id);
+    std::shared_ptr<SipTransport>
+    getIceTransport(const std::shared_ptr<IceTransport>, unsigned comp_id);
+
+    std::shared_ptr<SipTransport>
+    getTlsIceTransport(const std::shared_ptr<IceTransport>, unsigned comp_id,
+                       const tls::TlsParams&);
 #endif
 
     std::shared_ptr<SipTransport> addTransport(pjsip_transport*);