From patchwork Tue Apr 28 08:28:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonin Godard X-Patchwork-Id: 87049 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7285FFF886C for ; Tue, 28 Apr 2026 08:29:19 +0000 (UTC) Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.8583.1777364950747972994 for ; Tue, 28 Apr 2026 01:29:12 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@bootlin.com header.s=dkim header.b=ace4WfY7; spf=pass (domain: bootlin.com, ip: 185.246.85.4, mailfrom: antonin.godard@bootlin.com) Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id D74124E42B5C for ; Tue, 28 Apr 2026 08:29:08 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id A16EE601D0 for ; Tue, 28 Apr 2026 08:29:08 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 73C1110728522; Tue, 28 Apr 2026 10:29:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1777364948; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding; bh=pK60vqhMWtcoQWwOYiEuJBWjvrYrbrJ15Itu8dSRcvU=; b=ace4WfY7NJMo6i097YGJktc15Cg6PlPyxP5y/VXrGMzdrDaQdNnTHky1khzmqaIFnbw7u6 Ze3B/55tBzYVM9UVi703HY9xDW5oGxTVAs/dnEicbU+9kf9kvTI/1jeqQajpcFJCkTX5Fp SQ2jPJO/HN7mvpLUeRfXQs7B2i7jZA+/mVwl3vBFLZDE1ldAItf8dJRXHRME66I755NLa7 4diYvAKJBOyQiJBptQMvZpVXWU2zR1tWkO/E0Lx1DsaHTtMyomROCmfvL/MKkckNJPKZ2U e8Kq9md/NRJmQzda522U5d8KZV5raU1RWcTD+xQ2tsD88bbN9+xEIx8jRl7YRA== From: Antonin Godard Date: Tue, 28 Apr 2026 10:28:54 +0200 Subject: [PATCH v3] Document shared state signing MIME-Version: 1.0 Message-Id: <20260428-sstate-signing-v3-1-52cc8177b4ff@bootlin.com> X-B4-Tracking: v=1; b=H4sIAAAAAAAC/3XNTQ6CMBCG4auYrq1hhp8GV97DuIAyhTHaGlobD eHuUtxojMs3+eaZSXgambzYbyYxUmTPzi6RbzdCD43tSXK3tMAMq6wAJb0PTSDpubdse2nqSqt SE9SgxHJ0G8nwYwWPp3f7e3smHZKSFgP74Mbn+jFC2v3FI0iQZWcAKsixqOnQOhcubHfaXUXiI 34ACD8ALoBqS4WAYNB038A8zy/hz4nyAQEAAA== X-Change-ID: 20260417-sstate-signing-f96c75ce1917 To: docs@lists.yoctoproject.org Cc: Thomas Petazzoni , Antonin Godard X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=17664; i=antonin.godard@bootlin.com; h=from:subject:message-id; bh=ny7eED7Myd01tcnb3rA+fGHScYvQ4qn6t89/D+iPl6M=; b=owEBbQKS/ZANAwAKAdGAQUApo6g2AcsmYgBp8G/TiuIM3Sh8DanfrGmuYILJ3Tbjue70EXOXs tyazwJt2xyJAjMEAAEKAB0WIQSGSHJRiN1AG7mg0//RgEFAKaOoNgUCafBv0wAKCRDRgEFAKaOo Ns3/D/9K5dUmCk27xIrPC0nHkTtFPalAdEywHN6Z2BZObwdLbZHUdSE41IrsnnTg8e1oHgvmm3r AVBNEwI0vTt5e4AsdIzshsfZHsqe9ae+SFrt5Atl/Wt0LlTn+LJpa+2xeG8tCmNCb4Feq8YVB2M 3qmytZh6QgMfdF62y4F28ex+168URfCGKC9gUXvZe2iY3dJzB0GTl9FYarxcwwr98DqKMvz5AU2 qUjeoIXHflfz4MvJvAr3bE+ZXuScElUUdatuM9q5Vnd6Mh2MQP/veP0aHz2gmF/SWayIcKbxaJQ IMOej/KwSKtMlT1KU0JkP2w9ZATc9eKu0a2IVCeMQ7njB795FFDYLIjNN4Tk1MXWg0AxuGmrssF r7kEh+S6SEdhxc7nVsEPFxsjO4VLxKpBShY7rIzGtO9Tsn0+Y8To/aaAqc2nkMJblO6bw981W+y Y8Swivk8Q30Negb904RAtgZCin4xx3G536Z8IEmB5MwtU1V91irvaVIjPQObmb3a/Uv5lLso7Tx BhVe4cKWXMsG/AEznIOlcnKHG8KNMy6PV7Kod1bygjrIZvAUNW3+As5P/qV1xhbxAuIj1W1RhtW 3XSpfV6YCYJJYm2/U+lf9K6eUHc9pQv26aYUk2CU+peb4UyrXBFH4s+1GMPNDeFEk32WSq2ftBe KG/d71sgHEJJcdA== X-Developer-Key: i=antonin.godard@bootlin.com; a=openpgp; fpr=8648725188DD401BB9A0D3FFD180414029A3A836 X-Last-TLS-Session-Version: TLSv1.3 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 28 Apr 2026 08:29:19 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/docs/message/9378 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 --- 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 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 + `. 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 ` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to + "1"), the :term:`SSTATE_SIG_KEY` variable is the :wikipedia:`GPG + ` 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 ` 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 ` @@ -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 ` artifacts (when :term:`SSTATE_VERIFY_SIG` is set to + "1"), the :term:`SSTATE_VALID_SIGS` variable is a space-separated list of + :wikipedia:`GPG ` 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) + 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 ` 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) `. 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 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) + 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 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 +`, 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.