This project is a Flutter Plugin that provides access to native cryptographic implementations written in Rust. It also provides a fallback pure-Dart implementation.
It provides out-of-the box support for cross-compiling native Rust code for all available iOS and Android architectures and call it from plain Dart using Foreign Function Interface.
The library provides:
The following platforms and architectures are supported:
Cross compilation for MacOS, Linux and Windows should not be difficult but is not available yet.
For a census of 1 million claims, ZK proof generation times are in the range of:
The Rust source code is located at https://github.com/vocdoni/dvote-rs-ffi and mounted on the rust
folder by git.
$ git submodule init
$ git submodule update
$ANDROID_NDK_HOME
points to the NDK base folder
/Users/name/Library/Android/sdk/ndk-bundle
on MacOS/home/name/dev/android/ndk-bundle
on Linuxrust
folder:
make
to see the available actionsmake init
to install the Rust targetsmake all
to build the libraries and the .h
fileCargo.toml
Generated artifacts:
rust/target/aarch64-linux-android/release/libdvote.so
rust/target/armv7-linux-androideabi/release/libdvote.so
rust/target/i686-linux-android/release/libdvote.so
rust/target/x86_64-linux-android/release/libdvote.so
rust/target/universal/release/libdvote.a
rust/target/bindings.h
Ensure that ios/dvote_native.podspec
includes the following directives:
...
s.source = { :path => '.' }
+ s.public_header_files = 'Classes**/*.h'
s.source_files = 'Classes/**/*'
+ s.static_framework = true
+ s.vendored_libraries = "**/*.a"
s.dependency 'Flutter'
s.platform = :ios, '9.0'
...
On flutter/ios
, place a symbolic link to the libdvote.a
file
$ cd flutter/ios
$ ln -s ../rust/target/universal/release/libdvote.a .
Append the generated function signatures from rust/target/bindings.h
into flutter/ios/Classes/DVotePlugin.h
$ cd flutter/ios
$ cat ../rust/target/bindings.h >> Classes/DVotePlugin.h
In our case, it will append char *rust_greeting(const char *to);
and void rust_cstr_free(char *s);
NOTE: By default, XCode will skip bundling the libdvote.a
library if it detects that it is not being used. To force its inclusion, add a dummy method in SwiftDVotePlugin.swift
that uses at least one of the native functions:
...
public func dummyMethodToEnforceBundling() {
rust_greeting("");
}
}
If you won’t be using Flutter channels, the rest of methods can be left empty.
Similarly as we did on iOS with libdvote.a
, create symlinks pointing to the binary libraries on rust/target
.
You should have the following structure on flutter/android
for each architecture:
src
└── main
└── jniLibs
├── arm64-v8a
│ └── libdvote.so@ -> ../../../../../rust/target/aarch64-linux-android/release/libdvote.so
├── armeabi-v7a
│ └── libdvote.so@ -> ../../../../../rust/target/armv7-linux-androideabi/release/libdvote.so
└── x86
└── libdvote.so@ -> ../../../../../rust/target/i686-linux-android/release/libdvote.so
As before, if you are not using Flutter channels, the methods within android/src/main/kotlin/com/dvote/dvote_native/DvoteNativePlugin.kt
can be left empty.
In /lib/dvote_native.dart
, initialize the function bindings from Dart and implement any additional logic that you need.
Load the library:
final DynamicLibrary nativeExampleLib = Platform.isAndroid
? DynamicLibrary.open("libdvote.so")
: DynamicLibrary.process();
Find the symbols we want to use, with the appropriate Dart signatures:
final Pointer<Utf8> Function(Pointer<Utf8>) rustGreeting = nativeExampleLib
.lookup<NativeFunction<Pointer<Utf8> Function(Pointer<Utf8>)>>("rust_greeting")
.asFunction();
final void Function(Pointer<Utf8>) freeGreeting = nativeExampleLib
.lookup<NativeFunction<Void Function(Pointer<Utf8>)>>("rust_cstr_free")
.asFunction();
Call them:
// Prepare the parameters
final name = "John Smith";
final Pointer<Utf8> namePtr = Utf8.toUtf8(name);
print("- Calling rust_greeting with argument: $namePtr");
// Call rust_greeting
final Pointer<Utf8> resultPtr = rustGreeting(namePtr);
print("- Result pointer: $resultPtr");
final String greetingStr = Utf8.fromUtf8(resultPtr);
print("- Response string: $greetingStr");
When we are done using greetingStr
, tell Rust to free it, since the Rust implementation kept it alive for us to use it.
freeGreeting(resultPtr);
Generating mnemonics and computing private/public keys
final wallet = EthereumWallet.random(hdPath: "m/44'/60'/0'/0/5");
final mnemonic = wallet.mnemonic;
final privKey = wallet.privateKey;
final pubKey = wallet.publicKey;
final addr = wallet.address;
Computing signatures using ECDSA cryptography
// Signing plain text
final hexSignature = signString(messageToSign, privateKey);
final recoveredPubKey = recoverSignerPubKey(hexSignature, messageToSign);
final valid = isValidSignature(hexSignature, messageToSign, publicKey);
// Signing reproduceable JSON data
final hexSignature2 = signJsonPayload({"hello": 1234}, privateKey);
final recoveredPubKey = recoverJsonSignerPubKey(hexSignature2, {"hello": 1234});
final valid2 = isValidJsonSignature(hexSignature2, {"hello": 1234}, publicKey);
Also available as async non-UI blocking functions:
// Signing plain text
final hexSignature = await signStringAsync(messageToSign, privateKey);
final recoveredPubKey = await recoverSignerPubKeyAsync(hexSignature, messageToSign);
final valid = await isValidSignatureAsync(hexSignature, messageToSign, publicKey);
// Signing reproduceable JSON data
final hexSignature2 = await signJsonPayloadAsync({"hello": 1234}, privateKey);
final recoveredPubKey = await recoverJsonSignerPubKeyAsync(hexSignature2, {"hello": 1234});
final valid2 = await isValidJsonSignatureAsync(hexSignature2, {"hello": 1234}, publicKey);
make init
to start developingmake publish
to update the symbolic links and upload to pub.devAuthor: vocdoni
Source Code: https://github.com/vocdoni/dvote-flutter-crypto
#flutter #dart #mobile-apps