A security check that rejects legitimate traffic is still a bug, even when the bug is on the paranoid side.
Today we released OxiTLS 0.3.0 — a correctness release for the COOLJAPAN Pure Rust TLS transport stack. It fixes an OCSP staple validation bug that could turn a legitimate, CA-signed certificate into an unconditional handshake failure, converts an RFC 9180 HPKE helper from panicking to returning Result, and hardens the workspace’s own attacker-facing parsers with three new fuzz targets.
No OpenSSL. No native-tls. No ring on the default path. No FFI, no -sys crates on the path you actually ship. OxiTLS compiles to a single static binary with no system libraries and no build-time C toolchain — Pure Rust from the byte that hits the socket to the AEAD that protects it.
Why OxiTLS 0.3.0 is a game changer
Correctness releases are where a TLS stack either earns trust or loses it — a false rejection is embarrassing, but a false acceptance is dangerous, and the fix has to land on the right side of that line every time:
OcspClientVerifierresolved the OCSP signer key by synthesising the end-entity certificate as its own issuer whenever the peer sent no intermediates. A CA-signed leaf presented with an empty intermediates list — a completely ordinary, spec-compliant server configuration — had its legitimate stapled OCSP response resolved against the leaf’s own SPKI instead of its real issuer’s, turning a valid staple into an unconditionalBadSignaturehandshake failure that evenSoftFailpolicy could not escape.labeled_expand, the RFC 9180 HPKELabeledExpandprimitive underneath everyDhKemencap/decapcall, panicked on a too-short PRK or an oversized output length instead of returning an error — a caller-controlled input shape could bring down the process rather than fail the handshake gracefully.- The workspace’s own hand-rolled, attacker-facing wire-format parsers — SCT lists, precert TBSCertificate reconstruction, OCSP staples — had fuzz coverage on some paths but not all of them.
OxiTLS 0.3.0 ends all of that.
- The leaf-only-chain OCSP bug is fixed at the root cause. The end-entity certificate is now only treated as its own issuer when it is structurally self-signed (
subject == issuer); otherwise a missing issuer correctly reports the staple as unverifiable (Unparseable), which policy governs from there instead of a hard-coded signature-forgery rejection. Six new tests pin the fix, including the exact case that broke: a CA-signed leaf with no intermediates under bothSoftFailandHardRequirepolicy. labeled_expandcan no longer panic. It now returnsResult, and every internal caller —key_schedule_base,kem::extract_and_expand, and all fourDhKem::encap/decapimplementations — propagates the error instead of aborting the process.- Three new fuzz targets close the parser coverage gap, alongside the existing
client_config_parser:sct_list_parser(RFC 6962 §3.3 SCT list wire format),precert_hash_parser(RFC 6962 §3.2 precert TBSCertificate reconstruction), andocsp_staple_parser— which fuzzes staple bytes end-to-end through the publicOcspClientVerifiertrait entry point against a fixed valid certificate pair. deny.tomlnow documents every intentional advisory and FFI edge with a written justification —cargo deny check advisoriesandcargo deny check bansboth pass clean, withwrappersscoping onring/aws-lc-rs/aws-lc-sys/security-frameworklimited to the specific dev-only, bench-only, or quarantine-crate edges that need them.sha2andrand_coreare each a singleworkspace.dependenciesentry instead of six and two independently-pinned copies respectively, tightening the dependency graph without touching the deliberately-separate pins inside the vendoredoxitls-rustcrypto-providerfork.- 364 tests passing with default features (451 with
--all-features) across 11 workspace crates (28,254 SLOC,tokei), zero clippy / compiler / rustdoc warnings.
Technical Deep Dive: where the fix lives, and what’s new around it
- The crypto engine (
oxitls-core,oxitls-rustcrypto-provider,oxitls-adapter-rustls-rustcrypto). The HPKELabeledExpandfix lives inoxitls-adapter-rustls-rustcrypto/src/hpke/kdf.rs; the OCSP and SCT-adjacent verifier hardening lives inoxitls-adapter-rustls-rustcrypto/src/verifier/ocsp_client.rs, specifically the newend_entity_is_self_signedcheck that replaces the old blanket assumption. - New fuzz targets (
oxitls-adapter-rustls-rustcrypto/fuzz).sct_list_parser,precert_hash_parser, andocsp_staple_parserjoinclient_config_parser, all targeting the crate’s own parsers rather than the upstreamrustls-pemfilesurface. - Four new runnable examples (
oxitls/examples).tls13_client.rs,tls13_server.rs,mtls.rs, andverifier_composition.rsship in this release — the last one builds the exact layered verifier pipeline (WebPkiServerVerifier→CertPinVerifier→SctVerifier→OcspClientVerifier) that would have surfaced the leaf-only-chain bug this release fixes. - Workspace hygiene. New root-level
rustfmt.tomlandclippy.tomlpin the workspace MSRV (1.89) consistently; stale[compile-blocked]annotations on three already-passing RFC 7250 raw-public-key integration tests were removed fromoxitls-adapter-rustls-rustcrypto/TODO.md.
Getting Started
cargo add oxitls
The client and server API is unchanged in 0.3.0 — the fixes in this release are internal correctness hardening, not surface changes:
use oxitls::{ClientBuilder, TlsError};
#[tokio::main]
async fn main() -> Result<(), TlsError> {
let stream = ClientBuilder::new()
.server_name("example.com")
.connect("example.com:443")
.await?;
Ok(())
}
Want to see the exact verifier composition that surfaced the leaf-only-chain OCSP bug, and confirm it now resolves correctly? Run the new example directly:
cargo run --example verifier_composition -p oxitls
It layers CertPinVerifier → SctVerifier → OcspClientVerifier over WebPkiServerVerifier, connects to a local Pure-Rust TLS 1.3 server with no intermediates in its chain, and exercises the exact code path this release fixed.
What’s New in 0.3.0
- Fixed: OCSP staple false-rejection on leaf-only chains —
OcspClientVerifierno longer treats a CA-signed end-entity as its own issuer when no intermediates are present;labeled_expand(RFC 9180 HPKE) no longer panics on a too-short PRK or oversized output length, returningResultinstead. - Added: three new fuzz targets (
sct_list_parser,precert_hash_parser,ocsp_staple_parser); four new runnable examples (mtls.rs,tls13_client.rs,tls13_server.rs,verifier_composition.rs); root-levelrustfmt.tomlandclippy.toml. - Changed:
deny.tomlnow documents every RUSTSEC advisory exception and FFI-cratewrappersscope with a written justification;oxitls-native-certs’s platform-specific dependencies (security-framework,schannel) are now gated per-target instead of unconditional;sha2andrand_coreconsolidated to singleworkspace.dependenciesentries;post-quantumfeature docs corrected to reflect that X25519MLKEM768 is fully wired (not a namespace reservation);oxiarc-deflate0.3.6 → 0.4.1;p256/p3840.14.0-rc.15 → 0.14.0 (stable).
Tips
- If you compose custom
ServerCertVerifierstacks withOcspClientVerifier, re-test against leaf-only chains. Before 0.3.0, a CA-signed leaf presented with zero intermediates and a valid staple could hard-fail even underSoftFailpolicy — that specific shape now correctly falls through to a policy-governedUnparseableoutcome instead. - If you call
oxitls_adapter_rustls_rustcrypto’s HPKE KEM functions directly, check your error handling.labeled_expandand everything built on it (key_schedule_base,extract_and_expand, all fourDhKem::encap/decapimpls) now returnsResultwhere it used to be able to panic — a caller relying on the old panic-as-crash behavior needs to handle the new error path. - Run
cargo deny check advisoriesandcargo deny check bansafter upgrading. Both now pass clean against this workspace’s owndeny.toml, with every RUSTSEC exception and FFI wrapper edge documented inline — useful as a template if you maintain your owndeny.tomldownstream. - The four new examples double as living documentation.
verifier_composition.rsin particular is the clearest way to see howCertPinVerifier,SctVerifier, andOcspClientVerifierdecorateWebPkiServerVerifierand delegate trust-chain validation onward. - Two
p256/p384version lines are still expected in yourCargo.lock.oxitls-rustcrypto-providertracks a proven 0.13.x fork line independently of the rest of the workspace’s 0.14.0 (now stable, no longer-rc); Cargo resolves both without conflict.
This is the foundation
OxiTLS belongs to NoFFI — the COOLJAPAN initiative to replace every C / C++ / Fortran / -sys FFI dependency in the Rust ecosystem with a clean, memory-safe, 100% Pure Rust implementation. A correctness release that closes a false-rejection on a completely ordinary certificate shape is exactly the kind of hardening that keeps that promise credible.
OxiTLS sits underneath a growing list of sibling COOLJAPAN projects — all bumped to oxitls = "0.3.0" (or one of its subcrates) as part of this release: OxiHTTP, OxiQUIC, and OxiRPC carry it as their transport-security layer; OxiSQL depends on it directly for its Postgres and MySQL adapters’ CryptoProvider; OxiGDAL pulls in oxitls-core, oxitls-adapter-rustls-rustcrypto, and oxitls-webpki-roots individually; and OxiRS, CeleRS, MielinOS, and VoiRS round out the list.
Repository: https://github.com/cool-japan/oxitls
Star the repo if you want a TLS stack where a false-rejection bug on an ordinary certificate chain gets fixed at the root cause, not patched around at the call site.
The era of a security check that’s paranoid at the cost of correctness is over. Pure Rust TLS — sovereign, safe, and precise about what it rejects — is here.
— KitaSan at COOLJAPAN OÜ August 6, 2026