Blob Blame History Raw
From 95d4f53fdaf75fa4b3a90322974ee0efea9af049 Mon Sep 17 00:00:00 2001
From: Curtis Vogt <curtis.vogt@gmail.com>
Date: Mon, 3 Jul 2017 12:25:27 -0500
Subject: [PATCH] Update to LibGit2 v0.26.0 (#22614)

* Update to LibGit2 v0.26.0

* Remove libgit2-free-config patch
---
 base/libgit2/libgit2.jl                            |   51 +-
 .../md5                                            |    1 -
 .../sha512                                         |    1 -
 deps/libgit2.mk                                    |   28 +-
 deps/libgit2.version                               |    4 +-
 deps/patches/libgit2-free-config.patch             |   25 -
 deps/patches/libgit2-gitconfig-symlink.patch       |   27 -
 deps/patches/libgit2-mbedtls-verify.patch          |  147 +-
 deps/patches/libgit2-mbedtls-writer-fix.patch      |   30 -
 deps/patches/libgit2-mbedtls.patch                 | 4827 ++++++++++++++++++--
 deps/patches/libgit2-remote-push-NULL.patch        |   26 -
 test/libgit2.jl                                    |   18 +-
 12 files changed, 4449 insertions(+), 736 deletions(-)
 delete mode 100644 deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5
 delete mode 100644 deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512
 delete mode 100644 deps/patches/libgit2-free-config.patch
 delete mode 100644 deps/patches/libgit2-gitconfig-symlink.patch
 delete mode 100644 deps/patches/libgit2-mbedtls-writer-fix.patch
 delete mode 100644 deps/patches/libgit2-remote-push-NULL.patch

diff --git a/base/libgit2/libgit2.jl b/base/libgit2/libgit2.jl
index 22b0f12..163318a 100644
--- a/base/libgit2/libgit2.jl
+++ b/base/libgit2/libgit2.jl
@@ -891,33 +891,13 @@ function set_ssl_cert_locations(cert_loc)
     cert_file = isfile(cert_loc) ? cert_loc : Cstring(C_NULL)
     cert_dir  = isdir(cert_loc) ? cert_loc : Cstring(C_NULL)
     cert_file == C_NULL && cert_dir == C_NULL && return
-    # TODO FIX https://github.com/libgit2/libgit2/pull/3935#issuecomment-253910017
-    #ccall((:git_libgit2_opts, :libgit2), Cint,
-    #      (Cint, Cstring, Cstring),
-    #      Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
-    ENV["SSL_CERT_FILE"] = cert_file
-    ENV["SSL_CERT_DIR"] = cert_dir
+    @check ccall((:git_libgit2_opts, :libgit2), Cint,
+          (Cint, Cstring, Cstring),
+          Cint(Consts.SET_SSL_CERT_LOCATIONS), cert_file, cert_dir)
 end
 
 function __init__()
-    # Look for OpenSSL env variable for CA bundle (linux only)
-    # windows and macOS use the OS native security backends
-    old_ssl_cert_dir = Base.get(ENV, "SSL_CERT_DIR", nothing)
-    old_ssl_cert_file = Base.get(ENV, "SSL_CERT_FILE", nothing)
-    @static if is_linux()
-        cert_loc = if "SSL_CERT_DIR" in keys(ENV)
-            ENV["SSL_CERT_DIR"]
-        elseif "SSL_CERT_FILE" in keys(ENV)
-            ENV["SSL_CERT_FILE"]
-        else
-            # If we have a bundled ca cert file, point libgit2 at that so SSL connections work.
-            abspath(ccall(:jl_get_julia_home, Any, ()),Base.DATAROOTDIR,"julia","cert.pem")
-        end
-        set_ssl_cert_locations(cert_loc)
-    end
-
-    err = ccall((:git_libgit2_init, :libgit2), Cint, ())
-    err > 0 || throw(ErrorException("error initializing LibGit2 module"))
+    @check ccall((:git_libgit2_init, :libgit2), Cint, ())
     REFCOUNT[] = 1
 
     atexit() do
@@ -927,21 +907,18 @@ function __init__()
         end
     end
 
+    # Look for OpenSSL env variable for CA bundle (linux only)
+    # windows and macOS use the OS native security backends
     @static if is_linux()
-        if old_ssl_cert_dir != Base.get(ENV, "SSL_CERT_DIR", "")
-            if old_ssl_cert_dir === nothing
-                delete!(ENV, "SSL_CERT_DIR")
-            else
-                ENV["SSL_CERT_DIR"] = old_ssl_cert_dir
-            end
-        end
-        if old_ssl_cert_file != Base.get(ENV, "SSL_CERT_FILE", "")
-            if old_ssl_cert_file === nothing
-                delete!(ENV, "SSL_CERT_FILE")
-            else
-                ENV["SSL_CERT_FILE"] = old_ssl_cert_file
-            end
+        cert_loc = if "SSL_CERT_DIR" in keys(ENV)
+            ENV["SSL_CERT_DIR"]
+        elseif "SSL_CERT_FILE" in keys(ENV)
+            ENV["SSL_CERT_FILE"]
+        else
+            # If we have a bundled ca cert file, point libgit2 at that so SSL connections work.
+            abspath(ccall(:jl_get_julia_home, Any, ()), Base.DATAROOTDIR, "julia", "cert.pem")
         end
+        set_ssl_cert_locations(cert_loc)
     end
 end
 
diff --git a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5 b/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5
deleted file mode 100644
index ccc33a3..0000000
--- a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/md5
+++ /dev/null
@@ -1 +0,0 @@
-fb1f1140f9b55fc8499caa960382fc03
diff --git a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512 b/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512
deleted file mode 100644
index cc5156e..0000000
--- a/deps/checksums/libgit2-2fcb8705e584ca61f6c4657525c9d2713f6a39d2.tar.gz/sha512
+++ /dev/null
@@ -1 +0,0 @@
-2086269728e14c0ec38f322b01f89d4d98a31a395cab3937591826a232193f00ec07bafe938ad99d70c74cb695af26c7228513019bfe6fc06427229bd2e098cf
diff --git a/deps/libgit2.mk b/deps/libgit2.mk
index 129fbe6..adf487f 100644
--- a/deps/libgit2.mk
+++ b/deps/libgit2.mk
@@ -37,7 +37,7 @@ LIBGIT2_OPTS += -DCURL_INCLUDE_DIRS=$(build_includedir) -DCURL_LIBRARIES="-L$(bu
 endif
 
 ifeq ($(OS),Linux)
-LIBGIT2_OPTS += -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON -DCMAKE_INSTALL_RPATH="\$$ORIGIN"
+LIBGIT2_OPTS += -DUSE_HTTPS=ON -DTLS_BACKEND="mbedTLS" -DCMAKE_INSTALL_RPATH="\$$ORIGIN" 
 endif
 
 ifeq ($(OS),FreeBSD)
@@ -79,29 +79,14 @@ $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied: $(LIBGIT2_SRC_PATH)/so
 		patch -p1 -f < $(SRCDIR)/patches/libgit2-agent-nonfatal.patch
 	echo 1 > $@
 
-$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls.patch-applied
-	cd $(LIBGIT2_SRC_PATH) && \
-		patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-writer-fix.patch
-	echo 1 > $@
-
-$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied
+$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied
 	cd $(LIBGIT2_SRC_PATH) && \
 		patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-verify.patch
 	echo 1 > $@
 
-$(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied
-	cd $(LIBGIT2_SRC_PATH) && \
-		patch -p1 -f < $(SRCDIR)/patches/libgit2-gitconfig-symlink.patch
-	echo 1 > $@
-
-$(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied
-	cd $(LIBGIT2_SRC_PATH) && \
-		patch -p1 -f < $(SRCDIR)/patches/libgit2-free-config.patch
-	echo 1 > $@
-
-$(LIBGIT2_SRC_PATH)/libgit2-remote-push-NULL.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied
+$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-fixup.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted | $(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied
 	cd $(LIBGIT2_SRC_PATH) && \
-		patch -p1 -f < $(SRCDIR)/patches/libgit2-remote-push-NULL.patch
+		patch -p1 -f < $(SRCDIR)/patches/libgit2-mbedtls-fixup.patch
 	echo 1 > $@
 
 $(build_datarootdir)/julia/cert.pem: $(CERTFILE)
@@ -112,11 +97,8 @@ $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: \
 	$(LIBGIT2_SRC_PATH)/libgit2-mbedtls.patch-applied \
 	$(LIBGIT2_SRC_PATH)/libgit2-ssh.patch-applied \
 	$(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied \
-	$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-writer-fix.patch-applied \
 	$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-verify.patch-applied \
-	$(LIBGIT2_SRC_PATH)/libgit2-gitconfig-symlink.patch-applied \
-	$(LIBGIT2_SRC_PATH)/libgit2-free-config.patch-applied \
-	$(LIBGIT2_SRC_PATH)/libgit2-remote-push-NULL.patch-applied
+	$(LIBGIT2_SRC_PATH)/libgit2-mbedtls-fixup.patch-applied
 
 ifneq ($(CERTFILE),)
 $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(build_datarootdir)/julia/cert.pem
diff --git a/deps/libgit2.version b/deps/libgit2.version
index f5596b2..306384f 100644
--- a/deps/libgit2.version
+++ b/deps/libgit2.version
@@ -1,2 +1,2 @@
-LIBGIT2_BRANCH=v0.25.1
-LIBGIT2_SHA1=2fcb8705e584ca61f6c4657525c9d2713f6a39d2
+LIBGIT2_BRANCH=v0.26.0
+LIBGIT2_SHA1=15e119375018fba121cf58e02a9f17fe22df0df8
diff --git a/deps/patches/libgit2-free-config.patch b/deps/patches/libgit2-free-config.patch
deleted file mode 100644
index 767aa81..0000000
--- a/deps/patches/libgit2-free-config.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 5552237686dae90fb9c22f0088de488b48654828 Mon Sep 17 00:00:00 2001
-From: Yichao Yu <yyc1992@gmail.com>
-Date: Sat, 29 Apr 2017 12:28:35 -0400
-Subject: [PATCH] Do not free config when creating remote
-
-The regression was introduced in 22261344de18b3cc60ee6937468d66a6a6a28875
----
- src/remote.c | 1 -
- 1 file changed, 1 deletion(-)
-
-diff --git a/src/remote.c b/src/remote.c
-index d3132f75c..15752ab27 100644
---- a/src/remote.c
-+++ b/src/remote.c
-@@ -260,7 +260,6 @@ on_error:
- 	if (error)
- 		git_remote_free(remote);
- 
--	git_config_free(config);
- 	git_buf_free(&canonical_url);
- 	git_buf_free(&var);
- 	return error;
--- 
-2.12.2
-
diff --git a/deps/patches/libgit2-gitconfig-symlink.patch b/deps/patches/libgit2-gitconfig-symlink.patch
deleted file mode 100644
index bd4e983..0000000
--- a/deps/patches/libgit2-gitconfig-symlink.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 86a8cd9f6a039889801b5bec865a4bc3deb30f47 Mon Sep 17 00:00:00 2001
-From: Sven Strickroth2 <email@cs-ware.de>
-Date: Mon, 20 Mar 2017 11:21:00 +0100
-Subject: [PATCH] filebuf: fix resolving absolute symlinks
-
-The symlink destination is always concatenated to the original path. Fix
-this by using `git_buf_sets` instead of `git_buf_puts`.
----
- src/filebuf.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/filebuf.c b/src/filebuf.c
-index ef68b16f4..825b9c04c 100644
---- a/src/filebuf.c
-+++ b/src/filebuf.c
-@@ -246,7 +246,7 @@ static int resolve_symlink(git_buf *out, const char *path)
- 
- 		root = git_path_root(target.ptr);
- 		if (root >= 0) {
--			if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
-+			if ((error = git_buf_sets(&curpath, target.ptr)) < 0)
- 				goto cleanup;
- 		} else {
- 			git_buf dir = GIT_BUF_INIT;
--- 
-2.12.2
-
diff --git a/deps/patches/libgit2-mbedtls-verify.patch b/deps/patches/libgit2-mbedtls-verify.patch
index 4c454c3..eb0b385 100644
--- a/deps/patches/libgit2-mbedtls-verify.patch
+++ b/deps/patches/libgit2-mbedtls-verify.patch
@@ -1,99 +1,100 @@
-diff --git a/src/mbedtls_stream.c b/src/mbedtls_stream.c
-index bcc2e8b..4706102 100644
---- a/src/mbedtls_stream.c
-+++ b/src/mbedtls_stream.c
-@@ -221,82 +221,34 @@ static int ssl_teardown(mbedtls_ssl_context *ssl)
-     return ret;
+commit eefe88eaf8c5c5b7c9a596da79e68dca3a3234d4
+Author: Curtis Vogt <curtis.vogt@gmail.com>
+Date:   Thu Jun 29 16:30:53 2017 -0500
+
+    Use mbedtls certificate verification
+    
+    Letting mbedtls handle all certficate verification and removed the
+    custom alternative names and common name checking.
+
+diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c
+index 0376ee4..e456ea8 100644
+--- a/src/streams/mbedtls.c
++++ b/src/streams/mbedtls.c
+@@ -228,82 +228,19 @@ static int ssl_teardown(mbedtls_ssl_context *ssl)
+ 	return ret;
  }
  
 -static int check_host_name(const char *name, const char *host)
 -{
--    if (!strcasecmp(name, host))
--        return 0;
+-	if (!strcasecmp(name, host))
+-		return 0;
 -
--    if (gitno__match_host(name, host) < 0)
--        return -1;
+-	if (gitno__match_host(name, host) < 0)
+-		return -1;
 -
--    return 0;
+-	return 0;
 -}
 -
  static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host)
  {
--    const mbedtls_x509_crt *cert;
--    const mbedtls_x509_sequence *alts;
--    int ret, matched = -1;
-+    mbedtls_x509_crt *cert;
-+    uint32_t flags;
-+    int ret = -1;
-     size_t sn_size = 512;
--    char subject_name[sn_size], alt_name[sn_size];
+-	const mbedtls_x509_crt *cert;
+-	const mbedtls_x509_sequence *alts;
+-	int ret, matched = -1;
+-	size_t sn_size = 512;
+-	char subject_name[sn_size], alt_name[sn_size];
 -
-+    char buf[sn_size];
++	int ret = -1;
++	(void)(host);  // Suppress unused parameter warning
  
-     if (( ret = mbedtls_ssl_get_verify_result(ssl) ) != 0) {
--        char vrfy_buf[512];
--        mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), "  ! ", ret );
--        giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf);
-+        mbedtls_x509_crt_verify_info(buf, sn_size, "  ! ", ret);
-+        giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", buf);
-         return GIT_ECERTIFICATE;
-     }
+ 	if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) {
+ 		char vrfy_buf[512];
+-		mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), "  ! ", ret );
+-		giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf);
++		mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret);
++		giterr_set(GITERR_SSL, "The SSL certificate is invalid: %x - %s", ret, vrfy_buf);
+ 		return GIT_ECERTIFICATE;
+ 	}
  
--    cert = mbedtls_ssl_get_peer_cert(ssl);
-+    cert = (mbedtls_x509_crt*) mbedtls_ssl_get_peer_cert(ssl);
-     if (!cert) {
-         giterr_set(GITERR_SSL, "the server did not provide a certificate");
-         return -1;
-     }
- 
--    /* Check the alternative names */
--    alts = &cert->subject_alt_names;
--    while (alts != NULL && matched != 1) {
--        // Buffer is too small
--        if( alts->buf.len >= sn_size )
--            goto on_error;
+-	cert = mbedtls_ssl_get_peer_cert(ssl);
+-	if (!cert) {
+-		giterr_set(GITERR_SSL, "the server did not provide a certificate");
+-		return -1;
+-	}
 -
--        memcpy(alt_name, alts->buf.p, alts->buf.len);
--        alt_name[alts->buf.len] = '\0';
+-	/* Check the alternative names */
+-	alts = &cert->subject_alt_names;
+-	while (alts != NULL && matched != 1) {
+-		// Buffer is too small
+-		if( alts->buf.len >= sn_size )
+-			goto on_error;
 -
--        if (!memchr(alt_name, '\0', alts->buf.len)) {
--            if (check_host_name(alt_name, host) < 0)
--                matched = 0;
--            else
--                matched = 1;
--        }
+-		memcpy(alt_name, alts->buf.p, alts->buf.len);
+-		alt_name[alts->buf.len] = '\0';
 -
--        alts = alts->next;
-+    if (mbedtls_x509_crt_verify(cert, git__ssl_conf->ca_chain, NULL, host, &flags, NULL, NULL) != 0) {
-+        mbedtls_x509_crt_verify_info(buf, sn_size, "", flags);
-+        buf[strlen(buf) - 1] = '\0';  // Remove trailing newline
-+        giterr_set(GITERR_SSL, buf);
-+        return GIT_ECERTIFICATE;
-     }
--    if (matched == 0)
--        goto cert_fail_name;
+-		if (!memchr(alt_name, '\0', alts->buf.len)) {
+-			if (check_host_name(alt_name, host) < 0)
+-				matched = 0;
+-			else
+-				matched = 1;
+-		}
 -
--    if (matched == 1)
--        return 0;
+-		alts = alts->next;
+-	}
+-	if (matched == 0)
+-		goto cert_fail_name;
 -
--    /* If no alternative names are available, check the common name */
--    ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject);
--    if (ret == 0)
--        goto on_error;
--    if (memchr(subject_name, '\0', ret))
--        goto cert_fail_name;
+-	if (matched == 1)
+-		return 0;
 -
--    if (check_host_name(subject_name, host) < 0)
--        goto cert_fail_name;
- 
-     return 0;
+-	/* If no alternative names are available, check the common name */
+-	ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject);
+-	if (ret == 0)
+-		goto on_error;
+-	if (memchr(subject_name, '\0', ret))
+-		goto cert_fail_name;
+-
+-	if (check_host_name(subject_name, host) < 0)
+-		goto cert_fail_name;
+-
+ 	return 0;
 -
 -on_error:
--    return ssl_set_error(ssl, 0);
+-	return ssl_set_error(ssl, 0);
 -
 -cert_fail_name:
