Modern TLS/SSL on 16-bit Windows

Lately, there’s been an resurgence of latest applications written for retro computer systems—the whole lot
from a Slack consumer to many Wordle clones, to a Mastodon consumer. But most of those applications, in the event that they
connect with the Internet, require a proxy operating on a contemporary laptop to deal with
the SSL/TLS connection, which nearly all APIs these days require. For my Gateway 4DX2-66 operating
Windows 3.11 for Workgroups, making it reliant on a contemporary machine for any sort of actual Internet
use is a tragic state of affairs—so I made a decision to alter the established order.

It wasn’t that Windows 3.1 did not assist safe connections; Internet Explorer 2, for example,
supported SSL. But over time, each purchasers and servers have upgraded to newer variations of the SSL
(now known as TLS) protocol and algorithms, and have dropped assist for older variations as
vulnerabilities like POODLE are discovered with them.

Normally, applications can improve to utilizing a more recent TLS library (e.g. OpenSSL) to get assist for
fashionable TLS, however one of many greatest limitations to this for Windows 3.1 is that it is a 16-bit OS;
TLS libraries right now are likely to assist 32-bit OS’s, and generally 16-bit OS’s for embedded {hardware},
however by no means Windows 3.1 itself.[1]

I used to be impressed by
Yeo Kheng Meng’s notes
as he found the identical challenges for his aforementioned Slack consumer—it appeared attainable
to someway modify a good TLS library and persuade it to compile and work within the Windows 3.1
surroundings, and I wished to present it a shot to present my Gateway, and different Windows 3.1 computer systems
around the globe, the flexibility to hook up with a lot of the Internet once more.

What does success seem like?

To connect with most servers right now, we want to have the ability to converse TLS 1.2.
Older variations of TLS have extensively been deprecated by servers in the previous couple of years.
TLS 1.3 is the latest model of TLS, standardized in 2018, so it will be a bonus to assist it as properly,
however not all servers assist it but.

From Wikipedia:
Transport Layer Security
ProtocolPublishedStatus
SSL 1.0UnpublishedUnpublished
SSL 2.01995Deprecated in 2011 (RFC 6176)
SSL 3.01996Deprecated in 2015 (RFC 7568)
TLS 1.01999Deprecated in 2021 (RFC 8996)
TLS 1.12006Deprecated in 2021 (RFC 8996)
TLS 1.22008In use since 2008
TLS 1.32018In use since 2018

We additionally must assist a set of common
cipher suites,
or the algorithms that the TLS protocol makes use of beneath the hood to really change keys and encrypt information.
Upon connection, the TLS consumer tells the server which cipher suites it helps within the “Client Hello”
message; if the server does not
assist any of them, it is going to reject the reference to a “No common ciphers” error. There are 37 cipher
suites for TLS 1.2, so we have to at the least assist the commonest (learn: much less susceptible) ones for our consumer.

One non-goal of this effort was really making a safe implementation. Given that I’m already
throwing safety out of the window by utilizing 30-year outdated software program on the Internet, I made a decision it was
okay to make some tradeoffs to get a working implementation. The greatest limitations are that I’m
utilizing a pretend random quantity generator, and I’m not verifying certificates.

Finding the appropriate TLS library

Given the success standards above, I regarded into a number of totally different TLS libraries which may work: OpenSSL,
BearSSL, Mbed TLS, and WolfSSL. WolfSSL stood out among the many pack because it had specific 16-bit compiler assist
whereas being fully-featured and well-supported.
It additionally had a variety of assist code for all kinds of {hardware} with totally different constraints—together with
restricted reminiscence—giving me a plethora of examples I may be taught from.

I additionally wanted a working TCP/IP stack on Windows. It’s laborious to consider now, however Windows 3.x did not include
TCP/IP inbuilt. There have been many choices customers may select from, together with Trumpet Winsock, a well-liked
shareware TCP/IP stack. For my testing functions, I made a decision to go together with Microsoft’s personal TCP/IP implementation,
TCP/IP-32, which got here as a separate obtain from Microsoft. This gives a TCP/IP implementation which
you interface by means of Winsock (the Windows Sockets API)—an older model of the identical API you should utilize even
right now on Windows 11, however it ought to work with another implementation.

Development surroundings

My plan was to compile WolfSSL right into a DLL that might then be used from any program. Because WolfSSL is
written in C, I wanted a C surroundings that might create 16-bit Windows DLLs. I made a decision to make use of
Open Watcom v2, which gives phenomenal
assist for 16-bit Windows applications and DLLs, and cross-compilation together with from 64-bit Windows 11.

