cipherbird 1.0.0 copy "cipherbird: ^1.0.0" to clipboard
cipherbird: ^1.0.0 copied to clipboard

Native cryptography for Flutter over dart:ffi: hashing, AEAD, post-quantum, hybrid KEM, sealed messaging, Noise, recipes and easy-mode keys, engine bundled.

cipherbird #

Native cryptography for Flutter. CipherBird binds its native C++ engine through dart:ffi and ships the compiled library inside the package, so an app gets hashing, authenticated encryption, public-key cryptography, post-quantum algorithms, hybrid key agreement, sealed messaging, a secure channel, password hashing, vaults, a keyring, anonymous credentials, threshold signatures and steganography from one dependency, with no platform channels and no setup. The Dart API carries the package name: CipherBird, CipherBirdRecipe, CipherBirdRunner.

Area What runs Notes
Hashing SHA-256, SHA-512, BLAKE2b, BLAKE3, Keccak-256 HMAC, HKDF, incremental BLAKE3
Passwords Argon2id PHC strings, derive keys, three cost profiles
Symmetric XChaCha20-Poly1305, AES-256-GCM, key-committing AEAD, SecretStream the committing mode is the default in easy mode
Public key X25519, Ed25519, boxes, sealed boxes, secp256k1 EVM addresses and recovery
Post-quantum ML-KEM, ML-DSA, SLH-DSA, sntrup761 FIPS 203, 204 and 205 through liboqs
Hybrid X25519 plus ML-KEM-768, Ed25519 plus ML-DSA-65, X25519 plus sntrup761 secure if either half holds
Protocols sealed messaging, Noise XX, HPKE, OPAQUE, OPRF, BBS, FROST, ECVRF, BLS, session ratchet composition of the primitives above
Storage vaults, MolecularVault, keyring with device and passphrase slots
Media media entropy, DRBG, Fortuna, steganography, forward error correction

Supported platforms: Android, iOS and macOS. The same code is published for plain Dart as cipherbird_dart for servers, command-line tools and desktop programs; both packages are generated from one source tree and expose the same API.

Flutter or Dart? #

cipherbird is the Flutter package. Use it in a Flutter app: it carries the compiled engine for every mobile and desktop target the app can run on and plugs it into Flutter's build through Swift Package Manager and jniLibs. It is optional everywhere else. A server, a command-line tool or a plain Dart desktop program should depend on cipherbird_dart instead, which has no Flutter dependency. The two expose the same classes and methods and run the same test suite; they differ only in how the engine reaches the program.

cipherbird cipherbird_dart
Depends on Flutter SDK, ffi ffi only
Engine binaries bundled per platform: Android arm64-v8a and x86_64, iOS device and simulator, macOS bundled for the host under native/<os>-<arch>/, macOS on Apple silicon in this release
How the engine is found the process image on iOS and macOS, libcipherbird.so from jniLibs on Android the package's native/ directory, located through the program's package configuration
Startup await CipherBird.preload() before runApp, or lazy on first use lazy on first use, preload optional
Build integration Flutter's plugin tooling, nothing to configure none needed; dart run, dart test and dart compile exe (pass the library path)
Download size about 28 MB about 5 MB
API, wire formats, tests identical identical

Install #

flutter pub add cipherbird

Nothing else is required. The native library is vendored per platform: a dynamic CipherBird framework through Swift Package Manager on iOS and macOS, and libcipherbird.so under jniLibs on Android. libsodium, liboqs, OpenSSL libcrypto, blst, secp256k1 and BLAKE3 are linked into it statically, so no system library is needed.

Platform Minimum
Android API 24, arm64-v8a and x86_64
iOS 15.0, arm64 device and arm64 simulator
macOS 12.0, Apple silicon, Swift Package Manager

Call CipherBird.preload() once at startup. It loads and initialises the library on a background isolate; every later call is synchronous:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await CipherBird.preload();
  runApp(const MyApp());
}

Skipping preload is allowed: the first use of CipherBird.instance does the same work synchronously.

Quick start #

import 'package:cipherbird/cipherbird.dart';

