.. SPDX-License-Identifier: CC-BY-SA-2.0-UK Shared State Signing ******************** The :term:`OpenEmbedded Build System` build system has a built-in mechanism allowing to save execution time by re-using pre-built artifacts: the :ref:`shared state cache (sstate cache) `. These artifacts are stored in a directory (:term:`SSTATE_DIR`) and are not signed by default. This document goes through the steps to enable shared state signing. This feature is fully dependent on :wikipedia:`GPG `, meaning examples shown in this document will use the ``gpg`` command-line tool. Host Requirements ================= As :wikipedia:`GPG ` is not part of the default :ref:`host requirements `, you will need to install it on your host. For example, Debian-based distributions provide it with the ``gpg`` package. Install it as follows: .. code-block:: console $ sudo apt-get install gpg Verify that your installation is successful by showing the version of GPG: .. code-block:: console $ gpg --version Generating A Public And Private Key Pair With GPG ================================================= .. note:: This step is optional if you already have a pair of public and private keys. You need a pair of public and private keys for two independent tasks: - Signing the shared state artifacts that the :term:`OpenEmbedded Build System` generates with your **private key**. - Verifying the shared state artifacts with your **public key** when re-using them. .. note:: For more information on public key cryptography, see :wikipedia:`Public-key_cryptography`. With the ``gpg`` command-line tool, generate a new pair of public and private keys: .. code-block:: console $ gpg --full-generate-key It will guide you through the steps of creating the key pair. Once done, you should be able to list your new key with the following command: .. code-block:: console $ gpg --list-keys pub ed25519 2026-04-17 [SC] 4049A47E3AAA99D0250966DC5B97632FA7F4E942 uid [ultimate] Antonin Godard (SState Signing) sub cv25519 2026-04-17 [E] In the above example, take note of the ``4049A47E3AAA99D0250966DC5B97632FA7F4E942`` key identifier. This will be used to configure the build. Configuring The Build System To Sign Shared State Artifacts =========================================================== Shared State Location --------------------- The build system needs to be configured to sign new shared state artifacts when they are generated. The generation of new artifacts is done once a task has finished being executed. For the following sections let's assume that the build system has the shared state directory location (:term:`SSTATE_DIR`) defined as follows:: SSTATE_DIR = "${TOPDIR}/sstate-cache" Assuming this directory and the temporary directory (:term:`TMPDIR`) are empty, let's run the ``create_recipe_spdx`` task of the ``gettext-minimal-native`` recipe: .. code-block:: console $ bitbake gettext-minimal-native -c create_recipe_spdx Let's take this command as an example throughout this document. After execution, the shared state directory should be populated with new files: .. code-block:: console $ find $BUILDDIR/sstate-cache/ -name "*gettext-minimal-native*create_recipe_spdx*" sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst.siginfo These are the default shared state artifacts generated by the :term:`OpenEmbedded Build System`. They are not signed with GPG by default, so let's see how to add signing of these artifacts. .. note:: Despite its name, the `siginfo` file is unrelated to GPG signing. Enabling Shared State Signing ----------------------------- Create a new :term:`configuration file` on your host **in a safe location** and add the two following statements:: SSTATE_VERIFY_SIG = "1" SSTATE_SIG_KEY = "4049A47E3AAA99D0250966DC5B97632FA7F4E942" SSTATE_SIG_PASSPHRASE = "" It is advised to put these statements in a separate file as those contain secrets and should not be shared. For this example, let's assume this file is ``conf/sstate-sig-key.conf``. You can make sure this file is only owned by you and not readable by another user with: .. code-block:: console $ chown $USER:$USER conf/sstate-sig-key.conf $ chmod o-rwx conf/sstate-sig-key.conf The statements in this file define: - :term:`SSTATE_VERIFY_SIG`: setting this variable to "1" enables the shared state signing feature. - :term:`SSTATE_SIG_KEY`: the GPG key identifier for signing shared state artifacts with the private key. In the example, this corresponds to the identifier printed with the ``gpg --list-keys`` command :ref:`above `. - :term:`SSTATE_SIG_PASSPHRASE`: the passphrase used to protect your private key, chosen when creating the key pair. Let's test the configuration: #. Continuing with the ``gettext-minimal-native`` example, let's first remove the existing shared state artifacts, to make sure the shared state artifacts for my ``do_create_recipe_spdx`` task are re-generated: .. code-block:: console $ rm -r sstate-cache/ #. Run the ``create_recipe_spdx`` task for ``gettext-minimal-native``, but this time pass the new ``sstate-sig-key.conf`` file to :term:`BitBake`: .. code-block:: console $ bitbake -R conf/sstate-sig-key.conf gettext-minimal-native -c create_recipe_spdx List the files in the shared state directory again: .. code-block:: console $ find sstate-cache/ -name "*gettext-minimal-native*create_recipe_spdx*" sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst.sig sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst.siginfo A new ``.sig`` file was created: this means the artifact was successfully signed, and the signature is stored in a separate ``.sig`` file. Verifying Signed Shared State Artifacts ======================================= Now that you have set up the build to sign shared state artifacts, let's see how you can verify them with the public key counterpart of the private key. .. note:: Signature of shared state and its verification can happen on two different hosts, meaning one host can be in charge of the signature while another only verifies the artifacts. This is preferred as the private key should not be shared between multiple hosts. From a :term:`configuration file` such as the :ref:`site configuration file `, include the following statements:: SSTATE_VERIFY_SIG = "1" SSTATE_VALID_SIGS = "5B97632FA7F4E942" These statements define: - :term:`SSTATE_VERIFY_SIG`: setting this variable to "1" enables the shared state signing feature. - :term:`SSTATE_VALID_SIGS`: a space-separated list of key identifiers for which shared state artifacts are accepted. This means that shared state will be reused **only if it was signed with the private key corresponding to key identifier**. You'll notice the short-form of the key identifier here, which are the last 16 characters of the long-form key identifier shown with ``gpg --list-keys`` (in bold text below): .. parsed-literal:: pub ed25519 2026-04-17 [SC] \4049A47E3AAA99D0250966DC\ **5B97632FA7F4E942** uid [ultimate] Antonin Godard (SState Signing) sub cv25519 2026-04-17 [E] Let's verify that signature verification works: #. First, remove temporary outputs (:term:`TMPDIR`) from the previous builds, to make the :term:`OpenEmbedded Build System` rebuild everything using the shared state: .. code-block:: console $ rm -rf tmp/ #. Then, run the task again: .. code-block:: console $ bitbake gettext-minimal-native -c create_recipe_spdx #. To make sure the shared state artifact was successfully used, look for the :ref:`setscene ` task for ``create_recipe_spdx`` in the latest log file from :term:`BitBake`: .. code-block:: console $ grep create_recipe_spdx_setscene tmp/log/cooker//console-latest.log NOTE: Running setscene task 1 of 1 (../layers/openembedded-core/meta/recipes-core/gettext/gettext-minimal-native_1.0.bb:do_create_recipe_spdx_setscene) NOTE: recipe gettext-minimal-native-1.0-r0: task do_create_recipe_spdx_setscene: Started NOTE: recipe gettext-minimal-native-1.0-r0: task do_create_recipe_spdx_setscene: Succeeded The shared state was successfully verified and used! .. note:: To make sure shared state verification is working, you can set a "fake" public key identifier in :term:`SSTATE_VALID_SIGS`:: SSTATE_VALID_SIGS = "CAFECAFECAFECAFE" Remove the temporary outputs again: .. code-block:: console $ rm -r tmp/ Now, try executing the task: .. code-block:: console $ bitbake gettext-minimal-native -c create_recipe_spdx You should have warnings from :term:`BitBake` printed on the console: .. code-block:: text WARNING: gettext-minimal-native-1.0-r0 do_create_recipe_spdx_setscene: No accepted signatures found. Good signatures found: 5B97632FA7F4E942. WARNING: gettext-minimal-native-1.0-r0 do_create_recipe_spdx_setscene: Cannot verify signature on sstate package ../build/sstate-cache/universal/a3/6e/sstate:gettext-minimal-native:x86_64-linux:1.0:r0:x86_64:14:a36ef66df6b8c0cb5a849bc70a99dcfd61e4bacd11cefe6bbaf4978b2b3b617a_create_recipe_spdx.tar.zst, skipping acceleration... WARNING: gettext-minimal-native-1.0-r0 do_create_recipe_spdx_setscene: No sstate archive obtainable, will run full task instead. WARNING: Logfile for failed setscene task is ../build/tmp/work/x86_64-linux/gettext-minimal-native/1.0/temp/log.do_create_recipe_spdx_setscene.6994 WARNING: Setscene task (../layers/openembedded-core/meta/recipes-core/gettext/gettext-minimal-native_1.0.bb:do_create_recipe_spdx_setscene) failed with exit code '1' - real task will be run instead As you can see, this does not prevent :term:`BitBake` from continuing, but the real task is executed instead of re-using the shared state.