This repository is parameter selection and lightweight wrapper around a number of (FFI wrapped) Rust cryptographic libraries. Its purpose isn't to implement primitives, rather to unify the API surface of existing libraries; limited to the tiny subset needed by the Dark Bio project.
The library is opinionated. Parameters and primitives were selected to provide matching levels of security in a post-quantum world. APIs were designed to make the library easy to use and hard to misuse. Flexibility will always be rejected in favor of safety.
- Digital signatures
- Encryption
- Key derivation
- Serialization
- Credential / Attestation
¹ As CBOR encoding/decoding would require a full reimplementation in Dart, that is delegated to any preferred 3rd party library. To ensure correctness, this package provides a cbor.verify, which it also implicitly enforces when crossing through cose and cwt.
Signatures come from xdsa, encryption from xhpke, and cose wraps both into COSE envelopes using the Dark Bio wire profile. Payloads and authenticated messages are plain Dart values within the CBOR subset above: bool, null, int, String, Uint8List for bytes, and lists and integer-keyed maps of those. The cbor library documents the details.
flutter pub add darkbio_cryptoCOSE signing and verification and xHPKE encryption and decryption use an application domain that both sides must agree on. It is prefixed with dark-bio-v1: internally and binds the operation to one purpose. Choose distinct domains for distinct purposes. Raw xdsa signatures carry no such application domain, which is why the cose envelopes are the recommended entry point.
import 'dart:convert';
import 'package:darkbio_crypto/darkbio_crypto.dart' as darkbio;
import 'package:darkbio_crypto/cose.dart' as cose;
import 'package:darkbio_crypto/xdsa.dart' as xdsa;
import 'package:darkbio_crypto/xhpke.dart' as xhpke;
Future<String> example() async {
// Load the native library once, before any other call
await darkbio.init();
// Long term identities, one for signing and one for receiving
final signer = xdsa.SecretKey.generate();
final recipient = xhpke.SecretKey.generate();
final domain = utf8.encode('example');
// A detached signature over a message that travels separately
final signature = cose.signDetached(msgToAuth: 'payload', signer: signer, domain: domain);
cose.verifyDetached(msgToCheck: signature, msgToAuth: 'payload', verifier: signer.publicKey(), domain: domain, maxDriftSecs: 60);
// Sign and encrypt a payload to the recipient, then open and verify it back.
// The second argument is authenticated but must be supplied separately.
final sealed = cose.seal(msgToSeal: 'payload', msgToAuth: 'metadata', signer: signer, recipient: recipient.publicKey(), domain: domain);
return cose.open<String>(msgToOpen: sealed, msgToAuth: 'metadata', recipient: recipient, sender: signer.publicKey(), domain: domain, maxDriftSecs: 60);
}The underlying implementation exists in two sibling repos, which track the same feature set and API surfaces, released at corresponding version points.
Sibling wrapper exists in one other repo:
- TypeScript
github.com/dark-bio/crypto-ts
Shoutout to Filippo Valsorda (@filosottile) for lots of tips and nudges on what kind of cryptographic primitives to use and how to combine them properly; and also for his work in general on cryptography standards.
Naturally, many thanks to the authors of all the libraries this project depends on.
This library is licensed under the BSD 3-Clause License.
