OQS for Dart
Dart FFI bindings for liboqs, providing post-quantum KEM and signature primitives.
Version Compatibility
oqs package |
liboqs |
|---|---|
3.x |
0.15.x |
2.x |
0.14.x (legacy) |
3.0.0 is a breaking release aligned to liboqs 0.15.0.
Install
dependencies:
oqs: ^3.1.0
Native Library Setup
You need a native liboqs library for your platform.
Option 1: Prebuilt binaries (Recommended)
Download pre-built binaries from:
Supported platforms:
- Linux: x86_64, ARM64 (aarch64)
- macOS: ARM64 (Apple Silicon)
- Windows: x64
- iOS: XCFramework (device + simulator)
- Android: armeabi-v7a, arm64-v8a, x86, x86_64
Using the all-platforms archive:
# Extract the combined archive
tar -xzf liboqs-0.15.0-all-platforms.tar.gz
# Use in your Dart code
LibOQSLoader.loadLibrary(binaryRoot: '/path/to/liboqs-0.15.0');
The all-platforms archive contains architecture-separated binaries:
liboqs-0.15.0/
lib/x86_64/liboqs.so # Linux x86_64
lib/aarch64/liboqs.so # Linux ARM64
lib/liboqs.dylib # macOS ARM64
bin/oqs.dll # Windows x64
android/arm64-v8a/ # Android binaries
liboqs.xcframework/ # iOS
Option 2: Build from source
git clone https://github.com/open-quantum-safe/liboqs.git
cd liboqs
mkdir build && cd build
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local ..
ninja
ninja install
Optional explicit paths
import 'package:oqs/oqs.dart';
LibOQSLoader.customPaths = LibraryPaths(
windows: r'C:\libs\oqs.dll',
linuxX64: '/usr/local/lib/liboqs.so',
linuxArm64: '/usr/local/lib/liboqs.so', // For ARM64 systems
macOS: '/opt/homebrew/lib/liboqs.dylib',
);
Library Loading Guide
LibOQSLoader.loadLibrary() uses fallback strategies in this exact order:
explicitPathargumentLibOQSLoader.customPaths(LibraryPaths)- Deprecated
LibOQSLoader.customPath - Environment variable (
LIBOQS_PATH, orenvVarName) binaryRootextracted release layout- Package-relative paths
- System loader/default name (
liboqs.so,oqs.dll,liboqs.dylib) - Legacy default paths (
bin/<platform>/...)
If all fail, LibraryLoadException includes all attempted strategies.
Auto Path Selection (Package-relative)
PackageRelativeStrategy checks:
./bin/<library-file>./lib/<library-file>./lib/native/<library-file>./native/<library-file>./blobs/<library-file>- Android extras:
./lib/arm64-v8a/liboqs.so./lib/armeabi-v7a/liboqs.so./lib/x86_64/liboqs.so./lib/x86/liboqs.so
Platform Notes
- Linux: Automatically detects x86_64 vs ARM64 (aarch64) architecture
- iOS: Uses
DynamicLibrary.process()(XCFramework/static linking), notDynamicLibrary.open(...) - Android: ABI-specific selection supported through
LibraryPaths.currentPlatformPath - macOS/Windows: System resolution works when library is installed in standard paths
Recommended Config Patterns
Use explicit, deterministic config for production:
final lib = LibOQSLoader.loadLibrary(
explicitPath: '/opt/liboqs/lib/liboqs.so',
);
Or per-platform config:
LibOQSLoader.customPaths = LibraryPaths(
windows: r'C:\oqs\oqs.dll',
linuxX64: '/usr/local/lib/liboqs.so',
linuxArm64: '/usr/local/lib/liboqs.so',
macOS: '/opt/homebrew/lib/liboqs.dylib',
androidArm64: '/data/local/tmp/liboqs.so',
);
Or extracted release root:
final lib = LibOQSLoader.loadLibrary(binaryRoot: '/opt/liboqs-0.15.0');
Cache Behavior
- Loader caches resolved
DynamicLibraryby default. - Update paths at runtime: set
LibOQSLoader.customPaths = ...(this clears cache). - Manual reset:
LibOQSLoader.clearCache().
Debug Checklist
- Verify
LibOQS.getVersion()returns non-empty string. - Print
LibOQS.getSupportedKEMAlgorithms()to confirm expected build features. - If loading fails, inspect thrown
LibraryLoadExceptionstrategy list and fix the earliest intended path.
Quick Start
import 'dart:typed_data';
import 'package:oqs/oqs.dart';
void main() {
LibOQS.init();
final kems = LibOQS.getSupportedKEMAlgorithms();
if (kems.isEmpty) {
throw StateError('No enabled KEM algorithms in loaded liboqs');
}
final kem = KEM.create(kems.first)!;
final kp = kem.generateKeyPair();
final enc = kem.encapsulate(kp.publicKey);
final dec = kem.decapsulate(enc.ciphertext, kp.secretKey);
print(dec.length == enc.sharedSecret.length); // true
kem.dispose();
LibOQS.cleanup();
}
API Notes
- Prefer runtime algorithm discovery:
LibOQS.getSupportedKEMAlgorithms()LibOQS.getSupportedSignatureAlgorithms()
- Do not hard-code key/signature lengths. Use:
kem.publicKeyLength,kem.secretKeyLength,kem.ciphertextLengthsig.publicKeyLength,sig.secretKeyLength,sig.maxSignatureLength
- Deterministic keypair generation is algorithm-dependent:
kem.supportsDeterministicGenerationkem.seedLength
Signature Example
import 'dart:convert';
import 'dart:typed_data';
import 'package:oqs/oqs.dart';
void main() {
final sigAlgs = LibOQS.getSupportedSignatureAlgorithms();
if (sigAlgs.isEmpty) {
throw StateError('No enabled signature algorithms');
}
final sig = Signature.create(sigAlgs.first);
final kp = sig.generateKeyPair();
final msg = Uint8List.fromList(utf8.encode('hello pqc'));
final s = sig.sign(msg, kp.secretKey);
final ok = sig.verify(msg, s, kp.publicKey);
print(ok); // true
sig.dispose();
}
Migration to 3.x (liboqs 0.15.0)
- Upgrade dependency in
pubspec.yamlto^3.1.0. - Ensure native
liboqsbinary is0.15.x. - Replace fixed algorithm assumptions (
Kyber*,Dilithium*) with runtime discovery. - Remove hard-coded size assertions and read lengths from each algorithm instance.
- Re-run tests against every target platform binary you ship.
Common Problems
Library not found
Set LibOQSLoader.customPaths or install liboqs to standard system paths.
Algorithm not available
Enabled algorithms depend on how your liboqs binary was built. Check:
print(LibOQS.getSupportedKEMAlgorithms());
print(LibOQS.getSupportedSignatureAlgorithms());
Security Notes
- Use NIST-standardized algorithms (
ML-KEM-*,ML-DSA-*) for production. - Dispose algorithm objects (
kem.dispose(),sig.dispose()) when done. - Keep
liboqsbinaries updated and track security advisories. - Do not share mutable crypto object state across isolates/threads.
Examples
See the example/ directory for end-to-end usage samples.