--    giterr_set(GITERR_SSL, "hostname does not match certificate");
--    return GIT_ECERTIFICATE;
+-	giterr_set(GITERR_SSL, "hostname does not match certificate");
+-	return GIT_ECERTIFICATE;
  }
  
  typedef struct {
diff --git a/deps/patches/libgit2-mbedtls-writer-fix.patch b/deps/patches/libgit2-mbedtls-writer-fix.patch
deleted file mode 100644
index d80a637..0000000
--- a/deps/patches/libgit2-mbedtls-writer-fix.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-diff -rup libgit2-211e117a0590583a720c53172406f34186c543bd/src/mbedtls_stream.c libgit2-211e117a0590583a720c53172406f34186c543bd-fix-write/src/mbedtls_stream.c
---- libgit2-211e117a0590583a720c53172406f34186c543bd/src/mbedtls_stream.c   2016-10-24 17:10:21.000000000 -0400
-+++ libgit2-211e117a0590583a720c53172406f34186c543bd-fix-write/src/mbedtls_stream.c 2016-10-24 17:04:26.000000000 -0400
-@@ -368,16 +368,20 @@ static int mbedtls_set_proxy(git_stream 
- 
- ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags)
- {
-+    size_t read = 0;
-     mbedtls_stream *st = (mbedtls_stream *) stream;
--    int ret;
- 
-     GIT_UNUSED(flags);
- 
--    if ((ret = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0) {
--        return ssl_set_error(st->ssl, ret);
--    }
-+   do {
-+       int error = mbedtls_ssl_write(st->ssl, (const unsigned char *)data + read, len - read);
-+       if (error <= 0) {
-+           return ssl_set_error(st->ssl, error);
-+       }
-+       read += error;
-+   } while (read < len);
- 
--    return ret;
-+    return read;
- }
- 
- ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len)
-
diff --git a/deps/patches/libgit2-mbedtls.patch b/deps/patches/libgit2-mbedtls.patch
index 955f165..af12728 100644
--- a/deps/patches/libgit2-mbedtls.patch
+++ b/deps/patches/libgit2-mbedtls.patch
@@ -1,66 +1,282 @@
+commit 4d69ea1172fec234e0f3c8cee8574e9e13bf825e
+Author: Etienne Samson <samson.etienne@gmail.com>
+Date:   Fri Jun 23 22:40:56 2017 +0000
+
+    mbedtls support for libgit2 v0.26.0
+    
+    See https://github.com/libgit2/libgit2/pull/4173
+    
+    cmake: simplify https support selection & tests
+    
+    Gather streams to src/streams
+    
+    Don't include OpenSSL from global.h so it doesn't namespace-leak
+    
+    Have clar exit immediately on initialization failure
+    
+    Generalize Travis' dependency installation
+    
+    Add USE_HTTPS as a CMake option
+    
+    It defaults to ON, e.g. "pick whatever default is appropriate for the platform".
+    It accepts one of SecureTransport, OpenSSL, WinHTTP, or OFF.
+    It errors if the backend library couldn't be found.
+    
+    mbedtls: initial support
+    
+    mbedtls: proper certificate verification
+    
+    mbedtls: use libmbedcrypto for hashing
+    
+    mbedtls: add global initialization
+    
+    mbedtls: default cipher list support
+    
+    mbedtls: load default CA certificates
+    
+    mbedtls: fix libgit2 hanging due to incomplete writes
+    
+    mbedtls: enable Travis CI tests
+    
+    mbedtls: use our own certificate validation
+    
+    Otherwise REQUIRED means that `git_stream_certificate` will always error.
+    We're doing the mbedtls check in verify_server_cert though.
+    
+    mbedtls: try all CA locations, stopping after any loaded
+    
+    WIP: distribution paths
+
+diff --git a/.travis.yml b/.travis.yml
+index af38252..e1be760 100644
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -45,11 +45,21 @@ matrix:
+        - VALGRIND=1
+          OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
+      os: linux
++   - compiler: gcc
++     env:
++         MBEDTLS=1
++         OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release -DUSE_TLS=mbedTLS -DMBEDTLS_ROOT_DIR=../mbedtls"
++     os: linux
++   - compiler: gcc
++     env:
++         MBEDTLS=1
++         OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DUSE_TLS=mbedTLS -DMBEDTLS_ROOT_DIR=../mbedtls"
++     os: linux
+  allow_failures:
+    - env: COVERITY=1
+ 
+ install:
+-  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
++  - if [ -x "./script/install-deps-${TRAVIS_OS_NAME}.sh" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
+ 
+ # Run the Build script and tests
+ script:
 diff --git a/CMakeLists.txt b/CMakeLists.txt
-index 4e1104f..8110489 100644
+index 4783e3e..5d7dbe3 100644
 --- a/CMakeLists.txt
 +++ b/CMakeLists.txt
-@@ -113,6 +113,10 @@ IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
- 	OPTION( USE_NSEC		"Care about sub-second file mtimes and ctimes"	ON  )
+@@ -40,6 +40,7 @@ OPTION( LIBGIT2_FILENAME	"Name of the produced binary"			OFF )
+ OPTION( USE_SHA1DC			"Use SHA-1 with collision detection"	OFF )
+ OPTION( USE_ICONV			"Link with and use iconv library" 		OFF )
+ OPTION( USE_SSH				"Link with libssh to enable SSH support" ON )
++OPTION( USE_HTTPS			"Enable HTTPS support"					ON )
+ OPTION( USE_GSSAPI			"Link with libgssapi for SPNEGO auth"   OFF )
+ OPTION( VALGRIND			"Configure build for valgrind"			OFF )
+ OPTION( CURL			"Use curl for HTTP if available" ON)
+@@ -89,10 +90,6 @@ IF(MSVC)
+ 	OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
  ENDIF()
  
-+IF (NOT USE_OPENSSL)
-+	OPTION( USE_MBEDTLS		"Link with and use mbedtls library"             OFF )
-+ENDIF()
-+
- # This variable will contain the libraries we need to put into
- # libgit2.pc's Requires.private. That is, what we're linking to or
- # what someone who's statically linking us needs to link to.
-@@ -283,6 +287,10 @@ ELSE ()
+-IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+-	OPTION( USE_OPENSSL                     "Link with and use openssl library"             ON )
+-ENDIF()
+-
+ CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h"
+ 	HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C)
+ CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h"
+@@ -208,21 +205,6 @@ STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "$
+ # Find required dependencies
+ INCLUDE_DIRECTORIES(src include)
+ 
+-IF (SECURITY_FOUND)
+-  # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there
+-  CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY)
+-  IF (HAVE_NEWER_SECURITY)
+-    MESSAGE("-- Found Security ${SECURITY_DIRS}")
+-    LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
+-  ELSE()
+-    MESSAGE("-- Security framework is too old, falling back to OpenSSL")
+-    SET(SECURITY_FOUND "NO")
+-    SET(SECURITY_DIRS "")
+-    SET(SECURITY_DIR "")
+-    SET(USE_OPENSSL "ON")
+-  ENDIF()
+-ENDIF()
+-
+ IF (COREFOUNDATION_FOUND)
+   MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}")
+   LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation")
+@@ -280,10 +262,14 @@ ELSE ()
+ 		PKG_CHECK_MODULES(CURL libcurl)
+ 	ENDIF ()
+ 
+-	IF (NOT AMIGA AND USE_OPENSSL)
++	IF (NOT AMIGA AND (USE_HTTPS STREQUAL "OpenSSL" OR USE_HTTPS STREQUAL "ON"))
  		FIND_PACKAGE(OpenSSL)
  	ENDIF ()
  
-+	IF (NOT AMIGA AND USE_MBEDTLS)
++	IF (NOT AMIGA AND (USE_HTTPS STREQUAL "mbedTLS" OR USE_HTTPS STREQUAL "ON"))
 +		FIND_PACKAGE(mbedTLS)
 +	ENDIF ()
 +
  	IF (CURL_FOUND)
  		ADD_DEFINITIONS(-DGIT_CURL)
  		INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
-@@ -316,6 +324,9 @@ ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
+@@ -293,6 +279,69 @@ ELSE ()
+ 	ENDIF()
+ ENDIF()
+ 
++IF (USE_HTTPS STREQUAL "ON")
++	IF (SECURITY_FOUND)
++		# OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there
++		CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY)
++		IF (HAVE_NEWER_SECURITY)
++			MESSAGE("-- Found Security ${SECURITY_DIRS}")
++			LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
++			SET(HTTPS_BACKEND "SecureTransport")
++		ELSE()
++			MESSAGE("-- Security framework is too old, falling back to OpenSSL")
++			SET(SECURITY_FOUND "NO")
++			SET(SECURITY_DIRS "")
++			SET(SECURITY_DIR "")
++			SET(HTTPS_BACKEND "OpenSSL")
++		ENDIF()
++	ELSEIF(WINHTTP)
++		SET(HTTPS_BACKEND "WinHTTP")
++	ELSEIF(MBEDTLS_FOUND)
++		SET(HTTPS_BACKEND "mbedTLS")
++	ELSE()
++		SET(HTTPS_BACKEND "OpenSSL")
++	ENDIF()
++ELSE()
++	SET(HTTPS_BACKEND ${USE_HTTPS})
++ENDIF()
++
++MESSAGE(STATUS "Using HTTPS backend ${HTTPS_BACKEND}")
++
++IF (HTTPS_BACKEND STREQUAL "SecureTransport")
++	IF (NOT SECURITY_FOUND)
++		MESSAGE(FATAL_ERROR "Asked for SecureTransport HTTPS backend, but it wasn't found")
++	ENDIF()
++
++	ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
++	INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
++ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL")
++	IF (NOT OPENSSL_FOUND)
++		MESSAGE(FATAL_ERROR "Asked for OpenSSL HTTPS backend, but it wasn't found")
++	ENDIF()
++
++	ADD_DEFINITIONS(-DGIT_OPENSSL)
++	INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
++	SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
++ELSEIF (TLS_BACKEND STREQUAL "mbedTLS")
++	IF (NOT MBEDTLS_FOUND)
++		MESSAGE(FATAL_ERROR "Asked for mbedTLS backend, but it wasn't found")
++	ENDIF()
++
++	ADD_DEFINITIONS(-DGIT_MBEDTLS)
++	INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
++	LINK_DIRECTORIES(${MBEDTLS_LIBRARY_DIR})
++	SET(SSL_LIBRARIES ${MBEDTLS_LIBRARIES})
++ELSEIF(HTTPS_BACKEND STREQUAL "WinHTTP")
++ENDIF()
++
++IF (USE_HTTPS AND NOT HTTPS_BACKEND)
++	MESSAGE(FATAL_ERROR "Asked for backend " ${HTTPS_BACKEND} " but it wasn't found")
++ENDIF()
++
++IF (HTTPS_BACKEND)
++	ADD_DEFINITIONS(-DGIT_HTTPS)
++ENDIF()
++
+ # Specify sha1 implementation
+ IF (USE_SHA1DC)
+ 	ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT)
+@@ -303,15 +352,18 @@ IF (USE_SHA1DC)
+ ELSEIF (WIN32 AND NOT MINGW)
+ 	ADD_DEFINITIONS(-DGIT_SHA1_WIN32)
+ 	FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
+-ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
++ELSEIF (TLS_BACKEND MATCHES "SecureTransport")
+ 	ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO)
+-ELSEIF (OPENSSL_FOUND)
++ELSEIF (TLS_BACKEND MATCHES "OpenSSL")
+ 	ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL)
+ 	IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ 		LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
  	ELSE()
  		SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
  	ENDIF ()
-+ELSEIF (MBEDTLS_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
++ELSEIF (TLS_BACKEND STREQUAL "mbedTLS")
 +	ADD_DEFINITIONS(-DMBEDTLS_SHA1)
 +	FILE(GLOB SRC_SHA1 src/hash/hash_mbedtls.c)
  ELSE()
  	FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
  ENDIF()
-@@ -543,6 +554,11 @@ IF (OPENSSL_FOUND)
-   SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
+@@ -543,21 +595,6 @@ ELSE()
+ 	# that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
  ENDIF()
  
-+IF (MBEDTLS_FOUND)
-+  ADD_DEFINITIONS(-DGIT_MBEDTLS)
-+  INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
-+  SET(SSL_LIBRARIES ${MBEDTLS_LIBRARIES})
-+ENDIF()
- 
- 
+-IF (SECURITY_FOUND)
+-  ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
+-  ADD_DEFINITIONS(-DGIT_HTTPS)
+-  INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
+-ENDIF ()
+-
+-IF (OPENSSL_FOUND)
+-  ADD_DEFINITIONS(-DGIT_OPENSSL)
+-  ADD_DEFINITIONS(-DGIT_HTTPS)
+-  INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
+-  SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
+-ENDIF()
+-
+-
+-
  IF (THREADSAFE)
-@@ -688,7 +704,7 @@ IF (BUILD_CLAR)
+ 	IF (NOT WIN32)
+ 		FIND_PACKAGE(Threads REQUIRED)
+@@ -595,7 +632,12 @@ ELSE()
+ 	ENDIF()
+ 	FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
+ ENDIF()
+-FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
++FILE(GLOB SRC_GIT2
++	src/*.c src/*.h
++	src/streams/*.c src/streams/*.h
++	src/transports/*.c src/transports/*.h
++	src/xdiff/*.c src/xdiff/*.h
++)
+ 
+ # Determine architecture of the machine
+ IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
+@@ -703,7 +745,7 @@ IF (BUILD_CLAR)
  	ENDIF ()
  
  	ENABLE_TESTING()
 -	IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
-+	IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND OR MBEDTLS_FOUND)
- 		ADD_TEST(libgit2_clar libgit2_clar -ionline)
++	IF (HAS_HTTPS_SUPPORT)
+ 		ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
  	ELSE ()
- 		ADD_TEST(libgit2_clar libgit2_clar -v)
+ 		ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
 diff --git a/cmake/Modules/FindmbedTLS.cmake b/cmake/Modules/FindmbedTLS.cmake
 new file mode 100644
-index 0000000..2f4adbc
+index 0000000..9329755
 --- /dev/null
 +++ b/cmake/Modules/FindmbedTLS.cmake
-@@ -0,0 +1,64 @@
+@@ -0,0 +1,93 @@
 +# - Try to find mbedTLS
 +# Once done this will define
 +#
@@ -72,17 +288,46 @@ index 0000000..2f4adbc
 +#  MBEDTLS_LIBRARY - path to mbedTLS library
 +#  MBEDX509_LIBRARY - path to mbedTLS X.509 library
 +#  MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library
++#
++# Hint
++#  MBEDTLS_ROOT_DIR can be pointed to a local mbedTLS installation.
++
++SET(_MBEDTLS_ROOT_HINTS
++    ${MBEDTLS_ROOT_DIR}
++    ENV MBEDTLS_ROOT_DIR
++)
++
++SET(_MBEDTLS_ROOT_HINTS_AND_PATHS
++    HINTS ${_MBEDTLS_ROOT_HINTS}
++    PATHS ${_MBEDTLS_ROOT_PATHS}
++)
 +
-+FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h)
++FIND_PATH(MBEDTLS_INCLUDE_DIR
++    NAMES mbedtls/version.h
++    ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
++    PATH_SUFFIXES include
++)
 +
 +IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES)
 +    # Already in cache, be silent
 +    SET(MBEDTLS_FIND_QUIETLY TRUE)
 +ENDIF()
 +
-+FIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509)
-+FIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509)
-+FIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto)
++FIND_LIBRARY(MBEDTLS_LIBRARY
++    NAMES mbedtls libmbedtls
++    ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
++    PATH_SUFFIXES library
++)
++FIND_LIBRARY(MBEDX509_LIBRARY
++    NAMES mbedx509 libmbedx509
++    ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
++    PATH_SUFFIXES library
++)
++FIND_LIBRARY(MBEDCRYPTO_LIBRARY
++    NAMES mbedcrypto libmbedcrypto
++    ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
++    PATH_SUFFIXES library
++)
 +
 +IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY)
 +     SET(MBEDTLS_FOUND TRUE)
@@ -125,68 +370,465 @@ index 0000000..2f4adbc
 +    MBEDX509_LIBRARY
 +    MBEDCRYPTO_LIBRARY
 +)
+diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh
+new file mode 100755
+index 0000000..94309b0
+--- /dev/null
++++ b/script/install-deps-linux.sh
+@@ -0,0 +1,12 @@
++#!/bin/sh
++
++echo "Installing dependencies"
++if [ "$MBEDTLS" ]; then
++  git clone https://github.com/ARMmbed/mbedtls.git ../mbedtls
++  cd ../mbedtls
++  git checkout mbedtls-2.4.2
++  cmake -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF .
++  cmake --build .
++
++  echo "mbedTLS built in `pwd`"
++fi
+diff --git a/src/curl_stream.c b/src/curl_stream.c
+deleted file mode 100644
+index 4e0455c..0000000
+--- a/src/curl_stream.c
++++ /dev/null
+@@ -1,362 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-
+-#ifdef GIT_CURL
+-
+-#include <curl/curl.h>
+-
+-#include "stream.h"
+-#include "git2/transport.h"
+-#include "buffer.h"
+-#include "vector.h"
+-#include "proxy.h"
+-
+-/* This is for backwards compatibility with curl<7.45.0. */
+-#ifndef CURLINFO_ACTIVESOCKET
+-# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
+-# define GIT_CURL_BADSOCKET -1
+-# define git_activesocket_t long
+-#else
+-# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
+-# define git_activesocket_t curl_socket_t
+-#endif
+-
+-typedef struct {
+-	git_stream parent;
+-	CURL *handle;
+-	curl_socket_t socket;
+-	char curl_error[CURL_ERROR_SIZE + 1];
+-	git_cert_x509 cert_info;
+-	git_strarray cert_info_strings;
+-	git_proxy_options proxy;
+-	git_cred *proxy_cred;
+-} curl_stream;
+-
+-static int seterr_curl(curl_stream *s)
+-{
+-	giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
+-	return -1;
+-}
+-
+-GIT_INLINE(int) error_no_credentials(void)
+-{
+-	giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
+-	return GIT_EAUTH;
+-}
+-
+-static int apply_proxy_creds(curl_stream *s)
+-{
+-	CURLcode res;
+-	git_cred_userpass_plaintext *userpass;
+-
+-	if (!s->proxy_cred)
+-		return GIT_ENOTFOUND;
+-
+-	userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
+-	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
+-		return seterr_curl(s);
+-	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	return 0;
+-}
+-
+-static int ask_and_apply_proxy_creds(curl_stream *s)
+-{
+-	int error;
+-	git_proxy_options *opts = &s->proxy;
+-
+-	if (!opts->credentials)
+-		return error_no_credentials();
+-
+-	/* TODO: see if PROXYAUTH_AVAIL helps us here */
+-	git_cred_free(s->proxy_cred);
+-	s->proxy_cred = NULL;
+-	giterr_clear();
+-	error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
+-	if (error == GIT_PASSTHROUGH)
+-		return error_no_credentials();
+-	if (error < 0) {
+-		if (!giterr_last())
+-			giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
+-		return error;
+-	}
+-
+-	if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+-		giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
+-		return -1;
+-	}
+-
+-	return apply_proxy_creds(s);
+-}
+-
+-static int curls_connect(git_stream *stream)
+-{
+-	curl_stream *s = (curl_stream *) stream;
+-	git_activesocket_t sockextr;
+-	long connect_last = 0;
+-	int failed_cert = 0, error;
+-	bool retry_connect;
+-	CURLcode res;
+-
+-	/* Apply any credentials we've already established */
+-	error = apply_proxy_creds(s);
+-	if (error < 0 && error != GIT_ENOTFOUND)
+-		return seterr_curl(s);
+-
+-	do {
+-		retry_connect = 0;
+-		res = curl_easy_perform(s->handle);
+-
+-		curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
+-
+-		/* HTTP 407 Proxy Authentication Required */
+-		if (connect_last == 407) {
+-			if ((error = ask_and_apply_proxy_creds(s)) < 0)
+-				return error;
+-
+-			retry_connect = true;
+-		}
+-	} while (retry_connect);
+-
+-	if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
+-		return seterr_curl(s);
+-	if (res == CURLE_PEER_FAILED_VERIFICATION)
+-		failed_cert = 1;
+-
+-	if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
+-		return seterr_curl(s);
+-	}
+-
+-	if (sockextr == GIT_CURL_BADSOCKET) {
+-		giterr_set(GITERR_NET, "curl socket is no longer valid");
+-		return -1;
+-	}
+-
+-	s->socket = sockextr;
+-
+-	if (s->parent.encrypted && failed_cert)
+-		return GIT_ECERTIFICATE;
+-
+-	return 0;
+-}
+-
+-static int curls_certificate(git_cert **out, git_stream *stream)
+-{
+-	int error;
+-	CURLcode res;
+-	struct curl_slist *slist;
+-	struct curl_certinfo *certinfo;
+-	git_vector strings = GIT_VECTOR_INIT;
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	/* No information is available, can happen with SecureTransport */
+-	if (certinfo->num_of_certs == 0) {
+-		s->cert_info.parent.cert_type = GIT_CERT_NONE;
+-		s->cert_info.data             = NULL;
+-		s->cert_info.len              = 0;
+-		return 0;
+-	}
+-
+-	if ((error = git_vector_init(&strings, 8, NULL)) < 0)
+-		return error;
+-
+-	for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
+-		char *str = git__strdup(slist->data);
+-		GITERR_CHECK_ALLOC(str);
+-		git_vector_insert(&strings, str);
+-	}
+-
+-	/* Copy the contents of the vector into a strarray so we can expose them */
+-	s->cert_info_strings.strings = (char **) strings.contents;
+-	s->cert_info_strings.count   = strings.length;
+-
+-	s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
+-	s->cert_info.data             = &s->cert_info_strings;
+-	s->cert_info.len              = strings.length;
+-
+-	*out = &s->cert_info.parent;
+-
+-	return 0;
+-}
+-
+-static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+-{
+-	int error;
+-	CURLcode res;
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
+-		return error;
+-
+-	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	return 0;
+-}
+-
+-static int wait_for(curl_socket_t fd, bool reading)
+-{
+-	int ret;
+-	fd_set infd, outfd, errfd;
+-
+-	FD_ZERO(&infd);
+-	FD_ZERO(&outfd);
+-	FD_ZERO(&errfd);
+-
+-	assert(fd >= 0);
+-	FD_SET(fd, &errfd);
+-	if (reading)
+-		FD_SET(fd, &infd);
+-	else
+-		FD_SET(fd, &outfd);
+-
+-	if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
+-		giterr_set(GITERR_OS, "error in select");
+-		return -1;
+-	}
+-
+-	return 0;
+-}
+-
+-static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
+-{
+-	int error;
+-	size_t off = 0, sent;
+-	CURLcode res;
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	GIT_UNUSED(flags);
+-
+-	do {
+-		if ((error = wait_for(s->socket, false)) < 0)
+-			return error;
+-
+-		res = curl_easy_send(s->handle, data + off, len - off, &sent);
+-		if (res == CURLE_OK)
+-			off += sent;
+-	} while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
+-
+-	if (res != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	return len;
+-}
+-
+-static ssize_t curls_read(git_stream *stream, void *data, size_t len)
+-{
+-	int error;
+-	size_t read;
+-	CURLcode res;
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	do {
+-		if ((error = wait_for(s->socket, true)) < 0)
+-			return error;
+-
+-		res = curl_easy_recv(s->handle, data, len, &read);
+-	} while (res == CURLE_AGAIN);
+-
+-	if (res != CURLE_OK)
+-		return seterr_curl(s);
+-
+-	return read;
+-}
+-
+-static int curls_close(git_stream *stream)
+-{
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	if (!s->handle)
+-		return 0;
+-
+-	curl_easy_cleanup(s->handle);
+-	s->handle = NULL;
+-	s->socket = 0;
+-
+-	return 0;
+-}
+-
+-static void curls_free(git_stream *stream)
+-{
+-	curl_stream *s = (curl_stream *) stream;
+-
+-	curls_close(stream);
+-	git_strarray_free(&s->cert_info_strings);
+-	git__free(s);
+-}
+-
+-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	curl_stream *st;
+-	CURL *handle;
+-	int iport = 0, error;
+-
+-	st = git__calloc(1, sizeof(curl_stream));
+-	GITERR_CHECK_ALLOC(st);
+-
+-	handle = curl_easy_init();
+-	if (handle == NULL) {
+-		giterr_set(GITERR_NET, "failed to create curl handle");
+-		git__free(st);
+-		return -1;
+-	}
+-
+-	if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
+-		git__free(st);
+-		return error;
+-	}
+-
+-	curl_easy_setopt(handle, CURLOPT_URL, host);
+-	curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
+-	curl_easy_setopt(handle, CURLOPT_PORT, iport);
+-	curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
+-	curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
+-	curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
+-	curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
+-	curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
+-
+-	/* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
+-
+-	st->parent.version = GIT_STREAM_VERSION;
+-	st->parent.encrypted = 0; /* we don't encrypt ourselves */
+-	st->parent.proxy_support = 1;
+-	st->parent.connect = curls_connect;
+-	st->parent.certificate = curls_certificate;
+-	st->parent.set_proxy = curls_set_proxy;
+-	st->parent.read = curls_read;
+-	st->parent.write = curls_write;
+-	st->parent.close = curls_close;
+-	st->parent.free = curls_free;
+-	st->handle = handle;
+-
+-	*out = (git_stream *) st;
+-	return 0;
+-}
+-
+-#else
+-
+-#include "stream.h"
+-
+-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	GIT_UNUSED(out);
+-	GIT_UNUSED(host);
+-	GIT_UNUSED(port);
+-
+-	giterr_set(GITERR_NET, "curl is not supported in this version");
+-	return -1;
+-}
+-
+-
+-#endif
+diff --git a/src/curl_stream.h b/src/curl_stream.h
+deleted file mode 100644
+index 283f0fe..0000000
+--- a/src/curl_stream.h
++++ /dev/null
+@@ -1,14 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-#ifndef INCLUDE_curl_stream_h__
+-#define INCLUDE_curl_stream_h__
+-
+-#include "git2/sys/stream.h"
+-
+-extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
+-
+-#endif
 diff --git a/src/global.c b/src/global.c
