Files
Christopher Fahlin 01e26dd717 feat(openssl): upgrade from 3.0.8 to 3.1.2 (FIPS 140-3 Cert #4985)
- Bump OPENSSL_VERSION default from 3.0.8 to 3.1.2
- Update SHA256 hash for openssl-3.1.2.tar.gz
- Update all compliance checks to validate OpenSSL 3.1.x series
- Update docs: README, install.md, CLAUDE.md, test READMEs
- Previous 3.0.8 had only FIPS 140-2 (Cert #4282); 3.1.2 is the
  first OpenSSL with full FIPS 140-3 validation (Cert #4985,
  valid through March 2030)
2026-05-09 12:38:27 -07:00

5.9 KiB

FIPS SQLCipher — Integration Guide

FIPS 140-3 compliant SQLCipher with OpenSSL 3.1.2 for Android and iOS.


Android

AAR Contents

fips-sqlcipher.aar
├── jni/<abi>/
│   ├── libcrypto.so, libssl.so, libsqlcipher.so
│   ├── libfips.so          (FIPS provider — never strip)
│   └── libfips_jni.so      (JNI bridge for Kotlin helpers)
├── classes.jar
│   ├── FIPSSQLCipher        — one-call FIPS init
│   ├── FipsDatabase         — SupportSQLiteDatabase over FIPS SQLCipher
│   └── FipsSQLiteOpenHelperFactory — Room integration
├── assets/
│   ├── fips/<abi>/fipsmodule.cnf   (per-ABI HMAC fingerprints)
│   └── native/{include,src}/       (C sources for custom JNI)
└── AndroidManifest.xml

Step 1: Add the AAR

Copy fips-sqlcipher.aar to app/libs/:

// app/build.gradle.kts
dependencies {
    implementation(files("libs/fips-sqlcipher.aar"))
}

android {
    packaging.jniLibs {
        keepDebugSymbols += setOf("**/libfips.so")  // stripping breaks FIPS HMAC
    }
}

Step 2: Initialize at startup

// Application.onCreate() or Activity.onCreate() — before any crypto or DB operations
FIPSSQLCipher.init(this)

That's it. FIPSSQLCipher.init() handles:

  1. Extracts per-ABI fipsmodule.cnf from assets to internal storage
  2. Generates openssl.cnf with absolute .include paths (required because Android's AT_SECURE blocks OPENSSL_CONF env vars)
  3. Extracts fips.so from the APK (renames from libfips.so — OpenSSL convention)
  4. Loads native libraries in the correct order: libcrypto.solibfips_jni.so → FIPS init → libsqlcipher.so
  5. Calls OSSL_PROVIDER_set_default_search_path() + OSSL_LIB_CTX_load_config() + OSSL_PROVIDER_load("fips") programmatically

Step 3: Use with Room (optional)

// AppDatabase.kt
@Database(entities = [MyEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun myDao(): MyDao

    companion object {
        fun create(context: Context): AppDatabase =
            Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
                .openHelperFactory(FipsSQLiteOpenHelperFactory("your-encryption-key"))
                .build()
    }
}

FipsSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory and wraps all database operations through FIPS-validated SQLCipher encryption. Room handles migrations, DAOs, and queries normally — the encryption is transparent.

Direct database access (without Room)

val db = FipsDatabase.open("/path/to/db", "encryption-key")
db.execSQL("CREATE TABLE demo (id INTEGER PRIMARY KEY, msg TEXT)")
db.insert("demo", SQLiteDatabase.CONFLICT_NONE, contentValuesOf("msg" to "hello"))
val cursor = db.query("SELECT * FROM demo")
db.close()

Custom native JNI (advanced)

The AAR bundles C source files in assets/native/ for apps that need custom native integration. Extract and add to your CMakeLists:

add_library(your_jni SHARED
    fips_init.c
    fips_init_android.c
)
target_include_directories(your_jni PRIVATE path/to/include)
target_compile_definitions(your_jni PRIVATE SQLITE_HAS_CODEC)
target_link_libraries(your_jni PRIVATE crypto sqlcipher log)

SQLITE_HAS_CODEC is required to declare sqlite3_key() / sqlite3_rekey().

16 KB page alignment (Android 15+)

Add to your native CMakeLists for Google Play compatibility:

set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")

The AAR's bundled libraries are already 16 KB aligned.


iOS

Step 1: Add the XCFramework

Drag FIPSSQLCipher.xcframework into your Xcode project. Set Embed & Sign.

Build Phases > Link Binary With Libraries:

  • Security.framework
  • libz.tbd

Step 3: Disable stripping (critical)

Build Settings:

STRIP_INSTALLED_PRODUCT = NO
DEAD_CODE_STRIPPING = NO

Stripping invalidates the FIPS provider's HMAC integrity check.

Step 4: Copy fips.dylib into the app bundle

Add a Run Script build phase:

FIPS_FW="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FIPSSQLCipher.framework"
FIPS_DST="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fips"
mkdir -p "$FIPS_DST"
if [ -f "$FIPS_FW/fips.dylib" ]; then
    cp "$FIPS_FW/fips.dylib" "$FIPS_DST/"
elif [ -f "$FIPS_FW/Resources/fips/fips.dylib" ]; then
    cp "$FIPS_FW/Resources/fips/fips.dylib" "$FIPS_DST/"
fi

Step 5: Add C sources + bridging header

Add fips_init.c and fips_init_ios.c to your Xcode target.

Create a bridging header:

// BridgingHeader.h
#define SQLITE_HAS_CODEC 1
#import "fips_init.h"
#import <sqlite3.h>

Step 6: Initialize at launch

Option A — Use the Swift helper (recommended):

Copy src/ios/FIPSSQLCipher.swift into your project:

// App init or AppDelegate
try FIPSSQLCipher.initialize()

Option B — Call C directly:

let fipsDir = Bundle.main.resourcePath! + "/fips"
let rc = fips_init_ios(fipsDir)
guard rc == FIPS_INIT_OK else {
    fatalError("FIPS: \(String(cString: fips_init_status_str(rc)))")
}

Verifying FIPS at runtime

#include "fips_init.h"

assert(fips_provider_is_active());  // provider loaded?
assert(fips_self_test_rerun());     // POST/KAT still passing?

Android (Kotlin):

assert(FIPSSQLCipher.isActive)   // provider loaded and active?
assert(FIPSSQLCipher.selfTest()) // POST/KAT re-run passes?

What breaks FIPS

Action Effect Fix
Stripping libfips.so / fips.dylib Invalidates HMAC keepDebugSymbols / STRIP_INSTALLED_PRODUCT=NO
UPX / binary compression Mutates sections Never compress FIPS modules
Missing SQLITE_HAS_CODEC define sqlite3_key() undeclared Add to compile definitions

Versions

  • OpenSSL 3.1.2 (FIPS 140-3 validated, CMVP Cert #4985), SQLCipher v4.6.1
  • Android: API 24+, iOS: 17.0+