final key = SymmetricKey.generate();
final box = key.encryptText('meet at dawn');
final back = key.decryptText(box);

final signer = SigningKey.generate(algorithm: SignatureAlgorithm.hybrid);
final signature = signer.signText('release 1.0.0');
final valid = signer.verifyKey.verifyText('release 1.0.0', signature);

final me = KemKeyPair.generate();
final blob = KemKeyPair.encryptTextFor(me.publicKey, 'for your eyes only');
final plain = me.decryptText(blob);

The easy-mode classes make the safe choice for you: the key-committing AEAD, Argon2id for passphrases, HKDF for sub-keys and the post-quantum hybrid signature. To stretch a passphrase into a key:

final salt = CipherBird.instance.easy.randomBytes(16);
final key = SymmetricKey.fromPassphrase('correct horse battery staple', salt: salt);
final databaseKey = key.derive('database');

Store the salt next to the data; it is not secret. The same passphrase and salt always give the same key.

Easy mode #

Every class exposes its raw bytes and can be rebuilt from them, so moving between the easy classes and the full API costs nothing.

Class Create Use Underneath
SymmetricKey generate(), fromBytes, fromHex, fromBase64, fromPassphrase(salt:) encrypt, decrypt, encryptText, decryptText, derive(purpose), asKeySource, destroy() key-committing AEAD, HKDF-SHA256, Argon2id
SigningKey generate(algorithm:), fromBytes sign, signText, verifyKey, scheme Ed25519, or Ed25519 plus ML-DSA-65
VerifyKey fromBytes, fromHex verify, verifyText, scheme
KemKeyPair generate(), fromBytes encapsulate, decapsulate, encryptFor, decrypt, encryptTextFor, decryptText X25519 plus ML-KEM-768, HKDF, committing AEAD
Identity lib.easy.identity(tier) sealText, openText, seal, open, streaming sealers and openers Flagship or Fortress sealed messaging

lib.easy holds the factories plus the small utilities every app needs:

Utility Returns
hashPassword(password) an Argon2id PHC string to store
verifyPassword(password, phc) whether the password matches
randomBytes(n), randomHex(n) bytes from the OS CSPRNG
token() a URL-safe random token with 32 bytes of entropy
sha256Hex(text) the digest as hex
symmetricKeyFromPassphraseAsync, hashPasswordAsync, verifyPasswordAsync the Argon2id operations on a worker

Strings and bytes convert in place:

Expression Result
'text'.bytes UTF-8 bytes
'ab12'.hexBytes, 'aGk='.base64Bytes decoded bytes
bytes.hex, bytes.base64, bytes.base64Url, bytes.text encoded strings
bytes.constantTimeEquals(other) comparison that does not leak the first mismatch
bytes.concat(other), bytes.wipe(), list.u8 join, zero, convert

Full API #

All 265 native operations are reachable on CipherBird, flat or through grouped views that keep autocomplete usable. Every operation takes and returns Uint8List, throws on failure and never returns partial data.

final lib = CipherBird.instance;
final digest = lib.hash.sha256('abc'.bytes);
final pair = lib.pq.hybridKem.keygen();
final (ciphertext, shared) = lib.pq.hybridKem.encapsulate(pair.publicKey);
final opened = lib.pq.hybridKem.decapsulate(ciphertext, pair.secretKey);
Group Operations
lib.hash SHA-256, SHA-512, BLAKE2b, BLAKE3 hash, keyed, derive key and incremental hasher, HMAC with constant-time verify, HKDF extract, expand and derive, Argon2id hash, verify and derive
lib.aead XChaCha20-Poly1305, AES-256-GCM, committing AEAD, SecretStream, key generation
lib.asym Ed25519 keys, sign, verify; X25519; boxes and sealed boxes
lib.pq mlKem, mlDsa, slhDsa, hybridKem, hybridSig, sntrup
lib.sealed identities, seal, open, streaming, envelope inspection, recipient matching
lib.channel Noise XX handshake and transport with re-entrant decryption by counter
lib.session post-quantum forward-secret session ratchet
lib.hpke HPKE in all four modes, single-shot and context forms
lib.opaque, lib.oprf OPAQUE password login and the OPRF it builds on
lib.bbs BBS signatures, selective disclosure, pseudonyms, blind issuance
lib.frost, lib.ecvrf, lib.bls threshold Ed25519, verifiable random function, BLS12-381
lib.vault, lib.keyring symmetric and asymmetric vaults, MolecularVault, keyrings
lib.suite, lib.composed one-call combinations: post-quantum messages, threshold vaults, file-as-key vaults, physical-media seals, HPKE into a carrier
lib.entropy, lib.stego, lib.rng media entropy and health, DRBG, Fortuna, steganography, forward error correction
lib.chain Keccak-256, secp256k1 sign and recover, EVM addresses

