Open-source project
cloudflare/boring avatar
cloudflare/boring

boring: safe Rust bindings to BoringSSL with tokio and hyper adapters

BoringSSL bindings for the Rust programming language.

527 stars180 forksRustLicense varies

At a glance

What is it?
boring is Cloudflare's Rust binding to BoringSSL, Google's fork of OpenSSL used by Chrome, Chromium and Android, packaged as four crates covering the safe API, the raw FFI layer, the tokio TLS adapters and the hyper HTTPS connector. The workspace shares one version across all four, supports FIPS builds and post-quantum key exchange, and is a v5 rewrite with an explicit upgrade path from v4.
Who is it for?
Adopt boring if you run Rust services that need TLS and want BoringSSL's behaviour, its FIPS module or its post-quantum key exchange rather than the platform OpenSSL. Skip it if you are on version 4 and cannot yet move, since the upgrade crosses a major boundary and changes several APIs.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

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

Editorial analysis

What BoringSSL is and why the bindings exist

BoringSSL is Google's fork of OpenSSL for Chrome, Chromium and Android, and the boring crate exists to make that library usable from Rust. The README describes the crate as providing safe bindings for the Rust programming language and TLS adapters for tokio and hyper built on top of it. The word safe is doing the work there, because the alternative to a binding is a hand-written FFI layer, and a hand-written FFI layer in a cryptography library is the kind of thing where a mistake becomes a memory safety problem rather than a logic bug. Cloudflare is the publisher, and the project is based on a fork of rust-openssl, which is acknowledged under an Accolades section. That lineage explains the shape of the API, since the safe layer inherits the shape of an existing Rust OpenSSL binding and then diverges where BoringSSL differs. The repository listing records the licence as unknown, but the contribution section states that unless you explicitly say otherwise, any contribution is dual licensed under both the Apache License 2.0 and the MIT licence without additional terms, which is the standard dual-licence commitment that lets the crates be consumed under either. The last push to the repository was on 2026-10-19 and the release history is regular, with v5.3.0 from 2026-10-14, 5.2.0 from 2026-08-06 and 5.1.0 from 2026-04-18.

Four crates, one version

The workspace manifest lists the members and the resolver version.

toml
[workspace]
members = [
    "boring",
    "boring-sys",
    "tokio-boring",
    "hyper-boring"
]
resolver = "2"

Each crate has its own documentation on docs.rs, and the README links them separately: the Boring API, the tokio TLS adapters, the hyper HTTPS connector and the FFI bindings. The split is the practical way to think about the project. The safe API crate is what an ordinary user depends on. The FFI bindings crate is the raw layer that the safe crate is built on, and it is what you need if you are reaching for something the safe API does not expose. The tokio crate provides the asynchronous TLS adapters, and the hyper crate provides an HTTPS connector built on those adapters. The hyper crate is not in the workspace dependencies list, which makes sense since it depends on the others and is published separately. The workspace package metadata sets the version to 5.3.0, the Rust version to 1.85, the repository to the GitHub URL and the edition to 2021, and the release metadata marks the workspace as a shared-version workspace with an empty tag prefix and publishing disabled at that level.

The dependency list and the build story

The workspace dependencies show how the library is built and what it pulls in.

toml
[workspace.dependencies]
boring-sys = { version = "5.3.0", path = "./boring-sys", default-features = false }
boring = { version = "5.3.0", path = "./boring", default-features = false }
tokio-boring = { version = "5.3.0", path = "./tokio-boring", default-features = false }

bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] }
bitflags = "2.9"
brotli = "8.0"
bytes = "1"
cmake = "0.1.54"
fs_extra = "1.3.0"
fslock = "0.2"
foreign-types = "0.5"
libc = "0.2"
hex = "0.4"
rusty-hook = "^0.11"
futures = "0.3"
tokio = "1"
anyhow = "1"
antidote = "1.0.0"
http = "1"
http-body-util = "0.1.2"
hyper = "1"
hyper-util = "0.1.6"
linked_hash_set = "0.1"
openssl-macros = "0.1.1"
tower = "0.4"
tower-layer = "0.3"
tower-service = "0.3"

