Dart bindings for libsodium, supporting both the VM and JS without flutter dependencies.
Table of contents
- Provides a simple to use dart API for accessing libsodium
- High-Level API that is the same for both VM and JS
- Aims to provide access to all primary libsodium APIs. See API Status for more details.
- Provides native APIs for tighter integration, if necessary
The following table shows the current status of the implementation. APIs that have not been implemented yet, but are planned to, are listed with X. If you find an API is missing from this table, please create an issue and it will be added, unless it is one of the "extended" APIs.
API based on libsodium version: 1.0.18
Note: Memory Management in JS is limited to overwriting the memory with 0. All other Memory-APIs are only available in the VM.
sodium to your
pubspec.yaml and run
pub get (or
flutter pub get).
The usage can be split into two parts. The first one is about loading the native libsodium into dart, the second one about using the API.
How you load the library depends on whether you are running in the dart VM or as transpiled JS code.
Note: For flutter users, there is a another library in the making that will completely automate this part.
VM - loading the dynamic library
In the dart VM,
dart:ffi is used as backend to load and interact with the
libsodium binary. So, all you need to do is load such a library and then pass
it to the sodium APIs. This generally looks like this:
// required imports import 'dart:ffi'; import 'package:sodium/sodium.dart'; // load the dynamic library into dart final libsodium = DynamicLibrary.open('/path/to/libsodium.XXX'); // or DynamicLibrary.process() // initialize the sodium APIs final sodium = await SodiumInit.init(libsodium);
The tricky part here is the path, aka
'/path/to/libsodium.XXX'. It depends on
the platform and how you intend to use the library. My recommendation is to
follow https://libsodium.gitbook.io/doc/installation to get the library binary
for your platform and then pass the correct path. If you are linking statically,
you can use
DynamicLibrary.process() (except on windows) instead of the path.
However, here are some tips on how to get the library for some platforms and how to load it there:
- Linux: Install
libsodiumvia your system package manager. Then, you can load the
libsodium.sofrom where the package manager put it.
- Windows: Download the correct binary from https://download.libsodium.org/libsodium/releases/ and simply use the path where you placed the library.
- macOS: Use homebrew and run
brew install libsodium- then locate the binary in the Cellar. It is typically something like
- Android: Coming soon...
- iOS: Coming soon...
// required imports import 'package:sodium/sodium.dart'; final sodiumJS = // somehow load the sodium.js into dart // initialize the sodium APIs final sodium = await SodiumInit.init(sodiumJS);
The only platform I have tried so far is the browser. However, similar approches should work for all JS environments that you can run transpiled dart code in.
Loading sodium.js into the browser via dart.
The idea here is, that the dart code asynchronously loads the
the browser and then acquires the result of loading it (As recommended in
). The following code uses the
sodium.js file from here:
Using the API
Once you have acquired the
Sodium instance, usage is fairly straight forward.
The API mirrors the original native C api, splitting different categories of
methods into different classes for maintainability, which are all built up in
hierachical order starting at
Sodium. For example, if you wanted to use the
crypto_secretbox_easy method from the C api, the eqivalent dart code would be:
final sodium = // load libsodium for your platform // The message to be encrypted, converted to an unsigned char array. final String message = 'my very secret message'; final Int8List messageChars = message.toCharArray(); final Uint8List messageBytes = messageChars.unsignedView(); // A randomly generated nonce final nonce = sodium.randombytes.buf( sodium.crypto.secretBox.nonceBytes, ); // Generate a secret key final SecureKey key = sodium.crypto.secretBox.keygen(); // Encrypt the data final encryptedData = sodium.crypto.secretBox.easy( message: messageBytes, nonce: nonce, key: key, ) print(encryptedData); // after you are done: key.dispose();
The only main differences here are, that instead of raw pointers, the dart typed
lists are used. Also, instead of simply passing a byte array as the key, the
SecureKey is used. It is a special class created for this library that wraps
native memory, thus providing a secure way of keeping your keys in memory. You
can either create such keys via the
*_keygen methods, or directly via
Note: Since these keys wrap native memory, it is mandatory that you dispose of them after you are done with a key, as otherwise they will leak memory.
The example runs both in the VM and on the web. To use it, see below.
As preparation for all platforms, run the following steps:
cd packages/sodium dart pub get dart run build_runner build
Example for the dart VM
Locate/Download the libsodium binrary and run the example with it:
cd packages/sodium/example dart pub get dart run bin/main_native.dart '/path/to/libsodium.XXX'
Example in the browser
sodium.js into the examples web directory. Then simply run the
dart pub global activate webdev cd packages/sodium/example/web curl -Lo sodium.js https://raw.githubusercontent.com/jedisct1/libsodium.js/master/dist/browsers/sodium.js cd .. dart pub get dart pub global run webdev serve --release # Visit http://127.0.0.1:8080 in the browser