-index e2ad8fe..c5b4269 100644
+index afa57e1..66f1d44 100644
 --- a/src/global.c
 +++ b/src/global.c
-@@ -10,7 +10,11 @@
+@@ -10,7 +10,8 @@
  #include "sysdir.h"
  #include "filter.h"
  #include "merge_driver.h"
-+#ifdef GIT_OPENSSL
- #include "openssl_stream.h"
-+#elif GIT_MBEDTLS
-+#include "mbedtls_stream.h"
-+#endif
+-#include "openssl_stream.h"
++#include "streams/mbedtls.h"
++#include "streams/openssl.h"
  #include "thread-utils.h"
  #include "git2/global.h"
  #include "transports/ssh.h"
-@@ -61,8 +65,13 @@ static int init_common(void)
- 		(ret = git_sysdir_global_init()) == 0 &&
+@@ -62,7 +63,8 @@ static int init_common(void)
  		(ret = git_filter_global_init()) == 0 &&
  		(ret = git_merge_driver_global_init()) == 0 &&
--		(ret = git_transport_ssh_global_init()) == 0 &&
+ 		(ret = git_transport_ssh_global_init()) == 0 &&
 -		(ret = git_openssl_stream_global_init()) == 0)
-+		(ret = git_transport_ssh_global_init()) == 0
-+#ifdef GIT_OPENSSL
-+		&& (ret = git_openssl_stream_global_init()) == 0
-+#elif GIT_MBEDTLS
-+		&& (ret = git_mbedtls_stream_global_init()) == 0
-+#endif
-+		)
++		(ret = git_openssl_stream_global_init()) == 0 &&
++		(ret = git_mbedtls_stream_global_init()) == 0)
  		ret = git_mwindow_global_init();
  
  	GIT_MEMORY_BARRIER;
 diff --git a/src/global.h b/src/global.h
-index 2199515..adadcd9 100644
+index 88f40aa..e4bbabf 100644
 --- a/src/global.h
 +++ b/src/global.h
-@@ -23,6 +23,12 @@ typedef struct {
- extern SSL_CTX *git__ssl_ctx;
- #endif
+@@ -24,11 +24,6 @@ typedef struct {
+ 	git_thread *current_thread;
+ } git_global_st;
  
-+#ifdef GIT_MBEDTLS
-+# include "mbedtls/platform.h"
-+# include "mbedtls/ssl.h"
-+extern mbedtls_ssl_config *git__ssl_conf;
-+#endif
-+
+-#ifdef GIT_OPENSSL
+-# include <openssl/ssl.h>
+-extern SSL_CTX *git__ssl_ctx;
+-#endif
+-
  git_global_st *git__global_state(void);
  
  extern git_mutex git__mwindow_mutex;
 diff --git a/src/hash.h b/src/hash.h
-index 0bc02a8..958d23b 100644
+index 0db0339..cba4462 100644
 --- a/src/hash.h
 +++ b/src/hash.h
-@@ -20,6 +20,8 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx);
- # include "hash/hash_common_crypto.h"
- #elif defined(OPENSSL_SHA1)
+@@ -24,6 +24,8 @@ void git_hash_ctx_cleanup(git_hash_ctx *ctx);
  # include "hash/hash_openssl.h"
+ #elif defined(GIT_SHA1_WIN32)
+ # include "hash/hash_win32.h"
 +#elif defined(MBEDTLS_SHA1)
 +# include "hash/hash_mbedtls.h"
- #elif defined(WIN32_SHA1)
- # include "hash/hash_win32.h"
  #else
+ # include "hash/hash_generic.h"
+ #endif
 diff --git a/src/hash/hash_mbedtls.c b/src/hash/hash_mbedtls.c
 new file mode 100644
 index 0000000..a19d763
@@ -233,7 +875,7 @@ index 0000000..a19d763
 +}
 diff --git a/src/hash/hash_mbedtls.h b/src/hash/hash_mbedtls.h
 new file mode 100644
-index 0000000..e50d295
+index 0000000..24196c5
 --- /dev/null
 +++ b/src/hash/hash_mbedtls.h
 @@ -0,0 +1,20 @@
@@ -257,13 +899,1803 @@ index 0000000..e50d295
 +#define git_hash_ctx_init(ctx) git_hash_init(ctx)
 +
 +#endif /* INCLUDE_hash_mbedtld_h__ */
