Downloading files over TLS

Jacob Carlborg doob at me.com
Fri Jun 26 10:12:09 UTC 2020


Downloading files over TLS. This seems that it's something that 
should be quite simple to do. My high level goals are 
cross-platform and easy distribution. I don't need anything fancy 
just a simple API like this:

download("https://url.com", "/local/file");

Because of these goals, I have a few requirements, which I don't 
think are unreasonable:

* Cross-platform (macOS, Linux, Windows, possible FreeBSD)

* Minimal runtime requirements (I don't mind a few compile time 
requirements). I also don't mind runtime requirements that are 
guaranteed to exist

* Ideally using the platform provided TLS implementation, or at 
least platform provided certificates (I've been bitten several 
times due to old certificates making request fail)

* On Linux I would really like to have a fully statically linked 
binary, this will make distribution easier

* Due to the previous requirement, I think using musl will be 
required. For most parts glibc works fine for static linking, but 
I think in the case of networking it does not work. The DNS 
lookup (or something like that) doesn't work with static linking

I think the main problem of all this is picking the right TLS 
implementation.

The obvious candidate is std.net.curl, but I'm going to explain 
why that is not ideal, at least not how it's currently 
implemented. std.net.curl relies on OpenSSL, which is deprecated 
on several platforms. On several platforms it's not the main, 
platform provided TLS implementation. As far as I know, it also 
provides its own certificates and doesn't rely on the ones 
provided by the platform.

I going to break down some alternatives per platform.

macOS:
* std.net.curl. Depends on libcurl and OpenSSL. OpenSSL has been 
deprecated on macOS for many years. In one wants to use OpenSSL 
anyway, one need to ship it with the binary (statically link it). 
For other problems, see above

* requests [1]. This is a potential better alternative than 
std.net.curl since it doesn't rely on libcurl. But, it still 
relies on OpenSSL, so it has the same problems as std.net.curl

* vibe.d [2]. Does not depend on libcurl, but does depend on 
OpenSSL. But, it also supports Botan, which is promising. I'll 
discuss Botan separately

* Hunt [3]. Same problem as the other ones, relies on OpenSSL

* Arsd [4]. Relies on OpenSSL

* SecureTransport [5]. This is an Apple specific library provided 
by the platform. It seems to more or less follow the same idea as 
OpenSSL, it's independent on the transport layer and works with 
BSD sockets. Initially this looks perfect, it's provided by the 
platform and uses certificates provided by the platform. The main 
problem is that it has been deprecated. It also only supports TLS 
up to version 1.2 and since it's deprecated, it's likely that it 
won't support any newer versions

* Network [6]. This is an Apple specific library provided by the 
platform. This is the recommend alternative to SecureTransport. 
The problem is that this is not just an alternative TLS 
implementation, it's a completely different alternative to BSD 
sockets. The API is completely different and will require some 
extra layers to to provide a cross-platform API. This means that 
I cannot use any of the existing library to just add TLS, it will 
be a completely different implementation, which might be ok. 
Another big problem is that it only available on macOS 10.14 and 
later. I have not decided yet if this is acceptable or not

* NSURLSession [7]. This is an  Apple specific Objective-C class, 
provided by the platform. It's higher level than the Network 
framework and is available on all versions of macOS. This will 
again require a some extra layers to hide the platform specific 
API. There's also a problem with LDC which has very limited 
support for Objective-C interoperability.

Linux:
As far as I know, OpenSSL (or some version of it, like LibreSSL) 
is what's provided by the platform. So I guess that has to be 
acceptable on Linux.

* std.net.curl. The main problem with std.net.curl on Linux is 
that it uses `dlopen` to load the TLS library. `dlopen` doesn't 
work for a statically linked executable. Side note, `dlopen` 
doesn't work on iOS (for other reasons) so it would be nice if 
this could be fixed

* requests. Seems to do that same as std.net.curl, uses `dlopen`

* For the other alternatives (mentioned in the macOS section), I 
haven't investigated if they use `dlopen` or not

Windows:
I don't know that much of this platform.

* std.net.curl and basically all other options already mentioned 
relies on OpenSSL, which is not provided by the platform

* SChannel. As far as I know, this the the platform provided 
implementation of TLS on Windows.

* Are there any high level APIs, like NSURLSession, on Windows 
that can be used to download files?

Botan:

This seems like a good candidate. It even exists a full D port of 
it [8].

* This is not used by any specific platform but it can use the 
platform provided certificates, which is a plus.

* The downside is that the code to access the platform provided 
certificates has not been ported to D (or it was added to a newer 
version of the C++ implementation).

* I think the the D port of Botan does not verify certificates, 
which is not great. It might be due to the previous point

* I also don't know if the D port is updated to match the latest 
version of the C++ implementation. By experience I know this can 
be a huge task, so I don't expect it

* Botan only supports up to TLS 1.2 (even the C++ 
implementation). Due to the previous point it's unclear if the D 
port of Botan will get support for TLS 1.3

I know there are other alternative TLS implementation, but I have 
not looked into detail on these.

A question regarding libcurl. I know that curl (the CLI tool) can 
take advantage of SecureTransport and SChannel as alternatives to 
OpenSSL. Due to std.net.curl containing some OpenSSL related 
code, is TLS handled completely outside of libcurl? So 
std.net.curl  can't easily take advantage of SecureTransport and 
SChannel the same way as curl does?

Is there anything obvious I'm missing or why does this seem so 
difficult? Do I have too high expectations and requirements?

[1] https://code.dlang.org/packages/requests
[2] https://code.dlang.org/packages/vibe-d
[3] https://code.dlang.org/packages/hunt
[4] https://code.dlang.org/packages/arsd-official%3Ahttp
[5] 
https://developer.apple.com/documentation/security/secure_transport
[6] https://developer.apple.com/documentation/network
[7] 
https://developer.apple.com/documentation/foundation/nsurlsession
[8] https://code.dlang.org/packages/botan

--
/Jacob Carlborg


More information about the Digitalmars-d-learn mailing list