Profiles and recipes #

SecurityProfile moves every parameter together so a maximal KEM is never paired with an interactive KDF.

Profile KEM Signature Sealed tier AEAD cascade Argon2id
balanced ML-KEM-768 ML-DSA-65 Flagship XChaCha20-Poly1305 2 passes, 64 MiB
high ML-KEM-768 ML-DSA-65 Flagship XChaCha20-Poly1305, AES-256-GCM 3 passes, 256 MiB
maximum ML-KEM-1024 ML-DSA-87 Fortress XChaCha20-Poly1305, AES-256-GCM, key-committing 4 passes, 512 MiB

CipherBirdRecipe composes a key source, the cascade, an optional signature, optional forward error correction and an optional steganographic carrier into one self-describing envelope:

final recipe = lib
    .maximumSecurity()
    .withPassphrase('correct horse battery staple')
    .signedBy(identity.secretKey, algorithm: SignatureAlgorithm.hybrid)
    .verifiedBy(identity.publicKey);
final envelope = recipe.seal(secret);
final back = recipe.open(envelope);
Method Effect
withKey, withPassphrase, withKeyFile, withKeySource where the 32-byte root key comes from
withLayers, addLayer the AEAD cascade, innermost first
signedBy, verifiedBy, signedWith, verifiedWith sign the plaintext before encryption
withFec repetition or Hamming error correction on the envelope
argon2Cost override the passphrase stretching cost
seal, open, sealIntoCarrier, openFromCarrier, describe run it, or print the configuration for review

Whatever the configuration, each layer gets its own HKDF-derived key, the header is authenticated by every layer, the order sign, encrypt, correct, conceal is fixed, and any failure throws before data is returned.

Bring your own parts #

Three bases can be subclassed, and the built-ins subclass the same ones.

Base Built-ins Register
ProtectionLayer xchacha20Poly1305, aes256Gcm, committing, molecular ProtectionLayer.register(layer) on both sides
CascadeLayer subclass it to mix several layers, built-in or custom, into one; cascades nest
KeySource RawKeySource, PassphraseKeySource, KeyFileSource recipe.withKeySource(source)
SignatureScheme Ed25519Signature, HybridSignature recipe.signedWith(scheme), recipe.verifiedWith(scheme)
final class BeltAndBraces extends CascadeLayer {
  const BeltAndBraces()
      : super(id: 201, wireName: 'belt-and-braces', layers: const [
          ProtectionLayer.xchacha20Poly1305,
          ProtectionLayer.aes256Gcm,
          ProtectionLayer.committing,
        ]);
}

Ids 0 to 127 are reserved and enforced, a key source must return exactly 32 bytes, a layer never chooses its own key, and the wire name feeds the key derivation, so a mismatched implementation fails the authentication tag instead of producing garbage.

Off-thread work #

Argon2id at 64 MiB takes a noticeable fraction of a second. The asynchronous twins run it through a CipherBirdRunner, CipherBirdIsolateRunner by default and CipherBirdInlineRunner in tests:

final key = await lib.easy.symmetricKeyFromPassphraseAsync('pw', salt: salt);
final phc = await lib.easy.hashPasswordAsync('hunter2');

Everything else is fast enough to call on the UI isolate; the figures below say how fast.