-\ No newline at end of file
-diff --git a/src/mbedtls_stream.c b/src/mbedtls_stream.c
+diff --git a/src/openssl_stream.c b/src/openssl_stream.c
+deleted file mode 100644
+index 759c501..0000000
+--- a/src/openssl_stream.c
++++ /dev/null
+@@ -1,656 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-
+-#ifdef GIT_OPENSSL
+-
+-#include <ctype.h>
+-
+-#include "global.h"
+-#include "posix.h"
+-#include "stream.h"
+-#include "socket_stream.h"
+-#include "openssl_stream.h"
+-#include "netops.h"
+-#include "git2/transport.h"
+-#include "git2/sys/openssl.h"
+-
+-#ifdef GIT_CURL
+-# include "curl_stream.h"
+-#endif
+-
+-#ifndef GIT_WIN32
+-# include <sys/types.h>
+-# include <sys/socket.h>
+-# include <netinet/in.h>
+-#endif
+-
+-#include <openssl/ssl.h>
+-#include <openssl/err.h>
+-#include <openssl/x509v3.h>
+-#include <openssl/bio.h>
+-
+-SSL_CTX *git__ssl_ctx;
+-
+-#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
+-
+-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+-
+-static git_mutex *openssl_locks;
+-
+-static void openssl_locking_function(
+-	int mode, int n, const char *file, int line)
+-{
+-	int lock;
+-
+-	GIT_UNUSED(file);
+-	GIT_UNUSED(line);
+-
+-	lock = mode & CRYPTO_LOCK;
+-
+-	if (lock) {
+-		git_mutex_lock(&openssl_locks[n]);
+-	} else {
+-		git_mutex_unlock(&openssl_locks[n]);
+-	}
+-}
+-
+-static void shutdown_ssl_locking(void)
+-{
+-	int num_locks, i;
+-
+-	num_locks = CRYPTO_num_locks();
+-	CRYPTO_set_locking_callback(NULL);
+-
+-	for (i = 0; i < num_locks; ++i)
+-		git_mutex_free(&openssl_locks[i]);
+-	git__free(openssl_locks);
+-}
+-
+-#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
+-
+-static BIO_METHOD *git_stream_bio_method;
+-static int init_bio_method(void);
+-
+-/**
+- * This function aims to clean-up the SSL context which
+- * we allocated.
+- */
+-static void shutdown_ssl(void)
+-{
+-	if (git_stream_bio_method) {
+-		BIO_meth_free(git_stream_bio_method);
+-		git_stream_bio_method = NULL;
+-	}
+-
+-	if (git__ssl_ctx) {
+-		SSL_CTX_free(git__ssl_ctx);
+-		git__ssl_ctx = NULL;
+-	}
+-}
+-
+-int git_openssl_stream_global_init(void)
+-{
+-#ifdef GIT_OPENSSL
+-	long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+-	const char *ciphers = git_libgit2__ssl_ciphers();
+-
+-	/* Older OpenSSL and MacOS OpenSSL doesn't have this */
+-#ifdef SSL_OP_NO_COMPRESSION
+-	ssl_opts |= SSL_OP_NO_COMPRESSION;
+-#endif
+-
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+-	SSL_load_error_strings();
+-	OpenSSL_add_ssl_algorithms();
+-#else
+-	OPENSSL_init_ssl(0, NULL);
+-#endif
+-
+-	/*
+-	 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
+-	 * which use the SSL hellos, which are often used for
+-	 * compatibility. We then disable SSL so we only allow OpenSSL
+-	 * to speak TLSv1 to perform the encryption itself.
+-	 */
+-	git__ssl_ctx = SSL_CTX_new(SSLv23_method());
+-	SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
+-	SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
+-	SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
+-	if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
+-		SSL_CTX_free(git__ssl_ctx);
+-		git__ssl_ctx = NULL;
+-		return -1;
+-	}
+-
+-	if (!ciphers) {
+-		ciphers = GIT_SSL_DEFAULT_CIPHERS;
+-	}
+-
+-	if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+-		SSL_CTX_free(git__ssl_ctx);
+-		git__ssl_ctx = NULL;
+-		return -1;
+-	}
+-
+-	if (init_bio_method() < 0) {
+-		SSL_CTX_free(git__ssl_ctx);
+-		git__ssl_ctx = NULL;
+-		return -1;
+-	}
+-
+-#endif
+-
+-	git__on_shutdown(shutdown_ssl);
+-
+-	return 0;
+-}
+-
+-int git_openssl_set_locking(void)
+-{
+-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+-	int num_locks, i;
+-
+-	num_locks = CRYPTO_num_locks();
+-	openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+-	GITERR_CHECK_ALLOC(openssl_locks);
+-
+-	for (i = 0; i < num_locks; i++) {
+-		if (git_mutex_init(&openssl_locks[i]) != 0) {
+-			giterr_set(GITERR_SSL, "failed to initialize openssl locks");
+-			return -1;
+-		}
+-	}
+-
+-	CRYPTO_set_locking_callback(openssl_locking_function);
+-	git__on_shutdown(shutdown_ssl_locking);
+-	return 0;
+-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+-	return 0;
+-#else
+-	giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
+-	return -1;
+-#endif
+-}
+-
+-
+-static int bio_create(BIO *b)
+-{
+-	BIO_set_init(b, 1);
+-	BIO_set_data(b, NULL);
+-
+-	return 1;
+-}
+-
+-static int bio_destroy(BIO *b)
+-{
+-	if (!b)
+-		return 0;
+-
+-	BIO_set_data(b, NULL);
+-
+-	return 1;
+-}
+-
+-static int bio_read(BIO *b, char *buf, int len)
+-{
+-	git_stream *io = (git_stream *) BIO_get_data(b);
+-
+-	return (int) git_stream_read(io, buf, len);
+-}
+-
+-static int bio_write(BIO *b, const char *buf, int len)
+-{
+-	git_stream *io = (git_stream *) BIO_get_data(b);
+-
+-	return (int) git_stream_write(io, buf, len, 0);
+-}
+-
+-static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+-{
+-	GIT_UNUSED(b);
+-	GIT_UNUSED(num);
+-	GIT_UNUSED(ptr);
+-
+-	if (cmd == BIO_CTRL_FLUSH)
+-		return 1;
+-
+-	return 0;
+-}
+-
+-static int bio_gets(BIO *b, char *buf, int len)
+-{
+-	GIT_UNUSED(b);
+-	GIT_UNUSED(buf);
+-	GIT_UNUSED(len);
+-	return -1;
+-}
+-
+-static int bio_puts(BIO *b, const char *str)
+-{
+-	return bio_write(b, str, strlen(str));
+-}
+-
+-static int init_bio_method(void)
+-{
+-	/* Set up the BIO_METHOD we use for wrapping our own stream implementations */
+-	git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
+-	GITERR_CHECK_ALLOC(git_stream_bio_method);
+-
+-	BIO_meth_set_write(git_stream_bio_method, bio_write);
+-	BIO_meth_set_read(git_stream_bio_method, bio_read);
+-	BIO_meth_set_puts(git_stream_bio_method, bio_puts);
+-	BIO_meth_set_gets(git_stream_bio_method, bio_gets);
+-	BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
+-	BIO_meth_set_create(git_stream_bio_method, bio_create);
+-	BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
+-
+-	return 0;
+-}
+-
+-static int ssl_set_error(SSL *ssl, int error)
+-{
+-	int err;
+-	unsigned long e;
+-
+-	err = SSL_get_error(ssl, error);
+-
+-	assert(err != SSL_ERROR_WANT_READ);
+-	assert(err != SSL_ERROR_WANT_WRITE);
+-
+-	switch (err) {
+-	case SSL_ERROR_WANT_CONNECT:
+-	case SSL_ERROR_WANT_ACCEPT:
+-		giterr_set(GITERR_NET, "SSL error: connection failure");
+-		break;
+-	case SSL_ERROR_WANT_X509_LOOKUP:
+-		giterr_set(GITERR_NET, "SSL error: x509 error");
+-		break;
+-	case SSL_ERROR_SYSCALL:
+-		e = ERR_get_error();
+-		if (e > 0) {
+-			giterr_set(GITERR_NET, "SSL error: %s",
+-					ERR_error_string(e, NULL));
+-			break;
+-		} else if (error < 0) {
+-			giterr_set(GITERR_OS, "SSL error: syscall failure");
+-			break;
+-		}
+-		giterr_set(GITERR_NET, "SSL error: received early EOF");
+-		return GIT_EEOF;
+-		break;
+-	case SSL_ERROR_SSL:
+-		e = ERR_get_error();
+-		giterr_set(GITERR_NET, "SSL error: %s",
+-				ERR_error_string(e, NULL));
+-		break;
+-	case SSL_ERROR_NONE:
+-	case SSL_ERROR_ZERO_RETURN:
+-	default:
+-		giterr_set(GITERR_NET, "SSL error: unknown error");
+-		break;
+-	}
+-	return -1;
+-}
+-
+-static int ssl_teardown(SSL *ssl)
+-{
+-	int ret;
+-
+-	ret = SSL_shutdown(ssl);
+-	if (ret < 0)
+-		ret = ssl_set_error(ssl, ret);
+-	else
+-		ret = 0;
+-
+-	return ret;
+-}
+-
+-static int check_host_name(const char *name, const char *host)
+-{
+-	if (!strcasecmp(name, host))
+-		return 0;
+-
+-	if (gitno__match_host(name, host) < 0)
+-		return -1;
+-
+-	return 0;
+-}
+-
+-static int verify_server_cert(SSL *ssl, const char *host)
+-{
+-	X509 *cert;
+-	X509_NAME *peer_name;
+-	ASN1_STRING *str;
+-	unsigned char *peer_cn = NULL;
+-	int matched = -1, type = GEN_DNS;
+-	GENERAL_NAMES *alts;
+-	struct in6_addr addr6;
+-	struct in_addr addr4;
+-	void *addr;
+-	int i = -1,j;
+-
+-	if (SSL_get_verify_result(ssl) != X509_V_OK) {
+-		giterr_set(GITERR_SSL, "the SSL certificate is invalid");
+-		return GIT_ECERTIFICATE;
+-	}
+-
+-	/* Try to parse the host as an IP address to see if it is */
+-	if (p_inet_pton(AF_INET, host, &addr4)) {
+-		type = GEN_IPADD;
+-		addr = &addr4;
+-	} else {
+-		if(p_inet_pton(AF_INET6, host, &addr6)) {
+-			type = GEN_IPADD;
+-			addr = &addr6;
+-		}
+-	}
+-
+-
+-	cert = SSL_get_peer_certificate(ssl);
+-	if (!cert) {
+-		giterr_set(GITERR_SSL, "the server did not provide a certificate");
+-		return -1;
+-	}
+-
+-	/* Check the alternative names */
+-	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+-	if (alts) {
+-		int num;
+-
+-		num = sk_GENERAL_NAME_num(alts);
+-		for (i = 0; i < num && matched != 1; i++) {
+-			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
+-			const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
+-			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
+-
+-			/* Skip any names of a type we're not looking for */
+-			if (gn->type != type)
+-				continue;
+-
+-			if (type == GEN_DNS) {
+-				/* If it contains embedded NULs, don't even try */
+-				if (memchr(name, '\0', namelen))
+-					continue;
+-
+-				if (check_host_name(name, host) < 0)
+-					matched = 0;
+-				else
+-					matched = 1;
+-			} else if (type == GEN_IPADD) {
+-				/* Here name isn't so much a name but a binary representation of the IP */
+-				matched = !!memcmp(name, addr, namelen);
+-			}
+-		}
+-	}
+-	GENERAL_NAMES_free(alts);
+-
+-	if (matched == 0)
+-		goto cert_fail_name;
+-
+-	if (matched == 1)
+-		return 0;
+-
+-	/* If no alternative names are available, check the common name */
+-	peer_name = X509_get_subject_name(cert);
+-	if (peer_name == NULL)
+-		goto on_error;
+-
+-	if (peer_name) {
+-		/* Get the index of the last CN entry */
+-		while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
+-			i = j;
+-	}
+-
+-	if (i < 0)
+-		goto on_error;
+-
+-	str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
+-	if (str == NULL)
+-		goto on_error;
+-
+-	/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
+-	if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
+-		int size = ASN1_STRING_length(str);
+-
+-		if (size > 0) {
+-			peer_cn = OPENSSL_malloc(size + 1);
+-			GITERR_CHECK_ALLOC(peer_cn);
+-			memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
+-			peer_cn[size] = '\0';
+-		} else {
+-			goto cert_fail_name;
+-		}
+-	} else {
+-		int size = ASN1_STRING_to_UTF8(&peer_cn, str);
+-		GITERR_CHECK_ALLOC(peer_cn);
+-		if (memchr(peer_cn, '\0', size))
+-			goto cert_fail_name;
+-	}
+-
+-	if (check_host_name((char *)peer_cn, host) < 0)
+-		goto cert_fail_name;
+-
+-	OPENSSL_free(peer_cn);
+-
+-	return 0;
+-
+-on_error:
+-	OPENSSL_free(peer_cn);
+-	return ssl_set_error(ssl, 0);
+-
+-cert_fail_name:
+-	OPENSSL_free(peer_cn);
+-	giterr_set(GITERR_SSL, "hostname does not match certificate");
+-	return GIT_ECERTIFICATE;
+-}
+-
+-typedef struct {
+-	git_stream parent;
+-	git_stream *io;
+-	bool connected;
+-	char *host;
+-	SSL *ssl;
+-	git_cert_x509 cert_info;
+-} openssl_stream;
+-
+-int openssl_close(git_stream *stream);
+-
+-int openssl_connect(git_stream *stream)
+-{
+-	int ret;
+-	BIO *bio;
+-	openssl_stream *st = (openssl_stream *) stream;
+-
+-	if ((ret = git_stream_connect(st->io)) < 0)
+-		return ret;
+-
+-	st->connected = true;
+-
+-	bio = BIO_new(git_stream_bio_method);
+-	GITERR_CHECK_ALLOC(bio);
+-
+-	BIO_set_data(bio, st->io);
+-	SSL_set_bio(st->ssl, bio, bio);
+-
+-	/* specify the host in case SNI is needed */
+-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+-	SSL_set_tlsext_host_name(st->ssl, st->host);
+-#endif
+-
+-	if ((ret = SSL_connect(st->ssl)) <= 0)
+-		return ssl_set_error(st->ssl, ret);
+-
+-	return verify_server_cert(st->ssl, st->host);
+-}
+-
+-int openssl_certificate(git_cert **out, git_stream *stream)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-	int len;
+-	X509 *cert = SSL_get_peer_certificate(st->ssl);
+-	unsigned char *guard, *encoded_cert;
+-
+-	/* Retrieve the length of the certificate first */
+-	len = i2d_X509(cert, NULL);
+-	if (len < 0) {
+-		giterr_set(GITERR_NET, "failed to retrieve certificate information");
+-		return -1;
+-	}
+-
+-	encoded_cert = git__malloc(len);
+-	GITERR_CHECK_ALLOC(encoded_cert);
+-	/* i2d_X509 makes 'guard' point to just after the data */
+-	guard = encoded_cert;
+-
+-	len = i2d_X509(cert, &guard);
+-	if (len < 0) {
+-		git__free(encoded_cert);
+-		giterr_set(GITERR_NET, "failed to retrieve certificate information");
+-		return -1;
+-	}
+-
+-	st->cert_info.parent.cert_type = GIT_CERT_X509;
+-	st->cert_info.data = encoded_cert;
+-	st->cert_info.len = len;
+-
+-	*out = &st->cert_info.parent;
+-
+-	return 0;
+-}
+-
+-static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-
+-	return git_stream_set_proxy(st->io, proxy_opts);
+-}
+-
+-ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-	int ret;
+-
+-	GIT_UNUSED(flags);
+-
+-	if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
+-		return ssl_set_error(st->ssl, ret);
+-	}
+-
+-	return ret;
+-}
+-
+-ssize_t openssl_read(git_stream *stream, void *data, size_t len)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-	int ret;
+-
+-	if ((ret = SSL_read(st->ssl, data, len)) <= 0)
+-		return ssl_set_error(st->ssl, ret);
+-
+-	return ret;
+-}
+-
+-int openssl_close(git_stream *stream)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-	int ret;
+-
+-	if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
+-		return -1;
+-
+-	st->connected = false;
+-
+-	return git_stream_close(st->io);
+-}
+-
+-void openssl_free(git_stream *stream)
+-{
+-	openssl_stream *st = (openssl_stream *) stream;
+-
+-	SSL_free(st->ssl);
+-	git__free(st->host);
+-	git__free(st->cert_info.data);
+-	git_stream_free(st->io);
+-	git__free(st);
+-}
+-
+-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	int error;
+-	openssl_stream *st;
+-
+-	st = git__calloc(1, sizeof(openssl_stream));
+-	GITERR_CHECK_ALLOC(st);
+-
+-	st->io = NULL;
+-#ifdef GIT_CURL
+-	error = git_curl_stream_new(&st->io, host, port);
+-#else
+-	error = git_socket_stream_new(&st->io, host, port);
+-#endif
+-
+-	if (error < 0)
+-		goto out_err;
+-
+-	st->ssl = SSL_new(git__ssl_ctx);
+-	if (st->ssl == NULL) {
+-		giterr_set(GITERR_SSL, "failed to create ssl object");
+-		error = -1;
+-		goto out_err;
+-	}
+-
+-	st->host = git__strdup(host);
+-	GITERR_CHECK_ALLOC(st->host);
+-
+-	st->parent.version = GIT_STREAM_VERSION;
+-	st->parent.encrypted = 1;
+-	st->parent.proxy_support = git_stream_supports_proxy(st->io);
+-	st->parent.connect = openssl_connect;
+-	st->parent.certificate = openssl_certificate;
+-	st->parent.set_proxy = openssl_set_proxy;
+-	st->parent.read = openssl_read;
+-	st->parent.write = openssl_write;
+-	st->parent.close = openssl_close;
+-	st->parent.free = openssl_free;
+-
+-	*out = (git_stream *) st;
+-	return 0;
+-
+-out_err:
+-	git_stream_free(st->io);
+-	git__free(st);
+-
+-	return error;
+-}
+-
+-#else
+-
+-#include "stream.h"
+-#include "git2/sys/openssl.h"
+-
+-int git_openssl_stream_global_init(void)
+-{
+-	return 0;
+-}
+-
+-int git_openssl_set_locking(void)
+-{
+-	giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
+-	return -1;
+-}
+-
+-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	GIT_UNUSED(out);
+-	GIT_UNUSED(host);
+-	GIT_UNUSED(port);
+-
+-	giterr_set(GITERR_SSL, "openssl is not supported in this version");
+-	return -1;
+-}
+-
+-#endif
+diff --git a/src/openssl_stream.h b/src/openssl_stream.h
+deleted file mode 100644
+index f5e59da..0000000
+--- a/src/openssl_stream.h
++++ /dev/null
+@@ -1,122 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-#ifndef INCLUDE_openssl_stream_h__
+-#define INCLUDE_openssl_stream_h__
+-
+-#include "git2/sys/stream.h"
+-
+-extern int git_openssl_stream_global_init(void);
+-
+-extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
+-
+-/*
+- * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+- * which do not exist in previous versions. We define these inline functions so
+- * we can program against the interface instead of littering the implementation
+- * with ifdefs.
+- */
+-#ifdef GIT_OPENSSL
+-# include <openssl/ssl.h>
+-# include <openssl/err.h>
+-# include <openssl/x509v3.h>
+-# include <openssl/bio.h>
+-
+-
+-
+-# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+-
+-GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
+-{
+-	BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+-	if (!meth) {
+-		return NULL;
+-	}
+-
+-	meth->type = type;
+-	meth->name = name;
+-
+-	return meth;
+-}
+-
+-GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
+-{
+-	git__free(biom);
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+-{
+-	biom->bwrite = write;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+-{
+-	biom->bread = read;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+-{
+-	biom->bputs = puts;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+-
+-{
+-	biom->bgets = gets;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+-{
+-	biom->ctrl = ctrl;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
+-{
+-	biom->create = create;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
+-{
+-	biom->destroy = destroy;
+-	return 1;
+-}
+-
+-GIT_INLINE(int) BIO_get_new_index(void)
+-{
+-	/* This exists as of 1.1 so before we'd just have 0 */
+-	return 0;
+-}
+-
+-GIT_INLINE(void) BIO_set_init(BIO *b, int init)
+-{
+-	b->init = init;
+-}
+-
+-GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
+-{
+-	a->ptr = ptr;
+-}
+-
+-GIT_INLINE(void*) BIO_get_data(BIO *a)
+-{
+-	return a->ptr;
+-}
+-
+-GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
+-{
+-	return ASN1_STRING_data((ASN1_STRING *)x);
+-}
+-
+-# endif // OpenSSL < 1.1
+-#endif // GIT_OPENSSL
+-
+-#endif
+diff --git a/src/settings.c b/src/settings.c
+index 52b861b..3a46f0d 100644
+--- a/src/settings.c
++++ b/src/settings.c
+@@ -9,6 +9,10 @@
+ # include <openssl/err.h>
+ #endif
+ 
++#ifdef GIT_MBEDTLS
++# include <mbedtls/error.h>
++#endif
++
+ #include <git2.h>
+ #include "common.h"
+ #include "sysdir.h"
+@@ -18,6 +22,8 @@
+ #include "odb.h"
+ #include "refs.h"
+ #include "transports/smart.h"
++#include "streams/openssl.h"
++#include "streams/mbedtls.h"
+ 
+ void git_libgit2_version(int *major, int *minor, int *rev)
+ {
+@@ -171,14 +177,19 @@ int git_libgit2_opts(int key, ...)
+ 		{
+ 			const char *file = va_arg(ap, const char *);
+ 			const char *path = va_arg(ap, const char *);
+-			if (!SSL_CTX_load_verify_locations(git__ssl_ctx, file, path)) {
+-				giterr_set(GITERR_NET, "SSL error: %s",
+-					ERR_error_string(ERR_get_error(), NULL));
+-				error = -1;
+-			}
++			error = git_openssl_set_cert_file(file, path);
++		}
++#elif GIT_MBEDTLS
++		{
++			const char *file = va_arg(ap, const char *);
++			const char *path = va_arg(ap, const char *);
++			if (file)
++				error = git_mbedtls_set_cert_file(file, 0);
++			if (error && path)
++				error = git_mbedtls_set_cert_file(path, 0);
+ 		}
+ #else
+-		giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
++		giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL or mbedTLS is not enabled");
+ 		error = -1;
+ #endif
+ 		break;
+diff --git a/src/socket_stream.c b/src/socket_stream.c
+deleted file mode 100644
+index c0a1684..0000000
+--- a/src/socket_stream.c
++++ /dev/null
+@@ -1,210 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-
+-#include "common.h"
+-#include "posix.h"
+-#include "netops.h"
+-#include "stream.h"
+-#include "socket_stream.h"
+-
+-#ifndef _WIN32
+-#	include <sys/types.h>
+-#	include <sys/socket.h>
+-#	include <sys/select.h>
+-#	include <sys/time.h>
+-#	include <netdb.h>
+-#	include <netinet/in.h>
+-#       include <arpa/inet.h>
+-#else
+-#	include <winsock2.h>
+-#	include <ws2tcpip.h>
+-#	ifdef _MSC_VER
+-#		pragma comment(lib, "ws2_32")
+-#	endif
+-#endif
+-
+-#ifdef GIT_WIN32
+-static void net_set_error(const char *str)
+-{
+-	int error = WSAGetLastError();
+-	char * win32_error = git_win32_get_error_message(error);
+-
+-	if (win32_error) {
+-		giterr_set(GITERR_NET, "%s: %s", str, win32_error);
+-		git__free(win32_error);
+-	} else {
+-		giterr_set(GITERR_NET, str);
+-	}
+-}
+-#else
+-static void net_set_error(const char *str)
+-{
+-	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+-}
+-#endif
+-
+-static int close_socket(GIT_SOCKET s)
+-{
+-	if (s == INVALID_SOCKET)
+-		return 0;
+-
+-#ifdef GIT_WIN32
+-	if (SOCKET_ERROR == closesocket(s))
+-		return -1;
+-
+-	if (0 != WSACleanup()) {
+-		giterr_set(GITERR_OS, "winsock cleanup failed");
+-		return -1;
+-	}
+-
+-	return 0;
+-#else
+-	return close(s);
+-#endif
+-
+-}
+-
+-int socket_connect(git_stream *stream)
+-{
+-	struct addrinfo *info = NULL, *p;
+-	struct addrinfo hints;
+-	git_socket_stream *st = (git_socket_stream *) stream;
+-	GIT_SOCKET s = INVALID_SOCKET;
+-	int ret;
+-
+-#ifdef GIT_WIN32
+-	/* on win32, the WSA context needs to be initialized
+-	 * before any socket calls can be performed */
+-	WSADATA wsd;
+-
+-	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+-		giterr_set(GITERR_OS, "winsock init failed");
+-		return -1;
+-	}
+-
+-	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
+-		WSACleanup();
+-		giterr_set(GITERR_OS, "winsock init failed");
+-		return -1;
+-	}
+-#endif
+-
+-	memset(&hints, 0x0, sizeof(struct addrinfo));
+-	hints.ai_socktype = SOCK_STREAM;
+-	hints.ai_family = AF_UNSPEC;
+-
+-	if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
+-		giterr_set(GITERR_NET,
+-			   "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
+-		return -1;
+-	}
+-
+-	for (p = info; p != NULL; p = p->ai_next) {
+-		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+-
+-		if (s == INVALID_SOCKET)
+-			continue;
+-
+-		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+-			break;
+-
+-		/* If we can't connect, try the next one */
+-		close_socket(s);
+-		s = INVALID_SOCKET;
+-	}
+-
+-	/* Oops, we couldn't connect to any address */
+-	if (s == INVALID_SOCKET && p == NULL) {
+-		giterr_set(GITERR_OS, "failed to connect to %s", st->host);
+-		p_freeaddrinfo(info);
+-		return -1;
+-	}
+-
+-	st->s = s;
+-	p_freeaddrinfo(info);
+-	return 0;
+-}
+-
+-ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
+-{
+-	ssize_t ret;
+-	size_t off = 0;
+-	git_socket_stream *st = (git_socket_stream *) stream;
+-
+-	while (off < len) {
+-		errno = 0;
+-		ret = p_send(st->s, data + off, len - off, flags);
+-		if (ret < 0) {
+-			net_set_error("Error sending data");
+-			return -1;
+-		}
+-
+-		off += ret;
+-	}
+-
+-	return off;
+-}
+-
+-ssize_t socket_read(git_stream *stream, void *data, size_t len)
+-{
+-	ssize_t ret;
+-	git_socket_stream *st = (git_socket_stream *) stream;
+-
+-	if ((ret = p_recv(st->s, data, len, 0)) < 0)
+-		net_set_error("Error receiving socket data");
+-
+-	return ret;
+-}
+-
+-int socket_close(git_stream *stream)
+-{
+-	git_socket_stream *st = (git_socket_stream *) stream;
+-	int error;
+-
+-	error = close_socket(st->s);
+-	st->s = INVALID_SOCKET;
+-
+-	return error;
+-}
+-
+-void socket_free(git_stream *stream)
+-{
+-	git_socket_stream *st = (git_socket_stream *) stream;
+-
+-	git__free(st->host);
+-	git__free(st->port);
+-	git__free(st);
+-}
+-
+-int git_socket_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	git_socket_stream *st;
+-
+-	assert(out && host);
+-
+-	st = git__calloc(1, sizeof(git_socket_stream));
+-	GITERR_CHECK_ALLOC(st);
+-
+-	st->host = git__strdup(host);
+-	GITERR_CHECK_ALLOC(st->host);
+-
+-	if (port) {
+-		st->port = git__strdup(port);
+-		GITERR_CHECK_ALLOC(st->port);
+-	}
+-
+-	st->parent.version = GIT_STREAM_VERSION;
+-	st->parent.connect = socket_connect;
+-	st->parent.write = socket_write;
+-	st->parent.read = socket_read;
+-	st->parent.close = socket_close;
+-	st->parent.free = socket_free;
+-	st->s = INVALID_SOCKET;
+-
+-	*out = (git_stream *) st;
+-	return 0;
+-}
+diff --git a/src/socket_stream.h b/src/socket_stream.h
+deleted file mode 100644
+index 8e9949f..0000000
+--- a/src/socket_stream.h
++++ /dev/null
+@@ -1,21 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-#ifndef INCLUDE_socket_stream_h__
+-#define INCLUDE_socket_stream_h__
+-
+-#include "netops.h"
+-
+-typedef struct {
+-	git_stream parent;
+-	char *host;
+-	char *port;
+-	GIT_SOCKET s;
+-} git_socket_stream;
+-
+-extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
+-
+-#endif
+diff --git a/src/stransport_stream.c b/src/stransport_stream.c
+deleted file mode 100644
+index 50ed945..0000000
+--- a/src/stransport_stream.c
++++ /dev/null
+@@ -1,294 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-
+-#ifdef GIT_SECURE_TRANSPORT
+-
+-#include <CoreFoundation/CoreFoundation.h>
+-#include <Security/SecureTransport.h>
+-#include <Security/SecCertificate.h>
+-
+-#include "git2/transport.h"
+-
+-#include "socket_stream.h"
+-#include "curl_stream.h"
+-
+-static int stransport_error(OSStatus ret)
+-{
+-	CFStringRef message;
+-
+-	if (ret == noErr || ret == errSSLClosedGraceful) {
+-		giterr_clear();
+-		return 0;
+-	}
+-
+-#if !TARGET_OS_IPHONE
+-	message = SecCopyErrorMessageString(ret, NULL);
+-	GITERR_CHECK_ALLOC(message);
+-
+-	giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+-	CFRelease(message);
+-#else
+-    giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
+-    GIT_UNUSED(message);
+-#endif
+-
+-	return -1;
+-}
+-
+-typedef struct {
+-	git_stream parent;
+-	git_stream *io;
+-	SSLContextRef ctx;
+-	CFDataRef der_data;
+-	git_cert_x509 cert_info;
+-} stransport_stream;
+-
+-static int stransport_connect(git_stream *stream)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-	int error;
+-	SecTrustRef trust = NULL;
+-	SecTrustResultType sec_res;
+-	OSStatus ret;
+-
+-	if ((error = git_stream_connect(st->io)) < 0)
+-		return error;
+-
+-	ret = SSLHandshake(st->ctx);
+-	if (ret != errSSLServerAuthCompleted) {
+-		giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
+-		return -1;
+-	}
+-
+-	if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+-		goto on_error;
+-
+-	if (!trust)
+-		return GIT_ECERTIFICATE;
+-
+-	if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
+-		goto on_error;
+-
+-	CFRelease(trust);
+-
+-	if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
+-		giterr_set(GITERR_SSL, "internal security trust error");
+-		return -1;
+-	}
+-
+-	if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+-	    sec_res == kSecTrustResultFatalTrustFailure)
+-		return GIT_ECERTIFICATE;
+-
+-	return 0;
+-
+-on_error:
+-	if (trust)
+-		CFRelease(trust);
+-
+-	return stransport_error(ret);
+-}
+-
+-static int stransport_certificate(git_cert **out, git_stream *stream)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-	SecTrustRef trust = NULL;
+-	SecCertificateRef sec_cert;
+-	OSStatus ret;
+-
+-	if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+-		return stransport_error(ret);
+-
+-	sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
+-	st->der_data = SecCertificateCopyData(sec_cert);
+-	CFRelease(trust);
+-
+-	if (st->der_data == NULL) {
+-		giterr_set(GITERR_SSL, "retrieved invalid certificate data");
+-		return -1;
+-	}
+-
+-	st->cert_info.parent.cert_type = GIT_CERT_X509;
+-	st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
+-	st->cert_info.len = CFDataGetLength(st->der_data);
+-
+-	*out = (git_cert *)&st->cert_info;
+-	return 0;
+-}
+-
+-static int stransport_set_proxy(
+-	git_stream *stream,
+-	const git_proxy_options *proxy_opts)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-
+-	return git_stream_set_proxy(st->io, proxy_opts);
+-}
+-
+-/*
+- * Contrary to typical network IO callbacks, Secure Transport write callback is
+- * expected to write *all* passed data, not just as much as it can, and any
+- * other case would be considered a failure.
+- *
+- * This behavior is actually not specified in the Apple documentation, but is
+- * required for things to work correctly (and incidentally, that's also how
+- * Apple implements it in its projects at opensource.apple.com).
+- *
+- * Libgit2 streams happen to already have this very behavior so this is just
+- * passthrough.
+- */
+-static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
+-{
+-	git_stream *io = (git_stream *) conn;
+-
+-	if (git_stream_write(io, data, *len, 0) < 0) {
+-		return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+-	}
+-
+-	return noErr;
+-}
+-
+-static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-	size_t data_len, processed;
+-	OSStatus ret;
+-
+-	GIT_UNUSED(flags);
+-
+-	data_len = len;
+-	if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
+-		return stransport_error(ret);
+-
+-	return processed;
+-}
+-
+-/*
+- * Contrary to typical network IO callbacks, Secure Transport read callback is
+- * expected to read *exactly* the requested number of bytes, not just as much
+- * as it can, and any other case would be considered a failure.
+- *
+- * This behavior is actually not specified in the Apple documentation, but is
+- * required for things to work correctly (and incidentally, that's also how
+- * Apple implements it in its projects at opensource.apple.com).
+- */
+-static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
+-{
+-	git_stream *io = (git_stream *) conn;
+-	OSStatus error = noErr;
+-	size_t off = 0;
+-	ssize_t ret;
+-
+-	do {
+-		ret = git_stream_read(io, data + off, *len - off);
+-		if (ret < 0) {
+-			error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+-			break;
+-		}
+-		if (ret == 0) {
+-			error = errSSLClosedGraceful;
+-			break;
+-		}
+-
+-		off += ret;
+-	} while (off < *len);
+-
+-	*len = off;
+-	return error;
+-}
+-
+-static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-	size_t processed;
+-	OSStatus ret;
+-
+-	if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
+-		return stransport_error(ret);
+-
+-	return processed;
+-}
+-
+-static int stransport_close(git_stream *stream)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-	OSStatus ret;
+-
+-	ret = SSLClose(st->ctx);
+-	if (ret != noErr && ret != errSSLClosedGraceful)
+-		return stransport_error(ret);
+-
+-	return git_stream_close(st->io);
+-}
+-
+-static void stransport_free(git_stream *stream)
+-{
+-	stransport_stream *st = (stransport_stream *) stream;
+-
+-	git_stream_free(st->io);
+-	CFRelease(st->ctx);
+-	if (st->der_data)
+-		CFRelease(st->der_data);
+-	git__free(st);
+-}
+-
+-int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
+-{
+-	stransport_stream *st;
+-	int error;
+-	OSStatus ret;
+-
+-	assert(out && host);
+-
+-	st = git__calloc(1, sizeof(stransport_stream));
+-	GITERR_CHECK_ALLOC(st);
+-
+-#ifdef GIT_CURL
+-	error = git_curl_stream_new(&st->io, host, port);
+-#else
+-	error = git_socket_stream_new(&st->io, host, port);
+-#endif
+-
+-	if (error < 0){
+-		git__free(st);
+-		return error;
+-	}
+-
+-	st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+-	if (!st->ctx) {
+-		giterr_set(GITERR_NET, "failed to create SSL context");
+-		git__free(st);
+-		return -1;
+-	}
+-
+-	if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
+-	    (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
+-	    (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
+-	    (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
+-	    (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
+-	    (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
+-		CFRelease(st->ctx);
+-		git__free(st);
+-		return stransport_error(ret);
+-	}
+-
+-	st->parent.version = GIT_STREAM_VERSION;
+-	st->parent.encrypted = 1;
+-	st->parent.proxy_support = git_stream_supports_proxy(st->io);
+-	st->parent.connect = stransport_connect;
+-	st->parent.certificate = stransport_certificate;
+-	st->parent.set_proxy = stransport_set_proxy;
+-	st->parent.read = stransport_read;
+-	st->parent.write = stransport_write;
+-	st->parent.close = stransport_close;
+-	st->parent.free = stransport_free;
+-
+-	*out = (git_stream *) st;
+-	return 0;
+-}
+-
+-#endif
+diff --git a/src/stransport_stream.h b/src/stransport_stream.h
+deleted file mode 100644
+index 714f902..0000000
+--- a/src/stransport_stream.h
++++ /dev/null
+@@ -1,14 +0,0 @@
+-/*
+- * Copyright (C) the libgit2 contributors. All rights reserved.
+- *
+- * This file is part of libgit2, distributed under the GNU GPL v2 with
+- * a Linking Exception. For full terms see the included COPYING file.
+- */
+-#ifndef INCLUDE_stransport_stream_h__
+-#define INCLUDE_stransport_stream_h__
+-
+-#include "git2/sys/stream.h"
+-
+-extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
+-
+-#endif
+diff --git a/src/streams/curl.c b/src/streams/curl.c
+new file mode 100644
+index 0000000..4e0455c
+--- /dev/null
++++ b/src/streams/curl.c
+@@ -0,0 +1,362 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++
++#ifdef GIT_CURL
++
++#include <curl/curl.h>
++
++#include "stream.h"
++#include "git2/transport.h"
++#include "buffer.h"
++#include "vector.h"
++#include "proxy.h"
++
++/* This is for backwards compatibility with curl<7.45.0. */
++#ifndef CURLINFO_ACTIVESOCKET
++# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
++# define GIT_CURL_BADSOCKET -1
++# define git_activesocket_t long
++#else
++# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
++# define git_activesocket_t curl_socket_t
++#endif
++
++typedef struct {
++	git_stream parent;
++	CURL *handle;
++	curl_socket_t socket;
++	char curl_error[CURL_ERROR_SIZE + 1];
++	git_cert_x509 cert_info;
++	git_strarray cert_info_strings;
++	git_proxy_options proxy;
++	git_cred *proxy_cred;
++} curl_stream;
++
++static int seterr_curl(curl_stream *s)
++{
++	giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
++	return -1;
++}
++
++GIT_INLINE(int) error_no_credentials(void)
++{
++	giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
++	return GIT_EAUTH;
++}
++
++static int apply_proxy_creds(curl_stream *s)
++{
++	CURLcode res;
++	git_cred_userpass_plaintext *userpass;
++
++	if (!s->proxy_cred)
++		return GIT_ENOTFOUND;
++
++	userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
++	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
++		return seterr_curl(s);
++	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
++		return seterr_curl(s);
++
++	return 0;
++}
++
++static int ask_and_apply_proxy_creds(curl_stream *s)
++{
++	int error;
++	git_proxy_options *opts = &s->proxy;
++
++	if (!opts->credentials)
++		return error_no_credentials();
++
++	/* TODO: see if PROXYAUTH_AVAIL helps us here */
++	git_cred_free(s->proxy_cred);
++	s->proxy_cred = NULL;
++	giterr_clear();
++	error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
++	if (error == GIT_PASSTHROUGH)
++		return error_no_credentials();
++	if (error < 0) {
++		if (!giterr_last())
++			giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
++		return error;
++	}
++
++	if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
++		giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
++		return -1;
++	}
++
++	return apply_proxy_creds(s);
++}
++
++static int curls_connect(git_stream *stream)
++{
++	curl_stream *s = (curl_stream *) stream;
++	git_activesocket_t sockextr;
++	long connect_last = 0;
++	int failed_cert = 0, error;
++	bool retry_connect;
++	CURLcode res;
++
++	/* Apply any credentials we've already established */
++	error = apply_proxy_creds(s);
++	if (error < 0 && error != GIT_ENOTFOUND)
++		return seterr_curl(s);
++
++	do {
++		retry_connect = 0;
++		res = curl_easy_perform(s->handle);
++
++		curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
++
++		/* HTTP 407 Proxy Authentication Required */
++		if (connect_last == 407) {
++			if ((error = ask_and_apply_proxy_creds(s)) < 0)
++				return error;
++
++			retry_connect = true;
++		}
++	} while (retry_connect);
++
++	if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
++		return seterr_curl(s);
++	if (res == CURLE_PEER_FAILED_VERIFICATION)
++		failed_cert = 1;
++
++	if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
++		return seterr_curl(s);
++	}
++
++	if (sockextr == GIT_CURL_BADSOCKET) {
++		giterr_set(GITERR_NET, "curl socket is no longer valid");
++		return -1;
++	}
++
++	s->socket = sockextr;
++
++	if (s->parent.encrypted && failed_cert)
++		return GIT_ECERTIFICATE;
++
++	return 0;
++}
++
++static int curls_certificate(git_cert **out, git_stream *stream)
++{
++	int error;
++	CURLcode res;
++	struct curl_slist *slist;
++	struct curl_certinfo *certinfo;
++	git_vector strings = GIT_VECTOR_INIT;
++	curl_stream *s = (curl_stream *) stream;
++
++	if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
++		return seterr_curl(s);
++
++	/* No information is available, can happen with SecureTransport */
++	if (certinfo->num_of_certs == 0) {
++		s->cert_info.parent.cert_type = GIT_CERT_NONE;
++		s->cert_info.data             = NULL;
++		s->cert_info.len              = 0;
++		return 0;
++	}
++
++	if ((error = git_vector_init(&strings, 8, NULL)) < 0)
++		return error;
++
++	for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
++		char *str = git__strdup(slist->data);
++		GITERR_CHECK_ALLOC(str);
++		git_vector_insert(&strings, str);
++	}
++
++	/* Copy the contents of the vector into a strarray so we can expose them */
++	s->cert_info_strings.strings = (char **) strings.contents;
++	s->cert_info_strings.count   = strings.length;
++
++	s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
++	s->cert_info.data             = &s->cert_info_strings;
++	s->cert_info.len              = strings.length;
++
++	*out = &s->cert_info.parent;
++
++	return 0;
++}
++
++static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
++{
++	int error;
++	CURLcode res;
++	curl_stream *s = (curl_stream *) stream;
++
++	if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
++		return error;
++
++	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
++		return seterr_curl(s);
++
++	if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
++		return seterr_curl(s);
++
++	return 0;
++}
++
++static int wait_for(curl_socket_t fd, bool reading)
++{
++	int ret;
++	fd_set infd, outfd, errfd;
++
++	FD_ZERO(&infd);
++	FD_ZERO(&outfd);
++	FD_ZERO(&errfd);
++
++	assert(fd >= 0);
++	FD_SET(fd, &errfd);
++	if (reading)
++		FD_SET(fd, &infd);
++	else
++		FD_SET(fd, &outfd);
++
++	if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
++		giterr_set(GITERR_OS, "error in select");
++		return -1;
++	}
++
++	return 0;
++}
++
++static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
++{
++	int error;
++	size_t off = 0, sent;
++	CURLcode res;
++	curl_stream *s = (curl_stream *) stream;
++
++	GIT_UNUSED(flags);
++
++	do {
++		if ((error = wait_for(s->socket, false)) < 0)
++			return error;
++
++		res = curl_easy_send(s->handle, data + off, len - off, &sent);
++		if (res == CURLE_OK)
++			off += sent;
++	} while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
++
++	if (res != CURLE_OK)
++		return seterr_curl(s);
++
++	return len;
++}
++
++static ssize_t curls_read(git_stream *stream, void *data, size_t len)
++{
++	int error;
++	size_t read;
++	CURLcode res;
++	curl_stream *s = (curl_stream *) stream;
++
++	do {
++		if ((error = wait_for(s->socket, true)) < 0)
++			return error;
++
++		res = curl_easy_recv(s->handle, data, len, &read);
++	} while (res == CURLE_AGAIN);
++
++	if (res != CURLE_OK)
++		return seterr_curl(s);
++
++	return read;
++}
++
++static int curls_close(git_stream *stream)
++{
++	curl_stream *s = (curl_stream *) stream;
++
++	if (!s->handle)
++		return 0;
++
++	curl_easy_cleanup(s->handle);
++	s->handle = NULL;
++	s->socket = 0;
++
++	return 0;
++}
++
++static void curls_free(git_stream *stream)
++{
++	curl_stream *s = (curl_stream *) stream;
++
++	curls_close(stream);
++	git_strarray_free(&s->cert_info_strings);
++	git__free(s);
++}
++
++int git_curl_stream_new(git_stream **out, const char *host, const char *port)
++{
++	curl_stream *st;
++	CURL *handle;
++	int iport = 0, error;
++
++	st = git__calloc(1, sizeof(curl_stream));
++	GITERR_CHECK_ALLOC(st);
++
++	handle = curl_easy_init();
++	if (handle == NULL) {
++		giterr_set(GITERR_NET, "failed to create curl handle");
++		git__free(st);
++		return -1;
++	}
++
++	if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
++		git__free(st);
++		return error;
++	}
++
++	curl_easy_setopt(handle, CURLOPT_URL, host);
++	curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
++	curl_easy_setopt(handle, CURLOPT_PORT, iport);
++	curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
++	curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
++	curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
++	curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
++	curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
++
++	/* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
++
++	st->parent.version = GIT_STREAM_VERSION;
++	st->parent.encrypted = 0; /* we don't encrypt ourselves */
++	st->parent.proxy_support = 1;
++	st->parent.connect = curls_connect;
++	st->parent.certificate = curls_certificate;
++	st->parent.set_proxy = curls_set_proxy;
++	st->parent.read = curls_read;
++	st->parent.write = curls_write;
++	st->parent.close = curls_close;
++	st->parent.free = curls_free;
++	st->handle = handle;
++
++	*out = (git_stream *) st;
++	return 0;
++}
++
++#else
++
++#include "stream.h"
++
++int git_curl_stream_new(git_stream **out, const char *host, const char *port)
++{
++	GIT_UNUSED(out);
++	GIT_UNUSED(host);
++	GIT_UNUSED(port);
++
++	giterr_set(GITERR_NET, "curl is not supported in this version");
++	return -1;
++}
++
++
++#endif
+diff --git a/src/streams/curl.h b/src/streams/curl.h
+new file mode 100644
+index 0000000..283f0fe
+--- /dev/null
++++ b/src/streams/curl.h
+@@ -0,0 +1,14 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++#ifndef INCLUDE_curl_stream_h__
++#define INCLUDE_curl_stream_h__
++
++#include "git2/sys/stream.h"
++
++extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
++
++#endif
+diff --git a/src/streams/mbedtls.c b/src/streams/mbedtls.c
 new file mode 100644