Two entries explain most of the build. The cmake crate is there because BoringSSL is built with CMake, so the build script compiles the C library from source rather than linking a system copy. The bindgen crate generates the Rust FFI declarations from the BoringSSL headers at build time, which is why the FFI crate has to track the library closely rather than shipping a hand-maintained header copy. The rusty-hook dependency is a build hook runner, and there is a .rusty-hook.toml at the top of the repository to configure it. The rest of the list is a familiar Rust async stack: tokio and futures for the runtime, http and http-body-util for the HTTP types, hyper and hyper-util for the client, tower with its layer and service traits for the middleware abstraction, anyhow for error handling and antidote for cancellation-safe channels. The manifest also enables default-features = false for the internal crates, which is what allows a downstream user to choose which optional pieces they pull in.

FIPS and post-quantum support

The README lists three capabilities beyond the basic bindings. The crate supports FIPS-compatible builds of BoringSSL, which matters for anyone who needs to run inside a certified cryptographic module and cannot use a stock build. It supports post-quantum crypto, referring to the TLS ECDHE with ML-KEM draft, which is the key exchange that a client and server would use to establish a shared secret using the post-quantum algorithm. And it supports Raw Public Key extensions, pointing at the peer_pubkey method on the SSL reference type, which is the mechanism by which a peer presents a raw public key instead of a certificate chain. That last one is a departure from the usual TLS identity model and it is worth understanding before relying on it, since the usual certificate-based flow and the raw public key flow are different trust arrangements. The upgrade notes make the feature story clearer still. The pq-experimental Cargo feature is no longer needed because post-quantum crypto is enabled by default, and the fips-precompiled feature has been merged into fips, with the BORING_BSSL_FIPS_PATH environment variable used to point at a precompiled library. The fips-compat feature was renamed to legacy-compat-deprecated. Enabling post-quantum by default is a notable decision, since it means the default handshake behaviour differs from a stock TLS stack.

Upgrading from version 4

The README devotes a section to moving from boring v4, and it is a list of concrete API changes rather than a general statement. The instruction is to update first to boring 4.22 and make sure it builds without deprecation warnings, which gives you a baseline version that already carries the deprecations. The feature changes are the ones described above, with the experimental post-quantum flag disappearing and fips-precompiled folding into fips. The type changes are where the work is. SslCurve and SslCurveNid have been removed, on the grounds that curve names are more stable and portable identifiers, so you use curve_name() and set_curves_list() instead. Ssl::new_from_ref becomes Ssl::new(). X509Builder::append_extension2 becomes X509Builder::append_extension. X509Store is now cheaply cloneable but immutable, which means that SslContextBuilder.cert_store_mut() cannot be used after set_cert_store(), and if you need cert_store_mut() you should either not overwrite the default store or use set_cert_store_builder(). X509StoreBuilder::add_cert now takes a reference. And support for hyper 0.x has been removed, so hyper 1.x is required. The dependency versions reflect that, with http 1, http-body-util 0.1.2, hyper 1 and hyper-util 0.1.6.

Supporting both major versions at once

The README gives a concrete answer for the case where a codebase cannot move to v5 immediately, which is a situation any library with a breaking major release runs into. To support both v4 and v5, add a version requirement of boring = ">=4.22,<6" to your Cargo.toml. If you need to auto-detect which version was selected, add boring-sys = ">=4.22,<6" as a direct dependency and check in your build.rs whether the DEP_BORINGSSL_VERSION_MAJOR environment variable is set to 5, with the note that versions older than 5.2.0 do not set it. The mechanism is the standard Cargo build script environment variable convention, where the build script of a dependency publishes metadata to the dependent's build script under a DEP_ prefix derived from the package name. The version 4.22 floor is not arbitrary, since it is the release where the deprecations appear, so a range starting below it would reintroduce the old names. The four-crate structure helps here too, since depending on boring-sys directly for the version probe is exactly the kind of use that the low-level crate exists for.

