2
0
Эх сурвалжийг харах

[WIP] SSL support for eval (#9009)

* start on eval-ssl

* add authmode config, use it for now because we don't have certs yet

* apply Aurel fixes

* clean up a bit

* start on certs

* use int return like everyone else

* remove rogue -cclib

* move NativeSocket to eval.vm

* fix stupidity

* apply more Aurel fixes

* break everything

* see if this magically works

* remove unused include

* link

* package files

* try mac

* more libs

* try building on linux

* fix path

* more libs again

* get mbedtls from ppa

* rewrite alloc/free to use caml_alloc_custom

* set authmode

* initialize Mbedtls statics lazily

* move setSocket to Mbedtls

* remove x509_crl references

* deal with more Aurel comments that GItHub hid from me

* exit early if cert parsing fails

* rename native Certificate to X509Crt

* add some Certificate functions

* deal with next and fix issuer/subject returns

* be honest about what isn't implemented

* clean up

* work towards API completion

* implement Certificate.get_altNames

* add Key API

* load root certificates on OS X

* fix lib params in azure build

Co-authored-by: Andy Li <[email protected]>
Co-authored-by: Aurel <[email protected]>
Simon Krajewski 5 жил өмнө
parent
commit
2351868d6b

+ 5 - 2
Makefile

@@ -60,9 +60,12 @@ HAXE_VERSION=$(shell $(CURDIR)/$(HAXE_OUTPUT) -version 2>&1 | awk '{print $$1;}'
 HAXE_VERSION_SHORT=$(shell echo "$(HAXE_VERSION)" | grep -oE "^[0-9]+\.[0-9]+\.[0-9]+")
 
 ifneq ($(STATICLINK),0)
-	LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre -lz -Wl,-Bdynamic '
+	LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre -lz -lmbedtls -lmbedx509 -lmbedcrypto -Wl,-Bdynamic '
 else
-	LIB_PARAMS?= -cclib -lpcre -cclib -lz
+	LIB_PARAMS?= -cclib -lpcre -cclib -lz -cclib -lmbedtls -cclib -lmbedx509 -cclib -lmbedcrypto
+endif
+ifeq ($(SYSTEM_NAME),Mac)
+	LIB_PARAMS+= -cclib '-framework Security -framework CoreFoundation'
 endif
 
 all: haxe tools

+ 10 - 1
Makefile.win

@@ -42,7 +42,16 @@ ifdef FILTER
 CC_CMD=($(COMPILER) $(ALL_CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
 endif
 
-PACKAGE_FILES=$(HAXE_OUTPUT) $(HAXELIB_OUTPUT) std "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep zlib1.dll | sed -e 's/^\s*//')" "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libpcre-1.dll | sed -e 's/^\s*//')"
+ifeq ($(STATICLINK),0)
+	LIB_PARAMS = -cclib -lpcre -cclib -lz -cclib -lcrypt32 -cclib -lmbedtls -cclib -lmbedcrypto -cclib -lmbedx509
+endif
+
+PACKAGE_FILES=$(HAXE_OUTPUT) $(HAXELIB_OUTPUT) std \
+	"$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep zlib1.dll | sed -e 's/^\s*//')" \
+	"$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libpcre-1.dll | sed -e 's/^\s*//')" \
+	"$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedcrypto.dll | sed -e 's/^\s*//')" \
+	"$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedtls.dll | sed -e 's/^\s*//')" \
+	"$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedx509.dll | sed -e 's/^\s*//')"
 
 echo_package_files:
 	echo $(PACKAGE_FILES)

+ 3 - 2
extra/azure-pipelines/build-linux.yml

@@ -15,9 +15,10 @@ jobs:
         submodules: recursive
       - script: |
           set -ex
-          sudo add-apt-repository ppa:avsm/ppa -y # provides newer version of OCaml and OPAM
+          sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2
+          sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls
           sudo apt-get update -qqy
-          sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre3-dev zlib1g-dev libgtk2.0-dev ninja-build
+          sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre3-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build
         displayName: Install dependencies
       - template: install-neko-snapshot.yaml
         parameters:

+ 1 - 1
extra/azure-pipelines/build-mac.yml

@@ -33,7 +33,7 @@ jobs:
         displayName: Install OCaml libraries
       - script: |
           set -ex
-          opam config exec -- make -s -j`sysctl -n hw.ncpu` STATICLINK=1 "LIB_PARAMS=/usr/local/opt/zlib/lib/libz.a /usr/local/lib/libpcre.a" haxe
+          opam config exec -- make -s -j`sysctl -n hw.ncpu` STATICLINK=1 "LIB_PARAMS=/usr/local/opt/zlib/lib/libz.a /usr/local/lib/libpcre.a /usr/local/lib/libmbedtls.a /usr/local/lib/libmbedcrypto.a /usr/local/lib/libmbedx509.a -cclib '-framework Security -framework CoreFoundation'" haxe
           opam config exec -- make -s haxelib
           make -s package_bin package_installer_mac
           ls -l out

+ 3 - 1
extra/azure-pipelines/build-windows.yml

@@ -41,9 +41,11 @@ jobs:
       - powershell: |
           Set-PSDebug -Trace 1
           curl.exe -fsSL -o cygwin-setup.exe --retry 3 $(CYGWIN_SETUP)
-          Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $(CYG_ROOT) -l C:/tmp -s $(CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre-devel -P mingw64-$(MINGW_ARCH)-zlib -P mingw64-$(MINGW_ARCH)-gcc-core -P mingw64-$(MINGW_ARCH)-pcre" -Wait
+          Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $(CYG_ROOT) -l C:/tmp -s $(CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre-devel -P mbedtls-devel -P mingw64-$(MINGW_ARCH)-zlib -P mingw64-$(MINGW_ARCH)-gcc-core -P mingw64-$(MINGW_ARCH)-pcre" -Wait
           curl.exe -fsSL -o "opam.tar.xz" --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.2/opam$(ARCH).tar.xz
+          curl.exe -fsSL -o "libmbedtls.tar.xz" --retry 3 https://github.com/Simn/mingw64-mbedtls/releases/download/2.16.3/mingw64-$(MINGW_ARCH)-mbedtls-2.16.3-1.tar.xz
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'echo "$OLDPWD"')
+          & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -C / -xvf libmbedtls.tar.xz')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -xf opam.tar.xz')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && bash opam$(ARCH)/install.sh')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'opam init mingw "https://github.com/fdopen/opam-repository-mingw.git#opam2" --comp 4.07.0+mingw$(ARCH)c --switch 4.07.0+mingw$(ARCH)c --auto-setup --yes 2>&1')

+ 9 - 0
libs/mbedtls/dune

@@ -0,0 +1,9 @@
+(include_subdirs no)
+
+(library
+	(name mbedtls)
+	(c_names
+		mbedtls_stubs
+	)
+	(wrapped false)
+)

+ 69 - 0
libs/mbedtls/mbedtls.ml

@@ -0,0 +1,69 @@
+type mbedtls_ctr_drbg_context
+type mbedtls_entropy_context
+type mbedtls_ssl_config
+type mbedtls_ssl_context
+type mbedtls_x509_crt
+type mbedtls_pk_context
+
+type mbedtls_result = int
+
+type t_mbedtls_entropy_func = mbedtls_entropy_context -> bytes -> int -> mbedtls_result
+
+external mbedtls_strerror : int -> string = "ml_mbedtls_strerror"
+
+external mbedtls_ctr_drbg_init : unit -> mbedtls_ctr_drbg_context = "ml_mbedtls_ctr_drbg_init"
+external mbedtls_ctr_drbg_random : mbedtls_ctr_drbg_context -> bytes -> int -> mbedtls_result = "ml_mbedtls_ctr_drbg_random"
+external mbedtls_ctr_drbg_seed :
+	mbedtls_ctr_drbg_context ->
+	'a ->
+	string option ->
+	mbedtls_result = "ml_mbedtls_ctr_drbg_seed"
+
+external mbedtls_entropy_func : mbedtls_entropy_context -> bytes -> int -> mbedtls_result = "ml_mbedtls_entropy_func"
+external mbedtls_entropy_init : unit -> mbedtls_entropy_context = "ml_mbedtls_entropy_init"
+
+external mbedtls_ssl_conf_ca_chain : mbedtls_ssl_config -> mbedtls_x509_crt -> unit = "ml_mbedtls_ssl_conf_ca_chain"
+external mbedtls_ssl_config_authmode : mbedtls_ssl_config -> int -> unit = "ml_mbedtls_ssl_conf_authmode"
+external mbedtls_ssl_config_defaults : mbedtls_ssl_config -> int -> int -> int -> mbedtls_result = "ml_mbedtls_ssl_config_defaults"
+external mbedtls_ssl_config_init : unit -> mbedtls_ssl_config = "ml_mbedtls_ssl_config_init"
+external mbedtls_ssl_config_rng : mbedtls_ssl_config -> 'a -> unit = "ml_mbedtls_ssl_conf_rng"
+
+external mbedtls_ssl_init : unit -> mbedtls_ssl_context = "ml_mbedtls_ssl_init"
+external mbedtls_ssl_get_peer_cert : mbedtls_ssl_context -> mbedtls_x509_crt option = "ml_mbedtls_ssl_get_peer_cert"
+external mbedtls_ssl_handshake : mbedtls_ssl_context -> mbedtls_result = "ml_mbedtls_ssl_handshake"
+external mbedtls_ssl_read : mbedtls_ssl_context -> bytes -> int -> int -> mbedtls_result = "ml_mbedtls_ssl_read"
+external mbedtls_ssl_set_bio :
+	mbedtls_ssl_context ->
+	'a ->
+	('a -> bytes -> mbedtls_result) ->
+	('a -> bytes -> mbedtls_result) ->
+	unit = "ml_mbedtls_ssl_set_bio"
+external mbedtls_ssl_set_hostname : mbedtls_ssl_context -> string -> mbedtls_result = "ml_mbedtls_ssl_set_hostname"
+external mbedtls_ssl_setup : mbedtls_ssl_context -> mbedtls_ssl_config -> mbedtls_result = "ml_mbedtls_ssl_setup"
+external mbedtls_ssl_write : mbedtls_ssl_context -> bytes -> int -> int -> mbedtls_result = "ml_mbedtls_ssl_write"
+
+external mbedtls_pk_init : unit -> mbedtls_pk_context = "ml_mbedtls_pk_init"
+external mbedtls_pk_parse_key : mbedtls_pk_context -> bytes -> string option -> mbedtls_result = "ml_mbedtls_pk_parse_key"
+external mbedtls_pk_parse_keyfile : mbedtls_pk_context -> string -> string option -> mbedtls_result = "ml_mbedtls_pk_parse_keyfile"
+external mbedtls_pk_parse_public_keyfile : mbedtls_pk_context -> string -> mbedtls_result = "ml_mbedtls_pk_parse_public_keyfile"
+external mbedtls_pk_parse_public_key : mbedtls_pk_context -> bytes -> mbedtls_result = "ml_mbedtls_pk_parse_public_key"
+
+external mbedtls_x509_crt_init : unit -> mbedtls_x509_crt = "ml_mbedtls_x509_crt_init"
+external mbedtls_x509_next : mbedtls_x509_crt -> mbedtls_x509_crt option = "ml_mbedtls_x509_next"
+external mbedtls_x509_crt_parse : mbedtls_x509_crt -> bytes -> mbedtls_result = "ml_mbedtls_x509_crt_parse"
+external mbedtls_x509_crt_parse_file : mbedtls_x509_crt -> string -> mbedtls_result = "ml_mbedtls_x509_crt_parse_file"
+external mbedtls_x509_crt_parse_path : mbedtls_x509_crt -> string -> mbedtls_result = "ml_mbedtls_x509_crt_parse_path"
+
+external hx_cert_get_alt_names : mbedtls_x509_crt -> string array = "hx_cert_get_alt_names"
+external hx_cert_get_issuer : mbedtls_x509_crt -> string -> string option = "hx_cert_get_issuer"
+external hx_cert_get_notafter : mbedtls_x509_crt -> float = "hx_cert_get_notafter"
+external hx_cert_get_notbefore : mbedtls_x509_crt -> float = "hx_cert_get_notbefore"
+external hx_cert_get_subject : mbedtls_x509_crt -> string -> string option = "hx_cert_get_subject"
+
+(* glue *)
+
+external hx_cert_load_defaults : mbedtls_x509_crt -> int = "hx_cert_load_defaults"
+external hx_get_ssl_authmode_flags : unit -> (string * int) array = "hx_get_ssl_authmode_flags"
+external hx_get_ssl_endpoint_flags : unit -> (string * int) array = "hx_get_ssl_endpoint_flags"
+external hx_get_ssl_preset_flags : unit -> (string * int) array = "hx_get_ssl_preset_flags"
+external hx_get_ssl_transport_flags : unit -> (string * int) array = "hx_get_ssl_transport_flags"

+ 598 - 0
libs/mbedtls/mbedtls_stubs.c

@@ -0,0 +1,598 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+#ifdef __APPLE__
+#include <Security/Security.h>
+#endif
+
+#include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#include <caml/fail.h>
+#include <caml/callback.h>
+#include <caml/custom.h>
+
+#include "mbedtls/debug.h"
+#include "mbedtls/error.h"
+#include "mbedtls/config.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/certs.h"
+#include "mbedtls/oid.h"
+
+#define PVoid_val(v) (*((void**) Data_custom_val(v)))
+
+void debug(void* ctx, int debug_level, const char* file_name, int line, const char* message) {
+	printf("%s:%i: %s", file_name, line, message);
+}
+
+#define Val_none Val_int(0)
+
+static value Val_some(value v) {
+    CAMLparam1(v);
+    CAMLlocal1(some);
+    some = caml_alloc(1, 0);
+    Store_field(some, 0, v);
+    CAMLreturn(some);
+}
+
+CAMLprim value ml_mbedtls_strerror(value code) {
+	CAMLparam1(code);
+	CAMLlocal1(r);
+	char buf[128];
+	mbedtls_strerror(Int_val(code), buf, sizeof(buf));
+	r = caml_copy_string(buf);
+	CAMLreturn(r);
+}
+
+// CtrDrbg
+
+#define CtrDrbg_val(v) (*((mbedtls_ctr_drbg_context**) Data_custom_val(v)))
+
+static void ml_mbedtls_ctr_drbg_finalize(value v) {
+	mbedtls_ctr_drbg_context* ctr_drbg = CtrDrbg_val(v);
+	if (ctr_drbg != NULL) {
+		mbedtls_ctr_drbg_free(ctr_drbg);
+	}
+}
+
+static struct custom_operations ctr_drbg_ops = {
+	.identifier  = "ml_ctr_drbg",
+	.finalize    = ml_mbedtls_ctr_drbg_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_ctr_drbg_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&ctr_drbg_ops, sizeof(mbedtls_ctr_drbg_context*), 0, 1);
+	mbedtls_ctr_drbg_context* ctr_drbg = malloc(sizeof(mbedtls_ctr_drbg_context));
+	mbedtls_ctr_drbg_init(ctr_drbg);
+	CtrDrbg_val(obj) = ctr_drbg;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_ctr_drbg_random(value p_rng, value output, value output_len) {
+	CAMLparam3(p_rng, output, output_len);
+	CAMLreturn(Val_int(mbedtls_ctr_drbg_random(CtrDrbg_val(p_rng), String_val(output), Int_val(output_len))));
+}
+
+CAMLprim value ml_mbedtls_ctr_drbg_seed(value ctx, value p_entropy, value custom) {
+	CAMLparam2(ctx, custom);
+	CAMLreturn(Val_int(mbedtls_ctr_drbg_seed(CtrDrbg_val(ctx), mbedtls_entropy_func, PVoid_val(p_entropy), NULL, 0)));
+}
+
+// Entropy
+
+#define Entropy_val(v) (*((mbedtls_entropy_context**) Data_custom_val(v)))
+
+static void ml_mbedtls_entropy_finalize(value v) {
+	mbedtls_entropy_context* entropy = Entropy_val(v);
+	if (entropy != NULL) {
+		mbedtls_entropy_free(entropy);
+	}
+}
+
+static struct custom_operations entropy_ops = {
+	.identifier  = "ml_entropy",
+	.finalize    = ml_mbedtls_entropy_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_entropy_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&entropy_ops, sizeof(mbedtls_entropy_context*), 0, 1);
+	mbedtls_entropy_context* entropy = malloc(sizeof(mbedtls_entropy_context));
+	mbedtls_entropy_init(entropy);
+	Entropy_val(obj) = entropy;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_entropy_func(value data, value output, value len) {
+	CAMLparam3(data, output, len);
+	CAMLreturn(Val_int(mbedtls_entropy_func(PVoid_val(data), String_val(output), Int_val(len))));
+}
+
+// Certificate
+
+#define X509Crt_val(v) (*((mbedtls_x509_crt**) Data_custom_val(v)))
+
+static void ml_mbedtls_x509_crt_finalize(value v) {
+	mbedtls_x509_crt* x509_crt = X509Crt_val(v);
+	if (x509_crt != NULL) {
+		mbedtls_x509_crt_free(x509_crt);
+	}
+}
+
+static struct custom_operations x509_crt_ops = {
+	.identifier  = "ml_x509_crt",
+	.finalize    = ml_mbedtls_x509_crt_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_x509_crt_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&x509_crt_ops, sizeof(mbedtls_x509_crt*), 0, 1);
+	mbedtls_x509_crt* x509_crt = malloc(sizeof(mbedtls_x509_crt));
+	mbedtls_x509_crt_init(x509_crt);
+	X509Crt_val(obj) = x509_crt;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_x509_next(value chain) {
+	CAMLparam1(chain);
+	CAMLlocal2(r, obj);
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	if (cert->next == NULL) {
+		CAMLreturn(Val_none);
+	}
+	obj = caml_alloc_custom(&x509_crt_ops, sizeof(mbedtls_x509_crt*), 0, 1);
+	X509Crt_val(obj) = cert->next;
+	CAMLreturn(Val_some(obj));
+}
+
+CAMLprim value ml_mbedtls_x509_crt_parse(value chain, value bytes) {
+	CAMLparam2(chain, bytes);
+	const char* buf = String_val(bytes);
+	int len = caml_string_length(bytes);
+	CAMLreturn(Val_int(mbedtls_x509_crt_parse(X509Crt_val(chain), buf, len + 1)));
+}
+
+CAMLprim value ml_mbedtls_x509_crt_parse_file(value chain, value path) {
+	CAMLparam2(chain, path);
+	CAMLreturn(Val_int(mbedtls_x509_crt_parse_file(X509Crt_val(chain), String_val(path))));
+}
+
+CAMLprim value ml_mbedtls_x509_crt_parse_path(value chain, value path) {
+	CAMLparam2(chain, path);
+	CAMLreturn(Val_int(mbedtls_x509_crt_parse_path(X509Crt_val(chain), String_val(path))));
+}
+
+// Certificate Haxe API
+
+value caml_string_of_asn1_buf(mbedtls_asn1_buf* dat) {
+	CAMLparam0();
+	CAMLlocal1(s);
+	s = caml_alloc_string(dat->len);
+	memcpy(String_val(s), dat->p, dat->len);
+	CAMLreturn(s);
+}
+
+CAMLprim value hx_cert_get_alt_names(value chain) {
+	CAMLparam1(chain);
+	CAMLlocal1(obj);
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	if (cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME == 0 || &cert->subject_alt_names == NULL) {
+		obj = Atom(0);
+	} else {
+		mbedtls_asn1_sequence* cur = &cert->subject_alt_names;
+		int i = 0;
+		while (cur != NULL) {
+			++i;
+			cur = cur->next;
+		}
+		obj = caml_alloc(i, 0);
+		cur = &cert->subject_alt_names;
+		i = 0;
+		while (cur != NULL) {
+			Store_field(obj, i, caml_string_of_asn1_buf(&cur->buf));
+			++i;
+			cur = cur->next;
+		}
+	}
+	CAMLreturn(obj);
+}
+
+CAMLprim value hx_cert_get_subject(value chain, value objname) {
+	CAMLparam2(chain, objname);
+	mbedtls_x509_name *obj;
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	const char *oname, *rname;
+	obj = &cert->subject;
+	rname = String_val(objname);
+	while (obj != NULL) {
+		int r = mbedtls_oid_get_attr_short_name(&obj->oid, &oname);
+		if (r == 0 && strcmp(oname, rname) == 0) {
+			CAMLreturn(Val_some(caml_string_of_asn1_buf(&obj->val)));
+		}
+		obj = obj->next;
+	}
+	CAMLreturn(Val_none);
+}
+
+CAMLprim value hx_cert_get_issuer(value chain, value objname) {
+	CAMLparam2(chain, objname);
+	mbedtls_x509_name *obj;
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	int r;
+	const char *oname, *rname;
+	obj = &cert->issuer;
+	rname = String_val(objname);
+	while (obj != NULL) {
+		r = mbedtls_oid_get_attr_short_name(&obj->oid, &oname);
+		if (r == 0 && strcmp(oname, rname) == 0) {
+			CAMLreturn(Val_some(caml_string_of_asn1_buf(&obj->val)));
+		}
+		obj = obj->next;
+	}
+	CAMLreturn(Val_none);
+}
+
+time_t time_to_time_t(mbedtls_x509_time* t) {
+	struct tm info;
+	info.tm_year = t->year - 1900;
+	info.tm_mon = t->mon - 1;
+	info.tm_mday = t->day;
+	info.tm_hour = t->hour;
+	info.tm_min = t->min;
+	info.tm_sec = t->sec;
+	return mktime(&info);
+}
+
+CAMLprim value hx_cert_get_notafter(value chain) {
+	CAMLparam1(chain);
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	mbedtls_x509_time *t = &cert->valid_to;
+	time_t time = time_to_time_t(t);
+	CAMLreturn(caml_copy_double((double)time));
+}
+
+CAMLprim value hx_cert_get_notbefore(value chain) {
+	CAMLparam1(chain);
+	mbedtls_x509_crt* cert = X509Crt_val(chain);
+	mbedtls_x509_time *t = &cert->valid_from;
+	time_t time = time_to_time_t(t);
+	CAMLreturn(caml_copy_double((double)time));
+}
+
+// Config
+
+#define Config_val(v) (*((mbedtls_ssl_config**) Data_custom_val(v)))
+
+static void ml_mbedtls_ssl_config_finalize(value v) {
+	mbedtls_ssl_config* ssl_config = Config_val(v);
+	if (ssl_config != NULL) {
+		mbedtls_ssl_config_free(ssl_config);
+	}
+}
+
+static struct custom_operations ssl_config_ops = {
+	.identifier  = "ml_ssl_config",
+	.finalize    = ml_mbedtls_ssl_config_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_ssl_config_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&ssl_config_ops, sizeof(mbedtls_ssl_config*), 0, 1);
+	mbedtls_ssl_config* ssl_config = malloc(sizeof(mbedtls_ssl_config));
+	mbedtls_ssl_config_init(ssl_config);
+	Config_val(obj) = ssl_config;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_ssl_conf_authmode(value conf, value authmode) {
+	CAMLparam2(conf, authmode);
+	mbedtls_ssl_conf_authmode(Config_val(conf), Int_val(authmode));
+	CAMLreturn(Val_unit);
+}
+
+CAMLprim value ml_mbedtls_ssl_conf_ca_chain(value conf, value ca_chain) {
+	CAMLparam2(conf, ca_chain);
+	mbedtls_ssl_conf_ca_chain(Config_val(conf), X509Crt_val(ca_chain), NULL);
+	CAMLreturn(Val_unit);
+}
+
+CAMLprim value ml_mbedtls_ssl_config_defaults(value conf, value endpoint, value transport, value preset) {
+	CAMLparam4(conf, endpoint, transport, preset);
+	CAMLreturn(Val_int(mbedtls_ssl_config_defaults(Config_val(conf), Int_val(endpoint), Int_val(transport), Int_val(preset))));
+}
+
+CAMLprim value ml_mbedtls_ssl_conf_rng(value conf, value p_rng) {
+	CAMLparam2(conf, p_rng);
+	mbedtls_ssl_conf_rng(Config_val(conf), mbedtls_ctr_drbg_random, PVoid_val(p_rng));
+	CAMLreturn(Val_unit);
+}
+
+// Pk
+
+#define PkContext_val(v) (*((mbedtls_pk_context**) Data_custom_val(v)))
+
+static void ml_mbedtls_pk_context_finalize(value v) {
+	mbedtls_pk_context* pk_context = PkContext_val(v);
+	if (pk_context != NULL) {
+		mbedtls_pk_free(pk_context);
+	}
+}
+
+static struct custom_operations pk_context_ops = {
+	.identifier  = "ml_pk_context",
+	.finalize    = ml_mbedtls_pk_context_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_pk_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&pk_context_ops, sizeof(mbedtls_pk_context*), 0, 1);
+	mbedtls_pk_context* pk_context = malloc(sizeof(mbedtls_pk_context));
+	mbedtls_pk_init(pk_context);
+	PkContext_val(obj) = pk_context;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_pk_parse_key(value ctx, value key, value password) {
+	CAMLparam3(ctx, key, password);
+	const char* pwd = NULL;
+	size_t pwdlen = 0;
+	if (password != Val_none) {
+		pwd = String_val(Field(password, 0));
+		pwdlen = caml_string_length(Field(password, 0));
+	}
+	CAMLreturn(mbedtls_pk_parse_key(PkContext_val(ctx), String_val(key), caml_string_length(key) + 1, pwd, pwdlen));
+}
+
+CAMLprim value ml_mbedtls_pk_parse_keyfile(value ctx, value path, value password) {
+	CAMLparam3(ctx, path, password);
+	const char* pwd = NULL;
+	if (password != Val_none) {
+		pwd = String_val(Field(password, 0));
+	}
+	CAMLreturn(mbedtls_pk_parse_keyfile(PkContext_val(ctx), String_val(path), pwd));
+}
+
+CAMLprim value ml_mbedtls_pk_parse_public_key(value ctx, value key) {
+	CAMLparam2(ctx, key);
+	CAMLreturn(mbedtls_pk_parse_public_key(PkContext_val(ctx), String_val(key), caml_string_length(key) + 1));
+}
+
+CAMLprim value ml_mbedtls_pk_parse_public_keyfile(value ctx, value path) {
+	CAMLparam2(ctx, path);
+	CAMLreturn(mbedtls_pk_parse_public_keyfile(PkContext_val(ctx), String_val(path)));
+}
+
+// Ssl
+
+#define SslContext_val(v) (*((mbedtls_ssl_context**) Data_custom_val(v)))
+
+static void ml_mbedtls_ssl_context_finalize(value v) {
+	mbedtls_ssl_context* ssl_context = SslContext_val(v);
+	if (ssl_context != NULL) {
+		mbedtls_ssl_free(ssl_context);
+	}
+}
+
+static struct custom_operations ssl_context_ops = {
+	.identifier  = "ml_ssl_context",
+	.finalize    = ml_mbedtls_ssl_context_finalize,
+	.compare     = custom_compare_default,
+	.hash        = custom_hash_default,
+	.serialize   = custom_serialize_default,
+	.deserialize = custom_deserialize_default,
+};
+
+CAMLprim value ml_mbedtls_ssl_init(void) {
+	CAMLparam0();
+	CAMLlocal1(obj);
+	obj = caml_alloc_custom(&ssl_context_ops, sizeof(mbedtls_ssl_context*), 0, 1);
+	mbedtls_ssl_context* ssl_context = malloc(sizeof(mbedtls_ssl_context));
+	mbedtls_ssl_init(ssl_context);
+	SslContext_val(obj) = ssl_context;
+	CAMLreturn(obj);
+}
+
+CAMLprim value ml_mbedtls_ssl_get_peer_cert(value ssl) {
+	CAMLparam1(ssl);
+	CAMLlocal1(obj);
+	mbedtls_ssl_context* ssl_context = SslContext_val(ssl);
+	mbedtls_x509_crt* crt = (mbedtls_x509_crt*)mbedtls_ssl_get_peer_cert(ssl_context);
+	if (crt == NULL) {
+		CAMLreturn(Val_none);
+	}
+	obj = caml_alloc_custom(&x509_crt_ops, sizeof(mbedtls_x509_crt*), 0, 1);
+	X509Crt_val(obj) = crt;
+	CAMLreturn(Val_some(obj));
+}
+
+CAMLprim value ml_mbedtls_ssl_handshake(value ssl) {
+	CAMLparam1(ssl);
+	CAMLreturn(Val_int(mbedtls_ssl_handshake(SslContext_val(ssl))));
+}
+
+CAMLprim value ml_mbedtls_ssl_read(value ssl, value buf, value pos, value len) {
+	CAMLparam4(ssl, buf, pos, len);
+	CAMLreturn(Val_int(mbedtls_ssl_read(SslContext_val(ssl), String_val(buf) + Int_val(pos), Int_val(len))));
+}
+
+static int bio_write_cb(void* ctx, const unsigned char* buf, size_t len) {
+	CAMLparam0();
+	CAMLlocal3(r, s, vctx);
+	vctx = (value)ctx;
+	s = caml_alloc_string(len);
+	memcpy(String_val(s), buf, len);
+	r = caml_callback2(Field(vctx, 1), Field(vctx, 0), s);
+	CAMLreturn(Int_val(r));
+}
+
+static int bio_read_cb(void* ctx, unsigned char* buf, size_t len) {
+	CAMLparam0();
+	CAMLlocal3(r, s, vctx);
+	vctx = (value)ctx;
+	s = caml_alloc_string(len);
+	r = caml_callback2(Field(vctx, 2), Field(vctx, 0), s);
+	memcpy(buf, String_val(s), len);
+	CAMLreturn(Int_val(r));
+}
+
+CAMLprim value ml_mbedtls_ssl_set_bio(value ssl, value p_bio, value f_send, value f_recv) {
+	CAMLparam4(ssl, p_bio, f_send, f_recv);
+	CAMLlocal1(ctx);
+	ctx = caml_alloc(3, 0);
+	Store_field(ctx, 0, p_bio);
+	Store_field(ctx, 1, f_send);
+	Store_field(ctx, 2, f_recv);
+	mbedtls_ssl_set_bio(SslContext_val(ssl), (void*)ctx, bio_write_cb, bio_read_cb, NULL);
+	CAMLreturn(Val_unit);
+}
+
+CAMLprim value ml_mbedtls_ssl_set_hostname(value ssl, value hostname) {
+	CAMLparam2(ssl, hostname);
+	CAMLreturn(Val_int(mbedtls_ssl_set_hostname(SslContext_val(ssl), String_val(hostname))));
+}
+
+CAMLprim value ml_mbedtls_ssl_setup(value ssl, value conf) {
+	CAMLparam2(ssl, conf);
+	CAMLreturn(Val_int(mbedtls_ssl_setup(SslContext_val(ssl), Config_val(conf))));
+}
+
+CAMLprim value ml_mbedtls_ssl_write(value ssl, value buf, value pos, value len) {
+	CAMLparam4(ssl, buf, pos, len);
+	CAMLreturn(Val_int(mbedtls_ssl_write(SslContext_val(ssl), String_val(buf) + Int_val(pos), Int_val(len))));
+}
+
+// glue
+
+CAMLprim value hx_cert_load_defaults(value certificate) {
+	CAMLparam1(certificate);
+	int r = 1;
+
+	mbedtls_x509_crt *chain = X509Crt_val(certificate);
+
+	#ifdef _WIN32
+	HCERTSTORE store;
+	PCCERT_CONTEXT cert;
+
+	if (store = CertOpenSystemStore(0, "Root")) {
+		cert = NULL;
+		while (cert = CertEnumCertificatesInStore(store, cert)) {
+			r = mbedtls_x509_crt_parse_der(chain, (unsigned char *)cert->pbCertEncoded, cert->cbCertEncoded);
+			if (r != 0) {
+				CAMLreturn(Val_int(r));
+			}
+		}
+		CertCloseStore(store, 0);
+	}
+	#endif
+
+	#ifdef __APPLE__
+	CFMutableDictionaryRef search;
+	CFArrayRef result;
+	SecKeychainRef keychain;
+	SecCertificateRef item;
+	CFDataRef dat;
+	// Load keychain
+	if (SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain", &keychain) == errSecSuccess) {
+		// Search for certificates
+		search = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
+		CFDictionarySetValue(search, kSecClass, kSecClassCertificate);
+		CFDictionarySetValue(search, kSecMatchLimit, kSecMatchLimitAll);
+		CFDictionarySetValue(search, kSecReturnRef, kCFBooleanTrue);
+		CFDictionarySetValue(search, kSecMatchSearchList, CFArrayCreate(NULL, (const void **)&keychain, 1, NULL));
+		if (SecItemCopyMatching(search, (CFTypeRef *)&result) == errSecSuccess) {
+			CFIndex n = CFArrayGetCount(result);
+			for (CFIndex i = 0; i < n; i++) {
+				item = (SecCertificateRef)CFArrayGetValueAtIndex(result, i);
+
+				// Get certificate in DER format
+				dat = SecCertificateCopyData(item);
+				if (dat) {
+					r = mbedtls_x509_crt_parse_der(chain, (unsigned char *)CFDataGetBytePtr(dat), CFDataGetLength(dat));
+					CFRelease(dat);
+					if (r != 0) {
+						CAMLreturn(Val_int(r));
+					}
+				}
+			}
+		}
+		CFRelease(keychain);
+	}
+	#endif
+
+	CAMLreturn(Val_int(r));
+}
+
+static value build_fields(int num_fields, const char* names[], int values[]) {
+	CAMLparam0();
+	CAMLlocal2(ret, tuple);
+	ret = caml_alloc(num_fields, 0);
+	for (int i = 0; i < num_fields; ++i) {
+		tuple = caml_alloc_tuple(2);
+		Store_field(tuple, 0, caml_copy_string(names[i]));
+		Store_field(tuple, 1, Val_int(values[i]));
+		Store_field(ret, i, tuple);
+	}
+	CAMLreturn(ret);
+}
+
+CAMLprim value hx_get_ssl_authmode_flags(value unit) {
+	CAMLparam1(unit);
+	const char* names[] = {"SSL_VERIFY_NONE", "SSL_VERIFY_OPTIONAL", "SSL_VERIFY_REQUIRED"};
+	int values[] = {MBEDTLS_SSL_VERIFY_NONE, MBEDTLS_SSL_VERIFY_OPTIONAL, MBEDTLS_SSL_VERIFY_REQUIRED};
+	CAMLreturn(build_fields(sizeof(values) / sizeof(values[0]), names, values));
+}
+
+CAMLprim value hx_get_ssl_endpoint_flags(value unit) {
+	CAMLparam1(unit);
+	const char* names[] = {"SSL_IS_CLIENT", "SSL_IS_SERVER"};
+	int values[] = {MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_IS_SERVER};
+	CAMLreturn(build_fields(sizeof(values) / sizeof(values[0]), names, values));
+}
+
+CAMLprim value hx_get_ssl_preset_flags(value unit) {
+	CAMLparam1(unit);
+	const char* names[] = {"SSL_PRESET_DEFAULT", "SSL_PRESET_SUITEB"};
+	int values[] = {MBEDTLS_SSL_PRESET_DEFAULT, MBEDTLS_SSL_PRESET_SUITEB};
+	CAMLreturn(build_fields(sizeof(values) / sizeof(values[0]), names, values));
+}
+
+CAMLprim value hx_get_ssl_transport_flags(value unit) {
+	CAMLparam1(unit);
+	const char* names[] = {"SSL_TRANSPORT_STREAM", "SSL_TRANSPORT_DATAGRAM"};
+	int values[] = {MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_TRANSPORT_DATAGRAM};
+	CAMLreturn(build_fields(sizeof(values) / sizeof(values[0]), names, values));
+}

+ 1 - 1
src/dune

@@ -9,7 +9,7 @@
 (library
 	(name haxe)
 	(libraries
-		extc extproc extlib_leftovers ilib javalib neko objsize pcre swflib ttflib ziplib
+		extc extproc extlib_leftovers ilib javalib mbedtls neko objsize pcre swflib ttflib ziplib
 		json
 		unix str threads dynlink
 		xml-light extlib ptmap sha

+ 8 - 1
src/macro/eval/evalHash.ml

@@ -112,7 +112,7 @@ let key___init__ = hash "__init__"
 let key_new = hash "new"
 let key_questionmark = hash "?"
 let key_haxe_StackItem = hash "haxe.StackItem"
-let key_sys_net__Socket_NativeSocket = hash "sys.net._Socket.NativeSocket"
+let key_eval_vm_NativeSocket = hash "eval.vm.NativeSocket"
 let key_ip = hash "ip"
 let key_port = hash "port"
 let key_sys_net_Socket = hash "sys.net.Socket"
@@ -130,3 +130,10 @@ let key_sys_net_Mutex = hash "sys.thread.Mutex"
 let key_sys_net_Lock = hash "sys.thread.Lock"
 let key_sys_net_Tls = hash "sys.thread.Tls"
 let key_sys_net_Deque = hash "sys.thread.Deque"
+
+let key_mbedtls_Config = hash "mbedtls.Config"
+let key_mbedtls_CtrDrbg = hash "mbedtls.CtrDrbg"
+let key_mbedtls_Entropy = hash "mbedtls.Entropy"
+let key_mbedtls_PkContext = hash "mbedtls.PkContext"
+let key_mbedtls_Ssl = hash "mbedtls.Ssl"
+let key_mbedtls_X509Crt = hash "mbedtls.X509Crt"

+ 202 - 0
src/macro/eval/evalSsl.ml

@@ -0,0 +1,202 @@
+open EvalHash
+open EvalValue
+open EvalEncode
+open EvalDecode
+open EvalExceptions
+open Mbedtls
+
+let as_x509_crt vthis = match vthis with
+	| VInstance {ikind = IMbedtlsX509Crt i} -> i
+	| _ -> unexpected_value vthis "X509Crt"
+
+let as_config vthis = match vthis with
+	| VInstance {ikind = IMbedtlsConfig i} -> i
+	| _ -> unexpected_value vthis "Config"
+
+let as_socket vthis = match vthis with
+	| VInstance {ikind = ISocket sock} -> sock
+	| _ -> unexpected_value vthis "NativeSocket"
+
+let as_ctr_drbg vthis = match vthis with
+	| VInstance {ikind = IMbedtlsCtrDrbg i} -> i
+	| _ -> unexpected_value vthis "CtrDrbg"
+
+let as_entropy vthis = match vthis with
+	| VInstance {ikind = IMbedtlsEntropy i} -> i
+	| _ -> unexpected_value vthis "Entropy"
+
+let as_pk_context vthis = match vthis with
+	| VInstance {ikind = IMbedtlsPkContext i} -> i
+	| _ -> unexpected_value vthis "PkContext"
+
+let as_ssl vthis = match vthis with
+	| VInstance {ikind = IMbedtlsSsl ctx} -> ctx
+	| _ -> unexpected_value vthis "Ssl"
+
+let init_constructors add =
+	add key_mbedtls_Config
+		(fun _ ->
+			let cfg = mbedtls_ssl_config_init() in
+			encode_instance key_mbedtls_Config ~kind:(IMbedtlsConfig cfg)
+		);
+	add key_mbedtls_CtrDrbg
+		(fun _ ->
+			let ctr = mbedtls_ctr_drbg_init() in
+			encode_instance key_mbedtls_CtrDrbg ~kind:(IMbedtlsCtrDrbg ctr)
+		);
+	add key_mbedtls_Entropy
+		(fun _ ->
+			let entropy = mbedtls_entropy_init() in
+			encode_instance key_mbedtls_Entropy ~kind:(IMbedtlsEntropy entropy)
+		);
+	add key_mbedtls_PkContext
+		(fun _ ->
+			let pk = mbedtls_pk_init() in
+			encode_instance key_mbedtls_PkContext ~kind:(IMbedtlsPkContext pk)
+		);
+	add key_mbedtls_Ssl
+		(fun _ ->
+			let ssl = mbedtls_ssl_init() in
+			encode_instance key_mbedtls_Ssl ~kind:(IMbedtlsSsl ssl)
+		);
+	add key_mbedtls_X509Crt
+		(fun _ ->
+			let cert = mbedtls_x509_crt_init() in
+			encode_instance key_mbedtls_X509Crt ~kind:(IMbedtlsX509Crt cert)
+		)
+
+let init_fields init_fields builtins =
+	let socket_send socket bytes =
+		Unix.send socket bytes 0 (Bytes.length bytes) []
+	in
+	let socket_receive socket bytes =
+		Unix.recv socket bytes 0 (Bytes.length bytes) []
+	in
+	let native_cert this =
+		as_x509_crt (EvalField.field this (hash "native"))
+	in
+	init_fields builtins (["sys";"ssl"],"Certificate") [] [
+		"get_altNames",vifun0 (fun this ->
+			let x509_crt = native_cert this  in
+			let a = hx_cert_get_alt_names x509_crt in
+			VArray (EvalArray.create (Array.map encode_string a))
+		);
+		"get_notAfter",vifun0 (fun this ->
+			let x509_crt = native_cert this  in
+			let f = hx_cert_get_notafter x509_crt in
+			encode_instance key_Date ~kind:(IDate f)
+		);
+		"get_notBefore",vifun0 (fun this ->
+			let x509_crt = native_cert this in
+			let f = hx_cert_get_notbefore x509_crt in
+			encode_instance key_Date ~kind:(IDate f)
+		);
+		"issuer",vifun1 (fun this field ->
+			let x509_crt = native_cert this in
+			match hx_cert_get_issuer x509_crt (decode_string field) with
+			| Some s -> encode_string s
+			| None -> vnull
+		);
+		"subject",vifun1 (fun this field ->
+			let x509_crt = native_cert this in
+			match hx_cert_get_subject x509_crt (decode_string field) with
+			| Some s -> encode_string s
+			| None -> vnull
+		);
+	];
+	init_fields builtins (["sys";"ssl"],"Mbedtls") [
+		"loadDefaults",vfun1 (fun this ->
+			vint (hx_cert_load_defaults (as_x509_crt this));
+		);
+		"setSocket",vfun2 (fun this socket ->
+			let ctx = as_ssl this in
+			let socket = as_socket socket in
+			mbedtls_ssl_set_bio ctx socket socket_send socket_receive;
+			vnull
+		);
+	] [];
+	init_fields builtins (["mbedtls"],"X509Crt") [] [
+		"next",vifun0 (fun this ->
+			match mbedtls_x509_next (as_x509_crt this) with
+			| None -> vnull
+			| Some cert -> encode_instance key_mbedtls_X509Crt ~kind:(IMbedtlsX509Crt cert)
+		);
+		"parse",vifun1 (fun this bytes ->
+			vint (mbedtls_x509_crt_parse (as_x509_crt this) (decode_bytes bytes));
+		);
+		"parse_file",vifun1 (fun this path ->
+			vint (mbedtls_x509_crt_parse_file (as_x509_crt this) (decode_string path));
+		);
+		"parse_path",vifun1 (fun this path ->
+			vint (mbedtls_x509_crt_parse_path (as_x509_crt this) (decode_string path));
+		);
+	];
+	init_fields builtins (["mbedtls"],"Config") [] [
+		"authmode",vifun1 (fun this authmode ->
+			mbedtls_ssl_config_authmode (as_config this) (decode_int authmode);
+			vnull;
+		);
+		"ca_chain",vifun1 (fun this ca_chain ->
+			mbedtls_ssl_conf_ca_chain (as_config this) (as_x509_crt ca_chain);
+			vnull;
+		);
+		"defaults",vifun3 (fun this endpoint transport preset ->
+			vint (mbedtls_ssl_config_defaults (as_config this) (decode_int endpoint) (decode_int transport) (decode_int preset));
+		);
+		"rng",vifun1(fun this p_rng ->
+			mbedtls_ssl_config_rng (as_config this) (as_ctr_drbg p_rng);
+			vnull
+		)
+	];
+	init_fields builtins (["mbedtls"],"CtrDrbg") [] [
+		"random",vifun2 (fun this output output_len ->
+			vint (mbedtls_ctr_drbg_random (as_ctr_drbg this) (decode_bytes output) (decode_int output_len));
+		);
+		"seed",vifun2(fun this entropy custom ->
+			vint (mbedtls_ctr_drbg_seed (as_ctr_drbg this) (as_entropy entropy) (match custom with VString s -> Some s.sstring | _ -> None))
+		)
+	];
+	init_fields builtins (["mbedtls"],"Error") [
+		"strerror",vfun1 (fun code -> encode_string (mbedtls_strerror (decode_int code)));
+	] [];
+	init_fields builtins (["mbedtls"],"PkContext") [] [
+		"parse_key",vifun2 (fun this key password ->
+			vint (mbedtls_pk_parse_key (as_pk_context this) (decode_bytes key) (match password with VNull -> None | _ -> Some (decode_string password)));
+		);
+		"parse_keyfile",vifun2 (fun this path password ->
+			vint (mbedtls_pk_parse_keyfile (as_pk_context this) (decode_string path) (match password with VNull -> None | _ -> Some (decode_string password)));
+		);
+		"parse_public_key",vifun1 (fun this key ->
+			vint (mbedtls_pk_parse_public_key (as_pk_context this) (decode_bytes key));
+		);
+		"parse_public_keyfile",vifun1 (fun this path ->
+			vint (mbedtls_pk_parse_public_keyfile (as_pk_context this) (decode_string path));
+		);
+	];
+	init_fields builtins (["mbedtls"],"Ssl") [] [
+		"get_peer_cert",vifun0 (fun this ->
+			match mbedtls_ssl_get_peer_cert (as_ssl this) with
+			| None -> vnull
+			| Some cert -> encode_instance key_mbedtls_X509Crt ~kind:(IMbedtlsX509Crt cert)
+		);
+		"handshake",vifun0 (fun this ->
+			vint (mbedtls_ssl_handshake (as_ssl this));
+		);
+		"read",vifun3(fun this buf pos len ->
+			vint (mbedtls_ssl_read (as_ssl this) (decode_bytes buf) (decode_int pos) (decode_int len);)
+		);
+		"set_hostname",vifun1 (fun this hostname ->
+			vint (mbedtls_ssl_set_hostname (as_ssl this) (decode_string hostname));
+		);
+		"setup",vifun1 (fun this conf ->
+			vint (mbedtls_ssl_setup (as_ssl this) (as_config conf))
+		);
+		"write",vifun3(fun this buf pos len ->
+			vint (mbedtls_ssl_write (as_ssl this) (decode_bytes buf) (decode_int pos) (decode_int len);)
+		);
+	];
+	let statics a = List.map (fun (s,i) -> s,vint i) (Array.to_list a) in
+	init_fields builtins (["mbedtls"],"SslAuthmode") (statics (hx_get_ssl_authmode_flags())) [];
+	init_fields builtins (["mbedtls"],"SslEndpoint") (statics (hx_get_ssl_endpoint_flags())) [];
+	init_fields builtins (["mbedtls"],"SslPreset") (statics (hx_get_ssl_preset_flags())) [];
+	init_fields builtins (["mbedtls"],"SslTransport") (statics (hx_get_ssl_transport_flags())) [];

+ 8 - 6
src/macro/eval/evalStdLib.ml

@@ -1999,7 +1999,7 @@ module StdSocket = struct
 	let accept = vifun0 (fun vthis ->
 		let this = this vthis in
 		let socket,_ = catch_unix_error Unix.accept this in
-		encode_instance key_sys_net__Socket_NativeSocket ~kind:(ISocket socket)
+		encode_instance key_eval_vm_NativeSocket ~kind:(ISocket socket)
 	)
 
 	let bind = vifun2 (fun vthis host port ->
@@ -3177,9 +3177,9 @@ let init_constructors builtins =
 				encode_instance key_sys_io__Process_NativeProcess ~kind:(IProcess (try Process.run cmd args with Failure msg -> exc_string msg))
 			| _ -> assert false
 		);
-	add key_sys_net__Socket_NativeSocket
+	add key_eval_vm_NativeSocket
 		(fun _ ->
-			encode_instance key_sys_net__Socket_NativeSocket ~kind:(ISocket ((catch_unix_error Unix.socket Unix.PF_INET Unix.SOCK_STREAM) 0))
+			encode_instance key_eval_vm_NativeSocket ~kind:(ISocket ((catch_unix_error Unix.socket Unix.PF_INET Unix.SOCK_STREAM) 0))
 		);
 	add key_haxe_zip_Compress
 		(fun vl -> match vl with
@@ -3230,7 +3230,8 @@ let init_constructors builtins =
 	add key_sys_net_Deque
 		(fun _ ->
 			encode_instance key_sys_net_Deque ~kind:(IDeque (Deque.create()))
-		)
+		);
+	EvalSsl.init_constructors add
 
 let init_empty_constructors builtins =
 	let h = builtins.empty_constructor_builtins in
@@ -3526,7 +3527,7 @@ let init_standard_library builtins =
 		"encode",StdSha1.encode;
 		"make",StdSha1.make;
 	] [];
-	init_fields builtins (["sys";"net";"_Socket"],"NativeSocket") [
+	init_fields builtins (["eval";"vm"],"NativeSocket") [
 		"select",StdSocket.select;
 	] [
 		"accept",StdSocket.accept;
@@ -3663,4 +3664,5 @@ let init_standard_library builtins =
 	] [
 		"addChar",StdUtf8.addChar;
 		"toString",StdUtf8.toString;
-	]
+	];
+	EvalSsl.init_fields init_fields builtins

+ 7 - 0
src/macro/eval/evalValue.ml

@@ -165,6 +165,13 @@ and vinstance_kind =
 	| ITypeDecl of Type.module_type
 	| ILazyType of (Type.tlazy ref) * (unit -> value)
 	| IRef of Obj.t
+	(* SSL *)
+	| IMbedtlsConfig of Mbedtls.mbedtls_ssl_config
+	| IMbedtlsCtrDrbg of Mbedtls.mbedtls_ctr_drbg_context
+	| IMbedtlsEntropy of Mbedtls.mbedtls_entropy_context
+	| IMbedtlsPkContext of Mbedtls.mbedtls_pk_context
+	| IMbedtlsSsl of Mbedtls.mbedtls_ssl_context
+	| IMbedtlsX509Crt of Mbedtls.mbedtls_x509_crt
 	| INormal
 
 and vinstance = {

+ 10 - 0
std/eval/_std/mbedtls/Config.hx

@@ -0,0 +1,10 @@
+package mbedtls;
+
+extern class Config {
+	function new():Void;
+
+	function authmode(authmode:SslAuthmode):Void;
+	function ca_chain(ca_chain:X509Crt):Void;
+	function defaults(endpoint:SslEndpoint, transport:SslTransport, preset:SslPreset):Int;
+	function rng<T>(p_rng:T):Void;
+}

+ 10 - 0
std/eval/_std/mbedtls/CtrDrbg.hx

@@ -0,0 +1,10 @@
+package mbedtls;
+
+import haxe.io.Bytes;
+
+extern class CtrDrbg {
+	function new():Void;
+
+	function random(output:Bytes, output_len:Int):Int;
+	function seed(entropy:Entropy, ?custom:String):Int;
+}

+ 5 - 0
std/eval/_std/mbedtls/Entropy.hx

@@ -0,0 +1,5 @@
+package mbedtls;
+
+extern class Entropy {
+	function new():Void;
+}

+ 5 - 0
std/eval/_std/mbedtls/Error.hx

@@ -0,0 +1,5 @@
+package mbedtls;
+
+class Error {
+	extern static public function strerror(code:Int):String;
+}

+ 12 - 0
std/eval/_std/mbedtls/PkContext.hx

@@ -0,0 +1,12 @@
+package mbedtls;
+
+import haxe.io.Bytes;
+
+extern class PkContext {
+	function new():Void;
+
+	function parse_key(key:Bytes, ?pwd:String):Int;
+	function parse_keyfile(path:String, ?password:String):Int;
+	function parse_public_key(key:Bytes):Int;
+	function parse_public_keyfile(path:String):Int;
+}

+ 15 - 0
std/eval/_std/mbedtls/Ssl.hx

@@ -0,0 +1,15 @@
+package mbedtls;
+
+import mbedtls.X509Crt;
+import haxe.io.Bytes;
+
+extern class Ssl {
+	function new():Void;
+
+	function get_peer_cert():Null<X509Crt>;
+	function handshake():Int;
+	function read(buf:Bytes, pos:Int, len:Int):Int;
+	function set_hostname(hostname:String):Int;
+	function setup(conf:Config):Int;
+	function write(buf:Bytes, pos:Int, len:Int):Int;
+}

+ 8 - 0
std/eval/_std/mbedtls/SslAuthmode.hx

@@ -0,0 +1,8 @@
+package mbedtls;
+
+@:native("mbedtls.SslAuthmode")
+extern enum abstract SslAuthmode(Int) {
+	var SSL_VERIFY_NONE;
+	var SSL_VERIFY_OPTIONAL;
+	var SSL_VERIFY_REQUIRED;
+}

+ 7 - 0
std/eval/_std/mbedtls/SslEndpoint.hx

@@ -0,0 +1,7 @@
+package mbedtls;
+
+@:native("mbedtls.SslEndpoint")
+extern enum abstract SslEndpoint(Int) {
+	var SSL_IS_CLIENT;
+	var SSL_IS_SERVER;
+}

+ 7 - 0
std/eval/_std/mbedtls/SslPreset.hx

@@ -0,0 +1,7 @@
+package mbedtls;
+
+@:native("mbedtls.SslPreset")
+extern enum abstract SslPreset(Int) {
+	var SSL_PRESET_DEFAULT;
+	var SSL_PRESET_SUITEB;
+}

+ 7 - 0
std/eval/_std/mbedtls/SslTransport.hx

@@ -0,0 +1,7 @@
+package mbedtls;
+
+@:native("mbedtls.SslTransport")
+extern enum abstract SslTransport(Int) {
+	var SSL_TRANSPORT_STREAM;
+	var SSL_TRANSPORT_DATAGRAM;
+}

+ 12 - 0
std/eval/_std/mbedtls/X509Crt.hx

@@ -0,0 +1,12 @@
+package mbedtls;
+
+import haxe.io.Bytes;
+
+extern class X509Crt {
+	function new():Void;
+
+	function next():Null<X509Crt>;
+	function parse(buf:Bytes):Int;
+	function parse_file(path:String):Int;
+	function parse_path(path:String):Int;
+}

+ 1 - 21
std/eval/_std/sys/net/Socket.hx

@@ -23,27 +23,7 @@
 package sys.net;
 
 import haxe.io.Error;
-
-extern private class NativeSocket {
-	function new():Void;
-	function accept():NativeSocket;
-	function bind(host:Int, port:Int):Void;
-	function close():Void;
-	function connect(host:Int, port:Int):Void;
-	function host():{ip:Int, port:Int};
-	function listen(connections:Int):Void;
-	function peer():{ip:Int, port:Int};
-	function receive(buf:haxe.io.Bytes, pos:Int, len:Int):Int;
-	function receiveChar():Int;
-	function send(buf:haxe.io.Bytes, pos:Int, len:Int):Int;
-	function sendChar(char:Int):Void;
-	function setFastSend(b:Bool):Void;
-	function setTimeout(timeout:Float):Void;
-	function shutdown(read:Bool, write:Bool):Void;
-
-	public static function select(read:Array<Socket>, write:Array<Socket>, others:Array<Socket>,
-		?timeout:Float):{read:Array<Socket>, write:Array<Socket>, others:Array<Socket>};
-}
+import eval.vm.NativeSocket;
 
 private class SocketOutput extends haxe.io.Output {
 	var socket:NativeSocket;

+ 102 - 0
std/eval/_std/sys/ssl/Certificate.hx

@@ -0,0 +1,102 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.ssl;
+
+import haxe.io.Bytes;
+import sys.ssl.Mbedtls;
+import mbedtls.X509Crt;
+
+@:coreApi
+class Certificate {
+	var native:X509Crt;
+
+	function new(native:X509Crt) {
+		this.native = native;
+	}
+
+	public static function loadFile(file:String):Certificate {
+		var cert = new X509Crt();
+		cert.parse_file(file);
+		return new Certificate(cert);
+	}
+
+	public static function loadPath(path:String):Certificate {
+		var cert = new X509Crt();
+		cert.parse_path(path);
+		return new Certificate(cert);
+	}
+
+	public static function fromString(str:String):Certificate {
+		var cert = new X509Crt();
+		trace(mbedtls.Error.strerror(cert.parse(Bytes.ofString(str))));
+		return new Certificate(cert);
+	}
+
+	public static function loadDefaults():Certificate {
+		var cert = new X509Crt();
+		Mbedtls.loadDefaultCertificates(cert);
+		return new Certificate(cert);
+	}
+
+	public var commonName(get, null):Null<String>;
+
+	public var altNames(get, null):Array<String>;
+
+	public var notBefore(get, null):Date;
+
+	public var notAfter(get, null):Date;
+
+	extern public function subject(field:String):Null<String>;
+
+	extern public function issuer(field:String):Null<String>;
+
+	public function next():Null<Certificate> {
+		var cert = native.next();
+		if (cert == null) {
+			return null;
+		}
+		return new Certificate(cert);
+	}
+
+	public function add(pem:String):Void {
+		native.parse(Bytes.ofString(pem));
+	}
+
+	public function addDER(der:Bytes):Void {
+		native.parse(der);
+	}
+
+	private function get_commonName():Null<String> {
+		return subject("CN");
+	}
+
+	extern private function get_altNames():Array<String>;
+
+	extern private function get_notBefore():Date;
+
+	extern private function get_notAfter():Date;
+
+	private inline function getNative():X509Crt {
+		return native;
+	}
+}

+ 69 - 0
std/eval/_std/sys/ssl/Key.hx

@@ -0,0 +1,69 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package sys.ssl;
+
+import haxe.io.Bytes;
+import mbedtls.PkContext;
+
+@:coreApi
+class Key {
+	var native:PkContext;
+
+	function new() {
+		native = new PkContext();
+	}
+
+	static public function loadFile(file:String, ?isPublic:Bool, ?pass:String):Key {
+		var key = new Key();
+		var code = if (isPublic) {
+			key.native.parse_public_keyfile(file);
+		} else {
+			key.native.parse_keyfile(file, pass);
+		}
+		if (code != 0) {
+			throw(mbedtls.Error.strerror(code));
+		}
+		return key;
+	}
+
+	static function parse(data:Bytes, isPublic:Bool, ?pass:String):Key {
+		var key = new Key();
+		var code = if (isPublic) {
+			key.native.parse_public_key(data);
+		} else {
+			key.native.parse_key(data);
+		}
+		if (code != 0) {
+			throw(mbedtls.Error.strerror(code));
+		}
+		return key;
+	}
+
+	static public function readPEM(data:String, isPublic:Bool, ?pass:String):Key {
+		return parse(Bytes.ofString(data), isPublic, pass);
+	}
+
+	static public function readDER(data:haxe.io.Bytes, isPublic:Bool):Key {
+		return parse(data, isPublic);
+	}
+}

+ 66 - 0
std/eval/_std/sys/ssl/Mbedtls.hx

@@ -0,0 +1,66 @@
+package sys.ssl;
+
+import eval.vm.NativeSocket;
+import mbedtls.Ssl;
+import mbedtls.Entropy;
+import mbedtls.CtrDrbg;
+import mbedtls.X509Crt;
+
+class Mbedtls {
+	static var entropy:Null<Entropy>;
+	static var ctr:Null<CtrDrbg>;
+
+	static public function getDefaultEntropy() {
+		if (entropy == null) {
+			entropy = new Entropy();
+		}
+		return entropy;
+	}
+
+	static public function getDefaultCtrDrbg() {
+		if (ctr == null) {
+			ctr = new CtrDrbg();
+			ctr.seed(getDefaultEntropy());
+		}
+		return ctr;
+	}
+
+	static public function loadDefaultCertificates(certificate:X509Crt) {
+		if (loadDefaults(certificate) == 0) {
+			return;
+		}
+		var defPaths = switch (Sys.systemName()) {
+			case "Linux":
+				[
+					"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
+					"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
+					"/etc/ssl/ca-bundle.pem", // OpenSUSE
+					"/etc/pki/tls/cacert.pem", // OpenELEC
+					"/etc/ssl/certs", // SLES10/SLES11
+					"/system/etc/security/cacerts" // Android
+				];
+			case "BSD":
+				[
+					"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
+					"/etc/ssl/cert.pem", // OpenBSD
+					"/etc/openssl/certs/ca-certificates.crt", // NetBSD
+				];
+			case "Android":
+				["/system/etc/security/cacerts"];
+			default:
+				[];
+		}
+		for (path in defPaths) {
+			if (sys.FileSystem.exists(path)) {
+				if (sys.FileSystem.isDirectory(path))
+					certificate.parse_path(path);
+				else
+					certificate.parse_file(path);
+			}
+		}
+	}
+
+	extern static public function setSocket(ssl:Ssl, socket:NativeSocket):Int;
+
+	extern static function loadDefaults(certificate:X509Crt):Int;
+}

+ 206 - 0
std/eval/_std/sys/ssl/Socket.hx

@@ -0,0 +1,206 @@
+package sys.ssl;
+
+import haxe.io.Bytes;
+import eval.vm.NativeSocket;
+import mbedtls.Config;
+import mbedtls.Ssl;
+
+private class SocketInput extends haxe.io.Input {
+	@:allow(sys.ssl.Socket) private var socket:Socket;
+	var readBuf:Bytes;
+
+	public function new(s:Socket) {
+		this.socket = s;
+		readBuf = Bytes.alloc(1);
+	}
+
+	public override function readByte() {
+		socket.handshake();
+		var r = @:privateAccess socket.ssl.read(readBuf, 0, 1);
+		if (r == -1)
+			throw haxe.io.Error.Blocked;
+		else if (r < 0)
+			throw new haxe.io.Eof();
+		return readBuf.get(0);
+	}
+
+	public override function readBytes(buf:haxe.io.Bytes, pos:Int, len:Int):Int {
+		if (pos < 0 || len < 0 || ((pos + len) : UInt) > (buf.length : UInt))
+			throw haxe.io.Error.OutsideBounds;
+		socket.handshake();
+		var r = @:privateAccess socket.ssl.read(buf, pos, len);
+		if (r == -1)
+			throw haxe.io.Error.Blocked;
+		else if (r <= 0)
+			throw new haxe.io.Eof();
+		return r;
+	}
+
+	public override function close() {
+		super.close();
+		if (socket != null)
+			socket.close();
+	}
+}
+
+private class SocketOutput extends haxe.io.Output {
+	@:allow(sys.ssl.Socket) private var socket:Socket;
+	var writeBuf:Bytes;
+
+	public function new(s:Socket) {
+		this.socket = s;
+		writeBuf = Bytes.alloc(1);
+	}
+
+	public override function writeByte(c:Int) {
+		socket.handshake();
+		writeBuf.set(0, c);
+		var r = @:privateAccess socket.ssl.write(writeBuf, 0, 1);
+		if (r == -1)
+			throw haxe.io.Error.Blocked;
+		else if (r < 0)
+			throw new haxe.io.Eof();
+	}
+
+	public override function writeBytes(buf:haxe.io.Bytes, pos:Int, len:Int):Int {
+		if (pos < 0 || len < 0 || ((pos + len) : UInt) > (buf.length : UInt))
+			throw haxe.io.Error.OutsideBounds;
+		socket.handshake();
+		var r = @:privateAccess socket.ssl.write(buf, pos, len);
+		if (r == -1)
+			throw haxe.io.Error.Blocked;
+		else if (r < 0)
+			throw new haxe.io.Eof();
+		return r;
+	}
+
+	public override function close() {
+		super.close();
+		if (socket != null)
+			socket.close();
+	}
+}
+
+@:coreApi
+class Socket extends sys.net.Socket {
+	public static var DEFAULT_VERIFY_CERT:Null<Bool> = true;
+
+	public static var DEFAULT_CA:Null<Certificate>;
+
+	private var conf:Config;
+	private var ssl:Ssl;
+
+	public var verifyCert:Null<Bool>;
+
+	private var caCert:Null<Certificate>;
+	private var hostname:String;
+
+	private var handshakeDone:Bool;
+	private var isBlocking:Bool = true;
+
+	override function init(socket:NativeSocket):Void {
+		this.socket = socket;
+		input = new SocketInput(this);
+		output = new SocketOutput(this);
+		if (DEFAULT_VERIFY_CERT && DEFAULT_CA == null) {
+			DEFAULT_CA = Certificate.loadDefaults();
+		}
+		verifyCert = DEFAULT_VERIFY_CERT;
+		caCert = DEFAULT_CA;
+	}
+
+	public override function connect(host:sys.net.Host, port:Int):Void {
+		conf = buildConfig(false);
+		ssl = new Ssl();
+		ssl.setup(conf);
+		Mbedtls.setSocket(ssl, socket);
+		handshakeDone = false;
+		if (hostname == null)
+			hostname = host.host;
+		if (hostname != null)
+			ssl.set_hostname(hostname);
+		socket.connect(host.ip, port);
+		if (isBlocking)
+			handshake();
+	}
+
+	public function handshake():Void {
+		if (!handshakeDone) {
+			var r = ssl.handshake();
+			if (r == 0)
+				handshakeDone = true;
+			else if (r == -1)
+				throw haxe.io.Error.Blocked;
+			else
+				throw mbedtls.Error.strerror(r);
+		}
+	}
+
+	override function setBlocking(b:Bool):Void {
+		super.setBlocking(b);
+		isBlocking = b;
+	}
+
+	public function setCA(cert:Certificate):Void {
+		caCert = cert;
+	}
+
+	public function setHostname(name:String):Void {
+		hostname = name;
+	}
+
+	public override function close():Void {
+		super.close();
+		var input:SocketInput = cast input;
+		var output:SocketOutput = cast output;
+		@:privateAccess input.socket = output.socket = null;
+		input.close();
+		output.close();
+	}
+
+	public override function bind(host:sys.net.Host, port:Int):Void {
+		conf = buildConfig(true);
+
+		socket.bind(host.ip, port);
+	}
+
+	public override function accept():Socket {
+		var c = socket.accept();
+		var cssl = new Ssl();
+		cssl.setup(conf);
+		Mbedtls.setSocket(cssl, c);
+
+		var s = Type.createEmptyInstance(sys.ssl.Socket);
+		s.socket = c;
+		s.ssl = cssl;
+		s.input = new SocketInput(s);
+		s.output = new SocketOutput(s);
+		s.handshakeDone = false;
+
+		return s;
+	}
+
+	public function addSNICertificate(cbServernameMatch:String->Bool, cert:Certificate, key:Key):Void {
+		throw "Not implemented";
+	}
+
+	public function peerCertificate():Certificate {
+		return @:privateAccess new Certificate(ssl.get_peer_cert());
+	}
+
+	public function setCertificate(cert:Certificate, key:Key):Void {
+		throw "Not implemented";
+	}
+
+	private function buildConfig(server:Bool):Config {
+		var conf = new Config();
+		conf.defaults(server ? SSL_IS_SERVER : SSL_IS_CLIENT, SSL_TRANSPORT_STREAM, SSL_PRESET_DEFAULT);
+		conf.rng(Mbedtls.getDefaultCtrDrbg());
+
+		if (caCert != null) {
+			conf.ca_chain(@:privateAccess caCert.getNative());
+		}
+		conf.authmode(if (verifyCert) SSL_VERIFY_REQUIRED else if (verifyCert == null) SSL_VERIFY_OPTIONAL else SSL_VERIFY_NONE);
+		return conf;
+	}
+}

+ 46 - 0
std/eval/vm/NativeSocket.hx

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package eval.vm;
+
+import sys.net.Socket;
+
+extern class NativeSocket {
+	function new():Void;
+	function accept():NativeSocket;
+	function bind(host:Int, port:Int):Void;
+	function close():Void;
+	function connect(host:Int, port:Int):Void;
+	function host():{ip:Int, port:Int};
+	function listen(connections:Int):Void;
+	function peer():{ip:Int, port:Int};
+	function receive(buf:haxe.io.Bytes, pos:Int, len:Int):Int;
+	function receiveChar():Int;
+	function send(buf:haxe.io.Bytes, pos:Int, len:Int):Int;
+	function sendChar(char:Int):Void;
+	function setFastSend(b:Bool):Void;
+	function setTimeout(timeout:Float):Void;
+	function shutdown(read:Bool, write:Bool):Void;
+
+	public static function select(read:Array<Socket>, write:Array<Socket>, others:Array<Socket>,
+		?timeout:Float):{read:Array<Socket>, write:Array<Socket>, others:Array<Socket>};
+}

+ 8 - 8
std/sys/Http.hx

@@ -104,7 +104,7 @@ class Http extends haxe.http.HttpBase {
 				sock = new java.net.SslSocket();
 				#elseif python
 				sock = new python.net.SslSocket();
-				#elseif (!no_ssl && (hxssl || hl || cpp || (neko && !(macro || interp))))
+				#elseif (!no_ssl && (hxssl || hl || cpp || (neko && !(macro || interp) || eval)))
 				sock = new sys.ssl.Socket();
 				#elseif (neko || cpp)
 				throw "Https is only supported with -lib hxssl";
@@ -239,9 +239,9 @@ class Http extends haxe.http.HttpBase {
 			else
 				sock.connect(new Host(host), port);
 			if (multipart)
-				writeBody(b,file.io,file.size,boundary,sock)
+				writeBody(b, file.io, file.size, boundary, sock)
 			else
-				writeBody(b,null,0,null,sock);
+				writeBody(b, null, 0, null, sock);
 			readHttpResponse(api, sock);
 			sock.close();
 		} catch (e:Dynamic) {
@@ -478,13 +478,13 @@ class Http extends haxe.http.HttpBase {
 	}
 
 	/**
-		Makes a synchronous request to `url`.
+	Makes a synchronous request to `url`.
 
-		This creates a new Http instance and makes a GET request by calling its
-		`request(false)` method.
+	This creates a new Http instance and makes a GET request by calling its
+	`request(false)` method.
 
-		If `url` is null, the result is unspecified.
-	**/
+	If `url` is null, the result is unspecified.
+**/
 	public static function requestUrl(url:String):String {
 		var h = new Http(url);
 		var r = null;

+ 1 - 0
tests/Brewfile

@@ -7,3 +7,4 @@ brew "pcre"
 brew "awscli"
 brew "cmake"
 brew "pkg-config"
+brew "mbedtls"