[libcamera-devel,v2,2/2] apps: Add ipa-verify application
diff mbox series

Message ID 20230704234422.11863-3-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Add an application to verify IPA module signatures
Related show

Commit Message

Laurent Pinchart July 4, 2023, 11:44 p.m. UTC
When packaging libcamera, distributions may break IPA module signatures
if the packaging process strips binaries. This can be fixed by resigning
the modules, but the process is error-prone.

Add a command line ipa-verify utility that tests the signature on an IPA
module to help packagers. The tool takes a single argument, the path to
an IPA module shared object, and expects the signature file (.sign) to
be in the same directory.

In order to access the public key needed for signature verification, add
a static function to the IPAManager class. As the class is internal to
libcamera, this doesn't affect the public API.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
---
 include/libcamera/internal/ipa_manager.h |  7 +++
 src/apps/ipa-verify/main.cpp             | 64 ++++++++++++++++++++++++
 src/apps/ipa-verify/meson.build          | 15 ++++++
 src/apps/meson.build                     |  2 +
 src/libcamera/ipa_manager.cpp            | 13 +++++
 5 files changed, 101 insertions(+)
 create mode 100644 src/apps/ipa-verify/main.cpp
 create mode 100644 src/apps/ipa-verify/meson.build

Comments

Kieran Bingham July 4, 2023, 11:54 p.m. UTC | #1
Hi Laurent,

Quoting Laurent Pinchart via libcamera-devel (2023-07-05 00:44:22)
> When packaging libcamera, distributions may break IPA module signatures
> if the packaging process strips binaries. This can be fixed by resigning
> the modules, but the process is error-prone.
> 
> Add a command line ipa-verify utility that tests the signature on an IPA
> module to help packagers. The tool takes a single argument, the path to
> an IPA module shared object, and expects the signature file (.sign) to
> be in the same directory.
> 
> In order to access the public key needed for signature verification, add
> a static function to the IPAManager class. As the class is internal to
> libcamera, this doesn't affect the public API.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>

