Open-source project
aws/s2n-tls avatar
aws/s2n-tls

s2n-tls: AWS's C99 TLS Library, and What It Costs to Adopt

An implementation of the TLS/SSL protocols

4,768 stars803 forksCApache-2.0

At a glance

What is it?
s2n-tls is a C99 implementation of TLS/SSL with no internal locks and a deliberately small API. It is a good fit for C services that need TLS 1.3 without OpenSSL's surface area, and a poor fit if you need the MSVC toolchain or a mature Rust-native stack.
Who is it for?
Adopt s2n-tls if you are writing or maintaining a C or C++ service that needs TLS 1.3, full-duplex non-blocking I/O, and an API small enough to audit, and if your target is one of the Tier 1 platforms. Do not adopt it if you need MSVC on Windows, if you are building a Rust service and would rather not cross an FFI boundary, or if you require a TLS stack whose every feature is documented in one place.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem s2n-tls solves, and who actually has that problem

TLS is the part of a networked service that is hardest to get right and least pleasant to own. Most C projects reach for OpenSSL by default, then spend years discovering which of its configuration knobs they never intended to touch. s2n-tls takes the opposite position: it is a C99 implementation of the TLS/SSL protocols, and the README describes it as designed to be simple, small, fast, and with security as a priority.

The audience follows from that. This is a library for people embedding TLS inside a C or C++ process, not for people terminating TLS at a proxy. The README states that the I/O APIs are designed to be intuitive to developers familiar with the widely-used POSIX I/O APIs, and that s2n-tls supports blocking, non-blocking, and full-duplex I/O. It also states there are no locks or mutexes within s2n-tls. That last property matters more than it sounds: a connection handle is not internally synchronized, so a server that multiplexes many connections across an event loop does not pay for locking it never asked for, and does not have to reason about lock ordering inside the TLS layer.

The name is a compression of "signal to noise", which the project's announcement blog post calls a nod to disguising meaningful signals as seemingly random noise. That is branding, not architecture. The architectural claim is the one above: small surface, no hidden synchronization.

How a connection is negotiated: the s2n_connection lifecycle

The mechanism is a handle-and-callback model. You allocate a connection in server or client mode, attach it to a file descriptor, drive the handshake, then read and write through the same handle. The README gives this sequence directly, and it is worth reading as a state machine rather than as boilerplate: s2n_connection_new(S2N_SERVER) returns a handle or NULL, s2n_connection_set_fd associates it with a descriptor, s2n_negotiate performs the handshake, and s2n_send writes application data once the handshake completes.

Every one of those calls that can block takes a pointer to an s2n_blocked_status. That is the whole non-blocking story. When a call returns a negative value, the blocked status tells you whether the operation is waiting on a read, waiting on a write, or has actually failed. In a blocking program you ignore it. In an event loop you use it to decide whether to re-arm the descriptor for reading or writing. Full-duplex support means a connection can be reading and writing concurrently, which is why the status is reported per call rather than stored on the connection.

The protocol and cipher surface is broad but the defaults are conservative. According to the README, s2n-tls implements SSLv3, TLS1.0, TLS1.1, TLS1.2, and TLS1.3, with 128-bit and 256-bit AES in CBC and GCM modes, ChaCha20, 3DES, and RC4 available, and both DHE and ECDHE for forward secrecy. It also states that SSLv3, RC4, 3DES, and DHE are each disabled by default for security reasons. The extensions listed are SNI, ALPN, and OCSP. The design intent behind the preference API is spelled out: because tracking which algorithms and protocols are currently best is difficult, s2n-tls offers a simple API to select the latest default set of preferences, with the option to pin to a specific version for backwards compatibility.

Building s2n-tls on Ubuntu and negotiating a first connection

The README carries a Quickstart for Ubuntu, and it is the shortest path to a working library. It clones the repository, installs cmake and a libcrypto, configures a Release build into ./s2n-tls-install, builds, runs the test suite through ctest, and installs. The libssl-dev package supplies the libcrypto that s2n-tls links against; the build documentation under docs/BUILD.md covers other platforms and other libcrypto choices.

