
Zig implementation of TLS 1.3 for QUIC, RFC 8446/9001 compliant with zero external dependencies, featuring AEAD protection, X.509 validation, and 1000+ tests.
A TLS 1.3 implementation for QUIC written in Zig, strictly adhering to RFC 8446 (TLS 1.3) and RFC 9001 (QUIC-TLS).
| Aspect | Info |
|---|---|
| API Stability | Production |
| Zig Version | 0.16.0 |
| Platforms | Linux, macOS, Windows |
The <2 ms handshake target is validated on Ed25519 and RSA-2048 certificate configurations. secp256r1 (P-256) workloads currently run 2–3 ms per handshake due to an upstream Zig stdlib limitation (PERF-229). See ADR-0002 for the full decision record.
✅ RFC Compliant - Implements TLS 1.3 (RFC 8446) and QUIC-TLS (RFC 9001) ✅ Zero Dependencies - Uses only Zig standard library ✅ Interop Tested - Conformance tests against BoringSSL and OpenSSL ✅ QUIC Native - Designed for QUIC integration, no traditional record layer ✅ Well Tested - 551 unit tests + RFC test vectors + 449 conformance tests
# Build and test everything
make
# Or manually
zig build
zig build test
The project uses both a Makefile and build.zig for flexibility:
# Build and run all tests (default)
make
# Build only
make build
# Run specific test suites
make test-unit # Unit tests
make test-vectors # RFC 8446/9001 test vectors
make test-conformance # BoringSSL/OpenSSL conformance (requires libssl-dev)
# Development workflow
make dev # Format + check + unit tests
make clean # Clean build artifacts
make help # Show all available targets
# Build the library
zig build
# Run all tests
zig build test
# Run specific test suites
zig build test-vectors
zig build test-conformance # Requires OpenSSL dev libraries
# With optimizations
zig build -Doptimize=ReleaseFast
Build Summary: All steps succeeded; 1000 tests passed ✅
Comprehensive Test Coverage:
Total: 1000 tests ensuring RFC compliance, cryptographic parity, and production readiness
src/
├── lib.zig # Main library entry point
├── tls13/ # TLS 1.3 handshake state machine (modularized)
├── tls13_full.zig # Full TLS 1.3 message parsing
├── key_schedule.zig # HKDF-based key derivation
├── record.zig # Record layer & AEAD operations
├── x509.zig # Certificate parsing & validation
├── sig.zig # Signature verification
├── asn1.zig # ASN.1 DER decoder
├── alert.zig # TLS alert protocol
├── entropy.zig # Fail-closed RNG helper (SEC-074)
├── zero_rtt.zig # 0-RTT support
└── quic_*.zig # QUIC integration interfaces
tests/
├── rfc8446_vectors.zig # RFC test vectors
├── boringssl_conformance.zig # BoringSSL parity tests
└── openssl_conformance.zig # OpenSSL conformance tests
const std = @import("std");
const tls = @import("ztls");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
// Configure TLS context
const config = tls.Config{
.allocator = gpa.allocator(),
.role = .client,
.supported_cipher_suites = &.{.TLS_AES_128_GCM_SHA256},
.supported_groups = &.{.x25519},
.alpn_protocols = &.{"h3"},
};
// Set up callbacks
const callbacks = tls.Callbacks{
.on_secret_available = mySecretCallback,
.on_handshake_complete = myHandshakeCallback,
};
// Initialize TLS context
var ctx = try tls.Context.init(config, callbacks);
defer ctx.deinit();
// Process incoming CRYPTO frames from QUIC
try ctx.processCrypto(crypto_data);
}
std.crypto.kdf.hkdfThe project includes comprehensive conformance tests to ensure compatibility with industry-standard TLS implementations:
To run conformance tests:
# Install OpenSSL development libraries first
make install-deps # Auto-detects your OS
# Run conformance tests
make test-conformance
ztls ships with an opt-in OpenTelemetry seam. When the -Dwith_otel=true
build flag is set, the library emits exactly one span per TLS handshake
(span name: "TLS handshake") with the following semantic-convention
attributes attached on completion:
tls.role -- "client" or "server"tls.protocol.version -- "1.3" (always for ztls)tls.cipher_suite -- the negotiated cipher (e.g.
TLS_AES_128_GCM_SHA256)network.protocol.name -- "tls"# Default build: no otel symbols, zero overhead.
zig build test
# Enabled build: one span per handshake, exported via the OTLP
# defaults (http://localhost:4318) or whatever OTEL_* env vars
# specify.
zig build test -Dwith_otel=true
The seam is gated behind a lazy dependency, so the otel package is
only fetched when the flag is on. Application entry-point code that
wants to install a TracerProvider should call
ztls.observability_init.Otel.init then installGlobals(); consult
the upstream otel integration recipe (otel/docs/integration/RECIPE.md)
for the full host-application bootstrap.
# Quick development iteration
make dev
# Format code
make fmt
# or
zig fmt src/ tests/
# Check syntax
make check
# Clean build
make clean build
When adding features:
Apache-2.0 License - see LICENSE for details.
Built with Zig 0.16.0 | TLS 1.3 | QUIC-Native | RFC-Compliant