Show a patch.

GET /api/patches/19419/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 19419,
    "url": "https://patchwork.libcamera.org/api/patches/19419/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/19419/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20240120143742.1302914-1-arnout@bzzt.net>",
    "date": "2024-01-20T14:37:42",
    "name": "[libcamera-devel] libcamera: ipa: allow trusting modules by checksum",
    "commit_ref": null,
    "pull_url": null,
    "state": "changes-requested",
    "archived": false,
    "hash": "0e01ea5cdab9fbe68cf73b52234b27fcae17b10b",
    "submitter": {
        "id": 180,
        "url": "https://patchwork.libcamera.org/api/people/180/?format=api",
        "name": "Arnout Engelen",
        "email": "arnout@bzzt.net"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/19419/mbox/",
    "series": [
        {
            "id": 4147,
            "url": "https://patchwork.libcamera.org/api/series/4147/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4147",
            "date": "2024-01-20T14:37:42",
            "name": "[libcamera-devel] libcamera: ipa: allow trusting modules by checksum",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/4147/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/19419/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/19419/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 62ECFC323E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 20 Jan 2024 14:56:17 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5249762916;\n\tSat, 20 Jan 2024 15:56:16 +0100 (CET)",
            "from wfhigh4-smtp.messagingengine.com\n\t(wfhigh4-smtp.messagingengine.com [64.147.123.155])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A909761D41\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 20 Jan 2024 15:38:17 +0100 (CET)",
            "from compute4.internal (compute4.nyi.internal [10.202.2.44])\n\tby mailfhigh.west.internal (Postfix) with ESMTP id C11BB180006C;\n\tSat, 20 Jan 2024 09:38:12 -0500 (EST)",
            "from mailfrontend2 ([10.202.2.163])\n\tby compute4.internal (MEProxy); Sat, 20 Jan 2024 09:38:13 -0500",
            "by mail.messagingengine.com (Postfix) with ESMTPA; Sat,\n\t20 Jan 2024 09:38:11 -0500 (EST)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1705762576;\n\tbh=kw5BTiQzmmYcKnfUwiIbvcebmI5PhOava7XV6mK56TY=;\n\th=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post:\n\tList-Help:List-Subscribe:From:Reply-To:Cc:From;\n\tb=xFivMQZo31B4CMfQ+sdvvEt7+CyLy2qani68HYwWT+YnZtRPbrAq/dFGmTEbZQZc8\n\tqoaPKQdhUEeRxylYF+3lGrMr9d6KYUClq7Qm+gBC7GXzzKYpS189mWvaw62GmxSRLm\n\t6WMLC/dfn1hHtElKVrYIOTmK/1RpV3OJVS7sx6QLRw4Swgs+1BHZlfQOxIh6j5FCPN\n\tXNPE+l4wVPoOzc4gYQQL8YoLGjxPP/DzIoHjZJlF115Zr61XSBpPPAoI1vXfIYN/my\n\tCaBLfkItHKdH8MeleTJFyXxcVReV/WRh2MGvXdDh+NCtR0e64oUV0MlZtWB9Sv2aN9\n\to+6ZmU0A+sOcg==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=bzzt.net; h=cc\n\t:cc:content-transfer-encoding:content-type:date:date:from:from\n\t:in-reply-to:message-id:mime-version:reply-to:subject:subject:to\n\t:to; s=fm1; t=1705761492; x=1705847892; bh=YFRsnRsyGZ/KFR1r47jQt\n\tpLKtU8kQ8rqTXjElMZZL7Y=; b=iKokYVv1gIqGIZDnohAOf9Eiq7zrucHUHkPxt\n\tOF/BqAPIyUy7VnlnOivF/dnWajfhkv2S8zc5nlh2Fw1oXRxtzsJq3X5fbt7Ms+/M\n\tMX0yECchkeYZt1vjyMJzdr8PRX3coEeRUyVP3gnCJZI7BicaTngx+RwLw6BCBh/I\n\tRjkU2HkoTW5gV/82s4NnouwxAfZ/E4CrChR8pMbj/oBtTZTGn/wOPpZp3/v6L/ia\n\tfZSdAckEoazYpa63u08d9S7P9KWRqE+Ihe/qHyRRZJygIe1w82jgRbRICJ2mYWXx\n\tt5f/1VVucyQb5b0RQLNl1y+qDKGksvxxFp2Rhg6L5N/Gf4/AA==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=\n\tmessagingengine.com; h=cc:cc:content-transfer-encoding\n\t:content-type:date:date:feedback-id:feedback-id:from:from\n\t:in-reply-to:message-id:mime-version:reply-to:subject:subject:to\n\t:to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=\n\tfm3; t=1705761492; x=1705847892; bh=YFRsnRsyGZ/KFR1r47jQtpLKtU8k\n\tQ8rqTXjElMZZL7Y=; b=mvXZMFIBf80HXXtl/LjSl6XSML8uMnYfMsXJJhFW3tox\n\taLvf4oVnJrKHjdWlxYsKRMzG02f4AKPYgaXbb6vaH8vEBJjPBvGm6L+lMJkOp66F\n\tVuOXPsPmOlue7omfdHR5T2VwbxKaJJ5YZFe/fV1CFdM4o6aSWxTWXkBwBVphklTB\n\tUISJZ3hoSILU/Z/wFciiWuhfMU9K8B8DoVig36TgAMqtNwE9Cr1Hi55PlpUI+Qwu\n\tVqPDbcXrrJT6XIRLGB+zg1S76ZWcblSHTWWrrDkt1HKyG0cR2abTwo+66en+kG3l\n\tXxepoa8sBQKapSrJbIkZ+LZs2AItFWdjAVw93xVOBw=="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=bzzt.net header.i=@bzzt.net\n\theader.b=\"iKokYVv1\"; dkim=pass (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com\n\theader.b=\"mvXZMFIB\"; dkim-atps=neutral",
        "X-ME-Sender": "<xms:1NqrZdWG93ogzwB0wOMGknbqZmugt2TjW0vWxN8f7d8_wgwCPlSWHg>\n\t<xme:1NqrZdni2Qgx87UCaE2otDajAcikSSel_kkn1YMC4LOVTLjUZ_fpAITzv8E0DMMG2\n\tZJ-yaHSS2OulyT4WUo>",
        "X-ME-Received": "<xmr:1NqrZZbkAMxp5ttoTT4oNnHX1O_aswtHDX3zJG2Dnwf2IaaSYvCindlWN6ETT1O_REhB5CnTI11_fJILkThhWAqgijeYVTI>",
        "X-ME-Proxy-Cause": "gggruggvucftvghtrhhoucdtuddrgedvkedrvdekvddgieekucetufdoteggodetrfdotf\n\tfvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen\n\tuceurghilhhouhhtmecufedttdenucenucfjughrpefhvfevufffkffoggfgsedtkeertd\n\tertddtnecuhfhrohhmpeetrhhnohhuthcugfhnghgvlhgvnhcuoegrrhhnohhuthessgii\n\tiihtrdhnvghtqeenucggtffrrghtthgvrhhnpefhkeegiefgieeutdehvedthfdtjeevfe\n\tejvdevfeehffduudekhefgfeekvdekleenucffohhmrghinheprhgvphhrohguuhgtihgs\n\tlhgvqdgsuhhilhgushdrohhrghenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmh\n\tepmhgrihhlfhhrohhmpegrrhhnohhuthessgiiiihtrdhnvght",
        "X-ME-Proxy": "<xmx:1NqrZQWE67yiHFQaAAY1LPkchUOoxr05lfs2BXhLE9HfIZopGuu5Qw>\n\t<xmx:1NqrZXl4w0_OzkZxlFSGo4BEqSqhmhAkZeHHgzaMc6-SHJgNhTQHGQ>\n\t<xmx:1NqrZded6RLOrFXI_jIdVJQaGO1hccABcwCvIemKP00RDKOgXOUh9g>\n\t<xmx:1NqrZcy7Tf0eSo4in3dyPKTDyO4gUZb5EH2pDoF2FphNLHKHkmhINVf4jPQ>",
        "Feedback-ID": "i8a1146c4:Fastmail",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Sat, 20 Jan 2024 15:37:42 +0100",
        "Message-ID": "<20240120143742.1302914-1-arnout@bzzt.net>",
        "X-Mailer": "git-send-email 2.43.0",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Mailman-Approved-At": "Sat, 20 Jan 2024 15:56:14 +0100",
        "Subject": "[libcamera-devel] [PATCH] libcamera: ipa: allow trusting modules by\n\tchecksum",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "From": "Arnout Engelen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Arnout Engelen <arnout@bzzt.net>",
        "Cc": "Arnout Engelen <arnout@bzzt.net>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Currently, libcamera signs the in-tree IPA modules during the build, and embeds\nthe public key so that only trusted IPA modules will be run in-process.\nOut-of-tree modules will be run using runtime isolation.\n\nThis commit adds a second mechanism to achieve the same: during packaging the\nchecksums of the in-tree IPA modules are recorded, and at run time the modules\nare considered trusted when they match the recorded trusted checksums.\n\nThe motivation behind adding this mechanism is that this allows rebuilding the\nlibrary and getting a bit-by-bit identical result, without having to share the\nkeys with which to sign the trusted modules. This is known as 'Reproducible\nBuilds', and you can read more about its advantages on\nhttps://reproducible-builds.org/. With this feature, packagers that care about\nreproducible builds can disable the module signing, and enjoy equivalent\nsecurity and performance while also allowing independent rebuilds.\n\nSigned-off-by: Arnout Engelen <arnout@bzzt.net>\n---\n Documentation/environment_variables.rst  |  3 +\n include/libcamera/internal/ipa_manager.h | 10 ++-\n include/libcamera/internal/ipa_module.h  |  2 +\n meson_options.txt                        |  4 +\n src/apps/ipa-verify/main.cpp             | 45 ++++++++++-\n src/apps/ipa-verify/meson.build          |  2 +-\n src/ipa/ipa-checksum-install.sh          | 22 ++++++\n src/ipa/meson.build                      | 11 +++\n src/libcamera/ipa_manager.cpp            | 99 ++++++++++++++++++++++--\n src/libcamera/ipa_module.cpp             | 30 +++++++\n src/meson.build                          | 17 +++-\n 11 files changed, 229 insertions(+), 16 deletions(-)\n create mode 100644 src/ipa/ipa-checksum-install.sh",
    "diff": "diff --git a/Documentation/environment_variables.rst b/Documentation/environment_variables.rst\nindex a9b230bc..baae225c 100644\n--- a/Documentation/environment_variables.rst\n+++ b/Documentation/environment_variables.rst\n@@ -32,6 +32,9 @@ LIBCAMERA_IPA_FORCE_ISOLATION\n \n    Example value: ``1``\n \n+LIBCAMERA_IPA_TRUSTED_MODULE_CHECKSUMS_FILE\n+   Define custom location of the trusted IPA module checksums file.\n+\n LIBCAMERA_IPA_MODULE_PATH\n    Define custom search locations for IPA modules (`more <IPA module_>`__).\n \ndiff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\nindex bf823563..c05583e8 100644\n--- a/include/libcamera/internal/ipa_manager.h\n+++ b/include/libcamera/internal/ipa_manager.h\n@@ -38,7 +38,7 @@ public:\n \t\tif (!m)\n \t\t\treturn nullptr;\n \n-\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self_->isSignatureValid(m));\n+\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self_->isTrusted(m));\n \t\tif (!proxy->isValid()) {\n \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n \t\t\treturn nullptr;\n@@ -53,6 +53,8 @@ public:\n \t\treturn pubKey_;\n \t}\n #endif\n+\tstatic int loadTrustedChecksums(const char *path,\n+\t\t\t\t\tstd::vector<std::vector<uint8_t>> &checksums);\n \n private:\n \tstatic IPAManager *self_;\n@@ -65,6 +67,8 @@ private:\n \t\t\t  uint32_t maxVersion);\n \n \tbool isSignatureValid(IPAModule *ipa) const;\n+\tbool isTrustedChecksum(IPAModule *ipa) const;\n+\tbool isTrusted(IPAModule *ipa) const;\n \n \tstd::vector<IPAModule *> modules_;\n \n@@ -72,6 +76,10 @@ private:\n \tstatic const uint8_t publicKeyData_[];\n \tstatic const PubKey pubKey_;\n #endif\n+\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tstd::vector<std::vector<uint8_t>> trusted_checksums_;\n+#endif\n };\n \n } /* namespace libcamera */\ndiff --git a/include/libcamera/internal/ipa_module.h b/include/libcamera/internal/ipa_module.h\nindex 8038bdee..f4c0ec5c 100644\n--- a/include/libcamera/internal/ipa_module.h\n+++ b/include/libcamera/internal/ipa_module.h\n@@ -30,6 +30,7 @@ public:\n \n \tconst struct IPAModuleInfo &info() const;\n \tconst std::vector<uint8_t> signature() const;\n+\tconst std::vector<uint8_t> checksum() const;\n \tconst std::string &path() const;\n \n \tbool load();\n@@ -47,6 +48,7 @@ private:\n \n \tstruct IPAModuleInfo info_;\n \tstd::vector<uint8_t> signature_;\n+\tstd::vector<uint8_t> checksum_;\n \n \tstd::string libPath_;\n \tbool valid_;\ndiff --git a/meson_options.txt b/meson_options.txt\nindex 5fdc7be8..a970ac30 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -30,6 +30,10 @@ option('ipas',\n         choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],\n         description : 'Select which IPA modules to build')\n \n+option('ipa_sign_modules',\n+        type : 'feature',\n+        description : 'enable IPA trusted module signing')\n+\n option('lc-compliance',\n         type : 'feature',\n         value : 'auto',\ndiff --git a/src/apps/ipa-verify/main.cpp b/src/apps/ipa-verify/main.cpp\nindex 76ba5073..368de4a0 100644\n--- a/src/apps/ipa-verify/main.cpp\n+++ b/src/apps/ipa-verify/main.cpp\n@@ -18,8 +18,9 @@ using namespace libcamera;\n \n namespace {\n \n-bool isSignatureValid(IPAModule *ipa)\n+bool isSignatureValid([[maybe_unused]] IPAModule *ipa)\n {\n+#if HAVE_IPA_PUBKEY\n \tFile file{ ipa->path() };\n \tif (!file.open(File::OpenModeFlag::ReadOnly))\n \t\treturn false;\n@@ -29,6 +30,32 @@ bool isSignatureValid(IPAModule *ipa)\n \t\treturn false;\n \n \treturn IPAManager::pubKey().verify(data, ipa->signature());\n+#else\n+\treturn false;\n+#endif\n+}\n+\n+bool isChecksumTrusted([[maybe_unused]] IPAModule *ipa)\n+{\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tstd::vector<std::vector<uint8_t>> trusted;\n+\tconst char *checksumsFile =\n+\t\tutils::secure_getenv(\"LIBCAMERA_IPA_TRUSTED_MODULE_CHECKSUMS_FILE\");\n+\tif (checksumsFile) {\n+\t\tIPAManager::loadTrustedChecksums(checksumsFile, trusted);\n+\t} else {\n+\t\tIPAManager::loadTrustedChecksums(IPA_TRUSTED_MODULE_CHECKSUMS_FILE, trusted);\n+\t}\n+\tfor (std::vector<uint8_t> t : trusted) {\n+\t\tif (std::equal(t.begin(), t.begin() + 32,\n+\t\t\t       ipa->checksum().begin())) {\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\treturn false;\n+#else\n+\treturn false;\n+#endif\n }\n \n void usage(char *argv0)\n@@ -54,11 +81,23 @@ int main(int argc, char **argv)\n \t\treturn EXIT_FAILURE;\n \t}\n \n-\tif (!isSignatureValid(&module)) {\n+\tbool ok = false;\n+\tif (isSignatureValid(&module)) {\n+\t\tstd::cout << \"IPA module signature is valid\" << std::endl;\n+\t\tok = true;\n+\t} else {\n \t\tstd::cout << \"IPA module signature is invalid\" << std::endl;\n+\t}\n+\tif (isChecksumTrusted(&module)) {\n+\t\tstd::cout << \"IPA module checksum is trusted\" << std::endl;\n+\t\tok = true;\n+\t} else {\n+\t\tstd::cout << \"IPA module checksum is not trusted\" << std::endl;\n+\t}\n+\n+\tif (!ok) {\n \t\treturn EXIT_FAILURE;\n \t}\n \n-\tstd::cout << \"IPA module signature is valid\" << std::endl;\n \treturn 0;\n }\ndiff --git a/src/apps/ipa-verify/meson.build b/src/apps/ipa-verify/meson.build\nindex 7fdda3b9..feffb538 100644\n--- a/src/apps/ipa-verify/meson.build\n+++ b/src/apps/ipa-verify/meson.build\n@@ -1,6 +1,6 @@\n # SPDX-License-Identifier: CC0-1.0\n \n-if not ipa_sign_module\n+if not ipa_sign_module and not ipa_checksum_trusted_modules\n     subdir_done()\n endif\n \ndiff --git a/src/ipa/ipa-checksum-install.sh b/src/ipa/ipa-checksum-install.sh\nnew file mode 100644\nindex 00000000..d8341c0c\n--- /dev/null\n+++ b/src/ipa/ipa-checksum-install.sh\n@@ -0,0 +1,22 @@\n+#!/bin/sh\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+# Copyright (C) 2020, Google Inc.\n+#\n+# Author: Arnout Engelen <arnout@bzzt.net>\n+#\n+# ipa-checksum-install.sh - Generate IPA module checksums when installing\n+\n+modules=$*\n+\n+echo \"Generating trusted IPA module checksums\"\n+\n+checksum_file=${MESON_INSTALL_DESTDIR_PREFIX}/share/libcamera/ipa/trusted_module_checksums.txt\n+\n+rm ${checksum_file} || true\n+\n+for module in ${modules} ; do\n+\tmodule=\"${MESON_INSTALL_DESTDIR_PREFIX}/${module}\"\n+\tif [ -f \"${module}\" ] ; then\n+        sha256sum -b \"${module}\" | sed -e \"s| .*/| |\" >> ${checksum_file}\n+\tfi\n+done\ndiff --git a/src/ipa/meson.build b/src/ipa/meson.build\nindex 48793e07..c3ae24ba 100644\n--- a/src/ipa/meson.build\n+++ b/src/ipa/meson.build\n@@ -8,6 +8,10 @@ ipa_install_dir = libcamera_libdir\n ipa_data_dir = libcamera_datadir / 'ipa'\n ipa_sysconf_dir = libcamera_sysconfdir / 'ipa'\n \n+config_h.set('IPA_TRUSTED_MODULE_CHECKSUMS_FILE',\n+             '\"' + get_option('prefix') / ipa_data_dir\n+                 + '/trusted_module_checksums.txt\"')\n+\n config_h.set('IPA_CONFIG_DIR',\n              '\"' + get_option('prefix') / ipa_sysconf_dir +\n              ':' + get_option('prefix') / ipa_data_dir + '\"')\n@@ -75,3 +79,10 @@ if ipa_sign_module\n                              enabled_ipa_modules,\n                              install_tag : 'runtime')\n endif\n+\n+if ipa_checksum_trusted_modules\n+    # Similarly, calculate checksums for the installed artifacts\n+    meson.add_install_script('ipa-checksum-install.sh',\n+                             enabled_ipa_modules,\n+                             install_tag : 'runtime')\n+endif\ndiff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp\nindex 7a4515d9..beb93de6 100644\n--- a/src/libcamera/ipa_manager.cpp\n+++ b/src/libcamera/ipa_manager.cpp\n@@ -114,6 +114,18 @@ IPAManager::IPAManager()\n \t\tLOG(IPAManager, Warning) << \"Public key not valid\";\n #endif\n \n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tconst char *checksumsFile =\n+\t\tutils::secure_getenv(\"LIBCAMERA_IPA_TRUSTED_MODULE_CHECKSUMS_FILE\");\n+\tif (checksumsFile) {\n+\t\tIPAManager::loadTrustedChecksums(checksumsFile,\n+\t\t\t\t\t\t trusted_checksums_);\n+\t} else {\n+\t\tIPAManager::loadTrustedChecksums(IPA_TRUSTED_MODULE_CHECKSUMS_FILE,\n+\t\t\t\t\t\t trusted_checksums_);\n+\t}\n+#endif\n+\n \tunsigned int ipaCount = 0;\n \n \t/* User-specified paths take precedence. */\n@@ -165,6 +177,41 @@ IPAManager::~IPAManager()\n \tself_ = nullptr;\n }\n \n+/**\n+ * \\brief Load trusted checksums\n+ * \\param[in] path The path to the file containing the trusted checksums\n+ * \\param[out] checksums A vector of checksums as 32-element byte vectors\n+ *\n+ * \\return Zero on success, an error code on failure.\n+ */\n+int IPAManager::loadTrustedChecksums([[maybe_unused]] const char *path, [[maybe_unused]] std::vector<std::vector<uint8_t>> &checksums)\n+{\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tFile file{ path };\n+\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n+\t\tLOG(IPAManager, Warning) << \"Failed to open trusted IPA checksums: \"\n+\t\t\t\t\t << strerror(-file.error());\n+\t\treturn file.error();\n+\t}\n+\tSpan<const uint8_t> span = file.map();\n+\tconst uint8_t* data = span.data();\n+\tconst int size = span.size();\n+\tfor (int i = 0; i < size - 64;) {\n+\t\tstd::vector<uint8_t> *checksum = new std::vector<uint8_t>();\n+\t\tfor (int c = 0; c < 64; c += 2) {\n+\t\t\tchar chr[3] = { static_cast<char>(data[i+c]), static_cast<char>(data[i+c+1]), '\\0' };\n+\t\t\tchecksum->push_back(strtol(chr, nullptr, 16));\n+\t\t}\n+\t\tchecksums.push_back(*checksum);\n+\t\twhile (data[i] != '\\n' && i < size) {\n+\t\t\ti++;\n+\t\t};\n+\t\ti++;\n+\t}\n+#endif\n+\treturn 0;\n+}\n+\n /**\n  * \\brief Identify shared library objects within a directory\n  * \\param[in] libDir The directory to search for shared objects\n@@ -295,14 +342,6 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,\n bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const\n {\n #if HAVE_IPA_PUBKEY\n-\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n-\tif (force && force[0] != '\\0') {\n-\t\tLOG(IPAManager, Debug)\n-\t\t\t<< \"Isolation of IPA module \" << ipa->path()\n-\t\t\t<< \" forced through environment variable\";\n-\t\treturn false;\n-\t}\n-\n \tFile file{ ipa->path() };\n \tif (!file.open(File::OpenModeFlag::ReadOnly))\n \t\treturn false;\n@@ -323,4 +362,48 @@ bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const\n #endif\n }\n \n+bool IPAManager::isTrustedChecksum([[maybe_unused]] IPAModule *ipa) const\n+{\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tFile file{ ipa->path() };\n+\tif (!file.open(File::OpenModeFlag::ReadOnly))\n+\t\treturn false;\n+\n+\tSpan<uint8_t> data = file.map();\n+\tif (data.empty())\n+\t\treturn false;\n+\n+\tbool valid = false;\n+\n+\tfor (std::vector<uint8_t> t : trusted_checksums_) {\n+\t\tif (std::equal(t.begin(), t.begin() + 32,\n+\t\t\t       ipa->checksum().begin())) {\n+\t\t\tvalid = true;\n+\t\t\tcontinue;\n+\t\t}\n+\t}\n+\n+\tLOG(IPAManager, Debug)\n+\t\t<< \"IPA module \" << ipa->path() << \" checksum is \"\n+\t\t<< (valid ? \"trusted\" : \"not trusted\");\n+\n+\treturn valid;\n+#else\n+\treturn false;\n+#endif\n+}\n+\n+bool IPAManager::isTrusted(IPAModule *ipa) const\n+{\n+\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n+\tif (force && force[0] != '\\0') {\n+\t\tLOG(IPAManager, Debug)\n+\t\t\t<< \"Isolation of IPA module \" << ipa->path()\n+\t\t\t<< \" forced through environment variable\";\n+\t\treturn false;\n+\t}\n+\treturn isTrustedChecksum(ipa) || isSignatureValid(ipa);\n+}\n+\n+\n } /* namespace libcamera */\ndiff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\nindex f2dd87e5..3d49432b 100644\n--- a/src/libcamera/ipa_module.cpp\n+++ b/src/libcamera/ipa_module.cpp\n@@ -27,6 +27,12 @@\n \n #include \"libcamera/internal/pipeline_handler.h\"\n \n+#if HAVE_CRYPTO\n+#include <openssl/sha.h>\n+#elif HAVE_GNUTLS\n+#include <gnutls/crypto.h>\n+#endif\n+\n /**\n  * \\file ipa_module.h\n  * \\brief Image Processing Algorithm module\n@@ -281,6 +287,17 @@ int IPAModule::loadIPAModuleInfo()\n \t}\n \n \tSpan<const uint8_t> data = file.map();\n+\n+\t/* Calculate the module checksum. */\n+\tuint8_t digest[32] = { 0 };\n+#if HAVE_CRYPTO\n+\tSHA256(data.data(), data.size(), digest);\n+#elif HAVE_GNUTLS\n+\tgnutls_hash_fast(GNUTLS_DIG_SHA256, data.data(), data.size(), digest);\n+#endif\n+\tchecksum_ = std::vector<uint8_t>(digest, digest + 32);\n+\n+\t/* Interpret the file. */\n \tint ret = elfVerifyIdent(data);\n \tif (ret) {\n \t\tLOG(IPAModule, Error) << \"IPA module is not an ELF file\";\n@@ -379,6 +396,19 @@ const std::vector<uint8_t> IPAModule::signature() const\n \treturn signature_;\n }\n \n+/**\n+ * \\brief Retrieve the IPA module checksum\n+ *\n+ * The IPA module checksum is loaded when the IPAModule instance is created.\n+ *\n+ * \\return The IPA module checksum\n+ */\n+const std::vector<uint8_t> IPAModule::checksum() const\n+{\n+\treturn checksum_;\n+}\n+\n+\n /**\n  * \\brief Retrieve the IPA module path\n  *\ndiff --git a/src/meson.build b/src/meson.build\nindex 165a77bb..6cfc3316 100644\n--- a/src/meson.build\n+++ b/src/meson.build\n@@ -14,19 +14,30 @@ summary({\n          'LIBCAMERA_SYSCONF_DIR' : config_h.get('LIBCAMERA_SYSCONF_DIR'),\n          }, section : 'Paths')\n \n+# Trusted module checksumming\n+ipa_checksum_trusted_modules = true\n+\n+if ipa_checksum_trusted_modules\n+    config_h.set('HAVE_IPA_TRUSTED_MODULE_CHECKSUMS', 1)\n+endif\n+\n # Module Signing\n-openssl = find_program('openssl', required : false)\n-if openssl.found()\n+openssl = find_program('openssl', required : get_option('ipa_sign_modules'))\n+if get_option('ipa_sign_modules').enabled() and openssl.found()\n     ipa_priv_key = custom_target('ipa-priv-key',\n                                  output : ['ipa-priv-key.pem'],\n                                  command : [gen_ipa_priv_key, '@OUTPUT@'])\n     config_h.set('HAVE_IPA_PUBKEY', 1)\n     ipa_sign_module = true\n else\n-    warning('openssl not found, all IPA modules will be isolated')\n     ipa_sign_module = false\n endif\n \n+if not ipa_checksum_trusted_modules and not ipa_sign_module\n+    warning('neither checksums nor signatures enabled,')\n+    warning('all IPA modules will be isolated')\n+endif\n+\n # libcamera must be built first as a dependency to the other components.\n subdir('libcamera')\n \n",
    "prefixes": [
        "libcamera-devel"
    ]
}