Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions .github/workflows/aws-lc-rs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,181 @@ jobs:
if: runner.os != 'Windows'
working-directory: ./aws-lc-rs
run: ./scripts/build/collect_build_src.sh

# CMake Rust bindings generation tests
cmake-rust-bindings:
if: github.repository_owner == 'aws'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
name: linux
- os: ubuntu-latest
name: linux-prefix
prefix: AWSLC_PREFIX
- os: macos-latest
name: macos
- os: macos-latest
name: macos-prefix
prefix: AWSLC_PREFIX
- os: windows-latest
name: windows
- os: ubuntu-latest
name: linux-no-ssl
build_libssl: OFF
runs-on: ${{ matrix.os }}
name: cmake-rust-bindings (${{ matrix.name }})
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: 'rustfmt'
- uses: ilammy/setup-nasm@v1
if: runner.os == 'Windows'
- uses: actions/setup-go@v4
with:
go-version: '>=1.20'
- name: Install bindgen-cli
run: cargo install --force --locked bindgen-cli

# Prefix builds need a non-prefixed build first to collect symbols
- name: Generate prefix symbols file
if: matrix.prefix
shell: bash
run: |
cmake -B build-noprefix -DBUILD_TESTING=OFF
cmake --build build-noprefix
go run ./util/read_symbols.go build-noprefix/crypto/libcrypto.a > symbols.txt
go run ./util/read_symbols.go build-noprefix/ssl/libssl.a >> symbols.txt
echo "Collected $(wc -l < symbols.txt) symbols"

- name: Configure with Rust bindings generation
shell: bash
run: |
cmake_args="-DGENERATE_RUST_BINDINGS=ON -DBUILD_TESTING=OFF"
if [ -n "${{ matrix.prefix }}" ]; then
cmake_args="$cmake_args -DBORINGSSL_PREFIX=${{ matrix.prefix }}"
cmake_args="$cmake_args -DBORINGSSL_PREFIX_SYMBOLS=$(pwd)/symbols.txt"
fi
if [ "${{ matrix.build_libssl }}" = "OFF" ]; then
cmake_args="$cmake_args -DBUILD_LIBSSL=OFF"
fi
cmake -B build $cmake_args

- name: Build libraries
run: cmake --build build --config Release

- name: Generate bindings
run: cmake --build build --target rust_bindings --config Release --verbose

- name: Verify bindings file exists
shell: bash
run: |
if [ ! -f build/rust/aws_lc_bindings.rs ]; then
echo "ERROR: Rust bindings file was not generated"
exit 1
fi
echo "Generated bindings file size: $(wc -c < build/rust/aws_lc_bindings.rs) bytes"
echo "Generated bindings line count: $(wc -l < build/rust/aws_lc_bindings.rs) lines"

- name: Verify bindings content
shell: bash
run: |
# Verify SSL bindings based on BUILD_LIBSSL setting (defaults to ON)
if [ "${{ matrix.build_libssl }}" != "OFF" ]; then
if ! grep -q "pub fn SSL_new" build/rust/aws_lc_bindings.rs; then
echo "ERROR: Expected SSL_new function not found"
exit 1
fi
else
if grep -q "pub fn SSL_new" build/rust/aws_lc_bindings.rs; then
echo "ERROR: Unexpected SSL_new found in BUILD_LIBSSL=OFF build"
exit 1
fi
echo "Confirmed: SSL bindings correctly excluded"
fi
if [ -n "${{ matrix.prefix }}" ]; then
# Prefix builds: link_name attributes should contain the prefix.
# The exact format varies by platform (e.g., _PREFIX_ on macOS vs PREFIX_ on Linux).
if ! grep -q 'link_name.*${{ matrix.prefix }}_' build/rust/aws_lc_bindings.rs; then
echo "ERROR: Expected prefixed link_name attributes not found"
exit 1
fi
if ! grep -B1 "pub fn SSL_new" build/rust/aws_lc_bindings.rs | grep -q 'link_name.*${{ matrix.prefix }}_'; then
echo "ERROR: SSL_new should have ${{ matrix.prefix }}_ prefixed link_name"
exit 1
fi
else
# Non-prefix builds should not have link_name attributes
if grep -q '#\[link_name' build/rust/aws_lc_bindings.rs; then
echo "ERROR: Unexpected link_name attributes found in no-prefix build"
exit 1
fi
fi
echo "Bindings content verification passed"