How it compares #

The pub.dev packages below each cover part of the same job. The first table is about scope; the second is measured.

cipherbird pointycastle 3.9 cryptography 2.7 crypto 3.0 sodium 3.x
Implementation C++ engine over dart:ffi pure Dart pure Dart, native through a companion package on some platforms pure Dart libsodium over dart:ffi
Hashing SHA-2, BLAKE2b, BLAKE3, Keccak, HMAC, HKDF SHA-2, SHA-3, BLAKE2b, HMAC, HKDF SHA-2, BLAKE2, HMAC, HKDF SHA-2, HMAC SHA-2, BLAKE2b
AEAD XChaCha20-Poly1305, AES-256-GCM, key-committing, streaming ChaCha20-Poly1305, AES-GCM ChaCha20-Poly1305, AES-GCM none XChaCha20-Poly1305, AES-GCM, streaming
Password hashing Argon2id Argon2 Argon2id none Argon2id
Post-quantum ML-KEM, ML-DSA, SLH-DSA, sntrup761, hybrids none none none none
Signatures Ed25519, hybrid, BLS, secp256k1 ECDSA, RSA Ed25519, ECDSA none Ed25519
Protocols sealed messaging, Noise XX, HPKE, OPAQUE, OPRF, BBS, FROST, VRF, ratchet none none none sealed boxes
Vaults, keyring, steganography, media entropy yes no no no no
Safe-default layer easy mode, profiles, recipes no partial no partial
Same wire format in other languages ten bindings, verified no no no libsodium compatible
Web no yes yes yes yes
License MIT MIT MIT BSD-3-Clause LGPL-3.0

Choose a pure-Dart package for the web or when a few kilobytes of download matter more than throughput. Choose cipherbird when speed, post-quantum readiness, higher-level protocols or cross-language compatibility matter.

Measured #

The harness in cipherbird_dart/benchmark/bench.dart runs each operation for two seconds after one warm-up call and reports the mean. Apple M3 Max, macOS 26.6, Dart 3.13.5, pointycastle 3.9.1, cryptography 2.7.0, crypto 3.0.6. Throughput rows are in MB/s on 1 MiB inputs; the rest are operations per second. Factors are relative to cipherbird.

Operation cipherbird (native) pointycastle cryptography crypto
SHA-256, 1 MiB 275 MB/s 76 MB/s (3.6x slower) 90 MB/s (3.1x slower) 90 MB/s (3.0x slower)
BLAKE2b-512, 1 MiB 624 MB/s 29 MB/s (21.3x slower) n/a n/a
BLAKE3, 1 MiB 1278 MB/s n/a n/a n/a
XChaCha20/ChaCha20-Poly1305 encrypt, 1 MiB 273 MB/s 46 MB/s (5.9x slower) 26 MB/s (10.5x slower) n/a
AES-256-GCM encrypt, 1 MiB 647 MB/s 1 MB/s (584.4x slower) 14 MB/s (47.7x slower) n/a
Ed25519 sign, 1 KiB 42771 ops/s n/a 415 ops/s (103.2x slower) n/a
Ed25519 verify, 1 KiB 20353 ops/s n/a 403 ops/s (50.5x slower) n/a
X25519 shared secret 24581 ops/s n/a 1494 ops/s (16.5x slower) n/a
Argon2id, 64 MiB, 2 passes 15.2 ops/s 1.8 ops/s (8.3x slower) 2.4 ops/s (6.3x slower) n/a
ML-KEM-768 keygen + encapsulate + decapsulate 11847 ops/s n/a n/a n/a
Hybrid X25519 + ML-KEM-768 keygen + encapsulate + decapsulate 3826 ops/s n/a n/a n/a

Reading the numbers fairly:

  • Every cipherbird call copies its input into native memory and the result back. The throughput rows include that cost; the public-key rows are pure compute and show the larger gap.
  • The cryptography package runs pure Dart on the Dart VM here. Its companion cryptography_flutter reaches platform APIs on some targets, which this harness does not measure.
  • pointycastle's AES-GCM is a pure-Dart table implementation with no hardware acceleration, which is where its figure comes from.
  • There is no pure-Dart ML-KEM in these packages, so the post-quantum rows show what the native engine delivers on its own.

