Libsodium library for Android


Please refer to this chapter to learn about a Workaround for Android that can be used if you wish to utilize Stronghold AND if you have problems creating libsodium.


The libsodium library is a widely used library for cryptography, offering a variety of functions for encryption, decryption, hashing, signatures, and more.

libsodium-sys is a Rust wrapper around the libsodium library. It provides the necessary Rust bindings and FFI (Foreign Function Interface) declarations to link and interact with the libsodium library from Rust code.

libsodium-sys is a dependency of the stronghold-runtime crate.

GitHub Repositories

πŸ‘‰   GitHub Repo - libsodium

πŸ‘‰   GitHub Repo - libsodium-sys

Challenge

In general, managing dependencies can be tricky, particularly when your project relies on multiple dependencies and you need to cross-compile it for various targets.

The utilization of the deprecated libsodium-sys project in Stronghold poses such a challenge. It came up that it is a time-intensive show-stopper and places a significant burden on the developer's life.

It's not enjoyable to search for workarounds and complicate workflows with special rules. Unfortunately, encountering such situations is quite common when aiming to support a wide range of target platforms.

Workaround

These are the three steps:

In Step 1, create the libsodium.so library file for the desired Android target, here e.g. ABI arm64-v8a (which is target aarch64-linux-android).

In Step 2, copy the created libsodium.so into a project folder and test the build.

In Step 3, include the library in Android's build process within Flutter by defining SODIUM_LIB_DIR and SODIUM_SHARED environment variables.

Step 1: Create libsodium.so

Isn't it pathetic that one has to create the library at all !?

Before proceeding with Step 1 yourself, please note that I provide a download for a prebuilt library (created on macOS) for each of the possible ABIs with the following links. However, it is important to mention that I provide no warranty for its contents or functionality:

arm64-v8a/libsodium.so

armeabi-v7a/libsodium.so

x86/libsodium.so

x86_64/libsodium.so

If the prebuilt libraries work, you can skip the rest of Step 1.


You'll need to clone the libsodium repository in a new IDE project.

git clone https://github.com/jedisct1/libsodium.git

You will need to install some tools on your macOS:

a) Homebrew

Check with brew --version if you can skip the installation. If already installed, a version is returned. Otherwise install Homebrew with the command:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

b) autoconf

Check with autoconf --version if you can skip the installation. If already installed, a version is returned. Otherwise install autoconf with the command:

brew install autoconf

c) automake

Check with automake --version if you can skip the installation. If already installed, a version is returned. Otherwise install automake with the command:

brew install automake

Once you've setup everything, you can navigate into the "libsodium-master" directory and execute:

./autogen.sh -s

This will create the configure file. The next command will create the library for the ABI arm64-v8a:

./dist-build/android-armv8-a.sh

The console output tells you where you can find the libsodium.so: It's located in the folder "libsodium-android-armv8-a+crypto/lib/".

You will need to repeat the last command for each ABI/target accordingly.

Step 2: Copy the libsodium.so file and test the build

Copy the created library and paste it into a directory of your choice. Personally, I utilize the folder "android/app/src/main/jniLibs/<ABI>/", where all libraries are stored together in the appropriate ABI directory for libs, see the image below.

Now, I recommend to test the playground_app build in Terminal by setting the following environment variables before executing the commands. Please replace "/path/to/library" with the correct directory path you have chosen (do NOT append "libsodium.so" at the end of the path).

Make a test build by executing:

SODIUM_LIB_DIR="/path/to/library" SODIUM_SHARED=1 cargo ndk -t arm64-v8a build

e.g.

SODIUM_LIB_DIR="/Users/yourname/playground_app/android/app/src/main/jniLibs/arm64-v8a" SODIUM_SHARED=1 cargo ndk -t arm64-v8a build
Successful test of cross-compilation for Android arm64-v8a

Successful test of cross-compilation for Android arm64-v8a

Step 3: Update build.gradle

In the aforementioned test, we defined the variables SODIUM_LIB_DIR and SODIUM_SHARED within the command line. However, these definitions will not persist.

To ensure proper setup for usage in Flutter, you need to make adjustments to the android/app/build.gradle file, see below (once again, replace "/path/to/library" with the actual directory path you have selected).

... environment ANDROID_NDK_HOME: "$ANDROID_NDK" environment SODIUM_LIB_DIR: "/path/to/library" environment SODIUM_SHARED: 1 commandLine 'cargo', 'ndk', ...

Be aware: At this point, I am anticipating the Android setup!


In android/app/build.gradle, fix error:

Replace GradleException by FileNotFoundException

In android/app/build.gradle, add at the bottom:

[ Debug: null, Profile: '--release', Release: '--release' ].each { def taskPostfix = it.key def profileMode = it.value tasks.whenTaskAdded { task -> if (task.name == "javaPreCompile$taskPostfix") { task.dependsOn "cargoBuild$taskPostfix" } } tasks.register("cargoBuild$taskPostfix", Exec) { workingDir "../../rust" // <-- ATTENTION: CHECK THE CORRECT FOLDER!!! environment ANDROID_NDK_HOME: "$ANDROID_NDK" environment SODIUM_LIB_DIR: "/Users/yourname/playground_app/android/app/src/main/jniLibs/arm64-v8a" // <-- ATTENTION: CHECK THE CORRECT FOLDER!!! environment SODIUM_SHARED: 1 commandLine 'cargo', 'ndk', // the 2 ABIs below are used by real Android devices // '-t', 'armeabi-v7a', '-t', 'arm64-v8a', // the below 2 ABIs are usually used for Android simulators, // add or remove these ABIs as needed. // '-t', 'x86', // '-t', 'x86_64', '-o', '../android/app/src/main/jniLibs', 'build' if (profileMode != null) { args profileMode } } }