.. py:currentmodule:: starlark_tugger .. _tugger_code_signing: ============ Code Signing ============ Tugger has support for automatically performing code signing when evaluating Starlark configuration files. Various platforms and distribution channels enforce requirements that binaries and other artifacts are cryptographically signed by a trusted certificate. For example: * On Windows, executables and installers must be signed by a trusted certificate to avoid warnings about running untrusted applications. * On macOS, executables, pkg installers, and more need to be signed by a trusted certificate or Gatekeeper (read: the OS) may refuse to run them. Tugger's support for automatic signing enables you to meet these requirements with hpoefully minimal effort. Code Signing Support ==================== Tugger supports signing the following signable entities: * PE binaries. This is the file executable format in use on Windows platforms. * MSI installers. This is a common file-based installer format on Windows. * Mach-O binaries. This is the file executable format in use on Apple platforms. * Apple application bundles. e.g. ``My Program.app`` directories. Bundles are a common application *packaging* format on Apple platforms. Signing on Windows currently uses Microsoft's ``signtool.exe`` to perform the signing. So signing Windows entities requires access to this tool. (We have plans to implement equivalent functionality in Rust to avoid this dependency.) Signing Apple formats uses a pure Rust implementation of the code signing functionality and works on any machine. Apple's ``codesign`` tool or access to Apple hardware is not required to sign Apple entities. Code signing requires the use of a *code signing certificate*. See :ref:`tugger_code_signing_certificates` for more. Tugger supports using *code signing certificates* in the following locations: * From a PFX / PKCS #12 file. (e.g. ``.pfx`` or ``.p12`` files.) * Certificates available in the *Windows certificate store*. Via the *Windows certificate store*, certificates stored in hardware devices (such as HSMs and hardware tokens such as YubiKeys) can also be used. Configuring Code Signing in Starlark ==================================== **Code signing needs to be explicitly enabled and configured in your Starlark configuration file.** From a high level, here's how it works: 1. Your Starlark configuration instantiates, configures, and enables a :py:class:`CodeSigner`, which is the entity that performs code signing. 2. As your configuration file is evaluated, actions that produce or encounter signable entities (such as creating Windows MSI installers) interact with registered :py:class:`CodeSigner` instances and attempt code signing. Tugger abstracts away a lot of the complexity around code signing, such as figuring out which files need to be signed (it looks at the content of files and determines if a file is signable). So in many cases, all you need to do is tell Tugger where your code signing certificate is and it can do the rest! Continuing reading for details on how to customize code signing. Or just straight into :ref:`tugger_code_signing_examples`. Instantiating :py:class:`CodeSigner` to Perform Code Signing ------------------------------------------------------------ To perform code signing, first instantiate a :py:class:`CodeSigner` via one of its available constructor functions: * :py:func:`code_signer_from_pfx_file` * :py:func:`code_signer_from_windows_store_sha1_thumbprint` * :py:func:`code_signer_from_windows_store_subject` * :py:func:`code_signer_from_windows_store_auto` :py:func:`code_signer_from_pfx_file` is the most versatile method, as it gives Tugger full access to the signing certificate and private key. However, this method is arguably the least secure, as it requires the private key to exist in a file and Tugger holds the decrypted private key in memory during signing. Both of these make the private key much more susceptible to being accessed by unwanted parties. If you are paranoid about security, you should only use this method on machines that you trust. The ``code_signer_from_windows_`` functions reference code signing keys stored in the Windows certificate store. Signature requests are processed through the Windows APIs and the private key never leaves the control of the Windows certificate store, helping to keep the private key secure. .. important:: Constructed :py:class:`CodeSigner` instances must be *activated* in order to automatically perform code signing. See :ref:`tugger_code_signing_activation` for more. Configuring :py:class:`CodeSigner` Instances -------------------------------------------- Once you've obtained a :py:class:`CodeSigner`, you may need to register additional settings to influence signing. Registering the Issuing Certificate Chain ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Produced signatures should often contain details about the *chain* of certificates that issued the code signing certificate. See :ref:`tugger_code_signing_certificates` for more on this topic. You may need to tell :py:class:`CodeSigner` about the existence of these certificates. * When using a code signing certificate backed by the Windows certificate store, you do not need to register the certificate's signing chain. * When using a code signing certificate backed by a PFX file, you need to register the certificate chain, even if those X.509 certificates are in the PFX file (we don't yet support reading these from the PFX file). :py:meth:`CodeSigner.chain_issuer_certificates_pem_file` is the most versatile method to register issuer certificates, as it works on all platforms and PEM is a very widespread format for storing X.509 certificates. * On macOS, :py:meth:`CodeSigner.chain_issuer_certificates_macos_keychain` can be called to attempt to resolve the certificate chain by speaking directly to the macOS keychain APIs. This requires that the signing certificate be accessible in the current user's keychain and its entire issuing chain to be present in that keychain. Influencing Signing Operations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :py:class:`CodeSigner` instances have the opportunity to influence individual signing operations. This gives you significant control over how signing is performed. :py:meth:`CodeSigner.set_signing_callback` registers a function that will be invoked on each attempted signing operation. This callback function receives an argument - a :py:class:`CodeSigningRequest` instance - that describes the entity capable of being signed. This type exposes functionality for influencing the signing operation. For example: * Setting :py:attr:`CodeSigningRequest.defer` to ``True`` will opt this :py:class:`CodeSigner` out of signing this particular entity. * Setting :py:attr:`CodeSigningRequest.prevent_signing` to ``True`` will prevent this and other :py:class:`CodeSigner` from signing this entity. See the :py:class:`CodeSigningRequest` API documentation for all available functionality on this type. Leveraging custom callback functions enables configuration files to employ arbitrarily complex logic for influencing code signing. Your main constraint are the settings exposed on :py:class:`CodeSigningRequest`. If you find yourself needing a setting that doesn't exist, please file a feature request! .. _tugger_code_signing_activation: Activating Automatic Code Signing --------------------------------- A :py:class:`CodeSigner` needs to be *activated* for automatic use by Tugger. i.e. your signable files won't be signed as your Starlark configuration file is evaluated unless a :py:class:`CodeSigner` is *activated*. To activate your :py:class:`CodeSigner`, simply call :py:meth:`CodeSigner.activate`. .. _tugger_code_signing_actions: Code Signing Actions -------------------- Various activities within the evaluation of your Starlark configuration file trigger the assessment of - and possible performing of - code signing. Each unique activity has its own string *action* name describing it. This name is accessible via :py:attr:`CodeSigningRequest.action`, enabling callback functions to key off of it. For example, you may want to not sign during certain operations. The following named actions are defined by Tugger: ``file-manifest-install`` Used when a :py:class:`FileManifest` is materialized on the filesystem through an action like :py:meth:`FileManifest.install()`. ``macos-application-bundle-creation`` When a macOS Application Bundle is created by Tugger. This will be triggered by :py:meth:`MacOsApplicationBundleBuilder.build()`. ``windows-installer-creation`` When a Windows installer file is created by Tugger. Methods like :py:meth:`WiXMSIBuilder.build` and :py:meth:`WiXBundleBuilder.build` will trigger this action. ``windows-installer-file-added`` When a file that will be installed is added to a Windows installer. Triggered by :py:meth:`WiXMSIBuilder.add_program_files_manifest`, :py:meth:`WiXInstaller.add_install_file`, and :py:meth:`WiXInstaller.add_install_files`. Other applications extending Tugger's core functionality may define their own actions. .. _tugger_code_signing_duplicate_events: Duplicate Events ---------------- It is possible for the same logical file to trigger multiple signing events as it is processed. For example, :py:meth:`MacOsApplicationBundleBuilder.build()` may trigger an event for macOS Application Bundle generation then a later action loads the bundle files into a :py:class:`FileManifest` and materializes them somewhere else via :py:meth:`FileManifest.install()`, which would trigger an additional signability check. As a result, the same file or entity may be signed multiple times. If this behavior is undesirable, the use of a custom callback function can be used to choose which signing requests to respond to. Unfortunately, we do not yet expose metadata on :py:class:`CodeSigningRequest` indicating if a file is signed or not. This would likely be the obvious attribute to filter against. This feature is tracked at https://github.com/indygreg/PyOxidizer/issues/400. .. _tugger_code_signing_examples: Code Signing Examples ===================== Automatically Sign all Signable Content with a Specific Certificate in the Windows Store ---------------------------------------------------------------------------------------- Say you have a code signing certificate in the Windows certificate store with the SHA-1 thumbprint ``deadbeefdeadbeefdeadbeefdeadbeefdeadbeef`` and you want Tugger to sign all signable files as it runs. Here's what you'll need to do in your Starlark configuration file: .. code-block:: python signer = code_signer_from_windows_store_sha1_thumbprint("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") signer.activate() As Tugger encounters ``.exe``, ``.dll``, ``.msi`` files and any file that it identifies as signable, it will attempt to automatically sign them! Choosing a Code Signing Certificate Dynamically ----------------------------------------------- Say you have multiple code signing certificates but want to parameterize which one to use. We can do that through the use of the ``VARS`` global dict, which holds settings passed in via the command line. .. code-block:: python PFX_PATH = VARS.get("PFX_PATH") PFX_PASSWORD = VARS.get("PFX_PASSWORD", "") # This needs to be in its own function because Starlark doesn't allow `if` # at the file/module scope. def make_code_signers(): if PFX_PATH: signer = code_signer_from_pfx_file(PFX_PATH, PFX_PASSWORD) signer.activate() # Don't forget to call the function! make_code_signers() Then when running the configuration file, specify an extra variable. e.g.:: $ pyoxidizer --var PFX_PATH /path/to/certificate.pfx --var PFX_PASSWORD hunter2 Or you could use functions like :py:func:`prompt_confirm`, :py:func:`prompt_input`, and :py:func:`prompt_password` to ask the user which certificate to use. .. code-block:: python def make_code_signers(): if prompt_confirm("enable code signing?", default=False): pfx_path = prompt_input("enter path to PFX file:") pfx_password = prompt_password("enter path to PFX password:", confirm=True) signer = code_signer_from_pfx_file(pfx_path, pfx_password) signer.activate() make_code_signers() Selectively Ignoring Files to Sign ---------------------------------- It is common to want to ignore certain files from signing. For example, you may ship a pre-built binary that already has a valid code signature. Here's how you can do that. .. code-block:: python # Define a function that will be called for every signing request that # can influence operation. def code_signer_callback(request): # Match a known filename that doesn't need signed and set # `prevent_signing = True` to prevent it from being signed. if request.filename == "vcruntime140.dll": request.prevent_signing = True signer = code_signer_from_windows_store_sha1_thumbprint("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") signer.set_signing_callback(code_signer_callback) signer.activate() You could even use the :py:func:`prompt_confirm` function to prompt whether to sign each file: .. code-block:: python def code_signer_callback(request): request.prevent_signing = not prompt_confirm("sign %s?" % request.filename) signer = code_signer_from_...() signer.set_signing_callback(code_signer_callback) signer.activate() .. _tugger_code_signing_certificates: Understanding Code Signing Certificates ======================================= A *code signing certificate* consists of a secure, private *key* and a public *certificate* that describes itself to others. These components are strictly separate but are often represented and stored together. The public certificate is an X.509 certificate, much like those used in HTTP to identify web sites. The main difference is that the certificate's subject describes a person or organization (instead of a website) and the certificate contains attributes that denote it for use by code signing. Like web site X.509 certificates, code signing certificates are *signed* by another X.509 certificate. This is called the *issuing* certificate. There is often a *chain* of certificates - the *certificate chain* - leading to a *self-signed* certificate (a certificate whose issuer was itself), which is referred to as the *root* certificate. Typically, the *certificate chain* is included in code signatures. This enables readers of the signature to have full access to all relevant certificates, without an implicit dependency on them being present on the reading machine. This enables validation to be conducted more robustly. .. _tugger_code_sigining_certificate_storage: Code Signing Certificate Storage -------------------------------- Code signing certificates can be stored in a number of formats. Here are the popular ones: * As standalone ``.pfx`` or ``.p12`` files. These are files containing data as defined by the PFX and PKCS #12 specifications. Most tools that support saving code signing certificates to files support this format if not use it by default. * In your operating system's certificate store. Windows, macOS, and other operating systems have built-in functionality for storing and accessing certificates. On Windows, the ``certmgr.msc`` tool can be used to view certificates. On macOS, ``Keychain Access`` is the official GUI application. In addition, the public X.509 certificates and the certificates in the *certificate chain* are often represented as PEM. This is a human-readable text format with content like ``-----BEGIN CERTIFICATE-----``. PEM is actually base64 encoded BER/DER encoding of ASN.1 data structures, but that's not important. What is important is public certificates are often stored in files having this ``-----BEGIN CERTIFICATE-----`` content. These files often have the extension ``.pem`` or ``.crt``. The *certificate chain* is constant for the lifetime of a code signing certificate. So it is possible to export these certificates to a persisted file and reference this file when you need to access the issuer certificates chain. .. _tugger_securing_code_signing_certificate: Securing Your Code Signing Certificate -------------------------------------- Your code signing certificate's private key attests that its owner was in possession of that certificate and has vouched for the integrity of whatever it signed. .. important:: Code signing certificates can be very attractive theft targets for hackers, as possession of a code signing certificate enables you to sign software that can run on other machines and appears to be trusted. Therefore, it is often important to try to secure your code signing certificates! The most secure way to store code signing certificates is in dedicated hardware devices, such as HSMs or personal hardware tokens (such as YubiKeys). Often, the private key component of the certificate is generated directly in said hardware and it is impossible to export the private key and obtain its raw value. Instead, operations like signing are issued to the hardware and the hardware gives you the rest. Tugger doesn't yet support interfacing directly with hardware devices. However, we do have support for interfacing with the operating system's certificate stores: * On Windows, a certificate in the Windows certificate store can be referenced by its SHA-1 fingerprint. (This is the preferred mechanism to reference a certificate on Windows.) * On Windows, a certificate in the Windows certificate store can be referenced by specifying a string to match against in the certificate's *subject* field. (This is less precise than specifying a certificate's SHA-1 fingerprint.) * On Windows, you can tell the signing tool to automatically find the most appropriate certificate to use. It will look for a certificate in known certificate stores. (This is the least precise of all options available on Windows.) .. note:: Your operating system's certificate store can often interface with hardware devices holding code signing certificates. So Tugger's support for interfacing with the operating system store is often just as effective as interfacing directly with hardware devices. For example, on Windows, certificates stored in a YubiKey will be available if you have the `YubiKey Smart Card Minidriver `_ installed. **If Tugger doesn't support using a remote certificate, you will need to export a certificate to a file and have Tugger use that. If you export your certificate to a file, you should take care to secure that file as best you can.** File-based code signing certificates often exist in ``.pfx`` or ``.p12`` files. These are often protected with a password. **You should use a strong and unique password to secure this file.** .. important:: If someone else gains access to the file containing your code signing certificate, they will be able to perform an offline attack using as many compute resources as possible to guess your password and gain access to the code signing certificate. You should take the following precautions to protect file-based code signing certificates: * Choose a strong, unique password for protecting the file content. * Limit the time the files exist. If you can create the file only when needed, this is better than having the file linger on the filesystem. * Limit the number of copies of the file. Every copy of the file is an opportunity for the file to be obtained by someone else. .. _tugger_code_signing_apple: Exporting a Code Signing Certificate from macOS Keychain -------------------------------------------------------- Apple platforms require a code signing certificate issued by Apple to sign distributed files. If you have an Apple-issued code signing certificate, it is likely registered in a *keychain* on your machine. Tugger doesn't currently support interfacing directly with the macOS keychain and you will need to export your signing certificate to a PFX / ``.p12`` file so Tugger can use it. Here's how to do that. 1. Press ``command + spacebar`` and search for and open the ``Keychain Access`` application. 2. Make sure the correct keychain is selected. The keychain code signing certificates are typically located in is the ``login`` keychain under the ``Default Keychains`` list. 3. From the horizontal list of filters above the main pane, select ``Certificates`` (it is probably the last item). 4. Find the certificate you want to export. It likely has a name like ``Developer ID Application: `` 5. Do a double finger tap, right click, or ``File -> Export Items ...`` to bring up the export dialog. 6. For the file format, make sure ``Personal Information Exchange (.p12)`` is selected. 7. Navigate to a folder where you want to save the file, choose an appropriate name, and click ``Save``. 8. You will be asked for a *password which will be used to protect the exported items*. Enter one. This password will need to be provided to Tugger later to unlock the content in the file. 9. You may be prompted to enter the password to the keychain to allow the key export. If so, enter that password. 10. You may be prompted multiple times. Just keep entering your keychain password(s) until it is done. 11. You are done! There should be a ``.p12`` file wherever you told ``Keychain Access`` to save it. .. important:: Please see :ref:`tugger_securing_code_signing_certificate` for important information on keeping your file-based code signing certificate secure. .. _tugger_code_signing_windows_thumbprint: Finding the Code Signing SHA-1 Thumbprint on Windows ---------------------------------------------------- On Windows, it is recommended to use code signing certificates in the Windows certificate store and to specify those certificates via their SHA-1 thumbprint, which should uniquely identify a certificate. The Windows certificate store supports interfacing with hardware certificate stores (such as YubiKeys and other hardware devices). So this method should work with connected hardware certificate stores as well. 1. Press ``Windows Key + r`` to open the ``Run`` panel. Type in ``certmgr.msc`` and run that program. 2. Code signing certificates are likely under ``Personal`` -> ``Certificates``. Find that item in the tree and look for a certificate in the main pane. 3. Find the certificate you want to use and double click on it to view its details. 4. Open the ``Details`` tab. 5. In the table of fields, find and select ``Thumbprint``. 6. Copy the 40 character hexadecimal value that is printed. The SHA-1 thumbprint can be fed into :py:func:`code_signer_from_windows_store_sha1_thumbprint` to construct a :py:class:`CodeSigner` that uses the specified certificate. If the certificate is protected by a password or requires key to unlock, you should see prompts to do that as Tugger attempts to sign things. .. _tugger_code_signing_windows_export: Exporting a Code Signing Certificate from Windows Certificate Store ------------------------------------------------------------------- Code signing certificates on Windows are often stored in the Windows certificate store. .. important:: Tugger has support for using certificates directly in the Windows certificate store. Exporting certificates to files will likely result in a net loss of security. Here is how you can export a certificate to a PFX file. 1. Press ``Windows Key + r`` to open the ``Run`` panel. Type in ``certmgr.msc`` and run that program. 2. Code signing certificates are likely under ``Personal`` -> ``Certificates``. Find that item in the tree and look for a certificate in the main pane. 3. Double click on the certificate you want to export, open its ``Details`` table, and click the ``Copy to File...`` button. This should open the *Certificate Export Wizard*. 4. Click ``Next``. 5. Make sure ``Yes, export the private key`` is selected and click ``Next``. 6. For the format, make sure the selected value is ``Personal Information Exchange PKCS #12 (PFX)``. For the checkboxes, check ``Include all certificates in the certificate path, if possible``. Then click ``Next``. 7. You should be prompted for a password. Enter a secure, unique password. In the ``Encryption`` drop-down, ensure ``TripleDES-SHA1`` is selected (we don't yet support ``AES256-SHA256``). Then click ``Next``. 8. Select a filename and click ``Next``. 9. Click ``Finish`` to close the wizard. .. important:: Please see :ref:`tugger_securing_code_signing_certificate` for important information on keeping your file-based code signing certificate secure.