I still think this is useful!

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
>  include/libcamera/internal/ipa_manager.h |  7 +++
>  src/apps/ipa-verify/main.cpp             | 64 ++++++++++++++++++++++++
>  src/apps/ipa-verify/meson.build          | 15 ++++++
>  src/apps/meson.build                     |  2 +
>  src/libcamera/ipa_manager.cpp            | 13 +++++
>  5 files changed, 101 insertions(+)
>  create mode 100644 src/apps/ipa-verify/main.cpp
>  create mode 100644 src/apps/ipa-verify/meson.build
> 
> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h
> index 7f36e58e8bfa..bf823563c91c 100644
> --- a/include/libcamera/internal/ipa_manager.h
> +++ b/include/libcamera/internal/ipa_manager.h
> @@ -47,6 +47,13 @@ public:
>                 return proxy;
>         }
>  
> +#if HAVE_IPA_PUBKEY
> +       static const PubKey &pubKey()
> +       {
> +               return pubKey_;
> +       }
> +#endif
> +
>  private:
>         static IPAManager *self_;
>  
> diff --git a/src/apps/ipa-verify/main.cpp b/src/apps/ipa-verify/main.cpp
> new file mode 100644
> index 000000000000..76ba5073d25a
> --- /dev/null
> +++ b/src/apps/ipa-verify/main.cpp
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2023, Ideas on Board Oy
> + *
> + * ipa_verify.cpp - Verify signature on an IPA module
> + */
> +
> +#include <iostream>
> +#include <libgen.h>
> +
> +#include <libcamera/base/file.h>
> +#include <libcamera/base/span.h>
> +
> +#include "libcamera/internal/ipa_manager.h"
> +#include "libcamera/internal/ipa_module.h"
> +
> +using namespace libcamera;
> +
> +namespace {
> +
> +bool isSignatureValid(IPAModule *ipa)
> +{
> +       File file{ ipa->path() };
> +       if (!file.open(File::OpenModeFlag::ReadOnly))
> +               return false;
> +
> +       Span<uint8_t> data = file.map();
> +       if (data.empty())
> +               return false;
> +
> +       return IPAManager::pubKey().verify(data, ipa->signature());
> +}
> +
> +void usage(char *argv0)
> +{
> +       std::cout << "Usage: " << basename(argv0) << " ipa_name.so" << std::endl;
> +       std::cout << std::endl;
> +       std::cout << "Verify the signature of an IPA module. The signature file ipa_name.so.sign is" << std::endl;
> +       std::cout << "expected to be in the same directory as the IPA module." << std::endl;
> +}
> +
> +} /* namespace */
> +
> +int main(int argc, char **argv)
> +{
> +       if (argc != 2) {
> +               usage(argv[0]);
> +               return EXIT_FAILURE;
> +       }
> +
> +       IPAModule module{ argv[1] };
> +       if (!module.isValid()) {
> +               std::cout << "Invalid IPA module " << argv[1] << std::endl;
> +               return EXIT_FAILURE;
> +       }
> +
> +       if (!isSignatureValid(&module)) {
> +               std::cout << "IPA module signature is invalid" << std::endl;
> +               return EXIT_FAILURE;
> +       }
> +
> +       std::cout << "IPA module signature is valid" << std::endl;
> +       return 0;
> +}
> diff --git a/src/apps/ipa-verify/meson.build b/src/apps/ipa-verify/meson.build
> new file mode 100644
> index 000000000000..7fdda3b9af4b
> --- /dev/null
> +++ b/src/apps/ipa-verify/meson.build
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: CC0-1.0
> +
> +if not ipa_sign_module
> +    subdir_done()
> +endif
> +
> +ipa_verify_sources = files([
> +    'main.cpp',
> +])
> +
> +ipa_verify  = executable('ipa_verify', ipa_verify_sources,
> +                         dependencies : [
> +                             libcamera_private,
> +                         ],
> +                         install : false)
> diff --git a/src/apps/meson.build b/src/apps/meson.build
> index 099876356bd1..af632b9a7b0b 100644
> --- a/src/apps/meson.build
> +++ b/src/apps/meson.build
> @@ -18,3 +18,5 @@ subdir('lc-compliance')
>  
>  subdir('cam')
>  subdir('qcam')
> +
> +subdir('ipa-verify')
> diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp
> index ac5397003b50..7a4515d90d7b 100644
> --- a/src/libcamera/ipa_manager.cpp
> +++ b/src/libcamera/ipa_manager.cpp
> @@ -279,6 +279,19 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,
>   * found or if the IPA proxy fails to initialize
>   */
>  
> +#if HAVE_IPA_PUBKEY
> +/**
> + * \fn IPAManager::pubKey()
> + * \brief Retrieve the IPA module signing public key
> + *
> + * IPA module signature verification is normally handled internally by the
> + * IPAManager class. This function is meant to be used by utilities that need to
> + * verify signatures externally.
> + *
> + * \return The IPA module signing public key
> + */
> +#endif
> +
>  bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const
>  {
>  #if HAVE_IPA_PUBKEY
> -- 
> Regards,
> 
> Laurent Pinchart
>
Javier Martinez Canillas July 5, 2023, 12:05 a.m. UTC | #2
Laurent Pinchart via libcamera-devel <libcamera-devel@lists.libcamera.org>
writes:

> When packaging libcamera, distributions may break IPA module signatures
> if the packaging process strips binaries. This can be fixed by resigning
> the modules, but the process is error-prone.
>
> Add a command line ipa-verify utility that tests the signature on an IPA
> module to help packagers. The tool takes a single argument, the path to
> an IPA module shared object, and expects the signature file (.sign) to
> be in the same directory.
>
> In order to access the public key needed for signature verification, add
> a static function to the IPAManager class. As the class is internal to
> libcamera, this doesn't affect the public API.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> ---

I used this tool to verify that the libcamera fedora packages IPA modules
were properly signed. I can confirm that it is very useful for packagers.

Tested-by: Javier Martinez Canillas <javierm@redhat.com>

Patch
diff mbox series

diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h
index 7f36e58e8bfa..bf823563c91c 100644
--- a/include/libcamera/internal/ipa_manager.h
+++ b/include/libcamera/internal/ipa_manager.h
@@ -47,6 +47,13 @@  public:
 		return proxy;
 	}
 