- name: Verify bindings build and link
shell: bash
run: |
mkdir -p "${RUNNER_TEMP}/test-bindings/src"
cat > "${RUNNER_TEMP}/test-bindings/Cargo.toml" << 'EOF'
[package]
name = "test-bindings"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "test-bindings"
path = "src/main.rs"
EOF
cat > "${RUNNER_TEMP}/test-bindings/build.rs" << 'EOF'
use std::env;
fn main() {
let build_dir = env::var("CMAKE_BUILD_DIR").expect("CMAKE_BUILD_DIR must be set");
// Library search paths for single-config generators (Unix Makefiles, Ninja)
println!("cargo:rustc-link-search=native={}/crypto", build_dir);
println!("cargo:rustc-link-search=native={}/ssl", build_dir);
// Library search paths for multi-config generators (Visual Studio)
println!("cargo:rustc-link-search=native={}/crypto/Release", build_dir);
println!("cargo:rustc-link-search=native={}/ssl/Release", build_dir);
println!("cargo:rustc-link-lib=static=crypto");
if env::var("INCLUDE_SSL").unwrap_or_default() == "1" {
println!("cargo:rustc-link-lib=static=ssl");
}
// Platform-specific system library dependencies required by aws-lc
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default();
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_family == "unix" {
println!("cargo:rustc-link-lib=dylib=pthread");
}
if target_os == "windows" {
println!("cargo:rustc-link-lib=dylib=ws2_32");
}
}
EOF
cat > "${RUNNER_TEMP}/test-bindings/src/main.rs" << 'EOF'
#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(improper_ctypes)]
#![allow(unpredictable_function_pointer_comparisons)]
include!(concat!(env!("BINDINGS_PATH"), "/aws_lc_bindings.rs"));
fn main() {
unsafe { CRYPTO_library_init(); }
println!("Bindings link test passed");
}
EOF
cd "${RUNNER_TEMP}/test-bindings"
include_ssl="0"
if [ "${{ matrix.build_libssl }}" != "OFF" ]; then
include_ssl="1"
fi
export CMAKE_BUILD_DIR="${GITHUB_WORKSPACE}/build"
export BINDINGS_PATH="${GITHUB_WORKSPACE}/build/rust"
export INCLUDE_SSL="${include_ssl}"
cargo run
echo "Bindings build, link, and run test passed"
111 changes: 111 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ option(ENABLE_FIPS_ENTROPY_CPU_JITTER "Enable FIPS entropy source: CPU Jitter" O
option(ENABLE_DATA_INDEPENDENT_TIMING "Enable automatic setting/resetting Data-Independent Timing
(DIT) flag in cryptographic functions. Currently only applicable to Arm64 (except on Windows)" OFF)
option(ENABLE_PRE_SONAME_BUILD "Build AWS-LC without SONAME configuration for shared library builds" ON)
option(GENERATE_RUST_BINDINGS "Generate Rust bindings using bindgen-cli" OFF)
set(RUST_BINDINGS_TARGET_VERSION "1.70" CACHE STRING "Minimum Rust version for generated bindings")

include(cmake/go.cmake)

if(NOT ENABLE_PRE_SONAME_BUILD AND BUILD_SHARED_LIBS AND UNIX AND NOT APPLE)
Expand All @@ -113,6 +116,60 @@ enable_language(C)
# Entropy by default; because it's the root of the Tree DRBG.
message(STATUS "Entropy source configured: Dynamic (default: CPU Jitter)")

# Validate Rust bindings prerequisites
if(GENERATE_RUST_BINDINGS)
find_program(BINDGEN_EXECUTABLE NAMES bindgen)
if(NOT BINDGEN_EXECUTABLE)
message(FATAL_ERROR "GENERATE_RUST_BINDINGS is enabled but bindgen-cli was not found. "
"Install it with: cargo install --force --locked bindgen-cli")
endif()

# Verify minimum version (0.69.5)
execute_process(
COMMAND ${BINDGEN_EXECUTABLE} --version
OUTPUT_VARIABLE BINDGEN_VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE BINDGEN_VERSION_RESULT
)

if(NOT BINDGEN_VERSION_RESULT EQUAL 0)
message(FATAL_ERROR "Failed to get bindgen version")
endif()

# Extract version number (format: "bindgen X.Y.Z")
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" BINDGEN_VERSION "${BINDGEN_VERSION_OUTPUT}")

if(BINDGEN_VERSION VERSION_LESS "0.69.5")
message(FATAL_ERROR "bindgen version ${BINDGEN_VERSION} is too old. "
"Minimum required version is 0.69.5. "
"Upgrade with: cargo install --force --locked bindgen-cli")
endif()

message(STATUS "Found bindgen-cli: ${BINDGEN_EXECUTABLE} (version ${BINDGEN_VERSION})")
message(STATUS "Rust bindings target version: ${RUST_BINDINGS_TARGET_VERSION}")

# Check for rustfmt (required for --formatter rustfmt option)
find_program(RUSTFMT_EXECUTABLE NAMES rustfmt)
if(NOT RUSTFMT_EXECUTABLE)
message(FATAL_ERROR "GENERATE_RUST_BINDINGS requires rustfmt but it was not found. "
"Install it with: rustup component add rustfmt")
endif()
message(STATUS "Found rustfmt: ${RUSTFMT_EXECUTABLE}")
endif()

if(DISABLE_CPU_JITTER_ENTROPY)
if(FIPS)
message(FATAL_ERROR "Cannot opt-out of CPU Jitter for the FIPS build")
endif()
add_definitions(-DDISABLE_CPU_JITTER_ENTROPY)
message(STATUS "Entropy source configuration: CPU Jitter opt-out")
message(STATUS "Entropy source configured: Dynamic (default: Operating system)")
else()
# The validated entropy source will always be configured to be CPU Jitter
# Entropy by default; because it's the root of the Tree DRBG.
message(STATUS "Entropy source configured: Dynamic (default: CPU Jitter)")
endif()

if(${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
# OpenBSD by defaults links with --execute-only this is problematic for two reasons:
# 1. The FIPS shared and static builds need to compute the module signature hash by reading the .text section
Expand Down Expand Up @@ -347,6 +404,60 @@ else()
)
endif()

# Rust bindings generation
if(GENERATE_RUST_BINDINGS)
include(cmake/rust_bindings.cmake)

# Generate the rust_wrapper.h header from template
generate_rust_wrapper_header()

set(RUST_BINDINGS_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rust/aws_lc_bindings.rs")

# Build list of include directories for bindgen.
# Prefix symbols header is intentionally excluded; see cmake/rust_bindings.cmake.
set(RUST_BINDINGS_INCLUDE_DIRS
"${CMAKE_CURRENT_BINARY_DIR}/include" # Generated rust_wrapper.h location
"${AWSLC_SOURCE_DIR}/include" # Main AWS-LC headers (unprefixed)
)

# Conditionally set optional arguments for generate_rust_bindings
if(BORINGSSL_PREFIX)
set(_rust_prefix_arg PREFIX "${BORINGSSL_PREFIX}")
else()
set(_rust_prefix_arg "")
endif()

if(BUILD_LIBSSL)
set(_rust_ssl_arg INCLUDE_SSL)
else()
set(_rust_ssl_arg "")
endif()

generate_rust_bindings(
OUTPUT_FILE "${RUST_BINDINGS_OUTPUT}"
INCLUDE_DIRS ${RUST_BINDINGS_INCLUDE_DIRS}
${_rust_prefix_arg}
${_rust_ssl_arg}
)

# Install the generated Rust bindings.
# The rust_bindings target is not part of ALL, so we build it on-demand during install.
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target rust_bindings)")

install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/rust/aws_lc_bindings.rs"
DESTINATION "share/rust"
COMPONENT Development
)

# Install the generated rust_wrapper.h header
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/include/rust_wrapper.h"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT Development
)
endif()


if("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten")
set(EMSCRIPTEN 1)
Expand Down
Loading
Loading