From db4b70185f8a3b943866e35197bb3e51a80230c8 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Sat, 28 Jun 2025 22:22:14 +0530 Subject: [PATCH 01/10] feat: add script to update boringssl --- .github/workflows/update-boringssl.yml | 222 +++++++++++++++ lib/src/third_party/boringssl/ffigen.yaml | 38 +-- src/CMakeLists.txt | 9 +- tool/update-boringssl.py | 331 +++++++++------------- 4 files changed, 389 insertions(+), 211 deletions(-) create mode 100644 .github/workflows/update-boringssl.yml diff --git a/.github/workflows/update-boringssl.yml b/.github/workflows/update-boringssl.yml new file mode 100644 index 00000000..481bf7fc --- /dev/null +++ b/.github/workflows/update-boringssl.yml @@ -0,0 +1,222 @@ +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 Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - 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: Clone BoringSSL to get latest revision + id: get-revision + run: | + # Clone BoringSSL to a temporary directory to get the latest revision + TEMP_DIR=$(mktemp -d) + git clone https://boringssl.googlesource.com/boringssl "$TEMP_DIR/boringssl" + cd "$TEMP_DIR/boringssl" + + if [ -n "${{ github.event.inputs.boringssl_revision }}" ]; then + LATEST_REVISION="${{ github.event.inputs.boringssl_revision }}" + echo "Using manually specified revision: $LATEST_REVISION" + else + LATEST_REVISION=$(git rev-parse HEAD) + echo "Latest BoringSSL revision: $LATEST_REVISION" + fi + + # Get current revision from the Python script + CURRENT_REVISION=$(grep "BORINGSSL_REVISION = " tool/update-boringssl.py | cut -d"'" -f2) + echo "Current revision in script: $CURRENT_REVISION" + + echo "latest_revision=$LATEST_REVISION" >> $GITHUB_OUTPUT + echo "current_revision=$CURRENT_REVISION" >> $GITHUB_OUTPUT + + # Check if update is needed + if [ "$LATEST_REVISION" = "$CURRENT_REVISION" ]; then + echo "needs_update=false" >> $GITHUB_OUTPUT + echo "No update needed - already at latest revision" + else + echo "needs_update=true" >> $GITHUB_OUTPUT + echo "Update needed: $CURRENT_REVISION -> $LATEST_REVISION" + fi + + # Cleanup + rm -rf "$TEMP_DIR" + + - name: Update BoringSSL revision in script + if: steps.get-revision.outputs.needs_update == 'true' + run: | + # Update the BORINGSSL_REVISION in the Python script + sed -i "s/BORINGSSL_REVISION = '[^']*'/BORINGSSL_REVISION = '${{ steps.get-revision.outputs.latest_revision }}'/" tool/update-boringssl.py + + # Verify the change + echo "Updated revision in script:" + grep "BORINGSSL_REVISION = " tool/update-boringssl.py + + - name: Run BoringSSL update script + if: steps.get-revision.outputs.needs_update == 'true' + run: | + # Step 1: Clean up build artifacts + echo "๐Ÿงน Cleaning up build artifacts..." + bash ./tool/clean.sh + + # Step 2: Update BoringSSL sources + echo "๐Ÿ“ฆ Updating BoringSSL sources..." + python3 tool/update-boringssl.py + + # Step 3: Get Dart dependencies + echo "๐Ÿ“ฅ Getting Dart dependencies..." + dart pub get + + # Step 4: Generate symbols table + echo "๐Ÿ”ข Generating symbols table..." + dart run ./tool/generate_symbols_table.dart + + # Step 5: Update FFI bindings + echo "๐Ÿ”— Updating FFI bindings..." + bash ./tool/update-bindings.sh + + - name: Get BoringSSL commit info + if: steps.get-revision.outputs.needs_update == 'true' + id: boringssl-info + run: | + # Clone BoringSSL again to get commit information + TEMP_DIR=$(mktemp -d) + git clone https://boringssl.googlesource.com/boringssl "$TEMP_DIR/boringssl" + cd "$TEMP_DIR/boringssl" + git checkout ${{ steps.get-revision.outputs.latest_revision }} + + COMMIT_DATE=$(git show -s --format=%ci ${{ steps.get-revision.outputs.latest_revision }}) + COMMIT_SUBJECT=$(git show -s --format=%s ${{ steps.get-revision.outputs.latest_revision }}) + COMMIT_AUTHOR=$(git show -s --format=%an ${{ steps.get-revision.outputs.latest_revision }}) + SHORT_SHA=$(echo "${{ steps.get-revision.outputs.latest_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: Run tests + if: steps.get-revision.outputs.needs_update == 'true' + run: | + echo "๐Ÿงช Running tests to verify update..." + bash ./tool/test.sh + + - name: Check for changes + if: steps.get-revision.outputs.needs_update == 'true' + 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.get-revision.outputs.needs_update == 'true' && steps.changes.outputs.has_changes == 'true' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + Update BoringSSL to ${{ steps.boringssl-info.outputs.short_sha }} + + - Updated from ${{ steps.get-revision.outputs.current_revision }} to ${{ steps.get-revision.outputs.latest_revision }} + - Latest 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 the latest revision. + + ### Changes + - **From**: `${{ steps.get-revision.outputs.current_revision }}` + - **To**: `${{ steps.get-revision.outputs.latest_revision }}` + + ### Latest Commit Details + - **Subject**: ${{ steps.boringssl-info.outputs.commit_subject }} + - **Author**: ${{ steps.boringssl-info.outputs.commit_author }} + - **Date**: ${{ steps.boringssl-info.outputs.commit_date }} + - **SHA**: [${{ steps.boringssl-info.outputs.short_sha }}](https://boringssl.googlesource.com/boringssl/+/${{ steps.get-revision.outputs.latest_revision }}) + + ### What's Updated + - Updated `tool/update-boringssl.py` with new revision + - Refreshed BoringSSL source files and headers + - Updated CMake configuration files + - Regenerated symbols table and FFI bindings + - **Tests passed** โœ… (verified during update process) + + ### Testing Status + - [x] Build tests pass + - [x] Unit tests pass + - [x] Integration tests pass + - [ ] Manual verification on target platforms + + --- + + ๐Ÿค– This PR was created automatically by the Update BoringSSL workflow. + + **Review Guidelines:** + 1. Check that the build and tests pass + 2. Review any breaking changes in the BoringSSL changelog + 3. Test critical cryptographic operations + 4. Verify Windows compatibility (especially ECDH PKCS8 operations) + + branch: update-boringssl-${{ steps.boringssl-info.outputs.short_sha }} + branch-suffix: timestamp + delete-branch: true + labels: | + dependencies + automated-pr + boringssl-update + + - name: Summary + run: | + if [ "${{ steps.get-revision.outputs.needs_update }}" = "false" ]; then + echo "โœ… No update needed - already at latest BoringSSL revision" + elif [ "${{ steps.changes.outputs.has_changes }}" = "false" ]; then + echo "โ„น๏ธ Update script ran but no changes were detected" + else + echo "๐Ÿš€ Successfully created PR to update BoringSSL" + echo " From: ${{ steps.get-revision.outputs.current_revision }}" + echo " To: ${{ steps.get-revision.outputs.latest_revision }}" + fi 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/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() From 2fabe63c02a55da6410f818f8ffa9b206ced5546 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Sun, 27 Jul 2025 04:07:16 +0530 Subject: [PATCH 02/10] refactor: unifiy scripts and update workflow --- .github/workflows/update-boringssl.yml | 173 +++------ lib/src/impl_ffi/impl_ffi.utils.dart | 8 +- tool/REVISION | 1 + tool/bump-boringssl-revision.sh | 482 +++++++++++++++++++++++++ 4 files changed, 548 insertions(+), 116 deletions(-) create mode 100644 tool/REVISION create mode 100644 tool/bump-boringssl-revision.sh diff --git a/.github/workflows/update-boringssl.yml b/.github/workflows/update-boringssl.yml index 481bf7fc..1b21339a 100644 --- a/.github/workflows/update-boringssl.yml +++ b/.github/workflows/update-boringssl.yml @@ -25,11 +25,6 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Set up Dart uses: dart-lang/setup-dart@v1 with: @@ -40,88 +35,38 @@ jobs: git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - - name: Clone BoringSSL to get latest revision - id: get-revision + - name: Run BoringSSL update + id: update run: | - # Clone BoringSSL to a temporary directory to get the latest revision - TEMP_DIR=$(mktemp -d) - git clone https://boringssl.googlesource.com/boringssl "$TEMP_DIR/boringssl" - cd "$TEMP_DIR/boringssl" - + # Run the BoringSSL update script with dry-run first to get info if [ -n "${{ github.event.inputs.boringssl_revision }}" ]; then - LATEST_REVISION="${{ github.event.inputs.boringssl_revision }}" - echo "Using manually specified revision: $LATEST_REVISION" + REVISION="${{ github.event.inputs.boringssl_revision }}" + echo "Using specified revision: $REVISION" else - LATEST_REVISION=$(git rev-parse HEAD) - echo "Latest BoringSSL revision: $LATEST_REVISION" + REVISION="" + echo "Using latest revision" fi - # Get current revision from the Python script - CURRENT_REVISION=$(grep "BORINGSSL_REVISION = " tool/update-boringssl.py | cut -d"'" -f2) - echo "Current revision in script: $CURRENT_REVISION" - - echo "latest_revision=$LATEST_REVISION" >> $GITHUB_OUTPUT - echo "current_revision=$CURRENT_REVISION" >> $GITHUB_OUTPUT + # Run the update script + bash ./tool/bump-boringssl-revision.sh $REVISION - # Check if update is needed - if [ "$LATEST_REVISION" = "$CURRENT_REVISION" ]; then - echo "needs_update=false" >> $GITHUB_OUTPUT - echo "No update needed - already at latest revision" - else - echo "needs_update=true" >> $GITHUB_OUTPUT - echo "Update needed: $CURRENT_REVISION -> $LATEST_REVISION" - fi - - # Cleanup - rm -rf "$TEMP_DIR" - - - name: Update BoringSSL revision in script - if: steps.get-revision.outputs.needs_update == 'true' - run: | - # Update the BORINGSSL_REVISION in the Python script - sed -i "s/BORINGSSL_REVISION = '[^']*'/BORINGSSL_REVISION = '${{ steps.get-revision.outputs.latest_revision }}'/" tool/update-boringssl.py - - # Verify the change - echo "Updated revision in script:" - grep "BORINGSSL_REVISION = " tool/update-boringssl.py - - - name: Run BoringSSL update script - if: steps.get-revision.outputs.needs_update == 'true' - run: | - # Step 1: Clean up build artifacts - echo "๐Ÿงน Cleaning up build artifacts..." - bash ./tool/clean.sh - - # Step 2: Update BoringSSL sources - echo "๐Ÿ“ฆ Updating BoringSSL sources..." - python3 tool/update-boringssl.py - - # Step 3: Get Dart dependencies - echo "๐Ÿ“ฅ Getting Dart dependencies..." - dart pub get - - # Step 4: Generate symbols table - echo "๐Ÿ”ข Generating symbols table..." - dart run ./tool/generate_symbols_table.dart - - # Step 5: Update FFI bindings - echo "๐Ÿ”— Updating FFI bindings..." - bash ./tool/update-bindings.sh + # 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 - if: steps.get-revision.outputs.needs_update == 'true' id: boringssl-info run: | - # Clone BoringSSL again to get commit information + # 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.get-revision.outputs.latest_revision }} + git checkout ${{ steps.update.outputs.new_revision }} - COMMIT_DATE=$(git show -s --format=%ci ${{ steps.get-revision.outputs.latest_revision }}) - COMMIT_SUBJECT=$(git show -s --format=%s ${{ steps.get-revision.outputs.latest_revision }}) - COMMIT_AUTHOR=$(git show -s --format=%an ${{ steps.get-revision.outputs.latest_revision }}) - SHORT_SHA=$(echo "${{ steps.get-revision.outputs.latest_revision }}" | cut -c1-8) + 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 @@ -131,14 +76,7 @@ jobs: # Cleanup rm -rf "$TEMP_DIR" - - name: Run tests - if: steps.get-revision.outputs.needs_update == 'true' - run: | - echo "๐Ÿงช Running tests to verify update..." - bash ./tool/test.sh - - name: Check for changes - if: steps.get-revision.outputs.needs_update == 'true' id: changes run: | if git diff --quiet; then @@ -151,56 +89,64 @@ jobs: fi - name: Create Pull Request - if: steps.get-revision.outputs.needs_update == 'true' && steps.changes.outputs.has_changes == 'true' + if: steps.changes.outputs.has_changes == 'true' uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: | - Update BoringSSL to ${{ steps.boringssl-info.outputs.short_sha }} + chore: Update BoringSSL to ${{ steps.boringssl-info.outputs.short_sha }} - - Updated from ${{ steps.get-revision.outputs.current_revision }} to ${{ steps.get-revision.outputs.latest_revision }} - - Latest commit: ${{ steps.boringssl-info.outputs.commit_subject }} + 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 the latest revision. - - ### Changes - - **From**: `${{ steps.get-revision.outputs.current_revision }}` - - **To**: `${{ steps.get-revision.outputs.latest_revision }}` + This PR updates BoringSSL to revision **${{ steps.boringssl-info.outputs.short_sha }}**. - ### Latest Commit Details - - **Subject**: ${{ steps.boringssl-info.outputs.commit_subject }} + ### ๐Ÿ“‹ 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 }} - - **SHA**: [${{ steps.boringssl-info.outputs.short_sha }}](https://boringssl.googlesource.com/boringssl/+/${{ steps.get-revision.outputs.latest_revision }}) - ### What's Updated - - Updated `tool/update-boringssl.py` with new revision - - Refreshed BoringSSL source files and headers - - Updated CMake configuration files - - Regenerated symbols table and FFI bindings - - **Tests passed** โœ… (verified during update process) + ### ๐Ÿ”ง 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 - ### Testing Status - - [x] Build tests pass - - [x] Unit tests pass - - [x] Integration tests pass - - [ ] Manual verification on target platforms + ### ๐Ÿ“ 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 --- - ๐Ÿค– This PR was created automatically by the Update BoringSSL workflow. + ๐Ÿค– **Automated by**: Update BoringSSL workflow **Review Guidelines:** - 1. Check that the build and tests pass - 2. Review any breaking changes in the BoringSSL changelog - 3. Test critical cryptographic operations - 4. Verify Windows compatibility (especially ECDH PKCS8 operations) + 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 @@ -208,15 +154,14 @@ jobs: dependencies automated-pr boringssl-update + security - name: Summary run: | - if [ "${{ steps.get-revision.outputs.needs_update }}" = "false" ]; then - echo "โœ… No update needed - already at latest BoringSSL revision" - elif [ "${{ steps.changes.outputs.has_changes }}" = "false" ]; then - echo "โ„น๏ธ Update script ran but no changes were detected" + 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 " From: ${{ steps.get-revision.outputs.current_revision }}" - echo " To: ${{ steps.get-revision.outputs.latest_revision }}" + 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/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 "$@" From 80203b803c4e0bca69182348280dcb907ebac725 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Wed, 7 Jan 2026 18:54:29 +0530 Subject: [PATCH 03/10] chore: remove old py script --- tool/update-boringssl.py | 246 --------------------------------------- 1 file changed, 246 deletions(-) delete mode 100755 tool/update-boringssl.py diff --git a/tool/update-boringssl.py b/tool/update-boringssl.py deleted file mode 100755 index 22894c17..00000000 --- a/tool/update-boringssl.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env python3 - -# 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. - -# Roll BoringSSL into third_party/boringssl/ -# Remember to bump BORINGSSL_REVISION. - -import os -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 = '78b48c1f2a973ff0a4ed18b9618d533101bd4144' - -FILES_TO_RETAIN = [ - 'README.md', - 'LICENSE', - 'INCORPORATING.md', -] - -BORINGSSL_FOLDER_README = """# 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/update-boringssl.py` 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/ -""" - -SOURCES_CMAKE_HEADER = """# 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/update-boringssl.py` -""" - -FAKE_DARWIN_SOURCE_HEADER = """/* - * 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/update-boringssl.py` -""" - - - -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) - - # 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) - - # 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('git') is None: - print('Error: git not on PATH', file=sys.stderr) - sys.exit(1) - - tmp = tempfile.mkdtemp(prefix='update-boringssl-') - try: - print('Cleaning up old boringssl/') - cleanup() - - 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__': - main() From 826066ba55633a9ee21e12cf8fd795f32fa9ab8b Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Thu, 29 Jan 2026 07:58:06 +0530 Subject: [PATCH 04/10] refactor: updated CBB allocation and boringssl verification --- lib/src/boringssl/bindings/ffigen.yaml | 1 + .../bindings/generated_bindings.dart | 11 ++++++++ lib/src/impl_ffi/impl_ffi.utils.dart | 8 +++--- src/webcrypto.c | 7 +++++ src/webcrypto.h | 5 +++- tool/bump-boringssl-revision.sh | 27 ++++--------------- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/lib/src/boringssl/bindings/ffigen.yaml b/lib/src/boringssl/bindings/ffigen.yaml index 04ece16d..d827d9a7 100644 --- a/lib/src/boringssl/bindings/ffigen.yaml +++ b/lib/src/boringssl/bindings/ffigen.yaml @@ -22,6 +22,7 @@ structs: functions: include: - webcrypto_lookup_symbol + - webcrypto_get_CBB_size preamble: | // Copyright 2021 Google LLC // diff --git a/lib/src/boringssl/bindings/generated_bindings.dart b/lib/src/boringssl/bindings/generated_bindings.dart index 0db16a7f..fa2ccc7a 100644 --- a/lib/src/boringssl/bindings/generated_bindings.dart +++ b/lib/src/boringssl/bindings/generated_bindings.dart @@ -37,6 +37,17 @@ class WebCrypto { lookup) : _lookup = lookup; + /// Helper function to get the size of CBB structure for FFI allocation + int webcrypto_get_CBB_size() { + return _webcrypto_get_CBB_size(); + } + + late final _webcrypto_get_CBB_sizePtr = + _lookup>( + 'webcrypto_get_CBB_size'); + late final _webcrypto_get_CBB_size = + _webcrypto_get_CBB_sizePtr.asFunction(); + /// Function to lookup BoringSSL symbols based on index in the Sym enum. /// See src/symbols.yaml for details. ffi.Pointer webcrypto_lookup_symbol( diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index a323c99f..d15dc2d2 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -326,9 +326,11 @@ extension on _Scope { } ffi.Pointer createCBB([int sizeHint = 4096]) { - // 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(); + // Get the actual size of CBB structure from native code + // This ensures we allocate exactly the right amount of memory + // regardless of platform (32-bit, 64-bit, ARM, x86, etc.) + final cbbSize = ssl.webcrypto_get_CBB_size(); + final cbb = allocate(cbbSize).cast(); ssl.CBB_zero(cbb); _checkOp(ssl.CBB_init(cbb, sizeHint) == 1, fallback: 'allocation failure'); defer(() => ssl.CBB_cleanup(cbb)); diff --git a/src/webcrypto.c b/src/webcrypto.c index 96d1726f..76b59282 100644 --- a/src/webcrypto.c +++ b/src/webcrypto.c @@ -22,3 +22,10 @@ WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index) { return _webcrypto_symbol_table[index]; } + +// Helper function to get the actual size of CBB structure +// This allows Dart FFI to allocate the correct amount of memory +// without hardcoding platform-specific sizes +WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void) { + return sizeof(CBB); +} diff --git a/src/webcrypto.h b/src/webcrypto.h index 707a9a09..d58dd1dc 100644 --- a/src/webcrypto.h +++ b/src/webcrypto.h @@ -26,4 +26,7 @@ // Function to lookup BoringSSL symbols based on index in the Sym enum. // See src/symbols.yaml for details. -WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index); \ No newline at end of file +WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index); + +// Helper function to get the size of CBB structure for FFI allocation +WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void); diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh index f55fb59b..775a672a 100644 --- a/tool/bump-boringssl-revision.sh +++ b/tool/bump-boringssl-revision.sh @@ -164,24 +164,6 @@ git_clone_boringssl() { 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" @@ -426,12 +408,13 @@ main() { # Check if update is needed if [ "$current_revision" = "$TARGET_REVISION" ]; then - log_info "No update needed - already at revision $TARGET_REVISION" - return 0 + log_info "Already at revision $TARGET_REVISION - verifying with git diff" + # Don't abort - continue to verify the files are actually correct + # Running the update will be a no-op if truly at the right revision + else + log_info "Update needed: $current_revision -> $TARGET_REVISION" 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" From cb28fd7e1a37c1684097c82300ae565fcc61bc66 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Sat, 2 May 2026 23:39:29 +0530 Subject: [PATCH 05/10] refactor: modernize BoringSSL roll tooling for native hooks --- .github/workflows/update-boringssl.yml | 185 +++------ lib/src/boringssl/lookup/lookup.dart | 7 +- lib/src/boringssl/lookup/utils.dart | 6 +- lib/src/impl_ffi/impl_ffi.dart | 3 +- lib/src/impl_ffi/impl_ffi.utils.dart | 2 +- lib/src/third_party/boringssl/ffigen.yaml | 38 +- src/CMakeLists.txt | 23 +- third_party/boringssl/README.md | 6 +- third_party/boringssl/sources.cmake | 2 +- tool/bump-boringssl-revision.sh | 447 +++++++++------------- tool/clean.sh | 27 +- tool/test.sh | 8 +- tool/update-bindings.sh | 6 +- 13 files changed, 314 insertions(+), 446 deletions(-) diff --git a/.github/workflows/update-boringssl.yml b/.github/workflows/update-boringssl.yml index 1b21339a..7c00ff05 100644 --- a/.github/workflows/update-boringssl.yml +++ b/.github/workflows/update-boringssl.yml @@ -1,167 +1,96 @@ -name: Update BoringSSL +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)' + revision: + description: Optional BoringSSL revision SHA. Leave empty for latest. required: false type: string + schedule: + - cron: '0 9 * * 1' + +permissions: + contents: write + pull-requests: write + +concurrency: + group: update-boringssl + cancel-in-progress: false jobs: - update-boringssl: + roll: + name: Roll BoringSSL runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - + timeout-minutes: 60 steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - - name: Set up Dart - uses: dart-lang/setup-dart@v1 + - uses: subosito/flutter-action@v2 with: - sdk: stable + channel: stable + cache: true - - name: Set up Git + - name: Install system dependencies run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' + sudo apt-get update -y + sudo apt-get install -y jq ninja-build libgtk-3-dev + flutter config --no-analytics - - name: Run BoringSSL update - id: update + - name: Configure git 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" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Run BoringSSL roll script + id: roll + shell: bash + run: | + set -euo pipefail + revision='${{ github.event.inputs.revision || '' }}' + if [[ -n "$revision" ]]; then + bash ./tool/bump-boringssl-revision.sh "$revision" else - REVISION="" - echo "Using latest revision" + bash ./tool/bump-boringssl-revision.sh 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" + new_revision="$(tr -d ' \t\n\r' < tool/REVISION)" + echo "revision=$new_revision" >> "$GITHUB_OUTPUT" + echo "short_revision=${new_revision:0:8}" >> "$GITHUB_OUTPUT" - name: Check for changes id: changes + shell: bash run: | if git diff --quiet; then - echo "has_changes=false" >> $GITHUB_OUTPUT - echo "No changes detected after running update script" + echo "changed=false" >> "$GITHUB_OUTPUT" else - echo "has_changes=true" >> $GITHUB_OUTPUT - echo "Changes detected:" - git diff --name-status + echo "changed=true" >> "$GITHUB_OUTPUT" fi - - name: Create Pull Request - if: steps.changes.outputs.has_changes == 'true' - uses: peter-evans/create-pull-request@v5 + - name: Create pull request + if: steps.changes.outputs.changed == 'true' + uses: peter-evans/create-pull-request@v7 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 + branch: "update-boringssl-${{ steps.roll.outputs.short_revision }}" delete-branch: true + commit-message: "chore: update BoringSSL to ${{ steps.roll.outputs.short_revision }}" + title: "chore: update BoringSSL to ${{ steps.roll.outputs.short_revision }}" + body: | + Updates vendored BoringSSL to `${{ steps.roll.outputs.revision }}` using `tool/bump-boringssl-revision.sh`. + + This workflow is intentionally thin: it just provisions the environment, runs the script, and opens a PR if the script produced a diff. labels: | dependencies - automated-pr boringssl-update - security - name: Summary + shell: bash run: | - if [ "${{ steps.changes.outputs.has_changes }}" = "false" ]; then - echo "โ„น๏ธ No changes detected - BoringSSL is already up to date" + if [[ '${{ steps.changes.outputs.changed }}' == 'true' ]]; then + echo "Created or updated a PR for BoringSSL revision ${{ steps.roll.outputs.revision }}" else - echo "๐Ÿš€ Successfully created PR to update BoringSSL" - echo " Revision: ${{ steps.update.outputs.new_revision }}" - echo " Commit: ${{ steps.boringssl-info.outputs.commit_subject }}" + echo "No vendored changes were produced for BoringSSL revision ${{ steps.roll.outputs.revision }}" fi diff --git a/lib/src/boringssl/lookup/lookup.dart b/lib/src/boringssl/lookup/lookup.dart index 40c9bc9f..9e689aa4 100644 --- a/lib/src/boringssl/lookup/lookup.dart +++ b/lib/src/boringssl/lookup/lookup.dart @@ -16,6 +16,7 @@ import 'dart:ffi'; +import '../bindings/generated_bindings.dart' show WebCrypto; import '../../third_party/boringssl/generated_bindings.dart'; import 'symbols.generated.dart'; @@ -28,8 +29,7 @@ export 'symbols.generated.dart' show Sym; ) external Pointer _nativeWebcryptoLookupSymbol(int index); -/// Resolve lookup from native assets first, then fall back to the legacy -/// runtime loading strategy used by `flutter pub run webcrypto:setup`. +/// Resolve lookup from the bundled native asset for `package:webcrypto`. Pointer lookup(String symbolName) { final sym = symFromString(symbolName); return _nativeWebcryptoLookupSymbol(sym.index).cast(); @@ -38,6 +38,9 @@ Pointer lookup(String symbolName) { /// Gives access to BoringSSL symbols. final BoringSsl ssl = BoringSsl.fromLookup(lookup); +/// Gives access to helper symbols exported by src/webcrypto.h. +final WebCrypto webcrypto = WebCrypto.fromLookup(lookup); + /// ERR_GET_LIB returns the library code for the error. This is one of the /// ERR_LIB_* values. /// diff --git a/lib/src/boringssl/lookup/utils.dart b/lib/src/boringssl/lookup/utils.dart index cb956ffc..457df6f2 100644 --- a/lib/src/boringssl/lookup/utils.dart +++ b/lib/src/boringssl/lookup/utils.dart @@ -69,9 +69,9 @@ lookupLibraryInDotDartTool() { /// Find the `.dart_tool/` folder, returns `null` if unable to find it. Uri? _findDotDartTool() { - // HACK: We have no good mechanism for finding the library created by: - // flutter pub run webcrypto:setup - // So we search relative to the script path and CWD. + // HACK: We have no good mechanism for finding the legacy + // `.dart_tool/webcrypto/` output, so we search relative to the script path + // and CWD. // Find script directory Uri root = Platform.script.resolve('./'); diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index 7f985909..d41a05f6 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -29,7 +29,8 @@ import 'package:webcrypto/src/third_party/boringssl/generated_bindings.dart'; import '../jsonwebkey.dart' show JsonWebKey; import '../webcrypto/webcrypto.dart'; import '../impl_interface/impl_interface.dart'; -import '../boringssl/lookup/lookup.dart' show ssl, ERR_GET_LIB, ERR_GET_REASON; +import '../boringssl/lookup/lookup.dart' + show ssl, webcrypto, ERR_GET_LIB, ERR_GET_REASON; part 'impl_ffi.aescbc.dart'; part 'impl_ffi.aesctr.dart'; diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index 28c14dda..fe5c0044 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -318,7 +318,7 @@ extension on _Scope { // Get the actual size of CBB structure from native code // This ensures we allocate exactly the right amount of memory // regardless of platform (32-bit, 64-bit, ARM, x86, etc.) - final cbbSize = ssl.webcrypto_get_CBB_size(); + final cbbSize = webcrypto.webcrypto_get_CBB_size(); final cbb = allocate(cbbSize).cast(); ssl.CBB_zero(cbb); _checkOp(ssl.CBB_init(cbb, sizeHint) == 1, fallback: 'allocation failure'); diff --git a/lib/src/third_party/boringssl/ffigen.yaml b/lib/src/third_party/boringssl/ffigen.yaml index 8c28b866..ce721aa4 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/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' + - '../../../../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.h' + - '../../../../third_party/boringssl/src/include/openssl/ecdh.h' + - '../../../../third_party/boringssl/src/include/openssl/ec_key.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' comments: style: any length: full diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 849133fc..a5a7e283 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,14 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Build script used by `flutter pub run webcrypto:setup` to create -# `.dart_tool/webcrypto/webcrypto.{so|dylib|dll}` for use by `flutter test`. -# -# When running as a plugin platform specific build scripts will be used: -# - `android/CMakeLists.txt`, for Android, -# - TODO: `ios/podspec...` -# -# This script is very similar to platform specific build scripts. +# Build script used by native hooks and local tooling to create the shared +# `webcrypto` library for the current host platform. cmake_minimum_required(VERSION 3.10.0) project(webcrypto) @@ -33,7 +27,7 @@ enable_language(ASM) # Set as required by ../third_party/boringssl/sources.cmake included below set(BORINGSSL_ROOT ../third_party/boringssl/) -# Import sources as generated by tool/update-boringssl.py +# Import sources as generated by tool/bump-boringssl-revision.sh # This provides variables, and requires BORINGSSL_ROOT to be set. # - crypto_sources # - crypto_sources_linux_aarch64 @@ -41,8 +35,11 @@ set(BORINGSSL_ROOT ../third_party/boringssl/) # - crypto_sources_linux_ppc64le # - crypto_sources_linux_x86 # - crypto_sources_linux_x86_64 -# - crypto_sources_mac_x86 -# - crypto_sources_mac_x86_64 +# - crypto_sources_apple_aarch64 +# - crypto_sources_apple_arm +# - crypto_sources_apple_x86 +# - crypto_sources_apple_x86_64 +# - crypto_sources_win_aarch64 # - crypto_sources_win_x86 # - crypto_sources_win_x86_64 include( @@ -89,7 +86,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") set(PLATFORM "win") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR ${CMAKE_SYSTEM_NAME} STREQUAL "iOS") - set(PLATFORM "mac") + set(PLATFORM "apple") else() # Assume we're on linux or similar platform set(PLATFORM "linux") @@ -159,7 +156,7 @@ target_include_directories( PRIVATE - ../third_party/boringssl/include/ + ../third_party/boringssl/src/include/ ) set_target_properties( diff --git a/third_party/boringssl/README.md b/third_party/boringssl/README.md index 1e5a5788..3d6ce1bc 100644 --- a/third_party/boringssl/README.md +++ b/third_party/boringssl/README.md @@ -3,9 +3,9 @@ **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/update-boringssl.py` which utilizes scripts and procedures from -`src/INCORPORATING.md` to faciliate embedding of libcrypto from BoringSSL. +incorporate libcrypto from BoringSSL. Contents of this folder are generated +using `tool/bump-boringssl-revision.sh`, following BoringSSL's +`INCORPORATING.md` guidance. Files in this folder are subject to `LICENSE` from the BoringSSL project. diff --git a/third_party/boringssl/sources.cmake b/third_party/boringssl/sources.cmake index 8b2cc7fe..1edab490 100644 --- a/third_party/boringssl/sources.cmake +++ b/third_party/boringssl/sources.cmake @@ -15,7 +15,7 @@ # **GENERATED FILE DO NOT MODIFY** # # This file is generated using: -# `tool/update-boringssl.py` +# `tool/bump-boringssl-revision.sh` set(crypto_sources ${BORINGSSL_ROOT}err_data.c diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh index 775a672a..fc83d578 100644 --- a/tool/bump-boringssl-revision.sh +++ b/tool/bump-boringssl-revision.sh @@ -14,51 +14,42 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e +set -euo pipefail -# Script to update BoringSSL revision and regenerate all necessary files +# 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 "" +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + cat <<'EOF' +Usage: ./tool/bump-boringssl-revision.sh [revision] [--dry-run] + +Updates BoringSSL to the specified revision or latest if no revision is given. + +Arguments: + revision Optional. Specific BoringSSL revision (SHA) to update to. + --dry-run Optional. Report the target revision without changing files. +EOF exit 0 fi -# Check for dry-run mode DRY_RUN=false TARGET_REVISION="" -# Parse arguments while [[ $# -gt 0 ]]; do - case $1 in + case "$1" in --dry-run) DRY_RUN=true shift ;; -*) - echo "Unknown option: $1" - echo "Use --help for usage information" + echo "Unknown option: $1" >&2 exit 1 ;; *) - if [ -z "$TARGET_REVISION" ]; then + if [[ -z "$TARGET_REVISION" ]]; then TARGET_REVISION="$1" else - echo "Multiple revisions specified. Use --help for usage information" + echo "Multiple revisions specified" >&2 exit 1 fi shift @@ -71,27 +62,16 @@ 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}" + echo "[info] $1" } log_success() { - echo -e "${GREEN}โœ… $1${NC}" -} - -log_warning() { - echo -e "${YELLOW}โš ๏ธ $1${NC}" + echo "[ok] $1" } log_error() { - echo -e "${RED}โŒ $1${NC}" + echo "[error] $1" >&2 } section() { @@ -100,79 +80,120 @@ section() { 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' + if [[ -f "$REVISION_FILE" ]]; then + tr -d ' \t\n\r' < "$REVISION_FILE" 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" +check_command() { + local command="$1" + local hint="$2" + if ! command -v "$command" >/dev/null 2>&1; then + log_error "$hint" exit 1 fi } -# Function to create directories mkdirp() { local path="$1" - if [ ! -d "$path" ]; then + if [[ ! -d "$path" ]]; then mkdir -p "$path" fi } -# Function to cleanup old BoringSSL files cleanup_boringssl() { + local path="$ROOT/third_party/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 + rm -rf "$path" + mkdirp "$path" } -# 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 + local target="$temp_dir/boringssl" + + log_info "Cloning BoringSSL repository..." + git clone "$BORINGSSL_REPOSITORY" "$target" >/dev/null 2>&1 + log_info "Checking out revision: $revision" + git -C "$target" checkout --detach "$revision" >/dev/null 2>&1 echo "$target" } -# Function to write sources.cmake +write_build_manifest() { + local src_root="$1" + local manifest="$2" + + python3 - "$src_root" > "$manifest" <<'PY' +import json +import os +import sys + + +class Capture: + def WriteFiles(self, file_sets, asm_outputs): + self.file_sets = file_sets + self.asm_outputs = asm_outputs + + +boringssl_root = os.path.abspath(sys.argv[1]) +sys.path.insert(0, os.path.join(boringssl_root, "util")) +import generate_build_files # type: ignore + +capture = Capture() +cwd = os.getcwd() +try: + os.chdir(boringssl_root) + generate_build_files.EMBED_TEST_DATA = False + generate_build_files.main([capture]) +finally: + os.chdir(cwd) + +payload = { + "crypto_sources": sorted(capture.file_sets["crypto"]), + "crypto_headers": sorted(capture.file_sets["crypto_headers"]), + "crypto_internal_headers": sorted(capture.file_sets["crypto_internal_headers"]), + "fips_fragments": sorted(capture.file_sets["fips_fragments"]), + "asm_outputs": { + f"{osname}_{arch}": sorted(files) + for (osname, arch), files in capture.asm_outputs + }, +} +json.dump(payload, sys.stdout, indent=2, sort_keys=True) +sys.stdout.write("\n") +PY +} + +prefix_src_tree_path() { + local path="$1" + if [[ "$path" == */* ]]; then + echo "src/$path" + else + echo "$path" + fi +} + write_sources_cmake() { - local sources="$1" - local asms="$2" + local manifest="$1" local dest="$ROOT/third_party/boringssl/sources.cmake" - + log_info "Writing sources.cmake..." - - cat > "$dest" << 'EOF' + + cat > "$dest" <<'EOF' # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -193,133 +214,86 @@ write_sources_cmake() { # `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" + jq -r '.crypto_sources[]' "$manifest" | while read -r file; do + echo " \${BORINGSSL_ROOT}$(prefix_src_tree_path "$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" + + jq -r '.asm_outputs | keys[]' "$manifest" | while read -r key; do + echo "" >> "$dest" + echo "set(crypto_sources_$key" >> "$dest" + jq -r --arg key "$key" '.asm_outputs[$key][]' "$manifest" | while read -r file; do + echo " \${BORINGSSL_ROOT}$file" >> "$dest" + done + echo ")" >> "$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 +copy_manifest_group() { + local manifest="$1" + local jq_selector="$2" + local src_root="$3" + local dest_root="$4" + + jq -r "$jq_selector" "$manifest" | while read -r file; do + if [[ -n "$file" ]]; then local src="$src_root/$file" - local dst="$dest_root/$file" + local dst="$dest_root/$(prefix_src_tree_path "$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 +} + +copy_asm_outputs() { + local manifest="$1" + local src_root="$2" + local dest_root="$3" + + jq -r '.asm_outputs[]?[]' "$manifest" | 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 +} + +copy_sources() { + local manifest="$1" + local src_root="$2" + local dest_root="$ROOT/third_party/boringssl" + + log_info "Copying BoringSSL sources..." + copy_manifest_group "$manifest" '.crypto_headers[]' "$src_root" "$dest_root" + copy_manifest_group "$manifest" '.crypto_sources[]' "$src_root" "$dest_root" + copy_manifest_group "$manifest" '.crypto_internal_headers[]' "$src_root" "$dest_root" + copy_manifest_group "$manifest" '.fips_fragments[]' "$src_root" "$dest_root" + copy_asm_outputs "$manifest" "$src_root" "$dest_root" + log_info "Copying root files..." for file in README.md LICENSE INCORPORATING.md; do - if [ -f "$src_root/$file" ]; then + 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' + + 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. +incorporate libcrypto from BoringSSL. Contents of this folder are generated +using `tool/bump-boringssl-revision.sh`, following BoringSSL's +`INCORPORATING.md` guidance. Files in this folder are subject to `LICENSE` from the BoringSSL project. @@ -332,134 +306,93 @@ files and information about BoringSSL refer to the [BoringSSL repository][1]. EOF } -# Main BoringSSL update function update_boringssl_sources() { local revision="$1" - local temp_dir=$(mktemp -d) - + local temp_dir + temp_dir=$(mktemp -d) + local manifest="$temp_dir/build-files.json" + 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" + local src_root + src_root=$(git_clone_boringssl "$revision" "$temp_dir") + + log_info "Enumerating source files using upstream generate_build_files.py" + write_build_manifest "$src_root" "$manifest" + + local source_count + source_count=$(jq '.crypto_sources | length' "$manifest") + local internal_header_count + internal_header_count=$(jq '(.crypto_internal_headers | length) + (.fips_fragments | length)' "$manifest") + local asm_count + asm_count=$(jq '[.asm_outputs[] | length] | add // 0' "$manifest") + log_info "Found $source_count source files, $internal_header_count internal headers, and $asm_count assembly files" + + write_sources_cmake "$manifest" + copy_sources "$manifest" "$src_root" write_boringssl_readme - + rm -rf "$temp_dir" - - log_success "Updated to BoringSSL revision $revision" + log_success "Updated vendored BoringSSL sources" } -# Main execution function main() { - local current_revision=$(get_current_revision) - + local current_revision + current_revision=$(get_current_revision) log_info "Current BoringSSL revision: $current_revision" - - # Determine target revision - if [ -n "$TARGET_REVISION" ]; then + + 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 "Already at revision $TARGET_REVISION - verifying with git diff" - # Don't abort - continue to verify the files are actually correct - # Running the update will be a no-op if truly at the right revision + + if [[ "$current_revision" == "$TARGET_REVISION" ]]; then + log_info "Already at revision $TARGET_REVISION; regenerating files to verify the checkout matches the recorded revision" else log_info "Update needed: $current_revision -> $TARGET_REVISION" fi - - # If dry-run mode, just report the status - if [ "$DRY_RUN" = true ]; then + + 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 + + check_command git "git is not installed or not in PATH" + check_command dart "dart is required to regenerate bindings and run tests" + check_command jq "jq is required to parse generated BoringSSL source metadata" + check_command python3 "python3 is required to enumerate BoringSSL source files" + 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'..." + log_info "Running 'dart pub get --no-example'..." cd "$ROOT" - dart pub get - - # Step 5: Generate symbols table + dart pub get --no-example + 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_success "BoringSSL update completed successfully" log_info "Updated from $current_revision to $TARGET_REVISION" } -# Run main function with all arguments -main "$@" +main "$@" diff --git a/tool/clean.sh b/tool/clean.sh index d0b04a9b..47356965 100755 --- a/tool/clean.sh +++ b/tool/clean.sh @@ -17,18 +17,21 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" ROOT="$DIR/.." -# Remove all the generated files -# Not removing .cxx/ and .gradle/ can cause problems when jumping between -# flutter versions. +# Remove generated build artifacts for the root Dart package and the Flutter +# example app. The root package is no longer a Flutter project, so `flutter +# clean` only applies inside example/. -cd "$ROOT" -flutter clean +rm -rf "$ROOT/.dart_tool/" +rm -rf "$ROOT/build/" -cd "$ROOT/example/" -flutter clean - -cd "$ROOT" -rm -rf android/.cxx/ -rm -rf example/android/.gradle/ -rm -f example/.packages +if [ -d "$ROOT/example/" ]; then + ( + cd "$ROOT/example/" + flutter clean + ) + rm -rf "$ROOT/example/.dart_tool/" + rm -rf "$ROOT/example/build/" + rm -rf "$ROOT/example/android/.gradle/" + rm -f "$ROOT/example/.packages" +fi diff --git a/tool/test.sh b/tool/test.sh index e75c0ae9..ecd56ec1 100755 --- a/tool/test.sh +++ b/tool/test.sh @@ -21,15 +21,17 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cd "$DIR/.." -# We use 'flutter pub get' because example/ requires flutter! -section 'Running "flutter pub get"' -flutter pub get +section 'Running "dart pub get --no-example"' +dart pub get --no-example section 'dart test (vm,chrome,firefox)' xvfb-run dart test -p vm,chrome,firefox cd "$DIR/../example" +section 'Running "flutter pub get" in example/' +flutter pub get + # List devices and run integration tests on each device (if available) # Skip "chrome" because it's not supported by integration test system. DEVICE_IDS=$(flutter devices --machine | grep '"sdk"') diff --git a/tool/update-bindings.sh b/tool/update-bindings.sh index cfdca997..9ff4da9e 100755 --- a/tool/update-bindings.sh +++ b/tool/update-bindings.sh @@ -20,7 +20,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cd "$DIR/.." -flutter pub get +dart pub get --no-example -flutter pub run ffigen --config=lib/src/boringssl/bindings/ffigen.yaml -flutter pub run ffigen --config=lib/src/third_party/boringssl/ffigen.yaml +dart run ffigen --config=lib/src/boringssl/bindings/ffigen.yaml +dart run ffigen --config=lib/src/third_party/boringssl/ffigen.yaml From 12d1ea7785d54edd4fadc9a37af0d3fbc0aacad3 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Sat, 2 May 2026 23:56:21 +0530 Subject: [PATCH 06/10] fix: format bindings and include stddef for CBB helper --- lib/src/boringssl/bindings/generated_bindings.dart | 7 ++++--- src/webcrypto.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/boringssl/bindings/generated_bindings.dart b/lib/src/boringssl/bindings/generated_bindings.dart index 0df4ab64..0e7e783a 100644 --- a/lib/src/boringssl/bindings/generated_bindings.dart +++ b/lib/src/boringssl/bindings/generated_bindings.dart @@ -43,9 +43,10 @@ class WebCrypto { late final _webcrypto_get_CBB_sizePtr = _lookup>( - 'webcrypto_get_CBB_size'); - late final _webcrypto_get_CBB_size = - _webcrypto_get_CBB_sizePtr.asFunction(); + 'webcrypto_get_CBB_size', + ); + late final _webcrypto_get_CBB_size = _webcrypto_get_CBB_sizePtr + .asFunction(); /// Function to lookup BoringSSL symbols based on index in the Sym enum. /// See src/symbols.yaml for details. diff --git a/src/webcrypto.h b/src/webcrypto.h index d58dd1dc..408c02d6 100644 --- a/src/webcrypto.h +++ b/src/webcrypto.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include // Macro for annotating all functions to be exported From 0bdf2e681d2d2f2a50db4dd075639211d0fbabed Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Sun, 3 May 2026 00:04:51 +0530 Subject: [PATCH 07/10] fix: revert obsolete native FFI changes --- lib/src/boringssl/bindings/ffigen.yaml | 1 - lib/src/boringssl/bindings/generated_bindings.dart | 12 ------------ lib/src/boringssl/lookup/lookup.dart | 4 ---- lib/src/impl_ffi/impl_ffi.dart | 3 +-- lib/src/impl_ffi/impl_ffi.utils.dart | 10 ++-------- src/webcrypto.c | 7 ------- src/webcrypto.h | 4 ---- 7 files changed, 3 insertions(+), 38 deletions(-) diff --git a/lib/src/boringssl/bindings/ffigen.yaml b/lib/src/boringssl/bindings/ffigen.yaml index d827d9a7..04ece16d 100644 --- a/lib/src/boringssl/bindings/ffigen.yaml +++ b/lib/src/boringssl/bindings/ffigen.yaml @@ -22,7 +22,6 @@ structs: functions: include: - webcrypto_lookup_symbol - - webcrypto_get_CBB_size preamble: | // Copyright 2021 Google LLC // diff --git a/lib/src/boringssl/bindings/generated_bindings.dart b/lib/src/boringssl/bindings/generated_bindings.dart index 0e7e783a..4f4eee44 100644 --- a/lib/src/boringssl/bindings/generated_bindings.dart +++ b/lib/src/boringssl/bindings/generated_bindings.dart @@ -36,18 +36,6 @@ class WebCrypto { ffi.Pointer Function(String symbolName) lookup, ) : _lookup = lookup; - /// Helper function to get the size of CBB structure for FFI allocation - int webcrypto_get_CBB_size() { - return _webcrypto_get_CBB_size(); - } - - late final _webcrypto_get_CBB_sizePtr = - _lookup>( - 'webcrypto_get_CBB_size', - ); - late final _webcrypto_get_CBB_size = _webcrypto_get_CBB_sizePtr - .asFunction(); - /// Function to lookup BoringSSL symbols based on index in the Sym enum. /// See src/symbols.yaml for details. ffi.Pointer webcrypto_lookup_symbol(int index) { diff --git a/lib/src/boringssl/lookup/lookup.dart b/lib/src/boringssl/lookup/lookup.dart index 9e689aa4..3d051ca1 100644 --- a/lib/src/boringssl/lookup/lookup.dart +++ b/lib/src/boringssl/lookup/lookup.dart @@ -16,7 +16,6 @@ import 'dart:ffi'; -import '../bindings/generated_bindings.dart' show WebCrypto; import '../../third_party/boringssl/generated_bindings.dart'; import 'symbols.generated.dart'; @@ -38,9 +37,6 @@ Pointer lookup(String symbolName) { /// Gives access to BoringSSL symbols. final BoringSsl ssl = BoringSsl.fromLookup(lookup); -/// Gives access to helper symbols exported by src/webcrypto.h. -final WebCrypto webcrypto = WebCrypto.fromLookup(lookup); - /// ERR_GET_LIB returns the library code for the error. This is one of the /// ERR_LIB_* values. /// diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index d41a05f6..7f985909 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -29,8 +29,7 @@ import 'package:webcrypto/src/third_party/boringssl/generated_bindings.dart'; import '../jsonwebkey.dart' show JsonWebKey; import '../webcrypto/webcrypto.dart'; import '../impl_interface/impl_interface.dart'; -import '../boringssl/lookup/lookup.dart' - show ssl, webcrypto, ERR_GET_LIB, ERR_GET_REASON; +import '../boringssl/lookup/lookup.dart' show ssl, ERR_GET_LIB, ERR_GET_REASON; part 'impl_ffi.aescbc.dart'; part 'impl_ffi.aesctr.dart'; diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index fe5c0044..5ea46b4e 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -308,18 +308,12 @@ extension on _Scope { ffi.Pointer createCBS(List data) { final cbs = this(); - // CBS_init is an inline function, so we need to initialize the struct directly - cbs.ref.data = dataAsPointer(data); - cbs.ref.len = data.length; + ssl.CBS_init(cbs, dataAsPointer(data), data.length); return cbs; } ffi.Pointer createCBB([int sizeHint = 4096]) { - // Get the actual size of CBB structure from native code - // This ensures we allocate exactly the right amount of memory - // regardless of platform (32-bit, 64-bit, ARM, x86, etc.) - final cbbSize = webcrypto.webcrypto_get_CBB_size(); - final cbb = allocate(cbbSize).cast(); + final cbb = this(); ssl.CBB_zero(cbb); _checkOp(ssl.CBB_init(cbb, sizeHint) == 1, fallback: 'allocation failure'); defer(() => ssl.CBB_cleanup(cbb)); diff --git a/src/webcrypto.c b/src/webcrypto.c index 76b59282..96d1726f 100644 --- a/src/webcrypto.c +++ b/src/webcrypto.c @@ -22,10 +22,3 @@ WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index) { return _webcrypto_symbol_table[index]; } - -// Helper function to get the actual size of CBB structure -// This allows Dart FFI to allocate the correct amount of memory -// without hardcoding platform-specific sizes -WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void) { - return sizeof(CBB); -} diff --git a/src/webcrypto.h b/src/webcrypto.h index 408c02d6..786c3766 100644 --- a/src/webcrypto.h +++ b/src/webcrypto.h @@ -14,7 +14,6 @@ * limitations under the License. */ -#include #include // Macro for annotating all functions to be exported @@ -28,6 +27,3 @@ // Function to lookup BoringSSL symbols based on index in the Sym enum. // See src/symbols.yaml for details. WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index); - -// Helper function to get the size of CBB structure for FFI allocation -WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void); From 89730a0c4417df9f323688d2d2464d3463c8b189 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Fri, 8 May 2026 02:04:00 +0530 Subject: [PATCH 08/10] refactor: simplify BoringSSL roll script helpers --- third_party/boringssl/README.md | 3 +-- tool/bump-boringssl-revision.sh | 37 ++++++++++++++++----------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/third_party/boringssl/README.md b/third_party/boringssl/README.md index 3d6ce1bc..1dfc67e8 100644 --- a/third_party/boringssl/README.md +++ b/third_party/boringssl/README.md @@ -4,8 +4,7 @@ This folder contains sources from BoringSSL allowing `package:webcrypto` to incorporate libcrypto from BoringSSL. Contents of this folder are generated -using `tool/bump-boringssl-revision.sh`, following BoringSSL's -`INCORPORATING.md` guidance. +using `tool/bump-boringssl-revision.sh`. Files in this folder are subject to `LICENSE` from the BoringSSL project. diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh index fc83d578..a307f2c0 100644 --- a/tool/bump-boringssl-revision.sh +++ b/tool/bump-boringssl-revision.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -89,6 +89,7 @@ get_current_revision() { fi } +# update_revision update_revision() { local new_revision="$1" echo "$new_revision" > "$REVISION_FILE" @@ -99,6 +100,7 @@ get_latest_revision() { git ls-remote "$BORINGSSL_REPOSITORY" HEAD | awk '{print $1}' } +# check_command check_command() { local command="$1" local hint="$2" @@ -108,32 +110,25 @@ check_command() { fi } -mkdirp() { - local path="$1" - if [[ ! -d "$path" ]]; then - mkdir -p "$path" - fi -} - cleanup_boringssl() { local path="$ROOT/third_party/boringssl" log_info "Cleaning up old BoringSSL files..." rm -rf "$path" - mkdirp "$path" + mkdir -p "$path" } +# git_clone_boringssl git_clone_boringssl() { local revision="$1" - local temp_dir="$2" - local target="$temp_dir/boringssl" + local target="$2" log_info "Cloning BoringSSL repository..." git clone "$BORINGSSL_REPOSITORY" "$target" >/dev/null 2>&1 log_info "Checking out revision: $revision" git -C "$target" checkout --detach "$revision" >/dev/null 2>&1 - echo "$target" } +# write_build_manifest write_build_manifest() { local src_root="$1" local manifest="$2" @@ -187,6 +182,7 @@ prefix_src_tree_path() { fi } +# write_sources_cmake write_sources_cmake() { local manifest="$1" local dest="$ROOT/third_party/boringssl/sources.cmake" @@ -194,7 +190,7 @@ write_sources_cmake() { log_info "Writing sources.cmake..." cat > "$dest" <<'EOF' -# Copyright 2020 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -231,6 +227,7 @@ EOF done } +# copy_manifest_group copy_manifest_group() { local manifest="$1" local jq_selector="$2" @@ -241,12 +238,13 @@ copy_manifest_group() { if [[ -n "$file" ]]; then local src="$src_root/$file" local dst="$dest_root/$(prefix_src_tree_path "$file")" - mkdirp "$(dirname "$dst")" + mkdir -p "$(dirname "$dst")" cp "$src" "$dst" fi done } +# copy_asm_outputs copy_asm_outputs() { local manifest="$1" local src_root="$2" @@ -256,12 +254,13 @@ copy_asm_outputs() { if [[ -n "$file" ]]; then local src="$src_root/$file" local dst="$dest_root/$file" - mkdirp "$(dirname "$dst")" + mkdir -p "$(dirname "$dst")" cp "$src" "$dst" fi done } +# copy_sources copy_sources() { local manifest="$1" local src_root="$2" @@ -292,8 +291,7 @@ write_boringssl_readme() { This folder contains sources from BoringSSL allowing `package:webcrypto` to incorporate libcrypto from BoringSSL. Contents of this folder are generated -using `tool/bump-boringssl-revision.sh`, following BoringSSL's -`INCORPORATING.md` guidance. +using `tool/bump-boringssl-revision.sh`. Files in this folder are subject to `LICENSE` from the BoringSSL project. @@ -306,17 +304,18 @@ files and information about BoringSSL refer to the [BoringSSL repository][1]. EOF } +# update_boringssl_sources update_boringssl_sources() { local revision="$1" local temp_dir temp_dir=$(mktemp -d) + local src_root="$temp_dir/boringssl" local manifest="$temp_dir/build-files.json" log_info "Starting BoringSSL update to revision: $revision" cleanup_boringssl - local src_root - src_root=$(git_clone_boringssl "$revision" "$temp_dir") + git_clone_boringssl "$revision" "$src_root" log_info "Enumerating source files using upstream generate_build_files.py" write_build_manifest "$src_root" "$manifest" From d3c29862eec10ad81e56f268380023b7f2823b06 Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Fri, 8 May 2026 02:19:25 +0530 Subject: [PATCH 09/10] refactor: use pregenerated BoringSSL sources metadata --- tool/bump-boringssl-revision.sh | 124 +++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh index a307f2c0..e68331f3 100644 --- a/tool/bump-boringssl-revision.sh +++ b/tool/bump-boringssl-revision.sh @@ -138,35 +138,101 @@ import json import os import sys - -class Capture: - def WriteFiles(self, file_sets, asm_outputs): - self.file_sets = file_sets - self.asm_outputs = asm_outputs - - boringssl_root = os.path.abspath(sys.argv[1]) -sys.path.insert(0, os.path.join(boringssl_root, "util")) -import generate_build_files # type: ignore - -capture = Capture() -cwd = os.getcwd() -try: - os.chdir(boringssl_root) - generate_build_files.EMBED_TEST_DATA = False - generate_build_files.main([capture]) -finally: - os.chdir(cwd) +sources_json = os.path.join(boringssl_root, "gen", "sources.json") +if not os.path.exists(sources_json): + sources_json = os.path.join(boringssl_root, "sources.json") + +if not os.path.exists(sources_json): + raise SystemExit(f"Could not find sources.json in {boringssl_root}") + +with open(sources_json, encoding="utf-8") as f: + sources = json.load(f) + + +def classify_asm(path): + normalized = path[4:] if path.startswith("src/") else path + + if normalized.endswith(".asm"): + if "-x86-win.asm" in normalized: + return "win_x86" + if "-x86_64-win.asm" in normalized: + return "win_x86_64" + return None + + if normalized.endswith("-win.S"): + return "win_aarch64" + + if normalized.endswith("-apple.S"): + if "x86_64" in normalized: + return "apple_x86_64" + if "-x86-" in normalized or "x86-apple" in normalized: + return "apple_x86" + if "armv7" in normalized or "armv4" in normalized: + return "apple_arm" + if "armv8" in normalized: + return "apple_aarch64" + return None + + if normalized.endswith("-linux.S"): + if "ppc" in normalized: + return "linux_ppc64le" + if "x86_64" in normalized: + return "linux_x86_64" + if "-x86-" in normalized or "586-linux" in normalized: + return "linux_x86" + if "armv7" in normalized or "armv4" in normalized: + return "linux_arm" + if "armv8" in normalized: + return "linux_aarch64" + return None + + if normalized == "crypto/curve25519/asm/x25519-asm-arm.S": + return "linux_arm" + if normalized == "crypto/poly1305/poly1305_arm_asm.S": + return "linux_arm" + if normalized == "crypto/hrss/asm/poly_rq_mul.S": + return "linux_x86_64" + if normalized.startswith("third_party/fiat/asm/"): + return "linux_x86_64" + + return None + + +asm_outputs = { + "apple_aarch64": [], + "apple_arm": [], + "apple_x86": [], + "apple_x86_64": [], + "linux_aarch64": [], + "linux_arm": [], + "linux_ppc64le": [], + "linux_x86": [], + "linux_x86_64": [], + "win_aarch64": [], + "win_x86": [], + "win_x86_64": [], +} + +for file in ( + sources.get("bcm_sources_asm", []) + + sources.get("bcm_sources_nasm", []) + + sources.get("crypto_sources_asm", []) + + sources.get("crypto_sources_nasm", []) +): + key = classify_asm(file) + if key is not None: + asm_outputs[key].append(file) payload = { - "crypto_sources": sorted(capture.file_sets["crypto"]), - "crypto_headers": sorted(capture.file_sets["crypto_headers"]), - "crypto_internal_headers": sorted(capture.file_sets["crypto_internal_headers"]), - "fips_fragments": sorted(capture.file_sets["fips_fragments"]), - "asm_outputs": { - f"{osname}_{arch}": sorted(files) - for (osname, arch), files in capture.asm_outputs - }, + "crypto_sources": sorted(sources.get("bcm_sources", []) + sources.get("crypto_sources", [])), + "crypto_headers": sorted(sources.get("crypto_headers", [])), + "crypto_internal_headers": sorted( + sources.get("bcm_internal_headers", []) + + sources.get("crypto_internal_headers", []) + ), + "fips_fragments": [], + "asm_outputs": {key: sorted(files) for key, files in asm_outputs.items() if files}, } json.dump(payload, sys.stdout, indent=2, sort_keys=True) sys.stdout.write("\n") @@ -175,6 +241,10 @@ PY prefix_src_tree_path() { local path="$1" + if [[ "$path" == src/* ]]; then + echo "$path" + return + fi if [[ "$path" == */* ]]; then echo "src/$path" else @@ -317,7 +387,7 @@ update_boringssl_sources() { cleanup_boringssl git_clone_boringssl "$revision" "$src_root" - log_info "Enumerating source files using upstream generate_build_files.py" + log_info "Enumerating source files using upstream sources.json" write_build_manifest "$src_root" "$manifest" local source_count From 0c4786722005a7d17e46a618cb039b98a8b9f60b Mon Sep 17 00:00:00 2001 From: HamdaanAliQuatil Date: Fri, 15 May 2026 05:46:39 +0530 Subject: [PATCH 10/10] fix: make BoringSSL roll work with current upstream metadata --- lib/src/boringssl/lookup/lookup.dart | 9 ++++ lib/src/impl_ffi/impl_ffi.dart | 3 +- lib/src/impl_ffi/impl_ffi.utils.dart | 6 ++- src/webcrypto.c | 4 ++ src/webcrypto.h | 4 ++ tool/bump-boringssl-revision.sh | 76 +++++++++++++++++----------- tool/test.sh | 12 ++++- 7 files changed, 79 insertions(+), 35 deletions(-) diff --git a/lib/src/boringssl/lookup/lookup.dart b/lib/src/boringssl/lookup/lookup.dart index 3d051ca1..4581ccf9 100644 --- a/lib/src/boringssl/lookup/lookup.dart +++ b/lib/src/boringssl/lookup/lookup.dart @@ -28,6 +28,12 @@ export 'symbols.generated.dart' show Sym; ) external Pointer _nativeWebcryptoLookupSymbol(int index); +@Native( + symbol: 'webcrypto_get_CBB_size', + assetId: 'package:webcrypto/webcrypto.dart', +) +external int _nativeWebcryptoGetCbbSize(); + /// Resolve lookup from the bundled native asset for `package:webcrypto`. Pointer lookup(String symbolName) { final sym = symFromString(symbolName); @@ -37,6 +43,9 @@ Pointer lookup(String symbolName) { /// Gives access to BoringSSL symbols. final BoringSsl ssl = BoringSsl.fromLookup(lookup); +/// Gets the native `sizeof(CBB)` value from the bundled helper library. +int nativeWebcryptoGetCbbSize() => _nativeWebcryptoGetCbbSize(); + /// ERR_GET_LIB returns the library code for the error. This is one of the /// ERR_LIB_* values. /// diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index 7f985909..e53bebbf 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -29,7 +29,8 @@ import 'package:webcrypto/src/third_party/boringssl/generated_bindings.dart'; import '../jsonwebkey.dart' show JsonWebKey; import '../webcrypto/webcrypto.dart'; import '../impl_interface/impl_interface.dart'; -import '../boringssl/lookup/lookup.dart' show ssl, ERR_GET_LIB, ERR_GET_REASON; +import '../boringssl/lookup/lookup.dart' + show ssl, nativeWebcryptoGetCbbSize, ERR_GET_LIB, ERR_GET_REASON; part 'impl_ffi.aescbc.dart'; part 'impl_ffi.aesctr.dart'; diff --git a/lib/src/impl_ffi/impl_ffi.utils.dart b/lib/src/impl_ffi/impl_ffi.utils.dart index 5ea46b4e..a44df3e3 100644 --- a/lib/src/impl_ffi/impl_ffi.utils.dart +++ b/lib/src/impl_ffi/impl_ffi.utils.dart @@ -308,12 +308,14 @@ extension on _Scope { ffi.Pointer createCBS(List data) { final cbs = this(); - ssl.CBS_init(cbs, dataAsPointer(data), data.length); + cbs.ref.data = dataAsPointer(data); + cbs.ref.len = data.length; return cbs; } ffi.Pointer createCBB([int sizeHint = 4096]) { - final cbb = this(); + final cbbSize = nativeWebcryptoGetCbbSize(); + final cbb = allocate(cbbSize).cast(); ssl.CBB_zero(cbb); _checkOp(ssl.CBB_init(cbb, sizeHint) == 1, fallback: 'allocation failure'); defer(() => ssl.CBB_cleanup(cbb)); diff --git a/src/webcrypto.c b/src/webcrypto.c index 96d1726f..48d25358 100644 --- a/src/webcrypto.c +++ b/src/webcrypto.c @@ -22,3 +22,7 @@ WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index) { return _webcrypto_symbol_table[index]; } + +WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void) { + return sizeof(CBB); +} diff --git a/src/webcrypto.h b/src/webcrypto.h index 786c3766..f29466d1 100644 --- a/src/webcrypto.h +++ b/src/webcrypto.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include // Macro for annotating all functions to be exported @@ -27,3 +28,6 @@ // Function to lookup BoringSSL symbols based on index in the Sym enum. // See src/symbols.yaml for details. WEBCRYPTO_EXPORT void* webcrypto_lookup_symbol(int32_t index); + +// Helper function to get the size of CBB structure for FFI allocation. +WEBCRYPTO_EXPORT size_t webcrypto_get_CBB_size(void); diff --git a/tool/bump-boringssl-revision.sh b/tool/bump-boringssl-revision.sh index e68331f3..d7b4fc79 100644 --- a/tool/bump-boringssl-revision.sh +++ b/tool/bump-boringssl-revision.sh @@ -61,6 +61,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" ROOT="$DIR/.." REVISION_FILE="$DIR/REVISION" BORINGSSL_REPOSITORY='https://boringssl.googlesource.com/boringssl' +PYTHON_BIN="" log_info() { echo "[info] $1" @@ -110,6 +111,21 @@ check_command() { fi } +# resolve_python +resolve_python() { + local command + for command in python3 python; do + if command -v "$command" >/dev/null 2>&1 && + "$command" -c "import sys" >/dev/null 2>&1; then + echo "$command" + return 0 + fi + done + + log_error "python3 or python is required to enumerate BoringSSL source files" + exit 1 +} + cleanup_boringssl() { local path="$ROOT/third_party/boringssl" log_info "Cleaning up old BoringSSL files..." @@ -133,7 +149,7 @@ write_build_manifest() { local src_root="$1" local manifest="$2" - python3 - "$src_root" > "$manifest" <<'PY' + "$PYTHON_BIN" - "$src_root" > "$manifest" <<'PY' import json import os import sys @@ -149,43 +165,41 @@ if not os.path.exists(sources_json): with open(sources_json, encoding="utf-8") as f: sources = json.load(f) +bcm = sources.get("bcm", {}) +crypto = sources.get("crypto", {}) +test_support = sources.get("test_support", {}) + def classify_asm(path): normalized = path[4:] if path.startswith("src/") else path if normalized.endswith(".asm"): - if "-x86-win.asm" in normalized: + if "-x86-win.asm" in normalized or "586-win.asm" in normalized: return "win_x86" - if "-x86_64-win.asm" in normalized: - return "win_x86_64" - return None + return "win_x86_64" if normalized.endswith("-win.S"): return "win_aarch64" if normalized.endswith("-apple.S"): - if "x86_64" in normalized: - return "apple_x86_64" - if "-x86-" in normalized or "x86-apple" in normalized: - return "apple_x86" if "armv7" in normalized or "armv4" in normalized: return "apple_arm" if "armv8" in normalized: return "apple_aarch64" - return None + if "-x86-" in normalized or "x86-apple" in normalized: + return "apple_x86" + return "apple_x86_64" if normalized.endswith("-linux.S"): if "ppc" in normalized: return "linux_ppc64le" - if "x86_64" in normalized: - return "linux_x86_64" - if "-x86-" in normalized or "586-linux" in normalized: - return "linux_x86" if "armv7" in normalized or "armv4" in normalized: return "linux_arm" if "armv8" in normalized: return "linux_aarch64" - return None + if "-x86-" in normalized or "586-linux" in normalized: + return "linux_x86" + return "linux_x86_64" if normalized == "crypto/curve25519/asm/x25519-asm-arm.S": return "linux_arm" @@ -215,21 +229,23 @@ asm_outputs = { } for file in ( - sources.get("bcm_sources_asm", []) - + sources.get("bcm_sources_nasm", []) - + sources.get("crypto_sources_asm", []) - + sources.get("crypto_sources_nasm", []) + bcm.get("asm", []) + + bcm.get("nasm", []) + + crypto.get("asm", []) + + crypto.get("nasm", []) + + test_support.get("asm", []) + + test_support.get("nasm", []) ): key = classify_asm(file) if key is not None: asm_outputs[key].append(file) payload = { - "crypto_sources": sorted(sources.get("bcm_sources", []) + sources.get("crypto_sources", [])), - "crypto_headers": sorted(sources.get("crypto_headers", [])), + "crypto_sources": sorted(bcm.get("srcs", []) + crypto.get("srcs", [])), + "crypto_headers": sorted(crypto.get("hdrs", [])), "crypto_internal_headers": sorted( - sources.get("bcm_internal_headers", []) - + sources.get("crypto_internal_headers", []) + bcm.get("internal_hdrs", []) + + crypto.get("internal_hdrs", []) ), "fips_fragments": [], "asm_outputs": {key: sorted(files) for key, files in asm_outputs.items() if files}, @@ -282,15 +298,15 @@ EOF echo "" >> "$dest" echo "set(crypto_sources" >> "$dest" - jq -r '.crypto_sources[]' "$manifest" | while read -r file; do + jq -r '.crypto_sources[]' "$manifest" | tr -d '\r' | while read -r file; do echo " \${BORINGSSL_ROOT}$(prefix_src_tree_path "$file")" >> "$dest" done echo ")" >> "$dest" - jq -r '.asm_outputs | keys[]' "$manifest" | while read -r key; do + jq -r '.asm_outputs | keys[]' "$manifest" | tr -d '\r' | while read -r key; do echo "" >> "$dest" echo "set(crypto_sources_$key" >> "$dest" - jq -r --arg key "$key" '.asm_outputs[$key][]' "$manifest" | while read -r file; do + jq -r --arg key "$key" '.asm_outputs[$key][]' "$manifest" | tr -d '\r' | while read -r file; do echo " \${BORINGSSL_ROOT}$file" >> "$dest" done echo ")" >> "$dest" @@ -304,7 +320,7 @@ copy_manifest_group() { local src_root="$3" local dest_root="$4" - jq -r "$jq_selector" "$manifest" | while read -r file; do + jq -r "$jq_selector" "$manifest" | tr -d '\r' | while read -r file; do if [[ -n "$file" ]]; then local src="$src_root/$file" local dst="$dest_root/$(prefix_src_tree_path "$file")" @@ -320,7 +336,7 @@ copy_asm_outputs() { local src_root="$2" local dest_root="$3" - jq -r '.asm_outputs[]?[]' "$manifest" | while read -r file; do + jq -r '.asm_outputs[]?[]' "$manifest" | tr -d '\r' | while read -r file; do if [[ -n "$file" ]]; then local src="$src_root/$file" local dst="$dest_root/$file" @@ -432,7 +448,7 @@ main() { check_command git "git is not installed or not in PATH" check_command dart "dart is required to regenerate bindings and run tests" check_command jq "jq is required to parse generated BoringSSL source metadata" - check_command python3 "python3 is required to enumerate BoringSSL source files" + PYTHON_BIN=$(resolve_python) section "Cleaning up build artifacts" log_info "Running clean.sh..." @@ -450,7 +466,7 @@ main() { section "Generating symbols table" log_info "Running generate_symbols_table.dart..." - dart run "$DIR/generate_symbols_table.dart" + dart "$DIR/generate_symbols_table.dart" section "Updating FFI bindings" log_info "Running update-bindings.sh..." diff --git a/tool/test.sh b/tool/test.sh index ecd56ec1..d6192b39 100755 --- a/tool/test.sh +++ b/tool/test.sh @@ -17,6 +17,14 @@ set -e section() { echo ''; echo "### $1"; echo '';} +run_with_xvfb() { + if command -v xvfb-run >/dev/null 2>&1; then + xvfb-run "$@" + else + "$@" + fi +} + DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cd "$DIR/.." @@ -25,7 +33,7 @@ section 'Running "dart pub get --no-example"' dart pub get --no-example section 'dart test (vm,chrome,firefox)' -xvfb-run dart test -p vm,chrome,firefox +run_with_xvfb dart test -p vm,chrome,firefox cd "$DIR/../example" @@ -38,7 +46,7 @@ DEVICE_IDS=$(flutter devices --machine | grep '"sdk"') for DEVICE in linux android; do if echo "$DEVICE_IDS" | grep -i "$DEVICE" > /dev/null; then section "Running integration tests on $DEVICE" - xvfb-run flutter test integration_test/webcrypto_test.dart -d "$DEVICE" + run_with_xvfb flutter test integration_test/webcrypto_test.dart -d "$DEVICE" else section "Skipping integration tests on $DEVICE (missing device)" fi