.. 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) <overview-manual/concepts:shared state
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 <GNU_Privacy_Guard>`, meaning
examples shown in this document will use the ``gpg`` command-line tool.

Host Requirements
=================

As :wikipedia:`GPG <GNU_Privacy_Guard>` is not part of the default :ref:`host
requirements <ref-manual/system-requirements:Required Packages for the Build
Host>`, 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) <antonin.godard\@bootlin.com>
   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 = "<your GPG key 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 <security-manual/sstate-signing:Generating
   A Public And Private Key Pair With GPG>`.

-  :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
<structure-build-conf-site.conf>`, 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) <antonin.godard\@bootlin.com>
      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 <overview-manual/concepts:setscene tasks and shared state>` task
   for ``create_recipe_spdx`` in the latest log file from :term:`BitBake`:

   .. code-block:: console

      $ grep create_recipe_spdx_setscene tmp/log/cooker/<machine>/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.
