@@ -38,7 +38,7 @@ public:
if (!m)
return nullptr;
- std::unique_ptr<T> proxy = std::make_unique<T>(m, !self_->isSignatureValid(m));
+ std::unique_ptr<T> proxy = std::make_unique<T>(m, !self_->isTrusted(m));
if (!proxy->isValid()) {
LOG(IPAManager, Error) << "Failed to load proxy";
return nullptr;
@@ -53,6 +53,7 @@ public:
return pubKey_;
}
#endif
+ static void loadTrustedChecksums(std::vector<std::vector<uint8_t>> &checksums);
private:
static IPAManager *self_;
@@ -65,6 +66,8 @@ private:
uint32_t maxVersion);
bool isSignatureValid(IPAModule *ipa) const;
+ bool isTrustedChecksum(IPAModule *ipa) const;
+ bool isTrusted(IPAModule *ipa) const;
std::vector<IPAModule *> modules_;
@@ -72,6 +75,10 @@ private:
static const uint8_t publicKeyData_[];
static const PubKey pubKey_;
#endif
+
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+ std::vector<std::vector<uint8_t>> trusted_checksums_;
+#endif
};
} /* namespace libcamera */
@@ -30,6 +30,7 @@ public:
const struct IPAModuleInfo &info() const;
const std::vector<uint8_t> signature() const;
+ const std::vector<uint8_t> checksum() const;
const std::string &path() const;
bool load();
@@ -47,6 +48,7 @@ private:
struct IPAModuleInfo info_;
std::vector<uint8_t> signature_;
+ std::vector<uint8_t> checksum_;
std::string libPath_;
bool valid_;
@@ -30,6 +30,14 @@ option('ipas',
choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
description : 'Select which IPA modules to build')
+option('ipa_sign_modules',
+ type : 'feature',
+ description : 'enable IPA trusted module signing')
+
+option('ipa_checksum_trusted_modules',
+ type : 'feature',
+ description : 'enable IPA trusted module checksums')
+
option('lc-compliance',
type : 'feature',
value : 'auto',
@@ -18,7 +18,8 @@ using namespace libcamera;
namespace {
-bool isSignatureValid(IPAModule *ipa)
+#if HAVE_IPA_PUBKEY
+bool isSignatureValid([[maybe_unused]] IPAModule *ipa)
{
File file{ ipa->path() };
if (!file.open(File::OpenModeFlag::ReadOnly))
@@ -30,6 +31,22 @@ bool isSignatureValid(IPAModule *ipa)
return IPAManager::pubKey().verify(data, ipa->signature());
}
+#endif
+
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+bool isChecksumTrusted([[maybe_unused]] IPAModule *ipa)
+{
+ std::vector<std::vector<uint8_t>> trusted;
+ IPAManager::loadTrustedChecksums(trusted);
+ for (std::vector<uint8_t> t : trusted) {
+ if (std::equal(t.begin(), t.begin() + 32,
+ ipa->checksum().begin())) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
void usage(char *argv0)
{
@@ -54,11 +71,31 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- if (!isSignatureValid(&module)) {
+ bool ok = false;
+#if HAVE_IPA_PUBKEY
+ if (isSignatureValid(&module)) {
+ std::cout << "IPA module signature is valid" << std::endl;
+ ok = true;
+ } else {
std::cout << "IPA module signature is invalid" << std::endl;
+ }
+#else
+ std::cout << "IPA module signing is disabled" << std::endl;
+#endif
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+ if (isChecksumTrusted(&module)) {
+ std::cout << "IPA module checksum is trusted" << std::endl;
+ ok = true;
+ } else {
+ std::cout << "IPA module checksum is not trusted" << std::endl;
+ }
+#else
+ std::cout << "IPA module checksums are disabled" << std::endl;
+#endif
+
+ if (!ok) {
return EXIT_FAILURE;
}
- std::cout << "IPA module signature is valid" << std::endl;
return 0;
}
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: CC0-1.0
-if not ipa_sign_module
+if not ipa_sign_module and not ipa_checksum_trusted_modules
subdir_done()
endif
new file mode 100644
@@ -0,0 +1,24 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2020, Google Inc.
+#
+# Author: Arnout Engelen <arnout@bzzt.net>
+#
+# ipa-checksum-install.sh - Generate IPA module checksums when installing
+
+modules=$*
+
+echo "Generating trusted IPA module checksums"
+
+lib_file=$(find ${MESON_INSTALL_DESTDIR_PREFIX}/lib -type f -regex .*/libcamera.so.*)
+
+i=1
+for module in ${modules} ; do
+ module="${MESON_INSTALL_DESTDIR_PREFIX}/${module}"
+ if [ -f "${module}" ] ; then
+ checksum=$(sha256sum -b "${module}" | cut -d " " -f 1)
+ # FIXME support for trusting multiple modules
+ sed -b -e "s/embedded_ipa_trusted_module_checksum_sha256_placeholder_0000000${i}/${checksum}/" -i ${lib_file}
+ i=$((i+1))
+ fi
+done
@@ -75,3 +75,10 @@ if ipa_sign_module
enabled_ipa_modules,
install_tag : 'runtime')
endif
+
+if ipa_checksum_trusted_modules
+ # Similarly, calculate checksums for the installed artifacts
+ meson.add_install_script('ipa-checksum-install.sh',
+ enabled_ipa_modules,
+ install_tag : 'runtime')
+endif
@@ -114,6 +114,10 @@ IPAManager::IPAManager()
LOG(IPAManager, Warning) << "Public key not valid";
#endif
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+ IPAManager::loadTrustedChecksums(trusted_checksums_);
+#endif
+
unsigned int ipaCount = 0;
/* User-specified paths take precedence. */
@@ -165,6 +169,38 @@ IPAManager::~IPAManager()
self_ = nullptr;
}
+/**
+ * \brief Load trusted checksums
+ * \param[out] checksums A vector of checksums as 32-element byte vectors
+ */
+void IPAManager::loadTrustedChecksums([[maybe_unused]] std::vector<std::vector<uint8_t>> &checksums)
+{
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+ // Since there are legitimate reasons for post-processing the modules
+ // in the install phase, we use static placeholders here and allow
+ // replacing them in the binary in the install phase.
+ std::string embeddedChecksums =
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000001\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000002\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000003\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000004\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000005\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000006\n"
+ "embedded_ipa_trusted_module_checksum_sha256_placeholder_00000007\n";
+
+ char* data = embeddedChecksums.data();
+ const int size = strlen(data);
+ for (int i = 0; i < size; i += 65) {
+ std::vector<uint8_t> *checksum = new std::vector<uint8_t>();
+ for (int c = 0; c < 64; c += 2) {
+ char chr[3] = { static_cast<char>(data[i+c]), static_cast<char>(data[i+c+1]), '\0' };
+ checksum->push_back(strtol(chr, nullptr, 16));
+ }
+ checksums.push_back(*checksum);
+ }
+#endif
+}
+
/**
* \brief Identify shared library objects within a directory
* \param[in] libDir The directory to search for shared objects
@@ -295,14 +331,6 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,
bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const
{
#if HAVE_IPA_PUBKEY
- char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION");
- if (force && force[0] != '\0') {
- LOG(IPAManager, Debug)
- << "Isolation of IPA module " << ipa->path()
- << " forced through environment variable";
- return false;
- }
-
File file{ ipa->path() };
if (!file.open(File::OpenModeFlag::ReadOnly))
return false;
@@ -323,4 +351,48 @@ bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const
#endif
}
+bool IPAManager::isTrustedChecksum([[maybe_unused]] IPAModule *ipa) const
+{
+#if HAVE_IPA_TRUSTED_MODULE_CHECKSUMS
+ File file{ ipa->path() };
+ if (!file.open(File::OpenModeFlag::ReadOnly))
+ return false;
+
+ Span<uint8_t> data = file.map();
+ if (data.empty())
+ return false;
+
+ bool valid = false;
+
+ for (std::vector<uint8_t> t : trusted_checksums_) {
+ if (std::equal(t.begin(), t.begin() + 32,
+ ipa->checksum().begin())) {
+ valid = true;
+ continue;
+ }
+ }
+
+ LOG(IPAManager, Debug)
+ << "IPA module " << ipa->path() << " checksum is "
+ << (valid ? "trusted" : "not trusted");
+
+ return valid;
+#else
+ return false;
+#endif
+}
+
+bool IPAManager::isTrusted(IPAModule *ipa) const
+{
+ char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION");
+ if (force && force[0] != '\0') {
+ LOG(IPAManager, Debug)
+ << "Isolation of IPA module " << ipa->path()
+ << " forced through environment variable";
+ return false;
+ }
+ return isTrustedChecksum(ipa) || isSignatureValid(ipa);
+}
+
+
} /* namespace libcamera */
@@ -27,6 +27,12 @@
#include "libcamera/internal/pipeline_handler.h"
+#if HAVE_CRYPTO
+#include <openssl/sha.h>
+#elif HAVE_GNUTLS
+#include <gnutls/crypto.h>
+#endif
+
/**
* \file ipa_module.h
* \brief Image Processing Algorithm module
@@ -281,6 +287,17 @@ int IPAModule::loadIPAModuleInfo()
}
Span<const uint8_t> data = file.map();
+
+ /* Calculate the module checksum. */
+ uint8_t digest[32] = { 0 };
+#if HAVE_CRYPTO
+ SHA256(data.data(), data.size(), digest);
+#elif HAVE_GNUTLS
+ gnutls_hash_fast(GNUTLS_DIG_SHA256, data.data(), data.size(), digest);
+#endif
+ checksum_ = std::vector<uint8_t>(digest, digest + 32);
+
+ /* Interpret the file. */
int ret = elfVerifyIdent(data);
if (ret) {
LOG(IPAModule, Error) << "IPA module is not an ELF file";
@@ -379,6 +396,19 @@ const std::vector<uint8_t> IPAModule::signature() const
return signature_;
}
+/**
+ * \brief Retrieve the IPA module checksum
+ *
+ * The IPA module checksum is loaded when the IPAModule instance is created.
+ *
+ * \return The IPA module checksum
+ */
+const std::vector<uint8_t> IPAModule::checksum() const
+{
+ return checksum_;
+}
+
+
/**
* \brief Retrieve the IPA module path
*
@@ -14,19 +14,31 @@ summary({
'LIBCAMERA_SYSCONF_DIR' : config_h.get('LIBCAMERA_SYSCONF_DIR'),
}, section : 'Paths')
+# Trusted module checksumming
+if get_option('ipa_checksum_trusted_modules').enabled() or get_option('ipa_checksum_trusted_modules').auto()
+ ipa_checksum_trusted_modules = true
+ config_h.set('HAVE_IPA_TRUSTED_MODULE_CHECKSUMS', 1)
+else
+ ipa_checksum_trusted_modules = false
+endif
+
# Module Signing
-openssl = find_program('openssl', required : false)
-if openssl.found()
+openssl = find_program('openssl', required : get_option('ipa_sign_modules'))
+if (get_option('ipa_sign_modules').enabled() or get_option('ipa_checksum_trusted_modules').auto()) and openssl.found()
ipa_priv_key = custom_target('ipa-priv-key',
output : ['ipa-priv-key.pem'],
command : [gen_ipa_priv_key, '@OUTPUT@'])
config_h.set('HAVE_IPA_PUBKEY', 1)
ipa_sign_module = true
else
- warning('openssl not found, all IPA modules will be isolated')
ipa_sign_module = false
endif
+if not ipa_checksum_trusted_modules and not ipa_sign_module
+ warning('neither checksums nor signatures enabled,')
+ warning('all IPA modules will be isolated')
+endif
+
# libcamera must be built first as a dependency to the other components.
subdir('libcamera')