-index 0000000..98ff808
+index 0000000..0376ee4
 --- /dev/null
-+++ b/src/mbedtls_stream.c
-@@ -0,0 +1,483 @@
++++ b/src/streams/mbedtls.c
+@@ -0,0 +1,538 @@
 +/*
 + * Copyright (C) the libgit2 contributors. All rights reserved.
 + *
@@ -277,26 +2709,26 @@ index 0000000..98ff808
 +
 +#include "global.h"
 +#include "stream.h"
-+#include "socket_stream.h"
++#include "streams/socket.h"
++#include "netops.h"
 +#include "git2/transport.h"
++#include "util.h"
 +
 +#ifdef GIT_CURL
-+# include "curl_stream.h"
++# include "streams/curl.h"
 +#endif
 +
-+#include "mbedtls/config.h"
-+#include <mbedtls/x509.h>
-+#include <mbedtls/x509_crt.h>
++#include <mbedtls/config.h>
++#include <mbedtls/ssl.h>
 +#include <mbedtls/error.h>
-+#include "mbedtls/net.h"
-+#include "mbedtls/debug.h"
-+#include "mbedtls/entropy.h"
-+#include "mbedtls/ctr_drbg.h"
-+#include "mbedtls/error.h"
-+#include "mbedtls/certs.h"
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
 +
 +#ifndef OPENSSLDIR
-+# define OPENSSLDIR              "/usr/lib/ssl"
++//# define OPENSSLDIR
++# define OPENSSLDIR              "/etc/ssl" // Debian
++//# define OPENSSLDIR              "/usr/lib/ssl" //Ubuntu
++//# define OPENSSLDIR              "/usr/local/etc/openssl" // mostly-Darwin
 +#endif
 +#define X509_CERT_DIR            OPENSSLDIR "/certs"
 +#define X509_CERT_FILE           OPENSSLDIR "/cert.pem"
@@ -306,7 +2738,8 @@ index 0000000..98ff808
 +mbedtls_ssl_config *git__ssl_conf;
 +mbedtls_entropy_context *mbedtls_entropy;
 +
-+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
++#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
++#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
 +
 +/**
 + * This function aims to clean-up the SSL context which
@@ -314,565 +2747,1985 @@ index 0000000..98ff808
 + */
 +static void shutdown_ssl(void)
 +{
-+    if (git__ssl_conf) {
-+        mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
-+        git__free(git__ssl_conf->ca_chain);
-+        mbedtls_ctr_drbg_free(git__ssl_conf->p_rng);
-+        git__free(git__ssl_conf->p_rng);
-+        mbedtls_ssl_config_free(git__ssl_conf);
-+        git__free(git__ssl_conf);
-+        git__ssl_conf = NULL;
-+    }
-+    if (mbedtls_entropy) {
-+        mbedtls_entropy_free(mbedtls_entropy);
-+        git__free(mbedtls_entropy);
-+        mbedtls_entropy = NULL;
-+    }
++	if (git__ssl_conf) {
++		mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
++		git__free(git__ssl_conf->ca_chain);
++		mbedtls_ctr_drbg_free(git__ssl_conf->p_rng);
++		git__free(git__ssl_conf->p_rng);
++		mbedtls_ssl_config_free(git__ssl_conf);
++		git__free(git__ssl_conf);
++		git__ssl_conf = NULL;
++	}
++	if (mbedtls_entropy) {
++		mbedtls_entropy_free(mbedtls_entropy);
++		git__free(mbedtls_entropy);
++		mbedtls_entropy = NULL;
++	}
 +}
 +
++int git_mbedtls_set_cert_file(const char *path, int is_dir);
++
 +int git_mbedtls_stream_global_init(void)
 +{
-+    int ret, isdir;
-+    char *crtpath;
-+    struct stat statbuf;
-+    // const int *cipherids;
-+    // const char *ciphers = git_libgit2__ssl_ciphers();
-+
-+    mbedtls_ctr_drbg_context *ctr_drbg;
-+
-+    mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
-+    mbedtls_entropy_init(mbedtls_entropy);
-+
-+    // Seeding the random number generator
-+    ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
-+    mbedtls_ctr_drbg_init(ctr_drbg);
-+    if (mbedtls_ctr_drbg_seed(ctr_drbg,
-+                mbedtls_entropy_func,
-+                mbedtls_entropy, NULL, 0) != 0) {
-+        mbedtls_ctr_drbg_free(ctr_drbg);
-+        mbedtls_entropy_free(mbedtls_entropy);
-+        git__free(ctr_drbg);
-+        git__free(mbedtls_entropy);
-+        return -1;
-+    }
-+
-+    // configure TLSv1
-+    git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
-+    mbedtls_ssl_config_init(git__ssl_conf);
-+    if ( mbedtls_ssl_config_defaults(git__ssl_conf,
-+                MBEDTLS_SSL_IS_CLIENT,
-+                MBEDTLS_SSL_TRANSPORT_STREAM,
-+                MBEDTLS_SSL_PRESET_DEFAULT ) != 0) {
-+        mbedtls_ctr_drbg_free(ctr_drbg);
-+        git__free(ctr_drbg);
-+        mbedtls_ssl_config_free(git__ssl_conf);
-+        git__free(git__ssl_conf);
-+        git__ssl_conf = NULL;
-+        return -1;
-+    }
-+
-+    mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
-+    mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
-+
-+    // find locations for which CA certificates
-+    isdir = 0;
-+    crtpath = getenv(X509_CERT_FILE_EVP);
-+    ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode) ? 0 : 1;
-+    if (ret) {
-+        isdir = 1;
-+        crtpath = getenv(X509_CERT_DIR_EVP);
-+        ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) ? 0 : 1;
-+    }
-+    if (ret) {
-+        isdir = 0;
-+        crtpath = X509_CERT_FILE;
-+        ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode) ? 0 : 1;
-+    }
-+    if (ret) {
-+        isdir = 1;
-+        crtpath = X509_CERT_DIR;
-+        ret = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) ? 0 : 1;
-+    }
-+
-+    // cannot find CA certificates
-+    if (ret) {
-+        mbedtls_ctr_drbg_free(ctr_drbg);
-+        git__free(ctr_drbg);
-+        mbedtls_ssl_config_free(git__ssl_conf);
-+        git__free(git__ssl_conf);
-+        git__ssl_conf = NULL;
-+        return -1;
-+    } else {
-+        // set root certificates
-+        mbedtls_x509_crt *cacert = git__malloc(sizeof(mbedtls_x509_crt));
-+        mbedtls_x509_crt_init(cacert);
-+        if (isdir)
-+            ret = mbedtls_x509_crt_parse_path(cacert, crtpath);
-+        else
-+            ret = mbedtls_x509_crt_parse_file(cacert, crtpath);
-+
-+        if (ret) {
-+            giterr_set(GITERR_SSL, "failed to load CA certificates: %d", ret);
-+            mbedtls_x509_crt_free(cacert);
-+            git__free(cacert);
-+            mbedtls_ctr_drbg_free(ctr_drbg);
-+            git__free(ctr_drbg);
-+            mbedtls_ssl_config_free(git__ssl_conf);
-+            git__free(git__ssl_conf);
-+            git__ssl_conf = NULL;
-+            return -1;
-+        } else {
-+            mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
-+        }
-+    }
-+
-+    // set the list of allowed ciphersuites
-+    // if (!ciphers) {
-+    //     cipherids = mbedtls_ssl_list_ciphersuites();
-+    // }
-+    // mbedtls_ssl_conf_ciphersuites(git__ssl_conf, cipherids);
-+
-+    git__on_shutdown(shutdown_ssl);
++	int found = -1, loaded = 0;
++	char *crtpath;
++	struct stat statbuf;
++	mbedtls_ctr_drbg_context *ctr_drbg = NULL;
 +
-+    return 0;
++	int *ciphers_list = NULL;
++	int ciphers_known = 0;
++	char *cipher_name = NULL;
++	char *cipher_string = NULL;
++	char *cipher_string_tmp = NULL;
++
++	mbedtls_x509_crt *cacert = NULL;
++
++	git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
++	mbedtls_ssl_config_init(git__ssl_conf);
++	if (mbedtls_ssl_config_defaults(git__ssl_conf,
++									MBEDTLS_SSL_IS_CLIENT,
++									MBEDTLS_SSL_TRANSPORT_STREAM,
++									MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
++		giterr_set(GITERR_SSL, "failed to initialize mbedTLS");
++		goto cleanup;
++	}
++
++	// configure TLSv1
++	mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0);
++	// verify_server_cert is responsible for making the check.
++	// OPTIONAL because REQUIRED drops the certificate as soon as the check
++	// is made, so we can never see the certificate and override it.
++	mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
++
++	// set the list of allowed ciphersuites
++	ciphers_list = calloc(GIT_SSL_DEFAULT_CIPHERS_COUNT, sizeof(int));
++	ciphers_known = 0;
++	cipher_string = cipher_string_tmp = git__strdup(GIT_SSL_DEFAULT_CIPHERS);
++	while ((cipher_name = git__strtok(&cipher_string_tmp, ":")) != NULL) {
++		int cipherid = mbedtls_ssl_get_ciphersuite_id(cipher_name);
++		if (cipherid == 0) continue;
++
++		ciphers_list[ciphers_known++] = cipherid;
++	}
++	git__free(cipher_string);
++
++	if (!ciphers_known) {
++		giterr_set(GITERR_SSL, "no cipher could be enabled");
++		goto cleanup;
++	}
++	mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list);
++
++	// Seeding the random number generator
++	mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
++	mbedtls_entropy_init(mbedtls_entropy);
++
++	ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
++	mbedtls_ctr_drbg_init(ctr_drbg);
++	if (mbedtls_ctr_drbg_seed(ctr_drbg,
++							  mbedtls_entropy_func,
++							  mbedtls_entropy, NULL, 0) != 0) {
++		giterr_set(GITERR_SSL, "failed to initialize mbedTLS entropy pool");
++		goto cleanup;
++	}
++
++	mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
++
++	// find locations for which CA certificates
++	{
++		crtpath = getenv(X509_CERT_FILE_EVP);
++		found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode);
++		if (found)
++			loaded = (git_mbedtls_set_cert_file(crtpath, 0) == 0);
++	}
++	if (!loaded) {
++		crtpath = getenv(X509_CERT_DIR_EVP);
++		found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode);
++		if (found)
++			loaded = (git_mbedtls_set_cert_file(crtpath, 1) == 0);
++	}
++	if (!loaded) {
++		crtpath = X509_CERT_FILE;
++		found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode);
++		if (found)
++			loaded = (git_mbedtls_set_cert_file(crtpath, 0) == 0);
++	}
++	if (!loaded) {
++		crtpath = X509_CERT_DIR;
++		found = crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode);
++		if (found)
++			loaded = (git_mbedtls_set_cert_file(crtpath, 1) == 0);
++	}
++
++	git__on_shutdown(shutdown_ssl);
++
++	return 0;
++
++cleanup:
++	mbedtls_x509_crt_free(cacert);
++	git__free(cacert);
++	mbedtls_ctr_drbg_free(ctr_drbg);
++	git__free(ctr_drbg);
++	mbedtls_ssl_config_free(git__ssl_conf);
++	git__free(git__ssl_conf);
++	git__ssl_conf = NULL;
++
++	return -1;
 +}
 +
