barbieri pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=f4198f022a4595efb4eb234b31af933510cb1575

commit f4198f022a4595efb4eb234b31af933510cb1575
Author: Gustavo Sverzut Barbieri <[email protected]>
Date:   Fri Oct 28 22:48:19 2016 -0200

    efl_net_socket_ssl: initial SSL wrapper.
    
    This is the first step towards SSL connections on top of sockets, with
    an example on how to upgrade a dialer and a server client using TCP.
---
 src/Makefile_Ecore_Con.am                          |  21 +-
 src/examples/ecore/.gitignore                      |   2 +
 src/examples/ecore/Makefile.am                     |  15 +-
 .../ecore/efl_net_socket_ssl_dialer_example.c      | 512 +++++++++++++++++
 .../ecore/efl_net_socket_ssl_server_example.c      | 351 ++++++++++++
 src/lib/ecore_con/Ecore_Con_Eo.h                   |   6 +
 src/lib/ecore_con/ecore_con.c                      |   6 +
 src/lib/ecore_con/ecore_con_private.h              |   4 +
 src/lib/ecore_con/efl_net_socket_ssl.c             | 611 +++++++++++++++++++++
 src/lib/ecore_con/efl_net_socket_ssl.eo            |  90 +++
 src/lib/ecore_con/efl_net_ssl_conn-gnutls.c        | 362 ++++++++++++
 src/lib/ecore_con/efl_net_ssl_conn-none.c          |  50 ++
 src/lib/ecore_con/efl_net_ssl_conn-openssl.c       | 520 ++++++++++++++++++
 src/lib/ecore_con/efl_net_ssl_context.c            | 373 +++++++++++++
 src/lib/ecore_con/efl_net_ssl_context.eo           | 122 ++++
 src/lib/ecore_con/efl_net_ssl_ctx-gnutls.c         | 310 +++++++++++
 src/lib/ecore_con/efl_net_ssl_ctx-none.c           |  38 ++
 src/lib/ecore_con/efl_net_ssl_ctx-openssl.c        | 387 +++++++++++++
 src/lib/ecore_con/efl_net_ssl_types.eot            |  26 +
 19 files changed, 3802 insertions(+), 4 deletions(-)

diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index 49cf65b..53ea70d 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -20,6 +20,8 @@ ecore_con_eolian_files = \
         lib/ecore_con/efl_net_server_tcp.eo \
         lib/ecore_con/efl_net_server_udp.eo \
         lib/ecore_con/efl_net_server_udp_client.eo \
+        lib/ecore_con/efl_net_socket_ssl.eo \
+        lib/ecore_con/efl_net_ssl_context.eo \
        lib/ecore_con/ecore_con_eet_base.eo \
        lib/ecore_con/ecore_con_eet_server_obj.eo \
        lib/ecore_con/ecore_con_eet_client_obj.eo \
@@ -34,7 +36,8 @@ ecore_con_eolian_files += \
 endif
 
 ecore_con_eolian_type_files = \
-        lib/ecore_con/efl_net_http_types.eot
+        lib/ecore_con/efl_net_http_types.eot \
+       lib/ecore_con/efl_net_ssl_types.eot
 
 
 ecore_con_eolian_c = $(ecore_con_eolian_files:%.eo=%.eo.c)
@@ -95,10 +98,24 @@ lib/ecore_con/efl_net_server.c \
 lib/ecore_con/efl_net_server_fd.c \
 lib/ecore_con/efl_net_server_tcp.c \
 lib/ecore_con/efl_net_server_udp.c \
-lib/ecore_con/efl_net_server_udp_client.c
+lib/ecore_con/efl_net_server_udp_client.c \
+lib/ecore_con/efl_net_socket_ssl.c \
+lib/ecore_con/efl_net_ssl_context.c
 
 EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c
 
+# these are included rather than compiled out
+# so the structures can be embedded into the
+# object Private Data and allows functions to
+# be all static
+EXTRA_DIST2 += \
+lib/ecore_con/efl_net_ssl_conn-openssl.c \
+lib/ecore_con/efl_net_ssl_conn-gnutls.c \
+lib/ecore_con/efl_net_ssl_conn-none.c \
+lib/ecore_con/efl_net_ssl_ctx-openssl.c \
+lib/ecore_con/efl_net_ssl_ctx-gnutls.c \
+lib/ecore_con/efl_net_ssl_ctx-none.c
+
 if HAVE_WINDOWS
 lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_local_win32.c
 else
diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore
index 0568bce..7068b7c 100644
--- a/src/examples/ecore/.gitignore
+++ b/src/examples/ecore/.gitignore
@@ -57,3 +57,5 @@
 /efl_net_dialer_udp_example
 /efl_net_dialer_unix_example
 /ecore_evas_vnc
+/efl_net_socket_ssl_dialer_example
+/efl_net_socket_ssl_server_example
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index a0eb588..7e26163 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -85,7 +85,10 @@ efl_net_server_example \
 efl_net_dialer_http_example \
 efl_net_dialer_websocket_example \
 efl_net_dialer_websocket_autobahntestee \
-efl_net_dialer_udp_example
+efl_net_dialer_udp_example \
+efl_net_socket_ssl_dialer_example \
+efl_net_socket_ssl_server_example
+
 
 ECORE_COMMON_LDADD = \
 $(top_builddir)/src/lib/ecore/libecore.la \
@@ -320,6 +323,12 @@ efl_net_dialer_unix_example_SOURCES = 
efl_net_dialer_unix_example.c
 efl_net_dialer_unix_example_LDADD = $(ECORE_CON_COMMON_LDADD)
 endif
 
+efl_net_socket_ssl_dialer_example_SOURCES = efl_net_socket_ssl_dialer_example.c
+efl_net_socket_ssl_dialer_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+
+efl_net_socket_ssl_server_example_SOURCES = efl_net_socket_ssl_server_example.c
+efl_net_socket_ssl_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+
 SRCS = \
 ecore_animator_example.c \
 ecore_buffer_example.c \
@@ -374,7 +383,9 @@ efl_net_server_example.c \
 efl_net_dialer_http_example.c \
 efl_net_dialer_websocket_example.c \
 efl_net_dialer_websocket_autobahntestee.c \
-efl_net_dialer_udp_example.c
+efl_net_dialer_udp_example.c \
+efl_net_socket_ssl_dialer_example.c \
+efl_net_socket_ssl_server_example.c
 
 DATA_FILES = red.png Makefile.examples
 
