#!/usr/bin/env python3 # Copyright (c) 2021 The Brave Authors. All rights reserved. # 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/. import argparse import os import shutil import sys from gclient_utils import exponential_backoff_retry from lib.util import execute from os.path import abspath, dirname from subprocess import CalledProcessError cert = os.environ.get('CERT') cert_hash = os.environ.get('AUTHENTICODE_HASH') signtool_args = ( os.environ.get('SIGNTOOL_ARGS') or # We use a http:// URL because at least our current version of signtool # (10.0.22621.0, March 2024) does not support https://. See # https://github.com/brave/brave-browser/issues/165#issuecomment-1983445659 # for more information. 'sign /t http://timestamp.digicert.com /sm ' '/fd sha256') assert cert or cert_hash or signtool_args, \ 'At least one of AUTHENTICODE_HASH, CERT and SIGNTOOL_ARGS must be set.\n'\ 'The preferred parameter is AUTHENTICODE_HASH. Its value can be obtained '\ 'via the command `Get-ChildItem -path cert:\\LocalMachine\\My`.\n' \ 'CERT is a part of the name in the //CurrentUser/My Windows Certificate ' \ 'Store. It is ambiguous and will likely be deprecated in the future.' # pylint: disable=dangerous-default-value def execute_with_retry(argv, env=os.environ, count=5): return exponential_backoff_retry(lambda: execute(argv, env), excs=(RuntimeError, CalledProcessError), count=count) def get_sign_cmd(file): # https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe # signtool should be in the path if it was set up correctly by gn through # src/build/vs_toolchain.py cmd = 'signtool {}'.format(signtool_args) if cert: cmd = cmd + ' /n "' + cert + '"' if cert_hash: cmd = cmd + ' /sha1 "' + cert_hash + '"' return cmd + ' "' + file + '"' def sign_binaries(base_dir, endswidth=('.exe', '.dll')): matches = [] for root, _, filenames in os.walk(base_dir): for filename in filenames: if filename.endswith(endswidth): matches.append(os.path.join(root, filename)) for binary in matches: sign_binary(binary) def sign_binary(binary, out_file=None): if out_file: os.makedirs(dirname(abspath(out_file)), exist_ok=True) shutil.copy(binary, out_file) binary = out_file cmd = get_sign_cmd(binary) execute_with_retry(cmd) def main(): parser = argparse.ArgumentParser() parser.add_argument('file', help='the file to sign.') parser.add_argument('--out_file', help=('where to place the signed file. By default, the ' 'file is signed in-place.')) args = parser.parse_args() sign_binary(args.file, args.out_file) if __name__ == '__main__': sys.exit(main())