Release bundle size, measured as the whole app against an empty Flutter app built the same way:

Build Empty Flutter app With cipherbird Added
macOS release .app, Apple silicon 37 MB 46 MB 9 MB (the CipherBird framework is 8 MB)
Android release APK, arm64 only 14.8 MB 30.1 MB 15.3 MB

The added size is the native library: libsodium, liboqs with every parameter set the package exposes, OpenSSL libcrypto, blst, secp256k1 and BLAKE3 linked into one binary per platform.

Interoperability #

Every binding of the same engine (Go, Node, Swift, Java, Kotlin, Python, Ruby, Rust, .NET and the two Dart packages) wraps the same C ABI and uses the library's own wire formats. Vaults, sealed envelopes, recipes and keyrings move between languages unchanged. The repository seals five recipe configurations in each language and opens them in every other one, 72 pairs, on every change.

Example and playground #

The example checks the library on a device: it loads the engine, verifies a SHA-256 known answer and round-trips a hybrid key agreement, a hybrid signature, a sealed message and a MolecularVault.

The playground is the app to play with: buttons to encrypt, corrupt and decrypt text with the committing AEAD, sign and verify with the hybrid signature, encrypt to a hybrid KEM public key and hash a passphrase with Argon2id on a worker.

Package structure #

lib/src follows one declaration per file with a limit of 100 lines, no else branches, no inline comments, final classes and const factory redirects; test/conventions_test.dart enforces it. Large extensions are split into named chunks such as CipherBirdHybridKem or CipherBirdRecipeSealing, all re-exported by the barrel. The pure-Dart package is generated from this one with scripts/sync_dart_package.sh.

Maintaining the binaries #

The prebuilt binaries are committed and published with the package. They go stale after any C ABI change because the Dart side resolves every symbol, so a stale binary fails at load. Rebuild from the repository root:

bash scripts/android/build_all.sh arm64-v8a x86_64
cp build/android/arm64-v8a/libcipherbird.so android/src/main/jniLibs/arm64-v8a/
cp build/android/x86_64/libcipherbird.so android/src/main/jniLibs/x86_64/
make ios
rm -rf ios/cipherbird/CipherBird.xcframework macos/cipherbird/CipherBird.xcframework
cp -R CipherBird.xcframework ios/cipherbird/CipherBird.xcframework
cp -R CipherBird.xcframework macos/cipherbird/CipherBird.xcframework

Then check the symbol count against the header, and run the consumer proof, which installs the exact archive as a hosted package in a fresh app and runs this package's tests on a device:

grep -c '^CRYPTO_API' bridge/cryptolib_c.h
nm -gU ios/cipherbird/CipherBird.xcframework/ios-arm64/CipherBird.framework/CipherBird | grep -c ' T _cryptolib_'
make flutter-consumer DEVICE=macos

The iOS simulator slice is arm64 only. A generic flutter build ios --simulator also tries x86_64 and fails; exclude that architecture for the simulator in the consuming app or add a universal slice.

Engine #

The engine is a header-only C++20 core with a pure C ABI of 265 functions, bindings for ten languages, a custom test runner with known-answer tests from the official sources, and a cross-language conformance harness. Its repository holds the build, the tests, the benchmark and the design notes. Everything is MIT licensed; the vendored dependencies are BSD, MIT, Apache or public domain. The license requires that the copyright notice, which credits the author Crdzbird, is kept in every copy or substantial portion of this software.

2
likes
0
points
453
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

Native cryptography for Flutter over dart:ffi: hashing, AEAD, post-quantum, hybrid KEM, sealed messaging, Noise, recipes and easy-mode keys, engine bundled.

Repository (GitHub)
View/report issues

Topics

#cryptography #encryption #post-quantum #ffi #security

License

unknown (license)

Dependencies

ffi, flutter

More

Packages that depend on cipherbird

Packages that implement cipherbird