bash
git clone https://github.com/aws/s2n-tls.git
cd s2n-tls
sudo apt update
sudo apt install cmake
sudo apt install libssl-dev
cmake . -Bbuild \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=./s2n-tls-install
cmake --build build -j $(nproc)
CTEST_PARALLEL_LEVEL=$(nproc) ctest --test-dir build
cmake --install build

After the build completes, ctest runs the project's own test suite in parallel; the README sets CTEST_PARALLEL_LEVEL from nproc, so expect the run to use every core. The install step places the library and headers under ./s2n-tls-install rather than a system prefix, which keeps the build self-contained and makes it easy to point a compiler at it without touching the system libcrypto.

A first server-mode connection follows the sequence the README shows. The handle is created in server mode, bound to a descriptor, and negotiated; only after a successful handshake does s2n_send carry application bytes.

c
struct s2n_connection *conn = s2n_connection_new(S2N_SERVER);
if (conn == NULL) {
    /* error */
}
if (s2n_connection_set_fd(conn, fd) < 0) {
    /* error */
}
s2n_blocked_status blocked;
if (s2n_negotiate(conn, &blocked) < 0) {
    /* error */
}
int bytes_written;
bytes_written = s2n_send(conn, "Hello World", sizeof("Hello World"), &blocked);

Two details in that snippet are easy to miss. sizeof("Hello World") includes the terminating null byte, so the example sends one byte more than the visible text; that is an artifact of the README's example, not a requirement of the API. And a negative return from s2n_negotiate does not by itself mean failure, which is why the blocked status is passed in. Callers that treat every negative return as fatal will break under non-blocking I/O. The repository also ships a Rust binding, with documentation on docs.rs, for teams that want the same library behind a Rust API.

Where s2n-tls is the wrong tool

Platform support is the first hard boundary. The README splits support into two tiers. Tier 1 platforms are guaranteed to build, run, and pass tests in CI, and that list is Ubuntu18, Ubuntu22, Ubuntu24, AL2, AL2023, NixOS, OpenBSD 7.4, FreeBSD latest, OSX latest, and Windows through MSYS2 with a MinGW toolchain. Tier 2 platforms are guaranteed to build and issues opened against them will be addressed, but they are not running in CI and are not actively reviewed with every commit; that list includes Fedora Core 34-36, Ubuntu14/16/20, aarch64 Ubuntu18/22/24, and OSX 12-14 on x86_64. The README also warns that these lists are not exhaustive and that missing tooling or a missing supported libcrypto library could prevent a successful build.

The Windows situation deserves its own sentence, because it is the most common surprise. The README states that the MSVC toolchain is not supported, that Windows is supported through MSYS2 with a MinGW toolchain (UCRT64, MINGW64, and CLANG64), and that on Windows AWS-LC is the only supported libcrypto. It also notes that some POSIX-specific features such as kTLS are not available on Windows. A team with a Visual Studio build will not be able to drop this in.

The second boundary is libcrypto. s2n-tls is not a from-scratch cryptographic implementation; it is a TLS protocol implementation that sits on top of a libcrypto. The README's Makefile exports LIBCRYPTO_ROOT, defaulting to a libcrypto-root directory under the source tree, and the repository carries a libcrypto-build directory and a crypto directory. That means the cryptographic primitives, and their security posture, come from whatever libcrypto you link. Choosing s2n-tls does not remove your dependency on a crypto library; it changes which code owns the protocol state machine.

The third boundary is documentation shape. The public API is documented with Doxygen and published on GitHub pages, and the usage guide explains how different TLS features can be configured. That is a reasonable split, but it means that for anything not covered by the usage guide, the answer lives in generated header documentation rather than in prose. Teams that need a single narrative document covering every feature will find the material thinner than they expect. The README does not document rollback or downgrade procedures for a deployed library, so treat version pinning as something you design yourself.

s2n-tls vs OpenSSL and vs rustls: three different bets

