diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 330e69bdfe..a0468cd0ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,13 @@ jobs: update-liboqs: - true - false + include: + # iOS cross-compilation testing + - os: macos-latest + rust: stable + target: aarch64-apple-ios + update-liboqs: false + features: no_openssl,kems,sigs env: # 20 MiB stack RUST_MIN_STACK: 20971520 @@ -64,36 +71,60 @@ jobs: echo RUST_BACKTRACE=1 >> $GITHUB_ENV shell: bash + - name: Install iOS targets + if: matrix.target == 'aarch64-apple-ios' || matrix.target == 'x86_64-apple-ios' + run: | + rustup target add ${{ matrix.target }} + - uses: Swatinem/rust-cache@v2 - - name: Cargo build + - name: Cargo build (iOS) + if: matrix.target == 'aarch64-apple-ios' || matrix.target == 'x86_64-apple-ios' + run: | + # Build the high-level oqs crate without openssl (this will also build oqs-sys) + cargo build --target ${{ matrix.target }} --no-default-features --features kems,sigs,std,no_openssl --manifest-path oqs/Cargo.toml + + - name: Cargo build (regular) + if: matrix.target == null || matrix.target == '' run: cargo build - name: Cargo test + if: matrix.target == null || matrix.target == '' run: cargo test - name: Cargo test --no-default-features + if: matrix.target == null || matrix.target == '' run: cargo test --no-default-features - name: Cargo test --no-default-features --features serde,kems,sigs,std + if: matrix.target == null || matrix.target == '' run: cargo test --no-default-features --features serde,kems,sigs,std --manifest-path oqs/Cargo.toml - name: Cargo test --no-default-features --features serde,kems,sigs + if: matrix.target == null || matrix.target == '' run: cargo test --no-default-features --features serde,kems,sigs --manifest-path oqs/Cargo.toml - name: Cargo test --no-default-features --features non_portable,kems,sigs,std + if: matrix.target == null || matrix.target == '' run: cargo test --no-default-features --features non_portable,kems,sigs,std --manifest-path oqs/Cargo.toml # skip windows, because the default image doesn't include several of the # system dependencies (e.g. Perl) required for the openssl-sys/vendored - name: Cargo test --features vendored_openssl - if: matrix.os != 'windows-latest' + if: (matrix.target == null || matrix.target == '') && matrix.os != 'windows-latest' run: cargo test --features vendored_openssl --manifest-path oqs/Cargo.toml + # Test the no_openssl feature on all platforms + - name: Cargo test oqs-sys --features no_openssl + if: matrix.target == null || matrix.target == '' + run: cargo test --features no_openssl --manifest-path oqs-sys/Cargo.toml + - name: Cargo fmt + if: matrix.target == null || matrix.target == '' run: cargo fmt --all -- --check - name: Cargo clippy + if: matrix.target == null || matrix.target == '' run: cargo clippy # vim: set ft=yaml ts=2 sw=2 tw=0 et : diff --git a/README.md b/README.md index 23323bf341..37ef949b5a 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,46 @@ features = ["sigs", "kems"] You will probably want to change the random-number generator through the [`OQS_RAND` API][] offered by `oqs-sys`. +## OpenSSL Support + +By default, `liboqs` is built with OpenSSL support for symmetric cryptography operations. This can be controlled through features: + +- Default behavior: OpenSSL enabled (requires system OpenSSL or vendored OpenSSL) +- `vendored_openssl`: Use bundled OpenSSL instead of system OpenSSL +- `no_openssl`: Force disable OpenSSL on all platforms + +### Platform-specific behavior + +- **iOS**: OpenSSL is automatically disabled to avoid build issues. The crate uses iOS Security.framework instead. +- **Other platforms**: OpenSSL enabled by default but can be overridden. + +### Environment variable control + +You can override OpenSSL configuration using the `OQS_USE_OPENSSL` environment variable: + +```bash +# Force disable OpenSSL +OQS_USE_OPENSSL=OFF cargo build + +# Force enable OpenSSL +OQS_USE_OPENSSL=ON cargo build +``` + +### Examples + +```toml +# iOS-compatible build without OpenSSL +[dependencies.oqs] +version = "*" +default-features = false +features = ["no_openssl", "sigs", "kems"] + +# Use vendored OpenSSL (recommended for Windows) +[dependencies.oqs] +version = "*" +features = ["vendored_openssl"] +``` + [`OQS_RAND` API]: https://open-quantum-safe.github.io/liboqs-rust/oqs_sys/rand/index.html ## `non_portable` feature diff --git a/oqs-sys/Cargo.toml b/oqs-sys/Cargo.toml index c3240be8e2..9abd8483bc 100644 --- a/oqs-sys/Cargo.toml +++ b/oqs-sys/Cargo.toml @@ -28,6 +28,7 @@ build-deps = "0.1" default = ["openssl", "kems", "sigs"] openssl = [] vendored_openssl = ["openssl", "vendored", "dep:openssl-sys"] +no_openssl = [] # Force disable OpenSSL on all platforms docs = [] non_portable = [] vendored = [] diff --git a/oqs-sys/README.md b/oqs-sys/README.md index 46d58940ce..c6bab3c44d 100644 --- a/oqs-sys/README.md +++ b/oqs-sys/README.md @@ -9,6 +9,8 @@ This crate provides the unsafe `ffi` bindings to [liboqs][]. * `vendored` (default): Compile the included version of liboqs instead of linking to the system version. * `openssl` (default): Compile with OpenSSL features (mostly symmetric cryptography) +* `vendored_openssl`: Use vendored OpenSSL (includes `openssl` feature) +* `no_openssl`: Force disable OpenSSL on all platforms * `non_portable`: Don't build a portable library. * `kems` (default): Compile with all KEMs enabled * `bike` (only on non-Windows) @@ -27,5 +29,50 @@ This crate provides the unsafe `ffi` bindings to [liboqs][]. * `sphincs`: SPHINCS+ * `uov` +## Platform-Specific Behavior + +### iOS Support +iOS builds automatically disable OpenSSL by default to avoid compilation issues. The crate will: +- Automatically set `OQS_USE_OPENSSL=OFF` when building for iOS targets +- Link against iOS system frameworks (`Security.framework`) for cryptographic functions +- Use system random number generation instead of OpenSSL + +### Other Platforms +- **macOS/Linux**: OpenSSL enabled by default (can be overridden) +- **Windows**: OpenSSL enabled by default (vendored OpenSSL recommended) +- **Android**: Follows same behavior as Linux + +## Environment Variables + +You can override the OpenSSL configuration using environment variables: + +- `OQS_USE_OPENSSL=OFF` or `OQS_USE_OPENSSL=NO`: Force disable OpenSSL +- `OQS_USE_OPENSSL=ON` or `OQS_USE_OPENSSL=YES`: Force enable OpenSSL + +These environment variables take precedence over feature flags and platform defaults. + +## Examples + +### Building for iOS without OpenSSL +```bash +# iOS builds automatically disable OpenSSL +cargo build --target aarch64-apple-ios + +# Or explicitly disable OpenSSL +cargo build --target aarch64-apple-ios --features no_openssl +``` + +### Building without OpenSSL on any platform +```bash +cargo build --features no_openssl +# or +OQS_USE_OPENSSL=OFF cargo build +``` + +### Building with vendored OpenSSL +```bash +cargo build --features vendored_openssl +``` + [oqs]: https://openquantumsafe.org [liboqs]: https://github.com/Open-Quantum-Safe/liboqs diff --git a/oqs-sys/build.rs b/oqs-sys/build.rs index d210253f93..d3709d9cea 100644 --- a/oqs-sys/build.rs +++ b/oqs-sys/build.rs @@ -1,3 +1,4 @@ +use std::env; use std::path::{Path, PathBuf}; fn generate_bindings(includedir: &Path, headerfile: &str, allow_filter: &str, block_filter: &str) { @@ -38,6 +39,81 @@ fn generate_bindings(includedir: &Path, headerfile: &str, allow_filter: &str, bl .expect("Couldn't write bindings!"); } +fn configure_openssl(config: &mut cmake::Config) { + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + + // Check explicit environment variable first (highest priority) + if let Ok(use_openssl) = env::var("OQS_USE_OPENSSL") { + match use_openssl.to_uppercase().as_str() { + "OFF" | "NO" | "0" | "FALSE" => { + println!("cargo:warning=OpenSSL explicitly disabled via OQS_USE_OPENSSL environment variable"); + config.define("OQS_USE_OPENSSL", "OFF"); + return; + } + "ON" | "YES" | "1" | "TRUE" => { + println!("cargo:warning=OpenSSL explicitly enabled via OQS_USE_OPENSSL environment variable"); + config.define("OQS_USE_OPENSSL", "ON"); + setup_openssl_linking(); + return; + } + _ => { + println!( + "cargo:warning=Invalid OQS_USE_OPENSSL value '{}', ignoring", + use_openssl + ); + } + } + } + + // Platform-specific and feature-based defaults + if target_os == "ios" { + println!("cargo:warning=iOS target detected - disabling OpenSSL by default"); + config.define("OQS_USE_OPENSSL", "OFF"); + // Link against iOS system frameworks for system random generation + println!("cargo:rustc-link-lib=framework=Security"); + } else if cfg!(feature = "no_openssl") { + println!("cargo:warning=no_openssl feature enabled - disabling OpenSSL"); + config.define("OQS_USE_OPENSSL", "OFF"); + } else if cfg!(any(feature = "openssl", feature = "vendored_openssl")) { + config.define("OQS_USE_OPENSSL", "ON"); + setup_openssl_linking(); + } else { + config.define("OQS_USE_OPENSSL", "OFF"); + } +} + +fn setup_openssl_linking() { + // Link the openssl libcrypto + if cfg!(windows) { + // Windows doesn't prefix with lib + println!("cargo:rustc-link-lib=libcrypto"); + } else { + println!("cargo:rustc-link-lib=crypto"); + } +} + +fn setup_openssl_paths(config: &mut cmake::Config) { + // Configure vendored OpenSSL paths if needed + if cfg!(feature = "vendored_openssl") { + // DEP_OPENSSL_ROOT is set by openssl-sys if a vendored build was used. + // We point CMake towards this so that the vendored openssl is preferred + // over the system openssl. + if let Ok(vendored_openssl_root) = env::var("DEP_OPENSSL_ROOT") { + config.define("OPENSSL_ROOT_DIR", vendored_openssl_root); + } else { + println!("cargo:warning=vendored_openssl feature enabled but DEP_OPENSSL_ROOT not set"); + } + } else if cfg!(feature = "openssl") { + println!("cargo:rerun-if-env-changed=OPENSSL_ROOT_DIR"); + if let Ok(dir) = env::var("OPENSSL_ROOT_DIR") { + let dir = Path::new(&dir).join("lib"); + println!("cargo:rustc-link-search={}", dir.display()); + } else if cfg!(windows) || cfg!(target_os = "macos") { + println!("cargo:warning=You may need to specify OPENSSL_ROOT_DIR or disable the default `openssl` feature."); + } + } +} + fn build_from_source() -> PathBuf { let mut config = cmake::Config::new("liboqs"); config.profile("Release"); @@ -89,36 +165,11 @@ fn build_from_source() -> PathBuf { config.define("CMAKE_SYSTEM_VERSION", "10.0"); } - // link the openssl libcrypto - if cfg!(any(feature = "openssl", feature = "vendored_openssl")) { - config.define("OQS_USE_OPENSSL", "Yes"); - if cfg!(windows) { - // Windows doesn't prefix with lib - println!("cargo:rustc-link-lib=libcrypto"); - } else { - println!("cargo:rustc-link-lib=crypto"); - } - } else { - config.define("OQS_USE_OPENSSL", "No"); - } + // Configure OpenSSL based on platform and features + configure_openssl(&mut config); - // let the linker know where to search for openssl libcrypto - if cfg!(feature = "vendored_openssl") { - // DEP_OPENSSL_ROOT is set by openssl-sys if a vendored build was used. - // We point CMake towards this so that the vendored openssl is preferred - // over the system openssl. - let vendored_openssl_root = std::env::var("DEP_OPENSSL_ROOT") - .expect("The `vendored_openssl` feature was enabled, but DEP_OPENSSL_ROOT was not set"); - config.define("OPENSSL_ROOT_DIR", vendored_openssl_root); - } else if cfg!(feature = "openssl") { - println!("cargo:rerun-if-env-changed=OPENSSL_ROOT_DIR"); - if let Ok(dir) = std::env::var("OPENSSL_ROOT_DIR") { - let dir = Path::new(&dir).join("lib"); - println!("cargo:rustc-link-search={}", dir.display()); - } else if cfg!(target_os = "windows") || cfg!(target_os = "macos") { - println!("cargo:warning=You may need to specify OPENSSL_ROOT_DIR or disable the default `openssl` feature."); - } - } + // Configure vendored OpenSSL paths if needed + setup_openssl_paths(&mut config); let permit_unsupported = "OQS_PERMIT_UNSUPPORTED_ARCHITECTURE"; if let Ok(str) = std::env::var(permit_unsupported) { diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml index 66ae692611..cda22027d7 100644 --- a/oqs/Cargo.toml +++ b/oqs/Cargo.toml @@ -25,6 +25,7 @@ std = [] non_portable = ["oqs-sys/non_portable"] vendored = ["oqs-sys/vendored"] vendored_openssl = ["oqs-sys/vendored_openssl"] +no_openssl = ["oqs-sys/no_openssl"] # algorithms: KEMs kems = ["oqs-sys/kems", "classic_mceliece", "frodokem", "hqc", "kyber", "ml_kem", "ntruprime"]