Repository: rauc/rauc (C)

Issue:
Cannot create a large amount of device keys for encrypted bundles
I am using Yocto ( Kirkstone ) to create an image for an embeded Linux device with Linux Kernel version 5.15.

I have implemented encrypted bundles with rauc using this guide:
https://www.pengutronix.de/en/blog/2022-03-31-tutorial-rauc-bundle-encryption-using-meta-rauc.html

Everything works great with the resulting bundle and on the end device, unless I try to create a number of device keys greater than 256.

I use this bit of code to create the device keys:

```
# Create X number of key/certs
for i in $(seq 1 256); do
    echo "*** Generating this key: $i ..."
    openssl ecparam -name prime256v1 -genkey -noout -out ${OUTPUT_DIR}/private/private-key-$i.pem
    openssl ec -in ${OUTPUT_DIR}/private/private-key-$i.pem -pubout -out ${OUTPUT_DIR}/public-key-$i.pem
    openssl req -new -x509 -key ${OUTPUT_DIR}/private/private-key-$i.pem -out ${OUTPUT_DIR}/recipient-cert-$i.pem -days 365250 -subj "/O=RAUC/CN=RAUC Test $i"
done
```

I then do a Yocto build, and encrypt the resulting *.raucb bundle with commands like this on the build machine:

```
# Encrypt bundle
echo "*** Encrypting this bundle: $LATEST_RAUCB_BUNDLE ..."
oe-run-native rauc-native rauc encrypt \
  --keyring=$RAUC_CA_KEYRING \
  --to $RECIPIENTS_FILE $LATEST_RAUCB_BUNDLE \
  $ENCRYPTED_RAUCB

# Define keys location
KEYS_DIR="/workspaces/keys/rauc/rauc-device-keys"

# Check bundle
echo "*** Checking this bundle: $ENCRYPTED_RAUCB ..."
oe-run-native rauc-native rauc info \
  --keyring=$RAUC_CA_KEYRING \
  --key=${KEYS_DIR}/private/private-key-1.pem --dump-recipients \
  $ENCRYPTED_RAUCB
```

If I generate only 256 keys initially, this whole process works and the encrypted bundle can be used for updates. If I generate more than 256 keys, I get the following error - both on the build machine during the 'rauc info' stage, and also on the device if I try to update with the bundle.

`Signature size [..] exceeds bundle size`

I've seen this issue which references the same error - however the file md5sums match what I expect and without changing anything else, if I create less keys, then it works:

https://github.com/rauc/meta-rauc/issues/235

I would greatly appreciate any help in resolving this  - thanks.


Discussion: Hm, this looks like you run into a sanity check that existed before we introduced 'crypt' bundles.

When the signature (i.e. all your encryption keys together + the CMS structure) is larger than the payload (i.e. the squashfs), you would actually run into this.
I am not sure why this happens exactly at 256 keys, but maybe it's just a coincidence.

What kind of payload do you have? Is it an actual image?

However, I would see this as a bug in RAUC since it prevents using a large number of recipients together with small payloads, which is not intentional.
The payload is a root file system and Linux Kernel - the total size of the bundle is about ~200 MB.

I do agree that this probably belongs under the RAUC repository.
> When the signature (i.e. all your encryption keys together + the CMS structure) is larger than the payload (i.e. the squashfs), you would actually run into this. I am not sure why this happens exactly at 256 keys, but maybe it's just a coincidence.

Re-checking this again I have to admit that my initial assumption about the sanity check was wrong. The sanity check is fine as it is. Thus I have no clue why you see

     Signature size [..] exceeds bundle size

in that case. I have re-tested encrypting and calling `rauc info` with > 256 keys (and a small payload) and it works as expected.

On the other hand, when using notably more keys, I can reproduce running into another sanity check:

    Invalid bundle format: Signature size (5827693) exceeds 64KiB

This should be addressed but is *not* an explanation for the `exceeds bundle size` error where I have no other clue than that the bundle might be corrupted.

@phishstang65 Could you provide an output log from the corresponding `rauc encrypt` and `rauc info` calls that caused the failure?
@phishstang65 Have you been able to get more information about the problem or any logs that we could have a look at?
@phishstang65 Do you have more information? Otherwise, we'll have to close this issue.
Hi @ejoerns @jluebbe , I apologize for the **extremely** late reply... I have finally come back to this issue and am still experiencing it with the latest meta-rauc and rauc version 1.13. I had originally gone back to only creating 256 keys, but now I have a need for more and I once again experience this issue.

Here is some more information. Please let me know if there is any other information that would help.

Encryption of bundle does **not** complain, however the subsequent 'info' command does:

