{"id":19444,"url":"https://patchwork.libcamera.org/api/patches/19444/?format=json","web_url":"https://patchwork.libcamera.org/patch/19444/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20240122114040.275771-2-libcamera@bzzt.net>","date":"2024-01-22T11:40:40","name":"[1/1] libcamera: ipa: allow trusting modules by checksum","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"a1ae0471bf92741100f892e5024ba277c53925af","submitter":{"id":182,"url":"https://patchwork.libcamera.org/api/people/182/?format=json","name":"Arnout Engelen","email":"libcamera@bzzt.net"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/19444/mbox/","series":[{"id":4153,"url":"https://patchwork.libcamera.org/api/series/4153/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=4153","date":"2024-01-22T11:40:39","name":"libcamera: ipa: allow trusting modules by checksum","version":1,"mbox":"https://patchwork.libcamera.org/series/4153/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/19444/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/19444/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 34F1DC3243\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 22 Jan 2024 11:41:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CFB776294B;\n\tMon, 22 Jan 2024 12:41:04 +0100 (CET)","from out2-smtp.messagingengine.com (out2-smtp.messagingengine.com\n\t[66.111.4.26])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 08929628B7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Jan 2024 12:41:03 +0100 (CET)","from compute5.internal (compute5.nyi.internal [10.202.2.45])\n\tby mailout.nyi.internal (Postfix) with ESMTP id 361B45C0070;\n\tMon, 22 Jan 2024 06:41:02 -0500 (EST)","from mailfrontend1 ([10.202.2.162])\n\tby compute5.internal (MEProxy); Mon, 22 Jan 2024 06:41:02 -0500","by mail.messagingengine.com (Postfix) with ESMTPA; Mon,\n\t22 Jan 2024 06:41:01 -0500 (EST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=bzzt.net header.i=@bzzt.net header.b=\"KDEgHwKn\";\n\tdkim=pass (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com header.b=\"l0MZceEe\"; \n\tdkim-atps=neutral","DKIM-Signature":["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:in-reply-to:message-id:mime-version:references\n\t:reply-to:subject:subject:to:to; s=fm1; t=1705923662; x=\n\t1706010062; bh=yR9oxHScZvpdNBrMhwgw4I8WA+zw/fQHnLmbgdjfuTE=; b=K\n\tDEgHwKn0zD23DUB0eERosgYUlJk2IWWhfjMe7XgcrmaxZVkbt8a8O936XgOhtroi\n\ttz7GnySUBMMeeKnl4po06ScGhj+TPyZzQLtd9jN4VSzs5Y1vlbuXrbWF8nxHhEs7\n\tAiB3tOUSxixVcsFpQyvn5q3L2bUHHSnXENY9kT4bsj1iZODkY88ZDQLrZFYvYtG4\n\t+Fjuj7mNFoO/4AOPwp0lIZ/tPeKsIVlZHqvk+qJFFR07c9s8jtUl/UfgN+sMYFv3\n\tk8teNTWrGvBjCaBWbP5bqk20XIn5hyFufk0jwj4Kg+ncrx1U35ImbkLDmTXNT+BP\n\tbmQ5wdGEhPen1obSGuHjw==","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:in-reply-to:message-id:mime-version:references\n\t:reply-to:subject:subject:to:to:x-me-proxy:x-me-proxy\n\t:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t=1705923662; x=\n\t1706010062; bh=yR9oxHScZvpdNBrMhwgw4I8WA+zw/fQHnLmbgdjfuTE=; b=l\n\t0MZceEegmkRbGl8xcIk7CC8Zd31PfnC7d3ZlbwmPLsY/k+vR9LTa8L7ybrqLjtLy\n\tBcOPiCDHDwwgOjd9TqxYsfL6f4QW5+UHF0DMwmAF2pBzTTkVm1Nz8Ay+1YeTwmVT\n\tZCE9Dpx9L6U/Yfp11DykpA6OksYc+dhFQC4bTg3oQrnZWRy5lfkcDXXfYMqcyIOM\n\tF47F2RdCdg/Ryz9DFOkXO5SKZnYFrl5ja9yUtAb9D0paefGqyF6b2xr69WFTEmUK\n\tihZsKwIk8OTTwZBq91ULRQ8eYMsIJcekxxmI1XzQX7a5TYeq8DZqs7LRQadEyDyV\n\tim6sILMum5saKm7g6IvMA=="],"X-ME-Sender":"<xms:TlSuZaxEd27GlFQLiUB4ItJC8DtlzNvtCKbjACRK7oeoWNibYzbKvA>\n\t<xme:TlSuZWThAO_YNCBa5ZOuwnYfLKKzU8IlVlK3aKqLvWmiNvcNwvgxVIBfQXcT6zcqy\n\tmuIFpDPVi714M2r5QQ>","X-ME-Received":"<xmr:TlSuZcUncAVLvEdVmSE52roPhmJGVclFNIS4VQ71BmvBgMHVPmfUJY47RICdlBHWCa9Twx6fnGWs71vpZRZBG_NZ-AcZr7o>","X-ME-Proxy-Cause":"gggruggvucftvghtrhhoucdtuddrgedvkedrvdekiedgfedtucetufdoteggodetrfdotf\n\tfvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen\n\tuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne\n\tcujfgurhephffvvefufffkofgjfhgggfestdekredtredttdenucfhrhhomheplhhisggt\n\trghmvghrrgessgiiiihtrdhnvghtnecuggftrfgrthhtvghrnhepheekkeegfedtjeehvd\n\tfggeevgedvudduudejveevfeefhedutdetgeevheehkeelnecuffhomhgrihhnpehrvghp\n\trhhoughutghisghlvgdqsghuihhlughsrdhorhhgnecuvehluhhsthgvrhfuihiivgeptd\n\tenucfrrghrrghmpehmrghilhhfrhhomheplhhisggtrghmvghrrgessgiiiihtrdhnvght","X-ME-Proxy":"<xmx:TlSuZQhZ-9iMSh9eg2ePJpaD16cPZZWa5Vgov3AjBlZgnOZfe_d1hA>\n\t<xmx:TlSuZcDcpXQoBMvVXpvv-36zEu4eBCK97rD-C6pO7iRPoc-QD2gHmQ>\n\t<xmx:TlSuZRIa2jB52uFsJRkRcOjPAXoICk3tCSciTmLjdRVeGk2nsxHAYw>\n\t<xmx:TlSuZXqY7p7JsaxgHrl9HPWO6vpc2FAKnUG5O0flwbOaQ_Vr_t3Plg>","Feedback-ID":"i7559471f:Fastmail","From":"libcamera@bzzt.net","To":"libcamera-devel@lists.libcamera.org","Subject":"[PATCH 1/1] libcamera: ipa: allow trusting modules by checksum","Date":"Mon, 22 Jan 2024 12:40:40 +0100","Message-ID":"<20240122114040.275771-2-libcamera@bzzt.net>","X-Mailer":"git-send-email 2.43.0","In-Reply-To":"<20240122114040.275771-1-libcamera@bzzt.net>","References":"<20240122114040.275771-1-libcamera@bzzt.net>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","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>","Cc":"Arnout Engelen <arnout@bzzt.net>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"From: Arnout Engelen <arnout@bzzt.net>\n\nCurrently, libcamera signs the in-tree IPA modules during the build, and\nembeds the public key so that only trusted IPA modules will be run\nin-process. Out-of-tree modules will be run using runtime isolation.\n\nThis commit adds a second mechanism to achieve the same: during installation\nthe checksums of the in-tree IPA modules are recorded, and at run time the\nmodules are considered trusted when they match the recorded trusted\nchecksums.\n\nThe motivation behind adding this mechanism is that this allows rebuilding\nthe library and getting a bit-by-bit identical result, without having to\nshare the keys with which to sign the trusted modules. This is known as\n'Reproducible Builds', and you can read more about its advantages on\nhttps://reproducible-builds.org/. With this feature, packagers that care\nabout reproducible builds can disable the module signing, and enjoy\nequivalent security and performance while also allowing independent\nrebuilds.\n\nSigned-off-by: Arnout Engelen <arnout@bzzt.net>\n---\n include/libcamera/internal/ipa_manager.h |  9 ++-\n include/libcamera/internal/ipa_module.h  |  2 +\n meson_options.txt                        |  8 +++\n src/apps/ipa-verify/main.cpp             | 43 +++++++++++-\n src/apps/ipa-verify/meson.build          |  2 +-\n src/ipa/ipa-checksum-install.sh          | 24 +++++++\n src/ipa/meson.build                      |  7 ++\n src/libcamera/ipa_manager.cpp            | 88 +++++++++++++++++++++---\n src/libcamera/ipa_module.cpp             | 30 ++++++++\n src/meson.build                          | 18 ++++-\n 10 files changed, 215 insertions(+), 16 deletions(-)\n create mode 100644 src/ipa/ipa-checksum-install.sh","diff":"diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\nindex bf823563..2475e26a 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,7 @@ public:\n \t\treturn pubKey_;\n \t}\n #endif\n+\tstatic void loadTrustedChecksums(std::vector<std::vector<uint8_t>> &checksums);\n \n private:\n \tstatic IPAManager *self_;\n@@ -65,6 +66,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 +75,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..dd753ca8 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -30,6 +30,14 @@ 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('ipa_checksum_trusted_modules',\n+        type : 'feature',\n+        description : 'enable IPA trusted module checksums')\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..facfd481 100644\n--- a/src/apps/ipa-verify/main.cpp\n+++ b/src/apps/ipa-verify/main.cpp\n@@ -18,7 +18,8 @@ using namespace libcamera;\n \n namespace {\n \n-bool isSignatureValid(IPAModule *ipa)\n+#if HAVE_IPA_PUBKEY\n+bool isSignatureValid([[maybe_unused]] IPAModule *ipa)\n {\n \tFile file{ ipa->path() };\n \tif (!file.open(File::OpenModeFlag::ReadOnly))\n@@ -30,6 +31,22 @@ bool isSignatureValid(IPAModule *ipa)\n \n \treturn IPAManager::pubKey().verify(data, ipa->signature());\n }\n+#endif\n+\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+bool isChecksumTrusted([[maybe_unused]] IPAModule *ipa)\n+{\n+\tstd::vector<std::vector<uint8_t>> trusted;\n+\tIPAManager::loadTrustedChecksums(trusted);\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+}\n+#endif\n \n void usage(char *argv0)\n {\n@@ -54,11 +71,31 @@ int main(int argc, char **argv)\n \t\treturn EXIT_FAILURE;\n \t}\n \n-\tif (!isSignatureValid(&module)) {\n+\tbool ok = false;\n+#if HAVE_IPA_PUBKEY\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+#else\n+\tstd::cout << \"IPA module signing is disabled\" << std::endl;\n+#endif\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\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+#else\n+\tstd::cout << \"IPA module checksums are disabled\" << std::endl;\n+#endif\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..41a1cb48\n--- /dev/null\n+++ b/src/ipa/ipa-checksum-install.sh\n@@ -0,0 +1,24 @@\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+lib_file=$(find ${MESON_INSTALL_DESTDIR_PREFIX}/lib -type f -regex .*/libcamera.so.*)\n+\n+i=1\n+for module in ${modules} ; do\n+\tmodule=\"${MESON_INSTALL_DESTDIR_PREFIX}/${module}\"\n+\tif [ -f \"${module}\" ] ; then\n+        checksum=$(sha256sum -b \"${module}\" | cut -d \" \" -f 1)\n+        # FIXME support for trusting multiple modules\n+        sed -b -e \"s/embedded_ipa_trusted_module_checksum_sha256_placeholder_0000000${i}/${checksum}/\" -i ${lib_file}\n+        i=$((i+1))\n+\tfi\n+done\ndiff --git a/src/ipa/meson.build b/src/ipa/meson.build\nindex 48793e07..00299a7c 100644\n--- a/src/ipa/meson.build\n+++ b/src/ipa/meson.build\n@@ -75,3 +75,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..96281758 100644\n--- a/src/libcamera/ipa_manager.cpp\n+++ b/src/libcamera/ipa_manager.cpp\n@@ -114,6 +114,10 @@ IPAManager::IPAManager()\n \t\tLOG(IPAManager, Warning) << \"Public key not valid\";\n #endif\n \n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\tIPAManager::loadTrustedChecksums(trusted_checksums_);\n+#endif\n+\n \tunsigned int ipaCount = 0;\n \n \t/* User-specified paths take precedence. */\n@@ -165,6 +169,38 @@ IPAManager::~IPAManager()\n \tself_ = nullptr;\n }\n \n+/**\n+ * \\brief Load trusted checksums\n+ * \\param[out] checksums A vector of checksums as 32-element byte vectors\n+ */\n+void IPAManager::loadTrustedChecksums([[maybe_unused]] std::vector<std::vector<uint8_t>> &checksums)\n+{\n+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS\n+\t// Since there are legitimate reasons for post-processing the modules\n+\t// in the install phase, we use static placeholders here and allow\n+\t// replacing them in the binary in the install phase.\n+\tstd::string embeddedChecksums =\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000001\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000002\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000003\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000004\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000005\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000006\\n\"\n+\t\t\"embedded_ipa_trusted_module_checksum_sha256_placeholder_00000007\\n\";\n+\n+\tchar* data = embeddedChecksums.data();\n+\tconst int size = strlen(data);\n+\tfor (int i = 0; i < size; i += 65) {\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}\n+#endif\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 +331,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 +351,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..2096ff79 100644\n--- a/src/meson.build\n+++ b/src/meson.build\n@@ -14,19 +14,31 @@ summary({\n          'LIBCAMERA_SYSCONF_DIR' : config_h.get('LIBCAMERA_SYSCONF_DIR'),\n          }, section : 'Paths')\n \n+# Trusted module checksumming\n+if get_option('ipa_checksum_trusted_modules').enabled() or get_option('ipa_checksum_trusted_modules').auto()\n+    ipa_checksum_trusted_modules = true\n+    config_h.set('HAVE_IPA_TRUSTED_MODULE_CHECKSUMS', 1)\n+else\n+    ipa_checksum_trusted_modules = false\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() or get_option('ipa_checksum_trusted_modules').auto()) 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":["1/1"]}