Unlike with Windle, to make issues a bit simpler for myself, I did the principle improvement
on Windows 11,
with a folder shared to a VirtualBox VM operating Windows 2000 (which might run 16-bit applications, in contrast to Windows 11)
for a lot of the iteration and testing; then
verified at intervals that it nonetheless labored on Windows 3.11 for Workgroups.

DLL Hell

My first problem was determining how one can construct and use DLLs on Windows 3.x. To accomplish that, I wanted to
perceive the distinction between far and close to pointers.

Nowadays we are likely to conceptually consider tips to reminiscence as absolute addresses; nonetheless,
when programming for 16-bit Windows, the
x86 reminiscence segmentation
structure relationship again to the Intel 8088 and 8086 CPUs must be taken into consideration.

Disclaimer: This is my understanding after studying up on this topic. I’ve tried to offer a
easy rationalization on this with out going into an excessive amount of of the main points, e.g. the variations between Real
and Protected modes. Please attain out if something right here is basically incorrect.

With segmentation, reminiscence is cut up in to segments every as much as 64KB in dimension. A section would possibly signify
the code (the directions) for this system, the information (the worldwide and static native variables), or the
stack for a program. Addresses in reminiscence encompass two elements: the section the reminiscence lives in, and the
offset inside that section. A pointer with this full, two-part handle is known as a far pointer.

Segment:xxxx xxxx xxxx xxxx 0000
+Offset:0000 yyyy yyyy yyyy yyyy
=Far pointer:zzzz zzzz zzzz zzzz zzzz
Example of a far pointer[2]

The system has a set of registers, together with the Code Segment (CS), Data Segment (DS), and Stack Segment (SS),
which maintain monitor of the segments which are relevant to the at the moment operating program. When passing a pointer
to a perform inside the identical program, you usually cross simply the offset of the variable you are referencing,
and the section is implied. This sort of pointer is known as a close to pointer, and is used a lot of the
time to avoid wasting on reminiscence.

When calling right into a DLL from a program, a number of issues are totally different. First, the directions for a DLL are
in a special section from this system’s directions, so the CS register should be modified to the DLL’s in
the perform name. The C compiler takes care of this for us if we use the non-standard __far key phrase in
entrance of the perform identify, that means that the compiler will generate a “far call”.

Second, when passing pointers into DLL capabilities, we’ve got to make use of far pointers; DLLs are tough in that
when operating code in them, the Code and Data segments are these of the DLL, however the Stack section is
that of the calling program’s. Using far pointers ensures that the incorrect assumptions aren’t made about
which segments pointers level to.

Another non-standard C key phrase that’s usually used for DLL capabilities is __pascal. This specifies
that this perform makes use of the Pascal
calling conference.
A calling conference specifies how parameters are handed to a perform in reminiscence and the way the stack is
cleaned up after a perform returns. (With pascal, parameters are pushed on the stack left-to-right and the
callee removes them from the stack on return). Windows 3.x adopted the
calling conference utilized by Borland Pascal (and Delphi 1.0),
therefore the identify, however later Windows variations dropped it in favor of stdcall.

This means all of the capabilities in WolfSSL that will likely be known as from an exterior program must be outlined as
__far __pascal. For instance, the perform to initialize WolfSSL is outlined as follows:


int __far __pascal wolfSSL_Init(void);

Everything I’ve mentioned above usually applies to calling the Windows API as properly; in any case, the Windows API
is carried out as calls into the DLLs which are a part of Windows.

Segment too giant

The greatest limitation I bumped into with porting WolfSSL to 16-bit Windows was the 64KB most dimension of
segments—every Code and Data section can solely be a most of 64KB. This is a problem whenever you’re
compiling a TLS library with assist for dozens of cipher suites, and numerous protocols each generally and
hardly ever used.

Thankfully, we’re not restricted to 64KB in whole code or information for our DLL. 16-bit compilers have an idea of
“memory models”, which have been outlined as Small, Medium, Compact, Large, and Huge. All compilers for the platform,
whether or not from Microsoft, Borland, or Open Watcom, assist these fashions as a compile flag; fashions change how the
compiler makes use of far/close to pointers and which libraries are used within the generated directions. I made a decision to make use of
the “Large” reminiscence mannequin, which permits for a number of Code segments and Data segments in your program or DLL.

From Programming Windows 3.1, Third Edition by Charles Petzold
ModelCode SegmentsData Segments
Small11
MediumMultiple1
Compact1Multiple
LargeMultipleMultiple
HugeMultipleMultiple

