HMAC fingerprint generated on-device via openssl fipsinstall against the 3.1.2 FIPS provider binary. All 29 KATs passed.
fips-sqlcipher
A reproducible cross-compile pipeline that produces FIPS 140-3 compliant OpenSSL 3.1.x + SQLCipher 4.6.x for Android and iOS. Output:
- Android: Self-contained AAR with
arm64-v8a+x86_64shared libs, FIPS provider module, and runtime config. - iOS: Universal XCFramework (device
arm64+ simulatorarm64/x86_64) with static libs, module maps for Swift/Objective-C++, and FIPS config.
Sources are fetched automatically. CMake's ExternalProject_Add downloads the
OpenSSL tarball (sha256-pinned) and git clone --depth=1's SQLCipher at the
configured tag. You do NOT need to vendor either source tree.
Quick Start
# 1. Install mise (one-time)
curl https://mise.run | sh
# 2. Bootstrap tooling
mise trust && mise install
# 3. Build everything
export ANDROID_NDK_ROOT="$HOME/Library/Android/sdk/ndk/26.3.11579264"
./build.sh all
# 4. Package
./build.sh package
Environment Setup (Mise)
This repo uses mise to pin deterministic versions of
all host build tools. The .mise.toml declares:
| Tool | Version | Purpose |
|---|---|---|
| cmake | 3.29 | Build system orchestration |
| ninja | 1.12.1 | iOS cmake generator (faster builds) |
| perl | 5.40.2 | OpenSSL Configure script |
| java | temurin-17 | AAR packaging (Gradle/AGP 8.x) |
| gradle | 8.10.2 | AAR packaging + test app |
Not managed by mise (install separately):
- Android NDK r26+ (via
sdkmanager --install "ndk;26.3.11579264") - Xcode + Command Line Tools (for iOS builds)
Installation
# Install mise
curl https://mise.run | sh
# Add to shell (if not already)
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
source ~/.zshrc
# From repo root:
mise trust
mise install
# Verify
cmake --version # 3.29.x
ninja --version # 1.12.1
perl --version # 5.40.x
Android NDK
# Via sdkmanager
sdkmanager --install "ndk;26.3.11579264"
export ANDROID_NDK_ROOT="$HOME/Library/Android/sdk/ndk/26.3.11579264"
# Or via Android Studio:
# Settings -> Languages & Frameworks -> Android SDK -> SDK Tools -> NDK
Build Commands
# --- Android ---
./build.sh android # arm64-v8a (default)
ANDROID_ABI=x86_64 ./build.sh android # emulator
./build.sh all # all platforms + arches
# --- iOS ---
./build.sh ios # device arm64
./build.sh ios-simulator # simulator arm64 + x86_64
./build.sh ios-all # device + simulator
# --- Packaging ---
./build.sh package-aar # -> dist/fips-sqlcipher.aar
./build.sh package-xcframework # -> dist/FIPSSQLCipher.xcframework
./build.sh package # both
Configuration Variables
| Variable | Default | Description |
|---|---|---|
ANDROID_NDK_ROOT |
(required) | Path to NDK r26+ |
ANDROID_ABI |
arm64-v8a |
arm64-v8a, armeabi-v7a, x86_64, x86 |
ANDROID_PLATFORM |
android-24 |
Minimum Android API level |
OPENSSL_VERSION |
3.1.2 |
FIPS 140-3 Cert #4985 baseline |
SQLCIPHER_VERSION |
v4.6.1 |
SQLCipher git tag |
IOS_DEPLOYMENT_TARGET |
15.0 |
Minimum iOS version |
OPENSSL_HOST_BIN |
(unset) | Host openssl for in-tree fipsinstall |
BUILD_TYPE |
Release |
CMake build type |
JOBS |
auto | Parallel compile jobs |
Artifacts
Android (dist/<abi>/)
| Path | Purpose |
|---|---|
lib/libcrypto.so |
OpenSSL crypto runtime |
lib/libssl.so |
OpenSSL TLS runtime |
lib/libsqlcipher.so |
SQLCipher (FIPS probe linked in) |
fips/libfips.so |
FIPS provider (HMAC-protected) |
fips/openssl.cnf |
Runtime config activating FIPS |
fips/fipsmodule.cnf |
HMAC manifest (generated on-device) |
fips/verify_integrity.sh |
CI integrity gate |
fips/fips_integrity.gradle |
AGP no-strip guard |
iOS (dist/ios-<arch>/)
| Path | Purpose |
|---|---|
lib/libcrypto.a |
OpenSSL (static, FIPS-enabled) |
lib/libssl.a |
OpenSSL TLS (static) |
lib/libsqlcipher.a |
SQLCipher (static, FIPS probe) |
fips/fips.a |
FIPS provider (static, incore HMAC) |
fips/fips.a.sha256 |
Baseline integrity hash |
Packaged
| Path | Format |
|---|---|
dist/fips-sqlcipher.aar |
Android AAR |
dist/FIPSSQLCipher.xcframework |
XCFramework |
Android Integration (AAR)
Gradle Setup
// app/build.gradle.kts
dependencies {
implementation(files("libs/fips-sqlcipher.aar"))
}
android {
packaging {
jniLibs {
useLegacyPackaging = false
keepDebugSymbols += setOf("**/libfips.so")
}
}
}
Runtime Initialization
class FipsInitializer : Initializer<Unit> {
override fun create(context: Context) {
val fipsDir = File(context.filesDir, "fips").apply { mkdirs() }
listOf("openssl.cnf", "fipsmodule.cnf").forEach { name ->
val dst = File(fipsDir, name)
if (!dst.exists()) {
context.assets.open("fips/$name").use { src ->
dst.outputStream().use { src.copyTo(it) }
}
}
}
Env.setenv("OPENSSL_CONF", File(fipsDir, "openssl.cnf").absolutePath)
Env.setenv("FIPSMODULE_CNF", File(fipsDir, "fipsmodule.cnf").absolutePath)
Env.setenv("OPENSSL_MODULES", context.applicationInfo.nativeLibraryDir)
System.loadLibrary("crypto")
System.loadLibrary("fips")
System.loadLibrary("sqlcipher")
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
iOS Integration (XCFramework)
Setup
- Drag
FIPSSQLCipher.xcframeworkinto your Xcode project (or add via SPM binary target). - In Build Settings, add to "Other Linker Flags":
-lz - Link
Security.framework. - Copy
Resources/fips/openssl.cnfandfipsmodule.cnfinto your app bundle.
Swift Usage
import FIPSSQLCipher
// Set env BEFORE any OpenSSL/SQLCipher call
let fipsDir = Bundle.main.path(forResource: "fips", ofType: nil)!
setenv("OPENSSL_CONF", "\(fipsDir)/openssl.cnf", 1)
setenv("FIPSMODULE_CNF", "\(fipsDir)/fipsmodule.cnf", 1)
Xcode Build Settings (FIPS Integrity)
Apply dist/ios-arm64/fips/fips_integrity.xcconfig or set manually:
STRIP_INSTALLED_PRODUCT = NO
COPY_PHASE_STRIP = NO
DEPLOYMENT_POSTPROCESSING = NO
ENABLE_BITCODE = NO
C++ Verification
The include/ directory ships a C++ verification class. Include path:
-I<repo>/include (or it's bundled in the XCFramework headers).
#include "fips_verify.hpp"
// After opening and keying a database:
auto result = fips::Verifier::check_all(db);
assert(result.provider_active); // OSSL_PROVIDER_available(NULL, "fips")
assert(result.self_test_passed); // POST KATs passed
assert(result.cipher_fips_status); // PRAGMA cipher_fips_status == 1
The fips_sqlcipher.h header wraps all OpenSSL and SQLCipher includes in
extern "C" for safe C++ interop without ODR violations:
#include "fips_sqlcipher.h" // Safe from any C++ TU
C++ flag compatibility:
- Your C++ code can freely use
-frtti,-fexceptions, or disable them. - The FIPS incore HMAC covers only the provider's
.text/.rodatasections, not your application code. C++ flags in your TUs cannot invalidate it. - On Android, use the NDK's shared
libc++(default) to avoid ODR violations when multiple shared libs link against the STL.
Generating fipsmodule.cnf
The FIPS manifest must be produced on a matching CPU architecture.
Android (on-device)
adb push dist/arm64-v8a/fips /data/local/tmp/fips
adb push dist/arm64-v8a/bin/openssl /data/local/tmp/fips/openssl
adb shell sh /data/local/tmp/fips/run_fipsinstall_on_device.sh
adb pull /data/local/tmp/fips/fipsmodule.cnf dist/arm64-v8a/fips/
Android (host shortcut, x86_64 only)
OPENSSL_HOST_BIN=/opt/homebrew/bin/openssl ANDROID_ABI=x86_64 ./build.sh android
iOS
On macOS arm64, the device slice (ios-arm64) can run fipsinstall via
Rosetta or on a connected device. For CI, use a matching-arch runner.
Integrity Verification (CI)
# Android
dist/arm64-v8a/fips/verify_integrity.sh dist/arm64-v8a/fips/libfips.so
# After APK packaging
unzip -p app-release.apk lib/arm64-v8a/libfips.so > /tmp/libfips.so
dist/arm64-v8a/fips/verify_integrity.sh /tmp/libfips.so
shasum -a 256 /tmp/libfips.so | diff - dist/arm64-v8a/fips/libfips.so.sha256
# iOS (static module baseline)
shasum -a 256 dist/ios-arm64/fips/fips.a | diff - dist/ios-arm64/fips/fips.a.sha256
FIPS Compliance Notes
-
Module boundary:
libfips.so(Android) /fips.a(iOS) is the sole FIPS-validated cryptographic boundary. Everything else is a consumer. -
Power-On Self-Tests (POST): Required by FIPS 140-3. Run at first
OSSL_PROVIDER_load(NULL, "fips"). Failure aborts before any crypto API returns. -
Post-build mutation: Strip, codesign with bitcode rewrite, ProGuard native-symbol manipulation, or APK compression invalidates the HMAC. The shipped
verify_integrity.sh(Android) and.sha256baselines (iOS) are your CI gates. -
iOS static linking: Since iOS prohibits
dlopenof arbitrary dylibs in production apps, the FIPS provider is statically linked. The incore integrity mechanism embeds the HMAC verification into the binary itself. -
Reproducibility: OpenSSL tarball SHA256 is pinned in
cmake/BuildOpenSSL.cmake. SQLCipher git tag is pinned incmake/BuildSQLCipher.cmake. Do not bump without updating hashes and re-running integrity baselines.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
FATAL: missing required host tools: cmake |
mise not bootstrapped | mise trust && mise install |
FATAL: export ANDROID_NDK_ROOT=... |
NDK not exported | See Android NDK |
| Ninja not found (iOS build) | mise not activated | eval "$(mise activate zsh)" |
xcrun: error: SDK not found |
Xcode CLI tools missing | xcode-select --install |
App aborts: FIPS provider not active |
OPENSSL_CONF not set before first call |
Move env setup earlier |
| FIPS audit fails after release packaging | AGP stripped libfips.so |
Apply fips_integrity.gradle |
XCFramework link error: _EVP_* undefined |
Missing -lz or Security.framework |
Add to Other Linker Flags |
Project Structure
.mise.toml # Pinned build tools
build.sh # Orchestration (android/ios/package)
CMakeLists.txt # Android entry point
CMakeLists_iOS.cmake # iOS entry point
cmake/
BuildOpenSSL.cmake # Android OpenSSL (shared, enable-fips)
BuildOpenSSL_iOS.cmake # iOS OpenSSL (static, enable-fips)
BuildSQLCipher.cmake # Android SQLCipher
BuildSQLCipher_iOS.cmake # iOS SQLCipher
PreserveFipsIntegrity.cmake # Strip kill-switch, integrity guards
include/
fips_sqlcipher.h # C/C++ interop header (extern "C")
fips_verify.hpp # C++ FIPS verification class
packaging/
package_aar.sh # Android AAR assembly
package_xcframework.sh # iOS XCFramework assembly
tests/android-fips/ # Runtime compliance test app
dist/ # Build output (gitignored)