```
*** company: Encrypting this bundle: /workspaces/company/builds/update-bundle-iot-gate-imx8plus-20250225212148.raucb ...
Running bitbake -e rauc-native
rauc-Message: 21:25:57.805: Reading bundle: /workspaces/company/builds/update-bundle-iot-gate-imx8plus-20250225212148.raucb
rauc-Message: 21:25:57.807: Verifying bundle signature... 
rauc-Message: 21:25:57.814: Verified inline signature by ''
rauc-Message: 21:25:58.514: Encrypted for 511 recipients
Encrypted bundle written to /workspaces/company/builds/encrypted-2025-02-25-21-25-44.raucb
*** company: Checking this bundle: /workspaces/company/builds/encrypted-2025-02-25-21-25-44.raucb ...
Running bitbake -e rauc-native
rauc-Message: 21:26:16.964: Reading bundle: /workspaces/company/builds/encrypted-2025-02-25-21-25-44.raucb
Invalid bundle format: Signature size (123836) exceeds 64KiB
*** company: Done ...
Successfully built: i.i.i [8 minutes and 11 seconds elapsed] ...
All done ...
```

RAUC installation failure with the produced bundle:

```
[root@device ~]$  systemctl status rauc
● rauc.service - RAUC Update Service
     Loaded: loaded (/lib/systemd/system/rauc.service; static)
     Active: active (running) since Mon 2025-02-24 19:10:54 UTC; 1 day 2h ago
       Docs: https://rauc.readthedocs.io
   Main PID: 511 (rauc)
      Tasks: 3 (limit: 4149)
     Memory: 4.2M
     CGroup: /system.slice/rauc.service
             └─511 /usr/bin/rauc --mount=/run/rauc service

Feb 25 21:39:43 device rauc[511]: Active slot bootname: rootfs_b
Feb 25 21:39:43 device rauc[511]: installing /data/fw_update.raucb: started
Feb 25 21:39:43 device rauc[511]: Installation 9cd09e16 started
Feb 25 21:39:43 device rauc[511]: installing /data/fw_update.raucb: Checking and mounting bundle...
Feb 25 21:39:43 device rauc[511]: Reading bundle: /data/fw_update.raucb
Feb 25 21:39:43 device rauc[511]: Installation 9cd09e16 failed: Invalid bundle format: Signature size (123836) exceeds 64KiB
Feb 25 21:39:43 device rauc[511]: Invalid bundle format: Signature size (123836) exceeds 64KiB
Feb 25 21:39:43 device rauc[511]: installing /data/fw_update.raucb: Invalid bundle format: Signature size (123836) exceeds 64KiB
Feb 25 21:39:43 device rauc[511]: installing /data/fw_update.raucb: finished
Feb 25 21:39:43 device rauc[511]: installing `/data/fw_update.raucb` failed: 1
```

Version:

```
developer@pop-os:~/nxp-bsp/sources/meta-rauc$ git log
commit b5fc74e4c94c2f0a0ec92f2283168125a723c6bf (HEAD -> kirkstone, origin/kirkstone)
Author: Enrico Jörns <ejo@pengutronix.de>
Date:   Wed Jan 22 22:07:36 2025 +0100

    rauc: update to v1.13
    
    Signed-off-by: Enrico Jörns <ejo@pengutronix.de>
    (cherry picked from commit d39b12bf4e94a3636fdd60f69692805d8f961d69)
    Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
```
The working update with only 256 keys is created with exactly the same method but yields in success:

