boring 0.4.1 copy "boring: ^0.4.1" to clipboard
boring: ^0.4.1 copied to clipboard

Raw FFI bindings to BoringSSL with Dart Native Assets and OPENSSL_cleanse-backed native allocation.

boring #

Raw ffigen bindings to BoringSSL powered by Dart Native Assets.

pub package License: Apache 2.0

package:boring bundles Google's BoringSSL (bssl_dart) via Dart Native Assets and exposes its raw @Native C bindings along with an OPENSSL_malloc / OPENSSL_free-backed ffi.Allocator across Linux, macOS, Windows, Android, and iOS.


Key Highlights #

  • 100% Symbol Isolation (bssl_dart): Compiled with -DBORINGSSL_PREFIX=bssl_dart, eliminating dynamic linker collisions with Flutter, the Dart VM, or system OpenSSL libraries.
  • Dart Native Assets: Bundles and dynamically loads native code automatically via package:code_assets and package:hooks.
  • Automatic Memory Scrubbing (opensslAllocator): Exports opensslAllocator (ffi.Allocator backed by OPENSSL_malloc / OPENSSL_free), which stores allocation sizes and unconditionally runs OPENSSL_cleanse before freeing native memory.
  • Concrete CBS & CBB Structs: Both CBS (CRYPTO ByteString) and CBB (CRYPTO ByteBuilder) are generated as concrete ffi.Struct types, allowing direct stack/arena allocation (arena<CBS>(), arena<CBB>()) without C wrapper shims.
  • Scoped Native Memory (BoringArena): An ffi.Allocator and resource tracker backed by opensslAllocator. BoringArena.run and BoringArena.stream release allocations and X_new / X_free resources (arena.using(EC_KEY_new(), EC_KEY_free)) in reverse order once the computation is done, including async ones. move() supports BoringSSL's set0 ownership transfer, and copyBytes, cbs(), cbb(), and CBB.toBytes() cover byte-string plumbing.
  • Finalizable Handles (NativeHandle): A GC-managed wrapper for long-lived BoringSSL objects (NativeHandle(EVP_PKEY_new(), addresses.EVP_PKEY_free)) with deterministic dispose(), using the *_free symbol addresses exposed as addresses.*.
  • Tree-Shaking: Release builds only bundle the BoringSSL functions the application uses, see Tree-Shaking.

Getting Started #

Add boring to your pubspec.yaml:

dependencies:
  boring: ^0.3.0

Usage #

Import package:boring/bindings.dart (or package:boring/boring.dart) and scope native allocations and resources with BoringArena:

import 'dart:convert';
import 'dart:ffi' as ffi;
import 'dart:typed_data';

import 'package:boring/bindings.dart' as ssl;

void main() {
  final digest = ssl.BoringArena.run((arena) {
    final input = utf8.encode('hello world');
    final md = ssl.EVP_sha256();
    final out = arena<ffi.Uint8>(ssl.EVP_MD_size(md));
    final outLen = arena<ffi.UnsignedInt>();
    final ctx = arena.using(ssl.EVP_MD_CTX_new(), ssl.EVP_MD_CTX_free);

    if (ssl.EVP_DigestInit(ctx, md) != 1 ||
        ssl.EVP_DigestUpdate(ctx, arena.copyBytes(input), input.length) != 1 ||
        ssl.EVP_DigestFinal(ctx, out, outLen) != 1) {
      throw StateError(ssl.extractBoringSslError() ?? 'SHA-256 failed');
    }
    return Uint8List.fromList(out.asTypedList(outLen.value));
  });

  final hex = digest.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
  print('SHA-256("hello world"): $hex');
}

Error Handling #

BoringSSL signals failure through return values and pushes details onto a per-thread error queue. A Dart isolate may resume on a different OS thread after an await, and every package using package:boring on a thread shares its queue, so:

  • Read errors with extractBoringSslError(), which also clears the queue, right after the failing call. Don't leave an await in between, and don't defer it to a finally that may run after one, such as the release of an async BoringArena.run.
  • Discard errors you ignore with ERR_clear_error(), for example when a failed signature verification just means false. Otherwise they are reported for the next, unrelated failure.
  • Call ERR_clear_error() before a call whose errors you report, so errors left behind by other code aren't attributed to it.

Native Asset Build Modes #

Configured in pubspec.yaml under hooks.user_defines.boring:

hooks:
  user_defines:
    boring:
      buildMode: fetch # 'fetch', 'checkout', or 'local'
  • fetch (default): Downloads prebuilt binaries from GitHub Releases verified against pinned SHA-256 checksums, falling back to local compilation if unavailable.
  • checkout: Always compiles BoringSSL locally from bundled sources via CMake and Ninja.
  • local: Uses a custom prebuilt dynamic library at localPath, which is bundled as is, without tree-shaking.

fetch and checkout provide a dynamic library with all of BoringSSL when linking is disabled (dart run, dart test, and Flutter debug builds), and a static library for tree-shaking when it is enabled. Every GitHub Release has both for each prebuilt target.


Tree-Shaking #

When linking is enabled (dart build, and Flutter profile and release builds), hook/link.dart links a dynamic library with only the functions the application uses from the static library. The bindings are annotated with @RecordUse(), so the Dart compiler records which of them the application calls, tears off, or takes the address of with addresses.*. For the example, the bundled library shrinks from 2.9 MB to 240 KB on Linux x64.

  • Use addresses.X rather than Native.addressOf(X) for the address of a function, for example for a NativeFinalizer. Native.addressOf isn't recorded, so the function would be missing from the library.
  • Without recorded uses, for example with flutter config --no-enable-record-use, all functions are kept.
  • Linking requires a C toolchain (Clang or GCC, Xcode, MSVC, or the Android NDK), even with prebuilt binaries.

Conformance Testing #

CI checks the bundled BoringSSL and the generated bindings against two external suites, calling BoringSSL directly through the bindings (see test/conformance/):

  • Project Wycheproof (./tool/run_conformance_tests.sh): AES-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305, AES-CBC, AES Key Wrap, Ed25519, ECDSA (P-256, P-384, P-521), RSA PKCS#1 v1.5 and RSA-PSS signatures, RSA-OAEP, ECDH, HKDF, HMAC, and PBKDF2.
  • x509-limbo (./tool/run_x509_limbo_tests.sh): 9,770 of the 9,793 path validation testcases run against X509_verify_cert, and 9,237 (94.5%) agree. The divergences, mostly name constraint types BoringSSL does not support and CA/Browser Forum profile checks it leaves to the caller, are listed and explained in test/conformance/x509_limbo_expected_failures.txt.

License #

Apache License, Version 2.0. See LICENSE for details. BoringSSL is licensed under Apache 2.0 and BSD-style licenses.

0
likes
160
points
393
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Raw FFI bindings to BoringSSL with Dart Native Assets and OPENSSL_cleanse-backed native allocation.

Repository (GitHub)
View/report issues

Topics

#crypto #ffi #security

License

Apache-2.0 (license)

Dependencies

code_assets, hooks, logging, meta, native_toolchain_c, native_toolchain_cmake, record_use

More

Packages that depend on boring