diff mbox series

[v3] Document shared state signing

Message ID 20260428-sstate-signing-v3-1-52cc8177b4ff@bootlin.com
State New
Headers show
Series [v3] Document shared state signing | expand

Commit Message

Antonin Godard April 28, 2026, 8:28 a.m. UTC
Document the shared state signing feature. Add a new document in the
Security manual, and definitions for the variables involved in this
process in the variable glossary.

[YOCTO #15217]

Signed-off-by: Antonin Godard <antonin.godard@bootlin.com>
---
Changes in v3:
- Rework after Quentin's suggestions (thanks!)
- Link to v2: https://patch.msgid.link/20260421-sstate-signing-v2-1-7b572121f2fd@bootlin.com

Changes in v2:
- Fix typos reported by Ulrich (thanks!)
- Link to v1: https://patch.msgid.link/20260417-sstate-signing-v1-1-5df11613249e@bootlin.com
---
 documentation/overview-manual/concepts.rst       |   6 +
 documentation/ref-manual/variables.rst           |  57 +++++
 documentation/security-manual/index.rst          |   1 +
 documentation/security-manual/sstate-signing.rst | 285 +++++++++++++++++++++++
 4 files changed, 349 insertions(+)


---
base-commit: eb74fdfd9e5e579a65e8872d1a73b51e77b14f63
change-id: 20260417-sstate-signing-f96c75ce1917

Comments

Quentin Schulz May 7, 2026, 9:49 a.m. UTC | #1
Hi Antonin,

On 4/28/26 10:28 AM, Antonin Godard via lists.yoctoproject.org wrote:
> Document the shared state signing feature. Add a new document in the
> Security manual, and definitions for the variables involved in this
> process in the variable glossary.
> 
> [YOCTO #15217]
> 
> Signed-off-by: Antonin Godard <antonin.godard@bootlin.com>
> ---
> Changes in v3:
> - Rework after Quentin's suggestions (thanks!)
> - Link to v2: https://patch.msgid.link/20260421-sstate-signing-v2-1-7b572121f2fd@bootlin.com
> 
> Changes in v2:
> - Fix typos reported by Ulrich (thanks!)
> - Link to v1: https://patch.msgid.link/20260417-sstate-signing-v1-1-5df11613249e@bootlin.com
> ---
>   documentation/overview-manual/concepts.rst       |   6 +
>   documentation/ref-manual/variables.rst           |  57 +++++
>   documentation/security-manual/index.rst          |   1 +
>   documentation/security-manual/sstate-signing.rst | 285 +++++++++++++++++++++++
>   4 files changed, 349 insertions(+)
> 
> diff --git a/documentation/overview-manual/concepts.rst b/documentation/overview-manual/concepts.rst
> index 1faa790f3..3f3093bfc 100644
> --- a/documentation/overview-manual/concepts.rst
> +++ b/documentation/overview-manual/concepts.rst
> @@ -1239,6 +1239,12 @@ variable is the function that determines whether a given dependency
>   needs to be followed, and whether for any given relationship the
>   function needs to be passed. The function returns a True or False value.
>   
> +.. note::
> +
> +   Is is possible to sign these artifacts with :wikipedia:`GPG

typo: s/Is/It/

> +   <GNU_Privacy_Guard>`. See :doc:`/security-manual/sstate-signing` in the Yocto
> +   Project Security Manual for more information.
> +
>   Images
>   ------
>   
> diff --git a/documentation/ref-manual/variables.rst b/documentation/ref-manual/variables.rst
> index e713204e3..3b3aeed4c 100644
> --- a/documentation/ref-manual/variables.rst
> +++ b/documentation/ref-manual/variables.rst
> @@ -10113,6 +10113,25 @@ system and gives an overview of their function and contents.
>   
>         For details on the process, see the :ref:`ref-classes-staging` class.
>   
> +   :term:`SSTATE_SIG_KEY`
> +      When signing :ref:`shared state <overview-manual/concepts:setscene tasks
> +      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
> +      "1"), the :term:`SSTATE_SIG_KEY` variable is the :wikipedia:`GPG
> +      <GNU_Privacy_Guard>` key identifier used to sign them (with the private
> +      key).
> +

thought: I'm not sure we need to specify that the private key is used 
for signing. That's what signing in asymmetric cryptography is?

> +      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
> +      Manual for more information.
> +
> +   :term:`SSTATE_SIG_PASSPHRASE`
> +      When signing :ref:`shared state <overview-manual/concepts:setscene tasks
> +      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
> +      "1"), the :term:`SSTATE_SIG_PASSPHRASE` variable is the passphrase used to
> +      protect the private key signing the artifacts.
> +
> +      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
> +      Manual for more information.
> +
thought: implement a file-based passphrase?

The detach_sign() function actually supports providing the passphrase 
either as a string or from a file. The file-based implem seems to be 
actually used for signing (ipk, rpm, deb) packages, see 
IPK_GPG_PASSPHRASE_FILE and PACKAGE_FEED_GPG_PASSPHRASE_FILE.

How much better that is in terms of security, unclear, as the file still 
needs to be readable but I guess at least the passphrase cannot leak 
when looking at the datastore (e.g. with bitbake -e).

>      :term:`SSTATE_SKIP_CREATION`
>         The :term:`SSTATE_SKIP_CREATION` variable can be used to skip the
>         creation of :ref:`shared state <overview-manual/concepts:shared state cache>`
> @@ -10133,6 +10152,44 @@ system and gives an overview of their function and contents.
>   
>            SSTATE_SKIP_CREATION = "1"
>   
> +   :term:`SSTATE_VALID_SIGS`
> +      When verifying :ref:`shared state <overview-manual/concepts:setscene tasks
> +      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
> +      "1"), the :term:`SSTATE_VALID_SIGS` variable is a space-separated list of
> +      :wikipedia:`GPG <GNU_Privacy_Guard>` key identifiers to use to verify their
> +      signature.
> +
> +      It must contain the short form identifier of the key pair. For example,
> +      when running the ``gpg --list-keys`` command (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]
> +
> +      The short form equals the last 16 characters of the identifier. In the
> +      above example: ``5B97632FA7F4E942``.
> +
> +      .. note::
> +
> +         Leaving this variable empty will make the :term:`OpenEmbedded Build
> +         System` let any key installed on the :term:`Build Host` be used for
> +         verify the shared state artifacts, as long as its private key

typo: s/verify/verifying/

suggestion (non-blocking): reword

A bit heavy to read, what about:

"""
If this variable is empty (the default), any of the available GPG key 
can be used by the :term:`OpenEmbedded Build System` to verify the 
shared state artifacts.
"""

In any case, I would strip the "as long as" part of the sentence, as 
that's how asymmetric cryptography works, you cannot validate a 
signature with a public key that isn't the other in the priavte-public 
key pair, that's the whole point of verifying a signature.

> +         counterpart was used for signing them.
> +
> +      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
> +      Manual for more information.
> +
> +   :term:`SSTATE_VERIFY_SIG`
> +      The :term:`SSTATE_VERIFY_SIG` variable controls whether to enable or
> +      disable the :ref:`shared state <overview-manual/concepts:setscene tasks
> +      and shared state>` artifacts signing feature.
> +
> +      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
> +      Manual for more information.
> +
>      :term:`STAGING_BASE_LIBDIR_NATIVE`
>         Specifies the path to the ``/lib`` subdirectory of the sysroot
>         directory for the build host.
> diff --git a/documentation/security-manual/index.rst b/documentation/security-manual/index.rst
> index 3453940f5..a767cd9c6 100644
> --- a/documentation/security-manual/index.rst
> +++ b/documentation/security-manual/index.rst
> @@ -14,6 +14,7 @@ Yocto Project Security Manual
>      securing-images
>      vulnerabilities
>      read-only-rootfs
> +   sstate-signing
>   
>   .. include:: /boilerplate.rst
>   
> diff --git a/documentation/security-manual/sstate-signing.rst b/documentation/security-manual/sstate-signing.rst
> new file mode 100644
> index 000000000..21cdff9fc
> --- /dev/null
> +++ b/documentation/security-manual/sstate-signing.rst
> @@ -0,0 +1,285 @@
> +.. 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 name.

typo: s/Debian based/Debian-based/

typo: s/package name/package/

suggestion (non-blocking): s/with/in/

> +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.
> +

issue: can you please synchronize this example with the one in 
SSTATE_VALID_SIGS?

> +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 when creating the key, chosen when creating the key pair.
> +

issue: redundant "when creating the key"

I can suggest removing the first "when creating the key", the end of the 
sentence is better worded.

> +Let's test the configuration:
> +
> +#. Continuing with the ``gettext-minimal-native`` example, let's first clean the
> +   existing shared state artifacts, to make sure my shared state artifacts for
> +   my ``do_create_recipe_spdx`` task are re-generated:
> +

issue: confusing switch to first-person

We've been using the second person (you, let's, ...) until now.

> +   .. code-block:: console
> +
> +      $ bitbake gettext-minimal-native -c cleansstate
> +
> +#. 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
> +

TIL about -R :)

Another option is to make conf/local.conf `require` it?

> +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/
> +

question: Will this actually work?

In the signing example, we only triggered a rebuild of 
gettext-minimal-native's create_recipe_spdx (and earlier) tasks. Every 
task which isn't from that recipe wasn't rebuilt as we only cleaned the 
sstate-cache from that recipe, therefore we would only use the shared 
state cache for the gettext-minimal-native recipe.

Cheers,
Quentin
diff mbox series

Patch

diff --git a/documentation/overview-manual/concepts.rst b/documentation/overview-manual/concepts.rst
index 1faa790f3..3f3093bfc 100644
--- a/documentation/overview-manual/concepts.rst
+++ b/documentation/overview-manual/concepts.rst
@@ -1239,6 +1239,12 @@  variable is the function that determines whether a given dependency
 needs to be followed, and whether for any given relationship the
 function needs to be passed. The function returns a True or False value.
 
+.. note::
+
+   Is is possible to sign these artifacts with :wikipedia:`GPG
+   <GNU_Privacy_Guard>`. See :doc:`/security-manual/sstate-signing` in the Yocto
+   Project Security Manual for more information.
+
 Images
 ------
 
diff --git a/documentation/ref-manual/variables.rst b/documentation/ref-manual/variables.rst
index e713204e3..3b3aeed4c 100644
--- a/documentation/ref-manual/variables.rst
+++ b/documentation/ref-manual/variables.rst
@@ -10113,6 +10113,25 @@  system and gives an overview of their function and contents.
 
       For details on the process, see the :ref:`ref-classes-staging` class.
 
+   :term:`SSTATE_SIG_KEY`
+      When signing :ref:`shared state <overview-manual/concepts:setscene tasks
+      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
+      "1"), the :term:`SSTATE_SIG_KEY` variable is the :wikipedia:`GPG
+      <GNU_Privacy_Guard>` key identifier used to sign them (with the private
+      key).
+
+      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
+      Manual for more information.
+
+   :term:`SSTATE_SIG_PASSPHRASE`
+      When signing :ref:`shared state <overview-manual/concepts:setscene tasks
+      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
+      "1"), the :term:`SSTATE_SIG_PASSPHRASE` variable is the passphrase used to
+      protect the private key signing the artifacts.
+
+      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
+      Manual for more information.
+
    :term:`SSTATE_SKIP_CREATION`
       The :term:`SSTATE_SKIP_CREATION` variable can be used to skip the
       creation of :ref:`shared state <overview-manual/concepts:shared state cache>`
@@ -10133,6 +10152,44 @@  system and gives an overview of their function and contents.
 
          SSTATE_SKIP_CREATION = "1"
 
+   :term:`SSTATE_VALID_SIGS`
+      When verifying :ref:`shared state <overview-manual/concepts:setscene tasks
+      and shared state>` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to
+      "1"), the :term:`SSTATE_VALID_SIGS` variable is a space-separated list of
+      :wikipedia:`GPG <GNU_Privacy_Guard>` key identifiers to use to verify their
+      signature.
+
+      It must contain the short form identifier of the key pair. For example,
+      when running the ``gpg --list-keys`` command (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]
+
+      The short form equals the last 16 characters of the identifier. In the
+      above example: ``5B97632FA7F4E942``.
+
+      .. note::
+
+         Leaving this variable empty will make the :term:`OpenEmbedded Build
+         System` let any key installed on the :term:`Build Host` be used for
+         verify the shared state artifacts, as long as its private key
+         counterpart was used for signing them.
+
+      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
+      Manual for more information.
+
+   :term:`SSTATE_VERIFY_SIG`
+      The :term:`SSTATE_VERIFY_SIG` variable controls whether to enable or
+      disable the :ref:`shared state <overview-manual/concepts:setscene tasks
+      and shared state>` artifacts signing feature.
+
+      See :doc:`/security-manual/sstate-signing` in the Yocto Project Security
+      Manual for more information.
+
    :term:`STAGING_BASE_LIBDIR_NATIVE`
       Specifies the path to the ``/lib`` subdirectory of the sysroot
       directory for the build host.
diff --git a/documentation/security-manual/index.rst b/documentation/security-manual/index.rst
index 3453940f5..a767cd9c6 100644
--- a/documentation/security-manual/index.rst
+++ b/documentation/security-manual/index.rst
@@ -14,6 +14,7 @@  Yocto Project Security Manual
    securing-images
    vulnerabilities
    read-only-rootfs
+   sstate-signing
 
 .. include:: /boilerplate.rst
 
diff --git a/documentation/security-manual/sstate-signing.rst b/documentation/security-manual/sstate-signing.rst
new file mode 100644
index 000000000..21cdff9fc
--- /dev/null
+++ b/documentation/security-manual/sstate-signing.rst
@@ -0,0 +1,285 @@ 
+.. 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 name.
+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 when creating the key, chosen when creating the key pair.
+
+Let's test the configuration:
+
+#. Continuing with the ``gettext-minimal-native`` example, let's first clean the
+   existing shared state artifacts, to make sure my shared state artifacts for
+   my ``do_create_recipe_spdx`` task are re-generated:
+
+   .. code-block:: console
+
+      $ bitbake gettext-minimal-native -c cleansstate
+
+#. 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.