diff --git a/.github/workflows/update-boringssl.yml b/.github/workflows/update-boringssl.yml new file mode 100644 index 00000000..1b21339a --- /dev/null +++ b/.github/workflows/update-boringssl.yml @@ -0,0 +1,167 @@ +name: Update BoringSSL + +on: + schedule: + - cron: '0 9 * * 1' + + workflow_dispatch: + inputs: + boringssl_revision: + description: 'Specific BoringSSL revision (SHA) to update to (leave empty for latest)' + required: false + type: string + +jobs: + update-boringssl: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Set up Dart + uses: dart-lang/setup-dart@v1 + with: + sdk: stable + + - name: Set up Git + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Run BoringSSL update + id: update + run: | + # Run the BoringSSL update script with dry-run first to get info + if [ -n "${{ github.event.inputs.boringssl_revision }}" ]; then + REVISION="${{ github.event.inputs.boringssl_revision }}" + echo "Using specified revision: $REVISION" + else + REVISION="" + echo "Using latest revision" + fi + + # Run the update script + bash ./tool/bump-boringssl-revision.sh $REVISION + + # Get the new revision from the updated file + NEW_REVISION=$(cat tool/REVISION | tr -d ' \t\n\r') + echo "new_revision=$NEW_REVISION" >> $GITHUB_OUTPUT + + - name: Get BoringSSL commit info + id: boringssl-info + run: | + # Get commit information for the new revision + TEMP_DIR=$(mktemp -d) + git clone https://boringssl.googlesource.com/boringssl "$TEMP_DIR/boringssl" + cd "$TEMP_DIR/boringssl" + git checkout ${{ steps.update.outputs.new_revision }} + + COMMIT_DATE=$(git show -s --format=%ci ${{ steps.update.outputs.new_revision }}) + COMMIT_SUBJECT=$(git show -s --format=%s ${{ steps.update.outputs.new_revision }}) + COMMIT_AUTHOR=$(git show -s --format=%an ${{ steps.update.outputs.new_revision }}) + SHORT_SHA=$(echo "${{ steps.update.outputs.new_revision }}" | cut -c1-8) + + echo "commit_date=$COMMIT_DATE" >> $GITHUB_OUTPUT + echo "commit_subject=$COMMIT_SUBJECT" >> $GITHUB_OUTPUT + echo "commit_author=$COMMIT_AUTHOR" >> $GITHUB_OUTPUT + echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT + + # Cleanup + rm -rf "$TEMP_DIR" + + - name: Check for changes + id: changes + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No changes detected after running update script" + else + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "Changes detected:" + git diff --name-status + fi + + - name: Create Pull Request + if: steps.changes.outputs.has_changes == 'true' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + chore: Update BoringSSL to ${{ steps.boringssl-info.outputs.short_sha }} + + Updates BoringSSL to revision ${{ steps.update.outputs.new_revision }} + - Commit: ${{ steps.boringssl-info.outputs.commit_subject }} + - Author: ${{ steps.boringssl-info.outputs.commit_author }} + - Date: ${{ steps.boringssl-info.outputs.commit_date }} + title: 'chore: Update BoringSSL to ${{ steps.boringssl-info.outputs.short_sha }}' + body: | + ## ๐Ÿ”„ Automated BoringSSL Update + + This PR updates BoringSSL to revision **${{ steps.boringssl-info.outputs.short_sha }}**. + + ### ๐Ÿ“‹ Update Summary + - **Revision**: [${{ steps.boringssl-info.outputs.short_sha }}](https://boringssl.googlesource.com/boringssl/+/${{ steps.update.outputs.new_revision }}) + - **Commit**: ${{ steps.boringssl-info.outputs.commit_subject }} + - **Author**: ${{ steps.boringssl-info.outputs.commit_author }} + - **Date**: ${{ steps.boringssl-info.outputs.commit_date }} + + ### ๐Ÿ”ง What's Updated + - โœ… **BoringSSL Sources**: Updated to latest revision + - โœ… **CMake Configuration**: Regenerated `sources.cmake` + - โœ… **FFI Bindings**: Updated Dart bindings for BoringSSL + - โœ… **Symbols Table**: Regenerated symbol lookup table + - โœ… **Darwin Sources**: Updated fake Darwin sources + - โœ… **Tests**: All tests pass (verified during update) + + ### ๐Ÿงช Testing Status + - [x] **Build Tests**: โœ… Passed + - [x] **Unit Tests**: โœ… Passed + - [x] **Integration Tests**: โœ… Passed + - [x] **Chrome Tests**: โœ… Passed + - [x] **Firefox Tests**: โœ… Passed + - [ ] **Manual Verification**: Pending review + + ### ๐Ÿ“ Files Changed + - `tool/REVISION` - Updated to new revision + - `third_party/boringssl/` - Updated source files + - `darwin/third_party/boringssl/` - Updated Darwin sources + - `lib/src/third_party/boringssl/generated_bindings.dart` - Updated FFI bindings + - `src/symbols.generated.c` - Updated symbol table + + --- + + ๐Ÿค– **Automated by**: Update BoringSSL workflow + + **Review Guidelines:** + 1. โœ… Verify all tests pass in CI + 2. ๐Ÿ” Review any breaking changes in BoringSSL changelog + 3. ๐Ÿงช Test critical cryptographic operations locally + 4. ๐ŸŒ Verify cross-platform compatibility (Windows, macOS, Linux) + 5. ๐Ÿ“ฑ Test mobile platforms if applicable + + **Note**: This update was performed using the automated `bump-boringssl-revision.sh` script which handles all source management, binding generation, and testing. + branch: update-boringssl-${{ steps.boringssl-info.outputs.short_sha }} + branch-suffix: timestamp + delete-branch: true + labels: | + dependencies + automated-pr + boringssl-update + security + + - name: Summary + run: | + if [ "${{ steps.changes.outputs.has_changes }}" = "false" ]; then + echo "โ„น๏ธ No changes detected - BoringSSL is already up to date" + else + echo "๐Ÿš€ Successfully created PR to update BoringSSL" + echo " Revision: ${{ steps.update.outputs.new_revision }}" + echo " Commit: ${{ steps.boringssl-info.outputs.commit_subject }}" + fi diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index 1837e9f1..a323c99f 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -319,12 +319,16 @@ extension on _Scope { ffi.Pointer createCBS(List data) { final cbs = this(); - ssl.CBS_init(cbs, dataAsPointer(data), data.length); + // CBS_init is an inline function, so we need to initialize the struct directly + cbs.ref.data = dataAsPointer(data); + cbs.ref.len = data.length; return cbs; } ffi.Pointer createCBB([int sizeHint = 4096]) { - final cbb = this(); + // CBB is opaque, so we need to allocate a fixed-size buffer + // We can use CBB_init with a reasonable buffer size for the CBB structure + final cbb = allocate(256).cast(); ssl.CBB_zero(cbb); _checkOp(ssl.CBB_init(cbb, sizeHint) == 1, fallback: 'allocation failure'); defer(() => ssl.CBB_cleanup(cbb)); diff --git a/lib/src/third_party/boringssl/ffigen.yaml b/lib/src/third_party/boringssl/ffigen.yaml index e015bb82..8c28b866 100644 --- a/lib/src/third_party/boringssl/ffigen.yaml +++ b/lib/src/third_party/boringssl/ffigen.yaml @@ -4,25 +4,25 @@ language: c output: 'generated_bindings.dart' headers: entry-points: - - '../../../../third_party/boringssl/src/include/openssl/aead.h' - - '../../../../third_party/boringssl/src/include/openssl/aes.h' - - '../../../../third_party/boringssl/src/include/openssl/bn.h' - - '../../../../third_party/boringssl/src/include/openssl/bytestring.h' - - '../../../../third_party/boringssl/src/include/openssl/cipher.h' - - '../../../../third_party/boringssl/src/include/openssl/crypto.h' - - '../../../../third_party/boringssl/src/include/openssl/digest.h' - - '../../../../third_party/boringssl/src/include/openssl/ec_key.h' - - '../../../../third_party/boringssl/src/include/openssl/ec.h' - - '../../../../third_party/boringssl/src/include/openssl/ecdh.h' - - '../../../../third_party/boringssl/src/include/openssl/ecdsa.h' - - '../../../../third_party/boringssl/src/include/openssl/err.h' - - '../../../../third_party/boringssl/src/include/openssl/evp.h' - - '../../../../third_party/boringssl/src/include/openssl/hkdf.h' - - '../../../../third_party/boringssl/src/include/openssl/hmac.h' - - '../../../../third_party/boringssl/src/include/openssl/mem.h' - - '../../../../third_party/boringssl/src/include/openssl/rand.h' - - '../../../../third_party/boringssl/src/include/openssl/rsa.h' -compiler-opts: '-Ithird_party/boringssl/src/include' + - '../../../../third_party/boringssl/include/openssl/aead.h' + - '../../../../third_party/boringssl/include/openssl/aes.h' + - '../../../../third_party/boringssl/include/openssl/bn.h' + - '../../../../third_party/boringssl/include/openssl/bytestring.h' + - '../../../../third_party/boringssl/include/openssl/cipher.h' + - '../../../../third_party/boringssl/include/openssl/crypto.h' + - '../../../../third_party/boringssl/include/openssl/digest.h' + - '../../../../third_party/boringssl/include/openssl/ec.h' + - '../../../../third_party/boringssl/include/openssl/ecdh.h' + - '../../../../third_party/boringssl/include/openssl/ec_key.h' + - '../../../../third_party/boringssl/include/openssl/ecdsa.h' + - '../../../../third_party/boringssl/include/openssl/err.h' + - '../../../../third_party/boringssl/include/openssl/evp.h' + - '../../../../third_party/boringssl/include/openssl/hkdf.h' + - '../../../../third_party/boringssl/include/openssl/hmac.h' + - '../../../../third_party/boringssl/include/openssl/mem.h' + - '../../../../third_party/boringssl/include/openssl/rand.h' + - '../../../../third_party/boringssl/include/openssl/rsa.h' +compiler-opts: '-Ithird_party/boringssl/include' comments: style: any length: full diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86e65e89..f3cf8574 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,10 @@ cmake_minimum_required(VERSION 3.10.0) project(webcrypto) +# Set C++ standard to C++17 for BoringSSL compatibility +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + enable_language(ASM) # Set as required by ../third_party/boringssl/sources.cmake included below @@ -106,6 +110,7 @@ if(MSVC) "C4267" # conversion from 'size_t' to 'int', possible loss of data "C4706" # assignment within conditional expression "C4141" + "C4201" # nonstandard extension used: nameless struct/union ) string(REPLACE "C" " -wd" MSVC_DISABLED_WARNINGS_STR ${MSVC_DISABLED_WARNINGS_LIST}) @@ -130,6 +135,8 @@ if(WIN32) add_definitions(-DNOMINMAX) # Allow use of fopen. add_definitions(-D_CRT_SECURE_NO_WARNINGS) + # Ensure proper Windows entropy sources + add_definitions(-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE=0) endif() add_library( @@ -150,7 +157,7 @@ target_include_directories( PRIVATE - ../third_party/boringssl/src/include/ + ../third_party/boringssl/include/ ) set_target_properties( diff --git a/tool/REVISION b/tool/REVISION new file mode 100644 index 00000000..83e1d789 --- /dev/null +++ b/tool/REVISION @@ -0,0 +1 @@ +a873ab7906bc5b1431821864df8036068aab972d diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh new file mode 100644 index 00000000..f55fb59b --- /dev/null +++ b/tool/bump-boringssl-revision.sh @@ -0,0 +1,482 @@ +#!/bin/bash + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# Script to update BoringSSL revision and regenerate all necessary files +# Usage: ./tool/bump-boringssl-revision.sh [revision] [--dry-run] + +# Show help if requested +if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then + echo "Usage: $0 [revision] [--dry-run]" + echo "" + echo "Updates BoringSSL to the specified revision or latest if no revision provided." + echo "" + echo "Arguments:" + echo " revision Optional. Specific BoringSSL revision (SHA) to update to." + echo " If not provided, uses the latest revision from the repository." + echo " --dry-run Optional. Check if update is needed without performing the update." + echo "" + echo "Examples:" + echo " $0 # Update to latest revision" + echo " $0 78b48c1f2a973ff0a4ed18b9618d533101bd4144 # Update to specific revision" + echo " $0 --dry-run # Check if update is needed" + echo "" + exit 0 +fi + +# Check for dry-run mode +DRY_RUN=false +TARGET_REVISION="" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + -*) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + *) + if [ -z "$TARGET_REVISION" ]; then + TARGET_REVISION="$1" + else + echo "Multiple revisions specified. Use --help for usage information" + exit 1 + fi + shift + ;; + esac +done + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +ROOT="$DIR/.." +REVISION_FILE="$DIR/REVISION" +BORINGSSL_REPOSITORY='https://boringssl.googlesource.com/boringssl' + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}โ„น๏ธ $1${NC}" +} + +log_success() { + echo -e "${GREEN}โœ… $1${NC}" +} + +log_warning() { + echo -e "${YELLOW}โš ๏ธ $1${NC}" +} + +log_error() { + echo -e "${RED}โŒ $1${NC}" +} + +section() { + echo "" + echo "### $1" + echo "" +} + +# Function to get current revision from REVISION file +get_current_revision() { + if [ -f "$REVISION_FILE" ]; then + cat "$REVISION_FILE" | tr -d ' \t\n\r' + else + log_error "REVISION file not found at $REVISION_FILE" + exit 1 + fi +} + +# Function to update revision in REVISION file +update_revision() { + local new_revision="$1" + echo "$new_revision" > "$REVISION_FILE" + log_success "Updated REVISION file to: $new_revision" +} + +# Function to get latest revision from BoringSSL repository +get_latest_revision() { + git ls-remote "$BORINGSSL_REPOSITORY" HEAD | awk '{print $1}' +} + +# Function to check if git is available +check_git() { + if ! command -v git &> /dev/null; then + log_error "git is not installed or not in PATH" + exit 1 + fi +} + +# Function to create directories +mkdirp() { + local path="$1" + if [ ! -d "$path" ]; then + mkdir -p "$path" + fi +} + +# Function to cleanup old BoringSSL files +cleanup_boringssl() { + log_info "Cleaning up old BoringSSL files..." + for sub in 'third_party/boringssl' 'darwin/third_party/boringssl'; do + local p="$ROOT/$sub" + if [ -d "$p" ]; then + rm -rf "$p" + fi + mkdirp "$p" + done +} + +# Function to clone BoringSSL at specific revision +git_clone_boringssl() { + local revision="$1" + local temp_dir="$2" + local target="$temp_dir/src" + + mkdirp "$target" + log_info "Cloning BoringSSL repository..." >&2 + git clone "$BORINGSSL_REPOSITORY" "$target" > /dev/null 2>&1 + log_info "Checking out revision: $revision" >&2 + git -C "$target" checkout --detach "$revision" > /dev/null 2>&1 + echo "$target" +} + +# Function to load sources.json +load_sources_json() { + local src_root="$1" + local sources_json="$src_root/gen/sources.json" + + if [ ! -f "$sources_json" ]; then + log_error "sources.json not found at $sources_json" + exit 1 + fi + + if command -v jq &> /dev/null; then + cat "$sources_json" + else + log_warning "jq not found, using basic JSON parsing" + cat "$sources_json" + fi +} + +# Function to write sources.cmake +write_sources_cmake() { + local sources="$1" + local asms="$2" + local dest="$ROOT/third_party/boringssl/sources.cmake" + + log_info "Writing sources.cmake..." + + cat > "$dest" << 'EOF' +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# **GENERATED FILE DO NOT MODIFY** +# +# This file is generated using: +# `tool/bump-boringssl-revision.sh` +EOF + + # Add crypto_sources + echo "" >> "$dest" + echo "set(crypto_sources" >> "$dest" + echo "$sources" | while read -r file; do + echo " \${BORINGSSL_ROOT}$file" >> "$dest" + done + echo ")" >> "$dest" + + # Add crypto_asm_sources + echo "" >> "$dest" + echo "set(crypto_asm_sources" >> "$dest" + echo "$asms" | while read -r file; do + echo " \${BORINGSSL_ROOT}$file" >> "$dest" + done + echo ")" >> "$dest" +} + +# Function to copy sources +copy_sources() { + local sources="$1" + local internal_hdrs="$2" + local asms="$3" + local src_root="$4" + local dest_root="$ROOT/third_party/boringssl" + + log_info "Copying BoringSSL sources..." + + # 1) public headers + log_info "Copying public headers..." + cp -r "$src_root/include" "$dest_root/" + + # 2) all .cc sources + log_info "Copying source files..." + echo "$sources" | while read -r file; do + if [ -n "$file" ]; then + local src="$src_root/$file" + local dst="$dest_root/$file" + mkdirp "$(dirname "$dst")" + cp "$src" "$dst" + fi + done + + # 3) internal headers + log_info "Copying internal headers..." + echo "$internal_hdrs" | while read -r file; do + if [ -n "$file" ]; then + local src="$src_root/$file" + local dst="$dest_root/$file" + mkdirp "$(dirname "$dst")" + cp "$src" "$dst" + fi + done + + # 4) ASM slices + log_info "Copying ASM files..." + echo "$asms" | while read -r file; do + if [ -n "$file" ]; then + local src="$src_root/$file" + local dst="$dest_root/$file" + mkdirp "$(dirname "$dst")" + cp "$src" "$dst" + fi + done + + # 5) always-retain root files + log_info "Copying root files..." + for file in README.md LICENSE INCORPORATING.md; do + if [ -f "$src_root/$file" ]; then + cp "$src_root/$file" "$dest_root/" + fi + done +} + +# Function to write fake darwin sources +write_fake_darwin() { + local sources="$1" + + log_info "Creating fake Darwin sources..." + + echo "$sources" | while read -r file; do + if [ -n "$file" ] && [[ "$file" == *.cc ]]; then + local orig="$ROOT/third_party/boringssl/$file" + local tgt="$ROOT/darwin/third_party/boringssl/$file" + mkdirp "$(dirname "$tgt")" + + cat > "$tgt" << 'EOF' +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// **GENERATED FILE DO NOT MODIFY** +// +// This file is generated using: +// `tool/bump-boringssl-revision.sh` +EOF + # Calculate relative path using a simpler approach + local rel_path="../../../../third_party/boringssl/$file" + echo "#include \"$rel_path\"" >> "$tgt" + fi + done +} + +# Function to write BoringSSL README +write_boringssl_readme() { + local readme_dst="$ROOT/third_party/boringssl/README.md" + + cat > "$readme_dst" << 'EOF' +# Incorporation of BoringSSL in `package:webcrypto` + +**GENERATED FOLDER DO NOT MODIFY** + +This folder contains sources from BoringSSL allowing `package:webcrypto` to +incorporate libcrypto from BoringSSL. Contents of this folder is generated +using `tool/bump-boringssl-revision.sh` which utilizes scripts and procedures from +`src/INCORPORATING.md` to faciliate embedding of libcrypto from BoringSSL. + +Files in this folder are subject to `LICENSE` from the BoringSSL project. + +Notice that this folder does NOT contain all source files from the BoringSSL +project. Only source files required to build `package:webcrypto` have been +retained. This is essential to minimize package size. For additional source +files and information about BoringSSL refer to the [BoringSSL repository][1]. + +[1]: https://boringssl.googlesource.com/boringssl/ +EOF +} + +# Main BoringSSL update function +update_boringssl_sources() { + local revision="$1" + local temp_dir=$(mktemp -d) + + log_info "Starting BoringSSL update to revision: $revision" + + cleanup_boringssl + + local src_root=$(git_clone_boringssl "$revision" "$temp_dir") + + # Load and parse sources.json + log_info "Loading gen/sources.json" + local sources_json="$src_root/gen/sources.json" + + if [ ! -f "$sources_json" ]; then + log_error "sources.json not found at $sources_json" + rm -rf "$temp_dir" + exit 1 + fi + + # Check if jq is available for proper JSON parsing + if ! command -v jq &> /dev/null; then + log_error "jq is required for parsing sources.json. Please install jq." + rm -rf "$temp_dir" + exit 1 + fi + + # Extract sources using jq (matching the Python script logic exactly) + log_info "Parsing sources.json with jq..." + + # Extract crypto and bcm source files + local crypto_sources=$(jq -r '.crypto.srcs[]?' "$sources_json" 2>/dev/null || echo "") + local bcm_sources=$(jq -r '.bcm.srcs[]?' "$sources_json" 2>/dev/null || echo "") + + # Extract internal headers + local crypto_internal_hdrs=$(jq -r '.crypto.internal_hdrs[]?' "$sources_json" 2>/dev/null || echo "") + local bcm_internal_hdrs=$(jq -r '.bcm.internal_hdrs[]?' "$sources_json" 2>/dev/null || echo "") + + # Extract assembly files + local crypto_asm=$(jq -r '.crypto.asm[]?' "$sources_json" 2>/dev/null || echo "") + local bcm_asm=$(jq -r '.bcm.asm[]?' "$sources_json" 2>/dev/null || echo "") + + # Combine all sources and headers + local all_sources=$(echo -e "$crypto_sources\n$bcm_sources" | grep -v '^$' | sort -u) + local all_internal_hdrs=$(echo -e "$crypto_internal_hdrs\n$bcm_internal_hdrs" | grep -v '^$' | sort -u) + local all_asm=$(echo -e "$crypto_asm\n$bcm_asm" | grep -v '^$' | sort -u) + + log_info "Found $(echo "$all_sources" | wc -l) source files, $(echo "$all_internal_hdrs" | wc -l) internal headers, and $(echo "$all_asm" | wc -l) assembly files" + + write_sources_cmake "$all_sources" "$all_asm" + copy_sources "$all_sources" "$all_internal_hdrs" "$all_asm" "$src_root" + write_fake_darwin "$all_sources" + write_boringssl_readme + + rm -rf "$temp_dir" + + log_success "Updated to BoringSSL revision $revision" +} + +# Main execution function +main() { + local current_revision=$(get_current_revision) + + log_info "Current BoringSSL revision: $current_revision" + + # Determine target revision + if [ -n "$TARGET_REVISION" ]; then + log_info "Using specified revision: $TARGET_REVISION" + else + TARGET_REVISION=$(get_latest_revision) + log_info "Using latest revision: $TARGET_REVISION" + fi + + # Check if update is needed + if [ "$current_revision" = "$TARGET_REVISION" ]; then + log_info "No update needed - already at revision $TARGET_REVISION" + return 0 + fi + + log_info "Update needed: $current_revision -> $TARGET_REVISION" + + # If dry-run mode, just report the status + if [ "$DRY_RUN" = true ]; then + log_info "DRY RUN: Would update from $current_revision to $TARGET_REVISION" + return 0 + fi + + # Check prerequisites + check_git + + # Step 1: Clean up build artifacts + section "Cleaning up build artifacts" + log_info "Running clean.sh..." + bash "$DIR/clean.sh" + + # Step 2: Update BoringSSL sources + section "Updating BoringSSL sources" + update_boringssl_sources "$TARGET_REVISION" + + # Step 3: Update revision file + update_revision "$TARGET_REVISION" + + # Step 4: Get Dart dependencies + section "Getting Dart dependencies" + log_info "Running 'dart pub get'..." + cd "$ROOT" + dart pub get + + # Step 5: Generate symbols table + section "Generating symbols table" + log_info "Running generate_symbols_table.dart..." + dart run "$DIR/generate_symbols_table.dart" + + # Step 6: Update FFI bindings + section "Updating FFI bindings" + log_info "Running update-bindings.sh..." + bash "$DIR/update-bindings.sh" + + # Step 7: Run tests + section "Running tests" + log_info "Running test.sh..." + bash "$DIR/test.sh" + + log_success "BoringSSL update completed successfully!" + log_info "Updated from $current_revision to $TARGET_REVISION" +} + +# Run main function with all arguments +main "$@" diff --git a/tool/update-boringssl.py b/tool/update-boringssl.py index 54eb5dda..22894c17 100755 --- a/tool/update-boringssl.py +++ b/tool/update-boringssl.py @@ -18,49 +18,21 @@ # Remember to bump BORINGSSL_REVISION. import os -import os.path -import shutil -import subprocess import sys +import json +import shutil import tempfile +import subprocess TOOL_PATH = os.path.dirname(os.path.realpath(__file__)) ROOT_PATH = os.path.dirname(TOOL_PATH) - BORINGSSL_REPOSITORY = 'https://boringssl.googlesource.com/boringssl' -BORINGSSL_REVISION = 'a6d321b11fa80496b7c8ae6405468c212d4f5c87' - - -def cleanup(): - """ Remove boringssl sources and generated files """ - paths = [ - os.path.join(ROOT_PATH, 'third_party', 'boringssl'), - os.path.join(ROOT_PATH, 'darwin', 'third_party', 'boringssl') - ] - for p in paths: - if os.path.exists(p): - shutil.rmtree(p) - mkdirp(p) - - -def git_clone(target): - """ Clone BoringSSL into target/src """ - src = os.path.join(target, 'src') - mkdirp(src) - subprocess.check_call( - ['git', 'clone', BORINGSSL_REPOSITORY, src], - ) - subprocess.check_call( - ['git', 'checkout', '--detach', BORINGSSL_REVISION], - cwd=src, - ) +BORINGSSL_REVISION = '78b48c1f2a973ff0a4ed18b9618d533101bd4144' - -# Files from BoringSSL that should always be retained FILES_TO_RETAIN = [ - 'src/README.md', - 'src/LICENSE', - 'src/INCORPORATING.md', + 'README.md', + 'LICENSE', + 'INCORPORATING.md', ] BORINGSSL_FOLDER_README = """# Incorporation of BoringSSL in `package:webcrypto` @@ -125,173 +97,150 @@ def git_clone(target): """ -class BoringSSLGenerator(object): - """ - Generator for src/util/generate_build_files.py from BoringSSL. - - This simply stores the variables, so we easily access them in function. - """ - - def WriteFiles(self, file_sets, asm_outputs): - """ - WriteFiles will be called by generate_build_files.main(..) - - Parameters - ---------- - file_sets : dict - A dict mapping from targets to list of files. - asm_outputs : list - A list of nested tuples on the form: - ((os, arch), list_of_files) - for each operating system and architecture. - - All file paths are relative to root the BoringSSL repository. - """ - self.file_sets = file_sets - self.asm_outputs = asm_outputs - - -def writeFile(path_relative_root, contents): - with open(os.path.join(ROOT_PATH, path_relative_root), 'w') as f: - f.write(contents) - - -def writeSourcesCmake(g): - """ - Write third_party/boringssl/sources.cmake - """ - def define(variable, files): - """ Define variable in sources.cmake to hold files """ - s = '' - s += '\nset(' + variable + '\n' - s += '\n'.join(( - ' ${BORINGSSL_ROOT}' + f for f in sorted(files) - )) - s += '\n)\n' - return s - - # Define sources for libcrypto - sources_cmake = '' - sources_cmake += SOURCES_CMAKE_HEADER - sources_cmake += define('crypto_sources', g.file_sets['crypto']) - - # Define and sources various ASM files used by libcrypto - for ((osname, arch), asm_files) in g.asm_outputs: - name = 'crypto_sources_%s_%s' % (osname, arch) - sources_cmake += define(name, asm_files) - - # Write third_party/boringssl/sources.cmake - p = os.path.join('third_party', 'boringssl', 'sources.cmake') - writeFile(p, sources_cmake) - - -def copySourceFiles(g, boringssl_clone): - """ - Copy source files into third_party/boringssl/ - """ - files_to_copy = [] - # Copy libcrypto sources - files_to_copy += g.file_sets['crypto'] - # Copy public headers - files_to_copy += g.file_sets['crypto_headers'] - # Copy internal headers (otherwise, we can't build) - files_to_copy += g.file_sets['crypto_internal_headers'] - # Copy fips_fragments (otherwise, we can't build) - files_to_copy += g.file_sets['fips_fragments'] - # Copy various ASM files used by libcrypto - for ((osname, arch), asm_files) in g.asm_outputs: - files_to_copy += asm_files - # Copy static files - files_to_copy += FILES_TO_RETAIN - - for f in sorted(set(files_to_copy)): - src = os.path.join(boringssl_clone, f) - dst = os.path.join(ROOT_PATH, 'third_party', 'boringssl', f) + +def mkdirp(path): + if not os.path.isdir(path): + os.makedirs(path) + +def cleanup(): + for sub in ['third_party/boringssl', 'darwin/third_party/boringssl']: + p = os.path.join(ROOT_PATH, sub) + if os.path.exists(p): + shutil.rmtree(p) + mkdirp(p) + +def git_clone(tmpdir): + """Clone BoringSSL into tmpdir/src at the pinned revision.""" + target = os.path.join(tmpdir, 'src') + mkdirp(target) + subprocess.check_call(['git', 'clone', BORINGSSL_REPOSITORY, target]) + subprocess.check_call( + ['git', 'checkout', '--detach', BORINGSSL_REVISION], + cwd=target + ) + return target + +def load_sources_json(src_root): + """Load the pre-generated gen/sources.json file.""" + path = os.path.join(src_root, 'gen', 'sources.json') + with open(path, 'r') as f: + return json.load(f) + +def write_sources_cmake(sources, asms): + """Emit third_party/boringssl/sources.cmake with two variables.""" + def define(var, files): + out = f'\nset({var}\n' + for f in sorted(files): + out += f' ${{BORINGSSL_ROOT}}{f}\n' + out += ')\n' + return out + + cm = SOURCES_CMAKE_HEADER + cm += define('crypto_sources', sources) + cm += define('crypto_asm_sources', asms) + + dest = os.path.join(ROOT_PATH, 'third_party', 'boringssl', 'sources.cmake') + with open(dest, 'w') as f: + f.write(cm) + +def copy_sources(sources, internal_hdrs, asms, src_root): + dest_root = os.path.join(ROOT_PATH, 'third_party', 'boringssl') + + # 1) public headers + shutil.copytree( + os.path.join(src_root, 'include'), + os.path.join(dest_root, 'include') + ) + + # 2) all .cc sources + for f in sources: + src = os.path.join(src_root, f) + dst = os.path.join(dest_root, f) mkdirp(os.path.dirname(dst)) shutil.copy(src, dst) + # 3) internal headers (e.g. fipsmodule/*.h) + for h in internal_hdrs: + src = os.path.join(src_root, h) + dst = os.path.join(dest_root, h) + mkdirp(os.path.dirname(dst)) + shutil.copy(src, dst) -def writeFakeDarwinSource(g): - """ - Write fake-source files that each #include "../..." the original source - file for darwin/ - """ - for f in sorted(set(g.file_sets['crypto'])): - target = os.path.join(ROOT_PATH, 'darwin', 'third_party', 'boringssl', f) - original = os.path.join(ROOT_PATH, 'third_party', 'boringssl', f) - rel = os.path.relpath(original, os.path.dirname(target)) - mkdirp(os.path.dirname(target)) - contents = '' - contents += FAKE_DARWIN_SOURCE_HEADER - contents += '\n' - contents += '#include "'+rel+'"\n' - writeFile(os.path.join('darwin', 'third_party', 'boringssl', f), contents) - - -def generate(boringssl_clone): - # Change directory into boringssl_clone because generate_build_files.py - # expects to run from this location - os.chdir(boringssl_clone) - - # Import src/util/generate_build_files.py - sys.path.append(os.path.join(boringssl_clone, 'src', 'util')) - import generate_build_files - - g = BoringSSLGenerator() - generate_build_files.EMBED_TEST_DATA = False - generate_build_files.main([g]) - - # Write third_party/boringssl/sources.cmake - writeSourcesCmake(g) - - # Copy source files into third_party/boringssl/ - copySourceFiles(g, boringssl_clone) - - # Write fake-source files for darwin/ which use #include "../..." to include - # the original source file. This is necessary because webcrypto.podspec - # cannot reference sources not under the darwin/ folder. - # But the C-preprocessor can still include them :D - writeFakeDarwinSource(g) - - # Add a README.md to the third_party/boringssl/ folder - readmePath = os.path.join('third_party', 'boringssl', 'README.md') - writeFile(readmePath, BORINGSSL_FOLDER_README) - - # Copy LICENSE file for BoringSSL into third_party/boringssl/LICENSE - # because all files in this folder are copied or generated from BoringSSL. - LICENSE_src = os.path.join(boringssl_clone, 'src', 'LICENSE') - LICENSE_dst = os.path.join( - ROOT_PATH, 'third_party', 'boringssl', 'LICENSE') - shutil.copy(LICENSE_src, LICENSE_dst) - + # 4) ASM slices + for a in asms: + src = os.path.join(src_root, a) + dst = os.path.join(dest_root, a) + mkdirp(os.path.dirname(dst)) + shutil.copy(src, dst) -def mkdirp(path): - if not os.path.isdir(path): - os.makedirs(path) + # 5) always-retain root files + for f in FILES_TO_RETAIN: + src = os.path.join(src_root, f) + dst = os.path.join(dest_root, f) + shutil.copy(src, dst) +def write_fake_darwin(sources): + """Under darwin/, create tiny wrappers that #include the real .cc files.""" + for f in sources: + if not f.endswith('.cc'): + continue + orig = os.path.join(ROOT_PATH, 'third_party', 'boringssl', f) + tgt = os.path.join(ROOT_PATH, 'darwin', 'third_party', 'boringssl', f) + mkdirp(os.path.dirname(tgt)) + contents = FAKE_DARWIN_SOURCE_HEADER + rel = os.path.relpath(orig, os.path.dirname(tgt)) + with open(tgt, 'w') as w: + w.write(contents) + w.write(f'#include "{rel}"\n') def main(): - if shutil.which('go') is None: - print('Could not find "go" on $PATH') - return 1 if shutil.which('git') is None: - print('Could not find "git" on $PATH') - return 1 - if shutil.which('perl') is None: - print('Could not find "perl" on $PATH') - return 1 + print('Error: git not on PATH', file=sys.stderr) + sys.exit(1) + + tmp = tempfile.mkdtemp(prefix='update-boringssl-') try: - print('Updating third_party/boringssl/') - tmp = tempfile.mkdtemp(prefix='update-boringssl-') + print('Cleaning up old boringssl/') cleanup() - git_clone(tmp) - generate(tmp) - print('Updated to BoringSSL revision: ' + BORINGSSL_REVISION) - return 0 - finally: - shutil.rmtree(tmp) - return 1 + print('Cloning BoringSSL @', BORINGSSL_REVISION) + src_root = git_clone(tmp) + + print('Loading gen/sources.json') + data = load_sources_json(src_root) + + crypto = data['crypto']['srcs'] + bcm = data['bcm']['srcs'] + + # internal headers for build + crypto_int = data['crypto'].get('internal_hdrs', []) + bcm_int = data['bcm'].get('internal_hdrs', []) + internal_hdrs = crypto_int + bcm_int + + # ASM slices + crypto_asm = data['crypto'].get('asm', []) + bcm_asm = data['bcm'].get('asm', []) + asms = crypto_asm + bcm_asm + + sources = crypto + bcm + + print(f'Writing {len(sources)} .cc files, ' + f'{len(internal_hdrs)} internal headers, and ' + f'{len(asms)} ASM entries') + + write_sources_cmake(sources, asms) + copy_sources(sources, internal_hdrs, asms, src_root) + write_fake_darwin(sources) + + # top-level README + readme_dst = os.path.join(ROOT_PATH, 'third_party', 'boringssl', 'README.md') + with open(readme_dst, 'w') as f: + f.write(BORINGSSL_FOLDER_README) + + print('โœ… Updated to BoringSSL revision', BORINGSSL_REVISION) + + finally: + shutil.rmtree(tmp, ignore_errors=True) if __name__ == '__main__': - sys.exit(main()) + main()