To save on reminiscence usually, I began by turning off as many compile-time characteristic flags in WolfSSL as
I may with out jeopardizing
its means to hook up with most fashionable TLS servers. This consists of options utilized by insecure cipher suites
(e.g. MD5, DES, RC4) in addition to different options like DTLS and server assist I would not be utilizing for a
easy TLS consumer.

I rapidly discovered that even that wasn’t sufficient, as I’m restricted to 64KB of code in every object file.
Every time an object file hit that restrict, the compilation would fail with “Segment too large”.
In specific, WolfSSL’s inner.c file is a whopping 1.25MB
(too giant
for even GitHub to render),
and would fail the construct each time I enabled a characteristic I wanted.

I used to be in a position to get that file right down to a dimension that might match into two segments by transferring code round
into two information, inner.c and internal2.c—elegant, I do know!
This was probably the most agonizing and time-consuming a part of this entire course of; I used to be in a loop of testing
the code to see if I had sufficient options to hook up with a server, enabling a characteristic, seeing “Segment too large”,
and transferring extra code whereas making an attempt to not break the intricate dependencies between capabilities, or inadvertently
altering the that means of the nested ifdef macros within the code. Eventually, it compiled.

Even with WOLFSSL.DLL compiled, I used to be operating into a wierd “Access Denied” error whereas loading the DLL with the
LoadLibrary Windows API name. Eventually, after a lot agony, I discovered I wanted to show off debugging
info within the generated code, and alter the goal processor from the default (Intel 8086) to 80286 within the
compiler choices.

It works!

Once I obtained the whole lot compiling, I whipped collectively a fast take a look at program to hit Qualys SSL Lab’s
browser functionality tester
and obtain the outcomes to a file.

I may debug what was going on (particularly with lacking options) by utilizing Wireshark to decode the
“Client Hello” message, which is the message {that a} TLS consumer sends to the server upon connection
promoting its capabilities.

In my preliminary builds I used to be lacking the Server Name Indication (SNI) extension, which is usually required
these days to permit servers to host a number of TLS web sites on one IP handle with totally different certificates. With
that added, and after some extra code transferring to internal2.c, I used to be in enterprise.

After downloading the Qualys outcomes on Windows 3.11 for Workgroups and opening the ensuing file in
Internet Explorer 3, voilà:

You can see our construct of WolfSSL helps a wholesome quantity (2) of the best cipher suites, along with a
bunch of much less safe ones—which is greater than sufficient for many websites to speak to us!

Bonus: TLS 1.3

Supporting TLS 1.2 was sufficient for many use circumstances, however WolfSSL has TLS 1.3 inbuilt and it might be
superior if our Windows 3.1 apps may reap the benefits of it too. TLS 1.3 has some important adjustments
within the protocol in comparison with TLS 1.2, and likewise dispenses with the 37 cipher suites in lieu of 5 safe ones.

After enjoying round with the compiler flags, and transferring much more code round to keep away from “segment too large” errors,
I used to be lastly in a position to get my construct of WolfSSL to compile with TLS 1.3 enabled. However, I did run into a difficulty
the place the code thinks the acquired encrypted information is “too large” as a result of it is 1 byte. For now, I’ve commented
the try, and it appears to work in real-world conditions, however I’d love to know why that is occurring.

Here’s a screenshot of the Qualys web page displaying TLS 1.3 working, with the TLS_AES_128_GCM_SHA256 cipher suite:

WinGPT

What’s the usage of a library if you cannot use it? Check out WinGPT, an AI Assistant for
Windows 3.1, which makes use of the WolfSSL port to attach on to the OpenAI API servers.

Download

I’m offering the modified supply code for WolfSSL right here, beneath the GNU General Public License (GPL) v2 license
that WolfSSL is licensed beneath.
As famous above, this code with the modifications I’ve made is not safe,
not dependable, and there’s no guarantee. You ought to positively not use it for
any functions aside from testing to your personal leisure. You can discover the
full GPL v2 license right here.

You may also discover that I’ve not discovered a strategy to combine with the construct instruments (like CMake) that the
remainder of WolfSSL makes use of, or made adjustments in a means that will likely be straightforward to combine again into the principle supply tree.
If somebody desires to make 16-bit Windows a supported structure for WolfSSL, that might be
nice—I have never obtained there but.

WolfSSL for Windows 16-bit + WinGPT 1.0 supply code (14M)

Footnotes

…. to be continued
Read the Original Article
Copyright for syndicated content material belongs to the linked Source : Hacker News – https://www.dialup.net/wingpt/tls.html

Exit mobile version