The comparison people actually search for is s2n-tls vs OpenSSL, and the difference is not performance, it is scope. OpenSSL is a toolkit: TLS, a general-purpose cryptography library, command-line tools, and decades of accumulated configuration surface. s2n-tls is a TLS protocol implementation with a deliberately narrow API, and it delegates cryptography to a libcrypto that may itself be OpenSSL or AWS-LC. If your application also needs to hash passwords, generate keys, or sign artifacts outside a TLS session, s2n-tls will not replace OpenSSL for you; you will still link a crypto library, and you may end up with both.

The comparison with rustls is a language-boundary question. rustls is a TLS implementation written in Rust, so a Rust service using it stays entirely inside the Rust toolchain and its memory-safety guarantees. s2n-tls is C99, and the repository exposes Rust bindings documented on docs.rs, so a Rust service can use it, but through an FFI boundary. The relevant searches here (s2n tls rust, s2n tls tokio, s2n tls hyper) point at that seam: using s2n-tls from an async Rust runtime means the s2n_blocked_status model has to be adapted to whatever readiness model the runtime uses. That is a real integration cost, not a hypothetical one, and it is the reason a Rust project would usually pick rustls unless it specifically needs s2n-tls behavior.

There is also a family question worth separating. The RELATED SEARCHES list includes S2n-quic and S2n-bignum. Those are different projects in the same naming family, not components of this one, and the s2n-tls README does not describe them. If you arrived looking for QUIC or for formally verified bignum arithmetic, this repository is not the answer.

Maintenance, releases, and the Apache-2.0 licence

The repository is not archived, and the last push was on 2026-09-22. Releases are frequent and small: v1.7.10 on 2026-09-14, v1.7.9 on 2026-09-03, and v1.7.8 on 2026-08-21. A cadence measured in weeks, not quarters, is the practical cost model here. If you vendor s2n-tls, you are choosing a dependency that moves, and the versioning policy lives in VERSIONING.rst rather than in the README, so read that file before deciding how tightly to pin.

Upgrade cost is mostly a build-and-test question. The README's Ubuntu quickstart runs ctest as part of the build, which means the project expects you to be able to run its test suite against your toolchain; that is the cheapest way to find out whether a new release breaks your platform. Because s2n-tls links a libcrypto you supply, an upgrade can also mean a libcrypto upgrade, and the two are not versioned together. The README does not document a compatibility matrix between s2n-tls releases and libcrypto versions.

Licensing is Apache-2.0, stated in the README and present as a LICENSE file at the repository root alongside a NOTICE file. Apache-2.0 is a permissive licence with an explicit patent grant and a NOTICE-file convention, which matters if you redistribute. The repository also carries a SECURITY.md with a security reporting policy and threat model. That is not legal advice, and if you are redistributing s2n-tls inside a product, the NOTICE file and any bundled libcrypto licences are the things to hand to whoever reviews your dependencies.

Editorial conclusion

Adopt s2n-tls if you are writing or maintaining a C or C++ service that needs TLS 1.3, full-duplex non-blocking I/O, and an API small enough to audit, and if your target is one of the Tier 1 platforms. Do not adopt it if you need MSVC on Windows, if you are building a Rust service and would rather not cross an FFI boundary, or if you require a TLS stack whose every feature is documented in one place. Verify three things before committing: that your platform and libcrypto combination appears in the build documentation, that the TLS features you rely on are covered in the usage guide rather than only in the Doxygen headers, and that you can live with the release cadence, which shipped v1.7.10 on 2026-09-14.

Frequently asked questions

What does s2n mean in s2n-tls?

s2n-tls is short for "signal to noise", which the project's announcement post describes as a nod to the act of encryption disguising meaningful signals as seemingly random noise.

What is s2n-tls?

It is a C99 implementation of the TLS/SSL protocols, released under the Apache License 2.0 and designed to be simple, small, fast, and with security as a priority.

What does TLS stand for?

TLS stands for Transport Layer Security. s2n-tls implements the TLS/SSL protocols, covering SSLv3, TLS1.0, TLS1.1, TLS1.2, and TLS1.3.

Official sources

  1. aws/s2n-tls on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aws-s2n-tls.svg)](https://hysenlabs.com/projects/aws-s2n-tls)