+#if HAVE_IPA_PUBKEY
+	static const PubKey &pubKey()
+	{
+		return pubKey_;
+	}
+#endif
+
 private:
 	static IPAManager *self_;
 
diff --git a/src/apps/ipa-verify/main.cpp b/src/apps/ipa-verify/main.cpp
new file mode 100644
index 000000000000..76ba5073d25a
--- /dev/null
+++ b/src/apps/ipa-verify/main.cpp
@@ -0,0 +1,64 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023, Ideas on Board Oy
+ *
+ * ipa_verify.cpp - Verify signature on an IPA module
+ */
+
+#include <iostream>
+#include <libgen.h>
+
+#include <libcamera/base/file.h>
+#include <libcamera/base/span.h>
+
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+
+using namespace libcamera;
+
+namespace {
+
+bool isSignatureValid(IPAModule *ipa)
+{
+	File file{ ipa->path() };
+	if (!file.open(File::OpenModeFlag::ReadOnly))
+		return false;
+
+	Span<uint8_t> data = file.map();
+	if (data.empty())
+		return false;
+
+	return IPAManager::pubKey().verify(data, ipa->signature());
+}
+
+void usage(char *argv0)
+{
+	std::cout << "Usage: " << basename(argv0) << " ipa_name.so" << std::endl;
+	std::cout << std::endl;
+	std::cout << "Verify the signature of an IPA module. The signature file ipa_name.so.sign is" << std::endl;
+	std::cout << "expected to be in the same directory as the IPA module." << std::endl;
+}
+
+} /* namespace */
+
+int main(int argc, char **argv)
+{
+	if (argc != 2) {
+		usage(argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	IPAModule module{ argv[1] };
+	if (!module.isValid()) {
+		std::cout << "Invalid IPA module " << argv[1] << std::endl;
+		return EXIT_FAILURE;
+	}
+
+	if (!isSignatureValid(&module)) {
+		std::cout << "IPA module signature is invalid" << std::endl;
+		return EXIT_FAILURE;
+	}
+
+	std::cout << "IPA module signature is valid" << std::endl;
+	return 0;
+}
diff --git a/src/apps/ipa-verify/meson.build b/src/apps/ipa-verify/meson.build
new file mode 100644
index 000000000000..7fdda3b9af4b
--- /dev/null
+++ b/src/apps/ipa-verify/meson.build
@@ -0,0 +1,15 @@ 
+# SPDX-License-Identifier: CC0-1.0
+
+if not ipa_sign_module
+    subdir_done()
+endif
+
+ipa_verify_sources = files([
+    'main.cpp',
+])
+
+ipa_verify  = executable('ipa_verify', ipa_verify_sources,
+                         dependencies : [
+                             libcamera_private,
+                         ],
+                         install : false)
diff --git a/src/apps/meson.build b/src/apps/meson.build
index 099876356bd1..af632b9a7b0b 100644
--- a/src/apps/meson.build
+++ b/src/apps/meson.build
@@ -18,3 +18,5 @@  subdir('lc-compliance')
 
 subdir('cam')
 subdir('qcam')
+
+subdir('ipa-verify')
diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp
index ac5397003b50..7a4515d90d7b 100644
--- a/src/libcamera/ipa_manager.cpp
+++ b/src/libcamera/ipa_manager.cpp
@@ -279,6 +279,19 @@  IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,
  * found or if the IPA proxy fails to initialize
  */
 
+#if HAVE_IPA_PUBKEY
+/**
+ * \fn IPAManager::pubKey()
+ * \brief Retrieve the IPA module signing public key
+ *
+ * IPA module signature verification is normally handled internally by the
+ * IPAManager class. This function is meant to be used by utilities that need to
+ * verify signatures externally.
+ *
+ * \return The IPA module signing public key
+ */
+#endif
+
 bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const
 {
 #if HAVE_IPA_PUBKEY