++mbedtls_ssl_config *git__ssl_conf;
++
 +static int bio_read(void *b, unsigned char *buf, size_t len)
 +{
-+    git_stream *io = (git_stream *) b;
-+    return (int) git_stream_read(io, buf, len);
++	git_stream *io = (git_stream *) b;
++	return (int) git_stream_read(io, buf, len);
 +}
 +
 +static int bio_write(void *b, const unsigned char *buf, size_t len)
 +{
-+    git_stream *io = (git_stream *) b;
-+    return (int) git_stream_write(io, (const char *)buf, len, 0);
++	git_stream *io = (git_stream *) b;
++	return (int) git_stream_write(io, (const char *)buf, len, 0);
 +}
 +
 +static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
 +{
-+    char errbuf[512];
-+    int ret = -1;
++	char errbuf[512];
++	int ret = -1;
++
++	assert(error != MBEDTLS_ERR_SSL_WANT_READ);
++	assert(error != MBEDTLS_ERR_SSL_WANT_WRITE);
++
++	if (error != 0)
++		mbedtls_strerror( error, errbuf, 512 );
 +
-+    assert(error != MBEDTLS_ERR_SSL_WANT_READ);
-+    assert(error != MBEDTLS_ERR_SSL_WANT_WRITE);
++	switch(error){
++		case 0:
++		giterr_set(GITERR_SSL, "SSL error: unknown error");
++		break;
 +
-+    if (error != 0)
-+        mbedtls_strerror( error, errbuf, 512 );
++	case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
++		giterr_set(GITERR_SSL, "SSL error: %x[%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
++		ret = GIT_ECERTIFICATE;
++		break;
 +
-+    switch(error){
-+    case 0:
-+        giterr_set(GITERR_SSL, "SSL error: unknown error");
-+        break;
-+    case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
-+        giterr_set(GITERR_SSL, "SSL error: %x[%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
-+        ret = GIT_ECERTIFICATE;
-+        break;
-+    default:
-+        giterr_set(GITERR_SSL, "SSL error: %x - %s", error, errbuf);
-+    }
++	default:
++		giterr_set(GITERR_SSL, "SSL error: %x - %s", error, errbuf);
++	}
 +
-+    return ret;
++	return ret;
 +}
 +
 +static int ssl_teardown(mbedtls_ssl_context *ssl)
 +{
-+    int ret = 0;
++	int ret = 0;
 +
-+    ret = mbedtls_ssl_close_notify(ssl);
-+    if (ret < 0)
-+        ret = ssl_set_error(ssl, ret);
++	ret = mbedtls_ssl_close_notify(ssl);
++	if (ret < 0)
++		ret = ssl_set_error(ssl, ret);
 +
-+    mbedtls_ssl_free(ssl);
-+    return ret;
++	mbedtls_ssl_free(ssl);
++	return ret;
 +}
 +
 +static int check_host_name(const char *name, const char *host)
 +{
-+    if (!strcasecmp(name, host))
-+        return 0;
++	if (!strcasecmp(name, host))
++		return 0;
 +
-+    if (gitno__match_host(name, host) < 0)
-+        return -1;
++	if (gitno__match_host(name, host) < 0)
++		return -1;
 +
-+    return 0;
++	return 0;
 +}
 +
 +static int verify_server_cert(mbedtls_ssl_context *ssl, const char *host)
 +{
-+    const mbedtls_x509_crt *cert;
-+    const mbedtls_x509_sequence *alts;
-+    int ret, matched = -1;
-+    size_t sn_size = 512;
-+    char subject_name[sn_size], alt_name[sn_size];
-+
-+
-+    if (( ret = mbedtls_ssl_get_verify_result(ssl) ) != 0) {
-+        char vrfy_buf[512];
-+        mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), "  ! ", ret );
-+        giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf);
-+        return GIT_ECERTIFICATE;
-+    }
-+
-+    cert = mbedtls_ssl_get_peer_cert(ssl);
-+    if (!cert) {
-+        giterr_set(GITERR_SSL, "the server did not provide a certificate");
-+        return -1;
-+    }
-+
-+    /* Check the alternative names */
-+    alts = &cert->subject_alt_names;
-+    while (alts != NULL && matched != 1) {
-+        // Buffer is too small
-+        if( alts->buf.len >= sn_size )
-+            goto on_error;
-+
-+        memcpy(alt_name, alts->buf.p, alts->buf.len);
-+        alt_name[alts->buf.len] = '\0';
-+
-+        if (!memchr(alt_name, '\0', alts->buf.len)) {
-+            if (check_host_name(alt_name, host) < 0)
-+                matched = 0;
-+            else
-+                matched = 1;
-+        }
-+
-+        alts = alts->next;
-+    }
-+    if (matched == 0)
-+        goto cert_fail_name;
-+
-+    if (matched == 1)
-+        return 0;
-+
-+    /* If no alternative names are available, check the common name */
-+    ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject);
-+    if (ret == 0)
-+        goto on_error;
-+    if (memchr(subject_name, '\0', ret))
-+        goto cert_fail_name;
-+
-+    if (check_host_name(subject_name, host) < 0)
-+        goto cert_fail_name;
++	const mbedtls_x509_crt *cert;
++	const mbedtls_x509_sequence *alts;
++	int ret, matched = -1;
++	size_t sn_size = 512;
++	char subject_name[sn_size], alt_name[sn_size];
 +
-+    return 0;
++
++	if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) {
++		char vrfy_buf[512];
++		mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), "  ! ", ret );
++		giterr_set(GITERR_SSL, "The SSL certificate is invalid: %s", vrfy_buf);
++		return GIT_ECERTIFICATE;
++	}
++
++	cert = mbedtls_ssl_get_peer_cert(ssl);
++	if (!cert) {
++		giterr_set(GITERR_SSL, "the server did not provide a certificate");
++		return -1;
++	}
++
++	/* Check the alternative names */
++	alts = &cert->subject_alt_names;
++	while (alts != NULL && matched != 1) {
++		// Buffer is too small
++		if( alts->buf.len >= sn_size )
++			goto on_error;
++
++		memcpy(alt_name, alts->buf.p, alts->buf.len);
++		alt_name[alts->buf.len] = '\0';
++
++		if (!memchr(alt_name, '\0', alts->buf.len)) {
++			if (check_host_name(alt_name, host) < 0)
++				matched = 0;
++			else
++				matched = 1;
++		}
++
++		alts = alts->next;
++	}
++	if (matched == 0)
++		goto cert_fail_name;
++
++	if (matched == 1)
++		return 0;
++
++	/* If no alternative names are available, check the common name */
++	ret = mbedtls_x509_dn_gets(subject_name, sn_size, &cert->subject);
++	if (ret == 0)
++		goto on_error;
++	if (memchr(subject_name, '\0', ret))
++		goto cert_fail_name;
++
++	if (check_host_name(subject_name, host) < 0)
++		goto cert_fail_name;
++
++	return 0;
 +
 +on_error:
-+    return ssl_set_error(ssl, 0);
++	return ssl_set_error(ssl, 0);
 +
 +cert_fail_name:
-+    giterr_set(GITERR_SSL, "hostname does not match certificate");
-+    return GIT_ECERTIFICATE;
++	giterr_set(GITERR_SSL, "hostname does not match certificate");
++	return GIT_ECERTIFICATE;
 +}
 +
 +typedef struct {
-+    git_stream parent;
-+    git_stream *io;
-+    bool connected;
-+    char *host;
-+    mbedtls_ssl_context *ssl;
-+    git_cert_x509 cert_info;
++	git_stream parent;
++	git_stream *io;
++	bool connected;
++	char *host;
++	mbedtls_ssl_context *ssl;
++	git_cert_x509 cert_info;
 +} mbedtls_stream;
 +
 +
 +int mbedtls_connect(git_stream *stream)
 +{
-+    int ret;
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
++	int ret;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
 +
-+    if ((ret = git_stream_connect(st->io)) < 0)
-+        return ret;
++	if ((ret = git_stream_connect(st->io)) < 0)
++		return ret;
 +
-+    st->connected = true;
++	st->connected = true;
 +
-+    mbedtls_ssl_set_hostname(st->ssl, st->host);
++	mbedtls_ssl_set_hostname(st->ssl, st->host);
 +
-+    mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL);
++	mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL);
 +
-+    if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0)
-+        return ssl_set_error(st->ssl, ret);
++	if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0)
++		return ssl_set_error(st->ssl, ret);
 +
-+    return verify_server_cert(st->ssl, st->host);
++	return verify_server_cert(st->ssl, st->host);
 +}
 +
 +int mbedtls_certificate(git_cert **out, git_stream *stream)
 +{
-+    unsigned char *encoded_cert;
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
++	unsigned char *encoded_cert;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
 +
-+    const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl);
-+    if (!cert) {
-+        giterr_set(GITERR_SSL, "the server did not provide a certificate");
-+        return -1;
-+    }
++	const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl);
++	if (!cert) {
++		giterr_set(GITERR_SSL, "the server did not provide a certificate");
++		return -1;
++	}
 +
-+    /* Retrieve the length of the certificate first */
-+    if (cert->raw.len == 0) {
-+        giterr_set(GITERR_NET, "failed to retrieve certificate information");
-+        return -1;
-+    }
++	/* Retrieve the length of the certificate first */
++	if (cert->raw.len == 0) {
++		giterr_set(GITERR_NET, "failed to retrieve certificate information");
++		return -1;
++	}
 +
-+    encoded_cert = git__malloc(cert->raw.len);
-+    GITERR_CHECK_ALLOC(encoded_cert);
-+    memcpy(encoded_cert, cert->raw.p, cert->raw.len);
++	encoded_cert = git__malloc(cert->raw.len);
++	GITERR_CHECK_ALLOC(encoded_cert);
++	memcpy(encoded_cert, cert->raw.p, cert->raw.len);
 +
-+    st->cert_info.parent.cert_type = GIT_CERT_X509;
-+    st->cert_info.data = encoded_cert;
-+    st->cert_info.len = cert->raw.len;
++	st->cert_info.parent.cert_type = GIT_CERT_X509;
++	st->cert_info.data = encoded_cert;
++	st->cert_info.len = cert->raw.len;
 +
-+    *out = &st->cert_info.parent;
++	*out = &st->cert_info.parent;
 +
-+    return 0;
++	return 0;
 +}
 +
-+static int mbedtls_set_proxy(git_stream *stream, const char *proxy_url)
++static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options)
 +{
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
 +
-+    return git_stream_set_proxy(st->io, proxy_url);
++	return git_stream_set_proxy(st->io, proxy_options);
 +}
 +
 +ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags)
 +{
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
-+    int ret;
++	size_t read = 0;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
 +
-+    GIT_UNUSED(flags);
++	GIT_UNUSED(flags);
 +
-+    if ((ret = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0) {
-+        return ssl_set_error(st->ssl, ret);
-+    }
++	do {
++		int error = mbedtls_ssl_write(st->ssl, (const unsigned char *)data + read, len - read);
++		if (error <= 0) {
++			return ssl_set_error(st->ssl, error);
++		}
++		read += error;
++	} while (read < len);
 +
-+    return ret;
++	return read;
 +}
 +
 +ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len)
 +{
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
-+    int ret;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
++	int ret;
 +
-+    if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0)
-+        ssl_set_error(st->ssl, ret);
++	if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0)
++		ssl_set_error(st->ssl, ret);
 +
-+    return ret;
++	return ret;
 +}
 +
 +int mbedtls_stream_close(git_stream *stream)
 +{
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
-+    int ret = 0;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
++	int ret = 0;
 +
-+    if (st->connected && (ret = ssl_teardown(st->ssl)) != 0)
-+        return -1;
++	if (st->connected && (ret = ssl_teardown(st->ssl)) != 0)
++		return -1;
 +
-+    st->connected = false;
++	st->connected = false;
 +
-+    return git_stream_close(st->io);
++	return git_stream_close(st->io);
 +}
 +
 +void mbedtls_stream_free(git_stream *stream)
 +{
-+    mbedtls_stream *st = (mbedtls_stream *) stream;
++	mbedtls_stream *st = (mbedtls_stream *) stream;
 +
-+    git__free(st->host);
-+    git__free(st->cert_info.data);
-+    git_stream_free(st->io);
-+    git__free(st->ssl);
-+    git__free(st);
++	git__free(st->host);
++	git__free(st->cert_info.data);
++	git_stream_free(st->io);
++	git__free(st->ssl);
++	git__free(st);
 +}
 +
 +int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port)
 +{
-+    int error;
-+    mbedtls_stream *st;
++	int error;
++	mbedtls_stream *st;
 +
-+    st = git__calloc(1, sizeof(mbedtls_stream));
-+    GITERR_CHECK_ALLOC(st);
++	st = git__calloc(1, sizeof(mbedtls_stream));
++	GITERR_CHECK_ALLOC(st);
 +
 +#ifdef GIT_CURL
-+    error = git_curl_stream_new(&st->io, host, port);
++	error = git_curl_stream_new(&st->io, host, port);
 +#else
-+    error = git_socket_stream_new(&st->io, host, port);
++	error = git_socket_stream_new(&st->io, host, port);
 +#endif
 +
-+    if (error < 0)
-+        return error;
-+
-+    st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
-+    GITERR_CHECK_ALLOC(st->ssl);
-+    mbedtls_ssl_init(st->ssl);
-+    if( (error = mbedtls_ssl_setup(st->ssl, git__ssl_conf)) != 0 ) {
-+        mbedtls_ssl_free(st->ssl);
-+        giterr_set(GITERR_SSL, "failed to create ssl object");
-+        return -1;
-+    }
-+
-+    st->host = git__strdup(host);
-+    GITERR_CHECK_ALLOC(st->host);
-+
-+    st->parent.version = GIT_STREAM_VERSION;
-+    st->parent.encrypted = 1;
-+    st->parent.proxy_support = git_stream_supports_proxy(st->io);
-+    st->parent.connect = mbedtls_connect;
-+    st->parent.certificate = mbedtls_certificate;
-+    st->parent.set_proxy = mbedtls_set_proxy;
-+    st->parent.read = mbedtls_stream_read;
-+    st->parent.write = mbedtls_stream_write;
-+    st->parent.close = mbedtls_stream_close;
-+    st->parent.free = mbedtls_stream_free;
-+
-+    *out = (git_stream *) st;
-+    return 0;
-+}
++	if (error < 0)
++		goto out_err;
 +
-+#else
++	st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
++	GITERR_CHECK_ALLOC(st->ssl);
++	mbedtls_ssl_init(st->ssl);
++	if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
++		giterr_set(GITERR_SSL, "failed to create ssl object");
++		error = -1;
++		goto out_err;
++	}
 +