diff --git a/src/examples/ecore/efl_net_socket_ssl_dialer_example.c 
b/src/examples/ecore/efl_net_socket_ssl_dialer_example.c
new file mode 100644
index 0000000..34abd16
--- /dev/null
+++ b/src/examples/ecore/efl_net_socket_ssl_dialer_example.c
@@ -0,0 +1,512 @@
+#define EFL_BETA_API_SUPPORT 1
+#define EFL_EO_API_SUPPORT 1
+#include <Ecore.h>
+#include <Ecore_Con.h>
+#include <Ecore_Getopt.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+static int retval = EXIT_SUCCESS;
+static Eina_List *pending_send = NULL;
+static size_t pending_send_offset = 0;
+static Eo *ssl_ctx;
+
+static void
+_ssl_ready(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+   fprintf(stderr, "INFO: SSL ready!\n");
+}
+
+static void
+_ssl_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   const Eina_Error *perr = event->info;
+   fprintf(stderr, "INFO: SSL error: %d '%s'\n", *perr, 
eina_error_msg_get(*perr));
+   retval = EXIT_FAILURE;
+}
+
+static void
+_ssl_can_read(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   char buf[63]; /* INFO: SSL read '...' will fit in 80 columns */
+   Eina_Error err;
+   Eina_Bool can_read = efl_io_reader_can_read_get(event->object);
+
+   /* NOTE: this message may appear with can read=0 BEFORE
+    * "read '...'" because efl_io_readr_read() will change the status
+    * of can_read to FALSE prior to return so we can print it!
+    */
+   fprintf(stderr, "INFO: SSL can read=%d\n", can_read);
+   if (!can_read) return;
+
+   do
+     {
+        Eina_Rw_Slice rw_slice;
+
+        /* reset on every read, as rw_slice will be modified! */
+        rw_slice.len = sizeof(buf);
+        rw_slice.mem = buf;
+        err = efl_io_reader_read(event->object, &rw_slice);
+        if (err)
+          {
+             if (err == EAGAIN) return;
+             fprintf(stderr, "ERROR: could not read: %s\n", 
eina_error_msg_get(err));
+             retval = EXIT_FAILURE;
+             ecore_main_loop_quit();
+             return;
+          }
+
+        fprintf(stderr, "INFO: SSL read '" EINA_SLICE_STR_FMT "'\n", 
EINA_SLICE_STR_PRINT(rw_slice));
+     }
+   while (efl_io_reader_can_read_get(event->object));
+}
+
+static void
+_ssl_eos(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+   fprintf(stderr, "INFO: SSL eos\n");
+   ecore_main_loop_quit();
+}
+
+static void
+_ssl_can_write(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   Eina_Bool can_write = efl_io_writer_can_write_get(event->object);
+   size_t len;
+
+   /* NOTE: this message may appear with can write=0 BEFORE
+    * "wrote '...'" because efl_io_writer_write() will change the status
+    * of can_write to FALSE prior to return so we can print it!
+    */
+   fprintf(stderr, "INFO: SSL can write=%d (needed writes=%u offset=%zd)\n", 
can_write, eina_list_count(pending_send), pending_send_offset);
+   if (!can_write) return;
+
+   if (!pending_send) return;
+
+   do
+     {
+        Eina_Slice slice;
+        Eina_Error err;
+
+        slice.bytes = pending_send->data;
+        slice.bytes += pending_send_offset;
+        slice.len = len = strlen(slice.mem);
+
+        err = efl_io_writer_write(event->object, &slice, NULL);
+        if (err)
+          {
+             if (err == EAGAIN) return;
+             fprintf(stderr, "ERROR: could not write: %s\n", 
eina_error_msg_get(err));
+             retval = EXIT_FAILURE;
+             ecore_main_loop_quit();
+             return;
+          }
+
+        fprintf(stderr, "INFO: SSL wrote '" EINA_SLICE_STR_FMT "'\n", 
EINA_SLICE_STR_PRINT(slice));
+
+        pending_send_offset += slice.len;
+        if (pending_send_offset == strlen(pending_send->data))
+          {
+             free(pending_send->data);
+             pending_send = eina_list_remove_list(pending_send, pending_send);
+             pending_send_offset = 0;
+          }
+     }
+   while ((pending_send) && (efl_io_writer_can_write_get(event->object)));
+}
+
+static void
+_ssl_closed(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: SSL closed\n");
+   efl_del(event->object);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(ssl_cbs,
+                           { EFL_NET_SOCKET_SSL_EVENT_SSL_READY, _ssl_ready },
+                           { EFL_NET_SOCKET_SSL_EVENT_SSL_ERROR, _ssl_error },
+                           { EFL_IO_READER_EVENT_CAN_READ_CHANGED, 
_ssl_can_read },
+                           { EFL_IO_READER_EVENT_EOS, _ssl_eos },
+                           { EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, 
_ssl_can_write },
+                           { EFL_IO_CLOSER_EVENT_CLOSED, _ssl_closed });
+
+static void
+_connected(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   Eo *ssl;
+   const char *hostname;
+
+   fprintf(stderr,
+           "INFO: connected to '%s' (%s)\n"
+           "INFO:  - local address=%s\n",
+           efl_net_dialer_address_dial_get(event->object),
+           efl_net_socket_address_remote_get(event->object),
+           efl_net_socket_address_local_get(event->object));
+
+   ssl = efl_add(EFL_NET_SOCKET_SSL_CLASS, efl_parent_get(event->object),
+                 efl_net_socket_ssl_adopt(efl_added, event->object, ssl_ctx),
+                 efl_event_callback_array_add(efl_added, ssl_cbs(), NULL));
+   if (!ssl)
+     {
+        fprintf(stderr, "ERROR: failed to wrap dialer=%p in SSL\n", 
event->object);
+        retval = EXIT_FAILURE;
+        ecore_main_loop_quit();
+        return;
+     }
+
+   hostname = efl_net_socket_ssl_hostname_override_get(ssl);
+   if (!hostname) hostname = "<none>";
+
+   fprintf(stderr,
+           "INFO:  - verify-mode=%d\n"
+           "INFO:  - hostname-verify=%d\n"
+           "INFO:  - hostname-override='%s'\n",
+           efl_net_socket_ssl_verify_mode_get(ssl),
+           efl_net_socket_ssl_hostname_verify_get(ssl),
+           hostname);
+}
+
+static void
+_resolved(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: resolved %s => %s\n",
+           efl_net_dialer_address_dial_get(event->object),
+           efl_net_socket_address_remote_get(event->object));
+}
+
+static void
+_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   const Eina_Error *perr = event->info;
+   fprintf(stderr, "INFO: error: %d '%s'\n", *perr, eina_error_msg_get(*perr));
+   retval = EXIT_FAILURE;
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
+                           { EFL_NET_DIALER_EVENT_CONNECTED, _connected },
+                           { EFL_NET_DIALER_EVENT_RESOLVED, _resolved },
+                           { EFL_NET_DIALER_EVENT_ERROR, _error });
+
+
+static char *
+_unescape(const char *str)
+{
+   char *ret = strdup(str);
+   char *c, *w;
+   Eina_Bool escaped = EINA_FALSE;
+
+   for (c = ret, w = ret; *c != '\0'; c++)
+     {
+        if (escaped)
+          {
+             escaped = EINA_FALSE;
+             switch (*c)
+               {
+                case 'n': *w = '\n'; break;
+                case 'r': *w = '\r'; break;
+                case 't': *w = '\t'; break;
+                default: w++; /* no change */
+               }
+             w++;
+          }
+        else
+          {
+             if (*c == '\\')
+               escaped = EINA_TRUE;
+             else
+               w++;
+          }
+     }
+   *w = '\0';
+   return ret;
+}
+
+/*
+ * Define USE_DEFAULT_CONTEXT will remove all context-setup functions
+ * and use a default context for dialers, what most applications
+ * should use.
+ */
+//#define USE_DEFAULT_CONTEXT 1
+
+#ifndef USE_DEFAULT_CONTEXT
+static const char *verify_mode_strs[] = {
+  "none",
+  "optional",
+  "required",
+  NULL
+};
+
+static const char *ciphers_strs[] = {
+  "auto",
+  "sslv3",
+  "tlsv1",
+  "tlsv1.1",
+  "tlsv1.2",
+  NULL
+};
+#endif
+
+static const Ecore_Getopt options = {
+  "efl_net_socket_ssl_dialer_example", /* program name */
+  NULL, /* usage line */
+  "1", /* version */
+  "(C) 2016 Enlightenment Project", /* copyright */
+  "BSD 2-Clause", /* license */
+  /* long description, may be multiline and contain \n */
+  "Example of 'upgrading' a regular Efl.Net.Dialer.Tcp to a SSL socket.",
+  EINA_FALSE,
+  {
+    ECORE_GETOPT_STORE_STR('d', "line-delimiter",
+                           "Changes the line delimiter to be used in both send 
and receive. Defaults to \\r\\n"),
+    ECORE_GETOPT_APPEND('s', "send", "send the given string to the server once 
connected.", ECORE_GETOPT_TYPE_STR),
+
+#ifndef USE_DEFAULT_CONTEXT
+    ECORE_GETOPT_CHOICE('c', "cipher", "Cipher to use, defaults to 'auto'", 
ciphers_strs),
+    ECORE_GETOPT_APPEND(0, "certificate", "certificate path to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "private-key", "private key path to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "crl", "certificate revogation list to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "ca", "certificate authorities path to use.", 
ECORE_GETOPT_TYPE_STR),
+
+    ECORE_GETOPT_STORE_FALSE(0, "no-default-paths", "Do not use default 
certificate paths from your system."),
+    ECORE_GETOPT_CHOICE(0, "verify-mode", "One of: none (do not verify), 
optional (verify if provided), required (require and verify). Defaults to 
required", verify_mode_strs),
+    ECORE_GETOPT_STORE_FALSE(0, "no-hostname-verify", "Do not Verify 
hostname"),
+    ECORE_GETOPT_STORE_STR(0, "hostname-override", "Use this hostname instead 
of server provided one"),
+#endif
+
+    ECORE_GETOPT_VERSION('V', "version"),
+    ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+    ECORE_GETOPT_LICENSE('L', "license"),
+    ECORE_GETOPT_HELP('h', "help"),
+    ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
+                                   "The address (URL) to dial", "address"),
+    ECORE_GETOPT_SENTINEL
+  }
+};
+
+int
+main(int argc, char **argv)
+{
+   char *address = NULL;
+   char *line_delimiter_str = NULL;
+   char *str;
+   Eina_List *to_send = NULL;
+#ifndef USE_DEFAULT_CONTEXT
+   Eina_Iterator *it;
+   char *verify_mode_choice = "required";
+   char *cipher_choice = "auto";
+   Eina_List *certificates = NULL;
+   Eina_List *private_keys = NULL;
+   Eina_List *crls = NULL;
+   Eina_List *cas = NULL;
+   Eina_Bool default_paths_load = EINA_TRUE;
+   Efl_Net_Ssl_Verify_Mode verify_mode = EFL_NET_SSL_VERIFY_MODE_OPTIONAL;
+   Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO;
+   Eina_Bool hostname_verify = EINA_TRUE;
+   char *hostname_override = NULL;
+#endif
+   Eina_Bool quit_option = EINA_FALSE;
+   double timeout_dial = 30.0;
+   Ecore_Getopt_Value values[] = {
+     ECORE_GETOPT_VALUE_STR(line_delimiter_str),
+     ECORE_GETOPT_VALUE_LIST(to_send),
+
+#ifndef USE_DEFAULT_CONTEXT
+     ECORE_GETOPT_VALUE_STR(cipher_choice),
+
+     ECORE_GETOPT_VALUE_LIST(certificates),
+     ECORE_GETOPT_VALUE_LIST(private_keys),
+     ECORE_GETOPT_VALUE_LIST(crls),
+     ECORE_GETOPT_VALUE_LIST(cas),
+
+     ECORE_GETOPT_VALUE_BOOL(default_paths_load),
+     ECORE_GETOPT_VALUE_STR(verify_mode_choice),
+     ECORE_GETOPT_VALUE_BOOL(hostname_verify),
+     ECORE_GETOPT_VALUE_STR(hostname_override),
+#endif
+
+     /* standard block to provide version, copyright, license and help */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
+
+     /* positional argument */
+     ECORE_GETOPT_VALUE_STR(address),
+
+     ECORE_GETOPT_VALUE_NONE /* sentinel */
+   };
+   int args;
+   Eo *dialer, *loop;
+   Eina_Error err;
+
+   ecore_init();
+   ecore_con_init();
+
+   args = ecore_getopt_parse(&options, values, argc, argv);
+   if (args < 0)
+     {
+        fputs("ERROR: Could not parse command line options.\n", stderr);
+        retval = EXIT_FAILURE;
+        goto end;
+     }
+
+   if (quit_option) goto end;
+
+   loop = ecore_main_loop_get();
+
+   args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
+   if (args < 0)
+     {
+        fputs("ERROR: Could not parse positional arguments.\n", stderr);
+        retval = EXIT_FAILURE;
+        goto end;
+     }
+
+#ifndef USE_DEFAULT_CONTEXT
+   if (verify_mode_choice)
+     {
+        if (strcmp(verify_mode_choice, "none") == 0)
+          verify_mode = EFL_NET_SSL_VERIFY_MODE_NONE;
+        else if (strcmp(verify_mode_choice, "optional") == 0)
+          verify_mode = EFL_NET_SSL_VERIFY_MODE_OPTIONAL;
+        else if (strcmp(verify_mode_choice, "required") == 0)
+          verify_mode = EFL_NET_SSL_VERIFY_MODE_REQUIRED;
+     }
+
+   if (cipher_choice)
+     {
+        if (strcmp(cipher_choice, "auto") == 0)
+          cipher = EFL_NET_SSL_CIPHER_AUTO;
+        else if (strcmp(cipher_choice, "sslv3") == 0)
+          cipher = EFL_NET_SSL_CIPHER_SSLV3;
+        else if (strcmp(cipher_choice, "tlsv1") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1;
+        else if (strcmp(cipher_choice, "tlsv1.1") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1_1;
+        else if (strcmp(cipher_choice, "tlsv1.2") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1_2;
+     }
+#endif
+
+   if (to_send)
+     {
+        line_delimiter_str = _unescape(line_delimiter_str ? line_delimiter_str 
: "\\r\\n");
+        if (line_delimiter_str[0] == '\0')
+          {
+             pending_send = to_send;
+             to_send = NULL;
+          }
+        else
+          {
+             EINA_LIST_FREE(to_send, str)
+               {
+                  /* ignore empty sends, but add line delimiter, so we can do 
HTTP's last line :-) */
+                  if (str[0] == '\0')
+                    free(str);
+                  else
+                    pending_send = eina_list_append(pending_send, str);
+
+                  if (line_delimiter_str[0])
+                    pending_send = eina_list_append(pending_send, 
strdup(line_delimiter_str));
+               }
+          }
+        free(line_delimiter_str);
+        line_delimiter_str = NULL;
+     }
+
+   /* create a new SSL context with command line configurations.
+    * another option would be to use the default dialer context */
+#ifndef USE_DEFAULT_CONTEXT
+   ssl_ctx = efl_add(EFL_NET_SSL_CONTEXT_CLASS, NULL,
+                     efl_net_ssl_context_certificates_set(efl_added, 
eina_list_iterator_new(certificates)),
+                     efl_net_ssl_context_private_keys_set(efl_added, 
eina_list_iterator_new(private_keys)),
+                     
efl_net_ssl_context_certificate_revogation_lists_set(efl_added, 
eina_list_iterator_new(crls)),
+                     
efl_net_ssl_context_certificate_authorities_set(efl_added, 
eina_list_iterator_new(cas)),
+                     efl_net_ssl_context_default_paths_load_set(efl_added, 
default_paths_load),
+                     efl_net_ssl_context_verify_mode_set(efl_added, 
verify_mode),
+                     efl_net_ssl_context_hostname_verify_set(efl_added, 
hostname_verify),
+                     efl_net_ssl_context_hostname_set(efl_added, 
hostname_override),
+                     efl_net_ssl_context_setup(efl_added, cipher, EINA_TRUE));
+#else
+   ssl_ctx = efl_net_ssl_context_default_dialer_get(EFL_NET_SSL_CONTEXT_CLASS);
+   fprintf(stderr, "INFO: using default context for dialers.\n");
+#endif
+
+   if (!ssl_ctx)
+     {
+        fprintf(stderr, "ERROR: could not create the SSL context!\n");
+        retval = EXIT_FAILURE;
+        goto no_ssl_ctx;
+     }
+
+   /* no point in printing default context, it's all empty */
+#ifndef USE_DEFAULT_CONTEXT
+   fprintf(stderr, "INFO:  - certificates in use:\n");
+   it = efl_net_ssl_context_certificates_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - private keys in use:\n");
+   it = efl_net_ssl_context_private_keys_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - certificate revogation lists in use:\n");
+   it = efl_net_ssl_context_certificate_revogation_lists_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - certificate authorities in use:\n");
+   it = efl_net_ssl_context_certificate_authorities_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+#endif
+
+   fprintf(stderr,
+           "INFO:  - verify-mode=%d\n",
+           efl_net_ssl_context_verify_mode_get(ssl_ctx));
+
+   dialer = efl_add(EFL_NET_DIALER_TCP_CLASS, loop,
+                    efl_name_set(efl_added, "dialer"),
+                    efl_net_dialer_timeout_dial_set(efl_added, timeout_dial),
+                    efl_event_callback_array_add(efl_added, dialer_cbs(), 
NULL));
+
+   err = efl_net_dialer_dial(dialer, address);
+   if (err != 0)
+     {
+        fprintf(stderr, "ERROR: could not dial '%s': %s",
+                address, eina_error_msg_get(err));
+        retval = EXIT_FAILURE;
+        goto no_mainloop;
+     }
+
+   ecore_main_loop_begin();
+
+   fprintf(stderr, "INFO: main loop finished.\n");
+
+ no_mainloop:
+   efl_io_closer_close(dialer); /* just del won't do as ssl has an extra ref */
+   efl_del(dialer);
+ no_ssl_ctx:
+   efl_del(ssl_ctx);
+
+ end:
+   EINA_LIST_FREE(pending_send, str) free(str);
+
+#ifndef USE_DEFAULT_CONTEXT
+   EINA_LIST_FREE(certificates, str) free(str);
+   EINA_LIST_FREE(private_keys, str) free(str);
+   EINA_LIST_FREE(crls, str) free(str);
+   EINA_LIST_FREE(cas, str) free(str);
+#endif
+
+   ecore_con_shutdown();
+   ecore_shutdown();
+
+   return retval;
+}
diff --git a/src/examples/ecore/efl_net_socket_ssl_server_example.c 
b/src/examples/ecore/efl_net_socket_ssl_server_example.c
new file mode 100644
index 0000000..c87fec3
--- /dev/null
+++ b/src/examples/ecore/efl_net_socket_ssl_server_example.c
@@ -0,0 +1,351 @@
+#define EFL_BETA_API_SUPPORT 1
+#define EFL_EO_API_SUPPORT 1
+#include <Ecore.h>
+#include <Ecore_Con.h>
+#include <Ecore_Getopt.h>
+#include <fcntl.h>
+
+static int retval = EXIT_SUCCESS;
+static double timeout = 30.0;
+static Eo *ssl_ctx = NULL;
+
+/* NOTE: client i/o events are only used as debug, you can omit these */
+
+static void
+_ssl_can_read_changed(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: ssl %s can_read=%d\n",
+           efl_net_socket_address_remote_get(event->object),
+           efl_io_reader_can_read_get(event->object));
+}
+
+static void
+_ssl_can_write_changed(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: ssl %s can_write=%d\n",
+           efl_net_socket_address_remote_get(event->object),
+           efl_io_writer_can_write_get(event->object));
+}
+
+static void
+_ssl_eos(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: ssl %s eos.\n",
+           efl_net_socket_address_remote_get(event->object));
+}
+
+static void
+_ssl_closed(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: ssl %s closed.\n",
+           efl_net_socket_address_remote_get(event->object));
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(ssl_cbs,
+                           { EFL_IO_READER_EVENT_CAN_READ_CHANGED, 
_ssl_can_read_changed },
+                           { EFL_IO_READER_EVENT_EOS, _ssl_eos },
+                           { EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, 
_ssl_can_write_changed },
+                           { EFL_IO_CLOSER_EVENT_CLOSED, _ssl_closed });
+
+
+/* copier events are of interest, you should hook to at least "done"
+ * and "error"
+ */
+
+/* echo copier is about the same socket, you can close it right away */
+
+static void
+_echo_copier_done(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   Eo *copier = event->object;
+   fprintf(stderr, "INFO: echo copier done, close and del %p\n", copier);
+   efl_del(copier); /* set to close_on_destructor, will auto close copier and 
ssl */
+}
+
+static void
+_echo_copier_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   Eo *copier = event->object;
+   const Eina_Error *perr = event->info;
+
+   if (*perr == ETIMEDOUT)
+     {
+        Eo *ssl = efl_io_copier_source_get(copier);
+        fprintf(stderr, "INFO: ssl '%s' timed out, delete it.\n",
+                efl_net_socket_address_remote_get(ssl));
+        efl_del(copier);
+        return;
+     }
+
+   retval = EXIT_FAILURE;
+
+   fprintf(stderr, "ERROR: echo copier %p failed %d '%s', close and del.\n",
+           copier, *perr, eina_error_msg_get(*perr));
+
+   efl_del(copier);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(echo_copier_cbs,
+                           { EFL_IO_COPIER_EVENT_DONE, _echo_copier_done },
+                           { EFL_IO_COPIER_EVENT_ERROR, _echo_copier_error});
+
+/* server events are mandatory, afterall you need to define what's
+ * going to happen after a client socket is connected. This is the
+ * "client,add" event.
+ */
+static void
+_server_client_add(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   Efl_Net_Socket *client = event->info;
+   Efl_Net_Socket_Ssl *ssl;
+   Eo *echo_copier;
+
+   fprintf(stderr, "INFO: accepted client %s\n",
+           efl_net_socket_address_remote_get(client));
+
+   /* to use a client, you must efl_ref() it. Here we're not doing it
+    * explicitly because Efl.Net.Socket.Ssl do take a reference.
+    */
+   ssl = efl_add(EFL_NET_SOCKET_SSL_CLASS, efl_loop_get(client),
+                 efl_net_socket_ssl_adopt(efl_added, client, ssl_ctx), /* 
mandatory inside efl_add() */
+                 efl_event_callback_array_add(efl_added, ssl_cbs(), NULL) /* 
optional, for debug purposes */
+                 );
+   if (!ssl)
+     {
+        fprintf(stderr, "ERROR: failed to wrap client=%p in SSL\n", client);
+        retval = EXIT_FAILURE;
+        ecore_main_loop_quit();
+        return;
+     }
+
+   /*
+    * SSL will do a handshake and keep can_read/can_write as false
+    * until it's finished, thus we can create the echo copier right
+    * away.
+    *
+    * remember to NEVER add a copier, read or write from wrapped
+    * socket, doing that will bypass the SSL layer and thus result in
+    * incorrect operation. You can forget about it
+    */
+
+   echo_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(ssl),
+                         efl_io_copier_source_set(efl_added, ssl),
+                         efl_io_copier_destination_set(efl_added, ssl),
+                         efl_io_copier_inactivity_timeout_set(efl_added, 
timeout),
+                         efl_event_callback_array_add(efl_added, 
echo_copier_cbs(), ssl),
+                         efl_io_closer_close_on_destructor_set(efl_added, 
EINA_TRUE) /* we want to auto-close as we have a single copier */
+                         );
+
+   fprintf(stderr, "INFO: using an echo copier=%p for ssl %s\n",
+           echo_copier, efl_net_socket_address_remote_get(ssl));
+}
+
+static void
+_server_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   const Eina_Error *perr = event->info;
+   fprintf(stderr, "ERROR: %d '%s'\n", *perr, eina_error_msg_get(*perr));
+   retval = EXIT_FAILURE;
+   ecore_main_loop_quit();
+}
+
+static void
+_server_serving(void *data EINA_UNUSED, const Efl_Event *event)
+{
+   fprintf(stderr, "INFO: serving at %s\n",
+           efl_net_server_address_get(event->object));
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(server_cbs,
+                           { EFL_NET_SERVER_EVENT_CLIENT_ADD, 
_server_client_add },
+                           { EFL_NET_SERVER_EVENT_ERROR, _server_error },
+                           { EFL_NET_SERVER_EVENT_SERVING, _server_serving });
+
+static const char *ciphers_strs[] = {
+  "auto",
+  "sslv3",
+  "tlsv1",
+  "tlsv1.1",
+  "tlsv1.2",
+  NULL
+};
+
+static const Ecore_Getopt options = {
+  "efl_net_socket_ssl_server_example", /* program name */
+  NULL, /* usage line */
+  "1", /* version */
+  "(C) 2016 Enlightenment Project", /* copyright */
+  "BSD 2-Clause", /* license */
+  /* long description, may be multiline and contain \n */
+  "Example of 'upgrading' a regular Efl.Net.Socket received from an 
Efl.Net.Server.Tcp to a SSL socket, then serving as 'echo' server.",
+  EINA_FALSE,
+  {
+    ECORE_GETOPT_CHOICE('c', "cipher", "Cipher to use, defaults to 'auto'", 
ciphers_strs),
+
+    ECORE_GETOPT_APPEND(0, "certificate", "certificate path to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "private-key", "private key path to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "crl", "certificate revogation list to use.", 
ECORE_GETOPT_TYPE_STR),
+    ECORE_GETOPT_APPEND(0, "ca", "certificate authorities path to use.", 
ECORE_GETOPT_TYPE_STR),
+
+    ECORE_GETOPT_VERSION('V', "version"),
+    ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+    ECORE_GETOPT_LICENSE('L', "license"),
+    ECORE_GETOPT_HELP('h', "help"),
+
+    ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
+                                   "The server address to listen, such as "
+                                   "IPv4:PORT, [IPv6]:PORT, Unix socket 
path...",
+                                   "address"),
+
+    ECORE_GETOPT_SENTINEL
+  }
+};
+
+int
+main(int argc, char **argv)
+{
+   char *address = NULL;
+   char *cipher_choice = "auto";
+   char *str;
+   Eina_List *certificates = NULL;
+   Eina_List *private_keys = NULL;
+   Eina_List *crls = NULL;
+   Eina_List *cas = NULL;
+   Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO;
+   Eina_Bool quit_option = EINA_FALSE;
+   Ecore_Getopt_Value values[] = {
+     ECORE_GETOPT_VALUE_STR(cipher_choice),
+
+     ECORE_GETOPT_VALUE_LIST(certificates),
+     ECORE_GETOPT_VALUE_LIST(private_keys),
+     ECORE_GETOPT_VALUE_LIST(crls),
+     ECORE_GETOPT_VALUE_LIST(cas),
+
+     /* standard block to provide version, copyright, license and help */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
+     ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
+
+     /* positional argument */
+     ECORE_GETOPT_VALUE_STR(address),
+
+     ECORE_GETOPT_VALUE_NONE /* sentinel */
+   };
+   int args;
+   Eina_Iterator *it;
+   Eo *server;
+   Eina_Error err;
+
+   ecore_init();
+   ecore_con_init();
+
+   args = ecore_getopt_parse(&options, values, argc, argv);
+   if (args < 0)
+     {
+        fputs("ERROR: Could not parse command line options.\n", stderr);
+        retval = EXIT_FAILURE;
+        goto end;
+     }
+
+   if (quit_option) goto end;
+
+   args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
+   if (args < 0)
+     {
+        fputs("ERROR: Could not parse positional arguments.\n", stderr);
+        retval = EXIT_FAILURE;
+        goto end;
+     }
+
+   if (cipher_choice)
+     {
+        if (strcmp(cipher_choice, "auto") == 0)
+          cipher = EFL_NET_SSL_CIPHER_AUTO;
+        else if (strcmp(cipher_choice, "sslv3") == 0)
+          cipher = EFL_NET_SSL_CIPHER_SSLV3;
+        else if (strcmp(cipher_choice, "tlsv1") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1;
+        else if (strcmp(cipher_choice, "tlsv1.1") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1_1;
+        else if (strcmp(cipher_choice, "tlsv1.2") == 0)
+          cipher = EFL_NET_SSL_CIPHER_TLSV1_2;
+     }
+
+   ssl_ctx = efl_add(EFL_NET_SSL_CONTEXT_CLASS, NULL,
+                     efl_net_ssl_context_certificates_set(efl_added, 
eina_list_iterator_new(certificates)),
+                     efl_net_ssl_context_private_keys_set(efl_added, 
eina_list_iterator_new(private_keys)),
+                     
efl_net_ssl_context_certificate_revogation_lists_set(efl_added, 
eina_list_iterator_new(crls)),
+                     
efl_net_ssl_context_certificate_authorities_set(efl_added, 
eina_list_iterator_new(cas)),
+                     efl_net_ssl_context_setup(efl_added, cipher, EINA_FALSE 
/* a server! */));
+
+   if (!ssl_ctx)
+     {
+        fprintf(stderr, "ERROR: could not create the SSL context!\n");
+        retval = EXIT_FAILURE;
+        goto end;
+     }
+
+   fprintf(stderr, "INFO:  - certificates in use:\n");
+   it = efl_net_ssl_context_certificates_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - private keys in use:\n");
+   it = efl_net_ssl_context_private_keys_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - certificate revogation lists in use:\n");
+   it = efl_net_ssl_context_certificate_revogation_lists_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   fprintf(stderr, "INFO:  - certificate authorities in use:\n");
+   it = efl_net_ssl_context_certificate_authorities_get(ssl_ctx);
+   EINA_ITERATOR_FOREACH(it, str)
+     fprintf(stderr, "INFO:     * %s\n", str);
+   eina_iterator_free(it);
+
+   server = efl_add(EFL_NET_SERVER_TCP_CLASS, ecore_main_loop_get(), /* it's 
mandatory to use a main loop provider as the server parent */
+                    efl_net_server_tcp_ipv6_only_set(efl_added, EINA_FALSE), 
/* optional, but helps testing IPv4 on IPv6 servers */
+                    efl_net_server_fd_close_on_exec_set(efl_added, EINA_TRUE), 
/* recommended */
+                    efl_net_server_fd_reuse_address_set(efl_added, EINA_TRUE), 
/* optional, but nice for testing */
+                    efl_net_server_fd_reuse_port_set(efl_added, EINA_TRUE), /* 
optional, but nice for testing... not secure unless you know what you're doing 
*/
+                    efl_event_callback_array_add(efl_added, server_cbs(), 
NULL)); /* mandatory to have "client,add" in order to be useful */
+   if (!server)
+     {
+        fprintf(stderr, "ERROR: could not create class Efl.Net.Server.Tcp\n");
+        goto end_ctx;
+     }
+
+   err = efl_net_server_serve(server, address);
+   if (err)
+     {
+        fprintf(stderr, "ERROR: could not serve(%s): %s\n",
+                address, eina_error_msg_get(err));
+        goto end_server;
+     }
+
+   ecore_main_loop_begin();
+
+ end_server:
+   efl_del(server);
+   server = NULL;
+ end_ctx:
+   efl_del(ssl_ctx);
+
+ end:
+   EINA_LIST_FREE(certificates, str) free(str);
+   EINA_LIST_FREE(private_keys, str) free(str);
+   EINA_LIST_FREE(crls, str) free(str);
+   EINA_LIST_FREE(cas, str) free(str);
+
+   ecore_con_shutdown();
+   ecore_shutdown();
+
+   return retval;
+}
diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h
index b3e3bf4..8e4cf65 100644
--- a/src/lib/ecore_con/Ecore_Con_Eo.h
+++ b/src/lib/ecore_con/Ecore_Con_Eo.h
@@ -31,3 +31,9 @@
 
 #include "efl_net_dialer_http.eo.h"
 #include "efl_net_dialer_websocket.eo.h"
+
+
+#include "efl_net_ssl_types.eot.h"
+
+#include "efl_net_ssl_context.eo.h"
+#include "efl_net_socket_ssl.eo.h"
diff --git a/src/lib/ecore_con/ecore_con.c b/src/lib/ecore_con/ecore_con.c
index 6f401a0..be0c1a8 100644
--- a/src/lib/ecore_con/ecore_con.c
+++ b/src/lib/ecore_con/ecore_con.c
@@ -184,6 +184,9 @@ EWAPI Eina_Error 
EFL_NET_DIALER_ERROR_PROXY_AUTHENTICATION_FAILED = 0;
 
 EWAPI Eina_Error EFL_NET_SERVER_ERROR_COULDNT_RESOLVE_HOST = 0;
 
+EWAPI Eina_Error EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE = 0;
+EWAPI Eina_Error EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED = 0;
+
 static Eina_List *servers = NULL;
 static int _ecore_con_init_count = 0;
 static int _ecore_con_event_count = 0;
@@ -241,6 +244,9 @@ ecore_con_init(void)
 
    EFL_NET_SERVER_ERROR_COULDNT_RESOLVE_HOST = 
eina_error_msg_static_register("Couldn't resolve host name");
 
+   EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE = eina_error_msg_static_register("Failed 
SSL handshake");
+   EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED = 
eina_error_msg_static_register("Failed to verify peer's certificate");
+
    eina_magic_string_set(ECORE_MAGIC_CON_SERVER, "Ecore_Con_Server");
    eina_magic_string_set(ECORE_MAGIC_CON_CLIENT, "Ecore_Con_Client");
    eina_magic_string_set(ECORE_MAGIC_CON_URL, "Ecore_Con_Url");
diff --git a/src/lib/ecore_con/ecore_con_private.h 
b/src/lib/ecore_con/ecore_con_private.h
index 2063932..ec2fc21 100644
--- a/src/lib/ecore_con/ecore_con_private.h
+++ b/src/lib/ecore_con/ecore_con_private.h
@@ -674,4 +674,8 @@ Eina_Error efl_net_multicast_loopback_get(SOCKET fd, int 
family, Eina_Bool *loop
  */
 size_t efl_net_udp_datagram_size_query(SOCKET fd);
 
+
+/* SSL abstraction API */
+extern void *efl_net_ssl_context_connection_new(Efl_Net_Ssl_Context *context);
+
 #endif
diff --git a/src/lib/ecore_con/efl_net_socket_ssl.c 
b/src/lib/ecore_con/efl_net_socket_ssl.c
new file mode 100644
index 0000000..eb27888
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_ssl.c
@@ -0,0 +1,611 @@
+#define EFL_NET_SOCKET_SSL_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+typedef struct _Efl_Net_Ssl_Conn Efl_Net_Ssl_Conn;
+
+/**
+ * Setups the SSL context
+ *
+ * Update the given lists, removing invalid entries. If all entries
+ * failed in a list, return EINVAL.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_setup(Efl_Net_Ssl_Conn *conn, Eina_Bool 
is_dialer, Efl_Net_Socket *sock, Efl_Net_Ssl_Context *context);
+
+/**
+ * Cleans up the SSL associated to this context.
+ * @internal
+ */
+static void efl_net_ssl_conn_teardown(Efl_Net_Ssl_Conn *conn);
+
+/**
+ * Send data to remote peer.
+ *
+ * This should be called once handshake is finished, otherwise it may
+ * lead the handshake to fail.
+ *
+ * @param slice[inout] takes the amount of bytes to write and
+ *        source memory, will store written length in slice->len.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_write(Efl_Net_Ssl_Conn *conn, Eina_Slice 
*slice);
+
+/**
+ * Receive data from remote peer.
+ *
+ * This should be called once handshake is finished, otherwise it may
+ * lead the handshake to fail.
+ *
+ * Note that even if the socket 'can_read', eventually it couldn't
+ * decipher a byte and it will return slice->len == 0 with EAGAIN as
+ * error.
+ *
+ * @param slice[inout] takes the amount of bytes to read and
+ *        destination memory, will store read length in slice->len.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_read(Efl_Net_Ssl_Conn *conn, Eina_Rw_Slice 
*slice);
+
+/**
+ * Attempt to finish the handshake.
+ *
+ * This should not block, if it's not finished yet, just set done =
+ * false.
+ *
+ * Errors, such as failed handshake, should be returned as Eina_Error.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_handshake(Efl_Net_Ssl_Conn *conn, Eina_Bool 
*done);
+
+/**
+ * Configure how to verify peer.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_verify_mode_set(Efl_Net_Ssl_Conn *conn, 
Efl_Net_Ssl_Verify_Mode verify_mode);
+
+/**
+ * Configure whenever to check for hostname.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_hostname_verify_set(Efl_Net_Ssl_Conn *conn, 
Eina_Bool hostname_verify);
+
+/**
+ * Overrides the hostname to use.
+ *
+ * @note duplicate hostname if needed!
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_conn_hostname_override_set(Efl_Net_Ssl_Conn 
*conn, const char *hostname);
+
+#if HAVE_OPENSSL
+#include "efl_net_ssl_conn-openssl.c"
+#elif HAVE_GNUTLS
+#include "efl_net_ssl_conn-gnutls.c"
+#else
+#include "efl_net_ssl_conn-none.c"
+#endif
+
+#define MY_CLASS EFL_NET_SOCKET_SSL_CLASS
+
+typedef struct _Efl_Net_Socket_Ssl_Data
+{
+   Eo *sock;
+   Efl_Net_Ssl_Context *context;
+   const char *hostname_override;
+   Efl_Net_Ssl_Conn ssl_conn;
+   Efl_Net_Ssl_Verify_Mode verify_mode;
+   Eina_Bool hostname_verify;
+   Eina_Bool did_handshake;
+   Eina_Bool can_read;
+   Eina_Bool eos;
+   Eina_Bool can_write;
+} Efl_Net_Socket_Ssl_Data;
+
+static void
+efl_net_socket_ssl_sock_eos(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *o = data;
+   efl_io_reader_eos_set(o, EINA_TRUE);
+}
+
+static void
+efl_net_socket_ssl_handshake_try(Eo *o, Efl_Net_Socket_Ssl_Data *pd)
+{
+   Eina_Error err;
+
+   DBG("SSL=%p handshake...", o);
+
+   err = efl_net_ssl_conn_handshake(&pd->ssl_conn, &pd->did_handshake);
+   if (err)
+     {
+        WRN("SSL=%p failed handshake: %s", o, eina_error_msg_get(err));
+        efl_event_callback_call(o, EFL_NET_SOCKET_SSL_EVENT_SSL_ERROR, &err);
+        efl_io_closer_close(o);
+        return;
+     }
+   if (!pd->did_handshake) return;
+
+   DBG("SSL=%p finished handshake", o);
+   efl_io_reader_can_read_set(o, efl_io_reader_can_read_get(pd->sock));
+   efl_io_writer_can_write_set(o, efl_io_writer_can_write_get(pd->sock));
+
+   efl_event_callback_call(o, EFL_NET_SOCKET_SSL_EVENT_SSL_READY, NULL);
+}
+
+static void
+efl_net_socket_ssl_sock_can_read_changed(void *data, const Efl_Event *event 
EINA_UNUSED)
+{
+   Eo *o = data;
+   Efl_Net_Socket_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+   efl_ref(o); /* we're emitting callbacks then continuing the workflow */
+
+   if (!efl_io_reader_can_read_get(pd->sock))
+     {
+        // TODO: stop jobs?
+        goto end;
+     }
+
+   if (pd->did_handshake)
+     efl_io_reader_can_read_set(o, EINA_TRUE);
+   else
+     efl_net_socket_ssl_handshake_try(o, pd);
+
+ end:
+   efl_unref(o);
+}
+
+static void
+efl_net_socket_ssl_sock_can_write_changed(void *data, const Efl_Event *event 
EINA_UNUSED)
+{
+   Eo *o = data;
+   Efl_Net_Socket_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+   efl_ref(o); /* we're emitting callbacks then continuing the workflow */
+
+   if (!efl_io_writer_can_write_get(pd->sock))
+     {
+        // TODO: stop jobs?
+        goto end;
+     }
+
+   if (pd->did_handshake)
+     efl_io_writer_can_write_set(o, EINA_TRUE);
+   else
+     efl_net_socket_ssl_handshake_try(o, pd);
+
+ end:
+   efl_unref(o);
+}
+
+static void
+efl_net_socket_ssl_sock_closed(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *o = data;
+   efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
+}
+
+static void
+efl_net_socket_ssl_sock_del(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *o = data;
+   Efl_Net_Socket_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS);
+   pd->sock = NULL;
+   efl_net_ssl_conn_teardown(&pd->ssl_conn);
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(efl_net_socket_ssl_sock_cbs,
+                           {EFL_IO_READER_EVENT_EOS, 
efl_net_socket_ssl_sock_eos},
+                           {EFL_IO_READER_EVENT_CAN_READ_CHANGED, 
efl_net_socket_ssl_sock_can_read_changed},
+                           {EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, 
efl_net_socket_ssl_sock_can_write_changed},
+                           {EFL_IO_CLOSER_EVENT_CLOSED, 
efl_net_socket_ssl_sock_closed},
+                           {EFL_EVENT_DEL, efl_net_socket_ssl_sock_del});
+
+static void
+efl_net_socket_ssl_sock_connected(void *data, const Efl_Event *event 
EINA_UNUSED)
+{
+   Eo *o = data;
+   Efl_Net_Socket_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS);
+   Eina_Error err;
+
+
+   efl_ref(o); /* we're emitting callbacks then continuing the workflow */
+
+   err = efl_net_ssl_conn_handshake(&pd->ssl_conn, &pd->did_handshake);
+   if (err)
+     {
+        WRN("SSL=%p failed handshake: %s", o, eina_error_msg_get(err));
+        efl_io_closer_close(o);
+        return;
+     }
+
+   efl_unref(o);
+}
+
+static void
+_efl_net_socket_ssl_context_del(void *data, const Efl_Event *event EINA_UNUSED)
+{
+   Eo *o = data;
+   Efl_Net_Socket_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS);
+   pd->context = NULL;
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_adopt(Eo *o, Efl_Net_Socket_Ssl_Data *pd, Efl_Net_Socket 
*sock, Efl_Net_Ssl_Context *context)
+{
+   Eina_Error err;
+   char *tmp = NULL;
+   const char *hostname;
+   Eina_Bool is_dialer;
+
+   EINA_SAFETY_ON_TRUE_RETURN(pd->sock != NULL);
+   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(sock, EFL_NET_SOCKET_INTERFACE));
+   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(context, EFL_NET_SSL_CONTEXT_CLASS));
+   EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o));
+
+   is_dialer = efl_isa(sock, EFL_NET_DIALER_INTERFACE);
+   err = efl_net_ssl_conn_setup(&pd->ssl_conn, is_dialer, sock, context);
+   if (err)
+     {
+        ERR("ssl=%p failed to adopt socket (is_dialer=%d) sock=%p", o, 
is_dialer, sock);
+        return;
+     }
+
+   pd->context = efl_ref(context);
+   efl_event_callback_add(context, EFL_EVENT_DEL, 
_efl_net_socket_ssl_context_del, o);
+
+   DBG("ssl=%p adopted socket (is_dialer=%d) sock=%p, ssl_conn=%p", o, 
is_dialer, sock, &pd->ssl_conn);
+
+   if (pd->hostname_verify == 0xff)
+     pd->hostname_verify = efl_net_ssl_context_hostname_verify_get(context);
+
+   hostname = pd->hostname_override;
+   if (!hostname)
+     hostname = pd->hostname_override = 
eina_stringshare_ref(efl_net_ssl_context_hostname_get(context));
+   if (!hostname)
+     {
+        const char *remote_address = (is_dialer ?
+                                      efl_net_dialer_address_dial_get(sock) :
+                                      efl_net_socket_address_remote_get(sock));
+        if (remote_address)
+          {
+             const char *host, *port;
+
+             tmp = strdup(remote_address);
+             EINA_SAFETY_ON_NULL_RETURN(tmp);
+             if (efl_net_ip_port_split(tmp, &host, &port))
+               hostname = host;
+          }
+     }
+
+   if ((uint8_t)pd->verify_mode == 0xff) pd->verify_mode = 
efl_net_ssl_context_verify_mode_get(context);
+   efl_net_ssl_conn_verify_mode_set(&pd->ssl_conn, pd->verify_mode);
+   efl_net_ssl_conn_hostname_verify_set(&pd->ssl_conn, pd->hostname_verify);
+   efl_net_ssl_conn_hostname_override_set(&pd->ssl_conn, hostname);
+   free(tmp);
+
+   pd->sock = efl_ref(sock);
+   efl_event_callback_array_add(sock, efl_net_socket_ssl_sock_cbs(), o);
+
+   if (efl_isa(sock, EFL_NET_DIALER_INTERFACE))
+     efl_event_callback_add(sock, EFL_NET_DIALER_EVENT_CONNECTED, 
efl_net_socket_ssl_sock_connected, o);
+
+   efl_net_socket_ssl_sock_can_read_changed(o, NULL);
+   efl_net_socket_ssl_sock_can_write_changed(o, NULL);
+   if (efl_io_closer_closed_get(sock))
+     efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
+}
+
+static Efl_Net_Ssl_Verify_Mode
+_efl_net_socket_ssl_verify_mode_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Ssl_Data 
*pd)
+{
+   return pd->verify_mode;
+}
+
+static void
+_efl_net_socket_ssl_verify_mode_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Ssl_Data 
*pd, Efl_Net_Ssl_Verify_Mode verify_mode)
+{
+   pd->verify_mode = verify_mode;
+   if (!efl_finalized_get(o)) return;
+
+   efl_net_ssl_conn_verify_mode_set(&pd->ssl_conn, pd->verify_mode);
+}
+
+static Eina_Bool
+_efl_net_socket_ssl_hostname_verify_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->hostname_verify;
+}
+
+static void
+_efl_net_socket_ssl_hostname_verify_set(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd, Eina_Bool hostname_verify)
+{
+   pd->hostname_verify = hostname_verify;
+   if (!efl_finalized_get(o)) return;
+
+   efl_net_ssl_conn_hostname_verify_set(&pd->ssl_conn, pd->hostname_verify);
+}
+
+static const char *
+_efl_net_socket_ssl_hostname_override_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->hostname_override;
+}
+
+static void
+_efl_net_socket_ssl_hostname_override_set(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd, const char* hostname_override)
+{
+   char *tmp = NULL;
+   const char *hostname;
+
+   eina_stringshare_replace(&pd->hostname_override, hostname_override);
+   hostname = pd->hostname_override;
+   if (!hostname)
+     {
+        const char *remote_address = (efl_isa(pd->sock, 
EFL_NET_DIALER_INTERFACE) ?
+                                      
efl_net_dialer_address_dial_get(pd->sock) :
+                                      
efl_net_socket_address_remote_get(pd->sock));
+        if (remote_address)
+          {
+             const char *host, *port;
+
+             tmp = strdup(remote_address);
+             EINA_SAFETY_ON_NULL_RETURN(tmp);
+             if (efl_net_ip_port_split(tmp, &host, &port))
+               hostname = host;
+          }
+     }
+
+   efl_net_ssl_conn_hostname_override_set(&pd->ssl_conn, hostname);
+   free(tmp);
+}
+
+EOLIAN static Efl_Object *
+_efl_net_socket_ssl_efl_object_finalize(Eo *o, Efl_Net_Socket_Ssl_Data *pd 
EINA_UNUSED)
+{
+   Eina_Error err;
+
+   o = efl_finalize(efl_super(o, MY_CLASS));
+   if (!o) return NULL;
+
+   if (!pd->sock)
+     {
+        ERR("no Efl.Net.Socket was adopted by this SSL=%p", o);
+        return NULL;
+     }
+
+   if (efl_isa(pd->sock, EFL_NET_DIALER_INTERFACE))
+     {
+        if (!efl_net_dialer_connected_get(pd->sock))
+          return o;
+     }
+
+   err = efl_net_ssl_conn_handshake(&pd->ssl_conn, &pd->did_handshake);
+   if (err)
+     {
+        WRN("SSL=%p failed handshake", o);
+        return NULL;
+     }
+
+   return o;
+}
+
+EOLIAN static Eo *
+_efl_net_socket_ssl_efl_object_constructor(Eo *o, Efl_Net_Socket_Ssl_Data *pd)
+{
+   pd->hostname_verify = 0xff;
+   pd->verify_mode = 0xff;
+   return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_efl_object_destructor(Eo *o, Efl_Net_Socket_Ssl_Data *pd)
+{
+   if (efl_io_closer_close_on_destructor_get(o) &&
+       (!efl_io_closer_closed_get(o)))
+     efl_io_closer_close(o);
+
+   efl_destructor(efl_super(o, MY_CLASS));
+
+   efl_net_ssl_conn_teardown(&pd->ssl_conn);
+   if (pd->sock)
+     {
+        efl_event_callback_array_del(pd->sock, efl_net_socket_ssl_sock_cbs(), 
o);
+        if (efl_isa(pd->sock, EFL_NET_DIALER_INTERFACE))
+          {
+             efl_event_callback_del(pd->sock, EFL_NET_DIALER_EVENT_CONNECTED, 
efl_net_socket_ssl_sock_connected, o);
+          }
+        efl_unref(pd->sock);
+        pd->sock = NULL;
+     }
+
+   if (pd->context)
+     {
+        efl_event_callback_del(pd->context, EFL_EVENT_DEL, 
_efl_net_socket_ssl_context_del, o);
+        efl_unref(pd->context);
+        pd->context = NULL;
+     }
+
+   eina_stringshare_replace(&pd->hostname_override, NULL);
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_ssl_efl_io_closer_close(Eo *o, Efl_Net_Socket_Ssl_Data *pd)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pd->sock, EBADF);
+
+   efl_io_reader_can_read_set(o, EINA_FALSE);
+   efl_io_reader_eos_set(o, EINA_TRUE);
+   efl_net_ssl_conn_teardown(&pd->ssl_conn);
+   if (efl_io_closer_closed_get(pd->sock))
+     return 0;
+   return efl_io_closer_close(pd->sock);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_closer_closed_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return (!pd->sock) || efl_io_closer_closed_get(pd->sock);
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_ssl_efl_io_reader_read(Eo *o, Efl_Net_Socket_Ssl_Data *pd, 
Eina_Rw_Slice *rw_slice)
+{
+   Eina_Error err;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
+
+   if (!pd->did_handshake)
+     {
+        rw_slice->mem = NULL;
+        rw_slice->len = 0;
+        return EAGAIN;
+     }
+
+   err = efl_net_ssl_conn_read(&pd->ssl_conn, rw_slice);
+
+   if (rw_slice->len == 0)
+     {
+        efl_io_reader_can_read_set(o, EINA_FALSE);
+        if (err == 0)
+          efl_io_reader_eos_set(o, EINA_TRUE);
+     }
+
+   return err;
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_efl_io_reader_can_read_set(Eo *o, Efl_Net_Socket_Ssl_Data 
*pd, Eina_Bool can_read)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->sock);
+   if (pd->can_read == can_read) return;
+   pd->can_read = can_read;
+   efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->can_read;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_reader_eos_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->eos;
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_efl_io_reader_eos_set(Eo *o, Efl_Net_Socket_Ssl_Data *pd, 
Eina_Bool is_eos)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->sock);
+   if (pd->eos == is_eos) return;
+   pd->eos = is_eos;
+   if (is_eos)
+     efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_ssl_efl_io_writer_write(Eo *o, Efl_Net_Socket_Ssl_Data *pd, 
Eina_Slice *ro_slice, Eina_Slice *remaining)
+{
+   Eina_Error err;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ro_slice, EINVAL);
+
+   if (!pd->did_handshake)
+     {
+        if (remaining) *remaining = *ro_slice;
+        ro_slice->mem = NULL;
+        ro_slice->len = 0;
+        return EAGAIN;
+     }
+
+   if (remaining) *remaining = *ro_slice;
+
+   err = efl_net_ssl_conn_write(&pd->ssl_conn, ro_slice);
+
+   if (remaining)
+     {
+        remaining->bytes += ro_slice->len;
+        remaining->len -= ro_slice->len;
+     }
+
+   if (ro_slice->len == 0)
+     efl_io_writer_can_write_set(o, EINA_FALSE);
+
+   return err;
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_efl_io_writer_can_write_set(Eo *o, Efl_Net_Socket_Ssl_Data 
*pd, Eina_Bool can_write)
+{
+   EINA_SAFETY_ON_NULL_RETURN(pd->sock);
+   if (pd->can_write == can_write) return;
+   pd->can_write = can_write;
+   efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->can_write;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd, Eina_Bool close_on_exec)
+{
+   if (pd->sock) efl_io_closer_close_on_exec_set(pd->sock, close_on_exec);
+   return EINA_TRUE;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_closer_close_on_exec_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->sock && efl_io_closer_close_on_exec_get(pd->sock);
+}
+
+EOLIAN static void
+_efl_net_socket_ssl_efl_io_closer_close_on_destructor_set(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd, Eina_Bool close_on_destructor)
+{
+   if (pd->sock) efl_io_closer_close_on_destructor_set(pd->sock, 
close_on_destructor);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_ssl_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   return pd->sock && efl_io_closer_close_on_destructor_get(pd->sock);
+}
+
+EOLIAN static const char *
+_efl_net_socket_ssl_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   if (!pd->sock) return "unbound";
+   return efl_net_socket_address_local_get(pd->sock);
+}
+
+EOLIAN static const char *
+_efl_net_socket_ssl_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, 
Efl_Net_Socket_Ssl_Data *pd)
+{
+   if (!pd->sock) return "unbound";
+   return efl_net_socket_address_remote_get(pd->sock);
+}
+
+#include "efl_net_socket_ssl.eo.c"
diff --git a/src/lib/ecore_con/efl_net_socket_ssl.eo 
b/src/lib/ecore_con/efl_net_socket_ssl.eo
new file mode 100644
index 0000000..0dd60e1
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_ssl.eo
@@ -0,0 +1,90 @@
+var Efl.Net.Socket.Ssl.Error.HANDSHAKE: Eina.Error; [[Failed SSL handshake]]
+var Efl.Net.Socket.Ssl.Error.CERTIFICATE_VERIFY_FAILED: Eina.Error; [[Failed 
to verify peer's certificate]]
+
+class Efl.Net.Socket.Ssl (Efl.Object, Efl.Net.Socket) {
+    [[A wrapper socket doing SSL (Secure Sockets Layer).
+
+      Use this wrapper around an existing socket to do secure
+      communication, a common use is to apply it to TCP sockets
+      created with @Efl.Net.Dialer.Tcp or @Efl.Net.Server.Tcp created
+      with "client,add" event.
+
+      @since 1.19
+    ]]
+
+    events {
+        ssl,ready; [[Notifies the SSL handshake was done and the socket is now 
able to communicate]]
+        ssl,error: Eina.Error; [[an error such as 
@Efl.Net.Socket.Ssl.Error.HANDSHAKE]]
+    }
+
+    methods {
+        adopt {
+            [[Adopt an Efl.Net.Dialer or regular Efl.Net.Socket that will be 
used for the actual communication.
+
+              If used with an Efl.Net.Dialer object, it will assume
+              the 'connect' role, otherwise will use 'accept'.
+
+              This is a constructor only method and should be called
+              before @Efl.Object.finalize.
+            ]]
+            params {
+                efl_net_socket: Efl.Net.Socket; [[The socket to adopt]]
+                ctx: Efl.Net.Ssl.Context; [[The SSL context to use when 
adopting the socket]]
+            }
+        }
+
+        @property verify_mode {
+            [[How to verify the remote peer.]]
+            values {
+                verify_mode: Efl.Net.Ssl.Verify_Mode;
+            }
+        }
+
+        @property hostname_verify {
+            [[Define if hostname should be verified.
+
+              This will check the socket hostname (without the port in
+              case of an IP) or the overriden value from
+              @.hostname_override.
+            ]]
+            values {
+                hostname_verify: bool;
+            }
+        }
+
+        @property hostname_override {
+            [[Overrides the hostname to use for this socket.
+
+              Most of time this is useful if you're using an IP
+              address but the server certificate only specifies DNS
+              (names).
+
+              If NULL, then it will fetch from socket using
+              @Efl.Net.Socket.address_remote or
+              @Efl.Net.Dialer.address_dial.
+
+              It's only used if @.hostname_verify is $true.
+            ]]
+            values {
+                hostname_override: string @nullable;
+            }
+        }
+    }
+
+    implements {
+        Efl.Object.constructor;
+        Efl.Object.destructor;
+        Efl.Object.finalize;
+        Efl.Io.Closer.close;
+        Efl.Io.Closer.closed.get;
+        Efl.Io.Closer.close_on_exec;
+        Efl.Io.Closer.close_on_destructor;
+        Efl.Io.Reader.read;
+        Efl.Io.Reader.can_read;
+        Efl.Io.Reader.eos;
+        Efl.Io.Writer.write;
+        Efl.Io.Writer.can_write;
+        Efl.Net.Socket.address_remote.get;
+        Efl.Net.Socket.address_local.get;
+    }
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_conn-gnutls.c 
b/src/lib/ecore_con/efl_net_ssl_conn-gnutls.c
new file mode 100644
index 0000000..c3601ed
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_conn-gnutls.c
@@ -0,0 +1,362 @@
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+struct _Efl_Net_Ssl_Conn {
+   gnutls_session_t session;
+   gnutls_datum_t ticket;
+   Eo *sock;
+   const char *hostname;
+   Efl_Net_Ssl_Verify_Mode verify_mode;
+   Eina_Bool hostname_verify;
+   Eina_Bool is_dialer;
+};
+
+static ssize_t
+_efl_net_ssl_conn_write(gnutls_transport_ptr_t transp, const void *buf, size_t 
len)
+{
+   Eina_Slice slice = {
+     .mem = buf,
+     .len = len
+   };
+   Efl_Net_Ssl_Conn *conn = transp;
+   Eina_Error err;
+
+   if ((!buf) || (len == 0)) return 0;
+   if (!conn) return 0;
+
+   if (!efl_io_writer_can_write_get(conn->sock))
+     {
+        DBG("socket=%p would block if written!", conn->sock);
+        gnutls_transport_set_errno(conn->session, EAGAIN);
+        return -1;
+     }
+
+   err = efl_io_writer_write(conn->sock, &slice, NULL);
+   if (err)
+     {
+        gnutls_transport_set_errno(conn->session, err);
+        return -1;
+     }
+
+   gnutls_transport_set_errno(conn->session, 0);
+   return slice.len;
+}
+
+static ssize_t
+_efl_net_ssl_conn_read(gnutls_transport_ptr_t transp, void *buf, size_t len)
+{
+   Eina_Rw_Slice slice = {
+     .mem = buf,
+     .len = len
+   };
+   Efl_Net_Ssl_Conn *conn = transp;
+   Eina_Error err;
+
+   if ((!buf) || (len == 0)) return 0;
+   if (!conn) return 0;
+
+   if (!efl_io_reader_can_read_get(conn->sock))
+     {
+        DBG("socket=%p would block if read!", conn->sock);
+        gnutls_transport_set_errno(conn->session, EAGAIN);
+        return -1;
+     }
+
+   err = efl_io_reader_read(conn->sock, &slice);
+   if (err)
+     {
+        gnutls_transport_set_errno(conn->session, err);
+        return -1;
+     }
+
+   gnutls_transport_set_errno(conn->session, 0);
+   return slice.len;
+}
+
+static Eina_Error
+efl_net_ssl_conn_setup(Efl_Net_Ssl_Conn *conn, Eina_Bool is_dialer, 
Efl_Net_Socket *sock, Efl_Net_Ssl_Context *context)
+{
+   gnutls_certificate_request_t req;
+   int r;
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(conn->session != NULL, EALREADY);
+
+   conn->is_dialer = is_dialer;
+
+   conn->session = efl_net_ssl_context_connection_new(context);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(conn->session, ENOSYS);
+
+   gnutls_handshake_set_private_extensions(conn->session, 1);
+
+   switch (conn->verify_mode)
+     {
+      case EFL_NET_SSL_VERIFY_MODE_NONE:
+         req = GNUTLS_CERT_IGNORE;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_OPTIONAL:
+         req = GNUTLS_CERT_REQUEST;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_REQUIRED:
+      default:
+         req = GNUTLS_CERT_REQUIRE;
+     }
+   gnutls_certificate_server_set_request(conn->session, req);
+
+   if (is_dialer)
+     {
+        r = gnutls_session_ticket_enable_client(conn->session);
+        if (r < 0)
+          {
+             ERR("ssl_conn=%p could not enable session's ticket client: %s", 
conn, gnutls_strerror(r));
+             goto error;
+          }
+     }
+   else
+     {
+        r = gnutls_session_ticket_key_generate(&conn->ticket);
+        if (r < 0)
+          {
+             ERR("ssl_conn=%p could not generate session ticket: %s", conn, 
gnutls_strerror(r));
+             goto error;
+          }
+
+        r = gnutls_session_ticket_enable_server(conn->session, &conn->ticket);
+        if (r < 0)
+          {
+             ERR("ssl_conn=%p could not enable session's ticket server: %s", 
conn, gnutls_strerror(r));
+             goto error_ticket;
+          }
+     }
+
+   conn->sock = sock;
+   gnutls_transport_set_ptr(conn->session, conn);
+   gnutls_transport_set_push_function(conn->session, _efl_net_ssl_conn_write);
+   gnutls_transport_set_pull_function(conn->session, _efl_net_ssl_conn_read);
+
+   return 0;
+
+ error_ticket:
+   gnutls_free(conn->ticket.data);
+   conn->ticket.data = NULL;
+
+ error:
+   gnutls_deinit(conn->session);
+   conn->session = NULL;
+   return ENOSYS;
+}
+
+static void
+efl_net_ssl_conn_teardown(Efl_Net_Ssl_Conn *conn)
+{
+   if (conn->session)
+     {
+        gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+        gnutls_deinit(conn->session);
+        conn->session = NULL;
+     }
+
+   if (conn->ticket.data)
+     {
+        gnutls_free(conn->ticket.data);
+        conn->ticket.data = NULL;
+     }
+
+   eina_stringshare_replace(&conn->hostname, NULL);
+}
+
+static Eina_Error
+efl_net_ssl_conn_write(Efl_Net_Ssl_Conn *conn, Eina_Slice *slice)
+{
+   ssize_t r = gnutls_record_send(conn->session, slice->mem, slice->len);
+   if (r < 0)
+     {
+        slice->len = 0;
+        if (gnutls_error_is_fatal(r))
+          {
+             ERR("ssl_conn=%p could not send %zd bytes: %s", conn, slice->len, 
gnutls_strerror(r));
+             return EINVAL;
+          }
+        DBG("ssl_conn=%p could not send %zd bytes: %s", conn, slice->len, 
gnutls_strerror(r));
+        return EAGAIN;
+     }
+   slice->len = r;
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_read(Efl_Net_Ssl_Conn *conn, Eina_Rw_Slice *slice)
+{
+   ssize_t r = gnutls_record_recv(conn->session, slice->mem, slice->len);
+   if (r < 0)
+     {
+        slice->len = 0;
+        if (gnutls_error_is_fatal(r))
+          {
+             ERR("ssl_conn=%p could not receive %zd bytes: %s", conn, 
slice->len, gnutls_strerror(r));
+             return EINVAL;
+          }
+        DBG("ssl_conn=%p could not receive %zd bytes: %s", conn, slice->len, 
gnutls_strerror(r));
+        return EAGAIN;
+     }
+   slice->len = r;
+   return 0;
+}
+
+static Eina_Error
+_efl_net_ssl_conn_verify(Efl_Net_Ssl_Conn *conn)
+{
+   unsigned status = 0;
+   int r;
+
+   r = gnutls_certificate_verify_peers2(conn->session, &status);
+   if (r < 0)
+     {
+        ERR("ssl_conn=%p could not verify peer: %s", conn, gnutls_strerror(r));
+        return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE;
+     }
+
+   if (!status) return 0;
+
+   if (status & GNUTLS_CERT_INVALID)
+     WRN("ssl_conn=%p The certificate is not trusted.", conn);
+   if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+     WRN("ssl_conn=%p The certificate hasn't got a known issuer.", conn);
+   if (status & GNUTLS_CERT_REVOKED)
+     WRN("ssl_conn=%p The certificate has been revoked.", conn);
+   if (status & GNUTLS_CERT_EXPIRED)
+     WRN("ssl_conn=%p The certificate has expired", conn);
+   if (status & GNUTLS_CERT_NOT_ACTIVATED)
+     WRN("ssl_conn=%p The certificate is not yet activated", conn);
+
+   return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+}
+
+static Eina_Error
+_efl_net_ssl_conn_hostname_verify(Efl_Net_Ssl_Conn *conn)
+{
+   const gnutls_datum_t *list;
+   unsigned int size;
+   gnutls_x509_crt_t cert = NULL;
+   int r;
+
+   if ((!conn->hostname) || (conn->hostname[0] == '\0'))
+     {
+        ERR("ssl_conn=%p no hostname, cannot verify", conn);
+        return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+     }
+
+   if (gnutls_certificate_type_get(conn->session) != GNUTLS_CRT_X509)
+     {
+        ERR("ssl_conn=%p PGP certificates are not yet supported!", conn);
+        return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+     }
+
+   list = gnutls_certificate_get_peers(conn->session, &size);
+   if (!list)
+     {
+        ERR("ssl_conn=%p no peer certificate!", conn);
+        return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE;
+     }
+
+   r = gnutls_x509_crt_init(&cert);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(r < 0, 
EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED);
+
+   r = gnutls_x509_crt_import(cert, &list[0], GNUTLS_X509_FMT_DER);
+   if (r < 0)
+     {
+        ERR("ssl_conn=%p could not import x509 certificate to verify: %s", 
conn, gnutls_strerror(r));
+        gnutls_x509_crt_deinit(cert);
+        return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+     }
+
+   r = gnutls_x509_crt_check_hostname(cert, conn->hostname);
+   gnutls_x509_crt_deinit(cert);
+
+   if (r == 1)
+     return 0;
+
+   ERR("ssl_conn=%p hostname='%s' doesn't match certificate.",
+       conn, conn->hostname);
+   return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+}
+
+static Eina_Error
+efl_net_ssl_conn_handshake(Efl_Net_Ssl_Conn *conn, Eina_Bool *done)
+{
+   int r = gnutls_handshake(conn->session);
+   if (r < 0)
+     {
+        *done = EINA_FALSE;
+        if (gnutls_error_is_fatal(r))
+          {
+             ERR("ssl_conn=%p could not handshake: %s", conn, 
gnutls_strerror(r));
+             return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE;
+          }
+
+        DBG("ssl_conn=%p did not finish handshake: %s", conn, 
gnutls_strerror(r));
+        return 0;
+     }
+
+   if (conn->verify_mode != EFL_NET_SSL_VERIFY_MODE_NONE)
+     {
+        Eina_Error err = _efl_net_ssl_conn_verify(conn);
+        if (err)
+          return err;
+     }
+
+   if (conn->hostname_verify)
+     {
+        Eina_Error err = _efl_net_ssl_conn_hostname_verify(conn);
+        if (err)
+          return err;
+     }
+
+   *done = EINA_TRUE;
+   DBG("ssl_conn=%p handshake finished!", conn);
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_verify_mode_set(Efl_Net_Ssl_Conn *conn, 
Efl_Net_Ssl_Verify_Mode verify_mode)
+{
+   gnutls_certificate_request_t req;
+   conn->verify_mode = verify_mode;
+
+   switch (conn->verify_mode)
+     {
+      case EFL_NET_SSL_VERIFY_MODE_NONE:
+         req = GNUTLS_CERT_IGNORE;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_OPTIONAL:
+         req = GNUTLS_CERT_REQUEST;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_REQUIRED:
+      default:
+         req = GNUTLS_CERT_REQUIRE;
+     }
+   gnutls_certificate_server_set_request(conn->session, req);
+
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_verify_set(Efl_Net_Ssl_Conn *conn, Eina_Bool 
hostname_verify)
+{
+   conn->hostname_verify = hostname_verify;
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_override_set(Efl_Net_Ssl_Conn *conn, const char 
*hostname)
+{
+   int r;
+   eina_stringshare_replace(&conn->hostname, hostname);
+   if (!hostname) hostname = "";
+   r = gnutls_server_name_set(conn->session, GNUTLS_NAME_DNS, hostname, 
strlen(hostname));
+   if (r < 0)
+     {
+        ERR("ssl_conn=%p could not set server name '%s': %s", conn, hostname, 
gnutls_strerror(r));
+        return EINVAL;
+     }
+   return 0;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_conn-none.c 
b/src/lib/ecore_con/efl_net_ssl_conn-none.c
new file mode 100644
index 0000000..e3e9292
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_conn-none.c
@@ -0,0 +1,50 @@
+struct _Efl_Net_Ssl_Conn {
+};
+
+static Eina_Error
+efl_net_ssl_conn_setup(Efl_Net_Ssl_Conn *conn EINA_UNUSED, Eina_Bool is_dialer 
EINA_UNUSED, Efl_Net_Socket *sock EINA_UNUSED, Efl_Net_Ssl_Context *context 
EINA_UNUSED)
+{
+   ERR("EFL compiled with --with-crypto=none");
+   return ENOSYS;
+}
+
+static void
+efl_net_ssl_conn_teardown(Efl_Net_Ssl_Conn *conn EINA_UNUSED)
+{
+}
+
+static Eina_Error
+efl_net_ssl_conn_write(Efl_Net_Ssl_Conn *conn EINA_UNUSED, Eina_Slice *slice 
EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_conn_read(Efl_Net_Ssl_Conn *conn EINA_UNUSED, Eina_Rw_Slice *slice 
EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_conn_handshake(Efl_Net_Ssl_Conn *conn EINA_UNUSED, Eina_Bool *done 
EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_conn_verify_mode_set(Efl_Net_Ssl_Conn *conn EINA_UNUSED, 
Efl_Net_Ssl_Verify_Mode verify_mode EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_verify_set(Efl_Net_Ssl_Conn *conn EINA_UNUSED, 
Eina_Bool hostname_verify EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_override_set(Efl_Net_Ssl_Conn *conn EINA_UNUSED, 
const char *hostname EINA_UNUSED)
+{
+   return ENOSYS;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_conn-openssl.c 
b/src/lib/ecore_con/efl_net_ssl_conn-openssl.c
new file mode 100644
index 0000000..88e73c7
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_conn-openssl.c
@@ -0,0 +1,520 @@
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/dh.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+/* OpenSSL's BIO is the abstraction for I/O, provide one for Efl.Io.* */
+static int
+efl_net_socket_bio_create(BIO *b)
+{
+   b->init = 1;
+   b->num = 0;
+   b->ptr = NULL;
+   b->flags = 0;
+   return 1;
+}
+
+static int
+efl_net_socket_bio_destroy(BIO *b)
+{
+   if (!b) return 0;
+   b->init = 0;
+   b->ptr = NULL;
+   b->flags = 0;
+   return 1;
+}
+
+static int
+efl_net_socket_bio_read(BIO *b, char *buf, int len)
+{
+   Eina_Rw_Slice slice = {
+     .mem = buf,
+     .len = len
+   };
+   Eo *sock = b->ptr;
+   Eina_Error err;
+
+   if ((!buf) || (len <= 0)) return 0;
+   if (!sock) return 0;
+
+   if (!efl_io_reader_can_read_get(sock))
+     {
+        DBG("socket=%p would block if read!", sock);
+        BIO_set_retry_read(b);
+        return -1;
+     }
+
+   err = efl_io_reader_read(sock, &slice);
+   BIO_clear_retry_flags(b);
+   if (err)
+     {
+        if (err == EAGAIN)
+          BIO_set_retry_write(b);
+        return -1;
+     }
+
+   return slice.len;
+}
+
+static int
+efl_net_socket_bio_write(BIO *b, const char *buf, int len)
+{
+   Eina_Slice slice = {
+     .mem = buf,
+     .len = len
+   };
+   Eo *sock = b->ptr;
+   Eina_Error err;
+
+   if ((!buf) || (len <= 0)) return 0;
+   if (!sock) return 0;
+
+   if (!efl_io_writer_can_write_get(sock))
+     {
+        DBG("socket=%p would block if written!", sock);
+        BIO_set_retry_write(b);
+        return -1;
+     }
+
+   err = efl_io_writer_write(sock, &slice, NULL);
+   BIO_clear_retry_flags(b);
+   if (err)
+     {
+        if (err == EAGAIN)
+          BIO_set_retry_write(b);
+        return -1;
+     }
+
+   return slice.len;
+}
+
+static long
+efl_net_socket_bio_ctrl(BIO *b EINA_UNUSED, int cmd, long num EINA_UNUSED, 
void *ptr EINA_UNUSED)
+{
+   if (cmd == BIO_CTRL_FLUSH)
+     /* looks mandatory, but doesn't have a meaning here */
+     return 1;
+   return 0;
+}
+
+static int
+efl_net_socket_bio_puts(BIO *b, const char *str)
+{
+   return efl_net_socket_bio_write(b, str, strlen(str));
+}
+
+static BIO_METHOD efl_net_socket_bio = {
+  0x400, /* 0x400 means source & sink */
+  "efl_net_socket wrapper",
+  efl_net_socket_bio_write,
+  efl_net_socket_bio_read,
+  efl_net_socket_bio_puts,
+  NULL, /* no gets */
+  efl_net_socket_bio_ctrl,
+  efl_net_socket_bio_create,
+  efl_net_socket_bio_destroy
+};
+
+struct _Efl_Net_Ssl_Conn
+{
+   SSL *ssl;
+   BIO *bio;
+   const char *hostname;
+   Eina_Bool hostname_verify;
+   Eina_Bool did_certificates;
+};
+
+#define EFL_NET_SOCKET_SSL_CIPHERS 
"aRSA+HIGH:+kEDH:+kRSA:!kSRP:!kPSK:+3DES:!MD5"
+
+#define _efl_net_ssl_conn_session_debug(conn) \
+  __efl_net_ssl_conn_session_debug(__FILE__, __LINE__, __FUNCTION__, conn)
+static void
+__efl_net_ssl_conn_session_debug(const char *file, int line, const char 
*fname, Efl_Net_Ssl_Conn *conn)
+{
+   STACK_OF(X509) * sk_X509;
+   STACK_OF(SSL_CIPHER) *sk_CIPHER;
+
+   if (!eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG)) 
return;
+
+   sk_X509 = SSL_get_peer_cert_chain(conn->ssl);
+   if (!sk_X509)
+     eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, line,
+                    "ssl_conn=%p No peer certificate chain", conn);
+   else
+     {
+        char subject[4096], issuer[4096];
+        int i;
+
+        eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, 
line,
+                       "ssl_conn=%p Peer Certificates:", conn);
+        for (i = 0; i < sk_X509_num(sk_X509); i++)
+          {
+             X509 *item = sk_X509_value(sk_X509, i);
+             eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, 
fname, line,
+                            "  #%02d %s (Issuer: %s)",
+                            i,
+                            X509_NAME_oneline(X509_get_subject_name(item), 
subject, sizeof(subject)),
+                            X509_NAME_oneline(X509_get_issuer_name(item), 
issuer, sizeof(issuer)));
+             if (eina_log_domain_level_check(_ecore_con_log_dom, 
EINA_LOG_LEVEL_DBG + 1))
+               X509_print_fp(stderr, item);
+          }
+     }
+
+   sk_CIPHER = SSL_get_ciphers(conn->ssl);
+   if (!sk_CIPHER)
+     eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, line,
+                    "ssl_conn=%p No ciphers", conn);
+   else
+     {
+        char shared[8192];
+        const SSL_CIPHER *cipher;
+        char *p;
+        int i;
+
+        eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, 
line,
+                       "ssl_conn=%p Ciphers:", conn);
+        for (i = 0; i < sk_SSL_CIPHER_num(sk_CIPHER); i++)
+          {
+             cipher = sk_SSL_CIPHER_value(sk_CIPHER, i);
+             eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, 
fname, line,
+                            "  #%03d %s", i, SSL_CIPHER_get_name(cipher));
+          }
+
+        p = SSL_get_shared_ciphers(conn->ssl, shared, sizeof(shared));
+        if (!p)
+          eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, 
line,
+                         "ssl_conn=%p No Client (Shared) Ciphers", conn);
+        else
+          {
+             eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, 
fname, line,
+                            "ssl_conn=%p Client (Shared) Ciphers:", conn);
+             i = 0;
+             do
+               {
+                  char *n = strchr(p, ':');
+                  if (n)
+                    {
+                       *n = '\0';
+                       n++;
+                    }
+
+                  eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, 
fname, line,
+                                 "  #%03d %s", i, p);
+                  p = n;
+                  i++;
+               }
+             while (p);
+          }
+
+        cipher = SSL_get_current_cipher(conn->ssl);
+        if (!cipher)
+          eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, 
line,
+                         "ssl_conn=%p No cipher in use", conn);
+        else
+          eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, 
line,
+                         "ssl_conn=%p Current Cipher: %s (%s)",
+                         conn,
+                         SSL_CIPHER_get_version(cipher),
+                         SSL_CIPHER_get_name(cipher));
+     }
+
+   if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG + 1))
+     SSL_SESSION_print_fp(stderr, SSL_get_session(conn->ssl));
+
+   eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG, file, fname, line,
+                  "ssl_conn=%p end of SSL session information", conn);
+}
+
+#define _efl_net_ssl_conn_check_errors() \
+  __efl_net_ssl_conn_check_errors(__FILE__, __LINE__, __FUNCTION__)
+static unsigned long
+__efl_net_ssl_conn_check_errors(const char *file, int line, const char *fname)
+{
+   unsigned long first = 0;
+   do
+     {
+        const char *_ssl_err_file, *_ssl_err_data;
+        int _ssl_err_line, _ssl_err_flags;
+        unsigned long _ssl_err = ERR_get_error_line_data(&_ssl_err_file, 
&_ssl_err_line, &_ssl_err_data, &_ssl_err_flags);
+        if (!_ssl_err) break;
+        if (!first) first = _ssl_err;
+        eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_ERR, file, fname, 
line,
+                       "OpenSSL error %s:%d%s%s: %s",
+                       _ssl_err_file, _ssl_err_line,
+                       (_ssl_err_flags & ERR_TXT_STRING) ? " " : "",
+                       (_ssl_err_flags & ERR_TXT_STRING) ? _ssl_err_data : "",
+                       ERR_reason_error_string(_ssl_err));
+     }
+   while (1);
+   return first;
+}
+
+static Eina_Error
+efl_net_ssl_conn_setup(Efl_Net_Ssl_Conn *conn, Eina_Bool is_dialer, 
Efl_Net_Socket *sock, Efl_Net_Ssl_Context *context)
+{
+   char vbuf[32];
+   const char *ssl_ver_str = NULL;
+   int ssl_ver;
+   static const struct {
+      int ver;
+      const char *str;
+   } *ssl_ver_itr, ssl_ver_map[] = {
+     {SSL3_VERSION, "SSLv3.0"},
+     {TLS1_VERSION, "TLSv1.0"},
+     {TLS1_1_VERSION, "TLSv1.1"},
+     {TLS1_2_VERSION, "TLSv1.2"},
+     {DTLS1_VERSION, "DTLSv1.0"},
+     {DTLS1_2_VERSION, "DTLSv1.2"},
+     {DTLS1_BAD_VER, "DTLSv1.0"},
+     {0, NULL}
+   };
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(conn->ssl != NULL, EALREADY);
+
+   conn->ssl = efl_net_ssl_context_connection_new(context);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(conn->ssl, ENOSYS);
+
+   conn->bio = BIO_new(&efl_net_socket_bio);
+   EINA_SAFETY_ON_NULL_GOTO(conn->bio, error_bio);
+
+   conn->bio->ptr = sock;
+
+   SSL_set_bio(conn->ssl, conn->bio, conn->bio);
+   if (is_dialer)
+     SSL_set_connect_state(conn->ssl);
+   else
+     SSL_set_accept_state(conn->ssl);
+
+   ssl_ver = SSL_version(conn->ssl);
+   for (ssl_ver_itr = ssl_ver_map; ssl_ver_itr->str != NULL; ssl_ver_itr++)
+     {
+        if (ssl_ver_itr->ver == ssl_ver)
+          {
+             ssl_ver_str = ssl_ver_itr->str;
+             break;
+          }
+     }
+   if (!ssl_ver_str)
+     {
+        snprintf(vbuf, sizeof(vbuf), "%#x", ssl_ver);
+        ssl_ver_str = vbuf;
+     }
+   DBG("Using SSL %s", ssl_ver_str);
+
+   return 0;
+
+ error_bio:
+   SSL_shutdown(conn->ssl);
+   SSL_free(conn->ssl);
+   conn->ssl = NULL;
+   return ENOSYS;
+}
+
+static void
+efl_net_ssl_conn_teardown(Efl_Net_Ssl_Conn *conn)
+{
+   if (conn->bio)
+     {
+        /* NOTE: no BIO_free() as it's done by SSL_free(). */
+     }
+
+   if (conn->ssl)
+     {
+        if (!SSL_shutdown(conn->ssl))
+          SSL_shutdown(conn->ssl);
+
+        SSL_free(conn->ssl);
+        conn->ssl = NULL;
+     }
+
+   eina_stringshare_replace(&conn->hostname, NULL);
+}
+
+static Eina_Error
+efl_net_ssl_conn_write(Efl_Net_Ssl_Conn *conn, Eina_Slice *slice)
+{
+   int r = SSL_write(conn->ssl, slice->mem, slice->len);
+   if (r < 0)
+     {
+        int ssl_err = SSL_get_error(conn->ssl, r);
+
+        slice->len = 0;
+        if (ssl_err == SSL_ERROR_WANT_WRITE) return EAGAIN;
+        _efl_net_ssl_conn_check_errors();
+        ERR("ssl_conn=%p could not write", conn);
+        return EINVAL;
+     }
+   slice->len = r;
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_read(Efl_Net_Ssl_Conn *conn, Eina_Rw_Slice *slice)
+{
+   int r = SSL_read(conn->ssl, slice->mem, slice->len);
+   if (r < 0)
+     {
+        int ssl_err = SSL_get_error(conn->ssl, r);
+
+        slice->len = 0;
+        if (ssl_err == SSL_ERROR_WANT_READ) return EAGAIN;
+        _efl_net_ssl_conn_check_errors();
+        ERR("ssl_conn=%p could not read", conn);
+        return EINVAL;
+     }
+   slice->len = r;
+   return 0;
+}
+
+static Eina_Error
+_efl_net_ssl_conn_hostname_verify(Efl_Net_Ssl_Conn *conn)
+{
+   X509 *x509;
+   struct sockaddr_storage addr;
+   const char *label;
+   int family = AF_INET;
+   int r;
+
+   if ((!conn->hostname) || (conn->hostname[0] == '\0'))
+     {
+        ERR("ssl_conn=%p no hostname, cannot verify", conn);
+        return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+     }
+
+   x509 = SSL_get_peer_certificate(conn->ssl);
+   if (!x509)
+     {
+        ERR("ssl_conn=%p no peer certificate!", conn);
+        return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE;
+     }
+
+   if (strchr(conn->hostname, ':')) family = AF_INET6;
+   if (inet_pton(family, conn->hostname, &addr) == 1)
+     {
+        label = "IP address";
+        r = X509_check_ip_asc(x509, conn->hostname, 0);
+     }
+   else
+     {
+        label = "hostname";
+        r = X509_check_host(x509, conn->hostname, 0, 0, NULL);
+     }
+
+   if (r != 1)
+     {
+        ERR("ssl_conn=%p %s='%s' doesn't match certificate.",
+            conn, label, conn->hostname);
+        return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+     }
+
+   DBG("ssl_conn=%p %s='%s' matches certificate.", conn, label, 
conn->hostname);
+
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_handshake(Efl_Net_Ssl_Conn *conn, Eina_Bool *done)
+{
+   int r = SSL_do_handshake(conn->ssl);
+   long err_ssl;
+   const char *err_file;
+   const char *err_data;
+   int err_line, err_flags;
+
+   *done = EINA_FALSE;
+
+   if (r == 1)
+     {
+        _efl_net_ssl_conn_session_debug(conn);
+
+        if (conn->hostname_verify)
+          {
+             Eina_Error err = _efl_net_ssl_conn_hostname_verify(conn);
+             if (err)
+               return err;
+          }
+
+        *done = EINA_TRUE;
+        DBG("ssl_conn=%p handshake finished!", conn);
+        return 0;
+     }
+
+   r = SSL_get_error(conn->ssl, r);
+   if ((r == SSL_ERROR_WANT_READ) || (r == SSL_ERROR_WANT_WRITE))
+     {
+        DBG("ssl_conn=%p handshake needs more data...", conn);
+        return 0;
+     }
+
+   err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, 
&err_flags);
+   _efl_net_ssl_conn_check_errors();
+
+   if (!err_ssl)
+     DBG("ssl_conn=%p handshake error=%#x (SSL_ERROR_SSL=%#x)", conn, r, 
SSL_ERROR_SSL);
+   else
+     DBG("ssl_conn=%p handshake error=%#x (SSL_ERROR_SSL=%#x) [%s:%d%s%s 
%#lx='%s']",
+         conn, r, SSL_ERROR_SSL,
+         err_file, err_line,
+         (err_flags & ERR_TXT_STRING) ? " " : "",
+         (err_flags & ERR_TXT_STRING) ? err_data : "",
+         err_ssl, ERR_reason_error_string(err_ssl));
+   if (r == SSL_ERROR_SSL)
+     {
+        _efl_net_ssl_conn_session_debug(conn);
+
+        if ((ERR_GET_LIB(err_ssl) == ERR_LIB_SSL) &&
+            (ERR_GET_REASON(err_ssl) == SSL_R_CERTIFICATE_VERIFY_FAILED))
+          {
+             WRN("ssl_conn=%p certificate verification failed, handshake 
failed", conn);
+             return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED;
+          }
+        WRN("ssl_conn=%p handshake failed: %s", conn, 
ERR_reason_error_string(err_ssl));
+        return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE;
+     }
+
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_verify_mode_set(Efl_Net_Ssl_Conn *conn, 
Efl_Net_Ssl_Verify_Mode verify_mode)
+{
+   int ssl_mode;
+
+   switch (verify_mode)
+     {
+      case EFL_NET_SSL_VERIFY_MODE_NONE:
+         ssl_mode = SSL_VERIFY_NONE;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_OPTIONAL:
+         ssl_mode = SSL_VERIFY_PEER;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_REQUIRED:
+         ssl_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+         break;
+      default:
+         ERR("unknown verify_mode=%d", verify_mode);
+         return EINVAL;
+     }
+
+   SSL_set_verify(conn->ssl, ssl_mode, SSL_get_verify_callback(conn->ssl));
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_verify_set(Efl_Net_Ssl_Conn *conn, Eina_Bool 
hostname_verify)
+{
+   conn->hostname_verify = hostname_verify;
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_conn_hostname_override_set(Efl_Net_Ssl_Conn *conn, const char 
*hostname)
+{
+   eina_stringshare_replace(&conn->hostname, hostname);
+   if (hostname && (!conn->hostname)) return ENOMEM;
+   return 0;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_context.c 
b/src/lib/ecore_con/efl_net_ssl_context.c
new file mode 100644
index 0000000..22c5ad8
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_context.c
@@ -0,0 +1,373 @@
+#define EFL_NET_SSL_CONTEXT_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#include "Emile.h"
+
+/**
+ * This function is used by efl_net_socket_ssl to retrieve a new
+ * connection based on the implementation-depentent context.
+ *
+ * @internal
+ */
+void *efl_net_ssl_context_connection_new(Efl_Net_Ssl_Context *context);
+
+typedef struct _Efl_Net_Ssl_Ctx Efl_Net_Ssl_Ctx;
+
+typedef struct _Efl_Net_Ssl_Ctx_Config {
+   Efl_Net_Ssl_Cipher cipher;
+   Eina_Bool is_dialer;
+   Eina_Bool load_defaults;
+   Eina_List **certificates;
+   Eina_List **private_keys;
+   Eina_List **certificate_revogation_lists;
+   Eina_List **certificate_authorities;
+} Efl_Net_Ssl_Ctx_Config;
+
+/**
+ * Returns the platform dependent context to efl_net_socket_ssl
+ * wrapper.
+ *
+ * @internal
+ */
+static void *efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx);
+
+/**
+ * Setups the SSL context
+ *
+ * Update the given lists, removing invalid entries. If all entries
+ * failed in a list, return EINVAL.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx, 
Efl_Net_Ssl_Ctx_Config cfg);
+
+/**
+ * Cleans up the SSL associated to this context.
+ * @internal
+ */
+static void efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx);
+
+/**
+ * Configure how to verify peer.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx, 
Efl_Net_Ssl_Verify_Mode verify_mode);
+
+/**
+ * Configure whenever to check for hostname.
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx, 
Eina_Bool hostname_verify);
+
+/**
+ * Configure the hostname to use.
+ *
+ * @note duplicate hostname if needed!
+ *
+ * @internal
+ */
+static Eina_Error efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx, const 
char *hostname);
+
+#if HAVE_OPENSSL
+#include "efl_net_ssl_ctx-openssl.c"
+#elif HAVE_GNUTLS
+#include "efl_net_ssl_ctx-gnutls.c"
+#else
+#include "efl_net_ssl_ctx-none.c"
+#endif
+
+#define MY_CLASS EFL_NET_SSL_CONTEXT_CLASS
+
+typedef struct _Efl_Net_Ssl_Context_Data
+{
+   Efl_Net_Ssl_Ctx ssl_ctx;
+   Eina_List *certificates;
+   Eina_List *private_keys;
+   Eina_List *certificate_revogation_lists;
+   Eina_List *certificate_authorities;
+   const char *hostname;
+   Efl_Net_Ssl_Cipher cipher;
+   Eina_Bool is_dialer;
+   Efl_Net_Ssl_Verify_Mode verify_mode;
+   Eina_Bool load_defaults;
+   Eina_Bool hostname_verify;
+   Eina_Bool did_handshake;
+   Eina_Bool can_read;
+   Eina_Bool eos;
+   Eina_Bool can_write;
+} Efl_Net_Ssl_Context_Data;
+
+
+void *
+efl_net_ssl_context_connection_new(Efl_Net_Ssl_Context *context)
+{
+   Efl_Net_Ssl_Context_Data *pd = efl_data_scope_get(context, MY_CLASS);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
+   return efl_net_ssl_ctx_connection_new(&pd->ssl_ctx);
+}
+
+EOLIAN static void
+_efl_net_ssl_context_setup(Eo *o, Efl_Net_Ssl_Context_Data *pd, 
Efl_Net_Ssl_Cipher cipher, Eina_Bool is_dialer)
+{
+   EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o));
+   EINA_SAFETY_ON_TRUE_RETURN(cipher > EFL_NET_SSL_CIPHER_TLSV1_2);
+
+   pd->cipher = cipher;
+   pd->is_dialer = is_dialer;
+}
+
+static Eina_List *
+_efl_net_ssl_context_string_iter_to_list(Eina_Iterator *it)
+{
+   Eina_List *lst = NULL;
+   const char *str;
+   EINA_ITERATOR_FOREACH(it, str)
+     {
+        if (!str) continue;
+        lst = eina_list_append(lst, eina_stringshare_add(str));
+     }
+   eina_iterator_free(it);
+   return lst;
+}
+
+static void
+_efl_net_ssl_context_string_list_free(Eina_List **p_lst)
+{
+   const char *str;
+   EINA_LIST_FREE(*p_lst, str)
+     eina_stringshare_del(str);
+}
+
+static Eina_Iterator *
+_efl_net_ssl_context_certificates_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return eina_list_iterator_new(pd->certificates);
+}
+
+static void
+_efl_net_ssl_context_certificates_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Iterator *it)
+{
+   _efl_net_ssl_context_string_list_free(&pd->certificates);
+   pd->certificates = _efl_net_ssl_context_string_iter_to_list(it);
+}
+
+static Eina_Iterator *
+_efl_net_ssl_context_private_keys_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return eina_list_iterator_new(pd->private_keys);
+}
+
+static void
+_efl_net_ssl_context_private_keys_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Iterator *it)
+{
+   _efl_net_ssl_context_string_list_free(&pd->private_keys);
+   pd->private_keys = _efl_net_ssl_context_string_iter_to_list(it);
+}
+
+static Eina_Iterator *
+_efl_net_ssl_context_certificate_revogation_lists_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return eina_list_iterator_new(pd->certificate_revogation_lists);
+}
+
+static void
+_efl_net_ssl_context_certificate_revogation_lists_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Iterator *it)
+{
+   _efl_net_ssl_context_string_list_free(&pd->certificate_revogation_lists);
+   pd->certificate_revogation_lists = 
_efl_net_ssl_context_string_iter_to_list(it);
+}
+
+static Eina_Iterator *
+_efl_net_ssl_context_certificate_authorities_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return eina_list_iterator_new(pd->certificate_authorities);
+}
+
+static void
+_efl_net_ssl_context_certificate_authorities_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Iterator *it)
+{
+   _efl_net_ssl_context_string_list_free(&pd->certificate_authorities);
+   pd->certificate_authorities = _efl_net_ssl_context_string_iter_to_list(it);
+}
+
+static Eina_Bool
+_efl_net_ssl_context_default_paths_load_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return pd->load_defaults;
+}
+
+static void
+_efl_net_ssl_context_default_paths_load_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Bool load_defaults)
+{
+   pd->load_defaults = load_defaults;
+}
+
+static Efl_Net_Ssl_Verify_Mode
+_efl_net_ssl_context_verify_mode_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return pd->verify_mode;
+}
+
+static void
+_efl_net_ssl_context_verify_mode_set(Eo *o, Efl_Net_Ssl_Context_Data *pd, 
Efl_Net_Ssl_Verify_Mode verify_mode)
+{
+   pd->verify_mode = verify_mode;
+   if (!efl_finalized_get(o)) return;
+
+   efl_net_ssl_ctx_verify_mode_set(&pd->ssl_ctx, pd->verify_mode);
+}
+
+static Eina_Bool
+_efl_net_ssl_context_hostname_verify_get(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd)
+{
+   return pd->hostname_verify;
+}
+
+static void
+_efl_net_ssl_context_hostname_verify_set(Eo *o EINA_UNUSED, 
Efl_Net_Ssl_Context_Data *pd, Eina_Bool hostname_verify)
+{
+   pd->hostname_verify = hostname_verify;
+   if (!efl_finalized_get(o)) return;
+
+   efl_net_ssl_ctx_hostname_verify_set(&pd->ssl_ctx, pd->hostname_verify);
+}
+
+static const char *
+_efl_net_ssl_context_hostname_get(Eo *o EINA_UNUSED, Efl_Net_Ssl_Context_Data 
*pd)
+{
+   return pd->hostname;
+}
+
+static void
+_efl_net_ssl_context_hostname_set(Eo *o EINA_UNUSED, Efl_Net_Ssl_Context_Data 
*pd, const char* hostname)
+{
+   eina_stringshare_replace(&pd->hostname, hostname);
+   if (!efl_finalized_get(o)) return;
+
+   efl_net_ssl_ctx_hostname_set(&pd->ssl_ctx, pd->hostname);
+}
+
+EOLIAN static Efl_Object *
+_efl_net_ssl_context_efl_object_finalize(Eo *o, Efl_Net_Ssl_Context_Data *pd)
+{
+   Eina_Error err;
+   Efl_Net_Ssl_Ctx_Config cfg;
+
+   o = efl_finalize(efl_super(o, MY_CLASS));
+   if (!o) return NULL;
+
+   if (!emile_cipher_init())
+     {
+        ERR("could not initialize cipher subsystem.");
+        return NULL;
+     }
+
+   if (pd->is_dialer)
+     {
+        if ((uint8_t)pd->verify_mode == 0xff)
+          pd->verify_mode = EFL_NET_SSL_VERIFY_MODE_REQUIRED;
+        if (pd->hostname_verify == 0xff)
+          pd->hostname_verify = EINA_TRUE;
+        if (pd->load_defaults == 0xff)
+          pd->load_defaults = EINA_TRUE;
+     }
+   else
+     {
+        cfg.is_dialer = EINA_FALSE;
+        if ((uint8_t)pd->verify_mode == 0xff)
+          pd->verify_mode = EFL_NET_SSL_VERIFY_MODE_NONE;
+        if (pd->hostname_verify == 0xff)
+          pd->hostname_verify = EINA_FALSE;
+        if (pd->load_defaults == 0xff)
+          pd->load_defaults = EINA_FALSE;
+     }
+
+   cfg.cipher = pd->cipher;
+   cfg.is_dialer = pd->is_dialer;
+   cfg.load_defaults = pd->load_defaults;
+   cfg.certificates = &pd->certificates;
+   cfg.private_keys = &pd->private_keys;
+   cfg.certificate_revogation_lists = &pd->certificate_revogation_lists;
+   cfg.certificate_authorities = &pd->certificate_authorities;
+   cfg.load_defaults = pd->load_defaults;
+
+   err = efl_net_ssl_ctx_setup(&pd->ssl_ctx, cfg);
+   if (err)
+     {
+        ERR("o=%p failed to setup context (is_dialer=%d)", o, cfg.is_dialer);
+        return NULL;
+     }
+   DBG("o=%p setup context (is_dialer=%d) ssl_ctx=%p", o, cfg.is_dialer, 
&pd->ssl_ctx);
+
+   efl_net_ssl_ctx_verify_mode_set(&pd->ssl_ctx, pd->verify_mode);
+   efl_net_ssl_ctx_hostname_verify_set(&pd->ssl_ctx, pd->hostname_verify);
+   efl_net_ssl_ctx_hostname_set(&pd->ssl_ctx, pd->hostname);
+
+   return o;
+}
+
+EOLIAN static Eo *
+_efl_net_ssl_context_efl_object_constructor(Eo *o, Efl_Net_Ssl_Context_Data 
*pd)
+{
+   pd->cipher = EFL_NET_SSL_CIPHER_AUTO;
+   pd->is_dialer = EINA_TRUE;
+   pd->load_defaults = 0xff;
+   pd->hostname_verify = 0xff;
+   pd->verify_mode = 0xff;
+   return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_ssl_context_efl_object_destructor(Eo *o, Efl_Net_Ssl_Context_Data *pd)
+{
+   efl_destructor(efl_super(o, MY_CLASS));
+
+   efl_net_ssl_ctx_teardown(&pd->ssl_ctx);
+
+   _efl_net_ssl_context_string_list_free(&pd->certificates);
+   _efl_net_ssl_context_string_list_free(&pd->private_keys);
+   _efl_net_ssl_context_string_list_free(&pd->certificate_revogation_lists);
+   _efl_net_ssl_context_string_list_free(&pd->certificate_authorities);
+
+   eina_stringshare_replace(&pd->hostname, NULL);
+}
+
+static Efl_Net_Ssl_Context *_efl_net_ssl_context_default_dialer = NULL;
+
+static void
+_efl_net_ssl_context_default_dialer_del(void *data EINA_UNUSED, const 
Efl_Event *event EINA_UNUSED)
+{
+   _efl_net_ssl_context_default_dialer = NULL;
+}
+
+EOLIAN static Efl_Net_Ssl_Context *
+_efl_net_ssl_context_default_dialer_get(Efl_Class *klass, void *pd EINA_UNUSED)
+{
+   if (!_efl_net_ssl_context_default_dialer)
+     {
+        _efl_net_ssl_context_default_dialer = efl_add(klass, NULL,
+                                                      
efl_net_ssl_context_verify_mode_set(efl_added, 
EFL_NET_SSL_VERIFY_MODE_REQUIRED),
+                                                      
efl_net_ssl_context_hostname_verify_set(efl_added, EINA_TRUE),
+                                                      
efl_net_ssl_context_default_paths_load_set(efl_added, EINA_TRUE),
+                                                      
efl_net_ssl_context_setup(efl_added, EFL_NET_SSL_CIPHER_AUTO, EINA_TRUE));
+        efl_event_callback_add(_efl_net_ssl_context_default_dialer,
+                               EFL_EVENT_DEL,
+                               _efl_net_ssl_context_default_dialer_del,
+                               NULL);
+     }
+   return _efl_net_ssl_context_default_dialer;
+}
+
+#include "efl_net_ssl_context.eo.c"
diff --git a/src/lib/ecore_con/efl_net_ssl_context.eo 
b/src/lib/ecore_con/efl_net_ssl_context.eo
new file mode 100644
index 0000000..3c8caec
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_context.eo
@@ -0,0 +1,122 @@
+import efl_net_ssl_types;
+
+class Efl.Net.Ssl.Context (Efl.Object) {
+    [[A SSL Context that is used to start a SSL socket wrapper.
+
+      The context will contain common configurations such as
+      certificates, private keys, certificate revogation lists (CRLs),
+      certificate authorities (CAs) and so on.
+
+      The method @.setup must be called once before
+      @Efl.Object.finalize in order to define the mandatory
+      operational parameters.
+
+      \@note All setter methods must be called before @Efl.Object.finalize.
+
+      @since 1.19
+    ]]
+
+    methods {
+        @property default_dialer @class {
+            [[The default context for dialers.
+
+              It will start with:
+
+               - default_paths_load = true
+               - cipher = auto
+               - verify_mode = required
+               - verify_hostname = true
+
+            ]]
+            get { }
+            values {
+                default_client_context: Efl.Net.Ssl.Context;
+            }
+        }
+
+        setup {
+            [[Defines the context mandatory operation parameters]]
+            params {
+                cipher: Efl.Net.Ssl.Cipher; [[Cipher to use, prefer 
@Efl.Net.Ssl.Cipher.auto]]
+                is_dialer: bool; [[If $true, this SSL context is targeted at 
dialers connecting to a remote serer]]
+            }
+        }
+
+        @property certificates {
+            [[The list of paths to certificates to use.]]
+            values {
+                paths: free(own(iterator<string>), eina_iterator_free);
+            }
+        }
+
+        @property private_keys {
+            [[The list of paths to private keys to use.]]
+            values {
+                paths: free(own(iterator<string>), eina_iterator_free);
+            }
+        }
+
+        @property certificate_revogation_lists {
+            [[The list of paths to CRL (certificate revogation list) to use.]]
+            values {
+                paths: free(own(iterator<string>), eina_iterator_free);
+            }
+        }
+
+        @property certificate_authorities {
+            [[The list of paths to CA (certificate authoritie) to use.]]
+            values {
+                paths: free(own(iterator<string>), eina_iterator_free);
+            }
+        }
+
+        @property default_paths_load {
+            [[If $true, will use system's default certificate storage]]
+            values {
+                default_paths_load: bool;
+            }
+        }
+
+        @property verify_mode {
+            [[How to verify the remote peer.]]
+            values {
+                verify_mode: Efl.Net.Ssl.Verify_Mode;
+            }
+        }
+
+        @property hostname_verify {
+            [[Define if hostname should be verified.
+
+              This will check the socket hostname (without the port in
+              case of an IP) or the overriden value from
+              @.hostname.
+            ]]
+            values {
+                hostname_verify: bool;
+            }
+        }
+
+        @property hostname {
+            [[Defines the hostname to use for sockets.
+
+              This is useful to avoid replicating a hostname in all
+              socket wrapper with hostname_override.
+
+              If NULL, then sockets wrappers will will fetch from
+              adopted socket using address_remote or
+              address_dial.
+
+              It's only used if @.hostname_verify is $true.
+            ]]
+            values {
+                hostname: string @nullable;
+            }
+        }
+    }
+
+    implements {
+        Efl.Object.constructor;
+        Efl.Object.destructor;
+        Efl.Object.finalize;
+    }
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_ctx-gnutls.c 
b/src/lib/ecore_con/efl_net_ssl_ctx-gnutls.c
new file mode 100644
index 0000000..cdd3969
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_ctx-gnutls.c
@@ -0,0 +1,310 @@
+#include <gnutls/gnutls.h>
+
+struct _Efl_Net_Ssl_Ctx {
+   gnutls_certificate_credentials_t x509_cred;
+   gnutls_priority_t priority;
+   Eina_Bool is_dialer;
+};
+
+static Eina_Error
+_efl_net_ssl_ctx_load_lists(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
+{
+   Eina_List *n, *n_next, *pk_node;
+   const char *path;
+   unsigned certificates_count = eina_list_count(*cfg.certificates);
+   unsigned private_keys_count = eina_list_count(*cfg.private_keys);
+   unsigned certificate_revogation_lists_count = 
eina_list_count(*cfg.certificate_revogation_lists);
+   unsigned certificate_authorities_count = 
eina_list_count(*cfg.certificate_authorities);
+   int r;
+
+   ctx->is_dialer = cfg.is_dialer;
+
+   if (cfg.load_defaults)
+     {
+        r = gnutls_certificate_set_x509_system_trust(ctx->x509_cred);
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not load default paths: %s", ctx, 
gnutls_strerror(r));
+             return ENOSYS;
+          }
+        DBG("ssl_ctx=%p loaded default paths", ctx);
+     }
+   else
+     DBG("ssl_ctx=%p did not load default paths", ctx);
+
+   /* GNUTLS needs certificate-key pairs, so we do:
+    *
+    *  - if no private keys, use certificate as its own key;
+    *
+    *  - if a private keys, walk the list alongside certificates, but
+    *    do NOT delete elements if list sizes are different. Stop at
+    *    last private key, allowing a single private key for multiple
+    *    certificates.
+    */
+   pk_node = *cfg.private_keys;
+   EINA_LIST_FOREACH_SAFE(*cfg.certificates, n, n_next, path)
+     {
+        const char *key = pk_node ? pk_node->data : path;
+
+        r = gnutls_certificate_set_x509_key_file(ctx->x509_cred, path, key, 
GNUTLS_X509_FMT_PEM);
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not use certificate from '%s' with key 
'%s': %s",
+                 ctx, path, key, gnutls_strerror(r));
+
+             if (pk_node)
+               {
+                  if (eina_list_count(*cfg.private_keys) == 
eina_list_count(*cfg.certificates))
+                    {
+                       pk_node = pk_node->next;
+                       eina_stringshare_del(key);
+                       *cfg.private_keys = 
eina_list_remove_list(*cfg.private_keys, pk_node->prev);
+                    }
+                  else if (pk_node->next) pk_node = pk_node->next;
+               }
+
+             eina_stringshare_del(path);
+             *cfg.certificates = eina_list_remove_list(*cfg.certificates, n);
+             continue;
+          }
+        else
+          {
+             if (pk_node->next) pk_node = pk_node->next;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate '%s' with key '%s'", ctx, path, 
key);
+     }
+   if (certificates_count && !*cfg.certificates)
+     {
+        ERR("ssl_ctx=%p none of the required certificates were loaded!", ctx);
+        return EINVAL;
+     }
+
+   if (private_keys_count && !*cfg.private_keys)
+     {
+        ERR("ssl_ctx=%p none of the required private keys were loaded!", ctx);
+        return EINVAL;
+     }
+   else if (pk_node != eina_list_last(*cfg.private_keys))
+     {
+        do
+          {
+             n = pk_node->next;
+             path = n->data;
+             ERR("ssl_ctx=%p extra private key is unused '%s'", ctx, path);
+             eina_stringshare_del(path);
+             *cfg.private_keys = eina_list_remove_list(*cfg.private_keys, n);
+          }
+        while (pk_node->next);
+     }
+
+   EINA_LIST_FOREACH_SAFE(*cfg.certificate_revogation_lists, n, n_next, path)
+     {
+        r = gnutls_certificate_set_x509_crl_file(ctx->x509_cred, path, 
GNUTLS_X509_FMT_PEM);
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not use certificate revogation lists from 
%s: %s",
+                 ctx, path, gnutls_strerror(r));
+             eina_stringshare_del(path);
+             *cfg.certificate_revogation_lists = 
eina_list_remove_list(*cfg.certificate_revogation_lists, n);
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate revogation lists '%s'", ctx, path);
+     }
+   if (certificate_revogation_lists_count && 
!*cfg.certificate_revogation_lists)
+     {
+        ERR("ssl_ctx=%p none of the required certificate revogation lists were 
loaded!", ctx);
+        return EINVAL;
+     }
+
+   EINA_LIST_FOREACH_SAFE(*cfg.certificate_authorities, n, n_next, path)
+     {
+        struct stat st;
+
+        r = 0;
+        if (stat(path, &st) != 0)
+          {
+             ERR("ssl_ctx=%p could not load certificate authorities from '%s': 
%s", ctx, path, strerror(errno));
+             eina_stringshare_del(path);
+             *cfg.certificate_authorities = 
eina_list_remove_list(*cfg.certificate_authorities, n);
+             continue;
+          }
+        else if (S_ISDIR(st.st_mode))
+          r = gnutls_certificate_set_x509_trust_dir(ctx->x509_cred, path, 
GNUTLS_X509_FMT_PEM);
+        else
+          r = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, path, 
GNUTLS_X509_FMT_PEM);
+
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not use certificate authorities from '%s': 
%s", ctx, path, gnutls_strerror(r));
+             eina_stringshare_del(path);
+             *cfg.certificate_authorities = 
eina_list_remove_list(*cfg.certificate_authorities, n);
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate authorities '%s'", ctx, path);
+     }
+   if (certificate_authorities_count && !*cfg.certificate_authorities)
+     {
+        ERR("ssl_ctx=%p none of the required certificate authorities were 
loaded!", ctx);
+        return EINVAL;
+     }
+
+   return 0;
+}
+
+static void *
+efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx)
+{
+   gnutls_session_t session;
+   int r;
+
+   r = gnutls_init(&session, ctx->is_dialer ? GNUTLS_CLIENT : GNUTLS_SERVER);
+   if (r < 0)
+     {
+        ERR("ssl_ctx=%p could not create %s session: %s",
+            ctx, ctx->is_dialer ? "dialer" : "server", gnutls_strerror(r));
+        return NULL;
+     }
+
+   if (!ctx->priority)
+     {
+        r = gnutls_set_default_priority(session);
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not set default cipher priority: %s", ctx, 
gnutls_strerror(r));
+             goto error;
+          }
+     }
+   else
+     {
+        r = gnutls_priority_set(session, ctx->priority);
+        if (r < 0)
+          {
+             ERR("ssl_ctx=%p could not set cipher priority: %s", ctx, 
gnutls_strerror(r));
+             goto error;
+          }
+     }
+
+   r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->x509_cred);
+   if (r < 0)
+     {
+        ERR("ssl_ctx=%p could not set session credentials: %s", ctx, 
gnutls_strerror(r));
+        goto error;
+     }
+
+   return session;
+
+ error:
+   gnutls_deinit(session);
+   return NULL;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
+{
+   Eina_Error err;
+   const char *priority;
+   int r;
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(ctx->x509_cred != NULL, EALREADY);
+
+   switch (cfg.cipher)
+     {
+      case EFL_NET_SSL_CIPHER_AUTO:
+         priority = NULL;
+         break;
+      case EFL_NET_SSL_CIPHER_SSLV3:
+         priority = 
"NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-TLS1.0:!VERS-TLS1.1:!VERS-TLS1.2";
+         break;
+      case EFL_NET_SSL_CIPHER_TLSV1:
+         priority = 
"NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0!VERS-TLS1.1:!VERS-TLS1.2";
+         break;
+      case EFL_NET_SSL_CIPHER_TLSV1_1:
+         priority = 
"NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.2";
+         break;
+      case EFL_NET_SSL_CIPHER_TLSV1_2:
+         priority = 
"NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
+         break;
+      default:
+         ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher);
+         return EINVAL;
+     }
+
+   if (priority)
+     {
+        const char *err_pos = NULL;
+        r = gnutls_priority_init(&ctx->priority, priority, &err_pos);
+        if (r < 0)
+          {
+             size_t off = err_pos - priority;
+             if (r == GNUTLS_E_INVALID_REQUEST)
+               {
+                  ERR("ssl_ctx=%p invalid syntax on GNUTLS priority string 
offset %zd: '%s'", ctx, off, priority);
+                  return EINVAL;
+               }
+             ERR("ssl_ctx=%p could not set GNUTLS priority offset %zd '%s': 
%s", ctx, off, priority, gnutls_strerror(r));
+             return EINVAL;
+          }
+     }
+
+   r = gnutls_certificate_allocate_credentials(&ctx->x509_cred);
+   if (r < 0)
+     {
+        ERR("ssl_ctx=%p could not allocate X509 credentials: %s", ctx, 
gnutls_strerror(r));
+        err = ENOSYS;
+        goto err_cert_alloc;
+     }
+
+   err = _efl_net_ssl_ctx_load_lists(ctx, cfg);
+   if (err)
+     {
+        ERR("ssl_ctx=%p failed to load certificate, private keys, CRL or CA", 
ctx);
+        goto err_load;
+     }
+
+   return 0;
+
+ err_load:
+   gnutls_certificate_free_credentials(ctx->x509_cred);
+   ctx->x509_cred = NULL;
+ err_cert_alloc:
+   gnutls_priority_deinit(ctx->priority);
+   ctx->priority = NULL;
+   return err;
+}
+
+static void
+efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx)
+{
+   if (ctx->x509_cred)
+     {
+        gnutls_certificate_free_credentials(ctx->x509_cred);
+        ctx->x509_cred = NULL;
+     }
+
+   if (ctx->priority)
+     {
+        gnutls_priority_deinit(ctx->priority);
+        ctx->priority = NULL;
+     }
+}
+
+static Eina_Error
+efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, 
Efl_Net_Ssl_Verify_Mode verify_mode EINA_UNUSED)
+{
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, 
Eina_Bool hostname_verify EINA_UNUSED)
+{
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, const char 
*hostname EINA_UNUSED)
+{
+   return 0;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_ctx-none.c 
b/src/lib/ecore_con/efl_net_ssl_ctx-none.c
new file mode 100644
index 0000000..1332a0f
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_ctx-none.c
@@ -0,0 +1,38 @@
+struct _Efl_Net_Ssl_Ctx {
+};
+
+static void *
+efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED)
+{
+   return NULL;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, Efl_Net_Ssl_Ctx_Config 
cfg EINA_UNUSED)
+{
+   ERR("EFL compiled with --with-crypto=none");
+   return ENOSYS;
+}
+
+static void
+efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED)
+{
+}
+
+static Eina_Error
+efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, 
Efl_Net_Ssl_Verify_Mode verify_mode EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, 
Eina_Bool hostname_verify EINA_UNUSED)
+{
+   return ENOSYS;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, const char 
*hostname EINA_UNUSED)
+{
+   return ENOSYS;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_ctx-openssl.c 
b/src/lib/ecore_con/efl_net_ssl_ctx-openssl.c
new file mode 100644
index 0000000..beefbac
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_ctx-openssl.c
@@ -0,0 +1,387 @@
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/dh.h>
+
+struct _Efl_Net_Ssl_Ctx
+{
+   SSL_CTX *ssl_ctx;
+   Eina_Bool did_certificates;
+   Eina_Bool is_dialer;
+};
+
+#define EFL_NET_SSL_CONTEXT_CIPHERS 
"aRSA+HIGH:+kEDH:+kRSA:!kSRP:!kPSK:+3DES:!MD5"
+
+#define _efl_net_ssl_ctx_check_errors() \
+  __efl_net_ssl_ctx_check_errors(__FILE__, __LINE__, __FUNCTION__)
+static unsigned long
+__efl_net_ssl_ctx_check_errors(const char *file, int line, const char *fname)
+{
+   unsigned long first = 0;
+   do
+     {
+        const char *_ssl_err_file, *_ssl_err_data;
+        int _ssl_err_line, _ssl_err_flags;
+        unsigned long _ssl_err = ERR_get_error_line_data(&_ssl_err_file, 
&_ssl_err_line, &_ssl_err_data, &_ssl_err_flags);
+        if (!_ssl_err) break;
+        if (!first) first = _ssl_err;
+        eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_ERR, file, fname, 
line,
+                       "OpenSSL error %s:%d%s%s: %s",
+                       _ssl_err_file, _ssl_err_line,
+                       (_ssl_err_flags & ERR_TXT_STRING) ? " " : "",
+                       (_ssl_err_flags & ERR_TXT_STRING) ? _ssl_err_data : "",
+                       ERR_reason_error_string(_ssl_err));
+     }
+   while (1);
+   return first;
+}
+
+static Eina_Error
+_efl_net_ssl_ctx_load_lists(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
+{
+   Eina_List *n, *n_next;
+   const char *path;
+   unsigned certificates_count = eina_list_count(*cfg.certificates);
+   unsigned private_keys_count = eina_list_count(*cfg.private_keys);
+   unsigned certificate_revogation_lists_count = 
eina_list_count(*cfg.certificate_revogation_lists);
+   unsigned certificate_authorities_count = 
eina_list_count(*cfg.certificate_authorities);
+   long err_ssl;
+   const char *err_file;
+   const char *err_data;
+   int err_line, err_flags;
+   X509_STORE *x509_store;
+   X509_LOOKUP *x509_lookup;
+   unsigned long x509_store_flags = X509_V_FLAG_TRUSTED_FIRST;
+
+   if (cfg.load_defaults)
+     {
+        if (SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1)
+          {
+             _efl_net_ssl_ctx_check_errors();
+             ERR("ssl_ctx=%p could not load default paths", ctx);
+             return ENOSYS;
+          }
+        DBG("ssl_ctx=%p loaded default paths", ctx);
+     }
+   else
+     DBG("ssl_ctx=%p did not load default paths", ctx);
+
+   EINA_LIST_FOREACH_SAFE(*cfg.certificates, n, n_next, path)
+     {
+        if ((SSL_CTX_use_certificate_file(ctx->ssl_ctx, path, 
SSL_FILETYPE_PEM) != 1) &&
+            (SSL_CTX_use_certificate_file(ctx->ssl_ctx, path, 
SSL_FILETYPE_ASN1) != 1))
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+
+_efl_net_ssl_ctx_check_errors();
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p could not use certificate from %s [%s:%d%s%s 
'%s']",
+                 ctx, path,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             eina_stringshare_del(path);
+             *cfg.certificates = eina_list_remove_list(*cfg.certificates, n);
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate '%s'", ctx, path);
+        ctx->did_certificates = EINA_TRUE;
+     }
+   if (certificates_count && !*cfg.certificates)
+     {
+        ERR("ssl_ctx=%p none of the required certificates were loaded!", ctx);
+        return EINVAL;
+     }
+
+   EINA_LIST_FOREACH_SAFE(*cfg.private_keys, n, n_next, path)
+     {
+        if ((SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, path, SSL_FILETYPE_PEM) 
!= 1) &&
+            (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, path, 
SSL_FILETYPE_ASN1) != 1))
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p could not use private key from %s [%s:%d%s%s 
'%s']",
+                 ctx, path,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             eina_stringshare_del(path);
+             *cfg.private_keys = eina_list_remove_list(*cfg.private_keys, n);
+             continue;
+          }
+
+        if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1)
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p could not check private key from %s [%s:%d%s%s 
'%s']",
+                 ctx, path,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded private key '%s'", ctx, path);
+     }
+   if (private_keys_count && !*cfg.private_keys)
+     {
+        ERR("ssl_ctx=%p none of the required private keys were loaded!", ctx);
+        return EINVAL;
+     }
+
+   x509_store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
+   if (!x509_store)
+     {
+        _efl_net_ssl_ctx_check_errors();
+        ERR("ssl_ctx=%p SSL has no X509 certificate store", ctx);
+        return ENOSYS;
+     }
+   x509_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
+   if (!x509_lookup)
+     {
+        _efl_net_ssl_ctx_check_errors();
+        ERR("ssl_ctx=%p could not add X509 file lookup", ctx);
+        return ENOSYS;
+     }
+
+   EINA_LIST_FOREACH_SAFE(*cfg.certificate_revogation_lists, n, n_next, path)
+     {
+        if ((X509_load_crl_file(x509_lookup, path, X509_FILETYPE_PEM) != 1) &&
+            (X509_load_crl_file(x509_lookup, path, X509_FILETYPE_ASN1) != 1))
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p could not use certificate revogation lists from 
%s [%s:%d%s%s '%s']",
+                 ctx, path,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             eina_stringshare_del(path);
+             *cfg.certificate_revogation_lists = 
eina_list_remove_list(*cfg.certificate_revogation_lists, n);
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate revogation lists '%s'", ctx, path);
+        x509_store_flags |= X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL;
+     }
+   if (certificate_revogation_lists_count && 
!*cfg.certificate_revogation_lists)
+     {
+        ERR("ssl_ctx=%p none of the required certificate revogation lists were 
loaded!", ctx);
+        return EINVAL;
+     }
+   X509_STORE_set_flags(x509_store, x509_store_flags);
+
+   EINA_LIST_FOREACH_SAFE(*cfg.certificate_authorities, n, n_next, path)
+     {
+        struct stat st;
+        const char *cafile = NULL, *cadir = NULL;
+
+        if (stat(path, &st) != 0)
+          {
+             ERR("ssl_ctx=%p could not load certificate authorities from '%s': 
%s", ctx, path, strerror(errno));
+             eina_stringshare_del(path);
+             *cfg.certificate_authorities = 
eina_list_remove_list(*cfg.certificate_authorities, n);
+             continue;
+          }
+        else if (S_ISDIR(st.st_mode)) cadir = path;
+        else cafile = path;
+
+        if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, cafile, cadir) != 1)
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p could not use certificate authorities from %s 
[%s:%d%s%s '%s']",
+                 ctx, path,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             eina_stringshare_del(path);
+             *cfg.certificate_authorities = 
eina_list_remove_list(*cfg.certificate_authorities, n);
+             continue;
+          }
+
+        DBG("ssl_ctx=%p loaded certificate authorities '%s'", ctx, path);
+     }
+   if (certificate_authorities_count && !*cfg.certificate_authorities)
+     {
+        ERR("ssl_ctx=%p none of the required certificate authorities were 
loaded!", ctx);
+        return EINVAL;
+     }
+
+   if (!ctx->did_certificates)
+     {
+        if (!SSL_CTX_set_cipher_list(ctx->ssl_ctx, 
EFL_NET_SSL_CONTEXT_CIPHERS))
+          {
+             err_ssl = ERR_peek_error_line_data(&err_file, &err_line, 
&err_data, &err_flags);
+             _efl_net_ssl_ctx_check_errors();
+
+             ERR("ssl_ctx=%p Could not set ciphers '%s' [%s:%d%s%s '%s']",
+                 ctx, EFL_NET_SSL_CONTEXT_CIPHERS,
+                 err_file, err_line,
+                 (err_flags & ERR_TXT_STRING) ? " " : "",
+                 (err_flags & ERR_TXT_STRING) ? err_data : "",
+                 ERR_reason_error_string(err_ssl));
+             return EINVAL;
+          }
+     }
+
+   return 0;
+}
+
+static void *
+efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx)
+{
+   return SSL_new(ctx->ssl_ctx);
+}
+
+static Eina_Error
+efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
+{
+   Eina_Error err;
+   unsigned long options;
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(ctx->ssl_ctx != NULL, EALREADY);
+
+   ctx->is_dialer = cfg.is_dialer;
+   if (ctx->is_dialer)
+     {
+        switch (cfg.cipher)
+          {
+           case EFL_NET_SSL_CIPHER_AUTO:
+              ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+              break;
+           case EFL_NET_SSL_CIPHER_SSLV3:
+#ifndef OPENSSL_NO_SSL3_METHOD
+              ctx->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+#else
+              ERR("ssl_ctx=%p SSLv3 is disabled in your OpenSSL build", ctx);
+#endif
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1_1:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_1_client_method());
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1_2:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
+              break;
+           default:
+              ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher);
+              return EINVAL;
+          }
+
+        EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->ssl_ctx, ENOSYS);
+     }
+   else
+     {
+        switch (cfg.cipher)
+          {
+           case EFL_NET_SSL_CIPHER_AUTO:
+              ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+              break;
+           case EFL_NET_SSL_CIPHER_SSLV3:
+#ifndef OPENSSL_NO_SSL3_METHOD
+              ctx->ssl_ctx = SSL_CTX_new(SSLv3_server_method());
+#else
+              ERR("ssl_ctx=%p SSLv3 is disabled in your OpenSSL build", ctx);
+#endif
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1_1:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_1_server_method());
+              break;
+           case EFL_NET_SSL_CIPHER_TLSV1_2:
+              ctx->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
+              break;
+           default:
+              ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher);
+              return EINVAL;
+          }
+
+        EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->ssl_ctx, ENOSYS);
+     }
+
+   options = SSL_CTX_get_options(ctx->ssl_ctx);
+   options |= SSL_OP_NO_SSLv2;
+   options |= SSL_OP_SINGLE_DH_USE;
+
+   if (cfg.cipher != EFL_NET_SSL_CIPHER_SSLV3)
+     options |= SSL_OP_NO_SSLv3;
+
+   SSL_CTX_set_options(ctx->ssl_ctx, options);
+
+   err = _efl_net_ssl_ctx_load_lists(ctx, cfg);
+   if (err)
+     {
+        ERR("ssl_ctx=%p failed to load certificate, private keys, CRL or CA", 
ctx);
+        goto error;
+     }
+
+   return 0;
+
+ error:
+   SSL_CTX_free(ctx->ssl_ctx);
+   ctx->ssl_ctx = NULL;
+   return err;
+}
+
+static void
+efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx)
+{
+   if (ctx->ssl_ctx)
+     {
+        SSL_CTX_free(ctx->ssl_ctx);
+        ctx->ssl_ctx = NULL;
+     }
+}
+
+static Eina_Error
+efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Verify_Mode 
verify_mode)
+{
+   int ssl_mode;
+
+   switch (verify_mode)
+     {
+      case EFL_NET_SSL_VERIFY_MODE_NONE:
+         ssl_mode = SSL_VERIFY_NONE;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_OPTIONAL:
+         ssl_mode = SSL_VERIFY_PEER;
+         break;
+      case EFL_NET_SSL_VERIFY_MODE_REQUIRED:
+         ssl_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+         break;
+      default:
+         ERR("unknown verify_mode=%d", verify_mode);
+         return EINVAL;
+     }
+
+   SSL_CTX_set_verify(ctx->ssl_ctx, ssl_mode, 
SSL_CTX_get_verify_callback(ctx->ssl_ctx));
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, 
Eina_Bool hostname_verify EINA_UNUSED)
+{
+   return 0;
+}
+
+static Eina_Error
+efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, const char 
*hostname EINA_UNUSED)
+{
+   return 0;
+}
diff --git a/src/lib/ecore_con/efl_net_ssl_types.eot 
b/src/lib/ecore_con/efl_net_ssl_types.eot
new file mode 100644
index 0000000..6556f7c
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_ssl_types.eot
@@ -0,0 +1,26 @@
+enum Efl.Net.Ssl.Verify_Mode {
+    [[Defines how remote peers should be verified.
+
+      @since 1.19
+    ]]
+    none, [[Do not verify peer]]
+    optional, [[If provided, verify. Otherwise proceed]]
+    required, [[Always verify and fail if certificate wasn't provided]]
+}
+
+enum Efl.Net.Ssl.Cipher {
+    [[Defines the SSL/TLS version to use.
+
+      Prefer 'auto' or one of the TLS variants.
+
+      \@note since it's very insecure, SSLv2 is not present. SSLv3
+      support depends on being available on the platform.
+
+      @since 1.19
+    ]]
+    auto, [[The default. Use the best your system supports, disables dangerous 
ciphers]]
+    sslv3, [[SSLv3, insecure and unsupported - DANGEROUS]]
+    tlsv1, [[TLSv1, secure and widely available]]
+    tlsv1_1, [[TLSv1.1, secure]]
+    tlsv1_2, [[TLSv1.2, secure]]
+}

-- 


Reply via email to