[PATCH v2 2/5] livepatch: Embed public key in Xen

Ross Lagerwall posted 5 patches 7 months ago
There is a newer version of this series
[PATCH v2 2/5] livepatch: Embed public key in Xen
Posted by Ross Lagerwall 7 months ago
From: Kevin Lampis <kevin.lampis@cloud.com>

Make it possible to embed a public key in Xen to be used when verifying
live patch payloads. Inclusion of the public key is optional.

To avoid needing to include a DER / X.509 parser in the hypervisor, the
public key is unpacked at build time and included in a form that is
convenient for the hypervisor to consume. This is different approach
from that used by Linux which embeds the entire X.509 certificate and
builds in a parser for it.

A suitable key can be created using openssl:

openssl req -x509 -newkey rsa:2048 -keyout priv.pem -out pub.pem \
    -sha256 -days 3650 -nodes \
    -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
openssl x509 -inform PEM -in pub.pem -outform PEM -pubkey -nocert -out verify_key.pem

Signed-off-by: Kevin Lampis <kevin.lampis@cloud.com>
Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---

In v2:

* Split the loading part into a separate patch.
* Expect public key in object tree rather than source tree.
* Rename Kconfig symbols and others to reflect that it is used for
  verification in Xen rather than signing.
* Move extern declaration to a header.
* Move raw key data to init.rodata.

 xen/common/Kconfig          | 18 ++++++++++++++++++
 xen/crypto/Makefile         | 13 +++++++++++++
 xen/include/xen/livepatch.h |  6 ++++++
 xen/tools/extract-key.py    | 37 +++++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+)
 create mode 100755 xen/tools/extract-key.py

diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 6d43be2e6e8a..e4466db595c2 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -461,6 +461,24 @@ config LIVEPATCH
 
 	  If unsure, say Y.
 
+config PAYLOAD_VERIFY
+	bool "Verify signed LivePatch payloads"
+	depends on LIVEPATCH
+	select CRYPTO
+	help
+	  Verify signed LivePatch payloads using an RSA public key built
+	  into the Xen hypervisor. Selecting this option requires a
+	  public key in PEM format to be available for embedding during
+	  the build.
+
+config PAYLOAD_VERIFY_KEY
+	string "File name of public key used to verify payloads"
+	default "verify_key.pem"
+	depends on PAYLOAD_VERIFY
+	help
+	  The file name of an RSA public key in PEM format to be used for
+	  verifying signed LivePatch payloads.
+
 config FAST_SYMBOL_LOOKUP
 	bool "Fast symbol lookup (bigger binary)"
 	default y
diff --git a/xen/crypto/Makefile b/xen/crypto/Makefile
index db29655333a3..64ed90ba55b1 100644
--- a/xen/crypto/Makefile
+++ b/xen/crypto/Makefile
@@ -1,2 +1,15 @@
 obj-y += rijndael.o
 obj-y += vmac.o
+
+obj-$(CONFIG_PAYLOAD_VERIFY) += builtin_payload_key.o
+
+ifeq ($(CONFIG_PAYLOAD_VERIFY),y)
+key_path := $(objtree)/$(patsubst "%",%,$(CONFIG_PAYLOAD_VERIFY_KEY))
+$(obj)/builtin_payload_key.bin: $(key_path) $(srctree)/tools/extract-key.py
+	$(srctree)/tools/extract-key.py < $< > $@.new
+	$(call move-if-changed,$@.new,$@)
+
+$(obj)/builtin_payload_key.S: BINFILE_FLAGS := -i
+$(obj)/builtin_payload_key.S: $(srctree)/tools/binfile $(obj)/builtin_payload_key.bin FORCE
+	$(call if_changed,binfile,$(obj)/builtin_payload_key.bin xen_livepatch_key_data)
+endif
diff --git a/xen/include/xen/livepatch.h b/xen/include/xen/livepatch.h
index d074a5bebecc..0265f1fce057 100644
--- a/xen/include/xen/livepatch.h
+++ b/xen/include/xen/livepatch.h
@@ -18,6 +18,7 @@ struct xen_sysctl_livepatch_op;
 
 #ifdef CONFIG_LIVEPATCH
 
+#include <xen/init.h>
 #include <xen/lib.h>
 
 /*
@@ -143,6 +144,11 @@ struct payload;
 int revert_payload(struct payload *data);
 void revert_payload_tail(struct payload *data);
 
+#ifdef CONFIG_PAYLOAD_VERIFY
+/* The public key data contained with Xen used to verify payload signatures. */
+extern const uint8_t __initconst xen_livepatch_key_data[];
+#endif
+
 #else
 
 /*
diff --git a/xen/tools/extract-key.py b/xen/tools/extract-key.py
new file mode 100755
index 000000000000..2980264b757d
--- /dev/null
+++ b/xen/tools/extract-key.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+
+# SPDX-License-Identifier: GPL-2.0
+
+import binascii
+import struct
+import sys
+import subprocess
+import re
+
+# Decode a certificate into a format suitable for embedding in Xen.
+
+out = subprocess.check_output(['openssl', 'rsa', '-pubin', '-inform', 'PEM',
+                               '-noout', '-text'], stdin=sys.stdin,
+                               universal_newlines=True)
+combined = ''
+for line in out.split('\n'):
+    line = line.rstrip()
+    if line.startswith('    '):
+        combined += line.strip().replace(':', '')
+    match = re.match(r'Exponent: .* \(0x(.*)\)', line)
+    if match:
+        e = match.group(1)
+
+n = combined.lstrip('0')
+if len(n) % 2 == 1:
+    n = '0' + n
+n = binascii.unhexlify(n)
+e = e.lstrip('0')
+if len(e) % 2 == 1:
+    e = '0' + e
+e = binascii.unhexlify(e)
+
+sys.stdout.buffer.write(struct.pack('I', len(n)))
+sys.stdout.buffer.write(n)
+sys.stdout.buffer.write(struct.pack('I', len(e)))
+sys.stdout.buffer.write(e)
-- 
2.49.0
Re: [PATCH v2 2/5] livepatch: Embed public key in Xen
Posted by Jan Beulich 7 months ago
On 15.05.2025 11:38, Ross Lagerwall wrote:
> --- a/xen/crypto/Makefile
> +++ b/xen/crypto/Makefile
> @@ -1,2 +1,15 @@
>  obj-y += rijndael.o
>  obj-y += vmac.o
> +
> +obj-$(CONFIG_PAYLOAD_VERIFY) += builtin_payload_key.o

For new files please prefer dashes over underscores in their names.

> +ifeq ($(CONFIG_PAYLOAD_VERIFY),y)

This isn't needed, is it?

> +key_path := $(objtree)/$(patsubst "%",%,$(CONFIG_PAYLOAD_VERIFY_KEY))

Since they can be used there, dashes imo also want preferring for new
make variables (unless they need exporting to the shell).

> @@ -143,6 +144,11 @@ struct payload;
>  int revert_payload(struct payload *data);
>  void revert_payload_tail(struct payload *data);
>  
> +#ifdef CONFIG_PAYLOAD_VERIFY
> +/* The public key data contained with Xen used to verify payload signatures. */
> +extern const uint8_t __initconst xen_livepatch_key_data[];

Nit: Section placement annotations are generally meaningless on declarations,
and hence want omitting from there.

Jan