Skip to content

Enable encryption for mobile targets #2070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

winghouchan
Copy link

Why?

#1423 introduced support for React Native however encryption was disabled due to the difficulty in getting successful builds with encryption enabled:

  • Encryption seems to be compiling sqlcipher (?) via cmake, which also includes OpenSSL flags, the problem the compilation step is hard to integrate with Android (and that's why I had to disable it). Someone else that knows the internals of libsql should give it a try.

#1423 (comment)

With encryption disabled, libSQL on React Native does not have feature parity with other platforms.

What?

This PR enables encryption for builds against mobile targets. However, while the encryption feature is enabled, there is additional work to get the encryption feature fully complete:

P.S. if you do turn on encryption, it will require a modification to the bindings to send the encryption key from JS, so, open a PR when the time comes and I will take a look.

#1423 (comment)

How?

  • The encryption feature of the libSQL dependency in the C bindings package has been enabled for all targets.
  • Build scripts and configurations in the libSQL FFI package have been updated so that libSQL products for each target and the OP SQLite Example App build successfully; and the tests against the OP SQLite Example App pass.

I'm not actually familiar with Android/iOS/C/Rust build systems, or libSQL / OP SQLite internals so configuration decisions were based on a feedback loop of running make <android|ios> in bindings/c and making updates until builds in libSQL and the OP SQLite Example App were successful. I'm unsure if there's anything else to check and will have to defer to Turso team members and @ospfranco for their knowledge.

@@ -3,6 +3,7 @@ CFLAGS := -Iinclude
LDFLAGS := -lm
ARCHS_IOS = x86_64-apple-ios aarch64-apple-ios aarch64-apple-ios-sim
ARCHS_ANDROID = aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android
ANDROID_PLATFORM := 31
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This value comes from the value already passed to the platform option when running cargo ndk. It has been pulled out and assigned here so that it can also be passed as an environment variable for libSQL FFI's build script which uses it to set the ANDROID_PLATFORM variable when running CMake.

@@ -452,7 +452,7 @@ fn build_multiple_ciphers(target: &str, out_path: &Path) {
let bundled_dir = format!("{out_dir}/sqlite3mc");
let sqlite3mc_build_dir = env::current_dir().unwrap().join(out_dir).join("sqlite3mc");

let mut cmake_opts: Vec<&str> = vec![];
let mut cmake_opts: Vec<String> = vec![];
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

cmake_opts changed from Vec<&str> to Vec<String> to allow for interpolating options values into a string with their option names.

Comment on lines +465 to +473
let ndk_cmake_toolchain_path = env::var("CARGO_NDK_CMAKE_TOOLCHAIN_PATH").ok();
let toolchain_path = ndk_cmake_toolchain_path
.clone()
.map(PathBuf::from)
.unwrap_or_else(|| sqlite3mc_build_dir.join("toolchain.cmake"));
let cmake_toolchain_opt = ndk_cmake_toolchain_path
.clone()
.map(|path| format!("-DCMAKE_TOOLCHAIN_FILE={}", path))
.unwrap_or_else(|| "-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake".to_string());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Use the Android NDK toolchain if available (determined by the value of the CARGO_NDK_CMAKE_TOOLCHAIN_PATH). This seems to be necessary for a successful build for Android.

Comment on lines +518 to +532
cmake_opts.push("-DCMAKE_BUILD_TYPE=Release".to_string());
cmake_opts.push("-DSQLITE3MC_STATIC=ON".to_string());
cmake_opts.push("-DCODEC_TYPE=AES256".to_string());
cmake_opts.push("-DSQLITE3MC_BUILD_SHELL=OFF".to_string());
cmake_opts.push("-DSQLITE_SHELL_IS_UTF8=OFF".to_string());
cmake_opts.push("-DSQLITE_USER_AUTHENTICATION=OFF".to_string());
cmake_opts.push("-DSQLITE_SECURE_DELETE=OFF".to_string());
cmake_opts.push("-DSQLITE_ENABLE_COLUMN_METADATA=ON".to_string());
cmake_opts.push("-DSQLITE_USE_URI=ON".to_string());
cmake_opts.push("-DCMAKE_POSITION_INDEPENDENT_CODE=ON".to_string());

if target.contains("musl") {
cmake_opts.push("-DCMAKE_C_FLAGS=\"-U_FORTIFY_SOURCE\" -D_FILE_OFFSET_BITS=32");
cmake_opts.push("-DCMAKE_CXX_FLAGS=\"-U_FORTIFY_SOURCE\" -D_FILE_OFFSET_BITS=32");
cmake_opts.push("-DCMAKE_C_FLAGS=\"-U_FORTIFY_SOURCE\" -D_FILE_OFFSET_BITS=32".to_string());
cmake_opts
.push("-DCMAKE_CXX_FLAGS=\"-U_FORTIFY_SOURCE\" -D_FILE_OFFSET_BITS=32".to_string());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Changing cmake_opts from Vec<&str> to Vec<String> necessitated calling to_string on these string literals.

This seems very verbose but I'm not familiar with Rust so please let me know if there is a better way.

Comment on lines +535 to +549
if target.contains("android") {
let android_abi = match target {
"aarch64-linux-android" => "arm64-v8a",
"armv7-linux-androideabi" => "armeabi-v7a",
"i686-linux-android" => "x86",
"x86_64-linux-android" => "x86_64",
_ => panic!("Unsupported Android target: {}", target),
};
let android_platform = std::env::var("ANDROID_PLATFORM")
.expect("ANDROID_PLATFORM environment variable must be set");

cmake_opts.push(cmake_toolchain_opt);
cmake_opts.push(format!("-DANDROID_ABI={}", android_abi));
cmake_opts.push(format!("-DANDROID_PLATFORM=android-{}", android_platform));
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This is probably the critical change to build for Android. It configures CMake in the following ways:

  • Sets CMAKE_TOOLCHAIN_FILE to the path to the Android NDK toolchain file (available via the CARGO_NDK_CMAKE_TOOLCHAIN_PATH environment variable).
  • Sets the ANDROID_ABI variable according to the defined target.
  • Sets the ANDROID_PLATFORM variable to the platform level specified in the C bindings package manifest, via an environment variable named ANDROID_PLATFORM.

Comment on lines +551 to +569
if target.contains("ios") {
cmake_opts.push("-DCMAKE_SYSTEM_NAME=iOS".to_string());

let (arch, processor, sysroot) = if target.contains("x86_64") {
("x86_64", "x86_64", "iphonesimulator")
} else if target.contains("aarch64") {
let sysroot = if target.contains("sim") {
"iphonesimulator"
} else {
"iphoneos"
};
("arm64", "arm64", sysroot)
} else {
panic!("Unsupported iOS target: {}", target);
};

cmake_opts.push(format!("-DCMAKE_OSX_ARCHITECTURES={}", arch));
cmake_opts.push(format!("-DCMAKE_SYSTEM_PROCESSOR={}", processor));
cmake_opts.push(format!("-DCMAKE_OSX_SYSROOT={}", sysroot));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This is probably the critical change to build for iOS. It configures CMake in the following ways:

  • Sets the CMAKE_OSX_ARCHITECTURES variable to the architecture according to the target.
  • Sets the CMAKE_SYSTEM_PROCESSOR variable to the processor according to the target.
  • Sets the CMAKE_OSX_SYSROOT variable to iphonesimulator if the target is a simulator or the x86_64 architecture (there are no physical x86_64 iPhones) or iphoneos if the target is a physical device.

@winghouchan
Copy link
Author

@ospfranco: given my unfamiliarity, would you mind expanding on this comment on #1423 please?

P.S. if you do turn on encryption, it will require a modification to the bindings to send the encryption key from JS, so, open a PR when the time comes and I will take a look.

@winghouchan
Copy link
Author

Related: #1384

@winghouchan
Copy link
Author

I see there's some logic for cross compilation:

libsql/libsql-ffi/build.rs

Lines 457 to 509 in c6ebbe1

let target_postfix = target.to_string().replace("-", "_");
let cross_cc_var_name = format!("CC_{}", target_postfix);
println!("cargo:warning=CC_var_name={}", cross_cc_var_name);
let cross_cc = env::var(&cross_cc_var_name).ok();
let cross_cxx_var_name = format!("CXX_{}", target_postfix);
let cross_cxx = env::var(&cross_cxx_var_name).ok();
let toolchain_path = sqlite3mc_build_dir.join("toolchain.cmake");
let cmake_toolchain_opt = "-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake".to_string();
let mut toolchain_file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(toolchain_path.clone())
.unwrap();
if let Some(ref cc) = cross_cc {
let system_name = if cc.contains("linux") {
"Linux"
} else if cc.contains("darwin") {
"Darwin"
} else if cc.contains("w64") {
"Windows"
} else {
panic!("Unsupported cross target {}", cc)
};
let system_processor = if cc.contains("x86_64") {
"x86_64"
} else if cc.contains("aarch64") {
"arm64"
} else if cc.contains("arm") {
"arm"
} else {
panic!("Unsupported cross target {}", cc)
};
cmake_opts.push(&cmake_toolchain_opt);
writeln!(toolchain_file, "set(CMAKE_SYSTEM_NAME \"{}\")", system_name).unwrap();
writeln!(
toolchain_file,
"set(CMAKE_SYSTEM_PROCESSOR \"{}\")",
system_processor
)
.unwrap();
writeln!(toolchain_file, "set(CMAKE_C_COMPILER {})", cc).unwrap();
}
if let Some(cxx) = cross_cxx {
writeln!(toolchain_file, "set(CMAKE_CXX_COMPILER {})", cxx).unwrap();
}

Would it be better to somehow work within this, instead of the changes currently proposed (79ff682)?

@penberg
Copy link
Collaborator

penberg commented May 20, 2025

@levydsa do you remember why we disabled encryption for mobile targets?

@levydsa
Copy link
Contributor

levydsa commented May 20, 2025

@penberg, because it was hard to cross-compile sqlite3mc to mobile targets. But apparently, its possible with a few tweaks.

@tyrauber
Copy link

Thank you @winghouchan, for picking this up. libsql is fantastic on mobile devices (thanks to op-sqlite), but the lack of database encryption is definitely problematic. I would love to see this get merged in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants