mirror of
https://github.com/zen-browser/desktop.git
synced 2026-04-17 04:53:11 +00:00
222 lines
7.6 KiB
Bash
222 lines
7.6 KiB
Bash
#!/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 "mar_sig" \
|
||
-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"
|
||
}
|
||
|
||
update_manifests() {
|
||
mar_file=$(basename "$1")
|
||
if [[ "$mar_file" == "linux.mar" ]]; then
|
||
manifest="linux_update_manifest_x86_64"
|
||
elif [[ "$mar_file" == "linux-aarch64.mar" ]]; then
|
||
manifest="linux_update_manifest_aarch64"
|
||
elif [[ "$mar_file" == "windows.mar" ]]; then
|
||
manifest=".github/workflows/object/windows-x64-signed-x86_64/update_manifest"
|
||
if [ ! -f "$manifest" ]; then
|
||
manifest="windows_update_manifest_x86_64"
|
||
fi
|
||
elif [[ "$mar_file" == "windows-arm64.mar" ]]; then
|
||
manifest=".github/workflows/object/windows-x64-signed-arm64/update_manifest"
|
||
if [ ! -f "$manifest" ]; then
|
||
manifest="windows_update_manifest_arm64"
|
||
fi
|
||
elif [[ "$mar_file" == "macos.mar" ]]; then
|
||
manifest="macos_update_manifest"
|
||
else
|
||
echo "Unknown MAR file name format: $mar_file. Skipping manifest update." >&2
|
||
exit 1
|
||
fi
|
||
# There can be any update.xml file, lets just recursively search for the one
|
||
manifest_files=$(find "$manifest" -type f -name "update.xml")
|
||
for manifest_file in $manifest_files; do
|
||
# Example manifest:
|
||
# <update type="minor" displayVersion="..." appVersion="..." platformVersion="..." buildID="...">
|
||
# <patch type="complete" URL="..." hashFunction="sha512" hashValue="..." size="..."/>
|
||
# </update>
|
||
# </updates>
|
||
# When signing the mar, hashValue and size will change, so we need to update the manifest with
|
||
# the new values. We can get the new values by running "mar -i signed_mar_file.mar"
|
||
echo "Updating manifest $manifest_file with new hash and size for $mar_file"
|
||
size=$(wc -c < "$1" | tr -d ' ')
|
||
hashValue=$(sha512sum "$1" | awk '{print $1}')
|
||
# Update the manifest with the new values. We can use sed to do this.
|
||
# We need to find the line that contains the URL of the mar file, and update the hashValue and size attributes in the same <patch> element.
|
||
old_hashValue=$(grep -oP 'hashValue="\K[^"]+' "$manifest_file")
|
||
old_size=$(grep -oP 'size="\K[^"]+' "$manifest_file")
|
||
if [ -z "$old_hashValue" ] || [ -z "$old_size" ]; then
|
||
echo "Could not find old hashValue or size in manifest. Skipping manifest update." >&2
|
||
exit 1
|
||
fi
|
||
echo "Old hashValue: $old_hashValue, Old size: $old_size"
|
||
echo "New hashValue: $hashValue, New size: $size"
|
||
sed -i.bak "s/hashValue=\"$old_hashValue\"/hashValue=\"$hashValue\"/g; s/size=\"$old_size\"/size=\"$size\"/g" "$manifest_file"
|
||
rm "$manifest_file.bak"
|
||
echo "Manifest updated with new hashValue and size for $mar_file"
|
||
done
|
||
}
|
||
|
||
sign_mars() {
|
||
if [ ! -f "$SIGNMAR" ]; then
|
||
echo "Error: signmar not found at $SIGNMAR. Build the engine first." >&2
|
||
exit 1
|
||
fi
|
||
|
||
chmod +x "$SIGNMAR"
|
||
|
||
create_nss_config_dir
|
||
|
||
folders=(
|
||
linux.mar
|
||
linux-aarch64.mar
|
||
windows.mar
|
||
windows-arm64.mar
|
||
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 "mar_sig" -s "$mar_file" "$mar_file".signed
|
||
echo "Signed $mar_file. Verifying signature..."
|
||
"$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "mar_sig" -v "$mar_file".signed
|
||
mv "$mar_file".signed "$mar_file"
|
||
echo "Successfully signed $mar_file"
|
||
update_manifests "$mar_file"
|
||
else
|
||
echo "No .mar files found in $folder, skipping."
|
||
exit 1
|
||
fi
|
||
done
|
||
else
|
||
echo "Directory $folder not found, skipping."
|
||
exit 1
|
||
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
|