--- /dev/null
+# Purpose: Pack a Chromium extension directory into crx3 format
+#
+# Sources:
+# https://stackoverflow.com/a/18709204
+# https://cs.chromium.org/chromium/src/components/crx_file/crx3.proto?l=1&rcl=09de3b4e1e13cf10e431ba119b179169cde289fe
+#
+# Modified by Andrew Lorimer, August 2019
+#
+
+description="\e[34m$(basename "$0")\e[0m
+Pack a Chromium extension into CRX3 format
+
+"
+usageshort="\e[1mUSAGE:\e[0m
+ $(basename "$0") [-s src_dir] [-p private_key] [-d output.crx]\n"
+usage="
+\e[1mOPTIONS:\e[0m
+ -s, --source
+ Directory containing all source files to be included in extension.
+ Default: ./src
+
+ -d, --destination
+ Name of a file where generated crx file will be placed. Extisting files will be overwritten.
+ Default: ./build/extension.crx
+
+ -p, --private
+ Location of the private key file.
+ Default: ./extension.pem
+
+ -n, --name
+ Use this as the file name instead of \"extension\" in the above file paths.
+
+ -h, --help
+ Show this help message\n"
+
+dir="$(pwd -P)/src"
+name=$(basename $(pwd -P))
+key=$(pwd -P)/$name.pem
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ printf "$description$usageshort$usage"
+ exit
+ ;;
+ -s|--source)
+ dir=(${2-})
+ echo "Using source directory $dir"
+ shift
+ ;;
+ -d|--destination)
+ out=(${2-})
+ echo "Generating crx output at $out"
+ shift
+ ;;
+ -p|--private)
+ key=(${2-})
+ shift
+ ;;
+ -n|--name)
+ name=(${2-})
+ shift
+ ;;
+ --)
+ break
+ ;;
+ *)
+ printf "\x1b[31mInvalid argument $1\x1b[0m\n\n"
+ printf "$usageshort"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+if [ -z $out ]; then out="$name.crx"; fi
+pub="$name.pub"
+sig="$name.sig"
+zip="$name.zip"
+tosign="$name.presig"
+binary_crx_id="$name.crxid"
+trap 'rm -f "$pub" "$sig" "$zip" "$tosign" "$binary_crx_id"' EXIT
+
+printf "Building $(realpath --relative-to=`pwd` $dir) to $(realpath --relative-to=`pwd` $out) with private key $(realpath --relative-to=`pwd` $key)\n"
+
+# Zip crx directory
+printf "\tZipping source directory\n"
+cwd=$(pwd -P)
+(cd "$dir" && zip -qr -9 -X "$cwd/$zip" .)
+
+
+# Extract crx id
+printf "\tExtracting crx id\n"
+{
+ openssl rsa -in "$key" -pubout -outform der | openssl dgst -sha256 -binary -out "$binary_crx_id"
+} &> /dev/null
+truncate -s 16 "$binary_crx_id"
+
+# Generate file to sign
+printf "\tSigning crx file\n"
+(
+ printf "CRX3 SignedData"
+ echo "00 12 00 00 00 0A 10" | xxd -r -p
+ cat "$binary_crx_id" "$zip"
+) > "$tosign"
+
+openssl dgst -sha256 -binary -sign "$key" < "$tosign" > "$sig" # Signature
+openssl rsa -pubout -outform DER < "$key" > "$pub" 2>/dev/null # Public key
+
+crmagic_hex="43 72 32 34" # Cr24
+version_hex="03 00 00 00" # 3
+header_length="45 02 00 00"
+header_chunk_1="12 AC 04 0A A6 02"
+header_chunk_2="12 80 02"
+header_chunk_3="82 F1 04 12 0A 10"
+(
+ echo "$crmagic_hex $version_hex $header_length $header_chunk_1" | xxd -r -p
+ cat "$pub"
+ echo "$header_chunk_2" | xxd -r -p
+ cat "$sig"
+ echo "$header_chunk_3" | xxd -r -p
+ cat "$binary_crx_id" "$zip"
+) > "$out"
+printf "\tWrote $out\n"