The release process, in unusual detail

The release section is the longest part of the README and it documents the publication procedure in terms that are unusual for a project README, so it is worth reading closely. A release pull request contains exactly one commit that changes the workspace version, with all four published crates resolving to that version. The PR is merged into master using the repository's rebase-and-merge strategy, and its post-rebase merge commit must remain reachable from master when the release is started. The release workflow is then run from master through the Publish to crates.io action, supplying the merged PR number and the expected version without the v prefix, and it verifies the PR, the merge commit and the crate versions before creating the immutable tag and publishing boring-sys. Once that first crate succeeds, it creates or reuses a non-latest GitHub Release and publishes boring, tokio-boring and hyper-boring in dependency order, updating the GitHub Release after each crate succeeds with a link to its crates.io version. The annotated v prefix tag is created before publication as the release's immutable anchor, and a partially completed release remains a non-latest GitHub Release and can be rerun with the same inputs, with already published crates remaining linked in the Release body. After all crates publish successfully the GitHub Release becomes the latest release. Pushing a tag alone does not publish a release. The crates-io environment policy permits master and retains its required reviewers.

Why the release checks are this strict

The last two paragraphs of the release section explain the reasoning behind the checks, and they are the part that tells you what the maintainers are worried about. On a rerun, an existing non-yanked crate version is accepted only when its crates.io checksum matches the archive generated from the release commit, and a checksum mismatch stops the release rather than associating an artifact from another source with this release. That is a supply-chain property stated in plain terms, and the alternative behaviour it rules out, silently attaching a differently built artefact to a release that people are about to pin, is exactly the failure a shared-version workspace of FFI bindings can least afford. The related rule is that versions with a prerelease component, such as 5.4.0-rc.1, are marked as GitHub prereleases and never become the repository's Latest release, so a release candidate cannot be picked up by a tool that follows the latest tag. The ordering requirement, that boring-sys publishes first and the rest follow in dependency order, exists because the other crates depend on it and a partially published set with the dependency missing is not a usable state. The repository top level matches this emphasis, with a RELEASE_NOTES file, a scripts/ directory, a THIRD_PARTY file listing third party code, a cliff.toml for generating changelogs from commit messages, a .gitmodules file for the BoringSSL submodule and a .rusty-hook.toml for the build hook.

Editorial conclusion

Adopt boring if you run Rust services that need TLS and want BoringSSL's behaviour, its FIPS module or its post-quantum key exchange rather than the platform OpenSSL. Skip it if you are on version 4 and cannot yet move, since the upgrade crosses a major boundary and changes several APIs. Verify first which crate you need, since the safe API, the FFI layer and the async adapters are published separately, and that the Rust version in use meets the workspace minimum.

Frequently asked questions

What is the boring crate and what does it give a Rust project?

It provides safe Rust bindings to BoringSSL, Google's fork of OpenSSL used by Chrome, Chromium and Android, along with TLS adapters for tokio and a hyper HTTPS connector built on them. It also supports FIPS-compatible builds of BoringSSL, post-quantum key exchange using the TLS ECDHE with ML-KEM draft, and Raw Public Key extensions.

What are the four crates in the boring workspace and how do they relate?

The workspace has four members, the boring safe API crate, boring-sys with the raw FFI bindings, tokio-boring with the asynchronous TLS adapters and hyper-boring with the hyper HTTPS connector. All four are published with a shared version, 5.3.0 at the time of writing, with the workspace pinned to Rust 1.85 and the 2021 edition.

How do I support both boring v4 and v5 in the same project?

Add boring = ">=4.22,<6" to your Cargo.toml. To detect the selected version automatically, add boring-sys = ">=4.22,<6" as a direct dependency and check in your build.rs whether the DEP_BORINGSSL_VERSION_MAJOR environment variable is set to 5, noting that versions older than 5.2.0 do not set it.

Official sources

  1. cloudflare/boring on GitHub
  2. Issues
  3. README
  4. 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/cloudflare-boring.svg)](https://hysenlabs.com/projects/cloudflare-boring)

Was this useful?