diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 607470d6f..07203a4a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -505,6 +505,20 @@ jobs: run: | git clone https://github.com/zen-browser/windows-binaries.git .github/workflows/object --depth 1 + - name: Download signmar-linux-x86_64 from artifacts + uses: actions/download-artifact@v4 + with: + name: signmar-linux-x86_64 + + - name: Sign MAR files + env: + SIGNMAR: ${{ github.workspace }}/signmar-linux-x86_64 + ZEN_MAR_SIGNING_PASSWORD: ${{ secrets.ZEN_MAR_SIGNING_PASSWORD }} + ZEN_SIGNING_CERT_PEM_BASE64: ${{ secrets.ZEN_SIGNING_CERT_PEM_BASE64 }} + ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64: ${{ secrets.ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 }} + run: | + bash scripts/mar_sign.sh -s + - name: Copy update manifests env: RELEASE_BRANCH: ${{ inputs.update_branch }} diff --git a/.github/workflows/linux-release-build.yml b/.github/workflows/linux-release-build.yml index 93c6a70da..2ce7906cc 100644 --- a/.github/workflows/linux-release-build.yml +++ b/.github/workflows/linux-release-build.yml @@ -173,3 +173,11 @@ jobs: retention-days: 5 name: linux_update_manifest_${{ matrix.arch }} path: ./dist/update + + - name: Upload signmar + if: ${{ matrix.arch == 'x86_64' }} + uses: actions/upload-artifact@v4 + with: + retention-days: 2 + name: signmar-linux-x86_64 + path: engine/obj-x86_64-pc-linux-gnu/dist/bin/signmar diff --git a/.github/workflows/src/release-build.sh b/.github/workflows/src/release-build.sh index e553f3942..4a522473d 100644 --- a/.github/workflows/src/release-build.sh +++ b/.github/workflows/src/release-build.sh @@ -10,6 +10,8 @@ fi . $HOME/.cargo/env +sh ./scripts/mar_sign.sh -i + ulimit -n 4096 if command -v Xvfb &> /dev/null; then diff --git a/.gitignore b/.gitignore index 716fd810f..67b3b7ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,10 @@ locales/firefox-l10n/ .DS_Store mozconfig + +build/signing/env/ +build/signing/nss_config/ +build/signing/cert.pem +build/signing/private_key.pem +build/signing/private_key.p12 + diff --git a/build/signing/public_key.der b/build/signing/public_key.der new file mode 100644 index 000000000..689941690 Binary files /dev/null and b/build/signing/public_key.der differ diff --git a/configs/common/mozconfig b/configs/common/mozconfig index 3d41bc83a..16a51385a 100644 --- a/configs/common/mozconfig +++ b/configs/common/mozconfig @@ -48,6 +48,8 @@ if test "$ZEN_RELEASE"; then ac_add_options --enable-optimize + ac_add_options --enable-verify-mar + ac_add_options --enable-release ac_add_options --disable-debug ac_add_options --disable-debug-symbols @@ -89,8 +91,6 @@ if test "$ZEN_RELEASE"; then ac_add_options --enable-replace-malloc fi -ac_add_options --enable-unverified-updates - ac_add_options --enable-jxl ac_add_options --with-unsigned-addon-scopes=app,system diff --git a/scripts/mar_sign.sh b/scripts/mar_sign.sh new file mode 100644 index 000000000..2dfbfbc6c --- /dev/null +++ b/scripts/mar_sign.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +set -e + +CERT_PATH_DIR=build/signing +UPDATER_CERT_DIR="engine/toolkit/mozapps/update/updater" +NSS_CONFIG_DIR="$CERT_PATH_DIR/nss_config" + +generate_certs() { + mkdir temp + cd temp + + # 1. Generate private key + openssl genrsa -out private_key.pem 4096 + + # 2. Generate self-signed certificate (required for PKCS#12 bundling) + openssl req -new -x509 \ + -key private_key.pem \ + -out cert.pem \ + -subj "/CN=MAR Signing" + + # 3. Export public key as SPKI DER (for embedding in updater) + openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der + + cd .. + mkdir -p "$CERT_PATH_DIR" + mv temp/private_key.pem "$CERT_PATH_DIR"/private_key.pem + mv temp/cert.pem "$CERT_PATH_DIR"/cert.pem + mv temp/public_key.der "$CERT_PATH_DIR"/public_key.der + + mkdir -p "$CERT_PATH_DIR/env" + base64 -w 0 "$CERT_PATH_DIR"/cert.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_CERT_PEM_BASE64 + base64 -w 0 "$CERT_PATH_DIR"/private_key.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 + + # Verify public key + openssl rsa -in "$CERT_PATH_DIR"/public_key.der \ + -pubin -inform DER -text -noout + + rm -rf temp +} + +import_cert() { + if [ ! -f "$CERT_PATH_DIR/public_key.der" ]; then + echo "Error: public_key.der not found. Run with -g first." >&2 + exit 1 + fi + echo "Importing certificate into $UPDATER_CERT_DIR/release_primary.der" + cp "$CERT_PATH_DIR/public_key.der" "$UPDATER_CERT_DIR/release_primary.der" + echo "Importing certificate into $UPDATER_CERT_DIR/release_secondary.der" + cp "$CERT_PATH_DIR/public_key.der" "$UPDATER_CERT_DIR/release_secondary.der" + echo "Done. Rebuild the updater to embed the new certificate." +} + +create_nss_config_dir() { + rm -rf "$NSS_CONFIG_DIR" + mkdir "$NSS_CONFIG_DIR" + + if [ -z "$ZEN_MAR_SIGNING_PASSWORD" ]; then + echo "Warning: ZEN_MAR_SIGNING_PASSWORD environment variable not set. Using empty password." >&2 + ZEN_MAR_SIGNING_PASSWORD="" + fi + + password_file="$NSS_CONFIG_DIR/password.txt" + echo "$ZEN_MAR_SIGNING_PASSWORD" > "$password_file" + + if [ "$ZEN_SIGNING_CERT_PEM_BASE64" ]; then + echo "Decoding signing certificate from ZEN_SIGNING_CERT_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_CERT_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/cert.pem" + fi + + if [ "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" ]; then + echo "Decoding signing private key from ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/private_key.pem" + fi + + echo "Generating NSS config directory at $NSS_CONFIG_DIR" + certutil -N -d "$NSS_CONFIG_DIR" -f "$password_file" + + echo "Wrapping private key into PKCS#12..." + echo "Wrapping key + cert into PKCS#12..." + openssl pkcs12 -export \ + -inkey "$CERT_PATH_DIR/private_key.pem" \ + -in "$CERT_PATH_DIR/cert.pem" \ + -name "private_key" \ + -passout pass:"$ZEN_MAR_SIGNING_PASSWORD" \ + -out "$CERT_PATH_DIR/private_key.p12" + + echo "Importing PKCS#12 into NSS database..." + pk12util \ + -i "$CERT_PATH_DIR/private_key.p12" \ + -d "$NSS_CONFIG_DIR" \ + -W "$ZEN_MAR_SIGNING_PASSWORD" \ + -K "$ZEN_MAR_SIGNING_PASSWORD" +} + +cleanup_certs() { + rm -rf "$NSS_CONFIG_DIR" + rm -rf "$CERT_PATH_DIR/env" + + rm -f "$CERT_PATH_DIR/private_key.p12" + rm -f "$CERT_PATH_DIR/private_key.pem" + rm -f "$CERT_PATH_DIR/cert.pem" +} + +sign_mars() { + if [ ! -f "$SIGNMAR" ]; then + echo "Error: signmar not found at $SIGNMAR. Build the engine first." >&2 + exit 1 + fi + + create_nss_config_dir + + folders=( + linux.mar + linux-aarch64.mar + windows.mar + windows-arm64 + macos.mar + ) + # each folder will contain the .mar files for that platform, and the signature will be written in-place + for folder in "${folders[@]}"; do + if [ -d "$folder" ]; then + for mar_file in "$folder"/*.mar; do + if [ -f "$mar_file" ]; then + echo "" + echo "Signing $mar_file..." + # mar [-C workingDir] -d NSSConfigDir -n certname -s archive.mar out_signed_archive.mar + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "private_key" -s "$mar_file" "$mar_file".signed + echo "Signed $mar_file. Verifying signature..." + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "private_key" -v "$mar_file".signed + mv "$mar_file".signed "$mar_file" + echo "Successfully signed $mar_file" + else + echo "No .mar files found in $folder, skipping." + fi + done + else + echo "Directory $folder not found, skipping." + fi + done + + cleanup_certs +} + +case "$1" in + -g) + generate_certs + ;; + -i) + import_cert + ;; + -s) + sign_mars + ;; + *) + echo "Usage: $0 [-g] [-i] [-s]" >&2 + echo " -g Generate MAR signing certificates" >&2 + echo " -i Import the certificate into the updater (release_primary.der)" >&2 + echo " -s Sign *.mar files in the current directory in-place" >&2 + exit 1 + ;; +esac diff --git a/src/browser/installer/package-manifest-in.patch b/src/browser/installer/package-manifest-in.patch index 3aec7d9df..f07fcc3ac 100644 --- a/src/browser/installer/package-manifest-in.patch +++ b/src/browser/installer/package-manifest-in.patch @@ -1,18 +1,8 @@ diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..951889b156498f66118d67d4245aca649e27a3a2 100644 +index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..b6bf327bf286de5246e5c50d89519d16d5771caf 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in -@@ -378,17 +378,17 @@ bin/libfreebl_64int_3.so - ; [MaintenanceService] - ; - #ifdef MOZ_MAINTENANCE_SERVICE --@BINPATH@/maintenanceservice.exe --@BINPATH@/maintenanceservice_installer.exe -+;@BINPATH@/maintenanceservice.exe -+;@BINPATH@/maintenanceservice_installer.exe - #endif - - ; [Crash Reporter] +@@ -386,9 +386,9 @@ bin/libfreebl_64int_3.so ; #ifdef MOZ_CRASHREPORTER #ifdef XP_MACOSX diff --git a/src/toolkit/mozapps/update/updater/updater-common-build.patch b/src/toolkit/mozapps/update/updater/updater-common-build.patch index 03390b4c7..ee8ed45d6 100644 --- a/src/toolkit/mozapps/update/updater/updater-common-build.patch +++ b/src/toolkit/mozapps/update/updater/updater-common-build.patch @@ -7,7 +7,7 @@ index 57ea0415653678bb300e277e66c97e332e756cc7..5f757e68a685948c83fee8f963c49bee "signmar", ] + if CONFIG["OS_ARCH"] == "Linux": -+ # Zen: --enable-unverified-updates is enabled, the RPATH is not added ++ # Zen: ensure RPATH is set so NSS/signmar libs are found at runtime + OS_LIBS += [ + "-Wl,-rpath=\\$$ORIGIN", + ]