-+#include "stream.h"
-+#include "git2/sys/openssl.h"
++	st->host = git__strdup(host);
++	GITERR_CHECK_ALLOC(st->host);
++
++	st->parent.version = GIT_STREAM_VERSION;
++	st->parent.encrypted = 1;
++	st->parent.proxy_support = git_stream_supports_proxy(st->io);
++	st->parent.connect = mbedtls_connect;
++	st->parent.certificate = mbedtls_certificate;
++	st->parent.set_proxy = mbedtls_set_proxy;
++	st->parent.read = mbedtls_stream_read;
++	st->parent.write = mbedtls_stream_write;
++	st->parent.close = mbedtls_stream_close;
++	st->parent.free = mbedtls_stream_free;
++
++	*out = (git_stream *) st;
++	return 0;
++
++out_err:
++	mbedtls_ssl_free(st->ssl);
++	git_stream_free(st->io);
++	git__free(st);
++
++	return error;
++}
++
++int git_mbedtls_set_cert_file(const char *path, int is_dir)
++{
++	int ret = 0;
++	char errbuf[512];
++	mbedtls_x509_crt *cacert;
++
++	assert(path != NULL);
++
++	cacert = git__malloc(sizeof(mbedtls_x509_crt));
++	mbedtls_x509_crt_init(cacert);
++	if (is_dir) {
++		ret = mbedtls_x509_crt_parse_path(cacert, path);
++	} else {
++		ret = mbedtls_x509_crt_parse_file(cacert, path);
++	}
++	// mbedtls_x509_crt_parse_path returns the number of invalid certs on success
++	if (ret <= 0) {
++		mbedtls_x509_crt_free(cacert);
++		git__free(cacert);
++		mbedtls_strerror( ret, errbuf, 512 );
++		giterr_set(GITERR_SSL, "failed to load CA certificates : %s (%d)", errbuf, ret);
++		return -1;
++	}
++
++	mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
++	git__free(git__ssl_conf->ca_chain);
++	mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
++
++	return 0;
++}
++
++#else
++
++#include "stream.h"
 +
 +int git_mbedtls_stream_global_init(void)
 +{
-+    return 0;
++	return 0;
++}
++
++int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port)
++{
++	GIT_UNUSED(out);
++	GIT_UNUSED(host);
++	GIT_UNUSED(port);
++
++	giterr_set(GITERR_SSL, "mbedTLS is not supported in this version");
++	return -1;
++}
++
++int git_mbedtls_set_cert_file(const char *path, int is_dir)
++{
++	GIT_UNUSED(is_dir);
++
++	giterr_set(GITERR_SSL, "mbedTLS is not supported in this version");
++	return -1;
++}
++
++#endif
+diff --git a/src/streams/mbedtls.h b/src/streams/mbedtls.h
+new file mode 100644
+index 0000000..7501397
+--- /dev/null
++++ b/src/streams/mbedtls.h
+@@ -0,0 +1,18 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++#ifndef INCLUDE_mbedtls_stream_h__
++#define INCLUDE_mbedtls_stream_h__
++
++#include "git2/sys/stream.h"
++
++extern int git_mbedtls_stream_global_init(void);
++
++extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port);
++
++extern int git_mbedtls_set_cert_file(const char *path, int is_dir);
++
++#endif
+diff --git a/src/streams/openssl.c b/src/streams/openssl.c
+new file mode 100644
+index 0000000..8668b78
+--- /dev/null
++++ b/src/streams/openssl.c
+@@ -0,0 +1,675 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++
++#ifdef GIT_OPENSSL
++
++#include <ctype.h>
++
++#include "global.h"
++#include "posix.h"
++#include "stream.h"
++#include "streams/socket.h"
++#include "streams/openssl.h"
++#include "netops.h"
++#include "git2/transport.h"
++#include "git2/sys/openssl.h"
++
++#ifdef GIT_CURL
++# include "streams/curl.h"
++#endif
++
++#ifndef GIT_WIN32
++# include <sys/types.h>
++# include <sys/socket.h>
++# include <netinet/in.h>
++#endif
++
++#include <openssl/ssl.h>
++#include <openssl/err.h>
++#include <openssl/x509v3.h>
++#include <openssl/bio.h>
++
++SSL_CTX *git__ssl_ctx;
++
++#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
++
++#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
++
++static git_mutex *openssl_locks;
++
++static void openssl_locking_function(
++	int mode, int n, const char *file, int line)
++{
++	int lock;
++
++	GIT_UNUSED(file);
++	GIT_UNUSED(line);
++
++	lock = mode & CRYPTO_LOCK;
++
++	if (lock) {
++		git_mutex_lock(&openssl_locks[n]);
++	} else {
++		git_mutex_unlock(&openssl_locks[n]);
++	}
++}
++
++static void shutdown_ssl_locking(void)
++{
++	int num_locks, i;
++
++	num_locks = CRYPTO_num_locks();
++	CRYPTO_set_locking_callback(NULL);
++
++	for (i = 0; i < num_locks; ++i)
++		git_mutex_free(&openssl_locks[i]);
++	git__free(openssl_locks);
++}
++
++#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
++
++static BIO_METHOD *git_stream_bio_method;
++static int init_bio_method(void);
++
++/**
++ * This function aims to clean-up the SSL context which
++ * we allocated.
++ */
++static void shutdown_ssl(void)
++{
++	if (git_stream_bio_method) {
++		BIO_meth_free(git_stream_bio_method);
++		git_stream_bio_method = NULL;
++	}
++
++	if (git__ssl_ctx) {
++		SSL_CTX_free(git__ssl_ctx);
++		git__ssl_ctx = NULL;
++	}
++}
++
++int git_openssl_stream_global_init(void)
++{
++#ifdef GIT_OPENSSL
++	long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
++	const char *ciphers = git_libgit2__ssl_ciphers();
++
++	/* Older OpenSSL and MacOS OpenSSL doesn't have this */
++#ifdef SSL_OP_NO_COMPRESSION
++	ssl_opts |= SSL_OP_NO_COMPRESSION;
++#endif
++
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
++	SSL_load_error_strings();
++	OpenSSL_add_ssl_algorithms();
++#else
++	OPENSSL_init_ssl(0, NULL);
++#endif
++
++	/*
++	 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
++	 * which use the SSL hellos, which are often used for
++	 * compatibility. We then disable SSL so we only allow OpenSSL
++	 * to speak TLSv1 to perform the encryption itself.
++	 */
++	git__ssl_ctx = SSL_CTX_new(SSLv23_method());
++	SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
++	SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
++	SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
++	if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
++		SSL_CTX_free(git__ssl_ctx);
++		git__ssl_ctx = NULL;
++		return -1;
++	}
++
++	if (!ciphers) {
++		ciphers = GIT_SSL_DEFAULT_CIPHERS;
++	}
++
++	if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
++		SSL_CTX_free(git__ssl_ctx);
++		git__ssl_ctx = NULL;
++		return -1;
++	}
++
++	if (init_bio_method() < 0) {
++		SSL_CTX_free(git__ssl_ctx);
++		git__ssl_ctx = NULL;
++		return -1;
++	}
++
++#endif
++
++	git__on_shutdown(shutdown_ssl);
++
++	return 0;
++}
++
++int git_openssl_set_locking(void)
++{
++#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
++	int num_locks, i;
++
++	num_locks = CRYPTO_num_locks();
++	openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
++	GITERR_CHECK_ALLOC(openssl_locks);
++
++	for (i = 0; i < num_locks; i++) {
++		if (git_mutex_init(&openssl_locks[i]) != 0) {
++			giterr_set(GITERR_SSL, "failed to initialize openssl locks");
++			return -1;
++		}
++	}
++
++	CRYPTO_set_locking_callback(openssl_locking_function);
++	git__on_shutdown(shutdown_ssl_locking);
++	return 0;
++#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
++	return 0;
++#else
++	giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
++	return -1;
++#endif
++}
++
++
++static int bio_create(BIO *b)
++{
++	BIO_set_init(b, 1);
++	BIO_set_data(b, NULL);
++
++	return 1;
++}
++
++static int bio_destroy(BIO *b)
++{
++	if (!b)
++		return 0;
++
++	BIO_set_data(b, NULL);
++
++	return 1;
++}
++
++static int bio_read(BIO *b, char *buf, int len)
++{
++	git_stream *io = (git_stream *) BIO_get_data(b);
++
++	return (int) git_stream_read(io, buf, len);
++}
++
++static int bio_write(BIO *b, const char *buf, int len)
++{
++	git_stream *io = (git_stream *) BIO_get_data(b);
++
++	return (int) git_stream_write(io, buf, len, 0);
++}
++
++static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
++{
++	GIT_UNUSED(b);
++	GIT_UNUSED(num);
++	GIT_UNUSED(ptr);
++
++	if (cmd == BIO_CTRL_FLUSH)
++		return 1;
++
++	return 0;
++}
++
++static int bio_gets(BIO *b, char *buf, int len)
++{
++	GIT_UNUSED(b);
++	GIT_UNUSED(buf);
++	GIT_UNUSED(len);
++	return -1;
++}
++
++static int bio_puts(BIO *b, const char *str)
++{
++	return bio_write(b, str, strlen(str));
++}
++
++static int init_bio_method(void)
++{
++	/* Set up the BIO_METHOD we use for wrapping our own stream implementations */
++	git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
++	GITERR_CHECK_ALLOC(git_stream_bio_method);
++
++	BIO_meth_set_write(git_stream_bio_method, bio_write);
++	BIO_meth_set_read(git_stream_bio_method, bio_read);
++	BIO_meth_set_puts(git_stream_bio_method, bio_puts);
++	BIO_meth_set_gets(git_stream_bio_method, bio_gets);
++	BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
++	BIO_meth_set_create(git_stream_bio_method, bio_create);
++	BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
++
++	return 0;
++}
++
++static int ssl_set_error(SSL *ssl, int error)
++{
++	int err;
++	unsigned long e;
++
++	err = SSL_get_error(ssl, error);
++
++	assert(err != SSL_ERROR_WANT_READ);
++	assert(err != SSL_ERROR_WANT_WRITE);
++
++	switch (err) {
++	case SSL_ERROR_WANT_CONNECT:
++	case SSL_ERROR_WANT_ACCEPT:
++		giterr_set(GITERR_NET, "SSL error: connection failure");
++		break;
++	case SSL_ERROR_WANT_X509_LOOKUP:
++		giterr_set(GITERR_NET, "SSL error: x509 error");
++		break;
++	case SSL_ERROR_SYSCALL:
++		e = ERR_get_error();
++		if (e > 0) {
++			giterr_set(GITERR_NET, "SSL error: %s",
++					ERR_error_string(e, NULL));
++			break;
++		} else if (error < 0) {
++			giterr_set(GITERR_OS, "SSL error: syscall failure");
++			break;
++		}
++		giterr_set(GITERR_NET, "SSL error: received early EOF");
++		return GIT_EEOF;
++		break;
++	case SSL_ERROR_SSL:
++		e = ERR_get_error();
++		giterr_set(GITERR_NET, "SSL error: %s",
++				ERR_error_string(e, NULL));
++		break;
++	case SSL_ERROR_NONE:
++	case SSL_ERROR_ZERO_RETURN:
++	default:
++		giterr_set(GITERR_NET, "SSL error: unknown error");
++		break;
++	}
++	return -1;
++}
++
++static int ssl_teardown(SSL *ssl)
++{
++	int ret;
++
++	ret = SSL_shutdown(ssl);
++	if (ret < 0)
++		ret = ssl_set_error(ssl, ret);
++	else
++		ret = 0;
++
++	return ret;
++}
++
++static int check_host_name(const char *name, const char *host)
++{
++	if (!strcasecmp(name, host))
++		return 0;
++
++	if (gitno__match_host(name, host) < 0)
++		return -1;
++
++	return 0;
++}
++
++static int verify_server_cert(SSL *ssl, const char *host)
++{
++	X509 *cert;
++	X509_NAME *peer_name;
++	ASN1_STRING *str;
++	unsigned char *peer_cn = NULL;
++	int matched = -1, type = GEN_DNS;
++	GENERAL_NAMES *alts;
++	struct in6_addr addr6;
++	struct in_addr addr4;
++	void *addr;
++	int i = -1,j;
++
++	if (SSL_get_verify_result(ssl) != X509_V_OK) {
++		giterr_set(GITERR_SSL, "the SSL certificate is invalid");
++		return GIT_ECERTIFICATE;
++	}
++
++	/* Try to parse the host as an IP address to see if it is */
++	if (p_inet_pton(AF_INET, host, &addr4)) {
++		type = GEN_IPADD;
++		addr = &addr4;
++	} else {
++		if(p_inet_pton(AF_INET6, host, &addr6)) {
++			type = GEN_IPADD;
++			addr = &addr6;
++		}
++	}
++
++
++	cert = SSL_get_peer_certificate(ssl);
++	if (!cert) {
++		giterr_set(GITERR_SSL, "the server did not provide a certificate");
++		return -1;
++	}
++
++	/* Check the alternative names */
++	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
++	if (alts) {
++		int num;
++
++		num = sk_GENERAL_NAME_num(alts);
++		for (i = 0; i < num && matched != 1; i++) {
++			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
++			const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
++			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
++
++			/* Skip any names of a type we're not looking for */
++			if (gn->type != type)
++				continue;
++
++			if (type == GEN_DNS) {
++				/* If it contains embedded NULs, don't even try */
++				if (memchr(name, '\0', namelen))
++					continue;
++
++				if (check_host_name(name, host) < 0)
++					matched = 0;
++				else
++					matched = 1;
++			} else if (type == GEN_IPADD) {
++				/* Here name isn't so much a name but a binary representation of the IP */
++				matched = !!memcmp(name, addr, namelen);
++			}
++		}
++	}
++	GENERAL_NAMES_free(alts);
++
++	if (matched == 0)
++		goto cert_fail_name;
++
++	if (matched == 1)
++		return 0;
++
++	/* If no alternative names are available, check the common name */
++	peer_name = X509_get_subject_name(cert);
++	if (peer_name == NULL)
++		goto on_error;
++
++	if (peer_name) {
++		/* Get the index of the last CN entry */
++		while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
++			i = j;
++	}
++
++	if (i < 0)
++		goto on_error;
++
++	str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
++	if (str == NULL)
++		goto on_error;
++
++	/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
++	if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
++		int size = ASN1_STRING_length(str);
++
++		if (size > 0) {
++			peer_cn = OPENSSL_malloc(size + 1);
++			GITERR_CHECK_ALLOC(peer_cn);
++			memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
++			peer_cn[size] = '\0';
++		} else {
++			goto cert_fail_name;
++		}
++	} else {
++		int size = ASN1_STRING_to_UTF8(&peer_cn, str);
++		GITERR_CHECK_ALLOC(peer_cn);
++		if (memchr(peer_cn, '\0', size))
++			goto cert_fail_name;
++	}
++
++	if (check_host_name((char *)peer_cn, host) < 0)
++		goto cert_fail_name;
++
++	OPENSSL_free(peer_cn);
++
++	return 0;
++
++on_error:
++	OPENSSL_free(peer_cn);
++	return ssl_set_error(ssl, 0);
++
++cert_fail_name:
++	OPENSSL_free(peer_cn);
++	giterr_set(GITERR_SSL, "hostname does not match certificate");
++	return GIT_ECERTIFICATE;
++}
++
++typedef struct {
++	git_stream parent;
++	git_stream *io;
++	bool connected;
++	char *host;
++	SSL *ssl;
++	git_cert_x509 cert_info;
++} openssl_stream;
++
++int openssl_close(git_stream *stream);
++
++int openssl_connect(git_stream *stream)
++{
++	int ret;
++	BIO *bio;
++	openssl_stream *st = (openssl_stream *) stream;
++
++	if ((ret = git_stream_connect(st->io)) < 0)
++		return ret;
++
++	st->connected = true;
++
++	bio = BIO_new(git_stream_bio_method);
++	GITERR_CHECK_ALLOC(bio);
++
++	BIO_set_data(bio, st->io);
++	SSL_set_bio(st->ssl, bio, bio);
++
++	/* specify the host in case SNI is needed */
++#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
++	SSL_set_tlsext_host_name(st->ssl, st->host);
++#endif
++
++	if ((ret = SSL_connect(st->ssl)) <= 0)
++		return ssl_set_error(st->ssl, ret);
++
++	return verify_server_cert(st->ssl, st->host);
++}
++
++int openssl_certificate(git_cert **out, git_stream *stream)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++	int len;
++	X509 *cert = SSL_get_peer_certificate(st->ssl);
++	unsigned char *guard, *encoded_cert;
++
++	/* Retrieve the length of the certificate first */
++	len = i2d_X509(cert, NULL);
++	if (len < 0) {
++		giterr_set(GITERR_NET, "failed to retrieve certificate information");
++		return -1;
++	}
++
++	encoded_cert = git__malloc(len);
++	GITERR_CHECK_ALLOC(encoded_cert);
++	/* i2d_X509 makes 'guard' point to just after the data */
++	guard = encoded_cert;
++
++	len = i2d_X509(cert, &guard);
++	if (len < 0) {
++		git__free(encoded_cert);
++		giterr_set(GITERR_NET, "failed to retrieve certificate information");
++		return -1;
++	}
++
++	st->cert_info.parent.cert_type = GIT_CERT_X509;
++	st->cert_info.data = encoded_cert;
++	st->cert_info.len = len;
++
++	*out = &st->cert_info.parent;
++
++	return 0;
++}
++
++static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++
++	return git_stream_set_proxy(st->io, proxy_opts);
++}
++
++ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++	int ret;
++
++	GIT_UNUSED(flags);
++
++	if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
++		return ssl_set_error(st->ssl, ret);
++	}
++
++	return ret;
++}
++
++ssize_t openssl_read(git_stream *stream, void *data, size_t len)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++	int ret;
++
++	if ((ret = SSL_read(st->ssl, data, len)) <= 0)
++		return ssl_set_error(st->ssl, ret);
++
++	return ret;
++}
++
++int openssl_close(git_stream *stream)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++	int ret;
++
++	if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
++		return -1;
++
++	st->connected = false;
++
++	return git_stream_close(st->io);
++}
++
++void openssl_free(git_stream *stream)
++{
++	openssl_stream *st = (openssl_stream *) stream;
++
++	SSL_free(st->ssl);
++	git__free(st->host);
++	git__free(st->cert_info.data);
++	git_stream_free(st->io);
++	git__free(st);
++}
++
++int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
++{
++	int error;
++	openssl_stream *st;
++
++	st = git__calloc(1, sizeof(openssl_stream));
++	GITERR_CHECK_ALLOC(st);
++
++	st->io = NULL;
++#ifdef GIT_CURL
++	error = git_curl_stream_new(&st->io, host, port);
++#else
++	error = git_socket_stream_new(&st->io, host, port);
++#endif
++
++	if (error < 0)
++		goto out_err;
++
++	st->ssl = SSL_new(git__ssl_ctx);
++	if (st->ssl == NULL) {
++		giterr_set(GITERR_SSL, "failed to create ssl object");
++		error = -1;
++		goto out_err;
++	}
++
++	st->host = git__strdup(host);
++	GITERR_CHECK_ALLOC(st->host);
++
++	st->parent.version = GIT_STREAM_VERSION;
++	st->parent.encrypted = 1;
++	st->parent.proxy_support = git_stream_supports_proxy(st->io);
++	st->parent.connect = openssl_connect;
++	st->parent.certificate = openssl_certificate;
++	st->parent.set_proxy = openssl_set_proxy;
++	st->parent.read = openssl_read;
++	st->parent.write = openssl_write;
++	st->parent.close = openssl_close;
++	st->parent.free = openssl_free;
++
++	*out = (git_stream *) st;
++	return 0;
++
++out_err:
++	git_stream_free(st->io);
++	git__free(st);
++
++	return error;
++}
++
++int git_openssl_set_cert_file(const char *file, const char *path)
++{
++	if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
++		giterr_set(GITERR_SSL, "SSL error: %s",
++				   ERR_error_string(ERR_get_error(), NULL));
++		return -1;
++	}
++	return 0;
++}
++
++#else
++
++#include "stream.h"
++#include "git2/sys/openssl.h"
++
++int git_openssl_stream_global_init(void)
++{
++	return 0;
++}
++
++int git_openssl_set_locking(void)
++{
++	giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
++	return -1;
++}
++
++int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
++{
++	GIT_UNUSED(out);
++	GIT_UNUSED(host);
++	GIT_UNUSED(port);
++
++	giterr_set(GITERR_SSL, "openssl is not supported in this version");
++	return -1;
++}
++
++int git_openssl_set_ca_location(const char *file, const char *path)
++{
++	GIT_UNUSED(file);
++	GIT_UNUSED(path);
++
++	giterr_set(GITERR_SSL, "openssl is not supported in this version");
++	return -1;
++}
++
++#endif
+diff --git a/src/streams/openssl.h b/src/streams/openssl.h
+new file mode 100644
+index 0000000..8c7a84d
+--- /dev/null
++++ b/src/streams/openssl.h
+@@ -0,0 +1,124 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++#ifndef INCLUDE_openssl_stream_h__
++#define INCLUDE_openssl_stream_h__
++
++#include "git2/sys/stream.h"
++
++extern int git_openssl_stream_global_init(void);
++
++extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
++
++extern int git_openssl_set_cert_file(const char *file, const char *path);
++
++/*
++ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
++ * which do not exist in previous versions. We define these inline functions so
++ * we can program against the interface instead of littering the implementation
++ * with ifdefs.
++ */
++#ifdef GIT_OPENSSL
++# include <openssl/ssl.h>
++# include <openssl/err.h>
++# include <openssl/x509v3.h>
++# include <openssl/bio.h>
++
++
++
++# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
++
++GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
++{
++	BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
++	if (!meth) {
++		return NULL;
++	}
++
++	meth->type = type;
++	meth->name = name;
++
++	return meth;
++}
++
++GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
++{
++	git__free(biom);
++}
++
++GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
++{
++	biom->bwrite = write;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
++{
++	biom->bread = read;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
++{
++	biom->bputs = puts;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
++
++{
++	biom->bgets = gets;
++	return 1;
 +}
 +
-+int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port)
++GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
++{
++	biom->ctrl = ctrl;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
++{
++	biom->create = create;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
++{
++	biom->destroy = destroy;
++	return 1;
++}
++
++GIT_INLINE(int) BIO_get_new_index(void)
++{
++	/* This exists as of 1.1 so before we'd just have 0 */
++	return 0;
++}
++
++GIT_INLINE(void) BIO_set_init(BIO *b, int init)
++{
++	b->init = init;
++}
++
++GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
++{
++	a->ptr = ptr;
++}
++
++GIT_INLINE(void*) BIO_get_data(BIO *a)
++{
++	return a->ptr;
++}
++
++GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
++{
++	return ASN1_STRING_data((ASN1_STRING *)x);
++}
++
++# endif // OpenSSL < 1.1
++#endif // GIT_OPENSSL
++
++#endif
+diff --git a/src/streams/socket.c b/src/streams/socket.c
+new file mode 100644
+index 0000000..1150b40
+--- /dev/null
++++ b/src/streams/socket.c
+@@ -0,0 +1,210 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++
++#include "common.h"
++#include "posix.h"
++#include "netops.h"
++#include "stream.h"
++#include "streams/socket.h"
++
++#ifndef _WIN32
++#	include <sys/types.h>
++#	include <sys/socket.h>
++#	include <sys/select.h>
++#	include <sys/time.h>
++#	include <netdb.h>
++#	include <netinet/in.h>
++#       include <arpa/inet.h>
++#else
++#	include <winsock2.h>
++#	include <ws2tcpip.h>
++#	ifdef _MSC_VER
++#		pragma comment(lib, "ws2_32")
++#	endif
++#endif
++
++#ifdef GIT_WIN32
++static void net_set_error(const char *str)
++{
++	int error = WSAGetLastError();
++	char * win32_error = git_win32_get_error_message(error);
++
++	if (win32_error) {
++		giterr_set(GITERR_NET, "%s: %s", str, win32_error);
++		git__free(win32_error);
++	} else {
++		giterr_set(GITERR_NET, str);
++	}
++}
++#else
++static void net_set_error(const char *str)
 +{
-+    GIT_UNUSED(out);
-+    GIT_UNUSED(host);
-+    GIT_UNUSED(port);
++	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
++}
++#endif
++
++static int close_socket(GIT_SOCKET s)
++{
++	if (s == INVALID_SOCKET)
++		return 0;
++
++#ifdef GIT_WIN32
++	if (SOCKET_ERROR == closesocket(s))
++		return -1;
++
++	if (0 != WSACleanup()) {
++		giterr_set(GITERR_OS, "winsock cleanup failed");
++		return -1;
++	}
++
++	return 0;
++#else
++	return close(s);
++#endif
 +
-+    giterr_set(GITERR_SSL, "mbedTLS is not supported in this version");
-+    return -1;
 +}
 +
++int socket_connect(git_stream *stream)
++{
++	struct addrinfo *info = NULL, *p;
++	struct addrinfo hints;
++	git_socket_stream *st = (git_socket_stream *) stream;
++	GIT_SOCKET s = INVALID_SOCKET;
++	int ret;
++
++#ifdef GIT_WIN32
++	/* on win32, the WSA context needs to be initialized
++	 * before any socket calls can be performed */
++	WSADATA wsd;
++
++	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
++		giterr_set(GITERR_OS, "winsock init failed");
++		return -1;
++	}
++
++	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
++		WSACleanup();
++		giterr_set(GITERR_OS, "winsock init failed");
++		return -1;
++	}
 +#endif
 +
-diff --git a/src/mbedtls_stream.h b/src/mbedtls_stream.h
++	memset(&hints, 0x0, sizeof(struct addrinfo));
++	hints.ai_socktype = SOCK_STREAM;
++	hints.ai_family = AF_UNSPEC;
++
++	if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
++		giterr_set(GITERR_NET,
++			   "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
++		return -1;
++	}
++
++	for (p = info; p != NULL; p = p->ai_next) {
++		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
++
++		if (s == INVALID_SOCKET)
++			continue;
++
++		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
++			break;
++
++		/* If we can't connect, try the next one */
++		close_socket(s);
++		s = INVALID_SOCKET;
++	}
++
++	/* Oops, we couldn't connect to any address */
++	if (s == INVALID_SOCKET && p == NULL) {
++		giterr_set(GITERR_OS, "failed to connect to %s", st->host);
++		p_freeaddrinfo(info);
++		return -1;
++	}
++
++	st->s = s;
++	p_freeaddrinfo(info);
++	return 0;
++}
++
++ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
++{
++	ssize_t ret;
++	size_t off = 0;
++	git_socket_stream *st = (git_socket_stream *) stream;
++
++	while (off < len) {
++		errno = 0;
++		ret = p_send(st->s, data + off, len - off, flags);
++		if (ret < 0) {
++			net_set_error("Error sending data");
++			return -1;
++		}
++
++		off += ret;
++	}
++
++	return off;
++}
++
++ssize_t socket_read(git_stream *stream, void *data, size_t len)
++{
++	ssize_t ret;
++	git_socket_stream *st = (git_socket_stream *) stream;
++
++	if ((ret = p_recv(st->s, data, len, 0)) < 0)
++		net_set_error("Error receiving socket data");
++
++	return ret;
++}
++
++int socket_close(git_stream *stream)
++{
++	git_socket_stream *st = (git_socket_stream *) stream;
++	int error;
++
++	error = close_socket(st->s);
++	st->s = INVALID_SOCKET;
++
++	return error;
++}
++
++void socket_free(git_stream *stream)
++{
++	git_socket_stream *st = (git_socket_stream *) stream;
++
++	git__free(st->host);
++	git__free(st->port);
++	git__free(st);
++}
++
++int git_socket_stream_new(git_stream **out, const char *host, const char *port)
++{
++	git_socket_stream *st;
++
++	assert(out && host);
++
++	st = git__calloc(1, sizeof(git_socket_stream));
++	GITERR_CHECK_ALLOC(st);
++
++	st->host = git__strdup(host);
++	GITERR_CHECK_ALLOC(st->host);
++
++	if (port) {
++		st->port = git__strdup(port);
++		GITERR_CHECK_ALLOC(st->port);
++	}
++
++	st->parent.version = GIT_STREAM_VERSION;
++	st->parent.connect = socket_connect;
++	st->parent.write = socket_write;
++	st->parent.read = socket_read;
++	st->parent.close = socket_close;
++	st->parent.free = socket_free;
++	st->s = INVALID_SOCKET;
++
++	*out = (git_stream *) st;
++	return 0;
++}
+diff --git a/src/streams/socket.h b/src/streams/socket.h
 new file mode 100644
-index 0000000..5cb1071
+index 0000000..8e9949f
 --- /dev/null
-+++ b/src/mbedtls_stream.h
-@@ -0,0 +1,16 @@
++++ b/src/streams/socket.h
+@@ -0,0 +1,21 @@
 +/*
 + * Copyright (C) the libgit2 contributors. All rights reserved.
 + *
 + * This file is part of libgit2, distributed under the GNU GPL v2 with
 + * a Linking Exception. For full terms see the included COPYING file.
 + */
-+#ifndef INCLUDE_mbedtls_stream_h__
-+#define INCLUDE_mbedtls_stream_h__
++#ifndef INCLUDE_socket_stream_h__
++#define INCLUDE_socket_stream_h__
 +
-+#include "git2/sys/stream.h"
++#include "netops.h"
 +
-+extern int git_mbedtls_stream_global_init(void);
++typedef struct {
++	git_stream parent;
++	char *host;
++	char *port;
++	GIT_SOCKET s;
++} git_socket_stream;
 +
-+extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port);
++extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
 +
 +#endif
-diff --git a/src/settings.c b/src/settings.c
-index 0da19ea..65bbb41 100644
---- a/src/settings.c
-+++ b/src/settings.c
-@@ -9,6 +9,10 @@
- # include <openssl/err.h>
- #endif
- 
-+#ifdef GIT_MBEDTLS
-+# include <mbedtls/error.h>
+diff --git a/src/streams/stransport.c b/src/streams/stransport.c
+new file mode 100644
+index 0000000..4c099f9
+--- /dev/null
++++ b/src/streams/stransport.c
+@@ -0,0 +1,294 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++
++#ifdef GIT_SECURE_TRANSPORT
++
++#include <CoreFoundation/CoreFoundation.h>
++#include <Security/SecureTransport.h>
++#include <Security/SecCertificate.h>
++
++#include "git2/transport.h"
++
++#include "streams/socket.h"
++#include "streams/curl.h"
++
++static int stransport_error(OSStatus ret)
++{
++	CFStringRef message;
++
++	if (ret == noErr || ret == errSSLClosedGraceful) {
++		giterr_clear();
++		return 0;
++	}
++
++#if !TARGET_OS_IPHONE
++	message = SecCopyErrorMessageString(ret, NULL);
++	GITERR_CHECK_ALLOC(message);
++
++	giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
++	CFRelease(message);
++#else
++    giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
++    GIT_UNUSED(message);
 +#endif
 +
- #include <git2.h>
- #include "common.h"
- #include "sysdir.h"
-@@ -29,7 +33,7 @@ int git_libgit2_features()
- #ifdef GIT_THREADS
- 		| GIT_FEATURE_THREADS
- #endif
--#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
-+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_MBEDTLS)
- 		| GIT_FEATURE_HTTPS
- #endif
- #if defined(GIT_SSH)
-@@ -174,8 +178,34 @@ int git_libgit2_opts(int key, ...)
- 				error = -1;
- 			}
- 		}
-+#elif GIT_MBEDTLS
-+		{
-+			const char *file = va_arg(ap, const char *);
-+			const char *path = va_arg(ap, const char *);
-+			int ret = 0;
-+			char errbuf[512];
-+			mbedtls_x509_crt *cacert;
-+			cacert = git__malloc(sizeof(mbedtls_x509_crt));
-+			mbedtls_x509_crt_init(cacert);
-+			if (file) {
-+				ret = mbedtls_x509_crt_parse_file(cacert, file);
-+			} else if (path) {
-+				ret = mbedtls_x509_crt_parse_path(cacert, path);
-+			}
-+			if (!ret) {
-+				mbedtls_x509_crt_free(cacert);
-+				git__free(cacert);
-+				mbedtls_strerror( ret, errbuf, 512 );
-+				giterr_set(GITERR_SSL, "SSL error: failed to load CA certificates : %s (%d)", ret, errbuf);
-+				error = -1;
-+			} else {
-+				mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
-+				git__free(git__ssl_conf->ca_chain);
-+				mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
-+			}
++	return -1;
++}
++
++typedef struct {
++	git_stream parent;
++	git_stream *io;
++	SSLContextRef ctx;
++	CFDataRef der_data;
++	git_cert_x509 cert_info;
++} stransport_stream;
++
++static int stransport_connect(git_stream *stream)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++	int error;
++	SecTrustRef trust = NULL;
++	SecTrustResultType sec_res;
++	OSStatus ret;
++
++	if ((error = git_stream_connect(st->io)) < 0)
++		return error;
++
++	ret = SSLHandshake(st->ctx);
++	if (ret != errSSLServerAuthCompleted) {
++		giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
++		return -1;
++	}
++
++	if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
++		goto on_error;
++
++	if (!trust)
++		return GIT_ECERTIFICATE;
++
++	if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
++		goto on_error;
++
++	CFRelease(trust);
++
++	if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
++		giterr_set(GITERR_SSL, "internal security trust error");
++		return -1;
++	}
++
++	if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
++	    sec_res == kSecTrustResultFatalTrustFailure)
++		return GIT_ECERTIFICATE;
++
++	return 0;
++
++on_error:
++	if (trust)
++		CFRelease(trust);
++
++	return stransport_error(ret);
++}
++
++static int stransport_certificate(git_cert **out, git_stream *stream)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++	SecTrustRef trust = NULL;
++	SecCertificateRef sec_cert;
++	OSStatus ret;
++
++	if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
++		return stransport_error(ret);
++
++	sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
++	st->der_data = SecCertificateCopyData(sec_cert);
++	CFRelease(trust);
++
++	if (st->der_data == NULL) {
++		giterr_set(GITERR_SSL, "retrieved invalid certificate data");
++		return -1;
++	}
++
++	st->cert_info.parent.cert_type = GIT_CERT_X509;
++	st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
++	st->cert_info.len = CFDataGetLength(st->der_data);
++
++	*out = (git_cert *)&st->cert_info;
++	return 0;
++}
++
++static int stransport_set_proxy(
++	git_stream *stream,
++	const git_proxy_options *proxy_opts)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++
++	return git_stream_set_proxy(st->io, proxy_opts);
++}
++
++/*
++ * Contrary to typical network IO callbacks, Secure Transport write callback is
++ * expected to write *all* passed data, not just as much as it can, and any
++ * other case would be considered a failure.
++ *
++ * This behavior is actually not specified in the Apple documentation, but is
++ * required for things to work correctly (and incidentally, that's also how
++ * Apple implements it in its projects at opensource.apple.com).
++ *
++ * Libgit2 streams happen to already have this very behavior so this is just
++ * passthrough.
++ */
++static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
++{
++	git_stream *io = (git_stream *) conn;
++
++	if (git_stream_write(io, data, *len, 0) < 0) {
++		return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
++	}
++
++	return noErr;
++}
++
++static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++	size_t data_len, processed;
++	OSStatus ret;
++
++	GIT_UNUSED(flags);
++
++	data_len = len;
++	if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
++		return stransport_error(ret);
++
++	return processed;
++}
++
++/*
++ * Contrary to typical network IO callbacks, Secure Transport read callback is
++ * expected to read *exactly* the requested number of bytes, not just as much
++ * as it can, and any other case would be considered a failure.
++ *
++ * This behavior is actually not specified in the Apple documentation, but is
++ * required for things to work correctly (and incidentally, that's also how
++ * Apple implements it in its projects at opensource.apple.com).
++ */
++static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
++{
++	git_stream *io = (git_stream *) conn;
++	OSStatus error = noErr;
++	size_t off = 0;
++	ssize_t ret;
++
++	do {
++		ret = git_stream_read(io, data + off, *len - off);
++		if (ret < 0) {
++			error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
++			break;
 +		}
- #else
--		giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
-+		giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL or mbedTLS is not enabled");
- 		error = -1;
- #endif
- 		break;
++		if (ret == 0) {
++			error = errSSLClosedGraceful;
++			break;
++		}
++
++		off += ret;
++	} while (off < *len);
++
++	*len = off;
++	return error;
++}
++
++static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++	size_t processed;
++	OSStatus ret;
++
++	if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
++		return stransport_error(ret);
++
++	return processed;
++}
++
++static int stransport_close(git_stream *stream)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++	OSStatus ret;
++
++	ret = SSLClose(st->ctx);
++	if (ret != noErr && ret != errSSLClosedGraceful)
++		return stransport_error(ret);
++
++	return git_stream_close(st->io);
++}
++
++static void stransport_free(git_stream *stream)
++{
++	stransport_stream *st = (stransport_stream *) stream;
++
++	git_stream_free(st->io);
++	CFRelease(st->ctx);
++	if (st->der_data)
++		CFRelease(st->der_data);
++	git__free(st);
++}
++
++int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
++{
++	stransport_stream *st;
++	int error;
++	OSStatus ret;
++
++	assert(out && host);
++
++	st = git__calloc(1, sizeof(stransport_stream));
++	GITERR_CHECK_ALLOC(st);
++
++#ifdef GIT_CURL
++	error = git_curl_stream_new(&st->io, host, port);
++#else
++	error = git_socket_stream_new(&st->io, host, port);
++#endif
++
++	if (error < 0){
++		git__free(st);
++		return error;
++	}
++
++	st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
++	if (!st->ctx) {
++		giterr_set(GITERR_NET, "failed to create SSL context");
++		git__free(st);
++		return -1;
++	}
++
++	if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
++	    (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
++	    (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
++	    (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
++	    (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
++	    (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
++		CFRelease(st->ctx);
++		git__free(st);
++		return stransport_error(ret);
++	}
++
++	st->parent.version = GIT_STREAM_VERSION;
++	st->parent.encrypted = 1;
++	st->parent.proxy_support = git_stream_supports_proxy(st->io);
++	st->parent.connect = stransport_connect;
++	st->parent.certificate = stransport_certificate;
++	st->parent.set_proxy = stransport_set_proxy;
++	st->parent.read = stransport_read;
++	st->parent.write = stransport_write;
++	st->parent.close = stransport_close;
++	st->parent.free = stransport_free;
++
++	*out = (git_stream *) st;
++	return 0;
++}
++
++#endif
+diff --git a/src/streams/stransport.h b/src/streams/stransport.h
+new file mode 100644
+index 0000000..714f902
+--- /dev/null
++++ b/src/streams/stransport.h
+@@ -0,0 +1,14 @@
++/*
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++#ifndef INCLUDE_stransport_stream_h__
++#define INCLUDE_stransport_stream_h__
++
++#include "git2/sys/stream.h"
++
++extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
++
++#endif
 diff --git a/src/tls_stream.c b/src/tls_stream.c
-index 83e2d06..6fb538f 100644
+index 83e2d06..27e5cc2 100644
 --- a/src/tls_stream.c
 +++ b/src/tls_stream.c
-@@ -9,6 +9,7 @@
+@@ -8,8 +8,9 @@
+ #include "git2/errors.h"
  #include "common.h"
  
- #include "openssl_stream.h"
-+#include "mbedtls_stream.h"
- #include "stransport_stream.h"
+-#include "openssl_stream.h"
+-#include "stransport_stream.h"
++#include "streams/mbedtls.h"
++#include "streams/openssl.h"
++#include "streams/stransport.h"
  
  static git_stream_cb tls_ctor;
+ 
 @@ -30,6 +31,8 @@ int git_tls_stream_new(git_stream **out, const char *host, const char *port)
  	return git_stransport_stream_new(out, host, port);
  #elif defined(GIT_OPENSSL)
  	return git_openssl_stream_new(out, host, port);
 +#elif defined(GIT_MBEDTLS)
-+    return git_mbedtls_stream_new(out, host, port);
++	return git_mbedtls_stream_new(out, host, port);
  #else
  	GIT_UNUSED(out);
  	GIT_UNUSED(host);
+diff --git a/src/transports/git.c b/src/transports/git.c
+index 01edfdc..cae10c3 100644
+--- a/src/transports/git.c
++++ b/src/transports/git.c
+@@ -10,7 +10,7 @@
+ #include "netops.h"
+ #include "git2/sys/transport.h"
+ #include "stream.h"
+-#include "socket_stream.h"
++#include "streams/socket.h"
+ 
+ #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
+ 
+diff --git a/src/transports/http.c b/src/transports/http.c
+index cb4a6d0..b602adf 100644
+--- a/src/transports/http.c
++++ b/src/transports/http.c
+@@ -16,8 +16,8 @@
+ #include "auth.h"
+ #include "auth_negotiate.h"
+ #include "tls_stream.h"
+-#include "socket_stream.h"
+-#include "curl_stream.h"
++#include "streams/socket.h"
++#include "streams/curl.h"
+ 
+ git_http_auth_scheme auth_schemes[] = {
+ 	{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
+diff --git a/src/transports/ssh.c b/src/transports/ssh.c
+index 4c55e3f..3cb5655 100644
+--- a/src/transports/ssh.c
++++ b/src/transports/ssh.c
+@@ -15,7 +15,7 @@
+ #include "netops.h"
+ #include "smart.h"
+ #include "cred.h"
+-#include "socket_stream.h"
++#include "streams/socket.h"
+ #include "ssh.h"
+ 
+ #ifdef GIT_SSH
 diff --git a/tests/core/stream.c b/tests/core/stream.c
-index 0cbf442..a4fa2b4 100644
+index 0cbf442..2da4b2f 100644
 --- a/tests/core/stream.c
 +++ b/tests/core/stream.c
-@@ -38,7 +38,7 @@ void test_core_stream__register_tls(void)
+@@ -37,8 +37,7 @@ void test_core_stream__register_tls(void)
+ 	 * or when openssl support is disabled (except on OSX
  	 * with Security framework).
  	 */
- #if defined(GIT_WIN32) || \
+-#if defined(GIT_WIN32) || \
 -	(!defined(GIT_SECURE_TRANSPORT) && !defined(GIT_OPENSSL))
-+	(!defined(GIT_SECURE_TRANSPORT) && !(defined(GIT_OPENSSL) || defined(GIT_MBEDTLS)))
++#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
  	cl_git_fail_with(-1, error);
  #else
  	cl_git_pass(error);
-diff --git a/tests/online/badssl.c b/tests/online/badssl.c
-index 66b090d..bba31cc 100644
---- a/tests/online/badssl.c
-+++ b/tests/online/badssl.c
-@@ -4,7 +4,7 @@
+diff --git a/tests/main.c b/tests/main.c
+index f67c8ff..3dadc5d 100644
+--- a/tests/main.c
++++ b/tests/main.c
+@@ -11,7 +11,11 @@ int main(int argc, char *argv[])
  
- static git_repository *g_repo;
+ 	clar_test_init(argc, argv);
+ 
+-	git_libgit2_init();
++	res = git_libgit2_init();
++	if (res < 0) {
++		return res;
++	}
++
+ 	cl_global_trace_register();
+ 	cl_sandbox_set_search_path_defaults();
  
--#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
-+#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_MBEDTLS)
- static bool g_has_ssl = true;
- #else
- static bool g_has_ssl = false;
diff --git a/deps/patches/libgit2-remote-push-NULL.patch b/deps/patches/libgit2-remote-push-NULL.patch
deleted file mode 100644
index ac84d6d..0000000
--- a/deps/patches/libgit2-remote-push-NULL.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 90cdf44ffb7c78cb9d36709f8a07a216e06bd919 Mon Sep 17 00:00:00 2001
-From: Yichao Yu <yyc1992@gmail.com>
-Date: Sat, 29 Apr 2017 13:00:07 -0400
-Subject: [PATCH] Allow NULL refspec in git_remote_push
-
-Since this is allowed in `git_remote_upload`
----
- src/remote.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/remote.c b/src/remote.c
-index d3132f75c..4cbc45eda 100644
---- a/src/remote.c
-+++ b/src/remote.c
-@@ -2412,7 +2412,7 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
- 		proxy = &opts->proxy_opts;
- 	}
- 
--	assert(remote && refspecs);
-+	assert(remote);
- 
- 	if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
- 		return error;
--- 
-2.12.2
-
diff --git a/test/libgit2.jl b/test/libgit2.jl
index 34db5d2..bd7ff8e 100644
--- a/test/libgit2.jl
+++ b/test/libgit2.jl
@@ -310,7 +310,9 @@ mktempdir() do dir
                 error("unexpected")
             catch e
                 @test typeof(e) == LibGit2.GitError
-                @test startswith(sprint(show,e),"GitError(Code:ENOTFOUND, Class:OS, Failed to resolve path")
+                @test startswith(
+                    lowercase(sprint(show, e)),
+                    lowercase("GitError(Code:ENOTFOUND, Class:OS, failed to resolve path"))
             end
             path = joinpath(dir, "Example.BareTwo")
             repo = LibGit2.init(path, true)
@@ -1653,15 +1655,19 @@ mktempdir() do dir
                 run(pipeline(`openssl req -new -x509 -newkey rsa:2048 -nodes -keyout $key -out $cert -days 1 -subj "/CN=$common_name"`, stderr=DevNull))
                 run(`openssl x509 -in $cert -out $pem -outform PEM`)
 
+                # Find an available port by listening
+                port, server = listenany(49152)
+                close(server)
+
                 # Make a fake Julia package and minimal HTTPS server with our generated
                 # certificate. The minimal server can't actually serve a Git repository.
                 mkdir(joinpath(root, "Example.jl"))
                 pobj = cd(root) do
-                    spawn(`openssl s_server -key $key -cert $cert -WWW`)
+                    spawn(`openssl s_server -key $key -cert $cert -WWW -accept $port`)
                 end
 
                 errfile = joinpath(root, "error")
-                repo_url = "https://$common_name:4433/Example.jl"
+                repo_url = "https://$common_name:$port/Example.jl"
                 repo_dir = joinpath(root, "dest")
                 code = """
                     dest_dir = "$repo_dir"
@@ -1684,6 +1690,7 @@ mktempdir() do dir
                         deserialize(f)
                     end
                     @test err.code == LibGit2.Error.ECERTIFICATE
+                    @test startswith(lowercase(err.msg), lowercase("The SSL certificate is invalid"))
 
                     rm(errfile)
 
@@ -1695,8 +1702,11 @@ mktempdir() do dir
                             deserialize(f)
                         end
                         @test err.code == LibGit2.Error.ERROR
-                        @test err.msg == "Invalid Content-Type: text/plain"
+                        @test lowercase(err.msg) == lowercase("invalid Content-Type: text/plain")
                     end
+
+                    # OpenSSL s_server should still be running
+                    @test process_running(pobj)
                 finally
                     kill(pobj)
                 end
-- 
2.9.4