```
*** company: Encrypting this bundle: /workspaces/company/builds/update-bundle-iot-gate-imx8plus-20250225215748.raucb ...
Running bitbake -e rauc-native
rauc-Message: 22:05:49.237: Reading bundle: /workspaces/company/builds/update-bundle-iot-gate-imx8plus-20250225215748.raucb
rauc-Message: 22:05:49.240: Verifying bundle signature... 
rauc-Message: 22:05:49.242: Verified inline signature by ''
rauc-Message: 22:05:49.685: Encrypted for 256 recipients
Encrypted bundle written to /workspaces/company/builds/encrypted-2025-02-25-22-05-18.raucb
*** company: Checking this bundle: /workspaces/company/builds/encrypted-2025-02-25-22-05-18.raucb ...
Running bitbake -e rauc-native
rauc-Message: 22:06:18.499: Reading bundle: /workspaces/company/builds/encrypted-2025-02-25-22-05-18.raucb
rauc-Message: 22:06:18.506: Decrypting signature...
rauc-Message: 22:06:18.511: Verifying bundle signature... 
rauc-Message: 22:06:18.512: Verified inline signature by ''
Compatible:     'company i.MX8 RAUC Update'
Version:        'v20230803'
Description:    'company RAUC Update Bundle'
Build:          '20250225215748'
Hooks:          ''
Bundle Format:  crypt [encrypted CMS]
  Crypt Key:    '<hidden>'
  Verity Salt:  '7f570a591ce9a7aa817a7bda331e66ff3a6a134f5111804d15c4c5e4707e8d77'
  Verity Hash:  '34f2ba71a79a81edca7632ceb5be11583bff8e739b8abeb98324634547a0c75b'
  Verity Size:  2064384
Manifest Hash:  '46d35e8c158b3a696661245a520425f20e3fd271e152ae7b541f04e31e61203a'


1 Image:
  [rootfs]
    Filename:  company-core-image-iot-gate-imx8plus.ext4
    Checksum:  71ac1ca181831ca32b55ef631824673d0df6de37c2b51f4789179438531464cd
    Size:      996.2 MB (996,196,352 bytes)
    Hooks:     
    Adaptive:  block-hash-index

256 Recipients:
  0   Issuer:    O = RAUC, CN = RAUC Test 5
      Serial:    0x1ED8D2430069CAEB392ECD70245D715D34EF9BA0
      Algorithm: dhSinglePass-stdDH-sha1kdf-scheme
  1   Issuer:    O = RAUC, CN = RAUC Test 65
      Serial:    0x70EF2CACAAD53897A4D9A29E0F12E775A578C0
      Algorithm: dhSinglePass-stdDH-sha1kdf-scheme
...
255   Issuer:    O = RAUC, CN = RAUC Test 116
      Serial:    0x56A350D9834D696706BBCB9C712594B3CC5E5540
      Algorithm: dhSinglePass-stdDH-sha1kdf-scheme

Certificate Chain:
 0 Subject: 
   Issuer: C = US, ST = Minnesota, L = Minneapolis, O = company ACME, OU = Device, CN = www.company.net
   SPKI sha256: FE:1B:09:8F:CE:7E:D4:F7:0D:3A:31:94:B7:6D:F5:65:9C:A6:BB:1C:0D:0E:A6:D9:70:64:0D:75:3A:AE:3B:ED
   Not Before: Jan 15 19:14:22 2024 GMT
   Not After:  Jan 10 16:23:05 2034 GMT
 1 Subject: C = US, ST = Minnesota, L = Minneapolis, O = company ACME, OU = Device, CN = www.company.net
   Issuer: C = US, ST = Minnesota, L = Minneapolis, O = company ACME, OU = Device, CN = www.company.net
   SPKI sha256: E1:6F:E9:FF:22:60:EF:A3:9F:2B:04:C9:A3:D3:2F:60:CB:FF:05:75:93:40:4F:73:93:06:76:0D:24:A1:99:B4
   Not Before: Jan 13 16:23:06 2024 GMT
   Not After:  Jan 10 16:23:05 2034 GMT

*** company: Done ...
Successfully built: 2.3.4 [16 minutes and 28 seconds elapsed] ...
All done ...
This is ~242 bytes per recipient. Independent of whether the size per recipient could be reduced, we'll hit the limit eventually, so we need some way to make it configurable.
Maybe we should print a warning if we exceed the default limit during bundle creation?
This could prevent users from accidentally running into this limitation while deploying the created bundles in the field.



Fix (patch):
diff --git a/docs/reference.rst b/docs/reference.rst
index 905101031..8733eafcb 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -212,6 +212,14 @@ Example configuration:
   If RAUC is configured with streaming support, this has no effect, as the
   bundle is not downloaded as a whole.
 
+``max-bundle-signature-size`` (optional)
+  Defines the maximum bundle signature size in bytes, and thus must be a simple
+  integer value (without unit) greater than zero.
+  It overwrites the compiled-in default value of 64 KiB.
+  As this limit also applies to the encrypted signature when using ``crypt``
+  bundles, you may need to increase it if you encrypt to a large number of
+  recipients.
+
 ``variant-name`` (optional)
   String to be used as variant name for this board.
   If set, neither ``variant-file`` nor ``variant-dtb`` must be set.
diff --git a/include/config_file.h b/include/config_file.h
index 0d6e311f5..186bd7c1f 100644
--- a/include/config_file.h
+++ b/include/config_file.h
@@ -8,6 +8,8 @@
 
 /* Default maximum downloadable bundle size (8 MiB) */
 #define DEFAULT_MAX_BUNDLE_DOWNLOAD_SIZE 8*1024*1024
+/* Default maximum signature/CMS size (64 KiB) */
+#define DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE 64*1024
 
 typedef enum {
 	R_CONFIG_ERROR_INVALID_FORMAT,
@@ -15,6 +17,7 @@ typedef enum {
 	R_CONFIG_ERROR_PARENT,
 	R_CONFIG_ERROR_PARENT_LOOP,
 	R_CONFIG_ERROR_MAX_BUNDLE_DOWNLOAD_SIZE,
+	R_CONFIG_ERROR_MAX_BUNDLE_SIGNATURE_SIZE,
 	R_CONFIG_ERROR_CHILD_HAS_BOOTNAME,
 	R_CONFIG_ERROR_DUPLICATE_BOOTNAME,
 	R_CONFIG_ERROR_DUPLICATE_CLASS,
@@ -53,6 +56,8 @@ typedef struct {
 	gboolean prevent_late_fallback;
 	/* maximum filesize to download in bytes */
 	guint64 max_bundle_download_size;
+	/* maximum signature/CMS size in bytes */
+	guint64 max_bundle_signature_size;
 	/* path prefix where rauc may create mount directories */
 	gchar *mount_prefix;
 	gchar *store_path;
diff --git a/src/bundle.c b/src/bundle.c
index 3ab12ca39..caa306dcd 100644
--- a/src/bundle.c
+++ b/src/bundle.c
@@ -98,8 +98,6 @@
 #define ZFS_SUPER_MAGIC 0x2fc12fc1
 #endif
 
-#define MAX_BUNDLE_SIGNATURE_SIZE 0x10000
-
 GQuark
 r_bundle_error_quark(void)
 {
@@ -2132,10 +2130,11 @@ static gboolean open_local_bundle(RaucBundle *bundle, GError **error)
 		res = FALSE;
 		goto out;
 	}
-	/* sanity check: signature should be smaller than 64KiB */
-	if (sigsize > MAX_BUNDLE_SIGNATURE_SIZE) {
+	/* sanity check: signature should not exceed the configured maximum */
+	if (sigsize > r_context()->config->max_bundle_signature_size) {
 		g_set_error(error, R_BUNDLE_ERROR, R_BUNDLE_ERROR_SIGNATURE,
-				"Signature size (%"G_GUINT64_FORMAT ") exceeds 64KiB", sigsize);
+				"Signature size (%"G_GUINT64_FORMAT ") exceeds max-bundle-signature-size (%"G_GUINT64_FORMAT ")",
+				sigsize, r_context()->config->max_bundle_signature_size);
 		res = FALSE;
 		goto out;
 	}
@@ -2222,10 +2221,11 @@ static gboolean open_remote_bundle(RaucBundle *bundle, GError **error)
 		res = FALSE;
 		goto out;
 	}
-	/* sanity check: signature should be smaller than 64KiB */
-	if (sigsize > MAX_BUNDLE_SIGNATURE_SIZE) {
+	/* sanity check: signature should not exceed the configured maximum */
+	if (sigsize > r_context()->config->max_bundle_signature_size) {
 		g_set_error(error, R_BUNDLE_ERROR, R_BUNDLE_ERROR_SIGNATURE,
-				"Signature size (%"G_GUINT64_FORMAT ") exceeds 64KiB", sigsize);
+				"Signature size (%"G_GUINT64_FORMAT ") exceeds max-bundle-signature-size (%"G_GUINT64_FORMAT ")",
+				sigsize, r_context()->config->max_bundle_signature_size);
 		res = FALSE;
 		goto out;
 	}
diff --git a/src/config_file.c b/src/config_file.c
index e67b9058d..c289abd9f 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -22,6 +22,7 @@ void default_config(RaucConfig **config)
 	RaucConfig *c = g_new0(RaucConfig, 1);
 
 	c->max_bundle_download_size = DEFAULT_MAX_BUNDLE_DOWNLOAD_SIZE;
+	c->max_bundle_signature_size = DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE;
 	c->mount_prefix = g_strdup("/mnt/rauc/");
 	/* When installing, we need a system.conf anyway, so this is used only
 	 * for info/convert/extract/...
@@ -903,6 +904,26 @@ gboolean load_config(const gchar *filename, RaucConfig **config, GError **error)
 	}
 	g_key_file_remove_key(key_file, "system", "max-bundle-download-size", NULL);
 
+	c->max_bundle_signature_size = g_key_file_get_uint64(key_file, "system", "max-bundle-signature-size", &ierror);
+	if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+		g_debug("No value for key \"max-bundle-signature-size\" in [system] defined "
+				"- using default value of %d bytes.", DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE);
+		c->max_bundle_signature_size = DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE;
+		g_clear_error(&ierror);
+	} else if (ierror) {
+		g_propagate_error(error, ierror);
+		return FALSE;
+	}
+	if (c->max_bundle_signature_size == 0) {
+		g_set_error(
+				error,
+				R_CONFIG_ERROR,
+				R_CONFIG_ERROR_MAX_BUNDLE_SIGNATURE_SIZE,
+				"Invalid value (%" G_GUINT64_FORMAT ") for key \"max-bundle-signature-size\" in system config", c->max_bundle_signature_size);
+		return FALSE;
+	}
+	g_key_file_remove_key(key_file, "system", "max-bundle-signature-size", NULL);
+
 	c->mount_prefix = key_file_consume_string(key_file, "system", "mountprefix", NULL);
 	if (!c->mount_prefix) {
 		g_debug("No mount prefix provided, using /mnt/rauc/ as default");